因网站组(.net)与游戏服务端(c++)原来使用REST API通讯效率稍显低下,准备下期重构时改用rpc方式,经比较Thrift和gRPC两者的优劣(参照网上的对比结果),最终决定使用Thrift。

首先下载Thrift代码生成器,编写根据Thrift的语法规范(可参看https://www.cnblogs.com/tianhuilove/archive/2011/09/05/2167669.html)编写脚本文件OrderService.thrift,如下:

  1. namespace csharp Qka.Contract
  2.  
  3. service OrderService{
  4.  
  5. InvokeResult Create(1:Order order)
  6.  
  7. Order Get(1:i32 orderId)
  8.  
  9. list<Order> GetListByUserId(1:i32 userId,2:bool isPaid)
  10.  
  11. InvokeResult Delete(1:i32 orderId)
  12. }
  13.  
  14. enum ResponseCode {
  15. SUCCESS = 0,
  16. FAILED = 1,
  17. }
  18.  
  19. struct Order {
  20. 1: required i32 OrderId;
  21. 2: required i32 SkuId;
  22. 3: required i32 Amount;
  23. 4: optional string Remark;
  24. }
  25.  
  26. struct InvokeResult {
  27. 1: required ResponseCode code;
  28. 2: optional string Message;
  29. }

在Thrift代码生成器的目录下执行命令:./thrift.exe -gen csharp OrderService.thrift,发现同目录下多了一个gen-csharp文件夹,生成的代码放在这个文件夹里面。

新建一个.net core的解决方案,结构如下:

三个项目均添加apache-thrift-netcore的nuget包(这里服务端寄宿在asp.net core程序的原因是因为我们采用微服务的模式,每一块业务的UI、Rest API、RPC Server全部放在一块),将刚刚生成的代码文件拷至Qka.Contract项目里,其他两个项目添加Qka.Contract项目的引用。

在Qka.WebServer中实现服务接口:

  1. public class OrderServiceImpl : Iface
  2. {
  3. public InvokeResult Create(Order order)
  4. {
  5. return new InvokeResult
  6. {
  7. Code = ResponseCode.SUCCESS,
  8. Message = $"订单{order.OrderId}创建成功!"
  9. };
  10. }
  11.  
  12. public InvokeResult Delete(int orderId)
  13. {
  14. return new InvokeResult
  15. {
  16. Code = ResponseCode.SUCCESS,
  17. Message = $"订单{orderId}删除成功成功!"
  18. };
  19. }
  20.  
  21. public Order Get(int orderId)
  22. {
  23. return new Order
  24. {
  25. OrderId = ,
  26. SkuId = ,
  27. Amount = ,
  28. Remark = "黄金万两"
  29. };
  30. }
  31.  
  32. public List<Order> GetListByUserId(int userId, bool isPaid)
  33. {
  34. return new List<Order>
  35. {
  36. new Order
  37. {
  38. OrderId = ,
  39. SkuId = ,
  40. Amount = ,
  41. Remark = "黄金万两"
  42. },
  43. new Order
  44. {
  45. OrderId = ,
  46. SkuId = ,
  47. Amount = ,
  48. Remark = "白银百两"
  49. },
  50. };
  51. }
  52. }

编写ApplicationExtenssion,代码如下:

  1. public static class ApplicationExtenssion
  2. {
  3. public static IApplicationBuilder UseThriftServer(this IApplicationBuilder appBuilder)
  4. {
  5. var orderService = new OrderServiceImpl();
  6. Processor processor = new Processor(orderService);
  7. TServerTransport transport = new TServerSocket();
  8. TServer server = new TThreadPoolServer(processor, transport);
  9.  
  10. var services = appBuilder.ApplicationServices.CreateScope().ServiceProvider;
  11.  
  12. var lifeTime = services.GetService<IApplicationLifetime>();
  13. lifeTime.ApplicationStarted.Register(() =>
  14. {
  15. server.Serve();
  16. });
  17. lifeTime.ApplicationStopped.Register(() =>
  18. {
  19. server.Stop();
  20. transport.Close();
  21. });
  22.  
  23. return appBuilder;
  24. }
  25. }

上面的代码用的是TThreadPoolServer,网上的代码均采用TSimpleServer,通过反编译比较TSimpleServer、TThreadedServer、TThreadPoolServer,发现TSimpleServer只能同时响应一个客户端,TThreadedServer则维护了一个clientQueue,clientQueue最大值是100,TThreadPoolServer则用的是用线程池响应多个客户请求,生产环境绝不能用TSimpleServer。

在Startup.cs文件的Configure方法中添加:

  1. app.UseThriftServer();

服务端代码大功告成,再来编写客户端调用代码:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. TTransport transport = new TSocket("localhost", );
  6. TProtocol protocol = new TBinaryProtocol(transport);
  7. var client = new OrderService.Client(protocol);
  8. transport.Open();
  9.  
  10. var createResult = client.Create(new Order
  11. {
  12. OrderId = ,
  13. SkuId = ,
  14. Amount = ,
  15. Remark = "测试创建订单"
  16. });
  17. var order = client.Get();
  18. var list = client.GetListByUserId(, true);
  19. var deleteResult = client.Delete();
  20.  
  21. transport.Close();
  22.  
  23. Console.ReadKey();
  24. }
  25. }

