.NET Core微服务之路:利用DotNetty实现一个简单的通信过程
2018-10-27 11:03
696 查看
上一篇我们已经全面的介绍过《基于gRPC服务发现与服务治理的方案》,我们先复习一下RPC的调用过程(笔者会在这一节的几篇文章中反复的强调这个过程调用方案),看下图
namespace Echo.Client { using System; using System.Text; using DotNetty.Buffers; using DotNetty.Transport.Channels; public class EchoClientHandler : ChannelHandlerAdapter { readonly IByteBuffer initialMessage; public override void ChannelActive(IChannelHandlerContext context) => context.WriteAndFlushAsync(this.initialMessage); public override void ChannelRead(IChannelHandlerContext context, object message) { if (message is IByteBuffer byteBuffer) { Console.WriteLine("Received from server: " + byteBuffer.ToString(Encoding.UTF8)); } } public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush(); public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) { Console.WriteLine("Exception: " + exception); context.CloseAsync(); } } }View Code 非常简单,将数据流显示到控制台。
实现结果
至此,我们使用DotNetty框架搭建简单的应答服务器就这样做好了,很简单,实现效果如下: C端主动向S端主动发送数据后,S端收到数据,在控制台打印出数据,并回传给C端,当然,S端还可以做很多很多的事情。
DotNetty内部调试记录分析
虽然DotNetty官方没有提供任何技术文档,但官方却提供了详细的调试记录,很多时候,我们学习者其实也可以通过调试记录来分析某一个功能的实现流程。我们可以通过将DotNetty的内部输入输出记录打印到控制台上。
InternalLoggerFactory.DefaultFactory.AddProvider(new ConsoleLoggerProvider((s, level) => true, false));
可以看到服务端的打印记录一下多出来了许多许多,有大部分是属于DotNetty内部调试时的打印记录,我们只着重看如下的部分。
dbug: SRV-LSTN[0] [id: 0x3e8afca1] HANDLER_ADDED dbug: SRV-LSTN[0] [id: 0x3e8afca1] REGISTERED (1) dbug: SRV-LSTN[0] [id: 0x3e8afca1] BIND: 0.0.0.0:8007 (2) wait the client input dbug: SRV-LSTN[0] [id: 0x3e8afca1, 0.0.0.0:8007] ACTIVE (3) dbug: SRV-LSTN[0] [id: 0x3e8afca1, 0.0.0.0:8007] READ (4) dbug: SRV-LSTN[0] [id: 0x3e8afca1, 0.0.0.0:8007] RECEIVED: [id: 0x7bac2775, 127.0.0.1:64073 :> 127.0.0.1:8007] (5) dbug: SRV-LSTN[0] [id: 0x3e8afca1, 0.0.0.0:8007] RECEIVED_COMPLETE (6) dbug: SRV-LSTN[0] [id: 0x3e8afca1, 0.0.0.0:8007] READ (7) dbug: SRV-CONN[0] [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] HANDLER_ADDED (8) dbug: SRV-CONN[0] [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] REGISTERED (9) dbug: SRV-CONN[0] [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] ACTIVE (10) dbug: SRV-CONN[0] [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] READ (11) dbug: DotNetty.Buffers.AbstractByteBuffer[0] (12) -Dio.netty.buffer.bytebuf.checkAccessible: True dbug: SRV-CONN[0] [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] RECEIVED: 14B (13) +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |100000000| 00 0C 68 65 6C 6C 6F 20 77 6F 72 6C 64 21 |..hello world! | +--------+-------------------------------------------------+----------------+ Received from client: hello world! dbug: SRV-CONN[0] (14) [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] WRITE: 2B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |100000000| 00 0C |.. | +--------+-------------------------------------------------+----------------+ dbug: SRV-CONN[0] (15) [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] WRITE: 12B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |100000000| 68 65 6C 6C 6F 20 77 6F 72 6C 64 21 |hello world! | +--------+-------------------------------------------------+----------------+ dbug: SRV-CONN[0] (16) [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] RECEIVED_COMPLETE dbug: SRV-CONN[0] (17) [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] FLUSH dbug: SRV-CONN[0] (18) [id: 0x7bac2775, 127.0.0.1:64073 => 127.0.0.1:8007] READ
咋一看,有18个操作,好像有点太多了,其实不然,还有很多很多的内部调试细节并没打印到控制台上。
- 通过手动建立的工作线程组,并将这组线程注册到管道中,这个管道可以是基于SOCKER,可以基于IChannel(1);
- 绑定自定的IP地址和端口号到自定义管道上(2);
- 激活自定义管道(3);
- 开始读取(其实也是开始监听)(4);
- 收到来自id为0x7bac2775的客户端连接请求,建立连接,并继续开始监听(5)(6)(7);
- 从第8步开始,日志已经变成id为0x7bac2775的记录了,当然一样包含注册管道,激活管道,开始监听等等与S端一模一样的操作(8)(9)(10)(11)
- 当笔者输入一条"hello world!"数据后,DotNetty.Buffers.AbstractByteBuffer会进行数据类型检查,以便确认能将数据放入到管道中。(12)
- 将数据发送到S端,数据大小为14B,hello world前有两个点,代表这是数据头,紧接着再发送两个点,但没有任何数据,代表数据已经结束。DotNetty将数据的十六进制存储位用易懂的方式表现了出来,很人性化。(13)(14)
- S端收到数据没有任何加工和处理,马上将数据回传到C端。(15)(16)
- 最后,当这个过程完成后,需要将缓存区的数据强制写入到管道中,所以会执行一次Flush操作,整个传输完成。接下来,不管是C端还是S端,继续将自己的状态改成READ,用于监听管道中的各种情况,比如连接状态,数据传输等等(17)。
总结
对于刚开始接触Socket编程的朋友而言,这是个噩梦,因为Socket编程的复杂性不会比多线程容易,甚至会更复杂。协议,压缩,传输,多线程,监听,流控制等等一系列问题摆在面前,因此而诞生了Netty这样优秀的开源框架,但是Netty是个半成品,因为你需要基于他来实现自己想要的协议,传输等等自定义操作,而底层的内容,你完全不用关心。不像某些框架,比如Newtonsoft.Json这样的功能性框架,不用配置,不用自定义,直接拿来用就可以了。 虽然DotNetty帮我们实现了底层大量的操作,但如果不熟悉或者一点也不懂网络通信,同样对上面的代码是一头雾水,为何?行情需要,我们程序员天天都在赶业务,哪有时间去了解和学习更多的细节...通过将调试记录打印出来,并逐行挨个的对照代码进行分析,就会慢慢开始理解最简单的通信流程了。 本篇只是实现了基于DotNetty最简单的通讯过程,也只是将数据做了一下回路,并没做到任何与RPC有关的调用,下一篇我们开始讲这个例子深入,介绍基于DotNetty的RPC调用。 码字不易,感谢阅读!
相关文章推荐
- .NET Core微服务之路:利用DotNetty实现一个简单的通信过程
- 【远程调用框架】如何实现一个简单的RPC框架(三)优化一:利用动态代理改变用户服务调用方式
- 一个简单的oracle分页存储过程的实现和调用
- 利用boost::asio实现一个简单的服务器框架
- 利用OpenGL在窗体上显示出一个最简单的形状的过程
- 一个利用sql 语句来实现分页的存储过程
- 利用Compass实现一个简单的搜索引擎
- 一个简单的oracle分页存储过程的实现和调用
- 利用java实现一个简单的远程监控程序
- 简单的使用自动化技术实现用WORD读取一个XML文件的过程~
- 一步一步教你实现一个简单的云服务
- Java 利用套接字Socket实现简单的服务器与客户端通信
- 一个简单的oracle分页存储过程的实现和调用
- 利用 Axis 实现一个简单的 Web Serive例子
- 一个简单的oracle分页存储过程的实现和调用
- 利用xmlHttp实现一个简单的Ajax无刷新
- 一个简单的oracle分页存储过程的实现和调用
- Windows CE .NET中实现服务的简单过程
- 利用java实现一个简单的远程监控程序
- 利用Compass实现一个简单的搜索引擎