服务治理: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:端口)、版本号、通信协议等等。注册中心会根据这些登记信息生成一份服务清单。另外,注册中心还需要以心跳的方式去检测清单中的服务是否可用,若不可用,则将登记信息从服务清单中剔除。
即:注册中心根据登记信息维护了一份服务清单,清单中包含各个服务的具体位置及信息。
SpringCloud架构:3-服务治理:Spring Cloud Eureka - 服务发现:有了服务治理框架,服务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)调用活动管理服务的申请活动接口
- 活动管理服务的日志显示调用新增活动接口成功:
SpringCloud架构:3-服务治理:Spring Cloud Eureka - 邮件服务的日志显示发送邮件的接口被调用了:
SpringCloud架构:3-服务治理:Spring Cloud Eureka - 多次调用新增活动接口,发现两个邮件服务实例轮流进行服务提供:
SpringCloud架构:3-服务治理:Spring Cloud Eureka
(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就是基于这些实例位置进行轮询,实现了基于客户端的负载均衡。另外还输出了各个实例的请求总数量、第一次连接信息、上一次连接信息、总的请求失败数量等等信息。