備忘springboot

0.學習目標

  • 瞭解系統架構的演變
  • 瞭解RPC與Http的區別
  • 知道什麼是SpringCloud
  • 獨立搭建Eureka註冊中心
  • 獨立配置Robbin負載均

1.系統架構演變

隨着互聯網的發展,網站應用的規模不斷擴大。需求的激增,帶來的是技術上的壓力。系統架構也所以不斷的演進、升級、迭代。從單一應用,到垂直拆分,到分佈式服務,到SOA,以及如今火熱的微服務架構,還有在Google帶領下來勢洶涌的Service Mesh。咱們究竟是該乘坐微服務的船隻駛向遠方,仍是偏安一隅得過且過?php

其實生活不止眼前的苟且,還有詩和遠方。因此咱們今天就回顧歷史,看一看系統架構演變的歷程;把握如今,學習如今最火的技術架構;展望將來,爭取成爲一名優秀的Java工程師。前端

1.1.集中式架構

當網站流量很小時,只需一個應用,將全部功能都部署在一塊兒,以減小部署節點和成本。此時,用於簡化增刪改查工做量的數據訪問框架(ORM)是影響項目開發的關鍵。java

存在的問題:mysql

  • 代碼耦合,開發維護困難
  • 沒法針對不一樣模塊進行鍼對性優化
  • 沒法水平擴展
  • 單點容錯率低,併發能力差

1.2.垂直拆分

當訪問量逐漸增大,單一應用沒法知足需求,此時爲了應對更高的併發和業務需求,咱們根據業務功能對系統進行拆分:程序員

優勢:web

  • 系統拆分實現了流量分擔,解決了併發問題
  • 能夠針對不一樣模塊進行優化
  • 方便水平擴展,負載均衡,容錯率提升

缺點:算法

  • 系統間相互獨立,會有不少重複開發工做,影響開發效率

1.3.分佈式服務

當垂直應用愈來愈多,應用之間交互不可避免,將核心業務抽取出來,做爲獨立的服務,逐漸造成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提升業務複用及整合的分佈式調用是關鍵。spring

優勢:sql

  • 將基礎服務進行了抽取,系統間相互調用,提升了代碼複用和開發效率

缺點:數據庫

  • 系統間耦合度變高,調用關係錯綜複雜,難以維護

1.4.流動計算架構(SOA)

SOA :面向服務的架構

當服務愈來愈多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增長一個調度中心基於訪問壓力實時管理集羣容量,提升集羣利用率。此時,用於提升機器利用率的資源調度和治理中心(SOA)是關鍵

之前出現了什麼問題?

  • 服務愈來愈多,須要管理每一個服務的地址
  • 調用關係錯綜複雜,難以理清依賴關係
  • 服務過多,服務狀態難以管理,沒法根據服務狀況動態管理

服務治理要作什麼?

  • 服務註冊中心,實現服務自動註冊和發現,無需人爲記錄服務地址
  • 服務自動訂閱,服務列表自動推送,服務調用透明化,無需關心依賴關係
  • 動態監控服務狀態監控報告,人爲控制服務狀態

缺點:

  • 服務間會有依賴關係,一旦某個環節出錯會影響較大
  • 服務關係複雜,運維、測試部署困難,不符合DevOps思想

1.5.微服務

前面說的SOA,英文翻譯過來是面向服務。微服務,彷佛也是服務,都是對系統進行拆分。所以二者很是容易混淆,但其實卻有一些差異:

微服務的特色:

  • 單一職責:微服務中每個服務都對應惟一的業務能力,作到單一職責
  • 微:微服務的服務拆分粒度很小,例如一個用戶管理就能夠做爲一個服務。每一個服務雖小,但「五臟俱全」。
  • 面向服務:面向服務是說每一個服務都要對外暴露Rest風格服務接口API。並不關心服務的技術實現,作到與平臺和語言無關,也不限定用什麼技術實現,只要提供Rest的接口便可。
  • 自治:自治是說服務間互相獨立,互不干擾
    • 團隊獨立:每一個服務都是一個獨立的開發團隊,人數不能過多。
    • 技術獨立:由於是面向服務,提供Rest接口,使用什麼技術沒有別人干涉
    • 先後端分離:採用先後端分離開發,提供統一Rest接口,後端不用再爲PC、移動段開發不一樣接口
    • 數據庫分離:每一個服務都使用本身的數據源
    • 部署獨立,服務間雖然有調用,但要作到服務重啓不影響其它服務。有利於持續集成和持續交付。每一個服務都是獨立的組件,可複用,可替換,下降耦合,易維護

