玩轉Spring Cloud之服務註冊發現(eureka)及負載均衡消費(ribbon、feign)

  若是說用Spring Boot+Spring MVC是開發單體應用(或單體服務)的利器,那麼Spring Boot+Spring MVC+Spring Cloud將是開發分佈式應用(快速構建微服務)的又一法寶,相信你們若是看到我近期總結的《JAVA WEB快速入門》系列文章,對Spring Boot+Spring MVC應該是比較熟悉了吧,從本文開始,一塊兒來熟悉Spring Cloud、玩轉Spring Cloud,至於什麼是Spring Cloud?我這裏就再也不介紹了,網上資源太多了,好比:大話Spring CloudSpringCloud是什麼?,固然介紹Spring Cloud系列文章也比較多(好比:http://www.javashuo.com/article/p-vpguiuwq-dw.html),你們也能夠參考,我這裏只是結合當前最新的Spring Boot、Spring MVC、Spring Cloud來從新演練一遍,把重要的知識點、遇到的一些坑分享出來,一來是爲本身作記錄(所謂「好記性不如爛筆頭」),二來能夠避免你們學習時走彎路,又由於介紹Spring Cloud文章實在太多了,故玩轉Spring Cloud系列文章更多的是以把實現的DEMO代碼一步步貼出來,一些組件名詞我就再也不詳細解釋了,而後對於涉及的重要知識點及踩坑點進行說明,以便你們能夠:知其然還能知其因此然。(注:全部示例代碼均採用IDEA IDE編寫)html

1、實現eureka server(註冊中心)java

  1.1.經過IDEA來建立一個空的spring boot項目(類型是:maven-archtype-quickstart,這樣最精簡,固然若是你使用webapp項目也是能夠,只是認爲沒有必要)。node

    建立步驟有2種,第一種是使用maven建立: maven->maven-archtype-quickstart,而後手動添加相關的spring boot依賴;第二種是使用spring initializer->填寫項目參數->選擇相關依賴(可直接選擇spring cloud相關依賴,如:eureka,這樣就一步到位,這裏所有先不選),最終的初始POM XML以下:git

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.zuowenjun.cloud</groupId>
  <artifactId>eurekaserver</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>eurekaserver</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.zuowenjun.cn</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
  </parent>



  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>


  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

如上所示(若是不是請改爲這樣,若是隻是多點依賴不要緊,固然我認爲此時只須要這麼多的依賴便可,多了也無用),咱們只是有spring boot的POM依賴,並無spring cloud的相關依賴。github

  1.2添加spring cloud相關依賴,以下所示:(添加了dependencyManagement節點,並配置spring-cloud-dependencies pom import依賴,目的是:便於依賴繼承,與parent節點功能相似,添加具體依賴時,若包含在parent中或pom import依賴中則無需版本號,可以保證組件的一致性,詳見:https://blog.csdn.net/mn960mn/article/details/50894022,相反若是沒有配置spring-cloud-dependencies pom import依賴,則添加具體依賴時須要指定version版本號,並且須要注意各依賴組件間的兼容性問題,以下面我把version註釋掉)web

  <dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Greenwich.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

  <dependencies>
    ... ...其它原有依賴
    
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter</artifactId>
      <!--<version>2.1.0.RELEASE</version>-->
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
      <!--<version>2.1.0.RELEASE</version>-->
    </dependency>

  </dependencies>

  1.3.在resouces目錄下(若沒有請建立,注意設爲souces root目錄,方法:右鍵文件夾->Mark directory as->souces root)建立application.yml(或application.properties,本文示例所有使用yml),添加以下配置:算法

server:
  port: 8800

spring:
  applcation:
    name: demo-eurekaserver

# config detail:https://www.jianshu.com/p/98f4e5f6bca7  or https://blog.csdn.net/wo18237095579/article/details/83276352
eureka:
  instance:
    hostname: eurekaserver1 #實例主機名,集羣時須要且惟一
  server:
    enable-self-preservation: true #自我保護,正式環境不要這麼作
    eviction-interval-timer-in-ms: 5000 #按期清理失效節點,默認60s
    peer-eureka-nodes-update-interval-ms: 6000 #同步更新節點頻率,默認10min
    renewal-percent-threshold: 0.49 #默認0.85
    response-cache-auto-expiration-in-seconds: 30

  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://localhost:${server.port}/eureka/

1.4.在spring boot 啓動類中添加@EnableEurekaServer便可,以下代碼:spring

package cn.zuowenjun.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class App 
{
    public static void main( String[] args )
    {
        SpringApplication.run(App.class, args);
    }
}
View Code

整個項目結構以下圖示,啓動後瀏覽地址:http://localhost:8800/,會出現spring eureka的主頁,就代表eureka server成功了。apache

2、實現service provider(含eureka  client)--服務提供者架構

【即:具體微服務項目,註冊服務信息,暴露API】,固然也有可能同時是service consumer【服務消費者】,須要遠程調用其它服務

  2.1.參照1.1方式建立一個空的spring boot項目,而後添加spring cloud 相關依賴(這裏主要是:eureka-client【實現服務自動發現與註冊】、web【即:springMVC,實現服務API】),POM XML添加配置以下:

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Greenwich.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </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-web</artifactId>
    </dependency>

    <!--當.yml配置不生效時,應添加snakeyaml依賴,但通常spring-boot-starter中默認有此依賴,非spring boot項目須要添加-->
    <dependency>
      <groupId>org.yaml</groupId>
      <artifactId>snakeyaml</artifactId>
      <version>1.23</version>
    </dependency>

  </dependencies>

  2.2.在application.yml文件中添加以下配置(若沒有請參見1.3法建立):注意spring.application.name,這個是服務實例名,註冊及服務消費時均需使用該名稱

server:
  port: 8801

spring:
  application:
    name: helloservice
    ip: localhost #自定義配置,在demo代碼中有用到

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8800/eureka/

  3.3.編寫controller 服務相關代碼,在spring boot啓動類上添加@EnableDiscoveryClient註解,具體完整實現代碼以下:(除了@EnableDiscoveryClient註解,基餘代碼與普通的spring MVC項目代碼均相同)

//controller:
package cn.zuowenjun.cloud.controller;

import cn.zuowenjun.cloud.model.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @Value("${spring.application.name}")
    private String serviceName;

    @Value("${spring.application.ip}")
    private String address;

    @Value("${server.port}")
    private String port;

    @Autowired
    DiscoveryClient discoveryClient;


    @GetMapping(value = "/")
    public String index(){
        return "demo service";
    }

    @RequestMapping("/hello")
    public  Object hello(){
        return discoveryClient.getServices();
    }


    @RequestMapping("/info")
    public Result info(){
        Result result = new Result();
        result.setServiceName(serviceName);
        result.setHost(String.format("%s:%s", address, port));
        result.setMessage("hello");
        return result;
    }

    @RequestMapping(value = "/multiply/{a}/{b}")
    public Result multiply(@PathVariable("a") int a,@PathVariable("b") int b){
        Result result = new Result();
        result.setServiceName(serviceName);
        result.setHost(String.format("%s:%s", address, port));
        result.setMessage("ok");
        result.setContent(a * b);
        return result;
    }
}

//model:
package cn.zuowenjun.cloud.model;

public class Result {

    private int code;

    private String message;

    private Object content;

    private String serviceName;

    private String host;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getContent() {
        return content;
    }

    public void setContent(Object content) {
        this.content = content;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }
}

//App spring boot啓動類:

package cn.zuowenjun.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class App 
{
    public static void main( String[] args )
    {
        SpringApplication.run(App.class, args);
    }
}
View Code

  完成上述步驟後即實現了服務提供者項目,完整項目結構以下圖示,啓動運行http://localhost:8801/multiply/324/561(只需關注這個服務方法,後面服務消費會調用這個方法) ,能夠看到正常響應出JSON結果,如:"code":0,"message":"ok","content":181764,"serviceName":"helloservice","host":"localhost:8801"}

爲了後面服務消費者能體驗出負載均衡的效果,能夠把該項目再以另外一個端口(server.port=8802)從新啓動運行一個實例(IDEA啓動多個實例的方法請參見:https://blog.csdn.net/forezp/article/details/76408139,最後不必定要改yml中的port配置,也能夠直接在Edit Configuration--> program argements中指定:--server.port=8802便可,原理與直接經過命令:java -jar xxx --server.port=8802相似),這樣就會有兩個服務提供者了,若是查看eureka server主頁(http://localhost:8800/)會在Instances currently registered with Eureka列表中展現出2個服務實例信息,以下圖示:

 

3、實現service consumer(含eureka  client)--服務消費者

【即:須要調用微服務API的項目,相對eureka,service provider來說,就是客戶端,消費方】,固然也有多是service provider【服務提供者】,暴露服務API給其它微服務項目

  3.0.參照1.1方式建立一個空的spring boot項目,而後添加spring cloud 相關依賴(這裏僅先是:eureka-client【實現服務自動發現與註冊】、web【即:springMVC,實現服務API】),POM XML添加配置以下:

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <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>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
View Code

  3.1方式一:使用restTemplate+ribbon實現服務消費(負載均衡調用遠程服務)

    3.1.1.在POM XML中添加spring-cloud-starter-netflix-ribbon依賴,以下:

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

    3.1.2.編寫controller相關代碼(含遠程服務調用類HelloService),修改spring boot 啓動類,具體完整實現代碼以下:

//spring boot啓動類:
package cn.zuowenjun.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
class EurekaclientconsumerApplication {

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

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

}

//controller:
package cn.zuowenjun.cloud.controller;

import cn.zuowenjun.cloud.service.HelloRemoteService;
import cn.zuowenjun.cloud.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired
    private  HelloService helloService;


    @RequestMapping("/x")
    public Object multiplyForRestTemplate(@RequestParam int a, @RequestParam int b) {
       return   helloService.multiply(a,b);
    }

}


//HelloService(遠程服務代理類) :
package cn.zuowenjun.cloud.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class HelloService {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${spring.application.helloServiceProvider}")
    private String  helloServiceName;

    public Object multiply(int a,int b){
        String url="http://"+ helloServiceName +"/multiply/" + a +"/" + b;
        return restTemplate.getForObject(url,String.class);
    }
}
View Code

 如上代碼中最核心的是:HelloService類,經過這個類遠程調用【消費】註冊在eureka server上對應的服務API,而這個類中最核心的對象是:RestTemplate,而這個又是經過在spring boot啓動類(EurekaclientconsumerApplication)中經過代碼注入到Spring IOC容器中的(固然也能夠自定義一個config類而後統一寫BEAN注入的方法),重點請看這個restTemplate Bean註冊方法上面的註解:@LoadBalanced,這個就是實現負載均衡(默認是採用輪詢的負載均衡算法,還有其它的負載均衡Rule),就這麼簡單嗎?是的,用起來簡單,但內部實現仍是很是複雜的,Ribbon的運行原理詳見:深刻理解Ribbon之源碼解析,核心思路是:RestTemplate內部維護了一個被@LoadBalance註解的RestTemplate列表,而這些RestTemplate列表又被添加了LoadBalancerInterceptor攔截器,而LoadBalancerInterceptor內部又使用了LoadBalancerClient,而LoadBalancerClient(實現類:RibbonLoadBalancerClient)具體選擇服務實例的邏輯又由ILoadBalancer來處理,ILoadBalancer經過配置IRule、IPing等信息,向EurekaClient獲取註冊列表的信息,並定時向EurekaClient發送「ping」心跳,進而檢查是否更新了服務列表,最後獲得註冊服務實例列表後,ILoadBalancer根據IRule的策略進行負載均衡。

  3.1.3.在application.yml文件中添加以下配置(若沒有請參見1.3法建立):

server:
  port: 8666

spring:
  application:
    name: ribbonclient
    helloServiceProvider: helloservice #自定義配置,指定訪問遠程服務名稱,固然也能夠寫死在代碼中

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8800/eureka/ #指向eureka server

完成上述步驟即實現了一個基於Ribbon的負載均衡服務消費者(客戶端)項目。

  3.2方式二:使用feign實現服務消費(負載均衡調用遠程服務調用)

  咱們仍然基於3.1節原有項目基礎上實現基於feign的負載均衡服務調用,注意feign的底層仍然使用了Ribbon。固然也能夠單首創一個新的spring boot項目(參照第一節介紹)而後再按下文步驟操做便可。

  3.2.1.在POM XML中添加spring-cloud-starter-openfeign依賴,配置以下:

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

  3.2.2.在spring boot啓動類(EurekaclientconsumerApplication)上添加:@EnableFeignClients 註解,而後在cn.zuowenjun.cloud.service包中添加自定義HelloRemoteService,這個就是遠程服務調用接口類(或稱:客戶端代理類【接口】),這個就是與3.1中定義的HelloService做用徹底相似,只是實現方式不一樣而矣,最後在controller中添加一個新的API ACTION方法,以即可以調用HelloRemoteService中的服務方法,完整實現代碼以下:

//spring boot啓動類
package cn.zuowenjun.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients(basePackages = "cn.zuowenjun.cloud.service") // 若是啓動類不在根目錄須要指定basePackages,不然不須要
class EurekaclientconsumerApplication {

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

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

}

//HelloRemoteService:

package cn.zuowenjun.cloud.service;


import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/*
*  bug-refer https://blog.csdn.net/zlh313_01/article/details/80309144
*  bug-refer https://blog.csdn.net/alinyua/article/details/80070890
 */
@FeignClient(name= "helloservice")
public interface HelloRemoteService {

    @RequestMapping("/multiply/{a}/{b}")
    Object  multiply(@PathVariable("a") int a, @PathVariable("b") int b);

}

//controller:

package cn.zuowenjun.cloud.controller;

import cn.zuowenjun.cloud.service.HelloRemoteService;
import cn.zuowenjun.cloud.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired
    private  HelloService helloService;

    @Autowired
    private HelloRemoteService helloRemoteService;

    @RequestMapping("/x")
    public Object multiplyForRestTemplate(@RequestParam int a, @RequestParam int b) {
       return   helloService.multiply(a,b);
    }

    @RequestMapping("/multiply/{a}/{b}")
    public Object multiplyForFeignClient(@PathVariable int a, @PathVariable int b) {
        return helloRemoteService.multiply(a,b);
    }
}
View Code

 如上代碼HelloRemoteService是重點,須要注意:

