427 lines
13 KiB
Go
427 lines
13 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/google/uuid"
|
|
"github.com/rs/zerolog/log"
|
|
"gitlab.33.cn/chat/im-pkg/trace"
|
|
comet "gitlab.33.cn/chat/im/api/comet/grpc"
|
|
"gitlab.33.cn/chat/im/common"
|
|
"gitlab.33.cn/chat/im/logic/auth"
|
|
"gitlab.33.cn/chat/im/logic/conf"
|
|
"gitlab.33.cn/chat/im/logic/dao"
|
|
"gitlab.33.cn/chat/im/naming"
|
|
xkey "gitlab.33.cn/chat/im/naming/balancer/key"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/resolver"
|
|
)
|
|
|
|
var ErrInvalidAuthReq = errors.New("ErrInvalidAuthReq")
|
|
var ErrInvalidAppId = errors.New("ErrInvalidAppId")
|
|
|
|
type Logic struct {
|
|
c *conf.Config
|
|
dao *dao.Dao
|
|
cometClient comet.CometClient
|
|
apps map[string]auth.Auth
|
|
}
|
|
|
|
func New(c *conf.Config) (l *Logic) {
|
|
cometClient, err := newCometClient(c)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
l = &Logic{
|
|
c: c,
|
|
dao: dao.New(c),
|
|
cometClient: cometClient,
|
|
apps: make(map[string]auth.Auth),
|
|
}
|
|
l.loadApps()
|
|
return l
|
|
}
|
|
|
|
func newCometClient(c *conf.Config) (comet.CometClient, error) {
|
|
rb := naming.NewResolver(c.Reg.RegAddrs, c.CometRPCClient.Schema)
|
|
resolver.Register(rb)
|
|
|
|
addr := fmt.Sprintf("%s:///%s", c.CometRPCClient.Schema, c.CometRPCClient.SrvName) // "schema://[authority]/service"
|
|
fmt.Println("rpc client call addr:", addr)
|
|
conn, err := common.NewGRPCConnWithKey(addr, time.Duration(c.CometRPCClient.Dial), grpc.WithUnaryInterceptor(trace.OpentracingClientInterceptor))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return comet.NewCometClient(conn), err
|
|
}
|
|
|
|
func (l *Logic) loadApps() {
|
|
for _, app := range l.c.Apps {
|
|
newAuth, _ := auth.Load(app.AppId)
|
|
if newAuth == nil {
|
|
panic("exec auth not exist:" + app.AppId)
|
|
}
|
|
exec := newAuth(app.AuthUrl, time.Duration(app.Timeout))
|
|
l.apps[app.AppId] = exec
|
|
}
|
|
}
|
|
|
|
func (l *Logic) Close() {
|
|
l.dao.Close()
|
|
}
|
|
|
|
// Connect connected a conn.
|
|
func (l *Logic) Connect(c context.Context, server string, p *comet.Proto) (mid string, appId string, key string, hb int64, err error) {
|
|
var (
|
|
authMsg comet.AuthMsg
|
|
bytes []byte
|
|
)
|
|
log.Info().Str("appId", authMsg.AppId).Bytes("body", p.Body).Str("token", authMsg.Token).Msg("call Connect")
|
|
err = proto.Unmarshal(p.Body, &authMsg)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if authMsg.AppId == "" || authMsg.Token == "" {
|
|
err = ErrInvalidAuthReq
|
|
return
|
|
}
|
|
log.Info().Str("appId", authMsg.AppId).Str("token", authMsg.Token).Msg("call auth")
|
|
appId = authMsg.AppId
|
|
authExec, _ := l.apps[authMsg.AppId]
|
|
if authExec == nil {
|
|
err = ErrInvalidAppId
|
|
return
|
|
}
|
|
|
|
mid, err = authExec.DoAuth(authMsg.Token)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
hb = int64(l.c.Node.Heartbeat) * int64(l.c.Node.HeartbeatMax)
|
|
key = uuid.New().String() //连接标识
|
|
if err = l.dao.AddMapping(c, mid, appId, key, server); err != nil {
|
|
log.Error().Err(err).Msg(fmt.Sprintf("l.dao.AddMapping(%s,%s,%s) error", mid, key, server))
|
|
return
|
|
}
|
|
log.Info().Str("key", key).Str("mid", mid).Str("appId", appId).Str("comet", server).Msg("conn connected")
|
|
// notify biz user connected
|
|
bytes, err = proto.Marshal(p)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = l.dao.PublishMsg(c, appId, mid, comet.Op_Auth, key, bytes)
|
|
return
|
|
}
|
|
|
|
// Disconnect disconnect a conn.
|
|
func (l *Logic) Disconnect(c context.Context, key string, server string) (has bool, err error) {
|
|
appId, mid, err := l.dao.GetMember(c, key)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if has, err = l.dao.DelMapping(c, mid, appId, key); err != nil {
|
|
log.Error().Err(err).Msg(fmt.Sprintf("l.dao.DelMapping(%s,%s) error", mid, appId))
|
|
return
|
|
}
|
|
log.Info().Str("key", key).Str("mid", mid).Str("appId", appId).Str("comet", server).Msg("conn disconnected")
|
|
// notify biz user disconnected
|
|
l.dao.PublishMsg(c, appId, mid, comet.Op_Disconnect, key, nil)
|
|
return
|
|
}
|
|
|
|
// Heartbeat heartbeat a conn.
|
|
func (l *Logic) Heartbeat(c context.Context, key string, server string) (err error) {
|
|
appId, mid, err := l.dao.GetMember(c, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var has bool
|
|
has, err = l.dao.ExpireMapping(c, mid, appId, key)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg(fmt.Sprintf("l.dao.ExpireMapping(%s,%s) error", mid, appId))
|
|
return
|
|
}
|
|
if !has {
|
|
if err = l.dao.AddMapping(c, mid, appId, key, server); err != nil {
|
|
log.Error().Err(err).Msg(fmt.Sprintf("l.dao.AddMapping(%s,%s,%s) error", mid, appId, server))
|
|
return
|
|
}
|
|
}
|
|
log.Debug().Str("key", key).Str("mid", mid).Str("appId", appId).Str("comet", server).Msg("conn heartbeat")
|
|
return
|
|
}
|
|
|
|
// Receive receive a message from client.
|
|
func (l *Logic) Receive(c context.Context, key string, p *comet.Proto) (err error) {
|
|
appId, mid, err := l.dao.GetMember(c, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debug().Str("appId", appId).Str("mid", mid).Interface("proto", p).Msg("receive proto")
|
|
msg, err := proto.Marshal(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return l.dao.PublishMsg(c, appId, mid, comet.Op(p.Op), key, msg)
|
|
}
|
|
|
|
// PushByMids Push push a biz message to client.
|
|
func (l *Logic) PushByMids(c context.Context, appId string, toIds []string, msg []byte) (reply *comet.PushMsgReply, err error) {
|
|
log.Debug().Str("appId", appId).Strs("mids", toIds).Msg("PushByMids start")
|
|
|
|
var p comet.Proto
|
|
err = proto.Unmarshal(msg, &p)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
server2keys, err := l.getServerByMids(c, appId, toIds)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for server, keys := range server2keys {
|
|
log.Debug().Strs("keys", keys).Str("server", server).Msg("PushByMids pushing")
|
|
reply, err = l.cometClient.PushMsg(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.PushMsgReq{Keys: keys, Proto: &p})
|
|
if err != nil {
|
|
log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("PushByMids l.cometClient.PushMsg")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// PushByKeys Push push a biz message to client.
|
|
func (l *Logic) PushByKeys(c context.Context, appId string, keys []string, msg []byte) (reply *comet.PushMsgReply, err error) {
|
|
log.Debug().Str("appId", appId).Strs("keys", keys).Msg("PushByKeys start")
|
|
|
|
var p comet.Proto
|
|
err = proto.Unmarshal(msg, &p)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
server2keys, err := l.getServerByKeys(c, keys)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for server, keys := range server2keys {
|
|
log.Debug().Strs("keys", keys).Str("server", server).Msg("PushByMids pushing")
|
|
reply, err = l.cometClient.PushMsg(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.PushMsgReq{Keys: keys, Proto: &p})
|
|
if err != nil {
|
|
log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("PushByMids l.cometClient.PushMsg")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// PushGroup Push push a biz message to client.
|
|
func (l *Logic) PushGroup(c context.Context, appId string, group string, msg []byte) (reply *comet.BroadcastGroupReply, err error) {
|
|
log.Debug().Str("appId", appId).Str("group", group).Msg("PushGroup start")
|
|
var p comet.Proto
|
|
err = proto.Unmarshal(msg, &p)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
cometServers, err := l.dao.ServersByGid(c, appId, group)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, server := range cometServers {
|
|
log.Debug().Str("appId", appId).Str("group", group).Str("server", server).Msg("PushGroup pushing")
|
|
reply, err = l.cometClient.BroadcastGroup(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.BroadcastGroupReq{GroupID: appGid(appId, group), Proto: &p})
|
|
if err != nil {
|
|
log.Error().Err(err).Str("appId", appId).Str("group", group).Str("server", server).Msg("PushGroup l.cometClient.BroadcastGroup")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// JoinGroupsByMids Push push a biz message to client.
|
|
func (l *Logic) JoinGroupsByMids(c context.Context, appId string, mids []string, gids []string) (reply *comet.JoinGroupsReply, err error) {
|
|
log.Debug().Str("appId", appId).Strs("mids", mids).Strs("group", gids).Msg("JoinGroupsByMids start")
|
|
|
|
server2keys, err := l.getServerByMids(c, appId, mids)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for server, keys := range server2keys {
|
|
for _, key := range keys {
|
|
_ = l.dao.IncGroupServer(c, appId, key, server, gids)
|
|
}
|
|
|
|
log.Debug().Strs("keys", keys).Str("server", server).Msg("JoinGroupsByMids pushing")
|
|
reply, err = l.cometClient.JoinGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.JoinGroupsReq{Keys: keys, Gid: appGids(appId, gids)})
|
|
if err != nil {
|
|
log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("JoinGroupsByMids l.cometClient.JoinGroups")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// JoinGroupsByKeys Push push a biz message to client.
|
|
func (l *Logic) JoinGroupsByKeys(c context.Context, appId string, keys []string, gids []string) (reply *comet.JoinGroupsReply, err error) {
|
|
log.Debug().Str("appId", appId).Strs("keys", keys).Strs("group", gids).Msg("JoinGroupsByKeys start")
|
|
|
|
server2keys, err := l.getServerByKeys(c, keys)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for server, keys := range server2keys {
|
|
for _, key := range keys {
|
|
_ = l.dao.IncGroupServer(c, appId, key, server, gids)
|
|
}
|
|
|
|
log.Debug().Strs("keys", keys).Str("server", server).Msg("JoinGroupsByKeys pushing")
|
|
reply, err = l.cometClient.JoinGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.JoinGroupsReq{Keys: keys, Gid: appGids(appId, gids)})
|
|
if err != nil {
|
|
log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("JoinGroupsByKeys l.cometClient.JoinGroups")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// LeaveGroupsByMids Push push a biz message to client.
|
|
func (l *Logic) LeaveGroupsByMids(c context.Context, appId string, mids []string, gids []string) (reply *comet.LeaveGroupsReply, err error) {
|
|
log.Debug().Str("appId", appId).Strs("mids", mids).Strs("group", gids).Msg("LeaveGroupsByMids start")
|
|
|
|
server2keys, err := l.getServerByMids(c, appId, mids)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for server, keys := range server2keys {
|
|
for _, key := range keys {
|
|
_ = l.dao.DecGroupServer(c, appId, key, server, gids)
|
|
}
|
|
|
|
log.Debug().Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByMids pushing")
|
|
reply, err = l.cometClient.LeaveGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.LeaveGroupsReq{Keys: keys, Gid: appGids(appId, gids)})
|
|
if err != nil {
|
|
log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByMids l.cometClient.LeaveGroups")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// LeaveGroupsByKeys Push push a biz message to client.
|
|
func (l *Logic) LeaveGroupsByKeys(c context.Context, appId string, keys []string, gids []string) (reply *comet.LeaveGroupsReply, err error) {
|
|
log.Debug().Str("appId", appId).Strs("keys", keys).Strs("group", gids).Msg("LeaveGroupsByKeys start")
|
|
|
|
server2keys, err := l.getServerByKeys(c, keys)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for server, keys := range server2keys {
|
|
for _, key := range keys {
|
|
_ = l.dao.DecGroupServer(c, appId, key, server, gids)
|
|
}
|
|
|
|
log.Debug().Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByKeys pushing")
|
|
reply, err = l.cometClient.LeaveGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.LeaveGroupsReq{Keys: keys, Gid: appGids(appId, gids)})
|
|
if err != nil {
|
|
log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByKeys l.cometClient.LeaveGroups")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// DelGroups Push push a biz message to client.
|
|
func (l *Logic) DelGroups(c context.Context, appId string, gids []string) (reply *comet.DelGroupsReply, err error) {
|
|
log.Debug().Str("appId", appId).Strs("groups", gids).Msg("DelGroups start")
|
|
|
|
server2gids := make(map[string][]string)
|
|
|
|
for _, gid := range gids {
|
|
cometServers, err := l.dao.ServersByGid(c, appId, gid)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, server := range cometServers {
|
|
server2gids[server] = append(server2gids[server], gid)
|
|
}
|
|
}
|
|
|
|
for server, gids := range server2gids {
|
|
log.Debug().Str("appId", appId).Interface("gids", gids).Str("server", server).Msg("DelGroups pushing")
|
|
reply, err = l.cometClient.DelGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.DelGroupsReq{Gid: appGids(appId, gids)})
|
|
if err != nil {
|
|
log.Error().Err(err).Str("appId", appId).Strs("groups", gids).Str("server", server).Msg("DelGroups l.cometClient.DelGroups")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// getServerByMids 通过 appid 和 mids 找到所在的 comet servers
|
|
func (l *Logic) getServerByMids(c context.Context, appId string, mids []string) (map[string][]string, error) {
|
|
ress, _, err := l.dao.KeysByMids(c, appId, mids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
server2keys := make(map[string][]string)
|
|
for k, s := range ress {
|
|
server2keys[s] = append(server2keys[s], k)
|
|
}
|
|
|
|
return server2keys, nil
|
|
}
|
|
|
|
// getServerByKeys 通过 keys 找到所在的 comet servers
|
|
func (l *Logic) getServerByKeys(c context.Context, keys []string) (map[string][]string, error) {
|
|
server2keys := make(map[string][]string)
|
|
for _, key := range keys {
|
|
server, _ := l.dao.GetServer(c, key)
|
|
server2keys[server] = append(server2keys[server], key)
|
|
}
|
|
return server2keys, nil
|
|
}
|
|
|
|
func appGid(appId, gid string) string {
|
|
return fmt.Sprintf("%s_%s", appId, gid)
|
|
}
|
|
|
|
func appGids(appId string, gids []string) []string {
|
|
res := make([]string, 0, len(gids))
|
|
for _, g := range gids {
|
|
res = append(res, appGid(appId, g))
|
|
}
|
|
return res
|
|
}
|
|
|
|
func convertAddress(addr string) string {
|
|
// todo
|
|
if len(addr) == 0 {
|
|
return addr
|
|
}
|
|
switch addr[0] {
|
|
case 'g':
|
|
return addr[7:]
|
|
default:
|
|
return addr
|
|
}
|
|
}
|