微服務結構圖:

2.服務調用方式

2.1.RPC和HTTP

不管是微服務仍是SOA,都面臨着服務間的遠程調用。那麼服務間的遠程調用方式有哪些呢?

常見的遠程調用方式有如下2種:

  • RPC:Remote Produce Call遠程過程調用,相似的還有RMI。自定義數據格式,基於原生TCP通訊,速度快,效率高。早期的webservice,如今熱門的dubbo,都是RPC的典型表明

  • Http:http實際上是一種網絡傳輸協議,基於TCP,規定了數據傳輸的格式。如今客戶端瀏覽器與服務端通訊基本都是採用Http協議,也能夠用來進行遠程服務調用。缺點是消息封裝臃腫,優點是對服務的提供和調用方沒有任何技術限定,自由靈活,更符合微服務理念。

    如今熱門的Rest風格,就能夠經過http協議來實現。

若是大家公司所有采用Java技術棧,那麼使用Dubbo做爲微服務架構是一個不錯的選擇。

相反,若是公司的技術棧多樣化,並且你更青睞Spring家族,那麼SpringCloud搭建微服務是不二之選。在咱們的項目中,咱們會選擇SpringCloud套件,所以咱們會使用Http方式來實現服務間調用。

2.2.Http客戶端工具

既然微服務選擇了Http,那麼咱們就須要考慮本身來實現對請求和響應的處理。不過開源世界已經有不少的http客戶端工具,可以幫助咱們作這些事情,例如:

  • HttpClient
  • OKHttp
  • URLConnection

接下來,不過這些不一樣的客戶端,API各不相同

2.3.Spring的RestTemplate

Spring提供了一個RestTemplate模板工具類,對基於Http的客戶端進行了封裝,而且實現了對象與json的序列化和反序列化,很是方便。RestTemplate並無限定Http的客戶端類型,而是進行了抽象,目前經常使用的3種都有支持:

  • HttpClient
  • OkHttp
  • JDK原生的URLConnection(默認的)

首先在項目中註冊一個RestTemplate對象,能夠在啓動類位置註冊:

@SpringBootApplication
public class HttpDemoApplication {

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

	@Bean
	public RestTemplate restTemplate() {
   
		return new RestTemplate();
	}
}
複製代碼

在測試類中直接@Autowired注入:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = HttpDemoApplication.class)
public class HttpDemoApplicationTests {

	@Autowired
	private RestTemplate restTemplate;

	@Test
	public void httpGet() {
        // 調用springboot案例中的rest接口
		User user = this.restTemplate.getForObject("http://localhost/user/1", User.class);
		System.out.println(user);
	}
}
複製代碼
  • 經過RestTemplate的getForObject()方法,傳遞url地址及實體類的字節碼,RestTemplate會自動發起請求,接收響應,而且幫咱們對響應結果進行反序列化。

學習完了Http客戶端工具,接下來就能夠正式學習微服務了。

3.初識SpringCloud

微服務是一種架構方式,最終確定須要技術架構去實施。

微服務的實現方式不少,可是最火的莫過於Spring Cloud了。爲何?

  • 後臺硬:做爲Spring家族的一員,有整個Spring全家桶靠山,背景十分強大。
  • 技術強:Spring做爲Java領域的前輩,能夠說是功力深厚。有強力的技術團隊支撐,通常人還真比不了
  • 羣衆基礎好:能夠說大多數程序員的成長都伴隨着Spring框架,試問:如今有幾家公司開發不用Spring?SpringCloud與Spring的各個框架無縫整合,對你們來講一切都是熟悉的配方,熟悉的味道。
  • 使用方便:相信你們都體會到了SpringBoot給咱們開發帶來的便利,而SpringCloud徹底支持SpringBoot的開發,用不多的配置就能完成微服務框架的搭建

3.1.簡介

SpringCloud是Spring旗下的項目之一,官網地址:http://projects.spring.io/spring-cloud/

Spring最擅長的就是集成,把世界上最好的框架拿過來,集成到本身的項目中。

