-
Notifications
You must be signed in to change notification settings - Fork 112
/
server.go
328 lines (297 loc) · 8.64 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
package motan
import (
"errors"
"flag"
"fmt"
"github.com/weibocom/motan-go/config"
motan "github.com/weibocom/motan-go/core"
"github.com/weibocom/motan-go/log"
"github.com/weibocom/motan-go/provider"
"github.com/weibocom/motan-go/registry"
mserver "github.com/weibocom/motan-go/server"
"hash/fnv"
"net/http"
"reflect"
"strconv"
"strings"
"sync"
"time"
)
// MSContext is Motan Server Context
type MSContext struct {
config *config.Config
context *motan.Context
extFactory motan.ExtensionFactory
portService map[int]motan.Exporter
portServer map[string]motan.Server
serviceImpls map[string]interface{}
registries map[string]motan.Registry // all registries used for services
registryLock sync.Mutex
status int
csync sync.Mutex
inited bool
}
const (
defaultServerPort = "9982"
defaultProtocol = "motan2"
)
var (
serverContextMap = make(map[string]*MSContext, 8)
serverContextMutex sync.Mutex
)
func NewMotanServerContextFromConfig(conf *config.Config) (ms *MSContext) {
ms = &MSContext{config: conf}
motan.Initialize(ms)
section, err := ms.context.Config.GetSection("motan-server")
if err != nil {
fmt.Println("get config of \"motan-server\" fail! err " + err.Error())
}
logDir := ""
if section != nil && section["log_dir"] != nil {
logDir = section["log_dir"].(string)
}
if logDir == "" {
logDir = "."
}
initLog(logDir, section)
registerSwitchers(ms.context)
return ms
}
// GetMotanServerContext start a motan server context by config
// a motan server context can listen multi ports and provide many services. so a single motan server context is suggested
// default context will be used if confFile is empty
func GetMotanServerContext(confFile string) *MSContext {
return GetMotanServerContextWithFlagParse(confFile, true)
}
func GetMotanServerContextWithFlagParse(confFile string, parseFlag bool) *MSContext {
if parseFlag && !flag.Parsed() {
flag.Parse()
}
serverContextMutex.Lock()
defer serverContextMutex.Unlock()
conf, err := config.NewConfigFromFile(confFile)
if err != nil {
vlog.Errorf("parse config file fail, error: %s, file: %s", err, confFile)
return nil
}
ms := serverContextMap[confFile]
if ms == nil {
ms = NewMotanServerContextFromConfig(conf)
serverContextMap[confFile] = ms
}
return ms
}
func (m *MSContext) Start(extfactory motan.ExtensionFactory) {
m.csync.Lock()
defer m.csync.Unlock()
m.extFactory = extfactory
if m.extFactory == nil {
m.extFactory = GetDefaultExtFactory()
}
for _, url := range m.context.ServiceURLs {
m.export(url)
}
go m.startRegistryFailback()
}
func (m *MSContext) hashInt(s string) int {
h := fnv.New32a()
h.Write([]byte(s))
return int(h.Sum32())
}
func (m *MSContext) export(url *motan.URL) {
defer motan.HandlePanic(nil)
service := m.serviceImpls[url.Parameters[motan.RefKey]]
if service != nil {
//TODO multi protocol support. convert to multi url
export := url.GetParam(motan.ExportKey, "")
port := defaultServerPort
protocol := defaultProtocol
if export != "" {
s := motan.TrimSplit(export, ":")
if len(s) == 1 {
port = s[0]
} else if len(s) == 2 {
if s[0] != "" {
protocol = s[0]
}
port = s[1]
}
}
url.Protocol = protocol
var porti int
var serverKey string
var err error
if v := url.GetParam(provider.ProxyHostKey, ""); strings.HasPrefix(v, motan.UnixSockProtocolFlag) {
porti = 0
serverKey = v
} else if v := url.GetParam(motan.UnixSockKey, ""); v != "" {
porti = 0
serverKey = v
} else {
porti, err = strconv.Atoi(port)
if err != nil {
vlog.Errorf("export port not int. port:%s, url:%+v", port, url)
return
}
serverKey = port
}
url.Port = porti
if url.Host == "" {
url.Host = motan.GetLocalIP()
}
url.ClearCachedInfo()
provider := GetDefaultExtFactory().GetProvider(url)
provider.SetService(service)
motan.Initialize(provider)
provider = mserver.WrapWithFilter(provider, m.extFactory, m.context)
exporter := &mserver.DefaultExporter{}
exporter.SetProvider(provider)
server := m.portServer[serverKey]
if server == nil {
server = m.extFactory.GetServer(url)
handler := GetDefaultExtFactory().GetMessageHandler("default")
motan.Initialize(handler)
handler.AddProvider(provider)
server.Open(false, false, handler, m.extFactory)
m.portServer[serverKey] = server
} else if canShareChannel(*url, *server.GetURL()) {
server.GetMessageHandler().AddProvider(provider)
} else {
vlog.Errorf("service export fail! can not share channel.url:%v, port url:%v", url, server.GetURL())
return
}
err = exporter.Export(server, m.extFactory, m.context)
if err != nil {
vlog.Errorf("service export fail! url:%v, err:%v", url, err)
} else {
vlog.Infof("service export success. url:%v", url)
for _, r := range exporter.Registries {
rid := r.GetURL().GetIdentity()
if _, ok := m.registries[rid]; !ok {
m.registries[rid] = r
}
}
}
}
}
func (m *MSContext) Initialize() {
m.csync.Lock()
defer m.csync.Unlock()
if !m.inited {
m.context = motan.NewContextFromConfig(m.config, "", "")
m.portService = make(map[int]motan.Exporter, 32)
m.portServer = make(map[string]motan.Server, 32)
m.serviceImpls = make(map[string]interface{}, 32)
m.registries = make(map[string]motan.Registry)
m.inited = true
}
}
func (m *MSContext) GetContext() *motan.Context {
return m.context
}
// RegisterService register service with serviceId for config ref.
// the type.string will used as serviceId if sid is not set. e.g. 'packageName.structName'
func (m *MSContext) RegisterService(s interface{}, sid string) error {
if s == nil {
vlog.Errorln("MSContext register service is nil!")
return errors.New("register service is nil")
}
v := reflect.ValueOf(s)
if v.Kind() != reflect.Ptr {
vlog.Errorf("register service must be a pointer of struct. service:%+v", s)
return errors.New("register service must be a pointer of struct")
}
t := v.Elem().Type()
hasConfig := false
ref := sid
if ref == "" {
ref = t.String()
}
// check export config
for _, url := range m.context.ServiceURLs {
if url.Parameters != nil && ref == url.Parameters[motan.RefKey] {
hasConfig = true
break
}
}
if !hasConfig {
vlog.Errorf("can not find export config for register service. service:%+v", s)
return errors.New("can not find export config for register service")
}
m.serviceImpls[ref] = s
return nil
}
// ServicesAvailable will enable all service registed in registries
func (m *MSContext) ServicesAvailable() {
// TODO: same as agent
m.registryLock.Lock()
defer m.registryLock.Unlock()
m.status = http.StatusOK
availableService(m.registries)
}
// ServicesUnavailable will enable all service registered in registries
func (m *MSContext) ServicesUnavailable() {
m.registryLock.Lock()
defer m.registryLock.Unlock()
m.status = http.StatusServiceUnavailable
unavailableService(m.registries)
}
func (m *MSContext) GetRegistryStatus() []map[string]*motan.RegistryStatus {
m.registryLock.Lock()
defer m.registryLock.Unlock()
var res []map[string]*motan.RegistryStatus
for _, v := range m.registries {
if vv, ok := v.(motan.RegistryStatusManager); ok {
res = append(res, vv.GetRegistryStatus())
}
}
return res
}
func (m *MSContext) startRegistryFailback() {
vlog.Infoln("start MSContext registry failback")
ticker := time.NewTicker(time.Duration(registry.GetFailbackInterval()) * time.Millisecond)
defer ticker.Stop()
for range ticker.C {
m.registryLock.Lock()
for _, v := range m.registries {
if vv, ok := v.(motan.RegistryStatusManager); ok {
statusMap := vv.GetRegistryStatus()
for _, j := range statusMap {
if m.status == http.StatusOK && j.Status == motan.RegisterFailed {
vlog.Infoln(fmt.Sprintf("detect register fail, do register again, service: %s", j.Service.GetIdentity()))
v.(motan.Registry).Available(j.Service)
} else if m.status == http.StatusServiceUnavailable && j.Status == motan.UnregisterFailed {
vlog.Infoln(fmt.Sprintf("detect unregister fail, do unregister again, service: %s", j.Service.GetIdentity()))
v.(motan.Registry).Unavailable(j.Service)
}
}
}
}
m.registryLock.Unlock()
}
}
func canShareChannel(u1 motan.URL, u2 motan.URL) bool {
if u1.Protocol != u2.Protocol {
return false
}
if !motan.IsSame(u1.Parameters, u2.Parameters, motan.SerializationKey, "") {
return false
}
return true
}
func availableService(registries map[string]motan.Registry) {
defer motan.HandlePanic(nil)
if registries != nil {
for _, r := range registries {
r.Available(nil)
}
}
}
func unavailableService(registries map[string]motan.Registry) {
defer motan.HandlePanic(nil)
if registries != nil {
for _, r := range registries {
r.Unavailable(nil)
}
}
}