Skip to content

Commit d51b06d

Browse files
authored
Add Nacos data source extension (alibaba#184)
1 parent a149984 commit d51b06d

File tree

5 files changed

+377
-15
lines changed

5 files changed

+377
-15
lines changed

ext/datasource/nacos/nacos.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package nacos
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/alibaba/sentinel-golang/ext/datasource"
7+
"github.com/alibaba/sentinel-golang/logging"
8+
"github.com/alibaba/sentinel-golang/util"
9+
"github.com/nacos-group/nacos-sdk-go/clients/config_client"
10+
"github.com/nacos-group/nacos-sdk-go/vo"
11+
"github.com/pkg/errors"
12+
)
13+
14+
var (
15+
logger = logging.GetDefaultLogger()
16+
)
17+
18+
type NacosDataSource struct {
19+
datasource.Base
20+
client config_client.IConfigClient
21+
isInitialized util.AtomicBool
22+
group string
23+
dataId string
24+
closeChan chan struct{}
25+
}
26+
27+
func NewNacosDataSource(client config_client.IConfigClient, group, dataId string, handlers ...datasource.PropertyHandler) (*NacosDataSource, error) {
28+
if client == nil {
29+
return nil, errors.New("Nil nacos config client")
30+
}
31+
if len(group) == 0 || len(dataId) == 0 {
32+
return nil, errors.New(fmt.Sprintf("Invalid parameters, group: %s, dataId: %s", group, dataId))
33+
}
34+
var ds = &NacosDataSource{
35+
Base: datasource.Base{},
36+
client: client,
37+
group: group,
38+
dataId: dataId,
39+
closeChan: make(chan struct{}, 1),
40+
}
41+
for _, h := range handlers {
42+
ds.AddPropertyHandler(h)
43+
}
44+
return ds, nil
45+
}
46+
47+
func (s *NacosDataSource) Initialize() error {
48+
if !s.isInitialized.CompareAndSet(false, true) {
49+
return nil
50+
}
51+
data, err := s.ReadSource()
52+
if err != nil {
53+
return err
54+
}
55+
if err = s.doUpdate(data); err != nil {
56+
return err
57+
}
58+
err = s.listen(s.client)
59+
if err == nil {
60+
logger.Infof("Nacos data source is successfully initialized, group: %s, dataId: %s", s.group, s.dataId)
61+
}
62+
return err
63+
}
64+
65+
func (s *NacosDataSource) ReadSource() ([]byte, error) {
66+
content, err := s.client.GetConfig(vo.ConfigParam{
67+
DataId: s.group,
68+
Group: s.dataId,
69+
})
70+
if err != nil {
71+
return nil, errors.Errorf("Failed to read the nacos data source when initialization, err: %+v", err)
72+
}
73+
74+
logger.Infof("Succeed to read source for group: %s, dataId: %s, data: %s", s.group, s.dataId, content)
75+
return []byte(content), err
76+
}
77+
78+
func (s *NacosDataSource) doUpdate(data []byte) error {
79+
return s.Handle(data)
80+
}
81+
82+
func (s *NacosDataSource) listen(client config_client.IConfigClient) (err error) {
83+
listener := vo.ConfigParam{
84+
DataId: s.dataId,
85+
Group: s.group,
86+
OnChange: func(namespace, group, dataId, data string) {
87+
logger.Infof("Receive listened property. namespace: %s, group: %s, dataId: %s, data: %s", namespace, group, dataId, data)
88+
err := s.doUpdate([]byte(data))
89+
if err != nil {
90+
logger.Errorf("Fail to update data source, err: %+v", err)
91+
}
92+
},
93+
ListenCloseChan: s.closeChan,
94+
}
95+
err = client.ListenConfig(listener)
96+
if err != nil {
97+
return errors.Errorf("Failed to listen to the nacos data source, err: %+v", err)
98+
}
99+
return nil
100+
}
101+
102+
func (s *NacosDataSource) Close() error {
103+
s.closeChan <- struct{}{}
104+
logger.Infof("The nacos datasource had been closed, group: %s, dataId: %s", s.group, s.dataId)
105+
return nil
106+
}

ext/datasource/nacos/nacos_example.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package nacos
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/alibaba/sentinel-golang/ext/datasource"
8+
"github.com/nacos-group/nacos-sdk-go/clients/config_client"
9+
"github.com/nacos-group/nacos-sdk-go/clients/nacos_client"
10+
"github.com/nacos-group/nacos-sdk-go/common/constant"
11+
"github.com/nacos-group/nacos-sdk-go/common/http_agent"
12+
"github.com/stretchr/testify/mock"
13+
)
14+
15+
var serverConfig = constant.ServerConfig{
16+
ContextPath: "/nacos",
17+
Port: 8848,
18+
IpAddr: "127.0.0.1",
19+
}
20+
21+
var clientConfigTest = constant.ClientConfig{
22+
BeatInterval: 10000,
23+
TimeoutMs: 10000,
24+
ListenInterval: 20000,
25+
}
26+
27+
func createConfigClientTest() (*config_client.ConfigClient, error) {
28+
nc := nacos_client.NacosClient{}
29+
err := nc.SetServerConfig([]constant.ServerConfig{serverConfig})
30+
err = nc.SetClientConfig(clientConfigTest)
31+
err = nc.SetHttpAgent(&http_agent.HttpAgent{})
32+
client, err := config_client.NewConfigClient(&nc)
33+
34+
return &client, err
35+
}
36+
37+
func Example_NacosDatasource_CustomizeClient() {
38+
client, err := createConfigClientTest()
39+
if err != nil {
40+
fmt.Printf("Fail to create client, err: %+v", err)
41+
return
42+
}
43+
h := &datasource.MockPropertyHandler{}
44+
h.On("isPropertyConsistent", mock.Anything).Return(true)
45+
h.On("Handle", mock.Anything).Return(nil)
46+
nds, err := NewNacosDataSource(client, "sentinel-go", "system-rules", h)
47+
if err != nil {
48+
fmt.Printf("Fail to create nacos data source client, err: %+v", err)
49+
return
50+
}
51+
err = nds.Initialize()
52+
if err != nil {
53+
fmt.Printf("Fail to initialize nacos data source client, err: %+v", err)
54+
return
55+
}
56+
57+
time.Sleep(time.Second * 10)
58+
nds.Close()
59+
fmt.Println("Nacos datasource is Closed")
60+
}

