简述

写博客记笔记违背了我的初心:记录学到的技术的使用,以免以后使用的时候忘记,
现在的情况是,自己记录的笔记并不完整,很多地方写的不好,而随手百度就能比我写的更完善更好,
写博客还需要大量时间,于此对比,自己写博客就显示很不值得,但是我并不想放弃写博客,不然学的东西
没有产出,打击学习积极性,所以我决定在自己的博客中使用别人写的优质博客,做知识的整理者,归纳者
而不生产创造优质文章(我这实力也创造不了),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);
        }
    }

最后是各个大佬的文章,网关还有很多限流,鉴权等等操作,没接触过,就不乱写了,看下大佬博客