In diesem Artikel erfahren Sie, was Netty ist. Die ausführliche Analyse des Netty-bezogenen Wissens hat einen gewissen Referenzwert. Freunde in Not können darauf zurückgreifen.
Was genau ist Netty?
Beginnen Sie mit HTTP
Mit Netty können Sie Ihren eigenen HTTP-Server, FTP-Server, UDP-Server, RPC-Server implementieren , WebSocket-Server, Redis-Proxy-Server, MySQL-Proxy-Server usw.
Lassen Sie uns die Prinzipien herkömmlicher HTTP-Server überprüfen
1. Erstellen Sie einen ServerSocket, hören Sie zu und binden Sie einen Port
Eine Reihe von Clients fordern diesen Port an
3. Der Server verwendet Accept, um ein Socket-Verbindungsobjekt vom Client zu erhalten
Starten Sie einen neuen Thread, um die Verbindung zu verarbeiten
Lesen Sie den Socket und erhalten Sie den Byte-Stream
4.2. Dekodieren Sie das Protokoll und rufen Sie das HTTP-Anforderungsobjekt ab.
4.3. Verarbeiten Sie die HTTP-Anfrage, erhalten Sie ein Ergebnis und kapseln Sie es in ein HttpResponse-Objekt.
4.4 und serialisieren Sie das Ergebnis in Wörter. Drosseln Sie das Schreiben des Sockets und senden Sie den Byte-Stream an den Client
5. Fahren Sie mit Schritt 3 fort
Der Grund, warum der HTTP-Server als HTTP-Server bezeichnet wird, liegt darin, dass Das Codierungs- und Decodierungsprotokoll ist das HTTP-Protokoll. Wenn das Protokoll das Redis-Protokoll ist, wird es zu einem Redis-Server, wenn das Protokoll WebSocket ist, wird es zu einem WebSocket-Server und so weiter. Mit Netty können Sie das Kodierungs- und Dekodierungsprotokoll anpassen und Ihren eigenen protokollspezifischen Server implementieren.
Das Obige ist ein herkömmlicher HTTP-Server, aber in einer Umgebung mit hoher Parallelität ist die Anzahl der Threads größer und die Systemlast höher, sodass NIO erstellt wird.
Es handelt sich nicht um ein für Java einzigartiges Konzept. Ein von NIO repräsentierter Begriff heißt IO-Multiplexing. Es handelt sich um einen vom Betriebssystem bereitgestellten Systemaufruf. Der Name dieses Betriebssystemaufrufs war zunächst „select“, die Leistung war jedoch gering. Später entwickelte er sich unter Linux zu „epoll“ und unter Mac zu „kqueue“. Wir nennen es im Allgemeinen Epoll, da niemand Apple-Computer als Server für die Bereitstellung externer Dienste verwendet. Netty ist ein Framework, das auf der Java NIO-Technologie basiert. Warum Kapselung? Da natives Java NIO nicht so bequem zu verwenden ist und berüchtigte Fehler aufweist, bietet es einen einfach zu bedienenden Nutzungsmodus und eine Benutzeroberfläche, die die Verwendung für Benutzer erheblich erleichtert.
Bevor wir über NIO sprechen, sprechen wir über BIO (Blocking IO). Wie ist dieses Blockieren zu verstehen?
Wenn der Client zuhört (Listen), wird Accept blockiert. Nur wenn eine neue Verbindung zustande kommt, kehrt Accept zurück und der Hauptthread kann fortgesetzt werden.
Beim Lesen und Schreiben des Sockets Nur wenn die Anforderungsnachricht eintrifft, kann Read zurückgegeben werden und der Sub-Thread kann mit der Verarbeitung fortfahren.
Beim Lesen und Schreiben des Sockets kann Write zurückgegeben werden Der Sub-Thread kann mit dem Lesen fortfahren
Im herkömmlichen BIO-Modus sind alle Threads vom Anfang bis zum Ende blockiert, belegen Systemressourcen und tun nichts.
Wie erreicht NIO also die Nichtblockierung? Es verwendet einen Ereignismechanismus. Es kann einen Thread verwenden, um die gesamte Logik des Akzeptierens, der Lese- und Schreibvorgänge sowie der Anforderungsverarbeitung auszuführen. Wenn nichts zu tun ist, wird der Thread nicht endlos wiederholt, bis das nächste Ereignis eintritt. Ein solcher Thread wird als NIO-Thread bezeichnet. Ausgedrückt im Pseudocode:
while true { events = takeEvents(fds) // 获取事件,如果没有事件,线程就休眠 for event in events { if event.isAcceptable { doAccept() // 新链接来了 } elif event.isReadable { request = doRead() // 读消息 if request.isComplete() { doProcess() } } elif event.isWriteable { doWrite() // 写消息 } } }
Reactor-Single-Thread-Modell
Ein NIO-Thread + ein Akzeptanz-Thread:
Reaktor-Multithreading-Modell
Reactor-Master-Slave-Modell
Master-Slave-Reactor-Multithreading: Mehrere Akzeptor-NIO-Thread-Pools werden verwendet, um Client-Verbindungen zu akzeptieren
Netty kann auf Basis der oben genannten drei Modelle flexibel konfiguriert werden.
Netty basiert auf NIO und Netty bietet zusätzlich zu NIO eine höhere Abstraktionsebene.
In Netty können Accept-Verbindungen von einem separaten Thread-Pool verarbeitet werden, und Lese- und Schreibvorgänge werden von einem anderen Thread-Pool verarbeitet.
Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的高性能并发模型。
如果不用netty,使用原生JDK的话,有如下问题:
1、API复杂
2、对多线程很熟悉:因为NIO涉及到Reactor模式
3、高可用的话:需要出路断连重连、半包读写、失败缓存等问题
4、JDK NIO的bug
而Netty来说,他的api简单、性能高而且社区活跃(dubbo、rocketmq等都使用了它)
现象
先看如下代码,这个代码是使用netty在client端重复写100次数据给server端,ByteBuf是netty的一个字节容器,里面存放是的需要发送的数据
public class FirstClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { for (int i = 0; i < 1000; i++) { ByteBuf buffer = getByteBuf(ctx); ctx.channel().writeAndFlush(buffer); } } private ByteBuf getByteBuf(ChannelHandlerContext ctx) { byte[] bytes = "需要更多资料加群:586446657".getBytes(Charset.forName("utf-8")); ByteBuf buffer = ctx.alloc().buffer(); buffer.writeBytes(bytes); return buffer; } }
从client端读取到的数据为:
从服务端的控制台输出可以看出,存在三种类型的输出
一种是正常的字符串输出。
一种是多个字符串“粘”在了一起,我们定义这种 ByteBuf 为粘包。
一种是一个字符串被“拆”开,形成一个破碎的包,我们定义这种 ByteBuf 为半包。
应用层面使用了Netty,但是对于操作系统来说,只认TCP协议,尽管我们的应用层是按照 ByteBuf 为 单位来发送数据,server按照Bytebuf读取,但是到了底层操作系统仍然是按照字节流发送数据,因此,数据到了服务端,也是按照字节流的方式读入,然后到了 Netty 应用层面,重新拼装成 ByteBuf,而这里的 ByteBuf 与客户端按顺序发送的 ByteBuf 可能是不对等的。因此,我们需要在客户端根据自定义协议来组装我们应用层的数据包,然后在服务端根据我们的应用层的协议来组装数据包,这个过程通常在服务端称为拆包,而在客户端称为粘包。
拆包和粘包是相对的,一端粘了包,另外一端就需要将粘过的包拆开,发送端将三个数据包粘成两个 TCP 数据包发送到接收端,接收端就需要根据应用协议将两个数据包重新组装成三个数据包。
在没有 Netty 的情况下,用户如果自己需要拆包,基本原理就是不断从 TCP 缓冲区中读取数据,每次读取完都需要判断是否是一个完整的数据包 如果当前读取的数据不足以拼接成一个完整的业务数据包,那就保留该数据,继续从 TCP 缓冲区中读取,直到得到一个完整的数据包。 如果当前读到的数据加上已经读取的数据足够拼接成一个数据包,那就将已经读取的数据拼接上本次读取的数据,构成一个完整的业务数据包传递到业务逻辑,多余的数据仍然保留,以便和下次读到的数据尝试拼接。
而在Netty中,已经造好了许多类型的拆包器,我们直接用就好:
选好拆包器后,在代码中client段和server端将拆包器加入到chanelPipeline之中就好了:
如上实例中:
客户端:
ch.pipeline().addLast(new FixedLengthFrameDecoder(31));
服务端:
ch.pipeline().addLast(new FixedLengthFrameDecoder(31));
是在发送数据的时候,传统的实现方式是:
1. `File.read(bytes)`
2. `Socket.send(bytes)`
这种方式需要四次数据拷贝和四次上下文切换:
1. 数据从磁盘读取到内核的read buffer
2. 数据从内核缓冲区拷贝到用户缓冲区
3. 数据从用户缓冲区拷贝到内核的socket buffer
4. 数据从内核的socket buffer拷贝到网卡接口(硬件)的缓冲区
明显上面的第二步和第三步是没有必要的,通过java的FileChannel.transferTo方法,可以避免上面两次多余的拷贝(当然这需要底层操作系统支持)
1. Rufen Sie transferTo auf, die Daten werden von der DMA-Engine aus der Datei in den Kernel-Lesepuffer kopiert
2. Dann kopiert DMA die Daten aus dem Kernel-Lesepuffer in den Netzwerkkarten-Schnittstellenpuffer
Zweimal oben Der Vorgang erfordert keine CPU-Beteiligung, sodass keine Kopie erreicht wird.
spiegelt sich hauptsächlich in drei Aspekten wider:
1. Bytebuffer
Netty verwendet hauptsächlich Bytebuffer zum Senden und Empfangen von Nachrichten und Bytebuffer Wird verwendet Externer Speicher (DirectMemory) liest und schreibt direkt Socket.
Grund: Wenn herkömmlicher Heap-Speicher zum Lesen und Schreiben von Sockets verwendet wird, kopiert die JVM den Heap-Speicherpuffer in den Direktspeicher und schreibt ihn dann in den Socket, wodurch eine zusätzliche Speicherkopie des Puffers erstellt wird. DirectMemory kann über DMA direkt an die Netzwerkkartenschnittstelle gesendet werden
2. Zusammengesetzte Puffer
Traditioneller ByteBuffer Wenn Sie die Daten in zwei ByteBuffer zusammenfassen müssen, müssen wir zunächst eine Größe erstellen = Erstellen Sie ein neues Array mit Größe1+Größe2 und kopieren Sie dann die Daten in den beiden Arrays in das neue Array. Mit dem von Netty bereitgestellten kombinierten ByteBuf können Sie solche Vorgänge jedoch vermeiden, da CompositeByteBuf nicht wirklich mehrere Puffer kombiniert, sondern deren Referenzen speichert, wodurch das Kopieren von Daten vermieden und eine Nullkopie erreicht wird.
3. Verwendung von FileChannel.transferTo
Netty verwendet die transferTo-Methode von FileChannel, die darauf angewiesen ist, dass das Betriebssystem eine Nullkopie implementiert.
Server:
1. Erstellen Sie eine ServerBootStrap-Instanz
2. Richten Sie den Reactor-Thread-Pool ein: EventLoopGroup soll den Kanal auf allen in diesem Thread registrierten Selektoren verarbeiten >
3. Richten Sie den Kanal auf dem Server ein und binden Sie ihn. 5. Erstellen Sie eine ChannelPipeline und einen Handler, um Netzwerkereignisse in Form eines Streams zu verarbeiten die meisten Funktionsanpassungen: z. B. Bearbeiten, Dekodieren der SSL-Sicherheitsauthentifizierung 6. Binden und Starten des Überwachungsports 7. Wenn der Bereitschaftskanal in Rotation trainiert wird, ist die Methode in der Pipeline vom Reactor-Thread ausgeführt: NioEventLoop, und schließlich geplant und ausgeführt vom ChannelHandler 8 Apropos: Ich möchte eine Java-Kommunikations- und Lern-Community empfehlen: 586446657, in der Sie nicht nur kommunizieren und diskutieren können, sondern auch Teilen Sie auch Interviewerfahrungen und laden Sie kostenlose Materialien herunter, einschließlich Spring-, MyBatis- und Netty-Quellcode-Analyse. Die Prinzipien der hohen Parallelität, der hohen Leistung, der verteilten Microservice-Architektur und der JVM-Leistungsoptimierung sind zu einem notwendigen Wissenssystem für Architekten geworden. Ich glaube, dass es für Programmierer, die bereits arbeiten und auf technische Engpässe stoßen, hier Inhalte gibt, die sie brauchen. KundeZusammenfassung
Das war's Ich habe einiges an Wissen zu Netty zusammengestellt. Wenn Sie anderer Meinung sind, können Sie dies gerne diskutieren!Das obige ist der detaillierte Inhalt vonWas ist Netty? Eingehende Analyse des Netty-bezogenen Wissens. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!