今天起,开始学习springCloud,写博客,只为自己更好的学习和总结,欢迎各位大佬一起探讨学习,有不对的地方希望大家积极纠正。
一:SpringCloud介绍
1.认识SpringCloud
1.1 SpringCloud基本概念
SpringCloud是基于SpringBoot实现的服务治理工具包,用于微服务架构中管理和协调服务。是一系列框架的有序集合,利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发。通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。有了SpringCloud之后,让微服务架构的落地变得更简单。
1.2 SpringCloud常用组件
当我们的项目采用微服务架构之后就会引发一些列的难题需要去解决,SpringCloud为这一系列的难题提供了相应的组件来解决,下面我们简单来理解一下SpringCloud最核心的几大组件如如下:
Netflix Eureka:
Eureka就是用来管理微服务的通信地址清单的,有了Eureka之后我们通过服务的名字就能实现服务的调用
Spring Cloud Config:分布式配置
分布式配置管理中心,用来统一的管理服务的配置文件
Netflix Zuul:服务网关
我们可以把它看作是微服务的大门,所有的请求都需要经过zuul之后才能到达目标服务,根据这一特性,我们可以把微服务公共的是事情交给zuul统一处理
Netflix Ribbon/Feiqn:客户端负载均衡
Ribbon和Feign都是客户端负载均衡器,它的作用是在服务发生调用的时候帮我们将请求按照某种规则分发到多个目标服务器上,简单理解就是用来解决微服务之间的通信问题。
Netflix Hystrix:断路器
Hystrix是用来解决微服务故障,保护微服务安全的组件,避免因为服务器故障,导致整个调用链上的微服务全都出现异常
Spring Cloud Bus:消息总线
消息总线是在微服务中给各个微服务广播消息的一个组件,我们使用消息总线构建一个消息中心,其他微服务来接入到消息中心,当消息总线发起消息,接入的微服务都可以收到消息从而进行消费。
Spring Cloud Sleuth:微服务链路追踪
链路追踪的作用就是来监控维护之间的调用关系,让程序员方便直观的感受到一个请求经历了哪些微服务,以及服务的请求时间,是否有异常等
1.3 SpringCloud的版本
SpringCloud是基于SpringBoot的,所以两者的jar包都需要导入,需要注意的是SprinbCloud的版本需要和SpringBoot的版本兼容,本文章采用的SpringBoot版本是2.2.5.RELEASE , SpringCloud的版本是Finchley.SR1。
二.服务注册与发现
1.Eureka介绍
1.1 什么是Eureka?
微服务的其中一个特点是服务之间需要进行网络通信,服务器之间发起调用时调用服务得知道被调用服务的通信地址,但是当微服务数量过多时,我们又该如何管理众多的通信地址呢?对于随时新增加的微服务和下线的微服务,又应该如何去动态添加和删除这些微服务的通信地址呢?所以我们需要借助一个强大的工具帮我们实现这一功能 - Eureka,同类型的组件还有 zookeeper,consul等
1.2 Eureka的工作原理
1.服务注册
Eureka是一个统一管理微服务的通信地址的组件,它包含了EurekaServer 服务端(也叫注册中心)和EurekaClient客户端两部分组成,EurekaServer是独立的服务,而EurekaClient需要集成到每个微服务中。
微服务(EurekaClient)在启动的时候会向EurekaServer提交自己的服务信息(通信地址如:服务名,ip,端口等),在 EurekaServer会形成一个微服务的通信地址列表存储起来。 --- 这叫服务注册
2.服务发现
微服务(EurekaClient)会定期(RegistryFetchIntervalSeconds:默认30s)的从EurekaServer拉取一份微服务通信地址列表缓存到本地。当一个微服务在向另一个微服务发起调用的时候会根据目标服务的服务名找到其通信地址,然后基于HTTP协议向目标服务发起请求。---这叫服务发现
3.服务续约
微服务(EurekaClient)采用定时(LeaseRenewalIntervalInSeconds:默认30s)发送“心跳”请求向EurekaServer发请求进行服务续约,其实就是定时向 EurekaServer发请求报告自己的健康状况,超过3次续约失败,会被剔除(它有一个保护机制,当阈值低于0.85的时候,不会剔除掉服务)
4.服务下线
微服务(EurekaClient)关闭服务前向注册中心发送下线请求,注册中心(EurekaServer)接受到下线请求负责将该服务实例从注册列表剔除
下面的图是Eureka的工作流程
2.EurekaServer实战
文章采用“用户”,“订单”,“支付”等业务来演示整个SpringCloud的各个组件
2.1 多模块项目结构
1.搭建项目结构
为了方便演示SpringCloud的各个组件,我这里采用多模块的方式搭建项目,所有的jar包交给父工程来管理
2. 父项目管理依赖
SpringCloud的Jar包管理参照文档:4. Quick Start
springcloud-parent父工程负责管理SpringBoot和SpringCloud的jar包 ,pom.xml如下
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<!--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包
-->
<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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--小辣椒-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2.2 搭建EurekaServer
参照文档:12. Service Discovery: Eureka Server
在springcloud-netflix-eureka-parnet父工程下面搭建好子工程springcloud-netflix-eureka,然后我们来集成EurekaServer。
1.导入依赖
<dependencies>
<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-web</artifactId>
</dependency>
</dependencies>
提示:spring-cloud-starter-netflix-eureka-server作为EurekaServer端的基础依赖,但同时这个包也把EurekaClient端的以来也导入进来了,spring-boot-starter-web作为web服务的基础依赖是不可缺少的。
2.主配置类
/**
* 注册中心启动类
* @EnableEurekaServer : 开启EurekaServer服务端
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class);
}
}
提示:在主配置类上通过 @EnableEurekaServer 注解开启了EurekaServer端的功能。
3. application.yml配置文件
server:
port: 10010 #端口
eureka:
instance:
hostname: localhost #主机
client: #客户端配置
#EurekaServer自己不要注册到EurekaServer自己 ,只有EurekaClient才注册
registerWithEureka: false
#EurekaServer不要拉取服务的通信地址列表 ,只有EurekaClient才拉取地址列表
fetchRegistry: false
serviceUrl: #注册中心的注册地址
#http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://localhost:10010/eureka/
server:
enable-self-preservation: false #关闭自我保护警告
提示:这里配置了EurekaServer的端口为 10010 ,主机名 localhost ,需要特别说明的是我们再引入EurekaServer的基础依赖spring-cloud-starter-netflix-eureka-server时,这个依赖即引入了EurekaServer所需要的包,也引入了EurekaClient的包,换句话说,现在的springcloud-netflix-eureka工程既是一个 EurekaServer,也是一个EurekaClient。
我们这里暂时把EurekaClient的功能屏蔽掉 ,即关闭它的服务注册和发现功能,让他做好EurekaServer该做的事情即可。
serviceUrl是服务注册地址,EurekaClient需要注册到EurekaServer就得跟上该地址。
registerWithEureka=false :禁止自己向自己注册
fetchRegistry=false : 禁止拉取服务注册列表
4.启动测试
启动springcloud-eureka-server-10010工程,浏览器访问 http://localhost:10010 ,出现如下界面代码EurekaServer集成成功:
2.3 Eureka自我保护机制
默认情况下,当EurekaServer接收到服务续约的心跳失败比例在15分钟之内低于85%,EurekaServer会把这些服务保护起来,即不会把该服务从服务注册地址清单中剔除掉,但是在此种情况下有可能会出现服务下线,那么消费者就会拿到一个无效的服务,请求会失败,那我们需要对消费者服务做一些重试,或在熔断策略。
当EurekaServer开启自我保护时,监控主界面会出现红色警告信息,我们可以使用eureka.server.enable-self-preservation=false
来关闭EurekaServer的保护机制,这样可以确保注册中心中不可用的实例被及时的剔除,但是不推荐
3.EurekaClient实战-用户模块
参照官方文档 : 11. Service Discovery: Eureka Clients
根据上一章节我们的Eureka的理解,Eureka分为服务端和客户端,服务端已经搭建成功,我们来搭建客户端。
根据我们上面的Eureka流程图,其实我们的用户服务 springcloud-netflix-service-user,订单服务springcloud-netflix-service-order 两个工程都是EurekaClient客户端,都需要去集成EurekaClient,我们先从springcloud-netflix-service-user 下手 。
1.导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.主配置类
/**
* 注册中心启动类
* @EnableEurekaServer : 开启EurekaServer服务端
*/
@SpringBootApplication
@EnableEurekaClient
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class);
}
}
3.application.yml配置
#注册到EurekaServer
eureka:
client:
serviceUrl: #注册中心的注册地址
defaultZone: http://localhost:10010/eureka/
instance:
prefer-ip-address: true #使用ip地址进行注册
instance-id: server-user0 #实例ID
spring:
application:
name: server-user #这个是
server:
port: 10030
4.测试EurekaClient
启动springcloud-netflix-eureka , 启动springcloud-netflix-service-user , 浏览器再次访问http://localhost:10010,那你应该可以看到我们的server-user服务已经被注册到EurekaServer。如下:
4.EurekaClient实战-订单模块
1.导入依赖
和user模块一样,此处省略
2.启动类配置
和user模块一样,此处省略
3.application.yml配置
订单服务除了服务名,端口和用户服务不一样,其他的都一样,如下:
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:10010/eureka/
instance:
prefer-ip-address: true #使用ip地址进行注册
instance-id: server-order:10020 #实例ID
spring:
application:
name: server-order #服务的名字
server:
port: 10020
4.测试
启动订单服务,访问Eureka Server的监控界面:http://localhost:10010,可以看到订单服务也注册进去了
三:RestTemplate服务通讯
1.如何实现服务通信
1.1 流程说明
上一章节我们对Eureka Server做了高可用集群搭建,这一章节我们来实现简单版的服务通信。
目前除了Eureka Server以外我们的微服务有订单服务springcloud-netflix-service-order,和用户服务springcloud-netflix-service-user, 我们就用这两个服务来演示微服务的通信,他们的调用关系应该是:浏览器 -> 订单服务 -> 用户服务,如下图:
这里订单服务通过RestTemplate向用户服务发起调用,目的是要获取到用户服务返回的User对象,最终是需要浏览器获取到User。
1.2 解决方案
用户服务需要提供User对象,我们需要为其编写Controller接口,编写相关方法返回User,订单服务需要从用户服务获取到User对象,而浏览器需要访问订单服务获取到User对象,所以订单服务也需要编写Controller接口供浏览器来调用。
我们发现不管是用户服务还是订单服务都需要用到User对象,那么是不是在用户服务和订单服务都需要创建User的模型?当然没必要,公共的东西就是要抽取,所以我们会把User对象封装在一个公共的模块 springcloud-netflix-pojo-user中然后让用户服务和订单服务都去依赖这个模块。
2.编码实战
2.1 搭建公共模块
1.创建工程模块 springcloud-netflix-pojo-user
2.在springcloud-netflix-pojo-user中创建User对象如下
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
//名字
private String username;
//简介
private String intro;
}
2.2 在用户模块和订单模块中引入springcloud-netflix-pojo-user
<!--公共user依赖-->
<dependency>
<groupId>com.lqf</groupId>
<artifactId>springcloud-netflix-pojo-user</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2.3 用户服务返回User
修改springcloud-netflix-service-user 工程,编写controller,返回User对象
@RestController
public class UserController {
@Value("${server.port}")//将yml里面server.port的端口号 复制给port变量
private int port;
@GetMapping("/user/{id}")
public User getById(@PathVariable("id") Long id){
return new User(id,"user","法外狂徒张三");
}
}
2.4 在订单服务中获取user
订单服务中需要使用RestTemplate调用用户服务,我们需要把RestTmplate配置成Bean方便使用(当然也可以不创建Bean,用的时候直接new对象也可以) ,修改工程springcloud-netflix-service-order在主配置类配置RestTemplate如下:
/**
* 订单的启动类
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
//配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
创建controller,通过RestTemplate调用用户服务,代码如下:
@RestController
public class OrderController {
@Autowired
private RestTemplate template;
@GetMapping("/order/{id}")
// SERVER-USER 服务名字
public User getById(@PathVariable("id")Long id){
return template.getForObject("http://localhost:10010/user/"+id, User.class);
}
}
这里的url是用户服务获取user资源的路径,通过RestTmplate发起Http调用,拿到User对象后返回给浏览器。
2.5 测试服务通信
依次启动Eureka Server注册中心(不启动也行) , 用户服务 ,订单服务 , 浏览器访问订单服务:http://localhost:10020/order/1 , 返回结果在页面就可以了
四:Ribbon客户端负载均衡
1.基本概念
1.1 为什么使用Ribbon
我们知道,为了防止应用出现单节点故障问题,同时为了提高应用的作业能力,我们需要对应用做集群 ,如果我们对server-user(用户服务)做了集群 ,那么这个时候回衍生出一些问题:现在有两个server-user(用户服务)就意味着有两个server-user(用户服务)的通信地址,我的server-order(订单服务)在向server-user(用户服务)发起调用的时候该访问哪个?如何访问?这个时候就需要有一个组件帮我们做请求的分发,即:负载均衡器,而Ribbon就是一个 - 客户端负载均衡器。
1.2 什么是Ribbon
Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法
。Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试
等。简单的说,Ribbon是一个客户端负载均衡器,Ribbon可以按照负载均衡算法(如简单轮询,随机连接等)向多个服务发起调用
(正好可以解决上面的问题),我们也很容易使用Ribbon实现自定义的负载均衡算法
。
1.3 Ribbon的工作机制
如下图,我们将server-user(用户服务)做集群处理,增加到2个节点(注意:两个server-user(用户服务)的服务名要一样,ip和端口不一样),在注册中心的服务通信地址清单中user-server(用户服务)这个服务下面会挂载两个通信地址 。 order-server(订单服务)会定时把服务通信地址清单拉取到本地进行缓存
, 那么当server-order(订单服务)在向user-server(用户服务)发起调用时,需要指定服务名为
server-user(用户服务)
;那么这个时候,ribbon会根据user-server(用户服务)这个服务名找到两个
server-order的通信地址
, 然后ribbon会按照负载均衡算法(默认轮询)选择其中的某一个通信地址,发起http请求实现服务的调用
,如下图:
2. Server-User用户服务集群
在idea界面点击Edit
我使用的idea是2022,不同版本开启一个程序运行多个实例的方法不同 可以百度搜索一下
在编辑页面点击Modify options
应用即可,然后修改yml的端口号和实例ID,改为10031即可 实例ID可以改为user1
然后修改user服务的controller
@RestController
public class UserController {
@Value("${server.port}")//将yml里面server.port的端口号 复制给port变量
private int port;
@GetMapping("/user/{id}")
public User getById(@PathVariable("id") Long id){
return new User(id,"user","我的端口号是"+port);
}
}
3. 消费者Server-order集成Ribbon
Ribbon集成官方文档:16. Client Side Load Balancer: Ribbon
3.1 添加依赖
<!--Ribbon依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
3.2 开启负载均衡
修改RestTemplate的Bean的定义方法,加上Ribbon的负载均衡注解@LoadBalanced赋予RestTemplate有负债均衡的能力。
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
//配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3.3 修改controller调用方式
在之前的案例中,我们调用用户服务是通过用户服务的主机加上端口“localhost:10030”的调用方式,现在我们把 “localhost:10030” 修改为 用户服务的服务名 。底层会通过服务发现的方式使用Ribbin进行负载均衡调用。
@RestController
public class OrderController {
@Autowired
private RestTemplate template;
@GetMapping("/order/{id}")
// SERVER-USER 服务名字
public User getById(@PathVariable("id")Long id){
return template.getForObject("http://SERVER-USER/user/"+id, User.class);
}
}
3.4 测试
分别启动EurekaServer注册中心 ,启动两个UserServer用户服务,启动OrderServer订单消费者服务,浏览器访问订单服务:http://localhost:10020/order/1 ,发送多次请求。
观察响应的结果中的端口变化 - 端口会交替出现10030,10031我们可以推断出Ribbon默认使用的是轮询策略。
负载均衡算法比较多,有兴趣的小伙伴可以自己拓展一下,好啦,第一天的学习到此就结束了,内容较多,喜欢的小伙伴可以点赞支持一下