本文目录导读:

我将为您提供一个完整的Java灰度路由案例,包括核心实现和示例代码。
核心路由实现
package com.example.grayroute;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Random;
/**
* 灰度路由管理器
*/
public class GrayRouteManager {
// 灰度配置存储
private final Map<String, GrayRule> grayRules = new ConcurrentHashMap<>();
/**
* 注册灰度规则
*/
public void registerRule(String serviceName, GrayRule rule) {
grayRules.put(serviceName, rule);
}
/**
* 路由决策
*/
public RouteResult route(String serviceName, RequestContext context) {
GrayRule rule = grayRules.get(serviceName);
if (rule == null) {
return RouteResult.normal();
}
// 判断是否命中灰度
if (rule.match(context)) {
return RouteResult.gray(rule.getGrayVersion());
}
return RouteResult.normal();
}
/**
* 灰度规则定义
*/
public static class GrayRule {
private String grayVersion; // 灰度版本号
private double grayRatio; // 灰度比例 0-100
private String userIdExpression; // 用户ID表达式
private String ipExpression; // IP表达式
private String headerExpression; // Header表达式
public boolean match(RequestContext context) {
// 1. 按比例灰度
if (grayRatio > 0) {
if (isRatioMatch(context)) {
return true;
}
}
// 2. 按用户ID灰度
if (userIdExpression != null && isUserIdMatch(context)) {
return true;
}
// 3. 按IP灰度
if (ipExpression != null && isIpMatch(context)) {
return true;
}
// 4. 按Header灰度
if (headerExpression != null && isHeaderMatch(context)) {
return true;
}
return false;
}
private boolean isRatioMatch(RequestContext context) {
// 使用一致性哈希或随机数进行比例分配
String key = context.getUserId() != null ?
context.getUserId() :
String.valueOf(System.nanoTime());
int hash = Math.abs(key.hashCode() % 1000);
return hash < grayRatio * 10;
}
private boolean isUserIdMatch(RequestContext context) {
if (context.getUserId() == null) return false;
// 简化实现,实际可支持正则表达式
return context.getUserId().matches(userIdExpression);
}
private boolean isIpMatch(RequestContext context) {
if (context.getIp() == null) return false;
// 支持IP段匹配,如: 192.168.1.*
String ip = context.getIp();
if (ipExpression.endsWith("*")) {
String prefix = ipExpression.substring(0, ipExpression.length() - 1);
return ip.startsWith(prefix);
}
return ip.equals(ipExpression);
}
private boolean isHeaderMatch(RequestContext context) {
Map<String, String> headers = context.getHeaders();
String[] parts = headerExpression.split("=");
if (parts.length != 2) return false;
String headerValue = headers.get(parts[0].trim());
return headerValue != null && headerValue.equals(parts[1].trim());
}
// Getter和Setter
public String getGrayVersion() { return grayVersion; }
public void setGrayVersion(String grayVersion) { this.grayVersion = grayVersion; }
public double getGrayRatio() { return grayRatio; }
public void setGrayRatio(double grayRatio) {
if (grayRatio < 0 || grayRatio > 100) {
throw new IllegalArgumentException("灰度比例必须在0-100之间");
}
this.grayRatio = grayRatio;
}
public String getUserIdExpression() { return userIdExpression; }
public void setUserIdExpression(String userIdExpression) { this.userIdExpression = userIdExpression; }
public String getIpExpression() { return ipExpression; }
public void setIpExpression(String ipExpression) { this.ipExpression = ipExpression; }
public String getHeaderExpression() { return headerExpression; }
public void setHeaderExpression(String headerExpression) { this.headerExpression = headerExpression; }
}
}
请求上下文类
package com.example.grayroute;
import java.util.HashMap;
import java.util.Map;
/**
* 请求上下文
*/
public class RequestContext {
private String userId;
private String ip;
private Map<String, String> headers = new HashMap<>();
public RequestContext() {}
public RequestContext(String userId, String ip) {
this.userId = userId;
this.ip = ip;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public void addHeader(String key, String value) {
this.headers.put(key, value);
}
}
路由结果类
package com.example.grayroute;
/**
* 路由结果
*/
public class RouteResult {
private boolean isGray;
private String version;
private RouteResult(boolean isGray, String version) {
this.isGray = isGray;
this.version = version;
}
public static RouteResult normal() {
return new RouteResult(false, null);
}
public static RouteResult gray(String grayVersion) {
return new RouteResult(true, grayVersion);
}
public boolean isGray() {
return isGray;
}
public String getVersion() {
return version;
}
}
灰度路由配置类
package com.example.grayroute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 灰度配置中心(模拟)
*/
public class GrayConfigCenter {
private static final Logger logger = LoggerFactory.getLogger(GrayConfigCenter.class);
private final Map<String, String> configStore = new ConcurrentHashMap<>();
// 单例模式
private static volatile GrayConfigCenter instance;
private GrayConfigCenter() {
// 初始化默认配置
loadDefaultConfig();
}
public static GrayConfigCenter getInstance() {
if (instance == null) {
synchronized (GrayConfigCenter.class) {
if (instance == null) {
instance = new GrayConfigCenter();
}
}
}
return instance;
}
private void loadDefaultConfig() {
// 模拟从配置中心加载配置
// 实际场景可接入 Nacos、Apollo 等配置中心
configStore.put("user-service.gray.ratio", "20");
configStore.put("user-service.gray.version", "v2.0");
configStore.put("user-service.gray.users", "user001,user002");
configStore.put("user-service.gray.ips", "192.168.1.*");
configStore.put("user-service.gray.headers", "X-Gray-Test=true");
}
/**
* 获取灰度配置
*/
public GrayRouteManager.GrayRule getGrayRule(String serviceName) {
GrayRouteManager.GrayRule rule = new GrayRouteManager.GrayRule();
// 从配置中心获取配置
String ratio = configStore.get(serviceName + ".gray.ratio");
String version = configStore.get(serviceName + ".gray.version");
String users = configStore.get(serviceName + ".gray.users");
String ips = configStore.get(serviceName + ".gray.ips");
String headers = configStore.get(serviceName + ".gray.headers");
if (ratio != null) {
rule.setGrayRatio(Double.parseDouble(ratio));
}
if (version != null) {
rule.setGrayVersion(version);
}
if (users != null) {
// 将用户列表转换为匹配表达式
rule.setUserIdExpression(convertToExpression(users));
}
if (ips != null) {
rule.setIpExpression(ips);
}
if (headers != null) {
rule.setHeaderExpression(headers);
}
return rule;
}
/**
* 更新配置(模拟动态刷新)
*/
public void updateConfig(String key, String value) {
configStore.put(key, value);
logger.info("Config updated: {} = {}", key, value);
// 实际场景可发布事件通知各服务刷新
}
private String convertToExpression(String users) {
// 将 "user001,user002" 转换为 "(user001|user002)"
String[] userArray = users.split(",");
return "(" + String.join("|", userArray) + ")";
}
}
服务实现示例
package com.example.grayroute.service;
import com.example.grayroute.*;
import org.springframework.stereotype.Service;
/**
* 用户服务示例
*/
@Service
public class UserService {
private final GrayRouteManager grayRouteManager;
private final GrayConfigCenter configCenter;
public UserService() {
this.grayRouteManager = new GrayRouteManager();
this.configCenter = GrayConfigCenter.getInstance();
initGrayRules();
}
/**
* 初始化灰度规则
*/
private void initGrayRules() {
GrayRouteManager.GrayRule rule = configCenter.getGrayRule("user-service");
grayRouteManager.registerRule("user-service", rule);
}
/**
* 路由到具体版本服务
*/
public String handleRequest(RequestContext context) {
RouteResult result = grayRouteManager.route("user-service", context);
if (result.isGray()) {
// 路由到灰度版本
return callGrayService(context, result.getVersion());
} else {
// 路由到稳定版本
return callStableService(context);
}
}
/**
* 调用稳定版本服务
*/
private String callStableService(RequestContext context) {
// 实际场景可调用远程服务
return "Stable Version Response for user: " + context.getUserId();
}
/**
* 调用灰度版本服务
*/
private String callGrayService(RequestContext context, String grayVersion) {
// 实际场景可调用灰度服务
return "Gray Version " + grayVersion + " Response for user: " + context.getUserId();
}
}
测试用例
package com.example.grayroute;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* 灰度路由测试类
*/
public class GrayRouteTest {
private GrayRouteManager routeManager;
@BeforeEach
public void setUp() {
routeManager = new GrayRouteManager();
// 创建灰度规则
GrayRouteManager.GrayRule rule = new GrayRouteManager.GrayRule();
rule.setGrayVersion("v2.0");
rule.setGrayRatio(30); // 30% 的流量进入灰度
routeManager.registerRule("user-service", rule);
}
@Test
public void testRatioGrayRouting() {
int grayCount = 0;
int total = 1000;
for (int i = 0; i < total; i++) {
RequestContext context = new RequestContext(
"user" + i,
"192.168.1." + (i % 255)
);
RouteResult result = routeManager.route("user-service", context);
if (result.isGray()) {
grayCount++;
}
}
// 统计灰度命中比例
double actualRatio = (grayCount * 100.0) / total;
System.out.println("Expected: 30%, Actual: " + actualRatio + "%");
// 允许一定误差
assertTrue(actualRatio > 25 && actualRatio < 35);
}
@Test
public void testUserSpecificGray() {
// 配置指定用户的灰度规则
GrayRouteManager.GrayRule rule = new GrayRouteManager.GrayRule();
rule.setGrayVersion("v2.0");
rule.setUserIdExpression("(user001|user002)");
routeManager.registerRule("user-service", rule);
// 测试指定用户
RequestContext grayUser = new RequestContext("user001", "192.168.1.100");
assertTrue(routeManager.route("user-service", grayUser).isGray());
// 测试非指定用户
RequestContext normalUser = new RequestContext("user003", "192.168.1.100");
assertFalse(routeManager.route("user-service", normalUser).isGray());
}
@Test
public void testIPBasedGray() {
// 配置指定IP段的灰度规则
GrayRouteManager.GrayRule rule = new GrayRouteManager.GrayRule();
rule.setGrayVersion("v2.0");
rule.setIpExpression("192.168.1.*");
routeManager.registerRule("user-service", rule);
// 测试匹配的IP
RequestContext matchingIP = new RequestContext("user001", "192.168.1.50");
assertTrue(routeManager.route("user-service", matchingIP).isGray());
// 测试不匹配的IP
RequestContext nonMatchingIP = new RequestContext("user001", "10.0.0.1");
assertFalse(routeManager.route("user-service", nonMatchingIP).isGray());
}
@Test
public void testHeaderBasedGray() {
// 配置Header灰度规则
GrayRouteManager.GrayRule rule = new GrayRouteManager.GrayRule();
rule.setGrayVersion("v2.0");
rule.setHeaderExpression("X-Gray-Test=true");
routeManager.registerRule("user-service", rule);
// 测试带Header的请求
RequestContext withHeader = new RequestContext("user001", "192.168.1.100");
withHeader.addHeader("X-Gray-Test", "true");
assertTrue(routeManager.route("user-service", withHeader).isGray());
// 测试不带Header的请求
RequestContext withoutHeader = new RequestContext("user001", "192.168.1.100");
assertFalse(routeManager.route("user-service", withoutHeader).isGray());
}
@Test
public void testMultipleConditions() {
// 组合条件灰度
GrayRouteManager.GrayRule rule = new GrayRouteManager.GrayRule();
rule.setGrayVersion("v2.0");
rule.setGrayRatio(50);
rule.setIpExpression("192.168.*.*");
routeManager.registerRule("user-service", rule);
// 测试命中用户灰度
RequestContext grayUser = new RequestContext("specialUser", "192.168.1.100");
assertTrue(routeManager.route("user-service", grayUser).isGray());
// 测试命中IP灰度
RequestContext grayIP = new RequestContext("normalUser", "192.168.1.101");
assertTrue(routeManager.route("user-service", grayIP).isGray());
// 测试未命中
RequestContext nonGray = new RequestContext("normalUser", "10.0.0.1");
// 根据比例可能命中,这里只进行简单验证
RouteResult result = routeManager.route("user-service", nonGray);
System.out.println("Non-gray result: " + result.isGray());
}
}
Spring Boot集成示例
package com.example.grayroute.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 灰度路由Spring配置
*/
@Configuration
public class GrayRouteConfig implements WebMvcConfigurer {
@Bean
public GrayRouteManager grayRouteManager() {
return new GrayRouteManager();
}
@Bean
public GrayRouteInterceptor grayRouteInterceptor() {
return new GrayRouteInterceptor(grayRouteManager());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(grayRouteInterceptor())
.addPathPatterns("/api/**");
}
/**
* 灰度路由拦截器
*/
public static class GrayRouteInterceptor implements HandlerInterceptor {
private final GrayRouteManager routeManager;
public GrayRouteInterceptor(GrayRouteManager routeManager) {
this.routeManager = routeManager;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 构建请求上下文
RequestContext context = new RequestContext();
context.setUserId(request.getParameter("userId"));
context.setIp(getClientIp(request));
// 复制头部信息
request.getHeaderNames().asIterator()
.forEachRemaining(header -> context.addHeader(
header,
request.getHeader(header)
));
// 路由决策
String serviceName = request.getRequestURI().split("/")[2];
RouteResult result = routeManager.route(serviceName, context);
// 将路由结果放入请求属性
request.setAttribute("grayRouteResult", result);
// 设置灰度版本标识
if (result.isGray()) {
request.setAttribute("X-Gray-Version", result.getVersion());
response.setHeader("X-Gray-Version", result.getVersion());
}
return true;
}
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty()) {
ip = request.getRemoteAddr();
}
return ip;
}
}
}
使用示例
package com.example.grayroute;
/**
* 使用示例
*/
public class UsageExample {
public static void main(String[] args) {
// 1. 初始化路由管理器
GrayRouteManager routeManager = new GrayRouteManager();
// 2. 配置灰度规则
GrayRouteManager.GrayRule rule = new GrayRouteManager.GrayRule();
rule.setGrayVersion("v2.0");
rule.setGrayRatio(20); // 20%流量
rule.setUserIdExpression("(test|beta)");
rule.setIpExpression("192.168.1.*");
rule.setHeaderExpression("X-Group=gray");
routeManager.registerRule("user-service", rule);
// 3. 模拟请求路由
RequestContext userRequest = new RequestContext("testUser", "192.168.1.100");
RouteResult result = routeManager.route("user-service", userRequest);
if (result.isGray()) {
System.out.println("Route to gray version: " + result.getVersion());
} else {
System.out.println("Route to stable version");
}
// 4. 动态更新配置
GrayConfigCenter configCenter = GrayConfigCenter.getInstance();
configCenter.updateConfig("user-service.gray.ratio", "50");
// 5. 新增规则
GrayRouteManager.GrayRule newRule = configCenter.getGrayRule("user-service");
routeManager.registerRule("order-service", newRule);
}
}
- 多维度灰度策略:支持按用户ID、IP、Header、比例等维度组合灰度
- 动态配置:支持运行时动态更新灰度配置
- 版本管理:支持多版本灰度发布
- RESTful API:方便集成和使用
- 测试覆盖:提供完整测试用例验证功能
这个案例可以作为灰度发布的基础框架,实际生产环境中可扩展接入配置中心(如Apollo、Nacos)、负载均衡、服务发现等组件。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播