mirror of https://github.com/dtm-labs/dtm.git
14 changed files with 302 additions and 25 deletions
@ -0,0 +1,68 @@ |
|||
package dtmsvr |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
|
|||
"github.com/yedf/dtm/common" |
|||
) |
|||
|
|||
type TransMsgProcessor struct { |
|||
*TransGlobal |
|||
} |
|||
|
|||
func init() { |
|||
registorProcessorCreator("msg", func(trans *TransGlobal) TransProcessor { return &TransMsgProcessor{TransGlobal: trans} }) |
|||
} |
|||
|
|||
func (t *TransMsgProcessor) GenBranches() []TransBranch { |
|||
branches := []TransBranch{} |
|||
steps := []M{} |
|||
common.MustUnmarshalString(t.Data, &steps) |
|||
for _, step := range steps { |
|||
branches = append(branches, TransBranch{ |
|||
Gid: t.Gid, |
|||
Branch: fmt.Sprintf("%d", len(branches)+1), |
|||
Data: step["data"].(string), |
|||
Url: step["action"].(string), |
|||
BranchType: "action", |
|||
Status: "prepared", |
|||
}) |
|||
} |
|||
return branches |
|||
} |
|||
|
|||
func (t *TransMsgProcessor) ExecBranch(db *common.MyDb, branch *TransBranch) { |
|||
resp, err := common.RestyClient.R().SetBody(branch.Data).SetQueryParam("gid", branch.Gid).Post(branch.Url) |
|||
e2p(err) |
|||
body := resp.String() |
|||
t.touch(db) |
|||
if strings.Contains(body, "SUCCESS") { |
|||
branch.changeStatus(db, "succeed") |
|||
} else { |
|||
panic(fmt.Errorf("unknown response: %s, will be retried", body)) |
|||
} |
|||
} |
|||
|
|||
func (t *TransMsgProcessor) ProcessOnce(db *common.MyDb, branches []TransBranch) { |
|||
t.MayQueryPrepared(db) |
|||
if t.Status != "committed" { |
|||
return |
|||
} |
|||
current := 0 // 当前正在处理的步骤
|
|||
for ; current < len(branches); current++ { |
|||
branch := &branches[current] |
|||
if branch.BranchType != "action" || branch.Status != "prepared" { |
|||
continue |
|||
} |
|||
t.ExecBranch(db, branch) |
|||
if branch.Status != "succeed" { |
|||
break |
|||
} |
|||
} |
|||
if current == len(branches) { // msg 事务完成
|
|||
t.changeStatus(db, "succeed") |
|||
return |
|||
} |
|||
panic("msg go pass all branch") |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
package examples |
|||
|
|||
import ( |
|||
"fmt" |
|||
"time" |
|||
|
|||
"github.com/gin-gonic/gin" |
|||
"github.com/sirupsen/logrus" |
|||
"github.com/yedf/dtm" |
|||
"github.com/yedf/dtm/common" |
|||
) |
|||
|
|||
// 事务参与者的服务地址
|
|||
const MsgBusiPort = 8085 |
|||
const MsgBusiApi = "/api/busi_msg" |
|||
|
|||
var MsgBusi = fmt.Sprintf("http://localhost:%d%s", MsgBusiPort, MsgBusiApi) |
|||
|
|||
func MsgMain() { |
|||
go MsgStartSvr() |
|||
MsgFireRequest() |
|||
time.Sleep(1000 * time.Second) |
|||
} |
|||
|
|||
func MsgStartSvr() { |
|||
logrus.Printf("msg examples starting") |
|||
app := common.GetGinApp() |
|||
MsgAddRoute(app) |
|||
app.Run(fmt.Sprintf(":%d", MsgBusiPort)) |
|||
} |
|||
|
|||
func MsgFireRequest() { |
|||
gid := common.GenGid() |
|||
logrus.Printf("busi transaction begin: %s", gid) |
|||
req := &TransReq{ |
|||
Amount: 30, |
|||
TransInResult: "SUCCESS", |
|||
TransOutResult: "SUCCESS", |
|||
} |
|||
msg := dtm.MsgNew(DtmServer, gid). |
|||
Add(MsgBusi+"/TransOut", req). |
|||
Add(MsgBusi+"/TransIn", req) |
|||
err := msg.Prepare(MsgBusi + "/TransQuery") |
|||
e2p(err) |
|||
logrus.Printf("busi trans commit") |
|||
err = msg.Commit() |
|||
e2p(err) |
|||
} |
|||
|
|||
// api
|
|||
|
|||
func MsgAddRoute(app *gin.Engine) { |
|||
app.POST(MsgBusiApi+"/TransIn", common.WrapHandler(msgTransIn)) |
|||
app.POST(MsgBusiApi+"/TransOut", common.WrapHandler(MsgTransOut)) |
|||
app.GET(MsgBusiApi+"/TransQuery", common.WrapHandler(msgTransQuery)) |
|||
logrus.Printf("examples msg listening at %d", MsgBusiPort) |
|||
} |
|||
|
|||
var MsgTransInResult = "" |
|||
var MsgTransOutResult = "" |
|||
var MsgTransQueryResult = "" |
|||
|
|||
func msgTransIn(c *gin.Context) (interface{}, error) { |
|||
gid := c.Query("gid") |
|||
req := transReqFromContext(c) |
|||
res := common.OrString(MsgTransInResult, req.TransInResult, "SUCCESS") |
|||
logrus.Printf("%s TransIn: %v result: %s", gid, req, res) |
|||
return M{"result": res}, nil |
|||
} |
|||
|
|||
func MsgTransOut(c *gin.Context) (interface{}, error) { |
|||
gid := c.Query("gid") |
|||
req := transReqFromContext(c) |
|||
res := common.OrString(MsgTransOutResult, req.TransOutResult, "SUCCESS") |
|||
logrus.Printf("%s TransOut: %v result: %s", gid, req, res) |
|||
return M{"result": res}, nil |
|||
} |
|||
|
|||
func msgTransQuery(c *gin.Context) (interface{}, error) { |
|||
gid := c.Query("gid") |
|||
logrus.Printf("%s TransQuery", gid) |
|||
res := common.OrString(MsgTransQueryResult, "SUCCESS") |
|||
return M{"result": res}, nil |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
package dtm |
|||
|
|||
import ( |
|||
"fmt" |
|||
|
|||
"github.com/sirupsen/logrus" |
|||
"github.com/yedf/dtm/common" |
|||
) |
|||
|
|||
type Msg struct { |
|||
MsgData |
|||
Server string |
|||
} |
|||
|
|||
type MsgData struct { |
|||
Gid string `json:"gid"` |
|||
TransType string `json:"trans_type"` |
|||
Steps []MsgStep `json:"steps"` |
|||
QueryPrepared string `json:"query_prepared"` |
|||
} |
|||
type MsgStep struct { |
|||
Action string `json:"action"` |
|||
Data string `json:"data"` |
|||
} |
|||
|
|||
func MsgNew(server string, gid string) *Msg { |
|||
return &Msg{ |
|||
MsgData: MsgData{ |
|||
Gid: gid, |
|||
TransType: "msg", |
|||
}, |
|||
Server: server, |
|||
} |
|||
} |
|||
func (s *Msg) Add(action string, postData interface{}) *Msg { |
|||
logrus.Printf("msg %s Add %s %v", s.Gid, action, postData) |
|||
step := MsgStep{ |
|||
Action: action, |
|||
Data: common.MustMarshalString(postData), |
|||
} |
|||
s.Steps = append(s.Steps, step) |
|||
return s |
|||
} |
|||
|
|||
func (s *Msg) Commit() error { |
|||
logrus.Printf("committing %s body: %v", s.Gid, &s.MsgData) |
|||
resp, err := common.RestyClient.R().SetBody(&s.MsgData).Post(fmt.Sprintf("%s/commit", s.Server)) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if resp.StatusCode() != 200 { |
|||
return fmt.Errorf("commit failed: %v", resp.Body()) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (s *Msg) Prepare(queryPrepared string) error { |
|||
s.QueryPrepared = common.OrString(queryPrepared, s.QueryPrepared) |
|||
logrus.Printf("preparing %s body: %v", s.Gid, &s.MsgData) |
|||
resp, err := common.RestyClient.R().SetBody(&s.MsgData).Post(fmt.Sprintf("%s/prepare", s.Server)) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if resp.StatusCode() != 200 { |
|||
return fmt.Errorf("prepare failed: %v", resp.Body()) |
|||
} |
|||
return nil |
|||
} |
|||
Loading…
Reference in new issue