代理模式
问题
Go 中如何实现代理模式?有哪些常见应用?
答案
接口代理
type UserService interface {
GetUser(id int) (*User, error)
}
// 真实服务
type userServiceImpl struct {
db *sql.DB
}
func (s *userServiceImpl) GetUser(id int) (*User, error) {
// 查数据库
return queryUser(s.db, id)
}
// 缓存代理
type cachedUserService struct {
real UserService
cache *redis.Client
}
func (s *cachedUserService) GetUser(id int) (*User, error) {
// 先查缓存
key := fmt.Sprintf("user:%d", id)
cached, err := s.cache.Get(ctx, key).Result()
if err == nil {
var user User
json.Unmarshal([]byte(cached), &user)
return &user, nil
}
// 缓存未命中,查真实服务
user, err := s.real.GetUser(id)
if err != nil {
return nil, err
}
// 写入缓存
data, _ := json.Marshal(user)
s.cache.Set(ctx, key, data, time.Hour)
return user, nil
}
常见代理类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 缓存代理 | 缓存结果避免重复查询 | 上面的例子 |
| 日志代理 | 记录调用日志 | gRPC Interceptor |
| 限流代理 | 控制调用频率 | API 限流 |
| 懒加载代理 | 延迟初始化重资源 | 数据库连接 |
gRPC Interceptor 就是代理
// 一元 RPC 拦截器(日志代理)
func LoggingInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
start := time.Now()
resp, err := handler(ctx, req) // 调用真实 Handler
log.Printf("method=%s duration=%v err=%v", info.FullMethod, time.Since(start), err)
return resp, err
}
server := grpc.NewServer(grpc.UnaryInterceptor(LoggingInterceptor))
常见面试问题
Q1: Go 没有动态代理(Java Proxy),怎么办?
答案:
- 手动实现:如上面的接口代理,写起来并不多
- 代码生成:mockgen、Wire 等工具自动生成代理代码
- gRPC Interceptor:框架层面的 AOP 能力
Go 哲学偏好显式代码而非运行时反射,手动实现更清晰。
Q2: 代理模式 vs 装饰器模式?
答案:
- 代理:控制访问(缓存、权限、限流),客户端不知道有代理
- 装饰器:增强功能(添加日志、计时),客户端主动选择装饰
实现方式相同(都是接口包装),区别在于意图。