|
|
|
@ -2,18 +2,22 @@ package logic |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"context" |
|
|
|
"context" |
|
|
|
|
|
|
|
"errors" |
|
|
|
"google.golang.org/grpc" |
|
|
|
"google.golang.org/grpc" |
|
|
|
"google.golang.org/grpc/reflection" |
|
|
|
"google.golang.org/grpc/reflection" |
|
|
|
"google.golang.org/protobuf/types/known/emptypb" |
|
|
|
"google.golang.org/protobuf/types/known/emptypb" |
|
|
|
"net" |
|
|
|
"net" |
|
|
|
"sonet/api/gen/postal" |
|
|
|
"sonet/api/gen/postal" |
|
|
|
|
|
|
|
"sonet/internal/postal/group" |
|
|
|
"sonet/pkg/config" |
|
|
|
"sonet/pkg/config" |
|
|
|
"sonet/pkg/grpc/client" |
|
|
|
"sonet/pkg/grpc/client" |
|
|
|
"sonet/pkg/grpc/discovery" |
|
|
|
"sonet/pkg/grpc/discovery" |
|
|
|
"sonet/pkg/grpc/interceptor" |
|
|
|
"sonet/pkg/grpc/interceptor" |
|
|
|
"sonet/pkg/plugins/cache" |
|
|
|
"sonet/pkg/plugins/cache" |
|
|
|
|
|
|
|
"sonet/pkg/plugins/mq" |
|
|
|
"sonet/pkg/protocol" |
|
|
|
"sonet/pkg/protocol" |
|
|
|
"sonet/pkg/protocol/session" |
|
|
|
"sonet/pkg/protocol/session" |
|
|
|
|
|
|
|
"sonet/pkg/utils/collect" |
|
|
|
"sonet/pkg/utils/logger" |
|
|
|
"sonet/pkg/utils/logger" |
|
|
|
"sonet/pkg/utils/shutdown" |
|
|
|
"sonet/pkg/utils/shutdown" |
|
|
|
) |
|
|
|
) |
|
|
|
@ -22,21 +26,27 @@ type PostalServer struct { |
|
|
|
postal.UnimplementedPostalServer |
|
|
|
postal.UnimplementedPostalServer |
|
|
|
endpointAddress string // websocket 前端连接地址
|
|
|
|
endpointAddress string // websocket 前端连接地址
|
|
|
|
broadcastAddress string // postal server 在注册中心注册的地址, 其他服务可直连访问
|
|
|
|
broadcastAddress string // postal server 在注册中心注册的地址, 其他服务可直连访问
|
|
|
|
sessionStore session.Store // 在线用户conn存储
|
|
|
|
sessionStore session.Store // k:uid 在线用户conn存储
|
|
|
|
|
|
|
|
groupStore *collect.ConcurrentMap[string, *group.Group] // k:gid 在线用户 groups 存储
|
|
|
|
subjectStore cache.MultiLevelCache // online subject address cache, ws gateway集群所有在线用户存储
|
|
|
|
subjectStore cache.MultiLevelCache // online subject address cache, ws gateway集群所有在线用户存储
|
|
|
|
clientFactory *client.GrpcDirectClientFactory |
|
|
|
clientFactory *client.GrpcDirectClientFactory |
|
|
|
|
|
|
|
producer mq.Producer |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewPostalServer( |
|
|
|
func NewPostalServer( |
|
|
|
endpointAddress string, |
|
|
|
endpointAddress string, |
|
|
|
sessionStore session.Store, |
|
|
|
sessionStore session.Store, |
|
|
|
|
|
|
|
groupStore *collect.ConcurrentMap[string, *group.Group], |
|
|
|
subjectStore cache.MultiLevelCache, |
|
|
|
subjectStore cache.MultiLevelCache, |
|
|
|
clientFactory *client.GrpcDirectClientFactory) *PostalServer { |
|
|
|
clientFactory *client.GrpcDirectClientFactory, |
|
|
|
|
|
|
|
producer mq.Producer) *PostalServer { |
|
|
|
return &PostalServer{ |
|
|
|
return &PostalServer{ |
|
|
|
endpointAddress: endpointAddress, |
|
|
|
endpointAddress: endpointAddress, |
|
|
|
sessionStore: sessionStore, |
|
|
|
sessionStore: sessionStore, |
|
|
|
|
|
|
|
groupStore: groupStore, |
|
|
|
subjectStore: subjectStore, |
|
|
|
subjectStore: subjectStore, |
|
|
|
clientFactory: clientFactory, |
|
|
|
clientFactory: clientFactory, |
|
|
|
|
|
|
|
producer: producer, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -72,6 +82,8 @@ func (s *PostalServer) Run(conf config.GrpcConfig, registry discovery.Registry) |
|
|
|
} |
|
|
|
} |
|
|
|
shutdown.AddHook(cancel) |
|
|
|
shutdown.AddHook(cancel) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
go s.processSessionStoreEvent(ctx) |
|
|
|
|
|
|
|
|
|
|
|
// 其他服务直连地址
|
|
|
|
// 其他服务直连地址
|
|
|
|
s.broadcastAddress = register.Addr |
|
|
|
s.broadcastAddress = register.Addr |
|
|
|
|
|
|
|
|
|
|
|
@ -81,6 +93,29 @@ func (s *PostalServer) Run(conf config.GrpcConfig, registry discovery.Registry) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) processSessionStoreEvent(ctx context.Context) { |
|
|
|
|
|
|
|
for { |
|
|
|
|
|
|
|
select { |
|
|
|
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case uid := <-s.sessionStore.OnStore(): |
|
|
|
|
|
|
|
logger.Info("uid %s online", uid) |
|
|
|
|
|
|
|
// todo mq event..., delay 5s
|
|
|
|
|
|
|
|
// s.producer.Publish()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case channel := <-s.sessionStore.OnDelete(): |
|
|
|
|
|
|
|
logger.Info("uid %s offline", channel.Uid) |
|
|
|
|
|
|
|
for _, gid := range channel.Groups() { |
|
|
|
|
|
|
|
if g, ok := s.groupStore.Load(gid); ok { |
|
|
|
|
|
|
|
g.Leave(channel.Uid) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// todo mq event...
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) deliverMessage(msg *postal.Message, conn session.NetConn) error { |
|
|
|
func (s *PostalServer) deliverMessage(msg *postal.Message, conn session.NetConn) error { |
|
|
|
header := &protocol.Header{ |
|
|
|
header := &protocol.Header{ |
|
|
|
Magic: protocol.Magic, |
|
|
|
Magic: protocol.Magic, |
|
|
|
@ -129,20 +164,30 @@ func (s *PostalServer) clusterGateReceivers(ctx context.Context, receivers []str |
|
|
|
} |
|
|
|
} |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
func (s *PostalServer) Deliver(ctx context.Context, req *postal.ReqDeliver) (*postal.ResDeliver, error) { |
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) Deliver(ctx context.Context, req *postal.ReqDeliver) (res *postal.ResDeliver, err error) { |
|
|
|
receiver, ok := s.sessionStore.Load(req.Receiver) |
|
|
|
receiver, ok := s.sessionStore.Load(req.Receiver) |
|
|
|
if ok { |
|
|
|
if ok { |
|
|
|
err := s.deliverMessage(req.Msg, receiver) |
|
|
|
var bytes []byte |
|
|
|
|
|
|
|
bytes, err = encodeDeliverMessage(req.Msg) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
return &postal.ResDeliver{Ok: true}, nil |
|
|
|
// write msg
|
|
|
|
|
|
|
|
err = receiver.Conn.Write(bytes) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
res = &postal.ResDeliver{Ok: true} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 用户连接不在当前gateway
|
|
|
|
// 用户连接不在当前gateway
|
|
|
|
|
|
|
|
// todo 向一致性 hash 下一个节点传递
|
|
|
|
gateReceivers, offline := s.clusterGateReceivers(ctx, []string{req.Receiver}) |
|
|
|
gateReceivers, offline := s.clusterGateReceivers(ctx, []string{req.Receiver}) |
|
|
|
if offline != nil && len(offline) > 0 { |
|
|
|
if offline != nil && len(offline) > 0 { |
|
|
|
return &postal.ResDeliver{Code: int32(postal.DeliverResult_ReceiverOffline.Number())}, nil |
|
|
|
res = &postal.ResDeliver{Code: int32(postal.DeliverResult_ReceiverOffline.Number())} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for postalAddr := range gateReceivers { |
|
|
|
for postalAddr := range gateReceivers { |
|
|
|
@ -171,7 +216,12 @@ func (s *PostalServer) Deliver(ctx context.Context, req *postal.ReqDeliver) (*po |
|
|
|
return nil, nil |
|
|
|
return nil, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverBatch) (*postal.ResDeliver, error) { |
|
|
|
func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverBatch) (res *postal.ResDeliver, err error) { |
|
|
|
|
|
|
|
bytes, err := encodeDeliverMessage(req.Msg) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var redirectReceivers []string |
|
|
|
var redirectReceivers []string |
|
|
|
for _, receiverId := range req.Receivers { |
|
|
|
for _, receiverId := range req.Receivers { |
|
|
|
receiver, ok := s.sessionStore.Load(receiverId) |
|
|
|
receiver, ok := s.sessionStore.Load(receiverId) |
|
|
|
@ -179,7 +229,7 @@ func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverB |
|
|
|
redirectReceivers = append(redirectReceivers, receiverId) |
|
|
|
redirectReceivers = append(redirectReceivers, receiverId) |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
err := s.deliverMessage(req.Msg, receiver) |
|
|
|
err = receiver.Conn.Write(bytes) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
logger.Errorf("deliver to %s error: ", receiver, err) |
|
|
|
logger.Errorf("deliver to %s error: ", receiver, err) |
|
|
|
} |
|
|
|
} |
|
|
|
@ -189,6 +239,7 @@ func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverB |
|
|
|
return &postal.ResDeliver{Ok: true}, nil |
|
|
|
return &postal.ResDeliver{Ok: true}, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// todo 向一致性 hash 下个节点传递
|
|
|
|
gateReceivers, offline := s.clusterGateReceivers(ctx, redirectReceivers) |
|
|
|
gateReceivers, offline := s.clusterGateReceivers(ctx, redirectReceivers) |
|
|
|
if len(offline) > 0 { |
|
|
|
if len(offline) > 0 { |
|
|
|
logger.Warning("offline redirect receivers: ", offline) |
|
|
|
logger.Warning("offline redirect receivers: ", offline) |
|
|
|
@ -224,22 +275,59 @@ func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverB |
|
|
|
return &postal.ResDeliver{Ok: true}, nil |
|
|
|
return &postal.ResDeliver{Ok: true}, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DeliverGroup group:
|
|
|
|
// DeliverGroup postal broadcast
|
|
|
|
func (s *PostalServer) DeliverGroup(ctx context.Context, group *postal.ReqDeliverGroup) (*postal.ResDeliver, error) { |
|
|
|
func (s *PostalServer) DeliverGroup(ctx context.Context, req *postal.ReqDeliverGroup) (res *postal.ResDeliver, err error) { |
|
|
|
|
|
|
|
bytes, err := encodeDeliverMessage(req.Msg) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return nil, nil |
|
|
|
res = &postal.ResDeliver{Ok: true} |
|
|
|
|
|
|
|
g, ok := s.groupStore.Load(req.Gid) |
|
|
|
|
|
|
|
if !ok { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
g.Write(bytes) |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) GroupJoin(ctx context.Context, join *postal.ReqGroupJoin) (*emptypb.Empty, error) { |
|
|
|
// GroupJoin uid consistent hash
|
|
|
|
return nil, nil |
|
|
|
func (s *PostalServer) GroupJoin(ctx context.Context, req *postal.ReqGroupJoin) (emp *emptypb.Empty, err error) { |
|
|
|
|
|
|
|
channel, ok := s.sessionStore.Load(req.Uid) |
|
|
|
|
|
|
|
if !ok { |
|
|
|
|
|
|
|
err = errors.New("uid not online") |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for _, gid := range req.Gids { |
|
|
|
|
|
|
|
g, _ := s.groupStore.ComputeIfAbsent(gid, func(gid string) *group.Group { return group.NewGroup(gid) }) |
|
|
|
|
|
|
|
g.Join(req.Uid, channel.Conn) |
|
|
|
|
|
|
|
channel.GroupJoin(gid) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) GroupLeave(ctx context.Context, leave *postal.ReqGroupLeave) (*emptypb.Empty, error) { |
|
|
|
// GroupLeave uid consistent hash
|
|
|
|
return nil, nil |
|
|
|
func (s *PostalServer) GroupLeave(ctx context.Context, req *postal.ReqGroupLeave) (emp *emptypb.Empty, err error) { |
|
|
|
|
|
|
|
channel, ok := s.sessionStore.Load(req.Uid) |
|
|
|
|
|
|
|
if !ok { |
|
|
|
|
|
|
|
err = errors.New("uid not online") |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for _, gid := range req.Gids { |
|
|
|
|
|
|
|
g, ok := s.groupStore.Load(gid) |
|
|
|
|
|
|
|
if !ok { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
g.Leave(req.Uid) |
|
|
|
|
|
|
|
channel.GroupLeave(gid) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) GroupDissolve(ctx context.Context, dissolve *postal.ReqGroupDissolve) (*emptypb.Empty, error) { |
|
|
|
// GroupDissolve postal broadcast
|
|
|
|
return nil, nil |
|
|
|
func (s *PostalServer) GroupDissolve(ctx context.Context, req *postal.ReqGroupDissolve) (emp *emptypb.Empty, err error) { |
|
|
|
|
|
|
|
s.groupStore.Delete(req.Gid) |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (s *PostalServer) Endpoint(context.Context, *emptypb.Empty) (*postal.ResEndpoint, error) { |
|
|
|
func (s *PostalServer) Endpoint(context.Context, *emptypb.Empty) (*postal.ResEndpoint, error) { |
|
|
|
|