github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/framework/configmgr/nacos.go (about) 1 package configmgr 2 3 import ( 4 "fmt" 5 "github.com/joho/godotenv" 6 "github.com/pkg/errors" 7 "github.com/unionj-cloud/go-doudou/toolkit/dotenv" 8 "github.com/unionj-cloud/go-doudou/toolkit/maputils" 9 "github.com/unionj-cloud/go-doudou/toolkit/yaml" 10 logger "github.com/unionj-cloud/go-doudou/toolkit/zlogger" 11 "github.com/wubin1989/nacos-sdk-go/clients" 12 "github.com/wubin1989/nacos-sdk-go/clients/cache" 13 "github.com/wubin1989/nacos-sdk-go/clients/config_client" 14 "github.com/wubin1989/nacos-sdk-go/util" 15 "github.com/wubin1989/nacos-sdk-go/vo" 16 "io" 17 "os" 18 "strings" 19 "sync" 20 ) 21 22 type nacosConfigType string 23 24 const ( 25 DotenvConfigFormat nacosConfigType = "dotenv" 26 YamlConfigFormat nacosConfigType = "yaml" 27 ) 28 29 type NacosConfigMgr struct { 30 dataIds []string 31 group string 32 format nacosConfigType 33 namespaceId string 34 client config_client.IConfigClient 35 listeners cache.ConcurrentMap 36 } 37 38 func (m *NacosConfigMgr) Listeners() cache.ConcurrentMap { 39 return m.listeners 40 } 41 42 func NewNacosConfigMgr(dataIds []string, group string, format nacosConfigType, namespaceId string, client config_client.IConfigClient, listeners cache.ConcurrentMap) *NacosConfigMgr { 43 return &NacosConfigMgr{dataIds: dataIds, group: group, format: format, namespaceId: namespaceId, client: client, listeners: listeners} 44 } 45 46 var NacosClient *NacosConfigMgr 47 48 type NacosChangeEvent struct { 49 Namespace, Group, DataId string 50 Changes map[string]maputils.Change 51 } 52 53 type NacosConfigListenerParam struct { 54 DataId string 55 OnChange func(event *NacosChangeEvent) 56 } 57 58 func (m *NacosConfigMgr) fetchConfig(dataId string) (string, error) { 59 content, err := m.client.GetConfig(vo.ConfigParam{ 60 DataId: dataId, 61 Group: m.group, 62 }) 63 if err != nil { 64 return "", err 65 } 66 return content, nil 67 } 68 69 func (m *NacosConfigMgr) loadDotenv(dataId string) error { 70 content, err := m.fetchConfig(dataId) 71 if err != nil { 72 return err 73 } 74 envMap, err := godotenv.Parse(StringReader(content)) 75 if err != nil { 76 return err 77 } 78 currentEnv := map[string]bool{} 79 rawEnv := os.Environ() 80 for _, rawEnvLine := range rawEnv { 81 key := strings.Split(rawEnvLine, "=")[0] 82 currentEnv[key] = true 83 } 84 for key, value := range envMap { 85 if !currentEnv[key] { 86 _ = os.Setenv(key, value) 87 } 88 } 89 return nil 90 } 91 92 func (m *NacosConfigMgr) loadYaml(dataId string) error { 93 content, err := m.fetchConfig(dataId) 94 if err != nil { 95 return err 96 } 97 return yaml.LoadReader(strings.NewReader(content)) 98 } 99 100 var onceNacos sync.Once 101 var NewConfigClient = clients.NewConfigClient 102 103 func InitialiseNacosConfig(param vo.NacosClientParam, dataId, format, group string) { 104 client, err := NewConfigClient(param) 105 if err != nil { 106 panic(errors.Wrap(err, "[go-doudou] failed to create nacos config client")) 107 } 108 dataIds := strings.Split(dataId, ",") 109 NacosClient = &NacosConfigMgr{ 110 dataIds: dataIds, 111 group: group, 112 format: nacosConfigType(format), 113 namespaceId: param.ClientConfig.NamespaceId, 114 client: client, 115 listeners: cache.NewConcurrentMap(), 116 } 117 } 118 119 func LoadFromNacos(param vo.NacosClientParam, dataId, format, group string) error { 120 onceNacos.Do(func() { 121 InitialiseNacosConfig(param, dataId, format, group) 122 }) 123 switch nacosConfigType(format) { 124 case YamlConfigFormat: 125 for _, item := range NacosClient.dataIds { 126 if err := NacosClient.loadYaml(item); err != nil { 127 return errors.Wrap(err, "[go-doudou] failed to load yaml config") 128 } 129 } 130 case DotenvConfigFormat: 131 for _, item := range NacosClient.dataIds { 132 if err := NacosClient.loadDotenv(item); err != nil { 133 return errors.Wrap(err, "[go-doudou] failed to load dotenv config") 134 } 135 } 136 default: 137 return fmt.Errorf("[go-doudou] unknown config format: %s\n", format) 138 } 139 NacosClient.listenConfig() 140 return nil 141 } 142 143 var StringReader = func(s string) io.Reader { 144 return strings.NewReader(s) 145 } 146 147 func (m *NacosConfigMgr) CallbackOnChange(namespace, group, dataId, data, old string) { 148 var newData, oldData map[string]interface{} 149 var err error 150 switch m.format { 151 case YamlConfigFormat: 152 if newData, err = yaml.LoadReaderAsMap(StringReader(data)); err != nil { 153 logger.Error().Err(err).Msg("[go-doudou] error from nacos config listener") 154 return 155 } 156 if oldData, err = yaml.LoadReaderAsMap(StringReader(old)); err != nil { 157 logger.Error().Err(err).Msg("[go-doudou] error from nacos config listener") 158 return 159 } 160 case DotenvConfigFormat: 161 if newData, err = dotenv.LoadAsMap(StringReader(data)); err != nil { 162 logger.Error().Err(err).Msg("[go-doudou] error from nacos config listener") 163 return 164 } 165 if oldData, err = dotenv.LoadAsMap(StringReader(old)); err != nil { 166 logger.Error().Err(err).Msg("[go-doudou] error from nacos config listener") 167 return 168 } 169 } 170 changes := maputils.Diff(newData, oldData) 171 m.onChange("__"+dataId+"__"+"registry", group, namespace, changes) 172 m.onChange("__"+dataId+"__"+"ddhttp", group, namespace, changes) 173 m.onChange(dataId, group, namespace, changes) 174 } 175 176 func (m *NacosConfigMgr) listenConfig() { 177 for _, dataId := range m.dataIds { 178 if err := m.client.ListenConfig(vo.ConfigParam{ 179 DataId: dataId, 180 Group: m.group, 181 OnChange: m.CallbackOnChange, 182 }); err != nil { 183 panic(err) 184 } 185 } 186 } 187 188 func (m *NacosConfigMgr) onChange(dataId, group, namespace string, changes map[string]maputils.Change) { 189 key := util.GetConfigCacheKey(dataId, group, namespace) 190 if v, ok := m.listeners.Get(key); ok { 191 listener := v.(NacosConfigListenerParam) 192 listener.OnChange(&NacosChangeEvent{ 193 Namespace: namespace, 194 Group: group, 195 DataId: dataId, 196 Changes: changes, 197 }) 198 } 199 } 200 201 func (m *NacosConfigMgr) AddChangeListener(param NacosConfigListenerParam) { 202 key := util.GetConfigCacheKey(param.DataId, m.group, m.namespaceId) 203 if _, ok := m.listeners.Get(key); ok { 204 logger.Warn().Msgf("[go-doudou] you have already add a config change listener for dataId: %s, you cannot override it", param.DataId) 205 return 206 } 207 m.listeners.Set(key, param) 208 }