Browse Source

postal cluster redeliver

master
tangmingyou 3 years ago
parent
commit
ff327e03dc
  1. 24
      api/postal.proto
  2. 6
      cmd/gateway_ws/config.toml
  3. 66
      cmd/gateway_ws/main.go
  4. 21
      internal/gateway_ws/gws_server/conn_handler.go
  5. 8
      internal/postal/logic/errors.go
  6. 77
      internal/postal/logic/postal_cluster_server.go
  7. 152
      internal/postal/logic/postal_server.go

24
api/postal.proto

@ -88,23 +88,23 @@ message ResEndpoint {
// ... // ...
service PostalCluster { service PostalCluster {
// socket不在当前节点 // socket不在当前节点
rpc Redirect(ReqRedirect) returns(ResDeliver); // rpc Redirect(ReqRedirect) returns(ResDeliver);
rpc ReDeliver(ReqReDeliver) returns(ResDeliver); rpc Redeliver(ReqRedeliver) returns(ResDeliver);
} }
message ReqReDeliver { message ReqRedeliver {
string receiver = 1; repeated string receivers = 1;
Message msg = 2; Message msg = 2;
int32 offset = 7; // consistent hash node offset, <0, >0, =0 int32 offset = 7; // consistent hash node offset, <0, >0, =0
repeated int64 nodes = 8; // [ip:port,] repeated int64 nodes = 8; // [ip:port,]
} }
message ReqRedirect { //message ReqRedirect {
int32 ttl = 1; // , 0 // int32 ttl = 1; // , 0
int32 redirectMethod = 2; // 10 deliver, 11 deliverBatch, 12 deliverGroup // int32 redirectMethod = 2; // 10 deliver, 11 deliverBatch, 12 deliverGroup
// bool sync = 7; // ,channel顺序投递 // // bool sync = 7; // ,channel顺序投递
ReqDeliver deliver = 10; // ReqDeliver deliver = 10;
ReqDeliverBatch deliverBatch = 11; // ReqDeliverBatch deliverBatch = 11;
ReqDeliverGroup deliverGroup = 12; // ReqDeliverGroup deliverGroup = 12;
} //}

6
cmd/gateway_ws/config.toml

@ -27,12 +27,6 @@ endpoints = ["124.222.131.236:3279"]
username = "root" username = "root"
password = "sopod@etcd" password = "sopod@etcd"
[redis]
Addr = "124.222.131.236:3379"
Password = "sopod@redis#"
DB = 1
MinIdleConns = 3
[nats] [nats]
Url = "nats://nats.sopod@124.222.131.236:3222" Url = "nats://nats.sopod@124.222.131.236:3222"
RetryOnFailedConnect = true RetryOnFailedConnect = true

66
cmd/gateway_ws/main.go

@ -1,8 +1,7 @@
package main package main
import ( import (
"github.com/nats-io/nats.go" "context"
"github.com/redis/go-redis/v9"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
@ -14,11 +13,10 @@ import (
"sonet/pkg/grpc/discovery" "sonet/pkg/grpc/discovery"
"sonet/pkg/grpc/discovery/etcd" "sonet/pkg/grpc/discovery/etcd"
"sonet/pkg/grpc/generic" "sonet/pkg/grpc/generic"
"sonet/pkg/plugins/cache"
"sonet/pkg/plugins/mq" "sonet/pkg/plugins/mq"
"sonet/pkg/protocol/deliver"
"sonet/pkg/protocol/session" "sonet/pkg/protocol/session"
"sonet/pkg/utils/collect" "sonet/pkg/utils/collect"
"sonet/pkg/utils/conver"
"sonet/pkg/utils/shutdown" "sonet/pkg/utils/shutdown"
) )
@ -43,11 +41,6 @@ func main() {
} }
shutdown.AddHook(func() { _ = etcdClient.Close() }, shutdown.WithOrderBack()) shutdown.AddHook(func() { _ = etcdClient.Close() }, shutdown.WithOrderBack())
subjectStore, producer, err := getSubjectMultiCache(appConf, conf.Redis, conf.Nats)
if err != nil {
panic(err)
}
dis := discovery.NewEtcdDiscovery(etcdClient) dis := discovery.NewEtcdDiscovery(etcdClient)
etcdResolver, err := dis.Resolver() etcdResolver, err := dis.Resolver()
if err != nil { if err != nil {
@ -67,7 +60,7 @@ func main() {
postalAddr := etcd.MustRegisterAddress(conf.Grpc.Address) postalAddr := etcd.MustRegisterAddress(conf.Grpc.Address)
//connHandler := server.NewConnHandler(postalAddr, grpcFactory, sessionStore, subjectStore) //connHandler := server.NewConnHandler(postalAddr, grpcFactory, sessionStore, subjectStore)
//httpServer := server.NewHttpServer(connHandler) //httpServer := server.NewHttpServer(connHandler)
gwsHandler := gws_server.NewGwsHandler(postalAddr, grpcFactory, sessionStore, subjectStore) gwsHandler := gws_server.NewGwsHandler(postalAddr, grpcFactory, sessionStore)
httpServer := gws_server.NewGwsServer(gwsHandler) httpServer := gws_server.NewGwsServer(gwsHandler)
httpServer.Init() httpServer.Init()
@ -78,18 +71,15 @@ func main() {
} }
}() }()
// run postal server // run postal cluster server
clientFactory := client.NewGrpcDirectClientFactory(grpc.WithTransportCredentials(insecure.NewCredentials())) ctx, cancel := context.WithCancel(context.Background())
postalServer := logic.NewPostalServer(appConf.EndpointAddress, sessionStore, groupStore, subjectStore, clientFactory, producer) shutdown.AddHook(cancel)
go func() { postalPicker := deliver.NewPostalPicker(dis, grpc.WithTransportCredentials(insecure.NewCredentials()))
err = postalServer.Run(conf.Grpc, dis) if err = postalPicker.Init(ctx); err != nil {
if err != nil {
panic(err) panic(err)
} }
}() clientFactory := client.NewGrpcDirectClientFactory(grpc.WithTransportCredentials(insecure.NewCredentials()))
postalClusterServer := logic.NewPostalClusterServer(sessionStore, postalPicker, clientFactory)
// run postal cluster server
postalClusterServer := logic.NewPostalClusterServer(postalServer, nil)
go func() { go func() {
err := postalClusterServer.Run(conf.Grpc.Address, config.GetGrpcOptions(conf.Grpc)...) err := postalClusterServer.Run(conf.Grpc.Address, config.GetGrpcOptions(conf.Grpc)...)
if err != nil { if err != nil {
@ -97,36 +87,20 @@ func main() {
} }
}() }()
shutdown.Await() // run postal server
}
func getSubjectMultiCache(appConf *GatewayWsConfig, redisOptions redis.Options, natsOptions nats.Options) (*cache.LocalRemoteCache, mq.Producer, error) {
// initial cache...
rdb := redis.NewClient(&redisOptions)
subjectRedisCache := cache.NewRedisCache(appConf.SubjectCacheTopic, rdb)
shutdown.AddHook(func() { _ = rdb.Close() })
// nats mq // nats mq
producer, err := mq.NewNatsProducer(natsOptions) producer, err := mq.NewNatsProducer(conf.Nats)
if err != nil { if err != nil {
return nil, nil, err panic(err)
} }
shutdown.AddHook(func() { producer.Stop() }) shutdown.AddHook(producer.Stop)
consumer, err := mq.NewNatsConsumer(natsOptions) postalServer := logic.NewPostalServer(appConf.EndpointAddress, sessionStore, groupStore, postalClusterServer, producer)
go func() {
err = postalServer.Run(conf.Grpc, dis)
if err != nil { if err != nil {
return nil, nil, err panic(err)
} }
shutdown.AddHook(func() { consumer.Stop() }) }()
// 多级缓存 shutdown.Await()
subjectLrcOpts := cache.LocalRemoteCacheOptions{
Topic: appConf.SubjectCacheTopic,
LocalExpiration: conver.MustParseDuration(appConf.SubjectLrcExpiration),
CleanupInterval: conver.MustParseDuration(appConf.SubjectLrcCleanupInterval),
Remote: subjectRedisCache,
Producer: producer,
Consumer: consumer,
}
subjectLrc, err := cache.NewLocalRemoteCache(subjectLrcOpts)
return subjectLrc, producer, err
} }

