网络协程编程

2023-10-30

一、背景

 为什么需要网络协程?

1、协程/纤程并不是一个新概念
2、大并发、高性能对于服务端的高要求
3、移动设备的快速增长加大了服务端大并发压力
4、Go 语言的兴起将协程带到了一个新的高度

支持协程的编程语言:
1、Go 语言,非常容易支持大并发、高性能
2、Python 语言
3、Erlang 语言
4、Lua 语言
。。。。。。

为什么要设计一套 C/C++ 网络协程库?
1、学习一部门语言的成本要远高于学习一个库
2、C/C++ 程序员多年的经验积累损耗巨大
3、C/C++ 综合运行效率高
二、关于并发
 - 虽已进入多核时代,但服务器的 CPU 核心总是有限的
 - 当进程/线程数越多操作系统的调度算法就越低效
 - TCP长连接及连接池的存在,造成服务端80%以上的连接是空闲的

为支持并发,我们需要采用:
1、多进程模式:支持并发能力非常有限,如 Postfix,Xinetd;
2、多线程模式:比多进程模式有提高,但依然有限,如 Mysql;
3、非阻塞模式:性能高,但编程复杂度极高,如 Nginx,Redis;
4、基于事件的多线程模式:并发度有较大提高,但编程提升依然有限,如 acl 中的 master_threads 服务模式;
三、设计目标
 我们需要一种新的编程模式来满足C/C++程序员:
1、支持大并发、高性能,较低的资源使用率
2、较低的编程复杂度:顺序思维模式
3、适合多数应用场景,提供丰富且简单易用的接口
4、与第三方网络库无缝集成,无需修改第三方库
四、一个简单的协程示例
1、创建协程类似于创建线程
2、支持大并发、高性能
3、顺序性编程方式
4、无需更改第三方库
5、仅使用一个线程资源

 五、协程的调度方式

 1、上下文切换
 通过操作系统提供的 API 完成:getcontext、makecontext、swapcontext、setcontext;
 或 自己通过汇编语言来实现协程运行栈空间的切换
 实现库举例:libtask,boost,libgo, libco,coroutine  等

 2、信号跳转
 通过系统提供的 API 完成:siglongjmp、longjmp、setjmp、sigsetjmp 等
 实现库举例:libmill,st ,coroutine 等

 

六、协程切换方式


 

七、网络协程调度


 

1、IO事件协程监控所有的IO事件
2、网络协程运行时遇到IO阻塞,则被挂起,其IO句柄由IO事件协程监控
3、IO事件发生时,其绑定的协程被再次唤醒

 

八、如何与第三方库无缝集成

1、HOOK IO相关API
读 API:read/readv/recv/recvfrom/recvmsg
写 API:write/writev/send/sendto/sendmsg
其它 API:pipe/popen/pclose/open/close/fcntl
2、HOOK 网络相关API
socket/socketpair/bind/listen/accept/connect
poll/select/epoll_create/epoll_wait/epoll_ctl
gethostbyname/gethostbyname_r

通过 HOOK 系统底层 API,可以实现:
1、直接接管第三方库(如:mysql/http/redis 等库)的网络连接及通信过程
2、直接接管第三方库的域名解析过程
3、将第三方网络阻塞过程协程化,在协程库底层转化为非阻塞过程

 

将mysql库协程化的例子参见:acl/lib_fiber/samples/mysql

 

九、为何要 HOOK 很多系统API

1、poll/select 为网络编程中常用系统 API
2、很多第三方网络库用 poll/select 模拟IO超时
3、epoll 在 reactor 类应用(如:聊天)方面比较广泛
4、gethostbyname 在域名解析方面应用广泛
5、listen 需要将监听描述字设为非阻塞模式
6、connect 需要将连接描述字设为非阻塞模式
7、bind/socket/socketpair/。。。为便于将出错号与协程绑定
 

十、基于协程的 errno

因为每个线程中存在大量协程,当某个协程的IO过程出错时,如果实现不同协程之间的 errno 是相互隔离的?
--- 在 Linux 平台下直接 HOOK __errno_location 系统函数
参见:/usr/include/bits/errno.h

extern int *__errno_location (void) __THROW __attribute__ ((__const__));
#define errno (*__errno_location ())

针对进程内全局变量:errno,操作系统将该变量定义为一个函数指针地址,函数内部会通过线程局部变量方式给每一个线程分配一个 error 对象
因此,通过 hook __errno_location 函数,在协程库里给每个协程一个协程局部变量,实现了 errno 全局变量的协程安全性

 

