惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
Martin Fowler
Martin Fowler
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Stack Overflow Blog
Stack Overflow Blog
C
Check Point Blog
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog
O
OpenAI News
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
V
Vulnerabilities – Threatpost
P
Privacy & Cybersecurity Law Blog
Microsoft Azure Blog
Microsoft Azure Blog
H
Help Net Security
博客园 - 三生石上(FineUI控件)
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News | PayPal Newsroom
The Register - Security
The Register - Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Threat Research - Cisco Blogs
C
Cisco Blogs
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Vercel News
Vercel News
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Attack and Defense Labs
Attack and Defense Labs
人人都是产品经理
人人都是产品经理
S
Security Affairs
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
T
Tor Project blog
大猫的无限游戏
大猫的无限游戏
L
Lohrmann on Cybersecurity
aimingoo的专栏
aimingoo的专栏
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 【当耐特】
V
V2EX
V
Visual Studio Blog
The GitHub Blog
The GitHub Blog
雷峰网
雷峰网
WordPress大学
WordPress大学
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 最新话题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Schneier on Security
Schneier on Security
小众软件
小众软件
PCI Perspectives
PCI Perspectives

博客园 - 许仙儿

从大括号圣战到人机掰手腕:AI时代,程序员的“累”换了个赛道 Git 文件忽略备忘录:`assume-unchanged` 完全指南 三号车间张三喜 为什么湖广不太吃鲤鱼 为什么夏天副高会导致伏旱,而冬天的西伯利亚高原又会带来寒流 普通RESTful和MCP调用对比 2026年放假安排 红楼梦龄官病死前和贾蔷同居的情节 古代的时辰,几更天与现在的时间对应关系是什么? 西游记结局评职称 生成一张图,苹果logo是透明冰块,安卓小机器人撒尿到苹果logo,冲出一个豁口 HL7v3和RIM是什么,和传统HL7,FHIR有什么关系 owl(Web Ontology Language)简介 关系型数据库 vs Elasticsearch 早上看到木蜂在抓取掉落到地上的国槐花,还一边打滚。它是否习性懒惰? Java SPI(Service Provider Interface)示例 常见古文中流行度和水平最高的前100首,按朝代分类 API Blueprint​​ 是一种基于 ​​Markdown​​ 的轻量级 API 描述语言 八卦的符号,读音,名称,代表的事务分别是什么 Spring Boot 中 JdbcTemplate、MyBatis 和 JPA 共用数据源的可行性 win10下python程序报错OPENSSL_Uplink(00007FFD0AC12FE8,08): no OPENSSL_Applink
用go写一个微服务gPRC为主RESTful为辅
许仙儿 · 2026-03-05 · via 博客园 - 许仙儿

环境准备

  • Windows 10 + Go 1.24
  • 需要安装的 Go 工具:
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
    go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
    go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
    go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
    
  • 还需要 protoc 编译器。可从 https://github.com/protocolbuffers/protobuf/releases 下载预编译二进制(protoc-<version>-win64.zip),解压后将 bin 目录加入系统 PATH。

项目结构

my-microservice/
├── proto/
│   └── user/
│       └── user.proto
├── server/                # gRPC 服务端实现
│   └── main.go
├── gateway/               # gRPC-Gateway HTTP 服务
│   └── main.go
├── gen/                   # 生成的代码(可忽略,由脚本生成)
│   └── proto/
│       └── user/
│           ├── user.pb.go
│           ├── user_grpc.pb.go
│           └── user.pb.gw.go
├── go.mod
└── Makefile               # 或 generate.bat 用于生成代码

步骤 1:定义 Protocol Buffers 文件

proto/user/user.proto

syntax = "proto3";

package user;

option go_package = "my-microservice/gen/proto/user";

import "google/api/annotations.proto";

service UserService {
  rpc GetUser (GetUserRequest) returns (User) {
    option (google.api.http) = {
      get: "/v1/users/{id}"
    };
  }
  rpc CreateUser (CreateUserRequest) returns (User) {
    option (google.api.http) = {
      post: "/v1/users"
      body: "user"
    };
  }
}

message GetUserRequest {
  string id = 1;
}

message CreateUserRequest {
  User user = 1;
}

message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

注意:为了生成 gRPC-Gateway 代码,我们需要导入 google/api/annotations.proto。该文件需要从 googleapis 下载到本地或通过 -I 指定包含路径。我们将 google/api 目录放在项目根目录的 third_party/googleapis 下。


步骤 2:生成 Go 代码

编写一个脚本 generate.bat(Windows 批处理):

@echo off
set PROTOC=protoc
set PROTO_PATH=proto;third_party\googleapis
set OUT_DIR=gen\proto

REM 清理旧生成文件
if exist %OUT_DIR% rmdir /s /q %OUT_DIR%
mkdir %OUT_DIR%

REM 生成 user.pb.go 和 user_grpc.pb.go
%PROTOC% -I=%PROTO_PATH% --go_out=%OUT_DIR% --go_opt=paths=source_relative --go-grpc_out=%OUT_DIR% --go-grpc_opt=paths=source_relative proto/user/user.proto

REM 生成 user.pb.gw.go (gRPC-Gateway)
%PROTOC% -I=%PROTO_PATH% --grpc-gateway_out=%OUT_DIR% --grpc-gateway_opt=paths=source_relative proto/user/user.proto

