Compare commits

...

6 Commits

Author SHA1 Message Date
binbin.zhang 9eef0329a7
update mod v2 (#265) 2021-06-10 23:30:32 +08:00
binbin.zhang fb490395ce
fix instance chooser (#254) 2021-05-31 09:14:44 +08:00
binbin.zhang 7a2d20f2bf
update naming&config ut (#253) 2021-05-30 20:38:14 +08:00
binbin.zhang a42d35ad2c
update 2.0.0 example (#248) 2021-05-25 17:29:25 +08:00
binbin.zhang b718836ea4
config support grpc (#242) 2021-05-23 22:03:03 +08:00
binbin.zhang 8cb8d63e7e
2.0.0 (#203)
* 2.0.0 naming support grpc
2021-04-29 21:54:40 +08:00
71 changed files with 4232 additions and 2650 deletions

View File

@ -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",
}

View File

@ -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) {
}
}

View File

@ -24,10 +24,10 @@ import (
"strconv" "strconv"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/nacos-group/nacos-sdk-go/common/file" "github.com/nacos-group/nacos-sdk-go/v2/common/file"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
) )
func GetFileName(cacheKey string, cacheDir string) string { func GetFileName(cacheKey string, cacheDir string) string {

View File

@ -19,12 +19,13 @@ package clients
import ( import (
"errors" "errors"
"github.com/nacos-group/nacos-sdk-go/clients/config_client" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_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/v2/clients/config_client"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"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/vo" "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 // CreateConfigClient use to create config client
@ -48,7 +49,7 @@ func NewConfigClient(param vo.NacosClientParam) (iClient config_client.IConfigCl
if err != nil { if err != nil {
return return
} }
iClient = &config iClient = config
return return
} }
@ -61,7 +62,7 @@ func NewNamingClient(param vo.NacosClientParam) (iClient naming_client.INamingCl
if err != nil { if err != nil {
return return
} }
iClient = &naming iClient = naming
return return
} }

View File

@ -4,8 +4,8 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/vo" "github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )

View File

@ -19,23 +19,28 @@ package config_client
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"os" "os"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/aliyun/alibaba-cloud-sdk-go/services/kms" "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
"github.com/nacos-group/nacos-sdk-go/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client" "github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error"
"github.com/nacos-group/nacos-sdk-go/common/nacos_error" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/inner/uuid"
"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"
)
const (
perTaskConfigSize = 3000
executorErrDelay = 5 * time.Second
) )
type ConfigClient struct { type ConfigClient struct {
@ -43,32 +48,27 @@ type ConfigClient struct {
kmsClient *kms.Client kmsClient *kms.Client
localConfigs []vo.ConfigParam localConfigs []vo.ConfigParam
mutex sync.Mutex mutex sync.Mutex
configProxy ConfigProxy configProxy IConfigProxy
configCacheDir string configCacheDir string
lastAllSyncTime time.Time
cacheMap cache.ConcurrentMap
uid string
listenExecute chan struct{}
} }
const (
perTaskConfigSize = 3000
executorErrDelay = 5 * time.Second
)
var (
currentTaskCount int
cacheMap = cache.NewConcurrentMap()
schedulerMap = cache.NewConcurrentMap()
)
type cacheData struct { type cacheData struct {
isInitializing bool isInitializing bool
dataId string dataId string
group string group string
content string content string
contentType string
tenant string tenant string
cacheDataListener *cacheDataListener cacheDataListener *cacheDataListener
md5 string md5 string
appName string appName string
taskId int taskId int
configClient *ConfigClient configClient *ConfigClient
isSyncWithServer bool
} }
type cacheDataListener struct { type cacheDataListener struct {
@ -76,65 +76,58 @@ type cacheDataListener struct {
lastMd5 string lastMd5 string
} }
func init() { func NewConfigClient(nc nacos_client.INacosClient) (*ConfigClient, error) {
schedulerMap.Set("root", true) config := &ConfigClient{}
go delayScheduler(time.NewTimer(1*time.Millisecond), 10*time.Millisecond, "root", listenConfigExecutor())
}
func NewConfigClient(nc nacos_client.INacosClient) (ConfigClient, error) {
config := ConfigClient{}
config.INacosClient = nc config.INacosClient = nc
clientConfig, err := nc.GetClientConfig() clientConfig, err := nc.GetClientConfig()
if err != nil { if err != nil {
return config, err return nil, err
} }
serverConfig, err := nc.GetServerConfig() serverConfig, err := nc.GetServerConfig()
if err != nil { if err != nil {
return config, err return nil, err
} }
httpAgent, err := nc.GetHttpAgent() httpAgent, err := nc.GetHttpAgent()
if err != nil { if err != nil {
return config, err return nil, err
} }
err = logger.InitLogger(logger.Config{
if err = initLogger(clientConfig); err != nil {
return nil, err
}
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 nil, err
}
config.kmsClient = kmsClient
}
uid, err := uuid.NewV4()
if err != nil {
return nil, err
}
config.uid = uid.String()
config.cacheMap = cache.NewConcurrentMap()
config.listenExecute = make(chan struct{}, 1)
config.startInternal()
return config, err
}
func initLogger(clientConfig constant.ClientConfig) error {
return logger.InitLogger(logger.Config{
Level: clientConfig.LogLevel, Level: clientConfig.LogLevel,
OutputPath: clientConfig.LogDir, OutputPath: clientConfig.LogDir,
RotationTime: clientConfig.RotateTime, RotationTime: clientConfig.RotateTime,
MaxAge: clientConfig.MaxAge, MaxAge: clientConfig.MaxAge,
}) })
if err != nil {
return config, err
}
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
}
config.kmsClient = kmsClient
}
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 (client *ConfigClient) GetConfig(param vo.ConfigParam) (content string, err error) { func (client *ConfigClient) GetConfig(param vo.ConfigParam) (content string, err error) {
@ -186,55 +179,55 @@ func (client *ConfigClient) getConfigInner(param vo.ConfigParam) (content string
return "", err return "", err
} }
if len(param.Group) <= 0 { if len(param.Group) <= 0 {
err = errors.New("[client.GetConfig] param.group can not be empty") param.Group = constant.DEFAULT_GROUP
return "", err
} }
//todo 优先使用本地配置
//todo 获取容灾配置的 EncryptedDataKey LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover
clientConfig, _ := client.GetClientConfig() clientConfig, _ := client.GetClientConfig()
cacheKey := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId) cacheKey := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)
content, err = client.configProxy.GetConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey) response, err := client.configProxy.queryConfig(param.DataId, param.Group, clientConfig.NamespaceId,
clientConfig.TimeoutMs, false, client)
if err != nil { if err != nil {
logger.Infof("get config from server error:%+v ", err) logger.Infof("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, "")
return "", errors.New("config not found")
}
if nacosErr.ErrorCode() == "403" {
return "", errors.New("get config forbidden")
}
}
content, err = cache.ReadConfigFromFile(cacheKey, client.configCacheDir) content, err = cache.ReadConfigFromFile(cacheKey, client.configCacheDir)
if err != nil { if err != nil {
logger.Errorf("get config from cache error:%+v ", err) logger.Errorf("get config from cache error:%+v ", err)
return "", errors.New("read config from both server and cache fail") return "", errors.New("read config from both server and cache fail")
} }
} else {
cache.WriteConfigToFile(cacheKey, client.configCacheDir, content)
}
return content, nil return content, nil
}
return response.Content, nil
} }
func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool, func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool, err error) {
err error) {
if len(param.DataId) <= 0 { if len(param.DataId) <= 0 {
err = errors.New("[client.PublishConfig] param.dataId can not be empty") 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")
}
if len(param.Content) <= 0 { if len(param.Content) <= 0 {
err = errors.New("[client.PublishConfig] param.content can not be empty") err = errors.New("[client.PublishConfig] param.content can not be empty")
} }
if len(param.Group) <= 0 {
param.Group = constant.DEFAULT_GROUP
}
if err != nil {
return false, err
}
param.Content, err = client.encrypt(param.DataId, param.Content) param.Content, err = client.encrypt(param.DataId, param.Content)
if err != nil { if err != nil {
return false, err return false, err
} }
clientConfig, _ := client.GetClientConfig() 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)
return response.IsSuccess(), err
} }
func (client *ConfigClient) DeleteConfig(param vo.ConfigParam) (deleted bool, err error) { func (client *ConfigClient) DeleteConfig(param vo.ConfigParam) (deleted bool, err error) {
@ -242,11 +235,17 @@ func (client *ConfigClient) DeleteConfig(param vo.ConfigParam) (deleted bool, er
err = errors.New("[client.DeleteConfig] param.dataId can not be empty") err = errors.New("[client.DeleteConfig] param.dataId can not be empty")
} }
if len(param.Group) <= 0 { 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() 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)
return response.IsSuccess(), err
} }
//Cancel Listen Config //Cancel Listen Config
@ -256,33 +255,11 @@ func (client *ConfigClient) CancelListenConfig(param vo.ConfigParam) (err error)
logger.Errorf("[checkConfigInfo.GetClientConfig] failed,err:%+v", err) logger.Errorf("[checkConfigInfo.GetClientConfig] failed,err:%+v", err)
return return
} }
cacheMap.Remove(util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)) client.cacheMap.Remove(util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId))
logger.Infof("Cancel listen config DataId:%s Group:%s", param.DataId, param.Group) logger.Infof("Cancel listen config DataId:%s Group:%s", param.DataId, param.Group)
remakeId := int(math.Ceil(float64(cacheMap.Count()) / float64(perTaskConfigSize)))
if remakeId < currentTaskCount {
remakeCacheDataTaskId(remakeId)
}
return err return err
} }
//Remake cache data taskId
func remakeCacheDataTaskId(remakeId int) {
for i := 0; i < remakeId; i++ {
count := 0
for _, key := range cacheMap.Keys() {
if count == perTaskConfigSize {
break
}
if value, ok := cacheMap.Get(key); ok {
cData := value.(cacheData)
cData.taskId = i
cacheMap.Set(key, cData)
}
count++
}
}
}
func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) { func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
if len(param.DataId) <= 0 { if len(param.DataId) <= 0 {
err = errors.New("[client.ListenConfig] DataId can not be empty") err = errors.New("[client.ListenConfig] DataId can not be empty")
@ -300,7 +277,7 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
key := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId) key := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)
var cData cacheData var cData cacheData
if v, ok := cacheMap.Get(key); ok { if v, ok := client.cacheMap.Get(key); ok {
cData = v.(cacheData) cData = v.(cacheData)
cData.isInitializing = true cData.isInitializing = true
} else { } else {
@ -328,150 +305,11 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
content: content, content: content,
md5: md5Str, md5: md5Str,
cacheDataListener: listener, cacheDataListener: listener,
taskId: cacheMap.Count() / perTaskConfigSize, taskId: client.cacheMap.Count() / perTaskConfigSize,
configClient: client, configClient: client,
} }
} }
cacheMap.Set(key, cData) 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 delayScheduler(t *time.Timer, delay time.Duration, taskId string, execute func() error) {
for {
if v, ok := 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 listenConfigExecutor() func() error {
return func() error {
listenerSize := cacheMap.Count()
taskCount := int(math.Ceil(float64(listenerSize) / float64(perTaskConfigSize)))
if taskCount > currentTaskCount {
for i := currentTaskCount; i < taskCount; i++ {
schedulerMap.Set(strconv.Itoa(i), true)
go delayScheduler(time.NewTimer(1*time.Millisecond), 10*time.Millisecond, strconv.Itoa(i), longPulling(i))
}
currentTaskCount = taskCount
} else if taskCount < currentTaskCount {
for i := taskCount; i < currentTaskCount; i++ {
if _, ok := schedulerMap.Get(strconv.Itoa(i)); ok {
schedulerMap.Set(strconv.Itoa(i), false)
}
}
currentTaskCount = taskCount
}
return nil
}
}
//Long polling listening configuration
func longPulling(taskId int) func() error {
return func() error {
var listeningConfigs string
var client *ConfigClient
initializationList := make([]cacheData, 0)
for _, key := range cacheMap.Keys() {
if value, ok := cacheMap.Get(key); ok {
cData := value.(cacheData)
client = cData.configClient
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
cacheMap.Set(util.GetConfigCacheKey(v.dataId, v.group, clientConfig.NamespaceId), 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) {
changedConfigs := strings.Split(changed, "%01")
for _, config := range changedConfigs {
attrs := strings.Split(config, "%02")
if len(attrs) >= 2 {
if value, ok := 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
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 return
} }
@ -479,42 +317,6 @@ func (client *ConfigClient) SearchConfig(param vo.SearchConfigParm) (*model.Conf
return client.searchConfigInner(param) 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) 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.SearchConfigParm) (*model.ConfigPage, error) { func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParm) (*model.ConfigPage, error) {
if param.Search != "accurate" && param.Search != "blur" { if param.Search != "accurate" && param.Search != "blur" {
return nil, errors.New("[client.searchConfigInner] param.search must be accurate or blur") return nil, errors.New("[client.searchConfigInner] param.search must be accurate or blur")
@ -526,7 +328,7 @@ func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParm) (*model
param.PageSize = 10 param.PageSize = 10
} }
clientConfig, _ := client.GetClientConfig() 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 { if err != nil {
logger.Errorf("search config from server error:%+v ", err) logger.Errorf("search config from server error:%+v ", err)
if _, ok := err.(*nacos_error.NacosError); ok { if _, ok := err.(*nacos_error.NacosError); ok {
@ -542,3 +344,130 @@ func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParm) (*model
} }
return configItems, nil 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.Now().Sub(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(string(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()
}
}
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{}{}
}

View File

@ -17,8 +17,8 @@
package config_client package config_client
import ( import (
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/vo" "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 //go:generate mockgen -destination ../../mock/mock_config_client_interface.go -package mock -source=./config_client_interface.go
@ -64,6 +64,4 @@ type IConfigClient interface {
// pageNo option,default is 1 // pageNo option,default is 1
// pageSize option,default is 10 // pageSize option,default is 10
SearchConfig(param vo.SearchConfigParm) (*model.ConfigPage, error) SearchConfig(param vo.SearchConfigParm) (*model.ConfigPage, error)
PublishAggr(param vo.ConfigParam) (published bool, err error)
} }

View File

@ -17,47 +17,21 @@
package config_client package config_client
import ( import (
"errors"
"fmt"
"net/http"
"runtime"
"strconv"
"testing" "testing"
"time"
"github.com/golang/mock/gomock" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/model"
"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/v2/clients/nacos_client"
"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/vo" "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" "github.com/stretchr/testify/assert"
) )
var goVersion = runtime.Version() var serverConfigWithOptions = constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos"))
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 clientConfigWithOptions = constant.NewClientConfig( var clientConfigWithOptions = constant.NewClientConfig(
constant.WithTimeoutMs(10*1000), constant.WithTimeoutMs(10*1000),
@ -65,116 +39,69 @@ var clientConfigWithOptions = constant.NewClientConfig(
constant.WithNotLoadCacheAtStart(true), constant.WithNotLoadCacheAtStart(true),
) )
var (
dataIdKey = goVersion + "dataId"
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": "group",
}
var configParamTest = vo.ConfigParam{
DataId: dataIdKey,
Group: "group",
}
var localConfigTest = vo.ConfigParam{ var localConfigTest = vo.ConfigParam{
DataId: dataIdKey, DataId: "dataId",
Group: "group", Group: "group",
Content: "content", Content: "content",
} }
var localConfigMapTest = map[string]string{ func createConfigClientTest() *ConfigClient {
"dataId": dataIdKey,
"group": "group",
"content": "content",
}
var headerTest = map[string][]string{
"Content-Type": {"application/x-www-form-urlencoded"},
}
var headerListenerTest = map[string][]string{
"Content-Type": {"application/x-www-form-urlencoded"},
"Listening-Configs": {"30000"},
}
var serverConfigsTest = []constant.ServerConfig{serverConfigTest}
var httpAgentTest = mock.MockIHttpAgent{}
func createConfigClientTest() ConfigClient {
nc := nacos_client.NacosClient{} nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions}) _ = nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
nc.SetClientConfig(*clientConfigWithOptions) _ = nc.SetClientConfig(*clientConfigWithOptions)
nc.SetHttpAgent(&http_agent.HttpAgent{}) _ = nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewConfigClient(&nc) client, _ := NewConfigClient(&nc)
client.configProxy = &MockConfigProxy{}
return client return client
} }
func createConfigClientTestWithTenant() ConfigClient { type MockConfigProxy struct {
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTestWithTenant)
nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewConfigClient(&nc)
return client
} }
func createConfigClientHttpTest(mockHttpAgent http_agent.IHttpAgent) ConfigClient { func (m *MockConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
nc := nacos_client.NacosClient{} return &rpc_response.ConfigQueryResponse{Content: "hello world"}, nil
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockHttpAgent)
client, _ := NewConfigClient(&nc)
return client
} }
func (m *MockConfigProxy) searchConfigProxy(param vo.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error) {
func createConfigClientHttpTestWithTenant(mockHttpAgent http_agent.IHttpAgent) ConfigClient { return &model.ConfigPage{TotalCount: 1}, nil
nc := nacos_client.NacosClient{} }
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest}) func (m *MockConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) {
nc.SetClientConfig(clientConfigTestWithTenant) return &rpc_response.MockResponse{Response: &rpc_response.Response{Success: true}}, nil
nc.SetHttpAgent(mockHttpAgent) }
client, _ := NewConfigClient(&nc) func (m *MockConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
return client return &rpc.RpcClient{}
}
func (m *MockConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
return &rpc.RpcClient{}
} }
func Test_GetConfig(t *testing.T) { func Test_GetConfig(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
success, err := client.PublishConfig(vo.ConfigParam{ success, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "group", Group: localConfigTest.Group,
Content: "hello world!222222"}) Content: "hello world"})
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, success) assert.True(t, success)
content, err := client.GetConfig(vo.ConfigParam{ content, err := client.GetConfig(vo.ConfigParam{
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "group"}) Group: "group"})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "hello world!222222", content) assert.Equal(t, "hello world", content)
} }
func Test_SearchConfig(t *testing.T) { func Test_SearchConfig(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
client.PublishConfig(vo.ConfigParam{ _, _ = client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "groDEFAULT_GROUPup", Group: "DEFAULT_GROUP",
Content: "hello world!222222"}) Content: "hello world"})
configPage, err := client.SearchConfig(vo.SearchConfigParm{ configPage, err := client.SearchConfig(vo.SearchConfigParm{
Search: "accurate", Search: "accurate",
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "groDEFAULT_GROUPup", Group: "DEFAULT_GROUP",
PageNo: 1, PageNo: 1,
PageSize: 10, PageSize: 10,
}) })
@ -182,86 +109,7 @@ func Test_SearchConfig(t *testing.T) {
assert.NotEmpty(t, configPage) assert.NotEmpty(t, configPage)
} }
func Test_GetConfigWithErrorResponse_401(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockHttpAgent := mock.NewMockIHttpAgent(controller)
client := createConfigClientHttpTest(mockHttpAgent)
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodGet),
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)
result, err := client.GetConfig(configParamTest)
assert.Nil(t, err)
fmt.Printf("result:%s \n", result)
}
func Test_GetConfigWithErrorResponse_404(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockHttpAgent := mock.NewMockIHttpAgent(controller)
client := createConfigClientHttpTest(mockHttpAgent)
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodGet),
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(404, ""), nil)
reslut, err := client.GetConfig(configParamTest)
assert.NotNil(t, err)
assert.Equal(t, "", reslut)
fmt.Println(err.Error())
}
func Test_GetConfigWithErrorResponse_403(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockHttpAgent := mock.NewMockIHttpAgent(controller)
client := createConfigClientHttpTest(mockHttpAgent)
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodGet),
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(403, ""), nil)
reslut, err := client.GetConfig(configParamTest)
assert.NotNil(t, err)
assert.Equal(t, "", reslut)
fmt.Println(err.Error())
}
func Test_GetConfigWithCache(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockHttpAgent := mock.NewMockIHttpAgent(controller)
client := createConfigClientHttpTest(mockHttpAgent)
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodGet),
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, "content"), nil)
content, err := client.GetConfig(configParamTest)
assert.Nil(t, err)
assert.Equal(t, "content", content)
mockHttpAgent.EXPECT().Request(gomock.Eq(http.MethodGet),
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)
content, err = client.GetConfig(configParamTest)
assert.Nil(t, err)
assert.Equal(t, "content", content)
}
// PublishConfig // PublishConfig
func Test_PublishConfigWithoutDataId(t *testing.T) { func Test_PublishConfigWithoutDataId(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
_, err := client.PublishConfig(vo.ConfigParam{ _, err := client.PublishConfig(vo.ConfigParam{
@ -272,20 +120,10 @@ func Test_PublishConfigWithoutDataId(t *testing.T) {
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func Test_PublishConfigWithoutGroup(t *testing.T) {
client := createConfigClientTest()
_, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey,
Group: "",
Content: "content",
})
assert.NotNil(t, err)
}
func Test_PublishConfigWithoutContent(t *testing.T) { func Test_PublishConfigWithoutContent(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
_, err := client.PublishConfig(vo.ConfigParam{ _, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "group", Group: "group",
Content: "", Content: "",
}) })
@ -297,56 +135,21 @@ func Test_PublishConfig(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
success, err := client.PublishConfig(vo.ConfigParam{ success, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "group", Group: "group",
Content: "hello world2!"}) Content: "hello world"})
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, success) assert.True(t, success)
} }
func Test_PublishConfigWithErrorResponse(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockHttpAgent := mock.NewMockIHttpAgent(controller)
client := 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 := client.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)
client := 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 := client.PublishConfig(localConfigTest)
assert.NotNil(t, err)
assert.True(t, !success)
}
// DeleteConfig // DeleteConfig
func Test_DeleteConfig(t *testing.T) { func Test_DeleteConfig(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
success, err := client.PublishConfig(vo.ConfigParam{ success, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "group", Group: "group",
Content: "hello world!"}) Content: "hello world!"})
@ -354,49 +157,13 @@ func Test_DeleteConfig(t *testing.T) {
assert.True(t, success) assert.True(t, success)
success, err = client.DeleteConfig(vo.ConfigParam{ success, err = client.DeleteConfig(vo.ConfigParam{
DataId: dataIdKey, DataId: localConfigTest.DataId,
Group: "group"}) Group: "group"})
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, success) assert.True(t, success)
} }
func Test_DeleteConfigWithErrorResponse_200(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockHttpAgent := mock.NewMockIHttpAgent(controller)
client := 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 := client.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)
client := 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 := client.DeleteConfig(configParamTest)
assert.NotNil(t, err)
assert.Equal(t, false, success)
}
func Test_DeleteConfigWithoutDataId(t *testing.T) { func Test_DeleteConfigWithoutDataId(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
success, err := client.DeleteConfig(vo.ConfigParam{ success, err := client.DeleteConfig(vo.ConfigParam{
@ -407,59 +174,21 @@ func Test_DeleteConfigWithoutDataId(t *testing.T) {
assert.Equal(t, false, success) assert.Equal(t, false, success)
} }
func Test_DeleteConfigWithoutGroup(t *testing.T) {
client := createConfigClientTest()
success, err := client.DeleteConfig(vo.ConfigParam{
DataId: dataIdKey,
Group: "",
})
assert.NotNil(t, err)
assert.Equal(t, false, success)
}
// ListenConfig
func TestListen(t *testing.T) { func TestListen(t *testing.T) {
// ListenConfig
t.Run("TestListenConfig", func(t *testing.T) { t.Run("TestListenConfig", func(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
key := util.GetConfigCacheKey(localConfigTest.DataId, localConfigTest.Group, clientConfigTest.NamespaceId) err := client.ListenConfig(vo.ConfigParam{
cache.WriteConfigToFile(key, client.configCacheDir, "")
var err error
var success bool
ch := make(chan string)
go func() {
err = client.ListenConfig(vo.ConfigParam{
DataId: localConfigTest.DataId, DataId: localConfigTest.DataId,
Group: localConfigTest.Group, Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) { OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
ch <- data
}, },
}) })
assert.Nil(t, err) assert.Nil(t, err)
}()
time.Sleep(2 * time.Second)
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):
fmt.Println("timeout")
assert.Errorf(t, errors.New("timeout"), "timeout")
}
}) })
// ListenConfig no dataId // ListenConfig no dataId
t.Run("TestListenConfigNoDataId", func(t *testing.T) { t.Run("TestListenConfigNoDataId", func(t *testing.T) {
listenConfigParam := vo.ConfigParam{ listenConfigParam := vo.ConfigParam{
Group: "gateway", Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) { OnChange: func(namespace, group, dataId, data string) {
}, },
} }
@ -467,107 +196,6 @@ func TestListen(t *testing.T) {
err := client.ListenConfig(listenConfigParam) err := client.ListenConfig(listenConfigParam)
assert.Error(t, err) assert.Error(t, err)
}) })
// ListenConfig no change
t.Run("TestListenConfigNoChange", func(t *testing.T) {
client := createConfigClientTest()
key := util.GetConfigCacheKey(configNoChangeKey, localConfigTest.Group, clientConfigTest.NamespaceId)
cache.WriteConfigToFile(key, client.configCacheDir, localConfigTest.Content)
var err error
var success bool
var content string
go func() {
err = client.ListenConfig(vo.ConfigParam{
DataId: configNoChangeKey,
Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) {
content = "data"
},
})
assert.Nil(t, err)
}()
time.Sleep(2 * time.Second)
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,
Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
ch <- data
},
}
client := createConfigClientTest()
key := util.GetConfigCacheKey(listenConfigParam.DataId, listenConfigParam.Group, clientConfigTest.NamespaceId)
cache.WriteConfigToFile(key, client.configCacheDir, "")
client.ListenConfig(listenConfigParam)
client1 := createConfigClientTest()
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):
fmt.Println("timeout")
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,
Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
ch <- data
},
}
client := createConfigClientTest()
key := util.GetConfigCacheKey(listenConfigParam.DataId, listenConfigParam.Group, clientConfigTest.NamespaceId)
cache.WriteConfigToFile(key, client.configCacheDir, "")
client.ListenConfig(listenConfigParam)
client1 := createConfigClientTest()
client1.ListenConfig(listenConfigParam)
success, err := client.PublishConfig(vo.ConfigParam{
DataId: multipleClientsMultipleConfigsKey,
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):
fmt.Println("timeout")
assert.Errorf(t, errors.New("timeout"), "timeout")
}
})
} }
// CancelListenConfig // CancelListenConfig
@ -576,120 +204,24 @@ func TestCancelListenConfig(t *testing.T) {
t.Run("TestMultipleListenersCancelOne", func(t *testing.T) { t.Run("TestMultipleListenersCancelOne", func(t *testing.T) {
client := createConfigClientTest() client := createConfigClientTest()
var err error var err error
var success bool
var context string
listenConfigParam := vo.ConfigParam{ listenConfigParam := vo.ConfigParam{
DataId: cancelOneKey, DataId: localConfigTest.DataId,
Group: "group", Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) { OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
}, },
} }
listenConfigParam1 := vo.ConfigParam{ listenConfigParam1 := vo.ConfigParam{
DataId: cancelOne1Key, DataId: localConfigTest.DataId + "1",
Group: "group1",
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group1:" + group + ", dataId1:" + dataId + ", data:" + data)
context = data
},
}
go func() {
client.ListenConfig(listenConfigParam)
}()
go func() {
client.ListenConfig(listenConfigParam1)
}()
fmt.Println("Start listening")
for i := 1; i <= 5; i++ {
go func() {
success, err = client.PublishConfig(vo.ConfigParam{
DataId: cancelOneKey,
Group: "group",
Content: "abcd" + strconv.Itoa(i)})
}()
go func() {
success, err = client.PublishConfig(vo.ConfigParam{
DataId: cancelOne1Key,
Group: "group1",
Content: "abcd" + strconv.Itoa(i)})
}()
if i == 3 {
client.CancelListenConfig(listenConfigParam)
fmt.Println("Cancel listen config")
}
time.Sleep(2 * time.Second)
assert.Nil(t, err)
assert.Equal(t, true, success)
}
assert.Equal(t, "abcd5", context)
})
t.Run("TestCancelListenConfig", func(t *testing.T) {
var context string
var err error
ch := make(chan string)
client := createConfigClientTest()
//
key := util.GetConfigCacheKey(localConfigTest.DataId, localConfigTest.Group, clientConfigTest.NamespaceId)
cache.WriteConfigToFile(key, client.configCacheDir, "")
listenConfigParam := vo.ConfigParam{
DataId: cancelListenConfigKey,
Group: localConfigTest.Group, Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) { OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
context = data
ch <- data
}, },
} }
go func() { _ = client.ListenConfig(listenConfigParam)
err = client.ListenConfig(listenConfigParam)
assert.Nil(t, err)
}()
success, err := client.PublishConfig(vo.ConfigParam{
DataId: cancelListenConfigKey,
Group: localConfigTest.Group,
Content: localConfigTest.Content})
assert.Nil(t, err)
assert.Equal(t, true, success)
select { _ = client.ListenConfig(listenConfigParam1)
case c := <-ch:
assert.Equal(t, c, localConfigTest.Content)
}
//Cancel listen config
client.CancelListenConfig(listenConfigParam)
success, err = client.PublishConfig(vo.ConfigParam{ err = client.CancelListenConfig(listenConfigParam)
DataId: cancelListenConfigKey,
Group: localConfigTest.Group,
Content: "abcd"})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, true, success)
assert.Equal(t, localConfigTest.Content, context)
}) })
} }
func TestGetConfigWithSpecialSymbol(t *testing.T) {
contentStr := "hello world!!@#$%^&&*()"
client := createConfigClientTest()
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)
}

