在前文实现的超时、熔断、限流等能力基础上,我们进一步为 Sidecar 增加ACL(访问控制列表) 能力,实现对接口的精细化权限管控 —— 只有符合权限规则的请求才能通过 Sidecar 转发,从流量入口层拦截非法访问,完全不侵入业务代码。
核心设计思路
- ACL 规则定义:基于「请求路径 + 客户端 IP/Token」的二维权限控制,支持白名单 / 黑名单模式;
- 中间件化实现:将 ACL 控制封装为 Sidecar 的中间件,可灵活挂载到入口 / 出口流量链路;
- 规则可配置:支持静态配置(演示)和动态更新(扩展),贴合真实 Service Mesh 的配置模式;
- 权限校验时机:入口流量在限流后、转发前校验,出口流量在熔断前校验(可选)。
以下是包含 ACL 接口权限控制、超时、熔断、限流、动态过载保护 的完整 Sidecar 代理代码,附带详细注释和可直接运行的验证方案。
一、完整代码结构
service-mesh-demo/
├── app/ # 业务应用
│ └── main.go
└── sidecar/ # 增强版Sidecar(含ACL)
└── main.go
二、业务应用代码(app/main.go)
运行
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
// 业务应用:提供/hello、/api/user、/health接口,模拟真实业务服务
func main() {
// 1. 核心业务接口
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "default"
}
// 调用外部服务(走Sidecar出口代理)
resp, err := http.Get("http://127.0.0.1:8001/proxy/ext-service")
if err != nil {
fmt.Fprintf(w, "hello %s, call ext service fail: %v", name, err)
return
}
defer resp.Body.Close()
extData, _ := ioutil.ReadAll(resp.Body)
fmt.Fprintf(w, "hello %s, ext service response: %s", name, extData)
})
// 2. API前缀接口(演示ACL路径前缀匹配)
http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"code":200,"msg":"success","data":{"user_id":"1001","name":"test"}}`)
})
// 3. 健康检查接口
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "ok")
})
// 业务应用仅监听本地端口,所有外部请求走Sidecar
fmt.Println("业务应用启动,监听 127.0.0.1:8080")
if err := http.ListenAndServe("127.0.0.1:8080", nil); err != nil {
panic(err)
}
}
三、Sidecar 代理完整代码(sidecar/main.go)
运行
package main
import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/afex/hystrix-go/hystrix"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/mem"
"golang.org/x/time/rate"
)
// ========== 1. ACL核心定义 ==========
// ACLRule 单条ACL规则(支持路径前缀、CIDR IP、HTTP方法、Token校验)
type ACLRule struct {
Path string `json:"path"` // 受控路径(精确:/hello | 前缀:/api/*)
Methods []string `json:"methods"` // 允许的HTTP方法(GET/POST/*)
AllowIPs []string `json:"allow_ips"` // 允许的IP列表(精确/ CIDR/ *)
AllowTokens []string `json:"allow_tokens"` // 允许的Token列表(*表示无需Token)
AllowRoles []string `json:"allow_roles"` // 预留:基于角色的权限控制
Enabled bool `json:"enabled"` // 规则是否启用
}
// ACLManager ACL管理器(线程安全)
type ACLManager struct {
rules map[string]ACLRule
mu sync.RWMutex
}
// 全局ACL管理器实例
var aclManager = &ACLManager{
rules: make(map[string]ACLRule),
}
// InitACL 初始化ACL规则
func (m *ACLManager) InitACL() {
m.mu.Lock()
defer m.mu.Unlock()
m.rules = map[string]ACLRule{
// 1. /hello接口:IP+Token双重校验
"/hello": {
Path: "/hello",
Methods: []string{"GET", "POST"},
AllowIPs: []string{"127.0.0.1", "192.168.1.0/24"}, // CIDR网段
AllowTokens: []string{"valid-token-123", "admin-token"},
AllowRoles: []string{"*"},
Enabled: true,
},
// 2. /api/*接口:仅内网IP,无需Token
"/api/*": {
Path: "/api/*",
Methods: []string{"*"},
AllowIPs: []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}, // 内网网段
AllowTokens: []string{"*"},
AllowRoles: []string{"*"},
Enabled: true,
},
// 3. 健康检查接口:完全开放
"/health": {
Path: "/health",
Methods: []string{"*"},
AllowIPs: []string{"*"},
AllowTokens: []string{"*"},
AllowRoles: []string{"*"},
Enabled: true,
},
}
fmt.Println("ACL规则初始化完成,当前规则数:", len(m.rules))
}
// UpdateRule 动态更新ACL规则
func (m *ACLManager) UpdateRule(rule ACLRule) {
m.mu.Lock()
defer m.mu.Unlock()
m.rules[rule.Path] = rule
fmt.Printf("ACL规则已更新:%+v\n", rule)
}
// matchPath 路径匹配(支持精确/前缀)
func matchPath(requestPath string, rulePath string) bool {
// 精确匹配
if requestPath == rulePath {
return true
}
// 前缀匹配(规则以/*结尾)
if strings.HasSuffix(rulePath, "/*") {
prefix := strings.TrimSuffix(rulePath, "/*")
return strings.HasPrefix(requestPath, prefix)
}
return false
}
// isIPInCIDR IP匹配(支持精确/前缀/CIDR)
func isIPInCIDR(ipStr string, cidrStr string) bool {
if cidrStr == "*" {
return true
}
// 处理前缀匹配(如192.168.1.)
if !strings.Contains(cidrStr, "/") {
return strings.HasPrefix(ipStr, cidrStr)
}
// 处理CIDR网段(如192.168.1.0/24)
_, cidr, err := net.ParseCIDR(cidrStr)
if err != nil {
return false
}
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
return cidr.Contains(ip)
}
// getClientIP 提取客户端真实IP(兼容反向代理/IPv4/IPv6)
func getClientIP(r *http.Request) string {
// 优先从X-Forwarded-For获取(反向代理场景)
xff := r.Header.Get("X-Forwarded-For")
if xff != "" {
parts := strings.Split(xff, ",")
if len(parts) > 0 && strings.TrimSpace(parts[0]) != "" {
return strings.TrimSpace(parts[0])
}
}
// 其次从X-Real-IP获取
xri := r.Header.Get("X-Real-IP")
if xri != "" {
return xri
}
// 最后从RemoteAddr提取
remoteAddr := r.RemoteAddr
if strings.Contains(remoteAddr, ":") {
// 处理IPv6 [::1]:54321 格式
if remoteAddr[0] == '[' {
idx := strings.Index(remoteAddr, "]")
if idx != -1 {
return remoteAddr[1:idx]
}
}
// 处理IPv4 127.0.0.1:54321 格式
idx := strings.LastIndex(remoteAddr, ":")
if idx != -1 {
return remoteAddr[:idx]
}
}
return remoteAddr
}
// CheckPermission 校验请求权限(核心ACL逻辑)
func (m *ACLManager) CheckPermission(r *http.Request) (bool, string) {
m.mu.RLock()
defer m.mu.RUnlock()
// 1. 提取请求信息
requestPath := r.URL.Path
requestMethod := r.Method
clientIP := getClientIP(r)
clientToken := r.Header.Get("X-Auth-Token")
// 2. 匹配路径规则
var matchedRule *ACLRule
for _, rule := range m.rules {
if matchPath(requestPath, rule.Path) {
matchedRule = &rule
break
}
}
if matchedRule == nil {
return false, fmt.Sprintf("接口%s无访问权限(未配置ACL规则)", requestPath)
}
rule := *matchedRule
if !rule.Enabled {
return true, fmt.Sprintf("接口%s ACL规则未启用,放行", requestPath)
}
// 3. 校验HTTP方法
methodAllowed := false
for _, allowMethod := range rule.Methods {
if allowMethod == "*" || requestMethod == allowMethod {
methodAllowed = true
break
}
}
if !methodAllowed {
return false, fmt.Sprintf("HTTP方法%s不允许访问%s接口", requestMethod, requestPath)
}
// 4. 校验IP权限
ipAllowed := false
for _, allowIP := range rule.AllowIPs {
if isIPInCIDR(clientIP, allowIP) {
ipAllowed = true
break
}
}
if !ipAllowed {
return false, fmt.Sprintf("IP%s无访问%s接口的权限(不在允许列表)", clientIP, requestPath)
}
// 5. 校验Token权限
tokenAllowed := false
for _, allowToken := range rule.AllowTokens {
if allowToken == "*" || clientToken == allowToken {
tokenAllowed = true
break
}
}
if !tokenAllowed {
return false, fmt.Sprintf("Token无效,无访问%s接口的权限", requestPath)
}
return true, "权限校验通过"
}
// logACLAudit ACL审计日志(记录到文件+控制台)
func logACLAudit(allowed bool, r *http.Request, msg string) {
clientIP := getClientIP(r)
clientToken := r.Header.Get("X-Auth-Token")
logEntry := fmt.Sprintf("[%s] ACL %s | Method=%s | Path=%s | IP=%s | Token=%s | Msg=%s",
time.Now().Format("2006-01-02 15:04:05"),
map[bool]string{true: "ALLOW", false: "DENY"}[allowed],
r.Method,
r.URL.Path,
clientIP,
clientToken,
msg)
// 1. 打印到控制台
fmt.Println(logEntry)
// 2. 写入审计日志文件
f, err := os.OpenFile("acl-audit.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("写入ACL审计日志失败:%v\n", err)
return
}
defer f.Close()
f.WriteString(logEntry + "\n")
}
// ========== 2. 流量治理配置 ==========
// 限流配置
var limiter = rate.NewLimiter(rate.Limit(10), 5) // 默认10 QPS,突发5个
var currentLimit atomic.Value // 动态限流阈值(原子操作)
// 熔断配置
const (
circuitName = "ext-service-circuit"
timeoutMs = 3000 // 超时时间3秒
maxConcurrent = 100 // 最大并发
errorThreshold = 50 // 错误率阈值50%
requestVolume = 20 // 触发熔断最小请求数
sleepWindowMs = 5000 // 熔断休眠5秒
)
// 动态过载保护配置
const (
cpuThreshold = 80.0 // CPU阈值80%
memThreshold = 80.0 // 内存阈值80%
lowQpsLimit = 2 // 过载时限流2 QPS
normalQpsLimit = 10 // 正常时限流10 QPS
checkInterval = 5 * time.Second // 系统监控间隔
)
// ========== 3. 初始化 ==========
func init() {
// 1. 初始化ACL规则
aclManager.InitACL()
// 2. 初始化熔断器
hystrix.ConfigureCommand(circuitName, hystrix.CommandConfig{
Timeout: timeoutMs,
MaxConcurrentRequests: maxConcurrent,
ErrorPercentThreshold: errorThreshold,
RequestVolumeThreshold: requestVolume,
SleepWindow: sleepWindowMs,
})
// 3. 初始化限流阈值
currentLimit.Store(rate.Limit(normalQpsLimit))
// 4. 启动动态过载监控
go monitorSystemAndAdjustLimit()
}
// ========== 4. 中间件 ==========
// aclMiddleware ACL权限校验中间件
func aclMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 1. ACL权限校验
allowed, msg := aclManager.CheckPermission(r)
// 2. 记录审计日志
logACLAudit(allowed, r, msg)
// 3. 权限拒绝
if !allowed {
http.Error(w, msg, http.StatusForbidden)
return
}
// 4. 权限通过,放行
next(w, r)
}
}
// rateLimitMiddleware 限流中间件
func rateLimitMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 获取动态限流阈值
limit := currentLimit.Load().(rate.Limit)
limiter.SetLimit(limit)
// 令牌桶限流
if !limiter.Allow() {
errMsg := "too many requests (rate limit)"
http.Error(w, errMsg, http.StatusTooManyRequests)
fmt.Printf("[%s] 限流触发:当前QPS阈值=%.1f | Path=%s\n",
time.Now().Format("2006-01-02 15:04:05"), limit, r.URL.Path)
return
}
// 放行
next(w, r)
}
}
// circuitBreakerMiddleware 熔断+超时中间件
func circuitBreakerMiddleware(targetURL string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := hystrix.Do(circuitName, func() error {
// 1. 超时控制
ctx, cancel := context.WithTimeout(r.Context(), time.Duration(timeoutMs)*time.Millisecond)
defer cancel()
// 2. 构建请求
req, err := http.NewRequestWithContext(ctx, r.Method, targetURL, r.Body)
if err != nil {
return err
}
req.Header = r.Header.Clone()
// 3. 发起请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 4. 转发响应
for k, v := range resp.Header {
w.Header()[k] = v
}
w.WriteHeader(resp.StatusCode)
httputil.NewResponseCopier(w, resp.Body).WriteTo(w)
return nil
}, func(err error) error {
// 降级逻辑
errMsg := fmt.Sprintf("external service unavailable: %v", err)
fmt.Printf("[%s] 出口流量熔断/超时:%s | Target=%s\n",
time.Now().Format("2006-01-02 15:04:05"), errMsg, targetURL)
http.Error(w, "external service unavailable (circuit breaker)", http.StatusServiceUnavailable)
return nil
})
if err != nil {
http.Error(w, "internal server error", http.StatusInternalServerError)
fmt.Printf("[%s] 熔断处理异常:%v\n", time.Now().Format("2006-01-02 15:04:05"), err)
}
}
}
// ========== 5. 动态过载保护 ==========
func monitorSystemAndAdjustLimit() {
for {
// 1. 获取CPU使用率
cpuPercent, err := cpu.Percent(1*time.Second, false)
if err != nil {
fmt.Printf("[%s] 获取CPU使用率失败:%v\n", time.Now().Format("2006-01-02 15:04:05"), err)
time.Sleep(checkInterval)
continue
}
cpuUsage := cpuPercent[0]
// 2. 获取内存使用率
memStat, err := mem.VirtualMemory()
if err != nil {
fmt.Printf("[%s] 获取内存使用率失败:%v\n", time.Now().Format("2006-01-02 15:04:05"), err)
time.Sleep(checkInterval)
continue
}
memUsage := memStat.UsedPercent
// 3. 动态调整限流阈值
var newLimit rate.Limit
if cpuUsage > cpuThreshold || memUsage > memThreshold {
newLimit = rate.Limit(lowQpsLimit)
fmt.Printf("[%s] 系统过载:CPU=%.1f%%, 内存=%.1f%%,限流阈值调整为%.1f QPS\n",
time.Now().Format("2006-01-02 15:04:05"), cpuUsage, memUsage, newLimit)
} else {
newLimit = rate.Limit(normalQpsLimit)
// 仅阈值变化时打印日志
if currentLimit.Load().(rate.Limit) != newLimit {
fmt.Printf("[%s] 系统正常:CPU=%.1f%%, 内存=%.1f%%,限流阈值恢复为%.1f QPS\n",
time.Now().Format("2006-01-02 15:04:05"), cpuUsage, memUsage, newLimit)
}
}
// 4. 原子更新阈值
currentLimit.Store(newLimit)
// 5. 间隔检查
time.Sleep(checkInterval)
}
}
// ========== 6. 动态更新ACL规则接口 ==========
func addACLUpdateHandler() {
http.HandleFunc("/acl/update", func(w http.ResponseWriter, r *http.Request) {
// 仅允许本地访问更新接口
clientIP := getClientIP(r)
if clientIP != "127.0.0.1" && clientIP != "::1" {
errMsg := "禁止远程更新ACL规则"
logACLAudit(false, r, errMsg)
http.Error(w, errMsg, http.StatusForbidden)
return
}
// 解析JSON规则
var rule ACLRule
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil {
errMsg := "规则格式错误:" + err.Error()
logACLAudit(false, r, errMsg)
http.Error(w, errMsg, http.StatusBadRequest)
return
}
// 更新规则
aclManager.UpdateRule(rule)
logACLAudit(true, r, "ACL规则更新成功")
fmt.Fprintf(w, "ACL规则更新成功:%+v", rule)
})
}
// ========== 7. 主函数 ==========
func main() {
// 1. 注册ACL动态更新接口
addACLUpdateHandler()
// 2. 入口流量代理:ACL → 限流 → 转发到业务应用
appBackend, _ := url.Parse("http://127.0.0.1:8080")
appProxy := httputil.NewSingleHostReverseProxy(appBackend)
entryHandler := func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("[%s] Sidecar拦截入口流量:%s %s\n",
time.Now().Format("2006-01-02 15:04:05"), r.Method, r.URL.Path)
appProxy.ServeHTTP(w, r)
}
// 挂载中间件(顺序:ACL → 限流 → 业务转发)
http.HandleFunc("/", aclMiddleware(rateLimitMiddleware(entryHandler)))
// 3. 出口流量代理:熔断+超时 → 转发到外部服务
extServiceAddr := "http://httpbin.org/get" // 测试用外部服务
http.HandleFunc("/proxy/ext-service", circuitBreakerMiddleware(extServiceAddr))
// 4. 启动Sidecar
fmt.Println("====================================================")
fmt.Println("增强版Sidecar代理启动(含ACL+限流+熔断+动态过载):")
fmt.Println(" - 入口流量:0.0.0.0:8000(ACL→限流→动态过载)")
fmt.Println(" - 出口流量:0.0.0.0:8001(超时+熔断)")
fmt.Println(" - ACL更新:127.0.0.1:8000/acl/update(仅本地可访问)")
fmt.Printf(" - 默认限流:%d QPS,熔断超时:%dms\n", normalQpsLimit, timeoutMs)
fmt.Println("====================================================")
if err := http.ListenAndServe(":8000", nil); err != nil {
panic(err)
}
}
四、依赖安装
进入sidecar目录,执行以下命令安装依赖:
运行
cd sidecar
go get github.com/afex/hystrix-go/hystrix
go get github.com/shirou/gopsutil/v3/cpu
go get github.com/shirou/gopsutil/v3/mem
go get golang.org/x/time/rate
五、完整验证流程
1. 启动服务
运行
# 终端1:启动业务应用
cd app && go run main.go
# 终端2:启动Sidecar代理
cd sidecar && go run main.go
2. 验证 ACL 权限控制
场景 1:合法 IP + 合法 Token 访问 /hello(放行)
运行
curl -H "X-Auth-Token: valid-token-123" http://127.0.0.1:8000/hello?name=test
预期结果:
- 返回业务响应(包含 httpbin 的内容);
- Sidecar 日志:
ACL ALLOW | Method=GET | Path=/hello | IP=127.0.0.1 | Token=valid-token-123 | Msg=权限校验通过。
场景 2:合法 IP + 非法 Token 访问 /hello(拒绝)
运行
curl -H "X-Auth-Token: wrong-token" http://127.0.0.1:8000/hello?name=test
预期结果:
- 返回
403 Forbidden; - Sidecar 日志:
ACL DENY | Method=GET | Path=/hello | IP=127.0.0.1 | Token=wrong-token | Msg=Token无效,无访问/hello接口的权限。
场景 3:内网 IP 访问 /api/user(放行)
运行
# 若你的机器IP是192.168.1.100,执行:
curl http://192.168.1.100:8000/api/user
预期结果:
- 返回
{"code":200,"msg":"success","data":{"user_id":"1001","name":"test"}}; - Sidecar 日志:
ACL ALLOW | Method=GET | Path=/api/user | IP=192.168.1.100 | Token= | Msg=权限校验通过。
场景 4:外网 IP 访问 /api/user(拒绝)
运行
# 用公网IP访问(或修改ACL规则为非内网IP)
curl http://你的公网IP:8000/api/user
预期结果:
- 返回
403 Forbidden; - Sidecar 日志:
ACL DENY | Method=GET | Path=/api/user | IP=x.x.x.x | Token= | Msg=IPx.x.x.x无访问/api/user接口的权限(不在允许列表)。
场景 5:健康检查接口(完全开放)
运行
curl http://任意IP:8000/health
预期结果:返回ok,日志显示ACL ALLOW。
3. 验证动态更新 ACL 规则
运行
# 本地更新/hello接口的Token规则
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/acl/update -d '{
"path": "/hello",
"methods": ["GET"],
"allow_ips": ["127.0.0.1"],
"allow_tokens": ["new-token-456"],
"allow_roles": ["*"],
"enabled": true
}'
验证更新后规则:
bash
运行
# 旧Token被拒绝
curl -H "X-Auth-Token: valid-token-123" http://127.0.0.1:8000/hello?name=test
# 新Token放行
curl -H "X-Auth-Token: new-token-456" http://127.0.0.1:8000/hello?name=test
4. 验证限流功能
bash
# 压测工具发起高并发请求(Linux/macOS)
ab -n 50 -c 5 http://127.0.0.1:8000/health
# Windows可使用Postman批量请求,或循环curl
for /l %i in (1,1,50) do curl -H "X-Auth-Token: valid-token-123" http://127.0.0.1:8000/hello?name=test
预期结果:
- 部分请求返回
429 Too Many Requests; - Sidecar 日志:
限流触发:当前QPS阈值=10.0 | Path=/hello。
5. 验证熔断 + 超时
修改sidecar/main.go中的外部服务地址为无效地址:
go
运行
extServiceAddr := "http://127.0.0.1:9999" // 不存在的服务
重启 Sidecar 后发起请求:
bash
运行
curl -H "X-Auth-Token: valid-token-123" http://127.0.0.1:8000/hello?name=test
预期结果:
- 首次请求等待 3 秒后返回
503 Service Unavailable; - 连续发起 20 + 次请求后,熔断器触发,请求立即返回
503; - 5 秒后熔断器半开,恢复请求尝试。
6. 验证动态过载保护
手动修改sidecar/main.go中的过载阈值(模拟过载):
运行
const (
cpuThreshold = 10.0 // 降低阈值,触发过载
memThreshold = 10.0
)
重启 Sidecar 后,观察日志:
系统过载:CPU=xx.x%, 内存=xx.x%,限流阈值调整为2.0 QPS
此时限流阈值自动降到 2 QPS,系统负载降低后会恢复为 10 QPS。
六、核心特性总结
| 能力 | 实现方式 | 核心价值 |
|---|---|---|
| ACL 权限控制 | 路径 + 方法 + IP+Token 多维校验 | 精细化管控接口访问 |
| 限流 | 令牌桶算法 + 动态阈值 | 保护业务应用不被高并发压垮 |
| 熔断 + 超时 | Hystrix 熔断器 + Context 超时 | 避免外部服务故障扩散 |
| 动态过载保护 | CPU / 内存监控 + 原子阈值更新 | 自动削峰填谷,保障系统稳定 |
| 审计日志 | 本地文件 + 控制台输出 | 可追溯权限访问行为 |
| 动态规则更新 | 本地 HTTP 接口 | 无需重启 Sidecar 更新规则 |
该实现完全贴合 Service Mesh 的核心设计理念:所有流量治理能力在 Sidecar 层实现,业务应用零感知、零修改,是理解 Istio/Linkerd 等产品底层逻辑的最佳实践。