邊作邊學,基於Spring Cloud的微服務架構最佳實踐

本文節選自開源無服務器PaaS Rainbond文檔,原文請戳連接html

概述

微服務是能夠獨立部署、水平擴展、獨立訪問(或者有獨立的數據庫)的服務單元,Spring Cloud則是用來管理微服務的一系列框架的有序集合。利用Spring Boot的開發便利性,Spring Cloud巧妙簡化了分佈式系統基礎設施的開發,例如服務發現註冊、配置中心、消息總線、負載均衡、斷路器等,均可以用Spring Boot的開發風格作到一鍵啓動和部署。git

Spring Cloud並無重複造輪子,而是將目前各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,經過Spring Boot風格進行再封裝,屏蔽掉了複雜的配置和實現原理,最終爲開發者提供了一套簡單易懂、易部署、易維護的分佈式系統開發工具包。github

Spring Cloud有不少組件,其中最核心的組件有:Eureka(註冊中心)、Hystrix(斷路器)、Config(配置中心)、Zuul(代理、網關)等等。web

接下來,咱們不妨經過幾個demo來了解Spring Cloud是如何一步步構建起來的。spring

示例源碼請戳源碼數據庫

  • 如何搭建Eureka
  • 如何搭建Hystrix
  • 如何搭建Config
  • 如何搭建Zuul

如何搭建Eureka

組件介紹

註冊中心Eureka是一個基於REST的服務,用於各個服務之間的互相發現。任何服務須要其它服務的支持都須要經過它獲取;一樣的,全部的服務都須要來這裏註冊,方便之後其它服務來調用。Eureka的好處是你不須要知道找什麼服務,只須要到註冊中心來獲取,也不須要知道提供支持的服務在哪裏、是幾個服務來支持的,直接來這裏獲取就能夠了。如此一來,便提高了穩定性,也下降了微服務架構搭建的難度。bootstrap

項目描述

正常調用服務A請求服務B:後端

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="50%" height="50%">
</div>瀏覽器

有了服務中心以後,服務A不能直接調用服務B,而是A,B經過在註冊中心中註冊服務,而後互相發現,服務A經過註冊中心來調用服務B:springboot

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="75%" height="75%">
</div>

以上只是2個服務之間的相互調用,若是有十幾個甚至幾十個服務,其中任何的一個項目改動,就可能牽連到好幾個項目的重啓,很麻煩並且容易出錯。經過註冊中心來獲取服務,你不須要關注你調用的項目IP地址,由幾臺服務器組成,每次直接去註冊中心獲取可使用的服務去調用既可。

部署到雲幫

註冊服務

一、pom中添加依賴

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

二、添加啓動代碼中添加@EnableEurekaServer註解

