分布式系统有很多成熟的解决方案。如:微软的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();
_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

自己动手,写一个分布式系统(附c#代码示例)的更多相关文章

  1. 动手写一个简单版的谷歌TPU-矩阵乘法和卷积

    谷歌TPU是一个设计良好的矩阵计算加速单元,可以很好的加速神经网络的计算.本系列文章将利用公开的TPU V1相关资料,对其进行一定的简化.推测和修改,来实际编写一个简单版本的谷歌TPU.计划实现到行为 ...

  2. 死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  3. 动手写一个简单版的谷歌TPU-指令集

    系列目录 谷歌TPU概述和简化 基本单元-矩阵乘法阵列 基本单元-归一化和池化(待发布) TPU中的指令集 SimpleTPU实例: (计划中) 拓展 TPU的边界(规划中) 重新审视深度神经网络中的 ...

  4. 死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  5. 自己动手写一个服务网关-java

    自己动手写一个服务网关 原文链接:https://www.cnblogs.com/bigben0123/p/9252444.html 引言 什么是网关?为什么需要使用网关? 如图所示,在不使用网关的情 ...

  6. 动手写一个简单的Web框架(模板渲染)

    动手写一个简单的Web框架(模板渲染) 在百度上搜索jinja2,显示的大部分内容都是jinja2的渲染语法,这个不是Web框架需要做的事,最终,居然在Werkzeug的官方文档里找到模板渲染的代码. ...

  7. 动手写一个简单的Web框架(Werkzeug路由问题)

    动手写一个简单的Web框架(Werkzeug路由问题) 继承上一篇博客,实现了HelloWorld,但是这并不是一个Web框架,只是自己手写的一个程序,别人是无法通过自己定义路由和返回文本,来使用的, ...

  8. 动手写一个简单的Web框架(HelloWorld的实现)

    动手写一个简单的Web框架(HelloWorld的实现) 关于python的wsgi问题可以看这篇博客 我就不具体阐述了,简单来说,wsgi标准需要我们提供一个可以被调用的python程序,可以实函数 ...

  9. 使用gfortran将数据写成Grads格式的代码示例

    使用gfortran将数据写成Grads格式的代码示例: !-----'Fortran4Grads.f90' program Fortran4Grads implicit none integer,p ...

随机推荐

  1. 交叉编译bash

    1 下载bash版本:[version 4.2.53]地址:http://ftp.gnu.org/gnu/bash/ 2 解压将下载的bash压缩包解压,命令: # mkdir /home/carri ...

  2. Error while executing: am start -n

    单纯clean project是不行的,选择File-Invalidate Caches/Restart,清除缓存,重启AS Android NDK 是在SDK前面又加上了"原生" ...

  3. jenkins shell部署

    jenkins执行shell脚本 jenkins执行shell 上一篇说的是jenkins+svn+maven把war包自动部署到Tomcat,这篇是从SVN上拉取代码maven生成jar文件,并且拷 ...

  4. 选择困难症的福音——团队Scrum冲刺阶段-Day5(补发 那天csshow)

    选择困难症的福音--团队Scrum冲刺阶段-Day 5 今日进展 编写提问部分 游戏分类的界面 将之前错误的图标改正 关于我们的俄罗斯方块,今天有了新的进展 NextBlockView(定义了下一个方 ...

  5. 设置DataGridView中表头颜色

    默认的DataGridView表头颜色实在不是太好看,想设置下,上google搜了一通, 都说这样设置 this.dataGridView1.ColumnHeadersDefaultCellStyle ...

  6. 利用python监测linux上的服务(简单实现服务宕掉自动发送邮件)

    python 这里用到了四个python 模块 : import time (时间模块) import re (正则模块) import socket (监测端口模块)import yagmail ( ...

  7. 【repost】H5的新特性及部分API详解

    h5新特性总览 移除的元素 纯表现的元素: basefont.big.center.font等 对可用性产生负面影响的元素: frame.frameset.noframes 新增的API 语义: 能够 ...

  8. 关于esp32的ADC采集

    对于ADC采集 程序源码如下: /* ADC1 Example This example code is in the Public Domain (or CC0 licensed, at your ...

  9. nginx 502错误 upstream sent too big header while reading response header from upstream

    原本的设置是 proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; 在这种配置下,使用fiddler进行抓包分 ...

  10. docker中gitlab-runner配置

    1.启动gitlab-runner docker run -d --name gitlab-runner --restart always \ -v /opt/data/gitlab-runner/c ...