View File

@ -21,15 +21,19 @@ import (
"errors" "errors"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"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/v2/clients/cache"
"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/vo" "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 { type ConfigProxy struct {
@ -37,34 +41,35 @@ type ConfigProxy struct {
clientConfig constant.ClientConfig 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{} proxy := ConfigProxy{}
var err error var err error
proxy.nacosServer, err = nacos_server.NewNacosServer(serverConfig, clientConfig, httpAgent, clientConfig.TimeoutMs, clientConfig.Endpoint) proxy.nacosServer, err = nacos_server.NewNacosServer(serverConfig, clientConfig, httpAgent, clientConfig.TimeoutMs, clientConfig.Endpoint)
proxy.clientConfig = clientConfig proxy.clientConfig = clientConfig
return proxy, err return &proxy, err
} }
func (cp *ConfigProxy) GetServerList() []constant.ServerConfig { func (cp *ConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) {
return cp.nacosServer.GetServerList() cp.nacosServer.InjectSecurityInfo(request.GetHeaders())
cp.injectCommHeader(request.GetHeaders())
signHeaders := nacos_server.GetSignHeaders(request.GetHeaders(), cp.clientConfig.SecretKey)
request.PutAllHeaders(signHeaders)
//todo Spas-SecurityToken/Spas-AccessKey.
//todo Config Limiter
return rpcClient.Request(request, int64(timeoutMills))
} }
func (cp *ConfigProxy) GetConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (string, error) { func (cp *ConfigProxy) injectCommHeader(param map[string]string) {
params := util.TransformObject2Param(param) now := string(util.CurrentMillis())
if len(tenant) > 0 { param[constant.CLIENT_APPNAME_HEADER] = cp.clientConfig.AppName
params["tenant"] = tenant 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"
var headers = map[string]string{} param[constant.CHARSET_KEY] = "utf-8"
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.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error) { func (cp *ConfigProxy) searchConfigProxy(param vo.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error) {
params := util.TransformObject2Param(param) params := util.TransformObject2Param(param)
if len(tenant) > 0 { if len(tenant) > 0 {
params["tenant"] = tenant params["tenant"] = tenant
@ -89,98 +94,97 @@ func (cp *ConfigProxy) SearchConfigProxy(param vo.SearchConfigParm, tenant, acce
} }
return &configPage, nil 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{} func (cp *ConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
headers["accessKey"] = accessKey if group == "" {
headers["secretKey"] = secretKey group = constant.DEFAULT_GROUP
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs) }
configQueryRequest := rpc_request.NewConfigQueryRequest(group, dataId, tenant)
configQueryRequest.Headers["notify"] = strconv.FormatBool(notify)
iResponse, err := cp.requestProxy(cp.getRpcClient(client), configQueryRequest, timeout)
if err != nil { if err != nil {
return false, errors.New("[client.PublishConfig] publish config failed:" + err.Error()) return nil, err
} }
if strings.ToLower(strings.Trim(result, " ")) == "true" { response, ok := iResponse.(*rpc_response.ConfigQueryResponse)
return true, nil if !ok {
} else { return nil, errors.New("ConfigQueryRequest returns type error")
return false, errors.New("[client.PublishConfig] publish config failed:" + result)
} }
if response.IsSuccess() {
//todo LocalConfigInfoProcessor.saveSnapshot
cacheKey := util.GetConfigCacheKey(dataId, group, tenant)
cache.WriteConfigToFile(cacheKey, cp.clientConfig.CacheDir, response.Content)
//todo LocalConfigInfoProcessor.saveEncryptDataKeySnapshot
if response.ContentType == "" {
response.ContentType = "text"
}
return response, nil
}
if response.GetErrorCode() == 300 {
//todo LocalConfigInfoProcessor.saveSnapshot
//todo LocalConfigInfoProcessor.saveEncryptDataKeySnapshot
return response, 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)
}
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) PublishAggProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) { func (cp *ConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
params := util.TransformObject2Param(param) labels := map[string]string{
if len(tenant) > 0 { constant.LABEL_SOURCE: constant.LABEL_SOURCE_SDK,
params["tenant"] = tenant constant.LABEL_MODULE: constant.LABEL_MODULE_CONFIG,
"taskId": taskId,
} }
params["method"] = "addDatum"
var headers = map[string]string{} iRpcClient, _ := rpc.CreateClient("config-"+taskId+"-"+client.uid, rpc.GRPC, labels, cp.nacosServer)
headers["accessKey"] = accessKey rpcClient := iRpcClient.GetRpcClient()
headers["secretKey"] = secretKey if rpcClient.IsInitialized() {
_, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_AGG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs) rpcClient.RegisterServerRequestHandler(func() rpc_request.IRequest {
if err != nil { return &rpc_request.ConfigChangeNotifyRequest{ConfigRequest: rpc_request.NewConfigRequest()}
return false, errors.New("[client.PublishAggProxy] publish agg failed:" + err.Error()) }, &ConfigChangeNotifyRequestHandler{client: client})
rpcClient.Tenant = cp.clientConfig.NamespaceId
rpcClient.Start()
} }
return true, nil return rpcClient
} }
func (cp *ConfigProxy) DeleteAggProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) { func (cp *ConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
params := util.TransformObject2Param(param) return cp.createRpcClient("0", client)
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
} }
func (cp *ConfigProxy) DeleteConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) { type ConfigChangeNotifyRequestHandler struct {
params := util.TransformObject2Param(param) client *ConfigClient
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))
}
} }
func (cp *ConfigProxy) ListenConfig(params map[string]string, isInitializing bool, tenant, accessKey, secretKey string) (string, error) { func (c *ConfigChangeNotifyRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *rpc.RpcClient) rpc_response.IResponse {
//fixed at 30000msavoid frequent request on the server configChangeNotifyRequest, ok := request.(*rpc_request.ConfigChangeNotifyRequest)
var listenInterval uint64 = 30000 if ok {
headers := map[string]string{ logger.Infof("%s [server-push] config changed. dataId=%s, group=%s,tenant=%s", rpcClient.Name,
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8", configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group, configChangeNotifyRequest.Tenant)
"Long-Pulling-Timeout": strconv.FormatUint(listenInterval, 10),
}
if isInitializing {
headers["Long-Pulling-Timeout-No-Hangup"] = "true"
}
headers["accessKey"] = accessKey cacheKey := util.GetConfigCacheKey(configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group,
headers["secretKey"] = secretKey configChangeNotifyRequest.Tenant)
cache, ok := c.client.cacheMap.Get(cacheKey)
if len(tenant) > 0 { if !ok {
params["tenant"] = tenant return nil
} }
logger.Infof("[client.ListenConfig] request params:%+v header:%+v \n", params, headers) cacheData := cache.(*cacheData)
// In order to prevent the server from handling the delay of the client's long task, cacheData.isSyncWithServer = false
// increase the client's read timeout to avoid this problem. c.client.notifyListenConfig()
timeout := listenInterval + listenInterval/10 return &rpc_response.NotifySubscriberResponse{
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_LISTEN_PATH, params, headers, http.MethodPost, timeout) Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
return result, err }
}
return nil
} }

