• 四、Spring Cloud OpenFeign 伪RPC调用


    官网地址 spring-cloud-openfeign

    Spring Cloud OpenFeign : 声明式的伪RPC调用,可以让服务调用者面向接口进行开发,底层是http通信。
    相较与Ribbon的客户端负载均衡,每次请求需要使用RestTemplate进行调用;而OpenFeign是在Ribbon基础上进行封装,将http请求封装为接口类,达到了调用远程接口就像调用内部interface实现一样。

    一、Spring Cloud OpenFeign简单使用

    1. 简单使用

    • 首先添加依赖spring-cloud-starter-openfeign
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
                <version>2.2.3.RELEASE</version>
            </dependency>
    

    openfeign 可以支持 OKHTTP,对HTTP通信做了很多优化。

    • 定义FeignClient

    基于接口服务来定义FeignClient:
      接口提供者服务的名称:spring-cloud-order-service
      提供的接口:@GetMapping("/orders")
    所以可以将FeignClient定义为:

    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    
    // 服务提供者的服务名称
    // 伪RPC
    @FeignClient("spring-cloud-order-service")
    public interface OrderServiceFeignClient {
    
        @GetMapping("/orders")
        String getAllOrder();
    
    }
    
    
    • Controller内注入该FeignClient接口
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class OrderFeignController {
    
        @Autowired
        OrderServiceFeignClient orderServiceFeignClient;
    
        @GetMapping("/testFeign")
        public String testFeign() {
            return orderServiceFeignClient.getAllOrder();
        }
    
    }
    

    可以直接在Controller内依赖注入OrderServiceFeignClient接口,进行http调用,而不需要再写RestTemplate。

    PS: 之前的写法:

    
    @RestController
    public class UserController2 {
    
        @Autowired
        RestTemplate restTemplate;
    
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    
        @GetMapping("/user2/{id}")
        public String getBuId(@PathVariable("id") int id) {
    
            String rst = restTemplate.getForObject("http://spring-cloud-order-service" + "/orders", String.class);
            return rst;
        }
    
    }
    
    • SpringBoot启用@EnableFeignClients且确保扫描到我们定义的OrderServiceFeignClient
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    import org.springframework.context.annotation.ComponentScan;
    
    @EnableFeignClients
    @ComponentScan("com.bigshen")
    @SpringBootApplication
    public class SpringCloudUserServiceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringCloudUserServiceApplication.class, args);
        }
    
    }
    
    
    • 启用OKHttp

    Feign底层其实使用的是Http通信,效率不高,如果想提升性能,可以使用OKHTTP:
    application.properties内启动配置:

    feign.okhttp.enabled=true
    feign.httpclient.enabled=false
    

    pom中引入OKhttp依赖:

            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-okhttp</artifactId>
                <version>11.0</version>
            </dependency>
    

    当传输的数据比较大时,也支持压缩传输等配置:

    2. FeignClient由服务提供者提供API jar

    一般我们实际使用时,FeignClient会由服务提供者封装为jar包方式的API供服务调用者调用;服务调用者不需要关心FeignClient的定义。

    spring-cloud-order-service 订单服务: Maven的多模块项目,包含两个服务提供者API的order-api模块,以及服务的具体实现order-service
    spring-cloud-user-service 用户服务: 单纯的SpringBoot项目

    order-api

    订单服务: order-api,

    OrderService : 定义订单服务提供的接口

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    
    public interface OrderService {
    
        @GetMapping("/order")
        String getAllOrders();
    
    
        @PostMapping("/order")
        String insertOrder(OrderDTO order);
    
    }
    

    OpenFeignClientService :

    import com.bigshen.springcloud.demo.OrderService;
    import org.springframework.cloud.openfeign.FeignClient;
    
    @FeignClient("shen-order-service")  // 声明FeignClient
    public interface OpenFeignClientService extends OrderService {
    
    }
    

    order-service

    订单接口的具体实现模块,依赖于 order-api 模块

    import com.bigshen.springcloud.demo.OrderDTO;
    import com.bigshen.springcloud.demo.OrderService;
    import org.springframework.web.bind.annotation.RestController;
    
    // 发布服务
    @RestController
    public class OrderServiceImpl implements OrderService {
    
        @Override
        public String getAllOrders() {
            System.out.println(1);
            return "Shen all orders";
        }
    
        @Override
        public String insertOrder(OrderDTO orderDTO) {
            System.out.println(orderDTO);
            return "Success insert";
        }
    
    
    }
    

    spring-cloud-user-service

    用户服务 : 依赖 order-api 模块

    import com.bigshen.springcloud.demo.OrderDTO;
    import com.bigshen.springcloud.demo.client.OpenFeignClientService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class UserController {
    
        @Autowired
        OpenFeignClientService openFeignClientService;  // 动态代理,可以直接依赖注入order-api的 OpenFeignClientService,表明该Bean肯定在IOC容器中
    
        @GetMapping("/testFeign001")
        public String getAllOrder() {
            return openFeignClientService.getAllOrders();
        }
    
        @GetMapping("/testFeign002")
        public String insertOrder() {
            OrderDTO orderDTO = new OrderDTO();
            orderDTO.setName("111");
            return openFeignClientService.insertOrder(orderDTO);
        }
    
    }
    
    

    Main:指定FeignClien所在的包,进行扫描

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableFeignClients(basePackages = "com.bigshen.springcloud.demo.client") // 扫描API包提供的FeignClient
    public class SpringCloudUserServiceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringCloudUserServiceApplication.class, args);
        }
    
    }
    
    

    二、原理分析

    Feign本身底层是基于Http通信来完成的。
    http通信: http//ip:port/requestPath?param1=aa&param2=bb,
    需要确定: ip、port、请求Method get or post or put or delete、参数params;
    而根据声明的FeignClient的注解,其实可以确定这些东西的。
    那么Feign要做的事情:

    • 参数的解析和装载
    • 针对指定的FeignClient,生成动态代理
    • 针对FeignClient中的方法描述进行解析
    • 组装出一个Request对象,发起请求

    源码分析路径

    
    源码: 
    首先需要扫描添加了`@FeignClient`注解的服务,
    
     -> @EnableFeignClients    
    	org.springframework.cloud.openfeign.EnableFeignClients  
    	注解内通过`@Import(FeignClientsRegistrar.class)`指定需要自动装配的Bean
     -> FeignClientsRegistrar  
    	org.springframework.cloud.openfeign.FeignClientsRegistrar 
        FeignClientsRegistrar实现ImportBeanDefinitionRegistrar进行Bean的自动装配	
    	
    	-> org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
    		
    		-> org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients
    			扫描`@EnableFeignClients`注解指定的baskPackage,得到该路径下添加了`@FeignClient`声明的interface
    			-> org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
    				注册FeignClient,将类信息解析为BeanDefinition
    				```
    				BeanDefinitionBuilder definition = BeanDefinitionBuilder
    				.genericBeanDefinition(FeignClientFactoryBean.class);
    				```
    				这里要注意的是,封装的BeanDefinition类为FeignClientFactoryBean,
    				
    				-> org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition
    					将BeanDeinition放入Spring IOC容器,也就是DefaultListableBeanFactory#beanDefinitionMap 中
    
    
    这里要注意的是,封装的BeanDefinition类为FeignClientFactoryBean,从FeignClientFactoryBean类名可以看出这是一个生产FeignClient工厂的Bean,
    且该类实现了`ApplicationContextAware`接口,也是一个Spring的上下文。
    FactoryBean的一个特征,当需要获得这个Bean的真正实例,会调用getObject()方法来实现,调用`org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject`
    		-> org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
    		  -> org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget
    			-> org.springframework.cloud.openfeign.DefaultTargeter#target 
    			  -> feign.Feign.Builder#target(feign.Target<T>)
    			   -> feign.ReflectiveFeign#newInstance
    			      解析接口声明的 GetMapping、接口路径、入参等
    				```
    					// 动态代理对象
    				    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);
    				```				
    				真正创建代理类:
    				  -> feign.InvocationHandlerFactory#create
    				  -> feign.InvocationHandlerFactory.Default#create
    				    -> feign.ReflectiveFeign.FeignInvocationHandler#FeignInvocationHandler
    
    	代理类生成后,当进行真正接口调用`xxxFeignClient.method()`时,动态代理会拦截,执行的是代理类的invoke方法:
    		-> feign.ReflectiveFeign.FeignInvocationHandler#invoke
    			内部先根据method,从dispatch中获取到对应执行类,根据method进行分发
    			```
    			dispatch.get(method).invoke(args);
    			```
    			-> feign.SynchronousMethodHandler#invoke
    				-> feign.SynchronousMethodHandler#executeAndDecode
    				  -> org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute
    				  ```
    			FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
    					this.delegate, request, uriWithoutHost);
    
    			IClientConfig requestConfig = getClientConfig(options, clientName);
    			return lbClient(clientName)
    					.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
    				  ```
    			
    			底层会依赖Ribbon的负载均衡,封装请求的request、encoder,接收response、decoder,底层仍是HTTP通信。	
    					
    
    
    

    本案例源码地址: Spring Cloud OpenFeign
    OpenFeign官网 : spring -> 头部导航Projects -> spring-cloud -> 左侧导航 Spring Cloud OpenFeign -> Spring Cloud OpenFeign -> 头部导航 LEARN -> Reference Doc.

  • 相关阅读:
    MiniOS系统
    《硅谷传奇》
    《构建之法》1—3章
    学术诚信与职业道德
    Sprint2
    Scrum 项目 7.0 Sprint回顾
    Scrum 项目 6.0 sprint演示
    Scrum 项目 5.0
    Scrum 项目4.0
    操作系统 实验三 进程调度模拟程序
  • 原文地址:https://www.cnblogs.com/Qkxh320/p/SpringCloud_OpenFeign.html
Copyright © 2020-2023  润新知