Java進階專題(二十二) 從零開始搭建一個微服務架構系統 (上)

前言

"微服務」一詞源於 Martin Fowler的名爲 Microservices的,博文,能夠在他的官方博客上找到http:/ /martinfowler . com/articles/microservices.html簡單地說,微服務是系統架構上的一種設計風格,它的主旨是將一個本來獨立的系統拆分紅多個小型服務,這些小型服務都在各自獨立的進程中運行,服務之間經過基於HTTP的 RESTfuL AP進行通訊協做。常見微服務框架:Spring的spring cloud、阿里dubbo、華爲ServiceComb、騰訊Tars、Facebook thrift、新浪微博Motan。本章節咱們先從瞭解組成完整系統的各個組件開始,下章節將利用這些組件,搭建出一個完善的分佈式系統。html

Spring Cloud

這就不用多說了,官網有詳細的介紹。java

Spring Cloud Alibaba

Spring Cloud ɵɹibaba 致力於提供微服務開發的一站式解決方案。此項目包含開發分佈式應用微服務的必需組件,方便開發者經過 Spring Cloud 編程模型輕鬆使用這些組件來開發分佈式應用服務mysql

主要組件
Sentinel:把流量做爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。
Nacos:一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平臺。
RocketMQ:一款開源的分佈式消息系統,基於高可用分佈式集羣技術,提供低延時的、高可靠的消息發佈與訂閱服務。
Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata:阿里巴巴開源產品,一個易於使用的高性能微服務分佈式事務解決方案。git

服務註冊與發現

Eureka:官方宣佈2.x再也不開源(閉源),以前的版本已經中止更新;也就說Eureka未來更多的技術提高已經沒有了。因此,若是但願註冊中心有更多強大功能的話,還須要另闢蹊徑 。
Zookeeper:在企業級Zookeeper註冊中心與 Dubbo組合比較多一些,kafka使用的也是,隨着Eureka的停更,咱們能夠經過spring-cloud-starter-zookeeper-discovery這個啓動器,將Zookeeper作爲springcloud的註冊中心。
Consul:go語言開發的,也是一個優秀的服務註冊框架,使用量也比較多。
Nacos:來自於SpringCloudɵɹibaba,在企業中通過了百萬級註冊考驗的,不但能夠完美替換Eureka,還能作其餘組件的替換,因此,Naocs也強烈建議使用。github

介紹下Nacos用做註冊中心

Nacos簡介

Nacos(Dynamic Naming and Configur ation Service) 是阿里巴巴2018年7月開源的項目,致力於發現、配置和管理微服務。web

Nacos安裝

單節點

--下載鏡像
docker pull nacos/nacos-server:1.3.1
--啓動容器
docker run  --name nacos --env MODE=standalone --privileged=true  -p 8848:8848 --restart=always   -d dc833dc45d8f

訪問:spring

http://127.0.0.1:8848/nacos 帳號密碼都是nacossql

集羣

安裝前提docker

64 bit OS Linux/Unix/Mac,推薦使用Linux系統。
集羣須要依賴mysql,單機可沒必要
3個或3個以上Nacos節點才能構成集羣
搭建Nacos高可用集羣步驟:

一、須要去Nacos官網clone Nacos集羣項目nacos-docker
二、nacos-docker是使用的Docker Compose對容器進行編排,因此首先須要安裝Docker Compose詳細信息可參照Nacos官網:https:/ /nacos.io/zh-cn/docs/quick-start-docker.htmlshell

1)安裝Docker Compose
什麼是Docker Compose
Compose項目是Docker官方的開源項目,負責實現對Docker容器集羣的快速編排。

#在Linux下下載(下載到/usr/local/bin)
curl -L https://github.com/docker/compose/releases/download/1.25.0/run.sh > /usr/local/bin/docker-compose
# 設置文件可執行權限
chmod +x /usr/local/bin/docker-compose
# 查看版本信息
docker-compose --version

2)克隆Nacos-docker項目

#切換到自定義目錄
cd /usr/local/nacos
#開始clone
git clone https://github.com/nacos-group/nacos-docker.git

3)運行nacos-docker腳本

#執行編排命令
docker-compose -f /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml up

