Spring Cloud 負載均衡初體驗

[toc]html

本文首發於個人我的博客,Spring Cloud 負載均衡初體驗 ,歡迎訪問!java

使用 Spring Cloud Netflix 組件 Eureka 和 Ribbon 構建單註冊中心的負載均衡服務。git

Spring Cloud 是基於 Spring 的微服務技術棧,能夠這麼歸納吧,裏面包含了不少例如服務發現註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等組件,能夠經過 Spring Boot 的形式進行集成和使用。github

目前,項目中有這麼個需求,Spring Boot 作一個 web 服務,而後調用 TensorFlow 模型獲得結果。可是因爲 TensorFlow GPU 版,不支持多線程多引擎,因此只能採用多進程的方式去進行調度,因此須要作一個負載均衡。負載均衡的話,能夠分爲客戶端和服務端負載均衡。我目前還沒能領悟到有什麼不一樣,畢竟總體的架構都是同樣的,以下如圖。其中客戶端均衡負載的表明是 Spring Cloud 的 Ribbon,服務端負載均衡表明是 Nginx。web

因爲項目的壓力並不大,日平均請求約 5000 左右,所以就採用 Spring Cloud 中的組件進行客戶端負載均衡。主要用到的就是 Spring Cloud 和 Eureka。不少博客中會也看到 Ribbon 的身影。其實他們都是 Spring Cloud Netflix 中的組件,用來構建微服務。本文所講的例子,也能夠看做是一個微服務,把原來一個的算法服務拆成了若干個小服務。以前講到的 Spring Cloud 的各類組件也都是爲了使得這些獨立的服務可以更好的管理和協做。算法

回到負載均衡,一個使用 Spring Boot 搭建的客戶端負載均衡服務,其實只須要 Rureka 這一個組件就夠了。spring

Eureka 是 Spring Cloud Netflix 當中的一個重要的組件,用於服務的註冊和發現。Eureka 採用了 C-S 的設計架構。具體以下圖,Eureka Server 做爲一個註冊中心,擔任服務中臺的角色,餘下的其餘服務都是 Eureka 的 Client。全部的服務都須要註冊到 Eureka Server 當中去,由它進行統一管理和發現。Eureka Server 做爲管理中心,天然,除了註冊和發現服務外,還有監控等其餘輔助管理的功能。springboot

具體從負載均衡的角度來說:多線程

  1. Eureka Server——提供服務註冊和發現
  2. Service Provider——服務提供方,通常有多個服務參與調度
  3. Service Consumer——服務消費方,從 Eureka 獲取註冊服務列表,從而可以消費服務,也就是請求的直接入口。

服務搭建

下面主要實戰一下負載均衡服務搭建。架構

正如上面所說,一套完整的負載均衡服務,至少須要三個服務。

1.註冊中心——Eureka Server

直接經過 IDEA 建立一個包含 Eureka Server 的 Spring Boot 項目,直接引入所需的 dependency。主要是 spring-cloud-dependenciesspring-cloud-starter-netflix-eureka-server

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</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>

在啓動類上添加註解 @EnableEurekaServer

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

在 yml 中配置 eureka。

server:
  port: 8080
eureka:.
    instance:
    prefer-ip-address: true
  client:
    # 表示是否將本身註冊到 Eureka Server,默認爲 true
    register-with-eureka: false
    # 表示是否從 Eureka Server 獲取註冊信息,默認爲 true
    fetch-registry: false
    service-url:
      # 默認註冊的域,其餘服務都往這個 url 上註冊
      defaultZone: http://localhost:${server.port}/eureka/

因爲目前配置的是單節點的註冊中心,所以 register-with-eurekafetch-registry 都設爲 false,不須要把本身註冊到服務中心,不須要獲取註冊信息。

啓動工程後,訪問:http://localhost:8080/,就能看到一個圖形化的管理界面,目前沒有註冊任何服務。

2.服務提供方——Service Provider

在 yml 中配置 Eureka。

