• 微服务_fegin


    关键字:微服务, Spring Cloud, OpenFeign, 服务调用, RPC
    
    • 1

    Feign与OpenFeign简介

    Feign(NetFlix)---->维护---->OpenFeign(开源社区维护)

    Feign本身并不支持Spring MVC的注解,它有一套自己的注解。
    后来Spring Cloud孵化了OpenFeign,为了更方便的使用,使其支持了Spring MVC的注解,如@RequestMapping@PathVariable等等。
    并支持使用 Spring Web 中默认使用的相同 HttpMessageConverters。
    OpenFeign@FeignClient可以解析Spring MVC@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。

    • 代理类中做负载均衡和Ribbon是怎么统一的?

    后面都是讨论OpenFegin,简称Fegin了。Feign正统在OpenFegin! doge

    OpenFegin是一个声明式的伪http客户端

    • 声明式?这是官网说的么?什么意思?
    • 伪是什么意思?

    使用简单:写一个接口,加一个注解,调用服务方代码。
    被调用方几乎什么都不用做,只要到注册中心登记即可。本身就是要重复利用他们已经写好的接口。

    • 因为是http调用,所以要调用的是服务提供方负责处理网络请求的controller层。
    • Q: 那Dubbo等RPC框架是调用其他服务的什么层?
      • A: Dubbo使用自己的RPC协议,服务提供方可以用dubbo的@Service注解标记自己的Service层,而调用方用@DubboReference来引用他们。

    具有可插拔的注解特性(支持SpringMVC注解),可使用Feign注解和JAX-RS注解.

    支持可插拔的编码器和解码器,默认集成了Ribbon做负载均衡。

    是客户端组件

    ruoyi系统中Log\Auth\User用了远程服务调用,用工厂模式给他的报错加了层工厂类,return错误的时候重写了以下方法。

    会调用同一url中的其他服务中的接口方法(对应其他服务中的controller),在这里只是映射下路径换个名字。所以不需要实现接口。
    为了方便统一管理,就都在api模块了。其实依赖了api模块,相当于自己有api模块。

    调用匹配机制:服务id + url。
    服务id在底层会被Ribbon解析成ip+端口,拼接成目标服务真正的uri,调用httpclient访问。

    @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.FORUM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
    public interface RemoteUserService
    
    • 1
    • 2

    注解上的value是服务名

    简单使用

    有两个主体:

    • 服务提供方,让自己的Controller接口可以被其他服务复用。
      • 本身RPC目的之一就是利用现成的、已经写好的接口
      • 所以提供方只要像注册中心注册服务就好,其他什么都不用做
    • 调用方:远程调用提供方的Controller接口,
      • 操作方式是在自己Service层写一个对应远程Controller接口的Interface,然后用在方法注解编写目标接口的访问路径(uri)
      • 返回值和形参一样就行,方法名不需要一样(因为是依赖服务id+uri来对应的)
    • 可不可以说生产者消费者?生产者消费者模型到底指什么?
    • Q:为什么是interface?
      • A: 因为底层使用了JDK动态代理,而JDK动态代理的原理是实现interface

    要记得把服务注册到注册中心。openFeign底层的Ribbon要从注册中心获取服务列表
    入口类要加入注解@EnableDiscoveryClient,consul的注解,此处案例注册中心用Consul
    org.springframework.cloud.client.discovery.EnableDiscoveryClient也有这个注解

    • 这个注解什么用?nacos也可以使用
    • ribbon不能单独?feign呢?
    • 似乎,,可以用uri的方式调用,,那样的话单独应该可以吧

    服务调用方(消费者)引入OpenFeign的依赖

    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在服务调用方入口类上加注解@EnableFeignClients。
    该注解会读取Feign相关配置、扫描@Feign注解、注册Bean等,详情见原理解析。

    被调用的服务提供方入口类不用加这个注解,除非它同时也是个调用方。
    用于远程调用的接口可以专门写个包或是模块统一管理,如feignClient包或者api模块。

    //例1 注解默认参数为name(别名value):服务名
    @FeignClient("被调用的服务id")	
    public interface ProductClient{
    	//...
    }
    
    //例2 ruoyi-cloud开源项目案例
    @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
    public interface RemoteUserService
    {
        /**
         * 通过用户名查询用户信息
         *
         * @param username 用户名
         * @param source 请求来源
         * @return 自己封装的统一返回类型
         */
        @GetMapping("/user/info/{username}")
        public R<LoginUser> getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    
        /**
         * 注册用户信息
         *
         * @param sysUser 用户信息
         * @param source 请求来源
         * @return 自己封装的统一返回类型
         */
        @PostMapping("/user/register")
        public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    该案例中,value使用封装好的常量ServiceNameConstants.SYSTEM_SERVICE,该常量被设置为服务名。

    • contextid等参数干嘛的?

    注入并调用

    public class SysLoginService
    {
        @Autowired
        private RemoteUserService remoteUserService;
        
        /**
         * 登录
         */
        public LoginUser login(String username, String password)
        {
    		//...
    			
            // 查询用户信息
            R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
    
    		//...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    集成了Ribbon,默认轮询负载均衡。

    传参

    Get方式传参,使用@PathVariable@RequestParam注解接收请求参数。

    @GetMapping(value = "/user/info/{username}")
    public R<LoginUser> getUserInfo(@PathVariable("username") String username);
    
    • 1
    • 2

    @RequestParam(“参数名”)
    Post方式传参,使用@RequestBody注解接收请求参数。

    @PostMapping("/operlog")
    public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog);
    
    • 1
    • 2

    反客为主了属于是,原来是参数从url中匹配,现在是调用时传给url,url给远程服务,远程服务再从url取

    在openfeign传递参数必须加入注解(在接口声明处),不然会报参数传入过多错误。

    跨模块公用的类。多个模块都要用,所以可以声明在专门的模块中统一管理,再让需要调用公共接口的模块依赖这个模块。如ruoyi-cloud中的api模块。

    数组:

    /test?ids=21&ids=22

    @GetMapping("/test3")
    String tests(@RequestParam("ids") String[] ids) 
    
    • 1
    • 2

    可以调用方传集合,被调用方用vo中的list接受,类似前端传数据,名字对应就行,会自动映射到vo中的list

    响应处理

    超时处理

    在调用方设置请求被调用方时的超时时间

    要求服务一秒内响应,否则报错

    修改默认超时设置

    feign.client.config.服务id.connectTimeout=5000	#配置指定服务连接超时
    feign.client.config.服务id.readTimeout=5000	#配置指定服务等待超时
    feign.client.config.default.connectTimeout=5000	#配置所有服务连接超时
    feign.client.config.default.readTimeout=5000	#配置所有服务等待超时
    
    • 1
    • 2
    • 3
    • 4

    ribbon超时设置

    Feign的负载均衡底层用的就是Ribbon,所以请求超时其实就只需要配置Ribbon参数。

    全局配置

    # 请求处理的超时时间
    ribbon:
      ReadTimeout: 10000
      ConnectTimeout: 10000
    
    • 1
    • 2
    • 3
    • 4

    局部配置

    # ruoyi-xxxx 为需要调用的服务名称
    ruoyi-xxxx:
      ribbon:
        ReadTimeout: 10000
        ConnectTimeout: 10000
    
    • 1
    • 2
    • 3
    • 4
    • 5

    日志

    在调试时开启详细日志

    logging.level.com.包名=debug 
    
    • 1

    feign为每个客户端提供了日志对象
    请添加图片描述

    feign.clientl.config.服务id.loggerLevel=full 
    
    • 1

    代码设置

    全局配置

    @Bean
    public Logger.Level getLog()
    {
    	return Logger.Level.FULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    局部配置

    import feign.Logger;
    
    /**
     * Feign 客户端配置
     *
     */
    @Configuration
    public class FeignConfiguration
    {
        @Bean
        Logger.Level feignLoggerLevel()
        {
            return Logger.Level.FULL;
        }
    }
    
    // ====== 在客户端接口指定此配置 ======
    
    /**
     * 用户服务
     * 
     * @author ruoyi
     */
    @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class, configuration = FeignConfiguration.class)
    public interface RemoteUserService
    {
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    Http连接池

    两台服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP连接池可以节约大量的时间提示吞吐量。

    FeignHTTP客户端支持3种框架:HttpURLConnectionHttpClientOkHttp

    默认是采用java.net.HttpURLConnection,每次请求都会建立、关闭连接,为了性能考虑,可以引入httpclientokhttp作为底层的通信框架。

    例如将FeignHTTP客户端工具修改为HttpClient

    1、添加依赖

    
    <dependency>
        <groupId>io.github.openfeigngroupId>
        <artifactId>feign-httpclientartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、全局配置

    feign:
      httpclient:
        # 开启httpclient
        enabled: true
    
    • 1
    • 2
    • 3
    • 4

    请求拦截器

    在可以通过实现feign.RequestInterceptor接口在feign执行后进行拦截,对请求头等信息进行修改。

    例如项目中利用feign拦截器将本服务的userIduserNameauthentication传递给下游服务

    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    
    /**
     * feign 请求拦截器
     * 
     */
    @Component
    public class FeignRequestInterceptor implements RequestInterceptor
    {
        @Override
        public void apply(RequestTemplate requestTemplate){
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    apache-atlas-hive-bridge-源码分析
    华为 2024 届实习校园招聘-硬件通⽤(大部分硬件技术工程师岗位适用)/单板开发——第四套
    初识MySQL
    Python pip 替换国内镜像源
    namonamo Daimayuan Online Judge
    AIGC绘画设计——midjourney有哪些好用的关键词?
    替代MySQL半同步复制,Meta技术团队推出MySQL Raft共识引擎
    Improving 3D Imaging with Pre-Trained Perpendicular 2D Diffusion Models
    基于c#上位机制作(WPF控件)
    AI:07-基于卷积神经网络的海洋生物的识别
  • 原文地址:https://blog.csdn.net/qq_44915801/article/details/131144414