Spring Cloud 學(xué)習(xí)筆記 - No.2 服務(wù)消費(fèi) Ribbon & Feign

請(qǐng)先閱讀之前的內(nèi)容:

上一步我們創(chuàng)建了服務(wù)提供者 eureka-client 來提供加法服務(wù)废睦,并且啟動(dòng)了兩個(gè)實(shí)例 Zones。

該服務(wù)對(duì)應(yīng)了兩個(gè)實(shí)例 Zones

下一步养泡,我們?nèi)ハM(fèi)服務(wù)提供者的接口嗜湃。在這里我們建立一個(gè)項(xiàng)目:

  • eureka-consumer:服務(wù)消費(fèi)者

在微服務(wù)架構(gòu)中,業(yè)務(wù)都會(huì)被拆分成一個(gè)獨(dú)立的服務(wù)澜掩,服務(wù)與服務(wù)的通訊是基于 HTTP Restful 的购披。Spring Cloud 有兩種服務(wù)調(diào)用方式,一種是 Ribbon + RestTemplate肩榕,另一種是 Feign刚陡。

eureka-consumer 服務(wù)消費(fèi)者

可以通過如下的 Spring Assistant 插件來創(chuàng)建項(xiàng)目,添加 Eureka DiscoveryWeb点把,Actuator 作為依賴橘荠。

eureka-consumer 的創(chuàng)建

eureka-consumer 的創(chuàng)建

eureka-consumer 的創(chuàng)建

pom.xml 中可以看出,導(dǎo)入了如下的依賴:

  • 2.0.3.RELEASE 版本的 Spring Boot
  • Finchley.RELEASE 版本的 Spring Cloud
  • spring-cloud-starter-netflix-eureka-client
  • spring-boot-starter-web
  • spring-boot-starter-actuator
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

隨后在啟動(dòng)程序中通過 @EnableDiscoveryClient 來激活 Eureka 中的 DiscoveryClient 實(shí)現(xiàn):

@SpringBootApplication
@EnableDiscoveryClient
public class EurekaConsumerApplication {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(EurekaConsumerApplication.class, args);
    }
}

隨后在 application.properties:指定 Eureka 注冊(cè)中心的地址郎逃。消費(fèi)者自己的端口是 3001。

spring.application.name=eureka-consumer
server.port=3001
eureka.client.serviceUrl.defaultZone=http://localhost:1234/eureka/

隨后挺份,我們創(chuàng)建一個(gè) ConsumerController 類來消費(fèi)服務(wù):

@RestController
public class ConsumerController {

    private final static Logger logger = LoggerFactory.getLogger(ConsumerController.class);

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @Autowired
    DiscoveryClient discoveryClient;

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/consumer")
    public Integer consumer() {

        // 獲取到的所有服務(wù)清單
        String services = "Services: " + discoveryClient.getServices();
        logger.info(services);

        // 調(diào)用加法服務(wù)
        ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client");
        String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/add";

        UriComponentsBuilder builder = UriComponentsBuilder
                .fromUriString(url)
                // Add query parameter
                .queryParam("operand1", 1)
                .queryParam("operand2", 2);

        logger.info(builder.toUriString());

        return restTemplate.getForObject(builder.toUriString(), Integer.class);
    }
}

org.springframework.cloud.client.discovery.DiscoveryClient 接口是 Spring Cloud 對(duì)服務(wù)治理做的一層抽象褒翰,所以可以屏蔽 Eureka 和 Consul 服務(wù)治理的實(shí)現(xiàn)細(xì)節(jié),我們的程序不需要做任何改變匀泊,只需要引入不同的服務(wù)治理依賴优训,并配置相關(guān)的配置屬性就能輕松的將微服務(wù)納入 Spring Cloud 的各個(gè)服務(wù)治理框架中。

可以看到這里各聘,我們注入了 LoadBalancerClientRestTemplate 揣非,先通過 loadBalancerClientchoose 函數(shù)來 負(fù)載均衡 的選出一個(gè) eureka-client 的服務(wù)實(shí)例,這個(gè)服務(wù)實(shí)例的基本信息存儲(chǔ)在 ServiceInstance 中躲因,然后通過這些對(duì)象中的信息拼接出訪問 /add 接口的詳細(xì)地址早敬,最后再利用 RestTemplate 對(duì)象實(shí)現(xiàn)對(duì)服務(wù)提供者接口的調(diào)用。

最后通過 mvn spring-boot:run 命令啟動(dòng)項(xiàng)目大脉,啟動(dòng)完成后搞监,可以通過 http://127.0.0.1:3001/consumer 消費(fèi)服務(wù)。

消費(fèi)服務(wù)

