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框架 ...
随机推荐
- nodejs创建一个HTTP服务器 简单入门级
const http = require('http');//请求http.createServer(function(request, response){ /*createServer该函数 ...
- 烂泥:【解决】ubuntu使用远程NFS报错
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 今天在ubuntu系统上使用远程NFS,发现一直报错无法使用. 查看NFS挂载命令没有错误,命令如下: mount -t nfs 192.168.1.1 ...
- poj 3237 Tree [LCA] (树链剖分)
poj 3237 tree inline : 1. inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开),没有了调用的开销,效率也很高. 2. 很明显,类 ...
- linux硬链接与软链接
在linux操作系统中的文件数据除了实际的内容外,还会含有文件权限(rwx)与文件属性(owner,group,other等),文件系统通常会将这两部分的数据存放在不同的区块,文件权限与文件属性放置在 ...
- GPS 坐标距离计算
CREATE FUNCTION [dbo].[Rad]( @d float ) RETURNS float BEGIN return @d * PI()/ 180.00; End CREATE FUN ...
- log4j日志优先级问题的后续
前文:http://www.cnblogs.com/chyu/p/4280440.html 出现一处吐槽失误,当时还想怎么会设置成warn级别.. <appender name="ST ...
- NOIP2007 T1奖学金 解题报告-S.B.S.
洛谷P1093 题目描述 某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前5名学生发奖学金.期末,每个学生都有3门课的成绩:语文.数学.英语.先按总分从高到低排序,如果两个同学总分相同, ...
- 从BUG工具redmine上获取数据后借助python模块pycha 画出BUG分析类报表
整体代码比较冗长,但是很好读.写的方法全是按照BUG分类去写的.所以写死了,凑合看吧,画出饼图,树状图和生成对应的数据excel,希望大家举一反三能帮助自己分析BUG #__author__ = 'x ...
- css3的媒体查询(Media Queries)
我今天就总结一下响应式设计的核心CSS技术Media(媒体查询器)的用法. 先看一个简单的例子: <link rel="stylesheet" media="scr ...
- UESTC 1227 & POJ 3667 Hotel
非常细腻的线段树题目啊,后来还是有个细节写错了,查了一个晚上..就不分析了. 代码: #include <iostream> #include <cstdio> #includ ...