SpringCloud(三)声明式Web服务客户端Feign
Feign是一个声明式的服务Web服务客户端,使得编写Web服务客户端更加容易。SpringCloud使用Feign,Ribbon和Eureka可实现负载均衡。
启用Feign
在调用方服务的pom文件中增加spring-cloud-starter-feign
依赖并声明启用Feign客户端。比如我们是web-app-service
调用sms-service
的接口,我们需要在web-app-service
服务中增加依赖并声明启用Feign。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(defaultConfiguration = FeignClientsConfiguration.class)
public class WebApplicationStarter {
public static void main(String[] args) {
SpringApplication.run(WebApplicationStarter.class, args);
}
}
新建一个Feign客户端
新建一个接口使用@FeignClient
注解,那么@FeignClient
注解中name
和value
属性用来创建一个Ribbon
负载均衡器。你也可以指定他的url
属性(绝对路径或者是一个主机名称)。
@FeignClient(name = "sms-service")
public interface SMSService {}
Robbin
客户端将根据@FeignClient
的属性发现sms-service
服务的物理地址。如果你的服务是一个Eureka客户端,他将会在Eureka服务端发现sms-service
服务。
@FeignClient
的configuration
属性可以自定义Feign客户端的配置,包括feign.Decoder
,feign.Encoder
和feignContract
,对于不同的FeignClient
他们的configuration
不会相互影响。
基于SpringMVC注解的FeignClient
SpringCloud中Feign默认的configuration
使用的Contract
是SpringMvcContract
所以它可以解析@RequestMapping
注解这样你就可以像开发一个Controller
一样定义一个接口。
@FeignClient(name = "sms-service")
public interface SMSService {
@RequestMapping(value="sms", method = RequestMethod.POST)
String add(@RequestBody SMSEntity entity);
@RequestMapping(value = "sms", method = RequestMethod.GET)
String queryAll();
@RequestMapping(value = "sms/{id}", method = RequestMethod.GET)
SMSEntity getById(@PathVariable("id") String id);
@RequestMapping(value = "sms/{id}", method = RequestMethod.PUT)
String update(@PathVariable("id") String id, @RequestBody SMSEntity entity);
@RequestMapping(value = "sms/{id}", method = RequestMethod.DELETE)
String delete(@PathVariable("id") String id);
}
基于Feign原生注解的FeignClient
我们也可以使用Feign原生的Contract
,在这个接口中我们的sms-service-cmmon-feign
服务在Eureka中不存在,我们通过url
指向一个Web服务,Feign通过url
和@RequestLine
解析路径和请求方式向Web服务发起请求。
/**
* 这是一个普通的Feign客户端,
* 使用这种方法可以直接调用未在同一Eureka(集群)中注册的应用或者外部接口
* Created by chenxyz on 2018/5/27.
*/
@FeignClient(name = "sms-service-common-feign", url = "http://localhost:8001", configuration = CommonFeignConfig.class)
public interface CommonSMSFeignService {
/** 普通GET请求 */
@RequestLine("GET /sms")
String querySms();
/** POST请求传递对象 */
@RequestLine("POST /sms")
String sendSms(SMSEntity sms);
/** 固定一个body请求的模板,占位符代表参数 */
@RequestLine("POST /sms")
@Headers("Content-Type: application/json")
// 可以是{"pohone":"10086","content":"hahha"}
// 或者可以是<xml></xml>
@Body("%7B\"phoneNo\": \"{phoneNo}\", \"message\": \"{message}\"%7D")
String sendSmsTemplate(@Param("phoneNo") String phoneNo, @Param("message") String message);
/** 获取短信内容 */
@RequestLine("GET /sms/{id}")
SMSEntity getSms(@Param("id") long id);
/** 获取短信内容 */
@Headers("Content-Type: application/json")
@RequestLine("PUT /sms/{id}")
String updateSms(@Param("id") long id, SMSEntity sms);
/** 获取短信内容 */
@RequestLine("DELETE /sms/{id}")
String delete(@Param("id") long id);
}
public class CommonFeignConfig {
@Bean
public Contract feignContract(ConversionService feignConversionService) {
return new feign.Contract.Default();
}
}
CommonFeignConfig
不必使用@Configuration
注解,如果你已经这么做了,需要从@ComponentScan
中排除,否则他将会成为feign.Decoder
,feign.Encoder
和feignContract
等方法默认的源。
SpringCloud的Feign客户端提供的默认的Bean。
BeanType | beanName | ClassName |
---|---|---|
Decoder | feignDecoder | ResponseEntityDecoder (which wraps a SpringDecoder) |
Encoder | feignEncoder | SpringEncoder |
Logger | feignLogger | Slf4jLogger |
Contract | feignContract | SpringMvcContract |
Feign.Builder | feignBuilder | HystrixFeign.Builder |
Client | feignClient | if Ribbon is enabled it is a LoadBalancerFeignClient, otherwise the default feign client is used |
CommonFeignConfig
使用feign.Contract.Default
代替了SpringMvcContract
,使得CommonSMSFeignService
可以解析原生的@RequestLine
注解。而SMSService
使用的SpringMvcContract
可以解析与SpringMVC中的@RequestMapping
注解。
在案例中两个Feign客户端分别使用了两个不同的configuration
,但是不会相互影响,这是因为他们是相互隔离的。SpringCloud为使用了FeignClientsConfiguration
的Feign客户端创建了一个新的ApplicationContext
集合。