feign&gateway

feign
	feign的作用
	feign的用法
	feign整合ribbon
	feign整合hystrix
	feign的优化
gateway
	gateway的作用
	gateway的路由
	gateway的过滤器

Feign的简介

SpringCloud提供的声明式的REST客户端,实现远程的服务的调用,只需要编写接口和SpringMVC的注解就能完成调用。

Feign = RestTemplate + Ribbon + Hystrix

Feign的用法

  1. 引入依赖 openfeign
  2. 在启动类上加 @EnableFeignClients(“Feign接口所在的包名”)
  3. 编写Feign接口,在接口上加@FeignClient(“被调用的服务名”)
  4. 在Feign接口中编写方法(对应提供者的方法),方法可以使用SpringMVC的注解
/**
 * 调用商品服务的Rest客户端
 */
@FeignClient("product-service")
public interface ProductFeignClient {

    /**
     * 调用商品服务的查询方法
     */
    @GetMapping("/product/{id}")
    ResponseEntity<Product> findProductById(@PathVariable("id")Long id);
}
  1. 注入接口对象,通过方法实现远程调用
    @Autowired
    private ProductFeignClient productFeignClient;

    @GetMapping("/order/{id}")
    public ResponseEntity<Order> findOrderById(@PathVariable("id")Long id){
        //模拟订单数据
        Order order = new Order(id,99L,null);
        //调用商品服务获得商品
        ResponseEntity<Product> entity = productFeignClient.findProductById(8L);
        order.setProduct(entity.getBody());
        return ResponseEntity.ok(order);
    }

练习:给商品服务编写增删改查的所有方法,在订单服务中通过Feign分别进行调用,在单元测试中调用

Feign的原理

实现步骤:

  1. 在启动类上加@EnableFeignClients启动Feign的功能,就会启动对Feign接口的扫描
  2. 扫描到@FeignClient注解接口,读取接口上的服务名、URL,包装后发给IOC容器
  3. 由IOC容器通过动态代理机制创建RequestTemplate对象,发送Http请求,交给Http客户端处理
  4. Http客户端被封装到LoadBalanceClient负载均衡客户端中,交给Ribbon处理

Feign整合Ribbon

Feign默认整合了Ribbon实现负载均衡

  • 全局配置

    ribbon.属性 = 值

  • 指定服务配置

    服务名称.ribbon.属性 = 值

Ribbon常用配置:

  • ConnectionTimeout 连接超时

  • ReadTimeout 读取超时

  • OkToRetryOnAllOperations 对所有操作启动重试机制 true/false

  • MaxAutoRetries 最大重试次数

  • MaxAutoRetriesNextServer 最大重试下个服务器次数

Feign整合Hystrix

Feign默认整合了Hystrix的熔断机制

Feign可以单独定义类,编写降级方法

实现:

1)定义降级处理类,继承对应的FeignClient接口

2)实现接口中的方法,所有方法均为回退方法

3)在FeignClient注解中配置降级方法处理类

fallback = GoodsServiceFallback.class

4)启动Feign的Hystrix特性

feign.hystrix.enabled=true

/**
 * goods-service的调用的降级处理类
 */
@Component
public class GoodsFeignClientFallback implements GoodsFeignClient {

    @Override
    public ResponseEntity<Goods> getGoodsById(Long id) {
        return ResponseEntity.ok(new Goods(-1L,"降级数据",0.0));
    }

    ...
}
@FeignClient(value = "goods-service",fallback = GoodsFeignClientFallback.class)
public interface GoodsFeignClient {...}

Feign的优化配置

Feign的优化配置

1. 优化连接池

Feign是基于HTTP协议,HTTP协议基于TCP协议,TCP具有三次握手四次挥手机制,频繁的创建连接比较消耗系统的资源和时间,降低系统的吞吐量。

使用连接池可以减少网络连接的创建,提高连接的使用效率,提高系统吞吐量。

Feign默认使用JDK自带的HttpURLConnection,没有连接池。可以使用HttpClient或OKHTTP

使用步骤:

1)导入httpclient、feign-httpclient依赖

2)feign.httpclient.enable = true

2. 请求压缩优化

Feign可以对网络数据进行GZIP压缩,减少网络流量,提高速度

启动压缩:

#启动请求压缩 
feign.compression.request.enabled=true 
#启动响应压缩 
feign.compression.response.enabled=true 
#设置请求压缩的多媒体类型 
feign.compression.request.mime-types=text/html,application/xml,application/json 
#设置请求压缩的下限 (默认2048)
feign.compression.request.min-request-size=1024 

3. 使用日志对连接过程进行监控

1)开启debug日志

logging.level.包名.FeignClient接口名 = DEBUG

2)定义配置类,配置日志

/** Feign日志配置  */ 
@Configuration 
public class FeignLoggerConfig {      
    /**  返回日志类型,NONE没有日志,BASIC基本信息(请求方法,URL,响应代码等),HEADERS(头部信息),FULL(基本+头部)*/     
    @Bean     
    public Logger.Level level(){         
    	return Logger.Level.BASIC;     
    } 
} 

4. 超时优化

Ribbon是具有重试机制的,就是连接失败后进行重连,问题是:Hystrix的超时时间默认是小于Ribbon的超时,Hystrix会提前熔断,Ribbon无法进行重试

Hystrix的超时要大于Ribbon的超时

# hystrix的超时 
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000 
# ribbton的超时 
ribbon.ConnectionTimeout=1000 
ribbon.ReadTimeout=3000

Gateway的简介

Gateway是SpringCloud中的API网关,提供鉴权和路由的功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ycZSkm0I-1654905284481)(feign&gateway.assets/image-20211215152351396.png)]

Gateway的工作机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7bh8tJDW-1654905284482)(feign&gateway.assets/image-20211215152426903.png)]

  1. 客户端发送请求给Gateway网关,网关将请求发送给处理器映射(HandlerMapping)
  2. 网关通过路由的匹配,将请求发送给Web处理器处理,请求就需要经过一系列过滤器
  3. 过滤器分为“pre"前置和”post"后置两种,前置过滤器实现鉴权作用,后置过滤实现性能统计或日志收集
  4. 通过过滤器就到达需要的服务

Gateway的路由

GateWay路由规则

时间点后匹配

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: https://example.org
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]

时间点前匹配

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

时间区间匹配

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

指定Cookie正则匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

指定Header正则匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

请求Host匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

请求Method匹配指定请求方式

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

请求路径正则匹配

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

请求包含某参数

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

请求包含某参数并且参数值匹配正则表达式

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=red, gree.

远程地址匹配

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

使用注册中心时,uri以 lb: //开头(lb代表从注册中心获取服务),后面是需要转发到的服务名称

使用方法:

1)创建网关项目, 引入eureka-client和gateway依赖

2)编写配置

server:
  port: 9999
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes:
        - id: goods-service-route
          uri: lb://goods-service
          predicates:
            - Path=/goods/**
        - id: order-service-route
          uri: lb://order-service
          predicates:
            - Path=/order/**,/orders/**
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    serviceUrl:
      defaultZone: http://localhost:8888/eureka

Gateway的过滤器

从过滤器生命周期(影响时机点)的角度来说,主要有两个pre和post:

生命周期时机点作用
pre这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
post这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

从过滤器类型的角度,Spring Cloud GateWay的过滤器分为GateWayFilter和GlobalFilter两种

过滤器类型影响范围
GateWayFilter应用到单个路由上
GlobalFilter应用到所有的路由上
/**
 * 自定义全局过滤器,用于用户权限验证
 */
@Slf4j
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {

    //过滤请求和响应
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获得请求和响应
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获得请求参数 如果有token=123456 就表示登录
        String token = request.getQueryParams().getFirst("token");
        log.info("url==>"+request.getURI());
        log.info("token==>"+token);
        if(!"123456".equals(token)){
            //如果没有参数token=123456,拒绝访问
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            String msg = "Request Denied!!";
            DataBuffer wrap = response.bufferFactory().wrap(msg.getBytes());
            return response.writeWith(Mono.just(wrap));
        }
        //如果有就放行
        return chain.filter(exchange);
    }

    //返回过滤器顺序,越小越靠前
    @Override
    public int getOrder() {
        return 0;
    }
}

作业

使用Vue展示订单数据(包含商品)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ucdh3YgD-1654905284483)(feign&gateway.assets/image-20211215164851927.png)]

[


版权声明:本文为weixin_44953152原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。