SpringCloud也是同樣,它將如今很是流行的一些技術整合到一塊兒,實現了諸如:配置管理,服務發現,智能路由,負載均衡,熔斷器,控制總線,集羣狀態等等功能。其主要涉及的組件包括:

  • Eureka:服務治理組件,包含服務註冊中心,服務註冊與發現機制的實現。(服務治理,服務註冊/發現)
  • Zuul:網關組件,提供智能路由,訪問過濾功能
  • Ribbon:客戶端負載均衡的服務調用組件(客戶端負載)
  • Feign:服務調用,給予Ribbon和Hystrix的聲明式服務調用組件 (聲明式服務調用)
  • Hystrix:容錯管理組件,實現斷路器模式,幫助服務依賴中出現的延遲和爲故障提供強大的容錯能力。(熔斷、斷路器,容錯)

架構圖:

以上只是其中一部分。

3.2.版本

由於Spring Cloud不一樣其餘獨立項目,它擁有不少子項目的大項目。因此它的版本是版本名+版本號 (如Angel.SR6)。

版本名:是倫敦的地鐵名

版本號:SR(Service Releases)是固定的 ,大概意思是穩定版本。後面會有一個遞增的數字。

因此 Edgware.SR3就是Edgware的第3個Release版本。

咱們在項目中,會是以Finchley的版本。

其中包含的組件,也都有各自的版本,以下表:

Component Edgware.SR3 Finchley.RC1 Finchley.BUILD-SNAPSHOT
spring-cloud-aws 1.2.2.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-bus 1.3.2.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-cli 1.4.1.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-commons 1.3.3.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-contract 1.2.4.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-config 1.4.3.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-netflix 1.4.4.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-security 1.2.2.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-cloudfoundry 1.1.1.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-consul 1.3.3.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-sleuth 1.3.3.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-stream Ditmars.SR3 Elmhurst.RELEASE Elmhurst.BUILD-SNAPSHOT
spring-cloud-zookeeper 1.2.1.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-boot 1.5.10.RELEASE 2.0.1.RELEASE 2.0.0.BUILD-SNAPSHOT
spring-cloud-task 1.2.2.RELEASE 2.0.0.RC1 2.0.0.RELEASE
spring-cloud-vault 1.1.0.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-gateway 1.0.1.RELEASE 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT
spring-cloud-openfeign 2.0.0.RC1 2.0.0.BUILD-SNAPSHOT

接下來,咱們就一一學習SpringCloud中的重要組件。

4.微服務場景模擬

首先,咱們須要模擬一個服務調用的場景,搭建兩個工程:itcast-service-provider(服務提供方)和itcast-service-consumer(服務調用方)。方便後面學習微服務架構

服務提供方:使用mybatis操做數據庫,實現對數據的增刪改查;並對外提供rest接口服務。

服務消費方:使用restTemplate遠程調用服務提供方的rest接口服務,獲取數據。

4.1.服務提供者

咱們新建一個項目:itcast-service-provider,對外提供根據id查詢用戶的服務。

4.1.1.Spring腳手架建立工程

藉助於Spring提供的快速搭建工具:

next-->填寫項目信息:

next --> 添加web依賴:

添加mybatis依賴:

Next --> 填寫項目位置:

生成的項目結構,已經包含了引導類(itcastServiceProviderApplication):

依賴也已經所有自動引入:

<?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.itcast.service</groupId>
    <artifactId>itcast-service-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-provider</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.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>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 須要手動引入通用mapper的啓動器,spring沒有收錄該依賴 -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>
    </dependencies>

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

</project>
複製代碼

固然,由於要使用通用mapper,因此咱們須要手動加一條依賴

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>
複製代碼

很是快捷啊!

4.1.2.編寫代碼

4.1.2.1.配置

屬性文件,這裏咱們採用了yaml語法,而不是properties:

server:
 port: 8081
spring:
 datasource:
 url: jdbc:mysql://localhost:3306/mybatis #你學習mybatis時,使用的數據庫地址
 username: root
 password: root
mybatis:
 type-aliases-package: cn.itcast.service.pojo
複製代碼

4.1.2.2.實體類

@Table(name = "tb_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // 用戶名
    private String userName;

    // 密碼
    private String password;

    // 姓名
    private String name;

    // 年齡
    private Integer age;

    // 性別,1男性,2女性
    private Integer sex;

    // 出生日期
    private Date birthday;

    // 建立時間
    private Date created;

    // 更新時間
    private Date updated;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }
}
複製代碼

