Spring Cloud Alibaba微服務開發的一站式解決方案

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

Spring Cloud Alibaba微服務開發的一站式解決方案

依託 Spring Cloud Alibaba,您只需要添加一些註解和少量配置,就可以將 Spring Cloud 應用接入阿里微服務解決方案,通過阿里中間件來迅速搭建分佈式應用系統。

Spring Cloud Alibaba微服務開發的一站式解決方案

主要功能

  • 服務限流降級:默認支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降級功能的接入,可以在運行時通過控制檯實時修改限流降級規則,還支持查看限流降級 Metrics 監控。
  • 服務註冊與發現:適配 Spring Cloud 服務註冊與發現標準,默認集成了 Ribbon 的支持。
  • 分佈式配置管理:支持分佈式系統中的外部化配置,配置更改時自動刷新。
  • 消息驅動能力:基於 Spring Cloud Stream 為微服務應用構建消息驅動能力。
  • 分佈式事務:使用 @GlobalTransactional 註解, 高效並且對業務零侵入地解決分佈式事務問題。。
  • 阿里雲對象存儲:阿里雲提供的海量、安全、低成本、高可靠的雲存儲服務。支持在任何應用、任何時間、任何地點存儲和訪問任意類型的數據。
  • 分佈式任務調度:提供秒級、精準、高可靠、高可用的定時(基於 Cron 表達式)任務調度服務。同時提供分佈式的任務執行模型,如網格任務。網格任務支持海量子任務均勻分配到所有 Worker(schedulerx-client)上執行。
  • 阿里雲短信服務:覆蓋全球的短信服務,友好、高效、智能的互聯化通訊能力,幫助企業迅速搭建客戶觸達通道。

組件

Sentinel:把流量作為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。

Nacos:一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平臺。

RocketMQ:一款開源的分佈式消息系統,基於高可用分佈式集群技術,提供低延時的、高可靠的消息發佈與訂閱服務。

Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。

Seata:阿里巴巴開源產品,一個易於使用的高性能微服務分佈式事務解決方案。

Alibaba Cloud ACM:一款在分佈式架構環境中對應用配置進行集中管理和推送的應用配置中心產品。

Alibaba Cloud OSS: 阿里雲對象存儲服務(Object Storage Service,簡稱 OSS),是阿里雲提供的海量、安全、低成本、高可靠的雲存儲服務。您可以在任何應用、任何時間、任何地點存儲和訪問任意類型的數據。

Alibaba Cloud SchedulerX: 阿里中間件團隊開發的一款分佈式任務調度產品,提供秒級、精準、高可靠、高可用的定時(基於 Cron 表達式)任務調度服務。

Alibaba Cloud SMS: 覆蓋全球的短信服務,友好、高效、智能的互聯化通訊能力,幫助企業迅速搭建客戶觸達通道。

如何使用

如何引入依賴

如果需要使用已發佈的版本,在 dependencyManagement 中添加如下配置。

<dependencymanagement>
<dependencies>
<dependency>
<groupid>com.alibaba.cloud/<groupid>
<artifactid>spring-cloud-alibaba-dependencies/<artifactid>
<version>2.1.0.RELEASE/<version>
<type>pom/<type>
<scope>import/<scope>
/<dependency>
/<dependencies>
/<dependencymanagement>

然後在 dependencies 中添加自己所需使用的依賴即可使用。

演示 Demo

Sentinel Example

項目說明

本項目演示如何使用 Sentinel starter 完成 Spring Cloud 應用的限流管理。

Sentinel 是阿里巴巴開源的分佈式系統的流量防衛組件,Sentinel 把流量作為切入點,從流量控制,熔斷降級,系統負載保護等多個維度保護服務的穩定性。

示例

如何接入

在啟動示例進行演示之前,我們先了解一下如何接入 Sentinel。

注意:本章節只是為了便於您理解接入方式,本示例代碼中已經完成接入工作,您無需再進行修改。

  1. 首先,修改 pom.xml 文件,引入 Sentinel starter。
<dependency>
<groupid>com.alibaba.cloud/<groupid>
<artifactid>spring-cloud-starter-alibaba-sentinel/<artifactid>
/<dependency>
  1. 接入限流埋點
  • HTTP 埋點
  • Sentinel starter 默認為所有的 HTTP 服務提供了限流埋點,如果只想對 HTTP 服務進行限流,那麼只需要引入依賴,無需修改代碼。
  • 自定義埋點
  • 如果需要對某個特定的方法進行限流或降級,可以通過 @SentinelResource 註解來完成限流的埋點,示例代碼如下:
 @SentinelResource("resource")
public String hello() {
return "Hello";
}
  • 當然也可以通過原始的 SphU.entry(xxx) 方法進行埋點,可以參見 Sentinel 文檔。
  1. 配置限流規則
  2. Sentinel 提供了兩種配置限流規則的方式:代碼配置 和 控制檯配置。本示例使用的方式為通過控制檯配置。
  3. 通過代碼來實現限流規則的配置。一個簡單的限流規則配置示例代碼如下,更多限流規則配置詳情請參考 Sentinel 文檔。
