SmartRoute之大规模消息转发集群实现

   消息转发的应用场景在现实中的应用非常普遍,我们常用的IM工具也是其中之一;现有很多云平台也提供了这种基础服务,可以让APP更容易集成相关功能而不必投入相应的开发成本。对于实现这样一个简单功能并不复杂,对于现有的技术来说用.net提个通讯服务器支持几十W用户相信也不是件困难的事情;但如果考虑可用性和更大规模那就需要下点功夫,并且对相关技术有深入的了解才能实现了。而在这里主要讲解一下如何通过SmartRoute来实现一个大规模的消息转发集群的基础服务。

        说到集群那肯定由N个服务组成的一组应,那做一个消息转发集群的基础服务需要那些服务节点呢?分析一下主要包括两大块:注册中心和消息网关;网关用于和应用对接,而注册中心则是明确应用所在位置。为了达到更好的可用性和更大规模支撑注册中心和网关都是N-N的关系。

    

        看到这样一个图估计会把很不了解这方面的朋友会卡住,这样一个东西实现会很复杂吧!其实在SmartRoute基础之上实现这样这样一个集群服务并不困难,不过对于消息交互原理和设计还是需要了解一下。接下来讲解一下如何用SmartRoute实现相应注册中心和网关服务。

注册中心

      注册中心的作用很简单就是保存应用标识所在位置,当网关需要转发消息的时候告诉网关这个应用标识在那个位置上。除了这一功能外当然还要考虑可用性,主要包括多中心发现和注册信息现步等;同样网关也具行指向多台中心的负载能力。

1
2
3
4
5
6
7
8
9
10
11
12
public  interface  ICenter : IDisposable
{
 
     String ID { get ; }
 
     INode Node { get ; set ; }
 
     IUserService UserService { get ; set ; }
 
     void  Open();
 
}

      中心的接口定义很简单,主要都是内部针对SmartRoute的INode进行相关消息操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public  void  Open()
{
     mCenterSubscriber = Node.Register<EventSubscriber>(ID);
     mCenterSubscriber.Register<Protocol.SyncUsers>(OnSyncUsers);
     mCenterSubscriber.Register<Protocol.CenterStarted>(OnOtherCenterStarted);
     mCenterSubscriber.Register<Protocol.Register>(OnSyncUser);
     mCenterSubscriber.Register<Protocol.UnRegister>(OnSyncUnRegister);
     Node.SubscriberRegisted += OnSubscriberRegisted;
     mStartServiceTimer = new  System.Threading.Timer(OnOpen, null , 5000, 5000);
     Node.Loger.Process(LogType.INFO, "search other center..." );
}
 
//处理用户上线所在网关信息
private  void  OnReceiveUsersInfo(Message msg, Protocol.GetUsersInfo e)
{
     string [] users = e.Receiver.Split( ';' );
     Protocol.GetUserInfoResponse response = new  Protocol.GetUserInfoResponse();
     response.RequestID = e.RequestID;
     Protocol.OperationStatus status = new  Protocol.OperationStatus();
     foreach  ( string  user in  users)
     {
         Protocol.UserInfo info = UserService.GetUserInfo(user, status);
         if  (info != null )
             response.Items.Add(info);
     }
     msg.Reply(response);
}
  //网关用户下线
private  void  OnUserUnregister(Message msg, Protocol.UnRegister e)
{
     Protocol.OperationStatus status = new  Protocol.OperationStatus();
     UserService.Remove(e.Name, status);
     msg.Reply(status);
     Node.Loger.Process(LogType.INFO, "{0} user unregister" , e.Name);
     //同步到其他中心节点
     if  (mHasOtherCenter)
         mCenterSubscriber.Publish(CENTER_OTHER_TAG, e, ReceiveMode.Regex);
}
  //网关用户上线
private  void  OnUserRegister(Message msg, Protocol.Register e)
{
     Protocol.OperationStatus status = new  Protocol.OperationStatus();
     UserService.Register( new  Protocol.UserInfo() { Name = e.Name, Gateway = e.GatewayID }, status);
     msg.Reply(status);
     Node.Loger.Process(LogType.INFO, "{0} user register from {1}" , e.Name, e.GatewayID);
     //同步到其他中心节点
     if  (mHasOtherCenter)
         mCenterSubscriber.Publish(CENTER_OTHER_TAG, e, ReceiveMode.Regex);
}
 
//同步下线
private  void  OnSyncUnRegister(Message msg, Protocol.UnRegister e)
{
     Protocol.OperationStatus status = new  Protocol.OperationStatus();
     UserService.Remove(e.Name, status);
     Node.Loger.Process(LogType.INFO, "{0} user unregister" , e.Name);
}
//同步上线
private  void  OnSyncUser(Message msg, Protocol.Register e)
{
     Protocol.OperationStatus status = new  Protocol.OperationStatus();
     UserService.Register( new  Protocol.UserInfo() { Name = e.Name, Gateway = e.GatewayID }, status);
     Node.Loger.Process(LogType.INFO, "{0} user register from {1}" , e.Name, e.GatewayID);
}
 
//同步其他中心上线信息
private  void  OnSyncUsers(Message msg, Protocol.SyncUsers e)
{
     Node.Loger.Process(LogType.INFO, "sync user info to local!" );
     Protocol.OperationStatus status = new  Protocol.OperationStatus();
     foreach  (Protocol.UserInfo item in  e.Items)
     {
         UserService.Register(item, status);
     }
}
 
private  void  OnSubscriberRegisted(INode node, ISubscriber subscriber)
{
     //发现其他中心服务,向服务发起同步用户请求
     if  (subscriber.Name.IndexOf(CENTER_TAG) == 0 && subscriber.Name != ID)
     {
         mHasOtherCenter = true ;
         mReadyToStart = false ;
         Node.Loger.Process(LogType.INFO, "find {0} center" , subscriber.Name);
         Protocol.CenterStarted started = new  Protocol.CenterStarted();
         started.Name = ID;
         mCenterSubscriber.Publish(subscriber.Name, started);
         Node.Loger.Process(LogType.INFO, "request sync user info ...." );
     }
}
 
public  INode Node
{
     get ; set ;
}

        实现并不复杂,主要是开启相关订阅并注册消息处理方法即可,主要针对注册,同步和获取用户所在网关信息。

网关

     网关的作用主要是接收消息,从注册中心获取用户标识对应的网关并把消息推送过去;所以功能也并不复杂主要也是针对INode的操作。

 

1
2
3
4
5
6
7
8
9
10
11
12
public  interface  IGateway : IDisposable
{
     INode Node { get ; set ; }
 
     Protocol.OperationStatus Register(UserToken userToken);
 
     Protocol.OperationStatus UnRegister( string  username);
 
     void  SendMessage( string  receivers, string  sender, object  message);
 
     void  Open();
}

    功能比较简单用户标识注册和注销功能,还加上一个消息推送方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public  OperationStatus Register(UserToken userToken)
         {
             OperationStatus result;
             Register register = new  Register();
             register.Name = userToken.Name;
             register.GatewayID = Node.DefaultEventSubscriber.Name;
             result = Node.DefaultSwitchSubscriber.SyncToService<Protocol.OperationStatus>(Center.USER_SERVICE_TAG, register);
             mUserActions[userToken.Name] = userToken;
             return  result;
         }
 
         public  void  SendMessage( string  receivers, string  sender, object  message)
         {
             MessageQueue.MessageItem item = new  MessageQueue.MessageItem();
             item.ID = GetRequestID();
             item.Receives = receivers;
             item.Sender = sender;
             item.Data = message;
             mMsgQueue.Push(item);
             GetUsersInfo getinfo = new  GetUsersInfo();
             getinfo.RequestID = item.ID;
             getinfo.Receiver = receivers;
             Node.DefaultSwitchSubscriber.ToService(Center.USER_SERVICE_TAG, getinfo);
         }
 
         public  void  Dispose()
         {
             if  (mMsgQueue != null )
                 mMsgQueue.Dispose();
         }
 
         public  void  Open()
         {
             mMsgQueue = new  MessageQueue( this , 2);
             mMsgQueue.Open();
             Node.DefaultSwitchSubscriber.DefaultEventSubscriber.Register<GetUserInfoResponse>(OnGetUserInfoRequest);
             Node.DefaultEventSubscriber.Register<UserMessage>(OnUserMessage);
         }
 
         public  OperationStatus UnRegister( string  username)
         {
             UnRegister unregister = new  UnRegister();
             unregister.Name = username;
             UserToken token = null ;
             mUserActions.TryRemove(username, out  token);
             return  Node.DefaultSwitchSubscriber.SyncToService<OperationStatus>(Center.USER_SERVICE_TAG, unregister);
         }

中心启动

      由于基于SmartRoute的设计,所以中心的启动并不需要进行其他配置,直接开启动行即可;对于多节点的中心怎办?如果有需要多启一个实例即可达到多中心负载能力。

1
2
3
4
5
6
7
8
9
10
11
12
public  class  Program
{
     public  static  void  Main( string [] args)
     {
         INode node = NodeFactory.Default;
         node.Loger.Type = LogType.ALL;
         node.AddLogHandler( new  SmartRoute.ConsoleLogHandler(LogType.ALL));
         node.Open();
         MRC.MCRFactory.Center.Open();
         System.Threading.Thread.Sleep(-1);     
     }
}

网关应用

     网关的启动和中心一样,不过需要根据实际需要发起用户标识注册,注册后就可以向集群中的任何标识发送消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public  class  Program
{
     public  static  void  Main( string [] args)
     {
         INode node = NodeFactory.Default;
         node.Loger.Type = LogType.ALL;
         node.AddLogHandler( new  SmartRoute.ConsoleLogHandler(LogType.ALL));
         node.Open();
         MRC.MCRFactory.Gateway.Open();
         System.Threading.ThreadPool.QueueUserWorkItem(OnTest);
         System.Threading.Thread.Sleep(-1);
     }
 
     private  static  void  OnTest( object  state)
     {
         System.Threading.Thread.Sleep(10000);
         UserToken token = new  UserToken( "ken" );
         token.Register();
         token.Receive = OnUserReceive;
     }
 
     private  static  void  OnUserReceive(UserToken token, Protocol.UserMessage e)
     {
         Console.WriteLine( "receive message from {0} {1}" , e.Sender, e.Data);
     }
}

    构建相应标识的UserToken注册到网关,网关会自动把标识同步到中心;然后定义UserToken相应的消息接收方法即可处理接收的消息。实际应用中可以继承UserToken并挂相应的客户端连接然后当接收消息做相应的网络转发就可以达到用户和用户间的通讯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值