View File

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

View File

@ -22,9 +22,9 @@ import (
"os" "os"
"strconv" "strconv"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/file" "github.com/nacos-group/nacos-sdk-go/v2/common/file"
"github.com/nacos-group/nacos-sdk-go/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
) )
type NacosClient struct { type NacosClient struct {
@ -68,7 +68,6 @@ func (client *NacosClient) SetClientConfig(config constant.ClientConfig) (err er
if config.LogDir == "" { if config.LogDir == "" {
config.LogDir = file.GetCurrentPath() + string(os.PathSeparator) + "log" config.LogDir = file.GetCurrentPath() + string(os.PathSeparator) + "log"
} }
log.Printf("[INFO] logDir:<%s> cacheDir:<%s>", config.LogDir, config.CacheDir) log.Printf("[INFO] logDir:<%s> cacheDir:<%s>", config.LogDir, config.CacheDir)
client.clientConfig = config client.clientConfig = config
client.clientConfigValid = true client.clientConfigValid = true

View File

@ -17,8 +17,8 @@
package nacos_client package nacos_client
import ( import (
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent" "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 //go:generate mockgen -destination mock_nacos_client_interface.go -package nacos_client -source=./nacos_client_interface.go

View File

@ -1,131 +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 (
"testing"
"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/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_GetServiceInfoConcurrent(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)
for i := 0; i < 10000; i++ {
go func() {
_, err := client.hostReactor.GetServiceInfo(param.ServiceName, param.ClusterName)
assert.Nil(t, err)
}()
}
}
func BenchmarkHostReactor_GetServiceInfoConcurrent(b *testing.B) {
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)
b.ResetTimer()
for i := 0; i < b.N; i++ {
client.hostReactor.GetServiceInfo(param.ServiceName, param.ClusterName)
}
}

View File

@ -1,173 +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"
"reflect"
"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 serviceMap == nil || 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)
if !ok || ok && !reflect.DeepEqual(service.Hosts, oldDomain.(model.Service).Hosts) {
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)
}
}

View File

@ -0,0 +1,121 @@
/*
* 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 (
"os"
"reflect"
"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 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(),
}
if notLoadCacheAtStart {
serviceInfoHolder.ServiceInfoMap = cache.NewConcurrentMap()
} else {
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
}
cacheKey := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
oldDomain, ok := s.ServiceInfoMap.Get(cacheKey)
if ok && !s.updateCacheWhenEmpty {
//if instance list is empty,not to update cache
if service.Hosts == nil || len(service.Hosts) == 0 {
logger.Errorf("do not have useful host, ignore it, name:%s", service.Name)
return
}
}
s.UpdateTimeMap.Set(cacheKey, uint64(util.CurrentMillis()))
s.ServiceInfoMap.Set(cacheKey, *service)
if !ok || ok && !reflect.DeepEqual(service.Hosts, oldDomain.(model.Service).Hosts) {
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, s.cacheDir)
s.subCallback.ServiceChanged(service)
}
}
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)
}

View File

@ -0,0 +1,89 @@
/*
* 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 (
"errors"
"sync"
"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)) {
logger.Info("adding " + serviceName + " with " + clusters + " to listener map")
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(service *model.Service) {
if service == nil || service.Name == "" {
return
}
key := util.GetServiceCacheKey(util.GetGroupName(service.Name, service.GroupName), service.Clusters)
funcs, ok := ed.callbackFuncMap.Get(key)
if ok {
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)
}
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package naming_client package naming_cache
import ( import (
"fmt" "fmt"
@ -23,15 +23,14 @@ import (
"testing" "testing"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/vo" "github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestEventDispatcher_AddCallbackFuncs(t *testing.T) { func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
service := model.Service{ service := model.Service{
Dom: "public@@Test",
Clusters: strings.Join([]string{"default"}, ","), Clusters: strings.Join([]string{"default"}, ","),
CacheMillis: 10000, CacheMillis: 10000,
Checksum: "abcd", Checksum: "abcd",
@ -39,7 +38,6 @@ func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
} }
var hosts []model.Instance var hosts []model.Instance
host := model.Instance{ host := model.Instance{
Valid: true,
Enable: true, Enable: true,
InstanceId: "123", InstanceId: "123",
Port: 8080, Port: 8080,
@ -56,15 +54,15 @@ func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
ServiceName: "Test", ServiceName: "Test",
Clusters: []string{"default"}, Clusters: []string{"default"},
GroupName: "public", GroupName: "public",
SubscribeCallback: func(services []model.SubscribeService, err error) { SubscribeCallback: func(services []model.Instance, err error) {
fmt.Println(util.ToJsonString(ed.callbackFuncsMap)) fmt.Println(util.ToJsonString(ed.callbackFuncMap))
}, },
} }
ed.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback) ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ",")) key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
for k, v := range ed.callbackFuncsMap.Items() { for k, v := range ed.callbackFuncMap.Items() {
assert.Equal(t, key, k, "key should be equal!") assert.Equal(t, key, k, "key should be equal!")
funcs := v.([]*func(services []model.SubscribeService, err error)) funcs := v.([]*func(services []model.Instance, err error))
assert.Equal(t, len(funcs), 1) assert.Equal(t, len(funcs), 1)
assert.Equal(t, funcs[0], &param.SubscribeCallback, "callback function must be equal!") assert.Equal(t, funcs[0], &param.SubscribeCallback, "callback function must be equal!")
@ -73,7 +71,6 @@ func TestEventDispatcher_AddCallbackFuncs(t *testing.T) {
func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) { func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
service := model.Service{ service := model.Service{
Dom: "public@@Test",
Clusters: strings.Join([]string{"default"}, ","), Clusters: strings.Join([]string{"default"}, ","),
CacheMillis: 10000, CacheMillis: 10000,
Checksum: "abcd", Checksum: "abcd",
@ -81,7 +78,6 @@ func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
} }
var hosts []model.Instance var hosts []model.Instance
host := model.Instance{ host := model.Instance{
Valid: true,
Enable: true, Enable: true,
InstanceId: "123", InstanceId: "123",
Port: 8080, Port: 8080,
@ -98,34 +94,34 @@ func TestEventDispatcher_RemoveCallbackFuncs(t *testing.T) {
ServiceName: "Test", ServiceName: "Test",
Clusters: []string{"default"}, Clusters: []string{"default"},
GroupName: "public", GroupName: "public",
SubscribeCallback: func(services []model.SubscribeService, err error) { SubscribeCallback: func(services []model.Instance, err error) {
fmt.Printf("func1:%s \n", util.ToJsonString(services)) fmt.Printf("func1:%s \n", util.ToJsonString(services))
}, },
} }
ed.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback) ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
assert.Equal(t, len(ed.callbackFuncsMap.Items()), 1, "callback funcs map length should be 1") assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 1")
param2 := vo.SubscribeParam{ param2 := vo.SubscribeParam{
ServiceName: "Test", ServiceName: "Test",
Clusters: []string{"default"}, Clusters: []string{"default"},
GroupName: "public", GroupName: "public",
SubscribeCallback: func(services []model.SubscribeService, err error) { SubscribeCallback: func(services []model.Instance, err error) {
fmt.Printf("func2:%s \n", util.ToJsonString(services)) fmt.Printf("func2:%s \n", util.ToJsonString(services))
}, },
} }
ed.AddCallbackFuncs(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback) ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback)
assert.Equal(t, len(ed.callbackFuncsMap.Items()), 1, "callback funcs map length should be 2") assert.Equal(t, len(ed.callbackFuncMap.Items()), 1, "callback funcs map length should be 2")
for k, v := range ed.callbackFuncsMap.Items() { for k, v := range ed.callbackFuncMap.Items() {
log.Printf("key:%s,%d", k, len(v.([]*func(services []model.SubscribeService, err error)))) 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, ","), &param2.SubscribeCallback) ed.RemoveCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback)
key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ",")) key := util.GetServiceCacheKey(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
for k, v := range ed.callbackFuncsMap.Items() { for k, v := range ed.callbackFuncMap.Items() {
assert.Equal(t, key, k, "key should be equal!") assert.Equal(t, key, k, "key should be equal!")
funcs := v.([]*func(services []model.SubscribeService, err error)) funcs := v.([]*func(services []model.Instance, err error))
assert.Equal(t, len(funcs), 1) assert.Equal(t, len(funcs), 1)
assert.Equal(t, funcs[0], &param.SubscribeCallback, "callback function must be equal!") assert.Equal(t, funcs[0], &param.SubscribeCallback, "callback function must be equal!")
@ -142,7 +138,6 @@ func TestSubscribeCallback_ServiceChanged(t *testing.T) {
} }
var hosts []model.Instance var hosts []model.Instance
host := model.Instance{ host := model.Instance{
Valid: true,
Enable: true, Enable: true,
InstanceId: "123", InstanceId: "123",
Port: 8080, Port: 8080,
@ -159,22 +154,22 @@ func TestSubscribeCallback_ServiceChanged(t *testing.T) {
ServiceName: "Test", ServiceName: "Test",
Clusters: []string{"default"}, Clusters: []string{"default"},
GroupName: "public", GroupName: "public",
SubscribeCallback: func(services []model.SubscribeService, err error) { SubscribeCallback: func(services []model.Instance, err error) {
log.Printf("func1:%s \n", util.ToJsonString(services)) log.Printf("func1:%s \n", util.ToJsonString(services))
}, },
} }
ed.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback) ed.AddCallbackFunc(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
param2 := vo.SubscribeParam{ param2 := vo.SubscribeParam{
ServiceName: "Test", ServiceName: "Test",
Clusters: []string{"default"}, Clusters: []string{"default"},
GroupName: "public", GroupName: "public",
SubscribeCallback: func(services []model.SubscribeService, err error) { SubscribeCallback: func(services []model.Instance, err error) {
log.Printf("func2:%s \n", util.ToJsonString(services)) log.Printf("func2:%s \n", util.ToJsonString(services))
}, },
} }
ed.AddCallbackFuncs(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback) ed.AddCallbackFunc(util.GetGroupName(param2.ServiceName, param2.GroupName), strings.Join(param2.Clusters, ","), &param2.SubscribeCallback)
ed.ServiceChanged(&service) ed.ServiceChanged(&service)
} }

View File

@ -19,72 +19,70 @@ package naming_client
import ( import (
"math" "math"
"math/rand" "math/rand"
"os"
"sort"
"strings" "strings"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/clients/cache" "github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/vo" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type NamingClient struct { type NamingClient struct {
nacos_client.INacosClient nacos_client.INacosClient
hostReactor HostReactor serviceProxy naming_proxy.INamingProxy
serviceProxy NamingProxy serviceInfoHolder *naming_cache.ServiceInfoHolder
subCallback SubscribeCallback
beatReactor BeatReactor
indexMap cache.ConcurrentMap
NamespaceId string
} }
type Chooser struct { func NewNamingClient(nc nacos_client.INacosClient) (*NamingClient, error) {
data []model.Instance rand.Seed(time.Now().UnixNano())
totals []int naming := &NamingClient{INacosClient: nc}
max int
}
func NewNamingClient(nc nacos_client.INacosClient) (NamingClient, error) {
naming := NamingClient{}
clientConfig, err := nc.GetClientConfig() clientConfig, err := nc.GetClientConfig()
if err != nil { if err != nil {
return naming, err return naming, err
} }
naming.NamespaceId = clientConfig.NamespaceId
serverConfig, err := nc.GetServerConfig() serverConfig, err := nc.GetServerConfig()
if err != nil { if err != nil {
return naming, err return naming, err
} }
httpAgent, err := nc.GetHttpAgent() httpAgent, err := nc.GetHttpAgent()
if err != nil { if err != nil {
return naming, err return naming, err
} }
err = logger.InitLogger(logger.Config{
if err = initLogger(clientConfig); err != nil {
return naming, err
}
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 = naming_proxy.NewNamingProxyDelegate(clientConfig, serverConfig, httpAgent, naming.serviceInfoHolder)
go NewServiceInfoUpdater(naming.serviceInfoHolder, clientConfig.UpdateThreadNum, naming.serviceProxy).asyncUpdateService()
if err != nil {
return naming, err
}
return naming, nil
}
func initLogger(clientConfig constant.ClientConfig) error {
return logger.InitLogger(logger.Config{
Level: clientConfig.LogLevel, Level: clientConfig.LogLevel,
OutputPath: clientConfig.LogDir, OutputPath: clientConfig.LogDir,
RotationTime: clientConfig.RotateTime, RotationTime: clientConfig.RotateTime,
MaxAge: clientConfig.MaxAge, MaxAge: clientConfig.MaxAge,
}) })
if err != nil {
return naming, err
}
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
} }
// 注册服务实例 // 注册服务实例
@ -108,24 +106,8 @@ func (sc *NamingClient) RegisterInstance(param vo.RegisterInstanceParam) (bool,
Weight: param.Weight, Weight: param.Weight,
Ephemeral: param.Ephemeral, Ephemeral: param.Ephemeral,
} }
beatInfo := model.BeatInfo{
Ip: param.Ip, return sc.serviceProxy.RegisterInstance(param.ServiceName, param.GroupName, instance)
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
} }
@ -134,21 +116,26 @@ func (sc *NamingClient) DeregisterInstance(param vo.DeregisterInstanceParam) (bo
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP param.GroupName = constant.DEFAULT_GROUP
} }
sc.beatReactor.RemoveBeatInfo(util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port) instance := model.Instance{
Ip: param.Ip,
_, err := sc.serviceProxy.DeregisterInstance(util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port, param.Cluster, param.Ephemeral) Port: param.Port,
if err != nil { ClusterName: param.Cluster,
return false, err Ephemeral: param.Ephemeral,
} }
return true, nil return sc.serviceProxy.DeregisterInstance(param.ServiceName, param.GroupName, instance)
} }
// 获取服务列表 // 获取服务列表
func (sc *NamingClient) GetService(param vo.GetServiceParam) (model.Service, error) { func (sc *NamingClient) GetService(param vo.GetServiceParam) (service model.Service, err error) {
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP 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 return service, err
} }
@ -156,22 +143,33 @@ func (sc *NamingClient) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (mod
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP param.GroupName = constant.DEFAULT_GROUP
} }
clientConfig, _ := sc.GetClientConfig()
if len(param.NameSpace) == 0 { if len(param.NameSpace) == 0 {
if len(sc.NamespaceId) == 0 { if len(clientConfig.NamespaceId) == 0 {
param.NameSpace = constant.DEFAULT_NAMESPACE_ID param.NameSpace = constant.DEFAULT_NAMESPACE_ID
} else { } else {
param.NameSpace = sc.NamespaceId param.NameSpace = clientConfig.NamespaceId
} }
} }
services := sc.hostReactor.GetAllServiceInfo(param.NameSpace, param.GroupName, param.PageNo, param.PageSize) services, err := sc.serviceProxy.GetServiceList(param.PageNo, param.PageSize, param.GroupName, &model.ExpressionSelector{})
return services, nil return services, err
} }
func (sc *NamingClient) SelectAllInstances(param vo.SelectAllInstancesParam) ([]model.Instance, error) { func (sc *NamingClient) SelectAllInstances(param vo.SelectAllInstancesParam) ([]model.Instance, error) {
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP 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 { if err != nil || service.Hosts == nil || len(service.Hosts) == 0 {
return []model.Instance{}, err return []model.Instance{}, err
} }
@ -182,10 +180,19 @@ func (sc *NamingClient) SelectInstances(param vo.SelectInstancesParam) ([]model.
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP 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 { if err != nil {
return nil, err return nil, err
} }
}
return sc.selectInstances(service, param.HealthyOnly) return sc.selectInstances(service, param.HealthyOnly)
} }
@ -207,10 +214,20 @@ func (sc *NamingClient) SelectOneHealthyInstance(param vo.SelectOneHealthInstanc
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP 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 { if err != nil {
return nil, err return nil, err
} }
}
return sc.selectOneHealthyInstances(service) return sc.selectOneHealthyInstances(service)
} }
@ -234,84 +251,29 @@ func (sc *NamingClient) selectOneHealthyInstances(service model.Service) (*model
return nil, errors.New("healthy instance list is empty!") return nil, errors.New("healthy instance list is empty!")
} }
chooser := newChooser(result) instance := newChooser(result).pick()
instance := chooser.pick()
return &instance, nil return &instance, nil
} }
func random(instances []model.Instance, mw int) []model.Instance {
if len(instances) <= 1 || mw <= 1 {
return instances
}
//实例交叉插入列表,避免列表中是连续的实例
var result = make([]model.Instance, 0)
for i := 1; i <= mw; i++ {
for _, host := range instances {
if int(math.Ceil(host.Weight)) >= i {
result = append(result, host)
}
}
}
return result
}
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 {
rand.Seed(time.Now().Unix())
r := rand.Intn(chs.max) + 1
i := sort.SearchInts(chs.totals, r)
return chs.data[i]
}
// 服务监听 // 服务监听
func (sc *NamingClient) Subscribe(param *vo.SubscribeParam) error { func (sc *NamingClient) Subscribe(param *vo.SubscribeParam) error {
if len(param.GroupName) == 0 { if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP param.GroupName = constant.DEFAULT_GROUP
} }
serviceParam := vo.GetServiceParam{ clusters := strings.Join(param.Clusters, ",")
ServiceName: param.ServiceName, sc.serviceInfoHolder.RegisterCallback(util.GetGroupName(param.ServiceName, param.GroupName), clusters, &param.SubscribeCallback)
GroupName: param.GroupName, sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
Clusters: param.Clusters,
}
sc.subCallback.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
svc, err := sc.GetService(serviceParam)
if err != nil {
return err
}
if !sc.hostReactor.serviceProxy.clientConfig.NotLoadCacheAtStart {
sc.subCallback.ServiceChanged(&svc)
}
return nil return nil
} }
//取消服务监听 //取消服务监听
func (sc *NamingClient) Unsubscribe(param *vo.SubscribeParam) error { func (sc *NamingClient) Unsubscribe(param *vo.SubscribeParam) error {
sc.subCallback.RemoveCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback) clusters := strings.Join(param.Clusters, ",")
serviceFullName := util.GetGroupName(param.ServiceName, param.GroupName)
sc.serviceInfoHolder.DeregisterCallback(serviceFullName, clusters, &param.SubscribeCallback)
if sc.serviceInfoHolder.IsSubscribed(serviceFullName, clusters) {
sc.serviceProxy.Unsubscribe(param.ServiceName, param.GroupName, clusters)
}
return nil return nil
} }

View File

@ -16,8 +16,8 @@
package naming_client package naming_client
import ( import (
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/vo" "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 //go:generate mockgen -destination ../../mock/mock_service_client_interface.go -package mock -source=./service_client_interface.go

View File

@ -17,18 +17,14 @@
package naming_client package naming_client
import ( import (
"fmt"
"net/http"
"testing" "testing"
"github.com/golang/mock/gomock" "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/v2/clients/nacos_client"
"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/mock" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -38,7 +34,7 @@ var clientConfigTest = *constant.NewClientConfig(
constant.WithNotLoadCacheAtStart(true), 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"))
var headers = map[string][]string{ var headers = map[string][]string{
"Client-Version": {constant.CLIENT_VERSION}, "Client-Version": {constant.CLIENT_VERSION},
@ -49,37 +45,46 @@ var headers = map[string][]string{
"Content-Type": {"application/x-www-form-urlencoded"}, "Content-Type": {"application/x-www-form-urlencoded"},
} }
func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) { type MockNamingProxy struct {
ctrl := gomock.NewController(t) }
defer func() {
ctrl.Finish() func (m *MockNamingProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
}() return true, nil
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl) }
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"), func (m *MockNamingProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
gomock.AssignableToTypeOf(http.Header{}), return true, nil
gomock.Eq(uint64(10*1000)), }
gomock.Eq(map[string]string{
"namespaceId": "", func (m *MockNamingProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
"serviceName": "DEFAULT_GROUP@@DEMO", return model.ServiceList{Doms: []string{""}}, nil
"groupName": "DEFAULT_GROUP", }
"app": "",
"clusterName": "", func (m *MockNamingProxy) ServerHealthy() bool {
"ip": "10.0.0.10", return true
"port": "80", }
"weight": "0",
"enable": "false", func (m *MockNamingProxy) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
"healthy": "false", return &model.Service{}, nil
"metadata": "{}", }
"ephemeral": "false",
})).Times(1). func (m *MockNamingProxy) Subscribe(serviceName, groupName, clusters string) (model.Service, error) {
Return(http_agent.FakeHttpResponse(200, `ok`), nil) return model.Service{}, nil
}
func (m *MockNamingProxy) Unsubscribe(serviceName, groupName, clusters string) {}
func NewTestNamingClient() *NamingClient {
nc := nacos_client.NacosClient{} nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest}) nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest) nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent) nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewNamingClient(&nc) 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", ServiceName: "DEMO",
Ip: "10.0.0.10", Ip: "10.0.0.10",
Port: 80, Port: 80,
@ -90,39 +95,8 @@ func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) {
} }
func Test_RegisterServiceInstance_withGroupName(t *testing.T) { func Test_RegisterServiceInstance_withGroupName(t *testing.T) {
ctrl := gomock.NewController(t) success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
defer func() { ServiceName: "DEMO",
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",
Ip: "10.0.0.10", Ip: "10.0.0.10",
Port: 80, Port: 80,
GroupName: "test_group", GroupName: "test_group",
@ -133,39 +107,8 @@ func Test_RegisterServiceInstance_withGroupName(t *testing.T) {
} }
func Test_RegisterServiceInstance_withCluster(t *testing.T) { func Test_RegisterServiceInstance_withCluster(t *testing.T) {
ctrl := gomock.NewController(t) success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
defer func() { ServiceName: "DEMO",
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",
Ip: "10.0.0.10", Ip: "10.0.0.10",
Port: 80, Port: 80,
GroupName: "test_group", GroupName: "test_group",
@ -175,149 +118,27 @@ func Test_RegisterServiceInstance_withCluster(t *testing.T) {
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, true, success) 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) { func TestNamingProxy_DeregisterService_WithoutGroupName(t *testing.T) {
ctrl := gomock.NewController(t) success, err := NewTestNamingClient().DeregisterInstance(vo.DeregisterInstanceParam{
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{
ServiceName: "DEMO5", ServiceName: "DEMO5",
Ip: "10.0.0.10", Ip: "10.0.0.10",
Port: 80, Port: 80,
Ephemeral: true, Ephemeral: true,
}) })
assert.Equal(t, nil, err)
assert.Equal(t, true, success)
} }
func TestNamingProxy_DeregisterService_WithGroupName(t *testing.T) { func TestNamingProxy_DeregisterService_WithGroupName(t *testing.T) {
ctrl := gomock.NewController(t) success, err := NewTestNamingClient().DeregisterInstance(vo.DeregisterInstanceParam{
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{
ServiceName: "DEMO6", ServiceName: "DEMO6",
Ip: "10.0.0.10", Ip: "10.0.0.10",
Port: 80, Port: 80,
GroupName: "test_group", GroupName: "test_group",
Ephemeral: true, Ephemeral: true,
}) })
} 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,
})
} }
var serviceJsonTest = `{ var serviceJsonTest = `{
@ -355,11 +176,9 @@ var serviceJsonTest = `{
}` }`
var serviceTest = model.Service(model.Service{Name: "DEFAULT_GROUP@@DEMO", var serviceTest = model.Service(model.Service{Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, UseSpecifiedURL: false, CacheMillis: 1000,
Hosts: []model.Instance{ Hosts: []model.Instance{
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-8888-a-DEMO", InstanceId: "10.10.10.10-8888-a-DEMO",
Port: 0x22b8, Port: 0x22b8,
Ip: "10.10.10.10", Ip: "10.10.10.10",
@ -370,8 +189,6 @@ var serviceTest = model.Service(model.Service{Name: "DEFAULT_GROUP@@DEMO",
Enable: true, Enable: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-8888-a-DEMO", InstanceId: "10.10.10.11-8888-a-DEMO",
Port: 0x22b8, Port: 0x22b8,
Ip: "10.10.10.11", Ip: "10.10.10.11",
@ -383,74 +200,14 @@ var serviceTest = model.Service(model.Service{Name: "DEFAULT_GROUP@@DEMO",
}, },
}, },
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a", LastRefTime: 1528787794594, Clusters: "a"})
Metadata: map[string]string(nil)})
//func TestNamingProxy_GetService_WithoutGroupName(t *testing.T) {
// ctrl := gomock.NewController(t)
// defer func() {
// ctrl.Finish()
// }()
// mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
//
// mockIHttpAgent.EXPECT().Request(gomock.Eq("GET"),
// gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance/list"),
// gomock.AssignableToTypeOf(http.Header{}),
// gomock.Eq(uint64(10*1000)),
// gomock.Any()).Times(2).
// Return(http_agent.FakeHttpResponse(200, serviceJsonTest), nil)
//
// nc := nacos_client.NacosClient{}
// nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
// nc.SetClientConfig(clientConfigTest)
// nc.SetHttpAgent(mockIHttpAgent)
// client, _ := NewNamingClient(&nc)
// result, err := client.GetService(vo.GetServiceParam{
// ServiceName: "DEMO",
// Clusters: []string{"a"},
// })
// assert.Nil(t, err)
// assert.Equal(t, serviceTest, result)
//
//}
//func TestNamingClient_SelectAllInstancs(t *testing.T) {
// ctrl := gomock.NewController(t)
// defer func() {
// ctrl.Finish()
// }()
// mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
//
// mockIHttpAgent.EXPECT().Request(gomock.Eq("GET"),
// gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance/list"),
// gomock.AssignableToTypeOf(http.Header{}),
// gomock.Eq(uint64(10*1000)),
// gomock.Any()).Times(2).
// Return(http_agent.FakeHttpResponse(200, serviceJsonTest), nil)
//
// nc := nacos_client.NacosClient{}
// nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
// nc.SetClientConfig(clientConfigTest)
// nc.SetHttpAgent(mockIHttpAgent)
// client, _ := NewNamingClient(&nc)
// instances, err := client.SelectAllInstances(vo.SelectAllInstancesParam{
// ServiceName: "DEMO",
// Clusters: []string{"a"},
// })
// fmt.Println(utils.ToJsonString(instances))
// assert.Nil(t, err)
// assert.Equal(t, 2, len(instances))
//}
func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) { func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
services := model.Service(model.Service{ services := model.Service{
Name: "DEFAULT_GROUP@@DEMO", Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{ Hosts: []model.Instance{
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO", InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.10", Ip: "10.10.10.10",
@ -462,8 +219,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO", InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.11", Ip: "10.10.10.11",
@ -475,8 +230,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO", InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.12", Ip: "10.10.10.12",
@ -488,8 +241,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: false, Healthy: false,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO", InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.13", Ip: "10.10.10.13",
@ -501,8 +252,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO", InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.14", Ip: "10.10.10.14",
@ -515,65 +264,33 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
}, },
}, },
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a", LastRefTime: 1528787794594, Clusters: "a"}
Metadata: map[string]string(nil)}) instance1, err := NewTestNamingClient().selectOneHealthyInstances(services)
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)
fmt.Println(util.ToJsonString(instance1))
assert.Nil(t, err) assert.Nil(t, err)
assert.NotNil(t, instance1) assert.NotNil(t, instance1)
instance2, err := client.selectOneHealthyInstances(services) instance2, err := NewTestNamingClient().selectOneHealthyInstances(services)
fmt.Println(util.ToJsonString(instance2))
assert.Nil(t, err) assert.Nil(t, err)
assert.NotNil(t, instance2) assert.NotNil(t, instance2)
//assert.NotEqual(t, instance1, instance2)
} }
func TestNamingClient_SelectOneHealthyInstance_Empty(t *testing.T) { func TestNamingClient_SelectOneHealthyInstance_Empty(t *testing.T) {
services := model.Service(model.Service{ services := model.Service{
Name: "DEFAULT_GROUP@@DEMO", Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{}, Hosts: []model.Instance{},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a", LastRefTime: 1528787794594, Clusters: "a"}
Metadata: map[string]string(nil)}) instance, err := NewTestNamingClient().selectOneHealthyInstances(services)
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)
fmt.Println(util.ToJsonString(instance))
assert.NotNil(t, err) assert.NotNil(t, err)
assert.Nil(t, instance) assert.Nil(t, instance)
} }
func TestNamingClient_SelectInstances_Healthy(t *testing.T) { func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
services := model.Service(model.Service{ services := model.Service{
Name: "DEFAULT_GROUP@@DEMO", Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{ Hosts: []model.Instance{
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO", InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.10", Ip: "10.10.10.10",
@ -585,8 +302,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO", InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.11", Ip: "10.10.10.11",
@ -598,8 +313,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO", InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.12", Ip: "10.10.10.12",
@ -611,8 +324,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: false, Healthy: false,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO", InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.13", Ip: "10.10.10.13",
@ -624,8 +335,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO", InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.14", Ip: "10.10.10.14",
@ -638,34 +347,18 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
}, },
}, },
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a", LastRefTime: 1528787794594, Clusters: "a"}
Metadata: map[string]string(nil)}) instances, err := NewTestNamingClient().selectInstances(services, true)
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)
fmt.Println(util.ToJsonString(instances))
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, len(instances)) assert.Equal(t, 2, len(instances))
} }
func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) { func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
services := model.Service(model.Service{ services := model.Service{
Name: "DEFAULT_GROUP@@DEMO", Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{ Hosts: []model.Instance{
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO", InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.10", Ip: "10.10.10.10",
@ -677,8 +370,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO", InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.11", Ip: "10.10.10.11",
@ -690,8 +381,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO", InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.12", Ip: "10.10.10.12",
@ -703,8 +392,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: false, Healthy: false,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO", InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.13", Ip: "10.10.10.13",
@ -716,8 +403,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO", InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.14", Ip: "10.10.10.14",
@ -730,103 +415,41 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
}, },
}, },
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a", LastRefTime: 1528787794594, Clusters: "a"}
Metadata: map[string]string(nil)}) instances, err := NewTestNamingClient().selectInstances(services, false)
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)
fmt.Println(util.ToJsonString(instances))
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, len(instances)) assert.Equal(t, 1, len(instances))
} }
func TestNamingClient_SelectInstances_Empty(t *testing.T) { func TestNamingClient_SelectInstances_Empty(t *testing.T) {
services := model.Service(model.Service{ services := model.Service{
Name: "DEFAULT_GROUP@@DEMO", Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{}, Hosts: []model.Instance{},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a", LastRefTime: 1528787794594, Clusters: "a"}
Metadata: map[string]string(nil)}) instances, err := NewTestNamingClient().selectInstances(services, false)
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)
fmt.Println(util.ToJsonString(instances))
assert.NotNil(t, err) assert.NotNil(t, err)
assert.Equal(t, 0, len(instances)) assert.Equal(t, 0, len(instances))
} }
func TestNamingClient_GetAllServicesInfo(t *testing.T) { func TestNamingClient_GetAllServicesInfo(t *testing.T) {
nc := nacos_client.NacosClient{} result, err := NewTestNamingClient().GetAllServicesInfo(vo.GetAllServiceInfoParam{
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewNamingClient(&nc)
reslut, err := client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
GroupName: "DEFAULT_GROUP", GroupName: "DEFAULT_GROUP",
PageNo: 1, PageNo: 1,
PageSize: 20, PageSize: 20,
}) })
assert.NotNil(t, reslut.Doms) assert.NotNil(t, result.Doms)
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestNamingClient_selectOneHealthyInstanceResult(t *testing.T) {
services := model.Service(model.Service{
Name: "DEFAULT_GROUP@@DEMO",
Hosts: []model.Instance{
{
Ip: "127.0.0.1",
Weight: 1,
Enable: true,
Healthy: true,
},
{
Ip: "127.0.0.2",
Weight: 9,
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++ {
i, _ := client.selectOneHealthyInstances(services)
fmt.Println(i.Ip)
}
}
func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) { func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
services := model.Service(model.Service{ services := model.Service{
Name: "DEFAULT_GROUP@@DEMO", Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{ Hosts: []model.Instance{
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO", InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.10", Ip: "10.10.10.10",
@ -838,8 +461,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO", InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.11", Ip: "10.10.10.11",
@ -851,8 +472,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO", InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.12", Ip: "10.10.10.12",
@ -864,8 +483,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: false, Healthy: false,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO", InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.13", Ip: "10.10.10.13",
@ -877,8 +494,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: true, Healthy: true,
}, },
{ {
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO", InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80, Port: 80,
Ip: "10.10.10.14", Ip: "10.10.10.14",
@ -891,178 +506,11 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
}, },
}, },
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a", LastRefTime: 1528787794594, Clusters: "a"}
Metadata: map[string]string(nil)}) client := NewTestNamingClient()
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
client, _ := NewNamingClient(&nc)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
client.selectOneHealthyInstances(services) client.selectOneHealthyInstances(services)
} }
} }
func BenchmarkNamingClient_Random(b *testing.B) {
services := model.Service(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",
Weight: 10,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO1",
Enable: true,
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80,
Ip: "10.10.10.11",
Weight: 9,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: true,
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80,
Ip: "10.10.10.12",
Weight: 8,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: true,
Healthy: false,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80,
Ip: "10.10.10.13",
Weight: 8,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: false,
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80,
Ip: "10.10.10.14",
Weight: 7,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: true,
Healthy: true,
},
},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
b.ResetTimer()
for i := 0; i < b.N; i++ {
random(services.Hosts, 10)
}
}
func BenchmarkNamingClient_ChooserPick(b *testing.B) {
services := model.Service(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",
Weight: 10,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO1",
Enable: true,
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80,
Ip: "10.10.10.11",
Weight: 9,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: true,
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80,
Ip: "10.10.10.12",
Weight: 8,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: true,
Healthy: false,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80,
Ip: "10.10.10.13",
Weight: 7,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: false,
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80,
Ip: "10.10.10.14",
Weight: 6,
Metadata: map[string]string{},
ClusterName: "a",
ServiceName: "DEMO",
Enable: true,
Healthy: true,
},
},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
b.ResetTimer()
for i := 0; i < b.N; i++ {
chooser := newChooser(services.Hosts)
chooser.pick()
}
}

View File

@ -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 naming_grpc
import (
"reflect"
"strings"
"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 *NamingGrpcProxy
registeredInstanceCached cache.ConcurrentMap
subscribes cache.ConcurrentMap
}
func NewConnectionEventListener(clientProxy *NamingGrpcProxy) *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
if len(info) > 2 {
_, err = c.clientProxy.Subscribe(info[0], info[1], info[2])
} else {
_, err = c.clientProxy.Subscribe(info[0], info[1], "")
}
if err != nil {
logger.Warnf("redo subscribe service:%s faild:%+v", info[0], err)
}
}
}
func (c *ConnectionEventListener) redoRegisterEachService() {
for k, v := range c.registeredInstanceCached.Items() {
info := strings.Split(k, constant.SERVICE_INFO_SPLITER)
serviceName := info[0]
groupName := info[1]
instances, ok := v.([]model.Instance)
if !ok {
logger.Warnf("redo register service:%s faild,instances type not is []model.instance", info[0])
}
for _, instance := range instances {
_, err := c.clientProxy.RegisterInstance(serviceName, groupName, instance)
if err != nil {
logger.Warnf("redo register service:%s groupName:%s faild:%s", info[0], info[1], err.Error())
}
}
}
}
func (c *ConnectionEventListener) CacheInstanceForRedo(serviceName, groupName string, instance model.Instance) {
var flag bool
key := util.GetGroupName(serviceName, groupName)
instances, ok := c.registeredInstanceCached.Get(key)
if !ok {
instances = []model.Instance{instance}
c.registeredInstanceCached.Set(key, instances)
} else {
instances, ok := instances.([]model.Instance)
if !ok {
c.registeredInstanceCached.Remove(key)
logger.Warn("cacheInstanceForRedo faild,instances type not is []model.instance")
return
}
for _, v := range instances {
if reflect.DeepEqual(v, instance) {
flag = false
}
}
if flag {
instances = append(instances, instance)
c.registeredInstanceCached.Set(key, instances)
}
}
}
func (c *ConnectionEventListener) RemoveInstanceForRedo(serviceName, groupName string, instance model.Instance) {
key := util.GetGroupName(serviceName, groupName)
instances, ok := c.registeredInstanceCached.Get(key)
if !ok {
return
}
instanceSlice := instances.([]model.Instance)
instancesNew := make([]model.Instance, 0, len(instanceSlice))
for _, v := range instanceSlice {
if !reflect.DeepEqual(v, instance) {
instancesNew = append(instancesNew, v)
}
}
c.registeredInstanceCached.Set(key, instancesNew)
}
func (c *ConnectionEventListener) CacheSubscriberForRedo(fullServiceName, clusters string) {
key := util.GetServiceCacheKey(fullServiceName, clusters)
if _, ok := c.subscribes.Get(key); !ok {
c.subscribes.Set(key, struct{}{})
}
return
}
func (c *ConnectionEventListener) RemoveSubscriberForRedo(fullServiceName, clusters string) {
c.subscribes.Remove(util.GetServiceCacheKey(fullServiceName, clusters))
}

View File

@ -0,0 +1,160 @@
/*
* 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 (
"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/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"
)
type NamingGrpcProxy struct {
clientConfig constant.ClientConfig
nacosServer *nacos_server.NacosServer
rpcClient rpc.IRpcClient
eventListener *ConnectionEventListener
serviceInfoHolder *naming_cache.ServiceInfoHolder
}
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) {
proxy.nacosServer.InjectSecurityInfo(request.GetHeaders())
//todo ak/sk
return proxy.rpcClient.GetRpcClient().Request(request, int64(proxy.clientConfig.TimeoutMs))
}
func (proxy *NamingGrpcProxy) 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))
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
}
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
}
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)
break
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
}
func (proxy *NamingGrpcProxy) ServerHealthy() bool {
return proxy.rpcClient.GetRpcClient().IsRunning()
}
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
}
func (proxy *NamingGrpcProxy) Subscribe(serviceName, groupName string, clusters string) (model.Service, error) {
response, err := proxy.requestToServer(rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName,
groupName, clusters, true))
if err != nil {
return model.Service{}, err
}
proxy.eventListener.CacheSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters)
subscribeServiceResponse := response.(*rpc_response.SubscribeServiceResponse)
return subscribeServiceResponse.ServiceInfo, nil
}
func (proxy *NamingGrpcProxy) Unsubscribe(serviceName, groupName, clusters string) {
proxy.requestToServer(rpc_request.NewSubscribeServiceRequest(proxy.clientConfig.NamespaceId, serviceName, groupName,
clusters, false))
proxy.eventListener.RemoveSubscriberForRedo(util.GetGroupName(serviceName, groupName), clusters)
}

View File

@ -14,40 +14,42 @@
* limitations under the License. * limitations under the License.
*/ */
package naming_client package naming_http
import ( import (
"errors"
"fmt"
"net/http"
"strconv" "strconv"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/clients/cache" "github.com/buger/jsonparser"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/util" "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"
nsema "github.com/toolkits/concurrent/semaphore" nsema "github.com/toolkits/concurrent/semaphore"
) )
type BeatReactor struct { type BeatReactor struct {
beatMap cache.ConcurrentMap beatMap cache.ConcurrentMap
serviceProxy NamingProxy nacosServer *nacos_server.NacosServer
clientBeatInterval int64
beatThreadCount int beatThreadCount int
beatThreadSemaphore *nsema.Semaphore beatThreadSemaphore *nsema.Semaphore
beatRecordMap cache.ConcurrentMap beatRecordMap cache.ConcurrentMap
clientCfg constant.ClientConfig
} }
const Default_Beat_Thread_Num = 20 const Default_Beat_Thread_Num = 20
func NewBeatReactor(serviceProxy NamingProxy, clientBeatInterval int64) BeatReactor { func NewBeatReactor(clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer) BeatReactor {
br := BeatReactor{} br := BeatReactor{}
if clientBeatInterval <= 0 {
clientBeatInterval = 5 * 1000
}
br.beatMap = cache.NewConcurrentMap() br.beatMap = cache.NewConcurrentMap()
br.serviceProxy = serviceProxy br.nacosServer = nacosServer
br.clientBeatInterval = clientBeatInterval br.clientCfg = clientCfg
br.beatThreadCount = Default_Beat_Thread_Num br.beatThreadCount = Default_Beat_Thread_Num
br.beatRecordMap = cache.NewConcurrentMap() br.beatRecordMap = cache.NewConcurrentMap()
br.beatThreadSemaphore = nsema.NewSemaphore(br.beatThreadCount) br.beatThreadSemaphore = nsema.NewSemaphore(br.beatThreadCount)
@ -87,7 +89,7 @@ func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
} }
//进行心跳通信 //进行心跳通信
beatInterval, err := br.serviceProxy.SendBeat(*beatInfo) beatInterval, err := br.SendBeat(*beatInfo)
if err != nil { if err != nil {
logger.Errorf("beat to server return error:%+v", err) logger.Errorf("beat to server return error:%+v", err)
br.beatThreadSemaphore.Release() br.beatThreadSemaphore.Release()
@ -106,3 +108,26 @@ func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
<-t.C <-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
}

View File

@ -14,20 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package naming_client package naming_http
import ( import (
"testing" "testing"
"github.com/nacos-group/nacos-sdk-go/common/nacos_server" "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/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestBeatReactor_AddBeatInfo(t *testing.T) { func TestBeatReactor_AddBeatInfo(t *testing.T) {
br := NewBeatReactor(NamingProxy{nacosServer: &nacos_server.NacosServer{}}, 5000) br := NewBeatReactor(constant.ClientConfig{}, &nacos_server.NacosServer{})
serviceName := "Test" serviceName := "Test"
groupName := "public" groupName := "public"
beatInfo := model.BeatInfo{ beatInfo := model.BeatInfo{
@ -46,7 +46,7 @@ func TestBeatReactor_AddBeatInfo(t *testing.T) {
} }
func TestBeatReactor_RemoveBeatInfo(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" serviceName := "Test"
groupName := "public" groupName := "public"
beatInfo1 := model.BeatInfo{ beatInfo1 := model.BeatInfo{

View File

@ -0,0 +1,199 @@
/*
* 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 (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"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"
)
type NamingHttpProxy struct {
clientConfig constant.ClientConfig
nacosServer *nacos_server.NacosServer
beatReactor BeatReactor
serviceInfoHolder *naming_cache.ServiceInfoHolder
}
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
}
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
}
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
}
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
}
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
}
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
}
func (proxy *NamingHttpProxy) Subscribe(serviceName, groupName, clusters string) (model.Service, error) {
return model.Service{}, nil
}
func (proxy *NamingHttpProxy) Unsubscribe(serviceName, groupName, clusters string) {
}

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package naming_client package naming_http
import ( import (
"bytes" "bytes"
@ -26,14 +26,15 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/util"
) )
type PushReceiver struct { type PushReceiver struct {
port int port int
host string host string
hostReactor *HostReactor serviceInfoHolder *naming_cache.ServiceInfoHolder
} }
type PushData struct { type PushData struct {
@ -46,9 +47,9 @@ var (
GZIP_MAGIC = []byte("\x1F\x8B") GZIP_MAGIC = []byte("\x1F\x8B")
) )
func NewPushReceiver(hostReactor *HostReactor) *PushReceiver { func NewPushReceiver(serviceInfoHolder *naming_cache.ServiceInfoHolder) *PushReceiver {
pr := PushReceiver{ pr := PushReceiver{
hostReactor: hostReactor, serviceInfoHolder: serviceInfoHolder,
} }
pr.startServer() pr.startServer()
return &pr return &pr
@ -118,7 +119,7 @@ func (us *PushReceiver) handleClient(conn *net.UDPConn) {
ack := make(map[string]string) ack := make(map[string]string)
if pushData.PushType == "dom" || pushData.PushType == "service" { if pushData.PushType == "dom" || pushData.PushType == "service" {
us.hostReactor.ProcessServiceJson(pushData.Data) us.serviceInfoHolder.ProcessServiceJson(pushData.Data)
ack["type"] = "push-ack" ack["type"] = "push-ack"
ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10) ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
@ -127,7 +128,7 @@ func (us *PushReceiver) handleClient(conn *net.UDPConn) {
} else if pushData.PushType == "dump" { } else if pushData.PushType == "dump" {
ack["type"] = "dump-ack" ack["type"] = "dump-ack"
ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10) ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)
ack["data"] = util.ToJsonString(us.hostReactor.serviceInfoMap) ack["data"] = util.ToJsonString(us.serviceInfoHolder.ServiceInfoMap)
} else { } else {
ack["type"] = "unknow-ack" ack["type"] = "unknow-ack"
ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10) ack["lastRefTime"] = strconv.FormatInt(pushData.LastRefTime, 10)

View File

@ -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]
}

View File

@ -1,191 +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) 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, errors.New(fmt.Sprintf("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)
break
default:
break
}
}
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, 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 nil, 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
}
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)
}

View File

@ -0,0 +1,108 @@
/*
* 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/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/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"
)
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) (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 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) {
proxy.serviceInfoHolder.StopUpdateIfContain(util.GetGroupName(serviceName, groupName), clusters)
proxy.grpcClientProxy.Unsubscribe(serviceName, groupName, clusters)
}

View File

@ -0,0 +1,37 @@
/*
* 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"
)
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)
}

View File

@ -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)
}

View File

@ -1,95 +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 {
var subscribeService model.SubscribeService
subscribeService.Valid = host.Valid
subscribeService.Port = host.Port
subscribeService.Ip = host.Ip
subscribeService.Metadata = host.Metadata
subscribeService.ServiceName = host.ServiceName
subscribeService.ClusterName = host.ClusterName
subscribeService.Weight = host.Weight
subscribeService.InstanceId = host.InstanceId
subscribeService.Enable = host.Enable
subscribeServices = append(subscribeServices, subscribeService)
}
(*funcItem)(subscribeServices, nil)
}
}
}

View File

@ -19,7 +19,7 @@ package constant
import ( import (
"os" "os"
"github.com/nacos-group/nacos-sdk-go/common/file" "github.com/nacos-group/nacos-sdk-go/v2/common/file"
) )
func NewClientConfig(opts ...ClientOption) *ClientConfig { func NewClientConfig(opts ...ClientOption) *ClientConfig {

View File

@ -20,7 +20,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/nacos-group/nacos-sdk-go/common/file" "github.com/nacos-group/nacos-sdk-go/v2/common/file"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )

View File

@ -29,6 +29,7 @@ type ClientConfig struct {
BeatInterval int64 //the time interval for sending beat to server,default value is 5000ms 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. NamespaceId string //the namespaceId of Nacos.When namespace is public, fill in the blank string here.
AppName string //the appName AppName string //the appName
AppKey string //the client identity information
Endpoint string //the endpoint for get Nacos server addresses Endpoint string //the endpoint for get Nacos server addresses
RegionId string //the regionId for kms RegionId string //the regionId for kms
AccessKey string //the AccessKey for kms AccessKey string //the AccessKey for kms

View File

@ -16,6 +16,8 @@
package constant package constant
import "time"
const ( const (
KEY_USERNAME = "username" KEY_USERNAME = "username"
KEY_PASSWORD = "password" KEY_PASSWORD = "password"
@ -66,7 +68,7 @@ const (
KEY_BEAT = "beat" KEY_BEAT = "beat"
KEY_DOM = "dom" KEY_DOM = "dom"
DEFAULT_CONTEXT_PATH = "/nacos" DEFAULT_CONTEXT_PATH = "/nacos"
CLIENT_VERSION = "Nacos-Go-Client:v1.0.1" CLIENT_VERSION = "Nacos-Go-Client:v2.0.0"
REQUEST_DOMAIN_RETRY_TIME = 3 REQUEST_DOMAIN_RETRY_TIME = 3
SERVICE_INFO_SPLITER = "@@" SERVICE_INFO_SPLITER = "@@"
CONFIG_INFO_SPLITER = "@@" CONFIG_INFO_SPLITER = "@@"
@ -75,4 +77,19 @@ const (
NAMING_INSTANCE_ID_SPLITTER = "#" NAMING_INSTANCE_ID_SPLITTER = "#"
DefaultClientErrorCode = "SDK.NacosError" DefaultClientErrorCode = "SDK.NacosError"
DEFAULT_SERVER_SCHEME = "http" DEFAULT_SERVER_SCHEME = "http"
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"
) )

View File

@ -20,9 +20,11 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
) )
type HttpAgent struct { type HttpAgent struct {
@ -56,7 +58,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) logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],err:%+v", method, path, util.ToJsonString(header), util.ToJsonString(params), err)
return "" 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) logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],status code error:%d", method, path, util.ToJsonString(header), util.ToJsonString(params), response.StatusCode)
return "" return ""
} }

View File

@ -20,7 +20,7 @@ import (
"strings" "strings"
"time" "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) { func post(path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {

View File

@ -24,7 +24,7 @@ import (
"time" "time"
rotatelogs "github.com/lestrrat/go-file-rotatelogs" rotatelogs "github.com/lestrrat/go-file-rotatelogs"
"github.com/nacos-group/nacos-sdk-go/common/file" "github.com/nacos-group/nacos-sdk-go/v2/common/file"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
) )

View File

@ -19,7 +19,7 @@ package nacos_error
import ( import (
"fmt" "fmt"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
) )
type NacosError struct { type NacosError struct {

View File

@ -29,15 +29,16 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/common/nacos_error" "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error"
"github.com/nacos-group/nacos-sdk-go/common/security" "github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/nacos-group/nacos-sdk-go/inner/uuid" "github.com/nacos-group/nacos-sdk-go/v2/inner/uuid"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
) )
type NacosServer struct { type NacosServer struct {
@ -50,6 +51,7 @@ type NacosServer struct {
lastSrvRefTime int64 lastSrvRefTime int64
vipSrvRefInterMills int64 vipSrvRefInterMills int64
contextPath string contextPath string
currentIndex int32
} }
func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.ClientConfig, httpAgent http_agent.IHttpAgent, timeoutMs uint64, endpoint string) (*NacosServer, error) { func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.ClientConfig, httpAgent http_agent.IHttpAgent, timeoutMs uint64, endpoint string) (*NacosServer, error) {
@ -67,6 +69,7 @@ func NewNacosServer(serverList []constant.ServerConfig, clientCfg constant.Clien
endpoint: endpoint, endpoint: endpoint,
vipSrvRefInterMills: 10000, vipSrvRefInterMills: 10000,
contextPath: clientCfg.ContextPath, contextPath: clientCfg.ContextPath,
currentIndex: rand.Int31n((int32)(len(serverList))),
} }
ns.initRefreshSrvIfNeed() ns.initRefreshSrvIfNeed()
_, err := securityLogin.Login() _, err := securityLogin.Login()
@ -85,7 +88,7 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
contextPath = constant.WEB_CONTEXT contextPath = constant.WEB_CONTEXT
} }
signHeaders := getSignHeaders(params, newHeaders) signHeaders := GetSignHeaders(params, newHeaders["secretKey"])
url := curServer + contextPath + api url := curServer + contextPath + api
@ -110,7 +113,7 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
headers["Spas-AccessKey"] = []string{newHeaders["accessKey"]} headers["Spas-AccessKey"] = []string{newHeaders["accessKey"]}
headers["Timestamp"] = []string{signHeaders["timeStamp"]} headers["Timestamp"] = []string{signHeaders["timeStamp"]}
headers["Spas-Signature"] = []string{signHeaders["Spas-Signature"]} headers["Spas-Signature"] = []string{signHeaders["Spas-Signature"]}
injectSecurityInfo(server, params) server.InjectSecurityInfo(params)
var response *http.Response var response *http.Response
response, err = server.httpAgent.Request(method, url, headers, timeoutMS, params) response, err = server.httpAgent.Request(method, url, headers, timeoutMS, params)
@ -124,7 +127,7 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
return return
} }
result = string(bytes) result = string(bytes)
if response.StatusCode == 200 { if response.StatusCode == constant.RESPONSE_CODE_SUCCESS {
return return
} else { } else {
err = nacos_error.NewNacosError(strconv.Itoa(response.StatusCode), string(bytes), nil) err = nacos_error.NewNacosError(strconv.Itoa(response.StatusCode), string(bytes), nil)
@ -152,7 +155,7 @@ func (server *NacosServer) callServer(api string, params map[string]string, meth
headers["Request-Module"] = []string{"Naming"} headers["Request-Module"] = []string{"Naming"}
headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"} headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"}
injectSecurityInfo(server, params) server.InjectSecurityInfo(params)
var response *http.Response var response *http.Response
response, err = server.httpAgent.Request(method, url, headers, server.timeoutMs, params) response, err = server.httpAgent.Request(method, url, headers, server.timeoutMs, params)
@ -166,7 +169,7 @@ func (server *NacosServer) callServer(api string, params map[string]string, meth
return return
} }
result = string(bytes) result = string(bytes)
if response.StatusCode == 200 { if response.StatusCode == constant.RESPONSE_CODE_SUCCESS {
return return
} else { } else {
err = errors.New(fmt.Sprintf("request return error code %d", response.StatusCode)) err = errors.New(fmt.Sprintf("request return error code %d", response.StatusCode))
@ -180,7 +183,7 @@ func (server *NacosServer) ReqConfigApi(api string, params map[string]string, he
return "", errors.New("server list is empty") return "", errors.New("server list is empty")
} }
injectSecurityInfo(server, params) server.InjectSecurityInfo(params)
//only one server,retry request when error //only one server,retry request when error
var err error var err error
@ -215,7 +218,7 @@ func (server *NacosServer) ReqApi(api string, params map[string]string, method s
return "", errors.New("server list is empty") return "", errors.New("server list is empty")
} }
injectSecurityInfo(server, params) server.InjectSecurityInfo(params)
//only one server,retry request when error //only one server,retry request when error
if len(srvs) == 1 { if len(srvs) == 1 {
@ -305,7 +308,7 @@ func (server *NacosServer) GetServerList() []constant.ServerConfig {
return server.serverList return server.serverList
} }
func injectSecurityInfo(server *NacosServer, param map[string]string) { func (server *NacosServer) InjectSecurityInfo(param map[string]string) {
accessToken := server.securityLogin.GetAccessToken() accessToken := server.securityLogin.GetAccessToken()
if accessToken != "" { if accessToken != "" {
param[constant.KEY_ACCESS_TOKEN] = accessToken param[constant.KEY_ACCESS_TOKEN] = accessToken
@ -319,7 +322,7 @@ func getAddress(cfg constant.ServerConfig) string {
return cfg.Scheme + "://" + cfg.IpAddr + ":" + strconv.Itoa(int(cfg.Port)) return cfg.Scheme + "://" + cfg.IpAddr + ":" + strconv.Itoa(int(cfg.Port))
} }
func getSignHeaders(params map[string]string, newHeaders map[string]string) map[string]string { func GetSignHeaders(params map[string]string, secretKey string) map[string]string {
resource := "" resource := ""
if len(params["tenant"]) != 0 { if len(params["tenant"]) != 0 {
@ -330,15 +333,15 @@ func getSignHeaders(params map[string]string, newHeaders map[string]string) map[
headers := map[string]string{} headers := map[string]string{}
timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10) timeStamp := strconv.FormatInt(util.CurrentMillis(), 10)
headers["timeStamp"] = timeStamp headers["timeStamp"] = timeStamp
signature := "" signature := ""
if resource == "" { if resource == "" {
signature = signWithhmacSHA1Encrypt(timeStamp, newHeaders["secretKey"]) signature = signWithhmacSHA1Encrypt(timeStamp, secretKey)
} else { } else {
signature = signWithhmacSHA1Encrypt(resource+"+"+timeStamp, newHeaders["secretKey"]) signature = signWithhmacSHA1Encrypt(resource+"+"+timeStamp, secretKey)
} }
headers["Spas-Signature"] = signature headers["Spas-Signature"] = signature
@ -354,3 +357,8 @@ func signWithhmacSHA1Encrypt(encryptText, encryptKey string) string {
return base64.StdEncoding.EncodeToString(mac.Sum(nil)) return base64.StdEncoding.EncodeToString(mac.Sum(nil))
} }
func (server *NacosServer) GetNextServer() constant.ServerConfig {
index := atomic.AddInt32(&server.currentIndex, 1) % (int32)(len(server.GetServerList()))
return server.GetServerList()[index]
}

View File

@ -19,7 +19,7 @@ package nacos_server
import ( import (
"testing" "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" "github.com/stretchr/testify/assert"
) )

View File

@ -0,0 +1,54 @@
/*
* 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)
}
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) close() {
c.conn.Close()
}

View File

@ -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()
}

View File

@ -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) {
}

View File

@ -0,0 +1,224 @@
/*
* 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/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),
lastActiveTimeStamp: time.Now(),
nacosServer: nacosServer,
serverRequestHandlerMapping: make(map[string]ServerRequestHandlerMapping, 8),
mux: new(sync.Mutex),
},
}
rpcClient.executeClient = rpcClient
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 getKeepAliveTimeMillis() keepalive.ClientParameters {
keepAliveTimeMillisInt, err := strconv.Atoi(os.Getenv("nacos.remote.grpc.keep.alive.millis"))
var keepAliveTime time.Duration
if err != nil {
keepAliveTime = 6 * 60 * 1000 * time.Millisecond
} else {
keepAliveTime = time.Duration(keepAliveTimeMillisInt) * time.Millisecond
}
return keepalive.ClientParameters{
Time: keepAliveTime, // send pings every 10 seconds if there is no activity
Timeout: 3 * time.Second, // wait 1 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())
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 <-grpcConn.streamCloseChan:
return
default:
payload, err := streamClient.Recv()
if err != nil {
running := c.IsRunning()
abandon := grpcConn.abandon
if c.IsRunning() && !grpcConn.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)
}
} else {
logger.Infof("%s Ignore event,isRunning:%v,isAbandon=%v", grpcConn.getConnectionId(), running, abandon)
}
} else {
c.handleServerRequest(payload, grpcConn)
}
}
}
}()
}
func serverCheck(client nacos_grpc_service.RequestClient) (rpc_response.IResponse, error) {
payload, err := client.Request(context.Background(), convertRequest(rpc_request.NewServerCheckRequest()))
if err != nil {
return nil, err
}
var response rpc_response.ServerCheckResponse
err = json.Unmarshal(payload.GetBody().Value, &response)
if err != nil {
return nil, err
}
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())
}
}

View File

@ -0,0 +1,106 @@
/*
* 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"
"errors"
"fmt"
"time"
"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
streamCloseChan chan struct{}
}
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,
streamCloseChan: make(chan struct{}),
}
}
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.streamCloseChan <- struct{}{}
}
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())},
}
}

View File

@ -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.
*/
package rpc
import (
"errors"
"fmt"
"math"
"os"
"os/signal"
"sync"
"sync/atomic"
"syscall"
"time"
"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
)
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
}
type RpcClient struct {
Name string
labels map[string]string
currentConnection IConnection
rpcClientStatus RpcClientStatus
eventChan chan ConnectionEvent
reconnectionChan chan ReconnectContext
connectionEventListeners []IConnectionEventListener
lastActiveTimeStamp time.Time
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
serverExist = true
}
}
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)
}
}
}()
var currentConnection IConnection
startUpRetryTimes := constant.REQUEST_DOMAIN_RETRY_TIME
for startUpRetryTimes > 0 && currentConnection == nil {
startUpRetryTimes--
serverInfo := r.nextRpcServer()
logger.Infof("[RpcClient.Start] %+v Try to connect to server on start up, server: %+v", r.Name, serverInfo)
if connection, err := r.executeClient.connectToServer(serverInfo); err != nil {
logger.Warnf("[RpcClient.Start] %+v Fail to connect to server on start up, error message=%+v, "+
"start up retry times left=%+v", r.Name, err.Error(), startUpRetryTimes)
} else {
currentConnection = connection
}
}
if currentConnection != nil {
logger.Infof("%s Success to connect to server %+v on start up,connectionId=%v", 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)
}
r.signalNotify()
}
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) signalNotify() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
for {
s := <-c
switch s {
case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
r.shutdown()
}
}
}()
}
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", request, handler)
return
}
logger.Infof("%s Register server push request:%s handler:%+v", r.Name, requestType, handler)
r.serverRequestHandlerMapping[requestType] = ServerRequestHandlerMapping{
serverRequest: request,
handler: handler,
}
}
func (r *RpcClient) RegisterConnectionListener(listener IConnectionEventListener) {
logger.Infof("%s Register connection listener [%+v] to current client", r.Name, listener)
r.connectionEventListeners = append(r.connectionEventListeners, listener)
}
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
}
serverInfoFlag := false
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)
}
switchSuccess := false
var reConnectTimes, retryTurns int
for !switchSuccess && !r.isShutdown() {
if serverInfoFlag {
serverInfo = r.nextRpcServer()
}
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))
switchSuccess = true
r.eventChan <- ConnectionEvent{eventType: CONNECTED}
return
}
if r.isShutdown() {
r.closeConnection()
}
if reConnectTimes > 0 && reConnectTimes%len(r.nacosServer.GetServerList()) == 0 {
logger.Infof("%s fail to connect server,after trying %v 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.Infof("%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) {
if len(r.connectionEventListeners) == 0 {
return
}
logger.Infof("%s Notify %s event to listeners.", r.Name, event.toString())
for _, v := range r.connectionEventListeners {
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
if time.Now().Sub(r.lastActiveTimeStamp) < constant.KEEP_ALIVE_TIME*time.Second {
return
}
if r.sendHealthCheck() {
r.lastActiveTimeStamp = 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() {
return false
}
return true
}
func (r *RpcClient) nextRpcServer() ServerInfo {
serverConfig := r.nacosServer.GetNextServer()
return ServerInfo{
serverIp: serverConfig.IpAddr,
serverPort: serverConfig.Port,
}
}
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)
}
//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.New(fmt.Sprintf(
"Client not connected,current status:%v", atomic.LoadInt32((*int32)(&r.rpcClientStatus)))))
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 = 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=%+v, retryTimes=%v,error=%+v", request, retryTimes, err)
time.Sleep(time.Duration(math.Min(100, float64(timeoutMills/3))) * time.Millisecond)
*retryTimes++
return err
}

