java @FeignClient(name = "gateway-test", value = "gateway-test", url = "localhost:9997", fallbackFactory = FallbackFactory.class, contextId = "GatewayClient")
name/value: 服务名称
```java //查看源码可知: 两个属性等价 public @interface FeignClient { @AliasFor("value") String name() default "";
@AliasFor("value")
String name() default "";
} //查看源码可知: 两个属性至少要配置一个 FeignClientsRegistrar { private String getClientName(Map client) { if (client == null) { return null; } String value = (String) client.get("contextId"); if (!StringUtils.hasText(value)) { value = (String) client.get("value"); } if (!StringUtils.hasText(value)) { value = (String) client.get("name"); } if (!StringUtils.hasText(value)) { value = (String) client.get("serviceId"); } if (StringUtils.hasText(value)) { return value; } throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName()); } } ```
url: 请求地址, 没配置的话, 会把name/value的属性值当成服务名进行调用, 配置的话则使用url的值
示例:
Product服务: 服提供者, 服务名product-client, 存在一个接口/product/test, 返回"test"字符串
User服务: 调用方, 服务名user-client, 存在一个接口/user/test, 调用product服务的test接口
```java //配置url方式,请求地址为url的地址 @FeignClient(name = "product-client", url = "localhost:9997") public interface ProductClient {
/**
* ..
* @return .
*/
@GetMapping("/product/test")
String test();
}
@RequestMapping("/user") public class UserController {
@Resource
private ProductClient client;
@GetMapping("/test")
public String test() {
return client.test();
}
}
//访问http://localhost:8200/user/test报错: //Connection refused: connect executing GET http://localhost:9997/product/test with root cause //说明配置了url属性后,确实是根据url的值作为请求地址进行调用 ```
```java //使用服务名调用方式 @FeignClient(name = "product-client") public interface ProductClient {
/**
* ..
* @return .
*/
@GetMapping("/product/test")
String test();
} //调用成功,后台能看到获取注册中心(nacos)的数据解析获取真实的url地址的日志 ```
```java //用一个不存在的服务名再验证一下 @FeignClient(name = "product-client123") public interface ProductClient {
/**
* ..
* @return .
*/
@GetMapping("/product/test")
String test();
} //报错信息: com.netflix.client.ClientException: Load balancer does not have available server for client: product-client123 ```
fallbackFactory: 获取异常信息
示例:
```java user服务开启hystrix feign: hystrix: enabled: true
//user服务添加fallback,用于在调用服务出现错误时,返回自定义信息 @Component public class ProductClientFallback implements ProductClient{ @Override public String test() { return "error"; } }
//user服务添加fallbackFactory,用户在调用服务出现错误时,获取错误信息 @Component public class ProductClientFallbackFactory implements FallbackFactory { @Resource ProductClientFallback fallback; @Override public ProductClient create(Throwable cause) { System.out.println("cause.getMessage() = " + cause.getMessage()); return fallback; } }
//product服务修改/product/test接口,模拟出现异常 @RestController @RequestMapping("/product") public class ProductController { @GetMapping("/test") public String test() throws Exception { throw new Exception("test Exception"); } }
```
Postman访问 http://localhost:8200/user/test ,成功返回自定义的异常信息
查看服务调用方User的日志, 成功打印出错误日志
message信息为空的话,配置一下Product服务:
yaml server: error: include-message: ALWAYS
具体原因请查看 博客, 也可以通过使用@ControllerAdvice来解决
contextId: 别名
假设一个User服务有两个FeignClient,都需要调用Product服务, 因为name属性值一样,所以需要通过配置contextId来区分,否则启动项目时会报错
```java @FeignClient(name = "product-client", fallbackFactory = ProductClientFallbackFactory.class, contextId = "ProductClientCopy") public interface ProductClientCopy {
/**
* ..
* @return .
*/
@GetMapping("/product/testCopy")
String test();
}
@FeignClient(name = "product-client", fallbackFactory = ProductClientFallbackFactory.class, contextId = "ProductClient") public interface ProductClient {
/**
* ..
* @return .
*/
@GetMapping("/product/testCopy")
String test();
} ```