請(qǐng)先閱讀之前的內(nèi)容:
上一步我們創(chuàng)建了服務(wù)提供者 eureka-client
來提供加法服務(wù)废睦,并且啟動(dòng)了兩個(gè)實(shí)例 Zones。
下一步养泡,我們?nèi)ハM(fèi)服務(wù)提供者的接口嗜湃。在這里我們建立一個(gè)項(xiàng)目:
-
eureka-consumer
:服務(wù)消費(fèi)者
在微服務(wù)架構(gòu)中,業(yè)務(wù)都會(huì)被拆分成一個(gè)獨(dú)立的服務(wù)澜掩,服務(wù)與服務(wù)的通訊是基于 HTTP Restful 的购披。Spring Cloud 有兩種服務(wù)調(diào)用方式,一種是 Ribbon + RestTemplate肩榕,另一種是 Feign刚陡。
eureka-consumer 服務(wù)消費(fèi)者
可以通過如下的 Spring Assistant 插件來創(chuàng)建項(xiàng)目,添加 Eureka Discovery
和 Web
点把,Actuator
作為依賴橘荠。
從 pom.xml
中可以看出,導(dǎo)入了如下的依賴:
- 2.0.3.RELEASE 版本的 Spring Boot
- Finchley.RELEASE 版本的 Spring Cloud
spring-cloud-starter-netflix-eureka-client
spring-boot-starter-web
spring-boot-starter-actuator
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
隨后在啟動(dòng)程序中通過 @EnableDiscoveryClient
來激活 Eureka 中的 DiscoveryClient
實(shí)現(xiàn):
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaConsumerApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerApplication.class, args);
}
}
隨后在 application.properties
:指定 Eureka 注冊(cè)中心的地址郎逃。消費(fèi)者自己的端口是 3001。
spring.application.name=eureka-consumer
server.port=3001
eureka.client.serviceUrl.defaultZone=http://localhost:1234/eureka/
隨后挺份,我們創(chuàng)建一個(gè) ConsumerController
類來消費(fèi)服務(wù):
@RestController
public class ConsumerController {
private final static Logger logger = LoggerFactory.getLogger(ConsumerController.class);
@Autowired
LoadBalancerClient loadBalancerClient;
@Autowired
DiscoveryClient discoveryClient;
@Autowired
RestTemplate restTemplate;
@GetMapping("/consumer")
public Integer consumer() {
// 獲取到的所有服務(wù)清單
String services = "Services: " + discoveryClient.getServices();
logger.info(services);
// 調(diào)用加法服務(wù)
ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client");
String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/add";
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(url)
// Add query parameter
.queryParam("operand1", 1)
.queryParam("operand2", 2);
logger.info(builder.toUriString());
return restTemplate.getForObject(builder.toUriString(), Integer.class);
}
}
org.springframework.cloud.client.discovery.DiscoveryClient
接口是 Spring Cloud 對(duì)服務(wù)治理做的一層抽象褒翰,所以可以屏蔽 Eureka 和 Consul 服務(wù)治理的實(shí)現(xiàn)細(xì)節(jié),我們的程序不需要做任何改變匀泊,只需要引入不同的服務(wù)治理依賴优训,并配置相關(guān)的配置屬性就能輕松的將微服務(wù)納入 Spring Cloud 的各個(gè)服務(wù)治理框架中。
可以看到這里各聘,我們注入了 LoadBalancerClient
和 RestTemplate
揣非,先通過 loadBalancerClient
的 choose
函數(shù)來 負(fù)載均衡 的選出一個(gè) eureka-client
的服務(wù)實(shí)例,這個(gè)服務(wù)實(shí)例的基本信息存儲(chǔ)在 ServiceInstance
中躲因,然后通過這些對(duì)象中的信息拼接出訪問 /add
接口的詳細(xì)地址早敬,最后再利用 RestTemplate
對(duì)象實(shí)現(xiàn)對(duì)服務(wù)提供者接口的調(diào)用。
最后通過 mvn spring-boot:run
命令啟動(dòng)項(xiàng)目大脉,啟動(dòng)完成后搞监,可以通過 http://127.0.0.1:3001/consumer 消費(fèi)服務(wù)。
刷新頁面多次镰矿,從日志中可以看出琐驴,依次調(diào)用 2001 和 2002 兩個(gè)端口對(duì)應(yīng)的服務(wù)提供者,這是通過 LoadBalancerClient
實(shí)現(xiàn)的負(fù)載均衡。
通過 Ribbon 來消費(fèi)服務(wù)
Spring Cloud Ribbon 是基于 Netflix Ribbon 實(shí)現(xiàn)的一套客戶端負(fù)載均衡的工具绝淡。它是一個(gè)基于 HTTP 和 TCP 的客戶端負(fù)載均衡器宙刘。它可以通過在客戶端中配置 ribbonServerList
來設(shè)置服務(wù)端列表去輪詢?cè)L問以達(dá)到均衡負(fù)載的作用。
- 當(dāng) Ribbon 與 Eureka 聯(lián)合使用時(shí)牢酵,
ribbonServerList
會(huì)被DiscoveryEnabledNIWSServerList
重寫悬包,擴(kuò)展成從 Eureka 服務(wù)注冊(cè)中心中獲取服務(wù)實(shí)例列表。同時(shí)它也會(huì)用NIWSDiscoveryPing
來取代IPing
茁帽,它將職責(zé)委托給 Eureka 來確定服務(wù)端是否已經(jīng)啟動(dòng)玉罐。 - 而當(dāng) Ribbon 與 Consul 聯(lián)合使用時(shí),
ribbonServerList
會(huì)被ConsulServerList
來擴(kuò)展成從 Consul 獲取服務(wù)實(shí)例列表潘拨。同時(shí)由ConsulPing
來作為IPing
接口的實(shí)現(xiàn)吊输。
首先在 pom.xml
中添加 Ribbon
依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
隨后在啟動(dòng)程序中通過 @LoadBalanced
來注解 RestTemplate
:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
由于 RestTemplate
被 @LoadBalanced
修飾,所以它具備客戶端負(fù)載均衡的能力铁追,當(dāng)請(qǐng)求真正發(fā)起的時(shí)候季蚂,URL 中的服務(wù)名會(huì)根據(jù)負(fù)載均衡策略從服務(wù)清單中挑選出一個(gè)實(shí)例來進(jìn)行訪問。
隨后琅束,我們修改 ConsumerController
類來消費(fèi)服務(wù):
@GetMapping("/consumer")
public Integer consumer() {
// 獲取到的所有服務(wù)清單
String services = "Services: " + discoveryClient.getServices();
logger.info(services);
// 調(diào)用加法服務(wù)
String url = "http://eureka-client/add";
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(url)
// Add query parameter
.queryParam("operand1", 1)
.queryParam("operand2", 2);
logger.info(builder.toUriString());
return restTemplate.getForObject(builder.toUriString(), Integer.class);
}
這里請(qǐng)求的 host 位置并沒有使用一個(gè)具體的 IP 地址和端口的形式扭屁,而是采用了服務(wù)名的方式組成。
Spring Cloud Ribbon 有一個(gè)攔截器涩禀,它能夠在這里進(jìn)行實(shí)際調(diào)用的時(shí)候料滥,自動(dòng)的去選取服務(wù)實(shí)例,并將實(shí)際要請(qǐng)求的 IP 地址和端口替換這里的服務(wù)名艾船,從而完成服務(wù)接口的調(diào)用葵腹。
通過 Feign 來消費(fèi)服務(wù)
Spring Cloud Feign 是一套基于 Netflix Feign 實(shí)現(xiàn)的聲明式服務(wù)調(diào)用客戶端。它使得編寫 Web 服務(wù)客戶端變得更加簡(jiǎn)單屿岂。
我們只需要通過創(chuàng)建接口并用注解來配置它既可完成對(duì) Web 服務(wù)接口的綁定践宴。它具備可插拔的注解支持,包括 Feign 注解爷怀、JAX-RS 注解阻肩。它也支持可插拔的編碼器和解碼器。
Spring Cloud Feign 還擴(kuò)展了對(duì) Spring MVC 注解的支持运授,同時(shí)還整合了 Ribbon 和 Eureka 來提供均衡負(fù)載的 HTTP 客戶端實(shí)現(xiàn)烤惊。
首先在 pom.xml
中添加 Ribbon
依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
隨后在啟動(dòng)程序中通過 @EnableFeignClients
注解開啟掃描 Spring Cloud Feign 客戶端的功能。
創(chuàng)建一個(gè) Feign 的客戶端接口定義徒坡。使用 @FeignClient
注解來指定這個(gè)接口所要調(diào)用的服務(wù)名稱撕氧,接口中定義的各個(gè)函數(shù)使用 Spring MVC 的注解就可以來綁定服務(wù)提供方的 REST 接口:
@FeignClient("eureka-client")
public interface CalculatorClient {
@GetMapping("/add")
Integer add(@RequestParam Integer operand1, @RequestParam Integer operand2);
}
隨后,我們修改 ConsumerController
類來消費(fèi)服務(wù):
@GetMapping("/consumer")
public Integer consumer() {
// 獲取到的所有服務(wù)清單
String services = "Services: " + discoveryClient.getServices();
logger.info(services);
// 調(diào)用加法服務(wù)
return calculatorClient.add(1, 2);
}
Ribbon的饑餓加載(eager-load)模式
引用:http://blog.didispace.com/spring-cloud-tips-ribbon-eager/
有時(shí)候會(huì)發(fā)現(xiàn)這樣一個(gè)問題:我們服務(wù)消費(fèi)方調(diào)用服務(wù)提供方接口的時(shí)候喇完,第一次請(qǐng)求經(jīng)常會(huì)超時(shí)伦泥,而之后的調(diào)用就沒有問題了剥啤。
造成第一次服務(wù)調(diào)用出現(xiàn)失敗的原因主要是 Ribbon 進(jìn)行客戶端負(fù)載均衡的 Client 并不是在服務(wù)啟動(dòng)的時(shí)候就初始化好的,而是在調(diào)用的時(shí)候才會(huì)去創(chuàng)建相應(yīng)的 Client不脯,所以第一次調(diào)用的耗時(shí)不僅僅包含發(fā)送 HTTP 請(qǐng)求的時(shí)間府怯,還包含了創(chuàng)建 RibbonClient 的時(shí)間,這樣一來如果創(chuàng)建時(shí)間速度較慢防楷,同時(shí)設(shè)置的超時(shí)時(shí)間又比較短的話牺丙,很容易就會(huì)出現(xiàn)上面所描述的顯現(xiàn)。
而 Feign 的實(shí)現(xiàn)基于 Ribbon复局,所以它也有一樣的問題冲簿。
我們可以通過下面的配置來開啟 Ribbon 的饑餓加載模式,這樣 RibbonClient 會(huì)提前創(chuàng)建亿昏,而不是在第一次調(diào)用的時(shí)候創(chuàng)建峦剔。
ribbon.eager-load.enabled=true
ribbon.eager-load.clients=eureka-client
引用:
程序猿DD Spring Cloud基礎(chǔ)教程
Spring Cloud構(gòu)建微服務(wù)架構(gòu):服務(wù)消費(fèi)(基礎(chǔ))【Dalston版】
Spring Cloud構(gòu)建微服務(wù)架構(gòu):服務(wù)消費(fèi)(Ribbon)【Dalston版】
Spring Cloud構(gòu)建微服務(wù)架構(gòu):服務(wù)消費(fèi)(Feign)【Dalston版】】
Spring Cloud Dalston中文文檔