下面这段话引自https://www.cnblogs.com/cyfonly/p/6059374.html,解释上面代码中为什么采用TSocket和TBinaryProtocol:

  1. Thrift 支持多种传输协议,用户可以根据实际需求选择合适的类型。Thrift 传输协议上总体可划分为文本 (text) 和二进制 (binary) 传输协议两大类,一般在生产环境中使用二进制类型的传输协议为多数(相对于文本和 JSON 具有更高的传输效率)。常用的协议包含:
  2. TBinaryProtocol:是Thrift的默认协议,使用二进制编码格式进行数据传输,基本上直接发送原始数据
  3. TCompactProtocol:压缩的、密集的数据传输协议,基于Variable-length quantityzigzag 编码格式
  4. TJSONProtocol:以JSON (JavaScript Object Notation)数据编码协议进行数据传输
  5. TDebugProtocol:常常用以编码人员测试,以文本的形式展现方便阅读
  6. 关于以上几种类型的传输协议,如果想更深入更具体的了解其实现及工作原理,可以参考站外相关文章《thrift源码研究》。
  7.  
  8. 传输方式
  9. 与传输协议一样,Thrift 也支持几种不同的传输方式。
  10. 1. TSocket:阻塞型 socket,用于客户端,采用系统函数 read write 进行读写数据。
  11. 2. TServerSocket:非阻塞型 socket,用于服务器端,accecpt 到的 socket 类型都是 TSocket(即阻塞型 socket)。
  12. 3. TBufferedTransport TFramedTransport 都是有缓存的,均继承TBufferBase,调用下一层 TTransport 类进行读写操作吗,结构极为相似。其中 TFramedTransport 以帧为传输单位,帧结构为:4个字节(int32_t)+传输字节串,头4个字节是存储后面字节串的长度,该字节串才是正确需要传输的数据,因此 TFramedTransport 每传一帧要比 TBufferedTransport TSocket 多传4个字节。
  13. 4. TMemoryBuffer 继承 TBufferBase,用于程序内部通信用,不涉及任何网络I/O,可用于三种模式:(1OBSERVE模式,不可写数据到缓存;(2TAKE_OWNERSHIP模式,需负责释放缓存;(3COPY模式,拷贝外面的内存块到TMemoryBuffer
  14. 5. TFileTransport 直接继承 TTransport,用于写数据到文件。对事件的形式写数据,主线程负责将事件入列,写线程将事件入列,并将事件里的数据写入磁盘。这里面用到了两个队列,类型为 TFileTransportBuffer,一个用于主线程写事件,另一个用于写线程读事件,这就避免了线程竞争。在读完队列事件后,就会进行队列交换,由于由两个指针指向这两个队列,交换只要交换指针即可。它还支持以 chunk(块)的形式写数据到文件。
  15. 6. TFDTransport 是非常简单地写数据到文件和从文件读数据,它的 write read 函数都是直接调用系统函数 write read 进行写和读文件。
  16. 7. TSimpleFileTransport 直接继承 TFDTransport,没有添加任何成员函数和成员变量,不同的是构造函数的参数和在 TSimpleFileTransport 构造函数里对父类进行了初始化(打开指定文件并将fd传给父类和设置父类的close_policyCLOSE_ON_DESTROY)。
  17. 8. TZlibTransport TBufferedTransport TFramedTransport一样,调用下一层 TTransport 类进行读写操作。它采用<zlib.h>提供的 zlib 压缩和解压缩库函数来进行压解缩,写时先压缩再调用底层 TTransport 类发送数据,读时先调用 TTransport 类接收数据再进行解压,最后供上层处理。
  18. 9. TSSLSocket 继承 TSocket,阻塞型 socket,用于客户端。采用 openssl 的接口进行读写数据。checkHandshake()函数调用 SSL_set_fd fd ssl 绑定在一起,之后就可以通过 ssl SSL_readSSL_write 接口进行读写网络数据。
  19. 10. TSSLServerSocket 继承 TServerSocket,非阻塞型 socket 用于服务器端。accecpt 到的 socket 类型都是 TSSLSocket 类型。
  20. 11. THttpClient THttpServer 是基于 Http1.1 协议的继承 Transport 类型,均继承 THttpTransport,其中 THttpClient 用于客户端,THttpServer 用于服务器端。两者都调用下一层 TTransport 类进行读写操作,均用到TMemoryBuffer 作为读写缓存,只有调用 flush() 函数才会将真正调用网络 I/O 接口发送数据。

  

.net core下使用Thrift的更多相关文章

  1. 在.net core中使用Thrift

    Thrift应用比较广泛,这里不介绍Thrift的基本概念和使用.Thrift对.net支持的很好,但自从.net core诞生引来,我曾多次关注Thrift的官方网站,看看对.net core是否提 ...

  2. 4.5 .net core下直接执行SQL语句并生成DataTable

    .net core可以执行SQL语句,但是只能生成强类型的返回结果.例如var blogs = context.Blogs.FromSql("SELECT * FROM dbo.Blogs& ...

  3. .Net Core 之 图形验证码 本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能。

    本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能. 通过测试的系统: Windows 8.1 64bit Ubuntu Server 16.04 LTS 64 ...

  4. .NET Core下使用gRpc公开服务(SSL/TLS)

    一.前言 前一阵子关于.NET的各大公众号都发表了关于gRpc的消息,而随之而来的就是一波关于.NET Core下如何使用的教程,但是在这众多的教程中基本都是泛泛而谈,难以实际在实际环境中使用,而该篇 ...

  5. .Net Core下如何管理配置文件

    一.前言 根据该issues来看,System.Configuration在.net core中已经不存在了,那么取而代之的是由Microsoft.Extensions.Cnfiguration.XX ...

  6. .Net Core下如何管理配置文件(转载)

    原文地址:http://www.cnblogs.com/yaozhenfa/p/5408009.html 一.前言 根据该issues来看,System.Configuration在.net core ...

  7. c# .net core 下的网络请求

    本文章是在VS2017的环境下,.net core 1.1版本以上. 在这期间,由于.net core 并不基于IIS,我们的过去的网络请求代码在.net core框架下,有可能会出现不兼容,报错的现 ...

  8. .Net Core下通过Proxy 模式 使用 WCF

    .NET Core下的WCF客户端也是开源的,这次发布.NET Core 2.0,同时也发布了 WCF for .NET Core 2.0.0, 本文介绍在.NET Core下如何通过Proxy 消费 ...

  9. .Net Core下使用WCF

    在.net core 下的wcf 和framework下的wcf使用方式有点不太一样.在core下用wc,需要安装VS扩展Visual Studio WCF Connected Service,目前这 ...

随机推荐

  1. SQL中内连接和外连接的问题!

    本文系转载,版权归原作者所有. 如表      -------------------------------------------------      table1 | table2 |    ...

  2. j2EE经典面试题

    1. hibernate中离线查询去除重复项怎么加条件? dc.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 2. http协议及端口,sm ...

  3. jvm栈-运行控制,jvm-堆运行存储共享单元

     JVM-栈 2012-09-17 15:43:53 分类: Java 原文转自:http://www.blogjava.net/nkjava/archive/2012/03/15/371971.ht ...

  4. 解决记录:win10 无法安装VS2017,visual studio installer下载进度始终为0

    问题描述:win10 下无法安装VS2017,visual studio installer下载进度始终为0,点击取消按钮后,也没有反应,visual studio installer也关闭不掉: 具 ...

  5. ffmpeg 时间戳处理

    视频的显示和存放原理 对于一个电影,帧是这样来显示的:I B B P.现在我们需要在显示B帧之前知道P帧中的信息.因此,帧可能会按照这样的方式来存储:IPBB.这就是为什么我们会有一个解码时间戳和一个 ...

  6. Android OkHttp文件上传与下载的进度监听扩展

    http://www.loongwind.com/archives/290.html 上一篇文章介绍了用Retrofit实现文件的上传与下载,但是我们发现没办法监听上传下载的进度,毕竟我们在做开发的时 ...

  7. .gitignore文件不起作用的解决方法

    http://keendawn.blog.163.com/blog/static/88880743201531554431124/ git rm -r --cached . git add . git ...

  8. Ocelot中文文档-Websockets

    Ocelot额外支持代理websockets.这个功能在问题 212中被提出. 为了是Ocelot代理websocket,你需要做如下事情. 在你的Configure方法中,你要告知应用程序使用Web ...

  9. 微信小程序之获取用户位置权限(拒绝后提醒)

    微信小程序获取用户当前位置有三个方式: 1. wx.getLocation(多与wx.openLocation一起用) 获取当前的精度.纬度.速度.不需要授权.当type设置为gcj02 返回可用于w ...

  10. python之文件操作(基础)

    文件操作作为python基础中的重点,必须要掌握. 1.默认我们在本地电脑E盘新建wp.txt文件进行测试,文件内容如下设置. 2.进行代码编写: f=open("E://wp.txt&qu ...