View File

@ -0,0 +1,7 @@
package rpc
import "testing"
func TestHealthCheck(t *testing.T) {
}

View File

@ -0,0 +1,119 @@
/*
* 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
Module string `json:"module"`
}
func NewConfigRequest() *ConfigRequest {
request := Request{
Headers: make(map[string]string, 8),
}
return &ConfigRequest{
Request: &request,
Module: "config",
}
}
//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
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
}
func NewConfigChangeNotifyRequest(group, dataId, tenant string) *ConfigChangeNotifyRequest {
return &ConfigChangeNotifyRequest{ConfigRequest: NewConfigRequest(), Group: group, DataId: dataId, Tenant: tenant}
}
func (r *ConfigChangeNotifyRequest) GetRequestType() string {
return "ConfigChangeNotifyRequest"
}
type ConfigQueryRequest struct {
*ConfigRequest
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
Tag string `json:"tag"`
}
func NewConfigQueryRequest(group, dataId, tenant string) *ConfigQueryRequest {
return &ConfigQueryRequest{ConfigRequest: NewConfigRequest(), Group: group, DataId: dataId, Tenant: tenant}
}
func (r *ConfigQueryRequest) GetRequestType() string {
return "ConfigQueryRequest"
}
type ConfigPublishRequest struct {
*ConfigRequest
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
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: group, DataId: dataId, Tenant: tenant, Content: content, CasMd5: casMd5, AdditionMap: make(map[string]string)}
}
func (r *ConfigPublishRequest) GetRequestType() string {
return "ConfigPublishRequest"
}
type ConfigRemoveRequest struct {
*ConfigRequest
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
}
func NewConfigRemoveRequest(group, dataId, tenant string) *ConfigRemoveRequest {
return &ConfigRemoveRequest{ConfigRequest: NewConfigRequest(),
Group: group, DataId: dataId, Tenant: tenant}
}
func (r *ConfigRemoveRequest) GetRequestType() string {
return "ConfigRemoveRequest"
}

View File

@ -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"
}

View File

@ -0,0 +1,127 @@
/*
* 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 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 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"
}

View File

@ -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.
*/
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
}
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
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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 rpc_response
import (
"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{}}
})
}