List<flowrule> rules = new ArrayList<flowrule>();
FlowRule rule = new FlowRule();
rule.setResource(str);
// set limit qps to 10
rule.setCount(10);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
/<flowrule>/<flowrule>
  1. 通過控制檯進行限流規則配置請參考文章後面的圖文說明。

啟動 Sentinel 控制檯

  1. 首先需要獲取 Sentinel 控制檯,支持直接下載和源碼構建兩種方式。
  2. 直接下載:下載 Sentinel 控制檯
  3. 源碼構建:進入 Sentinel Github 項目頁面,將代碼 git clone 到本地自行編譯打包,參考此文檔。
  4. 啟動控制檯,執行 Java 命令 java -jar sentinel-dashboard.jar完成 Sentinel 控制檯的啟動。 控制檯默認的監聽端口為 8080。Sentinel 控制檯使用 Spring Boot 編程模型開發,如果需要指定其他端口,請使用 Spring Boot 容器配置的標準方式,詳情請參考 Spring Boot 文檔。

應用啟動

  1. 增加配置,在應用的 /src/main/resources/application.properties 中添加基本配置信息
spring.application.name=sentinel-example
server.port=18083
spring.cloud.sentinel.transport.dashboard=localhost:8080
  1. 啟動應用,支持 IDE 直接啟動和編譯打包後啟動。
  2. IDE直接啟動:找到主類 ServiceApplication,執行 main 方法啟動應用。
  3. 打包編譯後啟動:首先執行 mvn clean package 將工程編譯打包,然後執行 java -jar sentinel-core-example.jar啟動應用。

調用服務

使用 curl 分別調用兩個 URL,可以看到訪問成功。

配置限流規則並驗證

  1. 訪問 http://localhost:8080 頁面,可以在左側看到 Sentinel-Example 應用已經註冊到了控制檯,單擊 流控規則 ,可以看到目前的流控規則為空。

注意:如果您在控制檯沒有找到應用,請調用一下進行了 Sentinel 埋點的 URL 或方法,因為 Sentinel 使用了 lazy load 策略。詳細的排查過程請參見 Sentinel FAQ。


  1. 配置 URL 限流規則:點擊新增流控規則,資源名填寫需要限流的 URL 相對路徑,單機閾值選擇需要限流的閾值,點擊新增進行確認。(為了便於演示效果,這裡將值設置成了 1)。


  1. 配置自定義限流規則:點擊新增流控規則,資源名填寫 @SentinelResource 註解 value 字段的值,單機閾值選擇需要限流的閾值,點擊新增進行確認。(為了便於演示效果,這裡將值設置成了 1)。


  1. 訪問 URL,當 QPS 超過 1 時,可以看到限流效果如下。


自定義限流處理邏輯

  • 默認限流異常處理

URL 限流觸發後默認處理邏輯是,直接返回 "Blocked by Sentinel (flow limiting)"。 如果需要自定義處理邏輯,實現的方式如下:

public class CustomUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
// todo add your logic
}
}
WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler());
  • 使用 @SentinelResource 註解下的限流異常處理

如果需要自定義處理邏輯,填寫 @SentinelResource 註解的 blockHandler 屬性(針對所有類型的 BlockException,需自行判斷)或 fallback 屬性(針對熔斷降級異常),注意對應方法的簽名和位置有限制,詳情見 Sentinel 註解支持文檔。示例實現如下:

public class TestService {
// blockHandler 是位於 ExceptionUtil 類下的 handleException 靜態方法,需符合對應的類型限制.
@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
public void test() {
System.out.println("Test");
}
// blockHandler 是位於當前類下的 exceptionHandler 方法,需符合對應的類型限制.
@SentinelResource(value = "hello", blockHandler = "exceptionHandler")
public String hello(long s) {
return String.format("Hello at %d", s);
}
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
}
public final class ExceptionUtil {
public static void handleException(BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
}
}

一個簡單的 @SentinelResource 示例可以見 sentinel-demo-annotation-spring-aop。

Endpoint 信息查看

Spring Boot 應用支持通過 Endpoint 來暴露相關信息,Sentinel Starter 也支持這一點。

在使用之前需要在 Maven 中添加 spring-boot-starter-actuator依賴,並在配置中允許 Endpoints 的訪問。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通過訪問 http://127.0.0.1:18083/sentinel 來查看 Sentinel Endpoint 的信息。Spring Boot 2.x 可以通過訪問 http://127.0.0.1:18083/actuator/sentinel 來訪問。



查看實時監控

Sentinel 控制檯支持實時監控查看,您可以通過 Sentinel 控制檯查看各鏈路的請求的通過數和被限流數等信息。 其中 p_qps 為通過(pass) 流控的 QPS,b_qps 為被限流 (block) 的 QPS。



ReadableDataSource 支持

