(七)分布式通信----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),它将网络分为七 ...
随机推荐
- mysql中的SQL语句执行的顺序
1. from2. on3. join4. where5. group by6. with7. having8. select9. distinct10. order by11. limit 例: s ...
- 洛谷 P3811 题解
题面 利用暴力快速幂O(nlogn)会TLE掉: 所以对于求1~n的所有逆元要用递推公式: #include <bits/stdc++.h> using namespace std; ]; ...
- 【iOS】No suitable application records found
昨天提交 Apple 审核时遇到这个问题,如图: 原来是还没在 iTunes Connect 创建 APP ... 一时着急大意了…… 后来想想还真是脑子一时没反应过来……
- JDK的可视化工具系列 (四) JConsole、VisualVM
JConsole: Java监视与管理控制台 代码清单1: import java.util.*; public class JConsoleDemo { static class OOMObject ...
- WebGL简易教程(一):第一个简单示例
目录 1. 概述 2. 示例:绘制一个点 1) HelloPoint1.html 2) HelloPoint1.js (1) 准备工作 (2) 着色器 (3) 顶点着色器 (4) 片元着色器 (5) ...
- HelloDjango 系列教程:Django 的接客之道
文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 Web 服务简单的说就是处理请求,每个请求就像是一个"顾客".首先热情地把顾客迎接进来,然后满足用户的个性 ...
- webupload项目中使用
目前项目需要一个多图上传的功能,使用LayUI并也是可以实现多图上传的,但是没有图片删除功能,参考了一下网上多图上传的插件,选择了WebUpload进行功能开发. 然而不幸的是,官方的插件并不带UI界 ...
- Java泛型使用的简单介绍
目录 一. 泛型是什么 二. 使用泛型有什么好处 三. 泛型类 四. 泛型接口 五. 泛型方法 六. 限定类型变量 七. 泛型通配符 7.1 上界通配符 7.2 下界通配符 7.3 无限定通配符 八. ...
- JavaWeb——Servlet开发1
Java Servlet是运行在服务器端上的程序,Servlet是Java Servlet包中的一个接口,能够直接处理和相应客户端的请求,也可以将工作委托给应用的其他类. public interfa ...
- Python连载30-多线程之进程&线程&线程使用举例
一.多线程 1.我们的环境 (1)xubuntu 16.04(2)anaconda(3)pycharm(4)python 3.6 2.程序:一堆代码以文本的形式存入一个文档 3.进程:程序运行的一个状 ...