@FeignClient常用屬性

@FeignClient(name = "gateway-test", value = "gateway-test", url = "localhost:9997", fallbackFactory = FallbackFactory.class, contextId = "GatewayClient")

name/value: 服務名稱

//查看源碼可知: 兩個屬性等價
public @interface FeignClient {
   @AliasFor("value")
	String name() default "";

	@AliasFor("value")
	String name() default ""; 
}
//查看源碼可知: 兩個屬性至少要配置一個
FeignClientsRegistrar {
    private String getClientName(Map<String, Object> 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的值

示例:

image

Product服務: 服提供者, 服務名product-client, 存在一個接口/product/test, 返回”test”字符串

User服務: 調用方, 服務名user-client, 存在一個接口/user/test, 調用product服務的test接口

//配置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();
    }
}    
//訪問//localhost:8200/user/test報錯:
//Connection refused: connect executing GET //localhost:9997/product/test with root cause
//說明配置了url屬性後,確實是根據url的值作為請求地址進行調用

//使用服務名調用方式
@FeignClient(name = "product-client")
public interface ProductClient {

    /**
     *  ..
     * @return .
     */
    @GetMapping("/product/test")
    String test();
}
//調用成功,後台能看到獲取註冊中心(nacos)的數據解析獲取真實的url地址的日誌

image

//用一個不存在的服務名再驗證一下
@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: 獲取異常信息

示例:

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<ProductClient> {
    @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訪問 //localhost:8200/user/test ,成功返回自定義的異常信息

image

查看服務調用方User的日誌, 成功打印出錯誤日誌

image

message信息為空的話,配置一下Product服務:

server:
  error:
    include-message: ALWAYS

具體原因請查看 博客, 也可以通過使用@ControllerAdvice來解決

contextId: 別名

假設一個User服務有兩個FeignClient,都需要調用Product服務, 因為name屬性值一樣,所以需要通過配置contextId來區分,否則啟動項目時會報錯

@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();
}