上面的編排命令主要下載mysql鏡像和nacos鏡像,自動完成鏡像下載/容器啓動
Pulling from nacos/nacos-mysql,版本是5. 7(執行初始化腳本)
Pulling nacos3 (nacos/nacos-server:latest)最新版本

4)中止、啓動

#啓動
docker-compose -f  /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml start
#中止
docker-compose -f  /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml stop

5)訪問Nacos

http://192.168.1.1:8848/nacos
http://192.168.1.1:8849/nacos
http://192.168.1.1:8850/nacos

Nacos快速入門

配置服務提供者

服務提供者能夠經過 Nacos 的服務註冊發現功能將其服務註冊到 Nacos server 上。

添加nacos依賴

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>${latest.version}</version>
</dependency>

添加配置

server.port=8070
spring.application.name=nacos-demo

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

啓動類

@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderApplication {

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

	@RestController
	class EchoController {
		@RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
		public String echo(@PathVariable String string) {
			return "Hello Nacos Discovery " + string;
		}
	}
}

啓動後,控制檯:

說明註冊成功,後臺查看該服務:

下面咱們用spring cloud 整合naocs實現服務調用

配置服務消費者

添加配置

server.port=8080
spring.application.name=service-consumer

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

添加啓動類:服務消費者使用 @LoadBalanced RestTemplate 實現服務調用

@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {

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

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

    @RestController
    public class TestController {

        private final RestTemplate restTemplate;

        @Autowired
        public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}

        @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
        public String echo(@PathVariable String str) {
            return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
        }
    }
}

測試

啓動 ProviderApplicationConsumerApplication ,調用 http://localhost:8080/echo/2018,返回內容爲 Hello Nacos Discovery 2018

分佈式配置中心解決方案與應用

目前市面上用的比較多的配置中心有(時間順序)
Disconf:2014年7月百度開源的配置管理中心,一樣具有配置的管理能力,不過目前已經不維護了,最近的一次提交是4-5年前了。
Spring Cloud Config:2014年9月開源,Spring Cloud 生態組件,能夠和Spring Cloud體系無縫整合。
Apollo:2016年5月,攜程開源的配置管理中心,具有規範的權限、流程治理等特性。
Nacos:2018年6月,阿里開源的配置中心,也能夠作DNS和RPC的服務發現

介紹下Nacos用做分佈式配置中心

啓動了 Nacos server 後,您就能夠參考如下示例代碼,爲您的 Spring Cloud 應用啓動 Nacos 配置管理服務了。完整示例代碼請參考:nacos-spring-cloud-config-example

  1. 添加依賴:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>${latest.version}</version>
</dependency>

注意:版本 2.1.x.RELEASE 對應的是 Spring Boot 2.1.x 版本。版本 2.0.x.RELEASE 對應的是 Spring Boot 2.0.x 版本,版本 1.5.x.RELEASE 對應的是 Spring Boot 1.5.x 版本。

更多版本對應關係參考:版本說明 Wiki

  1. bootstrap.properties 中配置 Nacos server 的地址和應用名
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

spring.application.name=example

說明:之因此須要配置 spring.application.name ,是由於它是構成 Nacos 配置管理 dataId字段的一部分。

在 Nacos Spring Cloud 中,dataId 的完整格式以下:

${prefix}-${spring.profiles.active}.${file-extension}
  • prefix 默認爲 spring.application.name 的值,也能夠經過配置項 spring.cloud.nacos.config.prefix來配置。
  • spring.profiles.active 即爲當前環境對應的 profile,詳情能夠參考 Spring Boot文檔注意:當 spring.profiles.active 爲空時,對應的鏈接符 - 也將不存在,dataId 的拼接格式變成 ${prefix}.${file-extension}
  • file-exetension 爲配置內容的數據格式,能夠經過配置項 spring.cloud.nacos.config.file-extension 來配置。目前只支持 propertiesyaml 類型。
  1. 經過 Spring Cloud 原生註解 @RefreshScope 實現配置自動更新:
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {

    @Value("${useLocalCache:false}")
    private boolean useLocalCache;

    @RequestMapping("/get")
    public boolean get() {
        return useLocalCache;
    }
}
  1. 首先經過調用 Nacos Open API 向 Nacos Server 發佈配置:dataId 爲example.properties,內容爲useLocalCache=true
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=true"
  1. 運行 NacosConfigApplication,調用 curl http://localhost:8080/config/get,返回內容是 true
  2. 再次調用 Nacos Open API 向 Nacos server 發佈配置:dataId 爲example.properties,內容爲useLocalCache=false
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=false"
  1. 再次訪問 http://localhost:8080/config/get,此時返回內容爲false,說明程序中的useLocalCache值已經被動態更新了。

