当前位置: 首页 > news >正文

Spring-Cloud-Feign-03

1、快速入门

1、创建一个提供者

这里就拿用户下订单举例

  1. 创建一个SpringCloud项目,引入以下依赖即可

    在这里插入图片描述

  2. 项目创建好后,记得修改pom.xml文件的springcloud依赖

    在这里插入图片描述

  3. 然后在启动类上添加@EnableEurekaClient注解开启eureka服务

  4. 配置application.xml文件,注册到eureka上

    在这里插入图片描述

  5. 修改完后,创建一个下订单的Controller,编写一个接口用来测试

    package com.tcc.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author 宇辰
     * @date 2022/9/27-8:59
     **/
    @RestController
    public class TestController {
    
        @RequestMapping("buyCar")
        public String buyCar(){
            return "劳斯莱斯-幻觉";
        }
    }
    
  6. 最后,启动项目,打开eureka地址查看order-a服务是否注册成功

2、创建一个调用者(主要)

调用者需要引入OpenFeign依赖

  1. 创建项目和上面的步骤一样,除了创建Controller,依赖引入如下

    在这里插入图片描述

  2. 创建好项目,修改好pom文件,配置好xml文件,配置好Eureka后,编写Controller

  3. Controller需要调用order-a服务提供的接口,步骤如下

  4. 先在启动类上添加注解@EnableFeignClients // 开启feign的客户端,才可以帮助我们发起调用

  5. 添加好后,我们使用OpenFeign写法,创建一个文件夹,在里面创建接口,名字为UserOrderFeign

    package com.tcc.feign;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-11:32
     **/
    @FeignClient("order-a") // value 为被调用的服务的应用名称
    public interface UserOrderFeign {
    
        /**
         * 需要调用哪个接口,就写它的方法签名
         * 方法签名就是包含一个方法的所有属性
         * @return
         */
        @RequestMapping("buyCar")
        public String buyCar();
    }
    
  6. 编写好接口后,我们在Controller里面自动注入这个接口,然后调用接口里面的方法

    package com.tcc.controller;
    
    import com.tcc.feign.UserOrderFeign;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-11:29
     **/
    @RestController
    public class TestController {
    
        // 注入写好的Feign接口
        @Autowired
        private UserOrderFeign userOrderFeign;
    
        @RequestMapping("doOrder")
        public String doOrder(){
            String doOrder = userOrderFeign.buyCar();
            return doOrder;
        }
    
    }
    
  7. 编写完毕后我们访问http://localhost:8090/doOrder调用接口,查看是否可以成功调用

    在这里插入图片描述

2、调用超时设置

测试:在order-a服务提供的接口里延迟两秒再返回内容,看看user-a是否可以正常获取

  1. order-a接口修改:

在这里插入图片描述

  1. 修改完毕后,重启服务,然后再次调用doOrder接口,查看结果

    在这里插入图片描述

结论:

当被调用的服务长时间无响应的时候,就停止访问,默认为1秒

RIbbon默认调用时长为1s,可以修改,超时调整,可以查看DefaultClientConfigImpl

在这里插入图片描述

ribbon:
  ReadTimeout: 5000 # 修改调用时长为5s
  ConnecTimeout: 5000 # 修改连接时长为5s

3、OpenFeign调用参数处理

Feign传参确保消费者和提供者的参数列表一致,包括返回值,方法签名要一致

  1. 通过URL传参,GET请求,参数列表使用@PathVariable("")
  2. 如果是GET请求,每个基本参数必须加@RequestParam("")
  3. 如果是POST请求,而且是对象集合等参数,必须加@Requestbody或者@RequestParam

