作者 邓超

优化同步

1 -<?php  
2 -  
3 -use Model\listsSql;  
4 -  
5 -  
6 -/**  
7 - * 把预热的邮件进行归档处理 减少数量量  
8 - * @author:dc  
9 - * @time 2025/1/17 17:10  
10 - * Class HotMail  
11 - */  
12 -class HotMailArchive {  
13 -  
14 - public function __construct(){  
15 - $this->db = db();  
16 - $this->start();  
17 - }  
18 -  
19 - /**  
20 - * shopk那边的预热邮箱  
21 - * @var array  
22 - */  
23 - private $hotEmail = [];  
24 -  
25 - /**  
26 - * @var \Lib\Db|\Lib\DbPool  
27 - */  
28 - private $db;  
29 -  
30 -  
31 - /**  
32 - * @author:dc  
33 - * @time 2024/7/18 14:04  
34 - */  
35 - private function start(){  
36 - _echo('启动预热邮件归档处理 '.getmypid());  
37 -  
38 -// $fid = [235623,235628,235633,235638,235643,235648,235653,235658,235663,235668,235673,235678,235683,235688,235693,235698,235703,235708,235713,235718,235723,235728,235733,235738,235743,235748,235753,235758,235763,235768,235773,235778,235783,235788,235793,235798,235803,235808,235813,235818,235823,235828,235833,235838,235843,235848,235853,235858,235863,235868,235873,235878,235883,235888,235893,235898,235903,235908,235913,235918,235923,235928,235933,235938,235943,235948,235953,235958,235963,235968,235973,235978,235983,235988,235993,235998,236003,236008,236013,236018];  
39 -// foreach ($fid as $i){  
40 -// $this->move($i);  
41 -// }  
42 -// return 0;  
43 -  
44 - $id = 0;  
45 - while (1){  
46 - $id = $this->run($id);  
47 - if($id === 0){  
48 - break;  
49 - }  
50 - }  
51 -  
52 - }  
53 -  
54 -  
55 - private function run($id):int {  
56 - $floder = $this->db->first(\Model\folderSql::first(['id.>'=>$id]));  
57 -  
58 - if($floder){  
59 - $this->move($floder['id']);  
60 - return $floder['id'];  
61 - }else{  
62 - return 0;  
63 - }  
64 -  
65 -  
66 - }  
67 -  
68 - public function move($fid){  
69 - _echo('正在移动 '.$fid);  
70 - $list = $this->db->all(\Model\listsSql::first(dbWhere(['folder_id'=>$fid,'is_hots'=>1])).'0000');  
71 - if($list){  
72 - $this->db->transaction(function () use ($list){  
73 - foreach ($list as $item){  
74 - try {  
75 - $ret = $this->db->throw()->insert('lists_hot',$item,false);  
76 - if($ret){  
77 - $this->db->delete(listsSql::$table,['id'=>$item['id']]);  
78 - }  
79 - }catch (Throwable $e){  
80 - $this->db->update('lists_hot',$item,dbWhere(['id'=>$item['id']]));  
81 - }  
82 - }  
83 - return true;  
84 - });  
85 - $list = null;  
86 - $this->move($fid);  
87 - }  
88 -  
89 -  
90 - }  
91 -  
92 -  
93 -}  
94 -  
95 -  
96 -include_once "../vendor/autoload.php";  
97 -  
98 -new HotMailArchive();  
99 -  
100 -_echo('执行完成 等待下次执行');  
101 -//  
102 -//swoole_set_process_name('hot-email-run-man');  
103 -//  
104 -//$pm = new Swoole\Process\Manager();  
105 -//  
106 -//$pm->addBatch(3,function (){  
107 -//  
108 -// swoole_set_process_name('hot-email-run');  
109 -//  
110 -// include_once "../vendor/autoload.php";  
111 -//  
112 -// new HotMail();  
113 -//  
114 -// exit();  
115 -//},true);  
116 -//  
117 -//$pm->start();  
118 -  
119 -  
1 -<?php  
2 -  
3 -  
4 -/**  
5 - * 循环本地,验证远程是否存在 不存在则删除本地  
6 - */  
7 -//error_reporting();  
8 -  
9 -use Swoole\Process;  
10 -  
11 -include_once __DIR__."/../vendor/autoload.php";  
12 -  
13 -function start(){  
14 -  
15 - swoole_set_process_name('php-email-sync-list-check');  
16 -  
17 - $id = 0;  
18 -  
19 -// $goNum = 0;  
20 - // 循环阻塞  
21 - while (true){  
22 -  
23 - $id = db()->value('select `id` from `'.\Model\emailSql::$table.'` where `id` > '.$id.' order by `id` asc limit 1');  
24 -  
25 - if($id){  
26 - // 启动一个协程  
27 -// go(function () use ($id,&$goNum){  
28 -// $goNum++;  
29 - // 开始同步  
30 - try {  
31 - sync($id);  
32 - }catch (\Throwable $e){  
33 - echo $e->getMessage();  
34 - }  
35 - \Lib\Log::getInstance()->write();  
36 -  
37 -// co::defer(function () use (&$goNum){  
38 -// $goNum--;  
39 -// });  
40 -// });  
41 -  
42 - }else{  
43 - break;  
44 - }  
45 - }  
46 -  
47 -// while ($goNum>0){  
48 -// co::sleep(1);  
49 -// }  
50 - _echo('结束了');  
51 -}  
52 -  
53 -/**  
54 - * 开始同步, 这里是主要的业务代码  
55 - * @param $email_id  
56 - * @param $worker_id  
57 - * @return int  
58 - * @author:dc  
59 - * @time 2023/3/10 10:19  
60 - */  
61 -function sync($email_id){  
62 -  
63 - $email = db()->first(\Model\emailSql::first($email_id));  
64 - if(!$email || $email['pwd_error']){  
65 -  
66 - // 密码错误,或者超过一个月没有更新的邮箱 清空数据  
67 - if($email['pwd_error'] && $email['updated_at'] && strtotime($email['updated_at']) < (time()-86400*7) ){  
68 - db()->delete(\Model\listsSql::$table,['email_id'=>$email['id']]);  
69 - }  
70 -  
71 - return 0;  
72 - }  
73 -  
74 - // 读取到邮箱中的文件夹  
75 - $folders = db()->all(\Model\folderSql::all($email['id']));  
76 - if(!$folders){  
77 - return 3;  
78 - }  
79 -  
80 -  
81 - $mailServer = new Lib\Mail\Mail($email['email'],base64_decode($email['password']),$email['imap']);  
82 -  
83 - // 登录服务器  
84 - if($mailServer->login()!==1){  
85 - return 2;  
86 - }  
87 -  
88 - $call = function ($email_id,$folder_id,$origin_folder) use ($mailServer){  
89 - _echo('run e '.$email_id.' fn '.$origin_folder);  
90 - // gmail 邮箱 这个是不可选的  
91 - if($origin_folder == '[Gmail]'){  
92 - return;  
93 - }  
94 - // 同步父文件夹  
95 - $mailServer->client->selectFolder($origin_folder);  
96 - $page = 0;  
97 - $db = db();  
98 - while (1){  
99 - $ids = $db->all("select `id`,`uid` from ".\Model\listsSql::$table." where `email_id` = {$email_id} and `folder_id` = {$folder_id} and `udate` < ".strtotime("-1 day")." limit 100 offset ".($page*100));  
100 - $page++;  
101 - if($ids){  
102 - try {  
103 - $result = $mailServer->client->fetch(array_column($ids,'uid'),'UID',true);  
104 - $result = array_column($result,'UID','UID');  
105 - }catch (Throwable $e){  
106 - _echo($e->getMessage());  
107 - return;  
108 - }  
109 -  
110 - foreach ($ids as $id){  
111 - $uid = $id['uid']; $id = $id['id'];  
112 - if(!$result || !isset($result[$uid])){  
113 - // 删除 如果远程没有,就删除本地  
114 - _echo('删除 e '.$email_id.' f '.$folder_id.' u '.$uid.' id '.$id.' d '.$db->delete(\Model\listsSql::$table,['id'=>$id]).' body '.$db->delete(\Model\bodySql::$table,['lists_id'=>$id]));  
115 - }  
116 - }  
117 - }  
118 - // 结束了  
119 - if(!$ids || count($ids) < 100){  
120 - break;  
121 - }  
122 -  
123 - }  
124 -  
125 -  
126 - };  
127 -  
128 -// $folders = list_to_tree($folders);  
129 - foreach ($folders as $folder){  
130 - try {  
131 - $is = true;  
132 - foreach ($folders as $f){  
133 - // 是否存在下级  
134 - if($f['pid'] == $folder['id']){  
135 - $is = false;  
136 - }  
137 - }  
138 -  
139 - if($is) $call($email_id,$folder['id'],$folder['origin_folder']);  
140 -  
141 - }catch (\Throwable $e){  
142 - echo $e->getMessage();  
143 - }  
144 - }  
145 -  
146 -  
147 - $email = null;  
148 - $mailServer = null;  
149 -}  
150 -  
151 -  
152 -if(!function_exists("imap_8bit")){  
153 - echo '请安装imap扩展';  
154 - exit(0);  
155 -}  
156 -  
157 -  
158 -\Co\run(function (){  
159 - start();  
160 -});  
161 -  
162 -  
163 -  
164 -  
165 -  
166 -  
167 -  
168 -  
169 -  
170 -  
@@ -4,7 +4,7 @@ include_once "../vendor/autoload.php"; @@ -4,7 +4,7 @@ include_once "../vendor/autoload.php";
4 4
5 // 这里试试不用多进程模式,用多协程模式 5 // 这里试试不用多进程模式,用多协程模式
6 6
7 - 7 +\Lib\DbPool::$clientNumber = 50;
8 8
9 class SendJob { 9 class SendJob {
10 10
@@ -117,6 +117,12 @@ class SendJob { @@ -117,6 +117,12 @@ class SendJob {
117 * @time 2024/4/10 9:25 117 * @time 2024/4/10 9:25
118 */ 118 */
119 public function go_($list){ 119 public function go_($list){
  120 + // 控制50个协程内
  121 + while ($this->cnum>=50){
  122 + co::sleep(0.5);
  123 + }
  124 +
  125 +
120 // 占用 id 126 // 占用 id
121 if(redis()->add('send_job_run_id_'.$list['id'],$list['id'],600)){ 127 if(redis()->add('send_job_run_id_'.$list['id'],$list['id'],600)){
122 go(function ($data) { 128 go(function ($data) {
@@ -253,6 +259,7 @@ class SendJob { @@ -253,6 +259,7 @@ class SendJob {
253 259
254 _echo('执行任务完成'.$data['id']); 260 _echo('执行任务完成'.$data['id']);
255 261
  262 + db()->close();
256 }); 263 });
257 264
258 },$list); 265 },$list);
@@ -285,6 +292,7 @@ switch ($argv[1]??0){ @@ -285,6 +292,7 @@ switch ($argv[1]??0){
285 292
286 _echo('进程已退出'); 293 _echo('进程已退出');
287 294
  295 + db()->close();
288 }); 296 });
289 297
290 break; 298 break;
@@ -2,54 +2,103 @@ @@ -2,54 +2,103 @@
2 2
3 //error_reporting(); 3 //error_reporting();
4 4
5 -include_once __DIR__."/../vendor/autoload.php";  
6 -  
7 -$runNumber = 1000;  
8 -// 循环阻塞  
9 -while ($runNumber){  
10 - $runNumber--;  
11 - // 需要同步的id  
12 - $id = redis()->lPop('sync_email_lists');  
13 -  
14 - if($id && is_numeric($id)){  
15 -  
16 - // 占用当前的id,占用2小时  
17 - if(redis()->add('just_sync_'.$id,time(),600)){  
18 -  
19 - try{  
20 - // 开始同步  
21 - $email = db()->cache(3600)->first(\Model\emailSql::first($id));  
22 - if($email){  
23 - $sync = new \Service\SyncMail($email);  
24 - // ai邮件只同步2天内的  
25 - $sync->search(  
26 - (new \Lib\Imap\ImapSearch())  
27 - ->dateGt(date('Y-m-d',strtotime("-1 day")))  
28 - );  
29 - $sync->isUidAfter(2)->sync();  
30 -  
31 - $sync = null;  
32 - unset($sync);  
33 - } 5 +use Swoole\Process;
  6 +
  7 +
  8 +
  9 +function start(){
  10 +
  11 +// 删除停止运行的值
  12 +// redis()->delete(SYNC_RUNNING_REDIS_KEY,'email_sync_stop_num');
  13 +
  14 + // 进程管理器
  15 + $pm = new Process\Manager();
  16 +
  17 + // 启动业务进程
  18 + $pm->addBatch(10,function (Process\Pool $pool, int $worker_id){
  19 +
  20 + swoole_set_process_name('php-email-sync-list-'.$worker_id);
  21 +
  22 + include_once __DIR__."/../vendor/autoload.php";
  23 + _echo("业务进程({$worker_id})启动成功");
  24 +
  25 + $goNum = 0;
  26 + // 循环阻塞
  27 + while (true){
34 28
35 - }catch (Throwable $e){  
36 - logs('sync : '.$e->getMessage()); 29 + while ($goNum > 50){
  30 + co::sleep(0.5);
  31 + continue;
  32 + }
  33 + // 需要同步的id
  34 + $id = redis()->lPop('sync_email_lists');
  35 +
  36 + if($id && is_numeric($id)){
  37 +
  38 + // 占用当前的id,占用2小时
  39 + if(redis()->add('just_sync_'.$id,time(),600)){
  40 +// redis()->set('sync_my_pid:'.getmypid(),time(),86400);
  41 + // 启动一个协程
  42 + go(function () use ($id,&$goNum){
  43 + $goNum++;
  44 + try{
  45 + // 开始同步
  46 + $email = db()->cache(3600)->first(\Model\emailSql::first($id));
  47 + if($email){
  48 + $sync = new \Service\SyncMail($email);
  49 + // ai邮件只同步2天内的
  50 + $sync->search(
  51 + (new \Lib\Imap\ImapSearch())
  52 + ->dateGt(date('Y-m-d',strtotime("-1 day")))
  53 + );
  54 + $sync->isUidAfter(2)->sync();
  55 +
  56 + $sync = null;
  57 + unset($sync);
  58 + }
  59 +
  60 + }catch (Throwable $e){
  61 + logs('sync : '.$e->getMessage());
  62 + }
  63 +
  64 +
  65 + // 协程完成后执行的函数
  66 + co::defer(function () use ($id,&$goNum){
  67 + $goNum--;
  68 + // 30秒后 消除占用
  69 + redis()->expire('just_sync_'.$id,120);
  70 + // 写入日志
  71 + \Lib\Log::getInstance()->write();
  72 + // 手动释放
  73 + db()->close();
  74 + });
  75 +
  76 + });
  77 + }
37 } 78 }
38 79
39 - // 30秒后 消除占用  
40 - redis()->expire('just_sync_'.$id,120); 80 + //每次都暂停1秒,防止同一时间启动太多的任务
  81 + co::sleep(1);
41 82
42 - // 写入日志  
43 - \Lib\Log::getInstance()->write();  
44 } 83 }
45 - }else{  
46 - break;  
47 - } 84 +
  85 + return 0;
  86 +
  87 + },true);
  88 +
  89 +
  90 + // 启动管理器
  91 + $pm->start();
  92 +
48 } 93 }
49 94
50 95
51 96
52 97
  98 +start();
  99 +
  100 +
  101 +
53 102
54 103
55 104
@@ -5,6 +5,8 @@ include_once __DIR__."/../vendor/autoload.php"; @@ -5,6 +5,8 @@ include_once __DIR__."/../vendor/autoload.php";
5 5
6 swoole_set_process_name('php-email-sync-list-my'); 6 swoole_set_process_name('php-email-sync-list-my');
7 7
  8 +\Lib\DbPool::$clientNumber = 600;
  9 +
8 \Co\run(function (){ 10 \Co\run(function (){
9 $goNum = 0; 11 $goNum = 0;
10 while (1){ 12 while (1){
@@ -36,6 +38,7 @@ swoole_set_process_name('php-email-sync-list-my'); @@ -36,6 +38,7 @@ swoole_set_process_name('php-email-sync-list-my');
36 38
37 \Lib\Log::getInstance()->write(); 39 \Lib\Log::getInstance()->write();
38 40
  41 + db()->close();
39 }); 42 });
40 },$id); 43 },$id);
41 44
@@ -6,7 +6,7 @@ use Swoole\Database\PDOConfig; @@ -6,7 +6,7 @@ use Swoole\Database\PDOConfig;
6 use Swoole\Database\PDOPool; 6 use Swoole\Database\PDOPool;
7 7
8 /** 8 /**
9 - * db 池 9 + * db 池 不主动释放连接
10 * @author:dc 10 * @author:dc
11 * @time 2023/2/13 15:03 11 * @time 2023/2/13 15:03
12 * Class DbPool 12 * Class DbPool
@@ -21,6 +21,17 @@ class DbPool { @@ -21,6 +21,17 @@ class DbPool {
21 */ 21 */
22 static $pool = null; 22 static $pool = null;
23 23
  24 + /**
  25 + * 获取到的连接
  26 + * @var array
  27 + */
  28 + public static $clientAll = [];
  29 +
  30 + /**
  31 + * 连接的数量
  32 + * @var int
  33 + */
  34 + public static $clientNumber = 1024;
24 35
25 /** 36 /**
26 * 实例 37 * 实例
@@ -44,21 +55,26 @@ class DbPool { @@ -44,21 +55,26 @@ class DbPool {
44 \PDO::ATTR_TIMEOUT => 30, 55 \PDO::ATTR_TIMEOUT => 30,
45 ]); 56 ]);
46 57
47 - static::$pool = new PDOPool($pdoconfig,1024); 58 + static::$pool = new PDOPool($pdoconfig,self::$clientNumber);
48 } 59 }
49 60
50 - // 获取链接  
51 - $this->client = static::$pool->get();  
52 61
53 } 62 }
54 63
  64 + public function getClient()
  65 + {
  66 + $id = \co::getCid();
  67 + if(empty(static::$clientAll[$id])){
  68 + static::$clientAll[$id] = self::$pool->get();
  69 + }
  70 + }
55 71
56 72
57 /** 73 /**
58 * 结束 74 * 结束
59 */ 75 */
60 public function __destruct(){ 76 public function __destruct(){
61 - $this->close(); 77 +// $this->close();
62 } 78 }
63 79
64 /** 80 /**
@@ -67,8 +83,11 @@ class DbPool { @@ -67,8 +83,11 @@ class DbPool {
67 * @time 2024/5/30 10:30 83 * @time 2024/5/30 10:30
68 */ 84 */
69 public function close(){ 85 public function close(){
70 - self::$pool->put($this->client);  
71 - $this->client = null; 86 + $id = \co::getCid();
  87 + if (isset(static::$clientAll[$id])){
  88 + self::$pool->put(static::$clientAll[$id]);
  89 + }
  90 + unset(static::$clientAll[$id]);
72 } 91 }
73 92
74 93