spring-cloud服務(wù)間調(diào)用 - 下 (feign)

前言
昨天我們分享了spring-cloud基于ribbon的服務(wù)調(diào)用,通過(guò)一個(gè)簡(jiǎn)單實(shí)例演示了ribbon負(fù)載均衡調(diào)用的基本方式,同時(shí)我們也提到了一些需要注意的點(diǎn),從總體內(nèi)容上來(lái)說(shuō),代碼量還是比較少的,而且過(guò)程也不算復(fù)雜,不過(guò)按照我最開(kāi)始的想法,是計(jì)劃把feign和它一起分享的,后來(lái)考慮到時(shí)間和篇幅的問(wèn)題,就把feign放在今天來(lái)講,所以今天我們就著重來(lái)分享下spring-cloud的另一種調(diào)用方式——聲明式調(diào)用(feign)。
Feign
依賴
首先我們要引入eureka和feign的pom依賴,這里需要注意的是,feign的版本必須要與spring-cloud的版本保持一致,否則啟動(dòng)會(huì)報(bào)錯(cuò),具體錯(cuò)誤可以看后面踩坑部分的內(nèi)容:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
調(diào)用方配置
引入pom依賴后,我們還需要服務(wù)對(duì)調(diào)用進(jìn)行配置,配置的方式很簡(jiǎn)單,只需要在調(diào)用方核心類加上@EnableFeignClients注解
@SpringBootApplication
@EnableFeignClients(basePackages = "io.github.syske.productservice")
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
這個(gè)注解的作用就是啟用FeignClients調(diào)用,這里的包可以指定也可以不指定,不指定的話我猜測(cè)它應(yīng)該會(huì)取ProductServiceApplication當(dāng)前的包路徑進(jìn)行掃描。啟用這個(gè)配置,Feign會(huì)掃描我們指定包下面聲明的Service接口,然后在我們需要用到service的地方直接@Autowired即可(不知道是不是動(dòng)態(tài)代理的方式)
這里的service接口,其實(shí)就是我們要調(diào)用的目標(biāo)服務(wù)的接口服務(wù),類似于rpc的facade,當(dāng)然也不完全一樣,只能說(shuō)類似。
定義接口服務(wù)
這里的接口服務(wù)就是我們前面說(shuō)的Service接口,就是為了給后面調(diào)用準(zhǔn)備的。
@FeignClient("user-service")
public interface UserService {
@GetMapping("/user/{id}")
public JSONObject getUser(@PathVariable("id") Long id);
}
@FeignClient("user-service")指定的是我們要調(diào)用的服務(wù),user-service是eureka注冊(cè)的服務(wù)id,也就是我們spring.application.name配置的值,如果這個(gè)值配置的不爭(zhēng)取,也是會(huì)報(bào)錯(cuò)的,具體看踩坑部分的內(nèi)容。
@GetMapping("/user/{id}")指定的是接口的地址,這里的接口地址必須與被調(diào)用方保持一致,否則調(diào)不通,下面是我的被調(diào)用方的方法實(shí)現(xiàn):

還是昨天的代碼,啥都沒(méi)變。
對(duì)比接口和被調(diào)用方實(shí)現(xiàn),我們可以看出了,接口的方法名不必和方法一致,但是參數(shù)列表必須保持一致,否則也是會(huì)報(bào)錯(cuò)的:

調(diào)用方實(shí)現(xiàn)
調(diào)用方實(shí)現(xiàn)就很簡(jiǎn)單了,就是一個(gè)簡(jiǎn)單的controller,只是這里需要通過(guò)@Autowired注入我們之前定義的聲明式調(diào)用接口,然后直接調(diào)用即可。這種方式在我看來(lái),就是很像rpc的調(diào)用
@RestController
public class ProductController {
@Autowired
private UserService userService;
@GetMapping("/feign")
public Object getUserPo() {
List<JSONObject> userList = Lists.newArrayList();
for (long i = 0; i < 10; i++) {
userList.add(userService.getUser((i + 1)));
}
return userList;
}
}
測(cè)試
下面我們啟動(dòng)5個(gè)服務(wù)提供者,啟動(dòng)一個(gè)服務(wù)消費(fèi)者。為了方便測(cè)試,我們就直接注冊(cè)到同一個(gè)Eureka注冊(cè)中心:

然后訪問(wèn)localhost:9002/feign,結(jié)果正常返回:

同時(shí)每個(gè)服務(wù)都被調(diào)用兩次,說(shuō)明我們的負(fù)載效果也達(dá)到了:

好了,測(cè)試就到這里,下面我們看下今天的踩坑記錄。
踩坑
下面是今天實(shí)踐過(guò)程中踩的一些坑,不過(guò)也正是這些坑,才讓我對(duì)feign有了更深刻的認(rèn)知。
版本不一致導(dǎo)致啟動(dòng)報(bào)錯(cuò)

這個(gè)錯(cuò)誤是由于版本問(wèn)題導(dǎo)致,下面是我之前pom依賴的版本,可以看出spring-cloud和feign的版本是不一致的
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.0.3</version>
</dependency>
解決方式也很簡(jiǎn)單,只需要把openfeign的版本改成2.2.9.RELEASE即可,即cloud組件的版本必須與spring-cloud的版本保持一致
依賴不正確導(dǎo)致服務(wù)注冊(cè)后關(guān)閉
后臺(tái)雖然沒(méi)有報(bào)錯(cuò),但是服務(wù)未成功啟動(dòng)

查詢了一些資料,發(fā)現(xiàn)這個(gè)問(wèn)題也是由于依賴不正確導(dǎo)致的,如果你的spring-boot依賴是:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
那么你需要把starter改成starter-web,然后再次啟動(dòng)即可。

然后再看下我們的注冊(cè)中心Eureka:

服務(wù)Id不正確導(dǎo)致調(diào)用服務(wù)時(shí)報(bào)錯(cuò)
這個(gè)錯(cuò)誤是調(diào)用的時(shí)候報(bào)的,查了一些資料發(fā)現(xiàn)是我的服務(wù)id寫(xiě)錯(cuò)了,我注冊(cè)的服務(wù)是user-service,但是我寫(xiě)的是user,所報(bào)錯(cuò)了,下面是錯(cuò)誤截圖:

上面這個(gè)報(bào)錯(cuò)有兩個(gè)原因,一個(gè)可能是服務(wù)名寫(xiě)錯(cuò)了;另一個(gè)就是你可能把ribbon.eureka.enabled設(shè)置成false,這個(gè)配置默認(rèn)是true。不過(guò)通過(guò)這個(gè)錯(cuò)誤,我發(fā)現(xiàn)feign應(yīng)該是基于ribbon實(shí)現(xiàn)的,或者說(shuō)它是基于ribbon實(shí)現(xiàn)的,關(guān)于這一點(diǎn),我們后期可以研究下源碼。

總結(jié)
今天的內(nèi)容雖然有點(diǎn)長(zhǎng),但是也算是把feign講的比較透徹,至少是在使用方面,我覺(jué)得應(yīng)該沒(méi)有太大問(wèn)題,所以也沒(méi)什么可總結(jié)的。
最后,我想說(shuō)兩句閑話,最近這幾天內(nèi)容更新都比較晚,一個(gè)主要原因就是早上時(shí)間不太夠;有時(shí)候洗漱完就七點(diǎn)半了,然后寫(xiě)完demo基本的就八點(diǎn)多,留給內(nèi)容梳理的時(shí)間只有差不多半小時(shí),然后剩余的內(nèi)容大都是中午午休時(shí)間完成的(差不多一個(gè)小時(shí)),不過(guò)對(duì)我而言,只要demo完成了,內(nèi)容梳理起來(lái)還是比較容易的。
另外一個(gè)影響原因就是天氣太熱了,感覺(jué)有點(diǎn)不在狀態(tài),早上也不出活。最近這兩天天氣特別熱,特別是昨天,昨天下班回家就停電了,然后吃完飯就一直在樓下溜達(dá),一直到十一點(diǎn)多都沒(méi)來(lái)電,后來(lái)實(shí)在沒(méi)辦法就回家睡了(沒(méi)有空調(diào)、沒(méi)有風(fēng)扇,熱死),雖然后來(lái)電了,但是沒(méi)一會(huì)就又?jǐn)嗔耍傊褪菙鄶嗬m(xù)續(xù)的,也不知道昨天晚上咋睡過(guò)了來(lái)的??。各位小伙伴一定要注意防暑,有機(jī)會(huì)多存點(diǎn)冷氣,畢竟夏天還是很容易停電的
- END -