服務生產與消費
Eureka 服務治理體系中有 3 個核心角色:服務注冊中心、服務提供者、服務消費者。服務注
冊中心及服務提供者,在前面已做介紹。
本隨筆主要介紹 Eureka 服務治理體系中的服務消費,以 Spring Cloud 服務調用中的
Ribbon+RestTemplate 方式為主。
什么是 Ribbon
Ribbon 是 Netflix 發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將 Netflix 的
中間層服務連接在一起。Ribbon 客戶端組件提供一系列完善的配置項如連接超時,重試等。
簡單的說,就是在配置文件中列出 Load Balancer(簡稱 LB)后面所有的機器,Ribbon 會自動
的幫助你基於某種規則(如簡單輪詢,隨即連接等)去連接這些機器。我們也很容易使用
Ribbon 實現自定義的負載均衡算法。
Ribbon 的負載均衡策略
1. 簡單輪詢負載均衡
以輪詢的方式依次將請求調度不同的服務器,即每次調度執行 i = (i + 1) mod n,並選出第
i 台服務器。
2. 隨機負載均衡
隨機選擇狀態為 UP 的 Server。
3. 加權響應時間負載均衡
根據相應時間分配一個 weight,相應時間越長,weight 越小,被選中的可能性越低。
4. 區域感知輪詢負載均衡
復合判斷 server 所在區域的性能和 server 的可用性選擇 server。
實現原理
• 一個服務注冊中心,eureka server,端口為 8761。
• micro-service 工程跑了兩個副本,端口分別為 8082、8083,分別向服務注冊中心注冊。
• micro-client 端口為 8081,向服務注冊中心注冊。
• 當 micro-client 通過 restTemplate 調用 micro-service 的 hello 接口時,因為用 ribbon 進行了負
載均衡,會輪流的調用 micro-service:8082 和 8083 兩個端口的 hello 接口。
代碼實踐
使用之前Eureka 實踐中建好的注冊中心,之前我們已經建好了服務生產者,現在在服
務生產者中添加接口,以方便服務消費者進行調用。
配置服務生產者
1. 添加 ServiceController 類:
package com.example.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by Administrator on 2018/11/8.
*/
@RestController
public class ServiceController {
@RequestMapping("hello/{name}")
public String hello(@PathVariable String name) {
return name + " say hello Eureka from port:8082";
}
}
2. 參照該服務生產者再建立一個副本,服務端口改為 8083,關鍵代碼如下:
(application.yml)
# HTTP (Tomcat) port
server:
port: 8083
spring:
application:
name: micro-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
(Controller類)
package com.example.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by Administrator on 2018/11/8.
*/
@RestController
public class ServiceController {
@RequestMapping("hello/{name}")
public String hello(@PathVariable String name) {
return name + " say hello Eureka from port:8083";
}
}
建立服務消費者
1. 在 IDEA 中創建一個 Spring Cloud 工程(服務消費者),pom 文件整體如下:
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>EurekaConsumer</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.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>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. application.yml 配置
# HTTP (Tomcat) port
server:
port: 8081
spring:
application:
name: micro-client
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# application parameters
app:
service-url: http://MICRO-SERVICE/
3. 編寫啟動類,在啟動類上添加@SpringBootApplication @EnableDiscoveryClient 注解,聲明
這是一個 Eureka Client。
ControllerApplication 類:
package com.example;
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;
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerApplication.class, args);
}
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
4. 通過@LoadBalanced 注解,調用 netflix 的 ILoadBalancer 實現客戶端負載均衡。然后通過
CallHelloService、CallHelloController 這兩個類去調用服務提供者提供的 hello 接口,關鍵代碼
如下:
package com.example.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* Created by Administrator on 2018/11/8.
*/
@Service
public class CallHelloService {
@Value("${app.service-url}")
private String appServiceUrl;
@Autowired
private RestTemplate restTemplate;
public String callHello(String name) {
// 是一個 http client
ResponseEntity result = restTemplate.postForEntity(appServiceUrl + "hello/" + name, null, String.class);
return (String) result.getBody();
}
}
package com.example.Controller;
import com.example.Service.CallHelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by Administrator on 2018/11/8.
*/
@RestController
public class CallHelloController {
@Autowired
private CallHelloService callHelloService;
@GetMapping("hello")
public String hello(String name) {
String result = callHelloService.callHello(name);
return result;
}
}
結果驗證
1. 訪問 http://localhost:8761/,如下圖所示:
2. 然后通過如下方式調用服務消費者可看到如下界面:
3. 刷新頁面,測試負載均衡,出現如下界面表示負載均衡配置成功:
注:在整個測試過程中 ,注意所有springboot啟動類在項目中的位置(如下)
否則會出現以下情況