微服务架构核心概念业务示例

微服务架构核心概念业务示例


一、什么是微服务

概念

微服务是一种架构风格,将单体应用拆分为一组小型、独立部署的服务,每个服务围绕特定业务能力构建,通过轻量级通信机制(通常是 HTTP/消息队列)协作。

与单体架构对比

对比项 单体架构 微服务架构
部署单元 整个应用一起打包部署 每个服务独立部署
数据库 所有模块共享一个数据库 每个服务有自己的数据库
通信方式 方法调用(进程内) HTTP/MQ(跨进程)
扩展方式 整体扩容 按需扩容单个服务
故障影响 一个模块崩溃,整体不可用 一个服务故障,其他不受影响
团队协作 所有人改同一个代码仓库 各团队负责自己的服务

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

二、服务间通信:OpenFeign

概念

OpenFeign 是 Spring Cloud 提供的声明式 HTTP 客户端。你只需要定义一个接口并加上注解,Feign 自动帮你完成 HTTP 请求的构造、发送和响应解析。

与手动 HTTP 调用的对比

手动 RestTemplate 方式(繁琐):

java 复制代码
String url = "http://xxx-order/api/xxx/order/result/receive";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<OmsOperationDto> entity = new HttpEntity<>(dto, headers);
ResponseEntity<RestControllerResult> response = 
    restTemplate.postForEntity(url, entity, RestControllerResult.class);

Feign 声明式方式(简洁):

java 复制代码
RestControllerResult<Boolean> result = xxxOrderFeign.noticeOms(dto);

通用代码示例

1. 定义 Feign 接口
java 复制代码
/**
 * 远程服务调用接口.
 * 声明式定义:只需写接口 + 注解,无需实现类.
 */
@FeignClient(
    value = "${service.name.user-service}/api",   // 服务名(从配置读取)
    fallbackFactory = UserFeignFallbackFactory.class  // 降级处理
)
public interface UserFeign {

    @GetMapping("/user/info/{userId}")
    RestControllerResult<UserDto> getUserInfo(@PathVariable("userId") Integer userId);

    @PostMapping("/user/batch-query")
    RestControllerResult<List<UserDto>> batchQuery(@RequestBody List<Integer> userIds);
}
2. Feign 降级工厂(Fallback Factory)
java 复制代码
/**
 * Feign 降级工厂.
 * 当远程服务不可用时执行降级逻辑,避免级联故障.
 */
@Component
public class UserFeignFallbackFactory implements FallbackFactory<UserFeign> {

    @Override
    public UserFeign create(Throwable cause) {
        return new UserFeign() {
            @Override
            public RestControllerResult<UserDto> getUserInfo(Integer userId) {
                log.warn("调用用户服务降级, userId={}, cause={}", userId, cause.getMessage());
                return RestControllerResult.fail("用户服务暂不可用");
            }

            @Override
            public RestControllerResult<List<UserDto>> batchQuery(List<Integer> userIds) {
                log.warn("批量查询用户降级, cause={}", cause.getMessage());
                return RestControllerResult.fail("用户服务暂不可用");
            }
        };
    }
}
3. 业务代码中调用
java 复制代码
@Service
public class OrderServiceImpl {

    @Resource
    private UserFeign userFeign;

    public OrderDetailVo getOrderDetail(Integer orderId) {
        Order order = orderRepository.findById(orderId);
        
        // 像调用本地方法一样调用远程服务
        RestControllerResult<UserDto> userResult = userFeign.getUserInfo(order.getUserId());
        
        if (Boolean.TRUE.equals(userResult.getSuccess())) {
            // 组装返回数据
            OrderDetailVo vo = new OrderDetailVo();
            vo.setOrder(order);
            vo.setUser(userResult.getData());
            return vo;
        }
        return null;
    }
}

Feign 的核心配置

yaml 复制代码
feign:
  sentinel:
    enabled: true          # 开启熔断
  client:
    config:
      default:
        connectTimeout: 60000   # 连接超时 60s
        readTimeout: 60000      # 读超时 60s
        loggerLevel: basic      # 日志级别

三、服务注册与发现

概念

微服务数量众多,每个服务可能有多个实例。服务注册与发现解决的问题是:调用方如何知道目标服务的真实地址?

复制代码
┌──────────┐  注册  ┌──────────────────┐  发现  ┌──────────┐
│ 服务A    │──────→│  注册中心         │←──────│ 服务B    │
│ 实例1    │       │  (Nacos/Eureka)  │       │          │
│ 实例2    │       │                  │       │          │
│ 实例3    │       │  服务A → [实例列表]│       │          │
└──────────┘       └──────────────────┘       └──────────┘

