🚀 引言

随着业务复杂度的增长和团队规模的扩大,传统的单体应用架构逐渐暴露出维护困难、扩展性差、技术栈固化等问题。微服务架构作为一种解决方案,通过将大型应用拆分为多个小型、独立的服务,实现了更好的可维护性、可扩展性和技术多样性。

Go语言凭借其出色的并发性能、简洁的语法和丰富的标准库,成为构建微服务的理想选择。本文将深入探讨如何使用Go语言设计和实现高质量的微服务架构。

🏗️ 微服务架构基础

1. 服务拆分策略

按业务领域拆分(DDD方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// 用户服务 - 用户领域
package user

import (
"context"
"time"
"github.com/google/uuid"
)

// 用户聚合根
type User struct {
ID uuid.UUID `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Email string `json:"email" db:"email"`
Password string `json:"-" db:"password_hash"`
Profile Profile `json:"profile"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
IsActive bool `json:"is_active" db:"is_active"`
}

type Profile struct {
FirstName string `json:"first_name" db:"first_name"`
LastName string `json:"last_name" db:"last_name"`
Avatar string `json:"avatar" db:"avatar"`
Bio string `json:"bio" db:"bio"`
Location string `json:"location" db:"location"`
Website string `json:"website" db:"website"`
}

// 用户仓储接口
type Repository interface {
Create(ctx context.Context, user *User) error
GetByID(ctx context.Context, id uuid.UUID) (*User, error)
GetByEmail(ctx context.Context, email string) (*User, error)
Update(ctx context.Context, user *User) error
Delete(ctx context.Context, id uuid.UUID) error
List(ctx context.Context, filter ListFilter) ([]*User, error)
}

// 用户服务接口
type Service interface {
Register(ctx context.Context, req RegisterRequest) (*User, error)
Login(ctx context.Context, req LoginRequest) (*LoginResponse, error)
GetProfile(ctx context.Context, userID uuid.UUID) (*User, error)
UpdateProfile(ctx context.Context, userID uuid.UUID, req UpdateProfileRequest) error
ChangePassword(ctx context.Context, userID uuid.UUID, req ChangePasswordRequest) error
DeactivateUser(ctx context.Context, userID uuid.UUID) error
}

// 用户服务实现
type service struct {
repo Repository
hasher PasswordHasher
jwt JWTManager
logger Logger
metrics Metrics
}

func NewService(repo Repository, hasher PasswordHasher, jwt JWTManager, logger Logger, metrics Metrics) Service {
return &service{
repo: repo,
hasher: hasher,
jwt: jwt,
logger: logger,
metrics: metrics,
}
}

func (s *service) Register(ctx context.Context, req RegisterRequest) (*User, error) {
// 验证请求
if err := req.Validate(); err != nil {
s.metrics.IncCounter("user_register_validation_error")
return nil, NewValidationError(err.Error())
}

// 检查用户是否已存在
existingUser, err := s.repo.GetByEmail(ctx, req.Email)
if err != nil && !IsNotFoundError(err) {
s.logger.Error("failed to check existing user", "error", err)
return nil, err
}
if existingUser != nil {
s.metrics.IncCounter("user_register_duplicate_email")
return nil, NewConflictError("用户已存在")
}

// 创建新用户
hashedPassword, err := s.hasher.Hash(req.Password)
if err != nil {
s.logger.Error("failed to hash password", "error", err)
return nil, err
}

user := &User{
ID: uuid.New(),
Username: req.Username,
Email: req.Email,
Password: hashedPassword,
Profile: Profile{
FirstName: req.FirstName,
LastName: req.LastName,
},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
IsActive: true,
}

if err := s.repo.Create(ctx, user); err != nil {
s.logger.Error("failed to create user", "error", err)
s.metrics.IncCounter("user_register_db_error")
return nil, err
}

s.metrics.IncCounter("user_register_success")
s.logger.Info("user registered successfully", "user_id", user.ID)

return user, nil
}

func (s *service) Login(ctx context.Context, req LoginRequest) (*LoginResponse, error) {
// 获取用户
user, err := s.repo.GetByEmail(ctx, req.Email)
if err != nil {
if IsNotFoundError(err) {
s.metrics.IncCounter("user_login_not_found")
return nil, NewUnauthorizedError("邮箱或密码错误")
}
s.logger.Error("failed to get user by email", "error", err)
return nil, err
}

// 验证密码
if !s.hasher.Verify(req.Password, user.Password) {
s.metrics.IncCounter("user_login_invalid_password")
return nil, NewUnauthorizedError("邮箱或密码错误")
}

// 检查用户状态
if !user.IsActive {
s.metrics.IncCounter("user_login_inactive")
return nil, NewUnauthorizedError("账户已被禁用")
}

// 生成JWT令牌
token, err := s.jwt.Generate(user.ID, user.Email)
if err != nil {
s.logger.Error("failed to generate JWT token", "error", err)
return nil, err
}

s.metrics.IncCounter("user_login_success")
s.logger.Info("user logged in successfully", "user_id", user.ID)

return &LoginResponse{
Token: token,
User: user,
ExpiresAt: time.Now().Add(24 * time.Hour),
}, nil
}

订单服务 - 订单领域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// 订单服务
package order

import (
"context"
"time"
"github.com/google/uuid"
"github.com/shopspring/decimal"
)

// 订单状态枚举
type Status string

const (
StatusPending Status = "pending"
StatusConfirmed Status = "confirmed"
StatusPaid Status = "paid"
StatusShipped Status = "shipped"
StatusDelivered Status = "delivered"
StatusCancelled Status = "cancelled"
StatusRefunded Status = "refunded"
)

// 订单聚合根
type Order struct {
ID uuid.UUID `json:"id" db:"id"`
UserID uuid.UUID `json:"user_id" db:"user_id"`
OrderNumber string `json:"order_number" db:"order_number"`
Status Status `json:"status" db:"status"`
Items []OrderItem `json:"items"`
TotalAmount decimal.Decimal `json:"total_amount" db:"total_amount"`
Currency string `json:"currency" db:"currency"`
ShippingAddress Address `json:"shipping_address"`
BillingAddress Address `json:"billing_address"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

type OrderItem struct {
ID uuid.UUID `json:"id" db:"id"`
OrderID uuid.UUID `json:"order_id" db:"order_id"`
ProductID uuid.UUID `json:"product_id" db:"product_id"`
SKU string `json:"sku" db:"sku"`
Name string `json:"name" db:"name"`
Price decimal.Decimal `json:"price" db:"price"`
Quantity int `json:"quantity" db:"quantity"`
Subtotal decimal.Decimal `json:"subtotal" db:"subtotal"`
}

type Address struct {
Street string `json:"street" db:"street"`
City string `json:"city" db:"city"`
State string `json:"state" db:"state"`
PostalCode string `json:"postal_code" db:"postal_code"`
Country string `json:"country" db:"country"`
}

// 订单服务接口
type Service interface {
CreateOrder(ctx context.Context, req CreateOrderRequest) (*Order, error)
GetOrder(ctx context.Context, orderID uuid.UUID) (*Order, error)
GetUserOrders(ctx context.Context, userID uuid.UUID, filter ListFilter) ([]*Order, error)
UpdateOrderStatus(ctx context.Context, orderID uuid.UUID, status Status) error
CancelOrder(ctx context.Context, orderID uuid.UUID, reason string) error
ProcessPayment(ctx context.Context, orderID uuid.UUID, paymentInfo PaymentInfo) error
}

// 订单服务实现
type service struct {
repo Repository
productService ProductService
paymentService PaymentService
eventBus EventBus
logger Logger
metrics Metrics
}

func (s *service) CreateOrder(ctx context.Context, req CreateOrderRequest) (*Order, error) {
// 验证请求
if err := req.Validate(); err != nil {
return nil, NewValidationError(err.Error())
}

// 验证产品库存
for _, item := range req.Items {
available, err := s.productService.CheckStock(ctx, item.ProductID, item.Quantity)
if err != nil {
s.logger.Error("failed to check product stock", "product_id", item.ProductID, "error", err)
return nil, err
}
if !available {
return nil, NewValidationError(fmt.Sprintf("产品 %s 库存不足", item.ProductID))
}
}

// 计算订单总金额
totalAmount := decimal.Zero
orderItems := make([]OrderItem, 0, len(req.Items))

for _, item := range req.Items {
product, err := s.productService.GetProduct(ctx, item.ProductID)
if err != nil {
return nil, err
}

subtotal := product.Price.Mul(decimal.NewFromInt(int64(item.Quantity)))
totalAmount = totalAmount.Add(subtotal)

orderItems = append(orderItems, OrderItem{
ID: uuid.New(),
ProductID: item.ProductID,
SKU: product.SKU,
Name: product.Name,
Price: product.Price,
Quantity: item.Quantity,
Subtotal: subtotal,
})
}

// 创建订单
order := &Order{
ID: uuid.New(),
UserID: req.UserID,
OrderNumber: s.generateOrderNumber(),
Status: StatusPending,
Items: orderItems,
TotalAmount: totalAmount,
Currency: "CNY",
ShippingAddress: req.ShippingAddress,
BillingAddress: req.BillingAddress,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}

// 保存订单
if err := s.repo.Create(ctx, order); err != nil {
s.logger.Error("failed to create order", "error", err)
return nil, err
}

// 预留库存
for _, item := range order.Items {
if err := s.productService.ReserveStock(ctx, item.ProductID, item.Quantity); err != nil {
s.logger.Error("failed to reserve stock", "product_id", item.ProductID, "error", err)
// 回滚订单创建
s.repo.Delete(ctx, order.ID)
return nil, err
}
}

// 发布订单创建事件
event := OrderCreatedEvent{
OrderID: order.ID,
UserID: order.UserID,
TotalAmount: order.TotalAmount,
CreatedAt: order.CreatedAt,
}
s.eventBus.Publish(ctx, "order.created", event)

s.metrics.IncCounter("order_created")
s.logger.Info("order created successfully", "order_id", order.ID)

return order, nil
}

func (s *service) ProcessPayment(ctx context.Context, orderID uuid.UUID, paymentInfo PaymentInfo) error {
// 获取订单
order, err := s.repo.GetByID(ctx, orderID)
if err != nil {
return err
}

// 检查订单状态
if order.Status != StatusPending {
return NewValidationError("订单状态不允许支付")
}

// 处理支付
paymentResult, err := s.paymentService.ProcessPayment(ctx, PaymentRequest{
OrderID: orderID,
Amount: order.TotalAmount,
Currency: order.Currency,
PaymentInfo: paymentInfo,
})
if err != nil {
s.logger.Error("payment processing failed", "order_id", orderID, "error", err)
return err
}

// 更新订单状态
if paymentResult.Success {
order.Status = StatusPaid
order.UpdatedAt = time.Now()

if err := s.repo.Update(ctx, order); err != nil {
s.logger.Error("failed to update order status", "order_id", orderID, "error", err)
return err
}

// 发布支付成功事件
event := OrderPaidEvent{
OrderID: orderID,
PaymentID: paymentResult.PaymentID,
Amount: order.TotalAmount,
PaidAt: time.Now(),
}
s.eventBus.Publish(ctx, "order.paid", event)

s.metrics.IncCounter("order_payment_success")
s.logger.Info("order payment processed successfully", "order_id", orderID)
} else {
s.metrics.IncCounter("order_payment_failed")
return NewPaymentError("支付失败: " + paymentResult.ErrorMessage)
}

return nil
}

2. 服务间通信

gRPC 通信实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// proto/user/user.proto
syntax = "proto3";

package user;
option go_package = "github.com/yourproject/proto/user";

import "google/protobuf/timestamp.proto";

service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}

message User {
string id = 1;
string username = 2;
string email = 3;
Profile profile = 4;
google.protobuf.Timestamp created_at = 5;
google.protobuf.Timestamp updated_at = 6;
bool is_active = 7;
}

message Profile {
string first_name = 1;
string last_name = 2;
string avatar = 3;
string bio = 4;
string location = 5;
string website = 6;
}

// gRPC 服务实现
package grpc

import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "github.com/yourproject/proto/user"
)

type userServer struct {
pb.UnimplementedUserServiceServer
userService user.Service
logger Logger
}

func NewUserServer(userService user.Service, logger Logger) pb.UserServiceServer {
return &userServer{
userService: userService,
logger: logger,
}
}

func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
userID, err := uuid.Parse(req.Id)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid user ID: %v", err)
}

user, err := s.userService.GetProfile(ctx, userID)
if err != nil {
if IsNotFoundError(err) {
return nil, status.Errorf(codes.NotFound, "user not found")
}
s.logger.Error("failed to get user", "user_id", userID, "error", err)
return nil, status.Errorf(codes.Internal, "internal server error")
}

return &pb.GetUserResponse{
User: s.convertUserToProto(user),
}, nil
}

func (s *userServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
registerReq := user.RegisterRequest{
Username: req.Username,
Email: req.Email,
Password: req.Password,
FirstName: req.Profile.FirstName,
LastName: req.Profile.LastName,
}

createdUser, err := s.userService.Register(ctx, registerReq)
if err != nil {
if IsValidationError(err) {
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}
if IsConflictError(err) {
return nil, status.Errorf(codes.AlreadyExists, err.Error())
}
s.logger.Error("failed to create user", "error", err)
return nil, status.Errorf(codes.Internal, "internal server error")
}

return &pb.CreateUserResponse{
User: s.convertUserToProto(createdUser),
}, nil
}

func (s *userServer) convertUserToProto(user *user.User) *pb.User {
return &pb.User{
Id: user.ID.String(),
Username: user.Username,
Email: user.Email,
Profile: &pb.Profile{
FirstName: user.Profile.FirstName,
LastName: user.Profile.LastName,
Avatar: user.Profile.Avatar,
Bio: user.Profile.Bio,
Location: user.Profile.Location,
Website: user.Profile.Website,
},
CreatedAt: timestamppb.New(user.CreatedAt),
UpdatedAt: timestamppb.New(user.UpdatedAt),
IsActive: user.IsActive,
}
}

// gRPC 客户端
type UserClient struct {
client pb.UserServiceClient
conn *grpc.ClientConn
logger Logger
}

func NewUserClient(address string, logger Logger) (*UserClient, error) {
conn, err := grpc.Dial(address,
grpc.WithInsecure(),
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(
grpc_retry.UnaryClientInterceptor(),
grpc_prometheus.UnaryClientInterceptor,
)),
)
if err != nil {
return nil, err
}

return &UserClient{
client: pb.NewUserServiceClient(conn),
conn: conn,
logger: logger,
}, nil
}

func (c *UserClient) GetUser(ctx context.Context, userID uuid.UUID) (*user.User, error) {
req := &pb.GetUserRequest{
Id: userID.String(),
}

resp, err := c.client.GetUser(ctx, req)
if err != nil {
c.logger.Error("failed to get user via gRPC", "user_id", userID, "error", err)
return nil, err
}

return c.convertProtoToUser(resp.User), nil
}

func (c *UserClient) Close() error {
return c.conn.Close()
}

事件驱动架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
// 事件总线实现
package eventbus

import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
)

// 事件接口
type Event interface {
EventType() string
EventID() string
Timestamp() time.Time
Payload() interface{}
}

// 基础事件结构
type BaseEvent struct {
ID string `json:"id"`
Type string `json:"type"`
Time time.Time `json:"timestamp"`
Data interface{} `json:"data"`
}

func (e BaseEvent) EventType() string { return e.Type }
func (e BaseEvent) EventID() string { return e.ID }
func (e BaseEvent) Timestamp() time.Time { return e.Time }
func (e BaseEvent) Payload() interface{} { return e.Data }

// 事件处理器接口
type Handler interface {
Handle(ctx context.Context, event Event) error
}

// 事件处理器函数类型
type HandlerFunc func(ctx context.Context, event Event) error

func (f HandlerFunc) Handle(ctx context.Context, event Event) error {
return f(ctx, event)
}

// 事件总线接口
type EventBus interface {
Publish(ctx context.Context, eventType string, data interface{}) error
Subscribe(eventType string, handler Handler) error
Unsubscribe(eventType string, handler Handler) error
Start(ctx context.Context) error
Stop() error
}

// 内存事件总线实现
type InMemoryEventBus struct {
handlers map[string][]Handler
mu sync.RWMutex
logger Logger
metrics Metrics
}

func NewInMemoryEventBus(logger Logger, metrics Metrics) *InMemoryEventBus {
return &InMemoryEventBus{
handlers: make(map[string][]Handler),
logger: logger,
metrics: metrics,
}
}

func (bus *InMemoryEventBus) Publish(ctx context.Context, eventType string, data interface{}) error {
event := BaseEvent{
ID: uuid.New().String(),
Type: eventType,
Time: time.Now(),
Data: data,
}

bus.mu.RLock()
handlers := bus.handlers[eventType]
bus.mu.RUnlock()

if len(handlers) == 0 {
bus.logger.Debug("no handlers for event type", "event_type", eventType)
return nil
}

// 异步处理事件
for _, handler := range handlers {
go func(h Handler) {
defer func() {
if r := recover(); r != nil {
bus.logger.Error("event handler panicked",
"event_type", eventType,
"event_id", event.ID,
"panic", r)
bus.metrics.IncCounter("event_handler_panic")
}
}()

start := time.Now()
err := h.Handle(ctx, event)
duration := time.Since(start)

if err != nil {
bus.logger.Error("event handler failed",
"event_type", eventType,
"event_id", event.ID,
"error", err)
bus.metrics.IncCounter("event_handler_error")
} else {
bus.metrics.IncCounter("event_handler_success")
}

bus.metrics.RecordDuration("event_handler_duration", duration,
map[string]string{"event_type": eventType})
}(handler)
}

bus.metrics.IncCounter("event_published")
bus.logger.Debug("event published", "event_type", eventType, "event_id", event.ID)

return nil
}

func (bus *InMemoryEventBus) Subscribe(eventType string, handler Handler) error {
bus.mu.Lock()
defer bus.mu.Unlock()

bus.handlers[eventType] = append(bus.handlers[eventType], handler)
bus.logger.Info("handler subscribed", "event_type", eventType)

return nil
}

// Redis 事件总线实现
type RedisEventBus struct {
client redis.Client
handlers map[string][]Handler
mu sync.RWMutex
logger Logger
metrics Metrics
done chan struct{}
}

func NewRedisEventBus(client redis.Client, logger Logger, metrics Metrics) *RedisEventBus {
return &RedisEventBus{
client: client,
handlers: make(map[string][]Handler),
logger: logger,
metrics: metrics,
done: make(chan struct{}),
}
}

func (bus *RedisEventBus) Publish(ctx context.Context, eventType string, data interface{}) error {
event := BaseEvent{
ID: uuid.New().String(),
Type: eventType,
Time: time.Now(),
Data: data,
}

eventData, err := json.Marshal(event)
if err != nil {
return fmt.Errorf("failed to marshal event: %w", err)
}

err = bus.client.Publish(ctx, eventType, eventData).Err()
if err != nil {
bus.logger.Error("failed to publish event to Redis",
"event_type", eventType,
"event_id", event.ID,
"error", err)
return err
}

bus.metrics.IncCounter("event_published")
bus.logger.Debug("event published to Redis", "event_type", eventType, "event_id", event.ID)

return nil
}

func (bus *RedisEventBus) Start(ctx context.Context) error {
bus.mu.RLock()
eventTypes := make([]string, 0, len(bus.handlers))
for eventType := range bus.handlers {
eventTypes = append(eventTypes, eventType)
}
bus.mu.RUnlock()

if len(eventTypes) == 0 {
return nil
}

pubsub := bus.client.Subscribe(ctx, eventTypes...)
defer pubsub.Close()

ch := pubsub.Channel()

for {
select {
case msg := <-ch:
bus.handleMessage(ctx, msg)
case <-bus.done:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
}

func (bus *RedisEventBus) handleMessage(ctx context.Context, msg *redis.Message) {
var event BaseEvent
if err := json.Unmarshal([]byte(msg.Payload), &event); err != nil {
bus.logger.Error("failed to unmarshal event", "error", err)
return
}

bus.mu.RLock()
handlers := bus.handlers[msg.Channel]
bus.mu.RUnlock()

for _, handler := range handlers {
go func(h Handler) {
defer func() {
if r := recover(); r != nil {
bus.logger.Error("event handler panicked",
"event_type", event.Type,
"event_id", event.ID,
"panic", r)
bus.metrics.IncCounter("event_handler_panic")
}
}()

start := time.Now()
err := h.Handle(ctx, event)
duration := time.Since(start)

if err != nil {
bus.logger.Error("event handler failed",
"event_type", event.Type,
"event_id", event.ID,
"error", err)
bus.metrics.IncCounter("event_handler_error")
} else {
bus.metrics.IncCounter("event_handler_success")
}

bus.metrics.RecordDuration("event_handler_duration", duration,
map[string]string{"event_type": event.Type})
}(handler)
}
}

// 事件处理器示例
type OrderEventHandler struct {
emailService EmailService
inventoryService InventoryService
logger Logger
}

func (h *OrderEventHandler) HandleOrderCreated(ctx context.Context, event Event) error {
var orderData OrderCreatedEvent
if err := mapstructure.Decode(event.Payload(), &orderData); err != nil {
return fmt.Errorf("failed to decode order created event: %w", err)
}

// 发送订单确认邮件
if err := h.emailService.SendOrderConfirmation(ctx, orderData.UserID, orderData.OrderID); err != nil {
h.logger.Error("failed to send order confirmation email",
"order_id", orderData.OrderID,
"error", err)
// 不返回错误,避免阻塞其他处理器
}

h.logger.Info("order created event processed", "order_id", orderData.OrderID)
return nil
}

func (h *OrderEventHandler) HandleOrderPaid(ctx context.Context, event Event) error {
var orderData OrderPaidEvent
if err := mapstructure.Decode(event.Payload(), &orderData); err != nil {
return fmt.Errorf("failed to decode order paid event: %w", err)
}

// 更新库存
if err := h.inventoryService.CommitReservation(ctx, orderData.OrderID); err != nil {
h.logger.Error("failed to commit inventory reservation",
"order_id", orderData.OrderID,
"error", err)
return err
}

// 发送支付成功邮件
if err := h.emailService.SendPaymentConfirmation(ctx, orderData.OrderID); err != nil {
h.logger.Error("failed to send payment confirmation email",
"order_id", orderData.OrderID,
"error", err)
}

h.logger.Info("order paid event processed", "order_id", orderData.OrderID)
return nil
}

🔧 服务治理

1. 服务注册与发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
// 服务注册与发现 - Consul 实现
package discovery

import (
"context"
"fmt"
"net"
"strconv"
"time"

"github.com/hashicorp/consul/api"
)

// 服务注册接口
type ServiceRegistry interface {
Register(ctx context.Context, service *ServiceInfo) error
Deregister(ctx context.Context, serviceID string) error
Discover(ctx context.Context, serviceName string) ([]*ServiceInfo, error)
Watch(ctx context.Context, serviceName string) (<-chan []*ServiceInfo, error)
}

// 服务信息
type ServiceInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
Port int `json:"port"`
Tags []string `json:"tags"`
Meta map[string]string `json:"meta"`
Health HealthCheck `json:"health"`
}

type HealthCheck struct {
HTTP string `json:"http,omitempty"`
TCP string `json:"tcp,omitempty"`
Interval time.Duration `json:"interval"`
Timeout time.Duration `json:"timeout"`
DeregisterCriticalServiceAfter time.Duration `json:"deregister_critical_service_after"`
}

// Consul 服务注册实现
type ConsulRegistry struct {
client *api.Client
logger Logger
}

func NewConsulRegistry(address string, logger Logger) (*ConsulRegistry, error) {
config := api.DefaultConfig()
config.Address = address

client, err := api.NewClient(config)
if err != nil {
return nil, fmt.Errorf("failed to create consul client: %w", err)
}

return &ConsulRegistry{
client: client,
logger: logger,
}, nil
}

func (r *ConsulRegistry) Register(ctx context.Context, service *ServiceInfo) error {
registration := &api.AgentServiceRegistration{
ID: service.ID,
Name: service.Name,
Address: service.Address,
Port: service.Port,
Tags: service.Tags,
Meta: service.Meta,
}

// 配置健康检查
if service.Health.HTTP != "" {
registration.Check = &api.AgentServiceCheck{
HTTP: service.Health.HTTP,
Interval: service.Health.Interval.String(),
Timeout: service.Health.Timeout.String(),
DeregisterCriticalServiceAfter: service.Health.DeregisterCriticalServiceAfter.String(),
}
} else if service.Health.TCP != "" {
registration.Check = &api.AgentServiceCheck{
TCP: service.Health.TCP,
Interval: service.Health.Interval.String(),
Timeout: service.Health.Timeout.String(),
DeregisterCriticalServiceAfter: service.Health.DeregisterCriticalServiceAfter.String(),
}
}

err := r.client.Agent().ServiceRegister(registration)
if err != nil {
r.logger.Error("failed to register service",
"service_id", service.ID,
"service_name", service.Name,
"error", err)
return fmt.Errorf("failed to register service: %w", err)
}

r.logger.Info("service registered successfully",
"service_id", service.ID,
"service_name", service.Name,
"address", fmt.Sprintf("%s:%d", service.Address, service.Port))

return nil
}

func (r *ConsulRegistry) Discover(ctx context.Context, serviceName string) ([]*ServiceInfo, error) {
services, _, err := r.client.Health().Service(serviceName, "", true, nil)
if err != nil {
r.logger.Error("failed to discover services",
"service_name", serviceName,
"error", err)
return nil, fmt.Errorf("failed to discover services: %w", err)
}

var result []*ServiceInfo
for _, service := range services {
serviceInfo := &ServiceInfo{
ID: service.Service.ID,
Name: service.Service.Service,
Address: service.Service.Address,
Port: service.Service.Port,
Tags: service.Service.Tags,
Meta: service.Service.Meta,
}
result = append(result, serviceInfo)
}

r.logger.Debug("services discovered",
"service_name", serviceName,
"count", len(result))

return result, nil
}

func (r *ConsulRegistry) Watch(ctx context.Context, serviceName string) (<-chan []*ServiceInfo, error) {
ch := make(chan []*ServiceInfo)

go func() {
defer close(ch)

var lastIndex uint64
for {
select {
case <-ctx.Done():
return
default:
queryOptions := &api.QueryOptions{
WaitIndex: lastIndex,
WaitTime: 30 * time.Second,
}

services, meta, err := r.client.Health().Service(serviceName, "", true, queryOptions)
if err != nil {
r.logger.Error("failed to watch services",
"service_name", serviceName,
"error", err)
time.Sleep(5 * time.Second)
continue
}

if meta.LastIndex != lastIndex {
lastIndex = meta.LastIndex

var result []*ServiceInfo
for _, service := range services {
serviceInfo := &ServiceInfo{
ID: service.Service.ID,
Name: service.Service.Service,
Address: service.Service.Address,
Port: service.Service.Port,
Tags: service.Service.Tags,
Meta: service.Service.Meta,
}
result = append(result, serviceInfo)
}

select {
case ch <- result:
case <-ctx.Done():
return
}
}
}
}
}()

return ch, nil
}

// 服务管理器
type ServiceManager struct {
registry ServiceRegistry
services map[string]*ServiceInfo
mu sync.RWMutex
logger Logger
}

func NewServiceManager(registry ServiceRegistry, logger Logger) *ServiceManager {
return &ServiceManager{
registry: registry,
services: make(map[string]*ServiceInfo),
logger: logger,
}
}

func (sm *ServiceManager) RegisterService(ctx context.Context, name, address string, port int) error {
// 获取本机IP
if address == "" {
ip, err := getLocalIP()
if err != nil {
return fmt.Errorf("failed to get local IP: %w", err)
}
address = ip
}

serviceID := fmt.Sprintf("%s-%s-%d", name, address, port)

service := &ServiceInfo{
ID: serviceID,
Name: name,
Address: address,
Port: port,
Tags: []string{"go", "microservice"},
Meta: map[string]string{
"version": "1.0.0",
"framework": "go",
},
Health: HealthCheck{
HTTP: fmt.Sprintf("http://%s:%d/health", address, port),
Interval: 10 * time.Second,
Timeout: 3 * time.Second,
DeregisterCriticalServiceAfter: 30 * time.Second,
},
}

err := sm.registry.Register(ctx, service)
if err != nil {
return err
}

sm.mu.Lock()
sm.services[serviceID] = service
sm.mu.Unlock()

return nil
}

func (sm *ServiceManager) DeregisterAll(ctx context.Context) error {
sm.mu.RLock()
services := make([]*ServiceInfo, 0, len(sm.services))
for _, service := range sm.services {
services = append(services, service)
}
sm.mu.RUnlock()

for _, service := range services {
if err := sm.registry.Deregister(ctx, service.ID); err != nil {
sm.logger.Error("failed to deregister service",
"service_id", service.ID,
"error", err)
}
}

return nil
}

func getLocalIP() (string, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", err
}

for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
}

return "", fmt.Errorf("no valid IP address found")
}

2. 负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
// 负载均衡器实现
package loadbalancer

import (
"context"
"errors"
"math/rand"
"sync"
"sync/atomic"
"time"
)

// 负载均衡策略
type Strategy string

const (
RoundRobin Strategy = "round_robin"
Random Strategy = "random"
WeightedRandom Strategy = "weighted_random"
LeastConn Strategy = "least_conn"
ConsistentHash Strategy = "consistent_hash"
)

// 服务实例
type Instance struct {
ID string
Address string
Port int
Weight int
Active int64 // 当前活跃连接数
Healthy bool
Metadata map[string]string
}

func (i *Instance) Endpoint() string {
return fmt.Sprintf("%s:%d", i.Address, i.Port)
}

func (i *Instance) IncrActive() {
atomic.AddInt64(&i.Active, 1)
}

func (i *Instance) DecrActive() {
atomic.AddInt64(&i.Active, -1)
}

func (i *Instance) GetActive() int64 {
return atomic.LoadInt64(&i.Active)
}

// 负载均衡器接口
type LoadBalancer interface {
Select(ctx context.Context, key string) (*Instance, error)
UpdateInstances(instances []*Instance)
GetInstances() []*Instance
}

// 轮询负载均衡器
type RoundRobinBalancer struct {
instances []*Instance
current uint64
mu sync.RWMutex
}

func NewRoundRobinBalancer() *RoundRobinBalancer {
return &RoundRobinBalancer{
instances: make([]*Instance, 0),
}
}

func (rb *RoundRobinBalancer) Select(ctx context.Context, key string) (*Instance, error) {
rb.mu.RLock()
defer rb.mu.RUnlock()

if len(rb.instances) == 0 {
return nil, errors.New("no available instances")
}

// 过滤健康的实例
healthyInstances := make([]*Instance, 0)
for _, instance := range rb.instances {
if instance.Healthy {
healthyInstances = append(healthyInstances, instance)
}
}

if len(healthyInstances) == 0 {
return nil, errors.New("no healthy instances")
}

// 轮询选择
index := atomic.AddUint64(&rb.current, 1) % uint64(len(healthyInstances))
return healthyInstances[index], nil
}

func (rb *RoundRobinBalancer) UpdateInstances(instances []*Instance) {
rb.mu.Lock()
defer rb.mu.Unlock()
rb.instances = instances
}

func (rb *RoundRobinBalancer) GetInstances() []*Instance {
rb.mu.RLock()
defer rb.mu.RUnlock()

result := make([]*Instance, len(rb.instances))
copy(result, rb.instances)
return result
}

// 加权随机负载均衡器
type WeightedRandomBalancer struct {
instances []*Instance
mu sync.RWMutex
rand *rand.Rand
}

func NewWeightedRandomBalancer() *WeightedRandomBalancer {
return &WeightedRandomBalancer{
instances: make([]*Instance, 0),
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
}
}

func (wrb *WeightedRandomBalancer) Select(ctx context.Context, key string) (*Instance, error) {
wrb.mu.RLock()
defer wrb.mu.RUnlock()

if len(wrb.instances) == 0 {
return nil, errors.New("no available instances")
}

// 过滤健康的实例并计算总权重
healthyInstances := make([]*Instance, 0)
totalWeight := 0
for _, instance := range wrb.instances {
if instance.Healthy {
healthyInstances = append(healthyInstances, instance)
totalWeight += instance.Weight
}
}

if len(healthyInstances) == 0 {
return nil, errors.New("no healthy instances")
}

if totalWeight == 0 {
// 如果所有权重都为0,使用随机选择
index := wrb.rand.Intn(len(healthyInstances))
return healthyInstances[index], nil
}

// 加权随机选择
randomWeight := wrb.rand.Intn(totalWeight)
currentWeight := 0

for _, instance := range healthyInstances {
currentWeight += instance.Weight
if randomWeight < currentWeight {
return instance, nil
}
}

// 理论上不应该到达这里
return healthyInstances[len(healthyInstances)-1], nil
}

func (wrb *WeightedRandomBalancer) UpdateInstances(instances []*Instance) {
wrb.mu.Lock()
defer wrb.mu.Unlock()
wrb.instances = instances
}

func (wrb *WeightedRandomBalancer) GetInstances() []*Instance {
wrb.mu.RLock()
defer wrb.mu.RUnlock()

result := make([]*Instance, len(wrb.instances))
copy(result, wrb.instances)
return result
}

// 最少连接负载均衡器
type LeastConnBalancer struct {
instances []*Instance
mu sync.RWMutex
}

func NewLeastConnBalancer() *LeastConnBalancer {
return &LeastConnBalancer{
instances: make([]*Instance, 0),
}
}

func (lcb *LeastConnBalancer) Select(ctx context.Context, key string) (*Instance, error) {
lcb.mu.RLock()
defer lcb.mu.RUnlock()

if len(lcb.instances) == 0 {
return nil, errors.New("no available instances")
}

// 过滤健康的实例
healthyInstances := make([]*Instance, 0)
for _, instance := range lcb.instances {
if instance.Healthy {
healthyInstances = append(healthyInstances, instance)
}
}

if len(healthyInstances) == 0 {
return nil, errors.New("no healthy instances")
}

// 选择连接数最少的实例
var selected *Instance
minConn := int64(-1)

for _, instance := range healthyInstances {
activeConn := instance.GetActive()
if minConn == -1 || activeConn < minConn {
minConn = activeConn
selected = instance
}
}

return selected, nil
}

func (lcb *LeastConnBalancer) UpdateInstances(instances []*Instance) {
lcb.mu.Lock()
defer lcb.mu.Unlock()
lcb.instances = instances
}

func (lcb *LeastConnBalancer) GetInstances() []*Instance {
lcb.mu.RLock()
defer lcb.mu.RUnlock()

result := make([]*Instance, len(lcb.instances))
copy(result, lcb.instances)
return result
}

// 负载均衡器工厂
func NewLoadBalancer(strategy Strategy) LoadBalancer {
switch strategy {
case RoundRobin:
return NewRoundRobinBalancer()
case WeightedRandom:
return NewWeightedRandomBalancer()
case LeastConn:
return NewLeastConnBalancer()
default:
return NewRoundRobinBalancer()
}
}

// 服务发现集成的负载均衡器
type ServiceLoadBalancer struct {
serviceName string
registry ServiceRegistry
balancer LoadBalancer
instances map[string]*Instance
mu sync.RWMutex
logger Logger
stopCh chan struct{}
}

func NewServiceLoadBalancer(serviceName string, registry ServiceRegistry, strategy Strategy, logger Logger) *ServiceLoadBalancer {
return &ServiceLoadBalancer{
serviceName: serviceName,
registry: registry,
balancer: NewLoadBalancer(strategy),
instances: make(map[string]*Instance),
logger: logger,
stopCh: make(chan struct{}),
}
}

func (slb *ServiceLoadBalancer) Start(ctx context.Context) error {
// 初始化服务实例
if err := slb.updateInstances(ctx); err != nil {
return err
}

// 监听服务变化
watchCh, err := slb.registry.Watch(ctx, slb.serviceName)
if err != nil {
return err
}

go func() {
for {
select {
case services := <-watchCh:
slb.handleServiceUpdate(services)
case <-slb.stopCh:
return
case <-ctx.Done():
return
}
}
}()

return nil
}

func (slb *ServiceLoadBalancer) Stop() {
close(slb.stopCh)
}

func (slb *ServiceLoadBalancer) Select(ctx context.Context, key string) (*Instance, error) {
return slb.balancer.Select(ctx, key)
}

func (slb *ServiceLoadBalancer) updateInstances(ctx context.Context) error {
services, err := slb.registry.Discover(ctx, slb.serviceName)
if err != nil {
return err
}

slb.handleServiceUpdate(services)
return nil
}

func (slb *ServiceLoadBalancer) handleServiceUpdate(services []*ServiceInfo) {
slb.mu.Lock()
defer slb.mu.Unlock()

// 创建新的实例映射
newInstances := make(map[string]*Instance)
instances := make([]*Instance, 0, len(services))

for _, service := range services {
instance := &Instance{
ID: service.ID,
Address: service.Address,
Port: service.Port,
Weight: 1, // 默认权重
Healthy: true,
Metadata: service.Meta,
}

// 如果实例已存在,保留其状态
if existingInstance, exists := slb.instances[service.ID]; exists {
instance.Active = existingInstance.Active
instance.Weight = existingInstance.Weight
}

newInstances[service.ID] = instance
instances = append(instances, instance)
}

slb.instances = newInstances
slb.balancer.UpdateInstances(instances)

slb.logger.Info("service instances updated",
"service_name", slb.serviceName,
"instance_count", len(instances))
}

🔒 安全与认证

JWT 认证实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// JWT 认证服务
package auth

import (
"context"
"crypto/rsa"
"fmt"
"time"

"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
)

// JWT 管理器接口
type JWTManager interface {
Generate(userID uuid.UUID, email string) (string, error)
Verify(tokenString string) (*Claims, error)
Refresh(tokenString string) (string, error)
}

// JWT 声明
type Claims struct {
UserID uuid.UUID `json:"user_id"`
Email string `json:"email"`
Role string `json:"role"`
jwt.RegisteredClaims
}

// JWT 管理器实现
type jwtManager struct {
privateKey *rsa.PrivateKey
publicKey *rsa.PublicKey
issuer string
expiry time.Duration
}

func NewJWTManager(privateKey *rsa.PrivateKey, publicKey *rsa.PublicKey, issuer string, expiry time.Duration) JWTManager {
return &jwtManager{
privateKey: privateKey,
publicKey: publicKey,
issuer: issuer,
expiry: expiry,
}
}

func (j *jwtManager) Generate(userID uuid.UUID, email string) (string, error) {
now := time.Now()
claims := &Claims{
UserID: userID,
Email: email,
Role: "user", // 可以根据用户信息设置角色
RegisteredClaims: jwt.RegisteredClaims{
Issuer: j.issuer,
Subject: userID.String(),
Audience: []string{"api"},
ExpiresAt: jwt.NewNumericDate(now.Add(j.expiry)),
NotBefore: jwt.NewNumericDate(now),
IssuedAt: jwt.NewNumericDate(now),
ID: uuid.New().String(),
},
}

token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
return token.SignedString(j.privateKey)
}

func (j *jwtManager) Verify(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return j.publicKey, nil
})

if err != nil {
return nil, err
}

if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}

return nil, fmt.Errorf("invalid token")
}

func (j *jwtManager) Refresh(tokenString string) (string, error) {
claims, err := j.Verify(tokenString)
if err != nil {
return "", err
}

// 检查令牌是否即将过期(在过期前30分钟内可以刷新)
if time.Until(claims.ExpiresAt.Time) > 30*time.Minute {
return "", fmt.Errorf("token is not eligible for refresh")
}

return j.Generate(claims.UserID, claims.Email)
}

// 认证中间件
func AuthMiddleware(jwtManager JWTManager) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Authorization header required", http.StatusUnauthorized)
return
}

const bearerPrefix = "Bearer "
if !strings.HasPrefix(authHeader, bearerPrefix) {
http.Error(w, "Invalid authorization header format", http.StatusUnauthorized)
return
}

tokenString := authHeader[len(bearerPrefix):]
claims, err := jwtManager.Verify(tokenString)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}

// 将用户信息添加到上下文
ctx := context.WithValue(r.Context(), "user_id", claims.UserID)
ctx = context.WithValue(ctx, "user_email", claims.Email)
ctx = context.WithValue(ctx, "user_role", claims.Role)

next.ServeHTTP(w, r.WithContext(ctx))
})
}
}

// 从上下文获取用户信息的辅助函数
func GetUserIDFromContext(ctx context.Context) (uuid.UUID, error) {
userID, ok := ctx.Value("user_id").(uuid.UUID)
if !ok {
return uuid.Nil, fmt.Errorf("user ID not found in context")
}
return userID, nil
}

func GetUserEmailFromContext(ctx context.Context) (string, error) {
email, ok := ctx.Value("user_email").(string)
if !ok {
return "", fmt.Errorf("user email not found in context")
}
return email, nil
}

// 角色权限检查中间件
func RequireRole(role string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userRole, ok := r.Context().Value("user_role").(string)
if !ok {
http.Error(w, "User role not found", http.StatusForbidden)
return
}

if userRole != role && userRole != "admin" { // admin 角色拥有所有权限
http.Error(w, "Insufficient permissions", http.StatusForbidden)
return
}

next.ServeHTTP(w, r)
})
}
}

📊 监控与可观测性

1. 指标收集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// 指标收集系统
package metrics

import (
"context"
"net/http"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// 指标收集器
type Collector struct {
// HTTP 请求指标
httpRequestsTotal *prometheus.CounterVec
httpRequestDuration *prometheus.HistogramVec
httpRequestsInFlight prometheus.Gauge

// 业务指标
businessOperationsTotal *prometheus.CounterVec
businessOperationDuration *prometheus.HistogramVec

// 系统指标
goroutinesCount prometheus.Gauge
memoryUsage prometheus.Gauge

registry *prometheus.Registry
}

func NewCollector() *Collector {
c := &Collector{
httpRequestsTotal: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "path", "status"},
),
httpRequestDuration: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request duration in seconds",
Buckets: prometheus.DefBuckets,
},
[]string{"method", "path"},
),
httpRequestsInFlight: prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "http_requests_in_flight",
Help: "Number of HTTP requests currently being processed",
},
),
businessOperationsTotal: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "business_operations_total",
Help: "Total number of business operations",
},
[]string{"operation", "status"},
),
businessOperationDuration: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "business_operation_duration_seconds",
Help: "Business operation duration in seconds",
Buckets: prometheus.DefBuckets,
},
[]string{"operation"},
),
goroutinesCount: prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "goroutines_count",
Help: "Number of goroutines",
},
),
memoryUsage: prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "memory_usage_bytes",
Help: "Memory usage in bytes",
},
),
registry: prometheus.NewRegistry(),
}

// 注册指标
c.registry.MustRegister(
c.httpRequestsTotal,
c.httpRequestDuration,
c.httpRequestsInFlight,
c.businessOperationsTotal,
c.businessOperationDuration,
c.goroutinesCount,
c.memoryUsage,
)

return c
}

// HTTP 指标中间件
func (c *Collector) HTTPMetricsMiddleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
c.httpRequestsInFlight.Inc()
defer c.httpRequestsInFlight.Dec()

// 包装 ResponseWriter 以捕获状态码
wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}

next.ServeHTTP(wrapped, r)

duration := time.Since(start).Seconds()
status := fmt.Sprintf("%d", wrapped.statusCode)

c.httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path, status).Inc()
c.httpRequestDuration.WithLabelValues(r.Method, r.URL.Path).Observe(duration)
})
}
}

type responseWriter struct {
http.ResponseWriter
statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}

// 业务操作指标记录
func (c *Collector) RecordBusinessOperation(operation string, duration time.Duration, success bool) {
status := "success"
if !success {
status = "error"
}

c.businessOperationsTotal.WithLabelValues(operation, status).Inc()
c.businessOperationDuration.WithLabelValues(operation).Observe(duration.Seconds())
}

// 系统指标更新
func (c *Collector) UpdateSystemMetrics() {
c.goroutinesCount.Set(float64(runtime.NumGoroutine()))

var m runtime.MemStats
runtime.ReadMemStats(&m)
c.memoryUsage.Set(float64(m.Alloc))
}

// 启动指标收集服务
func (c *Collector) StartMetricsServer(addr string) error {
// 定期更新系统指标
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()

for range ticker.C {
c.UpdateSystemMetrics()
}
}()

http.Handle("/metrics", promhttp.HandlerFor(c.registry, promhttp.HandlerOpts{}))
return http.ListenAndServe(addr, nil)
}

2. 分布式链路追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// 分布式链路追踪
package tracing

import (
"context"
"fmt"
"net/http"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
oteltrace "go.opentelemetry.io/otel/trace"
)

// 初始化链路追踪
func InitTracing(serviceName, jaegerEndpoint string) (func(), error) {
// 创建 Jaeger 导出器
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(jaegerEndpoint)))
if err != nil {
return nil, fmt.Errorf("failed to create jaeger exporter: %w", err)
}

// 创建资源
res, err := resource.New(context.Background(),
resource.WithAttributes(
semconv.ServiceNameKey.String(serviceName),
semconv.ServiceVersionKey.String("1.0.0"),
),
)
if err != nil {
return nil, fmt.Errorf("failed to create resource: %w", err)
}

// 创建追踪提供者
tp := trace.NewTracerProvider(
trace.WithBatcher(exp),
trace.WithResource(res),
trace.WithSampler(trace.AlwaysSample()),
)

// 设置全局追踪提供者
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))

return func() {
if err := tp.Shutdown(context.Background()); err != nil {
fmt.Printf("Error shutting down tracer provider: %v\n", err)
}
}, nil
}

// HTTP 追踪中间件
func TracingMiddleware(serviceName string) func(http.Handler) http.Handler {
tracer := otel.Tracer(serviceName)

return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))

spanName := fmt.Sprintf("%s %s", r.Method, r.URL.Path)
ctx, span := tracer.Start(ctx, spanName,
oteltrace.WithAttributes(
semconv.HTTPMethodKey.String(r.Method),
semconv.HTTPURLKey.String(r.URL.String()),
semconv.HTTPUserAgentKey.String(r.UserAgent()),
),
)
defer span.End()

// 包装 ResponseWriter 以捕获状态码
wrapped := &tracingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}

next.ServeHTTP(wrapped, r.WithContext(ctx))

span.SetAttributes(
semconv.HTTPStatusCodeKey.Int(wrapped.statusCode),
)

if wrapped.statusCode >= 400 {
span.RecordError(fmt.Errorf("HTTP %d", wrapped.statusCode))
}
})
}
}

type tracingResponseWriter struct {
http.ResponseWriter
statusCode int
}

func (rw *tracingResponseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}

// gRPC 追踪拦截器
func UnaryServerInterceptor(serviceName string) grpc.UnaryServerInterceptor {
tracer := otel.Tracer(serviceName)

return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
ctx, span := tracer.Start(ctx, info.FullMethod,
oteltrace.WithAttributes(
attribute.String("rpc.system", "grpc"),
attribute.String("rpc.service", serviceName),
attribute.String("rpc.method", info.FullMethod),
),
)
defer span.End()

resp, err := handler(ctx, req)
if err != nil {
span.RecordError(err)
span.SetAttributes(attribute.String("error", err.Error()))
}

return resp, err
}
}

// 业务操作追踪辅助函数
func StartSpan(ctx context.Context, operationName string, attributes ...attribute.KeyValue) (context.Context, oteltrace.Span) {
tracer := otel.Tracer("business-operations")
return tracer.Start(ctx, operationName, oteltrace.WithAttributes(attributes...))
}

func RecordError(span oteltrace.Span, err error) {
if err != nil {
span.RecordError(err)
span.SetAttributes(attribute.String("error", err.Error()))
}
}

🚀 部署与运维

1. Docker 容器化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Dockerfile
FROM golang:1.21-alpine AS builder

WORKDIR /app

# 安装依赖
RUN apk add --no-cache git ca-certificates tzdata

# 复制 go mod 文件
COPY go.mod go.sum ./
RUN go mod download

# 复制源代码
COPY . .

# 构建应用
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/server

# 运行阶段
FROM alpine:latest

RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/

# 从构建阶段复制二进制文件
COPY --from=builder /app/main .
COPY --from=builder /app/configs ./configs

# 创建非 root 用户
RUN adduser -D -s /bin/sh appuser
USER appuser

EXPOSE 8080

CMD ["./main"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# docker-compose.yml
version: '3.8'

services:
# 用户服务
user-service:
build:
context: ./user-service
dockerfile: Dockerfile
ports:
- "8081:8080"
environment:
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=userdb
- DB_USER=postgres
- DB_PASSWORD=password
- REDIS_HOST=redis
- REDIS_PORT=6379
- CONSUL_HOST=consul
- CONSUL_PORT=8500
depends_on:
- postgres
- redis
- consul
networks:
- microservices

# 订单服务
order-service:
build:
context: ./order-service
dockerfile: Dockerfile
ports:
- "8082:8080"
environment:
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=orderdb
- DB_USER=postgres
- DB_PASSWORD=password
- REDIS_HOST=redis
- REDIS_PORT=6379
- CONSUL_HOST=consul
- CONSUL_PORT=8500
depends_on:
- postgres
- redis
- consul
networks:
- microservices

# API 网关
api-gateway:
build:
context: ./api-gateway
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- CONSUL_HOST=consul
- CONSUL_PORT=8500
depends_on:
- consul
networks:
- microservices

# PostgreSQL 数据库
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=microservices
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
networks:
- microservices

# Redis 缓存
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- microservices

# Consul 服务发现
consul:
image: consul:1.16
ports:
- "8500:8500"
command: agent -server -bootstrap -ui -client=0.0.0.0
volumes:
- consul_data:/consul/data
networks:
- microservices

# Jaeger 链路追踪
jaeger:
image: jaegertracing/all-in-one:1.49
ports:
- "16686:16686"
- "14268:14268"
environment:
- COLLECTOR_OTLP_ENABLED=true
networks:
- microservices

# Prometheus 监控
prometheus:
image: prom/prometheus:v2.47.0
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
networks:
- microservices

# Grafana 可视化
grafana:
image: grafana/grafana:10.1.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources
networks:
- microservices

volumes:
postgres_data:
redis_data:
consul_data:
prometheus_data:
grafana_data:

networks:
microservices:
driver: bridge

2. Kubernetes 部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: microservices

---
# k8s/user-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
namespace: microservices
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: user-service:latest
ports:
- containerPort: 8080
env:
- name: DB_HOST
value: "postgres-service"
- name: DB_PORT
value: "5432"
- name: DB_NAME
value: "userdb"
- name: DB_USER
valueFrom:
secretKeyRef:
name: postgres-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: REDIS_HOST
value: "redis-service"
- name: CONSUL_HOST
value: "consul-service"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
name: user-service
namespace: microservices
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP

---
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: microservices
data:
app.yaml: |
server:
port: 8080
read_timeout: 30s
write_timeout: 30s

database:
max_open_conns: 25
max_idle_conns: 5
conn_max_lifetime: 300s

redis:
pool_size: 10
min_idle_conns: 5

logging:
level: info
format: json

---
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
namespace: microservices
type: Opaque
data:
username: cG9zdGdyZXM= # postgres (base64)
password: cGFzc3dvcmQ= # password (base64)

---
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
namespace: microservices
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80

🎯 最佳实践总结

1. 架构设计原则

  • 单一职责: 每个服务只负责一个业务领域
  • 松耦合: 服务间通过明确定义的接口通信
  • 高内聚: 相关功能聚合在同一个服务内
  • 数据独立: 每个服务拥有独立的数据存储
  • 故障隔离: 一个服务的故障不应影响其他服务

2. 开发规范

  • API 设计: 遵循 RESTful 设计原则,使用标准 HTTP 状态码
  • 错误处理: 统一的错误处理和响应格式
  • 日志记录: 结构化日志,包含请求 ID 用于链路追踪
  • 配置管理: 外部化配置,支持不同环境
  • 版本控制: API 版本管理,向后兼容

3. 运维监控

  • 健康检查: 实现 liveness 和 readiness 探针
  • 指标监控: 收集业务和技术指标
  • 链路追踪: 分布式请求追踪
  • 日志聚合: 集中化日志管理
  • 告警机制: 及时发现和响应问题

结语

Go语言在微服务架构中展现出了强大的优势,其出色的并发性能、简洁的语法和丰富的生态系统,使得构建高质量的微服务变得更加容易。

通过本文的深入探讨,我们了解了从服务拆分、通信机制到部署运维的完整微服务架构实践。在实际项目中,需要根据业务特点和团队情况,选择合适的技术栈和架构模式。

记住,微服务不是银弹,它带来了分布式系统的复杂性。在选择微服务架构时,要权衡其带来的好处和成本,确保团队有足够的能力来管理这种复杂性。

最重要的是,要建立完善的监控和运维体系,确保系统的稳定性和可维护性。只有这样,才能真正发挥微服务架构的优势,构建出高可用、可扩展的分布式系统。


你在微服务架构实践中遇到过哪些挑战?欢迎在评论区分享你的经验和见解!