Sentinel 內部提供了動態規則的擴展實現 ReadableDataSource。

Sentinel starter 整合了目前存在的幾類 ReadableDataSource。只需要在配置文件中進行相關配置,即可在 Spring 容器中自動註冊 DataSource。

比如要定義兩個ReadableDataSource,分別是 FileRefreshableDataSource 和 NacosDataSource,配置如下:

spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json 

spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json

ds1 和 ds2 表示ReadableDataSource的名稱,可隨意編寫。ds1 和 ds2 後面的 file 和 nacos 表示ReadableDataSource的類型。

目前支持file, nacos, zk, apollo,redis 這5種類型。

其中nacos,zk,apollo,redis 這4種類型的使用需要加上對應的依賴sentinel-datasource-nacos, sentinel-datasource-zookeeper, sentinel-datasource-apollo, sentinel-datasource-redis。

當ReadableDataSource加載規則數據成功的時候,控制檯會打印出相應的日誌信息:

[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule
[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule

Nacos Config Example

項目說明

本項目演示如何使用 Nacos Config Starter 完成 Spring Cloud 應用的配置管理。

Nacos 是阿里巴巴開源的一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平臺。

示例

如何接入

在啟動示例進行演示之前,我們先了解一下 Spring Cloud 應用如何接入 Nacos Config。 注意 本章節只是為了便於您理解接入方式,本示例代碼中已經完成接入工作,您無需再進行修改。

  1. 首先,修改 pom.xml 文件,引入 Nacos Config Starter。
 <dependency>
<groupid>com.alibaba.cloud/<groupid>
<artifactid>spring-cloud-starter-alibaba-nacos-config/<artifactid>
/<dependency>
  1. 在應用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 元數據
 spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
  1. 完成上述兩步後,應用會從 Nacos Config 中獲取相應的配置,並添加在 Spring Environment 的 PropertySources 中。這裡我們使用 @Value 註解來將對應的配置注入到 SampleController 的 userName 和 age 字段,並添加 @RefreshScope 打開動態刷新功能
 @RefreshScope
class SampleController {
\t@Value("${user.name}")
\tString userName;
\t@Value("${user.age}")
\tint age;
}

啟動 Nacos Server 並添加配置

  1. 首先需要獲取 Nacos Server,支持直接下載和源碼構建兩種方式。推薦使用最新版本 Nacos Server
  2. 直接下載:Nacos Server 下載頁
  3. 源碼構建:進入 Nacos Github 項目頁面,將代碼 git clone 到本地自行編譯打包,參考此文檔。
  4. 啟動 Server,進入下載到本地並解壓完成後的文件夾(使用源碼構建的方式則進入編譯打包好的文件夾),再進去其相對文件夾 nacos/bin,並對照操作系統實際情況執行如下命令。詳情參考此文檔。
  5. Linux/Unix/Mac 操作系統,執行命令 sh startup.sh -m standalone
  6. Windows 操作系統,執行命令 cmd startup.cmd
  7. 在命令行執行如下命令,向 Nacos Server 中添加一條配置。
 curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos-config-example.properties&group=DEFAULT_GROUP&content=user.id=1%0Auser.name=james%0Auser.age=17"
  1. 注:你也可以使用其他方式添加,遵循 HTTP API 規範即可,若您使用的 Nacos 版本自帶控制檯,建議直接使用控制檯進行配置
  2. 添加的配置的詳情如下
 dataId 為 nacos-config-example.properties
group 為 DEFAULT_GROUP

內容如下

\tuser.id=1
\tuser.name=james
\tuser.age=17\t

應用啟動

  1. 增加配置,在應用的 /src/main/resources/application.properties 中添加基本配置信息
 server.port=18084
management.endpoints.web.exposure.include=*
  1. 啟動應用,支持 IDE 直接啟動和編譯打包後啟動。
  2. IDE直接啟動:找到主類 Application,執行 main 方法啟動應用。
  3. 打包編譯後啟動:首先執行 mvn clean package 將工程編譯打包,然後執行 java -jar nacos-config-example.jar啟動應用。

驗證

驗證自動注入

在瀏覽器地址欄輸入 http://127.0.0.1:18084/user,並點擊調轉,可以看到成功從 Nacos Config Server 中獲取了數據。

Spring Cloud Alibaba微服務開發的一站式解決方案

驗證動態刷新

  1. 執行如下命令,修改 Nacos Server 端的配置數據
 curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos-config-example.properties&group=DEFAULT_GROUP&content=user.id=1%0Auser.name=james%0Auser.age=18"
  1. 在瀏覽器地址欄輸入 http://127.0.0.1:18084/user,並點擊調轉,可以看到應用從 Nacos Server 中獲取了最新的數據,age 變成了 18。
Spring Cloud Alibaba微服務開發的一站式解決方案

原理

Nacos Config 數據結構

Nacos Config 主要通過 dataId 和 group 來唯一確定一條配置,我們假定你已經瞭解此背景。如果不瞭解,請參考 Nacos 文檔。

Nacos Client 從 Nacos Server 端獲取數據時,調用的是此接口 ConfigService.getConfig(String dataId, String group, long timeoutMs)。

Spring Cloud 應用獲取數據

dataID

在 Nacos Config Starter 中,dataId 的拼接格式如下

${prefix} - ${spring.profiles.active} . ${file-extension}
  • prefix 默認為 spring.application.name 的值,也可以通過配置項 spring.cloud.nacos.config.prefix來配置。
  • spring.profiles.active 即為當前環境對應的 profile,詳情可以參考 Spring Boot文檔
  • 注意,當 activeprofile 為空時,對應的連接符 - 也將不存在,dataId 的拼接格式變成 ${prefix}.${file-extension}
  • file-extension 為配置內容的數據格式,可以通過配置項 spring.cloud.nacos.config.file-extension來配置。 目前只支持 properties 類型。

group

  • group 默認為 DEFAULT_GROUP,可以通過 spring.cloud.nacos.config.group 配置。

自動注入

Nacos Config Starter 實現了 org.springframework.cloud.bootstrap.config.PropertySourceLocator接口,並將優先級設置成了最高。

在 Spring Cloud 應用啟動階段,會主動從 Nacos Server 端獲取對應的數據,並將獲取到的數據轉換成 PropertySource 且注入到 Environment 的 PropertySources 屬性中,所以使用 @Value 註解也能直接獲取 Nacos Server 端配置的內容。

動態刷新

Nacos Config Starter 默認為所有獲取數據成功的 Nacos 的配置項添加了監聽功能,在監聽到服務端配置發生變化時會實時觸發 org.springframework.cloud.context.refresh.ContextRefresher 的 refresh 方法 。

如果需要對 Bean 進行動態刷新,請參照 Spring 和 Spring Cloud 規範。推薦給類添加 @RefreshScope 或 @ConfigurationProperties 註解,

更多詳情請參考 ContextRefresher Java Doc。

Endpoint 信息查看

Spring Boot 應用支持通過 Endpoint 來暴露相關信息,Nacos Config Starter 也支持這一點。

在使用之前需要在 maven 中添加 spring-boot-starter-actuator依賴,並在配置中允許 Endpoints 的訪問。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通過訪問 http://127.0.0.1:18084/nacos_config 來查看 Nacos Endpoint 的信息。

Spring Boot 2.x 可以通過訪問 http://127.0.0.1:18084/actuator/nacos-config 來訪問。

Spring Cloud Alibaba微服務開發的一站式解決方案

如上圖所示,Sources 表示此客戶端從哪些 Nacos Config 配置項中獲取了信息,RefreshHistory 表示動態刷新的歷史記錄,最多保存20條,NacosConfigProperties 則為 Nacos Config Starter 本身的配置。

Nacos Discovery Example

項目說明

本項目演示如何使用 Nacos Discovery Starter 完成 Spring Cloud 應用的服務註冊與發現。

Nacos 是阿里巴巴開源的一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平臺。

示例

如何接入

在啟動示例進行演示之前,我們先了解一下 Spring Cloud 應用如何接入 Nacos Discovery。 注意 本章節只是為了便於您理解接入方式,本示例代碼中已經完成接入工作,您無需再進行修改。

  1. 首先,修改 pom.xml 文件,引入 Nacos Discovery Starter。
 <dependency>
<groupid>com.alibaba.cloud/<groupid>
<artifactid>spring-cloud-starter-alibaba-nacos-discovery/<artifactid>
/<dependency>
  1. 在應用的 /src/main/resources/application.properties 配置文件中配置 Nacos Server 地址
 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
  1. 使用 @EnableDiscoveryClient 註解開啟服務註冊與發現功能
 @SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
\tpublic static void main(String[] args) {
\t\tSpringApplication.run(Application.class, args);

\t}
\t@RestController
\tclass EchoController {
\t\t@RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
\t\tpublic String echo(@PathVariable String string) {
\t\t\t\treturn string;
\t\t}
\t}
}

啟動 Nacos Server

  1. 首先需要獲取 Nacos Server,支持直接下載和源碼構建兩種方式。
  2. 直接下載:Nacos Server 下載頁
  3. 源碼構建:進入 Nacos Github 項目頁面,將代碼 git clone 到本地自行編譯打包,參考此文檔。推薦使用源碼構建方式以獲取最新版本
  4. 啟動 Server,進入解壓後文件夾或編譯打包好的文件夾,找到如下相對文件夾 nacos/bin,並對照操作系統實際情況之下如下命令。
  5. Linux/Unix/Mac 操作系統,執行命令 sh startup.sh -m standalone
  6. Windows 操作系統,執行命令 cmd startup.cmd

應用啟動

  1. 增加配置,在 nacos-discovery-provider-example 項目的 /src/main/resources/application.properties 中添加基本配置信息
 spring.application.name=service-provider
server.port=18082
  1. 啟動應用,支持 IDE 直接啟動和編譯打包後啟動。
  2. IDE直接啟動:找到 nacos-discovery-provider-example 項目的主類 ProviderApplication,執行 main 方法啟動應用。
  3. 打包編譯後啟動:在 nacos-discovery-provider-example 項目中執行 mvn clean package 將工程編譯打包,然後執行 java -jar nacos-discovery-provider-example.jar啟動應用。

驗證

查詢服務

在瀏覽器輸入此地址 http://127.0.0.1:8848/nacos/v1/ns/catalog/instances?serviceName=service-provider&clusterName=DEFAULT&pageSize=10&pageNo=1&namespaceId=,並點擊跳轉,可以看到服務節點已經成功註冊到 Nacos Server。

Spring Cloud Alibaba微服務開發的一站式解決方案

服務發現

集成 Ribbon

為了便於使用,NacosServerList 實現了 com.netflix.loadbalancer.ServerList 接口,並在 @ConditionOnMissingBean 的條件下進行自動注入。如果您有定製化的需求,可以自己實現自己的 ServerList。

Nacos Discovery Starter 默認集成了 Ribbon ,所以對於使用了 Ribbon 做負載均衡的組件,可以直接使用 Nacos 的服務發現。

使用 RestTemplate 和 FeignClient

下面將分析 nacos-discovery-consumer-example 項目的代碼,演示如何 RestTemplate 與 FeignClient。

注意 本章節只是為了便於您理解接入方式,本示例代碼中已經完成接入工作,您無需再進行修改。此處只涉及Ribbon、RestTemplate、FeignClient相關的內容,如果已經使用了其他服務發現組件,可以通過直接替換依賴來接入 Nacos Discovery。

  1. 添加 @LoadBlanced 註解,使得 RestTemplate 接入 Ribbon
 @Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
  1. FeignClient 已經默認集成了 Ribbon ,此處演示如何配置一個 FeignClient。
 @FeignClient(name = "service-provider")
public interface EchoService {
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
String echo(@PathVariable("str") String str);
}
  1. 使用 @FeignClient 註解將 EchoService 這個接口包裝成一個 FeignClient,屬性 name 對應服務名 service-provider。
  2. echo 方法上的 @RequestMapping 註解將 echo 方法與 URL "/echo/{str}" 相對應,@PathVariable 註解將 URL 路徑中的 {str} 對應成 echo 方法的參數 str。
  3. 完成以上配置後,將兩者自動注入到 TestController 中。
 @RestController
public class TestController {

@Autowired
private RestTemplate restTemplate;
@Autowired
private EchoService echoService;

@RequestMapping(value = "/echo-rest/{str}", method = RequestMethod.GET)
public String rest(@PathVariable String str) {
return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
}
@RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET)
public String feign(@PathVariable String str) {
return echoService.echo(str);
}
}
  1. 配置必要的配置,在 nacos-discovery-consumer-example 項目的 /src/main/resources/application.properties 中添加基本配置信息
 spring.application.name=service-consumer 

