微服务架构设计模式
概述
微服务架构是现代分布式系统的主流架构模式,它将单一应用程序开发为一套小服务,每个服务运行在自己的进程中,并通过轻量级机制通信。本文深入讲解微服务的设计模式、服务治理、以及实践中的关键技术。
核心面试问题
1. 微服务拆分策略
面试问题:如何进行微服务拆分?拆分的原则和边界是什么?
服务拆分原则
// 1. 业务边界拆分 - DDD领域驱动设计
// 用户域服务
@RestController
@RequestMapping("/api/users")
public class UserServiceController {
@Autowired
private UserDomainService userDomainService;
@PostMapping
public ResponseEntity<UserDTO> createUser(@RequestBody CreateUserRequest request) {
// 用户域的核心业务逻辑
User user = userDomainService.createUser(request);
return ResponseEntity.ok(UserDTO.from(user));
}
@GetMapping("/{userId}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long userId) {
User user = userDomainService.getUser(userId);
return ResponseEntity.ok(UserDTO.from(user));
}
@PutMapping("/{userId}/profile")
public ResponseEntity<Void> updateProfile(@PathVariable Long userId,
@RequestBody UpdateProfileRequest request) {
userDomainService.updateProfile(userId, request);
return ResponseEntity.ok().build();
}
}
// 订单域服务
@RestController
@RequestMapping("/api/orders")
public class OrderServiceController {
@Autowired
private OrderDomainService orderDomainService;
@Autowired
private UserServiceClient userServiceClient; // 远程调用用户服务
@PostMapping
public ResponseEntity<OrderDTO> createOrder(@RequestBody CreateOrderRequest request) {
// 验证用户信息(跨服务调用)
UserDTO user = userServiceClient.getUser(request.getUserId());
if (user == null) {
throw new UserNotFoundException("用户不存在");
}
Order order = orderDomainService.createOrder(request);
return ResponseEntity.ok(OrderDTO.from(order));
}
@GetMapping("/{orderId}")
public ResponseEntity<OrderDTO> getOrder(@PathVariable Long orderId) {
Order order = orderDomainService.getOrder(orderId);
return ResponseEntity.ok(OrderDTO.from(order));
}
}
// 2. 数据拆分 - 数据库分离
@Configuration
public class DatabaseConfig {
// 用户服务数据源
@Bean
@Primary
@ConfigurationProperties(prefix = "app.datasource.user")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
// 订单服务数据源
@Bean
@ConfigurationProperties(prefix = "app.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
// 用户服务的JPA配置
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean userEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(userDataSource());
em.setPackagesToScan("com.example.user.entity");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
// 订单服务的JPA配置
@Bean
public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(orderDataSource());
em.setPackagesToScan("com.example.order.entity");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
}
// 3. 团队组织结构对应 - Conway's Law
// 每个微服务对应一个独立的开发团队
public class ServiceOwnership {
// 用户服务团队负责
public static class UserServiceTeam {
// 团队成员:产品经理、架构师、开发工程师、测试工程师、运维工程师
// 技术栈:Spring Boot、MySQL、Redis
// 职责:用户注册、登录、个人信息管理、权限认证
}
// 订单服务团队负责
public static class OrderServiceTeam {
// 团队成员:产品经理、架构师、开发工程师、测试工程师、运维工程师
// 技术栈:Spring Boot、PostgreSQL、RabbitMQ
// 职责:订单创建、支付、物流、售后
}
// 商品服务团队负责
public static class ProductServiceTeam {
// 团队成员:产品经理、架构师、开发工程师、测试工程师、运维工程师
// 技术栈:Spring Boot、MongoDB、Elasticsearch
// 职责:商品管理、库存管理、搜索推荐
}
}
服务拆分实战
// 拆分前的单体应用
@RestController
public class MonolithController {
@Autowired
private UserService userService;
@Autowired
private OrderService orderService;
@Autowired
private ProductService productService;
@Autowired
private PaymentService paymentService;
// 下单流程 - 单体应用中的复杂业务逻辑
@PostMapping("/orders")
@Transactional
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
// 1. 验证用户
User user = userService.validateUser(request.getUserId());
// 2. 验证商品和库存
Product product = productService.getProduct(request.getProductId());
productService.checkStock(request.getProductId(), request.getQuantity());
// 3. 计算价格
BigDecimal totalAmount = product.getPrice().multiply(BigDecimal.valueOf(request.getQuantity()));
// 4. 创建订单
Order order = orderService.createOrder(user, product, request.getQuantity(), totalAmount);
// 5. 扣减库存
productService.reduceStock(request.getProductId(), request.getQuantity());
// 6. 处理支付
Payment payment = paymentService.processPayment(order.getId(), totalAmount);
// 7. 更新订单状态
orderService.updateOrderStatus(order.getId(), OrderStatus.PAID);
return ResponseEntity.ok(order);
}
}
// 拆分后的微服务架构
// 订单编排服务(Orchestrator Pattern)
@Service
public class OrderOrchestrationService {
@Autowired
private UserServiceClient userServiceClient;
@Autowired
private ProductServiceClient productServiceClient;
@Autowired
private PaymentServiceClient paymentServiceClient;
@Autowired
private OrderRepository orderRepository;
@Autowired
private EventPublisher eventPublisher;
public OrderResult createOrder(CreateOrderRequest request) {
String orderId = UUID.randomUUID().toString();
try {
// 1. 验证用户(远程调用)
UserValidationResult userResult = userServiceClient.validateUser(request.getUserId());
if (!userResult.isValid()) {
return OrderResult.failure("用户验证失败");
}
// 2. 验证商品和预占库存(远程调用)
StockReservationResult stockResult = productServiceClient.reserveStock(
request.getProductId(), request.getQuantity(), orderId);
if (!stockResult.isSuccess()) {
return OrderResult.failure("库存不足");
}
// 3. 创建订单(本地操作)
Order order = new Order(orderId, request.getUserId(), request.getProductId(),
request.getQuantity(), stockResult.getTotalAmount());
order.setStatus(OrderStatus.CREATED);
orderRepository.save(order);
// 4. 处理支付(远程调用)
PaymentResult paymentResult = paymentServiceClient.processPayment(
orderId, stockResult.getTotalAmount());
if (paymentResult.isSuccess()) {
// 支付成功,确认库存扣减
productServiceClient.confirmStockReduction(orderId);
// 更新订单状态
order.setStatus(OrderStatus.PAID);
orderRepository.save(order);
// 发布订单支付成功事件
eventPublisher.publishEvent(new OrderPaidEvent(orderId));
return OrderResult.success(orderId);
} else {
// 支付失败,释放库存
productServiceClient.releaseStock(orderId);
order.setStatus(OrderStatus.PAYMENT_FAILED);
orderRepository.save(order);
return OrderResult.failure("支付失败");
}
} catch (Exception e) {
// 异常处理,清理资源
cleanupResources(orderId);
return OrderResult.failure("系统异常: " + e.getMessage());
}
}
private void cleanupResources(String orderId) {
try {
productServiceClient.releaseStock(orderId);
} catch (Exception e) {
System.err.println("清理库存失败: " + e.getMessage());
}
}
}
// Saga模式 - 分布式事务处理
@Component
public class OrderSagaOrchestrator {
private final Map<String, SagaTransaction> activeSagas = new ConcurrentHashMap<>();
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
String sagaId = event.getOrderId();
SagaTransaction saga = new SagaTransaction(sagaId);
activeSagas.put(sagaId, saga);
// 执行Saga步骤
executeReserveInventory(saga, event);
}
private void executeReserveInventory(SagaTransaction saga, OrderCreatedEvent event) {
try {
productServiceClient.reserveInventory(event.getProductId(), event.getQuantity(), saga.getId());
saga.addCompletedStep(new ReserveInventoryStep(event.getProductId(), event.getQuantity()));
// 下一步:处理支付
executeProcessPayment(saga, event);
} catch (Exception e) {
// 补偿操作
compensateSaga(saga);
}
}
private void executeProcessPayment(SagaTransaction saga, OrderCreatedEvent event) {
try {
paymentServiceClient.processPayment(event.getOrderId(), event.getAmount());
saga.addCompletedStep(new ProcessPaymentStep(event.getOrderId(), event.getAmount()));
// Saga成功完成
completeSaga(saga);
} catch (Exception e) {
// 补偿操作
compensateSaga(saga);
}
}
private void compensateSaga(SagaTransaction saga) {
// 逆序执行补偿操作
List<SagaStep> completedSteps = saga.getCompletedSteps();
Collections.reverse(completedSteps);
for (SagaStep step : completedSteps) {
try {
step.compensate();
} catch (Exception e) {
System.err.println("补偿操作失败: " + e.getMessage());
}
}
activeSagas.remove(saga.getId());
}
private void completeSaga(SagaTransaction saga) {
activeSagas.remove(saga.getId());
System.out.println("Saga事务完成: " + saga.getId());
}
}
2. 服务通信模式
面试问题:微服务之间的通信方式有哪些?同步调用和异步消息的适用场景?
同步通信实现
// 1. RESTful API调用
@Component
public class UserServiceClient {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancer loadBalancer; // 负载均衡器
@Value("${services.user-service.url}")
private String userServiceUrl;
public UserDTO getUser(Long userId) {
try {
String url = userServiceUrl + "/api/users/" + userId;
return restTemplate.getForObject(url, UserDTO.class);
} catch (RestClientException e) {
throw new ServiceCallException("调用用户服务失败", e);
}
}
public UserValidationResult validateUser(Long userId) {
try {
String url = userServiceUrl + "/api/users/" + userId + "/validate";
return restTemplate.getForObject(url, UserValidationResult.class);
} catch (RestClientException e) {
// 服务降级
return UserValidationResult.defaultValid();
}
}
// 使用服务发现的动态调用
public UserDTO getUserWithDiscovery(Long userId) {
ServiceInstance instance = loadBalancer.choose("user-service");
if (instance == null) {
throw new ServiceUnavailableException("用户服务不可用");
}
String url = "http://" + instance.getHost() + ":" + instance.getPort() +
"/api/users/" + userId;
return restTemplate.getForObject(url, UserDTO.class);
}
}
// 2. gRPC调用(高性能场景)
@Service
public class OrderServiceGrpcClient {
private final OrderServiceGrpc.OrderServiceBlockingStub blockingStub;
private final OrderServiceGrpc.OrderServiceStub asyncStub;
public OrderServiceGrpcClient(@Value("${services.order-service.grpc.host}") String host,
@Value("${services.order-service.grpc.port}") int port) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
this.blockingStub = OrderServiceGrpc.newBlockingStub(channel);
this.asyncStub = OrderServiceGrpc.newStub(channel);
}
public CreateOrderResponse createOrder(CreateOrderRequest request) {
CreateOrderGrpcRequest grpcRequest = CreateOrderGrpcRequest.newBuilder()
.setUserId(request.getUserId())
.setProductId(request.getProductId())
.setQuantity(request.getQuantity())
.build();
try {
CreateOrderGrpcResponse grpcResponse = blockingStub.createOrder(grpcRequest);
return CreateOrderResponse.builder()
.orderId(grpcResponse.getOrderId())
.status(grpcResponse.getStatus())
.message(grpcResponse.getMessage())
.build();
} catch (StatusRuntimeException e) {
throw new ServiceCallException("gRPC调用失败: " + e.getStatus(), e);
}
}
// 异步gRPC调用
public CompletableFuture<CreateOrderResponse> createOrderAsync(CreateOrderRequest request) {
CreateOrderGrpcRequest grpcRequest = CreateOrderGrpcRequest.newBuilder()
.setUserId(request.getUserId())
.setProductId(request.getProductId())
.setQuantity(request.getQuantity())
.build();
CompletableFuture<CreateOrderResponse> future = new CompletableFuture<>();
asyncStub.createOrder(grpcRequest, new StreamObserver<CreateOrderGrpcResponse>() {
@Override
public void onNext(CreateOrderGrpcResponse response) {
CreateOrderResponse result = CreateOrderResponse.builder()
.orderId(response.getOrderId())
.status(response.getStatus())
.message(response.getMessage())
.build();
future.complete(result);
}
@Override
public void onError(Throwable t) {
future.completeExceptionally(new ServiceCallException("异步gRPC调用失败", t));
}
@Override
public void onCompleted() {
// 调用完成
}
});
return future;
}
}
// 3. 服务熔断器模式
@Component
public class PaymentServiceClient {
@Autowired
private RestTemplate restTemplate;
private final CircuitBreaker circuitBreaker;
public PaymentServiceClient() {
this.circuitBreaker = CircuitBreaker.ofDefaults("payment-service");
// 配置熔断器
circuitBreaker.getEventPublisher()
.onStateTransition(event ->
System.out.println("熔断器状态变更: " + event.getStateTransition()));
}
public PaymentResult processPayment(String orderId, BigDecimal amount) {
Supplier<PaymentResult> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> doProcessPayment(orderId, amount));
try {
return decoratedSupplier.get();
} catch (CallNotPermittedException e) {
// 熔断器打开,使用降级逻辑
return PaymentResult.failure("支付服务暂时不可用,请稍后重试");
}
}
private PaymentResult doProcessPayment(String orderId, BigDecimal amount) {
String url = "http://payment-service/api/payments";
PaymentRequest request = new PaymentRequest(orderId, amount);
PaymentResponse response = restTemplate.postForObject(url, request, PaymentResponse.class);
if (response != null && response.isSuccess()) {
return PaymentResult.success(response.getTransactionId());
} else {
throw new PaymentException("支付处理失败");
}
}
}
异步通信实现
// 1. 事件驱动架构
@Component
public class OrderEventHandler {
@Autowired
private InventoryService inventoryService;
@Autowired
private NotificationService notificationService;
@Autowired
private EventPublisher eventPublisher;
// 处理订单创建事件
@EventListener
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
try {
// 异步扣减库存
inventoryService.reduceInventory(event.getProductId(), event.getQuantity());
// 发布库存扣减成功事件
eventPublisher.publishEvent(new InventoryReducedEvent(
event.getOrderId(), event.getProductId(), event.getQuantity()));
} catch (Exception e) {
// 发布库存扣减失败事件
eventPublisher.publishEvent(new InventoryReductionFailedEvent(
event.getOrderId(), e.getMessage()));
}
}
// 处理支付成功事件
@EventListener
@Async
public void handlePaymentSucceeded(PaymentSucceededEvent event) {
try {
// 发送订单确认通知
notificationService.sendOrderConfirmation(event.getOrderId());
// 更新用户积分
updateUserPoints(event.getUserId(), event.getAmount());
} catch (Exception e) {
System.err.println("处理支付成功事件失败: " + e.getMessage());
}
}
private void updateUserPoints(Long userId, BigDecimal amount) {
// 异步更新用户积分
CompletableFuture.runAsync(() -> {
try {
userServiceClient.addPoints(userId, amount.intValue() / 10);
} catch (Exception e) {
System.err.println("更新用户积分失败: " + e.getMessage());
}
});
}
}
// 2. 消息队列通信
@Component
public class OrderMessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
// 发送订单事件到RabbitMQ
public void sendOrderEvent(OrderEvent event) {
String routingKey = getRoutingKey(event.getEventType());
rabbitTemplate.convertAndSend("order.exchange", routingKey, event, message -> {
// 设置消息属性
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
message.getMessageProperties().setTimestamp(new Date());
message.getMessageProperties().setPersistent(true);
return message;
});
}
// 发送订单事件到Kafka
public void sendOrderEventToKafka(OrderEvent event) {
String topic = "order-events";
String key = event.getOrderId(); // 使用订单ID作为分区键,保证同一订单的事件有序
kafkaTemplate.send(topic, key, event).addCallback(
result -> System.out.println("订单事件发送成功: " + event.getOrderId()),
failure -> System.err.println("订单事件发送失败: " + failure.getMessage())
);
}
private String getRoutingKey(OrderEventType eventType) {
switch (eventType) {
case ORDER_CREATED:
return "order.created";
case ORDER_PAID:
return "order.paid";
case ORDER_SHIPPED:
return "order.shipped";
default:
return "order.unknown";
}
}
}
@Component
public class OrderMessageConsumer {
@Autowired
private OrderService orderService;
// 消费RabbitMQ消息
@RabbitListener(queues = "inventory.order.queue")
public void handleInventoryUpdate(InventoryUpdateEvent event, Acknowledgment ack) {
try {
orderService.updateOrderInventoryStatus(event.getOrderId(), event.getStatus());
ack.acknowledge();
} catch (Exception e) {
System.err.println("处理库存更新事件失败: " + e.getMessage());
// 消息会重新入队重试
}
}
// 消费Kafka消息
@KafkaListener(topics = "payment-events", groupId = "order-service-group")
public void handlePaymentEvent(PaymentEvent event) {
try {
if (event.getEventType() == PaymentEventType.PAYMENT_SUCCEEDED) {
orderService.markOrderAsPaid(event.getOrderId());
} else if (event.getEventType() == PaymentEventType.PAYMENT_FAILED) {
orderService.markOrderAsPaymentFailed(event.getOrderId());
}
} catch (Exception e) {
System.err.println("处理支付事件失败: " + e.getMessage());
}
}
}
// 3. 请求-响应模式的异步实现
@Service
public class AsyncOrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
private final Map<String, CompletableFuture<OrderResult>> pendingRequests = new ConcurrentHashMap<>();
// 异步创建订单
public CompletableFuture<OrderResult> createOrderAsync(CreateOrderRequest request) {
String requestId = UUID.randomUUID().toString();
CompletableFuture<OrderResult> future = new CompletableFuture<>();
// 存储待处理的请求
pendingRequests.put(requestId, future);
// 发送异步请求
AsyncOrderCreateRequest asyncRequest = new AsyncOrderCreateRequest(requestId, request);
rabbitTemplate.convertAndSend("order.async.request", asyncRequest);
// 设置超时
CompletableFuture.delayedExecutor(30, TimeUnit.SECONDS).execute(() -> {
CompletableFuture<OrderResult> timeoutFuture = pendingRequests.remove(requestId);
if (timeoutFuture != null && !timeoutFuture.isDone()) {
timeoutFuture.completeExceptionally(new TimeoutException("订单创建超时"));
}
});
return future;
}
// 处理异步响应
@RabbitListener(queues = "order.async.response")
public void handleAsyncOrderResponse(AsyncOrderCreateResponse response) {
String requestId = response.getRequestId();
CompletableFuture<OrderResult> future = pendingRequests.remove(requestId);
if (future != null) {
if (response.isSuccess()) {
future.complete(OrderResult.success(response.getOrderId()));
} else {
future.complete(OrderResult.failure(response.getErrorMessage()));
}
}
}
}
3. 服务发现与注册
面试问题:服务发现的实现方式有哪些?Eureka、Consul、Nacos的区别?
服务注册与发现实现
// 1. Eureka服务注册与发现
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
@LoadBalanced // 启用客户端负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
public class EurekaServiceDiscovery {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private EurekaClient eurekaClient;
public List<ServiceInstance> getServiceInstances(String serviceName) {
return discoveryClient.getInstances(serviceName);
}
public ServiceInstance chooseInstance(String serviceName) {
List<ServiceInstance> instances = getServiceInstances(serviceName);
if (instances.isEmpty()) {
throw new ServiceNotFoundException("服务不存在: " + serviceName);
}
// 简单的轮询负载均衡
int index = ThreadLocalRandom.current().nextInt(instances.size());
return instances.get(index);
}
// 使用Eureka原生API
public Application getApplication(String serviceName) {
return eurekaClient.getApplication(serviceName);
}
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress) {
return eurekaClient.getInstancesByVipAddress(vipAddress, false);
}
}
// 2. Consul服务注册与发现
@Configuration
@EnableConsulServiceRegistry
public class ConsulConfig {
@Bean
public ConsulServiceRegistryAutoConfiguration.ConsulServiceRegistry consulServiceRegistry(
ConsulClient consulClient, ConsulDiscoveryProperties properties) {
return new ConsulServiceRegistryAutoConfiguration.ConsulServiceRegistry(
consulClient, properties, new HeartbeatProperties());
}
@Bean
public ConsulClient consulClient(@Value("${spring.cloud.consul.host:localhost}") String host,
@Value("${spring.cloud.consul.port:8500}") int port) {
return new ConsulClient(host, port);
}
}
@Service
public class ConsulServiceDiscovery {
@Autowired
private ConsulClient consulClient;
@Autowired
private DiscoveryClient discoveryClient;
public List<ServiceInstance> getHealthyInstances(String serviceName) {
return discoveryClient.getInstances(serviceName).stream()
.filter(instance -> isInstanceHealthy(serviceName, instance))
.collect(Collectors.toList());
}
private boolean isInstanceHealthy(String serviceName, ServiceInstance instance) {
try {
Response<List<HealthService>> response = consulClient.getHealthServices(
serviceName, true, QueryParams.DEFAULT);
return response.getValue().stream()
.anyMatch(service ->
service.getService().getAddress().equals(instance.getHost()) &&
service.getService().getPort().equals(instance.getPort()));
} catch (Exception e) {
return false;
}
}
public void registerService(String serviceName, String serviceId, String host, int port) {
NewService newService = new NewService();
newService.setId(serviceId);
newService.setName(serviceName);
newService.setAddress(host);
newService.setPort(port);
// 设置健康检查
NewService.Check check = new NewService.Check();
check.setHttp("http://" + host + ":" + port + "/actuator/health");
check.setInterval("10s");
newService.setCheck(check);
consulClient.agentServiceRegister(newService);
}
}
// 3. 自定义服务注册与发现
@Component
public class CustomServiceRegistry {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ApplicationContext applicationContext;
private static final String SERVICE_REGISTRY_KEY = "services:registry:";
private static final String SERVICE_HEARTBEAT_KEY = "services:heartbeat:";
// 服务注册
@PostConstruct
public void registerService() {
String serviceName = getServiceName();
String serviceId = getServiceId();
ServiceInstance instance = createServiceInstance();
// 注册服务实例
String registryKey = SERVICE_REGISTRY_KEY + serviceName;
redisTemplate.opsForHash().put(registryKey, serviceId, JSON.toJSONString(instance));
// 设置服务过期时间
redisTemplate.expire(registryKey, Duration.ofMinutes(5));
// 启动心跳
startHeartbeat(serviceName, serviceId);
}
private void startHeartbeat(String serviceName, String serviceId) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleWithFixedDelay(() -> {
try {
// 发送心跳
String heartbeatKey = SERVICE_HEARTBEAT_KEY + serviceName + ":" + serviceId;
redisTemplate.opsForValue().set(heartbeatKey, System.currentTimeMillis(), Duration.ofMinutes(2));
// 续约服务注册
String registryKey = SERVICE_REGISTRY_KEY + serviceName;
redisTemplate.expire(registryKey, Duration.ofMinutes(5));
} catch (Exception e) {
System.err.println("服务心跳失败: " + e.getMessage());
}
}, 30, 30, TimeUnit.SECONDS);
}
// 服务发现
public List<ServiceInstance> discoverServices(String serviceName) {
String registryKey = SERVICE_REGISTRY_KEY + serviceName;
Map<Object, Object> serviceMap = redisTemplate.opsForHash().entries(registryKey);
List<ServiceInstance> instances = new ArrayList<>();
for (Map.Entry<Object, Object> entry : serviceMap.entrySet()) {
String serviceId = (String) entry.getKey();
String instanceJson = (String) entry.getValue();
// 检查心跳
if (isServiceAlive(serviceName, serviceId)) {
ServiceInstance instance = JSON.parseObject(instanceJson, ServiceInstance.class);
instances.add(instance);
}
}
return instances;
}
private boolean isServiceAlive(String serviceName, String serviceId) {
String heartbeatKey = SERVICE_HEARTBEAT_KEY + serviceName + ":" + serviceId;
Object lastHeartbeat = redisTemplate.opsForValue().get(heartbeatKey);
if (lastHeartbeat == null) {
return false;
}
long lastHeartbeatTime = Long.parseLong(lastHeartbeat.toString());
long currentTime = System.currentTimeMillis();
// 心跳超时阈值:2分钟
return (currentTime - lastHeartbeatTime) < 120000;
}
private String getServiceName() {
return applicationContext.getEnvironment().getProperty("spring.application.name", "unknown");
}
private String getServiceId() {
return getServiceName() + "-" + UUID.randomUUID().toString().substring(0, 8);
}
private ServiceInstance createServiceInstance() {
String host = getLocalHost();
int port = getServerPort();
ServiceInstance instance = new ServiceInstance();
instance.setServiceName(getServiceName());
instance.setHost(host);
instance.setPort(port);
instance.setHealthy(true);
instance.setMetadata(getServiceMetadata());
return instance;
}
private String getLocalHost() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (Exception e) {
return "localhost";
}
}
private int getServerPort() {
return applicationContext.getEnvironment().getProperty("server.port", Integer.class, 8080);
}
private Map<String, String> getServiceMetadata() {
Map<String, String> metadata = new HashMap<>();
metadata.put("version", "1.0.0");
metadata.put("environment", getEnvironment());
metadata.put("startTime", String.valueOf(System.currentTimeMillis()));
return metadata;
}
private String getEnvironment() {
return applicationContext.getEnvironment().getProperty("spring.profiles.active", "default");
}
}
4. API网关模式
面试问题:API网关的作用是什么?如何实现路由、认证、限流等功能?
API网关实现
// 1. 使用Spring Cloud Gateway
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 用户服务路由
.route("user-service", r -> r.path("/api/users/**")
.filters(f -> f
.stripPrefix(1) // 去掉 /api 前缀
.addRequestHeader("X-Gateway", "Spring-Cloud-Gateway")
.circuitBreaker(c -> c
.setName("user-service-cb")
.setFallbackUri("forward:/fallback/user"))
.retry(retryConfig -> retryConfig
.setRetries(3)
.setBackoff(Duration.ofSeconds(1), Duration.ofSeconds(5), 2, true)))
.uri("lb://user-service"))
// 订单服务路由
.route("order-service", r -> r.path("/api/orders/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway", "Spring-Cloud-Gateway")
.requestRateLimiter(rl -> rl
.setRateLimiter(redisRateLimiter())
.setKeyResolver(userKeyResolver())))
.uri("lb://order-service"))
// 商品服务路由
.route("product-service", r -> r.path("/api/products/**")
.and().method(HttpMethod.GET)
.filters(f -> f
.stripPrefix(1)
.addResponseHeader("Cache-Control", "max-age=300"))
.uri("lb://product-service"))
.build();
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20); // 每秒10个请求,突发20个
}
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
String userId = exchange.getRequest().getHeaders().getFirst("User-Id");
return Mono.just(userId != null ? userId : "anonymous");
};
}
}
// 2. 自定义过滤器
@Component
public class AuthenticationGatewayFilter implements GatewayFilter, Ordered {
@Autowired
private AuthService authService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 跳过不需要认证的路径
if (isPublicPath(path)) {
return chain.filter(exchange);
}
// 获取认证令牌
String token = extractToken(request);
if (token == null) {
return handleUnauthorized(exchange, "缺少认证令牌");
}
// 验证令牌
return authService.validateToken(token)
.flatMap(userInfo -> {
if (userInfo != null) {
// 添加用户信息到请求头
ServerHttpRequest mutatedRequest = request.mutate()
.header("User-Id", userInfo.getUserId().toString())
.header("User-Name", userInfo.getUsername())
.header("User-Roles", String.join(",", userInfo.getRoles()))
.build();
ServerWebExchange mutatedExchange = exchange.mutate()
.request(mutatedRequest)
.build();
return chain.filter(mutatedExchange);
} else {
return handleUnauthorized(exchange, "无效的认证令牌");
}
})
.onErrorResume(throwable -> {
return handleUnauthorized(exchange, "认证服务异常");
});
}
private boolean isPublicPath(String path) {
List<String> publicPaths = Arrays.asList(
"/api/auth/login",
"/api/auth/register",
"/api/health",
"/api/docs"
);
return publicPaths.stream().anyMatch(path::startsWith);
}
private String extractToken(ServerHttpRequest request) {
String authorization = request.getHeaders().getFirst("Authorization");
if (authorization != null && authorization.startsWith("Bearer ")) {
return authorization.substring(7);
}
return null;
}
private Mono<Void> handleUnauthorized(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type", "application/json");
String body = JSON.toJSONString(new ErrorResponse("UNAUTHORIZED", message));
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
return -100; // 高优先级,在其他过滤器之前执行
}
}
// 3. 限流过滤器
@Component
public class RateLimitingGatewayFilter implements GatewayFilter, Ordered {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String clientId = getClientId(request);
String path = request.getURI().getPath();
return checkRateLimit(clientId, path)
.flatMap(allowed -> {
if (allowed) {
return chain.filter(exchange);
} else {
return handleRateLimitExceeded(exchange);
}
});
}
private Mono<Boolean> checkRateLimit(String clientId, String path) {
return Mono.fromCallable(() -> {
String key = "rate_limit:" + clientId + ":" + path;
// 使用滑动窗口算法
long currentTime = System.currentTimeMillis();
long windowSize = 60000; // 1分钟窗口
int maxRequests = 100; // 每分钟最多100个请求
// 使用Lua脚本保证原子性
String script = """
local key = KEYS[1]
local window = tonumber(ARGV[1])
local limit = tonumber(ARGV[2])
local current = tonumber(ARGV[3])
-- 清除过期的记录
redis.call('zremrangebyscore', key, '-inf', current - window)
-- 获取当前窗口内的请求数
local count = redis.call('zcard', key)
if count < limit then
-- 添加当前请求
redis.call('zadd', key, current, current)
redis.call('expire', key, math.ceil(window / 1000))
return 1
else
return 0
end
""";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Long result = redisTemplate.execute(redisScript,
Collections.singletonList(key),
String.valueOf(windowSize),
String.valueOf(maxRequests),
String.valueOf(currentTime));
return result != null && result == 1;
});
}
private String getClientId(ServerHttpRequest request) {
// 优先使用用户ID
String userId = request.getHeaders().getFirst("User-Id");
if (userId != null) {
return "user:" + userId;
}
// 其次使用API Key
String apiKey = request.getHeaders().getFirst("X-API-Key");
if (apiKey != null) {
return "api:" + apiKey;
}
// 最后使用IP地址
String clientIP = getClientIP(request);
return "ip:" + clientIP;
}
private String getClientIP(ServerHttpRequest request) {
String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
String xRealIP = request.getHeaders().getFirst("X-Real-IP");
if (xRealIP != null && !xRealIP.isEmpty()) {
return xRealIP;
}
return request.getRemoteAddress() != null ?
request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
}
private Mono<Void> handleRateLimitExceeded(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
response.getHeaders().add("Content-Type", "application/json");
String body = JSON.toJSONString(new ErrorResponse("RATE_LIMIT_EXCEEDED", "请求频率超过限制"));
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
return -50; // 在认证过滤器之后执行
}
}
// 4. 全局异常处理
@Component
public class GlobalErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes,
WebProperties.Resources resources,
ApplicationContext applicationContext) {
super(errorAttributes, resources, applicationContext);
}
@Override
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Throwable error = getError(request);
if (error instanceof ConnectException) {
// 服务连接异常
return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(new ErrorResponse("SERVICE_UNAVAILABLE", "服务暂时不可用")));
} else if (error instanceof TimeoutException) {
// 请求超时
return ServerResponse.status(HttpStatus.GATEWAY_TIMEOUT)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(new ErrorResponse("GATEWAY_TIMEOUT", "请求超时")));
} else {
// 其他异常
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(new ErrorResponse("INTERNAL_ERROR", "网关内部错误")));
}
}
}
5. 微服务监控与治理
面试问题:如何监控微服务的健康状态?分布式链路追踪的实现原理?
监控与治理实现
// 1. 健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public Health health() {
try {
// 检查数据库连接
boolean dbHealthy = checkDatabaseHealth();
// 检查Redis连接
boolean redisHealthy = checkRedisHealth();
// 检查外部依赖
boolean externalHealthy = checkExternalDependencies();
if (dbHealthy && redisHealthy && externalHealthy) {
return Health.up()
.withDetail("database", "UP")
.withDetail("redis", "UP")
.withDetail("external", "UP")
.withDetail("timestamp", new Date())
.build();
} else {
return Health.down()
.withDetail("database", dbHealthy ? "UP" : "DOWN")
.withDetail("redis", redisHealthy ? "UP" : "DOWN")
.withDetail("external", externalHealthy ? "UP" : "DOWN")
.withDetail("timestamp", new Date())
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.withDetail("timestamp", new Date())
.build();
}
}
private boolean checkDatabaseHealth() {
try (Connection connection = dataSource.getConnection()) {
return connection.isValid(5); // 5秒超时
} catch (Exception e) {
return false;
}
}
private boolean checkRedisHealth() {
try {
redisTemplate.execute((RedisCallback<String>) connection -> connection.ping());
return true;
} catch (Exception e) {
return false;
}
}
private boolean checkExternalDependencies() {
// 检查外部服务可用性
return checkUserService() && checkPaymentService();
}
private boolean checkUserService() {
try {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(
"http://user-service/actuator/health", String.class);
return response.getStatusCode().is2xxSuccessful();
} catch (Exception e) {
return false;
}
}
private boolean checkPaymentService() {
try {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(
"http://payment-service/actuator/health", String.class);
return response.getStatusCode().is2xxSuccessful();
} catch (Exception e) {
return false;
}
}
}
// 2. 指标收集
@Component
public class CustomMetrics {
private final Counter orderCreatedCounter;
private final Timer orderProcessingTimer;
private final Gauge activeOrdersGauge;
@Autowired
private OrderRepository orderRepository;
public CustomMetrics(MeterRegistry meterRegistry) {
this.orderCreatedCounter = Counter.builder("orders.created")
.description("Total orders created")
.register(meterRegistry);
this.orderProcessingTimer = Timer.builder("order.processing.time")
.description("Order processing time")
.register(meterRegistry);
this.activeOrdersGauge = Gauge.builder("orders.active")
.description("Number of active orders")
.register(meterRegistry, this, CustomMetrics::getActiveOrderCount);
}
public void recordOrderCreated() {
orderCreatedCounter.increment();
}
public Timer.Sample startOrderProcessingTimer() {
return Timer.start();
}
public void recordOrderProcessingTime(Timer.Sample sample) {
sample.stop(orderProcessingTimer);
}
private double getActiveOrderCount() {
return orderRepository.countByStatus(OrderStatus.PROCESSING);
}
// 自定义业务指标
@EventListener
public void handleOrderEvent(OrderEvent event) {
Tags tags = Tags.of(
"event_type", event.getEventType().toString(),
"service", "order-service"
);
switch (event.getEventType()) {
case ORDER_CREATED:
recordOrderCreated();
break;
case ORDER_PAID:
Metrics.counter("orders.paid", tags).increment();
break;
case ORDER_SHIPPED:
Metrics.counter("orders.shipped", tags).increment();
break;
case ORDER_CANCELLED:
Metrics.counter("orders.cancelled", tags).increment();
break;
}
}
}
// 3. 分布式链路追踪
@Component
public class TracingConfiguration {
// 使用Sleuth进行链路追踪
@Bean
public Sampler alwaysSampler() {
return Sampler.create(1.0f); // 100%采样
}
// 自定义Span
@NewSpan("order-processing")
public OrderResult processOrder(@SpanTag("orderId") String orderId, CreateOrderRequest request) {
Span span = tracer.nextSpan()
.name("process-order")
.tag("order.id", orderId)
.tag("user.id", request.getUserId().toString())
.start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
// 添加自定义事件
span.event("order.validation.start");
// 执行业务逻辑
validateOrder(request);
span.event("order.validation.complete");
span.event("order.creation.start");
OrderResult result = createOrder(request);
span.event("order.creation.complete");
span.tag("order.status", result.getStatus());
return result;
} catch (Exception e) {
span.tag("error", e.getMessage());
throw e;
} finally {
span.end();
}
}
@Autowired
private Tracer tracer;
private void validateOrder(CreateOrderRequest request) {
// 订单验证逻辑
}
private OrderResult createOrder(CreateOrderRequest request) {
// 订单创建逻辑
return OrderResult.success("order-123");
}
}
// 4. 日志聚合
@Component
public class StructuredLogging {
private static final Logger logger = LoggerFactory.getLogger(StructuredLogging.class);
public void logOrderEvent(String orderId, String eventType, Map<String, Object> details) {
// 结构化日志
MDC.put("service", "order-service");
MDC.put("orderId", orderId);
MDC.put("eventType", eventType);
MDC.put("timestamp", Instant.now().toString());
String jsonLog = JSON.toJSONString(Map.of(
"service", "order-service",
"orderId", orderId,
"eventType", eventType,
"details", details,
"timestamp", Instant.now().toString()
));
logger.info("Order event: {}", jsonLog);
// 清理MDC
MDC.clear();
}
// 使用ELK Stack进行日志收集和分析
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
Map<String, Object> details = Map.of(
"userId", event.getUserId(),
"productId", event.getProductId(),
"quantity", event.getQuantity(),
"amount", event.getAmount()
);
logOrderEvent(event.getOrderId(), "ORDER_CREATED", details);
}
}
// 5. 服务熔断与降级
@Component
public class CircuitBreakerService {
private final CircuitBreaker userServiceCircuitBreaker;
private final CircuitBreaker paymentServiceCircuitBreaker;
public CircuitBreakerService() {
// 配置用户服务熔断器
this.userServiceCircuitBreaker = CircuitBreaker.ofDefaults("user-service");
configureCircuitBreaker(userServiceCircuitBreaker);
// 配置支付服务熔断器
this.paymentServiceCircuitBreaker = CircuitBreaker.ofDefaults("payment-service");
configureCircuitBreaker(paymentServiceCircuitBreaker);
}
private void configureCircuitBreaker(CircuitBreaker circuitBreaker) {
circuitBreaker.getEventPublisher()
.onStateTransition(event -> {
System.out.println("熔断器状态变更: " + event);
// 发送告警通知
sendAlert(event);
})
.onCallNotPermitted(event -> {
System.out.println("熔断器阻断调用: " + event);
})
.onError(event -> {
System.out.println("熔断器记录错误: " + event);
});
}
public UserDTO getUserWithFallback(Long userId) {
Supplier<UserDTO> decoratedSupplier = CircuitBreaker
.decorateSupplier(userServiceCircuitBreaker, () -> userServiceClient.getUser(userId));
try {
return decoratedSupplier.get();
} catch (CallNotPermittedException e) {
// 熔断器打开,使用降级策略
return getFallbackUser(userId);
}
}
private UserDTO getFallbackUser(Long userId) {
// 降级策略:返回默认用户信息或从缓存获取
return UserDTO.builder()
.id(userId)
.username("Unknown User")
.email("unknown@example.com")
.status(1)
.build();
}
private void sendAlert(CircuitBreakerOnStateTransitionEvent event) {
if (event.getStateTransition().getToState() == CircuitBreaker.State.OPEN) {
// 熔断器打开告警
String message = String.format("服务熔断器打开: %s", event.getCircuitBreakerName());
alertService.sendAlert("CIRCUIT_BREAKER_OPEN", message);
}
}
@Autowired
private AlertService alertService;
@Autowired
private UserServiceClient userServiceClient;
}
高频面试题目
1. 理论深度题目
Q: 微服务和单体应用的优缺点对比?
A: 微服务优点:
- 技术多样性:不同服务可使用不同技术栈
- 独立部署:服务可独立发布和扩展
- 团队自治:小团队独立开发维护
- 故障隔离:单个服务故障不影响整体
微服务缺点:
- 分布式复杂性:网络延迟、分布式事务
- 运维复杂度:需要完善的监控和治理
- 数据一致性:跨服务事务处理困难
Q: 如何解决微服务的数据一致性问题?
A: 数据一致性解决方案:
- 最终一致性:通过事件驱动达到最终一致
- Saga模式:长事务拆分为多个短事务
- TCC模式:Try-Confirm-Cancel两阶段提交
- 事件溯源:通过事件重放恢复状态
2. 实战应用题目
Q: 如何设计一个电商系统的微服务架构?
答题要点:
- 服务拆分:用户、商品、订单、支付、库存等服务
- 数据设计:每个服务独立数据库
- 通信方式:同步调用+异步消息
- 一致性保证:事件驱动+补偿机制
- 监控治理:链路追踪+健康检查
Q: 微服务部署策略有哪些?
答题要点:
- 蓝绿部署:零停机时间部署
- 金丝雀发布:灰度发布降低风险
- 滚动更新:逐步替换实例
- 容器化部署:Docker+Kubernetes
- 服务网格:Istio统一治理
总结
微服务架构面试重点:
- 架构设计:服务拆分原则、边界定义
- 通信模式:同步/异步通信、服务间调用
- 服务治理:注册发现、负载均衡、熔断降级
- 数据管理:分布式事务、数据一致性
- 运维监控:健康检查、链路追踪、日志聚合
建议结合实际微服务项目经验,能够描述完整的架构设计和技术选型思路。