forked from weibocom/motan-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
251 lines (228 loc) · 6.3 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
package motan
import (
"errors"
"flag"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
motan "github.com/weibocom/motan-go/core"
"github.com/weibocom/motan-go/log"
mserver "github.com/weibocom/motan-go/server"
"runtime/debug"
)
// MSContext is Motan Server Context
type MSContext struct {
confFile string
context *motan.Context
extFactory motan.ExtentionFactory
portService map[int]motan.Exporter
portServer map[int]motan.Server
serviceImpls map[string]interface{}
registries map[string]motan.Registry // all registries used for services
csync sync.Mutex
inited bool
}
const (
defaultServerPort = "9982"
defaultProtocal = "motan2"
)
var (
serverContextMap = make(map[string]*MSContext, 8)
serverContextMutex sync.Mutex
)
// 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 {
if !flag.Parsed() {
flag.Parse()
}
serverContextMutex.Lock()
defer serverContextMutex.Unlock()
ms := serverContextMap[confFile]
if ms == nil {
ms = &MSContext{confFile: confFile}
serverContextMap[confFile] = ms
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)
}
return ms
}
func (m *MSContext) Start(extfactory motan.ExtentionFactory) {
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)
}
}
func (m *MSContext) export(url *motan.URL) {
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
vlog.Errorf("MSContext export fail! url: %v, err:%+v\n", url, err)
}
}()
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 := defaultProtocal
if export != "" {
s := strings.Split(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
porti, err := strconv.Atoi(port)
if err != nil {
vlog.Errorf("export port not int. port:%s, url:%+v\n", port, url)
return
}
url.Port = porti
if url.Host == "" {
url.Host = motan.GetLocalIP()
}
provider := GetDefaultExtFactory().GetProvider(url)
provider.SetService(service)
motan.Initialize(provider)
provider = mserver.WarperWithFilter(provider, m.extFactory)
exporter := &mserver.DefaultExporter{}
exporter.SetProvider(provider)
server := m.portServer[url.Port]
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[url.Port] = 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\n", url, server.GetURL())
return
}
err = exporter.Export(server, m.extFactory, m.context)
if err != nil {
vlog.Errorf("service export fail! url:%v, err:%v\n", url, err)
} else {
vlog.Infof("service export success. url:%v\n", url)
for _, r := range exporter.Registrys {
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.Context{ConfigFile: m.confFile}
m.context.Initialize()
m.portService = make(map[int]motan.Exporter, 32)
m.portServer = make(map[int]motan.Server, 32)
m.serviceImpls = make(map[string]interface{}, 32)
m.registries = make(map[string]motan.Registry)
m.inited = true
}
}
// 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\n", 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\n", 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() {
availableService(m.registries)
}
// ServicesUnavailable will enable all service registed in registries
func (m *MSContext) ServicesUnavailable() {
unavailableService(m.registries)
}
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 func() {
if err := recover(); err != nil {
vlog.Errorf("availableService got a panic!err:%+v\n", err)
}
}()
if registries != nil {
for _, r := range registries {
r.Available(nil)
}
}
}
func unavailableService(registries map[string]motan.Registry) {
defer func() {
if err := recover(); err != nil {
vlog.Errorf("unavailableService got a panic!err:%+v\n", err)
}
}()
if registries != nil {
for _, r := range registries {
r.Unavailable(nil)
}
}
}