工作流程

  1. 服务A 启动后向注册中心注册自己的地址
  2. 服务B 需要调用服务A 时,先从注册中心获取服务A 的实例列表
  3. 通过负载均衡策略选择一个实例发起调用
  4. 注册中心通过心跳检测剔除不健康实例

实现方式

请求链路:Feign → API Gateway → 目标服务,网关负责服务路由和负载均衡。


四、服务熔断与降级

概念

当远程服务出现故障(超时、异常),如果不做处理,调用方会一直等待,最终拖垮自身。熔断器就像电路保险丝,当故障达到阈值时"断开",直接走降级逻辑。

三种状态

复制代码
        ┌──────────────────────────────────────────┐
        │                                          │
        ▼                                          │
  ┌──────────┐   故障率达标    ┌──────────┐        │
  │  CLOSED  │ ────────────→ │   OPEN   │        │ 探测成功
  │ (正常)   │               │ (熔断)   │        │
  └──────────┘               └─────┬────┘        │
        ▲                          │ 等待超时     │
        │                          ▼              │
        │                    ┌──────────┐         │
        │   探测失败          │HALF-OPEN │─────────┘
        └────────────────── │ (半开)    │
                            └──────────┘
  • CLOSED:正常调用,统计失败率
  • OPEN:直接走降级逻辑,不再调用远程服务
  • HALF-OPEN:放行少量请求探测,成功则恢复,失败则继续熔断

通用代码示例

java 复制代码
/**
 * 带降级的远程调用示例.
 * 降级逻辑保证主流程不受下游故障影响.
 */
@Service
@Slf4j
public class NotificationService {

    @Resource
    private NotificationFeign notificationFeign;

    /**
     * 发送通知(不影响主业务).
     * 即使通知服务挂了,主业务继续执行.
     */
    public void sendNotification(String message) {
        try {
            RestControllerResult<Boolean> result = notificationFeign.send(message);
            if (!Boolean.TRUE.equals(result.getSuccess())) {
                log.warn("发送通知失败: {}", result.getMessage());
            }
        } catch (Exception e) {
            // 通知失败不影响主流程
            log.warn("通知服务调用异常", e);
        }
    }
}

五、消息队列(MQ)

概念

消息队列是服务内部异步解耦的核心组件。生产者发送消息到队列,消费者从队列拉取消息处理,两者时间上解耦。

与事件中心的定位区别

复制代码
┌──────────────────────────────────────────────────────────┐
│  同一个服务/系统内部                                       │
│                                                          │
│  业务方法 ──MQ──→ 异步处理方法                             │
│  (同一个代码仓库,同一个团队维护)                           │
│                                                          │
│  用途:解耦耗时操作、削峰填谷、保证顺序                    │
└──────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────┐
│  跨系统/跨团队                                            │
│                                                          │
│  a服务 ──事件中心──→ b服务                           │
│  (不同代码仓库,不同团队维护)                               │
│                                                          │
│  用途:系统间通知、发布/订阅                               │
└──────────────────────────────────────────────────────────┘

通用代码示例

生产者
java 复制代码
/**
 * MQ 消息生产者.
 * 发送消息到指定队列.
 */
@Component
@Slf4j
public class OrderMqSender {

    @JshRabbitProducer(
        queue = "${mq.queue.order-process}",
        exchange = "${mq.exchange.order}")
    private JshRabbitTemplate rabbitTemplate;

    public void sendOrderProcess(Integer orderId) {
        if (orderId != null) {
            rabbitTemplate.send(orderId);
            log.info("发送订单处理消息, orderId={}", orderId);
        }
    }
}
消费者
java 复制代码
/**
 * MQ 消息消费者.
 * 通过 @Profile 控制是否启用.
 */
@Profile("order-process-consumer")  // 本地不想消费时可去掉此 profile
@Component
@Slf4j
public class OrderMqConsumer {

    @Resource
    private OrderProcessService orderProcessService;

    @JshRabbitConsumer(queue = "${mq.queue.order-process}")
    public void consume(Integer orderId) {
        log.info("消费订单处理消息, orderId={}", orderId);
        try {
            orderProcessService.process(orderId);
        } catch (Exception e) {
            log.error("订单处理失败, orderId={}", orderId, e);
            throw e;  // 抛出异常触发 MQ 重试
        }
    }
}

MQ 的作用:

  1. 异步化:不阻塞主业务事务
  2. 重试:消费失败自动重试
  3. 削峰:高并发时消息排队处理

