From 30f4a2360b261230a78792dacd747cc1855eca08 Mon Sep 17 00:00:00 2001 From: liangdas <1587790525@qq.com> Date: Fri, 19 May 2017 19:39:23 +0800 Subject: [PATCH] =?UTF-8?q?=3Dv1.2.0=E7=89=88=E6=9C=AC=20=E6=9E=81?= =?UTF-8?q?=E5=A4=A7=E7=9A=84=E4=BC=98=E5=8C=96=E4=BA=86mqant=E6=80=A7?= =?UTF-8?q?=E8=83=BD,=E8=B7=B3=E8=B0=83=E6=95=B4=E4=BA=86=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +- app/app.go | 90 ++-- gate/define.go | 44 +- gate/gate_handler.go | 60 +-- gate/mqtt/mqtt_client_server.go | 2 +- gate/mqtt/pack_queue.go | 2 +- gate/mqtt_agent.go | 80 +++- gate/mqtt_gate.go | 73 +++- gate/session.go | 222 ++++++---- gate/session.pb.go | 109 +++++ gate/session.proto | 10 + gate/session_test.go | 47 ++ log/log.go | 28 +- log/logger.go | 18 +- module/base/DefaultModule.go | 65 +++ module/base/ModuleManager.go | 132 ++++++ module/base/ServerSession.go | 70 +++ module/{ => base}/base_module.go | 50 ++- module/{server.go => base/rpcserver.go} | 22 +- module/module.go | 227 +++------- module/{ => modules}/master_module.go | 17 +- module/{ => modules}/timer_module.go | 9 +- mqant.go | 10 +- network/tcp_server.go | 2 +- network/ws_server.go | 2 +- rpc/{ => base}/amqp_client.go | 46 +- rpc/{ => base}/amqp_server.go | 60 ++- rpc/{ => base}/local_client.go | 26 +- rpc/{ => base}/local_server.go | 22 +- rpc/{ => base}/rabbit.go | 2 +- rpc/base/rpc_client.go | 175 ++++++++ rpc/base/rpc_server.go | 310 +++++++++++++ rpc/pb/rpc.pb.go | 176 ++++++++ rpc/pb/rpc.proto | 19 + rpc/pb/rpc_test.go | 63 +++ rpc/rpc.go | 80 ++++ rpc/rpc_client.go | 219 ---------- rpc/rpc_server.go | 550 ------------------------ rpc/util/argsutil.go | 119 +++++ utils/fatih/structs/.travis.yml | 2 +- utils/params_bytes.go | 108 +++++ utils/params_bytes_test.go | 55 +++ utils/safemap.go | 14 +- 43 files changed, 2184 insertions(+), 1262 deletions(-) create mode 100644 gate/session.pb.go create mode 100644 gate/session.proto create mode 100644 gate/session_test.go create mode 100644 module/base/DefaultModule.go create mode 100644 module/base/ModuleManager.go create mode 100644 module/base/ServerSession.go rename module/{ => base}/base_module.go (78%) rename module/{server.go => base/rpcserver.go} (75%) rename module/{ => modules}/master_module.go (95%) rename module/{ => modules}/timer_module.go (83%) rename rpc/{ => base}/amqp_client.go (85%) rename rpc/{ => base}/amqp_server.go (77%) rename rpc/{ => base}/local_client.go (82%) rename rpc/{ => base}/local_server.go (74%) rename rpc/{ => base}/rabbit.go (99%) create mode 100644 rpc/base/rpc_client.go create mode 100644 rpc/base/rpc_server.go create mode 100644 rpc/pb/rpc.pb.go create mode 100644 rpc/pb/rpc.proto create mode 100644 rpc/pb/rpc_test.go create mode 100644 rpc/rpc.go delete mode 100644 rpc/rpc_client.go delete mode 100644 rpc/rpc_server.go create mode 100644 rpc/util/argsutil.go create mode 100644 utils/params_bytes.go create mode 100644 utils/params_bytes_test.go diff --git a/README.md b/README.md index 96ece80..e1027c9 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,6 @@ # mqant mqant是一款基于Golang语言的简洁,高效,高性能的分布式游戏服务器框架,研发的初衷是要实现一款能支持高并发,高性能,高实时性,的游戏服务器框架,也希望mqant未来能够做即时通讯和物联网方面的应用 -# pymqant -pymqant是已经mqant相同的设计原理用python实现的,python版本设计的初衷并不是替代golang语言版本的mqant,而是希望mqant能充分利用python语言的优势(丰富的开源库),因此python是golang语言版本的mqant辅助版本 - -[https://github.com/liangdas/pymqant](https://github.com/liangdas/pymqant) - -## 可以用pymqant来干什么? -理论上pymqant也实现了mqant完全相同的功能,只是缺少一个网关模块,可以用pymqant的flask实现游戏的web api模块,mqant实现对性能要求较高的游戏核心逻辑模块,pymqant模块与mqant模块之间RPC方法可以无缝相互调用,实现双向通信。 @@ -78,6 +71,8 @@ bug请直接通过issue提交 ##版本日志 +###[v1.3.0新特性](https://github.com/liangdas/mqant/wiki/v1.3.0) + ###[v1.2.0新特性](https://github.com/liangdas/mqant/wiki/v1.2.0) ###[v1.1.0新特性](https://github.com/liangdas/mqant/wiki/v1.1.0) diff --git a/app/app.go b/app/app.go index 6ef22c7..1412889 100644 --- a/app/app.go +++ b/app/app.go @@ -11,13 +11,14 @@ // 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 app +package defaultApp import ( "flag" "fmt" "github.com/liangdas/mqant/conf" "github.com/liangdas/mqant/log" + "github.com/liangdas/mqant/module/base" "github.com/liangdas/mqant/module" "github.com/liangdas/mqant/rpc" "hash/crc32" @@ -27,13 +28,16 @@ import ( "os/signal" "path/filepath" "strings" + "github.com/liangdas/mqant/rpc/base" + "github.com/liangdas/mqant/module/modules" ) + func NewApp(version string) module.App { app := new(DefaultApp) - app.routes = map[string]func(app module.App, Type string, hash string) *module.ServerSession{} - app.serverList = map[string]*module.ServerSession{} - app.defaultRoutes = func(app module.App, Type string, hash string) *module.ServerSession { + app.routes = map[string]func(app module.App, Type string, hash string) module.ServerSession{} + app.serverList = map[string]module.ServerSession{} + app.defaultRoutes = func(app module.App, Type string, hash string) module.ServerSession { //默认使用第一个Server servers := app.GetServersByType(Type) if len(servers) == 0 { @@ -42,6 +46,7 @@ func NewApp(version string) module.App { index := int(math.Abs(float64(crc32.ChecksumIEEE([]byte(hash))))) % len(servers) return servers[index] } + app.rpcserializes=map[string]module.RPCSerialize{} app.version = version return app } @@ -49,10 +54,11 @@ func NewApp(version string) module.App { type DefaultApp struct { module.App version string - serverList map[string]*module.ServerSession + serverList map[string]module.ServerSession settings conf.Config - routes map[string]func(app module.App, Type string, hash string) *module.ServerSession - defaultRoutes func(app module.App, Type string, hash string) *module.ServerSession + routes map[string]func(app module.App, Type string, hash string) module.ServerSession + defaultRoutes func(app module.App, Type string, hash string) module.ServerSession + rpcserializes map[string]module.RPCSerialize } func (app *DefaultApp) Run(debug bool, mods ...module.Module) error { @@ -84,8 +90,8 @@ func (app *DefaultApp) Run(debug bool, mods ...module.Module) error { app.Configure(conf.Conf) //配置信息 log.Info("mqant %v starting up", app.version) - manager := module.NewModuleManager() - manager.RegisterRunMod(module.TimerModule()) //注册时间轮模块 每一个进程都默认运行 + manager := basemodule.NewModuleManager() + manager.RegisterRunMod(modules.TimerModule()) //注册时间轮模块 每一个进程都默认运行 // module for i := 0; i < len(mods); i++ { manager.Register(mods[i]) @@ -101,11 +107,11 @@ func (app *DefaultApp) Run(debug bool, mods ...module.Module) error { log.Info("mqant closing down (signal: %v)", sig) return nil } -func (app *DefaultApp) Route(moduleType string, fn func(app module.App, Type string, hash string) *module.ServerSession) error { +func (app *DefaultApp) Route(moduleType string, fn func(app module.App, Type string, hash string) module.ServerSession) error { app.routes[moduleType] = fn return nil } -func (app *DefaultApp) getRoute(moduleType string) func(app module.App, Type string, hash string) *module.ServerSession { +func (app *DefaultApp) getRoute(moduleType string) func(app module.App, Type string, hash string) module.ServerSession { fn := app.routes[moduleType] if fn == nil { //如果没有设置的路由,则使用默认的 @@ -114,6 +120,19 @@ func (app *DefaultApp) getRoute(moduleType string) func(app module.App, Type str return fn } + +func (app *DefaultApp) AddRPCSerialize(name string, Interface module.RPCSerialize) error{ + if _,ok:=app.rpcserializes[name];ok{ + return fmt.Errorf("The name(%s) has been occupied",name) + } + app.rpcserializes[name]=Interface + return nil +} + +func (app *DefaultApp)GetRPCSerialize()(map[string]module.RPCSerialize){ + return app.rpcserializes +} + func (app *DefaultApp) Configure(settings conf.Config) error { app.settings = settings return nil @@ -122,15 +141,15 @@ func (app *DefaultApp) Configure(settings conf.Config) error { /** */ func (app *DefaultApp) OnInit(settings conf.Config) error { - app.serverList = make(map[string]*module.ServerSession) + app.serverList = make(map[string]module.ServerSession) for Type, ModuleInfos := range settings.Module { for _, moduel := range ModuleInfos { m := app.serverList[moduel.Id] if m != nil { //如果Id已经存在,说明有两个相同Id的模块,这种情况不能被允许,这里就直接抛异常 强制崩溃以免以后调试找不到问题 - panic(fmt.Sprintf("ServerId (%s) Type (%s) of the modules already exist Can not be reused ServerId (%s) Type (%s)", m.Id, m.Stype, moduel.Id, Type)) + panic(fmt.Sprintf("ServerId (%s) Type (%s) of the modules already exist Can not be reused ServerId (%s) Type (%s)", m.GetId(), m.GetType(), moduel.Id, Type)) } - client, err := mqrpc.NewRPCClient() + client, err := defaultrpc.NewRPCClient(app,moduel.Id) if err != nil { continue } @@ -138,11 +157,7 @@ func (app *DefaultApp) OnInit(settings conf.Config) error { //如果远程的rpc存在则创建一个对应的客户端 client.NewRemoteClient(moduel.Rabbitmq) } - session := &module.ServerSession{ - Id: moduel.Id, - Stype: Type, - Rpc: client, - } + session := basemodule.NewServerSession(moduel.Id,Type,client) app.serverList[moduel.Id] = session log.Info("RPCClient create success type(%s) id(%s)", Type, moduel.Id) } @@ -152,26 +167,26 @@ func (app *DefaultApp) OnInit(settings conf.Config) error { func (app *DefaultApp) OnDestroy() error { for id, session := range app.serverList { - err := session.Rpc.Done() + err := session.GetRpc().Done() if err != nil { - log.Warning("RPCClient close fail type(%s) id(%s)", session.Stype, id) + log.Warning("RPCClient close fail type(%s) id(%s)", session.GetType(), id) } else { - log.Info("RPCClient close success type(%s) id(%s)", session.Stype, id) + log.Info("RPCClient close success type(%s) id(%s)", session.GetType(), id) } } return nil } -func (app *DefaultApp) RegisterLocalClient(serverId string, server *mqrpc.RPCServer) error { +func (app *DefaultApp) RegisterLocalClient(serverId string, server mqrpc.RPCServer) error { if session, ok := app.serverList[serverId]; ok { - return session.Rpc.NewLocalClient(server) + return session.GetRpc().NewLocalClient(server) } else { return fmt.Errorf("Server(%s) Not Found", serverId) } return nil } -func (app *DefaultApp) GetServersById(serverId string) (*module.ServerSession, error) { +func (app *DefaultApp) GetServersById(serverId string) (module.ServerSession, error) { if session, ok := app.serverList[serverId]; ok { return session, nil } else { @@ -179,17 +194,17 @@ func (app *DefaultApp) GetServersById(serverId string) (*module.ServerSession, e } } -func (app *DefaultApp) GetServersByType(Type string) []*module.ServerSession { - sessions := make([]*module.ServerSession, 0) +func (app *DefaultApp) GetServersByType(Type string) []module.ServerSession { + sessions := make([]module.ServerSession, 0) for _, session := range app.serverList { - if session.Stype == Type { + if session.GetType() == Type { sessions = append(sessions, session) } } return sessions } -func (app *DefaultApp) GetRouteServers(filter string, hash string) (s *module.ServerSession, err error) { +func (app *DefaultApp) GetRouteServers(filter string, hash string) (s module.ServerSession, err error) { sl := strings.Split(filter, "@") if len(sl) == 2 { moduleID := sl[1] @@ -226,3 +241,20 @@ func (app *DefaultApp) RpcInvokeNR(module module.RPCModule, moduleType string, _ } return server.CallNR(_func, params...) } + +func (app *DefaultApp) RpcInvokeArgs(module module.RPCModule, moduleType string, _func string, ArgsType []string,args [][]byte) (result interface{}, err string) { + server, e := app.GetRouteServers(moduleType, module.GetServerId()) + if e != nil { + err = e.Error() + return + } + return server.CallArgs(_func, ArgsType,args) +} + +func (app *DefaultApp) RpcInvokeNRArgs(module module.RPCModule, moduleType string, _func string, ArgsType []string,args [][]byte) (err error) { + server, err := app.GetRouteServers(moduleType, module.GetServerId()) + if err != nil { + return + } + return server.CallNRArgs(_func, ArgsType,args) +} diff --git a/gate/define.go b/gate/define.go index 980640d..4c501d9 100644 --- a/gate/define.go +++ b/gate/define.go @@ -17,14 +17,40 @@ package gate net代理服务 处理器 */ type GateHandler interface { - Bind(Sessionid string, Userid string) (result interface{}, err string) //Bind the session with the the Userid. - UnBind(Sessionid string) (result interface{}, err string) //UnBind the session with the the Userid. - Set(Sessionid string, key string, value interface{}) (result interface{}, err string) //Set values (one or many) for the session. + Bind(Sessionid string, Userid string) (result Session, err string) //Bind the session with the the Userid. + UnBind(Sessionid string) (result Session, err string) //UnBind the session with the the Userid. + Set(Sessionid string, key string, value string) (result Session, err string) //Set values (one or many) for the session. Remove(Sessionid string, key string) (result interface{}, err string) //Remove value from the session. - Push(Sessionid string, Settings map[string]interface{}) (result interface{}, err string) //推送信息给Session + Push(Sessionid string, Settings map[string]string) (result Session, err string) //推送信息给Session Send(Sessionid string, topic string, body []byte) (result interface{}, err string) //Send message to the session. Close(Sessionid string) (result interface{}, err string) //主动关闭连接 - Update(Sessionid string) (result interface{}, err string) //更新整个Session 通常是其他模块拉取最新数据 + Update(Sessionid string) (result Session, err string) //更新整个Session 通常是其他模块拉取最新数据 +} + +type Session interface { + GetIP() string + GetNetwork() string + GetUserid() string + GetSessionid() string + GetServerid() string + GetSettings() map[string]string + SetIP(ip string) + SetNetwork(network string) + SetUserid(userid string) + SetSessionid(sessionid string) + SetServerid(serverid string) + SetSettings(settings map[string]string) + Serializable()([]byte,error) + Update() (err string) + Bind(Userid string) (err string) + UnBind() (err string) + Push() (err string) + Set(key string, value string) (err string) + Get(key string) (result string) + Remove(key string) (err string) + Send(topic string, body []byte) (err string) + SendNR(topic string, body []byte) (err string) + Close() (err string) } /** @@ -35,7 +61,7 @@ type StorageHandler interface { 存储用户的Session信息 Session Bind Userid以后每次设置 settings都会调用一次Storage */ - Storage(Userid string, settings map[string]interface{}) (err error) + Storage(Userid string, settings map[string]string) (err error) /** 强制删除Session信息 */ @@ -44,7 +70,7 @@ type StorageHandler interface { 获取用户Session信息 Bind Userid时会调用Query获取最新信息 */ - Query(Userid string) (settings map[string]interface{}, err error) + Query(Userid string) (settings map[string]string, err error) /** 用户心跳,一般用户在线时1s发送一次 可以用来延长Session信息过期时间 @@ -60,6 +86,8 @@ type Agent interface { WriteMsg(topic string, body []byte) error Close() Destroy() + RevNum() int64 + SendNum() int64 IsClosed() bool - GetSession() *Session + GetSession() Session } diff --git a/gate/gate_handler.go b/gate/gate_handler.go index cf35141..ce5700d 100644 --- a/gate/gate_handler.go +++ b/gate/gate_handler.go @@ -36,95 +36,95 @@ func NewGateHandler(gate *Gate) *handler { //当连接建立 并且MQTT协议握手成功 func (h *handler) Connect(a Agent) { if a.GetSession() != nil { - h.sessions.Set(a.GetSession().Sessionid, a) + h.sessions.Set(a.GetSession().GetSessionid(), a) } } //当连接关闭 或者客户端主动发送MQTT DisConnect命令 func (h *handler) DisConnect(a Agent) { if a.GetSession() != nil { - h.sessions.Delete(a.GetSession().Sessionid) + h.sessions.Delete(a.GetSession().GetSessionid()) } } /** *更新整个Session 通常是其他模块拉取最新数据 */ -func (h *handler) Update(Sessionid string) (result interface{}, err string) { +func (h *handler) Update(Sessionid string) (result Session, err string) { agent := h.sessions.Get(Sessionid) if agent == nil { err = "No Sesssion found" return } - result = agent.(Agent).GetSession().ExportMap() + result = agent.(Agent).GetSession() return } /** *Bind the session with the the Userid. */ -func (h *handler) Bind(Sessionid string, Userid string) (result interface{}, err string) { +func (h *handler) Bind(Sessionid string, Userid string) (result Session, err string) { agent := h.sessions.Get(Sessionid) if agent == nil { err = "No Sesssion found" return } - agent.(Agent).GetSession().Userid = Userid + agent.(Agent).GetSession().SetUserid(Userid) - if h.gate.storage != nil && agent.(Agent).GetSession().Userid != "" { + if h.gate.storage != nil && agent.(Agent).GetSession().GetUserid() != "" { //可以持久化 settings, err := h.gate.storage.Query(Userid) if err == nil && settings != nil { //有已持久化的数据,可能是上一次连接保存的 - if agent.(Agent).GetSession().Settings == nil { - agent.(Agent).GetSession().Settings = settings + if agent.(Agent).GetSession().GetSettings() == nil { + agent.(Agent).GetSession().SetSettings(settings) } else { //合并两个map 并且以 agent.(Agent).GetSession().Settings 已有的优先 for k, v := range settings { - if _, ok := agent.(Agent).GetSession().Settings[k]; ok { + if _, ok := agent.(Agent).GetSession().GetSettings()[k]; ok { //不用替换 } else { - agent.(Agent).GetSession().Settings[k] = v + agent.(Agent).GetSession().GetSettings()[k] = v } } //数据持久化 - h.gate.storage.Storage(Userid, agent.(Agent).GetSession().Settings) + h.gate.storage.Storage(Userid, agent.(Agent).GetSession().GetSettings()) } } } - result = agent.(Agent).GetSession().ExportMap() + result = agent.(Agent).GetSession() return } /** *UnBind the session with the the Userid. */ -func (h *handler) UnBind(Sessionid string) (result interface{}, err string) { +func (h *handler) UnBind(Sessionid string) (result Session, err string) { agent := h.sessions.Get(Sessionid) if agent == nil { err = "No Sesssion found" return } - agent.(Agent).GetSession().Userid = "" - result = agent.(Agent).GetSession().ExportMap() + agent.(Agent).GetSession().SetUserid("") + result = agent.(Agent).GetSession() return } /** *Push the session with the the Userid. */ -func (h *handler) Push(Sessionid string, Settings map[string]interface{}) (result interface{}, err string) { +func (h *handler) Push(Sessionid string, Settings map[string]string) (result Session, err string) { agent := h.sessions.Get(Sessionid) if agent == nil { err = "No Sesssion found" return } - agent.(Agent).GetSession().Settings = Settings - result = agent.(Agent).GetSession().ExportMap() - if h.gate.storage != nil && agent.(Agent).GetSession().Userid != "" { - err := h.gate.storage.Storage(agent.(Agent).GetSession().Userid, agent.(Agent).GetSession().Settings) + agent.(Agent).GetSession().SetSettings(Settings) + result = agent.(Agent).GetSession() + if h.gate.storage != nil && agent.(Agent).GetSession().GetUserid() != "" { + err := h.gate.storage.Storage(agent.(Agent).GetSession().GetUserid(), agent.(Agent).GetSession().GetSettings()) if err != nil { log.Error("gate session storage failure") } @@ -136,17 +136,17 @@ func (h *handler) Push(Sessionid string, Settings map[string]interface{}) (resul /** *Set values (one or many) for the session. */ -func (h *handler) Set(Sessionid string, key string, value interface{}) (result interface{}, err string) { +func (h *handler) Set(Sessionid string, key string, value string) (result Session, err string) { agent := h.sessions.Get(Sessionid) if agent == nil { err = "No Sesssion found" return } - agent.(Agent).GetSession().Settings[key] = value - result = agent.(Agent).GetSession().ExportMap() + agent.(Agent).GetSession().GetSettings()[key] = value + result = agent.(Agent).GetSession() - if h.gate.storage != nil && agent.(Agent).GetSession().Userid != "" { - err := h.gate.storage.Storage(agent.(Agent).GetSession().Userid, agent.(Agent).GetSession().Settings) + if h.gate.storage != nil && agent.(Agent).GetSession().GetUserid() != "" { + err := h.gate.storage.Storage(agent.(Agent).GetSession().GetUserid(), agent.(Agent).GetSession().GetSettings()) if err != nil { log.Error("gate session storage failure") } @@ -164,11 +164,11 @@ func (h *handler) Remove(Sessionid string, key string) (result interface{}, err err = "No Sesssion found" return } - delete(agent.(Agent).GetSession().Settings, key) - result = agent.(Agent).GetSession().ExportMap() + delete(agent.(Agent).GetSession().GetSettings(), key) + result = agent.(Agent).GetSession() - if h.gate.storage != nil && agent.(Agent).GetSession().Userid != "" { - err := h.gate.storage.Storage(agent.(Agent).GetSession().Userid, agent.(Agent).GetSession().Settings) + if h.gate.storage != nil && agent.(Agent).GetSession().GetUserid() != "" { + err := h.gate.storage.Storage(agent.(Agent).GetSession().GetUserid(), agent.(Agent).GetSession().GetSettings()) if err != nil { log.Error("gate session storage failure") } diff --git a/gate/mqtt/mqtt_client_server.go b/gate/mqtt/mqtt_client_server.go index 59e2d53..bb3a8a2 100755 --- a/gate/mqtt/mqtt_client_server.go +++ b/gate/mqtt/mqtt_client_server.go @@ -229,7 +229,7 @@ func (c *Client) WriteMsg(topic string, body []byte) error { if c.isStop { return fmt.Errorf("connection is closed") } - pack := GetPubPack(1, 0, c.getOnlineMsgId(), &topic, body) + pack := GetPubPack(0, 0, c.getOnlineMsgId(), &topic, body) return c.pushMsg(pack) //return nil } diff --git a/gate/mqtt/pack_queue.go b/gate/mqtt/pack_queue.go index 795ac40..33f74a2 100755 --- a/gate/mqtt/pack_queue.go +++ b/gate/mqtt/pack_queue.go @@ -205,7 +205,7 @@ func (queue *PackQueue) ReadPackInLoop() { } else { <-queue.noticeFin // - log.Info("Queue FIN") + log.Info("Queue not continue") break loop } diff --git a/gate/mqtt_agent.go b/gate/mqtt_agent.go index b3a418f..0f38261 100644 --- a/gate/mqtt_agent.go +++ b/gate/mqtt_agent.go @@ -25,6 +25,7 @@ import ( "runtime" "strings" "time" + "github.com/liangdas/mqant/rpc/util" ) type resultInfo struct { @@ -34,7 +35,7 @@ type resultInfo struct { type agent struct { Agent - session *Session + session Session conn network.Conn r *bufio.Reader w *bufio.Writer @@ -42,13 +43,15 @@ type agent struct { client *mqtt.Client isclose bool last_storage_heartbeat_data_time int64 //上一次发送存储心跳时间 + rev_num int64 + send_num int64 } func (a *agent) IsClosed() bool { return a.isclose } -func (a *agent) GetSession() *Session { +func (a *agent) GetSession() Session { return a.session } @@ -83,13 +86,17 @@ func (a *agent) Run() (err error) { //log.Debug("Read login pack %s %s %s %s",*id,*psw,info.GetProtocol(),info.GetVersion()) c := mqtt.NewClient(conf.Conf.Mqtt, a, a.r, a.w, a.conn, info.GetKeepAlive()) a.client = c - a.session = NewSession(a.gate.App, map[string]interface{}{ + a.session,err= NewSessionByMap(a.gate.App, map[string]interface{}{ "Sessionid": Get_uuid(), "Network": a.conn.RemoteAddr().Network(), "IP": a.conn.RemoteAddr().String(), "Serverid": a.gate.GetServerId(), - "Settings": make(map[string]interface{}), + "Settings": make(map[string]string), }) + if err!=nil{ + log.Error("gate create agent fail",err.Error()) + return + } a.gate.agentLearner.Connect(a) //发送连接成功的事件 //回复客户端 CONNECT @@ -107,6 +114,13 @@ func (a *agent) OnClose() error { a.gate.agentLearner.DisConnect(a) //发送连接断开的事件 return nil } + +func (a *agent)RevNum() int64{ + return a.rev_num +} +func (a *agent)SendNum() int64{ + return a.send_num +} func (a *agent) OnRecover(pack *mqtt.Pack) { defer func() { if r := recover(); r != nil { @@ -130,6 +144,7 @@ func (a *agent) OnRecover(pack *mqtt.Pack) { //路由服务 switch pack.GetType() { case mqtt.PUBLISH: + a.rev_num=a.rev_num+1 pub := pack.GetVariable().(*mqtt.Publish) topics := strings.Split(*pub.GetTopic(), "/") var msgid string @@ -139,18 +154,27 @@ func (a *agent) OnRecover(pack *mqtt.Pack) { } else if len(topics) == 3 { msgid = topics[2] } - - var obj interface{} // var obj map[string]interface{} - err := json.Unmarshal(pub.GetMsg(), &obj) - if err != nil { - if msgid != "" { - toResult(a, *pub.GetTopic(), nil, "body must be JSON format") + var ArgsType []string=make([]string, 2) + var args [][]byte=make([][]byte, 2) + if pub.GetMsg()[0]=='{'&&pub.GetMsg()[len(pub.GetMsg())-1]=='}'{ + //尝试解析为json为map + var obj interface{} // var obj map[string]interface{} + err := json.Unmarshal(pub.GetMsg(), &obj) + if err != nil { + if msgid != "" { + toResult(a, *pub.GetTopic(), nil, "The JSON format is incorrect") + } + return } - return + ArgsType[1]=argsutil.MAP + args[1]=pub.GetMsg() + }else{ + ArgsType[1]=argsutil.BYTES + args[1]=pub.GetMsg() } hash := "" - if a.session.Userid != "" { - hash = a.session.Userid + if a.session.GetUserid() != "" { + hash = a.session.GetUserid() } else { hash = a.gate.GetServerId() } @@ -169,31 +193,50 @@ func (a *agent) OnRecover(pack *mqtt.Pack) { } return } - result, e := serverSession.Call(topics[1], a.GetSession().ExportMap(), obj) + if msgid != "" { + ArgsType[0]=RPC_PARAM_SESSION_TYPE + b,err:=a.GetSession().Serializable() + if err!=nil{ + return + } + args[0]=b + result, e := serverSession.CallArgs(topics[1],ArgsType, args) toResult(a, *pub.GetTopic(), result, e) + }else{ + ArgsType[0]=RPC_PARAM_SESSION_TYPE + b,err:=a.GetSession().Serializable() + if err!=nil{ + return + } + args[0]=b + + e := serverSession.CallNRArgs(topics[1],ArgsType, args) + if e!=nil{ + log.Warning("Gate RPC",e.Error()) + } } - if a.GetSession().Userid != "" { + if a.GetSession().GetUserid() != "" { //这个链接已经绑定Userid interval := time.Now().UnixNano()/1000000/1000 - a.last_storage_heartbeat_data_time //单位秒 if interval > a.gate.MinStorageHeartbeat { //如果用户信息存储心跳包的时长已经大于一秒 if a.gate.storage != nil { - a.gate.storage.Heartbeat(a.GetSession().Userid) + a.gate.storage.Heartbeat(a.GetSession().GetUserid()) a.last_storage_heartbeat_data_time = time.Now().UnixNano() / 1000000 / 1000 } } } case mqtt.PINGREQ: //客户端发送的心跳包 - if a.GetSession().Userid != "" { + if a.GetSession().GetUserid() != "" { //这个链接已经绑定Userid interval := time.Now().UnixNano()/1000000/1000 - a.last_storage_heartbeat_data_time //单位秒 if interval > a.gate.MinStorageHeartbeat { //如果用户信息存储心跳包的时长已经大于60秒 if a.gate.storage != nil { - a.gate.storage.Heartbeat(a.GetSession().Userid) + a.gate.storage.Heartbeat(a.GetSession().GetUserid()) a.last_storage_heartbeat_data_time = time.Now().UnixNano() / 1000000 / 1000 } } @@ -202,6 +245,7 @@ func (a *agent) OnRecover(pack *mqtt.Pack) { } func (a *agent) WriteMsg(topic string, body []byte) error { + a.send_num++ return a.client.WriteMsg(topic, body) } diff --git a/gate/mqtt_gate.go b/gate/mqtt_gate.go index e789e89..c2c3d3d 100644 --- a/gate/mqtt_gate.go +++ b/gate/mqtt_gate.go @@ -16,13 +16,18 @@ package gate import ( "bufio" "github.com/liangdas/mqant/conf" - "github.com/liangdas/mqant/module" "github.com/liangdas/mqant/network" "time" + "github.com/liangdas/mqant/module" + "github.com/liangdas/mqant/module/base" + "fmt" + "reflect" + "github.com/liangdas/mqant/log" ) - +var RPC_PARAM_SESSION_TYPE="SESSION" type Gate struct { - module.BaseModule + module.RPCSerialize + basemodule.BaseModule MaxConnNum int MaxMsgLen uint32 MinStorageHeartbeat int64 //Session持久化最短心跳包 @@ -55,10 +60,52 @@ func (gate *Gate) SetStorageHandler(storage StorageHandler) error { func (gate *Gate) GetStorageHandler() (storage StorageHandler) { return gate.storage } +func (gate *Gate)OnConfChanged(settings *conf.ModuleSettings) { + +} + +/** +自定义rpc参数序列化反序列化 Session + */ +func (gate *Gate)Serialize(param interface{})(ptype string,p []byte, err error){ + switch v2:=param.(type) { + case Session: + bytes,err:=v2.Serializable() + if err != nil{ + return RPC_PARAM_SESSION_TYPE,nil,err + } + return RPC_PARAM_SESSION_TYPE,bytes,nil + default: + return "", nil,fmt.Errorf("args [%s] Types not allowed",reflect.TypeOf(param)) + } +} + +func (gate *Gate)Deserialize(ptype string,b []byte)(param interface{},err error){ + switch ptype { + case RPC_PARAM_SESSION_TYPE: + mps,errs:= NewSession(gate.App,b) + if errs!=nil{ + return nil,errs + } + return mps,nil + default: + return nil,fmt.Errorf("args [%s] Types not allowed",ptype) + } +} + +func (gate *Gate)GetTypes()([]string){ + return []string{RPC_PARAM_SESSION_TYPE} +} func (gate *Gate) OnInit(subclass module.RPCModule, app module.App, settings *conf.ModuleSettings) { gate.BaseModule.OnInit(subclass, app, settings) //这是必须的 + //添加Session结构体的序列化操作类 + err:=app.AddRPCSerialize("gate",gate) + if err!=nil{ + log.Warning("Adding session structures failed to serialize interfaces",err.Error()) + } + gate.MaxConnNum = int(settings.Settings["MaxConnNum"].(float64)) gate.MaxMsgLen = uint32(settings.Settings["MaxMsgLen"].(float64)) gate.WSAddr = settings.Settings["WSAddr"].(string) @@ -91,14 +138,14 @@ func (gate *Gate) OnInit(subclass module.RPCModule, app module.App, settings *co gate.agentLearner = handler gate.handler = handler - gate.GetServer().RegisterGO("Update", gate.handler.Update) - gate.GetServer().RegisterGO("Bind", gate.handler.Bind) - gate.GetServer().RegisterGO("UnBind", gate.handler.UnBind) - gate.GetServer().RegisterGO("Push", gate.handler.Push) - gate.GetServer().RegisterGO("Set", gate.handler.Set) - gate.GetServer().RegisterGO("Remove", gate.handler.Remove) - gate.GetServer().RegisterGO("Send", gate.handler.Send) - gate.GetServer().RegisterGO("Close", gate.handler.Close) + gate.GetServer().Register("Update", gate.handler.Update) + gate.GetServer().Register("Bind", gate.handler.Bind) + gate.GetServer().Register("UnBind", gate.handler.UnBind) + gate.GetServer().Register("Push", gate.handler.Push) + gate.GetServer().Register("Set", gate.handler.Set) + gate.GetServer().Register("Remove", gate.handler.Remove) + gate.GetServer().Register("Send", gate.handler.Send) + gate.GetServer().Register("Close", gate.handler.Close) } func (gate *Gate) Run(closeSig chan bool) { @@ -119,6 +166,8 @@ func (gate *Gate) Run(closeSig chan bool) { r: bufio.NewReader(conn), w: bufio.NewWriter(conn), isclose: false, + rev_num:0, + send_num:0, } return a } @@ -139,6 +188,8 @@ func (gate *Gate) Run(closeSig chan bool) { r: bufio.NewReader(conn), w: bufio.NewWriter(conn), isclose: false, + rev_num:0, + send_num:0, } return a } diff --git a/gate/session.go b/gate/session.go index 8488716..ae343ff 100644 --- a/gate/session.go +++ b/gate/session.go @@ -15,167 +15,227 @@ package gate import ( "fmt" + "github.com/golang/protobuf/proto" "github.com/liangdas/mqant/module" ) -type Session struct { +type sessionagent struct { app module.App - IP string //客户端IP - Network string //网络类型 TCP UDP websocket - Userid string - Sessionid string - Serverid string - Settings map[string]interface{} + session *session } -func NewSession(app module.App, s map[string]interface{}) *Session { - se := &Session{ - app: app, +func NewSession(app module.App, data []byte) (Session,error) { + agent:=&sessionagent{ + app:app, + } + se := &session{} + err := proto.Unmarshal(data, se) + if err != nil { + return nil,err + } // 测试结果 + agent.session=se + return agent,nil +} + +func NewSessionByMap(app module.App, data map[string]interface{}) (Session,error) { + agent:=&sessionagent{ + app:app, + session:new(session), } - se.update(s) - return se + err:=agent.updateMap(data) + if err!=nil{ + return nil,err + } + return agent,nil +} + +func (session *sessionagent) GetIP() string { + return session.session.GetIP() +} + +func (session *sessionagent) GetNetwork() string { + return session.session.GetNetwork() +} + +func (session *sessionagent) GetUserid() string { + return session.session.GetUserid() +} + +func (session *sessionagent) GetSessionid() string { + return session.session.GetSessionid() +} + +func (session *sessionagent) GetServerid() string { + return session.session.GetServerid() +} + +func (session *sessionagent) GetSettings() map[string]string { + return session.session.GetSettings() +} + + +func (session *sessionagent)SetIP(ip string){ + session.session.IP=ip +} +func (session *sessionagent)SetNetwork(network string){ + session.session.Network=network +} +func (session *sessionagent)SetUserid(userid string){ + session.session.Userid=userid +} +func (session *sessionagent)SetSessionid(sessionid string){ + session.session.Sessionid=sessionid +} +func (session *sessionagent)SetServerid(serverid string){ + session.session.Serverid=serverid +} +func (session *sessionagent)SetSettings(settings map[string]string){ + session.session.Settings=settings } -func (session *Session) update(s map[string]interface{}) { +func (session *sessionagent) updateMap(s map[string]interface{})error { Userid := s["Userid"] if Userid != nil { - session.Userid = Userid.(string) + session.session.Userid = Userid.(string) } IP := s["IP"] if IP != nil { - session.IP = IP.(string) + session.session.IP = IP.(string) } Network := s["Network"] if Network != nil { - session.Network = Network.(string) + session.session.Network = Network.(string) } Sessionid := s["Sessionid"] if Sessionid != nil { - session.Sessionid = Sessionid.(string) + session.session.Sessionid = Sessionid.(string) } Serverid := s["Serverid"] if Serverid != nil { - session.Serverid = Serverid.(string) + session.session.Serverid = Serverid.(string) } Settings := s["Settings"] if Settings != nil { - session.Settings = Settings.(map[string]interface{}) + session.session.Settings = Settings.(map[string]string) } + return nil } -func (session *Session) ExportMap() map[string]interface{} { - s := map[string]interface{}{} - if session.Userid != "" { - s["Userid"] = session.Userid - } - if session.IP != "" { - s["IP"] = session.IP - } - if session.Network != "" { - s["Network"] = session.Network - } - if session.Sessionid != "" { - s["Sessionid"] = session.Sessionid - } - if session.Serverid != "" { - s["Serverid"] = session.Serverid - } - if session.Settings != nil { - s["Settings"] = session.Settings - } - return s +func (session *sessionagent) update(s Session)error { + Userid := s.GetUserid() + session.session.Userid = Userid + IP := s.GetIP() + session.session.IP = IP + Network := s.GetNetwork() + session.session.Network = Network + Sessionid := s.GetSessionid() + session.session.Sessionid = Sessionid + Serverid := s.GetServerid() + session.session.Serverid = Serverid + Settings := s.GetSettings() + session.session.Settings = Settings + return nil +} + +func (session *sessionagent)Serializable()([]byte,error){ + data, err := proto.Marshal(session.session) + if err != nil { + return nil,err + } // 进行解码 + return data,nil } -func (session *Session) Update() (err string) { + +func (session *sessionagent) Update() (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - server, e := session.app.GetServersById(session.Serverid) + server, e := session.app.GetServersById(session.session.Serverid) if e != nil { - err = fmt.Sprintf("Service not found id(%s)", session.Serverid) + err = fmt.Sprintf("Service not found id(%s)", session.session.Serverid) return } - result, err := server.Call("Update", session.Sessionid) + result, err := server.Call("Update", session.session.Sessionid) if err == "" { if result != nil { //绑定成功,重新更新当前Session - session.update(result.(map[string]interface{})) + session.update(result.(Session)) } } return } -func (session *Session) Bind(Userid string) (err string) { +func (session *sessionagent) Bind(Userid string) (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - server, e := session.app.GetServersById(session.Serverid) + server, e := session.app.GetServersById(session.session.Serverid) if e != nil { - err = fmt.Sprintf("Service not found id(%s)", session.Serverid) + err = fmt.Sprintf("Service not found id(%s)", session.session.Serverid) return } - result, err := server.Call("Bind", session.Sessionid, Userid) + result, err := server.Call("Bind", session.session.Sessionid, Userid) if err == "" { if result != nil { //绑定成功,重新更新当前Session - session.update(result.(map[string]interface{})) + session.update(result.(Session)) } } return } -func (session *Session) UnBind() (err string) { +func (session *sessionagent) UnBind() (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - server, e := session.app.GetServersById(session.Serverid) + server, e := session.app.GetServersById(session.session.Serverid) if e != nil { - err = fmt.Sprintf("Service not found id(%s)", session.Serverid) + err = fmt.Sprintf("Service not found id(%s)", session.session.Serverid) return } - result, err := server.Call("UnBind", session.Sessionid) + result, err := server.Call("UnBind", session.session.Sessionid) if err == "" { if result != nil { //绑定成功,重新更新当前Session - session.update(result.(map[string]interface{})) + session.update(result.(Session)) } } return } -func (session *Session) Push() (err string) { +func (session *sessionagent) Push() (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - server, e := session.app.GetServersById(session.Serverid) + server, e := session.app.GetServersById(session.session.Serverid) if e != nil { - err = fmt.Sprintf("Service not found id(%s)", session.Serverid) + err = fmt.Sprintf("Service not found id(%s)", session.session.Serverid) return } - result, err := server.Call("Push", session.Sessionid, session.Settings) + result, err := server.Call("Push", session.session.Sessionid, session.session.Settings) if err == "" { if result != nil { //绑定成功,重新更新当前Session - session.update(result.(map[string]interface{})) + session.update(result.(Session)) } } return } -func (session *Session) Set(key string, value interface{}) (err string) { +func (session *sessionagent) Set(key string, value string) (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - if session.Settings == nil { + if session.session.Settings == nil { err = fmt.Sprintf("Session.Settings is nil") return } - session.Settings[key] = value + session.session.Settings[key] = value //server,e:=session.app.GetServersById(session.Serverid) //if e!=nil{ // err=fmt.Sprintf("Service not found id(%s)",session.Serverid) @@ -191,24 +251,24 @@ func (session *Session) Set(key string, value interface{}) (err string) { return } -func (session *Session) Get(key string) (result interface{}) { - if session.Settings == nil { +func (session *sessionagent) Get(key string) (result string) { + if session.session.Settings == nil { return } - result = session.Settings[key] + result = session.session.Settings[key] return } -func (session *Session) Remove(key string) (err string) { +func (session *sessionagent) Remove(key string) (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - if session.Settings == nil { + if session.session.Settings == nil { err = fmt.Sprintf("Session.Settings is nil") return } - delete(session.Settings, key) + delete(session.session.Settings, key) //server,e:=session.app.GetServersById(session.Serverid) //if e!=nil{ // err=fmt.Sprintf("Service not found id(%s)",session.Serverid) @@ -223,47 +283,47 @@ func (session *Session) Remove(key string) (err string) { //} return } -func (session *Session) Send(topic string, body []byte) (err string) { +func (session *sessionagent) Send(topic string, body []byte) (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - server, e := session.app.GetServersById(session.Serverid) + server, e := session.app.GetServersById(session.session.Serverid) if e != nil { - err = fmt.Sprintf("Service not found id(%s)", session.Serverid) + err = fmt.Sprintf("Service not found id(%s)", session.session.Serverid) return } - _, err = server.Call("Send", session.Sessionid, topic, body) + _, err = server.Call("Send", session.session.Sessionid, topic, body) return } -func (session *Session) SendNR(topic string, body []byte) (err string) { +func (session *sessionagent) SendNR(topic string, body []byte) (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - server, e := session.app.GetServersById(session.Serverid) + server, e := session.app.GetServersById(session.session.Serverid) if e != nil { - err = fmt.Sprintf("Service not found id(%s)", session.Serverid) + err = fmt.Sprintf("Service not found id(%s)", session.session.Serverid) return } - e = server.CallNR("Send", session.Sessionid, topic, body) + e = server.CallNR("Send", session.session.Sessionid, topic, body) if e != nil { err = e.Error() } return "" } -func (session *Session) Close() (err string) { +func (session *sessionagent) Close() (err string) { if session.app == nil { err = fmt.Sprintf("Module.App is nil") return } - server, e := session.app.GetServersById(session.Serverid) + server, e := session.app.GetServersById(session.session.Serverid) if e != nil { - err = fmt.Sprintf("Service not found id(%s)", session.Serverid) + err = fmt.Sprintf("Service not found id(%s)", session.session.Serverid) return } - _, err = server.Call("Close", session.Sessionid) + _, err = server.Call("Close", session.session.Sessionid) return } diff --git a/gate/session.pb.go b/gate/session.pb.go new file mode 100644 index 0000000..f1b0dae --- /dev/null +++ b/gate/session.pb.go @@ -0,0 +1,109 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: session/session.proto + +/* +Package gate is a generated protocol buffer package. + +It is generated from these files: + session/session.proto + +It has these top-level messages: + Session +*/ +package gate + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import ( + math "math" +) + +// 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.ProtoPackageIsVersion2 // please upgrade the proto package + +type session struct { + IP string `protobuf:"bytes,1,opt,name=IP" json:"IP,omitempty"` + Network string `protobuf:"bytes,2,opt,name=Network" json:"Network,omitempty"` + Userid string `protobuf:"bytes,3,opt,name=Userid" json:"Userid,omitempty"` + Sessionid string `protobuf:"bytes,4,opt,name=Sessionid" json:"Sessionid,omitempty"` + Serverid string `protobuf:"bytes,5,opt,name=Serverid" json:"Serverid,omitempty"` + Settings map[string]string `protobuf:"bytes,6,rep,name=Settings" json:"Settings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *session) Reset() { *m = session{} } +func (m *session) String() string { return proto.CompactTextString(m) } +func (*session) ProtoMessage() {} +func (*session) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *session) GetIP() string { + if m != nil { + return m.IP + } + return "" +} + +func (m *session) GetNetwork() string { + if m != nil { + return m.Network + } + return "" +} + +func (m *session) GetUserid() string { + if m != nil { + return m.Userid + } + return "" +} + +func (m *session) GetSessionid() string { + if m != nil { + return m.Sessionid + } + return "" +} + +func (m *session) GetServerid() string { + if m != nil { + return m.Serverid + } + return "" +} + +func (m *session) GetSettings() map[string]string { + if m != nil { + return m.Settings + } + return nil +} + +func init() { + proto.RegisterType((*session)(nil), "gate.Session") +} + +func init() { proto.RegisterFile("session/session.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 207 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x4e, 0x2d, 0x2e, + 0xce, 0xcc, 0xcf, 0xd3, 0x87, 0xd2, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x2c, 0xe9, 0x89, + 0x25, 0xa9, 0x4a, 0xbf, 0x19, 0xb9, 0xd8, 0x83, 0x21, 0xe2, 0x42, 0x7c, 0x5c, 0x4c, 0x9e, 0x01, + 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x4c, 0x9e, 0x01, 0x42, 0x12, 0x5c, 0xec, 0x7e, 0xa9, + 0x25, 0xe5, 0xf9, 0x45, 0xd9, 0x12, 0x4c, 0x60, 0x41, 0x18, 0x57, 0x48, 0x8c, 0x8b, 0x2d, 0xb4, + 0x38, 0xb5, 0x28, 0x33, 0x45, 0x82, 0x19, 0x2c, 0x01, 0xe5, 0x09, 0xc9, 0x70, 0x71, 0x42, 0x0d, + 0xcb, 0x4c, 0x91, 0x60, 0x01, 0x4b, 0x21, 0x04, 0x84, 0xa4, 0xb8, 0x38, 0x82, 0x53, 0x8b, 0xca, + 0xc0, 0xfa, 0x58, 0xc1, 0x92, 0x70, 0xbe, 0x90, 0x39, 0x48, 0xae, 0xa4, 0x24, 0x33, 0x2f, 0xbd, + 0x58, 0x82, 0x4d, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x5a, 0x0f, 0xe4, 0x40, 0x3d, 0xa8, 0x76, 0x3d, + 0x98, 0xac, 0x6b, 0x5e, 0x49, 0x51, 0x65, 0x10, 0x5c, 0xb1, 0x94, 0x35, 0x17, 0x2f, 0x8a, 0x94, + 0x90, 0x00, 0x17, 0x73, 0x76, 0x6a, 0x25, 0xd4, 0x1b, 0x20, 0xa6, 0x90, 0x08, 0x17, 0x6b, 0x59, + 0x62, 0x4e, 0x69, 0x2a, 0xd4, 0x17, 0x10, 0x8e, 0x15, 0x93, 0x05, 0x63, 0x12, 0x1b, 0x38, 0x28, + 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x8e, 0xe7, 0xbc, 0x38, 0x23, 0x01, 0x00, 0x00, +} diff --git a/gate/session.proto b/gate/session.proto new file mode 100644 index 0000000..e8f30f2 --- /dev/null +++ b/gate/session.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package gate; +message session { + string IP = 1; + string Network = 2; + string Userid = 3; + string Sessionid = 4; + string Serverid = 5; + map Settings = 6; +} \ No newline at end of file diff --git a/gate/session_test.go b/gate/session_test.go new file mode 100644 index 0000000..70cc51f --- /dev/null +++ b/gate/session_test.go @@ -0,0 +1,47 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 gate +import ( + "github.com/golang/protobuf/proto" + "testing" +) +func TestSession(t *testing.T) { + session := &session{ // 使用辅助函数设置域的值 + IP: *proto.String("127.0.0.1"), + Network: *proto.String("tcp"), + Sessionid: *proto.String("iii"), + Serverid: *proto.String("232244"), + } // 进行编码 + session.Settings=map[string]string{"isLogin":"true"} + data, err := proto.Marshal(session) + if err != nil { + t.Fatalf("marshaling error: ", err) + } // 进行解码 + newSession := &session{} + err = proto.Unmarshal(data, newSession) + if err != nil { + t.Fatalf("unmarshaling error: ", err) + } // 测试结果 + if session.Serverid != newSession.GetServerid() { + t.Fatalf("data mismatch %q != %q", session.GetServerid(), newSession.GetServerid()) + } + if newSession.GetSettings()==nil{ + t.Fatalf("data mismatch Settings == nil") + }else{ + if newSession.GetSettings()["isLogin"]!="true"{ + t.Fatalf("data mismatch %q != %q", session.GetSettings()["isLogin"], newSession.GetSettings()["isLogin"]) + } + } + +} diff --git a/log/log.go b/log/log.go index 4719c5e..8d0dfe7 100644 --- a/log/log.go +++ b/log/log.go @@ -12,32 +12,44 @@ // See the License for the specific language governing permissions and // limitations under the License. package log - -var mqlog *MqantLog - +import ( + "github.com/liangdas/mqant/logger/ozzo-log" +) +var mqlog *log.Logger +var defaultLogger *log.Logger func Init(debug bool, ProcessID string, Logdir string) { mqlog = NewMqantLog(debug, ProcessID, Logdir) } +func Log()(*log.Logger){ + if mqlog==nil{ + if defaultLogger==nil{ + defaultLogger=NewDefaultLogger() + } + return defaultLogger + } + return mqlog +} + func Debug(format string, a ...interface{}) { //gLogger.doPrintf(debugLevel, printDebugLevel, format, a...) - mqlog.Debug(format, a...) + Log().Debug(format, a...) } func Info(format string, a ...interface{}) { //gLogger.doPrintf(releaseLevel, printReleaseLevel, format, a...) - mqlog.Info(format, a...) + Log().Info(format, a...) } func Error(format string, a ...interface{}) { //gLogger.doPrintf(errorLevel, printErrorLevel, format, a...) - mqlog.Error(format, a...) + Log().Error(format, a...) } func Warning(format string, a ...interface{}) { //gLogger.doPrintf(fatalLevel, printFatalLevel, format, a...) - mqlog.Warning(format, a...) + Log().Warning(format, a...) } func Close() { - mqlog.Close() + Log().Close() } diff --git a/log/logger.go b/log/logger.go index 9008b44..256c08c 100644 --- a/log/logger.go +++ b/log/logger.go @@ -10,13 +10,21 @@ type MqantLog struct { *log.Logger } -func NewMqantLog(debug bool, ProcessID string, Logdir string) *MqantLog { +func NewMqantLog(debug bool, ProcessID string, Logdir string) *log.Logger { var Mqlog = &MqantLog{} - Mqlog.GetDefaultLogger(debug, ProcessID, Logdir) - return Mqlog + Mqlog.GetLogger(debug, ProcessID, Logdir) + return Mqlog.Logger } - -func (m *MqantLog) GetDefaultLogger(debug bool, ProcessID string, Logdir string) { +func NewDefaultLogger()(*log.Logger) { + logger := log.NewLogger() + logger.CallStackDepth = 3 + t1 := log.NewConsoleTarget() + t1.MinLevel = log.LevelNotice + t1.MaxLevel = log.LevelDebug + logger.Targets = append(logger.Targets, t1) + return logger +} +func (m *MqantLog) GetLogger(debug bool, ProcessID string, Logdir string) { // 创建根记录器(root logger) logger := log.NewLogger() logger.CallStackDepth = 3 diff --git a/module/base/DefaultModule.go b/module/base/DefaultModule.go new file mode 100644 index 0000000..212390e --- /dev/null +++ b/module/base/DefaultModule.go @@ -0,0 +1,65 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 basemodule + +import ( + "github.com/liangdas/mqant/conf" + "github.com/liangdas/mqant/log" + "sync" + "runtime" + "github.com/liangdas/mqant/module" +) + +type DefaultModule struct { + mi module.Module + settings *conf.ModuleSettings + closeSig chan bool + wg sync.WaitGroup +} + + + + +func run(m *DefaultModule) { + defer func() { + if r := recover(); r != nil { + if conf.LenStackBuf > 0 { + buf := make([]byte, conf.LenStackBuf) + l := runtime.Stack(buf, false) + log.Error("%v: %s", r, buf[:l]) + } else { + log.Error("%v", r) + } + } + }() + m.mi.Run(m.closeSig) + m.wg.Done() +} + +func destroy(m *DefaultModule) { + defer func() { + if r := recover(); r != nil { + if conf.LenStackBuf > 0 { + buf := make([]byte, conf.LenStackBuf) + l := runtime.Stack(buf, false) + log.Error("%v: %s", r, buf[:l]) + } else { + log.Error("%v", r) + } + } + }() + m.mi.OnDestroy() +} + + diff --git a/module/base/ModuleManager.go b/module/base/ModuleManager.go new file mode 100644 index 0000000..2b6e924 --- /dev/null +++ b/module/base/ModuleManager.go @@ -0,0 +1,132 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 basemodule + +import ( + "fmt" + "github.com/liangdas/mqant/conf" + "github.com/liangdas/mqant/module/modules/timer" + "github.com/liangdas/mqant/log" + "github.com/liangdas/mqant/module" +) +func NewModuleManager() (m *ModuleManager) { + m = new(ModuleManager) + return +} +type ModuleManager struct { + app module.App + mods []*DefaultModule + runMods []*DefaultModule +} + +func (mer *ModuleManager) Register(mi module.Module) { + md := new(DefaultModule) + md.mi = mi + md.closeSig = make(chan bool, 1) + + mer.mods = append(mer.mods, md) +} +func (mer *ModuleManager) RegisterRunMod(mi module.Module) { + md := new(DefaultModule) + md.mi = mi + md.closeSig = make(chan bool, 1) + + mer.runMods = append(mer.runMods, md) +} + +func (mer *ModuleManager) Init(app module.App, ProcessID string) { + log.Info("This service ProcessID is [%s]", ProcessID) + mer.app = app + mer.CheckModuleSettings() //配置文件规则检查 + for i := 0; i < len(mer.mods); i++ { + for Type, modSettings := range conf.Conf.Module { + if mer.mods[i].mi.GetType() == Type { + //匹配 + for _, setting := range modSettings { + //这里可能有BUG 公网IP和局域网IP处理方式可能不一样,先不管 + if ProcessID == setting.ProcessID { + mer.runMods = append(mer.runMods, mer.mods[i]) //这里加入能够运行的组件 + mer.mods[i].settings = setting + } + } + break //跳出内部循环 + } + } + } + + for i := 0; i < len(mer.runMods); i++ { + m := mer.runMods[i] + m.mi.OnInit(app, m.settings) + m.wg.Add(1) + go run(m) + } + timer.SetTimer(3, mer.ReportStatistics, nil) //统计汇报定时任务 +} + +/** +module配置文件规则检查 +1. ID全局必须唯一 +2. 每一个类型的Module列表中ProcessID不能重复 +*/ +func (mer *ModuleManager) CheckModuleSettings() { + gid := map[string]string{} //用来保存全局ID-ModuleType + for Type, modSettings := range conf.Conf.Module { + pid := map[string]string{} //用来保存模块中的 ProcessID-ID + for _, setting := range modSettings { + if Stype, ok := gid[setting.Id]; ok { + //如果Id已经存在,说明有两个相同Id的模块,这种情况不能被允许,这里就直接抛异常 强制崩溃以免以后调试找不到问题 + panic(fmt.Sprintf("ID (%s) been used in modules of type [%s] and cannot be reused", setting.Id, Stype)) + } else { + gid[setting.Id] = Type + } + + if Id, ok := pid[setting.ProcessID]; ok { + //如果Id已经存在,说明有两个相同Id的模块,这种情况不能被允许,这里就直接抛异常 强制崩溃以免以后调试找不到问题 + panic(fmt.Sprintf("In the list of modules of type [%s], ProcessID (%s) has been used for ID module for (%s)", Type, setting.ProcessID, Id)) + } else { + pid[setting.ProcessID] = setting.Id + } + } + } +} +func (mer *ModuleManager) Destroy() { + for i := len(mer.runMods) - 1; i >= 0; i-- { + m := mer.runMods[i] + m.closeSig <- true + m.wg.Wait() + destroy(m) + } +} +func (mer *ModuleManager) ReportStatistics(args interface{}) { + if mer.app.GetSettings().Master.Enable { + for _, m := range mer.runMods { + mi := m.mi + switch value := mi.(type) { + case module.RPCModule: + //汇报统计 + servers := mer.app.GetServersByType("Master") + if len(servers) == 1 { + b, _ := value.GetStatistical() + _, err := servers[0].Call("ReportForm", value.GetType(), m.settings.ProcessID, m.settings.Id, value.Version(), b, value.GetExecuting()) + if err != "" { + log.Warning("Report To Master error :", err) + } + } + default: + } + } + timer.SetTimer(3, mer.ReportStatistics, nil) + } +} + diff --git a/module/base/ServerSession.go b/module/base/ServerSession.go new file mode 100644 index 0000000..e095ce7 --- /dev/null +++ b/module/base/ServerSession.go @@ -0,0 +1,70 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 basemodule + +import ( + "github.com/liangdas/mqant/rpc" + "github.com/liangdas/mqant/module" +) + +func NewServerSession(Id string ,Stype string,Rpc mqrpc.RPCClient) (module.ServerSession) { + session := &serverSession{ + Id: Id, + Stype: Stype, + Rpc: Rpc, + } + return session +} + +type serverSession struct { + Id string + Stype string + Rpc mqrpc.RPCClient +} +func (c *serverSession)GetId()string{ + return c.Id +} +func (c *serverSession)GetType()string{ + return c.Stype +} +func (c *serverSession)GetRpc()mqrpc.RPCClient{ + return c.Rpc +} +/** +消息请求 需要回复 +*/ +func (c *serverSession) Call(_func string, params ...interface{}) (interface{}, string) { + return c.Rpc.Call(_func, params...) +} + +/** +消息请求 需要回复 +*/ +func (c *serverSession) CallNR(_func string, params ...interface{}) (err error) { + return c.Rpc.CallNR(_func, params...) +} + +/** +消息请求 需要回复 +*/ +func (c *serverSession) CallArgs(_func string, ArgsType []string,args [][]byte) (interface{}, string) { + return c.Rpc.CallArgs(_func, ArgsType,args) +} + +/** +消息请求 需要回复 +*/ +func (c *serverSession) CallNRArgs(_func string, ArgsType []string,args [][]byte) (err error) { + return c.Rpc.CallNRArgs(_func, ArgsType,args) +} diff --git a/module/base_module.go b/module/base/base_module.go similarity index 78% rename from module/base_module.go rename to module/base/base_module.go index 99b944b..7cbca6e 100644 --- a/module/base_module.go +++ b/module/base/base_module.go @@ -11,7 +11,7 @@ // 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 module +package basemodule import ( "encoding/json" @@ -19,6 +19,8 @@ import ( "github.com/liangdas/mqant/rpc" "sync" "time" + "github.com/liangdas/mqant/rpc/pb" + "github.com/liangdas/mqant/module" ) type StatisticalMethod struct { @@ -44,10 +46,10 @@ func LoadStatisticalMethod(j string) map[string]*StatisticalMethod { } type BaseModule struct { - App App - subclass RPCModule + App module.App + subclass module.RPCModule settings *conf.ModuleSettings - server *Server + server *rpcserver listener mqrpc.RPCListener statistical map[string]*StatisticalMethod //统计 rwmutex sync.RWMutex @@ -57,14 +59,21 @@ func (m *BaseModule) GetServerId() string { //很关键,需要与配置文件中的Module配置对应 return m.settings.Id } -func (m *BaseModule) GetServer() *Server { + +func (m *BaseModule) GetApp() module.App { + return m.App +} + +func (m *BaseModule) GetServer() *rpcserver { if m.server == nil { - m.server = new(Server) + m.server = new(rpcserver) } return m.server } +func (m *BaseModule)OnConfChanged(settings *conf.ModuleSettings) { -func (m *BaseModule) OnInit(subclass RPCModule, app App, settings *conf.ModuleSettings) { +} +func (m *BaseModule) OnInit(subclass module.RPCModule, app module.App, settings *conf.ModuleSettings) { //初始化模块 m.App = app m.subclass = subclass @@ -86,7 +95,7 @@ func (m *BaseModule) SetListener(listener mqrpc.RPCListener) { func (m *BaseModule) GetModuleSettings() *conf.ModuleSettings { return m.settings } -func (m *BaseModule) GetRouteServers(moduleType string, hash string) (s *ServerSession, err error) { +func (m *BaseModule) GetRouteServers(moduleType string, hash string) (s module.ServerSession, err error) { return m.App.GetRouteServers(moduleType, hash) } @@ -107,6 +116,23 @@ func (m *BaseModule) RpcInvokeNR(moduleType string, _func string, params ...inte return server.CallNR(_func, params...) } +func (m *BaseModule) RpcInvokeArgs(moduleType string, _func string, ArgsType []string,args [][]byte) (result interface{}, err string) { + server, e := m.App.GetRouteServers(moduleType, m.subclass.GetServerId()) + if e != nil { + err = e.Error() + return + } + return server.CallArgs(_func, ArgsType,args) +} + +func (m *BaseModule) RpcInvokeNRArgs(moduleType string, _func string, ArgsType []string,args [][]byte) (err error) { + server, err := m.App.GetRouteServers(moduleType, m.subclass.GetServerId()) + if err != nil { + return + } + return server.CallNRArgs(_func, ArgsType,args) +} + func (m *BaseModule) OnTimeOut(fn string, Expired int64) { m.rwmutex.RLock() if statisticalMethod, ok := m.statistical[fn]; ok { @@ -126,7 +152,7 @@ func (m *BaseModule) OnTimeOut(fn string, Expired int64) { m.listener.OnTimeOut(fn, Expired) } } -func (m *BaseModule) OnError(fn string, params []interface{}, err error) { +func (m *BaseModule) OnError(fn string, callInfo *mqrpc.CallInfo, err error) { m.rwmutex.RLock() if statisticalMethod, ok := m.statistical[fn]; ok { statisticalMethod.ExecFailure++ @@ -142,7 +168,7 @@ func (m *BaseModule) OnError(fn string, params []interface{}, err error) { } m.rwmutex.RUnlock() if m.listener != nil { - m.listener.OnError(fn, params, err) + m.listener.OnError(fn, callInfo, err) } } @@ -152,7 +178,7 @@ params 参数 result 执行结果 exec_time 方法执行时间 单位为 Nano 纳秒 1000000纳秒等于1毫秒 */ -func (m *BaseModule) OnComplete(fn string, params []interface{}, result *mqrpc.ResultInfo, exec_time int64) { +func (m *BaseModule) OnComplete(fn string, callInfo *mqrpc.CallInfo, result *rpcpb.ResultInfo, exec_time int64) { m.rwmutex.RLock() if statisticalMethod, ok := m.statistical[fn]; ok { statisticalMethod.ExecSuccess++ @@ -176,7 +202,7 @@ func (m *BaseModule) OnComplete(fn string, params []interface{}, result *mqrpc.R } m.rwmutex.RUnlock() if m.listener != nil { - m.listener.OnComplete(fn, params, result, exec_time) + m.listener.OnComplete(fn, callInfo, result, exec_time) } } func (m *BaseModule) GetExecuting() int64 { diff --git a/module/server.go b/module/base/rpcserver.go similarity index 75% rename from module/server.go rename to module/base/rpcserver.go index b981d05..47eefe7 100644 --- a/module/server.go +++ b/module/base/rpcserver.go @@ -11,25 +11,27 @@ // 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 module +package basemodule import ( "github.com/liangdas/mqant/conf" "github.com/liangdas/mqant/log" "github.com/liangdas/mqant/rpc" + "github.com/liangdas/mqant/module" + "github.com/liangdas/mqant/rpc/base" ) -type Server struct { +type rpcserver struct { settings *conf.ModuleSettings - server *mqrpc.RPCServer + server mqrpc.RPCServer } -func (s *Server) GetId() string { +func (s *rpcserver) GetId() string { return s.settings.Id } -func (s *Server) OnInit(app App, settings *conf.ModuleSettings) { +func (s *rpcserver) OnInit(app module.App, settings *conf.ModuleSettings) { s.settings = settings - server, err := mqrpc.NewRPCServer() //默认会创建一个本地的RPC + server, err := defaultrpc.NewRPCServer(app) //默认会创建一个本地的RPC if err != nil { log.Warning("Dial: %s", err) } @@ -46,7 +48,7 @@ func (s *Server) OnInit(app App, settings *conf.ModuleSettings) { } log.Info("RPCServer init success id(%s)", s.settings.Id) } -func (s *Server) OnDestroy() { +func (s *rpcserver) OnDestroy() { if s.server != nil { err := s.server.Done() if err != nil { @@ -58,21 +60,21 @@ func (s *Server) OnDestroy() { } } -func (s *Server) Register(id string, f interface{}) { +func (s *rpcserver) Register(id string, f interface{}) { if s.server == nil { panic("invalid RPCServer") } s.server.Register(id, f) } -func (s *Server) RegisterGO(id string, f interface{}) { +func (s *rpcserver) RegisterGO(id string, f interface{}) { if s.server == nil { panic("invalid RPCServer") } s.server.RegisterGO(id, f) } -func (s *Server) GetRPCServer() *mqrpc.RPCServer { +func (s *rpcserver) GetRPCServer() mqrpc.RPCServer { if s.server == nil { panic("invalid RPCServer") } diff --git a/module/module.go b/module/module.go index ef099c8..0285a4f 100644 --- a/module/module.go +++ b/module/module.go @@ -14,63 +14,56 @@ package module import ( - "fmt" "github.com/liangdas/mqant/conf" - "github.com/liangdas/mqant/log" - "github.com/liangdas/mqant/module/modules/timer" "github.com/liangdas/mqant/rpc" - "runtime" - "sync" ) - +type ServerSession interface { + GetId()string + GetType()string + GetRpc()mqrpc.RPCClient + Call(_func string, params ...interface{}) (interface{}, string) + CallNR(_func string, params ...interface{}) (err error) + CallArgs(_func string, ArgsType []string,args [][]byte) (interface{}, string) + CallNRArgs(_func string, ArgsType []string,args [][]byte) (err error) +} type App interface { Run(debug bool, mods ...Module) error /** 当同一个类型的Module存在多个服务时,需要根据情况选择最终路由到哪一个服务去 fn: func(moduleType string,serverId string,[]*ServerSession)(*ServerSession) */ - Route(moduleType string, fn func(app App, Type string, hash string) *ServerSession) error + Route(moduleType string, fn func(app App, Type string, hash string) ServerSession) error Configure(settings conf.Config) error OnInit(settings conf.Config) error OnDestroy() error - RegisterLocalClient(serverId string, server *mqrpc.RPCServer) error - GetServersById(id string) (*ServerSession, error) + RegisterLocalClient(serverId string, server mqrpc.RPCServer) error + GetServersById(id string) (ServerSession, error) /** filter 调用者服务类型 moduleType|moduleType@moduleID Type 想要调用的服务类型 */ - GetRouteServers(filter string, hash string) (*ServerSession, error) //获取经过筛选过的服务 - GetServersByType(Type string) []*ServerSession + GetRouteServers(filter string, hash string) (ServerSession, error) //获取经过筛选过的服务 + GetServersByType(Type string) []ServerSession GetSettings() conf.Config //获取配置信息 RpcInvoke(module RPCModule, moduleType string, _func string, params ...interface{}) (interface{}, string) RpcInvokeNR(module RPCModule, moduleType string, _func string, params ...interface{}) error -} - -type ServerSession struct { - Id string - Stype string - Rpc *mqrpc.RPCClient -} -/** -消息请求 需要回复 -*/ -func (c *ServerSession) Call(_func string, params ...interface{}) (interface{}, string) { - return c.Rpc.Call(_func, params...) -} + /** + 添加一个 自定义参数序列化接口 + gate,system 关键词一被占用请使用其他名称 + */ + AddRPCSerialize(name string, Interface RPCSerialize) error -/** -消息请求 需要回复 -*/ -func (c *ServerSession) CallNR(_func string, params ...interface{}) (err error) { - return c.Rpc.CallNR(_func, params...) + GetRPCSerialize()(map[string]RPCSerialize) } type Module interface { Version() string //模块版本 GetType() string //模块类型 + OnConfChanged(settings *conf.ModuleSettings) //为以后动态服务发现做准备 OnInit(app App, settings *conf.ModuleSettings) OnDestroy() + GetApp()(App) Run(closeSig chan bool) } type RPCModule interface { @@ -78,162 +71,40 @@ type RPCModule interface { GetServerId() string //模块类型 RpcInvoke(moduleType string, _func string, params ...interface{}) (interface{}, string) RpcInvokeNR(moduleType string, _func string, params ...interface{}) error + RpcInvokeArgs(moduleType string, _func string, ArgsType []string,args [][]byte) (interface{}, string) + RpcInvokeNRArgs(moduleType string, _func string, ArgsType []string,args [][]byte) error GetModuleSettings() (settings *conf.ModuleSettings) /** filter 调用者服务类型 moduleType|moduleType@moduleID Type 想要调用的服务类型 */ - GetRouteServers(filter string, hash string) (*ServerSession, error) + GetRouteServers(filter string, hash string) (ServerSession, error) GetStatistical() (statistical string, err error) GetExecuting() int64 } -type module struct { - mi Module - settings *conf.ModuleSettings - closeSig chan bool - wg sync.WaitGroup -} - -func NewModuleManager() (m *ModuleManager) { - m = new(ModuleManager) - return -} - -type ModuleManager struct { - app App - mods []*module - runMods []*module -} - -func (mer *ModuleManager) Register(mi Module) { - md := new(module) - md.mi = mi - md.closeSig = make(chan bool, 1) - - mer.mods = append(mer.mods, md) -} -func (mer *ModuleManager) RegisterRunMod(mi Module) { - md := new(module) - md.mi = mi - md.closeSig = make(chan bool, 1) - - mer.runMods = append(mer.runMods, md) -} - -func (mer *ModuleManager) Init(app App, ProcessID string) { - log.Info("This service ProcessID is [%s]", ProcessID) - mer.app = app - mer.CheckModuleSettings() //配置文件规则检查 - for i := 0; i < len(mer.mods); i++ { - for Type, modSettings := range conf.Conf.Module { - if mer.mods[i].mi.GetType() == Type { - //匹配 - for _, setting := range modSettings { - //这里可能有BUG 公网IP和局域网IP处理方式可能不一样,先不管 - if ProcessID == setting.ProcessID { - mer.runMods = append(mer.runMods, mer.mods[i]) //这里加入能够运行的组件 - mer.mods[i].settings = setting - } - } - break //跳出内部循环 - } - } - } - - for i := 0; i < len(mer.runMods); i++ { - m := mer.runMods[i] - m.mi.OnInit(app, m.settings) - m.wg.Add(1) - go run(m) - } - timer.SetTimer(3, mer.ReportStatistics, nil) //统计汇报定时任务 -} - /** -module配置文件规则检查 -1. ID全局必须唯一 -2. 每一个类型的Module列表中ProcessID不能重复 -*/ -func (mer *ModuleManager) CheckModuleSettings() { - gid := map[string]string{} //用来保存全局ID-ModuleType - for Type, modSettings := range conf.Conf.Module { - pid := map[string]string{} //用来保存模块中的 ProcessID-ID - for _, setting := range modSettings { - if Stype, ok := gid[setting.Id]; ok { - //如果Id已经存在,说明有两个相同Id的模块,这种情况不能被允许,这里就直接抛异常 强制崩溃以免以后调试找不到问题 - panic(fmt.Sprintf("ID (%s) been used in modules of type [%s] and cannot be reused", setting.Id, Stype)) - } else { - gid[setting.Id] = Type - } - - if Id, ok := pid[setting.ProcessID]; ok { - //如果Id已经存在,说明有两个相同Id的模块,这种情况不能被允许,这里就直接抛异常 强制崩溃以免以后调试找不到问题 - panic(fmt.Sprintf("In the list of modules of type [%s], ProcessID (%s) has been used for ID module for (%s)", Type, setting.ProcessID, Id)) - } else { - pid[setting.ProcessID] = setting.Id - } - } - } -} -func (mer *ModuleManager) Destroy() { - for i := len(mer.runMods) - 1; i >= 0; i-- { - m := mer.runMods[i] - m.closeSig <- true - m.wg.Wait() - destroy(m) - } -} - -func run(m *module) { - defer func() { - if r := recover(); r != nil { - if conf.LenStackBuf > 0 { - buf := make([]byte, conf.LenStackBuf) - l := runtime.Stack(buf, false) - log.Error("%v: %s", r, buf[:l]) - } else { - log.Error("%v", r) - } - } - }() - m.mi.Run(m.closeSig) - m.wg.Done() -} - -func destroy(m *module) { - defer func() { - if r := recover(); r != nil { - if conf.LenStackBuf > 0 { - buf := make([]byte, conf.LenStackBuf) - l := runtime.Stack(buf, false) - log.Error("%v: %s", r, buf[:l]) - } else { - log.Error("%v", r) - } - } - }() - m.mi.OnDestroy() -} - -func (mer *ModuleManager) ReportStatistics(args interface{}) { - if mer.app.GetSettings().Master.Enable { - for _, m := range mer.runMods { - mi := m.mi - switch value := mi.(type) { - case RPCModule: - //汇报统计 - servers := mer.app.GetServersByType("Master") - if len(servers) == 1 { - b, _ := value.GetStatistical() - _, err := servers[0].Call("ReportForm", value.GetType(), m.settings.ProcessID, m.settings.Id, value.Version(), b, value.GetExecuting()) - if err != "" { - log.Warning("Report To Master error :", err) - } - } - default: - } - } - timer.SetTimer(3, mer.ReportStatistics, nil) - } -} +rpc 自定义参数序列化接口 + */ +type RPCSerialize interface { + /** + 序列化 结构体-->[]byte + param 需要序列化的参数值 + @return ptype 当能够序列化这个值,并且正确解析为[]byte时 返回改值正确的类型,否则返回 ""即可 + @return p 解析成功得到的数据, 如果无法解析该类型,或者解析失败 返回nil即可 + @return err 无法解析该类型,或者解析失败 返回错误信息 + */ + Serialize(param interface{})(ptype string,p []byte, err error) + /** + 反序列化 []byte-->结构体 + ptype 参数类型 与Serialize函数中ptype 对应 + b 参数的字节流 + @return param 解析成功得到的数据结构 + @return err 无法解析该类型,或者解析失败 返回错误信息 + */ + Deserialize(ptype string,b []byte)(param interface{},err error) + /** + 返回这个接口能够处理的所有类型 + */ + GetTypes()([]string) +} \ No newline at end of file diff --git a/module/master_module.go b/module/modules/master_module.go similarity index 95% rename from module/master_module.go rename to module/modules/master_module.go index 19c941a..a888dd1 100644 --- a/module/master_module.go +++ b/module/modules/master_module.go @@ -1,7 +1,7 @@ /** 一定要记得在confin.json配置这个模块的参数,否则无法使用 */ -package module +package modules import ( "encoding/json" @@ -15,9 +15,11 @@ import ( "strconv" "strings" "sync" + "github.com/liangdas/mqant/module" + "github.com/liangdas/mqant/module/base" ) -var MasterModule = func() Module { +var MasterModule = func() module.Module { master := new(Master) return master } @@ -66,18 +68,17 @@ type ModuleReport struct { Version string ProcessID string Executing int64 //当前正在执行的函数数量,暂态的,下一次上报时刷新 - ReportForm map[string]*StatisticalMethod //运行状态报表 + ReportForm map[string]*basemodule.StatisticalMethod //运行状态报表 } type Master struct { - BaseModule - app App + basemodule.BaseModule + app module.App listener net.Listener ProcessMap map[string]*master.Process ModuleReports map[string]*ModuleReport //moduleID -- ModuleReport rwmutex sync.RWMutex } - func (m *Master) GetType() string { //很关键,需要与配置文件中的Module配置对应 return "Master" @@ -86,7 +87,7 @@ func (m *Master) Version() string { return "1.0.0" } -func (m *Master) OnInit(app App, settings *conf.ModuleSettings) { +func (m *Master) OnInit(app module.App, settings *conf.ModuleSettings) { m.BaseModule.OnInit(m, app, settings) m.app = app m.ModuleReports = map[string]*ModuleReport{} @@ -360,7 +361,7 @@ func (m *Master) stopProcess(s map[string]interface{}, msg map[string]interface{ 模块汇报 */ func (m *Master) ReportForm(moduleType string, ProcessID string, Id string, Version string, statistics string, Executing int64) (result string, err string) { - sm := LoadStatisticalMethod(statistics) + sm := basemodule.LoadStatisticalMethod(statistics) if sm == nil { err = "JSON format is not correct" } diff --git a/module/timer_module.go b/module/modules/timer_module.go similarity index 83% rename from module/timer_module.go rename to module/modules/timer_module.go index f471ea5..942c995 100644 --- a/module/timer_module.go +++ b/module/modules/timer_module.go @@ -1,20 +1,21 @@ /** 一定要记得在confin.json配置这个模块的参数,否则无法使用 */ -package module +package modules import ( "github.com/liangdas/mqant/conf" "github.com/liangdas/mqant/module/modules/timer" + "github.com/liangdas/mqant/module" ) -var TimerModule = func() Module { +var TimerModule = func() module.Module { Timer := new(Timer) return Timer } type Timer struct { - Module + module.Module } func (m *Timer) GetType() string { @@ -22,7 +23,7 @@ func (m *Timer) GetType() string { return "Timer" } -func (m *Timer) OnInit(app App, settings *conf.ModuleSettings) { +func (m *Timer) OnInit(app module.App, settings *conf.ModuleSettings) { // 定时器2,不传参数 //SetTimer("callback2", 100, m.callback2, time.Now().UnixNano()) } diff --git a/mqant.go b/mqant.go index f4b776e..5f5e1b3 100644 --- a/mqant.go +++ b/mqant.go @@ -12,12 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. package mqant - -import ( - "github.com/liangdas/mqant/app" - "github.com/liangdas/mqant/module" -) - +import "github.com/liangdas/mqant/module" +import "github.com/liangdas/mqant/app" func CreateApp() module.App { - return app.NewApp(Version) + return defaultApp.NewApp(Version) } diff --git a/network/tcp_server.go b/network/tcp_server.go index 4e64287..4846b6a 100644 --- a/network/tcp_server.go +++ b/network/tcp_server.go @@ -48,7 +48,7 @@ func (server *TCPServer) init() { } if server.MaxConnNum <= 0 { - server.MaxConnNum = 100 + server.MaxConnNum = 10000 log.Warning("invalid MaxConnNum, reset to %v", server.MaxConnNum) } if server.NewAgent == nil { diff --git a/network/ws_server.go b/network/ws_server.go index 1e2c67b..8db3f98 100644 --- a/network/ws_server.go +++ b/network/ws_server.go @@ -95,7 +95,7 @@ func (server *WSServer) Start() { } if server.MaxConnNum <= 0 { - server.MaxConnNum = 100 + server.MaxConnNum = 10000 log.Warning("invalid MaxConnNum, reset to %v", server.MaxConnNum) } if server.MaxMsgLen <= 0 { diff --git a/rpc/amqp_client.go b/rpc/base/amqp_client.go similarity index 85% rename from rpc/amqp_client.go rename to rpc/base/amqp_client.go index 1355ad2..aa7a8d5 100644 --- a/rpc/amqp_client.go +++ b/rpc/base/amqp_client.go @@ -11,18 +11,21 @@ // 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 mqrpc +package defaultrpc import ( - "encoding/json" "fmt" "github.com/liangdas/mqant/conf" "github.com/liangdas/mqant/log" "github.com/liangdas/mqant/module/modules/timer" + "github.com/golang/protobuf/proto" "github.com/liangdas/mqant/utils" "github.com/streadway/amqp" "sync" "time" + "github.com/liangdas/mqant/rpc/pb" + "github.com/liangdas/mqant/rpc" + "github.com/liangdas/mqant/rpc/util" ) type AMQPClient struct { @@ -36,7 +39,7 @@ type AMQPClient struct { type ClinetCallInfo struct { correlation_id string timeout int64 //超时 - call chan ResultInfo + call chan rpcpb.ResultInfo } func NewAMQPClient(info *conf.Rabbitmq) (client *AMQPClient, err error) { @@ -106,22 +109,22 @@ func (c *AMQPClient) Done() (err error) { /** 消息请求 */ -func (c *AMQPClient) Call(callInfo CallInfo, callback chan ResultInfo) error { +func (c *AMQPClient) Call(callInfo mqrpc.CallInfo, callback chan rpcpb.ResultInfo) error { var err error if c.callinfos == nil { return fmt.Errorf("AMQPClient is closed") } - callInfo.ReplyTo=c.Consumer.callback_queue - var correlation_id = callInfo.Cid + callInfo.RpcInfo.ReplyTo=c.Consumer.callback_queue + var correlation_id = callInfo.RpcInfo.Cid clinetCallInfo := &ClinetCallInfo{ correlation_id: correlation_id, call: callback, - timeout: callInfo.Expired, + timeout: callInfo.RpcInfo.Expired, } c.callinfos.Set(correlation_id, *clinetCallInfo) - body, err := c.Marshal(&callInfo) + body, err := c.Marshal(&callInfo.RpcInfo) if err != nil { return err } @@ -149,10 +152,10 @@ func (c *AMQPClient) Call(callInfo CallInfo, callback chan ResultInfo) error { /** 消息请求 不需要回复 */ -func (c *AMQPClient) CallNR(callInfo CallInfo) error { +func (c *AMQPClient) CallNR(callInfo mqrpc.CallInfo) error { var err error - body, err := c.Marshal(&callInfo) + body, err := c.Marshal(&callInfo.RpcInfo) if err != nil { return err } @@ -185,9 +188,10 @@ func (c *AMQPClient) on_timeout_handle(args interface{}) { var clinetCallInfo = clinetCallInfo.(ClinetCallInfo) if clinetCallInfo.timeout < (time.Now().UnixNano() / 1000000) { //已经超时了 - resultInfo := &ResultInfo{ + resultInfo := &rpcpb.ResultInfo{ Result: nil, Error: "timeout: This is Call", + ResultType:argsutil.NULL, } //发送一个超时的消息 clinetCallInfo.call <- *resultInfo @@ -243,37 +247,35 @@ func (c *AMQPClient) on_response_handle(deliveries <-chan amqp.Delivery, done ch } } -func (c *AMQPClient) UnmarshalResult(data []byte) (*ResultInfo, error) { +func (c *AMQPClient) UnmarshalResult(data []byte) (*rpcpb.ResultInfo, error) { //fmt.Println(msg) //保存解码后的数据,Value可以为任意数据类型 - var resultInfo ResultInfo - err := json.Unmarshal(data, &resultInfo) + var resultInfo rpcpb.ResultInfo + err := proto.Unmarshal(data, &resultInfo) if err != nil { return nil, err } else { return &resultInfo, err } - - panic("bug") } -func (c *AMQPClient) Unmarshal(data []byte) (*CallInfo, error) { +func (c *AMQPClient) Unmarshal(data []byte) (*rpcpb.RPCInfo, error) { //fmt.Println(msg) //保存解码后的数据,Value可以为任意数据类型 - var callInfo CallInfo - err := json.Unmarshal(data, &callInfo) + var rpcInfo rpcpb.RPCInfo + err := proto.Unmarshal(data, &rpcInfo) if err != nil { return nil, err } else { - return &callInfo, err + return &rpcInfo, err } panic("bug") } // goroutine safe -func (c *AMQPClient) Marshal(callInfo *CallInfo) ([]byte, error) { +func (c *AMQPClient) Marshal(rpcInfo *rpcpb.RPCInfo) ([]byte, error) { //map2:= structs.Map(callInfo) - b, err := json.Marshal(callInfo) + b, err := proto.Marshal(rpcInfo) return b, err } diff --git a/rpc/amqp_server.go b/rpc/base/amqp_server.go similarity index 77% rename from rpc/amqp_server.go rename to rpc/base/amqp_server.go index 654dafc..c1eb6f7 100644 --- a/rpc/amqp_server.go +++ b/rpc/base/amqp_server.go @@ -11,23 +11,26 @@ // 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 mqrpc +package defaultrpc import ( - "encoding/json" "fmt" "github.com/liangdas/mqant/conf" "github.com/liangdas/mqant/log" "github.com/streadway/amqp" + "github.com/liangdas/mqant/rpc/pb" + "github.com/golang/protobuf/proto" + "github.com/liangdas/mqant/rpc" + "runtime" ) type AMQPServer struct { - call_chan chan CallInfo + call_chan chan mqrpc.CallInfo Consumer *Consumer done chan error } -func NewAMQPServer(info *conf.Rabbitmq, call_chan chan CallInfo) (*AMQPServer, error) { +func NewAMQPServer(info *conf.Rabbitmq, call_chan chan mqrpc.CallInfo) (*AMQPServer, error) { var queueName = info.Queue var key = info.BindingKey var exchange = info.Exchange @@ -103,9 +106,9 @@ func (s *AMQPServer) Shutdown() error { return s.Consumer.Shutdown() } -func (s *AMQPServer) Callback(callinfo CallInfo) error { +func (s *AMQPServer) Callback(callinfo mqrpc.CallInfo) error { body, _ := s.MarshalResult(callinfo.Result) - return s.response(callinfo.props, body) + return s.response(callinfo.Props, body) } /** @@ -138,6 +141,22 @@ func (s *AMQPServer) response(props map[string]interface{}, body []byte) error { 接收请求信息 */ func (s *AMQPServer) on_request_handle(deliveries <-chan amqp.Delivery, done chan error) { + defer func() { + if r := recover(); r != nil { + var rn = "" + switch r.(type) { + + case string: + rn = r.(string) + case error: + rn = r.(error).Error() + } + buf := make([]byte, 1024) + l := runtime.Stack(buf, false) + errstr := string(buf[:l]) + log.Error("%s\n ----Stack----\n%s",rn,errstr) + } + }() for { select { case d, ok := <-deliveries: @@ -151,13 +170,16 @@ func (s *AMQPServer) on_request_handle(deliveries <-chan amqp.Delivery, done cha // d.Body, //) d.Ack(false) - callInfo, err := s.Unmarshal(d.Body) + rpcInfo, err := s.Unmarshal(d.Body) if err == nil { - callInfo.props = map[string]interface{}{ - "reply_to": callInfo.ReplyTo, + callInfo:=&mqrpc.CallInfo{ + RpcInfo:*rpcInfo, + } + callInfo.Props = map[string]interface{}{ + "reply_to": callInfo.RpcInfo.ReplyTo, } - callInfo.agent = s //设置代理为AMQPServer + callInfo.Agent = s //设置代理为AMQPServer s.call_chan <- *callInfo } else { @@ -175,29 +197,23 @@ func (s *AMQPServer) on_request_handle(deliveries <-chan amqp.Delivery, done cha } } -func (s *AMQPServer) Unmarshal(data []byte) (*CallInfo, error) { +func (s *AMQPServer) Unmarshal(data []byte) (*rpcpb.RPCInfo, error) { //fmt.Println(msg) //保存解码后的数据,Value可以为任意数据类型 - var callInfo CallInfo - err := json.Unmarshal(data, &callInfo) + var rpcInfo rpcpb.RPCInfo + err := proto.Unmarshal(data, &rpcInfo) if err != nil { return nil, err } else { - return &callInfo, err + return &rpcInfo, err } panic("bug") } - -// goroutine safe -func (s *AMQPServer) Marshal(callInfo *CallInfo) ([]byte, error) { - b, err := json.Marshal(callInfo) - return b, err -} // goroutine safe -func (s *AMQPServer) MarshalResult(resultInfo ResultInfo) ([]byte, error) { +func (s *AMQPServer) MarshalResult(resultInfo rpcpb.ResultInfo) ([]byte, error) { //log.Error("",map2) - b, err := json.Marshal(resultInfo) + b, err := proto.Marshal(&resultInfo) return b, err } diff --git a/rpc/local_client.go b/rpc/base/local_client.go similarity index 82% rename from rpc/local_client.go rename to rpc/base/local_client.go index 117fcee..cb061b0 100644 --- a/rpc/local_client.go +++ b/rpc/base/local_client.go @@ -11,7 +11,7 @@ // 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 mqrpc +package defaultrpc import ( "fmt" @@ -19,23 +19,25 @@ import ( "github.com/liangdas/mqant/utils" "sync" "time" + "github.com/liangdas/mqant/rpc/pb" + "github.com/liangdas/mqant/rpc" ) type LocalClient struct { //callinfos map[string]*ClinetCallInfo callinfos *utils.BeeMap cmutex sync.Mutex //操作callinfos的锁 - local_server *LocalServer - result_chan chan ResultInfo + local_server mqrpc.LocalServer + result_chan chan rpcpb.ResultInfo done chan error } -func NewLocalClient(server *LocalServer) (*LocalClient, error) { +func NewLocalClient(server mqrpc.LocalServer) (*LocalClient, error) { client := new(LocalClient) client.callinfos = utils.NewBeeMap() client.local_server = server client.done = make(chan error) - client.result_chan = make(chan ResultInfo) + client.result_chan = make(chan rpcpb.ResultInfo,50) go client.on_response_handle(client.result_chan, client.done) client.on_timeout_handle(nil) //处理超时请求的协程 return client, nil @@ -64,7 +66,7 @@ func (c *LocalClient) Done() error { /** 消息请求 */ -func (c *LocalClient) Call(callInfo CallInfo, callback chan ResultInfo) (err error) { +func (c *LocalClient) Call(callInfo mqrpc.CallInfo, callback chan rpcpb.ResultInfo) (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf(r.(string)) @@ -75,15 +77,15 @@ func (c *LocalClient) Call(callInfo CallInfo, callback chan ResultInfo) (err err return fmt.Errorf("MQClient is closed") } - var correlation_id = callInfo.Cid + var correlation_id = callInfo.RpcInfo.Cid clinetCallInfo := &ClinetCallInfo{ correlation_id: correlation_id, call: callback, - timeout: callInfo.Expired, + timeout: callInfo.RpcInfo.Expired, } c.callinfos.Set(correlation_id, *clinetCallInfo) - callInfo.props = map[string](interface{}){ + callInfo.Props = map[string]interface{}{ "reply_to": c.result_chan, } //发送消息 @@ -95,7 +97,7 @@ func (c *LocalClient) Call(callInfo CallInfo, callback chan ResultInfo) (err err /** 消息请求 不需要回复 */ -func (c *LocalClient) CallNR(callInfo CallInfo) (err error) { +func (c *LocalClient) CallNR(callInfo mqrpc.CallInfo) (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf(r.(string)) @@ -115,7 +117,7 @@ func (c *LocalClient) on_timeout_handle(args interface{}) { var clinetCallInfo = clinetCallInfo.(ClinetCallInfo) if clinetCallInfo.timeout < (time.Now().UnixNano() / 1000000) { //已经超时了 - resultInfo := &ResultInfo{ + resultInfo := &rpcpb.ResultInfo{ Result: nil, Error: "timeout: This is Call", } @@ -136,7 +138,7 @@ func (c *LocalClient) on_timeout_handle(args interface{}) { /** 接收应答信息 */ -func (c *LocalClient) on_response_handle(deliveries <-chan ResultInfo, done chan error) { +func (c *LocalClient) on_response_handle(deliveries <-chan rpcpb.ResultInfo, done chan error) { for { select { case resultInfo, ok := <-deliveries: diff --git a/rpc/local_server.go b/rpc/base/local_server.go similarity index 74% rename from rpc/local_server.go rename to rpc/base/local_server.go index 7bb82b3..82906f3 100644 --- a/rpc/local_server.go +++ b/rpc/base/local_server.go @@ -11,26 +11,28 @@ // 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 mqrpc +package defaultrpc import () import ( "fmt" "sync" + "github.com/liangdas/mqant/rpc/pb" + "github.com/liangdas/mqant/rpc" ) type LocalServer struct { - call_chan chan CallInfo - local_chan chan CallInfo + call_chan chan mqrpc.CallInfo + local_chan chan mqrpc.CallInfo done chan error isclose bool lock *sync.Mutex } -func NewLocalServer(call_chan chan CallInfo) (*LocalServer, error) { +func NewLocalServer(call_chan chan mqrpc.CallInfo) (*LocalServer, error) { server := new(LocalServer) server.call_chan = call_chan - server.local_chan = make(chan CallInfo, 5) + server.local_chan = make(chan mqrpc.CallInfo, 50) server.isclose = false server.lock = new(sync.Mutex) go server.on_request_handle(server.local_chan) @@ -47,7 +49,7 @@ func (s *LocalServer) IsClose() bool { /** 停止接收请求 */ -func (s *LocalServer) Write(callInfo CallInfo) error { +func (s *LocalServer) Write(callInfo mqrpc.CallInfo) error { if s.isclose { return fmt.Errorf("LocalServer is closed") } @@ -81,8 +83,8 @@ func (s *LocalServer) Shutdown() (err error) { /** */ -func (s *LocalServer) Callback(callinfo CallInfo) error { - reply_to := callinfo.props["reply_to"].(chan ResultInfo) +func (s *LocalServer) Callback(callinfo mqrpc.CallInfo) error { + reply_to := callinfo.Props["reply_to"].(chan rpcpb.ResultInfo) reply_to <- callinfo.Result return nil } @@ -90,14 +92,14 @@ func (s *LocalServer) Callback(callinfo CallInfo) error { /** 接收请求信息 */ -func (s *LocalServer) on_request_handle(local_chan <-chan CallInfo) { +func (s *LocalServer) on_request_handle(local_chan <-chan mqrpc.CallInfo) { for { select { case callInfo, ok := <-local_chan: if !ok { local_chan = nil } else { - callInfo.agent = s //设置代理为LocalServer + callInfo.Agent = s //设置代理为LocalServer s.call_chan <- callInfo } } diff --git a/rpc/rabbit.go b/rpc/base/rabbit.go similarity index 99% rename from rpc/rabbit.go rename to rpc/base/rabbit.go index d52fba7..32bfae3 100644 --- a/rpc/rabbit.go +++ b/rpc/base/rabbit.go @@ -11,7 +11,7 @@ // 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 mqrpc +package defaultrpc import ( "fmt" diff --git a/rpc/base/rpc_client.go b/rpc/base/rpc_client.go new file mode 100644 index 0000000..f149cd4 --- /dev/null +++ b/rpc/base/rpc_client.go @@ -0,0 +1,175 @@ +// Copyright 2014 mqant Author. All Rights Reserved. +// +// 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 defaultrpc + +import ( + "time" + "fmt" + "github.com/liangdas/mqant/conf" + "github.com/liangdas/mqant/log" + "github.com/liangdas/mqant/utils/uuid" + "github.com/liangdas/mqant/rpc/pb" + "github.com/golang/protobuf/proto" + "github.com/liangdas/mqant/rpc/util" + "github.com/liangdas/mqant/rpc" + "github.com/liangdas/mqant/module" +) + +type RPCClient struct { + app module.App + serverId string + remote_client *AMQPClient + local_client *LocalClient +} + +func NewRPCClient(app module.App,serverId string) (mqrpc.RPCClient, error) { + rpc_client := new(RPCClient) + rpc_client.serverId=serverId + rpc_client.app=app + return rpc_client, nil +} + +func (c *RPCClient) NewRemoteClient(info *conf.Rabbitmq) (err error) { + //创建本地连接 + if info != nil && c.remote_client == nil { + c.remote_client, err = NewAMQPClient(info) + if err != nil { + log.Error("Dial: %s", err) + } + } + return +} + +func (c *RPCClient) NewLocalClient(server mqrpc.RPCServer) (err error) { + //创建本地连接 + if server != nil && server.GetLocalServer() != nil && c.local_client == nil { + c.local_client, err = NewLocalClient(server.GetLocalServer()) + if err != nil { + log.Error("Dial: %s", err) + } + } + return +} + +func (c *RPCClient) Done() (err error) { + if c.remote_client != nil { + err = c.remote_client.Done() + } + if c.local_client != nil { + err = c.local_client.Done() + } + return +} + +func (c *RPCClient) CallArgs(_func string, ArgsType []string,args [][]byte ) (interface{}, string) { + var correlation_id = uuid.Rand().Hex() + rpcInfo := &rpcpb.RPCInfo{ + Fn: *proto.String(_func), + Reply: *proto.Bool(true), + Expired: *proto.Int64((time.Now().UTC().Add(time.Second * time.Duration(conf.RpcExpired)).UnixNano()) / 1000000), + Cid: *proto.String(correlation_id), + Args: args, + ArgsType:ArgsType, + } + callInfo := &mqrpc.CallInfo{ + RpcInfo: *rpcInfo, + } + callback := make(chan rpcpb.ResultInfo, 1) + var err error + + + //优先使用本地rpc + if c.local_client != nil { + err = c.local_client.Call(*callInfo, callback) + } else { + if c.remote_client != nil { + err = c.remote_client.Call(*callInfo, callback) + } else { + return nil, fmt.Sprintf("rpc service (%s) connection failed",c.serverId) + } + } + + if err != nil { + return nil, err.Error() + } + + resultInfo, ok := <-callback + if !ok { + return nil, "client closed" + } + result,err:=argsutil.Bytes2Args(c.app,resultInfo.ResultType,resultInfo.Result) + return result, resultInfo.Error +} + +func (c *RPCClient) CallNRArgs(_func string, ArgsType []string,args [][]byte ) (err error) { + var correlation_id = uuid.Rand().Hex() + rpcInfo := &rpcpb.RPCInfo{ + Fn: *proto.String(_func), + Reply: *proto.Bool(false), + Expired: *proto.Int64((time.Now().UTC().Add(time.Second * time.Duration(conf.RpcExpired)).UnixNano()) / 1000000), + Cid: *proto.String(correlation_id), + Args: args, + ArgsType:ArgsType, + } + callInfo := &mqrpc.CallInfo{ + RpcInfo: *rpcInfo, + } + + //优先使用本地rpc + if c.local_client != nil { + err = c.local_client.CallNR(*callInfo) + } else { + if c.remote_client != nil { + err = c.remote_client.CallNR(*callInfo) + } else { + return fmt.Errorf("rpc service (%s) connection failed",c.serverId) + } + } + + if err != nil { + return err + } + return nil +} + +/** +消息请求 需要回复 +*/ +func (c *RPCClient) Call(_func string, params ...interface{}) (interface{}, string) { + var ArgsType []string=make([]string, len(params)) + var args [][]byte=make([][]byte, len(params)) + for k, param := range params { + var err error=nil + ArgsType[k],args[k],err=argsutil.ArgsTypeAnd2Bytes(c.app,param) + if err != nil{ + return nil, fmt.Sprintf( "args[%d] error %s",k,err.Error()) + } + } + return c.CallArgs(_func,ArgsType,args) +} + +/** +消息请求 不需要回复 +*/ +func (c *RPCClient) CallNR(_func string, params ...interface{}) (err error) { + var ArgsType []string=make([]string, len(params)) + var args [][]byte=make([][]byte, len(params)) + for k, param := range params { + ArgsType[k],args[k],err=argsutil.ArgsTypeAnd2Bytes(c.app,param) + if err != nil{ + return fmt.Errorf( "args[%d] error %s",k,err.Error()) + } + } + return c.CallNRArgs(_func,ArgsType,args) +} diff --git a/rpc/base/rpc_server.go b/rpc/base/rpc_server.go new file mode 100644 index 0000000..24317c4 --- /dev/null +++ b/rpc/base/rpc_server.go @@ -0,0 +1,310 @@ +// Copyright 2014 mqant Author. All Rights Reserved. +// +// 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 defaultrpc + +import ( + "fmt" + "github.com/liangdas/mqant/conf" + "github.com/liangdas/mqant/log" + "github.com/liangdas/mqant/rpc/pb" + "reflect" + "sync" + "time" + "runtime" + "github.com/liangdas/mqant/rpc/util" + "github.com/liangdas/mqant/module" + "github.com/liangdas/mqant/rpc" +) + + + +type RPCServer struct { + app module.App + functions map[string]mqrpc.FunctionInfo + remote_server *AMQPServer + local_server *LocalServer + mq_chan chan mqrpc.CallInfo //接收到请求信息的队列 + callback_chan chan mqrpc.CallInfo //信息处理完成的队列 + wg sync.WaitGroup //任务阻塞 + call_chan_done chan error + listener mqrpc.RPCListener + executing int64 //正在执行的goroutine数量 +} + + +func NewRPCServer(app module.App) (mqrpc.RPCServer, error) { + rpc_server := new(RPCServer) + rpc_server.app=app + rpc_server.call_chan_done = make(chan error) + rpc_server.functions = make(map[string]mqrpc.FunctionInfo) + rpc_server.mq_chan = make(chan mqrpc.CallInfo,50) + rpc_server.callback_chan = make(chan mqrpc.CallInfo,50) + + //先创建一个本地的RPC服务 + local_server, err := NewLocalServer(rpc_server.mq_chan) + if err != nil { + log.Error("LocalServer Dial: %s", err) + } + rpc_server.local_server = local_server + + go rpc_server.on_call_handle(rpc_server.mq_chan, rpc_server.callback_chan, rpc_server.call_chan_done) + + go rpc_server.on_callback_handle(rpc_server.callback_chan) //结果发送队列 + return rpc_server, nil +} + +/** +创建一个支持远程RPC的服务 +*/ +func (s *RPCServer) NewRemoteRPCServer(info *conf.Rabbitmq) (err error) { + remote_server, err := NewAMQPServer(info, s.mq_chan) + if err != nil { + log.Error("AMQPServer Dial: %s", err) + } + s.remote_server = remote_server + return +} +func (s *RPCServer) SetListener(listener mqrpc.RPCListener) { + s.listener = listener +} +func (s *RPCServer) GetLocalServer()mqrpc.LocalServer { + return s.local_server +} + +/** +获取当前正在执行的goroutine 数量 +*/ +func (s *RPCServer) GetExecuting() int64 { + return s.executing +} + +// you must call the function before calling Open and Go +func (s *RPCServer) Register(id string, f interface{}) { + + if _, ok := s.functions[id]; ok { + panic(fmt.Sprintf("function id %v: already registered", id)) + } + + s.functions[id] = *&mqrpc.FunctionInfo{ + Function: f, + Goroutine: false, + } +} + +// you must call the function before calling Open and Go +func (s *RPCServer) RegisterGO(id string, f interface{}) { + + if _, ok := s.functions[id]; ok { + panic(fmt.Sprintf("function id %v: already registered", id)) + } + + s.functions[id] = *&mqrpc.FunctionInfo{ + Function: f, + Goroutine: true, + } +} + +func (s *RPCServer) Done() (err error) { + //设置队列停止接收请求 + if s.remote_server != nil { + err = s.remote_server.StopConsume() + } + if s.local_server != nil { + err = s.local_server.StopConsume() + } + //等待正在执行的请求完成 + close(s.mq_chan) //关闭mq_chan通道 + <-s.call_chan_done //mq_chan通道的信息都已处理完 + s.wg.Wait() + close(s.callback_chan) //关闭结果发送队列 + //关闭队列链接 + if s.remote_server != nil { + err = s.remote_server.Shutdown() + } + if s.local_server != nil { + err = s.local_server.Shutdown() + } + return +} + +/** +处理结果信息 +*/ +func (s *RPCServer) on_callback_handle(callbacks <-chan mqrpc.CallInfo) { + for { + select { + case callInfo, ok := <-callbacks: + if !ok { + callbacks = nil + } else { + if callInfo.RpcInfo.Reply { + //需要回复的才回复 + callInfo.Agent.(mqrpc.MQServer).Callback(callInfo) + } + } + } + if callbacks == nil { + break + } + } +} + +/** +接收请求信息 +*/ +func (s *RPCServer) on_call_handle(calls <-chan mqrpc.CallInfo, callbacks chan<- mqrpc.CallInfo, done chan error) { + for { + select { + case callInfo, ok := <-calls: + if !ok { + calls = nil + } else { + if callInfo.RpcInfo.Expired < (time.Now().UnixNano() / 1000000) { + //请求超时了,无需再处理 + if s.listener != nil { + s.listener.OnTimeOut(callInfo.RpcInfo.Fn, callInfo.RpcInfo.Expired) + } else { + fmt.Println("timeout: This is Call", callInfo.RpcInfo.Fn, callInfo.RpcInfo.Expired, time.Now().UnixNano()/1000000) + } + } else { + s.runFunc(callInfo, callbacks) + } + } + } + if calls == nil { + done <- nil + break + } + } +} + +//---------------------------------if _func is not a function or para num and type not match,it will cause panic +func (s *RPCServer) runFunc(callInfo mqrpc.CallInfo, callbacks chan<- mqrpc.CallInfo) { + _errorCallback:= func(Cid string,Error string) { + resultInfo := rpcpb.NewResultInfo(Cid,Error,argsutil.NULL,nil) + callInfo.Result = *resultInfo + callbacks <- callInfo + + if s.listener != nil { + s.listener.OnError(callInfo.RpcInfo.Fn, &callInfo, fmt.Errorf(Error)) + } + } + defer func() { + if r := recover(); r != nil { + var rn = "" + switch r.(type) { + + case string: + rn = r.(string) + case error: + rn = r.(error).Error() + } + _errorCallback(callInfo.RpcInfo.Cid,rn) + } + }() + + functionInfo, ok := s.functions[callInfo.RpcInfo.Fn] + if !ok { + _errorCallback(callInfo.RpcInfo.Cid,fmt.Sprintf("Remote function(%s) not found", callInfo.RpcInfo.Fn)) + return + } + _func := functionInfo.Function + params := callInfo.RpcInfo.Args + ArgsType:=callInfo.RpcInfo.ArgsType + f := reflect.ValueOf(_func) + if len(params) != f.Type().NumIn() { + //因为在调研的 _func的时候还会额外传递一个回调函数 cb + _errorCallback(callInfo.RpcInfo.Cid,fmt.Sprintf("The number of params %s is not adapted.%s", params, f.String())) + return + } + //if len(params) != len(callInfo.RpcInfo.ArgsType) { + // //因为在调研的 _func的时候还会额外传递一个回调函数 cb + // _errorCallback(callInfo.RpcInfo.Cid,fmt.Sprintf("The number of params %s is not adapted ArgsType .%s", params, callInfo.RpcInfo.ArgsType)) + // return + //} + + //typ := reflect.TypeOf(_func) + var in []reflect.Value + if len(ArgsType)>0{ + in = make([]reflect.Value, len(params)) + for k,v:=range ArgsType{ + v,err:=argsutil.Bytes2Args(s.app,v,params[k]) + if err!=nil{ + _errorCallback(callInfo.RpcInfo.Cid,fmt.Sprintf("args[%d] [%s] Types not allowed",k,reflect.TypeOf(params[k]))) + return + } + in[k] = reflect.ValueOf(v) + } + } + s.wg.Add(1) + s.executing++ + _runFunc := func() { + defer func() { + if r := recover(); r != nil { + var rn = "" + switch r.(type) { + + case string: + rn = r.(string) + case error: + rn = r.(error).Error() + } + buf := make([]byte, 1024) + l := runtime.Stack(buf, false) + errstr := string(buf[:l]) + log.Error("rpc func(%s) error %s\n ----Stack----\n%s",callInfo.RpcInfo.Fn,rn,errstr) + _errorCallback(callInfo.RpcInfo.Cid,rn) + } + s.wg.Add(-1) + s.executing-- + }() + exec_time := time.Now().UnixNano() + //t:=RandInt64(2,3) + //time.Sleep(time.Second*time.Duration(t)) + // f 为函数地址 + out := f.Call(in) + var rs []interface{} + if len(out) != 2 { + _errorCallback(callInfo.RpcInfo.Cid,"The number of prepare is not adapted.") + return + } + if len(out) > 0 { //prepare out paras + rs = make([]interface{}, len(out), len(out)) + for i, v := range out { + rs[i] = v.Interface() + } + } + argsType,args,err:=argsutil.ArgsTypeAnd2Bytes(s.app,rs[0]) + if err!=nil{ + _errorCallback(callInfo.RpcInfo.Cid,err.Error()) + return + } + resultInfo :=rpcpb.NewResultInfo( + callInfo.RpcInfo.Cid, + rs[1].(string), + argsType, + args, + ) + callInfo.Result = *resultInfo + callbacks <- callInfo + if s.listener != nil { + s.listener.OnComplete(callInfo.RpcInfo.Fn, &callInfo, resultInfo, time.Now().UnixNano()-exec_time) + } + } + if functionInfo.Goroutine { + go _runFunc() + } else { + _runFunc() + } +} diff --git a/rpc/pb/rpc.pb.go b/rpc/pb/rpc.pb.go new file mode 100644 index 0000000..4997c7d --- /dev/null +++ b/rpc/pb/rpc.pb.go @@ -0,0 +1,176 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: rpc/rpc.proto + +/* +Package rpcpb is a generated protocol buffer package. + +It is generated from these files: + rpc/rpc.proto + +It has these top-level messages: + RPCInfo + ResultInfo +*/ +package rpcpb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// 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.ProtoPackageIsVersion2 // please upgrade the proto package + +type RPCInfo struct { + Cid string `protobuf:"bytes,1,opt,name=Cid" json:"Cid,omitempty"` + Fn string `protobuf:"bytes,2,opt,name=Fn" json:"Fn,omitempty"` + ReplyTo string `protobuf:"bytes,3,opt,name=ReplyTo" json:"ReplyTo,omitempty"` + Track string `protobuf:"bytes,4,opt,name=track" json:"track,omitempty"` + Expired int64 `protobuf:"varint,5,opt,name=Expired" json:"Expired,omitempty"` + Reply bool `protobuf:"varint,6,opt,name=Reply" json:"Reply,omitempty"` + ArgsType []string `protobuf:"bytes,7,rep,name=ArgsType" json:"ArgsType,omitempty"` + Args [][]byte `protobuf:"bytes,8,rep,name=Args,proto3" json:"Args,omitempty"` +} + +func (m *RPCInfo) Reset() { *m = RPCInfo{} } +func (m *RPCInfo) String() string { return proto.CompactTextString(m) } +func (*RPCInfo) ProtoMessage() {} +func (*RPCInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *RPCInfo) GetCid() string { + if m != nil { + return m.Cid + } + return "" +} + +func (m *RPCInfo) GetFn() string { + if m != nil { + return m.Fn + } + return "" +} + +func (m *RPCInfo) GetReplyTo() string { + if m != nil { + return m.ReplyTo + } + return "" +} + +func (m *RPCInfo) GetTrack() string { + if m != nil { + return m.Track + } + return "" +} + +func (m *RPCInfo) GetExpired() int64 { + if m != nil { + return m.Expired + } + return 0 +} + +func (m *RPCInfo) GetReply() bool { + if m != nil { + return m.Reply + } + return false +} + +func (m *RPCInfo) GetArgsType() []string { + if m != nil { + return m.ArgsType + } + return nil +} + +func (m *RPCInfo) GetArgs() [][]byte { + if m != nil { + return m.Args + } + return nil +} + +type ResultInfo struct { + Cid string `protobuf:"bytes,1,opt,name=Cid" json:"Cid,omitempty"` + Error string `protobuf:"bytes,2,opt,name=Error" json:"Error,omitempty"` + ResultType string `protobuf:"bytes,4,opt,name=ResultType" json:"ResultType,omitempty"` + Result []byte `protobuf:"bytes,5,opt,name=Result,proto3" json:"Result,omitempty"` +} + +func NewResultInfo(Cid string,Error string,ArgsType string,result []byte) *ResultInfo { + resultInfo:=&ResultInfo{ + Cid: *proto.String(Cid), + Error:*proto.String(Error), + ResultType:*proto.String(ArgsType), + Result:result, + } + return resultInfo +} +func (m *ResultInfo) Reset() { *m = ResultInfo{} } +func (m *ResultInfo) String() string { return proto.CompactTextString(m) } +func (*ResultInfo) ProtoMessage() {} +func (*ResultInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *ResultInfo) GetCid() string { + if m != nil { + return m.Cid + } + return "" +} + +func (m *ResultInfo) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +func (m *ResultInfo) GetResultType() string { + if m != nil { + return m.ResultType + } + return "" +} + +func (m *ResultInfo) GetResult() []byte { + if m != nil { + return m.Result + } + return nil +} + +func init() { + proto.RegisterType((*RPCInfo)(nil), "rpcpb.RPCInfo") + proto.RegisterType((*ResultInfo)(nil), "rpcpb.ResultInfo") +} + +func init() { proto.RegisterFile("rpc/rpc.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 230 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xcf, 0x4a, 0xc3, 0x40, + 0x10, 0xc6, 0xd9, 0x6c, 0xf3, 0xa7, 0x43, 0x15, 0x19, 0x8a, 0x0c, 0x1e, 0x64, 0xe9, 0x69, 0x4f, + 0x7a, 0xf0, 0x09, 0xa4, 0xb4, 0xe0, 0x4d, 0x86, 0xbe, 0x80, 0x4d, 0xa3, 0x14, 0x43, 0x76, 0x99, + 0x46, 0xb0, 0xcf, 0xe6, 0xcb, 0x49, 0x66, 0x13, 0xf1, 0xd2, 0xdb, 0xf7, 0xfb, 0x31, 0xc3, 0x7e, + 0x3b, 0x70, 0x25, 0xb1, 0x7e, 0x94, 0x58, 0x3f, 0x44, 0x09, 0x7d, 0xc0, 0x5c, 0x62, 0x1d, 0xf7, + 0xab, 0x1f, 0x03, 0x25, 0xbf, 0xae, 0x5f, 0xba, 0xf7, 0x80, 0x37, 0x60, 0xd7, 0xc7, 0x03, 0x19, + 0x67, 0xfc, 0x9c, 0x87, 0x88, 0xd7, 0x90, 0x6d, 0x3b, 0xca, 0x54, 0x64, 0xdb, 0x0e, 0x09, 0x4a, + 0x6e, 0x62, 0x7b, 0xde, 0x05, 0xb2, 0x2a, 0x27, 0xc4, 0x25, 0xe4, 0xbd, 0xbc, 0xd5, 0x9f, 0x34, + 0x53, 0x9f, 0x60, 0x98, 0xdf, 0x7c, 0xc7, 0xa3, 0x34, 0x07, 0xca, 0x9d, 0xf1, 0x96, 0x27, 0x1c, + 0xe6, 0x75, 0x95, 0x0a, 0x67, 0x7c, 0xc5, 0x09, 0xf0, 0x0e, 0xaa, 0x67, 0xf9, 0x38, 0xed, 0xce, + 0xb1, 0xa1, 0xd2, 0x59, 0x3f, 0xe7, 0x3f, 0x46, 0x84, 0xd9, 0x90, 0xa9, 0x72, 0xd6, 0x2f, 0x58, + 0xf3, 0xaa, 0x05, 0xe0, 0xe6, 0xf4, 0xd5, 0xf6, 0x17, 0xfa, 0x2f, 0x21, 0xdf, 0x88, 0x04, 0x19, + 0xbf, 0x90, 0x00, 0xef, 0xa7, 0x2d, 0x7d, 0x27, 0x15, 0xfe, 0x67, 0xf0, 0x16, 0x8a, 0x44, 0x5a, + 0x7a, 0xc1, 0x23, 0xed, 0x0b, 0xbd, 0xdc, 0xd3, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x95, + 0xb9, 0x9e, 0x4a, 0x01, 0x00, 0x00, +} diff --git a/rpc/pb/rpc.proto b/rpc/pb/rpc.proto new file mode 100644 index 0000000..fa87f4b --- /dev/null +++ b/rpc/pb/rpc.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package rpcpb; +message RPCInfo { + string Cid = 1; + string Fn = 2; + string ReplyTo = 3; + string track = 4; + int64 Expired = 5; + bool Reply = 6; + repeated string ArgsType = 7; + repeated bytes Args = 8; +} + +message ResultInfo { + string Cid = 1; + string Error = 2; + string ResultType = 4; + bytes Result = 5; +} \ No newline at end of file diff --git a/rpc/pb/rpc_test.go b/rpc/pb/rpc_test.go new file mode 100644 index 0000000..229dab9 --- /dev/null +++ b/rpc/pb/rpc_test.go @@ -0,0 +1,63 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 rpcpb +import ( + "github.com/golang/protobuf/proto" + "testing" + "time" +) +func TestRPCInfo(t *testing.T) { + rpc := &RPCInfo{ // 使用辅助函数设置域的值 + Cid: *proto.String("123457"), + Fn: *proto.String("hello"), + Expired: *proto.Int64(time.Now().UnixNano() / 1000000), + Reply: *proto.Bool(true), + ReplyTo: *proto.String("232244"), + } // 进行编码 + rpc.ArgsType=[]string{"s","s"} + rpc.Args=[][]byte{[]byte("hello"),[]byte("world")} + data, err := proto.Marshal(rpc) + if err != nil { + t.Fatalf("marshaling error: ", err) + } // 进行解码 + newRPC := &RPCInfo{} + err = proto.Unmarshal(data, newRPC) + if err != nil { + t.Fatalf("unmarshaling error: ", err) + } // 测试结果 + if rpc.ReplyTo != newRPC.GetReplyTo() { + t.Fatalf("data mismatch %q != %q", rpc.GetReplyTo(), newRPC.GetReplyTo()) + } +} + +func TestResultInfo(t *testing.T) { + result := &ResultInfo{ // 使用辅助函数设置域的值 + Cid: *proto.String("123457"), + Error: *proto.String("hello"), + ResultType: *proto.String("s"), + Result: []byte("232244"), + } // 进行编码 + data, err := proto.Marshal(result) + if err != nil { + t.Fatalf("marshaling error: ", err) + } // 进行解码 + newResult := &ResultInfo{} + err = proto.Unmarshal(data, newResult) + if err != nil { + t.Fatalf("unmarshaling error: ", err) + } // 测试结果 + if result.Cid != newResult.GetCid() { + t.Fatalf("data mismatch %q != %q", result.GetCid(), newResult.GetCid()) + } +} diff --git a/rpc/rpc.go b/rpc/rpc.go new file mode 100644 index 0000000..13bd890 --- /dev/null +++ b/rpc/rpc.go @@ -0,0 +1,80 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 mqrpc + +import ( + "github.com/liangdas/mqant/conf" + "github.com/liangdas/mqant/rpc/pb" +) + + +type FunctionInfo struct { + Function interface{} + Goroutine bool +} + +type MQServer interface { + Callback(callinfo CallInfo) error +} + +type CallInfo struct { + RpcInfo rpcpb.RPCInfo + Result rpcpb.ResultInfo + Props map[string]interface{} + Agent MQServer //代理者 AMQPServer / LocalServer 都继承 Callback(callinfo CallInfo)(error) 方法 +} +type RPCListener interface { + OnTimeOut(fn string, Expired int64) + OnError(fn string, callInfo *CallInfo, err error) + /** + fn 方法名 + params 参数 + result 执行结果 + exec_time 方法执行时间 单位为 Nano 纳秒 1000000纳秒等于1毫秒 + */ + OnComplete(fn string, callInfo *CallInfo, result *rpcpb.ResultInfo, exec_time int64) +} +type RPCServer interface { + NewRemoteRPCServer(info *conf.Rabbitmq) (err error) + SetListener(listener RPCListener) + GetExecuting() int64 + GetLocalServer() LocalServer + Register(id string, f interface{}) + RegisterGO(id string, f interface{}) + Done() (err error) +} + +type RPCClient interface { + NewRemoteClient(info *conf.Rabbitmq) (err error) + NewLocalClient(server RPCServer) (err error) + Done() (err error) + CallArgs(_func string, ArgsType []string,args [][]byte ) (interface{}, string) + CallNRArgs(_func string, ArgsType []string,args [][]byte ) (err error) + Call(_func string, params ...interface{}) (interface{}, string) + CallNR(_func string, params ...interface{}) (err error) +} + + +type LocalClient interface { + Done() error + Call(callInfo CallInfo, callback chan rpcpb.ResultInfo) (err error) + CallNR(callInfo CallInfo) (err error) +} +type LocalServer interface { + IsClose() bool + Write(callInfo CallInfo) error + StopConsume() error + Shutdown() (err error) + Callback(callinfo CallInfo) error +} \ No newline at end of file diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go deleted file mode 100644 index 901c73e..0000000 --- a/rpc/rpc_client.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2014 mqant Author. All Rights Reserved. -// -// 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 mqrpc - -import ( - "github.com/liangdas/mqant/conf" - "github.com/liangdas/mqant/log" - "github.com/liangdas/mqant/utils/uuid" - "time" - "fmt" - "reflect" - "encoding/base64" -) - -type RPCClient struct { - remote_client *AMQPClient - local_client *LocalClient -} - -func NewRPCClient() (*RPCClient, error) { - rpc_client := new(RPCClient) - return rpc_client, nil -} - -func (c *RPCClient) NewRemoteClient(info *conf.Rabbitmq) (err error) { - //创建本地连接 - if info != nil && c.remote_client == nil { - c.remote_client, err = NewAMQPClient(info) - if err != nil { - log.Error("Dial: %s", err) - } - } - return -} - -func (c *RPCClient) NewLocalClient(server *RPCServer) (err error) { - //创建本地连接 - if server != nil && server.local_server != nil && c.local_client == nil { - c.local_client, err = NewLocalClient(server.local_server) - if err != nil { - log.Error("Dial: %s", err) - } - } - return -} - -func (c *RPCClient) Done() (err error) { - if c.remote_client != nil { - err = c.remote_client.Done() - } - if c.local_client != nil { - err = c.local_client.Done() - } - return -} - -/** -消息请求 需要回复 -*/ -func (c *RPCClient) Call(_func string, params ...interface{}) (interface{}, string) { - var ArgsType []string=nil - var args []interface{}=make([]interface{}, len(params)) - if len(params) > 0 { - //prepare in paras - ArgsType = make([]string, len(params)) - for k, param := range params { - switch v2:=param.(type) { - case string: - ArgsType[k] = STRING - args[k]=params[k] - case bool: - ArgsType[k] = BOOL - args[k]=params[k] - case int: - ArgsType[k] = INT - args[k]=params[k] - case int64: - ArgsType[k] = LONG - args[k]=params[k] - case float32: - ArgsType[k] = FLOAT - args[k]=params[k] - case float64: - ArgsType[k] = DOUBLE - args[k]=params[k] - case []byte: - //这里要把[]byte转base64 - ArgsType[k] = BYTES - uEnc := base64.URLEncoding.EncodeToString(v2) - args[k]=uEnc - case map[string]interface{}: - ArgsType[k] = MAP - args[k]=params[k] - case map[string]string: - ArgsType[k] = MAP - args[k]=params[k] - default: - return nil, fmt.Sprintf( "args[%d] [%s] Types not allowed",k,reflect.TypeOf(param)) - } - - } - } - var correlation_id = uuid.Rand().Hex() - callInfo := &CallInfo{ - Fn: _func, - Args: args, - ArgsType:ArgsType, - Reply: true, //客户端是否需要结果 - Expired: (time.Now().UTC().Add(time.Second * time.Duration(conf.RpcExpired)).UnixNano()) / 1000000, //超时日期 unix 时间戳 单位/毫秒 要求服务端与客户端时间精准同步 - Cid: correlation_id, - } - - callback := make(chan ResultInfo, 1) - var err error - - //优先使用本地rpc - if c.local_client != nil { - err = c.local_client.Call(*callInfo, callback) - } else { - if c.remote_client != nil { - err = c.remote_client.Call(*callInfo, callback) - } else { - return nil, "rpc service connection failed" - } - } - - if err != nil { - return nil, err.Error() - } - - resultInfo, ok := <-callback - if !ok { - return nil, "client closed" - } - return resultInfo.Result, resultInfo.Error -} - -/** -消息请求 不需要回复 -*/ -func (c *RPCClient) CallNR(_func string, params ...interface{}) (err error) { - var ArgsType []string=nil - var args []interface{}=make([]interface{}, len(params)) - if len(params) > 0 { - //prepare in paras - ArgsType = make([]string, len(params)) - for k, param := range params { - switch v2:=param.(type) { - case string: - ArgsType[k] = STRING - args[k]=params[k] - case bool: - ArgsType[k] = BOOL - args[k]=params[k] - case int: - ArgsType[k] = INT - args[k]=params[k] - case int64: - ArgsType[k] = LONG - args[k]=params[k] - case float32: - ArgsType[k] = FLOAT - args[k]=params[k] - case float64: - ArgsType[k] = DOUBLE - args[k]=params[k] - case []byte: - //这里要把[]byte转base64 - ArgsType[k] = BYTES - uEnc := base64.URLEncoding.EncodeToString(v2) - args[k]=uEnc - case map[string]interface{}: - ArgsType[k] = MAP - args[k]=params[k] - case map[string]string: - ArgsType[k] = MAP - args[k]=params[k] - default: - return fmt.Errorf( "args[%d] [%s] Types not allowed",k,reflect.TypeOf(param)) - } - - } - } - var correlation_id = uuid.Rand().Hex() - callInfo := &CallInfo{ - Fn: _func, - Args: args, - Reply: false, //客户端是否需要结果 - Expired: (time.Now().UTC().Add(time.Second * time.Duration(conf.RpcExpired)).UnixNano()) / 1000000, //超时日期 unix 时间戳 单位/毫秒 要求服务端与客户端时间精准同步 - Cid: correlation_id, - } - - //优先使用本地rpc - if c.local_client != nil { - err = c.local_client.CallNR(*callInfo) - } else { - if c.remote_client != nil { - err = c.remote_client.CallNR(*callInfo) - } else { - return fmt.Errorf("rpc service connection failed") - } - } - - if err != nil { - return err - } - return nil -} diff --git a/rpc/rpc_server.go b/rpc/rpc_server.go deleted file mode 100644 index fcb8f77..0000000 --- a/rpc/rpc_server.go +++ /dev/null @@ -1,550 +0,0 @@ -// Copyright 2014 mqant Author. All Rights Reserved. -// -// 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 mqrpc - -import ( - "fmt" - "github.com/liangdas/mqant/conf" - "github.com/liangdas/mqant/log" - "reflect" - "runtime" - "sync" - "time" - "encoding/base64" -) -var ( - BOOL="bool" //bool - INT="int" //int - LONG="long" //long64 - FLOAT="float" //float32 - DOUBLE="double" //float64 - BYTES="bytes" //[]byte - STRING="string" //string - MAP="map" //map[string]interface{} -) -type RPCListener interface { - OnTimeOut(fn string, Expired int64) - OnError(fn string, params []interface{}, err error) - /** - fn 方法名 - params 参数 - result 执行结果 - exec_time 方法执行时间 单位为 Nano 纳秒 1000000纳秒等于1毫秒 - */ - OnComplete(fn string, params []interface{}, result *ResultInfo, exec_time int64) -} - -type CallInfo struct { - Cid string //Correlation_id - Fn string - Args []interface{} - ArgsType[]string //参数类型 - Expired int64 //超时 - Reply bool //客户端是否需要结果 - ReplyTo string - Result ResultInfo - props map[string]interface{} - agent interface{} //代理者 AMQPServer / LocalServer 都继承 Callback(callinfo CallInfo)(error) 方法 -} -type ResultInfo struct { - Cid string //Correlation_id - Error string //错误结果 如果为nil表示请求正确 - Result interface{} //结果 -} - -type FunctionInfo struct { - function interface{} - goroutine bool -} - -type MQServer interface { - Callback(callinfo CallInfo) error -} - -type RPCServer struct { - functions map[string]FunctionInfo - remote_server *AMQPServer - local_server *LocalServer - mq_chan chan CallInfo //接收到请求信息的队列 - callback_chan chan CallInfo //信息处理完成的队列 - wg sync.WaitGroup //任务阻塞 - call_chan_done chan error - listener RPCListener - executing int64 //正在执行的goroutine数量 -} - -func NewRPCServer() (*RPCServer, error) { - rpc_server := new(RPCServer) - rpc_server.call_chan_done = make(chan error) - rpc_server.functions = make(map[string]FunctionInfo) - rpc_server.mq_chan = make(chan CallInfo) - rpc_server.callback_chan = make(chan CallInfo) - - //先创建一个本地的RPC服务 - local_server, err := NewLocalServer(rpc_server.mq_chan) - if err != nil { - log.Error("LocalServer Dial: %s", err) - } - rpc_server.local_server = local_server - - go rpc_server.on_call_handle(rpc_server.mq_chan, rpc_server.callback_chan, rpc_server.call_chan_done) - - go rpc_server.on_callback_handle(rpc_server.callback_chan) //结果发送队列 - return rpc_server, nil -} - -/** -创建一个支持远程RPC的服务 -*/ -func (s *RPCServer) NewRemoteRPCServer(info *conf.Rabbitmq) (err error) { - remote_server, err := NewAMQPServer(info, s.mq_chan) - if err != nil { - log.Error("AMQPServer Dial: %s", err) - } - s.remote_server = remote_server - return -} - -func (s *RPCServer) SetListener(listener RPCListener) { - s.listener = listener -} - -/** -获取当前正在执行的goroutine 数量 -*/ -func (s *RPCServer) GetExecuting() int64 { - return s.executing -} - -// you must call the function before calling Open and Go -func (s *RPCServer) Register(id string, f interface{}) { - - if _, ok := s.functions[id]; ok { - panic(fmt.Sprintf("function id %v: already registered", id)) - } - - s.functions[id] = *&FunctionInfo{ - function: f, - goroutine: false, - } -} - -// you must call the function before calling Open and Go -func (s *RPCServer) RegisterGO(id string, f interface{}) { - - if _, ok := s.functions[id]; ok { - panic(fmt.Sprintf("function id %v: already registered", id)) - } - - s.functions[id] = *&FunctionInfo{ - function: f, - goroutine: true, - } -} - -func (s *RPCServer) Done() (err error) { - //设置队列停止接收请求 - if s.remote_server != nil { - err = s.remote_server.StopConsume() - } - if s.local_server != nil { - err = s.local_server.StopConsume() - } - //等待正在执行的请求完成 - close(s.mq_chan) //关闭mq_chan通道 - <-s.call_chan_done //mq_chan通道的信息都已处理完 - s.wg.Wait() - close(s.callback_chan) //关闭结果发送队列 - //关闭队列链接 - if s.remote_server != nil { - err = s.remote_server.Shutdown() - } - if s.local_server != nil { - err = s.local_server.Shutdown() - } - return -} - -/** -处理结果信息 -*/ -func (s *RPCServer) on_callback_handle(callbacks <-chan CallInfo) { - for { - select { - case callInfo, ok := <-callbacks: - if !ok { - callbacks = nil - } else { - if callInfo.Reply { - //需要回复的才回复 - callInfo.agent.(MQServer).Callback(callInfo) - } - } - } - if callbacks == nil { - break - } - } -} - -/** -接收请求信息 -*/ -func (s *RPCServer) on_call_handle(calls <-chan CallInfo, callbacks chan<- CallInfo, done chan error) { - for { - select { - case callInfo, ok := <-calls: - if !ok { - calls = nil - } else { - if callInfo.Expired < (time.Now().UnixNano() / 1000000) { - //请求超时了,无需再处理 - if s.listener != nil { - s.listener.OnTimeOut(callInfo.Fn, callInfo.Expired) - } else { - fmt.Println("timeout: This is Call", callInfo.Fn, callInfo.Expired, time.Now().UnixNano()/1000000) - } - } else { - s.runFunc(callInfo, callbacks) - } - } - } - if calls == nil { - done <- nil - break - } - } -} - -//---------------------------------if _func is not a function or para num and type not match,it will cause panic -func (s *RPCServer) runFunc(callInfo CallInfo, callbacks chan<- CallInfo) { - defer func() { - if r := recover(); r != nil { - var lerr error - var rn = "" - switch r.(type) { - - case string: - rn = r.(string) - lerr = fmt.Errorf(rn) - case error: - rn = r.(error).Error() - lerr = r.(error) - } - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: rn, - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - - if s.listener != nil { - s.listener.OnError(callInfo.Fn, callInfo.Args, lerr) - } - } - }() - - functionInfo, ok := s.functions[callInfo.Fn] - if !ok { - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: fmt.Sprintf("Remote function(%s) not found", callInfo.Fn), - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - if s.listener != nil { - s.listener.OnError(callInfo.Fn, callInfo.Args, fmt.Errorf("function not found")) - } - return - } - _func := functionInfo.function - params := callInfo.Args - ArgsType:=callInfo.ArgsType - f := reflect.ValueOf(_func) - if len(params) != f.Type().NumIn() { - //因为在调研的 _func的时候还会额外传递一个回调函数 cb - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: fmt.Sprintf("The number of params %s is not adapted.%s", params, f.String()), - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - if s.listener != nil { - s.listener.OnError(callInfo.Fn, callInfo.Args, fmt.Errorf("The number of params %s is not adapted.%s", params, f.String())) - } - return - } - if len(params) != len(callInfo.ArgsType) { - //因为在调研的 _func的时候还会额外传递一个回调函数 cb - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: fmt.Sprintf("The number of params %s is not adapted ArgsType .%s", params, callInfo.ArgsType), - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - if s.listener != nil { - s.listener.OnError(callInfo.Fn, callInfo.Args, fmt.Errorf("The number of params %s is not adapted ArgsType .%s", params, callInfo.ArgsType)) - } - return - } - - //typ := reflect.TypeOf(_func) - var in []reflect.Value - if len(ArgsType)>0{ - in = make([]reflect.Value, len(params)) - for k, Type := range ArgsType { - switch Type { - case STRING: - //params[k] = string(params[k]) - case BOOL: - //params[k]= bool(params[k]) - case INT: - if value, ok := params[k].(uint64); ok { - params[k]=int(value) - }else if value, ok := params[k].(float64); ok { - params[k]=int(value) - } - case LONG: - if value, ok := params[k].(uint64); ok { - params[k]=int64(value) - }else if value, ok := params[k].(float64); ok { - params[k]=int64(value) - } - case FLOAT: - if value, ok := params[k].(float64); ok { - params[k]=float32(value) - }else if value, ok := params[k].(float64); ok { - params[k]=float32(value) - } - case DOUBLE: - - case BYTES: - //这里要把[]byte转base64 - if base64str, ok := params[k].(string); ok { - uDec, err := base64.URLEncoding.DecodeString(base64str) - if err != nil { - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: fmt.Sprintf("args[%d] [%s] Not converted to Base64",k,reflect.TypeOf(params[k])), - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - return - } - params[k]=uDec - }else{ - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: fmt.Sprintf("args[%d] [%s] Not converted to Base64 string",k,reflect.TypeOf(params[k])), - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - return - } - case MAP: - - default: - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: fmt.Sprintf("args[%d] [%s] Types not allowed",k,reflect.TypeOf(params[k])), - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - return - } - in[k] = reflect.ValueOf(params[k]) - } - } - - //if len(params) > 0 { //prepare in paras - // in = make([]reflect.Value, len(params)) - // for k, param := range params { - // switch v2 := param.(type) { - // case string: - // fmt.Println(k, "is string", v2) - // case int: - // fmt.Println(k, "is int", v2) - // case int64: - // fmt.Println(k, "is int64", v2) - // case bool: - // fmt.Println(k, "is bool", v2) - // case map[string]interface{}: - // fmt.Println(k, "is map", v2) - // decodedMapValue := map[interface{}]interface{}{} - // for key, v := range param.(map[string]interface{}) { - // decodedMapValue[key]= v - // } - // param = decodedMapValue - // default: - // fmt.Println(k, "is another type not handle yet",v2,reflect.TypeOf(param)) - // } - // in[k] = reflect.ValueOf(param) - // //field := typ.In(k) - // //if field != reflect.TypeOf(param) { - // // switch param.(type) { //多选语句switch - // // case int: - // // p := param.(int) - // // switch field.Kind().String() { //多选语句switch - // // case "int": - // // in[k] = reflect.ValueOf(int(p)) - // // case "int32": - // // in[k] = reflect.ValueOf(int32(p)) - // // case "int64": - // // in[k] = reflect.ValueOf(int64(p)) - // // case "float32": - // // in[k] = reflect.ValueOf(float32(p)) - // // case "float64": - // // in[k] = reflect.ValueOf(float64(p)) - // // } - // // case int32: - // // p := param.(int32) - // // switch field.Kind().String() { //多选语句switch - // // case "int": - // // in[k] = reflect.ValueOf(int(p)) - // // case "int32": - // // in[k] = reflect.ValueOf(int32(p)) - // // case "int64": - // // in[k] = reflect.ValueOf(int64(p)) - // // case "float32": - // // in[k] = reflect.ValueOf(float32(p)) - // // case "float64": - // // in[k] = reflect.ValueOf(float64(p)) - // // } - // // case int64: - // // p := param.(int64) - // // switch field.Kind().String() { //多选语句switch - // // case "int": - // // in[k] = reflect.ValueOf(int(p)) - // // case "int32": - // // in[k] = reflect.ValueOf(int32(p)) - // // case "int64": - // // in[k] = reflect.ValueOf(int64(p)) - // // case "float32": - // // in[k] = reflect.ValueOf(float32(p)) - // // case "float64": - // // in[k] = reflect.ValueOf(float64(p)) - // // } - // // case float32: - // // p := param.(float32) - // // switch field.Kind().String() { //多选语句switch - // // case "int": - // // in[k] = reflect.ValueOf(int(p)) - // // case "int32": - // // in[k] = reflect.ValueOf(int32(p)) - // // case "int64": - // // in[k] = reflect.ValueOf(int64(p)) - // // case "float32": - // // in[k] = reflect.ValueOf(float32(p)) - // // case "float64": - // // in[k] = reflect.ValueOf(float64(p)) - // // } - // // case float64: - // // p := param.(float64) - // // switch field.Kind().String() { //多选语句switch - // // case "int": - // // in[k] = reflect.ValueOf(int(p)) - // // case "int32": - // // in[k] = reflect.ValueOf(int32(p)) - // // case "int64": - // // in[k] = reflect.ValueOf(int64(p)) - // // case "float32": - // // in[k] = reflect.ValueOf(float32(p)) - // // case "float64": - // // in[k] = reflect.ValueOf(float64(p)) - // // } - // // } - // // - // //} else { - // // in[k] = reflect.ValueOf(param) - // //} - // } - //} - s.wg.Add(1) - s.executing++ - _runFunc := func() { - defer func() { - if r := recover(); r != nil { - var rn = "" - switch r.(type) { - - case string: - rn = r.(string) - case error: - rn = r.(error).Error() - } - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: rn, - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - if s.listener != nil { - buf := make([]byte, 1024) - l := runtime.Stack(buf, false) - errstr := string(buf[:l]) - log.Error(callInfo.Fn,reflect.TypeOf(_func).String(),rn,errstr) - s.listener.OnError(callInfo.Fn, callInfo.Args, fmt.Errorf(callInfo.Fn,reflect.TypeOf(_func).String(),rn,errstr)) - } - } - s.wg.Add(-1) - s.executing-- - }() - exec_time := time.Now().UnixNano() - //t:=RandInt64(2,3) - //time.Sleep(time.Second*time.Duration(t)) - // f 为函数地址 - out := f.Call(in) - var rs []interface{} - if len(out) != 2 { - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: "The number of prepare is not adapted.", - Result: nil, - } - callInfo.Result = *resultInfo - callbacks <- callInfo - return - } - if len(out) > 0 { //prepare out paras - rs = make([]interface{}, len(out), len(out)) - for i, v := range out { - rs[i] = v.Interface() - } - } - resultInfo := &ResultInfo{ - Cid: callInfo.Cid, - Error: rs[1].(string), - Result: rs[0], - } - callInfo.Result = *resultInfo - callbacks <- callInfo - if s.listener != nil { - s.listener.OnComplete(callInfo.Fn, callInfo.Args, resultInfo, time.Now().UnixNano()-exec_time) - } - } - - if functionInfo.goroutine { - go _runFunc() - } else { - _runFunc() - } -} diff --git a/rpc/util/argsutil.go b/rpc/util/argsutil.go new file mode 100644 index 0000000..ff5ff24 --- /dev/null +++ b/rpc/util/argsutil.go @@ -0,0 +1,119 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 argsutil + +import ( + "github.com/liangdas/mqant/utils" + "fmt" + "reflect" + "github.com/liangdas/mqant/module" +) + +var ( + NULL="null" //nil null + BOOL="bool" //bool + INT="int" //int + LONG="long" //long64 + FLOAT="float" //float32 + DOUBLE="double" //float64 + BYTES="bytes" //[]byte + STRING="string" //string + MAP="map" //map[string]interface{} + MAPSTR="mapstr" //map[string]string{} +) + +func ArgsTypeAnd2Bytes(app module.App,arg interface{}) (string,[]byte,error) { + switch v2:=arg.(type) { + case nil: + return NULL,nil,nil + case string: + return STRING,[]byte(v2),nil + case bool: + return BOOL,utils.BoolToBytes(v2),nil + case int32: + return INT,utils.Int32ToBytes(v2),nil + case int64: + return LONG,utils.Int64ToBytes(v2),nil + case float32: + return FLOAT,utils.Float32ToBytes(v2),nil + case float64: + return DOUBLE,utils.Float64ToBytes(v2),nil + case []byte: + return BYTES,v2,nil + case map[string]interface{}: + bytes,err:=utils.MapToBytes(v2) + if err != nil{ + return MAP,nil,err + } + return MAP,bytes,nil + case map[string]string: + bytes,err:=utils.MapToBytesString(v2) + if err != nil{ + return MAPSTR,nil,err + } + return MAPSTR,bytes,nil + default: + for _,v:=range app.GetRPCSerialize(){ + ptype,vk,err:=v.Serialize(arg) + if err==nil{ + //解析成功了 + return ptype,vk,err + } + } + return "", nil,fmt.Errorf("args [%s] Types not allowed",reflect.TypeOf(arg)) + } +} + +func Bytes2Args(app module.App,argsType string,args []byte )(interface{},error){ + switch argsType { + case NULL: + return nil,nil + case STRING: + return string(args),nil + case BOOL: + return utils.BytesToBool(args),nil + case INT: + return utils.BytesToInt32(args),nil + case LONG: + return utils.BytesToInt64(args),nil + case FLOAT: + return utils.BytesToFloat32(args),nil + case DOUBLE: + return utils.BytesToFloat64(args),nil + case BYTES: + return args,nil + case MAP: + mps,errs:= utils.BytesToMap(args) + if errs!=nil{ + return nil,errs + } + return mps,nil + case MAPSTR: + mps,errs:= utils.BytesToMapString(args) + if errs!=nil{ + return nil,errs + } + return mps,nil + default: + for _,v:=range app.GetRPCSerialize(){ + vk,err:=v.Deserialize(argsType,args) + if err==nil{ + //解析成功了 + return vk,err + } + } + return nil,fmt.Errorf("args [%s] Types not allowed",argsType) + } +} + diff --git a/utils/fatih/structs/.travis.yml b/utils/fatih/structs/.travis.yml index cbf2ccc..9e68f17 100644 --- a/utils/fatih/structs/.travis.yml +++ b/utils/fatih/structs/.travis.yml @@ -6,6 +6,6 @@ sudo: false before_install: - go get github.com/axw/gocov/gocov - go get github.com/mattn/goveralls -- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi +- if ! go get github.com/golang/tools/cmd/cover; then go get github.com/liangdas/mqant/utils/x/tools/cmd/cover; fi script: - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/utils/params_bytes.go b/utils/params_bytes.go new file mode 100644 index 0000000..42016a8 --- /dev/null +++ b/utils/params_bytes.go @@ -0,0 +1,108 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 utils +import ( + "encoding/binary" + "math" + "encoding/json" +) + +func BoolToBytes(v bool) []byte { + var buf = make([]byte, 1) + if v { + buf[0] = 1 + } else { + buf[0] = 0 + } + return buf +} + +func BytesToBool(buf []byte) bool { + var data bool= buf[0] != 0 + return data +} + +func Int32ToBytes(i int32) []byte { + var buf = make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(i)) + return buf +} + +func BytesToInt32(buf []byte) int32 { + return int32(binary.BigEndian.Uint32(buf)) +} + +func Int64ToBytes(i int64) []byte { + var buf = make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(i)) + return buf +} + +func BytesToInt64(buf []byte) int64 { + return int64(binary.BigEndian.Uint64(buf)) +} + +func Float32ToBytes(float float32) []byte { + bits := math.Float32bits(float) + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, bits) + + return bytes +} + +func BytesToFloat32(bytes []byte) float32 { + bits := binary.LittleEndian.Uint32(bytes) + + return math.Float32frombits(bits) +} + +func Float64ToBytes(float float64) []byte { + bits := math.Float64bits(float) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + + return bytes +} + +func BytesToFloat64(bytes []byte) float64 { + bits := binary.LittleEndian.Uint64(bytes) + + return math.Float64frombits(bits) +} + + +func MapToBytes(jmap map[string]interface{}) ([]byte,error) { + bytes,err:=json.Marshal(jmap) + return bytes,err +} + +func BytesToMap(bytes []byte) (map[string]interface{},error){ + v:=make(map[string]interface{}) + err:=json.Unmarshal(bytes,&v) + + return v,err +} + +func MapToBytesString(jmap map[string]string) ([]byte,error) { + bytes,err:=json.Marshal(jmap) + return bytes,err +} + +func BytesToMapString(bytes []byte) (map[string]string,error){ + v:=make(map[string]string) + err:=json.Unmarshal(bytes,&v) + + return v,err +} + diff --git a/utils/params_bytes_test.go b/utils/params_bytes_test.go new file mode 100644 index 0000000..b0d0097 --- /dev/null +++ b/utils/params_bytes_test.go @@ -0,0 +1,55 @@ +// Copyright 2014 loolgame Author. All Rights Reserved. +// +// 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 utils +import ( + "testing" +) +func assertEqual(t *testing.T,val interface{}, exp interface{}) { + if val != exp { + t.Errorf("Expected %v, got %v.", exp, val) + } +} +func TestBoolToBytes(t *testing.T) { + buf:=BoolToBytes(true) + v:=BytesToBool(buf) + assertEqual(t,v,true) +} + +func TestInt32ToBytes(t *testing.T) { + n:=int32(64) + buf:=Int32ToBytes(n) + v:=BytesToInt32(buf) + assertEqual(t,v,n) +} + +func TestInt64ToBytes(t *testing.T) { + n:=int64(64) + buf:=Int64ToBytes(n) + v:=BytesToInt64(buf) + assertEqual(t,v,n) +} + +func TestFloat32ToByte(t *testing.T) { + n:=float32(64.043) + buf:=Float32ToBytes(n) + v:=BytesToFloat32(buf) + assertEqual(t,v,n) +} + +func TestFloat64ToByte(t *testing.T) { + n:=float64(64.043) + buf:=Float64ToBytes(n) + v:=BytesToFloat64(buf) + assertEqual(t,v,n) +} \ No newline at end of file diff --git a/utils/safemap.go b/utils/safemap.go index 0b12be7..3b3ff5c 100644 --- a/utils/safemap.go +++ b/utils/safemap.go @@ -35,10 +35,11 @@ func NewBeeMap() *BeeMap { // Get from maps return the k's value func (m *BeeMap) Get(k interface{}) interface{} { m.lock.RLock() - defer m.lock.RUnlock() if val, ok := m.bm[k]; ok { + m.lock.RUnlock() return val } + m.lock.RUnlock() return nil } @@ -46,12 +47,14 @@ func (m *BeeMap) Get(k interface{}) interface{} { // if the key is already in the map and changes nothing. func (m *BeeMap) Set(k interface{}, v interface{}) bool { m.lock.Lock() - defer m.lock.Unlock() if val, ok := m.bm[k]; !ok { m.bm[k] = v + m.lock.Unlock() } else if val != v { m.bm[k] = v + m.lock.Unlock() } else { + m.lock.Unlock() return false } return true @@ -60,27 +63,28 @@ func (m *BeeMap) Set(k interface{}, v interface{}) bool { // Check Returns true if k is exist in the map. func (m *BeeMap) Check(k interface{}) bool { m.lock.RLock() - defer m.lock.RUnlock() if _, ok := m.bm[k]; !ok { + m.lock.RUnlock() return false } + m.lock.RUnlock() return true } // Delete the given key and value. func (m *BeeMap) Delete(k interface{}) { m.lock.Lock() - defer m.lock.Unlock() delete(m.bm, k) + m.lock.Unlock() } // Items returns all items in safemap. func (m *BeeMap) Items() map[interface{}]interface{} { m.lock.RLock() - defer m.lock.RUnlock() r := make(map[interface{}]interface{}) for k, v := range m.bm { r[k] = v } + m.lock.RUnlock() return r }