本文目录导读:

Java蓝绿发布的实现通常依赖于负载均衡器、容器编排工具或云平台的路由能力,配合自动化脚本完成流量切换,以下是标准的实现步骤和核心代码示例:
核心实现原理
┌─────────────┐
│ Load │
│ Balancer │
└──────┬──────┘
│
┌─────────────┼─────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ Blue Env. │ │ Green Env.│ │ 等其他环境 │
│ (当前活跃) │ │ (待发布) │ │ │
└───────────┘ └───────────┘ └───────────┘
基于Nginx+Java的蓝绿发布实现
Nginx配置模板(nginx-blue-green.conf)
upstream backend {
# 蓝环境(当前生产)
server 192.168.1.10:8080 weight=100;
# 绿环境(待切换)
#server 192.168.1.20:8080 weight=0;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 健康检查
health_check interval=5s fails=3 passes=2;
}
# 蓝绿切换接口(受保护)
location /switch-blue-green {
allow 127.0.0.1;
deny all;
content_by_lua_block {
local new_env = ngx.var.arg_env
if new_env == "green" then
-- 切换权重
os.execute("sed -i 's/server 192.168.1.10:8080 weight=100;/server 192.168.1.10:8080 weight=0;/' /etc/nginx/conf.d/backend.conf")
os.execute("sed -i 's/#server 192.168.1.20:8080 weight=0;/server 192.168.1.20:8080 weight=100;/' /etc/nginx/conf.d/backend.conf")
ngx.say("Switched to GREEN environment")
elseif new_env == "blue" then
-- 回滚
os.execute("sed -i 's/server 192.168.1.20:8080 weight=100;/server 192.168.1.20:8080 weight=0;/' /etc/nginx/conf.d/backend.conf")
os.execute("sed -i 's/#server 192.168.1.10:8080 weight=0;/server 192.168.1.10:8080 weight=100;/' /etc/nginx/conf.d/backend.conf")
ngx.say("Switched back to BLUE environment")
end
-- 重载Nginx
os.execute("nginx -s reload")
}
}
}
Java应用配置(application-blue.yml / application-green.yml)
# application-blue.yml
server:
port: 8080
spring:
profiles: blue
datasource:
url: jdbc:mysql://blue-db:3306/app?useSSL=false
# application-green.yml
server:
port: 8080
spring:
profiles: green
datasource:
url: jdbc:mysql://green-db:3306/app?useSSL=false
部署脚本(deploy.sh)
#!/bin/bash
# 蓝绿发布脚本
APP_NAME="my-java-app"
BLUE_PORT=8080
GREEN_PORT=8081
NGINX_CONF="/etc/nginx/conf.d/backend.conf"
# 部署到绿环境
deploy_to_green() {
echo "Deploying to GREEN environment..."
# 停止旧绿环境(如果存在)
docker-compose -f docker-compose-green.yml down
# 拉取新代码并构建
git pull origin main
mvn clean package -DskipTests
# 启动绿环境
docker-compose -f docker-compose-green.yml up -d
# 健康检查
for i in {1..30}; do
if curl -sf http://localhost:$GREEN_PORT/health; then
echo "GREEN environment is healthy"
return 0
fi
sleep 2
done
echo "Health check failed for GREEN environment"
return 1
}
# 切换流量到绿环境
switch_to_green() {
echo "Switching traffic to GREEN..."
# 修改Nginx配置
sed -i 's/server 192.168.1.10:8080 weight=100;/server 192.168.1.10:8080 weight=0;/' $NGINX_CONF
sed -i 's/#server 192.168.1.20:8080 weight=0;/server 192.168.1.20:8080 weight=100;/' $NGINX_CONF
# 平滑重载Nginx
nginx -s reload
echo "Traffic switched to GREEN"
}
# 回滚到蓝环境
rollback_to_blue() {
echo "Rolling back to BLUE..."
sed -i 's/server 192.168.1.20:8080 weight=100;/server 192.168.1.20:8080 weight=0;/' $NGINX_CONF
sed -i 's/#server 192.168.1.10:8080 weight=0;/server 192.168.1.10:8080 weight=100;/' $NGINX_CONF
nginx -s reload
echo "Rolled back to BLUE"
}
# 主流程
if deploy_to_green; then
# 验证新版本
sleep 60 # 缓存预热时间
# 执行流量切换
switch_to_green
# 观察期
sleep 300
# 如果发现问题则自动回滚(可配置)
# if check_problems; then
# rollback_to_blue
# fi
else
echo "Deployment failed, no changes made"
exit 1
fi
基于Kubernetes的蓝绿发布
Service定义(service.yaml)
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
version: blue # 通过标签选择
ports:
- protocol: TCP
port: 80
targetPort: 8080
Deployment蓝绿版本
# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-blue
labels:
app: my-app
version: blue
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: blue
template:
metadata:
labels:
app: my-app
version: blue
spec:
containers:
- name: my-app
image: registry/my-app:1.0.0-blue
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
# green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-green
labels:
app: my-app
version: green
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: green
template:
metadata:
labels:
app: my-app
version: green
spec:
containers:
- name: my-app
image: registry/my-app:2.0.0-green
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
自动化切换脚本(blue-green-switch.sh)
#!/bin/bash
# Kubernetes蓝绿切换
NAMESPACE="production"
SERVICE="my-app-service"
K8S_API="https://k8s-api.example.com:6443"
switch_to_green() {
# 更新Service的selector指向green版本
kubectl patch service $SERVICE -n $NAMESPACE -p '{"spec":{"selector":{"version":"green"}}}'
# 验证切换
if kubectl get pods -n $NAMESPACE -l version=green | grep -q "Running"; then
echo "Successfully switched to GREEN"
else
echo "Switch failed, rolling back..."
kubectl patch service $SERVICE -n $NAMESPACE -p '{"spec":{"selector":{"version":"blue"}}}'
fi
}
switch_to_blue() {
kubectl patch service $SERVICE -n $NAMESPACE -p '{"spec":{"selector":{"version":"blue"}}}'
}
# 执行切换
switch_to_green
基于Spring Cloud Gateway的蓝绿发布
Gateway配置(application.yml)
spring:
cloud:
gateway:
routes:
- id: blue_green_route
uri: lb://my-app
predicates:
- Path=/api/**
filters:
- name: RequestHeaderToRequestUri
args:
headerName: X-Env
# header值为blue或green
# 蓝绿分流逻辑
metadata:
response-timeout: 5000
Java代码实现路由控制
@Component
public class BlueGreenRouteFilter implements GatewayFilter {
@Autowired
private DiscoveryClient discoveryClient;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String currentEnv = determineCurrentEnv();
// 根据当前活跃环境选择服务实例
List<ServiceInstance> instances = discoveryClient.getInstances("my-app");
ServiceInstance target = instances.stream()
.filter(instance -> currentEnv.equals(instance.getMetadata().get("env")))
.findFirst()
.orElseThrow(() -> new RuntimeException("No available instance for env: " + currentEnv));
// 修改请求的目标URL
URI newUri = URI.create(String.format("http://%s:%d%s",
target.getHost(), target.getPort(), exchange.getRequest().getURI().getPath()));
ServerHttpRequest modifiedRequest = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(modifiedRequest).build());
}
private String determineCurrentEnv() {
// 从配置中心或Redis读取当前活跃环境
return System.getProperty("active.env", "blue");
}
}
最佳实践建议
- 数据库兼容性:确保蓝绿环境的数据库Schema兼容,建议使用双向兼容的迁移脚本
- Session管理:使用外部Session存储(如Redis),避免切换时Session丢失
- 渐进式切换:先切换10%流量,观察无问题后再全量切换
- 自动化验证:在切换前执行自动化测试和健康检查
- 回滚机制:如果切换后发现问题,能快速回滚到蓝环境
- 日志监控:实时监控两个环境的错误率和响应时间
常见问题处理
// 处理蓝绿切换时的数据不一致问题
@Component
public class BlueGreenTransactionManager {
@Transactional
public void handleCrossEnvTransaction() {
// 使用分布式事务或最终一致性方案
// 推荐使用Saga模式处理长事务
}
}
蓝绿发布能够显著降低发布风险,但需要配套的自动化运维体系和监控告警机制才能发挥最大价值。