微服务架构:服务拆分、服务发现(Consul)、配置中心、API网关

说实话,微服务这个词已经被聊烂了。但真正落地的时候,很多人还是栽在「怎么拆」和「怎么连」这两个问题上。我刚开始带团队做微服务改造时,也踩过不少坑。今天咱们就聊聊这四个核心环节:服务拆分、服务发现、配置中心、API网关。嗯,我会结合Consul和Spring Cloud Gateway来讲,毕竟这是目前Kotlin生态里比较成熟的组合。

一、服务拆分:别为了拆而拆

很多人一上来就问:「我该拆成多少个服务?」其实这是个伪命题。你想想看,拆服务的目的是什么?是为了独立部署、独立扩展、独立故障隔离。如果拆完反而让系统更复杂、更慢,那还不如不拆。

我个人习惯遵循两个原则:

  • 业务边界原则:按领域驱动设计(DDD)的限界上下文来拆。比如订单、库存、支付,天然就是不同的上下文。
  • 数据独立性原则:每个服务拥有自己的数据库,绝不共享库。我在项目中遇到过共享数据库导致耦合爆炸的情况,后来花了两个月才拆干净。
避坑指南:我曾经把一个「用户服务」拆成了「用户基础信息」和「用户权限」两个服务。结果发现每次登录都要跨服务调用两次,延迟翻倍。后来合并成一个服务,反而更清爽。所以,拆之前先画一下调用链路图。

下面这张图是我常用的服务拆分逻辑,你可以参考一下:

微服务拆分逻辑图 客户端请求 API网关(路由/鉴权) 订单服务 库存服务 支付服务 Consul 服务注册与发现(所有服务启动时注册) 配置中心(Consul KV / Spring Cloud Config)

二、服务发现: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跑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注解才能刷新。

警告:敏感信息(如密码)不要明文存在Consul里。建议用Vault或Spring Cloud Config的加密功能。我曾经见过同事把生产数据库密码直接写在Consul里,结果被安全审计打了个大大的红叉。

四、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的实例列表,然后轮询转发。

避坑指南:我曾经在网关层做了全局CORS配置,结果发现某些服务自己又配了一遍CORS,导致重复头信息,前端报错。后来统一在网关层处理,服务层全部去掉CORS配置。记住:网关是边界,跨域、鉴权这类横切关注点尽量收敛到网关。

五、整体架构一览

最后,我把这四个组件串起来,画一张完整的架构图。你一看就明白它们怎么协作的:

微服务架构总览(Consul + Gateway) 客户端(App/Web) API网关(路由/鉴权/限流) 订单服务 库存服务 支付服务 Consul 集群(服务注册/发现 + KV配置中心) 订单数据库 库存数据库 支付数据库

看到没?所有服务启动时都去Consul注册,网关从Consul动态获取服务列表。配置也统一存在Consul KV里。这样任何一个服务挂了,Consul的健康检查会自动摘掉它,网关也不会再往那个实例转发请求。这就是微服务最核心的「自愈」能力。

最后说一句:微服务不是银弹。如果你的团队只有两三个人,业务也不复杂,单体应用可能更合适。但如果你已经决定走这条路,那服务拆分、Consul、配置中心、API网关这四个组件,就是你的必修课。我当年就是一步步从单体拆过来的,虽然过程痛苦,但拆完后的灵活性和可维护性,确实值得。

公众号:蓝海资料掘金营,微信deep3321