spring cloud alibaba 服務治理

2020-08-12 11:53:48

服務治理介紹

先來思考一個問題

通過上一章的操作,我們已經可以實現微服務之間的呼叫。但是我們把服務提供者的網路地址(ip,埠)等寫死到了程式碼中,這種做法存在許多問題:

一旦服務提供者地址變化,就需要手工修改程式碼
一旦是多個服務提供者,無法實現負載均衡功能
一旦服務變得越來越多,人工維護呼叫關係困難

那麼應該怎麼解決呢, 這時候就需要通過註冊中心動態的實現服務治理。

什麼是服務治理

服務治理是微服務架構中最核心最基本的模組。用於實現各個微服務的自動化註冊與發現

服務註冊: 在服務治理框架中,都會構建一個註冊中心,每個服務單元向註冊中心登記自己提供服
務的詳細資訊。並在註冊中心形成一張服務的清單,服務註冊中心需要以心跳的方式去監測清單中
的服務是否可用,如果不可用,需要在服務清單中剔除不可用的服務。

服務發現: 服務呼叫方向服務註冊中心諮詢服務,並獲取所有服務的範例清單,實現對具體服務範例的存取。

服務註冊中心,它是微服務架構非常重要的一個元件,在微服務架構裡主要起到了協調者的一個作用。註冊中心一般包含如下幾個功能:

  1. 服務發現:
服務註冊:儲存服務提供者和服務呼叫者的資訊

服務訂閱:服務呼叫者訂閱服務提供者的資訊,註冊中心向訂閱者推播提供者的資訊
  1. 服務設定:
設定訂閱:服務提供者和服務呼叫者訂閱微服務相關的設定

設定下發:主動將設定推播給服務提供者和服務呼叫者
  1. 服務健康檢測
檢測服務提供者的健康情況,如果發現異常,執行服務剔除

常見的註冊中心

Zookeeper

zookeeper是一個分佈式服務架構,是Apache Hadoop 的一個子專案,它主要是用來解決分佈式
應用中經常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、叢集管理、分佈式應用
設定項的管理等。

Eureka

Eureka是Springcloud Netflix中的重要元件,主要作用就是做服務註冊和發現。但是現在已經閉

Consul

Consul是基於GO語言開發的開源工具,主要面向分佈式,服務化的系統提供服務註冊、服務發現
和設定管理的功能。Consul的功能都很實用,其中包括:服務註冊/發現、健康檢查、Key/Value
儲存、多數據中心和分佈式一致性保證等特性。Consul本身只是一個二進制的可執行檔案,所以
安裝和部署都非常簡單,只需要從官網下載後,在執行對應的啓動指令碼即可。

Nacos

Nacos是一個更易於構建雲原生應用的動態服務發現、設定管理和服務管理平臺。它是 Spring
Cloud Alibaba 元件之一,負責服務註冊發現和服務設定,可以這樣認爲nacos=eureka+config。

nacos簡介

Nacos 致力於幫助您發現、設定和管理微服務。Nacos 提供了一組簡單易用的特性集,幫助您快速
實現動態服務發現、服務設定、服務元數據及流量管理。

從上面的介紹就可以看出,nacos的作用就是一個註冊中心,用來管理註冊上來的各個微服務。

nacos

搭建nacos環境acos

下載Nacos:https://nacos.io/en-us/

win下啓動:\nacos\bin目錄下點選startup.cmd啓動

存取:http://127.0.0.1:8848/nacos/ 預設賬號密碼均爲:nacos

將商品微服務註冊到nacos

接下來開始修改 shop-product 模組的程式碼, 將其註冊到nacos服務上

1、在pom.xml中新增nacos的依賴

<!--nacos用戶端-->
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

yml

server:
  port: 8081
spring:
  application:
    name: user
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.0.2:3306/demo?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848


logging:
  level:
    com.jx.user.dao : debug

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

Application

package com.jx.user;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author yangxinlei
 * @date 2020/8/10
 */
@SpringBootApplication
@EnableDiscoveryClient
public class UserApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class,args);
    }
}

order服務同樣方式註冊到註冊中心

服務呼叫

修改JxOrderServiceImpl

package com.jx.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.jx.common.User;
import com.jx.user.dao.JxOrderMapper;
import com.jx.user.service.JxOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author yangxinlei
 * @date 2020/8/11
 */

@Service
@Slf4j
public class JxOrderServiceImpl implements JxOrderService {

    @Autowired
    private RestTemplate restTemplate;


    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private JxOrderMapper jxOrderMapper;


    @Override
    public Object getOrderList() throws Exception {
        log.info(">>使用者資訊");

        //從nacos中獲取服務地址
        ServiceInstance serviceInstance = discoveryClient.getInstances("user").get(0);
        String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
        log.info(">>從nacos中獲取到的微服務地址爲:" + url);

        //通過restTemplate呼叫商品微服務
        List<User> product = restTemplate.getForObject("http://user/user/getUserList",  List.class);
        log.info(">>使用者資訊,查詢結果:" + JSON.toJSONString(product));


        return product;
    }
}

JxOrderApplication

package com.jx.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * @author yangxinlei
 * @date 2020/8/11
 */