21
internal/gateway_ws/gws_server/conn_handler.go

@ -8,7 +8,6 @@ import (
"regexp" "regexp"
"sonet/api/gen/auth" "sonet/api/gen/auth"
"sonet/pkg/grpc/generic" "sonet/pkg/grpc/generic"
"sonet/pkg/plugins/cache"
"sonet/pkg/protocol" "sonet/pkg/protocol"
"sonet/pkg/protocol/session" "sonet/pkg/protocol/session"
"sonet/pkg/utils/logger" "sonet/pkg/utils/logger"
@ -26,18 +25,16 @@ type GwsHandler struct {
postalServerAddress string postalServerAddress string
grpcFactory *generic.GrpcGenericClientFactory grpcFactory *generic.GrpcGenericClientFactory
sessionStore session.Store // <string, *session.NetSubject> 当前连接用户,内存缓存 sessionStore session.Store // <string, *session.NetSubject> 当前连接用户,内存缓存
subjectStore cache.MultiLevelCache
} }
func NewGwsHandler(postalServerAddress string, func NewGwsHandler(postalServerAddress string,
grpcFactory *generic.GrpcGenericClientFactory, grpcFactory *generic.GrpcGenericClientFactory,
sessionStore session.Store, sessionStore session.Store,
subjectStore cache.MultiLevelCache) *GwsHandler { ) *GwsHandler {
return &GwsHandler{ return &GwsHandler{
grpcFactory: grpcFactory, grpcFactory: grpcFactory,
postalServerAddress: postalServerAddress, postalServerAddress: postalServerAddress,
sessionStore: sessionStore, sessionStore: sessionStore,
subjectStore: subjectStore,
} }
} }
@ -56,10 +53,6 @@ func (c *GwsHandler) OnClose(socket *gws.Conn, err error) {
} }
uid := val.(string) uid := val.(string)
c.sessionStore.Delete(uid) c.sessionStore.Delete(uid)
// todo offline event
if err = c.subjectStore.Del(context.Background(), uid); err != nil {
logger.Error("del offline subject store error: ", err)
}
logger.Info("subject offline: ", uid) logger.Info("subject offline: ", uid)
} }
@ -179,22 +172,10 @@ func (c *GwsHandler) extractAuthVerify(conn *gwsConn, resp []byte) (sessionUid s
} }
// 设置连接身份信息 // 设置连接身份信息
sessionUid = authSubject.Uid sessionUid = authSubject.Uid
gateSubject := &session.GateSubject{
Uid: authSubject.Uid,
Online: 1,
Time: time.Now().UnixMilli(),
Gate: c.postalServerAddress,
}
// 存储session
err = c.subjectStore.Set(context.Background(), sessionUid, gateSubject) // store to redis cluster cache
if err != nil {
return
}
// 关闭旧的链接 // 关闭旧的链接
oldChannel, ok := c.sessionStore.Load(sessionUid) oldChannel, ok := c.sessionStore.Load(sessionUid)
if ok { if ok {
// TODO nats offline / force load subject target cluster call offline
oldChannel.Conn.(*gwsConn).conn.WriteClose(1000, nil) oldChannel.Conn.(*gwsConn).conn.WriteClose(1000, nil)
logger.Infof("close subject old conn: %s\n", sessionUid) logger.Infof("close subject old conn: %s\n", sessionUid)
} }