4.1.2.3.UserMapper

@Mapper
public interface UserMapper extends tk.mybatis.mapper.common.Mapper<User>{
}
複製代碼

4.1.2.4.UserService

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User queryById(Long id) {
        return this.userMapper.selectByPrimaryKey(id);
    }
}
複製代碼

4.1.2.5.UserController

添加一個對外查詢的接口:

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id) {
        return this.userService.queryById(id);
    }
}
複製代碼

4.1.3.啓動並測試

啓動項目,訪問接口:http://localhost:8081/user/1

4.2.服務調用者

搭建itcast-service-consumer服務消費方工程。

4.2.1.建立工程

與上面相似,這裏再也不贅述,須要注意的是,咱們調用itcast-service-provider的解耦獲取數據,所以不須要mybatis相關依賴了。

pom:

<?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.itcast.service</groupId>
    <artifactId>itcast-service-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.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>
    </properties>

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

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

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


</project>

複製代碼

4.2.2.編寫代碼

首先在引導類中註冊RestTemplate

@SpringBootApplication
public class ItcastServiceConsumerApplication {

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

    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}
複製代碼

編寫配置(application.yml):

server:
  port: 80
複製代碼

編寫UserController:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        User user = this.restTemplate.getForObject("http://localhost:8081/user/" + id, User.class);
        return user;
    }

}
複製代碼

pojo對象(User):

public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    // 用戶名
    private String userName;

    // 密碼
    private String password;

    // 姓名
    private String name;

    // 年齡
    private Integer age;

    // 性別,1男性,2女性
    private Integer sex;

    // 出生日期
    private Date birthday;

    // 建立時間
    private Date created;

    // 更新時間
    private Date updated;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }
}
複製代碼

4.2.3.啓動測試

由於咱們沒有配置端口,那麼默認就是8080,咱們訪問:http://localhost/consumer/user?id=1

一個簡單的遠程服務調用案例就實現了。

4.3.有沒有問題?

簡單回顧一下,剛纔咱們寫了什麼:

  • itcast-service-provider:一個提供根據id查詢用戶的微服務。
  • itcast-service-consumer:一個服務調用者,經過RestTemplate遠程調用itcast-service-provider。

存在什麼問題?

  • 在consumer中,咱們把url地址硬編碼到了代碼中,不方便後期維護
  • consumer須要記憶provider的地址,若是出現變動,可能得不到通知,地址將失效
  • consumer不清楚provider的狀態,服務宕機也不知道
  • provider只有1臺服務,不具有高可用性
  • 即使provider造成集羣,consumer還需本身實現負載均衡

其實上面說的問題,歸納一下就是分佈式服務必然要面臨的問題:

  • 服務管理
    • 如何自動註冊和發現
    • 如何實現狀態監管
    • 如何實現動態路由
  • 服務如何實現負載均衡
  • 服務如何解決容災問題
  • 服務如何實現統一配置

以上的問題,咱們都將在SpringCloud中獲得答案。

5.Eureka註冊中心

5.1.認識Eureka

首先咱們來解決第一問題,服務的管理。

問題分析

在剛纔的案例中,itcast-service-provider對外提供服務,須要對外暴露本身的地址。而consumer(調用者)須要記錄服務提供者的地址。未來地址出現變動,還須要及時更新。這在服務較少的時候並不以爲有什麼,可是在如今日益複雜的互聯網環境,一個項目確定會拆分出十幾,甚至數十個微服務。此時若是還人爲管理地址,不只開發困難,未來測試、發佈上線都會很是麻煩,這與DevOps的思想是背道而馳的。

網約車

這就比如是 網約車出現之前,人們出門叫車只能叫出租車。一些私家車想作出租卻沒有資格,被稱爲黑車。而不少人想要約車,可是無奈出租車太少,不方便。私家車不少卻不敢攔,並且滿大街的車,誰知道哪一個纔是願意載人的。一個想要,一個願意給,就是缺乏引子,缺少管理啊。

此時滴滴這樣的網約車平臺出現了,全部想載客的私家車所有到滴滴注冊,記錄你的車型(服務類型),身份信息(聯繫方式)。這樣提供服務的私家車,在滴滴那裏都能找到,一目瞭然。

