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负载均衡地发起调用。
本节涉及实例及相关代码已上传至github:
https://github.com/devyf/SpringCloud_Study/tree/main/springcloud_hello