项目地址 :  https://github.com/kelin-xycs/MessageRPC

MessageRPC

一个 用 C# 实现的 使用 Message 的 RPC

MessageRPC ,使用 Message 进行 RPC 通信 。 每一个调用(Call) 就是一次 发送消息(Send), 返回结果也是返回一条消息 。

MessageRPC 的 RPC 协议格式 可以算是 Http 的 一个 简化版 。 包括了 Head 和 Body(Content) , Head 包含多个 Header , 目前定义的 Header 有 3 个: Parameters Error Content-Length , Parameters 用来传递参数,值的格式是 “id=001&name=小明&” 这样,和 Http 查询字符串 格式一样 。 Error 用来 传递 错误信息 。 如果有 Error Header , 则 rpc 会抛出 RPCServerException 。

Content-Length 用来表示 Body 的长度,可以用 body 来传递 大二进制数据 。 比如 图片 文件 等 。

Header 之间通过 \r\n 分隔, Head 和 Body 之间也通过 \r\n 分隔 , 这和 Http 是一样的 。

这 3 个 Header 都是可选, 但最少要有 1 个,不然 服务器端 解析 时会报错并关闭连接 。 Header 的值可以为空,如 “Parameters: ” 。

Header 值 会经过 UrlEncode 编码,具体的说是 Parameters 的参数字符串里的 参数名 和 参数值 会 经过 UrlEncode 编码 ,还有 Error 的 值也会经过 UrlEncode 编码 。 Content-Length 的值是 数字(long),所以不需要 UrlEncode 。

在 通信机制 上,采用了 连接池(SocketPool)的机制 , 每个 Socket 的 存活期 是 2 分钟,超过 2 分钟 未被使用的 Socket 将被回收(关闭), 连接池大小没有上限 。 这是为了满足 实时响应性 和 吞吐量 。 就是说,如果 连接池 中的 Socket 不够用, 会创建新的 Socket 。

因为采用了 连接池 , 所以在 数据通信 上必须严格的准确,具体是指 每次发送的 Body(Content) 长度必须等于 Content-Length 的长度, 小于了必须 通过 SendVacancy() 发送 空字符 \0 来补齐 , 大于了必须截断 。 也因为此, 在 协议通信 中如果发生错误(异常),则会 关闭连接, 否则 上一次的 Content 可能被当成这一次的 Head, 或者, 这一次的 Head 被当成上一次 的 Content, 并且这种错误只要发生一次,之后很可能就一直错误下去,所以最好的做法就是把连接关闭 。

一般情况,发生上述的 数据传输 的 异常,通常是因为 服务器 网络 问题 或者 受到攻击 。

开发中解决的一些问题 :

通常服务器不会主动关闭连接,如果意外关闭了连接(比如 Message RPC Host 进程重启或关闭),那么 客户端(SocketPool) 不知道服务器端已关闭连接, 仍然会返回 连接池(SocketPool)中的 Socket 使用,但此时 Socket 已经失效,会抛出 “远程主机强行关闭了一个已有的连接” 异常 。 为了解决这个问题, 在 客户端 向 服务器 Send() 的过程中,在 Send Head 的时候,如果 Socket 抛出异常, 会让 SocketPool 新建一个 Socket 重新 Send Head , 此时如果 服务器 已经恢复正常监听,则 可以正常 通信 , 如果 服务器 仍然未恢复正常监听,则 Socket 会抛出异常,此时即按正常流程继续执行。

这样做的原因是,以目前了解的资料来看,客户端 如果要知道 服务器 有没有关闭连接 , 好像需要发送一个 测试消息 。 正常的情况下每次 Send 之前都发送一个测试消息的话对 性能 比较浪费 。 所以就采用了 上述 的 重试 的 方式 。

我想以前用 Ado.net 的时候,有时候会抛出 “基础连接已关闭 ……” 这样的 异常,是不是跟上述类似的情况。啊哈哈哈

