优化朋友圈
博客自从添加了朋友圈功能,优化就一直没有停止过,但是近2年下来,一直不满意,主要是前台打开页面等待速读太长。太长的原因是因为需要在后台获取目标网站的feed内容后才可以读取,随着友链网站越来越多,等待的时间也会越来越长。
前两天浏览我心向阳的博文《创建一个定时任务刷新订阅缓存》,给我很大的启发。我能否把现有的朋友圈代码分为2个独立的文件,一个文件用来后台获取友链feed并写入redis,另外一个文件用来前台直接读取redis并展示。我现在的朋友圈定的是2小时更新一次,所以后台获取只要提早2分钟也就是在1小时58分钟时获取一次,这样除非是在获取的时间内访问网站,其他时间段基本都能快速打开前台页面,我试了试,比着以前的访问速度真的是有了质的提升。
重新布置朋友圈代码如下:
1、在pix/page目录下也就是朋友圈页面friends.php的同级,建立rsscache.php用来后台获取友链网站的feed内容,在保存到pix/rsscache目录下的同时写入到redis。rsscache.php代码如下:
<?php
// 手动指定 wp-load.php 文件的完整路径,可以是绝对路径,也可以是相对路径,下面一句为例子
require_once('../../../wp-load.php');
// 配置信息
define('TIMEZONE', 'Asia/Shanghai');
define('CACHE_UPDATE_INTERVAL', 2 * 3600); // 缓存更新间隔,单位:秒
define('CACHE_DIR', get_stylesheet_directory(). '/rsscache/');
define('REDIS_HOST', '127.0.0.1');
define('REDIS_PORT', 6379);
date_default_timezone_set(TIMEZONE);
// 引入 SimplePie 类
require_once(ABSPATH. WPINC. '/class-simplepie.php');
// 验证链接格式是否合法
function is_valid_url($url) {
return filter_var($url, FILTER_VALIDATE_URL)!== false;
}
// 封装文件读写操作
function read_file($file_path) {
return file_exists($file_path)? file_get_contents($file_path) : false;
}
function write_file($file_path, $content) {
return file_put_contents($file_path, $content);
}
// 缓存友情链接信息
function get_cached_friend_links() {
$links_key = 'friend_links';
$links = get_transient($links_key);
if (!$links) {
$links = get_bookmarks([
'orderby' => 'name',
'category' => 0,
'order' => 'ASC',
'hide_invisible' => 1,
'categorize' => 0,
'title_li' => '',
'echo' => 0
]);
set_transient($links_key, $links, 3600); // 缓存 1 小时
}
return $links;
}
// 获取 Redis 实例
function get_redis_instance() {
static $redis = null;
if ($redis === null) {
$redis = new Redis();
try {
$redis->connect(REDIS_HOST, REDIS_PORT);
} catch (Exception $e) {
error_log("Redis 连接错误: ". $e->getMessage());
$redis = null;
}
}
return $redis;
}
// 获取 RSS 内容,优化错误处理
function get_rss_content($feed_url) {
$response = wp_safe_remote_get($feed_url, ['sslverify' => true, 'timeout' => 10]);
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
return wp_remote_retrieve_body($response);
}
$response = wp_safe_remote_get($feed_url, ['sslverify' => false, 'timeout' => 10]);
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
return wp_remote_retrieve_body($response);
}
error_log("无法从提供 RSS 的 URL: {$feed_url} 获取 RSS 内容,错误信息: ". print_r($response, true));
return '无法从提供 RSS 的 URL 获取 RSS 内容。';
}
// 处理 RSS 内容,删除指定标签及部分内容
function process_rss_content($rss_content) {
$rss_content = preg_replace('/<category.*?<\/category>/is', '', $rss_content);
$rss_content = preg_replace('/<description.*?<\/description>/is', '', $rss_content);
$rss_content = preg_replace('/<content:encoded.*?<\/content:encoded>/is', '', $rss_content);
preg_match_all('/<item>(.*?)<\/item>/is', $rss_content, $matches);
$new_items = array_slice($matches[0], 0, 5);
$new_items_str = implode('', $new_items);
$rss_content = preg_replace('/(<item>.*<\/item>)/is', $new_items_str, $rss_content);
return $rss_content;
}
// 并行获取 RSS 内容
function parallel_get_rss_content($urls) {
$mh = curl_multi_init();
$handles = [];
$responses = [];
foreach ($urls as $i => $url) {
$handles[$i] = curl_init($url);
curl_setopt_array($handles[$i], [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_TIMEOUT => 10
]);
curl_multi_add_handle($mh, $handles[$i]);
}
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running > 0);
foreach ($handles as $i => $handle) {
$responses[$i] = curl_multi_getcontent($handle);
curl_multi_remove_handle($mh, $handle);
}
curl_multi_close($mh);
return $responses;
}
// 获取并缓存友情链接的最新文章数据
function get_cached_friend_link_rss_data($cache_update_interval, $cache_dir) {
$links = get_cached_friend_links();
$urls = [];
$link_map = [];
$redis = get_redis_instance();
foreach ($links as $link) {
if ($link->link_rss) {
$domain = parse_url($link->link_url, PHP_URL_HOST);
if (!$domain) {
continue;
}
$cache_key = 'rss_'. $domain;
$cache_file = $cache_dir. $domain. '.xml';
$urls[] = $link->link_rss;
$link_map[] = $link;
}
}
if ($urls) {
$responses = parallel_get_rss_content($urls);
foreach ($responses as $i => $rss_content) {
if ($rss_content!== '无法从提供 RSS 的 URL 获取 RSS 内容。') {
$link = $link_map[$i];
$domain = parse_url($link->link_url, PHP_URL_HOST);
$cache_key = 'rss_'. $domain;
$cache_file = $cache_dir. $domain. '.xml';
$processed_rss_content = process_rss_content($rss_content);
write_file($cache_file, $processed_rss_content);
if ($redis) {
$redis->setex($cache_key, $cache_update_interval, $processed_rss_content);
}
}
}
}
}
// 执行缓存更新
get_cached_friend_link_rss_data(CACHE_UPDATE_INTERVAL, CACHE_DIR);
2、friends.php的代码更改为:
3、宝塔后台建立计划任务如下:
沉沦
你这缓存到 redis 是我万万没想到的。友圈数据多了的话占内存啊,后台定时生成 json 文件存储吧,毕竟存储不像内存那么金贵。
似水流年
我加了分批次处理,每次只处理10个网站,并且中间有一秒的间隔,下来网站还没有崩过。
obaby
所有依赖于其他网站的内容,实时获取肯定是影响速度
似水流年
确实是这样。所以现在前端代码和后台获取分开了,后台定时获取并写入redis,随时为前台读取做好准备。
ymz316
写的很详细,来学习代码了😁
似水流年
还得感谢你👍