简述
写博客记笔记违背了我的初心:记录学到的技术的使用,以免以后使用的时候忘记,
现在的情况是,自己记录的笔记并不完整,很多地方写的不好,而随手百度就能比我写的更完善更好,
写博客还需要大量时间,于此对比,自己写博客就显示很不值得,但是我并不想放弃写博客,不然学的东西
没有产出,打击学习积极性,所以我决定在自己的博客中使用别人写的优质博客,做知识的整理者,归纳者
而不生产创造优质文章(我这实力也创造不了),OK,通过看别人写的优质文章,不仅提升自己,
而且节约时间,以后某个技术不记得怎么使用了来查查就行,最后还算是产出不打击学习积极性,完美~
本章内容
OpenFeign
使用注解方式发送服务调用的HTTP客户端,内置了ribbon负载均衡工具,hystrix熔断器
OpenFeign与Feign的区别见下图:

基本使用
声明式
假设场景:消费者调用生产者的controller接口,那么openFeign就需要配置在消费者端,以提供服务调用,来看下使用步骤:1,导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--服务注册中心用eureka还是nacos随意--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
2,在消费者主类上标记注解
@SpringBootApplication @EnableDiscoveryClient //注册中心 @EnableFeignClients //openfeign注解,该注解的basePackages属性可以指定扫描包路径 public class ProviderApplication { public static void main(String[] args) throws Exception { SpringApplication.run(ProviderApplication.class, args); } }
3,消费者端增加一个接口,ProviderClient接口
//会根据服务名,从注册中心找到对应的IP地址,name值表示生产者的服务名称 @FeignClient(name = "provider") public interface ProviderClient { //这里跟服务生产者controller接口的URL一致,调用生产者的/provider/list @RequestMapping("/provider/list") String list(); }
4,定义好上述的接口,然后消费者的controller处调用上面接口的方法
@RestController public class ConsumerController { //引入Feign客户端 @Resource private ProviderClient providerClient; @RequestMapping("/consumer/callProvider") public String callProvider() { //使用Feign客户端调用其他服务的接口,底层自动调用生产者的controller,拿到数据 return providerClient.list(); } }
继承式
上面就是声明式使用openfeign,我们会发现消费者定义的ProviderClient接口,下面的方法其实和
生产者的controller方法一样,使用同样的注解,那么我们可以定义一个模块,把ProviderClient接口
写在那个模块中,然后生产者的controller类实现该模块的ProviderClient接口,消费者的接口继承
ProviderClient接口,这样,我们只需在ProviderClient接口定义方法和注解,生产者实现细节,
消费者继承接口后,消费者自己的接口也拥有了ProviderClient接口的方法但是这样做让这三个模块耦合在一起了,感觉非常不好,具体代码,请在最下面链接处查看
超时控制
feign内部包含了ribbon和hystrix,所以我们需要设置ribbon的超时时间和hystrix的熔断超时时间。
所以feign的超时控制指的是配置这两个工具的超时时间,下面是一个样例:feign: client: config: default: #全局设置, # ribbon的超时时间,默认等待1秒,得不到爆错 connectTimeout: 5000 # 指的是建立连接所用的时间,适用于网路状况正常情况下,两端连接所用的时间 readTimeout: 5000 # 指的是建立连接后从服务器读取到可用资源所用的时间 hystrix: enabled: true # 开启熔断器,默认包装请求接口的所有方法,并将这些方法过期时间设置为1秒,需要进行下面的配置 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 10000 # 设置hystrix的超时时间为10000ms
如何配置好Hystrix和Ribbon的超时时间呢?
因为Feign的请求:其实是Hystrix+Ribbon。Hystrix在最外层,然后再到Ribbon,最后里面的是http请求
所以说。Hystrix的熔断时间必须大于Ribbon的 ( ConnectTimeout + ReadTimeout )。而如果Ribbon开启了重试机制
还需要乘以对应的重试次数,保证在Ribbon里的请求还没结束时,Hystrix的熔断时间不会超时。关于更多超时控制,请看最下面的链接
日志增强
日志配置分为全局配置和局部配置,分为代码实现和配置实现,这里都是配置实现,代码查看下面链接局部配置日志级别
局部配置指的是针对请求某个微服务时,设置特定的日志级别,而其他的日志级别使用默认feign: client: config: #想要调用的微服务名称,server1指的是其他微服务的服务名称 server-1: loggerLevel: FULL
全局配置日志级别
全局配置日志级别顾名思义,对所有微服务请求时,都使用该日志级别feign: client: config: #将调用的微服务名称改成default就配置成全局的了 default: loggerLevel: FULL
负载均衡配置
openfeign中的负载均衡配置,就是ribbon的负载均衡的配置服务名称: #这里填微服务的服务名称 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置规则 随机 #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询 #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置规则 重试 #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重 #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略
更多文章
Hystrix
在微服务架构下,服务调用的链路可能会非常长,服务A调用服务B,服务B调用服务C,,,
一次请求调用的链路越长,那么一旦链路中某个服务不可用,或者该服务执行时间太久,
则会造成整个请求时间变长,即一粒老鼠屎坏了一锅粥,我调用你的服务,你服务不能用或者你服务处理太慢
你得告诉我一声,总之,我不管你服务是好是坏,我就要及时得到反馈。
此时Hystrix,豪猪哥出现了,专门解决一旦某个服务接口不可用或延迟高,仍然能给对方一个反馈
一般出现在并发量比较大的场景,当一个接口并发量特别大,会导致tomcat的工作线程全都处理该接口下的请求
使得其他接口响应也会变慢,一句话,我提供的接口如果其中一个接口非常多请求,我的其他接口响应速度下降
那我就要降低这个接口的请求量,怎么降低?接口并发量大的具体表现是该接口响应变慢,我设定,只要该接口
响应延迟特别慢或者该接口内报错,我直接响应一个后备接口,这就是服务降级,这就是上面所说的反馈。
后备方法里面,一般写该服务不可用,你一直请求别人,接收到的都是服务不可用,自然请求量就降下来了
请求量降下来,接口处理速度就上来了,处理接口速度上来了,自然就不走后备方法,转成去执行正常的接口了
除了服务降级,你还可能听说过服务熔断,它指的是:我根据请求量进行统计,一旦执行后备方法多了,
我直接让所有请求全部走后备方法,啥也不处理了,直接躺平,这就是服务熔断,等一段时间后,
我放一个请求进来,看接口正不正常(延迟小,不报错),如果正常,我就关闭服务熔断,让接口正常处理请求,否则继续走我的后备方法。
hystrix服务熔断的状态可以用一个断路器表示,如果服务被熔断了,则处于断路器打开状态,
此时服务正常业务接口不可用,走后备方法,过了一段时间,熔断器自动变成半开状态,这一状态下,
hystrix会监测服务接口是否正常,如果正常,则断路器关闭,服务熔断状态关闭,服务业务接口正常处理请求。
下面介绍简单的使用方式,更多的内容看最下方的链接
导入依赖
<!--新增hystrix,不过openfeign包含了hystrix依赖,可以不用引入--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--本次实验openfeign和hystrix一起使用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
添加hystrix注解在模块主类上
@EnableFeignClients @SpringBootApplication @EnableDiscoveryClient @@EnableCircuitBreaker //用于hystrix服务熔断注解,如果只使用服务降级,不需要该注解 //友情提示:@SpringCloudApplication可代替下面三个注解 public class MyConsumerFeignWithHystrixOrder80 { public static void main(String[] args) { SpringApplication.run(MyConsumerFeignWithHystrixOrder80.class,args); } }
实验是openfeign配合hystrix使用,则我们也需要配置ribbon的超时时间
feign: client: config: default: #全局设置, # ribbon的超时时间,默认等待1秒,得不到爆错 connectTimeout: 5000 # 指的是建立连接所用的时间,适用于网路状况正常情况下,两端连接所用的时间 readTimeout: 5000 # 指的是建立连接后从服务器读取到可用资源所用的时间
服务降级的配置可以配置在消费方也可以配置在生产方,因为消费者也可能是生产者,
下面服务降级的配置,配置在消费方的controller中//消费方的controller @RestController @Slf4j @RequestMapping("/order/feign/hystrix") //@DefaultProperties(defaultFallback = "globalFallback") //配置全局的降级方法/后备方法 public class MyFeignController { @Resource private MyFeignWithPayment myFeignWithPayment; //消费方的接口,直接使用即可拿到其他服务数据 @GetMapping("/ok/{id}") // @HystrixCommand //如果没有指定fallback,则走全局降级方法,这样就无需每个业务方法都写个fallback方法 public String getPaymentInfo_OKTest(@PathVariable("id")String id){ System.out.println("执行order模块中的okTest方法"); return myFeignWithPayment.getPaymentInfo_OKTest(id); } @GetMapping("/timeout/{id}") @HystrixCommand(fallbackMethod = "order_timeoutOrError",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value ="3000") }) //3秒内,该注解的方法没有执行完毕,则走降级方法,表示消费者不等生产方提供数据了 public String getPaymentInfo_NotOKTest(@PathVariable("id")String id){ System.out.println("执行order模块中的notOKTest方法"); String result = myFeignWithPayment.getPaymentInfo_NotOKTest(id); //调用其他服务 System.out.println(result); System.out.println("你是啥?"+result); return result; } /*当上面服务处理失败后(运行异常,超时异常),回调下面的方法,进行兜底操作,即指定的降级方法*/ public String order_timeoutOrError(String id){ log.info(id); return "订单接口异常,当前线程名:"+Thread.currentThread().getName(); } /*全局降级方法,只要标注了@HystrixCommand注解,但没有指定fallback,就执行全局的降级方法*/ public String globalFallback(){ log.info("进入全局的熔断方法"); return "方法执行异常。。。。"; } } /* 你可以指定fallback降级方法,如果使用了@HystrixCommand注解,不指定fallback方法,则执行全局的降级方法 */
默认情况下,使用@HystrixCommand注解会导致该接口hystrix的超时时间为1秒,你需要在配置文件中配置
#在新版本的Springcloud中,Feign默认关闭了对Hystrix的支持 feign hystrix: enabled: true # 开启hyxtrix,默认包装请求接口的所有方法,并将这些方法过期时间设置为1秒,需要进行下面的配置 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 # 设置hystrix的超时时间为6000ms
上面是服务降级的配置,下面来看看服务熔断的配置,本案例将服务熔断配置在生产方的service中
//服务熔断指的是,如果短时间内有较多的方法被服务降级,则触发器状态,使得之后一定时间内,任何请求访问的都是降级方法 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = { //是否开启断路器 @HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //请求次数,默认10秒内大于20次请求则进行统计 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //统计的时间范围 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //失败率达到多少后跳闸,10秒内fallback到60% @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), }) public String paymentCircuitBreaker(Integer id){ if (id < 0){ throw new RuntimeException("*****id 不能负数"); } String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber; } //降级方法 public String paymentCircuitBreaker_fallback(Integer id){ return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id; }
hystrix还有很多内容,请看:hystrix原理
Gateway
spring cloud Gateway,服务网关,内外系统的中间门户,用户发出请求,到达nginx集群,然后nginx将请求负载到Gateway网关集群
网关再对请求做一些认证,鉴权,监控,负载均衡,流量管控等等,最后将请求转发给各个微服务中。
网关分为流量网关和业务网关,nginx就是流量网关,而springCloud Gateway就是业务网关,至于其具体区别
大概可以看下下图:然后他们各自做的事,看下大佬写的博客,在最下面
再来看看几张关于网关的图,了解一下它在微服务中扮演的角色:
大佬的博客写的太好,我这里就简单写写配置吧:
导入依赖,菜狗是这样的,导依赖,写配置,加注解三板斧~~
<!--gateway当然也是需要注册进服务注册中心的,eureka/nacos/...--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
配置网关,gateway中路由,断言,过滤器是基本操作
server: port: 9527 spring: application: name: MyGateway cloud: gateway: routes: #下面是配置的一个个路由,即转发规则和转发微服务地址,满足断言规则,则转发请求 - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名 # uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://MyProviderPayment #开启服务发现后,只用服务名称进行请求转发 predicates: # 断言,满足下面的条件才转发请求 - Path=/payment/eureka/** #断言,路径相匹配的进行路由 filters: #路由实例过滤器,仅对该路由有效 - AddRequestHeader=Truth,zhangsan is freaking awesome! - id: payment_routh2 # uri: http://localhost:8001 uri: lb://MyProviderPayment #使用服务名进行路由,需要开启discovery.locator.enabled=true predicates: - Path=/payment/eureka/lb #断言,路径相匹配的进行路由 - After=2021-10-26T22:48:16.597+08:00[Asia/Shanghai] discovery: locator: enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由 default-filters: #默认过滤器,作用于每个路由 - After=2021-10-26T22:48:16.597+08:00[Asia/Shanghai] # 这里是默认路由作用每一个路由 # 注意:路由实例过滤器(配置在路由里面),defaultFilter,globalFilter,这三个路由器是根据order排序的 # 如果order值相同,则defaultFilter>路由实例过滤器>globalFilter
上面是通过配置进行网关的路由功能,下面一个案例是使用编码的方式,设置路由规则,仅作了解,不太懂
@Configuration public class GatewayConfig { /* * 网关的配置可以通过编码方式,也可以通过配置application.xml方式,这里是通过编码方式 * */ @Bean public RouteLocator toBlog(RouteLocatorBuilder routeLocatorBuilder){ RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); routes.route("keyi.world",r->{ //这里有问题,网关只是替换了域名和端口,但是后面路径不替换,因为我博客后没有/keyi路径,所以404 return r.path("/keyi").uri("http://www.keyi.world"); }); return routes.build(); } }
项目中往往需要自定义过滤器,而不是使用网关中提供给我们的过滤器,下面是一个案例实现全局过滤器接口
@Component @Order(-1) //数字越小,优先级越高 @Slf4j public class MyLogGatewayGlobalFilter implements GlobalFilter { /** * 自定义的网关过滤器 * @param exchange * @param chain * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("********************进入自定义网关全局过滤器,time:"+new Date()); String wanyi = exchange.getRequest().getQueryParams().getFirst("wanyi"); if (Objects.isNull(wanyi)){ log.info("wanyi为null,不允许进入系统"); exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //返回状态码401,表示未登录 return exchange.getResponse().setComplete(); } return chain.filter(exchange); //请求放行,执行下一个过滤器 } }
再来看看Gateway中是如何配置跨域的吧
spring: cloud: gateway: # 。。。 globalcors: # 全局的跨域处理 add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题 corsConfigurations: '[/**]': allowedOrigins: # 允许哪些网站的跨域请求 - "http://localhost:8090" allowedMethods: # 允许的跨域ajax的请求方式 - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" # 允许在请求中携带的头信息 allowCredentials: true # 是否允许携带cookie maxAge: 360000 # 这次跨域检测的有效期
当然你也可以通过编码方式实现跨域,例如自定义过滤器解决跨域
@Configuration public class CorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedMethod("*"); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } }
最后是各个大佬的文章,网关还有很多限流,鉴权等等操作,没接触过,就不乱写了,看下大佬博客