server.port=18083
  1. 啟動應用,支持 IDE 直接啟動和編譯打包後啟動。
  2. IDE直接啟動:找到 nacos-discovery-consumer-example 項目的主類 ConsumerApplication,執行 main 方法啟動應用。
  3. 打包編譯後啟動:在 nacos-discovery-consumer-example 項目中執行 mvn clean package 將工程編譯打包,然後執行 java -jar nacos-discovery-consumer-example.jar啟動應用。

驗證

  1. 在瀏覽器地址欄中輸入 http://127.0.0.1:18083/echo-rest/1234,點擊跳轉,可以看到瀏覽器顯示了 nacos-discovery-provider-example 返回的消息 "hello Nacos Discovery 1234",證明服務發現生效。
Spring Cloud Alibaba微服務開發的一站式解決方案

  1. 在瀏覽器地址欄中輸入 http://127.0.0.1:18083/echo-feign/12345,點擊跳轉,可以看到瀏覽器顯示 nacos-discovery-provider-example 返回的消息 "hello Nacos Discovery 12345",證明服務發現生效。
Spring Cloud Alibaba微服務開發的一站式解決方案

原理

服務註冊

Spring Cloud Nacos Discovery 遵循了 spring cloud common 標準,實現了 AutoServiceRegistration、ServiceRegistry、Registration 這三個接口。

