add cloud deploy

This commit is contained in:
yedf2 2021-09-14 20:15:57 +08:00
parent 344e2840bd
commit 7f18d0fb0d
8 changed files with 80 additions and 6 deletions

View File

@ -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

View File

@ -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使用子事务屏障技术简化您的业务编写。

View File

@ -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")

View File

@ -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
}

View File

@ -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"))
}
}
}

View File

@ -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
View 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'

View File

@ -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) {