PHP项目批量拉黑如何导入名单同步多节点

wen PHP项目 28

本文目录导读:

PHP项目批量拉黑如何导入名单同步多节点

  1. 方案一:Redis + 消息队列(推荐)
  2. 方案二:使用Redis集群(最简单)
  3. 方案三:文件同步 + inotify(适合小规模)
  4. 方案四:数据库 + 轮询(适合低频率)
  5. 各方案对比
  6. 最佳实践建议

针对PHP项目批量拉黑名单同步到多节点,推荐以下几种成熟方案:

Redis + 消息队列(推荐)

批量导入黑名单接口

<?php
// BatchBlacklistController.php
class BatchBlacklistController {
    public function import($request) {
        // 1. 接收批量数据(CSV/Excel/JSON)
        $file = $request->file('blacklist');
        $data = $this->parseFile($file);
        // 2. 数据校验
        $validData = $this->validate($data);
        // 3. 写入Redis Set(原子操作)
        $redis = new Redis();
        $redis->sAddArray('global:blacklist', $validData);
        // 4. 发布消息到队列
        $this->publishSyncMessage([
            'action' => 'batch_add',
            'data' => $validData,
            'timestamp' => time()
        ]);
        return json(['success' => true, 'count' => count($validData)]);
    }
    private function publishSyncMessage($message) {
        // 使用Redis Stream或RabbitMQ
        $redis = new Redis();
        $redis->xAdd('blacklist_sync', '*', $message);
    }
}

多节点同步消费者

<?php
// BlacklistSyncWorker.php
class BlacklistSyncWorker {
    public function consume() {
        $redis = new Redis();
        $redis->connect('redis-master', 6379);
        // 每个节点独立消费
        $group = 'blacklist_sync_group';
        $consumer = 'node_' . gethostname();
        while (true) {
            $messages = $redis->xReadGroup(
                $group, 
                $consumer,
                ['blacklist_sync' => '>'],
                1,  // 每次读取1条
                1000  // 阻塞1秒
            );
            if (!empty($messages)) {
                foreach ($messages as $stream => $msgs) {
                    foreach ($msgs as $id => $msg) {
                        $this->processMessage($msg);
                        $redis->xAck('blacklist_sync', $group, [$id]);
                    }
                }
            }
        }
    }
    private function processMessage($msg) {
        $action = $msg['action'];
        $data = json_decode($msg['data'], true);
        switch ($action) {
            case 'batch_add':
                // 更新本地内存/文件/数据库
                $this->updateLocalBlacklist($data);
                break;
            case 'remove':
                $this->removeFromLocal($data);
                break;
        }
    }
}

使用Redis集群(最简单)

单节点写入,集群自动同步

<?php
// 使用Redis Cluster自动同步
class BlacklistManager {
    private $redis;
    public function __construct() {
        // 连接Redis集群
        $this->redis = new RedisCluster(null, [
            '192.168.1.1:6379',
            '192.168.1.2:6379',
            '192.168.1.3:6379'
        ]);
    }
    public function batchAdd(array $users) {
        // 使用Pipeline批量写入
        $pipe = $this->redis->multi(Redis::PIPELINE);
        foreach ($users as $user) {
            $pipe->sAdd('global:blacklist', $user);
        }
        $pipe->exec();
    }
    public function isBlacklisted($userId) {
        return $this->redis->sIsMember('global:blacklist', $userId);
    }
}

文件同步 + inotify(适合小规模)

主节点生成文件

<?php
// Master节点
class BlacklistFileSync {
    public function exportFile() {
        $data = $this->getAllBlacklistUsers();
        $content = implode("\n", $data);
        // 写入共享存储(NFS/GFS/S3)
        file_put_contents('/shared/blacklist/blacklist_' . date('Ymd_Hi') . '.txt', $content);
        // 生成MD5校验
        file_put_contents('/shared/blacklist/blacklist.md5', md5($content));
    }
}
// 从节点监听
class BlacklistFileWatcher {
    public function watch() {
        // 使用inotify监控文件变化
        $inotify = inotify_init();
        $watch = inotify_add_watch($inotify, '/shared/blacklist', IN_CLOSE_WRITE);
        while (true) {
            $events = inotify_read($inotify);
            foreach ($events as $event) {
                if ($event['name'] === 'blacklist.md5') {
                    $this->reloadBlacklist();
                }
            }
        }
    }
    public function reloadBlacklist() {
        // 重新加载到内存/Redis/APC
        $content = file_get_contents('/shared/blacklist/blacklist_latest.txt');
        $users = explode("\n", $content);
        // 更新到本地内存缓存
        apcu_store('blacklist', $users);
        // 或写入本地Redis
        // $redis->del('local:blacklist');
        // $redis->sAddArray('local:blacklist', $users);
    }
}

数据库 + 轮询(适合低频率)

共享数据库 + 本地缓存

<?php
class BlacklistDatabaseSync {
    // 写入主库
    public function batchAdd(array $users) {
        DB::table('blacklists')->insert($users);
        // 更新同步时间戳
        Cache::put('blacklist_last_update', time());
    }
    // 各节点定时获取增量
    public function syncFromDb() {
        $lastSync = Cache::get('local_blacklist_sync_time', 0);
        $newRecords = DB::table('blacklists')
            ->where('created_at', '>', date('Y-m-d H:i:s', $lastSync))
            ->get();
        if ($newRecords->isNotEmpty()) {
            // 更新本地缓存
            foreach ($newRecords as $record) {
                Redis::sAdd('local:blacklist', $record->user_id);
            }
            Cache::put('local_blacklist_sync_time', time());
        }
    }
}

各方案对比

方案 延迟 复杂度 可靠性 适合场景
Redis + MQ <100ms 大型分布式系统
Redis Cluster <1ms 已有Redis集群
文件同步 1-5s 小规模、非实时
数据库轮询 10-30s 低频率更新

最佳实践建议

  1. 本地缓存+远程同步:各节点保留本地内存+Redis双重缓存
  2. 增量同步:避免全量同步造成性能问题
  3. 健康检查:定期验证各节点黑名单一致性
  4. 降级方案:同步失败时使用本地文件兜底
// 本地缓存 + Redis双写
class BlacklistChecker {
    public function isBlocked($userId) {
        // 先查本地内存(最快)
        if (in_array($userId, $this->localBlacklist)) {
            return true;
        }
        // 再查Redis(次快)
        if ($this->redis->sIsMember('global:blacklist', $userId)) {
            return true;
        }
        // 最后查数据库(兜底)
        return DB::table('blacklists')->where('user_id', $userId)->exists();
    }
}

选择方案时考虑你的系统规模、实时性要求和运维能力,Redis + MQ方案是大多数生产环境的最佳选择。

抱歉,评论功能暂时关闭!