You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Golang gRPC网关服务动态配置自定义Ingress Controller规则

实现gRPC网关的动态Ingress规则配置

我正在弃用Traefik Ingress Controller,改用Golang自行实现gRPC网关。当前代码中硬编码了gRPC方法白名单,需要替换为动态配置的Ingress规则,支持通过gRPC请求更新规则,并根据规则将请求转发到对应后端gRPC服务。现有硬编码代码如下:

var whitelistMethods = [...]string{`/auth.Auth/Authenticate`, `/xvm.xvmService/NewClientID`,`/xvm.xvmService/Authenticate`, `/xvm.xvmService/DeleteClientID`}

func gatewayDirector() proxy.StreamDirector {
    return func(ctx context.Context, method string) (context.Context, *grpc.ClientConn, error) {
      meta, ok := metadata.FromIncomingContext(ctx)
      if !ok {
        return ctx, nil, errInvalidMeta
      }
      var clientAddr string
      if clientPeer, ok := peer.FromContext(ctx); ok {
        addr, _, err := net.SplitHostPort(clientPeer.Addr.String())
        if err != nil {
            log.DErrorf("erred while parsing client address, error:%v", err)
        } else {
            clientAddr = addr
        }
      }
      if isWhitelistMethod(method) {
        log.Debugf("whitelist method:%v, forward to security service", method)
        return ctx, securityClientConn, nil
      }

      var clientConn *grpc.ClientConn
      // handle regular RPC methods and get the corresponding backend client
      clientConn, err = handleRegularMethods(method, meta)
      if err != nil {
        return ctx, nil, err
      }

      return ctx, clientConn, nil
   }
}

func isWhitelistMethod(method string) bool {
  for _, m := range whitelistMethods {
     if m == method {
         return true
     }
  }
  return false
}

核心解决方案

替换硬编码白名单,实现三个核心模块:线程安全的动态规则存储、外部可调用的gRPC配置接口、基于规则的动态路由逻辑。

1. 定义动态规则与线程安全存储

首先定义规则结构体和并发安全的存储容器:

import (
    "google.golang.org/grpc"
    "sync"
    "strings"
)

// IngressRule 存储单条Ingress规则
type IngressRule struct {
    MethodPattern string        // 匹配的gRPC方法,支持精确匹配(如"/auth.Auth/Authenticate")或前缀匹配(如"/xvm.xvmService/*")
    BackendAddr   string        // 后端gRPC服务地址
    Conn          *grpc.ClientConn // 已建立的后端连接
}

// RuleStore 线程安全的规则存储容器
type RuleStore struct {
    sync.RWMutex
    rules map[string]*IngressRule
}

var globalRuleStore = &RuleStore{
    rules: make(map[string]*IngressRule),
}

2. 实现规则的增删改查操作

为RuleStore添加线程安全的操作方法,包含连接的自动管理:

// AddOrUpdateRule 添加或更新规则,自动重建后端连接
func (rs *RuleStore) AddOrUpdateRule(pattern string, backendAddr string) error {
    rs.Lock()
    defer rs.Unlock()

    // 检查规则是否存在,地址未变更则直接返回
    if rule, exists := rs.rules[pattern]; exists {
        if rule.BackendAddr == backendAddr {
            return nil
        }
        // 关闭旧连接
        _ = rule.Conn.Close()
    }

    // 建立新的后端连接
    conn, err := grpc.Dial(backendAddr, grpc.WithInsecure()) // 根据实际需求添加TLS等配置
    if err != nil {
        return err
    }

    rs.rules[pattern] = &IngressRule{
        MethodPattern: pattern,
        BackendAddr:   backendAddr,
        Conn:          conn,
    }
    return nil
}

// RemoveRule 删除指定规则并关闭对应连接
func (rs *RuleStore) RemoveRule(pattern string) error {
    rs.Lock()
    defer rs.Unlock()

    rule, exists := rs.rules[pattern]
    if !exists {
        return nil
    }
    _ = rule.Conn.Close()
    delete(rs.rules, pattern)
    return nil
}

