(七)分布式通信----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),它将网络分为七 ...
随机推荐
- Vue双向绑定原理及其实现
在之前面试的时候被面试官问到是否了解Vue双向绑定的原理,其实自己之前看过双向绑定的原理,但也就是粗略的了解,但是没有深入.面试官当时让我手写一个原理,但是就蒙了
- python每个文件都需要顶部注释,那今天介绍一个方法,只需要设置一次,下次新建python文件后,注释自动出现在顶部的方法
python每个文件都需要顶部注释,那今天介绍一个方法,只需要设置一次,下次新建python文件后,注释自动出现在顶部的方法 只需要在file -----settings------file and ...
- ansible-service
#service#查询服务状态 ansible server01 -m service -a "name=httpd state=started" #停止服务 ansible se ...
- Github上fork的项目如何merge原Git项目
问题场景 小明在Github上fork了一个大佬的项目,并clone到本地开发一段时间,再提交merge request到原Git项目,过了段时间,原作者联系小明,扔给他下面这幅截图并告知合并处理冲突 ...
- 进军pc市场 华为剑走偏锋可有戏?
尽管官方并未正式公布,但在前段时间,华为将要进军PC市场的消息在业内传得沸沸扬扬,据知情人士曝料,其第一款个人电脑将在今年4月上线.而华为将进军PC市场的消息,对其他智能手机厂商来说又意味着什么呢? ...
- mac下安装mongodb数据库教程
前言 由于最近在学习node+express,学习到持久化存储章节需要连接mongodb数据库,然后之前也有试过安装mongodb但是失败了,这次就找了很多资料,终于安装完成了,故此记录下来安装步骤, ...
- 利用MAVEN打包可运行jar包,包括依赖的第三方包
转载自:http://bglmmz.iteye.com/blog/2058914 背景: 另一篇文章说了如何利用IDEA来打包,现在来说说如何利用MAVEN打包 目标:应用本身打成一个jar包,依赖的 ...
- 【Java笔记】【Java核心技术卷1】chapter3 D4变量
package chapter3; public class D4变量 { public static final int BBB=100; //类常量 public static void main ...
- MQ服务器端和客户端通信浅谈
MQ服务器端和客户端通信浅谈 1. WebSphere MQ的服务端的安装和配置 (1)创建名为venus.queue.manager的默认队列管理器. 在DOS窗口命令提示符下,输入以下命令: cr ...
- 正则表达式之Matcher类中group方法
前言 同事把一个excel表给我,里面的数据大概有几千的样子吧.自己需要把里面的数据一个一个拿出来做一个http请求,对得到的结果进行过滤,然后再写到上面去.这是就涉及到用脚本来进行操作了,于是自己搞 ...