分佈式服務調用

RPC概述

RPC 的主要功能目標是讓構建分佈式計算(應用)更容易,在提供強大的遠程調用能力時不損失本地調用的語義簡潔性。爲實現該目標,RPC 框架需提供一種透明調用機制,讓使用者沒必要顯式的區分本地調用和遠程調用。

RPC的優勢:分佈式設計、部署靈活、解耦服務、擴展性強。

RPC框架

Dubbo:國內最先開源的 RPC 框架,由阿里巴巴公司開發並於 2011 年底對外開源,僅支持 Java 語言。
Motan:微博內部使用的 RPC 框架,於 2016 年對外開源,僅支持 Java 語言。
Tars:騰訊內部使用的 RPC 框架,於 2017 年對外開源,僅支持 C++ 語言。
Spring Cloud:國外 Pivotal 公司 2014 年對外開源的 RPC 框架,提供了豐富的生態組件。
gRPC:Google 於 2015 年對外開源的跨語言 RPC 框架,支持多種語言。
Thrift:最初是由 Facebook 開發的內部系統跨語言的 RPC 框架,2007 年貢獻給了 Apache 基金,成爲
Apache:開源項目之一,支持多種語言。

RPC框架優勢

RPC框架通常使用長連接,沒必要每次通訊都要3次握手,減小網絡開銷。
RPC框架通常都有註冊中心,有豐富的監控管理髮布、下線接口、動態擴展等,對調用方來講是無感知、統一化的操做協議私密,安全性較高
RPC 協議更簡單內容更小,效率更高,服務化架構、服務化治理,RPC框架是一個強力的支撐。

RPC框架應用:使用Spring Cloud Alibaba 整合Dubbo實現

因爲 Dubbo Spring Cloud 構建在原生的 Spring Cloud 之上, 其服務治理方面的能力可認爲是 Spring Cloud Plus,不只徹底覆蓋 Spring Cloud 原生特性,並且提供更爲穩定和成熟的實現,特性比對以下表所示:

Dubbo 做爲 Spring Cloud 服務調用