8
internal/postal/logic/errors.go

@ -0,0 +1,8 @@
package logic
import "errors"
var (
errRedeliverTtlEnd = errors.New("redeliver ttl ended")
errRedeliverOffsetNodeRepeat = errors.New("redeliver offset node repeat")
)

77
internal/postal/logic/postal_cluster_server.go

@ -2,11 +2,11 @@ package logic
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"google.golang.org/grpc" "google.golang.org/grpc"
"net" "net"
"sonet/api/gen/postal" "sonet/api/gen/postal"
"sonet/pkg/grpc/client"
"sonet/pkg/protocol/deliver" "sonet/pkg/protocol/deliver"
"sonet/pkg/protocol/session" "sonet/pkg/protocol/session"
"sonet/pkg/utils/logger" "sonet/pkg/utils/logger"
@ -28,24 +28,23 @@ func PostalAddr2Cluster(postalServerAddr string) (string, error) {
return fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port+PostalClusterPortOffset), nil return fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port+PostalClusterPortOffset), nil
} }
var (
methodRedirectDeliver int32 = 10
methodRedirectDeliverBatch int32 = 11
methodRedirectDeliverGroup int32 = 12
)
// PostalClusterServer 用于postal server集群间, 消息转发通信 // PostalClusterServer 用于postal server集群间, 消息转发通信
type PostalClusterServer struct { type PostalClusterServer struct {
postal.UnimplementedPostalClusterServer postal.UnimplementedPostalClusterServer
postalServer postal.PostalServer // 当前节点 postal server
postalPicker *deliver.PostalPicker
sessionStore session.Store sessionStore session.Store
postalPicker *deliver.PostalPicker
clientFactory *client.GrpcDirectClientFactory // todo 清理过期连接
} }
func NewPostalClusterServer(postalServer postal.PostalServer, postalPicker *deliver.PostalPicker) *PostalClusterServer { func NewPostalClusterServer(
sessionStore session.Store,
postalPicker *deliver.PostalPicker,
clientFactory *client.GrpcDirectClientFactory,
) *PostalClusterServer {
return &PostalClusterServer{ return &PostalClusterServer{
postalServer: postalServer, sessionStore: sessionStore,
postalPicker: postalPicker, postalPicker: postalPicker,
clientFactory: clientFactory,
} }
} }
@ -69,58 +68,72 @@ func (s *PostalClusterServer) Run(postalAddr string, opts ...grpc.ServerOption)
return return
} }
func (s *PostalClusterServer) Redirect(ctx context.Context, req *postal.ReqRedirect) (*postal.ResDeliver, error) { func (s *PostalClusterServer) Redeliver(ctx context.Context, req *postal.ReqRedeliver) (res *postal.ResDeliver, err error) {
req.Ttl -= 1
switch req.RedirectMethod {
case methodRedirectDeliver:
return s.postalServer.Deliver(ctx, req.Deliver)
case methodRedirectDeliverBatch:
return s.postalServer.DeliverBatch(ctx, req.DeliverBatch)
case methodRedirectDeliverGroup:
return s.postalServer.DeliverGroup(ctx, req.DeliverGroup)
}
return nil, errors.New(fmt.Sprintf("not found redirect type %d", req.RedirectMethod))
}
func (s *PostalClusterServer) ReDeliver(ctx context.Context, req *postal.ReqReDeliver) (res *postal.ResDeliver, err error) {
channel, ok := s.sessionStore.Load(req.Receiver)
if ok {
bytes, err := encodeDeliverMessage(req.Msg) bytes, err := encodeDeliverMessage(req.Msg)
if err != nil { if err != nil {
return return
} }
var redelivers []string
for _, receiver := range req.Receivers {
channel, ok := s.sessionStore.Load(receiver)
if !ok {
redelivers = append(redelivers, receiver)
continue
}
err = channel.Conn.Write(bytes) err = channel.Conn.Write(bytes)
if err != nil { if err != nil {
return logger.Errorf("redeliver channel %s write error: %v", receiver, err)
continue
} }
}
if len(redelivers) == 0 {
res = &postal.ResDeliver{Ok: true} res = &postal.ResDeliver{Ok: true}
return return
} }
req.Receivers = redelivers
// 重新投递 // 重新投递
var next int var next int
next, req.Offset = convergeOffset(req.Offset) next, req.Offset = convergeOffset(req.Offset)
if next == 0 || req.Offset == 0 { if next == 0 || req.Offset == 0 {
// 重投次数结束 // 重投次数结束
err = errRedeliverTtlEnd
return return
} }
node, _, err := s.postalPicker.PickOffsetNode(req.Receiver, next)
node, _, err := s.postalPicker.PickOffsetNode(redelivers[0], next)
if err != nil { if err != nil {
return return
} }
addr, err := nets.Address2i64(node.GetAddr()) addr := node.GetAddr()
addrI64, err := nets.Address2i64(addr)
if err != nil { if err != nil {
return return
} }
for _, nodeI64 := range req.Nodes { for _, nodeI64 := range req.Nodes {
if addr == nodeI64 { if addrI64 == nodeI64 {
// 重复投递节点, 结束投递 // 重复投递节点, 结束投递
err = errRedeliverOffsetNodeRepeat
return return
} }
} }
req.Nodes = append(req.Nodes, addr) // 再次投递
req.Nodes = append(req.Nodes, addrI64)
// postal集群中转发消息
clusterAddr, err := PostalAddr2Cluster(addr)
if err != nil {
logger.Errorf("parse postal server addr error: %s", addr, err)
return
}
conn, err := s.clientFactory.GetConn(ctx, clusterAddr)
if err != nil {
logger.Error("get postal cluster conn error: ", err)
return nil, err
}
clusterClient := postal.NewPostalClusterClient(conn)
res, err = clusterClient.Redeliver(ctx, req)
return return
} }