此時要叫車的人,只須要打開APP,輸入你的目的地,選擇車型(服務類型),滴滴自動安排一個符合需求的車到你面前,爲你服務,完美!

Eureka作什麼?

Eureka就比如是滴滴,負責管理、記錄服務提供者的信息。服務調用者無需本身尋找服務,而是把本身的需求告訴Eureka,而後Eureka會把符合你需求的服務告訴你。

同時,服務提供方與Eureka之間經過「心跳」機制進行監控,當某個服務提供方出現問題,Eureka天然會把它從服務列表中剔除。

這就實現了服務的自動註冊、發現、狀態監控。

5.2.原理圖

基本架構:

  • Eureka:就是服務註冊中心(能夠是一個集羣),對外暴露本身的地址
  • 提供者:啓動後向Eureka註冊本身信息(地址,提供什麼服務)
  • 消費者:向Eureka訂閱服務,Eureka會將對應服務的全部提供者地址列表發送給消費者,而且按期更新
  • 心跳(續約):提供者按期經過http方式向Eureka刷新本身的狀態

5.3.入門案例

5.3.1.搭建EurekaServer

接下來咱們建立一個項目,啓動一個EurekaServer:

依然使用spring提供的快速搭建工具:

選擇依賴:EurekaServer-服務註冊中心依賴,Eureka Discovery-服務提供方和服務消費方。由於,對於eureka來講:服務提供方和服務消費方都屬於客戶端

完整的Pom文件:

<?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.itcast.eureka</groupId>
    <artifactId>itcast-eureka</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-eureka</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.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.RC2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

    </dependencies>

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

</project>
複製代碼

編寫application.yml配置:

server:
 port: 10086 # 端口
spring:
 application:
 name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
 client:
 service-url: # EurekaServer的地址,如今是本身的地址,若是是集羣,須要加上其它Server的地址。
 defaultZone: http://127.0.0.1:${server.port}/eureka
複製代碼

修改引導類,在類上添加@EnableEurekaServer註解:

@SpringBootApplication
@EnableEurekaServer // 聲明當前springboot應用是一個eureka服務中心
public class ItcastEurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(ItcastEurekaApplication.class, args);
    }
}
複製代碼

啓動服務,並訪問:http://127.0.0.1:10086

5.3.2.註冊到Eureka

註冊服務,就是在服務上添加Eureka的客戶端依賴,客戶端代碼會自動把服務註冊到EurekaServer中。

修改itcast-service-provider工程

  1. 在pom.xml中,添加springcloud的相關依賴。
  2. 在application.yml中,添加springcloud的相關依賴。
  3. 在引導類上添加註解,把服務注入到eureka註冊中心。

具體操做

5.3.2.1.pom.xml

參照itcast-eureka,先添加SpringCloud依賴:

<!-- SpringCloud的依賴 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
複製代碼

而後是Eureka客戶端:

<!-- Eureka客戶端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
複製代碼

完整pom.xml:

<?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.itcast.service</groupId>
    <artifactId>itcast-service-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-provider</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.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>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

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

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

</project>
複製代碼

5.3.2.2.application.yml

server:
 port: 8081
spring:
 datasource:
 url: jdbc:mysql://localhost:3306/heima
 username: root
 password: root
 driverClassName: com.mysql.jdbc.Driver
 application:
 name: service-provider # 應用名稱,註冊到eureka後的服務名稱
mybatis:
 type-aliases-package: cn.itcast.service.pojo
eureka:
 client:
 service-url: # EurekaServer地址
 defaultZone: http://127.0.0.1:10086/eureka
複製代碼

注意:

  • 這裏咱們添加了spring.application.name屬性來指定應用名稱,未來會做爲應用的id使用。

5.3.2.3.引導類

在引導類上開啓Eureka客戶端功能

經過添加@EnableDiscoveryClient來開啓Eureka客戶端功能

@SpringBootApplication
@EnableDiscoveryClient
public class ItcastServiceProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceApplication.class, args);
    }
}
複製代碼

重啓項目,訪問Eureka監控頁面查看

咱們發現service-provider服務已經註冊成功了

5.3.3.從Eureka獲取服務

接下來咱們修改itcast-service-consumer,嘗試從EurekaServer獲取服務。

