>  기사  >  Java  >  네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

不言
不言앞으로
2018-11-24 16:22:323148검색

이 기사에서는 Netty가 무엇인지 소개합니다. 네티 관련 지식에 대한 심층 분석은 일정한 참고 가치가 있습니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

Netty란 정확히 무엇인가요

HTTP로 시작

Netty를 사용하면 자체 HTTP 서버, FTP 서버, UDP 서버, RPC 서버, WebSocket 서버, Redis 프록시 서버, MySQL 프록시 서버 등을 구현할 수 있습니다.

기존 HTTP 서버의 원리를 살펴보겠습니다

1. ServerSocket을 생성하고 포트를 수신하고 바인딩합니다

2. 일련의 클라이언트가 이 포트를 요청합니다

3. 서버는 클라이언트로부터 요청을 받기 위해 Accept를 사용합니다. 소켓 연결 개체

4. 새 스레드를 시작하여 연결을 처리합니다

4.1. 소켓을 읽고 바이트 스트림을 가져옵니다. 4.2 프로토콜을 디코딩하고 Http 요청 개체를 가져옵니다.4.3. 결과를 HttpResponse 개체로 캡슐화합니다.

4.4. 인코딩 프로토콜, 결과 직렬화된 바이트 스트림을 소켓에 쓰고 바이트 스트림을 클라이언트에 보냅니다.

5. HTTP 서버가 반복되는 이유 HTTP 서버라고 불리는 이유는 인코딩과 디코딩 때문에 프로토콜이 HTTP 프로토콜이고, 프로토콜이 Redis 프로토콜이면 Redis 서버가 되고, 프로토콜이 WebSocket이면 WebSocket 서버가 되는 식입니다. Netty를 사용하면 인코딩 및 디코딩 프로토콜을 사용자 정의하고 자체 프로토콜별 서버를 구현할 수 있습니다.

NIO

위는 전통적인 HTTP 서버이지만 동시성이 높은 환경에서는 스레드 수가 많아지고 시스템 부하도 높아지므로 NIO가 생성됩니다.

Java만의 고유한 개념은 아닙니다. NIO로 대표되는 용어를 IO 다중화라고 합니다. 초기에는 운영체제에서 제공하는 시스템 콜로, 초기에는 이 운영체제 호출의 이름을 정했으나 성능이 낮았으나, 이후 리눅스에서는 epoll, 맥에서는 kqueue로 진화했다. 외부 서비스를 제공하기 위해 Apple 컴퓨터를 서버로 사용하는 사람이 없기 때문에 일반적으로 이를 epoll이라고 부릅니다. Netty는 Java NIO 기술을 기반으로 한 프레임워크입니다. 캡슐화가 필요한 이유 네이티브 Java NIO는 사용하기가 쉽지 않고 버그가 악명 높기 때문에 Netty를 캡슐화한 후에는 작동하기 쉬운 사용 모드와 인터페이스를 제공하므로 사용자가 훨씬 더 편리하게 사용할 수 있습니다.

NIO에 대해 이야기하기 전에 BIO(Blocking IO)에 대해 먼저 알아보겠습니다.

클라이언트가 수신(Listen)되면 Accept가 차단됩니다. 새로운 연결이 올 때만 Accept가 반환되고 메인 스레드가 계속될 수 있습니다.

소켓을 읽고 쓸 때 요청 메시지가 차단될 때만 Read가 차단됩니다. 반환 후 하위 스레드는 계속 처리할 수 있습니다

소켓을 읽고 쓸 때 쓰기는 차단됩니다. 클라이언트가 메시지를 수신한 경우에만 쓰기 반환이 가능하고 하위 스레드는 다음 요청을 계속해서 읽을 수 있습니다.

기존 BIO 모드에서는 처음부터 끝까지 모든 스레드가 차단됩니다. 이러한 스레드는 대기만 하며 시스템 리소스를 점유하고 아무것도 하지 않습니다.

