Compare commits

..

39 Commits

Author SHA1 Message Date
binbin.zhang 2ca1b696b9
ignore cache content (#392) 2022-01-27 14:42:47 +08:00
binbin.zhang 844e4c3a45
update readme (#391) 2022-01-27 12:04:02 +08:00
binbin.zhang 414a46fac3
subscribe add appname (#390) 2022-01-26 20:47:21 +08:00
Gagharv 78dce8d303
support tls (#290)
Change-Id: I9ffece1abe1335c38790d04c2bca3cd0573b0903
2022-01-26 20:33:17 +08:00
Jerry dc509307fc
fix nacos server list rand panic
Co-authored-by: binbin.zhang <bbz17640380550@163.com>
2022-01-26 19:58:56 +08:00
binbin.zhang 23c3b33a81
subscriber return err (#389) 2022-01-26 19:02:14 +08:00
shuzhongling 9616d1548a
client automatically re-registers (#327)
* fix bug: when the nacos server start after the app using client, the nacos discover function may be not correct.

Co-authored-by: szl <13635432100@139.com>
2022-01-26 14:48:37 +08:00
赵延 b50677f109
support config flow control window size. (#346)
Co-authored-by: horizonzy <horizon@apache.org>
2022-01-26 14:31:56 +08:00
binbin.zhang b358dd0a84
add lumberjack logs can be divided by size and time (#387) 2022-01-26 14:00:21 +08:00
binbin.zhang 04e738e4c3
close stream client (#348) 2022-01-26 10:14:43 +08:00
binbin.zhang 02cd22fca1
update instance (#375) 2022-01-21 19:43:56 +08:00
believening e7a62ed728
fix a panic when requestProxy returns (#378)
a nil response response when calling PublishConfig
2022-01-21 19:42:24 +08:00
Frank Yang e4f5d94918
format and fix typo and fill comment
* format and fix typo and fill comment

* translate Chinese into English in comment

Co-authored-by: 杨延庆 <yangyq@300624.cn>
2022-01-13 09:26:29 +08:00
binbin.zhang 4ef6dce4d3
update example subscribeParam (#363) 2021-12-28 19:46:22 +08:00
赵延 2b21779c5f
Log print format optimization. (#358)
* log print format optimization.

* enhance log info.
2021-12-27 14:10:59 +08:00
binbin.zhang 07bc4095ca
close grpc client (#361) 2021-12-27 14:10:33 +08:00
Gerrard-YNWA 8cf7f352b0
bugfix: throw NewConfigProxy error (#355) 2021-12-20 21:18:10 +08:00
赵延 28f90d347f
Fix push again problem. (#347)
* fix push again problem.
2021-12-20 09:40:34 +08:00
赵延 d0e2fa985d
modify the keep alive config. (#349) 2021-12-17 10:34:59 +08:00
Gerrard-YNWA c4b577a5df
bugfix: subscribe params order (#336)
* bugfix: subscribe params order
2021-12-13 19:56:48 +08:00
赵延 4d6396bee3
when disconnect, sleep 1 second to avoid flush too many logs. (#340) 2021-12-07 23:05:16 +08:00
binbin.zhang 0fdfbf59d6
refine naming (#344) 2021-12-07 09:50:29 +08:00
赵延 5b6391ed54
Support dynamic refresh grpc connection. (#339)
* support dynamic refresh grpc connection.

Co-authored-by: horizonzy <horizon@apache.org>
2021-12-05 12:38:17 +08:00
赵延 952ae170cb
pre end loop. (#341)
Co-authored-by: horizonzy <horizon@apache.org>
2021-12-05 12:34:21 +08:00
赵延 91488fa1e7
format log info. (#343)
Co-authored-by: horizonzy <horizon@apache.org>
2021-12-05 12:30:48 +08:00
赵延 1f92c286e1
fix reconnect can't subscribe problem. (#338)
* fix reconnect can't subscribe problem.
2021-12-02 09:42:24 +08:00
李晓双 Li Xiao Shuang 0b971d688d
[ISSUE #249] Remove the sets in connection event lister (#324)
* Remove the sets in connection event lister
2021-11-15 09:42:11 +08:00
binbin.zhang 0070bd57be
update 2.x readme (#310) 2021-10-08 19:17:26 +08:00
tyltr a4db9f2ed6
optimize code (#301)
* optimize code
2021-09-11 12:00:48 +08:00
Yuzhe Yang 967abf1c9f
doc: refresh v2.0 doc (#279) 2021-07-21 23:14:16 +08:00
robotljw 859229d1f8
bugfix: redoRegisterEachService function to get servicename and groupname error (#277) 2021-07-07 22:56:09 +08:00
binbin.zhang 3466363959
fix ListenConfig func() cache not pointer (#271)
fix ListenConfig func() cache not pointer
2021-06-22 20:48:50 +08:00
Yu Zhao 84562372a3
fix bug 267 (#269)
* fix bug 267
2021-06-22 20:45:44 +08:00
binbin.zhang 9eef0329a7
update mod v2 (#265) 2021-06-10 23:30:32 +08:00
binbin.zhang fb490395ce
fix instance chooser (#254) 2021-05-31 09:14:44 +08:00
binbin.zhang 7a2d20f2bf
update naming&config ut (#253) 2021-05-30 20:38:14 +08:00
binbin.zhang a42d35ad2c
update 2.0.0 example (#248) 2021-05-25 17:29:25 +08:00
binbin.zhang b718836ea4
config support grpc (#242) 2021-05-23 22:03:03 +08:00
binbin.zhang 8cb8d63e7e
2.0.0 (#203)
* 2.0.0 naming support grpc
2021-04-29 21:54:40 +08:00
116 changed files with 1807 additions and 8011 deletions

View File

@ -1,21 +0,0 @@
---
name: Bug Report
about: Report a bug.
labels: 'Type: Bug'
---
### What version of nacos-sdk-go are you using?
### What version of nacos-sever are you using?
### What version of Go are you using (`go version`)?
### What operating system (Linux, Windows, …) and version?
### What did you do?
If possible, provide a recipe for reproducing the error.
### What did you expect to see?
### What did you see instead?

View File

@ -1,14 +0,0 @@
---
name: Feature Request
about: Suggest an idea for nacos-sdk-go
labels: 'Type: Feature'
---
### Use case(s) - what problem will this feature solve?
### Proposed Solution
### Alternatives Considered
### Additional Context

View File

@ -1,7 +0,0 @@
---
name: Question
about: Ask a question about nacos-sdk-go
labels: 'Type: Question'
---

View File

@ -14,7 +14,8 @@ jobs:
strategy: strategy:
matrix: matrix:
config: config:
- go_version: 1.21 - go_version: 1.13
- go_version: 1.14
steps: steps:
- name: Set up Go 1.x - name: Set up Go 1.x
@ -26,11 +27,14 @@ jobs:
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Install goimports
run: go get golang.org/x/tools/cmd/goimports
- name: Test - name: Test
run: | run: |
diff -u <(echo -n) <(gofmt -d -s .) diff -u <(echo -n) <(gofmt -d -s .)
diff -u <(echo -n) <(goimports -d .) diff -u <(echo -n) <(goimports -d .)
go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic go test -v ./... -coverprofile=coverage.txt -covermode=atomic
- name: Coverage - name: Coverage
run: bash <(curl -s https://codecov.io/bash) run: bash <(curl -s https://codecov.io/bash)

394
README.md
View File

@ -10,7 +10,7 @@ Nacos-sdk-go for Go client allows you to access Nacos service,it supports servic
## Requirements ## Requirements
Supported Go version over 1.15 Supported Go version over 1.14
Supported Nacos version over 2.x Supported Nacos version over 2.x
@ -27,42 +27,37 @@ $ go get -u github.com/nacos-group/nacos-sdk-go/v2
* ClientConfig * ClientConfig
```go ```go
constant.ClientConfig{
constant.ClientConfig { TimeoutMs uint64 // timeout for requesting Nacos server, default value is 10000ms
TimeoutMs uint64 // timeout for requesting Nacos server, default value is 10000ms NamespaceId string // the namespaceId of Nacos
NamespaceId string // the namespaceId of Nacos Endpoint string // the endpoint for ACM. https://help.aliyun.com/document_detail/130146.html
Endpoint string // the endpoint for ACM. https://help.aliyun.com/document_detail/130146.html RegionId string // the regionId for ACM & KMS
RegionId string // the regionId for ACM & KMS AccessKey string // the AccessKey for ACM & KMS
AccessKey string // the AccessKey for ACM & KMS SecretKey string // the SecretKey for ACM & KMS
SecretKey string // the SecretKey for ACM & KMS OpenKMS bool // it's to open KMS, default is false. https://help.aliyun.com/product/28933.html
OpenKMS bool // it's to open KMS, default is false. https://help.aliyun.com/product/28933.html // , to enable encrypt/decrypt, DataId should be start with "cipher-"
// , to enable encrypt/decrypt, DataId should be start with "cipher-" CacheDir string // the directory for persist nacos service info,default value is current path
CacheDir string // the directory for persist nacos service info,default value is current path UpdateThreadNum int // the number of goroutine for update nacos service info,default value is 20
UpdateThreadNum int // the number of goroutine for update nacos service info,default value is 20 NotLoadCacheAtStart bool // not to load persistent nacos service info in CacheDir at start time
NotLoadCacheAtStart bool // not to load persistent nacos service info in CacheDir at start time UpdateCacheWhenEmpty bool // update cache when get empty service instance from server
UpdateCacheWhenEmpty bool // update cache when get empty service instance from server Username string // the username for nacos auth
Username string // the username for nacos auth Password string // the password for nacos auth
Password string // the password for nacos auth LogDir string // the directory for log, default is current path
LogDir string // the directory for log, default is current path RotateTime string // the rotate time for log, eg: 30m, 1h, 24h, default is 24h
RotateTime string // the rotate time for log, eg: 30m, 1h, 24h, default is 24h MaxAge int64 // the max age of a log file, default value is 3
MaxAge int64 // the max age of a log file, default value is 3 LogLevel string // the level of log, it's must be debug,info,warn,error, default value is info
LogLevel string // the level of log, it's must be debug,info,warn,error, default value is info
} }
``` ```
* ServerConfig * ServerConfig
```go ```go
constant.ServerConfig{ constant.ServerConfig{
Scheme string // the nacos server scheme,defaut=http,this is not required in 2.0 Scheme string // the nacos server scheme,defaut=http,this is not required in 2.0
ContextPath string // the nacos server contextpath,defaut=/nacos,this is not required in 2.0 ContextPath string // the nacos server contextpath,defaut=/nacos,this is not required in 2.0
IpAddr string // the nacos server address IpAddr string // the nacos server address
Port uint64 // nacos server port Port uint64 // nacos server port
GrpcPort uint64 // nacos server grpc port, default=server port + 1000, this is not required
} }
``` ```
<b>NoteWe can config multiple ServerConfig,the client will rotate request the servers</b> <b>NoteWe can config multiple ServerConfig,the client will rotate request the servers</b>
@ -70,83 +65,83 @@ constant.ServerConfig{
### Create client ### Create client
```go ```go
//create clientConfig
clientConfig := constant.ClientConfig{
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here.
TimeoutMs: 5000,
NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
LogLevel: "debug",
}
//Another way of create clientConfig
clientConfig := *constant.NewClientConfig(
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"), //When namespace is public, fill in the blank string here.
constant.WithTimeoutMs(5000),
constant.WithNotLoadCacheAtStart(true),
constant.WithLogDir("/tmp/nacos/log"),
constant.WithCacheDir("/tmp/nacos/cache"),
constant.WithLogLevel("debug"),
)
//create clientConfig // At least one ServerConfig
clientConfig := constant.ClientConfig{ serverConfigs := []constant.ServerConfig{
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. {
TimeoutMs: 5000, IpAddr: "console1.nacos.io",
NotLoadCacheAtStart: true, ContextPath: "/nacos",
LogDir: "/tmp/nacos/log", Port: 80,
CacheDir: "/tmp/nacos/cache", Scheme: "http",
LogLevel: "debug", },
} {
//Another way of create clientConfig IpAddr: "console2.nacos.io",
clientConfig := *constant.NewClientConfig( ContextPath: "/nacos",
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"), //When namespace is public, fill in the blank string here. Port: 80,
constant.WithTimeoutMs(5000), Scheme: "http",
constant.WithNotLoadCacheAtStart(true), },
constant.WithLogDir("/tmp/nacos/log"), }
constant.WithCacheDir("/tmp/nacos/cache"), //Another way of create serverConfigs
constant.WithLogLevel("debug"), serverConfigs := []constant.ServerConfig{
) *constant.NewServerConfig(
// At least one ServerConfig "console1.nacos.io",
serverConfigs := []constant.ServerConfig{ 80,
{ constant.WithScheme("http"),
IpAddr: "console1.nacos.io", constant.WithContextPath("/nacos")
ContextPath: "/nacos", ),
Port: 80, *constant.NewServerConfig(
Scheme: "http", "console2.nacos.io",
}, 80,
{ constant.WithScheme("http"),
IpAddr: "console2.nacos.io", constant.WithContextPath("/nacos")
ContextPath: "/nacos", ),
Port: 80, }
Scheme: "http",
},
}
//Another way of create serverConfigs
serverConfigs := []constant.ServerConfig{
*constant.NewServerConfig(
"console1.nacos.io",
80,
constant.WithScheme("http"),
constant.WithContextPath("/nacos")
),
*constant.NewServerConfig(
"console2.nacos.io",
80,
constant.WithScheme("http"),
constant.WithContextPath("/nacos")
),
}
// Create naming client for service discovery // Create naming client for service discovery
_, _ := clients.CreateNamingClient(map[string]interface{}{ _, _ := clients.CreateNamingClient(map[string]interface{}{
"serverConfigs": serverConfigs, "serverConfigs": serverConfigs,
"clientConfig": clientConfig, "clientConfig": clientConfig,
}) })
// Create config client for dynamic configuration // Create config client for dynamic configuration
_, _ := clients.CreateConfigClient(map[string]interface{}{ _, _ := clients.CreateConfigClient(map[string]interface{}{
"serverConfigs": serverConfigs, "serverConfigs": serverConfigs,
"clientConfig": clientConfig, "clientConfig": clientConfig,
}) })
// Another way of create naming client for service discovery (recommend) // Another way of create naming client for service discovery (recommend)
namingClient, err := clients.NewNamingClient( namingClient, err := clients.NewNamingClient(
vo.NacosClientParam{ vo.NacosClientParam{
ClientConfig: &clientConfig, ClientConfig: &clientConfig,
ServerConfigs: serverConfigs, ServerConfigs: serverConfigs,
}, },
) )
// Another way of create config client for dynamic configuration (recommend) // Another way of create config client for dynamic configuration (recommend)
configClient, err := clients.NewConfigClient( configClient, err := clients.NewConfigClient(
vo.NacosClientParam{ vo.NacosClientParam{
ClientConfig: &clientConfig, ClientConfig: &clientConfig,
ServerConfigs: serverConfigs, ServerConfigs: serverConfigs,
}, },
) )
``` ```
@ -155,25 +150,23 @@ constant.ServerConfig{
https://help.aliyun.com/document_detail/130146.html https://help.aliyun.com/document_detail/130146.html
```go ```go
cc := constant.ClientConfig{ cc := constant.ClientConfig{
Endpoint: "acm.aliyun.com:8080", Endpoint: "acm.aliyun.com:8080",
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468",
RegionId: "cn-shanghai", RegionId: "cn-shanghai",
AccessKey: "LTAI4G8KxxxxxxxxxxxxxbwZLBr", AccessKey: "LTAI4G8KxxxxxxxxxxxxxbwZLBr",
SecretKey: "n5jTL9YxxxxxxxxxxxxaxmPLZV9", SecretKey: "n5jTL9YxxxxxxxxxxxxaxmPLZV9",
OpenKMS: true, OpenKMS: true,
TimeoutMs: 5000, TimeoutMs: 5000,
LogLevel: "debug", LogLevel: "debug",
} }
// a more graceful way to create config client
client, err := clients.NewConfigClient(
vo.NacosClientParam{
ClientConfig: &cc,
},
)
// a more graceful way to create config client
client, err := clients.NewConfigClient(
vo.NacosClientParam{
ClientConfig: &cc,
},
)
``` ```
### Service Discovery ### Service Discovery
@ -183,17 +176,17 @@ cc := constant.ClientConfig{
```go ```go
success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{ success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{
Ip: "10.0.0.11", Ip: "10.0.0.11",
Port: 8848, Port: 8848,
ServiceName: "demo.go", ServiceName: "demo.go",
Weight: 10, Weight: 10,
Enable: true, Enable: true,
Healthy: true, Healthy: true,
Ephemeral: true, Ephemeral: true,
Metadata: map[string]string{"idc":"shanghai"}, Metadata: map[string]string{"idc":"shanghai"},
ClusterName: "cluster-a", // default value is DEFAULT ClusterName: "cluster-a", // default value is DEFAULT
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
}) })
``` ```
@ -202,13 +195,13 @@ success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{
```go ```go
success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{ success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{
Ip: "10.0.0.11", Ip: "10.0.0.11",
Port: 8848, Port: 8848,
ServiceName: "demo.go", ServiceName: "demo.go",
Ephemeral: true, Ephemeral: true,
Cluster: "cluster-a", // default value is DEFAULT Cluster: "cluster-a", // default value is DEFAULT
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
}) })
``` ```
@ -217,37 +210,35 @@ success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{
```go ```go
services, err := namingClient.GetService(vo.GetServiceParam{ services, err := namingClient.GetService(vo.GetServiceParam{
ServiceName: "demo.go", ServiceName: "demo.go",
Clusters: []string{"cluster-a"}, // default value is DEFAULT Clusters: []string{"cluster-a"}, // default value is DEFAULT
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
}) })
``` ```
* Get all instancesSelectAllInstances * Get all instancesSelectAllInstances
```go ```go
// SelectAllInstance return all instances,include healthy=false,enable=false,weight<=0 // SelectAllInstance return all instances,include healthy=false,enable=false,weight<=0
instances, err := namingClient.SelectAllInstances(vo.SelectAllInstancesParam{ instances, err := namingClient.SelectAllInstances(vo.SelectAllInstancesParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
Clusters: []string{"cluster-a"}, // default value is DEFAULT Clusters: []string{"cluster-a"}, // default value is DEFAULT
}) })
``` ```
* Get instances SelectInstances * Get instances SelectInstances
```go ```go
// SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0 // SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0
instances, err := namingClient.SelectInstances(vo.SelectInstancesParam{ instances, err := namingClient.SelectInstances(vo.SelectInstancesParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
Clusters: []string{"cluster-a"}, // default value is DEFAULT Clusters: []string{"cluster-a"}, // default value is DEFAULT
HealthyOnly: true, HealthyOnly: true,
}) })
``` ```
@ -255,12 +246,12 @@ services, err := namingClient.GetService(vo.GetServiceParam{
```go ```go
// SelectOneHealthyInstance return one instance by WRR strategy for load balance // SelectOneHealthyInstance return one instance by WRR strategy for load balance
// And the instance should be health=true,enable=true and weight>0 // And the instance should be health=true,enable=true and weight>0
instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{ instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
Clusters: []string{"cluster-a"}, // default value is DEFAULT Clusters: []string{"cluster-a"}, // default value is DEFAULT
}) })
``` ```
@ -269,15 +260,15 @@ services, err := namingClient.GetService(vo.GetServiceParam{
```go ```go
// Subscribe key = serviceName+groupName+cluster // Subscribe key = serviceName+groupName+cluster
// Note: We call add multiple SubscribeCallback with the same key. // Note: We call add multiple SubscribeCallback with the same key.
err := namingClient.Subscribe(vo.SubscribeParam{ err := namingClient.Subscribe(vo.SubscribeParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
Clusters: []string{"cluster-a"}, // default value is DEFAULT Clusters: []string{"cluster-a"}, // default value is DEFAULT
SubscribeCallback: func (services []model.Instance, err error) { SubscribeCallback: func (services []model.Instance, err error) {
log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services)) log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services))
}, },
}) })
``` ```
@ -286,13 +277,13 @@ services, err := namingClient.GetService(vo.GetServiceParam{
```go ```go
err := namingClient.Unsubscribe(vo.SubscribeParam{ err := namingClient.Unsubscribe(vo.SubscribeParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a", // default value is DEFAULT_GROUP GroupName: "group-a", // default value is DEFAULT_GROUP
Clusters: []string{"cluster-a"}, // default value is DEFAULT Clusters: []string{"cluster-a"}, // default value is DEFAULT
SubscribeCallback: func (services []model.Instance, err error) { SubscribeCallback: func (services []model.Instance, err error) {
log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services)) log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services))
}, },
}) })
``` ```
@ -301,10 +292,10 @@ err := namingClient.Unsubscribe(vo.SubscribeParam{
```go ```go
serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{ serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f", NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f",
PageNo: 1, PageNo: 1,
PageSize: 10, PageSize: 10,
}), }),
``` ```
@ -315,9 +306,9 @@ serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
```go ```go
success, err := configClient.PublishConfig(vo.ConfigParam{ success, err := configClient.PublishConfig(vo.ConfigParam{
DataId: "dataId", DataId: "dataId",
Group: "group", Group: "group",
Content: "hello world!222222"}) Content: "hello world!222222"})
``` ```
@ -326,8 +317,8 @@ success, err := configClient.PublishConfig(vo.ConfigParam{
```go ```go
success, err = configClient.DeleteConfig(vo.ConfigParam{ success, err = configClient.DeleteConfig(vo.ConfigParam{
DataId: "dataId", DataId: "dataId",
Group: "group"}) Group: "group"})
``` ```
@ -336,9 +327,8 @@ success, err = configClient.DeleteConfig(vo.ConfigParam{
```go ```go
content, err := configClient.GetConfig(vo.ConfigParam{ content, err := configClient.GetConfig(vo.ConfigParam{
DataId: "dataId", DataId: "dataId",
Group: "group"}) Group: "group"})
``` ```
@ -347,12 +337,12 @@ content, err := configClient.GetConfig(vo.ConfigParam{
```go ```go
err := configClient.ListenConfig(vo.ConfigParam{ err := configClient.ListenConfig(vo.ConfigParam{
DataId: "dataId", DataId: "dataId",
Group: "group", Group: "group",
OnChange: func (namespace, group, dataId, data string) { OnChange: func (namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data) fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
}, },
}) })
``` ```
@ -361,9 +351,9 @@ err := configClient.ListenConfig(vo.ConfigParam{
```go ```go
err := configClient.CancelListenConfig(vo.ConfigParam{ err := configClient.CancelListenConfig(vo.ConfigParam{
DataId: "dataId", DataId: "dataId",
Group: "group", Group: "group",
}) })
``` ```
@ -371,12 +361,12 @@ err := configClient.CancelListenConfig(vo.ConfigParam{
```go ```go
configPage, err := configClient.SearchConfig(vo.SearchConfigParam{ configPage, err := configClient.SearchConfig(vo.SearchConfigParam{
Search: "blur", Search: "blur",
DataId: "", DataId: "",
Group: "", Group: "",
PageNo: 1, PageNo: 1,
PageSize: 10, PageSize: 10,
}) })
``` ```
## Example ## Example

View File

@ -9,7 +9,7 @@
Nacos-sdk-go是Nacos的Go语言客户端它实现了服务发现和动态配置的功能 Nacos-sdk-go是Nacos的Go语言客户端它实现了服务发现和动态配置的功能
## 使用限制 ## 使用限制
支持Go>=v1.15版本 支持Go>v1.14版本
支持Nacos>2.x版本 支持Nacos>2.x版本
@ -52,7 +52,6 @@ constant.ServerConfig{
IpAddr string // Nacos的服务地址 IpAddr string // Nacos的服务地址
Port uint64 // Nacos的服务端口 Port uint64 // Nacos的服务端口
Scheme string // Nacos的服务地址前缀默认http在2.0中不需要设置 Scheme string // Nacos的服务地址前缀默认http在2.0中不需要设置
GrpcPort uint64 // Nacos的 grpc 服务端口, 默认为 服务端口+1000, 不是必填
} }
``` ```
@ -63,7 +62,7 @@ constant.ServerConfig{
```go ```go
// 创建clientConfig // 创建clientConfig
clientConfig := constant.ClientConfig{ clientConfig := constant.ClientConfig{
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", // 如果需要支持多namespace我们可以创建多个client,它们有不同的NamespaceId。当namespace是public时此处填空字符串。 NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", // 如果需要支持多namespace我们可以场景多个client,它们有不同的NamespaceId。当namespace是public时此处填空字符串。
TimeoutMs: 5000, TimeoutMs: 5000,
NotLoadCacheAtStart: true, NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log", LogDir: "/tmp/nacos/log",
@ -103,13 +102,13 @@ serverConfigs := []constant.ServerConfig{
"console1.nacos.io", "console1.nacos.io",
80, 80,
constant.WithScheme("http"), constant.WithScheme("http"),
constant.WithContextPath("/nacos"), constant.WithContextPath("/nacos")
), ),
*constant.NewServerConfig( *constant.NewServerConfig(
"console2.nacos.io", "console2.nacos.io",
80, 80,
constant.WithScheme("http"), constant.WithScheme("http"),
constant.WithContextPath("/nacos"), constant.WithContextPath("/nacos")
), ),
} }

View File

@ -1,233 +1,467 @@
// /*
// Copyright 1999-2020 Alibaba Group Holding Ltd. * Copyright 1999-2020 Alibaba Group Holding Ltd.
// *
// Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
// You may obtain a copy of the License at * You may obtain a copy of the License at
// *
// http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
// *
// Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
// limitations under the License. * limitations under the License.
*/
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // source: nacos_grpc_service.proto
// protoc-gen-go v1.36.7
// protoc v5.29.3
// source: api/proto/nacos_grpc_service.proto
package auto package grpc
import ( import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect" context "context"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" fmt "fmt"
anypb "google.golang.org/protobuf/types/known/anypb" math "math"
reflect "reflect"
sync "sync" proto "github.com/golang/protobuf/proto"
unsafe "unsafe" any "github.com/golang/protobuf/ptypes/any"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
) )
const ( // Reference imports to suppress errors if they are not otherwise used.
// Verify that this generated code is sufficiently up-to-date. var _ = proto.Marshal
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) var _ = fmt.Errorf
// Verify that runtime/protoimpl is sufficiently up-to-date. var _ = math.Inf
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) // This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Metadata struct { type Metadata struct {
state protoimpl.MessageState `protogen:"open.v1"`
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
ClientIp string `protobuf:"bytes,8,opt,name=clientIp,proto3" json:"clientIp,omitempty"` ClientIp string `protobuf:"bytes,8,opt,name=clientIp,proto3" json:"clientIp,omitempty"`
Headers map[string]string `protobuf:"bytes,7,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Headers map[string]string `protobuf:"bytes,7,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
unknownFields protoimpl.UnknownFields XXX_NoUnkeyedLiteral struct{} `json:"-"`
sizeCache protoimpl.SizeCache XXX_unrecognized []byte `json:"-"`
} XXX_sizecache int32 `json:"-"`
func (x *Metadata) Reset() {
*x = Metadata{}
mi := &file_api_proto_nacos_grpc_service_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Metadata) String() string {
return protoimpl.X.MessageStringOf(x)
} }
func (m *Metadata) Reset() { *m = Metadata{} }
func (m *Metadata) String() string { return proto.CompactTextString(m) }
func (*Metadata) ProtoMessage() {} func (*Metadata) ProtoMessage() {}
func (x *Metadata) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_nacos_grpc_service_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
func (*Metadata) Descriptor() ([]byte, []int) { func (*Metadata) Descriptor() ([]byte, []int) {
return file_api_proto_nacos_grpc_service_proto_rawDescGZIP(), []int{0} return fileDescriptor_f908b146bdb05ce9, []int{0}
} }
func (x *Metadata) GetType() string { func (m *Metadata) XXX_Unmarshal(b []byte) error {
if x != nil { return xxx_messageInfo_Metadata.Unmarshal(m, b)
return x.Type }
func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Metadata.Marshal(b, m, deterministic)
}
func (m *Metadata) XXX_Merge(src proto.Message) {
xxx_messageInfo_Metadata.Merge(m, src)
}
func (m *Metadata) XXX_Size() int {
return xxx_messageInfo_Metadata.Size(m)
}
func (m *Metadata) XXX_DiscardUnknown() {
xxx_messageInfo_Metadata.DiscardUnknown(m)
}
var xxx_messageInfo_Metadata proto.InternalMessageInfo
func (m *Metadata) GetType() string {
if m != nil {
return m.Type
} }
return "" return ""
} }
func (x *Metadata) GetClientIp() string { func (m *Metadata) GetClientIp() string {
if x != nil { if m != nil {
return x.ClientIp return m.ClientIp
} }
return "" return ""
} }
func (x *Metadata) GetHeaders() map[string]string { func (m *Metadata) GetHeaders() map[string]string {
if x != nil { if m != nil {
return x.Headers return m.Headers
} }
return nil return nil
} }
type Payload struct { type Payload struct {
state protoimpl.MessageState `protogen:"open.v1"`
Metadata *Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` Metadata *Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"`
Body *anypb.Any `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` Body *any.Any `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
unknownFields protoimpl.UnknownFields XXX_NoUnkeyedLiteral struct{} `json:"-"`
sizeCache protoimpl.SizeCache XXX_unrecognized []byte `json:"-"`
} XXX_sizecache int32 `json:"-"`
func (x *Payload) Reset() {
*x = Payload{}
mi := &file_api_proto_nacos_grpc_service_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Payload) String() string {
return protoimpl.X.MessageStringOf(x)
} }
func (m *Payload) Reset() { *m = Payload{} }
func (m *Payload) String() string { return proto.CompactTextString(m) }
func (*Payload) ProtoMessage() {} func (*Payload) ProtoMessage() {}
func (x *Payload) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_nacos_grpc_service_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Payload.ProtoReflect.Descriptor instead.
func (*Payload) Descriptor() ([]byte, []int) { func (*Payload) Descriptor() ([]byte, []int) {
return file_api_proto_nacos_grpc_service_proto_rawDescGZIP(), []int{1} return fileDescriptor_f908b146bdb05ce9, []int{1}
} }
func (x *Payload) GetMetadata() *Metadata { func (m *Payload) XXX_Unmarshal(b []byte) error {
if x != nil { return xxx_messageInfo_Payload.Unmarshal(m, b)
return x.Metadata }
func (m *Payload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Payload.Marshal(b, m, deterministic)
}
func (m *Payload) XXX_Merge(src proto.Message) {
xxx_messageInfo_Payload.Merge(m, src)
}
func (m *Payload) XXX_Size() int {
return xxx_messageInfo_Payload.Size(m)
}
func (m *Payload) XXX_DiscardUnknown() {
xxx_messageInfo_Payload.DiscardUnknown(m)
}
var xxx_messageInfo_Payload proto.InternalMessageInfo
func (m *Payload) GetMetadata() *Metadata {
if m != nil {
return m.Metadata
} }
return nil return nil
} }
func (x *Payload) GetBody() *anypb.Any { func (m *Payload) GetBody() *any.Any {
if x != nil { if m != nil {
return x.Body return m.Body
} }
return nil return nil
} }
var File_api_proto_nacos_grpc_service_proto protoreflect.FileDescriptor func init() {
proto.RegisterType((*Metadata)(nil), "Metadata")
const file_api_proto_nacos_grpc_service_proto_rawDesc = "" + proto.RegisterMapType((map[string]string)(nil), "Metadata.HeadersEntry")
"\n" + proto.RegisterType((*Payload)(nil), "Payload")
"\"api/proto/nacos_grpc_service.proto\x1a\x19google/protobuf/any.proto\"\xa8\x01\n" +
"\bMetadata\x12\x12\n" +
"\x04type\x18\x03 \x01(\tR\x04type\x12\x1a\n" +
"\bclientIp\x18\b \x01(\tR\bclientIp\x120\n" +
"\aheaders\x18\a \x03(\v2\x16.Metadata.HeadersEntryR\aheaders\x1a:\n" +
"\fHeadersEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"Z\n" +
"\aPayload\x12%\n" +
"\bmetadata\x18\x02 \x01(\v2\t.MetadataR\bmetadata\x12(\n" +
"\x04body\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\x04body28\n" +
"\rRequestStream\x12'\n" +
"\rrequestStream\x12\b.Payload\x1a\b.Payload\"\x000\x012*\n" +
"\aRequest\x12\x1f\n" +
"\arequest\x12\b.Payload\x1a\b.Payload\"\x002>\n" +
"\x0fBiRequestStream\x12+\n" +
"\x0frequestBiStream\x12\b.Payload\x1a\b.Payload\"\x00(\x010\x01B^\n" +
"\x1fcom.alibaba.nacos.api.grpc.autoP\x01Z9github.com/nacos-group/nacos-sdk-go/v2/api/grpc/auto;autob\x06proto3"
var (
file_api_proto_nacos_grpc_service_proto_rawDescOnce sync.Once
file_api_proto_nacos_grpc_service_proto_rawDescData []byte
)
func file_api_proto_nacos_grpc_service_proto_rawDescGZIP() []byte {
file_api_proto_nacos_grpc_service_proto_rawDescOnce.Do(func() {
file_api_proto_nacos_grpc_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_proto_nacos_grpc_service_proto_rawDesc), len(file_api_proto_nacos_grpc_service_proto_rawDesc)))
})
return file_api_proto_nacos_grpc_service_proto_rawDescData
} }
var file_api_proto_nacos_grpc_service_proto_msgTypes = make([]protoimpl.MessageInfo, 3) func init() { proto.RegisterFile("nacos_grpc_service.proto", fileDescriptor_f908b146bdb05ce9) }
var file_api_proto_nacos_grpc_service_proto_goTypes = []any{
(*Metadata)(nil), // 0: Metadata var fileDescriptor_f908b146bdb05ce9 = []byte{
(*Payload)(nil), // 1: Payload // 333 bytes of a gzipped FileDescriptorProto
nil, // 2: Metadata.HeadersEntry 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4f, 0x4b, 0xeb, 0x40,
(*anypb.Any)(nil), // 3: google.protobuf.Any 0x10, 0x7f, 0xdb, 0xf6, 0xbd, 0xa4, 0xd3, 0x57, 0x2a, 0x4b, 0x91, 0x98, 0x4b, 0x4b, 0x45, 0x0c,
} 0x0a, 0xdb, 0x12, 0x2f, 0xa5, 0x07, 0xc1, 0x82, 0xa0, 0x07, 0xa1, 0xc4, 0x9b, 0x97, 0x32, 0x49,
var file_api_proto_nacos_grpc_service_proto_depIdxs = []int32{ 0xd6, 0x1a, 0x4c, 0xb3, 0x71, 0xb3, 0x29, 0xec, 0x37, 0xf2, 0x63, 0x4a, 0x37, 0x69, 0xb0, 0x20,
2, // 0: Metadata.headers:type_name -> Metadata.HeadersEntry 0xde, 0x66, 0x7e, 0x7f, 0xe6, 0xc7, 0xcc, 0x80, 0x93, 0x61, 0x24, 0x8a, 0xf5, 0x46, 0xe6, 0xd1,
0, // 1: Payload.metadata:type_name -> Metadata 0xba, 0xe0, 0x72, 0x97, 0x44, 0x9c, 0xe5, 0x52, 0x28, 0xe1, 0x9e, 0x6d, 0x84, 0xd8, 0xa4, 0x7c,
3, // 2: Payload.body:type_name -> google.protobuf.Any 0x6a, 0xba, 0xb0, 0x7c, 0x9d, 0x62, 0xa6, 0x2b, 0x6a, 0xf2, 0x49, 0xc0, 0x7e, 0xe2, 0x0a, 0x63,
1, // 3: RequestStream.requestStream:input_type -> Payload 0x54, 0x48, 0x29, 0x74, 0x94, 0xce, 0xb9, 0xd3, 0x1e, 0x13, 0xaf, 0x1b, 0x98, 0x9a, 0xba, 0x60,
1, // 4: Request.request:input_type -> Payload 0x47, 0x69, 0xc2, 0x33, 0xf5, 0x98, 0x3b, 0xb6, 0xc1, 0x9b, 0x9e, 0xce, 0xc0, 0x7a, 0xe3, 0x18,
1, // 5: BiRequestStream.requestBiStream:input_type -> Payload 0x73, 0x59, 0x38, 0xd6, 0xb8, 0xed, 0xf5, 0xfc, 0x53, 0x76, 0x98, 0xc5, 0x1e, 0x2a, 0xe2, 0x3e,
1, // 6: RequestStream.requestStream:output_type -> Payload 0x53, 0x52, 0x07, 0x07, 0x99, 0xbb, 0x80, 0xff, 0xdf, 0x09, 0x7a, 0x02, 0xed, 0x77, 0xae, 0x1d,
1, // 7: Request.request:output_type -> Payload 0x62, 0x06, 0xef, 0x4b, 0x3a, 0x84, 0xbf, 0x3b, 0x4c, 0x4b, 0xee, 0xb4, 0x0c, 0x56, 0x35, 0x8b,
1, // 8: BiRequestStream.requestBiStream:output_type -> Payload 0xd6, 0x9c, 0x4c, 0x5e, 0xc0, 0x5a, 0xa1, 0x4e, 0x05, 0xc6, 0xf4, 0x02, 0xec, 0x6d, 0x1d, 0x64,
6, // [6:9] is the sub-list for method output_type 0x74, 0x3d, 0xbf, 0xdb, 0x24, 0x07, 0x0d, 0x45, 0x3d, 0xe8, 0x84, 0x22, 0xd6, 0x66, 0x9f, 0x9e,
3, // [3:6] is the sub-list for method input_type 0x3f, 0x64, 0xd5, 0x19, 0xd8, 0xe1, 0x0c, 0xec, 0x2e, 0xd3, 0x81, 0x51, 0xf8, 0x73, 0xe8, 0x07,
3, // [3:3] is the sub-list for extension type_name 0xfc, 0xa3, 0xe4, 0x85, 0x7a, 0x56, 0x92, 0xe3, 0x96, 0x5e, 0x42, 0x5f, 0x1e, 0x01, 0x36, 0xab,
3, // [3:3] is the sub-list for extension extendee 0xc3, 0xdd, 0xa6, 0x9a, 0xfc, 0x99, 0x11, 0xff, 0x0a, 0xac, 0xda, 0x49, 0x47, 0x60, 0xd5, 0x9e,
0, // [0:3] is the sub-list for field type_name 0x9f, 0xd5, 0xfe, 0x2d, 0x0c, 0x96, 0xc9, 0x71, 0xce, 0x35, 0x0c, 0x6a, 0xcf, 0x32, 0xf9, 0x2d,
0xc9, 0x23, 0x33, 0xb2, 0x3c, 0x87, 0x51, 0x24, 0xb6, 0x0c, 0xd3, 0x24, 0xc4, 0x10, 0x99, 0xf9,
0x37, 0xc3, 0x3c, 0x61, 0xfb, 0x9f, 0x33, 0x2c, 0x95, 0x58, 0x91, 0xf0, 0x9f, 0x59, 0xef, 0xe6,
0x2b, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x9e, 0xc7, 0x2d, 0x0f, 0x02, 0x00, 0x00,
} }
func init() { file_api_proto_nacos_grpc_service_proto_init() } // Reference imports to suppress errors if they are not otherwise used.
func file_api_proto_nacos_grpc_service_proto_init() { var _ context.Context
if File_api_proto_nacos_grpc_service_proto != nil { var _ grpc.ClientConn
return
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// RequestStreamClient is the client API for RequestStream service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RequestStreamClient interface {
// build a streamRequest
RequestStream(ctx context.Context, in *Payload, opts ...grpc.CallOption) (RequestStream_RequestStreamClient, error)
}
type requestStreamClient struct {
cc *grpc.ClientConn
}
func NewRequestStreamClient(cc *grpc.ClientConn) RequestStreamClient {
return &requestStreamClient{cc}
}
func (c *requestStreamClient) RequestStream(ctx context.Context, in *Payload, opts ...grpc.CallOption) (RequestStream_RequestStreamClient, error) {
stream, err := c.cc.NewStream(ctx, &_RequestStream_serviceDesc.Streams[0], "/RequestStream/requestStream", opts...)
if err != nil {
return nil, err
} }
type x struct{} x := &requestStreamRequestStreamClient{stream}
out := protoimpl.TypeBuilder{ if err := x.ClientStream.SendMsg(in); err != nil {
File: protoimpl.DescBuilder{ return nil, err
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), }
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_nacos_grpc_service_proto_rawDesc), len(file_api_proto_nacos_grpc_service_proto_rawDesc)), if err := x.ClientStream.CloseSend(); err != nil {
NumEnums: 0, return nil, err
NumMessages: 3, }
NumExtensions: 0, return x, nil
NumServices: 3, }
type RequestStream_RequestStreamClient interface {
Recv() (*Payload, error)
grpc.ClientStream
}
type requestStreamRequestStreamClient struct {
grpc.ClientStream
}
func (x *requestStreamRequestStreamClient) Recv() (*Payload, error) {
m := new(Payload)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// RequestStreamServer is the server API for RequestStream service.
type RequestStreamServer interface {
// build a streamRequest
RequestStream(*Payload, RequestStream_RequestStreamServer) error
}
// UnimplementedRequestStreamServer can be embedded to have forward compatible implementations.
type UnimplementedRequestStreamServer struct {
}
func (*UnimplementedRequestStreamServer) RequestStream(req *Payload, srv RequestStream_RequestStreamServer) error {
return status.Errorf(codes.Unimplemented, "method RequestStream not implemented")
}
func RegisterRequestStreamServer(s *grpc.Server, srv RequestStreamServer) {
s.RegisterService(&_RequestStream_serviceDesc, srv)
}
func _RequestStream_RequestStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Payload)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RequestStreamServer).RequestStream(m, &requestStreamRequestStreamServer{stream})
}
type RequestStream_RequestStreamServer interface {
Send(*Payload) error
grpc.ServerStream
}
type requestStreamRequestStreamServer struct {
grpc.ServerStream
}
func (x *requestStreamRequestStreamServer) Send(m *Payload) error {
return x.ServerStream.SendMsg(m)
}
var _RequestStream_serviceDesc = grpc.ServiceDesc{
ServiceName: "RequestStream",
HandlerType: (*RequestStreamServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "requestStream",
Handler: _RequestStream_RequestStream_Handler,
ServerStreams: true,
}, },
GoTypes: file_api_proto_nacos_grpc_service_proto_goTypes, },
DependencyIndexes: file_api_proto_nacos_grpc_service_proto_depIdxs, Metadata: "nacos_grpc_service.proto",
MessageInfos: file_api_proto_nacos_grpc_service_proto_msgTypes, }
}.Build()
File_api_proto_nacos_grpc_service_proto = out.File // RequestClient is the client API for Request service.
file_api_proto_nacos_grpc_service_proto_goTypes = nil //
file_api_proto_nacos_grpc_service_proto_depIdxs = nil // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RequestClient interface {
// Sends a commonRequest
Request(ctx context.Context, in *Payload, opts ...grpc.CallOption) (*Payload, error)
}
type requestClient struct {
cc *grpc.ClientConn
}
func NewRequestClient(cc *grpc.ClientConn) RequestClient {
return &requestClient{cc}
}
func (c *requestClient) Request(ctx context.Context, in *Payload, opts ...grpc.CallOption) (*Payload, error) {
out := new(Payload)
err := c.cc.Invoke(ctx, "/Request/request", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RequestServer is the server API for Request service.
type RequestServer interface {
// Sends a commonRequest
Request(context.Context, *Payload) (*Payload, error)
}
// UnimplementedRequestServer can be embedded to have forward compatible implementations.
type UnimplementedRequestServer struct {
}
func (*UnimplementedRequestServer) Request(ctx context.Context, req *Payload) (*Payload, error) {
return nil, status.Errorf(codes.Unimplemented, "method Request not implemented")
}
func RegisterRequestServer(s *grpc.Server, srv RequestServer) {
s.RegisterService(&_Request_serviceDesc, srv)
}
func _Request_Request_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Payload)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RequestServer).Request(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Request/Request",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RequestServer).Request(ctx, req.(*Payload))
}
return interceptor(ctx, in, info, handler)
}
var _Request_serviceDesc = grpc.ServiceDesc{
ServiceName: "Request",
HandlerType: (*RequestServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "request",
Handler: _Request_Request_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "nacos_grpc_service.proto",
}
// BiRequestStreamClient is the client API for BiRequestStream service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type BiRequestStreamClient interface {
// Sends a commonRequest
RequestBiStream(ctx context.Context, opts ...grpc.CallOption) (BiRequestStream_RequestBiStreamClient, error)
}
type biRequestStreamClient struct {
cc *grpc.ClientConn
}
func NewBiRequestStreamClient(cc *grpc.ClientConn) BiRequestStreamClient {
return &biRequestStreamClient{cc}
}
func (c *biRequestStreamClient) RequestBiStream(ctx context.Context, opts ...grpc.CallOption) (BiRequestStream_RequestBiStreamClient, error) {
stream, err := c.cc.NewStream(ctx, &_BiRequestStream_serviceDesc.Streams[0], "/BiRequestStream/requestBiStream", opts...)
if err != nil {
return nil, err
}
x := &biRequestStreamRequestBiStreamClient{stream}
return x, nil
}
type BiRequestStream_RequestBiStreamClient interface {
Send(*Payload) error
Recv() (*Payload, error)
grpc.ClientStream
}
type biRequestStreamRequestBiStreamClient struct {
grpc.ClientStream
}
func (x *biRequestStreamRequestBiStreamClient) Send(m *Payload) error {
return x.ClientStream.SendMsg(m)
}
func (x *biRequestStreamRequestBiStreamClient) Recv() (*Payload, error) {
m := new(Payload)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// BiRequestStreamServer is the server API for BiRequestStream service.
type BiRequestStreamServer interface {
// Sends a commonRequest
RequestBiStream(BiRequestStream_RequestBiStreamServer) error
}
// UnimplementedBiRequestStreamServer can be embedded to have forward compatible implementations.
type UnimplementedBiRequestStreamServer struct {
}
func (*UnimplementedBiRequestStreamServer) RequestBiStream(srv BiRequestStream_RequestBiStreamServer) error {
return status.Errorf(codes.Unimplemented, "method RequestBiStream not implemented")
}
func RegisterBiRequestStreamServer(s *grpc.Server, srv BiRequestStreamServer) {
s.RegisterService(&_BiRequestStream_serviceDesc, srv)
}
func _BiRequestStream_RequestBiStream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(BiRequestStreamServer).RequestBiStream(&biRequestStreamRequestBiStreamServer{stream})
}
type BiRequestStream_RequestBiStreamServer interface {
Send(*Payload) error
Recv() (*Payload, error)
grpc.ServerStream
}
type biRequestStreamRequestBiStreamServer struct {
grpc.ServerStream
}
func (x *biRequestStreamRequestBiStreamServer) Send(m *Payload) error {
return x.ServerStream.SendMsg(m)
}
func (x *biRequestStreamRequestBiStreamServer) Recv() (*Payload, error) {
m := new(Payload)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _BiRequestStream_serviceDesc = grpc.ServiceDesc{
ServiceName: "BiRequestStream",
HandlerType: (*BiRequestStreamServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "requestBiStream",
Handler: _BiRequestStream_RequestBiStream_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "nacos_grpc_service.proto",
} }

View File

@ -1,343 +0,0 @@
//
// Copyright 1999-2020 Alibaba Group Holding Ltd.
//
// Licensed 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.
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: api/proto/nacos_grpc_service.proto
package auto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
RequestStream_RequestStream_FullMethodName = "/RequestStream/requestStream"
)
// RequestStreamClient is the client API for RequestStream service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type RequestStreamClient interface {
// build a streamRequest
RequestStream(ctx context.Context, in *Payload, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Payload], error)
}
type requestStreamClient struct {
cc grpc.ClientConnInterface
}
func NewRequestStreamClient(cc grpc.ClientConnInterface) RequestStreamClient {
return &requestStreamClient{cc}
}
func (c *requestStreamClient) RequestStream(ctx context.Context, in *Payload, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Payload], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &RequestStream_ServiceDesc.Streams[0], RequestStream_RequestStream_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[Payload, Payload]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type RequestStream_RequestStreamClient = grpc.ServerStreamingClient[Payload]
// RequestStreamServer is the server API for RequestStream service.
// All implementations must embed UnimplementedRequestStreamServer
// for forward compatibility.
type RequestStreamServer interface {
// build a streamRequest
RequestStream(*Payload, grpc.ServerStreamingServer[Payload]) error
mustEmbedUnimplementedRequestStreamServer()
}
// UnimplementedRequestStreamServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedRequestStreamServer struct{}
func (UnimplementedRequestStreamServer) RequestStream(*Payload, grpc.ServerStreamingServer[Payload]) error {
return status.Errorf(codes.Unimplemented, "method RequestStream not implemented")
}
func (UnimplementedRequestStreamServer) mustEmbedUnimplementedRequestStreamServer() {}
func (UnimplementedRequestStreamServer) testEmbeddedByValue() {}
// UnsafeRequestStreamServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to RequestStreamServer will
// result in compilation errors.
type UnsafeRequestStreamServer interface {
mustEmbedUnimplementedRequestStreamServer()
}
func RegisterRequestStreamServer(s grpc.ServiceRegistrar, srv RequestStreamServer) {
// If the following call pancis, it indicates UnimplementedRequestStreamServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&RequestStream_ServiceDesc, srv)
}
func _RequestStream_RequestStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Payload)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RequestStreamServer).RequestStream(m, &grpc.GenericServerStream[Payload, Payload]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type RequestStream_RequestStreamServer = grpc.ServerStreamingServer[Payload]
// RequestStream_ServiceDesc is the grpc.ServiceDesc for RequestStream service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var RequestStream_ServiceDesc = grpc.ServiceDesc{
ServiceName: "RequestStream",
HandlerType: (*RequestStreamServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "requestStream",
Handler: _RequestStream_RequestStream_Handler,
ServerStreams: true,
},
},
Metadata: "api/proto/nacos_grpc_service.proto",
}
const (
Request_Request_FullMethodName = "/Request/request"
)
// RequestClient is the client API for Request service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type RequestClient interface {
// Sends a commonRequest
Request(ctx context.Context, in *Payload, opts ...grpc.CallOption) (*Payload, error)
}
type requestClient struct {
cc grpc.ClientConnInterface
}
func NewRequestClient(cc grpc.ClientConnInterface) RequestClient {
return &requestClient{cc}
}
func (c *requestClient) Request(ctx context.Context, in *Payload, opts ...grpc.CallOption) (*Payload, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Payload)
err := c.cc.Invoke(ctx, Request_Request_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// RequestServer is the server API for Request service.
// All implementations must embed UnimplementedRequestServer
// for forward compatibility.
type RequestServer interface {
// Sends a commonRequest
Request(context.Context, *Payload) (*Payload, error)
mustEmbedUnimplementedRequestServer()
}
// UnimplementedRequestServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedRequestServer struct{}
func (UnimplementedRequestServer) Request(context.Context, *Payload) (*Payload, error) {
return nil, status.Errorf(codes.Unimplemented, "method Request not implemented")
}
func (UnimplementedRequestServer) mustEmbedUnimplementedRequestServer() {}
func (UnimplementedRequestServer) testEmbeddedByValue() {}
// UnsafeRequestServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to RequestServer will
// result in compilation errors.
type UnsafeRequestServer interface {
mustEmbedUnimplementedRequestServer()
}
func RegisterRequestServer(s grpc.ServiceRegistrar, srv RequestServer) {
// If the following call pancis, it indicates UnimplementedRequestServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Request_ServiceDesc, srv)
}
func _Request_Request_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Payload)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RequestServer).Request(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Request_Request_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RequestServer).Request(ctx, req.(*Payload))
}
return interceptor(ctx, in, info, handler)
}
// Request_ServiceDesc is the grpc.ServiceDesc for Request service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Request_ServiceDesc = grpc.ServiceDesc{
ServiceName: "Request",
HandlerType: (*RequestServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "request",
Handler: _Request_Request_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/proto/nacos_grpc_service.proto",
}
const (
BiRequestStream_RequestBiStream_FullMethodName = "/BiRequestStream/requestBiStream"
)
// BiRequestStreamClient is the client API for BiRequestStream service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type BiRequestStreamClient interface {
// Sends a commonRequest
RequestBiStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Payload, Payload], error)
}
type biRequestStreamClient struct {
cc grpc.ClientConnInterface
}
func NewBiRequestStreamClient(cc grpc.ClientConnInterface) BiRequestStreamClient {
return &biRequestStreamClient{cc}
}
func (c *biRequestStreamClient) RequestBiStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Payload, Payload], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &BiRequestStream_ServiceDesc.Streams[0], BiRequestStream_RequestBiStream_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[Payload, Payload]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type BiRequestStream_RequestBiStreamClient = grpc.BidiStreamingClient[Payload, Payload]
// BiRequestStreamServer is the server API for BiRequestStream service.
// All implementations must embed UnimplementedBiRequestStreamServer
// for forward compatibility.
type BiRequestStreamServer interface {
// Sends a commonRequest
RequestBiStream(grpc.BidiStreamingServer[Payload, Payload]) error
mustEmbedUnimplementedBiRequestStreamServer()
}
// UnimplementedBiRequestStreamServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedBiRequestStreamServer struct{}
func (UnimplementedBiRequestStreamServer) RequestBiStream(grpc.BidiStreamingServer[Payload, Payload]) error {
return status.Errorf(codes.Unimplemented, "method RequestBiStream not implemented")
}
func (UnimplementedBiRequestStreamServer) mustEmbedUnimplementedBiRequestStreamServer() {}
func (UnimplementedBiRequestStreamServer) testEmbeddedByValue() {}
// UnsafeBiRequestStreamServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to BiRequestStreamServer will
// result in compilation errors.
type UnsafeBiRequestStreamServer interface {
mustEmbedUnimplementedBiRequestStreamServer()
}
func RegisterBiRequestStreamServer(s grpc.ServiceRegistrar, srv BiRequestStreamServer) {
// If the following call pancis, it indicates UnimplementedBiRequestStreamServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&BiRequestStream_ServiceDesc, srv)
}
func _BiRequestStream_RequestBiStream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(BiRequestStreamServer).RequestBiStream(&grpc.GenericServerStream[Payload, Payload]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type BiRequestStream_RequestBiStreamServer = grpc.BidiStreamingServer[Payload, Payload]
// BiRequestStream_ServiceDesc is the grpc.ServiceDesc for BiRequestStream service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var BiRequestStream_ServiceDesc = grpc.ServiceDesc{
ServiceName: "BiRequestStream",
HandlerType: (*BiRequestStreamServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "requestBiStream",
Handler: _BiRequestStream_RequestBiStream_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "api/proto/nacos_grpc_service.proto",
}

View File

@ -21,8 +21,6 @@ import "google/protobuf/any.proto";
option java_multiple_files = true; option java_multiple_files = true;
option java_package = "com.alibaba.nacos.api.grpc.auto"; option java_package = "com.alibaba.nacos.api.grpc.auto";
option go_package = "github.com/nacos-group/nacos-sdk-go/v2/api/grpc/auto;auto";
message Metadata { message Metadata {
string type = 3; string type = 3;

View File

@ -296,7 +296,7 @@ func (m ConcurrentMap) Keys() []string {
return keys return keys
} }
// Reviles ConcurrentMap "private" variables to json marshal. //Reviles ConcurrentMap "private" variables to json marshal.
func (m ConcurrentMap) MarshalJSON() ([]byte, error) { func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
// Create a temporary map, which will hold all item spread across shards. // Create a temporary map, which will hold all item spread across shards.
tmp := make(map[string]interface{}) tmp := make(map[string]interface{})

View File

@ -1,11 +0,0 @@
package cache
type ConfigCachedFileType string
const (
ConfigContent ConfigCachedFileType = "Config Content"
ConfigEncryptedDataKey ConfigCachedFileType = "Config Encrypted Data Key"
ENCRYPTED_DATA_KEY_FILE_NAME = "encrypted-data-key"
FAILOVER_FILE_SUFFIX = "_failover"
)

View File

@ -19,58 +19,35 @@ package cache
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"strconv" "strconv"
"strings"
"syscall"
"github.com/go-errors/errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/file" "github.com/nacos-group/nacos-sdk-go/v2/common/file"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/pkg/errors"
) )
var ( func GetFileName(cacheKey string, cacheDir string) string {
fileNotExistError = errors.New("file not exist")
)
func GetFileName(cacheKey, cacheDir string) string {
return cacheDir + string(os.PathSeparator) + cacheKey return cacheDir + string(os.PathSeparator) + cacheKey
} }
func GetEncryptedDataKeyDir(cacheDir string) string { func WriteServicesToFile(service model.Service, cacheDir string) {
return cacheDir + string(os.PathSeparator) + ENCRYPTED_DATA_KEY_FILE_NAME file.MkdirIfNecessary(cacheDir)
} sb, _ := json.Marshal(service)
domFileName := GetFileName(util.GetServiceCacheKey(service.Name, service.Clusters), cacheDir)
func GetConfigEncryptedDataKeyFileName(cacheKey, cacheDir string) string { err := ioutil.WriteFile(domFileName, sb, 0666)
return GetEncryptedDataKeyDir(cacheDir) + string(os.PathSeparator) + cacheKey
}
func GetConfigFailOverContentFileName(cacheKey, cacheDir string) string {
return GetFileName(cacheKey, cacheDir) + FAILOVER_FILE_SUFFIX
}
func GetConfigFailOverEncryptedDataKeyFileName(cacheKey, cacheDir string) string {
return GetConfigEncryptedDataKeyFileName(cacheKey, cacheDir) + FAILOVER_FILE_SUFFIX
}
func WriteServicesToFile(service *model.Service, cacheKey, cacheDir string) {
err := file.MkdirIfNecessary(cacheDir)
if err != nil { if err != nil {
logger.Errorf("mkdir cacheDir failed,cacheDir:%s,err:", cacheDir, err) logger.Errorf("failed to write name cache:%s ,value:%s ,err:%+v", domFileName, string(sb), err)
return
}
bytes, _ := json.Marshal(service)
domFileName := GetFileName(cacheKey, cacheDir)
err = os.WriteFile(domFileName, bytes, 0666)
if err != nil {
logger.Errorf("failed to write name cache:%s ,value:%s ,err:%v", domFileName, string(bytes), err)
} }
} }
func ReadServicesFromFile(cacheDir string) map[string]model.Service { func ReadServicesFromFile(cacheDir string) map[string]model.Service {
files, err := os.ReadDir(cacheDir) files, err := ioutil.ReadDir(cacheDir)
if err != nil { if err != nil {
logger.Errorf("read cacheDir:%s failed!err:%+v", cacheDir, err) logger.Errorf("read cacheDir:%s failed!err:%+v", cacheDir, err)
return nil return nil
@ -78,9 +55,9 @@ func ReadServicesFromFile(cacheDir string) map[string]model.Service {
serviceMap := map[string]model.Service{} serviceMap := map[string]model.Service{}
for _, f := range files { for _, f := range files {
fileName := GetFileName(f.Name(), cacheDir) fileName := GetFileName(f.Name(), cacheDir)
b, err := os.ReadFile(fileName) b, err := ioutil.ReadFile(fileName)
if err != nil { if err != nil {
logger.Errorf("failed to read name cache file:%s,err:%v ", fileName, err) logger.Errorf("failed to read name cache file:%s,err:%+v ", fileName, err)
continue continue
} }
@ -90,114 +67,28 @@ func ReadServicesFromFile(cacheDir string) map[string]model.Service {
if service == nil { if service == nil {
continue continue
} }
cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
serviceMap[cacheKey] = *service serviceMap[f.Name()] = *service
} }
logger.Infof("finish loading name cache, total: %s", strconv.Itoa(len(files))) logger.Info("finish loading name cache, total: " + strconv.Itoa(len(files)))
return serviceMap return serviceMap
} }
func WriteConfigToFile(cacheKey string, cacheDir string, content string) error { func WriteConfigToFile(cacheKey string, cacheDir string, content string) {
err := file.MkdirIfNecessary(cacheDir) file.MkdirIfNecessary(cacheDir)
fileName := GetFileName(cacheKey, cacheDir)
err := ioutil.WriteFile(fileName, []byte(content), 0666)
if err != nil { if err != nil {
errMsg := fmt.Sprintf("make dir failed, dir path %s, err: %v.", cacheDir, err) logger.Errorf("failed to write config cache:%s ,value:%s ,err:%+v", fileName, content, err)
logger.Error(errMsg)
return errors.New(errMsg)
} }
err = writeConfigToFile(GetFileName(cacheKey, cacheDir), content, ConfigContent)
if err != nil {
logger.Error(err)
return err
}
return nil
}
func WriteEncryptedDataKeyToFile(cacheKey string, cacheDir string, content string) error {
err := file.MkdirIfNecessary(GetEncryptedDataKeyDir(cacheDir))
if err != nil {
errMsg := fmt.Sprintf("make dir failed, dir path %s, err: %v.", cacheDir, err)
logger.Error(errMsg)
return errors.New(errMsg)
}
err = writeConfigToFile(GetConfigEncryptedDataKeyFileName(cacheKey, cacheDir), content, ConfigEncryptedDataKey)
if err != nil {
logger.Error(err)
return err
}
return nil
}
func writeConfigToFile(fileName string, content string, fileType ConfigCachedFileType) error {
if len(strings.TrimSpace(content)) == 0 {
// delete config snapshot
if err := os.Remove(fileName); err != nil {
if err != syscall.ENOENT {
logger.Debug(fmt.Sprintf("no need to delete %s cache file, file path %s, file doesn't exist.", fileType, fileName))
return nil
}
errMsg := fmt.Sprintf("failed to delete %s cache file, file path %s, err:%v", fileType, fileName, err)
return errors.New(errMsg)
}
}
err := os.WriteFile(fileName, []byte(content), 0666)
if err != nil {
errMsg := fmt.Sprintf("failed to write %s cache file, file name: %s, value: %s, err:%v", fileType, fileName, content, err)
return errors.New(errMsg)
}
return nil
}
func ReadEncryptedDataKeyFromFile(cacheKey string, cacheDir string) (string, error) {
content, err := readConfigFromFile(GetConfigEncryptedDataKeyFileName(cacheKey, cacheDir), ConfigEncryptedDataKey)
if err != nil {
if errors.Is(err, fileNotExistError) {
logger.Warn(err)
return "", nil
}
}
return content, nil
} }
func ReadConfigFromFile(cacheKey string, cacheDir string) (string, error) { func ReadConfigFromFile(cacheKey string, cacheDir string) (string, error) {
return readConfigFromFile(GetFileName(cacheKey, cacheDir), ConfigEncryptedDataKey) fileName := GetFileName(cacheKey, cacheDir)
} b, err := ioutil.ReadFile(fileName)
func readConfigFromFile(fileName string, fileType ConfigCachedFileType) (string, error) {
if !file.IsExistFile(fileName) {
errMsg := fmt.Sprintf("read cache file %s failed. cause file doesn't exist, file path: %s.", fileType, fileName)
return "", errors.Wrap(fileNotExistError, errMsg)
}
b, err := os.ReadFile(fileName)
if err != nil { if err != nil {
errMsg := fmt.Sprintf("get %s from cache failed, filePath:%s, error:%v ", fileType, fileName, err) return "", errors.New(fmt.Sprintf("failed to read config cache file:%s,err:%+v ", fileName, err))
return "", errors.New(errMsg)
} }
return string(b), nil return string(b), nil
} }
// GetFailover , get failover content
func GetFailover(key, dir string) string {
filePath := GetConfigFailOverContentFileName(key, dir)
return getFailOverConfig(filePath, ConfigContent)
}
func GetFailoverEncryptedDataKey(key, dir string) string {
filePath := GetConfigFailOverEncryptedDataKeyFileName(key, dir)
return getFailOverConfig(filePath, ConfigEncryptedDataKey)
}
func getFailOverConfig(filePath string, fileType ConfigCachedFileType) string {
if !file.IsExistFile(filePath) {
errMsg := fmt.Sprintf("read %s failed. cause file doesn't exist, file path: %s.", fileType, filePath)
logger.Warn(errMsg)
return ""
}
logger.Warnf("reading failover %s from path:%s", fileType, filePath)
fileContent, err := os.ReadFile(filePath)
if err != nil {
logger.Errorf("fail to read failover %s from %s", fileType, filePath)
return ""
}
return string(fileContent)
}

View File

@ -1,127 +0,0 @@
package cache
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"math/rand"
"os"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/nacos-group/nacos-sdk-go/v2/common/file"
)
var (
dir = file.GetCurrentPath()
group = "FILE_GROUP"
ns = "chasu"
)
func TestWriteAndGetConfigToFile(t *testing.T) {
dataIdSuffix := strconv.Itoa(rand.Intn(1000))
t.Run("write and get config content", func(t *testing.T) {
dataId := "config_content" + dataIdSuffix
cacheKey := util.GetConfigCacheKey(dataId, group, ns)
configContent := "config content"
err := WriteConfigToFile(cacheKey, dir, "")
assert.Nil(t, err)
configFromFile, err := ReadConfigFromFile(cacheKey, dir)
assert.NotNil(t, err)
assert.Equal(t, configFromFile, "")
err = WriteConfigToFile(cacheKey, dir, configContent)
assert.Nil(t, err)
fromFile, err := ReadConfigFromFile(cacheKey, dir)
assert.Nil(t, err)
assert.Equal(t, fromFile, configContent)
err = WriteConfigToFile(cacheKey, dir, "")
assert.Nil(t, err)
configFromFile, err = ReadConfigFromFile(cacheKey, dir)
assert.Nil(t, err)
assert.Equal(t, configFromFile, "")
})
t.Run("write and get config encryptedDataKey", func(t *testing.T) {
dataId := "config_encryptedDataKey" + dataIdSuffix
cacheKey := util.GetConfigCacheKey(dataId, group, ns)
configContent := "config encrypted data key"
err := WriteEncryptedDataKeyToFile(cacheKey, dir, "")
assert.Nil(t, err)
configFromFile, err := ReadEncryptedDataKeyFromFile(cacheKey, dir)
assert.Nil(t, err)
assert.Equal(t, configFromFile, "")
err = WriteEncryptedDataKeyToFile(cacheKey, dir, configContent)
assert.Nil(t, err)
fromFile, err := ReadEncryptedDataKeyFromFile(cacheKey, dir)
assert.Nil(t, err)
assert.Equal(t, fromFile, configContent)
err = WriteEncryptedDataKeyToFile(cacheKey, dir, "")
assert.Nil(t, err)
configFromFile, err = ReadEncryptedDataKeyFromFile(cacheKey, dir)
assert.Nil(t, err)
assert.Equal(t, configFromFile, "")
})
t.Run("double write config file", func(t *testing.T) {
dataId := "config_encryptedDataKey" + dataIdSuffix
cacheKey := util.GetConfigCacheKey(dataId, group, ns)
configContent := "config encrypted data key"
err := WriteConfigToFile(cacheKey, dir, configContent)
assert.Nil(t, err)
err = WriteConfigToFile(cacheKey, dir, configContent)
assert.Nil(t, err)
fromFile, err := ReadConfigFromFile(cacheKey, dir)
assert.Nil(t, err)
assert.Equal(t, fromFile, configContent)
})
t.Run("read doesn't existed config file", func(t *testing.T) {
dataId := "config_encryptedDataKey" + dataIdSuffix + strconv.Itoa(rand.Intn(1000))
cacheKey := util.GetConfigCacheKey(dataId, group, ns)
_, err := ReadConfigFromFile(cacheKey, dir)
assert.NotNil(t, err)
_, err = ReadEncryptedDataKeyFromFile(cacheKey, dir)
assert.Nil(t, err)
})
}
func TestGetFailover(t *testing.T) {
cacheKey := "test_failOver"
fileContent := "test_failover"
t.Run("writeContent", func(t *testing.T) {
filepath := dir + string(os.PathSeparator) + cacheKey + "_failover"
fmt.Println(filepath)
err := writeFileContent(filepath, fileContent)
assert.Nil(t, err)
})
t.Run("getContent", func(t *testing.T) {
content := GetFailover(cacheKey, dir)
assert.Equal(t, content, fileContent)
})
t.Run("clearContent", func(t *testing.T) {
filepath := dir + string(os.PathSeparator) + cacheKey + "_failover"
err := writeFileContent(filepath, "")
assert.Nil(t, err)
})
}
// write file content
func writeFileContent(filepath, content string) error {
return os.WriteFile(filepath, []byte(content), 0666)
}

View File

@ -17,7 +17,7 @@
package clients package clients
import ( import (
"github.com/pkg/errors" "errors"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
@ -34,7 +34,7 @@ func CreateConfigClient(properties map[string]interface{}) (iClient config_clien
return NewConfigClient(param) return NewConfigClient(param)
} }
// CreateNamingClient use to create a nacos naming client //CreateNamingClient use to create a nacos naming client
func CreateNamingClient(properties map[string]interface{}) (iClient naming_client.INamingClient, err error) { func CreateNamingClient(properties map[string]interface{}) (iClient naming_client.INamingClient, err error) {
param := getConfigParam(properties) param := getConfigParam(properties)
return NewNamingClient(param) return NewNamingClient(param)
@ -45,7 +45,7 @@ func NewConfigClient(param vo.NacosClientParam) (iClient config_client.IConfigCl
if err != nil { if err != nil {
return return
} }
config, err := config_client.NewConfigClientWithRamCredentialProvider(nacosClient, param.RamCredentialProvider) config, err := config_client.NewConfigClient(nacosClient)
if err != nil { if err != nil {
return return
} }
@ -58,7 +58,7 @@ func NewNamingClient(param vo.NacosClientParam) (iClient naming_client.INamingCl
if err != nil { if err != nil {
return return
} }
naming, err := naming_client.NewNamingClientWithRamCredentialProvider(nacosClient, param.RamCredentialProvider) naming, err := naming_client.NewNamingClient(nacosClient)
if err != nil { if err != nil {
return return
} }
@ -104,14 +104,6 @@ func setConfig(param vo.NacosClientParam) (iClient nacos_client.INacosClient, er
} }
_ = client.SetServerConfig(nil) _ = client.SetServerConfig(nil)
} else { } else {
for i := range param.ServerConfigs {
if param.ServerConfigs[i].Port == 0 {
param.ServerConfigs[i].Port = 8848
}
if param.ServerConfigs[i].GrpcPort == 0 {
param.ServerConfigs[i].GrpcPort = param.ServerConfigs[i].Port + constant.RpcPortOffset
}
}
err = client.SetServerConfig(param.ServerConfigs) err = client.SetServerConfig(param.ServerConfigs)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -1,7 +1,6 @@
package clients package clients
import ( import (
"net"
"reflect" "reflect"
"testing" "testing"
@ -10,33 +9,18 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func getIntranetIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "127.0.0.1"
}
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return "127.0.0.1"
}
func TestSetConfigClient(t *testing.T) { func TestSetConfigClient(t *testing.T) {
ip := getIntranetIP()
sc := []constant.ServerConfig{ sc := []constant.ServerConfig{
*constant.NewServerConfig( *constant.NewServerConfig(
ip, "console.nacos.io",
8848, 80,
), constant.WithScheme("http"),
constant.WithContextPath("/nacos")),
} }
cc := *constant.NewClientConfig( cc := *constant.NewClientConfig(
constant.WithNamespaceId("public"), constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"),
constant.WithTimeoutMs(5000), constant.WithTimeoutMs(5000),
constant.WithNotLoadCacheAtStart(true), constant.WithNotLoadCacheAtStart(true),
constant.WithLogDir("/tmp/nacos/log"), constant.WithLogDir("/tmp/nacos/log"),
@ -65,4 +49,5 @@ func TestSetConfigClient(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, reflect.DeepEqual(nacosClientFromMap, nacosClientFromStruct)) assert.True(t, reflect.DeepEqual(nacosClientFromMap, nacosClientFromStruct))
}) })
} }

View File

@ -17,21 +17,18 @@
package config_client package config_client
import ( import (
"context" "errors"
"fmt" "fmt"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"os" "os"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client" "github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
nacos_inner_encryption "github.com/nacos-group/nacos-sdk-go/v2/common/encryption"
"github.com/nacos-group/nacos-sdk-go/v2/common/filter"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error" "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
@ -39,7 +36,6 @@ import (
"github.com/nacos-group/nacos-sdk-go/v2/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/v2/vo" "github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/pkg/errors"
) )
const ( const (
@ -48,10 +44,8 @@ const (
) )
type ConfigClient struct { type ConfigClient struct {
ctx context.Context
cancel context.CancelFunc
nacos_client.INacosClient nacos_client.INacosClient
configFilterChainManager filter.IConfigFilterChain kmsClient *kms.Client
localConfigs []vo.ConfigParam localConfigs []vo.ConfigParam
mutex sync.Mutex mutex sync.Mutex
configProxy IConfigProxy configProxy IConfigProxy
@ -60,7 +54,6 @@ type ConfigClient struct {
cacheMap cache.ConcurrentMap cacheMap cache.ConcurrentMap
uid string uid string
listenExecute chan struct{} listenExecute chan struct{}
isClosed bool
} }
type cacheData struct { type cacheData struct {
@ -69,7 +62,6 @@ type cacheData struct {
group string group string
content string content string
contentType string contentType string
encryptedDataKey string
tenant string tenant string
cacheDataListener *cacheDataListener cacheDataListener *cacheDataListener
md5 string md5 string
@ -84,28 +76,8 @@ type cacheDataListener struct {
lastMd5 string lastMd5 string
} }
func (cacheData *cacheData) executeListener() { func NewConfigClient(nc nacos_client.INacosClient) (*ConfigClient, error) {
cacheData.cacheDataListener.lastMd5 = cacheData.md5
cacheData.configClient.cacheMap.Set(util.GetConfigCacheKey(cacheData.dataId, cacheData.group, cacheData.tenant), *cacheData)
param := &vo.ConfigParam{
DataId: cacheData.dataId,
Content: cacheData.content,
EncryptedDataKey: cacheData.encryptedDataKey,
UsageType: vo.ResponseType,
}
if err := cacheData.configClient.configFilterChainManager.DoFilters(param); err != nil {
logger.Errorf("do filters failed ,dataId=%s,group=%s,tenant=%s,err:%+v ", cacheData.dataId,
cacheData.group, cacheData.tenant, err)
return
}
decryptedContent := param.Content
go cacheData.cacheDataListener.listener(cacheData.tenant, cacheData.group, cacheData.dataId, decryptedContent)
}
func NewConfigClientWithRamCredentialProvider(nc nacos_client.INacosClient, provider security.RamCredentialProvider) (*ConfigClient, error) {
config := &ConfigClient{} config := &ConfigClient{}
config.ctx, config.cancel = context.WithCancel(context.Background())
config.INacosClient = nc config.INacosClient = nc
clientConfig, err := nc.GetClientConfig() clientConfig, err := nc.GetClientConfig()
if err != nil { if err != nil {
@ -123,39 +95,33 @@ func NewConfigClientWithRamCredentialProvider(nc nacos_client.INacosClient, prov
if err = initLogger(clientConfig); err != nil { if err = initLogger(clientConfig); err != nil {
return nil, err return nil, err
} }
clientConfig.CacheDir = clientConfig.CacheDir + string(os.PathSeparator) + "config" config.configCacheDir = clientConfig.CacheDir + string(os.PathSeparator) + "config"
config.configCacheDir = clientConfig.CacheDir
if config.configProxy, err = NewConfigProxyWithRamCredentialProvider(config.ctx, serverConfig, clientConfig, httpAgent, provider); err != nil { if config.configProxy, err = NewConfigProxy(serverConfig, clientConfig, httpAgent); err != nil {
return nil, err return nil, err
} }
config.configFilterChainManager = filter.NewConfigFilterChainManager()
if clientConfig.OpenKMS { if clientConfig.OpenKMS {
kmsEncryptionHandler := nacos_inner_encryption.NewKmsHandler() kmsClient, err := kms.NewClientWithAccessKey(clientConfig.RegionId, clientConfig.AccessKey, clientConfig.SecretKey)
nacos_inner_encryption.RegisterConfigEncryptionKmsPlugins(kmsEncryptionHandler, clientConfig)
encryptionFilter := filter.NewDefaultConfigEncryptionFilter(kmsEncryptionHandler)
err := filter.RegisterConfigFilterToChain(config.configFilterChainManager, encryptionFilter)
if err != nil { if err != nil {
logger.Error(err) return nil, err
} }
config.kmsClient = kmsClient
} }
uid, err := uuid.NewV4() uid, err := uuid.NewV4()
if err != nil { if err != nil {
return nil, err return nil, err
} }
config.uid = uid.String() config.uid = uid.String()
config.cacheMap = cache.NewConcurrentMap()
config.listenExecute = make(chan struct{})
config.startInternal()
return config, err
}
func NewConfigClient(nc nacos_client.INacosClient) (*ConfigClient, error) { config.cacheMap = cache.NewConcurrentMap()
return NewConfigClientWithRamCredentialProvider(nc, nil)
config.listenExecute = make(chan struct{}, 1)
config.startInternal()
return config, err
} }
func initLogger(clientConfig constant.ClientConfig) error { func initLogger(clientConfig constant.ClientConfig) error {
@ -163,72 +129,72 @@ func initLogger(clientConfig constant.ClientConfig) error {
} }
func (client *ConfigClient) GetConfig(param vo.ConfigParam) (content string, err error) { func (client *ConfigClient) GetConfig(param vo.ConfigParam) (content string, err error) {
content, encryptedDataKey, err := client.getConfigInner(param) content, err = client.getConfigInner(param)
if err != nil { if err != nil {
return "", err return "", err
} }
deepCopyParam := param.DeepCopy()
deepCopyParam.EncryptedDataKey = encryptedDataKey return client.decrypt(param.DataId, content)
deepCopyParam.Content = content }
deepCopyParam.UsageType = vo.ResponseType
if err = client.configFilterChainManager.DoFilters(deepCopyParam); err != nil { func (client *ConfigClient) decrypt(dataId, content string) (string, error) {
return "", err if client.kmsClient != nil && strings.HasPrefix(dataId, "cipher-") {
request := kms.CreateDecryptRequest()
request.Method = "POST"
request.Scheme = "https"
request.AcceptFormat = "json"
request.CiphertextBlob = content
response, err := client.kmsClient.Decrypt(request)
if err != nil {
return "", fmt.Errorf("kms decrypt failed: %v", err)
}
content = response.Plaintext
} }
content = deepCopyParam.Content
return content, nil return content, nil
} }
func (client *ConfigClient) getConfigInner(param vo.ConfigParam) (content, encryptedDataKey string, err error) { func (client *ConfigClient) encrypt(dataId, content string) (string, error) {
if client.kmsClient != nil && strings.HasPrefix(dataId, "cipher-") {
request := kms.CreateEncryptRequest()
request.Method = "POST"
request.Scheme = "https"
request.AcceptFormat = "json"
request.KeyId = "alias/acs/acm" // use default key
request.Plaintext = content
response, err := client.kmsClient.Encrypt(request)
if err != nil {
return "", fmt.Errorf("kms encrypt failed: %v", err)
}
content = response.CiphertextBlob
}
return content, nil
}
func (client *ConfigClient) getConfigInner(param vo.ConfigParam) (content string, err error) {
if len(param.DataId) <= 0 { if len(param.DataId) <= 0 {
err = errors.New("[client.GetConfig] param.dataId can not be empty") err = errors.New("[client.GetConfig] param.dataId can not be empty")
return "", "", err return "", err
} }
if len(param.Group) <= 0 { if len(param.Group) <= 0 {
param.Group = constant.DEFAULT_GROUP param.Group = constant.DEFAULT_GROUP
} }
//todo 优先使用本地配置
//todo 获取容灾配置的 EncryptedDataKey LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover
clientConfig, _ := client.GetClientConfig() clientConfig, _ := client.GetClientConfig()
cacheKey := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId) cacheKey := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)
content = cache.GetFailover(cacheKey, client.configCacheDir)
if len(content) > 0 {
logger.Warnf("%s %s %s is using failover content!", clientConfig.NamespaceId, param.Group, param.DataId)
encryptedDataKey = cache.GetFailoverEncryptedDataKey(cacheKey, client.configCacheDir)
return content, encryptedDataKey, nil
}
response, err := client.configProxy.queryConfig(param.DataId, param.Group, clientConfig.NamespaceId, response, err := client.configProxy.queryConfig(param.DataId, param.Group, clientConfig.NamespaceId,
clientConfig.TimeoutMs, false, client) clientConfig.TimeoutMs, false, client)
if err != nil { if err != nil {
logger.Errorf("get config from server error:%v, dataId=%s, group=%s, namespaceId=%s", err, logger.Infof("get config from server error:%+v ", err)
param.DataId, param.Group, clientConfig.NamespaceId) content, err = cache.ReadConfigFromFile(cacheKey, client.configCacheDir)
if err != nil {
if clientConfig.DisableUseSnapShot { logger.Errorf("get config from cache error:%+v ", err)
return "", "", errors.Errorf("get config from remote nacos server fail, and is not allowed to read local file, err:%v", err) return "", errors.New("read config from both server and cache fail")
} }
return content, nil
cacheContent, cacheErr := cache.ReadConfigFromFile(cacheKey, client.configCacheDir)
if cacheErr != nil {
return "", "", errors.Errorf("read config from both server and cache fail, err=%vdataId=%s, group=%s, namespaceId=%s",
cacheErr, param.DataId, param.Group, clientConfig.NamespaceId)
} }
return response.Content, nil
if !strings.HasPrefix(param.DataId, nacos_inner_encryption.CipherPrefix) {
return cacheContent, "", nil
}
encryptedDataKey, cacheErr = cache.ReadEncryptedDataKeyFromFile(cacheKey, client.configCacheDir)
if cacheErr != nil {
return "", "", errors.Errorf("read encryptedDataKey from server and cache fail, err=%vdataId=%s, group=%s, namespaceId=%s",
cacheErr, param.DataId, param.Group, clientConfig.NamespaceId)
}
logger.Warnf("read config from cache success, dataId=%s, group=%s, namespaceId=%s", param.DataId, param.Group, clientConfig.NamespaceId)
return cacheContent, encryptedDataKey, nil
}
if response != nil && response.Response != nil && !response.IsSuccess() {
return response.Content, response.EncryptedDataKey, errors.New(response.GetMessage())
}
encryptedDataKey = response.EncryptedDataKey
content = response.Content
return content, encryptedDataKey, nil
} }
func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool, err error) { func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool, err error) {
@ -244,28 +210,21 @@ func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool,
if len(param.Group) <= 0 { if len(param.Group) <= 0 {
param.Group = constant.DEFAULT_GROUP param.Group = constant.DEFAULT_GROUP
} }
if param.Content, err = client.encrypt(param.DataId, param.Content); err != nil {
param.UsageType = vo.RequestType return
if err = client.configFilterChainManager.DoFilters(&param); err != nil {
return false, err
} }
clientConfig, _ := client.GetClientConfig() clientConfig, _ := client.GetClientConfig()
request := rpc_request.NewConfigPublishRequest(param.Group, param.DataId, clientConfig.NamespaceId, param.Content, param.CasMd5) request := rpc_request.NewConfigPublishRequest(param.Group, param.DataId, clientConfig.NamespaceId, param.Content, param.CasMd5)
request.AdditionMap["tag"] = param.Tag request.AdditionMap["tag"] = param.Tag
request.AdditionMap["config_tags"] = param.ConfigTags
request.AdditionMap["appName"] = param.AppName request.AdditionMap["appName"] = param.AppName
request.AdditionMap["betaIps"] = param.BetaIps request.AdditionMap["betaIps"] = param.BetaIps
request.AdditionMap["type"] = param.Type request.AdditionMap["type"] = param.Type
request.AdditionMap["src_user"] = param.SrcUser
request.AdditionMap["encryptedDataKey"] = param.EncryptedDataKey request.AdditionMap["encryptedDataKey"] = param.EncryptedDataKey
rpcClient := client.configProxy.getRpcClient(client) rpcClient := client.configProxy.getRpcClient(client)
response, err := client.configProxy.requestProxy(rpcClient, request, constant.DEFAULT_TIMEOUT_MILLS) response, err := client.configProxy.requestProxy(rpcClient, request, constant.DEFAULT_TIMEOUT_MILLS)
if err != nil {
return false, err
}
if response != nil { if response != nil {
return client.buildResponse(response) return response.IsSuccess(), err
} }
return false, err return false, err
} }
@ -284,16 +243,13 @@ func (client *ConfigClient) DeleteConfig(param vo.ConfigParam) (deleted bool, er
request := rpc_request.NewConfigRemoveRequest(param.Group, param.DataId, clientConfig.NamespaceId) request := rpc_request.NewConfigRemoveRequest(param.Group, param.DataId, clientConfig.NamespaceId)
rpcClient := client.configProxy.getRpcClient(client) rpcClient := client.configProxy.getRpcClient(client)
response, err := client.configProxy.requestProxy(rpcClient, request, constant.DEFAULT_TIMEOUT_MILLS) response, err := client.configProxy.requestProxy(rpcClient, request, constant.DEFAULT_TIMEOUT_MILLS)
if err != nil {
return false, err
}
if response != nil { if response != nil {
return client.buildResponse(response) return response.IsSuccess(), err
} }
return false, err return false, err
} }
// Cancel Listen Config //Cancel Listen Config
func (client *ConfigClient) CancelListenConfig(param vo.ConfigParam) (err error) { func (client *ConfigClient) CancelListenConfig(param vo.ConfigParam) (err error) {
clientConfig, err := client.GetClientConfig() clientConfig, err := client.GetClientConfig()
if err != nil { if err != nil {
@ -321,20 +277,16 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
} }
key := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId) key := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)
var cData cacheData var cData *cacheData
if v, ok := client.cacheMap.Get(key); ok { if v, ok := client.cacheMap.Get(key); ok {
cData = v.(cacheData) cData = v.(*cacheData)
cData.isInitializing = true cData.isInitializing = true
} else { } else {
var ( var (
content string content string
md5Str string md5Str string
innerErr error
) )
if content, innerErr = cache.ReadConfigFromFile(key, client.configCacheDir); innerErr != nil { content, _ = cache.ReadConfigFromFile(key, client.configCacheDir)
logger.Warn(innerErr)
}
encryptedDataKey, _ := cache.ReadEncryptedDataKeyFromFile(key, client.configCacheDir)
if len(content) > 0 { if len(content) > 0 {
md5Str = util.Md5(content) md5Str = util.Md5(content)
} }
@ -343,7 +295,7 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
lastMd5: md5Str, lastMd5: md5Str,
} }
cData = cacheData{ cData = &cacheData{
isInitializing: true, isInitializing: true,
dataId: param.DataId, dataId: param.DataId,
group: param.Group, group: param.Group,
@ -351,7 +303,6 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
content: content, content: content,
md5: md5Str, md5: md5Str,
cacheDataListener: listener, cacheDataListener: listener,
encryptedDataKey: encryptedDataKey,
taskId: client.cacheMap.Count() / perTaskConfigSize, taskId: client.cacheMap.Count() / perTaskConfigSize,
configClient: client, configClient: client,
} }
@ -360,23 +311,15 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
return return
} }
func (client *ConfigClient) SearchConfig(param vo.SearchConfigParam) (*model.ConfigPage, error) { func (client *ConfigClient) SearchConfig(param vo.SearchConfigParm) (*model.ConfigPage, error) {
return client.searchConfigInner(param) return client.searchConfigInner(param)
} }
func (client *ConfigClient) CloseClient() { func (client *ConfigClient) CloseClient() {
client.mutex.Lock()
defer client.mutex.Unlock()
if client.isClosed {
return
}
client.configProxy.getRpcClient(client).Shutdown() client.configProxy.getRpcClient(client).Shutdown()
client.cancel()
client.isClosed = true
} }
func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParam) (*model.ConfigPage, error) { func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParm) (*model.ConfigPage, error) {
if param.Search != "accurate" && param.Search != "blur" { if param.Search != "accurate" && param.Search != "blur" {
return nil, errors.New("[client.searchConfigInner] param.search must be accurate or blur") return nil, errors.New("[client.searchConfigInner] param.search must be accurate or blur")
} }
@ -405,17 +348,14 @@ func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParam) (*mode
} }
func (client *ConfigClient) startInternal() { func (client *ConfigClient) startInternal() {
go func() {
timer := time.NewTimer(executorErrDelay) timer := time.NewTimer(executorErrDelay)
defer timer.Stop() go func() {
for { for {
select { select {
case <-client.listenExecute: case <-client.listenExecute:
client.executeConfigListen() client.executeConfigListen()
case <-timer.C: case <-timer.C:
client.executeConfigListen() client.executeConfigListen()
case <-client.ctx.Done():
return
} }
timer.Reset(executorErrDelay) timer.Reset(executorErrDelay)
} }
@ -423,74 +363,81 @@ func (client *ConfigClient) startInternal() {
} }
func (client *ConfigClient) executeConfigListen() { func (client *ConfigClient) executeConfigListen() {
var ( listenCachesMap := make(map[int][]*cacheData, 16)
needAllSync = time.Since(client.lastAllSyncTime) >= constant.ALL_SYNC_INTERNAL needAllSync := time.Since(client.lastAllSyncTime) >= constant.ALL_SYNC_INTERNAL
hasChangedKeys = false for _, v := range client.cacheMap.Items() {
) cache, ok := v.(*cacheData)
listenTaskMap := client.buildListenTask(needAllSync)
if len(listenTaskMap) == 0 {
return
}
for taskId, caches := range listenTaskMap {
request := buildConfigBatchListenRequest(caches)
rpcClient := client.configProxy.createRpcClient(client.ctx, fmt.Sprintf("%d", taskId), client)
iResponse, err := client.configProxy.requestProxy(rpcClient, request, 3000)
if err != nil {
logger.Warnf("ConfigBatchListenRequest failure, err:%v", err)
continue
}
if iResponse == nil {
logger.Warnf("ConfigBatchListenRequest failure, response is nil")
continue
}
if !iResponse.IsSuccess() {
logger.Warnf("ConfigBatchListenRequest failure, error code:%d", iResponse.GetErrorCode())
continue
}
response, ok := iResponse.(*rpc_response.ConfigChangeBatchListenResponse)
if !ok { if !ok {
continue continue
} }
if cache.isSyncWithServer {
if cache.md5 != cache.cacheDataListener.lastMd5 {
go cache.cacheDataListener.listener(cache.tenant, cache.group, cache.dataId, cache.content)
cache.cacheDataListener.lastMd5 = cache.md5
}
if !needAllSync {
continue
}
}
var cacheDatas []*cacheData
if cacheDatas, ok = listenCachesMap[cache.taskId]; ok {
cacheDatas = append(cacheDatas, cache)
} else {
cacheDatas = append(cacheDatas, cache)
}
listenCachesMap[cache.taskId] = cacheDatas
}
hasChangedKeys := false
if len(listenCachesMap) > 0 {
for taskId, listenCaches := range listenCachesMap {
request := buildConfigBatchListenRequest(listenCaches)
rpcClient := client.configProxy.createRpcClient(fmt.Sprintf("%d", taskId), client)
iResponse, err := client.configProxy.requestProxy(rpcClient, request, 3000)
if err != nil {
logger.Warnf("ConfigBatchListenRequest failure,err:%+v", err)
continue
}
if iResponse == nil && !iResponse.IsSuccess() {
continue
}
changeKeys := make(map[string]struct{})
if response, ok := iResponse.(*rpc_response.ConfigChangeBatchListenResponse); ok {
if len(response.ChangedConfigs) > 0 { if len(response.ChangedConfigs) > 0 {
hasChangedKeys = true hasChangedKeys = true
}
changeKeys := make(map[string]struct{}, len(response.ChangedConfigs))
for _, v := range response.ChangedConfigs { for _, v := range response.ChangedConfigs {
changeKey := util.GetConfigCacheKey(v.DataId, v.Group, v.Tenant) changeKey := util.GetConfigCacheKey(v.DataId, v.Group, v.Tenant)
changeKeys[changeKey] = struct{}{} changeKeys[changeKey] = struct{}{}
if value, ok := client.cacheMap.Get(changeKey); ok { if cache, ok := client.cacheMap.Get(changeKey); !ok {
cData := value.(cacheData) continue
client.refreshContentAndCheck(cData, !cData.isInitializing) } else {
cacheData := cache.(*cacheData)
client.refreshContentAndCheck(cacheData, !cacheData.isInitializing)
}
} }
} }
for _, v := range client.cacheMap.Items() { for _, v := range listenCaches {
data := v.(cacheData) changeKey := util.GetConfigCacheKey(v.dataId, v.group, v.tenant)
changeKey := util.GetConfigCacheKey(data.dataId, data.group, data.tenant)
if _, ok := changeKeys[changeKey]; !ok { if _, ok := changeKeys[changeKey]; !ok {
data.isSyncWithServer = true v.isSyncWithServer = true
client.cacheMap.Set(changeKey, data)
continue continue
} }
data.isInitializing = true v.isInitializing = true
client.cacheMap.Set(changeKey, data) }
}
} }
} }
if needAllSync { if needAllSync {
client.lastAllSyncTime = time.Now() client.lastAllSyncTime = time.Now()
} }
if hasChangedKeys { if hasChangedKeys {
client.asyncNotifyListenConfig() client.notifyListenConfig()
} }
monitor.GetListenConfigCountMonitor().Set(float64(client.cacheMap.Count()))
} }
func buildConfigBatchListenRequest(caches []cacheData) *rpc_request.ConfigBatchListenRequest { func buildConfigBatchListenRequest(caches []*cacheData) *rpc_request.ConfigBatchListenRequest {
request := rpc_request.NewConfigBatchListenRequest(len(caches)) request := rpc_request.NewConfigBatchListenRequest(len(caches))
for _, cache := range caches { for _, cache := range caches {
request.ConfigListenContexts = append(request.ConfigListenContexts, request.ConfigListenContexts = append(request.ConfigListenContexts,
@ -499,7 +446,7 @@ func buildConfigBatchListenRequest(caches []cacheData) *rpc_request.ConfigBatchL
return request return request
} }
func (client *ConfigClient) refreshContentAndCheck(cacheData cacheData, notify bool) { func (client *ConfigClient) refreshContentAndCheck(cacheData *cacheData, notify bool) {
configQueryResponse, err := client.configProxy.queryConfig(cacheData.dataId, cacheData.group, cacheData.tenant, configQueryResponse, err := client.configProxy.queryConfig(cacheData.dataId, cacheData.group, cacheData.tenant,
constant.DEFAULT_TIMEOUT_MILLS, notify, client) constant.DEFAULT_TIMEOUT_MILLS, notify, client)
if err != nil { if err != nil {
@ -507,14 +454,8 @@ func (client *ConfigClient) refreshContentAndCheck(cacheData cacheData, notify b
cacheData.group, cacheData.tenant) cacheData.group, cacheData.tenant)
return return
} }
if configQueryResponse != nil && configQueryResponse.Response != nil && !configQueryResponse.IsSuccess() {
logger.Errorf("refresh cached config from server error:%v, dataId=%s, group=%s", configQueryResponse.GetMessage(),
cacheData.dataId, cacheData.group)
return
}
cacheData.content = configQueryResponse.Content cacheData.content = configQueryResponse.Content
cacheData.contentType = configQueryResponse.ContentType cacheData.contentType = configQueryResponse.ContentType
cacheData.encryptedDataKey = configQueryResponse.EncryptedDataKey
if notify { if notify {
logger.Infof("[config_rpc_client] [data-received] dataId=%s, group=%s, tenant=%s, md5=%s, content=%s, type=%s", logger.Infof("[config_rpc_client] [data-received] dataId=%s, group=%s, tenant=%s, md5=%s, content=%s, type=%s",
cacheData.dataId, cacheData.group, cacheData.tenant, cacheData.md5, cacheData.dataId, cacheData.group, cacheData.tenant, cacheData.md5,
@ -522,42 +463,13 @@ func (client *ConfigClient) refreshContentAndCheck(cacheData cacheData, notify b
} }
cacheData.md5 = util.Md5(cacheData.content) cacheData.md5 = util.Md5(cacheData.content)
if cacheData.md5 != cacheData.cacheDataListener.lastMd5 { if cacheData.md5 != cacheData.cacheDataListener.lastMd5 {
cacheDataPtr := &cacheData go cacheData.cacheDataListener.listener(cacheData.tenant, cacheData.group, cacheData.dataId, cacheData.content)
cacheDataPtr.executeListener() cacheData.cacheDataListener.lastMd5 = cacheData.md5
client.cacheMap.Set(util.GetConfigCacheKey(cacheData.dataId, cacheData.group, cacheData.tenant), cacheData)
} }
} }
func (client *ConfigClient) buildListenTask(needAllSync bool) map[int][]cacheData { func (client *ConfigClient) notifyListenConfig() {
listenTaskMap := make(map[int][]cacheData, 8)
for _, v := range client.cacheMap.Items() {
data, ok := v.(cacheData)
if !ok {
continue
}
if data.isSyncWithServer {
if data.md5 != data.cacheDataListener.lastMd5 {
data.executeListener()
}
if !needAllSync {
continue
}
}
listenTaskMap[data.taskId] = append(listenTaskMap[data.taskId], data)
}
return listenTaskMap
}
func (client *ConfigClient) asyncNotifyListenConfig() {
go func() {
client.listenExecute <- struct{}{} client.listenExecute <- struct{}{}
}()
}
func (client *ConfigClient) buildResponse(response rpc_response.IResponse) (bool, error) {
if response.IsSuccess() {
return response.IsSuccess(), nil
}
return false, errors.New(response.GetMessage())
} }

View File

@ -63,7 +63,7 @@ type IConfigClient interface {
// tenant ==>nacos.namespace optional // tenant ==>nacos.namespace optional
// pageNo option,default is 1 // pageNo option,default is 1
// pageSize option,default is 10 // pageSize option,default is 10
SearchConfig(param vo.SearchConfigParam) (*model.ConfigPage, error) SearchConfig(param vo.SearchConfigParm) (*model.ConfigPage, error)
// CloseClient Close the GRPC client // CloseClient Close the GRPC client
CloseClient() CloseClient()

View File

@ -17,10 +17,6 @@
package config_client package config_client
import ( import (
"context"
"errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"testing" "testing"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
@ -35,29 +31,12 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var serverConfigWithOptions = constant.NewServerConfig("127.0.0.1", 8848) var serverConfigWithOptions = constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos"))
var clientConfigWithOptions = constant.NewClientConfig( var clientConfigWithOptions = constant.NewClientConfig(
constant.WithTimeoutMs(10*1000), constant.WithTimeoutMs(10*1000),
constant.WithBeatInterval(2*1000), constant.WithBeatInterval(2*1000),
constant.WithNotLoadCacheAtStart(true), constant.WithNotLoadCacheAtStart(true),
constant.WithAccessKey("LTAxxx"),
constant.WithSecretKey("EdPxxx"),
constant.WithOpenKMS(true),
constant.WithKMSVersion(constant.KMSv1),
constant.WithRegionId("cn-hangzhou"),
)
var clientTLsConfigWithOptions = constant.NewClientConfig(
constant.WithTimeoutMs(10*1000),
constant.WithBeatInterval(2*1000),
constant.WithNotLoadCacheAtStart(true),
/*constant.WithTLS(constant.TLSConfig{
Enable: true,
TrustAll: false,
CaFile: "mse-nacos-ca.cer",
}),*/
) )
var localConfigTest = vo.ConfigParam{ var localConfigTest = vo.ConfigParam{
@ -76,61 +55,19 @@ func createConfigClientTest() *ConfigClient {
return client return client
} }
func createConfigClientTestTls() *ConfigClient {
nc := nacos_client.NacosClient{}
_ = nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
_ = nc.SetClientConfig(*clientTLsConfigWithOptions)
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewConfigClient(&nc)
client.configProxy = &MockConfigProxy{}
return client
}
func createConfigClientCommon() *ConfigClient {
nc := nacos_client.NacosClient{}
_ = nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
_ = nc.SetClientConfig(*clientConfigWithOptions)
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewConfigClient(&nc)
client.configProxy = &MockConfigProxy{}
return client
}
func createConfigClientForKms() *ConfigClient {
nc := nacos_client.NacosClient{}
_ = nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
_ = nc.SetClientConfig(*clientConfigWithOptions)
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewConfigClient(&nc)
client.configProxy = &MockConfigProxyForUsingLocalDiskCache{}
return client
}
type MockConfigProxyForUsingLocalDiskCache struct {
MockConfigProxy
}
func (m *MockConfigProxyForUsingLocalDiskCache) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
return nil, errors.New("mock err for using localCache")
}
type MockConfigProxy struct { type MockConfigProxy struct {
} }
func (m *MockConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) { func (m *MockConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
cacheKey := util.GetConfigCacheKey(dataId, group, tenant) return &rpc_response.ConfigQueryResponse{Content: "hello world"}, nil
if IsLimited(cacheKey) {
return nil, errors.New("request is limited")
}
return &rpc_response.ConfigQueryResponse{Content: "hello world", Response: &rpc_response.Response{Success: true}}, nil
} }
func (m *MockConfigProxy) searchConfigProxy(param vo.SearchConfigParam, tenant, accessKey, secretKey string) (*model.ConfigPage, error) { func (m *MockConfigProxy) searchConfigProxy(param vo.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error) {
return &model.ConfigPage{TotalCount: 1}, nil return &model.ConfigPage{TotalCount: 1}, nil
} }
func (m *MockConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) { func (m *MockConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) {
return &rpc_response.MockResponse{Response: &rpc_response.Response{Success: true}}, nil return &rpc_response.MockResponse{Response: &rpc_response.Response{Success: true}}, nil
} }
func (m *MockConfigProxy) createRpcClient(ctx context.Context, taskId string, client *ConfigClient) *rpc.RpcClient { func (m *MockConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
return &rpc.RpcClient{} return &rpc.RpcClient{}
} }
func (m *MockConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient { func (m *MockConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
@ -149,7 +86,7 @@ func Test_GetConfig(t *testing.T) {
content, err := client.GetConfig(vo.ConfigParam{ content, err := client.GetConfig(vo.ConfigParam{
DataId: localConfigTest.DataId, DataId: localConfigTest.DataId,
Group: localConfigTest.Group}) Group: "group"})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "hello world", content) assert.Equal(t, "hello world", content)
@ -161,7 +98,7 @@ func Test_SearchConfig(t *testing.T) {
DataId: localConfigTest.DataId, DataId: localConfigTest.DataId,
Group: "DEFAULT_GROUP", Group: "DEFAULT_GROUP",
Content: "hello world"}) Content: "hello world"})
configPage, err := client.SearchConfig(vo.SearchConfigParam{ configPage, err := client.SearchConfig(vo.SearchConfigParm{
Search: "accurate", Search: "accurate",
DataId: localConfigTest.DataId, DataId: localConfigTest.DataId,
Group: "DEFAULT_GROUP", Group: "DEFAULT_GROUP",
@ -172,74 +109,6 @@ func Test_SearchConfig(t *testing.T) {
assert.NotEmpty(t, configPage) assert.NotEmpty(t, configPage)
} }
func Test_GetConfigTls(t *testing.T) {
client := createConfigClientTestTls()
_, _ = client.PublishConfig(vo.ConfigParam{
DataId: localConfigTest.DataId,
Group: "DEFAULT_GROUP",
Content: "hello world"})
configPage, err := client.SearchConfig(vo.SearchConfigParam{
Search: "accurate",
DataId: localConfigTest.DataId,
Group: "DEFAULT_GROUP",
PageNo: 1,
PageSize: 10,
})
assert.Nil(t, err)
assert.NotEmpty(t, configPage)
}
// only using by ak sk for cipher config of aliyun kms
/*
func TestPublishAndGetConfigByUsingLocalCache(t *testing.T) {
param := vo.ConfigParam{
DataId: "cipher-kms-aes-256-usingCache" + strconv.Itoa(rand.Int()),
Group: "DEFAULT",
Content: "content加密&&" + strconv.Itoa(rand.Int()),
}
t.Run("PublishAndGetConfigByUsingLocalCache", func(t *testing.T) {
commonClient := createConfigClientCommon()
_, err := commonClient.PublishConfig(param)
assert.Nil(t, err)
time.Sleep(2 * time.Second)
configQueryContent, err := commonClient.GetConfig(param)
assert.Nil(t, err)
assert.Equal(t, param.Content, configQueryContent)
usingKmsCacheClient := createConfigClientForKms()
configQueryContentByUsingCache, err := usingKmsCacheClient.GetConfig(param)
assert.Nil(t, err)
assert.Equal(t, param.Content, configQueryContentByUsingCache)
newCipherContent := param.Content + "new"
param.Content = newCipherContent
err = commonClient.ListenConfig(vo.ConfigParam{
DataId: param.DataId,
Group: param.Group,
OnChange: func(namespace, group, dataId, data string) {
t.Log("origin data: " + newCipherContent + "; new data: " + data)
assert.Equal(t, newCipherContent, data)
},
})
assert.Nil(t, err)
result, err := commonClient.PublishConfig(param)
assert.Nil(t, err)
assert.True(t, result)
time.Sleep(2 * time.Second)
newContentCommon, err := commonClient.GetConfig(param)
assert.Nil(t, err)
assert.Equal(t, param.Content, newContentCommon)
newContentKms, err := usingKmsCacheClient.GetConfig(param)
assert.Nil(t, err)
assert.Equal(t, param.Content, newContentKms)
})
}
*/
// PublishConfig // PublishConfig
func Test_PublishConfigWithoutDataId(t *testing.T) { func Test_PublishConfigWithoutDataId(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
@ -268,7 +137,6 @@ func Test_PublishConfig(t *testing.T) {
success, err := client.PublishConfig(vo.ConfigParam{ success, err := client.PublishConfig(vo.ConfigParam{
DataId: localConfigTest.DataId, DataId: localConfigTest.DataId,
Group: "group", Group: "group",
SrcUser: "nacos-client-go",
Content: "hello world"}) Content: "hello world"})
assert.Nil(t, err) assert.Nil(t, err)
@ -357,55 +225,3 @@ func TestCancelListenConfig(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
}) })
} }
type MockAccessKeyCredentialProvider struct {
accessKey string
secretKey string
signatureRegionId string
}
func (provider *MockAccessKeyCredentialProvider) MatchProvider() bool {
return true
}
func (provider *MockAccessKeyCredentialProvider) Init() error {
return nil
}
func (provider *MockAccessKeyCredentialProvider) GetCredentialsForNacosClient() security.RamContext {
ramContext := security.RamContext{
AccessKey: provider.accessKey,
SecretKey: provider.secretKey,
SignatureRegionId: "",
}
return ramContext
}
func Test_ConfigClientWithProvider(t *testing.T) {
nc := nacos_client.NacosClient{}
_ = nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
clientConfigWithOptions.AccessKey = ""
clientConfigWithOptions.SecretKey = ""
_ = nc.SetClientConfig(*clientConfigWithOptions)
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
provider := &MockAccessKeyCredentialProvider{
accessKey: "LTAxxx",
secretKey: "EdPxxx",
}
client, _ := NewConfigClientWithRamCredentialProvider(&nc, provider)
client.configProxy = &MockConfigProxy{}
success, err := client.PublishConfig(vo.ConfigParam{
DataId: localConfigTest.DataId,
Group: localConfigTest.Group,
Content: "hello world"})
assert.Nil(t, err)
assert.True(t, success)
content, err := client.GetConfig(vo.ConfigParam{
DataId: localConfigTest.DataId,
Group: localConfigTest.Group})
assert.Nil(t, err)
assert.Equal(t, "hello world", content)
}

View File

@ -1,64 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 config_client
import (
"strconv"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
)
type ConfigConnectionEventListener struct {
client *ConfigClient
taskId string
}
func NewConfigConnectionEventListener(client *ConfigClient, taskId string) *ConfigConnectionEventListener {
return &ConfigConnectionEventListener{
client: client,
taskId: taskId,
}
}
func (c *ConfigConnectionEventListener) OnConnected() {
logger.Info("[ConfigConnectionEventListener] connect to config server for taskId: " + c.taskId)
if c.client != nil {
c.client.asyncNotifyListenConfig()
}
}
func (c *ConfigConnectionEventListener) OnDisConnect() {
logger.Info("[ConfigConnectionEventListener] disconnect from config server for taskId: " + c.taskId)
if c.client != nil {
taskIdInt, err := strconv.Atoi(c.taskId)
if err != nil {
logger.Errorf("[ConfigConnectionEventListener] parse taskId error: %v", err)
return
}
items := c.client.cacheMap.Items()
for key, v := range items {
if data, ok := v.(cacheData); ok {
if data.taskId == taskIdInt {
data.isSyncWithServer = false
c.client.cacheMap.Set(key, data)
}
}
}
}
}

View File

@ -1,192 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 config_client
import (
"context"
"testing"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/stretchr/testify/assert"
)
func TestNewConfigConnectionEventListener(t *testing.T) {
client := &ConfigClient{}
taskId := "123"
listener := NewConfigConnectionEventListener(client, taskId)
assert.Equal(t, client, listener.client)
assert.Equal(t, taskId, listener.taskId)
}
func TestOnDisConnectWithMock(t *testing.T) {
client := &ConfigClient{
cacheMap: cache.NewConcurrentMap(),
}
data1 := cacheData{
dataId: "dataId1",
group: "group1",
tenant: "",
taskId: 1,
isSyncWithServer: true,
}
data2 := cacheData{
dataId: "dataId2",
group: "group1",
tenant: "",
taskId: 1,
isSyncWithServer: true,
}
data3 := cacheData{
dataId: "dataId3",
group: "group2",
tenant: "",
taskId: 2,
isSyncWithServer: true,
}
key1 := util.GetConfigCacheKey(data1.dataId, data1.group, data1.tenant)
key2 := util.GetConfigCacheKey(data2.dataId, data2.group, data2.tenant)
key3 := util.GetConfigCacheKey(data3.dataId, data3.group, data3.tenant)
client.cacheMap.Set(key1, data1)
client.cacheMap.Set(key2, data2)
client.cacheMap.Set(key3, data3)
listener := NewConfigConnectionEventListener(client, "1")
listener.OnDisConnect()
item1, _ := client.cacheMap.Get(key1)
item2, _ := client.cacheMap.Get(key2)
item3, _ := client.cacheMap.Get(key3)
updatedData1 := item1.(cacheData)
updatedData2 := item2.(cacheData)
updatedData3 := item3.(cacheData)
assert.False(t, updatedData1.isSyncWithServer, "dataId1 should be marked as not sync")
assert.False(t, updatedData2.isSyncWithServer, "dataId2 should be marked as not sync")
assert.True(t, updatedData3.isSyncWithServer, "dataId3 should be marked as sync")
}
func TestOnConnectedWithMock(t *testing.T) {
listenChan := make(chan struct{}, 1)
client := &ConfigClient{
listenExecute: listenChan,
}
listener := NewConfigConnectionEventListener(client, "1")
listener.OnConnected()
time.Sleep(100 * time.Millisecond)
select {
case <-listenChan:
assert.True(t, true, "asyncNotifyListenConfig should be called")
default:
t.Fatalf("asyncNotifyListenConfig should be called but not")
}
}
type MockRpcClientForListener struct {
requestCalled rpc_request.IRequest
}
func (m *MockRpcClientForListener) Request(request rpc_request.IRequest) (rpc_response.IResponse, error) {
m.requestCalled = request
return &rpc_response.ConfigChangeBatchListenResponse{
Response: &rpc_response.Response{
ResultCode: 200,
},
ChangedConfigs: []model.ConfigContext{},
}, nil
}
func TestReconnectionFlow(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mockRpc := &MockRpcClientForListener{}
listenChan := make(chan struct{}, 1)
client := &ConfigClient{
ctx: ctx,
configProxy: &MockConfigProxy{},
cacheMap: cache.NewConcurrentMap(),
listenExecute: listenChan,
}
done := make(chan bool)
go func() {
for {
select {
case <-listenChan:
mockRpc.Request(&rpc_request.ConfigBatchListenRequest{})
done <- true
case <-ctx.Done():
return
}
}
}()
data1 := cacheData{
dataId: "dataId1",
group: "group1",
tenant: "",
taskId: 1,
isSyncWithServer: true,
}
key1 := util.GetConfigCacheKey(data1.dataId, data1.group, data1.tenant)
client.cacheMap.Set(key1, data1)
listener := NewConfigConnectionEventListener(client, "1")
initialData, _ := client.cacheMap.Get(key1)
assert.True(t, initialData.(cacheData).isSyncWithServer, "initial data should be sync with server")
listener.OnDisConnect()
afterDisconnectData, _ := client.cacheMap.Get(key1)
assert.False(t, afterDisconnectData.(cacheData).isSyncWithServer, "disconnect should set isSyncWithServer to false")
listener.OnConnected()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Fatalf("wait for done timeout")
}
assert.NotNil(t, mockRpc.requestCalled, "should call request")
_, ok := mockRpc.requestCalled.(*rpc_request.ConfigBatchListenRequest)
assert.True(t, ok, "should be a ConfigBatchListenRequest")
}

View File

@ -17,26 +17,23 @@
package config_client package config_client
import ( import (
"context"
"encoding/json" "encoding/json"
"errors"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/nacos-group/nacos-sdk-go/v2/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/v2/vo" "github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/pkg/errors"
) )
type ConfigProxy struct { type ConfigProxy struct {
@ -44,25 +41,22 @@ type ConfigProxy struct {
clientConfig constant.ClientConfig clientConfig constant.ClientConfig
} }
func NewConfigProxy(ctx context.Context, serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent) (IConfigProxy, error) { func NewConfigProxy(serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent) (IConfigProxy, error) {
return NewConfigProxyWithRamCredentialProvider(ctx, serverConfig, clientConfig, httpAgent, nil)
}
func NewConfigProxyWithRamCredentialProvider(ctx context.Context, serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent, provider security.RamCredentialProvider) (IConfigProxy, error) {
proxy := ConfigProxy{} proxy := ConfigProxy{}
var err error var err error
proxy.nacosServer, err = nacos_server.NewNacosServerWithRamCredentialProvider(ctx, serverConfig, clientConfig, httpAgent, clientConfig.TimeoutMs, clientConfig.Endpoint, nil, provider) proxy.nacosServer, err = nacos_server.NewNacosServer(serverConfig, clientConfig, httpAgent, clientConfig.TimeoutMs, clientConfig.Endpoint)
proxy.clientConfig = clientConfig proxy.clientConfig = clientConfig
return &proxy, err return &proxy, err
} }
func (cp *ConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) { func (cp *ConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) {
start := time.Now() cp.nacosServer.InjectSecurityInfo(request.GetHeaders())
cp.nacosServer.InjectSecurityInfo(request.GetHeaders(), security.BuildConfigResourceByRequest(request))
cp.injectCommHeader(request.GetHeaders()) cp.injectCommHeader(request.GetHeaders())
response, err := rpcClient.Request(request, int64(timeoutMills)) signHeaders := nacos_server.GetSignHeaders(request.GetHeaders(), cp.clientConfig.SecretKey)
monitor.GetConfigRequestMonitor(constant.GRPC, request.GetRequestType(), rpc_response.GetGrpcResponseStatusCode(response)).Observe(float64(time.Now().Nanosecond() - start.Nanosecond())) request.PutAllHeaders(signHeaders)
return response, err //todo Spas-SecurityToken/Spas-AccessKey.
//todo Config Limiter
return rpcClient.Request(request, int64(timeoutMills))
} }
func (cp *ConfigProxy) injectCommHeader(param map[string]string) { func (cp *ConfigProxy) injectCommHeader(param map[string]string) {
@ -74,7 +68,7 @@ func (cp *ConfigProxy) injectCommHeader(param map[string]string) {
param[constant.CHARSET_KEY] = "utf-8" param[constant.CHARSET_KEY] = "utf-8"
} }
func (cp *ConfigProxy) searchConfigProxy(param vo.SearchConfigParam, tenant, accessKey, secretKey string) (*model.ConfigPage, error) { func (cp *ConfigProxy) searchConfigProxy(param vo.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error) {
params := util.TransformObject2Param(param) params := util.TransformObject2Param(param)
if len(tenant) > 0 { if len(tenant) > 0 {
params["tenant"] = tenant params["tenant"] = tenant
@ -86,27 +80,14 @@ func (cp *ConfigProxy) searchConfigProxy(param vo.SearchConfigParam, tenant, acc
params["dataId"] = "" params["dataId"] = ""
} }
var headers = map[string]string{} var headers = map[string]string{}
var version = "v2" headers["accessKey"] = accessKey
headers["secretKey"] = secretKey
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodGet, cp.clientConfig.TimeoutMs) result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodGet, cp.clientConfig.TimeoutMs)
if err != nil {
if len(tenant) > 0 {
params["namespaceId"] = params["tenant"]
}
params["groupName"] = params["group"]
result, err = cp.nacosServer.ReqConfigApi("/v3/admin/cs/config/list", params, headers, http.MethodGet, cp.clientConfig.TimeoutMs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
version = "v3"
}
var configPage model.ConfigPage var configPage model.ConfigPage
if version == "v2" {
err = json.Unmarshal([]byte(result), &configPage) err = json.Unmarshal([]byte(result), &configPage)
} else {
var configPageResult model.ConfigPageResult
err = json.Unmarshal([]byte(result), &configPageResult)
configPage = configPageResult.Data
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,12 +100,6 @@ func (cp *ConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64,
} }
configQueryRequest := rpc_request.NewConfigQueryRequest(group, dataId, tenant) configQueryRequest := rpc_request.NewConfigQueryRequest(group, dataId, tenant)
configQueryRequest.Headers["notify"] = strconv.FormatBool(notify) configQueryRequest.Headers["notify"] = strconv.FormatBool(notify)
cacheKey := util.GetConfigCacheKey(dataId, group, tenant)
// use the same key of config file as the limit checker's key
if IsLimited(cacheKey) {
// return error when check limited
return nil, errors.New("ConfigQueryRequest is limited")
}
iResponse, err := cp.requestProxy(cp.getRpcClient(client), configQueryRequest, timeout) iResponse, err := cp.requestProxy(cp.getRpcClient(client), configQueryRequest, timeout)
if err != nil { if err != nil {
return nil, err return nil, err
@ -134,8 +109,10 @@ func (cp *ConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64,
return nil, errors.New("ConfigQueryRequest returns type error") return nil, errors.New("ConfigQueryRequest returns type error")
} }
if response.IsSuccess() { if response.IsSuccess() {
//todo LocalConfigInfoProcessor.saveSnapshot
cacheKey := util.GetConfigCacheKey(dataId, group, tenant)
cache.WriteConfigToFile(cacheKey, cp.clientConfig.CacheDir, response.Content) cache.WriteConfigToFile(cacheKey, cp.clientConfig.CacheDir, response.Content)
cache.WriteEncryptedDataKeyToFile(cacheKey, cp.clientConfig.CacheDir, response.EncryptedDataKey) //todo LocalConfigInfoProcessor.saveEncryptDataKeySnapshot
if response.ContentType == "" { if response.ContentType == "" {
response.ContentType = "text" response.ContentType = "text"
} }
@ -143,9 +120,8 @@ func (cp *ConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64,
} }
if response.GetErrorCode() == 300 { if response.GetErrorCode() == 300 {
cache.WriteConfigToFile(cacheKey, cp.clientConfig.CacheDir, "") //todo LocalConfigInfoProcessor.saveSnapshot
cache.WriteEncryptedDataKeyToFile(cacheKey, cp.clientConfig.CacheDir, "") //todo LocalConfigInfoProcessor.saveEncryptDataKeySnapshot
response.SetSuccess(true)
return response, nil return response, nil
} }
@ -157,39 +133,25 @@ func (cp *ConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64,
} }
if response.GetErrorCode() > 0 { if response.GetErrorCode() > 0 {
logger.Errorf("[config_rpc_client] [sub-server-error] dataId=%s, group=%s, tenant=%s, code=%+v", dataId, group, logger.Errorf("[config_rpc_client] [sub-server-error] dataId=%s, group=%s, tenant=%s, code=%v", dataId, group,
tenant, response) tenant, response)
} }
return response, nil return response, nil
} }
func appName(client *ConfigClient) string { func (cp *ConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
if clientConfig, err := client.GetClientConfig(); err == nil {
appName := clientConfig.AppName
return appName
}
return "unknown"
}
func (cp *ConfigProxy) createRpcClient(ctx context.Context, taskId string, client *ConfigClient) *rpc.RpcClient {
labels := map[string]string{ labels := map[string]string{
constant.LABEL_SOURCE: constant.LABEL_SOURCE_SDK, constant.LABEL_SOURCE: constant.LABEL_SOURCE_SDK,
constant.LABEL_MODULE: constant.LABEL_MODULE_CONFIG, constant.LABEL_MODULE: constant.LABEL_MODULE_CONFIG,
constant.APPNAME_HEADER: appName(client),
"taskId": taskId, "taskId": taskId,
} }
iRpcClient, _ := rpc.CreateClient(ctx, "config-"+taskId+"-"+client.uid, rpc.GRPC, labels, cp.nacosServer, &cp.clientConfig.TLSCfg, cp.clientConfig.AppConnLabels) iRpcClient, _ := rpc.CreateClient("config-"+taskId+"-"+client.uid, rpc.GRPC, labels, cp.nacosServer)
rpcClient := iRpcClient.GetRpcClient() rpcClient := iRpcClient.GetRpcClient()
if rpcClient.IsInitialized() { if rpcClient.IsInitialized() {
rpcClient.RegisterServerRequestHandler(func() rpc_request.IRequest { rpcClient.RegisterServerRequestHandler(func() rpc_request.IRequest {
// TODO fix the group/dataId empty problem return &rpc_request.ConfigChangeNotifyRequest{ConfigRequest: rpc_request.NewConfigRequest()}
return rpc_request.NewConfigChangeNotifyRequest("", "", "")
}, &ConfigChangeNotifyRequestHandler{client: client}) }, &ConfigChangeNotifyRequestHandler{client: client})
configListener := NewConfigConnectionEventListener(client, taskId)
rpcClient.RegisterConnectionListener(configListener)
rpcClient.Tenant = cp.clientConfig.NamespaceId rpcClient.Tenant = cp.clientConfig.NamespaceId
rpcClient.Start() rpcClient.Start()
} }
@ -197,36 +159,31 @@ func (cp *ConfigProxy) createRpcClient(ctx context.Context, taskId string, clien
} }
func (cp *ConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient { func (cp *ConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
return cp.createRpcClient(client.ctx, "0", client) return cp.createRpcClient("0", client)
} }
type ConfigChangeNotifyRequestHandler struct { type ConfigChangeNotifyRequestHandler struct {
client *ConfigClient client *ConfigClient
} }
func (c *ConfigChangeNotifyRequestHandler) Name() string {
return "ConfigChangeNotifyRequestHandler"
}
func (c *ConfigChangeNotifyRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *rpc.RpcClient) rpc_response.IResponse { func (c *ConfigChangeNotifyRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *rpc.RpcClient) rpc_response.IResponse {
configChangeNotifyRequest, ok := request.(*rpc_request.ConfigChangeNotifyRequest) configChangeNotifyRequest, ok := request.(*rpc_request.ConfigChangeNotifyRequest)
if !ok { if ok {
return nil logger.Infof("%s [server-push] config changed. dataId=%s, group=%s,tenant=%s", rpcClient.Name,
}
logger.Infof("%s [server-push] config changed. dataId=%s, group=%s,tenant=%s", rpcClient.Name(),
configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group, configChangeNotifyRequest.Tenant) configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group, configChangeNotifyRequest.Tenant)
cacheKey := util.GetConfigCacheKey(configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group, cacheKey := util.GetConfigCacheKey(configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group,
configChangeNotifyRequest.Tenant) configChangeNotifyRequest.Tenant)
data, ok := c.client.cacheMap.Get(cacheKey) cache, ok := c.client.cacheMap.Get(cacheKey)
if !ok { if !ok {
return nil return nil
} }
cData := data.(cacheData) cacheData := cache.(*cacheData)
cData.isSyncWithServer = false cacheData.isSyncWithServer = false
c.client.cacheMap.Set(cacheKey, cData) c.client.notifyListenConfig()
c.client.asyncNotifyListenConfig()
return &rpc_response.NotifySubscriberResponse{ return &rpc_response.NotifySubscriberResponse{
Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS}, Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
} }
}
return nil
} }

View File

@ -1,8 +1,6 @@
package config_client package config_client
import ( import (
"context"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
@ -12,8 +10,8 @@ import (
type IConfigProxy interface { type IConfigProxy interface {
queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error)
searchConfigProxy(param vo.SearchConfigParam, tenant, accessKey, secretKey string) (*model.ConfigPage, error) searchConfigProxy(param vo.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error)
requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error)
createRpcClient(ctx context.Context, taskId string, client *ConfigClient) *rpc.RpcClient createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient
getRpcClient(client *ConfigClient) *rpc.RpcClient getRpcClient(client *ConfigClient) *rpc.RpcClient
} }

View File

@ -1,56 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 config_client
import (
"sync"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"golang.org/x/time/rate"
)
type rateLimiterCheck struct {
rateLimiterCache cache.ConcurrentMap // cache
mux sync.Mutex
}
var checker rateLimiterCheck
func init() {
checker = rateLimiterCheck{
rateLimiterCache: cache.NewConcurrentMap(),
mux: sync.Mutex{},
}
}
// IsLimited return true when request is limited
func IsLimited(checkKey string) bool {
checker.mux.Lock()
defer checker.mux.Unlock()
var limiter *rate.Limiter
lm, exist := checker.rateLimiterCache.Get(checkKey)
if !exist {
// define a new limiter,allow 5 times per second,and reserve stock is 5.
limiter = rate.NewLimiter(rate.Limit(5), 5)
checker.rateLimiterCache.Set(checkKey, limiter)
} else {
limiter = lm.(*rate.Limiter)
}
add := time.Now().Add(time.Second)
return !limiter.AllowN(add, 1)
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 config_client
import (
"testing"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/stretchr/testify/assert"
)
func TestLimiter(t *testing.T) {
client := createConfigClientTest()
success, err := client.PublishConfig(vo.ConfigParam{
DataId: localConfigTest.DataId,
Group: "default-group",
Content: "hello world"})
assert.Nil(t, err)
assert.True(t, success)
for i := 0; i <= 10; i++ {
content, err := client.GetConfig(vo.ConfigParam{
DataId: localConfigTest.DataId,
Group: "default-group"})
if i > 4 {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
assert.Equal(t, "hello world", content)
}
}
}

View File

@ -17,11 +17,11 @@
package nacos_client package nacos_client
import ( import (
"errors"
"log"
"os" "os"
"strconv" "strconv"
"github.com/pkg/errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/file" "github.com/nacos-group/nacos-sdk-go/v2/common/file"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
@ -35,7 +35,7 @@ type NacosClient struct {
serverConfigs []constant.ServerConfig serverConfigs []constant.ServerConfig
} }
// SetClientConfig is use to set nacos client Config //SetClientConfig is use to set nacos client Config
func (client *NacosClient) SetClientConfig(config constant.ClientConfig) (err error) { func (client *NacosClient) SetClientConfig(config constant.ClientConfig) (err error) {
if config.TimeoutMs <= 0 { if config.TimeoutMs <= 0 {
config.TimeoutMs = 10 * 1000 config.TimeoutMs = 10 * 1000
@ -60,14 +60,14 @@ func (client *NacosClient) SetClientConfig(config constant.ClientConfig) (err er
if config.LogDir == "" { if config.LogDir == "" {
config.LogDir = file.GetCurrentPath() + string(os.PathSeparator) + "log" config.LogDir = file.GetCurrentPath() + string(os.PathSeparator) + "log"
} }
log.Printf("[INFO] logDir:<%s> cacheDir:<%s>", config.LogDir, config.CacheDir)
client.clientConfig = config client.clientConfig = config
client.clientConfigValid = true client.clientConfigValid = true
return return
} }
// SetServerConfig is use to set nacos server config //SetServerConfig is use to set nacos server config
func (client *NacosClient) SetServerConfig(configs []constant.ServerConfig) (err error) { func (client *NacosClient) SetServerConfig(configs []constant.ServerConfig) (err error) {
if len(configs) <= 0 { if len(configs) <= 0 {
//it's may be use endpoint to get nacos server address //it's may be use endpoint to get nacos server address
@ -92,7 +92,7 @@ func (client *NacosClient) SetServerConfig(configs []constant.ServerConfig) (err
return return
} }
// GetClientConfig use to get client config //GetClientConfig use to get client config
func (client *NacosClient) GetClientConfig() (config constant.ClientConfig, err error) { func (client *NacosClient) GetClientConfig() (config constant.ClientConfig, err error) {
config = client.clientConfig config = client.clientConfig
if !client.clientConfigValid { if !client.clientConfigValid {
@ -101,7 +101,7 @@ func (client *NacosClient) GetClientConfig() (config constant.ClientConfig, err
return return
} }
// GetServerConfig use to get server config //GetServerConfig use to get server config
func (client *NacosClient) GetServerConfig() (configs []constant.ServerConfig, err error) { func (client *NacosClient) GetServerConfig() (configs []constant.ServerConfig, err error) {
configs = client.serverConfigs configs = client.serverConfigs
if !client.serverConfigsValid { if !client.serverConfigsValid {
@ -110,7 +110,7 @@ func (client *NacosClient) GetServerConfig() (configs []constant.ServerConfig, e
return return
} }
// SetHttpAgent use to set http agent //SetHttpAgent use to set http agent
func (client *NacosClient) SetHttpAgent(agent http_agent.IHttpAgent) (err error) { func (client *NacosClient) SetHttpAgent(agent http_agent.IHttpAgent) (err error) {
if agent == nil { if agent == nil {
err = errors.New("[client.SetHttpAgent] http agent can not be nil") err = errors.New("[client.SetHttpAgent] http agent can not be nil")
@ -120,7 +120,7 @@ func (client *NacosClient) SetHttpAgent(agent http_agent.IHttpAgent) (err error)
return return
} }
// GetHttpAgent use to get http agent //GetHttpAgent use to get http agent
func (client *NacosClient) GetHttpAgent() (agent http_agent.IHttpAgent, err error) { func (client *NacosClient) GetHttpAgent() (agent http_agent.IHttpAgent, err error) {
if client.agent == nil { if client.agent == nil {
err = errors.New("[client.GetHttpAgent] invalid http agent") err = errors.New("[client.GetHttpAgent] invalid http agent")

View File

@ -19,25 +19,20 @@ package naming_cache
import ( import (
"os" "os"
"reflect" "reflect"
"sort"
"strconv"
"strings"
"sync"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
"github.com/nacos-group/nacos-sdk-go/v2/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
) )
type ServiceInfoHolder struct { type ServiceInfoHolder struct {
ServiceInfoMap sync.Map ServiceInfoMap cache.ConcurrentMap
updateCacheWhenEmpty bool updateCacheWhenEmpty bool
cacheDir string cacheDir string
notLoadCacheAtStart bool notLoadCacheAtStart bool
subCallback *SubscribeCallback subCallback *SubscribeCallback
UpdateTimeMap sync.Map UpdateTimeMap cache.ConcurrentMap
} }
func NewServiceInfoHolder(namespace, cacheDir string, updateCacheWhenEmpty, notLoadCacheAtStart bool) *ServiceInfoHolder { func NewServiceInfoHolder(namespace, cacheDir string, updateCacheWhenEmpty, notLoadCacheAtStart bool) *ServiceInfoHolder {
@ -47,8 +42,8 @@ func NewServiceInfoHolder(namespace, cacheDir string, updateCacheWhenEmpty, notL
notLoadCacheAtStart: notLoadCacheAtStart, notLoadCacheAtStart: notLoadCacheAtStart,
cacheDir: cacheDir, cacheDir: cacheDir,
subCallback: NewSubscribeCallback(), subCallback: NewSubscribeCallback(),
UpdateTimeMap: sync.Map{}, UpdateTimeMap: cache.NewConcurrentMap(),
ServiceInfoMap: sync.Map{}, ServiceInfoMap: cache.NewConcurrentMap(),
} }
if !notLoadCacheAtStart { if !notLoadCacheAtStart {
@ -63,7 +58,7 @@ func (s *ServiceInfoHolder) loadCacheFromDisk() {
return return
} }
for k, v := range serviceMap { for k, v := range serviceMap {
s.ServiceInfoMap.Store(k, v) s.ServiceInfoMap.Set(k, v)
} }
} }
@ -75,117 +70,57 @@ func (s *ServiceInfoHolder) ProcessService(service *model.Service) {
if service == nil { if service == nil {
return return
} }
if !s.updateCacheWhenEmpty { cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
oldDomain, ok := s.ServiceInfoMap.Get(cacheKey)
if ok && !s.updateCacheWhenEmpty {
//if instance list is empty,not to update cache //if instance list is empty,not to update cache
if service.Hosts == nil || len(service.Hosts) == 0 { if service.Hosts == nil || len(service.Hosts) == 0 {
logger.Warnf("instance list is empty, updateCacheWhenEmpty is set to false, callback is not triggered. service name:%s", service.Name) logger.Errorf("do not have useful host, ignore it, name:%s", service.Name)
return return
} }
} }
cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
oldDomain, ok := s.ServiceInfoMap.Load(cacheKey)
if ok && oldDomain.(model.Service).LastRefTime >= service.LastRefTime { if ok && oldDomain.(model.Service).LastRefTime >= service.LastRefTime {
logger.Warnf("out of date data received, old-t: %d, new-t: %d", oldDomain.(model.Service).LastRefTime, service.LastRefTime) logger.Warnf("out of date data received, old-t: %d, new-t: %d", oldDomain.(model.Service).LastRefTime, service.LastRefTime)
return return
} }
s.UpdateTimeMap.Store(cacheKey, uint64(util.CurrentMillis())) s.UpdateTimeMap.Set(cacheKey, uint64(util.CurrentMillis()))
s.ServiceInfoMap.Store(cacheKey, *service) s.ServiceInfoMap.Set(cacheKey, *service)
if !ok || checkInstanceChanged(oldDomain, *service) { if !ok || ok && !reflect.DeepEqual(service.Hosts, oldDomain.(model.Service).Hosts) {
logger.Infof("service key:%s was updated to:%s", cacheKey, util.ToJsonString(service)) if !ok {
cache.WriteServicesToFile(service, cacheKey, s.cacheDir) logger.Info("service not found in cache " + cacheKey)
s.subCallback.ServiceChanged(cacheKey, service) } else {
logger.Info("service key:%s was updated to:%s", cacheKey, util.ToJsonString(service))
}
cache.WriteServicesToFile(*service, s.cacheDir)
s.subCallback.ServiceChanged(service)
} }
var count int
s.ServiceInfoMap.Range(func(key, value interface{}) bool {
count++
return true
})
monitor.GetServiceInfoMapSizeMonitor().Set(float64(count))
} }
func (s *ServiceInfoHolder) GetServiceInfo(serviceName, groupName, clusters string) (model.Service, bool) { func (s *ServiceInfoHolder) GetServiceInfo(serviceName, groupName, clusters string) (model.Service, bool) {
cacheKey := util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters) cacheKey := util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters)
//todo FailoverReactor //todo FailoverReactor
service, ok := s.ServiceInfoMap.Load(cacheKey) service, ok := s.ServiceInfoMap.Get(cacheKey)
if ok { if ok {
return service.(model.Service), ok return service.(model.Service), ok
} }
return model.Service{}, ok return model.Service{}, ok
} }
func (s *ServiceInfoHolder) RegisterCallback(serviceName string, clusters string, callbackWrapper *SubscribeCallbackFuncWrapper) { func (s *ServiceInfoHolder) RegisterCallback(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
s.subCallback.AddCallbackFunc(serviceName, clusters, callbackWrapper) s.subCallback.AddCallbackFunc(serviceName, clusters, callbackFunc)
} }
func (s *ServiceInfoHolder) DeregisterCallback(serviceName string, clusters string, callbackWrapper *SubscribeCallbackFuncWrapper) { func (s *ServiceInfoHolder) DeregisterCallback(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
s.subCallback.RemoveCallbackFunc(serviceName, clusters, callbackWrapper) s.subCallback.RemoveCallbackFunc(serviceName, clusters, callbackFunc)
} }
func (s *ServiceInfoHolder) StopUpdateIfContain(serviceName, clusters string) { func (s *ServiceInfoHolder) StopUpdateIfContain(serviceName, clusters string) {
cacheKey := util.GetServiceCacheKey(serviceName, clusters) cacheKey := util.GetServiceCacheKey(serviceName, clusters)
s.ServiceInfoMap.Delete(cacheKey) s.ServiceInfoMap.Remove(cacheKey)
} }
func (s *ServiceInfoHolder) IsSubscribed(serviceName, clusters string) bool { func (s *ServiceInfoHolder) IsSubscribed(serviceName, clusters string) bool {
return s.subCallback.IsSubscribed(serviceName, clusters) return s.subCallback.IsSubscribed(serviceName, clusters)
} }
func checkInstanceChanged(oldDomain interface{}, service model.Service) bool {
if oldDomain == nil {
return true
}
oldService := oldDomain.(model.Service)
return isServiceInstanceChanged(oldService, service)
}
// return true when service instance changed ,otherwise return false.
func isServiceInstanceChanged(oldService, newService model.Service) bool {
oldHostsLen := len(oldService.Hosts)
newHostsLen := len(newService.Hosts)
if oldHostsLen != newHostsLen {
return true
}
// compare refTime
oldRefTime := oldService.LastRefTime
newRefTime := newService.LastRefTime
if oldRefTime > newRefTime {
logger.Warnf("out of date data received, old-t: %v , new-t: %v", oldRefTime, newRefTime)
return false
}
// sort instance list
oldInstance := oldService.Hosts
newInstance := make([]model.Instance, len(newService.Hosts))
copy(newInstance, newService.Hosts)
sortInstance(oldInstance)
sortInstance(newInstance)
return !reflect.DeepEqual(oldInstance, newInstance)
}
type instanceSorter []model.Instance
func (s instanceSorter) Len() int {
return len(s)
}
func (s instanceSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s instanceSorter) Less(i, j int) bool {
insI, insJ := s[i], s[j]
// using ip and port to sort
ipNum1, _ := strconv.Atoi(strings.ReplaceAll(insI.Ip, ".", ""))
ipNum2, _ := strconv.Atoi(strings.ReplaceAll(insJ.Ip, ".", ""))
if ipNum1 < ipNum2 {
return true
}
if insI.Port < insJ.Port {
return true
}
return false
}
// sort instances
func sortInstance(instances []model.Instance) {
sort.Sort(instanceSorter(instances))
}

View File

@ -1,152 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 naming_cache
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/stretchr/testify/assert"
)
func TestServiceInfoHolder_isServiceInstanceChanged(t *testing.T) {
rand.Seed(time.Now().Unix())
defaultIp := createRandomIp()
defaultPort := creatRandomPort()
serviceA := model.Service{
LastRefTime: 1000,
Hosts: []model.Instance{
{
Ip: defaultIp,
Port: defaultPort,
},
{
Ip: defaultIp,
Port: defaultPort + 1,
},
{
Ip: defaultIp,
Port: defaultPort + 2,
},
},
}
serviceB := model.Service{
LastRefTime: 1001,
Hosts: []model.Instance{
{
Ip: defaultIp,
Port: defaultPort,
},
{
Ip: defaultIp,
Port: defaultPort + 3,
},
{
Ip: defaultIp,
Port: defaultPort + 4,
},
},
}
ip := createRandomIp()
serviceC := model.Service{
LastRefTime: 1001,
Hosts: []model.Instance{
{
Ip: ip,
Port: defaultPort,
},
{
Ip: ip,
Port: defaultPort + 3,
},
{
Ip: ip,
Port: defaultPort + 4,
},
},
}
t.Run("compareWithSelf", func(t *testing.T) {
changed := isServiceInstanceChanged(serviceA, serviceA)
assert.Equal(t, false, changed)
})
// compareWithIp
t.Run("compareWithIp", func(t *testing.T) {
changed := isServiceInstanceChanged(serviceA, serviceC)
assert.Equal(t, true, changed)
})
// compareWithPort
t.Run("compareWithPort", func(t *testing.T) {
changed := isServiceInstanceChanged(serviceA, serviceB)
assert.Equal(t, true, changed)
})
}
func TestHostReactor_isServiceInstanceChangedWithUnOrdered(t *testing.T) {
rand.Seed(time.Now().Unix())
serviceA := model.Service{
LastRefTime: 1001,
Hosts: []model.Instance{
{
Ip: createRandomIp(),
Port: creatRandomPort(),
},
{
Ip: createRandomIp(),
Port: creatRandomPort(),
},
{
Ip: createRandomIp(),
Port: creatRandomPort(),
},
},
}
serviceB := model.Service{
LastRefTime: 1001,
Hosts: []model.Instance{
{
Ip: createRandomIp(),
Port: creatRandomPort(),
},
{
Ip: createRandomIp(),
Port: creatRandomPort(),
},
{
Ip: createRandomIp(),
Port: creatRandomPort(),
},
},
}
logger.Info("serviceA:%s and serviceB:%s are comparing", serviceA.Hosts, serviceB.Hosts)
changed := isServiceInstanceChanged(serviceA, serviceB)
assert.True(t, changed)
}
// create random ip addr
func createRandomIp() string {
ip := fmt.Sprintf("%d.%d.%d.%d", rand.Intn(255), rand.Intn(255), rand.Intn(255), rand.Intn(255))
return ip
}
func creatRandomPort() uint64 {
return rand.Uint64()
}

View File

@ -17,6 +17,7 @@
package naming_cache package naming_cache
import ( import (
"errors"
"sync" "sync"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
@ -36,34 +37,32 @@ func NewSubscribeCallback() *SubscribeCallback {
func (ed *SubscribeCallback) IsSubscribed(serviceName, clusters string) bool { func (ed *SubscribeCallback) IsSubscribed(serviceName, clusters string) bool {
key := util.GetServiceCacheKey(serviceName, clusters) key := util.GetServiceCacheKey(serviceName, clusters)
funcs, ok := ed.callbackFuncMap.Get(key) _, ok := ed.callbackFuncMap.Get(key)
if ok { return ok
return len(funcs.([]*SubscribeCallbackFuncWrapper)) > 0
}
return false
} }
func (ed *SubscribeCallback) AddCallbackFunc(serviceName string, clusters string, callbackWrapper *SubscribeCallbackFuncWrapper) { func (ed *SubscribeCallback) AddCallbackFunc(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
logger.Info("adding " + serviceName + " with " + clusters + " to listener map")
key := util.GetServiceCacheKey(serviceName, clusters) key := util.GetServiceCacheKey(serviceName, clusters)
ed.mux.Lock()
defer ed.mux.Unlock() defer ed.mux.Unlock()
var funcSlice []*SubscribeCallbackFuncWrapper ed.mux.Lock()
var funcSlice []*func(services []model.Instance, err error)
old, ok := ed.callbackFuncMap.Get(key) old, ok := ed.callbackFuncMap.Get(key)
if ok { if ok {
funcSlice = append(funcSlice, old.([]*SubscribeCallbackFuncWrapper)...) funcSlice = append(funcSlice, old.([]*func(services []model.Instance, err error))...)
} }
funcSlice = append(funcSlice, callbackWrapper) funcSlice = append(funcSlice, callbackFunc)
ed.callbackFuncMap.Set(key, funcSlice) ed.callbackFuncMap.Set(key, funcSlice)
} }
func (ed *SubscribeCallback) RemoveCallbackFunc(serviceName string, clusters string, callbackWrapper *SubscribeCallbackFuncWrapper) { func (ed *SubscribeCallback) RemoveCallbackFunc(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
logger.Info("removing " + serviceName + " with " + clusters + " to listener map") logger.Info("removing " + serviceName + " with " + clusters + " to listener map")
key := util.GetServiceCacheKey(serviceName, clusters) key := util.GetServiceCacheKey(serviceName, clusters)
funcs, ok := ed.callbackFuncMap.Get(key) funcs, ok := ed.callbackFuncMap.Get(key)
if ok && funcs != nil { if ok && funcs != nil {
var newFuncs []*SubscribeCallbackFuncWrapper var newFuncs []*func(services []model.Instance, err error)
for _, funcItem := range funcs.([]*SubscribeCallbackFuncWrapper) { for _, funcItem := range funcs.([]*func(services []model.Instance, err error)) {
if funcItem.CallbackFunc != callbackWrapper.CallbackFunc || !funcItem.Selector.Equals(callbackWrapper.Selector) { if funcItem != callbackFunc {
newFuncs = append(newFuncs, funcItem) newFuncs = append(newFuncs, funcItem)
} }
} }
@ -72,11 +71,19 @@ func (ed *SubscribeCallback) RemoveCallbackFunc(serviceName string, clusters str
} }
func (ed *SubscribeCallback) ServiceChanged(cacheKey string, service *model.Service) { func (ed *SubscribeCallback) ServiceChanged(service *model.Service) {
funcs, ok := ed.callbackFuncMap.Get(cacheKey) if service == nil || service.Name == "" {
return
}
key := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
funcs, ok := ed.callbackFuncMap.Get(key)
if ok { if ok {
for _, funcItem := range funcs.([]*SubscribeCallbackFuncWrapper) { for _, funcItem := range funcs.([]*func(services []model.Instance, err error)) {
funcItem.notifyListener(service) if len(service.Hosts) == 0 {
(*funcItem)(service.Hosts, errors.New("[client.Subscribe] subscribe failed,hosts is empty"))
continue
}
(*funcItem)(service.Hosts, nil)
} }
} }
} }

View File

@ -58,15 +58,13 @@ func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
fmt.Println(util.ToJsonString(ed.callbackFuncMap)) fmt.Println(util.ToJsonString(ed.callbackFuncMap))
}, },
} }
clusterSelector := NewClusterSelector(param.Clusters) ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
callbackWrapper := NewSubscribeCallbackFuncWrapper(clusterSelector, &param.SubscribeCallback)
ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), callbackWrapper)
key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ",")) key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
for k, v := range ed.callbackFuncMap.Items() { for k, v := range ed.callbackFuncMap.Items() {
assert.Equal(t, key, k, "key should be equal!") assert.Equal(t, key, k, "key should be equal!")
funcs := v.([]*SubscribeCallbackFuncWrapper) funcs := v.([]*func(services []model.Instance, err error))
assert.Equal(t, len(funcs), 1) assert.Equal(t, len(funcs), 1)
assert.Equal(t, funcs[0].CallbackFunc, &param.SubscribeCallback, "callback function must be equal!") assert.Equal(t, funcs[0], &param.SubscribeCallback, "callback function must be equal!")
} }
} }
@ -100,9 +98,7 @@ func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
fmt.Printf("func1:%s \n", util.ToJsonString(services)) fmt.Printf("func1:%s \n", util.ToJsonString(services))
}, },
} }
clusterSelector := NewClusterSelector(param.Clusters) ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
callbackWrapper := NewSubscribeCallbackFuncWrapper(clusterSelector, &param.SubscribeCallback)
ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), callbackWrapper)
assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 1") assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 1")
param2 := vo.SubscribeParam{ param2 := vo.SubscribeParam{
@ -113,23 +109,21 @@ func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
fmt.Printf("func2:%s \n", util.ToJsonString(services)) fmt.Printf("func2:%s \n", util.ToJsonString(services))
}, },
} }
clusterSelector2 := NewClusterSelector(param2.Clusters) ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback)
callbackWrapper2 := NewSubscribeCallbackFuncWrapper(clusterSelector2, &param2.SubscribeCallback)
ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), callbackWrapper2)
assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 2") assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 2")
for k, v := range ed.callbackFuncMap.Items() { for k, v := range ed.callbackFuncMap.Items() {
log.Printf("key:%s,%d", k, len(v.([]*SubscribeCallbackFuncWrapper))) log.Printf("key:%s,%d", k, len(v.([]*func(services []model.Instance, err error))))
} }
ed.RemoveCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), callbackWrapper2) ed.RemoveCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback)
key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ",")) key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
for k, v := range ed.callbackFuncMap.Items() { for k, v := range ed.callbackFuncMap.Items() {
assert.Equal(t, key, k, "key should be equal!") assert.Equal(t, key, k, "key should be equal!")
funcs := v.([]*SubscribeCallbackFuncWrapper) funcs := v.([]*func(services []model.Instance, err error))
assert.Equal(t, len(funcs), 1) assert.Equal(t, len(funcs), 1)
assert.Equal(t, funcs[0].CallbackFunc, &param.SubscribeCallback, "callback function must be equal!") assert.Equal(t, funcs[0], &param.SubscribeCallback, "callback function must be equal!")
} }
} }
@ -164,9 +158,7 @@ func TestSubscribeCallback_ServiceChanged(t *testing.T) {
log.Printf("func1:%s \n", util.ToJsonString(services)) log.Printf("func1:%s \n", util.ToJsonString(services))
}, },
} }
clusterSelector := NewClusterSelector(param.Clusters) ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
callbackWrapper := NewSubscribeCallbackFuncWrapper(clusterSelector, &param.SubscribeCallback)
ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), callbackWrapper)
param2 := vo.SubscribeParam{ param2 := vo.SubscribeParam{
ServiceName: "Test", ServiceName: "Test",
@ -177,54 +169,7 @@ func TestSubscribeCallback_ServiceChanged(t *testing.T) {
}, },
} }
clusterSelector2 := NewClusterSelector(param2.Clusters) ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback)
callbackWrapper2 := NewSubscribeCallbackFuncWrapper(clusterSelector2, &param2.SubscribeCallback)
ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), callbackWrapper2) ed.ServiceChanged(&service)
cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
ed.ServiceChanged(cacheKey, &service)
}
func TestSubscribeCallback_RemoveCallbackFunc(t *testing.T) {
ed := NewSubscribeCallback()
serviceName := "Test"
clusters := "default"
groupName := "public"
callback1 := func(services []model.Instance, err error) {
log.Printf("callback1:%s \n", util.ToJsonString(services))
}
clusterSelector1 := NewClusterSelector([]string{clusters})
callbackWrapper1 := NewSubscribeCallbackFuncWrapper(clusterSelector1, &callback1)
callback2 := func(services []model.Instance, err error) {
log.Printf("callback2:%s \n", util.ToJsonString(services))
}
clusterSelector2 := NewClusterSelector([]string{clusters})
callbackWrapper2 := NewSubscribeCallbackFuncWrapper(clusterSelector2, &callback2)
// Add both callbacks
ed.AddCallbackFunc(util.GetGroupName(serviceName, groupName), clusters, callbackWrapper1)
ed.AddCallbackFunc(util.GetGroupName(serviceName, groupName), clusters, callbackWrapper2)
assert.True(t, ed.IsSubscribed(util.GetGroupName(serviceName, groupName), clusters))
// Remove the first callback
ed.RemoveCallbackFunc(util.GetGroupName(serviceName, groupName), clusters, callbackWrapper1)
// Check if only the second callback remains
cacheKey := util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters)
funcs, ok := ed.callbackFuncMap.Get(cacheKey)
if !ok || len(funcs.([]*SubscribeCallbackFuncWrapper)) != 1 {
t.Errorf("Expected 1 callback function, got %d", len(funcs.([]*SubscribeCallbackFuncWrapper)))
}
assert.True(t, ed.IsSubscribed(util.GetGroupName(serviceName, groupName), clusters))
// Remove the second callback
ed.RemoveCallbackFunc(util.GetGroupName(serviceName, groupName), clusters, callbackWrapper2)
// Check if no callbacks remain
funcs, ok = ed.callbackFuncMap.Get(cacheKey)
if ok && len(funcs.([]*SubscribeCallbackFuncWrapper)) != 0 {
t.Errorf("Expected 0 callback functions, got %d", len(funcs.([]*func(services []model.Instance, err error))))
}
assert.False(t, ed.IsSubscribed(util.GetGroupName(serviceName, groupName), clusters))
} }

View File

@ -1,106 +0,0 @@
package naming_cache
import (
"sort"
"strings"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
)
type Selector interface {
SelectInstance(service *model.Service) []model.Instance
Equals(o Selector) bool
}
type ClusterSelector struct {
ClusterNames string
Clusters []string
}
func NewClusterSelector(clusters []string) *ClusterSelector {
if len(clusters) == 0 {
return &ClusterSelector{
ClusterNames: "",
Clusters: []string{},
}
}
// 创建副本避免外部修改
clustersCopy := make([]string, len(clusters))
copy(clustersCopy, clusters)
return &ClusterSelector{
ClusterNames: joinCluster(clusters),
Clusters: clustersCopy,
}
}
func NewSubscribeCallbackFuncWrapper(selector Selector, callback *func(services []model.Instance, err error)) *SubscribeCallbackFuncWrapper {
if selector == nil {
panic("selector cannot be nil")
}
if callback == nil {
panic("callback cannot be nil")
}
return &SubscribeCallbackFuncWrapper{
Selector: selector,
CallbackFunc: callback,
}
}
type SubscribeCallbackFuncWrapper struct {
Selector Selector
CallbackFunc *func(services []model.Instance, err error)
}
func (ed *SubscribeCallbackFuncWrapper) notifyListener(service *model.Service) {
instances := ed.Selector.SelectInstance(service)
if ed.CallbackFunc != nil {
(*ed.CallbackFunc)(instances, nil)
}
}
func (cs *ClusterSelector) SelectInstance(service *model.Service) []model.Instance {
var instances []model.Instance
if cs.ClusterNames == "" {
return service.Hosts
}
for _, instance := range service.Hosts {
if util.Contains(cs.Clusters, instance.ClusterName) {
instances = append(instances, instance)
}
}
return instances
}
func (cs *ClusterSelector) Equals(o Selector) bool {
if o == nil {
return false
}
if o, ok := o.(*ClusterSelector); ok {
return cs.ClusterNames == o.ClusterNames
}
return false
}
func joinCluster(cluster []string) string {
// 使用map实现去重
uniqueSet := make(map[string]struct{})
for _, item := range cluster {
if item != "" { // 过滤空字符串类似Java中的isNotEmpty
uniqueSet[item] = struct{}{}
}
}
uniqueSlice := make([]string, 0, len(uniqueSet))
for item := range uniqueSet {
uniqueSlice = append(uniqueSlice, item)
}
sort.Strings(uniqueSlice)
// 使用逗号连接
return strings.Join(uniqueSlice, ",")
}

View File

@ -17,15 +17,11 @@
package naming_client package naming_client
import ( import (
"context"
"math" "math"
"math/rand" "math/rand"
"strings" "strings"
"sync"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client" "github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
@ -41,24 +37,14 @@ import (
// NamingClient ... // NamingClient ...
type NamingClient struct { type NamingClient struct {
nacos_client.INacosClient nacos_client.INacosClient
ctx context.Context
cancel context.CancelFunc
serviceProxy naming_proxy.INamingProxy serviceProxy naming_proxy.INamingProxy
serviceInfoHolder *naming_cache.ServiceInfoHolder serviceInfoHolder *naming_cache.ServiceInfoHolder
isClosed bool
mutex sync.Mutex
} }
// NewNamingClient ... // NewNamingClient ...
func NewNamingClient(nc nacos_client.INacosClient) (*NamingClient, error) { func NewNamingClient(nc nacos_client.INacosClient) (*NamingClient, error) {
return NewNamingClientWithRamCredentialProvider(nc, nil)
}
// NewNamingClientWithRamCredentialProvider ...
func NewNamingClientWithRamCredentialProvider(nc nacos_client.INacosClient, provider security.RamCredentialProvider) (*NamingClient, error) {
ctx, cancel := context.WithCancel(context.Background())
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
naming := &NamingClient{INacosClient: nc, ctx: ctx, cancel: cancel} naming := &NamingClient{INacosClient: nc}
clientConfig, err := nc.GetClientConfig() clientConfig, err := nc.GetClientConfig()
if err != nil { if err != nil {
return naming, err return naming, err
@ -77,19 +63,15 @@ func NewNamingClientWithRamCredentialProvider(nc nacos_client.INacosClient, prov
if err = initLogger(clientConfig); err != nil { if err = initLogger(clientConfig); err != nil {
return naming, err return naming, err
} }
if clientConfig.NamespaceId == "" { if clientConfig.NamespaceId == "" {
clientConfig.NamespaceId = constant.DEFAULT_NAMESPACE_ID clientConfig.NamespaceId = constant.DEFAULT_NAMESPACE_ID
} }
naming.serviceInfoHolder = naming_cache.NewServiceInfoHolder(clientConfig.NamespaceId, clientConfig.CacheDir, naming.serviceInfoHolder = naming_cache.NewServiceInfoHolder(clientConfig.NamespaceId, clientConfig.CacheDir,
clientConfig.UpdateCacheWhenEmpty, clientConfig.NotLoadCacheAtStart) clientConfig.UpdateCacheWhenEmpty, clientConfig.NotLoadCacheAtStart)
naming.serviceProxy, err = NewNamingProxyDelegateWithRamCredentialProvider(ctx, clientConfig, serverConfig, httpAgent, naming.serviceInfoHolder, provider) naming.serviceProxy, err = NewNamingProxyDelegate(clientConfig, serverConfig, httpAgent, naming.serviceInfoHolder)
if clientConfig.AsyncUpdateService { go NewServiceInfoUpdater(naming.serviceInfoHolder, clientConfig.UpdateThreadNum, naming.serviceProxy).asyncUpdateService()
go NewServiceInfoUpdater(ctx, naming.serviceInfoHolder, clientConfig.UpdateThreadNum, naming.serviceProxy).asyncUpdateService()
}
if err != nil { if err != nil {
return naming, err return naming, err
} }
@ -122,37 +104,9 @@ func (sc *NamingClient) RegisterInstance(param vo.RegisterInstanceParam) (bool,
Weight: param.Weight, Weight: param.Weight,
Ephemeral: param.Ephemeral, Ephemeral: param.Ephemeral,
} }
return sc.serviceProxy.RegisterInstance(param.ServiceName, param.GroupName, instance) return sc.serviceProxy.RegisterInstance(param.ServiceName, param.GroupName, instance)
}
func (sc *NamingClient) BatchRegisterInstance(param vo.BatchRegisterInstanceParam) (bool, error) {
if param.ServiceName == "" {
return false, errors.New("serviceName cannot be empty!")
}
if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP
}
if len(param.Instances) == 0 {
return false, errors.New("instances cannot be empty!")
}
var modelInstances []model.Instance
for _, param := range param.Instances {
if !param.Ephemeral {
return false, errors.Errorf("Batch registration does not allow persistent instance registration! instance:%+v", param)
}
modelInstances = append(modelInstances, model.Instance{
Ip: param.Ip,
Port: param.Port,
Metadata: param.Metadata,
ClusterName: param.ClusterName,
Healthy: param.Healthy,
Enable: param.Enable,
Weight: param.Weight,
Ephemeral: param.Ephemeral,
})
}
return sc.serviceProxy.BatchRegisterInstance(param.ServiceName, param.GroupName, modelInstances)
} }
// DeregisterInstance ... // DeregisterInstance ...
@ -201,14 +155,11 @@ func (sc *NamingClient) GetService(param vo.GetServiceParam) (service model.Serv
param.GroupName = constant.DEFAULT_GROUP param.GroupName = constant.DEFAULT_GROUP
} }
var ok bool var ok bool
clusterSelector := naming_cache.NewClusterSelector(param.Clusters)
clusters := strings.Join(param.Clusters, ",") clusters := strings.Join(param.Clusters, ",")
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, "") service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
if !ok { if !ok {
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, "") service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
} }
service.Clusters = clusters
service.Hosts = clusterSelector.SelectInstance(&service)
return service, err return service, err
} }
@ -225,7 +176,7 @@ func (sc *NamingClient) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (mod
param.NameSpace = clientConfig.NamespaceId param.NameSpace = clientConfig.NamespaceId
} }
} }
services, err := sc.serviceProxy.GetServiceList(param.PageNo, param.PageSize, param.GroupName, param.NameSpace, &model.ExpressionSelector{}) services, err := sc.serviceProxy.GetServiceList(param.PageNo, param.PageSize, param.GroupName, &model.ExpressionSelector{})
return services, err return services, err
} }
@ -234,24 +185,21 @@ func (sc *NamingClient) SelectAllInstances(param vo.SelectAllInstancesParam) ([]
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP param.GroupName = constant.DEFAULT_GROUP
} }
clusters := strings.Join(param.Clusters, ",")
var ( var (
service model.Service service model.Service
ok bool ok bool
err error err error
) )
clusterSelector := naming_cache.NewClusterSelector(param.Clusters)
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, "") service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
if !ok { if !ok {
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, "") service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
} }
if err != nil { if err != nil || service.Hosts == nil || len(service.Hosts) == 0 {
return []model.Instance{}, err return []model.Instance{}, err
} }
instances := clusterSelector.SelectInstance(&service) return service.Hosts, err
if instances == nil || len(instances) == 0 {
return []model.Instance{}, err
}
return instances, err
} }
// SelectInstances Get all instance by DataId, Group and Health // SelectInstances Get all instance by DataId, Group and Health
@ -264,15 +212,14 @@ func (sc *NamingClient) SelectInstances(param vo.SelectInstancesParam) ([]model.
ok bool ok bool
err error err error
) )
clusterSelector := naming_cache.NewClusterSelector(param.Clusters) clusters := strings.Join(param.Clusters, ",")
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, "") service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
if !ok { if !ok {
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, "") service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
service.Hosts = clusterSelector.SelectInstance(&service)
return sc.selectInstances(service, param.HealthyOnly) return sc.selectInstances(service, param.HealthyOnly)
} }
@ -282,7 +229,6 @@ func (sc *NamingClient) selectInstances(service model.Service, healthy bool) ([]
} }
hosts := service.Hosts hosts := service.Hosts
var result []model.Instance var result []model.Instance
logger.Infof("select instances with options: [healthy:<%t>], with service:<%s>", healthy, util.GetGroupName(service.Name, service.GroupName))
for _, host := range hosts { for _, host := range hosts {
if host.Healthy == healthy && host.Enable && host.Weight > 0 { if host.Healthy == healthy && host.Enable && host.Weight > 0 {
result = append(result, host) result = append(result, host)
@ -301,15 +247,15 @@ func (sc *NamingClient) SelectOneHealthyInstance(param vo.SelectOneHealthInstanc
ok bool ok bool
err error err error
) )
clusterSelector := naming_cache.NewClusterSelector(param.Clusters) clusters := strings.Join(param.Clusters, ",")
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, "") service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
if !ok { if !ok {
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, "") service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
service.Hosts = clusterSelector.SelectInstance(&service)
return sc.selectOneHealthyInstances(service) return sc.selectOneHealthyInstances(service)
} }
@ -342,40 +288,25 @@ func (sc *NamingClient) Subscribe(param *vo.SubscribeParam) error {
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP param.GroupName = constant.DEFAULT_GROUP
} }
clusterSelector := naming_cache.NewClusterSelector(param.Clusters) clusters := strings.Join(param.Clusters, ",")
callbackWrapper := naming_cache.NewSubscribeCallbackFuncWrapper(clusterSelector, &param.SubscribeCallback) sc.serviceInfoHolder.RegisterCallback(util.GetGroupName(param.ServiceName, param.GroupName), clusters, &param.SubscribeCallback)
sc.serviceInfoHolder.RegisterCallback(util.GetGroupName(param.ServiceName, param.GroupName), "", callbackWrapper) _, err := sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
_, err := sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, "")
return err return err
} }
// Unsubscribe ... // Unsubscribe ...
func (sc *NamingClient) Unsubscribe(param *vo.SubscribeParam) (err error) { func (sc *NamingClient) Unsubscribe(param *vo.SubscribeParam) (err error) {
clusterSelector := naming_cache.NewClusterSelector(param.Clusters) clusters := strings.Join(param.Clusters, ",")
callbackWrapper := naming_cache.NewSubscribeCallbackFuncWrapper(clusterSelector, &param.SubscribeCallback)
serviceFullName := util.GetGroupName(param.ServiceName, param.GroupName) serviceFullName := util.GetGroupName(param.ServiceName, param.GroupName)
sc.serviceInfoHolder.DeregisterCallback(serviceFullName, "", callbackWrapper) sc.serviceInfoHolder.DeregisterCallback(serviceFullName, clusters, &param.SubscribeCallback)
if !sc.serviceInfoHolder.IsSubscribed(serviceFullName, "") { if sc.serviceInfoHolder.IsSubscribed(serviceFullName, clusters) {
err = sc.serviceProxy.Unsubscribe(param.ServiceName, param.GroupName, "") err = sc.serviceProxy.Unsubscribe(param.ServiceName, param.GroupName, clusters)
} }
return err return err
} }
// ServerHealthy ...
func (sc *NamingClient) ServerHealthy() bool {
return sc.serviceProxy.ServerHealthy()
}
// CloseClient ... // CloseClient ...
func (sc *NamingClient) CloseClient() { func (sc *NamingClient) CloseClient() {
sc.mutex.Lock()
defer sc.mutex.Unlock()
if sc.isClosed {
return
}
sc.serviceProxy.CloseClient() sc.serviceProxy.CloseClient()
sc.cancel()
sc.isClosed = true
} }

View File

@ -39,13 +39,6 @@ type INamingClient interface {
// Ephemeral optional // Ephemeral optional
RegisterInstance(param vo.RegisterInstanceParam) (bool, error) RegisterInstance(param vo.RegisterInstanceParam) (bool, error)
// BatchRegisterInstance use to batch register instance
// ClusterName optional,default:DEFAULT
// ServiceName require
// GroupName optional,default:DEFAULT_GROUP
// Instances require,batch register instance list (serviceName, groupName in instances do not need to be set)
BatchRegisterInstance(param vo.BatchRegisterInstanceParam) (bool, error)
// DeregisterInstance use to deregister instance // DeregisterInstance use to deregister instance
// Ip required // Ip required
// Port required // Port required
@ -112,9 +105,6 @@ type INamingClient interface {
// GetAllServicesInfo use to get all service info by page // GetAllServicesInfo use to get all service info by page
GetAllServicesInfo(param vo.GetAllServiceInfoParam) (model.ServiceList, error) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (model.ServiceList, error)
// ServerHealthy use to check the connectivity to server
ServerHealthy() bool
//CloseClient close the GRPC client //CloseClient close the GRPC client
CloseClient() CloseClient()
} }

View File

@ -37,23 +37,17 @@ var clientConfigTest = *constant.NewClientConfig(
var serverConfigTest = *constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos")) var serverConfigTest = *constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos"))
type MockNamingProxy struct { type MockNamingProxy struct {
unsubscribeCalled bool
unsubscribeParams []string // 记录调用参数
} }
func (m *MockNamingProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) { func (m *MockNamingProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
return true, nil return true, nil
} }
func (m *MockNamingProxy) BatchRegisterInstance(serviceName string, groupName string, instances []model.Instance) (bool, error) {
return true, nil
}
func (m *MockNamingProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) { func (m *MockNamingProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
return true, nil return true, nil
} }
func (m *MockNamingProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName, namespaceId string, selector *model.ExpressionSelector) (model.ServiceList, error) { func (m *MockNamingProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
return model.ServiceList{Doms: []string{""}}, nil return model.ServiceList{Doms: []string{""}}, nil
} }
@ -70,8 +64,6 @@ func (m *MockNamingProxy) Subscribe(serviceName, groupName, clusters string) (mo
} }
func (m *MockNamingProxy) Unsubscribe(serviceName, groupName, clusters string) error { func (m *MockNamingProxy) Unsubscribe(serviceName, groupName, clusters string) error {
m.unsubscribeCalled = true
m.unsubscribeParams = []string{serviceName, groupName, clusters}
return nil return nil
} }
@ -456,108 +448,3 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
} }
} }
func TestNamingClient_Unsubscribe_WithCallback_ShouldNotCallServiceProxyUnsubscribe(t *testing.T) {
// 创建一个带有回调函数的订阅参数
callback := func(services []model.Instance, err error) {
// 空回调函数
}
param := &vo.SubscribeParam{
ServiceName: "test-service",
GroupName: "test-group",
Clusters: []string{"test-cluster"},
SubscribeCallback: callback,
}
// 创建测试客户端
client := NewTestNamingClient()
mockProxy := client.serviceProxy.(*MockNamingProxy)
// 执行 Unsubscribe
err := client.Unsubscribe(param)
// 验证没有错误
assert.Nil(t, err)
assert.True(t, mockProxy.unsubscribeCalled)
}
func TestNamingClient_Unsubscribe_WithoutCallback_ShouldCallServiceProxyUnsubscribe(t *testing.T) {
// 创建一个没有回调函数的订阅参数
param := &vo.SubscribeParam{
ServiceName: "test-service",
GroupName: "test-group",
Clusters: []string{"test-cluster"},
// SubscribeCallback 为 nil
}
// 创建测试客户端
client := NewTestNamingClient()
// 获取原始的 MockNamingProxy 来检查调用状态
mockProxy := client.serviceProxy.(*MockNamingProxy)
// 执行 Unsubscribe
err := client.Unsubscribe(param)
// 验证没有错误
assert.Nil(t, err)
assert.True(t, mockProxy.unsubscribeCalled)
}
// TestNamingClient_Unsubscribe_Integration_Test 集成测试,使用真实的 ServiceInfoHolder 来测试修复后的逻辑
func TestNamingClient_Unsubscribe_Integration_Test(t *testing.T) {
// 创建测试客户端
client := NewTestNamingClient()
// 获取原始的 MockNamingProxy 来检查调用状态
mockProxy := client.serviceProxy.(*MockNamingProxy)
// 创建回调函数
callback1 := func(services []model.Instance, err error) {
// 回调函数1
}
callback2 := func(services []model.Instance, err error) {
// 回调函数2
}
// 测试场景1先注册两个回调函数然后取消订阅第一个
// 这种情况下,取消订阅第一个回调函数后,还有其他回调函数,所以不应该调用 serviceProxy.Unsubscribe
// 注册第一个回调函数
param1 := &vo.SubscribeParam{
ServiceName: "test-service",
GroupName: "test-group",
Clusters: []string{"test-cluster"},
SubscribeCallback: callback1,
}
// 注册第二个回调函数
param2 := &vo.SubscribeParam{
ServiceName: "test-service",
GroupName: "test-group",
Clusters: []string{"test-cluster"},
SubscribeCallback: callback2,
}
// 先注册两个回调函数
err := client.Subscribe(param1)
assert.Nil(t, err)
err = client.Subscribe(param2)
assert.Nil(t, err)
// 重置 MockNamingProxy 的调用状态
mockProxy.unsubscribeCalled = false
mockProxy.unsubscribeParams = nil
// 取消订阅第一个回调函数
err = client.Unsubscribe(param1)
assert.Nil(t, err)
assert.False(t, mockProxy.unsubscribeCalled)
// 取消订阅第二个回调函数
err = client.Unsubscribe(param2)
assert.Nil(t, err)
assert.True(t, mockProxy.unsubscribeCalled)
}

View File

@ -17,6 +17,7 @@
package naming_grpc package naming_grpc
import ( import (
"reflect"
"strings" "strings"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy"
@ -52,11 +53,6 @@ func (c *ConnectionEventListener) OnDisConnect() {
} }
func (c *ConnectionEventListener) redoSubscribe() { func (c *ConnectionEventListener) redoSubscribe() {
grpcProxy, ok := c.clientProxy.(*NamingGrpcProxy)
if !ok {
logger.Error("redo subscribe clientProxy type error")
return
}
for _, key := range c.subscribes.Keys() { for _, key := range c.subscribes.Keys() {
info := strings.Split(key, constant.SERVICE_INFO_SPLITER) info := strings.Split(key, constant.SERVICE_INFO_SPLITER)
var err error var err error
@ -66,9 +62,15 @@ func (c *ConnectionEventListener) redoSubscribe() {
} else { } else {
service, err = c.clientProxy.Subscribe(info[1], info[0], "") service, err = c.clientProxy.Subscribe(info[1], info[0], "")
} }
if err != nil { if err != nil {
logger.Warnf("redo subscribe service:%s faild:%+v", info[1], err) logger.Warnf("redo subscribe service:%s faild:%+v", info[1], err)
continue return
}
grpcProxy, ok := c.clientProxy.(*NamingGrpcProxy)
if !ok {
return
} }
grpcProxy.serviceInfoHolder.ProcessService(&service) grpcProxy.serviceInfoHolder.ProcessService(&service)
} }
@ -79,31 +81,31 @@ func (c *ConnectionEventListener) redoRegisterEachService() {
info := strings.Split(k, constant.SERVICE_INFO_SPLITER) info := strings.Split(k, constant.SERVICE_INFO_SPLITER)
serviceName := info[1] serviceName := info[1]
groupName := info[0] groupName := info[0]
if instance, ok := v.(model.Instance); ok { instance, ok := v.(model.Instance)
if _, err := c.clientProxy.RegisterInstance(serviceName, groupName, instance); err != nil { if !ok {
logger.Warnf("redo register service:%s faild,instances type not is model.instance", info[1])
return
}
_, err := c.clientProxy.RegisterInstance(serviceName, groupName, instance)
if err != nil {
logger.Warnf("redo register service:%s groupName:%s faild:%s", info[1], info[0], err.Error()) logger.Warnf("redo register service:%s groupName:%s faild:%s", info[1], info[0], err.Error())
continue
}
}
if instances, ok := v.([]model.Instance); ok {
if _, err := c.clientProxy.BatchRegisterInstance(serviceName, groupName, instances); err != nil {
logger.Warnf("redo batch register service:%s groupName:%s faild:%s", info[1], info[0], err.Error())
continue
}
} }
} }
} }
func (c *ConnectionEventListener) CacheInstanceForRedo(serviceName, groupName string, instance model.Instance) { func (c *ConnectionEventListener) CacheInstanceForRedo(serviceName, groupName string, instance model.Instance) {
key := util.GetGroupName(serviceName, groupName) key := util.GetGroupName(serviceName, groupName)
getInstance, ok := c.registeredInstanceCached.Get(key)
if !ok {
logger.Warnf("CacheInstanceForRedo get cache instance is null,key:%s", key)
return
}
if reflect.DeepEqual(getInstance.(model.Instance), instance) {
return
}
c.registeredInstanceCached.Set(key, instance) c.registeredInstanceCached.Set(key, instance)
} }
func (c *ConnectionEventListener) CacheInstancesForRedo(serviceName, groupName string, instances []model.Instance) {
key := util.GetGroupName(serviceName, groupName)
c.registeredInstanceCached.Set(key, instances)
}
func (c *ConnectionEventListener) RemoveInstanceForRedo(serviceName, groupName string, instance model.Instance) { func (c *ConnectionEventListener) RemoveInstanceForRedo(serviceName, groupName string, instance model.Instance) {
key := util.GetGroupName(serviceName, groupName) key := util.GetGroupName(serviceName, groupName)
_, ok := c.registeredInstanceCached.Get(key) _, ok := c.registeredInstanceCached.Get(key)
@ -115,16 +117,11 @@ func (c *ConnectionEventListener) RemoveInstanceForRedo(serviceName, groupName s
func (c *ConnectionEventListener) CacheSubscriberForRedo(fullServiceName, clusters string) { func (c *ConnectionEventListener) CacheSubscriberForRedo(fullServiceName, clusters string) {
key := util.GetServiceCacheKey(fullServiceName, clusters) key := util.GetServiceCacheKey(fullServiceName, clusters)
if !c.IsSubscriberCached(key) { if _, ok := c.subscribes.Get(key); !ok {
c.subscribes.Set(key, struct{}{}) c.subscribes.Set(key, struct{}{})
} }
} }
func (c *ConnectionEventListener) IsSubscriberCached(key string) bool {
_, ok := c.subscribes.Get(key)
return ok
}
func (c *ConnectionEventListener) RemoveSubscriberForRedo(fullServiceName, clusters string) { func (c *ConnectionEventListener) RemoveSubscriberForRedo(fullServiceName, clusters string) {
c.subscribes.Remove(util.GetServiceCacheKey(fullServiceName, clusters)) c.subscribes.Remove(util.GetServiceCacheKey(fullServiceName, clusters))
} }

View File

@ -1,14 +1,14 @@
package naming_grpc package naming_grpc
import ( import (
"testing"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"testing"
) )
func TestRedoSubscribe(t *testing.T) { func TestRedoSubscribe(t *testing.T) {
t.Skip("Skipping test,It failed due to a previous commit and is difficult to modify because of the use of struct type assertions in the code.")
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()

View File

@ -17,18 +17,13 @@
package naming_grpc package naming_grpc
import ( import (
"context"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/nacos-group/nacos-sdk-go/v2/inner/uuid" "github.com/nacos-group/nacos-sdk-go/v2/inner/uuid"
"github.com/nacos-group/nacos-sdk-go/v2/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
@ -44,7 +39,7 @@ type NamingGrpcProxy struct {
} }
// NewNamingGrpcProxy create naming grpc proxy // NewNamingGrpcProxy create naming grpc proxy
func NewNamingGrpcProxy(ctx context.Context, clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer, func NewNamingGrpcProxy(clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer,
serviceInfoHolder *naming_cache.ServiceInfoHolder) (*NamingGrpcProxy, error) { serviceInfoHolder *naming_cache.ServiceInfoHolder) (*NamingGrpcProxy, error) {
srvProxy := NamingGrpcProxy{ srvProxy := NamingGrpcProxy{
clientConfig: clientCfg, clientConfig: clientCfg,
@ -62,7 +57,7 @@ func NewNamingGrpcProxy(ctx context.Context, clientCfg constant.ClientConfig, na
constant.LABEL_MODULE: constant.LABEL_MODULE_NAMING, constant.LABEL_MODULE: constant.LABEL_MODULE_NAMING,
} }
iRpcClient, err := rpc.CreateClient(ctx, uid.String(), rpc.GRPC, labels, srvProxy.nacosServer, &clientCfg.TLSCfg, clientCfg.AppConnLabels) iRpcClient, err := rpc.CreateClient(uid.String(), rpc.GRPC, labels, srvProxy.nacosServer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,33 +78,18 @@ func NewNamingGrpcProxy(ctx context.Context, clientCfg constant.ClientConfig, na
} }
func (proxy *NamingGrpcProxy) requestToServer(request rpc_request.IRequest) (rpc_response.IResponse, error) { func (proxy *NamingGrpcProxy) requestToServer(request rpc_request.IRequest) (rpc_response.IResponse, error) {
start := time.Now() proxy.nacosServer.InjectSecurityInfo(request.GetHeaders())
proxy.nacosServer.InjectSecurityInfo(request.GetHeaders(), security.BuildNamingResourceByRequest(request)) // todo ak/sk
response, err := proxy.rpcClient.GetRpcClient().Request(request, int64(proxy.clientConfig.TimeoutMs)) return proxy.rpcClient.GetRpcClient().Request(request, int64(proxy.clientConfig.TimeoutMs))
monitor.GetNamingRequestMonitor(constant.GRPC, request.GetRequestType(), rpc_response.GetGrpcResponseStatusCode(response)).Observe(float64(time.Now().Nanosecond() - start.Nanosecond()))
return response, err
} }
// RegisterInstance ... // RegisterInstance ...
func (proxy *NamingGrpcProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) { func (proxy *NamingGrpcProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
logger.Infof("register instance namespaceId:<%s>,serviceName:<%s> with instance:<%s>", logger.Infof("instance namespaceId:<%s>,serviceName:<%s> with instance:<%s>",
proxy.clientConfig.NamespaceId, serviceName, util.ToJsonString(instance)) proxy.clientConfig.NamespaceId, serviceName, util.ToJsonString(instance))
proxy.eventListener.CacheInstanceForRedo(serviceName, groupName, instance)
instanceRequest := rpc_request.NewInstanceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, "registerInstance", instance) instanceRequest := rpc_request.NewInstanceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, "registerInstance", instance)
response, err := proxy.requestToServer(instanceRequest) response, err := proxy.requestToServer(instanceRequest)
if err != nil { proxy.eventListener.CacheInstanceForRedo(serviceName, groupName, instance)
return false, err
}
return response.IsSuccess(), err
}
// BatchRegisterInstance ...
func (proxy *NamingGrpcProxy) BatchRegisterInstance(serviceName string, groupName string, instances []model.Instance) (bool, error) {
logger.Infof("batch register instance namespaceId:<%s>,serviceName:<%s> with instance:<%s>",
proxy.clientConfig.NamespaceId, serviceName, util.ToJsonString(instances))
proxy.eventListener.CacheInstancesForRedo(serviceName, groupName, instances)
batchInstanceRequest := rpc_request.NewBatchInstanceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, "batchRegisterInstance", instances)
response, err := proxy.requestToServer(batchInstanceRequest)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -130,7 +110,7 @@ func (proxy *NamingGrpcProxy) DeregisterInstance(serviceName string, groupName s
} }
// GetServiceList ... // GetServiceList ...
func (proxy *NamingGrpcProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName, namespaceId string, selector *model.ExpressionSelector) (model.ServiceList, error) { func (proxy *NamingGrpcProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
var selectorStr string var selectorStr string
if selector != nil { if selector != nil {
switch selector.Type { switch selector.Type {
@ -140,7 +120,7 @@ func (proxy *NamingGrpcProxy) GetServiceList(pageNo uint32, pageSize uint32, gro
break break
} }
} }
response, err := proxy.requestToServer(rpc_request.NewServiceListRequest(namespaceId, "", response, err := proxy.requestToServer(rpc_request.NewServiceListRequest(proxy.clientConfig.NamespaceId, "",
groupName, int(pageNo), int(pageSize), selectorStr)) groupName, int(pageNo), int(pageSize), selectorStr))
if err != nil { if err != nil {
return model.ServiceList{}, err return model.ServiceList{}, err
@ -158,8 +138,8 @@ func (proxy *NamingGrpcProxy) ServerHealthy() bool {
} }
// QueryInstancesOfService ... // QueryInstancesOfService ...
func (proxy *NamingGrpcProxy) QueryInstancesOfService(serviceName, groupName, cluster string, udpPort int, healthyOnly bool) (*model.Service, error) { func (proxy *NamingGrpcProxy) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
response, err := proxy.requestToServer(rpc_request.NewServiceQueryRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, cluster, response, err := proxy.requestToServer(rpc_request.NewServiceQueryRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, clusters,
healthyOnly, udpPort)) healthyOnly, udpPort))
if err != nil { if err != nil {
return nil, err return nil, err
@ -168,14 +148,8 @@ func (proxy *NamingGrpcProxy) QueryInstancesOfService(serviceName, groupName, cl
return &queryServiceResponse.ServiceInfo, nil return &queryServiceResponse.ServiceInfo, nil
} }
func (proxy *NamingGrpcProxy) IsSubscribed(serviceName, groupName string, clusters string) bool {
return proxy.eventListener.IsSubscriberCached(util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters))
}
// Subscribe ... // Subscribe ...
func (proxy *NamingGrpcProxy) Subscribe(serviceName, groupName string, clusters string) (model.Service, error) { func (proxy *NamingGrpcProxy) Subscribe(serviceName, groupName string, clusters string) (model.Service, error) {
logger.Infof("Subscribe Service namespaceId:<%s>, serviceName:<%s>, groupName:<%s>, clusters:<%s>",
proxy.clientConfig.NamespaceId, serviceName, groupName, clusters)
proxy.eventListener.CacheSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters) proxy.eventListener.CacheSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters)
request := rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName, request := rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName,
groupName, clusters, true) groupName, clusters, true)
@ -190,8 +164,6 @@ func (proxy *NamingGrpcProxy) Subscribe(serviceName, groupName string, clusters
// Unsubscribe ... // Unsubscribe ...
func (proxy *NamingGrpcProxy) Unsubscribe(serviceName, groupName, clusters string) error { func (proxy *NamingGrpcProxy) Unsubscribe(serviceName, groupName, clusters string) error {
logger.Infof("Unsubscribe Service namespaceId:<%s>, serviceName:<%s>, groupName:<%s>, clusters:<%s>",
proxy.clientConfig.NamespaceId, serviceName, groupName, clusters)
proxy.eventListener.RemoveSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters) proxy.eventListener.RemoveSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters)
_, err := proxy.requestToServer(rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, _, err := proxy.requestToServer(rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName,
clusters, false)) clusters, false))
@ -199,6 +171,5 @@ func (proxy *NamingGrpcProxy) Unsubscribe(serviceName, groupName, clusters strin
} }
func (proxy *NamingGrpcProxy) CloseClient() { func (proxy *NamingGrpcProxy) CloseClient() {
logger.Info("Close Nacos Go SDK Client...")
proxy.rpcClient.GetRpcClient().Shutdown() proxy.rpcClient.GetRpcClient().Shutdown()
} }

View File

@ -9,10 +9,6 @@ func (m *MockNamingGrpc) RegisterInstance(serviceName string, groupName string,
return true, nil return true, nil
} }
func (m *MockNamingGrpc) BatchRegisterInstance(serviceName string, groupName string, instances []model.Instance) (bool, error) {
return true, nil
}
func (m *MockNamingGrpc) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) { func (m *MockNamingGrpc) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
return true, nil return true, nil
} }
@ -33,8 +29,4 @@ func (m *MockNamingGrpc) Subscribe(serviceName, groupName, clusters string) (mod
return model.Service{}, nil return model.Service{}, nil
} }
func (m *MockNamingGrpc) Unsubscribe(serviceName, groupName, clusters string) error { func (m *MockNamingGrpc) Unsubscribe(serviceName, groupName, clusters string) {}
return nil
}
func (m *MockNamingGrpc) CloseClient() {}

View File

@ -17,18 +17,13 @@
package naming_http package naming_http
import ( import (
"context" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pkg/errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
@ -36,32 +31,28 @@ import (
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server"
"github.com/nacos-group/nacos-sdk-go/v2/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"golang.org/x/sync/semaphore" nsema "github.com/toolkits/concurrent/semaphore"
) )
type BeatReactor struct { type BeatReactor struct {
ctx context.Context
beatMap cache.ConcurrentMap beatMap cache.ConcurrentMap
nacosServer *nacos_server.NacosServer nacosServer *nacos_server.NacosServer
beatThreadCount int beatThreadCount int
beatThreadSemaphore *semaphore.Weighted beatThreadSemaphore *nsema.Semaphore
beatRecordMap cache.ConcurrentMap beatRecordMap cache.ConcurrentMap
clientCfg constant.ClientConfig clientCfg constant.ClientConfig
mux *sync.Mutex
} }
const DefaultBeatThreadNum = 20 const Default_Beat_Thread_Num = 20
func NewBeatReactor(ctx context.Context, clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer) BeatReactor { func NewBeatReactor(clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer) BeatReactor {
br := BeatReactor{} br := BeatReactor{}
br.ctx = ctx
br.beatMap = cache.NewConcurrentMap() br.beatMap = cache.NewConcurrentMap()
br.nacosServer = nacosServer br.nacosServer = nacosServer
br.clientCfg = clientCfg br.clientCfg = clientCfg
br.beatThreadCount = DefaultBeatThreadNum br.beatThreadCount = Default_Beat_Thread_Num
br.beatRecordMap = cache.NewConcurrentMap() br.beatRecordMap = cache.NewConcurrentMap()
br.beatThreadSemaphore = semaphore.NewWeighted(int64(br.beatThreadCount)) br.beatThreadSemaphore = nsema.NewSemaphore(br.beatThreadCount)
br.mux = new(sync.Mutex)
return br return br
} }
@ -69,54 +60,39 @@ func buildKey(serviceName string, ip string, port uint64) string {
return serviceName + constant.NAMING_INSTANCE_ID_SPLITTER + ip + constant.NAMING_INSTANCE_ID_SPLITTER + strconv.Itoa(int(port)) return serviceName + constant.NAMING_INSTANCE_ID_SPLITTER + ip + constant.NAMING_INSTANCE_ID_SPLITTER + strconv.Itoa(int(port))
} }
func (br *BeatReactor) AddBeatInfo(serviceName string, beatInfo *model.BeatInfo) { func (br *BeatReactor) AddBeatInfo(serviceName string, beatInfo model.BeatInfo) {
logger.Infof("adding beat: <%s> to beat map", util.ToJsonString(beatInfo)) logger.Infof("adding beat: <%s> to beat map", util.ToJsonString(beatInfo))
k := buildKey(serviceName, beatInfo.Ip, beatInfo.Port) k := buildKey(serviceName, beatInfo.Ip, beatInfo.Port)
defer br.mux.Unlock() br.beatMap.Set(k, &beatInfo)
br.mux.Lock() go br.sendInstanceBeat(k, &beatInfo)
if data, ok := br.beatMap.Get(k); ok {
beatInfo = data.(*model.BeatInfo)
atomic.StoreInt32(&beatInfo.State, int32(model.StateShutdown))
br.beatMap.Remove(k)
}
br.beatMap.Set(k, beatInfo)
beatInfo.Metadata = util.DeepCopyMap(beatInfo.Metadata)
monitor.GetDom2BeatSizeMonitor().Set(float64(br.beatMap.Count()))
go br.sendInstanceBeat(k, beatInfo)
} }
func (br *BeatReactor) RemoveBeatInfo(serviceName string, ip string, port uint64) { func (br *BeatReactor) RemoveBeatInfo(serviceName string, ip string, port uint64) {
logger.Infof("remove beat: %s@%s:%d from beat map", serviceName, ip, port) logger.Infof("remove beat: %s@%s:%d from beat map", serviceName, ip, port)
k := buildKey(serviceName, ip, port) k := buildKey(serviceName, ip, port)
defer br.mux.Unlock()
br.mux.Lock()
data, exist := br.beatMap.Get(k) data, exist := br.beatMap.Get(k)
if exist { if exist {
beatInfo := data.(*model.BeatInfo) beatInfo := data.(*model.BeatInfo)
atomic.StoreInt32(&beatInfo.State, int32(model.StateShutdown)) atomic.StoreInt32(&beatInfo.State, int32(model.StateShutdown))
} }
monitor.GetDom2BeatSizeMonitor().Set(float64(br.beatMap.Count()))
br.beatMap.Remove(k) br.beatMap.Remove(k)
} }
func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) { func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
t := time.NewTimer(beatInfo.Period)
defer t.Stop()
for { for {
br.beatThreadSemaphore.Acquire(br.ctx, 1) br.beatThreadSemaphore.Acquire()
//如果当前实例注销,则进行停止心跳 //如果当前实例注销,则进行停止心跳
if atomic.LoadInt32(&beatInfo.State) == int32(model.StateShutdown) { if atomic.LoadInt32(&beatInfo.State) == int32(model.StateShutdown) {
logger.Infof("instance[%s] stop heartBeating", k) logger.Infof("instance[%s] stop heartBeating", k)
br.beatThreadSemaphore.Release(1) br.beatThreadSemaphore.Release()
return return
} }
//进行心跳通信 //进行心跳通信
beatInterval, err := br.SendBeat(beatInfo) beatInterval, err := br.SendBeat(*beatInfo)
if err != nil { if err != nil {
logger.Errorf("beat to server return error:%+v", err) logger.Errorf("beat to server return error:%+v", err)
br.beatThreadSemaphore.Release(1) br.beatThreadSemaphore.Release()
t := time.NewTimer(beatInfo.Period) t := time.NewTimer(beatInfo.Period)
<-t.C <-t.C
continue continue
@ -126,17 +102,14 @@ func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
} }
br.beatRecordMap.Set(k, util.CurrentMillis()) br.beatRecordMap.Set(k, util.CurrentMillis())
br.beatThreadSemaphore.Release(1) br.beatThreadSemaphore.Release()
t.Reset(beatInfo.Period)
select { t := time.NewTimer(beatInfo.Period)
case <-t.C: <-t.C
case <-br.ctx.Done():
return
}
} }
} }
func (br *BeatReactor) SendBeat(info *model.BeatInfo) (int64, error) { func (br *BeatReactor) SendBeat(info model.BeatInfo) (int64, error) {
logger.Infof("namespaceId:<%s> sending beat to server:<%s>", logger.Infof("namespaceId:<%s> sending beat to server:<%s>",
br.clientCfg.NamespaceId, util.ToJsonString(info)) br.clientCfg.NamespaceId, util.ToJsonString(info))
params := map[string]string{} params := map[string]string{}
@ -144,7 +117,7 @@ func (br *BeatReactor) SendBeat(info *model.BeatInfo) (int64, error) {
params["serviceName"] = info.ServiceName params["serviceName"] = info.ServiceName
params["beat"] = util.ToJsonString(info) params["beat"] = util.ToJsonString(info)
api := constant.SERVICE_BASE_PATH + "/instance/beat" api := constant.SERVICE_BASE_PATH + "/instance/beat"
result, err := br.nacosServer.ReqApi(api, params, http.MethodPut, br.clientCfg) result, err := br.nacosServer.ReqApi(api, params, http.MethodPut)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -17,7 +17,6 @@
package naming_http package naming_http
import ( import (
"context"
"testing" "testing"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
@ -28,10 +27,10 @@ import (
) )
func TestBeatReactor_AddBeatInfo(t *testing.T) { func TestBeatReactor_AddBeatInfo(t *testing.T) {
br := NewBeatReactor(context.Background(), constant.ClientConfig{}, &nacos_server.NacosServer{}) br := NewBeatReactor(constant.ClientConfig{}, &nacos_server.NacosServer{})
serviceName := "Test" serviceName := "Test"
groupName := "public" groupName := "public"
beatInfo := &model.BeatInfo{ beatInfo := model.BeatInfo{
Ip: "127.0.0.1", Ip: "127.0.0.1",
Port: 8080, Port: 8080,
Metadata: map[string]string{}, Metadata: map[string]string{},
@ -47,10 +46,10 @@ func TestBeatReactor_AddBeatInfo(t *testing.T) {
} }
func TestBeatReactor_RemoveBeatInfo(t *testing.T) { func TestBeatReactor_RemoveBeatInfo(t *testing.T) {
br := NewBeatReactor(context.Background(), constant.ClientConfig{}, &nacos_server.NacosServer{}) br := NewBeatReactor(constant.ClientConfig{}, &nacos_server.NacosServer{})
serviceName := "Test" serviceName := "Test"
groupName := "public" groupName := "public"
beatInfo1 := &model.BeatInfo{ beatInfo1 := model.BeatInfo{
Ip: "127.0.0.1", Ip: "127.0.0.1",
Port: 8080, Port: 8080,
Metadata: map[string]string{}, Metadata: map[string]string{},
@ -59,7 +58,7 @@ func TestBeatReactor_RemoveBeatInfo(t *testing.T) {
Weight: 1, Weight: 1,
} }
br.AddBeatInfo(util.GetGroupName(serviceName, groupName), beatInfo1) br.AddBeatInfo(util.GetGroupName(serviceName, groupName), beatInfo1)
beatInfo2 := &model.BeatInfo{ beatInfo2 := model.BeatInfo{
Ip: "127.0.0.2", Ip: "127.0.0.2",
Port: 8080, Port: 8080,
Metadata: map[string]string{}, Metadata: map[string]string{},

View File

@ -17,13 +17,12 @@
package naming_http package naming_http
import ( import (
"context" "errors"
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
"github.com/pkg/errors"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
@ -43,7 +42,7 @@ type NamingHttpProxy struct {
} }
// NewNamingHttpProxy create naming http proxy // NewNamingHttpProxy create naming http proxy
func NewNamingHttpProxy(ctx context.Context, clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer, func NewNamingHttpProxy(clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer,
serviceInfoHolder *naming_cache.ServiceInfoHolder) (*NamingHttpProxy, error) { serviceInfoHolder *naming_cache.ServiceInfoHolder) (*NamingHttpProxy, error) {
srvProxy := NamingHttpProxy{ srvProxy := NamingHttpProxy{
clientConfig: clientCfg, clientConfig: clientCfg,
@ -51,9 +50,9 @@ func NewNamingHttpProxy(ctx context.Context, clientCfg constant.ClientConfig, na
serviceInfoHolder: serviceInfoHolder, serviceInfoHolder: serviceInfoHolder,
} }
srvProxy.beatReactor = NewBeatReactor(ctx, clientCfg, nacosServer) srvProxy.beatReactor = NewBeatReactor(clientCfg, nacosServer)
NewPushReceiver(ctx, serviceInfoHolder).startServer() NewPushReceiver(serviceInfoHolder).startServer()
return &srvProxy, nil return &srvProxy, nil
} }
@ -76,12 +75,12 @@ func (proxy *NamingHttpProxy) RegisterInstance(serviceName string, groupName str
params["healthy"] = strconv.FormatBool(instance.Healthy) params["healthy"] = strconv.FormatBool(instance.Healthy)
params["metadata"] = util.ToJsonString(instance.Metadata) params["metadata"] = util.ToJsonString(instance.Metadata)
params["ephemeral"] = strconv.FormatBool(instance.Ephemeral) params["ephemeral"] = strconv.FormatBool(instance.Ephemeral)
_, err := proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodPost, proxy.clientConfig) _, err := proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodPost)
if err != nil { if err != nil {
return false, err return false, err
} }
if instance.Ephemeral { if instance.Ephemeral {
beatInfo := &model.BeatInfo{ beatInfo := model.BeatInfo{
Ip: instance.Ip, Ip: instance.Ip,
Port: instance.Port, Port: instance.Port,
Metadata: instance.Metadata, Metadata: instance.Metadata,
@ -96,10 +95,6 @@ func (proxy *NamingHttpProxy) RegisterInstance(serviceName string, groupName str
return true, nil return true, nil
} }
func (proxy *NamingHttpProxy) BatchRegisterInstance(serviceName string, groupName string, instances []model.Instance) (bool, error) {
panic("implement me")
}
// DeregisterInstance ... // DeregisterInstance ...
func (proxy *NamingHttpProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) { func (proxy *NamingHttpProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
serviceName = util.GetGroupName(serviceName, groupName) serviceName = util.GetGroupName(serviceName, groupName)
@ -113,7 +108,7 @@ func (proxy *NamingHttpProxy) DeregisterInstance(serviceName string, groupName s
params["ip"] = instance.Ip params["ip"] = instance.Ip
params["port"] = strconv.Itoa(int(instance.Port)) params["port"] = strconv.Itoa(int(instance.Port))
params["ephemeral"] = strconv.FormatBool(instance.Ephemeral) params["ephemeral"] = strconv.FormatBool(instance.Ephemeral)
_, err := proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodDelete, proxy.clientConfig) _, err := proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodDelete)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -121,9 +116,9 @@ func (proxy *NamingHttpProxy) DeregisterInstance(serviceName string, groupName s
} }
// GetServiceList ... // GetServiceList ...
func (proxy *NamingHttpProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName, namespaceId string, selector *model.ExpressionSelector) (model.ServiceList, error) { func (proxy *NamingHttpProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
params := map[string]string{} params := map[string]string{}
params["namespaceId"] = namespaceId params["namespaceId"] = proxy.clientConfig.NamespaceId
params["groupName"] = groupName params["groupName"] = groupName
params["pageNo"] = strconv.Itoa(int(pageNo)) params["pageNo"] = strconv.Itoa(int(pageNo))
params["pageSize"] = strconv.Itoa(int(pageSize)) params["pageSize"] = strconv.Itoa(int(pageSize))
@ -141,7 +136,7 @@ func (proxy *NamingHttpProxy) GetServiceList(pageNo uint32, pageSize uint32, gro
serviceList := model.ServiceList{} serviceList := model.ServiceList{}
api := constant.SERVICE_BASE_PATH + "/service/list" api := constant.SERVICE_BASE_PATH + "/service/list"
result, err := proxy.nacosServer.ReqApi(api, params, http.MethodGet, proxy.clientConfig) result, err := proxy.nacosServer.ReqApi(api, params, http.MethodGet)
if err != nil { if err != nil {
return serviceList, err return serviceList, err
} }
@ -151,14 +146,14 @@ func (proxy *NamingHttpProxy) GetServiceList(pageNo uint32, pageSize uint32, gro
count, err := jsonparser.GetInt([]byte(result), "count") count, err := jsonparser.GetInt([]byte(result), "count")
if err != nil { if err != nil {
return serviceList, errors.Errorf("namespaceId:<%s> get service list pageNo:<%d> pageSize:<%d> selector:<%s> from <%s> get 'count' from <%s> error:<%+v>", namespaceId, pageNo, pageSize, util.ToJsonString(selector), groupName, result, err) return serviceList, errors.New(fmt.Sprintf("namespaceId:<%s> get service list pageNo:<%d> pageSize:<%d> selector:<%s> from <%s> get 'count' from <%s> error:<%+v>", proxy.clientConfig.NamespaceId, pageNo, pageSize, util.ToJsonString(selector), groupName, result, err))
} }
var doms []string var doms []string
_, err = jsonparser.ArrayEach([]byte(result), func(value []byte, dataType jsonparser.ValueType, offset int, err error) { _, err = jsonparser.ArrayEach([]byte(result), func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
doms = append(doms, string(value)) doms = append(doms, string(value))
}, "doms") }, "doms")
if err != nil { if err != nil {
return serviceList, errors.Errorf("namespaceId:<%s> get service list pageNo:<%d> pageSize:<%d> selector:<%s> from <%s> get 'doms' from <%s> error:<%+v> ", namespaceId, pageNo, pageSize, util.ToJsonString(selector), groupName, result, err) return serviceList, errors.New(fmt.Sprintf("namespaceId:<%s> get service list pageNo:<%d> pageSize:<%d> selector:<%s> from <%s> get 'doms' from <%s> error:<%+v> ", proxy.clientConfig.NamespaceId, pageNo, pageSize, util.ToJsonString(selector), groupName, result, err))
} }
serviceList.Count = count serviceList.Count = count
serviceList.Doms = doms serviceList.Doms = doms
@ -168,7 +163,7 @@ func (proxy *NamingHttpProxy) GetServiceList(pageNo uint32, pageSize uint32, gro
// ServerHealthy ... // ServerHealthy ...
func (proxy *NamingHttpProxy) ServerHealthy() bool { func (proxy *NamingHttpProxy) ServerHealthy() bool {
api := constant.SERVICE_BASE_PATH + "/operator/metrics" api := constant.SERVICE_BASE_PATH + "/operator/metrics"
result, err := proxy.nacosServer.ReqApi(api, map[string]string{}, http.MethodGet, proxy.clientConfig) result, err := proxy.nacosServer.ReqApi(api, map[string]string{}, http.MethodGet)
if err != nil { if err != nil {
logger.Errorf("namespaceId:[%s] sending server healthy failed!,result:%s error:%+v", proxy.clientConfig.NamespaceId, result, err) logger.Errorf("namespaceId:[%s] sending server healthy failed!,result:%s error:%+v", proxy.clientConfig.NamespaceId, result, err)
return false return false
@ -195,7 +190,7 @@ func (proxy *NamingHttpProxy) QueryInstancesOfService(serviceName, groupName, cl
param["healthyOnly"] = strconv.FormatBool(healthyOnly) param["healthyOnly"] = strconv.FormatBool(healthyOnly)
param["clientIP"] = util.LocalIP() param["clientIP"] = util.LocalIP()
api := constant.SERVICE_PATH + "/list" api := constant.SERVICE_PATH + "/list"
result, err := proxy.nacosServer.ReqApi(api, param, http.MethodGet, proxy.clientConfig) result, err := proxy.nacosServer.ReqApi(api, param, http.MethodGet)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,9 +19,8 @@ package naming_http
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"context"
"encoding/json" "encoding/json"
"io" "io/ioutil"
"math/rand" "math/rand"
"net" "net"
"strconv" "strconv"
@ -33,7 +32,6 @@ import (
) )
type PushReceiver struct { type PushReceiver struct {
ctx context.Context
port int port int
host string host string
serviceInfoHolder *naming_cache.ServiceInfoHolder serviceInfoHolder *naming_cache.ServiceInfoHolder
@ -49,11 +47,11 @@ var (
GZIP_MAGIC = []byte("\x1F\x8B") GZIP_MAGIC = []byte("\x1F\x8B")
) )
func NewPushReceiver(ctx context.Context, serviceInfoHolder *naming_cache.ServiceInfoHolder) *PushReceiver { func NewPushReceiver(serviceInfoHolder *naming_cache.ServiceInfoHolder) *PushReceiver {
pr := PushReceiver{ pr := PushReceiver{
ctx: ctx,
serviceInfoHolder: serviceInfoHolder, serviceInfoHolder: serviceInfoHolder,
} }
pr.startServer()
return &pr return &pr
} }
@ -74,18 +72,16 @@ func (us *PushReceiver) tryListen() (*net.UDPConn, bool) {
} }
func (us *PushReceiver) startServer() { func (us *PushReceiver) startServer() {
var ( var conn *net.UDPConn
conn *net.UDPConn
ok bool
)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
port := r.Intn(1000) + 54951 port := r.Intn(1000) + 54951
us.port = port us.port = port
conn, ok = us.tryListen() conn1, ok := us.tryListen()
if ok { if ok {
conn = conn1
logger.Infof("udp server start, port: " + strconv.Itoa(port)) logger.Infof("udp server start, port: " + strconv.Itoa(port))
break break
} }
@ -95,20 +91,11 @@ func (us *PushReceiver) startServer() {
} }
} }
if conn == nil {
return
}
go func() { go func() {
defer conn.Close() defer conn.Close()
for { for {
select {
case <-us.ctx.Done():
return
default:
us.handleClient(conn) us.handleClient(conn)
} }
}
}() }()
} }
@ -168,7 +155,7 @@ func TryDecompressData(data []byte) string {
} }
defer reader.Close() defer reader.Close()
bs, err := io.ReadAll(reader) bs, err := ioutil.ReadAll(reader)
if err != nil { if err != nil {
logger.Errorf("failed to decompress gzip data,err:%+v", err) logger.Errorf("failed to decompress gzip data,err:%+v", err)

View File

@ -24,11 +24,9 @@ import (
type INamingProxy interface { type INamingProxy interface {
RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error)
BatchRegisterInstance(serviceName string, groupName string, instances []model.Instance) (bool, error)
DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error)
GetServiceList(pageNo uint32, pageSize uint32, groupName, namespaceId string, selector *model.ExpressionSelector) (model.ServiceList, error) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error)
ServerHealthy() bool ServerHealthy() bool

View File

@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: clients/naming_client/naming_proxy/proxy_interface.go // Source: proxy_interface.go
// Package naming_proxy is a generated GoMock package. // Package naming_proxy is a generated GoMock package.
package naming_proxy package naming_proxy
@ -34,21 +34,6 @@ func (m *MockINamingProxy) EXPECT() *MockINamingProxyMockRecorder {
return m.recorder return m.recorder
} }
// BatchRegisterInstance mocks base method.
func (m *MockINamingProxy) BatchRegisterInstance(serviceName, groupName string, instances []model.Instance) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BatchRegisterInstance", serviceName, groupName, instances)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BatchRegisterInstance indicates an expected call of BatchRegisterInstance.
func (mr *MockINamingProxyMockRecorder) BatchRegisterInstance(serviceName, groupName, instances interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchRegisterInstance", reflect.TypeOf((*MockINamingProxy)(nil).BatchRegisterInstance), serviceName, groupName, instances)
}
// CloseClient mocks base method. // CloseClient mocks base method.
func (m *MockINamingProxy) CloseClient() { func (m *MockINamingProxy) CloseClient() {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -77,18 +62,18 @@ func (mr *MockINamingProxyMockRecorder) DeregisterInstance(serviceName, groupNam
} }
// GetServiceList mocks base method. // GetServiceList mocks base method.
func (m *MockINamingProxy) GetServiceList(pageNo, pageSize uint32, groupName, namespaceId string, selector *model.ExpressionSelector) (model.ServiceList, error) { func (m *MockINamingProxy) GetServiceList(pageNo, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetServiceList", pageNo, pageSize, groupName, namespaceId, selector) ret := m.ctrl.Call(m, "GetServiceList", pageNo, pageSize, groupName, selector)
ret0, _ := ret[0].(model.ServiceList) ret0, _ := ret[0].(model.ServiceList)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// GetServiceList indicates an expected call of GetServiceList. // GetServiceList indicates an expected call of GetServiceList.
func (mr *MockINamingProxyMockRecorder) GetServiceList(pageNo, pageSize, groupName, namespaceId, selector interface{}) *gomock.Call { func (mr *MockINamingProxyMockRecorder) GetServiceList(pageNo, pageSize, groupName, selector interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceList", reflect.TypeOf((*MockINamingProxy)(nil).GetServiceList), pageNo, pageSize, groupName, namespaceId, selector) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceList", reflect.TypeOf((*MockINamingProxy)(nil).GetServiceList), pageNo, pageSize, groupName, selector)
} }
// QueryInstancesOfService mocks base method. // QueryInstancesOfService mocks base method.

View File

@ -17,10 +17,6 @@
package naming_client package naming_client
import ( import (
"context"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/nacos-group/nacos-sdk-go/v2/inner/uuid"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_grpc" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_grpc"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http"
@ -39,35 +35,20 @@ type NamingProxyDelegate struct {
serviceInfoHolder *naming_cache.ServiceInfoHolder serviceInfoHolder *naming_cache.ServiceInfoHolder
} }
func NewNamingProxyDelegate(ctx context.Context, clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, func NewNamingProxyDelegate(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig,
httpAgent http_agent.IHttpAgent, serviceInfoHolder *naming_cache.ServiceInfoHolder) (naming_proxy.INamingProxy, error) { httpAgent http_agent.IHttpAgent, serviceInfoHolder *naming_cache.ServiceInfoHolder) (naming_proxy.INamingProxy, error) {
return NewNamingProxyDelegateWithRamCredentialProvider(ctx, clientCfg, serverCfgs, httpAgent, serviceInfoHolder, nil)
}
func NewNamingProxyDelegateWithRamCredentialProvider(ctx context.Context, clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, nacosServer, err := nacos_server.NewNacosServer(serverCfgs, clientCfg, httpAgent, clientCfg.TimeoutMs, clientCfg.Endpoint)
httpAgent http_agent.IHttpAgent, serviceInfoHolder *naming_cache.ServiceInfoHolder, provider security.RamCredentialProvider) (naming_proxy.INamingProxy, error) {
uid, err := uuid.NewV4()
if err != nil {
return nil, err
}
namingHeader := map[string][]string{
"Client-Version": {constant.CLIENT_VERSION},
"User-Agent": {constant.CLIENT_VERSION},
"RequestId": {uid.String()},
"Request-Module": {"Naming"},
}
nacosServer, err := nacos_server.NewNacosServerWithRamCredentialProvider(ctx, serverCfgs, clientCfg, httpAgent, clientCfg.TimeoutMs, clientCfg.Endpoint, namingHeader, provider)
if err != nil { if err != nil {
return nil, err return nil, err
} }
httpClientProxy, err := naming_http.NewNamingHttpProxy(ctx, clientCfg, nacosServer, serviceInfoHolder) httpClientProxy, err := naming_http.NewNamingHttpProxy(clientCfg, nacosServer, serviceInfoHolder)
if err != nil { if err != nil {
return nil, err return nil, err
} }
grpcClientProxy, err := naming_grpc.NewNamingGrpcProxy(ctx, clientCfg, nacosServer, serviceInfoHolder) grpcClientProxy, err := naming_grpc.NewNamingGrpcProxy(clientCfg, nacosServer, serviceInfoHolder)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -92,16 +73,12 @@ func (proxy *NamingProxyDelegate) RegisterInstance(serviceName string, groupName
return proxy.getExecuteClientProxy(instance).RegisterInstance(serviceName, groupName, instance) return proxy.getExecuteClientProxy(instance).RegisterInstance(serviceName, groupName, instance)
} }
func (proxy *NamingProxyDelegate) BatchRegisterInstance(serviceName string, groupName string, instances []model.Instance) (bool, error) {
return proxy.grpcClientProxy.BatchRegisterInstance(serviceName, groupName, instances)
}
func (proxy *NamingProxyDelegate) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) { func (proxy *NamingProxyDelegate) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
return proxy.getExecuteClientProxy(instance).DeregisterInstance(serviceName, groupName, instance) return proxy.getExecuteClientProxy(instance).DeregisterInstance(serviceName, groupName, instance)
} }
func (proxy *NamingProxyDelegate) GetServiceList(pageNo uint32, pageSize uint32, groupName, namespaceId string, selector *model.ExpressionSelector) (model.ServiceList, error) { func (proxy *NamingProxyDelegate) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
return proxy.grpcClientProxy.GetServiceList(pageNo, pageSize, groupName, namespaceId, selector) return proxy.grpcClientProxy.GetServiceList(pageNo, pageSize, groupName, selector)
} }
func (proxy *NamingProxyDelegate) ServerHealthy() bool { func (proxy *NamingProxyDelegate) ServerHealthy() bool {
@ -113,17 +90,15 @@ func (proxy *NamingProxyDelegate) QueryInstancesOfService(serviceName, groupName
} }
func (proxy *NamingProxyDelegate) Subscribe(serviceName, groupName string, clusters string) (model.Service, error) { func (proxy *NamingProxyDelegate) Subscribe(serviceName, groupName string, clusters string) (model.Service, error) {
var err error
isSubscribed := proxy.grpcClientProxy.IsSubscribed(serviceName, groupName, clusters)
serviceNameWithGroup := util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters) serviceNameWithGroup := util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters)
serviceInfo, ok := proxy.serviceInfoHolder.ServiceInfoMap.Load(serviceNameWithGroup) serviceInfo, ok := proxy.serviceInfoHolder.ServiceInfoMap.Get(serviceNameWithGroup)
if !isSubscribed || !ok { if !ok {
serviceInfo, err = proxy.grpcClientProxy.Subscribe(serviceName, groupName, clusters) result, err := proxy.grpcClientProxy.Subscribe(serviceName, groupName, clusters)
if err != nil { if err != nil {
return model.Service{}, err return model.Service{}, err
} }
serviceInfo = result
} }
service := serviceInfo.(model.Service) service := serviceInfo.(model.Service)
proxy.serviceInfoHolder.ProcessService(&service) proxy.serviceInfoHolder.ProcessService(&service)
return service, nil return service, nil

View File

@ -17,7 +17,6 @@
package naming_client package naming_client
import ( import (
"context"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
@ -28,17 +27,15 @@ import (
) )
type ServiceInfoUpdater struct { type ServiceInfoUpdater struct {
ctx context.Context
serviceInfoHolder *naming_cache.ServiceInfoHolder serviceInfoHolder *naming_cache.ServiceInfoHolder
updateThreadNum int updateThreadNum int
namingProxy naming_proxy.INamingProxy namingProxy naming_proxy.INamingProxy
} }
func NewServiceInfoUpdater(ctx context.Context, serviceInfoHolder *naming_cache.ServiceInfoHolder, updateThreadNum int, func NewServiceInfoUpdater(serviceInfoHolder *naming_cache.ServiceInfoHolder, updateThreadNum int,
namingProxy naming_proxy.INamingProxy) *ServiceInfoUpdater { namingProxy naming_proxy.INamingProxy) *ServiceInfoUpdater {
return &ServiceInfoUpdater{ return &ServiceInfoUpdater{
ctx: ctx,
serviceInfoHolder: serviceInfoHolder, serviceInfoHolder: serviceInfoHolder,
updateThreadNum: updateThreadNum, updateThreadNum: updateThreadNum,
namingProxy: namingProxy, namingProxy: namingProxy,
@ -48,13 +45,9 @@ func NewServiceInfoUpdater(ctx context.Context, serviceInfoHolder *naming_cache.
func (s *ServiceInfoUpdater) asyncUpdateService() { func (s *ServiceInfoUpdater) asyncUpdateService() {
sema := util.NewSemaphore(s.updateThreadNum) sema := util.NewSemaphore(s.updateThreadNum)
for { for {
select { for _, v := range s.serviceInfoHolder.ServiceInfoMap.Items() {
case <-s.ctx.Done(): service := v.(model.Service)
return lastRefTime, ok := s.serviceInfoHolder.UpdateTimeMap.Get(util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName),
default:
s.serviceInfoHolder.ServiceInfoMap.Range(func(key, value interface{}) bool {
service := value.(model.Service)
lastRefTime, ok := s.serviceInfoHolder.UpdateTimeMap.Load(util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName),
service.Clusters)) service.Clusters))
if !ok { if !ok {
lastRefTime = uint64(0) lastRefTime = uint64(0)
@ -62,14 +55,12 @@ func (s *ServiceInfoUpdater) asyncUpdateService() {
if uint64(util.CurrentMillis())-lastRefTime.(uint64) > service.CacheMillis { if uint64(util.CurrentMillis())-lastRefTime.(uint64) > service.CacheMillis {
sema.Acquire() sema.Acquire()
go func() { go func() {
defer sema.Release()
s.updateServiceNow(service.Name, service.GroupName, service.Clusters) s.updateServiceNow(service.Name, service.GroupName, service.Clusters)
sema.Release()
}() }()
} }
return true
})
time.Sleep(1 * time.Second)
} }
time.Sleep(1 * time.Second)
} }
} }
@ -77,7 +68,7 @@ func (s *ServiceInfoUpdater) updateServiceNow(serviceName, groupName, clusters s
result, err := s.namingProxy.QueryInstancesOfService(serviceName, groupName, clusters, 0, false) result, err := s.namingProxy.QueryInstancesOfService(serviceName, groupName, clusters, 0, false)
if err != nil { if err != nil {
logger.Errorf("QueryInstances error, serviceName:%s, cluster:%s, err:%v", serviceName, clusters, err) logger.Errorf("QueryList return error!serviceName:%s cluster:%s err:%+v", serviceName, clusters, err)
return return
} }
s.serviceInfoHolder.ProcessService(result) s.serviceInfoHolder.ProcessService(result)

View File

@ -81,27 +81,6 @@ func WithEndpoint(endpoint string) ClientOption {
} }
} }
// WithEndpointContextPath ...
func WithEndpointContextPath(endpointContextPath string) ClientOption {
return func(config *ClientConfig) {
config.EndpointContextPath = endpointContextPath
}
}
// WithEndpointQueryParams ...
func WithEndpointQueryParams(endpointQueryPrams string) ClientOption {
return func(config *ClientConfig) {
config.EndpointQueryParams = endpointQueryPrams
}
}
// WithClusterName ...
func WithClusterName(clusterName string) ClientOption {
return func(config *ClientConfig) {
config.ClusterName = clusterName
}
}
// WithRegionId ... // WithRegionId ...
func WithRegionId(regionId string) ClientOption { func WithRegionId(regionId string) ClientOption {
return func(config *ClientConfig) { return func(config *ClientConfig) {
@ -123,12 +102,6 @@ func WithSecretKey(secretKey string) ClientOption {
} }
} }
func WithRamConfig(ramConfig *RamConfig) ClientOption {
return func(config *ClientConfig) {
config.RamConfig = ramConfig
}
}
// WithOpenKMS ... // WithOpenKMS ...
func WithOpenKMS(openKMS bool) ClientOption { func WithOpenKMS(openKMS bool) ClientOption {
return func(config *ClientConfig) { return func(config *ClientConfig) {
@ -136,25 +109,6 @@ func WithOpenKMS(openKMS bool) ClientOption {
} }
} }
// WithOpenKMS ...
func WithKMSVersion(kmsVersion KMSVersion) ClientOption {
return func(config *ClientConfig) {
config.KMSVersion = kmsVersion
}
}
func WithKMSv3Config(kmsv3Config *KMSv3Config) ClientOption {
return func(config *ClientConfig) {
config.KMSv3Config = kmsv3Config
}
}
func WithKMSConfig(kmsConfig *KMSConfig) ClientOption {
return func(config *ClientConfig) {
config.KMSConfig = kmsConfig
}
}
// WithCacheDir ... // WithCacheDir ...
func WithCacheDir(cacheDir string) ClientOption { func WithCacheDir(cacheDir string) ClientOption {
return func(config *ClientConfig) { return func(config *ClientConfig) {
@ -162,13 +116,6 @@ func WithCacheDir(cacheDir string) ClientOption {
} }
} }
// WithDisableUseSnapShot ...
func WithDisableUseSnapShot(disableUseSnapShot bool) ClientOption {
return func(config *ClientConfig) {
config.DisableUseSnapShot = disableUseSnapShot
}
}
// WithUpdateThreadNum ... // WithUpdateThreadNum ...
func WithUpdateThreadNum(updateThreadNum int) ClientOption { func WithUpdateThreadNum(updateThreadNum int) ClientOption {
return func(config *ClientConfig) { return func(config *ClientConfig) {
@ -234,13 +181,6 @@ func WithLogRollingConfig(rollingConfig *ClientLogRollingConfig) ClientOption {
func WithTLS(tlsCfg TLSConfig) ClientOption { func WithTLS(tlsCfg TLSConfig) ClientOption {
return func(config *ClientConfig) { return func(config *ClientConfig) {
tlsCfg.Appointed = true
config.TLSCfg = tlsCfg config.TLSCfg = tlsCfg
} }
} }
func WithAppConnLabels(appConnLabels map[string]string) ClientOption {
return func(config *ClientConfig) {
config.AppConnLabels = appConnLabels
}
}

View File

@ -19,11 +19,10 @@ package constant
import "time" import "time"
type ServerConfig struct { type ServerConfig struct {
Scheme string // the nacos server scheme,default=http,this is not required in 2.0 Scheme string // the nacos server scheme,defaut=http,this is not required in 2.0
ContextPath string // the nacos server contextpath,default=/nacos,this is not required in 2.0 ContextPath string // the nacos server contextpath,defaut=/nacos,this is not required in 2.0
IpAddr string // the nacos server address IpAddr string // the nacos server address
Port uint64 // nacos server port Port uint64 // nacos server port
GrpcPort uint64 // nacos server grpc port, default=server port + 1000, this is not required
} }
type ClientConfig struct { type ClientConfig struct {
@ -37,14 +36,9 @@ type ClientConfig struct {
RegionId string // the regionId for kms RegionId string // the regionId for kms
AccessKey string // the AccessKey for kms AccessKey string // the AccessKey for kms
SecretKey string // the SecretKey for kms SecretKey string // the SecretKey for kms
RamConfig *RamConfig OpenKMS bool // it's to open kms,default is false. https://help.aliyun.com/product/28933.html
OpenKMS bool // it's to open kms, default is false. https://help.aliyun.com/product/28933.html
KMSVersion KMSVersion // kms client version. https://help.aliyun.com/document_detail/380927.html
KMSv3Config *KMSv3Config //KMSv3 configuration. https://help.aliyun.com/document_detail/601596.html
KMSConfig *KMSConfig
CacheDir string // the directory for persist nacos service info,default value is current path CacheDir string // the directory for persist nacos service info,default value is current path
DisableUseSnapShot bool // It's a switch, default is false, means that when get remote config fail, use local cache file instead UpdateThreadNum int // the number of gorutine for update nacos service info,default value is 20
UpdateThreadNum int // the number of goroutine for update nacos service info,default value is 20
NotLoadCacheAtStart bool // not to load persistent nacos service info in CacheDir at start time NotLoadCacheAtStart bool // not to load persistent nacos service info in CacheDir at start time
UpdateCacheWhenEmpty bool // update cache when get empty service instance from server UpdateCacheWhenEmpty bool // update cache when get empty service instance from server
Username string // the username for nacos auth Username string // the username for nacos auth
@ -52,15 +46,9 @@ type ClientConfig struct {
LogDir string // the directory for log, default is current path LogDir string // the directory for log, default is current path
LogLevel string // the level of log, it's must be debug,info,warn,error, default value is info LogLevel string // the level of log, it's must be debug,info,warn,error, default value is info
ContextPath string // the nacos server contextpath ContextPath string // the nacos server contextpath
AppendToStdout bool // if append log to stdout
LogSampling *ClientLogSamplingConfig // the sampling config of log LogSampling *ClientLogSamplingConfig // the sampling config of log
LogRollingConfig *ClientLogRollingConfig // log rolling config LogRollingConfig *ClientLogRollingConfig // log rolling config
TLSCfg TLSConfig // tls Config TLSCfg TLSConfig // tls Config
AsyncUpdateService bool // open async update service by query
EndpointContextPath string // the address server endpoint contextPath
EndpointQueryParams string // the address server endpoint query params
ClusterName string // the address server clusterName
AppConnLabels map[string]string // app conn labels
} }
type ClientLogSamplingConfig struct { type ClientLogSamplingConfig struct {
@ -97,38 +85,9 @@ type ClientLogRollingConfig struct {
} }
type TLSConfig struct { type TLSConfig struct {
Appointed bool // Appointed or not ,if false,will get from env.
Enable bool // enable tls Enable bool // enable tls
TrustAll bool // trust all server
CaFile string // clients use when verifying server certificates CaFile string // clients use when verifying server certificates
CertFile string // server use when verifying client certificates CertFile string // server use when verifying client certificates
KeyFile string // server use when verifying client certificates KeyFile string // server use when verifying client certificates
ServerNameOverride string // serverNameOverride is for testing only ServerNameOverride string // serverNameOverride is for testing only
} }
type KMSv3Config struct {
ClientKeyContent string
Password string
Endpoint string
CaContent string
}
type KMSConfig struct {
Endpoint string
OpenSSL string
CaContent string
}
type RamConfig struct {
SecurityToken string
SignatureRegionId string
RamRoleName string
RoleArn string
Policy string
RoleSessionName string
RoleSessionExpiration int
OIDCProviderArn string
OIDCTokenFilePath string
CredentialsURI string
SecretName string
}

View File

@ -18,14 +18,6 @@ package constant
import "time" import "time"
type KMSVersion string
const (
KMSv1 KMSVersion = "KMSv1"
KMSv3 KMSVersion = "KMSv3"
DEFAULT_KMS_VERSION KMSVersion = "" //to fit original version
UNKNOWN_KMS_VERSION KMSVersion = "UNKNOWN_KMS_VERSION"
)
const ( const (
KEY_USERNAME = "username" KEY_USERNAME = "username"
KEY_PASSWORD = "password" KEY_PASSWORD = "password"
@ -76,7 +68,7 @@ const (
KEY_BEAT = "beat" KEY_BEAT = "beat"
KEY_DOM = "dom" KEY_DOM = "dom"
DEFAULT_CONTEXT_PATH = "/nacos" DEFAULT_CONTEXT_PATH = "/nacos"
CLIENT_VERSION = "Nacos-Go-Client:v2.3.3" CLIENT_VERSION = "Nacos-Go-Client:v2.0.0"
REQUEST_DOMAIN_RETRY_TIME = 3 REQUEST_DOMAIN_RETRY_TIME = 3
SERVICE_INFO_SPLITER = "@@" SERVICE_INFO_SPLITER = "@@"
CONFIG_INFO_SPLITER = "@@" CONFIG_INFO_SPLITER = "@@"
@ -97,25 +89,10 @@ const (
DEFAULT_TIMEOUT_MILLS = 3000 DEFAULT_TIMEOUT_MILLS = 3000
ALL_SYNC_INTERNAL = 5 * time.Minute ALL_SYNC_INTERNAL = 5 * time.Minute
CLIENT_APPNAME_HEADER = "Client-AppName" CLIENT_APPNAME_HEADER = "Client-AppName"
APPNAME_HEADER = "AppName"
CLIENT_REQUEST_TS_HEADER = "Client-RequestTS" CLIENT_REQUEST_TS_HEADER = "Client-RequestTS"
CLIENT_REQUEST_TOKEN_HEADER = "Client-RequestToken" CLIENT_REQUEST_TOKEN_HEADER = "Client-RequestToken"
EX_CONFIG_INFO = "exConfigInfo" EX_CONFIG_INFO = "exConfigInfo"
CHARSET_KEY = "charset" CHARSET_KEY = "charset"
LOG_FILE_NAME = "nacos-sdk.log" LOG_FILE_NAME = "nacos-sdk.log"
HTTPS_SERVER_PORT = 443 HTTPS_SERVER_PORT = 443
GRPC = "grpc"
RpcPortOffset = 1000
MSE_KMSv1_DEFAULT_KEY_ID = "alias/acs/mse"
CONFIG_PUBLISH_REQUEST_NAME = "ConfigPublishRequest"
CONFIG_QUERY_REQUEST_NAME = "ConfigQueryRequest"
CONFIG_REMOVE_REQUEST_NAME = "ConfigRemoveRequest"
INSTANCE_REQUEST_NAME = "InstanceRequest"
BATCH_INSTANCE_REQUEST_NAME = "BatchInstanceRequest"
SERVICE_LIST_REQUEST_NAME = "ServiceListRequest"
SERVICE_QUERY_REQUEST_NAME = "ServiceQueryRequest"
SUBSCRIBE_SERVICE_REQUEST_NAME = "SubscribeServiceRequest"
NOTIFY_SUBSCRIBE_REQUEST_NAME = "NotifySubscriberRequest"
CONFIG_BATCH_LISTEN_REQUEST_NAME = "ConfigBatchListenRequest"
CONFIG_CHANGE_NOTIFY_REQUEST_NAME = "ConfigChangeNotifyRequest"
) )

View File

@ -34,37 +34,30 @@ func NewServerConfig(ipAddr string, port uint64, opts ...ServerOption) *ServerCo
// ServerOption ... // ServerOption ...
type ServerOption func(*ServerConfig) type ServerOption func(*ServerConfig)
// WithScheme set Scheme for server //WithScheme set Scheme for server
func WithScheme(scheme string) ServerOption { func WithScheme(scheme string) ServerOption {
return func(config *ServerConfig) { return func(config *ServerConfig) {
config.Scheme = scheme config.Scheme = scheme
} }
} }
// WithContextPath set contextPath for server //WithContextPath set contextPath for server
func WithContextPath(contextPath string) ServerOption { func WithContextPath(contextPath string) ServerOption {
return func(config *ServerConfig) { return func(config *ServerConfig) {
config.ContextPath = contextPath config.ContextPath = contextPath
} }
} }
// WithIpAddr set ip address for server //WithIpAddr set ip address for server
func WithIpAddr(ipAddr string) ServerOption { func WithIpAddr(ipAddr string) ServerOption {
return func(config *ServerConfig) { return func(config *ServerConfig) {
config.IpAddr = ipAddr config.IpAddr = ipAddr
} }
} }
// WithPort set port for server //WithPort set port for server
func WithPort(port uint64) ServerOption { func WithPort(port string) ServerOption {
return func(config *ServerConfig) { return func(config *ServerConfig) {
config.Port = port config.IpAddr = port
}
}
// WithGrpcPort set grpc port for server
func WithGrpcPort(port uint64) ServerOption {
return func(config *ServerConfig) {
config.GrpcPort = port
} }
} }

View File

@ -1,72 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 encoding
import (
"encoding/base64"
"unicode/utf8"
)
func DecodeString2Utf8Bytes(data string) []byte {
resBytes := make([]byte, 0, 8)
if len(data) == 0 {
return resBytes
}
bytesLen := 0
runes := []rune(data)
for _, r := range runes {
bytesLen += utf8.RuneLen(r)
}
resBytes = make([]byte, bytesLen)
pos := 0
for _, r := range runes {
pos += utf8.EncodeRune(resBytes[pos:], r)
}
return resBytes
}
func EncodeUtf8Bytes2String(bytes []byte) string {
if len(bytes) == 0 {
return ""
}
var startPos, endPos int
resRunes := make([]rune, 0, 8)
for endPos <= len(bytes) {
if utf8.FullRune(bytes[startPos:endPos]) {
decodedRune, _ := utf8.DecodeRune(bytes[startPos:endPos])
resRunes = append(resRunes, decodedRune)
startPos = endPos
}
endPos++
}
return string(resRunes)
}
func DecodeBase64(bytes []byte) ([]byte, error) {
dst := make([]byte, base64.StdEncoding.DecodedLen(len(bytes)))
n, err := base64.StdEncoding.Decode(dst, bytes)
if err != nil {
return nil, err
}
return dst[:n], nil
}
func EncodeBase64(bytes []byte) ([]byte, error) {
dst := make([]byte, base64.StdEncoding.EncodedLen(len(bytes)))
base64.StdEncoding.Encode(dst, bytes)
return dst[:], nil
}

View File

@ -1,88 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 encryption
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"
)
func AesEcbPkcs5PaddingEncrypt(plainContent, key []byte) (retBytes []byte, err error) {
if len(plainContent) == 0 {
return nil, nil
}
aesCipherBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
pkcs5PaddingBytes := PKCS5Padding(plainContent, aesCipherBlock.BlockSize())
return BlockEncrypt(pkcs5PaddingBytes, aesCipherBlock)
}
func AesEcbPkcs5PaddingDecrypt(cipherContent, key []byte) (retBytes []byte, err error) {
if len(cipherContent) == 0 {
return nil, nil
}
aesCipherBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
decryptBytes, err := BlockDecrypt(cipherContent, aesCipherBlock)
if err != nil {
return nil, err
}
retBytes = PKCS5UnPadding(decryptBytes)
return retBytes, nil
}
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
func BlockEncrypt(src []byte, b cipher.Block) (dst []byte, err error) {
if len(src)%b.BlockSize() != 0 {
return nil, fmt.Errorf("input not full blocks")
}
buf := make([]byte, b.BlockSize())
for i := 0; i < len(src); i += b.BlockSize() {
b.Encrypt(buf, src[i:i+b.BlockSize()])
dst = append(dst, buf...)
}
return
}
func BlockDecrypt(src []byte, b cipher.Block) (dst []byte, err error) {
if len(src)%b.BlockSize() != 0 {
return nil, fmt.Errorf("input not full blocks")
}
buf := make([]byte, b.BlockSize())
for i := 0; i < len(src); i += b.BlockSize() {
b.Decrypt(buf, src[i:i+b.BlockSize()])
dst = append(dst, buf...)
}
return
}

View File

@ -1,68 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 encryption
import "fmt"
const (
CipherPrefix = "cipher-"
KmsAes128AlgorithmName = "cipher-kms-aes-128"
KmsAes256AlgorithmName = "cipher-kms-aes-256"
KmsAlgorithmName = "cipher"
kmsAes128KeySpec = "AES_128"
kmsAes256KeySpec = "AES_256"
kmsScheme = "https"
kmsAcceptFormat = "XML"
kmsCipherAlgorithm = "AES/ECB/PKCS5Padding"
maskUnit8Width = 8
maskUnit32Width = 32
KmsHandlerName = "KmsHandler"
)
var (
DataIdParamCheckError = fmt.Errorf("dataId prefix should start with: %s", CipherPrefix)
ContentParamCheckError = fmt.Errorf("content need to encrypt is nil")
KeyIdParamCheckError = fmt.Errorf("keyId is nil, need to be set")
)
var (
PluginNotFoundError = fmt.Errorf("cannot find encryption plugin by dataId prefix")
)
var (
EmptyEncryptedDataKeyError = fmt.Errorf("empty encrypted data key error")
EmptyPlainDataKeyError = fmt.Errorf("empty plain data key error")
EmptyContentError = fmt.Errorf("encrypt empty content error")
)
var (
EmptyRegionKmsV1ClientInitError = fmt.Errorf("init kmsV1 client failed with empty region")
EmptyAkKmsV1ClientInitError = fmt.Errorf("init kmsV1 client failed with empty ak")
EmptySkKmsV1ClientInitError = fmt.Errorf("init kmsV1 client failed with empty sk")
EmptyEndpointKmsV3ClientInitError = fmt.Errorf("init kmsV3 client failed with empty endpoint")
EmptyPasswordKmsV3ClientInitError = fmt.Errorf("init kmsV3 client failed with empty password")
EmptyClientKeyContentKmsV3ClientInitError = fmt.Errorf("init kmsV3 client failed with empty client key content")
EmptyCaVerifyKmsV3ClientInitError = fmt.Errorf("init kmsV3 client failed with empty ca verify")
EmptyEndpointKmsRamClientInitError = fmt.Errorf("init kmsRam client failed with empty endpoint")
)

View File

@ -1,224 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 encryption
import (
"fmt"
"github.com/alibabacloud-go/tea/tea"
dkms_api "github.com/aliyun/alibabacloud-dkms-gcs-go-sdk/openapi"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/pkg/errors"
"strings"
)
type HandlerParam struct {
DataId string `json:"dataId"` //required
Content string `json:"content"` //required
EncryptedDataKey string `json:"encryptedDataKey"`
PlainDataKey string `json:"plainDataKey"`
KeyId string `json:"keyId"`
}
type Plugin interface {
Encrypt(*HandlerParam) error
Decrypt(*HandlerParam) error
AlgorithmName() string
GenerateSecretKey(*HandlerParam) (string, error)
EncryptSecretKey(*HandlerParam) (string, error)
DecryptSecretKey(*HandlerParam) (string, error)
}
type Handler interface {
EncryptionHandler(*HandlerParam) error
DecryptionHandler(*HandlerParam) error
RegisterPlugin(Plugin) error
GetHandlerName() string
}
func NewKmsHandler() Handler {
return newKmsHandler()
}
func newKmsHandler() *KmsHandler {
kmsHandler := &KmsHandler{
encryptionPlugins: make(map[string]Plugin, 2),
}
logger.Debug("successfully create encryption KmsHandler")
return kmsHandler
}
func RegisterConfigEncryptionKmsPlugins(encryptionHandler Handler, clientConfig constant.ClientConfig) {
innerKmsClient, err := innerNewKmsClient(clientConfig)
if innerKmsClient == nil {
err = errors.New("create kms client failed.")
}
if err != nil && innerKmsClient == nil {
err = errors.New("create kms client failed.")
}
if err != nil {
logger.Error(err)
}
if err := encryptionHandler.RegisterPlugin(&KmsAes128Plugin{kmsPlugin{kmsClient: innerKmsClient}}); err != nil {
logger.Errorf("failed to register encryption plugin[%s] to %s", KmsAes128AlgorithmName, encryptionHandler.GetHandlerName())
} else {
logger.Debugf("successfully register encryption plugin[%s] to %s", KmsAes128AlgorithmName, encryptionHandler.GetHandlerName())
}
if err := encryptionHandler.RegisterPlugin(&KmsAes256Plugin{kmsPlugin{kmsClient: innerKmsClient}}); err != nil {
logger.Errorf("failed to register encryption plugin[%s] to %s", KmsAes256AlgorithmName, encryptionHandler.GetHandlerName())
} else {
logger.Debugf("successfully register encryption plugin[%s] to %s", KmsAes256AlgorithmName, encryptionHandler.GetHandlerName())
}
if err := encryptionHandler.RegisterPlugin(&KmsBasePlugin{kmsPlugin{kmsClient: innerKmsClient}}); err != nil {
logger.Errorf("failed to register encryption plugin[%s] to %s", KmsAlgorithmName, encryptionHandler.GetHandlerName())
} else {
logger.Debugf("successfully register encryption plugin[%s] to %s", KmsAlgorithmName, encryptionHandler.GetHandlerName())
}
}
type KmsHandler struct {
encryptionPlugins map[string]Plugin
}
func (d *KmsHandler) EncryptionHandler(param *HandlerParam) error {
if err := d.encryptionParamCheck(*param); err != nil {
return err
}
plugin, err := d.getPluginByDataIdPrefix(param.DataId)
if err != nil {
return err
}
plainSecretKey, err := plugin.GenerateSecretKey(param)
if err != nil {
return err
}
param.PlainDataKey = plainSecretKey
return plugin.Encrypt(param)
}
func (d *KmsHandler) DecryptionHandler(param *HandlerParam) error {
if err := d.decryptionParamCheck(*param); err != nil {
return err
}
plugin, err := d.getPluginByDataIdPrefix(param.DataId)
if err != nil {
return err
}
plainSecretkey, err := plugin.DecryptSecretKey(param)
if err != nil {
return err
}
param.PlainDataKey = plainSecretkey
return plugin.Decrypt(param)
}
func (d *KmsHandler) getPluginByDataIdPrefix(dataId string) (Plugin, error) {
var (
matchedCount int
matchedPlugin Plugin
)
for k, v := range d.encryptionPlugins {
if strings.Contains(dataId, k) {
if len(k) > matchedCount {
matchedCount = len(k)
matchedPlugin = v
}
}
}
if matchedPlugin == nil {
return matchedPlugin, PluginNotFoundError
}
return matchedPlugin, nil
}
func (d *KmsHandler) RegisterPlugin(plugin Plugin) error {
if _, v := d.encryptionPlugins[plugin.AlgorithmName()]; v {
logger.Warnf("encryption algorithm [%s] has already registered to defaultHandler, will be update", plugin.AlgorithmName())
} else {
logger.Debugf("register encryption algorithm [%s] to defaultHandler", plugin.AlgorithmName())
}
d.encryptionPlugins[plugin.AlgorithmName()] = plugin
return nil
}
func (d *KmsHandler) GetHandlerName() string {
return KmsHandlerName
}
func (d *KmsHandler) encryptionParamCheck(param HandlerParam) error {
if err := d.dataIdParamCheck(param.DataId); err != nil {
return DataIdParamCheckError
}
if err := d.contentParamCheck(param.Content); err != nil {
return ContentParamCheckError
}
return nil
}
func (d *KmsHandler) decryptionParamCheck(param HandlerParam) error {
return d.encryptionParamCheck(param)
}
func (d *KmsHandler) keyIdParamCheck(keyId string) error {
if len(keyId) == 0 {
return fmt.Errorf("cipher dataId using kmsService need to set keyId, but keyId is nil")
}
return nil
}
func (d *KmsHandler) dataIdParamCheck(dataId string) error {
if !strings.Contains(dataId, CipherPrefix) {
return fmt.Errorf("dataId prefix should start with: %s", CipherPrefix)
}
return nil
}
func (d *KmsHandler) contentParamCheck(content string) error {
if len(content) == 0 {
return fmt.Errorf("content need to encrypt is nil")
}
return nil
}
func innerNewKmsClient(clientConfig constant.ClientConfig) (kmsClient KmsClient, err error) {
switch clientConfig.KMSVersion {
case constant.KMSv1, constant.DEFAULT_KMS_VERSION:
kmsClient, err = newKmsRamClient(clientConfig)
case constant.KMSv3:
kmsClient, err = newKmsV3Client(clientConfig)
default:
err = fmt.Errorf("init kms client failed. unknown kms version:%s\n", clientConfig.KMSVersion)
}
return kmsClient, err
}
func newKmsV1Client(clientConfig constant.ClientConfig) (KmsClient, error) {
return NewKmsV1ClientWithAccessKey(clientConfig.RegionId, clientConfig.AccessKey, clientConfig.SecretKey)
}
func newKmsV3Client(clientConfig constant.ClientConfig) (KmsClient, error) {
return NewKmsV3ClientWithConfig(&dkms_api.Config{
Protocol: tea.String("https"),
Endpoint: tea.String(clientConfig.KMSv3Config.Endpoint),
ClientKeyContent: tea.String(clientConfig.KMSv3Config.ClientKeyContent),
Password: tea.String(clientConfig.KMSv3Config.Password),
}, clientConfig.KMSv3Config.CaContent)
}
func newKmsRamClient(clientConfig constant.ClientConfig) (KmsClient, error) {
return NewKmsRamClient(clientConfig.KMSConfig, clientConfig.RegionId, clientConfig.AccessKey, clientConfig.SecretKey)
}

View File

@ -1,303 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 encryption
import (
"fmt"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
kms20160120 "github.com/alibabacloud-go/kms-20160120/v3/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
dkms_api "github.com/aliyun/alibabacloud-dkms-gcs-go-sdk/openapi"
dkms_transfer "github.com/aliyun/alibabacloud-dkms-transfer-go-sdk/sdk"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/pkg/errors"
"net/http"
"strings"
)
type KmsClient interface {
Decrypt(cipherContent string) (string, error)
Encrypt(content string, keyId string) (string, error)
GenerateDataKey(keyId, keySpec string) (string, string, error)
GetKmsVersion() constant.KMSVersion
setKmsVersion(constant.KMSVersion)
}
type TransferKmsClient struct {
*dkms_transfer.KmsTransferClient
kmsVersion constant.KMSVersion
}
func NewKmsV1ClientWithAccessKey(regionId, ak, sk string) (*TransferKmsClient, error) {
var rErr error
if rErr = checkKmsV1InitParam(regionId, ak, sk); rErr != nil {
return nil, rErr
}
kmsClient, err := newKmsV1ClientWithAccessKey(regionId, ak, sk)
if err != nil {
rErr = errors.Wrap(err, "init kms v1 client with ak/sk failed")
} else {
kmsClient.setKmsVersion(constant.KMSv1)
}
return kmsClient, rErr
}
func checkKmsV1InitParam(regionId, ak, sk string) error {
if len(regionId) == 0 {
return EmptyRegionKmsV1ClientInitError
}
if len(ak) == 0 {
return EmptyAkKmsV1ClientInitError
}
if len(sk) == 0 {
return EmptySkKmsV1ClientInitError
}
return nil
}
func checkKmsRamInitParam(endpoint, ak, sk string) error {
if len(endpoint) == 0 {
return EmptyEndpointKmsRamClientInitError
}
if len(ak) == 0 {
return EmptyAkKmsV1ClientInitError
}
if len(sk) == 0 {
return EmptySkKmsV1ClientInitError
}
return nil
}
func NewKmsV3ClientWithConfig(config *dkms_api.Config, caVerify string) (*TransferKmsClient, error) {
var rErr error
if rErr = checkKmsV3InitParam(config, caVerify); rErr != nil {
return nil, rErr
}
kmsClient, err := newKmsV3ClientWithConfig(config)
if err != nil {
rErr = errors.Wrap(err, "init kms v3 client with config failed")
} else {
if len(strings.TrimSpace(caVerify)) != 0 {
logger.Debugf("set kms client Ca with content: %s\n", caVerify[:len(caVerify)/maskUnit32Width])
kmsClient.SetVerify(caVerify)
} else {
kmsClient.SetHTTPSInsecure(true)
}
kmsClient.setKmsVersion(constant.KMSv3)
}
return kmsClient, rErr
}
func checkKmsV3InitParam(config *dkms_api.Config, caVerify string) error {
if len(*config.Endpoint) == 0 {
return EmptyEndpointKmsV3ClientInitError
}
if len(*config.Password) == 0 {
return EmptyPasswordKmsV3ClientInitError
}
if len(*config.ClientKeyContent) == 0 {
return EmptyClientKeyContentKmsV3ClientInitError
}
if len(caVerify) == 0 {
return EmptyCaVerifyKmsV3ClientInitError
}
return nil
}
func newKmsV1ClientWithAccessKey(regionId, ak, sk string) (*TransferKmsClient, error) {
logger.Debugf("init kms client with region:[%s], ak:[%s]xxx, sk:[%s]xxx\n",
regionId, ak[:len(ak)/maskUnit8Width], sk[:len(sk)/maskUnit8Width])
return newKmsClient(regionId, ak, sk, nil)
}
func newKmsV3ClientWithConfig(config *dkms_api.Config) (*TransferKmsClient, error) {
logger.Debugf("init kms client with endpoint:[%s], clientKeyContent:[%s], password:[%s]\n",
config.Endpoint, (*config.ClientKeyContent)[:len(*config.ClientKeyContent)/maskUnit8Width],
(*config.Password)[:len(*config.Password)/maskUnit8Width])
return newKmsClient("", "", "", config)
}
func newKmsClient(regionId, ak, sk string, config *dkms_api.Config) (*TransferKmsClient, error) {
client, err := dkms_transfer.NewClientWithAccessKey(regionId, ak, sk, config)
if err != nil {
return nil, err
}
return &TransferKmsClient{
KmsTransferClient: client,
}, nil
}
func (kmsClient *TransferKmsClient) GetKmsVersion() constant.KMSVersion {
return kmsClient.kmsVersion
}
func (kmsClient *TransferKmsClient) setKmsVersion(kmsVersion constant.KMSVersion) {
logger.Debug("successfully set kms client version to " + kmsVersion)
kmsClient.kmsVersion = kmsVersion
}
func (kmsClient *TransferKmsClient) GenerateDataKey(keyId, keySpec string) (string, string, error) {
generateDataKeyRequest := kms.CreateGenerateDataKeyRequest()
generateDataKeyRequest.Scheme = kmsScheme
generateDataKeyRequest.AcceptFormat = kmsAcceptFormat
generateDataKeyRequest.KeyId = keyId
generateDataKeyRequest.KeySpec = keySpec
generateDataKeyResponse, err := kmsClient.KmsTransferClient.GenerateDataKey(generateDataKeyRequest)
if err != nil {
return "", "", err
}
return generateDataKeyResponse.Plaintext, generateDataKeyResponse.CiphertextBlob, nil
}
func (kmsClient *TransferKmsClient) Decrypt(cipherContent string) (string, error) {
request := kms.CreateDecryptRequest()
request.Method = http.MethodPost
request.Scheme = kmsScheme
request.AcceptFormat = kmsAcceptFormat
request.CiphertextBlob = cipherContent
response, err := kmsClient.KmsTransferClient.Decrypt(request)
if err != nil {
return "", fmt.Errorf("kms decrypt failed: %v", err)
}
return response.Plaintext, nil
}
func (kmsClient *TransferKmsClient) Encrypt(content, keyId string) (string, error) {
request := kms.CreateEncryptRequest()
request.Method = http.MethodPost
request.Scheme = kmsScheme
request.AcceptFormat = kmsAcceptFormat
request.Plaintext = content
request.KeyId = keyId
response, err := kmsClient.KmsTransferClient.Encrypt(request)
if err != nil {
return "", fmt.Errorf("kms encrypt failed: %v", err)
}
return response.CiphertextBlob, nil
}
func GetDefaultKMSv1KeyId() string {
return constant.MSE_KMSv1_DEFAULT_KEY_ID
}
type RamKmsClient struct {
*kms20160120.Client
kmsVersion constant.KMSVersion
runtime *util.RuntimeOptions
}
func NewKmsRamClient(kmsConfig *constant.KMSConfig, regionId, ak, sk string) (*RamKmsClient, error) {
if kmsConfig == nil || len(kmsConfig.Endpoint) == 0 {
if err := checkKmsV1InitParam(regionId, ak, sk); err != nil {
return nil, err
}
KmsV1Config := &openapi.Config{}
KmsV1Config.AccessKeyId = tea.String(ak)
KmsV1Config.AccessKeySecret = tea.String(sk)
KmsV1Config.RegionId = tea.String(regionId)
_result, _err := kms20160120.NewClient(KmsV1Config)
if _err != nil {
return nil, _err
}
_ramClient := &RamKmsClient{
Client: _result,
kmsVersion: constant.KMSv1,
runtime: &util.RuntimeOptions{},
}
return _ramClient, nil
}
if err := checkKmsRamInitParam(kmsConfig.Endpoint, ak, sk); err != nil {
return nil, err
}
config := &openapi.Config{}
config.AccessKeyId = tea.String(ak)
config.AccessKeySecret = tea.String(sk)
if len(regionId) != 0 {
config.RegionId = tea.String(regionId)
}
config.Endpoint = tea.String(kmsConfig.Endpoint)
config.Ca = tea.String(kmsConfig.CaContent)
runtimeOption := &util.RuntimeOptions{}
if len(kmsConfig.CaContent) == 0 {
runtimeOption.IgnoreSSL = tea.Bool(true)
}
if kmsConfig.OpenSSL == "true" {
runtimeOption.IgnoreSSL = tea.Bool(false)
} else if kmsConfig.OpenSSL == "false" {
runtimeOption.IgnoreSSL = tea.Bool(true)
}
_result, _err := kms20160120.NewClient(config)
if _err != nil {
return nil, _err
}
_ramClient := &RamKmsClient{
Client: _result,
kmsVersion: constant.KMSv3,
runtime: runtimeOption,
}
return _ramClient, nil
}
func (kmsClient *RamKmsClient) GetKmsVersion() constant.KMSVersion {
return kmsClient.kmsVersion
}
func (kmsClient *RamKmsClient) setKmsVersion(kmsVersion constant.KMSVersion) {
logger.Debug("successfully set kms client version to " + kmsVersion)
kmsClient.kmsVersion = kmsVersion
}
func (kmsClient *RamKmsClient) GenerateDataKey(keyId, keySpec string) (string, string, error) {
request := &kms20160120.GenerateDataKeyRequest{
KeyId: tea.String(keyId),
KeySpec: tea.String(keySpec),
}
_body, _err := kmsClient.Client.GenerateDataKeyWithOptions(request, kmsClient.runtime)
if _err != nil {
return "", "", _err
}
return *_body.Body.Plaintext, *_body.Body.CiphertextBlob, nil
}
func (kmsClient *RamKmsClient) Decrypt(cipherContent string) (string, error) {
request := &kms20160120.DecryptRequest{
CiphertextBlob: tea.String(cipherContent),
}
_body, _err := kmsClient.Client.DecryptWithOptions(request, kmsClient.runtime)
if _err != nil {
return "", _err
}
return *_body.Body.Plaintext, nil
}
func (kmsClient *RamKmsClient) Encrypt(content, keyId string) (string, error) {
request := &kms20160120.EncryptRequest{
Plaintext: tea.String(content),
KeyId: tea.String(keyId),
}
_body, _err := kmsClient.Client.EncryptWithOptions(request, kmsClient.runtime)
if _err != nil {
return "", _err
}
return *_body.Body.CiphertextBlob, nil
}

View File

@ -1,299 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 encryption
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
inner_encoding "github.com/nacos-group/nacos-sdk-go/v2/common/encoding"
"strings"
)
type kmsPlugin struct {
kmsClient KmsClient
}
func (k *kmsPlugin) Encrypt(param *HandlerParam) error {
err := k.encryptionParamCheck(*param)
if err != nil {
return err
}
secretKeyBase64Decoded, err := inner_encoding.DecodeBase64(inner_encoding.DecodeString2Utf8Bytes(param.PlainDataKey))
if err != nil {
return err
}
contentUtf8Bytes := inner_encoding.DecodeString2Utf8Bytes(param.Content)
encryptedContent, err := AesEcbPkcs5PaddingEncrypt(contentUtf8Bytes, secretKeyBase64Decoded)
if err != nil {
return err
}
contentBase64Encoded, err := inner_encoding.EncodeBase64(encryptedContent)
if err != nil {
return err
}
param.Content = inner_encoding.EncodeUtf8Bytes2String(contentBase64Encoded)
return nil
}
func (k *kmsPlugin) Decrypt(param *HandlerParam) error {
err := k.decryptionParamCheck(*param)
if err != nil {
return err
}
secretKeyBase64Decoded, err := inner_encoding.DecodeBase64(inner_encoding.DecodeString2Utf8Bytes(param.PlainDataKey))
if err != nil {
return err
}
contentBase64Decoded, err := inner_encoding.DecodeBase64(inner_encoding.DecodeString2Utf8Bytes(param.Content))
if err != nil {
return err
}
decryptedContent, err := AesEcbPkcs5PaddingDecrypt(contentBase64Decoded, secretKeyBase64Decoded)
if err != nil {
return err
}
param.Content = inner_encoding.EncodeUtf8Bytes2String(decryptedContent)
return nil
}
func (k *kmsPlugin) AlgorithmName() string {
return ""
}
func (k *kmsPlugin) GenerateSecretKey(param *HandlerParam) (string, error) {
return "", nil
}
func (k *kmsPlugin) EncryptSecretKey(param *HandlerParam) (string, error) {
var keyId string
var err error
if keyId, err = k.keyIdParamCheck(param.KeyId); err != nil {
return "", err
}
if len(param.PlainDataKey) == 0 {
return "", EmptyPlainDataKeyError
}
encryptedDataKey, err := k.kmsClient.Encrypt(param.PlainDataKey, keyId)
if err != nil {
return "", err
}
if len(encryptedDataKey) == 0 {
return "", EmptyEncryptedDataKeyError
}
param.EncryptedDataKey = encryptedDataKey
return encryptedDataKey, nil
}
func (k *kmsPlugin) DecryptSecretKey(param *HandlerParam) (string, error) {
if len(param.EncryptedDataKey) == 0 {
return "", EmptyEncryptedDataKeyError
}
plainDataKey, err := k.kmsClient.Decrypt(param.EncryptedDataKey)
if err != nil {
return "", err
}
if len(plainDataKey) == 0 {
return "", EmptyPlainDataKeyError
}
param.PlainDataKey = plainDataKey
return plainDataKey, nil
}
func (k *kmsPlugin) encryptionParamCheck(param HandlerParam) error {
if err := k.plainDataKeyParamCheck(param.PlainDataKey); err != nil {
return KeyIdParamCheckError
}
if err := k.contentParamCheck(param.Content); err != nil {
return ContentParamCheckError
}
return nil
}
func (k *kmsPlugin) decryptionParamCheck(param HandlerParam) error {
return k.encryptionParamCheck(param)
}
func (k *kmsPlugin) plainDataKeyParamCheck(plainDataKey string) error {
if len(plainDataKey) == 0 {
return EmptyPlainDataKeyError
}
return nil
}
func (k *kmsPlugin) dataIdParamCheck(dataId string) error {
if !strings.Contains(dataId, CipherPrefix) {
return fmt.Errorf("dataId prefix should start with: %s", CipherPrefix)
}
return nil
}
func (k *kmsPlugin) keyIdParamCheck(keyId string) (string, error) {
if len(strings.TrimSpace(keyId)) == 0 {
if k.kmsClient.GetKmsVersion() == constant.KMSv1 {
return GetDefaultKMSv1KeyId(), nil
}
return "", KeyIdParamCheckError
}
return keyId, nil
}
func (k *kmsPlugin) contentParamCheck(content string) error {
if len(content) == 0 {
return fmt.Errorf("content need to encrypt is nil")
}
return nil
}
type KmsAes128Plugin struct {
kmsPlugin
}
func (k *KmsAes128Plugin) Encrypt(param *HandlerParam) error {
return k.kmsPlugin.Encrypt(param)
}
func (k *KmsAes128Plugin) Decrypt(param *HandlerParam) error {
return k.kmsPlugin.Decrypt(param)
}
func (k *KmsAes128Plugin) AlgorithmName() string {
return KmsAes128AlgorithmName
}
func (k *KmsAes128Plugin) GenerateSecretKey(param *HandlerParam) (string, error) {
var keyId string
var err error
if keyId, err = k.keyIdParamCheck(param.KeyId); err != nil {
return "", err
}
plainSecretKey, encryptedSecretKey, err := k.kmsClient.GenerateDataKey(keyId, kmsAes128KeySpec)
if err != nil {
return "", err
}
param.PlainDataKey = plainSecretKey
param.EncryptedDataKey = encryptedSecretKey
if len(param.PlainDataKey) == 0 {
return "", EmptyPlainDataKeyError
}
if len(param.EncryptedDataKey) == 0 {
return "", EmptyEncryptedDataKeyError
}
return plainSecretKey, nil
}
func (k *KmsAes128Plugin) EncryptSecretKey(param *HandlerParam) (string, error) {
return k.kmsPlugin.EncryptSecretKey(param)
}
func (k *KmsAes128Plugin) DecryptSecretKey(param *HandlerParam) (string, error) {
return k.kmsPlugin.DecryptSecretKey(param)
}
type KmsAes256Plugin struct {
kmsPlugin
}
func (k *KmsAes256Plugin) Encrypt(param *HandlerParam) error {
return k.kmsPlugin.Encrypt(param)
}
func (k *KmsAes256Plugin) Decrypt(param *HandlerParam) error {
return k.kmsPlugin.Decrypt(param)
}
func (k *KmsAes256Plugin) AlgorithmName() string {
return KmsAes256AlgorithmName
}
func (k *KmsAes256Plugin) GenerateSecretKey(param *HandlerParam) (string, error) {
var keyId string
var err error
if keyId, err = k.keyIdParamCheck(param.KeyId); err != nil {
return "", err
}
plainSecretKey, encryptedSecretKey, err := k.kmsClient.GenerateDataKey(keyId, kmsAes256KeySpec)
if err != nil {
return "", err
}
param.PlainDataKey = plainSecretKey
param.EncryptedDataKey = encryptedSecretKey
if len(param.PlainDataKey) == 0 {
return "", EmptyPlainDataKeyError
}
if len(param.EncryptedDataKey) == 0 {
return "", EmptyEncryptedDataKeyError
}
return plainSecretKey, nil
}
func (k *KmsAes256Plugin) EncryptSecretKey(param *HandlerParam) (string, error) {
return k.kmsPlugin.EncryptSecretKey(param)
}
func (k *KmsAes256Plugin) DecryptSecretKey(param *HandlerParam) (string, error) {
return k.kmsPlugin.DecryptSecretKey(param)
}
type KmsBasePlugin struct {
kmsPlugin
}
func (k *KmsBasePlugin) Encrypt(param *HandlerParam) error {
var keyId string
var err error
if keyId, err = k.keyIdParamCheck(param.KeyId); err != nil {
return err
}
if len(param.Content) == 0 {
return EmptyContentError
}
encryptedContent, err := k.kmsClient.Encrypt(param.Content, keyId)
if err != nil {
return err
}
param.Content = encryptedContent
return nil
}
func (k *KmsBasePlugin) Decrypt(param *HandlerParam) error {
if len(param.Content) == 0 {
return nil
}
plainContent, err := k.kmsClient.Decrypt(param.Content)
if err != nil {
return err
}
param.Content = plainContent
return nil
}
func (k *KmsBasePlugin) AlgorithmName() string {
return KmsAlgorithmName
}
func (k *KmsBasePlugin) GenerateSecretKey(param *HandlerParam) (string, error) {
return "", nil
}
func (k *KmsBasePlugin) EncryptSecretKey(param *HandlerParam) (string, error) {
return "", nil
}
func (k *KmsBasePlugin) DecryptSecretKey(param *HandlerParam) (string, error) {
return "", nil
}

View File

@ -73,17 +73,3 @@ func GetCurrentPath() string {
} }
return dir return dir
} }
func IsExistFile(filePath string) bool {
if len(filePath) == 0 {
return false
}
_, err := os.Stat(filePath)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}

View File

@ -1,87 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 filter
import (
nacos_inner_encryption "github.com/nacos-group/nacos-sdk-go/v2/common/encryption"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/pkg/errors"
"strings"
)
const (
defaultConfigEncryptionFilterName = "defaultConfigEncryptionFilter"
)
var (
noNeedEncryptionError = errors.New("dataId doesn't need to encrypt/decrypt.")
)
type DefaultConfigEncryptionFilter struct {
handler nacos_inner_encryption.Handler
}
func NewDefaultConfigEncryptionFilter(handler nacos_inner_encryption.Handler) IConfigFilter {
return &DefaultConfigEncryptionFilter{handler}
}
func (d *DefaultConfigEncryptionFilter) DoFilter(param *vo.ConfigParam) error {
if err := d.paramCheck(*param); err != nil {
if errors.Is(err, noNeedEncryptionError) {
return nil
}
}
if param.UsageType == vo.RequestType {
encryptionParam := &nacos_inner_encryption.HandlerParam{
DataId: param.DataId,
Content: param.Content,
KeyId: param.KmsKeyId,
}
if err := d.handler.EncryptionHandler(encryptionParam); err != nil {
return err
}
param.Content = encryptionParam.Content
param.EncryptedDataKey = encryptionParam.EncryptedDataKey
} else if param.UsageType == vo.ResponseType {
decryptionParam := &nacos_inner_encryption.HandlerParam{
DataId: param.DataId,
Content: param.Content,
EncryptedDataKey: param.EncryptedDataKey,
}
if err := d.handler.DecryptionHandler(decryptionParam); err != nil {
return err
}
param.Content = decryptionParam.Content
}
return nil
}
func (d *DefaultConfigEncryptionFilter) GetOrder() int {
return 0
}
func (d *DefaultConfigEncryptionFilter) GetFilterName() string {
return defaultConfigEncryptionFilterName
}
func (d *DefaultConfigEncryptionFilter) paramCheck(param vo.ConfigParam) error {
if !strings.HasPrefix(param.DataId, nacos_inner_encryption.CipherPrefix) ||
len(strings.TrimSpace(param.Content)) == 0 {
return noNeedEncryptionError
}
return nil
}

View File

@ -1,104 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 filter
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
type IConfigFilterChain interface {
AddFilter(IConfigFilter) error
GetFilters() []IConfigFilter
DoFilters(*vo.ConfigParam) error
DoFilterByName(*vo.ConfigParam, string) error
}
type IConfigFilter interface {
DoFilter(*vo.ConfigParam) error
GetOrder() int
GetFilterName() string
}
func RegisterConfigFilterToChain(chain IConfigFilterChain, filter IConfigFilter) error {
return chain.AddFilter(filter)
}
func NewConfigFilterChainManager() IConfigFilterChain {
return newConfigFilterChainManager()
}
func newConfigFilterChainManager() *DefaultConfigFilterChainManager {
return &DefaultConfigFilterChainManager{
configFilterPriorityQueue: make([]IConfigFilter, 0, 2),
}
}
type DefaultConfigFilterChainManager struct {
configFilterPriorityQueue
}
func (m *DefaultConfigFilterChainManager) AddFilter(filter IConfigFilter) error {
return m.configFilterPriorityQueue.addFilter(filter)
}
func (m *DefaultConfigFilterChainManager) GetFilters() []IConfigFilter {
return m.configFilterPriorityQueue
}
func (m *DefaultConfigFilterChainManager) DoFilters(param *vo.ConfigParam) error {
for index := 0; index < len(m.GetFilters()); index++ {
if err := m.GetFilters()[index].DoFilter(param); err != nil {
return err
}
}
return nil
}
func (m *DefaultConfigFilterChainManager) DoFilterByName(param *vo.ConfigParam, name string) error {
for index := 0; index < len(m.GetFilters()); index++ {
if m.GetFilters()[index].GetFilterName() == name {
if err := m.GetFilters()[index].DoFilter(param); err != nil {
return err
}
return nil
}
}
return fmt.Errorf("cannot find the filter[%s]", name)
}
type configFilterPriorityQueue []IConfigFilter
func (c *configFilterPriorityQueue) addFilter(filter IConfigFilter) error {
var pos int = len(*c)
for i := 0; i < len(*c); i++ {
if filter.GetFilterName() == (*c)[i].GetFilterName() {
return nil
}
if filter.GetOrder() < (*c)[i].GetOrder() {
pos = i
break
}
}
if pos == len(*c) {
*c = append((*c)[:], filter)
} else {
temp := append((*c)[:pos], filter)
*c = append(temp[:], (*c)[pos:]...)
}
return nil
}

View File

@ -18,23 +18,20 @@ package http_agent
import ( import (
"net/http" "net/http"
"net/url"
"strings" "strings"
"time" "time"
) )
func delete(client *http.Client, path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) { func delete(client *http.Client, path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {
if len(params) > 0 {
if !strings.HasSuffix(path, "?") { if !strings.HasSuffix(path, "?") {
path = path + "?" path = path + "?"
} }
for key, value := range params { for key, value := range params {
path = path + key + "=" + url.QueryEscape(value) + "&" path = path + key + "=" + value + "&"
} }
if strings.HasSuffix(path, "&") { if strings.HasSuffix(path, "&") {
path = path[:len(path)-1] path = path[:len(path)-1]
} }
}
client.Timeout = time.Millisecond * time.Duration(timeoutMs) client.Timeout = time.Millisecond * time.Duration(timeoutMs)
request, errNew := http.NewRequest(http.MethodDelete, path, nil) request, errNew := http.NewRequest(http.MethodDelete, path, nil)
if errNew != nil { if errNew != nil {

View File

@ -18,21 +18,17 @@ package http_agent
import ( import (
"net/http" "net/http"
"net/url"
"strings" "strings"
"time" "time"
) )
func get(client *http.Client, path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) { func get(client *http.Client, path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {
if !strings.Contains(path, "?") { if !strings.HasSuffix(path, "?") {
path = path + "?" path = path + "?"
} }
for key, value := range params { for key, value := range params {
if !strings.HasSuffix(path, "&") { path = path + key + "=" + value + "&"
path = path + "&"
}
path = path + key + "=" + url.QueryEscape(value) + "&"
} }
if strings.HasSuffix(path, "&") { if strings.HasSuffix(path, "&") {
path = path[:len(path)-1] path = path[:len(path)-1]

View File

@ -17,15 +17,15 @@
package http_agent package http_agent
import ( import (
"io" "io/ioutil"
"net/http" "net/http"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/tls" "github.com/nacos-group/nacos-sdk-go/v2/common/tls"
"github.com/go-errors/errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/pkg/errors"
) )
type HttpAgent struct { type HttpAgent struct {
@ -68,7 +68,7 @@ func (agent *HttpAgent) RequestOnlyResult(method string, path string, header htt
logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],status code error:%d", method, path, util.ToJsonString(header), util.ToJsonString(params), response.StatusCode) logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],status code error:%d", method, path, util.ToJsonString(header), util.ToJsonString(params), response.StatusCode)
return "" return ""
} }
bytes, errRead := io.ReadAll(response.Body) bytes, errRead := ioutil.ReadAll(response.Body)
defer response.Body.Close() defer response.Body.Close()
if errRead != nil { if errRead != nil {
logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],read error:%+v", method, path, util.ToJsonString(header), util.ToJsonString(params), errRead) logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],read error:%+v", method, path, util.ToJsonString(header), util.ToJsonString(params), errRead)

View File

@ -42,7 +42,6 @@ var levelMap = map[string]zapcore.Level{
type Config struct { type Config struct {
Level string Level string
Sampling *SamplingConfig Sampling *SamplingConfig
AppendToStdout bool
LogRollingConfig *lumberjack.Logger LogRollingConfig *lumberjack.Logger
} }
@ -84,14 +83,13 @@ func init() {
EncodeCaller: zapcore.ShortCallerEncoder, EncodeCaller: zapcore.ShortCallerEncoder,
} }
zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig
zapLogger, _ := zapLoggerConfig.Build(zap.AddCaller(), zap.AddCallerSkip(1)) zapLogger, _ := zapLoggerConfig.Build(zap.AddCallerSkip(1))
SetLogger(&NacosLogger{zapLogger.Sugar()}) SetLogger(&NacosLogger{zapLogger.Sugar()})
} }
func BuildLoggerConfig(clientConfig constant.ClientConfig) Config { func BuildLoggerConfig(clientConfig constant.ClientConfig) Config {
loggerConfig := Config{ loggerConfig := Config{
Level: clientConfig.LogLevel, Level: clientConfig.LogLevel,
AppendToStdout: clientConfig.AppendToStdout,
} }
if clientConfig.LogSampling != nil { if clientConfig.LogSampling != nil {
loggerConfig.Sampling = &SamplingConfig{ loggerConfig.Sampling = &SamplingConfig{
@ -110,12 +108,6 @@ func BuildLoggerConfig(clientConfig constant.ClientConfig) Config {
loggerConfig.LogRollingConfig.MaxBackups = logRollingConfig.MaxBackups loggerConfig.LogRollingConfig.MaxBackups = logRollingConfig.MaxBackups
loggerConfig.LogRollingConfig.LocalTime = logRollingConfig.LocalTime loggerConfig.LogRollingConfig.LocalTime = logRollingConfig.LocalTime
loggerConfig.LogRollingConfig.Compress = logRollingConfig.Compress loggerConfig.LogRollingConfig.Compress = logRollingConfig.Compress
} else {
loggerConfig.LogRollingConfig.MaxSize = 100
loggerConfig.LogRollingConfig.MaxAge = 30
loggerConfig.LogRollingConfig.MaxBackups = 5
loggerConfig.LogRollingConfig.LocalTime = true
loggerConfig.LogRollingConfig.Compress = false
} }
return loggerConfig return loggerConfig
} }
@ -133,10 +125,8 @@ func InitNacosLogger(config Config) (Logger, error) {
logLevel := getLogLevel(config.Level) logLevel := getLogLevel(config.Level)
encoder := getEncoder() encoder := getEncoder()
writer := config.getLogWriter() writer := config.getLogWriter()
if config.AppendToStdout { core := zapcore.NewCore(zapcore.NewConsoleEncoder(encoder),
writer = zapcore.NewMultiWriteSyncer(writer, zapcore.AddSync(os.Stdout)) zapcore.NewMultiWriteSyncer(writer, zapcore.AddSync(os.Stdout)), logLevel)
}
core := zapcore.NewCore(zapcore.NewConsoleEncoder(encoder), writer, logLevel)
zaplogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) zaplogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
return &NacosLogger{zaplogger.Sugar()}, nil return &NacosLogger{zaplogger.Sugar()}, nil
} }
@ -163,7 +153,7 @@ func getEncoder() zapcore.EncoderConfig {
} }
} }
// SetLogger sets logger for sdk //SetLogger sets logger for sdk
func SetLogger(log Logger) { func SetLogger(log Logger) {
logLock.Lock() logLock.Lock()
defer logLock.Unlock() defer logLock.Unlock()

View File

@ -40,13 +40,18 @@ func TestGetLogger(t *testing.T) {
// not yet init get default log // not yet init get default log
log := GetLogger() log := GetLogger()
config := Config{ config := Config{
Level: "debug", Level: "degug",
} }
_ = InitLogger(config) _ = InitLogger(config)
// after init logger // after init logger
log2 := GetLogger() log2 := GetLogger()
assert.NotEqual(t, log, log2) assert.NotEqual(t, log, log2)
// the secend init logger
config.Level = "info"
_ = InitLogger(config)
log3 := GetLogger()
assert.NotEqual(t, log2, log3)
reset() reset()
} }
@ -61,6 +66,13 @@ func TestSetLogger(t *testing.T) {
assert.NotEqual(t, log, log2) assert.NotEqual(t, log, log2)
assert.Equal(t, log1, log2) assert.Equal(t, log1, log2)
config := Config{
Level: "degug",
}
_ = InitLogger(config)
// after init logger
log3 := GetLogger()
assert.NotEqual(t, log2, log3)
reset() reset()
} }

View File

@ -1,65 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 monitor
import "github.com/prometheus/client_golang/prometheus"
var (
gaugeMonitorVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "nacos_monitor",
Help: "nacos_monitor",
}, []string{"module", "name"})
histogramMonitorVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "nacos_client_request",
Help: "nacos_client_request",
}, []string{"module", "method", "url", "code"})
)
// register collectors vec
func init() {
prometheus.MustRegister(gaugeMonitorVec, histogramMonitorVec)
}
// get gauge with labels and use gaugeMonitorVec
func GetGaugeWithLabels(labels ...string) prometheus.Gauge {
return gaugeMonitorVec.WithLabelValues(labels...)
}
func GetServiceInfoMapSizeMonitor() prometheus.Gauge {
return GetGaugeWithLabels("serviceInfo", "serviceInfoMapSize")
}
func GetDom2BeatSizeMonitor() prometheus.Gauge {
return GetGaugeWithLabels("dom2Beat", "dom2BeatSize")
}
func GetListenConfigCountMonitor() prometheus.Gauge {
return GetGaugeWithLabels("listenConfig", "listenConfigCount")
}
// get histogram with labels and use histogramMonitorVec
func GetHistogramWithLabels(labels ...string) prometheus.Observer {
return histogramMonitorVec.WithLabelValues(labels...)
}
func GetConfigRequestMonitor(method, url, code string) prometheus.Observer {
return GetHistogramWithLabels("config", method, url, code)
}
func GetNamingRequestMonitor(method, url, code string) prometheus.Observer {
return GetHistogramWithLabels("naming", method, url, code)
}

View File

@ -1,39 +0,0 @@
package monitor
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGaugeMonitor(t *testing.T) {
t.Run("getGaugeWithLabels", func(t *testing.T) {
// will panic because of wrong label count.
defer func() {
r := recover()
assert.NotNil(t, r)
}()
GetGaugeWithLabels("gauge", "test_gauge", "should_not_exist_label")
})
t.Run("serviceInfoMapMonitor", func(t *testing.T) {
monitor := GetServiceInfoMapSizeMonitor()
assert.NotNil(t, monitor)
})
}
func TestHistorgam(t *testing.T) {
t.Run("getHistogram", func(t *testing.T) {
// will panic because of wrong label count.
defer func() {
r := recover()
assert.NotNil(t, r)
}()
GetHistogramWithLabels("histogram", "test_histogram", "should_not_exist_label")
})
t.Run("serviceInfoMapMonitor", func(t *testing.T) {
monitor := GetConfigRequestMonitor("GET", "url", "NA")
assert.NotNil(t, monitor)
})
}

View File

@ -17,8 +17,12 @@
package nacos_server package nacos_server
import ( import (
"context" "crypto/hmac"
"io" "crypto/sha1"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"math/rand" "math/rand"
"net/http" "net/http"
"reflect" "reflect"
@ -28,10 +32,6 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pkg/errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
@ -43,7 +43,7 @@ import (
type NacosServer struct { type NacosServer struct {
sync.RWMutex sync.RWMutex
securityLogin security.SecurityProxy securityLogin security.AuthClient
serverList []constant.ServerConfig serverList []constant.ServerConfig
httpAgent http_agent.IHttpAgent httpAgent http_agent.IHttpAgent
timeoutMs uint64 timeoutMs uint64
@ -51,25 +51,17 @@ type NacosServer struct {
lastSrvRefTime int64 lastSrvRefTime int64
vipSrvRefInterMills int64 vipSrvRefInterMills int64
contextPath string contextPath string
endpointContextPath string
endpointQueryParams string
endpointQueryHeader map[string][]string
clusterName string
currentIndex int32 currentIndex int32
ServerSrcChangeSignal chan struct{} ServerSrcChangeSignal chan struct{}
} }
func NewNacosServer(ctx context.Context, serverList []constant.ServerConfig, clientCfg constant.ClientConfig, httpAgent http_agent.IHttpAgent, timeoutMs uint64, endpoint string, endpointQueryHeader map[string][]string) (*NacosServer, error) { func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.ClientConfig, httpAgent http_agent.IHttpAgent, timeoutMs uint64, endpoint string) (*NacosServer, error) {
return NewNacosServerWithRamCredentialProvider(ctx, serverList, clientCfg, httpAgent, timeoutMs, endpoint, endpointQueryHeader, nil)
}
func NewNacosServerWithRamCredentialProvider(ctx context.Context, serverList []constant.ServerConfig, clientCfg constant.ClientConfig, httpAgent http_agent.IHttpAgent, timeoutMs uint64, endpoint string, endpointQueryHeader map[string][]string, provider security.RamCredentialProvider) (*NacosServer, error) {
severLen := len(serverList) severLen := len(serverList)
if severLen == 0 && endpoint == "" { if severLen == 0 && endpoint == "" {
return &NacosServer{}, errors.New("both serverlist and endpoint are empty") return &NacosServer{}, errors.New("both serverlist and endpoint are empty")
} }
securityLogin := security.NewSecurityProxyWithRamCredentialProvider(clientCfg, serverList, httpAgent, provider) securityLogin := security.NewAuthClient(clientCfg, serverList, httpAgent)
ns := NacosServer{ ns := NacosServer{
serverList: serverList, serverList: serverList,
@ -78,31 +70,32 @@ func NewNacosServerWithRamCredentialProvider(ctx context.Context, serverList []c
timeoutMs: timeoutMs, timeoutMs: timeoutMs,
endpoint: endpoint, endpoint: endpoint,
vipSrvRefInterMills: 10000, vipSrvRefInterMills: 10000,
endpointContextPath: clientCfg.EndpointContextPath,
endpointQueryParams: clientCfg.EndpointQueryParams,
endpointQueryHeader: endpointQueryHeader,
clusterName: clientCfg.ClusterName,
contextPath: clientCfg.ContextPath, contextPath: clientCfg.ContextPath,
ServerSrcChangeSignal: make(chan struct{}, 1), ServerSrcChangeSignal: make(chan struct{}, 1),
} }
if severLen > 0 { if severLen > 0 {
ns.currentIndex = rand.Int31n(int32(severLen)) ns.currentIndex = rand.Int31n(int32(severLen))
} else {
ns.initRefreshSrvIfNeed(ctx)
} }
ns.securityLogin.Login() ns.initRefreshSrvIfNeed()
ns.securityLogin.AutoRefresh(ctx) _, err := securityLogin.Login()
if err != nil {
return &ns, err
}
securityLogin.AutoRefresh()
return &ns, nil return &ns, nil
} }
func (server *NacosServer) callConfigServer(api string, params map[string]string, newHeaders map[string]string, func (server *NacosServer) callConfigServer(api string, params map[string]string, newHeaders map[string]string,
method string, curServer string, contextPath string, timeoutMS uint64) (result string, err error) { method string, curServer string, contextPath string, timeoutMS uint64) (result string, err error) {
start := time.Now()
if contextPath == "" { if contextPath == "" {
contextPath = constant.WEB_CONTEXT contextPath = constant.WEB_CONTEXT
} }
signHeaders := GetSignHeaders(params, newHeaders["secretKey"])
url := curServer + contextPath + api url := curServer + contextPath + api
headers := map[string][]string{} headers := map[string][]string{}
@ -121,16 +114,20 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
return return
} }
headers["RequestId"] = []string{uid.String()} headers["RequestId"] = []string{uid.String()}
headers["Request-Module"] = []string{"Naming"}
headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"} headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"}
headers["Spas-AccessKey"] = []string{newHeaders["accessKey"]}
headers["Timestamp"] = []string{signHeaders["timeStamp"]}
headers["Spas-Signature"] = []string{signHeaders["Spas-Signature"]}
server.InjectSecurityInfo(params)
var response *http.Response var response *http.Response
response, err = server.httpAgent.Request(method, url, headers, timeoutMS, params) response, err = server.httpAgent.Request(method, url, headers, timeoutMS, params)
monitor.GetConfigRequestMonitor(method, url, util.GetStatusCode(response)).Observe(float64(time.Now().Nanosecond() - start.Nanosecond()))
if err != nil { if err != nil {
return return
} }
var bytes []byte var bytes []byte
bytes, err = io.ReadAll(response.Body) bytes, err = ioutil.ReadAll(response.Body)
defer response.Body.Close() defer response.Body.Close()
if err != nil { if err != nil {
return return
@ -145,7 +142,6 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
} }
func (server *NacosServer) callServer(api string, params map[string]string, method string, curServer string, contextPath string) (result string, err error) { func (server *NacosServer) callServer(api string, params map[string]string, method string, curServer string, contextPath string) (result string, err error) {
start := time.Now()
if contextPath == "" { if contextPath == "" {
contextPath = constant.WEB_CONTEXT contextPath = constant.WEB_CONTEXT
} }
@ -165,23 +161,24 @@ func (server *NacosServer) callServer(api string, params map[string]string, meth
headers["Request-Module"] = []string{"Naming"} headers["Request-Module"] = []string{"Naming"}
headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"} headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"}
server.InjectSecurityInfo(params)
var response *http.Response var response *http.Response
response, err = server.httpAgent.Request(method, url, headers, server.timeoutMs, params) response, err = server.httpAgent.Request(method, url, headers, server.timeoutMs, params)
if err != nil { if err != nil {
return return
} }
var bytes []byte var bytes []byte
bytes, err = io.ReadAll(response.Body) bytes, err = ioutil.ReadAll(response.Body)
defer response.Body.Close() defer response.Body.Close()
if err != nil { if err != nil {
return return
} }
result = string(bytes) result = string(bytes)
monitor.GetNamingRequestMonitor(method, api, util.GetStatusCode(response)).Observe(float64(time.Now().Nanosecond() - start.Nanosecond()))
if response.StatusCode == constant.RESPONSE_CODE_SUCCESS { if response.StatusCode == constant.RESPONSE_CODE_SUCCESS {
return return
} else { } else {
err = errors.Errorf("request return error code %d", response.StatusCode) err = errors.New(fmt.Sprintf("request return error code %d", response.StatusCode))
return return
} }
} }
@ -192,7 +189,7 @@ func (server *NacosServer) ReqConfigApi(api string, params map[string]string, he
return "", errors.New("server list is empty") return "", errors.New("server list is empty")
} }
server.InjectSecurityInfo(params, security.BuildConfigResource(params["tenant"], params["group"], params["dataId"])) server.InjectSecurityInfo(params)
//only one server,retry request when error //only one server,retry request when error
var err error var err error
@ -205,6 +202,7 @@ func (server *NacosServer) ReqConfigApi(api string, params map[string]string, he
} }
logger.Errorf("api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s>", api, method, util.ToJsonString(params), err, result) logger.Errorf("api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s>", api, method, util.ToJsonString(params), err, result)
} }
return "", err
} else { } else {
index := rand.Intn(len(srvs)) index := rand.Intn(len(srvs))
for i := 1; i <= len(srvs); i++ { for i := 1; i <= len(srvs); i++ {
@ -216,86 +214,67 @@ func (server *NacosServer) ReqConfigApi(api string, params map[string]string, he
logger.Errorf("[ERROR] api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s> \n", api, method, util.ToJsonString(params), err, result) logger.Errorf("[ERROR] api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s> \n", api, method, util.ToJsonString(params), err, result)
index = (index + i) % len(srvs) index = (index + i) % len(srvs)
} }
return "", err
} }
return "", errors.Wrapf(err, "retry %d times request failed!", constant.REQUEST_DOMAIN_RETRY_TIME)
} }
func (server *NacosServer) ReqApi(api string, params map[string]string, method string, config constant.ClientConfig) (string, error) { func (server *NacosServer) ReqApi(api string, params map[string]string, method string) (string, error) {
srvs := server.serverList srvs := server.serverList
if srvs == nil || len(srvs) == 0 { if srvs == nil || len(srvs) == 0 {
return "", errors.New("server list is empty") return "", errors.New("server list is empty")
} }
server.InjectSecurityInfo(params, security.BuildNamingResource(params["namespaceId"], params["serviceName"], params["groupName"])) server.InjectSecurityInfo(params)
//only one server,retry request when error //only one server,retry request when error
var err error
var result string
if len(srvs) == 1 { if len(srvs) == 1 {
for i := 0; i < constant.REQUEST_DOMAIN_RETRY_TIME; i++ { for i := 0; i < constant.REQUEST_DOMAIN_RETRY_TIME; i++ {
result, err = server.callServer(api, params, method, getAddress(srvs[0]), srvs[0].ContextPath) result, err := server.callServer(api, params, method, getAddress(srvs[0]), srvs[0].ContextPath)
if err == nil { if err == nil {
return result, nil return result, nil
} }
logger.Errorf("api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s>", api, method, util.ToJsonString(params), err, result) logger.Errorf("api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s>", api, method, util.ToJsonString(params), err, result)
} }
return "", errors.New("retry " + strconv.Itoa(constant.REQUEST_DOMAIN_RETRY_TIME) + " times request failed!")
} else { } else {
index := rand.Intn(len(srvs)) index := rand.Intn(len(srvs))
for i := 1; i <= len(srvs); i++ { for i := 1; i <= len(srvs); i++ {
curServer := srvs[index] curServer := srvs[index]
result, err = server.callServer(api, params, method, getAddress(curServer), curServer.ContextPath) result, err := server.callServer(api, params, method, getAddress(curServer), curServer.ContextPath)
if err == nil { if err == nil {
return result, nil return result, nil
} }
logger.Errorf("api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s>", api, method, util.ToJsonString(params), err, result) logger.Errorf("api<%s>,method:<%s>, params:<%s>, call domain error:<%+v> , result:<%s>", api, method, util.ToJsonString(params), err, result)
index = (index + i) % len(srvs) index = (index + i) % len(srvs)
} }
return "", errors.New("retry " + strconv.Itoa(constant.REQUEST_DOMAIN_RETRY_TIME) + " times request failed!")
} }
return "", errors.Wrapf(err, "retry %d times request failed!", constant.REQUEST_DOMAIN_RETRY_TIME)
} }
func (server *NacosServer) initRefreshSrvIfNeed(ctx context.Context) { func (server *NacosServer) initRefreshSrvIfNeed() {
if server.endpoint == "" { if server.endpoint == "" {
return return
} }
server.refreshServerSrvIfNeed()
if len(strings.TrimSpace(server.endpointContextPath)) == 0 {
server.endpointContextPath = "nacos"
}
if len(strings.TrimSpace(server.clusterName)) == 0 {
server.clusterName = "serverlist"
}
urlString := "http://" + server.endpoint + "/" + strings.TrimSpace(server.endpointContextPath) + "/" + strings.TrimSpace(server.clusterName)
if len(strings.TrimSpace(server.endpointQueryParams)) != 0 {
urlString += "?" + server.endpointQueryParams
}
logger.Infof("nacos address server url: <%s>", urlString)
server.refreshServerSrvIfNeed(urlString, server.endpointQueryHeader)
go func() { go func() {
for { for {
select { time.Sleep(time.Duration(1) * time.Second)
case <-ctx.Done(): server.refreshServerSrvIfNeed()
return
default:
time.Sleep(time.Duration(10) * time.Second)
server.refreshServerSrvIfNeed(urlString, server.endpointQueryHeader)
}
} }
}() }()
} }
func (server *NacosServer) refreshServerSrvIfNeed(urlString string, header map[string][]string) { func (server *NacosServer) refreshServerSrvIfNeed() {
if util.CurrentMillis()-server.lastSrvRefTime < server.vipSrvRefInterMills && len(server.serverList) > 0 { if util.CurrentMillis()-server.lastSrvRefTime < server.vipSrvRefInterMills && len(server.serverList) > 0 {
return return
} }
var list []string var list []string
urlString := "http://" + server.endpoint + "/nacos/serverlist"
result := server.httpAgent.RequestOnlyResult(http.MethodGet, urlString, header, server.timeoutMs, nil) result := server.httpAgent.RequestOnlyResult(http.MethodGet, urlString, nil, server.timeoutMs, nil)
list = strings.Split(result, "\n") list = strings.Split(result, "\n")
logger.Infof("http nacos server list: <%s>", result)
var servers []constant.ServerConfig var servers []constant.ServerConfig
contextPath := server.contextPath contextPath := server.contextPath
@ -321,15 +300,10 @@ func (server *NacosServer) refreshServerSrvIfNeed(urlString string, header map[s
if len(servers) > 0 { if len(servers) > 0 {
if !reflect.DeepEqual(server.serverList, servers) { if !reflect.DeepEqual(server.serverList, servers) {
server.Lock() server.Lock()
var serverPrev = server.serverList logger.Infof("server list is updated, old: <%v>,new:<%v>", server.serverList, servers)
logger.Infof("server list is updated, old: <%v>,new:<%v>", serverPrev, servers)
server.serverList = servers server.serverList = servers
if serverPrev != nil {
server.ServerSrcChangeSignal <- struct{}{} server.ServerSrcChangeSignal <- struct{}{}
}
server.lastSrvRefTime = util.CurrentMillis() server.lastSrvRefTime = util.CurrentMillis()
server.securityLogin.UpdateServerList(servers)
server.Unlock() server.Unlock()
} }
@ -341,10 +315,10 @@ func (server *NacosServer) GetServerList() []constant.ServerConfig {
return server.serverList return server.serverList
} }
func (server *NacosServer) InjectSecurityInfo(param map[string]string, resource security.RequestResource) { func (server *NacosServer) InjectSecurityInfo(param map[string]string) {
securityInfo := server.securityLogin.GetSecurityInfo(resource) accessToken := server.securityLogin.GetAccessToken()
for k, v := range securityInfo { if accessToken != "" {
param[k] = v param[constant.KEY_ACCESS_TOKEN] = accessToken
} }
} }
@ -355,6 +329,42 @@ func getAddress(cfg constant.ServerConfig) string {
return cfg.Scheme + "://" + cfg.IpAddr + ":" + strconv.Itoa(int(cfg.Port)) return cfg.Scheme + "://" + cfg.IpAddr + ":" + strconv.Itoa(int(cfg.Port))
} }
func GetSignHeaders(params map[string]string, secretKey string) map[string]string {
resource := ""
if len(params["tenant"]) != 0 {
resource = params["tenant"] + "+" + params["group"]
} else {
resource = params["group"]
}
headers := map[string]string{}
timeStamp := strconv.FormatInt(util.CurrentMillis(), 10)
headers["timeStamp"] = timeStamp
signature := ""
if resource == "" {
signature = signWithhmacSHA1Encrypt(timeStamp, secretKey)
} else {
signature = signWithhmacSHA1Encrypt(resource+"+"+timeStamp, secretKey)
}
headers["Spas-Signature"] = signature
return headers
}
func signWithhmacSHA1Encrypt(encryptText, encryptKey string) string {
//hmac ,use sha1
key := []byte(encryptKey)
mac := hmac.New(sha1.New, key)
mac.Write([]byte(encryptText))
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}
func (server *NacosServer) GetNextServer() (constant.ServerConfig, error) { func (server *NacosServer) GetNextServer() (constant.ServerConfig, error) {
serverLen := len(server.GetServerList()) serverLen := len(server.GetServerList())
if serverLen == 0 { if serverLen == 0 {

View File

@ -17,12 +17,8 @@
package nacos_server package nacos_server
import ( import (
"context"
"testing" "testing"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -50,138 +46,3 @@ func Test_getAddressWithoutScheme(t *testing.T) {
assert.Equal(t, "https://console.nacos.io:80", getAddress(serverConfigTest)) assert.Equal(t, "https://console.nacos.io:80", getAddress(serverConfigTest))
} }
func buildNacosServer(clientConfig constant.ClientConfig) (*NacosServer, error) {
return NewNacosServer(context.Background(),
[]constant.ServerConfig{*constant.NewServerConfig("http://console.nacos.io", 80)},
clientConfig,
&http_agent.HttpAgent{},
1000,
"",
nil)
}
func TestNacosServer_InjectSignForNamingHttp_NoAk(t *testing.T) {
clientConfig := constant.ClientConfig{
AccessKey: "",
SecretKey: "",
}
server, err := buildNacosServer(clientConfig)
if err != nil {
t.FailNow()
}
param := make(map[string]string, 4)
param["serviceName"] = "s-0"
param["groupName"] = "g-0"
server.InjectSecurityInfo(param, security.BuildNamingResource(param["namespaceId"], param["groupName"], param["serviceName"]))
assert.Empty(t, param["ak"])
assert.Empty(t, param["data"])
assert.Empty(t, param["signature"])
}
func TestNacosServer_InjectSignForNamingHttp_WithGroup(t *testing.T) {
clientConfig := constant.ClientConfig{
AccessKey: "123",
SecretKey: "321",
}
server, err := buildNacosServer(clientConfig)
if err != nil {
t.FailNow()
}
param := make(map[string]string, 4)
param["serviceName"] = "s-0"
param["groupName"] = "g-0"
server.InjectSecurityInfo(param, security.BuildNamingResource(param["namespaceId"], param["groupName"], param["serviceName"]))
assert.Equal(t, "123", param["ak"])
assert.Contains(t, param["data"], "@@g-0@@s-0")
_, has := param["signature"]
assert.True(t, has)
}
func TestNacosServer_InjectSignForNamingHttp_WithoutGroup(t *testing.T) {
clientConfig := constant.ClientConfig{
AccessKey: "123",
SecretKey: "321",
}
server, err := buildNacosServer(clientConfig)
if err != nil {
t.FailNow()
}
param := make(map[string]string, 4)
param["serviceName"] = "s-0"
server.InjectSecurityInfo(param, security.BuildNamingResource(param["namespaceId"], param["groupName"], param["serviceName"]))
assert.Equal(t, "123", param["ak"])
assert.NotContains(t, param["data"], "@@g-0@@s-0")
assert.Contains(t, param["data"], "@@s-0")
_, has := param["signature"]
assert.True(t, has)
}
func TestNacosServer_InjectSignForNamingHttp_WithoutServiceName(t *testing.T) {
clientConfig := constant.ClientConfig{
AccessKey: "123",
SecretKey: "321",
}
server, err := buildNacosServer(clientConfig)
if err != nil {
t.FailNow()
}
param := make(map[string]string, 4)
param["groupName"] = "g-0"
server.InjectSecurityInfo(param, security.BuildNamingResource(param["namespaceId"], param["groupName"], param["serviceName"]))
assert.Equal(t, "123", param["ak"])
assert.Contains(t, param["data"], "@@")
assert.Regexp(t, "\\d+", param["data"])
_, has := param["signature"]
assert.True(t, has)
}
func TestNacosServer_InjectSignForNamingHttp_WithoutServiceNameAndGroup(t *testing.T) {
clientConfig := constant.ClientConfig{
AccessKey: "123",
SecretKey: "321",
}
server, err := buildNacosServer(clientConfig)
if err != nil {
t.FailNow()
}
param := make(map[string]string, 4)
server.InjectSecurityInfo(param, security.BuildNamingResource(param["namespaceId"], param["serviceName"], param["groupName"]))
assert.Equal(t, "123", param["ak"])
assert.NotContains(t, param["data"], "@@")
assert.Regexp(t, "\\d+", param["data"])
_, has := param["signature"]
assert.True(t, has)
}
func TestNacosServer_UpdateServerListForSecurityLogin(t *testing.T) {
endpoint := "console.nacos.io:80"
clientConfig := constant.ClientConfig{
Username: "nacos",
Password: "nacos",
NamespaceId: "namespace_1",
Endpoint: endpoint,
EndpointContextPath: "nacos",
ClusterName: "serverlist",
AppendToStdout: true,
}
server, err := NewNacosServer(context.Background(),
nil,
clientConfig,
&http_agent.HttpAgent{},
1000,
endpoint,
nil)
if err != nil {
t.FailNow()
}
nacosAuthClient := server.securityLogin.Clients[0]
client, ok := nacosAuthClient.(*security.NacosAuthClient)
assert.True(t, ok)
assert.Equal(t, server.GetServerList(), client.GetServerList())
}

View File

@ -55,5 +55,5 @@ func (c *Connection) getAbandon() bool {
} }
func (c *Connection) close() { func (c *Connection) close() {
_ = c.conn.Close() c.conn.Close()
} }

View File

@ -18,21 +18,15 @@ package rpc
import ( import (
"context" "context"
"crypto/tls"
"crypto/x509"
"encoding/json" "encoding/json"
"fmt" "errors"
"google.golang.org/grpc/credentials"
"io" "io"
"log"
"os" "os"
"strconv" "strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pkg/errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
@ -46,26 +40,23 @@ import (
type GrpcClient struct { type GrpcClient struct {
*RpcClient *RpcClient
*constant.TLSConfig
} }
func NewGrpcClient(ctx context.Context, clientName string, nacosServer *nacos_server.NacosServer, tlsConfig *constant.TLSConfig) *GrpcClient { func NewGrpcClient(clientName string, nacosServer *nacos_server.NacosServer) *GrpcClient {
rpcClient := &GrpcClient{ rpcClient := &GrpcClient{
&RpcClient{ &RpcClient{
ctx: ctx, Name: clientName,
name: clientName,
labels: make(map[string]string, 8), labels: make(map[string]string, 8),
rpcClientStatus: INITIALIZED, rpcClientStatus: INITIALIZED,
eventChan: make(chan ConnectionEvent, 1), eventChan: make(chan ConnectionEvent),
reconnectionChan: make(chan ReconnectContext, 1), reconnectionChan: make(chan ReconnectContext, 1),
lastActiveTimeStamp: time.Now(),
nacosServer: nacosServer, nacosServer: nacosServer,
serverRequestHandlerMapping: make(map[string]ServerRequestHandlerMapping, 8),
mux: new(sync.Mutex), mux: new(sync.Mutex),
}, tlsConfig, },
} }
rpcClient.RpcClient.lastActiveTimestamp.Store(time.Now())
rpcClient.executeClient = rpcClient rpcClient.executeClient = rpcClient
listeners := make([]IConnectionEventListener, 0, 8)
rpcClient.connectionEventListeners.Store(listeners)
return rpcClient return rpcClient
} }
@ -93,49 +84,6 @@ func getInitialConnWindowSize() int32 {
return int32(initialConnWindowSize) return int32(initialConnWindowSize)
} }
func getTLSCredentials(tlsConfig *constant.TLSConfig, serverInfo ServerInfo) credentials.TransportCredentials {
logger.Infof("build tls config for connecting to server %s,tlsConfig = %s", serverInfo.serverIp, tlsConfig)
certPool, err := x509.SystemCertPool()
if err != nil {
log.Fatalf("load root cert pool fail : %v", err)
}
if len(tlsConfig.CaFile) != 0 {
cert, err := os.ReadFile(tlsConfig.CaFile)
if err != nil {
fmt.Errorf("err, %v", err)
}
if ok := certPool.AppendCertsFromPEM(cert); !ok {
fmt.Errorf("failed to append ca certs")
}
}
config := tls.Config{
InsecureSkipVerify: tlsConfig.TrustAll,
RootCAs: certPool,
Certificates: []tls.Certificate{},
}
if len(tlsConfig.CertFile) != 0 && len(tlsConfig.KeyFile) != 0 {
cert, err := tls.LoadX509KeyPair(tlsConfig.CertFile, tlsConfig.KeyFile)
if err != nil {
log.Fatalf("load cert fail : %v", err)
}
config.Certificates = append(config.Certificates, cert)
}
credentials := credentials.NewTLS(&config)
return credentials
}
func getInitialGrpcTimeout() int32 {
initialGrpcTimeout, err := strconv.Atoi(os.Getenv("nacos.remote.client.grpc.timeout"))
if err != nil {
return constant.DEFAULT_TIMEOUT_MILLS
}
return int32(initialGrpcTimeout)
}
func getKeepAliveTimeMillis() keepalive.ClientParameters { func getKeepAliveTimeMillis() keepalive.ClientParameters {
keepAliveTimeMillisInt, err := strconv.Atoi(os.Getenv("nacos.remote.grpc.keep.alive.millis")) keepAliveTimeMillisInt, err := strconv.Atoi(os.Getenv("nacos.remote.grpc.keep.alive.millis"))
var keepAliveTime time.Duration var keepAliveTime time.Duration
@ -158,75 +106,35 @@ func (c *GrpcClient) createNewConnection(serverInfo ServerInfo) (*grpc.ClientCon
opts = append(opts, grpc.WithInsecure()) opts = append(opts, grpc.WithInsecure())
opts = append(opts, grpc.WithInitialWindowSize(getInitialWindowSize())) opts = append(opts, grpc.WithInitialWindowSize(getInitialWindowSize()))
opts = append(opts, grpc.WithInitialConnWindowSize(getInitialConnWindowSize())) opts = append(opts, grpc.WithInitialConnWindowSize(getInitialConnWindowSize()))
c.getEnvTLSConfig(c.TLSConfig) rpcPort := serverInfo.serverPort + c.rpcPortOffset()
if c.TLSConfig.Enable {
logger.Infof(" tls enable ,trying to connection to server %s with tls config %s", serverInfo.serverIp, c.TLSConfig)
opts = append(opts, grpc.WithTransportCredentials(getTLSCredentials(c.TLSConfig, serverInfo)))
}
rpcPort := serverInfo.serverGrpcPort
if rpcPort == 0 {
rpcPort = serverInfo.serverPort + c.rpcPortOffset()
}
return grpc.Dial(serverInfo.serverIp+":"+strconv.FormatUint(rpcPort, 10), opts...) return grpc.Dial(serverInfo.serverIp+":"+strconv.FormatUint(rpcPort, 10), opts...)
} }
func (c *GrpcClient) getEnvTLSConfig(config *constant.TLSConfig) {
logger.Infof("check tls config ", config)
if config.Appointed == true {
return
}
logger.Infof("try to get tls config from env")
enableTls, err := strconv.ParseBool(os.Getenv("nacos_remote_client_rpc_tls_enable"))
if err == nil {
config.Enable = enableTls
logger.Infof("get tls config from env key = enableTls value = %s", enableTls)
}
if enableTls != true {
logger.Infof(" tls config from env is not enable")
return
}
trustAll, err := strconv.ParseBool(os.Getenv("nacos_remote_client_rpc_tls_trustAll"))
if err == nil {
config.TrustAll = trustAll
logger.Infof("get tls config from env key = trustAll value = %s", trustAll)
}
config.CaFile = os.Getenv("nacos_remote_client_rpc_tls_trustCollectionChainPath")
logger.Infof("get tls config from env key = trustCollectionChainPath value = %s", config.CaFile)
config.CertFile = os.Getenv("nacos_remote_client_rpc_tls_certChainFile")
logger.Infof("get tls config from env key = certChainFile value = %s", config.CertFile)
config.KeyFile = os.Getenv("nacos_remote_client_rpc_tls_certPrivateKey")
logger.Infof("get tls config from env key = certPrivateKey value = %s", config.KeyFile)
}
func (c *GrpcClient) connectToServer(serverInfo ServerInfo) (IConnection, error) { func (c *GrpcClient) connectToServer(serverInfo ServerInfo) (IConnection, error) {
var client nacos_grpc_service.RequestClient var client nacos_grpc_service.RequestClient
var biStreamClient nacos_grpc_service.BiRequestStreamClient var biStreamClient nacos_grpc_service.BiRequestStreamClient
conn, err := c.createNewConnection(serverInfo) conn, err := c.createNewConnection(serverInfo)
if err != nil { if err != nil {
return nil, errors.Errorf("grpc create new connection failed , err:%v", err) return nil, err
} }
client = nacos_grpc_service.NewRequestClient(conn) client = nacos_grpc_service.NewRequestClient(conn)
response, err := serverCheck(client) response, err := serverCheck(client)
if err != nil { if err != nil {
_ = conn.Close() conn.Close()
return nil, errors.Errorf("server check request failed , err:%v", err) return nil, err
} }
serverCheckResponse := response.(*rpc_response.ServerCheckResponse)
biStreamClient = nacos_grpc_service.NewBiRequestStreamClient(conn) biStreamClient = nacos_grpc_service.NewBiRequestStreamClient(conn)
serverCheckResponse := response.(*rpc_response.ServerCheckResponse)
biStreamRequestClient, err := biStreamClient.RequestBiStream(context.Background()) biStreamRequestClient, err := biStreamClient.RequestBiStream(context.Background())
if err != nil {
return nil, errors.Errorf("create biStreamRequestClient failed , err:%v", err)
}
grpcConn := NewGrpcConnection(serverInfo, serverCheckResponse.ConnectionId, conn, client, biStreamRequestClient) grpcConn := NewGrpcConnection(serverInfo, serverCheckResponse.ConnectionId, conn, client, biStreamRequestClient)
c.bindBiRequestStream(biStreamRequestClient, grpcConn) c.bindBiRequestStream(biStreamRequestClient, grpcConn)
err = c.sendConnectionSetupRequest(grpcConn) err = c.sendConnectionSetupRequest(grpcConn)
return grpcConn, err return grpcConn, err
@ -240,7 +148,7 @@ func (c *GrpcClient) sendConnectionSetupRequest(grpcConn *GrpcConnection) error
csr.ClientAbilities = c.clientAbilities csr.ClientAbilities = c.clientAbilities
err := grpcConn.biStreamSend(convertRequest(csr)) err := grpcConn.biStreamSend(convertRequest(csr))
if err != nil { if err != nil {
logger.Warnf("send connectionSetupRequest error:%v", err) logger.Warnf("Send ConnectionSetupRequest error:%+v", err)
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
return err return err
@ -251,7 +159,7 @@ func (c *GrpcClient) getConnectionType() ConnectionType {
} }
func (c *GrpcClient) rpcPortOffset() uint64 { func (c *GrpcClient) rpcPortOffset() uint64 {
return constant.RpcPortOffset return 1000
} }
func (c *GrpcClient) bindBiRequestStream(streamClient nacos_grpc_service.BiRequestStream_RequestBiStreamClient, grpcConn *GrpcConnection) { func (c *GrpcClient) bindBiRequestStream(streamClient nacos_grpc_service.BiRequestStream_RequestBiStreamClient, grpcConn *GrpcConnection) {
@ -259,7 +167,6 @@ func (c *GrpcClient) bindBiRequestStream(streamClient nacos_grpc_service.BiReque
for { for {
select { select {
case <-streamClient.Context().Done(): case <-streamClient.Context().Done():
logger.Warnf("connectionId %s stream client close", grpcConn.getConnectionId())
return return
default: default:
payload, err := streamClient.Recv() payload, err := streamClient.Recv()
@ -268,21 +175,22 @@ func (c *GrpcClient) bindBiRequestStream(streamClient nacos_grpc_service.BiReque
abandon := grpcConn.getAbandon() abandon := grpcConn.getAbandon()
if c.IsRunning() && !abandon { if c.IsRunning() && !abandon {
if err == io.EOF { if err == io.EOF {
logger.Infof("connectionId %s request stream onCompleted, switch server", grpcConn.getConnectionId()) logger.Infof("%s Request stream onCompleted, switch server", grpcConn.getConnectionId())
} else { } else {
logger.Errorf("connectionId %s request stream error, switch server, error=%v", grpcConn.getConnectionId(), err) logger.Errorf("%s Request stream error, switch server, error=%+v", grpcConn.getConnectionId(), err)
} }
if atomic.CompareAndSwapInt32((*int32)(&c.rpcClientStatus), int32(RUNNING), int32(UNHEALTHY)) { if atomic.CompareAndSwapInt32((*int32)(&c.rpcClientStatus), int32(RUNNING), int32(UNHEALTHY)) {
c.switchServerAsync(ServerInfo{}, false) c.switchServerAsync(ServerInfo{}, false)
return return
} }
} else { } else {
logger.Errorf("connectionId %s received error event, isRunning:%v, isAbandon=%v, error=%v", grpcConn.getConnectionId(), running, abandon, err) logger.Infof("%s received error event, isRunning:%v, isAbandon=%v, error=%+v", grpcConn.getConnectionId(), running, abandon, err)
return return
} }
} else { } else {
c.handleServerRequest(payload, grpcConn) c.handleServerRequest(payload, grpcConn)
} }
} }
} }
}() }()
@ -290,10 +198,8 @@ func (c *GrpcClient) bindBiRequestStream(streamClient nacos_grpc_service.BiReque
func serverCheck(client nacos_grpc_service.RequestClient) (rpc_response.IResponse, error) { func serverCheck(client nacos_grpc_service.RequestClient) (rpc_response.IResponse, error) {
var response rpc_response.ServerCheckResponse var response rpc_response.ServerCheckResponse
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(getInitialGrpcTimeout())*time.Millisecond)
defer cancel()
for i := 0; i <= 30; i++ { for i := 0; i <= 30; i++ {
payload, err := client.Request(ctx, convertRequest(rpc_request.NewServerCheckRequest())) payload, err := client.Request(context.Background(), convertRequest(rpc_request.NewServerCheckRequest()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -319,14 +225,12 @@ func (c *GrpcClient) handleServerRequest(p *nacos_grpc_service.Payload, grpcConn
client := c.GetRpcClient() client := c.GetRpcClient()
payLoadType := p.GetMetadata().GetType() payLoadType := p.GetMetadata().GetType()
handlerMapping, ok := client.serverRequestHandlerMapping.Load(payLoadType) mapping, ok := client.serverRequestHandlerMapping[payLoadType]
if !ok { if !ok {
logger.Errorf("%s Unsupported payload type", grpcConn.getConnectionId()) logger.Errorf("%s Unsupported payload type", grpcConn.getConnectionId())
return return
} }
mapping := handlerMapping.(ServerRequestHandlerMapping)
serverRequest := mapping.serverRequest() serverRequest := mapping.serverRequest()
err := json.Unmarshal(p.GetBody().Value, serverRequest) err := json.Unmarshal(p.GetBody().Value, serverRequest)
if err != nil { if err != nil {

View File

@ -18,17 +18,18 @@ package rpc
import ( import (
"context" "context"
"encoding/json"
"errors"
"fmt"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
nacos_grpc_service "github.com/nacos-group/nacos-sdk-go/v2/api/grpc"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/pkg/errors"
"google.golang.org/protobuf/types/known/anypb"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/golang/protobuf/ptypes/any"
nacos_grpc_service "github.com/nacos-group/nacos-sdk-go/v2/api/grpc"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@ -57,18 +58,18 @@ func (g *GrpcConnection) request(request rpc_request.IRequest, timeoutMills int6
defer cancel() defer cancel()
responsePayload, err := g.client.Request(ctx, p) responsePayload, err := g.client.Request(ctx, p)
if err != nil { if err != nil {
logger.Debugf("%s grpc request nacos server failed, request=%+v, err=%v ", g.getConnectionId(), p, err)
return nil, err return nil, err
} }
responseFunc, ok := rpc_response.ClientResponseMapping[responsePayload.Metadata.GetType()] responseFunc, ok := rpc_response.ClientResponseMapping[responsePayload.Metadata.GetType()]
if !ok {
return nil, errors.Errorf("request:%s,unsupported response type:%s", request.GetRequestType(),
responsePayload.Metadata.GetType())
}
logger.Debugf("%s grpc request nacos server success, request=%+v, response=%s", g.getConnectionId(), p, string(responsePayload.GetBody().Value)) if !ok {
return rpc_response.InnerResponseJsonUnmarshal(responsePayload.GetBody().Value, responseFunc) return nil, errors.New(fmt.Sprintf("request:%s,unsupported response type:%s", request.GetRequestType(),
responsePayload.Metadata.GetType()))
}
response := responseFunc()
err = json.Unmarshal(responsePayload.GetBody().Value, response)
return response, err
} }
func (g *GrpcConnection) close() { func (g *GrpcConnection) close() {
@ -87,7 +88,7 @@ func convertRequest(r rpc_request.IRequest) *nacos_grpc_service.Payload {
} }
return &nacos_grpc_service.Payload{ return &nacos_grpc_service.Payload{
Metadata: &Metadata, Metadata: &Metadata,
Body: &anypb.Any{Value: []byte(r.GetBody(r))}, Body: &any.Any{Value: []byte(r.GetBody(r))},
} }
} }
@ -98,6 +99,6 @@ func convertResponse(r rpc_response.IResponse) *nacos_grpc_service.Payload {
} }
return &nacos_grpc_service.Payload{ return &nacos_grpc_service.Payload{
Metadata: &Metadata, Metadata: &Metadata,
Body: &anypb.Any{Value: []byte(r.GetBody())}, Body: &any.Any{Value: []byte(r.GetBody())},
} }
} }

View File

@ -17,18 +17,14 @@
package rpc package rpc
import ( import (
"context" "errors"
"fmt" "fmt"
"math" "math"
"os"
"reflect" "reflect"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pkg/errors"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server"
@ -93,22 +89,20 @@ type IRpcClient interface {
type ServerInfo struct { type ServerInfo struct {
serverIp string serverIp string
serverPort uint64 serverPort uint64
serverGrpcPort uint64
} }
type RpcClient struct { type RpcClient struct {
ctx context.Context Name string
name string
labels map[string]string labels map[string]string
currentConnection IConnection currentConnection IConnection
rpcClientStatus RpcClientStatus rpcClientStatus RpcClientStatus
eventChan chan ConnectionEvent eventChan chan ConnectionEvent
reconnectionChan chan ReconnectContext reconnectionChan chan ReconnectContext
connectionEventListeners atomic.Value connectionEventListeners []IConnectionEventListener
lastActiveTimestamp atomic.Value lastActiveTimeStamp time.Time
executeClient IRpcClient executeClient IRpcClient
nacosServer *nacos_server.NacosServer nacosServer *nacos_server.NacosServer
serverRequestHandlerMapping sync.Map serverRequestHandlerMapping map[string]ServerRequestHandlerMapping
mux *sync.Mutex mux *sync.Mutex
clientAbilities rpc_request.ClientAbilities clientAbilities rpc_request.ClientAbilities
Tenant string Tenant string
@ -150,31 +144,17 @@ func getClient(clientName string) IRpcClient {
return clientMap[clientName] return clientMap[clientName]
} }
func CreateClient(ctx context.Context, clientName string, connectionType ConnectionType, labels map[string]string, nacosServer *nacos_server.NacosServer, tlsConfig *constant.TLSConfig, appConnLabels map[string]string) (IRpcClient, error) { func CreateClient(clientName string, connectionType ConnectionType, labels map[string]string, nacosServer *nacos_server.NacosServer) (IRpcClient, error) {
cMux.Lock() cMux.Lock()
defer cMux.Unlock() defer cMux.Unlock()
if _, ok := clientMap[clientName]; !ok { if _, ok := clientMap[clientName]; !ok {
logger.Infof("init rpc client for name ", clientName)
var rpcClient IRpcClient var rpcClient IRpcClient
if GRPC == connectionType { if GRPC == connectionType {
rpcClient = NewGrpcClient(ctx, clientName, nacosServer, tlsConfig) rpcClient = NewGrpcClient(clientName, nacosServer)
} }
if rpcClient == nil { if rpcClient == nil {
return nil, errors.New("unsupported connection type") return nil, errors.New("unsupported connection type")
} }
logger.Infof("get app conn labels from client config %v ", appConnLabels)
appConnLabelsEnv := getAppLabelsFromEnv()
logger.Infof("get app conn labels from env %v ", appConnLabelsEnv)
appConnLabelsFinal := mergerAppLabels(appConnLabels, appConnLabelsEnv)
logger.Infof("final app conn labels : %v ", appConnLabelsFinal)
appConnLabelsFinal = addPrefixForEachKey(appConnLabelsFinal, "app_")
if len(appConnLabelsFinal) != 0 {
rpcClient.putAllLabels(appConnLabelsFinal)
}
rpcClient.putAllLabels(labels) rpcClient.putAllLabels(labels)
clientMap[clientName] = rpcClient clientMap[clientName] = rpcClient
return rpcClient, nil return rpcClient, nil
@ -182,92 +162,6 @@ func CreateClient(ctx context.Context, clientName string, connectionType Connect
return clientMap[clientName], nil return clientMap[clientName], nil
} }
func mergerAppLabels(appLabelsAppointed map[string]string, appLabelsEnv map[string]string) map[string]string {
preferred := strings.ToLower(os.Getenv("nacos_app_conn_labels_preferred"))
var preferFirst bool
if preferred != "env" {
preferFirst = true
} else {
preferFirst = false
}
return mergeMaps(appLabelsAppointed, appLabelsEnv, preferFirst)
}
func mergeMaps(map1, map2 map[string]string, preferFirst bool) map[string]string {
result := make(map[string]string, 8)
for k, v := range map1 {
result[k] = v
}
for k, v := range map2 {
_, ok := map1[k]
if preferFirst && ok {
continue
}
result[k] = v
}
return result
}
func getAppLabelsFromEnv() map[string]string {
configMap := make(map[string]string, 8)
// nacos_config_gray_label
grayLabel := os.Getenv("nacos_config_gray_label")
if grayLabel != "" {
configMap["nacos_config_gray_label"] = grayLabel
}
// nacos_app_conn_labels
connLabels := os.Getenv("nacos_app_conn_labels")
if connLabels != "" {
labelsMap := parseLabels(connLabels)
for k, v := range labelsMap {
configMap[k] = v
}
}
return configMap
}
func parseLabels(rawLabels string) map[string]string {
if strings.TrimSpace(rawLabels) == "" {
return make(map[string]string, 2)
}
resultMap := make(map[string]string, 2)
labels := strings.Split(rawLabels, ",")
for _, label := range labels {
if strings.TrimSpace(label) != "" {
kv := strings.Split(label, "=")
if len(kv) == 2 {
resultMap[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
} else {
fmt.Println("unknown label format:", label)
}
}
}
return resultMap
}
func addPrefixForEachKey(m map[string]string, prefix string) map[string]string {
if len(m) == 0 {
return m
}
newMap := make(map[string]string, len(m))
for k, v := range m {
if strings.TrimSpace(k) != "" {
newKey := prefix + k
newMap[newKey] = v
}
}
return newMap
}
func (r *RpcClient) Start() { func (r *RpcClient) Start() {
if ok := atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), (int32)(INITIALIZED), (int32)(STARTING)); !ok { if ok := atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), (int32)(INITIALIZED), (int32)(STARTING)); !ok {
return return
@ -275,12 +169,8 @@ func (r *RpcClient) Start() {
r.registerServerRequestHandlers() r.registerServerRequestHandlers()
go func() { go func() {
for { for {
select { event := <-r.eventChan
case event := <-r.eventChan:
r.notifyConnectionEvent(event) r.notifyConnectionEvent(event)
case <-r.ctx.Done():
return
}
} }
}() }()
@ -294,13 +184,12 @@ func (r *RpcClient) Start() {
for _, v := range r.nacosServer.GetServerList() { for _, v := range r.nacosServer.GetServerList() {
if rc.serverInfo.serverIp == v.IpAddr { if rc.serverInfo.serverIp == v.IpAddr {
rc.serverInfo.serverPort = v.Port rc.serverInfo.serverPort = v.Port
rc.serverInfo.serverGrpcPort = v.GrpcPort
serverExist = true serverExist = true
break break
} }
} }
if !serverExist { if !serverExist {
logger.Infof("%s recommend server is not in server list, ignore recommend server %+v", r.name, rc.serverInfo) logger.Infof("%s recommend server is not in server list, ignore recommend server %+v", r.Name, rc.serverInfo)
rc.serverInfo = ServerInfo{} rc.serverInfo = ServerInfo{}
} }
} }
@ -309,8 +198,6 @@ func (r *RpcClient) Start() {
r.healthCheck(timer) r.healthCheck(timer)
case <-r.nacosServer.ServerSrcChangeSignal: case <-r.nacosServer.ServerSrcChangeSignal:
r.notifyServerSrvChange() r.notifyServerSrvChange()
case <-r.ctx.Done():
return
} }
} }
}() }()
@ -321,33 +208,29 @@ func (r *RpcClient) Start() {
startUpRetryTimes-- startUpRetryTimes--
serverInfo, err := r.nextRpcServer() serverInfo, err := r.nextRpcServer()
if err != nil { if err != nil {
logger.Errorf("[RpcClient.nextRpcServer],err:%v", err) logger.Errorf("[RpcClient.nextRpcServer],err:%+v", err)
break break
} }
logger.Infof("[RpcClient.Start] %s try to connect to server on start up, server: %+v", r.name, serverInfo) logger.Infof("[RpcClient.Start] %+v try to connect to server on start up, server: %+v", r.Name, serverInfo)
if connection, err := r.executeClient.connectToServer(serverInfo); err != nil { if connection, err := r.executeClient.connectToServer(serverInfo); err != nil {
logger.Warnf("[RpcClient.Start] %s fail to connect to server on start up, error message=%v, "+ logger.Warnf("[RpcClient.Start] %+v fail to connect to server on start up, error message=%+v, "+
"start up retry times left=%d", r.name, err.Error(), startUpRetryTimes) "start up retry times left=%+v", r.Name, err.Error(), startUpRetryTimes)
} else { } else {
currentConnection = connection currentConnection = connection
break break
} }
} }
if currentConnection != nil { if currentConnection != nil {
logger.Infof("%s success to connect to server %+v on start up, connectionId=%s", r.name, logger.Infof("%s success to connect to server %+v on start up, connectionId=%v", r.Name,
currentConnection.getServerInfo(), currentConnection.getConnectionId()) currentConnection.getServerInfo(), currentConnection.getConnectionId())
r.currentConnection = currentConnection r.currentConnection = currentConnection
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING)) atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING))
r.notifyConnectionChange(CONNECTED) r.eventChan <- ConnectionEvent{eventType: CONNECTED}
} else { } else {
r.switchServerAsync(ServerInfo{}, false) r.switchServerAsync(ServerInfo{}, false)
} }
} }
func (r *RpcClient) notifyConnectionChange(eventType ConnectionStatus) {
r.eventChan <- ConnectionEvent{eventType: eventType}
}
func (r *RpcClient) notifyServerSrvChange() { func (r *RpcClient) notifyServerSrvChange() {
if r.currentConnection == nil { if r.currentConnection == nil {
r.switchServerAsync(ServerInfo{}, false) r.switchServerAsync(ServerInfo{}, false)
@ -387,22 +270,19 @@ func (r *RpcClient) RegisterServerRequestHandler(request func() rpc_request.IReq
requestType := request().GetRequestType() requestType := request().GetRequestType()
if handler == nil || requestType == "" { if handler == nil || requestType == "" {
logger.Errorf("%s register server push request handler "+ logger.Errorf("%s register server push request handler "+
"missing required parameters,request:%+v handler:%+v", r.name, requestType, handler.Name()) "missing required parameters,request:%+v handler", r.Name, requestType, handler)
return return
} }
logger.Debugf("%s register server push request:%s handler:%+v", r.name, requestType, handler.Name()) logger.Infof("%s register server push request:%s handler", r.Name, requestType, handler)
r.serverRequestHandlerMapping.Store(requestType, ServerRequestHandlerMapping{ r.serverRequestHandlerMapping[requestType] = ServerRequestHandlerMapping{
serverRequest: request, serverRequest: request,
handler: handler, handler: handler,
}) }
} }
func (r *RpcClient) RegisterConnectionListener(listener IConnectionEventListener) { func (r *RpcClient) RegisterConnectionListener(listener IConnectionEventListener) {
logger.Debugf("%s register connection listener [%+v] to current client", r.name, reflect.TypeOf(listener)) logger.Infof("%s register connection listener [%+v] to current client", r.Name, reflect.TypeOf(listener))
listeners := r.connectionEventListeners.Load() r.connectionEventListeners = append(r.connectionEventListeners, listener)
connectionEventListeners := listeners.([]IConnectionEventListener)
connectionEventListeners = append(connectionEventListeners, listener)
r.connectionEventListeners.Store(connectionEventListeners)
} }
func (r *RpcClient) switchServerAsync(recommendServerInfo ServerInfo, onRequestFail bool) { func (r *RpcClient) switchServerAsync(recommendServerInfo ServerInfo, onRequestFail bool) {
@ -411,9 +291,8 @@ func (r *RpcClient) switchServerAsync(recommendServerInfo ServerInfo, onRequestF
func (r *RpcClient) reconnect(serverInfo ServerInfo, onRequestFail bool) { func (r *RpcClient) reconnect(serverInfo ServerInfo, onRequestFail bool) {
if onRequestFail && r.sendHealthCheck() { if onRequestFail && r.sendHealthCheck() {
logger.Infof("%s server check success, currentServer is %+v", r.name, r.currentConnection.getServerInfo()) logger.Infof("%s server check success, currentServer is %+v", r.Name, r.currentConnection.getServerInfo())
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING)) atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING))
r.notifyConnectionChange(CONNECTED)
return return
} }
var ( var (
@ -423,38 +302,38 @@ func (r *RpcClient) reconnect(serverInfo ServerInfo, onRequestFail bool) {
) )
if (serverInfo == ServerInfo{}) { if (serverInfo == ServerInfo{}) {
serverInfoFlag = true serverInfoFlag = true
logger.Infof("%s try to re connect to a new server, server is not appointed, will choose a random server.", r.name) logger.Infof("%s try to re connect to a new server, server is not appointed, will choose a random server.", r.Name)
} }
for !r.isShutdown() { for !r.isShutdown() {
if serverInfoFlag { if serverInfoFlag {
serverInfo, err = r.nextRpcServer() serverInfo, err = r.nextRpcServer()
if err != nil { if err != nil {
logger.Errorf("[RpcClient.nextRpcServer],err:%v", err) logger.Errorf("[RpcClient.nextRpcServer],err:%+v", err)
break break
} }
} }
connectionNew, err := r.executeClient.connectToServer(serverInfo) connectionNew, err := r.executeClient.connectToServer(serverInfo)
if connectionNew != nil && err == nil { if connectionNew != nil && err == nil {
logger.Infof("%s success to connect a server %+v, connectionId=%s", r.name, serverInfo, logger.Infof("%s success to connect a server %+v, connectionId=%s", r.Name, serverInfo,
connectionNew.getConnectionId()) connectionNew.getConnectionId())
if r.currentConnection != nil { if r.currentConnection != nil {
logger.Infof("%s abandon prev connection, server is %+v, connectionId is %s", r.name, serverInfo, logger.Infof("%s abandon prev connection, server is %+v, connectionId is %s", r.Name, serverInfo,
r.currentConnection.getConnectionId()) r.currentConnection.getConnectionId())
r.currentConnection.setAbandon(true) r.currentConnection.setAbandon(true)
r.closeConnection() r.closeConnection()
} }
r.currentConnection = connectionNew r.currentConnection = connectionNew
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING)) atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING))
r.notifyConnectionChange(CONNECTED) r.eventChan <- ConnectionEvent{eventType: CONNECTED}
return return
} }
if r.isShutdown() { if r.isShutdown() {
r.closeConnection() r.closeConnection()
} }
if reConnectTimes > 0 && reConnectTimes%len(r.nacosServer.GetServerList()) == 0 { if reConnectTimes > 0 && reConnectTimes%len(r.nacosServer.GetServerList()) == 0 {
logger.Warnf("%s fail to connect server, after trying %d times, last try server is %+v, error=%v", r.name, logger.Infof("%s fail to connect server, after trying %v times, last try server is %+v, error=%+v", r.Name,
reConnectTimes, serverInfo, err) reConnectTimes, serverInfo, err)
if retryTurns < 50 { if retryTurns < 50 {
retryTurns++ retryTurns++
@ -466,25 +345,24 @@ func (r *RpcClient) reconnect(serverInfo ServerInfo, onRequestFail bool) {
} }
} }
if r.isShutdown() { if r.isShutdown() {
logger.Warnf("%s client is shutdown, stop reconnect to server", r.name) logger.Infof("%s client is shutdown, stop reconnect to server", r.Name)
} }
} }
func (r *RpcClient) closeConnection() { func (r *RpcClient) closeConnection() {
if r.currentConnection != nil { if r.currentConnection != nil {
r.currentConnection.close() r.currentConnection.close()
r.notifyConnectionChange(DISCONNECTED) r.eventChan <- ConnectionEvent{eventType: DISCONNECTED}
} }
} }
// Notify when client new connected. // Notify when client new connected.
func (r *RpcClient) notifyConnectionEvent(event ConnectionEvent) { func (r *RpcClient) notifyConnectionEvent(event ConnectionEvent) {
listeners := r.connectionEventListeners.Load().([]IConnectionEventListener) if len(r.connectionEventListeners) == 0 {
if len(listeners) == 0 {
return return
} }
logger.Infof("%s notify %s event to listeners , connectionId=%s", r.name, event.toString(), r.currentConnection.getConnectionId()) logger.Infof("%s notify %s event to listeners.", r.Name, event.toString())
for _, v := range listeners { for _, v := range r.connectionEventListeners {
if event.isConnected() { if event.isConnected() {
v.OnConnected() v.OnConnected()
} }
@ -497,18 +375,17 @@ func (r *RpcClient) notifyConnectionEvent(event ConnectionEvent) {
func (r *RpcClient) healthCheck(timer *time.Timer) { func (r *RpcClient) healthCheck(timer *time.Timer) {
defer timer.Reset(constant.KEEP_ALIVE_TIME * time.Second) defer timer.Reset(constant.KEEP_ALIVE_TIME * time.Second)
var reconnectContext ReconnectContext var reconnectContext ReconnectContext
lastActiveTimeStamp := r.lastActiveTimestamp.Load().(time.Time) if time.Now().Sub(r.lastActiveTimeStamp) < constant.KEEP_ALIVE_TIME*time.Second {
if time.Now().Sub(lastActiveTimeStamp) < constant.KEEP_ALIVE_TIME*time.Second {
return return
} }
if r.sendHealthCheck() { if r.sendHealthCheck() {
r.lastActiveTimestamp.Store(time.Now()) r.lastActiveTimeStamp = time.Now()
return return
} else { } else {
if r.currentConnection == nil || r.isShutdown() { if r.currentConnection == nil {
return return
} }
logger.Infof("%s server healthy check fail, currentConnection=%s", r.name, r.currentConnection.getConnectionId()) logger.Infof("%s server healthy check fail, currentConnection=%s", r.Name, r.currentConnection.getConnectionId())
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(UNHEALTHY)) atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(UNHEALTHY))
reconnectContext = ReconnectContext{onRequestFail: false} reconnectContext = ReconnectContext{onRequestFail: false}
} }
@ -522,7 +399,6 @@ func (r *RpcClient) sendHealthCheck() bool {
response, err := r.currentConnection.request(rpc_request.NewHealthCheckRequest(), response, err := r.currentConnection.request(rpc_request.NewHealthCheckRequest(),
constant.DEFAULT_TIMEOUT_MILLS, r) constant.DEFAULT_TIMEOUT_MILLS, r)
if err != nil { if err != nil {
logger.Errorf("client sendHealthCheck failed,err=%v", err)
return false return false
} }
if !response.IsSuccess() { if !response.IsSuccess() {
@ -545,7 +421,6 @@ func (r *RpcClient) nextRpcServer() (ServerInfo, error) {
return ServerInfo{ return ServerInfo{
serverIp: serverConfig.IpAddr, serverIp: serverConfig.IpAddr,
serverPort: serverConfig.Port, serverPort: serverConfig.Port,
serverGrpcPort: serverConfig.GrpcPort,
}, nil }, nil
} }
@ -557,12 +432,12 @@ func (c *ConnectionEvent) isDisConnected() bool {
return c.eventType == DISCONNECTED return c.eventType == DISCONNECTED
} }
// check is this client is shutdown. //check is this client is shutdown.
func (r *RpcClient) isShutdown() bool { func (r *RpcClient) isShutdown() bool {
return atomic.LoadInt32((*int32)(&r.rpcClientStatus)) == (int32)(SHUTDOWN) return atomic.LoadInt32((*int32)(&r.rpcClientStatus)) == (int32)(SHUTDOWN)
} }
// IsRunning check is this client is running. //check is this client is running.
func (r *RpcClient) IsRunning() bool { func (r *RpcClient) IsRunning() bool {
return atomic.LoadInt32((*int32)(&r.rpcClientStatus)) == (int32)(RUNNING) return atomic.LoadInt32((*int32)(&r.rpcClientStatus)) == (int32)(RUNNING)
} }
@ -587,17 +462,14 @@ func (r *RpcClient) Request(request rpc_request.IRequest, timeoutMills int64) (r
var currentErr error var currentErr error
for retryTimes < constant.REQUEST_DOMAIN_RETRY_TIME && util.CurrentMillis() < start+timeoutMills { for retryTimes < constant.REQUEST_DOMAIN_RETRY_TIME && util.CurrentMillis() < start+timeoutMills {
if r.currentConnection == nil || !r.IsRunning() { if r.currentConnection == nil || !r.IsRunning() {
currentErr = waitReconnect(timeoutMills, &retryTimes, request, currentErr = waitReconnect(timeoutMills, &retryTimes, request, errors.New(fmt.Sprintf(
errors.Errorf("client not connected, current status:%s", r.rpcClientStatus.getDesc())) "Client not connected, current status:%s", r.rpcClientStatus.getDesc())))
continue continue
} }
response, err := r.currentConnection.request(request, timeoutMills, r) response, err := r.currentConnection.request(request, timeoutMills, r)
if err != nil { if err == nil {
currentErr = waitReconnect(timeoutMills, &retryTimes, request, err) if response, ok := response.(*rpc_response.ErrorResponse); ok {
continue if response.GetErrorCode() == constant.UN_REGISTER {
}
if resp, ok := response.(*rpc_response.ErrorResponse); ok {
if resp.GetErrorCode() == constant.UN_REGISTER {
r.mux.Lock() r.mux.Lock()
if atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING), (int32)(UNHEALTHY)) { if atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING), (int32)(UNHEALTHY)) {
logger.Infof("Connection is unregistered, switch server, connectionId=%s, request=%s", logger.Infof("Connection is unregistered, switch server, connectionId=%s, request=%s",
@ -609,11 +481,11 @@ func (r *RpcClient) Request(request rpc_request.IRequest, timeoutMills int64) (r
currentErr = waitReconnect(timeoutMills, &retryTimes, request, errors.New(response.GetMessage())) currentErr = waitReconnect(timeoutMills, &retryTimes, request, errors.New(response.GetMessage()))
continue continue
} }
if response != nil && !response.IsSuccess() { r.lastActiveTimeStamp = time.Now()
logger.Warnf("%s request received fail response, error code: %d, result code: %d, message: [%s]", request.GetRequestType(), response.GetErrorCode(), response.GetResultCode(), response.GetMessage())
}
r.lastActiveTimestamp.Store(time.Now())
return response, nil return response, nil
} else {
currentErr = waitReconnect(timeoutMills, &retryTimes, request, err)
}
} }
if atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), int32(RUNNING), int32(UNHEALTHY)) { if atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), int32(RUNNING), int32(UNHEALTHY)) {
@ -631,7 +503,3 @@ func waitReconnect(timeoutMills int64, retryTimes *int, request rpc_request.IReq
*retryTimes++ *retryTimes++
return err return err
} }
func (r *RpcClient) Name() string {
return r.name
}

View File

@ -16,45 +16,24 @@
package rpc_request package rpc_request
import ( import "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/model"
)
type ConfigRequest struct { type ConfigRequest struct {
*Request *Request
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
Module string `json:"module"` Module string `json:"module"`
} }
func NewConfigRequest(group, dataId, tenant string) *ConfigRequest { func NewConfigRequest() *ConfigRequest {
request := Request{ request := Request{
Headers: make(map[string]string, 8), Headers: make(map[string]string, 8),
} }
return &ConfigRequest{ return &ConfigRequest{
Request: &request, Request: &request,
Group: group,
DataId: dataId,
Tenant: tenant,
Module: "config", Module: "config",
} }
} }
func (r *ConfigRequest) GetDataId() string { //request of listening a batch of configs.
return r.DataId
}
func (r *ConfigRequest) GetGroup() string {
return r.Group
}
func (r *ConfigRequest) GetTenant() string {
return r.Tenant
}
// request of listening a batch of configs.
type ConfigBatchListenRequest struct { type ConfigBatchListenRequest struct {
*ConfigRequest *ConfigRequest
Listen bool `json:"listen"` Listen bool `json:"listen"`
@ -65,63 +44,76 @@ func NewConfigBatchListenRequest(cacheLen int) *ConfigBatchListenRequest {
return &ConfigBatchListenRequest{ return &ConfigBatchListenRequest{
Listen: true, Listen: true,
ConfigListenContexts: make([]model.ConfigListenContext, 0, cacheLen), ConfigListenContexts: make([]model.ConfigListenContext, 0, cacheLen),
ConfigRequest: NewConfigRequest("", "", ""), ConfigRequest: NewConfigRequest(),
} }
} }
func (r *ConfigBatchListenRequest) GetRequestType() string { func (r *ConfigBatchListenRequest) GetRequestType() string {
return constant.CONFIG_BATCH_LISTEN_REQUEST_NAME return "ConfigBatchListenRequest"
} }
type ConfigChangeNotifyRequest struct { type ConfigChangeNotifyRequest struct {
*ConfigRequest *ConfigRequest
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
} }
func NewConfigChangeNotifyRequest(group, dataId, tenant string) *ConfigChangeNotifyRequest { func NewConfigChangeNotifyRequest(group, dataId, tenant string) *ConfigChangeNotifyRequest {
return &ConfigChangeNotifyRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant)} return &ConfigChangeNotifyRequest{ConfigRequest: NewConfigRequest(), Group: group, DataId: dataId, Tenant: tenant}
} }
func (r *ConfigChangeNotifyRequest) GetRequestType() string { func (r *ConfigChangeNotifyRequest) GetRequestType() string {
return constant.CONFIG_CHANGE_NOTIFY_REQUEST_NAME return "ConfigChangeNotifyRequest"
} }
type ConfigQueryRequest struct { type ConfigQueryRequest struct {
*ConfigRequest *ConfigRequest
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
Tag string `json:"tag"` Tag string `json:"tag"`
} }
func NewConfigQueryRequest(group, dataId, tenant string) *ConfigQueryRequest { func NewConfigQueryRequest(group, dataId, tenant string) *ConfigQueryRequest {
return &ConfigQueryRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant)} return &ConfigQueryRequest{ConfigRequest: NewConfigRequest(), Group: group, DataId: dataId, Tenant: tenant}
} }
func (r *ConfigQueryRequest) GetRequestType() string { func (r *ConfigQueryRequest) GetRequestType() string {
return constant.CONFIG_QUERY_REQUEST_NAME return "ConfigQueryRequest"
} }
type ConfigPublishRequest struct { type ConfigPublishRequest struct {
*ConfigRequest *ConfigRequest
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
Content string `json:"content"` Content string `json:"content"`
CasMd5 string `json:"casMd5"` CasMd5 string `json:"casMd5"`
AdditionMap map[string]string `json:"additionMap"` AdditionMap map[string]string `json:"additionMap"`
} }
func NewConfigPublishRequest(group, dataId, tenant, content, casMd5 string) *ConfigPublishRequest { func NewConfigPublishRequest(group, dataId, tenant, content, casMd5 string) *ConfigPublishRequest {
return &ConfigPublishRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant), return &ConfigPublishRequest{ConfigRequest: NewConfigRequest(),
Content: content, CasMd5: casMd5, AdditionMap: make(map[string]string)} Group: group, DataId: dataId, Tenant: tenant, Content: content, CasMd5: casMd5, AdditionMap: make(map[string]string)}
} }
func (r *ConfigPublishRequest) GetRequestType() string { func (r *ConfigPublishRequest) GetRequestType() string {
return constant.CONFIG_PUBLISH_REQUEST_NAME return "ConfigPublishRequest"
} }
type ConfigRemoveRequest struct { type ConfigRemoveRequest struct {
*ConfigRequest *ConfigRequest
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
} }
func NewConfigRemoveRequest(group, dataId, tenant string) *ConfigRemoveRequest { func NewConfigRemoveRequest(group, dataId, tenant string) *ConfigRemoveRequest {
return &ConfigRemoveRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant)} return &ConfigRemoveRequest{ConfigRequest: NewConfigRequest(),
Group: group, DataId: dataId, Tenant: tenant}
} }
func (r *ConfigRemoveRequest) GetRequestType() string { func (r *ConfigRemoveRequest) GetRequestType() string {
return constant.CONFIG_REMOVE_REQUEST_NAME return "ConfigRemoveRequest"
} }

View File

@ -17,11 +17,6 @@
package rpc_request package rpc_request
import ( import (
"fmt"
"strconv"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
) )
@ -33,6 +28,12 @@ type NamingRequest struct {
Module string `json:"module"` Module string `json:"module"`
} }
type InstanceRequest struct {
*NamingRequest
Type string `json:"type"`
Instance model.Instance `json:"instance"`
}
func NewNamingRequest(namespace, serviceName, groupName string) *NamingRequest { func NewNamingRequest(namespace, serviceName, groupName string) *NamingRequest {
request := Request{ request := Request{
Headers: make(map[string]string, 8), Headers: make(map[string]string, 8),
@ -46,20 +47,6 @@ func NewNamingRequest(namespace, serviceName, groupName string) *NamingRequest {
} }
} }
func (r *NamingRequest) GetStringToSign() string {
data := strconv.FormatInt(time.Now().Unix()*1000, 10)
if r.ServiceName != "" || r.GroupName != "" {
data = fmt.Sprintf("%s@@%s@@%s", data, r.GroupName, r.ServiceName)
}
return data
}
type InstanceRequest struct {
*NamingRequest
Type string `json:"type"`
Instance model.Instance `json:"instance"`
}
func NewInstanceRequest(namespace, serviceName, groupName, Type string, instance model.Instance) *InstanceRequest { func NewInstanceRequest(namespace, serviceName, groupName, Type string, instance model.Instance) *InstanceRequest {
return &InstanceRequest{ return &InstanceRequest{
NamingRequest: NewNamingRequest(namespace, serviceName, groupName), NamingRequest: NewNamingRequest(namespace, serviceName, groupName),
@ -69,25 +56,7 @@ func NewInstanceRequest(namespace, serviceName, groupName, Type string, instance
} }
func (r *InstanceRequest) GetRequestType() string { func (r *InstanceRequest) GetRequestType() string {
return constant.INSTANCE_REQUEST_NAME return "InstanceRequest"
}
type BatchInstanceRequest struct {
*NamingRequest
Type string `json:"type"`
Instances []model.Instance `json:"instances"`
}
func NewBatchInstanceRequest(namespace, serviceName, groupName, Type string, instances []model.Instance) *BatchInstanceRequest {
return &BatchInstanceRequest{
NamingRequest: NewNamingRequest(namespace, serviceName, groupName),
Type: Type,
Instances: instances,
}
}
func (r *BatchInstanceRequest) GetRequestType() string {
return constant.BATCH_INSTANCE_REQUEST_NAME
} }
type NotifySubscriberRequest struct { type NotifySubscriberRequest struct {
@ -96,7 +65,7 @@ type NotifySubscriberRequest struct {
} }
func (r *NotifySubscriberRequest) GetRequestType() string { func (r *NotifySubscriberRequest) GetRequestType() string {
return constant.NOTIFY_SUBSCRIBE_REQUEST_NAME return "NotifySubscriberRequest"
} }
type ServiceListRequest struct { type ServiceListRequest struct {
@ -116,7 +85,7 @@ func NewServiceListRequest(namespace, serviceName, groupName string, pageNo, pag
} }
func (r *ServiceListRequest) GetRequestType() string { func (r *ServiceListRequest) GetRequestType() string {
return constant.SERVICE_LIST_REQUEST_NAME return "ServiceListRequest"
} }
type SubscribeServiceRequest struct { type SubscribeServiceRequest struct {
@ -134,25 +103,25 @@ func NewSubscribeServiceRequest(namespace, serviceName, groupName, clusters stri
} }
func (r *SubscribeServiceRequest) GetRequestType() string { func (r *SubscribeServiceRequest) GetRequestType() string {
return constant.SUBSCRIBE_SERVICE_REQUEST_NAME return "SubscribeServiceRequest"
} }
type ServiceQueryRequest struct { type ServiceQueryRequest struct {
*NamingRequest *NamingRequest
Cluster string `json:"cluster"` Clusters string `json:"clusters"`
HealthyOnly bool `json:"healthyOnly"` HealthyOnly bool `json:"healthyOnly"`
UdpPort int `json:"udpPort"` UdpPort int `json:"udpPort"`
} }
func NewServiceQueryRequest(namespace, serviceName, groupName, cluster string, healthyOnly bool, udpPort int) *ServiceQueryRequest { func NewServiceQueryRequest(namespace, serviceName, groupName, clusters string, healthyOnly bool, udpPort int) *ServiceQueryRequest {
return &ServiceQueryRequest{ return &ServiceQueryRequest{
NamingRequest: NewNamingRequest(namespace, serviceName, groupName), NamingRequest: NewNamingRequest(namespace, serviceName, groupName),
Cluster: cluster, Clusters: clusters,
HealthyOnly: healthyOnly, HealthyOnly: healthyOnly,
UdpPort: udpPort, UdpPort: udpPort,
} }
} }
func (r *ServiceQueryRequest) GetRequestType() string { func (r *ServiceQueryRequest) GetRequestType() string {
return constant.SERVICE_QUERY_REQUEST_NAME return "ServiceQueryRequest"
} }

View File

@ -29,19 +29,9 @@ type IRequest interface {
GetBody(request IRequest) string GetBody(request IRequest) string
PutAllHeaders(headers map[string]string) PutAllHeaders(headers map[string]string)
GetRequestId() string GetRequestId() string
GetStringToSign() string
}
type IConfigRequest interface {
GetDataId() string
GetGroup() string
GetTenant() string
} }
func (r *Request) PutAllHeaders(headers map[string]string) { func (r *Request) PutAllHeaders(headers map[string]string) {
if r.Headers == nil {
r.Headers = make(map[string]string)
}
for k, v := range headers { for k, v := range headers {
r.Headers[k] = v r.Headers[k] = v
} }
@ -61,7 +51,3 @@ func (r *Request) GetBody(request IRequest) string {
func (r *Request) GetRequestId() string { func (r *Request) GetRequestId() string {
return r.RequestId return r.RequestId
} }
func (r *Request) GetStringToSign() string {
return ""
}

View File

@ -1,10 +0,0 @@
package rpc_response
type ResponseCode int
const (
ResponseSuccessCode ResponseCode = 200
ResponseFailCode ResponseCode = 500
ResponseSuccessField = "success"
)

View File

@ -53,14 +53,6 @@ func (c *InstanceResponse) GetResponseType() string {
return "InstanceResponse" return "InstanceResponse"
} }
type BatchInstanceResponse struct {
*Response
}
func (c *BatchInstanceResponse) GetResponseType() string {
return "BatchInstanceResponse"
}
type QueryServiceResponse struct { type QueryServiceResponse struct {
*Response *Response
ServiceInfo model.Service `json:"serviceInfo"` ServiceInfo model.Service `json:"serviceInfo"`

View File

@ -17,8 +17,6 @@
package rpc_response package rpc_response
import ( import (
"strconv"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
) )
@ -36,7 +34,6 @@ type IResponse interface {
GetBody() string GetBody() string
GetErrorCode() int GetErrorCode() int
IsSuccess() bool IsSuccess() bool
SetSuccess(bool)
GetResultCode() int GetResultCode() int
GetMessage() string GetMessage() string
} }
@ -61,10 +58,6 @@ func (r *Response) IsSuccess() bool {
return r.Success return r.Success
} }
func (r *Response) SetSuccess(successResult bool) {
r.Success = successResult
}
func (r *Response) GetErrorCode() int { func (r *Response) GetErrorCode() int {
return r.ErrorCode return r.ErrorCode
} }
@ -92,11 +85,6 @@ func registerClientResponses() {
return &InstanceResponse{Response: &Response{}} return &InstanceResponse{Response: &Response{}}
}) })
// register BatchInstanceResponse.
registerClientResponse(func() IResponse {
return &BatchInstanceResponse{Response: &Response{}}
})
// register QueryServiceResponse. // register QueryServiceResponse.
registerClientResponse(func() IResponse { registerClientResponse(func() IResponse {
return &QueryServiceResponse{Response: &Response{}} return &QueryServiceResponse{Response: &Response{}}
@ -147,12 +135,3 @@ func registerClientResponses() {
return &ConfigRemoveResponse{Response: &Response{}} return &ConfigRemoveResponse{Response: &Response{}}
}) })
} }
// get grpc response status code with NA default.
func GetGrpcResponseStatusCode(response IResponse) string {
if response != nil {
return strconv.Itoa(response.GetResultCode())
} else {
return "NA"
}
}

View File

@ -1,40 +0,0 @@
package rpc_response
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestRpcResponseIsSuccess(t *testing.T) {
responseBody0 := `{"resultCode":200,"errorCode":0}`
responseBody1 := `{"resultCode":200,"errorCode":0,"success":true}`
responseBody2 := `{"resultCode":200,"errorCode":0,"success":"true"}`
responseBody3 := `{"resultCode":200,"errorCode":0,"success":false}`
responseBody4 := `{"resultCode":500,"errorCode":0,"success":true}`
responseBody5 := `{"resultCode":500,"errorCode":0,"success":false}`
responseBodyList := make([]string, 0)
responseBodyList = append(responseBodyList, responseBody0, responseBody1, responseBody2, responseBody3, responseBody4, responseBody5)
for k, v := range ClientResponseMapping {
t.Run("test "+k, func(t *testing.T) {
for index, responseBody := range responseBodyList {
response, err := InnerResponseJsonUnmarshal([]byte(responseBody), v)
switch index {
case 0, 1, 4:
assert.True(t, response.IsSuccess())
break
case 3, 5:
assert.False(t, response.IsSuccess())
break
case 2:
assert.Nil(t, response)
assert.NotNil(t, err)
t.Logf("handle %d failed with responseBody: %s", index, responseBody)
break
default:
panic("unknown index")
}
}
})
}
}

View File

@ -1,24 +0,0 @@
package rpc_response
import "encoding/json"
func InnerResponseJsonUnmarshal(responseBody []byte, responseFunc func() IResponse) (IResponse, error) {
response := responseFunc()
err := json.Unmarshal(responseBody, response)
if err != nil {
return nil, err
}
if !response.IsSuccess() {
tempFiledMap := make(map[string]interface{})
err = json.Unmarshal(responseBody, &tempFiledMap)
if err != nil {
return response, nil
}
if _, ok := tempFiledMap[ResponseSuccessField]; !ok {
response.SetSuccess(response.GetResultCode() == int(ResponseSuccessCode))
}
}
return response, err
}

View File

@ -26,20 +26,16 @@ import (
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
) )
// IServerRequestHandler to process the request from server side. //ServerRequestHandler, to process the request from server side.
type IServerRequestHandler interface { type IServerRequestHandler interface {
Name() string
//RequestReply Handle request from server. //Handle request from server.
RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse
} }
type ConnectResetRequestHandler struct { type ConnectResetRequestHandler struct {
} }
func (c *ConnectResetRequestHandler) Name() string {
return "ConnectResetRequestHandler"
}
func (c *ConnectResetRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse { func (c *ConnectResetRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
connectResetRequest, ok := request.(*rpc_request.ConnectResetRequest) connectResetRequest, ok := request.(*rpc_request.ConnectResetRequest)
if ok { if ok {
@ -49,7 +45,7 @@ func (c *ConnectResetRequestHandler) RequestReply(request rpc_request.IRequest,
if connectResetRequest.ServerIp != "" { if connectResetRequest.ServerIp != "" {
serverPortNum, err := strconv.Atoi(connectResetRequest.ServerPort) serverPortNum, err := strconv.Atoi(connectResetRequest.ServerPort)
if err != nil { if err != nil {
logger.Errorf("ConnectResetRequest ServerPort type conversion error:%+v", err) logger.Infof("ConnectResetRequest ServerPort type conversion error:%+v", err)
return nil return nil
} }
rpcClient.switchServerAsync(ServerInfo{serverIp: connectResetRequest.ServerIp, serverPort: uint64(serverPortNum)}, false) rpcClient.switchServerAsync(ServerInfo{serverIp: connectResetRequest.ServerIp, serverPort: uint64(serverPortNum)}, false)
@ -65,11 +61,7 @@ func (c *ConnectResetRequestHandler) RequestReply(request rpc_request.IRequest,
type ClientDetectionRequestHandler struct { type ClientDetectionRequestHandler struct {
} }
func (c *ClientDetectionRequestHandler) Name() string { func (c *ClientDetectionRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
return "ClientDetectionRequestHandler"
}
func (c *ClientDetectionRequestHandler) RequestReply(request rpc_request.IRequest, _ *RpcClient) rpc_response.IResponse {
_, ok := request.(*rpc_request.ClientDetectionRequest) _, ok := request.(*rpc_request.ClientDetectionRequest)
if ok { if ok {
return &rpc_response.ClientDetectionResponse{ return &rpc_response.ClientDetectionResponse{
@ -83,18 +75,12 @@ type NamingPushRequestHandler struct {
ServiceInfoHolder *naming_cache.ServiceInfoHolder ServiceInfoHolder *naming_cache.ServiceInfoHolder
} }
func (*NamingPushRequestHandler) Name() string { func (c *NamingPushRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
return "NamingPushRequestHandler"
}
func (c *NamingPushRequestHandler) RequestReply(request rpc_request.IRequest, client *RpcClient) rpc_response.IResponse {
notifySubscriberRequest, ok := request.(*rpc_request.NotifySubscriberRequest) notifySubscriberRequest, ok := request.(*rpc_request.NotifySubscriberRequest)
if ok { if ok {
c.ServiceInfoHolder.ProcessService(&notifySubscriberRequest.ServiceInfo) c.ServiceInfoHolder.ProcessService(&notifySubscriberRequest.ServiceInfo)
logger.Debugf("%s naming push response success ackId->%s", client.currentConnection.getConnectionId(),
request.GetRequestId())
return &rpc_response.NotifySubscriberResponse{ return &rpc_response.NotifySubscriberResponse{
Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS, Success: true}, Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
} }
} }
return nil return nil

View File

@ -1,185 +0,0 @@
package security
import (
"context"
"encoding/json"
"io"
"net/http"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/pkg/errors"
)
type NacosAuthClient struct {
username string
password string
accessToken *atomic.Value
tokenTtl int64
lastRefreshTime int64
tokenRefreshWindow int64
agent http_agent.IHttpAgent
clientCfg constant.ClientConfig
serverCfgs []constant.ServerConfig
}
func NewNacosAuthClient(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, agent http_agent.IHttpAgent) *NacosAuthClient {
client := &NacosAuthClient{
username: clientCfg.Username,
password: clientCfg.Password,
serverCfgs: serverCfgs,
clientCfg: clientCfg,
agent: agent,
accessToken: &atomic.Value{},
}
return client
}
func (ac *NacosAuthClient) GetAccessToken() string {
v := ac.accessToken.Load()
if v == nil {
return ""
}
return v.(string)
}
func (ac *NacosAuthClient) GetSecurityInfo(resource RequestResource) map[string]string {
var securityInfo = make(map[string]string, 4)
v := ac.accessToken.Load()
if v != nil {
securityInfo[constant.KEY_ACCESS_TOKEN] = v.(string)
}
return securityInfo
}
func (ac *NacosAuthClient) AutoRefresh(ctx context.Context) {
// If the username is not set, the automatic refresh Token is not enabled
if ac.username == "" {
return
}
go func() {
var timer *time.Timer
if lastLoginSuccess := ac.lastRefreshTime > 0 && ac.tokenTtl > 0 && ac.tokenRefreshWindow > 0; lastLoginSuccess {
timer = time.NewTimer(time.Second * time.Duration(ac.tokenTtl-ac.tokenRefreshWindow))
} else {
timer = time.NewTimer(time.Second * time.Duration(5))
}
defer timer.Stop()
for {
select {
case <-timer.C:
_, err := ac.Login()
if err != nil {
logger.Errorf("login has error %+v", err)
timer.Reset(time.Second * time.Duration(5))
} else {
logger.Infof("login success, tokenTtl: %+v seconds, tokenRefreshWindow: %+v seconds", ac.tokenTtl, ac.tokenRefreshWindow)
timer.Reset(time.Second * time.Duration(ac.tokenTtl-ac.tokenRefreshWindow))
}
case <-ctx.Done():
return
}
}
}()
}
func (ac *NacosAuthClient) Login() (bool, error) {
var throwable error = nil
for i := 0; i < len(ac.serverCfgs); i++ {
result, err := ac.login(ac.serverCfgs[i])
throwable = err
if result {
return true, nil
}
}
return false, throwable
}
func (ac *NacosAuthClient) UpdateServerList(serverList []constant.ServerConfig) {
ac.serverCfgs = serverList
}
func (ac *NacosAuthClient) GetServerList() []constant.ServerConfig {
return ac.serverCfgs
}
func (ac *NacosAuthClient) login(server constant.ServerConfig) (bool, error) {
if ac.lastRefreshTime > 0 && ac.tokenTtl > 0 {
// We refresh 2 windows before expiration to ensure continuous availability
tokenRefreshTime := ac.lastRefreshTime + ac.tokenTtl - 2*ac.tokenRefreshWindow
if time.Now().Unix() < tokenRefreshTime {
return true, nil
}
}
if ac.username == "" {
ac.lastRefreshTime = time.Now().Unix()
return true, nil
}
contextPath := server.ContextPath
if !strings.HasPrefix(contextPath, "/") {
contextPath = "/" + contextPath
}
if strings.HasSuffix(contextPath, "/") {
contextPath = contextPath[0 : len(contextPath)-1]
}
if server.Scheme == "" {
server.Scheme = "http"
}
reqUrl := server.Scheme + "://" + server.IpAddr + ":" + strconv.FormatInt(int64(server.Port), 10) + contextPath + "/v1/auth/users/login"
header := http.Header{
"content-type": []string{"application/x-www-form-urlencoded"},
}
resp, err := ac.agent.Post(reqUrl, header, ac.clientCfg.TimeoutMs, map[string]string{
"username": ac.username,
"password": ac.password,
})
if err != nil {
return false, err
}
var bytes []byte
bytes, err = io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return false, err
}
if resp.StatusCode != constant.RESPONSE_CODE_SUCCESS {
errMsg := string(bytes)
return false, errors.New(errMsg)
}
var result map[string]interface{}
err = json.Unmarshal(bytes, &result)
if err != nil {
return false, err
}
if val, ok := result[constant.KEY_ACCESS_TOKEN]; ok {
ac.accessToken.Store(val)
ac.lastRefreshTime = time.Now().Unix()
ac.tokenTtl = int64(result[constant.KEY_TOKEN_TTL].(float64))
ac.tokenRefreshWindow = ac.tokenTtl / 10
}
return true, nil
}

View File

@ -1,289 +0,0 @@
package security
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"testing"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/stretchr/testify/assert"
)
// MockResponseBody creates a mock response body for testing
type MockResponseBody struct {
*bytes.Buffer
}
func (m *MockResponseBody) Close() error {
return nil
}
func NewMockResponseBody(data interface{}) io.ReadCloser {
var buf bytes.Buffer
if str, ok := data.(string); ok {
buf.WriteString(str)
} else {
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false)
enc.Encode(data)
}
return &MockResponseBody{&buf}
}
// MockHttpAgent implements http_agent.IHttpAgent for testing
type MockHttpAgent struct {
PostFunc func(url string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error)
}
func (m *MockHttpAgent) Request(method string, url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
switch method {
case http.MethodPost:
return m.Post(url, header, timeoutMs, params)
default:
return &http.Response{
StatusCode: http.StatusMethodNotAllowed,
Body: NewMockResponseBody("method not allowed"),
}, nil
}
}
func (m *MockHttpAgent) RequestOnlyResult(method string, url string, header http.Header, timeoutMs uint64, params map[string]string) string {
resp, err := m.Request(method, url, header, timeoutMs, params)
if err != nil {
return ""
}
if resp.Body == nil {
return ""
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return ""
}
return string(data)
}
func (m *MockHttpAgent) Get(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
return m.Request(http.MethodGet, url, header, timeoutMs, params)
}
func (m *MockHttpAgent) Post(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
if m.PostFunc != nil {
return m.PostFunc(url, header, timeoutMs, params)
}
return &http.Response{
StatusCode: http.StatusNotImplemented,
Body: NewMockResponseBody("not implemented"),
}, nil
}
func (m *MockHttpAgent) Delete(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
return m.Request(http.MethodDelete, url, header, timeoutMs, params)
}
func (m *MockHttpAgent) Put(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
return m.Request(http.MethodPut, url, header, timeoutMs, params)
}
func TestNacosAuthClient_Login_Success(t *testing.T) {
// Setup mock response
mockResp := &http.Response{
StatusCode: constant.RESPONSE_CODE_SUCCESS,
Body: NewMockResponseBody(map[string]interface{}{
constant.KEY_ACCESS_TOKEN: "test-token",
constant.KEY_TOKEN_TTL: float64(10),
}),
}
mockAgent := &MockHttpAgent{
PostFunc: func(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
// Verify request parameters
assert.Equal(t, "test-user", params["username"])
assert.Equal(t, "test-pass", params["password"])
contentType := header["content-type"]
assert.Equal(t, []string{"application/x-www-form-urlencoded"}, contentType)
return mockResp, nil
},
}
// Create client config
clientConfig := constant.ClientConfig{
Username: "test-user",
Password: "test-pass",
TimeoutMs: 10000,
}
serverConfigs := []constant.ServerConfig{
{
IpAddr: "127.0.0.1",
Port: 8848,
ContextPath: "/nacos",
},
}
client := NewNacosAuthClient(clientConfig, serverConfigs, mockAgent)
// Test login
success, err := client.Login()
assert.NoError(t, err)
assert.True(t, success)
// Verify token is stored
assert.Equal(t, "test-token", client.GetAccessToken())
}
func TestNacosAuthClient_Login_NoAuth(t *testing.T) {
mockAgent := &MockHttpAgent{
PostFunc: func(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
t.Fatal("Should not make HTTP call when no username is set")
return nil, nil
},
}
clientConfig := constant.ClientConfig{}
serverConfigs := []constant.ServerConfig{{}}
client := NewNacosAuthClient(clientConfig, serverConfigs, mockAgent)
success, err := client.Login()
assert.NoError(t, err)
assert.True(t, success)
assert.Empty(t, client.GetAccessToken())
}
func TestNacosAuthClient_TokenRefresh(t *testing.T) {
callCount := 0
mockAgent := &MockHttpAgent{
PostFunc: func(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
callCount++
return &http.Response{
StatusCode: constant.RESPONSE_CODE_SUCCESS,
Body: NewMockResponseBody(map[string]interface{}{
constant.KEY_ACCESS_TOKEN: "token-" + fmt.Sprintf("%d", callCount),
constant.KEY_TOKEN_TTL: float64(1), // 1 second TTL for quick testing
}),
}, nil
},
}
clientConfig := constant.ClientConfig{
Username: "test-user",
Password: "test-pass",
}
client := NewNacosAuthClient(clientConfig, []constant.ServerConfig{{IpAddr: "localhost"}}, mockAgent)
// Initial login
success, err := client.Login()
assert.NoError(t, err)
assert.True(t, success)
assert.Equal(t, "token-1", client.GetAccessToken())
// Wait for token to require refresh (1 second TTL)
time.Sleep(time.Second * 2)
// Second login should get new token
success, err = client.Login()
assert.NoError(t, err)
assert.True(t, success)
assert.Equal(t, "token-2", client.GetAccessToken())
}
func TestNacosAuthClient_AutoRefresh(t *testing.T) {
callCount := 0
tokenChan := make(chan string, 2)
mockAgent := &MockHttpAgent{
PostFunc: func(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
callCount++
token := fmt.Sprintf("auto-token-%d", callCount)
tokenChan <- token
t.Logf("Mock server received request #%d, returning token: %s", callCount, token)
return &http.Response{
StatusCode: constant.RESPONSE_CODE_SUCCESS,
Body: NewMockResponseBody(map[string]interface{}{
constant.KEY_ACCESS_TOKEN: token,
constant.KEY_TOKEN_TTL: float64(10), // 10 seconds TTL, resulting in 1s refresh window
}),
}, nil
},
}
clientConfig := constant.ClientConfig{
Username: "test-user",
Password: "test-pass",
}
client := NewNacosAuthClient(clientConfig, []constant.ServerConfig{{IpAddr: "localhost"}}, mockAgent)
// First do a manual login
t.Log("Performing initial manual login")
success, err := client.Login()
assert.NoError(t, err)
assert.True(t, success)
token1 := <-tokenChan // Get the token from the first login
t.Logf("Initial login successful, token: %s", token1)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
// Start auto refresh
t.Log("Starting auto refresh")
client.AutoRefresh(ctx)
// Wait for token refresh (should happen after TTL-2*refreshWindow seconds = 8 seconds)
// We'll wait a bit longer to account for any delays
t.Log("Waiting for token refresh")
var token2 string
select {
case token2 = <-tokenChan:
t.Logf("Received refreshed token: %s", token2)
case <-time.After(time.Second * 12):
t.Fatal("Timeout waiting for token refresh")
}
assert.NotEqual(t, token1, token2, "Token should have been refreshed")
assert.Equal(t, "auto-token-1", token1, "First token should be auto-token-1")
assert.Equal(t, "auto-token-2", token2, "Second token should be auto-token-2")
}
func TestNacosAuthClient_GetSecurityInfo(t *testing.T) {
client := NewNacosAuthClient(constant.ClientConfig{}, []constant.ServerConfig{}, nil)
// When no token
info := client.GetSecurityInfo(RequestResource{})
assert.Empty(t, info[constant.KEY_ACCESS_TOKEN])
// When token exists
mockToken := "test-security-token"
client.accessToken.Store(mockToken)
info = client.GetSecurityInfo(RequestResource{})
assert.Equal(t, mockToken, info[constant.KEY_ACCESS_TOKEN])
}
func TestNacosAuthClient_LoginFailure(t *testing.T) {
mockAgent := &MockHttpAgent{
PostFunc: func(url string, header http.Header, timeoutMs uint64, params map[string]string) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusUnauthorized,
Body: NewMockResponseBody("Invalid credentials"),
}, nil
},
}
client := NewNacosAuthClient(
constant.ClientConfig{Username: "wrong-user", Password: "wrong-pass"},
[]constant.ServerConfig{{IpAddr: "localhost"}},
mockAgent,
)
success, err := client.Login()
assert.Error(t, err)
assert.False(t, success)
assert.Empty(t, client.GetAccessToken())
}

View File

@ -1,95 +0,0 @@
package security
import (
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
)
type RamContext struct {
SignatureRegionId string
AccessKey string
SecretKey string
SecurityToken string
EphemeralAccessKeyId bool
}
type RamAuthClient struct {
clientConfig constant.ClientConfig
ramCredentialProviders []RamCredentialProvider
resourceInjector map[string]ResourceInjector
matchedProvider RamCredentialProvider
}
func NewRamAuthClient(clientCfg constant.ClientConfig) *RamAuthClient {
var providers = []RamCredentialProvider{
&RamRoleArnCredentialProvider{
clientConfig: clientCfg,
},
&EcsRamRoleCredentialProvider{
clientConfig: clientCfg,
},
&OIDCRoleArnCredentialProvider{
clientConfig: clientCfg,
},
&CredentialsURICredentialProvider{
clientConfig: clientCfg,
},
&AutoRotateCredentialProvider{
clientConfig: clientCfg,
},
&StsTokenCredentialProvider{
clientConfig: clientCfg,
},
&AccessKeyCredentialProvider{
clientConfig: clientCfg,
},
}
injectors := map[string]ResourceInjector{
REQUEST_TYPE_NAMING: &NamingResourceInjector{},
REQUEST_TYPE_CONFIG: &ConfigResourceInjector{},
}
return &RamAuthClient{
clientConfig: clientCfg,
ramCredentialProviders: providers,
resourceInjector: injectors,
}
}
func NewRamAuthClientWithProvider(clientCfg constant.ClientConfig, ramCredentialProvider RamCredentialProvider) *RamAuthClient {
ramAuthClient := NewRamAuthClient(clientCfg)
if ramCredentialProvider != nil {
ramAuthClient.ramCredentialProviders = append(ramAuthClient.ramCredentialProviders, ramCredentialProvider)
}
return ramAuthClient
}
func (rac *RamAuthClient) Login() (bool, error) {
for _, provider := range rac.ramCredentialProviders {
if provider.MatchProvider() {
rac.matchedProvider = provider
break
}
}
if rac.matchedProvider == nil {
return false, nil
}
err := rac.matchedProvider.Init()
if err != nil {
return false, err
}
return true, nil
}
func (rac *RamAuthClient) GetSecurityInfo(resource RequestResource) map[string]string {
var securityInfo = make(map[string]string, 4)
if rac.matchedProvider == nil {
return securityInfo
}
ramContext := rac.matchedProvider.GetCredentialsForNacosClient()
rac.resourceInjector[resource.requestType].doInject(resource, ramContext, securityInfo)
return securityInfo
}
func (rac *RamAuthClient) UpdateServerList(serverList []constant.ServerConfig) {
return
}

View File

@ -1,380 +0,0 @@
package security
import (
"encoding/json"
"os"
"strconv"
"github.com/aliyun/aliyun-secretsmanager-client-go/sdk"
"github.com/aliyun/credentials-go/credentials"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
)
const (
ENV_PREFIX string = "ALIBABA_CLOUD_"
ACCESS_KEY_ID_KEY string = ENV_PREFIX + "ACCESS_KEY_ID"
ACCESS_KEY_SECRET_KEY string = ENV_PREFIX + "ACCESS_KEY_SECRET"
SECURITY_TOKEN_KEY string = ENV_PREFIX + "SECURITY_TOKEN"
SIGNATURE_REGION_ID_KEY string = ENV_PREFIX + "SIGNATURE_REGION_ID"
RAM_ROLE_NAME_KEY string = ENV_PREFIX + "RAM_ROLE_NAME"
ROLE_ARN_KEY string = ENV_PREFIX + "ROLE_ARN"
ROLE_SESSION_NAME_KEY string = ENV_PREFIX + "ROLE_SESSION_NAME"
ROLE_SESSION_EXPIRATION_KEY string = ENV_PREFIX + "ROLE_SESSION_EXPIRATION"
POLICY_KEY string = ENV_PREFIX + "POLICY"
OIDC_PROVIDER_ARN_KEY string = ENV_PREFIX + "OIDC_PROVIDER_ARN"
OIDC_TOKEN_FILE_KEY string = ENV_PREFIX + "OIDC_TOKEN_FILE"
CREDENTIALS_URI_KEY string = ENV_PREFIX + "CREDENTIALS_URI"
SECRET_NAME_KEY string = ENV_PREFIX + "SECRET_NAME"
)
func GetNacosProperties(property string, envKey string) string {
if property != "" {
return property
} else {
return os.Getenv(envKey)
}
}
type RamCredentialProvider interface {
MatchProvider() bool
Init() error
GetCredentialsForNacosClient() RamContext
}
type AccessKeyCredentialProvider struct {
clientConfig constant.ClientConfig
accessKey string
secretKey string
signatureRegionId string
}
func (provider *AccessKeyCredentialProvider) MatchProvider() bool {
accessKey := GetNacosProperties(provider.clientConfig.AccessKey, ACCESS_KEY_ID_KEY)
secretKey := GetNacosProperties(provider.clientConfig.SecretKey, ACCESS_KEY_SECRET_KEY)
return accessKey != "" && secretKey != ""
}
func (provider *AccessKeyCredentialProvider) Init() error {
provider.accessKey = GetNacosProperties(provider.clientConfig.AccessKey, ACCESS_KEY_ID_KEY)
provider.secretKey = GetNacosProperties(provider.clientConfig.SecretKey, ACCESS_KEY_SECRET_KEY)
if provider.clientConfig.RamConfig != nil {
provider.signatureRegionId = GetNacosProperties(provider.clientConfig.RamConfig.SignatureRegionId, SIGNATURE_REGION_ID_KEY)
} else {
provider.signatureRegionId = ""
}
return nil
}
func (provider *AccessKeyCredentialProvider) GetCredentialsForNacosClient() RamContext {
ramContext := RamContext{
AccessKey: provider.accessKey,
SecretKey: provider.secretKey,
SignatureRegionId: provider.signatureRegionId,
}
return ramContext
}
type AutoRotateCredentialProvider struct {
clientConfig constant.ClientConfig
secretManagerCacheClient *sdk.SecretManagerCacheClient
secretName string
signatureRegionId string
}
func (provider *AutoRotateCredentialProvider) MatchProvider() bool {
if provider.clientConfig.RamConfig == nil {
return false
}
secretName := GetNacosProperties(provider.clientConfig.RamConfig.SecretName, SECRET_NAME_KEY)
return secretName != ""
}
func (provider *AutoRotateCredentialProvider) Init() error {
secretName := GetNacosProperties(provider.clientConfig.RamConfig.SecretName, SECRET_NAME_KEY)
client, err := sdk.NewClient()
if err != nil {
return err
}
provider.secretManagerCacheClient = client
provider.secretName = secretName
provider.signatureRegionId = GetNacosProperties(provider.clientConfig.RamConfig.SignatureRegionId, SIGNATURE_REGION_ID_KEY)
return nil
}
func (provider *AutoRotateCredentialProvider) GetCredentialsForNacosClient() RamContext {
if provider.secretManagerCacheClient == nil || provider.secretName == "" {
return RamContext{}
}
secretInfo, err := provider.secretManagerCacheClient.GetSecretInfo(provider.secretName)
if err != nil {
return RamContext{}
}
var m map[string]string
err = json.Unmarshal([]byte(secretInfo.SecretValue), &m)
if err != nil {
return RamContext{}
}
accessKeyId := m["AccessKeyId"]
accessKeySecret := m["AccessKeySecret"]
ramContext := RamContext{
AccessKey: accessKeyId,
SecretKey: accessKeySecret,
SignatureRegionId: provider.signatureRegionId,
EphemeralAccessKeyId: false,
}
return ramContext
}
type StsTokenCredentialProvider struct {
clientConfig constant.ClientConfig
accessKey string
secretKey string
securityToken string
signatureRegionId string
}
func (provider *StsTokenCredentialProvider) MatchProvider() bool {
accessKey := GetNacosProperties(provider.clientConfig.AccessKey, ACCESS_KEY_ID_KEY)
secretKey := GetNacosProperties(provider.clientConfig.SecretKey, ACCESS_KEY_SECRET_KEY)
if provider.clientConfig.RamConfig == nil {
return false
}
stsToken := GetNacosProperties(provider.clientConfig.RamConfig.SecurityToken, SECURITY_TOKEN_KEY)
return accessKey != "" && secretKey != "" && stsToken != ""
}
func (provider *StsTokenCredentialProvider) Init() error {
provider.accessKey = GetNacosProperties(provider.clientConfig.AccessKey, ACCESS_KEY_ID_KEY)
provider.secretKey = GetNacosProperties(provider.clientConfig.SecretKey, ACCESS_KEY_SECRET_KEY)
provider.securityToken = GetNacosProperties(provider.clientConfig.RamConfig.SecurityToken, SECURITY_TOKEN_KEY)
provider.signatureRegionId = GetNacosProperties(provider.clientConfig.RamConfig.SignatureRegionId, SIGNATURE_REGION_ID_KEY)
return nil
}
func (provider *StsTokenCredentialProvider) GetCredentialsForNacosClient() RamContext {
ramContext := RamContext{
AccessKey: provider.accessKey,
SecretKey: provider.secretKey,
SecurityToken: provider.securityToken,
SignatureRegionId: provider.signatureRegionId,
EphemeralAccessKeyId: true,
}
return ramContext
}
type EcsRamRoleCredentialProvider struct {
clientConfig constant.ClientConfig
credentialClient credentials.Credential
signatureRegionId string
}
func (provider *EcsRamRoleCredentialProvider) MatchProvider() bool {
if provider.clientConfig.RamConfig == nil {
return false
}
ramRoleName := GetNacosProperties(provider.clientConfig.RamConfig.RamRoleName, RAM_ROLE_NAME_KEY)
return ramRoleName != ""
}
func (provider *EcsRamRoleCredentialProvider) Init() error {
ramRoleName := GetNacosProperties(provider.clientConfig.RamConfig.RamRoleName, RAM_ROLE_NAME_KEY)
credentialsConfig := new(credentials.Config).SetType("ecs_ram_role").SetRoleName(ramRoleName)
credentialClient, err := credentials.NewCredential(credentialsConfig)
if err != nil {
return err
}
provider.credentialClient = credentialClient
provider.signatureRegionId = GetNacosProperties(provider.clientConfig.RamConfig.SignatureRegionId, SIGNATURE_REGION_ID_KEY)
return nil
}
func (provider *EcsRamRoleCredentialProvider) GetCredentialsForNacosClient() RamContext {
if provider.credentialClient == nil {
return RamContext{}
}
credential, err := provider.credentialClient.GetCredential()
if err != nil {
return RamContext{}
}
ramContext := RamContext{
AccessKey: *credential.AccessKeyId,
SecretKey: *credential.AccessKeySecret,
SecurityToken: *credential.SecurityToken,
SignatureRegionId: provider.signatureRegionId,
EphemeralAccessKeyId: true,
}
return ramContext
}
type RamRoleArnCredentialProvider struct {
clientConfig constant.ClientConfig
credentialClient credentials.Credential
signatureRegionId string
}
func (provider *RamRoleArnCredentialProvider) MatchProvider() bool {
if provider.clientConfig.RamConfig == nil {
return false
}
accessKey := GetNacosProperties(provider.clientConfig.AccessKey, ACCESS_KEY_ID_KEY)
secretKey := GetNacosProperties(provider.clientConfig.SecretKey, ACCESS_KEY_SECRET_KEY)
roleArn := GetNacosProperties(provider.clientConfig.RamConfig.RoleArn, ROLE_ARN_KEY)
roleSessionName := GetNacosProperties(provider.clientConfig.RamConfig.RoleSessionName, ROLE_SESSION_NAME_KEY)
oidcProviderArn := GetNacosProperties(provider.clientConfig.RamConfig.OIDCProviderArn, OIDC_PROVIDER_ARN_KEY)
return accessKey == "" && secretKey == "" && roleArn != "" && roleSessionName != "" && oidcProviderArn == ""
}
func (provider *RamRoleArnCredentialProvider) Init() error {
accessKey := GetNacosProperties(provider.clientConfig.AccessKey, ACCESS_KEY_ID_KEY)
secretKey := GetNacosProperties(provider.clientConfig.SecretKey, ACCESS_KEY_SECRET_KEY)
roleArn := GetNacosProperties(provider.clientConfig.RamConfig.RoleArn, ROLE_ARN_KEY)
roleSessionName := GetNacosProperties(provider.clientConfig.RamConfig.RoleSessionName, ROLE_SESSION_NAME_KEY)
credentialsConfig := new(credentials.Config).SetType("ram_role_arn").
SetAccessKeyId(accessKey).SetAccessKeySecret(secretKey).
SetRoleArn(roleArn).SetRoleSessionName(roleSessionName)
if roleSessionExpiration := GetNacosProperties(strconv.Itoa(provider.clientConfig.RamConfig.RoleSessionExpiration), ROLE_SESSION_EXPIRATION_KEY); roleSessionExpiration != "" {
if roleSessionExpirationTime, err := strconv.Atoi(roleSessionExpiration); err == nil {
if roleSessionExpirationTime == 0 {
roleSessionExpirationTime = 3600
}
credentialsConfig.SetRoleSessionExpiration(roleSessionExpirationTime)
}
}
policy := GetNacosProperties(provider.clientConfig.RamConfig.Policy, POLICY_KEY)
if policy != "" {
credentialsConfig.SetPolicy(policy)
}
credentialClient, err := credentials.NewCredential(credentialsConfig)
if err != nil {
return err
}
provider.credentialClient = credentialClient
provider.signatureRegionId = GetNacosProperties(provider.clientConfig.RamConfig.SignatureRegionId, SIGNATURE_REGION_ID_KEY)
return nil
}
func (provider *RamRoleArnCredentialProvider) GetCredentialsForNacosClient() RamContext {
if provider.credentialClient == nil {
return RamContext{}
}
credential, err := provider.credentialClient.GetCredential()
if err != nil {
return RamContext{}
}
return RamContext{
AccessKey: *credential.AccessKeyId,
SecretKey: *credential.AccessKeySecret,
SecurityToken: *credential.SecurityToken,
SignatureRegionId: provider.signatureRegionId,
EphemeralAccessKeyId: true,
}
}
type OIDCRoleArnCredentialProvider struct {
clientConfig constant.ClientConfig
credentialClient credentials.Credential
signatureRegionId string
}
func (provider *OIDCRoleArnCredentialProvider) MatchProvider() bool {
if provider.clientConfig.RamConfig == nil {
return false
}
roleArn := GetNacosProperties(provider.clientConfig.RamConfig.RoleArn, ROLE_ARN_KEY)
roleSessionName := GetNacosProperties(provider.clientConfig.RamConfig.RoleSessionName, ROLE_SESSION_NAME_KEY)
oidcProviderArn := GetNacosProperties(provider.clientConfig.RamConfig.OIDCProviderArn, OIDC_PROVIDER_ARN_KEY)
oidcTokenFile := GetNacosProperties(provider.clientConfig.RamConfig.OIDCTokenFilePath, OIDC_TOKEN_FILE_KEY)
return roleArn != "" && roleSessionName != "" && oidcProviderArn != "" && oidcTokenFile != ""
}
func (provider *OIDCRoleArnCredentialProvider) Init() error {
ramRoleArn := GetNacosProperties(provider.clientConfig.RamConfig.RoleArn, ROLE_ARN_KEY)
roleSessionName := GetNacosProperties(provider.clientConfig.RamConfig.RoleSessionName, ROLE_SESSION_NAME_KEY)
oidcProviderArn := GetNacosProperties(provider.clientConfig.RamConfig.OIDCProviderArn, OIDC_PROVIDER_ARN_KEY)
oidcTokenFilePath := GetNacosProperties(provider.clientConfig.RamConfig.OIDCTokenFilePath, OIDC_TOKEN_FILE_KEY)
credentialsConfig := new(credentials.Config).SetType("oidc_role_arn").
SetRoleArn(ramRoleArn).SetRoleSessionName(roleSessionName).
SetOIDCProviderArn(oidcProviderArn).SetOIDCTokenFilePath(oidcTokenFilePath)
if roleSessionExpiration := GetNacosProperties(strconv.Itoa(provider.clientConfig.RamConfig.RoleSessionExpiration), ROLE_SESSION_EXPIRATION_KEY); roleSessionExpiration != "" {
if roleSessionExpirationTime, err := strconv.Atoi(roleSessionExpiration); err == nil {
if roleSessionExpirationTime == 0 {
roleSessionExpirationTime = 3600
}
credentialsConfig.SetRoleSessionExpiration(roleSessionExpirationTime)
}
}
policy := GetNacosProperties(provider.clientConfig.RamConfig.Policy, POLICY_KEY)
if policy != "" {
credentialsConfig.SetPolicy(policy)
}
credentialClient, err := credentials.NewCredential(credentialsConfig)
if err != nil {
return err
}
provider.credentialClient = credentialClient
provider.signatureRegionId = GetNacosProperties(provider.clientConfig.RamConfig.SignatureRegionId, SIGNATURE_REGION_ID_KEY)
return nil
}
func (provider *OIDCRoleArnCredentialProvider) GetCredentialsForNacosClient() RamContext {
if provider.credentialClient == nil {
return RamContext{}
}
credential, err := provider.credentialClient.GetCredential()
if err != nil {
return RamContext{}
}
return RamContext{
AccessKey: *credential.AccessKeyId,
SecretKey: *credential.AccessKeySecret,
SecurityToken: *credential.SecurityToken,
SignatureRegionId: provider.signatureRegionId,
EphemeralAccessKeyId: true,
}
}
type CredentialsURICredentialProvider struct {
clientConfig constant.ClientConfig
credentialClient credentials.Credential
signatureRegionId string
}
func (provider *CredentialsURICredentialProvider) MatchProvider() bool {
if provider.clientConfig.RamConfig == nil {
return false
}
credentialsURI := GetNacosProperties(provider.clientConfig.RamConfig.CredentialsURI, CREDENTIALS_URI_KEY)
return credentialsURI != ""
}
func (provider *CredentialsURICredentialProvider) Init() error {
credentialsURI := GetNacosProperties(provider.clientConfig.RamConfig.CredentialsURI, CREDENTIALS_URI_KEY)
credentialsConfig := new(credentials.Config).SetType("credentials_uri").SetURLCredential(credentialsURI)
credentialClient, err := credentials.NewCredential(credentialsConfig)
if err != nil {
return err
}
provider.credentialClient = credentialClient
provider.signatureRegionId = GetNacosProperties(provider.clientConfig.RamConfig.SignatureRegionId, SIGNATURE_REGION_ID_KEY)
return nil
}
func (provider *CredentialsURICredentialProvider) GetCredentialsForNacosClient() RamContext {
if provider.credentialClient == nil {
return RamContext{}
}
if provider.credentialClient == nil {
return RamContext{}
}
credential, err := provider.credentialClient.GetCredential()
if err != nil {
return RamContext{}
}
return RamContext{
AccessKey: *credential.AccessKeyId,
SecretKey: *credential.AccessKeySecret,
SecurityToken: *credential.SecurityToken,
SignatureRegionId: provider.signatureRegionId,
EphemeralAccessKeyId: true,
}
}

View File

@ -1,127 +0,0 @@
package security
import (
"fmt"
"strings"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
)
type ResourceInjector interface {
doInject(resource RequestResource, ramContext RamContext, param map[string]string)
}
const (
CONFIG_AK_FILED string = "Spas-AccessKey"
NAMING_AK_FILED string = "ak"
SECURITY_TOKEN_HEADER string = "Spas-SecurityToken"
SIGNATURE_VERSION_HEADER string = "signatureVersion"
SIGNATURE_VERSION_V4 string = "v4"
SERVICE_INFO_SPLITER string = "@@"
TIMESTAMP_HEADER string = "Timestamp"
SIGNATURE_HEADER string = "Spas-Signature"
)
type NamingResourceInjector struct {
}
func (n *NamingResourceInjector) doInject(resource RequestResource, ramContext RamContext, param map[string]string) {
param[NAMING_AK_FILED] = ramContext.AccessKey
if ramContext.EphemeralAccessKeyId {
param[SECURITY_TOKEN_HEADER] = ramContext.SecurityToken
}
secretKey := trySignatureWithV4(ramContext, param)
signatures := n.calculateSignature(resource, secretKey, ramContext)
for k, v := range signatures {
param[k] = v
}
}
func (n *NamingResourceInjector) calculateSignature(resource RequestResource, secretKey string, ramContext RamContext) map[string]string {
var result = make(map[string]string, 4)
signData := n.getSignData(n.getGroupedServiceName(resource))
signature, err := Sign(signData, secretKey)
if err != nil {
logger.Errorf("get v4 signatrue error: %v", err)
return result
}
result["signature"] = signature
result["data"] = signData
return result
}
func (n *NamingResourceInjector) getGroupedServiceName(resource RequestResource) string {
if strings.Contains(resource.resource, SERVICE_INFO_SPLITER) || resource.group == "" {
return resource.resource
}
return resource.group + SERVICE_INFO_SPLITER + resource.resource
}
func (n *NamingResourceInjector) getSignData(serviceName string) string {
if serviceName != "" {
return fmt.Sprintf("%d%s%s", time.Now().UnixMilli(), SERVICE_INFO_SPLITER, serviceName)
}
return fmt.Sprintf("%d", time.Now().UnixMilli())
}
type ConfigResourceInjector struct {
}
func (c *ConfigResourceInjector) doInject(resource RequestResource, ramContext RamContext, param map[string]string) {
param[CONFIG_AK_FILED] = ramContext.AccessKey
if ramContext.EphemeralAccessKeyId {
param[SECURITY_TOKEN_HEADER] = ramContext.SecurityToken
}
secretKey := trySignatureWithV4(ramContext, param)
signatures := c.calculateSignature(resource, secretKey, ramContext)
for k, v := range signatures {
param[k] = v
}
}
func (c *ConfigResourceInjector) calculateSignature(resource RequestResource, secretKey string, ramContext RamContext) map[string]string {
var result = make(map[string]string, 4)
resourceName := c.getResourceName(resource)
signHeaders := c.getSignHeaders(resourceName, secretKey)
for k, v := range signHeaders {
result[k] = v
}
return result
}
func (c *ConfigResourceInjector) getResourceName(resource RequestResource) string {
if resource.namespace != "" {
return resource.namespace + "+" + resource.group
} else {
return resource.group
}
}
func (c *ConfigResourceInjector) getSignHeaders(resource, secretKey string) map[string]string {
header := make(map[string]string, 4)
timeStamp := fmt.Sprintf("%d", time.Now().UnixMilli())
header[TIMESTAMP_HEADER] = timeStamp
if secretKey != "" {
var signature string
if strings.TrimSpace(resource) == "" {
signature = signWithHmacSha1Encrypt(timeStamp, secretKey)
} else {
signature = signWithHmacSha1Encrypt(resource+"+"+timeStamp, secretKey)
}
header[SIGNATURE_HEADER] = signature
}
return header
}
func trySignatureWithV4(ramContext RamContext, param map[string]string) string {
if ramContext.SignatureRegionId == "" {
return ramContext.SecretKey
}
signatureV4, err := finalSigningKeyStringWithDefaultInfo(ramContext.SecretKey, ramContext.SignatureRegionId)
if err != nil {
logger.Errorf("get v4 signatrue error: %v", err)
return ramContext.SecretKey
}
param[SIGNATURE_VERSION_HEADER] = SIGNATURE_VERSION_V4
return signatureV4
}

View File

@ -1,98 +0,0 @@
package security
import (
"github.com/stretchr/testify/assert"
"testing"
)
func Test_NamingResourceInjector_doInject(t *testing.T) {
namingResourceInjector := NamingResourceInjector{}
resource := BuildNamingResource("testNamespace", "testGroup", "testServiceName")
t.Run("test_doInject_v4_sts", func(t *testing.T) {
ramContext := RamContext{
AccessKey: "testAccessKey",
SecretKey: "testSecretKey",
SecurityToken: "testSecurityToken",
EphemeralAccessKeyId: true,
SignatureRegionId: "testSignatureRegionId",
}
param := map[string]string{}
namingResourceInjector.doInject(resource, ramContext, param)
assert.Equal(t, param[NAMING_AK_FILED], ramContext.AccessKey)
assert.Equal(t, param[SECURITY_TOKEN_HEADER], ramContext.SecurityToken)
assert.Equal(t, param[SIGNATURE_VERSION_HEADER], SIGNATURE_VERSION_V4)
assert.NotEmpty(t, param["signature"])
})
t.Run("test_doInject", func(t *testing.T) {
ramContext := RamContext{
AccessKey: "testAccessKey",
SecretKey: "testSecretKey",
}
param := map[string]string{}
namingResourceInjector.doInject(resource, ramContext, param)
assert.Equal(t, param[NAMING_AK_FILED], ramContext.AccessKey)
assert.Empty(t, param[SECURITY_TOKEN_HEADER])
assert.Empty(t, param[SIGNATURE_VERSION_HEADER])
assert.NotEmpty(t, param["signature"])
})
}
func Test_NamingResourceInjector_getGroupedServiceName(t *testing.T) {
namingResourceInjector := NamingResourceInjector{}
t.Run("test_getGroupedServiceName", func(t *testing.T) {
resource := BuildNamingResource("testNamespace", "testGroup", "testServiceName")
assert.Equal(t, namingResourceInjector.getGroupedServiceName(resource), "testGroup@@testServiceName")
})
t.Run("test_getGroupedServiceName_without_group", func(t *testing.T) {
resource := BuildNamingResource("testNamespace", "", "testServiceName")
assert.Equal(t, namingResourceInjector.getGroupedServiceName(resource), "testServiceName")
})
}
func Test_ConfigResourceInjector_doInject(t *testing.T) {
configResourceInjector := ConfigResourceInjector{}
resource := BuildConfigResource("testTenant", "testGroup", "testDataId")
t.Run("test_doInject_v4_sts", func(t *testing.T) {
ramContext := RamContext{
AccessKey: "testAccessKey",
SecretKey: "testSecretKey",
SecurityToken: "testSecurityToken",
EphemeralAccessKeyId: true,
SignatureRegionId: "testSignatureRegionId",
}
param := map[string]string{}
configResourceInjector.doInject(resource, ramContext, param)
assert.Equal(t, param[CONFIG_AK_FILED], ramContext.AccessKey)
assert.Equal(t, param[SECURITY_TOKEN_HEADER], ramContext.SecurityToken)
assert.Equal(t, param[SIGNATURE_VERSION_HEADER], SIGNATURE_VERSION_V4)
assert.NotEmpty(t, param[SIGNATURE_HEADER])
assert.NotEmpty(t, param[TIMESTAMP_HEADER])
})
t.Run("test_doInject", func(t *testing.T) {
ramContext := RamContext{
AccessKey: "testAccessKey",
SecretKey: "testSecretKey",
}
param := map[string]string{}
configResourceInjector.doInject(resource, ramContext, param)
assert.Equal(t, param[CONFIG_AK_FILED], ramContext.AccessKey)
assert.Empty(t, param[SECURITY_TOKEN_HEADER])
assert.Empty(t, param[SIGNATURE_VERSION_HEADER])
assert.NotEmpty(t, param[SIGNATURE_HEADER])
assert.NotEmpty(t, param[TIMESTAMP_HEADER])
})
}
func Test_ConfigResourceInjector_getResourceName(t *testing.T) {
configResourceInjector := ConfigResourceInjector{}
t.Run("test_getGroupedServiceName", func(t *testing.T) {
resource := BuildConfigResource("testTenant", "testGroup", "testDataId")
assert.Equal(t, configResourceInjector.getResourceName(resource), "testTenant+testGroup")
})
t.Run("test_getGroupedServiceName_without_group", func(t *testing.T) {
resource := BuildConfigResource("testTenant", "", "testDataId")
assert.Equal(t, configResourceInjector.getResourceName(resource), "testTenant+")
})
}

View File

@ -17,157 +17,145 @@
package security package security
import ( import (
"context" "encoding/json"
"errors"
"io/ioutil"
"net/http"
"strconv"
"strings"
"sync/atomic"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
) )
type RequestResource struct { type AuthClient struct {
requestType string username string
namespace string password string
group string accessToken *atomic.Value
resource string tokenTtl int64
lastRefreshTime int64
tokenRefreshWindow int64
agent http_agent.IHttpAgent
clientCfg constant.ClientConfig
serverCfgs []constant.ServerConfig
} }
const ( func NewAuthClient(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, agent http_agent.IHttpAgent) AuthClient {
REQUEST_TYPE_CONFIG = "config" client := AuthClient{
REQUEST_TYPE_NAMING = "naming" username: clientCfg.Username,
) password: clientCfg.Password,
serverCfgs: serverCfgs,
clientCfg: clientCfg,
agent: agent,
accessToken: &atomic.Value{},
}
func BuildConfigResourceByRequest(request rpc_request.IRequest) RequestResource { return client
if request.GetRequestType() == constant.CONFIG_QUERY_REQUEST_NAME {
configQueryRequest := request.(*rpc_request.ConfigQueryRequest)
return BuildConfigResource(configQueryRequest.Tenant, configQueryRequest.Group, configQueryRequest.DataId)
}
if request.GetRequestType() == constant.CONFIG_PUBLISH_REQUEST_NAME {
configPublishRequest := request.(*rpc_request.ConfigPublishRequest)
return BuildConfigResource(configPublishRequest.Tenant, configPublishRequest.Group, configPublishRequest.DataId)
}
if request.GetRequestType() == "ConfigRemoveRequest" {
configRemoveRequest := request.(*rpc_request.ConfigRemoveRequest)
return BuildConfigResource(configRemoveRequest.Tenant, configRemoveRequest.Group, configRemoveRequest.DataId)
}
return RequestResource{
requestType: REQUEST_TYPE_CONFIG,
}
} }
func BuildNamingResourceByRequest(request rpc_request.IRequest) RequestResource { func (ac *AuthClient) GetAccessToken() string {
if request.GetRequestType() == constant.INSTANCE_REQUEST_NAME { v := ac.accessToken.Load()
instanceRequest := request.(*rpc_request.InstanceRequest) if v == nil {
return BuildNamingResource(instanceRequest.Namespace, instanceRequest.GroupName, instanceRequest.ServiceName) return ""
}
if request.GetRequestType() == constant.BATCH_INSTANCE_REQUEST_NAME {
batchInstanceRequest := request.(*rpc_request.BatchInstanceRequest)
return BuildNamingResource(batchInstanceRequest.Namespace, batchInstanceRequest.GroupName, batchInstanceRequest.ServiceName)
}
if request.GetRequestType() == constant.SERVICE_LIST_REQUEST_NAME {
serviceListRequest := request.(*rpc_request.ServiceListRequest)
return BuildNamingResource(serviceListRequest.Namespace, serviceListRequest.GroupName, serviceListRequest.ServiceName)
}
if request.GetRequestType() == constant.SERVICE_QUERY_REQUEST_NAME {
serviceQueryRequest := request.(*rpc_request.ServiceQueryRequest)
return BuildNamingResource(serviceQueryRequest.Namespace, serviceQueryRequest.GroupName, serviceQueryRequest.ServiceName)
}
if request.GetRequestType() == constant.SUBSCRIBE_SERVICE_REQUEST_NAME {
subscribeServiceRequest := request.(*rpc_request.SubscribeServiceRequest)
return BuildNamingResource(subscribeServiceRequest.Namespace, subscribeServiceRequest.GroupName, subscribeServiceRequest.ServiceName)
}
return RequestResource{
requestType: REQUEST_TYPE_NAMING,
} }
return v.(string)
} }
func BuildConfigResource(tenant, group, dataId string) RequestResource { func (ac *AuthClient) AutoRefresh() {
return RequestResource{
requestType: REQUEST_TYPE_CONFIG,
namespace: tenant,
group: group,
resource: dataId,
}
}
func BuildNamingResource(namespace, group, serviceName string) RequestResource { // If the username is not set, the automatic refresh Token is not enabled
return RequestResource{
requestType: REQUEST_TYPE_NAMING,
namespace: namespace,
group: group,
resource: serviceName,
}
}
type AuthClient interface { if ac.username == "" {
Login() (bool, error) return
GetSecurityInfo(resource RequestResource) map[string]string }
UpdateServerList(serverList []constant.ServerConfig)
}
type SecurityProxy struct {
Clients []AuthClient
}
func (sp *SecurityProxy) Login() {
for _, client := range sp.Clients {
_, err := client.Login()
if err != nil {
logger.Errorf("login in err:%v", err)
}
}
}
func (sp *SecurityProxy) GetSecurityInfo(resource RequestResource) map[string]string {
var securityInfo = make(map[string]string, 4)
for _, client := range sp.Clients {
info := client.GetSecurityInfo(resource)
if info != nil {
for k, v := range info {
securityInfo[k] = v
}
}
}
return securityInfo
}
func (sp *SecurityProxy) UpdateServerList(serverList []constant.ServerConfig) {
for _, client := range sp.Clients {
client.UpdateServerList(serverList)
}
}
func (sp *SecurityProxy) AutoRefresh(ctx context.Context) {
go func() { go func() {
var timer = time.NewTimer(time.Second * time.Duration(5)) timer := time.NewTimer(time.Second * time.Duration(ac.tokenTtl-ac.tokenRefreshWindow))
defer timer.Stop()
for { for {
select { select {
case <-timer.C: case <-timer.C:
sp.Login() _, err := ac.Login()
timer.Reset(time.Second * time.Duration(5)) if err != nil {
case <-ctx.Done(): logger.Errorf("login has error %+v", err)
return }
timer.Reset(time.Second * time.Duration(ac.tokenTtl-ac.tokenRefreshWindow))
} }
} }
}() }()
} }
func NewSecurityProxy(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, agent http_agent.IHttpAgent) SecurityProxy { func (ac *AuthClient) Login() (bool, error) {
var securityProxy = SecurityProxy{} var throwable error = nil
securityProxy.Clients = []AuthClient{ for i := 0; i < len(ac.serverCfgs); i++ {
NewNacosAuthClient(clientCfg, serverCfgs, agent), result, err := ac.login(ac.serverCfgs[i])
NewRamAuthClient(clientCfg), throwable = err
if result {
return true, nil
} }
return securityProxy }
return false, throwable
} }
func NewSecurityProxyWithRamCredentialProvider(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, agent http_agent.IHttpAgent, provider RamCredentialProvider) SecurityProxy { func (ac *AuthClient) login(server constant.ServerConfig) (bool, error) {
var securityProxy = SecurityProxy{} if ac.username != "" {
securityProxy.Clients = []AuthClient{ contextPath := server.ContextPath
NewNacosAuthClient(clientCfg, serverCfgs, agent),
NewRamAuthClientWithProvider(clientCfg, provider), if !strings.HasPrefix(contextPath, "/") {
contextPath = "/" + contextPath
} }
return securityProxy
if strings.HasSuffix(contextPath, "/") {
contextPath = contextPath[0 : len(contextPath)-1]
}
if server.Scheme == "" {
server.Scheme = "http"
}
reqUrl := server.Scheme + "://" + server.IpAddr + ":" + strconv.FormatInt(int64(server.Port), 10) + contextPath + "/v1/auth/users/login"
header := http.Header{
"content-type": []string{"application/x-www-form-urlencoded"},
}
resp, err := ac.agent.Post(reqUrl, header, ac.clientCfg.TimeoutMs, map[string]string{
"username": ac.username,
"password": ac.password,
})
if err != nil {
return false, err
}
var bytes []byte
bytes, err = ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return false, err
}
if resp.StatusCode != constant.RESPONSE_CODE_SUCCESS {
errMsg := string(bytes)
return false, errors.New(errMsg)
}
var result map[string]interface{}
err = json.Unmarshal(bytes, &result)
if err != nil {
return false, err
}
if val, ok := result[constant.KEY_ACCESS_TOKEN]; ok {
ac.accessToken.Store(val)
ac.tokenTtl = int64(result[constant.KEY_TOKEN_TTL].(float64))
ac.tokenRefreshWindow = ac.tokenTtl / 10
}
}
return true, nil
} }

View File

@ -1,97 +0,0 @@
package security
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"fmt"
"time"
)
const (
PREFIX = "aliyun_v4"
CONSTANT = "aliyun_v4_request"
V4_SIGN_DATE_FORMATTER = "20060102"
SIGNATURE_V4_PRODUCE = "mse"
)
func signWithHmacSha1Encrypt(encryptText, encryptKey string) string {
key := []byte(encryptKey)
mac := hmac.New(sha1.New, key)
mac.Write([]byte(encryptText))
expectedMAC := mac.Sum(nil)
return base64.StdEncoding.EncodeToString(expectedMAC)
}
func Sign(data, key string) (string, error) {
signature, err := sign([]byte(data), []byte(key))
if err != nil {
return "", fmt.Errorf("unable to calculate a request signature: %w", err)
}
return base64.StdEncoding.EncodeToString(signature), nil
}
// sign 方法用于生成签名字节数组
func sign(data, key []byte) ([]byte, error) {
mac := hmac.New(sha1.New, key)
if _, err := mac.Write(data); err != nil {
return nil, err
}
return mac.Sum(nil), nil
}
func finalSigningKeyStringWithDefaultInfo(secret, region string) (string, error) {
signDate := time.Now().UTC().Format(V4_SIGN_DATE_FORMATTER)
return finalSigningKeyString(secret, signDate, region, SIGNATURE_V4_PRODUCE)
}
func finalSigningKeyString(secret, date, region, productCode string) (string, error) {
finalKey, err := finalSigningKey(secret, date, region, productCode)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(finalKey), nil
}
func finalSigningKey(secret, date, region, productCode string) ([]byte, error) {
secondSignkey, err := regionSigningKey(secret, date, region)
if err != nil {
return nil, err
}
mac := hmac.New(sha256.New, secondSignkey)
_, err = mac.Write([]byte(productCode))
if err != nil {
return nil, err
}
thirdSigningKey := mac.Sum(nil)
mac = hmac.New(sha256.New, thirdSigningKey)
_, err = mac.Write([]byte(CONSTANT))
if err != nil {
return nil, err
}
return mac.Sum(nil), nil
}
func regionSigningKey(secret, date, region string) ([]byte, error) {
firstSignkey, err := firstSigningKey(secret, date)
if err != nil {
return nil, err
}
mac := hmac.New(sha256.New, firstSignkey)
_, err = mac.Write([]byte(region))
if err != nil {
return nil, err
}
return mac.Sum(nil), nil
}
func firstSigningKey(secret, date string) ([]byte, error) {
mac := hmac.New(sha256.New, []byte(PREFIX+secret))
_, err := mac.Write([]byte(date))
if err != nil {
return nil, err
}
return mac.Sum(nil), nil
}

View File

@ -20,7 +20,7 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"os" "io/ioutil"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
) )
@ -48,7 +48,7 @@ func NewTLS(c constant.TLSConfig) (tc *tls.Config, err error) {
} }
func rootCert(caFile string) (*x509.CertPool, error) { func rootCert(caFile string) (*x509.CertPool, error) {
b, err := os.ReadFile(caFile) b, err := ioutil.ReadFile(caFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,6 +17,7 @@
package tls package tls
import ( import (
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -92,16 +93,16 @@ q9K53Jum9GDmkbUODa77sWR1zQsdrqSKywcjP/6FYXU9RMDqKUpm
) )
func Test_NewTLS(t *testing.T) { func Test_NewTLS(t *testing.T) {
dir, err := os.MkdirTemp("", "tls-test") dir, err := ioutil.TempDir("", "tls-test")
if err != nil { if err != nil {
t.Errorf(err.Error()) t.Errorf(err.Error())
} }
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
caPath, crtPath, keyPath := filepath.Join(dir, "ca.crt"), filepath.Join(dir, "client.crt"), filepath.Join(dir, "client.key") caPath, crtPath, keyPath := filepath.Join(dir, "ca.crt"), filepath.Join(dir, "client.crt"), filepath.Join(dir, "client.key")
os.WriteFile(caPath, testCaCrt, 0666) ioutil.WriteFile(caPath, testCaCrt, 0666)
os.WriteFile(crtPath, testClientCrt, 0666) ioutil.WriteFile(crtPath, testClientCrt, 0666)
os.WriteFile(keyPath, testClientKey, 0666) ioutil.WriteFile(keyPath, testClientKey, 0666)
t.Run("TestNoAuth", func(t *testing.T) { t.Run("TestNoAuth", func(t *testing.T) {
cfg, err := NewTLS(constant.SkipVerifyConfig) cfg, err := NewTLS(constant.SkipVerifyConfig)

View File

@ -1 +0,0 @@
LTAIxxxxxxxxxxxBHS21E6

View File

@ -18,123 +18,67 @@ package main
import ( import (
"fmt" "fmt"
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client" "github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/vo" "github.com/nacos-group/nacos-sdk-go/v2/vo"
"time"
) )
var localServerConfigWithOptions = constant.NewServerConfig(
"mse-d12e6112-p.nacos-ans.mse.aliyuncs.com",
8848,
)
var localClientConfigWithOptions = constant.NewClientConfig(
constant.WithTimeoutMs(10*1000),
constant.WithBeatInterval(2*1000),
constant.WithNotLoadCacheAtStart(true),
//constant.WithAccessKey(getFileContent(path.Join(getWDR(), "ak"))),
//constant.WithSecretKey(getFileContent(path.Join(getWDR(), "sk"))),
constant.WithAccessKey("LTAxxxgQL"),
constant.WithSecretKey("iG4xxxV6C"),
constant.WithOpenKMS(true),
constant.WithKMSVersion(constant.KMSv1),
constant.WithRegionId("cn-beijing"),
)
var localConfigList = []vo.ConfigParam{
{
DataId: "common-config",
Group: "default",
Content: "common",
},
{
DataId: "cipher-crypt",
Group: "default",
Content: "cipher",
},
{
DataId: "cipher-kms-aes-128-crypt",
Group: "default",
Content: "cipher-aes-128",
},
{
DataId: "cipher-kms-aes-256-crypt",
Group: "default",
Content: "cipher-aes-256",
},
}
func main() { func main() {
cc := constant.ClientConfig{
Endpoint: "acm.aliyun.com:8080",
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468",
RegionId: "cn-shanghai",
AccessKey: "LTAI4G8KxxxxxxxxxxxxxbwZLBr",
SecretKey: "n5jTL9YxxxxxxxxxxxxaxmPLZV9",
OpenKMS: true,
TimeoutMs: 5000,
}
// a more graceful way to create config client
client, err := clients.NewConfigClient(
vo.NacosClientParam{
ClientConfig: &cc,
},
)
client, err := createConfigClient()
if err != nil { if err != nil {
panic(err) panic(err)
} }
for _, localConfig := range localConfigList {
// to enable encrypt/decrypt, DataId should be start with "cipher-" // to enable encrypt/decrypt, DataId should be start with "cipher-"
configParam := vo.ConfigParam{ _, err = client.PublishConfig(vo.ConfigParam{
DataId: localConfig.DataId, DataId: "cipher-dataId-1",
Group: localConfig.Group, Group: "test-group",
Content: localConfig.Content, Content: "hello world!",
OnChange: func(namespace, group, dataId, data string) { })
fmt.Printf("successfully receive changed config: \n"+
"group[%s], dataId[%s], data[%s]\n", group, dataId, data)
},
}
err := client.ListenConfig(configParam)
if err != nil { if err != nil {
fmt.Printf("failed to listen: group[%s], dataId[%s] with error: %s\n", fmt.Printf("PublishConfig err: %v\n", err)
configParam.Group, configParam.DataId, err)
} else {
fmt.Printf("successfully ListenConfig: group[%s], dataId[%s]\n", configParam.Group, configParam.DataId)
} }
published, err := client.PublishConfig(configParam)
if published && err == nil {
fmt.Printf("successfully publish: group[%s], dataId[%s], data[%s]\n", configParam.Group, configParam.DataId, configParam.Content)
} else {
fmt.Printf("failed to publish: group[%s], dataId[%s], data[%s]\n with error: %s\n",
configParam.Group, configParam.DataId, configParam.Content, err)
}
//wait for config change callback to execute
time.Sleep(2 * time.Second)
//get config //get config
content, err := client.GetConfig(configParam) content, err := client.GetConfig(vo.ConfigParam{
if err == nil { DataId: "cipher-dataId-3",
fmt.Printf("successfully get config: group[%s], dataId[%s], data[%s]\n", configParam.Group, configParam.DataId, configParam.Content) Group: "test-group",
} else { })
fmt.Printf("failed to get config: group[%s], dataId[%s], data[%s]\n with error: %s\n", fmt.Printf("GetConfig, config: %s, error: %v\n", content, err)
configParam.Group, configParam.DataId, configParam.Content, err)
}
if content != localConfig.Content { // DataId is not start with "cipher-", content will not be encrypted.
panic("publish/get encrypted config failed.") _, err = client.PublishConfig(vo.ConfigParam{
} else { DataId: "dataId-1",
fmt.Println("publish/get encrypted config success.") Group: "test-group",
} Content: "hello world!",
//wait for config change callback to execute })
//time.Sleep(2 * time.Second)
}
}
func createConfigClient() (*config_client.ConfigClient, error) {
nc := nacos_client.NacosClient{}
_ = nc.SetServerConfig([]constant.ServerConfig{*localServerConfigWithOptions})
_ = nc.SetClientConfig(*localClientConfigWithOptions)
fmt.Println("ak: " + localClientConfigWithOptions.AccessKey)
fmt.Println("sk: " + localClientConfigWithOptions.SecretKey)
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
client, err := config_client.NewConfigClient(&nc)
if err != nil { if err != nil {
return nil, err fmt.Printf("PublishConfig err: %v\n", err)
} }
return client, nil
//get config
content, err = client.GetConfig(vo.ConfigParam{
DataId: "dataId-1",
Group: "test-group",
})
fmt.Printf("GetConfig, config: %s, error: %v\n", content, err)
} }

View File

@ -1 +0,0 @@
kr6JxxxxxxxxxxxxxY8nHnsD6

View File

@ -1,145 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 main
import (
"fmt"
"time"
"github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
func main() {
//create ClientConfig
cc := *constant.NewClientConfig(
constant.WithNamespaceId(""),
constant.WithTimeoutMs(5000),
constant.WithNotLoadCacheAtStart(true),
constant.WithLogDir("/tmp/nacos/log"),
constant.WithCacheDir("/tmp/nacos/cache"),
constant.WithLogLevel("debug"),
constant.WithAppName("yiyantest"),
constant.WithEndpoint("jmenv.tbsite.net:8080"),
constant.WithClusterName("serverlist"),
constant.WithEndpointQueryParams("nofix=1"),
constant.WithEndpointContextPath("nacos"),
)
// create config client
client, err := clients.NewConfigClient(
vo.NacosClientParam{
ClientConfig: &cc,
},
)
if err != nil {
panic(err)
}
//publish config
//config key=dataId+group+namespaceId
_, err = client.PublishConfig(vo.ConfigParam{
DataId: "test-data",
Group: "test-group",
Content: "hello world!",
})
_, err = client.PublishConfig(vo.ConfigParam{
DataId: "test-data-2",
Group: "test-group",
Content: "hello world!",
})
if err != nil {
fmt.Printf("PublishConfig err:%+v \n", err)
}
time.Sleep(1 * time.Second)
//get config
content, err := client.GetConfig(vo.ConfigParam{
DataId: "test-data",
Group: "test-group",
})
fmt.Println("GetConfig,config :" + content)
//Listen config change,key=dataId+group+namespaceId.
err = client.ListenConfig(vo.ConfigParam{
DataId: "test-data",
Group: "test-group",
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data)
},
})
err = client.ListenConfig(vo.ConfigParam{
DataId: "test-data-2",
Group: "test-group",
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data)
},
})
time.Sleep(1 * time.Second)
var content2 = "helo 130"
_, err = client.PublishConfig(vo.ConfigParam{
DataId: "test-data",
Group: "test-group",
Content: content2,
})
if err == nil {
fmt.Println("publish config success:" + "test-group" + ", dataId:" + "test-data" + ", content:" + content2)
} else {
fmt.Println("publish config fail :" + "test-group" + ", dataId:" + "test-data" + ", content:" + content2)
}
time.Sleep(1 * time.Second)
_, err = client.PublishConfig(vo.ConfigParam{
DataId: "test-data-2",
Group: "test-group",
Content: "test-listen",
})
time.Sleep(2 * time.Second)
time.Sleep(1 * time.Second)
_, err = client.DeleteConfig(vo.ConfigParam{
DataId: "test-data",
Group: "test-group",
})
fmt.Println("delete config success:" + "test-group" + ", dataId:" + "test-data")
time.Sleep(1 * time.Second)
/* //cancel config change
err = client.CancelListenConfig(vo.ConfigParam{
DataId: "test-data",
Group: "test-group",
})
*/
searchPage, _ := client.SearchConfig(vo.SearchConfigParam{
Search: "blur",
DataId: "test-data",
Group: "",
PageNo: 1,
PageSize: 10,
})
fmt.Printf("Search config:%+v \n", searchPage)
time.Sleep(1000 * time.Second)
}

View File

@ -1 +0,0 @@
LTAxxxxgQL

View File

@ -1,3 +0,0 @@
-----BEGIN CERTIFICATE-----
xxx
-----END CERTIFICATE-----

View File

@ -1,4 +0,0 @@
{
"KeyId": "KAAxxxxe74",
"PrivateKeyData": "MIIxxxA=="
}

View File

@ -1 +0,0 @@
kst-bjj64f82f41yjrs66eygc.cryptoservice.kms.aliyuncs.com

View File

@ -1,171 +0,0 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed 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 main
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"io/ioutil"
"os"
"path"
"time"
)
var localServerConfigWithOptions = constant.NewServerConfig(
"mse-cdf17f60-p.nacos-ans.mse.aliyuncs.com",
8848,
)
var localClientConfigWithOptions = constant.NewClientConfig(
constant.WithTimeoutMs(10*1000),
constant.WithBeatInterval(2*1000),
constant.WithNotLoadCacheAtStart(true),
constant.WithAccessKey(getFileContent(path.Join(getWDR(), "ak"))),
constant.WithSecretKey(getFileContent(path.Join(getWDR(), "sk"))),
//constant.WithNamespaceId("791fd262-3735-40df-a605-e3236f8ff495"),
constant.WithOpenKMS(true),
constant.WithKMSVersion(constant.KMSv3),
constant.WithKMSv3Config(&constant.KMSv3Config{
ClientKeyContent: getFileContent(path.Join(getWDR(), "client_key.json")),
Password: getFileContent(path.Join(getWDR(), "password")),
Endpoint: getFileContent(path.Join(getWDR(), "endpoint")),
CaContent: getFileContent(path.Join(getWDR(), "ca.pem")),
}),
constant.WithRegionId("cn-beijing"),
)
var localConfigList = []vo.ConfigParam{
{
DataId: "common-config",
Group: "default",
Content: "common普通&&",
},
{
DataId: "cipher-crypt",
Group: "default",
Content: "cipher加密&&",
KmsKeyId: "key-xxx", //可以识别
},
{
DataId: "cipher-kms-aes-128-crypt",
Group: "default",
Content: "cipher-aes-128加密&&",
KmsKeyId: "key-xxx", //可以识别
},
{
DataId: "cipher-kms-aes-256-crypt",
Group: "default",
Content: "cipher-aes-256加密&&",
KmsKeyId: "key-xxx", //可以识别
},
}
func main() {
usingKMSv3ClientAndStoredByNacos()
//onlyUsingFilters()
}
func usingKMSv3ClientAndStoredByNacos() {
client := createConfigClient()
if client == nil {
panic("init ConfigClient failed")
}
for _, localConfig := range localConfigList {
// to enable encrypt/decrypt, DataId should be start with "cipher-"
configParam := vo.ConfigParam{
DataId: localConfig.DataId,
Group: localConfig.Group,
Content: localConfig.Content,
KmsKeyId: localConfig.KmsKeyId,
OnChange: func(namespace, group, dataId, data string) {
fmt.Printf("successfully receive changed config: \n"+
"group[%s], dataId[%s], data[%s]\n", group, dataId, data)
},
}
err := client.ListenConfig(configParam)
if err != nil {
fmt.Printf("failed to listen: group[%s], dataId[%s] with error: %s\n",
configParam.Group, configParam.DataId, err)
} else {
fmt.Printf("successfully ListenConfig: group[%s], dataId[%s]\n", configParam.Group, configParam.DataId)
}
published, err := client.PublishConfig(configParam)
if published && err == nil {
fmt.Printf("successfully publish: group[%s], dataId[%s], data[%s]\n", configParam.Group, configParam.DataId, configParam.Content)
} else {
fmt.Printf("failed to publish: group[%s], dataId[%s], data[%s]\n with error: %s\n",
configParam.Group, configParam.DataId, configParam.Content, err)
}
//wait for config change callback to execute
time.Sleep(2 * time.Second)
//get config
content, err := client.GetConfig(configParam)
if err == nil {
fmt.Printf("successfully get config: group[%s], dataId[%s], data[%s]\n", configParam.Group, configParam.DataId, configParam.Content)
} else {
fmt.Printf("failed to get config: group[%s], dataId[%s], data[%s]\n with error: %s\n",
configParam.Group, configParam.DataId, configParam.Content, err)
}
if content != localConfig.Content {
panic("publish/get encrypted config failed.")
} else {
fmt.Println("publish/get encrypted config success.")
}
//wait for config change callback to execute
//time.Sleep(2 * time.Second)
}
}
func createConfigClient() *config_client.ConfigClient {
nc := nacos_client.NacosClient{}
_ = nc.SetServerConfig([]constant.ServerConfig{*localServerConfigWithOptions})
_ = nc.SetClientConfig(*localClientConfigWithOptions)
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
client, err := config_client.NewConfigClient(&nc)
if err != nil {
logger.Errorf("create config client failed: " + err.Error())
return nil
}
return client
}
func getWDR() string {
getwd, err := os.Getwd()
if err != nil {
return ""
}
return getwd
}
func getFileContent(filePath string) string {
file, err := ioutil.ReadFile(filePath)
if err != nil {
return ""
}
return string(file)
}

View File

@ -1 +0,0 @@
19axxxxx213

Some files were not shown because too many files have changed in this diff Show More