零拷贝
直接IO技术
磁盘–>内核缓冲区(内核空间)–>应用程序内存(用户空间)–>Socket 缓冲区(内核缓冲区)–>网络。
内存映射文件技术
磁盘–>内核缓冲区(内核缓冲区)–>Socket缓冲区(内核缓冲区)–>网络
零拷贝技术
磁盘–>内核缓冲区–>网络
Netty和Protobuff
Netty功能强大,内置了多种解码编码器,支持多种协议。
性能高,对比其他主流的NIO框架,Netty的性能最优。
Dubbo、Elasticsearch都采用了Netty,质量得到验证。
Protobuff是一种用于序列化结构化数据的免费开源跨平台数据格式³。它由Google开发,支持多种编程语言,如C++、Java、Python等。Protobuff的优点有:
- 轻量级高效,可以将数据压缩到很小的字节流,节省网络传输和存储空间。
- 可扩展,可以在不破坏兼容性的情况下添加或删除数据字段。
- 无关平台,无关语言,可以在不同的系统和环境中使用。
Hadoop使用netty
hadoop是通过Netty构建性能更高的网络IO层的原理如下:
- Netty是一个基于Java NIO的异步事件驱动的网络应用框架,它提供了统一的API,灵活的事件模型,可定制的线程模型,以及完整的SSL/TLS和StartTLS支持¹。
- Netty使用Reactor模式,将网络IO操作分配给多个EventLoop,每个EventLoop负责处理一个或多个Channel(代表Socket连接或其他IO组件)²。
- Netty使用ChannelPipeline和ChannelHandler机制,将业务逻辑和网络IO操作分离,每个Channel都有一个ChannelPipeline,其中包含多个ChannelHandler,用于处理不同的事件和数据转换²。
- Netty使用ByteBuf作为数据容器,它是一个可读写、自动扩展、引用计数、池化的字节缓冲区,它可以减少内存复制和垃圾回收开销²。
- Netty使用Bootstrap或ServerBootstrap来配置和启动客户端或服务器端的网络应用程序,它们可以设置各种参数,如端口号、线程组、Channel类型、ChannelInitializer、ChannelOption等²。
- Netty支持多种协议栈,如TCP/UDP、HTTP/HTTPS、WebSocket、Google Protocol Buffers等,也可以方便地定制和开发私有协议栈¹。
综上所述,hadoop是通过Netty构建性能更高的网络IO层的,主要是利用了Netty的异步事件驱动、Reactor模式、ChannelPipeline机制、ByteBuf优化、Bootstrap配置和协议栈支持等特点。hadoop使用Netty作为其RPC框架Avro的基础通信组件,实现了跨节点的高效通信。
java网络IO模型
同步与异步
- 异步:read/write 过程托管给操作系统来完成,完成后操作系统会通知(通过回调或者事件)应用网络 IO 程序(其中的线程)来进行后续的处理。
- 同步:read/write 过程由网络 IO 程序(其中的线程)来完成。
BIO
BIO:同步的、阻塞式 IO。在这种模型中,服务器上一个线程处理一次连接,即客户端每发起一个请求,服务端都要开启一个线程专门处理该请求。这种模型对线程量的耗费极大,且线程利用率低,难以承受请求的高并发。BIO 虽然可以使用线程池+等待队列进行优化,避免使用过多的线程,但是依然无法解决线程利用率低的问题。
NIO
NIO:同步的、非阻塞式 IO。在这种模型中,服务器上一个线程处理多个连接,即多个客户端请求都会被注册到多路复用器(后文要讲的 Selector)上,多路复用器会轮训这些连接,轮训到连接上有 IO 活动就进行处理。NIO 降低了线程的需求量,提高了线程的利用率。Netty 就是基于 NIO 的。NIO 是面向缓冲区编程的,从缓冲区读取数据的时候游标在缓冲区中是可以前后移动的,这就增加了数据处理的灵活性。这和面向流的 BIO 只能顺序读取流中数据有很大的不同。
为什么Netty是异步的
NIO是一种非阻塞的IO模型,它可以让线程在等待IO操作完成时,去处理其他任务,而不是一直阻塞在IO操作上。这样就提高了线程的利用率和系统的吞吐量。
- Netty是一个基于NIO的网络框架,它使用Reactor模式,将网络IO操作分配给多个EventLoop,每个EventLoop负责处理一个或多个Channel(代表Socket连接或其他IO组件)。EventLoop内部维护了一个Selector,用于监听Channel上的事件,如可读、可写、连接等²。
- 当EventLoop检测到某个Channel上有事件发生时,它会调用相应的ChannelHandler来处理事件。ChannelHandler是Netty中的核心概念,它是一个接口,定义了各种回调方法,用于处理不同的事件和数据转换。用户可以自定义ChannelHandler来实现业务逻辑²。
- Netty中的所有IO操作都是异步的,也就是说,当用户调用某个IO操作时,例如write或connect,Netty会立即返回一个ChannelFuture对象,表示该操作的结果。ChannelFuture是一个接口,它提供了一些方法来检查操作是否完成、是否成功、是否取消等。用户可以通过添加Listener来监听ChannelFuture的状态变化,从而获取IO操作的结果²。
- 由于Netty是基于NIO的,所以它可以利用NIO的非阻塞特性,让EventLoop在等待某个Channel上的事件时,去处理其他Channel上的事件,而不是一直阻塞在某个Channel上。这样就实现了异步事件驱动的模型,提高了系统的性能和可扩展性²。
AIO
AIO:异步非阻塞式 IO。在这种模型中,由操作系统完成与客户端之间的 read/write,之后再由操作系统主动通知服务器线程去处理后面的工作,在这个过程中服务器线程不必同步等待 read/write 完成。由于不同的操作系统对 AIO 的支持程度不同,AIO 目前未得到广泛应用。因此本文对 AIO 不做过多描述。
使用 Java NIO 构建的 IO 程序,它的工作模式是:主动轮训 IO 事件,IO 事件发生后程序的线程主动处理 IO 工作,这种模式也叫做 Reactor 模式。使用 Java AIO 构建的 IO 程序,它的工作模式是:将 IO 事件的处理托管给操作系统,操作系统完成 IO 工作之后会通知程序的线程去处理后面的工作,这种模式也叫做 Proactor 模式。
OSI七层网络模型
OSI七层模型 | |||
---|---|---|---|
层级 | 层 | 英文全称 | 常用协议 |
7 | 应用层 | Application Layer | HTTP,FTP,SMTP,TELNET,NNTP |
6 | 表示层 | Presentation Layer | LPP |
5 | 会话层 | Session Layer | SSL,TLS,DAP |
4 | 传输层 | Transport Layer | TCP,UDP |
3 | 网络层 | Network Layer | IP,ICMP,RIP,IGMP,OSPF,路由器它使用IP地址进行寻址和路由选择,实现源IP到目标IP的端到端的无连接数据报服务。 |
2 | 数据链路层 | Data Link Layer | 以太网,网卡,交换机 |
1 | 物理层 | Physical Layer | 光纤,集线器 |
TCP和UDP区别
TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,当应用程序采用 TCP 发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。TCP是面向面向字节流,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。
TCP 为提供可靠性传输,实行“顺序控制”或“重发控制”机制。此外还具备“流控制(流量控制)”、“拥塞控制”、提高网络利用率等众多功能。
UDP 是面向报文的,所谓面向报文,是指面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。
UDP 是不具有可靠性的数据报协议,细微的处理它会交给上层的应用去完成。在 UDP 的情况下,虽然可以确保发送消息的大小,却不能保证消息一定会到达。因此,应用有时会根据自己的需要进行重发处理。
UDP 常用于以下几个方面:
1.包总量较少的通信(DNS、SNMP等);
2.视频、音频等多媒体通信(即时通信);
3.限定于 LAN 等特定网络中的应用通信;
4.广播通信(广播、多播)。
三次握手四次握手
第一次握手
客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。
PS1:SYN=1,ACK=0表示该报文段为连接请求报文。
PS2:x为本次TCP通信的字节流的初始序号。 TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。 该应答发送完成后便进入SYN-RCVD状态。
PS1:SYN=1,ACK=1表示该报文段为连接同意的应答报文。
PS2:seq=y表示服务端作为发送者时,发送字节流的初始序号。
PS3:ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。 该报文段的头部为:ACK=1,seq=x+1,ack=y+1。 客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
什么连接建立需要三次握手,而不是两次握手?
防止失效的连接请求报文段被服务端接收,从而产生错误。
PS:失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是『失效的』。
若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。
为什么 A 要先进入TIME-WAIT状态,等待2MSL时间后才进入CLOSED状态
为了保证B能收到A的确认应答。 若A发完确认应答后直接进入CLOSED状态,那么如果该应答丢失,B等待超时后就会重新发送连接释放请求,但此时A已经关闭了,不会作出任何响应,因此B永远无法正常关闭。
HTTP
请求方法
- GET:请求指定的页面信息,并返回实体主体。
- POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
- HEAD:类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
- PUT:从客户端向服务器传送的数据取代指定的文档的内容。
- DELETE:请求服务器删除指定的页面。
GET与PUT的区别
- 都包含请求头请求行,post多了请求body。
- get多用来查询,请求参数放在url中,不会对服务器上的内容产生作用。post用来提交,如把账号密码放入body中。
- GET是直接添加到URL后面的,直接就可以在URL中看到内容,而POST是放在报文内部的,用户无法直接看到。
- GET提交的数据长度是有限制的,因为URL长度有限制,具体的长度限制视浏览器而定。而POST没有。
状态码
1XX- 信息型,服务器收到请求,需要请求者继续操作。
2XX- 成功型,请求成功收到,理解并处理。
3XX - 重定向,需要进一步的操作以完成请求。
4XX - 客户端错误,请求包含语法错误或无法完成请求。
5XX - 服务器错误,服务器在处理请求的过程中发生了错误。
200 OK - 客户端请求成功
301 - 资源(网页等)被永久转移到其它URL
302 - 临时跳转
400 Bad Request - 客户端请求有语法错误,不能被服务器所理解
401 Unauthorized - 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
404 - 请求资源不存在,可能是输入了错误的URL
500 - 服务器内部发生了不可预期的错误
503 Server Unavailable - 服务器当前不能处理客户端的请求,一段时间后可能恢复正常。
缺点
- 请求信息明文传输,容易被窃听截取。
- 数据的完整性未校验,容易被篡改
- 没有验证对方身份,存在冒充危险
HTTPS
HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer):一般理解为HTTP+SSL(Secure Socket Layer,安全套接字层)/TLS(Transport Layer Security,传输层安全),通过 SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密。
传输流程
- 首先客户端通过URL访问服务器建立SSL连接。
- 服务端收到客户端请求后,会将网站支持的证书信息(证书中包含公钥)传送一份给客户端。
- 客户端的服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
- 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
- 服务器利用自己的私钥解密出会话密钥。
- 服务器利用会话密钥加密与客户端之间的通信。
缺点
- HTTPS协议多次握手,导致页面的加载时间延长近50%;
- HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗;
- 申请SSL证书需要钱,功能越强大的证书费用越高。
- SSL涉及到的安全算法会消耗 CPU 资源,对服务器资源消耗较大。
HTTPS和HTTP区别
- HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
- HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
- HTTP 的端口号是 80,HTTPS 的端口号是 443。
- HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。
RPC
RPC与HTTP的区别
传输协议
RPC,可以基于TCP协议,也可以基于HTTP协议。HTTP,基于HTTP协议
传输效率
RPC,使用自定义的TCP协议,可以让请求报⽂体积更⼩,或者使⽤HTTP2协议,也可以很好的减少报⽂的体积,提⾼传输效率
HTTP,如果是基于HTTP1.1的协议,请求中会包含很多无用的内容,如果是基于HTTP2.0,那么简单的封装以下是可以作为⼀个RPC来使⽤的,这时标准RPC框架更多的是服务治理
性能消耗
RPC,可以基于thrift实现⾼效的⼆进制传输
HTTP,⼤部分是通过json来实现的,字节⼤⼩和序列化耗时都⽐thrift要更消耗性能
原因:thrift使用二进制格式进行数据传输,比json更高效和紧凑,但也更难阅读和调试。json使用文本格式进行数据传输,比thrift更易读和通用,但也更占用空间和性能。
负载均衡
RPC,基本都⾃带了负载均衡策略。
HTTP,需要配置Nginx,HAProxy来实现
服务治理(下游服务新增,重启,下线时如何不影响上游调用者)
RPC,能做到⾃动通知,不影响上游
HTTP,需要事先通知,修改Nginx/HAProxy配置
RPC负载均衡
根据网上搜索结果,我为您介绍一下RPC的负载均衡策略:
RPC的负载均衡是指在RPC框架中,服务调用方如何从多个服务提供方中选择一个合适的节点来处理请求的问题。
- 随机策略是指按照一定的概率随机选择一个服务节点,可以实现简单的负载均衡,但不考虑服务节点的实际负载情况。
- 轮询策略是指按照顺序依次选择一个服务节点,可以保证每个节点都有相同的请求量,但也不考虑服务节点的实际负载情况。
- 权重策略是指根据每个服务节点的预设权重值来分配请求量,可以实现按照比例分配流量,但需要手动配置权重值,并且权重值不会随着服务节点的实际负载情况而变化。
- Hash策略是指根据请求的参数或者IP地址等信息计算一个哈希值,然后根据哈希值选择一个服务节点,可以实现相同请求总是落在同一个节点上,但也不考虑服务节点的实际负载情况。
- 除了这些基本的策略,还有一些自适应的负载均衡策略,它们可以根据服务节点的实际负载情况来动态调整权重或者选择最优的节点。这些策略需要收集和分析服务节点的指标数据,如CPU负载、内存占用率、请求耗时等,并且根据一定的算法来计算出一个分数或者权重,然后根据分数或者权重来选择合适的服务节点。这些策略可以更好地利用集群资源,提高系统性能和可用性。
SpringCloud和Dubbo的区别
生态环境不同:SpringCloud依托于Spring平台,具备更加完善的生态体系;而Dubbo一开始只是做RPC远程调用,生态相对匮乏,现在逐渐丰富起来。
调用方式:SpringCloud是采用Http协议做远程调用,接口一般是Rest风格,比较灵活;Dubbo是采用Dubbo协议,接口一般是Java的Service接口,格式固定。但调用时采用Netty的NIO方式,性能较好。
组件差异比较多,例如SpringCloud注册中心一般用Eureka,而Dubbo用的是Zookeeper。
SpringCloud:Spring公司开源的微服务框架,SpirngCloud 定位为微服务架构下的一站式解决方案。Dubbo:阿里巴巴开源的RPC框架,Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。
Euraka和Zookeeper的区别
从集群设计来看:Eureka集群各节点平等,没有主从关系,因此可能出现数据不一致情况;ZK为了满足一致性,必须包含主从关系,一主多从。集群无主时,不对外提供服务
CAP原则来看:Eureka满足AP原则,为了保证整个服务可用性,牺牲了集群数据的一致性;而Zookeeper满足CP原则,为了保证各节点数据一致性,牺牲了整个服务的可用性。
服务拉取方式来看:Eureka采用的是服务主动拉取策略,消费者按照固定频率(默认30秒)去Eureka拉取服务并缓存在本地;ZK中的消费者首次启动到ZK订阅自己需要的服务信息,并缓存在本地。然后监听服务列表变化,以后服务变更ZK会推送给消费者。
Dubbo框架
节点介绍
Provider: 暴露服务的服务提供方
Consumer: 调用远程服务的服务消费方
Registry: 服务注册与发现的注册中心
Monitor: 统计服务的调用次数和调用时间的监控中心
Container: 服务运行容器
调用关系介绍
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟8. 发送一次统计数据到监控中心。
宕机处理
注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
服务提供者无状态,任意一台宕掉后,不影响使用
服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
动态代理
根据网上搜索结果,我为您介绍一下java在RPC实现中使用的动态代理:
- 动态代理是一种设计模式,它可以在运行时动态地创建一个对象的代理,而不需要事先为每个对象编写代理类。
- 动态代理在RPC实现中的作用是生成客户端存根(client stub)和服务端存根(server stub),这两个存根负责将方法调用转换为网络通信,从而实现远程过程调用的透明性¹²。
- java提供了两种方式来实现动态代理:基于接口的JDK动态代理和基于类的CGLIB动态代理³。
- JDK动态代理是通过实现java.lang.reflect.InvocationHandler接口和使用java.lang.reflect.Proxy类来创建代理对象的,它要求被代理的对象必须实现一个或多个接口³。
- CGLIB动态代理是通过继承被代理对象的类和使用net.sf.cglib.proxy.Enhancer类来创建代理对象的,它不要求被代理的对象实现接口,但不能代理final类或方法³。