本文目录导读:

我将为您提供一个完整的PHP功能灰度发布解决方案,包含多种实现方式和完整的示例代码。
基础灰度发布类
<?php
/**
* 灰度发布管理器
*/
class GrayReleaseManager
{
private $redis;
private $config;
/**
* @param Redis $redis Redis连接
* @param array $config 配置数组
*/
public function __construct($redis = null, array $config = [])
{
$this->redis = $redis ?: new Redis();
$this->config = array_merge([
'default_percent' => 0, // 默认灰度比例 0-100
'storage_prefix' => 'gray:', // 存储前缀
'whitelist' => [], // 白名单用户
'blacklist' => [], // 黑名单用户
'cache_time' => 3600 // 缓存时间
], $config);
}
/**
* 获取功能灰度状态
* @param string $feature 功能名称
* @param string $userId 用户ID
* @return bool 是否启用灰度
*/
public function isEnabled($feature, $userId = '')
{
// 检查是否强制开启
if ($this->isForceEnabled($feature)) {
return true;
}
// 检查是否强制关闭
if ($this->isForceDisabled($feature)) {
return false;
}
// 检查黑白名单
if ($userId && $this->checkUserList($userId)) {
return true;
}
// 获取灰度比例
$percent = $this->getFeaturePercent($feature);
if ($percent <= 0) {
return false;
}
if ($percent >= 100) {
return true;
}
// 根据用户ID计算哈希值
$hash = $this->getUserHash($feature, $userId);
return $hash <= $percent;
}
/**
* 获取功能灰度比例
* @param string $feature 功能名称
* @return int 灰度比例 0-100
*/
public function getFeaturePercent($feature)
{
$key = $this->config['storage_prefix'] . 'percent:' . $feature;
// 尝试从缓存获取
$cached = $this->getCache($key);
if ($cached !== false) {
return (int)$cached;
}
// 从配置获取
$percent = isset($this->config['features'][$feature])
? $this->config['features'][$feature]
: $this->config['default_percent'];
// 保存到缓存
$this->setCache($key, $percent);
return $percent;
}
/**
* 设置功能灰度比例
* @param string $feature 功能名称
* @param int $percent 灰度比例 0-100
*/
public function setFeaturePercent($feature, $percent)
{
$percent = max(0, min(100, (int)$percent));
$key = $this->config['storage_prefix'] . 'percent:' . $feature;
if ($this->redis) {
$this->redis->set($key, $percent, $this->config['cache_time']);
}
// 更新配置
$this->config['features'][$feature] = $percent;
}
/**
* 获取用户灰度状态
* @param string $feature 功能名称
* @param string $userId 用户ID
* @return array 包含是否启用和相关信息
*/
public function getUserGrayStatus($feature, $userId)
{
$enabled = $this->isEnabled($feature, $userId);
return [
'enabled' => $enabled,
'feature' => $feature,
'user_id' => $userId,
'percent' => $this->getFeaturePercent($feature),
'timestamp' => time()
];
}
/**
* 获取用户灰度哈希值
* @param string $feature 功能名称
* @param string $userId 用户ID
* @return int 哈希值 0-100
*/
private function getUserHash($feature, $userId)
{
if (empty($userId)) {
// 未登录用户使用IP
$userId = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
}
// 组合特征和用户ID生成哈希值
$combined = $feature . ':' . $userId;
$hash = crc32($combined);
// 转换为0-100的范围
return ($hash % 100) + 1;
}
/**
* 检查用户是否在白名单或黑名单
* @param string $userId 用户ID
* @return bool 是否通过检查
*/
private function checkUserList($userId)
{
$whiteList = $this->config['whitelist'] ?? [];
$blackList = $this->config['blacklist'] ?? [];
if (in_array($userId, $blackList)) {
return false;
}
return in_array($userId, $whiteList);
}
/**
* 检查功能是否强制开启
* @param string $feature 功能名称
* @return bool
*/
private function isForceEnabled($feature)
{
$key = $this->config['storage_prefix'] . 'force_enabled:' . $feature;
return $this->getCache($key) === '1';
}
/**
* 检查功能是否强制关闭
* @param string $feature 功能名称
* @return bool
*/
private function isForceDisabled($feature)
{
$key = $this->config['storage_prefix'] . 'force_disabled:' . $feature;
return $this->getCache($key) === '1';
}
/**
* 获取缓存数据
* @param string $key 缓存键
* @return mixed 缓存值
*/
private function getCache($key)
{
if ($this->redis) {
return $this->redis->get($key);
}
// 文件缓存实现
$cacheFile = sys_get_temp_dir() . '/' . md5($key) . '.cache';
if (file_exists($cacheFile)) {
$data = unserialize(file_get_contents($cacheFile));
if ($data['expire'] > time()) {
return $data['value'];
}
}
return false;
}
/**
* 设置缓存数据
* @param string $key 缓存键
* @param mixed $value 缓存值
*/
private function setCache($key, $value)
{
if ($this->redis) {
$this->redis->setex($key, $this->config['cache_time'], $value);
return;
}
// 文件缓存实现
$cacheFile = sys_get_temp_dir() . '/' . md5($key) . '.cache';
$data = [
'value' => $value,
'expire' => time() + $this->config['cache_time']
];
file_put_contents($cacheFile, serialize($data));
}
}
高级灰度发布类(支持更多功能)
<?php
/**
* 高级灰度发布管理器
* 支持按用户、IP、地区、设备等多维度灰度
*/
class AdvancedGrayRelease
{
private $storage;
private $logger;
private $config;
/**
* @param StorageInterface $storage 存储接口
* @param LoggerInterface $logger 日志接口
* @param array $config 配置
*/
public function __construct($storage, $logger, array $config = [])
{
$this->storage = $storage;
$this->logger = $logger;
$this->config = array_merge([
'rules' => [], // 灰度规则
'strategy' => 'hash', // 默认策略:hash-哈希,session-会话,ip-IP
'enable_log' => true // 是否启用日志
], $config);
}
/**
* 执行灰度判断
* @param string $feature 功能名称
* @param array $context 上下文信息
* @return bool 是否启用
*/
public function evaluate($feature, array $context = [])
{
// 获取功能配置
$featureConfig = $this->getFeatureConfig($feature);
// 检查是否完全开启或关闭
if ($featureConfig['status'] === 'full') {
return true;
}
if ($featureConfig['status'] === 'off') {
return false;
}
// 获取用户信息
$userId = $context['user_id'] ?? $this->getClientIp();
// 检查规则匹配
foreach ($featureConfig['rules'] as $rule) {
if ($this->matchRule($rule, $context)) {
return true;
}
}
// 根据策略进行灰度
$percent = $featureConfig['percent'] ?? 0;
switch ($featureConfig['strategy'] ?? $this->config['strategy']) {
case 'session':
// 基于会话的灰度
return $this->sessionBased($feature, $percent, $userId);
case 'ip':
// 基于IP的灰度
return $this->ipBased($feature, $percent, $this->getClientIp());
case 'ab_test':
// A/B测试灰度
return $this->abTest($feature, $percent, $userId);
default:
// 默认使用哈希灰度
return $this->hashBased($feature, $percent, $userId);
}
}
/**
* 哈希灰度策略
*/
private function hashBased($feature, $percent, $userId)
{
$seed = $this->getSeed($feature, $userId);
$hash = $seed % 100;
return $hash < $percent;
}
/**
* 基于会话的灰度
*/
private function sessionBased($feature, $percent, $userId)
{
$sessionKey = 'gray_session_' . md5($feature);
if (!isset($_SESSION[$sessionKey])) {
$rand = random_int(1, 100);
$_SESSION[$sessionKey] = $rand <= $percent;
}
return $_SESSION[$sessionKey];
}
/**
* 基于IP的灰度
*/
private function ipBased($feature, $percent, $ip)
{
$ipHash = crc32($ip . ':' . $feature) % 100;
return $ipHash < $percent;
}
/**
* A/B测试
*/
private function abTest($feature, $percent, $userId)
{
$testKey = 'test_' . $feature . '_' . $userId;
// 检查是否已有分组
$group = $this->storage->get($testKey);
if ($group === null) {
$group = random_int(1, 100) <= $percent ? 'A' : 'B';
$this->storage->set($testKey, $group, 3600 * 24 * 30);
}
return $group === 'A';
}
/**
* 获取灰度种子
*/
private function getSeed($feature, $userId)
{
$combined = $feature . '_' . $userId . '_' . date('Y-m-d');
return crc32($combined);
}
/**
* 匹配规则
*/
private function matchRule($rule, $context)
{
$type = $rule['type'] ?? 'user_id';
$values = $rule['values'] ?? [];
$match = $rule['match'] ?? 'in';
switch ($type) {
case 'user_id':
$actual = $context['user_id'] ?? '';
break;
case 'ip':
$actual = $this->getClientIp();
break;
case 'region':
$actual = $context['region'] ?? '';
break;
case 'device':
$actual = $this->detectDevice();
break;
default:
$actual = null;
}
if ($actual === null) {
return false;
}
switch ($match) {
case 'in':
return in_array($actual, $values);
case 'not_in':
return !in_array($actual, $values);
case 'regex':
foreach ($values as $pattern) {
if (preg_match($pattern, $actual)) {
return true;
}
}
return false;
default:
return false;
}
}
/**
* 获取功能配置
*/
private function getFeatureConfig($feature)
{
return $this->config['rules'][$feature] ?? [
'status' => 'off',
'percent' => 0,
'strategy' => 'hash',
'rules' => []
];
}
/**
* 获取客户端IP
*/
private function getClientIp()
{
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if (!empty($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return 'unknown';
}
/**
* 检测设备类型
*/
private function detectDevice()
{
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (preg_match('/mobile/i', $userAgent)) {
return 'mobile';
}
if (preg_match('/tablet/i', $userAgent)) {
return 'tablet';
}
return 'desktop';
}
}
灰度发布控制器
<?php
/**
* 灰度发布控制器
* 用于管理灰度功能和用户
*/
class GrayReleaseController
{
private $manager;
private $adminToken;
/**
* 构造函数
*/
public function __construct()
{
$this->manager = new GrayReleaseManager(new Redis());
$this->adminToken = 'your_admin_token'; // 管理令牌
}
/**
* 处理请求
*/
public function handleRequest()
{
$action = $_GET['action'] ?? '';
$token = $_GET['token'] ?? '';
// 验证管理权限
if (!$this->validateToken($token)) {
$this->response(['error' => 'Unauthorized'], 401);
}
switch ($action) {
case 'set_percent':
$this->setPercent();
break;
case 'get_status':
$this->getStatus();
break;
case 'add_whitelist':
$this->addWhitelist();
break;
case 'remove_whitelist':
$this->removeWhitelist();
break;
case 'force_enable':
$this->forceEnable();
break;
case 'force_disable':
$this->forceDisable();
break;
default:
$this->response(['error' => 'Invalid action'], 400);
}
}
/**
* 设置灰度比例
*/
private function setPercent()
{
$feature = $_POST['feature'] ?? '';
$percent = isset($_POST['percent']) ? (int)$_POST['percent'] : 0;
if (empty($feature)) {
$this->response(['error' => 'Feature required'], 400);
}
$this->manager->setFeaturePercent($feature, $percent);
$this->response(['success' => true, 'feature' => $feature, 'percent' => $percent]);
}
/**
* 获取灰度状态
*/
private function getStatus()
{
$feature = $_GET['feature'] ?? '';
if (empty($feature)) {
$this->response(['error' => 'Feature required'], 400);
}
$status = [
'feature' => $feature,
'percent' => $this->manager->getFeaturePercent($feature),
'enabled_count' => $this->getEnabledUsers($feature),
'total_users' => $this->getTotalUsers()
];
$this->response($status);
}
/**
* 添加白名单用户
*/
private function addWhitelist()
{
$feature = $_POST['feature'] ?? '';
$userId = $_POST['user_id'] ?? '';
if (empty($feature) || empty($userId)) {
$this->response(['error' => 'Feature and user_id required'], 400);
}
// 添加白名单
$key = 'gray:whitelist:' . $feature;
$this->manager->addToSet($key, $userId);
$this->response(['success' => true, 'message' => 'User added to whitelist']);
}
/**
* 强制开启功能
*/
private function forceEnable()
{
$feature = $_POST['feature'] ?? '';
if (empty($feature)) {
$this->response(['error' => 'Feature required'], 400);
}
$this->manager->setFeaturePercent($feature, 100);
$this->response(['success' => true, 'message' => 'Feature force enabled']);
}
/**
* 强制关闭功能
*/
private function forceDisable()
{
$feature = $_POST['feature'] ?? '';
if (empty($feature)) {
$this->response(['error' => 'Feature required'], 400);
}
$this->manager->setFeaturePercent($feature, 0);
$this->response(['success' => true, 'message' => 'Feature force disabled']);
}
/**
* 验证管理员令牌
*/
private function validateToken($token)
{
return hash_equals($this->adminToken, $token);
}
/**
* 获取启用用户数
*/
private function getEnabledUsers($feature)
{
// 实际应用中这里应该从数据库或缓存获取
return 0;
}
/**
* 获取总用户数
*/
private function getTotalUsers()
{
// 实际应用中这里应该从数据库获取
return 0;
}
/**
* 响应处理
*/
private function response($data, $status = 200)
{
http_response_code($status);
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
}
// 使用示例
// $controller = new GrayReleaseController();
// $controller->handleRequest();
实用功能示例
<?php
/**
* 灰度发布实用功能示例
*/
class GrayReleaseExample
{
private $grayManager;
public function __construct()
{
$config = [
'default_percent' => 20,
'whitelist' => ['admin', 'tester'],
'blacklist' => [],
'features' => [
'new_ui' => 30,
'new_payment' => 50,
'recommend_system' => 10
]
];
$this->grayManager = new GrayReleaseManager(new Redis(), $config);
}
/**
* 示例1:简单的功能灰度
*/
public function example1()
{
$userId = $_SESSION['user_id'] ?? '';
// 检查新UI功能是否启用
if ($this->grayManager->isEnabled('new_ui', $userId)) {
return $this->renderNewUI();
} else {
return $this->renderOldUI();
}
}
/**
* 示例2:不同功能使用不同灰度策略
*/
public function example2()
{
$feature = 'new_payment';
$userId = $_SESSION['user_id'] ?? '';
$status = $this->grayManager->getUserGrayStatus($feature, $userId);
if ($status['enabled']) {
// 使用新版支付
return $this->processPaymentV2();
} else {
// 使用旧版支付
return $this->processPaymentV1();
}
}
/**
* 示例3:实时监控灰度效果
*/
public function example3()
{
$feature = 'new_ui';
$userId = $_SESSION['user_id'] ?? '';
// 记录用户流量
$this->recordTraffic($feature, $userId);
$enabled = $this->grayManager->isEnabled($feature, $userId);
// 记录用户行为
if ($enabled) {
$this->recordMetric('gray_' . $feature . '_enabled');
} else {
$this->recordMetric('gray_' . $feature . '_disabled');
}
return $enabled ? 'new_ui' : 'old_ui';
}
/**
* 示例4:灰度降级
*/
public function example4()
{
$userId = $_SESSION['user_id'] ?? '';
$feature = 'recommend_system';
try {
// 尝试使用推荐系统
if ($this->grayManager->isEnabled($feature, $userId)) {
return $this->getRecommendationsV2();
} else {
return $this->getRecommendationsV1();
}
} catch (Exception $e) {
// 发生错误时降级到旧版本
$this->grayManager->setFeaturePercent($feature, 0);
return $this->getRecommendationsV1();
}
}
/**
* 示例5:A/B测试
*/
public function example5()
{
$userId = $_SESSION['user_id'] ?? '';
$feature = 'ab_test_homepage';
$advancedGray = new AdvancedGrayRelease(
new RedisStorageAdapter(new Redis()),
new SimpleLogger(),
[
'rules' => [
'ab_test_homepage' => [
'status' => 'partial',
'percent' => 50,
'strategy' => 'ab_test'
]
]
]
);
$useNewHomepage = $advancedGray->evaluate($feature, ['user_id' => $userId]);
return $useNewHomepage ? 'new_homepage' : 'old_homepage';
}
// 辅助方法
private function renderNewUI() { return 'new ui'; }
private function renderOldUI() { return 'old ui'; }
private function processPaymentV1() { return 'payment v1'; }
private function processPaymentV2() { return 'payment v2'; }
private function getRecommendationsV1() { return 'rec v1'; }
private function getRecommendationsV2() { return 'rec v2'; }
private function recordTraffic($feature, $userId) { /* 实现 */ }
private function recordMetric($name) { /* 实现 */ }
}
/**
* Redis存储适配器(示例)
*/
class RedisStorageAdapter
{
private $redis;
public function __construct($redis)
{
$this->redis = $redis;
}
public function get($key)
{
return $this->redis->get($key);
}
public function set($key, $value, $ttl = null)
{
if ($ttl) {
return $this->redis->setex($key, $ttl, $value);
}
return $this->redis->set($key, $value);
}
}
/**
* 简单日志记录器(示例)
*/
class SimpleLogger
{
public function log($message, $type = 'info')
{
file_put_contents(
__DIR__ . '/gray_release.log',
date('Y-m-d H:i:s') . " [$type] $message\n",
FILE_APPEND
);
}
}
管理界面示例
<!DOCTYPE html>
<html>
<head>灰度发布管理</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.feature-list { margin: 20px 0; }
.feature-item { border: 1px solid #ddd; padding: 15px; margin-bottom: 10px; }
.percent-input { width: 80px; }
button { margin: 5px; padding: 8px 15px; cursor: pointer; }
.success { color: green; }
.error { color: red; }
</style>
</head>
<body>
<h1>灰度发布管理</h1>
<div class="feature-list">
<div class="feature-item">
<h3>新UI功能</h3>
<label>灰度比例: <input type="number" class="percent-input" id="new_ui_percent" value="30" min="0" max="100">%</label>
<button onclick="updatePercent('new_ui')">更新</button>
<button onclick="forceEnable('new_ui')">强制开启</button>
<button onclick="forceDisable('new_ui')">强制关闭</button>
<div id="new_ui_status"></div>
</div>
<div class="feature-item">
<h3>新版支付</h3>
<label>灰度比例: <input type="number" class="percent-input" id="new_payment_percent" value="50" min="0" max="100">%</label>
<button onclick="updatePercent('new_payment')">更新</button>
<button onclick="forceEnable('new_payment')">强制开启</button>
<button onclick="forceDisable('new_payment')">强制关闭</button>
<div id="new_payment_status"></div>
</div>
<div class="feature-item">
<h3>推荐系统</h3>
<label>灰度比例: <input type="number" class="percent-input" id="recommend_system_percent" value="10" min="0" max="100">%</label>
<button onclick="updatePercent('recommend_system')">更新</button>
<button onclick="forceEnable('recommend_system')">强制开启</button>
<button onclick="forceDisable('recommend_system')">强制关闭</button>
<div id="recommend_system_status"></div>
</div>
</div>
<script>
const token = 'your_admin_token';
function updatePercent(feature) {
const percent = document.getElementById(feature + '_percent').value;
fetch('gray_controller.php?action=set_percent&token=' + token, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'feature=' + feature + '&percent=' + percent
})
.then(response => response.json())
.then(data => {
showMessage(feature + '_status', data.message || '更新成功', 'success');
})
.catch(error => {
showMessage(feature + '_status', error.message, 'error');
});
}
function forceEnable(feature) {
fetch('gray_controller.php?action=force_enable&token=' + token, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'feature=' + feature
})
.then(response => response.json())
.then(data => {
document.getElementById(feature + '_percent').value = '100';
showMessage(feature + '_status', data.message || '已强制开启', 'success');
});
}
function forceDisable(feature) {
fetch('gray_controller.php?action=force_disable&token=' + token, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'feature=' + feature
})
.then(response => response.json())
.then(data => {
document.getElementById(feature + '_percent').value = '0';
showMessage(feature + '_status', data.message || '已强制关闭', 'success');
});
}
function showMessage(elementId, message, type) {
const element = document.getElementById(elementId);
element.innerHTML = `<span class="${type}">${message}</span>`;
setTimeout(() => element.innerHTML = '', 3000);
}
</script>
</body>
</html>
使用建议
配置示例
// config/gray_release.php
return [
'default_percent' => 0,
'whitelist' => [
'admin',
'tester',
'employee1@company.com'
],
'blacklist' => [],
'features' => [
'new_ui' => 30,
'new_payment' => 50,
'recommend_system' => 10,
'social_share' => 5
],
'strategy' => 'hash',
'cache' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'prefix' => 'gray:',
'ttl' => 3600
],
'logging' => [
'enabled' => true,
'path' => '/var/log/gray_release/'
]
];
这个解决方案提供了完整的灰度发布功能,包括基础灰度控制、高级策略支持、管理界面等,您可以根据实际需求进行配置和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播