Блог jsbintask, Пожалуйста, укажите источник.
Введение в нетти
Nowadays we use general purpose applications or libraries to communicate with each other. For example, we often use an HTTP client library to retrieve information from a web server and to invoke a remote procedure call via web services. However, a general purpose protocol or its implementation sometimes does not scale very well. It is like how we don't use a general purpose HTTP server to exchange huge files, e-mail messages, and near-realtime messages such as financial information and multiplayer game data. What's required is a highly optimized protocol implementation that is dedicated to a special purpose. For example, you might want to implement an HTTP server that is optimized for AJAX-based chat application, media streaming, or large file transfer. You could even want to design and implement a whole new protocol that is precisely tailored to your need. Another inevitable case is when you have to deal with a legacy proprietary protocol to ensure the interoperability with an old system. What matters in this case is how quickly we can implement that protocol while not sacrificing the stability and performance of the resulting application.
Это официальное введение netty, что, вероятно, означает: Мы часто хотим, чтобы наши приложения могли взаимодействовать с другими приложениями. Например, мы часто используем http-запросы для запроса информации или используем rpc для вызова веб-сервисов, но для этого конкретного протокола (http, ftp и т. Расширьте собственное приложение. Допустим, мы не собираемся использовать протокол httpПередача больших файлов, почты, обмена мгновенными сообщениями (финансовая информация), что требует большой оптимизации существующего протокола! Таким образом, мы можем использовать netty для настройки вашего собственного протокола!
Зачем изучать нетти?
взять здесьЗнай почтиПредыдущий ответ:
Как студент Java, если вы не изучали Netty, то ваше использование и понимание языка Java находится только на поверхностном уровне.Вы можете нажать SSH, написать несколько MVC, получить доступ к базам данных и кешам, это только элементарные вещи. Java-программисты делают. Если вы хотите продвинуться вперед и хотите понять глубокие и продвинутые знания Java-сервера, Netty определенно является порогом, который необходимо пройти. С Netty вы можете реализовать свой собственный HTTP-сервер, FTP-сервер, UDP-сервер, RPC-сервер, сервер WebSocket, прокси-сервер для Redis, прокси-сервер для MySQL и многое другое. Если вы хотите знать, как написан Nginx, если вы хотите знать, как реализованы Tomcat, Jetty и Dubbo, если вы также хотите реализовать простой сервер Redis, вы должны хорошо понимать Netty, их принципы высокой производительности — все. аналогичный.
while ture
events = takeEvents(fds) // 获取事件,如果没有事件,线程就休眠
for event in events {
if event.isAcceptable {
doAccept() // 新链接来了
} elif event.isReadable {
request = doRead() // 读消息
if request.isComplete() {
doProcess()
}
} elif event.isWriteable {
doWrite() // 写消息
}
}
}
Процесс NIO — это примерно процесс, описанный псевдокодом выше, который сильно отличается от реального кода, но для новичков этого понимания достаточно. Netty основана на NIO, а Netty обеспечивает более высокий уровень абстракции поверх NIO. В Netty соединения Accept могут обрабатываться отдельным пулом потоков, а операции чтения и записи обрабатываются другим пулом потоков. Прием соединений и операции чтения и записи также могут обрабатываться с использованием одного и того же пула потоков. Логика обработки запросов может обрабатываться с использованием отдельного пула потоков или вместе с потоками чтения и записи. Каждый поток в пуле потоков является потоком NIO. Пользователи могут собраться в соответствии с реальной ситуацией и построить модель параллелизма, отвечающую системным требованиям. Netty предоставляет встроенные общие кодеки, включая линейный кодек [один запрос на строку], кодек длины префикса [первые N байтов определяют запрашиваемую длину байта], воспроизводимый декодер [запись полупакетных сообщений] Статус], кодек HTTP, кодек сообщений WebSocket и т. д. Netty предоставляет ряд интерфейсов обратного вызова жизненного цикла, когда поступает полный запрос, когда соединение закрывается, когда соединение устанавливается, пользователь получает событие обратного вызова, а затем выполняет логическую обработку. Netty может управлять несколькими портами одновременно и может использовать клиентскую модель NIO, необходимую для служб RPC. Помимо обработки сокетов TCP, Netty также может обрабатывать сокеты UDP. В процессе чтения и записи сообщений необходимо использовать большое количество ByteBuffers, Netty оптимизирует и абстрагирует ByteBuffers с точки зрения производительности и удобства использования. Короче говоря, Netty — это необходимая магия для продвинутых Java-программистов. Если вы это знаете и хотите знать почему, обязательно изучите Нетти. Если вы думаете, что Java — это скучно, Netty — это ключ к возобновлению вашего интереса к Java.
Резюме: Оружие для продвинутых программистов!
упражняться
примечание: в этом примере, помимо очень важных основных классов, другие классы не будут слишком подробно объясняться.Эта глава является только введением, а другие главы будут посвящены объяснению!Мы уже знаем роль netty (гибкая оптимизация и настройка собственного протокола) и почему вам следует изучить netty. Затем мы шаг за шагом настроим наше собственное соглашение и, наконец, завершим чат!
распечатать протокол
Поскольку мы назвали протокол печати, в этом смысл печати: сервер принимает информацию от службы поддержки клиентов и распечатывает ее! Сначала мы пишемChannelInboundHandlerAdapter, используемый для обработки полученного сообщения, мы сначала анализируем роль этого класса, отношения наследования следующие:
Его функция просто резюмируется как: процессор для обработки событий канала сокета, поэтому мы, естественно, используем его для обработки сообщений в этом примере, поэтому у нас есть следующие классы:PrintServerHandler:public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println(byteBuf.toString(Charset.forName("utf-8")));
ctx.writeAndFlush(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
Получив сообщение, распечатайте его, а затем продолжайте писать класс запуска для запуска службы, которая запускает наш собственный протокол,PrintServerApp:
public class EchoServerApp {
private int port;
public EchoServerApp(int port) {
this.port = port;
}
public void run() throws Exception {
NioEventLoopGroup bossLoopGroup = new NioEventLoopGroup();
NioEventLoopGroup workLoopGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossLoopGroup, workLoopGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossLoopGroup.shutdownGracefully();
workLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoServerApp(8080).run();
}
}
запускать. Затем мы используем инструмент telnet, который поставляется с win для проверки (Панель управления -> Программы и управление -> Включение или выключение оконной функции, проверьте telnet). Откройте cmd и введите
telnet localhost 8080
Тест прошел успешно, мы завершили первую демонстрацию и внедрили собственный протокол печати. Далее мы также заменим клиента на netty.Цель: Запустить клиент и получить время сервера, называемый временным протоколом.
Time Protocol
Сначала, как указано выше, напишитеTimeServerHandler:
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf timeBuf = ctx.alloc().buffer();
timeBuf.writeBytes(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()).getBytes());
ChannelFuture channelFuture = ctx.writeAndFlush(timeBuf);
channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
assert channelFuture == future;
// ctx.close();
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
Начните так же, как и выше, далее напишите клиентTimeClientHandler:
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
ByteBuf byteBuf = (ByteBuf) msg;
int length = byteBuf.readableBytes();
byte[] buff = new byte[1024];
byteBuf.readBytes(buff, 0, length);
System.out.println("current time: " + new String(buff, 0, length));
ctx.close();
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
Запустите сервер и клиент соответственно.
Результаты теста показаны на рисунке После запуска клиента он получает время сервера, поэтому мы реализуем собственный протокол времени, а затем продолжаем расширяться и пишем чат для общения между клиентом и сервером:chatroom server
Прежде всего, мы абстрагируем объект для информации, передаваемой между клиентом и сервером,Messageи класс инструмента:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message {
private String username;
private Date sentTime;
private String msg;
}
public class Utils {
public static String encodeMsg(Message message) {
return message.getUsername() + "~" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(message.getSentTime())) + "~" + message.getMsg();
}
public static String formatDateTime(Date time) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time);
}
public static Date parseDateTime(String time) {
try {
return new SimpleDateFormat("yyyy-MM-dd Hh:mm:ss").parse(time);
} catch (ParseException e) {
return null;
}
}
public static void printMsg(Message msg) {
System.out.println("=================================================================================================");
System.out.println(" " + Utils.formatDateTime(msg.getSentTime()) + " ");
System.out.println(msg.getUsername() + ": " + msg.getMsg());
System.out.println("=================================================================================================");
}
}
Три атрибута представляют имя пользователя, время отправки и содержимое сообщения, а затем пишут обработчик для обработки входного сообщения, который используется для преобразованияbyteсообщение вMessage,ServerTransferMsgHandler:
public class ServerTransferMsgHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
String totalMsg = in.readCharSequence(in.readableBytes(), Charset.forName("utf-8")).toString();
String[] content = totalMsg.split("~");
out.add(new Message(content[0], Utils.parseDateTime(content[1]), content[2]));
}
}
Затем напишите обработчик, который обрабатывает полученное сообщение, чтобы распечатать сообщение, отправленное клиентом,ServerMsgHandler:
public class ServerMsgHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("jsbintask-client进入聊天室。");
Message message = new Message(Constants.SERVER, new Date(), "Hello, I'm jsbintask-server side.");
ByteBuf buffer = ctx.alloc().buffer();
String content = Utils.encodeMsg(message);
buffer.writeBytes(content.getBytes());
ctx.writeAndFlush(buffer);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg1) throws Exception {
try {
Message msg = (Message) msg1;
Utils.printMsg(msg);
Scanner scanner = new Scanner(System.in);
System.out.print("jsbintask-server, please input msg: ");
String reply = scanner.nextLine();
Message message = new Message(Constants.SERVER, new Date(), reply);
ctx.writeAndFlush(message);
} finally {
ReferenceCountUtil.release(msg1);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Следует отметить, что метод channelActive при подключении клиента сначала отправляет сообщение клиенту, и, наконец, при записи пользователя на серверMessageПревратиться вByteОбработчик сообщений, MessageEncoder:
public class MessageEncoder extends MessageToByteEncoder<Message> {
@Override
protected void encode(ChannelHandlerContext ctx, Message message, ByteBuf out) throws Exception {
ByteBuf buffer = ctx.alloc().buffer();
String content = Utils.encodeMsg(message);
buffer.writeBytes(content.getBytes(StandardCharsets.UTF_8));
ctx.writeAndFlush(buffer);
}
}
Наконец, напишите класс запуска на стороне сервера,ChatroomServerApp:
public class ChatroomServerApp {
public static void main(String[] args) throws Exception {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new MessageEncoder(), new ServerTransferMsgHandler(), new ServerMsgHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 1024 * 10)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
Запустите сервер и продолжайте писать ChatroomClient.
chatroom client
Как и на стороне сервера, ключом к клиенту также является обработчик ClientMsgHandler:
public class ClientMsgHandler extends SimpleChannelInboundHandler<Message> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception {
try {
Utils.printMsg(msg);
Scanner scanner = new Scanner(System.in);
System.out.print("jsbintask-client, please input msg: ");
String reply = scanner.nextLine();
Message message = new Message(Constants.CLIENT, new Date(), reply);
ByteBuf buffer = ctx.alloc().buffer();
String content = message.getUsername() + "~" + Utils.formatDateTime(message.getSentTime()) + "~" + message.getMsg();
buffer.writeBytes(content.getBytes(StandardCharsets.UTF_8));
ctx.writeAndFlush(buffer);
} finally {
ReferenceCountUtil.release(msg);
}
}
}
Тогда тоже будетbyteпреобразовать вMessageПреобразователь, ClientMsgHandler:
public class ClientTransferMsgHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
byte[] buff = new byte[2024];
int length = in.readableBytes();
in.readBytes(buff, 0, length);
String totalMsg = new String(buff, 0, length, StandardCharsets.UTF_8);
String[] content = totalMsg.split("~");
out.add(new Message(content[0], Utils.parseDateTime(content[1]), content[2]));
}
}
Наконец, запустите класс ChatroomClientApp:
public class ChatroomClientApp {
public static void main(String[] args) throws Exception {
NioEventLoopGroup workLoopGroup = new NioEventLoopGroup();
try {
Bootstrap clientBootstrap = new Bootstrap();
clientBootstrap.group(workLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ClientTransferMsgHandler(), new ClientMsgHandler());
}
})
.option(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture channelFuture = clientBootstrap.connect("localhost", 8888).sync();
channelFuture.channel().closeFuture().sync();
} finally {
workLoopGroup.shutdownGracefully();
}
}
}
Также запустите клиент и наблюдайте за консолью. Сначала сервер предлагает клиенту войти в чат, и клиент видит приветственное сообщение, отправленное сервером:
Это означает, что наша ссылка установлена, а затем клиент и сервер отправляют друг другу сообщения: Как показано на рисунке, таким образом наш чат был успешно написан.Полная демонстрация выглядит следующим образом:Суммировать
В этой главе мы открыли дверь для изучения netty, впервые представили netty, почему мы должны изучать netty, и реализовали наш собственный протокол, печать, время, протокол преобразования сообщений шаг за шагом в трех случаях и успешно вошли в дверь netty. В этой главе мы узнаем об архитектуре netty!
Исходный код:GitHub.com/ — корзина task22….
关注我,这里只有干货!