Оригинальный адрес блога:блог Пимайка
предисловие
Эта статья в основном знакомит с примером кода клиента и сервера Netty, имеет интуитивное представление о Netty и показывает, как использовать Netty.В последующих статьях будет подробно проанализирован каждый компонент Netty.
Введение в Нетти
Netty — это асинхронная среда веб-приложений, управляемая событиями, которая поддерживает быструю разработку поддерживаемых приложений.высокая производительностьпротокол-ориентированный сервер и клиент. Netty в основном инкапсулирует пакет nio для Java.
Зачем использовать Нетти
Представленная выше, Нетти являетсяВысокопроизводительная сетевая коммуникационная среда, то почему мы должны использовать Netty, другими словами, каковы преимущества Netty, которые заставляют нас использовать его, почему бы не использовать собственное программирование Java Socket или использовать Java NIO, представленный в Java 1.4. Затем проанализируйте и проанализируйте программирование Java Socket и Java NIO.
Сетевое программирование на Java
Давайте сначала рассмотрим пример сетевого программирования на Java:
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
System.out.println("received: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
Выше показан пример простого сервера сокетов. Код может одновременно обрабатывать только одно соединение. Чтобы управлять несколькими одновременными клиентами, необходимо создать новый поток для каждого нового сокета клиента. Эта схема параллелизма приемлема для небольшого и среднего количества клиентов. Если она предназначена для высокой параллелизма, более 100 000 одновременных подключений, эта схема не рекомендуется. Она требует слишком много ресурсов потоков и может существовать в любое время. Большое количество потоки находятся в заблокированном состоянии, ожидая готовности входных или выходных данных, а производительность всего решения слишком низкая. Следовательно, в сценариях с высокой степенью параллелизма общие решения для сетевого программирования на Java не рекомендуются.
Java NIO
Давайте сначала рассмотрим пример Java NIO:
public class ServerSocketChannelDemo {
private ServerSocketChannel serverSocketChannel;
private Selector selector;
public ServerSocketChannelDemo(int port) throws IOException {
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listener() throws IOException {
while (true) {
int n = selector.select();
if (n == 0) {
continue;
}
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
if (selectionKey.isAcceptable()) {
ServerSocketChannel server_channel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = server_channel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
//如果通道处于读就绪的状态
//读操作
//TODO
}
}
}
}
}
Основные части NIO в основном включают:
- канал
- Буфер Буфер
- Селектор мультиплексора
Селектор Селектор является ключом к реализации неблокирующего ввода/вывода в Java.Зарегистрируйте канал канала в селекторе.Если канал канала отправляет событие чтения или записи, канал находится в состоянии готовности и будет опрошен Селектор для последующей операции ввода/вывода. Этот метод мультиплексирования ввода-вывода обеспечивает лучшее управление ресурсами, чем описанная выше модель блокирующего ввода-вывода:
- Многие соединения могут обрабатываться с меньшим количеством потоков, что снижает накладные расходы на управление памятью и переключение контекста.
- Потоки также можно использовать для других задач, когда не нужно обрабатывать операции ввода-вывода.
Хотя использование Java NIO позволяет нам обрабатывать множество соединений с меньшим количеством потоков,Надежная и эффективная обработка и планирование операций ввода-вывода при высокой нагрузке — утомительная и подверженная ошибкам задача., так что это привело к высокопроизводительным специалистам по сетевому программированию——Netty
Особенности сети
У Netty есть много отличных функций, которые стоит использовать (от Netty в действии):
дизайн
- Единый API для разных протоколов (блокирующих и неблокирующих)
- На основе гибкой и расширяемой событийно-управляемой модели
- Настраиваемая модель потоковой передачи
- Надежная поддержка сокет данных без подключения без подключения (UDP)
представление
- лучшая пропускная способность, низкая задержка
- меньшее потребление ресурсов
- минимальная копия памяти
прочность
- Больше не вызывает OutOfMemoryError для слишком быстрых, слишком медленных или перегруженных соединений.
- Больше нет проблемы непостоянных частот чтения и записи NIO в высокоскоростных сетевых средах.
безопасность:
- Полная поддержка SSL/TLS и STARTTLS
- Может использоваться в ограниченных средах, таких как апплеты и OSGI.
легко использовать:
- Подробный Javadoc и большой набор примеров
- Никаких зависимостей, кроме JDK 1.6+, не требуется.
Пример кода Netty
Ниже приведен пример кода сервера и клиента. Давайте сначала рассмотрим код Netty. В последующих статьях будет подробно проанализирован каждый модуль.
Код сервера
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws InterruptedException {
new EchoServer(8888).start();
}
public void start() throws InterruptedException {
final EchoServerHandler serverHandler = new EchoServerHandler();
//创建EventLoopGroup,处理事件
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(boss,worker)
//指定所使用的NIO传输 Channel
.channel(NioServerSocketChannel.class)
//使用指定的端口设置套接字地址
.localAddress(new InetSocketAddress(port))
//添加一个EchoServerHandler到子Channel的ChannelPipeline
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//EchoServerHandler标志为@Shareable,所以我们可以总是使用同样的实例
socketChannel.pipeline().addLast(serverHandler);
}
});
//异步的绑定服务器,调用sync()方法阻塞等待直到绑定完成
ChannelFuture future = b.bind().sync();
future.channel().closeFuture().sync();
} finally {
//关闭EventLoopGroup,释放所有的资源
group.shutdownGracefully().sync();
worker.shutdownGracefully().sync();
}
}
}
EchoServerHandler
@ChannelHandler.Sharable //标识一个 ChannelHandler可以被多个Channel安全地共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
//将消息记录到控制台
System.out.println("Server received: " + buffer.toString(CharsetUtil.UTF_8));
//将接受到消息回写给发送者
ctx.write(buffer);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将未消息冲刷到远程节点,并且关闭该 Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//打印异常栈跟踪
cause.printStackTrace();
//关闭该Channel
ctx.close();
}
}
Интерпретация кодовой точки:
-
ServerBootStrap
Это класс начальной загрузки, вспомогательный класс, помогающий запустить службу, и он может устанавливать параметры сокета. -
EventLoopGroup
Это пул потоков, который обрабатывает операции ввода-вывода и используется для распределения операций ввода-вывода и событий, которые обслуживают канал.EventLoop
,иNioEventLoopGroup
даEventLoopGroup
Класс реализации . Два экземпляра здесьNioEventLoopGroup
,Одинboss
, в основном используемый для обработки клиентских подключений,worker
Используется для обработки операций чтения и записи данных на стороне клиента. -
EchoServerHandler
Реализована бизнес-логика - позвонив
ServerBootStrap.bind()
метод привязки к серверу
Код клиента
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture channelFuture = b.connect().sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient("127.0.0.1", 8888).start();
}
}
EchoClientHandler
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("Client received: "+byteBuf.toString());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Интерпретация кодовой точки:
- Для инициализации клиента создается экземпляр BootStrap с
ServerBootStrap
То же самое, это также класс начальной загрузки, в основном помогающий клиенту - назначен
NioEventLoopGroup
экземпляр, внутриEventLoop
, который обрабатывает события, происходящие в течение жизненного цикла соединения. -
EchoClientHandler
Класс отвечает за обработку бизнес-логики, а серверная частьEchoSeverHandler
Эффект аналогичен.