Thrift搭建分布式微服务(三)
第一篇 《连接配置》
第二篇 《连接池》
第三篇 标准通信
一、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搭建分布式微服务(三)的更多相关文章
- Thrift搭建分布式微服务(二)
第二篇 连接池 连接池配置,请前往Thrift搭建分布式微服务(一) 下面要介绍的其实不是单一的连接池,应该说是连接池集合.因为它要管理多个Tcp Socket连接节点,每个服务节点都有设置了自己 ...
- Thrift搭建分布式微服务1
Thrift搭建分布式微服务 一.Thrift是什么? 关于Thrift的基本介绍,参看张善友的文章Thrift简介. 二.为什么使用微服务? 在公司的高速发展过程中,随着业务的增长,子系统越来越多. ...
- Thrift搭建分布式微服务(四)
第一篇 <连接配置> 第二篇 <连接池> 第三篇 <标准通信> 第四篇 快速暴露接口 之前的文章,我们介绍了如何使用连接池管理Thrift节点,以及使用Thri ...
- Thrift搭建分布式微服务(一)
一.Thrift是什么? 关于Thrift的基本介绍,参看张善友的文章Thrift简介. 二.为什么使用微服务? 在公司的高速发展过程中,随着业务的增长,子系统越来越多.各系统间又不同程度的在某些逻辑 ...
- Kite: 一个分布式微服务框架(翻译)
原文链接:https://blog.gopheracademy.com/birthday-bash-2014/kite-microservice-library/ 此为中文翻译 用GO语言来编写web ...
- Dapeng框架-开源高性能分布式微服务框架
我们公司性质是新零售,公司也有专门的框架组.这群大牛自己开发了一整套分布式微服务框架.我们也在使用这套框架,有很多心得体会. 该框架既Dapeng也!开源github地址:https://github ...
- Train-Alypay-Cloud:分布式微服务中间件sofa 开发培训(第二次)
ylbtech-Train-Alypay-Cloud:分布式微服务中间件sofa 开发培训(第二次) 1.返回顶部 1. 这是本次培训的内容,望各位提前配好环境.工具.2.6-2.7 我们在环球金融8 ...
- Surging 分布式微服务框架使用入门
原文:Surging 分布式微服务框架使用入门 前言 本文非 Surging 官方教程,只是自己学习的总结.如有哪里不对,还望指正. 我对 surging 的看法 我目前所在的公司采用架构就是类似与S ...
- [转载]Surging 分布式微服务框架使用入门
前言 本文非 Surging 官方教程,只是自己学习的总结.如有哪里不对,还望指正. 我对 surging 的看法 我目前所在的公司采用架构就是类似与Surging的RPC框架,在.NET 4.0框架 ...
随机推荐
- MyCat 学习笔记 第八篇.数据分片 之 求摸运算分片
1 应用场景 Mycat 自带了多套数据分片的机制,其实根据数值取摸应该是最简单的一种. 优点:数据离散概率较为平均,可以有效的提高应用的数据吞吐. 缺点:比较明显,后期数据运维与迁移比较困难.好在M ...
- MyCat 学习笔记 第七篇.数据分片 之 按数据范围分片
1 应用场景 Mycat 其实自带了2个数据范围分片的方案,一个是纯数据范围的分片,比如 1至 10000 号的数据放到分片1 ,10001 至 20000号数据放到分片2里. 另一个是数据常量形式的 ...
- Android开发之 Android 的基本组件的概述
Android是一个为组件化而搭建的平台,它的应用是由一些零散的有联系的组件组成,并通过AndroidManifest.xml文件 把它们绑定起来. Android常用的组件有: Activity(活 ...
- JavaScript中点号“.”的多义性
点号「.」在JavaScript中有两种语义 语义1.表示算术中的小数点(浮点数),如 2.5 语义2.取对象属性.方法,如 [].push(2) 这几乎没有任何难理解的地方,但下面这个问题则很有趣. ...
- C++ inline
内联函数相对于宏的区别和优点: 宏是在预处理时进行的机械替换,内联是在编译时进行的.内联函数是真正的函数,只是在调用时,没有调用开销,像宏一样进行展开.内联函数会进行参数匹配检查,相对于带参数的宏有很 ...
- [转]ionic tab view hide tab bar
http://stackoverflow.com/questions/23991852/how-do-i-hide-the-tabs-in-ionic-framework ////// tabs.ht ...
- Python中的逗号有什么作用?
最近研究python 遇到个逗号的问题 一直没弄明白 今天总算搞清楚了 1.逗号在参数传递中的使用: 这种情况不多说 没有什么不解的地方 就是形参或者实参传递的时候参数之间的逗号 例如def a ...
- 第2章 面向对象的设计原则(SOLID):5_迪米特法则
5. 迪米特法则(Law of Demeter,LoD) 5.1 定义 (1)应尽量减少其他对象之间的交互,对象只和自己的朋友交谈,即对其他依赖的类越少越好(不要和太多的类发生关系). (2)尽量不要 ...
- python playfair
#########################Playfair密码######################### #约定1:若明文字母数量为奇数,在明文末尾添加一个'Z' #约定2:'I'作为 ...
- SVN和git的使用(附github的简单玩法)
今天简单的总结了下SVN和git的使用,也尝试了下github,应该好好提高下自己的英文水平了,梦想有一天不再使用任何翻译软件. [svn]:集中式的代码管理工具(版本控制工具--版本记录) 1> ...