add
This commit is contained in:
766
网关管理详解.md
Normal file
766
网关管理详解.md
Normal file
@@ -0,0 +1,766 @@
|
||||
# 微服务网关管理详解
|
||||
|
||||
## 🚪 什么是 API 网关?
|
||||
|
||||
API 网关就像一个**智能门卫**,站在所有微服务的前面:
|
||||
|
||||
```
|
||||
客户端请求 → API网关 → 路由到具体的微服务
|
||||
↑ ↑ ↑
|
||||
用户App 统一入口 用户服务
|
||||
管理后台 ↓ 支付服务
|
||||
第三方系统 认证授权 产品服务
|
||||
限流控制 数据服务
|
||||
日志记录
|
||||
```
|
||||
|
||||
## 🎯 网关的核心功能
|
||||
|
||||
### 1. 请求路由 (Request Routing)
|
||||
|
||||
```go
|
||||
// 路由配置示例
|
||||
type Route struct {
|
||||
Path string `yaml:"path"`
|
||||
Method string `yaml:"method"`
|
||||
Service string `yaml:"service"`
|
||||
Middlewares []string `yaml:"middlewares"`
|
||||
Headers map[string]string `yaml:"headers"`
|
||||
}
|
||||
|
||||
var routes = []Route{
|
||||
{
|
||||
Path: "/api/v1/users/*",
|
||||
Method: "*",
|
||||
Service: "user-service",
|
||||
Middlewares: []string{"auth", "rate-limit"},
|
||||
},
|
||||
{
|
||||
Path: "/api/v1/payments/*",
|
||||
Method: "*",
|
||||
Service: "payment-service",
|
||||
Middlewares: []string{"auth", "encrypt"},
|
||||
},
|
||||
{
|
||||
Path: "/api/v1/products/*",
|
||||
Method: "*",
|
||||
Service: "product-service",
|
||||
Middlewares: []string{"auth", "cache"},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 负载均衡 (Load Balancing)
|
||||
|
||||
```go
|
||||
// 负载均衡器
|
||||
type LoadBalancer interface {
|
||||
Next() *ServiceInstance
|
||||
}
|
||||
|
||||
// 轮询负载均衡
|
||||
type RoundRobinBalancer struct {
|
||||
instances []*ServiceInstance
|
||||
current int
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (rb *RoundRobinBalancer) Next() *ServiceInstance {
|
||||
rb.mutex.Lock()
|
||||
defer rb.mutex.Unlock()
|
||||
|
||||
if len(rb.instances) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
instance := rb.instances[rb.current]
|
||||
rb.current = (rb.current + 1) % len(rb.instances)
|
||||
return instance
|
||||
}
|
||||
|
||||
// 加权轮询
|
||||
type WeightedRoundRobinBalancer struct {
|
||||
instances []*WeightedInstance
|
||||
total int
|
||||
current int
|
||||
}
|
||||
|
||||
type WeightedInstance struct {
|
||||
Instance *ServiceInstance
|
||||
Weight int
|
||||
Current int
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 服务发现 (Service Discovery)
|
||||
|
||||
```go
|
||||
// 服务发现接口
|
||||
type ServiceDiscovery interface {
|
||||
Register(service *ServiceInstance) error
|
||||
Deregister(serviceID string) error
|
||||
Discover(serviceName string) ([]*ServiceInstance, error)
|
||||
Watch(serviceName string) <-chan []*ServiceInstance
|
||||
}
|
||||
|
||||
// Consul 服务发现实现
|
||||
type ConsulDiscovery struct {
|
||||
client *consul.Client
|
||||
}
|
||||
|
||||
func (cd *ConsulDiscovery) Discover(serviceName string) ([]*ServiceInstance, error) {
|
||||
services, _, err := cd.client.Health().Service(serviceName, "", true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var instances []*ServiceInstance
|
||||
for _, service := range services {
|
||||
instances = append(instances, &ServiceInstance{
|
||||
ID: service.Service.ID,
|
||||
Name: service.Service.Service,
|
||||
Address: service.Service.Address,
|
||||
Port: service.Service.Port,
|
||||
Meta: service.Service.Meta,
|
||||
})
|
||||
}
|
||||
|
||||
return instances, nil
|
||||
}
|
||||
```
|
||||
|
||||
## 🛡️ 网关中间件系统
|
||||
|
||||
### 1. 认证中间件
|
||||
|
||||
```go
|
||||
// 认证中间件
|
||||
func AuthMiddleware(jwtSecret string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 1. 获取 Token
|
||||
token := extractToken(c)
|
||||
if token == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "缺少认证令牌"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 验证 Token
|
||||
claims, err := validateJWT(token, jwtSecret)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证令牌"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 设置用户上下文
|
||||
c.Set("user_id", claims.UserID)
|
||||
c.Set("username", claims.Username)
|
||||
c.Set("roles", claims.Roles)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func extractToken(c *gin.Context) string {
|
||||
// 从 Header 获取
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader != "" && strings.HasPrefix(authHeader, "Bearer ") {
|
||||
return strings.TrimPrefix(authHeader, "Bearer ")
|
||||
}
|
||||
|
||||
// 从 Query 参数获取
|
||||
return c.Query("token")
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 限流中间件
|
||||
|
||||
```go
|
||||
// 限流中间件
|
||||
type RateLimiter struct {
|
||||
redis *redis.Client
|
||||
window time.Duration
|
||||
limit int
|
||||
}
|
||||
|
||||
func NewRateLimiter(redis *redis.Client, window time.Duration, limit int) *RateLimiter {
|
||||
return &RateLimiter{
|
||||
redis: redis,
|
||||
window: window,
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *RateLimiter) Middleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 1. 获取客户端标识
|
||||
clientID := getClientID(c)
|
||||
|
||||
// 2. 检查限流
|
||||
allowed, err := rl.isAllowed(clientID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "限流检查失败"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
c.JSON(http.StatusTooManyRequests, gin.H{
|
||||
"error": "请求过于频繁,请稍后再试",
|
||||
"retry_after": int(rl.window.Seconds()),
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *RateLimiter) isAllowed(clientID string) (bool, error) {
|
||||
key := fmt.Sprintf("rate_limit:%s", clientID)
|
||||
|
||||
// 使用滑动窗口算法
|
||||
now := time.Now().Unix()
|
||||
window := now - int64(rl.window.Seconds())
|
||||
|
||||
pipe := rl.redis.Pipeline()
|
||||
|
||||
// 删除过期的记录
|
||||
pipe.ZRemRangeByScore(context.Background(), key, "0", fmt.Sprintf("%d", window))
|
||||
|
||||
// 获取当前窗口内的请求数
|
||||
countCmd := pipe.ZCard(context.Background(), key)
|
||||
|
||||
// 添加当前请求
|
||||
pipe.ZAdd(context.Background(), key, &redis.Z{
|
||||
Score: float64(now),
|
||||
Member: fmt.Sprintf("%d", now),
|
||||
})
|
||||
|
||||
// 设置过期时间
|
||||
pipe.Expire(context.Background(), key, rl.window)
|
||||
|
||||
_, err := pipe.Exec(context.Background())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
count := countCmd.Val()
|
||||
return count < int64(rl.limit), nil
|
||||
}
|
||||
|
||||
func getClientID(c *gin.Context) string {
|
||||
// 优先使用用户ID
|
||||
if userID := c.GetString("user_id"); userID != "" {
|
||||
return "user:" + userID
|
||||
}
|
||||
|
||||
// 其次使用API Key
|
||||
if apiKey := c.GetHeader("X-API-Key"); apiKey != "" {
|
||||
return "api:" + apiKey
|
||||
}
|
||||
|
||||
// 最后使用IP地址
|
||||
return "ip:" + c.ClientIP()
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 缓存中间件
|
||||
|
||||
```go
|
||||
// 缓存中间件
|
||||
type CacheMiddleware struct {
|
||||
redis *redis.Client
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func NewCacheMiddleware(redis *redis.Client, ttl time.Duration) *CacheMiddleware {
|
||||
return &CacheMiddleware{
|
||||
redis: redis,
|
||||
ttl: ttl,
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *CacheMiddleware) Middleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 只缓存 GET 请求
|
||||
if c.Request.Method != http.MethodGet {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 生成缓存键
|
||||
cacheKey := generateCacheKey(c)
|
||||
|
||||
// 尝试从缓存获取
|
||||
cached, err := cm.redis.Get(context.Background(), cacheKey).Result()
|
||||
if err == nil {
|
||||
var response CachedResponse
|
||||
if json.Unmarshal([]byte(cached), &response) == nil {
|
||||
// 设置响应头
|
||||
for key, value := range response.Headers {
|
||||
c.Header(key, value)
|
||||
}
|
||||
|
||||
c.Data(response.Status, response.ContentType, response.Body)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 包装 ResponseWriter 来捕获响应
|
||||
crw := &cachedResponseWriter{
|
||||
ResponseWriter: c.Writer,
|
||||
body: &bytes.Buffer{},
|
||||
headers: make(map[string]string),
|
||||
}
|
||||
c.Writer = crw
|
||||
|
||||
c.Next()
|
||||
|
||||
// 缓存响应
|
||||
if crw.status >= 200 && crw.status < 300 {
|
||||
response := CachedResponse{
|
||||
Status: crw.status,
|
||||
Headers: crw.headers,
|
||||
ContentType: crw.Header().Get("Content-Type"),
|
||||
Body: crw.body.Bytes(),
|
||||
}
|
||||
|
||||
if data, err := json.Marshal(response); err == nil {
|
||||
cm.redis.Set(context.Background(), cacheKey, data, cm.ttl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CachedResponse struct {
|
||||
Status int `json:"status"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
ContentType string `json:"content_type"`
|
||||
Body []byte `json:"body"`
|
||||
}
|
||||
|
||||
type cachedResponseWriter struct {
|
||||
gin.ResponseWriter
|
||||
body *bytes.Buffer
|
||||
headers map[string]string
|
||||
status int
|
||||
}
|
||||
|
||||
func (crw *cachedResponseWriter) Write(data []byte) (int, error) {
|
||||
crw.body.Write(data)
|
||||
return crw.ResponseWriter.Write(data)
|
||||
}
|
||||
|
||||
func (crw *cachedResponseWriter) WriteHeader(status int) {
|
||||
crw.status = status
|
||||
crw.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 网关配置管理
|
||||
|
||||
### 1. 动态配置
|
||||
|
||||
```yaml
|
||||
# gateway.yaml
|
||||
gateway:
|
||||
port: 8080
|
||||
timeout: 30s
|
||||
|
||||
services:
|
||||
user-service:
|
||||
discovery: consul
|
||||
load_balancer: round_robin
|
||||
health_check:
|
||||
enabled: true
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
path: /health
|
||||
circuit_breaker:
|
||||
enabled: true
|
||||
failure_threshold: 5
|
||||
timeout: 60s
|
||||
|
||||
payment-service:
|
||||
discovery: consul
|
||||
load_balancer: weighted_round_robin
|
||||
weights:
|
||||
- instance: payment-service-1
|
||||
weight: 3
|
||||
- instance: payment-service-2
|
||||
weight: 1
|
||||
|
||||
middlewares:
|
||||
auth:
|
||||
jwt_secret: ${JWT_SECRET}
|
||||
excluded_paths:
|
||||
- /api/v1/auth/login
|
||||
- /api/v1/auth/register
|
||||
- /health
|
||||
|
||||
rate_limit:
|
||||
default:
|
||||
window: 60s
|
||||
limit: 100
|
||||
user_limits:
|
||||
premium_user:
|
||||
window: 60s
|
||||
limit: 1000
|
||||
basic_user:
|
||||
window: 60s
|
||||
limit: 100
|
||||
|
||||
cache:
|
||||
ttl: 300s
|
||||
excluded_paths:
|
||||
- /api/v1/users/me
|
||||
- /api/v1/payments/*
|
||||
|
||||
routes:
|
||||
- path: /api/v1/users/*
|
||||
service: user-service
|
||||
middlewares: [auth, rate_limit]
|
||||
|
||||
- path: /api/v1/payments/*
|
||||
service: payment-service
|
||||
middlewares: [auth, rate_limit]
|
||||
|
||||
- path: /api/v1/products/*
|
||||
service: product-service
|
||||
middlewares: [auth, rate_limit, cache]
|
||||
```
|
||||
|
||||
### 2. 配置热更新
|
||||
|
||||
```go
|
||||
// 配置管理器
|
||||
type ConfigManager struct {
|
||||
config *GatewayConfig
|
||||
mutex sync.RWMutex
|
||||
watchers []chan *GatewayConfig
|
||||
}
|
||||
|
||||
func NewConfigManager() *ConfigManager {
|
||||
return &ConfigManager{
|
||||
watchers: make([]chan *GatewayConfig, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) LoadConfig(path string) error {
|
||||
cm.mutex.Lock()
|
||||
defer cm.mutex.Unlock()
|
||||
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var config GatewayConfig
|
||||
if err := yaml.Unmarshal(data, &config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cm.config = &config
|
||||
|
||||
// 通知所有监听者
|
||||
for _, watcher := range cm.watchers {
|
||||
select {
|
||||
case watcher <- &config:
|
||||
default:
|
||||
// 非阻塞发送
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) Watch() <-chan *GatewayConfig {
|
||||
cm.mutex.Lock()
|
||||
defer cm.mutex.Unlock()
|
||||
|
||||
watcher := make(chan *GatewayConfig, 1)
|
||||
cm.watchers = append(cm.watchers, watcher)
|
||||
|
||||
// 立即发送当前配置
|
||||
if cm.config != nil {
|
||||
watcher <- cm.config
|
||||
}
|
||||
|
||||
return watcher
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) WatchFile(path string) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
err = watcher.Add(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
log.Println("配置文件已修改,重新加载...")
|
||||
time.Sleep(100 * time.Millisecond) // 避免多次触发
|
||||
cm.LoadConfig(path)
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
log.Println("配置文件监听错误:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 网关监控和可观测性
|
||||
|
||||
### 1. 指标收集
|
||||
|
||||
```go
|
||||
// 指标收集
|
||||
var (
|
||||
requestsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "gateway_requests_total",
|
||||
Help: "Total number of requests",
|
||||
},
|
||||
[]string{"service", "method", "path", "status"},
|
||||
)
|
||||
|
||||
requestDuration = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "gateway_request_duration_seconds",
|
||||
Help: "Request duration in seconds",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
[]string{"service", "method", "path"},
|
||||
)
|
||||
|
||||
activeConnections = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "gateway_active_connections",
|
||||
Help: "Number of active connections",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
func MetricsMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
|
||||
activeConnections.Inc()
|
||||
defer activeConnections.Dec()
|
||||
|
||||
c.Next()
|
||||
|
||||
duration := time.Since(start).Seconds()
|
||||
status := strconv.Itoa(c.Writer.Status())
|
||||
service := c.GetString("target_service")
|
||||
|
||||
requestsTotal.WithLabelValues(
|
||||
service,
|
||||
c.Request.Method,
|
||||
c.Request.URL.Path,
|
||||
status,
|
||||
).Inc()
|
||||
|
||||
requestDuration.WithLabelValues(
|
||||
service,
|
||||
c.Request.Method,
|
||||
c.Request.URL.Path,
|
||||
).Observe(duration)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 链路追踪
|
||||
|
||||
```go
|
||||
// OpenTelemetry 追踪
|
||||
func TracingMiddleware(tracer trace.Tracer) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spanName := fmt.Sprintf("%s %s", c.Request.Method, c.Request.URL.Path)
|
||||
|
||||
ctx, span := tracer.Start(c.Request.Context(), spanName)
|
||||
defer span.End()
|
||||
|
||||
// 设置 span 属性
|
||||
span.SetAttributes(
|
||||
attribute.String("http.method", c.Request.Method),
|
||||
attribute.String("http.url", c.Request.URL.String()),
|
||||
attribute.String("http.user_agent", c.Request.UserAgent()),
|
||||
attribute.String("client.ip", c.ClientIP()),
|
||||
)
|
||||
|
||||
// 传递上下文
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
|
||||
c.Next()
|
||||
|
||||
// 设置响应属性
|
||||
span.SetAttributes(
|
||||
attribute.Int("http.status_code", c.Writer.Status()),
|
||||
attribute.Int("http.response_size", c.Writer.Size()),
|
||||
)
|
||||
|
||||
if c.Writer.Status() >= 400 {
|
||||
span.SetStatus(codes.Error, fmt.Sprintf("HTTP %d", c.Writer.Status()))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🏗️ 网关架构设计
|
||||
|
||||
### 1. 高可用部署
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
version: "3.8"
|
||||
services:
|
||||
gateway-1:
|
||||
image: your-gateway:latest
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
- GATEWAY_ID=gateway-1
|
||||
- CONSUL_ADDRESS=consul:8500
|
||||
depends_on:
|
||||
- consul
|
||||
- redis
|
||||
|
||||
gateway-2:
|
||||
image: your-gateway:latest
|
||||
ports:
|
||||
- "8081:8080"
|
||||
environment:
|
||||
- GATEWAY_ID=gateway-2
|
||||
- CONSUL_ADDRESS=consul:8500
|
||||
depends_on:
|
||||
- consul
|
||||
- redis
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
depends_on:
|
||||
- gateway-1
|
||||
- gateway-2
|
||||
|
||||
consul:
|
||||
image: consul:latest
|
||||
ports:
|
||||
- "8500:8500"
|
||||
command: consul agent -dev -client=0.0.0.0
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
```
|
||||
|
||||
### 2. Nginx 负载均衡配置
|
||||
|
||||
```nginx
|
||||
# nginx.conf
|
||||
upstream gateway {
|
||||
server gateway-1:8080 weight=1 max_fails=3 fail_timeout=30s;
|
||||
server gateway-2:8080 weight=1 max_fails=3 fail_timeout=30s;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.example.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://gateway;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
# 健康检查
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# 健康检查端点
|
||||
location /health {
|
||||
access_log off;
|
||||
proxy_pass http://gateway;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 实施建议
|
||||
|
||||
### 1. 渐进式迁移
|
||||
|
||||
```
|
||||
阶段1: 搭建基础网关
|
||||
├── 基本的路由功能
|
||||
├── 健康检查
|
||||
└── 监控指标
|
||||
|
||||
阶段2: 添加安全功能
|
||||
├── JWT 认证
|
||||
├── API 限流
|
||||
└── 请求日志
|
||||
|
||||
阶段3: 性能优化
|
||||
├── 响应缓存
|
||||
├── 连接池
|
||||
└── 负载均衡
|
||||
|
||||
阶段4: 高级功能
|
||||
├── 熔断器
|
||||
├── 灰度发布
|
||||
└── API 版本管理
|
||||
```
|
||||
|
||||
### 2. 网关选型建议
|
||||
|
||||
**自研网关 (推荐给你)**:
|
||||
|
||||
```go
|
||||
// 基于 gin + consul + redis 的轻量级网关
|
||||
// 优势: 完全控制、定制化强、集成容易
|
||||
// 适合: 中小型项目、快速迭代
|
||||
```
|
||||
|
||||
**开源网关**:
|
||||
|
||||
- **Kong**: 功能丰富,插件生态好
|
||||
- **Traefik**: 配置简单,支持多种后端
|
||||
- **Envoy**: 性能极高,配置复杂
|
||||
|
||||
## 📋 总结
|
||||
|
||||
网关管理的核心要点:
|
||||
|
||||
1. **统一入口** - 所有外部请求通过网关
|
||||
2. **服务路由** - 将请求路由到正确的后端服务
|
||||
3. **安全认证** - 统一的身份验证和授权
|
||||
4. **流量控制** - 限流、熔断、负载均衡
|
||||
5. **可观测性** - 监控、日志、链路追踪
|
||||
6. **高可用** - 多实例部署、健康检查
|
||||
|
||||
对于你的项目,建议先实现基础的路由和认证功能,然后逐步添加高级特性。
|
||||
Reference in New Issue
Block a user