另一个问题是 服务器端 在 客户端访问之后一段时间客户端没有访问的话,服务器端的 CPU 占用率 会 上升到 30%左右,也有可能 70 以上 。 这个问题的原因是,客户端访问之后一段时间客户端没有访问的话 , 客户端 连接池(Socket Pool)会将 Socket 回收掉(Shutdown() Close()), 客户端 回收掉 Socket 之后, 服务器 会 Receive() 到 一个 长度为 0 的数据 , 按照 微软 docs 的说法 , 当对方主机 “优雅的” 关闭了连接后,己方会收到一个 长度为 0 的数据 。 但在我以前写的一个 Socket 程序里,我记得 客户端(WebClient) 一段时间之后会关闭连接,此时 服务器端 确实收到了 一个 长度为 0 的数据 , 但当再次 Receive() 的时候,就会抛出异常 “你的本机上的软件关闭了一个已有的连接” (客户端 和 服务器 都在我本机) , 但现在的情况是,再次 Receive() 仍然会收到一个 一个 长度为 0 的数据 , 并不会抛出异常 , 而我的程序逻辑是通过 异常 来判断 客户端 是否关闭连接 , 如果没有异常则一直循环 Receive() 下去,于是就造成了无限循环 Receive() , 每次接收到 一个 长度为 0 的数据 , 这样相当于 “空转” , 就造成了 CPU 占用率升高 。

那为什么 CPU 占用率有时候是 30% , 有时候是 70% 呢 , 这个跟 服务器端 “空转” 的 线程数有关, 如果 客户端 和 服务器端 只建立了一个连接,那么 服务器端也只有 1 个线程在监听, 也就只有 1 个线程 “空转” , 在多核处理器上, 1 个线程空转最多只会 占用 1 个核的资源,并不会占用 100% 的 CPU 。 所以 30% 是只有 1 个线程空转时的情况 , 70% 是 1 个以上的线程(Socket)空转的情况 。

对于这个问题,现在的解决办法是如果 Receive 的数据长度为 0 (Socket.Receive()方法的返回值), 则抛出异常,这样就可以关闭连接了。也避免了空转 。

为什么 印象中 WebClient 将连接关闭后 服务器端的 Socket 第一次 Receive() 得到 长度为 0 的数据 而 第二次就抛出异常呢 ? 也许 WebClient 在 关闭连接 时 先调用了 Socket.DisConnect() 方法, 这是说 也许,因为没有看过 WebClient 的代码, 而我的程序中是直接调用 Shutdown() Close() 来关闭 Socket 的 。 没有调用 DisConnect() 方法 , 不知会不会跟 DisConnect() 方法有关系 。 但 服务器端 按上述做法(Receive 的数据长度为 0 则 抛出异常)来做应该也是合理, 因为 服务器端 无法控制 客户端 是以什么方法来关闭连接 。

