第一篇 《连接配置》

    第二篇 《连接池》

第三篇 标准通信

一、TCP的连接是无状态的,怎样知道我的请求被服务端接受并且正确执行了呢?

    我的解决方案是使用自己定义的标准输入输出,Push操作和Delete操作都要返回Json的字符串,也就是说,每一个Thrift接口方法的输入参数和返回参数都是Json字符串。标准返回,Code表示状态码,Desc表示对执行结果的描述,如果Code表示服务端出错,Desc为错误信息。

     public class StandResponse<T>
{
/// <summary>
/// 服务端成功调用:0
/// 服务端业务异常:-1
/// </summary>
public string Code { get; set; } public string Desc { get; set; } public T Data { get; set; }
}

二、一般情况,服务端和客户端服务器应在同一个内网,所以可以不用进行接口调用的身份验证,只需保证服务端不被外网访问即可。当然也可以简单的,采用对客户端调用时间戳加密,并把时间戳和密文发到服务端后,用相同的加密算法对时间戳加密,对密文进行比较来验证。

//标准请求,包括客户端服务器的IP及主机名,如果客户端是Web服务器,用户请求的URL和用户请求IP也会一并带到服务器端,用于做验证或请求限制。
public class StandRequest<T>
{
private string SIGN_KEY = ConfigurationManager.AppSettings["ThriftSignKey"] ?? "Thrift_Sign"; public string HostName { get; set; } public string IP { get; set; } public string RequestUrl { get; set; } public string UserRequestIP { get; set; } public string TimeStamp { get; set; } public string Sign { get; set; } public T Data { get; set; } public string SignTimestamp(string timeStamp)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] array = md5.ComputeHash(Encoding.UTF8.GetBytes(timeStamp + SIGN_KEY));
return Convert.ToBase64String(array);
} //验证客户请求是否合法
public bool IsValid()
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] array = md5.ComputeHash(Encoding.UTF8.GetBytes(TimeStamp + SIGN_KEY));
string sign = Convert.ToBase64String(array);
return Sign.Equals(sign);
}
}

可通过在客户端和服务端,配置相同的ThriftSignKey来确保请求来自自己开发的客户端。其实在标准请求里还应该定义一个参数,就是当前请求的时间,这样到服务端时可以监控请求在传输时的时延。

三、使用了标准输入输出来传输数据,但每次让开发者去序列化标准输入输出进行这些重复性劳动也是不能忍的。以及让开发者去处理超时异常和服务端如果抛出未经处理的异常,客户端和服务端会失去连接,客户端抛TTransportException,每请求一次都让开发者来处理这些异常,让人难以接受。我再次想到使用反射,这样我可以在结果返回给开发者之前做一些处理。相当于拦截器,或者理解为面向切面的处理。

         private string serviceName;

         private TTransport transport;

         private T instance;        

         private bool disposed;

         public ThriftClient(string serviceName)
{
disposed = false;
this.serviceName = serviceName;
transport = ThriftFactory.BorrowInstance(serviceName);
TProtocol protocol = new TBinaryProtocol(transport);
//使用Invoker创建实例,InvokerEmitter抛异常“找不到公开实例构造方法”,可能是内部类获取的类名有点奇怪导致,例如“HelloWorldService.Client”。
//instance = (T)Invoker.CreateInstance<T>(protocol);
instance = (T)Activator.CreateInstance(typeof(T),protocol);
}

