监控阮一峰老师的blog
阮一峰大家基本都认识,很厉害的一个人,经济学博士,文章写得很棒,知识面很广泛,计算机、算法、英语、文采,这是能想到的他的一些标签,他的博客应该算是最受欢迎的博客之一了。
我经常回去看他的博客,但有时候时间长了,再次去看,发现他已经有好几篇新文章了,我就在想,能不能自己写个程序,监控的他博客,当他有新文章的时候,第一时间推送给我。
他的博客中有一个feed,是一个返回xml格式文档的接口,这个接口是最新的文章列表,总共15篇,可以通过监控这个接口中前几篇文章列表的变化来间接的检测他的文章更新,如果有新的文章马上给我的邮箱发送邮件,这样我就可以尽可能早的get到阮老师的最新文章了。
这是设计思路图:
首先就是rss解析了,这个使用php的原生函数simplexml_load_string得到一个解析之后的SimpleXMLElement对象,通过该对象可以很轻松的获取到阮老师更新的前几篇文章。然后和redis中的已发送文章列表集合作对比,如果有新的更新,将更新的信息发送给UDPserver,让UDPserver去发送邮件给用户列表。检测程序循环执行,每10分钟跑一次。
有了思路编码就很快了(这里其实优化了好几次,刚开始的时候思路也没有这么明了,边做边改,当然了,还是要慢慢锻炼,开始之前尽可能多的完善思路)。
使用PHP依赖管理利器——Composer,经常使用PHP的开发者对这个工具应该很熟悉,不熟悉的同学可以点击前面的链接进行脑补,文档是中文的,很好懂。这个小系统将会使用到三个类库:phpmailer
,predis
,workerman
。第一个是一个发送邮件的类库,可以点击这里查看他的相关信息,第二个类库是php对redis接口的封装,这里是他的源码地址,第三个是一个创建UDPserver的类库,这里是他的官方网站。
首先新建一个项目目录,然后进入项目目录安装依赖
<code>mkdir blog-observercd blog-observercomposer require phpmailer/phpmailercomposer require predis/prediscomposer require workerman/workerman</code>
执行完上面的命令之后,会在项目目录blog-observer目录下面看到下面几个文件composer.json
,composer.lock
和文件夹vender
,composer.json中的内容如下,至此依赖的类库安装好了。
<code class="sourceCode json"><span class="fu">{</span> <span class="dt">"require"</span><span class="fu">:</span> <span class="fu">{</span> <span class="dt">"phpmailer/phpmailer"</span><span class="fu">:</span> <span class="st">"^5.2"</span><span class="fu">,</span> <span class="dt">"predis/predis"</span><span class="fu">:</span> <span class="st">"^1.0"</span><span class="fu">,</span> <span class="dt">"workerman/workerman"</span><span class="fu">:</span> <span class="st">"^3.3"</span> <span class="fu">}</span><span class="fu">}</span></code>
下面是主要代码,由于是服务端程序,所以这里设置为daemon进程,我这里UDPserver为udp://127.0.0.1:1234
<code class="sourceCode php">daemonize<span class="ot">();</span><span class="kw">while</span><span class="ot">(</span><span class="dv">1</span><span class="ot">)</span>{ <span class="co">//获取最新的几篇文章,看看是否需要推送</span> <span class="kw">$c</span> = <span class="fu">file_get_contents</span><span class="ot">(</span><span class="kw">XML_URL</span><span class="ot">);</span> <span class="kw">$parse</span> = <span class="er">@</span><span class="fu">simplexml_load_string</span><span class="ot">(</span><span class="kw">$c</span><span class="ot">);</span> <span class="kw">if</span><span class="ot">(</span><span class="kw">$parse</span><span class="ot">)</span> { <span class="kw">$count</span> = <span class="fu">count</span><span class="ot">(</span><span class="kw">$parse</span>->entry<span class="ot">);</span> <span class="kw">$count</span> = <span class="kw">$count</span> > <span class="kw">RECENT_NUM</span> <span class="ot">?</span> <span class="kw">RECENT_NUM</span> <span class="ot">:</span> <span class="kw">$count</span><span class="ot">;</span> <span class="kw">$maynew</span> = <span class="ot">[];</span> <span class="kw">for</span><span class="ot">(</span><span class="kw">$i</span> = <span class="dv">0</span><span class="ot">;</span> <span class="kw">$i</span> < <span class="kw">$count</span><span class="ot">;</span> <span class="kw">$i</span>++<span class="ot">)</span> { <span class="kw">$maynew</span><span class="ot">[</span><span class="kw">$parse</span>->entry<span class="ot">[</span><span class="kw">$i</span><span class="ot">]</span>-><span class="fu">link</span>->attributes<span class="ot">()</span>->href-><span class="fu">__toString</span><span class="ot">()]</span> = <span class="kw">$parse</span>->entry<span class="ot">[</span><span class="kw">$i</span><span class="ot">]</span>->title-><span class="fu">__toString</span><span class="ot">();</span> } <span class="kw">$body</span> = <span class="st">""</span><span class="ot">;</span> <span class="co">//是否推送</span> <span class="kw">foreach</span><span class="ot">(</span><span class="kw">$maynew</span> <span class="kw">as</span> <span class="kw">$url</span> => <span class="kw">$title</span><span class="ot">)</span> { <span class="kw">if</span><span class="ot">(</span><span class="kw">$client</span>->sadd<span class="ot">(</span><span class="kw">SENDED_SET_KEY</span><span class="ot">,</span> <span class="kw">$url</span><span class="ot">))</span> { <span class="co">//send EMAIL</span> <span class="kw">$body</span> .= <span class="st">"<a href='"</span>.<span class="kw">$url</span>.<span class="st">"'>"</span>.<span class="kw">$title</span>.<span class="st">"</a><br>"</span><span class="ot">;</span> } } <span class="kw">if</span><span class="ot">(</span><span class="kw">$body</span><span class="ot">)</span> { <span class="kw">$msg</span> = <span class="ot">[];</span> <span class="kw">$msg</span><span class="ot">[</span><span class="st">'type'</span><span class="ot">]</span> = <span class="dv">1</span><span class="ot">;</span> <span class="kw">$msg</span><span class="ot">[</span><span class="st">'mailbody'</span><span class="ot">]</span> = <span class="kw">$body</span><span class="ot">;</span> <span class="kw">$start</span> = <span class="dv">0</span><span class="ot">;</span> <span class="kw">while</span><span class="ot">(</span><span class="kw">$mailaddrs</span> = <span class="kw">$client</span>->lrange<span class="ot">(</span><span class="kw">EMAIL_LIST_KEY</span> <span class="ot">,</span><span class="kw">$start</span><span class="ot">,</span> <span class="ot">(</span><span class="kw">$start</span> + <span class="kw">EVERY_SEND_NUM</span> -<span class="dv">1</span> <span class="ot">)))</span> { <span class="kw">$msg</span><span class="ot">[</span><span class="st">'mailaddrs'</span><span class="ot">]</span> = <span class="kw">$mailaddrs</span><span class="ot">;</span> <span class="kw">$send_msg</span> = <span class="fu">json_encode</span><span class="ot">(</span><span class="kw">$msg</span><span class="ot">);</span> <span class="fu">socket_sendto</span><span class="ot">(</span><span class="kw">$sock</span><span class="ot">,</span> <span class="kw">$send_msg</span><span class="ot">,</span> <span class="fu">strlen</span><span class="ot">(</span><span class="kw">$send_msg</span><span class="ot">),</span> <span class="dv">0</span><span class="ot">,</span> <span class="st">'127.0.0.1'</span><span class="ot">,</span> <span class="dv">1234</span><span class="ot">);</span> <span class="kw">$start</span> += <span class="kw">EVERY_SEND_NUM</span><span class="ot">;</span> } } } <span class="fu">sleep</span><span class="ot">(</span><span class="kw">GAP_SECONDS</span><span class="ot">);</span>}</code>
有了workerman,可以很方便的实现UDPserver,比自己写来的快得多。
<code class="sourceCode php"><span class="kw">$udp_worker</span> = <span class="kw">new</span> Workerman\Worker<span class="ot">(</span><span class="st">"udp://0.0.0.0:"</span>.<span class="kw">MAIL_UDP_PORT</span><span class="ot">);</span><span class="kw">$udp_worker</span>-><span class="fu">count</span> = <span class="dv">2</span><span class="ot">;</span><span class="kw">$udp_worker</span>->onMessage = <span class="kw">function</span><span class="ot">(</span><span class="kw">$connection</span><span class="ot">,</span> <span class="kw">$data</span><span class="ot">)</span> <span class="kw">use</span> <span class="ot">(</span><span class="kw">$mail</span><span class="ot">)</span>{ <span class="kw">$arr</span> = <span class="fu">json_decode</span><span class="ot">(</span><span class="kw">$data</span><span class="ot">,</span> <span class="kw">true</span><span class="ot">);</span> <span class="kw">switch</span><span class="ot">(</span><span class="kw">$arr</span><span class="ot">[</span><span class="st">'type'</span><span class="ot">])</span> { <span class="co">//发送邮件</span> <span class="kw">case </span><span class="st">'1'</span><span class="ot">:</span> { <span class="kw">$mailaddrs</span> = <span class="kw">$arr</span><span class="ot">[</span><span class="st">'mailaddrs'</span><span class="ot">];</span> <span class="kw">if</span><span class="ot">(</span>!<span class="fu">empty</span><span class="ot">(</span><span class="kw">$mailaddrs</span><span class="ot">)</span> && <span class="kw">$arr</span><span class="ot">[</span><span class="st">'mailbody'</span><span class="ot">])</span> { <span class="kw">foreach</span><span class="ot">(</span><span class="kw">$mailaddrs</span> <span class="kw">as</span> <span class="kw">$to</span><span class="ot">)</span> { <span class="kw">$mail</span>->clearAddresses<span class="ot">();</span> <span class="kw">$mail</span>->AddAddress<span class="ot">(</span><span class="kw">$to</span><span class="ot">);</span> <span class="kw">$mail</span>->Body = <span class="kw">$arr</span><span class="ot">[</span><span class="st">'mailbody'</span><span class="ot">];</span> <span class="kw">if</span><span class="ot">(</span>!<span class="kw">$mail</span>->Send<span class="ot">())</span> { <span class="fu">echo</span> <span class="st">"发送邮件失败:</span><span class="kw">\n</span><span class="st">"</span>.<span class="st">"address:"</span>.<span class="kw">$to</span>.<span class="st">"</span><span class="kw">\n</span><span class="st">"</span><span class="ot">;</span> } } } <span class="kw">break</span><span class="ot">;</span> } <span class="kw">default:</span> <span class="kw">break</span><span class="ot">;</span> }}<span class="ot">;</span>Workerman\Worker::runAll<span class="ot">();</span></code>
好了,至此所有的设计编码工作就完成了,现在启动程序,进程启动之后会议daemon的形式运行,不会随着终端的关闭而停止。
<code>php xmldup.php startphp xmlmail.php</code>
这是一个小系统,当然了还有很多不规范的地方,比如daemon进程一般都会以字母d
结尾,还有就是启动很不方便,要启动两次脚本,哈哈,当然了,这只是自己先来无事玩玩了,要真设计一个完成的系统估计会考虑很多很多的东西,加油吧,继续前进。
这里是github地址:blog-observer,自己试用的时候记得修改邮箱名称和密码。
如果谁也想第一时间获取到最新的阮老师的文章可以给我发邮件,我把你们的邮件地添加到邮件list中,但不保证会发送到,有时候关了电脑程序就停止了~