| @@ -0,0 +1,8 @@ | |||||
| # Default ignored files | |||||
| /shelf/ | |||||
| /workspace.xml | |||||
| # Editor-based HTTP Client requests | |||||
| /httpRequests/ | |||||
| # Datasource local storage ignored files | |||||
| /dataSources/ | |||||
| /dataSources.local.xml | |||||
| @@ -0,0 +1,15 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="GitToolBoxProjectSettings"> | |||||
| <option name="commitMessageIssueKeyValidationOverride"> | |||||
| <BoolValueOverride> | |||||
| <option name="enabled" value="true" /> | |||||
| </BoolValueOverride> | |||||
| </option> | |||||
| <option name="commitMessageValidationConfigOverride"> | |||||
| <CommitMessageValidationOverride> | |||||
| <option name="enabled" value="true" /> | |||||
| </CommitMessageValidationOverride> | |||||
| </option> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,8 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="ProjectModuleManager"> | |||||
| <modules> | |||||
| <module fileurl="file://$PROJECT_DIR$/.idea/seata-go.iml" filepath="$PROJECT_DIR$/.idea/seata-go.iml" /> | |||||
| </modules> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,9 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <module type="WEB_MODULE" version="4"> | |||||
| <component name="Go" enabled="true" /> | |||||
| <component name="NewModuleRootManager"> | |||||
| <content url="file://$MODULE_DIR$" /> | |||||
| <orderEntry type="inheritedJdk" /> | |||||
| <orderEntry type="sourceFolder" forTests="false" /> | |||||
| </component> | |||||
| </module> | |||||
| @@ -0,0 +1,6 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="VcsDirectoryMappings"> | |||||
| <mapping directory="$PROJECT_DIR$" vcs="Git" /> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,5 @@ | |||||
| package main | |||||
| func main() { | |||||
| // start the server | |||||
| } | |||||
| @@ -0,0 +1,37 @@ | |||||
| # 配置类;io.seata.spring.boot.autoconfigure.StarterConstants | |||||
| seata: | |||||
| transport: | |||||
| type: "TCP" | |||||
| #NIO NATIVE | |||||
| server: "NIO" | |||||
| #enable heartbeat | |||||
| heartbeat: true | |||||
| # the client batch send request enable | |||||
| enableClientBatchSendRequest: true | |||||
| compressor: nome | |||||
| service: | |||||
| client: | |||||
| rm: | |||||
| asyncCommitBufferLimit: 10000 | |||||
| reportRetryCount: 5 | |||||
| tableMetaCheckEnable: false | |||||
| reportSuccessEnable: false | |||||
| sagaBranchRegisterEnable: 10000 | |||||
| sagaJsonParser: fastjson | |||||
| sagaRetryPersistModeUpdate: false | |||||
| sagaCompensatePersistModeUpdate: false | |||||
| tm: | |||||
| commitRetryCount: 5 | |||||
| rollbackRetryCount: 5 | |||||
| defaultGlobalTransactionTimeout: 60000 | |||||
| degradeCheck: false | |||||
| degradeCheckAllowTimes: 10 | |||||
| degradeCheckPeriod: 2000 | |||||
| undo: | |||||
| dataValidation: true | |||||
| logSerialization: jackson | |||||
| logTable: undo_log | |||||
| onlyCareUpdateColumns: true | |||||
| @@ -0,0 +1,4 @@ | |||||
| mode: atomic | |||||
| github.com/seata/seata-go/pkg/rm/tcc/tcc_resource.go:20.51,22.2 1 0 | |||||
| github.com/seata/seata-go/pkg/rm/tcc/tcc_resource.go:24.46,26.2 1 0 | |||||
| github.com/seata/seata-go/pkg/rm/tcc/tcc_resource.go:28.56,30.2 1 0 | |||||
| @@ -1,3 +1,18 @@ | |||||
| module github.com/seata/seata-go | module github.com/seata/seata-go | ||||
| go 1.16 | go 1.16 | ||||
| require ( | |||||
| github.com/BurntSushi/toml v1.1.0 // indirect | |||||
| github.com/apache/dubbo-getty v1.4.8 | |||||
| github.com/dubbogo/gost v1.11.23 | |||||
| github.com/dubbogo/tools v1.0.9 // indirect | |||||
| github.com/golang/snappy v0.0.4 // indirect | |||||
| github.com/natefinch/lumberjack v2.0.0+incompatible | |||||
| github.com/pkg/errors v0.9.1 | |||||
| go.uber.org/atomic v1.9.0 | |||||
| go.uber.org/zap v1.19.1 | |||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | |||||
| vimagination.zapto.org/byteio v0.0.0-20200222190125-d27cba0f0b10 | |||||
| vimagination.zapto.org/memio v0.0.0-20200222190306-588ebc67b97d // indirect | |||||
| ) | |||||
| @@ -0,0 +1,58 @@ | |||||
| VERSION=$(shell cat "./VERSION" 2> /dev/null) | |||||
| GO_FLAGS := -ldflags "-X main.Branch=$(GIT_BRANCH) -X main.Revision=$(GIT_REVISION) -X main.Version=$(VERSION) -extldflags \"-static\" -s -w" -tags netgo | |||||
| GO = go | |||||
| GO_PATH = $(shell $(GO) env GOPATH) | |||||
| GO_OS = $(shell $(GO) env GOOS) | |||||
| ifeq ($(GO_OS), darwin) | |||||
| GO_OS = mac | |||||
| endif | |||||
| # License environment | |||||
| GO_LICENSE_CHECKER_DIR = license-header-checker-$(GO_OS) | |||||
| GO_LICENSE_CHECKER = $(GO_PATH)/bin/license-header-checker | |||||
| LICENSE_DIR = /tmp/tools/license | |||||
| # format import code | |||||
| format-import: | |||||
| go get -d github.com/dubbogo/tools/cmd/imports-formatter | |||||
| imports-formatter -path . -module github.com/seata/seata-go -bl false | |||||
| unit-test: | |||||
| go test ./pkg/... -coverprofile=coverage.txt -covermode=atomic | |||||
| # Generate binaries for a Cortex release | |||||
| dist dist/seatago-linux-amd64 dist/seatago-darwin-amd64 dist/seatago-linux-amd64-sha-256 dist/seatago-darwin-amd64-sha-256: | |||||
| rm -fr ./dist | |||||
| mkdir -p ./dist | |||||
| GOOS="linux" GOARCH="amd64" CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago-linux-amd64 ./cmd | |||||
| GOOS="darwin" GOARCH="amd64" CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago-darwin-amd64 ./cmd | |||||
| sha256sum ./dist/seatago-darwin-amd64 | cut -d ' ' -f 1 > ./dist/seatago-darwin-amd64-sha-256 | |||||
| sha256sum ./dist/seatago-linux-amd64 | cut -d ' ' -f 1 > ./dist/seatago-linux-amd64-sha-256 | |||||
| # Generate binaries for a Cortex release | |||||
| build dist/seatago dist/seatago-sha-256: | |||||
| rm -fr ./dist | |||||
| mkdir -p ./dist | |||||
| CGO_ENABLED=0 go build $(GO_FLAGS) -o ./dist/seatago ./cmd | |||||
| sha256sum ./dist/seatago | cut -d ' ' -f 1 > ./dist/seatago-sha-256 | |||||
| #docker-build: | |||||
| # docker build -t seatago/seatago:latest . | |||||
| integration-test: | |||||
| @go clean -testcache | |||||
| go test -tags integration -v ./test/... | |||||
| clean: | |||||
| @rm -rf coverage.txt | |||||
| @rm -rf dist | |||||
| prepareLic: | |||||
| echo 'The makefile is for ci test and has dependencies. Do not run it locally. If you want to run the unit tests, run command `go test ./...` directly.' | |||||
| $(GO_LICENSE_CHECKER) -version || (wget https://github.com/lsm-dev/license-header-checker/releases/download/v1.2.0/$(GO_LICENSE_CHECKER_DIR).zip -O $(GO_LICENSE_CHECKER_DIR).zip && unzip -o $(GO_LICENSE_CHECKER_DIR).zip && mkdir -p $(GO_PATH)/bin/ && cp $(GO_LICENSE_CHECKER_DIR)/64bit/license-header-checker $(GO_PATH)/bin/) | |||||
| ls /tmp/tools/license/license.txt || wget -P $(LICENSE_DIR) https://github.com/dubbogo/resources/raw/master/tools/license/license.txt | |||||
| .PHONY: license | |||||
| license: prepareLic | |||||
| $(GO_LICENSE_CHECKER) -v -a -r -i vendor $(LICENSE_DIR)/license.txt . go && [[ -z `git status -s` ]] | |||||
| @@ -0,0 +1,51 @@ | |||||
| package config | |||||
| import ( | |||||
| "time" | |||||
| ) | |||||
| var clientConfig *ClientConfig | |||||
| type ClientConfig struct { | |||||
| ApplicationID string `yaml:"application_id" json:"application_id,omitempty"` | |||||
| TransactionServiceGroup string `yaml:"transaction_service_group" json:"transaction_service_group,omitempty"` | |||||
| EnableClientBatchSendRequest bool `yaml:"enable-rpc_client-batch-send-request" json:"enable-rpc_client-batch-send-request,omitempty"` | |||||
| SeataVersion string `yaml:"seata_version" json:"seata_version,omitempty"` | |||||
| GettyConfig GettyConfig `yaml:"getty" json:"getty,omitempty"` | |||||
| TMConfig TMConfig `yaml:"tm" json:"tm,omitempty"` | |||||
| ATConfig struct { | |||||
| DSN string `yaml:"dsn" json:"dsn,omitempty"` | |||||
| ReportRetryCount int `default:"5" yaml:"report_retry_count" json:"report_retry_count,omitempty"` | |||||
| ReportSuccessEnable bool `default:"false" yaml:"report_success_enable" json:"report_success_enable,omitempty"` | |||||
| LockRetryInterval time.Duration `default:"10ms" yaml:"lock_retry_interval" json:"lock_retry_interval,omitempty"` | |||||
| LockRetryTimes int `default:"30" yaml:"lock_retry_times" json:"lock_retry_times,omitempty"` | |||||
| } `yaml:"at" json:"at,omitempty"` | |||||
| RegistryConfig RegistryConfig `yaml:"registry_config" json:"registry_config,omitempty"` //注册中心配置信息 | |||||
| ConfigCenterConfig ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"` //配置中心配置信息 | |||||
| } | |||||
| func GetClientConfig() *ClientConfig { | |||||
| // todo mock data | |||||
| //return clientConfig | |||||
| return &ClientConfig{ | |||||
| GettyConfig: GetDefaultGettyConfig(), | |||||
| } | |||||
| } | |||||
| func GetTMConfig() TMConfig { | |||||
| return clientConfig.TMConfig | |||||
| } | |||||
| func GetDefaultClientConfig(applicationID string) ClientConfig { | |||||
| return ClientConfig{ | |||||
| ApplicationID: applicationID, | |||||
| TransactionServiceGroup: "127.0.0.1:8091", | |||||
| EnableClientBatchSendRequest: false, | |||||
| SeataVersion: "1.1.0", | |||||
| GettyConfig: GetDefaultGettyConfig(), | |||||
| TMConfig: GetDefaultTmConfig(), | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,31 @@ | |||||
| package config | |||||
| import ( | |||||
| "time" | |||||
| ) | |||||
| // ConfigCenterConfig config center config | |||||
| type ConfigCenterConfig struct { | |||||
| Mode string `yaml:"type" json:"type,omitempty"` //类型 | |||||
| NacosConfig NacosConfigCenter `yaml:"nacos" json:"nacos,omitempty"` | |||||
| ETCDConfig EtcdConfigCenter `yaml:"etcdv3" json:"etcdv3,omitempty"` | |||||
| } | |||||
| // NacosConfigCenter nacos config center | |||||
| type NacosConfigCenter struct { | |||||
| ServerAddr string `yaml:"server_addr" json:"server_addr,omitempty"` | |||||
| Group string `default:"SEATA_GROUP" yaml:"group" json:"group,omitempty"` | |||||
| Namespace string `yaml:"namespace" json:"namespace,omitempty"` | |||||
| Cluster string `yaml:"cluster" json:"cluster,omitempty"` | |||||
| UserName string `yaml:"username" json:"username,omitempty"` | |||||
| Password string `yaml:"password" json:"password,omitempty"` | |||||
| DataID string `default:"seata" yaml:"data_id" json:"data_id,omitempty"` | |||||
| } | |||||
| type EtcdConfigCenter struct { | |||||
| Name string `default:"seata-config-center" yaml:"name" json:"name"` | |||||
| ConfigKey string `default:"config-seata" yaml:"config_key" json:"config_key,omitempty"` | |||||
| Endpoints string `yaml:"endpoints" json:"endpoints,omitempty"` | |||||
| Heartbeats int `yaml:"heartbeats" json:"heartbeats"` | |||||
| Timeout time.Duration `yaml:"timeout" json:"timeout"` | |||||
| } | |||||
| @@ -0,0 +1,56 @@ | |||||
| package config | |||||
| import ( | |||||
| "time" | |||||
| ) | |||||
| // GettyConfig | |||||
| //Config holds supported types by the multiconfig package | |||||
| type GettyConfig struct { | |||||
| ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` | |||||
| // getty_session pool | |||||
| ConnectionNum int `default:"16" yaml:"connection_number" json:"connection_number,omitempty"` | |||||
| // heartbeat | |||||
| HeartbeatPeriod time.Duration `default:"15s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"` | |||||
| // getty_session tcp parameters | |||||
| GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` | |||||
| } | |||||
| // GetDefaultGettyConfig ... | |||||
| func GetDefaultGettyConfig() GettyConfig { | |||||
| return GettyConfig{ | |||||
| ReconnectInterval: 0, | |||||
| ConnectionNum: 2, | |||||
| HeartbeatPeriod: 10 * time.Second, | |||||
| GettySessionParam: GettySessionParam{ | |||||
| CompressEncoding: false, | |||||
| TCPNoDelay: true, | |||||
| TCPKeepAlive: true, | |||||
| KeepAlivePeriod: 180 * time.Second, | |||||
| TCPRBufSize: 262144, | |||||
| TCPWBufSize: 65536, | |||||
| TCPReadTimeout: time.Second, | |||||
| TCPWriteTimeout: 5 * time.Second, | |||||
| WaitTimeout: time.Second, | |||||
| MaxMsgLen: 4096, | |||||
| SessionName: "rpc_client", | |||||
| }, | |||||
| } | |||||
| } | |||||
| // GettySessionParam getty session param | |||||
| type GettySessionParam struct { | |||||
| CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` | |||||
| TCPNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` | |||||
| TCPKeepAlive bool `default:"true" yaml:"tcp_keep_alive" json:"tcp_keep_alive,omitempty"` | |||||
| KeepAlivePeriod time.Duration `default:"180s" yaml:"keep_alive_period" json:"keep_alive_period,omitempty"` | |||||
| TCPRBufSize int `default:"262144" yaml:"tcp_r_buf_size" json:"tcp_r_buf_size,omitempty"` | |||||
| TCPWBufSize int `default:"65536" yaml:"tcp_w_buf_size" json:"tcp_w_buf_size,omitempty"` | |||||
| TCPReadTimeout time.Duration `default:"1s" yaml:"tcp_read_timeout" json:"tcp_read_timeout,omitempty"` | |||||
| TCPWriteTimeout time.Duration `default:"5s" yaml:"tcp_write_timeout" json:"tcp_write_timeout,omitempty"` | |||||
| WaitTimeout time.Duration `default:"7s" yaml:"wait_timeout" json:"wait_timeout,omitempty"` | |||||
| MaxMsgLen int `default:"4096" yaml:"max_msg_len" json:"max_msg_len,omitempty"` | |||||
| SessionName string `default:"rpc" yaml:"session_name" json:"session_name,omitempty"` | |||||
| } | |||||
| @@ -0,0 +1,42 @@ | |||||
| package config | |||||
| import ( | |||||
| "time" | |||||
| ) | |||||
| var config *RegistryConfig | |||||
| // RegistryConfig registry config | |||||
| type RegistryConfig struct { | |||||
| Mode string `yaml:"type" json:"type,omitempty"` //类型 | |||||
| NacosConfig NacosConfig `yaml:"nacos" json:"nacos,omitempty"` | |||||
| EtcdConfig EtcdConfig `yaml:"etcdv3" json:"etcdv3"` | |||||
| } | |||||
| // NacosConfig nacos config | |||||
| type NacosConfig struct { | |||||
| Application string `yaml:"application" json:"application,omitempty"` | |||||
| ServerAddr string `yaml:"server_addr" json:"server_addr,omitempty"` | |||||
| Group string `default:"SEATA_GROUP" yaml:"group" json:"group,omitempty"` | |||||
| Namespace string `yaml:"namespace" json:"namespace,omitempty"` | |||||
| Cluster string `yaml:"cluster" json:"cluster,omitempty"` | |||||
| UserName string `yaml:"username" json:"username,omitempty"` | |||||
| Password string `yaml:"password" json:"password,omitempty"` | |||||
| } | |||||
| // InitRegistryConfig init registry config | |||||
| func InitRegistryConfig(registryConfig *RegistryConfig) { | |||||
| config = registryConfig | |||||
| } | |||||
| // GetRegistryConfig get registry config | |||||
| func GetRegistryConfig() *RegistryConfig { | |||||
| return config | |||||
| } | |||||
| type EtcdConfig struct { | |||||
| ClusterName string `default:"seata-golang-etcdv3" yaml:"cluster_name" json:"cluster_name,omitempty"` | |||||
| Endpoints string `yaml:"endpoints" json:"endpoints,omitempty"` | |||||
| Heartbeats int `yaml:"heartbeats" json:"heartbeats"` | |||||
| Timeout time.Duration `yaml:"timeout" json:"timeout"` | |||||
| } | |||||
| @@ -0,0 +1,13 @@ | |||||
| package config | |||||
| type TMConfig struct { | |||||
| CommitRetryCount int32 `default:"5" yaml:"commit_retry_count" json:"commit_retry_count,omitempty"` | |||||
| RollbackRetryCount int32 `default:"5" yaml:"rollback_retry_count" json:"rollback_retry_count,omitempty"` | |||||
| } | |||||
| func GetDefaultTmConfig() TMConfig { | |||||
| return TMConfig{ | |||||
| CommitRetryCount: 5, | |||||
| RollbackRetryCount: 5, | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,112 @@ | |||||
| package model | |||||
| import ( | |||||
| "fmt" | |||||
| ) | |||||
| type BranchType int8 | |||||
| type BranchStatus int8 | |||||
| const ( | |||||
| AT BranchType = 0 | |||||
| TCC BranchType = 1 | |||||
| SAGA BranchType = 2 | |||||
| XA BranchType = 3 | |||||
| ) | |||||
| const ( | |||||
| /** | |||||
| * The BranchStatus_Unknown. | |||||
| * description:BranchStatus_Unknown branch status. | |||||
| */ | |||||
| BranchStatusUnknown BranchStatus = iota | |||||
| /** | |||||
| * The BranchStatus_Registered. | |||||
| * description:BranchStatus_Registered to TC. | |||||
| */ | |||||
| BranchStatusRegistered | |||||
| /** | |||||
| * The Phase one done. | |||||
| * description:Branch logic is successfully done at phase one. | |||||
| */ | |||||
| BranchStatusPhaseoneDone | |||||
| /** | |||||
| * The Phase one failed. | |||||
| * description:Branch logic is failed at phase one. | |||||
| */ | |||||
| BranchStatusPhaseoneFailed | |||||
| /** | |||||
| * The Phase one timeout. | |||||
| * description:Branch logic is NOT reported for a timeout. | |||||
| */ | |||||
| BranchStatusPhaseoneTimeout | |||||
| /** | |||||
| * The Phase two committed. | |||||
| * description:Commit logic is successfully done at phase two. | |||||
| */ | |||||
| BranchStatusPhasetwoCommitted | |||||
| /** | |||||
| * The Phase two commit failed retryable. | |||||
| * description:Commit logic is failed but retryable. | |||||
| */ | |||||
| BranchStatusPhasetwoCommitFailedRetryable | |||||
| /** | |||||
| * The Phase two commit failed unretryable. | |||||
| * description:Commit logic is failed and NOT retryable. | |||||
| */ | |||||
| BranchStatusPhasetwoCommitFailedUnretryable | |||||
| /** | |||||
| * The Phase two rollbacked. | |||||
| * description:Rollback logic is successfully done at phase two. | |||||
| */ | |||||
| BranchStatusPhasetwoRollbacked | |||||
| /** | |||||
| * The Phase two rollback failed retryable. | |||||
| * description:Rollback logic is failed but retryable. | |||||
| */ | |||||
| BranchStatusPhasetwoRollbackFailedRetryable | |||||
| /** | |||||
| * The Phase two rollback failed unretryable. | |||||
| * description:Rollback logic is failed but NOT retryable. | |||||
| */ | |||||
| BranchStatusPhasetwoRollbackFailedUnretryable | |||||
| ) | |||||
| func (s BranchStatus) String() string { | |||||
| switch s { | |||||
| case BranchStatusUnknown: | |||||
| return "Unknown" | |||||
| case BranchStatusRegistered: | |||||
| return "Registered" | |||||
| case BranchStatusPhaseoneDone: | |||||
| return "PhaseoneDone" | |||||
| case BranchStatusPhaseoneFailed: | |||||
| return "PhaseoneFailed" | |||||
| case BranchStatusPhaseoneTimeout: | |||||
| return "PhaseoneTimeout" | |||||
| case BranchStatusPhasetwoCommitted: | |||||
| return "PhasetwoCommitted" | |||||
| case BranchStatusPhasetwoCommitFailedRetryable: | |||||
| return "PhasetwoCommitFailedRetryable" | |||||
| case BranchStatusPhasetwoCommitFailedUnretryable: | |||||
| return "CommitFailedUnretryable" | |||||
| case BranchStatusPhasetwoRollbacked: | |||||
| return "PhasetwoRollbacked" | |||||
| case BranchStatusPhasetwoRollbackFailedRetryable: | |||||
| return "RollbackFailedRetryable" | |||||
| case BranchStatusPhasetwoRollbackFailedUnretryable: | |||||
| return "RollbackFailedUnretryable" | |||||
| default: | |||||
| return fmt.Sprintf("%d", s) | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,102 @@ | |||||
| package model | |||||
| type GlobalStatus int64 | |||||
| const ( | |||||
| /** | |||||
| * Un known global status. | |||||
| */ | |||||
| // Unknown | |||||
| UnKnown GlobalStatus = 0 | |||||
| /** | |||||
| * The Begin. | |||||
| */ | |||||
| // PHASE 1: can accept new branch registering. | |||||
| Begin GlobalStatus = 1 | |||||
| /** | |||||
| * PHASE 2: Running Status: may be changed any time. | |||||
| */ | |||||
| // Committing. | |||||
| Committing GlobalStatus = 2 | |||||
| /** | |||||
| * The Commit retrying. | |||||
| */ | |||||
| // Retrying commit after a recoverable failure. | |||||
| CommitRetrying GlobalStatus = 3 | |||||
| /** | |||||
| * Rollbacking global status. | |||||
| */ | |||||
| // Rollbacking | |||||
| Rollbacking GlobalStatus = 4 | |||||
| /** | |||||
| * The Rollback retrying. | |||||
| */ | |||||
| // Retrying rollback after a recoverable failure. | |||||
| RollbackRetrying GlobalStatus = 5 | |||||
| /** | |||||
| * The Timeout rollbacking. | |||||
| */ | |||||
| // Rollbacking since timeout | |||||
| TimeoutRollbacking GlobalStatus = 6 | |||||
| /** | |||||
| * The Timeout rollback retrying. | |||||
| */ | |||||
| // Retrying rollback GlobalStatus = since timeout) after a recoverable failure. | |||||
| TimeoutRollbackRetrying GlobalStatus = 7 | |||||
| /** | |||||
| * All branches can be async committed. The committing is NOT done yet, but it can be seen as committed for TM/RM | |||||
| * client. | |||||
| */ | |||||
| AsyncCommitting GlobalStatus = 8 | |||||
| /** | |||||
| * PHASE 2: Final Status: will NOT change any more. | |||||
| */ | |||||
| // Finally: global transaction is successfully committed. | |||||
| Committed GlobalStatus = 9 | |||||
| /** | |||||
| * The Commit failed. | |||||
| */ | |||||
| // Finally: failed to commit | |||||
| CommitFailed GlobalStatus = 10 | |||||
| /** | |||||
| * The Rollbacked. | |||||
| */ | |||||
| // Finally: global transaction is successfully rollbacked. | |||||
| Rollbacked GlobalStatus = 11 | |||||
| /** | |||||
| * The Rollback failed. | |||||
| */ | |||||
| // Finally: failed to rollback | |||||
| RollbackFailed GlobalStatus = 12 | |||||
| /** | |||||
| * The Timeout rollbacked. | |||||
| */ | |||||
| // Finally: global transaction is successfully rollbacked since timeout. | |||||
| TimeoutRollbacked GlobalStatus = 13 | |||||
| /** | |||||
| * The Timeout rollback failed. | |||||
| */ | |||||
| // Finally: failed to rollback since timeout | |||||
| TimeoutRollbackFailed GlobalStatus = 14 | |||||
| /** | |||||
| * The Finished. | |||||
| */ | |||||
| // Not managed in session MAP any more | |||||
| Finished GlobalStatus = 15 | |||||
| ) | |||||
| @@ -0,0 +1,41 @@ | |||||
| package model | |||||
| // Resource that can be managed by Resource Manager and involved into global transaction | |||||
| type Resource interface { | |||||
| GetResourceGroupId() string | |||||
| GetResourceId() string | |||||
| GetBranchType() BranchType | |||||
| } | |||||
| // Control a branch transaction commit or rollback | |||||
| type ResourceManagerInbound interface { | |||||
| // Commit a branch transaction | |||||
| BranchCommit(branchType BranchType, xid, branchId int64, resourceId, applicationData string) (BranchStatus, error) | |||||
| // Rollback a branch transaction | |||||
| BranchRollback(branchType BranchType, xid string, branchId int64, resourceId, applicationData string) (BranchStatus, error) | |||||
| } | |||||
| // Resource Manager: send outbound request to TC | |||||
| type ResourceManagerOutbound interface { | |||||
| // Branch register long | |||||
| BranchRegister(branchType BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) | |||||
| // Branch report | |||||
| BranchReport(branchType BranchType, xid string, branchId int64, status BranchStatus, applicationData string) error | |||||
| // Lock query boolean | |||||
| LockQuery(branchType BranchType, resourceId, xid, lockKeys string) (bool, error) | |||||
| } | |||||
| // Resource Manager: common behaviors | |||||
| type ResourceManager interface { | |||||
| ResourceManagerInbound | |||||
| ResourceManagerOutbound | |||||
| // Register a Resource to be managed by Resource Manager | |||||
| RegisterResource(resource Resource) error | |||||
| // Unregister a Resource from the Resource Manager | |||||
| UnregisterResource(resource Resource) error | |||||
| // Get all resources managed by this manager | |||||
| GetManagedResources() map[string]Resource | |||||
| // Get the BranchType | |||||
| GetBranchType() BranchType | |||||
| } | |||||
| @@ -0,0 +1,110 @@ | |||||
| package model | |||||
| type TransactionExceptionCode byte | |||||
| const ( | |||||
| /** | |||||
| * Unknown transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeUnknown TransactionExceptionCode = iota | |||||
| /** | |||||
| * BeginFailed | |||||
| */ | |||||
| TransactionExceptionCodeBeginFailed | |||||
| /** | |||||
| * Lock key conflict transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeLockKeyConflict | |||||
| /** | |||||
| * Io transaction exception code. | |||||
| */ | |||||
| IO | |||||
| /** | |||||
| * Branch rollback failed retriable transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeBranchRollbackFailedRetriable | |||||
| /** | |||||
| * Branch rollback failed unretriable transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeBranchRollbackFailedUnretriable | |||||
| /** | |||||
| * Branch register failed transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeBranchRegisterFailed | |||||
| /** | |||||
| * Branch report failed transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeBranchReportFailed | |||||
| /** | |||||
| * Lockable check failed transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeLockableCheckFailed | |||||
| /** | |||||
| * Branch transaction not exist transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeBranchTransactionNotExist | |||||
| /** | |||||
| * Global transaction not exist transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeGlobalTransactionNotExist | |||||
| /** | |||||
| * Global transaction not active transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeGlobalTransactionNotActive | |||||
| /** | |||||
| * Global transaction status invalid transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeGlobalTransactionStatusInvalid | |||||
| /** | |||||
| * Failed to send branch commit request transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeFailedToSendBranchCommitRequest | |||||
| /** | |||||
| * Failed to send branch rollback request transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeFailedToSendBranchRollbackRequest | |||||
| /** | |||||
| * Failed to add branch transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeFailedToAddBranch | |||||
| /** | |||||
| * Failed to lock global transaction exception code. | |||||
| */ | |||||
| TransactionExceptionCodeFailedLockGlobalTranscation | |||||
| /** | |||||
| * FailedWriteSession | |||||
| */ | |||||
| TransactionExceptionCodeFailedWriteSession | |||||
| /** | |||||
| * Failed to holder exception code | |||||
| */ | |||||
| FailedStore | |||||
| ) | |||||
| type TransactionException struct { | |||||
| Code TransactionExceptionCode | |||||
| Message string | |||||
| } | |||||
| //Error 隐式继承 builtin.error 接口 | |||||
| func (e TransactionException) Error() string { | |||||
| return "TransactionException: " + e.Message | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| package model | |||||
| type TransactionManager interface { | |||||
| // Begin a new global transaction. | |||||
| Begin(applicationId, transactionServiceGroup, name string, timeout int64) (string, error) | |||||
| // Global commit. | |||||
| Commit(xid string) (GlobalStatus, error) | |||||
| //Global rollback. | |||||
| Rollback(xid string) (GlobalStatus, error) | |||||
| // Get current status of the give transaction. | |||||
| GetStatus(xid string) (GlobalStatus, error) | |||||
| // Global report. | |||||
| GlobalReport(xid string, globalStatus GlobalStatus) (GlobalStatus, error) | |||||
| } | |||||
| @@ -0,0 +1,247 @@ | |||||
| package codec | |||||
| import ( | |||||
| "bytes" | |||||
| ) | |||||
| import ( | |||||
| "vimagination.zapto.org/byteio" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| log "github.com/seata/seata-go/pkg/util/log" | |||||
| ) | |||||
| type SerializerType byte | |||||
| const ( | |||||
| SEATA = byte(0x1) | |||||
| PROTOBUF = byte(0x2) | |||||
| KRYO = byte(0x4) | |||||
| FST = byte(0x8) | |||||
| ) | |||||
| type Encoder func(in interface{}) []byte | |||||
| type Decoder func(in []byte) (interface{}, int) | |||||
| func MessageEncoder(codecType byte, in interface{}) []byte { | |||||
| switch codecType { | |||||
| case SEATA: | |||||
| return SeataEncoder(in) | |||||
| default: | |||||
| log.Errorf("not support codecType, %s", codecType) | |||||
| return nil | |||||
| } | |||||
| } | |||||
| func MessageDecoder(codecType byte, in []byte) (interface{}, int) { | |||||
| switch codecType { | |||||
| case SEATA: | |||||
| return SeataDecoder(in) | |||||
| default: | |||||
| log.Errorf("not support codecType, %s", codecType) | |||||
| return nil, 0 | |||||
| } | |||||
| } | |||||
| func SeataEncoder(in interface{}) []byte { | |||||
| var result = make([]byte, 0) | |||||
| msg := in.(protocol.MessageTypeAware) | |||||
| typeCode := msg.GetTypeCode() | |||||
| encoder := getMessageEncoder(typeCode) | |||||
| typeC := uint16(typeCode) | |||||
| if encoder != nil { | |||||
| body := encoder(in) | |||||
| result = append(result, []byte{byte(typeC >> 8), byte(typeC)}...) | |||||
| result = append(result, body...) | |||||
| } | |||||
| return result | |||||
| } | |||||
| func SeataDecoder(in []byte) (interface{}, int) { | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| typeCode, _, _ := r.ReadInt16() | |||||
| decoder := getMessageDecoder(typeCode) | |||||
| if decoder != nil { | |||||
| return decoder(in[2:]) | |||||
| } | |||||
| return nil, 0 | |||||
| } | |||||
| func getMessageEncoder(typeCode int16) Encoder { | |||||
| switch typeCode { | |||||
| case protocol.TypeSeataMerge: | |||||
| return MergedWarpMessageEncoder | |||||
| case protocol.TypeSeataMergeResult: | |||||
| return MergeResultMessageEncoder | |||||
| case protocol.TypeRegClt: | |||||
| return RegisterTMRequestEncoder | |||||
| case protocol.TypeRegCltResult: | |||||
| return RegisterTMResponseEncoder | |||||
| case protocol.TypeRegRm: | |||||
| return RegisterRMRequestEncoder | |||||
| case protocol.TypeRegRmResult: | |||||
| return RegisterRMResponseEncoder | |||||
| case protocol.TypeBranchCommit: | |||||
| return BranchCommitRequestEncoder | |||||
| case protocol.TypeBranchRollback: | |||||
| return BranchRollbackRequestEncoder | |||||
| case protocol.TypeGlobalReport: | |||||
| return GlobalReportRequestEncoder | |||||
| default: | |||||
| var encoder Encoder | |||||
| encoder = getMergeRequestMessageEncoder(typeCode) | |||||
| if encoder != nil { | |||||
| return encoder | |||||
| } | |||||
| encoder = getMergeResponseMessageEncoder(typeCode) | |||||
| if encoder != nil { | |||||
| return encoder | |||||
| } | |||||
| log.Errorf("not support typeCode, %d", typeCode) | |||||
| return nil | |||||
| } | |||||
| } | |||||
| func getMergeRequestMessageEncoder(typeCode int16) Encoder { | |||||
| switch typeCode { | |||||
| case protocol.TypeGlobalBegin: | |||||
| return GlobalBeginRequestEncoder | |||||
| case protocol.TypeGlobalCommit: | |||||
| return GlobalCommitRequestEncoder | |||||
| case protocol.TypeGlobalRollback: | |||||
| return GlobalRollbackRequestEncoder | |||||
| case protocol.TypeGlobalStatus: | |||||
| return GlobalStatusRequestEncoder | |||||
| case protocol.TypeGlobalLockQuery: | |||||
| return GlobalLockQueryRequestEncoder | |||||
| case protocol.TypeBranchRegister: | |||||
| return BranchRegisterRequestEncoder | |||||
| case protocol.TypeBranchStatusReport: | |||||
| return BranchReportRequestEncoder | |||||
| case protocol.TypeGlobalReport: | |||||
| return GlobalReportRequestEncoder | |||||
| default: | |||||
| break | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func getMergeResponseMessageEncoder(typeCode int16) Encoder { | |||||
| switch typeCode { | |||||
| case protocol.TypeGlobalBeginResult: | |||||
| return GlobalBeginResponseEncoder | |||||
| case protocol.TypeGlobalCommitResult: | |||||
| return GlobalCommitResponseEncoder | |||||
| case protocol.TypeGlobalRollbackResult: | |||||
| return GlobalRollbackResponseEncoder | |||||
| case protocol.TypeGlobalStatusResult: | |||||
| return GlobalStatusResponseEncoder | |||||
| case protocol.TypeGlobalLockQueryResult: | |||||
| return GlobalLockQueryResponseEncoder | |||||
| case protocol.TypeBranchRegisterResult: | |||||
| return BranchRegisterResponseEncoder | |||||
| case protocol.TypeBranchStatusReportResult: | |||||
| return BranchReportResponseEncoder | |||||
| case protocol.TypeBranchCommitResult: | |||||
| return BranchCommitResponseEncoder | |||||
| case protocol.TypeBranchRollbackResult: | |||||
| return BranchRollbackResponseEncoder | |||||
| case protocol.TypeGlobalReportResult: | |||||
| return GlobalReportResponseEncoder | |||||
| default: | |||||
| break | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func getMessageDecoder(typeCode int16) Decoder { | |||||
| switch typeCode { | |||||
| case protocol.TypeSeataMerge: | |||||
| return MergedWarpMessageDecoder | |||||
| case protocol.TypeSeataMergeResult: | |||||
| return MergeResultMessageDecoder | |||||
| case protocol.TypeRegClt: | |||||
| return RegisterTMRequestDecoder | |||||
| case protocol.TypeRegCltResult: | |||||
| return RegisterTMResponseDecoder | |||||
| case protocol.TypeRegRm: | |||||
| return RegisterRMRequestDecoder | |||||
| case protocol.TypeRegRmResult: | |||||
| return RegisterRMResponseDecoder | |||||
| case protocol.TypeBranchCommit: | |||||
| return BranchCommitRequestDecoder | |||||
| case protocol.TypeBranchRollback: | |||||
| return BranchRollbackRequestDecoder | |||||
| case protocol.TypeGlobalReport: | |||||
| return GlobalReportRequestDecoder | |||||
| default: | |||||
| var Decoder Decoder | |||||
| Decoder = getMergeRequestMessageDecoder(typeCode) | |||||
| if Decoder != nil { | |||||
| return Decoder | |||||
| } | |||||
| Decoder = getMergeResponseMessageDecoder(typeCode) | |||||
| if Decoder != nil { | |||||
| return Decoder | |||||
| } | |||||
| log.Errorf("not support typeCode, %d", typeCode) | |||||
| return nil | |||||
| } | |||||
| } | |||||
| func getMergeRequestMessageDecoder(typeCode int16) Decoder { | |||||
| switch typeCode { | |||||
| case protocol.TypeGlobalBegin: | |||||
| return GlobalBeginRequestDecoder | |||||
| case protocol.TypeGlobalCommit: | |||||
| return GlobalCommitRequestDecoder | |||||
| case protocol.TypeGlobalRollback: | |||||
| return GlobalRollbackRequestDecoder | |||||
| case protocol.TypeGlobalStatus: | |||||
| return GlobalStatusRequestDecoder | |||||
| case protocol.TypeGlobalLockQuery: | |||||
| return GlobalLockQueryRequestDecoder | |||||
| case protocol.TypeBranchRegister: | |||||
| return BranchRegisterRequestDecoder | |||||
| case protocol.TypeBranchStatusReport: | |||||
| return BranchReportRequestDecoder | |||||
| case protocol.TypeGlobalReport: | |||||
| return GlobalReportRequestDecoder | |||||
| default: | |||||
| break | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func getMergeResponseMessageDecoder(typeCode int16) Decoder { | |||||
| switch typeCode { | |||||
| case protocol.TypeGlobalBeginResult: | |||||
| return GlobalBeginResponseDecoder | |||||
| case protocol.TypeGlobalCommitResult: | |||||
| return GlobalCommitResponseDecoder | |||||
| case protocol.TypeGlobalRollbackResult: | |||||
| return GlobalRollbackResponseDecoder | |||||
| case protocol.TypeGlobalStatusResult: | |||||
| return GlobalStatusResponseDecoder | |||||
| case protocol.TypeGlobalLockQueryResult: | |||||
| return GlobalLockQueryResponseDecoder | |||||
| case protocol.TypeBranchRegisterResult: | |||||
| return BranchRegisterResponseDecoder | |||||
| case protocol.TypeBranchStatusReportResult: | |||||
| return BranchReportResponseDecoder | |||||
| case protocol.TypeBranchCommitResult: | |||||
| return BranchCommitResponseDecoder | |||||
| case protocol.TypeBranchRollbackResult: | |||||
| return BranchRollbackResponseDecoder | |||||
| case protocol.TypeGlobalReportResult: | |||||
| return GlobalReportResponseDecoder | |||||
| default: | |||||
| break | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -0,0 +1,779 @@ | |||||
| package codec | |||||
| import ( | |||||
| "bytes" | |||||
| ) | |||||
| import ( | |||||
| "vimagination.zapto.org/byteio" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/model" | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| ) | |||||
| func AbstractResultMessageDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractResultMessage{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| resultCode, _ := r.ReadByte() | |||||
| msg.ResultCode = protocol.ResultCode(resultCode) | |||||
| totalReadN += 1 | |||||
| if msg.ResultCode == protocol.ResultCodeFailed { | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Msg, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func MergedWarpMessageDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| size16 int16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| result := protocol.MergedWarpMessage{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| r.ReadInt32() | |||||
| totalReadN += 4 | |||||
| size16, readN, _ = r.ReadInt16() | |||||
| totalReadN += readN | |||||
| result.Msgs = make([]protocol.MessageTypeAware, 0) | |||||
| for index := 0; index < int(size16); index++ { | |||||
| typeCode, _, _ := r.ReadInt16() | |||||
| totalReadN += 2 | |||||
| decoder := getMessageDecoder(typeCode) | |||||
| if decoder != nil { | |||||
| msg, readN := decoder(in[totalReadN:]) | |||||
| totalReadN += readN | |||||
| result.Msgs = append(result.Msgs, msg.(protocol.MessageTypeAware)) | |||||
| } | |||||
| } | |||||
| return result, totalReadN | |||||
| } | |||||
| func MergeResultMessageDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| size16 int16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| result := protocol.MergeResultMessage{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| r.ReadInt32() | |||||
| totalReadN += 4 | |||||
| size16, readN, _ = r.ReadInt16() | |||||
| totalReadN += readN | |||||
| result.Msgs = make([]protocol.MessageTypeAware, 0) | |||||
| for index := 0; index < int(size16); index++ { | |||||
| typeCode, _, _ := r.ReadInt16() | |||||
| totalReadN += 2 | |||||
| decoder := getMessageDecoder(typeCode) | |||||
| if decoder != nil { | |||||
| msg, readN := decoder(in[totalReadN:]) | |||||
| totalReadN += readN | |||||
| result.Msgs = append(result.Msgs, msg.(protocol.MessageTypeAware)) | |||||
| } | |||||
| } | |||||
| return result, totalReadN | |||||
| } | |||||
| func AbstractIdentifyRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractIdentifyRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Version, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ApplicationId, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.TransactionServiceGroup, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ExtraData = make([]byte, int(length16)) | |||||
| readN, _ := r.Read(msg.ExtraData) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func AbstractIdentifyResponseDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractIdentifyResponse{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| identified, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| if identified == byte(1) { | |||||
| msg.Identified = true | |||||
| } else if identified == byte(0) { | |||||
| msg.Identified = false | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Version, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func RegisterRMRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length32 uint32 = 0 | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.RegisterRMRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Version, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ApplicationId, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.TransactionServiceGroup, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ExtraData = make([]byte, int(length16)) | |||||
| readN, _ := r.Read(msg.ExtraData) | |||||
| totalReadN += readN | |||||
| } | |||||
| length32, readN, _ = r.ReadUint32() | |||||
| totalReadN += readN | |||||
| if length32 > 0 { | |||||
| msg.ResourceIds, readN, _ = r.ReadString(int(length32)) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func RegisterRMResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractIdentifyResponseDecoder(in) | |||||
| abstractIdentifyResponse := resp.(protocol.AbstractIdentifyResponse) | |||||
| msg := protocol.RegisterRMResponse{AbstractIdentifyResponse: abstractIdentifyResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func RegisterTMRequestDecoder(in []byte) (interface{}, int) { | |||||
| req, totalReadN := AbstractIdentifyRequestDecoder(in) | |||||
| abstractIdentifyRequest := req.(protocol.AbstractIdentifyRequest) | |||||
| msg := protocol.RegisterTMRequest{AbstractIdentifyRequest: abstractIdentifyRequest} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func RegisterTMResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractIdentifyResponseDecoder(in) | |||||
| abstractIdentifyResponse := resp.(protocol.AbstractIdentifyResponse) | |||||
| msg := protocol.RegisterRMResponse{AbstractIdentifyResponse: abstractIdentifyResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func AbstractTransactionResponseDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractTransactionResponse{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| resultCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.ResultCode = protocol.ResultCode(resultCode) | |||||
| if msg.ResultCode == protocol.ResultCodeFailed { | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Msg, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| } | |||||
| exceptionCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||||
| return msg, totalReadN | |||||
| } | |||||
| func AbstractBranchEndRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractBranchEndRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Xid, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| msg.BranchId, _, _ = r.ReadInt64() | |||||
| totalReadN += 8 | |||||
| branchType, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.BranchType = model.BranchType(branchType) | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ApplicationData = make([]byte, int(length16)) | |||||
| readN, _ := r.Read(msg.ApplicationData) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func AbstractBranchEndResponseDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractBranchEndResponse{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| resultCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.ResultCode = protocol.ResultCode(resultCode) | |||||
| if msg.ResultCode == protocol.ResultCodeFailed { | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Msg, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| } | |||||
| exceptionCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Xid, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| msg.BranchId, _, _ = r.ReadInt64() | |||||
| totalReadN += 8 | |||||
| branchStatus, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.BranchStatus = model.BranchStatus(branchStatus) | |||||
| return msg, totalReadN | |||||
| } | |||||
| func AbstractGlobalEndRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractGlobalEndRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Xid, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ExtraData = make([]byte, int(length16)) | |||||
| readN, _ := r.Read(msg.ExtraData) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func AbstractGlobalEndResponseDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.AbstractGlobalEndResponse{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| resultCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.ResultCode = protocol.ResultCode(resultCode) | |||||
| if msg.ResultCode == protocol.ResultCodeFailed { | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Msg, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| } | |||||
| exceptionCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||||
| globalStatus, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.GlobalStatus = model.GlobalStatus(globalStatus) | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchCommitRequestDecoder(in []byte) (interface{}, int) { | |||||
| req, totalReadN := AbstractBranchEndRequestDecoder(in) | |||||
| abstractBranchEndRequest := req.(protocol.AbstractBranchEndRequest) | |||||
| msg := protocol.BranchCommitRequest{AbstractBranchEndRequest: abstractBranchEndRequest} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchCommitResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractBranchEndResponseDecoder(in) | |||||
| abstractBranchEndResponse := resp.(protocol.AbstractBranchEndResponse) | |||||
| msg := protocol.BranchCommitResponse{AbstractBranchEndResponse: abstractBranchEndResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchRegisterRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length32 uint32 = 0 | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.BranchRegisterRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Xid, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| branchType, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.BranchType = model.BranchType(branchType) | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length32, readN, _ = r.ReadUint32() | |||||
| totalReadN += readN | |||||
| if length32 > 0 { | |||||
| msg.LockKey, readN, _ = r.ReadString(int(length32)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length32, readN, _ = r.ReadUint32() | |||||
| totalReadN += readN | |||||
| if length32 > 0 { | |||||
| msg.ApplicationData = make([]byte, int(length32)) | |||||
| readN, _ := r.Read(msg.ApplicationData) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchRegisterResponseDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.BranchRegisterResponse{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| resultCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.ResultCode = protocol.ResultCode(resultCode) | |||||
| if msg.ResultCode == protocol.ResultCodeFailed { | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Msg, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| } | |||||
| exceptionCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||||
| msg.BranchId, readN, _ = r.ReadInt64() | |||||
| totalReadN += readN | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchReportRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.BranchReportRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Xid, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| msg.BranchId, _, _ = r.ReadInt64() | |||||
| branchStatus, _ := r.ReadByte() | |||||
| msg.Status = model.BranchStatus(branchStatus) | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ApplicationData = make([]byte, int(length16)) | |||||
| readN, _ := r.Read(msg.ApplicationData) | |||||
| totalReadN += readN | |||||
| } | |||||
| branchType, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.BranchType = model.BranchType(branchType) | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchReportResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractTransactionResponseDecoder(in) | |||||
| abstractTransactionResponse := resp.(protocol.AbstractTransactionResponse) | |||||
| msg := protocol.BranchReportResponse{AbstractTransactionResponse: abstractTransactionResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchRollbackRequestDecoder(in []byte) (interface{}, int) { | |||||
| req, totalReadN := AbstractBranchEndRequestDecoder(in) | |||||
| abstractBranchEndRequest := req.(protocol.AbstractBranchEndRequest) | |||||
| msg := protocol.BranchRollbackRequest{AbstractBranchEndRequest: abstractBranchEndRequest} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func BranchRollbackResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractBranchEndResponseDecoder(in) | |||||
| abstractBranchEndResponse := resp.(protocol.AbstractBranchEndResponse) | |||||
| msg := protocol.BranchRollbackResponse{AbstractBranchEndResponse: abstractBranchEndResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalBeginRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.GlobalBeginRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| timeout, readN, _ := r.ReadInt32() | |||||
| totalReadN += readN | |||||
| msg.Timeout = timeout | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.TransactionName, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalBeginResponseDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.GlobalBeginResponse{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| resultCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.ResultCode = protocol.ResultCode(resultCode) | |||||
| if msg.ResultCode == protocol.ResultCodeFailed { | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Msg, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| } | |||||
| exceptionCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Xid, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ExtraData = make([]byte, int(length16)) | |||||
| readN, _ := r.Read(msg.ExtraData) | |||||
| totalReadN += readN | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalCommitRequestDecoder(in []byte) (interface{}, int) { | |||||
| req, totalReadN := AbstractGlobalEndRequestDecoder(in) | |||||
| abstractGlobalEndRequest := req.(protocol.AbstractGlobalEndRequest) | |||||
| msg := protocol.GlobalCommitRequest{AbstractGlobalEndRequest: abstractGlobalEndRequest} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalCommitResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||||
| abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||||
| msg := protocol.GlobalCommitResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalLockQueryRequestDecoder(in []byte) (interface{}, int) { | |||||
| req, totalReadN := BranchRegisterRequestDecoder(in) | |||||
| branchRegisterRequest := req.(protocol.BranchRegisterRequest) | |||||
| msg := protocol.GlobalLockQueryRequest{BranchRegisterRequest: branchRegisterRequest} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalLockQueryResponseDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.GlobalLockQueryResponse{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| resultCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.ResultCode = protocol.ResultCode(resultCode) | |||||
| if msg.ResultCode == protocol.ResultCodeFailed { | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Msg, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| } | |||||
| exceptionCode, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.TransactionExceptionCode = model.TransactionExceptionCode(exceptionCode) | |||||
| lockable, readN, _ := r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if lockable == uint16(1) { | |||||
| msg.Lockable = true | |||||
| } else if lockable == uint16(0) { | |||||
| msg.Lockable = false | |||||
| } | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalReportRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.GlobalReportRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.Xid, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ExtraData = make([]byte, int(length16)) | |||||
| readN, _ := r.Read(msg.ExtraData) | |||||
| totalReadN += readN | |||||
| } | |||||
| globalStatus, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.GlobalStatus = model.GlobalStatus(globalStatus) | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalReportResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||||
| abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||||
| msg := protocol.GlobalReportResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalRollbackRequestDecoder(in []byte) (interface{}, int) { | |||||
| req, totalReadN := AbstractGlobalEndRequestDecoder(in) | |||||
| abstractGlobalEndRequest := req.(protocol.AbstractGlobalEndRequest) | |||||
| msg := protocol.GlobalRollbackRequest{AbstractGlobalEndRequest: abstractGlobalEndRequest} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalRollbackResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||||
| abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||||
| msg := protocol.GlobalRollbackResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalStatusRequestDecoder(in []byte) (interface{}, int) { | |||||
| req, totalReadN := AbstractGlobalEndRequestDecoder(in) | |||||
| abstractGlobalEndRequest := req.(protocol.AbstractGlobalEndRequest) | |||||
| msg := protocol.GlobalStatusRequest{AbstractGlobalEndRequest: abstractGlobalEndRequest} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func GlobalStatusResponseDecoder(in []byte) (interface{}, int) { | |||||
| resp, totalReadN := AbstractGlobalEndResponseDecoder(in) | |||||
| abstractGlobalEndResponse := resp.(protocol.AbstractGlobalEndResponse) | |||||
| msg := protocol.GlobalStatusResponse{AbstractGlobalEndResponse: abstractGlobalEndResponse} | |||||
| return msg, totalReadN | |||||
| } | |||||
| func UndoLogDeleteRequestDecoder(in []byte) (interface{}, int) { | |||||
| var ( | |||||
| length16 uint16 = 0 | |||||
| readN = 0 | |||||
| totalReadN = 0 | |||||
| ) | |||||
| msg := protocol.UndoLogDeleteRequest{} | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(in)} | |||||
| branchType, _ := r.ReadByte() | |||||
| totalReadN += 1 | |||||
| msg.BranchType = model.BranchType(branchType) | |||||
| length16, readN, _ = r.ReadUint16() | |||||
| totalReadN += readN | |||||
| if length16 > 0 { | |||||
| msg.ResourceId, readN, _ = r.ReadString(int(length16)) | |||||
| totalReadN += readN | |||||
| } | |||||
| msg.SaveDays, readN, _ = r.ReadInt16() | |||||
| totalReadN += readN | |||||
| return msg, totalReadN | |||||
| } | |||||
| @@ -0,0 +1,565 @@ | |||||
| package codec | |||||
| import ( | |||||
| "bytes" | |||||
| ) | |||||
| import ( | |||||
| "vimagination.zapto.org/byteio" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| log "github.com/seata/seata-go/pkg/util/log" | |||||
| ) | |||||
| func AbstractResultMessageEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| message := in.(protocol.AbstractResultMessage) | |||||
| w.WriteByte(byte(message.ResultCode)) | |||||
| if message.ResultCode == protocol.ResultCodeFailed { | |||||
| var msg string | |||||
| if message.Msg != "" { | |||||
| if len(message.Msg) > 128 { | |||||
| msg = message.Msg[:128] | |||||
| } else { | |||||
| msg = message.Msg | |||||
| } | |||||
| // 暂时不考虑 message.Msg 包含中文的情况,这样字符串的长度就是 byte 数组的长度 | |||||
| w.WriteInt16(int16(len(msg))) | |||||
| w.WriteString(msg) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| } | |||||
| return b.Bytes() | |||||
| } | |||||
| func MergedWarpMessageEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| b bytes.Buffer | |||||
| result = make([]byte, 0) | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.MergedWarpMessage) | |||||
| w.WriteInt16(int16(len(req.Msgs))) | |||||
| for _, msg := range req.Msgs { | |||||
| encoder := getMessageEncoder(msg.GetTypeCode()) | |||||
| if encoder != nil { | |||||
| data := encoder(msg) | |||||
| w.WriteInt16(msg.GetTypeCode()) | |||||
| w.Write(data) | |||||
| } | |||||
| } | |||||
| size := uint32(b.Len()) | |||||
| result = append(result, []byte{byte(size >> 24), byte(size >> 16), byte(size >> 8), byte(size)}...) | |||||
| result = append(result, b.Bytes()...) | |||||
| if len(req.Msgs) > 20 { | |||||
| log.Debugf("msg in one packet: %s ,buffer size: %s", len(req.Msgs), size) | |||||
| } | |||||
| return result | |||||
| } | |||||
| func MergeResultMessageEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| b bytes.Buffer | |||||
| result = make([]byte, 0) | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.MergeResultMessage) | |||||
| w.WriteInt16(int16(len(req.Msgs))) | |||||
| for _, msg := range req.Msgs { | |||||
| encoder := getMessageEncoder(msg.GetTypeCode()) | |||||
| if encoder != nil { | |||||
| data := encoder(msg) | |||||
| w.WriteInt16(msg.GetTypeCode()) | |||||
| w.Write(data) | |||||
| } | |||||
| } | |||||
| size := uint32(b.Len()) | |||||
| result = append(result, []byte{byte(size >> 24), byte(size >> 16), byte(size >> 8), byte(size)}...) | |||||
| result = append(result, b.Bytes()...) | |||||
| if len(req.Msgs) > 20 { | |||||
| log.Debugf("msg in one packet: %s ,buffer size: %s", len(req.Msgs), size) | |||||
| } | |||||
| return result | |||||
| } | |||||
| func AbstractIdentifyRequestEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req := in.(protocol.AbstractIdentifyRequest) | |||||
| if req.Version != "" { | |||||
| w.WriteInt16(int16(len(req.Version))) | |||||
| w.WriteString(req.Version) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if req.ApplicationId != "" { | |||||
| w.WriteInt16(int16(len(req.ApplicationId))) | |||||
| w.WriteString(req.ApplicationId) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if req.TransactionServiceGroup != "" { | |||||
| w.WriteInt16(int16(len(req.TransactionServiceGroup))) | |||||
| w.WriteString(req.TransactionServiceGroup) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if req.ExtraData != nil { | |||||
| w.WriteUint16(uint16(len(req.ExtraData))) | |||||
| w.Write(req.ExtraData) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| return b.Bytes() | |||||
| } | |||||
| func AbstractIdentifyResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.AbstractIdentifyResponse) | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| if resp.Identified { | |||||
| w.WriteByte(byte(1)) | |||||
| } else { | |||||
| w.WriteByte(byte(0)) | |||||
| } | |||||
| if resp.Version != "" { | |||||
| w.WriteInt16(int16(len(resp.Version))) | |||||
| w.WriteString(resp.Version) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| return b.Bytes() | |||||
| } | |||||
| func RegisterRMRequestEncoder(in interface{}) []byte { | |||||
| req := in.(protocol.RegisterRMRequest) | |||||
| data := AbstractIdentifyRequestEncoder(req.AbstractIdentifyRequest) | |||||
| var ( | |||||
| zero32 int32 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| if req.ResourceIds != "" { | |||||
| w.WriteInt32(int32(len(req.ResourceIds))) | |||||
| w.WriteString(req.ResourceIds) | |||||
| } else { | |||||
| w.WriteInt32(zero32) | |||||
| } | |||||
| result := append(data, b.Bytes()...) | |||||
| return result | |||||
| } | |||||
| func RegisterRMResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.RegisterRMResponse) | |||||
| return AbstractIdentifyResponseEncoder(resp.AbstractIdentifyResponse) | |||||
| } | |||||
| func RegisterTMRequestEncoder(in interface{}) []byte { | |||||
| req := in.(protocol.RegisterTMRequest) | |||||
| return AbstractIdentifyRequestEncoder(req.AbstractIdentifyRequest) | |||||
| } | |||||
| func RegisterTMResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.RegisterTMResponse) | |||||
| return AbstractIdentifyResponseEncoder(resp.AbstractIdentifyResponse) | |||||
| } | |||||
| func AbstractTransactionResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.AbstractTransactionResponse) | |||||
| data := AbstractResultMessageEncoder(resp.AbstractResultMessage) | |||||
| result := append(data, byte(resp.TransactionExceptionCode)) | |||||
| return result | |||||
| } | |||||
| func AbstractBranchEndRequestEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero32 int32 = 0 | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.AbstractBranchEndRequest) | |||||
| if req.Xid != "" { | |||||
| w.WriteInt16(int16(len(req.Xid))) | |||||
| w.WriteString(req.Xid) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| w.WriteInt64(req.BranchId) | |||||
| w.WriteByte(byte(req.BranchType)) | |||||
| if req.ResourceId != "" { | |||||
| w.WriteInt16(int16(len(req.ResourceId))) | |||||
| w.WriteString(req.ResourceId) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if req.ApplicationData != nil { | |||||
| w.WriteUint32(uint32(len(req.ApplicationData))) | |||||
| w.Write(req.ApplicationData) | |||||
| } else { | |||||
| w.WriteInt32(zero32) | |||||
| } | |||||
| return b.Bytes() | |||||
| } | |||||
| func AbstractBranchEndResponseEncoder(in interface{}) []byte { | |||||
| resp, _ := in.(protocol.AbstractBranchEndResponse) | |||||
| data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| if resp.Xid != "" { | |||||
| w.WriteInt16(int16(len(resp.Xid))) | |||||
| w.WriteString(resp.Xid) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| w.WriteInt64(resp.BranchId) | |||||
| w.WriteByte(byte(resp.BranchStatus)) | |||||
| result := append(data, b.Bytes()...) | |||||
| return result | |||||
| } | |||||
| func AbstractGlobalEndRequestEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.AbstractGlobalEndRequest) | |||||
| if req.Xid != "" { | |||||
| w.WriteInt16(int16(len(req.Xid))) | |||||
| w.WriteString(req.Xid) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if req.ExtraData != nil { | |||||
| w.WriteUint16(uint16(len(req.ExtraData))) | |||||
| w.Write(req.ExtraData) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| return b.Bytes() | |||||
| } | |||||
| func AbstractGlobalEndResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.AbstractGlobalEndResponse) | |||||
| data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||||
| result := append(data, byte(resp.GlobalStatus)) | |||||
| return result | |||||
| } | |||||
| func BranchCommitRequestEncoder(in interface{}) []byte { | |||||
| req := in.(protocol.BranchCommitRequest) | |||||
| return AbstractBranchEndRequestEncoder(req.AbstractBranchEndRequest) | |||||
| } | |||||
| func BranchCommitResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.BranchCommitResponse) | |||||
| return AbstractBranchEndResponseEncoder(resp.AbstractBranchEndResponse) | |||||
| } | |||||
| func BranchRegisterRequestEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero32 int32 = 0 | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.BranchRegisterRequest) | |||||
| if req.Xid != "" { | |||||
| w.WriteInt16(int16(len(req.Xid))) | |||||
| w.WriteString(req.Xid) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| w.WriteByte(byte(req.BranchType)) | |||||
| if req.ResourceId != "" { | |||||
| w.WriteInt16(int16(len(req.ResourceId))) | |||||
| w.WriteString(req.ResourceId) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if req.LockKey != "" { | |||||
| w.WriteInt32(int32(len(req.LockKey))) | |||||
| w.WriteString(req.LockKey) | |||||
| } else { | |||||
| w.WriteInt32(zero32) | |||||
| } | |||||
| if req.ApplicationData != nil { | |||||
| w.WriteUint32(uint32(len(req.ApplicationData))) | |||||
| w.Write(req.ApplicationData) | |||||
| } else { | |||||
| w.WriteInt32(zero32) | |||||
| } | |||||
| return b.Bytes() | |||||
| } | |||||
| func BranchRegisterResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.BranchRegisterResponse) | |||||
| data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||||
| c := uint64(resp.BranchId) | |||||
| branchIdBytes := []byte{ | |||||
| byte(c >> 56), | |||||
| byte(c >> 48), | |||||
| byte(c >> 40), | |||||
| byte(c >> 32), | |||||
| byte(c >> 24), | |||||
| byte(c >> 16), | |||||
| byte(c >> 8), | |||||
| byte(c), | |||||
| } | |||||
| result := append(data, branchIdBytes...) | |||||
| return result | |||||
| } | |||||
| func BranchReportRequestEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero32 int32 = 0 | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.BranchReportRequest) | |||||
| if req.Xid != "" { | |||||
| w.WriteInt16(int16(len(req.Xid))) | |||||
| w.WriteString(req.Xid) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| w.WriteInt64(req.BranchId) | |||||
| w.WriteByte(byte(req.Status)) | |||||
| if req.ResourceId != "" { | |||||
| w.WriteInt16(int16(len(req.ResourceId))) | |||||
| w.WriteString(req.ResourceId) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if req.ApplicationData != nil { | |||||
| w.WriteUint32(uint32(len(req.ApplicationData))) | |||||
| w.Write(req.ApplicationData) | |||||
| } else { | |||||
| w.WriteInt32(zero32) | |||||
| } | |||||
| w.WriteByte(byte(req.BranchType)) | |||||
| return b.Bytes() | |||||
| } | |||||
| func BranchReportResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.BranchReportResponse) | |||||
| return AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||||
| } | |||||
| func BranchRollbackRequestEncoder(in interface{}) []byte { | |||||
| req := in.(protocol.BranchRollbackRequest) | |||||
| return AbstractBranchEndRequestEncoder(req.AbstractBranchEndRequest) | |||||
| } | |||||
| func BranchRollbackResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.BranchRollbackResponse) | |||||
| return AbstractBranchEndResponseEncoder(resp.AbstractBranchEndResponse) | |||||
| } | |||||
| func GlobalBeginRequestEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.GlobalBeginRequest) | |||||
| w.WriteInt32(req.Timeout) | |||||
| if req.TransactionName != "" { | |||||
| w.WriteInt16(int16(len(req.TransactionName))) | |||||
| w.WriteString(req.TransactionName) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| return b.Bytes() | |||||
| } | |||||
| func GlobalBeginResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.GlobalBeginResponse) | |||||
| data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| if resp.Xid != "" { | |||||
| w.WriteInt16(int16(len(resp.Xid))) | |||||
| w.WriteString(resp.Xid) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| if resp.ExtraData != nil { | |||||
| w.WriteUint16(uint16(len(resp.ExtraData))) | |||||
| w.Write(resp.ExtraData) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| result := append(data, b.Bytes()...) | |||||
| return result | |||||
| } | |||||
| func GlobalCommitRequestEncoder(in interface{}) []byte { | |||||
| req := in.(protocol.GlobalCommitRequest) | |||||
| return AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||||
| } | |||||
| func GlobalCommitResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.GlobalCommitResponse) | |||||
| return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||||
| } | |||||
| func GlobalLockQueryRequestEncoder(in interface{}) []byte { | |||||
| return BranchRegisterRequestEncoder(in) | |||||
| } | |||||
| func GlobalLockQueryResponseEncoder(in interface{}) []byte { | |||||
| resp, _ := in.(protocol.GlobalLockQueryResponse) | |||||
| data := AbstractTransactionResponseEncoder(resp.AbstractTransactionResponse) | |||||
| var result []byte | |||||
| if resp.Lockable { | |||||
| result = append(data, byte(0), byte(1)) | |||||
| } else { | |||||
| result = append(data, byte(0), byte(0)) | |||||
| } | |||||
| return result | |||||
| } | |||||
| func GlobalReportRequestEncoder(in interface{}) []byte { | |||||
| req, _ := in.(protocol.GlobalReportRequest) | |||||
| data := AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||||
| result := append(data, byte(req.GlobalStatus)) | |||||
| return result | |||||
| } | |||||
| func GlobalReportResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.GlobalReportResponse) | |||||
| return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||||
| } | |||||
| func GlobalRollbackRequestEncoder(in interface{}) []byte { | |||||
| req := in.(protocol.GlobalRollbackRequest) | |||||
| return AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||||
| } | |||||
| func GlobalRollbackResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.GlobalRollbackResponse) | |||||
| return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||||
| } | |||||
| func GlobalStatusRequestEncoder(in interface{}) []byte { | |||||
| req := in.(protocol.GlobalStatusRequest) | |||||
| return AbstractGlobalEndRequestEncoder(req.AbstractGlobalEndRequest) | |||||
| } | |||||
| func GlobalStatusResponseEncoder(in interface{}) []byte { | |||||
| resp := in.(protocol.GlobalStatusResponse) | |||||
| return AbstractGlobalEndResponseEncoder(resp.AbstractGlobalEndResponse) | |||||
| } | |||||
| func UndoLogDeleteRequestEncoder(in interface{}) []byte { | |||||
| var ( | |||||
| zero16 int16 = 0 | |||||
| b bytes.Buffer | |||||
| ) | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| req, _ := in.(protocol.UndoLogDeleteRequest) | |||||
| w.WriteByte(byte(req.BranchType)) | |||||
| if req.ResourceId != "" { | |||||
| w.WriteInt16(int16(len(req.ResourceId))) | |||||
| w.WriteString(req.ResourceId) | |||||
| } else { | |||||
| w.WriteInt16(zero16) | |||||
| } | |||||
| w.WriteInt16(req.SaveDays) | |||||
| return b.Bytes() | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| package protocol | |||||
| var MAGIC_CODE_BYTES = [2]byte{0xda, 0xda} | |||||
| const ( | |||||
| VERSION = 1 | |||||
| // MaxFrameLength max frame length | |||||
| MaxFrameLength = 8 * 1024 * 1024 | |||||
| // V1HeadLength v1 head length | |||||
| V1HeadLength = 16 | |||||
| // MSGTypeRequest request message type | |||||
| MSGTypeRequest = 0 | |||||
| // MSGTypeResponse response message type | |||||
| MSGTypeResponse = 1 | |||||
| // MSGTypeRequestOneway request one way | |||||
| MSGTypeRequestOneway = 2 | |||||
| // MSGTypeHeartbeatRequest heart beat request | |||||
| MSGTypeHeartbeatRequest = 3 | |||||
| // MSGTypeHeartbeatResponse heart beat response | |||||
| MSGTypeHeartbeatResponse = 4 | |||||
| ) | |||||
| @@ -0,0 +1,16 @@ | |||||
| package protocol | |||||
| type HeartBeatMessage struct { | |||||
| Ping bool | |||||
| } | |||||
| var HeartBeatMessagePing = HeartBeatMessage{true} | |||||
| var HeartBeatMessagePong = HeartBeatMessage{false} | |||||
| func (msg HeartBeatMessage) ToString() string { | |||||
| if msg.Ping { | |||||
| return "services ping" | |||||
| } else { | |||||
| return "services pong" | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| package protocol | |||||
| type AbstractResultMessage struct { | |||||
| ResultCode ResultCode | |||||
| Msg string | |||||
| } | |||||
| type AbstractIdentifyRequest struct { | |||||
| Version string | |||||
| ApplicationId string `json:"applicationId"` | |||||
| TransactionServiceGroup string | |||||
| ExtraData []byte | |||||
| } | |||||
| type AbstractIdentifyResponse struct { | |||||
| AbstractResultMessage | |||||
| Version string | |||||
| ExtraData []byte | |||||
| Identified bool | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| package protocol | |||||
| type MergedWarpMessage struct { | |||||
| Msgs []MessageTypeAware | |||||
| MsgIds []int32 | |||||
| } | |||||
| func (req MergedWarpMessage) GetTypeCode() int16 { | |||||
| return TypeSeataMerge | |||||
| } | |||||
| type MergeResultMessage struct { | |||||
| Msgs []MessageTypeAware | |||||
| } | |||||
| func (resp MergeResultMessage) GetTypeCode() int16 { | |||||
| return TypeSeataMergeResult | |||||
| } | |||||
| @@ -0,0 +1,17 @@ | |||||
| package protocol | |||||
| // MessageFuture ... | |||||
| type MessageFuture struct { | |||||
| ID int32 | |||||
| Err error | |||||
| Response interface{} | |||||
| Done chan bool | |||||
| } | |||||
| // NewMessageFuture ... | |||||
| func NewMessageFuture(message RpcMessage) *MessageFuture { | |||||
| return &MessageFuture{ | |||||
| ID: message.ID, | |||||
| Done: make(chan bool), | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,119 @@ | |||||
| package protocol | |||||
| const ( | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_BEGIN. | |||||
| */ | |||||
| TypeGlobalBegin = 1 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_BEGIN_RESULT. | |||||
| */ | |||||
| TypeGlobalBeginResult = 2 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_COMMIT. | |||||
| */ | |||||
| TypeGlobalCommit = 7 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_COMMIT_RESULT. | |||||
| */ | |||||
| TypeGlobalCommitResult = 8 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_ROLLBACK. | |||||
| */ | |||||
| TypeGlobalRollback = 9 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_ROLLBACK_RESULT. | |||||
| */ | |||||
| TypeGlobalRollbackResult = 10 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_STATUS. | |||||
| */ | |||||
| TypeGlobalStatus = 15 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_STATUS_RESULT. | |||||
| */ | |||||
| TypeGlobalStatusResult = 16 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_REPORT. | |||||
| */ | |||||
| TypeGlobalReport = 17 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_REPORT_RESULT. | |||||
| */ | |||||
| TypeGlobalReportResult = 18 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_LOCK_QUERY. | |||||
| */ | |||||
| TypeGlobalLockQuery = 21 | |||||
| /** | |||||
| * The constant TYPE_GLOBAL_LOCK_QUERY_RESULT. | |||||
| */ | |||||
| TypeGlobalLockQueryResult = 22 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_COMMIT. | |||||
| */ | |||||
| TypeBranchCommit = 3 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_COMMIT_RESULT. | |||||
| */ | |||||
| TypeBranchCommitResult = 4 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_ROLLBACK. | |||||
| */ | |||||
| TypeBranchRollback = 5 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_ROLLBACK_RESULT. | |||||
| */ | |||||
| TypeBranchRollbackResult = 6 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_REGISTER. | |||||
| */ | |||||
| TypeBranchRegister = 11 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_REGISTER_RESULT. | |||||
| */ | |||||
| TypeBranchRegisterResult = 12 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_STATUS_REPORT. | |||||
| */ | |||||
| TypeBranchStatusReport = 13 | |||||
| /** | |||||
| * The constant TYPE_BRANCH_STATUS_REPORT_RESULT. | |||||
| */ | |||||
| TypeBranchStatusReportResult = 14 | |||||
| /** | |||||
| * The constant TYPE_SEATA_MERGE. | |||||
| */ | |||||
| TypeSeataMerge = 59 | |||||
| /** | |||||
| * The constant TYPE_SEATA_MERGE_RESULT. | |||||
| */ | |||||
| TypeSeataMergeResult = 60 | |||||
| /** | |||||
| * The constant TYPE_REG_CLT. | |||||
| */ | |||||
| TypeRegClt = 101 | |||||
| /** | |||||
| * The constant TYPE_REG_CLT_RESULT. | |||||
| */ | |||||
| TypeRegCltResult = 102 | |||||
| /** | |||||
| * The constant TYPE_REG_RM. | |||||
| */ | |||||
| TypeRegRm = 103 | |||||
| /** | |||||
| * The constant TYPE_REG_RM_RESULT. | |||||
| */ | |||||
| TypeRegRmResult = 104 | |||||
| /** | |||||
| * The constant TYPE_RM_DELETE_UNDOLOG. | |||||
| */ | |||||
| TypeRmDeleteUndolog = 111 | |||||
| /** | |||||
| * the constant TYPE_HEARTBEAT_MSG | |||||
| */ | |||||
| TypeHeartbeatMsg = 120 | |||||
| ) | |||||
| @@ -0,0 +1,5 @@ | |||||
| package protocol | |||||
| type MessageTypeAware interface { | |||||
| GetTypeCode() int16 | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| package protocol | |||||
| type ResultCode byte | |||||
| const ( | |||||
| /** | |||||
| * ResultCodeFailed result code. | |||||
| */ | |||||
| // ResultCodeFailed | |||||
| ResultCodeFailed ResultCode = iota | |||||
| /** | |||||
| * Success result code. | |||||
| */ | |||||
| // Success | |||||
| ResultCodeSuccess | |||||
| ) | |||||
| @@ -0,0 +1,18 @@ | |||||
| package protocol | |||||
| type RegisterRMRequest struct { | |||||
| AbstractIdentifyRequest | |||||
| ResourceIds string | |||||
| } | |||||
| func (req RegisterRMRequest) GetTypeCode() int16 { | |||||
| return TypeRegRm | |||||
| } | |||||
| type RegisterRMResponse struct { | |||||
| AbstractIdentifyResponse | |||||
| } | |||||
| func (resp RegisterRMResponse) GetTypeCode() int16 { | |||||
| return TypeRegRmResult | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package protocol | |||||
| type RpcMessage struct { | |||||
| ID int32 | |||||
| MessageType byte | |||||
| Codec byte | |||||
| Compressor byte | |||||
| HeadMap map[string]string | |||||
| Body interface{} | |||||
| } | |||||
| @@ -0,0 +1,17 @@ | |||||
| package protocol | |||||
| type RegisterTMRequest struct { | |||||
| AbstractIdentifyRequest | |||||
| } | |||||
| func (req RegisterTMRequest) GetTypeCode() int16 { | |||||
| return TypeRegClt | |||||
| } | |||||
| type RegisterTMResponse struct { | |||||
| AbstractIdentifyResponse | |||||
| } | |||||
| func (resp RegisterTMResponse) GetTypeCode() int16 { | |||||
| return TypeRegCltResult | |||||
| } | |||||
| @@ -0,0 +1,226 @@ | |||||
| package protocol | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/model" | |||||
| ) | |||||
| type AbstractTransactionResponse struct { | |||||
| AbstractResultMessage | |||||
| TransactionExceptionCode model.TransactionExceptionCode | |||||
| } | |||||
| type AbstractBranchEndRequest struct { | |||||
| Xid string | |||||
| BranchId int64 | |||||
| BranchType model.BranchType | |||||
| ResourceId string | |||||
| ApplicationData []byte | |||||
| } | |||||
| type AbstractBranchEndResponse struct { | |||||
| AbstractTransactionResponse | |||||
| Xid string | |||||
| BranchId int64 | |||||
| BranchStatus model.BranchStatus | |||||
| } | |||||
| type AbstractGlobalEndRequest struct { | |||||
| Xid string | |||||
| ExtraData []byte | |||||
| } | |||||
| type AbstractGlobalEndResponse struct { | |||||
| AbstractTransactionResponse | |||||
| GlobalStatus model.GlobalStatus | |||||
| } | |||||
| type BranchRegisterRequest struct { | |||||
| Xid string | |||||
| BranchType model.BranchType | |||||
| ResourceId string | |||||
| LockKey string | |||||
| ApplicationData []byte | |||||
| } | |||||
| func (req BranchRegisterRequest) GetTypeCode() int16 { | |||||
| return TypeBranchRegister | |||||
| } | |||||
| type BranchRegisterResponse struct { | |||||
| AbstractTransactionResponse | |||||
| BranchId int64 | |||||
| } | |||||
| func (resp BranchRegisterResponse) GetTypeCode() int16 { | |||||
| return TypeBranchRegisterResult | |||||
| } | |||||
| type BranchReportRequest struct { | |||||
| Xid string | |||||
| BranchId int64 | |||||
| ResourceId string | |||||
| Status model.BranchStatus | |||||
| ApplicationData []byte | |||||
| BranchType model.BranchType | |||||
| } | |||||
| func (req BranchReportRequest) GetTypeCode() int16 { | |||||
| return TypeBranchStatusReport | |||||
| } | |||||
| type BranchReportResponse struct { | |||||
| AbstractTransactionResponse | |||||
| } | |||||
| func (resp BranchReportResponse) GetTypeCode() int16 { | |||||
| return TypeBranchStatusReportResult | |||||
| } | |||||
| type BranchCommitRequest struct { | |||||
| AbstractBranchEndRequest | |||||
| } | |||||
| func (req BranchCommitRequest) GetTypeCode() int16 { | |||||
| return TypeBranchCommit | |||||
| } | |||||
| type BranchCommitResponse struct { | |||||
| AbstractBranchEndResponse | |||||
| } | |||||
| func (resp BranchCommitResponse) GetTypeCode() int16 { | |||||
| return TypeBranchCommitResult | |||||
| } | |||||
| type BranchRollbackRequest struct { | |||||
| AbstractBranchEndRequest | |||||
| } | |||||
| func (req BranchRollbackRequest) GetTypeCode() int16 { | |||||
| return TypeBranchRollback | |||||
| } | |||||
| type BranchRollbackResponse struct { | |||||
| AbstractBranchEndResponse | |||||
| } | |||||
| func (resp BranchRollbackResponse) GetTypeCode() int16 { | |||||
| return TypeGlobalRollbackResult | |||||
| } | |||||
| type GlobalBeginRequest struct { | |||||
| Timeout int32 | |||||
| TransactionName string | |||||
| } | |||||
| func (req GlobalBeginRequest) GetTypeCode() int16 { | |||||
| return TypeGlobalBegin | |||||
| } | |||||
| type GlobalBeginResponse struct { | |||||
| AbstractTransactionResponse | |||||
| Xid string | |||||
| ExtraData []byte | |||||
| } | |||||
| func (resp GlobalBeginResponse) GetTypeCode() int16 { | |||||
| return TypeGlobalBeginResult | |||||
| } | |||||
| type GlobalStatusRequest struct { | |||||
| AbstractGlobalEndRequest | |||||
| } | |||||
| func (req GlobalStatusRequest) GetTypeCode() int16 { | |||||
| return TypeGlobalStatus | |||||
| } | |||||
| type GlobalStatusResponse struct { | |||||
| AbstractGlobalEndResponse | |||||
| } | |||||
| func (resp GlobalStatusResponse) GetTypeCode() int16 { | |||||
| return TypeGlobalStatusResult | |||||
| } | |||||
| type GlobalLockQueryRequest struct { | |||||
| BranchRegisterRequest | |||||
| } | |||||
| func (req GlobalLockQueryRequest) GetTypeCode() int16 { | |||||
| return TypeGlobalLockQuery | |||||
| } | |||||
| type GlobalLockQueryResponse struct { | |||||
| AbstractTransactionResponse | |||||
| Lockable bool | |||||
| } | |||||
| func (resp GlobalLockQueryResponse) GetTypeCode() int16 { | |||||
| return TypeGlobalLockQueryResult | |||||
| } | |||||
| type GlobalReportRequest struct { | |||||
| AbstractGlobalEndRequest | |||||
| GlobalStatus model.GlobalStatus | |||||
| } | |||||
| func (req GlobalReportRequest) GetTypeCode() int16 { | |||||
| return TypeGlobalStatus | |||||
| } | |||||
| type GlobalReportResponse struct { | |||||
| AbstractGlobalEndResponse | |||||
| } | |||||
| func (resp GlobalReportResponse) GetTypeCode() int16 { | |||||
| return TypeGlobalStatusResult | |||||
| } | |||||
| type GlobalCommitRequest struct { | |||||
| AbstractGlobalEndRequest | |||||
| } | |||||
| func (req GlobalCommitRequest) GetTypeCode() int16 { | |||||
| return TypeGlobalCommit | |||||
| } | |||||
| type GlobalCommitResponse struct { | |||||
| AbstractGlobalEndResponse | |||||
| } | |||||
| func (resp GlobalCommitResponse) GetTypeCode() int16 { | |||||
| return TypeGlobalCommitResult | |||||
| } | |||||
| type GlobalRollbackRequest struct { | |||||
| AbstractGlobalEndRequest | |||||
| } | |||||
| func (req GlobalRollbackRequest) GetTypeCode() int16 { | |||||
| return TypeGlobalRollback | |||||
| } | |||||
| type GlobalRollbackResponse struct { | |||||
| AbstractGlobalEndResponse | |||||
| } | |||||
| func (resp GlobalRollbackResponse) GetTypeCode() int16 { | |||||
| return TypeGlobalRollbackResult | |||||
| } | |||||
| type UndoLogDeleteRequest struct { | |||||
| ResourceId string | |||||
| SaveDays int16 | |||||
| BranchType model.BranchType | |||||
| } | |||||
| func (req UndoLogDeleteRequest) GetTypeCode() int16 { | |||||
| return TypeRmDeleteUndolog | |||||
| } | |||||
| @@ -0,0 +1,8 @@ | |||||
| package api | |||||
| type BusinessActionContext struct { | |||||
| Xid string | |||||
| BranchId string | |||||
| ActionName string | |||||
| ActionContext map[string]interface{} | |||||
| } | |||||
| @@ -0,0 +1,94 @@ | |||||
| package rm | |||||
| import ( | |||||
| "fmt" | |||||
| "sync" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/model" | |||||
| ) | |||||
| var ( | |||||
| // BranchType -> ResourceManager | |||||
| resourceManagerMap sync.Map | |||||
| // singletone defaultResourceManager | |||||
| defaultRM *defaultResourceManager | |||||
| ) | |||||
| func init() { | |||||
| defaultRM = &defaultResourceManager{} | |||||
| } | |||||
| type defaultResourceManager struct { | |||||
| } | |||||
| // 将事务管理器注册到这里 | |||||
| func RegisterResource(resourceManager model.ResourceManager) { | |||||
| resourceManagerMap.Store(resourceManager.GetBranchType(), resourceManager) | |||||
| } | |||||
| func GetDefaultResourceManager() model.ResourceManager { | |||||
| return defaultRM | |||||
| } | |||||
| func (*defaultResourceManager) getResourceManager(branchType model.BranchType) model.ResourceManager { | |||||
| rm, ok := resourceManagerMap.Load(branchType) | |||||
| if !ok { | |||||
| panic(fmt.Sprintf("No ResourceManager for BranchType: %v", branchType)) | |||||
| } | |||||
| return rm.(model.ResourceManager) | |||||
| } | |||||
| // Commit a branch transaction | |||||
| func (d *defaultResourceManager) BranchCommit(branchType model.BranchType, xid, branchId int64, resourceId, applicationData string) (model.BranchStatus, error) { | |||||
| return d.getResourceManager(branchType).BranchCommit(branchType, xid, branchId, resourceId, applicationData) | |||||
| } | |||||
| // Rollback a branch transaction | |||||
| func (d *defaultResourceManager) BranchRollback(branchType model.BranchType, xid string, branchId int64, resourceId, applicationData string) (model.BranchStatus, error) { | |||||
| return d.getResourceManager(branchType).BranchRollback(branchType, xid, branchId, resourceId, applicationData) | |||||
| } | |||||
| // Branch register long | |||||
| func (d *defaultResourceManager) BranchRegister(branchType model.BranchType, resourceId, clientId, xid, applicationData, lockKeys string) (int64, error) { | |||||
| return d.getResourceManager(branchType).BranchRegister(branchType, resourceId, clientId, xid, applicationData, lockKeys) | |||||
| } | |||||
| // Branch report | |||||
| func (d *defaultResourceManager) BranchReport(branchType model.BranchType, xid string, branchId int64, status model.BranchStatus, applicationData string) error { | |||||
| return d.getResourceManager(branchType).BranchReport(branchType, xid, branchId, status, applicationData) | |||||
| } | |||||
| // Lock query boolean | |||||
| func (d *defaultResourceManager) LockQuery(branchType model.BranchType, resourceId, xid, lockKeys string) (bool, error) { | |||||
| return d.getResourceManager(branchType).LockQuery(branchType, resourceId, xid, lockKeys) | |||||
| } | |||||
| // Register a model.Resource to be managed by model.Resource Manager | |||||
| func (d *defaultResourceManager) RegisterResource(resource model.Resource) error { | |||||
| return d.getResourceManager(resource.GetBranchType()).RegisterResource(resource) | |||||
| } | |||||
| // Unregister a model.Resource from the model.Resource Manager | |||||
| func (d *defaultResourceManager) UnregisterResource(resource model.Resource) error { | |||||
| return d.getResourceManager(resource.GetBranchType()).UnregisterResource(resource) | |||||
| } | |||||
| // Get all resources managed by this manager | |||||
| func (d *defaultResourceManager) GetManagedResources() map[string]model.Resource { | |||||
| var allResource map[string]model.Resource | |||||
| resourceManagerMap.Range(func(branchType, resourceManager interface{}) bool { | |||||
| rs := resourceManager.(model.ResourceManager).GetManagedResources() | |||||
| for k, v := range rs { | |||||
| rs[k] = v | |||||
| } | |||||
| return true | |||||
| }) | |||||
| return allResource | |||||
| } | |||||
| // Get the model.BranchType | |||||
| func (d *defaultResourceManager) GetBranchType() model.BranchType { | |||||
| panic("DefaultResourceManager isn't a real ResourceManager") | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| package tcc | |||||
| import ( | |||||
| "reflect" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/model" | |||||
| ) | |||||
| type TCCResource struct { | |||||
| ResourceGroupId string `default:"DEFAULT"` | |||||
| AppName string | |||||
| ActionName string | |||||
| TargetBean interface{} | |||||
| PrepareMethod reflect.Method | |||||
| CommitMethodName string | |||||
| CommitMethod reflect.Method | |||||
| RollbackMethodName string | |||||
| RollbackMethod reflect.Method | |||||
| } | |||||
| func (t *TCCResource) GetResourceGroupId() string { | |||||
| return t.ResourceGroupId | |||||
| } | |||||
| func (t *TCCResource) GetResourceId() string { | |||||
| return t.ActionName | |||||
| } | |||||
| func (t *TCCResource) GetBranchType() model.BranchType { | |||||
| return model.TCC | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| package tcc | |||||
| import ( | |||||
| "context" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/rm/api" | |||||
| ) | |||||
| type TCCService interface { | |||||
| Prepare(ctx context.Context, param interface{}) error | |||||
| Commit(ctx context.Context, businessActionContext api.BusinessActionContext) error | |||||
| Rollback(ctx context.Context, businessActionContext api.BusinessActionContext) error | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| package tcc | |||||
| @@ -0,0 +1,279 @@ | |||||
| package rpc11 | |||||
| import ( | |||||
| "bytes" | |||||
| "encoding/binary" | |||||
| ) | |||||
| import ( | |||||
| getty "github.com/apache/dubbo-getty" | |||||
| "github.com/pkg/errors" | |||||
| "vimagination.zapto.org/byteio" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| "github.com/seata/seata-go/pkg/protocol/codec" | |||||
| ) | |||||
| /** | |||||
| * <pre> | |||||
| * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |||||
| * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |||||
| * | magic |Proto| Full length | Head | Msg |Seria|Compr| RequestID | | |||||
| * | code |colVer| (head+body) | Length |Type |lizer|ess | | | |||||
| * +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||||
| * | | | |||||
| * | Head Map [Optional] | | |||||
| * +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||||
| * | | | |||||
| * | body | | |||||
| * | | | |||||
| * | ... ... | | |||||
| * +-----------------------------------------------------------------------------------------------+ | |||||
| * </pre> | |||||
| * <p> | |||||
| * <li>Full Length: include all data </li> | |||||
| * <li>Head Length: include head data from magic code to head map. </li> | |||||
| * <li>Body Length: Full Length - Head Length</li> | |||||
| * </p> | |||||
| * https://github.com/seata/seata/issues/893 | |||||
| */ | |||||
| const ( | |||||
| SeataV1PackageHeaderReservedLength = 16 | |||||
| ) | |||||
| var ( | |||||
| // RpcPkgHandler | |||||
| rpcPkgHandler = &RpcPackageHandler{} | |||||
| ) | |||||
| var ( | |||||
| ErrNotEnoughStream = errors.New("packet stream is not enough") | |||||
| ErrTooLargePackage = errors.New("package length is exceed the getty package's legal maximum length.") | |||||
| ErrInvalidPackage = errors.New("invalid rpc package") | |||||
| ErrIllegalMagic = errors.New("package magic is not right.") | |||||
| ) | |||||
| type RpcPackageHandler struct{} | |||||
| type SeataV1PackageHeader struct { | |||||
| Magic0 byte | |||||
| Magic1 byte | |||||
| Version byte | |||||
| TotalLength uint32 | |||||
| HeadLength uint16 | |||||
| MessageType byte | |||||
| CodecType byte | |||||
| CompressType byte | |||||
| ID uint32 | |||||
| Meta map[string]string | |||||
| BodyLength uint32 | |||||
| } | |||||
| func (h *SeataV1PackageHeader) Unmarshal(buf *bytes.Buffer) (int, error) { | |||||
| bufLen := buf.Len() | |||||
| if bufLen < SeataV1PackageHeaderReservedLength { | |||||
| return 0, ErrNotEnoughStream | |||||
| } | |||||
| // magic | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.Magic0)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.Magic1)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| if h.Magic0 != protocol.MAGIC_CODE_BYTES[0] || h.Magic1 != protocol.MAGIC_CODE_BYTES[1] { | |||||
| return 0, ErrIllegalMagic | |||||
| } | |||||
| // version | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.Version)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // TODO check version compatible here | |||||
| // total length | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.TotalLength)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // head length | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.HeadLength)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // message type | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.MessageType)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // codec type | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.CodecType)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // compress type | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.CompressType)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // id | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.ID)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // todo meta map | |||||
| if h.HeadLength > SeataV1PackageHeaderReservedLength { | |||||
| headMapLength := h.HeadLength - SeataV1PackageHeaderReservedLength | |||||
| h.Meta = headMapDecode(buf.Bytes()[:headMapLength]) | |||||
| } | |||||
| h.BodyLength = h.TotalLength - uint32(h.HeadLength) | |||||
| return int(h.TotalLength), nil | |||||
| } | |||||
| // Read read binary data from to rpc message | |||||
| func (p *RpcPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { | |||||
| var header SeataV1PackageHeader | |||||
| buf := bytes.NewBuffer(data) | |||||
| _, err := header.Unmarshal(buf) | |||||
| if err != nil { | |||||
| if err == ErrNotEnoughStream { | |||||
| // getty case2 | |||||
| return nil, 0, nil | |||||
| } | |||||
| // getty case1 | |||||
| return nil, 0, err | |||||
| } | |||||
| if uint32(len(data)) < header.TotalLength { | |||||
| // get case3 | |||||
| return nil, int(header.TotalLength), nil | |||||
| } | |||||
| //r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||||
| rpcMessage := protocol.RpcMessage{ | |||||
| Codec: header.CodecType, | |||||
| ID: int32(header.ID), | |||||
| Compressor: header.CompressType, | |||||
| MessageType: header.MessageType, | |||||
| HeadMap: header.Meta, | |||||
| } | |||||
| if header.MessageType == protocol.MSGTypeHeartbeatRequest { | |||||
| rpcMessage.Body = protocol.HeartBeatMessagePing | |||||
| } else if header.MessageType == protocol.MSGTypeHeartbeatResponse { | |||||
| rpcMessage.Body = protocol.HeartBeatMessagePong | |||||
| } else { | |||||
| if header.BodyLength > 0 { | |||||
| //todo compress | |||||
| msg, _ := codec.MessageDecoder(header.CodecType, data[header.HeadLength:]) | |||||
| rpcMessage.Body = msg | |||||
| } | |||||
| } | |||||
| return rpcMessage, int(header.TotalLength), nil | |||||
| } | |||||
| // Write write rpc message to binary data | |||||
| func (p *RpcPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { | |||||
| msg, ok := pkg.(protocol.RpcMessage) | |||||
| if !ok { | |||||
| return nil, ErrInvalidPackage | |||||
| } | |||||
| fullLength := protocol.V1HeadLength | |||||
| headLength := protocol.V1HeadLength | |||||
| var result = make([]byte, 0, fullLength) | |||||
| var b bytes.Buffer | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| result = append(result, protocol.MAGIC_CODE_BYTES[:2]...) | |||||
| result = append(result, protocol.VERSION) | |||||
| w.WriteByte(msg.MessageType) | |||||
| w.WriteByte(msg.Codec) | |||||
| w.WriteByte(msg.Compressor) | |||||
| w.WriteInt32(msg.ID) | |||||
| if msg.HeadMap != nil && len(msg.HeadMap) > 0 { | |||||
| headMapBytes, headMapLength := headMapEncode(msg.HeadMap) | |||||
| headLength += headMapLength | |||||
| fullLength += headMapLength | |||||
| w.Write(headMapBytes) | |||||
| } | |||||
| if msg.MessageType != protocol.MSGTypeHeartbeatRequest && | |||||
| msg.MessageType != protocol.MSGTypeHeartbeatResponse { | |||||
| bodyBytes := codec.MessageEncoder(msg.Codec, msg.Body) | |||||
| fullLength += len(bodyBytes) | |||||
| w.Write(bodyBytes) | |||||
| } | |||||
| fullLen := int32(fullLength) | |||||
| headLen := int16(headLength) | |||||
| result = append(result, []byte{byte(fullLen >> 24), byte(fullLen >> 16), byte(fullLen >> 8), byte(fullLen)}...) | |||||
| result = append(result, []byte{byte(headLen >> 8), byte(headLen)}...) | |||||
| result = append(result, b.Bytes()...) | |||||
| return result, nil | |||||
| } | |||||
| func headMapDecode(data []byte) map[string]string { | |||||
| size := len(data) | |||||
| if size == 0 { | |||||
| return nil | |||||
| } | |||||
| mp := make(map[string]string) | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||||
| readLength := 0 | |||||
| for readLength < size { | |||||
| var key, value string | |||||
| lengthK, _, _ := r.ReadUint16() | |||||
| if lengthK < 0 { | |||||
| break | |||||
| } else if lengthK == 0 { | |||||
| key = "" | |||||
| } else { | |||||
| key, _, _ = r.ReadString(int(lengthK)) | |||||
| } | |||||
| lengthV, _, _ := r.ReadUint16() | |||||
| if lengthV < 0 { | |||||
| break | |||||
| } else if lengthV == 0 { | |||||
| value = "" | |||||
| } else { | |||||
| value, _, _ = r.ReadString(int(lengthV)) | |||||
| } | |||||
| mp[key] = value | |||||
| readLength += int(lengthK + lengthV) | |||||
| } | |||||
| return mp | |||||
| } | |||||
| func headMapEncode(data map[string]string) ([]byte, int) { | |||||
| var b bytes.Buffer | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| for k, v := range data { | |||||
| if k == "" { | |||||
| w.WriteUint16(0) | |||||
| } else { | |||||
| w.WriteUint16(uint16(len(k))) | |||||
| w.WriteString(k) | |||||
| } | |||||
| if v == "" { | |||||
| w.WriteUint16(0) | |||||
| } else { | |||||
| w.WriteUint16(uint16(len(v))) | |||||
| w.WriteString(v) | |||||
| } | |||||
| } | |||||
| return b.Bytes(), b.Len() | |||||
| } | |||||
| @@ -0,0 +1,159 @@ | |||||
| package rpc11 | |||||
| import ( | |||||
| "fmt" | |||||
| "net" | |||||
| "sync" | |||||
| "sync/atomic" | |||||
| ) | |||||
| import ( | |||||
| getty "github.com/apache/dubbo-getty" | |||||
| gxsync "github.com/dubbogo/gost/sync" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/config" | |||||
| log "github.com/seata/seata-go/pkg/util/log" | |||||
| ) | |||||
| var ( | |||||
| rpcClient *RpcClient | |||||
| MAX_CHECK_ALIVE_RETRY = 600 | |||||
| CHECK_ALIVE_INTERNAL = 100 | |||||
| ) | |||||
| func init() { | |||||
| rpcClient = newRpcClient() | |||||
| } | |||||
| type RpcClient struct { | |||||
| lock sync.RWMutex | |||||
| conf *config.ClientConfig | |||||
| gettyClients []getty.Client | |||||
| listener getty.EventListener | |||||
| // serverAddress -> rpc_client.Session -> bool | |||||
| serverSessionsMap sync.Map | |||||
| sessionSize int32 | |||||
| } | |||||
| func newRpcClient() *RpcClient { | |||||
| rpcClient := &RpcClient{ | |||||
| conf: config.GetClientConfig(), | |||||
| gettyClients: make([]getty.Client, 0), | |||||
| listener: NewClientEventHandler(), | |||||
| } | |||||
| rpcClient.init() | |||||
| return rpcClient | |||||
| } | |||||
| func (r *RpcClient) init() { | |||||
| //todo 暂时写死地址,待改为配置 | |||||
| //addressList := []string{"127.0.0.1:8091"} | |||||
| addressList := []string{"127.0.0.1:8090"} | |||||
| if len(addressList) == 0 { | |||||
| log.Warn("no have valid seata server list") | |||||
| } | |||||
| for _, address := range addressList { | |||||
| gettyClient := getty.NewTCPClient( | |||||
| getty.WithServerAddress(address), | |||||
| getty.WithConnectionNumber((int)(r.conf.GettyConfig.ConnectionNum)), | |||||
| getty.WithReconnectInterval(r.conf.GettyConfig.ReconnectInterval), | |||||
| getty.WithClientTaskPool(gxsync.NewTaskPoolSimple(2)), | |||||
| ) | |||||
| gettyClient.RunEventLoop(r.newSession) | |||||
| r.gettyClients = append(r.gettyClients, gettyClient) | |||||
| } | |||||
| } | |||||
| func (r *RpcClient) newSession(session getty.Session) error { | |||||
| var ( | |||||
| ok bool | |||||
| tcpConn *net.TCPConn | |||||
| ) | |||||
| if r.conf.GettyConfig.GettySessionParam.CompressEncoding { | |||||
| session.SetCompressType(getty.CompressZip) | |||||
| } | |||||
| if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { | |||||
| panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) | |||||
| } | |||||
| tcpConn.SetNoDelay(r.conf.GettyConfig.GettySessionParam.TCPNoDelay) | |||||
| tcpConn.SetKeepAlive(r.conf.GettyConfig.GettySessionParam.TCPKeepAlive) | |||||
| if r.conf.GettyConfig.GettySessionParam.TCPKeepAlive { | |||||
| tcpConn.SetKeepAlivePeriod(r.conf.GettyConfig.GettySessionParam.KeepAlivePeriod) | |||||
| } | |||||
| tcpConn.SetReadBuffer(r.conf.GettyConfig.GettySessionParam.TCPRBufSize) | |||||
| tcpConn.SetWriteBuffer(r.conf.GettyConfig.GettySessionParam.TCPWBufSize) | |||||
| session.SetName(r.conf.GettyConfig.GettySessionParam.SessionName) | |||||
| session.SetMaxMsgLen(r.conf.GettyConfig.GettySessionParam.MaxMsgLen) | |||||
| session.SetPkgHandler(rpcPkgHandler) | |||||
| session.SetEventListener(r.listener) | |||||
| session.SetReadTimeout(r.conf.GettyConfig.GettySessionParam.TCPReadTimeout) | |||||
| session.SetWriteTimeout(r.conf.GettyConfig.GettySessionParam.TCPWriteTimeout) | |||||
| session.SetCronPeriod((int)(r.conf.GettyConfig.HeartbeatPeriod.Nanoseconds() / 1e6)) | |||||
| session.SetWaitTime(r.conf.GettyConfig.GettySessionParam.WaitTimeout) | |||||
| log.Debugf("rpc_client new session:%s", session.Stat()) | |||||
| return nil | |||||
| } | |||||
| func (r *RpcClient) AcquireGettySession() getty.Session { | |||||
| // map 遍历是随机的 | |||||
| var session getty.Session | |||||
| r.serverSessionsMap.Range(func(key, value interface{}) bool { | |||||
| session = key.(getty.Session) | |||||
| if session.IsClosed() { | |||||
| r.ReleaseGettySession(session) | |||||
| } else { | |||||
| return false | |||||
| } | |||||
| return true | |||||
| }) | |||||
| if session != nil { | |||||
| return session | |||||
| } | |||||
| //if sessionSize == 0 { | |||||
| // ticker := time.NewTicker(time.Duration(CHECK_ALIVE_INTERNAL) * time.Millisecond) | |||||
| // defer ticker.Stop() | |||||
| // for i := 0; i < MAX_CHECK_ALIVE_RETRY; i++ { | |||||
| // <-ticker.C | |||||
| // allSessions.Range(func(key, value interface{}) bool { | |||||
| // session = key.(getty.Session) | |||||
| // if session.IsClosed() { | |||||
| // sessionManager.ReleaseGettySession(session) | |||||
| // } else { | |||||
| // return false | |||||
| // } | |||||
| // return true | |||||
| // }) | |||||
| // if session != nil { | |||||
| // return session | |||||
| // } | |||||
| // } | |||||
| //} | |||||
| return nil | |||||
| } | |||||
| func (r *RpcClient) RegisterGettySession(session getty.Session) { | |||||
| //r.serverSessionsMap.Store(session, true) | |||||
| m, _ := r.serverSessionsMap.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||||
| sMap := m.(*sync.Map) | |||||
| sMap.Store(session, true) | |||||
| atomic.AddInt32(&r.sessionSize, 1) | |||||
| } | |||||
| func (r *RpcClient) ReleaseGettySession(session getty.Session) { | |||||
| r.serverSessionsMap.Delete(session) | |||||
| if !session.IsClosed() { | |||||
| m, _ := r.serverSessionsMap.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||||
| sMap := m.(*sync.Map) | |||||
| sMap.Delete(session) | |||||
| session.Close() | |||||
| } | |||||
| atomic.AddInt32(&r.sessionSize, -1) | |||||
| } | |||||
| @@ -0,0 +1,93 @@ | |||||
| package rpc11 | |||||
| import ( | |||||
| "time" | |||||
| ) | |||||
| import ( | |||||
| getty "github.com/apache/dubbo-getty" | |||||
| "github.com/pkg/errors" | |||||
| "go.uber.org/atomic" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| "github.com/seata/seata-go/pkg/protocol/codec" | |||||
| log "github.com/seata/seata-go/pkg/util/log" | |||||
| ) | |||||
| type ClientEventHandler struct { | |||||
| } | |||||
| func NewClientEventHandler() *ClientEventHandler { | |||||
| return &ClientEventHandler{} | |||||
| } | |||||
| func (h *ClientEventHandler) OnOpen(session getty.Session) error { | |||||
| log.Infof("OnOpen session{%s} open", session.Stat()) | |||||
| rpcClient.RegisterGettySession(session) | |||||
| return nil | |||||
| } | |||||
| func (h *ClientEventHandler) OnError(session getty.Session, err error) { | |||||
| log.Infof("OnError session{%s} got error{%v}, will be closed.", session.Stat(), err) | |||||
| } | |||||
| func (h *ClientEventHandler) OnClose(session getty.Session) { | |||||
| log.Infof("OnClose session{%s} is closing......", session.Stat()) | |||||
| } | |||||
| func (h *ClientEventHandler) OnMessage(session getty.Session, pkg interface{}) { | |||||
| s, ok := pkg.(string) | |||||
| if !ok { | |||||
| log.Infof("illegal packge{%#v}", pkg) | |||||
| return | |||||
| } | |||||
| log.Infof("OnMessage: %s", s) | |||||
| } | |||||
| func (h *ClientEventHandler) OnCron(session getty.Session) { | |||||
| active := session.GetActive() | |||||
| if 20*time.Second.Nanoseconds() < time.Since(active).Nanoseconds() { | |||||
| log.Infof("OnCorn session{%s} timeout{%s}", session.Stat(), time.Since(active).String()) | |||||
| session.Close() | |||||
| } | |||||
| } | |||||
| func (client *ClientEventHandler) sendMergedMessage(mergedMessage protocol.MergedWarpMessage) { | |||||
| ss := rpcClient.AcquireGettySession() | |||||
| err := client.sendAsync(ss, mergedMessage) | |||||
| if err != nil { | |||||
| log.Errorf("error sendMergedMessage") | |||||
| } | |||||
| } | |||||
| func (client *ClientEventHandler) sendAsync(session getty.Session, msg interface{}) error { | |||||
| var err error | |||||
| if session == nil || session.IsClosed() { | |||||
| log.Warn("sendAsyncRequestWithResponse nothing, caused by null channel.") | |||||
| } | |||||
| idGenerator := atomic.Uint32{} | |||||
| rpcMessage := protocol.RpcMessage{ | |||||
| ID: int32(idGenerator.Inc()), | |||||
| MessageType: protocol.MSGTypeRequestOneway, | |||||
| Codec: codec.SEATA, | |||||
| Compressor: 0, | |||||
| Body: msg, | |||||
| } | |||||
| log.Infof("store message, id %d : %#v", rpcMessage.ID, msg) | |||||
| //client.mergeMsgMap.Store(rpcMessage.ID, msg) | |||||
| //config timeout | |||||
| pkgLen, sendLen, err := session.WritePkg(rpcMessage, time.Duration(0)) | |||||
| if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||||
| log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||||
| //runtime.GoWithRecover(func() { | |||||
| // session.Close() | |||||
| //}, nil) | |||||
| return errors.Wrap(err, "pkg not send completely!") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -0,0 +1,27 @@ | |||||
| package rpc11 | |||||
| import ( | |||||
| "testing" | |||||
| "time" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| ) | |||||
| func TestSendMergedMessage(test *testing.T) { | |||||
| request := protocol.RegisterRMRequest{ | |||||
| ResourceIds: "1111", | |||||
| AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||||
| ApplicationId: "ApplicationID", | |||||
| TransactionServiceGroup: "TransactionServiceGroup", | |||||
| }, | |||||
| } | |||||
| mergedMessage := protocol.MergedWarpMessage{ | |||||
| Msgs: []protocol.MessageTypeAware{request}, | |||||
| MsgIds: []int32{1212}, | |||||
| } | |||||
| handler := NewClientEventHandler() | |||||
| handler.sendMergedMessage(mergedMessage) | |||||
| time.Sleep(100000 * time.Second) | |||||
| } | |||||
| @@ -0,0 +1,21 @@ | |||||
| package rpc_client | |||||
| import ( | |||||
| "time" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| ) | |||||
| type ClientMessageSender interface { | |||||
| // Send msg with response object. | |||||
| SendMsgWithResponse(msg interface{}) (interface{}, error) | |||||
| // Send msg with response object. | |||||
| SendMsgWithResponseAndTimeout(msg interface{}, timeout time.Duration) (interface{}, error) | |||||
| // Send response. | |||||
| SendResponse(request protocol.RpcMessage, serverAddress string, msg interface{}) | |||||
| } | |||||
| @@ -0,0 +1,101 @@ | |||||
| package rpc_client | |||||
| import ( | |||||
| "sync" | |||||
| "sync/atomic" | |||||
| "time" | |||||
| ) | |||||
| import ( | |||||
| getty "github.com/apache/dubbo-getty" | |||||
| ) | |||||
| var ( | |||||
| MAX_CHECK_ALIVE_RETRY = 600 | |||||
| CHECK_ALIVE_INTERNAL = 100 | |||||
| allSessions = sync.Map{} | |||||
| // serverAddress -> rpc_client.Session -> bool | |||||
| serverSessions = sync.Map{} | |||||
| sessionSize int32 = 0 | |||||
| clientSessionManager = &GettyClientSessionManager{} | |||||
| ) | |||||
| type GettyClientSessionManager struct{} | |||||
| func (sessionManager *GettyClientSessionManager) AcquireGettySession() getty.Session { | |||||
| // map 遍历是随机的 | |||||
| var session getty.Session | |||||
| allSessions.Range(func(key, value interface{}) bool { | |||||
| session = key.(getty.Session) | |||||
| if session.IsClosed() { | |||||
| sessionManager.ReleaseGettySession(session) | |||||
| } else { | |||||
| return false | |||||
| } | |||||
| return true | |||||
| }) | |||||
| if session != nil { | |||||
| return session | |||||
| } | |||||
| if sessionSize == 0 { | |||||
| ticker := time.NewTicker(time.Duration(CHECK_ALIVE_INTERNAL) * time.Millisecond) | |||||
| defer ticker.Stop() | |||||
| for i := 0; i < MAX_CHECK_ALIVE_RETRY; i++ { | |||||
| <-ticker.C | |||||
| allSessions.Range(func(key, value interface{}) bool { | |||||
| session = key.(getty.Session) | |||||
| if session.IsClosed() { | |||||
| sessionManager.ReleaseGettySession(session) | |||||
| } else { | |||||
| return false | |||||
| } | |||||
| return true | |||||
| }) | |||||
| if session != nil { | |||||
| return session | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (sessionManager *GettyClientSessionManager) AcquireGettySessionByServerAddress(serverAddress string) getty.Session { | |||||
| m, _ := serverSessions.LoadOrStore(serverAddress, &sync.Map{}) | |||||
| sMap := m.(*sync.Map) | |||||
| var session getty.Session | |||||
| sMap.Range(func(key, value interface{}) bool { | |||||
| session = key.(getty.Session) | |||||
| if session.IsClosed() { | |||||
| sessionManager.ReleaseGettySession(session) | |||||
| } else { | |||||
| return false | |||||
| } | |||||
| return true | |||||
| }) | |||||
| return session | |||||
| } | |||||
| func (sessionManager *GettyClientSessionManager) ReleaseGettySession(session getty.Session) { | |||||
| allSessions.Delete(session) | |||||
| if !session.IsClosed() { | |||||
| m, _ := serverSessions.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||||
| sMap := m.(*sync.Map) | |||||
| sMap.Delete(session) | |||||
| session.Close() | |||||
| } | |||||
| atomic.AddInt32(&sessionSize, -1) | |||||
| } | |||||
| func (sessionManager *GettyClientSessionManager) RegisterGettySession(session getty.Session) { | |||||
| allSessions.Store(session, true) | |||||
| m, _ := serverSessions.LoadOrStore(session.RemoteAddr(), &sync.Map{}) | |||||
| sMap := m.(*sync.Map) | |||||
| sMap.Store(session, true) | |||||
| atomic.AddInt32(&sessionSize, 1) | |||||
| } | |||||
| @@ -0,0 +1,279 @@ | |||||
| package rpc_client | |||||
| import ( | |||||
| "bytes" | |||||
| "encoding/binary" | |||||
| ) | |||||
| import ( | |||||
| getty "github.com/apache/dubbo-getty" | |||||
| "github.com/pkg/errors" | |||||
| "vimagination.zapto.org/byteio" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| "github.com/seata/seata-go/pkg/protocol/codec" | |||||
| ) | |||||
| /** | |||||
| * <pre> | |||||
| * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |||||
| * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |||||
| * | magic |Proto| Full length | Head | Msg |Seria|Compr| RequestID | | |||||
| * | code |colVer| (head+body) | Length |Type |lizer|ess | | | |||||
| * +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||||
| * | | | |||||
| * | Head Map [Optional] | | |||||
| * +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+ | |||||
| * | | | |||||
| * | body | | |||||
| * | | | |||||
| * | ... ... | | |||||
| * +-----------------------------------------------------------------------------------------------+ | |||||
| * </pre> | |||||
| * <p> | |||||
| * <li>Full Length: include all data </li> | |||||
| * <li>Head Length: include head data from magic code to head map. </li> | |||||
| * <li>Body Length: Full Length - Head Length</li> | |||||
| * </p> | |||||
| * https://github.com/seata/seata/issues/893 | |||||
| */ | |||||
| const ( | |||||
| SeataV1PackageHeaderReservedLength = 16 | |||||
| ) | |||||
| var ( | |||||
| // RpcPkgHandler | |||||
| rpcPkgHandler = &RpcPackageHandler{} | |||||
| ) | |||||
| var ( | |||||
| ErrNotEnoughStream = errors.New("packet stream is not enough") | |||||
| ErrTooLargePackage = errors.New("package length is exceed the getty package's legal maximum length.") | |||||
| ErrInvalidPackage = errors.New("invalid rpc package") | |||||
| ErrIllegalMagic = errors.New("package magic is not right.") | |||||
| ) | |||||
| type RpcPackageHandler struct{} | |||||
| type SeataV1PackageHeader struct { | |||||
| Magic0 byte | |||||
| Magic1 byte | |||||
| Version byte | |||||
| TotalLength uint32 | |||||
| HeadLength uint16 | |||||
| MessageType byte | |||||
| CodecType byte | |||||
| CompressType byte | |||||
| ID uint32 | |||||
| Meta map[string]string | |||||
| BodyLength uint32 | |||||
| } | |||||
| func (h *SeataV1PackageHeader) Unmarshal(buf *bytes.Buffer) (int, error) { | |||||
| bufLen := buf.Len() | |||||
| if bufLen < SeataV1PackageHeaderReservedLength { | |||||
| return 0, ErrNotEnoughStream | |||||
| } | |||||
| // magic | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.Magic0)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.Magic1)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| if h.Magic0 != protocol.MAGIC_CODE_BYTES[0] || h.Magic1 != protocol.MAGIC_CODE_BYTES[1] { | |||||
| return 0, ErrIllegalMagic | |||||
| } | |||||
| // version | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.Version)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // TODO check version compatible here | |||||
| // total length | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.TotalLength)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // head length | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.HeadLength)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // message type | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.MessageType)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // codec type | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.CodecType)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // compress type | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.CompressType)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // id | |||||
| if err := binary.Read(buf, binary.BigEndian, &(h.ID)); err != nil { | |||||
| return 0, err | |||||
| } | |||||
| // todo meta map | |||||
| if h.HeadLength > SeataV1PackageHeaderReservedLength { | |||||
| headMapLength := h.HeadLength - SeataV1PackageHeaderReservedLength | |||||
| h.Meta = headMapDecode(buf.Bytes()[:headMapLength]) | |||||
| } | |||||
| h.BodyLength = h.TotalLength - uint32(h.HeadLength) | |||||
| return int(h.TotalLength), nil | |||||
| } | |||||
| // Read read binary data from to rpc message | |||||
| func (p *RpcPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { | |||||
| var header SeataV1PackageHeader | |||||
| buf := bytes.NewBuffer(data) | |||||
| _, err := header.Unmarshal(buf) | |||||
| if err != nil { | |||||
| if err == ErrNotEnoughStream { | |||||
| // getty case2 | |||||
| return nil, 0, nil | |||||
| } | |||||
| // getty case1 | |||||
| return nil, 0, err | |||||
| } | |||||
| if uint32(len(data)) < header.TotalLength { | |||||
| // get case3 | |||||
| return nil, int(header.TotalLength), nil | |||||
| } | |||||
| //r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||||
| rpcMessage := protocol.RpcMessage{ | |||||
| Codec: header.CodecType, | |||||
| ID: int32(header.ID), | |||||
| Compressor: header.CompressType, | |||||
| MessageType: header.MessageType, | |||||
| HeadMap: header.Meta, | |||||
| } | |||||
| if header.MessageType == protocol.MSGTypeHeartbeatRequest { | |||||
| rpcMessage.Body = protocol.HeartBeatMessagePing | |||||
| } else if header.MessageType == protocol.MSGTypeHeartbeatResponse { | |||||
| rpcMessage.Body = protocol.HeartBeatMessagePong | |||||
| } else { | |||||
| if header.BodyLength > 0 { | |||||
| //todo compress | |||||
| msg, _ := codec.MessageDecoder(header.CodecType, data[header.HeadLength:]) | |||||
| rpcMessage.Body = msg | |||||
| } | |||||
| } | |||||
| return rpcMessage, int(header.TotalLength), nil | |||||
| } | |||||
| // Write write rpc message to binary data | |||||
| func (p *RpcPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { | |||||
| msg, ok := pkg.(protocol.RpcMessage) | |||||
| if !ok { | |||||
| return nil, ErrInvalidPackage | |||||
| } | |||||
| fullLength := protocol.V1HeadLength | |||||
| headLength := protocol.V1HeadLength | |||||
| var result = make([]byte, 0, fullLength) | |||||
| var b bytes.Buffer | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| result = append(result, protocol.MAGIC_CODE_BYTES[:2]...) | |||||
| result = append(result, protocol.VERSION) | |||||
| w.WriteByte(msg.MessageType) | |||||
| w.WriteByte(msg.Codec) | |||||
| w.WriteByte(msg.Compressor) | |||||
| w.WriteInt32(msg.ID) | |||||
| if msg.HeadMap != nil && len(msg.HeadMap) > 0 { | |||||
| headMapBytes, headMapLength := headMapEncode(msg.HeadMap) | |||||
| headLength += headMapLength | |||||
| fullLength += headMapLength | |||||
| w.Write(headMapBytes) | |||||
| } | |||||
| if msg.MessageType != protocol.MSGTypeHeartbeatRequest && | |||||
| msg.MessageType != protocol.MSGTypeHeartbeatResponse { | |||||
| bodyBytes := codec.MessageEncoder(msg.Codec, msg.Body) | |||||
| fullLength += len(bodyBytes) | |||||
| w.Write(bodyBytes) | |||||
| } | |||||
| fullLen := int32(fullLength) | |||||
| headLen := int16(headLength) | |||||
| result = append(result, []byte{byte(fullLen >> 24), byte(fullLen >> 16), byte(fullLen >> 8), byte(fullLen)}...) | |||||
| result = append(result, []byte{byte(headLen >> 8), byte(headLen)}...) | |||||
| result = append(result, b.Bytes()...) | |||||
| return result, nil | |||||
| } | |||||
| func headMapDecode(data []byte) map[string]string { | |||||
| size := len(data) | |||||
| if size == 0 { | |||||
| return nil | |||||
| } | |||||
| mp := make(map[string]string) | |||||
| r := byteio.BigEndianReader{Reader: bytes.NewReader(data)} | |||||
| readLength := 0 | |||||
| for readLength < size { | |||||
| var key, value string | |||||
| lengthK, _, _ := r.ReadUint16() | |||||
| if lengthK < 0 { | |||||
| break | |||||
| } else if lengthK == 0 { | |||||
| key = "" | |||||
| } else { | |||||
| key, _, _ = r.ReadString(int(lengthK)) | |||||
| } | |||||
| lengthV, _, _ := r.ReadUint16() | |||||
| if lengthV < 0 { | |||||
| break | |||||
| } else if lengthV == 0 { | |||||
| value = "" | |||||
| } else { | |||||
| value, _, _ = r.ReadString(int(lengthV)) | |||||
| } | |||||
| mp[key] = value | |||||
| readLength += int(lengthK + lengthV) | |||||
| } | |||||
| return mp | |||||
| } | |||||
| func headMapEncode(data map[string]string) ([]byte, int) { | |||||
| var b bytes.Buffer | |||||
| w := byteio.BigEndianWriter{Writer: &b} | |||||
| for k, v := range data { | |||||
| if k == "" { | |||||
| w.WriteUint16(0) | |||||
| } else { | |||||
| w.WriteUint16(uint16(len(k))) | |||||
| w.WriteString(k) | |||||
| } | |||||
| if v == "" { | |||||
| w.WriteUint16(0) | |||||
| } else { | |||||
| w.WriteUint16(uint16(len(v))) | |||||
| w.WriteString(v) | |||||
| } | |||||
| } | |||||
| return b.Bytes(), b.Len() | |||||
| } | |||||
| @@ -0,0 +1,102 @@ | |||||
| package rpc_client | |||||
| import ( | |||||
| "fmt" | |||||
| "github.com/seata/seata-go/pkg/config" | |||||
| "github.com/seata/seata-go/pkg/util/log" | |||||
| "net" | |||||
| ) | |||||
| import ( | |||||
| getty "github.com/apache/dubbo-getty" | |||||
| gxsync "github.com/dubbogo/gost/sync" | |||||
| ) | |||||
| type RpcClient struct { | |||||
| conf *config.ClientConfig | |||||
| gettyClients []getty.Client | |||||
| rpcHandler RpcRemoteClient | |||||
| } | |||||
| func init() { | |||||
| newRpcClient() | |||||
| } | |||||
| func newRpcClient() *RpcClient { | |||||
| rpcClient := &RpcClient{ | |||||
| conf: config.GetClientConfig(), | |||||
| gettyClients: make([]getty.Client, 0), | |||||
| rpcHandler: *InitRpcRemoteClient(), | |||||
| } | |||||
| rpcClient.init() | |||||
| return rpcClient | |||||
| } | |||||
| func (c *RpcClient) init() { | |||||
| addressList := getAvailServerList(c.conf) | |||||
| if len(addressList) == 0 { | |||||
| log.Warn("no have valid seata server list") | |||||
| } | |||||
| for _, address := range addressList { | |||||
| gettyClient := getty.NewTCPClient( | |||||
| getty.WithServerAddress(address), | |||||
| getty.WithConnectionNumber((int)(c.conf.GettyConfig.ConnectionNum)), | |||||
| getty.WithReconnectInterval(c.conf.GettyConfig.ReconnectInterval), | |||||
| getty.WithClientTaskPool(gxsync.NewTaskPoolSimple(0)), | |||||
| ) | |||||
| go gettyClient.RunEventLoop(c.newSession) | |||||
| // c.gettyClients = append(c.gettyClients, gettyClient) | |||||
| } | |||||
| } | |||||
| // todo mock | |||||
| func getAvailServerList(config *config.ClientConfig) []string { | |||||
| //reg, err := extension.GetRegistry(config.RegistryConfig.Mode) | |||||
| //if err != nil { | |||||
| // logger.Errorf("Registry can not connect success, program is going to panic.Error message is %s", err.Error()) | |||||
| // panic(err.Error()) | |||||
| //} | |||||
| //addrs, err := reg.Lookup() | |||||
| //if err != nil { | |||||
| // logger.Errorf("no hava valid server list", err.Error()) | |||||
| // return nil | |||||
| //} | |||||
| //return addrs | |||||
| return []string{"127.0.0.1:8091"} | |||||
| } | |||||
| func (c *RpcClient) newSession(session getty.Session) error { | |||||
| var ( | |||||
| ok bool | |||||
| tcpConn *net.TCPConn | |||||
| ) | |||||
| if c.conf.GettyConfig.GettySessionParam.CompressEncoding { | |||||
| session.SetCompressType(getty.CompressZip) | |||||
| } | |||||
| if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { | |||||
| panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) | |||||
| } | |||||
| tcpConn.SetNoDelay(c.conf.GettyConfig.GettySessionParam.TCPNoDelay) | |||||
| tcpConn.SetKeepAlive(c.conf.GettyConfig.GettySessionParam.TCPKeepAlive) | |||||
| if c.conf.GettyConfig.GettySessionParam.TCPKeepAlive { | |||||
| tcpConn.SetKeepAlivePeriod(c.conf.GettyConfig.GettySessionParam.KeepAlivePeriod) | |||||
| } | |||||
| tcpConn.SetReadBuffer(c.conf.GettyConfig.GettySessionParam.TCPRBufSize) | |||||
| tcpConn.SetWriteBuffer(c.conf.GettyConfig.GettySessionParam.TCPWBufSize) | |||||
| session.SetName(c.conf.GettyConfig.GettySessionParam.SessionName) | |||||
| session.SetMaxMsgLen(c.conf.GettyConfig.GettySessionParam.MaxMsgLen) | |||||
| session.SetPkgHandler(rpcPkgHandler) | |||||
| session.SetEventListener(&c.rpcHandler) | |||||
| session.SetReadTimeout(c.conf.GettyConfig.GettySessionParam.TCPReadTimeout) | |||||
| session.SetWriteTimeout(c.conf.GettyConfig.GettySessionParam.TCPWriteTimeout) | |||||
| session.SetCronPeriod((int)(c.conf.GettyConfig.HeartbeatPeriod.Nanoseconds() / 1e6)) | |||||
| session.SetWaitTime(c.conf.GettyConfig.GettySessionParam.WaitTimeout) | |||||
| log.Debugf("rpc_client new session:%s\n", session.Stat()) | |||||
| return nil | |||||
| } | |||||
| @@ -0,0 +1,412 @@ | |||||
| package rpc_client | |||||
| import ( | |||||
| "math/rand" | |||||
| "strings" | |||||
| "sync" | |||||
| "time" | |||||
| ) | |||||
| import ( | |||||
| getty "github.com/apache/dubbo-getty" | |||||
| gxtime "github.com/dubbogo/gost/time" | |||||
| "github.com/pkg/errors" | |||||
| "go.uber.org/atomic" | |||||
| ) | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/config" | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| "github.com/seata/seata-go/pkg/protocol/codec" | |||||
| log "github.com/seata/seata-go/pkg/util/log" | |||||
| "github.com/seata/seata-go/pkg/util/runtime" | |||||
| ) | |||||
| const ( | |||||
| RPC_REQUEST_TIMEOUT = 30 * time.Second | |||||
| ) | |||||
| var rpcRemoteClient *RpcRemoteClient | |||||
| func InitRpcRemoteClient() *RpcRemoteClient { | |||||
| rpcRemoteClient = &RpcRemoteClient{ | |||||
| conf: config.GetClientConfig(), | |||||
| idGenerator: &atomic.Uint32{}, | |||||
| futures: &sync.Map{}, | |||||
| mergeMsgMap: &sync.Map{}, | |||||
| rpcMessageChannel: make(chan protocol.RpcMessage, 100), | |||||
| BranchRollbackRequestChannel: make(chan RpcRMMessage), | |||||
| BranchCommitRequestChannel: make(chan RpcRMMessage), | |||||
| GettySessionOnOpenChannel: make(chan string), | |||||
| } | |||||
| if rpcRemoteClient.conf.EnableClientBatchSendRequest { | |||||
| go rpcRemoteClient.processMergedMessage() | |||||
| } | |||||
| return rpcRemoteClient | |||||
| } | |||||
| func GetRpcRemoteClient() *RpcRemoteClient { | |||||
| return rpcRemoteClient | |||||
| } | |||||
| type RpcRemoteClient struct { | |||||
| conf *config.ClientConfig | |||||
| idGenerator *atomic.Uint32 | |||||
| futures *sync.Map | |||||
| mergeMsgMap *sync.Map | |||||
| rpcMessageChannel chan protocol.RpcMessage | |||||
| BranchCommitRequestChannel chan RpcRMMessage | |||||
| BranchRollbackRequestChannel chan RpcRMMessage | |||||
| GettySessionOnOpenChannel chan string | |||||
| } | |||||
| // OnOpen ... | |||||
| func (client *RpcRemoteClient) OnOpen(session getty.Session) error { | |||||
| go func() { | |||||
| request := protocol.RegisterTMRequest{AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||||
| Version: client.conf.SeataVersion, | |||||
| ApplicationId: client.conf.ApplicationID, | |||||
| TransactionServiceGroup: client.conf.TransactionServiceGroup, | |||||
| }} | |||||
| _, err := client.sendAsyncRequestWithResponse(session, request, RPC_REQUEST_TIMEOUT) | |||||
| if err == nil { | |||||
| clientSessionManager.RegisterGettySession(session) | |||||
| client.GettySessionOnOpenChannel <- session.RemoteAddr() | |||||
| } | |||||
| }() | |||||
| return nil | |||||
| } | |||||
| // OnError ... | |||||
| func (client *RpcRemoteClient) OnError(session getty.Session, err error) { | |||||
| clientSessionManager.ReleaseGettySession(session) | |||||
| } | |||||
| // OnClose ... | |||||
| func (client *RpcRemoteClient) OnClose(session getty.Session) { | |||||
| clientSessionManager.ReleaseGettySession(session) | |||||
| } | |||||
| // OnMessage ... | |||||
| func (client *RpcRemoteClient) OnMessage(session getty.Session, pkg interface{}) { | |||||
| log.Debugf("received message: {%#v}", pkg) | |||||
| rpcMessage, ok := pkg.(protocol.RpcMessage) | |||||
| if !ok { | |||||
| log.Errorf("received message is not protocol.RpcMessage. pkg: %#v", pkg) | |||||
| return | |||||
| } | |||||
| heartBeat, isHeartBeat := rpcMessage.Body.(protocol.HeartBeatMessage) | |||||
| if isHeartBeat && heartBeat == protocol.HeartBeatMessagePong { | |||||
| log.Debugf("received PONG from %s", session.RemoteAddr()) | |||||
| return | |||||
| } | |||||
| if rpcMessage.MessageType == protocol.MSGTypeRequest || | |||||
| rpcMessage.MessageType == protocol.MSGTypeRequestOneway { | |||||
| log.Debugf("msgID: %d, body: %#v", rpcMessage.ID, rpcMessage.Body) | |||||
| client.onMessage(rpcMessage, session.RemoteAddr()) | |||||
| return | |||||
| } | |||||
| mergedResult, isMergedResult := rpcMessage.Body.(protocol.MergeResultMessage) | |||||
| if isMergedResult { | |||||
| mm, loaded := client.mergeMsgMap.Load(rpcMessage.ID) | |||||
| if loaded { | |||||
| mergedMessage := mm.(protocol.MergedWarpMessage) | |||||
| log.Infof("rpcMessageID: %d, rpcMessage: %#v, result: %#v", rpcMessage.ID, mergedMessage, mergedResult) | |||||
| for i := 0; i < len(mergedMessage.Msgs); i++ { | |||||
| msgID := mergedMessage.MsgIds[i] | |||||
| resp, loaded := client.futures.Load(msgID) | |||||
| if loaded { | |||||
| response := resp.(*protocol.MessageFuture) | |||||
| response.Response = mergedResult.Msgs[i] | |||||
| response.Done <- true | |||||
| client.futures.Delete(msgID) | |||||
| } | |||||
| } | |||||
| client.mergeMsgMap.Delete(rpcMessage.ID) | |||||
| } | |||||
| } else { | |||||
| resp, loaded := client.futures.Load(rpcMessage.ID) | |||||
| if loaded { | |||||
| response := resp.(*protocol.MessageFuture) | |||||
| response.Response = rpcMessage.Body | |||||
| response.Done <- true | |||||
| client.futures.Delete(rpcMessage.ID) | |||||
| } | |||||
| } | |||||
| } | |||||
| // OnCron ... | |||||
| func (client *RpcRemoteClient) OnCron(session getty.Session) { | |||||
| client.defaultSendRequest(session, protocol.HeartBeatMessagePing) | |||||
| } | |||||
| func (client *RpcRemoteClient) onMessage(rpcMessage protocol.RpcMessage, serverAddress string) { | |||||
| msg := rpcMessage.Body.(protocol.MessageTypeAware) | |||||
| log.Debugf("onMessage: %#v", msg) | |||||
| switch msg.GetTypeCode() { | |||||
| case protocol.TypeBranchCommit: | |||||
| client.BranchCommitRequestChannel <- RpcRMMessage{ | |||||
| RpcMessage: rpcMessage, | |||||
| ServerAddress: serverAddress, | |||||
| } | |||||
| case protocol.TypeBranchRollback: | |||||
| client.BranchRollbackRequestChannel <- RpcRMMessage{ | |||||
| RpcMessage: rpcMessage, | |||||
| ServerAddress: serverAddress, | |||||
| } | |||||
| case protocol.TypeRmDeleteUndolog: | |||||
| break | |||||
| default: | |||||
| break | |||||
| } | |||||
| } | |||||
| //************************************* | |||||
| // ClientMessageSender | |||||
| //************************************* | |||||
| func (client *RpcRemoteClient) SendMsgWithResponse(msg interface{}) (interface{}, error) { | |||||
| if client.conf.EnableClientBatchSendRequest { | |||||
| return client.sendAsyncRequest2(msg, RPC_REQUEST_TIMEOUT) | |||||
| } | |||||
| return client.SendMsgWithResponseAndTimeout(msg, RPC_REQUEST_TIMEOUT) | |||||
| } | |||||
| func (client *RpcRemoteClient) SendMsgWithResponseAndTimeout(msg interface{}, timeout time.Duration) (interface{}, error) { | |||||
| ss := clientSessionManager.AcquireGettySession() | |||||
| return client.sendAsyncRequestWithResponse(ss, msg, timeout) | |||||
| } | |||||
| func (client *RpcRemoteClient) SendResponse(request protocol.RpcMessage, serverAddress string, msg interface{}) { | |||||
| client.defaultSendResponse(request, clientSessionManager.AcquireGettySessionByServerAddress(serverAddress), msg) | |||||
| } | |||||
| func (client *RpcRemoteClient) sendAsyncRequestWithResponse(session getty.Session, msg interface{}, timeout time.Duration) (interface{}, error) { | |||||
| if timeout <= time.Duration(0) { | |||||
| return nil, errors.New("timeout should more than 0ms") | |||||
| } | |||||
| return client.sendAsyncRequest(session, msg, timeout) | |||||
| } | |||||
| func (client *RpcRemoteClient) sendAsyncRequestWithoutResponse(session getty.Session, msg interface{}) error { | |||||
| _, err := client.sendAsyncRequest(session, msg, time.Duration(0)) | |||||
| return err | |||||
| } | |||||
| func (client *RpcRemoteClient) sendAsyncRequest(session getty.Session, msg interface{}, timeout time.Duration) (interface{}, error) { | |||||
| var err error | |||||
| if session == nil || session.IsClosed() { | |||||
| log.Warn("sendAsyncRequestWithResponse nothing, caused by null channel.") | |||||
| } | |||||
| rpcMessage := protocol.RpcMessage{ | |||||
| ID: int32(client.idGenerator.Inc()), | |||||
| MessageType: protocol.MSGTypeRequestOneway, | |||||
| Codec: codec.SEATA, | |||||
| Compressor: 0, | |||||
| Body: msg, | |||||
| } | |||||
| resp := protocol.NewMessageFuture(rpcMessage) | |||||
| client.futures.Store(rpcMessage.ID, resp) | |||||
| //config timeout | |||||
| _, _, err = session.WritePkg(rpcMessage, time.Duration(0)) | |||||
| if err != nil { | |||||
| client.futures.Delete(rpcMessage.ID) | |||||
| log.Errorf("send message: %#v, session: %s", rpcMessage, session.Stat()) | |||||
| return nil, err | |||||
| } | |||||
| log.Debugf("send message: %#v, session: %s", rpcMessage, session.Stat()) | |||||
| if timeout > time.Duration(0) { | |||||
| select { | |||||
| case <-gxtime.GetDefaultTimerWheel().After(timeout): | |||||
| client.futures.Delete(rpcMessage.ID) | |||||
| if session != nil { | |||||
| return nil, errors.Errorf("wait response timeout, ip: %s, request: %#v", session.RemoteAddr(), rpcMessage) | |||||
| } else { | |||||
| return nil, errors.Errorf("wait response timeout and session is nil, request: %#v", rpcMessage) | |||||
| } | |||||
| case <-resp.Done: | |||||
| err = resp.Err | |||||
| return resp.Response, err | |||||
| } | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| func (client *RpcRemoteClient) sendAsyncRequest2(msg interface{}, timeout time.Duration) (interface{}, error) { | |||||
| var err error | |||||
| rpcMessage := protocol.RpcMessage{ | |||||
| ID: int32(client.idGenerator.Inc()), | |||||
| MessageType: protocol.MSGTypeRequest, | |||||
| Codec: codec.SEATA, | |||||
| Compressor: 0, | |||||
| Body: msg, | |||||
| } | |||||
| resp := protocol.NewMessageFuture(rpcMessage) | |||||
| client.futures.Store(rpcMessage.ID, resp) | |||||
| client.rpcMessageChannel <- rpcMessage | |||||
| log.Infof("send message: %#v", rpcMessage) | |||||
| if timeout > time.Duration(0) { | |||||
| select { | |||||
| case <-gxtime.GetDefaultTimerWheel().After(timeout): | |||||
| client.futures.Delete(rpcMessage.ID) | |||||
| return nil, errors.Errorf("wait response timeout, request: %#v", rpcMessage) | |||||
| case <-resp.Done: | |||||
| err = resp.Err | |||||
| } | |||||
| return resp.Response, err | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| func (client *RpcRemoteClient) sendAsync(session getty.Session, msg interface{}) error { | |||||
| var err error | |||||
| if session == nil || session.IsClosed() { | |||||
| log.Warn("sendAsyncRequestWithResponse nothing, caused by null channel.") | |||||
| } | |||||
| rpcMessage := protocol.RpcMessage{ | |||||
| ID: int32(client.idGenerator.Inc()), | |||||
| MessageType: protocol.MSGTypeRequestOneway, | |||||
| Codec: codec.SEATA, | |||||
| Compressor: 0, | |||||
| Body: msg, | |||||
| } | |||||
| log.Infof("store message, id %d : %#v", rpcMessage.ID, msg) | |||||
| client.mergeMsgMap.Store(rpcMessage.ID, msg) | |||||
| //config timeout | |||||
| pkgLen, sendLen, err := session.WritePkg(rpcMessage, time.Duration(0)) | |||||
| if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||||
| log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||||
| runtime.GoWithRecover(func() { | |||||
| session.Close() | |||||
| }, nil) | |||||
| return errors.Wrap(err, "pkg not send completely!") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (client *RpcRemoteClient) defaultSendRequest(session getty.Session, msg interface{}) { | |||||
| rpcMessage := protocol.RpcMessage{ | |||||
| ID: int32(client.idGenerator.Inc()), | |||||
| Codec: codec.SEATA, | |||||
| Compressor: 0, | |||||
| Body: msg, | |||||
| } | |||||
| _, ok := msg.(protocol.HeartBeatMessage) | |||||
| if ok { | |||||
| rpcMessage.MessageType = protocol.MSGTypeHeartbeatRequest | |||||
| } else { | |||||
| rpcMessage.MessageType = protocol.MSGTypeRequest | |||||
| } | |||||
| pkgLen, sendLen, err := session.WritePkg(rpcMessage, client.conf.GettyConfig.GettySessionParam.TCPWriteTimeout) | |||||
| if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||||
| log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||||
| runtime.GoWithRecover(func() { | |||||
| session.Close() | |||||
| }, nil) | |||||
| } | |||||
| } | |||||
| func (client *RpcRemoteClient) defaultSendResponse(request protocol.RpcMessage, session getty.Session, msg interface{}) { | |||||
| resp := protocol.RpcMessage{ | |||||
| ID: request.ID, | |||||
| Codec: request.Codec, | |||||
| Compressor: request.Compressor, | |||||
| Body: msg, | |||||
| } | |||||
| _, ok := msg.(protocol.HeartBeatMessage) | |||||
| if ok { | |||||
| resp.MessageType = protocol.MSGTypeHeartbeatResponse | |||||
| } else { | |||||
| resp.MessageType = protocol.MSGTypeResponse | |||||
| } | |||||
| pkgLen, sendLen, err := session.WritePkg(resp, time.Duration(0)) | |||||
| if err != nil || (pkgLen != 0 && pkgLen != sendLen) { | |||||
| log.Warnf("start to close the session because %d of %d bytes data is sent success. err:%+v", sendLen, pkgLen, err) | |||||
| runtime.GoWithRecover(func() { | |||||
| session.Close() | |||||
| }, nil) | |||||
| } | |||||
| } | |||||
| func (client *RpcRemoteClient) RegisterResource(serverAddress string, request protocol.RegisterRMRequest) { | |||||
| session := clientSessionManager.AcquireGettySessionByServerAddress(serverAddress) | |||||
| if session != nil { | |||||
| err := client.sendAsyncRequestWithoutResponse(session, request) | |||||
| if err != nil { | |||||
| log.Errorf("register resource failed, session:{},resourceID:{}", session, request.ResourceIds) | |||||
| } | |||||
| } | |||||
| } | |||||
| func loadBalance(transactionServiceGroup string) string { | |||||
| addressList := getAddressList(transactionServiceGroup) | |||||
| if len(addressList) == 1 { | |||||
| return addressList[0] | |||||
| } | |||||
| return addressList[rand.Intn(len(addressList))] | |||||
| } | |||||
| func getAddressList(transactionServiceGroup string) []string { | |||||
| addressList := strings.Split(transactionServiceGroup, ",") | |||||
| return addressList | |||||
| } | |||||
| func (client *RpcRemoteClient) processMergedMessage() { | |||||
| ticker := time.NewTicker(5 * time.Millisecond) | |||||
| mergedMessage := protocol.MergedWarpMessage{ | |||||
| Msgs: make([]protocol.MessageTypeAware, 0), | |||||
| MsgIds: make([]int32, 0), | |||||
| } | |||||
| for { | |||||
| select { | |||||
| case rpcMessage := <-client.rpcMessageChannel: | |||||
| message := rpcMessage.Body.(protocol.MessageTypeAware) | |||||
| mergedMessage.Msgs = append(mergedMessage.Msgs, message) | |||||
| mergedMessage.MsgIds = append(mergedMessage.MsgIds, rpcMessage.ID) | |||||
| if len(mergedMessage.Msgs) == 20 { | |||||
| client.sendMergedMessage(mergedMessage) | |||||
| mergedMessage = protocol.MergedWarpMessage{ | |||||
| Msgs: make([]protocol.MessageTypeAware, 0), | |||||
| MsgIds: make([]int32, 0), | |||||
| } | |||||
| } | |||||
| case <-ticker.C: | |||||
| if len(mergedMessage.Msgs) > 0 { | |||||
| client.sendMergedMessage(mergedMessage) | |||||
| mergedMessage = protocol.MergedWarpMessage{ | |||||
| Msgs: make([]protocol.MessageTypeAware, 0), | |||||
| MsgIds: make([]int32, 0), | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| func (client *RpcRemoteClient) sendMergedMessage(mergedMessage protocol.MergedWarpMessage) { | |||||
| ss := clientSessionManager.AcquireGettySession() | |||||
| err := client.sendAsync(ss, mergedMessage) | |||||
| if err != nil { | |||||
| for _, id := range mergedMessage.MsgIds { | |||||
| resp, loaded := client.futures.Load(id) | |||||
| if loaded { | |||||
| response := resp.(*protocol.MessageFuture) | |||||
| response.Done <- true | |||||
| client.futures.Delete(id) | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| package rpc_client | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| "testing" | |||||
| "time" | |||||
| ) | |||||
| func TestSendMsgWithResponse(test *testing.T) { | |||||
| request := protocol.RegisterRMRequest{ | |||||
| ResourceIds: "1111", | |||||
| AbstractIdentifyRequest: protocol.AbstractIdentifyRequest{ | |||||
| ApplicationId: "ApplicationID", | |||||
| TransactionServiceGroup: "TransactionServiceGroup", | |||||
| }, | |||||
| } | |||||
| mergedMessage := protocol.MergedWarpMessage{ | |||||
| Msgs: []protocol.MessageTypeAware{request}, | |||||
| MsgIds: []int32{1212}, | |||||
| } | |||||
| handler := GetRpcRemoteClient() | |||||
| handler.sendMergedMessage(mergedMessage) | |||||
| time.Sleep(100000 * time.Second) | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package rpc_client | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/protocol" | |||||
| ) | |||||
| type RpcRMMessage struct { | |||||
| RpcMessage protocol.RpcMessage | |||||
| ServerAddress string | |||||
| } | |||||
| @@ -0,0 +1,45 @@ | |||||
| package api | |||||
| import ( | |||||
| "github.com/seata/seata-go/pkg/model" | |||||
| ) | |||||
| type GlobalTransactionRole int8 | |||||
| const ( | |||||
| LAUNCHER GlobalTransactionRole = 0 | |||||
| PARTICIPANT GlobalTransactionRole = 1 | |||||
| ) | |||||
| type GlobalTransaction interface { | |||||
| // Begin a new global transaction with given timeout and given name. | |||||
| begin(timeout int64, name string) error | |||||
| // Commit the global transaction. | |||||
| commit() error | |||||
| // Rollback the global transaction. | |||||
| rollback() error | |||||
| // Suspend the global transaction. | |||||
| suspend() (SuspendedResourcesHolder, error) | |||||
| // Resume the global transaction. | |||||
| resume(suspendedResourcesHolder SuspendedResourcesHolder) error | |||||
| // Ask TC for current status of the corresponding global transaction. | |||||
| getStatus() (model.GlobalStatus, error) | |||||
| // Get XID. | |||||
| getXid() string | |||||
| // report the global transaction status. | |||||
| globalReport(globalStatus model.GlobalStatus) error | |||||
| // local status of the global transaction. | |||||
| getLocalStatus() model.GlobalStatus | |||||
| // get global transaction role. | |||||
| getGlobalTransactionRole() GlobalTransactionRole | |||||
| } | |||||
| @@ -0,0 +1,11 @@ | |||||
| package api | |||||
| type SuspendedResourcesHolder struct { | |||||
| Xid string | |||||
| } | |||||
| func NewSuspendedResourcesHolder(xid string) SuspendedResourcesHolder { | |||||
| return SuspendedResourcesHolder{ | |||||
| Xid: xid, | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,218 @@ | |||||
| /* | |||||
| * Licensed to the Apache Software Foundation (ASF) under one or more | |||||
| * contributor license agreements. See the NOTICE file distributed with | |||||
| * this work for additional information regarding copyright ownership. | |||||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
| * (the "License"); you may not use this file except in compliance with | |||||
| * the License. You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package log | |||||
| import ( | |||||
| "bytes" | |||||
| "errors" | |||||
| "fmt" | |||||
| ) | |||||
| import ( | |||||
| "github.com/natefinch/lumberjack" | |||||
| "go.uber.org/zap" | |||||
| "go.uber.org/zap/zapcore" | |||||
| ) | |||||
| // Level represents the level of logging. | |||||
| type LogLevel int8 | |||||
| const ( | |||||
| // DebugLevel logs are typically voluminous, and are usually disabled in | |||||
| // production. | |||||
| DebugLevel = LogLevel(zapcore.DebugLevel) | |||||
| // InfoLevel is the default logging priority. | |||||
| InfoLevel = LogLevel(zapcore.InfoLevel) | |||||
| // WarnLevel logs are more important than Info, but don't need individual | |||||
| // human review. | |||||
| WarnLevel = LogLevel(zapcore.WarnLevel) | |||||
| // ErrorLevel logs are high-priority. If an application is running smoothly, | |||||
| // it shouldn't generate any error-level logs. | |||||
| ErrorLevel = LogLevel(zapcore.ErrorLevel) | |||||
| // PanicLevel logs a message, then panics. | |||||
| PanicLevel = LogLevel(zapcore.PanicLevel) | |||||
| // FatalLevel logs a message, then calls os.Exit(1). | |||||
| FatalLevel = LogLevel(zapcore.FatalLevel) | |||||
| ) | |||||
| func (l *LogLevel) UnmarshalText(text []byte) error { | |||||
| if l == nil { | |||||
| return errors.New("can't unmarshal a nil *Level") | |||||
| } | |||||
| if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) { | |||||
| return fmt.Errorf("unrecognized level: %q", text) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (l *LogLevel) unmarshalText(text []byte) bool { | |||||
| switch string(text) { | |||||
| case "debug", "DEBUG": | |||||
| *l = DebugLevel | |||||
| case "info", "INFO", "": // make the zero value useful | |||||
| *l = InfoLevel | |||||
| case "warn", "WARN": | |||||
| *l = WarnLevel | |||||
| case "error", "ERROR": | |||||
| *l = ErrorLevel | |||||
| case "panic", "PANIC": | |||||
| *l = PanicLevel | |||||
| case "fatal", "FATAL": | |||||
| *l = FatalLevel | |||||
| default: | |||||
| return false | |||||
| } | |||||
| return true | |||||
| } | |||||
| type Logger interface { | |||||
| Debug(v ...interface{}) | |||||
| Debugf(format string, v ...interface{}) | |||||
| Info(v ...interface{}) | |||||
| Infof(format string, v ...interface{}) | |||||
| Warn(v ...interface{}) | |||||
| Warnf(format string, v ...interface{}) | |||||
| Error(v ...interface{}) | |||||
| Errorf(format string, v ...interface{}) | |||||
| Panic(v ...interface{}) | |||||
| Panicf(format string, v ...interface{}) | |||||
| Fatal(v ...interface{}) | |||||
| Fatalf(format string, v ...interface{}) | |||||
| } | |||||
| var ( | |||||
| log Logger | |||||
| zapLogger *zap.Logger | |||||
| zapLoggerConfig = zap.NewDevelopmentConfig() | |||||
| zapLoggerEncoderConfig = zapcore.EncoderConfig{ | |||||
| TimeKey: "time", | |||||
| LevelKey: "level", | |||||
| NameKey: "logger", | |||||
| CallerKey: "caller", | |||||
| MessageKey: "message", | |||||
| StacktraceKey: "stacktrace", | |||||
| EncodeLevel: zapcore.CapitalColorLevelEncoder, | |||||
| EncodeTime: zapcore.ISO8601TimeEncoder, | |||||
| EncodeDuration: zapcore.SecondsDurationEncoder, | |||||
| EncodeCaller: zapcore.ShortCallerEncoder, | |||||
| } | |||||
| ) | |||||
| func init() { | |||||
| zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig | |||||
| zapLogger, _ = zapLoggerConfig.Build(zap.AddCallerSkip(1)) | |||||
| log = zapLogger.Sugar() | |||||
| } | |||||
| func Init(logPath string, level LogLevel) { | |||||
| lumberJackLogger := &lumberjack.Logger{ | |||||
| Filename: logPath, | |||||
| MaxSize: 10, | |||||
| MaxBackups: 5, | |||||
| MaxAge: 30, | |||||
| Compress: false, | |||||
| } | |||||
| syncer := zapcore.AddSync(lumberJackLogger) | |||||
| encoderConfig := zap.NewProductionEncoderConfig() | |||||
| encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder | |||||
| encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder | |||||
| encoder := zapcore.NewConsoleEncoder(encoderConfig) | |||||
| core := zapcore.NewCore(encoder, syncer, zap.NewAtomicLevelAt(zapcore.Level(level))) | |||||
| zapLogger = zap.New(core, zap.AddCaller()) | |||||
| log = zapLogger.Sugar() | |||||
| } | |||||
| // SetLogger: customize yourself logger. | |||||
| func SetLogger(logger Logger) { | |||||
| log = logger | |||||
| } | |||||
| // GetLogger get logger | |||||
| func GetLogger() Logger { | |||||
| return log | |||||
| } | |||||
| // Debug ... | |||||
| func Debug(v ...interface{}) { | |||||
| log.Debug(v...) | |||||
| } | |||||
| // Debugf ... | |||||
| func Debugf(format string, v ...interface{}) { | |||||
| log.Debugf(format, v...) | |||||
| } | |||||
| // Info ... | |||||
| func Info(v ...interface{}) { | |||||
| log.Info(v...) | |||||
| } | |||||
| // Infof ... | |||||
| func Infof(format string, v ...interface{}) { | |||||
| log.Infof(format, v...) | |||||
| } | |||||
| // Warn ... | |||||
| func Warn(v ...interface{}) { | |||||
| log.Warn(v...) | |||||
| } | |||||
| // Warnf ... | |||||
| func Warnf(format string, v ...interface{}) { | |||||
| log.Warnf(format, v...) | |||||
| } | |||||
| // Error ... | |||||
| func Error(v ...interface{}) { | |||||
| log.Error(v...) | |||||
| } | |||||
| // Errorf ... | |||||
| func Errorf(format string, v ...interface{}) { | |||||
| log.Errorf(format, v...) | |||||
| } | |||||
| // Panic ... | |||||
| func Panic(v ...interface{}) { | |||||
| log.Panic(v...) | |||||
| } | |||||
| // Panicf ... | |||||
| func Panicf(format string, v ...interface{}) { | |||||
| log.Panicf(format, v...) | |||||
| } | |||||
| // Fatal ... | |||||
| func Fatal(v ...interface{}) { | |||||
| log.Fatal(v...) | |||||
| } | |||||
| // Fatalf ... | |||||
| func Fatalf(format string, v ...interface{}) { | |||||
| log.Fatalf(format, v...) | |||||
| } | |||||
| @@ -0,0 +1,38 @@ | |||||
| package runtime | |||||
| import ( | |||||
| "fmt" | |||||
| "os" | |||||
| "runtime/debug" | |||||
| "time" | |||||
| ) | |||||
| var debugIgnoreStdout = false | |||||
| // GoWithRecover wraps a `go func()` with recover() | |||||
| func GoWithRecover(handler func(), recoverHandler func(r interface{})) { | |||||
| go func() { | |||||
| defer func() { | |||||
| if r := recover(); r != nil { | |||||
| // TODO: log | |||||
| if !debugIgnoreStdout { | |||||
| fmt.Fprintf(os.Stderr, "%s goroutine panic: %v\n%s\n", | |||||
| time.Now(), r, string(debug.Stack())) | |||||
| } | |||||
| if recoverHandler != nil { | |||||
| go func() { | |||||
| defer func() { | |||||
| if p := recover(); p != nil { | |||||
| if !debugIgnoreStdout { | |||||
| fmt.Fprintf(os.Stderr, "recover goroutine panic:%v\n%s\n", p, string(debug.Stack())) | |||||
| } | |||||
| } | |||||
| }() | |||||
| recoverHandler(r) | |||||
| }() | |||||
| } | |||||
| } | |||||
| }() | |||||
| handler() | |||||
| }() | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| /* | |||||
| * Licensed to the Apache Software Foundation (ASF) under one or more | |||||
| * contributor license agreements. See the NOTICE file distributed with | |||||
| * this work for additional information regarding copyright ownership. | |||||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
| * (the "License"); you may not use this file except in compliance with | |||||
| * the License. You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package test | |||||
| import ( | |||||
| "testing" | |||||
| ) | |||||
| func Test1(t *testing.T) { | |||||
| } | |||||
| @@ -0,0 +1,29 @@ | |||||
| DROP TABLE IF EXISTS `stock_tbl`; | |||||
| CREATE TABLE `stock_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; | |||||