152
internal/postal/logic/postal_server.go

@ -10,12 +10,9 @@ import (
"sonet/api/gen/postal" "sonet/api/gen/postal"
"sonet/internal/postal/group" "sonet/internal/postal/group"
"sonet/pkg/config" "sonet/pkg/config"
"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/mq" "sonet/pkg/plugins/mq"
"sonet/pkg/protocol"
"sonet/pkg/protocol/event" "sonet/pkg/protocol/event"
"sonet/pkg/protocol/session" "sonet/pkg/protocol/session"
"sonet/pkg/utils/collect" "sonet/pkg/utils/collect"
@ -29,8 +26,7 @@ type PostalServer struct {
broadcastAddress string // postal server 在注册中心注册的地址, 其他服务可直连访问 broadcastAddress string // postal server 在注册中心注册的地址, 其他服务可直连访问
sessionStore session.Store // k:uid 在线用户conn存储 sessionStore session.Store // k:uid 在线用户conn存储
groupStore *collect.ConcurrentMap[string, *group.Group] // k:gid 在线用户 groups 存储 groupStore *collect.ConcurrentMap[string, *group.Group] // k:gid 在线用户 groups 存储
subjectStore cache.MultiLevelCache // online subject address cache, ws gateway集群所有在线用户存储 postalClusterServer *PostalClusterServer
clientFactory *client.GrpcDirectClientFactory
producer mq.Producer producer mq.Producer
} }
@ -38,15 +34,13 @@ func NewPostalServer(
endpointAddress string, endpointAddress string,
sessionStore session.Store, sessionStore session.Store,
groupStore *collect.ConcurrentMap[string, *group.Group], groupStore *collect.ConcurrentMap[string, *group.Group],
subjectStore cache.MultiLevelCache, postalClusterServer *PostalClusterServer,
clientFactory *client.GrpcDirectClientFactory,
producer mq.Producer) *PostalServer { producer mq.Producer) *PostalServer {
return &PostalServer{ return &PostalServer{
endpointAddress: endpointAddress, endpointAddress: endpointAddress,
sessionStore: sessionStore, sessionStore: sessionStore,
groupStore: groupStore, groupStore: groupStore,
subjectStore: subjectStore, postalClusterServer: postalClusterServer,
clientFactory: clientFactory,
producer: producer, producer: producer,
} }
} }
@ -134,55 +128,6 @@ func (s *PostalServer) processSessionStoreEvent(ctx context.Context) {
} }
} }
func (s *PostalServer) deliverMessage(msg *postal.Message, conn session.NetConn) error {
header := &protocol.Header{
Magic: protocol.Magic,
Type: protocol.TypeNotice,
UrlType: 1,
SerializeType: 1,
Svc: msg.Svc,
Target: msg.Msg,
}
payload := &protocol.Payload{Header: header, Body: msg.Body}
bytes, err := protocol.EncodeSo(payload)
if err != nil {
logger.Error("req deliver encode notice error: ", err)
return err
}
return conn.Write(bytes)
}
// receiverGates 找receiver在集群内哪些其他节点
func (s *PostalServer) clusterGateReceivers(ctx context.Context, receivers []string) (gateReceivers map[string][]string, offline []string) {
gateReceivers = make(map[string][]string)
for _, receiver := range receivers {
subject := &session.GateSubject{}
err := s.subjectStore.Load(ctx, receiver, subject)
if err != nil {
offline = append(offline, receiver)
if err != cache.NotExists {
logger.Error("load from subject store error: ", err)
offline = append(offline, receiver)
}
continue
}
if subject.Gate == s.broadcastAddress {
offline = append(offline, receiver)
// 清除失效缓存
err := s.subjectStore.Del(ctx, receiver)
if err != nil {
logger.Error("del subject store error: ", receiver, err)
}
continue
}
// put receiver gate addr
gateReceivers[subject.Gate] = append(gateReceivers[subject.Gate], receiver)
}
return
}
func (s *PostalServer) Deliver(ctx context.Context, req *postal.ReqDeliver) (res *postal.ResDeliver, err 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 {
@ -199,40 +144,20 @@ func (s *PostalServer) Deliver(ctx context.Context, req *postal.ReqDeliver) (res
res = &postal.ResDeliver{Ok: true} res = &postal.ResDeliver{Ok: true}
return return
} }
// todo 向一致性 hash 下一个节点传递 // 向一致性 hash 下一个节点传递
// todo picker reqCluster offset +-3 // picker reqCluster offset +3:
for _, offset := range []int32{3, -3} {
// 用户连接不在当前gateway res, err = s.postalClusterServer.Redeliver(ctx, &postal.ReqRedeliver{
gateReceivers, offline := s.clusterGateReceivers(ctx, []string{req.Receiver}) Receivers: []string{req.Receiver},
if offline != nil && len(offline) > 0 { Msg: req.Msg,
res = &postal.ResDeliver{Code: int32(postal.DeliverResult_ReceiverOffline.Number())} Offset: offset,
})
if err == nil {
res = &postal.ResDeliver{Ok: true}
return return
} }
for postalAddr := range gateReceivers {
// gateway集群中转发消息
clusterAddr, err := PostalAddr2Cluster(postalAddr)
if err != nil {
logger.Errorf("parse postal server addr error: %s", postalAddr, err)
continue
}
conn, err := s.clientFactory.GetConn(context.Background(), clusterAddr)
if err != nil {
logger.Error("get postal cluster conn error: ", err)
return nil, err
}
clusterClient := postal.NewPostalClusterClient(conn)
reqRedirect := &postal.ReqRedirect{
Ttl: 3, // TODO 转发n次就丢弃
RedirectMethod: methodRedirectDeliver,
Deliver: req,
}
resp, err := clusterClient.Redirect(ctx, reqRedirect)
return resp, err
} }
return
return nil, nil
} }
func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverBatch) (res *postal.ResDeliver, err error) { func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverBatch) (res *postal.ResDeliver, err error) {
@ -241,11 +166,11 @@ func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverB
return return
} }
var redirectReceivers []string var redeliverReceivers []string
for _, receiverId := range req.Receivers { for _, receiverId := range req.Receivers {
receiver, ok := s.sessionStore.Load(receiverId) receiver, ok := s.sessionStore.Load(receiverId)
if !ok { if !ok {
redirectReceivers = append(redirectReceivers, receiverId) redeliverReceivers = append(redeliverReceivers, receiverId)
continue continue
} }
err = receiver.Conn.Write(bytes) err = receiver.Conn.Write(bytes)
@ -254,44 +179,23 @@ func (s *PostalServer) DeliverBatch(ctx context.Context, req *postal.ReqDeliverB
} }
} }
if len(redirectReceivers) == 0 { if len(redeliverReceivers) == 0 {
return &postal.ResDeliver{Ok: true}, nil return &postal.ResDeliver{Ok: true}, nil
} }
// todo 向一致性 hash 下个节点传递 // 向一致性 hash 下一个节点传递
gateReceivers, offline := s.clusterGateReceivers(ctx, redirectReceivers) for _, offset := range []int32{3, -3} {
if len(offline) > 0 { res, err = s.postalClusterServer.Redeliver(ctx, &postal.ReqRedeliver{
logger.Warning("offline redirect receivers: ", offline) Receivers: redeliverReceivers,
} Msg: req.Msg,
Offset: offset,
if len(gateReceivers) > 0 { })
for postalAddr, receivers := range gateReceivers { if err == nil {
// gateway集群中转发消息 res = &postal.ResDeliver{Ok: true}
clusterAddr, err := PostalAddr2Cluster(postalAddr) return
if err != nil {
logger.Errorf("parse postal server addr error: %s", postalAddr, err)
continue
}
conn, err := s.clientFactory.GetConn(ctx, clusterAddr)
if err != nil {
logger.Error("get postal cluster conn error: ", err)
continue
}
clusterClient := postal.NewPostalClusterClient(conn)
req.Receivers = receivers
reqRedirect := &postal.ReqRedirect{
Ttl: 3, // TODO 转发n次就丢弃
RedirectMethod: methodRedirectDeliverBatch,
DeliverBatch: req,
}
_, err = clusterClient.Redirect(ctx, reqRedirect)
if err != nil {
logger.Error("redirect batch error: ", err)
}
} }
} }
return &postal.ResDeliver{Ok: true}, nil return
} }
// DeliverGroup postal broadcast // DeliverGroup postal broadcast

Loading…
Cancel
Save