方法與消費者相似,只須要在項目中添加EurekaClient依賴,就能夠經過服務名稱來獲取信息了!

  1. pom.xml
<?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.itcast.service</groupId>
    <artifactId>itcast-service-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.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>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Eureka客戶端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

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

    <!-- SpringCloud的依賴 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

複製代碼
  1. 修改配置
server:
 port: 80
spring:
 application:
 name: service-consumer
eureka:
 client:
 service-url:
 defaultZone: http://localhost:10086/eureka
複製代碼
  1. 在啓動類開啓Eureka客戶端
@SpringBootApplication
@EnableDiscoveryClient // 開啓Eureka客戶端
public class ItcastServiceConsumerApplication {

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

    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}
複製代碼
  1. 修改UserController代碼,用DiscoveryClient類的方法,根據服務名稱,獲取服務實例:
@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient; // eureka客戶端,能夠獲取到eureka中服務的信息

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        // 根據服務名稱,獲取服務實例。有多是集羣,因此是service實例集合
        List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");
        // 由於只有一個Service-provider。因此獲取第一個實例
        ServiceInstance instance = instances.get(0);
        // 獲取ip和端口信息,拼接成服務地址
        String baseUrl = "http://" + instance.getHost() + ":" + instance.getPort() + "/user/" + id;
        User user = this.restTemplate.getForObject(baseUrl, User.class);
        return user;
    }

}
複製代碼

5)Debug跟蹤運行:

生成的URL:

訪問結果:

5.4.Eureka詳解

接下來咱們詳細講解Eureka的原理及配置。

5.4.1.基礎架構

Eureka架構中的三個核心角色:

  • 服務註冊中心

    Eureka的服務端應用,提供服務註冊和發現功能,就是剛剛咱們創建的itcast-eureka。

  • 服務提供者

    提供服務的應用,能夠是SpringBoot應用,也能夠是其它任意技術實現,只要對外提供的是Rest風格服務便可。本例中就是咱們實現的itcast-service-provider。

  • 服務消費者

    消費應用從註冊中心獲取服務列表,從而得知每一個服務方的信息,知道去哪裏調用服務方。本例中就是咱們實現的itcast-service-consumer。

5.4.2.高可用的Eureka Server

Eureka Server即服務的註冊中心,在剛纔的案例中,咱們只有一個EurekaServer,事實上EurekaServer也能夠是一個集羣,造成高可用的Eureka中心。

服務同步

多個Eureka Server之間也會互相註冊爲服務,當服務提供者註冊到Eureka Server集羣中的某個節點時,該節點會把服務的信息同步給集羣中的每一個節點,從而實現數據同步。所以,不管客戶端訪問到Eureka Server集羣中的任意一個節點,均可以獲取到完整的服務列表信息。

動手搭建高可用的EurekaServer

咱們假設要運行兩個EurekaServer的集羣,端口分別爲:10086和10087。只須要把itcast-eureka啓動兩次便可。

1)啓動第一個eurekaServer,咱們修改原來的EurekaServer配置:

server:
 port: 10086 # 端口
spring:
 application:
 name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
 client:
 service-url: # 配置其餘Eureka服務的地址,而不是本身,好比10087
 defaultZone: http://127.0.0.1:10087/eureka
複製代碼

所謂的高可用註冊中心,其實就是把EurekaServer本身也做爲一個服務進行註冊,這樣多個EurekaServer之間就能互相發現對方,從而造成集羣。所以咱們作了如下修改:

  • 把service-url的值改爲了另一臺EurekaServer的地址,而不是本身

啓動報錯,很正常。由於10087服務沒有啓動:

2)啓動第二個eurekaServer,再次修改itcast-eureka的配置:

server:
 port: 10087 # 端口
spring:
 application:
 name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
 client:
 service-url: # 配置其餘Eureka服務的地址,而不是本身,好比10087
 defaultZone: http://127.0.0.1:10086/eureka
複製代碼

注意:idea中一個應用不能啓動兩次,咱們須要從新配置一個啓動器:

而後啓動便可。

3)訪問集羣,測試:

4)客戶端註冊服務到集羣

由於EurekaServer不止一個,所以註冊服務的時候,service-url參數須要變化:

eureka:
 client:
 service-url: # EurekaServer地址,多個地址以','隔開
 defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
