尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java 微服务架构:从单体到分布式的演进之路

Java 微服务架构:从单体到分布式的演进之路 Java 微服务架构从单体到分布式的演进之路先讲个故事为什么要拆分系统想象你开了一家餐厅最开始只有一个厨师负责洗菜、切菜、炒菜、装盘、收银、打扫——一个人包揽所有事情。生意不错时这个厨师忙不过来你有两个选择克隆厨师再雇一个全能厨师两人干一模一样的活传统的scale up专业分工雇一个专门切菜的一个专门炒菜的一个专门收银的各司其职微服务架构就是第二种思路。把一个庞大的系统拆成多个小服务每个服务只做一件事独立部署、独立扩展。在 Java 开发中这个转变尤其明显因为传统 Java 企业应用最爱造巨无霸——一个 war 包动辄几百 MB启动要几分钟改一行代码要重新部署整个应用。微服务就是来解决这些痛点的。什么是微服务架构定义微服务架构Microservices Architecture是一种将单一应用程序拆分为一组小型服务的架构风格每个服务独立进程运行在自己的进程中通常是独立的 JVM独立部署可以单独发布、升级、回滚单一职责只负责一个业务领域如用户管理、订单处理轻量通信通过 HTTP REST、gRPC、消息队列等方式互相调用去中心化每个服务可以用不同的技术栈、数据库对比单体架构 vs 微服务架构维度单体架构微服务架构部署单位一个 war/jar 包多个独立服务技术栈统一如全是 Spring MVC可混合订单用 Java推荐用 Python扩展方式整体复制按需扩展单个服务故障影响一处崩溃全局瘫痪故障隔离开发团队全员改同一个代码库按服务分团队上线速度改一行代码要部署全量只部署改动的服务Java 微服务技术栈全景图Java 生态的微服务工具链已经非常成熟这里按职责分类1. 服务框架写服务的基础Spring Boot是事实标准提供开箱即用的配置SpringBootApplicationRestControllerpublicclassOrderServiceApplication{GetMapping(/orders/{id})publicOrdergetOrder(PathVariableLongid){// 查询订单逻辑returnorderRepository.findById(id);}publicstaticvoidmain(String[]args){SpringApplication.run(OrderServiceApplication.class,args);}}一个最小的微服务只需要这几行代码。spring-boot-starter-web自动配置了 Tomcat、Jackson、日志等。Quarkus和Micronaut是新生代框架启动速度更快、内存占用更小适合云原生和 Serverless。2. 服务注册与发现微服务之间要互相调用但 IP 地址是动态的容器环境下每次重启 IP 都变。需要一个电话簿让服务找到彼此。常用方案EurekaNetflix 开源Spring Cloud 默认ConsulHashiCorp 出品支持健康检查Nacos阿里巴巴国内用得多// 服务提供者把自己注册到 EurekaEnableEurekaClientSpringBootApplicationpublicclassUserService{}// 服务消费者通过服务名调用不用写死 IPAutowiredprivateRestTemplaterestTemplate;publicOrdergetOrder(Longid){// order-service 是注册的服务名Eureka 自动解析成 IPreturnrestTemplate.getForObject(http://order-service/orders/id,Order.class);}3. 负载均衡当order-service部署了 5 个实例时如何分配流量Ribbon客户端负载均衡已经进入维护模式现在推荐Spring Cloud LoadBalancerSpring Cloud 新默认Kubernetes Service容器环境下用 K8s 原生能力BeanLoadBalanced// 这一行启用负载均衡publicRestTemplaterestTemplate(){returnnewRestTemplate();}4. 服务网关API Gateway用户不应该直接访问几十个微服务的地址需要一个统一入口处理路由/api/users/*转发到 user-service鉴权检查 JWT token限流防止某个服务被打垮跨域处理浏览器 CORS 请求常用网关Spring Cloud GatewayWebFlux 异步性能好Zuul1.x 是 Servlet 同步2.x 异步但项目停滞Kong基于 Nginx可用 Lua 插件SpringBootApplicationpublicclassGatewayApplication{BeanpublicRouteLocatorroutes(RouteLocatorBuilderbuilder){returnbuilder.routes().route(user-service,r-r.path(/api/users/**).filters(f-f.stripPrefix(1))// 去掉 /api 前缀.uri(lb://user-service))// lb 表示负载均衡.build();}}5. 配置中心微服务数量多了配置文件散落在各处很难管理。需要集中存储、动态刷新。方案Spring Cloud Config基于 Git 仓库Apollo携程开源UI 友好Nacos既做注册中心又做配置中心# application.yml 指向配置中心spring:cloud:config:uri:http://config-server:8888name:order-serviceprofile:prod改配置后不用重启服务发个刷新请求即可RefreshScope// 支持动态刷新RestControllerpublicclassOrderController{Value(${order.max-items})privateintmaxItems;// 这个值会自动更新}6. 熔断与限流防止雪崩场景订单服务调用库存服务库存服务挂了订单服务的线程全部阻塞等待最后自己也挂了——这叫雪崩效应。解决方案熔断器Circuit Breaker检测到下游故障时快速失败返回降级结果不再傻等限流超过阈值直接拒绝请求Resilience4j是目前主流选择Hystrix 已停更RestControllerpublicclassOrderController{GetMapping(/orders/{id})CircuitBreaker(nameinventory,fallbackMethodgetOrderFallback)publicOrdergetOrder(PathVariableLongid){// 调用库存服务InventoryinvinventoryClient.getInventory(id);returnbuildOrder(inv);}// 降级方法库存服务挂了时返回缓存数据publicOrdergetOrderFallback(Longid,Exceptione){returnOrder.builder().id(id).status(库存服务暂时不可用请稍后再试).build();}}7. 链路追踪排查问题一个用户请求可能经过 10 个微服务如何知道是哪一环出了问题分布式追踪系统给每个请求分配一个Trace ID在各服务间传递用户请求 [trace-id: abc123] → Gateway [耗时 5ms] → User Service [耗时 20ms] → Order Service [耗时 200ms] ← 发现瓶颈在这 → DB 查询 [耗时 180ms]常用工具Spring Cloud Sleuth自动注入 Trace IDZipkin可视化追踪链路JaegerUber 开源CNCF 项目SkyWalking国产支持中文功能全面配置非常简单dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-sleuth/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-sleuth-zipkin/artifactId/dependency日志自动带上 Trace ID2024-01-15 10:23:45 [order-service,abc123,def456] INFO 查询订单 #123458. 消息队列异步解耦不是所有调用都要同步等待结果比如下单后发邮件// 同步调用用户等邮件发完才能看到下单成功慢orderService.createOrder(order);emailService.sendConfirmation(order);// 如果邮件服务挂了订单也创建不了returnsuccess;// 异步消息订单创建完就返回邮件慢慢发快且解耦orderService.createOrder(order);messageQueue.send(order-created,order);// 发到队列就不管了returnsuccess;Java 常用消息队列RabbitMQErlang 实现支持复杂路由Kafka高吞吐适合日志、大数据场景RocketMQ阿里开源支持事务消息Spring Boot 集成示例ComponentpublicclassOrderEventPublisher{AutowiredprivateRabbitTemplaterabbitTemplate;publicvoidpublishOrderCreated(Orderorder){rabbitTemplate.convertAndSend(order.exchange,order.created,order);}}ComponentpublicclassEmailListener{RabbitListener(queuesemail.queue)publicvoidhandleOrderCreated(Orderorder){// 发邮件emailService.send(order.getUserEmail(),订单已创建);}}9. 分布式事务经典难题用户下单时要同时扣减库存、扣减余额、创建订单记录这三个操作分别在三个微服务里。如何保证要么全成功要么全失败方案对比方案一致性性能复杂度适用场景2PC/XA强一致差阻塞低金融核心系统TCC强一致好高要写 Try/Confirm/Cancel对账系统Saga最终一致好中写补偿逻辑电商订单本地消息表最终一致好低通用Seata是阿里开源的分布式事务框架支持多种模式GlobalTransactional// 开启分布式事务publicvoidcreateOrder(Orderorder){// 1. 创建订单orderRepository.save(order);// 2. 扣减库存调用库存服务inventoryService.deduct(order.getProductId(),order.getQuantity());// 3. 扣减余额调用账户服务accountService.deduct(order.getUserId(),order.getAmount());// 如果任意一步失败Seata 自动回滚所有操作}一个完整的电商微服务架构示例用户请求 ↓ ┌──────────────────┐ │ Spring Cloud │ │ Gateway │ 统一网关路由、鉴权、限流 └──────────────────┘ ↓ ┌─────────────────┼─────────────────┐ ↓ ↓ ↓ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ User │ │ Order │ │ Product │ 业务服务 │ Service │ │ Service │ │ Service │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └────────┬────────┴────────┬────────┘ ↓ ↓ ┌────────────┐ ┌────────────┐ │ Eureka │ │ RabbitMQ │ 基础设施 │ (注册中心) │ │ (消息队列) │ └────────────┘ └────────────┘ ↓ ┌────────────┐ │ Zipkin │ 监控追踪 └────────────┘技术选型网关Spring Cloud Gateway注册中心Eureka Server配置中心Spring Cloud Config熔断Resilience4j追踪Sleuth Zipkin消息RabbitMQ数据库每个服务独立 MySQL用户库、订单库、商品库项目结构ecommerce-microservices/ ├── gateway/ # 网关服务 ├── eureka-server/ # 注册中心 ├── config-server/ # 配置中心 ├── user-service/ # 用户服务 │ ├── src/main/java/ │ ├── pom.xml │ └── application.yml ├── order-service/ # 订单服务 ├── product-service/ # 商品服务 └── common/ # 公共依赖DTO、工具类微服务的代价与挑战微服务不是银弹拆分后会带来新的复杂度1. 分布式系统的固有问题网络不可靠服务间调用可能超时、丢包部分失败A 服务成功了B 服务失败了数据不一致时钟不同步各服务器时间可能有偏差2. 运维复杂度暴增部署从部署 1 个应用变成部署 20 个服务监控要盯着 20 个服务的 CPU、内存、日志排查故障一个请求跨 10 个服务定位问题像查案解决方案上容器化Docker 编排平台Kubernetes 可观测性日志、指标、追踪3. 数据一致性单体应用用数据库事务保证一致性微服务拆分后每个服务独立数据库强一致性很难做到只能接受最终一致性。4. 接口变更的连锁反应用户服务改了接口订单服务、商品服务都要跟着改。需要严格的接口版本管理和契约测试。什么时候该用微服务不要为了微服务而微服务。以下情况考虑拆分✅团队规模大了超过 20 人单体代码库合并冲突频繁✅业务复杂了不同模块发版频率差异大核心交易每周发版推荐算法每天发版✅性能瓶颈明确只有个别模块需要扩容不想为了扩展搜索功能就复制整个应用✅技术栈需要异构主系统 Java推荐引擎 Python实时计算 Scala❌不该用的场景创业公司初期团队 10 人业务不稳定需求天天变团队没有分布式系统经验运维能力跟不上连 Docker 都没用过建议先做好单体架构的模块化按业务领域拆包接口清晰需要时再平滑演进到微服务。Martin Fowler 说得好“Monolith First”——除非你有充分理由否则先从单体开始。快速上手用 Spring Cloud 搭建第一个微服务1. 创建注册中心Eureka Server!-- pom.xml --dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-server/artifactId/dependencyEnableEurekaServerSpringBootApplicationpublicclassEurekaServerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(EurekaServerApplication.class,args);}}# application.ymlserver:port:8761eureka:client:register-with-eureka:false# 自己不注册fetch-registry:false访问http://localhost:8761看到 Eureka 控制台。2. 创建服务提供者User ServicedependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencyRestControllerSpringBootApplicationpublicclassUserServiceApplication{GetMapping(/users/{id})publicUsergetUser(PathVariableLongid){returnnewUser(id,张三,zhangsanexample.com);}publicstaticvoidmain(String[]args){SpringApplication.run(UserServiceApplication.class,args);}}spring:application:name:user-serviceeureka:client:service-url:defaultZone:http://localhost:8761/eureka/server:port:80813. 创建服务消费者Order ServiceRestControllerSpringBootApplicationpublicclassOrderServiceApplication{BeanLoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}AutowiredprivateRestTemplaterestTemplate;GetMapping(/orders/{id})publicStringgetOrder(PathVariableLongid){// 通过服务名调用 user-serviceUseruserrestTemplate.getForObject(http://user-service/users/1,User.class);return订单 #id 属于用户user.getName();}publicstaticvoidmain(String[]args){SpringApplication.run(OrderServiceApplication.class,args);}}启动三个服务访问http://localhost:8082/orders/123看到跨服务调用成功。未来趋势云原生与 Service MeshKubernetes 成为新基础设施传统微服务用 Eureka 做注册中心现在越来越多公司直接用Kubernetes服务发现K8s Service 自动负载均衡配置管理ConfigMap / Secret扩缩容HPA根据 CPU 自动扩容Java 应用只需要做成 Docker 镜像其他交给 K8s。Service Mesh服务网格问题微服务框架的治理逻辑负载均衡、熔断、追踪都耦合在业务代码里换个语言要重新实现一遍。Service Mesh把这些逻辑下沉到基础设施层每个服务旁边部署一个Sidecar 代理如 Envoy流量都经过代理处理Order Service → Envoy (sidecar) → 网络 → Envoy (sidecar) → User ServiceIstio是最流行的 Service Mesh业务代码完全不用管治理逻辑# 用配置文件定义熔断规则不写 Java 代码apiVersion:networking.istio.io/v1kind:DestinationRulemetadata:name:user-servicespec:host:user-servicetrafficPolicy:connectionPool:tcp:maxConnections:100outlierDetection:consecutive5xxErrors:5interval:30s总结微服务架构是一种组织策略不仅仅是技术选型。它让大团队能并行开发、快速迭代代价是分布式系统的复杂性。Java 生态的微服务工具链已经非常成熟Spring Cloud提供全家桶式解决方案KubernetesIstio代表云原生方向国产方案Dubbo、Nacos、Seata在国内企业广泛使用给新手的建议先学好 Spring Boot 单体应用理解分布式系统的 CAP 理论、BASE 理论用 Docker Compose 在本地跑一套完整的微服务环境读 Martin Fowler 的《Microservices》原文到中大型公司实习看真实的微服务是怎么治理的不要盲目追新合适的架构取决于你的团队规模、业务复杂度和技术储备。很多时候一个设计良好的单体应用比拆得乱七八糟的微服务要健康得多。后记2026年8月10日于上海在claude opus 4.8辅助下完成。
返回列表