前言
SpringCloud入门,本文会继续上篇文章,对SpringCloud的组件做详细介绍,并对其使用方法做实例演示说明,本章主要是对SpringCloud的组件OpenFeign(负载均衡)、Hystrix(熔断器)、Zuul(服务网关)做详细介绍学习,欢迎评论区讨论交流。
目录
系列文章目录
目录
5.2、Fiegn接口熔断-fallbackFactory方式
第一章 负载均衡--OpenFeign
1、Feign介绍
Feign是一个声明式的http客户端,使用Feign可以实现声明式REST调用,它的目的就是让Web Service调用更加简单。OpenFeign是SpringCloud官方对Feign的升级,底层实现差不多一样.在上一章中我们使用的Ribbon负则负载均衡,但是使用的时候要去拼接url地址,对应请求交多的调用这样会很麻烦,所以使用Feign来负载均衡就可以解决这个问题。
Feign整合了Ribbon和SpringMvc注解,这让Feign的客户端接口看起来就像一个Controller。 Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息
。
同时Feign整合了Hystrix,可以很容易的实现服务熔断和降级(关于Hystrix我们后面再讲)。
2、使用Feign
以微服务用户服务、支付服务、已经用户为实例说明,我们以支付服务为例。
大致流程如下:
2.1、导入依赖
<!--1、导入Feign的包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.2、书写配置类
主配置类增加@EnableFeignClients标签
//2.开肩Feign的客户端,它会去扫描接口
//原理:肩动的时候,该注解会去扫描当前包及其子包,中的打了@FeignClient的接口,创建代理,叫给Spring管理
//执行的时候,接口中的每个方法,都会生成一个RequestTemplate,
//把该对象会交给一个Http客户端(比如:RestTemplate)去执行,
//底层走Ribbon的负载均衡算法,实现服务的调用
@EnableFeignClients
@SpringBootApplication
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class);
}
}
2.3、yml配置
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:10010/eureka/ #注册地址
instance:
instance-id: pay-server #实例ID
spring:
application:
name: pay-server #服务名
server:
port: 1040 #端口号
2.4、编写Feign的客户端接口
Feign的客户端接口是用来调用微服务的
@FeignClient("user-server") : user-server是用户服务的服务名字,Feign根据服务名能够在注册中心找到目标服务的通信地址,服务名要一致 , url路径要一致 , 参数要一致 , 返回值类型要一致。
// 该注解@FeignClient会去找到指定的服务名
@FeignClient(value = "user-server")
public interface UserFeignClients {
// 拷贝要请求的方法,调用会去找该服务的url和请求参数
@GetMapping("/user/{id}")
User userById(@PathVariable("id") Long id);
}
2.5、最后编写controller层接口
。。。
原理:
Feign,我们除了导入依赖之外,需要主配置类通过@EnableFeignClients
注解开启Feign。同时我们需要为Feign编写客户端接口,接口上需要注解@FeignClient
标签。 当程序启动,注解了@FeignClient的接口将会被扫描到然后交给Spring管理
。
当请求发起,会使用jdk的动态代理方式代理接口,生成相应的RequestTemplate
,Feign会为每个方法生成一个RequestTemplate同时封装好http信息,如:url,请求参数等等
最终RequestTemplate生成request请求,交给Http客户端
(UrlConnection ,HttpClient,OkHttp)。然后Http客户端会交给LoadBalancerClient,使用Ribbon的负载均衡发起调用。
3、Feign的参数配置
Feign已经集成了Ribbon,所以它的负载均衡配置基于Ribbon配置即可
user-server:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
配置Hystrix超时时间:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 #hystrix超时时间
4、Feign开启日志调试
有的时候我们需要看到Feign的调用过程中的参数及相应,我们可以对Feign的日志进行配置,Feign支持如下几种日志模式来决定日志记录内容多少:
-
NONE,不记录(DEFAULT)。
-
BASIC,仅记录请求方法和URL以及响应状态代码和执行时间。
-
HEADERS,记录基本信息以及请求和响应标头。
-
FULL,记录请求和响应的标题,正文和元数据。
4.2、创建配置类
@Configuration
public class FeignConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL; //打印Feign的所有日志
}
}
配置UserFeignClient的日志打印级别,上面的配置打印Feign的那些内容,下面这个是配置日志框架打印日志的级别,在yml中配置
logging:
level:
cn.itsource.springboot.feignclient.UserFeignClient: debug
第二章 Hystrix熔断器
Hystrix是国外知名的视频网站Netflix所开源的非常流行的高可用架构框架。Hystrix能够完美的解决分布式系统架构中打造高可用服务面临的一系列技术难题,如雪崩。
Hystrix是处理依赖隔离的框架,将出现故障的服务通过熔断、降级等手段隔离开来
,这样不影响整个系统的主业务,同时也是可以帮我们做服务的治理和监控
。
1、Hystrix功能如下:
1、资源隔离(流控,限流)
1.1、线程池隔离:使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量。
1.2信号量隔离:使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量。
2、服务熔断
熔断机制是对服务链路的保护机制,如果链路上的某个服务不可访问,调用超时,发生异常等,服务会触发降级返回托底数据,然后熔断服务的调用(失败率达到某个阀值服务标记为短路状态),当检查到该节点能正常使用时服务会快速恢复。
简单理解就是当服务不可访问了或者服务器报错了或者服务调用超过一定时间没返回结果,就立马触发熔断机制配合降级返回预先准备的兜底数据返回,不至于长时间的等待服务的相应造成大量的请求阻塞,也不至于返回一些错误信息给客户端,而是返回一些兜底数据。
3、降级机制
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
简单理解就是服务降级就是当服务因为网络故障,服务器故障,读取超时等原因造成服务不可达的情况下返回一些预先准备好的数据给客户端。
4、缓存
提供了请求缓存、请求合并实现 , 在高并发的场景之下,Hystrix请求缓存可以方便地开启和使用请求缓存来优化系统,达到减轻高并发时请求线程的消耗、降低请求响应时间的效果。
5、OpenFeign使用Hystrix
5.1、开启Hystrix
feign:
hystrix:
enabled: true #开启hystrix服务,默认关闭
5.2、配置类开启Hystrix
主配置类通过 @EnableCircuitBreaker 标签开启熔断功能
5.2、Fiegn接口熔断-fallbackFactory方式
使用fallbackFactory属性,使用工厂方式指定托底,
fallbackFactory:降级方法 在访问userById方法出现错误时执行该降级方法
@FeignClient(value = "user-server",fallbackFactory = UserFeignClientsFallbackFactory.class)
5.3、编写托底类
工程方式的托底类需要去实现 FallbackFactory接口 ,并指定泛型为“”Feign客户端接口(UserFeignClient )。FallbackFactory的create方法返回了Feign客户端接口的实例,该方法的throwable是参数是Feign调用失败的异常信息,如下:
/*降级方法 UserFeignClients中的方法出现错误时执行 需要固定格式实现FallbackFactory接口 泛型为需要降级的接口类型*/
@Component
public class UserFeignClientsFallbackFactory implements FallbackFactory<UserFeignClients> {
// chon
@Override
public UserFeignClients create(Throwable throwable) {
return new UserFeignClients() {
// 匿名内部类->降级方法
@Override
public User userById(Long id) {
// 控制台打印错误信息
throwable.printStackTrace();
// 降级方法返回的提示消息
return new User(1L,"","服务错误,请稍后重试!");
}
};
}
}
第三章 Zuul 服务网关
1、Zull介绍
Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet(filter)应用。Zuul 在云平台上提供动态路由(请求分发),监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门,也要注册入Eureka,用一张图来理解zuul在架构中的的角色:
需要注意的是,zuul本身是一个独立的服务,默认集成了Ribbon,zuul通过Ribbon将客户端的请求分发到下游的微服务,所以zuul需要通过Eureka做服务发行,同时zuul也集成了Hystrix。
根据上图理解 ,我们需要建立独立的工程去搭建Zuul服务,同时需要把Zuul注册到EurekaServer,因为当请求过来时,zuul需要通过EurekaServer获取下游的微服务通信地址,使用Ribbon发起调用。
2、Zull使用
2.1、导入依赖
<!--整合zuul的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2.2、启动类
@EnableZuulProxy : 开启zuul 可以看做是 @EnableZuulServer 的增强版 ,一般用这个
@EnableZuulServer : 这个标签也可以开启zuul,但是这个标签开启的Filter更少
<!--整合zuul的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2.3、配置文件配置
我们对zuul主要做了三个配置
-
zuul.prefix : 作为统一的前缀,在浏览器访问的时候需要加上该前缀
-
zuul.ignoredServices : 忽略使用服务名方式访问服务,而是通过routes指定的路径进行访问
-
zuul.routes : 配置服务的访问路径
#zuul配置
zuul:
prefix: "/service" #配置前置
ignored-services: "*" #禁用掉使用浏览器通过服务名的方式访问服务
routes: #配置路由 别名
user-servce: "/user/**"
pay-servce: "/pay/**"
order-server: "/order/**"
3、zuul的工作原理
zuul的底层是通过各种Filter来实现的,zuul中的filter按照执行顺序分为了“pre”前置(”custom”自定义一般是前置),“routing”路由,“post”后置,以及“error”异常Filter组成,当各种Filter出现了异常,请求会跳转到“error filter”,然后再经过“post filter” 最后返回结果,下面是Filter的执行流程图:
提示:
-
正常流程:
-
请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
-
-
异常流程:
-
整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
-
如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
-
如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。
-
Zuul提供了一个抽象的Filter:ZuulFilter我们可以通过该抽象类来自定义Filter,该Filter有四个核心方法,如下:
-
filterType :是用来指定filter的类型的(类型见常量类:FilterConstants)
-
filterOrder :是filter的执行顺序,越小越先执行
-
shouldFilter :是其父接口IZuulFilter的方法,用来决定run方法是否要被执行
-
run :是其父接口IZuulFilter的方法,该方法是Filter的核心业务方法
自定义Filter 实例:
在Zuul层实现统一的登录检查:如果请求头中有“token”属性,我们就认为已经登录成功,可以继续往下游的服务执行,否则就视为请求未登录,直接返回错误信息,这一需求需要自定义Filter继承ZuulFilter类来实现:
/*zuul中的Filter使用*/
@Component
public class LoginZuulFinter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
// 获取请求对象
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String uri = request.getRequestURI();
// 判断
if (uri.contains("/login")){
return false;
}
return true;
}
@Override
public Object run() throws ZuulException {
// 获取请求对象
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
//响应对象
HttpServletResponse response = currentContext.getResponse();
// 获取请求头中的token
String token = request.getHeader("token");
// 如果没有token,登录检查失败
if (!StringUtils.hasLength(token)){
//中文编码
response.setContentType("application/json;charset=utf-8");
try {
// 阻止filter继续往后执行
currentContext.setSendZuulResponse(false);
response.getWriter().println("滚去登录");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
注意:
-
在 filterType方法中我们返回“pre”前置filter的常量,让他成为前置filter(登录检查需要在请求的最前面来做)
-
在filterOrder方法中返回的顺序值是 1 ,执行顺序越小越先执行
-
在shouldFilter方法中通过判断请求的url来决定是否需要做登录检查,返回true就是要做然后才会执行run方法
-
在run方法中我们通过获取请求头中的token判断是否登录,如果没登录就返回错误信息,阻止继续执行。
-
RequestContext.getCurrentContext() 是一个Zuul提供的请求上下文对象
总结
本文对SpringCloud做了简单介绍,并对SpringCloud的常用组件OpenFeign(负载均衡)、Hystrix(熔断器)、Zuul(服务网关)做详细介绍和它们的使用流程,文章如有雷同纯属巧合,如需转载请私信联系,本文为Java技术小白,日常分享学习心得,欢迎您的探讨和交流。