举例:

  1. 先在order-a和user-a两个服务编写User

    package com.tcc.entity;
    
    import lombok.*;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-19:45
     **/
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class User {
        private String name;
        private Integer age;
    }
    
  2. 然后在order-a服务编写好接口

    package com.tcc.controller;
    
    import com.tcc.entity.User;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-19:40
     **/
    @RestController
    public class ParamController {
    
        /**
         * 路径中传参    使用PathVariable注解
         * @param name
         * @param age
         * @return
         */
        @GetMapping("testUrl/{name}/and{age}")
        public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age){
            return "姓名:"+name+",年龄:"+age;
        }
    
        /**
         * Get请求传参,使用@RequestParam("name")  后面需要加上value
         * @param name
         * @return
         */
        @GetMapping("onParam")
        public String onParam(@RequestParam("name") String name){
            return "姓名:" + name;
        }
    
        /**
         * Post请求,传入一个对象,使用@RequestBody
         * @param user
         * @return
         */
        @PostMapping("onObject")
        public String oneObject(@RequestBody User user){
            return "姓名:"+user.getName()+",年龄:"+user.getAge();
        }
    
        /**
         * Post请求,传入一个对象,一个基本参数 对象使用;@RequestBody   基本参数使用:@RequestParam("sex") 后面需要加上value
         * @param user
         * @param sex
         * @return
         */
        @PostMapping("onObjectAndOnParam")
        public String onObjectAndOnParam(@RequestBody User user, @RequestParam("sex") String sex){
            return "姓名:"+user.getName()+",年龄:"+user.getAge()+",性别:" + sex;
        }
    }
    
  3. user-a服务的Feign中引入需要调用的方法签名

    package com.tcc.feign;
    
    import com.tcc.entity.User;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-11:32
     **/
    // value 为服务的应用名称
    @FeignClient("order-a")
    public interface UserOrderFeign {
    
        /**
         * 需要调用哪个接口,就写它的方法签名
         * 方法签名就是包含一个方法的所有属性
         * @return
         */
        @RequestMapping("buyCar")
        public String buyCar();
    
        @GetMapping("testUrl/{name}/and{age}")
        public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age);
    
        @GetMapping("onParam")
        public String onParam(@RequestParam("name") String name);
    
        @PostMapping("onObject")
        public String oneObject(@RequestBody User user);
    
        @PostMapping("onObjectAndOnParam")
        public String onObjectAndOnParam(@RequestBody User user, @RequestParam("sex") String sex);
    }
    
  4. 在Controller中远程调用服务进行测试

    @GetMapping("testParam")
    public String testParam(){
        String s1 = userOrderFeign.testUrl("张三", 20);
        System.out.println(s1);
    
        String s2 = userOrderFeign.onParam("李四");
        System.out.println(s2);
    
        User user = User.builder()
            .name("王五")
            .age(21)
            .build();
        String s3 = userOrderFeign.oneObject(user);
        System.out.println(s3);
    
        user = User.builder()
            .name("赵六")
            .age(22)
            .build();
        String s4 = userOrderFeign.onObjectAndOnParam(user, "男");
        System.out.println(s4);
    
        return "ok";
    }
    
  5. 访问http://localhost:8090/testParam地址,查看是否调用成功

    在这里插入图片描述

  6. 结果

    在这里插入图片描述

传递事件日期参数的坑

传过去的日期参数,会相差12个小时

  1. 先在order-a服务编写接口,参数为Date类型。

    @GetMapping("oneDate")
    public Date oneDate(@RequestParam("date") Date date){
        return date;
    }
    
  2. UserOrderFeign接口中写好方法签名

    @GetMapping("oneDate")
    public Date oneDate(@RequestParam("date") Date date);
    
  3. 然后在user-a服务中,编写方法,远程调用服务

    @GetMapping("oneDate")
    public Date oneDate(){
        Date d = new Date();
        System.out.println(d);
        Date date = userOrderFeign.oneDate(d);
        System.out.println(date);
        return date;
    }
    
  4. 访问接口,查看控制台打印,可以发现相差12小时

    在这里插入图片描述

解决办法

  1. 可以把日期先转为字符串类型,再传过去

  2. 可以使用jdk8提供的日期类型

    LocalDate now = LocalDate.now(); // 没有问题
    LocalDateTime now1 = LocalDateTime.now(); // 会丢失s
    
  3. 最好把日期类型放到对象里传入过去

4、手写Feign核心功能

通过打断点,可以发现,我们注入的UserOrderFeign是被代理过的

在这里插入图片描述

  • 接口是不能做事情的
  • 如果想做事,必须要有对象
  • 那么这个接口肯定是被创建处代理对象的
  • 动态代理:1.jdk(java interface 接口 $proxy) 2.cglib(subClass 子类)
  • jdk动态代理,只要是代理对象调用的方法,必须走java.lang.reflect.InvocationHandler#invoke方法