ext/datasource/nacos/nacos_test.go

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package nacos
2+
3+
import (
4+
"testing"
5+
6+
"github.com/nacos-group/nacos-sdk-go/clients/config_client"
7+
8+
"github.com/alibaba/sentinel-golang/ext/datasource"
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/nacos-group/nacos-sdk-go/model"
12+
"github.com/nacos-group/nacos-sdk-go/vo"
13+
"github.com/stretchr/testify/mock"
14+
)
15+
16+
const (
17+
TestSystemRules = `[
18+
{
19+
"id": 0,
20+
"metricType": 0,
21+
"adaptiveStrategy": 0
22+
},
23+
{
24+
"id": 1,
25+
"metricType": 0,
26+
"adaptiveStrategy": 0
27+
},
28+
{
29+
"id": 2,
30+
"metricType": 0,
31+
"adaptiveStrategy": 0
32+
}
33+
]`
34+
)
35+
36+
var (
37+
Group = "sentinel-go"
38+
DataId = "system-rules"
39+
)
40+
41+
type nacosClientMock struct {
42+
mock.Mock
43+
}
44+
45+
func (n *nacosClientMock) GetConfig(param vo.ConfigParam) (string, error) {
46+
ret := n.Called(param)
47+
return ret.String(0), ret.Error(1)
48+
}
49+
50+
func (n *nacosClientMock) PublishConfig(param vo.ConfigParam) (bool, error) {
51+
ret := n.Called(param)
52+
return ret.Bool(0), ret.Error(1)
53+
}
54+
55+
func (n *nacosClientMock) DeleteConfig(param vo.ConfigParam) (bool, error) {
56+
ret := n.Called(param)
57+
return ret.Bool(0), ret.Error(1)
58+
}
59+
60+
func (n *nacosClientMock) ListenConfig(params vo.ConfigParam) (err error) {
61+
ret := n.Called(params)
62+
return ret.Error(0)
63+
}
64+
func (n *nacosClientMock) SearchConfig(param vo.SearchConfigParm) (*model.ConfigPage, error) {
65+
ret := n.Called(param)
66+
return ret.Get(0).(*model.ConfigPage), ret.Error(1)
67+
}
68+
69+
func getNacosDataSource(client config_client.IConfigClient) (*NacosDataSource, error) {
70+
mh1 := &datasource.MockPropertyHandler{}
71+
mh1.On("Handle", mock.Anything).Return(nil)
72+
mh1.On("isPropertyConsistent", mock.Anything).Return(false)
73+
nds, err := NewNacosDataSource(client, Group, DataId, mh1)
74+
75+
return nds, err
76+
}
77+
78+
func TestNacosDataSource(t *testing.T) {
79+
80+
t.Run("NewNacosDataSource", func(t *testing.T) {
81+
client, err := createConfigClientTest()
82+
assert.Nil(t, err)
83+
nds, err := getNacosDataSource(client)
84+
assert.True(t, nds != nil && err == nil)
85+
})
86+
87+
t.Run("NacosDataSource_Initialize", func(t *testing.T) {
88+
mh1 := &datasource.MockPropertyHandler{}
89+
mh1.On("Handle", mock.Anything).Return(nil)
90+
mh1.On("isPropertyConsistent", mock.Anything).Return(false)
91+
nacosClientMock := new(nacosClientMock)
92+
nacosClientMock.On("GetConfig", mock.Anything).Return(TestSystemRules, nil)
93+
nacosClientMock.On("ListenConfig", mock.Anything).Return(nil)
94+
nds, err := getNacosDataSource(nacosClientMock)
95+
assert.True(t, nds != nil && err == nil)
96+
err = nds.Initialize()
97+
assert.True(t, err == nil)
98+
})
99+
}

go.mod

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ go 1.13
44

55
require (
66
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
7-
github.com/apache/dubbo-go v0.1.2-0.20200224151332-dd1a3c24d656
7+
github.com/apache/dubbo-go v1.4.2
88
github.com/coreos/etcd v3.3.13+incompatible
99
github.com/fsnotify/fsnotify v1.4.7
1010
github.com/gin-gonic/gin v1.5.0
1111
github.com/go-ole/go-ole v1.2.4 // indirect
1212
github.com/google/uuid v1.1.1
1313
github.com/hashicorp/consul/api v1.4.0
1414
github.com/labstack/echo/v4 v4.1.15
15-
github.com/pkg/errors v0.8.1
15+
github.com/nacos-group/nacos-sdk-go v0.4.0
16+
github.com/pkg/errors v0.9.1
1617
github.com/shirou/gopsutil v2.19.12+incompatible
17-
github.com/stretchr/testify v1.4.0
18+
github.com/stretchr/testify v1.5.1
19+
github.com/tidwall/gjson v1.6.0
1820
go.uber.org/multierr v1.5.0
1921
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b // indirect
2022
google.golang.org/grpc v1.22.1
21-
github.com/tidwall/gjson v1.6.0
2223
gopkg.in/yaml.v2 v2.2.8
2324
)

0 commit comments

Comments
 (0)