持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
09、在SpringCloud中使用Feign
SpringCloud整合feign
POM文件:
<!--SpringCloud整合feign添加依赖Start-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</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>
<!--使用下面的Jar包 spring-cloud-netflix-core,注解也使用下面jar包的@EnableFeignClients ,启动就会报错,估计是版本问题-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-netflix-core</artifactId>-->
<!--<version>1.4.4.RELEASE</version>-->
<!--</dependency>-->
<!--SpringCloud整合feign添加依赖end-->
在服务调用者的启动类中 ,打开 Feign 开关
import org.springframework.cloud.openfeign.EnableFeignClients;
//使用的是上面Jar包的注解 EnableFeignClients 如果使用另一个spring-cloud-netflix-core启动报错
@SpringBootApplication
@EnableDiscoveryClient
@EnableEurekaClient
@EnableFeignClients
public class SrlInvokerServerApplication {
public static void main(String[] args) {
SpringApplication.run(SrlInvokerServerApplication.class, args);
}
}
编写客户端接口 与直接使用 Feign 类似
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient("provider-server")
public interface PersonClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
String hello();
/**
* 使用了Spring Cloud的“翻译器”后,将不能再使用Feign的默认注解
*/
@RequestMapping(method = RequestMethod.GET, value = "/hello/{name}")
String hello(@PathVariable("name") String name);
@RequestMapping(method = RequestMethod.GET, value = "/person/{personId}")
Person getPerson(@PathVariable("personId") Integer personId);
}
与单独使用 Feign 不同的是,接口使用了@FeignClient 注解来修饰,井且声明了 需要 调用的服务名称,本例的服务提供者名称为 spring-feign-provider 。另外,接口方法使用 @RequestMapping 来修饰,根据 5.2 节的介绍可知,通过编写“翻译器(Contract)",可 以让 Feign 知道第三方注解的含义, Spring Cloud 也提供翻译器,会将@RequestMapping注 解的含义告知 Feign ,因此我们的服务接口就可以直接使用该注解 除了方法的@RequestMapping 注解外,默认还支持@RequestParam 、@RequestHeader @Path Variable 参数注解, 也就是说,在定义方法时,可以使用以下方式定义参数:
编写Controller类:
@RestController
@Configuration
public class FeignInvokerController {
//编译器报错,无视。 因为这个Bean是在程序启动的时候注入的,编译器感知不到,所以报错。
@Autowired
private PersonClient personClient;
@RequestMapping(value = "/invokerHello", method = RequestMethod.GET)
public String invokerHello() {
return personClient.hello();
}
@RequestMapping(value = "/invokerPerson", method = RequestMethod.GET)
public Person invokerPerson() {
Person person = personClient.getPerson(2);
return person;
}
}
Feign 负载均衡
我们尝试过编写自定义的 Feign 客户端,在 Spring Cloud 中,同样提供了自 定义的 Feign 客户端。大家可能己经猜到,如果结合 Ribbon 使用, Spring Cloud 所提供的 客户端会拥有负载均衡的功能 Spring Cloud 实现的 Feign 客户端,类名为 LoadBalancerFeignClient ,在该类中,维护 着与 SpringClientFactory 相关的实例。通过 SpringClientFactory 可以获取负载均衡器,负载 均衡器会根据一定 的规则来选取处理请求的服务器,最终实现负载均衡的功能。 Feign默认集成了Ribbon,使用上面Controller调用接口,即可实现负载均衡。前几节自定义的负载均衡规则,自定义的Ping机制,默认都会使用到
默认配置
SpringCloud为Feign的使用提供了各种默认属性,例如前面讲到的注解翻译器(Contract )、 Feign 客户端。默认情况下, Spring 将会为 Feign 的属性提供以下的 Bean。 默认情况下,Spring将会为Feign的属性提供bean
- 解码器(Decoder):bean 名称为 feignDecoder,ResponseEntityDecoder 类
- 编码器(Encoder):bean 名称为 feignEecoder,SpringEncoder 类
- 日志(Logger): bean 名称为 feignLogger,Slf4jLogger 类
- 注解翻译器(Contract): bean 名称为 feignContract,SpringMvcContract 类
- Feign 实例的创建者 Feign Builder : Bean 名称为 feignBuilder HystrixFeign.Builder,Hystrix 框架将在后 章节 讲述。
- Feign 客户端( Client) : Bean 名称为 feignClient , LoadBalanceFeignClient类
一般情况下, Spring 提供的这 Bean 己经足够我们使用,如果有些更特殊的需求,可 以实现自己的 Bean 请见下 小节
自定义配置
如果需要使用自己提供的 Feign 实现,可以 Spring 的配置类中返回对应的Bean,下面自定义一个简单的注解翻译器
@Configuration
public class MyContractConfig {
/**
* 返回一个自定义的注解翻译器
*/
@Bean
public Contract FeignContract() {
return new MyContract();
}
}
配置类中返回了 MyContract 实例 MyContract 是我们自定义的“翻译器”, 实现
public class MyContract extends SpringMvcContract {
@Override
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
//调用父类的方法,让其支持@RequestMapping 注解
super.processAnnotationOnMethod(data, methodAnnotation, method);
//是MyUrl的注解才处理
if (MyUrl.class.isInstance(methodAnnotation)) {
//获取注解的示例
MyUrl myUrlAnn = method.getAnnotation(MyUrl.class);
// 获取配置的Http 方法
String httpMethod = myUrlAnn.method();
// 获取请求服务的URL
String url = myUrlAnn.url();
//将值设置到模板中
data.template().method(httpMethod);
data.template().append(url);
}
}
}
在前面的章节中 ,我们也实现过自定义的 Contract,与前面实现的 Contract 不同的是, 本例的 MyContract 继承了 SpringMvcContract ,在重写 processAnnotationOnMethod 方法时, 调用了父类的 processAnnotationOnMethod 。简单点说 我们实现的这个 Contract 除了支持 Spring 的注解外,还支持我们自定义的 @MyUrl 注解。@MyUrl 注解与前面章节中介绍的一 致,
@Target(METHOD)
@Retention(RUNTIME)
public @interface MyUrl {
//定义 url 与 method 属性
String url();
String method() ;
}
编写接口类:分别使用Spring的注解 和 自定义的 @MyUrl注解 /**
- 默认会生成一个"provider-server"为ID的JavaBean在Spring容器里,如果多个这样的接口,@FeignClient值是一样的会报错
- 错误信息:The bean 'provider-server.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
- 解决方案:spring.main.allow-bean-definition-overriding=true #当遇到同样名字的时候,是否允许覆盖注册,默认false
- 还有就是 一个服务 例如,provider,只写一个接口 */
/**
* 默认会生成一个"provider-server"为ID的JavaBean在Spring容器里,如果多个这样的接口,@FeignClient值是一样的会报错
* 错误信息:The bean 'provider-server.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
* 解决方案:spring.main.allow-bean-definition-overriding=true #当遇到同样名字的时候,是否允许覆盖注册,默认false
* 还有就是 一个服务 例如,provider,只写一个接口
*/
@FeignClient("provider-server-BUG")
public interface HelloClientTestContract {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
String springHello();
@MyUrl(method = "GET", url = "/hello")
String MyHello();
}
Controller调用:
@RequestMapping(value = "/testContract", method = RequestMethod.GET)
@ResponseBody
public String testContract() {
String springResult = helloClient.springHello();
System.out.println("使用@RequestMapper注解返回的结果 " + springResult);
String myResult = helloClient.MyHello();
System.out.println("使用@MyUrl注解返回的结果 " + myResult);
return "";
}
除了自定义的注解翻译器外,还可以自定义其他的 Bean ,实现过程 实现与上面基本一致,
可选配置:
上面介绍的配置,spring为这些配置提供了默认的Bean,除了这些配置外,还有如下配置,Spring并没有提供默认的Bean。
- Logger.level:接口日志的记录级别,相当于调用了Feign.Builder的logLevel方法
- Retryer:重试处理器,相当于调用了Feign.Builder.的retryer方法
- ErrorDecoder:异常解码器,相当于调用了Feign.Builder.的ErrorDecoder方法
- Request.Options:设置请求的配置项,,相当于调用了 Feign.Builder的options 方法
- Collection”<”RequestInterceptor”>”:设置请求拦截器,相当于调用了 Feign.Builder requestlnterceptors 方法。
- SetterFactory:该配置与Hystrix框架有关,后面讲
以上的配置,如果没有提供对应的 Bean ,则不会被设置 在此需要注意的是请求拦截 器,由于可以设置多个请求拦截器,在创建 Bean 时也可以创建多个,返回类型需要为 Requestlnterceptor 或者实现类 要设置多个请求拦截器,请见以下代码片断
3.6、压缩配置 Feign 支持对请求和响应进行压缩处理,默认使用 GZIP 进行压缩,压缩操作在 Feign 的请求拦截器中实现。可以在配置文件中加入以下配置
- feign.compression.request.enabled:设置为true开启请求压缩
- feign.compression.response.enabled:设置为true开启响应压缩
- feign.compression.request.mine-types:数据类型列表,默认为text/xml,application/xml,application/json
- feign.compression.request.min-request-size:设置请求内容的最小阀值,默认2048