alpha是什么:
編譯
獲取源碼:
$ git clone https://github.com/apache/servicecomb-pack.git
$ cd servicecomb-pack
Saga可通過以下任一方式進行構建:
-
只構建可執行文件:
$ mvn clean install -DskipTests
-
同時構建可執行文件和docker鏡像:
$ mvn clean install -DskipTests -Pdocker
-
同時構建可執行文件以及Saga發行包
$ mvn clean install -DskipTests -Prelease
在執行以上任一指令后,可在alpha/alpha-server/target/saga/alpha-server-${version}-exec.jar
中找到alpha server的可執行文件。
如何使用
引入Pack的依賴
<dependency>
<groupId>org.apache.servicecomb.pack</groupId>
<artifactId>omega-spring-starter</artifactId>
<version>${pack.version}</version>
</dependency>
<dependency>
<groupId>org.apache.servicecomb.pack</groupId>
<artifactId>omega-transport-resttemplate</artifactId>
<version>${pack.version}</version>
</dependency>
注意: 請將${pack.version}
更改為實際的版本號。版本遷移提示: 從0.3.0 開始,整個項目的代碼庫名由servicecomb-saga改名為servicecomb-pack, 與此同時我們也更新了對應發布包的組名以及相關包名。如果你的項目是從saga 0.2.x 遷移過來,請按照下表所示進行修改。
name | 0.2.x | 0.3.x |
---|---|---|
groupId | org.apache.servicecomb.saga | org.apache.servicecomb.pack |
Package Name | org.apache.servicecomb.saga | org.apache.servicecomb.pack |
Saga 支持
添加Saga的注解及相應的補償方法以一個轉賬應用為例:
-
在全局事務的起點添加
@SagaStart
的注解。import org.apache.servicecomb.pack.omega.context.annotations.SagaStart;
timeout=10) (
public boolean transferMoney(String from, String to, int amount) {
transferOut(from, amount);
transferIn(to, amount);
}注意: 默認情況下,超時設置需要顯式聲明才生效。
-
在子事務處添加
@Compensable
的注解並指明其對應的補償方法。import javax.transaction.Transactional;
import org.apache.servicecomb.pack.omega.transaction.annotations.Compensable;
timeout=5, compensationMethod="cancel") (
public boolean transferOut(String from, int amount) {
repo.reduceBalanceByUsername(from, amount);
}
public boolean cancel(String from, int amount) {
repo.addBalanceByUsername(from, amount);
}注意: 實現的服務使用相當的參數,實現的服務和補償必須滿足冪等的條件,同時建議使用Spring @Transactional標注提供本地事務保證。
注意: 默認情況下,超時設置需要顯式聲明才生效。
注意: 若全局事務起點與子事務起點重合,需同時聲明
@SagaStart
和@Compensable
的注解。 -
增加 alpha.cluster.address 參數
alpha
cluster
address alpha-server.servicecomb.io8080 -
對轉入服務重復第二步即可。
-
從pack-0.3.0開始, 你可以在服務函數或者取消函數中通過訪問 OmegaContext 來獲取 gloableTxId 以及 localTxId 信息。
<a name="explicit-tx-context-passing"></a>顯式傳遞事務上下文
在一般情況下,Omega能夠替你處理事務上下文的傳遞工作(詳情見服務間通信流程),因此你的代碼並不需要知道事務上下文的存在。而事務上下文的傳遞實際上是通過在請求方注入、在接受方提取來完成的,下面舉一個請例子來說明這個過程:
Service A:
@SagaStart public void foo() { restTemplate.postForEntity("http://service-b/bar", ...); }
Service B:
@GetMapping("/bar") @Compensable public void bar() { ... }
我們可以先來看看Omega是怎么傳遞事務上下文的:
-
Service A的foo方法會開啟一個新的全局事務。
-
TransactionClientHttpRequestInterceptor會在RestTemplate請求Service B時在Http請求頭中注入事務上下文信息。
-
當Servce B接收到請求時,TransactionHandlerInterceptor會從請求頭中提取事務上下文信息。
目前Omega支持以下形式的隱式事務上下文傳遞:
-
omega-transport-{dubbo,feign,resttemplate,servicecomb}。
-
同線程內調用(基於OmegaContext的ThreadLocal字段)。
-
標注了@OmegaContextAware的java.util.concurrent.Executor{Service}。
那么問題來了,如果無法隱式傳遞事務上下文怎么辦?比如Service A使用某種RPC機制件來調用Service B,而你又沒有辦法注入或提取事務上下文信息。這個時候你只能采用顯式的方式把事務上下文傳遞出去。ServiceComb Pack從0.5.0開始提供了兩個類來實現這一點。
利用TransactionContext傳遞
Service A:
@SagaStart public void foo(BarCommand cmd) { TransactionContext localTxContext = omegaContext.getTransactionContext(); someRpc.send(cmd, localTxContext); }
Service B:
public void listen(BarCommand cmd, TransactionContext injectedTxContext) { bar(cmd, injectedTxContext); } @Compensable public void bar(BarCommand cmd, TransactionContext injectedTxContext) { ... // TransactionContext localTxContext = omegaContext.getTransactionContext(); }
需要注意的是bar
方法接收到的是注入的事務上下文,在進入bar
之后從OmegaContext得到的是本地事務上下文(Omega替你開啟了新的事務)。如果Service B也需要顯式地傳遞事務上下文,那么應該使用本地事務上下文。
利用TransactionContextProperties傳遞
Service A:
public class BarCommand {} public class BarCommandWithTxContext extends BarCommand implements TransactionContextProperties { // setter getter for globalTxId // setter getter for localTxId } @SagaStart public void foo(BarCommand cmd) { BarCommandWithTxContext cmdWithTxContext = new BarCommandWithTxContext(cmd); cmdWithTxContext.setGlobalTxId(omegaContext.globalTxId()); cmdWithTxContext.setLocalTxId(omegaContext.localTxId()); someRpc.send(cmdWithTxContext); }
Service B:
public void listen(BarCommandWithTxContext cmdWithTxContext) { bar(cmdWithTxContext); } @Compensable public void bar(BarCommandWithTxContext cmdWithTxContext) { ... // TransactionContext localTxContext = omegaContext.getTransactionContext(); }
和前面一種方式類似,TransactionContextProperties.get{Global,Local}TxId()返回的也是注入的事務上下文信息。
TCC 支持
在對應的方法中添加TccStart 和 Participate標注 以一個轉賬應用為例:
-
在全局事務的起點添加
@TccStart
的注解。import org.apache.servicecomb.pack.omega.context.annotations.TccStart; @TccStart public boolean transferMoney(String from, String to, int amount) { transferOut(from, amount); transferIn(to, amount); }
Note: 當前TCC還不支持Timeout
-
在子事務嘗試方法處添加
@Participate
的注解並指明其對應的執行以及補償方法名,import javax.transaction.Transactional; import org.apache.servicecomb.pack.omega.transaction.annotations.Participate; @Participate(confirmMethod = "confirm", cancelMethod = "cancel") @Transactional public void transferOut(String from, int amount) { // check banalance } @Transactional public void confirm(String from, int amount) { repo.reduceBalanceByUsername(from, amount); } @Transactional public void cancel(String from, int amount) { repo.addBalanceByUsername(from, amount); }
注意:
confirm
和cancel
方法的參數列表應該與@Participate
方法一樣,並且它們必須是冪等。我們強烈建議使用Spring的@Transactional
來保證本地事務的一致性。注意: 若全局事務起點與子事務起點重合,需同時聲明
@TccStart
和@Participate
的注解。 -
增加 alpha.cluster.address 參數
alpha: cluster: address: alpha-server.servicecomb.io:8080
-
對轉入服務重復第二步即可。
-
從pack-0.3.0開始, 你可以在服務函數或者取消函數中通過訪問 OmegaContext 來獲取 gloableTxId 以及 localTxId 信息。
顯式傳遞事務上下文
與Saga的@Compensable
一樣,TCC的@Participate
也支持顯式傳遞事務上下文,詳情可參閱Saga章節。
如何運行
-
運行postgreSQL,
docker run -d -e "POSTGRES_DB=saga" -e "POSTGRES_USER=saga" -e "POSTGRES_PASSWORD=password" -p 5432:5432 postgres
如果你想使用MySQL做為后台數據庫,可以參考 此文檔。
-
運行alpha。在運行alpha前,請確保postgreSQL已正常啟動。可通過docker或可執行文件的方式來啟動alpha。
-
通過docker運行:
docker run -d -p 8080:8080 -p 8090:8090 -e "JAVA_OPTS=-Dspring.profiles.active=prd -Dspring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false" alpha-server:${saga_version}
-
通過可執行文件運行:
java -Dspring.profiles.active=prd -D"spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false" -jar alpha-server-${saga_version}-exec.jar 1、postgresql java -D"spring.profiles.active=prd" -D"spring.datasource.url=jdbc:postgresql://localhost:5432/saga?useSSL=false" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -jar alpha-server-0.5.0-exec.jar 2、mysql支持 2.1 創建插件目錄 2.2 java -D"spring.profiles.active=mysql" -D"loader.path=./plugins" -D"spring.datasource.url=jdbc:mysql://localhost:3306/saga?useSSL=false&serverTimezone=Asia/Shanghai" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -D"spring.cloud.consul.host=http://127.0.0.1" -D"spring.cloud.consul.port=8500" -D"spring.cloud.consul.enabled=true" -D"alpha.server.port=8011" -D"server.port=8092" -jar alpha-server-0.5.0-exec.jar // 集群支持 -D"alpha.cluster.master.enabled=true"
注意: 請在執行命令前將
${saga_version}
和${host_address}
更改為實際值。 -
注意: 默認情況下,8080端口用於處理omega處發起的gRPC的請求,而8090端口用於處理查詢存儲在alpha處的事件信息。
-
配置omega。在
application.yaml
添加下面的配置項:spring: application: name: {application.name} alpha: cluster: address: {alpha.cluster.addresses}
然后就可以運行相關的微服務了,可通過訪問http://${alpha-server:port}/saga/events 來獲取所有的saga事件信息。
在Alpha與Omega之間啟用SSL
詳情請參考啟用 SSL文檔.
注冊中心支持
支持Alpha啟動時注冊到發現服務,Omega通過發現服務獲取Alpha的實例列表和gRPC地址
Consul 支持
-
運行alpha
運行時增加
spring.cloud.consul.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --spring.cloud.consul.enabled=true \ --spring.cloud.consul.host=${consul_host} \ --spring.cloud.consul.port=${consul_port} \ --spring.profiles.active=prd java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://localhost:5432/saga?useSSL=false \ --spring.datasource.username=root \ --spring.datasource.password=root \ --spring.cloud.consul.enabled=true \ --spring.cloud.consul.host=${consul_host} \ --spring.cloud.consul.port=${consul_port} \ --spring.profiles.active=prd
注意:
${consul_host}
是 consul 地址,${consul_port}
是 consul 端口注意: 更多 Consul 參數請參考 Spring Cloud Consul 2.xSpring Cloud Consul 1.x
-
驗證是否注冊成功
訪問 Consul 的注冊實例查詢接口
curl http://127.0.0.1:8500/v1/agent/services
可以看到如下注冊信息,在你 Tags 中可以看到 Alpha 的 gRPC 訪問地址已經注冊{ "servicecomb-alpha-server-0-0-0-0-8090": { "ID": "servicecomb-alpha-server-0-0-0-0-8090", "Service": "servicecomb-alpha-server", "Tags": [ "alpha-server-host=0.0.0.0", "alpha-server-port=8080", "secure=false" ], "Meta": {}, "Port": 8090, "Address": "10.50.7.14", "Weights": { "Passing": 1, "Warning": 1 }, "EnableTagOverride": false } }
注意: 默認情況下注冊的服務名是
servicecomb-alpha-server
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置 -
配置omega
在項目中引入依賴包
omega-spring-cloud-consul-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-consul-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:spring: cloud: consul: discovery: register: false host: 127.0.0.1 port: 8500 alpha: cluster: register: type: consul
-
spring.cloud.consul.host
配置 Consul 注冊中心的地址,spring.cloud.consul.port
配置 Consul 注冊中心的端口,spring.cloud.consul.discovery.register=false
表示不注冊自己到注冊中心,更多 Consul 客戶端配置可以參考Spring Cloud Consul 2.xSpring Cloud Consul 1.x -
alpha.cluster.register.type=consul
配置Omega獲取Alpha的方式是通過 Consul 的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-consul-discovery版本
spring boot spring-cloud-starter-consul-discovery 2.1.x.RELEASE 2.1.1.RELEASE 2.0.x.RELEASE 2.0.2.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> <version>2.0.2.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數
spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名 -
Spring Cloud Eureka支持
-
編譯 eureka 的版本
使用
-Pspring-cloud-eureka
參數編譯支持 eureka 的版本git clone https://github.com/apache/servicecomb-pack.git cd servicecomb-pack mvn clean install -DskipTests=true -Pspring-cloud-eureka
-
運行alpha
運行時增加
eureka.client.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --eureka.client.enabled=true \ --eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka \ --spring.profiles.active=prd
注意: 更多 eureka 參數請參考 Spring Cloud Netflix 2.xSpring Cloud Netflix 1.x
-
驗證是否注冊成功
訪問Eureka的注冊實例查詢接口
curl http://127.0.0.1:8761/eureka/apps/
可以看到如下注冊信息,在你metadata中可以看到Alpha的gRPC訪問地址<servicecomb-alpha-server>0.0.0.0:8080</servicecomb-alpha-server>
已經注冊<applications> <versions__delta>1</versions__delta> <apps__hashcode>UP_1_</apps__hashcode> <application> <name>SERVICECOMB-ALPHA-SERVER</name> <instance> <instanceId>0.0.0.0::servicecomb-alpha-server:8090</instanceId> <hostName>0.0.0.0</hostName> <app>SERVICECOMB-ALPHA-SERVER</app> <ipAddr>0.0.0.0</ipAddr> <status>UP</status> ... <metadata> <management.port>8090</management.port> <servicecomb-alpha-server>0.0.0.0:8080</servicecomb-alpha-server> </metadata> ... </instance> </application> </applications>
注意: 默認情況下注冊的服務名是
SERVICECOMB-ALPHA-SERVER
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置 -
配置omega
在項目中引入依賴包
omega-spring-cloud-eureka-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-eureka-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:eureka: client: service-url: defaultZone: http://127.0.0.1:8761/eureka alpha: cluster: register: type: eureka
-
eureka.client.service-url.defaultZone
配置Eureka注冊中心的地址,更多Eureka客戶端配置可以參考Spring Cloud Netflix 2.x 或 Spring Cloud Netflix 1.x -
alpha.cluster.register.type=eureka
配置Omega獲取Alpha的方式是通過Eureka的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-consul-discovery版本
spring boot spring-cloud-starter-netflix-eureka-client 2.1.x.RELEASE 2.1.1.RELEASE 2.0.x.RELEASE 2.0.3.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.0.3.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數
spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名 -
Spring Cloud Zookeeper 支持
-
運行alpha
運行時增加
spring.cloud.zookeeper.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --spring.cloud.zookeeper.enabled=true \ --spring.cloud.zookeeper.connectString=${zookeeper_host}:${zookeeper_port} \ --spring.profiles.active=prd
注意:
${zookeeper_host}
是 zookeeper 地址,${zookeeper_port}
是 zookeeper 端口注意: 更多 Zookeeper 參數請參考 Spring Cloud Zookeeper 2.xSpring Cloud Zookeeper 1.x
-
驗證是否注冊成功
訪問Zookeeper的實例, 在znode /services/servicecomb-alapha-server 下,查看服務注冊znode, 在注冊的znode中,存在類似以下值
{ "name": "servicecomb-alpha-server", "id": "9b2223ae-50e6-49a6-9f3b-87a1ff06a016", "address": "arch-office", "port": 8090, "sslPort": null, "payload": { "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance", "id": "servicecomb-alpha-server-1", "name": "servicecomb-alpha-server", "metadata": { "servicecomb-alpha-server": "arch-office:8080" } }, "registrationTimeUTC": 1558000134185, "serviceType": "DYNAMIC", "uriSpec": { "parts": [ { "value": "scheme", "variable": true }, { "value": "://", "variable": false }, { "value": "address", "variable": true }, { "value": ":", "variable": false }, { "value": "port", "variable": true } ] } }
注意: 默認情況下注冊的服務名是
servicecomb-alpha-server
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置 -
配置omega
在項目中引入依賴包
omega-spring-cloud-zookeeper-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-zookeeper-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:spring: cloud: zookeeper: enabled: true connectString: 127.0.0.1:2181 alpha: cluster: register: type: zookeeper
-
spring.cloud.zookeeper.connectString
配置 Zookeeper 注冊中心的地址,更多zookeeper客戶端配置可以參考Spring Cloud Zookeeper 2.xSpring Cloud Zookeeper 1.x
-
-
alpha.cluster.register.type=zookeeper
配置Omega獲取Alpha的方式是通過 Zookeeper 的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-zookeeper-discovery版本
spring boot spring-cloud-starter-zookeeper-discovery 2.1.x.RELEASE 2.1.1.RELEASE 1.5.17.RELEASE 1.2.2.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <version>2.1.1.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名
Spring Cloud Nacos Discovery 支持
-
運行alpha
運行時增加
nacos.client.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --spring.cloud.nacos.discovery.enabled=true \ --spring.cloud.nacos.discovery.serverAddr=${nacos_host}:${nacos_port} \ --nacos.client.enabled=true \ --spring.profiles.active=prd
注意:
${nacos_host}
是 nacos 地址,${nacos_port}
是 nacos 端口注意: 更多 Nacos 參數請參考 Spring Cloud Nacos Discovery
-
驗證是否注冊成功
訪問Nacos的實例, 通過nacos 提供的openapi
curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=servicecomb-alpha-server‘
可以看到如下服務注冊信息,在metadata 中可以發現gRPC的地址已經被注冊
{ "metadata": {}, "dom": "servicecomb-alpha-server", "cacheMillis": 3000, "useSpecifiedURL": false, "hosts": [ { "valid": true, "marked": false, "metadata": { "preserved.register.source": "SPRING_CLOUD", "servicecomb-alpha-server": "192.168.2.28:8080" }, "instanceId": "192.168.2.28#8090#DEFAULT#DEFAULT_GROUP@@servicecomb-alpha-server", "port": 8090, "healthy": true, "ip": "192.168.2.28", "clusterName": "DEFAULT", "weight": 1, "ephemeral": true, "serviceName": "servicecomb-alpha-server", "enabled": true } ], "name": "DEFAULT_GROUP@@servicecomb-alpha-server", "checksum": "d9e8deefd1c4f198980f4443d7c1b1fd", "lastRefTime": 1562567653565, "env": "", "clusters": "" }
注意: 默認情況下注冊的服務名是servicecomb-alpha-server
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置
-
配置omega
在項目中引入依賴包
omega-spring-cloud-nacos-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-nacos-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:spring: cloud: nacos: discovery: enabled: true serverAddr: 127.0.0.1:8848 alpha: cluster: register: type: nacos
-
spring.cloud.nacos.discovery.serverAddr
配置 Nacos 注冊中心的地址,更多Nacos 參數請參考 Spring Cloud Nacos Discovery
-
-
alpha.cluster.register.type=nacos
配置Omega獲取Alpha的方式是通過 Nacos 的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-alibaba-nacos-discovery版本
spring boot spring-cloud-starter-alibaba-nacos-discovery 2.1.x.RELEASE 0.2.2.RELEASE 1.5.17.RELEASE 0.1.2.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.2.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名
集群
Alpha 可以通過部署多實例的方式保證高可用,使用 alpha.cluster.master.enabled=true
參數開啟集群支持
實驗
編譯
獲取源碼:
$ git clone https://github.com/apache/servicecomb-pack.git
$ cd servicecomb-pack
Saga可通過以下任一方式進行構建:
-
只構建可執行文件:
$ mvn clean install -DskipTests
-
同時構建可執行文件和docker鏡像:
$ mvn clean install -DskipTests -Pdocker
-
同時構建可執行文件以及Saga發行包
$ mvn clean install -DskipTests -Prelease
在執行以上任一指令后,可在alpha/alpha-server/target/saga/alpha-server-${version}-exec.jar
中找到alpha server的可執行文件。
如何使用
引入Pack的依賴
<dependency>
<groupId>org.apache.servicecomb.pack</groupId>
<artifactId>omega-spring-starter</artifactId>
<version>${pack.version}</version>
</dependency>
<dependency>
<groupId>org.apache.servicecomb.pack</groupId>
<artifactId>omega-transport-resttemplate</artifactId>
<version>${pack.version}</version>
</dependency>
注意: 請將${pack.version}
更改為實際的版本號。版本遷移提示: 從0.3.0 開始,整個項目的代碼庫名由servicecomb-saga改名為servicecomb-pack, 與此同時我們也更新了對應發布包的組名以及相關包名。如果你的項目是從saga 0.2.x 遷移過來,請按照下表所示進行修改。
name | 0.2.x | 0.3.x |
---|---|---|
groupId | org.apache.servicecomb.saga | org.apache.servicecomb.pack |
Package Name | org.apache.servicecomb.saga | org.apache.servicecomb.pack |
Saga 支持
添加Saga的注解及相應的補償方法以一個轉賬應用為例:
-
在全局事務的起點添加
@SagaStart
的注解。import org.apache.servicecomb.pack.omega.context.annotations.SagaStart;
timeout=10) (
public boolean transferMoney(String from, String to, int amount) {
transferOut(from, amount);
transferIn(to, amount);
}注意: 默認情況下,超時設置需要顯式聲明才生效。
-
在子事務處添加
@Compensable
的注解並指明其對應的補償方法。import javax.transaction.Transactional;
import org.apache.servicecomb.pack.omega.transaction.annotations.Compensable;
timeout=5, compensationMethod="cancel") (
public boolean transferOut(String from, int amount) {
repo.reduceBalanceByUsername(from, amount);
}
public boolean cancel(String from, int amount) {
repo.addBalanceByUsername(from, amount);
}注意: 實現的服務使用相當的參數,實現的服務和補償必須滿足冪等的條件,同時建議使用Spring @Transactional標注提供本地事務保證。
注意: 默認情況下,超時設置需要顯式聲明才生效。
注意: 若全局事務起點與子事務起點重合,需同時聲明
@SagaStart
和@Compensable
的注解。 -
增加 alpha.cluster.address 參數
alpha
cluster
address alpha-server.servicecomb.io8080 -
對轉入服務重復第二步即可。
-
從pack-0.3.0開始, 你可以在服務函數或者取消函數中通過訪問 OmegaContext 來獲取 gloableTxId 以及 localTxId 信息。
<a name="explicit-tx-context-passing"></a>顯式傳遞事務上下文
在一般情況下,Omega能夠替你處理事務上下文的傳遞工作(詳情見服務間通信流程),因此你的代碼並不需要知道事務上下文的存在。而事務上下文的傳遞實際上是通過在請求方注入、在接受方提取來完成的,下面舉一個請例子來說明這個過程:
Service A:
@SagaStart public void foo() { restTemplate.postForEntity("http://service-b/bar", ...); }
Service B:
@GetMapping("/bar") @Compensable public void bar() { ... }
我們可以先來看看Omega是怎么傳遞事務上下文的:
-
Service A的foo方法會開啟一個新的全局事務。
-
TransactionClientHttpRequestInterceptor會在RestTemplate請求Service B時在Http請求頭中注入事務上下文信息。
-
當Servce B接收到請求時,TransactionHandlerInterceptor會從請求頭中提取事務上下文信息。
目前Omega支持以下形式的隱式事務上下文傳遞:
-
omega-transport-{dubbo,feign,resttemplate,servicecomb}。
-
同線程內調用(基於OmegaContext的ThreadLocal字段)。
-
標注了@OmegaContextAware的java.util.concurrent.Executor{Service}。
那么問題來了,如果無法隱式傳遞事務上下文怎么辦?比如Service A使用某種RPC機制件來調用Service B,而你又沒有辦法注入或提取事務上下文信息。這個時候你只能采用顯式的方式把事務上下文傳遞出去。ServiceComb Pack從0.5.0開始提供了兩個類來實現這一點。
利用TransactionContext傳遞
Service A:
@SagaStart public void foo(BarCommand cmd) { TransactionContext localTxContext = omegaContext.getTransactionContext(); someRpc.send(cmd, localTxContext); }
Service B:
public void listen(BarCommand cmd, TransactionContext injectedTxContext) { bar(cmd, injectedTxContext); } @Compensable public void bar(BarCommand cmd, TransactionContext injectedTxContext) { ... // TransactionContext localTxContext = omegaContext.getTransactionContext(); }
需要注意的是bar
方法接收到的是注入的事務上下文,在進入bar
之后從OmegaContext得到的是本地事務上下文(Omega替你開啟了新的事務)。如果Service B也需要顯式地傳遞事務上下文,那么應該使用本地事務上下文。
利用TransactionContextProperties傳遞
Service A:
public class BarCommand {} public class BarCommandWithTxContext extends BarCommand implements TransactionContextProperties { // setter getter for globalTxId // setter getter for localTxId } @SagaStart public void foo(BarCommand cmd) { BarCommandWithTxContext cmdWithTxContext = new BarCommandWithTxContext(cmd); cmdWithTxContext.setGlobalTxId(omegaContext.globalTxId()); cmdWithTxContext.setLocalTxId(omegaContext.localTxId()); someRpc.send(cmdWithTxContext); }
Service B:
public void listen(BarCommandWithTxContext cmdWithTxContext) { bar(cmdWithTxContext); } @Compensable public void bar(BarCommandWithTxContext cmdWithTxContext) { ... // TransactionContext localTxContext = omegaContext.getTransactionContext(); }
和前面一種方式類似,TransactionContextProperties.get{Global,Local}TxId()返回的也是注入的事務上下文信息。
TCC 支持
在對應的方法中添加TccStart 和 Participate標注 以一個轉賬應用為例:
-
在全局事務的起點添加
@TccStart
的注解。import org.apache.servicecomb.pack.omega.context.annotations.TccStart; @TccStart public boolean transferMoney(String from, String to, int amount) { transferOut(from, amount); transferIn(to, amount); }
Note: 當前TCC還不支持Timeout
-
在子事務嘗試方法處添加
@Participate
的注解並指明其對應的執行以及補償方法名,import javax.transaction.Transactional; import org.apache.servicecomb.pack.omega.transaction.annotations.Participate; @Participate(confirmMethod = "confirm", cancelMethod = "cancel") @Transactional public void transferOut(String from, int amount) { // check banalance } @Transactional public void confirm(String from, int amount) { repo.reduceBalanceByUsername(from, amount); } @Transactional public void cancel(String from, int amount) { repo.addBalanceByUsername(from, amount); }
注意:
confirm
和cancel
方法的參數列表應該與@Participate
方法一樣,並且它們必須是冪等。我們強烈建議使用Spring的@Transactional
來保證本地事務的一致性。注意: 若全局事務起點與子事務起點重合,需同時聲明
@TccStart
和@Participate
的注解。 -
增加 alpha.cluster.address 參數
alpha: cluster: address: alpha-server.servicecomb.io:8080
-
對轉入服務重復第二步即可。
-
從pack-0.3.0開始, 你可以在服務函數或者取消函數中通過訪問 OmegaContext 來獲取 gloableTxId 以及 localTxId 信息。
顯式傳遞事務上下文
與Saga的@Compensable
一樣,TCC的@Participate
也支持顯式傳遞事務上下文,詳情可參閱Saga章節。
如何運行
-
運行postgreSQL,
docker run -d -e "POSTGRES_DB=saga" -e "POSTGRES_USER=saga" -e "POSTGRES_PASSWORD=password" -p 5432:5432 postgres
如果你想使用MySQL做為后台數據庫,可以參考 此文檔。
-
運行alpha。在運行alpha前,請確保postgreSQL已正常啟動。可通過docker或可執行文件的方式來啟動alpha。
-
通過docker運行:
docker run -d -p 8080:8080 -p 8090:8090 -e "JAVA_OPTS=-Dspring.profiles.active=prd -Dspring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false" alpha-server:${saga_version}
-
通過可執行文件運行:
java -Dspring.profiles.active=prd -D"spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false" -jar alpha-server-${saga_version}-exec.jar 1、postgresql java -D"spring.profiles.active=prd" -D"spring.datasource.url=jdbc:postgresql://localhost:5432/saga?useSSL=false" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -jar alpha-server-0.5.0-exec.jar 2、mysql支持 2.1 創建插件目錄 2.2 java -D"spring.profiles.active=mysql" -D"loader.path=./plugins" -D"spring.datasource.url=jdbc:mysql://localhost:3306/saga?useSSL=false&serverTimezone=Asia/Shanghai" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -D"spring.cloud.consul.host=http://127.0.0.1" -D"spring.cloud.consul.port=8500" -D"spring.cloud.consul.enabled=true" -D"alpha.server.port=8011" -D"server.port=8092" -jar alpha-server-0.5.0-exec.jar // 集群支持 -D"alpha.cluster.master.enabled=true"
注意: 請在執行命令前將
${saga_version}
和${host_address}
更改為實際值。 -
注意: 默認情況下,8080端口用於處理omega處發起的gRPC的請求,而8090端口用於處理查詢存儲在alpha處的事件信息。
-
配置omega。在
application.yaml
添加下面的配置項:spring: application: name: {application.name} alpha: cluster: address: {alpha.cluster.addresses}
然后就可以運行相關的微服務了,可通過訪問http://${alpha-server:port}/saga/events 來獲取所有的saga事件信息。
在Alpha與Omega之間啟用SSL
詳情請參考啟用 SSL文檔.
注冊中心支持
支持Alpha啟動時注冊到發現服務,Omega通過發現服務獲取Alpha的實例列表和gRPC地址
Consul 支持
-
運行alpha
運行時增加
spring.cloud.consul.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --spring.cloud.consul.enabled=true \ --spring.cloud.consul.host=${consul_host} \ --spring.cloud.consul.port=${consul_port} \ --spring.profiles.active=prd java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://localhost:5432/saga?useSSL=false \ --spring.datasource.username=root \ --spring.datasource.password=root \ --spring.cloud.consul.enabled=true \ --spring.cloud.consul.host=${consul_host} \ --spring.cloud.consul.port=${consul_port} \ --spring.profiles.active=prd
注意:
${consul_host}
是 consul 地址,${consul_port}
是 consul 端口注意: 更多 Consul 參數請參考 Spring Cloud Consul 2.xSpring Cloud Consul 1.x
-
驗證是否注冊成功
訪問 Consul 的注冊實例查詢接口
curl http://127.0.0.1:8500/v1/agent/services
可以看到如下注冊信息,在你 Tags 中可以看到 Alpha 的 gRPC 訪問地址已經注冊{ "servicecomb-alpha-server-0-0-0-0-8090": { "ID": "servicecomb-alpha-server-0-0-0-0-8090", "Service": "servicecomb-alpha-server", "Tags": [ "alpha-server-host=0.0.0.0", "alpha-server-port=8080", "secure=false" ], "Meta": {}, "Port": 8090, "Address": "10.50.7.14", "Weights": { "Passing": 1, "Warning": 1 }, "EnableTagOverride": false } }
注意: 默認情況下注冊的服務名是
servicecomb-alpha-server
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置 -
配置omega
在項目中引入依賴包
omega-spring-cloud-consul-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-consul-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:spring: cloud: consul: discovery: register: false host: 127.0.0.1 port: 8500 alpha: cluster: register: type: consul
-
spring.cloud.consul.host
配置 Consul 注冊中心的地址,spring.cloud.consul.port
配置 Consul 注冊中心的端口,spring.cloud.consul.discovery.register=false
表示不注冊自己到注冊中心,更多 Consul 客戶端配置可以參考Spring Cloud Consul 2.xSpring Cloud Consul 1.x -
alpha.cluster.register.type=consul
配置Omega獲取Alpha的方式是通過 Consul 的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-consul-discovery版本
spring boot spring-cloud-starter-consul-discovery 2.1.x.RELEASE 2.1.1.RELEASE 2.0.x.RELEASE 2.0.2.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> <version>2.0.2.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數
spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名 -
Spring Cloud Eureka支持
-
編譯 eureka 的版本
使用
-Pspring-cloud-eureka
參數編譯支持 eureka 的版本git clone https://github.com/apache/servicecomb-pack.git cd servicecomb-pack mvn clean install -DskipTests=true -Pspring-cloud-eureka
-
運行alpha
運行時增加
eureka.client.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --eureka.client.enabled=true \ --eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka \ --spring.profiles.active=prd
注意: 更多 eureka 參數請參考 Spring Cloud Netflix 2.xSpring Cloud Netflix 1.x
-
驗證是否注冊成功
訪問Eureka的注冊實例查詢接口
curl http://127.0.0.1:8761/eureka/apps/
可以看到如下注冊信息,在你metadata中可以看到Alpha的gRPC訪問地址<servicecomb-alpha-server>0.0.0.0:8080</servicecomb-alpha-server>
已經注冊<applications> <versions__delta>1</versions__delta> <apps__hashcode>UP_1_</apps__hashcode> <application> <name>SERVICECOMB-ALPHA-SERVER</name> <instance> <instanceId>0.0.0.0::servicecomb-alpha-server:8090</instanceId> <hostName>0.0.0.0</hostName> <app>SERVICECOMB-ALPHA-SERVER</app> <ipAddr>0.0.0.0</ipAddr> <status>UP</status> ... <metadata> <management.port>8090</management.port> <servicecomb-alpha-server>0.0.0.0:8080</servicecomb-alpha-server> </metadata> ... </instance> </application> </applications>
注意: 默認情況下注冊的服務名是
SERVICECOMB-ALPHA-SERVER
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置 -
配置omega
在項目中引入依賴包
omega-spring-cloud-eureka-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-eureka-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:eureka: client: service-url: defaultZone: http://127.0.0.1:8761/eureka alpha: cluster: register: type: eureka
-
eureka.client.service-url.defaultZone
配置Eureka注冊中心的地址,更多Eureka客戶端配置可以參考Spring Cloud Netflix 2.x 或 Spring Cloud Netflix 1.x -
alpha.cluster.register.type=eureka
配置Omega獲取Alpha的方式是通過Eureka的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-consul-discovery版本
spring boot spring-cloud-starter-netflix-eureka-client 2.1.x.RELEASE 2.1.1.RELEASE 2.0.x.RELEASE 2.0.3.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.0.3.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數
spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名 -
Spring Cloud Zookeeper 支持
-
運行alpha
運行時增加
spring.cloud.zookeeper.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --spring.cloud.zookeeper.enabled=true \ --spring.cloud.zookeeper.connectString=${zookeeper_host}:${zookeeper_port} \ --spring.profiles.active=prd
注意:
${zookeeper_host}
是 zookeeper 地址,${zookeeper_port}
是 zookeeper 端口注意: 更多 Zookeeper 參數請參考 Spring Cloud Zookeeper 2.xSpring Cloud Zookeeper 1.x
-
驗證是否注冊成功
訪問Zookeeper的實例, 在znode /services/servicecomb-alapha-server 下,查看服務注冊znode, 在注冊的znode中,存在類似以下值
{ "name": "servicecomb-alpha-server", "id": "9b2223ae-50e6-49a6-9f3b-87a1ff06a016", "address": "arch-office", "port": 8090, "sslPort": null, "payload": { "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance", "id": "servicecomb-alpha-server-1", "name": "servicecomb-alpha-server", "metadata": { "servicecomb-alpha-server": "arch-office:8080" } }, "registrationTimeUTC": 1558000134185, "serviceType": "DYNAMIC", "uriSpec": { "parts": [ { "value": "scheme", "variable": true }, { "value": "://", "variable": false }, { "value": "address", "variable": true }, { "value": ":", "variable": false }, { "value": "port", "variable": true } ] } }
注意: 默認情況下注冊的服務名是
servicecomb-alpha-server
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置 -
配置omega
在項目中引入依賴包
omega-spring-cloud-zookeeper-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-zookeeper-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:spring: cloud: zookeeper: enabled: true connectString: 127.0.0.1:2181 alpha: cluster: register: type: zookeeper
-
spring.cloud.zookeeper.connectString
配置 Zookeeper 注冊中心的地址,更多zookeeper客戶端配置可以參考Spring Cloud Zookeeper 2.xSpring Cloud Zookeeper 1.x
-
-
alpha.cluster.register.type=zookeeper
配置Omega獲取Alpha的方式是通過 Zookeeper 的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-zookeeper-discovery版本
spring boot spring-cloud-starter-zookeeper-discovery 2.1.x.RELEASE 2.1.1.RELEASE 1.5.17.RELEASE 1.2.2.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <version>2.1.1.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名
Spring Cloud Nacos Discovery 支持
-
運行alpha
運行時增加
nacos.client.enabled=true
參數java -jar alpha-server-${saga_version}-exec.jar \ --spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false \ --spring.datasource.username=saga \ --spring.datasource.password=saga \ --spring.cloud.nacos.discovery.enabled=true \ --spring.cloud.nacos.discovery.serverAddr=${nacos_host}:${nacos_port} \ --nacos.client.enabled=true \ --spring.profiles.active=prd
注意:
${nacos_host}
是 nacos 地址,${nacos_port}
是 nacos 端口注意: 更多 Nacos 參數請參考 Spring Cloud Nacos Discovery
-
驗證是否注冊成功
訪問Nacos的實例, 通過nacos 提供的openapi
curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=servicecomb-alpha-server‘
可以看到如下服務注冊信息,在metadata 中可以發現gRPC的地址已經被注冊
{ "metadata": {}, "dom": "servicecomb-alpha-server", "cacheMillis": 3000, "useSpecifiedURL": false, "hosts": [ { "valid": true, "marked": false, "metadata": { "preserved.register.source": "SPRING_CLOUD", "servicecomb-alpha-server": "192.168.2.28:8080" }, "instanceId": "192.168.2.28#8090#DEFAULT#DEFAULT_GROUP@@servicecomb-alpha-server", "port": 8090, "healthy": true, "ip": "192.168.2.28", "clusterName": "DEFAULT", "weight": 1, "ephemeral": true, "serviceName": "servicecomb-alpha-server", "enabled": true } ], "name": "DEFAULT_GROUP@@servicecomb-alpha-server", "checksum": "d9e8deefd1c4f198980f4443d7c1b1fd", "lastRefTime": 1562567653565, "env": "", "clusters": "" }
注意: 默認情況下注冊的服務名是servicecomb-alpha-server
,如果你需要自定義服務名可以在運行Alpha的時候通過命令行參數spring.application.name
配置
-
配置omega
在項目中引入依賴包
omega-spring-cloud-nacos-starter
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-nacos-starter</artifactId> <version>${pack.version}</version> </dependency>
在
application.yaml
添加下面的配置項:spring: cloud: nacos: discovery: enabled: true serverAddr: 127.0.0.1:8848 alpha: cluster: register: type: nacos
-
spring.cloud.nacos.discovery.serverAddr
配置 Nacos 注冊中心的地址,更多Nacos 參數請參考 Spring Cloud Nacos Discovery
-
-
alpha.cluster.register.type=nacos
配置Omega獲取Alpha的方式是通過 Nacos 的注冊中心 -
spring boot 版本兼容
如果你的項目使用的不是spring boot 2.1.1版本,那么請參照此列表增加兼容的spring-cloud-starter-alibaba-nacos-discovery版本
spring boot spring-cloud-starter-alibaba-nacos-discovery 2.1.x.RELEASE 0.2.2.RELEASE 1.5.17.RELEASE 0.1.2.RELEASE <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.2.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
注意: 如果你在啟動Alpha的時候通過命令行參數spring.application.name
自定義了服務名,那么你需要在Omega中通過參數alpha.cluster.serviceId
指定這個服務名
集群
Alpha 可以通過部署多實例的方式保證高可用,使用 alpha.cluster.master.enabled=true
參數開啟集群支持
實驗