六、分布式事务与数据一致性

问题

微服务各自有独立数据库,一个业务操作可能跨多个服务,无法使用传统的数据库事务保证一致性。

方案:本地消息表(最终一致性)

复制代码
┌─────────────────────────────────────────────────┐
│ 同一个数据库事务                                  │
│                                                   │
│  ① 执行业务逻辑(更新发货单状态等)               │
│  ② 插入消息记录(a_send_b_record_log)       │
│  ③ 事务提交                                      │
└───────────────────────┬─────────────────────────┘
                        │
                        ▼ 事务提交后
┌─────────────────────────────────────────────────┐
│  ④ 发送 MQ(携带 logId)                         │
│  ⑤ 消费者读取消息记录,推送事件中心               │
│  ⑥ 成功/失败更新消息状态                         │
│  ⑦ 失败可重试(运维接口/定时任务)               │
└─────────────────────────────────────────────────┘

通用代码示例

java 复制代码
/**
 * 本地消息表实现最终一致性.
 */
@Service
public class OrderService {

    @Resource
    private OrderRepository orderRepository;
    @Resource
    private MessageLogRepository messageLogRepository;

    @Transactional(rollbackFor = Exception.class)
    public void createOrder(OrderDto dto) {
        // ① 业务操作
        Order order = new Order();
        order.setOrderCode(dto.getOrderCode());
        order.setStatus("CREATED");
        orderRepository.save(order);

        // ② 同一事务内写消息记录
        MessageLog msgLog = new MessageLog();
        msgLog.setBizKey(order.getOrderCode());
        msgLog.setEventType("order.created");
        msgLog.setPayload(JSON.toJSONString(order));
        msgLog.setStatus(0);
        messageLogRepository.save(msgLog);

        // ③ 注册事务提交后的回调
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    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、第三方地址等)。配置中心提供统一管理和动态刷新。

通过配置实现多环境切换

yaml 复制代码
# 本地开发
eventcenter:
  event:
    route-event-to-oms:
      url: https://xxx.net/.../send   # 测试环境

# 生产环境(配置中心覆盖)
eventcenter:
  event:
    route-event-to-oms:
      url: https://xxx/.../send        # 生产环境

代码中通过 @Value 注入,无需修改代码即可切换环境:

java 复制代码
@Value("${eventcenter.event.route-event-to-oms.url}")
private String routeEventToOmsEventUrl;

九、服务间调用的安全认证

JWT Token 传递

微服务间调用需要传递身份信息。项目通过 Feign 请求拦截器自动带上 JWT Token:

复制代码
用户请求 → 网关(验证JWT)→ a服务 → Feign调用b服务(携带JWT)

通用示例:Feign 请求拦截器

java 复制代码
/**
 * Feign 请求拦截器.
 * 自动将当前请求的 JWT Token 传递给下游服务.
 */
@Configuration
public class FeignInterceptorConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            // 从当前请求上下文获取 Token
            ServletRequestAttributes attributes = 
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                String token = attributes.getRequest().getHeader("Authorization");
                if (token != null) {
                    requestTemplate.header("Authorization", token);
                }
            }
        };
    }
}
相关推荐
带娃的IT创业者1 小时前
当推理速度突破物理极限:深度解析 MiMo-v2.5-Pro-UltraSpeed 的 1000 TPS 架构革命
人工智能·架构·大模型·架构优化·mimo·tps·推理速度
光学补偿2 小时前
微服务学习 --- Spring Cloud概述
学习·spring cloud·微服务
一枚码农出身的猎头2 小时前
岗位招聘:架构师,base湖南长沙,70-80w
java·架构
2601_954706492 小时前
云虚拟化终端架构解析:ARM 云端集群部署与自动化运维实践
运维·arm开发·架构
米尔的可达鸭2 小时前
深入操作系统 Socket 底层:套接字控制块、FD映射、阻塞IO核心完整实现
arm开发·数据结构·websocket·网络协议·算法·架构·安全架构
春卷同学2 小时前
HarmonyOS掌上记账APP开发实践第1篇:HarmonyOS 多模块 HAR 架构最佳实践 — MoneyTrack 多模块拆分之道
华为·架构·harmonyos
AOwhisky2 小时前
Python 学习笔记(第六期)——函数:定义、参数传递与作用域
笔记·python·学习·云原生·运维开发·函数·参数传递
小小龙学IT3 小时前
Tigris:下一代无服务器数据库
数据库·云原生·serverless
爱勇宝3 小时前
《道德经》第5章:生产环境不相信眼泪
前端·后端·架构