前言

  在前几个小节中我们讲了Thrift框架的基本概念以及重要的名称空间,接下来的几个小节,我们将站在实战的角度来深入讲解一些Thrift的重要类型。本小节我先要讲一下Thrift框架支持TCP通信的类,客户端TSocket,服务器端TServerSocket。

客户端TSocket

  Tsocket作为Thrift框架实现TCP通信的底层类型(上面两层分别为Protocol层和Client层),我们首先来看一下TSocket的构造函数:

  public TSocket(TcpClient client);
public TSocket(string host, int port);
public TSocket(string host, int port, int timeout);

Tsocket有三个构造函数:

  • + 第一个构造需要我们自己维护一个TcpClient,对于熟悉.net Socket通信的同学来说,这个很简单,就不在此赘述了
  • + 第二个和第三个构造函数相似,唯一的不同是在是否有设置超时时间TimeOut这个参数上

构造函数 有无timeout参数的问题

  我们重点来讲一些后两个构造函数上,其实后两个构造函数在内部实现上并无差别,看一下源码我们就清晰了:

public TSocket(string host, int port) : this(host, port, 0)
{
} public TSocket(string host, int port, int timeout)
{
this.host = host;
this.port = port;
this.timeout = timeout;
this.InitSocket();
}

在内部实现上如果我们不设置timeout参数,它会被设置为0,然后还是调用三个参数的构造构造函数的。是不是我们调用这个两个构造函数去实例化这个类没有一点差别呢?答案是 否定的。他们在调用**Open**方法时走了不同的代码分支:

if (this.timeout == 0)
{
this.client.Connect(this.host, this.port);
}
else
{
TSocket.ConnectHelper connectHelper = new TSocket.ConnectHelper(this.client);
IAsyncResult asyncResult =
this.client.BeginConnect(this.host, this.port,
new AsyncCallback(TSocket.ConnectCallback), connectHelper);
if (!asyncResult.AsyncWaitHandle.WaitOne(this.timeout) || !this.client.Connected)
{
......

  

这里先说明一点Timeout参数被赋值给TcpLient类型的SendTimeout和ReceiveTimeout参数上:

public int Timeout
{
set
{
TcpClient arg_1E_0 = this.client;
TcpClient arg_18_0 = this.client;
this.timeout = value;
arg_18_0.SendTimeout = value;
arg_1E_0.ReceiveTimeout = value;
}
}

如果你没有设置timeout参数,需要记住一点,host参数你要传IPv6对应的字符串,如果你传了ipv4对应的字符串,你将收到莫名其妙的三种类型的错误:

  1. 调用sendto方法前没有设置远程终结点
  2. 远程主机关闭了现有链接
  3. 内部错误

thrift框架在错误提示上有点不友好,给出的错误提示没有一点用处。这个错误解决的方法我们可以从源码上找到问题所在,请看一下代码:

internal static TcpClient CreateTcpClient()
{
return new TcpClient(AddressFamily.InterNetworkV6)
{
Client =
{
DualMode = true
}
};
}

  上面代码我们在**InitSocket**方法中找到创建TcpClient类型的方法,我们一下代码已经知道了原因,因为将TcpClient类型定位到了InterNetworkV6的类型,如果我们创建时传了ipv4的地址,就会出上述问题。如果,我们设置了timeout参数,既是我们传了ipv4的地址也不会有问题,这个可能和connect,beginconnect两种链接方式的内部实现有关吧。

服务端

服务器端就是一个监听连接,处理请求的过程,上文我们已经讲过服务器端的处理大致处理过程,这里不再赘述。

TMultiplexedProtocol和TMultiplexedProcessor

  接下来我们将一下合并监听端口的主要的处理类,TMultiplexedProtocol为客户端使用类,TMultiplexedProcessor为服务器端使用类。前面的文章,我们提到过这两个类怎么用,也对两个类的调用方法进行的简单的封装处理,这里我想讲一下它们的内部时怎么处理请求的。

想要说明一个类的实现原理,最好的方法时带着大家去看下它的源代码,首先,我们看一下TMultiplexedProtocol的部分源码:

public override void WriteMessageBegin(TMessage tMessage)
{
TMessageType type = tMessage.Type;
if (type == TMessageType.Call || type == TMessageType.Oneway)
{
base.WriteMessageBegin(
new TMessage(this.ServiceName + TMultiplexedProtocol.SEPARATOR + tMessage.Name, tMessage.Type, tMessage.SeqID));
return;
}
base.WriteMessageBegin(tMessage);
}

看过源码后,我们一目了然,是的,它把ServiceName写到了请求中,那么在服务器端时怎么处理的呢?同样,我们看下服务器端的处理方法:

public bool Process(TProtocol iprot, TProtocol oprot)
{
bool result;
try
{
TMessage message = iprot.ReadMessageBegin();
if (message.Type != TMessageType.Call && message.Type != TMessageType.Oneway)
{
this.Fail(oprot, message, TApplicationException.ExceptionType.InvalidMessageType, "Message type CALL or ONEWAY expected");
result = false;
}
else
{
int num = message.Name.IndexOf(TMultiplexedProtocol.SEPARATOR);
if (num < 0)
{
this.Fail(oprot, message, TApplicationException.ExceptionType.InvalidProtocol
                                          , "Service name not found in message name: " + message.Name
                                    + ". Did you forget to use a TMultiplexProtocol in your client?");
result = false;
}
else
{
string text = message.Name.Substring(0, num);
TProcessor tProcessor;
if (!this.ServiceProcessorMap.TryGetValue(text, out tProcessor))
{
this.Fail(oprot, message, TApplicationException.ExceptionType.InternalError,
                                            "Service name not found: " + text + ". Did you forget to call RegisterProcessor()?");
result = false;
}
else
{
TMessage messageBegin = new TMessage(message.Name.Substring(text.Length + TMultiplexedProtocol.SEPARATOR.Length)
                                                        , message.Type, message.SeqID);
result = tProcessor.Process(new TMultiplexedProcessor.StoredMessageProtocol(iprot, messageBegin), oprot);
}
}
}
}

看到源码中的第一个else分支,它解析出serviceName,然后在中ServiceProcessorMap集合中获取我们之前注册过的对应的请求处理器。

其他一些问题

  • + 服务器端被调用的方法不能返回Null类型,否则调用方法会抛出异常

  • + thrift框架进行RPC调用是不是线程安全的,因此,线程安全部分需要自己去处理

结尾

本小节我们讲述了Tsocket在实战中会遇到的一些坑,希望对您有帮助。

C#使用Thrift作为RPC框架实战(四)之TSocket的更多相关文章

  1. C#使用Thrift作为RPC框架入门(三)之三层架构

    前言 这是我们讲解Thrift框架的第三篇文章,前两篇我们讲了Thrift作为RPC框架的基本用法以及架构的设计.为了我们更好的使用和理解Thrift框架,接下来,我们将来学习一下Thrift框架提供 ...

  2. CSharp使用Thrift作为RPC框架入门(一)

    前言 本文将介绍由 Facebook 开发的远程服务调用框架 Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++ ...

  3. 【WePY小程序框架实战四】-使用async&await异步请求数据

    [WePY小程序框架实战一]-创建项目 [WePY小程序框架实战二]-页面结构 [WePY小程序框架实战三]-组件传值 async await 是对promise的近一步优化,既解决了promise链 ...

  4. 带你手写基于 Spring 的可插拔式 RPC 框架(四)代理类的注入与服务启动

    上一章节我们已经实现了从客户端往服务端发送数据并且通过反射方法调用服务端的实现类最后返回给客户端的底层协议. 这一章节我们来实现客户端代理类的注入. 承接上一章,我们实现了多个底层协议,procoto ...

  5. 一个简单RPC框架是怎样炼成的(V)——引入传输层

    开局篇我们说了,RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注冊 RPC消息处理    接下来处理传输数据.实际应用场景一般都是基于socket.socket代码比較多, ...

  6. 一个简单RPC框架是怎样炼成的(VI)——引入服务注冊机制

    开局篇我们说了.RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注冊 RPC消息处理 接下来处理RPC服务的注冊机制.所谓注冊机制,就是Server须要声明支持哪些rpc方法 ...

  7. 一个简单RPC框架是怎样炼成的(II)——制定RPC消息

    开局篇我们说了,RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注冊 RPC消息处理 以下,我们先看一个普通的过程调用 class Client(object): def _ ...

  8. 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍

    概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...

  9. 服务化实战之 dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型

    转自: http://blog.csdn.net/liubenlong007/article/details/54692241 概述 前段时间项目要做服务化,所以我比较了现在流行的几大RPC框架的优缺 ...

随机推荐

  1. TCC分布式事务的实现原理

    目录 一.写在前面 二.业务场景介绍 三.进一步思考 四.落地实现TCC分布式事务 (1)TCC实现阶段一:Try (2)TCC实现阶段二:Confirm (3)TCC实现阶段三:Cancel 五.总 ...

  2. java的加载与执行原理剖析

    到目前为止,我们接触过的重点术语,总结一下: Java体系的技术被划分为三大块: JavaSE:标准版 JavaEE:企业版 JavaME:微型版 安装JDK之后: JDK:java开发工具箱 JRE ...

  3. pycharm安装pika提示CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com>

    1. 问题描述: pycharm安装第三方库时提示CondaHTTPError: HTTP 000 CONNECTION FAILED. 2. 错误原因:默认镜像源访问速度过慢,会导致超时从而导致更新 ...

  4. [对对子队]测试报告Beta

    一.测试中发现的bug BETA阶段的新bug 描述 提出者(可能需要发现者在会议上复现) 处理人 是否解决 第四关中工作区的循环语句拖动到组件区后成本的大小比原来不一样的问题 梁河览 何瑞 是 循环 ...

  5. Ubuntu下在当前用户下安装JDK1.8

    Oracle官网的JDK下载需要用户登录才能下载,JDK1.8的下载地址:https://www.oracle.com/cn/java/technologies/javase/javase-jdk8- ...

  6. 对SQLServer错误使用聚集索引的优化案例(千万级数据量)

    前言: 半个月前发了文章 SQLServer聚集索引导致的插入性能低 终于等到生产环境休整半天,这篇文章是对前文的实际操作. 以下正文开始: 异常:近期发现偶尔有新数据插入超时. 分析:插入条码有多种 ...

  7. Spring Cloud 微服务实战——nacos 服务注册中心搭建(附源码)

    作为微服务的基础功能之一的注册中心担任重要的角色.微服务将单体的服务拆分成不同的模块下的服务,而不同的模块的服务如果进行通信调用呢?这就需要服务注册与发现.本文将使用阿里开源项目 nacos 搭建服务 ...

  8. Open vSwitch 应用实践

    基础配置以及要点: 1.交换机创建和端口的配置 1) 创建一个新的 OVS 交换机[格式:$ ovs-vsctl add-br [名称]] $ovs-vsctl add-br ovs-switch 2 ...

  9. matlab与python scipy.signal中 freqs freqz 中w,什么时候是角频率,什么时候是真实的工程中使用的采样频率Hz,如何转化

    matlab与python scipy.signal中的freqs,freqz频率分析函数,输出的w,有时候是角频率,有时候是真实频率,容易搞混,这里对比一下. 0.  精要总结: 1) freqs: ...

  10. QuantumTunnel:Netty实现

    接上一篇文章内网穿透服务设计挖的坑,本篇来聊一下内网穿透的实现. 为了方便理解,我们先统一定义使用到的名词: UserClient:用户客户端,真实的请求发起方: UserServer:内网穿透-用户 ...