在 spring cloud 應用的啟動階段,監聽了 WebServerInitializedEvent 事件,當Web容器初始化完成後,即收到 WebServerInitializedEvent 事件後,會觸發註冊的動作,調用 ServiceRegistry 的 register 方法,將服務註冊到 Nacos Server。

服務發現

NacosServerList 實現了 com.netflix.loadbalancer.ServerList 接口,並在 @ConditionOnMissingBean 的條件下進行自動注入,默認集成了Ribbon。

如果需要有更加自定義的可以使用 @Autowired 注入一個 NacosRegistration 實例,通過其持有的 NamingService 字段內容直接調用 Nacos API。

Endpoint 信息查看

Spring Boot 應用支持通過 Endpoint 來暴露相關信息,Nacos Discovery Starter 也支持這一點。

在使用之前需要在 maven 中添加 spring-boot-starter-actuator依賴,並在配置中允許 Endpoints 的訪問。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通過訪問 http://127.0.0.1:18083/nacos_discovery 來查看 Nacos Endpoint 的信息。

Spring Boot 2.x 可以通過訪問 http://127.0.0.1:18083/actuator/nacos-discovery 來訪問。

如上圖所示,NacosDiscoveryProperties 則為 Spring Cloud Nacos Discovery 本身的配置,也包括本機註冊的內容,subscribe 為本機已訂閱的服務信息。

