InMemory 그룹을 redis로 변경

This commit is contained in:
2023-07-19 09:37:02 +09:00
parent 01da5bb3a4
commit 8e1b232d57
8 changed files with 209 additions and 448 deletions

View File

@ -1,7 +1,6 @@
package core package core
import ( import (
"encoding/json"
"net/http" "net/http"
common "repositories.action2quare.com/ayo/gocommon" common "repositories.action2quare.com/ayo/gocommon"
@ -84,7 +83,7 @@ func (sub *subTavern) JoinGroup(w http.ResponseWriter, r *http.Request) {
} }
if err == nil { if err == nil {
json.NewEncoder(w).Encode(map[string]string{ writeBsonDoc(w, map[string]string{
"gid": gidobj.Hex(), "gid": gidobj.Hex(),
"tid": tidobj.Hex(), "tid": tidobj.Hex(),
}) })
@ -195,9 +194,6 @@ func (sub *subTavern) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
// TODO : full group doc을 내려보냄
// w.Write([]byte(gidbytes.Hex()))
} }
func (sub *subTavern) DenyInvitation(w http.ResponseWriter, r *http.Request) { func (sub *subTavern) DenyInvitation(w http.ResponseWriter, r *http.Request) {
@ -292,7 +288,7 @@ func (sub *subTavern) SearchGroup(w http.ResponseWriter, r *http.Request) {
} }
if err := writeBsonArr(w, result); err != nil { if err := writeBsonArr(w, result); err != nil {
logger.Error("json marshal failed :", err) logger.Error("bson marshal failed :", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
@ -369,7 +365,7 @@ func (sub *subTavern) QueryGroup(w http.ResponseWriter, r *http.Request) {
} }
if err := writeBsonDoc(w, result); err != nil { if err := writeBsonDoc(w, result); err != nil {
logger.Error("json marshal failed :", err) logger.Error("bson marshal failed :", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
@ -486,8 +482,7 @@ func (sub *subTavern) UpdateGroupDocument(w http.ResponseWriter, r *http.Request
} }
var frag bson.M var frag bson.M
dec := json.NewDecoder(r.Body) if err := readBsonDoc(r.Body, &frag); err != nil {
if err := dec.Decode(&frag); err != nil {
logger.Error("UpdateGroupDocument failed. readBsonDoc err :", err) logger.Error("UpdateGroupDocument failed. readBsonDoc err :", err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
@ -499,3 +494,33 @@ func (sub *subTavern) UpdateGroupDocument(w http.ResponseWriter, r *http.Request
return return
} }
} }
func (sub *subTavern) QueryGroupMembers(w http.ResponseWriter, r *http.Request) {
typename, _ := common.ReadStringFormValue(r.Form, "type")
group := sub.groups[typename]
if group == nil {
logger.Println("QueryGroupMembers failed. type is missing")
w.WriteHeader(http.StatusBadRequest)
return
}
gid, ok := common.ReadObjectIDFormValue(r.Form, "gid")
if !ok {
logger.Println("QueryGroupMembers failed. gid is missing")
w.WriteHeader(http.StatusBadRequest)
return
}
members, err := group.QueryGroupMembers(gid)
if err != nil {
logger.Error("QueryGroupMembers failed. group.QueryGroupMembers returns err :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if err := writeBsonDoc(w, members); err != nil {
logger.Error("QueryGroupMembers failed. writeBsonDoc return err :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}

View File

@ -38,4 +38,5 @@ type group interface {
UpdateMemberDocument(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error UpdateMemberDocument(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error
Dismiss(groupID primitive.ObjectID) error Dismiss(groupID primitive.ObjectID) error
UpdateGroupDocument(groupID primitive.ObjectID, doc bson.M) error UpdateGroupDocument(groupID primitive.ObjectID, doc bson.M) error
QueryGroupMembers(groupID primitive.ObjectID) (bson.M, error)
} }

View File

@ -54,36 +54,45 @@ type memberDoc struct {
type InvitationFail bson.M type InvitationFail bson.M
type groupDoc struct { type groupDoc struct {
Body bson.M `json:"_body"` Members map[string]any `json:"_members"`
Members map[string]*memberDoc `json:"_members"` InCharge string `json:"_incharge"`
InCharge string `json:"_incharge"` Gid string `json:"_gid"`
Gid string `json:"_gid"`
rh *RedisonHandler rh *gocommon.RedisonHandler
id groupID id groupID
} }
func (p groupDoc) MarshalJSON() ([]byte, error) { func (gd *groupDoc) loadMemberFull(tid string) (bson.M, error) {
if len(p.Gid) == 0 { full, err := gd.rh.JSONGet(gd.strid(), "$._members."+tid)
p.Gid = p.id.Hex() if err != nil {
return nil, err
} }
// Turn p into a map bt := []byte(full.(string))
type groupDoc_ groupDoc // prevent recursion bt = bt[1 : len(bt)-1]
b, _ := json.Marshal(groupDoc_(p))
var m map[string]json.RawMessage var doc bson.M
_ = json.Unmarshal(b, &m) if err = json.Unmarshal(bt, &doc); err != nil {
return nil, err
// Add tags to the map, possibly overriding struct fields
for k, v := range p.Body {
// if overriding struct fields is not acceptable:
// if _, ok := m[k]; ok { continue }
b, _ = json.Marshal(v)
m[k] = b
} }
return json.Marshal(m) return doc, nil
}
func (gd *groupDoc) loadFull() (doc bson.M) {
// 새 멤버에 그룹 전체를 알림
full, err := gd.rh.JSONGet(gd.strid(), "$")
if err == nil {
bt := []byte(full.(string))
bt = bt[1 : len(bt)-1]
err = json.Unmarshal(bt, &doc)
if err != nil {
logger.Println("loadFull err :", err)
}
} else {
logger.Println("loadFull err :", err)
}
return
} }
func (gd *groupDoc) strid() string { func (gd *groupDoc) strid() string {
@ -160,18 +169,19 @@ func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*m
return newdoc, err return newdoc, err
} }
func (gd *groupDoc) addMember(mid accountID, doc bson.M) (*memberDoc, error) { func (gd *groupDoc) addMember(mid accountID, doc bson.M) (bson.M, error) {
memdoc := &memberDoc{ tid := gd.tid(mid)
Body: doc, prefix := "$._members." + tid
Invite: false,
InviteExpire: 0,
}
if _, err := gd.rh.JSONSet(gd.strid(), "$._members."+gd.tid(mid), memdoc, SetOptionXX); err != nil { if _, err := gd.rh.JSONMerge(gd.strid(), prefix+"._body", doc, gocommon.RedisonSetOptionXX); err != nil {
return nil, err return nil, err
} }
return memdoc, nil if err := gd.rh.JSONMDel(gd.strid(), []string{prefix + "._invite", prefix + "._invite_exp"}); err != nil {
return nil, err
}
return gd.loadMemberFull(tid)
} }
func (gd *groupDoc) removeMember(mid accountID) error { func (gd *groupDoc) removeMember(mid accountID) error {
@ -179,21 +189,41 @@ func (gd *groupDoc) removeMember(mid accountID) error {
return err return err
} }
func (gd *groupDoc) getMembers() (map[string]any, error) {
res, err := gd.rh.JSONGet(gd.strid(), "$._members")
if err != nil {
return nil, err
}
var temp []map[string]any
err = json.Unmarshal([]byte(res.(string)), &temp)
if err != nil {
return nil, err
}
out := make(map[string]any)
for k, v := range temp[0] {
body := v.(map[string]any)["_body"]
out[gd.mid(k).Hex()] = body
}
return out, nil
}
type groupInMemory struct { type groupInMemory struct {
*groupConfig *groupConfig
sendUpstreamMessage func(*wshandler.UpstreamMessage) sendUpstreamMessage func(*wshandler.UpstreamMessage)
sendEnterRoomMessage func(groupID, accountID) sendEnterRoomMessage func(groupID, accountID)
sendLeaveRoomMessage func(groupID, accountID) sendLeaveRoomMessage func(groupID, accountID)
rh *RedisonHandler rh *gocommon.RedisonHandler
} }
func (gm *groupInMemory) createGroup(newid groupID, charge accountID, chargeDoc bson.M) (*groupDoc, error) { func (gm *groupInMemory) createGroup(newid groupID, charge accountID, chargeDoc bson.M) (*groupDoc, error) {
tid := makeTid(newid, charge) tid := makeTid(newid, charge)
gd := &groupDoc{ gd := &groupDoc{
Body: bson.M{}, Members: map[string]any{
Members: map[string]*memberDoc{ tid: &memberDoc{
tid: {
Body: chargeDoc, Body: chargeDoc,
Invite: false, Invite: false,
InviteExpire: 0, InviteExpire: 0,
@ -205,7 +235,7 @@ func (gm *groupInMemory) createGroup(newid groupID, charge accountID, chargeDoc
id: newid, id: newid,
} }
_, err := gm.rh.JSONSet(gd.strid(), "$", gd, SetOptionNX) _, err := gm.rh.JSONSet(gd.strid(), "$", gd, gocommon.RedisonSetOptionNX)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -243,18 +273,36 @@ func (gm *groupInMemory) Candidate(gid groupID, mid accountID, doc bson.M) error
var errGroupNotExist = errors.New("group does not exist") var errGroupNotExist = errors.New("group does not exist")
func (gm *groupInMemory) Join(gid groupID, mid accountID, doc bson.M) error { func (gm *groupInMemory) Join(gid groupID, mid accountID, doc bson.M) error {
group, err := gm.find(gid) gd, err := gm.find(gid)
if err != nil { if err != nil {
return err return err
} }
if group == nil { if gd == nil {
// 그룹이 없다. 실패 // 그룹이 없다. 실패
return errGroupNotExist return errGroupNotExist
} }
// 내 정보 업데이트할 때에도 사용됨 // 내 정보 업데이트할 때에도 사용됨
_, err = group.addMember(mid, doc) if memdoc, err := gd.addMember(mid, doc); err == nil {
// 기존 유저에게 새 유저 알림
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: map[string]any{
gd.tid(mid): memdoc,
},
Tag: []string{"MemberDocFull"},
})
gm.sendEnterRoomMessage(gid, mid)
// 새 멤버에 그룹 전체를 알림
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(),
Body: gd.loadFull(),
Tag: []string{"GroupDocFull"},
})
}
return err return err
} }
@ -269,7 +317,7 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
} }
// targetid에 초대한 mid가 들어있다. // targetid에 초대한 mid가 들어있다.
already, err := gm.rh.Get(gm.rh.ctx, targetid.Hex()).Result() already, err := gm.rh.Get(context.Background(), targetid.Hex()).Result()
if err != nil && err != redis.Nil { if err != nil && err != redis.Nil {
return "", err return "", err
} }
@ -313,7 +361,7 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
} }
// 초대 중 표시 // 초대 중 표시
_, err = gm.rh.SetNX(gm.rh.ctx, targetid.Hex(), mid.Hex(), time.Duration(gm.InviteExpire)*time.Second).Result() _, err = gm.rh.SetNX(context.Background(), targetid.Hex(), mid.Hex(), time.Duration(gm.InviteExpire)*time.Second).Result()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -340,9 +388,7 @@ func (gm *groupInMemory) CancelInvitation(gid groupID, tid ticketID) error {
var errInvitationExpired = errors.New("invitation is already expired") var errInvitationExpired = errors.New("invitation is already expired")
func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bson.M) error { func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bson.M) error {
logger.Println("accept invitation key :", mid.Hex()) cnt, err := gm.rh.Del(context.Background(), mid.Hex()).Result()
cnt, err := gm.rh.Del(gm.rh.ctx, mid.Hex()).Result()
if err != nil { if err != nil {
return err return err
} }
@ -351,7 +397,7 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bso
return errInvitationExpired return errInvitationExpired
} }
gd := groupDoc{ gd := &groupDoc{
id: gid, id: gid,
rh: gm.rh, rh: gm.rh,
} }
@ -366,25 +412,13 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bso
}, },
Tag: []string{"MemberDocFull"}, Tag: []string{"MemberDocFull"},
}) })
gm.sendEnterRoomMessage(gid, mid) gm.sendEnterRoomMessage(gid, mid)
// 새 멤버에 그룹 전체를 알림 // 새 멤버에 그룹 전체를 알림
full, err := gm.rh.JSONGet(gd.strid(), "$")
if err != nil {
return err
}
var temp []*groupDoc
err = json.Unmarshal([]byte(full.(string)), &temp)
if err != nil {
return err
}
test, _ := json.Marshal(temp[0])
logger.Println(string(test))
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(), Target: "@" + mid.Hex(),
Body: temp[0], Body: gd.loadFull(),
Tag: []string{"GroupDocFull"}, Tag: []string{"GroupDocFull"},
}) })
} }
@ -393,8 +427,16 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bso
return err return err
} }
func (gm *groupInMemory) QueryGroupMembers(gid groupID) (bson.M, error) {
gd := groupDoc{
id: gid,
rh: gm.rh,
}
return gd.getMembers()
}
func (gm *groupInMemory) DenyInvitation(gid groupID, mid accountID, tid ticketID) error { func (gm *groupInMemory) DenyInvitation(gid groupID, mid accountID, tid ticketID) error {
gm.rh.Del(gm.rh.ctx, mid.Hex()).Result() gm.rh.Del(context.Background(), mid.Hex()).Result()
gd := groupDoc{ gd := groupDoc{
id: gid, id: gid,
rh: gm.rh, rh: gm.rh,
@ -435,10 +477,11 @@ func (gm *groupInMemory) Leave(gid groupID, mid accountID) error {
} }
func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bson.M) error { func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bson.M) error {
gd := groupDoc{ gd := &groupDoc{
id: gid, id: gid,
rh: gm.rh, rh: gm.rh,
} }
prefixPath := fmt.Sprintf("$._members.%s.", gd.tid(mid)) prefixPath := fmt.Sprintf("$._members.%s.", gd.tid(mid))
err := gm.rh.JSONMSetRel(gd.strid(), prefixPath, doc) err := gm.rh.JSONMSetRel(gd.strid(), prefixPath, doc)
if err != nil { if err != nil {
@ -494,7 +537,7 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, typename string, su
// 각 함수에서는 publish // 각 함수에서는 publish
gm := &groupInMemory{ gm := &groupInMemory{
groupConfig: cfg, groupConfig: cfg,
rh: NewRedisonHandler(ctx, redisClient), rh: gocommon.NewRedisonHandler(ctx, redisClient),
sendUpstreamMessage: func(msg *wshandler.UpstreamMessage) { sendUpstreamMessage: func(msg *wshandler.UpstreamMessage) {
wsh.SendUpstreamMessage(region, msg) wsh.SendUpstreamMessage(region, msg)
}, },

View File

@ -1,372 +0,0 @@
package core
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/go-redis/redis/v8"
)
type SetOption = string
type GetOption = [2]any
const (
// JSONSET command Options
SetOptionNX SetOption = "NX"
SetOptionXX SetOption = "XX"
)
var (
GetOptionSPACE = GetOption{"SPACE", " "}
GetOptionINDENT = GetOption{"INDENT", "\t"}
GetOptionNEWLINE = GetOption{"NEWLINE", "\n"}
GetOptionNOESCAPE = GetOption{"NOESCAPE", ""}
)
// gocommon으로 옮길 거
type RedisonHandler struct {
*redis.Client
ctx context.Context
}
func NewRedisonHandler(ctx context.Context, redisClient *redis.Client) *RedisonHandler {
return &RedisonHandler{
Client: redisClient,
ctx: ctx,
}
}
func respToArray[T any](resp any, err error) ([]T, error) {
if err != nil {
return nil, err
}
resArr := resp.([]any)
v := make([]T, len(resArr))
for i, e := range resArr {
v[i] = e.(T)
}
return v, nil
}
func appendArgs[T any](args []any, ext ...T) []any {
for _, e := range ext {
args = append(args, e)
}
return args
}
func (rh *RedisonHandler) JSONMSetRel(key string, prefixPath string, kv map[string]any) error {
if len(prefixPath) > 0 && !strings.HasSuffix(prefixPath, ".") {
prefixPath += "."
}
pl := rh.Pipeline()
for path, obj := range kv {
b, err := json.Marshal(obj)
if err != nil {
return err
}
pl.Do(rh.ctx, "JSON.SET", key, prefixPath+path, b)
}
cmders, err := pl.Exec(rh.ctx)
if err != nil {
return err
}
for _, cmder := range cmders {
if cmder.Err() != nil {
return cmder.Err()
}
}
return nil
}
func (rh *RedisonHandler) JSONMSet(key string, kv map[string]any) error {
return rh.JSONMSetRel(key, "", kv)
}
func (rh *RedisonHandler) JSONSet(key, path string, obj any, opts ...SetOption) (bool, error) {
b, err := json.Marshal(obj)
if err != nil {
return false, err
}
args := []any{
"JSON.SET",
key,
path,
b,
}
if len(opts) > 0 {
args = append(args, opts[0])
}
res, err := rh.Do(rh.ctx, args...).Result()
if err != nil {
return false, err
}
return res.(string) == "OK", nil
}
func (rh *RedisonHandler) JSONGet(key, path string, opts ...GetOption) (res any, err error) {
args := appendArgs[string]([]any{
"JSON.GET",
key,
}, strings.Split(path, " ")...)
for _, opt := range opts {
args = append(args, opt[:]...)
}
return rh.Do(rh.ctx, args...).Result()
}
func (rh *RedisonHandler) JSONGetString(key, path string) ([]string, error) {
return respToArray[string](rh.JSONResp(key, path))
}
func (rh *RedisonHandler) JSONGetInt64(key, path string) ([]int64, error) {
return respToArray[int64](rh.JSONResp(key, path))
}
func (rh *RedisonHandler) JSONMGet(path string, keys ...string) (res any, err error) {
args := appendArgs[string]([]any{
"JSON.MGET",
path,
}, keys...)
return rh.Do(rh.ctx, args...).Result()
}
func (rh *RedisonHandler) JSONMDel(key string, paths []string) error {
pl := rh.Pipeline()
for _, path := range paths {
args := []any{
"JSON.DEL",
key,
path,
}
pl.Do(rh.ctx, args...)
}
_, err := pl.Exec(rh.ctx)
return err
}
func (rh *RedisonHandler) JSONDel(key, path string) (int64, error) {
args := []any{
"JSON.DEL",
key,
path,
}
resp, err := rh.Do(rh.ctx, args...).Result()
if err != nil {
return 0, err
}
return resp.(int64), nil
}
func (rh *RedisonHandler) JSONType(key, path string) ([]string, error) {
args := []any{
"JSON.TYPE",
key,
path,
}
return respToArray[string](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONNumIncrBy(key, path string, number int) ([]any, error) {
args := []any{
"JSON.NUMINCRBY",
key,
path,
number,
}
resp, err := rh.Do(rh.ctx, args...).Result()
if err != nil {
return nil, err
}
return resp.([]any), nil
}
func (rh *RedisonHandler) JSONNumMultBy(key, path string, number int) (res any, err error) {
args := []any{
"JSON.NUMMULTBY",
key,
path,
number,
}
resp, err := rh.Do(rh.ctx, args...).Result()
if err != nil {
return nil, err
}
return resp.([]any), nil
}
func (rh *RedisonHandler) JSONStrAppend(key, path string, jsonstring string) ([]int64, error) {
args := []any{
"JSON.STRAPPEND",
key,
path,
fmt.Sprintf(`'"%s"'`, jsonstring),
}
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONStrLen(key, path string) (res []int64, err error) {
args := []any{
"JSON.STRLEN",
key,
path,
}
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONArrAppend(key, path string, values ...any) (int64, error) {
args := []any{
"JSON.ARRAPPEND",
key,
path,
}
resp, err := rh.Do(rh.ctx, args...).Result()
if err != nil {
return 0, err
}
return resp.(int64), nil
}
func (rh *RedisonHandler) JSONArrLen(key, path string) ([]int64, error) {
args := []any{
"JSON.ARRLEN",
key,
path,
}
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONArrPop(key, path string, index int) (res any, err error) {
args := []any{
"JSON.ARRPOP",
key,
path,
index,
}
resp, err := rh.Do(rh.ctx, args...).Result()
if err != nil {
return nil, err
}
return resp.([]any)[0], nil
}
func appendValues(args []any, values ...any) []any {
for _, jsonValue := range values {
switch jsonValue := jsonValue.(type) {
case string:
args = append(args, fmt.Sprintf(`'"%s"'`, jsonValue))
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
args = append(args, jsonValue)
default:
bt, _ := json.Marshal(jsonValue)
args = append(args, bt)
}
}
return args
}
func (rh *RedisonHandler) JSONArrIndex(key, path string, jsonValue any, optionalRange ...int) ([]int64, error) {
args := appendValues([]any{
"JSON.ARRINDEX",
key,
path,
}, jsonValue)
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONArrTrim(key, path string, start, end int) (res any, err error) {
args := []any{
"JSON.ARRTRIM",
key,
path,
start,
end,
}
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONArrInsert(key, path string, index int, values ...any) (res any, err error) {
args := appendValues([]any{
"JSON.ARRINSERT",
key,
path,
index,
}, values...)
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONObjKeys(key, path string) ([]string, error) {
args := []any{
"JSON.OBJKEYS",
key,
path,
}
res, err := rh.Do(rh.ctx, args...).Result()
if err != nil {
return nil, err
}
resArr := res.([]any)
resArr = resArr[0].([]any)
slc := make([]string, len(resArr))
for i, r := range resArr {
slc[i] = r.(string)
}
return slc, nil
}
func (rh *RedisonHandler) JSONObjLen(key, path string) ([]int64, error) {
args := []any{
"JSON.OBJLEN",
key,
}
if path != "$" {
args = append(args, path)
}
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
}
func (rh *RedisonHandler) JSONDebug(key, path string) (res any, err error) {
args := []any{
"JSON.DEBUG",
"MEMORY",
key,
path,
}
return rh.Do(rh.ctx, args...).Result()
}
func (rh *RedisonHandler) JSONForget(key, path string) (int64, error) {
return rh.JSONDel(key, path)
}
func (rh *RedisonHandler) JSONResp(key, path string) (res any, err error) {
args := []any{
"JSON.RESP",
key,
path,
}
return rh.Do(rh.ctx, args...).Result()
}

View File

@ -9,7 +9,9 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"strings" "strings"
"time"
"github.com/go-redis/redis/v8"
"repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/wshandler" "repositories.action2quare.com/ayo/gocommon/wshandler"
@ -84,6 +86,7 @@ type Tavern struct {
type subTavern struct { type subTavern struct {
mongoClient gocommon.MongoClient mongoClient gocommon.MongoClient
redisClient *redis.Client
wsh *wshandler.WebsocketHandler wsh *wshandler.WebsocketHandler
region string region string
groups map[string]group groups map[string]group
@ -141,7 +144,7 @@ func (tv *Tavern) Cleanup() {
} }
func (tv *Tavern) prepare(ctx context.Context) error { func (tv *Tavern) prepare(ctx context.Context) error {
for region := range config.RegionStorage { for region, addr := range config.RegionStorage {
var dbconn gocommon.MongoClient var dbconn gocommon.MongoClient
var err error var err error
var groupinstance group var groupinstance group
@ -154,9 +157,15 @@ func (tv *Tavern) prepare(ctx context.Context) error {
methods[method.Name] = method methods[method.Name] = method
} }
redisClient, err := gocommon.NewRedisClient(addr.Redis["tavern"])
if err != nil {
return err
}
sub := &subTavern{ sub := &subTavern{
wsh: tv.wsh, wsh: tv.wsh,
mongoClient: dbconn, mongoClient: dbconn,
redisClient: redisClient,
region: region, region: region,
methods: methods, methods: methods,
} }
@ -191,7 +200,7 @@ func (tv *Tavern) prepare(ctx context.Context) error {
func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error { func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
for _, sub := range tv.subTaverns { for _, sub := range tv.subTaverns {
tv.wsh.RegisterReceiver(sub.region, sub.clientMessageReceived) tv.wsh.RegisterReceiver(sub.region, sub)
var pattern string var pattern string
if sub.region == "default" { if sub.region == "default" {
pattern = gocommon.MakeHttpHandlerPattern(prefix, "api") pattern = gocommon.MakeHttpHandlerPattern(prefix, "api")
@ -204,11 +213,11 @@ func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux,
return nil return nil
} }
func (sub *subTavern) clientMessageReceived(sender *wshandler.Sender, messageType wshandler.WebSocketMessageType, body io.Reader) { func (sub *subTavern) OnClientMessageReceived(sender *wshandler.Sender, messageType wshandler.WebSocketMessageType, body io.Reader) {
if messageType == wshandler.Connected { if messageType == wshandler.Connected {
logger.Println("OnClientMessageReceived : connected ", sender.Accid.Hex())
} else if messageType == wshandler.Disconnected { } else if messageType == wshandler.Disconnected {
logger.Println("OnClientMessageReceived : disconnected ", sender.Accid.Hex())
} else if messageType == wshandler.BinaryMessage { } else if messageType == wshandler.BinaryMessage {
var msg map[string][]any var msg map[string][]any
dec := json.NewDecoder(body) dec := json.NewDecoder(body)
@ -242,6 +251,20 @@ func (sub *subTavern) clientMessageReceived(sender *wshandler.Sender, messageTyp
} }
} }
func (sub *subTavern) OnRoomCreated(region, name string) {
_, err := sub.redisClient.Persist(context.Background(), name).Result()
if err != nil {
logger.Println("OnRoomCreate Persist failed :", err)
}
}
func (sub *subTavern) OnRoomDestroyed(region, name string) {
_, err := sub.redisClient.Expire(context.Background(), name, 3600*time.Second).Result()
if err != nil {
logger.Println("OnRoomDestroyed Persist failed :", err)
}
}
func (sub *subTavern) api(w http.ResponseWriter, r *http.Request) { func (sub *subTavern) api(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
s := recover() s := recover()

View File

@ -3,16 +3,37 @@ package core
import ( import (
"context" "context"
"fmt"
"testing" "testing"
"time"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
) )
func TestPubSub(t *testing.T) {
opt0, _ := redis.ParseURL("redis://192.168.8.94:6380/0")
opt1, _ := redis.ParseURL("redis://192.168.8.94:6380/1")
rc0 := redis.NewClient(opt0)
rc1 := redis.NewClient(opt1)
go func() {
time.Sleep(time.Second)
rc1.Publish(context.Background(), "__testchan", "real???")
fmt.Println("published")
}()
pubsub := rc0.Subscribe(context.Background(), "__testchan")
msg, err := pubsub.ReceiveMessage(context.Background())
fmt.Println(msg.Payload, err)
}
func TestReJSON(t *testing.T) { func TestReJSON(t *testing.T) {
rc := redis.NewClient(&redis.Options{Addr: "192.168.8.94:6380"}) rc := redis.NewClient(&redis.Options{Addr: "192.168.8.94:6380"})
rh := NewRedisonHandler(context.Background(), rc) rh := gocommon.NewRedisonHandler(context.Background(), rc)
testDoc := map[string]any{ testDoc := map[string]any{
"members": map[string]any{ "members": map[string]any{

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.20
require ( require (
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
go.mongodb.org/mongo-driver v1.11.7 go.mongodb.org/mongo-driver v1.11.7
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003337-29b2f258507d
) )
require ( require (

20
go.sum
View File

@ -112,3 +112,23 @@ repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732 h1
repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e h1:/eG6tAQzEaN178Aib+/erjHrE/+IjIVLRSmP4gx6D7E= repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e h1:/eG6tAQzEaN178Aib+/erjHrE/+IjIVLRSmP4gx6D7E=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718004527-4b35e0e6386b h1:K3YQXnVP/W6LzwGzqOxwKmFUD5IrrNPEWYcN/fSinck=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718004527-4b35e0e6386b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718005518-289af24a8ffa h1:YmzJ1YccK3BxC/NbfB11SEUG1S6Lkz6ejg4kS3q/3Qc=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718005518-289af24a8ffa/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020415-82abcddb497b h1:baO9csa0Esnp7UW+L8zJW/ygpjGHRve4rU2/1pVvXQg=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020415-82abcddb497b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020838-c21017d2cd8b h1:FqLKDrFji0+giFwAJ3oV6dIOR6Sd/aaay76WgWIEVR8=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020838-c21017d2cd8b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718032106-40a603522d40 h1:VyFfS0d6pTX2HbZoDHOxJwag4aVSLOh/LrQXqfSJLBg=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718032106-40a603522d40/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718083804-d724cc84fa94 h1:iQPrRcZ6XfFblpVHxe/CIoWyTj7imF+3edIGSX6ZMM8=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718083804-d724cc84fa94/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718084512-89fa9e4ac585 h1:Wy6qjZ0uHfp02/H688zotRfzYGRPjun7Qay0Z9B/hSg=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718084512-89fa9e4ac585/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718105124-72a683fed2c0 h1:8LmRo2nKaLi4QCmO/agSpNTmCD0EdwFycjHjOweQJp8=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718105124-72a683fed2c0/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003101-256bfd030c29 h1:ADScrqJgmk/TfyOu/6oXD3WkSH8sh3Bw360O8GKuEV8=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003101-256bfd030c29/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003337-29b2f258507d h1:eMzrvVkQfbs5X5dcw80TGGKtJ+6XELl7zNsWiuq4gzs=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003337-29b2f258507d/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=