十一、内存安全检测

配合 valgrind 做内存检测:
- valgrind 与 xxxcontext 的不兼容性
- 需下载 valgrind 开发包,调用 VALGRIND_STACK_REGISTER通知
  valgrind 跳过检测该内存区域
- 检测时在 Makefile 里打开 –DUSE_VALGRIND 编译选项,重新编译 lib_fiber.a

 

十二、有效使用多核

 每个线程一个独立的协程调度器,通过创建多个线程使用多核
使用 acl master 服务器框架,创建多进程使用多核,每个进程一个协程调度器

多线程示例参见:acl/lib_fiber/samples/redis_threads
多进程示例参见:acl/lib_fiber/samples/master_fiber

 

十三、协程同步原语



 

基于协程的协程锁:
1、协程互斥锁
2、协程读写锁

 

十四、协程挂起与唤醒

-- 协程挂起方式
1、主动让出 CPU 控制权
当前运行的协程通过调用 acl_fiber_yield 主动让出 CPU 控制权,协程调度器调用别的协程
2、指定休眠时间
当前运行的协程通过调用 acl_fiber_sleep 使当前协程休眠指定时间
3、IO阻塞被挂起
当前运行的协程等待IO完成时,需要将自身挂起

-- 协程唤醒方式
1、主动 yield 的协程又重新获得 CPU 控制权
2、处于休眠状态的协程时间到达
3、因IO阻塞而被挂起的协程因IO准备好而被唤醒

示例参考:
1、yield 方式:acl/lib_fiber/samples/fiber
2、sleep 方式:acl/lib_fiber/samples/sleep
3、IO 方式:acl/lib_fiber/samples/select

 

十五、过载保护


 

十六、协程间通信

协程间为什么需要通信?
1、业务逻辑的模块化
2、业务模块的分层设计
3、团队开发的协作性

协程间“通信”的本质:
- 协程间数据的传递通过协程上下文的切换,本质上是协程间的数据交换

协程间“通信”的成本:
1、协程上下文切换
2、内存分配、释放
3、数据拷贝

协程间“通信”方式:
- 支持多对多数据交互



- 协程通信管道支持多对多方式
- 协程间通信通过切换协程上下文及数据交换完成
- 协程间通信时的数据交换支持缓冲模式
- 协程间通信时的数据交换采用随机分配方式

十七、线程间通信

协程模式下为何需要线程间通信?
- 为使用多核,开启多个线程,线程间需要交换数据
- 有些任务需要在线程池里异步完成,结果需要传递给主线程

协程模式下线程间的通信方式:
- 无锁消息队列 + IO 模式

 

十八、线程间通信


1、生产者/消费者之间优先通过无锁队列进行数据传递
2、当生产者无数据时,消费者通过IO堵塞
3、当消费者堵塞在IO等待新消息时,生产者若有新消息则通过IO通知消费者
4、无锁队列利用率越高,则处理性能越高

 

十九、应用场景

(一)、问答式应用服务
基于 HTTP 协议的服务应用,诸如:网站
基于 SMTP/POP3/IMAP 协议的服务应用
(二)、生产者 – 消费者类应用服务
如消息队列类应用
(三)、reactor 和 proactor 两种模式的结合
统一的事件引擎监控所有的网络连接,有一个连接就绪时创建协程独立处理
此类应用如聊天服务、游戏服务等无状态的应用服务
(四)、大并发类应用服务
因为通过协程方式,将上层应用的堵塞式在底层转为非阻塞模式,所以非常容易以较低资源支持大并发类应用
如内网的多数应用服务为提高效率都支持连接池模式,需要服务端支持非常大的并发
(五)、网络限流
在协程中可以直接 sleep,非常容易控制网络流量

 

二十、协程编程注意事项

(一)、协程运行堆栈空间的合理分配
每个协程都需要分配一定的内存空间用于上下文的切换,如果分配大了则会造成内存浪费,分配小了可能造成意外不可恢复的崩溃
一般情况下,每个协程分配32KB ~ 320KB

(二)、协程间需要协作,防止有的忙死,有的饿死
当协程长期占用 CPU 时,应该主动 yield 让出 CPU

(三)、协程内防止有堵塞式操作,以防堵塞当前线程中的所有协程
应通过对业务逻辑模块进行分类,确定不同的协程工作方式,使堵塞操作放在线程池中运行

 

