上一篇 Nacos服務(wù)發(fā)現(xiàn)-一、概覽
本小節(jié),將演示如何使用Spring Cloud Alibaba Nacos Discovery為Spring cloud 應(yīng)用程序與 Nacos 的無縫集成。 通過一些原生的Spring Cloud注解,我們可以快速來實現(xiàn)Spring Cloud微服務(wù)的服務(wù)發(fā)現(xiàn)機(jī)制,并使用Nacos Server作為服務(wù)發(fā)現(xiàn)中心,統(tǒng)一管理所有微服務(wù)。
2.1 Spring Cloud服務(wù)協(xié)作流程
現(xiàn)在,我們對Spring cloud內(nèi)的一些組件還不了解,為了能夠完全理解快速入門程序,我們需要學(xué)習(xí)以下內(nèi)容。
Spring Cloud 常見的集成方式是使用Feign+Ribbon技術(shù)來完成服務(wù)間遠(yuǎn)程調(diào)用及負(fù)載均衡的,如下圖:

- 在微服務(wù)啟動時,會向服務(wù)發(fā)現(xiàn)中心上報自身實例信息,這里ServiceB 包含多個實例。每個實例包括:IP地址、端口號信息。
- 微服務(wù)會定期從Nacos Server(服務(wù)發(fā)現(xiàn)中心)獲取服務(wù)實例列表。
- 當(dāng)ServiceA調(diào)用ServiceB時,Ribbon組件從本地服務(wù)實例列表中查找ServiceB的實例,如獲取了多個實例如Instance1、Instance2。這時Ribbon會通過用戶所配置的負(fù)載均衡策略從中選擇一個實例。
- 最終,F(xiàn)eign組件會通過Ribbon選取的實例發(fā)送HTTP請求。
采用Feign+Ribbon的整合方式,是由Feign完成遠(yuǎn)程調(diào)用的整個流程。而Feign集成了Ribbon,F(xiàn)eign使用Ribbon完成調(diào)用實例的負(fù)載均衡。
2.1.1 負(fù)載均衡的概念
在SpringCloud服務(wù)協(xié)議流程中,ServiceA通過負(fù)載均衡調(diào)用ServiceB,下邊來了解一下負(fù)載均衡:
負(fù)載均衡就是將用戶請求(流量)通過一定的策略,分?jǐn)傇诙鄠€服務(wù)實例上執(zhí)行,它是系統(tǒng)處理高并發(fā)、緩解網(wǎng)絡(luò)壓力和進(jìn)行服務(wù)端擴(kuò)容的重要手段之一。它分為服務(wù)端負(fù)載均衡和客戶端負(fù)載均衡。
服務(wù)器端負(fù)載均衡:

在負(fù)載均衡器中維護(hù)一個可用的服務(wù)實例清單,當(dāng)客戶端請求來臨時,負(fù)載均衡服務(wù)器按照某種配置好的規(guī)則(負(fù)載均衡算法)從可用服務(wù)實例清單中選取其一,去處理客戶端的請求。這就是服務(wù)端負(fù)載均衡。
例如Nginx,通過Nginx進(jìn)行負(fù)載均衡,客戶端發(fā)送請求至Nginx,Nginx通過負(fù)載均衡算法,在多個服務(wù)器之間選擇一個進(jìn)行訪問。即在服務(wù)器端再進(jìn)行負(fù)載均衡算法分配。
客戶端服務(wù)負(fù)載均衡:

我們接下來要講的Ribbon,就屬于客戶端負(fù)載均衡。在Ribbon客戶端會有一個服務(wù)實例地址列表,在發(fā)送請求前通過負(fù)載均衡算法選擇一個服務(wù)實例,然后進(jìn)行訪問,這是客戶端負(fù)載均衡。即在客戶端就進(jìn)行負(fù)載均衡算法分配。
Ribbon是一個客戶端負(fù)載均衡器,它的責(zé)任是通過負(fù)載均衡策略從一組實例列表中挑選合適的實例
Ribbon核心組件IRule是負(fù)載均衡策略接口,它有如下實現(xiàn),大家僅做了解:
RoundRobinRule(默認(rèn)):輪詢,即按一定的順序輪換獲取實例的地址
RandomRule:隨機(jī),即以隨機(jī)的方式獲取實例的地址
AvailabilityFilteringRule: 會先過濾掉由于多次訪問故障而處于斷路器跳閘狀態(tài)的服務(wù),以及并發(fā)的連接數(shù)量超過閾值的服務(wù),然后對剩余的服務(wù)列表按照輪詢策略進(jìn)行訪問
-
WeightedResponseTimeRule: 根據(jù)平均響應(yīng)時間計算所有服務(wù)的權(quán)重,響應(yīng)時間越快,服務(wù)權(quán)重越大,被選中的機(jī)率越高
剛啟動時,如果統(tǒng)計信息不足,則使用RoundRobinRule策略,等統(tǒng)計信息足夠時,會切換到WeightedResponseTimeRule
RetryRule:先按照RoundRobinRule的策略獲取服務(wù),如果獲取服務(wù)失敗,則在指定時間內(nèi)會進(jìn)行重試,獲取可用的服務(wù)
BestAvailableRule:會先過濾掉由于多次訪問故障而處于斷路器跳閘狀態(tài)的服務(wù),然后選擇一個并發(fā)量最小的服務(wù)
ZoneAvoidanceRule:默認(rèn)規(guī)則,復(fù)合判斷Server所在區(qū)域的性能和Server的可用性選擇服務(wù)器
可通過下面方式在Spring Boot 配置文件中修改默認(rèn)的負(fù)載均衡策略:
account‐service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
注:account-service是調(diào)用的服務(wù)的名稱,后面的組成部分是固定的。
2.1.2 Feign 介紹
Feign是Netflix開發(fā)的聲明式、模板化的HTTP客戶端, Feign可以幫助我們更快捷、優(yōu)雅地調(diào)用HTTP API。Feign的英文表意為"假裝,偽裝,變形",可以理解為將HTTP報文請求方式偽裝為簡單的Java接口調(diào)用方式。
小結(jié)
通過上面的學(xué)習(xí),已經(jīng)了解Spring Cloud的微服務(wù)是如何協(xié)作的,通過哪些組件的配合能夠完成服務(wù)間協(xié)作?我們了解了什么是負(fù)載均衡,F(xiàn)eign用于服務(wù)間Http調(diào)用,Ribbon用于執(zhí)行負(fù)載均衡算法選取訪問實例,而Ribbon的實例列表來源是由Spring Cloud的服務(wù)發(fā)現(xiàn)中心提供(當(dāng)前實現(xiàn)為Nacos)。下一節(jié),我們將通過實作的方式使用Feign調(diào)用服務(wù)提供者
2.2 使用Feign進(jìn)行服務(wù)調(diào)用
2.2.1 Nacos
# 通過單機(jī)模式啟動Nacos
startup -m standalone
2.2.2 生產(chǎn)者
(1) 創(chuàng)建生產(chǎn)者模塊

