|
|
|
|
@ -4,6 +4,7 @@ import (
|
|
|
|
|
"context" |
|
|
|
|
"encoding/base64" |
|
|
|
|
"encoding/json" |
|
|
|
|
"errors" |
|
|
|
|
"flag" |
|
|
|
|
"fmt" |
|
|
|
|
"github.com/bytedance/sonic" |
|
|
|
|
@ -16,6 +17,7 @@ import (
|
|
|
|
|
"math/rand" |
|
|
|
|
"net" |
|
|
|
|
"net/http" |
|
|
|
|
"reflect" |
|
|
|
|
"runtime" |
|
|
|
|
"sonet/api/gen/auth" |
|
|
|
|
"sonet/api/gen/chat" |
|
|
|
|
@ -23,6 +25,7 @@ import (
|
|
|
|
|
"sonet/pkg/grpc/discovery" |
|
|
|
|
"sonet/pkg/protocol" |
|
|
|
|
"sonet/pkg/protocol/deliver" |
|
|
|
|
"sonet/pkg/utils/collect" |
|
|
|
|
"sonet/pkg/utils/logger" |
|
|
|
|
"sonet/pkg/utils/security" |
|
|
|
|
"sonet/pkg/utils/shutdown" |
|
|
|
|
@ -41,12 +44,10 @@ var (
|
|
|
|
|
mockNetUsers []*NetUser |
|
|
|
|
sendCounter int64 = 0 |
|
|
|
|
receiverCounter int64 = 0 |
|
|
|
|
//wsUrls = []string{"ws://192.168.110.36:7001", "ws://192.168.110.36:7003"}
|
|
|
|
|
httpClient *http.Client |
|
|
|
|
etcdClient *clientv3.Client |
|
|
|
|
seqId int32 |
|
|
|
|
callbacks map[int32]func(res any) |
|
|
|
|
callbackMutex *sync.Mutex |
|
|
|
|
callbacks *collect.ConcurrentMap[int32, func(res any)] |
|
|
|
|
useMsSum int64 |
|
|
|
|
useMsAvg int64 |
|
|
|
|
) |
|
|
|
|
@ -54,8 +55,11 @@ var (
|
|
|
|
|
func init() { |
|
|
|
|
config.InitLogger(false) |
|
|
|
|
|
|
|
|
|
callbacks = make(map[int32]func(res any), 128) |
|
|
|
|
callbackMutex = &sync.Mutex{} |
|
|
|
|
//callbacks = make(map[int32]func(res any), 128)
|
|
|
|
|
//callbackMutex = &sync.Mutex{}
|
|
|
|
|
callbacks = collect.NewConcurrentMap[int32, func(res any)](16, func(k int32) string { |
|
|
|
|
return strconv.Itoa(int(k)) |
|
|
|
|
}) |
|
|
|
|
httpClient = &http.Client{Timeout: 10 * time.Second} |
|
|
|
|
var err error |
|
|
|
|
etcdClient, err = clientv3.New(clientv3.Config{ |
|
|
|
|
@ -72,10 +76,10 @@ func init() {
|
|
|
|
|
func main() { |
|
|
|
|
runtime.GOMAXPROCS(runtime.NumCPU()) |
|
|
|
|
|
|
|
|
|
flag.StringVar(&gatewayHttp, "gateway", "http://124.222.131.236:30830", "http gateway address") // 192.168.110.41:7000
|
|
|
|
|
flag.StringVar(&benchmarkMode, "mode", "deliver", "benchmark mode: deliver/group") |
|
|
|
|
flag.IntVar(&mockUsers, "users", 1000, "mock users") |
|
|
|
|
flag.IntVar(&eachUserSend, "send", 10000, "each user send msg count") |
|
|
|
|
flag.StringVar(&gatewayHttp, "gateway", "http://192.168.110.41:7000", "http gateway address") // 124.222.131.236:30830
|
|
|
|
|
flag.StringVar(&benchmarkMode, "mode", "group", "benchmark mode: deliver/group") |
|
|
|
|
flag.IntVar(&mockUsers, "users", 100, "mock users") |
|
|
|
|
flag.IntVar(&eachUserSend, "send", 10, "each user send msg count") |
|
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
|
|
if benchmarkMode == "group" { |
|
|
|
|
@ -114,10 +118,11 @@ func benchmark() {
|
|
|
|
|
// go prof.StartPprof(":8888")
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background()) |
|
|
|
|
mockUsersConnect(ctx) |
|
|
|
|
|
|
|
|
|
go records(ctx) |
|
|
|
|
|
|
|
|
|
mockUsersConnect(ctx) |
|
|
|
|
|
|
|
|
|
for i := 0; i < mockUsers; i++ { |
|
|
|
|
netUser := mockNetUsers[i] |
|
|
|
|
go sendBatchChatMessage(ctx, netUser.Uid, netUser.Conn, eachUserSend) |
|
|
|
|
@ -146,32 +151,60 @@ func benchmarkGroup() {
|
|
|
|
|
groupId := "9527" |
|
|
|
|
|
|
|
|
|
mockUsersConnect(ctx) |
|
|
|
|
|
|
|
|
|
// create room
|
|
|
|
|
channel, err := send(mockNetUsers[0].Conn, chat.Chat_ServiceDesc.ServiceName, "RoomCreate", &chat.ReqRoomCreate{Rname: "benchmark"}) |
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
// wait result
|
|
|
|
|
res := <-channel |
|
|
|
|
if err, failed := res.(error); failed { |
|
|
|
|
panic(err) |
|
|
|
|
} else { |
|
|
|
|
groupId = res.(*chat.ResRoomCreate).Room.Rid |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, user := range mockNetUsers { |
|
|
|
|
// join group
|
|
|
|
|
err := groupDeliver.GroupJoin(context.Background(), user.Uid, []string{groupId}) |
|
|
|
|
channel, err := send(user.Conn, chat.Chat_ServiceDesc.ServiceName, "RoomJoin", &chat.ReqRoomJoin{Rid: groupId}) |
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
// wait result
|
|
|
|
|
if err, failed := (<-channel).(error); failed { |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
shutdown.AddHook(func() { |
|
|
|
|
if err := groupDeliver.GroupDissolve(context.Background(), groupId); err != nil { |
|
|
|
|
logger.Error("dissolve group error: ", err) |
|
|
|
|
// delete room
|
|
|
|
|
channel, err := send(mockNetUsers[0].Conn, chat.Chat_ServiceDesc.ServiceName, "RoomDissolve", &chat.ReqRoomDissolve{Rid: groupId}) |
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
// wait result
|
|
|
|
|
if err, failed := (<-channel).(error); failed { |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
logger.Info("test group dissolved") |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
go records(ctx) |
|
|
|
|
|
|
|
|
|
// send group message
|
|
|
|
|
message := &chat.ChatMessage{Sender: "10001", Content: "hi"} |
|
|
|
|
args := &chat.ReqRoomSend{Rid: groupId, Message: "hi"} |
|
|
|
|
for i := 0; i < eachUserSend; i++ { |
|
|
|
|
err := groupDeliver.DeliverGroup(context.Background(), groupId, message) |
|
|
|
|
channel, err := send(mockNetUsers[0].Conn, chat.Chat_ServiceDesc.ServiceName, "RoomSend", args) |
|
|
|
|
if err != nil { |
|
|
|
|
logger.Error("deliver group error:", err) |
|
|
|
|
logger.Error("room send error: ", err) |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
// wait result
|
|
|
|
|
if err, failed := (<-channel).(error); failed { |
|
|
|
|
logger.Error("room send failed: ", err) |
|
|
|
|
} |
|
|
|
|
atomic.AddInt64(&sendCounter, 1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
shutdown.Await() |
|
|
|
|
@ -183,12 +216,17 @@ func mockUsersConnect(ctx context.Context) {
|
|
|
|
|
mockNetUsers = make([]*NetUser, mockUsers) |
|
|
|
|
|
|
|
|
|
concurrent := 100 |
|
|
|
|
wg := sync.WaitGroup{} |
|
|
|
|
wg := &sync.WaitGroup{} |
|
|
|
|
wg.Add(concurrent) |
|
|
|
|
each := (mockUsers / concurrent) + 1 |
|
|
|
|
|
|
|
|
|
for i := 0; i < concurrent; i++ { |
|
|
|
|
//begin, end := i*each, (i+1)*each
|
|
|
|
|
//fmt.Printf("%d: %d~%d \n", i, begin, end)
|
|
|
|
|
|
|
|
|
|
go func(segment int) { |
|
|
|
|
defer wg.Done() |
|
|
|
|
|
|
|
|
|
begin, end := segment*each, (segment+1)*each |
|
|
|
|
for i := begin; i < end && i < mockUsers; i++ { |
|
|
|
|
uid := strconv.Itoa(110000 + i) |
|
|
|
|
@ -204,12 +242,8 @@ func mockUsersConnect(ctx context.Context) {
|
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
mockNetUsers[i] = &NetUser{ |
|
|
|
|
Uid: uid, |
|
|
|
|
Conn: conn, |
|
|
|
|
} |
|
|
|
|
mockNetUsers[i] = &NetUser{Uid: uid, Conn: conn} |
|
|
|
|
} |
|
|
|
|
wg.Done() |
|
|
|
|
}(i) |
|
|
|
|
} |
|
|
|
|
wg.Wait() |
|
|
|
|
@ -221,20 +255,16 @@ func sendBatchChatMessage(ctx context.Context, uid string, conn *websocket.Conn,
|
|
|
|
|
receiverUid := mockNetUsers[r.Intn(mockUsers)].Uid |
|
|
|
|
args := &chat.ReqSend{ |
|
|
|
|
Receiver: receiverUid, |
|
|
|
|
Content: "hello", |
|
|
|
|
Content: "hi", |
|
|
|
|
} |
|
|
|
|
channel, err := send(conn, chat.Chat_ServiceDesc.ServiceName, "Send", args) |
|
|
|
|
if err != nil { |
|
|
|
|
fmt.Println("send failed:", err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res := <-channel // wait result
|
|
|
|
|
err, failed := res.(error) |
|
|
|
|
if failed { |
|
|
|
|
// wait result
|
|
|
|
|
if err, failed := (<-channel).(error); failed { |
|
|
|
|
fmt.Println("send failed:", err) |
|
|
|
|
} else { |
|
|
|
|
// fmt.Println("send ok:", res)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -299,11 +329,16 @@ func handleConn(ctx context.Context, uid string, token string, conn *websocket.C
|
|
|
|
|
if err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
res := <-channel |
|
|
|
|
// fmt.Printf("%v\n", res)
|
|
|
|
|
ch := time.After(time.Second * 5) |
|
|
|
|
|
|
|
|
|
select { |
|
|
|
|
case <-ch: |
|
|
|
|
err = errors.New("req verify timeout") |
|
|
|
|
case res := <-channel: |
|
|
|
|
if e, failed := res.(error); failed { |
|
|
|
|
err = e |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -349,21 +384,6 @@ func listen(ctx context.Context, conn *websocket.Conn) {
|
|
|
|
|
atomic.AddInt64(&receiverCounter, 1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if header.Type == 4 { |
|
|
|
|
callbackMutex.Lock() |
|
|
|
|
callback, ok := callbacks[header.SeqId] |
|
|
|
|
if ok { |
|
|
|
|
delete(callbacks, header.SeqId) |
|
|
|
|
} |
|
|
|
|
callbackMutex.Unlock() |
|
|
|
|
if !ok { |
|
|
|
|
e = fmt.Errorf("callback %d not found", header.SeqId) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
callback(err) |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// notice message
|
|
|
|
|
if header.Type == protocol.TypeNotice { |
|
|
|
|
switch header.Target { |
|
|
|
|
@ -379,46 +399,55 @@ func listen(ctx context.Context, conn *websocket.Conn) {
|
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
callback, ok := callbacks.LoadAndDelete(header.SeqId) |
|
|
|
|
if !ok { |
|
|
|
|
logger.Errorf("callback %d not found", header.SeqId) |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if header.Type == protocol.TypeError { |
|
|
|
|
err = errors.New(string(payload.Body)) |
|
|
|
|
callback(err) |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// rpc response
|
|
|
|
|
|
|
|
|
|
var msg proto.Message |
|
|
|
|
switch header.Svc { |
|
|
|
|
case auth.Auth_ServiceDesc.ServiceName: |
|
|
|
|
switch header.Target { |
|
|
|
|
case "Subject": |
|
|
|
|
// fmt.Println("res verify...")
|
|
|
|
|
msg = &auth.Subject{} |
|
|
|
|
} |
|
|
|
|
case chat.Chat_ServiceDesc.ServiceName: |
|
|
|
|
switch header.Target { |
|
|
|
|
case "ResSend": |
|
|
|
|
// fmt.Println("res send...")
|
|
|
|
|
msg = &chat.ResSend{} |
|
|
|
|
if svc, ok := protoStructs[header.Svc]; ok { |
|
|
|
|
if s, ok := svc[header.Target]; ok { |
|
|
|
|
val := reflect.New(reflect.TypeOf(s).Elem()) |
|
|
|
|
msg = val.Interface().(proto.Message) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if msg == nil { |
|
|
|
|
logger.Warning("unknown rpc response: ", len(payload.Body), header) |
|
|
|
|
|
|
|
|
|
if msg == nil { // protobuf.Empty
|
|
|
|
|
// logger.Warning("unknown rpc response: ", len(payload.Body), header)
|
|
|
|
|
callback(nil) |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
err = proto.Unmarshal(payload.Body, msg) |
|
|
|
|
if e != nil { |
|
|
|
|
if err != nil { |
|
|
|
|
logger.Error("unmarshal rcp res body error: ", payload, err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
callbackMutex.Lock() |
|
|
|
|
callback, ok := callbacks[header.SeqId] |
|
|
|
|
if ok { |
|
|
|
|
delete(callbacks, header.SeqId) |
|
|
|
|
} |
|
|
|
|
callbackMutex.Unlock() |
|
|
|
|
if !ok { |
|
|
|
|
e = fmt.Errorf("callback %d not found", header.SeqId) |
|
|
|
|
callback(fmt.Errorf("unmarshal rcp res body error: %v", err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
callback(msg) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var protoStructs = map[string]map[string]proto.Message{ |
|
|
|
|
auth.Auth_ServiceDesc.ServiceName: { |
|
|
|
|
"Subject": &auth.Subject{}, |
|
|
|
|
}, |
|
|
|
|
chat.Chat_ServiceDesc.ServiceName: { |
|
|
|
|
"ResSend": &chat.ResSend{}, |
|
|
|
|
"ResRoomCreate": &chat.ResRoomCreate{}, |
|
|
|
|
"ResRoomInfo": &chat.ResRoomInfo{}, |
|
|
|
|
"ResRoomList": &chat.ResRoomList{}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func send(conn *websocket.Conn, svc, method string, reqArgs proto.Message) (channel chan any, err error) { |
|
|
|
|
seq := atomic.AddInt32(&seqId, 1) |
|
|
|
|
header := &protocol.Header{ |
|
|
|
|
@ -445,16 +474,14 @@ func send(conn *websocket.Conn, svc, method string, reqArgs proto.Message) (chan
|
|
|
|
|
channel = make(chan any) |
|
|
|
|
|
|
|
|
|
// ready callback
|
|
|
|
|
callbackMutex.Lock() |
|
|
|
|
callbacks[seq] = func(res any) { |
|
|
|
|
callbacks.Store(seq, func(res any) { |
|
|
|
|
ms := time.Now().UnixMilli() - begin |
|
|
|
|
msSum := atomic.AddInt64(&useMsSum, ms) |
|
|
|
|
counter := atomic.AddInt64(&receiverCounter, 1) |
|
|
|
|
atomic.StoreInt64(&useMsAvg, msSum/counter) |
|
|
|
|
|
|
|
|
|
channel <- res |
|
|
|
|
} |
|
|
|
|
callbackMutex.Unlock() |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// send message
|
|
|
|
|
err = conn.WriteMessage(websocket.BinaryMessage, bytes) |