首页
网站导航
关于
Search
1
解决Typecho Joe主题访问fastly.jsdelivr.net速度慢的方案 | 快速优化技巧
2,190 阅读
2
解决WSL2内存不释放问题的最佳指南
1,399 阅读
3
如何在 Typecho Joe 主题的文章中增加目录
1,170 阅读
4
GO语言环境的搭建教程 - 完全指南
1,004 阅读
5
如何解决Win11电脑桌面上方显示横线问题 | 窗口11教程
1,004 阅读
默认分类
编程语言
GO语言
PHP
Node
javascript
html
rust
java
Css
Python
资源分享
chrome插件
阅读思考
运维架构
redis
Nginx
linux
memcached
mongodb
mysql
windows
docker
k8s
Mq
apache
CI
Git
swoole
elk
系统设计
thinkPhp
beego
登录
Search
标签搜索
重要
go基础
git 命令
go包
phpstorm
sublime
thinkphp6
mysql问题
软件分享
redis命令
php基础
thinkphp3.2
php第三扩展包
小蚯蚓博客
累计撰写
333
篇文章
累计收到
48
条评论
首页
栏目
默认分类
编程语言
GO语言
PHP
Node
javascript
html
rust
java
Css
Python
资源分享
chrome插件
阅读思考
运维架构
redis
Nginx
linux
memcached
mongodb
mysql
windows
docker
k8s
Mq
apache
CI
Git
swoole
elk
系统设计
thinkPhp
beego
页面
网站导航
关于
搜索到
7
篇与
的结果
2023-12-30
Swoole进程池及其通信详解 | Swoole教程
pool方式启动 https://wiki.swoole.com/#/process/process_pool // 启动2个进程,通过socket方式进行通信,true启动协程 $pool = new Swoole\Process\Pool(2, SWOOLE_IPC_UNIXSOCK,0,true); $pool->on('workerStart',function (Swoole\Process\Pool $pool,int $workerId){ var_dump($workerId); $process = $pool->getProcess(0);// 获取workerId为0的进程 $socket = $process->exportSocket();//获取workerId为0的进程的socket if ($workerId == 0) { var_dump('000'); echo $socket->recv();// 阻塞在这里 $socket->send("hello proc1\n"); echo "proc0 stop\n"; } else { var_dump('111'); // \n作为数据的分隔符,表示结束 $socket->send("hello proc0\n"); echo $socket->recv();// 阻塞在这里 echo "proc1 stop\n"; $pool->shutdown(); } }); $pool->start(); 执行后,将会输出 int(0) int(1) string(3) "string(3) "000" 111" hello proc0 proc0 stop hello proc1 proc1 stop Manage方式启动
2023年12月30日
109 阅读
0 评论
0 点赞
2022-10-13
Swoole毫秒定时器和高性能内存表 - 深度探索与实践
定时器 我们工作中,定时器经常使用的是contab,但是这个只支持秒级别的,所以我们可以通过swoole的毫秒级别的定时器。 下例中: 我们可以通过tick来设置定时器,并且还可以传入参数 使用clear来清除定时器,被清除的定时器就不会执行了 可以使用after来设置多少秒后执行 可以用info方法,获取定时器的信息 可以用clearAll方法来清除所有定时器 use Swoole\Coroutine; use Swoole\Timer; Coroutine\run(function () { // 创建2秒定时器 Timer::tick(2000, function (int $timerId, $param1, $param2) { echo "定时器,定时器ID是:{$timerId},在2000ms后执行;参数1是:{$param1},参数2是:{$param2}", PHP_EOL; Timer::clear($timerId); // 2秒定时器里又创建一个4秒的定时器 Timer::tick(4000, function ($timerId) { echo "定时器里的定时器,定时器ID是:{$timerId},在4000ms后执行", PHP_EOL; Timer::clear($timerId); }); }, "A", 'B'); // A,B是参数,传递给$param1、$param2 // 如果只想执行一次,可以用after Timer::after(3000, function () { echo "通过after触发的定时器,在3000ms后执行", PHP_EOL; }); foreach (Timer::list() as $timerId) { echo "这里是循环", PHP_EOL; print_r(Timer::info($timerId)); } // 如果清除所有的定时器,那么所有定时器都不会执行,因为都被清除了 // Timer::clearAll(); }); 高性能内存表 内存表,可以理解成mysql的数据表 use Swoole\Table; // 实现一个类似redis key $table = new Table(3072); // 内存表增加列 $table->column('id', Table::TYPE_INT); $table->column('name', Table::TYPE_STRING, 60); $table->column('price', Table::TYPE_FLOAT); // 创建内存表。定义好表的结构后,执行 create 向操作系统申请内存,创建表 $table->create(); $server = new Swoole\Server('127.0.0.1', 9502); $server->set([ // 多个进程的负载均衡策略,例如轮询、ip hash、worker进程空闲状态来 'dispatch_mode' => 1, ]); $server->table = $table; $server->on('receive', function ($server, $fd, $reactorId, $data) { $cmd = explode(' ', trim($data)); if ($cmd[0] == 'get') { if (count($cmd) < 2) { $cmd[1] = $fd; } $get_fd = intval($cmd[1]); // 获取一行数据 $info = $server->table->get($get_fd); $server->send($fd, var_export($info, true) . PHP_EOL); } elseif ($cmd[0] == 'set') { // 设置行的数据。Table 使用 key-value 的方式来访问数据 // 返回成功或者失败 $ret = $server->table->set($fd, [ 'price' => rand(1, 100), 'id' => $fd, 'name' => $cmd[1], ]); if ($ret) { $server->send($fd, "OK" . PHP_EOL); } else { $server->send($fd, "ERROR" . PHP_EOL); } } else { $server->send($fd, "COMMAND ERROR" . PHP_EOL); } }); $server->start(); // 然后在telnet里输入:set 1和get 1试下 优点是: 读写速度很快,比如redis都还快;
2022年10月13日
210 阅读
0 评论
0 点赞
2022-10-13
Swoole入门指南 | Swoole基础教程
php的几种运行模式 cgi模式:每次请求过来,开一个进程,执行完成后销毁进程;这就会导致频繁的创建关闭进程。 在nginx下fastcgi:是更快的cgi的意思,是通过php-fpm进程管理管理,可以事先启动很多进程在那等待,避免频繁的创建和关闭cgi进程。 在apapche下是php-mode 还有一个是phpcli:在swoole模式下,就是用这个 优势 常驻内存,可以不需要一直加载框架文件 协程,对于比较慢的io操作啥的,就可以异步不等待
2022年10月13日
212 阅读
0 评论
0 点赞
2022-10-13
利用Swoole编写WebSocket服务 - 高效可靠的Web应用开发
执行以下代码,启动websocket服务 $server = new Swoole\WebSocket\Server('0.0.0.0', 9502); $server->set(['open_websocket_close_frame' => true]); // 连接 $server->on('Open',function($server, $request){ // 这里面可以做用户登录的判断 var_dump($request->fd);// 这个fd相当于连接id,是唯一的 $server->push($request->fd, "hello welcome\n"); }); // 发送消息 $server->on('Message', function ($server, $frame) { if ($frame->opcode == 0x08) { echo "Close frame receive:code {$frame->code} Reason {$frame->reason}"; } else { // 只要用户一直连接,服务端可以一直push消息给客户端 $server->push($frame->fd,"server data is :".$frame->data); } }); // 关闭 $server->on('Close', function ($server, $fd) { var_dump("Client close by:".$fd); }); $server->start(); 客户端可以用js代码测试 var wsServer = 'ws://172.26.211.189:9502'; var websocket = new WebSocket(wsServer); websocket.onopen = function (evt) { console.log("Connected to Websocket server."); } websocket.onclose = function (evt) { console.log("Disconnected"); } websocket.onmessage = function (evt) { console.log('Retrieved data from server:' + evt.data); } websocket.onerror = function (evt, e) { console.log('Error occured:' + evt.data); } // websocket.send("发送消息");
2022年10月13日
183 阅读
0 评论
0 点赞
2022-10-13
Swoole协程的深度解析与使用技巧
创建 go和create方式创建 use Swoole\Coroutine; use function Swoole\Coroutine\Run; // 必须使用run创建协程容器,才能写协程,否则会报错 // 框架中可以直接使用go是因为框架启动的时候已经创建了协程容器 run(function () { // go方式创建协程,php.ini里开启短命名才可以 // [swoole] // swoole.use_shortname = on // Swoole\Coroutine::create等价于go函数 go(function () { Coroutine::sleep(2); // 必须使用协程的sleep,不然会同步阻塞 echo "通过go创建协程", PHP_EOL; }); // 最原始的方式创建协程,创建完成后,会返回协程的id $cid = Coroutine::create(function () { Coroutine::sleep(2); echo "通过Coroutine::create创建协程", PHP_EOL; }); echo "这里是第一个输出的地方", PHP_EOL; }); echo "这里是最后输出的地方", PHP_EOL; Scheduler方式创建 use Swoole\Coroutine\Scheduler; $scheduler = new Scheduler(); $scheduler->set([ 'max_coroutine'=>200, ]); // 添加单个协程 $scheduler->add(function ($a,$b){ // 逻辑 },'参数a','参数b'); // 创建多个协程 $scheduler->parallel(2, function () { // 同样的协程创建两个 }); $scheduler->start();// 执行协程 echo "最后执行这里"; batch方式创建 parallel方式创建 通信 上述例子中的两个协程没有通信,如果要通信,可以用channel进行通信 use Swoole\Coroutine; use Swoole\Coroutine\Channel; use function Swoole\Coroutine\Run; run(function () { $channel = new Channel(1); Coroutine::create(function () use ($channel) { for ($i = 0; $i < 10; $i++) { echo "生产消息的协程{$i}", PHP_EOL; Coroutine::sleep(1.0); $channel->push([ 'rand' => rand(1000, 9999), 'index' => $i, ]); } }); Coroutine::create(function () use ($channel) { while (1) { // 应该会阻塞在这里 $data = $channel->pop(1); if ($data) { echo "接收消息的协程"; print_r($data); } else { echo $channel->errCode === SWOOLE_CHANNEL_TIMEOUT; break; } } }); }); 我们还可以通过waitGroup同步阻塞 use Swoole\Coroutine; use Swoole\Coroutine\WaitGroup; use function Swoole\Coroutine\Run; run(function () { $wg = new WaitGroup(); $result = [];// 共享内存 $wg->add(); Coroutine::create(function () use ($wg, &$result) { Coroutine::sleep(1.0); $result[] = 1; $wg->done(); }); $wg->add(); Coroutine::create(function () use ($wg, &$result) { Coroutine::sleep(1.0); $result[] = 2; $wg->done(); }); $wg->wait(); // 如果计数不为0,则会阻塞这里 print_r($result);// 可以得到两个协程的数据 }); 还可以通过Barrier来简化waitGroup,不需要add和done操作 use Swoole\Coroutine; use Swoole\Coroutine\Barrier; use function Swoole\Coroutine\Run; run(function () { $barrier = Barrier::make(); $result = []; // 共享内存 // 该use还是得use,即使可以不使用 Coroutine::create(function () use ($barrier,&$result) { Coroutine::sleep(1.0); $result[] = 1; }); Coroutine::create(function () use ($barrier, &$result) { Coroutine::sleep(1.0); $result[] = 2; }); Barrier::wait($barrier); // 如果计数不为0,则会阻塞这里 print_r($result); // 可以得到两个协程的数据 }); 异常处理 协程之间是互不干扰的,但是有个协程报错如果不做处理导致程序退出的,会影响到其它协程。 use Swoole\Coroutine; use function Swoole\Coroutine\Run; run(function () { // 协程1 Coroutine::create(function () { a(); // 调用不存在的协程,导出php进程中断,协程2还没执行完成就被退出了 }); Coroutine::create(function (){ Coroutine::sleep(1); echo "协程2"; }); }); 协程挂起 协程可以执行到一半的时候,挂起,等待别的协程去恢复它执行。 当协程执行到yield的是,协程会挂起,然后使用resume方法去恢复它的执行。 我们可以用defer方法,执行到协程容器末尾的时候,最后执行 我们可以通过listCoroutine方法获取所有的协程;
2022年10月13日
197 阅读
0 评论
0 点赞
2022-10-13
精通Swoole的HTTP服务编写 - 深入解析与教程
利用swoole启动一个http服务 $server = new Swoole\Http\Server('0.0.0.0', 9502); $server->set(['enable_coroutine' => true]); $global_var = 0; // 协程 $server->on('Request', function ($request, $response) use (&$global_var) { // 因为浏览器会默认发送一个ico请求 if($request->server['path_info']=='/favicon.ico' || $request->server['request_uri']=='/favicon.ico'){ $response->end(); return; } // 因为是常驻内存,所以该变量不会释放,每次请求都会增加 // 如果叠加这个变量,要注意内存会不会溢出 $global_var += 1; list($controller, $action) = explode('/', trim($request->server['request_uri'],'/')); $response->header('content-Type', 'text/html;charset=utf-8'); $response->end("controller 是 {$controller},action 是 {$action}。请求次数是{$global_var}"); }); $server->start(); 然后执行 php swoole.php 然后会阻塞在这里,常驻内存 通过ps命令可以看到,有1个父进程,4个子进程,每个子进程有一个协程管理器: 常驻内存的好处是: php文件不需要每次去加载。因为我们不会去改框架的代码,所以我们只需要将php框架的部分代码放在最外层,启动的时候加载一次就好了;然后业务代码啥的放在Reqeust事件里面,每次都去加载。 redis、mysql等连接不需要每次去连接 但是需要注意的是,内存有可能会内存溢出。
2022年10月13日
177 阅读
0 评论
0 点赞
2022-10-13
Swoole的TCP服务专家 | 高效的Swoole TCP服务器解决方案
tcp概述 TCP(Transmission Control Protocol)又叫传输控制协议,TCP是面向连接的,并且是一种可靠的协议,在基于TCP进行通信时,通信双方需要建立TCP连接,建立连接需要经过三次握手,握手成功才可以通信。TCP 是解决了 UDP 的顺序和丢包这两个重要的问题,它是会相互建立连接的。 优点: 可靠,稳定。TCP的可靠性体现在传输数据之前,三次握手建立连接(四次挥手断开连接),并且在数据传递时,有确认,窗口,重传,拥塞控制机制,数据传完之后断开连接来节省系统资源。 应用场景: 对网络通信质量有要求时,比如:整个数据要准确无误的传递给对方,这往往对于一些要求可靠的应用,比如HTTP,HTTPS,FTP等传输文件的协议,POP,SMTP等邮件的传输协议,websocket协议等。 swoole tcp服务 入门程序代码 $server = new Swoole\Server('127.0.0.1', 9502); // 启动后会有6个进程:主进程和管理进程,是默认都有的 $server->set([ 'worker_num' => 2, // 2个work进程,用于处理正常的业务 'task_worker_num' => 3, // 2个task进程,用于处理例如发送邮件啥耗时的工作 ]); // 启动后在主进程(master)的主线程回调此函数 $server->on('start', function ($server) { // 在此事件之前 Server 已进行了如下操作 // 1. 启动创建完成 Manager 进程 // 2. 启动创建完成 Worker 子进程 // 3. 监听所有 TCP/UDP/unixSocket 端口,但未开始 Accept 连接和请求 // 4. 监听了定时器 echo 'onStart-> main进程 启动', PHP_EOL; // 只能在 onStart/onWorkerStart 之后获取到 echo "服务器的主进程PID:{$server->master_pid}", PHP_EOL; // 主进程id,相当于 // 只能在 onStart/onWorkerStart 之后获取到 echo "服务器管理进程PID:{$server->manager_pid}", PHP_EOL; // 管理进程id,相当于管理fpm的master进程 echo PHP_EOL; }); // 此事件在 Worker 进程 / Task 进程 启动时发生,这里创建的对象可以在进程生命周期内使用。 // onWorkerStart/onStart 是并发执行的,没有先后顺序,不过好像每次都是onStart在前面 $server->on('WorkerStart', function ($server, int $workerId) { // $workerId:是Worker 进程 id(非进程的 PID) echo "onWorkerStart-> worker进程 启动", PHP_EOL; echo "当前Worker进程的PID:{$server->worker_pid}", PHP_EOL; echo "Worker进程ID:{$workerId},范围是从0开始的", PHP_EOL; // 当前进程是否是 Task 进程 if ($server->taskworker == true) { echo "当前进程类型:当前是task进程", PHP_EOL; } else { echo "当前进程类型:当前是worker进程", PHP_EOL; } echo PHP_EOL; }); // 有新的连接进入时,在 worker 进程中回调,比如:telnet 127.0.0.1 9502 $server->on('Connect', function ($server, $fd, $reactorId) { echo "onConnect->客户端连接", PHP_EOL; echo "文件描述符fd是:{$fd}", PHP_EOL; echo "连接所在的Reactor线程ID是:{$reactorId}", PHP_EOL; echo PHP_EOL; }); $process = new Swoole\Process(function ($process) use ($server) { $socket = $process->exportSocket(); while (true) { $message = "Your data is :" . $socket->recv(); foreach ($server->connections as $conn) { $server->send($conn, $message); } } }, false, 2, 1); // 接收到数据时回调此函数,发生在 worker 进程中被调用 $server->on('Receive', function ($server, $fd, $reactorId, $data) use ($process) { echo "onReceive->服务端 接收消息", PHP_EOL; echo "文件描述符fd是:{$fd}", PHP_EOL; echo "连接所在的Reactor线程ID是:{$reactorId}", PHP_EOL; echo "Worker进程ID:{$server->worker_id}", PHP_EOL; echo "收到的数据内容,可能是文本或者二进制内容:{$data}", PHP_EOL; $server->tick(1000, function () use ($server, $fd) { // $server->send($fd, "hello world"); }); // $socket = $process->exportSocket(); // $socket->send($data); // $server->send($fd,"Server receive-data:{$data}.reactor-is {$reactor_id} \n"); // $clientInfo=$server->getClientInfo($fd); // var dump($clientInfo); if (trim($data) == 'task') { // 向task进程投递任务,具体是哪个进程,系统分配 $server->task("async task coming"); } else { $workerIds = [0, 1, 2, 3]; foreach($workerIds as &$val){ if ($val == $server->worker_id) { // 不能向自己发送消息 unset($val); } } $worker_id = array_rand($workerIds,1); // 该方法,向任意 Worker 进程或者 Task 进程发送消息。在非主进程和管理进程中可调用。 // 收到消息的进程会触发 onPipeMessage 事件 $server->sendMessage("hello task process", $worker_id); } echo PHP_EOL; }); // 在 task 进程内被调用 // worker 进程可以使用 task 函数向 task_worker 进程投递新的任务 // 当前的 Task 进程在调用 onTask 回调函数时会将进程状态切换为忙碌 // 这时将不再接收新的 Task,当 onTask 函数返回时会将进程状态切换为空闲然后继续接收新的 Task。 $server->on('task', function ($server, $task_id, $src_worker_id, $data) { // echo "onTask-> task进程接收到消息",PHP_EOL; echo "执行任务的 task 进程 id:{$task_id}",PHP_EOL; echo "投递任务的 worker 进程 id:{$src_worker_id}",PHP_EOL; echo "任务的数据内容:{$data}",PHP_EOL; echo PHP_EOL; }); // 当工作进程收到由 $server->sendMessage() 发送的 unixSocket 消息时会触发 onPipeMessage 事件 // worker/task 进程都可能会触发 onPipeMessage 事件 $server->on('pipeMessage', function ($server, $src_worker_id, $data) { echo 'onPipeMessage->当工作进程收到由 $server->sendMessage() 发送的消息',PHP_EOL; // 当前进程是否是 Task 进程 if ($server->taskworker == true) { echo "当前进程类型:当前是task进程", PHP_EOL; } else { echo "当前进程类型:当前是worker进程", PHP_EOL; } echo "消息来自的worker或者task进程ID:{$src_worker_id}",PHP_EOL; echo "当前处理的worker或者task进程ID:{$server->worker_id}",PHP_EOL; echo '消息是:', $data, PHP_EOL; echo PHP_EOL; }); $server->start(); 进程 tcp服务启动后,首先会有2个基本的进程:主进程和管理进程,是默认都有的 然后还会有woker进程和task进程,这个需要看下配置: worker_num进程数量:相当于食堂打饭的时候的窗口,如果窗口比较多,就可以同时服务多个人,当然这个进程的数量不是越多越好,要取决于cpu的核数。因为tcp接收事件onReceive是在worker进程中进行的。 task_worker_num进程数量:属于后台进程,相当于食堂的后勤人员,当你点的菜制作起来比较耗时间,可以交给后勤人员慢慢做,就不会占用着窗口。当然任务有没完成,我们可以通过websocket或者轮询的方式跟前台页面进行交互。使用场景有,给用户发短信,导出数据啥的都可以用这个task进程来做。 进程通信 可以通过sendMessage向woker或者task进程进行发送消息,然后worker进程或者task进程里的onPipeMessage事件里就可以收到消息,该事件存在于worker或者task进程里。 可以通过task方法,向task进程发送消息,然后在task进程的onTask事件里就可以收到消息,该事件只会在task进程里。
2022年10月13日
278 阅读
0 评论
0 点赞