Cet article vous apporte qu'est-ce que Netty ? L'analyse approfondie des connaissances liées à Netty a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'elle vous sera utile.
Qu'est-ce que Netty exactement
Commencez avec HTTP
Avec Netty, vous pouvez implémenter votre propre serveur HTTP, serveur FTP, serveur UDP, serveur RPC , serveur WebSocket, serveur proxy Redis, serveur proxy MySQL, etc.
Revoyons les principes des serveurs HTTP traditionnels
1 Créez un ServerSocket, écoutez et liez un port
2. Une série de clients demandent ce port
<.>3. Le serveur utilise Accept pour obtenir un objet de connexion Socket du client4 Démarrez un nouveau thread pour traiter la connexion4.1. Lisez le Socket et obtenez le flux d'octets. 🎜 >4.2. Décoder le protocole et obtenir l'objet de requête Http
4.3. Traiter la requête Http et obtenir un résultat et l'encapsuler dans un objet HttpResponse
4.4. et sérialisez le résultat en mots. Throttle write Socket et envoyez le flux d'octets au client
5 Continuez à parcourir l'étape 3
La raison pour laquelle le serveur HTTP est appelé serveur HTTP est parce que le serveur HTTP est appelé serveur HTTP. Le protocole d'encodage et de décodage est le protocole HTTP. Si le protocole est le protocole Redis, alors il devient un serveur Redis, si le protocole est WebSocket, alors il devient un serveur WebSocket, et ainsi de suite. À l'aide de Netty, vous pouvez personnaliser le protocole d'encodage et de décodage et implémenter votre propre serveur spécifique au protocole.
Ce qui précède est un serveur HTTP traditionnel, mais dans un environnement à haute concurrence, le nombre de threads sera plus grand et la charge du système sera plus élevée, donc NIO est créé.
Ce n'est pas un concept propre à Java. Un terme représenté par NIO est appelé multiplexage IO. Il s'agit d'un appel système fourni par le système d'exploitation. Au début, le nom de cet appel du système d'exploitation était select, mais ses performances étaient faibles. Plus tard, il a progressivement évolué vers epoll sous Linux et kqueue sous Mac. Nous l'appelons généralement epoll, car personne n'utilise les ordinateurs Apple comme serveurs pour fournir des services externes. Netty est un framework basé sur la technologie Java NIO. Pourquoi l'encapsulation est-elle nécessaire ? Parce que Java NIO natif n'est pas très pratique à utiliser et présente des bogues notoires. Une fois que Netty l'a encapsulé, il fournit un mode d'utilisation et une interface faciles à utiliser, ce qui le rend beaucoup plus pratique à utiliser pour les utilisateurs.
Avant de parler de NIO, parlons de BIO (Blocking IO). Comment comprendre ce Blocking ?
Lorsque le client écoute (Écouter), Accept est bloqué. Ce n'est que lorsqu'une nouvelle connexion arrive, Accept reviendra et le thread principal peut continuer
Lors de la lecture et de l'écriture du socket, Read. est bloqué. Ce n'est que lorsque le message de requête arrive que Read peut revenir et le sous-thread peut continuer à traiter
Lors de la lecture et de l'écriture du socket, l'écriture est bloquée. Seulement lorsque le client reçoit le message peut Write return et. le sous-thread peut continuer la lecture. Une requête
Dans le mode BIO traditionnel, tous les threads sont bloqués du début à la fin. Ces threads attendent simplement, occupant les ressources du système, et ne font rien.
Alors, comment NIO parvient-il à ne pas bloquer ? Il utilise un mécanisme événementiel. Il peut utiliser un seul thread pour effectuer toute la logique d’acceptation, d’opérations de lecture et d’écriture et de traitement des demandes. S'il n'y a rien à faire, il ne bouclera pas sans fin. Il mettra le thread en veille jusqu'à ce que le prochain événement arrive, puis continuera à fonctionner. Un tel thread est appelé un thread NIO. Exprimé en pseudo code :
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() // 写消息 } } }
Modèle de thread de réacteur unique
Un thread NIO + un thread d'acceptation :
Modèle multi-threading Reactor
Modèle maître-esclave du Reactor
Multi-threading du Reactor maître-esclave : plusieurs pools de threads NIO accepteurs sont utilisés pour accepter les connexions client
Netty peut être configuré de manière flexible en fonction des trois modèles ci-dessus.
Netty est construit sur NIO, et Netty fournit un niveau d'abstraction plus élevé en plus de NIO.
Dans Netty, les connexions Accept peuvent être gérées par un pool de threads distinct, et les opérations de lecture et d'écriture sont gérées par un autre pool de threads.
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. Appelez transferTo, et les données sont copiées du fichier vers le tampon de lecture du noyau par le moteur DMA
2 Ensuite, DMA copie les données du tampon de lecture du noyau vers le tampon d'interface de la carte réseau<.>
Deux fois ci-dessus L'opération ne nécessite pas de participation du processeur, donc aucune copie n'est obtenue. Zéro copie dans Netty se reflète principalement dans trois aspects : 1 bytebufferNetty utilise principalement bytebuffer pour envoyer et recevoir des messages, et bytebuffer. est utilisé La mémoire externe (DirectMemory) lit et écrit directement Socket. Raison : si la mémoire tas traditionnelle est utilisée pour la lecture et l'écriture du socket, la JVM copiera le tampon mémoire tas dans la mémoire directe, puis l'écrira sur le socket, créant ainsi une copie mémoire supplémentaire du tampon. DirectMemory peut être envoyé directement à l'interface de la carte réseau via DMA2 Composite BuffersByteBuffer traditionnel Si vous devez combiner les données dans deux ByteBuffers ensemble, nous devons d'abord créer une taille. = Créez un nouveau tableau de taille1+taille2, puis copiez les données des deux tableaux dans le nouveau tableau. Cependant, en utilisant le ByteBuf combiné fourni par Netty, vous pouvez éviter de telles opérations, car CompositeByteBuf ne combine pas vraiment plusieurs tampons, mais enregistre leurs références, évitant ainsi la copie des données et obtenant zéro copie. 3. Utilisation de FileChannel.transferTo Netty utilise la méthode transferTo de FileChannel, qui s'appuie sur le système d'exploitation pour implémenter zéro copie. Processus d'exécution interne de NettyServeur : 1. Créez une instance ServerBootStrap2. Configurez et liez le pool de threads Reactor : EventLoopGroup doit traiter le canal sur tous les sélecteurs enregistrés dans ce thread3. Configurez et liez le canal sur le serveur
4. 5. Créez un ChannelPipeline et un gestionnaire pour traiter les événements réseau qui le traversent sous la forme d'un flux. la plupart de la personnalisation des fonctions : comme la modification de l'authentification de sécurité Decode SSL
6. Lier et démarrer le port d'écoute
7. Lorsque le canal prêt est entraîné en rotation, la méthode dans le pipeline est exécuté par le thread Reactor : NioEventLoop, et enfin ChannelHandler planifié et exécuté
8 En parlant de cela, je voudrais recommander une communauté de communication et d'apprentissage Java : 586446657, où vous pouvez non seulement communiquer et discuter, mais. partagez également votre expérience d'entretien et téléchargez du matériel gratuit, notamment l'analyse du code source Spring, MyBatis et Netty, les principes de haute concurrence, de haute performance, d'architecture distribuée, de microservices et d'optimisation des performances JVM sont devenus un système de connaissances nécessaire pour les architectes. Je pense que pour les codeurs qui travaillent déjà et qui rencontrent des goulots d'étranglement techniques, il y aura ici le contenu dont vous avez besoin.
Client
Résumé
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!