View File

@ -0,0 +1,87 @@
/*
* 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"
)
//ServerRequestHandler, to process the request from server side.
type IServerRequestHandler interface {
//Handle request from server.
RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse
}
type ConnectResetRequestHandler struct {
}
func (c *ConnectResetRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
defer rpcClient.mux.Unlock()
connectResetRequest, ok := request.(*rpc_request.ConnectResetRequest)
if ok {
rpcClient.mux.Lock()
if rpcClient.IsRunning() {
if connectResetRequest.ServerIp != "" {
serverPortNum, err := strconv.Atoi(connectResetRequest.ServerPort)
if err != nil {
logger.Infof("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) 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 (c *NamingPushRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *RpcClient) rpc_response.IResponse {
notifySubscriberRequest, ok := request.(*rpc_request.NotifySubscriberRequest)
if ok {
c.ServiceInfoHolder.ProcessService(&notifySubscriberRequest.ServiceInfo)
return &rpc_response.NotifySubscriberResponse{
Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
}
}
return nil
}

View File

@ -26,9 +26,9 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent" "github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
) )
type AuthClient struct { type AuthClient struct {
@ -137,7 +137,7 @@ func (ac *AuthClient) login(server constant.ServerConfig) (bool, error) {
return false, err return false, err
} }
if resp.StatusCode != 200 { if resp.StatusCode != constant.RESPONSE_CODE_SUCCESS {
errMsg := string(bytes) errMsg := string(bytes)
return false, errors.New(errMsg) return false, errors.New(errMsg)
} }

View File

@ -20,40 +20,20 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/clients" "github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/vo" "github.com/nacos-group/nacos-sdk-go/v2/vo"
) )
func main() { func main() {
//create ServerConfig
sc := []constant.ServerConfig{ sc := []constant.ServerConfig{
{ *constant.NewServerConfig("127.0.0.1", 8848, constant.WithContextPath("/nacos")),
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")),
} }
cc := constant.ClientConfig{ //create ClientConfig
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", //namespace id cc := *constant.NewClientConfig(
TimeoutMs: 5000, constant.WithNamespaceId(""),
NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
RotateTime: "1h",
MaxAge: 3,
LogLevel: "debug",
}
//or a more graceful way to create ClientConfig
_ = *constant.NewClientConfig(
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"),
constant.WithTimeoutMs(5000), constant.WithTimeoutMs(5000),
constant.WithNotLoadCacheAtStart(true), constant.WithNotLoadCacheAtStart(true),
constant.WithLogDir("/tmp/nacos/log"), constant.WithLogDir("/tmp/nacos/log"),
@ -63,7 +43,7 @@ func main() {
constant.WithLogLevel("debug"), constant.WithLogLevel("debug"),
) )
// a more graceful way to create config client // create config client
client, err := clients.NewConfigClient( client, err := clients.NewConfigClient(
vo.NacosClientParam{ vo.NacosClientParam{
ClientConfig: &cc, ClientConfig: &cc,
@ -90,7 +70,7 @@ func main() {
if err != nil { if err != nil {
fmt.Printf("PublishConfig err:%+v \n", err) fmt.Printf("PublishConfig err:%+v \n", err)
} }
time.Sleep(1 * time.Second)
//get config //get config
content, err := client.GetConfig(vo.ConfigParam{ content, err := client.GetConfig(vo.ConfigParam{
DataId: "test-data", DataId: "test-data",
@ -115,13 +95,15 @@ func main() {
}, },
}) })
time.Sleep(1 * time.Second)
_, err = client.PublishConfig(vo.ConfigParam{ _, err = client.PublishConfig(vo.ConfigParam{
DataId: "test-data", DataId: "test-data",
Group: "test-group", Group: "test-group",
Content: "test-listen", Content: "test-listen",
}) })
time.Sleep(2 * time.Second) time.Sleep(1 * time.Second)
_, err = client.PublishConfig(vo.ConfigParam{ _, err = client.PublishConfig(vo.ConfigParam{
DataId: "test-data-2", DataId: "test-data-2",
@ -137,12 +119,12 @@ func main() {
Group: "test-group", Group: "test-group",
}) })
time.Sleep(2 * time.Second) time.Sleep(1 * time.Second)
_, err = client.DeleteConfig(vo.ConfigParam{ _, err = client.DeleteConfig(vo.ConfigParam{
DataId: "test-data", DataId: "test-data",
Group: "test-group", Group: "test-group",
}) })
time.Sleep(5 * time.Second) time.Sleep(1 * time.Second)
searchPage, _ := client.SearchConfig(vo.SearchConfigParm{ searchPage, _ := client.SearchConfig(vo.SearchConfigParm{
Search: "blur", Search: "blur",

View File

@ -20,38 +20,22 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/clients" "github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/util" "github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/vo" "github.com/nacos-group/nacos-sdk-go/v2/vo"
) )
func main() { func main() {
//create ServerConfig
sc := []constant.ServerConfig{ sc := []constant.ServerConfig{
{ *constant.NewServerConfig("127.0.0.1", 8848, constant.WithContextPath("/nacos")),
IpAddr: "console.nacos.io",
Port: 80,
},
}
//or a more graceful way to create ServerConfig
_ = []constant.ServerConfig{
*constant.NewServerConfig("console.nacos.io", 80),
} }
cc := constant.ClientConfig{ //create ClientConfig
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", //namespace id cc := *constant.NewClientConfig(
TimeoutMs: 5000, constant.WithNamespaceId(""),
NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
RotateTime: "1h",
MaxAge: 3,
LogLevel: "debug",
}
//or a more graceful way to create ClientConfig
_ = *constant.NewClientConfig(
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"),
constant.WithTimeoutMs(5000), constant.WithTimeoutMs(5000),
constant.WithNotLoadCacheAtStart(true), constant.WithNotLoadCacheAtStart(true),
constant.WithLogDir("/tmp/nacos/log"), constant.WithLogDir("/tmp/nacos/log"),
@ -61,7 +45,7 @@ func main() {
constant.WithLogLevel("debug"), constant.WithLogLevel("debug"),
) )
// a more graceful way to create naming client // create naming client
client, err := clients.NewNamingClient( client, err := clients.NewNamingClient(
vo.NacosClientParam{ vo.NacosClientParam{
ClientConfig: &cc, ClientConfig: &cc,
@ -79,6 +63,8 @@ func main() {
Ip: "10.0.0.10", Ip: "10.0.0.10",
Port: 8848, Port: 8848,
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a",
ClusterName: "cluster-a",
Weight: 10, Weight: 10,
Enable: true, Enable: true,
Healthy: true, Healthy: true,
@ -86,56 +72,6 @@ func main() {
Metadata: map[string]string{"idc": "shanghai"}, 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 //DeRegister with ip,port,serviceName
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP //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. //Note:ip=10.0.0.10,port=8848 should belong to the cluster of DEFAULT and the group of DEFAULT_GROUP.
@ -143,72 +79,50 @@ func main() {
Ip: "10.0.0.10", Ip: "10.0.0.10",
Port: 8848, Port: 8848,
ServiceName: "demo.go", ServiceName: "demo.go",
Ephemeral: true, //it must be true GroupName: "group-a",
})
//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",
Cluster: "cluster-a", Cluster: "cluster-a",
Ephemeral: true, //it must be true Ephemeral: true, //it must be true
}) })
//DeRegister with ip,port,serviceName,cluster,group //Register with default cluster and group
ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{ //ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
Ip: "10.0.0.14", ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.10",
Port: 8848, Port: 8848,
ServiceName: "demo.go", ServiceName: "demo.go",
Cluster: "cluster-b", GroupName: "group-a",
GroupName: "group-b", ClusterName: "cluster-a",
Ephemeral: true, //it must be true Weight: 10,
Enable: true,
Healthy: true,
Ephemeral: true,
Metadata: map[string]string{"idc": "shanghai"},
}) })
time.Sleep(1 * time.Second)
//Get service with serviceName //Get service with serviceName
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP //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
ExampleServiceClient_GetService(client, vo.GetServiceParam{ ExampleServiceClient_GetService(client, vo.GetServiceParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a", GroupName: "group-a",
}) Clusters: []string{"cluster-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",
}) })
//SelectAllInstance //SelectAllInstance
//GroupName=DEFAULT_GROUP //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{ ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a", GroupName: "group-a",
Clusters: []string{"cluster-a"},
}) })
//SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0 //SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP //ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
ExampleServiceClient_SelectInstances(client, vo.SelectInstancesParam{ ExampleServiceClient_SelectInstances(client, vo.SelectInstancesParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a",
Clusters: []string{"cluster-a"},
}) })
//SelectOneHealthyInstance return one instance by WRR strategy for load balance //SelectOneHealthyInstance return one instance by WRR strategy for load balance
@ -216,38 +130,36 @@ func main() {
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP //ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
ExampleServiceClient_SelectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{ ExampleServiceClient_SelectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a",
Clusters: []string{"cluster-a"},
}) })
//Subscribe key=serviceName+groupName+cluster //Subscribe key=serviceName+groupName+cluster
//Note:We call add multiple SubscribeCallback with the same key. //Note:We call add multiple SubscribeCallback with the same key.
param := &vo.SubscribeParam{ param := &vo.SubscribeParam{
ServiceName: "demo.go", ServiceName: "demo.go",
Clusters: []string{"cluster-b"}, GroupName: "group-a",
SubscribeCallback: func(services []model.SubscribeService, err error) { Clusters: []string{"cluster-a"},
SubscribeCallback: func(services []model.Instance, err error) {
fmt.Printf("callback111 return services:%s \n\n", util.ToJsonString(services)) fmt.Printf("callback111 return services:%s \n\n", util.ToJsonString(services))
}, },
} }
ExampleServiceClient_Subscribe(client, param) 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{ ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.112", Ip: "10.0.0.10",
Port: 8848, Port: 8848,
ServiceName: "demo.go", ServiceName: "demo.go",
GroupName: "group-a",
ClusterName: "cluster-a",
Weight: 10, Weight: 10,
ClusterName: "cluster-b",
Enable: true, Enable: true,
Healthy: true, Healthy: true,
Ephemeral: true, Ephemeral: true,
Metadata: map[string]string{"idc": "beijing"},
}) })
//wait for client pull change from server //wait for client pull change from server
time.Sleep(10 * time.Second) time.Sleep(3 * time.Second)
//Now we just unsubscribe callback1, and callback2 will still receive change event //Now we just unsubscribe callback1, and callback2 will still receive change event
ExampleServiceClient_UnSubscribe(client, param) ExampleServiceClient_UnSubscribe(client, param)
@ -259,7 +171,7 @@ func main() {
Cluster: "cluster-b", Cluster: "cluster-b",
}) })
//wait for client pull change from server //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 //GeAllService will get the list of service name
//NameSpace default value is public.If the client set the namespaceId, NameSpace will use it. //NameSpace default value is public.If the client set the namespaceId, NameSpace will use it.
@ -268,10 +180,4 @@ func main() {
PageNo: 1, PageNo: 1,
PageSize: 10, PageSize: 10,
}) })
ExampleServiceClient_GetAllService(client, vo.GetAllServiceInfoParam{
NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f",
PageNo: 1,
PageSize: 10,
})
} }

View File

@ -19,8 +19,9 @@ package main
import ( import (
"fmt" "fmt"
"github.com/nacos-group/nacos-sdk-go/clients/naming_client" "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
) )
func ExampleServiceClient_RegisterServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) { func ExampleServiceClient_RegisterServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) {
@ -50,7 +51,7 @@ func ExampleServiceClient_SelectInstances(client naming_client.INamingClient, pa
func ExampleServiceClient_SelectOneHealthyInstance(client naming_client.INamingClient, param vo.SelectOneHealthInstanceParam) { func ExampleServiceClient_SelectOneHealthyInstance(client naming_client.INamingClient, param vo.SelectOneHealthInstanceParam) {
instances, _ := client.SelectOneHealthyInstance(param) instances, _ := client.SelectOneHealthyInstance(param)
fmt.Printf("SelectInstances,param:%+v, result:%+v \n\n", param, instances) fmt.Printf("SelectOneHealthyInstance,param:%+v, result:%+v \n\n", param, instances)
} }
func ExampleServiceClient_Subscribe(client naming_client.INamingClient, param *vo.SubscribeParam) { func ExampleServiceClient_Subscribe(client naming_client.INamingClient, param *vo.SubscribeParam) {

6
go.mod
View File

@ -1,6 +1,6 @@
module github.com/nacos-group/nacos-sdk-go module github.com/nacos-group/nacos-sdk-go/v2
go 1.12 go 1.14
require ( require (
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 github.com/aliyun/alibaba-cloud-sdk-go v1.61.18
@ -8,6 +8,7 @@ require (
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/go-errors/errors v1.0.1 github.com/go-errors/errors v1.0.1
github.com/golang/mock v1.3.1 github.com/golang/mock v1.3.1
github.com/golang/protobuf v1.4.2
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
github.com/jonboulle/clockwork v0.1.0 // indirect github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/json-iterator/go v1.1.6 // indirect github.com/json-iterator/go v1.1.6 // indirect
@ -19,5 +20,6 @@ require (
github.com/tebeka/strftime v0.1.3 // indirect github.com/tebeka/strftime v0.1.3 // indirect
github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3
go.uber.org/zap v1.15.0 go.uber.org/zap v1.15.0
google.golang.org/grpc v1.36.1
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
) )

77
go.sum
View File

@ -1,20 +1,51 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU= github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= 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/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/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/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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
@ -49,6 +80,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 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/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -73,26 +105,69 @@ go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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-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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/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-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 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-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.36.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
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.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
@ -102,5 +177,7 @@ gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/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.0-20190102054323-c2f93a96b099/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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -31,3 +31,16 @@ type ConfigPage struct {
PagesAvailable int `param:"pagesAvailable"` PagesAvailable int `param:"pagesAvailable"`
PageItems []ConfigItem `param:"pageItems"` 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"`
}

View File

@ -24,31 +24,32 @@ const (
) )
type Instance struct { type Instance struct {
Valid bool `json:"valid"`
Marked bool `json:"marked"`
InstanceId string `json:"instanceId"` InstanceId string `json:"instanceId"`
Port uint64 `json:"port"`
Ip string `json:"ip"` Ip string `json:"ip"`
Port uint64 `json:"port"`
Weight float64 `json:"weight"` 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"` ClusterName string `json:"clusterName"`
ServiceName string `json:"serviceName"` ServiceName string `json:"serviceName"`
Enable bool `json:"enabled"` Metadata map[string]string `json:"metadata"`
Healthy bool `json:"healthy"` InstanceHeartBeatInterval int `json:"instanceHeartBeatInterval"`
Ephemeral bool `json:"ephemeral"` IpDeleteTimeout int `json:"ipDeleteTimeout"`
InstanceHeartBeatTimeOut int `json:"instanceHeartBeatTimeOut"`
} }
type Service struct { type Service struct {
Dom string `json:"dom"`
CacheMillis uint64 `json:"cacheMillis"` CacheMillis uint64 `json:"cacheMillis"`
UseSpecifiedURL bool `json:"useSpecifiedUrl"`
Hosts []Instance `json:"hosts"` Hosts []Instance `json:"hosts"`
Checksum string `json:"checksum"` Checksum string `json:"checksum"`
LastRefTime uint64 `json:"lastRefTime"` LastRefTime uint64 `json:"lastRefTime"`
Env string `json:"env"`
Clusters string `json:"clusters"` Clusters string `json:"clusters"`
Metadata map[string]string `json:"metadata"`
Name string `json:"name"` Name string `json:"name"`
GroupName string `json:"groupName"`
Valid bool `json:"valid"`
AllIPs bool `json:"allIPs"`
ReachProtectionThreshold bool `json:"reachProtectionThreshold"`
} }
type ServiceDetail struct { type ServiceDetail struct {
@ -84,18 +85,6 @@ type ClusterHealthChecker struct {
Type string `json:"type"` 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"`
}
type BeatInfo struct { type BeatInfo struct {
Ip string `json:"ip"` Ip string `json:"ip"`
Port uint64 `json:"port"` Port uint64 `json:"port"`

View File

@ -23,9 +23,9 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/logger" "github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/v2/model"
) )
func CurrentMillis() int64 { func CurrentMillis() int64 {

29
util/content.go Normal file
View File

@ -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]
}

View File

@ -22,7 +22,7 @@ import (
"strconv" "strconv"
"strings" "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) { func TransformObject2Param(object interface{}) (params map[string]string) {

View File

@ -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 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 { type NacosClientParam struct {
ClientConfig *constant.ClientConfig // optional ClientConfig *constant.ClientConfig // optional

View File

@ -22,7 +22,12 @@ type ConfigParam struct {
DataId string `param:"dataId"` //required DataId string `param:"dataId"` //required
Group string `param:"group"` //required Group string `param:"group"` //required
Content string `param:"content"` //required Content string `param:"content"` //required
DatumId string `param:"datumId"` 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) OnChange func(namespace, group, dataId, data string)
} }

View File

@ -16,7 +16,7 @@
package vo package vo
import "github.com/nacos-group/nacos-sdk-go/model" import "github.com/nacos-group/nacos-sdk-go/v2/model"
type RegisterInstanceParam struct { type RegisterInstanceParam struct {
Ip string `param:"ip"` //required Ip string `param:"ip"` //required
@ -57,7 +57,7 @@ type SubscribeParam struct {
ServiceName string `param:"serviceName"` //required ServiceName string `param:"serviceName"` //required
Clusters []string `param:"clusters"` //optional,default:DEFAULT Clusters []string `param:"clusters"` //optional,default:DEFAULT
GroupName string `param:"groupName"` //optional,default:DEFAULT_GROUP 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 { type SelectAllInstancesParam struct {