一、什么是微服务的API网关?
微服务的 API 网关是系统对外的****唯一入口 ,负责统一接收请求,并完成鉴权、路由、限流、协议转换 等通用能力,再将请求转发给内部微服务。它的作用是屏蔽内部复杂性,保护系统稳定,让业务服务专注业务本身
二、API网关能解决哪些核心问题?
- 统一入口
客户端只需要访问统一地址:
js
api.your-domain.com
内部发生什么变化,客户端完全不感知。这是微服务"可演进"的前提。
- 统一鉴权
-
API网关统一完成:
-
JWT/OAuth2校验
-
用户身份解析
-
权限控制
Go服务只关心:
css
uid := c.Value("uid")
鉴权是入口问题,不是业务问题
- 统一限流与防护
API网关可以统一做:
-
全局限流
-
单用户限流
-
IP限流
-
防刷防爬
把流量风险挡在系统外。
- 统一协议与路由
API网关可以:
-
HTTP -> gRPC
-
REST -> RPC
-
路径重写
-
版本控制
这让Go微服务可以:
-
内部专心用gRPC
-
外部保持HTTP友好
- 服务聚合(BFF场景)
一个接口需要:
-
用户信息
-
订单信息
-
推荐数据
API网关/BFF可以:
-
并发调用多个服务
-
聚合后返回
客户端请求次数大幅减少。
- 流量治理与发布能力
API网关可以:
-
灰度发布
-
金丝雀发布
-
A/B测试
-
熔断、降级
这是成熟系统必备能力
三、API网关Kong实例(文末有源码地址)
- 编写docker-compose.yml(./docker-compose.yml)
bash
networks:
codee_jun:
driver: bridge
# external: true
services:
consul:
container_name: consul
image: consul:1.15.4
ports:
- 8500:8500
restart: always
networks:
- codee_jun
kong:
container_name: kong
image: kong:ubuntu
ports:
- 8000:8000
- 8001:8001
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /kong/kong.yml
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
KONG_ADMIN_LISTEN: 0.0.0.0:8001
depends_on:
- consul
volumes:
- ./kong/kong.yml:/kong/kong.yml
restart: always
networks:
- codee_jun
user-server:
container_name: user-server
build:
context: ./user-server
dockerfile: Dockerfile
ports:
- 8080:8080
depends_on:
- consul
restart: always
networks:
- codee_jun
order-server:
container_name: order-server
build:
context: ./order-server
dockerfile: Dockerfile
ports:
- 8081:8081
depends_on:
- consul
restart: always
networks:
- codee_jun
- Kong配置文件(./kong/kong.yml)
perl
# Kong 声明式配置文件版本
# 表示该配置文件遵循 Kong 3.x 的格式规范
_format_version: "3.0"
# services 定义的是「Kong 要代理的后端服务」
# 每一个 service,通常对应一个真实的微服务
services:
# service 的唯一名称,用于:
# - 管理
# - 绑定路由
# - 挂载插件(限流、鉴权等)
- name: user-service
# 后端真实服务地址
# Kong 会把匹配到的请求转发到这个地址
# 这里是一个 Go 用户服务,监听在 8080 端口
url: http://192.168.1.31:8080
# routes 定义「什么样的请求会被转发到这个 service」
routes:
- name: user-route
# route 名称,仅用于管理和排查问题
# paths 表示路径匹配规则
# 当请求路径以 /api/user 开头时
# Kong 会将请求转发给 user-service
paths:
- /api/user
- name: order-service
url: http://192.168.1.31:8081
routes:
- name: order-route
paths:
- /api/order
# methods 表示 HTTP 方法匹配规则
- 启动Kong
js
docker-compose up -d