刷新頁面多次镰矿,從日志中可以看出琐驴,依次調(diào)用 2001 和 2002 兩個(gè)端口對(duì)應(yīng)的服務(wù)提供者,這是通過 LoadBalancerClient 實(shí)現(xiàn)的負(fù)載均衡。

通過 LoadBalancerClient 實(shí)現(xiàn)的負(fù)載均衡

通過 Ribbon 來消費(fèi)服務(wù)

Spring Cloud Ribbon 是基于 Netflix Ribbon 實(shí)現(xiàn)的一套客戶端負(fù)載均衡的工具绝淡。它是一個(gè)基于 HTTP 和 TCP 的客戶端負(fù)載均衡器宙刘。它可以通過在客戶端中配置 ribbonServerList 來設(shè)置服務(wù)端列表去輪詢?cè)L問以達(dá)到均衡負(fù)載的作用

  • 當(dāng) Ribbon 與 Eureka 聯(lián)合使用時(shí)牢酵,ribbonServerList 會(huì)被 DiscoveryEnabledNIWSServerList 重寫悬包,擴(kuò)展成從 Eureka 服務(wù)注冊(cè)中心中獲取服務(wù)實(shí)例列表。同時(shí)它也會(huì)用 NIWSDiscoveryPing 來取代 IPing茁帽,它將職責(zé)委托給 Eureka 來確定服務(wù)端是否已經(jīng)啟動(dòng)玉罐。
  • 而當(dāng) Ribbon 與 Consul 聯(lián)合使用時(shí),ribbonServerList 會(huì)被 ConsulServerList 來擴(kuò)展成從 Consul 獲取服務(wù)實(shí)例列表潘拨。同時(shí)由 ConsulPing 來作為 IPing 接口的實(shí)現(xiàn)吊输。

首先在 pom.xml 中添加 Ribbon 依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

隨后在啟動(dòng)程序中通過 @LoadBalanced 來注解 RestTemplate

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

由于 RestTemplate@LoadBalanced 修飾,所以它具備客戶端負(fù)載均衡的能力铁追,當(dāng)請(qǐng)求真正發(fā)起的時(shí)候季蚂,URL 中的服務(wù)名會(huì)根據(jù)負(fù)載均衡策略從服務(wù)清單中挑選出一個(gè)實(shí)例來進(jìn)行訪問。

隨后琅束,我們修改 ConsumerController 類來消費(fèi)服務(wù):

@GetMapping("/consumer")
public Integer consumer() {

    // 獲取到的所有服務(wù)清單
    String services = "Services: " + discoveryClient.getServices();
    logger.info(services);

    // 調(diào)用加法服務(wù)
    String url = "http://eureka-client/add";

    UriComponentsBuilder builder = UriComponentsBuilder
            .fromUriString(url)
            // Add query parameter
            .queryParam("operand1", 1)
            .queryParam("operand2", 2);

    logger.info(builder.toUriString());

    return restTemplate.getForObject(builder.toUriString(), Integer.class);
}

這里請(qǐng)求的 host 位置并沒有使用一個(gè)具體的 IP 地址和端口的形式扭屁,而是采用了服務(wù)名的方式組成。
Spring Cloud Ribbon 有一個(gè)攔截器涩禀,它能夠在這里進(jìn)行實(shí)際調(diào)用的時(shí)候料滥,自動(dòng)的去選取服務(wù)實(shí)例,并將實(shí)際要請(qǐng)求的 IP 地址和端口替換這里的服務(wù)名艾船,從而完成服務(wù)接口的調(diào)用葵腹。

通過 Feign 來消費(fèi)服務(wù)

Spring Cloud Feign 是一套基于 Netflix Feign 實(shí)現(xiàn)的聲明式服務(wù)調(diào)用客戶端。它使得編寫 Web 服務(wù)客戶端變得更加簡(jiǎn)單屿岂。
我們只需要通過創(chuàng)建接口并用注解來配置它既可完成對(duì) Web 服務(wù)接口的綁定践宴。它具備可插拔的注解支持,包括 Feign 注解爷怀、JAX-RS 注解阻肩。它也支持可插拔的編碼器和解碼器。
Spring Cloud Feign 還擴(kuò)展了對(duì) Spring MVC 注解的支持运授,同時(shí)還整合了 Ribbon 和 Eureka 來提供均衡負(fù)載的 HTTP 客戶端實(shí)現(xiàn)烤惊。

首先在 pom.xml 中添加 Ribbon 依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

隨后在啟動(dòng)程序中通過 @EnableFeignClients 注解開啟掃描 Spring Cloud Feign 客戶端的功能。

