前言

  在前几个小节中我们讲了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. GAN实战笔记——第一章GAN简介

    GAN简介 一.什么是GAN GAN是一类由两个同时训练的模型组成的机器学习技术:一个是生成器,训练其生成伪数据:另一个是鉴别器,训练其从真实数据中识别伪数据. 生成(generative)一词预示着 ...

  2. Visual Studio 安装 C++

    Visual Studio 安装 C++

  3. 初始CSS01

    CSS基础知识 CSS介绍 CSS全称为层叠样式表,与HTML相辅相成,实现网页的排版布局与样式美化. 使用方式 根据样式表在页面中呈现的方式不同,可以通过以下三种方式在页面中使用格式 内联样式 改样 ...

  4. 【UE4 C++】 启动 / 关闭外部exe、开启虚拟键盘

    启动/关闭外部exe 引擎自带 FPlatformProcess::CreateProc() FPlatformProcess::TerminateProc() windows api ShellEx ...

  5. 通过Nacos动态刷新Spring Cloud Gateway的路由

    通过Nacos动态刷新Spring Cloud Gateway的路由 一.背景 二.解决方案 三.实现功能 四.实现步骤 1.网关服务的实现 1.pom文件 2.bootstrap.yml配置文件 3 ...

  6. 《HelloGitHub》第 67 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweihan/HelloGitHu ...

  7. 利用DMA实现采样数据的直接搬运存储

    尝试了下STM32的ADC采样,并利用DMA实现采样数据的直接搬运存储,这样就不用CPU去参与操作了. 找了不少例子参考,ADC和DMA的设置了解了个大概,并直接利用开发板来做一些实验来验证相关的操作 ...

  8. 奔跑吧linux-第三章实验

    基于树莓派+openeuler平台 实验 3-2:汇编语言练习--查找最大数 1.实验目的 通过本实验了解和熟悉 ARM64 汇编语言. 2.实验要求 使用 ARM64 汇编语言来实现如下功能:在给定 ...

  9. 数字孪生 3D 科技馆的科学传播新模式

    前言 科技馆是一种参与型体验型的博物馆,以传播科学知识.培养公众的科学创新技术为宗旨,并以其生动的展现方式得到公众的广泛欢迎.一直以来,我国科技馆的发展受到各种因素的制约和影响,发展缓慢.如今在我国经 ...

  10. 在Ubuntu下的C语言编程

    以运行在虚拟机下的Ubuntu为例: mkdir fenchen 来创建一个文件夹 cd fenchen 切换到这个文件夹下面 vi test.c 创建并编辑一个test.c文件 按 i 编辑,之后把 ...