rpc 패키지 적용

This commit is contained in:
2023-07-10 15:39:56 +09:00
parent 8d0f21077d
commit ec0ed1ce06
10 changed files with 325 additions and 651 deletions

View File

@ -10,20 +10,17 @@ import (
"fmt"
"net/url"
"os"
"path"
"reflect"
"runtime"
"strings"
"sync"
"time"
"repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/rpc"
"repositories.action2quare.com/ayo/gocommon/wshandler"
"repositories.action2quare.com/ayo/tavern/core/rpc"
"github.com/go-redis/redis/v8"
"github.com/gorilla/websocket"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
@ -32,8 +29,6 @@ type accountID = primitive.ObjectID
type ticketID = primitive.ObjectID
type groupID = primitive.ObjectID
var everyHost, _ = primitive.ObjectIDFromHex("010203040506070809101112")
type Invitation struct {
GroupID groupID `json:"gid"`
TicketID ticketID `json:"tid"`
@ -56,7 +51,6 @@ type PublicMemberDoc struct {
type FullGroupDoc struct {
Gid groupID
DM string
AllMembers []*PublicMemberDoc `json:",omitempty"`
Body GroupDocBody `json:",omitempty"`
}
@ -69,7 +63,7 @@ type memberDoc struct {
// underscore keys in Hidden
Hidden bson.M
rconn *wshandler.Richconn
rconn *connection
Mid accountID
}
@ -99,28 +93,28 @@ func (gd *groupDoc) updateBodyWithBson(src []byte) ([]byte, error) {
return bson.Marshal(gd.Body)
}
func (gd *groupDoc) updateBodyWithJson(src []byte) ([]byte, error) {
func (gd *groupDoc) updateBodyWithJson(src []byte) []byte {
gd.Lock()
defer gd.Unlock()
err := json.Unmarshal(src, &gd.Body)
if err != nil {
return nil, err
return nil
}
return json.Marshal(makeTypeMessage(gd.Body))
return makeTypeMessage(gd.Body)
}
func (gd *groupDoc) updateBodyBsonToJson(bsonSrc []byte) (jsonBt []byte, err error) {
func (gd *groupDoc) updateBodyBsonToJson(bsonSrc []byte) []byte {
gd.Lock()
defer gd.Unlock()
err = bson.Unmarshal(bsonSrc, &gd.Body)
err := bson.Unmarshal(bsonSrc, &gd.Body)
if err != nil {
return nil, err
return nil
}
return json.Marshal(makeTypeMessage(gd.Body))
return makeTypeMessage(gd.Body)
}
func (gd *groupDoc) updateBody(bsonSrc []byte) error {
@ -130,7 +124,7 @@ func (gd *groupDoc) updateBody(bsonSrc []byte) error {
return bson.Unmarshal(bsonSrc, &gd.Body)
}
func (gd *groupDoc) addInvite(inviteeDoc bson.M, rconn *wshandler.Richconn, ttl time.Duration, max int) (ticketID, *memberDoc) {
func (gd *groupDoc) addInvite(inviteeDoc bson.M, rconn *connection, ttl time.Duration, max int) (ticketID, *memberDoc) {
gd.Lock()
defer gd.Unlock()
@ -194,7 +188,7 @@ func seperateHidden(in bson.M) (public bson.M, hidden bson.M) {
return in, hidden
}
func (gd *groupDoc) addInCharge(mid accountID, rconn *wshandler.Richconn, doc bson.M) (ticketID, *memberDoc) {
func (gd *groupDoc) addInCharge(mid accountID, rconn *connection, doc bson.M) (ticketID, *memberDoc) {
gd.Lock()
defer gd.Unlock()
@ -291,21 +285,6 @@ func (gd *groupDoc) removeMember(mid accountID, tid *ticketID) {
}
}
func (gd *groupDoc) conns(includeInvitee bool) (out []*wshandler.Richconn) {
gd.Lock()
defer gd.Unlock()
for _, mem := range gd.tickets {
if mem.rconn != nil {
if !includeInvitee && mem.Invite {
continue
}
out = append(out, mem.rconn)
}
}
return
}
func (gd *groupDoc) ticket(mid accountID) ticketID {
gd.Lock()
defer gd.Unlock()
@ -399,7 +378,7 @@ func (gd *groupDoc) iterateMembers(cb func(ticketID, *memberDoc)) {
}
}
func (gd *groupDoc) serializeFull(gid groupID, directMessageChanName string) []byte {
func (gd *groupDoc) serializeFull(gid groupID) []byte {
gd.Lock()
defer gd.Unlock()
@ -416,14 +395,11 @@ func (gd *groupDoc) serializeFull(gid groupID, directMessageChanName string) []b
})
}
bt, _ := json.Marshal(makeTypeMessage(FullGroupDoc{
return makeTypeMessage(FullGroupDoc{
Gid: gid,
DM: directMessageChanName,
AllMembers: output,
Body: gd.Body,
}))
return bt
})
}
type groupContainer struct {
@ -433,11 +409,13 @@ type groupContainer struct {
type groupInMemory struct {
*groupConfig
groupDocSync func(groupID, []byte) error
memberSync func(groupID, accountID, ticketID, *memberDoc, bool) error
rpcCall func([]byte) error
hasConn func(accountID) *wshandler.Richconn
groups groupContainer
groupDocSync func(groupID, []byte) error
memberSync func(groupID, accountID, ticketID, *memberDoc, bool) error
rpcCall func([]byte) error
hasConn func(accountID) *connection
sendUpstreamMessage func(*wshandler.UpstreamMessage)
sendCloseMessage func(accountID, string)
groups groupContainer
}
func (gc *groupContainer) add(id groupID, doc *groupDoc) {
@ -496,46 +474,6 @@ func (gm *groupInMemory) Candidate(gid groupID, mid accountID, doc bson.M) error
}
var errGroupNotExist = errors.New("group does not exist")
var errFuncNameIsMissing = errors.New("how func name is missin")
func (gm *groupInMemory) callProxyRpc(target accountID, name string, args ...any) error {
bt, err := rpc.Encode(target, name, args...)
if err != nil {
return err
}
return gm.rpcCall(bt)
}
type rpcTarget struct {
gm *groupInMemory
target accountID
}
func (rt rpcTarget) call(args ...any) error {
pc := make([]uintptr, 1)
n := runtime.Callers(2, pc[:])
if n < 1 {
return nil
}
frame, _ := runtime.CallersFrames(pc).Next()
funcname := path.Ext(frame.Func.Name())
if len(funcname) > 0 {
funcname = funcname[1:]
return rt.gm.callProxyRpc(rt.target, funcname, args...)
}
return errFuncNameIsMissing
}
func (gm *groupInMemory) rpc(target accountID) rpcTarget {
return rpcTarget{
gm: gm,
target: target,
}
}
var errNoEmptySlot = errors.New("no more seat in group")
func (gm *groupInMemory) Join(gid groupID, mid accountID, tid ticketID, doc bson.M) (ticketID, error) {
@ -558,58 +496,47 @@ func (gm *groupInMemory) FindTicketID(gid groupID, mid groupID) ticketID {
return primitive.NilObjectID
}
func makeTypeMessage[T any](msg T) bson.M {
func makeTypeMessage[T any](msg T) []byte {
var ptr *T
name := reflect.TypeOf(ptr).Elem().Name()
return bson.M{name: msg}
bt, _ := json.Marshal(bson.M{name: msg})
return bt
}
func sendTypedMessageDirect[T any](rconn *wshandler.Richconn, msg T) {
bt, _ := json.Marshal(makeTypeMessage(msg))
rconn.WriteBytes(bt)
}
// func sendTypedMessageDirect[T any](rconn *wshandler.Richconn, msg T) {
// bt, _ := json.Marshal(makeTypeMessage(msg))
// rconn.WriteBytes(bt)
// }
func sendTypedMessage[T any](gm *groupInMemory, target accountID, msg T) {
bt, _ := json.Marshal(makeTypeMessage(msg))
gm.SendMessage(target, bt)
}
// func sendTypedMessage[T any](gm *groupInMemory, target accountID, msg T) {
// bt, _ := json.Marshal(makeTypeMessage(msg))
// gm.SendMessage(target, bt)
// }
func (gm *groupInMemory) SendMessage(target accountID, msg []byte) {
rconn := gm.hasConn(target)
if rconn != nil {
rconn.WriteBytes(msg)
} else {
gm.rpc(target).call(target, msg)
}
}
// func (gm *groupInMemory) SendMessage(target accountID, msg []byte) {
// rconn := gm.hasConn(target)
// if rconn != nil {
// rconn.WriteBytes(msg)
// } else {
// gm.rpc(target).call(target, msg)
// }
// }
func multicast(conns []*wshandler.Richconn, raw []byte) {
for _, rconn := range conns {
rconn.WriteBytes(raw)
}
}
// func multicast(conns []*wshandler.Richconn, raw []byte) {
// for _, rconn := range conns {
// rconn.WriteBytes(raw)
// }
// }
func broadcastTypedMessage[T any](gm *groupInMemory, gid groupID, msg T) {
if gd := gm.groups.find(gid); gd != nil {
bt, _ := json.Marshal(makeTypeMessage(msg))
go multicast(gd.conns(false), bt)
}
}
// func broadcastTypedMessage[T any](gm *groupInMemory, gid groupID, msg T) {
// if gd := gm.groups.find(gid); gd != nil {
// bt, _ := json.Marshal(makeTypeMessage(msg))
// go multicast(gd.conns(false), bt)
// }
// }
var errInviteeDocMidMissing = errors.New("inviteeDoc must have '_mid' field")
func (gm *groupInMemory) SendInvitationFailed(mid accountID, inviteeDoc bson.M) error {
delete(inviteeDoc, "_mid")
rconn := gm.hasConn(mid)
if rconn == nil {
return gm.rpc(mid).call(mid, inviteeDoc)
}
sendTypedMessage(gm, mid, InvitationFail(inviteeDoc))
return nil
}
func (gm *groupInMemory) InviteImplement(gid groupID, mid accountID, inviteeDoc bson.M, inviterDoc bson.M) error {
targetid := inviteeDoc["_mid"].(accountID)
@ -617,7 +544,7 @@ func (gm *groupInMemory) InviteImplement(gid groupID, mid accountID, inviteeDoc
// invitee의 rconn이 종료될 때 그룹에 반영해야 하므로 rconn을 찾자
rconn := gm.hasConn(targetid)
if rconn == nil {
return gm.rpc(targetid).call(gid, inviteeDoc, inviterDoc)
return rpc.Make(gm).To(targetid).Call(gid, mid, inviteeDoc, inviterDoc)
}
gd := gm.groups.find(gid)
@ -625,10 +552,14 @@ func (gm *groupInMemory) InviteImplement(gid groupID, mid accountID, inviteeDoc
return errGroupNotExist
}
if rconn.HasOnCloseFunc("member_remove_invite") {
if rconn.hasOnCloseFunc("member_remove_invite") {
// 이미 초대 중이다.
// inviter한테 알려줘야 한다.
return gm.SendInvitationFailed(mid, inviteeDoc)
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(),
Body: makeTypeMessage(InvitationFail(inviteeDoc)),
})
return nil
}
tid, newdoc := gd.addInvite(inviteeDoc, rconn, time.Duration(gm.InviteExpire)*time.Second, gm.MaxMember)
@ -636,17 +567,20 @@ func (gm *groupInMemory) InviteImplement(gid groupID, mid accountID, inviteeDoc
return errNoEmptySlot
}
rconn.RegistOnCloseFunc("member_remove_invite", func() {
rconn.registOnCloseFunc("member_remove_invite", func() {
gd.removeMember(targetid, &tid)
gm.memberSync(gid, targetid, tid, nil, false)
})
gm.memberSync(gid, targetid, tid, newdoc, false)
sendTypedMessage(gm, targetid, Invitation{
GroupID: gid,
TicketID: tid,
Inviter: inviterDoc,
ExpireAtUTC: newdoc.InviteExpire.Unix(),
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + targetid.Hex(),
Body: makeTypeMessage(Invitation{
GroupID: gid,
TicketID: tid,
Inviter: inviterDoc,
ExpireAtUTC: newdoc.InviteExpire.Unix(),
}),
})
return nil
@ -681,7 +615,7 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
if rconn == nil {
// mid가 있는 곳에서 처리를 해야 접속 끊겼을 때 콜백을 먼저 등록할 수 있다.
// 콜백이 rconn에 먼저 등록되지 않으면 좀비 group이 생길 가능성이 생긴다.
return gid.Hex(), gm.rpc(mid).call(gid, mid, inviteeDoc, inviteeDoc)
return "", rpc.Make(gm).To(targetid).Call(gid, mid, inviteeDoc, inviterDoc)
}
// 이제 여기는 mid가 InCharge이면서 rconn이 존재
@ -689,7 +623,7 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
if gd == nil {
_, gd = gm.groups.createWithID(gid, bson.M{})
tid, newdoc := gd.addInCharge(mid, rconn, inviterDoc)
rconn.RegistOnCloseFunc("member_remove", func() {
rconn.registOnCloseFunc("member_remove", func() {
// 내가 InCharge이므로 접속이 종료될 때 그룹을 해체한다.
gm.groupDocSync(gid, nil)
})
@ -710,9 +644,6 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
return gid.Hex(), gm.InviteImplement(gid, mid, inviteeDoc, inviterDoc)
}
func (gm *groupInMemory) UpdateGroupMember(gid groupID, mid accountID, tid ticketID, doc bson.M) error {
return nil
}
func (gm *groupInMemory) CancelInvitation(gid groupID, tid ticketID) error {
return nil
}
@ -725,17 +656,17 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, tid ticket
rconn := gm.hasConn(mid)
if rconn == nil {
return gid, gm.rpc(mid).call(gid, mid, tid, member)
return gid, rpc.Make(gm).To(mid).Call(gid, mid, tid, member)
}
oldFunc := rconn.UnregistOnCloseFunc("member_remove")
oldFunc := rconn.unregistOnCloseFunc("member_remove")
if oldFunc != nil {
// 기존 멤버였으면 탈퇴 처리
oldFunc()
}
inviteFunc := rconn.UnregistOnCloseFunc("member_remove_invite")
rconn.RegistOnCloseFunc("member_remove", inviteFunc)
inviteFunc := rconn.unregistOnCloseFunc("member_remove_invite")
rconn.registOnCloseFunc("member_remove", inviteFunc)
result, isNew := gd.addMember(mid, &tid, member)
if result != nil {
@ -754,10 +685,10 @@ func (gm *groupInMemory) DenyInvitation(gid groupID, mid accountID, tid ticketID
rconn := gm.hasConn(mid)
if rconn == nil {
return gm.rpc(mid).call(gid, mid, tid)
return rpc.Make(gm).To(mid).Call(gid, mid, tid)
}
inviteFunc := rconn.UnregistOnCloseFunc("member_remove_invite")
inviteFunc := rconn.unregistOnCloseFunc("member_remove_invite")
if inviteFunc != nil {
inviteFunc() // removeMember는 여기에 들어있다.
return nil
@ -806,10 +737,16 @@ func (gm *groupInMemory) DropPausedMember(gid primitive.ObjectID, mid primitive.
return nil
}
func (gm *groupInMemory) PauseMember(gid primitive.ObjectID, mid primitive.ObjectID, rconn *wshandler.Richconn) error {
rconn.UnregistOnCloseFunc("member_remove")
rconn.UnregistOnCloseFunc("member_remove_invite")
rconn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "pause"), time.Time{})
func (gm *groupInMemory) PauseMember(gid primitive.ObjectID, mid primitive.ObjectID) error {
rconn := gm.hasConn(mid)
if rconn == nil {
return rpc.Make(gm).To(mid).Call(gid, mid)
}
// 접속은 끊기지만 그룹에서 제거하지는 않는 상태
rconn.unregistOnCloseFunc("member_remove")
rconn.unregistOnCloseFunc("member_remove_invite")
gm.sendCloseMessage(mid, "pause")
gd := gm.groups.find(gid)
if gd == nil {
@ -903,24 +840,40 @@ func (gm *groupInMemory) Leave(gid groupID, mid accountID, tid ticketID) error {
// targetmid의 "member_remove" 함수를 등록 해제해야 하므로 rconn이 있는 곳에서 하자
rconn := gm.hasConn(targetmid)
if rconn == nil {
return gm.rpc(targetmid).call(gid, mid, tid)
return rpc.Make(gm).To(targetmid).Call(gid, mid, tid)
}
if oldfunc := rconn.UnregistOnCloseFunc("member_remove"); oldfunc != nil {
if oldfunc := rconn.unregistOnCloseFunc("member_remove"); oldfunc != nil {
oldfunc() // 이 안에 다 있다.
}
// 나한테는 빈 FullGroupDoc을 보낸다.
sendTypedMessageDirect(rconn, FullGroupDoc{
Gid: gid,
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(),
Body: makeTypeMessage(FullGroupDoc{Gid: gid}),
})
return nil
}
func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bson.M) error {
gd := gm.groups.find(gid)
if gd == nil {
return errGroupNotExist
}
tid := gd.ticket(mid)
bt, _ := json.Marshal(doc)
personalized := []byte(fmt.Sprintf(`{"%s":%s}`, tid.Hex(), string(bt)))
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: gd.updateBodyWithJson(personalized),
})
return nil
}
func (gm *groupInMemory) Dismiss(gid groupID) error {
return nil
}
@ -938,15 +891,21 @@ func (gm *groupInMemory) UpdateGroupDocument(gid groupID, body []byte) error {
return gm.groupDocSync(gid, newbody)
}
func (gm *groupInMemory) TargetExists(target primitive.ObjectID) bool {
return gm.hasConn(target) != nil
}
var devflag = flagx.Bool("dev", false, "")
func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, typename string, wsh *wshandler.WebsocketHandler) (group, error) {
func (cfg *groupConfig) prepareInMemory(ctx context.Context, typename string, sub *subTavern) (group, error) {
// group document
// member document
region := sub.region
wsh := sub.wsh
groupDocSyncChanName := fmt.Sprintf("d_mgc_%s_%s", region, typename)
memberSyncChanName := fmt.Sprintf("m_mgc_%s_%s", region, typename)
rpcChanName := fmt.Sprintf("r_mgc_%s_%s", region, typename)
clientMessageChanName := fmt.Sprintf("c_mgc_%s_%s", region, typename)
toHashHex := func(name string) string {
hash := md5.New()
@ -962,7 +921,6 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
groupDocSyncChanName = toHashHex(groupDocSyncChanName)
memberSyncChanName = toHashHex(memberSyncChanName)
rpcChanName = toHashHex(rpcChanName)
clientMessageChanName = toHashHex(clientMessageChanName)
// 여기서는 subscribe channel
// 각 함수에서는 publish
@ -996,15 +954,22 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
_, err := wsh.RedisSync.Publish(ctx, rpcChanName, bt).Result()
return err
},
hasConn: func(t accountID) *wshandler.Richconn {
return wsh.Conn(region, t)
hasConn: func(t accountID) *connection {
return sub.cm.get(t)
},
groups: groupContainer{
groupDocs: make(map[groupID]*groupDoc),
},
sendUpstreamMessage: func(msg *wshandler.UpstreamMessage) {
wsh.SendUpstreamMessage(region, msg)
},
sendCloseMessage: func(target accountID, text string) {
wsh.SendCloseMessage(region, target.Hex(), text)
},
}
// TODO : processChannelMessage 스레드 분리해보자
rpc.RegistReceiver(gm)
processChannelMessage := func(gm *groupInMemory, pubsub *redis.PubSub) *redis.PubSub {
defer func() {
r := recover()
@ -1020,34 +985,7 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
}
switch msg.Channel {
case clientMessageChanName:
bt := []byte(msg.Payload)
if len(bt) < 24 {
break
}
// 주!! mid가 먼저
var mid groupID
copy(mid[:], bt[:12])
bt = bt[12:]
var gid groupID
copy(gid[:], bt[:12])
bt = bt[12:]
gd := gm.groups.find(gid)
if gd == nil {
break
}
tid, _ := gd.memberByAccount(mid)
if !tid.IsZero() {
personalized := []byte(fmt.Sprintf(`{"%s":%s}`, tid.Hex(), string(bt)))
if after, err := gd.updateBodyWithJson(personalized); err == nil {
go multicast(gd.conns(false), after)
}
}
case groupDocSyncChanName:
case groupDocSyncChanName: // 호스트들간 그룹 정보 동기화 채널
payload := []byte(msg.Payload)
if len(payload) < len(config.macAddr) {
break
@ -1065,17 +1003,17 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
if len(remain) == 0 {
// gid 그룹 삭제
// 그룹 안에 있는 멤버에게 알림
bt, _ := json.Marshal(makeTypeMessage(FullGroupDoc{
Gid: gid,
}))
go multicast(gd.conns(true), bt)
bt := makeTypeMessage(FullGroupDoc{Gid: gid})
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: bt,
})
gm.groups.delete(gid)
} else if string(senderHost) != config.macAddr {
if r, err := gd.updateBodyBsonToJson(remain); err != nil {
logger.Error("groupDocSyncChanName message decode failed :", remain, err)
} else {
go multicast(gd.conns(true), r)
}
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: gd.updateBodyBsonToJson(remain),
})
}
} else if string(senderHost) != config.macAddr {
var newDoc groupDoc
@ -1086,7 +1024,7 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
}
}
case memberSyncChanName:
case memberSyncChanName: // 호스트들간 멤버 정보 동기화 채널
if len(msg.Payload) < len(config.macAddr) {
break
}
@ -1116,7 +1054,7 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
}
var updated *memberDoc
rconn := wsh.Conn(region, mid)
rconn := gm.hasConn(mid)
if senderHost != config.macAddr {
// 내가 보낸 메시지가 아니면 멤버 도큐먼트 업데이트 하고 브로드캐스팅
@ -1136,56 +1074,37 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
if rconn != nil {
// gid에 이미 다른 값이 있을 수 있다.
// 정확하게 이 값이면 제거하고, 아니면 넘어간다.
rconn.RemoveTag("gid", fmt.Sprintf("%s@%s", gid.Hex(), gm.Name))
rconn.removeTag("gid", fmt.Sprintf("%s@%s", gid.Hex(), gm.Name))
}
broadcastTypedMessage(gm, gid, PublicMemberDoc{Tid: tid})
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: makeTypeMessage(PublicMemberDoc{Tid: tid}),
})
} else {
if isNewMember && updated.rconn == nil && rconn != nil {
updated.rconn = rconn
}
// 업데이트 된 플레이어(새로 들어온 플레이어 포함)를 모두에게 알려준다. 본인 포함, invitee 제외
broadcastTypedMessage(gm, gid, PublicMemberDoc{
Tid: tid,
memberDocCommon: updated.memberDocCommon,
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: makeTypeMessage(PublicMemberDoc{
Tid: tid,
memberDocCommon: updated.memberDocCommon,
}),
})
}
if isNewMember {
if rconn != nil {
// 새 멤버이므로 기존 멤버를 다 보내준다.
rconn.AddTag("gid", fmt.Sprintf("%s@%s", gid.Hex(), gm.Name))
rconn.WriteBytes(gd.serializeFull(gid, clientMessageChanName))
rconn.addTag("gid", fmt.Sprintf("%s@%s", gid.Hex(), gm.Name))
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(),
Body: gd.serializeFull(gid),
})
}
}
case rpcChanName:
targetbt, fn, params, err := rpc.Decode[accountID]([]byte(msg.Payload))
if err != nil {
logger.Error("rpcChanName message decode failed :", msg.Payload, err)
break
}
call := func() {
method, ok := reflect.TypeOf(gm).MethodByName(fn)
if !ok {
logger.Println("message decode failed :", err, targetbt, msg.Payload)
}
args := []reflect.Value{
reflect.ValueOf(gm),
}
for _, arg := range params {
args = append(args, reflect.ValueOf(arg))
}
method.Func.Call(args)
}
if *targetbt == everyHost {
call()
} else if rconn := wsh.Conn(region, *targetbt); rconn != nil {
call()
}
default:
logger.Println("unknown channel")
}
@ -1204,7 +1123,7 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, region string, type
var pubsub *redis.PubSub
for {
if pubsub == nil {
pubsub = wsh.RedisSync.Subscribe(ctx, groupDocSyncChanName, memberSyncChanName, rpcChanName, clientMessageChanName)
pubsub = wsh.RedisSync.Subscribe(ctx, groupDocSyncChanName, memberSyncChanName, rpcChanName)
}
if pubsub == nil {