点击“开发者技术前线”,选择“星标”
在看|星标|留言, 真爱
下图以?阻塞读为例展示了整个异步?阻塞读及回调处理过程:
协程之间的切换?般可分为『星形切换』和『环形切换』,参照下图:
在 络协程库中,内部有一个缺省的IO调度协程,其负责处理与 络IO相关的协程调度过程,故称之为IO调度协程:
-
每?个?络连接绑定?个套接字句柄,该套接字绑定?个协程;
-
当对?络套接字进?读或写发生阻塞时,将该套接字添加? IO 调度协程的事件引擎中并设置读写事件,然后将该协程挂起;这样所有处于读写等待状态的?络协程都被挂起,且与之关联的?络套接字均由 IO 调度协程的事件引擎统?监控管理;
-
当某些?络套接字满?可读或可写条件时,IO 调度协程的事件引擎返回这些套接字的状态,IO 调度协程找到与这些套接字绑定的协程对象,然后将这些协程追加至协程调度队列中,使其依次运?;
-
IO 事件协程内部本身是由系统事件引擎(如:Linux 下的 epoll 事件引擎)驱动的,其内部 IO 事件的驱动机制和上?介绍的?阻塞过程相似,当某个套接字句柄『准备就绪』时,IO 调度协程便将其所绑定的协程添加进协程调度队列中,待本次 IO 调度协程返回后,会依次运?协程调度队列?的所有协程。
(四)?络协程示例
下?给出?个使?协程?式编写的?络服务器程序(更多示例参见:https://github.com/iqiyi/libfiber/tree/master/samples ):
仔细观察上?处理流程,可以发现在图中的标注4(唤醒协程)和标注5(挂起协程)之间的两个事件操作:标注2取消读事件 与 标注3注册读事件,再结合 标注1注册读事件,完全可以把注2和标注3处的两个事件取消,因为标注1?标注3的?标是 注册读事件。最后,通过缓存事件操作的中间状态,合并中间态的事件操作过程,使 libfiber 的 IO 处理性能提升 20% 左右。
下图给出了采? libfiber 编写的回显服务器与采?其它?络协程库编写的回显服务器的性能对?(对?单核条件下的 IO 处理能?):
同?线程内的协程在等待锁资源时,该协程将被挂起并被加?锁等待队列中,当加锁协程解锁后会唤醒锁等待队列中的头部协程,单线程内部的协程互斥锁正是利?了协程的挂起和唤醒机制。
3.3.2、多线程之间的协程互斥
虽然 libfiber 的协程调度器是单线程模式的,但却可以启动多个线程使每个线程运?独?的协程调度器,如果?些资源需要在多个线程中的协程间共享,则就需要有?把可以跨线程使?的协程互斥锁。将 libfiber 应?在多线程的简单场景时,直接使?系统提供的线程锁就可以解决很多问题,但线程锁当遇到如下场景时就显得?能为?:
该可?于在线程之间的协程进?互斥的事件互斥锁的处理流程为:
? 协程B(假设其属于线程b)已经对事件锁加锁后;
? 协程A(假设其属于线程a)想对该事件锁加锁时,对原?数加锁失败后创建IO管道,将IO读管道置?该事件锁的IO读等待队列中,此时协程A被挂起;
? 当协程B 对事件锁解锁时,会?先获得协程A 的读管道,解锁后再向管道中写?消息,从?唤醒协程A;
? 协程A 被唤醒后读取管道中的消息,然后再次尝试对事件锁中的原?数加锁,如加锁成功便可以继续运?,否则会再次进?睡眠状态(有可能此事件锁?被其它协程提前抢占)。
在上述事件锁的加/解锁处理过程中,使?原?数和IO管道的好处是:
-
通过使?原?数可以使协程快速加锁空闲的事件锁,原?数在多线程或协程环境中的?为相同的,可以保证安全性;
-
当锁被占?时,该协程进入IO管道读等待状态而被挂起,这并不会影响其所属的线程调度器的正常运行;在 Linux 平台上可以使? eventfd 代替管道,其占?资源更少。
3.3.3、协程条件变量
在使?线程编程时,都知道线程条件变量的价值:在线程之间传递消息时往往需要组合线程条件变量和线程锁。因此,在 libfiber 中也设计了协程条件变量(源码? fiber_cond.c),通过组合使? libfiber 中的协程事件锁(fiber_event.c)和协程条件变量,?户便可以编写出?于在线程之间、线程与协程之间、线程内的协程之间、线程间的协程之间进?消息传递的消息队列。下图为使? libfiber 中协程条件变量时的交互过程:
当有?量协程需要访问后台系统时,通过协程信 量将?量的协程『挡在外?』,只允许部分协程与后端系统建?连接。
注: ?前 libfiber 的协程信 量仅?在同?线程内部,还不能跨线程使?,要想在多线程环境中使?,需在每个线程内部创建独?的协程信 量。
3.4、域名解析
?络协程既然?向?络应用场景,?然离不开域名的协程化?持,现在很多?络协程库的设计者往往忽视了这?点,有些?络协程库在使?系统 API 进?域名解析时为了防?阻塞协程调度器,将域名解析过程(即调?gethostbyname/getaddrinfo 等系统 API)扔给独?的线程去执?,当域名解析并发量较?时必然会造成很多线程资源被占?。
在 libfiber 中集成了第三? dns 源码,实现了域名解析过程的协程化,占?更低的系统资源,基本满?了?部分服务端应?系统对于域名解析的需求。
3.5、Hook 系统 API
在 络协程广泛使用前,很多?络库很早就存在了,并且?部分这些?络库都是阻塞式的,要改造这些?络库使之协程化的成本是?常巨?的,我们不可能采?协程?式将这些?络库重新实现?遍,?前?个?泛采?的?案是 Hook 与 IO 及 络相关的系统中 API,在 Unix 平台上 Hook 系统 API 相对简单,在初始化时,先加载并保留系统 API 的原始地址,然后编写?个与系统 API 函数名相同且参数也相同的函数,将这段代码与应?代码?起编译,则编译器会优先使?这些 Hooked API,下?的代码给出了在 Unix 平台上 Hook 系统 API 的简单示例:
HPDNS 服务的特点如下:
优点 |
说明 |
高性能 |
启用 Linux 3.0 内核的 REUSEPORT 功能,提升多线程并行收发包的能力 |
采用 Linux 3.0 内核的 recvmmsg/sendmmsg API,提升单次 IO 数据包收发能力 |
|
采用内存预分配策略,减少内存动态分配/释放时的“锁”冲突 |
|
针对 TCP 服务模式,采用 络协程框架,最大化 TCP 并发能力 |
|
高可用 |
采用RCU(Read Copy Update)方式更新视图数据及配置项,无需停止服务,且不影响性能 |
卡 IP 地址变化自动感知(即可自动添加新 IP 或摘除老IP而不必停止服务) |
|
采用 Keepalived 保证服务高可用 |
|
易管理 |
由 master 服务管理模块管理 DNS 进程,控制 DNS 进程的启动、停止、重读配置/数据、异常重启及异常 警等 |
由于 DNS 协议要求 DNS 服务端需要同时支持 UDP 及 TCP 两种通信方式,除了要求 UDP 模块具备高性能外,对 TCP 模块也要求支持高并发及高性能,该模块的 络通信部分使用 libfiber 编写,从而支持更高的并发连接,同时具备更高的性能,又因启用多个线程调度器,从而可以更加方便地使用多核。
4.2.3、项目成果
爱奇艺自研的高性能 DNS 的单机处理能力(非 DPDK 版本)可以达到 200 万次/秒以上;将业务域名变更后的信息同步至全 自建 DNS 节点可以在一分钟内完成。
五、总结
分段锁是一种优秀的优化思想,juc中提供的的ConcurrentHashMap也是基于分段锁保证读写操作的线程安全。
请求速率,给“流量限制”配置burst和nodelay参数。还涵盖了针对客户端IP地址的白名单和黑名单应用不同“流量限制”的高级配置,阐述了如何去日志记录被拒绝和延时的请求。
前线推出学习交流群,加群一定要备注:
研究/工作方向+地点+学校/公司+昵称(如Java+上海+上交+可可)
根据格式备注,可更快被通过且邀请进群,领取一份专属学习礼包
-
好文点个在看吧!
好文点个在看吧!

文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树并发并发的定义91312 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!