* feat: new feature for service discovery 1.Completed the code for organizing the workflow. 2.Implemented FileRegistryService based on local configuration files. * style: go imports * refactor: avoid the uncertainty of SQL field order in unit test. refactor select_for_update_executor_test.go to avoid the uncertainty of SQL field order causing test code to pass and fail sometimes.tags/v2.0.0
| @@ -23,6 +23,7 @@ import ( | |||
| "github.com/seata/seata-go/pkg/datasource" | |||
| at "github.com/seata/seata-go/pkg/datasource/sql" | |||
| "github.com/seata/seata-go/pkg/datasource/sql/exec/config" | |||
| "github.com/seata/seata-go/pkg/discovery" | |||
| "github.com/seata/seata-go/pkg/integration" | |||
| remoteConfig "github.com/seata/seata-go/pkg/remoting/config" | |||
| "github.com/seata/seata-go/pkg/remoting/getty" | |||
| @@ -41,7 +42,7 @@ func Init() { | |||
| // InitPath init client with config path | |||
| func InitPath(configFilePath string) { | |||
| cfg := LoadPath(configFilePath) | |||
| initRegistry(cfg) | |||
| initRmClient(cfg) | |||
| initTmClient(cfg) | |||
| initDatasource() | |||
| @@ -51,6 +52,7 @@ var ( | |||
| onceInitTmClient sync.Once | |||
| onceInitRmClient sync.Once | |||
| onceInitDatasource sync.Once | |||
| onceInitRegistry sync.Once | |||
| ) | |||
| // InitTmClient init client tm client | |||
| @@ -95,3 +97,9 @@ func initDatasource() { | |||
| datasource.Init() | |||
| }) | |||
| } | |||
| func initRegistry(cfg *Config) { | |||
| onceInitRegistry.Do(func() { | |||
| discovery.InitRegistry(&cfg.ServiceConfig, &cfg.RegistryConfig) | |||
| }) | |||
| } | |||
| @@ -31,6 +31,7 @@ import ( | |||
| "github.com/knadh/koanf/parsers/toml" | |||
| "github.com/knadh/koanf/parsers/yaml" | |||
| "github.com/knadh/koanf/providers/rawbytes" | |||
| "github.com/seata/seata-go/pkg/discovery" | |||
| "github.com/seata/seata-go/pkg/datasource/sql" | |||
| "github.com/seata/seata-go/pkg/datasource/sql/undo" | |||
| @@ -81,7 +82,8 @@ type Config struct { | |||
| ClientConfig ClientConfig `yaml:"client" json:"client" koanf:"client"` | |||
| GettyConfig remoteConfig.Config `yaml:"getty" json:"getty" koanf:"getty"` | |||
| TransportConfig remoteConfig.TransportConfig `yaml:"transport" json:"transport" koanf:"transport"` | |||
| ServiceConfig tm.ServiceConfig `yaml:"service" json:"service" koanf:"service"` | |||
| ServiceConfig discovery.ServiceConfig `yaml:"service" json:"service" koanf:"service"` | |||
| RegistryConfig discovery.RegistryConfig `yaml:"registry" json:"registry" koanf:"registry"` | |||
| } | |||
| func (c *Config) RegisterFlags(f *flag.FlagSet) { | |||
| @@ -98,6 +100,7 @@ func (c *Config) RegisterFlags(f *flag.FlagSet) { | |||
| c.ClientConfig.RegisterFlagsWithPrefix("client", f) | |||
| c.GettyConfig.RegisterFlagsWithPrefix("getty", f) | |||
| c.TransportConfig.RegisterFlagsWithPrefix("transport", f) | |||
| c.RegistryConfig.RegisterFlagsWithPrefix("registry", f) | |||
| c.ServiceConfig.RegisterFlagsWithPrefix("service", f) | |||
| } | |||
| @@ -112,6 +112,20 @@ func TestLoadPath(t *testing.T) { | |||
| assert.Equal(t, "default", cfg.ServiceConfig.VgroupMapping["default_tx_group"]) | |||
| assert.Equal(t, "127.0.0.1:8091", cfg.ServiceConfig.Grouplist["default"]) | |||
| assert.NotNil(t, cfg.RegistryConfig) | |||
| assert.Equal(t, "file", cfg.RegistryConfig.Type) | |||
| assert.Equal(t, "seatago.yml", cfg.RegistryConfig.File.Name) | |||
| assert.Equal(t, "seata-server", cfg.RegistryConfig.Nacos.Application) | |||
| assert.Equal(t, "127.0.0.1:8848", cfg.RegistryConfig.Nacos.ServerAddr) | |||
| assert.Equal(t, "SEATA_GROUP", cfg.RegistryConfig.Nacos.Group) | |||
| assert.Equal(t, "test-namespace", cfg.RegistryConfig.Nacos.Namespace) | |||
| assert.Equal(t, "test-username", cfg.RegistryConfig.Nacos.Username) | |||
| assert.Equal(t, "test-password", cfg.RegistryConfig.Nacos.Password) | |||
| assert.Equal(t, "test-access-key", cfg.RegistryConfig.Nacos.AccessKey) | |||
| assert.Equal(t, "test-secret-key", cfg.RegistryConfig.Nacos.SecretKey) | |||
| assert.Equal(t, "default", cfg.RegistryConfig.Etcd3.Cluster) | |||
| assert.Equal(t, "http://localhost:2379", cfg.RegistryConfig.Etcd3.ServerAddr) | |||
| // reset flag.CommandLine | |||
| flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) | |||
| } | |||
| @@ -44,7 +44,8 @@ func TestBuildSelectPKSQL(t *testing.T) { | |||
| ctx, err := parser.DoParser(sql) | |||
| metaData := types.TableMeta{ | |||
| TableName: "t_user", | |||
| TableName: "t_user", | |||
| ColumnNames: []string{"id", "order_id", "age"}, | |||
| Indexs: map[string]types.IndexMeta{ | |||
| "id": { | |||
| IType: types.IndexTypePrimaryKey, | |||
| @@ -0,0 +1,39 @@ | |||
| /* | |||
| * 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 discovery | |||
| const ( | |||
| FILE string = "file" | |||
| NACOS string = "nacos" | |||
| ETCD string = "etcd" | |||
| EUREKA string = "eureka" | |||
| REDIS string = "redis" | |||
| ZK string = "zk" | |||
| CONSUL string = "consul" | |||
| SOFA string = "sofa" | |||
| ) | |||
| type ServiceInstance struct { | |||
| Addr string | |||
| Port int | |||
| } | |||
| type RegistryService interface { | |||
| Lookup(key string) ([]*ServiceInstance, error) | |||
| Close() | |||
| } | |||
| @@ -0,0 +1,92 @@ | |||
| /* | |||
| * 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 discovery | |||
| import ( | |||
| "flag" | |||
| "github.com/seata/seata-go/pkg/util/flagext" | |||
| ) | |||
| type ServiceConfig struct { | |||
| VgroupMapping flagext.StringMap `yaml:"vgroup-mapping" json:"vgroup-mapping" koanf:"vgroup-mapping"` | |||
| Grouplist flagext.StringMap `yaml:"grouplist" json:"grouplist" koanf:"grouplist"` | |||
| EnableDegrade bool `yaml:"enable-degrade" json:"enable-degrade" koanf:"enable-degrade"` | |||
| DisableGlobalTransaction bool `yaml:"disable-global-transaction" json:"disable-global-transaction" koanf:"disable-global-transaction"` | |||
| } | |||
| func (cfg *ServiceConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { | |||
| f.BoolVar(&cfg.EnableDegrade, prefix+".enable-degrade", false, "degrade current not support.") | |||
| f.BoolVar(&cfg.DisableGlobalTransaction, prefix+".disable-global-transaction", false, "disable globalTransaction.") | |||
| f.Var(&cfg.VgroupMapping, prefix+".vgroup-mapping", "The vgroup mapping.") | |||
| f.Var(&cfg.Grouplist, prefix+".grouplist", "The group list.") | |||
| } | |||
| type RegistryConfig struct { | |||
| Type string `yaml:"type" json:"type" koanf:"type"` | |||
| File FileConfig `yaml:"file" json:"file" koanf:"file"` | |||
| Nacos NacosConfig `yaml:"nacos" json:"nacos" koanf:"nacos"` | |||
| Etcd3 Etcd3Config `yaml:"etcd3" json:"etcd3" koanf:"etcd3"` | |||
| } | |||
| func (cfg *RegistryConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { | |||
| f.StringVar(&cfg.Type, prefix+".type", "file", "The registry type.") | |||
| cfg.File.RegisterFlagsWithPrefix(prefix+".file", f) | |||
| cfg.Nacos.RegisterFlagsWithPrefix(prefix+".nacos", f) | |||
| cfg.Etcd3.RegisterFlagsWithPrefix(prefix+".etcd3", f) | |||
| } | |||
| type FileConfig struct { | |||
| Name string `yaml:"name" json:"name" koanf:"name"` | |||
| } | |||
| func (cfg *FileConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { | |||
| f.StringVar(&cfg.Name, prefix+".name", "registry.conf", "The file name of registry.") | |||
| } | |||
| type NacosConfig struct { | |||
| Application string `yaml:"application" json:"application" koanf:"application"` | |||
| ServerAddr string `yaml:"server-addr" json:"server-addr" koanf:"server-addr"` | |||
| Group string `yaml:"group" json:"group" koanf:"group"` | |||
| Namespace string `yaml:"namespace" json:"namespace" koanf:"namespace"` | |||
| Username string `yaml:"username" json:"username" koanf:"username"` | |||
| Password string `yaml:"password" json:"password" koanf:"password"` | |||
| AccessKey string `yaml:"access-key" json:"access-key" koanf:"access-key"` | |||
| SecretKey string `yaml:"secret-key" json:"secret-key" koanf:"secret-key"` | |||
| } | |||
| func (cfg *NacosConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { | |||
| f.StringVar(&cfg.Application, prefix+".application", "seata", "The application name of registry.") | |||
| f.StringVar(&cfg.ServerAddr, prefix+".server-addr", "", "The server address of registry.") | |||
| f.StringVar(&cfg.Group, prefix+".group", "SEATA_GROUP", "The group of registry.") | |||
| f.StringVar(&cfg.Namespace, prefix+".namespace", "", "The namespace of registry.") | |||
| f.StringVar(&cfg.Username, prefix+".username", "", "The username of registry.") | |||
| f.StringVar(&cfg.Password, prefix+".password", "", "The password of registry.") | |||
| f.StringVar(&cfg.AccessKey, prefix+".access-key", "", "The access key of registry.") | |||
| f.StringVar(&cfg.SecretKey, prefix+".secret-key", "", "The secret key of registry.") | |||
| } | |||
| type Etcd3Config struct { | |||
| Cluster string `yaml:"cluster" json:"cluster" koanf:"cluster"` | |||
| ServerAddr string `yaml:"server-addr" json:"server-addr" koanf:"server-addr"` | |||
| } | |||
| func (cfg *Etcd3Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { | |||
| f.StringVar(&cfg.Cluster, prefix+".cluster", "default", "The server address of registry.") | |||
| f.StringVar(&cfg.ServerAddr, prefix+".server-addr", "http://localhost:2379", "The server address of registry.") | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* | |||
| * 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 discovery | |||
| type ConsulRegistryService struct{} | |||
| func (s *ConsulRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| func (s *ConsulRegistryService) Close() { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* | |||
| * 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 discovery | |||
| type EtcdRegistryService struct{} | |||
| func (s *EtcdRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| func (s *EtcdRegistryService) Close() { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* | |||
| * 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 discovery | |||
| type EurekaRegistryService struct{} | |||
| func (s *EurekaRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| func (s *EurekaRegistryService) Close() { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| @@ -0,0 +1,88 @@ | |||
| /* | |||
| * 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 discovery | |||
| import ( | |||
| "fmt" | |||
| "strconv" | |||
| "strings" | |||
| "github.com/seata/seata-go/pkg/util/log" | |||
| ) | |||
| const ( | |||
| endPointSplitChar = ";" | |||
| ipPortSplitChar = ":" | |||
| ) | |||
| type FileRegistryService struct { | |||
| serviceConfig *ServiceConfig | |||
| } | |||
| func newFileRegistryService(config *ServiceConfig) RegistryService { | |||
| if config == nil { | |||
| log.Fatalf("service config is nil") | |||
| panic("service config is nil") | |||
| } | |||
| return &FileRegistryService{ | |||
| serviceConfig: config, | |||
| } | |||
| } | |||
| func (s *FileRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| var group string | |||
| if v, ok := s.serviceConfig.VgroupMapping[key]; ok { | |||
| group = v | |||
| } | |||
| if group == "" { | |||
| log.Errorf("vgroup is empty. key: %s", key) | |||
| return nil, fmt.Errorf("vgroup is empty. key: %s", key) | |||
| } | |||
| var addrStr string | |||
| if v, ok := s.serviceConfig.Grouplist[group]; ok { | |||
| addrStr = v | |||
| } | |||
| if addrStr == "" { | |||
| log.Errorf("endpoint is empty. key: %s group: %s", group) | |||
| return nil, fmt.Errorf("endpoint is empty. key: %s group: %s", key, group) | |||
| } | |||
| addrs := strings.Split(addrStr, endPointSplitChar) | |||
| instances := make([]*ServiceInstance, 0) | |||
| for _, addr := range addrs { | |||
| ipPort := strings.Split(addr, ipPortSplitChar) | |||
| if len(ipPort) != 2 { | |||
| return nil, fmt.Errorf("endpoint format should like ip:port. endpoint: %s", addr) | |||
| } | |||
| ip := ipPort[0] | |||
| port, err := strconv.Atoi(ipPort[1]) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| instances = append(instances, &ServiceInstance{ | |||
| Addr: ip, | |||
| Port: port, | |||
| }) | |||
| } | |||
| return instances, nil | |||
| } | |||
| func (s *FileRegistryService) Close() { | |||
| } | |||
| @@ -0,0 +1,178 @@ | |||
| /* | |||
| * 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 discovery | |||
| import ( | |||
| "reflect" | |||
| "testing" | |||
| ) | |||
| func TestFileRegistryService_Lookup(t *testing.T) { | |||
| type fields struct { | |||
| serviceConfig *ServiceConfig | |||
| } | |||
| type args struct { | |||
| key string | |||
| } | |||
| tests := []struct { | |||
| name string | |||
| fields fields | |||
| args args | |||
| want []*ServiceInstance | |||
| wantErr bool | |||
| wantErrMsg string | |||
| }{ | |||
| { | |||
| name: "normal single endpoint.", | |||
| args: args{ | |||
| key: "default_tx_group", | |||
| }, | |||
| fields: fields{ | |||
| serviceConfig: &ServiceConfig{ | |||
| VgroupMapping: map[string]string{ | |||
| "default_tx_group": "default", | |||
| }, | |||
| Grouplist: map[string]string{ | |||
| "default": "127.0.0.1:8091", | |||
| }, | |||
| }, | |||
| }, | |||
| want: []*ServiceInstance{ | |||
| { | |||
| Addr: "127.0.0.1", | |||
| Port: 8091, | |||
| }, | |||
| }, | |||
| wantErr: false, | |||
| }, | |||
| { | |||
| name: "normal multi endpoints.", | |||
| args: args{ | |||
| key: "default_tx_group", | |||
| }, | |||
| fields: fields{ | |||
| serviceConfig: &ServiceConfig{ | |||
| VgroupMapping: map[string]string{ | |||
| "default_tx_group": "default", | |||
| }, | |||
| Grouplist: map[string]string{ | |||
| "default": "127.0.0.1:8091;192.168.0.1:8092", | |||
| }, | |||
| }, | |||
| }, | |||
| want: []*ServiceInstance{ | |||
| { | |||
| Addr: "127.0.0.1", | |||
| Port: 8091, | |||
| }, | |||
| { | |||
| Addr: "192.168.0.1", | |||
| Port: 8092, | |||
| }, | |||
| }, | |||
| wantErr: false, | |||
| }, | |||
| { | |||
| name: "vgroup is empty.", | |||
| args: args{ | |||
| key: "default_tx_group", | |||
| }, | |||
| fields: fields{ | |||
| serviceConfig: &ServiceConfig{ | |||
| VgroupMapping: map[string]string{ | |||
| "default_tx_group": "", | |||
| }, | |||
| }, | |||
| }, | |||
| want: nil, | |||
| wantErr: true, | |||
| wantErrMsg: "vgroup is empty. key: default_tx_group", | |||
| }, | |||
| { | |||
| name: "endpoint is empty.", | |||
| args: args{ | |||
| key: "default_tx_group", | |||
| }, | |||
| fields: fields{ | |||
| serviceConfig: &ServiceConfig{ | |||
| VgroupMapping: map[string]string{ | |||
| "default_tx_group": "default", | |||
| }, | |||
| }, | |||
| }, | |||
| want: nil, | |||
| wantErr: true, | |||
| wantErrMsg: "endpoint is empty. key: default_tx_group group: default", | |||
| }, | |||
| { | |||
| name: "format is not ip:port", | |||
| args: args{ | |||
| key: "default_tx_group", | |||
| }, | |||
| fields: fields{ | |||
| serviceConfig: &ServiceConfig{ | |||
| VgroupMapping: map[string]string{ | |||
| "default_tx_group": "default", | |||
| }, | |||
| Grouplist: map[string]string{ | |||
| "default": "127.0.0.18091", | |||
| }, | |||
| }, | |||
| }, | |||
| want: nil, | |||
| wantErr: true, | |||
| wantErrMsg: "endpoint format should like ip:port. endpoint: 127.0.0.18091", | |||
| }, | |||
| { | |||
| name: "port is not number", | |||
| args: args{ | |||
| key: "default_tx_group", | |||
| }, | |||
| fields: fields{ | |||
| serviceConfig: &ServiceConfig{ | |||
| VgroupMapping: map[string]string{ | |||
| "default_tx_group": "default", | |||
| }, | |||
| Grouplist: map[string]string{ | |||
| "default": "127.0.0.1:abc", | |||
| }, | |||
| }, | |||
| }, | |||
| want: nil, | |||
| wantErr: true, | |||
| wantErrMsg: "strconv.Atoi: parsing \"abc\": invalid syntax", | |||
| }, | |||
| } | |||
| for _, tt := range tests { | |||
| t.Run(tt.name, func(t *testing.T) { | |||
| s := &FileRegistryService{ | |||
| serviceConfig: tt.fields.serviceConfig, | |||
| } | |||
| got, err := s.Lookup(tt.args.key) | |||
| if (err != nil) != tt.wantErr { | |||
| t.Errorf("Lookup() error = %v, wantErr = %v", err, tt.wantErr) | |||
| } | |||
| if tt.wantErr && err.Error() != tt.wantErrMsg { | |||
| t.Errorf("Lookup() errMsg = %v, wantErrMsg = %v", err.Error(), tt.wantErrMsg) | |||
| } | |||
| if !tt.wantErr && !reflect.DeepEqual(got, tt.want) { | |||
| t.Errorf("Lookup() got = %v, want = %v", got, tt.want) | |||
| } | |||
| }) | |||
| } | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| /* | |||
| * 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 discovery | |||
| import ( | |||
| "fmt" | |||
| ) | |||
| var ( | |||
| registryServiceInstance RegistryService | |||
| ) | |||
| func InitRegistry(serviceConfig *ServiceConfig, registryConfig *RegistryConfig) { | |||
| var registryService RegistryService | |||
| var err error | |||
| switch registryConfig.Type { | |||
| case FILE: | |||
| //init file registry | |||
| registryService = newFileRegistryService(serviceConfig) | |||
| case ETCD: | |||
| //TODO: init etcd registry | |||
| case NACOS: | |||
| //TODO: init nacos registry | |||
| case EUREKA: | |||
| //TODO: init eureka registry | |||
| case REDIS: | |||
| //TODO: init redis registry | |||
| case ZK: | |||
| //TODO: init zk registry | |||
| case CONSUL: | |||
| //TODO: init consul registry | |||
| case SOFA: | |||
| //TODO: init sofa registry | |||
| default: | |||
| err = fmt.Errorf("service registry not support registry type:%s", registryConfig.Type) | |||
| } | |||
| if err != nil { | |||
| panic(fmt.Errorf("init service registry err:%v", err)) | |||
| } | |||
| registryServiceInstance = registryService | |||
| } | |||
| func GetRegistry() RegistryService { | |||
| return registryServiceInstance | |||
| } | |||
| @@ -0,0 +1,78 @@ | |||
| /* | |||
| * 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 discovery | |||
| import ( | |||
| "reflect" | |||
| "testing" | |||
| ) | |||
| func TestInitRegistry(t *testing.T) { | |||
| type args struct { | |||
| serviceConfig *ServiceConfig | |||
| registryConfig *RegistryConfig | |||
| } | |||
| tests := []struct { | |||
| name string | |||
| args args | |||
| hasPanic bool | |||
| expectedType string | |||
| }{ | |||
| { | |||
| name: "normal", | |||
| args: args{ | |||
| registryConfig: &RegistryConfig{ | |||
| Type: FILE, | |||
| }, | |||
| serviceConfig: &ServiceConfig{}, | |||
| }, | |||
| expectedType: "FileRegistryService", | |||
| }, | |||
| { | |||
| name: "unknown type", | |||
| args: args{ | |||
| registryConfig: &RegistryConfig{ | |||
| Type: "unknown", | |||
| }, | |||
| serviceConfig: &ServiceConfig{}, | |||
| }, | |||
| hasPanic: true, | |||
| }, | |||
| } | |||
| for _, tt := range tests { | |||
| t.Run(tt.name, func(t *testing.T) { | |||
| defer func() { | |||
| if r := recover(); r != nil { | |||
| if !tt.hasPanic { | |||
| t.Errorf("panic is not expected!") | |||
| } | |||
| } else if tt.hasPanic { | |||
| t.Errorf("Expected a panic but did not receive one") | |||
| } | |||
| }() | |||
| InitRegistry(tt.args.serviceConfig, tt.args.registryConfig) | |||
| instance := GetRegistry() | |||
| if !tt.hasPanic { | |||
| actualType := reflect.TypeOf(instance).Elem().Name() | |||
| if actualType != tt.expectedType { | |||
| t.Errorf("type = %v, want %v", actualType, tt.expectedType) | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* | |||
| * 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 discovery | |||
| type NacosRegistryService struct{} | |||
| func (s *NacosRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| func (NacosRegistryService) Close() { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* | |||
| * 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 discovery | |||
| type RedisRegistryService struct{} | |||
| func (s *RedisRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| func (RedisRegistryService) Close() { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* | |||
| * 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 discovery | |||
| type SofaRegistryService struct{} | |||
| func (s *SofaRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| func (s *SofaRegistryService) Close() { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* | |||
| * 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 discovery | |||
| type ZkRegistryService struct{} | |||
| func (s *ZkRegistryService) Lookup(key string) ([]*ServiceInstance, error) { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| func (s *ZkRegistryService) Close() { | |||
| //TODO implement me | |||
| panic("implement me") | |||
| } | |||
| @@ -21,12 +21,11 @@ import ( | |||
| "crypto/tls" | |||
| "fmt" | |||
| "net" | |||
| "strings" | |||
| "sync" | |||
| getty "github.com/apache/dubbo-getty" | |||
| gxsync "github.com/dubbogo/gost/sync" | |||
| "github.com/seata/seata-go/pkg/discovery" | |||
| "github.com/seata/seata-go/pkg/protocol/codec" | |||
| "github.com/seata/seata-go/pkg/remoting/config" | |||
| "github.com/seata/seata-go/pkg/util/log" | |||
| @@ -57,7 +56,7 @@ func (c *RpcClient) init() { | |||
| } | |||
| for _, address := range addressList { | |||
| gettyClient := getty.NewTCPClient( | |||
| getty.WithServerAddress(address), | |||
| getty.WithServerAddress(fmt.Sprintf("%s:%d", address.Addr, address.Port)), | |||
| // todo if read c.gettyConf.ConnectionNum, will cause the connect to fail | |||
| getty.WithConnectionNumber(1), | |||
| getty.WithReconnectInterval(c.gettyConf.ReconnectInterval), | |||
| @@ -68,21 +67,13 @@ func (c *RpcClient) init() { | |||
| } | |||
| } | |||
| func (c *RpcClient) getAvailServerList() []string { | |||
| defaultAddressList := []string{"127.0.0.1:8091"} | |||
| txServiceGroup := c.seataConf.TxServiceGroup | |||
| if txServiceGroup == "" { | |||
| return defaultAddressList | |||
| } | |||
| clusterName := c.seataConf.ServiceVgroupMapping[txServiceGroup] | |||
| if clusterName == "" { | |||
| return defaultAddressList | |||
| } | |||
| grouplist := c.seataConf.ServiceGrouplist[clusterName] | |||
| if grouplist == "" { | |||
| return defaultAddressList | |||
| func (c *RpcClient) getAvailServerList() []*discovery.ServiceInstance { | |||
| registryService := discovery.GetRegistry() | |||
| instances, err := registryService.Lookup(c.seataConf.TxServiceGroup) | |||
| if err != nil { | |||
| return nil | |||
| } | |||
| return strings.Split(grouplist, ",") | |||
| return instances | |||
| } | |||
| func (c *RpcClient) newSession(session getty.Session) error { | |||
| @@ -20,8 +20,6 @@ package tm | |||
| import ( | |||
| "flag" | |||
| "time" | |||
| "github.com/seata/seata-go/pkg/util/flagext" | |||
| ) | |||
| type TmConfig struct { | |||
| @@ -43,17 +41,3 @@ func (cfg *TmConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { | |||
| f.DurationVar(&cfg.DegradeCheckAllowTimes, prefix+".degrade-check-allow-times", 10*time.Second, "The duration allowed for degrade checking.") | |||
| f.IntVar(&cfg.InterceptorOrder, prefix+".interceptor-order", -2147482648, "The order of interceptor.") | |||
| } | |||
| type ServiceConfig struct { | |||
| VgroupMapping flagext.StringMap `yaml:"vgroup-mapping" json:"vgroup-mapping" koanf:"vgroup-mapping"` | |||
| Grouplist flagext.StringMap `yaml:"grouplist" json:"grouplist" koanf:"grouplist"` | |||
| EnableDegrade bool `yaml:"enable-degrade" json:"enable-degrade" koanf:"enable-degrade"` | |||
| DisableGlobalTransaction bool `yaml:"disable-global-transaction" json:"disable-global-transaction" koanf:"disable-global-transaction"` | |||
| } | |||
| func (cfg *ServiceConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { | |||
| f.BoolVar(&cfg.EnableDegrade, prefix+".enable-degrade", false, "degrade current not support.") | |||
| f.BoolVar(&cfg.DisableGlobalTransaction, prefix+".disable-global-transaction", false, "disable globalTransaction.") | |||
| f.Var(&cfg.VgroupMapping, prefix+".vgroup-mapping", "The vgroup mapping.") | |||
| f.Var(&cfg.Grouplist, prefix+".grouplist", "The group list.") | |||
| } | |||
| @@ -122,17 +122,20 @@ seata: | |||
| registry: | |||
| type: file | |||
| file: | |||
| name: registry.conf | |||
| name: seatago.yml | |||
| nacos: | |||
| application: seata-server | |||
| server-addr: 127.0.0.1:8848 | |||
| group: "SEATA_GROUP" | |||
| namespace: "" | |||
| username: "" | |||
| password: "" | |||
| namespace: "test-namespace" | |||
| username: "test-username" | |||
| password: "test-password" | |||
| ##if use MSE Nacos with auth, mutex with username/password attribute # | |||
| #access-key: "" # | |||
| #secret-key: "" # | |||
| access-key: "test-access-key" # | |||
| secret-key: "test-secret-key" # | |||
| etcd3: | |||
| cluster: "default" | |||
| server-addr: "http://localhost:2379" | |||
| log: | |||
| exception-rate: 100 | |||
| tcc: | |||