RPC框架整个的流程
1、服务端扫描目录,查找是否有自定义注解,有的话就知道是一个服务,会把服务实现类与接口名放入一个Map(本地注册表),把接口名与服务端地址注册到注册中心。之后,服务端初始化ChannelPipeline,定义各种监听事件。
2、客户端启动,通过反向代理调用接口的方法,先会进行服务发现,从Nacos获取对应服务的服务端地址,再与服务端进行连接并初始化客户端,就准备发送消息了,同时客户端会做一个负载均衡,如果当前这个接口名在多个服务端有实现, 就通过负载均衡算法选一个进行发送。
3、服务端收到消息后,到自己的本地注册表中查找对应接口的实现类,通过反射执行对应的方法,将结果返回。
如果发送到服务端,服务端发生了异常怎么处理
NIO比BIO效率高的原因
- 面向缓存区和面向流
- 阻塞IO和非堵塞IO
- 零拷贝
面向流与面向缓冲区
BIO是面向流的,NIO是面向缓冲区的。数据会首先被读取到缓冲区,然后程序可以在缓冲区内随意移动,进行读取、写入操作。
BIO
最传统的IO模型,也被称为同步堵塞IO。客户端每发出一个连接,服务端就需要启动一个线程进行处理,在整个IO期间会被堵塞直到拿到结果。
NIO
称为同步非堵塞IO,基于事件驱动来处理IO。NIO由三部分组成:selector、channel、buffer。
流程如下:当客户端发出一个连接,服务端将其注册到selector,也就是channel,这个连接是非堵塞的,当没有数据读写时不会占用资源。Selector会不断轮询注册在其上的所有channel,如果某个channel发生了注册时指定的事件,那么selector就让服务端程序处理这个事件。
数据的读写会在Buffer中,这可以减少上下文切换的开销。
为什么NIO中一个线程可以管理多个Channel?
堵塞IO与非堵塞IO
BIO的各种流是堵塞的。当一个线程调用read()或write()时,该线程会被堵塞,直到有数据读入或数据写入。而NIO的非堵塞是指当通道中没有东西可读或不可写,读写函数会马上返回,不会堵塞,这个线程就可以去做其他事情。线程将非堵塞的IO空闲时间用在其他通道执行IO操作,这就是为什么一个线程可以管理多个通道,即IO复用的原理。
零拷贝
零拷贝是指避免在用户态与内核态之间来回拷贝数据。
page cahce(页缓存)是内核态和用户态的中间层,如果用户态能直接访问到页缓存,那么就不必来回拷贝了。
select/poll/epoll
这三个是用来实现多路复用
的。也就是线程不会被任何一个连接堵塞,且任何一个连接有数据时要通知 select/poll/epoll 线程。
select采取主动轮询的方式:当事件触发时,把已连接的socket放到文件描述符集合,调用select函数会把fd集合全部拷贝到用户态进行遍历,因为用户态并不知道哪些文件描述符要进行处理。
epoll:采用事件回调机制。会在cache中创建一颗红黑树和一个就绪链表。红黑树存储所有注册的socket的文件描述符。当文件描述符有读写操作时,就将这个fd放入就绪链表。epoll只需要观察就绪链表中有无数据即可。有的话就传递到用户态,并且这里不需要像select那样进行拷贝,因为epoll使用了mmap共享用户态和内核态内存。
ET & LT:
ET:边缘触发。当一个不可读的socket变为可读时,epoll_wait会返回该文件描述符,但之后,即使数据仍然可读,epoll_wait不会再次返回该文件描述符,除非再次从不可读变为可读。
LT:水平触发。在处理该文件描述符时,如果没有读取或写入所有数据(例如,只读取了部分数据),那么该文件描述符在下一次epoll_wait调用时仍然会被返回,直到条件不再满足为止。
高性能网络模型Reactor模型
基于事件驱动。
- 单Reactor单线程模型:I/O操作和非I/O操作都在一个线程上处理。当有多个请求时会延迟响应速度,因此应该把非I/O的操作从线程中卸载。
- 单Reactor多线程模型:与上面不同的是,添加了个工作者线程池,将非I/O操作从Reactor线程中转移到线程池中处理。
- 主从多线程模型:将“接受客户端的连接请求”和“与该客户端的通信”分在了两个Reactor线程来完成。主Reactor完成与客户端的连接请求,但它不负责与客户端通信。它把建立好的连接转交给从Reactor完成与客户端的通信。还可以通过实现subReactor线程池来将海量的连接分发给多个subReactor线程。
Netty重要的组件
- Channel:Netty网络操作类,包括基本的 I/O 操作,如 bind、connect、read、write 等。
- EventLoop:事件调度,通过Reactor 线程模型将事件交给handler。
- ChannelHandler:处理各种事件。
- ChannelPipeline:为ChannelHandler提供了链式容器,组装各种 ChannelHandler。
- ChannelFuture:Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 ChannelFutureListener 监听事件,当操作执行成功或者失败时,监听就会自动触发返回结果。
为什么需要注册中心
1、没有注册中心,客户端是通过固定的服务端地址进行访问的,如果服务端换了地址或服务挂了,客户端就无法访问了。而服务中心可以保存服务提供者的信息,每个服务提供者向注册中心注册自己的服务。这样客户端就可以直接向注册中心调用服务,如果拿到的服务挂了,还可以重新调用服务。
2、当服务端这边的服务更新了,通过注册中心可以及时通知给客户端,客户端就会重新订阅服务,得到最新的服务。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1216271933@qq.com