// MatchRule 根据请求方法匹配规则,优先精确匹配,再尝试前缀匹配
func (rs *RuleStore) MatchRule(method string) (*IngressRule, bool) {
    rs.RLock()
    defer rs.RUnlock()

    // 精确匹配
    if rule, exists := rs.rules[method]; exists {
        return rule, true
    }

    // 前缀匹配(针对带*的规则)
    for pattern, rule := range rs.rules {
        if len(pattern) > 0 && pattern[len(pattern)-1] == '*' && strings.HasPrefix(method, pattern[:len(pattern)-1]) {
            return rule, true
        }
    }

    return nil, false
}

3. 提供gRPC配置接口

定义外部可调用的gRPC服务,用于动态更新规则。首先编写proto文件:

syntax = "proto3";

package gatewayconfig;

service GatewayConfigService {
    rpc AddOrUpdateRule(AddRuleRequest) returns (AddRuleResponse);
    rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse);
}

message AddRuleRequest {
    string method_pattern = 1;
    string backend_addr = 2;
}

message AddRuleResponse {
    bool success = 1;
    string message = 2;
}

message RemoveRuleRequest {
    string method_pattern = 1;
}

message RemoveRuleResponse {
    bool success = 1;
    string message = 2;
}

然后实现服务逻辑:

import (
    "context"
    pb "your/proto/path/gatewayconfig"
)

type gatewayConfigService struct {
    pb.UnimplementedGatewayConfigServiceServer
}

func (s *gatewayConfigService) AddOrUpdateRule(ctx context.Context, req *pb.AddRuleRequest) (*pb.AddRuleResponse, error) {
    err := globalRuleStore.AddOrUpdateRule(req.MethodPattern, req.BackendAddr)
    if err != nil {
        return &pb.AddRuleResponse{Success: false, Message: err.Error()}, nil
    }
    return &pb.AddRuleResponse{Success: true, Message: "Rule updated successfully"}, nil
}

func (s *gatewayConfigService) RemoveRule(ctx context.Context, req *pb.RemoveRuleRequest) (*pb.RemoveRuleResponse, error) {
    err := globalRuleStore.RemoveRule(req.MethodPattern)
    if err != nil {
        return &pb.RemoveRuleResponse{Success: false, Message: err.Error()}, nil
    }
    return &pb.RemoveRuleResponse{Success: true, Message: "Rule removed successfully"}, nil
}

// 在网关启动时注册该配置服务
func RegisterConfigService(s *grpc.Server) {
    pb.RegisterGatewayConfigServiceServer(s, &gatewayConfigService{})
}

4. 修改网关路由逻辑

替换原有的硬编码白名单判断,改为从动态规则中匹配:

func gatewayDirector() proxy.StreamDirector {
    return func(ctx context.Context, method string) (context.Context, *grpc.ClientConn, error) {
        meta, ok := metadata.FromIncomingContext(ctx)
        if !ok {
            return ctx, nil, errInvalidMeta
        }
        var clientAddr string
        if clientPeer, ok := peer.FromContext(ctx); ok {
            addr, _, err := net.SplitHostPort(clientPeer.Addr.String())
            if err != nil {
                log.DErrorf("erred while parsing client address, error:%v", err)
            } else {
                clientAddr = addr
            }
        }

        // 从动态规则中匹配后端服务
        if rule, matched := globalRuleStore.MatchRule(method); matched {
            log.Debugf("matched rule for method:%v, forward to backend:%v", method, rule.BackendAddr)
            return ctx, rule.Conn, nil
        }

        // 处理未匹配规则的请求(保留原有逻辑)
        clientConn, err := handleRegularMethods(method, meta)
        if err != nil {
            return ctx, nil, err
        }

        return ctx, clientConn, nil
    }
}

5. 可选优化点

  • 连接健康检查:为后端连接添加心跳检测,断开时自动重建
  • 规则持久化:将规则存储到Etcd、Redis或本地文件,避免网关重启后规则丢失
  • 配置权限控制:在配置接口中添加身份验证,防止非法修改规则

内容的提问来源于stack exchange,提问作者Abhishek Karigar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 13:13:19