微服务架构:服务拆分、服务发现(Consul)、配置中心、API网关
说实话,微服务这个词已经被聊烂了。但真正落地的时候,很多人还是栽在「怎么拆」和「怎么连」这两个问题上。我刚开始带团队做微服务改造时,也踩过不少坑。今天咱们就聊聊这四个核心环节:服务拆分、服务发现、配置中心、API网关。嗯,我会结合Consul和Spring Cloud Gateway来讲,毕竟这是目前Kotlin生态里比较成熟的组合。
一、服务拆分:别为了拆而拆
很多人一上来就问:「我该拆成多少个服务?」其实这是个伪命题。你想想看,拆服务的目的是什么?是为了独立部署、独立扩展、独立故障隔离。如果拆完反而让系统更复杂、更慢,那还不如不拆。
我个人习惯遵循两个原则:
- 业务边界原则:按领域驱动设计(DDD)的限界上下文来拆。比如订单、库存、支付,天然就是不同的上下文。
- 数据独立性原则:每个服务拥有自己的数据库,绝不共享库。我在项目中遇到过共享数据库导致耦合爆炸的情况,后来花了两个月才拆干净。
下面这张图是我常用的服务拆分逻辑,你可以参考一下:
二、服务发现:Consul 实战
服务拆完之后,最大的问题就是:A服务怎么找到B服务?总不能写死IP吧。我早期用Eureka,后来发现Consul在健康检查和KV存储上更胜一筹。说白了,Consul就是一个「电话本」——每个服务启动时去注册自己的地址,下线时自动注销。
在Kotlin + Spring Cloud项目中,集成Consul非常简单。下面是我常用的配置:
// build.gradle.kts
dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-consul-discovery")
implementation("org.springframework.cloud:spring-cloud-starter-consul-config")
}
// application.yml
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: order-service
health-check-path: /actuator/health
health-check-interval: 10s
注意:健康检查路径一定要暴露。我曾经忘了配/actuator/health,结果Consul一直显示服务不健康,排查了半天才发现是端点没开。
docker run -d -p 8500:8500 consul。一行命令搞定,不用装客户端。
三、配置中心:别再硬编码了
配置中心解决的是「改配置不用重启」的问题。Consul本身就带KV存储,可以直接当配置中心用。我个人更推荐Consul Config,因为它和Consul Discovery无缝集成,少维护一个组件。
举个例子,我们把数据库连接放在Consul的KV里:
// 在Consul UI中创建 key/value
config/order-service/datasource.url = jdbc:mysql://localhost:3306/order
config/order-service/datasource.username = root
config/order-service/datasource.password = secret
// 代码中自动注入
@Configuration
class DataSourceConfig {
@Value("\${datasource.url}")
lateinit var url: String
@Bean
fun dataSource(): DataSource {
return HikariDataSource().apply {
jdbcUrl = url
// ...
}
}
}
这样改配置时,只需要在Consul UI里修改,然后调用/actuator/refresh端点,配置就会热更新。嗯,这里要注意:有些Bean需要加@RefreshScope注解才能刷新。
四、API网关:统一入口
API网关是微服务的「大门」。所有外部请求都先经过网关,由网关做路由、鉴权、限流、日志等。我常用Spring Cloud Gateway,因为它基于WebFlux,性能好,而且支持Kotlin协程。
下面是一个简单的路由配置:
// application.yml
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
// Kotlin DSL方式(我个人更喜欢)
@Bean
fun routeLocator(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes {
route("order-route") {
path("/api/order/**")
filters {
stripPrefix(1)
circuitBreaker { name = "orderCB" }
}
uri("lb://order-service")
}
}
}
这里lb://前缀表示从Consul中按服务名负载均衡。说白了,网关会自动从Consul拉取order-service的实例列表,然后轮询转发。
五、整体架构一览
最后,我把这四个组件串起来,画一张完整的架构图。你一看就明白它们怎么协作的:
看到没?所有服务启动时都去Consul注册,网关从Consul动态获取服务列表。配置也统一存在Consul KV里。这样任何一个服务挂了,Consul的健康检查会自动摘掉它,网关也不会再往那个实例转发请求。这就是微服务最核心的「自愈」能力。
公众号:蓝海资料掘金营,微信deep3321