默認狀況,Spring Cloud Open Feign 以及@LoadBalanced`RestTemplate 做爲 Spring Cloud 的兩種服務調用方式。 Dubbo Spring Cloud 爲其提供了第三種選擇,即 Dubbo 服務將做爲 Spring Cloud 服務調用的同等公民出現,應用可經過 Apache Dubbo 註解@Service 和@Reference 暴露和引用 Dubbo 服務,實現服務間多種協議的通信。同時,也能夠利用 Dubbo 泛化接口輕鬆實現服務網關。

快速上手

按照傳統的 Dubbo 開發模式,在構建服務提供者以前,第一個步驟是爲服務提供者和服務消費者定義 Dubbo 服務接口。
爲了確保契約的一致性,推薦的作法是將 Dubbo 服務接口打包在第二方或者第三方的 artifact(jar)中,該 artifact 甚至無需添加任何依賴。
對於服務提供方而言,不只經過依賴 artifact 的形式引入 Dubbo 服務接口,並且須要將其實現。對應的服務消費端,一樣地須要依賴該 artifact,並以接口調用的方式執行遠程方法。接下來的步驟則是建立 artifact。

建立服務API

建立一個api模塊,專門寫各類接口的:

/**
 * @author 原
 * @date 2020/12/8
 * @since 1.0
 **/
public interface TestService {

    String getMsg();
}

建立服務提供者

導入依賴

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-dubbo</artifactId>
        </dependency>
        <!-- api接口的依賴包-->
        <dependency>
            <groupId>com.dubbo.demo</groupId>
            <artifactId>dubbo-demo-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

編寫配置

dubbo:
	scan:
	# dubbo 服務掃描基準包
		base-packages: org.springframework.cloud.alibaba.dubbo.bootstrap
	protocol:
	# dubbo 協議
		name: dubbo
		# dubbo 協議端口( -1 表示自增端口,從 20880 開始)
		port: -1
	spring:
		cloud:
			nacos:
			# Nacos 服務發現與註冊配置
			discovery:
				server-addr: 127.0.0.1:8848實現
/**
 * @author 原
 * @date 2021/1/28
 * @since 1.0
 **/
@Service//dubbo的service註解
public class TestServiceImpl implements TestService {
    @Override
    public String getMsg() {
        return "123123";
    }
}

啓動類

/**
 * @author 原
 * @date 2021/1/28
 * @since 1.0
 **/
@SpringBootApplication
@EnableDiscoveryClient
public class DubboProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(DubboProviderApplication.class,args);
    }
}

建立服務消費者

除了api的實現類 其餘複用提供者的代碼

編寫測試類

/**
 * @author 原
 * @date 2021/1/28
 * @since 1.0
 **/
@RestController
public class TestController {

    @Reference
    TestService testService;

    @GetMapping("/dubbo/test")
    public String getMsg(){
        return testService.getMsg();
    }
}

訪問:

返回111

服務流量管理

爲何要流控降級

流量是很是隨機性的、不可預測的。前一秒可能還風平浪靜,後一秒可能就出現流量洪峯了(例如雙十一零點的場景)。然而咱們系統的容量老是有限的,若是忽然而來的流量超過了系統的承受能力,就可能會致使請求處理不過來,堆積的請求處理緩慢,CPU/Load飆高,最後致使系統崩潰。所以,咱們須要針對這種突發的流量來進行限制,在儘量處理請求的同時來保障服務不被打垮,這就是流量控制

一個服務經常會調用別的模塊,多是另外的一個遠程服務、數據庫,或者第三方API 等。例如,支付的時候,可能須要遠程調用銀聯提供的 API;查詢某個商品的價格,可能須要進行數據庫查詢。然而,這個被依賴服務的穩定性是不能保證的。若是依賴的服務出現了不穩定的狀況,請求的響應時間變長,那麼調用服務的方法的響應時間也會變長,線程會產生堆積,最終可能耗盡業務自身的線程池,服務自己也變得不可用。

現代微服務架構都是分佈式的,由很是多的服務組成。不一樣服務之間相互調用,組成複雜的調用鏈路。以上的問題在鏈路調用中會產生放大的效果。複雜鏈路上的某一環不穩定,就可能會層層級聯, 最終致使整個鏈路都不可用。 所以咱們須要對不穩定的弱依賴服務進行熔斷降級,暫時切斷不穩定調用,避免局部不穩定因素致使總體的雪崩。

關於容錯組件的停更/升級/替換

服務降級:
Hystrix:官網不極力推薦,可是中國企業中還在大規模使用,對於限流和熔斷降級雖然在1 .5版本官方還支持(版本穩定),
但如今官方已經開始推薦你們使用Resilience4j
Resilience4J:官網推薦使用,可是國內不多用這個。
Sentienl:來自於Spring Cloud Alibaba,在中國企業替換Hystrix的組件,國內強烈建議使用

這裏就主要介紹下Sentinel。

Sentinel介紹

Sentinel是阿里開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個維度來保障服務之間的穩定性。
Sentinel的流控操做起來很是簡單,在控制檯進行配置便可看見效,所見即所得

Sentinel 具備如下特徵:

  • 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量能夠承受的範圍)、消息削峯填谷、集羣流量控制、實時熔斷下游不可用應用等。
  • 完備的實時監控:Sentinel 同時提供實時的監控功能。您能夠在控制檯中看到接入應用的單臺機器秒級數據,甚至 500 臺如下規模的集羣的彙總運行狀況。
  • 普遍的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只須要引入相應的依賴並進行簡單的配置便可快速地接入 Sentinel。
  • 完善的 SPI 擴展點:Sentinel 提供簡單易用、完善的 SPI 擴展接口。您能夠經過實現擴展接口來快速地定製邏輯。例如定製規則管理、適配動態數據源等。

官網

https://github.com/alibaba/Sentinel
中文
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
https://sentinelguard.io/zh-cn/docs/introduction.html

Sentinel 的使用能夠分爲兩個部分:
核心庫(Java 客戶端):不依賴任何框架/庫,可以運行於 Java 7 及以上的版本的運行時環境,同時對Dubbo / Spring Cloud 等框架也有較好的支持。
控制檯(Dashboard):控制檯主要負責管理推送規則、監控、集羣限流分配管理、機器發現等

使用場景

 在服務提供方(Service Provider)的場景下,咱們須要保護服務提供方自身不被流量洪峯打垮。 這時候一般根據服務提供方的服務能力進行流量控制, 或針對特定的服務調用方進行限制。咱們能夠結合前期壓測評估核心口的承受能力,配置 QPS 模式的限流,當每秒的請求量超過設定的閾值時,會自動拒絕多餘的請求。

 爲了不調用其餘服務時被不穩定的服務拖垮自身,咱們須要在服務調用端(Service Consumer)對不穩定服務依賴進行隔離和熔斷。手段包括信號量隔離、異常比例降級、RT 降級等多種手段。

 當系統長期處於低水位的狀況下, 流量忽然增長時, 直接把系統拉昇到高水位可能瞬間把系統壓垮。這時候咱們能夠藉助 Sentinel 的 WarmUp 流控模式控制經過的流量緩慢增長,在必定時間內逐漸增長到閾值上限,而不是在一瞬間所有放行。這樣能夠給冷系統一個預熱的時間,避免冷系統被壓垮。

 利用 Sentinel 的勻速排隊模式進行「削峯填谷」, 把請求突刺均攤到一段時間內, 讓系統負載保持在請求處理水位以內,同時儘量地處理更多請求。

 利用 Sentinel 的網關流控特性,在網關入口處進行流量防禦,或限制 API 的調用頻率。

Sentinel安裝

一、下載jar包https://github.com/alibaba/Sentinel/releases

二、啓動

java -Dserver.port=8787 -Dcsp.sentinel.dashboard.server=127.0.0.1:8787 -Dproject.name=sentinel-dashboard -jar /home/sentinel/sentinel-dashboard-1.8.0.jar

三、訪問

http://127.0.0.1:8787/#/login

初始帳號密碼sentinel/sentinel

能夠看到sentinel是本身自己的監控

sentinel快速入門

一、導入依賴

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.0</version>
</dependency>

二、測試類

public class TestService {

    public static void main(String[] args) {
        initFlowRules();
        while (true) {
            Entry entry = null;
            try {
                entry = SphU.entry("HelloWorld");
                /*您的業務邏輯 - 開始*/
                System.out.println("hello world");
                /*您的業務邏輯 - 結束*/
            } catch (BlockException e1) {
                /*流控邏輯處理 - 開始*/
                System.out.println("block!");
                /*流控邏輯處理 - 結束*/
            } finally {
                if (entry != null) {
                    entry.exit();
                }
            }
        }
    }

    //設置流量控制規則 設置當QPS達到20時 會限制流量(拋出異常,能夠執行處理)
    private static void initFlowRules(){
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("HelloWorld");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // Set limit QPS to 20.
        rule.setCount(20);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

執行結果:

能夠看到,這個程序每秒穩定輸出 "hello world" 20 次,和規則中預先設定的閾值是同樣的。 block表示被阻止的請求。

官方使用文檔:https://github.com/alibaba/Sentinel/wiki/如何使用

Sentinel整合SpringCloud實現服務限流/熔斷

導入依賴

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
    </dependencies>

配置

server.port=8082
spring.application.name=sentinel-demo
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8787
//在須要流控的方法加上@SentinelResource
@RestController
public class TestController {

    @GetMapping("/sentinel")
    @SentinelResource
    public String getMsg(){
        return "11";
    }
}

啓動應用,訪問http://127.0.0.1:8082/sentinel後去sentinel後臺

下面咱們來配一條最簡單的流控規則。針對 sentinel_spring_web_context /sentinel 這個服務調用配置限流規則(須要有過訪問量才能看到)。咱們配一條 QPS 爲 1的流控規則,這表明針對該服務方法的調用每秒鐘不能超過 1 次,超出會直接拒絕。

如今快速訪問:http://localhost:8082/sentinel

查看實時監控頁面:

其餘功能的使用,你們能夠參考官方文檔自行摸索。

如何選擇流控降級組件

如下是 Sent inel 與其它fault-tolerance 組件的對比:

分佈式事務

待續...

相關文章
相關標籤/搜索