这段时间一直在用RestTemplate做restful服务的调度,与新同事交流后学到了另外的方式用Feign来调用。以前用dubbo多了,确实对spring-cloud全家桶的认识不足。今天用feign的调用方式将文件服务的相关接口做了改造。但是对@FeignClient注解的相关属性不是很清楚。同时在不指定url的情况下,feign是如何找到服务地址的?带着这两个问题,做了今天的源码解读。接下来做个总结
一、@FeignClient的各属性解读
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
/**
* 说明:
* 1、value 与 name 互为别名,两者二选一即可
* 2、当 contextId 没有值的时候,会默认获取(value/name)的值
* 3、当未指定 url 请求地址的时候,最终会通过 ribbon-loadbalancer工具,从consul注册节点中选取 service-id 等于 value的服务作为请求地址
* @return
*/
@AliasFor("name")
String value() default "";
/**
* 说明:
* 1、serviceId 已经作废,其目的与 value一致。如果设置了 serviceId,则value/name 皆以 serviceId 为准
*/
/** @deprecated */
@Deprecated
String serviceId() default "";
/**
* 说明:
* 1、当 contextId 没有值的时候,会默认获取(value/name)的值
* 2、当 qualifier 没有值的时候,会将 '${contextId}FeignClient'作为 feign 的bean组件别名
*/
String contextId() default "";
/**
* 说明:同 value, 二选一
*/
@AliasFor("value")
String name() default "";
/**
* 说明:
* 1、feign的bean组件别名,拥有最高优先级;
* 2、当 qualifier 为空时,取 '${contextId}FeignClient' 作为 bean 的名称
*/
String qualifier() default "";
/**
* 说明:
* 1、设置 url 以后,后续发起http调用时,直接读取该地址作为请求目标;
* 2、未设置 url 时,借助 ribbon-loadbalancer 组件,根据 (value/name)从consul的服务列表中选中service-id匹配的目标服务;
*/
String url() default "";
boolean decode404() default false;
Class<?>[] configuration() default {};
Class<?> fallback() default void.class;
Class<?> fallbackFactory() default void.class;
/**
* 说明:
* 目标服务器 对应的资源 uri (FeignClient下所有方法相同的path路径)
*/
String path() default "";
boolean primary() default true;
}
核心源码截图如下
org\springframework\cloud\spring-cloud-openfeign-core\2.1.5.RELEASE\spring-cloud-openfeign-core-2.1.5.RELEASE.jar!\org\springframework\cloud\openfeign\FeignClientsRegistrar.class
二、当FeignClient未设置url时,通过ribbon查找服务的核心方法
\com\netflix\ribbon\ribbon-loadbalancer\2.3.0\ribbon-loadbalancer-2.3.0.jar!\com\netflix\client\AbstractLoadBalancerAwareClient.class
三、使用Feign做restful服务调用的简单示例
3.1、开启FeignClient的扫描(EnableFeignClients)
@Configuration
@EnableFeignClients(basePackages = "com.simm")
public class FeignConfiguration {
}
3.2、设置 RequestInterceptor,统一添加授权的Header
@Slf4j
@Component(value = "core_feign_interceptor")
@AllArgsConstructor
public class FeignInterceptor implements RequestInterceptor {
private final ClientTokenTemplate clientTokenTemplate;
/**
* fengin请求添加header
* @param requestTemplate
*/
@Override
public void apply(RequestTemplate requestTemplate) {
// 如果token中的类型加入了验证, 则设置rdc token
Collection<String> authHeaders = requestTemplate.headers().get(TokenConstant.AUTH_TYPE);
if (CollectionUtils.isNotEmpty(authHeaders) && authHeaders.contains(TokenConstant.CLIENT_AUTH)) {
requestTemplate.header("Authorization", clientTokenTemplate.getRedisToken().getAccess_token());
} else {
AuthInfo authInfo = BizContext.getValue(Constants.BizContextKey.AUTH);
if(authInfo!=null && !StringUtils.isEmpty(authInfo.getAccess_token())){
requestTemplate.header("Authorization", authInfo.getAccess_token());
}
}
}
}
View Code
3.3、文件服务的客户端实现
/**
* 文件服务客户端
*
* @author simm
*/
@ConditionalOnProperty(name = {"gateway.host", "gateway.apis.file-service"})
@FeignClient(value = "rdc-file-service", url = "${gateway.host}/${gateway.apis.file-service}", path = "/api/v2/files")
public interface RdcFileClient {
/**
* 获取文件信息
*
* @param fileId 文件ID
* @return 文件内容
*/
@GetMapping(value = "/{fileId}", headers = TokenConstant.GATEWAY_AUTH_HEADER)
PluginFileInfoResponse getFileInfo(@PathVariable String fileId);
/**
* 上传文件测试
*
* @param file 文件
* @return 文件注册信息
*/
@PostMapping(value = "/", headers = TokenConstant.GATEWAY_AUTH_HEADER, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
PluginFileInfoResponse uploadFile(MultipartFile file);
/**
* 获取下载地址
*
* @param fileId 文件ID
* @return
*/
@GetMapping(value = "/{fileId}/download-url", headers = TokenConstant.GATEWAY_AUTH_HEADER)
String getDownloadUrl(@PathVariable String fileId);
/**
* 批量获取文件详情
*
* @param object 信息
* @return
*/
@PostMapping(value = "/batchInfos", headers = TokenConstant.GATEWAY_AUTH_HEADER,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
List<PluginFileInfoResponse> batchGetFileDetail(JSONObject object);
/**
* 删除缓存的文件
*
* @param fileId 文件ID
* @return
*/
@DeleteMapping(value = "/{fileId}", headers = TokenConstant.GATEWAY_AUTH_HEADER)
Object delFile(@PathVariable String fileId);
}
View Code
每天都是崭新的开始 ——Mr.司满(214382122)[]~( ̄▽ ̄)~*