• OpenFeign笔记


    0 环境

    系统环境:win10
    编辑器:idea
    springcloud版本:H版

    1 前言

    之前使用的eureka/hystrix 都是调用RestTemplate(繁琐 重复高) OpenFeign对请求进行简化。Feign停更了 OpenFeign是在Feign基础上开发出来的

    • 常用的几种接口调用方法
    • Httpclient 易用 灵活
    • Okhttp 处理网络请求 轻量级 支持多协议。。
    • HttpURLConnection 使用复杂
    • RestTemplate Rest服务的客户端 提供多种便携访问HTTP服务的方法

    2 尝鲜

    2.1 创建springboot项目

    在这里插入图片描述

    2.2 yml配置

    spring:
      application:
        name: openfeign
    
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:1234/eureka
    
    server:
      port: 5000
    
    

    2.3 启动类配置

    @EnableFeignClients --> 开启Feign在这里插入图片描述

    2.4 接口配置

    使用的是之前的eureka server和provider以及如今使用的openfeign

    // openfeign service
    // 对比之前xxx.getForObject("http://provider/hello1", String.class)
    // 现在只需要抽取provider hello1 拼接不需要我们操心了
    @FeignClient("provider")
    public interface HelloService {
    	@GetMapping("/hello")
        // 方法名无所谓 无参调用
        String hello();
    }    
    

    2.5 接口调用

    @RestController
    public class HelloController {
        @Autowired
        HelloService helloService;
    
        // 无参测试
        @GetMapping("/hello")
        public String hello(){
            return helloService.hello();
        }
    }    
    

    2.6 测试结果

    开启eureka server provider openfeign
    在这里插入图片描述

    3 参数传递

    3.1 导入依赖模块

    因为要用到类 之前新建一个模块 现在该opfeign需要引入依赖 可以在opfeign中定义类(随意)

    		<dependency>
                <groupId>xxx</groupId>
                <artifactId>commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    

    3.2 接口配置

    // eureka provider
    @RestController
    public class HelloController {
    	
    	@GetMapping("/hello1")
        public String hello1(String name){
            return "hello provider: " + name;
        }
    	
    	@PostMapping("/user1")
        public User addUser1(@RequestBody User user){
            return user;
        }
    	
    	@DeleteMapping("/user1/{id}")
        public void delUser1(@PathVariable Integer id){
            System.out.println("json形式:" + id);
        }
    
        @GetMapping("/user2")
        public void getUserByName(@RequestHeader String name) throws UnsupportedEncodingException {
            // 解码
            System.out.println(URLDecoder.decode(name, "utf-8"));
        }
    }    
    
    // openfeign service配置
    @FeignClient("provider")
    public interface HelloService {
        // 参数传递一定要绑定参数名
        @GetMapping("/hello1")
        String hello1(@RequestParam("name") String name);
    
        // json
        // 注:key/value形式的参数 一定要标记参数的名称
        @PostMapping("/user1")
        User user1(@RequestBody User user);
    
        // 删除id
        // /user1/{id}
        @DeleteMapping("/user1/{id}")
        void delUser1(@PathVariable Integer id);
    
        // 通过header来传参 中文要转码
        @GetMapping("/user2")
        void getUserByName(@RequestHeader String name);
    
    }
    

    3.3 接口调用

    	// openfeign controller 传参
        @GetMapping("/hello1")
        public void hello1() throws UnsupportedEncodingException {
            String s = helloService.hello1("你好呀");
            System.out.println("hello1:" + s);
            System.out.println("---------------------------------------");
            User user = new User();
            user.setId(1);
            user.setName("小个");
            user.setNickName("萨达过");
            User user1 = helloService.user1(user);
            System.out.println("user:" + user1);
            System.out.println("---------------------------------------");
            helloService.delUser1(1);
            System.out.println("---------------------------------------");
            // 放在heard中的中文参数 一定要先编码在传递
            helloService.getUserByName(URLEncoder.encode("方便热土", "utf-8"));
    
        }
    

    3.4 测试结果

    开启eureka server provider openfeign
    在这里插入图片描述

    3.5 小结

    • 参数传递
    • 参数一定要绑定参数名
    • 若通过header来传递参数 中文需转码 编码后在传递(URLEncoder.encode(xxx, "utf-8"))
      .provider解码(URLDecoder.decode(xxx, "utf-8"))

    4 继承特性

    4.1 新建maven子模块

    这个包被其他模块依赖 需要springmvc依赖

    <dependencies>
            <!-- 涉及到springmvc -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.2.5.RELEASE</version>
            </dependency>
    
            <!-- 存储类模块 -->
            <dependency>
                <groupId>xxx</groupId>
                <artifactId>commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
        </dependencies>
    

    4.2 封装接口

    public interface IUserService {
        @GetMapping("/hello")
        String hello();
    
        // 参数传递一定要绑定参数名 若是参数最好用@RequestParam
        // 最好别用map 其问题是可以随意传参
        @GetMapping("/hello1")
        String hello1(@RequestParam("name") String name);
    
        // json
        // 注:key/value形式的参数 一定要标记参数的名称
        @PostMapping("/user1")
        User addUser1(@RequestBody User user);
    
        // 删除id
        // /user1/{id}
        // 添加@PathVariable("id") 注意了一定要把("id")添加进去 不然会报错
        @DeleteMapping("/user1/{id}")
        void delUser1(@PathVariable("id") Integer id);
    
        // 通过header来传参 中文要转码
        // 添加@RequestHeader("name") 注意了一定要把("name")添加进去 不然会报RequestHeader0参数错
        @GetMapping("/user2")
        void getUserByName(@RequestHeader("name") String name) throws UnsupportedEncodingException;
    }
    
    

    4.3 消费者和openfeign添加依赖

    		<dependency>
                <groupId>com.sundown</groupId>
                <artifactId>commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>com.sundown</groupId>
                <artifactId>hi-api</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    

    4.4 提供者实现接口

    @RestController
    //@RequestMapping("/test")
    public class Hello1Controller implements IUserService {
    
        @Value("${server.port}")
        Integer port;
    	
    	// 重写
        @Override
        public String hello(){
            return "hello provider:" + port;
        }
    
    
        /**
        * @Description: consumer访问该接口 调用RestTemplate的get请求
        * @Param: [name]
        * @return: java.lang.String
        * @Author: 水面行走
        * @Date: 2020/3/4
        */
        @Override
        public String hello1(String name){
            return "hello provider: " + name;
        }
    
        @GetMapping("/hello2")
        public String hello2(String name){
            System.out.println(new Date() + "--->" + name);
            return "hello " + name;
        }
    
        // 在provider 提供2个post接口
    
        /**
        * @Description: key:value形式传参
        * @Param: [user]
        * @return: model.User
        * @Author: 水面行走
        * @Date: 2020/3/7
        */
        @PostMapping("/user")
        public User addUser(User user){
            return user;
        }
    
    
        /**
         * @Description: json形式传参
         * @Param: [user]
         * @return: model.User
         * @Author: 水面行走
         * @Date: 2020/3/7
         */
        @Override
        public User addUser1(@RequestBody User user){
            return user;
        }
    
        /**
        * @Description: k/v形式 因为是更新操作 put方法返回为void 所以返回值为void就行 有返回值不会报错
        * @Param:
        * @return:
        * @Author: 水面行走
        * @Date: 2020/xx/xx
        */
        @PutMapping("/update-user")
        public void updateUser(User user){
            System.out.println("k/v形式:" + user);
        }
    
        /**
         * @Description: json形式 别忘了传参添加注解 因为是更新操作 put方法返回为void 所以返回值为void就行 有返回值不会报错
         * @Param:
         * @return:
         * @Author: 水面行走
         * @Date: 2020/xx/xx
         */
        @PutMapping("/update-user1")
        public void updateUser1(@RequestBody User user){
            System.out.println("json形式:" + user);
        }
    
        /**
        * @Description: k/v形式的删除 xxx?id=1
        * @Param:
        * @return:
        * @Author: 水面行走
        * @Date: 2020/3/8
        */
        @DeleteMapping("/deluser")
        public void delUser(Integer id){
            System.out.println("k/v形式:" + id);
        }
    
        /**
         * @Description: PathVariable(参数放在路径中 xxx/1)形式的删除
         * @Param:
         * @return:
         * @Author: 水面行走
         * @Date: 2020/3/8
         */
        @Override
        public void delUser1(@PathVariable Integer id){
            System.out.println("json形式:" + id);
        }
    
        @Override
        public void getUserByName(@RequestHeader String name) throws UnsupportedEncodingException {
            // 解码
            System.out.println(URLDecoder.decode(name, "utf-8"));
        }
    }
    

    4.5 openfeign配置

    // 继承接口
    // 继承特性的好处:抽出公共模块 provider和consumer代码一致 只需更改公共接口即可 减少出错率
    // 坏处就是耦合度变高
    @FeignClient("provider")
    public interface Hello1Service extends IUserService {
    
    }
    
    // 调用Hello1Service
    // 其他这个类没有变化 调用结果和上次是一致的
    @RestController
    public class Hello1Controller {
        @Autowired
        Hello1Service hello1Service;
    
        // 无参测试
        @GetMapping("/hello")
        public String hello(){
            return hello1Service.hello();
        }
    
        // 传参
        @GetMapping("/hello1")
        public void hello1() throws UnsupportedEncodingException {
            String s = hello1Service.hello1("你好呀");
            System.out.println("hello1:" + s);
            System.out.println("---------------------------------------");
            User user = new User();
            user.setId(1);
            user.setName("小个");
            user.setNickName("萨达过");
            User user1 = hello1Service.addUser1(user);
            System.out.println("user:" + user1);
            System.out.println("---------------------------------------");
            hello1Service.delUser1(1);
            System.out.println("---------------------------------------");
            // 放在heard中的中文参数 一定要先编码在传递
            hello1Service.getUserByName(URLEncoder.encode("个百分点", "utf-8"));
    
        }
    
    }
    

    4.6 小结

    • 继承特性
    • 代码简洁 服务者和消费者指向同一目标 一改都改 出错烦恼大减(既是优点也是缺点(耦合度高) 类似赤壁之战 曹操的战船相连)
    • 无论是否继承 参数(无参还是传参方式依然不变)

    5 数据压缩

    开启压缩 节省资源 提升性能

    feign:
      compression:
        request:
          # 开启数据压缩请求
          enabled: true
          # 压缩数据类型
          mime-types: text/xml, application/xml, application/json
          # 数据压缩下限 2048表示传输数据大于2048 才会进行数据压缩(最小压缩值标准)
          min-request-size: 2048
        # 开启数据压缩响应
        response:
          enabled: true
    

    在这里插入图片描述在这里插入图片描述

    6 日志配置

    • 配置日志 分4种
    1. NONE: 不开启日志(默认)
    2. BASIC: 记录请求方法、URL、响应状态、执行时间
    3. HEADERS: 在BASIC基础上 加载请求/响应头(+2)
    4. FULL: 在HEADERS基础上 增加body和请求元数据(+3)

    6.1 在yml中配置日志级别

    # 可以在yml feign.client.config.xxx 配置超时时间 拦截器等配置
    logging:
      level:
        com.sundown.openfeign:  debug
    

    6.2 两种配置日志bean方式

    • 在applicaton类中配置bean
    import feign.Logger;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    import org.springframework.context.annotation.Bean;
    
    @SpringBootApplication
    @EnableFeignClients
    public class OpenfeignApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(OpenfeignApplication.class, args);
        }
    
        // 配置日志 分4种
        // 1. NONE: 不开启日志(默认)
        // 2. BASIC: 记录请求方法、URL、响应状态、执行时间
        // 3. HEADERS: 在BASIC基础上 加载请求/响应头(+2)
        // 4. FULL: 在HEADERS基础上 增加body和请求元数据(+3)
        // 通过bean配置
        @Bean
        Logger.Level loggerLevel(){
            return Logger.Level.FULL;
        }
    }
    
    • Configuration中配置

    正好将超时和自定义拦截器加入

    @Configuration
    public class FeignConfig {
         // 超时时间配置(通过Options可配置连接超时时间和读取超时时间)
        // Options第一个参数连接超时时间(ms 默认1000*10)
        // Options第二个参数取超时时间(ms 默认1000*60)
        @Bean
        public Request.Options options(){
            return new Request.Options(3000, 8000);
        }
    
        /**
         * 日志级别
         * 在这里配置日志级别
         * @return
         */
        @Bean
        Logger.Level feignLoggerLevel() {
            return Logger.Level.FULL;
        }
    	
    	 // 配置basic认证
        // 通常: 调用有权限控制的接口 可能认证的值通过传参/请求头去传认证信息
        @Bean
        public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
            return new BasicAuthRequestInterceptor("user", "password");
        }
    
        // 自定义拦截器配置
        @Bean
        public FeignBasicAuthRequestInterceptor feignBasicAuthRequestInterceptor() {
            return new FeignBasicAuthRequestInterceptor();
        }
    }    
    
    // OpenFeign自定义拦截器(自定义认证方式) 实现RequestInterceptor
    // 自定义一个请求拦截器 在请求之前做认证操作 在往请求头中配置认证后的信息
    public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
    
        // 业务逻辑
        @Override
        public void apply(RequestTemplate requestTemplate) {
            System.err.println("欢迎进入拦截器: " + requestTemplate);
        }
    }
    
    // 在openfeign service层配置
    //@FeignClient(value = "provider",configuration= FeignConfig.class) 配置类-->拦截器啥的
    @FeignClient(value = "provider",configuration= FeignConfig.class)
    public interface Hello1Service extends IUserService {
    
    }
    

    启动eureka server和provider 还有openfeign http://localhost:5000/hello1
    在这里插入图片描述
    在这里插入图片描述

    7 openfeign+hystrix配合使用

    降级@FeignClient+value+fallback/fallbackFactory属性(都是在openfeign模块中实现 且要开启hystrix)

      hystrix:
        # 开启hystrix
        enabled: true
    
    • 降级fallback属性
    // fallback属性实现类 
    @Component
    @RequestMapping("/milk") // implements Hello1Service相当于调用了2次 避免重复的请求地址 不然会报错
    public class HelloServiceFallback implements Hello1Service{
    
        @Override
        public String hello() {
            return "error-hello";
        }
    
        @Override
        public String hello1(String name) {
            return "error-hello1";
        }
    
        @Override
        public User addUser1(User user) {
            return null;
        }
    
        @Override
        public void delUser1(Integer id) {
    
        }
    
        @Override
        public void getUserByName(String name) throws UnsupportedEncodingException {
    
        }
    }
    
    @FeignClient(value = "provider",fallback= HelloServiceFallback.class)
    public interface Hello1Service extends IUserService {
    
    }
    

    启动/重启eureka server和openfeign 断开provider http://localhost:5000/hellohttp://localhost:5000/hello1
    在这里插入图片描述
    在这里插入图片描述

    • 降级fallbackFactory属性
    @Component
    public class HelloServiceFallFactory implements FallbackFactory<Hello1Service> {
    
        @Override
        public Hello1Service create(Throwable throwable) {
            return new Hello1Service() {
                @Override
                public String hello() {
                    return "error1---------";
                }
    
                @Override
                public String hello1(String name) {
                    return "error2---------";
                }
    
                @Override
                public User addUser1(User user) {
                    return null;
                }
    
                @Override
                public void delUser1(Integer id) {
    
                }
    
                @Override
                public void getUserByName(String name) throws UnsupportedEncodingException {
    
                }
            };
        }
    }
    
    
    //@FeignClient(value = "provider",fallback= HelloServiceFallback.class) fallback和fallbackFactory不能同时使用
    @FeignClient(value = "provider",fallbackFactory= HelloServiceFallFactory.class)
    public interface Hello1Service extends IUserService {
    
    }
    

    启动/重启eureka server和openfeign 断开provider http://localhost:5000/hellohttp://localhost:5000/hello1
    在这里插入图片描述
    在这里插入图片描述

    8 小结

    • openfeign只需要我们提供关键的value就行了 自行拼接
    • openfeign环境: 依赖eureka连接依赖 web openfeign
    • yml eureka连接配置
    • 注解开启openfeign
    • 无参 无需参数直接调用即可
    • 有参 --> 绑定参数、header传递 中文要解码、多参数的话 建议@RequestParam("xxx")
    • 特性继承 --> provider和openfeign公用一个接口 特别注意(一定要定义名字@RequestHeader("xxx") @PathVariable("xxx")。。。不然会报错) 它的好处既是坏处
    • 数据压缩 yml配置 开启压缩请求和响应 最小压缩值标准还有压缩类型
    • 日志配置 四种级别NONE BASIC HEADERS FULL
    • yml --> logging: level: com.sundown.openfeign: debug
    • 在bean配置 要么在application中或是在config中配置好(还可以超时配置和认证配置或自定义拦截器) 在@FeignClient中配置configuration属性 例如@FeignClient(value = "provider",configuration= FeignConfig.class)
    • 另外 也可在yml feign.client.config.xxx 配置超时时间 拦截器等
    • openfeign+hystrix降级操作 在yml中开启hystrix 在@FeignClient中实现fallbackFactory属性(需要implements FallbackFactory<T>)或fallback(实现接口implements Hello1Service添加@RequestMapping("/xx")作为区分) 2种实现方式都要添加@Component注解
    作者:以罗伊
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    用Bootstrap框架弹出iframe页面 在弹出的模态框中载人iframe页面,bootstrapiframe
    Jquery Ajax表单提交插件jquery form用法
    ASP.NET MVC 在控制器中接收视图表单POST过来的数据方法
    RabbitMQ用户管理+VirtualHost管理
    Linux 查看Oracle 信息
    页面元素的坐标位置
    Selenium之Action Chains类
    测试管理:用量化的思想管理工作
    Python selenium ---父子,兄弟,相邻节点定位方式详解
    webdriver 清空input元素的值
  • 原文地址:https://www.cnblogs.com/my-ordinary/p/12508997.html
Copyright © 2020-2023  润新知