MessageRPC的更多相关文章

  1. 利用 MessageRPC 和 ShareMemory 来实现 分布式并行计算

    可以利用 MessageRPC + ShareMemory 来实现 分布式并行计算 . MessageRPC :  https://www.cnblogs.com/KSongKing/p/945541 ...

  2. WCF服务的异常消息

    原创地址:http://www.cnblogs.com/jfzhu/p/4055024.html 转载请注明出处 WCF Service发生异常的时候,客户端一般只能看见这样一个错误:“The ser ...

  3. 异常信息:由于内部错误,服务器无法处理该请求。有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults

    有方法说找到web.config 文件修改如下(蓝色部分) <behaviors>      <serviceBehaviors>        <behavior> ...

  4. biztalk中使用WCF-SQL接受传送数据【转】

    接触biztalk时间不长,转载一篇学习教程: http://www.cnblogs.com/chnking/archive/2010/05/09/1731098.html chnking写的. 一. ...

  5. WCF X.b 操作引用了已经从 Y.b 操作导出的消息元素 [http://tempuri.org/:b]。可以通过更改方法名称或使用 OperationContractAttribute 的 Name 属性更改其中一个操作的名称...

    详细错误如下: 很可能由 IncludeExceptionDetailInFaults=true 创建的 ExceptionDetail,其值为: System.InvalidOperationExc ...

  6. WCF技术剖析之二十一: WCF基本的异常处理模式[上篇]

    原文:WCF技术剖析之二十一: WCF基本的异常处理模式[上篇] 由于WCF采用.NET托管语言(C#和NET)作为其主要的编程语言,注定以了基于WCF的编程方式不可能很复杂.同时,WCF设计的一个目 ...

  7. 网络负载均衡环境下wsHttpBinding+Message Security+Windows Authentication的常见异常

    提高Windows Communication Foundation (WCF) 应用程序负载能力的方法之一就是通过把它们部署到负载均衡的服务器场中. 其中可以使用标准的负载均衡技术, Windows ...

  8. 探讨 : Host在IIS上的WCF Service的执行方式

    一个WCF请求由两个线程来完成 运行在IIS上的WCF service, 你可能会注意到一个比较有趣的现象. 当WCF service接收到一个请求时, 这个请求实际上会有两个线程在执行这个请求. 一 ...

  9. Request for the permission of type异常

    调用wcf调用的时候引发一个错误,错误信息如下: <Message>Request for the permission of type 'System.Configuration.Con ...

随机推荐

  1. CoderForce 180D-Name (构造+回溯)

    题目大意:给两个字符串s,t,用s中的字符重新组合构造出按字典序最小的但比t大的新字符串. 题目分析:先统计s中各个字母出现的次数,然后从t的左端向右端依次构造出新串的每一位上的字母.这个过程我是用回 ...

  2. sql server数据库中char、nchar、varchar、nvarchar的选择

    在数据库中,字符型的数据是最多的,可以占到整个数据库的80%以上.为此正确处理字符型的数据,对于提高数据库的性能有很大的作用. 在字符型数据中,用的最多的就是Char与Varchar两种类型.前面的是 ...

  3. [Leetcode] Unique binary search trees 唯一二叉搜索树

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  4. zk如何实现watch

    在客户端发送命令:stat /zhang watch 在zk server中产生如下图的调用栈: //在DataTree类中有 private final WatchManager dataWatch ...

  5. git HEAD游离状态问题解决

    最近在迭代一个版本的时候,出现 HEAD detached at xxx 提示,应该是我切换分支的时候,哪里没弄对.   那么可以通过如下办法解决 git checkout 05 # 先checkou ...

  6. 快速切题 sgu102.Coprimes 欧拉函数 模板程度 难度:0

    102. Coprimes time limit per test: 0.25 sec. memory limit per test: 4096 KB For given integer N (1&l ...

  7. 2017广东工业大学程序设计竞赛决赛 Problem E: 倒水(Water) (详解)

    倒水(Water) Description 一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水.接着~~CC发现瓶子实在太多了,于是他决定保留不超过K个瓶子.每次他选择两个当前含水 ...

  8. 软工作业NO.2小学生线上杨永信——四则运算题目生成

    项目题目:实现一个自动生成小学四则运算题目的命令行程序 github地址:https://github.com/a249970271/Formula 驾驶员:梁沛诗 副驾驶:曾祎祺 项目说明 自然数: ...

  9. Spring学习笔记之Testing

    测试嘛,一般也就两种,一种就是单元测试,另外一个就是集成测试.都是废话 一.单元测试 以前也就是搞个模拟,main函数一写搞定. 现在呢,有了个spring,也有了个推荐规范?这个是个什么东西?什么叫 ...

  10. sonarqube 代码检查

    再好的程序员也会出bug,所以代码检查很有必要.今天就出一个简单的检查工具代替人工检查. 参考: http://www.cnblogs.com/qiaoyeye/p/5249786.html 环境及版 ...