RocketMQ Example

項目說明

本項目演示如何使用 RocketMQ Binder 完成 Spring Cloud 應用消息的訂閱和發佈。

RocketMQ 是一款開源的分佈式消息系統,基於高可用分佈式集群技術,提供低延時的、高可靠的消息發佈與訂閱服務。

在說明 RocketMQ 的示例之前,我們先了解一下 Spring Cloud Stream。

這是官方對 Spring Cloud Stream 的一段介紹:

Spring Cloud Stream 是一個用於構建基於消息的微服務應用框架。它基於 SpringBoot 來創建具有生產級別的單機 Spring 應用,並且使用 Spring Integration 與 Broker 進行連接。

Spring Cloud Stream 提供了消息中間件配置的統一抽象,推出了 publish-subscribe、consumer groups、partition 這些統一的概念。

Spring Cloud Stream 內部有兩個概念:Binder 和 Binding。

  • Binder: 跟外部消息中間件集成的組件,用來創建 Binding,各消息中間件都有自己的 Binder 實現。

比如 Kafka 的實現 KafkaMessageChannelBinder,RabbitMQ 的實現 RabbitMessageChannelBinder 以及 RocketMQ 的實現 RocketMQMessageChannelBinder。

  • Binding: 包括 Input Binding 和 Output Binding。

Binding 在消息中間件與應用程序提供的 Provider 和 Consumer 之間提供了一個橋樑,實現了開發者只需使用應用程序的 Provider 或 Consumer 生產或消費數據即可,屏蔽了開發者與底層消息中間件的接觸。

下圖是 Spring Cloud Stream 的架構設計。

Spring Cloud Alibaba微服務開發的一站式解決方案

示例

如何接入

在啟動示例進行演示之前,我們先了解一下 Spring Cloud 應用如何接入 RocketMQ Binder。

注意:本章節只是為了便於您理解接入方式,本示例代碼中已經完成****接入工作,您無需再進行修改。

  1. 首先,修改 pom.xml 文件,引入 RocketMQ Stream Starter。
<dependency>
<groupid>com.alibaba.cloud/<groupid>
<artifactid>spring-cloud-starter-stream-rocketmq/<artifactid>
/<dependency>
  1. 配置 Input 和 Output 的 Binding 信息並配合 @EnableBinding 註解使其生效
@SpringBootApplication
@EnableBinding({ Source.class, Sink.class })
public class RocketMQApplication {
\tpublic static void main(String[] args) {
\t\tSpringApplication.run(RocketMQApplication.class, args);
\t}
}

配置 Binding 信息:

# 配置rocketmq的nameserver地址
spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876
# 定義name為output的binding
spring.cloud.stream.bindings.output.destination=test-topic
spring.cloud.stream.bindings.output.content-type=application/json
# 定義name為input的binding
spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=application/json
spring.cloud.stream.bindings.input.group=test-group
  1. 消息發送及消息訂閱

下載並啟動 RocketMQ

在接入 RocketMQ Binder 之前,首先需要啟動 RocketMQ 的 Name Server 和 Broker。

  1. 下載RocketMQ最新的二進制文件,並解壓
  2. 啟動 Name Server
sh bin/mqnamesrv
  1. 啟動 Broker
sh bin/mqbroker -n localhost:9876
  1. 創建 Topic: test-topic
sh bin/mqadmin updateTopic -n localhost:9876 -c DefaultCluster -t test-topic

應用啟動

  1. 增加配置,在應用的 /src/main/resources/application.properties 中添加基本配置信息
spring.application.name=rocketmq-example
server.port=28081
  1. 啟動應用,支持 IDE 直接啟動和編譯打包後啟動。
  2. IDE 直接啟動:找到主類 RocketMQApplication,執行 main 方法啟動應用。
  3. 打包編譯後啟動:首先執行 mvn clean package 將工程編譯打包,然後執行 java -jar rocketmq-example.jar 啟動應用。

消息處理

使用 name 為 output 對應的 binding 發送消息到 test-topic 這個 topic。

使用2個 input binding 訂閱數據。

  • input1: 訂閱 topic 為 test-topic 的消息,順序消費所有消息(順序消費的前提是所有消息都在一個 MessageQueue 中)
  • input2: 訂閱 topic 為 test-topic 的消息,異步消費 tags 為 tagStr 的消息,Consumer 端線程池個數為20