eureka:
  instance:
    prefer-ip-address: true    # 以 IP 的形式註冊
    # 默認是 hostname 開頭的,修改爲 ip 開頭
    instance-id: \${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/    # 註冊到以前的服務中心
server:
  port: 8084
spring:
  application:
    name: services-provider    # 應用名稱

默認,Eureka 是經過 hostname 來註冊到 Eureka Server 上的,因爲後面可能涉及到多節點的配置,hostname 可能不如 ip 方便管理,因此將 prefer-ip-address 設爲 true,經過 ip 註冊,並修改 instance-id 格式爲:${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}。注意 ip-address 爲短線鏈接。

而後寫一個簡單的 web 服務作測試。

@RestController
public class ServicesController {
    @RequestMapping("/")
    public String home() {
        return "Hello world";
    }
}

而後運行工程。

3.服務消費方——Service Consumer

新建項目同 2,而後配置 Eureka:

eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/
server:
  port: 8085
spring:
  application:
    name: services-consumer

和以前同樣,注意區分端口號和 name 就能夠了。

再在啓動類,配置 RestTemplate 用於負載均衡的服務分發。這是服務消費的一種基本方式,下面還會介紹第二種 Feign。

@SpringBootApplication
public class ServicesConsumerApplication {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ServicesConsumerApplication.class, args);
    }
}

在 Controller 中的調用

@RestController
public class ConsumerController {
    // services-provider 爲服務 provider 的 application.name,負載均衡要求多個服務的 name 相同
    private final String servicesUrl = "http://services-provider/";
    private final RestTemplate restTemplate;

    public ConsumerController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @RequestMapping("/")
    public String home() {
        return restTemplate.getForObject(servicesUrl,String.class);
    }
}

而後運行工程,能夠發現兩個服務都已經成功註冊到註冊中心。

有的博客中可能提到須要添加,@EnableDiscoveryClient 或 @EnableEurekaClient,而實際上,官方文檔已經明確,只要 spring-cloud-starter-netflix-eureka-client 在 classpath 中,應用會自動註冊到 Eureka Server 中去。

By having spring-cloud-starter-netflix-eureka-clienton the classpath, your application automatically registers with the Eureka Server. Configuration is required to locate the Eureka server. https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.2.0.M1/

直接調用 localhost:8085,就顯示 hello world,說明成功!若是不相信的話,能夠起兩個 provider 服務,而後分別輸出不一樣的字符串來驗證負載均衡是否成功。

2019-08-13 15:08:18.689  INFO 14100 --- [nio-8085-exec-4] c.n.l.DynamicServerListLoadBalancer: DynamicServerListLoadBalancer for client services-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=services-provider,current list of Servers=[192.168.140.1:8084]}

簡化後的日誌如上,能夠看到,註解 @LoadBalanced 的負載均衡功能是經過 DynamicServerListLoadBalancer 來實現,這是一種默認的負載均衡策略。獲取到服務列表後,經過 Round Robin 策略(服務按序輪詢從 1 開始,直到 N)進行服務分發。

至此,本地的負載均衡服務就搭建完成了。

服務消費 Feign 與斷路器 Hystrix

若是是初體驗的話能夠忽略這一節。添加這一節的目的是爲了服務的完整性。在實際開發中,Feign 可能用到的更多,而且多會配合 Hystrix。

Feign 也是一種服務消費的方式,採用的是申明式的形式,使用起來更像是本地調用,它也是 Spring Cloud Netflix 中的一員。

Hystrix,簡而言之就是一種斷路器,負載均衡中若是有服務出現問題不可達後,經過配置 Hystrix,能夠實現服務降級和斷路的做用,這個功能 Eureka 默認是不提供的。所以,以前說了,爲了負載均衡的完整性,須要添上它,不然,Ribbon 依然會將請求分發到有問題的服務上去。

那麼要使用他們,首先須要添加依賴

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

在啓動類上加上 @EnableFeignClients

調用的時候須要實現接口。

@FeignClient(value = "services-provider")
public interface IRestRemote {
    @GetMapping("/")
    String home();
}

最後在 Controller 中注入接口,直接調用就能夠了。

@RestController
public class ConsumerController {
    private final IRestRemote remote;

    public ConsumerController(IRestRemote remote) {
        this.remote = remote;
    }

    @RequestMapping("/")
    public String home() {
        return remote.home();
    }

相對於 RestTemplate,Fegin 使用起來更簡單,不須要去構造參數請求了,而且 Feign 底層集成了 Ribbon,也不須要顯示添加 @LoadBalanced 註解了。同時 Feign 也能夠直接使用下面要講的 Hystrix。

Hystrix 的功能不少,遠不止斷路器一種,須要詳細瞭解可看別的博客。這裏就講一下 Feign 如何集成 Hystrix 工做的。

下面先描述一下具體場景。假如如今有兩個負載均衡服務,其中有一個掛了或出現異常了,若是沒有斷路器,進行服務分發的時候,仍然會分配到。若是開啓 Hystrix,首先會進行服務降級(FallBack),也就是出現問題,執行默認的方法,並在知足必定的條件下,熔斷該服務。

在原來 @FeignClient 的基礎上添加 fallback 參數, 並實現降級服務。

@FeignClient(value = "services-provider",fallback = RestRemoteHystrix.class)
public interface IRestRemote {
    @GetMapping("/")
    String home();
}
@Component
public class RestRemoteHystrix implements IRestRemote {
    @override
    public String home() {
        return "default";
    }
}

最後,在配置文件中開啓 Feign 的 Hystrix 開關。

feign:
  hystrix:
    enabled: true

下面就能夠測試了,沿着以前的例子,分別開啓兩個服務,並輸出不一樣的文本,當關閉一個服務後,再請求會發現,分配到關閉的那個服務時,會顯示 「default」,請求屢次後發現,該服務不可用,說明斷路器配置成功!欲知更多,能夠閱讀參考文獻 5-6。

特別注意

1.多節點的配置

若是不是單機,則須要修改部分字段,具體以下注釋:

eureka:
  instance:
    prefer-ip-address: true     # 以 IP 的形式註冊
    # 主機的 ip 地址(一樣考慮集羣環境,一個節點可能會配置多個網段 ip,這裏可指定具體的)
    ip-address:
    # 主機的 http 外網通訊端口,該端口和 server.post 不一樣,好比外網須要訪問某個集羣節點,直接是沒法訪問 server.post 的,而是須要映射到另外一端口。
    # 這個字段就是配置映射到外網的端口號
    non-secure-port:
     # 默認是 hostname 開頭的,修改爲 ip 開頭
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
  client:
    serviceUrl:
      # 這裏不是 localhost 了,而是註冊中心的 ip:port
      defaultZone: http://ip:8080/eureka/
server:
  port: 8084
spring:
  application:
    name: services-provider    # 應用名稱

2.高可用註冊中心

本文上述搭建的是單個註冊中心的例子,基本已經知足我目前的項目需求。可是在閱讀別的博客中,發現真正的微服務架構,爲了實現高可用,通常會有多個註冊中心,而且相互註冊造成。這裏先簡單作個記錄。

3.更復雜,自定義的負載均衡規則。

目前,其實只是引入了 spring cloud 和 Eureka 的依賴就實現了簡單的負載均衡。但仔細看 DynamicServerListLoadBalancer 類的位置,是在 Ribbon 下的。雖然沒有顯式去添加 Ribbon 的依賴包,可是實際上已經被包含進去了。那麼 Ribbon 是什麼?

Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients. Feign already uses Ribbon, so, if you use @FeignClient, this section also applies.

Ribbon 是 Spring Cloud 中的一個組件,主要是作客戶端負載均衡的。不關注底層細節,可能上文搭建的服務它無關,實際上仍是用到了。那麼,若是須要自定義複雜均衡的規則,則須要經過配置 Ribbon 來實現。

Summary

本文主要是」拿來主義「的思想,直接用了 Spring Cloud 中的幾個組件來組建一個負載均衡服務。爲了可以更好地進行服務治理,還需循序漸進先學習基本的原理概念,例如CAP理論等,在逐步學習 Cloud 中的組件,有必定的全局觀。

註冊中心 Eureka 自己知足的就是 AP,在生產環境中,爲了保證服務的高可用,勢必要有至少兩個的註冊中心。

Reference

  1. springcloud(二):註冊中心 Eureka
  2. springcloud(三):服務提供與調用
  3. Spring Cloud Netflix
  4. SpringCloud - 負載均衡器 Ribbon
  5. Spring Cloud(四):服務容錯保護 Hystrix【Finchley 版】
  6. Setup a Circuit Breaker with Hystrix, Feign Client and Spring Boot

Source Code

原文出處:https://www.cnblogs.com/Sinte-Beuve/p/11569789.html

相關文章
相關標籤/搜索