服務治理:Spring Cloud Eureka
-
- 一、服務治理
-
-
-
- 1.什麼是服務治理
- 2.為什麼需要服務治理
- 3.服務治理的機制
-
-
- 二、Netflix Eureka
-
- 1.搭建簡單的注冊中心
-
-
-
- (1)建立springboot項目
- (2)引入依賴
- (3)啟用Eureka服務端
- (4)配置Eureka
- (5)啟動并通路
-
-
- 2.改造原有的郵件服務注冊到Eureka Server
-
-
-
- (1)引入依賴
- (2)啟用Eureka用戶端
- (4)配置Eureka
- (5)啟動并通路
-
-
- 3.高可用的注冊中心-Eureka Server雙節點
-
-
-
- (1)如何使Eureka Server高可用
- (2)注冊中心節點peer1配置
- (3)注冊中心節點peer2配置
- (4)配置本地host
- (5)啟動兩個節點
- (6)通路注冊中心
-
-
- 4.高可用的注冊中心-Eureka Server叢集
-
-
-
- (1)注冊中心節點peer1配置
- (2)注冊中心節點peer2配置
- (3)注冊中心節點peer3配置
- (4)啟動
-
-
- 6.郵件服務注冊到Eureka Server叢集
-
-
-
- (1)修改配置
- (2)啟動郵件服務
-
-
- 7.服務發現與消費
-
-
-
- (1)啟動兩個郵件服務執行個體,啟動注冊中心叢集
- (2)建構活動管理服務
- (3)在郵件服務中編寫發送郵件Restful Api
- (4)在活動管理服務中編寫申請活動Restful Api
- (5)啟動活動管理服務
- (6)調用活動管理服務的申請活動接口
- (7)Ribbon日志
-
-
一、服務治理
1.什麼是服務治理
服務治理是微服務架構中最為核心和基礎的子產品,它主要來實作各個微服務執行個體的自動化注冊與發現。
2.為什麼需要服務治理
随着業務的發展,系統功能越來越複雜,相應的微服務應用也不斷增加,每個服務的具體位置、命名都有可能發生變化,如果采用手工維護靜态配置的方式,極易發生錯誤或者命名沖突。為了解決微服務架構中服務執行個體維護的問題,産生了大量的服務治理架構和産品。
3.服務治理的機制
-
服務注冊:在服務治理架構中,通常會建構一個注冊中心,每個微服務執行個體向這個注冊中心登記自己的資訊,包括:自己的服務名、位置(IP:端口)、版本号、通信協定等等。注冊中心會根據這些登記資訊生成一份服務清單。另外,注冊中心還需要以心跳的方式去檢測清單中的服務是否可用,若不可用,則将登記資訊從服務清單中剔除。
即:注冊中心根據登記資訊維護了一份服務清單,清單中包含各個服務的具體位置及資訊。
- 服務發現:有了服務治理架構,服務A調用服務B可以不再指定服務B的執行個體位址(IP:端口号)來實作,而是通過向服務B的服務名發起請求。簡單的服務發現邏輯:以上圖為基礎,發票驗證服務需要調用基礎資料服務,發票驗證服務會向注冊中心發起咨詢服務請求,注冊中心傳回基礎資料服務的的資訊,即傳回{基礎資料:192.168.110.122:8098,192.168.110.121:8091},這樣發票驗證服務就獲得了基礎資料服務的兩個可用位置,發票服務會以某種輪詢政策從注冊中心傳回的基礎資料服務清單中選擇一個位置發起請求(此處便是用戶端負載均衡的政策)。
- 關于服務發現:服務從注冊中心擷取服務清單這個過程并不等于服務發現,隻是服務發現的一部分。服務發現還包含選擇服務執行個體的過程,這就是用戶端負載均衡或者服務端負載均衡的實作了。
以上隻是簡單介紹邏輯,在真實的服務治理架構中,為了性能等因素,不會每次調用都會想注冊中心發起咨詢服務請求,針對不同的場景,有不同的緩存和服務剔除機制和政策。
二、Netflix Eureka
Spring Cloud Eureka,使用Netflix Eureka來實作服務注冊于發現, 它包含了服務端元件與用戶端元件。
- Eureka服務端:服務注冊中心。
- Eureka用戶端:主要使用者處理服務的注冊和發現,用戶端通過注解或參數配置的方式,嵌入在用戶端應用程式(即各個服務)的代碼中,當用戶端應用程式(各個服務)運作時,Eureka用戶端會向注冊中心注冊自身所嵌入的服務的資訊,并周期性的發送心跳來更新它的服務續約,以上即是服務的注冊。同是,他能從注冊中心查詢目前的服務清單,并把它們緩存到本地并周期性的重新整理服務狀态,以上即是服務的發現。
1.搭建簡單的注冊中心
(1)建立springboot項目
(2)引入依賴
需要注意下springboot與SpringCloud的版本關系:
springboot 2.2.1引入Spring Cloud Finchley版本竟然報錯!!!引入Hoxton.RC2版本就好了。
(3)啟用Eureka服務端
// 啟用Eureka注冊中心
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
(4)配置Eureka
server:
port: 9001
# Eureka相關配置
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false # 預設情況下,注冊中心也會将自己作為一個服務嘗試注冊自己,關閉
fetch-registry: false # 注冊中心維護服務清單即可,不需要去檢索服務
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
(5)啟動并通路
2.改造原有的郵件服務注冊到Eureka Server
(1)引入依賴
引入Eureka依賴和SpringCloud依賴
(2)啟用Eureka用戶端
// 啟用Eureka用戶端
@EnableEurekaClient
@SpringBootApplication
public class ScloudEmailApplication {
public static void main(String[] args) {
SpringApplication.run(ScloudEmailApplication.class, args);
}
}
(4)配置Eureka
server:
port: 8003
spring:
application:
name: scloud-email-server # 郵件服務名
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9091/eureka/ # 指定注冊中心位址
(5)啟動并通路
啟動郵件服務:
3.高可用的注冊中心-Eureka Server雙節點
Eureka Server的設計一開始就考慮到了高可用的問題,在Eureka服務治理的理念中,每一個節點都是服務提供方,也是服務消費方,注冊中心也不例外。是以我們在上面還需要設定注冊中心不注冊自己。
(1)如何使Eureka Server高可用
Eureka Server将自己作為一個服務向其他注冊中心注冊自己,這樣就可以形成一組互相注冊的服務注冊中心,即一個高可用的注冊中心叢集。
就采用上面的注冊中心工程,建立兩個配置檔案,分别用兩個配置檔案運作一次,即啟動了兩個注冊中心節點。
(2)注冊中心節點peer1配置
# Eureka Server叢集 - 節點1
server:
port: 9091
spring:
application:
name: eureka-server # 注冊中心服務名
# Eureka配置
eureka:
instance:
hostname: peer1
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://peer2:9092/eureka/ # 注冊中心位址指向節點2的位址
(3)注冊中心節點peer2配置
# Eureka Server叢集 - 節點2
server:
port: 9092
spring:
application:
name: eureka-server # 注冊中心服務名
# Eureka配置
eureka:
instance:
hostname: peer2
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://peer1:9091/eureka/ # 注冊中心位址指向節點1的位址
以上請注意要設定register-with-eureka和fetch-registry兩個參數,否則啟動後會發現互相不在執行個體表中的現象,即啟動了兩個注冊中心單點執行個體。
(4)配置本地host
編輯本機C:\Windows\System32\drivers\etc\hosts檔案,添加如下配置
(5)啟動兩個節點
把eureka server項目打成一個jar包:
使用application-peer1.yml配置檔案啟動Eureka Server節點1:
java -jar server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1
使用application-peer2.yml配置檔案啟動Eureka Server節點2:
java -jar server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2
(6)通路注冊中心
4.高可用的注冊中心-Eureka Server叢集
在生産環境中我們可能需要三個或者大于三個的注冊中心節點來保證注冊中心服務的穩定性,配置的原理與雙節點一樣,将注冊中心節點的注冊位址指向其他的節點。
(1)注冊中心節點peer1配置
# Eureka Server叢集 - 節點1
server:
port: 9091
spring:
application:
name: eureka-server # 注冊中心服務名
# Eureka配置
eureka:
instance:
hostname: peer1
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://peer2:9092/eureka/,http://peer3:9093/eureka/ # 注冊中心位址指向節點2、節點3的位址
(2)注冊中心節點peer2配置
# Eureka Server叢集 - 節點2
server:
port: 9092
spring:
application:
name: eureka-server # 注冊中心服務名
# Eureka配置
eureka:
instance:
hostname: peer2
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://peer1:9091/eureka/,http://peer3:9093/eureka/ # 注冊中心位址指向節點1的位址
(3)注冊中心節點peer3配置
# Eureka Server叢集 - 節點3
server:
port: 9093
spring:
application:
name: eureka-server # 注冊中心服務名
# Eureka配置
eureka:
instance:
hostname: peer3
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://peer1:9091/eureka/,http://peer2:9092/eureka/ # 注冊中心位址指向節點1、節點2的位址
(4)啟動
java -jar server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1
java -jar server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2
java -jar server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer3
6.郵件服務注冊到Eureka Server叢集
(1)修改配置
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://peer1:9091/eureka/,http://peer2:9092/eureka/,http://peer3:9093/eureka/ # 指定注冊中心位址
(2)啟動郵件服務
7.服務發現與消費
上面的内容我們主要做了:搭建高可用的Eureka注冊中心,一個郵件服務作為服務提供者注冊到注冊中心。
接下來我們要建構一個服務,暫且就叫它活動管理服務,這個服務要去發現郵件服務(發現服務)并且調用郵件服務(消費服務)。
- 服務發現:服務發現功能由Eureka用戶端來完成,即内嵌在活動管理服務中的Eureka用戶端,去向注冊中心咨詢服務清單,找到郵件服務執行個體的位址。
- 服務消費:服務消費功能由Ribbon來完成,Ribbon是一個機遇HTTP和TCP的用戶端負載均衡器。此處不對Ribbon做深入研究,隻需知道:活動管理服務找到郵件服務的多個可用執行個體之後,由Ribbon用一種政策,選擇其中一個郵件服務的執行個體進行調用,以實作用戶端負載均衡的服務消費。
(1)啟動兩個郵件服務執行個體,啟動注冊中心叢集
(2)建構活動管理服務
- 建立springboot項目,引入依賴:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cloud</groupId>
<artifactId>activimanage</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>activimanage</name>
<description>SCloud-Activity Management</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Eureka依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--Ribbon依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!--Spring Cloud 依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RC2</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
- 給Application添加@EnableEurekaClient注解
- 建立RestTemplate的Spring Bean執行個體,添加@LoadBalanced開啟用戶端負載均衡
// 啟用Eureka用戶端
@EnableEurekaClient
@SpringBootApplication
public class ActivimanageApplication {
// 建立RestTemplate的Spring Bean執行個體,添加@LoadBalanced開啟用戶端負載均衡
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ActivimanageApplication.class, args);
}
}
- 配置活動管理服務服務名,注冊中心位址等
server:
port: 8100
spring:
application:
name: scloud-activimanage-server # 活動管理服務名
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://peer1:9091/eureka/,http://peer2:9092/eureka/,http://peer3:9093/eureka/ # 指定注冊中心位址
(3)在郵件服務中編寫發送郵件Restful Api
@RestController
@RequestMapping("/api/email")
public class EmailSendController {
/**
* sendSimpleEmail : 發送一封簡單的郵件
*
* @author zhaoxudong
* @date 2019/11/21 15:34
* @param to 收件人
* @param subject 主題
* @param content 内容
*/
@GetMapping("/simple/send")
public void sendSimpleEmail(@RequestParam("to")String to, @RequestParam("subject") String subject, @RequestParam("content") String content){
// 郵件發送邏輯....
System.out.println("| ------------------------------------------");
System.out.println("| 郵件服務(SCLOUD-EMAIL-SERVER):");
System.out.println("| 簡單郵件發送接口(/api/email/simple/send):");
System.out.println("| to(收件人) : " + to);
System.out.println("| subject(主題) : " + subject);
System.out.println("| content(内容) : " + content);
System.out.println("| ..... 郵件發送成功 .....");
System.out.println("| ------------------------------------------");
}
}
(4)在活動管理服務中編寫申請活動Restful Api
@RestController
@RequestMapping("/api/activity")
public class ActivityApplyController {
@Autowired
private RestTemplate restTemplate;
/**
* applySimpleActivity : 申請活動
*
* @author zhaoxudong
* @date 2019/11/21 18:02
*/
@GetMapping(value = "/simple/apply")
public void applySimpleActivity(){
// 申請活動邏輯...
// 調用【郵件服務】發送活動申請成功郵件
String emailServiceRs = restTemplate.getForEntity("http://SCLOUD-EMAIL-SERVICE/api/email/simple/[email protected]&subject=活動申請&content=申請活動成功,此郵件為活動管理服務調用郵件服務發送!",
String.class).getBody();
System.out.println("| -------------------------------");
System.out.println("| 活動管理服務(SCLOUD-ACTIVIMANAGE-SERVICE):");
System.out.println("| 活動申請成功... ");
System.out.println("| 郵件服務傳回: " + emailServiceRs);
System.out.println("| -------------------------------");
}
}
(5)啟動活動管理服務
(6)調用活動管理服務的申請活動接口
- 活動管理服務的日志顯示調用新增活動接口成功:
- 郵件服務的日志顯示發送郵件的接口被調用了:
- 多次調用新增活動接口,發現兩個郵件服務執行個體輪流進行服務提供:
(7)Ribbon日志
在調用活動管理服務的申請活動接口的時候,在活動管理服務調用郵件服務之前,Ribbon輸出了一些非常有用的日志:
DynamicServerListLoadBalancer for client SCLOUD-EMAIL-SERVICE initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SCLOUD-EMAIL-SERVICE,current list of Servers=[GZHHDLP0003.AZCN.COM:8003, GZHHDLP0003.AZCN.COM:8004],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:GZHHDLP0003.AZCN.COM:8004; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
, [Server:GZHHDLP0003.AZCN.COM:8003; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.[email protected]
其中包含了目前Eureka用戶端向注冊中心咨詢的郵件服務的資訊,包含郵件服務的執行個體位置,Ribbon就是基于這些執行個體位置進行輪詢,實作了基于用戶端的負載均衡。另外還輸出了各個執行個體的請求總數量、第一次連接配接資訊、上一次連接配接資訊、總的請求失敗數量等等資訊。