基於Spring Cloud和Netflix OSS 構建微服務-Part 1

前一篇文章《微服務操做模型》中,咱們定義了微服務使用的操做模型。這篇文章中,咱們將開始使用Spring Cloud和Netflix OSS實現這一模型,包含核心部分:服務發現(Service Discovery)、動態路由(Dynamic Routing)、負載均衡(Load Balancing),和邊緣服務器(Edge Server),其餘部分在後面的文章中介紹。html

咱們將使用來自Spring Cloud和Netflix OSS的一些核心組件,實如今已部署的微服務交互,沒必要手動管理配置,如每個微服務的端口或者手工配置路由規則等等。爲了不端口衝突,咱們的微服務在啓動時,將從端口段中動態獲取可用的端口。爲了方便訪問微服務,咱們將使用Edge Server提供一個微服務的訪問入口點。java

在簡要介紹Spring Cloud和Netflix OSS組件以後,咱們將描述本系列文章使用的系統,以及如何訪問源代碼,並編譯。同時,也會簡要指出源代碼中的最重要部分。最後,咱們將運行一些訪問服務的測試代碼,也會演示如何簡單地建立一個新的服務實例,獲取並使用負載均衡,全部這一些都沒必要手工配置。git

 

1. Spring Cloud和Netflix OSSgithub

Spring Cloud是spring.io家庭的一個新項目,包含一系列組件,可用來實現咱們的操做模型。很大程度上而言,Spring Cloud 1.0 是基於Netflix OSS組件。在Spring環境中,Spring Cloud 很是友好地集成了Netflix 組件,使用了和Spring Boot類似的自動配置和慣例優於配置。web

下表映射了操做模式中介紹的組件和咱們將要使用的實際組件:spring

本文將包含Eureka、Ribbon和Zuul:shell

1/Netflix Eureka – Service Discover Server服務發現json

Netflix Eureka容許微服務在運行時自我註冊安全

2/Netflix Ribbon-Dynamic Routing and Load Balancer動態路由和負載均衡服務器

Netflix Ribbon能夠在服務消費方運行時查詢微服務。Ribbon使用Eureka中的信息定位合適的服務實例。若是發現了多個服務實例,Ribbon將應用負載均衡來轉發請求到可用的微服務實例。Ribbon 不做爲一個單獨的服務運行,而是嵌入在每個服務消費方中。

3/Netflix Zuul – Edge Server邊緣服務器

Zuul是咱們對外部世界的守門員,禁止任一未受權的外部請求進入。Zuul在系統內部也提供了方便的進入入口點。經過使用動態分配的端口,能夠避免端口衝突,以及最小化管理成本,可是也致使服務消費方更難接入。Zuul 使用Ribbon來查詢可用的服務,並路由外部的請求到合適的服務實例。在本文中,咱們將僅僅使用Zuul提供了便利的訪問入口點,安所有分在下一篇文章中討論。

備註:經過邊緣服務器(Edge Server),可被外部訪問的微服務,在系統中可稱爲API。

 

2. 系統架構

爲了測試這些組件,咱們須要一個可實施的業務系統。本文章的目標是開發實現以下系統:

 

上圖包含4個業務服務(綠色文本框):

1/ 三個核心服務負責處理信息:產品、推薦和評論;

2/ 一個組合服務 product-composite,用來聚合3個核心服務的信息,組合包含評論和推薦的產品信息視圖;

 

爲了支持業務服務,咱們使用了以下基礎設施服務和組件(藍色文本框):

1/ 服務發現服務器(Service Discovery Server – Netflix Eureka)

2/ 動態路由和負載均衡(Netflix Ribbon)

3/ 邊緣服務器(Edge Server – Netflix Zuul)

 

爲了強調微服務和單體應用的差別,咱們將每個服務運行在單獨的微服務進程中。在一個大系統中,如此細粒度的微服務可能並不方便。相應地,一組相關的微服務可能合併爲一組,保持微服務的數量在可管理的水平,但這並非退回到巨大的單體應用。

 

3. 獲取源代碼並編譯

獲取源代碼,並進行測試,須要已安裝Java SE8和Git,接着執行以下操做:

$ git clone https://github.com/callistaenterprise/blog-microservices.git

$ cd blog-microservices

$ git checkout -b B1 M1.1

 

將生成以下的目錄結構:

 

每個組件獨立編譯(記住咱們再也不編譯單體應用),所以每個組件都有本身的build文件。咱們使用Gradle編譯系統,若是你沒有安裝Gradle,build文件將自動下載。爲了簡化編譯過程,咱們提供了一個小的shell腳本,可用來編譯組件:

$ ./build-all.sh

 

若是在Windows環境下,你能夠執行相應的bat文件 build-all.bat。

