• 【分布式】SpringCloud(5)--Feign负载均衡


    1.Feign概述

    1.1.Feign是什么

    Feign是一个声明式的web服务客户端,使得编写Web服务客户端变得非常容易。只需要创建一个接口,然后在上面添加注解即可

    1.2.Feign能干什么

    相比与Ribbon,Ribbon在实现负载均衡调用请求时,是利用RestTemplate请求进行封装处理,形成了一套Restful风格的模板化调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用

    Feign在Ribbon的基础上做了进一步封装,在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用SpringCloud Ribbon时,自动封装服务调用客户端的开发量。

    2.Feign的简单使用

    首先理一下思路:Feign的实现是基于服务提供方的接口来实现调用的,那么我们事先就要创建一个声明式的接口,而这个接口我们一般放在pojo公共类的服务方。方便之后在客户端调用服务提供者的接口时,只需要引入pojo微服务的依赖jar包即可;再通过接口注入Bean的方式,使用Feign提供的注解,开启对服务提供者远端接口的请求调用,无需开发者再自己构造一套复杂的Restful url请求调用。

    Feign的风格:接口调用、依赖注入请求接口;

    Ribbon的风格:基于RestTemplate调用、还需要自己封装请求url;

    相对来说Feign的实现方式更符合面向接口编程的套路,代码更加简化便捷。

    2.1.公共类api接口编写

    (1)在springcloud-api公共pojo服务类下新创建一个服务接口service层,编写客户端调用接口:

    //value中对应服务提供方的名字(value值表示调用服务的名称)
    @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
    @Component //注入到容器中
    public interface DeptClientService {
        /**
         * 接口中的三个方法对应服务提供者的三个方法
         * 这里面的接口对应服务提供者的controller接口,名称必须一致,然后接口的Mapping映射url必须一致
         * @param dept
         * @return
         */
    
        @PostMapping("/dept/add")
        public boolean addDept(Dept dept);
    
        @GetMapping("/dept/queryById/{id}")
        public Dept queryById(@PathVariable("id") Long id);
    
        @GetMapping("/dept/queryAll")
        public List<Dept> queryAll();
    }

    2.2.消费方客户端服务调用Feign方式实现

    这里基于原来Ribbon实现客户端调用来重写Feign实现,具体Ribbon实现方式见我上一篇博文:SpringCloud(4)--Ribbon负载均衡,这里不再过多描述。

    (1)引入pom.xml依赖:

     <!--引入feign:版本和ribbon相同-->
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-feign</artifactId>
         <version>1.4.6.RELEASE</version>
     </dependency>
     <!--引入Ribbon相关:ribbon->eureka->config-->
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-ribbon</artifactId>
         <version>1.4.6.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-config</artifactId>
     </dependency>
     <!--微服务provider方引入eureka-->
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <!--没有带sever表示就是client客户端-->
         <artifactId>spring-cloud-starter-eureka</artifactId>
         <version>1.4.6.RELEASE</version>
     </dependency>
    <dependency>
        <groupId>com.fengye</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    (2)application.yml配置:

    server:
      port: 81
    
    #spring的配置
    spring:
      application:
        name: springcloud-consumer-dept  #默认注册进eureka中的实例名称(显示大写)
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/springcloud_db01?useUnicode=true&characterEncoding=utf-8
        username: root
        password: admin
    
    #eureka的配置,确定客户端服务注册到eureka服务列表内
    eureka:
      client:
        register-with-eureka: false  #表示是否向Eureka注册中心注册自己,false表示不注册自己
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

    (3)基于接口编写客户端请求Controller:

    @RestController
    public class DeptConsumerController {
        @Autowired
        private DeptClientService deptClientService;  //注入api中公共接口,面向接口调用服务
    
        @RequestMapping("/consumer/dept/add")
        public boolean add(Dept dept){
            return deptClientService.addDept(dept);
        }
    
        @RequestMapping("/consumer/dept/get/{id}")
        public Dept get(@PathVariable("id") Long id){
            Dept result = deptClientService.queryById(id);
            System.out.println("客户端请求:data from port= " + result.getDname());
            return result;
        }
    
        @RequestMapping("/consumer/dept/list")
        public List<Dept> list(){
            return deptClientService.queryAll();
        }
    }

    (4)启动类中开启FeignClients注解支持:

    @SpringBootApplication
    @EnableEurekaClient  //标识这是一个eureka的client客户端
    @EnableFeignClients(basePackages = {"com.fengye.springcloud"})  //扫描哪个包下面的Feign注解
    public class DeptServiceFeignConsumer {
        public static void main(String[] args) {
            SpringApplication.run(DeptServiceFeignConsumer.class, args);
        }
    }

    2.3.启动服务测试

    这里就单独只启动重写的Feign客户端服务调用请求即可。

    请求接口访问:默认轮询输出dbSource

    客户端请求打印输出语句,可以看到Feign同样实现了服务提供端的负载均衡

     

     3.Feign原理简述

    Feign注解实现负载均衡的执行流程简要说明:

    • 启动时,程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate
    • RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。
    • RequestTemplate声明Request,然后将Request交给client处理,这个client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
    • 最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用。
    更多Feign源码底层细节及Feign参数使用细则推荐两篇博文:

    微服务实战SpringCloud之Feign简介及使用

    Spring Cloud Feign - 内部实现细节

     

    本节涉及实例及相关代码已上传至github:

    https://github.com/devyf/SpringCloud_Study/tree/main/springcloud_hello

  • 相关阅读:
    解决异常:“The last packet sent successfully to the server was 0 milliseconds ago. ”的办法
    关于时间复杂度
    关于如何在MyEclipse下修改项目名包名,以及类
    Error filterStart
    类A是公共的,应在名为A.java的文件中声明错误
    Eclipse快捷键大全
    JVM 是用什么语言写的?
    退出cmd命令
    Java 如何对文件进行多个Object对象流的读写操作
    SublimeText2 快捷键一览表
  • 原文地址:https://www.cnblogs.com/yif0118/p/14615127.html
Copyright © 2020-2023  润新知