a.必需是interface,由於@FeignClient註解只能用於interface中,並且很顯然HelloRemoteService 是遠程調用,本地不該有實現的,若是知道原理就更明白這個接口只是爲了生成可供restTemplate調用的URL方法而矣;

b.@FeignClient註解的name(別名屬性)或value必填,這個就是須要遠程調用服務的應用名稱【即:代表消費哪一個服務】

c.接口中定義的方法應與遠程服務的controller中的方法保持一致(方法簽名,註解),同時注意方法上的一些映射請求的註解,如:@RequestMapping,這些與咱們在spring MVC用法相同,但含義卻不相同,spring MVC是指處理請求路徑,而這裏是調用請求路徑,這個路徑必需與服務提供者API 的對應的ACITON方法上的保持相同,不然將沒法成功發送請求。常見問題及解決辦法可參見:https://blog.csdn.net/zlh313_01/article/details/80309144

3.2.3.application.yml配置與3.1.3配置相同,即保持不變便可,最後啓動項目便可(如今這個項目同時包含了Ribbon與Feign的負載均衡遠程調用服務的方式),經過屢次訪問:http://localhost:8666/x?a=數字&b=數字 (基於Ribbon實現)、http://localhost:8666/multiply/數字/數字(基於Feign實現)能夠看到遠程調用服務成功(即:消費服務成功)。

