Files
chain33-im/logic/logic.go
2022-03-17 15:55:27 +08:00

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
}
}