介绍
之前我们无论是基本调用,还是Hystrix,我们实际上都是通过手动调用RestTemplate来实现远程调用的。使用RestTemplate存在一个问题:繁琐,每一个请求,参数不同,请求地址不同,返回数据类型不容,其他都是一样的,所以我们希望对请求进行简化。简化方案就是openFeign。一开始这个组件不叫这个名字,一开始叫Feign,Netflix Feign,但是Netflix中的组件现在已经停止开源工作,openFeign是Springcloud团队在Netflix Feign的基础上开发出来的声明式服务调用组件。
简单使用
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.application.properties配置
spring:
application:
name: opentfeign
server:
port: 4000
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka
3.启动类加@EnableFeignClients注解
@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {
public static void main(String[] args) {
SpringApplication.run(OpenfeignApplication.class, args);
}
}
4.定义Feign服务层接口
定义接口之前在eureka-provider中添加一个方法用于测试参数放在header中传递
@GetMapping("/user3")
public void getUserByName(@RequestHeader String name){
System.out.println(name);
}
Feign服务层接口:
@FeignClient("provider") //跟某一个服务绑定
public interface HelloService {
@GetMapping("/hello")
String hello();
@GetMapping("/hello2")
String hello2(@RequestParam("name") String name);
@PostMapping("/user2")
User addUser(@RequestBody User user);
@DeleteMapping("/user2/{id}")
void deleteUserById(@PathVariable("id") Integer id);
@GetMapping("/user3")
void getUserByName(@RequestHeader("name") String name);
}
1.@FeignClient(“服务名”):跟某一个服务绑定
2.接口中的方法名可以随便写,但是映射名、返回值必须得跟对应方法保持一致
3.凡是key/value形式的参数,一定要标记参数的名称
4.放在header中的参数一定要编码之后再传递
@FeignClient
注解中相关参数:
- value/name:这两个是同一个含义,都是用配置client的服务名称
- serviceId:已经过期,使用name即可
- contextId:
- 1.不想把client的多个服务(接口)定义在一个类时使用(不配置时报错):比如我们有个user服务,但user服务中有很多个接口,我们不想将所有的调用接口都定义在一个类中
- 2.作为client别名的一部分(如果配置了@qualifier优先使用@qualifier别名)
- fallback:熔断或发生错误兜底处理类
- fallbackFactory:调用client发生错误兜底类
5.控制层调用接口
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello() throws UnsupportedEncodingException {
String pikachues = helloService.hello2("pikachues");
System.out.println(pikachues);
User user = new User();
user.setUsername("pikachues");
user.setPassword("123");
user.setId(99);
User u = helloService.add2(user);
System.out.println(u);
helloService.deleteUser2(99);
helloService.getUserByName(URLEncoder.encode("小明","UTF-8"));
return helloService.hello();
}
}
6.启动测试
继承性
从代码上看,eureka-provider提供的接口和openFeign所调用的接口几乎相同,这时我们可以将相同的部分抽取出来。
新建一个Module,叫hello-api,注意由于这个模块要被其他模块所依赖,所以这个模块是一个maven项目,但是由于这个模块要用到SpringMVC的东西,因此创建后,引入web依赖,导入SpringMVC需要的一套jar。
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
2.定义抽取的公共接口
public interface IUserService {
@GetMapping("/hello")
String hello();
@GetMapping("/hello2")
String hello2(@RequestParam("name") String name);
@PostMapping("/user2")
User add2(@RequestBody User user);
@DeleteMapping("/user2/{id}")
void deleteUser2(@PathVariable("id") Integer id);
@GetMapping("/user3")
void getUserByName(@RequestHeader("name") String name);
}
3.将hello-api打包之后在eureka-provider和openfeign中引入
<dependency>
<groupId>com.dpf</groupId>
<artifactId>hello-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
4.eureka-provider中HelloController实现该接口
@RestController
public class HelloController implements IUserService {
.....
}
5.openfeign中的HelloService继承该接口
@FeignClient("eureka-provider")
public interface HelloService extends IUserService {
}
到这里继承就实现了,可以跟之前一样测试一下,测试效果一样。
6.优缺点分析:
- 优点是使用继承性,可以使服务端和消费端代码统一简洁不易出错。
- 缺点是这样会增加服务端与消费端的耦合度。
日志
OpenFeign中,我们可以通过配置日志,来查看整个请求的调用过程。日志级别一共分为四种:
- NONE:不开启日志,默认就是这个
- BASIC:记录请求方法、URL响应状态码、执行时间
- HEADERS:在BASIC的基础上,加载请求/响应头
- FULL:在HEADERS基础上,再增加body以及请求元数据
1.通过bean配置日志
@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {
public static void main(String[] args) {
SpringApplication.run(OpenfeignApplication.class, args);
}
@Bean
Logger.Level loggerLevel(){
return Logger.Level.FULL;
}
}
2.application.yml开启日志级别
logging:
level:
com:
dpf:
openfeign:
service:
HelloService: debug
这里 logging.level 是指日志级别的前缀,com.dpf.openfeifn.service.HelloService,表示该类 以 debug 级别输出日志。当然,类路径也可以是一个 package ,这样就表示该 package 下的所有 class 以 debug 级别输出日志。启动调用接口可以看到如下日志。
数据压缩
数据的压缩,主要是解决传输效率,具体配置如下:
feign:
compression:
request:
#开启请求压缩
enabled: true
# 压缩的数据类型
mime-types: text/html,application/json
# 压缩数据的下限,2048表示当要传输的数据大于2048时,才会进行数据要锁
min-request-size: 2048
response:
# 开启响应压缩
enabled: true
整合Hystrix
1.在配置中开启Hystrix
hystrix:
enabled: true
2.第一种方法
- 定义HelloServiceFallback方法
@Component
@RequestMapping("pikachues") //防止请求地址重复
public class HelloServiceFallback implements HelloService{
@Override
public String hello() {
return "error1";
}
@Override
public String hello2(String name) {
return "error2";
}
@Override
public User add2(User user) {
return null;
}
@Override
public void deleteUser2(Integer id) {
}
@Override
public void getUserByName(String name) {
}
}
- 在HelloService中的FeignClient添加fallback属性
@FeignClient(value = "eureka-provider",fallback = HelloServiceFallback.class)
public interface HelloService extends IUserService {
}
3.第二种方法
- 定义HelloServiceFallbackFactory方法
@Component
public class HelloServiceFallbackFactory implements FallbackFactory<HelloService> {
@Override
public HelloService create(Throwable throwable) {
return new HelloService() {
@Override
public String hello() {
return "error1----";
}
@Override
public String hello2(String name) {
return "error2----";
}
@Override
public User add2(User user) {
return null;
}
@Override
public void deleteUser2(Integer id) {
}
@Override
public void getUserByName(String name) {
}
};
}
}
- 在HelloService中的FeignClient添加fallbackFactory 属性
@FeignClient(value = "eureka-provider",fallbackFactory = HelloServiceFallbackFactory.class)
public interface HelloService extends IUserService {
}
到这里整合就结束了。