FeignClient的運行原理詳見:深刻理解Feign之源碼解析,核心思路是:spring boot項目啓動時檢查@EnableFeignClients,如有則掃描被@FeignClient註解接口並注入到spring IOC容器中,而後在請求被@ FeignCleint標註的接口方法時,會經過JDK動態代理來生成具體的RequesTemplate,RequesTemplate又會生成Request,Request交給Client去處理,最後Client被封裝到LoadBalanceClient類,這個類Ribbon中的LoadBalancerClient相同,後面的負載均衡的處理請求相同。

項目結構及遠程調用效果以下圖所示:

  

4、下面分享相關可參考的博文資料連接:

Spring Cloud之Eureka服務註冊與發現(概念原理篇)

微服務架構:Eureka參數配置項詳解(轉載)

Spring Cloud Netflix - Eureka Server源碼閱讀

Eureka 參數調優

 

提示:本文相關示例項目代碼已上傳GITHUB,地址以下:

https://github.com/zuowj/learning-demos/tree/master/java/demo-eurekaserver

https://github.com/zuowj/learning-demos/tree/master/java/demo-eurekaclient

https://github.com/zuowj/learning-demos/tree/master/java/demo-eurekaclientconsumer

說明:文中如有不足之處歡迎指出,碼字不易,請多支持,謝謝!

相關文章
相關標籤/搜索