zoukankan      html  css  js  c++  java
  • 87--spring cloud (Feign声明式客户端接口/集成)

    Feign 声明式客户端接口

    Fegin是一个集成工具,它集成了

    • 远程调用
    • ribbon
    • hystrix

    微服务应用中,ribbon 和 hystrix 总是同时出现,feign 整合了两者,并提供了声明式消费者客户端

    用 feign 代替 hystrix+ribbon

    feign

    创建项目-远程调用

    导入依赖pom.xml

    actuator,web,netflix-eureka-client,netflix-hystrix,openfeign

    需要添加 sp01-commons 依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>cn.tedu</groupId>
        <artifactId>sp09-feign</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>sp09-feign</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <spring-cloud.version>Hoxton.SR7</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>cn.tedu</groupId>
                <artifactId>sp01-commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    application.yml

    spring:
      application:
        name: feign
        
    server:
      port: 3001
      
    eureka:
      client:
        service-url:
          defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
    
    

    主程序添加 @EnableFeignClients

    package cn.tedu.sp09;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    @EnableCircuitBreaker //启动断路器
    @EnableFeignClients //启用feign客户端
    @SpringBootApplication
    public class Sp09FeignApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Sp09FeignApplication.class, args);
        }
    }
    

    feign 声明式客户端

    feign 利用了我们熟悉的 spring mvc 注解来对接口方法进行设置,降低了我们的学习成本
    通过这些设置,feign可以拼接后台服务的访问路径和提交的参数

    例如:

    @FeignClient(value = "user-service")
    public interface UserFeignClient {
        @GetMapping("/{userId}/score")
        JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
    
    }
    

    当这样调用该方法:

    @Autowired
    private UserFeignClient userFeignClient;
    //在控制层进行调用声明式接口
    @GetMapping("/user-service/{userId}/score")
    public  JsonResult addScore(@PathVariable Integer userId,Integer score){
        log.info("调用远程用户服务,新增积分");
        return  userFeignClient.addScore(userId,score);
    }
    

    那么 feign 会向服务器发送请求:

    http://用户微服务/7/score?score=100
    
    • 注意:如果 score 参数名与变量名不同,需要添加参数名设置:
    @GetMapping("/{userId}/score") 
    JsonResult addScore(@PathVariable Integer userId, @RequestParam("score") Integer s);
    

    ItemFeignClient

    package cn.tedu.sp09.feign;
    
    import cn.tedu.sp01.pojo.Item;
    import cn.tedu.sp09.feign.impl.ItemFeignClientFB;
    import cn.tedu.web.util.JsonResult;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    
    import java.util.List;
    @FeignClient(name = "item-service" ,fallback = ItemFeignClientFB.class )
    public interface ItemFeignClient {
    
        @GetMapping("/{orderId}")
        JsonResult<List<Item>> getItems(@PathVariable String orderId);
    
        @PostMapping("/decreaseNumber") //post提交的参数要放在请求协议体中 使用参数@RequestBody
        JsonResult decrease(@RequestBody List<Item> items);
    
    }
    

    UserFeignClient

    注意,如果请求参数名与方法参数名不同,@RequestParam不能省略,并且要指定请求参数名:
    @RequestParam("score") Integer s

    package cn.tedu.sp09.feign;
    
    import cn.tedu.sp01.pojo.User;
    import cn.tedu.sp09.feign.impl.UserFeignClientFB;
    import cn.tedu.web.util.JsonResult;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @FeignClient(value = "user-service",fallback = UserFeignClientFB.class)
    public interface UserFeignClient {
    
        @GetMapping("/{userId}")
        JsonResult<User> getUser(@PathVariable Integer userId);
    
        @GetMapping("/{userId}/score")
        JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
    }
    

    OrderFeignClient

    package cn.tedu.sp09.feign;
    
    import cn.tedu.sp01.pojo.Order;
    import cn.tedu.sp09.feign.impl.OrderFeignClientFB;
    import cn.tedu.web.util.JsonResult;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    @FeignClient(name = "order-service",fallback = OrderFeignClientFB.class)
    public interface OrderFeignClient {
    
        @GetMapping("/{orderId}")
        JsonResult<Order> getOrder(@PathVariable String orderId);
    
        @GetMapping("/")
        JsonResult addOrder();
    }
    

    FeignController

    package cn.tedu.sp09.controller;
    
    import cn.tedu.sp01.pojo.Item;
    import cn.tedu.sp01.pojo.Order;
    import cn.tedu.sp01.pojo.User;
    import cn.tedu.sp09.feign.ItemFeignClient;
    import cn.tedu.sp09.feign.OrderFeignClient;
    import cn.tedu.sp09.feign.UserFeignClient;
    import cn.tedu.web.util.JsonResult;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    @RestController
    @Slf4j
    public class FeignController {
        @Autowired
        private ItemFeignClient itemFeignClient;
        @Autowired
        private UserFeignClient userFeignClient;
        @Autowired
        private OrderFeignClient orderFeignClient;
    
        @GetMapping("/item-service/{orderId}")
        public JsonResult<List<Item>> getItems(@PathVariable  String orderId){
            log.info("调用远程商品服务,获取订单列表");
            return itemFeignClient.getItems(orderId);
        }
    
        @PostMapping("/item-service/decreaseNumber")
        public  JsonResult decreaseNumber(List<Item> items){
            log.info("调用远程商品服务,减少库存");
            return  itemFeignClient.decrease(items);
        }
        @GetMapping("/user-service/{userId}")
        public JsonResult<User> getUser(@PathVariable  Integer userId){
            log.info("调用远程用户服务,获取用户信息");
            return userFeignClient.getUser(userId);
        }
    
        @GetMapping("/user-service/{userId}/score")
        public  JsonResult addScore(@PathVariable Integer userId,Integer score){
            log.info("调用远程用户服务,新增积分");
            return  userFeignClient.addScore(userId,score);
        }
        @GetMapping("/order-service/{orderId}")
        public JsonResult<Order> getOrder(@PathVariable  String orderId){
            log.info("调用远程订单服务,获取订单");
            return orderFeignClient.getOrder(orderId);
        }
    
        @GetMapping("/order-service")
        public  JsonResult addOrder(){
            log.info("调用远程订单服务,新增订单");
            return  orderFeignClient.addOrder();
        }
    }
    
    

    调用流程

    流程

    feign + ribbon 负载均衡和重试

    无需额外配置,feign 默认已启用了 ribbon 负载均衡和重试机制。可以通过配置对参数进行调整

    重试的默认配置参数:

    ConnectTimeout=1000
    ReadTimeout=1000
    MaxAutoRetries=0
    MaxAutoRetriesNextServer=1
    

    可以自己配置重试参数:

    ribbon:
      ConnectTimeout: 1000
      ReadTimeout: 1000
      
    item-service:
      ribbon:
        MaxAutoRetries: 1
        MaxAutoRetriesNextServer: 2
        ConnectTimeout: 1000
        ReadTimeout: 500
    

    Feign 集成 Hystrix

    feign 启用 hystrix

    feign 默认没有启用 hystrix,不推荐启用Hystrix, 如果有特殊需求,可以添加配置,启用 hystrix

    • 添加 Hystrix 完整依赖
    • 添加注解 @EnableCircuitBreaker
    • feign.hystrix.enabled=true

    application.yml 添加配置

    feign:
      hystrix:
        enabled: true
    

    启用 hystrix 后,访问服务

    默认1秒会快速失败,没有降级方法时,会显示白板页

    可以添加配置,暂时减小降级超时时间,以便后续对降级进行测试

    feign:
      hystrix:
        enabled: true
        
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 500
    

    feign + hystrix 降级

    feign 远程接口中指定降级类

    远程调用失败, 会执行降级类中的代码

    添加降级代码

    // 声明式客户端接口添加注解属性
    @FeignClient(name="item-service", fallback=降级类.class)
    public interface ItemFeignClient {
        
    }
    
    
    //定义降级类,实现声明式客户端接口
    public class 降级类 implements ItemFeignClient {
            
    }
    
    

    ItemFeignClient

    @FeignClient(name = "item-service" ,fallback = ItemFeignClientFB.class )
    public interface ItemFeignClient {
    ............
    }
    
    

    UserFeignClient

    @FeignClient(value = "user-service",fallback = UserFeignClientFB.class)
    public interface UserFeignClient {
    
    ........
    
    }
    

    OrderFeignClient

    @FeignClient(name = "order-service",fallback = OrderFeignClientFB.class)
    public interface OrderFeignClient {
    
    .......
    }
    

    降级类

    降级类需要实现远程接口

    ItemFeignClientFB

    package cn.tedu.sp09.feign.impl;
    
    import cn.tedu.sp01.pojo.Item;
    import cn.tedu.sp09.feign.ItemFeignClient;
    import cn.tedu.web.util.JsonResult;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    @Component
    public class ItemFeignClientFB implements ItemFeignClient {
        @Override
        public JsonResult<List<Item>> getItems(String orderId) {
    
            return JsonResult.err().msg("获取商品列表失败");
        }
    
        @Override
        public JsonResult decrease(List<Item> items) {
            return JsonResult.err().msg("减少商品库存失败");
        }
    }
    
    

    UserFeignClientFB

    package cn.tedu.sp09.feign.impl;
    
    import cn.tedu.sp01.pojo.User;
    import cn.tedu.sp09.feign.UserFeignClient;
    import cn.tedu.web.util.JsonResult;
    import org.springframework.stereotype.Component;
    
    @Component
    public class UserFeignClientFB implements UserFeignClient {
        @Override
        public JsonResult<User> getUser(Integer userId) {
            return JsonResult.err().msg("获取用户失败");
        }
    
        @Override
        public JsonResult addScore(Integer userId, Integer score) {
            return JsonResult.err().msg("新增积分失败");
        }
    }
    
    

    OrderFeignClientFB

    package cn.tedu.sp09.feign.impl;
    
    import cn.tedu.sp01.pojo.Order;
    import cn.tedu.sp01.pojo.User;
    import cn.tedu.web.util.JsonResult;
    import org.springframework.stereotype.Component;
    
    @Component
    public class OrderFeignClientFB implements OrderFeignClient{
        @Override
        public JsonResult<Order> getOrder(String orderId) {
            return JsonResult.err().msg("获取订单失败");
        }
    
        @Override
        public JsonResult addOrder() {
            return JsonResult.err().msg("新增订单失败");
        }
    }
    
    

    启动服务,访问测试

    Feign + hystrix 监控和熔断测试

    主程序添加 @EnableCircuitBreaker

    package cn.tedu.sp09;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    @EnableCircuitBreaker //启动断路器
    @EnableFeignClients //启用feign客户端
    @SpringBootApplication
    public class Sp09FeignApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Sp09FeignApplication.class, args);
        }
    
    }
    
    

    配置 actuator,暴露 hystrix.stream 监控端点

    actuator 依赖

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    

    application.yml 暴露 hystrix.stream 端点

    m.e.w.e.i=hystrix.stream

    management:
      endpoints:
        web:
          exposure:
            include: hystrix.stream
    

    hystrix dashboard

    1. 重启项目,调用后台服务

      http://localhost:3001/item-service/45ty3t3t4

      http://localhost:3001/user-service/7

      http://localhost:3001/order-service/56564

    2. 查看监控端点,有没有数据

      http://localhost:3001/actuator/hystrix.stream

    3. 访问仪表盘,开启监控

      http://localhost:4001/hystrix

    熔断测试

    • 用 ab 工具,以并发50次,来发送20000个请求
    ab -n 20000 -c 50 http://localhost:3001/item-service/35
    
    • 断路器状态为 Open,所有请求会被短路,直接降级执行 fallback 方法

    压力测试

  • 相关阅读:
    Python tutorial阅读之基本数据结构
    Leetcode:Merge Sorted Array
    Python tutorial阅读之函数的定义与使用
    LeetCode:3Sum
    Python tutorial阅读之Python基本运算与基本变量
    Python tutorial阅读之使用 Python 解释器
    LeetCode:Longest Substring Without Repeating Characters
    如何过好幸福且富裕的一生----查理芒格
    创业学习---《调研黑客上:锁定调研目标》--D-2.调研模块---HHR计划---以太一堂
    创业学习---《如何展开竞争情报调研》--D-1.调研模块---HHR计划---以太一堂
  • 原文地址:https://www.cnblogs.com/liqbk/p/13583553.html
Copyright © 2011-2022 走看看