配置信息如下:

spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876
spring.cloud.stream.bindings.output.destination=test-topic
spring.cloud.stream.bindings.output.content-type=application/json
spring.cloud.stream.bindings.input1.destination=test-topic
spring.cloud.stream.bindings.input1.content-type=text/plain
spring.cloud.stream.bindings.input1.group=test-group1
spring.cloud.stream.rocketmq.bindings.input1.consumer.orderly=true
spring.cloud.stream.bindings.input2.destination=test-topic
spring.cloud.stream.bindings.input2.content-type=text/plain
spring.cloud.stream.bindings.input2.group=test-group2
spring.cloud.stream.rocketmq.bindings.input2.consumer.orderly=false
spring.cloud.stream.rocketmq.bindings.input2.consumer.tags=tagStr
spring.cloud.stream.bindings.input2.consumer.concurrency=20

消息發送

使用 MessageChannel 進行消息發送:

public class ProducerRunner implements CommandLineRunner {
@Autowired
private MessageChannel output; // 獲取name為output的binding
@Override
public void run(String... args) throws Exception {

Map<string> headers = new HashMap<>();
headers.put(MessageConst.PROPERTY_TAGS, "tagStr");
Message message = MessageBuilder.createMessage(msg, new MessageHeaders(headers));
output.send(message);
}
}
/<string>

或者使用 RocketMQ 原生的 API 進行消息發送:

public class RocketMQProducer {
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();

Message msg = new Message("test-topic", "tagStr", "message from rocketmq producer".getBytes());
producer.send(msg);
}

消息接收

使用 @StreamListener 註解接收消息:

@Service
public class ReceiveService {
\t@StreamListener("input1")
\tpublic void receiveInput1(String receiveMsg) {
\t\tSystem.out.println("input1 receive: " + receiveMsg);
\t}
\t@StreamListener("input2")
\tpublic void receiveInput2(String receiveMsg) {
\t\tSystem.out.println("input2 receive: " + receiveMsg);
\t}
}

Endpoint 信息查看

Spring Boot 應用支持通過 Endpoint 來暴露相關信息,RocketMQ Stream Starter 也支持這一點。

在使用之前需要在 Maven 中添加 spring-boot-starter-actuator依賴,並在配置中允許 Endpoints 的訪問。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false
  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*

Spring Boot 1.x 可以通過訪問 http://127.0.0.1:18083/rocketmq_binder 來查看 RocketMQ Binder Endpoint 的信息。Spring Boot 2.x 可以通過訪問 http://127.0.0.1:28081/actuator/rocketmq-binder 來訪問。

這裡會統計消息最後一次發送的數據,消息發送成功或失敗的次數,消息消費成功或失敗的次數等數據。

{
\t"runtime": {
\t\t"lastSend.timestamp": 1542786623915
\t},
\t"metrics": {
\t\t"scs-rocketmq.consumer.test-topic.totalConsumed": {
\t\t\t"count": 11
\t\t},
\t\t"scs-rocketmq.consumer.test-topic.totalConsumedFailures": {
\t\t\t"count": 0
\t\t},
\t\t"scs-rocketmq.producer.test-topic.totalSentFailures": {
\t\t\t"count": 0
\t\t},
\t\t"scs-rocketmq.consumer.test-topic.consumedPerSecond": {
\t\t\t"count": 11,
\t\t\t"fifteenMinuteRate": 0.012163847780107841,
\t\t\t"fiveMinuteRate": 0.03614605351360527,
\t\t\t"meanRate": 0.3493213353657594,
\t\t\t"oneMinuteRate": 0.17099243039490175
\t\t},
\t\t"scs-rocketmq.producer.test-topic.totalSent": {
\t\t\t"count": 5
\t\t},
\t\t"scs-rocketmq.producer.test-topic.sentPerSecond": {
\t\t\t"count": 5,
\t\t\t"fifteenMinuteRate": 0.005540151995103271,
\t\t\t"fiveMinuteRate": 0.01652854617838251,
\t\t\t"meanRate": 0.10697493212602836,
\t\t\t"oneMinuteRate": 0.07995558537067671
\t\t},
\t\t"scs-rocketmq.producer.test-topic.sentFailuresPerSecond": {
\t\t\t"count": 0,

\t\t\t"fifteenMinuteRate": 0.0,
\t\t\t"fiveMinuteRate": 0.0,
\t\t\t"meanRate": 0.0,
\t\t\t"oneMinuteRate": 0.0
\t\t},
\t\t"scs-rocketmq.consumer.test-topic.consumedFailuresPerSecond": {
\t\t\t"count": 0,
\t\t\t"fifteenMinuteRate": 0.0,
\t\t\t"fiveMinuteRate": 0.0,
\t\t\t"meanRate": 0.0,
\t\t\t"oneMinuteRate": 0.0
\t\t}
\t}
}

