(七)分布式通信----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),它将网络分为七 ...
随机推荐
- jQuery 解析 url 参数
应用场景: 三毛:我现在拿到一个 url 地址(https://www.google.com/search?dcr=&ei=5C&q=param),我现在要获取 location.se ...
- 从零开始react实战:云书签-1 react环境搭建
总览篇:react 实战之云书签 本篇是实战系列的第一篇,主要是搭建 react 开发环境,在create-react-app的基础上加上如下功能: antd 组件库按需引入 ,支持主题定制 支持 l ...
- 并发编程之Java内存模型
在介绍Java内存模型之前,先来了解一下为什么要有内存模型,以及内存模型是什么.然后我们基于对内存模型的了解,学习Java内存模型以及并发编程的三大特性. 为什么要有内存模型 在计算机中,所有的运算操 ...
- 【Android Studio】提示代码忽略大小写
在 Preference... 中找到如下,设置 Case sensitive completion 为 None 即可,如下图: PS: 该截图是 Intellij IDEA (Android St ...
- light oj 1011 - Marriage Ceremonies (状态压缩+记忆化搜索)
题目链接 大概题意是有n个男的n个女的(原谅我这么说,我是粗人),给你一个n*n的矩阵,第i行第j列表示第i个女(男)对第j个男(女)的好感度,然后要安排n对相亲,保证都是正常的(无搞基百合之类的), ...
- 新IT运维时代 | Docker运维之最佳实践-上篇
容器技术的发展可以分为两个阶段,第一个阶段聚焦在IaaS层,仅仅把容器当做更轻量级虚拟机来使用,解决了应用运行时进程级资源隔离的问题:随着Docker的出现,容器虚拟化才有了统一的平台,由此容器技术发 ...
- Appium+python自动化(二十八)- 滑呀滑,滑到奈何桥喝碗孟婆汤 - 高级滑动(超详解)
简介 奈何桥上叹奈何,三生石前憾三生,彼岸花下非彼岸,奈何三生彼岸人. 相传过了鬼门关便上一条路叫黄泉路,路上盛开着只见花,不见叶的彼岸花.花叶生生两不见,相念相惜永相失,路尽头有一条河叫忘川河,河上 ...
- selenium操作cookies实现免密登录,自动发微博
一直想用selenium实现个小功能,比如发微博之类的,但是有的网站在登录会有验证码,没想到太好的方法解决,于是想到利用cookies来登录网站 第一步:获取一个可用的cookies,获取的cooki ...
- 手摸手,带你用vue实现后台管理权限系统及顶栏三级菜单显示
手摸手,带你用vue实现后台管理权限系统及顶栏三级菜单显示 效果演示地址 项目demo展示 重要功能总结 权限功能的实现 权限路由思路: 根据用户登录的roles信息与路由中配置的roles信息进行比 ...
- 【Java例题】4.5异常处理
5. 对于输入的数,如果出现小数,则作为异常处理,并舍去小数,显示结果:如果输入的数据类型不对也作为异常处理,显示结果0. package chapter4; import java.util.*; ...