그럼 NIO는 어떻게 논블로킹을 달성하나요? 이벤트 메커니즘을 사용합니다. 하나의 스레드를 사용하여 승인, 읽기 및 쓰기 작업, 요청 처리의 모든 논리를 수행할 수 있습니다. 할 일이 없으면 끝없이 반복되지 않습니다. 다음 이벤트가 올 때까지 스레드를 대기한 다음 계속 작업합니다. 이러한 스레드를 NIO 스레드라고 합니다. 의사 코드로 표현:

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 스레드 모델

Reactor 단일 스레드 모델

1개의 NIO 스레드 + 1개의 Accept 스레드:

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석Reactor 멀티 스레드 모델

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석Reactor 마스터-슬레이브 모델

Master-slave Reactor 멀티 스레딩: 여러 수락자의 NIO 스레드 풀을 사용하여 클라이언트 연결을 허용합니다.

Netty는 위의 세 가지를 기반으로 유연하게 구성할 수 있습니다. 모델. 네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

요약

Netty는 NIO를 기반으로 구축되었으며 Netty는 NIO 위에 더 높은 수준의 추상화를 제공합니다.

Netty에서는 연결 수락은 별도의 스레드 풀에서 처리할 수 있으며, 읽기 및 쓰기 작업은 다른 스레드 풀에서 처리됩니다.

Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的高性能并发模型。

为什么选择Netty

如果不用netty,使用原生JDK的话,有如下问题:

1、API复杂

2、对多线程很熟悉:因为NIO涉及到Reactor模式

3、高可用的话:需要出路断连重连、半包读写、失败缓存等问题

4、JDK NIO的bug

而Netty来说,他的api简单、性能高而且社区活跃(dubbo、rocketmq等都使用了它)

什么是TCP 粘包/拆包

现象

先看如下代码,这个代码是使用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端读取到的数据为:

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

从服务端的控制台输出可以看出,存在三种类型的输出

一种是正常的字符串输出。

一种是多个字符串“粘”在了一起,我们定义这种 ByteBuf 为粘包。

一种是一个字符串被“拆”开,形成一个破碎的包,我们定义这种 ByteBuf 为半包。

透过现象分析原因

应用层面使用了Netty,但是对于操作系统来说,只认TCP协议,尽管我们的应用层是按照 ByteBuf 为 单位来发送数据,server按照Bytebuf读取,但是到了底层操作系统仍然是按照字节流发送数据,因此,数据到了服务端,也是按照字节流的方式读入,然后到了 Netty 应用层面,重新拼装成 ByteBuf,而这里的 ByteBuf 与客户端按顺序发送的 ByteBuf 可能是不对等的。因此,我们需要在客户端根据自定义协议来组装我们应用层的数据包,然后在服务端根据我们的应用层的协议来组装数据包,这个过程通常在服务端称为拆包,而在客户端称为粘包。

拆包和粘包是相对的,一端粘了包,另外一端就需要将粘过的包拆开,发送端将三个数据包粘成两个 TCP 数据包发送到接收端,接收端就需要根据应用协议将两个数据包重新组装成三个数据包。

如何解决

在没有 Netty 的情况下,用户如果自己需要拆包,基本原理就是不断从 TCP 缓冲区中读取数据,每次读取完都需要判断是否是一个完整的数据包 如果当前读取的数据不足以拼接成一个完整的业务数据包,那就保留该数据,继续从 TCP 缓冲区中读取,直到得到一个完整的数据包。 如果当前读到的数据加上已经读取的数据足够拼接成一个数据包,那就将已经读取的数据拼接上本次读取的数据,构成一个完整的业务数据包传递到业务逻辑,多余的数据仍然保留,以便和下次读到的数据尝试拼接。

而在Netty中,已经造好了许多类型的拆包器,我们直接用就好:

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

选好拆包器后,在代码中client段和server端将拆包器加入到chanelPipeline之中就好了:

如上实例中:

客户端:

ch.pipeline().addLast(new FixedLengthFrameDecoder(31));

服务端:

ch.pipeline().addLast(new FixedLengthFrameDecoder(31));

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

Netty 的零拷贝

传统意义的拷贝

是在发送数据的时候,传统的实现方式是:

1. `File.read(bytes)`

2. `Socket.send(bytes)`

这种方式需要四次数据拷贝和四次上下文切换:

