add cloud deploy
This commit is contained in:
parent
344e2840bd
commit
7f18d0fb0d
@ -1,4 +1,4 @@
|
|||||||
FROM daocloud.io/atsctoo/golang:1.15
|
FROM golang:1.15
|
||||||
WORKDIR /app/dtm
|
WORKDIR /app/dtm
|
||||||
RUN go env -w GO111MODULE=on
|
RUN go env -w GO111MODULE=on
|
||||||
RUN go env -w GOPROXY=https://goproxy.io,direct
|
RUN go env -w GOPROXY=https://goproxy.io,direct
|
||||||
|
|||||||
@ -23,19 +23,19 @@
|
|||||||
* 一站式解决分布式事务需求
|
* 一站式解决分布式事务需求
|
||||||
- 支持TCC、SAGA、XA、事务消息、可靠消息
|
- 支持TCC、SAGA、XA、事务消息、可靠消息
|
||||||
* 支持跨语言栈事务
|
* 支持跨语言栈事务
|
||||||
- 适合多语言栈的公司使用。支持Go、Python、PHP、Nodejs、Java、c# 各类语言SDK。
|
- 支持多语言栈混合的分布式事务。支持Go、Python、PHP、Nodejs、Java、c# 各类语言SDK。
|
||||||
* 自动处理子事务乱序
|
* 自动处理子事务乱序
|
||||||
- 首创子事务屏障技术,框架层自动处理悬挂、空补偿、幂等网络异常问题
|
- 首创子事务屏障技术,框架层自动处理悬挂、空补偿、幂等网络异常问题
|
||||||
* 支持单服务多数据源访问
|
* 支持单服务多数据源访问
|
||||||
- 通过子事务屏障技术,保证单服务多数据源访问的一致性,也能保证本地长事务拆分多个子事务后的一致性
|
- 通过子事务屏障技术,保证单服务多数据源访问的一致性,也能保证本地长事务拆分多个子事务后的一致性
|
||||||
* 开箱即用,提供云上服务
|
* 开箱即用,提供云上服务
|
||||||
- 支持通用的HTTP、gRPC协议接入,云原生友好。支持Mysql接入。提供测试版本的云上服务,方便新用户测试
|
- 支持通用的HTTP、gRPC协议接入,云原生友好。支持Mysql接入。提供测试版本的云上服务,方便新用户测试接入
|
||||||
|
|
||||||
## 与其他框架对比
|
## 与其他框架对比
|
||||||
|
|
||||||
目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,其中以seata应用最为广泛。
|
目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,其中以seata应用最为广泛。
|
||||||
|
|
||||||
下面是dtm和seata的主要特性对比:
|
下面是DTM和SEATA的主要特性对比:
|
||||||
|
|
||||||
| 特性| DTM | SEATA |备注|
|
| 特性| DTM | SEATA |备注|
|
||||||
|:-----:|:----:|:----:|:----:|
|
|:-----:|:----:|:----:|:----:|
|
||||||
@ -46,7 +46,7 @@
|
|||||||
|AT事务|<span style="color:red">✗</span>|<span style="color:green">✓</span>|AT与XA类似,性能更好,但有脏回滚|
|
|AT事务|<span style="color:red">✗</span>|<span style="color:green">✓</span>|AT与XA类似,性能更好,但有脏回滚|
|
||||||
| SAGA事务 |<span style="color:orange">简单模式</span> |<span style="color:green">状态机复杂模式</span> |dtm的状态机模式在规划中|
|
| SAGA事务 |<span style="color:orange">简单模式</span> |<span style="color:green">状态机复杂模式</span> |dtm的状态机模式在规划中|
|
||||||
|事务消息|<span style="color:green">✓</span>|<span style="color:red">✗</span>|dtm提供类似rocketmq的事务消息|
|
|事务消息|<span style="color:green">✓</span>|<span style="color:red">✗</span>|dtm提供类似rocketmq的事务消息|
|
||||||
|通信协议|HTTP、GRPC|dubbo等协议,无HTTP||
|
|通信协议|HTTP、gRPC|dubbo等协议,无HTTP||
|
||||||
|star数量|<img src="https://img.shields.io/github/stars/yedf/dtm.svg?style=social" alt="github stars"/>|<img src="https://img.shields.io/github/stars/seata/seata.svg?style=social" alt="github stars"/>|dtm从20210604发布0.1,发展快|
|
|star数量|<img src="https://img.shields.io/github/stars/yedf/dtm.svg?style=social" alt="github stars"/>|<img src="https://img.shields.io/github/stars/seata/seata.svg?style=social" alt="github stars"/>|dtm从20210604发布0.1,发展快|
|
||||||
|
|
||||||
从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。
|
从上面对比的特性来看,如果您的语言栈包含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也可以选择接入dtm,使用子事务屏障技术,简化您的业务编写。
|
||||||
|
|||||||
@ -125,16 +125,22 @@ func DbGet(conf map[string]string) *DB {
|
|||||||
type dtmConfigType struct {
|
type dtmConfigType struct {
|
||||||
TransCronInterval int64 `yaml:"TransCronInterval"` // 单位秒 当事务等待这个时间之后,还没有变化,则进行一轮处理,包括prepared中的任务和committed的任务
|
TransCronInterval int64 `yaml:"TransCronInterval"` // 单位秒 当事务等待这个时间之后,还没有变化,则进行一轮处理,包括prepared中的任务和committed的任务
|
||||||
DB map[string]string `yaml:"DB"`
|
DB map[string]string `yaml:"DB"`
|
||||||
|
DisableLocalhost int64 `yaml:"DisableLocalhost"`
|
||||||
|
RetryLimit int64 `yaml:"RetryLimit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DtmConfig 配置
|
// DtmConfig 配置
|
||||||
var DtmConfig = dtmConfigType{}
|
var DtmConfig = dtmConfigType{}
|
||||||
|
|
||||||
|
func getIntEnv(key string, defaultV string) int64 {
|
||||||
|
return int64(dtmcli.MustAtoi(dtmcli.OrString(os.Getenv(key), defaultV)))
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if len(os.Args) == 1 {
|
if len(os.Args) == 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
DtmConfig.TransCronInterval = int64(dtmcli.MustAtoi(dtmcli.OrString(os.Getenv("TRANS_CRON_INTERVAL"), "10")))
|
DtmConfig.TransCronInterval = getIntEnv("TRANS_CRON_INTERVAL", "10")
|
||||||
DtmConfig.DB = map[string]string{
|
DtmConfig.DB = map[string]string{
|
||||||
"driver": dtmcli.OrString(os.Getenv("DB_DRIVER"), "mysql"),
|
"driver": dtmcli.OrString(os.Getenv("DB_DRIVER"), "mysql"),
|
||||||
"host": os.Getenv("DB_HOST"),
|
"host": os.Getenv("DB_HOST"),
|
||||||
@ -142,6 +148,8 @@ func init() {
|
|||||||
"user": os.Getenv("DB_USER"),
|
"user": os.Getenv("DB_USER"),
|
||||||
"password": os.Getenv("DB_PASSWORD"),
|
"password": os.Getenv("DB_PASSWORD"),
|
||||||
}
|
}
|
||||||
|
DtmConfig.DisableLocalhost = getIntEnv("DISABLE_LOCALHOST", "0")
|
||||||
|
DtmConfig.RetryLimit = getIntEnv("RETRY_LIMIT", "2000000000")
|
||||||
cont := []byte{}
|
cont := []byte{}
|
||||||
for d := MustGetwd(); d != "" && d != "/"; d = filepath.Dir(d) {
|
for d := MustGetwd(); d != "" && d != "/"; d = filepath.Dir(d) {
|
||||||
cont1, err := ioutil.ReadFile(d + "/conf.yml")
|
cont1, err := ioutil.ReadFile(d + "/conf.yml")
|
||||||
|
|||||||
@ -16,6 +16,7 @@ func addRoute(engine *gin.Engine) {
|
|||||||
engine.POST("/api/dtmsvr/registerTccBranch", common.WrapHandler(registerTccBranch))
|
engine.POST("/api/dtmsvr/registerTccBranch", common.WrapHandler(registerTccBranch))
|
||||||
engine.POST("/api/dtmsvr/abort", common.WrapHandler(abort))
|
engine.POST("/api/dtmsvr/abort", common.WrapHandler(abort))
|
||||||
engine.GET("/api/dtmsvr/query", common.WrapHandler(query))
|
engine.GET("/api/dtmsvr/query", common.WrapHandler(query))
|
||||||
|
engine.GET("/api/dtmsvr/all", common.WrapHandler(all))
|
||||||
engine.GET("/api/dtmsvr/newGid", common.WrapHandler(newGid))
|
engine.GET("/api/dtmsvr/newGid", common.WrapHandler(newGid))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,3 +75,14 @@ func query(c *gin.Context) (interface{}, error) {
|
|||||||
db.Must().Where("gid", gid).Find(&branches)
|
db.Must().Where("gid", gid).Find(&branches)
|
||||||
return M{"transaction": trans, "branches": branches}, nil
|
return M{"transaction": trans, "branches": branches}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func all(c *gin.Context) (interface{}, error) {
|
||||||
|
lastId := c.Query("last_id")
|
||||||
|
if lastId == "" {
|
||||||
|
lastId = "2000000000"
|
||||||
|
}
|
||||||
|
lid := dtmcli.MustAtoi(lastId)
|
||||||
|
trans := []TransGlobal{}
|
||||||
|
dbGet().Must().Where("id < ?", lid).Order("id desc").Limit(100).Find(&trans)
|
||||||
|
return M{"transactions": trans}, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package dtmsvr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -222,6 +223,7 @@ func (t *TransGlobal) saveNew(db *common.DB) {
|
|||||||
branches := t.getProcessor().GenBranches()
|
branches := t.getProcessor().GenBranches()
|
||||||
if len(branches) > 0 {
|
if len(branches) > 0 {
|
||||||
writeTransLog(t.Gid, "save branches", t.Status, "", dtmcli.MustMarshalString(branches))
|
writeTransLog(t.Gid, "save branches", t.Status, "", dtmcli.MustMarshalString(branches))
|
||||||
|
checkLocalhost(branches)
|
||||||
db.Must().Clauses(clause.OnConflict{
|
db.Must().Clauses(clause.OnConflict{
|
||||||
DoNothing: true,
|
DoNothing: true,
|
||||||
}).Create(&branches)
|
}).Create(&branches)
|
||||||
@ -271,3 +273,14 @@ func TransFromDb(db *common.DB, gid string) *TransGlobal {
|
|||||||
e2p(dbr.Error)
|
e2p(dbr.Error)
|
||||||
return &m
|
return &m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkLocalhost(branches []TransBranch) {
|
||||||
|
if config.DisableLocalhost == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, branch := range branches {
|
||||||
|
if strings.HasPrefix(branch.URL, "http://localhost") || strings.HasPrefix(branch.URL, "localhost") {
|
||||||
|
panic(errors.New("url for localhost is disabled. check for your config"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -19,3 +19,16 @@ func TestUtils(t *testing.T) {
|
|||||||
CronExpiredTrans(1)
|
CronExpiredTrans(1)
|
||||||
sleepCronTime(10)
|
sleepCronTime(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckLocalHost(t *testing.T) {
|
||||||
|
config.DisableLocalhost = 1
|
||||||
|
err := dtmcli.CatchP(func() {
|
||||||
|
checkLocalhost([]TransBranch{{URL: "http://localhost"}})
|
||||||
|
})
|
||||||
|
assert.Error(t, err)
|
||||||
|
config.DisableLocalhost = 0
|
||||||
|
err = dtmcli.CatchP(func() {
|
||||||
|
checkLocalhost([]TransBranch{{URL: "http://localhost"}})
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|||||||
25
helper/compose.cloud.yml
Normal file
25
helper/compose.cloud.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
build: .
|
||||||
|
environment:
|
||||||
|
IS_DOCKER: 1
|
||||||
|
DISABLE_LOCALHOST: 1
|
||||||
|
RETRY_LIMIT: 6
|
||||||
|
ports:
|
||||||
|
- '9080:8080'
|
||||||
|
volumes:
|
||||||
|
- .:/app/dtm
|
||||||
|
mysql:
|
||||||
|
image: 'mysql:5.7'
|
||||||
|
environment:
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: 1
|
||||||
|
TZ: Asia/shanghai
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
'--character-set-server=utf8mb4',
|
||||||
|
'--collation-server=utf8mb4_unicode_ci',
|
||||||
|
'--default-time-zone=+8:00',
|
||||||
|
]
|
||||||
|
ports:
|
||||||
|
- '3306:3306'
|
||||||
@ -105,6 +105,9 @@ func transQuery(t *testing.T, gid string) {
|
|||||||
dtmcli.MustUnmarshalString(resp.String(), &m)
|
dtmcli.MustUnmarshalString(resp.String(), &m)
|
||||||
assert.Equal(t, nil, m["transaction"])
|
assert.Equal(t, nil, m["transaction"])
|
||||||
assert.Equal(t, 0, len(m["branches"].([]interface{})))
|
assert.Equal(t, 0, len(m["branches"].([]interface{})))
|
||||||
|
|
||||||
|
resp, err = dtmcli.RestyClient.R().Get(examples.DtmServer + "/all")
|
||||||
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlDB(t *testing.T) {
|
func TestSqlDB(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user