Feign學習筆記
項目依賴
feign需要依賴eureka客戶端。目前一定需要連上eureka,所以目前還沒找到可不走euraka直連的方法(已有辦法,看下篇博客),所以現在就走eureka好了。
依賴如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transprot-native-epoll</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
啓動類
啓動類其實並沒有什麼,只是@EnableEurekaClient 啓動euraka客戶端,@EnableFeignClients 啓動feign客戶端
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class FeignMain {
public static void main(String[] args) {
SpringApplication.run(FeignMain.class, args);
}
}
需要注意的是,使用euraka是需要在application.yml中配置spring.application.name: xxxxx。
此處只貼出了feign客戶端的application.yml。服務提供方的feign就不貼了。
server.port: 10103
spring:
application:
name: feign
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://xx.xx.xx.xx:10101/eureka/
instance:
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
prefer-ip-address: true
lease-expiration-duration-in-seconds: 30
lease-renewal-interval-in-seconds: 10
FeignClient
使用接口定義,然後加上@FeignClient(“服務提供放名稱”)即可使用。其他語法與spring mvc相同
@FeignClient("rest-server")
public interface OrdinaryService {
@RequestMapping(value = "/getNum")
int getRandomInt();
}
實戰細節
FeignClient默認RequestMethod是GET
@RequestMapping(value = "/getNum", method = RequestMethod.GET)
public int getRandomInt() {
Random random = new Random();
Integer num = random.nextInt(100);
logger.info("getRandomInt:" + num);
return num;
}
客戶端使用如下方法調用
@RequestMapping(value = "/getNum")
int getRandomInt();
如果將服務提供方的方法改爲post,測試時會提示找不到方法。
默認參數使用RequestBody
GET轉換爲POST的是有比較複雜的原因,也有方法可以避免。後面詳解。
原因簡介:因爲此處參數中沒有加入註釋所以參數是用的RequestBody。
服務提供方方法
@RequestMapping(value = "/addOne", method = RequestMethod.POST)
public int addOne(@RequestBody int num) {
return num + 1;
}
客戶端使用如下方法調用
@RequestMapping(value = "/addOne")
int addOne(int num);
GET請求被轉換爲POST
如果上個方法中客戶端調用沒有寫method = RequestMethod.POST,這裏與默認使用GET方法有衝突?不,因爲feign默認使用的HTTPClient是java自帶的。這個http客戶端會檢查body中是否攜帶參數,如果有就會吧GET請求轉換爲POST。
將上面代碼改爲:
服務提供方方法
@RequestMapping(value = "/addOne", method = RequestMethod.GET)
public int addOne(@RequestBody int num) {
return num + 1;
}
客戶端使用如下方法調用
@RequestMapping(value = "/addOne", method = RequestMethod.GET)
int addOne(int num);
結果出現
feign.FeignException: status 405 reading OrdinaryService#addOne(int); content:
{"timestamp":1501516961283,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/addOne"}
避免GET請求被轉換成POST請求
實際上上面的方法將GET改爲POST就可以正常使用,但是實際上開發中這樣寫還是非常彆扭。比如,我有如下接口。
服務提供方方法
@RequestMapping(value = "/addTwo", method = RequestMethod.GET)
public int addTwo(int num) {
return num + 1;
}
這麼正常的方法,是在找不出理由去修改成爲post。實際上調用方也是支持的
客戶端使用如下方法調用
@RequestMapping(value = "/addTwo", method = RequestMethod.GET)
int addTwo(@RequestParam("num") int num);
此處有坑
RequestParam中必須填寫,如果不填寫會出現
Caused by:java.lang.IllegalStateException:QueryMap parameter must be a map:class ....
nested exception is java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
異常信息都是隻截取了一半,但是都是關鍵信息
第一個異常是spring-cloud-netflix-core-1.2.6.RELEASE.jar會出現的。
第二個異常是spring-cloud-netflix-core-1.3.2.RELEASE.jar出現的。
其他版本應該差不多,雖然進化了好幾個版本,但是主要問題依舊在於feign重寫了RequestParam註解但是沒有給予value默認值引起的。
所以使用RequestParam必須給予值,因爲沒有默認值