初始化得到一个服务的Client,也就是instance。

    真正的访问服务端的方法被封装成一个使用标准输入输出的私有化方法,完成服务端和客户端通信的方法就只有Invoker.MethodInvoke一行,其余的都是对异常进行处理。

          private StandResponse<P> Invoke<Q, P>(string methodName, StandRequest<Q> request)
{
StandResponse<P> res = null;
StandResponse<string> response = null;
try
{
string result = (string)Invoker.MethodInvoke(instance, methodName, SerializeHelper.JsonSerialize2(request));
response = SerializeHelper.JsonDeserialize2<StandResponse<string>>(result);
}
catch (IOException idEx)
{
throw new ThriftException(string.Format("请求超时。\r请求服务:{0}\r请求方法:{1}\r请求数据:{2}", serviceName, methodName, SerializeHelper.JsonSerialize2(request)));
}
catch (TTransportException transEx)
{
throw new ThriftException(string.Format("服务端未处理系统异常。\r请求服务:{0}\r请求方法:{1}\r请求数据:{2}", serviceName, methodName, SerializeHelper.JsonSerialize2(request)));
}
catch(Exception ex)
{
throw ex;
}
if (response != null && response.Code == "-1")
{
throw new ThriftException(string.Format("请求数据异常。\r请求服务:{0}\r请求方法:{1}\r请求数据:{2}", serviceName, methodName, SerializeHelper.JsonSerialize2(request)));
}
if (response != null && response.Code == "-2")
{
throw new ThriftException(string.Format("服务端系统异常。\r请求服务:{0}\r请求方法:{1}\r请求数据:{2}", serviceName, methodName, SerializeHelper.JsonSerialize2(request)));
}
if (response != null)
{
res = new StandResponse<P>()
{
Code = response.Code,
Desc = response.Desc,
Data = SerializeHelper.JsonDeserialize2<P>(response.Data)
};
}
return res;
}

然后再对这个私有的方法进一步封装,返回开发者看得懂的数据结构。

        /// <summary>
/// 无返回接口调用
/// </summary>
/// <typeparam name="Q"></typeparam>
/// <typeparam name="P"></typeparam>
/// <param name="methodName"></param>
/// <param name="request"></param>
public void Invoke<Q>(string methodName, Q request)
{
Invoke<Q, string>(methodName, BuildRequest<Q>(request));
} /// <summary>
/// 无参接口调用,服务端一定是有请求参数的,这里的无参是指Service方法无参
/// </summary>
/// <typeparam name="P"></typeparam>
/// <param name="methodName"></param>
/// <returns></returns>
public P Invoke<P>(string methodName)
{
StandResponse<P> response = Invoke<string, P>(methodName, BuildRequest<string>(string.Empty));
if (response != null)
{
return response.Data;
}
return default(P);
} public P Invoke<Q, P>(string methodName, Q request)
{
StandResponse<P> response = Invoke<Q, P>(methodName, BuildRequest<Q>(request));
if (response != null)
{
return response.Data;
}
return default(P);
}

关于Tcp连接的归还释放,请自行下载源码来看。

    下一篇会用一个Demo来示范怎样使用Thrift.Utility来快速暴露接口,让客户端和服务端通信。

Thrift微服务代码下载Thrift.Utility

