SpringCloud入门与基础使用
引言
2021.8.21更新~~
经历了毕业设计的摧残,秋招春招的毒打,进入工作岗位上的我更加坚定的认为,生命不息,学习不止。SpringCloud作为优秀的微服务架构,其中包含了许多的优秀设计理念。最近再次捡起SpringCloud学习,在这里做点笔记,也让大家能稍微了解一下微服务中SpringCloud中的组件(微服务也可以使用Dubbo与Zookeeper)。
首先声明!!!!!
Eureka 停止更新进入维护 ——> Nacos
Ribbon 停止更新进入维护 ——> OpenFeign
Hystrix 停止更新进入维护 ——> Sentinel
Zuul 停止更新正在开发Zuul2 ——> gateway
明确一点,netflex微服务五大组件大多已经停止了维护,在我们后序引入依赖时要注意,netflex组件maven依赖为spring-cloud-strater-netflex-xxxx
,而其他的大多为spring-cloud-strter-xxxx
,或者是alibaba,以下是netflex的五大组件。
SpringCloud开发五大组件
- 服务发现——Netflix Eureka
- 客服端负载均衡——Netflix Ribbon
- 断路器——Netflix Hystrix
- 服务网关——Netflix Zuul
- 分布式配置——Spring Cloud Config
SpringCloud中的大多数组件都停止了更新,只在被动的维护Bug,大多数的组件都推荐更加优秀的SpringCloud Alibaba组件代替,如Eureka可以使用Nacos代替,Hystrix可以使用Sentinal代替。
PS:使用SpringCloud时,一定要注意与SpringBoot的版本配置,其中版本对应才可以。进入SpringCloud官网可以查到
其中推荐大家使用稳定版,也就是RELEASE版本。
这里给大家推荐一下建立一个微服务工程简单的步骤,参考尚硅谷周阳。
- 建Moudle(可以考虑建立一个父级Moudle,之后使用DependencyManagement进行版本管理)
- 改Pom(导入SpringBoot以及Cloud依赖、MySQL、MyBatis等)
- 创Yml(将需要的配置写入,这里可以上网百度,但是一定要先看好引入的依赖是什么)
- 启动类(需要开启什么服务,就加什么注解,如@EnableEurekaClient、@EnableEurekaServer等)
- 写业务(具体业务,CRUD)
#### 前言
- 近年来,随着分布式以及微服务的盛行,使用Java语言进行开发的程序员从最初的Spring到SpringBoot,最终都会来到SpringCloud。
- SpringCloud是什么呢?很多人都有这个疑惑,微服务到底和分布式有什么区别,这是大多数刚接触SpringCloud的人都会有的问题,微服务是通过多个SpringBoot服务,通过每个SpringBoot项目上部署不同的服务(也可以理解成为某个功能),通过Eureka Server进行调度,Zuul网关进行转发,Ribbon进行服务发现,通过RabbitMQ或者Kafka进行服务间通信,使用Hystrix进行熔断策略、服务降级,这些不同的功能去完成,关
于微服务,可以在单节点进行部署,也可以在多节点进行部署,而分布式,则是在创建项目开始,就进行多节点的部署,比如Redis集群,通过多节点部署,通过Sentinel进行监控以及故障转移等,所以,分布式也算作微服务 。 - SpringCloud目前我们使用的分为两种派系,首先是以SpringCloud Eureka为代表的的SpringClou,另外一种是以Dubbo为代表的SpringCloud Alibaba。
SpringCloud Eureka
简介:Eureka作为SpringCloud的服务注册中心,微服务通过注册到Eureka来完成整个服务的搭建,Eureka提供了心跳检测、负载均衡等服务,通过不断的询问各微服务是否存活来进行心跳检测。
使用方法:在注册中心的SpringBoot项目入口类声明 @EnableEurekaServer,在其他微服务入口类上声明 @EnableEurekaClient,并且进行yml文件配置。
Pom依赖
Spring-cloud-starter-netflex-eureka-server #eureka服务端依赖
Spring-cloud-starter-netflex-eureka-client #客户端依赖
启动类声明
- 使用Eureka必须创建注册端以及客户端,将所有的客户端注册在服务端。
- 在服务端入口类加上@EnableEurekaServer
- 在客户端入口类加上@EnableEurekaClient
服务器端YAML配置
server:
port: 9000
eureka:
instance:
hostname: localhost #声明当前微服务的名称
client:
#表示是否从eureka server 获取注册信息,默认是true,如果这是一个单节点的Eureka Server,不需要同步其他节点的数据 ,则设置为false。
fetch-registry: false
#是否将自己注册到Eureka Server 默认是true ,因为当前是单节点的,所以设置为false。
register-with-eureka: false
#设置 Eureka Server的所在的地址,查询服务和注册服务都要依赖这个地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
客户端微服务YAML配置
server:
port: 8000
eureka:
client:
service-url:
#标识向哪个Eureka服务器进行注册
defaultZone: http://server1:${port}/eureka/
配置完后之后,就可以通过访问在Eureka中声明的路径去查看当前SpringCloud中的微服务了,可以看到当前注册在Server上的所有的微服务。
Eureka的高可用
在生产环境中,我们通常会配置多个Eureka Server节点来保证微服务的高可用,防止因为Eureka Server挂掉而导致整个系统挂掉,在单机情况下,我们可以通过修改Eureka Server的的port以及注册地址来部署多个Eureka Server,从而实现高可用。这里要注意,当某微服务注册在Server1上,而Server1和Server2相互注册时,在Server2上也会显示注册在Server1上的微服务。
所谓相互注册,就是defaultZone加上其他注册中心的地址。
配置了注册中心高可用后,在客户端中需要向两个注册中心都去注册,有几个注册中心写几个URL。
客户端集群配置(高可用)
客户端集群可以使我们实现高可用以及负载均衡,在客户端的YML文件中编写
eureka-instance-hostname: XXXX
声明相同的服务名称,注册到Eureka上即可。
eureka:
client:
service-url:
#标识向哪个zk服务器进行注册
defaultZone: http://localhost:2181
其他注册中心
Zookeeper
ZK也是一款优秀的注册中心,可以配合Dubbo实现微服务,可以注册Kafka。
使用zk的方式也比较简单,下载Zookeeper,进行配置后启动,将微服务客户端中的defaultZone地址改成zookeeper的地址,即可注册,这里推荐使用Docker来安装zk镜像之后启动
启动之后,要在注册到zk的客户端上添加依赖。
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</dependency>
<!-- 一定搞清楚是哪个厂家的-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!-- 把自带的zookeeper去除掉,装和当前本机装的zookeeper软件一致的jar包-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
之后更改配置文件向zk注册
spring:
application:
name: 服务名
cloud:
zookeeper:
connect-string: localhost:2181
Consul
Consul也是一款注册中心,同eureka相同的是他们都有直观的界面,可以观察到各个微服务注册情况;同zk相同的是他们都需要下载后启动,并不需要配置一个注册中心微服务。百度直接下载即可。其他同Nacos相似。
Nacos
Nacos作为未来替代Eureka的一款非常优秀的注册中心,同时实现了服务配置,消息总线,注册中心的功能,在SpringCloud Alibaba中会提到,感兴趣的可以去我博客中看看。
SpringCloud Feign/OpenFeign
Ribbon作为一款优秀的服务调用框架,很不幸的是它也进入了维护状态,未来代替它的是OpenFeign。
微服务应用间通信:在传统的微服务框架中,有两种通信方式,一种是基于RPC协议的Dubbo,另外一种是基于HTTP的SpringCloud。SpringCloud在服务间进行通信有两种方式,首先是通过RestTemplate,以及Feign(Feign是在Ribbon的基础上进行了增强,同样都是进行服务发现)。
Ribbon
Ribbon负载均衡:Ribbon通过客户端负载均衡来实现面对微服务多实例下的负载均衡,有服务发现(通过实例名称发现服务),服务选择规则(通过某种算法进行服务的选择),服务监听(检测失效的服务,进行剔除)。ServerList进行服务发现,通过ServerListFilter进行服务筛选,通过IRule进行服务的选择。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflex-ribbon</artifactId>
</dependency>
RestTemplate+LoadBalancerClient进行服务间通讯,使用LoadBalancerClient获取服务名称对应的IP以及Port组合成套接字,通过RestTemplate访问套接字进行服务请求。这种方式也是使用了Ribbon的组件。
配置RestTemplate
/**
* 让Spring将RestTemplate管理起来
* LoadBalance实现负载均衡,默认是轮询机制
* 使用RestTepmlate的getForObject/PostForObject来进行HTTP通信
*/
@Configuration
public class ApplicationContextConfiguration{
@Bean
@LoadBalance
public RestTemplate getTemplate(){
return new RestTemplate();
}
LoadBalance比较重要,开启负载均衡
}
@Autowried
private RestTemplate rest;
Object o = rest.getForObject("http://服务名/请求",返回值类型);
配置IRule规则
代码创建
@Configuration
public class RibbonConfiguration {
@Bean
public IRule ribbonRule(){
// 负载均衡规则改为随机
return new RandomRule();
}
}
注意:该类不应该被应用程序上下文的@ComponentScan注解扫描到
@SpringBootApplication
@RibbonClient(name = "xxx",configuration = RibbonConfiguration.class)
public class MainApplitcaion{
public static void main(String[] args){
SpringAppliction.run(MainApplitcaion.class,args);
}
}
这样就可以为指定的客户端:xxx服务配置指定的负载均衡策略
负载均衡策略
- com.netflex.loadBanalancer.RoundRobinRule 轮询
- com.netflex.loadBanalancer.RandomRule 随机
- com.netflex.loadBanalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果过去服务失败则在指定时间内进行重试,获取可用的服务
- WeightResponseTimeRule 对RoundRobinRule的扩展,相应速度越快的实力权重越多大,越容易被选择
- BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule 默认规则,复合判断service所在区域的性能和server的可用性选择服务器
Feign与OpenFeign
前言:Ribbon已经进入了老年期,只会被动的维护自己,Feign在Ribbon的基础上添加了功能,而OpenFeign则是Feign的升级版,可以更好的完成服务调用。
- Feign进行服务发现:首先引入Feign依赖
Spring-cloud-starter-OpenFeign
,之后在主类上声明注解@EnableFeignClient
,最后编写一个接口用来调用对应的微服务,在该接口上添加@FeignClient(name="xxx")
,并且将对应微服务的接口方法重新在该接口中声明一遍,使用@Autowried
进行调用即可
Feign是一种声明式的REST客户端,看起来很像RPC(RPC是一种接口调用模式,比如直接调用其他微服务的接口方法),但实际是HTTP请求,并且其采用了基于接口的注解方式进行使用。
在使用Feign进行服务间功能调用时,要注意引入的Feign依赖,引入Feign依赖之后,必须引入Spring-boot-starter-web
,否则会报错,无法启动微服务,另外,要注意SpringCloud与Feign的版本问题,否则也会出现无法启动的问题。
OpenFeign配置文件YAML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflex-eureka-client</artifactId>
</dependency>
<!-- 踩坑,使用Feign必须要加入web支持,否则会报错-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
OpenFeign主启动类
@EnableFeignClients
@SpringBootAplitcaion
public class MainApplitcaion{
public static void main(String[] args){
SpringAppliction.run(MainApplitcaion.class,args);
}
}
OpenFeign业务接口
该接口中定义着其他微服务的方法
@FeignClient(name="微服务名称")
public interface getMessage{
这里直接将服务提供方的controller粘贴过来就行,不过要接口方式
}
OpenFeign超时控制
Feign因为整合了Ribbon,所以其天生具有Feign的超市控制,在YMAL中定义超时设置。
#配置ribbon超时设置
ribbon:
# 建立连接的时间,默认是一秒钟
ReadTimeout: 5000
# 建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
OpenFeign日志设置
设置具体Feign的接口路径,就可以设置对应的日志管理
logging:
level:
# 类路径
com.atguigu.springcloud.service.PaymentFeignService: debug
SpringCloud Config | SpringCloud Bus
在企业级开发中,我们经常会遇到不同开发团队开发的不同服务,在项目上线或者是平常测试时,对项目的配置进行改变,如我们要将本地的MySQL换成云服务器上的MySQL。这就需要我们将配置文件修改,但是面对众多的微服务,我们又不可能一个一个去修改,并且下线,这样显然是不好的。
以上图片为我们介绍了SpringCloud Config配置中心的原理,首先,我们将配置文件存放在GIT上,并且通过WebHooks
绑定git更新接口,当我们的git进行更新时,会发送请求到server-config配置中心,收到请求时,server会远程拉取仓库中的新配置文件,并且通过MQ将消息发送到各个微服务,从而达到不重启微服务的情况下完成配置文件的更新。
Config服务端配置
引入依赖
首先引入pom依赖,一定要引入spring-boot-starter-web
,否则会出现找不到容器的错误,这里引入的是spring-cloud-starter-config-server
的依赖,是服务端。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
修改yaml文件
server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git: 这里要注意,要输入当前的git地址
uri: https://gitee.com/xxxx/springcloud-config.git #github上的git名字
#搜索目录
search-paths:
- springcloud-config
username: xxxx
password: xxxx
label: master
#服务注册到eureka上
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
开启config功能
在主启动类上添加@EnableConfigServer
@EnableEurekaClient
@EnableConfigServer
@SpringBootApplication
public class configServerMain3344 {
public static void main(String[] args) {
SpringApplication.run(configServerMain3344.class,args);
}
}
配置git文件
在gitEE或者gitHub上新建仓库,在其中添加yml文件
按地址访问yaml文件
如果访问成功,则说明服务端配置成功。
Config客户端
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
配置YAML(bootstrap.yml)
server:
port: 3355
spring:
application:
name: config-client
cloud:
config:
label: master # 分支名称
name: config #配置文件名称
profile: dev # 读取后缀名称
uri: http://localhost:3344 #配置中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
# 暴露监控端口,方便配置动态更新
management:
endpoints:
web:
exposure:
include: "*"
配置自动刷新
在需要使用动态刷新的类上加上@RefreshScope
,系统会自动强制刷新最新版的配置文件属性,
@RestController
@RefreshScope
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}
动态刷新Webhook
但是这样还需要外部向该客户端发送一条post请求才能强制刷新
localhost:3355/actuator/refresh
这就需要配置webhook了
webhook在gitee和github上都是在更新文件后,自动向所配置地址发送一条post请求
配置webhook需要一个外网地址映射到本机的localhost(如果实在本机部署的话),这时就可以使用netapp来进行外网穿透
SpringCloud Bus
上面说了如何实现配置更改后的自动刷新,但是对于一个大的微服务分布式集群来说,每次更换配置文件总不能一个一个发送请求,而webhook也可以配置多个,为此,springcloud使用消息总线来进行配置更换后的广播操作,使得每次 在服务端config更新后,将消息推送到各个订阅的微服务中(订阅模式可以很好地解决指定微服务更新配置的操作)。目前springcloud仅支持两种消息总线的配置(Kafka和RabbitMQ)
RabbitMQ安装
docker可以很好的帮助我们安装RabbitMQ,安装docker之后,使用docker命令。
docker search rabbitmq
这时会出现以下的各种镜像,选择最上面的官方镜像,执行命令
docker pull rabbitmq
即可拉取镜像,之后使用Docker Dashboard启动镜像,在浏览器中输入http://localhost:15672
,输入账号密码guest即可进入。
服务端引入消息总线
在我们的configServer中引入依赖
<!-- 添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
配置application.yml文件
注意别放在spring下面
rabbitmq:
port: 15672
host: localhost
password: guest
username: guest
# RabbitMQ相关配置,暴露bus刷新配置的端点
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: 'bus-refresh'
客户端引入消息总线
引入依赖
<!-- 添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
配置bootstrap.yml
注意别放在spring下面
rabbitmq:
port: 15672
host: localhost
password: guest
username: guest
配置完成后,启动所有客户端和服务端,更改git上的文件,之后向服务端发送在yml中配置好的请求(这里是bus-refresh),其他的客户端都会被自动的刷新。如果是单机练习,建议使用git的webhook配和netapp来自动发送请求。 也可以查看rabbitmq是否成功发送信息。
实现定点刷新
如果只想通知其中某个微服务改变配置,需要发送指定的POST请求,请求格式为 http://localhost:配置中心的端口号/actuator/bus-refresh/{destination} /bus/refresh请求不再发送到具体的服务实例上,而是发给config server并通过destination参数类指定需要更新配置的服务或实例。
例子:curl -X POST “http://localhost:3344/actuator/bus-refresh/config-client:3355” 只刷新了3355
微服务中的消息传递(SpringCloud Stream)
在微服务中,微服务间传递与接收纤细的方式有同步和异步两种,同步方法就如同我们在上面一样,直接去调用其他微服务的接口,用时才调用,但是在一定的情况下,同步会产生一定的等待,而异步可以让我们在该服务A发生变化时就想其他服务B发布信息,等微服务B要调用时,直接消费发布的信息即可,并且再次将信息异步的发送到之前的微服务A。
目前比较使用广泛的消息队列有Kafka、RabbitMQ,通过引入消息队列并且对消息队列进行监听而得到消息。
- Kafka:Kafka作为一款优秀的消息队列,可以起到在微服务中传递消息,从而实现异步通信的工作。使用Kafka时,我们首先要下载kafka的压缩包,并且解压,之后安装zookeeper,并且通过zookeeper启动kafka,此后在java项目中配置kafka依赖,使用kafka的监听器监听消息队列。
- RabbitMQ:rabbitMQ作为另外一款优秀的消息队列。这里推荐使用docker版的rabbitMQ,Mac中使用Docker,关于docker如何下载,我另外的博客有提到。在项目中,首先引入MQ的依赖,此外使用
@RabbitListener
监听器进行启动以及监听消息队列,关于如何启动,可以参照该注解中的参数,这里不再赘述。
作为SpringCloud简化MQ使用过程,Stream支持Kafka与RabbitMQ,使用过程十分简单。
首先将Spring-cloud-starter-stream-rabbit
依赖引入,其次,配置一个channel类,用于建立一个channel进行通信,一个channel既可以发送也可以接受,不同种类的channel全部继承自MessageChannel。
服务端配置Stream
引入依赖
首先,引入依赖,如上文所讲,引入Spring-cloud-starter-stream-rabbit
。
配置yaml文件
server:
port: 8801
spring:
application:
name: cloud-stream-privider
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整和
type: rabbit #设置组件类型
environment: #设置rabbitmq的相关管径的配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: #服务的整和处理
output: #这个名字是一个通道的名字
destination: studyExchange #表示要使用的exchange名称定义
content-type: application/json #设置消息类型
binder: defaultRabbit #设置要绑定的消息服务的具体设置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
lease-expiration-duration-in-seconds: 5 #如果现在超过了5秒的间隔
lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔
instance-id: send-8801.com
prefer-ip-address: true #访问的路径变为IP地址
这里我踩了一个大坑,rabbitMQ的端口要配成5672,否则会出现无法连接mq而超时的问题,是因为15672是访问mq网页端的端口,而我们要访问客户端的话,必须使用5672。。。
编写通信service
编写接口
public interface SenderService {
public String send();
}
编写实现类
package com.atguigu.springcloud.service.impl;
import com.atguigu.springcloud.service.SenderService;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import javax.annotation.Resource;
import java.util.UUID;
@EnableBinding(Source.class) 注意这里的Source引的是什么包
public class SenderServiceImpl implements SenderService {
@Resource
private MessageChannel output;
@Override
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
return null;
}
}
调用该方法即可向配置文件中配置的的topic发送消息。
客户端配置Stream
首先,引入stream的包,同上一样
到这里,SpringCloud对于Channel的使用已经结束了,如果微服务是多实例部署的话,想让多实例共享一个消息队列,则需要配置配置文件。
配置yml文件
server:
port: 8802
spring:
application:
name: cloud-stream-privider
cloud:
stream:
binders: #自此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
input: #这个名字是一个通道的名称
destination: studyExchange #表示要使用的exchange名称定义
content-type: application/json #设置消息类型,本次为json
binder: defaultRabbit #设置要绑定的消息服务的具体设置
group: atguiguA
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
lease-expiration-duration-in-seconds: 5 #如果现在超过了5秒的间隔
lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔
instance-id: send-8802.com
prefer-ip-address: true #访问的路径变为IP地址
消息接收类
@Component
@EnableBinding(Sink.class)
public class StreamConsumeController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT)
public void input(Message<String> message){
System.out.println("我是消费者1号,-----》接受到的消息是:"+message.getPayload()+"\t"+serverPort);
}
}
分组消费与持久化
分组消费
注意在stream中,一个group中的多个消费者是竞争关系,消息只能被一个消费者消费。不同的group可以重复消费。
而我们不配置组的话,会默认认为是不同的组。
如果当前消费端挂了,但是它还在这个组中,当他在重启过程中,生产端一直在发消息,当服务重新启动后,该消费者会自动消费刚才错过的消息,所以分组是一个非常重要的事情。
SpringCloud Zuul
Zuul在SpringCloud Gateway出现之后已经基本不再更新,而Zuul2目前Spring官方也没有纳入Spring体系,未来更多的是Gateway
zuul作为SpringCloud一站式微服务的网关,负责代理以及分发请求,提供负载均衡,让用户感受不到微服务的存在,通过访问网关定义的端口来访问各个微服务,网关服务如同web项目中的过滤器一般,可以在请求发起前、返回响应后、请求出错时进行处理。
SpringCloud Zuul使用时同样要配置SpringCloud Config(所有的微服务除了Eureka,都需要配置在Config中,为了解决热部署配置文件的问题),并且要引入Zuul的依赖spring-cloud-starter-netflix-zuul
,在主类声明@EnableZuulProxy
,之后就可以根据需求进行请求的拦截,分发。
SpringCloud Zuul解决跨域问题,由于浏览器的同源策略,在进行前后端分离的项目时必须要进行跨域处理,而Zuul为我们提供了简便的方案。
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter(){
final UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
final CorsConfiguration conf = new CorsConfiguration();
conf.setAllowCredentials(true);
conf.setAllowedOrigins(Arrays.asList("*"));
conf.setAllowedHeaders(Arrays.asList("*"));
conf.setAllowedMethods(Arrays.asList("*"));
conf.setMaxAge(300l);
source.registerCorsConfiguration("/**",conf);
return new CorsFilter(source);
}
}
SpringCloud Hystrix
Hystrix已经进入了维护状态,不再更新以及合并新内容
服务降级
SpringCloud Hystrix(豪猪)是SpringCloud家族中的保护措施,也就是进行服务降级,当我们的微服务在进行层层调用时,如果某一层微服务出现了故障,很有可能就会造成级联雪崩效应,而Hystrix可以对微服务进行服务降级用来保护微服务,可以在出现错误时执行预先设定好的对应函数,或者在fallbackMethod中实现对应的处理措施。
哪些情况会触发降级
- 程序运行异常
- 超时
- 服务熔断出发服务降级
- 线程池/信号量也会导致服务降级(Tomcat线程池就是一个好例子)
Pom配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
YAML文件配置
在定义了Zuul网关的微服务中,我们可以同样在yml配置文件中直接设置Hystrix的HystrixCommand属性,定义其超时时间等。
重点:在Feign中开启Hystrix要在配置文件中写 feign.hystrix.enabled: true
PS:这里记录一下网关启动的一个坑,在我们启动微服务时,如果该微服务是第一次启动,系统会以懒汉模式进行启动,就是直到第一次访问才去加载对应的类,如果这时访问该微服务的时间超过了默认Hystrix的时间设置,就会报错(Zuul里集成了Hystrix),这时只要在配置文件中设置一下超时时间即可解决。
feign:
hystrix:
enabled: true
启动类配置
@SpringBootApplication
@EnableEurekaClient
//@EnableCircuitBreaker
@EnableHystrix
public class HistrixPaymentMain8001 {
两个注解都是启用Hystrix的作用,下面的覆盖了上面的
public static void main(String[] args) {
SpringApplication.run(HistrixPaymentMain8001.class,args);
}
}
单独使用
使用:首先在pom文件中加入Hystrix依赖spring-cloud-starter-netflix-hystrix
,在要使用Hystrix的地方声明一个方法,并且在方法上添加@HystrixCommand
注解,当该方法中发生异常或者是违反了HystrixCommand定义的一些参数配置时(如超过HystrixCommand定义的默认时间),就会发生服务降级,此刻,就会使用预先定义的策略来向外界发送反馈(如同线程池的拒绝策略一样,可以自定义)。
@RestController
@DefaultProperties(defaultFallback = "defaultFallback")
这里是指定降级方法,再最上面说明整个类的都需要这个方法去处理
public class HystrixController {
这个注解说明要开启Hystrix
@HystrixCommand
@GetMapping("/getProductInfoList")
public String getProductInfoList(@RequestParam("number") Integer number) {
if (number % 2 == 0) {
throw new RuntimeException("发送异常了");
// return "success";
}
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject("http://127.0.0.1:8081/product/listForOrder",
Arrays.asList("157875196366160022"),
String.class);
}
private String defaultFallback() {
return "默认提示:太拥挤了, 请稍后再试~~";
}
}
解耦合实现Hystrix
同样,hystrix已经集成在了Feign和Zuul的依赖包中,在添加了这两种依赖的微服务中,可以直接使用Hystrix。
package com.imooc.product.client;
import com.imooc.product.common.DecreaseStockInput;
import com.imooc.product.common.ProductInfoOutput;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(name = "product",fallback = ProductClient.ProductClientClientFallback.class)
public interface ProductClient {
@PostMapping("/product/listForOrder")
List<ProductInfoOutput> listForOrder(@RequestBody List<String> productIdList);
@PostMapping("/product/decreaseStock")
void decreaseStock(@RequestBody List<DecreaseStockInput> decreaseStockInputList);
@Component
static class ProductClientClientFallback implements ProductClient{
@Override
public List<ProductInfoOutput> listForOrder(List<String> productIdList) {
return null;
}
@Override
public void decreaseStock(List<DecreaseStockInput> decreaseStockInputList) {
}
}
}
以上定义了该微服务对其他微服务调用的接口,使用了Feign,此时只需要在@FeignClient
注解中加上用于进行服务降级处理的类即可,该类继承自可能出现服务降级的接口,继承的方法就是该方法出问题时的应对策略。
服务限流
Sentinel中说明
服务熔断
在我们的服务访问达到了最大阈值的时候,如果此时再发送请求,就会导致失败,而服务熔断就是为了解决该问题,当请求达到阈值,服务会进行熔断处于关闭状态;当一定时间后,服务会尝试放过一些请求,当该请求成功后,服务此时切换到半开状态;再过一定时间,服务恢复到完全打开状态(就像是具有自动修复功能一样)。
@HystrixCommand(fallbackMethod = "timeoutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000") //超时设置5秒
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//失败率达到多少后跳闸
})
@RequestMapping("/payment/hystrix/timeout/{id}")
public String getFliedTimeout(@PathVariable("id") Integer id){
String result = consumerService.getFliedTimeout(id);
log.info("****result: " + result);
return result;
}
熔断类型
- 熔断打开——请求不再调用当前服务,内部设置一般为MTTR(平均故障处理时间),当打开时间达到设置时钟是进入半开状态
- 熔断关闭——熔断后不再进行请求阻挡
- 熔断半开——请求访问当前服务,如果成功且服务断路器规定的规则,则服务恢复正常——达到完全关闭状态
断路器开启或者关闭条件
- 当满足一定阈值的时候(默认10秒超过20个请求)
- 当失败率达到一定的比例
- 到达以上阈值的时候
- 开启时,所有请求都不会进行转发
- 一段时间后(默认5秒),这个时候断路器是半开状态,会让其他的请求尝试进行转发,成功,断路器关闭,失败,继续开启断路器,重复4和5。
HystrixDashboard 监控中心
建Moudle
改Pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
配置YAML
server:
port: 9001
启动类
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001{
public static void main(String[] args){
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
微服务提供类
所有要进行跟踪的微服务都需要添加actuator监控依赖以及打开@EnableHystrix
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
服务追踪(链路监控)
SpringCloud集成sleuth,通过可视化界面Zipkin进行服务追踪,在各个微服务组合完成全部任务时,有时我们需要了解各个微服务的运行状况,运行时间,Sleuth可以帮我们完成这项工作。
首先引入依赖spring-cloud-starter-zipkin
,该依赖包含了Zipkin以及Sleuth的全部依赖。
其次配置配置文件,在配置文件中,我们要配置Zipkin的地址,以及日志以什么方式发送到zipkin,配置发送日志的多少到zinkin。
spring:
zipkin:
base-url: http://localhost:9411/
sender:
type: web
sleuth:
sampler:
probability: 1
logging:
level:
org.springframework.cloud.openfeign: debug
#这里是要追踪服务的包名以及要打印日志的slave(级别)
最后,下载Zipkin进入https://zipkin.io/即可。
打开Zipkin就可以看到了。