創(chuàng)建一個(gè) Feign 的客戶端接口定義徒坡。使用 @FeignClient 注解來指定這個(gè)接口所要調(diào)用的服務(wù)名稱撕氧,接口中定義的各個(gè)函數(shù)使用 Spring MVC 的注解就可以來綁定服務(wù)提供方的 REST 接口:

@FeignClient("eureka-client")
public interface CalculatorClient {
    @GetMapping("/add")
    Integer add(@RequestParam Integer operand1, @RequestParam Integer operand2);
}

隨后,我們修改 ConsumerController 類來消費(fèi)服務(wù):

@GetMapping("/consumer")
public Integer consumer() {

    // 獲取到的所有服務(wù)清單
    String services = "Services: " + discoveryClient.getServices();
    logger.info(services);

    // 調(diào)用加法服務(wù)
    return calculatorClient.add(1, 2);
}

Ribbon的饑餓加載(eager-load)模式

引用:http://blog.didispace.com/spring-cloud-tips-ribbon-eager/

有時(shí)候會(huì)發(fā)現(xiàn)這樣一個(gè)問題:我們服務(wù)消費(fèi)方調(diào)用服務(wù)提供方接口的時(shí)候喇完,第一次請(qǐng)求經(jīng)常會(huì)超時(shí)伦泥,而之后的調(diào)用就沒有問題了剥啤。

造成第一次服務(wù)調(diào)用出現(xiàn)失敗的原因主要是 Ribbon 進(jìn)行客戶端負(fù)載均衡的 Client 并不是在服務(wù)啟動(dòng)的時(shí)候就初始化好的,而是在調(diào)用的時(shí)候才會(huì)去創(chuàng)建相應(yīng)的 Client不脯,所以第一次調(diào)用的耗時(shí)不僅僅包含發(fā)送 HTTP 請(qǐng)求的時(shí)間府怯,還包含了創(chuàng)建 RibbonClient 的時(shí)間,這樣一來如果創(chuàng)建時(shí)間速度較慢防楷,同時(shí)設(shè)置的超時(shí)時(shí)間又比較短的話牺丙,很容易就會(huì)出現(xiàn)上面所描述的顯現(xiàn)。
而 Feign 的實(shí)現(xiàn)基于 Ribbon复局,所以它也有一樣的問題冲簿。

我們可以通過下面的配置來開啟 Ribbon 的饑餓加載模式,這樣 RibbonClient 會(huì)提前創(chuàng)建亿昏,而不是在第一次調(diào)用的時(shí)候創(chuàng)建峦剔。

ribbon.eager-load.enabled=true
ribbon.eager-load.clients=eureka-client

引用:
程序猿DD Spring Cloud基礎(chǔ)教程
Spring Cloud構(gòu)建微服務(wù)架構(gòu):服務(wù)消費(fèi)(基礎(chǔ))【Dalston版】
Spring Cloud構(gòu)建微服務(wù)架構(gòu):服務(wù)消費(fèi)(Ribbon)【Dalston版】
Spring Cloud構(gòu)建微服務(wù)架構(gòu):服務(wù)消費(fèi)(Feign)【Dalston版】】
Spring Cloud Dalston中文文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市角钩,隨后出現(xiàn)的幾起案子吝沫,更是在濱河造成了極大的恐慌,老刑警劉巖递礼,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惨险,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡脊髓,警方通過查閱死者的電腦和手機(jī)辫愉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來将硝,“玉大人一屋,你說我怎么就攤上這事〈撸” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵闸衫,是天一觀的道長(zhǎng)涛贯。 經(jīng)常有香客問我,道長(zhǎng)蔚出,這世上最難降的妖魔是什么弟翘? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮骄酗,結(jié)果婚禮上稀余,老公的妹妹穿的比我還像新娘。我一直安慰自己趋翻,他們只是感情好睛琳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般师骗。 火紅的嫁衣襯著肌膚如雪历等。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天辟癌,我揣著相機(jī)與錄音寒屯,去河邊找鬼。 笑死黍少,一個(gè)胖子當(dāng)著我的面吹牛寡夹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厂置,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼菩掏,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了农渊?” 一聲冷哼從身側(cè)響起患蹂,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砸紊,沒想到半個(gè)月后传于,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡醉顽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年沼溜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片游添。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡系草,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唆涝,到底是詐尸還是另有隱情找都,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布廊酣,位于F島的核電站能耻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏亡驰。R本人自食惡果不足惜晓猛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凡辱。 院中可真熱鬧戒职,春花似錦、人聲如沸透乾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蚓曼,卻和暖如春亲澡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纫版。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工床绪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人其弊。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓癞己,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親梭伐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痹雅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容