2.0.x (#496)
This commit is contained in:
parent
dec3976954
commit
373906a4be
|
@ -14,8 +14,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- go_version: 1.13
|
||||
- go_version: 1.14
|
||||
- go_version: 1.15
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
|
@ -24,6 +23,10 @@ jobs:
|
|||
go-version: ${{ matrix.config.go_version }}
|
||||
id: go
|
||||
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
|
|
64
README.md
64
README.md
|
@ -9,51 +9,55 @@
|
|||
Nacos-sdk-go for Go client allows you to access Nacos service,it supports service discovery and dynamic configuration.
|
||||
|
||||
## Requirements
|
||||
Supported Go version over 1.12
|
||||
|
||||
Supported Nacos version over 1.x
|
||||
Supported Go version over 1.15
|
||||
|
||||
Supported Nacos version over 2.x
|
||||
|
||||
## Installation
|
||||
|
||||
Use `go get` to install SDK:
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/nacos-group/nacos-sdk-go
|
||||
$ go get -u github.com/nacos-group/nacos-sdk-go/v2
|
||||
```
|
||||
|
||||
## Quick Examples
|
||||
|
||||
* ClientConfig
|
||||
|
||||
```go
|
||||
constant.ClientConfig{
|
||||
TimeoutMs uint64 // timeout for requesting Nacos server, default value is 10000ms
|
||||
NamespaceId string // the namespaceId of Nacos.When namespace is public, fill in the blank string here.
|
||||
AppName string // the appName
|
||||
Endpoint string // the endpoint for get Nacos server addresses
|
||||
RegionId string // the regionId for kms
|
||||
AccessKey string // the AccessKey for kms
|
||||
SecretKey string // the SecretKey for kms
|
||||
OpenKMS bool // it's to open kms,default is false. https://help.aliyun.com/product/28933.html
|
||||
NamespaceId string // the namespaceId of Nacos
|
||||
Endpoint string // the endpoint for ACM. https://help.aliyun.com/document_detail/130146.html
|
||||
RegionId string // the regionId for ACM & KMS
|
||||
AccessKey string // the AccessKey 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
|
||||
// , to enable encrypt/decrypt, DataId should be start with "cipher-"
|
||||
CacheDir string // the directory for persist nacos service info,default value is current path
|
||||
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
|
||||
UpdateCacheWhenEmpty bool // update cache when get empty service instance from server
|
||||
Username string // the username for nacos auth
|
||||
Password string // the password for nacos auth
|
||||
LogDir string // the directory for log, default is current path
|
||||
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
|
||||
LogLevel string // the level of log, it's must be debug,info,warn,error, default value is info
|
||||
LogSampling *ClientLogSamplingConfig // the sampling config of log
|
||||
ContextPath string // the nacos server contextpath
|
||||
LogRollingConfig *ClientLogRollingConfig // the log rolling config
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
* ServerConfig
|
||||
|
||||
```go
|
||||
constant.ServerConfig{
|
||||
ContextPath string // the nacos server context path
|
||||
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
|
||||
IpAddr string // the nacos server address
|
||||
Port uint64 // the nacos server port
|
||||
Scheme string // the nacos server scheme
|
||||
Port uint64 // nacos server port
|
||||
GrpcPort uint64 // nacos server grpc port, default=server port + 1000, this is not required
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -113,13 +117,13 @@ serverConfigs := []constant.ServerConfig{
|
|||
}
|
||||
|
||||
// Create naming client for service discovery
|
||||
_, _ = clients.CreateNamingClient(map[string]interface{}{
|
||||
_, _ := clients.CreateNamingClient(map[string]interface{}{
|
||||
"serverConfigs": serverConfigs,
|
||||
"clientConfig": clientConfig,
|
||||
})
|
||||
|
||||
// Create config client for dynamic configuration
|
||||
_, _ = clients.CreateConfigClient(map[string]interface{}{
|
||||
_, _ := clients.CreateConfigClient(map[string]interface{}{
|
||||
"serverConfigs": serverConfigs,
|
||||
"clientConfig": clientConfig,
|
||||
})
|
||||
|
@ -143,6 +147,7 @@ configClient, err := clients.NewConfigClient(
|
|||
```
|
||||
|
||||
### Create client for ACM
|
||||
|
||||
https://help.aliyun.com/document_detail/130146.html
|
||||
|
||||
```go
|
||||
|
@ -165,7 +170,6 @@ client, err := clients.NewConfigClient(
|
|||
)
|
||||
```
|
||||
|
||||
|
||||
### Service Discovery
|
||||
|
||||
* Register instance:RegisterInstance
|
||||
|
@ -262,7 +266,7 @@ err := namingClient.Subscribe(vo.SubscribeParam{
|
|||
ServiceName: "demo.go",
|
||||
GroupName: "group-a", // default value is DEFAULT_GROUP
|
||||
Clusters: []string{"cluster-a"}, // default value is DEFAULT
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func (services []model.Instance, err error) {
|
||||
log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services))
|
||||
},
|
||||
})
|
||||
|
@ -277,7 +281,7 @@ err := namingClient.Unsubscribe(vo.SubscribeParam{
|
|||
ServiceName: "demo.go",
|
||||
GroupName: "group-a", // default value is DEFAULT_GROUP
|
||||
Clusters: []string{"cluster-a"}, // default value is DEFAULT
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func (services []model.Instance, err error) {
|
||||
log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services))
|
||||
},
|
||||
})
|
||||
|
@ -285,9 +289,10 @@ err := namingClient.Unsubscribe(vo.SubscribeParam{
|
|||
```
|
||||
|
||||
* Get all services name:GetAllServicesInfo
|
||||
|
||||
```go
|
||||
|
||||
serviceInfos, err := client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f",
|
||||
PageNo: 1,
|
||||
PageSize: 10,
|
||||
|
@ -341,6 +346,7 @@ err := configClient.ListenConfig(vo.ConfigParam{
|
|||
})
|
||||
|
||||
```
|
||||
|
||||
* Cancel the listening of config change event:CancelListenConfig
|
||||
|
||||
```go
|
||||
|
@ -353,6 +359,7 @@ err := configClient.CancelListenConfig(vo.ConfigParam{
|
|||
```
|
||||
|
||||
* Search config: SearchConfig
|
||||
|
||||
```go
|
||||
configPage, err := configClient.SearchConfig(vo.SearchConfigParam{
|
||||
Search: "blur",
|
||||
|
@ -362,20 +369,27 @@ configPage, err := configClient.SearchConfig(vo.SearchConfigParam{
|
|||
PageSize: 10,
|
||||
})
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
We can run example to learn how to use nacos go client.
|
||||
|
||||
* [Config Example](./example/config)
|
||||
* [Naming Example](./example/service)
|
||||
|
||||
## Documentation
|
||||
|
||||
You can view the open-api documentation from the [Nacos open-api wepsite](https://nacos.io/en-us/docs/open-api.html).
|
||||
|
||||
You can view the full documentation from the [Nacos website](https://nacos.io/en-us/docs/what-is-nacos.html).
|
||||
|
||||
## Contributing
|
||||
Contributors are welcomed to join Nacos-sdk-go project. Please check [CONTRIBUTING.md](./CONTRIBUTING.md) about how to contribute to this project.
|
||||
|
||||
Contributors are welcomed to join Nacos-sdk-go project. Please check [CONTRIBUTING.md](./CONTRIBUTING.md) about how to
|
||||
contribute to this project.
|
||||
|
||||
## Contact
|
||||
|
||||
* Join us from DingDing Group(23191211).
|
||||
* [Gitter](https://gitter.im/alibaba/nacos): Nacos's IM tool for community messaging, collaboration and discovery.
|
||||
* [Twitter](https://twitter.com/nacos2): Follow along for latest nacos news on Twitter.
|
||||
|
|
26
README_CN.md
26
README_CN.md
|
@ -9,14 +9,14 @@
|
|||
Nacos-sdk-go是Nacos的Go语言客户端,它实现了服务发现和动态配置的功能
|
||||
|
||||
## 使用限制
|
||||
支持Go>v1.12版本
|
||||
支持Go>=v1.15版本
|
||||
|
||||
支持Nacos>1.x版本
|
||||
支持Nacos>2.x版本
|
||||
|
||||
## 安装
|
||||
使用`go get`安装SDK:
|
||||
```sh
|
||||
$ go get -u github.com/nacos-group/nacos-sdk-go
|
||||
$ go get -u github.com/nacos-group/nacos-sdk-go/v2
|
||||
```
|
||||
## 快速使用
|
||||
* ClientConfig
|
||||
|
@ -25,7 +25,6 @@ $ go get -u github.com/nacos-group/nacos-sdk-go
|
|||
constant.ClientConfig{
|
||||
TimeoutMs uint64 // 请求Nacos服务端的超时时间,默认是10000ms
|
||||
NamespaceId string // ACM的命名空间Id
|
||||
AppName string // App名称
|
||||
Endpoint string // 当使用ACM时,需要该配置. https://help.aliyun.com/document_detail/130146.html
|
||||
RegionId string // ACM&KMS的regionId,用于配置中心的鉴权
|
||||
AccessKey string // ACM&KMS的AccessKey,用于配置中心的鉴权
|
||||
|
@ -39,9 +38,9 @@ constant.ClientConfig{
|
|||
Username string // Nacos服务端的API鉴权Username
|
||||
Password string // Nacos服务端的API鉴权Password
|
||||
LogDir string // 日志存储路径
|
||||
RotateTime string // 日志轮转周期,比如:30m, 1h, 24h, 默认是24h
|
||||
MaxAge int64 // 日志最大文件数,默认3
|
||||
LogLevel string // 日志默认级别,值必须是:debug,info,warn,error,默认值是info
|
||||
LogSampling *ClientLogSamplingConfig // 日志采样配置
|
||||
LogRollingConfig *ClientLogRollingConfig // 日志归档配置
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -49,10 +48,11 @@ constant.ClientConfig{
|
|||
|
||||
```go
|
||||
constant.ServerConfig{
|
||||
ContextPath string // Nacos的ContextPath
|
||||
ContextPath string // Nacos的ContextPath,默认/nacos,在2.0中不需要设置
|
||||
IpAddr string // Nacos的服务地址
|
||||
Port uint64 // Nacos的服务端口
|
||||
Scheme string // Nacos的服务地址前缀
|
||||
Scheme string // Nacos的服务地址前缀,默认http,在2.0中不需要设置
|
||||
GrpcPort uint64 // Nacos的 grpc 服务端口, 默认为 服务端口+1000, 不是必填
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -114,13 +114,13 @@ serverConfigs := []constant.ServerConfig{
|
|||
}
|
||||
|
||||
// 创建服务发现客户端
|
||||
_, _ = clients.CreateNamingClient(map[string]interface{}{
|
||||
_, _ := clients.CreateNamingClient(map[string]interface{}{
|
||||
"serverConfigs": serverConfigs,
|
||||
"clientConfig": clientConfig,
|
||||
})
|
||||
|
||||
// 创建动态配置客户端
|
||||
_, _ = clients.CreateConfigClient(map[string]interface{}{
|
||||
_, _ := clients.CreateConfigClient(map[string]interface{}{
|
||||
"serverConfigs": serverConfigs,
|
||||
"clientConfig": clientConfig,
|
||||
})
|
||||
|
@ -262,7 +262,7 @@ err := namingClient.Subscribe(vo.SubscribeParam{
|
|||
ServiceName: "demo.go",
|
||||
GroupName: "group-a", // 默认值DEFAULT_GROUP
|
||||
Clusters: []string{"cluster-a"}, // 默认值DEFAULT
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services))
|
||||
},
|
||||
})
|
||||
|
@ -277,7 +277,7 @@ err := namingClient.Unsubscribe(vo.SubscribeParam{
|
|||
ServiceName: "demo.go",
|
||||
GroupName: "group-a", // 默认值DEFAULT_GROUP
|
||||
Clusters: []string{"cluster-a"}, // 默认值DEFAULT
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services))
|
||||
},
|
||||
})
|
||||
|
@ -287,7 +287,7 @@ err := namingClient.Unsubscribe(vo.SubscribeParam{
|
|||
* 获取服务名列表:GetAllServicesInfo
|
||||
```go
|
||||
|
||||
serviceInfos, err := client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f",
|
||||
PageNo: 1,
|
||||
PageSize: 10,
|
||||
|
|
|
@ -0,0 +1,467 @@
|
|||
/*
|
||||
* 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. DO NOT EDIT.
|
||||
// source: nacos_grpc_service.proto
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
math "math"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
any "github.com/golang/protobuf/ptypes/any"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// 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 string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,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,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Metadata) Reset() { *m = Metadata{} }
|
||||
func (m *Metadata) String() string { return proto.CompactTextString(m) }
|
||||
func (*Metadata) ProtoMessage() {}
|
||||
func (*Metadata) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f908b146bdb05ce9, []int{0}
|
||||
}
|
||||
|
||||
func (m *Metadata) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Metadata.Unmarshal(m, b)
|
||||
}
|
||||
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 ""
|
||||
}
|
||||
|
||||
func (m *Metadata) GetClientIp() string {
|
||||
if m != nil {
|
||||
return m.ClientIp
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Metadata) GetHeaders() map[string]string {
|
||||
if m != nil {
|
||||
return m.Headers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Payload struct {
|
||||
Metadata *Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
Body *any.Any `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Payload) Reset() { *m = Payload{} }
|
||||
func (m *Payload) String() string { return proto.CompactTextString(m) }
|
||||
func (*Payload) ProtoMessage() {}
|
||||
func (*Payload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f908b146bdb05ce9, []int{1}
|
||||
}
|
||||
|
||||
func (m *Payload) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Payload.Unmarshal(m, b)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func (m *Payload) GetBody() *any.Any {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Metadata)(nil), "Metadata")
|
||||
proto.RegisterMapType((map[string]string)(nil), "Metadata.HeadersEntry")
|
||||
proto.RegisterType((*Payload)(nil), "Payload")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("nacos_grpc_service.proto", fileDescriptor_f908b146bdb05ce9) }
|
||||
|
||||
var fileDescriptor_f908b146bdb05ce9 = []byte{
|
||||
// 333 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4f, 0x4b, 0xeb, 0x40,
|
||||
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,
|
||||
0xd6, 0x1a, 0x4c, 0xb3, 0x71, 0xb3, 0x29, 0xec, 0x37, 0xf2, 0x63, 0x4a, 0x37, 0x69, 0xb0, 0x20,
|
||||
0xde, 0x66, 0x7e, 0x7f, 0xe6, 0xc7, 0xcc, 0x80, 0x93, 0x61, 0x24, 0x8a, 0xf5, 0x46, 0xe6, 0xd1,
|
||||
0xba, 0xe0, 0x72, 0x97, 0x44, 0x9c, 0xe5, 0x52, 0x28, 0xe1, 0x9e, 0x6d, 0x84, 0xd8, 0xa4, 0x7c,
|
||||
0x6a, 0xba, 0xb0, 0x7c, 0x9d, 0x62, 0xa6, 0x2b, 0x6a, 0xf2, 0x49, 0xc0, 0x7e, 0xe2, 0x0a, 0x63,
|
||||
0x54, 0x48, 0x29, 0x74, 0x94, 0xce, 0xb9, 0xd3, 0x1e, 0x13, 0xaf, 0x1b, 0x98, 0x9a, 0xba, 0x60,
|
||||
0x47, 0x69, 0xc2, 0x33, 0xf5, 0x98, 0x3b, 0xb6, 0xc1, 0x9b, 0x9e, 0xce, 0xc0, 0x7a, 0xe3, 0x18,
|
||||
0x73, 0x59, 0x38, 0xd6, 0xb8, 0xed, 0xf5, 0xfc, 0x53, 0x76, 0x98, 0xc5, 0x1e, 0x2a, 0xe2, 0x3e,
|
||||
0x53, 0x52, 0x07, 0x07, 0x99, 0xbb, 0x80, 0xff, 0xdf, 0x09, 0x7a, 0x02, 0xed, 0x77, 0xae, 0x1d,
|
||||
0x62, 0x06, 0xef, 0x4b, 0x3a, 0x84, 0xbf, 0x3b, 0x4c, 0x4b, 0xee, 0xb4, 0x0c, 0x56, 0x35, 0x8b,
|
||||
0xd6, 0x9c, 0x4c, 0x5e, 0xc0, 0x5a, 0xa1, 0x4e, 0x05, 0xc6, 0xf4, 0x02, 0xec, 0x6d, 0x1d, 0x64,
|
||||
0x74, 0x3d, 0xbf, 0xdb, 0x24, 0x07, 0x0d, 0x45, 0x3d, 0xe8, 0x84, 0x22, 0xd6, 0x66, 0x9f, 0x9e,
|
||||
0x3f, 0x64, 0xd5, 0x19, 0xd8, 0xe1, 0x0c, 0xec, 0x2e, 0xd3, 0x81, 0x51, 0xf8, 0x73, 0xe8, 0x07,
|
||||
0xfc, 0xa3, 0xe4, 0x85, 0x7a, 0x56, 0x92, 0xe3, 0x96, 0x5e, 0x42, 0x5f, 0x1e, 0x01, 0x36, 0xab,
|
||||
0xc3, 0xdd, 0xa6, 0x9a, 0xfc, 0x99, 0x11, 0xff, 0x0a, 0xac, 0xda, 0x49, 0x47, 0x60, 0xd5, 0x9e,
|
||||
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,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// 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
|
||||
}
|
||||
x := &requestStreamRequestStreamClient{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
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
Metadata: "nacos_grpc_service.proto",
|
||||
}
|
||||
|
||||
// RequestClient is the client API for Request service.
|
||||
//
|
||||
// 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",
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.alibaba.nacos.api.grpc.auto";
|
||||
|
||||
message Metadata {
|
||||
string type = 3;
|
||||
string clientIp = 8;
|
||||
map<string, string> headers = 7;
|
||||
}
|
||||
|
||||
|
||||
message Payload {
|
||||
Metadata metadata = 2;
|
||||
google.protobuf.Any body = 3;
|
||||
}
|
||||
|
||||
service RequestStream {
|
||||
// build a streamRequest
|
||||
rpc requestStream (Payload) returns (stream Payload) {
|
||||
}
|
||||
}
|
||||
|
||||
service Request {
|
||||
// Sends a commonRequest
|
||||
rpc request (Payload) returns (Payload) {
|
||||
}
|
||||
}
|
||||
|
||||
service BiRequestStream {
|
||||
// Sends a commonRequest
|
||||
rpc requestBiStream (stream Payload) returns (stream Payload) {
|
||||
}
|
||||
}
|
|
@ -21,37 +21,32 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/file"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"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/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func GetFileName(cacheKey string, cacheDir string) string {
|
||||
|
||||
if runtime.GOOS == constant.OS_WINDOWS {
|
||||
cacheKey = strings.ReplaceAll(cacheKey, ":", constant.WINDOWS_LEGAL_NAME_SPLITER)
|
||||
}
|
||||
|
||||
return cacheDir + string(os.PathSeparator) + cacheKey
|
||||
}
|
||||
|
||||
func WriteServicesToFile(service model.Service, cacheDir string) {
|
||||
file.MkdirIfNecessary(cacheDir)
|
||||
sb, _ := json.Marshal(service)
|
||||
domFileName := GetFileName(util.GetServiceCacheKey(service.Name, service.Clusters), cacheDir)
|
||||
|
||||
err := ioutil.WriteFile(domFileName, sb, 0666)
|
||||
func WriteServicesToFile(service *model.Service, cacheKey, cacheDir string) {
|
||||
err := file.MkdirIfNecessary(cacheDir)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to write name cache:%s ,value:%s ,err:%+v", domFileName, string(sb), err)
|
||||
logger.Errorf("mkdir cacheDir failed,cacheDir:%s,err:", cacheDir, err)
|
||||
return
|
||||
}
|
||||
bytes, _ := json.Marshal(service)
|
||||
domFileName := GetFileName(cacheKey, cacheDir)
|
||||
err = ioutil.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 {
|
||||
|
@ -75,21 +70,29 @@ func ReadServicesFromFile(cacheDir string) map[string]model.Service {
|
|||
if service == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
serviceMap[f.Name()] = *service
|
||||
cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
|
||||
serviceMap[cacheKey] = *service
|
||||
}
|
||||
|
||||
logger.Info("finish loading name cache, total: " + strconv.Itoa(len(files)))
|
||||
logger.Infof("finish loading name cache, total: %s", strconv.Itoa(len(files)))
|
||||
return serviceMap
|
||||
}
|
||||
|
||||
func WriteConfigToFile(cacheKey string, cacheDir string, content string) {
|
||||
file.MkdirIfNecessary(cacheDir)
|
||||
fileName := GetFileName(cacheKey, cacheDir)
|
||||
if len(content) == 0 {
|
||||
// delete config snapshot
|
||||
if err := os.Remove(fileName); err != nil {
|
||||
logger.Errorf("failed to delete config file,cache:%s ,value:%s ,err:%+v", fileName, content, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
err := ioutil.WriteFile(fileName, []byte(content), 0666)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to write config cache:%s ,value:%s ,err:%+v", fileName, content, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ReadConfigFromFile(cacheKey string, cacheDir string) (string, error) {
|
||||
|
@ -100,3 +103,18 @@ func ReadConfigFromFile(cacheKey string, cacheDir string) (string, error) {
|
|||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// GetFailover , get failover content
|
||||
func GetFailover(key, dir string) string {
|
||||
filePath := dir + string(os.PathSeparator) + key + constant.FAILOVER_FILE_SUFFIX
|
||||
if !file.IsExistFile(filePath) {
|
||||
return ""
|
||||
}
|
||||
logger.GetLogger().Warn(fmt.Sprintf("reading failover content from path:%s", filePath))
|
||||
fileContent, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
logger.GetLogger().Error(fmt.Sprintf("fail to read failover content from %s", filePath))
|
||||
return ""
|
||||
}
|
||||
return string(fileContent)
|
||||
}
|
||||
|
|
|
@ -1,36 +1,38 @@
|
|||
/*
|
||||
* 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 cache
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/file"
|
||||
)
|
||||
|
||||
func TestGetFileName(t *testing.T) {
|
||||
|
||||
name := GetFileName("nacos@@providers:org.apache.dubbo.UserProvider:hangzhou", "tmp")
|
||||
|
||||
if runtime.GOOS == constant.OS_WINDOWS {
|
||||
assert.Equal(t, name, "tmp\\nacos@@providers&&org.apache.dubbo.UserProvider&&hangzhou")
|
||||
} else {
|
||||
assert.Equal(t, name, "tmp/nacos@@providers:org.apache.dubbo.UserProvider:hangzhou")
|
||||
func TestGetFailover(t *testing.T) {
|
||||
cacheKey := "test_failOver"
|
||||
dir := file.GetCurrentPath()
|
||||
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 ioutil.WriteFile(filepath, []byte(content), 0666)
|
||||
}
|
||||
|
|
|
@ -17,14 +17,15 @@
|
|||
package clients
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/config_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
|
||||
|
||||
"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/vo"
|
||||
)
|
||||
|
||||
// CreateConfigClient use to create config client
|
||||
|
@ -61,7 +62,7 @@ func NewNamingClient(param vo.NacosClientParam) (iClient naming_client.INamingCl
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
iClient = &naming
|
||||
iClient = naming
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -84,9 +85,15 @@ func setConfig(param vo.NacosClientParam) (iClient nacos_client.INacosClient, er
|
|||
client := &nacos_client.NacosClient{}
|
||||
if param.ClientConfig == nil {
|
||||
// default clientConfig
|
||||
_ = client.SetClientConfig(constant.ClientConfig{})
|
||||
_ = client.SetClientConfig(constant.ClientConfig{
|
||||
TimeoutMs: 10 * 1000,
|
||||
BeatInterval: 5 * 1000,
|
||||
})
|
||||
} else {
|
||||
_ = client.SetClientConfig(*param.ClientConfig)
|
||||
err = client.SetClientConfig(*param.ClientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(param.ServerConfigs) == 0 {
|
||||
|
@ -95,7 +102,7 @@ func setConfig(param vo.NacosClientParam) (iClient nacos_client.INacosClient, er
|
|||
err = errors.New("server configs not found in properties")
|
||||
return nil, err
|
||||
}
|
||||
_ = client.SetServerConfig([]constant.ServerConfig{})
|
||||
_ = client.SetServerConfig(nil)
|
||||
} else {
|
||||
err = client.SetServerConfig(param.ServerConfigs)
|
||||
if err != nil {
|
||||
|
@ -104,7 +111,9 @@ func setConfig(param vo.NacosClientParam) (iClient nacos_client.INacosClient, er
|
|||
}
|
||||
|
||||
if _, _err := client.GetHttpAgent(); _err != nil {
|
||||
_ = client.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
if clientCfg, err := client.GetClientConfig(); err == nil {
|
||||
_ = client.SetHttpAgent(&http_agent.HttpAgent{TlsConfig: clientCfg.TLSCfg})
|
||||
}
|
||||
}
|
||||
iClient = client
|
||||
return
|
||||
|
|
|
@ -1,26 +1,42 @@
|
|||
package clients
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSetConfigClient(t *testing.T) {
|
||||
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) {
|
||||
ip := getIntranetIP()
|
||||
sc := []constant.ServerConfig{
|
||||
*constant.NewServerConfig(
|
||||
"console.nacos.io",
|
||||
80,
|
||||
constant.WithScheme("http"),
|
||||
constant.WithContextPath("/nacos")),
|
||||
ip,
|
||||
8848,
|
||||
),
|
||||
}
|
||||
|
||||
cc := *constant.NewClientConfig(
|
||||
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"),
|
||||
constant.WithNamespaceId("public"),
|
||||
constant.WithTimeoutMs(5000),
|
||||
constant.WithNotLoadCacheAtStart(true),
|
||||
constant.WithLogDir("/tmp/nacos/log"),
|
||||
|
@ -49,5 +65,4 @@ func TestSetConfigClient(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
assert.True(t, reflect.DeepEqual(nacosClientFromMap, nacosClientFromStruct))
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -17,28 +17,34 @@
|
|||
package config_client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/cache"
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/nacos_error"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
|
||||
|
||||
"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/nacos_client"
|
||||
"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/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_response"
|
||||
"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/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
const (
|
||||
perTaskConfigSize = 3000
|
||||
executorErrDelay = 5 * time.Second
|
||||
)
|
||||
|
||||
type ConfigClient struct {
|
||||
|
@ -46,28 +52,27 @@ type ConfigClient struct {
|
|||
kmsClient *kms.Client
|
||||
localConfigs []vo.ConfigParam
|
||||
mutex sync.Mutex
|
||||
configProxy ConfigProxy
|
||||
configProxy IConfigProxy
|
||||
configCacheDir string
|
||||
currentTaskCount int32
|
||||
lastAllSyncTime time.Time
|
||||
cacheMap cache.ConcurrentMap
|
||||
schedulerMap cache.ConcurrentMap
|
||||
uid string
|
||||
listenExecute chan struct{}
|
||||
}
|
||||
|
||||
const (
|
||||
perTaskConfigSize = 3000
|
||||
executorErrDelay = 5 * time.Second
|
||||
)
|
||||
|
||||
type cacheData struct {
|
||||
isInitializing bool
|
||||
dataId string
|
||||
group string
|
||||
content string
|
||||
contentType string
|
||||
tenant string
|
||||
cacheDataListener *cacheDataListener
|
||||
md5 string
|
||||
appName string
|
||||
taskId int
|
||||
configClient *ConfigClient
|
||||
isSyncWithServer bool
|
||||
}
|
||||
|
||||
type cacheDataListener struct {
|
||||
|
@ -76,70 +81,56 @@ type cacheDataListener struct {
|
|||
}
|
||||
|
||||
func NewConfigClient(nc nacos_client.INacosClient) (*ConfigClient, error) {
|
||||
config := &ConfigClient{
|
||||
cacheMap: cache.NewConcurrentMap(),
|
||||
schedulerMap: cache.NewConcurrentMap(),
|
||||
}
|
||||
config.schedulerMap.Set("root", true)
|
||||
go config.delayScheduler(time.NewTimer(1*time.Millisecond), 500*time.Millisecond, "root", config.listenConfigExecutor())
|
||||
|
||||
config := &ConfigClient{}
|
||||
config.INacosClient = nc
|
||||
clientConfig, err := nc.GetClientConfig()
|
||||
if err != nil {
|
||||
return config, err
|
||||
return nil, err
|
||||
}
|
||||
serverConfig, err := nc.GetServerConfig()
|
||||
if err != nil {
|
||||
return config, err
|
||||
return nil, err
|
||||
}
|
||||
httpAgent, err := nc.GetHttpAgent()
|
||||
if err != nil {
|
||||
return config, err
|
||||
return nil, err
|
||||
}
|
||||
loggerConfig := logger.Config{
|
||||
LogFileName: constant.LOG_FILE_NAME,
|
||||
Level: clientConfig.LogLevel,
|
||||
Sampling: clientConfig.LogSampling,
|
||||
LogRollingConfig: clientConfig.LogRollingConfig,
|
||||
LogDir: clientConfig.LogDir,
|
||||
CustomLogger: clientConfig.CustomLogger,
|
||||
LogStdout: clientConfig.LogStdout,
|
||||
|
||||
if err = initLogger(clientConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = logger.InitLogger(loggerConfig)
|
||||
if err != nil {
|
||||
return config, err
|
||||
clientConfig.CacheDir = clientConfig.CacheDir + string(os.PathSeparator) + "config"
|
||||
config.configCacheDir = clientConfig.CacheDir
|
||||
|
||||
if config.configProxy, err = NewConfigProxy(serverConfig, clientConfig, httpAgent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.GetLogger().Infof("logDir:<%s> cacheDir:<%s>", clientConfig.LogDir, clientConfig.CacheDir)
|
||||
config.configCacheDir = clientConfig.CacheDir + string(os.PathSeparator) + "config"
|
||||
config.configProxy, err = NewConfigProxy(serverConfig, clientConfig, httpAgent)
|
||||
|
||||
if clientConfig.OpenKMS {
|
||||
kmsClient, err := kms.NewClientWithAccessKey(clientConfig.RegionId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return nil, err
|
||||
}
|
||||
config.kmsClient = kmsClient
|
||||
}
|
||||
|
||||
uid, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.uid = uid.String()
|
||||
|
||||
config.cacheMap = cache.NewConcurrentMap()
|
||||
// maximum buffered queue to prevent chan deadlocks during frequent configuration file updates
|
||||
config.listenExecute = make(chan struct{}, math.MaxInt64)
|
||||
|
||||
config.startInternal()
|
||||
|
||||
return config, err
|
||||
}
|
||||
|
||||
func (client *ConfigClient) sync() (clientConfig constant.ClientConfig,
|
||||
serverConfigs []constant.ServerConfig, agent http_agent.IHttpAgent, err error) {
|
||||
clientConfig, err = client.GetClientConfig()
|
||||
if err != nil {
|
||||
logger.Errorf("getClientConfig catch error:%+v", err)
|
||||
return
|
||||
}
|
||||
serverConfigs, err = client.GetServerConfig()
|
||||
if err != nil {
|
||||
logger.Errorf("getServerConfig catch error:%+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
agent, err = client.GetHttpAgent()
|
||||
if err != nil {
|
||||
logger.Errorf("getHttpAgent catch error:%+v", err)
|
||||
}
|
||||
return
|
||||
func initLogger(clientConfig constant.ClientConfig) error {
|
||||
return logger.InitLogger(logger.BuildLoggerConfig(clientConfig))
|
||||
}
|
||||
|
||||
func (client *ConfigClient) GetConfig(param vo.ConfigParam) (content string, err error) {
|
||||
|
@ -174,7 +165,7 @@ func (client *ConfigClient) encrypt(dataId, content string) (string, error) {
|
|||
request.Method = "POST"
|
||||
request.Scheme = "https"
|
||||
request.AcceptFormat = "json"
|
||||
request.KeyId = "alias/acs/acm" // use default key
|
||||
request.KeyId = "alias/acs/mse" // use default key
|
||||
request.Plaintext = content
|
||||
response, err := client.kmsClient.Encrypt(request)
|
||||
if err != nil {
|
||||
|
@ -191,56 +182,61 @@ func (client *ConfigClient) getConfigInner(param vo.ConfigParam) (content string
|
|||
return "", err
|
||||
}
|
||||
if len(param.Group) <= 0 {
|
||||
err = errors.New("[client.GetConfig] param.group can not be empty")
|
||||
return "", err
|
||||
param.Group = constant.DEFAULT_GROUP
|
||||
}
|
||||
|
||||
//todo 获取容灾配置的 EncryptedDataKey LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover
|
||||
clientConfig, _ := client.GetClientConfig()
|
||||
cacheKey := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)
|
||||
content, err = client.configProxy.GetConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
|
||||
content = cache.GetFailover(cacheKey, client.configCacheDir)
|
||||
if len(content) > 0 {
|
||||
logger.GetLogger().Warn(fmt.Sprintf("%s %s %s is using failover content!", clientConfig.NamespaceId, param.Group, param.DataId))
|
||||
return content, nil
|
||||
}
|
||||
response, err := client.configProxy.queryConfig(param.DataId, param.Group, clientConfig.NamespaceId,
|
||||
clientConfig.TimeoutMs, false, client)
|
||||
if err != nil {
|
||||
logger.Errorf("get config from server error:%+v ", err)
|
||||
if _, ok := err.(*nacos_error.NacosError); ok {
|
||||
nacosErr := err.(*nacos_error.NacosError)
|
||||
if nacosErr.ErrorCode() == "404" {
|
||||
cache.WriteConfigToFile(cacheKey, client.configCacheDir, "")
|
||||
logger.Warnf("[client.GetConfig] config not found, dataId: %s, group: %s, namespaceId: %s.", param.DataId, param.Group, clientConfig.NamespaceId)
|
||||
return "", nil
|
||||
}
|
||||
if nacosErr.ErrorCode() == "403" {
|
||||
return "", errors.New("get config forbidden")
|
||||
}
|
||||
}
|
||||
logger.Infof("get config from server error:%+v ", err)
|
||||
content, err = cache.ReadConfigFromFile(cacheKey, client.configCacheDir)
|
||||
if err != nil {
|
||||
logger.Errorf("get config from cache error:%+v ", err)
|
||||
return "", errors.New("read config from both server and cache fail")
|
||||
}
|
||||
|
||||
} else {
|
||||
cache.WriteConfigToFile(cacheKey, client.configCacheDir, content)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
return response.Content, nil
|
||||
}
|
||||
|
||||
func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool,
|
||||
err error) {
|
||||
func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool, err error) {
|
||||
if len(param.DataId) <= 0 {
|
||||
err = errors.New("[client.PublishConfig] param.dataId can not be empty")
|
||||
}
|
||||
if len(param.Group) <= 0 {
|
||||
err = errors.New("[client.PublishConfig] param.group can not be empty")
|
||||
return
|
||||
}
|
||||
if len(param.Content) <= 0 {
|
||||
err = errors.New("[client.PublishConfig] param.content can not be empty")
|
||||
return
|
||||
}
|
||||
|
||||
param.Content, err = client.encrypt(param.DataId, param.Content)
|
||||
if err != nil {
|
||||
return false, err
|
||||
if len(param.Group) <= 0 {
|
||||
param.Group = constant.DEFAULT_GROUP
|
||||
}
|
||||
if param.Content, err = client.encrypt(param.DataId, param.Content); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
clientConfig, _ := client.GetClientConfig()
|
||||
return client.configProxy.PublishConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
request := rpc_request.NewConfigPublishRequest(param.Group, param.DataId, clientConfig.NamespaceId, param.Content, param.CasMd5)
|
||||
request.AdditionMap["tag"] = param.Tag
|
||||
request.AdditionMap["appName"] = param.AppName
|
||||
request.AdditionMap["betaIps"] = param.BetaIps
|
||||
request.AdditionMap["type"] = param.Type
|
||||
request.AdditionMap["encryptedDataKey"] = param.EncryptedDataKey
|
||||
rpcClient := client.configProxy.getRpcClient(client)
|
||||
response, err := client.configProxy.requestProxy(rpcClient, request, constant.DEFAULT_TIMEOUT_MILLS)
|
||||
if response != nil {
|
||||
return response.IsSuccess(), err
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (client *ConfigClient) DeleteConfig(param vo.ConfigParam) (deleted bool, err error) {
|
||||
|
@ -248,11 +244,19 @@ func (client *ConfigClient) DeleteConfig(param vo.ConfigParam) (deleted bool, er
|
|||
err = errors.New("[client.DeleteConfig] param.dataId can not be empty")
|
||||
}
|
||||
if len(param.Group) <= 0 {
|
||||
err = errors.New("[client.DeleteConfig] param.group can not be empty")
|
||||
param.Group = constant.DEFAULT_GROUP
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
clientConfig, _ := client.GetClientConfig()
|
||||
return client.configProxy.DeleteConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
request := rpc_request.NewConfigRemoveRequest(param.Group, param.DataId, clientConfig.NamespaceId)
|
||||
rpcClient := client.configProxy.getRpcClient(client)
|
||||
response, err := client.configProxy.requestProxy(rpcClient, request, constant.DEFAULT_TIMEOUT_MILLS)
|
||||
if response != nil {
|
||||
return response.IsSuccess(), err
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
//Cancel Listen Config
|
||||
|
@ -264,32 +268,9 @@ func (client *ConfigClient) CancelListenConfig(param vo.ConfigParam) (err error)
|
|||
}
|
||||
client.cacheMap.Remove(util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId))
|
||||
logger.Infof("Cancel listen config DataId:%s Group:%s", param.DataId, param.Group)
|
||||
remakeId := int(math.Ceil(float64(client.cacheMap.Count()) / float64(perTaskConfigSize)))
|
||||
currentTaskCount := int(atomic.LoadInt32(&client.currentTaskCount))
|
||||
if remakeId < currentTaskCount {
|
||||
client.remakeCacheDataTaskId(remakeId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Remake cache data taskId
|
||||
func (client *ConfigClient) remakeCacheDataTaskId(remakeId int) {
|
||||
for i := 0; i < remakeId; i++ {
|
||||
count := 0
|
||||
for _, key := range client.cacheMap.Keys() {
|
||||
if count == perTaskConfigSize {
|
||||
break
|
||||
}
|
||||
if value, ok := client.cacheMap.Get(key); ok {
|
||||
cData := value.(cacheData)
|
||||
cData.taskId = i
|
||||
client.cacheMap.Set(key, cData)
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
|
||||
if len(param.DataId) <= 0 {
|
||||
err = errors.New("[client.ListenConfig] DataId can not be empty")
|
||||
|
@ -306,16 +287,17 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
|
|||
}
|
||||
|
||||
key := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)
|
||||
var cData cacheData
|
||||
var cData *cacheData
|
||||
if v, ok := client.cacheMap.Get(key); ok {
|
||||
cData = v.(cacheData)
|
||||
cData = v.(*cacheData)
|
||||
cData.isInitializing = true
|
||||
} else {
|
||||
var (
|
||||
content string
|
||||
md5Str string
|
||||
)
|
||||
if content, _ = cache.ReadConfigFromFile(key, client.configCacheDir); len(content) > 0 {
|
||||
content, _ = cache.ReadConfigFromFile(key, client.configCacheDir)
|
||||
if len(content) > 0 {
|
||||
md5Str = util.Md5(content)
|
||||
}
|
||||
listener := &cacheDataListener{
|
||||
|
@ -323,7 +305,7 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
|
|||
lastMd5: md5Str,
|
||||
}
|
||||
|
||||
cData = cacheData{
|
||||
cData = &cacheData{
|
||||
isInitializing: true,
|
||||
dataId: param.DataId,
|
||||
group: param.Group,
|
||||
|
@ -332,191 +314,22 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
|
|||
md5: md5Str,
|
||||
cacheDataListener: listener,
|
||||
taskId: client.cacheMap.Count() / perTaskConfigSize,
|
||||
configClient: client,
|
||||
}
|
||||
}
|
||||
client.cacheMap.Set(key, cData)
|
||||
return
|
||||
}
|
||||
|
||||
// Delay Scheduler
|
||||
// initialDelay the time to delay first execution
|
||||
// delay the delay between the termination of one execution and the commencement of the next
|
||||
func (client *ConfigClient) delayScheduler(t *time.Timer, delay time.Duration, taskId string, execute func() error) {
|
||||
for {
|
||||
if v, ok := client.schedulerMap.Get(taskId); ok {
|
||||
if !v.(bool) {
|
||||
return
|
||||
}
|
||||
}
|
||||
<-t.C
|
||||
d := delay
|
||||
if err := execute(); err != nil {
|
||||
d = executorErrDelay
|
||||
}
|
||||
t.Reset(d)
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for the configuration executor
|
||||
func (client *ConfigClient) listenConfigExecutor() func() error {
|
||||
return func() error {
|
||||
listenerSize := client.cacheMap.Count()
|
||||
taskCount := int(math.Ceil(float64(listenerSize) / float64(perTaskConfigSize)))
|
||||
currentTaskCount := int(atomic.LoadInt32(&client.currentTaskCount))
|
||||
if taskCount > currentTaskCount {
|
||||
for i := currentTaskCount; i < taskCount; i++ {
|
||||
client.schedulerMap.Set(strconv.Itoa(i), true)
|
||||
go client.delayScheduler(time.NewTimer(1*time.Millisecond), 10*time.Millisecond, strconv.Itoa(i), client.longPulling(i))
|
||||
}
|
||||
atomic.StoreInt32(&client.currentTaskCount, int32(taskCount))
|
||||
} else if taskCount < currentTaskCount {
|
||||
for i := taskCount; i < currentTaskCount; i++ {
|
||||
if _, ok := client.schedulerMap.Get(strconv.Itoa(i)); ok {
|
||||
client.schedulerMap.Set(strconv.Itoa(i), false)
|
||||
}
|
||||
}
|
||||
atomic.StoreInt32(&client.currentTaskCount, int32(taskCount))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Long polling listening configuration
|
||||
func (client *ConfigClient) longPulling(taskId int) func() error {
|
||||
return func() error {
|
||||
var listeningConfigs string
|
||||
initializationList := make([]cacheData, 0)
|
||||
for _, key := range client.cacheMap.Keys() {
|
||||
if value, ok := client.cacheMap.Get(key); ok {
|
||||
cData := value.(cacheData)
|
||||
if cData.taskId == taskId {
|
||||
if cData.isInitializing {
|
||||
initializationList = append(initializationList, cData)
|
||||
}
|
||||
if len(cData.tenant) > 0 {
|
||||
listeningConfigs += cData.dataId + constant.SPLIT_CONFIG_INNER + cData.group + constant.SPLIT_CONFIG_INNER +
|
||||
cData.md5 + constant.SPLIT_CONFIG_INNER + cData.tenant + constant.SPLIT_CONFIG
|
||||
} else {
|
||||
listeningConfigs += cData.dataId + constant.SPLIT_CONFIG_INNER + cData.group + constant.SPLIT_CONFIG_INNER +
|
||||
cData.md5 + constant.SPLIT_CONFIG
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(listeningConfigs) > 0 {
|
||||
clientConfig, err := client.GetClientConfig()
|
||||
if err != nil {
|
||||
logger.Errorf("[checkConfigInfo.GetClientConfig] err: %+v", err)
|
||||
return err
|
||||
}
|
||||
// http get
|
||||
params := make(map[string]string)
|
||||
params[constant.KEY_LISTEN_CONFIGS] = listeningConfigs
|
||||
|
||||
var changed string
|
||||
changedTmp, err := client.configProxy.ListenConfig(params, len(initializationList) > 0, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
if err == nil {
|
||||
changed = changedTmp
|
||||
} else {
|
||||
if _, ok := err.(*nacos_error.NacosError); ok {
|
||||
changed = changedTmp
|
||||
} else {
|
||||
logger.Errorf("[client.ListenConfig] listen config error: %+v", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
for _, v := range initializationList {
|
||||
v.isInitializing = false
|
||||
client.cacheMap.Set(util.GetConfigCacheKey(v.dataId, v.group, v.tenant), v)
|
||||
}
|
||||
if len(strings.ToLower(strings.Trim(changed, " "))) == 0 {
|
||||
logger.Info("[client.ListenConfig] no change")
|
||||
} else {
|
||||
logger.Info("[client.ListenConfig] config changed:" + changed)
|
||||
client.callListener(changed, clientConfig.NamespaceId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Execute the Listener callback func()
|
||||
func (client *ConfigClient) callListener(changed, tenant string) {
|
||||
changedDecoded, _ := url.QueryUnescape(changed)
|
||||
changedConfigs := strings.Split(changedDecoded, "\u0001")
|
||||
for _, config := range changedConfigs {
|
||||
attrs := strings.Split(config, "\u0002")
|
||||
if len(attrs) >= 2 {
|
||||
if value, ok := client.cacheMap.Get(util.GetConfigCacheKey(attrs[0], attrs[1], tenant)); ok {
|
||||
cData := value.(cacheData)
|
||||
content, err := client.getConfigInner(vo.ConfigParam{
|
||||
DataId: cData.dataId,
|
||||
Group: cData.group,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Errorf("[client.getConfigInner] DataId:[%s] Group:[%s] Error:[%+v]", cData.dataId, cData.group, err)
|
||||
continue
|
||||
}
|
||||
cData.content = content
|
||||
cData.md5 = util.Md5(content)
|
||||
if cData.md5 != cData.cacheDataListener.lastMd5 {
|
||||
go cData.cacheDataListener.listener(tenant, attrs[1], attrs[0], cData.content)
|
||||
cData.cacheDataListener.lastMd5 = cData.md5
|
||||
client.cacheMap.Set(util.GetConfigCacheKey(cData.dataId, cData.group, tenant), cData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (client *ConfigClient) buildBasePath(serverConfig constant.ServerConfig) (basePath string) {
|
||||
basePath = "http://" + serverConfig.IpAddr + ":" +
|
||||
strconv.FormatUint(serverConfig.Port, 10) + serverConfig.ContextPath + constant.CONFIG_PATH
|
||||
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)
|
||||
}
|
||||
|
||||
func (client *ConfigClient) PublishAggr(param vo.ConfigParam) (published bool,
|
||||
err error) {
|
||||
if len(param.DataId) <= 0 {
|
||||
err = errors.New("[client.PublishAggr] param.dataId can not be empty")
|
||||
}
|
||||
if len(param.Group) <= 0 {
|
||||
err = errors.New("[client.PublishAggr] param.group can not be empty")
|
||||
}
|
||||
if len(param.Content) <= 0 {
|
||||
err = errors.New("[client.PublishAggr] param.content can not be empty")
|
||||
}
|
||||
if len(param.DatumId) <= 0 {
|
||||
err = errors.New("[client.PublishAggr] param.DatumId can not be empty")
|
||||
}
|
||||
clientConfig, _ := client.GetClientConfig()
|
||||
return client.configProxy.PublishAggProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
func (client *ConfigClient) CloseClient() {
|
||||
client.configProxy.getRpcClient(client).Shutdown()
|
||||
}
|
||||
|
||||
func (client *ConfigClient) RemoveAggr(param vo.ConfigParam) (published bool,
|
||||
err error) {
|
||||
if len(param.DataId) <= 0 {
|
||||
err = errors.New("[client.DeleteAggr] param.dataId can not be empty")
|
||||
}
|
||||
if len(param.Group) <= 0 {
|
||||
err = errors.New("[client.DeleteAggr] param.group can not be empty")
|
||||
}
|
||||
if len(param.Content) <= 0 {
|
||||
err = errors.New("[client.DeleteAggr] param.content can not be empty")
|
||||
}
|
||||
if len(param.DatumId) <= 0 {
|
||||
err = errors.New("[client.DeleteAggr] param.DatumId can not be empty")
|
||||
}
|
||||
clientConfig, _ := client.GetClientConfig()
|
||||
return client.configProxy.DeleteAggProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
}
|
||||
|
||||
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" {
|
||||
return nil, errors.New("[client.searchConfigInner] param.search must be accurate or blur")
|
||||
}
|
||||
|
@ -527,13 +340,13 @@ func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParam) (*mode
|
|||
param.PageSize = 10
|
||||
}
|
||||
clientConfig, _ := client.GetClientConfig()
|
||||
configItems, err := client.configProxy.SearchConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
configItems, err := client.configProxy.searchConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
|
||||
if err != nil {
|
||||
logger.Errorf("search config from server error:%+v ", err)
|
||||
if _, ok := err.(*nacos_error.NacosError); ok {
|
||||
nacosErr := err.(*nacos_error.NacosError)
|
||||
if nacosErr.ErrorCode() == "404" {
|
||||
return nil, nil
|
||||
return nil, errors.New("config not found")
|
||||
}
|
||||
if nacosErr.ErrorCode() == "403" {
|
||||
return nil, errors.New("get config forbidden")
|
||||
|
@ -543,3 +356,130 @@ func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParam) (*mode
|
|||
}
|
||||
return configItems, nil
|
||||
}
|
||||
|
||||
func (client *ConfigClient) startInternal() {
|
||||
timer := time.NewTimer(executorErrDelay)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-client.listenExecute:
|
||||
client.executeConfigListen()
|
||||
case <-timer.C:
|
||||
client.executeConfigListen()
|
||||
}
|
||||
timer.Reset(executorErrDelay)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (client *ConfigClient) executeConfigListen() {
|
||||
listenCachesMap := make(map[int][]*cacheData, 16)
|
||||
needAllSync := time.Since(client.lastAllSyncTime) >= constant.ALL_SYNC_INTERNAL
|
||||
for _, v := range client.cacheMap.Items() {
|
||||
cache, ok := v.(*cacheData)
|
||||
if !ok {
|
||||
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 {
|
||||
hasChangedKeys = true
|
||||
for _, v := range response.ChangedConfigs {
|
||||
changeKey := util.GetConfigCacheKey(v.DataId, v.Group, v.Tenant)
|
||||
changeKeys[changeKey] = struct{}{}
|
||||
if cache, ok := client.cacheMap.Get(changeKey); !ok {
|
||||
continue
|
||||
} else {
|
||||
cacheData := cache.(*cacheData)
|
||||
client.refreshContentAndCheck(cacheData, !cacheData.isInitializing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range listenCaches {
|
||||
changeKey := util.GetConfigCacheKey(v.dataId, v.group, v.tenant)
|
||||
if _, ok := changeKeys[changeKey]; !ok {
|
||||
v.isSyncWithServer = true
|
||||
continue
|
||||
}
|
||||
v.isInitializing = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if needAllSync {
|
||||
client.lastAllSyncTime = time.Now()
|
||||
}
|
||||
|
||||
if hasChangedKeys {
|
||||
client.notifyListenConfig()
|
||||
}
|
||||
monitor.GetListenConfigCountMonitor().Set(float64(client.cacheMap.Count()))
|
||||
}
|
||||
|
||||
func buildConfigBatchListenRequest(caches []*cacheData) *rpc_request.ConfigBatchListenRequest {
|
||||
request := rpc_request.NewConfigBatchListenRequest(len(caches))
|
||||
for _, cache := range caches {
|
||||
request.ConfigListenContexts = append(request.ConfigListenContexts,
|
||||
model.ConfigListenContext{Group: cache.group, Md5: cache.md5, DataId: cache.dataId, Tenant: cache.tenant})
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
func (client *ConfigClient) refreshContentAndCheck(cacheData *cacheData, notify bool) {
|
||||
configQueryResponse, err := client.configProxy.queryConfig(cacheData.dataId, cacheData.group, cacheData.tenant,
|
||||
constant.DEFAULT_TIMEOUT_MILLS, notify, client)
|
||||
if err != nil {
|
||||
logger.Errorf("refresh content and check md5 fail ,dataId=%s,group=%s,tenant=%s ", cacheData.dataId,
|
||||
cacheData.group, cacheData.tenant)
|
||||
return
|
||||
}
|
||||
cacheData.content = configQueryResponse.Content
|
||||
cacheData.contentType = configQueryResponse.ContentType
|
||||
if notify {
|
||||
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,
|
||||
util.TruncateContent(cacheData.content), cacheData.contentType)
|
||||
}
|
||||
cacheData.md5 = util.Md5(cacheData.content)
|
||||
if cacheData.md5 != cacheData.cacheDataListener.lastMd5 {
|
||||
go cacheData.cacheDataListener.listener(cacheData.tenant, cacheData.group, cacheData.dataId, cacheData.content)
|
||||
cacheData.cacheDataListener.lastMd5 = cacheData.md5
|
||||
client.cacheMap.Set(util.GetConfigCacheKey(cacheData.dataId, cacheData.group, cacheData.tenant), cacheData)
|
||||
}
|
||||
}
|
||||
|
||||
func (client *ConfigClient) notifyListenConfig() {
|
||||
client.listenExecute <- struct{}{}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
package config_client
|
||||
|
||||
import (
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination ../../mock/mock_config_client_interface.go -package mock -source=./config_client_interface.go
|
||||
|
@ -63,7 +63,8 @@ type IConfigClient interface {
|
|||
// tenant ==>nacos.namespace optional
|
||||
// pageNo option,default is 1
|
||||
// pageSize option,default is 10
|
||||
SearchConfig(param vo.SearchConfigParam) (*model.ConfigPage, error)
|
||||
SearchConfig(param vo.SearchConfigParm) (*model.ConfigPage, error)
|
||||
|
||||
PublishAggr(param vo.ConfigParam) (published bool, err error)
|
||||
// CloseClient Close the GRPC client
|
||||
CloseClient()
|
||||
}
|
||||
|
|
|
@ -18,132 +18,97 @@ package config_client
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/cache"
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/mock"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
|
||||
"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/model"
|
||||
|
||||
"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/vo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var goVersion = runtime.Version()
|
||||
|
||||
var clientConfigTest = constant.ClientConfig{
|
||||
TimeoutMs: 10000,
|
||||
ListenInterval: 20000,
|
||||
BeatInterval: 10000,
|
||||
}
|
||||
|
||||
var clientConfigTestWithTenant = constant.ClientConfig{
|
||||
TimeoutMs: 10000,
|
||||
ListenInterval: 20000,
|
||||
BeatInterval: 10000,
|
||||
NamespaceId: "tenant",
|
||||
}
|
||||
|
||||
var serverConfigTest = constant.ServerConfig{
|
||||
ContextPath: "/nacos",
|
||||
Port: 80,
|
||||
IpAddr: "console.nacos.io",
|
||||
}
|
||||
|
||||
var serverConfigWithOptions = constant.NewServerConfig("console.nacos.io", 80, constant.WithContextPath("/nacos"))
|
||||
var serverConfigWithOptions = constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos"))
|
||||
|
||||
var clientConfigWithOptions = constant.NewClientConfig(
|
||||
constant.WithCustomLogger(mockLogger{}),
|
||||
constant.WithTimeoutMs(10*1000),
|
||||
constant.WithBeatInterval(2*1000),
|
||||
constant.WithNotLoadCacheAtStart(true),
|
||||
)
|
||||
|
||||
var (
|
||||
dataIdKey = goVersion + "dataId"
|
||||
groupKey = goVersion + "group:env"
|
||||
configNoChangeKey = goVersion + "ConfigNoChange"
|
||||
multipleClientsKey = goVersion + "MultipleClients"
|
||||
multipleClientsMultipleConfigsKey = goVersion + "MultipleClientsMultipleConfig"
|
||||
cancelOneKey = goVersion + "CancelOne"
|
||||
cancelOne1Key = goVersion + "CancelOne1"
|
||||
cancelListenConfigKey = goVersion + "cancel_listen_config"
|
||||
specialSymbolKey = goVersion + "special_symbol"
|
||||
)
|
||||
|
||||
var configParamMapTest = map[string]string{
|
||||
"dataId": dataIdKey,
|
||||
"group": groupKey,
|
||||
}
|
||||
|
||||
var configParamTest = vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
Group: groupKey,
|
||||
}
|
||||
|
||||
var localConfigTest = vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
Group: groupKey,
|
||||
DataId: "dataId",
|
||||
Group: "group",
|
||||
Content: "content",
|
||||
}
|
||||
|
||||
var localConfigMapTest = map[string]string{
|
||||
"dataId": dataIdKey,
|
||||
"group": groupKey,
|
||||
"content": "content",
|
||||
}
|
||||
var client *ConfigClient
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
func createConfigClientTest() *ConfigClient {
|
||||
nc := nacos_client.NacosClient{}
|
||||
nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
|
||||
nc.SetClientConfig(*clientConfigWithOptions)
|
||||
nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client, _ = NewConfigClient(&nc)
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func createConfigClientHttpTest(mockHttpAgent http_agent.IHttpAgent) *ConfigClient {
|
||||
nc := nacos_client.NacosClient{}
|
||||
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
nc.SetClientConfig(clientConfigTest)
|
||||
nc.SetHttpAgent(mockHttpAgent)
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
|
||||
_ = nc.SetClientConfig(*clientConfigWithOptions)
|
||||
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client, _ := NewConfigClient(&nc)
|
||||
client.configProxy = &MockConfigProxy{}
|
||||
return client
|
||||
}
|
||||
|
||||
type MockConfigProxy struct {
|
||||
}
|
||||
|
||||
func (m *MockConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
|
||||
cacheKey := util.GetConfigCacheKey(dataId, group, tenant)
|
||||
if IsLimited(cacheKey) {
|
||||
return nil, errors.New("request is limited")
|
||||
}
|
||||
return &rpc_response.ConfigQueryResponse{Content: "hello world"}, nil
|
||||
}
|
||||
func (m *MockConfigProxy) searchConfigProxy(param vo.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error) {
|
||||
return &model.ConfigPage{TotalCount: 1}, nil
|
||||
}
|
||||
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
|
||||
}
|
||||
func (m *MockConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
|
||||
return &rpc.RpcClient{}
|
||||
}
|
||||
func (m *MockConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
|
||||
return &rpc.RpcClient{}
|
||||
}
|
||||
|
||||
func Test_GetConfig(t *testing.T) {
|
||||
client := createConfigClientTest()
|
||||
success, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
Group: "group",
|
||||
Content: "hello world!222222"})
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: localConfigTest.Group,
|
||||
Content: "hello world"})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, success)
|
||||
|
||||
content, err := client.GetConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: "group"})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "hello world!222222", content)
|
||||
assert.Equal(t, "hello world", content)
|
||||
}
|
||||
|
||||
func Test_SearchConfig(t *testing.T) {
|
||||
client.PublishConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
Group: "groDEFAULT_GROUPup",
|
||||
Content: "hello world!222222"})
|
||||
configPage, err := client.SearchConfig(vo.SearchConfigParam{
|
||||
client := createConfigClientTest()
|
||||
_, _ = client.PublishConfig(vo.ConfigParam{
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: "DEFAULT_GROUP",
|
||||
Content: "hello world"})
|
||||
configPage, err := client.SearchConfig(vo.SearchConfigParm{
|
||||
Search: "accurate",
|
||||
DataId: dataIdKey,
|
||||
Group: "groDEFAULT_GROUPup",
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: "DEFAULT_GROUP",
|
||||
PageNo: 1,
|
||||
PageSize: 10,
|
||||
})
|
||||
|
@ -153,6 +118,7 @@ func Test_SearchConfig(t *testing.T) {
|
|||
|
||||
// PublishConfig
|
||||
func Test_PublishConfigWithoutDataId(t *testing.T) {
|
||||
client := createConfigClientTest()
|
||||
_, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: "",
|
||||
Group: "group",
|
||||
|
@ -161,18 +127,10 @@ func Test_PublishConfigWithoutDataId(t *testing.T) {
|
|||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func Test_PublishConfigWithoutGroup(t *testing.T) {
|
||||
_, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
Group: "",
|
||||
Content: "content",
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func Test_PublishConfigWithoutContent(t *testing.T) {
|
||||
client := createConfigClientTest()
|
||||
_, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: "group",
|
||||
Content: "",
|
||||
})
|
||||
|
@ -180,66 +138,25 @@ func Test_PublishConfigWithoutContent(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_PublishConfig(t *testing.T) {
|
||||
|
||||
client := createConfigClientTest()
|
||||
|
||||
success, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: "group",
|
||||
Content: "hello world2!"})
|
||||
Content: "hello world"})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, success)
|
||||
}
|
||||
|
||||
func Test_PublishConfigWithType(t *testing.T) {
|
||||
success, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
Group: "group",
|
||||
Content: "foo",
|
||||
Type: vo.YAML,
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, success)
|
||||
}
|
||||
|
||||
func Test_PublishConfigWithErrorResponse(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
mockHttpAgent := mock.NewMockIHttpAgent(controller)
|
||||
clientHttp := createConfigClientHttpTest(mockHttpAgent)
|
||||
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodPost),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/cs/configs"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(clientConfigTest.TimeoutMs),
|
||||
gomock.Eq(localConfigMapTest),
|
||||
).Times(3).Return(http_agent.FakeHttpResponse(401, "no security"), nil)
|
||||
success, err := clientHttp.PublishConfig(localConfigTest)
|
||||
assert.NotNil(t, err)
|
||||
assert.True(t, !success)
|
||||
}
|
||||
|
||||
func Test_PublishConfigWithErrorResponse_200(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
mockHttpAgent := mock.NewMockIHttpAgent(controller)
|
||||
clientHttp := createConfigClientHttpTest(mockHttpAgent)
|
||||
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodPost),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/cs/configs"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(clientConfigTest.TimeoutMs),
|
||||
gomock.Eq(localConfigMapTest),
|
||||
).Times(1).Return(http_agent.FakeHttpResponse(200, "false"), nil)
|
||||
success, err := clientHttp.PublishConfig(localConfigTest)
|
||||
assert.NotNil(t, err)
|
||||
assert.True(t, !success)
|
||||
}
|
||||
|
||||
// DeleteConfig
|
||||
|
||||
func Test_DeleteConfig(t *testing.T) {
|
||||
|
||||
client := createConfigClientTest()
|
||||
|
||||
success, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: "group",
|
||||
Content: "hello world!"})
|
||||
|
||||
|
@ -247,50 +164,15 @@ func Test_DeleteConfig(t *testing.T) {
|
|||
assert.True(t, success)
|
||||
|
||||
success, err = client.DeleteConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: "group"})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, success)
|
||||
}
|
||||
|
||||
func Test_DeleteConfigWithErrorResponse_200(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
mockHttpAgent := mock.NewMockIHttpAgent(controller)
|
||||
clientHttp := createConfigClientHttpTest(mockHttpAgent)
|
||||
|
||||
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodDelete),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/cs/configs"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(clientConfigTest.TimeoutMs),
|
||||
gomock.Eq(configParamMapTest),
|
||||
).Times(1).Return(http_agent.FakeHttpResponse(200, "false"), nil)
|
||||
success, err := clientHttp.DeleteConfig(configParamTest)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, false, success)
|
||||
}
|
||||
|
||||
func Test_DeleteConfigWithErrorResponse_401(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
mockHttpAgent := mock.NewMockIHttpAgent(controller)
|
||||
clientHttp := createConfigClientHttpTest(mockHttpAgent)
|
||||
|
||||
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodDelete),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/cs/configs"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(clientConfigTest.TimeoutMs),
|
||||
gomock.Eq(configParamMapTest),
|
||||
).Times(3).Return(http_agent.FakeHttpResponse(401, "no security"), nil)
|
||||
success, err := clientHttp.DeleteConfig(configParamTest)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, false, success)
|
||||
}
|
||||
|
||||
func Test_DeleteConfigWithoutDataId(t *testing.T) {
|
||||
client := createConfigClientTest()
|
||||
success, err := client.DeleteConfig(vo.ConfigParam{
|
||||
DataId: "",
|
||||
Group: "group",
|
||||
|
@ -299,206 +181,54 @@ func Test_DeleteConfigWithoutDataId(t *testing.T) {
|
|||
assert.Equal(t, false, success)
|
||||
}
|
||||
|
||||
func Test_DeleteConfigWithoutGroup(t *testing.T) {
|
||||
success, err := client.DeleteConfig(vo.ConfigParam{
|
||||
DataId: dataIdKey,
|
||||
Group: "",
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, false, success)
|
||||
}
|
||||
|
||||
// ListenConfig
|
||||
func TestListen(t *testing.T) {
|
||||
// ListenConfig
|
||||
t.Run("TestListenConfig", func(t *testing.T) {
|
||||
key := util.GetConfigCacheKey(localConfigTest.DataId, localConfigTest.Group, clientConfigTest.NamespaceId)
|
||||
cache.WriteConfigToFile(key, client.configCacheDir, "")
|
||||
var err error
|
||||
var success bool
|
||||
ch := make(chan string)
|
||||
err = client.ListenConfig(vo.ConfigParam{
|
||||
client := createConfigClientTest()
|
||||
err := client.ListenConfig(vo.ConfigParam{
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: localConfigTest.Group,
|
||||
OnChange: func(namespace, group, dataId, data string) {
|
||||
ch <- data
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
success, err = client.PublishConfig(vo.ConfigParam{
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: localConfigTest.Group,
|
||||
Content: localConfigTest.Content})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, success)
|
||||
select {
|
||||
case c := <-ch:
|
||||
assert.Equal(t, c, localConfigTest.Content)
|
||||
case <-time.After(10 * time.Second):
|
||||
assert.Errorf(t, errors.New("timeout"), "timeout")
|
||||
}
|
||||
})
|
||||
// ListenConfig no dataId
|
||||
t.Run("TestListenConfigNoDataId", func(t *testing.T) {
|
||||
listenConfigParam := vo.ConfigParam{
|
||||
Group: "gateway",
|
||||
Group: localConfigTest.Group,
|
||||
OnChange: func(namespace, group, dataId, data string) {
|
||||
},
|
||||
}
|
||||
client := createConfigClientTest()
|
||||
err := client.ListenConfig(listenConfigParam)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
// ListenConfig no change
|
||||
t.Run("TestListenConfigNoChange", func(t *testing.T) {
|
||||
key := util.GetConfigCacheKey(configNoChangeKey, localConfigTest.Group, clientConfigTest.NamespaceId)
|
||||
cache.WriteConfigToFile(key, client.configCacheDir, localConfigTest.Content)
|
||||
}
|
||||
|
||||
// CancelListenConfig
|
||||
func TestCancelListenConfig(t *testing.T) {
|
||||
//Multiple listeners listen for different configurations, cancel one
|
||||
t.Run("TestMultipleListenersCancelOne", func(t *testing.T) {
|
||||
client := createConfigClientTest()
|
||||
var err error
|
||||
var success bool
|
||||
var content string
|
||||
|
||||
err = client.ListenConfig(vo.ConfigParam{
|
||||
DataId: configNoChangeKey,
|
||||
Group: localConfigTest.Group,
|
||||
OnChange: func(namespace, group, dataId, data string) {
|
||||
content = "data"
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
success, err = client.PublishConfig(vo.ConfigParam{
|
||||
DataId: configNoChangeKey,
|
||||
Group: localConfigTest.Group,
|
||||
Content: localConfigTest.Content})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, success)
|
||||
assert.Equal(t, content, "")
|
||||
})
|
||||
// Multiple clients listen to the same configuration file
|
||||
t.Run("TestListenConfigWithMultipleClients", func(t *testing.T) {
|
||||
ch := make(chan string)
|
||||
listenConfigParam := vo.ConfigParam{
|
||||
DataId: multipleClientsKey,
|
||||
DataId: localConfigTest.DataId,
|
||||
Group: localConfigTest.Group,
|
||||
OnChange: func(namespace, group, dataId, data string) {
|
||||
ch <- data
|
||||
},
|
||||
}
|
||||
key := util.GetConfigCacheKey(listenConfigParam.DataId, listenConfigParam.Group, clientConfigTest.NamespaceId)
|
||||
cache.WriteConfigToFile(key, client.configCacheDir, "")
|
||||
client.ListenConfig(listenConfigParam)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
|
||||
nc.SetClientConfig(*clientConfigWithOptions)
|
||||
nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client1, _ := NewConfigClient(&nc)
|
||||
client1.ListenConfig(listenConfigParam)
|
||||
|
||||
success, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: multipleClientsKey,
|
||||
Group: localConfigTest.Group,
|
||||
Content: localConfigTest.Content})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, success)
|
||||
select {
|
||||
case c := <-ch:
|
||||
assert.Equal(t, localConfigTest.Content, c)
|
||||
case <-time.After(10 * time.Second):
|
||||
assert.Errorf(t, errors.New("timeout"), "timeout")
|
||||
}
|
||||
|
||||
})
|
||||
// Multiple clients listen to multiple configuration files
|
||||
t.Run("TestListenConfigWithMultipleClientsMultipleConfig", func(t *testing.T) {
|
||||
ch := make(chan string)
|
||||
listenConfigParam := vo.ConfigParam{
|
||||
DataId: multipleClientsMultipleConfigsKey,
|
||||
listenConfigParam1 := vo.ConfigParam{
|
||||
DataId: localConfigTest.DataId + "1",
|
||||
Group: localConfigTest.Group,
|
||||
OnChange: func(namespace, group, dataId, data string) {
|
||||
ch <- data
|
||||
},
|
||||
}
|
||||
key := util.GetConfigCacheKey(listenConfigParam.DataId, listenConfigParam.Group, clientConfigTest.NamespaceId)
|
||||
cache.WriteConfigToFile(key, client.configCacheDir, "")
|
||||
client.ListenConfig(listenConfigParam)
|
||||
_ = client.ListenConfig(listenConfigParam)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
|
||||
nc.SetClientConfig(*clientConfigWithOptions)
|
||||
nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client1, _ := NewConfigClient(&nc)
|
||||
client1.ListenConfig(listenConfigParam)
|
||||
|
||||
success, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: multipleClientsMultipleConfigsKey,
|
||||
Group: localConfigTest.Group,
|
||||
Content: localConfigTest.Content})
|
||||
_ = client.ListenConfig(listenConfigParam1)
|
||||
|
||||
err = client.CancelListenConfig(listenConfigParam)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, success)
|
||||
select {
|
||||
case c := <-ch:
|
||||
assert.Equal(t, localConfigTest.Content, c)
|
||||
case <-time.After(10 * time.Second):
|
||||
assert.Errorf(t, errors.New("timeout"), "timeout")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetConfigWithSpecialSymbol(t *testing.T) {
|
||||
contentStr := "hello world!!@#$%^&&*()"
|
||||
success, err := client.PublishConfig(vo.ConfigParam{
|
||||
DataId: specialSymbolKey,
|
||||
Group: localConfigTest.Group,
|
||||
Content: contentStr})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, success)
|
||||
|
||||
content, err := client.GetConfig(vo.ConfigParam{
|
||||
DataId: specialSymbolKey,
|
||||
Group: localConfigTest.Group})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, contentStr, content)
|
||||
}
|
||||
|
||||
type mockLogger struct {
|
||||
}
|
||||
|
||||
func (m mockLogger) Info(args ...interface{}) {
|
||||
logrus.Info(args...)
|
||||
}
|
||||
|
||||
func (m mockLogger) Warn(args ...interface{}) {
|
||||
logrus.Info(args...)
|
||||
}
|
||||
|
||||
func (m mockLogger) Error(args ...interface{}) {
|
||||
logrus.Info(args...)
|
||||
}
|
||||
|
||||
func (m mockLogger) Debug(args ...interface{}) {
|
||||
logrus.Info(args...)
|
||||
}
|
||||
|
||||
func (m mockLogger) Infof(format string, args ...interface{}) {
|
||||
logrus.Infof(format, args...)
|
||||
}
|
||||
|
||||
func (m mockLogger) Warnf(format string, args ...interface{}) {
|
||||
logrus.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (m mockLogger) Errorf(format string, args ...interface{}) {
|
||||
logrus.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func (m mockLogger) Debugf(format string, args ...interface{}) {
|
||||
logrus.Debugf("implement me")
|
||||
}
|
||||
|
|
|
@ -18,18 +18,26 @@ package config_client
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/nacos_server"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
|
||||
|
||||
"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/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/common/nacos_server"
|
||||
"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/vo"
|
||||
)
|
||||
|
||||
type ConfigProxy struct {
|
||||
|
@ -37,34 +45,37 @@ type ConfigProxy struct {
|
|||
clientConfig constant.ClientConfig
|
||||
}
|
||||
|
||||
func NewConfigProxy(serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent) (ConfigProxy, error) {
|
||||
func NewConfigProxy(serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent) (IConfigProxy, error) {
|
||||
proxy := ConfigProxy{}
|
||||
var err error
|
||||
proxy.nacosServer, err = nacos_server.NewNacosServer(serverConfig, clientConfig, httpAgent, clientConfig.TimeoutMs, clientConfig.Endpoint)
|
||||
proxy.clientConfig = clientConfig
|
||||
return proxy, err
|
||||
|
||||
return &proxy, err
|
||||
}
|
||||
|
||||
func (cp *ConfigProxy) GetServerList() []constant.ServerConfig {
|
||||
return cp.nacosServer.GetServerList()
|
||||
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.injectCommHeader(request.GetHeaders())
|
||||
cp.nacosServer.InjectSkAk(request.GetHeaders(), cp.clientConfig)
|
||||
signHeaders := nacos_server.GetSignHeadersFromRequest(request.(rpc_request.IConfigRequest), cp.clientConfig.SecretKey)
|
||||
request.PutAllHeaders(signHeaders)
|
||||
//todo Config Limiter
|
||||
response, err := rpcClient.Request(request, int64(timeoutMills))
|
||||
monitor.GetConfigRequestMonitor(constant.GRPC, request.GetRequestType(), rpc_response.GetGrpcResponseStatusCode(response)).Observe(float64(time.Now().Nanosecond() - start.Nanosecond()))
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (cp *ConfigProxy) GetConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (string, error) {
|
||||
params := util.TransformObject2Param(param)
|
||||
if len(tenant) > 0 {
|
||||
params["tenant"] = tenant
|
||||
func (cp *ConfigProxy) injectCommHeader(param map[string]string) {
|
||||
now := strconv.FormatInt(util.CurrentMillis(), 10)
|
||||
param[constant.CLIENT_APPNAME_HEADER] = cp.clientConfig.AppName
|
||||
param[constant.CLIENT_REQUEST_TS_HEADER] = now
|
||||
param[constant.CLIENT_REQUEST_TOKEN_HEADER] = util.Md5(now + cp.clientConfig.AppKey)
|
||||
param[constant.EX_CONFIG_INFO] = "true"
|
||||
param[constant.CHARSET_KEY] = "utf-8"
|
||||
}
|
||||
|
||||
var headers = map[string]string{}
|
||||
headers["accessKey"] = accessKey
|
||||
headers["secretKey"] = secretKey
|
||||
|
||||
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodGet, cp.clientConfig.TimeoutMs)
|
||||
return result, err
|
||||
}
|
||||
|
||||
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)
|
||||
if len(tenant) > 0 {
|
||||
params["tenant"] = tenant
|
||||
|
@ -89,98 +100,108 @@ func (cp *ConfigProxy) SearchConfigProxy(param vo.SearchConfigParam, tenant, acc
|
|||
}
|
||||
return &configPage, nil
|
||||
}
|
||||
func (cp *ConfigProxy) PublishConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) {
|
||||
params := util.TransformObject2Param(param)
|
||||
if len(tenant) > 0 {
|
||||
params["tenant"] = tenant
|
||||
}
|
||||
|
||||
var headers = map[string]string{}
|
||||
headers["accessKey"] = accessKey
|
||||
headers["secretKey"] = secretKey
|
||||
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs)
|
||||
func (cp *ConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
|
||||
if group == "" {
|
||||
group = constant.DEFAULT_GROUP
|
||||
}
|
||||
configQueryRequest := rpc_request.NewConfigQueryRequest(group, dataId, tenant)
|
||||
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)
|
||||
if err != nil {
|
||||
return false, errors.New("[client.PublishConfig] publish config failed:" + err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if strings.ToLower(strings.Trim(result, " ")) == "true" {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, errors.New("[client.PublishConfig] publish config failed:" + result)
|
||||
response, ok := iResponse.(*rpc_response.ConfigQueryResponse)
|
||||
if !ok {
|
||||
return nil, errors.New("ConfigQueryRequest returns type error")
|
||||
}
|
||||
if response.IsSuccess() {
|
||||
//todo LocalConfigInfoProcessor.saveSnapshot
|
||||
cache.WriteConfigToFile(cacheKey, cp.clientConfig.CacheDir, response.Content)
|
||||
//todo LocalConfigInfoProcessor.saveEncryptDataKeySnapshot
|
||||
if response.ContentType == "" {
|
||||
response.ContentType = "text"
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (cp *ConfigProxy) PublishAggProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) {
|
||||
params := util.TransformObject2Param(param)
|
||||
if len(tenant) > 0 {
|
||||
params["tenant"] = tenant
|
||||
}
|
||||
params["method"] = "addDatum"
|
||||
var headers = map[string]string{}
|
||||
headers["accessKey"] = accessKey
|
||||
headers["secretKey"] = secretKey
|
||||
_, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_AGG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs)
|
||||
if err != nil {
|
||||
return false, errors.New("[client.PublishAggProxy] publish agg failed:" + err.Error())
|
||||
}
|
||||
return true, nil
|
||||
if response.GetErrorCode() == 300 {
|
||||
//todo LocalConfigInfoProcessor.saveSnapshot
|
||||
cache.WriteConfigToFile(cacheKey, cp.clientConfig.CacheDir, "")
|
||||
//todo LocalConfigInfoProcessor.saveEncryptDataKeySnapshot
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (cp *ConfigProxy) DeleteAggProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) {
|
||||
params := util.TransformObject2Param(param)
|
||||
if len(tenant) > 0 {
|
||||
params["tenant"] = tenant
|
||||
}
|
||||
params["method"] = "deleteDatum"
|
||||
var headers = map[string]string{}
|
||||
headers["accessKey"] = accessKey
|
||||
headers["secretKey"] = secretKey
|
||||
_, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_AGG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs)
|
||||
if err != nil {
|
||||
return false, errors.New("[client.DeleteAggProxy] delete agg failed:" + err.Error())
|
||||
}
|
||||
return true, nil
|
||||
if response.GetErrorCode() == 400 {
|
||||
logger.Errorf(
|
||||
"[config_rpc_client] [sub-server-error] get server config being modified concurrently, dataId=%s, group=%s, "+
|
||||
"tenant=%s", dataId, group, tenant)
|
||||
return nil, errors.New("data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant)
|
||||
}
|
||||
|
||||
func (cp *ConfigProxy) DeleteConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) {
|
||||
params := util.TransformObject2Param(param)
|
||||
if len(tenant) > 0 {
|
||||
params["tenant"] = tenant
|
||||
}
|
||||
var headers = map[string]string{}
|
||||
headers["accessKey"] = accessKey
|
||||
headers["secretKey"] = secretKey
|
||||
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodDelete, cp.clientConfig.TimeoutMs)
|
||||
if err != nil {
|
||||
return false, errors.New("[client.DeleteConfig] deleted config failed:" + err.Error())
|
||||
}
|
||||
if strings.ToLower(strings.Trim(result, " ")) == "true" {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, errors.New("[client.DeleteConfig] deleted config failed: " + string(result))
|
||||
if response.GetErrorCode() > 0 {
|
||||
logger.Errorf("[config_rpc_client] [sub-server-error] dataId=%s, group=%s, tenant=%s, code=%+v", dataId, group,
|
||||
tenant, response)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (cp *ConfigProxy) ListenConfig(params map[string]string, isInitializing bool, tenant, accessKey, secretKey string) (string, error) {
|
||||
//fixed at 30000ms,avoid frequent request on the server
|
||||
var listenInterval uint64 = 30000
|
||||
headers := map[string]string{
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
||||
"Long-Pulling-Timeout": strconv.FormatUint(listenInterval, 10),
|
||||
}
|
||||
if isInitializing {
|
||||
headers["Long-Pulling-Timeout-No-Hangup"] = "true"
|
||||
func (cp *ConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
|
||||
labels := map[string]string{
|
||||
constant.LABEL_SOURCE: constant.LABEL_SOURCE_SDK,
|
||||
constant.LABEL_MODULE: constant.LABEL_MODULE_CONFIG,
|
||||
"taskId": taskId,
|
||||
}
|
||||
|
||||
headers["accessKey"] = accessKey
|
||||
headers["secretKey"] = secretKey
|
||||
iRpcClient, _ := rpc.CreateClient("config-"+taskId+"-"+client.uid, rpc.GRPC, labels, cp.nacosServer)
|
||||
rpcClient := iRpcClient.GetRpcClient()
|
||||
if rpcClient.IsInitialized() {
|
||||
rpcClient.RegisterServerRequestHandler(func() rpc_request.IRequest {
|
||||
// TODO fix the group/dataId empty problem
|
||||
return rpc_request.NewConfigChangeNotifyRequest("", "", "")
|
||||
}, &ConfigChangeNotifyRequestHandler{client: client})
|
||||
rpcClient.Tenant = cp.clientConfig.NamespaceId
|
||||
rpcClient.Start()
|
||||
}
|
||||
return rpcClient
|
||||
}
|
||||
|
||||
if len(tenant) > 0 {
|
||||
params["tenant"] = tenant
|
||||
func (cp *ConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
|
||||
return cp.createRpcClient("0", client)
|
||||
}
|
||||
logger.Infof("[client.ListenConfig] request params:%+v header:%+v \n", params, headers)
|
||||
// In order to prevent the server from handling the delay of the client's long task,
|
||||
// increase the client's read timeout to avoid this problem.
|
||||
timeout := listenInterval + listenInterval/10
|
||||
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_LISTEN_PATH, params, headers, http.MethodPost, timeout)
|
||||
return result, err
|
||||
|
||||
type ConfigChangeNotifyRequestHandler struct {
|
||||
client *ConfigClient
|
||||
}
|
||||
|
||||
func (c *ConfigChangeNotifyRequestHandler) Name() string {
|
||||
return "ConfigChangeNotifyRequestHandler"
|
||||
}
|
||||
|
||||
func (c *ConfigChangeNotifyRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *rpc.RpcClient) rpc_response.IResponse {
|
||||
configChangeNotifyRequest, ok := request.(*rpc_request.ConfigChangeNotifyRequest)
|
||||
if ok {
|
||||
logger.Infof("%s [server-push] config changed. dataId=%s, group=%s,tenant=%s", rpcClient.Name,
|
||||
configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group, configChangeNotifyRequest.Tenant)
|
||||
|
||||
cacheKey := util.GetConfigCacheKey(configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group,
|
||||
configChangeNotifyRequest.Tenant)
|
||||
data, ok := c.client.cacheMap.Get(cacheKey)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
cData := data.(*cacheData)
|
||||
cData.isSyncWithServer = false
|
||||
c.client.notifyListenConfig()
|
||||
return &rpc_response.NotifySubscriberResponse{
|
||||
Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package config_client
|
||||
|
||||
import (
|
||||
"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/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
type IConfigProxy interface {
|
||||
queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, 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)
|
||||
createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient
|
||||
getRpcClient(client *ConfigClient) *rpc.RpcClient
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,14 +17,15 @@
|
|||
package nacos_client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/file"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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/http_agent"
|
||||
)
|
||||
|
||||
type NacosClient struct {
|
||||
|
@ -60,21 +61,7 @@ func (client *NacosClient) SetClientConfig(config constant.ClientConfig) (err er
|
|||
if config.LogDir == "" {
|
||||
config.LogDir = file.GetCurrentPath() + string(os.PathSeparator) + "log"
|
||||
}
|
||||
|
||||
if config.LogSampling != nil {
|
||||
if config.LogSampling.Initial < 0 {
|
||||
config.LogSampling.Initial = 100
|
||||
}
|
||||
|
||||
if config.LogSampling.Thereafter < 0 {
|
||||
config.LogSampling.Thereafter = 100
|
||||
}
|
||||
|
||||
if config.LogSampling.Tick < 0 {
|
||||
config.LogSampling.Tick = 10 * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[INFO] logDir:<%s> cacheDir:<%s>", config.LogDir, config.CacheDir)
|
||||
client.clientConfig = config
|
||||
client.clientConfigValid = true
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
package nacos_client
|
||||
|
||||
import (
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination mock_nacos_client_interface.go -package nacos_client -source=./nacos_client_interface.go
|
||||
|
|
|
@ -1,228 +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_client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/cache"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
)
|
||||
|
||||
type HostReactor struct {
|
||||
serviceInfoMap cache.ConcurrentMap
|
||||
cacheDir string
|
||||
updateThreadNum int
|
||||
serviceProxy NamingProxy
|
||||
pushReceiver PushReceiver
|
||||
subCallback SubscribeCallback
|
||||
updateTimeMap cache.ConcurrentMap
|
||||
updateCacheWhenEmpty bool
|
||||
}
|
||||
|
||||
const Default_Update_Thread_Num = 20
|
||||
|
||||
func NewHostReactor(serviceProxy NamingProxy, cacheDir string, updateThreadNum int, notLoadCacheAtStart bool, subCallback SubscribeCallback, updateCacheWhenEmpty bool) HostReactor {
|
||||
if updateThreadNum <= 0 {
|
||||
updateThreadNum = Default_Update_Thread_Num
|
||||
}
|
||||
hr := HostReactor{
|
||||
serviceProxy: serviceProxy,
|
||||
cacheDir: cacheDir,
|
||||
updateThreadNum: updateThreadNum,
|
||||
serviceInfoMap: cache.NewConcurrentMap(),
|
||||
subCallback: subCallback,
|
||||
updateTimeMap: cache.NewConcurrentMap(),
|
||||
updateCacheWhenEmpty: updateCacheWhenEmpty,
|
||||
}
|
||||
pr := NewPushReceiver(&hr)
|
||||
hr.pushReceiver = *pr
|
||||
if !notLoadCacheAtStart {
|
||||
hr.loadCacheFromDisk()
|
||||
}
|
||||
go hr.asyncUpdateService()
|
||||
return hr
|
||||
}
|
||||
|
||||
func (hr *HostReactor) loadCacheFromDisk() {
|
||||
serviceMap := cache.ReadServicesFromFile(hr.cacheDir)
|
||||
if len(serviceMap) == 0 {
|
||||
return
|
||||
}
|
||||
for k, v := range serviceMap {
|
||||
hr.serviceInfoMap.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (hr *HostReactor) ProcessServiceJson(result string) {
|
||||
service := util.JsonToService(result)
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
cacheKey := util.GetServiceCacheKey(service.Name, service.Clusters)
|
||||
|
||||
oldDomain, ok := hr.serviceInfoMap.Get(cacheKey)
|
||||
if ok && !hr.updateCacheWhenEmpty {
|
||||
//if instance list is empty,not to update cache
|
||||
if service.Hosts == nil || len(service.Hosts) == 0 {
|
||||
logger.Errorf("do not have useful host, ignore it, name:%s", service.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
hr.updateTimeMap.Set(cacheKey, uint64(util.CurrentMillis()))
|
||||
hr.serviceInfoMap.Set(cacheKey, *service)
|
||||
oldService, serviceOk := oldDomain.(model.Service)
|
||||
if !ok || ok && serviceOk && isServiceInstanceChanged(&oldService, service) {
|
||||
if !ok {
|
||||
logger.Info("service not found in cache " + cacheKey)
|
||||
} else {
|
||||
logger.Info("service key:%s was updated to:%s", cacheKey, util.ToJsonString(service))
|
||||
}
|
||||
cache.WriteServicesToFile(*service, hr.cacheDir)
|
||||
hr.subCallback.ServiceChanged(service)
|
||||
}
|
||||
}
|
||||
|
||||
func (hr *HostReactor) GetServiceInfo(serviceName string, clusters string) (model.Service, error) {
|
||||
key := util.GetServiceCacheKey(serviceName, clusters)
|
||||
cacheService, ok := hr.serviceInfoMap.Get(key)
|
||||
if !ok {
|
||||
hr.updateServiceNow(serviceName, clusters)
|
||||
if cacheService, ok = hr.serviceInfoMap.Get(key); !ok {
|
||||
return model.Service{}, errors.New("get service info failed")
|
||||
}
|
||||
}
|
||||
|
||||
return cacheService.(model.Service), nil
|
||||
}
|
||||
|
||||
func (hr *HostReactor) GetAllServiceInfo(nameSpace, groupName string, pageNo, pageSize uint32) model.ServiceList {
|
||||
data := model.ServiceList{}
|
||||
result, err := hr.serviceProxy.GetAllServiceInfoList(nameSpace, groupName, pageNo, pageSize)
|
||||
if err != nil {
|
||||
logger.Errorf("GetAllServiceInfoList return error!nameSpace:%s groupName:%s pageNo:%d, pageSize:%d err:%+v",
|
||||
nameSpace, groupName, pageNo, pageSize, err)
|
||||
return data
|
||||
}
|
||||
if result == "" {
|
||||
logger.Errorf("GetAllServiceInfoList result is empty!nameSpace:%s groupName:%s pageNo:%d, pageSize:%d",
|
||||
nameSpace, groupName, pageNo, pageSize)
|
||||
return data
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(result), &data)
|
||||
if err != nil {
|
||||
logger.Errorf("GetAllServiceInfoList result json.Unmarshal error!nameSpace:%s groupName:%s pageNo:%d, pageSize:%d",
|
||||
nameSpace, groupName, pageNo, pageSize)
|
||||
return data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (hr *HostReactor) updateServiceNow(serviceName, clusters string) {
|
||||
result, err := hr.serviceProxy.QueryList(serviceName, clusters, hr.pushReceiver.port, false)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("QueryList return error!serviceName:%s cluster:%s err:%+v", serviceName, clusters, err)
|
||||
return
|
||||
}
|
||||
if result == "" {
|
||||
logger.Errorf("QueryList result is empty!serviceName:%s cluster:%s", serviceName, clusters)
|
||||
return
|
||||
}
|
||||
hr.ProcessServiceJson(result)
|
||||
}
|
||||
|
||||
func (hr *HostReactor) asyncUpdateService() {
|
||||
sema := util.NewSemaphore(hr.updateThreadNum)
|
||||
for {
|
||||
for _, v := range hr.serviceInfoMap.Items() {
|
||||
service := v.(model.Service)
|
||||
lastRefTime, ok := hr.updateTimeMap.Get(util.GetServiceCacheKey(service.Name, service.Clusters))
|
||||
if !ok {
|
||||
lastRefTime = uint64(0)
|
||||
}
|
||||
if uint64(util.CurrentMillis())-lastRefTime.(uint64) > service.CacheMillis {
|
||||
sema.Acquire()
|
||||
go func() {
|
||||
hr.updateServiceNow(service.Name, service.Clusters)
|
||||
sema.Release()
|
||||
}()
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.Warn(fmt.Sprintf("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))
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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/monitor"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
type ServiceInfoHolder struct {
|
||||
ServiceInfoMap cache.ConcurrentMap
|
||||
updateCacheWhenEmpty bool
|
||||
cacheDir string
|
||||
notLoadCacheAtStart bool
|
||||
subCallback *SubscribeCallback
|
||||
UpdateTimeMap cache.ConcurrentMap
|
||||
}
|
||||
|
||||
func NewServiceInfoHolder(namespace, cacheDir string, updateCacheWhenEmpty, notLoadCacheAtStart bool) *ServiceInfoHolder {
|
||||
cacheDir = cacheDir + string(os.PathSeparator) + "naming" + string(os.PathSeparator) + namespace
|
||||
serviceInfoHolder := &ServiceInfoHolder{
|
||||
updateCacheWhenEmpty: updateCacheWhenEmpty,
|
||||
notLoadCacheAtStart: notLoadCacheAtStart,
|
||||
cacheDir: cacheDir,
|
||||
subCallback: NewSubscribeCallback(),
|
||||
UpdateTimeMap: cache.NewConcurrentMap(),
|
||||
ServiceInfoMap: cache.NewConcurrentMap(),
|
||||
}
|
||||
|
||||
if !notLoadCacheAtStart {
|
||||
serviceInfoHolder.loadCacheFromDisk()
|
||||
}
|
||||
return serviceInfoHolder
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) loadCacheFromDisk() {
|
||||
serviceMap := cache.ReadServicesFromFile(s.cacheDir)
|
||||
if serviceMap == nil || len(serviceMap) == 0 {
|
||||
return
|
||||
}
|
||||
for k, v := range serviceMap {
|
||||
s.ServiceInfoMap.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) ProcessServiceJson(data string) {
|
||||
s.ProcessService(util.JsonToService(data))
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) ProcessService(service *model.Service) {
|
||||
if service == nil {
|
||||
return
|
||||
}
|
||||
if !s.updateCacheWhenEmpty {
|
||||
//if instance list is empty,not to update cache
|
||||
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)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
|
||||
oldDomain, ok := s.ServiceInfoMap.Get(cacheKey)
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
s.UpdateTimeMap.Set(cacheKey, uint64(util.CurrentMillis()))
|
||||
s.ServiceInfoMap.Set(cacheKey, *service)
|
||||
if !ok || checkInstanceChanged(oldDomain, *service) {
|
||||
logger.Infof("service key:%s was updated to:%s", cacheKey, util.ToJsonString(service))
|
||||
cache.WriteServicesToFile(service, cacheKey, s.cacheDir)
|
||||
s.subCallback.ServiceChanged(cacheKey, service)
|
||||
}
|
||||
monitor.GetServiceInfoMapSizeMonitor().Set(float64(s.ServiceInfoMap.Count()))
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) GetServiceInfo(serviceName, groupName, clusters string) (model.Service, bool) {
|
||||
cacheKey := util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters)
|
||||
//todo FailoverReactor
|
||||
service, ok := s.ServiceInfoMap.Get(cacheKey)
|
||||
if ok {
|
||||
return service.(model.Service), ok
|
||||
}
|
||||
return model.Service{}, ok
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) RegisterCallback(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
|
||||
s.subCallback.AddCallbackFunc(serviceName, clusters, callbackFunc)
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) DeregisterCallback(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
|
||||
s.subCallback.RemoveCallbackFunc(serviceName, clusters, callbackFunc)
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) StopUpdateIfContain(serviceName, clusters string) {
|
||||
cacheKey := util.GetServiceCacheKey(serviceName, clusters)
|
||||
s.ServiceInfoMap.Remove(cacheKey)
|
||||
}
|
||||
|
||||
func (s *ServiceInfoHolder) IsSubscribed(serviceName, clusters string) bool {
|
||||
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.Warn(fmt.Sprintf("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))
|
||||
}
|
|
@ -13,8 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package naming_client
|
||||
package naming_cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -22,69 +21,16 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"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 TestHostReactor_GetServiceInfo(t *testing.T) {
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client, _ := NewNamingClient(&nc)
|
||||
param := vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.11",
|
||||
Port: 8848,
|
||||
ServiceName: "test",
|
||||
Weight: 10,
|
||||
ClusterName: "test",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
}
|
||||
if param.GroupName == "" {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
param.ServiceName = util.GetGroupName(param.ServiceName, param.GroupName)
|
||||
_, _ = client.RegisterInstance(param)
|
||||
_, err := client.hostReactor.GetServiceInfo(param.ServiceName, param.ClusterName)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestHostReactor_GetServiceInfoErr(t *testing.T) {
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client, _ := NewNamingClient(&nc)
|
||||
param := vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.11",
|
||||
Port: 8848,
|
||||
ServiceName: "test",
|
||||
Weight: 10,
|
||||
ClusterName: "test",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
}
|
||||
_, _ = client.RegisterInstance(param)
|
||||
_, err := client.hostReactor.GetServiceInfo(param.ServiceName, param.ClusterName)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestHostReactor_isServiceInstanceChanged(t *testing.T) {
|
||||
func TestServiceInfoHolder_isServiceInstanceChanged(t *testing.T) {
|
||||
rand.Seed(time.Now().Unix())
|
||||
defaultIp := createRandomIp()
|
||||
defaultPort := creatRandomPort()
|
||||
serviceA := &model.Service{
|
||||
serviceA := model.Service{
|
||||
LastRefTime: 1000,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
|
@ -101,7 +47,7 @@ func TestHostReactor_isServiceInstanceChanged(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
serviceB := &model.Service{
|
||||
serviceB := model.Service{
|
||||
LastRefTime: 1001,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
|
@ -119,7 +65,7 @@ func TestHostReactor_isServiceInstanceChanged(t *testing.T) {
|
|||
},
|
||||
}
|
||||
ip := createRandomIp()
|
||||
serviceC := &model.Service{
|
||||
serviceC := model.Service{
|
||||
LastRefTime: 1001,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
|
@ -155,7 +101,7 @@ func TestHostReactor_isServiceInstanceChanged(t *testing.T) {
|
|||
|
||||
func TestHostReactor_isServiceInstanceChangedWithUnOrdered(t *testing.T) {
|
||||
rand.Seed(time.Now().Unix())
|
||||
serviceA := &model.Service{
|
||||
serviceA := model.Service{
|
||||
LastRefTime: 1001,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
|
@ -173,7 +119,7 @@ func TestHostReactor_isServiceInstanceChangedWithUnOrdered(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
serviceB := &model.Service{
|
||||
serviceB := model.Service{
|
||||
LastRefTime: 1001,
|
||||
Hosts: []model.Instance{
|
||||
{
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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 (
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
type SubscribeCallback struct {
|
||||
callbackFuncMap cache.ConcurrentMap
|
||||
mux *sync.Mutex
|
||||
}
|
||||
|
||||
func NewSubscribeCallback() *SubscribeCallback {
|
||||
return &SubscribeCallback{callbackFuncMap: cache.NewConcurrentMap(), mux: new(sync.Mutex)}
|
||||
}
|
||||
|
||||
func (ed *SubscribeCallback) IsSubscribed(serviceName, clusters string) bool {
|
||||
key := util.GetServiceCacheKey(serviceName, clusters)
|
||||
_, ok := ed.callbackFuncMap.Get(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (ed *SubscribeCallback) AddCallbackFunc(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
|
||||
key := util.GetServiceCacheKey(serviceName, clusters)
|
||||
defer ed.mux.Unlock()
|
||||
ed.mux.Lock()
|
||||
var funcSlice []*func(services []model.Instance, err error)
|
||||
old, ok := ed.callbackFuncMap.Get(key)
|
||||
if ok {
|
||||
funcSlice = append(funcSlice, old.([]*func(services []model.Instance, err error))...)
|
||||
}
|
||||
funcSlice = append(funcSlice, callbackFunc)
|
||||
ed.callbackFuncMap.Set(key, funcSlice)
|
||||
}
|
||||
|
||||
func (ed *SubscribeCallback) RemoveCallbackFunc(serviceName string, clusters string, callbackFunc *func(services []model.Instance, err error)) {
|
||||
logger.Info("removing " + serviceName + " with " + clusters + " to listener map")
|
||||
key := util.GetServiceCacheKey(serviceName, clusters)
|
||||
funcs, ok := ed.callbackFuncMap.Get(key)
|
||||
if ok && funcs != nil {
|
||||
var newFuncs []*func(services []model.Instance, err error)
|
||||
for _, funcItem := range funcs.([]*func(services []model.Instance, err error)) {
|
||||
if funcItem != callbackFunc {
|
||||
newFuncs = append(newFuncs, funcItem)
|
||||
}
|
||||
}
|
||||
ed.callbackFuncMap.Set(key, newFuncs)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (ed *SubscribeCallback) ServiceChanged(cacheKey string, service *model.Service) {
|
||||
funcs, ok := ed.callbackFuncMap.Get(cacheKey)
|
||||
if ok {
|
||||
for _, funcItem := range funcs.([]*func(services []model.Instance, err error)) {
|
||||
if len(service.Hosts) == 0 {
|
||||
(*funcItem)(service.Hosts, errors.New("[client.Subscribe] subscribe failed,hosts is empty"))
|
||||
continue
|
||||
}
|
||||
(*funcItem)(service.Hosts, nil)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,23 +14,23 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package naming_client
|
||||
package naming_cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"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/vo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
|
||||
service := model.Service{
|
||||
Dom: "public@@Test",
|
||||
Clusters: strings.Join([]string{"default"}, ","),
|
||||
CacheMillis: 10000,
|
||||
Checksum: "abcd",
|
||||
|
@ -38,7 +38,6 @@ func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
|
|||
}
|
||||
var hosts []model.Instance
|
||||
host := model.Instance{
|
||||
Valid: true,
|
||||
Enable: true,
|
||||
InstanceId: "123",
|
||||
Port: 8080,
|
||||
|
@ -55,14 +54,15 @@ func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
|
|||
ServiceName: "Test",
|
||||
Clusters: []string{"default"},
|
||||
GroupName: "public",
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
fmt.Println(util.ToJsonString(ed.callbackFuncMap))
|
||||
},
|
||||
}
|
||||
ed.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
|
||||
for k, v := range ed.callbackFuncsMap.Items() {
|
||||
for k, v := range ed.callbackFuncMap.Items() {
|
||||
assert.Equal(t, key, k, "key should be equal!")
|
||||
funcs := v.([]*func(services []model.SubscribeService, err error))
|
||||
funcs := v.([]*func(services []model.Instance, err error))
|
||||
assert.Equal(t, len(funcs), 1)
|
||||
assert.Equal(t, funcs[0], ¶m.SubscribeCallback, "callback function must be equal!")
|
||||
|
||||
|
@ -71,7 +71,6 @@ func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
|
|||
|
||||
func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
|
||||
service := model.Service{
|
||||
Dom: "public@@Test",
|
||||
Clusters: strings.Join([]string{"default"}, ","),
|
||||
CacheMillis: 10000,
|
||||
Checksum: "abcd",
|
||||
|
@ -79,7 +78,6 @@ func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
|
|||
}
|
||||
var hosts []model.Instance
|
||||
host := model.Instance{
|
||||
Valid: true,
|
||||
Enable: true,
|
||||
InstanceId: "123",
|
||||
Port: 8080,
|
||||
|
@ -96,32 +94,34 @@ func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
|
|||
ServiceName: "Test",
|
||||
Clusters: []string{"default"},
|
||||
GroupName: "public",
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
fmt.Printf("func1:%s \n", util.ToJsonString(services))
|
||||
},
|
||||
}
|
||||
ed.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
assert.Equal(t, len(ed.callbackFuncsMap.Items()), 1, "callback funcs map length should be 1")
|
||||
ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 1")
|
||||
|
||||
param2 := vo.SubscribeParam{
|
||||
ServiceName: "Test",
|
||||
Clusters: []string{"default"},
|
||||
GroupName: "public",
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
fmt.Printf("func2:%s \n", util.ToJsonString(services))
|
||||
},
|
||||
}
|
||||
ed.AddCallbackFuncs(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), ¶m2.SubscribeCallback)
|
||||
assert.Equal(t, len(ed.callbackFuncsMap.Items()), 1, "callback funcs map length should be 2")
|
||||
ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), ¶m2.SubscribeCallback)
|
||||
assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 2")
|
||||
|
||||
for k, v := range ed.callbackFuncsMap.Items() {
|
||||
log.Printf("key:%s,%d", k, len(v.([]*func(services []model.SubscribeService, err error))))
|
||||
for k, v := range ed.callbackFuncMap.Items() {
|
||||
log.Printf("key:%s,%d", k, len(v.([]*func(services []model.Instance, err error))))
|
||||
}
|
||||
|
||||
ed.RemoveCallbackFuncs(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), ¶m2.SubscribeCallback)
|
||||
ed.RemoveCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), ¶m2.SubscribeCallback)
|
||||
|
||||
key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
|
||||
for k, v := range ed.callbackFuncsMap.Items() {
|
||||
for k, v := range ed.callbackFuncMap.Items() {
|
||||
assert.Equal(t, key, k, "key should be equal!")
|
||||
funcs := v.([]*func(services []model.SubscribeService, err error))
|
||||
funcs := v.([]*func(services []model.Instance, err error))
|
||||
assert.Equal(t, len(funcs), 1)
|
||||
assert.Equal(t, funcs[0], ¶m.SubscribeCallback, "callback function must be equal!")
|
||||
|
||||
|
@ -138,7 +138,6 @@ func TestSubscribeCallback_ServiceChanged(t *testing.T) {
|
|||
}
|
||||
var hosts []model.Instance
|
||||
host := model.Instance{
|
||||
Valid: true,
|
||||
Enable: true,
|
||||
InstanceId: "123",
|
||||
Port: 8080,
|
||||
|
@ -155,22 +154,22 @@ func TestSubscribeCallback_ServiceChanged(t *testing.T) {
|
|||
ServiceName: "Test",
|
||||
Clusters: []string{"default"},
|
||||
GroupName: "public",
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
log.Printf("func1:%s \n", util.ToJsonString(services))
|
||||
},
|
||||
}
|
||||
ed.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
|
||||
param2 := vo.SubscribeParam{
|
||||
ServiceName: "Test",
|
||||
Clusters: []string{"default"},
|
||||
GroupName: "public",
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
log.Printf("func2:%s \n", util.ToJsonString(services))
|
||||
|
||||
},
|
||||
}
|
||||
ed.AddCallbackFuncs(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), ¶m2.SubscribeCallback)
|
||||
|
||||
ed.ServiceChanged(&service)
|
||||
ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), ¶m2.SubscribeCallback)
|
||||
cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
|
||||
ed.ServiceChanged(cacheKey, &service)
|
||||
}
|
|
@ -19,81 +19,71 @@ package naming_client
|
|||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/cache"
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
|
||||
"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_proxy"
|
||||
"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/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
// NamingClient ...
|
||||
type NamingClient struct {
|
||||
nacos_client.INacosClient
|
||||
hostReactor HostReactor
|
||||
serviceProxy NamingProxy
|
||||
subCallback SubscribeCallback
|
||||
beatReactor BeatReactor
|
||||
indexMap cache.ConcurrentMap
|
||||
NamespaceId string
|
||||
serviceProxy naming_proxy.INamingProxy
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder
|
||||
}
|
||||
|
||||
type Chooser struct {
|
||||
data []model.Instance
|
||||
totals []int
|
||||
max int
|
||||
}
|
||||
|
||||
func NewNamingClient(nc nacos_client.INacosClient) (NamingClient, error) {
|
||||
// NewNamingClient ...
|
||||
func NewNamingClient(nc nacos_client.INacosClient) (*NamingClient, error) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
naming := NamingClient{INacosClient: nc}
|
||||
naming := &NamingClient{INacosClient: nc}
|
||||
clientConfig, err := nc.GetClientConfig()
|
||||
if err != nil {
|
||||
return naming, err
|
||||
}
|
||||
naming.NamespaceId = clientConfig.NamespaceId
|
||||
|
||||
serverConfig, err := nc.GetServerConfig()
|
||||
if err != nil {
|
||||
return naming, err
|
||||
}
|
||||
|
||||
httpAgent, err := nc.GetHttpAgent()
|
||||
if err != nil {
|
||||
return naming, err
|
||||
}
|
||||
loggerConfig := logger.Config{
|
||||
LogFileName: constant.LOG_FILE_NAME,
|
||||
Level: clientConfig.LogLevel,
|
||||
Sampling: clientConfig.LogSampling,
|
||||
LogRollingConfig: clientConfig.LogRollingConfig,
|
||||
LogDir: clientConfig.LogDir,
|
||||
CustomLogger: clientConfig.CustomLogger,
|
||||
LogStdout: clientConfig.LogStdout,
|
||||
|
||||
if err = initLogger(clientConfig); err != nil {
|
||||
return naming, err
|
||||
}
|
||||
err = logger.InitLogger(loggerConfig)
|
||||
if clientConfig.NamespaceId == "" {
|
||||
clientConfig.NamespaceId = constant.DEFAULT_NAMESPACE_ID
|
||||
}
|
||||
naming.serviceInfoHolder = naming_cache.NewServiceInfoHolder(clientConfig.NamespaceId, clientConfig.CacheDir,
|
||||
clientConfig.UpdateCacheWhenEmpty, clientConfig.NotLoadCacheAtStart)
|
||||
|
||||
naming.serviceProxy, err = NewNamingProxyDelegate(clientConfig, serverConfig, httpAgent, naming.serviceInfoHolder)
|
||||
|
||||
go NewServiceInfoUpdater(naming.serviceInfoHolder, clientConfig.UpdateThreadNum, naming.serviceProxy).asyncUpdateService()
|
||||
if err != nil {
|
||||
return naming, err
|
||||
}
|
||||
logger.GetLogger().Infof("logDir:<%s> cacheDir:<%s>", clientConfig.LogDir, clientConfig.CacheDir)
|
||||
naming.subCallback = NewSubscribeCallback()
|
||||
naming.serviceProxy, err = NewNamingProxy(clientConfig, serverConfig, httpAgent)
|
||||
if err != nil {
|
||||
return naming, err
|
||||
}
|
||||
naming.hostReactor = NewHostReactor(naming.serviceProxy, clientConfig.CacheDir+string(os.PathSeparator)+"naming",
|
||||
clientConfig.UpdateThreadNum, clientConfig.NotLoadCacheAtStart, naming.subCallback, clientConfig.UpdateCacheWhenEmpty)
|
||||
naming.beatReactor = NewBeatReactor(naming.serviceProxy, clientConfig.BeatInterval)
|
||||
naming.indexMap = cache.NewConcurrentMap()
|
||||
|
||||
return naming, nil
|
||||
}
|
||||
|
||||
//RegisterInstance register instance
|
||||
func initLogger(clientConfig constant.ClientConfig) error {
|
||||
return logger.InitLogger(logger.BuildLoggerConfig(clientConfig))
|
||||
}
|
||||
|
||||
// RegisterInstance ...
|
||||
func (sc *NamingClient) RegisterInstance(param vo.RegisterInstanceParam) (bool, error) {
|
||||
if param.ServiceName == "" {
|
||||
return false, errors.New("serviceName cannot be empty!")
|
||||
|
@ -114,127 +104,122 @@ func (sc *NamingClient) RegisterInstance(param vo.RegisterInstanceParam) (bool,
|
|||
Weight: param.Weight,
|
||||
Ephemeral: param.Ephemeral,
|
||||
}
|
||||
beatInfo := &model.BeatInfo{
|
||||
Ip: param.Ip,
|
||||
Port: param.Port,
|
||||
Metadata: param.Metadata,
|
||||
ServiceName: util.GetGroupName(param.ServiceName, param.GroupName),
|
||||
Cluster: param.ClusterName,
|
||||
Weight: param.Weight,
|
||||
Period: util.GetDurationWithDefault(param.Metadata, constant.HEART_BEAT_INTERVAL, time.Second*5),
|
||||
State: model.StateRunning,
|
||||
}
|
||||
_, err := sc.serviceProxy.RegisterInstance(util.GetGroupName(param.ServiceName, param.GroupName), param.GroupName, instance)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if instance.Ephemeral {
|
||||
sc.beatReactor.AddBeatInfo(util.GetGroupName(param.ServiceName, param.GroupName), beatInfo)
|
||||
}
|
||||
return true, nil
|
||||
|
||||
return sc.serviceProxy.RegisterInstance(param.ServiceName, param.GroupName, instance)
|
||||
|
||||
}
|
||||
|
||||
//DeregisterInstance deregister instance
|
||||
// DeregisterInstance ...
|
||||
func (sc *NamingClient) DeregisterInstance(param vo.DeregisterInstanceParam) (bool, error) {
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
sc.beatReactor.RemoveBeatInfo(util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port)
|
||||
|
||||
_, err := sc.serviceProxy.DeregisterInstance(util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port, param.Cluster, param.Ephemeral)
|
||||
if err != nil {
|
||||
return false, err
|
||||
instance := model.Instance{
|
||||
Ip: param.Ip,
|
||||
Port: param.Port,
|
||||
ClusterName: param.Cluster,
|
||||
Ephemeral: param.Ephemeral,
|
||||
}
|
||||
return true, nil
|
||||
return sc.serviceProxy.DeregisterInstance(param.ServiceName, param.GroupName, instance)
|
||||
}
|
||||
|
||||
//UpdateInstance update information for exist instance.
|
||||
// UpdateInstance ...
|
||||
func (sc *NamingClient) UpdateInstance(param vo.UpdateInstanceParam) (bool, error) {
|
||||
if param.ServiceName == "" {
|
||||
return false, errors.New("serviceName cannot be empty!")
|
||||
}
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
|
||||
if param.Ephemeral {
|
||||
// Update the heartbeat information first to prevent the information
|
||||
// from being flushed back to the original information after reconnecting
|
||||
sc.beatReactor.RemoveBeatInfo(util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port)
|
||||
beatInfo := &model.BeatInfo{
|
||||
if param.Metadata == nil {
|
||||
param.Metadata = make(map[string]string)
|
||||
}
|
||||
instance := model.Instance{
|
||||
Ip: param.Ip,
|
||||
Port: param.Port,
|
||||
Metadata: param.Metadata,
|
||||
ServiceName: util.GetGroupName(param.ServiceName, param.GroupName),
|
||||
Cluster: param.ClusterName,
|
||||
ClusterName: param.ClusterName,
|
||||
Healthy: param.Healthy,
|
||||
Enable: param.Enable,
|
||||
Weight: param.Weight,
|
||||
Period: util.GetDurationWithDefault(param.Metadata, constant.HEART_BEAT_INTERVAL, time.Second*5),
|
||||
State: model.StateRunning,
|
||||
}
|
||||
sc.beatReactor.AddBeatInfo(util.GetGroupName(param.ServiceName, param.GroupName), beatInfo)
|
||||
Ephemeral: param.Ephemeral,
|
||||
}
|
||||
|
||||
// Do update instance
|
||||
_, err := sc.serviceProxy.UpdateInstance(
|
||||
util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port, param.ClusterName, param.Ephemeral,
|
||||
param.Weight, param.Enable, param.Metadata)
|
||||
return sc.serviceProxy.RegisterInstance(param.ServiceName, param.GroupName, instance)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
//GetService get service info
|
||||
func (sc *NamingClient) GetService(param vo.GetServiceParam) (model.Service, error) {
|
||||
// GetService Get service info by Group and DataId, clusters was optional
|
||||
func (sc *NamingClient) GetService(param vo.GetServiceParam) (service model.Service, err error) {
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
service, err := sc.hostReactor.GetServiceInfo(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
|
||||
var ok bool
|
||||
clusters := strings.Join(param.Clusters, ",")
|
||||
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
|
||||
if !ok {
|
||||
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
|
||||
}
|
||||
return service, err
|
||||
}
|
||||
|
||||
//GetAllServicesInfo get all services info
|
||||
// GetAllServicesInfo Get all instance by Namespace and Group with page
|
||||
func (sc *NamingClient) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (model.ServiceList, error) {
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
clientConfig, _ := sc.GetClientConfig()
|
||||
if len(param.NameSpace) == 0 {
|
||||
if len(sc.NamespaceId) == 0 {
|
||||
if len(clientConfig.NamespaceId) == 0 {
|
||||
param.NameSpace = constant.DEFAULT_NAMESPACE_ID
|
||||
} else {
|
||||
param.NameSpace = sc.NamespaceId
|
||||
param.NameSpace = clientConfig.NamespaceId
|
||||
}
|
||||
}
|
||||
if param.PageNo == 0 {
|
||||
param.PageNo = 1
|
||||
}
|
||||
if param.PageSize == 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
services := sc.hostReactor.GetAllServiceInfo(param.NameSpace, param.GroupName, param.PageNo, param.PageSize)
|
||||
return services, nil
|
||||
services, err := sc.serviceProxy.GetServiceList(param.PageNo, param.PageSize, param.GroupName, &model.ExpressionSelector{})
|
||||
return services, err
|
||||
}
|
||||
|
||||
//SelectAllInstances select all instances
|
||||
// SelectAllInstances Get all instance by DataId 和 Group
|
||||
func (sc *NamingClient) SelectAllInstances(param vo.SelectAllInstancesParam) ([]model.Instance, error) {
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
service, err := sc.hostReactor.GetServiceInfo(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
|
||||
clusters := strings.Join(param.Clusters, ",")
|
||||
var (
|
||||
service model.Service
|
||||
ok bool
|
||||
err error
|
||||
)
|
||||
|
||||
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
|
||||
if !ok {
|
||||
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
|
||||
}
|
||||
if err != nil || service.Hosts == nil || len(service.Hosts) == 0 {
|
||||
return []model.Instance{}, err
|
||||
}
|
||||
return service.Hosts, err
|
||||
}
|
||||
|
||||
//SelectInstances select instances
|
||||
// SelectInstances Get all instance by DataId, Group and Health
|
||||
func (sc *NamingClient) SelectInstances(param vo.SelectInstancesParam) ([]model.Instance, error) {
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
service, err := sc.hostReactor.GetServiceInfo(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
|
||||
var (
|
||||
service model.Service
|
||||
ok bool
|
||||
err error
|
||||
)
|
||||
clusters := strings.Join(param.Clusters, ",")
|
||||
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
|
||||
if !ok {
|
||||
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return sc.selectInstances(service, param.HealthyOnly)
|
||||
}
|
||||
|
||||
|
@ -252,15 +237,25 @@ func (sc *NamingClient) selectInstances(service model.Service, healthy bool) ([]
|
|||
return result, nil
|
||||
}
|
||||
|
||||
//SelectOneHealthyInstance select one healthy instance
|
||||
// SelectOneHealthyInstance Get one healthy instance by DataId and Group
|
||||
func (sc *NamingClient) SelectOneHealthyInstance(param vo.SelectOneHealthInstanceParam) (*model.Instance, error) {
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
service, err := sc.hostReactor.GetServiceInfo(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
|
||||
var (
|
||||
service model.Service
|
||||
ok bool
|
||||
err error
|
||||
)
|
||||
clusters := strings.Join(param.Clusters, ",")
|
||||
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
|
||||
if !ok {
|
||||
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return sc.selectOneHealthyInstances(service)
|
||||
}
|
||||
|
||||
|
@ -284,67 +279,34 @@ func (sc *NamingClient) selectOneHealthyInstances(service model.Service) (*model
|
|||
return nil, errors.New("healthy instance list is empty!")
|
||||
}
|
||||
|
||||
chooser := newChooser(result)
|
||||
instance := chooser.pick()
|
||||
instance := newChooser(result).pick()
|
||||
return &instance, nil
|
||||
}
|
||||
|
||||
type instance []model.Instance
|
||||
|
||||
func (a instance) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a instance) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func (a instance) Less(i, j int) bool {
|
||||
return a[i].Weight < a[j].Weight
|
||||
}
|
||||
|
||||
// NewChooser initializes a new Chooser for picking from the provided Choices.
|
||||
func newChooser(instances []model.Instance) Chooser {
|
||||
sort.Sort(instance(instances))
|
||||
totals := make([]int, len(instances))
|
||||
runningTotal := 0
|
||||
for i, c := range instances {
|
||||
runningTotal += int(c.Weight)
|
||||
totals[i] = runningTotal
|
||||
}
|
||||
return Chooser{data: instances, totals: totals, max: runningTotal}
|
||||
}
|
||||
|
||||
func (chs Chooser) pick() model.Instance {
|
||||
r := rand.Intn(chs.max) + 1
|
||||
i := sort.SearchInts(chs.totals, r)
|
||||
return chs.data[i]
|
||||
}
|
||||
|
||||
//Subscribe subscibe service
|
||||
// Subscribe ...
|
||||
func (sc *NamingClient) Subscribe(param *vo.SubscribeParam) error {
|
||||
if len(param.GroupName) == 0 {
|
||||
param.GroupName = constant.DEFAULT_GROUP
|
||||
}
|
||||
serviceParam := vo.GetServiceParam{
|
||||
ServiceName: param.ServiceName,
|
||||
GroupName: param.GroupName,
|
||||
Clusters: param.Clusters,
|
||||
}
|
||||
|
||||
sc.subCallback.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
svc, err := sc.GetService(serviceParam)
|
||||
if err != nil {
|
||||
clusters := strings.Join(param.Clusters, ",")
|
||||
sc.serviceInfoHolder.RegisterCallback(util.GetGroupName(param.ServiceName, param.GroupName), clusters, ¶m.SubscribeCallback)
|
||||
_, err := sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
|
||||
return err
|
||||
}
|
||||
if !sc.hostReactor.serviceProxy.clientConfig.NotLoadCacheAtStart {
|
||||
sc.subCallback.ServiceChanged(&svc)
|
||||
}
|
||||
return nil
|
||||
|
||||
// Unsubscribe ...
|
||||
func (sc *NamingClient) Unsubscribe(param *vo.SubscribeParam) (err error) {
|
||||
clusters := strings.Join(param.Clusters, ",")
|
||||
serviceFullName := util.GetGroupName(param.ServiceName, param.GroupName)
|
||||
sc.serviceInfoHolder.DeregisterCallback(serviceFullName, clusters, ¶m.SubscribeCallback)
|
||||
if sc.serviceInfoHolder.IsSubscribed(serviceFullName, clusters) {
|
||||
err = sc.serviceProxy.Unsubscribe(param.ServiceName, param.GroupName, clusters)
|
||||
}
|
||||
|
||||
//Unsubscribe unsubscribe service
|
||||
func (sc *NamingClient) Unsubscribe(param *vo.SubscribeParam) error {
|
||||
sc.subCallback.RemoveCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), ¶m.SubscribeCallback)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// CloseClient ...
|
||||
func (sc *NamingClient) CloseClient() {
|
||||
sc.serviceProxy.CloseClient()
|
||||
}
|
||||
|
|
|
@ -13,21 +13,22 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package naming_client
|
||||
|
||||
import (
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination ../../mock/mock_service_client_interface.go -package mock -source=./service_client_interface.go
|
||||
|
||||
// INamingClient interface for naming client
|
||||
type INamingClient interface {
|
||||
|
||||
// RegisterInstance use to register instance
|
||||
// Ip require
|
||||
// Port require
|
||||
//Tenant optional
|
||||
// Weight require,it must be lager than 0
|
||||
// Enable require,the instance can be access or not
|
||||
// Healthy require,the instance is health or not
|
||||
|
@ -48,17 +49,17 @@ type INamingClient interface {
|
|||
// Ephemeral optional
|
||||
DeregisterInstance(param vo.DeregisterInstanceParam) (bool, error)
|
||||
|
||||
// UpdateInstance use to modify instance
|
||||
// Ip required
|
||||
// Port required
|
||||
// Tenant optional
|
||||
// Cluster optional,default:DEFAULT
|
||||
// UpdateInstance use to update instance
|
||||
// Ip require
|
||||
// Port require
|
||||
// Weight require,it must be lager than 0
|
||||
// Enable require,the instance can be access or not
|
||||
// Healthy require,the instance is health or not
|
||||
// Metadata optional
|
||||
// ClusterName optional,default:DEFAULT
|
||||
// ServiceName require
|
||||
// GroupName optional,default:DEFAULT_GROUP
|
||||
// Ephemeral optional
|
||||
// Weight require,it must be lager than 0
|
||||
// Enable require,the instance can be access or not
|
||||
// Metadata optional
|
||||
UpdateInstance(param vo.UpdateInstanceParam) (bool, error)
|
||||
|
||||
// GetService use to get service
|
||||
|
@ -67,7 +68,7 @@ type INamingClient interface {
|
|||
// GroupName optional,default:DEFAULT_GROUP
|
||||
GetService(param vo.GetServiceParam) (model.Service, error)
|
||||
|
||||
//SelectAllInstance return all instances,include healthy=false,enable=false,weight<=0
|
||||
// SelectAllInstances return all instances,include healthy=false,enable=false,weight<=0
|
||||
// ServiceName require
|
||||
// Clusters optional,default:DEFAULT
|
||||
// GroupName optional,default:DEFAULT_GROUP
|
||||
|
@ -80,7 +81,7 @@ type INamingClient interface {
|
|||
// HealthyOnly optional
|
||||
SelectInstances(param vo.SelectInstancesParam) ([]model.Instance, error)
|
||||
|
||||
//SelectInstances 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
|
||||
// ServiceName require
|
||||
// Clusters optional,default:DEFAULT
|
||||
|
@ -103,4 +104,7 @@ type INamingClient interface {
|
|||
|
||||
// GetAllServicesInfo use to get all service info by page
|
||||
GetAllServicesInfo(param vo.GetAllServiceInfoParam) (model.ServiceList, error)
|
||||
|
||||
//CloseClient close the GRPC client
|
||||
CloseClient()
|
||||
}
|
||||
|
|
|
@ -17,18 +17,15 @@
|
|||
package naming_client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/mock"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"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/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var clientConfigTest = *constant.NewClientConfig(
|
||||
|
@ -37,39 +34,52 @@ var clientConfigTest = *constant.NewClientConfig(
|
|||
constant.WithNotLoadCacheAtStart(true),
|
||||
)
|
||||
|
||||
var serverConfigTest = *constant.NewServerConfig("console.nacos.io", 80, constant.WithContextPath("/nacos"))
|
||||
var serverConfigTest = *constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos"))
|
||||
|
||||
func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "DEFAULT_GROUP@@DEMO",
|
||||
"groupName": "DEFAULT_GROUP",
|
||||
"app": "",
|
||||
"clusterName": "",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"weight": "0",
|
||||
"enable": "false",
|
||||
"healthy": "false",
|
||||
"metadata": "{}",
|
||||
"ephemeral": "false",
|
||||
})).Times(1).
|
||||
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
|
||||
type MockNamingProxy struct {
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
|
||||
return model.ServiceList{Doms: []string{""}}, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) ServerHealthy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
|
||||
return &model.Service{}, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) Subscribe(serviceName, groupName, clusters string) (model.Service, error) {
|
||||
return model.Service{}, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) Unsubscribe(serviceName, groupName, clusters string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockNamingProxy) CloseClient() {}
|
||||
|
||||
func NewTestNamingClient() *NamingClient {
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client, _ := NewNamingClient(&nc)
|
||||
success, err := client.RegisterInstance(vo.RegisterInstanceParam{
|
||||
client.serviceProxy = &MockNamingProxy{}
|
||||
return client
|
||||
}
|
||||
func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) {
|
||||
success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
|
||||
ServiceName: "DEMO",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
|
@ -80,39 +90,8 @@ func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_RegisterServiceInstance_withGroupName(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "test_group@@DEMO2",
|
||||
"groupName": "test_group",
|
||||
"app": "",
|
||||
"clusterName": "",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"weight": "0",
|
||||
"enable": "false",
|
||||
"healthy": "false",
|
||||
"metadata": "{}",
|
||||
"ephemeral": "false",
|
||||
})).Times(1).
|
||||
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
success, err := client.RegisterInstance(vo.RegisterInstanceParam{
|
||||
ServiceName: "DEMO2",
|
||||
success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
|
||||
ServiceName: "DEMO",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
GroupName: "test_group",
|
||||
|
@ -123,39 +102,8 @@ func Test_RegisterServiceInstance_withGroupName(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_RegisterServiceInstance_withCluster(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "test_group@@DEMO3",
|
||||
"groupName": "test_group",
|
||||
"app": "",
|
||||
"clusterName": "test",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"weight": "0",
|
||||
"enable": "false",
|
||||
"healthy": "false",
|
||||
"metadata": "{}",
|
||||
"ephemeral": "false",
|
||||
})).Times(1).
|
||||
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
success, err := client.RegisterInstance(vo.RegisterInstanceParam{
|
||||
ServiceName: "DEMO3",
|
||||
success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
|
||||
ServiceName: "DEMO",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
GroupName: "test_group",
|
||||
|
@ -165,198 +113,35 @@ func Test_RegisterServiceInstance_withCluster(t *testing.T) {
|
|||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, true, success)
|
||||
}
|
||||
|
||||
func Test_RegisterServiceInstance_401(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "test_group@@DEMO4",
|
||||
"groupName": "test_group",
|
||||
"app": "",
|
||||
"clusterName": "",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"weight": "0",
|
||||
"enable": "false",
|
||||
"healthy": "false",
|
||||
"metadata": "{}",
|
||||
"ephemeral": "false",
|
||||
})).Times(3).
|
||||
Return(http_agent.FakeHttpResponse(401, `no security`), nil)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
result, err := client.RegisterInstance(vo.RegisterInstanceParam{
|
||||
ServiceName: "DEMO4",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
GroupName: "test_group",
|
||||
Ephemeral: false,
|
||||
})
|
||||
assert.Equal(t, false, result)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNamingProxy_DeregisterService_WithoutGroupName(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("DELETE"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "DEFAULT_GROUP@@DEMO5",
|
||||
"clusterName": "",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"ephemeral": "true",
|
||||
})).Times(1).
|
||||
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
_, _ = client.DeregisterInstance(vo.DeregisterInstanceParam{
|
||||
success, err := NewTestNamingClient().DeregisterInstance(vo.DeregisterInstanceParam{
|
||||
ServiceName: "DEMO5",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
Ephemeral: true,
|
||||
})
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, true, success)
|
||||
}
|
||||
|
||||
func TestNamingProxy_DeregisterService_WithGroupName(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("DELETE"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "test_group@@DEMO6",
|
||||
"clusterName": "",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"ephemeral": "true",
|
||||
})).Times(1).
|
||||
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
_, _ = client.DeregisterInstance(vo.DeregisterInstanceParam{
|
||||
success, err := NewTestNamingClient().DeregisterInstance(vo.DeregisterInstanceParam{
|
||||
ServiceName: "DEMO6",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
GroupName: "test_group",
|
||||
Ephemeral: true,
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UpdateServiceInstance_withoutGroupName(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("PUT"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "DEFAULT_GROUP@@DEMO",
|
||||
"clusterName": "",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"weight": "0",
|
||||
"enable": "false",
|
||||
"metadata": "{}",
|
||||
"ephemeral": "false",
|
||||
})).Times(1).
|
||||
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
success, err := client.UpdateInstance(vo.UpdateInstanceParam{
|
||||
ServiceName: "DEMO",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
Ephemeral: false,
|
||||
Metadata: map[string]string{},
|
||||
})
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, true, success)
|
||||
}
|
||||
|
||||
func TestNamingProxy_DeregisterService_401(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
mockIHttpAgent.EXPECT().Request(gomock.Eq("DELETE"),
|
||||
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
|
||||
gomock.AssignableToTypeOf(http.Header{}),
|
||||
gomock.Eq(uint64(10*1000)),
|
||||
gomock.Eq(map[string]string{
|
||||
"namespaceId": "",
|
||||
"serviceName": "test_group@@DEMO7",
|
||||
"clusterName": "",
|
||||
"ip": "10.0.0.10",
|
||||
"port": "80",
|
||||
"ephemeral": "true",
|
||||
})).Times(3).
|
||||
Return(http_agent.FakeHttpResponse(401, `no security`), nil)
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
_, _ = client.DeregisterInstance(vo.DeregisterInstanceParam{
|
||||
ServiceName: "DEMO7",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 80,
|
||||
GroupName: "test_group",
|
||||
Ephemeral: true,
|
||||
})
|
||||
}
|
||||
|
||||
func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
|
||||
services := model.Service(model.Service{
|
||||
services := model.Service{
|
||||
Name: "DEFAULT_GROUP@@DEMO",
|
||||
CacheMillis: 1000,
|
||||
UseSpecifiedURL: false,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.10-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.10",
|
||||
|
@ -368,8 +153,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.11-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.11",
|
||||
|
@ -381,8 +164,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.12-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.12",
|
||||
|
@ -394,8 +175,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
|
|||
Healthy: false,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.13-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.13",
|
||||
|
@ -407,8 +186,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.14-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.14",
|
||||
|
@ -421,61 +198,33 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
|
||||
LastRefTime: 1528787794594, Env: "", Clusters: "a",
|
||||
Metadata: map[string]string(nil)})
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
instance1, err := client.selectOneHealthyInstances(services)
|
||||
LastRefTime: 1528787794594, Clusters: "a"}
|
||||
instance1, err := NewTestNamingClient().selectOneHealthyInstances(services)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, instance1)
|
||||
instance2, err := client.selectOneHealthyInstances(services)
|
||||
instance2, err := NewTestNamingClient().selectOneHealthyInstances(services)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, instance2)
|
||||
}
|
||||
|
||||
func TestNamingClient_SelectOneHealthyInstance_Empty(t *testing.T) {
|
||||
services := model.Service(model.Service{
|
||||
services := model.Service{
|
||||
Name: "DEFAULT_GROUP@@DEMO",
|
||||
CacheMillis: 1000,
|
||||
UseSpecifiedURL: false,
|
||||
Hosts: []model.Instance{},
|
||||
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
|
||||
LastRefTime: 1528787794594, Env: "", Clusters: "a",
|
||||
Metadata: map[string]string(nil)})
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
instance, err := client.selectOneHealthyInstances(services)
|
||||
LastRefTime: 1528787794594, Clusters: "a"}
|
||||
instance, err := NewTestNamingClient().selectOneHealthyInstances(services)
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, instance)
|
||||
}
|
||||
|
||||
func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
|
||||
services := model.Service(model.Service{
|
||||
services := model.Service{
|
||||
Name: "DEFAULT_GROUP@@DEMO",
|
||||
CacheMillis: 1000,
|
||||
UseSpecifiedURL: false,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.10-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.10",
|
||||
|
@ -487,8 +236,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.11-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.11",
|
||||
|
@ -500,8 +247,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.12-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.12",
|
||||
|
@ -513,8 +258,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
|
|||
Healthy: false,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.13-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.13",
|
||||
|
@ -526,8 +269,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.14-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.14",
|
||||
|
@ -540,33 +281,18 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
|
||||
LastRefTime: 1528787794594, Env: "", Clusters: "a",
|
||||
Metadata: map[string]string(nil)})
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
instances, err := client.selectInstances(services, true)
|
||||
LastRefTime: 1528787794594, Clusters: "a"}
|
||||
instances, err := NewTestNamingClient().selectInstances(services, true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(instances))
|
||||
}
|
||||
|
||||
func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
|
||||
services := model.Service(model.Service{
|
||||
services := model.Service{
|
||||
Name: "DEFAULT_GROUP@@DEMO",
|
||||
CacheMillis: 1000,
|
||||
UseSpecifiedURL: false,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.10-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.10",
|
||||
|
@ -578,8 +304,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.11-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.11",
|
||||
|
@ -591,8 +315,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.12-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.12",
|
||||
|
@ -604,8 +326,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
|
|||
Healthy: false,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.13-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.13",
|
||||
|
@ -617,8 +337,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
|
|||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Valid: true,
|
||||
Marked: false,
|
||||
InstanceId: "10.10.10.14-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.14",
|
||||
|
@ -631,87 +349,102 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
|
||||
LastRefTime: 1528787794594, Env: "", Clusters: "a",
|
||||
Metadata: map[string]string(nil)})
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
instances, err := client.selectInstances(services, false)
|
||||
LastRefTime: 1528787794594, Clusters: "a"}
|
||||
instances, err := NewTestNamingClient().selectInstances(services, false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(instances))
|
||||
}
|
||||
|
||||
func TestNamingClient_SelectInstances_Empty(t *testing.T) {
|
||||
services := model.Service(model.Service{
|
||||
services := model.Service{
|
||||
Name: "DEFAULT_GROUP@@DEMO",
|
||||
CacheMillis: 1000,
|
||||
UseSpecifiedURL: false,
|
||||
Hosts: []model.Instance{},
|
||||
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
|
||||
LastRefTime: 1528787794594, Env: "", Clusters: "a",
|
||||
Metadata: map[string]string(nil)})
|
||||
ctrl := gomock.NewController(t)
|
||||
defer func() {
|
||||
ctrl.Finish()
|
||||
}()
|
||||
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
|
||||
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(mockIHttpAgent)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
instances, err := client.selectInstances(services, false)
|
||||
LastRefTime: 1528787794594, Clusters: "a"}
|
||||
instances, err := NewTestNamingClient().selectInstances(services, false)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, 0, len(instances))
|
||||
}
|
||||
|
||||
func TestNamingClient_GetAllServicesInfo(t *testing.T) {
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
|
||||
client, _ := NewNamingClient(&nc)
|
||||
reslut, err := client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
result, err := NewTestNamingClient().GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
GroupName: "DEFAULT_GROUP",
|
||||
PageNo: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
|
||||
assert.NotNil(t, reslut.Doms)
|
||||
assert.NotNil(t, result.Doms)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestNamingClient_selectOneHealthyInstanceResult(t *testing.T) {
|
||||
services := model.Service(model.Service{
|
||||
func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
|
||||
services := model.Service{
|
||||
Name: "DEFAULT_GROUP@@DEMO",
|
||||
CacheMillis: 1000,
|
||||
Hosts: []model.Instance{
|
||||
{
|
||||
Ip: "127.0.0.1",
|
||||
Weight: 1,
|
||||
InstanceId: "10.10.10.10-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.10",
|
||||
Weight: 10,
|
||||
Metadata: map[string]string{},
|
||||
ClusterName: "a",
|
||||
ServiceName: "DEMO1",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
},
|
||||
{
|
||||
Ip: "127.0.0.2",
|
||||
Weight: 9,
|
||||
InstanceId: "10.10.10.11-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.11",
|
||||
Weight: 10,
|
||||
Metadata: map[string]string{},
|
||||
ClusterName: "a",
|
||||
ServiceName: "DEMO2",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
},
|
||||
}})
|
||||
nc := nacos_client.NacosClient{}
|
||||
_ = nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
|
||||
_ = nc.SetClientConfig(clientConfigTest)
|
||||
client, _ := NewNamingClient(&nc)
|
||||
for i := 0; i < 10; i++ {
|
||||
{
|
||||
InstanceId: "10.10.10.12-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.12",
|
||||
Weight: 1,
|
||||
Metadata: map[string]string{},
|
||||
ClusterName: "a",
|
||||
ServiceName: "DEMO3",
|
||||
Enable: true,
|
||||
Healthy: false,
|
||||
},
|
||||
{
|
||||
InstanceId: "10.10.10.13-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.13",
|
||||
Weight: 1,
|
||||
Metadata: map[string]string{},
|
||||
ClusterName: "a",
|
||||
ServiceName: "DEMO4",
|
||||
Enable: false,
|
||||
Healthy: true,
|
||||
},
|
||||
{
|
||||
InstanceId: "10.10.10.14-80-a-DEMO",
|
||||
Port: 80,
|
||||
Ip: "10.10.10.14",
|
||||
Weight: 0,
|
||||
Metadata: map[string]string{},
|
||||
ClusterName: "a",
|
||||
ServiceName: "DEMO5",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
},
|
||||
},
|
||||
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
|
||||
LastRefTime: 1528787794594, Clusters: "a"}
|
||||
client := NewTestNamingClient()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = client.selectOneHealthyInstances(services)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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_grpc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy"
|
||||
|
||||
"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/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
type ConnectionEventListener struct {
|
||||
clientProxy naming_proxy.INamingProxy
|
||||
registeredInstanceCached cache.ConcurrentMap
|
||||
subscribes cache.ConcurrentMap
|
||||
}
|
||||
|
||||
func NewConnectionEventListener(clientProxy naming_proxy.INamingProxy) *ConnectionEventListener {
|
||||
return &ConnectionEventListener{
|
||||
clientProxy: clientProxy,
|
||||
registeredInstanceCached: cache.NewConcurrentMap(),
|
||||
subscribes: cache.NewConcurrentMap(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) OnConnected() {
|
||||
c.redoSubscribe()
|
||||
c.redoRegisterEachService()
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) OnDisConnect() {
|
||||
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) redoSubscribe() {
|
||||
for _, key := range c.subscribes.Keys() {
|
||||
info := strings.Split(key, constant.SERVICE_INFO_SPLITER)
|
||||
var err error
|
||||
var service model.Service
|
||||
if len(info) > 2 {
|
||||
service, err = c.clientProxy.Subscribe(info[1], info[0], info[2])
|
||||
} else {
|
||||
service, err = c.clientProxy.Subscribe(info[1], info[0], "")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Warnf("redo subscribe service:%s faild:%+v", info[1], err)
|
||||
return
|
||||
}
|
||||
|
||||
grpcProxy, ok := c.clientProxy.(*NamingGrpcProxy)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
grpcProxy.serviceInfoHolder.ProcessService(&service)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) redoRegisterEachService() {
|
||||
for k, v := range c.registeredInstanceCached.Items() {
|
||||
info := strings.Split(k, constant.SERVICE_INFO_SPLITER)
|
||||
serviceName := info[1]
|
||||
groupName := info[0]
|
||||
instance, ok := v.(model.Instance)
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) CacheInstanceForRedo(serviceName, groupName string, instance model.Instance) {
|
||||
key := util.GetGroupName(serviceName, groupName)
|
||||
getInstance, _ := c.registeredInstanceCached.Get(key)
|
||||
if getInstance != nil && reflect.DeepEqual(getInstance.(model.Instance), instance) {
|
||||
return
|
||||
}
|
||||
c.registeredInstanceCached.Set(key, instance)
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) RemoveInstanceForRedo(serviceName, groupName string, instance model.Instance) {
|
||||
key := util.GetGroupName(serviceName, groupName)
|
||||
_, ok := c.registeredInstanceCached.Get(key)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.registeredInstanceCached.Remove(key)
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) CacheSubscriberForRedo(fullServiceName, clusters string) {
|
||||
key := util.GetServiceCacheKey(fullServiceName, clusters)
|
||||
if _, ok := c.subscribes.Get(key); !ok {
|
||||
c.subscribes.Set(key, struct{}{})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnectionEventListener) RemoveSubscriberForRedo(fullServiceName, clusters string) {
|
||||
c.subscribes.Remove(util.GetServiceCacheKey(fullServiceName, clusters))
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package naming_grpc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"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/util"
|
||||
)
|
||||
|
||||
func TestRedoSubscribe(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockProxy := naming_proxy.NewMockINamingProxy(ctrl)
|
||||
evListener := NewConnectionEventListener(mockProxy)
|
||||
|
||||
cases := []struct {
|
||||
serviceName string
|
||||
groupName string
|
||||
clusters string
|
||||
}{
|
||||
{"service-a", "group-a", ""},
|
||||
{"service-b", "group-b", "cluster-b"},
|
||||
}
|
||||
|
||||
for _, v := range cases {
|
||||
fullServiceName := util.GetGroupName(v.serviceName, v.groupName)
|
||||
evListener.CacheSubscriberForRedo(fullServiceName, v.clusters)
|
||||
mockProxy.EXPECT().Subscribe(v.serviceName, v.groupName, v.clusters)
|
||||
evListener.redoSubscribe()
|
||||
evListener.RemoveSubscriberForRedo(fullServiceName, v.clusters)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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_grpc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"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/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/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/inner/uuid"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
// NamingGrpcProxy ...
|
||||
type NamingGrpcProxy struct {
|
||||
clientConfig constant.ClientConfig
|
||||
nacosServer *nacos_server.NacosServer
|
||||
rpcClient rpc.IRpcClient
|
||||
eventListener *ConnectionEventListener
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder
|
||||
}
|
||||
|
||||
// NewNamingGrpcProxy create naming grpc proxy
|
||||
func NewNamingGrpcProxy(clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer,
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder) (*NamingGrpcProxy, error) {
|
||||
srvProxy := NamingGrpcProxy{
|
||||
clientConfig: clientCfg,
|
||||
nacosServer: nacosServer,
|
||||
serviceInfoHolder: serviceInfoHolder,
|
||||
}
|
||||
|
||||
uid, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels := map[string]string{
|
||||
constant.LABEL_SOURCE: constant.LABEL_SOURCE_SDK,
|
||||
constant.LABEL_MODULE: constant.LABEL_MODULE_NAMING,
|
||||
}
|
||||
|
||||
iRpcClient, err := rpc.CreateClient(uid.String(), rpc.GRPC, labels, srvProxy.nacosServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
srvProxy.rpcClient = iRpcClient
|
||||
|
||||
rpcClient := srvProxy.rpcClient.GetRpcClient()
|
||||
rpcClient.Start()
|
||||
|
||||
rpcClient.RegisterServerRequestHandler(func() rpc_request.IRequest {
|
||||
return &rpc_request.NotifySubscriberRequest{NamingRequest: &rpc_request.NamingRequest{}}
|
||||
}, &rpc.NamingPushRequestHandler{ServiceInfoHolder: serviceInfoHolder})
|
||||
|
||||
srvProxy.eventListener = NewConnectionEventListener(&srvProxy)
|
||||
rpcClient.RegisterConnectionListener(srvProxy.eventListener)
|
||||
|
||||
return &srvProxy, nil
|
||||
}
|
||||
|
||||
func (proxy *NamingGrpcProxy) requestToServer(request rpc_request.IRequest) (rpc_response.IResponse, error) {
|
||||
start := time.Now()
|
||||
proxy.nacosServer.InjectSign(request, request.GetHeaders(), proxy.clientConfig)
|
||||
proxy.nacosServer.InjectSecurityInfo(request.GetHeaders())
|
||||
response, err := proxy.rpcClient.GetRpcClient().Request(request, int64(proxy.clientConfig.TimeoutMs))
|
||||
monitor.GetConfigRequestMonitor(constant.GRPC, request.GetRequestType(), rpc_response.GetGrpcResponseStatusCode(response)).Observe(float64(time.Now().Nanosecond() - start.Nanosecond()))
|
||||
return response, err
|
||||
}
|
||||
|
||||
// RegisterInstance ...
|
||||
func (proxy *NamingGrpcProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
logger.Infof("instance namespaceId:<%s>,serviceName:<%s> with instance:<%s>",
|
||||
proxy.clientConfig.NamespaceId, serviceName, util.ToJsonString(instance))
|
||||
instanceRequest := rpc_request.NewInstanceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, "registerInstance", instance)
|
||||
response, err := proxy.requestToServer(instanceRequest)
|
||||
proxy.eventListener.CacheInstanceForRedo(serviceName, groupName, instance)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return response.IsSuccess(), err
|
||||
}
|
||||
|
||||
// DeregisterInstance ...
|
||||
func (proxy *NamingGrpcProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
logger.Infof("deregister instance namespaceId:<%s>,serviceName:<%s> with instance:<%s:%d@%s>",
|
||||
proxy.clientConfig.NamespaceId, serviceName, instance.Ip, instance.Port, instance.ClusterName)
|
||||
instanceRequest := rpc_request.NewInstanceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName, "deregisterInstance", instance)
|
||||
response, err := proxy.requestToServer(instanceRequest)
|
||||
proxy.eventListener.RemoveInstanceForRedo(serviceName, groupName, instance)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return response.IsSuccess(), err
|
||||
}
|
||||
|
||||
// GetServiceList ...
|
||||
func (proxy *NamingGrpcProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
|
||||
var selectorStr string
|
||||
if selector != nil {
|
||||
switch selector.Type {
|
||||
case "label":
|
||||
selectorStr = util.ToJsonString(selector)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
response, err := proxy.requestToServer(rpc_request.NewServiceListRequest(proxy.clientConfig.NamespaceId, "",
|
||||
groupName, int(pageNo), int(pageSize), selectorStr))
|
||||
if err != nil {
|
||||
return model.ServiceList{}, err
|
||||
}
|
||||
serviceListResponse := response.(*rpc_response.ServiceListResponse)
|
||||
return model.ServiceList{
|
||||
Count: int64(serviceListResponse.Count),
|
||||
Doms: serviceListResponse.ServiceNames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ServerHealthy ...
|
||||
func (proxy *NamingGrpcProxy) ServerHealthy() bool {
|
||||
return proxy.rpcClient.GetRpcClient().IsRunning()
|
||||
}
|
||||
|
||||
// QueryInstancesOfService ...
|
||||
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, clusters,
|
||||
healthyOnly, udpPort))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queryServiceResponse := response.(*rpc_response.QueryServiceResponse)
|
||||
return &queryServiceResponse.ServiceInfo, nil
|
||||
}
|
||||
|
||||
// Subscribe ...
|
||||
func (proxy *NamingGrpcProxy) Subscribe(serviceName, groupName string, clusters string) (model.Service, error) {
|
||||
proxy.eventListener.CacheSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters)
|
||||
request := rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName,
|
||||
groupName, clusters, true)
|
||||
request.Headers["app"] = proxy.clientConfig.AppName
|
||||
response, err := proxy.requestToServer(request)
|
||||
if err != nil {
|
||||
return model.Service{}, err
|
||||
}
|
||||
subscribeServiceResponse := response.(*rpc_response.SubscribeServiceResponse)
|
||||
return subscribeServiceResponse.ServiceInfo, nil
|
||||
}
|
||||
|
||||
// Unsubscribe ...
|
||||
func (proxy *NamingGrpcProxy) Unsubscribe(serviceName, groupName, clusters string) error {
|
||||
proxy.eventListener.RemoveSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters)
|
||||
_, err := proxy.requestToServer(rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName,
|
||||
clusters, false))
|
||||
return err
|
||||
}
|
||||
|
||||
func (proxy *NamingGrpcProxy) CloseClient() {
|
||||
proxy.rpcClient.GetRpcClient().Shutdown()
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package naming_grpc
|
||||
|
||||
import "github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
|
||||
type MockNamingGrpc struct {
|
||||
}
|
||||
|
||||
func (m *MockNamingGrpc) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingGrpc) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingGrpc) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
|
||||
return model.ServiceList{Doms: []string{""}}, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingGrpc) ServerHealthy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *MockNamingGrpc) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
|
||||
return &model.Service{}, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingGrpc) Subscribe(serviceName, groupName, clusters string) (model.Service, error) {
|
||||
return model.Service{}, nil
|
||||
}
|
||||
|
||||
func (m *MockNamingGrpc) Unsubscribe(serviceName, groupName, clusters string) {}
|
|
@ -14,30 +14,38 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package naming_client
|
||||
package naming_http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/cache"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
"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/logger"
|
||||
"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/util"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
type BeatReactor struct {
|
||||
beatMap cache.ConcurrentMap
|
||||
serviceProxy NamingProxy
|
||||
clientBeatInterval int64
|
||||
nacosServer *nacos_server.NacosServer
|
||||
beatThreadCount int
|
||||
beatThreadSemaphore *semaphore.Weighted
|
||||
beatRecordMap cache.ConcurrentMap
|
||||
clientCfg constant.ClientConfig
|
||||
mux *sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -45,14 +53,11 @@ const DefaultBeatThreadNum = 20
|
|||
|
||||
var ctx = context.Background()
|
||||
|
||||
func NewBeatReactor(serviceProxy NamingProxy, clientBeatInterval int64) BeatReactor {
|
||||
func NewBeatReactor(clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer) BeatReactor {
|
||||
br := BeatReactor{}
|
||||
if clientBeatInterval <= 0 {
|
||||
clientBeatInterval = 5 * 1000
|
||||
}
|
||||
br.beatMap = cache.NewConcurrentMap()
|
||||
br.serviceProxy = serviceProxy
|
||||
br.clientBeatInterval = clientBeatInterval
|
||||
br.nacosServer = nacosServer
|
||||
br.clientCfg = clientCfg
|
||||
br.beatThreadCount = DefaultBeatThreadNum
|
||||
br.beatRecordMap = cache.NewConcurrentMap()
|
||||
br.beatThreadSemaphore = semaphore.NewWeighted(int64(br.beatThreadCount))
|
||||
|
@ -76,6 +81,7 @@ func (br *BeatReactor) AddBeatInfo(serviceName string, beatInfo *model.BeatInfo)
|
|||
}
|
||||
br.beatMap.Set(k, beatInfo)
|
||||
beatInfo.Metadata = util.DeepCopyMap(beatInfo.Metadata)
|
||||
monitor.GetDom2BeatSizeMonitor().Set(float64(br.beatMap.Count()))
|
||||
go br.sendInstanceBeat(k, beatInfo)
|
||||
}
|
||||
|
||||
|
@ -89,16 +95,14 @@ func (br *BeatReactor) RemoveBeatInfo(serviceName string, ip string, port uint64
|
|||
beatInfo := data.(*model.BeatInfo)
|
||||
atomic.StoreInt32(&beatInfo.State, int32(model.StateShutdown))
|
||||
}
|
||||
monitor.GetDom2BeatSizeMonitor().Set(float64(br.beatMap.Count()))
|
||||
br.beatMap.Remove(k)
|
||||
|
||||
}
|
||||
|
||||
func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
|
||||
for {
|
||||
err := br.beatThreadSemaphore.Acquire(ctx, 1)
|
||||
if err != nil {
|
||||
logger.Errorf("sendInstanceBeat failed to acquire semaphore: %v", err)
|
||||
return
|
||||
}
|
||||
br.beatThreadSemaphore.Acquire(ctx, 1)
|
||||
//如果当前实例注销,则进行停止心跳
|
||||
if atomic.LoadInt32(&beatInfo.State) == int32(model.StateShutdown) {
|
||||
logger.Infof("instance[%s] stop heartBeating", k)
|
||||
|
@ -107,7 +111,7 @@ func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
|
|||
}
|
||||
|
||||
//进行心跳通信
|
||||
beatInterval, err := br.serviceProxy.SendBeat(beatInfo)
|
||||
beatInterval, err := br.SendBeat(beatInfo)
|
||||
if err != nil {
|
||||
logger.Errorf("beat to server return error:%+v", err)
|
||||
br.beatThreadSemaphore.Release(1)
|
||||
|
@ -126,3 +130,26 @@ func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
|
|||
<-t.C
|
||||
}
|
||||
}
|
||||
|
||||
func (br *BeatReactor) SendBeat(info *model.BeatInfo) (int64, error) {
|
||||
logger.Infof("namespaceId:<%s> sending beat to server:<%s>",
|
||||
br.clientCfg.NamespaceId, util.ToJsonString(info))
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = br.clientCfg.NamespaceId
|
||||
params["serviceName"] = info.ServiceName
|
||||
params["beat"] = util.ToJsonString(info)
|
||||
api := constant.SERVICE_BASE_PATH + "/instance/beat"
|
||||
result, err := br.nacosServer.ReqApi(api, params, http.MethodPut)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if result != "" {
|
||||
interVal, err := jsonparser.GetInt([]byte(result), "clientBeatInterval")
|
||||
if err != nil {
|
||||
return 0, errors.New(fmt.Sprintf("namespaceId:<%s> sending beat to server:<%s> get 'clientBeatInterval' from <%s> error:<%+v>", br.clientCfg.NamespaceId, util.ToJsonString(info), result, err))
|
||||
} else {
|
||||
return interVal, nil
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
|
@ -14,21 +14,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package naming_client
|
||||
package naming_http
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/nacos_server"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"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/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBeatReactor_AddBeatInfo(t *testing.T) {
|
||||
br := NewBeatReactor(NamingProxy{nacosServer: &nacos_server.NacosServer{}}, 5000)
|
||||
br := NewBeatReactor(constant.ClientConfig{}, &nacos_server.NacosServer{})
|
||||
serviceName := "Test"
|
||||
groupName := "public"
|
||||
beatInfo := &model.BeatInfo{
|
||||
|
@ -38,7 +37,6 @@ func TestBeatReactor_AddBeatInfo(t *testing.T) {
|
|||
ServiceName: util.GetGroupName(serviceName, groupName),
|
||||
Cluster: "default",
|
||||
Weight: 1,
|
||||
Period: time.Second * 5,
|
||||
}
|
||||
br.AddBeatInfo(util.GetGroupName(serviceName, groupName), beatInfo)
|
||||
key := buildKey(util.GetGroupName(serviceName, groupName), beatInfo.Ip, beatInfo.Port)
|
||||
|
@ -48,7 +46,7 @@ func TestBeatReactor_AddBeatInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBeatReactor_RemoveBeatInfo(t *testing.T) {
|
||||
br := NewBeatReactor(NamingProxy{nacosServer: &nacos_server.NacosServer{}}, 5000)
|
||||
br := NewBeatReactor(constant.ClientConfig{}, &nacos_server.NacosServer{})
|
||||
serviceName := "Test"
|
||||
groupName := "public"
|
||||
beatInfo1 := &model.BeatInfo{
|
||||
|
@ -58,7 +56,6 @@ func TestBeatReactor_RemoveBeatInfo(t *testing.T) {
|
|||
ServiceName: util.GetGroupName(serviceName, groupName),
|
||||
Cluster: "default",
|
||||
Weight: 1,
|
||||
Period: time.Second * 5,
|
||||
}
|
||||
br.AddBeatInfo(util.GetGroupName(serviceName, groupName), beatInfo1)
|
||||
beatInfo2 := &model.BeatInfo{
|
||||
|
@ -68,7 +65,6 @@ func TestBeatReactor_RemoveBeatInfo(t *testing.T) {
|
|||
ServiceName: util.GetGroupName(serviceName, groupName),
|
||||
Cluster: "default",
|
||||
Weight: 1,
|
||||
Period: time.Second * 5,
|
||||
}
|
||||
br.AddBeatInfo(util.GetGroupName(serviceName, groupName), beatInfo2)
|
||||
br.RemoveBeatInfo(util.GetGroupName(serviceName, groupName), "127.0.0.1", 8080)
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* 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_http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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/common/constant"
|
||||
"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/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
// NamingHttpProxy ...
|
||||
type NamingHttpProxy struct {
|
||||
clientConfig constant.ClientConfig
|
||||
nacosServer *nacos_server.NacosServer
|
||||
beatReactor BeatReactor
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder
|
||||
}
|
||||
|
||||
// NewNamingHttpProxy create naming http proxy
|
||||
func NewNamingHttpProxy(clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer,
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder) (*NamingHttpProxy, error) {
|
||||
srvProxy := NamingHttpProxy{
|
||||
clientConfig: clientCfg,
|
||||
nacosServer: nacosServer,
|
||||
serviceInfoHolder: serviceInfoHolder,
|
||||
}
|
||||
|
||||
srvProxy.beatReactor = NewBeatReactor(clientCfg, nacosServer)
|
||||
|
||||
NewPushReceiver(serviceInfoHolder).startServer()
|
||||
|
||||
return &srvProxy, nil
|
||||
}
|
||||
|
||||
// RegisterInstance ...
|
||||
func (proxy *NamingHttpProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
logger.Infof("register instance namespaceId:<%s>,serviceName:<%s> with instance:<%s>",
|
||||
proxy.clientConfig.NamespaceId, serviceName, util.ToJsonString(instance))
|
||||
serviceName = util.GetGroupName(serviceName, groupName)
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["serviceName"] = serviceName
|
||||
params["groupName"] = groupName
|
||||
params["app"] = proxy.clientConfig.AppName
|
||||
params["clusterName"] = instance.ClusterName
|
||||
params["ip"] = instance.Ip
|
||||
params["port"] = strconv.Itoa(int(instance.Port))
|
||||
params["weight"] = strconv.FormatFloat(instance.Weight, 'f', -1, 64)
|
||||
params["enable"] = strconv.FormatBool(instance.Enable)
|
||||
params["healthy"] = strconv.FormatBool(instance.Healthy)
|
||||
params["metadata"] = util.ToJsonString(instance.Metadata)
|
||||
params["ephemeral"] = strconv.FormatBool(instance.Ephemeral)
|
||||
_, err := proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodPost)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if instance.Ephemeral {
|
||||
beatInfo := &model.BeatInfo{
|
||||
Ip: instance.Ip,
|
||||
Port: instance.Port,
|
||||
Metadata: instance.Metadata,
|
||||
ServiceName: util.GetGroupName(serviceName, groupName),
|
||||
Cluster: instance.ClusterName,
|
||||
Weight: instance.Weight,
|
||||
Period: util.GetDurationWithDefault(instance.Metadata, constant.HEART_BEAT_INTERVAL, time.Second*5),
|
||||
State: model.StateRunning,
|
||||
}
|
||||
proxy.beatReactor.AddBeatInfo(util.GetGroupName(serviceName, groupName), beatInfo)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// DeregisterInstance ...
|
||||
func (proxy *NamingHttpProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
serviceName = util.GetGroupName(serviceName, groupName)
|
||||
logger.Infof("deregister instance namespaceId:<%s>,serviceName:<%s> with instance:<%s:%d@%s>",
|
||||
proxy.clientConfig.NamespaceId, serviceName, instance.Ip, instance.Port, instance.ClusterName)
|
||||
proxy.beatReactor.RemoveBeatInfo(serviceName, instance.Ip, instance.Port)
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["serviceName"] = serviceName
|
||||
params["clusterName"] = instance.ClusterName
|
||||
params["ip"] = instance.Ip
|
||||
params["port"] = strconv.Itoa(int(instance.Port))
|
||||
params["ephemeral"] = strconv.FormatBool(instance.Ephemeral)
|
||||
_, err := proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodDelete)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetServiceList ...
|
||||
func (proxy *NamingHttpProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["groupName"] = groupName
|
||||
params["pageNo"] = strconv.Itoa(int(pageNo))
|
||||
params["pageSize"] = strconv.Itoa(int(pageSize))
|
||||
|
||||
if selector != nil {
|
||||
switch selector.Type {
|
||||
case "label":
|
||||
params["selector"] = util.ToJsonString(selector)
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
||||
}
|
||||
}
|
||||
serviceList := model.ServiceList{}
|
||||
|
||||
api := constant.SERVICE_BASE_PATH + "/service/list"
|
||||
result, err := proxy.nacosServer.ReqApi(api, params, http.MethodGet)
|
||||
if err != nil {
|
||||
return serviceList, err
|
||||
}
|
||||
if result == "" {
|
||||
return serviceList, errors.New("request server return empty")
|
||||
}
|
||||
|
||||
count, err := jsonparser.GetInt([]byte(result), "count")
|
||||
if err != nil {
|
||||
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
|
||||
_, err = jsonparser.ArrayEach([]byte(result), func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
|
||||
doms = append(doms, string(value))
|
||||
}, "doms")
|
||||
if err != nil {
|
||||
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.Doms = doms
|
||||
return serviceList, nil
|
||||
}
|
||||
|
||||
// ServerHealthy ...
|
||||
func (proxy *NamingHttpProxy) ServerHealthy() bool {
|
||||
api := constant.SERVICE_BASE_PATH + "/operator/metrics"
|
||||
result, err := proxy.nacosServer.ReqApi(api, map[string]string{}, http.MethodGet)
|
||||
if err != nil {
|
||||
logger.Errorf("namespaceId:[%s] sending server healthy failed!,result:%s error:%+v", proxy.clientConfig.NamespaceId, result, err)
|
||||
return false
|
||||
}
|
||||
if result != "" {
|
||||
status, err := jsonparser.GetString([]byte(result), "status")
|
||||
if err != nil {
|
||||
logger.Errorf("namespaceId:[%s] sending server healthy failed!,result:%s error:%+v", proxy.clientConfig.NamespaceId, result, err)
|
||||
} else {
|
||||
return status == "UP"
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// QueryInstancesOfService ...
|
||||
func (proxy *NamingHttpProxy) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
|
||||
param := make(map[string]string)
|
||||
param["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
param["serviceName"] = util.GetGroupName(serviceName, groupName)
|
||||
param["app"] = proxy.clientConfig.AppName
|
||||
param["clusters"] = clusters
|
||||
param["udpPort"] = strconv.Itoa(udpPort)
|
||||
param["healthyOnly"] = strconv.FormatBool(healthyOnly)
|
||||
param["clientIP"] = util.LocalIP()
|
||||
api := constant.SERVICE_PATH + "/list"
|
||||
result, err := proxy.nacosServer.ReqApi(api, param, http.MethodGet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return util.JsonToService(result), nil
|
||||
|
||||
}
|
||||
|
||||
// Subscribe ...
|
||||
func (proxy *NamingHttpProxy) Subscribe(serviceName, groupName, clusters string) (model.Service, error) {
|
||||
return model.Service{}, nil
|
||||
}
|
||||
|
||||
// Unsubscribe ...
|
||||
func (proxy *NamingHttpProxy) Unsubscribe(serviceName, groupName, clusters string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *NamingHttpProxy) CloseClient() {
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package naming_client
|
||||
package naming_http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -26,14 +26,15 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
type PushReceiver struct {
|
||||
port int
|
||||
host string
|
||||
hostReactor *HostReactor
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder
|
||||
}
|
||||
|
||||
type PushData struct {
|
||||
|
@ -46,11 +47,10 @@ var (
|
|||
GZIP_MAGIC = []byte("\x1F\x8B")
|
||||
)
|
||||
|
||||
func NewPushReceiver(hostReactor *HostReactor) *PushReceiver {
|
||||
func NewPushReceiver(serviceInfoHolder *naming_cache.ServiceInfoHolder) *PushReceiver {
|
||||
pr := PushReceiver{
|
||||
hostReactor: hostReactor,
|
||||
serviceInfoHolder: serviceInfoHolder,
|
||||
}
|
||||
pr.startServer()
|
||||
return &pr
|
||||
}
|
||||
|
||||
|
@ -70,29 +70,32 @@ func (us *PushReceiver) tryListen() (*net.UDPConn, bool) {
|
|||
return conn, true
|
||||
}
|
||||
|
||||
func (us *PushReceiver) getConn() *net.UDPConn {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
func (us *PushReceiver) startServer() {
|
||||
var (
|
||||
conn *net.UDPConn
|
||||
ok bool
|
||||
)
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := 0; i < 3; i++ {
|
||||
port := r.Intn(1000) + 54951
|
||||
us.port = port
|
||||
conn, ok := us.tryListen()
|
||||
conn, ok = us.tryListen()
|
||||
|
||||
if ok {
|
||||
logger.Infof("udp server start, port: " + strconv.Itoa(port))
|
||||
return conn
|
||||
break
|
||||
}
|
||||
|
||||
if !ok && i == 2 {
|
||||
logger.Errorf("failed to start udp server after trying 3 times.")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us *PushReceiver) startServer() {
|
||||
conn := us.getConn()
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer conn.Close()
|
||||
for {
|
||||
|
@ -102,15 +105,6 @@ func (us *PushReceiver) startServer() {
|
|||
}
|
||||
|
||||
func (us *PushReceiver) handleClient(conn *net.UDPConn) {
|
||||
|
||||
if conn == nil {
|
||||
time.Sleep(time.Second * 5)
|
||||
conn = us.getConn()
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data := make([]byte, 4024)
|
||||
n, remoteAddr, err := conn.ReadFromUDP(data)
|
||||
if err != nil {
|
||||
|
@ -130,7 +124,7 @@ func (us *PushReceiver) handleClient(conn *net.UDPConn) {
|
|||
ack := make(map[string]string)
|
||||
|
||||
if pushData.PushType == "dom" || pushData.PushType == "service" {
|
||||
us.hostReactor.ProcessServiceJson(pushData.Data)
|
||||
us.serviceInfoHolder.ProcessServiceJson(pushData.Data)
|
||||
|
||||
ack["type"] = "push-ack"
|
||||
ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
|
||||
|
@ -139,7 +133,7 @@ func (us *PushReceiver) handleClient(conn *net.UDPConn) {
|
|||
} else if pushData.PushType == "dump" {
|
||||
ack["type"] = "dump-ack"
|
||||
ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
|
||||
ack["data"] = util.ToJsonString(us.hostReactor.serviceInfoMap)
|
||||
ack["data"] = util.ToJsonString(us.serviceInfoHolder.ServiceInfoMap)
|
||||
} else {
|
||||
ack["type"] = "unknow-ack"
|
||||
ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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_client
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
)
|
||||
|
||||
type Chooser struct {
|
||||
data []model.Instance
|
||||
totals []int
|
||||
max int
|
||||
}
|
||||
|
||||
type instance []model.Instance
|
||||
|
||||
func (a instance) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a instance) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func (a instance) Less(i, j int) bool {
|
||||
return a[i].Weight < a[j].Weight
|
||||
}
|
||||
|
||||
// NewChooser initializes a new Chooser for picking from the provided Choices.
|
||||
func newChooser(instances []model.Instance) Chooser {
|
||||
sort.Sort(instance(instances))
|
||||
totals := make([]int, len(instances))
|
||||
runningTotal := 0
|
||||
for i, c := range instances {
|
||||
runningTotal += int(c.Weight)
|
||||
totals[i] = runningTotal
|
||||
}
|
||||
return Chooser{data: instances, totals: totals, max: runningTotal}
|
||||
}
|
||||
|
||||
func (chs Chooser) pick() model.Instance {
|
||||
r := rand.Intn(chs.max) + 1
|
||||
i := sort.SearchInts(chs.totals, r)
|
||||
return chs.data[i]
|
||||
}
|
|
@ -1,205 +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_client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/nacos_server"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
)
|
||||
|
||||
type NamingProxy struct {
|
||||
clientConfig constant.ClientConfig
|
||||
nacosServer *nacos_server.NacosServer
|
||||
}
|
||||
|
||||
func NewNamingProxy(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig, httpAgent http_agent.IHttpAgent) (NamingProxy, error) {
|
||||
srvProxy := NamingProxy{}
|
||||
srvProxy.clientConfig = clientCfg
|
||||
|
||||
var err error
|
||||
srvProxy.nacosServer, err = nacos_server.NewNacosServer(serverCfgs, clientCfg, httpAgent, clientCfg.TimeoutMs, clientCfg.Endpoint)
|
||||
if err != nil {
|
||||
return srvProxy, err
|
||||
}
|
||||
|
||||
return srvProxy, nil
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (string, error) {
|
||||
logger.Infof("register instance namespaceId:<%s>,serviceName:<%s> with instance:<%s>",
|
||||
proxy.clientConfig.NamespaceId, serviceName, util.ToJsonString(instance))
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["serviceName"] = serviceName
|
||||
params["groupName"] = groupName
|
||||
params["app"] = proxy.clientConfig.AppName
|
||||
params["clusterName"] = instance.ClusterName
|
||||
params["ip"] = instance.Ip
|
||||
params["port"] = strconv.Itoa(int(instance.Port))
|
||||
params["weight"] = strconv.FormatFloat(instance.Weight, 'f', -1, 64)
|
||||
params["enable"] = strconv.FormatBool(instance.Enable)
|
||||
params["healthy"] = strconv.FormatBool(instance.Healthy)
|
||||
params["metadata"] = util.ToJsonString(instance.Metadata)
|
||||
params["ephemeral"] = strconv.FormatBool(instance.Ephemeral)
|
||||
return proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodPost)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) DeregisterInstance(serviceName string, ip string, port uint64, clusterName string, ephemeral bool) (string, error) {
|
||||
logger.Infof("deregister instance namespaceId:<%s>,serviceName:<%s> with instance:<%s:%d@%s>",
|
||||
proxy.clientConfig.NamespaceId, serviceName, ip, port, clusterName)
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["serviceName"] = serviceName
|
||||
params["clusterName"] = clusterName
|
||||
params["ip"] = ip
|
||||
params["port"] = strconv.Itoa(int(port))
|
||||
params["ephemeral"] = strconv.FormatBool(ephemeral)
|
||||
return proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodDelete)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) UpdateInstance(serviceName string, ip string, port uint64, clusterName string, ephemeral bool, weight float64, enable bool, metadata map[string]string) (string, error) {
|
||||
logger.Infof("modify instance namespaceId:<%s>,serviceName:<%s> with instance:<%s:%d@%s>",
|
||||
proxy.clientConfig.NamespaceId, serviceName, ip, port, clusterName)
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["serviceName"] = serviceName
|
||||
params["clusterName"] = clusterName
|
||||
params["ip"] = ip
|
||||
params["port"] = strconv.Itoa(int(port))
|
||||
params["ephemeral"] = strconv.FormatBool(ephemeral)
|
||||
params["weight"] = strconv.FormatFloat(weight, 'f', -1, 64)
|
||||
params["enable"] = strconv.FormatBool(enable)
|
||||
params["metadata"] = util.ToJsonString(metadata)
|
||||
return proxy.nacosServer.ReqApi(constant.SERVICE_PATH, params, http.MethodPut)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) SendBeat(info *model.BeatInfo) (int64, error) {
|
||||
|
||||
logger.Infof("namespaceId:<%s> sending beat to server:<%s>",
|
||||
proxy.clientConfig.NamespaceId, util.ToJsonString(info))
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["serviceName"] = info.ServiceName
|
||||
params["beat"] = util.ToJsonString(info)
|
||||
api := constant.SERVICE_BASE_PATH + "/instance/beat"
|
||||
result, err := proxy.nacosServer.ReqApi(api, params, http.MethodPut)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if result != "" {
|
||||
interVal, err := jsonparser.GetInt([]byte(result), "clientBeatInterval")
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("namespaceId:<%s> sending beat to server:<%s> get 'clientBeatInterval' from <%s> error:<%+v>", proxy.clientConfig.NamespaceId, util.ToJsonString(info), result, err)
|
||||
} else {
|
||||
return interVal, nil
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) GetServiceList(pageNo int, pageSize int, groupName string, selector *model.ExpressionSelector) (*model.ServiceList, error) {
|
||||
params := map[string]string{}
|
||||
params["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
params["groupName"] = groupName
|
||||
params["pageNo"] = strconv.Itoa(pageNo)
|
||||
params["pageSize"] = strconv.Itoa(pageSize)
|
||||
|
||||
if selector != nil {
|
||||
switch selector.Type {
|
||||
case "label":
|
||||
params["selector"] = util.ToJsonString(selector)
|
||||
}
|
||||
}
|
||||
|
||||
api := constant.SERVICE_BASE_PATH + "/service/list"
|
||||
result, err := proxy.nacosServer.ReqApi(api, params, http.MethodGet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == "" {
|
||||
return nil, errors.New("request server return empty")
|
||||
}
|
||||
|
||||
serviceList := model.ServiceList{}
|
||||
count, err := jsonparser.GetInt([]byte(result), "count")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("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
|
||||
_, err = jsonparser.ArrayEach([]byte(result), func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
|
||||
doms = append(doms, string(value))
|
||||
}, "doms")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("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.Doms = doms
|
||||
return &serviceList, nil
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) ServerHealthy() bool {
|
||||
api := constant.SERVICE_BASE_PATH + "/operator/metrics"
|
||||
result, err := proxy.nacosServer.ReqApi(api, map[string]string{}, http.MethodGet)
|
||||
if err != nil {
|
||||
logger.Errorf("namespaceId:[%s] sending server healthy failed!,result:%s error:%+v", proxy.clientConfig.NamespaceId, result, err)
|
||||
return false
|
||||
}
|
||||
if result != "" {
|
||||
status, err := jsonparser.GetString([]byte(result), "status")
|
||||
if err != nil {
|
||||
logger.Errorf("namespaceId:[%s] sending server healthy failed!,result:%s error:%+v", proxy.clientConfig.NamespaceId, result, err)
|
||||
} else {
|
||||
return status == "UP"
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) QueryList(serviceName string, clusters string, udpPort int, healthyOnly bool) (string, error) {
|
||||
param := make(map[string]string)
|
||||
param["namespaceId"] = proxy.clientConfig.NamespaceId
|
||||
param["serviceName"] = serviceName
|
||||
param["app"] = proxy.clientConfig.AppName
|
||||
param["clusters"] = clusters
|
||||
param["udpPort"] = strconv.Itoa(udpPort)
|
||||
param["healthyOnly"] = strconv.FormatBool(healthyOnly)
|
||||
param["clientIP"] = util.LocalIP()
|
||||
api := constant.SERVICE_PATH + "/list"
|
||||
return proxy.nacosServer.ReqApi(api, param, http.MethodGet)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxy) GetAllServiceInfoList(namespace, groupName string, pageNo, pageSize uint32) (string, error) {
|
||||
param := make(map[string]string)
|
||||
param["namespaceId"] = namespace
|
||||
param["groupName"] = groupName
|
||||
param["pageNo"] = strconv.Itoa(int(pageNo))
|
||||
param["pageSize"] = strconv.Itoa(int(pageSize))
|
||||
api := constant.SERVICE_INFO_PATH + "/list"
|
||||
return proxy.nacosServer.ReqApi(api, param, http.MethodGet)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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_proxy
|
||||
|
||||
import (
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
)
|
||||
|
||||
// INamingProxy ...
|
||||
type INamingProxy interface {
|
||||
RegisterInstance(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 string, selector *model.ExpressionSelector) (model.ServiceList, error)
|
||||
|
||||
ServerHealthy() bool
|
||||
|
||||
QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error)
|
||||
|
||||
Subscribe(serviceName, groupName, clusters string) (model.Service, error)
|
||||
|
||||
Unsubscribe(serviceName, groupName, clusters string) error
|
||||
|
||||
CloseClient()
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: proxy_interface.go
|
||||
|
||||
// Package naming_proxy is a generated GoMock package.
|
||||
package naming_proxy
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
model "github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
)
|
||||
|
||||
// MockINamingProxy is a mock of INamingProxy interface.
|
||||
type MockINamingProxy struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockINamingProxyMockRecorder
|
||||
}
|
||||
|
||||
// MockINamingProxyMockRecorder is the mock recorder for MockINamingProxy.
|
||||
type MockINamingProxyMockRecorder struct {
|
||||
mock *MockINamingProxy
|
||||
}
|
||||
|
||||
// NewMockINamingProxy creates a new mock instance.
|
||||
func NewMockINamingProxy(ctrl *gomock.Controller) *MockINamingProxy {
|
||||
mock := &MockINamingProxy{ctrl: ctrl}
|
||||
mock.recorder = &MockINamingProxyMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockINamingProxy) EXPECT() *MockINamingProxyMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CloseClient mocks base method.
|
||||
func (m *MockINamingProxy) CloseClient() {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "CloseClient")
|
||||
}
|
||||
|
||||
// CloseClient indicates an expected call of CloseClient.
|
||||
func (mr *MockINamingProxyMockRecorder) CloseClient() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseClient", reflect.TypeOf((*MockINamingProxy)(nil).CloseClient))
|
||||
}
|
||||
|
||||
// DeregisterInstance mocks base method.
|
||||
func (m *MockINamingProxy) DeregisterInstance(serviceName, groupName string, instance model.Instance) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeregisterInstance", serviceName, groupName, instance)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DeregisterInstance indicates an expected call of DeregisterInstance.
|
||||
func (mr *MockINamingProxyMockRecorder) DeregisterInstance(serviceName, groupName, instance interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeregisterInstance", reflect.TypeOf((*MockINamingProxy)(nil).DeregisterInstance), serviceName, groupName, instance)
|
||||
}
|
||||
|
||||
// GetServiceList mocks base method.
|
||||
func (m *MockINamingProxy) GetServiceList(pageNo, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetServiceList", pageNo, pageSize, groupName, selector)
|
||||
ret0, _ := ret[0].(model.ServiceList)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetServiceList indicates an expected call of GetServiceList.
|
||||
func (mr *MockINamingProxyMockRecorder) GetServiceList(pageNo, pageSize, groupName, selector interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceList", reflect.TypeOf((*MockINamingProxy)(nil).GetServiceList), pageNo, pageSize, groupName, selector)
|
||||
}
|
||||
|
||||
// QueryInstancesOfService mocks base method.
|
||||
func (m *MockINamingProxy) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "QueryInstancesOfService", serviceName, groupName, clusters, udpPort, healthyOnly)
|
||||
ret0, _ := ret[0].(*model.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// QueryInstancesOfService indicates an expected call of QueryInstancesOfService.
|
||||
func (mr *MockINamingProxyMockRecorder) QueryInstancesOfService(serviceName, groupName, clusters, udpPort, healthyOnly interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryInstancesOfService", reflect.TypeOf((*MockINamingProxy)(nil).QueryInstancesOfService), serviceName, groupName, clusters, udpPort, healthyOnly)
|
||||
}
|
||||
|
||||
// RegisterInstance mocks base method.
|
||||
func (m *MockINamingProxy) RegisterInstance(serviceName, groupName string, instance model.Instance) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RegisterInstance", serviceName, groupName, instance)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// RegisterInstance indicates an expected call of RegisterInstance.
|
||||
func (mr *MockINamingProxyMockRecorder) RegisterInstance(serviceName, groupName, instance interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterInstance", reflect.TypeOf((*MockINamingProxy)(nil).RegisterInstance), serviceName, groupName, instance)
|
||||
}
|
||||
|
||||
// ServerHealthy mocks base method.
|
||||
func (m *MockINamingProxy) ServerHealthy() bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ServerHealthy")
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ServerHealthy indicates an expected call of ServerHealthy.
|
||||
func (mr *MockINamingProxyMockRecorder) ServerHealthy() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerHealthy", reflect.TypeOf((*MockINamingProxy)(nil).ServerHealthy))
|
||||
}
|
||||
|
||||
// Subscribe mocks base method.
|
||||
func (m *MockINamingProxy) Subscribe(serviceName, groupName, clusters string) (model.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Subscribe", serviceName, groupName, clusters)
|
||||
ret0, _ := ret[0].(model.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Subscribe indicates an expected call of Subscribe.
|
||||
func (mr *MockINamingProxyMockRecorder) Subscribe(serviceName, groupName, clusters interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockINamingProxy)(nil).Subscribe), serviceName, groupName, clusters)
|
||||
}
|
||||
|
||||
// Unsubscribe mocks base method.
|
||||
func (m *MockINamingProxy) Unsubscribe(serviceName, groupName, clusters string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Unsubscribe", serviceName, groupName, clusters)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Unsubscribe indicates an expected call of Unsubscribe.
|
||||
func (mr *MockINamingProxyMockRecorder) Unsubscribe(serviceName, groupName, clusters interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockINamingProxy)(nil).Unsubscribe), serviceName, groupName, clusters)
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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_client
|
||||
|
||||
import (
|
||||
"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_http"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy"
|
||||
"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/nacos_server"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
// NamingProxyDelegate ...
|
||||
type NamingProxyDelegate struct {
|
||||
httpClientProxy *naming_http.NamingHttpProxy
|
||||
grpcClientProxy *naming_grpc.NamingGrpcProxy
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder
|
||||
}
|
||||
|
||||
func NewNamingProxyDelegate(clientCfg constant.ClientConfig, serverCfgs []constant.ServerConfig,
|
||||
httpAgent http_agent.IHttpAgent, serviceInfoHolder *naming_cache.ServiceInfoHolder) (naming_proxy.INamingProxy, error) {
|
||||
|
||||
nacosServer, err := nacos_server.NewNacosServer(serverCfgs, clientCfg, httpAgent, clientCfg.TimeoutMs, clientCfg.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpClientProxy, err := naming_http.NewNamingHttpProxy(clientCfg, nacosServer, serviceInfoHolder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grpcClientProxy, err := naming_grpc.NewNamingGrpcProxy(clientCfg, nacosServer, serviceInfoHolder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NamingProxyDelegate{
|
||||
httpClientProxy: httpClientProxy,
|
||||
grpcClientProxy: grpcClientProxy,
|
||||
serviceInfoHolder: serviceInfoHolder,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) getExecuteClientProxy(instance model.Instance) (namingProxy naming_proxy.INamingProxy) {
|
||||
if instance.Ephemeral {
|
||||
namingProxy = proxy.grpcClientProxy
|
||||
} else {
|
||||
namingProxy = proxy.httpClientProxy
|
||||
}
|
||||
return namingProxy
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
return proxy.getExecuteClientProxy(instance).RegisterInstance(serviceName, groupName, instance)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
|
||||
return proxy.getExecuteClientProxy(instance).DeregisterInstance(serviceName, groupName, instance)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
|
||||
return proxy.grpcClientProxy.GetServiceList(pageNo, pageSize, groupName, selector)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) ServerHealthy() bool {
|
||||
return proxy.grpcClientProxy.ServerHealthy() || proxy.httpClientProxy.ServerHealthy()
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
|
||||
return proxy.grpcClientProxy.QueryInstancesOfService(serviceName, groupName, clusters, udpPort, healthyOnly)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) Subscribe(serviceName, groupName string, clusters string) (model.Service, error) {
|
||||
serviceNameWithGroup := util.GetServiceCacheKey(util.GetGroupName(serviceName, groupName), clusters)
|
||||
serviceInfo, ok := proxy.serviceInfoHolder.ServiceInfoMap.Get(serviceNameWithGroup)
|
||||
if !ok {
|
||||
result, err := proxy.grpcClientProxy.Subscribe(serviceName, groupName, clusters)
|
||||
if err != nil {
|
||||
return model.Service{}, err
|
||||
}
|
||||
serviceInfo = result
|
||||
}
|
||||
service := serviceInfo.(model.Service)
|
||||
proxy.serviceInfoHolder.ProcessService(&service)
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) Unsubscribe(serviceName, groupName, clusters string) error {
|
||||
proxy.serviceInfoHolder.StopUpdateIfContain(util.GetGroupName(serviceName, groupName), clusters)
|
||||
return proxy.grpcClientProxy.Unsubscribe(serviceName, groupName, clusters)
|
||||
}
|
||||
|
||||
func (proxy *NamingProxyDelegate) CloseClient() {
|
||||
proxy.grpcClientProxy.CloseClient()
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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_client
|
||||
|
||||
import (
|
||||
"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_proxy"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
type ServiceInfoUpdater struct {
|
||||
serviceInfoHolder *naming_cache.ServiceInfoHolder
|
||||
updateThreadNum int
|
||||
namingProxy naming_proxy.INamingProxy
|
||||
}
|
||||
|
||||
func NewServiceInfoUpdater(serviceInfoHolder *naming_cache.ServiceInfoHolder, updateThreadNum int,
|
||||
namingProxy naming_proxy.INamingProxy) *ServiceInfoUpdater {
|
||||
|
||||
return &ServiceInfoUpdater{
|
||||
serviceInfoHolder: serviceInfoHolder,
|
||||
updateThreadNum: updateThreadNum,
|
||||
namingProxy: namingProxy,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceInfoUpdater) asyncUpdateService() {
|
||||
sema := util.NewSemaphore(s.updateThreadNum)
|
||||
for {
|
||||
for _, v := range s.serviceInfoHolder.ServiceInfoMap.Items() {
|
||||
service := v.(model.Service)
|
||||
lastRefTime, ok := s.serviceInfoHolder.UpdateTimeMap.Get(util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName),
|
||||
service.Clusters))
|
||||
if !ok {
|
||||
lastRefTime = uint64(0)
|
||||
}
|
||||
if uint64(util.CurrentMillis())-lastRefTime.(uint64) > service.CacheMillis {
|
||||
sema.Acquire()
|
||||
go func() {
|
||||
s.updateServiceNow(service.Name, service.GroupName, service.Clusters)
|
||||
sema.Release()
|
||||
}()
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceInfoUpdater) updateServiceNow(serviceName, groupName, clusters string) {
|
||||
result, err := s.namingProxy.QueryInstancesOfService(serviceName, groupName, clusters, 0, false)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("QueryList return error!serviceName:%s cluster:%s err:%+v", serviceName, clusters, err)
|
||||
return
|
||||
}
|
||||
s.serviceInfoHolder.ProcessService(result)
|
||||
}
|
|
@ -1,97 +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_client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/cache"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
)
|
||||
|
||||
type SubscribeCallback struct {
|
||||
callbackFuncsMap cache.ConcurrentMap
|
||||
}
|
||||
|
||||
func NewSubscribeCallback() SubscribeCallback {
|
||||
ed := SubscribeCallback{}
|
||||
ed.callbackFuncsMap = cache.NewConcurrentMap()
|
||||
return ed
|
||||
}
|
||||
|
||||
func (ed *SubscribeCallback) AddCallbackFuncs(serviceName string, clusters string, callbackFunc *func(services []model.SubscribeService, err error)) {
|
||||
logger.Info("adding " + serviceName + " with " + clusters + " to listener map")
|
||||
key := util.GetServiceCacheKey(serviceName, clusters)
|
||||
var funcs []*func(services []model.SubscribeService, err error)
|
||||
old, ok := ed.callbackFuncsMap.Get(key)
|
||||
if ok {
|
||||
funcs = append(funcs, old.([]*func(services []model.SubscribeService, err error))...)
|
||||
}
|
||||
funcs = append(funcs, callbackFunc)
|
||||
ed.callbackFuncsMap.Set(key, funcs)
|
||||
}
|
||||
|
||||
func (ed *SubscribeCallback) RemoveCallbackFuncs(serviceName string, clusters string, callbackFunc *func(services []model.SubscribeService, err error)) {
|
||||
logger.Info("removing " + serviceName + " with " + clusters + " to listener map")
|
||||
key := util.GetServiceCacheKey(serviceName, clusters)
|
||||
funcs, ok := ed.callbackFuncsMap.Get(key)
|
||||
if ok && funcs != nil {
|
||||
var newFuncs []*func(services []model.SubscribeService, err error)
|
||||
for _, funcItem := range funcs.([]*func(services []model.SubscribeService, err error)) {
|
||||
if funcItem != callbackFunc {
|
||||
newFuncs = append(newFuncs, funcItem)
|
||||
}
|
||||
}
|
||||
ed.callbackFuncsMap.Set(key, newFuncs)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (ed *SubscribeCallback) ServiceChanged(service *model.Service) {
|
||||
if service == nil || service.Name == "" {
|
||||
return
|
||||
}
|
||||
key := util.GetServiceCacheKey(service.Name, service.Clusters)
|
||||
funcs, ok := ed.callbackFuncsMap.Get(key)
|
||||
if ok {
|
||||
for _, funcItem := range funcs.([]*func(services []model.SubscribeService, err error)) {
|
||||
var subscribeServices []model.SubscribeService
|
||||
if len(service.Hosts) == 0 {
|
||||
(*funcItem)(subscribeServices, errors.New("[client.Subscribe] subscribe failed,hosts is empty"))
|
||||
return
|
||||
}
|
||||
for _, host := range service.Hosts {
|
||||
subscribeService := model.SubscribeService{
|
||||
Valid: host.Valid,
|
||||
Port: host.Port,
|
||||
Ip: host.Ip,
|
||||
Metadata: host.Metadata,
|
||||
ServiceName: host.ServiceName,
|
||||
ClusterName: host.ClusterName,
|
||||
Weight: host.Weight,
|
||||
InstanceId: host.InstanceId,
|
||||
Enable: host.Enable,
|
||||
Healthy: host.Healthy,
|
||||
}
|
||||
subscribeServices = append(subscribeServices, subscribeService)
|
||||
}
|
||||
(*funcItem)(subscribeServices, nil)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,9 +20,7 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/file"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/file"
|
||||
)
|
||||
|
||||
func NewClientConfig(opts ...ClientOption) *ClientConfig {
|
||||
|
@ -48,13 +46,6 @@ func NewClientConfig(opts ...ClientOption) *ClientConfig {
|
|||
// ClientOption ...
|
||||
type ClientOption func(*ClientConfig)
|
||||
|
||||
// WithCustomLogger ...
|
||||
func WithCustomLogger(logger logger.Logger) ClientOption {
|
||||
return func(config *ClientConfig) {
|
||||
config.CustomLogger = logger
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimeoutMs ...
|
||||
func WithTimeoutMs(timeoutMs uint64) ClientOption {
|
||||
return func(config *ClientConfig) {
|
||||
|
@ -62,6 +53,13 @@ func WithTimeoutMs(timeoutMs uint64) ClientOption {
|
|||
}
|
||||
}
|
||||
|
||||
// WithAppName ...
|
||||
func WithAppName(appName string) ClientOption {
|
||||
return func(config *ClientConfig) {
|
||||
config.AppName = appName
|
||||
}
|
||||
}
|
||||
|
||||
// WithBeatInterval ...
|
||||
func WithBeatInterval(beatInterval int64) ClientOption {
|
||||
return func(config *ClientConfig) {
|
||||
|
@ -170,20 +168,19 @@ func WithLogLevel(logLevel string) ClientOption {
|
|||
// WithLogSampling ...
|
||||
func WithLogSampling(tick time.Duration, initial int, thereafter int) ClientOption {
|
||||
return func(config *ClientConfig) {
|
||||
config.LogSampling = &logger.SamplingConfig{Initial: initial, Thereafter: thereafter, Tick: tick}
|
||||
config.LogSampling = &ClientLogSamplingConfig{initial, thereafter, tick}
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogRollingConfig ...
|
||||
func WithLogRollingConfig(rollingConfig *lumberjack.Logger) ClientOption {
|
||||
func WithLogRollingConfig(rollingConfig *ClientLogRollingConfig) ClientOption {
|
||||
return func(config *ClientConfig) {
|
||||
config.LogRollingConfig = rollingConfig
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogStdout ...
|
||||
func WithLogStdout(logStdout bool) ClientOption {
|
||||
func WithTLS(tlsCfg TLSConfig) ClientOption {
|
||||
return func(config *ClientConfig) {
|
||||
config.LogStdout = logStdout
|
||||
config.TLSCfg = tlsCfg
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@ package constant
|
|||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/file"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/file"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -49,7 +48,6 @@ func TestNewClientConfig(t *testing.T) {
|
|||
assert.Equal(t, config.RegionId, "")
|
||||
assert.Equal(t, config.AccessKey, "")
|
||||
assert.Equal(t, config.SecretKey, "")
|
||||
assert.Nil(t, config.LogSampling)
|
||||
}
|
||||
|
||||
func TestNewClientConfigWithOptions(t *testing.T) {
|
||||
|
@ -73,8 +71,6 @@ func TestNewClientConfigWithOptions(t *testing.T) {
|
|||
WithNamespaceId("namespace_1"),
|
||||
WithAccessKey("accessKey_1"),
|
||||
WithSecretKey("secretKey_1"),
|
||||
|
||||
WithLogSampling(time.Second*10, 5, 10),
|
||||
)
|
||||
|
||||
assert.Equal(t, config.TimeoutMs, uint64(20000))
|
||||
|
@ -96,8 +92,4 @@ func TestNewClientConfigWithOptions(t *testing.T) {
|
|||
assert.Equal(t, config.NamespaceId, "namespace_1")
|
||||
assert.Equal(t, config.AccessKey, "accessKey_1")
|
||||
assert.Equal(t, config.SecretKey, "secretKey_1")
|
||||
|
||||
assert.Equal(t, config.LogSampling.Tick, time.Second*10)
|
||||
assert.Equal(t, config.LogSampling.Initial, 5)
|
||||
assert.Equal(t, config.LogSampling.Thereafter, 10)
|
||||
}
|
||||
|
|
|
@ -16,17 +16,14 @@
|
|||
|
||||
package constant
|
||||
|
||||
import (
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
import "time"
|
||||
|
||||
type ServerConfig struct {
|
||||
Scheme string //the nacos server scheme
|
||||
ContextPath string //the nacos server contextpath
|
||||
Scheme string // the nacos server scheme,default=http,this is not required in 2.0
|
||||
ContextPath string // the nacos server contextpath,default=/nacos,this is not required in 2.0
|
||||
IpAddr string // the nacos server address
|
||||
Port uint64 //the 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 {
|
||||
|
@ -35,6 +32,7 @@ type ClientConfig struct {
|
|||
BeatInterval int64 // the time interval for sending beat to server,default value is 5000ms
|
||||
NamespaceId string // the namespaceId of Nacos.When namespace is public, fill in the blank string here.
|
||||
AppName string // the appName
|
||||
AppKey string // the client identity information
|
||||
Endpoint string // the endpoint for get Nacos server addresses
|
||||
RegionId string // the regionId for kms
|
||||
AccessKey string // the AccessKey for kms
|
||||
|
@ -48,9 +46,50 @@ type ClientConfig struct {
|
|||
Password string // the password for nacos auth
|
||||
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
|
||||
LogSampling *logger.SamplingConfig // the sampling config of log
|
||||
ContextPath string // the nacos server contextpath
|
||||
LogRollingConfig *lumberjack.Logger // the log rolling config
|
||||
CustomLogger logger.Logger // the custom log interface ,With a custom Logger (nacos sdk will not provide log cutting and archiving capabilities)
|
||||
LogStdout bool // the stdout redirect for log, default is false
|
||||
AppendToStdout bool // if append log to stdout
|
||||
LogSampling *ClientLogSamplingConfig // the sampling config of log
|
||||
LogRollingConfig *ClientLogRollingConfig // log rolling config
|
||||
TLSCfg TLSConfig // tls Config
|
||||
}
|
||||
|
||||
type ClientLogSamplingConfig struct {
|
||||
Initial int //the sampling initial of log
|
||||
Thereafter int //the sampling thereafter of log
|
||||
Tick time.Duration //the sampling tick of log
|
||||
}
|
||||
|
||||
type ClientLogRollingConfig struct {
|
||||
// MaxSize is the maximum size in megabytes of the log file before it gets
|
||||
// rotated. It defaults to 100 megabytes.
|
||||
MaxSize int
|
||||
|
||||
// MaxAge is the maximum number of days to retain old log files based on the
|
||||
// timestamp encoded in their filename. Note that a day is defined as 24
|
||||
// hours and may not exactly correspond to calendar days due to daylight
|
||||
// savings, leap seconds, etc. The default is not to remove old log files
|
||||
// based on age.
|
||||
MaxAge int
|
||||
|
||||
// MaxBackups is the maximum number of old log files to retain. The default
|
||||
// is to retain all old log files (though MaxAge may still cause them to get
|
||||
// deleted.)
|
||||
MaxBackups int
|
||||
|
||||
// LocalTime determines if the time used for formatting the timestamps in
|
||||
// backup files is the computer's local time. The default is to use UTC
|
||||
// time.
|
||||
LocalTime bool
|
||||
|
||||
// Compress determines if the rotated log files should be compressed
|
||||
// using gzip. The default is not to perform compression.
|
||||
Compress bool
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
Enable bool // enable tls
|
||||
CaFile string // clients use when verifying server certificates
|
||||
CertFile string // server use when verifying client certificates
|
||||
KeyFile string // server use when verifying client certificates
|
||||
ServerNameOverride string // serverNameOverride is for testing only
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package constant
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
KEY_USERNAME = "username"
|
||||
KEY_PASSWORD = "password"
|
||||
|
@ -66,7 +68,7 @@ const (
|
|||
KEY_BEAT = "beat"
|
||||
KEY_DOM = "dom"
|
||||
DEFAULT_CONTEXT_PATH = "/nacos"
|
||||
CLIENT_VERSION = "Nacos-Go-Client:v1.0.1"
|
||||
CLIENT_VERSION = "Nacos-Go-Client:v2.0.0"
|
||||
REQUEST_DOMAIN_RETRY_TIME = 3
|
||||
SERVICE_INFO_SPLITER = "@@"
|
||||
CONFIG_INFO_SPLITER = "@@"
|
||||
|
@ -75,7 +77,24 @@ const (
|
|||
NAMING_INSTANCE_ID_SPLITTER = "#"
|
||||
DefaultClientErrorCode = "SDK.NacosError"
|
||||
DEFAULT_SERVER_SCHEME = "http"
|
||||
WINDOWS_LEGAL_NAME_SPLITER = "&&"
|
||||
OS_WINDOWS = "windows"
|
||||
HTTPS_SERVER_SCHEME = "https"
|
||||
LABEL_SOURCE = "source"
|
||||
LABEL_SOURCE_SDK = "sdk"
|
||||
LABEL_MODULE = "module"
|
||||
LABEL_MODULE_CONFIG = "config"
|
||||
LABEL_MODULE_NAMING = "naming"
|
||||
RESPONSE_CODE_SUCCESS = 200
|
||||
UN_REGISTER = 301
|
||||
KEEP_ALIVE_TIME = 5
|
||||
DEFAULT_TIMEOUT_MILLS = 3000
|
||||
ALL_SYNC_INTERNAL = 5 * time.Minute
|
||||
CLIENT_APPNAME_HEADER = "Client-AppName"
|
||||
CLIENT_REQUEST_TS_HEADER = "Client-RequestTS"
|
||||
CLIENT_REQUEST_TOKEN_HEADER = "Client-RequestToken"
|
||||
EX_CONFIG_INFO = "exConfigInfo"
|
||||
CHARSET_KEY = "charset"
|
||||
LOG_FILE_NAME = "nacos-sdk.log"
|
||||
HTTPS_SERVER_PORT = 443
|
||||
GRPC = "grpc"
|
||||
FAILOVER_FILE_SUFFIX = "_failover"
|
||||
)
|
||||
|
|
|
@ -61,3 +61,10 @@ func WithPort(port uint64) ServerOption {
|
|||
config.Port = port
|
||||
}
|
||||
}
|
||||
|
||||
//WithGrpcPort set grpc port for server
|
||||
func WithGrpcPort(port uint64) ServerOption {
|
||||
return func(config *ServerConfig) {
|
||||
config.GrpcPort = port
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 constant
|
||||
|
||||
var SkipVerifyConfig = TLSConfig{Enable: true}
|
||||
|
||||
func NewTLSConfig(opts ...TLSOption) *TLSConfig {
|
||||
tlsConfig := TLSConfig{Enable: true}
|
||||
for _, opt := range opts {
|
||||
opt(&tlsConfig)
|
||||
}
|
||||
return &tlsConfig
|
||||
}
|
||||
|
||||
type TLSOption func(*TLSConfig)
|
||||
|
||||
func WithCA(caFile, serverNameOverride string) TLSOption {
|
||||
return func(tc *TLSConfig) {
|
||||
tc.CaFile = caFile
|
||||
tc.ServerNameOverride = serverNameOverride
|
||||
}
|
||||
}
|
||||
|
||||
func WithCertificate(certFile, keyFile string) TLSOption {
|
||||
return func(tc *TLSConfig) {
|
||||
tc.CertFile = certFile
|
||||
tc.KeyFile = keyFile
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package constant
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewTLSConfigWithOptions(t *testing.T) {
|
||||
t.Run("TestNoOption", func(t *testing.T) {
|
||||
cfg := SkipVerifyConfig
|
||||
assert.Equal(t, "", cfg.CaFile)
|
||||
assert.Equal(t, "", cfg.CertFile)
|
||||
assert.Equal(t, "", cfg.KeyFile)
|
||||
assert.Equal(t, "", cfg.ServerNameOverride)
|
||||
})
|
||||
|
||||
t.Run("TestCAOption", func(t *testing.T) {
|
||||
cfg := NewTLSConfig(
|
||||
WithCA("ca", "host"),
|
||||
)
|
||||
assert.Equal(t, "ca", cfg.CaFile)
|
||||
assert.Equal(t, "", cfg.CertFile)
|
||||
assert.Equal(t, "", cfg.KeyFile)
|
||||
assert.Equal(t, "host", cfg.ServerNameOverride)
|
||||
})
|
||||
|
||||
t.Run("TestCertOption", func(t *testing.T) {
|
||||
cfg := NewTLSConfig(
|
||||
WithCA("ca", "host"),
|
||||
WithCertificate("cert", "key"),
|
||||
)
|
||||
assert.Equal(t, "ca", cfg.CaFile)
|
||||
assert.Equal(t, "cert", cfg.CertFile)
|
||||
assert.Equal(t, "key", cfg.KeyFile)
|
||||
assert.Equal(t, "host", cfg.ServerNameOverride)
|
||||
})
|
||||
}
|
|
@ -21,6 +21,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var osType string
|
||||
|
@ -38,16 +39,51 @@ func init() {
|
|||
}
|
||||
|
||||
func MkdirIfNecessary(createDir string) (err error) {
|
||||
return os.MkdirAll(createDir, os.ModePerm)
|
||||
s := strings.Split(createDir, path)
|
||||
startIndex := 0
|
||||
dir := ""
|
||||
if s[0] == "" {
|
||||
startIndex = 1
|
||||
} else {
|
||||
dir, _ = os.Getwd() //当前的目录
|
||||
}
|
||||
for i := startIndex; i < len(s); i++ {
|
||||
var d string
|
||||
if osType == WINDOWS && filepath.IsAbs(createDir) {
|
||||
d = strings.Join(s[startIndex:i+1], path)
|
||||
} else {
|
||||
d = dir + path + strings.Join(s[startIndex:i+1], path)
|
||||
}
|
||||
if _, e := os.Stat(d); os.IsNotExist(e) {
|
||||
err = os.Mkdir(d, os.ModePerm) //在当前目录下生成md目录
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetCurrentPath() string {
|
||||
dir, err := os.Getwd() //当前的目录
|
||||
if err != nil {
|
||||
dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
|
||||
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
if err != nil {
|
||||
log.Println("can not get current path")
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func delete(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 !strings.HasSuffix(path, "?") {
|
||||
path = path + "?"
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ func delete(path string, header http.Header, timeoutMs uint64, params map[string
|
|||
if strings.HasSuffix(path, "&") {
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
client := http.Client{}
|
||||
client.Timeout = time.Millisecond * time.Duration(timeoutMs)
|
||||
request, errNew := http.NewRequest(http.MethodDelete, path, nil)
|
||||
if errNew != nil {
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func get(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.HasSuffix(path, "?") {
|
||||
path = path + "?"
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ func get(path string, header http.Header, timeoutMs uint64, params map[string]st
|
|||
path = path[:len(path)-1]
|
||||
}
|
||||
|
||||
client := http.Client{}
|
||||
client.Timeout = time.Millisecond * time.Duration(timeoutMs)
|
||||
request, errNew := http.NewRequest(http.MethodGet, path, nil)
|
||||
if errNew != nil {
|
||||
|
|
|
@ -20,17 +20,25 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"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/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type HttpAgent struct {
|
||||
TlsConfig constant.TLSConfig
|
||||
}
|
||||
|
||||
func (agent *HttpAgent) Get(path string, header http.Header, timeoutMs uint64,
|
||||
params map[string]string) (response *http.Response, err error) {
|
||||
return get(path, header, timeoutMs, params)
|
||||
client, err := agent.createClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return get(client, path, header, timeoutMs, params)
|
||||
}
|
||||
|
||||
func (agent *HttpAgent) RequestOnlyResult(method string, path string, header http.Header, timeoutMs uint64, params map[string]string) string {
|
||||
|
@ -56,7 +64,7 @@ func (agent *HttpAgent) RequestOnlyResult(method string, path string, header htt
|
|||
logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],err:%+v", method, path, util.ToJsonString(header), util.ToJsonString(params), err)
|
||||
return ""
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
if response.StatusCode != constant.RESPONSE_CODE_SUCCESS {
|
||||
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 ""
|
||||
}
|
||||
|
@ -92,13 +100,37 @@ func (agent *HttpAgent) Request(method string, path string, header http.Header,
|
|||
}
|
||||
func (agent *HttpAgent) Post(path string, header http.Header, timeoutMs uint64,
|
||||
params map[string]string) (response *http.Response, err error) {
|
||||
return post(path, header, timeoutMs, params)
|
||||
client, err := agent.createClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return post(client, path, header, timeoutMs, params)
|
||||
}
|
||||
func (agent *HttpAgent) Delete(path string, header http.Header, timeoutMs uint64,
|
||||
params map[string]string) (response *http.Response, err error) {
|
||||
return delete(path, header, timeoutMs, params)
|
||||
client, err := agent.createClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delete(client, path, header, timeoutMs, params)
|
||||
}
|
||||
func (agent *HttpAgent) Put(path string, header http.Header, timeoutMs uint64,
|
||||
params map[string]string) (response *http.Response, err error) {
|
||||
return put(path, header, timeoutMs, params)
|
||||
client, err := agent.createClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return put(client, path, header, timeoutMs, params)
|
||||
}
|
||||
|
||||
func (agent *HttpAgent) createClient() (*http.Client, error) {
|
||||
if !agent.TlsConfig.Enable {
|
||||
return &http.Client{}, nil
|
||||
}
|
||||
cfg, err := tls.NewTLS(agent.TlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &http.Client{Transport: &http.Transport{TLSClientConfig: cfg}}, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -20,11 +20,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
func post(path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {
|
||||
client := http.Client{}
|
||||
func post(client *http.Client, path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {
|
||||
client.Timeout = time.Millisecond * time.Duration(timeoutMs)
|
||||
|
||||
body := util.GetUrlFormedMap(params)
|
||||
|
|
|
@ -22,8 +22,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func put(path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {
|
||||
client := http.Client{}
|
||||
func put(client *http.Client, path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {
|
||||
client.Timeout = time.Millisecond * time.Duration(timeoutMs)
|
||||
var body string
|
||||
for key, value := range params {
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
|
@ -40,12 +41,9 @@ var levelMap = map[string]zapcore.Level{
|
|||
|
||||
type Config struct {
|
||||
Level string
|
||||
LogFileName string
|
||||
Sampling *SamplingConfig
|
||||
AppendToStdout bool
|
||||
LogRollingConfig *lumberjack.Logger
|
||||
LogDir string
|
||||
CustomLogger Logger
|
||||
LogStdout bool
|
||||
}
|
||||
|
||||
type SamplingConfig struct {
|
||||
|
@ -86,35 +84,53 @@ func init() {
|
|||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
}
|
||||
zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig
|
||||
defaultLogger, _ := zapLoggerConfig.Build(zap.AddCaller(), zap.AddCallerSkip(1))
|
||||
setLogger(&NacosLogger{defaultLogger.Sugar()})
|
||||
zapLogger, _ := zapLoggerConfig.Build(zap.AddCaller(), zap.AddCallerSkip(1))
|
||||
SetLogger(&NacosLogger{zapLogger.Sugar()})
|
||||
}
|
||||
|
||||
func BuildLoggerConfig(clientConfig constant.ClientConfig) Config {
|
||||
loggerConfig := Config{
|
||||
Level: clientConfig.LogLevel,
|
||||
AppendToStdout: clientConfig.AppendToStdout,
|
||||
}
|
||||
if clientConfig.LogSampling != nil {
|
||||
loggerConfig.Sampling = &SamplingConfig{
|
||||
Initial: clientConfig.LogSampling.Initial,
|
||||
Thereafter: clientConfig.LogSampling.Thereafter,
|
||||
Tick: clientConfig.LogSampling.Tick,
|
||||
}
|
||||
}
|
||||
loggerConfig.LogRollingConfig = &lumberjack.Logger{
|
||||
Filename: clientConfig.LogDir + string(os.PathSeparator) + constant.LOG_FILE_NAME,
|
||||
}
|
||||
logRollingConfig := clientConfig.LogRollingConfig
|
||||
if logRollingConfig != nil {
|
||||
loggerConfig.LogRollingConfig.MaxSize = logRollingConfig.MaxSize
|
||||
loggerConfig.LogRollingConfig.MaxAge = logRollingConfig.MaxAge
|
||||
loggerConfig.LogRollingConfig.MaxBackups = logRollingConfig.MaxBackups
|
||||
loggerConfig.LogRollingConfig.LocalTime = logRollingConfig.LocalTime
|
||||
loggerConfig.LogRollingConfig.Compress = logRollingConfig.Compress
|
||||
}
|
||||
return loggerConfig
|
||||
}
|
||||
|
||||
// InitLogger is init global logger for nacos
|
||||
func InitLogger(config Config) (err error) {
|
||||
l, err := initNacosLogger(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setLogger(l)
|
||||
logLock.Lock()
|
||||
defer logLock.Unlock()
|
||||
logger, err = InitNacosLogger(config)
|
||||
return
|
||||
}
|
||||
|
||||
// InitNacosLogger is init nacos default logger
|
||||
func initNacosLogger(config Config) (Logger, error) {
|
||||
if config.CustomLogger != nil {
|
||||
return &NacosLogger{config.CustomLogger}, nil
|
||||
}
|
||||
func InitNacosLogger(config Config) (Logger, error) {
|
||||
logLevel := getLogLevel(config.Level)
|
||||
encoder := getEncoder()
|
||||
writer := config.getLogWriter()
|
||||
|
||||
core := zapcore.NewCore(zapcore.NewConsoleEncoder(encoder), writer, logLevel)
|
||||
|
||||
if config.Sampling != nil {
|
||||
core = zapcore.NewSamplerWithOptions(core, config.Sampling.Tick, config.Sampling.Initial, config.Sampling.Thereafter)
|
||||
if config.AppendToStdout {
|
||||
writer = zapcore.NewMultiWriteSyncer(writer, zapcore.AddSync(os.Stdout))
|
||||
}
|
||||
|
||||
core := zapcore.NewCore(zapcore.NewConsoleEncoder(encoder), writer, logLevel)
|
||||
zaplogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
|
||||
return &NacosLogger{zaplogger.Sugar()}, nil
|
||||
}
|
||||
|
@ -134,7 +150,7 @@ func getEncoder() zapcore.EncoderConfig {
|
|||
CallerKey: "caller",
|
||||
MessageKey: "message",
|
||||
StacktraceKey: "stacktrace",
|
||||
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
|
@ -142,7 +158,7 @@ func getEncoder() zapcore.EncoderConfig {
|
|||
}
|
||||
|
||||
//SetLogger sets logger for sdk
|
||||
func setLogger(log Logger) {
|
||||
func SetLogger(log Logger) {
|
||||
logLock.Lock()
|
||||
defer logLock.Unlock()
|
||||
logger = log
|
||||
|
@ -156,12 +172,5 @@ func GetLogger() Logger {
|
|||
|
||||
// getLogWriter get Lumberjack writer by LumberjackConfig
|
||||
func (c *Config) getLogWriter() zapcore.WriteSyncer {
|
||||
if c.LogRollingConfig == nil {
|
||||
c.LogRollingConfig = &lumberjack.Logger{}
|
||||
}
|
||||
c.LogRollingConfig.Filename = c.LogDir + string(os.PathSeparator) + c.LogFileName
|
||||
if c.LogStdout {
|
||||
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(c.LogRollingConfig), zapcore.AddSync(os.Stdout))
|
||||
}
|
||||
return zapcore.AddSync(c.LogRollingConfig)
|
||||
}
|
||||
|
|
|
@ -24,12 +24,12 @@ import (
|
|||
)
|
||||
|
||||
func reset() {
|
||||
setLogger(nil)
|
||||
SetLogger(nil)
|
||||
}
|
||||
|
||||
func TestInitLogger(t *testing.T) {
|
||||
config := Config{
|
||||
Level: "debug",
|
||||
Level: "degug",
|
||||
}
|
||||
err := InitLogger(config)
|
||||
assert.NoError(t, err)
|
||||
|
@ -40,7 +40,7 @@ func TestGetLogger(t *testing.T) {
|
|||
// not yet init get default log
|
||||
log := GetLogger()
|
||||
config := Config{
|
||||
Level: "debug",
|
||||
Level: "degug",
|
||||
}
|
||||
_ = InitLogger(config)
|
||||
// after init logger
|
||||
|
@ -59,7 +59,7 @@ func TestSetLogger(t *testing.T) {
|
|||
// not yet init get default log
|
||||
log := GetLogger()
|
||||
log1 := &mockLogger{}
|
||||
setLogger(log1)
|
||||
SetLogger(log1)
|
||||
|
||||
// after set logger
|
||||
log2 := GetLogger()
|
||||
|
@ -82,7 +82,7 @@ func TestRaceLogger(t *testing.T) {
|
|||
wg.Add(3)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
setLogger(&mockLogger{})
|
||||
SetLogger(&mockLogger{})
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
@ -91,7 +91,7 @@ func TestRaceLogger(t *testing.T) {
|
|||
go func() {
|
||||
defer wg.Done()
|
||||
config := Config{
|
||||
Level: "debug",
|
||||
Level: "degug",
|
||||
}
|
||||
_ = InitLogger(config)
|
||||
}()
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
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)
|
||||
})
|
||||
}
|
|
@ -19,7 +19,7 @@ package nacos_error
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
)
|
||||
|
||||
type NacosError struct {
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
|
@ -29,15 +28,22 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/nacos_error"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/security"
|
||||
"github.com/nacos-group/nacos-sdk-go/inner/uuid"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"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/monitor"
|
||||
|
||||
"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/common/nacos_error"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
type NacosServer struct {
|
||||
|
@ -50,10 +56,13 @@ type NacosServer struct {
|
|||
lastSrvRefTime int64
|
||||
vipSrvRefInterMills int64
|
||||
contextPath string
|
||||
currentIndex int32
|
||||
ServerSrcChangeSignal chan struct{}
|
||||
}
|
||||
|
||||
func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.ClientConfig, httpAgent http_agent.IHttpAgent, timeoutMs uint64, endpoint string) (*NacosServer, error) {
|
||||
if len(serverList) == 0 && endpoint == "" {
|
||||
severLen := len(serverList)
|
||||
if severLen == 0 && endpoint == "" {
|
||||
return &NacosServer{}, errors.New("both serverlist and endpoint are empty")
|
||||
}
|
||||
|
||||
|
@ -67,12 +76,17 @@ func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.Clien
|
|||
endpoint: endpoint,
|
||||
vipSrvRefInterMills: 10000,
|
||||
contextPath: clientCfg.ContextPath,
|
||||
ServerSrcChangeSignal: make(chan struct{}, 1),
|
||||
}
|
||||
if severLen > 0 {
|
||||
ns.currentIndex = rand.Int31n(int32(severLen))
|
||||
}
|
||||
|
||||
ns.initRefreshSrvIfNeed()
|
||||
_, err := securityLogin.Login()
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("login has error %+v", err)
|
||||
return &ns, err
|
||||
}
|
||||
|
||||
securityLogin.AutoRefresh()
|
||||
|
@ -81,11 +95,12 @@ func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.Clien
|
|||
|
||||
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) {
|
||||
start := time.Now()
|
||||
if contextPath == "" {
|
||||
contextPath = constant.WEB_CONTEXT
|
||||
}
|
||||
|
||||
signHeaders := getSignHeaders(params, newHeaders)
|
||||
signHeaders := GetSignHeaders(params, newHeaders["secretKey"])
|
||||
|
||||
url := curServer + contextPath + api
|
||||
|
||||
|
@ -105,15 +120,15 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
|
|||
return
|
||||
}
|
||||
headers["RequestId"] = []string{uid.String()}
|
||||
headers["Request-Module"] = []string{"Naming"}
|
||||
headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"}
|
||||
headers["Spas-AccessKey"] = []string{newHeaders["accessKey"]}
|
||||
headers["Timestamp"] = []string{signHeaders["timeStamp"]}
|
||||
headers["Timestamp"] = []string{signHeaders["Timestamp"]}
|
||||
headers["Spas-Signature"] = []string{signHeaders["Spas-Signature"]}
|
||||
injectSecurityInfo(server, params)
|
||||
server.InjectSecurityInfo(params)
|
||||
|
||||
var response *http.Response
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -124,7 +139,7 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
|
|||
return
|
||||
}
|
||||
result = string(bytes)
|
||||
if response.StatusCode == 200 {
|
||||
if response.StatusCode == constant.RESPONSE_CODE_SUCCESS {
|
||||
return
|
||||
} else {
|
||||
err = nacos_error.NewNacosError(strconv.Itoa(response.StatusCode), string(bytes), nil)
|
||||
|
@ -133,6 +148,7 @@ 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) {
|
||||
start := time.Now()
|
||||
if contextPath == "" {
|
||||
contextPath = constant.WEB_CONTEXT
|
||||
}
|
||||
|
@ -152,7 +168,7 @@ func (server *NacosServer) callServer(api string, params map[string]string, meth
|
|||
headers["Request-Module"] = []string{"Naming"}
|
||||
headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"}
|
||||
|
||||
injectSecurityInfo(server, params)
|
||||
server.InjectSecurityInfo(params)
|
||||
|
||||
var response *http.Response
|
||||
response, err = server.httpAgent.Request(method, url, headers, server.timeoutMs, params)
|
||||
|
@ -166,7 +182,8 @@ func (server *NacosServer) callServer(api string, params map[string]string, meth
|
|||
return
|
||||
}
|
||||
result = string(bytes)
|
||||
if response.StatusCode == 200 {
|
||||
monitor.GetNamingRequestMonitor(method, api, util.GetStatusCode(response)).Observe(float64(time.Now().Nanosecond() - start.Nanosecond()))
|
||||
if response.StatusCode == constant.RESPONSE_CODE_SUCCESS {
|
||||
return
|
||||
} else {
|
||||
err = errors.New(fmt.Sprintf("request return error code %d", response.StatusCode))
|
||||
|
@ -180,7 +197,7 @@ func (server *NacosServer) ReqConfigApi(api string, params map[string]string, he
|
|||
return "", errors.New("server list is empty")
|
||||
}
|
||||
|
||||
injectSecurityInfo(server, params)
|
||||
server.InjectSecurityInfo(params)
|
||||
|
||||
//only one server,retry request when error
|
||||
var err error
|
||||
|
@ -193,7 +210,6 @@ 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)
|
||||
}
|
||||
return "", err
|
||||
} else {
|
||||
index := rand.Intn(len(srvs))
|
||||
for i := 1; i <= len(srvs); i++ {
|
||||
|
@ -205,8 +221,8 @@ 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)
|
||||
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) (string, error) {
|
||||
|
@ -214,13 +230,12 @@ func (server *NacosServer) ReqApi(api string, params map[string]string, method s
|
|||
if srvs == nil || len(srvs) == 0 {
|
||||
return "", errors.New("server list is empty")
|
||||
}
|
||||
var (
|
||||
result string
|
||||
err error
|
||||
)
|
||||
injectSecurityInfo(server, params)
|
||||
|
||||
server.InjectSecurityInfo(params)
|
||||
|
||||
//only one server,retry request when error
|
||||
var err error
|
||||
var result string
|
||||
if len(srvs) == 1 {
|
||||
for i := 0; i < constant.REQUEST_DOMAIN_RETRY_TIME; i++ {
|
||||
result, err = server.callServer(api, params, method, getAddress(srvs[0]), srvs[0].ContextPath)
|
||||
|
@ -241,7 +256,7 @@ func (server *NacosServer) ReqApi(api string, params map[string]string, method s
|
|||
index = (index + i) % len(srvs)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("retry%stimes request failed,err=%v", strconv.Itoa(constant.REQUEST_DOMAIN_RETRY_TIME), err)
|
||||
return "", errors.Wrapf(err, "retry %d times request failed!", constant.REQUEST_DOMAIN_RETRY_TIME)
|
||||
}
|
||||
|
||||
func (server *NacosServer) initRefreshSrvIfNeed() {
|
||||
|
@ -259,7 +274,7 @@ func (server *NacosServer) initRefreshSrvIfNeed() {
|
|||
}
|
||||
|
||||
func (server *NacosServer) refreshServerSrvIfNeed() {
|
||||
if len(server.serverList) > 0 || util.CurrentMillis()-server.lastSrvRefTime < server.vipSrvRefInterMills {
|
||||
if util.CurrentMillis()-server.lastSrvRefTime < server.vipSrvRefInterMills && len(server.serverList) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -295,6 +310,7 @@ func (server *NacosServer) refreshServerSrvIfNeed() {
|
|||
server.Lock()
|
||||
logger.Infof("server list is updated, old: <%v>,new:<%v>", server.serverList, servers)
|
||||
server.serverList = servers
|
||||
server.ServerSrcChangeSignal <- struct{}{}
|
||||
server.lastSrvRefTime = util.CurrentMillis()
|
||||
server.Unlock()
|
||||
}
|
||||
|
@ -307,13 +323,27 @@ func (server *NacosServer) GetServerList() []constant.ServerConfig {
|
|||
return server.serverList
|
||||
}
|
||||
|
||||
func injectSecurityInfo(server *NacosServer, param map[string]string) {
|
||||
func (server *NacosServer) InjectSecurityInfo(param map[string]string) {
|
||||
accessToken := server.securityLogin.GetAccessToken()
|
||||
if accessToken != "" {
|
||||
param[constant.KEY_ACCESS_TOKEN] = accessToken
|
||||
}
|
||||
}
|
||||
|
||||
func (server *NacosServer) InjectSign(request rpc_request.IRequest, param map[string]string, clientConfig constant.ClientConfig) {
|
||||
if clientConfig.AccessKey == "" || clientConfig.SecretKey == "" {
|
||||
return
|
||||
}
|
||||
sts := request.GetStringToSign()
|
||||
if sts == "" {
|
||||
return
|
||||
}
|
||||
signature := signWithhmacSHA1Encrypt(sts, clientConfig.SecretKey)
|
||||
param["data"] = sts
|
||||
param["signature"] = signature
|
||||
param["ak"] = clientConfig.AccessKey
|
||||
}
|
||||
|
||||
func getAddress(cfg constant.ServerConfig) string {
|
||||
if strings.Index(cfg.IpAddr, "http://") >= 0 || strings.Index(cfg.IpAddr, "https://") >= 0 {
|
||||
return cfg.IpAddr + ":" + strconv.Itoa(int(cfg.Port))
|
||||
|
@ -321,7 +351,34 @@ func getAddress(cfg constant.ServerConfig) string {
|
|||
return cfg.Scheme + "://" + cfg.IpAddr + ":" + strconv.Itoa(int(cfg.Port))
|
||||
}
|
||||
|
||||
func getSignHeaders(params map[string]string, newHeaders map[string]string) map[string]string {
|
||||
func GetSignHeadersFromRequest(cr rpc_request.IConfigRequest, secretKey string) map[string]string {
|
||||
resource := ""
|
||||
|
||||
if len(cr.GetGroup()) != 0 {
|
||||
resource = cr.GetTenant() + "+" + cr.GetGroup()
|
||||
} else {
|
||||
resource = cr.GetGroup()
|
||||
}
|
||||
|
||||
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 GetSignHeaders(params map[string]string, secretKey string) map[string]string {
|
||||
resource := ""
|
||||
|
||||
if len(params["tenant"]) != 0 {
|
||||
|
@ -332,15 +389,15 @@ func getSignHeaders(params map[string]string, newHeaders map[string]string) map[
|
|||
|
||||
headers := map[string]string{}
|
||||
|
||||
timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
|
||||
headers["timeStamp"] = timeStamp
|
||||
timeStamp := strconv.FormatInt(util.CurrentMillis(), 10)
|
||||
headers["Timestamp"] = timeStamp
|
||||
|
||||
signature := ""
|
||||
|
||||
if resource == "" {
|
||||
signature = signWithhmacSHA1Encrypt(timeStamp, newHeaders["secretKey"])
|
||||
signature = signWithhmacSHA1Encrypt(timeStamp, secretKey)
|
||||
} else {
|
||||
signature = signWithhmacSHA1Encrypt(resource+"+"+timeStamp, newHeaders["secretKey"])
|
||||
signature = signWithhmacSHA1Encrypt(resource+"+"+timeStamp, secretKey)
|
||||
}
|
||||
|
||||
headers["Spas-Signature"] = signature
|
||||
|
@ -356,3 +413,18 @@ func signWithhmacSHA1Encrypt(encryptText, encryptKey string) string {
|
|||
|
||||
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
||||
func (server *NacosServer) GetNextServer() (constant.ServerConfig, error) {
|
||||
serverLen := len(server.GetServerList())
|
||||
if serverLen == 0 {
|
||||
return constant.ServerConfig{}, errors.New("server is empty")
|
||||
}
|
||||
index := atomic.AddInt32(&server.currentIndex, 1) % int32(serverLen)
|
||||
return server.GetServerList()[index], nil
|
||||
}
|
||||
|
||||
func (server *NacosServer) InjectSkAk(params map[string]string, clientConfig constant.ClientConfig) {
|
||||
if clientConfig.AccessKey != "" {
|
||||
params["Spas-AccessKey"] = clientConfig.AccessKey
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ package nacos_server
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 rpc
|
||||
|
||||
import (
|
||||
"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"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type IConnection interface {
|
||||
request(request rpc_request.IRequest, timeoutMills int64, client *RpcClient) (rpc_response.IResponse, error)
|
||||
close()
|
||||
getConnectionId() string
|
||||
getServerInfo() ServerInfo
|
||||
setAbandon(flag bool)
|
||||
getAbandon() bool
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
conn *grpc.ClientConn
|
||||
connectionId string
|
||||
abandon bool
|
||||
serverInfo ServerInfo
|
||||
}
|
||||
|
||||
func (c *Connection) getConnectionId() string {
|
||||
return c.connectionId
|
||||
}
|
||||
|
||||
func (c *Connection) getServerInfo() ServerInfo {
|
||||
return c.serverInfo
|
||||
}
|
||||
|
||||
func (c *Connection) setAbandon(flag bool) {
|
||||
c.abandon = flag
|
||||
}
|
||||
|
||||
func (c *Connection) getAbandon() bool {
|
||||
return c.abandon
|
||||
}
|
||||
|
||||
func (c *Connection) close() {
|
||||
c.conn.Close()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 rpc
|
||||
|
||||
type IConnectionEventListener interface {
|
||||
|
||||
//notify when connected to server.
|
||||
OnConnected()
|
||||
|
||||
//notify when disconnected to server.
|
||||
OnDisConnect()
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
type MockConnection struct {
|
||||
}
|
||||
|
||||
func (m *MockConnection) request(request rpc_request.IRequest, timeoutMills int64, client *RpcClient) (rpc_response.IResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockConnection) close() {
|
||||
|
||||
}
|
||||
func (m *MockConnection) getConnectionId() string {
|
||||
return ""
|
||||
}
|
||||
func (m *MockConnection) getServerInfo() ServerInfo {
|
||||
return ServerInfo{}
|
||||
}
|
||||
func (m *MockConnection) setAbandon(flag bool) {
|
||||
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* 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 rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"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_response"
|
||||
|
||||
nacos_grpc_service "github.com/nacos-group/nacos-sdk-go/v2/api/grpc"
|
||||
"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/nacos_server"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
)
|
||||
|
||||
type GrpcClient struct {
|
||||
*RpcClient
|
||||
}
|
||||
|
||||
func NewGrpcClient(clientName string, nacosServer *nacos_server.NacosServer) *GrpcClient {
|
||||
rpcClient := &GrpcClient{
|
||||
&RpcClient{
|
||||
Name: clientName,
|
||||
labels: make(map[string]string, 8),
|
||||
rpcClientStatus: INITIALIZED,
|
||||
eventChan: make(chan ConnectionEvent),
|
||||
reconnectionChan: make(chan ReconnectContext, 1),
|
||||
nacosServer: nacosServer,
|
||||
serverRequestHandlerMapping: make(map[string]ServerRequestHandlerMapping, 8),
|
||||
mux: new(sync.Mutex),
|
||||
},
|
||||
}
|
||||
rpcClient.RpcClient.lastActiveTimestamp.Store(time.Now())
|
||||
rpcClient.executeClient = rpcClient
|
||||
listeners := make([]IConnectionEventListener, 0, 8)
|
||||
rpcClient.connectionEventListeners.Store(listeners)
|
||||
return rpcClient
|
||||
}
|
||||
|
||||
func getMaxCallRecvMsgSize() int {
|
||||
maxCallRecvMsgSizeInt, err := strconv.Atoi(os.Getenv("nacos.remote.client.grpc.maxinbound.message.size"))
|
||||
if err != nil {
|
||||
return 10 * 1024 * 1024
|
||||
}
|
||||
return maxCallRecvMsgSizeInt
|
||||
}
|
||||
|
||||
func getInitialWindowSize() int32 {
|
||||
initialWindowSize, err := strconv.Atoi(os.Getenv("nacos.remote.client.grpc.initial.window.size"))
|
||||
if err != nil {
|
||||
return 10 * 1024 * 1024
|
||||
}
|
||||
return int32(initialWindowSize)
|
||||
}
|
||||
|
||||
func getInitialConnWindowSize() int32 {
|
||||
initialConnWindowSize, err := strconv.Atoi(os.Getenv("nacos.remote.client.grpc.initial.conn.window.size"))
|
||||
if err != nil {
|
||||
return 10 * 1024 * 1024
|
||||
}
|
||||
return int32(initialConnWindowSize)
|
||||
}
|
||||
|
||||
func getKeepAliveTimeMillis() keepalive.ClientParameters {
|
||||
keepAliveTimeMillisInt, err := strconv.Atoi(os.Getenv("nacos.remote.grpc.keep.alive.millis"))
|
||||
var keepAliveTime time.Duration
|
||||
if err != nil {
|
||||
keepAliveTime = 60 * 1000 * time.Millisecond
|
||||
} else {
|
||||
keepAliveTime = time.Duration(keepAliveTimeMillisInt) * time.Millisecond
|
||||
}
|
||||
return keepalive.ClientParameters{
|
||||
Time: keepAliveTime, // send pings every 60 seconds if there is no activity
|
||||
Timeout: 20 * time.Second, // wait 20 second for ping ack before considering the connection dead
|
||||
PermitWithoutStream: true, // send pings even without active streams
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GrpcClient) createNewConnection(serverInfo ServerInfo) (*grpc.ClientConn, error) {
|
||||
var opts []grpc.DialOption
|
||||
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(getMaxCallRecvMsgSize())))
|
||||
opts = append(opts, grpc.WithKeepaliveParams(getKeepAliveTimeMillis()))
|
||||
opts = append(opts, grpc.WithInsecure())
|
||||
opts = append(opts, grpc.WithInitialWindowSize(getInitialWindowSize()))
|
||||
opts = append(opts, grpc.WithInitialConnWindowSize(getInitialConnWindowSize()))
|
||||
rpcPort := serverInfo.serverGrpcPort
|
||||
if rpcPort == 0 {
|
||||
rpcPort = serverInfo.serverPort + c.rpcPortOffset()
|
||||
}
|
||||
return grpc.Dial(serverInfo.serverIp+":"+strconv.FormatUint(rpcPort, 10), opts...)
|
||||
|
||||
}
|
||||
|
||||
func (c *GrpcClient) connectToServer(serverInfo ServerInfo) (IConnection, error) {
|
||||
var client nacos_grpc_service.RequestClient
|
||||
var biStreamClient nacos_grpc_service.BiRequestStreamClient
|
||||
|
||||
conn, err := c.createNewConnection(serverInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client = nacos_grpc_service.NewRequestClient(conn)
|
||||
response, err := serverCheck(client)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
biStreamClient = nacos_grpc_service.NewBiRequestStreamClient(conn)
|
||||
|
||||
serverCheckResponse := response.(*rpc_response.ServerCheckResponse)
|
||||
|
||||
biStreamRequestClient, err := biStreamClient.RequestBiStream(context.Background())
|
||||
|
||||
grpcConn := NewGrpcConnection(serverInfo, serverCheckResponse.ConnectionId, conn, client, biStreamRequestClient)
|
||||
|
||||
c.bindBiRequestStream(biStreamRequestClient, grpcConn)
|
||||
err = c.sendConnectionSetupRequest(grpcConn)
|
||||
return grpcConn, err
|
||||
}
|
||||
|
||||
func (c *GrpcClient) sendConnectionSetupRequest(grpcConn *GrpcConnection) error {
|
||||
csr := rpc_request.NewConnectionSetupRequest()
|
||||
csr.ClientVersion = constant.CLIENT_VERSION
|
||||
csr.Tenant = c.Tenant
|
||||
csr.Labels = c.labels
|
||||
csr.ClientAbilities = c.clientAbilities
|
||||
err := grpcConn.biStreamSend(convertRequest(csr))
|
||||
if err != nil {
|
||||
logger.Warnf("Send ConnectionSetupRequest error:%+v", err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *GrpcClient) getConnectionType() ConnectionType {
|
||||
return GRPC
|
||||
}
|
||||
|
||||
func (c *GrpcClient) rpcPortOffset() uint64 {
|
||||
return 1000
|
||||
}
|
||||
|
||||
func (c *GrpcClient) bindBiRequestStream(streamClient nacos_grpc_service.BiRequestStream_RequestBiStreamClient, grpcConn *GrpcConnection) {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-streamClient.Context().Done():
|
||||
return
|
||||
default:
|
||||
payload, err := streamClient.Recv()
|
||||
if err != nil {
|
||||
running := c.IsRunning()
|
||||
abandon := grpcConn.getAbandon()
|
||||
if c.IsRunning() && !abandon {
|
||||
if err == io.EOF {
|
||||
logger.Infof("%s Request stream onCompleted, switch server", grpcConn.getConnectionId())
|
||||
} else {
|
||||
logger.Errorf("%s Request stream error, switch server, error=%+v", grpcConn.getConnectionId(), err)
|
||||
}
|
||||
if atomic.CompareAndSwapInt32((*int32)(&c.rpcClientStatus), int32(RUNNING), int32(UNHEALTHY)) {
|
||||
c.switchServerAsync(ServerInfo{}, false)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
logger.Infof("%s received error event, isRunning:%v, isAbandon=%v, error=%+v", grpcConn.getConnectionId(), running, abandon, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.handleServerRequest(payload, grpcConn)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func serverCheck(client nacos_grpc_service.RequestClient) (rpc_response.IResponse, error) {
|
||||
var response rpc_response.ServerCheckResponse
|
||||
for i := 0; i <= 30; i++ {
|
||||
payload, err := client.Request(context.Background(), convertRequest(rpc_request.NewServerCheckRequest()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(payload.GetBody().Value, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check if the server is ready, if not, wait 1 second and try again
|
||||
if response.GetErrorCode() >= 300 && response.GetErrorCode() < 400 {
|
||||
// if we wait 30 second, but the server is not ready,then throw this error
|
||||
if i == 30 {
|
||||
return nil, errors.New("the nacos server is not ready to work in 30 seconds, connect to server failed")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (c *GrpcClient) handleServerRequest(p *nacos_grpc_service.Payload, grpcConn *GrpcConnection) {
|
||||
client := c.GetRpcClient()
|
||||
payLoadType := p.GetMetadata().GetType()
|
||||
|
||||
mapping, ok := client.serverRequestHandlerMapping[payLoadType]
|
||||
if !ok {
|
||||
logger.Errorf("%s Unsupported payload type", grpcConn.getConnectionId())
|
||||
return
|
||||
}
|
||||
|
||||
serverRequest := mapping.serverRequest()
|
||||
err := json.Unmarshal(p.GetBody().Value, serverRequest)
|
||||
if err != nil {
|
||||
logger.Errorf("%s Fail to json Unmarshal for request:%s, ackId->%s", grpcConn.getConnectionId(),
|
||||
serverRequest.GetRequestType(), serverRequest.GetRequestId())
|
||||
return
|
||||
}
|
||||
|
||||
serverRequest.PutAllHeaders(p.GetMetadata().Headers)
|
||||
|
||||
response := mapping.handler.RequestReply(serverRequest, client)
|
||||
if response == nil {
|
||||
logger.Warnf("%s Fail to process server request, ackId->%s", grpcConn.getConnectionId(),
|
||||
serverRequest.GetRequestId())
|
||||
return
|
||||
}
|
||||
response.SetRequestId(serverRequest.GetRequestId())
|
||||
err = grpcConn.biStreamSend(convertResponse(response))
|
||||
if err != nil && err != io.EOF {
|
||||
logger.Warnf("%s Fail to send response:%s,ackId->%s", grpcConn.getConnectionId(),
|
||||
response.GetResponseType(), serverRequest.GetRequestId())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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_response"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type GrpcConnection struct {
|
||||
*Connection
|
||||
client nacos_grpc_service.RequestClient
|
||||
biStreamClient nacos_grpc_service.BiRequestStream_RequestBiStreamClient
|
||||
}
|
||||
|
||||
func NewGrpcConnection(serverInfo ServerInfo, connectionId string, conn *grpc.ClientConn,
|
||||
client nacos_grpc_service.RequestClient, biStreamClient nacos_grpc_service.BiRequestStream_RequestBiStreamClient) *GrpcConnection {
|
||||
return &GrpcConnection{
|
||||
Connection: &Connection{
|
||||
serverInfo: serverInfo,
|
||||
connectionId: connectionId,
|
||||
abandon: false,
|
||||
conn: conn,
|
||||
},
|
||||
client: client,
|
||||
biStreamClient: biStreamClient,
|
||||
}
|
||||
}
|
||||
func (g *GrpcConnection) request(request rpc_request.IRequest, timeoutMills int64, client *RpcClient) (rpc_response.IResponse, error) {
|
||||
p := convertRequest(request)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMills)*time.Millisecond)
|
||||
defer cancel()
|
||||
responsePayload, err := g.client.Request(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responseFunc, ok := rpc_response.ClientResponseMapping[responsePayload.Metadata.GetType()]
|
||||
|
||||
if !ok {
|
||||
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() {
|
||||
g.Connection.close()
|
||||
}
|
||||
|
||||
func (g *GrpcConnection) biStreamSend(payload *nacos_grpc_service.Payload) error {
|
||||
return g.biStreamClient.Send(payload)
|
||||
}
|
||||
|
||||
func convertRequest(r rpc_request.IRequest) *nacos_grpc_service.Payload {
|
||||
Metadata := nacos_grpc_service.Metadata{
|
||||
Type: r.GetRequestType(),
|
||||
Headers: r.GetHeaders(),
|
||||
ClientIp: util.LocalIP(),
|
||||
}
|
||||
return &nacos_grpc_service.Payload{
|
||||
Metadata: &Metadata,
|
||||
Body: &any.Any{Value: []byte(r.GetBody(r))},
|
||||
}
|
||||
}
|
||||
|
||||
func convertResponse(r rpc_response.IResponse) *nacos_grpc_service.Payload {
|
||||
Metadata := nacos_grpc_service.Metadata{
|
||||
Type: r.GetResponseType(),
|
||||
ClientIp: util.LocalIP(),
|
||||
}
|
||||
return &nacos_grpc_service.Payload{
|
||||
Metadata: &Metadata,
|
||||
Body: &any.Any{Value: []byte(r.GetBody())},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* 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 rpc
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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/nacos_server"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
type ConnectionType uint32
|
||||
|
||||
const (
|
||||
GRPC ConnectionType = iota
|
||||
)
|
||||
|
||||
type RpcClientStatus int32
|
||||
|
||||
const (
|
||||
INITIALIZED RpcClientStatus = iota
|
||||
STARTING
|
||||
UNHEALTHY
|
||||
RUNNING
|
||||
SHUTDOWN
|
||||
)
|
||||
|
||||
func (status RpcClientStatus) getDesc() string {
|
||||
switch status {
|
||||
case INITIALIZED:
|
||||
return "INITIALIZED"
|
||||
case STARTING:
|
||||
return "STARTING"
|
||||
case UNHEALTHY:
|
||||
return "UNHEALTHY"
|
||||
case RUNNING:
|
||||
return "RUNNING"
|
||||
case SHUTDOWN:
|
||||
return "SHUTDOWN"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
type ConnectionStatus uint32
|
||||
|
||||
const (
|
||||
DISCONNECTED ConnectionStatus = iota
|
||||
CONNECTED
|
||||
)
|
||||
|
||||
var (
|
||||
cMux = new(sync.Mutex)
|
||||
clientMap = make(map[string]IRpcClient)
|
||||
)
|
||||
|
||||
type IRpcClient interface {
|
||||
connectToServer(serverInfo ServerInfo) (IConnection, error)
|
||||
getConnectionType() ConnectionType
|
||||
putAllLabels(labels map[string]string)
|
||||
rpcPortOffset() uint64
|
||||
GetRpcClient() *RpcClient
|
||||
}
|
||||
|
||||
type ServerInfo struct {
|
||||
serverIp string
|
||||
serverPort uint64
|
||||
serverGrpcPort uint64
|
||||
}
|
||||
|
||||
type RpcClient struct {
|
||||
Name string
|
||||
labels map[string]string
|
||||
currentConnection IConnection
|
||||
rpcClientStatus RpcClientStatus
|
||||
eventChan chan ConnectionEvent
|
||||
reconnectionChan chan ReconnectContext
|
||||
connectionEventListeners atomic.Value
|
||||
lastActiveTimestamp atomic.Value
|
||||
executeClient IRpcClient
|
||||
nacosServer *nacos_server.NacosServer
|
||||
serverRequestHandlerMapping map[string]ServerRequestHandlerMapping
|
||||
mux *sync.Mutex
|
||||
clientAbilities rpc_request.ClientAbilities
|
||||
Tenant string
|
||||
}
|
||||
|
||||
type ServerRequestHandlerMapping struct {
|
||||
serverRequest func() rpc_request.IRequest
|
||||
handler IServerRequestHandler
|
||||
}
|
||||
|
||||
type ReconnectContext struct {
|
||||
onRequestFail bool
|
||||
serverInfo ServerInfo
|
||||
}
|
||||
|
||||
type ConnectionEvent struct {
|
||||
eventType ConnectionStatus
|
||||
}
|
||||
|
||||
func (r *RpcClient) putAllLabels(labels map[string]string) {
|
||||
for k, v := range labels {
|
||||
r.labels[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RpcClient) GetRpcClient() *RpcClient {
|
||||
return r
|
||||
}
|
||||
|
||||
/**
|
||||
* get all client.
|
||||
*
|
||||
*/
|
||||
func getAllClient() map[string]IRpcClient {
|
||||
return clientMap
|
||||
}
|
||||
|
||||
func getClient(clientName string) IRpcClient {
|
||||
return clientMap[clientName]
|
||||
}
|
||||
|
||||
func CreateClient(clientName string, connectionType ConnectionType, labels map[string]string, nacosServer *nacos_server.NacosServer) (IRpcClient, error) {
|
||||
cMux.Lock()
|
||||
defer cMux.Unlock()
|
||||
if _, ok := clientMap[clientName]; !ok {
|
||||
var rpcClient IRpcClient
|
||||
if GRPC == connectionType {
|
||||
rpcClient = NewGrpcClient(clientName, nacosServer)
|
||||
}
|
||||
if rpcClient == nil {
|
||||
return nil, errors.New("unsupported connection type")
|
||||
}
|
||||
rpcClient.putAllLabels(labels)
|
||||
clientMap[clientName] = rpcClient
|
||||
return rpcClient, nil
|
||||
}
|
||||
return clientMap[clientName], nil
|
||||
}
|
||||
|
||||
func (r *RpcClient) Start() {
|
||||
if ok := atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), (int32)(INITIALIZED), (int32)(STARTING)); !ok {
|
||||
return
|
||||
}
|
||||
r.registerServerRequestHandlers()
|
||||
go func() {
|
||||
for {
|
||||
event := <-r.eventChan
|
||||
r.notifyConnectionEvent(event)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case rc := <-r.reconnectionChan:
|
||||
if (rc.serverInfo != ServerInfo{}) {
|
||||
var serverExist bool
|
||||
for _, v := range r.nacosServer.GetServerList() {
|
||||
if rc.serverInfo.serverIp == v.IpAddr {
|
||||
rc.serverInfo.serverPort = v.Port
|
||||
rc.serverInfo.serverGrpcPort = v.GrpcPort
|
||||
serverExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !serverExist {
|
||||
logger.Infof("%s recommend server is not in server list, ignore recommend server %+v", r.Name, rc.serverInfo)
|
||||
rc.serverInfo = ServerInfo{}
|
||||
}
|
||||
}
|
||||
r.reconnect(rc.serverInfo, rc.onRequestFail)
|
||||
case <-timer.C:
|
||||
r.healthCheck(timer)
|
||||
case <-r.nacosServer.ServerSrcChangeSignal:
|
||||
r.notifyServerSrvChange()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var currentConnection IConnection
|
||||
startUpRetryTimes := constant.REQUEST_DOMAIN_RETRY_TIME
|
||||
for startUpRetryTimes > 0 && currentConnection == nil {
|
||||
startUpRetryTimes--
|
||||
serverInfo, err := r.nextRpcServer()
|
||||
if err != nil {
|
||||
logger.Errorf("[RpcClient.nextRpcServer],err:%+v", err)
|
||||
break
|
||||
}
|
||||
logger.Infof("[RpcClient.Start] %s try to connect to server on start up, server: %+v", r.Name, serverInfo)
|
||||
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, "+
|
||||
"start up retry times left=%d", r.Name, err.Error(), startUpRetryTimes)
|
||||
} else {
|
||||
currentConnection = connection
|
||||
break
|
||||
}
|
||||
}
|
||||
if currentConnection != nil {
|
||||
logger.Infof("%s success to connect to server %+v on start up, connectionId=%s", r.Name,
|
||||
currentConnection.getServerInfo(), currentConnection.getConnectionId())
|
||||
r.currentConnection = currentConnection
|
||||
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING))
|
||||
r.eventChan <- ConnectionEvent{eventType: CONNECTED}
|
||||
} else {
|
||||
r.switchServerAsync(ServerInfo{}, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RpcClient) notifyServerSrvChange() {
|
||||
if r.currentConnection == nil {
|
||||
r.switchServerAsync(ServerInfo{}, false)
|
||||
return
|
||||
}
|
||||
curServerInfo := r.currentConnection.getServerInfo()
|
||||
var found bool
|
||||
for _, ele := range r.nacosServer.GetServerList() {
|
||||
if ele.IpAddr == curServerInfo.serverIp {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logger.Infof("Current connected server %s:%d is not in latest server list, switch switchServerAsync", curServerInfo.serverIp, curServerInfo.serverPort)
|
||||
r.switchServerAsync(ServerInfo{}, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RpcClient) registerServerRequestHandlers() {
|
||||
// register ConnectResetRequestHandler.
|
||||
r.RegisterServerRequestHandler(func() rpc_request.IRequest {
|
||||
return &rpc_request.ConnectResetRequest{InternalRequest: rpc_request.NewInternalRequest()}
|
||||
}, &ConnectResetRequestHandler{})
|
||||
|
||||
// register client detection request.
|
||||
r.RegisterServerRequestHandler(func() rpc_request.IRequest {
|
||||
return &rpc_request.ClientDetectionRequest{InternalRequest: rpc_request.NewInternalRequest()}
|
||||
}, &ClientDetectionRequestHandler{})
|
||||
}
|
||||
|
||||
func (r *RpcClient) Shutdown() {
|
||||
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(SHUTDOWN))
|
||||
r.closeConnection()
|
||||
}
|
||||
|
||||
func (r *RpcClient) RegisterServerRequestHandler(request func() rpc_request.IRequest, handler IServerRequestHandler) {
|
||||
requestType := request().GetRequestType()
|
||||
if handler == nil || requestType == "" {
|
||||
logger.Errorf("%s register server push request handler "+
|
||||
"missing required parameters,request:%+v handler:%+v", r.Name, requestType, handler.Name())
|
||||
return
|
||||
}
|
||||
logger.Debugf("%s register server push request:%s handler:%+v", r.Name, requestType, handler.Name())
|
||||
r.serverRequestHandlerMapping[requestType] = ServerRequestHandlerMapping{
|
||||
serverRequest: request,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RpcClient) RegisterConnectionListener(listener IConnectionEventListener) {
|
||||
logger.Debugf("%s register connection listener [%+v] to current client", r.Name, reflect.TypeOf(listener))
|
||||
listeners := r.connectionEventListeners.Load()
|
||||
connectionEventListeners := listeners.([]IConnectionEventListener)
|
||||
connectionEventListeners = append(connectionEventListeners, listener)
|
||||
r.connectionEventListeners.Store(connectionEventListeners)
|
||||
}
|
||||
|
||||
func (r *RpcClient) switchServerAsync(recommendServerInfo ServerInfo, onRequestFail bool) {
|
||||
r.reconnectionChan <- ReconnectContext{serverInfo: recommendServerInfo, onRequestFail: onRequestFail}
|
||||
}
|
||||
|
||||
func (r *RpcClient) reconnect(serverInfo ServerInfo, onRequestFail bool) {
|
||||
if onRequestFail && r.sendHealthCheck() {
|
||||
logger.Infof("%s server check success, currentServer is %+v", r.Name, r.currentConnection.getServerInfo())
|
||||
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING))
|
||||
return
|
||||
}
|
||||
var (
|
||||
serverInfoFlag bool
|
||||
reConnectTimes, retryTurns int
|
||||
err error
|
||||
)
|
||||
if (serverInfo == ServerInfo{}) {
|
||||
serverInfoFlag = true
|
||||
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() {
|
||||
if serverInfoFlag {
|
||||
serverInfo, err = r.nextRpcServer()
|
||||
if err != nil {
|
||||
logger.Errorf("[RpcClient.nextRpcServer],err:%v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
connectionNew, err := r.executeClient.connectToServer(serverInfo)
|
||||
if connectionNew != nil && err == nil {
|
||||
logger.Infof("%s success to connect a server %+v, connectionId=%s", r.Name, serverInfo,
|
||||
connectionNew.getConnectionId())
|
||||
|
||||
if r.currentConnection != nil {
|
||||
logger.Infof("%s abandon prev connection, server is %+v, connectionId is %s", r.Name, serverInfo,
|
||||
r.currentConnection.getConnectionId())
|
||||
r.currentConnection.setAbandon(true)
|
||||
r.closeConnection()
|
||||
}
|
||||
r.currentConnection = connectionNew
|
||||
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING))
|
||||
r.eventChan <- ConnectionEvent{eventType: CONNECTED}
|
||||
return
|
||||
}
|
||||
if r.isShutdown() {
|
||||
r.closeConnection()
|
||||
}
|
||||
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,
|
||||
reConnectTimes, serverInfo, err)
|
||||
if retryTurns < 50 {
|
||||
retryTurns++
|
||||
}
|
||||
}
|
||||
reConnectTimes++
|
||||
if !r.IsRunning() {
|
||||
time.Sleep(time.Duration((math.Min(float64(retryTurns), 50))*100) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
if r.isShutdown() {
|
||||
logger.Warnf("%s client is shutdown, stop reconnect to server", r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RpcClient) closeConnection() {
|
||||
if r.currentConnection != nil {
|
||||
r.currentConnection.close()
|
||||
r.eventChan <- ConnectionEvent{eventType: DISCONNECTED}
|
||||
}
|
||||
}
|
||||
|
||||
// Notify when client new connected.
|
||||
func (r *RpcClient) notifyConnectionEvent(event ConnectionEvent) {
|
||||
listeners := r.connectionEventListeners.Load().([]IConnectionEventListener)
|
||||
if len(listeners) == 0 {
|
||||
return
|
||||
}
|
||||
logger.Infof("%s notify %s event to listeners.", r.Name, event.toString())
|
||||
for _, v := range listeners {
|
||||
if event.isConnected() {
|
||||
v.OnConnected()
|
||||
}
|
||||
if event.isDisConnected() {
|
||||
v.OnDisConnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RpcClient) healthCheck(timer *time.Timer) {
|
||||
defer timer.Reset(constant.KEEP_ALIVE_TIME * time.Second)
|
||||
var reconnectContext ReconnectContext
|
||||
lastActiveTimeStamp := r.lastActiveTimestamp.Load().(time.Time)
|
||||
if time.Now().Sub(lastActiveTimeStamp) < constant.KEEP_ALIVE_TIME*time.Second {
|
||||
return
|
||||
}
|
||||
if r.sendHealthCheck() {
|
||||
r.lastActiveTimestamp.Store(time.Now())
|
||||
return
|
||||
} else {
|
||||
if r.currentConnection == nil {
|
||||
return
|
||||
}
|
||||
logger.Infof("%s server healthy check fail, currentConnection=%s", r.Name, r.currentConnection.getConnectionId())
|
||||
atomic.StoreInt32((*int32)(&r.rpcClientStatus), (int32)(UNHEALTHY))
|
||||
reconnectContext = ReconnectContext{onRequestFail: false}
|
||||
}
|
||||
r.reconnect(reconnectContext.serverInfo, reconnectContext.onRequestFail)
|
||||
}
|
||||
|
||||
func (r *RpcClient) sendHealthCheck() bool {
|
||||
if r.currentConnection == nil {
|
||||
return false
|
||||
}
|
||||
response, err := r.currentConnection.request(rpc_request.NewHealthCheckRequest(),
|
||||
constant.DEFAULT_TIMEOUT_MILLS, r)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if !response.IsSuccess() {
|
||||
// when client request immediately after the nacos server starts, the server may not ready to serve new request
|
||||
// the server will return code 3xx, tell the client to retry after a while
|
||||
// this situation, just return true,because the healthCheck will start again after 5 seconds
|
||||
if response.GetErrorCode() >= 300 && response.GetErrorCode() < 400 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *RpcClient) nextRpcServer() (ServerInfo, error) {
|
||||
serverConfig, err := r.nacosServer.GetNextServer()
|
||||
if err != nil {
|
||||
return ServerInfo{}, err
|
||||
}
|
||||
return ServerInfo{
|
||||
serverIp: serverConfig.IpAddr,
|
||||
serverPort: serverConfig.Port,
|
||||
serverGrpcPort: serverConfig.GrpcPort,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *ConnectionEvent) isConnected() bool {
|
||||
return c.eventType == CONNECTED
|
||||
}
|
||||
|
||||
func (c *ConnectionEvent) isDisConnected() bool {
|
||||
return c.eventType == DISCONNECTED
|
||||
}
|
||||
|
||||
//check is this client is shutdown.
|
||||
func (r *RpcClient) isShutdown() bool {
|
||||
return atomic.LoadInt32((*int32)(&r.rpcClientStatus)) == (int32)(SHUTDOWN)
|
||||
}
|
||||
|
||||
//IsRunning check is this client is running.
|
||||
func (r *RpcClient) IsRunning() bool {
|
||||
return atomic.LoadInt32((*int32)(&r.rpcClientStatus)) == (int32)(RUNNING)
|
||||
}
|
||||
|
||||
func (r *RpcClient) IsInitialized() bool {
|
||||
return atomic.LoadInt32((*int32)(&r.rpcClientStatus)) == (int32)(INITIALIZED)
|
||||
}
|
||||
|
||||
func (c *ConnectionEvent) toString() string {
|
||||
if c.isConnected() {
|
||||
return "connected"
|
||||
}
|
||||
if c.isDisConnected() {
|
||||
return "disconnected"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *RpcClient) Request(request rpc_request.IRequest, timeoutMills int64) (rpc_response.IResponse, error) {
|
||||
retryTimes := 0
|
||||
start := util.CurrentMillis()
|
||||
var currentErr error
|
||||
for retryTimes < constant.REQUEST_DOMAIN_RETRY_TIME && util.CurrentMillis() < start+timeoutMills {
|
||||
if r.currentConnection == nil || !r.IsRunning() {
|
||||
currentErr = waitReconnect(timeoutMills, &retryTimes, request,
|
||||
errors.Errorf("client not connected, current status:%s", r.rpcClientStatus.getDesc()))
|
||||
continue
|
||||
}
|
||||
response, err := r.currentConnection.request(request, timeoutMills, r)
|
||||
if err == nil {
|
||||
if response, ok := response.(*rpc_response.ErrorResponse); ok {
|
||||
if response.GetErrorCode() == constant.UN_REGISTER {
|
||||
r.mux.Lock()
|
||||
if atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), (int32)(RUNNING), (int32)(UNHEALTHY)) {
|
||||
logger.Infof("Connection is unregistered, switch server, connectionId=%s, request=%s",
|
||||
r.currentConnection.getConnectionId(), request.GetRequestType())
|
||||
r.switchServerAsync(ServerInfo{}, false)
|
||||
}
|
||||
r.mux.Unlock()
|
||||
}
|
||||
currentErr = waitReconnect(timeoutMills, &retryTimes, request, errors.New(response.GetMessage()))
|
||||
continue
|
||||
}
|
||||
r.lastActiveTimestamp.Store(time.Now())
|
||||
return response, nil
|
||||
} else {
|
||||
currentErr = waitReconnect(timeoutMills, &retryTimes, request, err)
|
||||
}
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapInt32((*int32)(&r.rpcClientStatus), int32(RUNNING), int32(UNHEALTHY)) {
|
||||
r.switchServerAsync(ServerInfo{}, true)
|
||||
}
|
||||
if currentErr != nil {
|
||||
return nil, currentErr
|
||||
}
|
||||
return nil, errors.New("request fail, unknown error")
|
||||
}
|
||||
|
||||
func waitReconnect(timeoutMills int64, retryTimes *int, request rpc_request.IRequest, err error) error {
|
||||
logger.Errorf("Send request fail, request=%s, body=%s, retryTimes=%v, error=%+v", request.GetRequestType(), request.GetBody(request), *retryTimes, err)
|
||||
time.Sleep(time.Duration(math.Min(100, float64(timeoutMills/3))) * time.Millisecond)
|
||||
*retryTimes++
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package rpc
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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 rpc_request
|
||||
|
||||
import "github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
|
||||
type ConfigRequest struct {
|
||||
*Request
|
||||
Group string `json:"group"`
|
||||
DataId string `json:"dataId"`
|
||||
Tenant string `json:"tenant"`
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
func NewConfigRequest(group, dataId, tenant string) *ConfigRequest {
|
||||
request := Request{
|
||||
Headers: make(map[string]string, 8),
|
||||
}
|
||||
return &ConfigRequest{
|
||||
Request: &request,
|
||||
Group: group,
|
||||
DataId: dataId,
|
||||
Tenant: tenant,
|
||||
Module: "config",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ConfigRequest) GetDataId() string {
|
||||
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 {
|
||||
*ConfigRequest
|
||||
Listen bool `json:"listen"`
|
||||
ConfigListenContexts []model.ConfigListenContext `json:"configListenContexts"`
|
||||
}
|
||||
|
||||
func NewConfigBatchListenRequest(cacheLen int) *ConfigBatchListenRequest {
|
||||
return &ConfigBatchListenRequest{
|
||||
Listen: true,
|
||||
ConfigListenContexts: make([]model.ConfigListenContext, 0, cacheLen),
|
||||
ConfigRequest: NewConfigRequest("", "", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ConfigBatchListenRequest) GetRequestType() string {
|
||||
return "ConfigBatchListenRequest"
|
||||
}
|
||||
|
||||
type ConfigChangeNotifyRequest struct {
|
||||
*ConfigRequest
|
||||
}
|
||||
|
||||
func NewConfigChangeNotifyRequest(group, dataId, tenant string) *ConfigChangeNotifyRequest {
|
||||
return &ConfigChangeNotifyRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant)}
|
||||
}
|
||||
|
||||
func (r *ConfigChangeNotifyRequest) GetRequestType() string {
|
||||
return "ConfigChangeNotifyRequest"
|
||||
}
|
||||
|
||||
type ConfigQueryRequest struct {
|
||||
*ConfigRequest
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
func NewConfigQueryRequest(group, dataId, tenant string) *ConfigQueryRequest {
|
||||
return &ConfigQueryRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant)}
|
||||
}
|
||||
|
||||
func (r *ConfigQueryRequest) GetRequestType() string {
|
||||
return "ConfigQueryRequest"
|
||||
}
|
||||
|
||||
type ConfigPublishRequest struct {
|
||||
*ConfigRequest
|
||||
Content string `json:"content"`
|
||||
CasMd5 string `json:"casMd5"`
|
||||
AdditionMap map[string]string `json:"additionMap"`
|
||||
}
|
||||
|
||||
func NewConfigPublishRequest(group, dataId, tenant, content, casMd5 string) *ConfigPublishRequest {
|
||||
return &ConfigPublishRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant),
|
||||
Content: content, CasMd5: casMd5, AdditionMap: make(map[string]string)}
|
||||
}
|
||||
|
||||
func (r *ConfigPublishRequest) GetRequestType() string {
|
||||
return "ConfigPublishRequest"
|
||||
}
|
||||
|
||||
type ConfigRemoveRequest struct {
|
||||
*ConfigRequest
|
||||
}
|
||||
|
||||
func NewConfigRemoveRequest(group, dataId, tenant string) *ConfigRemoveRequest {
|
||||
return &ConfigRemoveRequest{ConfigRequest: NewConfigRequest(group, dataId, tenant)}
|
||||
}
|
||||
|
||||
func (r *ConfigRemoveRequest) GetRequestType() string {
|
||||
return "ConfigRemoveRequest"
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 rpc_request
|
||||
|
||||
type ClientAbilities struct {
|
||||
}
|
||||
|
||||
type InternalRequest struct {
|
||||
*Request
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
func NewInternalRequest() *InternalRequest {
|
||||
request := Request{
|
||||
Headers: make(map[string]string, 8),
|
||||
}
|
||||
return &InternalRequest{
|
||||
Request: &request,
|
||||
Module: "internal",
|
||||
}
|
||||
}
|
||||
|
||||
type HealthCheckRequest struct {
|
||||
*InternalRequest
|
||||
}
|
||||
|
||||
func NewHealthCheckRequest() *HealthCheckRequest {
|
||||
return &HealthCheckRequest{
|
||||
InternalRequest: NewInternalRequest(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HealthCheckRequest) GetRequestType() string {
|
||||
return "HealthCheckRequest"
|
||||
}
|
||||
|
||||
type ConnectResetRequest struct {
|
||||
*InternalRequest
|
||||
ServerIp string
|
||||
ServerPort string
|
||||
}
|
||||
|
||||
func (r *ConnectResetRequest) GetRequestType() string {
|
||||
return "ConnectResetRequest"
|
||||
}
|
||||
|
||||
type ClientDetectionRequest struct {
|
||||
*InternalRequest
|
||||
}
|
||||
|
||||
func (r *ClientDetectionRequest) GetRequestType() string {
|
||||
return "ClientDetectionRequest"
|
||||
}
|
||||
|
||||
type ServerCheckRequest struct {
|
||||
*InternalRequest
|
||||
}
|
||||
|
||||
func NewServerCheckRequest() *ServerCheckRequest {
|
||||
return &ServerCheckRequest{
|
||||
InternalRequest: NewInternalRequest(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ServerCheckRequest) GetRequestType() string {
|
||||
return "ServerCheckRequest"
|
||||
}
|
||||
|
||||
type ConnectionSetupRequest struct {
|
||||
*InternalRequest
|
||||
ClientVersion string `json:"clientVersion"`
|
||||
Tenant string `json:"tenant"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ClientAbilities ClientAbilities `json:"clientAbilities"`
|
||||
}
|
||||
|
||||
func NewConnectionSetupRequest() *ConnectionSetupRequest {
|
||||
return &ConnectionSetupRequest{
|
||||
InternalRequest: NewInternalRequest(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ConnectionSetupRequest) GetRequestType() string {
|
||||
return "ConnectionSetupRequest"
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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 rpc_request
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
)
|
||||
|
||||
type NamingRequest struct {
|
||||
*Request
|
||||
Namespace string `json:"namespace"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
GroupName string `json:"groupName"`
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
type InstanceRequest struct {
|
||||
*NamingRequest
|
||||
Type string `json:"type"`
|
||||
Instance model.Instance `json:"instance"`
|
||||
}
|
||||
|
||||
func NewNamingRequest(namespace, serviceName, groupName string) *NamingRequest {
|
||||
request := Request{
|
||||
Headers: make(map[string]string, 8),
|
||||
}
|
||||
return &NamingRequest{
|
||||
Request: &request,
|
||||
Namespace: namespace,
|
||||
ServiceName: serviceName,
|
||||
GroupName: groupName,
|
||||
Module: "naming",
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func NewInstanceRequest(namespace, serviceName, groupName, Type string, instance model.Instance) *InstanceRequest {
|
||||
return &InstanceRequest{
|
||||
NamingRequest: NewNamingRequest(namespace, serviceName, groupName),
|
||||
Type: Type,
|
||||
Instance: instance,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InstanceRequest) GetRequestType() string {
|
||||
return "InstanceRequest"
|
||||
}
|
||||
|
||||
type NotifySubscriberRequest struct {
|
||||
*NamingRequest
|
||||
ServiceInfo model.Service `json:"serviceInfo"`
|
||||
}
|
||||
|
||||
func (r *NotifySubscriberRequest) GetRequestType() string {
|
||||
return "NotifySubscriberRequest"
|
||||
}
|
||||
|
||||
type ServiceListRequest struct {
|
||||
*NamingRequest
|
||||
PageNo int `json:"pageNo"`
|
||||
PageSize int `json:"pageSize"`
|
||||
Selector string `json:"selector"`
|
||||
}
|
||||
|
||||
func NewServiceListRequest(namespace, serviceName, groupName string, pageNo, pageSize int, selector string) *ServiceListRequest {
|
||||
return &ServiceListRequest{
|
||||
NamingRequest: NewNamingRequest(namespace, serviceName, groupName),
|
||||
PageNo: pageNo,
|
||||
PageSize: pageSize,
|
||||
Selector: selector,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ServiceListRequest) GetRequestType() string {
|
||||
return "ServiceListRequest"
|
||||
}
|
||||
|
||||
type SubscribeServiceRequest struct {
|
||||
*NamingRequest
|
||||
Subscribe bool `json:"subscribe"`
|
||||
Clusters string `json:"clusters"`
|
||||
}
|
||||
|
||||
func NewSubscribeServiceRequest(namespace, serviceName, groupName, clusters string, subscribe bool) *SubscribeServiceRequest {
|
||||
return &SubscribeServiceRequest{
|
||||
NamingRequest: NewNamingRequest(namespace, serviceName, groupName),
|
||||
Subscribe: subscribe,
|
||||
Clusters: clusters,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SubscribeServiceRequest) GetRequestType() string {
|
||||
return "SubscribeServiceRequest"
|
||||
}
|
||||
|
||||
type ServiceQueryRequest struct {
|
||||
*NamingRequest
|
||||
Clusters string `json:"clusters"`
|
||||
HealthyOnly bool `json:"healthyOnly"`
|
||||
UdpPort int `json:"udpPort"`
|
||||
}
|
||||
|
||||
func NewServiceQueryRequest(namespace, serviceName, groupName, clusters string, healthyOnly bool, udpPort int) *ServiceQueryRequest {
|
||||
return &ServiceQueryRequest{
|
||||
NamingRequest: NewNamingRequest(namespace, serviceName, groupName),
|
||||
Clusters: clusters,
|
||||
HealthyOnly: healthyOnly,
|
||||
UdpPort: udpPort,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ServiceQueryRequest) GetRequestType() string {
|
||||
return "ServiceQueryRequest"
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 rpc_request
|
||||
|
||||
import "github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
|
||||
type Request struct {
|
||||
Headers map[string]string `json:"-"`
|
||||
RequestId string `json:"requestId"`
|
||||
}
|
||||
|
||||
type IRequest interface {
|
||||
GetHeaders() map[string]string
|
||||
GetRequestType() string
|
||||
GetBody(request IRequest) string
|
||||
PutAllHeaders(headers map[string]string)
|
||||
GetRequestId() string
|
||||
GetStringToSign() string
|
||||
}
|
||||
|
||||
type IConfigRequest interface {
|
||||
GetDataId() string
|
||||
GetGroup() string
|
||||
GetTenant() string
|
||||
}
|
||||
|
||||
func (r *Request) PutAllHeaders(headers map[string]string) {
|
||||
for k, v := range headers {
|
||||
r.Headers[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Request) ClearHeaders() {
|
||||
r.Headers = make(map[string]string)
|
||||
}
|
||||
|
||||
func (r *Request) GetHeaders() map[string]string {
|
||||
return r.Headers
|
||||
}
|
||||
|
||||
func (r *Request) GetBody(request IRequest) string {
|
||||
return util.ToJsonString(request)
|
||||
}
|
||||
func (r *Request) GetRequestId() string {
|
||||
return r.RequestId
|
||||
}
|
||||
|
||||
func (r *Request) GetStringToSign() string {
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 rpc_response
|
||||
|
||||
import "github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
|
||||
type ConfigChangeBatchListenResponse struct {
|
||||
*Response
|
||||
ChangedConfigs []model.ConfigContext `json:"changedConfigs"`
|
||||
}
|
||||
|
||||
func (c *ConfigChangeBatchListenResponse) GetResponseType() string {
|
||||
return "ConfigChangeBatchListenResponse"
|
||||
}
|
||||
|
||||
type ConfigQueryResponse struct {
|
||||
*Response
|
||||
Content string `json:"content"`
|
||||
EncryptedDataKey string `json:"encryptedDataKey"`
|
||||
ContentType string `json:"contentType"`
|
||||
Md5 string `json:"md5"`
|
||||
LastModified int64 `json:"lastModified"`
|
||||
IsBeta bool `json:"isBeta"`
|
||||
Tag bool `json:"tag"`
|
||||
}
|
||||
|
||||
func (c *ConfigQueryResponse) GetResponseType() string {
|
||||
return "ConfigQueryResponse"
|
||||
}
|
||||
|
||||
type ConfigPublishResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *ConfigPublishResponse) GetResponseType() string {
|
||||
return "ConfigPublishResponse"
|
||||
}
|
||||
|
||||
type ConfigRemoveResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *ConfigRemoveResponse) GetResponseType() string {
|
||||
return "ConfigRemoveResponse"
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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 rpc_response
|
||||
|
||||
import (
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
)
|
||||
|
||||
type ConnectResetResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *ConnectResetResponse) GetResponseType() string {
|
||||
return "ConnectResetResponse"
|
||||
}
|
||||
|
||||
type ClientDetectionResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *ClientDetectionResponse) GetResponseType() string {
|
||||
return "ClientDetectionResponse"
|
||||
}
|
||||
|
||||
type ServerCheckResponse struct {
|
||||
*Response
|
||||
ConnectionId string `json:"connectionId"`
|
||||
}
|
||||
|
||||
func (c *ServerCheckResponse) GetResponseType() string {
|
||||
return "ServerCheckResponse"
|
||||
}
|
||||
|
||||
type InstanceResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *InstanceResponse) GetResponseType() string {
|
||||
return "InstanceResponse"
|
||||
}
|
||||
|
||||
type QueryServiceResponse struct {
|
||||
*Response
|
||||
ServiceInfo model.Service `json:"serviceInfo"`
|
||||
}
|
||||
|
||||
func (c *QueryServiceResponse) GetResponseType() string {
|
||||
return "QueryServiceResponse"
|
||||
}
|
||||
|
||||
type SubscribeServiceResponse struct {
|
||||
*Response
|
||||
ServiceInfo model.Service `json:"serviceInfo"`
|
||||
}
|
||||
|
||||
func (c *SubscribeServiceResponse) GetResponseType() string {
|
||||
return "SubscribeServiceResponse"
|
||||
}
|
||||
|
||||
type ServiceListResponse struct {
|
||||
*Response
|
||||
Count int `json:"count"`
|
||||
ServiceNames []string `json:"serviceNames"`
|
||||
}
|
||||
|
||||
func (c *ServiceListResponse) GetResponseType() string {
|
||||
return "ServiceListResponse"
|
||||
}
|
||||
|
||||
type NotifySubscriberResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *NotifySubscriberResponse) GetResponseType() string {
|
||||
return "NotifySubscriberResponse"
|
||||
}
|
||||
|
||||
type HealthCheckResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *HealthCheckResponse) GetResponseType() string {
|
||||
return "HealthCheckResponse"
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *ErrorResponse) GetResponseType() string {
|
||||
return "ErrorResponse"
|
||||
}
|
||||
|
||||
type MockResponse struct {
|
||||
*Response
|
||||
}
|
||||
|
||||
func (c *MockResponse) GetResponseType() string {
|
||||
return "MockResponse"
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* 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 rpc_response
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
)
|
||||
|
||||
var ClientResponseMapping map[string]func() IResponse
|
||||
|
||||
func init() {
|
||||
ClientResponseMapping = make(map[string]func() IResponse)
|
||||
registerClientResponses()
|
||||
}
|
||||
|
||||
type IResponse interface {
|
||||
GetResponseType() string
|
||||
SetRequestId(requestId string)
|
||||
GetBody() string
|
||||
GetErrorCode() int
|
||||
IsSuccess() bool
|
||||
GetResultCode() int
|
||||
GetMessage() string
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
ResultCode int `json:"resultCode"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
RequestId string `json:"requestId"`
|
||||
}
|
||||
|
||||
func (r *Response) SetRequestId(requestId string) {
|
||||
r.RequestId = requestId
|
||||
}
|
||||
|
||||
func (r *Response) GetBody() string {
|
||||
return util.ToJsonString(r)
|
||||
}
|
||||
|
||||
func (r *Response) IsSuccess() bool {
|
||||
return r.Success
|
||||
}
|
||||
|
||||
func (r *Response) GetErrorCode() int {
|
||||
return r.ErrorCode
|
||||
}
|
||||
|
||||
func (r *Response) GetResultCode() int {
|
||||
return r.ResultCode
|
||||
}
|
||||
|
||||
func (r *Response) GetMessage() string {
|
||||
return r.Message
|
||||
}
|
||||
|
||||
func registerClientResponse(response func() IResponse) {
|
||||
responseType := response().GetResponseType()
|
||||
if responseType == "" {
|
||||
logger.Errorf("Register client response error: responseType is nil")
|
||||
return
|
||||
}
|
||||
ClientResponseMapping[responseType] = response
|
||||
}
|
||||
|
||||
func registerClientResponses() {
|
||||
// register InstanceResponse.
|
||||
registerClientResponse(func() IResponse {
|
||||
return &InstanceResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
// register QueryServiceResponse.
|
||||
registerClientResponse(func() IResponse {
|
||||
return &QueryServiceResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
// register SubscribeServiceResponse.
|
||||
registerClientResponse(func() IResponse {
|
||||
return &SubscribeServiceResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
// register ServiceListResponse.
|
||||
registerClientResponse(func() IResponse {
|
||||
return &ServiceListResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
// register NotifySubscriberResponse.
|
||||
registerClientResponse(func() IResponse {
|
||||
return &NotifySubscriberResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
// register HealthCheckResponse.
|
||||
registerClientResponse(func() IResponse {
|
||||
return &HealthCheckResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
// register ErrorResponse.
|
||||
registerClientResponse(func() IResponse {
|
||||
return &ErrorResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
//register ConfigChangeBatchListenResponse
|
||||
registerClientResponse(func() IResponse {
|
||||
return &ConfigChangeBatchListenResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
//register ConfigQueryResponse
|
||||
registerClientResponse(func() IResponse {
|
||||
return &ConfigQueryResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
//register ConfigPublishResponse
|
||||
registerClientResponse(func() IResponse {
|
||||
return &ConfigPublishResponse{Response: &Response{}}
|
||||
})
|
||||
|
||||
//register ConfigRemoveResponse
|
||||
registerClientResponse(func() IResponse {
|
||||
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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 rpc
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"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/logger"
|
||||
"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"
|
||||
)
|
||||
|
||||
//IServerRequestHandler to process the request from server side.
|
||||
type IServerRequestHandler interface {
|
||||
Name() string
|
||||
//RequestReply Handle request from server.
|
||||
RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse
|
||||
}
|
||||
|
||||
type ConnectResetRequestHandler struct {
|
||||
}
|
||||
|
||||
func (c *ConnectResetRequestHandler) Name() string {
|
||||
return "ConnectResetRequestHandler"
|
||||
}
|
||||
|
||||
func (c *ConnectResetRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
|
||||
connectResetRequest, ok := request.(*rpc_request.ConnectResetRequest)
|
||||
if ok {
|
||||
rpcClient.mux.Lock()
|
||||
defer rpcClient.mux.Unlock()
|
||||
if rpcClient.IsRunning() {
|
||||
if connectResetRequest.ServerIp != "" {
|
||||
serverPortNum, err := strconv.Atoi(connectResetRequest.ServerPort)
|
||||
if err != nil {
|
||||
logger.Errorf("ConnectResetRequest ServerPort type conversion error:%+v", err)
|
||||
return nil
|
||||
}
|
||||
rpcClient.switchServerAsync(ServerInfo{serverIp: connectResetRequest.ServerIp, serverPort: uint64(serverPortNum)}, false)
|
||||
} else {
|
||||
rpcClient.switchServerAsync(ServerInfo{}, true)
|
||||
}
|
||||
}
|
||||
return &rpc_response.ConnectResetResponse{Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ClientDetectionRequestHandler struct {
|
||||
}
|
||||
|
||||
func (c *ClientDetectionRequestHandler) Name() string {
|
||||
return "ClientDetectionRequestHandler"
|
||||
}
|
||||
|
||||
func (c *ClientDetectionRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
|
||||
_, ok := request.(*rpc_request.ClientDetectionRequest)
|
||||
if ok {
|
||||
return &rpc_response.ClientDetectionResponse{
|
||||
Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NamingPushRequestHandler struct {
|
||||
ServiceInfoHolder *naming_cache.ServiceInfoHolder
|
||||
}
|
||||
|
||||
func (*NamingPushRequestHandler) Name() string {
|
||||
return "NamingPushRequestHandler"
|
||||
}
|
||||
|
||||
func (c *NamingPushRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
|
||||
notifySubscriberRequest, ok := request.(*rpc_request.NotifySubscriberRequest)
|
||||
if ok {
|
||||
c.ServiceInfoHolder.ProcessService(¬ifySubscriberRequest.ServiceInfo)
|
||||
return &rpc_response.NotifySubscriberResponse{
|
||||
Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -18,7 +18,6 @@ package security
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -26,9 +25,11 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type AuthClient struct {
|
||||
|
@ -50,7 +51,6 @@ func NewAuthClient(clientCfg constant.ClientConfig, serverCfgs []constant.Server
|
|||
serverCfgs: serverCfgs,
|
||||
clientCfg: clientCfg,
|
||||
agent: agent,
|
||||
tokenTtl: 5, // default refresh token 5 second, if first login error
|
||||
accessToken: &atomic.Value{},
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ func (ac *AuthClient) login(server constant.ServerConfig) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
if resp.StatusCode != constant.RESPONSE_CODE_SUCCESS {
|
||||
errMsg := string(bytes)
|
||||
return false, errors.New(errMsg)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 tls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
)
|
||||
|
||||
// NewTLS returns a config structure is used to configure a TLS client
|
||||
func NewTLS(c constant.TLSConfig) (tc *tls.Config, err error) {
|
||||
tc = &tls.Config{}
|
||||
if len(c.CertFile) > 0 && len(c.KeyFile) > 0 {
|
||||
cert, err := certificate(c.CertFile, c.KeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tc.Certificates = []tls.Certificate{*cert}
|
||||
}
|
||||
|
||||
if len(c.CaFile) <= 0 {
|
||||
tc.InsecureSkipVerify = true
|
||||
return tc, nil
|
||||
}
|
||||
if len(c.ServerNameOverride) > 0 {
|
||||
tc.ServerName = c.ServerNameOverride
|
||||
}
|
||||
tc.RootCAs, err = rootCert(c.CaFile)
|
||||
return
|
||||
}
|
||||
|
||||
func rootCert(caFile string) (*x509.CertPool, error) {
|
||||
b, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(b) {
|
||||
return nil, fmt.Errorf("credentials: failed to append certificates")
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func certificate(certFile, keyFile string) (*tls.Certificate, error) {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cert, nil
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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 tls
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
testCaCrt = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIICojCCAYoCCQDbLXd9WTa7rTANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAh3
|
||||
ZS1hcy1jYTAeFw0yMTA4MDQxNTE3MTlaFw00ODEyMjAxNTE3MTlaMBMxETAPBgNV
|
||||
BAMMCHdlLWFzLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyc0D
|
||||
ca+T6zwVnroauoVYQvPEx6R2jxmEgmCEclXegmO0rJ+23nP63nhgDvLN2Yv4olmv
|
||||
d+eh1WfsnmqfdtqUcooZQIYZHWw5jWSYygZOwUWzfIclVFcyfkZnP7qTMGjYPn9Y
|
||||
hOfdgSIh1c/DXrKFu1VQd9p3DevUD+ImAbxYJW4SMgYvliooPABbFU/sm3ZrHPwb
|
||||
Ik8U1KlGHoYtw8KslD0INTfOOEYfQToeZtoAkoajykyteYYbI0kNVYBr2W3AOEXt
|
||||
/QQkj/kAa1o8YKrVkufvi90UI/53SnJa0o5TDzXJCAu4jg4Xfpq0tVogFuEamMeI
|
||||
f2R4JL77flG41nqN2QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCOYw0C4wJTbHly
|
||||
LRmR7lJiLDkdObVKyQM6UUrbY62h9Fu02vYI9a8sj4xVSr3JKTXWBUwXSDqTUqgr
|
||||
+9+sWoWxGwHbDINVHSsy2vnZlhGFkRkdWv3qgAPOn1Dc2ZMVzNEXRnmLFc1X2Ir/
|
||||
niuk4cqSKwFE4IoJz9CHDDOlJzowimTwD6ReIrJDhi0pEFE6YtBVfRF5XPvz3AyG
|
||||
mIFTX9LPRmCBnRi7We9cea+zuFarbjU6qDtf9jDfWANz1Gv6OHf0oM6BoCJ0jp0b
|
||||
tJ5yJe4OCybgpb5bMZygBkGWozeQ5I/XzhkswNN0jVXeC3e0UWLscYvsgPVAM1kH
|
||||
vZvo/wBG
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
testClientCrt = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIICozCCAYsCCQDaqEi3maR5ojANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDDAh3
|
||||
ZS1hcy1jYTAeFw0yMTA4MDQxNTE4MzFaFw0yMjA4MDQxNTE4MzFaMBQxEjAQBgNV
|
||||
BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPN
|
||||
fcRRVPcinxUhS+Q1pvL6nZk7Gg7vcyoOqdX68R/iG/by/dL/wKbYzi2gyMyNN7Q8
|
||||
o1+74inrsS5KMXAuyxrurdudWaoS9eWFzrd9r98+47kxUVwye5R4leT9+NCiI0mp
|
||||
HOqEkOFv+X//kkCtCpDVj/XkfZJ+UrJHgAHvL9me/v5yUvLDDgu7/cdGU1tRwGQc
|
||||
zabzk8SkvoQjMWaZ+R1eIWfeg9lYFyWQyYJhZkAqlBhlyDHw3FfrfPKjrxsI6uDq
|
||||
ACeptHUuMZu6H6EzKDJtnx5DhSrwXTOwEcsOzTl60Hb2wT154CGi+7VbaZDGl6Uf
|
||||
ZBkVAiSZvNCDJ0NGa9kCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAqjg4WeFbUcYC
|
||||
Ko5R6UNTEYUvLYk45hks/Cmu5Mdqe3tFbPsr3EVdqd+zFJrINQQTlhZx14stJjIO
|
||||
b3+eRX7Rldow7AI9Q2dyCQUoWiYmmco//4Mx1jObN2wMUd7tavhwg8RNdps1Yly2
|
||||
/l0Vj2OhNDFnApqAiHZ0NGuCW7CLBvuD2XFCPZLCYFv0aQTw/Vr0+hHvNApmFYn0
|
||||
4wiveiWUf98KKrp93lbzskAd3OZmoNx4bIo/J2Arcz0KzuliBgXDcGPOb4YLRLgs
|
||||
QBhpY/VCGRat52Ys+sm6l/+2Cv+C2mHhn6V4BNVifaZIgOofXwzO4vaQO9sNTZ4f
|
||||
qqJ4/s4nDg==
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
testClientKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAs819xFFU9yKfFSFL5DWm8vqdmTsaDu9zKg6p1frxH+Ib9vL9
|
||||
0v/AptjOLaDIzI03tDyjX7viKeuxLkoxcC7LGu6t251ZqhL15YXOt32v3z7juTFR
|
||||
XDJ7lHiV5P340KIjSakc6oSQ4W/5f/+SQK0KkNWP9eR9kn5SskeAAe8v2Z7+/nJS
|
||||
8sMOC7v9x0ZTW1HAZBzNpvOTxKS+hCMxZpn5HV4hZ96D2VgXJZDJgmFmQCqUGGXI
|
||||
MfDcV+t88qOvGwjq4OoAJ6m0dS4xm7ofoTMoMm2fHkOFKvBdM7ARyw7NOXrQdvbB
|
||||
PXngIaL7tVtpkMaXpR9kGRUCJJm80IMnQ0Zr2QIDAQABAoIBAFMiakpBSMXT7jY4
|
||||
5Pwpin3CPuhAmXXaZSdHDGPx2VdiloeCJrZOpmb+y6XxN6bMjLr7Zpa3KoUzgwLi
|
||||
LyWtnR9gyGZIxNKMXcG4MrJInO7eBzDziqjUdqtZbgUpIMhmj2ZZmRMeJFb4DSaP
|
||||
prHc0IvTEvMgqKb5XYcs5BUA4OD/ihXvpW7GN4c/+iakJN0UBTI0/P/bTiYkERN8
|
||||
ousw8UebrODyUbI20rqL/UO8UyOPIyifFpU29nvn/57Z42TzU+BqdxmJG5VJCR7w
|
||||
lvJhA+jkldorHksHm1Z/9qDMj2UIvuPTJoa6L2t1utJRFgE+27QKHQ9Nv7IjbkUr
|
||||
gdHO/QECgYEA5TneVmplJ4ARg8JIFYBotArcSj4f+Z2pZ36KO2CMklnnTFy2tAuw
|
||||
766yxvZULCU5hr9AwlQwGkqt99o50a8WP4HBGjZ27r6CPODbvZvF7JnsFsD8z178
|
||||
H52GNMO626KogDrC6aoJnYPJQAY+wGhR6Gg05goGoiKdUnhBOzKuEfECgYEAyM3O
|
||||
TUch0FTBmKGA7IWGRL9bQBpw13UtZOokm5g5zTg+yDsQZ5BZCIgycZf77zQmIpbZ
|
||||
TJe8xeFBI8fjfAF+UAfvzwwc4b3dSmD/jUSrv2gcKfCff2wZw8c8sYwbqNpSeX3l
|
||||
Y2m7VJj5fw8I2vMOKjzISNKX55qNc8BGUuLiEGkCgYEA1Gb93ccyuhpSoGuLDdlx
|
||||
q7sQiv7r9AmiqpK3lfON7iK+T6TtawIWTtHrOK+SKWHI31IiuK7377TZZPvibai2
|
||||
jdw2yYpERE9lMPIOy7AnA2lROXhUCfdy2fzGGehwIgqj5kYMzCXSSRGPjvL6fKFt
|
||||
nFPLCImrwdsfOgbSMv9wCpECgYA1n2fxEQbBmHCebrp77ug9IZCfnK/3iW4W3cPq
|
||||
3QrKd7OkSsmFrnFoKt61oO2BIj7wy7G5l2esvAtmH7Hq4oc1nfj3JHft/ILEowR7
|
||||
WBQ5J/claAFfyKFUu7bEfvK/85VEpk8Ebi69V6CAwqYNugxVUSf28m3oRkhx2a2t
|
||||
4rKVyQKBgBYWALJLO3YwzpdelzVJiOPatVrphQarUKafsE32u/iBBvJwfpYpkclh
|
||||
kJ4wLmJAMU8VAhvfSh6T8z8us9z3znONoUI0z6GzwbjROFRtd2WiffXvgcKfTacu
|
||||
q9K53Jum9GDmkbUODa77sWR1zQsdrqSKywcjP/6FYXU9RMDqKUpm
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
|
||||
func Test_NewTLS(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "tls-test")
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
caPath, crtPath, keyPath := filepath.Join(dir, "ca.crt"), filepath.Join(dir, "client.crt"), filepath.Join(dir, "client.key")
|
||||
ioutil.WriteFile(caPath, testCaCrt, 0666)
|
||||
ioutil.WriteFile(crtPath, testClientCrt, 0666)
|
||||
ioutil.WriteFile(keyPath, testClientKey, 0666)
|
||||
|
||||
t.Run("TestNoAuth", func(t *testing.T) {
|
||||
cfg, err := NewTLS(constant.SkipVerifyConfig)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, cfg.InsecureSkipVerify)
|
||||
assert.Nil(t, cfg.RootCAs)
|
||||
assert.Nil(t, cfg.Certificates)
|
||||
|
||||
})
|
||||
|
||||
t.Run("TestClientAuth", func(t *testing.T) {
|
||||
cfg, err := NewTLS(*constant.NewTLSConfig(
|
||||
constant.WithCA(caPath, ""),
|
||||
))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, false, cfg.InsecureSkipVerify)
|
||||
assert.NotNil(t, cfg.RootCAs)
|
||||
assert.Nil(t, cfg.Certificates)
|
||||
|
||||
})
|
||||
|
||||
t.Run("TestServerAuth", func(t *testing.T) {
|
||||
cfg, err := NewTLS(*constant.NewTLSConfig(
|
||||
constant.WithCA(caPath, ""),
|
||||
constant.WithCertificate(crtPath, keyPath),
|
||||
))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, false, cfg.InsecureSkipVerify)
|
||||
assert.NotNil(t, cfg.RootCAs)
|
||||
assert.NotNil(t, cfg.Certificates)
|
||||
})
|
||||
}
|
|
@ -19,9 +19,9 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"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() {
|
||||
|
|
|
@ -20,48 +20,28 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"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 ServerConfig
|
||||
sc := []constant.ServerConfig{
|
||||
{
|
||||
IpAddr: "console.nacos.io",
|
||||
Port: 80,
|
||||
},
|
||||
}
|
||||
//or a more graceful way to create ServerConfig
|
||||
_ = []constant.ServerConfig{
|
||||
*constant.NewServerConfig(
|
||||
"console.nacos.io",
|
||||
80,
|
||||
constant.WithScheme("http"),
|
||||
constant.WithContextPath("/nacos")),
|
||||
*constant.NewServerConfig("127.0.0.1", 8848, constant.WithContextPath("/nacos")),
|
||||
}
|
||||
|
||||
cc := constant.ClientConfig{
|
||||
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", //namespace id
|
||||
TimeoutMs: 5000,
|
||||
NotLoadCacheAtStart: true,
|
||||
LogDir: "/tmp/nacos/log",
|
||||
CacheDir: "/tmp/nacos/cache",
|
||||
LogLevel: "debug",
|
||||
LogStdout: true,
|
||||
}
|
||||
//or a more graceful way to create ClientConfig
|
||||
_ = *constant.NewClientConfig(
|
||||
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"),
|
||||
//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.WithLogStdout(true),
|
||||
)
|
||||
|
||||
// a more graceful way to create config client
|
||||
// create config client
|
||||
client, err := clients.NewConfigClient(
|
||||
vo.NacosClientParam{
|
||||
ClientConfig: &cc,
|
||||
|
@ -88,7 +68,7 @@ func main() {
|
|||
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",
|
||||
|
@ -113,13 +93,15 @@ func main() {
|
|||
},
|
||||
})
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
_, err = client.PublishConfig(vo.ConfigParam{
|
||||
DataId: "test-data",
|
||||
Group: "test-group",
|
||||
Content: "test-listen",
|
||||
})
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
_, err = client.PublishConfig(vo.ConfigParam{
|
||||
DataId: "test-data-2",
|
||||
|
@ -135,14 +117,14 @@ func main() {
|
|||
Group: "test-group",
|
||||
})
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
time.Sleep(1 * time.Second)
|
||||
_, err = client.DeleteConfig(vo.ConfigParam{
|
||||
DataId: "test-data",
|
||||
Group: "test-group",
|
||||
})
|
||||
time.Sleep(5 * time.Second)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
searchPage, _ := client.SearchConfig(vo.SearchConfigParam{
|
||||
searchPage, _ := client.SearchConfig(vo.SearchConfigParm{
|
||||
Search: "blur",
|
||||
DataId: "",
|
||||
Group: "",
|
||||
|
|
|
@ -20,49 +20,30 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"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/model"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/util"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//create ServerConfig
|
||||
sc := []constant.ServerConfig{
|
||||
{
|
||||
IpAddr: "console.nacos.io",
|
||||
Port: 80,
|
||||
},
|
||||
}
|
||||
//or a more graceful way to create ServerConfig
|
||||
_ = []constant.ServerConfig{
|
||||
*constant.NewServerConfig("console.nacos.io", 80),
|
||||
*constant.NewServerConfig("127.0.0.1", 8848, constant.WithContextPath("/nacos")),
|
||||
}
|
||||
|
||||
cc := constant.ClientConfig{
|
||||
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", //namespace id
|
||||
TimeoutMs: 5000,
|
||||
NotLoadCacheAtStart: true,
|
||||
LogDir: "/tmp/nacos/log",
|
||||
CacheDir: "/tmp/nacos/cache",
|
||||
LogRollingConfig: &lumberjack.Logger{MaxSize: 10},
|
||||
LogLevel: "debug",
|
||||
LogStdout: true,
|
||||
}
|
||||
//or a more graceful way to create ClientConfig
|
||||
_ = *constant.NewClientConfig(
|
||||
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"),
|
||||
//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.WithLogRollingConfig(&lumberjack.Logger{MaxSize: 10}),
|
||||
constant.WithLogStdout(true),
|
||||
)
|
||||
|
||||
// a more graceful way to create naming client
|
||||
// create naming client
|
||||
client, err := clients.NewNamingClient(
|
||||
vo.NacosClientParam{
|
||||
ClientConfig: &cc,
|
||||
|
@ -74,12 +55,13 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
//Register with default cluster and group
|
||||
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
|
||||
//Register
|
||||
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.10",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
ClusterName: "cluster-a",
|
||||
Weight: 10,
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
|
@ -87,129 +69,53 @@ func main() {
|
|||
Metadata: map[string]string{"idc": "shanghai"},
|
||||
})
|
||||
|
||||
//Register with cluster name
|
||||
//GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.11",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
Weight: 10,
|
||||
ClusterName: "cluster-a",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
})
|
||||
|
||||
//Register different cluster
|
||||
//GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.12",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
Weight: 10,
|
||||
ClusterName: "cluster-b",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
})
|
||||
|
||||
//Register different group
|
||||
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.13",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
Weight: 10,
|
||||
ClusterName: "cluster-b",
|
||||
GroupName: "group-a",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
})
|
||||
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.14",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
Weight: 10,
|
||||
ClusterName: "cluster-b",
|
||||
GroupName: "group-b",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
})
|
||||
|
||||
//DeRegister with ip,port,serviceName
|
||||
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP
|
||||
//Note:ip=10.0.0.10,port=8848 should belong to the cluster of DEFAULT and the group of DEFAULT_GROUP.
|
||||
//DeRegister
|
||||
ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{
|
||||
Ip: "10.0.0.10",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
Ephemeral: true, //it must be true
|
||||
})
|
||||
|
||||
//DeRegister with ip,port,serviceName,cluster
|
||||
//GroupName=DEFAULT_GROUP
|
||||
//Note:ip=10.0.0.10,port=8848,cluster=cluster-a should belong to the group of DEFAULT_GROUP.
|
||||
ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{
|
||||
Ip: "10.0.0.11",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
Cluster: "cluster-a",
|
||||
Ephemeral: true, //it must be true
|
||||
})
|
||||
|
||||
//DeRegister with ip,port,serviceName,cluster,group
|
||||
ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{
|
||||
Ip: "10.0.0.14",
|
||||
//Register
|
||||
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.10",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
Cluster: "cluster-b",
|
||||
GroupName: "group-b",
|
||||
Ephemeral: true, //it must be true
|
||||
GroupName: "group-a",
|
||||
ClusterName: "cluster-a",
|
||||
Weight: 10,
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
Metadata: map[string]string{"idc": "shanghai"},
|
||||
})
|
||||
|
||||
//Get service with serviceName
|
||||
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_GetService(client, vo.GetServiceParam{
|
||||
ServiceName: "demo.go",
|
||||
})
|
||||
//Get service with serviceName and cluster
|
||||
//GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_GetService(client, vo.GetServiceParam{
|
||||
ServiceName: "demo.go",
|
||||
Clusters: []string{"cluster-a", "cluster-b"},
|
||||
})
|
||||
//Get service with serviceName ,group
|
||||
//ClusterName=DEFAULT
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
//Get service with serviceName, groupName , clusters
|
||||
ExampleServiceClient_GetService(client, vo.GetServiceParam{
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
})
|
||||
|
||||
//SelectAllInstance return all instances,include healthy=false,enable=false,weight<=0
|
||||
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{
|
||||
ServiceName: "demo.go",
|
||||
Clusters: []string{"cluster-a"},
|
||||
})
|
||||
|
||||
//SelectAllInstance
|
||||
//GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{
|
||||
ServiceName: "demo.go",
|
||||
Clusters: []string{"cluster-a", "cluster-b"},
|
||||
})
|
||||
|
||||
//SelectAllInstance
|
||||
//ClusterName=DEFAULT
|
||||
ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
Clusters: []string{"cluster-a"},
|
||||
})
|
||||
|
||||
//SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0
|
||||
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_SelectInstances(client, vo.SelectInstancesParam{
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
Clusters: []string{"cluster-a"},
|
||||
})
|
||||
|
||||
//SelectOneHealthyInstance return one instance by WRR strategy for load balance
|
||||
|
@ -217,38 +123,51 @@ func main() {
|
|||
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
|
||||
ExampleServiceClient_SelectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
Clusters: []string{"cluster-a"},
|
||||
})
|
||||
|
||||
//Subscribe key=serviceName+groupName+cluster
|
||||
//Note:We call add multiple SubscribeCallback with the same key.
|
||||
param := &vo.SubscribeParam{
|
||||
ServiceName: "demo.go",
|
||||
Clusters: []string{"cluster-b"},
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
fmt.Printf("callback111 return services:%s \n\n", util.ToJsonString(services))
|
||||
GroupName: "group-a",
|
||||
SubscribeCallback: func(services []model.Instance, err error) {
|
||||
fmt.Printf("callback return services:%s \n\n", util.ToJsonString(services))
|
||||
},
|
||||
}
|
||||
ExampleServiceClient_Subscribe(client, param)
|
||||
param2 := &vo.SubscribeParam{
|
||||
ServiceName: "demo.go",
|
||||
Clusters: []string{"cluster-b"},
|
||||
SubscribeCallback: func(services []model.SubscribeService, err error) {
|
||||
fmt.Printf("callback222 return services:%s \n\n", util.ToJsonString(services))
|
||||
},
|
||||
}
|
||||
ExampleServiceClient_Subscribe(client, param2)
|
||||
|
||||
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
|
||||
Ip: "10.0.0.112",
|
||||
Ip: "10.0.0.10",
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
ClusterName: "cluster-a",
|
||||
Weight: 10,
|
||||
ClusterName: "cluster-b",
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
Metadata: map[string]string{"idc": "beijing"},
|
||||
})
|
||||
//wait for client pull change from server
|
||||
time.Sleep(10 * time.Second)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
ExampleServiceClient_UpdateServiceInstance(client, vo.UpdateInstanceParam{
|
||||
Ip: "10.0.0.11", //update ip
|
||||
Port: 8848,
|
||||
ServiceName: "demo.go",
|
||||
GroupName: "group-a",
|
||||
ClusterName: "cluster-a",
|
||||
Weight: 10,
|
||||
Enable: true,
|
||||
Healthy: true,
|
||||
Ephemeral: true,
|
||||
Metadata: map[string]string{"idc": "beijing1"}, //update metadata
|
||||
})
|
||||
|
||||
//wait for client pull change from server
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
//Now we just unsubscribe callback1, and callback2 will still receive change event
|
||||
ExampleServiceClient_UnSubscribe(client, param)
|
||||
|
@ -260,7 +179,7 @@ func main() {
|
|||
Cluster: "cluster-b",
|
||||
})
|
||||
//wait for client pull change from server
|
||||
time.Sleep(10 * time.Second)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
//GeAllService will get the list of service name
|
||||
//NameSpace default value is public.If the client set the namespaceId, NameSpace will use it.
|
||||
|
@ -269,10 +188,4 @@ func main() {
|
|||
PageNo: 1,
|
||||
PageSize: 10,
|
||||
})
|
||||
|
||||
ExampleServiceClient_GetAllService(client, vo.GetAllServiceInfoParam{
|
||||
NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f",
|
||||
PageNo: 1,
|
||||
PageSize: 10,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -19,38 +19,65 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
|
||||
"github.com/nacos-group/nacos-sdk-go/vo"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
func ExampleServiceClient_RegisterServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) {
|
||||
success, _ := client.RegisterInstance(param)
|
||||
success, err := client.RegisterInstance(param)
|
||||
if !success || err != nil {
|
||||
panic("RegisterServiceInstance failed!")
|
||||
}
|
||||
fmt.Printf("RegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
|
||||
}
|
||||
|
||||
func ExampleServiceClient_DeRegisterServiceInstance(client naming_client.INamingClient, param vo.DeregisterInstanceParam) {
|
||||
success, _ := client.DeregisterInstance(param)
|
||||
success, err := client.DeregisterInstance(param)
|
||||
if !success || err != nil {
|
||||
panic("DeRegisterServiceInstance failed!")
|
||||
}
|
||||
fmt.Printf("DeRegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
|
||||
}
|
||||
|
||||
func ExampleServiceClient_UpdateServiceInstance(client naming_client.INamingClient, param vo.UpdateInstanceParam) {
|
||||
success, err := client.UpdateInstance(param)
|
||||
if !success || err != nil {
|
||||
panic("UpdateInstance failed!")
|
||||
}
|
||||
fmt.Printf("UpdateServiceInstance,param:%+v,result:%+v \n\n", param, success)
|
||||
}
|
||||
|
||||
func ExampleServiceClient_GetService(client naming_client.INamingClient, param vo.GetServiceParam) {
|
||||
service, _ := client.GetService(param)
|
||||
service, err := client.GetService(param)
|
||||
if err != nil {
|
||||
panic("GetService failed!")
|
||||
}
|
||||
fmt.Printf("GetService,param:%+v, result:%+v \n\n", param, service)
|
||||
}
|
||||
|
||||
func ExampleServiceClient_SelectAllInstances(client naming_client.INamingClient, param vo.SelectAllInstancesParam) {
|
||||
instances, _ := client.SelectAllInstances(param)
|
||||
instances, err := client.SelectAllInstances(param)
|
||||
if err != nil {
|
||||
panic("SelectAllInstances failed!")
|
||||
}
|
||||
fmt.Printf("SelectAllInstance,param:%+v, result:%+v \n\n", param, instances)
|
||||
}
|
||||
|
||||
func ExampleServiceClient_SelectInstances(client naming_client.INamingClient, param vo.SelectInstancesParam) {
|
||||
instances, _ := client.SelectInstances(param)
|
||||
instances, err := client.SelectInstances(param)
|
||||
if err != nil {
|
||||
panic("SelectInstances failed!")
|
||||
}
|
||||
fmt.Printf("SelectInstances,param:%+v, result:%+v \n\n", param, instances)
|
||||
}
|
||||
|
||||
func ExampleServiceClient_SelectOneHealthyInstance(client naming_client.INamingClient, param vo.SelectOneHealthInstanceParam) {
|
||||
instances, _ := client.SelectOneHealthyInstance(param)
|
||||
fmt.Printf("SelectInstances,param:%+v, result:%+v \n\n", param, instances)
|
||||
instances, err := client.SelectOneHealthyInstance(param)
|
||||
if err != nil {
|
||||
panic("SelectOneHealthyInstance failed!")
|
||||
}
|
||||
fmt.Printf("SelectOneHealthyInstance,param:%+v, result:%+v \n\n", param, instances)
|
||||
}
|
||||
|
||||
func ExampleServiceClient_Subscribe(client naming_client.INamingClient, param *vo.SubscribeParam) {
|
||||
|
@ -62,6 +89,9 @@ func ExampleServiceClient_UnSubscribe(client naming_client.INamingClient, param
|
|||
}
|
||||
|
||||
func ExampleServiceClient_GetAllService(client naming_client.INamingClient, param vo.GetAllServiceInfoParam) {
|
||||
service, _ := client.GetAllServicesInfo(param)
|
||||
service, err := client.GetAllServicesInfo(param)
|
||||
if err != nil {
|
||||
panic("GetAllService failed!")
|
||||
}
|
||||
fmt.Printf("GetAllService,param:%+v, result:%+v \n\n", param, service)
|
||||
}
|
||||
|
|
23
go.mod
23
go.mod
|
@ -1,18 +1,19 @@
|
|||
module github.com/nacos-group/nacos-sdk-go
|
||||
module github.com/nacos-group/nacos-sdk-go/v2
|
||||
|
||||
go 1.12
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704
|
||||
github.com/buger/jsonparser v1.1.1
|
||||
github.com/go-errors/errors v1.0.1
|
||||
github.com/golang/mock v1.3.1
|
||||
github.com/json-iterator/go v1.1.6 // indirect
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.5.1
|
||||
go.uber.org/zap v1.15.0
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
||||
google.golang.org/grpc v1.48.0
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
)
|
||||
|
|
528
go.sum
528
go.sum
|
@ -1,95 +1,525 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 h1:PpfENOj/vPfhhy9N2OFRjpue0hjM5XqAp2thFmkXXIk=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
|
||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
vo "github.com/nacos-group/nacos-sdk-go/vo"
|
||||
vo "github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
// MockIConfigClient is a mock of IConfigClient interface
|
||||
|
|
|
@ -23,9 +23,10 @@ package mock
|
|||
import (
|
||||
reflect "reflect"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
constant "github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
http_agent "github.com/nacos-group/nacos-sdk-go/common/http_agent"
|
||||
)
|
||||
|
||||
// MockINacosClient is a mock of INacosClient interface
|
||||
|
|
|
@ -24,8 +24,8 @@ import (
|
|||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
model "github.com/nacos-group/nacos-sdk-go/model"
|
||||
vo "github.com/nacos-group/nacos-sdk-go/vo"
|
||||
model "github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
vo "github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||
)
|
||||
|
||||
// MockINamingClient is a mock of INamingClient interface
|
||||
|
|
|
@ -31,3 +31,16 @@ type ConfigPage struct {
|
|||
PagesAvailable int `param:"pagesAvailable"`
|
||||
PageItems []ConfigItem `param:"pageItems"`
|
||||
}
|
||||
|
||||
type ConfigListenContext struct {
|
||||
Group string `json:"group"`
|
||||
Md5 string `json:"md5"`
|
||||
DataId string `json:"dataId"`
|
||||
Tenant string `json:"tenant"`
|
||||
}
|
||||
|
||||
type ConfigContext struct {
|
||||
Group string `json:"group"`
|
||||
DataId string `json:"dataId"`
|
||||
Tenant string `json:"tenant"`
|
||||
}
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
const (
|
||||
StateRunning = iota
|
||||
|
@ -26,31 +24,32 @@ const (
|
|||
)
|
||||
|
||||
type Instance struct {
|
||||
Valid bool `json:"valid"`
|
||||
Marked bool `json:"marked"`
|
||||
InstanceId string `json:"instanceId"`
|
||||
Port uint64 `json:"port"`
|
||||
Ip string `json:"ip"`
|
||||
Port uint64 `json:"port"`
|
||||
Weight float64 `json:"weight"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Enable bool `json:"enabled"`
|
||||
Ephemeral bool `json:"ephemeral"`
|
||||
ClusterName string `json:"clusterName"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Enable bool `json:"enabled"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Ephemeral bool `json:"ephemeral"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
InstanceHeartBeatInterval int `json:"instanceHeartBeatInterval"`
|
||||
IpDeleteTimeout int `json:"ipDeleteTimeout"`
|
||||
InstanceHeartBeatTimeOut int `json:"instanceHeartBeatTimeOut"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Dom string `json:"dom"`
|
||||
CacheMillis uint64 `json:"cacheMillis"`
|
||||
UseSpecifiedURL bool `json:"useSpecifiedUrl"`
|
||||
Hosts []Instance `json:"hosts"`
|
||||
Checksum string `json:"checksum"`
|
||||
LastRefTime uint64 `json:"lastRefTime"`
|
||||
Env string `json:"env"`
|
||||
Clusters string `json:"clusters"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
Name string `json:"name"`
|
||||
GroupName string `json:"groupName"`
|
||||
Valid bool `json:"valid"`
|
||||
AllIPs bool `json:"allIPs"`
|
||||
ReachProtectionThreshold bool `json:"reachProtectionThreshold"`
|
||||
}
|
||||
|
||||
type ServiceDetail struct {
|
||||
|
@ -86,19 +85,6 @@ type ClusterHealthChecker struct {
|
|||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type SubscribeService struct {
|
||||
ClusterName string `json:"clusterName"`
|
||||
Enable bool `json:"enable"`
|
||||
InstanceId string `json:"instanceId"`
|
||||
Ip string `json:"ip"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
Port uint64 `json:"port"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Valid bool `json:"valid"`
|
||||
Weight float64 `json:"weight"`
|
||||
Healthy bool `json:"healthy"`
|
||||
}
|
||||
|
||||
type BeatInfo struct {
|
||||
Ip string `json:"ip"`
|
||||
Port uint64 `json:"port"`
|
||||
|
|
110
util/common.go
110
util/common.go
|
@ -19,13 +19,14 @@ package util
|
|||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/model"
|
||||
"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/model"
|
||||
)
|
||||
|
||||
func CurrentMillis() int64 {
|
||||
|
@ -65,91 +66,37 @@ func GetConfigCacheKey(dataId string, group string, tenant string) string {
|
|||
return dataId + constant.CONFIG_INFO_SPLITER + group + constant.CONFIG_INFO_SPLITER + tenant
|
||||
}
|
||||
|
||||
var (
|
||||
localIP = ""
|
||||
privateCIDR []*net.IPNet
|
||||
)
|
||||
var localIP = ""
|
||||
|
||||
func LocalIP() string {
|
||||
if localIP != "" {
|
||||
return localIP
|
||||
}
|
||||
|
||||
faces, err := getFaces()
|
||||
if localIP == "" {
|
||||
netInterfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
logger.Errorf("get Interfaces failed,err:%+v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, address := range faces {
|
||||
ipNet, ok := address.(*net.IPNet)
|
||||
if !ok || ipNet.IP.To4() == nil || isFilteredIP(ipNet.IP) {
|
||||
continue
|
||||
}
|
||||
|
||||
localIP = ipNet.IP.String()
|
||||
break
|
||||
}
|
||||
|
||||
if localIP != "" {
|
||||
logger.Infof("Local IP:%s", localIP)
|
||||
}
|
||||
|
||||
return localIP
|
||||
}
|
||||
|
||||
// SetFilterNetNumberAndMask ipMask like 127.0.0.0/8
|
||||
func SetFilterNetNumberAndMask(ipMask ...string) error {
|
||||
var (
|
||||
err error
|
||||
ipNet *net.IPNet
|
||||
)
|
||||
for _, b := range ipMask {
|
||||
_, ipNet, err = net.ParseCIDR(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privateCIDR = append(privateCIDR, ipNet)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// getFaces return addresses from interfaces that is up
|
||||
func getFaces() ([]net.Addr, error) {
|
||||
var upAddrs []net.Addr
|
||||
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
logger.Errorf("get Interfaces failed,err:%+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue
|
||||
}
|
||||
if (iface.Flags & net.FlagLoopback) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
addresses, err := iface.Addrs()
|
||||
for i := 0; i < len(netInterfaces); i++ {
|
||||
if ((netInterfaces[i].Flags & net.FlagUp) != 0) && ((netInterfaces[i].Flags & net.FlagLoopback) == 0) {
|
||||
addrs, err := netInterfaces[i].Addrs()
|
||||
if err != nil {
|
||||
logger.Errorf("get InterfaceAddress failed,err:%+v", err)
|
||||
return nil, err
|
||||
return ""
|
||||
}
|
||||
for _, address := range addrs {
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil {
|
||||
localIP = ipnet.IP.String()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
upAddrs = append(upAddrs, addresses...)
|
||||
}
|
||||
|
||||
return upAddrs, nil
|
||||
}
|
||||
|
||||
func isFilteredIP(ip net.IP) bool {
|
||||
for _, privateIP := range privateCIDR {
|
||||
if privateIP.Contains(ip) {
|
||||
return true
|
||||
if len(localIP) > 0 {
|
||||
logger.Infof("Local IP:%s", localIP)
|
||||
}
|
||||
}
|
||||
return false
|
||||
return localIP
|
||||
}
|
||||
|
||||
func GetDurationWithDefault(metadata map[string]string, key string, defaultDuration time.Duration) time.Duration {
|
||||
|
@ -174,6 +121,17 @@ func GetUrlFormedMap(source map[string]string) (urlEncoded string) {
|
|||
return
|
||||
}
|
||||
|
||||
// get status code by response,default is NA
|
||||
func GetStatusCode(response *http.Response) string {
|
||||
var statusCode string
|
||||
if response != nil {
|
||||
statusCode = strconv.Itoa(response.StatusCode)
|
||||
} else {
|
||||
statusCode = "NA"
|
||||
}
|
||||
return statusCode
|
||||
}
|
||||
|
||||
func DeepCopyMap(params map[string]string) map[string]string {
|
||||
result := make(map[string]string, len(params))
|
||||
for k, v := range params {
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsPrivateIP(t *testing.T) {
|
||||
if err := SetFilterNetNumberAndMask([]string{
|
||||
"10.0.0.0/8", // RFC 1918 IPv4 private network address
|
||||
"100.64.0.0/10", // RFC 6598 IPv4 shared address space
|
||||
"127.0.0.0/8", // RFC 1122 IPv4 loopback address
|
||||
"169.254.0.0/16", // RFC 3927 IPv4 link local address
|
||||
"172.16.0.0/12", // RFC 1918 IPv4 private network address
|
||||
"192.0.0.0/24", // RFC 6890 IPv4 IANA address
|
||||
"192.0.2.0/24", // RFC 5737 IPv4 documentation address
|
||||
"192.168.0.0/16", // RFC 1918 IPv4 private network address
|
||||
}...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tests := []struct {
|
||||
ip string
|
||||
private bool
|
||||
}{
|
||||
// IPv4 private addresses
|
||||
{"10.0.0.1", true}, // private network address
|
||||
{"100.64.0.1", true}, // shared address space
|
||||
{"172.16.0.1", true}, // private network address
|
||||
{"192.168.0.1", true}, // private network address
|
||||
{"192.0.0.1", true}, // IANA address
|
||||
{"192.0.2.1", true}, // documentation address
|
||||
{"127.0.0.1", true}, // loopback address
|
||||
{"127.1.0.1", true}, // loopback address
|
||||
{"169.254.0.1", true}, // link local address
|
||||
|
||||
// IPv4 public addresses
|
||||
{"1.2.3.4", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.ip, func(t *testing.T) {
|
||||
ip := net.ParseIP(tt.ip)
|
||||
if ip == nil {
|
||||
t.Fatalf("%s is not a valid ip address", tt.ip)
|
||||
}
|
||||
if got, want := isFilteredIP(ip), tt.private; got != want {
|
||||
t.Fatalf("got %v for %v want %v", got, ip, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package util
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const SHOW_CONTENT_SIZE = 100
|
||||
|
||||
func TruncateContent(content string) string {
|
||||
if content == "" {
|
||||
return ""
|
||||
}
|
||||
if len(content) <= SHOW_CONTENT_SIZE {
|
||||
return content
|
||||
}
|
||||
return content[0:SHOW_CONTENT_SIZE]
|
||||
}
|
|
@ -22,7 +22,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/common/logger"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
|
||||
)
|
||||
|
||||
func TransformObject2Param(object interface{}) (params map[string]string) {
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
/*
|
||||
* 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 vo
|
||||
|
||||
import "github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
import "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||
|
||||
type NacosClientParam struct {
|
||||
ClientConfig *constant.ClientConfig // optional
|
||||
|
|
|
@ -16,30 +16,22 @@
|
|||
|
||||
package vo
|
||||
|
||||
type ConfigType string
|
||||
|
||||
const (
|
||||
PROPERTIES ConfigType = "properties"
|
||||
XML ConfigType = "xml"
|
||||
JSON ConfigType = "json"
|
||||
TEXT ConfigType = "text"
|
||||
HTML ConfigType = "html"
|
||||
YAML ConfigType = "yaml"
|
||||
)
|
||||
|
||||
type Listener func(namespace, group, dataId, data string)
|
||||
|
||||
type ConfigParam struct {
|
||||
DataId string `param:"dataId"` //required
|
||||
Group string `param:"group"` //required
|
||||
Content string `param:"content"` //required
|
||||
DatumId string `param:"datumId"`
|
||||
Type ConfigType `param:"type"`
|
||||
|
||||
Tag string `param:"tag"`
|
||||
AppName string `param:"appName"`
|
||||
BetaIps string `param:"betaIps"`
|
||||
CasMd5 string `param:"casMd5"`
|
||||
Type string `param:"type"`
|
||||
EncryptedDataKey string `param:"encryptedDataKey"`
|
||||
OnChange func(namespace, group, dataId, data string)
|
||||
}
|
||||
|
||||
type SearchConfigParam struct {
|
||||
type SearchConfigParm struct {
|
||||
Search string `param:"search"`
|
||||
DataId string `param:"dataId"`
|
||||
Group string `param:"group"`
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package vo
|
||||
|
||||
import "github.com/nacos-group/nacos-sdk-go/model"
|
||||
import "github.com/nacos-group/nacos-sdk-go/v2/model"
|
||||
|
||||
type RegisterInstanceParam struct {
|
||||
Ip string `param:"ip"` //required
|
||||
|
@ -43,13 +43,14 @@ type DeregisterInstanceParam struct {
|
|||
type UpdateInstanceParam struct {
|
||||
Ip string `param:"ip"` //required
|
||||
Port uint64 `param:"port"` //required
|
||||
ClusterName string `param:"cluster"` // optional,default:DEFAULT
|
||||
Weight float64 `param:"weight"` //required,it must be lager than 0
|
||||
Enable bool `param:"enabled"` //required,the instance can be access or not
|
||||
Healthy bool `param:"healthy"` //required,the instance is health or not
|
||||
Metadata map[string]string `param:"metadata"` //optional
|
||||
ClusterName string `param:"clusterName"` //optional,default:DEFAULT
|
||||
ServiceName string `param:"serviceName"` //required
|
||||
GroupName string `param:"groupName"` //optional,default:DEFAULT_GROUP
|
||||
Ephemeral bool `param:"ephemeral"` //optional
|
||||
Weight float64 `param:"weight"` // required,it must be lager than 0
|
||||
Enable bool `param:"enabled"` // required,the instance can be access or not
|
||||
Metadata map[string]string `param:"metadata"` // optional
|
||||
}
|
||||
|
||||
type GetServiceParam struct {
|
||||
|
@ -69,7 +70,7 @@ type SubscribeParam struct {
|
|||
ServiceName string `param:"serviceName"` //required
|
||||
Clusters []string `param:"clusters"` //optional,default:DEFAULT
|
||||
GroupName string `param:"groupName"` //optional,default:DEFAULT_GROUP
|
||||
SubscribeCallback func(services []model.SubscribeService, err error) //required
|
||||
SubscribeCallback func(services []model.Instance, err error) //required
|
||||
}
|
||||
|
||||
type SelectAllInstancesParam struct {
|
||||
|
|
Loading…
Reference in New Issue