- 编写一个user-server服务(./user-server/main.go)
go
package main
import (
...
)
const (
srvName = "golang_per_day_51:user-server"
srvHost = "user-server" //这里写user-server,方便在docker-compose中使用
srvPort = 8080
srvID = "golang_per_day_51-user-server:8080"
)
var (
appConfig *Config
lock sync.RWMutex
)
type Config struct {
Consul ConsulConfig `mapstructure:"consul" json:"consul"`
Redis RedisConfig `mapstructure:"redis" json:"redis"`
}
type RedisConfig struct {
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
Db int `mapstructure:"db" json:"db"`
Password string `mapstructure:"password" json:"password"`
}
type ConsulConfig struct {
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
}
func main() {
r := gin.Default()
// 定义一个 ping 接口
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong from user-server",
})
})
// 1. 定义健康检查接口
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{
"status": "ok",
})
})
// 2. 读取配置中心的配置,这里用Consul
config := api.DefaultConfig()
config.Address = "192.168.1.31:8500" // Consul 的地址
client, _ := api.NewClient(config)
kvPath := "config/golang_per_day/51/user"
loadConfig(client, kvPath)
go watchConfig(client, kvPath)
// 4. 启动 Gin 服务
go func() {
if err := r.Run(fmt.Sprintf(":%d", srvPort)); err != nil {
log.Fatal("服务启动失败: ", err)
}
}()
// 5. 优雅退出:监听信号注销服务
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// 退出前注销服务
deregisterFromConsul()
log.Println("服务已退出并从 Consul 注销")
}
// 注册逻辑
func registerToConsul() {
config := api.DefaultConfig()
config.Address = fmt.Sprintf("%s:%d", appConfig.Consul.Host, appConfig.Consul.Port) // Consul 的地址
client, _ := api.NewClient(config)
registration := &api.AgentServiceRegistration{
ID: srvID,
Name: srvName,
Address: srvHost,
Port: srvPort,
Check: &api.AgentServiceCheck{
HTTP: fmt.Sprintf("http://%s:%d/health", srvHost, srvPort),
Interval: "5s", // 每 5 秒检查一次
Timeout: "3s", // 超时时间
},
}
err := client.Agent().ServiceRegister(registration)
if err != nil {
log.Fatal("注册失败: ", err)
}
log.Println("服务注册成功!")
}
// 注销逻辑
func deregisterFromConsul() {
config := api.DefaultConfig()
client, _ := api.NewClient(config)
_ = client.Agent().ServiceDeregister(srvID)
}
// 读取配置中心的 Redis 配置
func loadConfig(client *api.Client, path string) {
pair, _, err := client.KV().Get(path, nil)
if err != nil {
log.Fatal("读取配置失败: ", err)
}
v := viper.New()
v.SetConfigType("yaml")
err = v.ReadConfig(bytes.NewBuffer(pair.Value))
if err != nil {
log.Fatal("读取配置失败: ", err)
}
newConfig := Config{}
err = v.Unmarshal(&newConfig)
if err != nil {
log.Fatal("解析配置失败: ", err)
}
lock.Lock()
defer lock.Unlock()
appConfig = &newConfig
log.Println("Redis 配置更新: ", appConfig)
// 3. 注册服务到 Consul
registerToConsul()
}
// 监听 Consul K/V 变化
func watchConfig(client *api.Client, path string) {
var lastIndex uint64
for {
// Consul 的 WaitIndex 机制:如果有变化会立即返回,否则阻塞直到超时
pair, meta, err := client.KV().Get(path, &api.QueryOptions{
WaitIndex: lastIndex,
})
if err != nil {
log.Printf("监听出错: %v", err)
continue
}
if pair != nil && meta.LastIndex > lastIndex {
lastIndex = meta.LastIndex
loadConfig(client, path)
}
}
}
- 再加一个order-server(./order-server/main.go)
go
package main
import (
...
)
const (
srvName = "golang_per_day_51:order-server"
srvHost = "order-server" //这里写order-server,方便在docker-compose中使用
srvPort = 8081
srvID = "golang_per_day_51-order-server:8081"
)
...
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong from order-server",
})
})
...
- 启动整个项目服务
css
docker-compose up -d --build

可以看见consul和kong都已经启动成功,user-server和order-server都还在不断重启,那是因为没有读到consul的kv配置。
- 在consul中配置kv
javascript
- config/golang_per_day/51/user
```yaml
consul:
host: consul
port: 8500
redis:
addr: 192.168.1.31
port: 6379
password: "123456"
db: 1
```
- config/golang_per_day/51/order
```yaml
consul:
host: consul
port: 8500
redis:
addr: 192.168.1.31
port: 6379
password: "123456"
db: 2
```
配置完成后,等待微服务重启几次,就可以看见服务好了



四、API网关和Go微服务的边界
API网关应该做以下事情:路由、鉴权、限流、熔断、协议转换;
不应该做:写业务逻辑、访问数据库、领域计算
API网关只做"控制流",不做"业务流"。
五、什么时候需要上API网关?
当你的Go微服务数量≥3的时候,你就应该上API网关了。
六、一句话总结
-
API 网关,是微服务系统的门
-
复杂性应该止步于入口
-
没有 API 网关的微服务,是伪微服务
*源码地址* 私给
如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!