二十一、参考

 

 基于协程的简单网络服务:使用 acl 协程编写高并发网络服务

 基于协程的 WEB 服务:使用协程方式编写高并发的 WEB 服务

 协程库:https://github.com/acl-dev/acl/tree/master/lib_fiber

 acl svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code

 acl github:https://github.com/acl-dev/acl

 acl 国内镜像:http://git.oschina.net/acl-dev/acl/

 qq 群242722074

 微博:http://weibo.com/zsxxsz/

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网络协程编程 的相关文章

  • C语言的异常机制 setjump longjump函数

    与刺激的abort 和exit 相比 goto语句看起来是处理异常的更可行方案 不幸的是 goto是本地的 它只能跳到所在函数内部的标号上 而不能将控制权转移到所在程序的任意地点 当然 除非你的所有代码都在main体中 为了解决这个限制 C
  • golang学习demo4-goroutine并行测试

    知识点 go语言的routine的使用 通道chan的使用 想测试一下go的routine 就写了个求大量素数的程序 分别开1 99个协程进行求解 对比结果 首先是没有开协程的 下面是开了少数几个协程的程序 可以看到在 cnt 5 的时候速
  • Golang笔记:使用exec包执行外部程序与Shell命令

    文章目录 目的 使用演示 Cmd结构体 总结 目的 程序中执行外部程序是比较常用的功能 Golang执行外部程序可以使用标准库中的 os exec https pkg go dev os exec 这个包默认是用来执行外部程序的 可以通过调
  • 网络协程编程

    一 背景 为什么需要网络协程 1 协程 纤程并不是一个新概念2 大并发 高性能对于服务端的高要求3 移动设备的快速增长加大了服务端大并发压力4 Go 语言的兴起将协程带到了一个新的高度支持协程的编程语言 1 Go 语言 非常容易支持大并发
  • [Unity] Unity的lua项目中模拟每帧运行一次的协程

    游戏中 我们经常需要把一些大量计算或一些需要持续逐帧运算的步骤交给协程处理 习惯了Monobehaviour中使用协程来完成这类操作的我们 然而 很多Unity项目在选择热更新方案的时候 基本都会选择使用slua ulua等lua解决方案
  • lua协程

    coroution协程 定义协程函数 co coroutine create function a b end 启动协程函数和继续运行 coroutine resume co 10 20 co coroutine wrap function
  • 多任务

    多任务 1 多任务的概念 多任务的最大好处是充分利用CPU资源 提高程序的执行效率 多任务是指在同一时间内执行多个任务 例如 现在电脑安装的操作系统都是多任务操作系统 可以同时运行着多个软件 多任务的执行方式 并发 并行 是多个任务真正意义
  • Python协程学习--爬取一本网络小说

    协程爬取 最近在学习Python爬虫 同时在公司同事的引导下接触到协程 开始学习使用协程编写异步爬虫 Python协程系列学习参考 https blog csdn net qq 27825451 article details 862182
  • Unity中协程(IEnumerator)的使用方法介绍

    在Unity中 一般的方法都是顺序执行的 一般的方法也都是在一帧中执行完毕的 当我们所写的方法需要耗费一定时间时 便会出现帧率下降 画面卡顿的现象 当我们调用一个方法想要让一个物体缓慢消失时 除了在Update中执行相关操作外 Unity还
  • lua 协同程序(coroutine)

    本文主要是根据菜鸟教程的lua协同程序 coroutine 优化出来的文章 对于网上的lua协同程序的文章 觉得菜鸟教程里的lua协同程序 coroutine 比较完善 但还是有一些地方没有说清楚 对于像我这样的新手学习起来 还是不能完全解
  • 进程、线程和协程的理解

    进程 线程和协程的理解 进程 线程和协程之间的关系和区别也困扰我一阵子了 最近有一些心得 写一下 进程拥有自己独立的堆和栈 既不共享堆 亦不共享栈 进程由操作系统调度 线程拥有自己独立的栈和共享的堆 共享堆 不共享栈 线程亦由操作系统调度
  • Unity 3D协程

    Unity3D 是单线程的 StartCoroutine 开启协程 Coroutine StartCoroutine IEnumerator routine Coroutine StartCoroutine string methodNam
  • 【Kotlin】快速理解协程与挂起

    本文不介绍协程和挂起的基础用法 如需要请移步其他博客 本文主要讲解 kotlin中的协程是什么 协程的作用 挂起是什么 挂起的作用 本文全程尽量白话 使得协程和挂起理解起来更容易 小故事or小事故 之前面试的时候 有个面试官问了我一个问题
  • Android:玩转Retrofit+OkHttp+Kotlin协程 网络请求架构

    文章目录 引言 实战 1 引入开源库 2 简单封装 3 开始使用 MVP架构模式 MVVM架构模式 看 这里 https blog csdn net sange77 article details 103959389 引言 目前做APP网络
  • C语言中协程(coroutine)实现

    C语言协程库实现说明 代码实现 1 当前支持的功能概览 1 1 创建任意数量协程并在协程中yield include
  • 【Unity3D】回合制游戏

    回合制游戏一直在游戏史 至少是在中国的游戏历史上扮演很重要的角色 从仙剑到梦幻 这类游戏深受玩家喜爱 那么在Unity3D中怎么实现呢 下面用一个比较简单Unity3D的一对一回合制游戏来说明这个问题 其实也不难 关键是理清各个处理关系 如
  • Kotlin协程概览

    协程 Coroutines 并不是 Kotlin 提出来的新概念 很多的编程语言都有实现 如 Go Python 等 本文所讲 专指kotlin的协程 在Android 11中 Asynctask已经被废弃了 因为协程可以更简单 直观的实现
  • Lua中的协程Coroutine

    一 协程是什么 1 线程 首先复习一下多线程 我们都知道线程 Thread 每一个线程都代表一个执行序列 当我们在程序中创建多线程的时候 看起来 同一时刻多个线程是同时执行的 不过实质上多个线程是并发的 因为只有一个CPU 所以实质上同一个
  • Golang协程与通道整理

    协程goroutine 不由OS调度 而是用户层自行释放CPU 从而在执行体之间切换 Go在底层进行协助实现 涉及系统调用的地方由Go标准库协助释放CPU 总之 不通过OS进行切换 自行切换 系统运行开支大大降低 通道channel 并发编
  • 进阶之Kotin协程原理和启动方式详细讲解(优雅使用协程)

    协程就是方法调用封装成类线程的API 方法调用当然比线程切换轻量 而封装成类线程的API后 它形似线程 可手动启动 有各种运行状态 能够协作工作 能够并发执行 前言 kotlin的协程在初学者看来是一个很神奇的东西 居然能做到用同步的代码块

