微服务架构核心概念业务示例 微服务架构核心概念业务示例一、什么是微服务概念微服务是一种架构风格将单体应用拆分为一组小型、独立部署的服务每个服务围绕特定业务能力构建通过轻量级通信机制通常是 HTTP/消息队列协作。与单体架构对比对比项单体架构微服务架构部署单元整个应用一起打包部署每个服务独立部署数据库所有模块共享一个数据库每个服务有自己的数据库通信方式方法调用进程内HTTP/MQ跨进程扩展方式整体扩容按需扩容单个服务故障影响一个模块崩溃整体不可用一个服务故障其他不受影响团队协作所有人改同一个代码仓库各团队负责自己的服务注博客https://blog.csdn.net/badao_liumang_qizhi二、服务间通信OpenFeign概念OpenFeign 是 Spring Cloud 提供的声明式 HTTP 客户端。你只需要定义一个接口并加上注解Feign 自动帮你完成 HTTP 请求的构造、发送和响应解析。与手动 HTTP 调用的对比手动 RestTemplate 方式繁琐Stringurlhttp://xxx-order/api/xxx/order/result/receive;HttpHeadersheadersnewHttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntityOmsOperationDtoentitynewHttpEntity(dto,headers);ResponseEntityRestControllerResultresponserestTemplate.postForEntity(url,entity,RestControllerResult.class);Feign 声明式方式简洁RestControllerResultBooleanresultxxxOrderFeign.noticeOms(dto);通用代码示例1. 定义 Feign 接口/** * 远程服务调用接口. * 声明式定义只需写接口 注解无需实现类. */FeignClient(value${service.name.user-service}/api,// 服务名从配置读取fallbackFactoryUserFeignFallbackFactory.class// 降级处理)publicinterfaceUserFeign{GetMapping(/user/info/{userId})RestControllerResultUserDtogetUserInfo(PathVariable(userId)IntegeruserId);PostMapping(/user/batch-query)RestControllerResultListUserDtobatchQuery(RequestBodyListIntegeruserIds);}2. Feign 降级工厂Fallback Factory/** * Feign 降级工厂. * 当远程服务不可用时执行降级逻辑避免级联故障. */ComponentpublicclassUserFeignFallbackFactoryimplementsFallbackFactoryUserFeign{OverridepublicUserFeigncreate(Throwablecause){returnnewUserFeign(){OverridepublicRestControllerResultUserDtogetUserInfo(IntegeruserId){log.warn(调用用户服务降级, userId{}, cause{},userId,cause.getMessage());returnRestControllerResult.fail(用户服务暂不可用);}OverridepublicRestControllerResultListUserDtobatchQuery(ListIntegeruserIds){log.warn(批量查询用户降级, cause{},cause.getMessage());returnRestControllerResult.fail(用户服务暂不可用);}};}}3. 业务代码中调用ServicepublicclassOrderServiceImpl{ResourceprivateUserFeignuserFeign;publicOrderDetailVogetOrderDetail(IntegerorderId){OrderorderorderRepository.findById(orderId);// 像调用本地方法一样调用远程服务RestControllerResultUserDtouserResultuserFeign.getUserInfo(order.getUserId());if(Boolean.TRUE.equals(userResult.getSuccess())){// 组装返回数据OrderDetailVovonewOrderDetailVo();vo.setOrder(order);vo.setUser(userResult.getData());returnvo;}returnnull;}}Feign 的核心配置feign:sentinel:enabled:true# 开启熔断client:config:default:connectTimeout:60000# 连接超时 60sreadTimeout:60000# 读超时 60sloggerLevel:basic# 日志级别三、服务注册与发现概念微服务数量众多每个服务可能有多个实例。服务注册与发现解决的问题是调用方如何知道目标服务的真实地址┌──────────┐ 注册 ┌──────────────────┐ 发现 ┌──────────┐ │ 服务A │──────→│ 注册中心 │←──────│ 服务B │ │ 实例1 │ │ (Nacos/Eureka) │ │ │ │ 实例2 │ │ │ │ │ │ 实例3 │ │ 服务A → [实例列表]│ │ │ └──────────┘ └──────────────────┘ └──────────┘工作流程服务A 启动后向注册中心注册自己的地址服务B 需要调用服务A 时先从注册中心获取服务A 的实例列表通过负载均衡策略选择一个实例发起调用注册中心通过心跳检测剔除不健康实例实现方式请求链路Feign → API Gateway → 目标服务网关负责服务路由和负载均衡。四、服务熔断与降级概念当远程服务出现故障超时、异常如果不做处理调用方会一直等待最终拖垮自身。熔断器就像电路保险丝当故障达到阈值时断开直接走降级逻辑。三种状态┌──────────────────────────────────────────┐ │ │ ▼ │ ┌──────────┐ 故障率达标 ┌──────────┐ │ │ CLOSED │ ────────────→ │ OPEN │ │ 探测成功 │ (正常) │ │ (熔断) │ │ └──────────┘ └─────┬────┘ │ ▲ │ 等待超时 │ │ ▼ │ │ ┌──────────┐ │ │ 探测失败 │HALF-OPEN │─────────┘ └────────────────── │ (半开) │ └──────────┘CLOSED正常调用统计失败率OPEN直接走降级逻辑不再调用远程服务HALF-OPEN放行少量请求探测成功则恢复失败则继续熔断通用代码示例/** * 带降级的远程调用示例. * 降级逻辑保证主流程不受下游故障影响. */ServiceSlf4jpublicclassNotificationService{ResourceprivateNotificationFeignnotificationFeign;/** * 发送通知不影响主业务. * 即使通知服务挂了主业务继续执行. */publicvoidsendNotification(Stringmessage){try{RestControllerResultBooleanresultnotificationFeign.send(message);if(!Boolean.TRUE.equals(result.getSuccess())){log.warn(发送通知失败: {},result.getMessage());}}catch(Exceptione){// 通知失败不影响主流程log.warn(通知服务调用异常,e);}}}五、消息队列MQ概念消息队列是服务内部异步解耦的核心组件。生产者发送消息到队列消费者从队列拉取消息处理两者时间上解耦。与事件中心的定位区别┌──────────────────────────────────────────────────────────┐ │ 同一个服务/系统内部 │ │ │ │ 业务方法 ──MQ──→ 异步处理方法 │ │ (同一个代码仓库同一个团队维护) │ │ │ │ 用途解耦耗时操作、削峰填谷、保证顺序 │ └──────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────┐ │ 跨系统/跨团队 │ │ │ │ a服务 ──事件中心──→ b服务 │ │ (不同代码仓库不同团队维护) │ │ │ │ 用途系统间通知、发布/订阅 │ └──────────────────────────────────────────────────────────┘通用代码示例生产者/** * MQ 消息生产者. * 发送消息到指定队列. */ComponentSlf4jpublicclassOrderMqSender{JshRabbitProducer(queue${mq.queue.order-process},exchange${mq.exchange.order})privateJshRabbitTemplaterabbitTemplate;publicvoidsendOrderProcess(IntegerorderId){if(orderId!null){rabbitTemplate.send(orderId);log.info(发送订单处理消息, orderId{},orderId);}}}消费者/** * MQ 消息消费者. * 通过 Profile 控制是否启用. */Profile(order-process-consumer)// 本地不想消费时可去掉此 profileComponentSlf4jpublicclassOrderMqConsumer{ResourceprivateOrderProcessServiceorderProcessService;JshRabbitConsumer(queue${mq.queue.order-process})publicvoidconsume(IntegerorderId){log.info(消费订单处理消息, orderId{},orderId);try{orderProcessService.process(orderId);}catch(Exceptione){log.error(订单处理失败, orderId{},orderId,e);throwe;// 抛出异常触发 MQ 重试}}}MQ 的作用异步化不阻塞主业务事务重试消费失败自动重试削峰高并发时消息排队处理六、分布式事务与数据一致性问题微服务各自有独立数据库一个业务操作可能跨多个服务无法使用传统的数据库事务保证一致性。方案本地消息表最终一致性┌─────────────────────────────────────────────────┐ │ 同一个数据库事务 │ │ │ │ ① 执行业务逻辑更新发货单状态等 │ │ ② 插入消息记录a_send_b_record_log │ │ ③ 事务提交 │ └───────────────────────┬─────────────────────────┘ │ ▼ 事务提交后 ┌─────────────────────────────────────────────────┐ │ ④ 发送 MQ携带 logId │ │ ⑤ 消费者读取消息记录推送事件中心 │ │ ⑥ 成功/失败更新消息状态 │ │ ⑦ 失败可重试运维接口/定时任务 │ └─────────────────────────────────────────────────┘通用代码示例/** * 本地消息表实现最终一致性. */ServicepublicclassOrderService{ResourceprivateOrderRepositoryorderRepository;ResourceprivateMessageLogRepositorymessageLogRepository;Transactional(rollbackForException.class)publicvoidcreateOrder(OrderDtodto){// ① 业务操作OrderordernewOrder();order.setOrderCode(dto.getOrderCode());order.setStatus(CREATED);orderRepository.save(order);// ② 同一事务内写消息记录MessageLogmsgLognewMessageLog();msgLog.setBizKey(order.getOrderCode());msgLog.setEventType(order.created);msgLog.setPayload(JSON.toJSONString(order));msgLog.setStatus(0);messageLogRepository.save(msgLog);// ③ 注册事务提交后的回调TransactionSynchronizationManager.registerSynchronization(newTransactionSynchronization(){OverridepublicvoidafterCommit(){mqSender.send(msgLog.getId());}});}}为什么不用分布式事务2PC/Seata方案一致性性能复杂度适用场景2PC/XA强一致低锁等待高金融转账Seata AT强一致中中短事务、同步场景本地消息表最终一致高低通知类、可接受短暂延迟TCC最终一致高高库存扣减等示例需求是通知系统b属于通知类场景不需要b的即时响应用本地消息表 最终一致性是最合适的方案。七、API 网关概念API 网关是微服务的统一入口所有外部请求先经过网关由网关路由到具体服务。┌─────────────────┐ 外部请求 ──────────→│ API Gateway │ │ │ │ 路由规则 │ │ /a-order → A │──→ a 服务 │ /b → B │──→ b服务 │ /order → C │──→ 订单服务 └─────────────────┘网关的职责职责说明路由转发根据 URL 前缀将请求转发到对应服务负载均衡同一服务多实例时分发请求鉴权认证统一校验 JWT Token限流熔断保护后端服务不被打爆协议转换HTTPS → HTTP外部加密内部明文八、配置中心概念微服务众多每个服务有各自的配置数据库、MQ、第三方地址等。配置中心提供统一管理和动态刷新。通过配置实现多环境切换# 本地开发eventcenter:event:route-event-to-oms:url:https://xxx.net/.../send# 测试环境# 生产环境配置中心覆盖eventcenter:event:route-event-to-oms:url:https://xxx/.../send# 生产环境代码中通过Value注入无需修改代码即可切换环境Value(${eventcenter.event.route-event-to-oms.url})privateStringrouteEventToOmsEventUrl;九、服务间调用的安全认证JWT Token 传递微服务间调用需要传递身份信息。项目通过 Feign 请求拦截器自动带上 JWT Token用户请求 → 网关验证JWT→ a服务 → Feign调用b服务携带JWT通用示例Feign 请求拦截器/** * Feign 请求拦截器. * 自动将当前请求的 JWT Token 传递给下游服务. */ConfigurationpublicclassFeignInterceptorConfig{BeanpublicRequestInterceptorrequestInterceptor(){returnrequestTemplate-{// 从当前请求上下文获取 TokenServletRequestAttributesattributes(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();if(attributes!null){Stringtokenattributes.getRequest().getHeader(Authorization);if(token!null){requestTemplate.header(Authorization,token);}}};}}