將顯示6個log消息,並顯示:BUILD SUCCESSFUL

 

4. 閱讀源代碼

快速看看關鍵的源代碼,每個微服務開發爲一個獨立的Spring Boot應用,並使用Undertow(一個輕量級的Servlet 3.1容器)做爲web server。Spring MVC用來實現 REST-based服務,Spring RestTemplate 用來執行外部調用。若是你想更多地瞭解這些核心技術,你能夠查看相關的文章。

 

這裏,咱們關注如何使用Spring Cloud和Netflix OSS功能。

備註:爲了讓源碼易於理解,咱們特地讓實現儘可能簡單。

 

4.1  Gradle依賴

本着Spring Boot的精髓,Spring Cloud定義了一組starter 依賴,便於引入須要的特定依賴。爲了在微服務中使用Eureka和Ribbon,以及方便調用其餘微服務,在build文件中添加以下:

    compile("org.springframework.cloud:spring-cloud-starter-eureka:1.0.0.RELEASE")

能夠查看product-service/build.gradle 獲取完整的例子。

 

爲了搭建Eureka 服務器,添加以下依賴:

    compile('org.springframework.cloud:spring-cloud-starter-eureka-server:1.0.0.RELEASE')

完整的例子,能夠查看discovery-server/build.gradle。

 

4.2 基礎設施服務器

基於Spring Cloud和Netflix OSS搭建基礎設施服務器至關方便。例如,在一個標準的Spring Boot應用中,添加@EnableEurekaServer標註來搭建Eureka 服務器。

@SpringBootApplication

@EnableEurekaServer

public class EurekaApplication {

 

    public static void main(String[] args) {

        SpringApplication.run(EurekaApplication.class, args);

    }

}

 

完整的實例,能夠查看EurekaApplication.java代碼。

搭建Zuul 服務器,能夠添加@EnableZuulProxy標註。完整的實例,能夠查看ZuulApplication.java代碼。

經過這些簡單的標註,能夠搭建一個默認的服務器配置。根據須要,也能夠經過特定的設置覆蓋默認配置。例如,咱們能夠經過覆蓋默認的配置,限制邊緣服務器容許路由調用的微服務。默認狀況下,Zuul搭建了Eureka中能夠發現的每個微服務的路由。經過以下的application.yml配置,限制了只容許訪問組合服務-product service的路由。

