前言

  在前几个小节中我们讲了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. css单位px,em,rem区别

    在css中单位长度用的最多的是px.em.rem,这三个的区别是: px是固定的像素,一旦设置了就无法因为适应页面大小而改变. em和rem相对于px更具有灵活性,他们是相对长度单位,意思是长度不是定 ...

  2. Kali安装OWASP

    我是2019版的kali,里面并没有自带OWASP工具,因为OWASP不再更新的因素,所以新版kali将它移除了  安装OWASP apt-get install zaproxy #以下都是安装软件时 ...

  3. 浅析ReDoS的原理与实践

    转载于http://www.freebuf.com/articles/network/124422.html ReDoS(Regular expression Denial of Service) 正 ...

  4. 【二食堂】Alpha - Scrum Meeting 9

    Scrum Meeting 9 例会时间:4.19 13:00~13:20 进度情况 组员 昨日进度 今日任务 李健 1. "文本区域"栏目完成,可实现实体和关系的添加issue ...

  5. Seata整合SpringBoot和Mybatis

    Seata整合SpringBoot和Mybatis 一.背景 二.实现功能 三.每个服务使用到的技术 1.账户服务 2.订单服务 四.服务实现 1.账户服务实现 1.引入jar包 2.项目配置 3.建 ...

  6. mysql分表之后怎么平滑上线?

    分表的目的 项目开发中,我们的数据库数据越来越大,随之而来的是单个表中数据太多.以至于查询数据变慢,而且由于表的锁机制导致应用操作也受到严重影响,出现了数据库性能瓶颈. 当出现这种情况时,我们可以考虑 ...

  7. 2021.10.7 NKOJ周赛总结

    Ⅰ. 自描述序列 问题描述: 序列 1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,... 看似毫无规律,但若我们将相邻的数字合并 : 1,22,11,2,1,22,1 ...

  8. series和读取外部数据

    1.为什么学习pandas 我们并不是不愿意学习新的知识,只是在学习之前我们更想知道学习他们能够帮助我们解决什么问题.--伟哥 numpy虽然能够帮助我们处理数值,但是pandas除了处理数值之外(基 ...

  9. Mybatis的分页插件com.github.pagehelper

    1. 需要引入PageHelper的jar包 如果没有使用maven,那直接把jar包导入到lib文件夹下即可,这个PageHelper插件在github上有开源, 地址为:https://githu ...

  10. pycharm基本使用与破解

    一.pycharm基本使用 pycharm这款ide软件虽然功能强大,但正因为他的强大,所以小白在刚使用这款软件时上手会有点难度,今天我们就来介绍一下ptcharm的基本使用. 1.基本配置 我们安装 ...