複製代碼

10086:

10087:

5.4.3.服務提供者

服務提供者要向EurekaServer註冊服務,而且完成服務續約等工做。

服務註冊

服務提供者在啓動時,會檢測配置屬性中的:eureka.client.register-with-eureka=true參數是否正確,事實上默認就是true。若是值確實爲true,則會向EurekaServer發起一個Rest請求,並攜帶本身的元數據信息,Eureka Server會把這些信息保存到一個雙層Map結構中。

  • 第一層Map的Key就是服務id,通常是配置中的spring.application.name屬性
  • 第二層Map的key是服務的實例id。通常host+ serviceId + port,例如:locahost:service-provider:8081
  • 值則是服務的實例對象,也就是說一個服務,能夠同時啓動多個不一樣實例,造成集羣。

服務續約

在註冊服務完成之後,服務提供者會維持一個心跳(定時向EurekaServer發起Rest請求),告訴EurekaServer:「我還活着」。這個咱們稱爲服務的續約(renew);

有兩個重要參數能夠修改服務續約的行爲:

eureka:
 instance:
 lease-expiration-duration-in-seconds: 90
 lease-renewal-interval-in-seconds: 30
複製代碼
  • lease-renewal-interval-in-seconds:服務續約(renew)的間隔,默認爲30秒
  • lease-expiration-duration-in-seconds:服務失效時間,默認值90秒

也就是說,默認狀況下每一個30秒服務會向註冊中心發送一次心跳,證實本身還活着。若是超過90秒沒有發送心跳,EurekaServer就會認爲該服務宕機,會從服務列表中移除,這兩個值在生產環境不要修改,默認便可。

可是在開發時,這個值有點太長了,常常咱們關掉一個服務,會發現Eureka依然認爲服務在活着。因此咱們在開發階段能夠適當調小。

eureka:
 instance:
 lease-expiration-duration-in-seconds: 10 # 10秒即過時
 lease-renewal-interval-in-seconds: 5 # 5秒一次心跳
複製代碼

5.4.4.服務消費者

獲取服務列表

當服務消費者啓動時,會檢測eureka.client.fetch-registry=true參數的值,若是爲true,則會拉取Eureka Server服務的列表只讀備份,而後緩存在本地。而且每隔30秒會從新獲取並更新數據。咱們能夠經過下面的參數來修改:

eureka:
 client:
 registry-fetch-interval-seconds: 5
複製代碼

生產環境中,咱們不須要修改這個值。

可是爲了開發環境下,可以快速獲得服務的最新狀態,咱們能夠將其設置小一點。

5.4.5.失效剔除和自我保護

服務下線

當服務進行正常關閉操做時,它會觸發一個服務下線的REST請求給Eureka Server,告訴服務註冊中心:「我要下線了」。服務中心接受到請求以後,將該服務置爲下線狀態。

失效剔除

有些時候,咱們的服務提供方並不必定會正常下線,可能由於內存溢出、網絡故障等緣由致使服務沒法正常工做。Eureka Server須要將這樣的服務剔除出服務列表。所以它會開啓一個定時任務,每隔60秒對全部失效的服務(超過90秒未響應)進行剔除。

能夠經過eureka.server.eviction-interval-timer-in-ms參數對其進行修改,單位是毫秒,生產環境不要修改。

這個會對咱們開發帶來極大的不變,你對服務重啓,隔了60秒Eureka才反應過來。開發階段能夠適當調整,好比:10秒

自我保護

咱們關停一個服務,就會在Eureka面板看到一條警告:

這是觸發了Eureka的自我保護機制。當一個服務未按時進行心跳續約時,Eureka會統計最近15分鐘心跳失敗的服務實例的比例是否超過了85%。在生產環境下,由於網絡延遲等緣由,心跳失敗實例的比例頗有可能超標,可是此時就把服務剔除列表並不穩當,由於服務可能沒有宕機。Eureka就會把當前實例的註冊信息保護起來,不予剔除。生產環境下這頗有效,保證了大多數服務依然可用。

可是這給咱們的開發帶來了麻煩, 所以開發階段咱們都會關閉自我保護模式:(itcast-eureka)

eureka:
 server:
 enable-self-preservation: false # 關閉自我保護模式(缺省爲打開)
 eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(缺省爲60*1000ms)
複製代碼