Thrift搭建分布式微服务(三)的更多相关文章

  1. Thrift搭建分布式微服务(二)

    第二篇 连接池  连接池配置,请前往Thrift搭建分布式微服务(一)  下面要介绍的其实不是单一的连接池,应该说是连接池集合.因为它要管理多个Tcp Socket连接节点,每个服务节点都有设置了自己 ...

  2. Thrift搭建分布式微服务1

    Thrift搭建分布式微服务 一.Thrift是什么? 关于Thrift的基本介绍,参看张善友的文章Thrift简介. 二.为什么使用微服务? 在公司的高速发展过程中,随着业务的增长,子系统越来越多. ...

  3. Thrift搭建分布式微服务(四)

      第一篇 <连接配置> 第二篇 <连接池> 第三篇 <标准通信> 第四篇 快速暴露接口 之前的文章,我们介绍了如何使用连接池管理Thrift节点,以及使用Thri ...

  4. Thrift搭建分布式微服务(一)

    一.Thrift是什么? 关于Thrift的基本介绍,参看张善友的文章Thrift简介. 二.为什么使用微服务? 在公司的高速发展过程中,随着业务的增长,子系统越来越多.各系统间又不同程度的在某些逻辑 ...

  5. Kite: 一个分布式微服务框架(翻译)

    原文链接:https://blog.gopheracademy.com/birthday-bash-2014/kite-microservice-library/ 此为中文翻译 用GO语言来编写web ...

  6. Dapeng框架-开源高性能分布式微服务框架

    我们公司性质是新零售,公司也有专门的框架组.这群大牛自己开发了一整套分布式微服务框架.我们也在使用这套框架,有很多心得体会. 该框架既Dapeng也!开源github地址:https://github ...

  7. Train-Alypay-Cloud:分布式微服务中间件sofa 开发培训(第二次)

    ylbtech-Train-Alypay-Cloud:分布式微服务中间件sofa 开发培训(第二次) 1.返回顶部 1. 这是本次培训的内容,望各位提前配好环境.工具.2.6-2.7 我们在环球金融8 ...

  8. Surging 分布式微服务框架使用入门

    原文:Surging 分布式微服务框架使用入门 前言 本文非 Surging 官方教程,只是自己学习的总结.如有哪里不对,还望指正. 我对 surging 的看法 我目前所在的公司采用架构就是类似与S ...

  9. [转载]Surging 分布式微服务框架使用入门

    前言 本文非 Surging 官方教程,只是自己学习的总结.如有哪里不对,还望指正. 我对 surging 的看法 我目前所在的公司采用架构就是类似与Surging的RPC框架,在.NET 4.0框架 ...

随机推荐

  1. strcat()函数常见问题

    strcat(char *_Destination,const char *_Source)函数的功能是将后一个字符串粘贴到前一个字符串的末尾 原型 char *strcat(char *_Desti ...

  2. hdu 4738 Caocao's Bridges 图--桥的判断模板

    Caocao's Bridges Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  3. matlab中subplot函数的功能

    转载自http://wenku.baidu.com/link?url=UkbSbQd3cxpT7sFrDw7_BO8zJDCUvPKrmsrbITk-7n7fP8g0Vhvq3QTC0DrwwrXfa ...

  4. sqlserver 2005 分布式架构 对等事务复制 .

    http://www.cnblogs.com/qanholas/archive/2012/03/22/2412444.html     一.为什么要使用对等事务复制 首先要说明的是使用sqlserve ...

  5. Hadoop 分布式文件系统 - HDFS

    当数据集超过一个单独的物理计算机的存储能力时,便有必要将它分不到多个独立的计算机上.管理着跨计算机网络存储的文件系统称为分布式文件系统.Hadoop 的分布式文件系统称为 HDFS,它 是为 以流式数 ...

  6. uva 10305 ordering tasks(超级烂题)——yhx

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABHIAAAHDCAYAAABI5T2bAAAgAElEQVR4nOydPY7svLW1awQGNABHCm

  7. POJ-1979 Red and Black(DFS)

    题目链接:http://poj.org/problem?id=1979 深度优先搜索非递归写法 #include <cstdio> #include <stack> using ...

  8. 【实践】jdbc批量插入数据

    参考文献:http://my.oschina.net/u/1452675/blog/203670 http://superjavason.iteye.com/blog/255423 /*测试批量写入数 ...

  9. UESTC 882 冬马党 --状压DP

    定义:dp[i][j]为状态为j时,第i行符合条件的状态数 转移方程:dp[i][j] += dp[i-1][t]   //t为上一行状态,与当前行不冲突. 从第一行开始向下枚举,每次枚举当前行的状态 ...

  10. UVA 11766 Racing Car Computer --DP

    题意:电脑记录了某一时刻每个赛车的前面和后面个有多少辆车(多个车并排时在别的车那只算一辆),问最少有多少个不合理的数据. 分析:看到n<=1000时,就尽量往DP上想吧. 每输入一组数据a,b, ...