zuul:

  ignoredServices: "*"

  routes:

    productcomposite:

      path: /productcomposite/**

 

查看edge-server/application.yml獲取完整的例子。

 

4.3 業務服務

經過在Spring Boot應用中,添加@EnableDiscoveryClient標註,自動註冊微服務到Eureka Server中。

@SpringBootApplication

@EnableDiscoveryClient

public class ProductServiceApplication {

 

    public static void main(String[] args) {

        SpringApplication.run(ProductServiceApplication.class, args);

    }

}

 

 

完整的例子,能夠查看ProductServiceApplication.java。

爲了查詢和調用微服務實例,可使用Ribbon和Spring RestTemplate,以下所示:

@Autowired

    private LoadBalancerClient loadBalancer;

    ...

    public ResponseEntity<List<Recommendation>> getReviews(int productId) {

   

            ServiceInstance instance = loadBalancer.choose("review");

            URI uri = instance.getUri();

              ...

            response = restTemplate.getForEntity(url, String.class);

 

 

 

服務消費方只須要知道服務的名字,如上述例子中的review,Ribbon(LoadBalancerClient類)將發現服務實例,並返回URI給服務消費方。

 

5. 啓動系統

在本文中,咱們將在本地開發環境中做爲一個java進程來啓動微服務。在接下來的文章中,咱們將描述如何部署微服務到雲環境和Docker容器中。

 

爲了運行下面的一些命令,須要安裝curl和jq工具。

每個微服務使用命令 ./gradlew bootRun 來啓動。

 

首先,啓動微服務基礎設施,如:

$ cd .../blog-microservices/microservices

 

$ cd support/discovery-server;  ./gradlew bootRun

$ cd support/edge-server;       ./gradlew bootRun

 

一旦啓動了上述基礎設施微服務,接着啓動業務微服務:

$ cd core/product-service;                ./gradlew bootRun

$ cd core/recommendation-service;         ./gradlew bootRun

$ cd core/review-service;                 ./gradlew bootRun

$ cd composite/product-composite-service; ./gradlew bootRun

 

若是在Windows環境下,能夠運行相應的bat文件,start-all.bat文件。

一旦微服務啓動了,將自注冊到服務發現服務器(Service Discovery Server - Eureka)中去,並輸出以下日誌:

DiscoveryClient ... - registration status: 204

 

在服務發現web 應用中,能夠看到以下4個業務服務,和邊緣服務器(Edge Server)(http://localhost:8761):

 

爲了瞭解上述服務的更多信息,如使用的ip地址和端口,可以使用Eureka REST API:

$ curl -s -H "Accept: application/json" http://localhost:8761/eureka/apps | jq '.applications.application[] | {service: .name, ip: .instance.ipAddr, port: .instance.port."$"}'

{

  "service": "PRODUCT",

  "ip": "192.168.0.116",

  "port": "59745"

}

{

  "service": "REVIEW",

  "ip": "192.168.0.116",

  "port": "59178"

}

{

  "service": "RECOMMENDATION",

  "ip": "192.168.0.116",

  "port": "48014"

}

{

  "service": "PRODUCTCOMPOSITE",

  "ip": "192.168.0.116",

  "port": "51658"

}

{

  "service": "EDGESERVER",

  "ip": "192.168.0.116",

  "port": "8765"

}

 

 

如今,咱們已經準備好進行測試了。首先,驗證能夠到達咱們的微服務,接着,咱們建立一個新的微服務實例,並經過Ribbon在多個服務實例上實施負載均衡。

備註:在接下來的文章中,咱們也會嘗試失敗的場景,演示電路斷路器(Circuit Breaker)是如何工做的。

 

5.1 開始測試

經過邊緣服務器來調用組合服務,邊緣服務器在端口8765(查看application.yml文件)。咱們經過邊緣服務器,以及路徑/productcomposite/** 可到達 productcomposite 服務。返回的組合響應以下:

$ curl -s localhost:8765/productcomposite/product/1 | jq .

{

    "name": "name",

    "productId": 1,

    "recommendations": [

        {

            "author": "Author 1",

            "rate": 1,

            "recommendationId": 1

        },

        ...

    ],

    "reviews": [

        {

            "author": "Author 1",

            "reviewId": 1,

            "subject": "Subject 1"

        },

        ...

    ],

    "weight": 123

}

 

 

 

若是在微服務內部,咱們實際上能夠直接調用微服務,沒必要經過邊緣服務器。固然,問題是咱們不知道服務運行在什麼端口,由於服務是動態分配的。可是,咱們能夠查看調用Eureka REST API 的輸出,就知道服務監聽的端口了。咱們可使用以下的命令調用3個核心服務(端口號採用Eureka REST API輸出的端口信息):

$ curl -s localhost:51658/product/1 | jq .

$ curl -s localhost:59745/product/1 | jq .

$ curl -s localhost:59178/review?productId=1 | jq .

$ curl -s localhost:48014/recommendation?productId=1 | jq .

 

 

在本身的環境中,使用相應的端口號。

 

5.2 動態負載均衡

爲了不服務故障或者臨時的網絡問題,一般須要多個服務實例,經過負載均衡分發請求。由於咱們使用動態分配的端口和服務發現Server,能夠很是容易添加新的服務實例。例如,能夠簡單啓動一個新的review服務,動態分配一個新的端口,並自我註冊到服務發現服務器(Service Discovery Server)中。

$ cd .../blog-microservices/microservices/core/review-service

$ ./gradlew bootRun

 

稍等片刻,第二個服務實例出如今服務發現web應用中(http://localhost:8761):

 

若是你運行以前的curl命令屢次(curl -s localhost:8765/productcomposite/product/1 | jq .),查看2個review實例的log日誌,能夠發現負載均衡在2個實例之間自動處理調用請求,沒必要手工配置。

 

6. 總結

咱們已經瞭解到Spring Cloud和Netflix OSS 組件是如何用來簡化獨立部署微服務協同工做的,沒必要人工管理每個微服務的端口,或者人工配置路由規則。當新的實例啓動以後,它們會自動被服務發現Server監測到,並經過負載均衡來接收請求。經過使用邊緣服務器(Edge Server),咱們能夠控制什麼微服務暴露給外部消費方,創建系統的API。

 

 

7. 下一步

OK,完成測試以後。接下來還有一些問題沒有回答,例如:

1/ 發生故障將如何處理,如出現一個失敗的微服務;

2/ 如何阻止對API的未受權訪問;

3/ 如何獲知微服務內部的運行圖,例如爲何訂單#123456沒有交付?

 

在接下來的文章中,咱們將瞭解如何使用電路斷路器(Circuit Breaker)來提高服務彈性,使用OAuth 2 限制外部訪問等等。也將瞭解如何使用ELK技術棧來收集全部微服務的日誌,並呈現日誌信息。

 

原文英文連接:Building microservices with Spring Cloud and Netflix OSS, part 1

譯者:Rickie(RickieChina#hotmail*com)

相關文章
相關標籤/搜索