SpringCloud Netflix Eureka
一、SpringCloud介绍
SpringCloud概念
Spring cloud是一个基于Spring Boot实现的服务治理工具包
,用于微服务架构中管理和协调服务的
。Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等
,都可以用Spring Boot的开发风格做到一键启动和部署。通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。有了SpringCloud之后,让微服务架构的落地变得更简单。
SpringCloud常用组件
当我们的项目采用微服务架构之后就会引发一些列的难题需要去解决,如众多微服务的通信地址应该如何管理,微服务之间应该使用何种方式发起调用,微服务故障该如何处理,众多微服务的配置文件如何集中管理等等,SpringCloud为这一系列的难题提供了相应的组件来解决,下面我们简单来理解一下SpringCloud最核心的几大组件如如下:
1、Netflix Eureka
当我们的微服务过多的时候,管理服务的通信地址是一个非常麻烦的事情,Eureka就是用来管理微服务的通信地址清单的,有了Eureka之后我们通过服务的名字就能实现服务的调用。
2、Netflix Ribbon\Feign : 客户端负载均衡
Ribbon和Feign都是客户端负载均衡器,它的作用是在服务发生调用的时候帮我们将请求按照某种规则分发到多个目标服务器上,简单理解就是用来解决微服务之间的通信问题。
3、Netflix Hystrix :断路器
微服务的调用是非常复杂的,有的时候一个请求需要很多的微服务共同完成,那么一旦某个服务发生故障,导致整个调用链上的微服务全都出现异常,甚至导致整个微服务架构瘫痪。Hystrix就是用来解决微服务故障,保护微服务安全的组件。
4、Netflix Zuul : 服务网关
zuul作为服务网关,我们可以把它看作是微服务的大门,所有的请求都需要经过zuul之后才能到达目标服务,根据这一特性,我们可以把微服务公共的是事情交给zuul统一处理,如:用户鉴权,请求监控等。
5、Spring Cloud Config :分布式配置
微服务架构中的服务实例非常的多,服务的配置文件分散在每个服务中,每次修改服务的配置文件和重新服务实例都是一个很麻烦的工作,Spring Cloud Config作为分布式配置管理中心就是用来统一的管理服务的配置文件。
6、Spring Cloud Bus : 消息总线
消息总线是在微服务中给各个微服务广播消息的一个组件,我们使用消息总线构建一个消息中心,其他微服务来接入到消息中心,当消息总线发起消息,接入的微服务都可以收到消息从而进行消费。
7、Spring Cloud sleuth :微服务链路追踪
当我们的应用采用微服务架构之后,后台可能有几十个甚至几百个服务在支撑,一个请求请求可能需要多次的服务调用最后才能完成,链路追踪的作用就是来监控维护之间的调用关系,让程序员方便直观的感受到一个请求经历了哪些微服务,以及服务的请求时间,是否有异常等。
SpringCloud与Dubbo
Dubbo简介
Dubbo最早是有阿里巴巴提供的一个服务治理和服务调用框架,现在已经成为Apache的顶级项目,Dubbo跟SpringCloud最显著的区别是Dubbo的定位只是一个RPC框架,相比SpringCloud来说它缺少很多功能模块,如:网关,链路追踪等,所以往往在使用Dubbo作为微服务开发框架的时候,还需要去配合其他的框架一起使用,如:加入zookeeper作为注册中心。
SpringCloud与Dubbo的区别
Spring Cloud和Dubbo是两个不同的分布式服务框架,它们的区别主要有以下几点:
1、生态环境:Spring Cloud是基于Spring生态系统构建的,而Dubbo是阿里巴巴自主开发的独立框架。Spring Cloud借助于Spring的开发模式和丰富的扩展库,与Spring框架及其他相关组件(如Spring Boot、Spring Data等)紧密集成,使得开发、部署和管理微服务更加简单和便捷。
2、语言支持:Spring Cloud主要面向Java语言,拥有强大的Java生态系统和广泛的社区支持。而Dubbo提供了跨语言的支持,能够与Java以外的语言(如PHP、Python等)进行集成,这对于多语言的微服务架构是有益的。
3、服务治理:Dubbo在服务治理方面提供了更多的功能,如服务注册与发现、负载均衡、路由等,对服务的管理和控制更加细粒度和灵活。而Spring Cloud提供了更高级的抽象,更加关注于开发者友好和快速启动,并通过集成第三方的注册中心(如Eureka、Consul、Zookeeper等)来实现服务治理。
4、配置管理:Spring Cloud提供了集中式的配置管理,通过Config Server来管理微服务的配置信息,并支持动态刷新配置。而Dubbo并没有内置的配置中心,需要借助第三方组件(如Nacos、Apollo等)来实现配置管理。
5、社区支持:Spring Cloud拥有非常庞大和活跃的社区,拥有丰富的文档、教程和案例,问题得到解决的速度也更快。而Dubbo虽然在国内拥有较为广泛的应用和社区,但相对于Spring Cloud来说,其社区规模相对较小。
Dubbo和Spring Cloud功能对比图:
功能 | Dubbo | SpringCloud |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfix Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
熔断器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
信息总线 | 无 | Spring Cloud Bus |
二、服务注册与发现
Eureka介绍
Eureka是Netflix开源的一款服务注册与发现组件,用于构建可弹性、高可用的分布式系统。它在Spring Cloud中作为默认的服务注册与发现中心,提供了方便的服务治理功能。
Eureka的基本概念如下:
-
服务注册与发现:微服务通过向Eureka注册自己的信息(如服务名称、IP地址、端口号)来实现服务的注册。其他微服务可以通过向Eureka发起查询请求来实现服务的发现,并获取可用的服务实例列表。
-
Eureka Server:Eureka Server是Eureka的服务端,用于维护和管理所有服务的注册信息。每个微服务通过向Eureka Server注册自己的信息,将自己标识为一个可用的服务实例。
-
Eureka Client:Eureka Client是每个微服务的客户端,用于向Eureka Server注册自己的信息,并定期向Eureka Server发送心跳信号以表明自己的可用性。同时,Eureka Client也可以从Eureka Server获取其他服务的注册信息,并在需要调用其他服务时进行服务发现。
-
服务实例:每个微服务可以在Eureka Server上注册一个或多个服务实例。一个服务实例代表了一个独立的运行实体,可以提供特定服务的访问能力。
Eureka的工作原理
1.服务注册
Eureka是一个服务注册与发现组件,简单说就是用来统一管理微服务的通信地址的组件,它包含了EurekaServer 服务端(也叫注册中心)和EurekaClient客户端两部分组成,EurekaServer是独立的服务,而EurekaClient需要集成到每个微服务中。
微服务(EurekaClient)在启动的时候会向EurekaServer提交自己的服务信息(通信地址如:服务名,ip,端口等),在 EurekaServer会形成一个微服务的通信地址列表存储起来。 — 这叫服务注册
2.服务发现
微服务(EurekaClient)会定期(RegistryFetchIntervalSeconds:默认30s)的从EurekaServer拉取一份微服务通信地址列表缓存到本地。当一个微服务在向另一个微服务发起调用的时候会根据目标服务的服务名找到其通信地址,然后基于HTTP协议向目标服务发起请求。—这叫服务发现
3.服务续约
另外,微服务(EurekaClient)采用定时(LeaseRenewalIntervalInSeconds:默认30s)发送“心跳”请求向EurekaServer发请求进行服务续约,其实就是定时向 EurekaServer发请求报告自己的健康状况,告诉EurekaServer自己还活着,不要把自己从服务地址清单中剔除掉,那么当微服务(EurekaClient)宕机未向EurekaServer续约,或者续约请求超时,注册中心机会从服务地址清单中剔除该续约失败的服务。
4.服务下线
微服务(EurekaClient)关闭服务前向注册中心发送下线请求,注册中心(EurekaServer)接受到下线请求负责将该服务实例从注册列表剔除
下面我们用一张图来介绍Eureka的工作流程
三、项目实战
1.搭建项目结构
为了方便演示SpringCloud的各个组件,我这里采用多模块的方式搭建项目,所有的jar包交给父工程来管理,搭建好的项目结构如下:
springCloud-netflixt //父项目
springCloud-netflix-eureka //注册中心EurekaServer
springCloud-netflix-servers-user //用户服务EurekaClient ,提供者
springCloud-netflix-servers-order //订单服务EurekaClient ,消费者
2.父项目管理依赖
springCloud-netflixt父工程负责管理SpringBoot和SpringCloud的jar包 ,pom.xml如下
<!-- 1.管理 SpringBoot的jar包 -->
<!--SpringBoot-->
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<!-- 2.管理 SpringCloud的jar包 -->
<!-- 声明SpringCloud的依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 3.这里是所有子项目都可以用的jar包 -->
<dependencies>
<!-- 引入Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 引入测试junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3.搭建Eureka Server
在springCloud-netflixt父工程下面搭建好子工程springCloud-netflix-eureka,然后我们来集成EurekaServer。
1.导入依赖,pom.xml
<!-- EurekaServer端的基础依赖 -->
<!-- 用于在项目中引入Spring Cloud中的Eureka Server组件 -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
2.创建主配置类
// Eureka启动类
@SpringBootApplication // 声明当前类是一个启动类
@EnableEurekaServer // 开启Eureka服务端
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
3.application.yml配置文件
server:
port: 1010 # 端口号
eureka:
instance:
hostname: localhost #主机名
client:
registerWithEureka: false # 不往eureka中注册
fetchRegistry: false # 不获取注册表
serviceUrl: # 注册中心的注册地址
#http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://localhost:1010/eureka/
4.启动测试
启动springCloud-netflix-eureka工程,浏览器访问 http://localhost:10011 ,出现如下界面代码EurekaServer集成成功:
Eureka自我保护
默认情况下,当EurekaServer接收到服务续约的心跳失败比例在15分钟之内低于85%,EurekaServer会把这些服务保护起来,即不会把该服务从服务注册地址清单中剔除掉,但是在此种情况下有可能会出现服务下线,那么消费者就会拿到一个无效的服务,请求会失败,那我们需要对消费者服务做一些重试,或在熔断策略。
当EurekaServer开启自我保护时,监控主界面会出现红色警告信息,我们可以使用eureka.server.enable-self-preservation=false
来关闭EurekaServer的保护机制,这样可以确保注册中心中不可用的实例被及时的剔除,但是不推荐
server:
port: 1010 # 端口号
eureka:
instance:
hostname: localhost #主机名
client:
registerWithEureka: false # 不往eureka中注册
fetchRegistry: false # 不获取注册表
serviceUrl:
#http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://localhost:1010/eureka/
server:
enable-self-preservation: false # 关闭eureka的自我保护模式
4.EurekaClient-用户服务
根据上面我们对Eureka的理解,Eureka分为服务端和客户端,服务端已经搭建成功,我们来搭建客户端。
1.导入依赖
<dependencies>
<!-- 在项目中引入Spring Cloud中的Eureka Client组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- web的基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.主配置类
@SpringBootApplication
public class UserApp {
public static void main(String[] args) {
SpringApplication.run(UserApp.class,args);
}
}
3.application.yml配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:10011/eureka/ # 注册地址
instance:
instance-id: user-server # 实例Id
server:
port: 1020 # 端口号
spring:
application:
name: user-server # 服务名
4.测试EurekaClient
启动springCloud-netflix-eureka , 启动springCloud-netflix-servers-user , 浏览器再次访问http://localhost:1010,那你应该可以看到我们的userServer服务已经被注册到EurekaServer。如下:
5.EurekaClient-订单服务
订单服务和用户服务的做法一样,只是yml配置文件中有些稍微的不同。
这里导入的依赖和主配置类同用户服务一样,省略…
application.yml配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:10011/eureka/ # 注册地址
instance:
instance-id: order-server # 实例Id
server:
port: 1030 # 端口号
spring:
application:
name: order-server # 服务名
四、RestTemplate服务通信
流程说明
目前除了Eureka Server以外我们的微服务有订单服务springCloud-netflix-servers-order,和用户服务springCloud-netflix-servers-user , 我们就用这两个服务来演示微服务的通信,他们的调用关系应该是:浏览器 -> 订单服务 -> 用户服务,如下图:
这里订单服务通过RestTemplate向用户服务发起调用,目的是要获取到用户服务返回的User对象,最终是需要浏览器获取到User。
解决方案
用户服务需要提供User对象,我们需要为其编写Controller接口,编写相关方法返回User,订单服务需要从用户服务获取到User对象,而浏览器需要访问订单服务获取到User对象,所以订单服务也需要编写Controller接口供浏览器来调用。
我们发现不管是用户服务还是订单服务都需要用到User对象,那么是不是在用户服务和订单服务都需要创建User的模型?当然没必要,公共的东西就是要抽取,所以我们会把User对象封装在一个公共的模块springCloud-netflix-pojo-user中然后让用户服务和订单服务都去依赖这个模块。
RestTemplate介绍
微服务的通信协议主流的有RPC,Http,SpringCloud是基于Http Restful 风格 ,在Java中发起一个Http请求的方式很多,比如 Apache的HttpClient , OKHttp等等 。Spring为我们封装了一个基于Restful的使用非常简单的Http客户端工具 RestTemplate ,我们就用它来实订单服务和用户服务的通信。需要注意的是,RestTmplate本身不具备服务发现和负载均衡器的功能,我们本章的案例只是演示在订单服务中使用RestTemplate基于ip和端口的方式向用户服务发起调用,即:不走注册中心,不使用服务发现方式。
编码实战
1、搭建公共模块
创建工程模块 springCloud-netflix-pojo-user ,效果如下:
springCloud-netflixt //父项目
springCloud-netflix-eureka //注册中心EurekaServer
springCloud-netflix-servers-user //用户服务EurekaClient ,提供者
springCloud-netflix-servers-order //订单服务EurekaClient ,消费者
springCloud-netflix-pojo-user //公共user模块
2、在springCloud-netflix-pojo-user中创建User对象如下
@Data
@AllArgsConstructor // 全参构造
@NoArgsConstructor // 无参构造
public class User {
// userId
private Long id;
// user名称
private String username;
// user介绍
private String intro;
}
3、用户和订单依赖User模块
修改工程springCloud-netflix-servers-order,springCloud-netflix-servers-user在他们的pom.xml都导入springCloud-netflix-pojo-user 模块
<!-- 导入公共user模块 -->
<dependency>
<groupId>cn.ms</groupId>
<artifactId>springCloud-netflix-pojo-user</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
4、用户服务返回User
修改springCloud-netflix-servers-user工程,编写controller,返回User对象
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getById(@PathVariable("id")Long id){
return new User(1L,"zs:1","我是zs");
}
}
5、订单服务获取User
在订单服务中需要使用RestTemplate调用用户服务,我们需要把RestTmplate配置成Bean方便使用(当然也可以不创建Bean,用的时候直接new对象也可以) ,修改工程springCloud-netflix-serves-order在主配置类配置RestTemplate如下:
@SpringBootApplication
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class,args);
}
// 向容器中注入一个RestTemplate
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
6、创建controller,通过RestTemplate调用用户服务,代码如下:
@RestController
public class OrderController {
@Autowired
private RestTemplate template;
@GetMapping("/order/{id}") // 根据id查询User
public User getById(@PathVariable("id")Long id){
// 远程调用 - user服务,拿到User
return template.getForObject("http://localhonst:1020/user/"+id,User.class);
}
}
7、测试服务通信
依次启动Eureka Server注册中心, 用户服务 ,订单服务 , 浏览器访问订单服务:http://localhost:1030/order/1 , 返回结果:
Ribbon客户端负载均衡
为什么要用Ribbon
我们知道,为了防止应用出现单节点故障问题,同时为了提高应用的作业能力,我们需要对应用做集群 ,如果我们对user-server(用户服务)做了集群 ,那么这个时候回衍生出一些问题:现在有两个user-server(用户服务)就意味着有两个user-server(用户服务)的通信地址,我的order-server(订单服务)在向user-server(用户服务)发起调用的时候该访问哪个?如何访问?这个时候就需要有一个组件帮我们做请求的分发,即:负载均衡器,而Ribbon就是一个 - 客户端负载均衡器。
什么是Ribbon
Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法
。Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试
等。简单的说,Ribbon是一个客户端负载均衡器,Ribbon可以按照负载均衡算法(如简单轮询,随机连接等)向多个服务发起调用
(正好可以解决上面的问题),我们也很容易使用Ribbon实现自定义的负载均衡算法
。
Ribbon的工作机制
如下图,我们将user-server(用户服务)做集群处理,增加到2个节点(注意:两个user-server(用户服务)的服务名要一样,ip和端口不一样),在注册中心的服务通信地址清单中user-server(用户服务)这个服务下面会挂载两个通信地址 。 order-server(订单服务)会定时把服务通信地址清单拉取到本地进行缓存
, 那么当order-server(订单服务)在向user-server(用户服务)发起调用时,需要指定服务名为 user-server(用户服务)
;那么这个时候,ribbon会根据user-server(用户服务)这个服务名找到两个order-server的通信地址
, 然后ribbon会按照负载均衡算法(默认轮询)选择其中的某一个通信地址,发起http请求实现服务的调用
,如下图:
项目使用
1、用户服务集群配置
在springCloud-netflix-servers-user的application.yml中修改不同的端口和实例Id,可以启动多个User服务,相当与集群效果。
2、修改Controller
为了后续测试的时候方便区分不同的用户服务实例,这里我们在Controller中读取应用的端口随User返回,在启动不同的用户服务实例的时候端口是不同的,修改后的代码如下:
@RestController
public class UserController {
// 读取配置文件中的端口号,用来区别不同的服务
@Value("${server.port}")
private int port;
@GetMapping("/user/{id}")
public User getById(@PathVariable("id")Long id){
return new User(1L,"zs:1","我是zs" + port);
}
}
3、消费者Order-server集成Ribbon
3.1、导入依赖
<!-- 引入Spring Cloud中的Ribbon组件,可以使用负载均衡的功能 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
3.2、开启负载均衡
修改RestTemplate的Bean的定义方法,加上Ribbon的负载均衡注解@LoadBalanced赋予RestTemplate有负债均衡的能力。
@SpringBootApplication
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class,args);
}
@Bean // 向容器中注入一个RestTemplate
@LoadBalanced //@LoadBalanced :让RestTemplate有负载均衡的功能
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3.3、修改Controller调用方式
在之前的案例中,我们调用用户服务是通过用户服务的主机加上端口“localhost:1020”的调用方式,现在我们把 “localhost:1020” 修改为 用户服务的服务名 。底层会通过服务发现的方式使用Ribbin进行负载均衡调用。
@RestController
public class OrderController {
@Autowired
private RestTemplate template;
@GetMapping("/order/{id}") // 根据id查询User
public User getById(@PathVariable("id")Long id){
// 远程调用 - user服务,拿到User
// http://localhonst:1020/user/"+id
return template.getForObject("http://userServer/user/"+id,User.class);
}
}
3.4、测试Ribbon
分别启动EurekaServer注册中心 ,启动两个UserServer用户服务,启动OrderServer订单消费者服务,浏览器访问订单服务:http://localhost:1030/order/1 ,发送多次请求。
总结
Eureka提供了方便的服务注册与发现机制,帮助微服务架构中的服务实例互相发现和通信,以实现高可用、弹性和灵活的分布式系统。它在Spring Cloud中扮演着重要的角色,并且得到了广泛的应用和支持。