内容简介:上一篇文章,讲述了如何通过创建一个在
上一篇文章,讲述了如何通过 RestTemplate
配合 Ribbon
去消费服务。 Feign
是一个 声明式
的 HTTP
伪客户端
,提供 面向接口
的 HTTP
客户端调用
编程。本文进一步讲如何通过 Feign
去消费服务。
-
Feign
只需要创建一个 接口 并提供 注解 即可调用。 -
Feign
具有 可插拔 的注解特性,可使用Feign
注解 和JAX-RS
注解 。 -
Feign
支持 可插拔 的 编码器 和 解码器 。 -
Feign
默认集成了Ribbon
,可以和Eureka
结合使用,默认实现了 负载均衡 的效果。
正文
1. 创建服务契约模块
创建一个 service-contract
的项目 Module
,创建完成后的 pom.xml
如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>io.ostenant.github.springcloud</groupId> <artifactId>service-contract</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-contract</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.ostenant.github.springcloud</groupId> <artifactId>service-contract</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project> 复制代码
在 service-contract
中定义 业务接口
和相关的 DTO
对象如下:
User.java
public class User implements Serializable { private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } public void getName() { return this.name; } public String setName() { this.name = name; } public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } } 复制代码
UserContract.java
UserContract
定义了 User
的所有行为,是一个使用 @FeignClient
注解标记的 声明式服务接口
。其中, @FeignClient
的 value
指定的是 服务提供者
的 服务名称
。
@FeignClient("service-provider") public interface UserContract { @PostMapping("/user") void add(@RequestBody User user); @GetMapping("/user/{name}") User findByName(@PathVariable String name); @GetMapping("/users") List<User> findAll(); } 复制代码
对于 服务提供者
而言,需要实现 UserContract
接口的方法;对于 服务消费者
而言,可以直接注入 UserContract
作为 客户端桩
使用。
2. 创建服务提供者
创建一个 service-provider
的项目 Module
,创建完成后引入 服务契约模块
的依赖, pom.xml
如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>io.ostenant.github.springcloud</groupId> <artifactId>service-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.ostenant.github.springcloud</groupId> <artifactId>service-contract</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 复制代码
通过 注解
@EnableEurekaClient
表明自己是一个 Eureka Client
。
@SpringBootApplication @EnableEurekaClient @RestController public class ServiceProviderApplication { public static void main(String[] args) { SpringApplication.run(ServiceProviderApplication.class, args); } } 复制代码
创建一个类 UserService
,实现 UserContract
接口的具体业务,对外提供 User
相关的 HTTP
的服务。
@RestController public class UserService implements UserContract { private static final Set<User> userSet = new HashSet<>(); static { userSet.add(new User("Alex", 28)); userSet.add(new User("Lambert", 32)); userSet.add(new User("Diouf", 30)); } @Override public void add(@RequestBody User user) { userSet.add(user); } @Override public User findByName(@PathVariable String name) { return userSet.stream().filter(user -> { return user.getName().equals(name); }).findFirst(); } @Override public List<User> findAll() { return new ArrayList<>(userSet); } } 复制代码
在 配置文件
中注明的 服务注册中心
的地址, application.yml
配置文件如下:
spring: active: profiles: sp1 # sp2 --- spring: profiles: sp1 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8770 spring: application: name: service-provider --- spring: profiles: sp2 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8771 spring: application: name: service-provider 复制代码
分别以 spring.profiles.active=sp1
和 spring.profiles.active=sp2
作为 Spring Boot
的 启动命令参数
,在 端口号
8770
和 8771
启动 2
个 服务提供者
实例。
3. 创建服务消费者
新建一个项目 Module
,取名为 service-consumer
,在它的 pom
文件中引入 Feign
的 起步依赖
和 服务契约模块
,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>io.ostenant.github.springcloud</groupId> <artifactId>service-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-consumer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.ostenant.github.springcloud</groupId> <artifactId>service-contract</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 复制代码
在项目的配置文件 application.yml
文件,指定 应用名称
为 service-consumer
, 端口号
为 8772
, 服务注册地址
为 http://localhost:8761/eureka/
,代码如下:
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8772 spring: application: name: service-feign 复制代码
在应用的启动类 ServiceConsumerApplication
上加上 @EnableFeignClients
注解开启 Feign
的功能。
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class ServiceConsumerApplication { public static void main(String[] args) { SpringApplication.run(ServiceConsumerApplication.class, args); } } 复制代码
定义一个 UserController
控制器,用于调用 服务提供者
提供的服务并 响应
前端。
@RestController public class UserController { @Autowired private UserContract userContract; @PostMapping("/user") public void add(@RequestBody User user) { userContract.add(user); } @GetMapping("/user/{name}") public User findByName(@PathVariable String name) { return userContract.findByName(name); } @GetMapping("/users") public List<User> findAll() { return userContract.findAll(); } } 复制代码
在 控制层
UserController
引入 Feign
接口,通过 @FeignClient
( 服务名称
),来指定调用的是哪个 服务
。
启动 服务消费者
应用,访问 http://localhost:8772/users
测试 服务消费者
的访问连通性,响应内容为:
[ { "name": "Alex", "age": 28 }, { "name": "Lambert", "age": 32 }, { "name": "Diouf", "age": 30 } ] 复制代码
4. Feign的源码实现过程
总的来说, Feign
的 源码实现
过程如下:
-
首先通过
@EnableFeignClients
注解开启FeignClient
的功能。只有这个 注解 存在,才会在程序启动时 启动@FeignClient
注解 的 包扫描 。 -
服务提供者实现基于
Feign
的 契约接口 ,并在 契约接口 上面加上@FeignClient
注解。 -
服务消费者启动后,会进行 包扫描 操作,扫描所有的
@FeignClient
的 注解 的类,并将这些信息注入Spring
上下文中。 -
当 接口 的方法被调用时,通过
JDK
的 代理 来生成具体的RequestTemplate
模板对象 。 -
根据
RequestTemplate
再生成HTTP
请求的Request
对象。 -
Request
对象交给Client
去处理,其中Client
内嵌的 网络请求框架 可以是HTTPURLConnection
、HttpClient
和OkHttp
。 -
最后
Client
被封装到LoadBalanceClient
类,这个类结合Ribbon
完成 负载均衡 功能。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 基于 Pub/Sub 的同步 RRPC 调用实战
- 实战使用 Arthas 排查生产问题:实例方法接口调用
- Python实战---制作专属有声小说(调用百度语音合成接口)
- Jaeger-分布式调用链跟踪系统理论与实战
- Spring Cloud实战系列(二) - 客户端调用Rest + Ribbon
- FGC实战:如何用Idea揪出开源组件调用System.gc导致频繁FGC
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。