(2) 在pom.xml添加依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nacosdiscover</artifactId>
<groupId>net.zhi365</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-feign-provider</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringCloud Ailibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Ailibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
</dependencies>
</project>
(3) 創(chuàng)建啟動類
ServiceFeignProviderApplication.java
package net.zhi365.servicefeignprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Xin.li
* @date 2021-01-16 12:49
*/
@SpringBootApplication
// 開啟服務(wù)發(fā)現(xiàn)與Feign客戶端
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceFeignProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceFeignProviderApplication.class, args);
}
}
(4) 生產(chǎn)者服務(wù)實現(xiàn)
ProviderController.java
package net.zhi365.servicefeignprovider.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Xin.li
* @date 2021-01-16 12:52
*/
@RestController
public class ProviderController {
@GetMapping("/service")
public String service() {
System.out.println("provider invoke");
return "provider invoke";
}
}
(5) 編寫配置文件
application.yml
server:
port: 56110
spring:
application:
name: feign-provider
cloud:
nacos:
server-addr: localhost:8848
2.2.3 消費者
(1) 新建模塊spring-feign-consumer并在pom文件中添加相應(yīng)依賴
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nacosdiscover</artifactId>
<groupId>net.zhi365</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-feign-consumer</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!-- SpringCloud Ailibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Ailibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
</project>
(2) 模塊啟動類
SpringFeignConsumerApplication.java
package net.zhi365.springfeignconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Xin.li
* @date 2021-01-16 12:10
*/
@SpringBootApplication
// 開啟服務(wù)發(fā)現(xiàn)與Feign客戶端
@EnableDiscoveryClient
@EnableFeignClients
public class SpringFeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringFeignConsumerApplication.class, args);
}
}
(3) 聲明Feign客戶端
ServiceProviderAgent.java
package net.zhi365.springfeignconsumer.remote;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author Xin.li
* @date 2021-01-16 11:57
*/
@FeignClient(value = "feign-provider")
public interface ServiceProviderAgent {
@GetMapping(value = "/service")
public String serviceProvider();
}
(4) 服務(wù)調(diào)用
package net.zhi365.springfeignconsumer.web;
import net.zhi365.springfeignconsumer.remote.ServiceProviderAgent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author Xin.li
* @date 2021-01-16 12:15
*/
@RestController
public class ConsumerController {
@Autowired
private ServiceProviderAgent serviceProviderAgent;
@GetMapping(value = "/service")
public String service(){
// 調(diào)用服務(wù)
String providerResult = serviceProviderAgent.serviceProvider();
return "consumer invoke | " + providerResult;
}
}
2.2.4 服務(wù)調(diào)用
(1) 在Nacos中查看服務(wù)

(2) 訪問消費者頁面
http://localhost:56120/service/

小結(jié):
非常簡單,并且在業(yè)務(wù)調(diào)用時,減少了與業(yè)務(wù)無關(guān)的http請求相關(guān)代碼的編寫,使業(yè)務(wù)邏輯清晰。我們來分析一下Feign幫我們做了哪些事情:
- 在聲明Feign客戶端之后,F(xiàn)eign會根據(jù)@FeignClient注解使用Java的動態(tài)代理技術(shù)生成代理類,在這里我們指定@FeignClient value為service,則說明這個類的遠(yuǎn)程目標(biāo)為Spring Cloud的服務(wù)名稱為service的微服務(wù)。
- service的具體訪問地址,F(xiàn)eign會交由Ribbon獲取,若該服務(wù)有多個實例地址,Ribbon會采用指定的負(fù)載均衡策略選取實例。
- Feign兼容Spring的Web注解(如:@GetMapping),它會分析聲明Feign客戶端方法中的Spring注解,得出Http請求Method、參數(shù)信息以及返回信息結(jié)構(gòu)。
- 當(dāng)業(yè)務(wù)調(diào)用Feign客戶端方法時,會調(diào)用代理類,根據(jù)以上分析結(jié)果,由代理類完成實際的參數(shù)封裝、遠(yuǎn)程Http請求,返回結(jié)果封裝等操作。
2.3 測試多實例負(fù)載均衡
(1) 配置應(yīng)用1-56100

(2) 配置應(yīng)用2-56110
復(fù)制應(yīng)用1為應(yīng)用2,并指定啟動端口為56110

(3) 查看Nacos服務(wù)

(4) 查看負(fù)載情況

經(jīng)過多次調(diào)用,觀看后臺日志,發(fā)現(xiàn)是被輪流調(diào)用,至此負(fù)載均衡實現(xiàn)成功~
注:Rabbin默認(rèn)負(fù)載均衡策略是輪詢
下一篇 Nacos服務(wù)發(fā)現(xiàn)-三、Nacos服務(wù)發(fā)現(xiàn)基礎(chǔ)應(yīng)用