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