echo Generation complete.

执行该脚本后,将在 gen/proto/user/ 下得到三个文件:

  • user.pb.go (消息定义)
  • user_grpc.pb.go (gRPC 客户端/服务端接口)
  • user.pb.gw.go (gRPC-Gateway 反向代理)

步骤 3:实现 gRPC 服务端

server/main.go

package main

import (
	"context"
	"log"
	"net"

	"my-microservice/gen/proto/user"
	"google.golang.org/grpc"
)

type userService struct {
	user.UnimplementedUserServiceServer
	// 模拟数据库
	users map[string]*user.User
}

func (s *userService) GetUser(ctx context.Context, req *user.GetUserRequest) (*user.User, error) {
	u, ok := s.users[req.Id]
	if !ok {
		return nil, nil // 实际应返回 NotFound 错误
	}
	return u, nil
}

func (s *userService) CreateUser(ctx context.Context, req *user.CreateUserRequest) (*user.User, error) {
	u := req.User
	if u.Id == "" {
		// 生成简单 ID
		u.Id = "123" // 生产环境应使用 UUID
	}
	s.users[u.Id] = u
	return u, nil
}

func main() {
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	s := grpc.NewServer()
	user.RegisterUserServiceServer(s, &userService{
		users: make(map[string]*user.User),
	})

	log.Println("gRPC server listening on :50051")
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

步骤 4:实现 gRPC-Gateway HTTP 服务

gateway/main.go

package main

import (
	"context"
	"log"
	"net/http"

	"my-microservice/gen/proto/user"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	// 连接到 gRPC 服务端
	conn, err := grpc.DialContext(
		context.Background(),
		"localhost:50051",
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithBlock(),
	)
	if err != nil {
		log.Fatalf("failed to dial gRPC server: %v", err)
	}
	defer conn.Close()

	// 创建 gRPC-Gateway 多路复用器
	gwmux := runtime.NewServeMux()

	// 注册 UserService 的 HTTP 代理
	if err := user.RegisterUserServiceHandler(context.Background(), gwmux, conn); err != nil {
		log.Fatalf("failed to register gateway: %v", err)
	}

	// 启动 HTTP 服务器
	addr := ":8080"
	log.Printf("HTTP server listening on %s", addr)
	if err := http.ListenAndServe(addr, gwmux); err != nil {
		log.Fatalf("failed to serve HTTP: %v", err)
	}
}

步骤 5:创建 go.mod 并整理依赖

在项目根目录执行:

go mod init my-microservice
go get google.golang.org/grpc
go get google.golang.org/protobuf
go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
go get github.com/grpc-ecosystem/grpc-gateway/v2/runtime

运行 go mod tidy 下载所有依赖。


步骤 6:运行与测试

  1. 启动 gRPC 服务端

    go run server/main.go
    

    输出:gRPC server listening on :50051

  2. 启动 HTTP 网关

    go run gateway/main.go
    

    输出:HTTP server listening on :8080

  3. 测试 RESTful API

    创建用户(POST):

    curl -X POST http://localhost:8080/v1/users \
         -H "Content-Type: application/json" \
         -d '{"id": "1001", "name": "Alice", "email": "alice@example.com"}'
    

    获取用户(GET):

    curl http://localhost:8080/v1/users/1001
    

    将收到 JSON 响应,例如:

    {"id":"1001","name":"Alice","email":"alice@example.com"}
    
  4. 测试 gRPC 调用(可选)
    可以使用 grpcurl 工具:

    grpcurl -plaintext -d '{"id":"1001"}' localhost:50051 user.UserService/GetUser
    

原理说明

  1. gRPC 作为主通信协议

    • gRPC 基于 HTTP/2,使用 Protocol Buffers 序列化,支持多路复用、双向流,性能高且接口契约强。
    • 服务端实现业务逻辑,通过 protoc 生成的接口确保类型安全。
  2. gRPC-Gateway 辅助提供 RESTful API

    • 在 proto 文件中使用 google.api.http 注解定义了 HTTP 方法与路径到 gRPC 方法的映射。
    • protoc-gen-grpc-gateway 根据注解生成一个反向代理服务器,它接收 HTTP/JSON 请求,将其转换为 gRPC 调用,然后将响应转换回 JSON。
    • 网关无需重写业务逻辑,只需与 gRPC 服务端建立连接,即可将 RESTful 流量无缝转发。
  3. 架构优势

    • 单一契约:只需维护 .proto 文件,即可同时生成 gRPC 和 HTTP 接口,避免 API 定义不一致。
    • 渐进式改造:对外暴露 RESTful 接口便于浏览器、移动端和第三方集成,内部服务间仍使用高效的 gRPC。
    • 生态丰富:可配合 OpenAPI (Swagger) 生成文档、客户端 SDK 等。

注意事项

  • 生产环境中 gRPC 服务端与网关通常部署在不同端口(或同一端口但需特殊处理),本例分开启动便于演示。
  • 示例中未处理错误与认证,实际应添加 TLS、拦截器、日志等。
  • 若要在 Windows 下长期运行,可考虑将两个服务注册为 Windows 服务或使用进程管理器。

通过以上步骤,我们成功构建了一个以 gRPC 为核心、同时提供 RESTful 接口的 Go 微服务。完整代码可打包为项目,按需扩展。