SpringCloud介绍
SpringCloud 是用于构建微服务开发和治理的框架集合。它本身不会提供具体功能性的操作,更专注于服务之间的通讯、熔断、监控等,因此就需要很多的组件来支持一套功能。Springcloud基于springboot,提供了一套分布式微服务的解决方案。
Springcloud下每个微服务都是一个springboot工程,以下介绍的所有组件都是在springboot工程中集成。
Springboot介绍
SpringBoot 基于 Spring 开发,是对spring的进一步封装,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程。通过封装、抽象、提供默认配置等方式让我们更容易使用。
采用 Spring Boot 可以大大的简化开发模式,它集成了大量常用的第三方库配置,所有你想集成的常用框架,它都有对应的组件支持,例如 Redis、数据库 等等。SpringBoot 应用中这些第三方库几乎可以零配置地开箱即用,大部分的 SpringBoot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。
Springboot内置servlet容器(tomcat\Jetty\Undertow),默认为tomcat。若需要更换servlet容器,需要在默认配置中排除tomcat,增加其他容器依赖。容器版本在springboot中指定,一般不需要单独指定。
Springboot工程需引入的包
Springcloud组件
springCloud常用的五大组件包括:eureka、Feign、ribbon、hystrix、zuul等。
Eureka
Eureka是springcloud中负责服务注册和发现的组件,它分为eureka server和eureka client。
- Eureka server
Eureka server为服务注册中心,本身也是一个springboot工程,做为服务端,所有其他的服务提供者和消费者都作为客户端,在服务端注册。Eureka server支持集群部署。配置步骤如下:
- Pom.xml依赖
- 启动类增加注解@EnableEurekaServer
- Yml配置
集群搭建时,需要把register-with-eureka和fetch-registry改为true,多个注册中心之间互相注册。
搭建完成,访问注册中心页面(hostname或ip+port):
展示已注册的微服务。
- Eureka client
做为服务提供者和提供消费者,每个服务中都集成了一个Eureka Client组件,这个组件负责将当前服务信息发送到Eureka Server中。各组件可以通过Eureka server发现并获取其它服务的地址信息,进行请求。配置步骤如下:
- Pom引入
- 启动类增加注解@EnableEurekaClient
- Yml配置
启动时,先启动服务端,再启动客户端。启动完成后,访问http://localhost:7000,就可以看到已经注册的客户端服务。
服务名称指定:
Ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。
客户端负载均衡,区别于Nginx的服务端负载均衡。客户端负载均衡是访问端(消费端)会有一个服务器地址列表,在发送请求前,通过负载均衡算法选择一个服务器,然后进行访问。Spring cloud Ribbon默认负载均衡算法是轮询算法,一个服务有多台机器上运行时按顺序每次请求分发到各个服务器上。
服务器列表初始化:客户端服务器地址列表是在首次请求时,
由RibbonClientConfiguration-> ZoneAwareLoadBalancer->DynamicServerListLoadBalancer完成服务列表的初始化,并放入缓存,再次请求时,直接通过缓存或取。默认30秒自动更新一次服务列表。
配置步骤如下:
- Pom引入
- 负载均衡算法说明:
- Ribbon支持的内置均衡算法大概有以下几种:
- RoundRobinRule 轮询,默认算法
- RandomRule 随机
- AvailabilityFilteringRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数超过阈值的服务,然后对剩余的服务列表进行轮询。
- WeightedResponseTimeRule 权重 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时,如果统计信息不足,则使用轮询策略,等信息足够,切换到 WeightedResponseTimeRule。
- RetryRule 重试 先按照轮询策略获取服务,如果获取失败则在指定时间内重试,获取可用服务。
- BestAvailableRule 选过滤掉多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
- ZoneAvoidanceRule 符合判断server所在区域的性能和server的可用性选择服务。
- 自定义负载均衡算法:创建规则类,实现IRule接口。
- 指定负载均衡算法
- 编写配置类,配置类中返回需要的负载均衡算法
- 若所有请求服务采用相同负载均衡算法时,可以将配置类放在主启动类所在包下或者直接将配置类中的方法放到启动类中。
- 若需要为不同的请求服务指定不同的负载均衡算法时,配置类不可放在主启动类所在包内。
- 指定配置类
- 注解方式(采用Feign方式访问微服务)
@RibbonClient(name=”调用服务名”,configuration=RibbonConfig.class)
多个服务需要配置时,用@RibbonClients,
@RibbonClients(value={@RibbonClient(name="PROVIDER-1",configuration = RibbonConfig.class)})
- 配置文件配置
yml文件中增加配置:
服务名:(区分大小写)
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
配置文件和注解@RibbonClient同时指定时,注解优先
Feign
Feigh是spring cloud中服务消费端的调用框架,在Ribbon+RestTemplate的基础上做了进一步的封装,它的工作方式就是处理注解,通过java的动态代理机制,封装参数,放入到一个Http请求模板,并能解析返回的结果。
Feign底层也是利用Ribbon对服务列表进行维护,负载均衡也是通过Spring Cloud Ribbon实现的,所以负载均衡的配置和Ribbon的配置一样。
配置说明:
- Pom引入
- 启动类增加注解 @EnableFeignClients启用Feign的支持。
- 定义远程调用服务接口,并通过@FeignClient(value=“服务名”) 注解绑定服务。
接口中方法使用SpringMVC注解@RequestMapping、@PostMapping、@GetMapping等,注解value值必须与调用的远程服务方法上注解保持一致,参数一致,方法名可随意。
原理:动态代理类,和mybatis类似。
Hystrix
在微服务架构中多层服务之间会相互调用,如果其中有一层服务故障了,可能会导致一层服务或者多层服务故障,从而导致整个系统故障。这种现象被称为服务雪崩效应。
SpringCloud 中的 Hystrix 组件就可以解决此类问题,Hystrix 负责监控服务之间的调用情况,连续多次失败的情况进行熔断保护。保护的方法就是使用 Fallback,当调用的服务出现故障时,就可以使用 Fallback 方法的返回值;Hystrix 间隔时间会再次检查故障的服务,如果故障服务恢复,将继续使用服务。
状态说明:熔断->降级->限流
超过设定阀值而熔断,熔断后请求降级,降级之后的规定时间内直接返回降级信息
Hystrix会根据调用接口的情况,让熔断器在closed,open,half-open三种状态之间自动切换。
open状态说明打开熔断,也就是服务调用方执行本地降级策略,不进行远程调用。
closed状态说明关闭了熔断,这时候服务调用方直接发起远程调用。
half-open状态,则是一个中间状态,当熔断器处于这种状态时候,直接发起远程调用
配置说明:
- Pom引入
<!--引入Hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
- 启动类增加注解 @EnableHystrix启用Hystrix的支持
- 整合Hystrix熔断的两种方式:ribbon整合和feign整合
- ribbon整合
- 全局熔断
需要在controller类上加注解 @DefaultProperties(defaultFallback=”无参的熔断触发方法名”),熔断方法上加注解@HystrixCommand
- 局部熔断
方法上加注解,并指定与被调方法参数一致的参数。
@HystrixCommand(fallbackMethod=”testFallBack”)
Public String test(@RequestParam(“id”) String id){
return “”;
}
Public String testFallBack(String id){
Return ”服务正忙,请稍后再试”;
}
testFallBack方法的参数和返回值必须和test方法一致。
- Feign整合
- application.yml中开启熔断器
feign:
hysitrix:
enabled:true
-
- FeignClient接口中注解@FeignClient指定fallback或fallbackFactory
Fallback指定类如下
fallbackFactory指定类如下
使用fallbackFactory时,可以取得出发熔断的原因。
两种方式同时配置 Feign整合优先。
Zuul
网关有以下几个作用:
- 统一入口:未全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
- 鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。
- 动态路由:动态的将请求路由到不同的后端集群中。
- 减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。
Zuul做为一个服务,注册到eureka server。
访问方式:
http://zullHostIp:post/服务名称/服务中的Mapping
配置说明:
- pom引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<!-- zuul网关的重试机制,不是使用ribbon内置的重试机制
是借助spring-retry组件实现的重试
开启zuul网关重试机制需要增加下述依赖
-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
- 网关启动器,启动类增加注解@EnableZuulProxy
- 匹配方式
配置文件中需要为每个服务配置访问的路由规则,匹配规则如下:
- url匹配
zuul.routes.eureka服务名.path=/api/**
zuul.routes.eureka服务名.url=http://127.0.0.1:8080/
请求服务时,用 http://zuulip:zuulport/eureka服务名/api/
- 服务名称匹配
zuul.routes.eureka服务名=/api/**
zuul.routes.eureka服务名.serviceId=eureka服务名[j1]
请求服务时,用 http://zuulip:zuulport/eureka服务名/api/
- 路由排除配置
服务列表排除:
zuul.ignored-services=eureka服务1,eureka服务2…[j2]
zuul.ignored-services=* 排除所有
通配方式排除:
Zuul.ignored-patterns=/**/test/** 符合本格式的不被zuul代理
- 路由前缀配置
zuul_prefix=/api
zuul.routes.eureka服务.path/appservice/**
请求服务时,用 http://zuulip:zuulport /api/getName 将会被代理到
http://zuulip:zuulport /eureka服务名/getName
- Zuul网关过滤器
继承ZullFilter,实现filterType, filterOrder, shouldFilter, run四个方法。
filterType 四个选项
pre-前置过滤;
route-路由过滤;
post-路由后过滤;
error-异常是过滤
- Zuul网关容错
实现FallbackProvider接口,只针对timeout异常处理,只要服务有返回(包括异常),都不触发。
返回的ClientHttpResponse对象中,getHeader()方法必须指定contentType,否则无法正常响应客户端。
分布式事务(LCN)
LCN分布式事务分为服务端(TM)和客户端(TC),TM是一个独立的服务,做为分布式事务的管理中心。TC就是我们需要引入分布式事务的各个微服务。
- TM搭建
- pom.xml引入
- 启动类增加注解@EnableTransactionManagerServer 启用事务管理服务
- 配置文件
因为引入的jar包中有默认的application.properties配置文件,所以需要使用自己的配置时,必须重新创建application.properties文件,否则不生效。
Springboot中,properties文件和yml文件同时存在时,properties文件优先,所以如果使用yml文件,会优先显示默认的properties文件,yml不生效。
Properties文件内容如下:
Tx-manager需要一张数据表,所以需要数据库配置
- Eureka服务和Redis配置
TM搭建集群是,需要依赖eureka服务,用于TM服务之间的相互发现。
TM依赖Redis服务,用于存放事务组的信息及补偿信息。
启动tm时,必须先启动redis。
启动后界面:
密码输入配置文件中配置的tx-lcn.manager.admin-key的值
- TC配置
- pom.xml引入
- 启动类增加注解@EnableDistributedTransaction 启用客户端分布式事务
- 创建事务、参与事务
在需要启用分布式事务控制的方法上增加注解
@LcnTransaction/@TccTransaction/@TxcTransaction
分布式事务的传播行为只支持DTXPropagation.REQUIRED和DTXPropagation.SUPPORTS,默认为DTXPropagation.REQUIRED,可以不指定,事务控制器会根据方法的调用顺序,自动判断是事务发起者还是参与者。
- 原理
通过切片类 TransactionAspect 拦截标注分布式注解的方法,获取分布式类型,发起者、参与者等信息,通过反射获取具体的事务控制类(对应接口DTXLocalControl),事务发起类对应的事务控制类为Lcn/Tcc/TxcStartingTransaction,参与者对应的事务控制类为Lcn/Tcc/TxcRunningTransaction。
事务发起者控制类创建事务组,并通知Tx-manager,并把创建的groupId传递给事务参与者,
事务参与者加入事务组
最终的事务结果由事务发起者通知tx-manger
- LCN5.0.2有三种模式 lcn、tcc、txc模式
Lcn模式:
原理:
LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
特点:
- 该模式对代码的嵌入性为低。
- 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
- 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
- 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
事务控制器:
发起者:LcnStartingTransaction
参与者:LcnRunningTransaction
在TxLcnBeanHelper类中通过反射机制或取对应的类
注意:
- 只有分布式事务发起者才能向TM发起最终通知,各个参与者的postBusinessCode方法为空。
- 如果远程子事务出现业务异常,但被统一处理了,并最终向事务发起者返回统一的格式化数据(一般对外提供的服务都会有这个封装),则需要在事务发起者进行结果解析,根据解析的结果决定是否抛出异常。
最佳实践:
- 在FeignClient中(事务发起者端),新增拦截器,在拦截器中对返回结果进行解析,如果是错误,则直接抛出异常。
- DTXUserControls.rollbackCurrentGroup();//手动rollback
原理参见:https://blog.csdn.net/zhaocuit/article/details/102796735
示例代码