1. 程式人生 > >SpringCloud筆記(三)使用DiscoveryClient手動實現客戶端負載均衡

SpringCloud筆記(三)使用DiscoveryClient手動實現客戶端負載均衡

1、什麼是客戶端負載均衡(Ribbon)?

Ribbon是從eureka註冊中心伺服器端上獲取服務註冊資訊列表,快取到本地,然後在本地實現輪訓負載均衡策略。既在客戶端實現負載均衡。

2、什麼是服務端負載均衡(Nginx)?

 Nginx是客戶端所有請求統一交給Nginx,由Nginx進行實現負載均衡請求轉發,屬於伺服器端負載均衡。 即請求由Nginx伺服器端進行轉發。

3、兩者的應用場景?

Nginx適合於伺服器端實現負載均衡 比如Tomcat ,Ribbon適合在微服務中RPC遠端呼叫實現本地服務負載均衡,比如Dubbo、SpringCloud中都是採用本地負載均衡。

4、SpringCloud中如何使用客戶端負載均衡?

在springcloud中使用客戶端的負載均衡非常簡單,在之前的部落格中已經提到過(SpringCloud筆記(一)服務註冊與發現),只需要在服務呼叫方配置如下

@Bean
@LoadBalanced //就能讓這個RestTemplate在請求時擁有客戶端負載均衡的能力
public RestTemplate restTemplate() {
    return new RestTemplate();
}

@RestController
public class OrderController {

	@Autowired
	private RestTemplate restTemplate;

	@RequestMapping("/getorder")
	public String getOrder() {
		// order 使用rpc 遠端呼叫技術 呼叫 會員服務restTemplate
		String memberUrl = "http://app-producer/getMember";
		String result = restTemplate.getForObject(memberUrl, String.class);
		System.out.println("會員服務呼叫訂單服務,result:" + result);
		return result;
	}
}

由於restTemplate開啟了@LoadBalance註解,因此就開啟了本地負載均衡策略。

當服務呼叫方拿著服務別名呼叫其他服務時,會首先在註冊中心根據別名查詢對應的服務地址列表,查詢到後首先會快取在本地JVM中,預設會每隔30s重新整理一次。之後在服務地址列表中根據負載均衡策略選取一個地址,在底層通過HttpClient進行遠端呼叫。

4、如何使用DiscoveryClient手動實現客戶端負載均衡?

我們可以根據DiscoveryClient獲取註冊中心的服務列表,因此可以使用DiscoveryClient手動實現本地負載均衡。

 @Autowired
 private DiscoveryClient discoveryClient;

使用DiscoveryClient的前提是

a、關閉restTemplate的@LoadBalance註解,負載均衡邏輯由我們手動控制。

b、關閉自我保護機制:保證不可用的服務能夠被及時踢出

自我保護機制的工作機制是如果在15分鐘內超過85%的客戶端節點都沒有正常的心跳,那麼Eureka就認為客戶端與註冊中心出現了網路故障,Eureka Server自動進入自我保護機制,此時會出現以下幾種情況:

      (1)Eureka Server不再從註冊列表中移除因為長時間沒收到心跳而應該過期的服務。
      (2)Eureka Server仍然能夠接受新服務的註冊和查詢請求,但是不會被同步到其它節點上,保證當前節點依然可用。
      (3)當網路穩定時,當前Eureka Server新的註冊資訊會被同步到其它節點中。

下來改造SpringCloud筆記(一)服務註冊與發現中的專案:

首先在eureka-sever的application,yml中關閉自我保護

#埠號
server:
  port: 8100

eureka:
  instance:
    #註冊到本地地址
    hostname: 127.0.0.1

  client:
    serviceUrl:
      #客戶端訪問的路徑
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

    #自己就是註冊中心,不需要註冊自己
    register-with-eureka: false
    #自己就是註冊中心,不需要檢索自己
    fetch-registry: false

  server:
    # 測試時關閉自我保護機制,保證不可用服務及時踢出
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 2000

對應的在springcloud-producer的application.yml中配置如下:

spring:
  application:
    name: app-producer

server:
  port: 8005

eureka:
  client:
    service-url:
         defaultZone: http://localhost:8100/eureka/
    register-with-eureka: true
    fetch-registry: true

  instance:
    #Eureka服務端在收到最後一次心跳之後等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規則等待自己)
    lease-expiration-duration-in-seconds: 2
    ##Eureka客戶端向服務端傳送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規則)
    lease-renewal-interval-in-seconds: 1

對應的在springcloud-consumer的application.yml中配置如下

spring:
  application:
    name: app-consumer

server:
  port: 8001

eureka:
  client:
    service-url:
         defaultZone: http://localhost:8100/eureka/ #註冊中心地址
    register-with-eureka: true
    fetch-registry: true

  instance:
    #Eureka服務端在收到最後一次心跳之後等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規則等待自己)
    lease-expiration-duration-in-seconds: 2
    ##Eureka客戶端向服務端傳送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規則)
    lease-renewal-interval-in-seconds: 1

之後在springcloud-consumer中自定義負載均衡邏輯:

/**
 * 自定義實現客戶端負載均衡
 */
@RestController
public class ExtRibbonController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    //初始化請求數
    private AtomicInteger requestCnt = new AtomicInteger(1);

    @RequestMapping("/ribbon")
    public String ribbon() {
        //1、選取一個服務地址
        String serviceUrl = getServiceUrl() + "/getMember";
        //2、遠端呼叫
        return restTemplate.getForObject(serviceUrl, String.class);
    }

    //本地負載均衡核心程式碼
    private String getServiceUrl() {
        //在註冊中心獲取app-producer對應的服務列表
        List<ServiceInstance> list = discoveryClient.getInstances("app-producer");
        if(list == null && list.size() == 0) {
            return null;
        }

        //獲取服務列表的數量
        int serviceSize = list.size();
        //請求數 % 服務列表數量 得到下標
        int index = requestCnt.get() % serviceSize;
        //將請求數增加1
        requestCnt.incrementAndGet();
        //根據計算的下標選取一個服務地址返回
        return list.get(index).getUri().toString();
    }
}

啟動類:關閉掉自動負載均衡策略

@SpringBootApplication
@EnableEurekaClient
public class SpringcloudConsumerApplication {

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

    @Bean
    //@LoadBalanced 在編寫自定義負載均衡時,關閉此註解
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

啟動eureka-sever

啟動springcloud-producer 更改埠後,再啟動一個例項

啟動springcloud-consumer

此時註冊中心的服務列表如下:

 

現在訪問APP-CONSUMER中我們自己實現的負載均衡邏輯:http://localhost:8001/ribbon

第一次訪問:

第二次訪問:

可以看到自己實現的負載均衡策略發揮處理本地負載均衡的策略!