开始手写

  1. 我们先在启动类上把RestTemplet类给放到Bean中,并添加@LoadBalanced注解,让Ribbon托管

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    
  2. 在测试类中,使用jdk的动态代理,代理UserOrderFeign接口,并重写方法

    package com.tcc;
    
    import com.tcc.feign.UserOrderFeign;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.client.RestTemplate;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    @SpringBootTest
    class UserAApplicationTests {
    
        // 自动注入被Ribbon托管的RestTemplet
        @Autowired
        private RestTemplate restTemplate;
    
        @Test
        void contextLoads() {
            // 创建动态代理对象
            UserOrderFeign o = (UserOrderFeign) Proxy.newProxyInstance(UserOrderFeign.class.getClassLoader(), new Class[]{UserOrderFeign.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 想要远程调用就必须获得服务名+接口地址,然后拼接:http://服务名/接口地址 最后用RestTemplet调用接口即可
    
                    // 通过方法获得类名,通过类名获得类名上的注解,再通过注解获取里面的值(服务名)
                    Class<?> aClass = method.getDeclaringClass();
                    FeignClient aClassAnnotation = aClass.getAnnotation(FeignClient.class);
                    String orderService = aClassAnnotation.value(); // order-a
    
                    // 通过方法获得注解,通过注解获得里面的值(接口地址)
                    RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                    String[] paths = annotation.value();
                    String path = paths[0]; // buyCar
    
                    // 拼接   http://order-a/buyCay
                    String url = "http://" + orderService + "/" + path;
                    String forObject = restTemplate.getForObject(url, String.class);
                    return forObject; // 劳斯莱斯-幻觉
                }
            });
    
            // 调用方法 走代理对象的invoke方法
            String s = o.buyCar();
            System.out.println(s); // 劳斯莱斯-幻觉
    
        }
    
    }
    

5、配置日志

  1. 在启动类上把FeignLevel类注入到Bean中

    @Bean
    public Logger.Level level(){
        return Logger.Level.FULL;
    }
    
  2. 在配置文件中进行配置

    logging:
      level:
        com.tcc.feign.UserOrderFeign: debug # 打印这个接口下的日志
    
  3. 访问接口进行测试

    在这里插入图片描述

相关文章:

  • 【深入Javascript闭包】
  • 词典
  • Spring Bean的生命周期
  • 秋招-致谢
  • 「实用工具—LICEcap」写博必备|动图制作|一键生成gif(GIF)
  • 3D目标检测(一)
  • 秋招面试- - -Java体系最新面试题(8)
  • 前端工程师面试题详解(四)
  • app端专项测试
  • 我操作MySQL的惊险一幕
  • 模糊预测股价走势
  • Qt之开源绘图控件QCustomPlot
  • Python 语言程序设计 第五章 字符串应用举例
  • C++ | 12天学好C++ (Day6)->结构图可视化、代码加通俗理解
  • Android Socket通讯 之 心跳消息
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 2017 年终总结 —— 在路上
  • Java知识点总结(JavaIO-打印流)
  • linux学习笔记
  • Linux中的硬链接与软链接
  • PAT A1092
  • Python实现BT种子转化为磁力链接【实战】
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • V4L2视频输入框架概述
  • 第2章 网络文档
  • 对象引论
  • 多线程 start 和 run 方法到底有什么区别?
  • 关于for循环的简单归纳
  • 关于List、List?、ListObject的区别
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • ​queue --- 一个同步的队列类​
  • # C++之functional库用法整理
  • # Maven错误Error executing Maven
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • $(function(){})与(function($){....})(jQuery)的区别
  • (003)SlickEdit Unity的补全
  • (二)换源+apt-get基础配置+搜狗拼音
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (三)终结任务
  • (四) 虚拟摄像头vivi体验
  • **PHP二维数组遍历时同时赋值
  • .NET Core 项目指定SDK版本
  • .NET 药厂业务系统 CPU爆高分析
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • .net项目IIS、VS 附加进程调试
  • .Net中ListT 泛型转成DataTable、DataSet
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • @Autowired多个相同类型bean装配问题
  • [1181]linux两台服务器之间传输文件和文件夹
  • [AIGC codze] Kafka 的 rebalance 机制
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [APUE]进程关系(下)
  • [AX]AX2012 AIF(四):文档服务应用实例