注意:要想查看統計數據需要在pom里加上 metrics-core依賴。如若不加,endpoint 將會顯示 warning 信息而不會顯示統計信息:

{
"warning": "please add metrics-core dependency, we use it for metrics"
}

More

RocketMQ 是一款功能強大的分佈式消息系統,廣泛應用於多個領域,包括異步通信解耦、企業解決方案、金融支付、電信、電子商務、快遞物流、廣告營銷、社交、即時通信、移動應用、手遊、視頻、物聯網、車聯網等。

Seata Example

項目說明

本項目演示如何使用 Seata Starter 完成 Spring Cloud 應用的分佈式事務接入。

Seata 是 阿里巴巴 開源的 分佈式事務中間件,以 高效 並且對業務 0 侵入 的方式,解決 微服務 場景下面臨的分佈式事務問題。

準備工作

在運行此示例之前,你需要先完成如下幾步準備工作:

  1. 配置數據庫
  2. 創建 UNDO_LOG 表
  3. 創建 示例中 業務所需要的數據庫表
  4. 啟動 Seata Server

配置數據庫

首先,你需要有一個支持 InnoDB 引擎的 MySQL 數據庫。

注意: 實際上,Seata 支持不同的應用使用完全不相干的數據庫,但是這裡為了簡單地演示一個原理,所以我們選擇了只使用一個數據庫。

將 account-server、order-service、storage-service 這三個應用中的 resources 目錄下的 application.properties 文件中的如下配置修改成你運行環境中的實際配置。

mysql.server.ip=your mysql server ip address
mysql.server.port=your mysql server listening port
mysql.db.name=your database name for test
mysql.user.name=your mysql server username
mysql.user.password=your mysql server password

創建 undo_log 表

Seata AT 模式 需要使用到 undo_log 表。

-- 注意此處0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

創建 示例中 業務所需要的數據庫表

DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

啟動 Seata Server

點擊這個頁面 https://github.com/seata/seata/releases,下載最新版本的 Seata Server 端.

進入解壓之後的 bin 目錄,執行如下命令來啟動

sh seata-server.sh -p $LISTEN_PORT -m $MODE(file or db)

在這個示例中,採用如下命令來啟動 Seata Server

sh seata-server.sh -p 8091 -m file

注意 如果你修改了endpoint且註冊中心使用默認file類型,那麼記得需要在各個示例工程中的 file.conf 文件中,修改 grouplist 的值(當registry.conf 中registry.type 或 config.type 為file 時會讀取內部的file節點中的文件名,若type不為file將直接從配置類型的對應元數據的註冊配置中心讀取數據),推薦大家使用 nacos 作為配置註冊中心。

運行示例

分別運行 account-server、order-service、storage-service 和 business-service 這三個應用的 Main 函數,啟動示例。

啟動示例後,通過 HTTP 的 GET 方法訪問如下兩個 URL,可以分別驗證在 business-service 中 通過 RestTemplate 和 FeignClient 調用其他服務的場景。

http://127.0.0.1:18081/seata/feign
http://127.0.0.1:18081/seata/rest

如何驗證分佈式事務成功?

Xid 信息是否成功傳遞

在 account-server、order-service 和 storage-service 三個 服務的 Controller 中,第一個執行的邏輯都是輸出 RootContext 中的 Xid 信息,如果看到都輸出了正確的 Xid 信息,即每次都發生變化,且同一次調用中所有服務的 Xid 都一致。則表明 Seata 的 Xid 的傳遞和還原是正常的。

數據庫中數據是否一致

在本示例中,我們模擬了一個用戶購買貨物的場景,StorageService 負責扣減庫存數量,OrderService 負責保存訂單,AccountService 負責扣減用戶賬戶餘額。

為了演示樣例,我們在 OrderService 和 AccountService 中 使用 Random.nextBoolean() 的方式來隨機拋出異常,模擬了在服務調用時隨機發生異常的場景。

如果分佈式事務生效的話, 那麼以下等式應該成立

  • 用戶原始金額(1000) = 用戶現存的金額 + 貨物單價 (2) * 訂單數量 * 每單的貨物數量(2)
  • 貨物的初始數量(100) = 貨物的現存數量 + 訂單數量 * 每單的貨物數量(2)

對 Spring Cloud 支持點

  • 通過 Spring MVC 提供服務的服務提供者,在收到 header 中含有 Seata 信息的 HTTP 請求時,可以自動還原 Seata 上下文。
  • 支持服務調用者通過 RestTemplate 調用時,自動傳遞 Seata 上下文。
  • 支持服務調用者通過 FeignClient 調用時,自動傳遞 Seata 上下文。
  • 支持 SeataClient 和 Hystrix 同時使用的場景。
  • 支持 SeataClient 和 Sentinel 同時使用的場景。

私信回覆"Spring Cloud Alibaba"獲取鏈接地址,喜歡的點個關注,一起學習探討新技術。


分享到:


相關文章: