zoukankan      html  css  js  c++  java
  • 自己动手,写一个分布式系统(附c#代码示例)

    分布式系统有很多成熟的解决方案。如:微软的WCF。WCF太过于复杂,配置也麻烦。其实可以自己动手设计一个小的分布式系统。系统的原理完全在自己掌握之中,可以根据业务随机而变。这里展示远程调用最核心最基本的处理逻辑,其实远程调用并不复杂神秘。

    分布式系统其实是数据流的交换。数据必须快速的从一段传送到另一端,否则系统性能就大打折扣。对于.net,本人设计一个非常优化易于使用的网络库(EasyNetMessage)。使用该库,不需要关心底层细节,所有处理对象是string、byte;发送时,不需要处理分包(几十M的数据也可以一次发送);收包时,不需要处理粘包。本文所示例代码,就是基于该网络库。本文章实例代码见底部。

    实现目标

    实现一个非常简单的应用   internal int AddCall(int value1, int value2);就是两个整数相加。调用过程和本地完全一样,只是执行加法操作是在远程服务器实现的。操作非常简单,但是揭示了远程调用的核心处理流程。

    处理过程:

    a) 数据发送

      每次函数调用必须有一个唯一标识CallId,这个也是发包的唯一标识;服务端处理完后,返回的结果也带有此标识。通过此标识,将发送端数据和返回数据关联起来了。

      发送完数据后,客户端线程要挂起,等待服务器端的处理结果。线程挂起使用ManualResetEvent。并建立起CallId与ManualResetEvent的对应关系。当数据返回时,就能找到对应的ManualResetEvent。

      //callid与事件关联
            Dictionary<int, ManualResetEvent> _callEventGroup = new Dictionary<int, ManualResetEvent>();
            //callId与返回结果关联
            Dictionary<int, NetCallAddAck> _callResultGroup = new Dictionary<int, NetCallAddAck>();
            internal int AddCall(int value1, int value2)
            {
                //组织发送包
                NetCallAdd add = new NetCallAdd();
                add.Value1 = value1.ToString();
                add.Value2 = value2.ToString();
    
                MonitorClient client = GetCurAppClient();
                if (client == null)
                    throw new Exception("socket未连接!");
    
                //生成线程事件,并与CallId关联
                ManualResetEvent callEvent = new ManualResetEvent(false);
                lock (_callEventGroup)
                {
                    _callEventGroup.Add(add.CallId, callEvent);
                }
    
                //发送数据
                EN_SendDataResult result = _netServer.SendData(client.ClientSocket, add.ToEasyMessage().ToNetPacket());
                if (result != EN_SendDataResult.ok)
                {
                    lock (_callEventGroup)
                    {
                        _callEventGroup.Remove(add.CallId);
                    }
                    throw new Exception("网络发送异常!");
                }
    
                //等待结果
                callEvent.WaitOne(3000);
                _callEventGroup.Remove(add.CallId);
    
                //查看结果集
                lock (_callResultGroup)
                {
                    if (_callResultGroup.ContainsKey(add.CallId))
                    {
                        NetCallAddAck ack = _callResultGroup[add.CallId];
                        return int.Parse(ack.Result);
                    }
                }
    
                throw new Exception("没有返回结果!");
            }

    b)数据返回

      数据返回后的处理是在另一个线程。数据返回后,先根据CallId查找对应的ManualResetEvent;如果找不到,有可能服务器处理太慢,超时了。

      先将返回结果存储到哈希数组中,key为CallId。再调用ManualResetEvent的Set函数,唤醒调用端线程。调用端线程根据CallId到哈希表中获取结果。

            internal void OnRcvAck(NetCallAddAck addAck)
            {
                //根据callid找到事件
                ManualResetEvent callEvent = null;
                lock (_callEventGroup)
                {
                    if (!_callEventGroup.ContainsKey(addAck.CallId))
                        return;
                    callEvent =_callEventGroup[addAck.CallId];
                }
    
                //结果存放到哈希表中
                lock (_callResultGroup)
                {
                    _callResultGroup.Remove(addAck.CallId);
                    _callResultGroup.Add(addAck.CallId, addAck);
                }
                //设置事件为有信号,调用方挂起的线程可以继续执行
                callEvent.Set();
            }

    进一步说明:可以在此基础上,进一步扩展。开发出类似Redis的内存库。客户端的调用也不一定是同步 ,可以采用异步回调的方式处理。其实如果知道处理的原理,可以根据自己的业务做裁剪。只有知其所以然,才能开发出最符合自己业务的系统,才可能进一步优化。

    实例代码 https://download.csdn.net/download/qq_29939347/10684858 

  • 相关阅读:
    httpclient之基本类
    安卓开发遇到的各种问题
    C#获取字符串宽度像素
    Android开发参考资料
    Windows Phone开发参考资料
    Windows Phone锁屏背景相关代码
    Windows Phone Launcher class
    Windows Phone SplashScreen初始屏幕示例
    ASP如何实现注册后发送激活邮件?
    SkyDrive开发申请ClientID
  • 原文地址:https://www.cnblogs.com/yuanchenhui/p/remoteCall.html
Copyright © 2011-2022 走看看