随机推荐

  • stm32F4 IAP实现原理讲解以及中断向量表的偏移

    一 IAP原理 IAP即是在应用编程 IAP 是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写 目的是为了在产品发布后可以方便地通过预留的通信口对产 品中的固件程序进行更新升级 通常实现IAP 功能时 即用户程序运行中
  • 生命在于磨炼——连续两年参加4C大赛心得

    一 4C大赛简介 1 大赛简介 中国大学生计算机设计大赛 下面简称 大赛 是由教育部高等学校计算机类专业教学指导委员会 教育部高等学校软件工程专业教学指导委员会 教育部高等学校大学计算机课程教学指导委员会 教育部高等学校文科计算机基础教学指
  • 操作系统笔记五(Linux存储管理)

    1 Buddy内存管理算法 内部碎片就是已经被分配出去 能明确指出属于哪个进程 却不能被利用的内存空间 外部碎片指的是还没有被分配出去 不属于任何进程 但由于太小了无法分配给申请内存空间的新进程的内存空闲区域 目的 努力让内存分配与相邻内存
  • Task2_MySQL_basic

    MySQL表数据类型 用SQL语句创建表 创建MySQL数据表需要以下信息 表名 表字段名 定义每个表字段 语句解释 设定列类型 大小 约束 设定主键 用SQL语句向表中添加数据 语句解释 多种添加方式 指定列名 不指定列名 用SQL语句删
  • Ubuntu16.04下搭建LAMP环境

    Ubuntu16 04下搭建LAMP环境 Ubuntu16 04下搭建LAMP环境 1 安装 Apache2 2 重启 apache2 3 测试apache2是否安装成功 4 安装php7 5 测试php是否安装成功 6 安装mysql数据
  • 序列化与反序列化之Flatbuffers(一):初步使用

    序列化与反序列化之Flatbuffers 一 初步使用 一 前言 在MNN中 一个训练好的静态模型是经过Flatbuffers序列化之后保存在硬盘中的 这带来两个问题 1 为什么模型信息要序列化不能直接保存 2 其他框架如caffe和onn
  • 深度学习在目标视觉检测中的应用进展与展望

    前言 文章综述了深度学习在目标视觉检测中的应用进展与展望 首先对目标视觉检测的基本流程进行总结 并介绍了目标视觉检测研究常用的公共数据集 然后重点介绍了目前发展迅猛的深度学习方法在目标视觉检测中的最新应用进展 最后讨论了深度学习方法应用于目
  • ORAN专题系列-0: O-RAN快速索引

    专题一 O RAN的快速概述 ORAN专题系列 1 什么是开放无线接入网O RAN ORAN专题系列 1 什么是开放无线接入网O RAN 文火冰糖的硅基工坊的博客 CSDN博客 什么是oran ORAN专题系列 2 O RAN的系统架构 O
  • C和C++安全编码笔记:动态内存管理

    4 1 C内存管理 C标准内存管理函数 1 malloc size t size 分配size个字节 并返回一个指向分配的内存的指针 分配的内存未被初始化为一个已知值 2 aligned alloc size t alignment siz
  • Spring Aop自定义注解用在Controller层

    前提项目用的框架是SpringMVC 切面类 Aspect Component 把这个注掉是为了不让Spring中扫描 应该让SpringMVC扫描 public class SysLogAop Pointcut annotation co
  • 图像识别毕业设计 opencv实现植物识别算法系统 - python 深度学习

    文章目录 0 前言 2 相关技术 2 1 VGG Net模型 2 2 VGG Net在植物识别的优势 1 卷积核 池化核大小固定 2 特征提取更全面 3 网络训练误差收敛速度较快 3 VGG Net的搭建 3 1 Tornado简介 1 优
  • Maven项目的jdk版本修改

    Maven项目的jdk版本修改 修改的办法有以下三种 一 选择项目 gt 右键 gt build path Configure build path 选择旧的jre 1 5 gt remove删除 gt add Library 添加新的jr
  • Activity 工作流引擎

    Activiti工作流引擎使用详解 http blog csdn net m0 37327416 article details 71743368 Activity用户手册 http www mossle com docs activiti
  • SpringBoot笔记:SpringBoot 集成 Dataway(一)

    文章目录 1 什么是 Dataway 2 主打场景 3 技术架构 4 整合SpringBoot 4 1 maven 依赖 4 2 初始化脚本 4 3 整合 SpringBoot 5 Dataway 接口管理 6 Mybatis 语法支持 7
  • Kafka3.0.0版本——文件清理策略

    目录 一 文件清理策略 1 1 文件清理策略的概述 1 2 文件清理策略的官方文档 1 3 日志超过了设置的时间如何处理 1 3 1 delete日志删除 将过期数据删除 1 3 2 compact日志压缩 一 文件清理策略 1 1 文件清
  • 【Pytorch】利用Pytorch+GRU实现情感分类(附源码)

    在这个实验中 数据的预处理过程以及网络的初始化及模型的训练等过程同前文 利用Pytorch LSTM实现中文新闻分类 具体这里就不再重复解释了 如果有读者在对数据集的预处理过程中有疑问 请参考我的其他博客 里面对这些方法均有我的一些个人体会
  • 稀缺原理

    不管是什么东西 只要你晓得会失去它 自然就会爱上它了 稀缺原理 机会越少见 价值似乎就越高 对失去某种东西的恐惧似乎比对获得同一物品的渴望 更能激发人们的行动力 稀缺原理的力量来源 1 基本可以根据获得一样东西的难易程度 迅速 准确的判断它
  • plsql developer 终极注册码

    product code 4v6hkjs66vc944tp74p3e7t4gs6duq4m4szbf3t38wq2 serial number 1412970386 password xs374ca 手机扫一扫 欢迎关注公众号 关注程序员成
  • python:从键盘输入一个字符,判别它是否大写字母,如果是,将它转换成小写字母;如果不是,则不转换。然后输出最后得到的字符。

    letter str input 请输入一个字母 if letter lt Z 凡是小于大写Z的都要转换成小写 print 转换小写字母为 letter lower lower 方法可以把大写转换成小写 else print 转换大写字母为
  • 网络协程编程

    一 背景 为什么需要网络协程 1 协程 纤程并不是一个新概念2 大并发 高性能对于服务端的高要求3 移动设备的快速增长加大了服务端大并发压力4 Go 语言的兴起将协程带到了一个新的高度支持协程的编程语言 1 Go 语言 非常容易支持大并发