接下來還有待於繼續優化,如向 https://feed43.com/ 那樣,輸入 Web URL 就能生成 RSS Feed,又能根據實際須要本身設定更新時間等。摘自:《花 2 小時擼一個 RSS 生成器》https://mp.weixin.qq.com/s/mRjoKgkq1PoqlVgOw8oRYwphp
今天試着完成如何能夠根據實際須要本身設定更新時間間隔時長。html
因爲咱們使用 xpath 方式去抓取網站的內容,這些網站更新了內容,但它們不會實時告訴你它們更新了;因此「RSS 閱讀器如何作到所謂的的「更新」呢?」前端
要獲取某個訂閱源的文章更新,最基礎而樸素的方法就是定時訪問其 RSS 地址,檢查對應的 XML 文件有無變化。laravel
同時,第二個問題出現了,雖然能夠檢查 RSS 是否有更新,但怎麼去通知咱們的「訂閱者」(如:IFTTT) 呢?git
雖然 Google Reader 已經關閉了,但它留下了一份遺產 ——PubSubHubbub 協議。在 PubSubHubbub 協議下,每當內容發佈者(Publisher)發佈新的內容時,都會主動通知一個被稱爲 Hub 的第三方服務器,Hub 隨即經過發送 HTTP POST 請求的方式,將更新狀況和文章內容「推送」給曾經向其訂閱過該內容源的訂戶(Subscriber),從而真正實現了「即時」更新。github
實踐中,RSS 服務每每扮演着上述三方關係中的訂戶角色,從而再也不須要爲了及時獲取內容反覆刷新訂閱源,而只要等着 Hub 傳來內容源主動通知的更新,「不勞而獲」就好了。這大大下降了成本,也完全消除了更新不及時的問題。至於這一協議中的樞紐——Hub 服務器,Google 則在當年本身搭建了一個,並且至今還在運營;另外,一家名叫 Superfeedr 的公司也公開提供 Hub 服務。promise
須要注意的是,所謂的「實時」只是相對的,通知的發送不可能快過 RSS 服務抓取到訂閱源更新的時間,而咱們已經知道後者每每存在不可避免的時間差。所以,實時推送功能的做用只是提醒咱們不要錯過關心的內容;要真正作到分鐘級的先知先覺,當今媒體生態下恐怕仍是直接瞄準社交網絡更爲靠譜。以上文字內容更多來自:《2018 年主流 RSS 服務選哪家?Feedly、Inoreader 和 NewsBlur 全面橫評》https://sspai.com/post/44420bash
有了 PubSubHubbub 協議來支撐咱們「自動更新」能力,那咱們就能夠完善咱們的代碼。服務器
php artisan make:migration add_interval_to_xpaths_table --table=xpaths
默認間隔時間是 2個小時:網絡
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddIntervalToXpathsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('xpaths', function (Blueprint $table) { $table->integer("interval")->default(2); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('xpaths', function (Blueprint $table) { // }); } }
php artisan migrate
添加時間間隔選擇框:
$form->select('interval', '更新間隔時間')->options( [ 1 => '一個小時', 2 => '兩個小時', 4 => '四個小時', 8 => '八個小時', 12 => '半天', ] );
因爲 Superfeedr 收費,因此使用了這個試試:http://phubb.cweiske.de/
按照要求,須要添加兩個 Link 到 RSS Header:
<link href="{{ url("/feed/$xpath->id") }}" rel="self" type="application/atom+xml"/> <link rel="hub" href="http://phubb.cweiske.de/hub.php" />
接下來咱們就須要根據每一個 RSS 自定義的時間間隔,利用 Laravel 的任務調度功能。
具體參考: https://laravel-china.org/docs/laravel/5.5/scheduling
使用調度器時,只需將如下 Cron 項目添加到服務器。
* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
這個 Cron 會每分鐘調用一次 Laravel 命令調度器。執行 schedule:run 命令時, Laravel 會根據你的調度運行預約任務。
每一個更新時間間隔的 RSS 會比較多,並且都須要網絡請求,因此這裏的採用「隊列任務調度」,並且是以每小時去執行一次:
$schedule->job(new AutoUpdateRss(new EloquentRssRepository()))->hourly();
到了真正核心的地方了!
這裏咱們使用 database
這個隊列驅動,首先須要建立一個數據表來存儲任務。能夠用 queue:table
這個 Artisan
命令來建立這個數據表的遷移。當遷移建立好之後,就能夠用 migrate
這條命令來建立數據表:
php artisan queue:table php artisan migrate
修改 Default Queue Driver
爲 database
:
'default' => env('QUEUE_DRIVER', 'database'),
生成任務類
php artisan make:job AutoUpdateRss
在 AutoUpdateRss 類執行查詢知足條件的 xpaths,實時去觸發 Hub 服務器,告知「訂閱者」該更新了。
<?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Repositories\RssRepositoryContract; class AutoUpdateRss implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private $rssRC; /** * Create a new job instance. * * @return void */ public function __construct(RssRepositoryContract $rssRC) { $this->rssRC = $rssRC; } /** * Execute the job. * * @return void */ public function handle() { $xpaths = $this->rssRC->query(); if (empty($xpaths) || count($xpaths) == 0) { return; } $this->rssRC->update($xpaths); } }
Laravel 的隊列系統介紹,看這: https://laravel-china.org/docs/laravel/5.5/queues
查詢方法
public function query() { $allXpaths = Xpath::all(); $now = Carbon::now(); $xpaths = $allXpaths->filter(function ($value, $key) use ($now) { $diff = $now->diffInHours(Carbon::parse($value->created_at)); return $diff % $value->interval == 0; }); return $xpaths; }
通知 Hub 方法
public function update($xpaths) { $client = new Client(); $requests = function ($xpaths) { $uri = 'http://phubb.cweiske.de/hub.php'; foreach($xpaths as $xpath) { yield new Request('POST', $uri, [ 'form_params' => [ 'hub.mode' => 'publish', 'hub.url' => url("/feed/$xpath->id") ] ] ); } }; $pool = new Pool($client, $requests($xpaths), [ 'concurrency' => 5, 'fulfilled' => function ($response, $index) { // this is delivered each successful response }, 'rejected' => function ($reason, $index) { // this is delivered each failed request }, ]); // Initiate the transfers and create a promise $promise = $pool->promise(); // Force the pool of requests to complete. $promise->wait(); }
這裏咱們使用 Guzzle http://guzzle-cn.readthedocs.io/zh_CN/latest/quickstart.html,利用多線程異步請求,提升效率。
// 安裝 guzzle 插件 composer require guzzlehttp/guzzle
Guzzle 更多的使用,參考:《推薦一個 PHP 網絡請求插件 Guzzle》 https://mp.weixin.qq.com/s/w2I8hUmHu0UgjgbSMPEKpg
萬事俱備,只剩下測試,跑跑效果了,咱們把時間改爲每隔五分鐘,去更新 RSS,咱們仍是利用「IFTTT 鏈接釘釘」的方法,看看效果:
代碼粗糙,但五臟俱全了,這過程主要使用了幾個核心技術和工具:
- Laravel 的任務調度
- Laravel 的隊列
- Guzzle 網絡請求插件
- phubb - PHP PubSubHubbub server
讓咱們的 RSS Feed 有了定時器更新功能,下一步能夠開始試着寫寫前端,作一個網站工具,讓更多的人使用。
具體代碼已同步 github:https://github.com/fanly/lrss
未完待續