1. 数据从磁盘读取到内核的read buffer

2. 数据从内核缓冲区拷贝到用户缓冲区

3. 数据从用户缓冲区拷贝到内核的socket buffer

4. 数据从内核的socket buffer拷贝到网卡接口(硬件)的缓冲区

零拷贝的概念

明显上面的第二步和第三步是没有必要的,通过java的FileChannel.transferTo方法,可以避免上面两次多余的拷贝(当然这需要底层操作系统支持)

1. transferTo를 호출하면 DMA 엔진이 파일에서 커널 읽기 버퍼로 데이터를 복사합니다

2. 그런 다음 DMA는 커널 읽기 버퍼에서 네트워크 카드 인터페이스 버퍼로 데이터를 복사합니다

위의 두 가지 작업은 다음과 같습니다. CPU 참여가 필요하지 않으므로 Zero Copy가 달성됩니다.

Netty의 Zero copy

는 주로 세 가지 측면에서 반영됩니다.

1.bytebuffer

Netty는 주로 메시지를 보내고 받기 위해 bytebuffer를 사용하고, bytebuffer는 소켓을 직접 읽고 쓰기 위해 외부 메모리(DirectMemory)를 사용합니다.

이유: 기존 힙 메모리가 소켓 읽기 및 쓰기에 사용되는 경우 JVM은 힙 메모리 버퍼를 직접 메모리에 복사한 다음 이를 소켓에 작성하여 버퍼의 추가 메모리 복사본을 생성합니다. DirectMemory는 DMA

2, Composite Buffers

Traditional ByteBuffer를 통해 네트워크 카드 인터페이스로 직접 보낼 수 있습니다. 두 ByteBuffer의 데이터를 결합해야 하는 경우 먼저 크기=size1+size2의 새 배열을 만들어야 합니다. 그런 다음 두 배열의 데이터를 새 배열에 복사합니다. 그러나 Netty에서 제공하는 결합된 ByteBuf를 사용하면 CompositeByteBuf가 실제로 여러 버퍼를 결합하지 않고 해당 참조를 저장하여 데이터 복사를 방지하고 제로 복사를 달성하므로 이러한 작업을 피할 수 있습니다.

3. FileChannel.transferTo 사용

Netty는 운영 체제를 사용하여 Zero Copy를 구현하는 FileChannel의 transferTo 메서드를 사용합니다.

Netty 내부 실행 프로세스

Server:

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

1. ServerBootStrap 인스턴스 생성

2 설정 및 바인딩 Reactor Group, EventL oop은 모든 것을 처리합니다. 등록 이 스레드의 선택기에서 채널로

3. 서버에서 채널을 설정하고 바인딩합니다.

4, 5. 네트워크 이벤트를 처리하기 위한 ChannelPipeline 및 핸들러를 생성합니다. 핸들러는 대부분의 기능 사용자 정의를 완료합니다. 예를 들어 SSL 보안 인증 인코딩 및 디코딩

6. 대기 포트 바인딩 및 시작

7. 준비된 채널이 순환적으로 훈련되면 NioEventLoop가 해당 메소드를 실행합니다. pipline, 그리고 마지막으로 채널 핸들러를 예약하고 실행합니다

8. Say 그런데, 저는 소통하고 토론할 수 있을 뿐만 아니라 인터뷰 경험을 공유하고 무료 자료를 다운로드할 수 있는 Java 통신 및 학습 커뮤니티인 586446657을 추천하고 싶습니다. Spring, MyBatis, Netty 소스 코드 분석, 높은 동시성, 고성능, 분산을 포함하여 마이크로서비스 아키텍처와 JVM 성능 최적화의 원리는 설계자에게 필수적인 지식 시스템이 되었습니다. 이미 작업 중이고 기술적인 병목 현상에 직면한 코더들에게 필요한 콘텐츠가 여기에 있을 것이라고 믿습니다.

Client

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석

Summary

위는 Netty 관련 지식을 요약한 내용입니다. 다른 의견이 있으시면 편하게 토론해주세요!

위 내용은 네티란 무엇인가요? Netty 관련 지식에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 cnblogs.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제