Spring Cloud 组件 —— feign
feign 作为一个声明式的 Http Client 开源项目。在微服务领域,相比于传统的 apache httpclient 与在 spring 中较为活跃的 RestTemplate 更面向服务化,更易于使用。底层封装了 Jersey 与 CXF 分别用于 REsT 与 SOAP 的服务(对应有 JAX-RS 与 JAX-WS API),当然也可以配置换成其它类似的实现,比如 OkHttp 、Ribbon 或者 Apache HC 等。
feign 基本用法及注解的使用看官方文档。下面介绍下 Spring Cloud 中的封装及实现细节(Spring Cloud 文档):
一、 基本用法
现有服务提供方:需要调用它的 createUser 方法
@RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @RequestMapping(value = "/current", method = RequestMethod.GET) public Principal getUser(Principal principal) { return principal; } @PreAuthorize("#oauth2.hasScope('server')") @RequestMapping(method = RequestMethod.POST) public void createUser(@Valid @RequestBody User user) { userService.create(user); } }
使用 feign 构建消费服务方,遵循以下步骤:
引入 maven 依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
1. 启用:使用 @EnableFeignClients 注解启动 feign 模块基础功能(扫描 feign client包,使用默认或指定的相关配置等等)
@SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
2. 声明:使用 @FeignClient("app-name") 创建声明式的 HC:很简单配置下 service 名称/url 与 发出请求的路径就好了。
@FeignClient(name = "auth-service") public interface AuthServiceClient { @RequestMapping(method = RequestMethod.POST, value = "/users", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) void createUser(User user); }
3. 调用:上面的代码,相对于服务提供方,它是客户端服务消费方。相对于服务消费方自身,它上升到一个 service,因此我们可以在 service 层或 controller 层调用它:
@Service public class AccountServiceImpl implements AccountService { private final Logger log = LoggerFactory.getLogger(getClass()); @Autowired private AuthServiceClient authClient; @Autowired private AccountRepository repository; /** * {@inheritDoc} */ @Override public Account create(User user) { Account existing = repository.findByName(user.getUsername()); Assert.isNull(existing, "account already exists: " + user.getUsername()); // 调用feign HC服务 authClient.createUser(user); Saving saving = new Saving(); saving.setAmount(new BigDecimal(0)); saving.setCurrency(Currency.getDefault()); saving.setInterest(new BigDecimal(0)); saving.setDeposit(false); saving.setCapitalization(false); Account account = new Account(); account.setName(user.getUsername()); account.setLastSeen(new Date()); account.setSaving(saving); repository.save(account); log.info("new account has been created: " + account.getName()); return account; } ...
二、feign 的相关配置(比如日志、hytrix 等)
配置方式有 3种:
1. 使用 JavaConfig 方式
配置类(这里建议不要加 @Configuration 注解,否则将变成全局配置)
public class FileConf { private final ObjectFactory<HttpMessageConverters> messageConverters; @Autowired public FileConf(ObjectFactory<HttpMessageConverters> messageConverters) { this.messageConverters = messageConverters; } @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } }
调用端
@FeignClient(value = "service-thirdparty", configuration = FileConf.class) @RequestMapping(value = "/storage") public interface StorageClient { /** * 上传图片 */ @RequestMapping( value = "/imageUpload", method = RequestMethod.POST, consumes = MULTIPART_FORM_DATA_VALUE) BusinessResult uploadImage(@RequestParam(value = "bucket") String bucket, @RequestPart(value = "file", required = false) MultipartFile file);
2. 使用 application.yml
# To disable Hystrix in Feign feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic hystrix: enabled: false # To set thread isolation to SEMAPHORE hystrix: command: default: execution: isolation: strategy: SEMAPHORE logging.level.project.user.UserClient: DEBUG
注意事项:
① 如果 @Configuration的 JavaConfig 与 properties 配置同时存在,那么后者会覆盖前者。通过设定 feign.client.default-to-properties
to false
. 改变优先权。
② 只有开启 DEBUG级别的日志, feign 的log功能才会生效。
③ 如果 classpath 引入了 hytrix(如下),并且配置启用 hytrix(比如 feign.hystrix.enabled=true),那么默认会启用 circuit breaker 。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
在 Spring Cloud Dalston 及之后的版本中,circuit breaker 是可选操作。需要使用 @EnableCircuitBreaker 手动开启。
@SpringBootApplication @EnableFeignClients @EnableCircuitBreaker public class Application {
扩展:使用断路器功能
声明:
@FeignClient(name = "statistics-service", fallback = StatisticsServiceClientFallback.class) public interface StatisticsServiceClient { @RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) void updateStatistics(@PathVariable("accountName") String accountName, Account account); }
定义 hytrix:
@Component public class StatisticsServiceClientFallback implements StatisticsServiceClient { private static final Logger LOGGER = LoggerFactory.getLogger(StatisticsServiceClientFallback.class); @Override public void updateStatistics(String accountName, Account account) { LOGGER.error("Error during update statistics for account: {}", accountName); } }
233
参考资料
https://github.com/sqshq/PiggyMetrics (完整示例)