(七)分布式通信----Netty实现NIO通信
==>>点击查看本系列文章目录
目录
1. 消息监听器
2. 指令执行器
3. 消息发送器
4. 客户端工厂
5. 序列化工具
6. 通信主机
项目文件结构图

通信主机:

1. 消息监听器(黄色框)
这部分由 Netty 实现,Netty是一个异步且非阻塞的通信框架。TCP通信实现服务端和客户端的交互。
Netty 的简单描述如下:
客户端(调用方):负责发送要执行的指令。
服务端(接收方):分为主从线程。主线程负责接收指令,将指令存入缓存区中,等待执行完成后再通知客户端(非阻塞);
从线程,有不止一个线程(异步),负责从缓存池中取出线程依次执行(按队列先后顺序执行),我们通过程序来决定先执行哪个,比如先解码,后执行,再编码。
上层接口:
/// <summary>
/// 接受到消息的委托。
/// </summary>
/// <param name="sender">消息发送者。</param>
/// <param name="message">接收到的消息。</param>
public delegate Task ReceivedDelegate(IMessageSender sender, TransportMessage message); /// <summary>
/// 一个抽象的消息监听者。
/// </summary>
public interface IMessageListener
{
/// <summary>
/// 接收到消息的事件。
/// </summary>
event ReceivedDelegate Received; /// <summary>
/// 触发接收到消息事件。
/// </summary>
/// <param name="sender">消息发送者。</param>
/// <param name="message">接收到的消息。</param>
/// <returns>一个任务。</returns>
Task OnReceived(IMessageSender sender, TransportMessage message);
}
客户端:
/// <summary>
/// 消息监听者。
/// </summary>
public class DotNettyClientMessageListener : IMessageListener
{
#region Implementation of IMessageListener /// <summary>
/// 接收到消息的事件。
/// </summary>
public event ReceivedDelegate Received; /// <summary>
/// 触发接收到消息事件。
/// </summary>
/// <param name="sender">消息发送者。</param>
/// <param name="message">接收到的消息。</param>
/// <returns>一个任务。</returns>
public async Task OnReceived(IMessageSender sender, TransportMessage message)
{
if (Received == null)
return;
await Received(sender, message);
} #endregion Implementation of IMessageListener
}
服务端:
public class DotNettyServerMessageListener : IMessageListener, IDisposable
{
#region Field private readonly ILogger<DotNettyServerMessageListener> _logger;
private readonly ITransportMessageDecoder _transportMessageDecoder;
private readonly ITransportMessageEncoder _transportMessageEncoder;
private IChannel _channel; #endregion Field #region Constructor public DotNettyServerMessageListener(ILogger<DotNettyServerMessageListener> logger, ITransportMessageCodecFactory codecFactory)
{
_logger = logger;
_transportMessageEncoder = codecFactory.GetEncoder();
_transportMessageDecoder = codecFactory.GetDecoder();
} #endregion Constructor #region Implementation of IMessageListener public event ReceivedDelegate Received; /// <summary>
/// 触发接收到消息事件。
/// </summary>
/// <param name="sender">消息发送者。</param>
/// <param name="message">接收到的消息。</param>
/// <returns>一个任务。</returns>
public async Task OnReceived(IMessageSender sender, TransportMessage message)
{
if (Received == null)
return;
await Received(sender, message);
} #endregion Implementation of IMessageListener public async Task StartAsync(EndPoint endPoint)
{
if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug($"准备启动服务主机,监听地址:{endPoint}。"); IEventLoopGroup bossGroup = new MultithreadEventLoopGroup();
IEventLoopGroup workerGroup = new MultithreadEventLoopGroup();//Default eventLoopCount is Environment.ProcessorCount * 2
var bootstrap = new ServerBootstrap();
bootstrap
.Channel<TcpServerSocketChannel>()
.Option(ChannelOption.SoBacklog, )
.ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
.Group(bossGroup, workerGroup)
.ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
{
var pipeline = channel.Pipeline;
pipeline.AddLast(new LengthFieldPrepender());
pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, , , , ));
pipeline.AddLast(new TransportMessageChannelHandlerAdapter(_transportMessageDecoder));
pipeline.AddLast(new ServerHandler(async (contenxt, message) =>
{
var sender = new DotNettyServerMessageSender(_transportMessageEncoder, contenxt);
await OnReceived(sender, message);
}, _logger));
}));
try
{
_channel = await bootstrap.BindAsync(endPoint);
if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug($"服务主机启动成功,监听地址:{endPoint}。");
}
catch
{
_logger.LogError($"服务主机启动失败,监听地址:{endPoint}。 ");
}
} public void CloseAsync()
{
Task.Run(async () =>
{
await _channel.EventLoop.ShutdownGracefullyAsync();
await _channel.CloseAsync();
}).Wait();
} #region Implementation of IDisposable /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
Task.Run(async () =>
{
await _channel.DisconnectAsync();
}).Wait();
} #endregion Implementation of IDisposable #region Help Class private class ServerHandler : ChannelHandlerAdapter
{
private readonly Action<IChannelHandlerContext, TransportMessage> _readAction;
private readonly ILogger _logger; public ServerHandler(Action<IChannelHandlerContext, TransportMessage> readAction, ILogger logger)
{
_readAction = readAction;
_logger = logger;
} #region Overrides of ChannelHandlerAdapter public override void ChannelRead(IChannelHandlerContext context, object message)
{
Task.Run(() =>
{
var transportMessage = (TransportMessage)message;
_readAction(context, transportMessage);
});
} public override void ChannelReadComplete(IChannelHandlerContext context)
{
context.Flush();
} public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
context.CloseAsync();//客户端主动断开需要应答,否则socket变成CLOSE_WAIT状态导致socket资源耗尽
if (_logger.IsEnabled(LogLevel.Error))
_logger.LogError(null,exception, $"与服务器:{context.Channel.RemoteAddress}通信时发送了错误。");
} #endregion Overrides of ChannelHandlerAdapter
} #endregion Help Class
}
2. 指令执行器(红色框)
上图中只有服务端的实现,服务端接收到指令后会回调执行器中的过程。
客户端的时候需要在业务场景中来实现,根据业务不同,接收到服务端消息后执行的过程也不同。然后通过控制反转,由程序自动找到该过程。
上层接口:
/// <summary>
/// 一个抽象的服务执行器。
/// </summary>
public interface IServiceExecutor
{
/// <summary>
/// 执行。
/// </summary>
/// <param name="sender">消息发送者。</param>
/// <param name="message">调用消息。</param>
Task ExecuteAsync(IMessageSender sender, TransportMessage message);
}
实现类,代码中没有处理逻辑(服务发现、执行,后续有专题来谈),只有输出打印 "服务提供者接收到消息。" :
public class HttpServiceExecutor : IServiceExecutor
{
#region Field private readonly ILogger<HttpServiceExecutor> _logger;
#endregion Field #region Constructor public HttpServiceExecutor(ILogger<HttpServiceExecutor> logger)
{
_logger = logger;
} #endregion Constructor #region Implementation of IServiceExecutor /// <summary>
/// 执行。
/// </summary>
/// <param name="sender">消息发送者。</param>
/// <param name="message">调用消息。</param>
public async Task ExecuteAsync(IMessageSender sender, TransportMessage message)
{
if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("服务提供者接收到消息。");
return; }
#endregion Implementation of IServiceExecutor
}
3. 消息发送器(蓝色框)
将要发送的消息写入到通信管道中,服务端和客户端都有实现。
上层接口:
/// <summary>
/// 一个抽象的发送者。
/// </summary>
public interface IMessageSender
{
/// <summary>
/// 发送消息。
/// </summary>
/// <param name="message">消息内容。</param>
/// <returns>一个任务。</returns>
Task SendAsync(TransportMessage message); /// <summary>
/// 发送消息并清空缓冲区。
/// </summary>
/// <param name="message">消息内容。</param>
/// <returns>一个任务。</returns>
Task SendAndFlushAsync(TransportMessage message);
}
实现类基类:
/// <summary>
/// 基于DotNetty的消息发送者基类。
/// </summary>
public abstract class DotNettyMessageSender
{
private readonly ITransportMessageEncoder _transportMessageEncoder; protected DotNettyMessageSender(ITransportMessageEncoder transportMessageEncoder)
{
_transportMessageEncoder = transportMessageEncoder;
} protected IByteBuffer GetByteBuffer(TransportMessage message)
{
var data = _transportMessageEncoder.Encode(message);
//var buffer = PooledByteBufferAllocator.Default.Buffer();
return Unpooled.WrappedBuffer(data);
}
}
服务端:
/// <summary>
/// 基于DotNetty服务端的消息发送者。
/// </summary>
public class DotNettyServerMessageSender : DotNettyMessageSender, IMessageSender
{
private readonly IChannelHandlerContext _context; public DotNettyServerMessageSender(ITransportMessageEncoder transportMessageEncoder, IChannelHandlerContext context) : base(transportMessageEncoder)
{
_context = context;
} #region Implementation of IMessageSender /// <summary>
/// 发送消息。
/// </summary>
/// <param name="message">消息内容。</param>
/// <returns>一个任务。</returns>
public async Task SendAsync(TransportMessage message)
{
var buffer = GetByteBuffer(message);
await _context.WriteAsync(buffer);
} /// <summary>
/// 发送消息并清空缓冲区。
/// </summary>
/// <param name="message">消息内容。</param>
/// <returns>一个任务。</returns>
public async Task SendAndFlushAsync(TransportMessage message)
{
var buffer = GetByteBuffer(message);
await _context.WriteAndFlushAsync(buffer);
} #endregion Implementation of IMessageSender
}
客户端:
/// <summary>
/// 基于DotNetty客户端的消息发送者。
/// </summary>
public class DotNettyMessageClientSender : DotNettyMessageSender, IMessageSender, IDisposable
{
private readonly IChannel _channel; public DotNettyMessageClientSender(ITransportMessageEncoder transportMessageEncoder, IChannel channel) : base(transportMessageEncoder)
{
_channel = channel;
} #region Implementation of IDisposable /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
Task.Run(async () =>
{
await _channel.DisconnectAsync();
}).Wait();
} #endregion Implementation of IDisposable #region Implementation of IMessageSender /// <summary>
/// 发送消息。
/// </summary>
/// <param name="message">消息内容。</param>
/// <returns>一个任务。</returns>
public async Task SendAsync(TransportMessage message)
{
var buffer = GetByteBuffer(message);
await _channel.WriteAndFlushAsync(buffer);
} /// <summary>
/// 发送消息并清空缓冲区。
/// </summary>
/// <param name="message">消息内容。</param>
/// <returns>一个任务。</returns>
public async Task SendAndFlushAsync(TransportMessage message)
{
var buffer = GetByteBuffer(message);
await _channel.WriteAndFlushAsync(buffer);
} #endregion Implementation of IMessageSender
}
4. 客户端工厂(绿色框)
为啥没有服务端工厂呢,因为服务端是由服务端主机(Host)直接创建的,主机直接调用监听器监听端口。既然我们重写了通信过程,就不能用微软原有的WebHost,后续会讲到如何搭建自己的主机。
客户端工厂用来创建客户端,然后与服务端主机通信。
上层接口:
/// <summary>
/// 一个抽象的传输客户端工厂。
/// </summary>
public interface ITransportClientFactory
{
/// <summary>
/// 创建客户端。
/// </summary>
/// <param name="endPoint">终结点。</param>
/// <returns>传输客户端实例。</returns>
Task<ITransportClient> CreateClientAsync(EndPoint endPoint);
}
/// <summary>
/// 一个抽象的传输客户端。
/// </summary>
public interface ITransportClient
{
/// <summary>
/// 发送消息。
/// </summary>
/// <param name="message">远程调用消息模型。</param>
/// <returns>远程调用消息的传输消息。</returns>
Task SendAsync(TransportMessage transportMessage);
}
实现类:
/// <summary>
/// 基于DotNetty的传输客户端工厂。
/// </summary>
public class DotNettyTransportClientFactory : ITransportClientFactory, IDisposable
{
#region Field private readonly ITransportMessageEncoder _transportMessageEncoder;
private readonly ITransportMessageDecoder _transportMessageDecoder;
private readonly ILogger<DotNettyTransportClientFactory> _logger;
private readonly IServiceExecutor _serviceExecutor;
private readonly ConcurrentDictionary<EndPoint, Lazy<Task<ITransportClient>>> _clients = new ConcurrentDictionary<EndPoint, Lazy<Task<ITransportClient>>>();
private readonly Bootstrap _bootstrap; private static readonly AttributeKey<IMessageSender> messageSenderKey = AttributeKey<IMessageSender>.ValueOf(typeof(DotNettyTransportClientFactory), nameof(IMessageSender));
private static readonly AttributeKey<IMessageListener> messageListenerKey = AttributeKey<IMessageListener>.ValueOf(typeof(DotNettyTransportClientFactory), nameof(IMessageListener));
private static readonly AttributeKey<EndPoint> origEndPointKey = AttributeKey<EndPoint>.ValueOf(typeof(DotNettyTransportClientFactory), nameof(EndPoint)); #endregion Field #region Constructor public DotNettyTransportClientFactory(ITransportMessageCodecFactory codecFactory, ILogger<DotNettyTransportClientFactory> logger)
: this(codecFactory, logger, null)
{
} public DotNettyTransportClientFactory(ITransportMessageCodecFactory codecFactory, ILogger<DotNettyTransportClientFactory> logger, IServiceExecutor serviceExecutor)
{
_transportMessageEncoder = codecFactory.GetEncoder();
_transportMessageDecoder = codecFactory.GetDecoder();
_logger = logger;
_serviceExecutor = serviceExecutor;
_bootstrap = GetBootstrap();
_bootstrap.Handler(new ActionChannelInitializer<ISocketChannel>(c =>
{
var pipeline = c.Pipeline;
pipeline.AddLast(new LengthFieldPrepender());
pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, , , , ));
pipeline.AddLast(new TransportMessageChannelHandlerAdapter(_transportMessageDecoder));
pipeline.AddLast(new DefaultChannelHandler(this));
}));
} #endregion Constructor #region Implementation of ITransportClientFactory /// <summary>
/// 创建客户端。
/// </summary>
/// <param name="endPoint">终结点。</param>
/// <returns>传输客户端实例。</returns>
public async Task<ITransportClient> CreateClientAsync(EndPoint endPoint)
{
var key = endPoint;
if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug($"准备为服务端地址:{key}创建客户端。");
try
{
return await _clients.GetOrAdd(key
, k => new Lazy<Task<ITransportClient>>(async () =>
{
//客户端对象
var bootstrap = _bootstrap;
//异步连接返回channel
var channel = await bootstrap.ConnectAsync(k);
var messageListener = new DotNettyClientMessageListener();
//设置监听
channel.GetAttribute(messageListenerKey).Set(messageListener);
//实例化发送者
var messageSender = new DotNettyMessageClientSender(_transportMessageEncoder, channel);
//设置channel属性
channel.GetAttribute(messageSenderKey).Set(messageSender);
channel.GetAttribute(origEndPointKey).Set(k);
//创建客户端
var client = new DotNettyTransportClient(messageSender, messageListener, _logger, _serviceExecutor);
return client;
}
)).Value;//返回实例
}
catch
{
throw;
}
} #endregion Implementation of ITransportClientFactory #region Implementation of IDisposable /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
foreach (var client in _clients.Values.Where(i => i.IsValueCreated))
{
(client.Value as IDisposable)?.Dispose();
}
} #endregion Implementation of IDisposable private static Bootstrap GetBootstrap()
{
IEventLoopGroup group; var bootstrap = new Bootstrap(); group = new MultithreadEventLoopGroup();
bootstrap.Channel<TcpServerSocketChannel>(); bootstrap
.Channel<TcpSocketChannel>()
.Option(ChannelOption.TcpNodelay, true)
.Option(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
.Group(group); return bootstrap;
} protected class DefaultChannelHandler : ChannelHandlerAdapter
{
private readonly DotNettyTransportClientFactory _factory; public DefaultChannelHandler(DotNettyTransportClientFactory factory)
{
this._factory = factory;
} #region Overrides of ChannelHandlerAdapter public override void ChannelInactive(IChannelHandlerContext context)
{
_factory._clients.TryRemove(context.Channel.GetAttribute(origEndPointKey).Get(), out var value);
} public override void ChannelRead(IChannelHandlerContext context, object message)
{
var transportMessage = message as TransportMessage; var messageListener = context.Channel.GetAttribute(messageListenerKey).Get();
var messageSender = context.Channel.GetAttribute(messageSenderKey).Get();
messageListener.OnReceived(messageSender, transportMessage);
} #endregion Overrides of ChannelHandlerAdapter
}
}
/// <summary>
/// 一个默认的传输客户端实现。
/// </summary>
public class DotNettyTransportClient : ITransportClient, IDisposable
{
#region Field private readonly IMessageSender _messageSender;
private readonly IMessageListener _messageListener;
private readonly ILogger _logger;
private readonly IServiceExecutor _serviceExecutor; #endregion Field #region Constructor public DotNettyTransportClient(IMessageSender messageSender, IMessageListener messageListener, ILogger logger, IServiceExecutor serviceExecutor)
{
_messageSender = messageSender;
_messageListener = messageListener;
_logger = logger;
_serviceExecutor = serviceExecutor;
messageListener.Received += MessageListener_Received;
} #endregion Constructor #region Implementation of ITransportClient /// <summary>
/// 发送消息。
/// </summary>
/// <param name="message">远程调用消息模型。</param>
/// <returns>远程调用消息的传输消息。</returns>
public async Task SendAsync(TransportMessage transportMessage)
{
try
{
if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug("准备发送消息。"); try
{
//发送
await _messageSender.SendAndFlushAsync(transportMessage);
}
catch (Exception exception)
{
throw new Exception("与服务端通讯时发生了异常。", exception);
} if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug("消息发送成功。"); }
catch (Exception exception)
{
if (_logger.IsEnabled(LogLevel.Error))
_logger.LogError(null,exception, "消息发送失败。");
throw;
}
} #endregion Implementation of ITransportClient #region Implementation of IDisposable /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
(_messageSender as IDisposable)?.Dispose();
(_messageListener as IDisposable)?.Dispose();
} #endregion Implementation of IDisposable #region Private Method private async Task MessageListener_Received(IMessageSender sender, TransportMessage message)
{
if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("服务消费者接收到消息。"); if (_serviceExecutor != null)
await _serviceExecutor.ExecuteAsync(sender, message);
} #endregion Private Method
}
5. 序列化工具(白色框)
上节中我们用MessagePack实现了序列化与反序列化,本节为通信,自然离不开消息序列化。
需要继承自 DotNetty.Transport.Channels.ChannelHandlerAdapter,才能被 netty 调用:
public class TransportMessageChannelHandlerAdapter : ChannelHandlerAdapter
{
private readonly ITransportMessageDecoder _transportMessageDecoder; public TransportMessageChannelHandlerAdapter(ITransportMessageDecoder transportMessageDecoder)
{
_transportMessageDecoder = transportMessageDecoder;
} #region Overrides of ChannelHandlerAdapter public override void ChannelRead(IChannelHandlerContext context, object message)
{
var buffer = (IByteBuffer)message;
var data = new byte[buffer.ReadableBytes];
buffer.ReadBytes(data);
var transportMessage = _transportMessageDecoder.Decode(data);
context.FireChannelRead(transportMessage);
ReferenceCountUtil.Release(buffer);
} #endregion Overrides of ChannelHandlerAdapter
}
6. 通信主机(黄色框)
用于启动通信监听端口
内部包含消息监听器(_serverMessageListener)和消息执行器(_serverMessageListener)。
接口:
public interface ITransportHost : IDisposable
{
/// <summary>
/// 启动主机。
/// </summary>
/// <param name="endPoint">主机终结点。</param>
/// <returns>一个任务。</returns>
Task StartAsync(EndPoint endPoint); /// <summary>
/// 启动主机。
/// </summary>
/// <param name="endPoint">ip地址。</param>
Task StartAsync(string ip, int port);
}
实现类:
public class DotNettyTransportHost : ITransportHost
{
#region Field private IServiceExecutor _serviceExecutor;
public IServiceExecutor ServiceExecutor { get => _serviceExecutor; }
private readonly Func<EndPoint, Task<IMessageListener>> _messageListenerFactory;
private IMessageListener _serverMessageListener; #endregion Field public DotNettyTransportHost(Func<EndPoint, Task<IMessageListener>> messageListenerFactory, IServiceExecutor serviceExecutor)
{
_messageListenerFactory = messageListenerFactory;
_serviceExecutor = serviceExecutor;
} /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
(_serverMessageListener as IDisposable)?.Dispose();
} /// <summary>
/// 启动主机。
/// </summary>
/// <param name="endPoint">主机终结点。</param>
/// <returns>一个任务。</returns>
public async Task StartAsync(EndPoint endPoint)
{
if (_serverMessageListener != null)
return;
_serverMessageListener = await _messageListenerFactory(endPoint);
_serverMessageListener.Received += MessageListener_Received;
} public async Task StartAsync(string ip, int port)
{
if (_serverMessageListener != null)
return;
_serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), port));
_serverMessageListener.Received += MessageListener_Received;
//await StartAsync(new IPEndPoint(IPAddress.Parse(ip), port));
} /// <summary>
/// 监听并回调
/// </summary>
/// <param name="sender">消息发送器</param>
/// <param name="message">监听到的消息</param>
/// <returns></returns>
private async Task MessageListener_Received(IMessageSender sender, TransportMessage message)
{
await _serviceExecutor.ExecuteAsync(sender, message);
}
}
(七)分布式通信----Netty实现NIO通信的更多相关文章
- Java NIO通信框架在电信领域的实践
[http://www.codeceo.com/article/java-nio-communication.html] 华为电信软件技术架构演进 Java NIO框架在技术变迁中起到的关键作用 ...
- 为什么选择Netty作为基础通信框架?
在开始之前,我先讲一个亲身经历的故事:曾经有两个项目组同时用到了NIO编程技术,一个项目组选择自己开发NIO服务端,直接使用JDK原生的API,结果两个多月过去了,他们的NIO服务端始终无法稳定,问题 ...
- 使用netty编写IM通信界面
前驱知识 WebSocket 维基百科: WebSocket是一种在单个TCP连接上进行全双工通信的协议.WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补 ...
- 【转】跟我学Kafka之NIO通信机制
from:云栖社区 玛德,今天又被人打脸了,小看人,艹,确实,相对比起来,在某些方面差一点,,,,该好好捋捋了,强化下短板,规划下日程,,,引以为耻,铭记于心. 跟我学Kafka之NIO通信机制 ...
- Hadoop源码解析之 rpc通信 client到server通信
rpc是Hadoop分布式底层通信的基础,无论是client和namenode,namenode和datanode,以及yarn新框架之间的通信模式等等都是采用的rpc方式. 下面我们来概要分析一下H ...
- 漫谈Java IO之 Netty与NIO服务器
前面介绍了基本的网络模型以及IO与NIO,那么有了NIO来开发非阻塞服务器,大家就满足了吗?有了技术支持,就回去追求效率,因此就产生了很多NIO的框架对NIO进行封装--这就是大名鼎鼎的Netty. ...
- 2.Netty 与 NIO 之前世今生
2.Netty 与 NIO 之前世今生 本文围绕一下几点阐述: 1. NIO 的核心组件 Buffer.Selector.Channel. 2.何谓多路复用? 3.Netty 支持的功能与特性. ...
- Netty(二)Netty 与 NIO 之前世今生
2.1 Java NIO 三件套 在 NIO 中有几个核心对象需要掌握:缓冲区(Buffer).选择器(Selector).通道(Channel). 2.1.1 缓冲区 Buffer 1.Buffer ...
- JAVA基础知识之网络编程——-TCP/IP协议,socket通信,服务器客户端通信demo
OSI模型分层 OSI模型是指国际标准化组织(ISO)提出的开放系统互连参考模型(Open System Interconnection Reference Model,OSI/RM),它将网络分为七 ...
随机推荐
- TP 5.0 架构 简介
TP 5.0 架构 简介 thinkphp 5.0 为API开发而设计的的高性能框架,是与以往thinkphp5.0以下版本大不相同的新型框架,病对以颠覆和重构版本,采用全新的架构思想,引入了更多的P ...
- python课堂整理2
一.字节和编码 1个字节是8位二进制 utf-8 表示一个中文要用3个字节 gbk 为中国汉字发明,2个字节可表示汉字 所以 utf-8 可以读gbk编码,而gbk去读utf-8 的内容会乱码 uni ...
- python中的内存机制
首先要明白对象和引用的概念 (例子:a=1, a为引用,1为对象,对象1的引用计数器为1,b=1此时内存中只有一个对象1,a,b都为引用,对象的引用计数器此时为2,因为有两个引用) a=1,b=1 i ...
- IO-文件输出流
一.输出流的原理 Java向文件中写数据的原理 Java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用写数据的方法-->把数据写入到文件中 tips: ...
- MyBatis映射,抛出Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'oid' in 'class java.lang.String'
原因在于: 在MyBatis中使用动态语句的判断时,传入的参数(parameterType)为Java基本数据类型,获取的结果(resultType)为JavaBean对象,此时就会抛出该异常,此时可 ...
- 菜单(menu)
菜单 menu ——菜单默认隐藏 ——实现菜单的接口: Menu,父接口,用于创建主菜单 SubMenu继承Menu接口,用于创建子菜单 ContextMenu接口继承Menu接口,用于创建上下文菜单 ...
- python编码问题——解决python3 UnicodeEncodeError: 'gbk' codec can't encode character '\xXX' in position XX
python实现爬虫遇到编码问题: error:UnicodeEncodeError: 'gbk' codec can't encode character '\xXX' in position XX ...
- 使用vsftp与shell实现对进程与服务状态的监控
先说一下需求吧,公司开发了一款新的产品,新产品嘛,有着不得不出问题的理由,四个云机房,总共三百余台机器,需要实时的监控进程状态,虽然有zabbix来实现,但领导需求是脚本和zabbix一起做,zabb ...
- redis分布式锁&队列应用
分布式锁 setnx(set if not exists) 如果设值成功则证明上锁成功,然后再调用del指令释放. // 这里的冒号:就是一个普通的字符,没特别含义,它可以是任意其它字符,不要误解 & ...
- log4net服务启动后没有记录日志
有时候在用log4net的时候,调试或执行是ok的,但是安装服务后没有记录日志. 这是因为服务启动是在C盘启动,而程序放的位置在别的目录. 这时候需要指定读取配置文件的位置为程序所在的目录 strin ...