Go 分布式系统实战:Kafka + gRPC + Etcd 构建消息队列系统
《一条龙开发指南:MCP AI Agent 理论+项目实战开发你的MCP Server》
🧑💻 面试人物设定
- 姓名: 赵子昂
- 年龄: 31 岁
- 学历: 计算机硕士
- 工作年限: 6 年
- 公司背景: 某头部物流科技平台
- 技术栈: Go, Kafka, gRPC, Etcd, Docker
- 核心职责:
- 使用 Go 构建分布式任务调度系统
- 结合 Kafka 实现异步消息处理
- 利用 Etcd 实现服务注册与发现
- 设计高可用架构保障系统稳定性
- 工作成果:
- 成功上线多个任务处理模块,日均处理消息量提升至千万级
- 将任务失败率控制在 0.01% 以下
🎤 第一轮面试:Go 分布式基础考察
面试官: “你好,请介绍一下你最近参与的 Go 项目。”
程序员: “您好!我最近主要负责一个基于 Kafka 和 gRPC 的分布式任务调度系统重构项目,使用 Etcd 实现服务注册与发现,并通过 Docker 容器化部署提升系统可扩展性。”
面试官: “你能写一个简单的 Go 并发任务吗?”
程序员: “当然可以,比如使用 Goroutine 和 Channel 实现并发任务。”
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for job := range jobs {
fmt.Printf("Worker %d started job %d\n", id, job)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished job %d\n", id, job)
results <- job * 2
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
for w := 1; w <= 3; w++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d started job %d\n", id, job)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished job %d\n", id, job)
results <- job * 2
}
}(w)
}
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
wg.Wait()
close(results)
for result := range results {
fmt.Println("Result:", result)
}
}
🧠 第二轮面试:Kafka 消息队列集成
面试官: “你是怎么使用 Kafka 的?”
程序员: “我们会使用 Sarama 库实现生产者和消费者的逻辑,并确保消息可靠投递。”
// 生产者示例
package main
import (
"fmt"
"github.com/Shopify/sarama"
)
func main() {
config := sarama.NewConfig()
config.Producer.RequiredAcks = sarama.WaitForAll
config.Producer.Partitioner = sarama.NewRandomPartitioner
config.Producer.Return.Successes = true
producer, err := sarama.NewSyncProducer([]string{"localhost:9092"}, config)
if err != nil {
panic(err)
}
defer producer.Close()
msg := &sarama.ProducerMessage{
Topic: "test",
Value: sarama.StringEncoder("Hello Kafka"),
}
partition, offset, err := producer.SendMessage(msg)
if err != nil {
fmt.Println("Send message failed, err:", err)
return
}
fmt.Printf("Message sent to partition %d at offset %d\n", partition, offset)
}
⚙️ 第三轮面试:gRPC 服务通信
面试官: “你们是怎么使用 gRPC 的?”
程序员: “我们会定义 Protobuf 接口,并生成服务代码用于跨服务调用。”
// service.proto
syntax = "proto3";
option go_package = ".;pb";
package task;
service TaskService {
rpc SubmitTask (TaskRequest) returns (TaskResponse);
rpc GetTaskStatus (TaskID) returns (TaskStatus);
}
message TaskRequest {
string task_id = 1;
string payload = 2;
}
message TaskResponse {
string status = 1;
string message = 2;
}
message TaskID {
string task_id = 1;
}
message TaskStatus {
string status = 1;
string result = 2;
}
// 服务端实现
func (s *server) SubmitTask(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
fmt.Printf("Received task: %v\n", req)
// 处理任务逻辑
return &pb.TaskResponse{Status: "success", Message: "Task processed"}, nil
}
// 客户端调用
conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
defer conn.Close()
c := pb.NewTaskServiceClient(conn)
resp, _ := c.SubmitTask(context.Background(), &pb.TaskRequest{TaskId: "123", Payload: "data"})
fmt.Println("Response:", resp)
📊 第四轮面试:Etcd 服务注册与发现
面试官: “你是怎么使用 Etcd 的?”
程序员: “我们使用 Etcd 实现服务注册与发现,并设计心跳机制确保服务可用性。”
package main
import (
"context"
"fmt"
"time"
"go.etcd.io/etcd/client/v3"
)
func main() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
})
if err != nil {
panic(err)
}
defer cli.Close()
// 注册服务
leaseGrantResp, _ := cli.LeaseGrant(context.TODO(), 10)
cli.Put(context.TODO(), "/services/task-service/127.0.0.1:50051", "active", clientv3.WithLease(leaseGrantResp.ID))
fmt.Println("Service registered")
// 心跳续约
ch, _ := cli.KeepAlive(context.TODO(), leaseGrantResp.ID)
for {
select {
case <-ch:
// 续约成功
default:
time.Sleep(2 * time.Second)
}
}
}
📈 第五轮面试:服务部署与监控
面试官: “你是怎么部署和监控服务的?”
程序员: “我们主要使用 Docker 容器化部署,并结合 Prometheus + Grafana 实现可视化监控。”
# Dockerfile
FROM golang:1.21
WORKDIR /app
COPY . .
RUN go mod download && go build -o task-service
CMD ["./task-service"]
# 构建镜像
docker build -t task-service:latest .
# 启动容器
docker run -d -p 50051:50051 --name task-service task-service:latest
💬 结尾环节
面试官: “今天的面试就到这里,我们会综合评估你的表现,后续 HR 会联系你。”
程序员: “谢谢您今天的时间,期待有机会加入贵公司。”