协程与Swoole的原理,相关应用以及适用场景等 - 知乎


本站和网页 https://zhuanlan.zhihu.com/p/94924860 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

协程与Swoole的原理,相关应用以及适用场景等 - 知乎首发于PHP大神进阶无障碍写文章登录/注册协程与Swoole的原理,相关应用以及适用场景等PHP进阶架构师PHP进阶架构师、Swoole协程高并发、微服务、分布式分享1 人赞同了该文章什么是协程协程(Coroutine)也叫用户态线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。协程是进程的补充,或者是互补关系。要理解是什么是“用户态的线程”,必然就要先理解什么是“内核态的线程”。 内核态的线程是由操作系统来进行调度的,在切换线程上下文时,要先保存上一个线程的上下文,然后执行下一个线程,当条件满足时,切换回上一个线程,并恢复上下文。 协程也是如此,只不过,用户态的线程不是由操作系统来调度的,而是由程序员来调度的,就是所谓的用户态的线程。协程的执行流程 协程的适用场景高并发服务,如秒杀系统、高性能API接口、RPC服务器,使用协程模式,服务的容错率会大大增加,某些接口出现故障时,不会导致整个服务崩溃。爬虫,可实现非常巨大的并发能力,即使是非常慢速的网络环境,也可以高效地利用带宽。即时通信服务,如IM聊天、游戏服务器、物联网、消息服务器等等,可以确保消息通信完全无阻塞,每个消息包均可即时地被处理。协程与线程区别Swoole的协程在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。这与线程不同,多个线程会被操作系统调度到多个CPU并行执行。一个协程正在运行时,其他协程会停止工作。当前协程执行阻塞IO操作时会挂起,底层调度器会进入事件循环。当有IO完成事件时,底层调度器恢复事件对应的协程的执行。对CPU多核的利用,仍然依赖于Swoole引擎的多进程机制。协程实现1、swoole的两种命名空间形式Swoole支持两种形式的命名空间一种是Swoole\Coroutine,2.2.0以上可使用Co\命名空间短命名简化类名。2、协程默认支持的位置目前Swoole4仅有部分事件回调函数底层自动创建了协程,以下回调函数可以调用协程客户端,可以查看这里https://wiki.swoole.com/wiki/page/696.html在不支持协程的位置可以使用go或Co::create创建协程3、协程的性能测试通过多个协程连接redis操作对比没有使用协程的方式4、协程并发协程其实也是阻塞运行的,如果,在一个执行中,比如同时查redis,再去查mysql,即使用了上面的协程,也是顺序执行的。那么可不可以几个协程并发执行呢?通过延迟收包的形式获取,遇到到IO 阻塞的时候,协程就挂起了,不会阻塞在那里等着网络回报,而是继续往下走,swoole当中可以用setDefer()方法声明延迟收包然后通过recv()方法收包。5、协程通讯 使用本地内存,不同的进程之间内存是隔离的。只能在同一进程的不同协程内进行push和pop操作向通道中写入数据。function Coroutine\Channel->push(mixed $data) : bool;从通道中读取数据。function Coroutine\Channel->pop() : mixed;对协程调用场景,最常见的“生产者-消费者”事件驱动模型,一个协程负责生产产品并将它们加入队列,另一个负责从队列中取出产品并使用它。6、协程的注意问题如果在多个协程间共用同一个协程客户端,同步阻塞程序不同,协程是并发处理请求的,因此同一时间可能会有很多个请求在并行处理,一旦共用客户端连接,就会导致不同协程之间发生数据错乱。swoole通用协程池的实现swoole官方的协程池是用只能用在Redis。因为协程池代码层耦合了Redis实例化逻辑。通过工厂函数实现了通用性。class RedisPool
/**
* @var \Swoole\Coroutine\Channel
*/
protected $pool;
/**
* RedisPool constructor.
* @param int $size 连接池的尺寸
*/
function __construct($size = 100)
$this->pool = new Swoole\Coroutine\Channel($size);
for ($i = 0; $i < $size; $i++)
$redis = new Swoole\Coroutine\Redis();
$res = $redis->connect('127.0.0.1', 6379);
if ($res == false)
throw new RuntimeException("failed to connect redis server.");
else
$this->put($redis);
function put($redis)
$this->pool->push($redis);
function get()
return $this->pool->pop();
利用工厂方法的改造如下:<?php
/**
* @author xialeistudio
* @date 2019-05-20
*/
namespace swoole\foundation\pool;
use Swoole\Coroutine\Channel;
/**
* Swoole generic connection pool
* Class Pool
* @package swoole\foundation\pool
*/
class GenericPool
/**
* @var int pool size
*/
private $size = 0;
/**
* @var callable construct a connection
*/
private $factory = null;
/**
* @var Channel
*/
private $channel = null;
/**
* GenericPool constructor.
* @param int $size
* @param callable $factory
* @throws InvalidParamException
*/
public function __construct($size, callable $factory)
$this->size = $size;
$this->factory = $factory;
$this->init();
/**
* check pool config
* @throws InvalidParamException
*/
private function init()
if ($this->size <= 0) {
throw new InvalidParamException('The "size" property must be greater than zero.');
if (empty($this->factory)) {
throw new InvalidParamException('The "factory" property must be set.');
if (!is_callable($this->factory)) {
throw new InvalidParamException('The "factory" property must be callable.');
$this->bootstrap();
/**
* bootstrap pool
*/
private function bootstrap()
$this->channel = new Channel($this->size);
for ($i = 0; $i < $this->size; $i++) {
$this->channel->push(call_user_func($this->factory));
/**
* Acquire a connection
* @param int $timeout
* @return mixed
*/
public function acquire($timeout = 0)
return $this->channel->pop($timeout);
/**
* Release a resource
* @param mixed $resource
*/
public function release($resource)
$this->channel->push($resource);
以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要请戳这里链接 或者关注咱们下面的专栏发布于 2019-12-03 10:02Swoole协程异步 I/O​赞同 1​​添加评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录PHP大神进阶PHP进阶架构师、高并发、微服务、分布式教程分享