@SpringBootApplication
@EnableDiscoveryClient
public class JxOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(JxOrderApplication.class,args);
    }


    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

實現服務呼叫的負載均衡

什麼是負載均衡

通俗的講, 負載均衡就是將負載(工作任務,存取請求)進行分攤到多個操作單元(伺服器,元件)上進行執行。

根據負載均衡發生位置的不同,一般分爲伺服器端負載均衡用戶端負載均衡

伺服器端負載均衡指的是發生在服務提供者一方,比如常見的nginx負載均衡

而用戶端負載均衡指的是發生在服務請求的一方,也就是在發送請求之前已經選好了由哪個範例處理請求。

自定義實現負載均衡

通過idea再啓動一個 user 微服務,設定其埠爲8083

-Dserver.port=8083

通過nacos檢視微服務的啓動情況

修改JxOrderServiceImpl

package com.jx.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.jx.common.User;
import com.jx.user.dao.JxOrderMapper;
import com.jx.user.service.JxOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @author yangxinlei
 * @date 2020/8/11
 */

@Service
@Slf4j
public class JxOrderServiceImpl implements JxOrderService {

    @Autowired
    private RestTemplate restTemplate;


    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private JxOrderMapper jxOrderMapper;


    @Override
    public Object getOrderList() throws Exception {
        log.info(">>使用者資訊");

        //從nacos中獲取服務地址
        List<ServiceInstance> instances = discoveryClient.getInstances("user");
        int index = new Random().nextInt(instances.size());
        ServiceInstance serviceInstance = instances.get(index);
        String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
        log.info(">>從nacos中獲取到的微服務地址爲:" + url);

        //通過restTemplate呼叫商品微服務
        List<User> product = restTemplate.getForObject("http://user/user/getUserList",  List.class);
        log.info(">>使用者資訊,查詢結果:" + JSON.toJSONString(product));


        return product;
    }
}

基於Ribbon實現負載均衡

Ribbon是Spring Cloud的一個元件, 它可以讓我們使用一個註解就能輕鬆的搞定負載均衡

在RestTemplate 的生成方法上新增@LoadBalanced註解

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
  return new RestTemplate();
}

修改服務呼叫

package com.jx.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.jx.common.User;
import com.jx.user.dao.JxOrderMapper;
import com.jx.user.service.JxOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @author yangxinlei
 * @date 2020/8/11
 */

@Service
@Slf4j
public class JxOrderServiceImpl implements JxOrderService {

    @Autowired
    private RestTemplate restTemplate;
    


    @Override
    public Object getOrderList() throws Exception {
        log.info(">>使用者資訊");
        //直接使用微服務名字, 從nacos中獲取服務地址
        String url = "user";
        //通過restTemplate呼叫商品微服務
        List<User> product = restTemplate.getForObject("http://"+url+"/user/getUserList",  List.class);
        log.info(">>使用者資訊,查詢結果:" + JSON.toJSONString(product));

        return product;
    }
}

策略名 策略描述 實現說明
BestAvailableRule 選擇一個最小的併發 請求的server 逐個考察Server,如果Server被tripped了,則忽略,在選擇其中ActiveRequestsCount最小的server
AvailabilityFilteringRule 過濾掉那些因爲一直連線失敗的被標記爲circuit tripped的後端server,並過濾掉那些高併發的的後端server(activeconnections 超過設定的閾值) 使用一個AvailabilityPredicate來包含過濾server的邏輯,其實就就是檢查status裡記錄的各個server的執行狀態
WeightedResponseTimeRule 根據相應時間分配一個weight,相應時間越長,weight越小,被選中的可能性越低。 一個後臺執行緒定期的從status裏面讀取評價響應時間,爲每個server計算一個weight。Weight的計算也比較簡單responsetime 減去每個server自己平均的responsetime是server的權重。當剛開始執行,沒有形成statas時,使用roubine策略選擇server。
RetryRule 對選定的負載均衡策略機上重試機制 機製。 在一個設定時間段內當選擇server不成功,則一直嘗試使用subRule的方式選擇一個可用的server
RoundRobinRule 輪詢方式輪詢選擇server 輪詢index,選擇index對應位置的server
RandomRule 隨機選擇一個server 在index上隨機,選擇index對應位置的server
ZoneAvoidanceRule 複合判斷server所在區域的效能和server的可用性選擇server 使用ZoneAvoidancePredicate和AvailabilityPredicate來判斷是否選擇某個server,前一個判斷判定一個zone的執行效能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用於過濾掉連線數過多的Server。

修改application.yml

user: # 呼叫的提供者的名稱
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

基於Feign實現服務呼叫

加入Fegin的依賴

<!--fegin元件-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

JxOrderApplication

package com.jx.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * @author yangxinlei
 * @date 2020/8/11
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//開啓Fegin
public class JxOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(JxOrderApplication.class,args);
    }


    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

新建一個service

package com.jx.user.service;

import com.jx.common.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.List;

/**
 * @author yangxinlei
 * @date 2020/8/12
 */
@FeignClient("user")
public interface UserService {

    @GetMapping(value = "/user/getUserList")
    Object userGetUserList();
}

呼叫

@Autowired
private UserService userService;


Object product = userService.userGetUserList();
log.info(">>使用者資訊,查詢結果:" + product);