@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurekaApplication {

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

三、配置文件

在默認設置下,該服務註冊中心也會將本身做爲客戶端來嘗試註冊它本身,因此咱們須要禁用它的客戶端註冊行爲,在application.properties添加如下配置:

spring.application.name=spring-cloud-eureka

server.port=8000
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
  • eureka.client.register-with-eureka :表示是否將本身註冊到Eureka Server,默認爲true。
  • eureka.client.fetch-registry :表示是否從Eureka Server獲取註冊信息,默認爲true。
  • eureka.client.serviceUrl.defaultZone :設置與Eureka Server交互的地址,查詢服務和註冊服務都須要依賴這個地址。默認是http://localhost:8761/eureka ;多個地址可以使用 , 分隔。

啓動工程後,訪問:http://localhost:8000/,能夠看到下面的頁面,其中尚未發現任何服務

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="100%" height="100%">
</div>

服務提供者(B)

一、pom包配置

建立一個springboot項目,pom.xml中添加以下配置:

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

二、配置文件

application.properties配置以下:

spring.application.name=spring-cloud-producer
server.port=9000
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

三、啓動類

啓動類中添加@EnableDiscoveryClient註解

@SpringBootApplication
@EnableDiscoveryClient
public class ProducerApplication {

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

四、服務提供

提供hello服務:

@RestController
public class HelloController {
    
    @RequestMapping("/hello")
    public String index(@RequestParam String name) {
        return "hello "+name+",this is first messge";
    }
}

添加@EnableDiscoveryClient註解後,項目就具備了服務註冊的功能。啓動工程後,就能夠在註冊中心的頁面看到SPRING-CLOUD-PRODUCER服務。

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="100%" height="100%">
</div>

到此服務提供者配置就完成了。

服務消費者(A)

一、pom包配置

和服務提供者一致

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

二、配置文件

application.properties配置以下:

spring.application.name=spring-cloud-consumer
server.port=9001
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

三、啓動類

啓動類添加@EnableDiscoveryClient@EnableFeignClients註解:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {

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

}
  • @EnableDiscoveryClient :啓用服務註冊與發現
  • @EnableFeignClients:啓用feign進行遠程調用

Feign是一個聲明式Web Service客戶端。使用Feign能讓編寫Web Service客戶端更加簡單, 它的使用方法是定義一個接口,而後在上面添加註解,同時也支持JAX-RS標準的註解。Feign也支持可拔插式的編碼器和解碼器。Spring Cloud對Feign進行了封裝,使其支持了Spring MVC標準註解和HttpMessageConverters。Feign能夠與Eureka和Ribbon組合使用以支持負載均衡。

四、feign調用實現

@FeignClient(name= "spring-cloud-producer")
public interface HelloRemote {
    @RequestMapping(value = "/hello")
    public String hello(@RequestParam(value = "name") String name);
}
  • name:遠程服務名,及spring.application.name配置的名稱

此類中的方法和遠程服務中contoller中的方法名和參數需保持一致。

五、web層調用遠程服務

將HelloRemote注入到controller層,像普通方法同樣去調用便可。

@RestController
public class ConsumerController {

    @Autowired
    HelloRemote HelloRemote;
    
    @RequestMapping("/hello/{name}")
    public String index(@PathVariable("name") String name) {
        return HelloRemote.hello(name);
    }

}

到此,最簡單的一個服務註冊與調用的例子就完成了。

測試

依次啓動spring-cloud-eureka、spring-cloud-producer、spring-cloud-consumer三個項目。

先輸入:http://localhost:9000/hello?name=neo 檢查spring-cloud-producer服務是否正常

返回:hello neo,this is first messge

說明spring-cloud-producer正常啓動,提供的服務也正常。

瀏覽器中輸入:http://localhost:9001/hello/neo

返回:hello neo,this is first messge

說明客戶端已經成功的經過feign調用了遠程服務hello,而且將結果返回到了瀏覽器。

{{site.data.alerts.callout_danger}}
部署在雲幫,須要驗證必須保證一下3點:

  1. 端口開啓了外部訪問功能
  2. consumer關聯了producer
  3. hello?name=neohello/neo添加在訪問所產生的url後

以後組件的驗證同理。

如何搭建Hystrix

組件介紹

​在微服務架構中一般會有多個服務層調用,基礎服務的故障可能會致使級聯故障,進而形成整個系統不可用的狀況,這種現象被稱爲服務雪崩效應。而使用Hystrix(熔斷器)就能夠避免這種問題。

​熔斷器的原理很簡單,如同電力過載保護器。它能夠實現快速失敗,若是它在一段時間內偵測到許多相似的錯誤,會強迫其之後的多個調用快速失敗,再也不訪問遠程服務器,從而防止應用程序不斷地嘗試執行可能會失敗的操做,使得應用程序繼續執行而不用等待修正錯誤,或者浪費CPU時間去等到長時間的超時產生。熔斷器也可使應用程序可以診斷錯誤是否已經修正,若是已經修正,應用程序會再次嘗試調用操做。

​當Hystrix Command請求後端服務失敗數量超過必定比例(默認50%), 斷路器會切換到開路狀態(Open). 這時全部請求會直接失敗而不會發送到後端服務. 斷路器保持在開路狀態一段時間後(默認5秒), 自動切換到半開路狀態(HALF-OPEN). 這時會判斷下一次請求的返回狀況, 若是請求成功, 斷路器切回閉路狀態(CLOSED), 不然從新切換到開路狀態(OPEN). Hystrix的斷路器就像咱們家庭電路中的保險絲, 一旦後端服務不可用, 斷路器會直接切斷請求鏈, 避免發送大量無效請求影響系統吞吐量, 而且斷路器有自我檢測並恢復的能力。

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="50%" height="50%">
</div>

項目描述

​經過將Hystrix組件添加到服務消費者,實現熔斷效果,只須要在Eureka的demo基礎加入Hystrix便可。

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="50%" height="50%">
</div>

Hystrix是做用在服務調用端的,所以須要添加在A上。

部署到Rainbond

Hystrix服務

由於熔斷只是做用在服務調用這一端,所以咱們根據上一篇的示例代碼只須要改動消費者(A)服務相關代碼就能夠。由於,Feign中已經依賴了Hystrix因此在maven配置上不用作任何改動。

一、配置文件
application.properties添加這一條:

feign.hystrix.enabled=true

二、建立回調類

建立HelloRemoteHystrix類繼承與HelloRemote實現回調的方法:

@Component
public class HelloRemoteHystrix implements HelloRemote{

    @Override
    public String hello(@RequestParam(value = "name") String name) {
        return "hello" +name+", this messge send failed ";
    }
}

三、添加fallback屬性

HelloRemote類添加指定fallback類,在服務熔斷的時候返回fallback類中的內容:

@FeignClient(name= "spring-cloud-producer",fallback = HelloRemoteHystrix.class)
public interface HelloRemote {

    @RequestMapping(value = "/hello")
    public String hello(@RequestParam(value = "name") String name);

}

四、測試

那咱們就來測試一下看看效果吧。

依次啓動spring-cloud-eureka、spring-cloud-producer、spring-cloud-consumer三個項目。

瀏覽器中輸入:http://localhost:9001/hello/neo

返回:hello neo,this is first messge

說明加入熔斷相關信息後,不影響正常的訪問。接下來咱們手動中止spring-cloud-producer項目再次測試:

瀏覽器中輸入:http://localhost:9001/hello/neo

返回:hello neo, this messge send failed

根據返回結果說明熔斷成功。

如何搭建Config

組件介紹

​ 隨着線上項目變的日益龐大,每一個項目都散落着各類配置文件,若是採用分佈式的開發模式,須要的配置文件隨着服務增長而不斷增多。某一個基礎服務信息變動,都會引發一系列的更新和重啓,不便於項目的維護,而Spring Cloud Config就是解決這個問題的。

業務描述

​目前Config支持git和svn做爲存放配置文件的倉庫,本次示例使用git倉庫來存放配置文件。這裏的Config-client 就至關於服務A和服務B,他們的配置文件都集中存放,經過Config-server來獲取各自的配置文件。

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="50%" height="50%">
</div>

部署到Rainbond

git倉庫

首先在github上面建立了一個文件夾config-repo用來存放配置文件,爲了模擬生產環境,咱們建立如下三個配置文件:

// 開發環境
neo-config-dev.properties
// 測試環境
neo-config-test.properties
// 生產環境
neo-config-pro.properties

每一個配置文件中都寫一個屬性neo.hello,屬性值分別是 hello im dev/test/pro 。下面咱們開始配置server端

config-server端

一、添加依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
</dependencies>

只須要加入spring-cloud-config-server包引用既可。

二、配置文件

server:
  port: 8040
spring:
  application:
    name: spring-cloud-config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/xxx                             # 配置git倉庫的地址
          search-paths: config-repo                             # git倉庫地址下的相對地址,能夠配置多個,用,分割。
          username:                                             # git倉庫的帳號
          password:                                             # git倉庫的密碼

Spring Cloud Config也提供本地存儲配置的方式。咱們只須要設置屬性spring.profiles.active=native,Config Server會默認從應用的src/main/resource目錄下檢索配置文件。也能夠經過spring.cloud.config.server.native.searchLocations=file:E:/properties/屬性來指定配置文件的位置。雖然Spring Cloud Config提供了這樣的功能,可是爲了支持更好的管理內容和版本控制的功能,仍是推薦使用git的方式。

三、啓動類

啓動類添加@EnableConfigServer,激活對配置中心的支持

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

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

到此server端相關配置已經完成

四、測試server端

首先咱們先要測試server端是否能夠讀取到github上面的配置信息,直接訪問:http://localhost:8001/neo-config/dev

返回信息以下:

{
    "name": "neo-config", 
    "profiles": [
        "dev"
    ], 
    "label": null, 
    "version": null, 
    "state": null, 
    "propertySources": [
        {
            "name": "https://github.com/goodrain-apps/spring-cloud-demo/config-repo/neo-config-dev.properties", 
            "source": {
                "neo.hello": "hello im dev update"
            }
        }
    ]
}

上述的返回的信息包含了配置文件的位置、版本、配置文件的名稱以及配置文件中的具體內容,說明server端已經成功獲取了git倉庫的配置信息。

若是直接查看配置文件中的配置信息可訪問:http://localhost:8001/neo-config-dev.properties,返回:neo.hello: hello im dev

修改配置文件neo-config-dev.properties中配置信息爲:neo.hello=hello im dev update,再次在瀏覽器訪問http://localhost:8001/neo-config-dev.properties,返回:neo.hello: hello im dev update。說明server端會自動讀取最新提交的內容

倉庫中的配置文件會被轉換成web接口,訪問能夠參照如下的規則:

  • /{application}/{profile}[/{label}]
  • /{application}-{profile}.yml
  • /{label}/{application}-{profile}.yml
  • /{application}-{profile}.properties
  • /{label}/{application}-{profile}.properties

以neo-config-dev.properties爲例子,它的application是neo-config,profile是dev。client會根據填寫的參數來選擇讀取對應的配置。

Config-client端

主要展現如何在業務項目中去獲取server端的配置信息

一、添加依賴

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

引入spring-boot-starter-web包方便web測試

二、配置文件

須要配置兩個配置文件,application.properties和bootstrap.properties

application.properties以下:

spring.application.name=spring-cloud-config-client
server.port=8002

bootstrap.properties以下:

spring.cloud.config.name=neo-config
spring.cloud.config.profile=dev
spring.cloud.config.uri=http://localhost:8001/
spring.cloud.config.label=master
  • spring.application.name:對應{application}部分
  • spring.cloud.config.profile:對應{profile}部分
  • spring.cloud.config.label:對應git的分支。若是配置中心使用的是本地存儲,則該參數無用
  • spring.cloud.config.uri:配置中心的具體地址
  • spring.cloud.config.discovery.service-id:指定配置中心的service-id,便於擴展爲高可用配置集羣。

上面這些與spring-cloud相關的屬性必須配置在bootstrap.properties中,config部份內容才能被正確加載。由於config的相關配置會先於application.properties,而bootstrap.properties的加載也是先於application.properties。

三、啓動類

啓動類添加@EnableConfigServer,激活對配置中心的支持

@SpringBootApplication
public class ConfigClientApplication {

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

啓動類只須要@SpringBootApplication註解就能夠

四、web測試

使用@Value註解來獲取server端參數的值

@RestController
class HelloController {
    @Value("${neo.hello}")
    private String hello;

    @RequestMapping("/hello")
    public String from() {
        return this.hello;
    }
}

啓動項目後訪問:http://localhost:8002/hello,返回:hello im dev update說明已經正確的從server端獲取到了參數。到此一個完整的服務端提供配置服務,客戶端獲取配置參數的例子就完成了。

如何搭建Zuul

組件介紹

​在微服務架構中,後端服務每每不直接開放給調用端,而是經過一個API網關根據請求的url,路由到相應的服務。當添加API網關後,在第三方調用端和服務提供方之間就建立了一面牆,這面牆直接與調用方通訊進行權限控制,後將請求均衡分發給後臺服務端。而用來進行代理調度的組件就是Zuul。

項目描述

​在項目中,只有Zuul提供對外訪問,Gateway經過請求的url的不一樣,將請求調度到不一樣的後端服務

<div align=center>
<img src="http://grstatic.oss-cn-shangh...; width="75%" height="75%">
</div>

部署到Rainbond

Gateway

一、添加依賴

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

引入spring-cloud-starter-zuul

二、配置文件

spring.application.name=gateway-service-zuul
server.port=8888

#這裏的配置表示,訪問/producer/** 直接重定向到http://域名/**
zuul.routes.baidu.path=/producer/**
zuul.routes.baidu.url=http://域名/

三、啓動類

@SpringBootApplication
@EnableZuulProxy
public class GatewayServiceZuulApplication {

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

啓動類添加@EnableZuulProxy,支持網關路由。

四、測試

啓動gateway-service-zuul-simple項目,先輸入:http://localhost:8888/producer/hello?name=neo

返回:hello neo,this is first messge

說明調度成功。

小結

至此,咱們就完成了Eureka、Hystrix、Config、Zuul等幾個Spring Cloud最核心組件的搭建,更多內容敬請關注Rainbond文檔

或參考雲框架項目,[[雲框架]基於Spring Cloud的微服務架構](https://github.com/cloudframe...

相關文章
相關標籤/搜索