Swoole入门到实战(二):进程,内存和协程、Swoole完美支持ThinkPHP5、分发Task异步任务机制实现

本文主要是介绍Swoole入门到实战(二):进程,内存和协程、Swoole完美支持ThinkPHP5、分发Task异步任务机制实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、进程,内存和协程

1.1 进程

1.1.1 进程

进程就是 正在运行的程序一个实例
$process = new swoole_process(function(swoole_process $pro) {// todo
//     php redis.php$pro->exec("/usr/local/php/bin/php", [__DIR__.'/../server/http_server.php']);
}, false);$pid = $process->start();
echo $pid . PHP_EOL;
//回收结束运行的子进程
swoole_process::wait();

    clipboard.png

    以树状图显示进程间的关系:pstree -p 进程id
    启动成功后会创建worker_num+2个进程。Master进程+Manager进程+serv->worker_numWorker进程

1.1.2 进程使用场景

    clipboard.png

    clipboard.png

管道:进程和进程间的一个桥梁
echo "process-start-time:".date("Ymd H:i:s");
$workers = [];
$urls = ['http://baidu.com','http://sina.com.cn','http://qq.com','http://baidu.com?search=singwa','http://baidu.com?search=singwa2','http://baidu.com?search=imooc',
];
//创建多个子进程分别模拟请求URL的内容
for($i = 0; $i < 6; $i++) {$process = new swoole_process(function(swoole_process $worker) use($i, $urls) {// curl$content = curlData($urls[$i]);//将内容写入管道//    echo $content.PHP_EOL;$worker->write($content.PHP_EOL);}, true);$pid = $process->start();$workers[$pid] = $process;
}
//获取管道内容
foreach($workers as $process) {echo $process->read();
}
/*** 模拟请求URL的内容  1s* @param $url* @return string*/
function curlData($url) {// curl file_get_contentssleep(1);return $url . "success".PHP_EOL;
}
echo "process-end-time:".date("Ymd H:i:s");

1.2 Swoole内存-table详解

内存操作模块之: Table

    swoole_table一个基于共享内存和锁实现的超高性能,并发数据结构
    使用场景:用于解决多进程/多线程数据共享和同步加锁问题
    进程结束后内存表会自动释放

// 创建内存表
$table = new swoole_table(1024);// 内存表增加一列
$table->column('id', $table::TYPE_INT, 4);
$table->column('name', $table::TYPE_STRING, 64);
$table->column('age', $table::TYPE_INT, 3);
$table->create();$table->set('singwa_imooc', ['id' => 1, 'name'=> 'singwa', 'age' => 30]);
// 另外一种方案
$table['singwa_imooc_2'] = ['id' => 2,'name' => 'singwa2','age' => 31,
];$table->decr('singwa_imooc_2', 'age', 2);
print_r($table['singwa_imooc_2']);echo "delete start:".PHP_EOL;
$table->del('singwa_imooc_2');
print_r($table['singwa_imooc_2']);

1.3 协程

    线程、进程、协程的区别
    进程,线程,协程与并行,并发
    并发与并行的区别?

$http = new swoole_http_server('0.0.0.0', 9001);$http->on('request', function($request, $response) {// 获取redis 里面 的key的内容, 然后输出浏览器$redis = new Swoole\Coroutine\Redis();$redis->connect('127.0.0.1', 6379);$value = $redis->get($request->get['a']);// mysql.....//执行时间取它们中最大的:time = max(redis,mysql)$response->header("Content-Type", "text/plain");$response->end($value);
});$http->start();    

二、Swoole完美支持ThinkPHP5(重难点

2.1 面向过程方案

2.1.1 面向过程代码实现

$http = new swoole_http_server("0.0.0.0", 9911);$http->set(['enable_static_handler' => true,'document_root' => "/home/wwwroot/swoole/thinkphp/public/static",'worker_num' => 5,]
);
//此事件在Worker进程/Task进程启动时发生,这里创建的对象可以在进程生命周期内使用
$http->on('WorkerStart', function(swoole_server $server,  $worker_id) {// 定义应用目录define('APP_PATH', __DIR__ . '/../../../../application/');// 加载框架里面的文件require __DIR__ . '/../../../../thinkphp/base.php';
});
$http->on('request', function($request, $response) use($http){//如果在每次请求时加载框架文件,则不用修改thinkphp5源码
//    // 定义应用目录
//    define('APP_PATH', __DIR__ . '/../../../../application/');
//    // 加载框架里面的文件
//    require_once __DIR__ . '/../../../../thinkphp/base.php';/*** 解决上一次输入的变量还存在的问题* 方案一:if(!empty($_GET)) {unset($_GET);}* 方案二:$http-close();把之前的进程kill,swoole会重新启一个进程,重启会释放内存,把上一次的资源包括变量等全部清空* 方案三:$_SERVER  =  []*/$_SERVER  =  [];if(isset($request->server)) {foreach($request->server as $k => $v) {$_SERVER[strtoupper($k)] = $v;}}if(isset($request->header)) {foreach($request->header as $k => $v) {$_SERVER[strtoupper($k)] = $v;}}$_GET = [];if(isset($request->get)) {foreach($request->get as $k => $v) {$_GET[$k] = $v;}}$_POST = [];if(isset($request->post)) {foreach($request->post as $k => $v) {$_POST[$k] = $v;}}//开启缓冲区ob_start();// 执行应用并响应try {think\Container::get('app', [APP_PATH])->run()->send();}catch (\Exception $e) {// todo}//输出TP当前请求的控制方法//echo "-action-".request()->action().PHP_EOL;//获取缓冲区内容$res = ob_get_contents();ob_end_clean();$response->end($res);//把之前的进程kill,swoole会重新启一个进程,重启会释放内存,把上一次的资源包括变量等全部清空//$http->close();
});
$http->start();

    测试:

    clipboard.png

2.1.2 onWorkerStart事件

//此事件在Worker进程/Task进程启动时发生,这里创建的对象可以在进程生命周期内使用
$http->on('WorkerStart', function(swoole_server $server,  $worker_id) {// 定义应用目录define('APP_PATH', __DIR__ . '/../../../../application/');// 加载框架里面的文件require __DIR__ . '/../../../../thinkphp/base.php';
});
Tips:如果修改了加载框架文件,需要重启: php php_server.php

    onWorkerStart:

    此事件在Worker进程/Task进程启动时发生,这里创建的对象可以在进程生命周期内使用
    在onWorkerStart中加载框架的核心文件后:

  1. 不用每次请求都加载框架核心文件,提高性能
  2. 可以在后续的回调事件中继续使用框架的核心文件或者类库

2.1.3 关于再次请求进程缓存解决方案

    当前worker进程没有结束,所以会保存上一次的资源等。解决上一次输入的变量还存在的问题:

  1. 方案一:if(!empty($_SERVER)) { unset($_SERVER); }
  2. 方案二:$http-close();把之前的进程killswoole会重新启一个进程,重启会释放内存,把上一次的资源包括变量等全部清空(php-cli控制台会提示错误)
  3. 方案三:$_SERVER = []推荐方案

2.1.3 关于ThinkPHP5路由解决方案

当第一次请求后下一次再请求不同的模块或者方法不生效,都是‘第一次’请求 模块/控制器/方法。如下图:

    clipboard.png

    clipboard.png

    clipboard.png

修改 ThinkPHP5框架 Request.php源码位置: /thinkphp/library/think/Request.php

    修改如下:

  1. function path() { }
    //注销判断,不再复用类成员变量$this->path
  2. function pathinfo() { }
    //注销判断,不再复用类成员变量$this->pathinfo
  3. 使其支持pathinfo路由,添加如下代码在function pathinfo() { }
if (isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] != '/') {return ltrim($_SERVER['PATH_INFO'], '/');}

    修改后完整Request.php文件:

    /*** 获取当前请求URL的pathinfo信息(含URL后缀)* @access public* @return string*/public function pathinfo(){if (isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] != '/') {return ltrim($_SERVER['PATH_INFO'], '/');}
//        if (is_null($this->pathinfo)) {if (isset($_GET[$this->config->get('var_pathinfo')])) {// 判断URL里面是否有兼容模式参数$_SERVER['PATH_INFO'] = $_GET[$this->config->get('var_pathinfo')];unset($_GET[$this->config->get('var_pathinfo')]);} elseif ($this->isCli()) {// CLI模式下 index.php module/controller/action/params/...$_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';}// 分析PATHINFO信息if (!isset($_SERVER['PATH_INFO'])) {foreach ($this->config->get('pathinfo_fetch') as $type) {if (!empty($_SERVER[$type])) {$_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ?substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type];break;}}}$this->pathinfo = empty($_SERVER['PATH_INFO']) ? '/' : ltrim($_SERVER['PATH_INFO'], '/');
//        }return $this->pathinfo;}/*** 获取当前请求URL的pathinfo信息(不含URL后缀)* @access public* @return string*/public function path(){//注销判断,不再复用类成员变量$this->path
//        if (is_null($this->path)) {$suffix   = $this->config->get('url_html_suffix');$pathinfo = $this->pathinfo();if (false === $suffix) {// 禁止伪静态访问$this->path = $pathinfo;} elseif ($suffix) {// 去除正常的URL后缀$this->path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);} else {// 允许任何后缀访问$this->path = preg_replace('/\.' . $this->ext() . '$/i', '', $pathinfo);}
//        }return $this->path;}

2.2 面向对象方案

class Http {CONST HOST = "0.0.0.0";CONST PORT = 9911;public $http = null;public function __construct() {$this->http = new swoole_http_server(self::HOST, self::PORT);$this->http->set(['enable_static_handler' => true,'document_root' => "/home/wwwroot/swoole/thinkphp/public/static",'worker_num' => 4,]);$this->http->on("workerstart", [$this, 'onWorkerStart']);$this->http->on("request", [$this, 'onRequest']);$this->http->on("close", [$this, 'onClose']);$this->http->start();}/*** 此事件在Worker进程/Task进程启动时发生,这里创建的对象可以在进程生命周期内使用* 在onWorkerStart中加载框架的核心文件后* 1.不用每次请求都加载框架核心文件,提高性能* 2.可以在后续的回调中继续使用框架的核心文件或者类库** @param $server* @param $worker_id*/public function onWorkerStart($server,  $worker_id) {// 定义应用目录define('APP_PATH', __DIR__ . '/../../../../application/');// 加载框架里面的文件require __DIR__ . '/../../../../thinkphp/base.php';}/*** request回调* 解决上一次输入的变量还存在的问题例:$_SERVER  =  []* @param $request* @param $response*/public function onRequest($request, $response) {$_SERVER  =  [];if(isset($request->server)) {foreach($request->server as $k => $v) {$_SERVER[strtoupper($k)] = $v;}}if(isset($request->header)) {foreach($request->header as $k => $v) {$_SERVER[strtoupper($k)] = $v;}}$_GET = [];if(isset($request->get)) {foreach($request->get as $k => $v) {$_GET[$k] = $v;}}$_POST = [];if(isset($request->post)) {foreach($request->post as $k => $v) {$_POST[$k] = $v;}}$_POST['http_server'] = $this->http;ob_start();// 执行应用并响应try {think\Container::get('app', [APP_PATH])->run()->send();}catch (\Exception $e) {// todo}$res = ob_get_contents();ob_end_clean();$response->end($res);}/*** close* @param $ws* @param $fd*/public function onClose($ws, $fd) {echo "clientid:{$fd}\n";}
}
new Http();

三、分发Task异步任务机制实现

    示例演示:发送验证码

1、优化,将对接第三方的接口放入异步任务中
$_POST['http_server']->task($taskData);
    /*** 发送验证码*/public function index() {$phoneNum = intval($_GET['phone_num']);// tp  inputif(empty($phoneNum)) {return Util::show(config('code.error'), 'error');}// 生成一个随机数$code = rand(1000, 9999);$taskData = ['method' => 'sendSms','data' => ['phone' => $phoneNum,'code' => $code,]];//优化,将对接第三方的接口放入异步任务中$_POST['http_server']->task($taskData);return Util::show(config('code.success'), 'ok');}
} 
2、将 http对象放入预定义 $_POST中,传给调用者
$_POST['http_server'] = $this->http;
    /*** request回调*/public function onRequest($request, $response) {......//将http对象放入预定义$_POST中,传给调用者$_POST['http_server'] = $this->http;ob_start();// 执行应用并响应try {think\Container::get('app', [APP_PATH])->run()->send();}catch (\Exception $e) {// todo}......}
3、Task任务分发
    /*** Task任务分发*/public function onTask($serv, $taskId, $workerId, $data) {// 分发 task 任务机制,让不同的任务 走不同的逻辑$obj = new app\common\lib\task\Task;$method = $data['method'];$flag = $obj->$method($data['data']);return $flag; // 告诉worker}
4、代表的是 swoole里面后续所有 task异步任务都放这里来
class Task {/*** 异步发送 验证码*/public function sendSms($data, $serv) {try {$response = Sms::sendSms($data['phone'], $data['code']);}catch (\Exception $e) {return false;}// 如果发送成功 把验证码记录到redis里面if($response->Code === "OK") {Predis::getInstance()->set(Redis::smsKey($data['phone']), $data['code'], config('redis.out_time'));}else {return false;}return true;}
}

这篇关于Swoole入门到实战(二):进程,内存和协程、Swoole完美支持ThinkPHP5、分发Task异步任务机制实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/993659

相关文章

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使

如何在Java Spring实现异步执行(详细篇)

《如何在JavaSpring实现异步执行(详细篇)》Spring框架通过@Async、Executor等实现异步执行,提升系统性能与响应速度,支持自定义线程池管理并发,本文给大家介绍如何在Sprin... 目录前言1. 使用 @Async 实现异步执行1.1 启用异步执行支持1.2 创建异步方法1.3 调用

java内存泄漏排查过程及解决

《java内存泄漏排查过程及解决》公司某服务内存持续增长,疑似内存泄漏,未触发OOM,排查方法包括检查JVM配置、分析GC执行状态、导出堆内存快照并用IDEAProfiler工具定位大对象及代码... 目录内存泄漏内存问题排查1.查看JVM内存配置2.分析gc是否正常执行3.导出 dump 各种工具分析4.

Spring Boot配置和使用两个数据源的实现步骤

《SpringBoot配置和使用两个数据源的实现步骤》本文详解SpringBoot配置双数据源方法,包含配置文件设置、Bean创建、事务管理器配置及@Qualifier注解使用,强调主数据源标记、代... 目录Spring Boot配置和使用两个数据源技术背景实现步骤1. 配置数据源信息2. 创建数据源Be

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

Spring Boot3.0新特性全面解析与应用实战

《SpringBoot3.0新特性全面解析与应用实战》SpringBoot3.0作为Spring生态系统的一个重要里程碑,带来了众多令人兴奋的新特性和改进,本文将深入解析SpringBoot3.0的... 目录核心变化概览Java版本要求提升迁移至Jakarta EE重要新特性详解1. Native Ima

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三