6.負載均衡Ribbon

在剛纔的案例中,咱們啓動了一個itcast-service-provider,而後經過DiscoveryClient來獲取服務實例信息,而後獲取ip和端口來訪問。

可是實際環境中,咱們每每會開啓不少個itcast-service-provider的集羣。此時咱們獲取的服務列表中就會有多個,到底該訪問哪個呢?

通常這種狀況下咱們就須要編寫負載均衡算法,在多個實例列表中進行選擇。

不過Eureka中已經幫咱們集成了負載均衡組件:Ribbon,簡單修改代碼便可使用。

什麼是Ribbon:

接下來,咱們就來使用Ribbon實現負載均衡。

6.1.啓動兩個服務實例

首先參照itcast-eureka啓動兩個ItcastServiceProviderApplication實例,一個8081,一個8082。

Eureka監控面板:

6.2.開啓負載均衡

由於Eureka中已經集成了Ribbon,因此咱們無需引入新的依賴,直接修改代碼。

修改itcast-service-consumer的引導類,在RestTemplate的配置方法上添加@LoadBalanced註解:

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}
複製代碼

修改調用方式,再也不手動獲取ip和端口,而是直接經過服務名稱調用:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    //@Autowired
    //private DiscoveryClient discoveryClient; // 注入discoveryClient,經過該客戶端獲取服務列表

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        // 經過client獲取服務提供方的服務列表,這裏咱們只有一個
        // ServiceInstance instance = discoveryClient.getInstances("service-provider").get(0);
        String baseUrl = "http://service-provider/user/" + id;
        User user = this.restTemplate.getForObject(baseUrl, User.class);
        return user;
    }

}
複製代碼

訪問頁面,查看結果:

完美!

6.3.源碼跟蹤

爲何咱們只輸入了service名稱就能夠訪問了呢?以前還要獲取ip和端口。

顯然有人幫咱們根據service名稱,獲取到了服務實例的ip和端口。它就是LoadBalancerInterceptor

在以下代碼打斷點:

一路源碼跟蹤:RestTemplate.getForObject --> RestTemplate.execute --> RestTemplate.doExecute:

點擊進入AbstractClientHttpRequest.execute --> AbstractBufferingClientHttpRequest.executeInternal --> InterceptingClientHttpRequest.executeInternal --> InterceptingClientHttpRequest.execute:

繼續跟入:LoadBalancerInterceptor.intercept方法

繼續跟入execute方法:發現獲取了8082端口的服務

再跟下一次,發現獲取的是8081:

6.4.負載均衡策略

Ribbon默認的負載均衡策略是簡單的輪詢,咱們能夠測試一下:

編寫測試類,在剛纔的源碼中咱們看到攔截中是使用RibbonLoadBalanceClient來進行負載均衡的,其中有一個choose方法,找到choose方法的接口方法,是這樣介紹的:

如今這個就是負載均衡獲取實例的方法。

咱們注入這個類的對象,而後對其測試:

測試內容:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ItcastServiceConsumerApplication.class)
public class LoadBalanceTest {

    @Autowired
    private RibbonLoadBalancerClient client;

    @Test
    public void testLoadBalance(){
        for (int i = 0; i < 100; i++) {
            ServiceInstance instance = this.client.choose("service-provider");
            System.out.println(instance.getHost() + ":" +instance.getPort());
        }
    }
}

複製代碼

結果:

符合了咱們的預期推測,確實是輪詢方式。

咱們是否能夠修改負載均衡的策略呢?

繼續跟蹤源碼,發現這麼一段代碼:

咱們看看這個rule是誰:

這裏的rule默認值是一個RoundRobinRule,看類的介紹:

這不就是輪詢的意思嘛。

咱們注意到,這個類實際上是實現了接口IRule的,查看一下:

定義負載均衡的規則接口。

它有如下實現:

SpringBoot也幫咱們提供了修改負載均衡規則的配置入口,在itcast-service-consumer的application.yml中添加以下配置:

server:
 port: 80
spring:
 application:
 name: service-consumer
eureka:
 client:
 service-url:
 defaultZone: http://127.0.0.1:10086/eureka
service-provider:
 ribbon:
 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
複製代碼

格式是:{服務名稱}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的實現類。

再次測試,發現結果變成了隨機:

相關文章
相關標籤/搜索