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"
"github.com/go-errors/errors"
"github.com/nacos-group/nacos-sdk-go/common/file"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/v2/common/file"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
)
func GetFileName(cacheKey string, cacheDir string) string {

View File

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

View File

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

View File

@ -19,23 +19,28 @@ package config_client
import (
"errors"
"fmt"
"math"
"os"
"strconv"
"strings"
"sync"
"time"
"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/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/nacos-group/nacos-sdk-go/common/nacos_error"
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/inner/uuid"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
const (
perTaskConfigSize = 3000
executorErrDelay = 5 * time.Second
)
type ConfigClient struct {
@ -43,32 +48,27 @@ type ConfigClient struct {
kmsClient *kms.Client
localConfigs []vo.ConfigParam
mutex sync.Mutex
configProxy ConfigProxy
configProxy IConfigProxy
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 {
isInitializing bool
dataId string
group string
content string
contentType string
tenant string
cacheDataListener *cacheDataListener
md5 string
appName string
taskId int
configClient *ConfigClient
isSyncWithServer bool
}
type cacheDataListener struct {
@ -76,65 +76,58 @@ type cacheDataListener struct {
lastMd5 string
}
func init() {
schedulerMap.Set("root", true)
go delayScheduler(time.NewTimer(1*time.Millisecond), 10*time.Millisecond, "root", listenConfigExecutor())
}
func NewConfigClient(nc nacos_client.INacosClient) (ConfigClient, error) {
config := ConfigClient{}
func NewConfigClient(nc nacos_client.INacosClient) (*ConfigClient, error) {
config := &ConfigClient{}
config.INacosClient = nc
clientConfig, err := nc.GetClientConfig()
if err != nil {
return config, err
return nil, err
}
serverConfig, err := nc.GetServerConfig()
if err != nil {
return config, err
return nil, err
}
httpAgent, err := nc.GetHttpAgent()
if err != nil {
return config, err
return nil, err
}
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,
OutputPath: clientConfig.LogDir,
RotationTime: clientConfig.RotateTime,
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) {
@ -186,55 +179,55 @@ func (client *ConfigClient) getConfigInner(param vo.ConfigParam) (content string
return "", err
}
if len(param.Group) <= 0 {
err = errors.New("[client.GetConfig] param.group can not be empty")
return "", err
param.Group = constant.DEFAULT_GROUP
}
//todo 优先使用本地配置
//todo 获取容灾配置的 EncryptedDataKey LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover
clientConfig, _ := client.GetClientConfig()
cacheKey := util.GetConfigCacheKey(param.DataId, param.Group, clientConfig.NamespaceId)
content, err = client.configProxy.GetConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
response, err := client.configProxy.queryConfig(param.DataId, param.Group, clientConfig.NamespaceId,
clientConfig.TimeoutMs, false, client)
if err != nil {
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)
if err != nil {
logger.Errorf("get config from cache error:%+v ", err)
return "", errors.New("read config from both server and cache fail")
}
} else {
cache.WriteConfigToFile(cacheKey, client.configCacheDir, content)
}
return content, nil
}
return response.Content, nil
}
func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool,
err error) {
func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool, err error) {
if len(param.DataId) <= 0 {
err = errors.New("[client.PublishConfig] param.dataId can not be empty")
}
if len(param.Group) <= 0 {
err = errors.New("[client.PublishConfig] param.group can not be empty")
}
if len(param.Content) <= 0 {
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)
if err != nil {
return false, err
}
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) {
@ -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")
}
if len(param.Group) <= 0 {
err = errors.New("[client.DeleteConfig] param.group can not be empty")
param.Group = constant.DEFAULT_GROUP
}
if err != nil {
return false, err
}
clientConfig, _ := client.GetClientConfig()
return client.configProxy.DeleteConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
request := rpc_request.NewConfigRemoveRequest(param.Group, param.DataId, clientConfig.NamespaceId)
rpcClient := client.configProxy.getRpcClient(client)
response, err := client.configProxy.requestProxy(rpcClient, request, constant.DEFAULT_TIMEOUT_MILLS)
return response.IsSuccess(), err
}
//Cancel Listen Config
@ -256,33 +255,11 @@ func (client *ConfigClient) CancelListenConfig(param vo.ConfigParam) (err error)
logger.Errorf("[checkConfigInfo.GetClientConfig] failed,err:%+v", err)
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)
remakeId := int(math.Ceil(float64(cacheMap.Count()) / float64(perTaskConfigSize)))
if remakeId < currentTaskCount {
remakeCacheDataTaskId(remakeId)
}
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) {
if len(param.DataId) <= 0 {
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)
var cData cacheData
if v, ok := cacheMap.Get(key); ok {
if v, ok := client.cacheMap.Get(key); ok {
cData = v.(cacheData)
cData.isInitializing = true
} else {
@ -328,150 +305,11 @@ func (client *ConfigClient) ListenConfig(param vo.ConfigParam) (err error) {
content: content,
md5: md5Str,
cacheDataListener: listener,
taskId: cacheMap.Count() / perTaskConfigSize,
taskId: client.cacheMap.Count() / perTaskConfigSize,
configClient: 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
client.cacheMap.Set(key, &cData)
return
}
@ -479,42 +317,6 @@ func (client *ConfigClient) SearchConfig(param vo.SearchConfigParm) (*model.Conf
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) {
if param.Search != "accurate" && param.Search != "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
}
clientConfig, _ := client.GetClientConfig()
configItems, err := client.configProxy.SearchConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
configItems, err := client.configProxy.searchConfigProxy(param, clientConfig.NamespaceId, clientConfig.AccessKey, clientConfig.SecretKey)
if err != nil {
logger.Errorf("search config from server error:%+v ", err)
if _, ok := err.(*nacos_error.NacosError); ok {
@ -542,3 +344,130 @@ func (client *ConfigClient) searchConfigInner(param vo.SearchConfigParm) (*model
}
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
import (
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
//go:generate mockgen -destination ../../mock/mock_config_client_interface.go -package mock -source=./config_client_interface.go
@ -64,6 +64,4 @@ type IConfigClient interface {
// pageNo option,default is 1
// pageSize option,default is 10
SearchConfig(param vo.SearchConfigParm) (*model.ConfigPage, error)
PublishAggr(param vo.ConfigParam) (published bool, err error)
}

View File

@ -17,47 +17,21 @@
package config_client
import (
"errors"
"fmt"
"net/http"
"runtime"
"strconv"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/nacos-group/nacos-sdk-go/clients/cache"
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/mock"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/stretchr/testify/assert"
)
var goVersion = runtime.Version()
var clientConfigTest = constant.ClientConfig{
TimeoutMs: 10000,
ListenInterval: 20000,
BeatInterval: 10000,
}
var clientConfigTestWithTenant = constant.ClientConfig{
TimeoutMs: 10000,
ListenInterval: 20000,
BeatInterval: 10000,
NamespaceId: "tenant",
}
var serverConfigTest = constant.ServerConfig{
ContextPath: "/nacos",
Port: 80,
IpAddr: "console.nacos.io",
}
var serverConfigWithOptions = constant.NewServerConfig("console.nacos.io", 80, constant.WithContextPath("/nacos"))
var serverConfigWithOptions = constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos"))
var clientConfigWithOptions = constant.NewClientConfig(
constant.WithTimeoutMs(10*1000),
@ -65,116 +39,69 @@ var clientConfigWithOptions = constant.NewClientConfig(
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{
DataId: dataIdKey,
DataId: "dataId",
Group: "group",
Content: "content",
}
var localConfigMapTest = map[string]string{
"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 {
func createConfigClientTest() *ConfigClient {
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
nc.SetClientConfig(*clientConfigWithOptions)
nc.SetHttpAgent(&http_agent.HttpAgent{})
_ = nc.SetServerConfig([]constant.ServerConfig{*serverConfigWithOptions})
_ = nc.SetClientConfig(*clientConfigWithOptions)
_ = nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewConfigClient(&nc)
client.configProxy = &MockConfigProxy{}
return client
}
func createConfigClientTestWithTenant() ConfigClient {
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTestWithTenant)
nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewConfigClient(&nc)
return client
type MockConfigProxy struct {
}
func createConfigClientHttpTest(mockHttpAgent http_agent.IHttpAgent) ConfigClient {
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockHttpAgent)
client, _ := NewConfigClient(&nc)
return client
func (m *MockConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
return &rpc_response.ConfigQueryResponse{Content: "hello world"}, nil
}
func createConfigClientHttpTestWithTenant(mockHttpAgent http_agent.IHttpAgent) ConfigClient {
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTestWithTenant)
nc.SetHttpAgent(mockHttpAgent)
client, _ := NewConfigClient(&nc)
return client
func (m *MockConfigProxy) searchConfigProxy(param vo.SearchConfigParm, tenant, accessKey, secretKey string) (*model.ConfigPage, error) {
return &model.ConfigPage{TotalCount: 1}, nil
}
func (m *MockConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) {
return &rpc_response.MockResponse{Response: &rpc_response.Response{Success: true}}, nil
}
func (m *MockConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
return &rpc.RpcClient{}
}
func (m *MockConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
return &rpc.RpcClient{}
}
func Test_GetConfig(t *testing.T) {
client := createConfigClientTest()
success, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey,
Group: "group",
Content: "hello world!222222"})
DataId: localConfigTest.DataId,
Group: localConfigTest.Group,
Content: "hello world"})
assert.Nil(t, err)
assert.True(t, success)
content, err := client.GetConfig(vo.ConfigParam{
DataId: dataIdKey,
DataId: localConfigTest.DataId,
Group: "group"})
assert.Nil(t, err)
assert.Equal(t, "hello world!222222", content)
assert.Equal(t, "hello world", content)
}
func Test_SearchConfig(t *testing.T) {
client := createConfigClientTest()
client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey,
Group: "groDEFAULT_GROUPup",
Content: "hello world!222222"})
_, _ = client.PublishConfig(vo.ConfigParam{
DataId: localConfigTest.DataId,
Group: "DEFAULT_GROUP",
Content: "hello world"})
configPage, err := client.SearchConfig(vo.SearchConfigParm{
Search: "accurate",
DataId: dataIdKey,
Group: "groDEFAULT_GROUPup",
DataId: localConfigTest.DataId,
Group: "DEFAULT_GROUP",
PageNo: 1,
PageSize: 10,
})
@ -182,86 +109,7 @@ func Test_SearchConfig(t *testing.T) {
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
func Test_PublishConfigWithoutDataId(t *testing.T) {
client := createConfigClientTest()
_, err := client.PublishConfig(vo.ConfigParam{
@ -272,20 +120,10 @@ func Test_PublishConfigWithoutDataId(t *testing.T) {
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) {
client := createConfigClientTest()
_, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey,
DataId: localConfigTest.DataId,
Group: "group",
Content: "",
})
@ -297,56 +135,21 @@ func Test_PublishConfig(t *testing.T) {
client := createConfigClientTest()
success, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey,
DataId: localConfigTest.DataId,
Group: "group",
Content: "hello world2!"})
Content: "hello world"})
assert.Nil(t, err)
assert.True(t, success)
}
func Test_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
func Test_DeleteConfig(t *testing.T) {
client := createConfigClientTest()
success, err := client.PublishConfig(vo.ConfigParam{
DataId: dataIdKey,
DataId: localConfigTest.DataId,
Group: "group",
Content: "hello world!"})
@ -354,49 +157,13 @@ func Test_DeleteConfig(t *testing.T) {
assert.True(t, success)
success, err = client.DeleteConfig(vo.ConfigParam{
DataId: dataIdKey,
DataId: localConfigTest.DataId,
Group: "group"})
assert.Nil(t, err)
assert.True(t, success)
}
func Test_DeleteConfigWithErrorResponse_200(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockHttpAgent := mock.NewMockIHttpAgent(controller)
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) {
client := createConfigClientTest()
success, err := client.DeleteConfig(vo.ConfigParam{
@ -407,59 +174,21 @@ func Test_DeleteConfigWithoutDataId(t *testing.T) {
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) {
// ListenConfig
t.Run("TestListenConfig", func(t *testing.T) {
client := createConfigClientTest()
key := util.GetConfigCacheKey(localConfigTest.DataId, localConfigTest.Group, clientConfigTest.NamespaceId)
cache.WriteConfigToFile(key, client.configCacheDir, "")
var err error
var success bool
ch := make(chan string)
go func() {
err = client.ListenConfig(vo.ConfigParam{
err := client.ListenConfig(vo.ConfigParam{
DataId: localConfigTest.DataId,
Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
ch <- data
},
})
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
t.Run("TestListenConfigNoDataId", func(t *testing.T) {
listenConfigParam := vo.ConfigParam{
Group: "gateway",
Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) {
},
}
@ -467,107 +196,6 @@ func TestListen(t *testing.T) {
err := client.ListenConfig(listenConfigParam)
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
@ -576,120 +204,24 @@ func TestCancelListenConfig(t *testing.T) {
t.Run("TestMultipleListenersCancelOne", func(t *testing.T) {
client := createConfigClientTest()
var err error
var success bool
var context string
listenConfigParam := vo.ConfigParam{
DataId: cancelOneKey,
Group: "group",
DataId: localConfigTest.DataId,
Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
},
}
listenConfigParam1 := vo.ConfigParam{
DataId: cancelOne1Key,
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,
DataId: localConfigTest.DataId + "1",
Group: localConfigTest.Group,
OnChange: func(namespace, group, dataId, data string) {
fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data)
context = data
ch <- data
},
}
go func() {
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)
_ = client.ListenConfig(listenConfigParam)
select {
case c := <-ch:
assert.Equal(t, c, localConfigTest.Content)
}
//Cancel listen config
client.CancelListenConfig(listenConfigParam)
_ = client.ListenConfig(listenConfigParam1)
success, err = client.PublishConfig(vo.ConfigParam{
DataId: cancelListenConfigKey,
Group: localConfigTest.Group,
Content: "abcd"})
err = client.CancelListenConfig(listenConfigParam)
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"
"net/http"
"strconv"
"strings"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/nacos-group/nacos-sdk-go/common/nacos_server"
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request"
"github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
type ConfigProxy struct {
@ -37,34 +41,35 @@ type ConfigProxy struct {
clientConfig constant.ClientConfig
}
func NewConfigProxy(serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent) (ConfigProxy, error) {
func NewConfigProxy(serverConfig []constant.ServerConfig, clientConfig constant.ClientConfig, httpAgent http_agent.IHttpAgent) (IConfigProxy, error) {
proxy := ConfigProxy{}
var err error
proxy.nacosServer, err = nacos_server.NewNacosServer(serverConfig, clientConfig, httpAgent, clientConfig.TimeoutMs, clientConfig.Endpoint)
proxy.clientConfig = clientConfig
return proxy, err
return &proxy, err
}
func (cp *ConfigProxy) GetServerList() []constant.ServerConfig {
return cp.nacosServer.GetServerList()
func (cp *ConfigProxy) requestProxy(rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) (rpc_response.IResponse, error) {
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) {
params := util.TransformObject2Param(param)
if len(tenant) > 0 {
params["tenant"] = tenant
}
var headers = map[string]string{}
headers["accessKey"] = accessKey
headers["secretKey"] = secretKey
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodGet, cp.clientConfig.TimeoutMs)
return result, err
func (cp *ConfigProxy) injectCommHeader(param map[string]string) {
now := string(util.CurrentMillis())
param[constant.CLIENT_APPNAME_HEADER] = cp.clientConfig.AppName
param[constant.CLIENT_REQUEST_TS_HEADER] = now
param[constant.CLIENT_REQUEST_TOKEN_HEADER] = util.Md5(now + cp.clientConfig.AppKey)
param[constant.EX_CONFIG_INFO] = "true"
param[constant.CHARSET_KEY] = "utf-8"
}
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)
if len(tenant) > 0 {
params["tenant"] = tenant
@ -89,98 +94,97 @@ func (cp *ConfigProxy) SearchConfigProxy(param vo.SearchConfigParm, tenant, acce
}
return &configPage, nil
}
func (cp *ConfigProxy) PublishConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) {
params := util.TransformObject2Param(param)
if len(tenant) > 0 {
params["tenant"] = tenant
}
var headers = map[string]string{}
headers["accessKey"] = accessKey
headers["secretKey"] = secretKey
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs)
func (cp *ConfigProxy) queryConfig(dataId, group, tenant string, timeout uint64, notify bool, client *ConfigClient) (*rpc_response.ConfigQueryResponse, error) {
if group == "" {
group = constant.DEFAULT_GROUP
}
configQueryRequest := rpc_request.NewConfigQueryRequest(group, dataId, tenant)
configQueryRequest.Headers["notify"] = strconv.FormatBool(notify)
iResponse, err := cp.requestProxy(cp.getRpcClient(client), configQueryRequest, timeout)
if err != nil {
return false, errors.New("[client.PublishConfig] publish config failed:" + err.Error())
return nil, err
}
if strings.ToLower(strings.Trim(result, " ")) == "true" {
return true, nil
} else {
return false, errors.New("[client.PublishConfig] publish config failed:" + result)
response, ok := iResponse.(*rpc_response.ConfigQueryResponse)
if !ok {
return nil, errors.New("ConfigQueryRequest returns type error")
}
if response.IsSuccess() {
//todo LocalConfigInfoProcessor.saveSnapshot
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) {
params := util.TransformObject2Param(param)
if len(tenant) > 0 {
params["tenant"] = tenant
func (cp *ConfigProxy) createRpcClient(taskId string, client *ConfigClient) *rpc.RpcClient {
labels := map[string]string{
constant.LABEL_SOURCE: constant.LABEL_SOURCE_SDK,
constant.LABEL_MODULE: constant.LABEL_MODULE_CONFIG,
"taskId": taskId,
}
params["method"] = "addDatum"
var headers = map[string]string{}
headers["accessKey"] = accessKey
headers["secretKey"] = secretKey
_, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_AGG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs)
if err != nil {
return false, errors.New("[client.PublishAggProxy] publish agg failed:" + err.Error())
iRpcClient, _ := rpc.CreateClient("config-"+taskId+"-"+client.uid, rpc.GRPC, labels, cp.nacosServer)
rpcClient := iRpcClient.GetRpcClient()
if rpcClient.IsInitialized() {
rpcClient.RegisterServerRequestHandler(func() rpc_request.IRequest {
return &rpc_request.ConfigChangeNotifyRequest{ConfigRequest: rpc_request.NewConfigRequest()}
}, &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) {
params := util.TransformObject2Param(param)
if len(tenant) > 0 {
params["tenant"] = tenant
}
params["method"] = "deleteDatum"
var headers = map[string]string{}
headers["accessKey"] = accessKey
headers["secretKey"] = secretKey
_, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_AGG_PATH, params, headers, http.MethodPost, cp.clientConfig.TimeoutMs)
if err != nil {
return false, errors.New("[client.DeleteAggProxy] delete agg failed:" + err.Error())
}
return true, nil
func (cp *ConfigProxy) getRpcClient(client *ConfigClient) *rpc.RpcClient {
return cp.createRpcClient("0", client)
}
func (cp *ConfigProxy) DeleteConfigProxy(param vo.ConfigParam, tenant, accessKey, secretKey string) (bool, error) {
params := util.TransformObject2Param(param)
if len(tenant) > 0 {
params["tenant"] = tenant
}
var headers = map[string]string{}
headers["accessKey"] = accessKey
headers["secretKey"] = secretKey
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_PATH, params, headers, http.MethodDelete, cp.clientConfig.TimeoutMs)
if err != nil {
return false, errors.New("[client.DeleteConfig] deleted config failed:" + err.Error())
}
if strings.ToLower(strings.Trim(result, " ")) == "true" {
return true, nil
} else {
return false, errors.New("[client.DeleteConfig] deleted config failed: " + string(result))
}
type ConfigChangeNotifyRequestHandler struct {
client *ConfigClient
}
func (cp *ConfigProxy) ListenConfig(params map[string]string, isInitializing bool, tenant, accessKey, secretKey string) (string, error) {
//fixed at 30000msavoid frequent request on the server
var listenInterval uint64 = 30000
headers := map[string]string{
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"Long-Pulling-Timeout": strconv.FormatUint(listenInterval, 10),
}
if isInitializing {
headers["Long-Pulling-Timeout-No-Hangup"] = "true"
}
func (c *ConfigChangeNotifyRequestHandler) RequestReply(request rpc_request.IRequest, rpcClient *rpc.RpcClient) rpc_response.IResponse {
configChangeNotifyRequest, ok := request.(*rpc_request.ConfigChangeNotifyRequest)
if ok {
logger.Infof("%s [server-push] config changed. dataId=%s, group=%s,tenant=%s", rpcClient.Name,
configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group, configChangeNotifyRequest.Tenant)
headers["accessKey"] = accessKey
headers["secretKey"] = secretKey
if len(tenant) > 0 {
params["tenant"] = tenant
cacheKey := util.GetConfigCacheKey(configChangeNotifyRequest.DataId, configChangeNotifyRequest.Group,
configChangeNotifyRequest.Tenant)
cache, ok := c.client.cacheMap.Get(cacheKey)
if !ok {
return nil
}
logger.Infof("[client.ListenConfig] request params:%+v header:%+v \n", params, headers)
// In order to prevent the server from handling the delay of the client's long task,
// increase the client's read timeout to avoid this problem.
timeout := listenInterval + listenInterval/10
result, err := cp.nacosServer.ReqConfigApi(constant.CONFIG_LISTEN_PATH, params, headers, http.MethodPost, timeout)
return result, err
cacheData := cache.(*cacheData)
cacheData.isSyncWithServer = false
c.client.notifyListenConfig()
return &rpc_response.NotifySubscriberResponse{
Response: &rpc_response.Response{ResultCode: constant.RESPONSE_CODE_SUCCESS},
}
}
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"
"strconv"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/file"
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/file"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
)
type NacosClient struct {
@ -68,7 +68,6 @@ func (client *NacosClient) SetClientConfig(config constant.ClientConfig) (err er
if config.LogDir == "" {
config.LogDir = file.GetCurrentPath() + string(os.PathSeparator) + "log"
}
log.Printf("[INFO] logDir:<%s> cacheDir:<%s>", config.LogDir, config.CacheDir)
client.clientConfig = config
client.clientConfigValid = true

View File

@ -17,8 +17,8 @@
package nacos_client
import (
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
)
//go:generate mockgen -destination mock_nacos_client_interface.go -package nacos_client -source=./nacos_client_interface.go

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

View File

@ -19,72 +19,70 @@ package naming_client
import (
"math"
"math/rand"
"os"
"sort"
"strings"
"time"
"github.com/nacos-group/nacos-sdk-go/clients/cache"
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_proxy"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/pkg/errors"
)
type NamingClient struct {
nacos_client.INacosClient
hostReactor HostReactor
serviceProxy NamingProxy
subCallback SubscribeCallback
beatReactor BeatReactor
indexMap cache.ConcurrentMap
NamespaceId string
serviceProxy naming_proxy.INamingProxy
serviceInfoHolder *naming_cache.ServiceInfoHolder
}
type Chooser struct {
data []model.Instance
totals []int
max int
}
func NewNamingClient(nc nacos_client.INacosClient) (NamingClient, error) {
naming := NamingClient{}
func NewNamingClient(nc nacos_client.INacosClient) (*NamingClient, error) {
rand.Seed(time.Now().UnixNano())
naming := &NamingClient{INacosClient: nc}
clientConfig, err := nc.GetClientConfig()
if err != nil {
return naming, err
}
naming.NamespaceId = clientConfig.NamespaceId
serverConfig, err := nc.GetServerConfig()
if err != nil {
return naming, err
}
httpAgent, err := nc.GetHttpAgent()
if err != nil {
return naming, err
}
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,
OutputPath: clientConfig.LogDir,
RotationTime: clientConfig.RotateTime,
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,
Ephemeral: param.Ephemeral,
}
beatInfo := model.BeatInfo{
Ip: param.Ip,
Port: param.Port,
Metadata: param.Metadata,
ServiceName: util.GetGroupName(param.ServiceName, param.GroupName),
Cluster: param.ClusterName,
Weight: param.Weight,
Period: util.GetDurationWithDefault(param.Metadata, constant.HEART_BEAT_INTERVAL, time.Second*5),
State: model.StateRunning,
}
_, err := sc.serviceProxy.RegisterInstance(util.GetGroupName(param.ServiceName, param.GroupName), param.GroupName, instance)
if err != nil {
return false, err
}
if instance.Ephemeral {
sc.beatReactor.AddBeatInfo(util.GetGroupName(param.ServiceName, param.GroupName), beatInfo)
}
return true, nil
return sc.serviceProxy.RegisterInstance(param.ServiceName, param.GroupName, instance)
}
@ -134,21 +116,26 @@ func (sc *NamingClient) DeregisterInstance(param vo.DeregisterInstanceParam) (bo
if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP
}
sc.beatReactor.RemoveBeatInfo(util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port)
_, err := sc.serviceProxy.DeregisterInstance(util.GetGroupName(param.ServiceName, param.GroupName), param.Ip, param.Port, param.Cluster, param.Ephemeral)
if err != nil {
return false, err
instance := model.Instance{
Ip: param.Ip,
Port: param.Port,
ClusterName: param.Cluster,
Ephemeral: param.Ephemeral,
}
return true, nil
return sc.serviceProxy.DeregisterInstance(param.ServiceName, param.GroupName, instance)
}
// 获取服务列表
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 {
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
}
@ -156,22 +143,33 @@ func (sc *NamingClient) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (mod
if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP
}
clientConfig, _ := sc.GetClientConfig()
if len(param.NameSpace) == 0 {
if len(sc.NamespaceId) == 0 {
if len(clientConfig.NamespaceId) == 0 {
param.NameSpace = constant.DEFAULT_NAMESPACE_ID
} else {
param.NameSpace = sc.NamespaceId
param.NameSpace = clientConfig.NamespaceId
}
}
services := sc.hostReactor.GetAllServiceInfo(param.NameSpace, param.GroupName, param.PageNo, param.PageSize)
return services, nil
services, err := sc.serviceProxy.GetServiceList(param.PageNo, param.PageSize, param.GroupName, &model.ExpressionSelector{})
return services, err
}
func (sc *NamingClient) SelectAllInstances(param vo.SelectAllInstancesParam) ([]model.Instance, error) {
if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP
}
service, err := sc.hostReactor.GetServiceInfo(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
clusters := strings.Join(param.Clusters, ",")
var (
service model.Service
ok bool
err error
)
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
if !ok {
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
}
if err != nil || service.Hosts == nil || len(service.Hosts) == 0 {
return []model.Instance{}, err
}
@ -182,10 +180,19 @@ func (sc *NamingClient) SelectInstances(param vo.SelectInstancesParam) ([]model.
if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP
}
service, err := sc.hostReactor.GetServiceInfo(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
var (
service model.Service
ok bool
err error
)
clusters := strings.Join(param.Clusters, ",")
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
if !ok {
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
if err != nil {
return nil, err
}
}
return sc.selectInstances(service, param.HealthyOnly)
}
@ -207,10 +214,20 @@ func (sc *NamingClient) SelectOneHealthyInstance(param vo.SelectOneHealthInstanc
if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP
}
service, err := sc.hostReactor.GetServiceInfo(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","))
var (
service model.Service
ok bool
err error
)
clusters := strings.Join(param.Clusters, ",")
service, ok = sc.serviceInfoHolder.GetServiceInfo(param.ServiceName, param.GroupName, clusters)
if !ok {
service, err = sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
if err != nil {
return nil, err
}
}
return sc.selectOneHealthyInstances(service)
}
@ -234,84 +251,29 @@ func (sc *NamingClient) selectOneHealthyInstances(service model.Service) (*model
return nil, errors.New("healthy instance list is empty!")
}
chooser := newChooser(result)
instance := chooser.pick()
instance := newChooser(result).pick()
return &instance, nil
}
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 {
if len(param.GroupName) == 0 {
param.GroupName = constant.DEFAULT_GROUP
}
serviceParam := vo.GetServiceParam{
ServiceName: param.ServiceName,
GroupName: param.GroupName,
Clusters: param.Clusters,
}
sc.subCallback.AddCallbackFuncs(util.GetGroupName(param.ServiceName, param.GroupName), strings.Join(param.Clusters, ","), &param.SubscribeCallback)
svc, err := sc.GetService(serviceParam)
if err != nil {
return err
}
if !sc.hostReactor.serviceProxy.clientConfig.NotLoadCacheAtStart {
sc.subCallback.ServiceChanged(&svc)
}
clusters := strings.Join(param.Clusters, ",")
sc.serviceInfoHolder.RegisterCallback(util.GetGroupName(param.ServiceName, param.GroupName), clusters, &param.SubscribeCallback)
sc.serviceProxy.Subscribe(param.ServiceName, param.GroupName, clusters)
return nil
}
//取消服务监听
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
}

View File

@ -16,8 +16,8 @@
package naming_client
import (
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
//go:generate mockgen -destination ../../mock/mock_service_client_interface.go -package mock -source=./service_client_interface.go

View File

@ -17,18 +17,14 @@
package naming_client
import (
"fmt"
"net/http"
"testing"
"github.com/golang/mock/gomock"
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/mock"
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/util"
"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/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
"github.com/stretchr/testify/assert"
)
@ -38,7 +34,7 @@ var clientConfigTest = *constant.NewClientConfig(
constant.WithNotLoadCacheAtStart(true),
)
var serverConfigTest = *constant.NewServerConfig("console.nacos.io", 80, constant.WithContextPath("/nacos"))
var serverConfigTest = *constant.NewServerConfig("127.0.0.1", 80, constant.WithContextPath("/nacos"))
var headers = map[string][]string{
"Client-Version": {constant.CLIENT_VERSION},
@ -49,37 +45,46 @@ var headers = map[string][]string{
"Content-Type": {"application/x-www-form-urlencoded"},
}
func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) {
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
gomock.AssignableToTypeOf(http.Header{}),
gomock.Eq(uint64(10*1000)),
gomock.Eq(map[string]string{
"namespaceId": "",
"serviceName": "DEFAULT_GROUP@@DEMO",
"groupName": "DEFAULT_GROUP",
"app": "",
"clusterName": "",
"ip": "10.0.0.10",
"port": "80",
"weight": "0",
"enable": "false",
"healthy": "false",
"metadata": "{}",
"ephemeral": "false",
})).Times(1).
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
type MockNamingProxy struct {
}
func (m *MockNamingProxy) RegisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
return true, nil
}
func (m *MockNamingProxy) DeregisterInstance(serviceName string, groupName string, instance model.Instance) (bool, error) {
return true, nil
}
func (m *MockNamingProxy) GetServiceList(pageNo uint32, pageSize uint32, groupName string, selector *model.ExpressionSelector) (model.ServiceList, error) {
return model.ServiceList{Doms: []string{""}}, nil
}
func (m *MockNamingProxy) ServerHealthy() bool {
return true
}
func (m *MockNamingProxy) QueryInstancesOfService(serviceName, groupName, clusters string, udpPort int, healthyOnly bool) (*model.Service, error) {
return &model.Service{}, nil
}
func (m *MockNamingProxy) Subscribe(serviceName, groupName, clusters string) (model.Service, error) {
return model.Service{}, nil
}
func (m *MockNamingProxy) Unsubscribe(serviceName, groupName, clusters string) {}
func NewTestNamingClient() *NamingClient {
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewNamingClient(&nc)
success, err := client.RegisterInstance(vo.RegisterInstanceParam{
client.serviceProxy = &MockNamingProxy{}
return client
}
func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) {
success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
ServiceName: "DEMO",
Ip: "10.0.0.10",
Port: 80,
@ -90,39 +95,8 @@ func Test_RegisterServiceInstance_withoutGroupName(t *testing.T) {
}
func Test_RegisterServiceInstance_withGroupName(t *testing.T) {
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
gomock.AssignableToTypeOf(http.Header{}),
gomock.Eq(uint64(10*1000)),
gomock.Eq(map[string]string{
"namespaceId": "",
"serviceName": "test_group@@DEMO2",
"groupName": "test_group",
"app": "",
"clusterName": "",
"ip": "10.0.0.10",
"port": "80",
"weight": "0",
"enable": "false",
"healthy": "false",
"metadata": "{}",
"ephemeral": "false",
})).Times(1).
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
success, err := client.RegisterInstance(vo.RegisterInstanceParam{
ServiceName: "DEMO2",
success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
ServiceName: "DEMO",
Ip: "10.0.0.10",
Port: 80,
GroupName: "test_group",
@ -133,39 +107,8 @@ func Test_RegisterServiceInstance_withGroupName(t *testing.T) {
}
func Test_RegisterServiceInstance_withCluster(t *testing.T) {
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
gomock.AssignableToTypeOf(http.Header{}),
gomock.Eq(uint64(10*1000)),
gomock.Eq(map[string]string{
"namespaceId": "",
"serviceName": "test_group@@DEMO3",
"groupName": "test_group",
"app": "",
"clusterName": "test",
"ip": "10.0.0.10",
"port": "80",
"weight": "0",
"enable": "false",
"healthy": "false",
"metadata": "{}",
"ephemeral": "false",
})).Times(1).
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
success, err := client.RegisterInstance(vo.RegisterInstanceParam{
ServiceName: "DEMO3",
success, err := NewTestNamingClient().RegisterInstance(vo.RegisterInstanceParam{
ServiceName: "DEMO",
Ip: "10.0.0.10",
Port: 80,
GroupName: "test_group",
@ -175,149 +118,27 @@ func Test_RegisterServiceInstance_withCluster(t *testing.T) {
assert.Equal(t, nil, err)
assert.Equal(t, true, success)
}
func Test_RegisterServiceInstance_401(t *testing.T) {
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
mockIHttpAgent.EXPECT().Request(gomock.Eq("POST"),
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
gomock.AssignableToTypeOf(http.Header{}),
gomock.Eq(uint64(10*1000)),
gomock.Eq(map[string]string{
"namespaceId": "",
"serviceName": "test_group@@DEMO4",
"groupName": "test_group",
"app": "",
"clusterName": "",
"ip": "10.0.0.10",
"port": "80",
"weight": "0",
"enable": "false",
"healthy": "false",
"metadata": "{}",
"ephemeral": "false",
})).Times(3).
Return(http_agent.FakeHttpResponse(401, `no security`), nil)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
result, err := client.RegisterInstance(vo.RegisterInstanceParam{
ServiceName: "DEMO4",
Ip: "10.0.0.10",
Port: 80,
GroupName: "test_group",
Ephemeral: false,
})
assert.Equal(t, false, result)
assert.NotNil(t, err)
}
func TestNamingProxy_DeregisterService_WithoutGroupName(t *testing.T) {
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
mockIHttpAgent.EXPECT().Request(gomock.Eq("DELETE"),
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
gomock.AssignableToTypeOf(http.Header{}),
gomock.Eq(uint64(10*1000)),
gomock.Eq(map[string]string{
"namespaceId": "",
"serviceName": "DEFAULT_GROUP@@DEMO5",
"clusterName": "",
"ip": "10.0.0.10",
"port": "80",
"ephemeral": "true",
})).Times(1).
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
client.DeregisterInstance(vo.DeregisterInstanceParam{
success, err := NewTestNamingClient().DeregisterInstance(vo.DeregisterInstanceParam{
ServiceName: "DEMO5",
Ip: "10.0.0.10",
Port: 80,
Ephemeral: true,
})
assert.Equal(t, nil, err)
assert.Equal(t, true, success)
}
func TestNamingProxy_DeregisterService_WithGroupName(t *testing.T) {
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
mockIHttpAgent.EXPECT().Request(gomock.Eq("DELETE"),
gomock.Eq("http://console.nacos.io:80/nacos/v1/ns/instance"),
gomock.AssignableToTypeOf(http.Header{}),
gomock.Eq(uint64(10*1000)),
gomock.Eq(map[string]string{
"namespaceId": "",
"serviceName": "test_group@@DEMO6",
"clusterName": "",
"ip": "10.0.0.10",
"port": "80",
"ephemeral": "true",
})).Times(1).
Return(http_agent.FakeHttpResponse(200, `ok`), nil)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
client.DeregisterInstance(vo.DeregisterInstanceParam{
success, err := NewTestNamingClient().DeregisterInstance(vo.DeregisterInstanceParam{
ServiceName: "DEMO6",
Ip: "10.0.0.10",
Port: 80,
GroupName: "test_group",
Ephemeral: true,
})
}
func 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,
})
assert.Equal(t, nil, err)
assert.Equal(t, true, success)
}
var serviceJsonTest = `{
@ -355,11 +176,9 @@ var serviceJsonTest = `{
}`
var serviceTest = model.Service(model.Service{Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000, UseSpecifiedURL: false,
CacheMillis: 1000,
Hosts: []model.Instance{
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-8888-a-DEMO",
Port: 0x22b8,
Ip: "10.10.10.10",
@ -370,8 +189,6 @@ var serviceTest = model.Service(model.Service{Name: "DEFAULT_GROUP@@DEMO",
Enable: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-8888-a-DEMO",
Port: 0x22b8,
Ip: "10.10.10.11",
@ -383,74 +200,14 @@ var serviceTest = model.Service(model.Service{Name: "DEFAULT_GROUP@@DEMO",
},
},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", 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))
//}
LastRefTime: 1528787794594, Clusters: "a"})
func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
services := model.Service(model.Service{
services := model.Service{
Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80,
Ip: "10.10.10.10",
@ -462,8 +219,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80,
Ip: "10.10.10.11",
@ -475,8 +230,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80,
Ip: "10.10.10.12",
@ -488,8 +241,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: false,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80,
Ip: "10.10.10.13",
@ -501,8 +252,6 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80,
Ip: "10.10.10.14",
@ -515,65 +264,33 @@ func TestNamingClient_SelectOneHealthyInstance_SameWeight(t *testing.T) {
},
},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
instance1, err := client.selectOneHealthyInstances(services)
fmt.Println(util.ToJsonString(instance1))
LastRefTime: 1528787794594, Clusters: "a"}
instance1, err := NewTestNamingClient().selectOneHealthyInstances(services)
assert.Nil(t, err)
assert.NotNil(t, instance1)
instance2, err := client.selectOneHealthyInstances(services)
fmt.Println(util.ToJsonString(instance2))
instance2, err := NewTestNamingClient().selectOneHealthyInstances(services)
assert.Nil(t, err)
assert.NotNil(t, instance2)
//assert.NotEqual(t, instance1, instance2)
}
func TestNamingClient_SelectOneHealthyInstance_Empty(t *testing.T) {
services := model.Service(model.Service{
services := model.Service{
Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
instance, err := client.selectOneHealthyInstances(services)
fmt.Println(util.ToJsonString(instance))
LastRefTime: 1528787794594, Clusters: "a"}
instance, err := NewTestNamingClient().selectOneHealthyInstances(services)
assert.NotNil(t, err)
assert.Nil(t, instance)
}
func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
services := model.Service(model.Service{
services := model.Service{
Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80,
Ip: "10.10.10.10",
@ -585,8 +302,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80,
Ip: "10.10.10.11",
@ -598,8 +313,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80,
Ip: "10.10.10.12",
@ -611,8 +324,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: false,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80,
Ip: "10.10.10.13",
@ -624,8 +335,6 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80,
Ip: "10.10.10.14",
@ -638,34 +347,18 @@ func TestNamingClient_SelectInstances_Healthy(t *testing.T) {
},
},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
instances, err := client.selectInstances(services, true)
fmt.Println(util.ToJsonString(instances))
LastRefTime: 1528787794594, Clusters: "a"}
instances, err := NewTestNamingClient().selectInstances(services, true)
assert.Nil(t, err)
assert.Equal(t, 2, len(instances))
}
func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
services := model.Service(model.Service{
services := model.Service{
Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80,
Ip: "10.10.10.10",
@ -677,8 +370,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80,
Ip: "10.10.10.11",
@ -690,8 +381,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80,
Ip: "10.10.10.12",
@ -703,8 +392,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: false,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80,
Ip: "10.10.10.13",
@ -716,8 +403,6 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80,
Ip: "10.10.10.14",
@ -730,103 +415,41 @@ func TestNamingClient_SelectInstances_Unhealthy(t *testing.T) {
},
},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
instances, err := client.selectInstances(services, false)
fmt.Println(util.ToJsonString(instances))
LastRefTime: 1528787794594, Clusters: "a"}
instances, err := NewTestNamingClient().selectInstances(services, false)
assert.Nil(t, err)
assert.Equal(t, 1, len(instances))
}
func TestNamingClient_SelectInstances_Empty(t *testing.T) {
services := model.Service(model.Service{
services := model.Service{
Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
ctrl := gomock.NewController(t)
defer func() {
ctrl.Finish()
}()
mockIHttpAgent := mock.NewMockIHttpAgent(ctrl)
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(mockIHttpAgent)
client, _ := NewNamingClient(&nc)
instances, err := client.selectInstances(services, false)
fmt.Println(util.ToJsonString(instances))
LastRefTime: 1528787794594, Clusters: "a"}
instances, err := NewTestNamingClient().selectInstances(services, false)
assert.NotNil(t, err)
assert.Equal(t, 0, len(instances))
}
func TestNamingClient_GetAllServicesInfo(t *testing.T) {
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
nc.SetHttpAgent(&http_agent.HttpAgent{})
client, _ := NewNamingClient(&nc)
reslut, err := client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
result, err := NewTestNamingClient().GetAllServicesInfo(vo.GetAllServiceInfoParam{
GroupName: "DEFAULT_GROUP",
PageNo: 1,
PageSize: 20,
})
assert.NotNil(t, reslut.Doms)
assert.NotNil(t, result.Doms)
assert.Nil(t, err)
}
func TestNamingClient_selectOneHealthyInstanceResult(t *testing.T) {
services := model.Service(model.Service{
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) {
services := model.Service(model.Service{
services := model.Service{
Name: "DEFAULT_GROUP@@DEMO",
CacheMillis: 1000,
UseSpecifiedURL: false,
Hosts: []model.Instance{
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.10-80-a-DEMO",
Port: 80,
Ip: "10.10.10.10",
@ -838,8 +461,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.11-80-a-DEMO",
Port: 80,
Ip: "10.10.10.11",
@ -851,8 +472,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.12-80-a-DEMO",
Port: 80,
Ip: "10.10.10.12",
@ -864,8 +483,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: false,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.13-80-a-DEMO",
Port: 80,
Ip: "10.10.10.13",
@ -877,8 +494,6 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
Healthy: true,
},
{
Valid: true,
Marked: false,
InstanceId: "10.10.10.14-80-a-DEMO",
Port: 80,
Ip: "10.10.10.14",
@ -891,178 +506,11 @@ func BenchmarkNamingClient_SelectOneHealthyInstances(b *testing.B) {
},
},
Checksum: "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
LastRefTime: 1528787794594, Env: "", Clusters: "a",
Metadata: map[string]string(nil)})
nc := nacos_client.NacosClient{}
nc.SetServerConfig([]constant.ServerConfig{serverConfigTest})
nc.SetClientConfig(clientConfigTest)
client, _ := NewNamingClient(&nc)
LastRefTime: 1528787794594, Clusters: "a"}
client := NewTestNamingClient()
b.ResetTimer()
for i := 0; i < b.N; i++ {
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.
*/
package naming_client
package naming_http
import (
"errors"
"fmt"
"net/http"
"strconv"
"sync/atomic"
"time"
"github.com/nacos-group/nacos-sdk-go/clients/cache"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/buger/jsonparser"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
nsema "github.com/toolkits/concurrent/semaphore"
)
type BeatReactor struct {
beatMap cache.ConcurrentMap
serviceProxy NamingProxy
clientBeatInterval int64
nacosServer *nacos_server.NacosServer
beatThreadCount int
beatThreadSemaphore *nsema.Semaphore
beatRecordMap cache.ConcurrentMap
clientCfg constant.ClientConfig
}
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{}
if clientBeatInterval <= 0 {
clientBeatInterval = 5 * 1000
}
br.beatMap = cache.NewConcurrentMap()
br.serviceProxy = serviceProxy
br.clientBeatInterval = clientBeatInterval
br.nacosServer = nacosServer
br.clientCfg = clientCfg
br.beatThreadCount = Default_Beat_Thread_Num
br.beatRecordMap = cache.NewConcurrentMap()
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 {
logger.Errorf("beat to server return error:%+v", err)
br.beatThreadSemaphore.Release()
@ -106,3 +108,26 @@ func (br *BeatReactor) sendInstanceBeat(k string, beatInfo *model.BeatInfo) {
<-t.C
}
}
func (br *BeatReactor) SendBeat(info model.BeatInfo) (int64, error) {
logger.Infof("namespaceId:<%s> sending beat to server:<%s>",
br.clientCfg.NamespaceId, util.ToJsonString(info))
params := map[string]string{}
params["namespaceId"] = br.clientCfg.NamespaceId
params["serviceName"] = info.ServiceName
params["beat"] = util.ToJsonString(info)
api := constant.SERVICE_BASE_PATH + "/instance/beat"
result, err := br.nacosServer.ReqApi(api, params, http.MethodPut)
if err != nil {
return 0, err
}
if result != "" {
interVal, err := jsonparser.GetInt([]byte(result), "clientBeatInterval")
if err != nil {
return 0, errors.New(fmt.Sprintf("namespaceId:<%s> sending beat to server:<%s> get 'clientBeatInterval' from <%s> error:<%+v>", br.clientCfg.NamespaceId, util.ToJsonString(info), result, err))
} else {
return interVal, nil
}
}
return 0, nil
}

View File

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

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

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 (
"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 {

View File

@ -20,7 +20,7 @@ import (
"os"
"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"
)

View File

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

View File

@ -16,6 +16,8 @@
package constant
import "time"
const (
KEY_USERNAME = "username"
KEY_PASSWORD = "password"
@ -66,7 +68,7 @@ const (
KEY_BEAT = "beat"
KEY_DOM = "dom"
DEFAULT_CONTEXT_PATH = "/nacos"
CLIENT_VERSION = "Nacos-Go-Client:v1.0.1"
CLIENT_VERSION = "Nacos-Go-Client:v2.0.0"
REQUEST_DOMAIN_RETRY_TIME = 3
SERVICE_INFO_SPLITER = "@@"
CONFIG_INFO_SPLITER = "@@"
@ -75,4 +77,19 @@ const (
NAMING_INSTANCE_ID_SPLITTER = "#"
DefaultClientErrorCode = "SDK.NacosError"
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"
"net/http"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/go-errors/errors"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/util"
)
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)
return ""
}
if response.StatusCode != 200 {
if response.StatusCode != constant.RESPONSE_CODE_SUCCESS {
logger.Errorf("request method[%s],request path[%s],header:[%s],params:[%s],status code error:%d", method, path, util.ToJsonString(header), util.ToJsonString(params), response.StatusCode)
return ""
}

View File

@ -20,7 +20,7 @@ import (
"strings"
"time"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/v2/util"
)
func post(path string, header http.Header, timeoutMs uint64, params map[string]string) (response *http.Response, err error) {

View File

@ -24,7 +24,7 @@ import (
"time"
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/zapcore"
)

View File

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

View File

@ -29,15 +29,16 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/nacos-group/nacos-sdk-go/common/nacos_error"
"github.com/nacos-group/nacos-sdk-go/common/security"
"github.com/nacos-group/nacos-sdk-go/inner/uuid"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/common/http_agent"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error"
"github.com/nacos-group/nacos-sdk-go/v2/common/security"
"github.com/nacos-group/nacos-sdk-go/v2/inner/uuid"
"github.com/nacos-group/nacos-sdk-go/v2/util"
)
type NacosServer struct {
@ -50,6 +51,7 @@ type NacosServer struct {
lastSrvRefTime int64
vipSrvRefInterMills int64
contextPath string
currentIndex int32
}
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,
vipSrvRefInterMills: 10000,
contextPath: clientCfg.ContextPath,
currentIndex: rand.Int31n((int32)(len(serverList))),
}
ns.initRefreshSrvIfNeed()
_, err := securityLogin.Login()
@ -85,7 +88,7 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
contextPath = constant.WEB_CONTEXT
}
signHeaders := getSignHeaders(params, newHeaders)
signHeaders := GetSignHeaders(params, newHeaders["secretKey"])
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["Timestamp"] = []string{signHeaders["timeStamp"]}
headers["Spas-Signature"] = []string{signHeaders["Spas-Signature"]}
injectSecurityInfo(server, params)
server.InjectSecurityInfo(params)
var response *http.Response
response, err = server.httpAgent.Request(method, url, headers, timeoutMS, params)
@ -124,7 +127,7 @@ func (server *NacosServer) callConfigServer(api string, params map[string]string
return
}
result = string(bytes)
if response.StatusCode == 200 {
if response.StatusCode == constant.RESPONSE_CODE_SUCCESS {
return
} else {
err = nacos_error.NewNacosError(strconv.Itoa(response.StatusCode), string(bytes), nil)
@ -152,7 +155,7 @@ func (server *NacosServer) callServer(api string, params map[string]string, meth
headers["Request-Module"] = []string{"Naming"}
headers["Content-Type"] = []string{"application/x-www-form-urlencoded;charset=utf-8"}
injectSecurityInfo(server, params)
server.InjectSecurityInfo(params)
var response *http.Response
response, err = server.httpAgent.Request(method, url, headers, server.timeoutMs, params)
@ -166,7 +169,7 @@ func (server *NacosServer) callServer(api string, params map[string]string, meth
return
}
result = string(bytes)
if response.StatusCode == 200 {
if response.StatusCode == constant.RESPONSE_CODE_SUCCESS {
return
} else {
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")
}
injectSecurityInfo(server, params)
server.InjectSecurityInfo(params)
//only one server,retry request when 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")
}
injectSecurityInfo(server, params)
server.InjectSecurityInfo(params)
//only one server,retry request when error
if len(srvs) == 1 {
@ -305,7 +308,7 @@ func (server *NacosServer) GetServerList() []constant.ServerConfig {
return server.serverList
}
func injectSecurityInfo(server *NacosServer, param map[string]string) {
func (server *NacosServer) InjectSecurityInfo(param map[string]string) {
accessToken := server.securityLogin.GetAccessToken()
if accessToken != "" {
param[constant.KEY_ACCESS_TOKEN] = accessToken
@ -319,7 +322,7 @@ func getAddress(cfg constant.ServerConfig) string {
return cfg.Scheme + "://" + cfg.IpAddr + ":" + strconv.Itoa(int(cfg.Port))
}
func getSignHeaders(params map[string]string, newHeaders map[string]string) map[string]string {
func GetSignHeaders(params map[string]string, secretKey string) map[string]string {
resource := ""
if len(params["tenant"]) != 0 {
@ -330,15 +333,15 @@ func getSignHeaders(params map[string]string, newHeaders map[string]string) map[
headers := map[string]string{}
timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
timeStamp := strconv.FormatInt(util.CurrentMillis(), 10)
headers["timeStamp"] = timeStamp
signature := ""
if resource == "" {
signature = signWithhmacSHA1Encrypt(timeStamp, newHeaders["secretKey"])
signature = signWithhmacSHA1Encrypt(timeStamp, secretKey)
} else {
signature = signWithhmacSHA1Encrypt(resource+"+"+timeStamp, newHeaders["secretKey"])
signature = signWithhmacSHA1Encrypt(resource+"+"+timeStamp, secretKey)
}
headers["Spas-Signature"] = signature
@ -354,3 +357,8 @@ func signWithhmacSHA1Encrypt(encryptText, encryptKey string) string {
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 (
"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"
)

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

View File

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

View File

@ -20,38 +20,22 @@ import (
"fmt"
"time"
"github.com/nacos-group/nacos-sdk-go/clients"
"github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/model"
"github.com/nacos-group/nacos-sdk-go/util"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/model"
"github.com/nacos-group/nacos-sdk-go/v2/util"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
func main() {
//create ServerConfig
sc := []constant.ServerConfig{
{
IpAddr: "console.nacos.io",
Port: 80,
},
}
//or a more graceful way to create ServerConfig
_ = []constant.ServerConfig{
*constant.NewServerConfig("console.nacos.io", 80),
*constant.NewServerConfig("127.0.0.1", 8848, constant.WithContextPath("/nacos")),
}
cc := constant.ClientConfig{
NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", //namespace id
TimeoutMs: 5000,
NotLoadCacheAtStart: true,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
RotateTime: "1h",
MaxAge: 3,
LogLevel: "debug",
}
//or a more graceful way to create ClientConfig
_ = *constant.NewClientConfig(
constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"),
//create ClientConfig
cc := *constant.NewClientConfig(
constant.WithNamespaceId(""),
constant.WithTimeoutMs(5000),
constant.WithNotLoadCacheAtStart(true),
constant.WithLogDir("/tmp/nacos/log"),
@ -61,7 +45,7 @@ func main() {
constant.WithLogLevel("debug"),
)
// a more graceful way to create naming client
// create naming client
client, err := clients.NewNamingClient(
vo.NacosClientParam{
ClientConfig: &cc,
@ -79,6 +63,8 @@ func main() {
Ip: "10.0.0.10",
Port: 8848,
ServiceName: "demo.go",
GroupName: "group-a",
ClusterName: "cluster-a",
Weight: 10,
Enable: true,
Healthy: true,
@ -86,56 +72,6 @@ func main() {
Metadata: map[string]string{"idc": "shanghai"},
})
//Register with cluster name
//GroupName=DEFAULT_GROUP
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.11",
Port: 8848,
ServiceName: "demo.go",
Weight: 10,
ClusterName: "cluster-a",
Enable: true,
Healthy: true,
Ephemeral: true,
})
//Register different cluster
//GroupName=DEFAULT_GROUP
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.12",
Port: 8848,
ServiceName: "demo.go",
Weight: 10,
ClusterName: "cluster-b",
Enable: true,
Healthy: true,
Ephemeral: true,
})
//Register different group
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.13",
Port: 8848,
ServiceName: "demo.go",
Weight: 10,
ClusterName: "cluster-b",
GroupName: "group-a",
Enable: true,
Healthy: true,
Ephemeral: true,
})
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.14",
Port: 8848,
ServiceName: "demo.go",
Weight: 10,
ClusterName: "cluster-b",
GroupName: "group-b",
Enable: true,
Healthy: true,
Ephemeral: true,
})
//DeRegister with ip,port,serviceName
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP
//Note:ip=10.0.0.10,port=8848 should belong to the cluster of DEFAULT and the group of DEFAULT_GROUP.
@ -143,72 +79,50 @@ func main() {
Ip: "10.0.0.10",
Port: 8848,
ServiceName: "demo.go",
Ephemeral: true, //it must be true
})
//DeRegister with ip,port,serviceName,cluster
//GroupName=DEFAULT_GROUP
//Note:ip=10.0.0.10,port=8848,cluster=cluster-a should belong to the group of DEFAULT_GROUP.
ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{
Ip: "10.0.0.11",
Port: 8848,
ServiceName: "demo.go",
GroupName: "group-a",
Cluster: "cluster-a",
Ephemeral: true, //it must be true
})
//DeRegister with ip,port,serviceName,cluster,group
ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{
Ip: "10.0.0.14",
//Register with default cluster and group
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.10",
Port: 8848,
ServiceName: "demo.go",
Cluster: "cluster-b",
GroupName: "group-b",
Ephemeral: true, //it must be true
GroupName: "group-a",
ClusterName: "cluster-a",
Weight: 10,
Enable: true,
Healthy: true,
Ephemeral: true,
Metadata: map[string]string{"idc": "shanghai"},
})
time.Sleep(1 * time.Second)
//Get service with serviceName
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP
ExampleServiceClient_GetService(client, vo.GetServiceParam{
ServiceName: "demo.go",
})
//Get service with serviceName and cluster
//GroupName=DEFAULT_GROUP
ExampleServiceClient_GetService(client, vo.GetServiceParam{
ServiceName: "demo.go",
Clusters: []string{"cluster-a", "cluster-b"},
})
//Get service with serviceName ,group
//ClusterName=DEFAULT
ExampleServiceClient_GetService(client, vo.GetServiceParam{
ServiceName: "demo.go",
GroupName: "group-a",
})
//SelectAllInstance return all instances,include healthy=false,enable=false,weight<=0
//ClusterName=DEFAULT, GroupName=DEFAULT_GROUP
ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{
ServiceName: "demo.go",
Clusters: []string{"cluster-a"},
})
//SelectAllInstance
//GroupName=DEFAULT_GROUP
ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{
ServiceName: "demo.go",
Clusters: []string{"cluster-a", "cluster-b"},
})
//SelectAllInstance
//ClusterName=DEFAULT
ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{
ServiceName: "demo.go",
GroupName: "group-a",
Clusters: []string{"cluster-a"},
})
//SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
ExampleServiceClient_SelectInstances(client, vo.SelectInstancesParam{
ServiceName: "demo.go",
GroupName: "group-a",
Clusters: []string{"cluster-a"},
})
//SelectOneHealthyInstance return one instance by WRR strategy for load balance
@ -216,38 +130,36 @@ func main() {
//ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
ExampleServiceClient_SelectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{
ServiceName: "demo.go",
GroupName: "group-a",
Clusters: []string{"cluster-a"},
})
//Subscribe key=serviceName+groupName+cluster
//Note:We call add multiple SubscribeCallback with the same key.
param := &vo.SubscribeParam{
ServiceName: "demo.go",
Clusters: []string{"cluster-b"},
SubscribeCallback: func(services []model.SubscribeService, err error) {
GroupName: "group-a",
Clusters: []string{"cluster-a"},
SubscribeCallback: func(services []model.Instance, err error) {
fmt.Printf("callback111 return services:%s \n\n", util.ToJsonString(services))
},
}
ExampleServiceClient_Subscribe(client, param)
param2 := &vo.SubscribeParam{
ServiceName: "demo.go",
Clusters: []string{"cluster-b"},
SubscribeCallback: func(services []model.SubscribeService, err error) {
fmt.Printf("callback222 return services:%s \n\n", util.ToJsonString(services))
},
}
ExampleServiceClient_Subscribe(client, param2)
ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{
Ip: "10.0.0.112",
Ip: "10.0.0.10",
Port: 8848,
ServiceName: "demo.go",
GroupName: "group-a",
ClusterName: "cluster-a",
Weight: 10,
ClusterName: "cluster-b",
Enable: true,
Healthy: true,
Ephemeral: true,
Metadata: map[string]string{"idc": "beijing"},
})
//wait for client pull change from server
time.Sleep(10 * time.Second)
time.Sleep(3 * time.Second)
//Now we just unsubscribe callback1, and callback2 will still receive change event
ExampleServiceClient_UnSubscribe(client, param)
@ -259,7 +171,7 @@ func main() {
Cluster: "cluster-b",
})
//wait for client pull change from server
time.Sleep(10 * time.Second)
time.Sleep(3 * time.Second)
//GeAllService will get the list of service name
//NameSpace default value is public.If the client set the namespaceId, NameSpace will use it.
@ -268,10 +180,4 @@ func main() {
PageNo: 1,
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 (
"fmt"
"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
func ExampleServiceClient_RegisterServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) {
@ -50,7 +51,7 @@ func ExampleServiceClient_SelectInstances(client naming_client.INamingClient, pa
func ExampleServiceClient_SelectOneHealthyInstance(client naming_client.INamingClient, param vo.SelectOneHealthInstanceParam) {
instances, _ := client.SelectOneHealthyInstance(param)
fmt.Printf("SelectInstances,param:%+v, result:%+v \n\n", param, instances)
fmt.Printf("SelectOneHealthyInstance,param:%+v, result:%+v \n\n", param, instances)
}
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 (
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/go-errors/errors v1.0.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/jonboulle/clockwork v0.1.0 // 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/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3
go.uber.org/zap v1.15.0
google.golang.org/grpc v1.36.1
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/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
github.com/buger/jsonparser 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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
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/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/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/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/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_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/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=
@ -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=
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/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/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
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/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/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/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-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
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/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -31,3 +31,16 @@ type ConfigPage struct {
PagesAvailable int `param:"pagesAvailable"`
PageItems []ConfigItem `param:"pageItems"`
}
type ConfigListenContext struct {
Group string `json:"group"`
Md5 string `json:"md5"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
}
type ConfigContext struct {
Group string `json:"group"`
DataId string `json:"dataId"`
Tenant string `json:"tenant"`
}

View File

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

View File

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

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"
"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) {

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
import "github.com/nacos-group/nacos-sdk-go/common/constant"
import "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
type NacosClientParam struct {
ClientConfig *constant.ClientConfig // optional

View File

@ -22,7 +22,12 @@ type ConfigParam struct {
DataId string `param:"dataId"` //required
Group string `param:"group"` //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)
}

View File

@ -16,7 +16,7 @@
package vo
import "github.com/nacos-group/nacos-sdk-go/model"
import "github.com/nacos-group/nacos-sdk-go/v2/model"
type RegisterInstanceParam struct {
Ip string `param:"ip"` //required
@ -57,7 +57,7 @@ type SubscribeParam struct {
ServiceName string `param:"serviceName"` //required
Clusters []string `param:"clusters"` //optional,default:DEFAULT
GroupName string `param:"groupName"` //optional,default:DEFAULT_GROUP
SubscribeCallback func(services []model.SubscribeService, err error) //required
SubscribeCallback func(services []model.Instance, err error) //required
}
type SelectAllInstancesParam struct {