Skip to content

Spring Cloud 集成

本文档介绍如何在 LCGYL Framework 中集成 Spring Cloud,构建微服务架构。

快速开始

添加依赖

gradle
dependencies {
    implementation 'com.lcgyl:lcgyl-spring-cloud:${version}'
    implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:2023.0.0"
    }
}
xml
<dependencies>
    <dependency>
        <groupId>com.lcgyl</groupId>
        <artifactId>lcgyl-spring-cloud</artifactId>
        <version>${version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2023.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

服务注册与发现

Nacos 配置

yaml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: dev
        group: DEFAULT_GROUP

Eureka 配置

yaml
spring:
  application:
    name: user-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

服务注册

java
@SpringBootApplication
@EnableDiscoveryClient
@EnableLcgylFramework
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

服务调用

OpenFeign

java
// 添加依赖
// implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
}

// 定义 Feign 客户端
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
    
    @GetMapping("/api/users/{id}")
    User getUser(@PathVariable("id") Long id);
    
    @GetMapping("/api/users")
    List<User> getUsers(@RequestParam("ids") List<Long> ids);
    
    @PostMapping("/api/users")
    User createUser(@RequestBody User user);
}

// 降级处理
@Component
public class UserClientFallback implements UserClient {
    
    @Override
    public User getUser(Long id) {
        return new User(id, "默认用户", "default@example.com");
    }
    
    @Override
    public List<User> getUsers(List<Long> ids) {
        return Collections.emptyList();
    }
    
    @Override
    public User createUser(User user) {
        throw new ServiceUnavailableException("用户服务不可用");
    }
}

RestTemplate

java
@Configuration
public class RestTemplateConfig {
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@Service
public class UserService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    public User getUser(Long id) {
        return restTemplate.getForObject(
            "http://user-service/api/users/{id}",
            User.class,
            id
        );
    }
}

WebClient

java
@Configuration
public class WebClientConfig {
    
    @Bean
    @LoadBalanced
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}

@Service
public class UserService {
    
    @Autowired
    private WebClient.Builder webClientBuilder;
    
    public Mono<User> getUser(Long id) {
        return webClientBuilder.build()
            .get()
            .uri("http://user-service/api/users/{id}", id)
            .retrieve()
            .bodyToMono(User.class);
    }
}

配置中心

Nacos 配置中心

yaml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        file-extension: yaml
        namespace: dev
        group: DEFAULT_GROUP

动态配置

java
@RefreshScope
@RestController
public class ConfigController {
    
    @Value("${app.feature.enabled:false}")
    private boolean featureEnabled;
    
    @GetMapping("/config")
    public Map<String, Object> getConfig() {
        return Map.of("featureEnabled", featureEnabled);
    }
}

配置监听

java
@Component
public class ConfigChangeListener {
    
    @NacosConfigListener(dataId = "user-service.yaml", groupId = "DEFAULT_GROUP")
    public void onConfigChange(String config) {
        logger.info("配置变更: {}", config);
        // 处理配置变更
    }
}

服务网关

Gateway 配置

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-Request-Source, gateway
        
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=1

自定义过滤器

java
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
    
    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = extractToken(exchange.getRequest());
        
        if (token == null || !tokenProvider.validateToken(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        
        // 添加用户信息到请求头
        String username = tokenProvider.getUsernameFromToken(token);
        ServerHttpRequest request = exchange.getRequest().mutate()
            .header("X-User-Name", username)
            .build();
        
        return chain.filter(exchange.mutate().request(request).build());
    }
    
    @Override
    public int getOrder() {
        return -100;
    }
    
    private String extractToken(ServerHttpRequest request) {
        String header = request.getHeaders().getFirst("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            return header.substring(7);
        }
        return null;
    }
}

限流配置

java
@Configuration
public class RateLimiterConfig {
    
    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getHeaders().getFirst("X-User-Name")
        );
    }
    
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
        );
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                key-resolver: "#{@userKeyResolver}"

熔断降级

Sentinel 配置

yaml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
      eager: true

熔断规则

java
@PostConstruct
public void initFlowRules() {
    List<FlowRule> rules = new ArrayList<>();
    
    FlowRule rule = new FlowRule();
    rule.setResource("getUserById");
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule.setCount(100);
    rules.add(rule);
    
    FlowRuleManager.loadRules(rules);
}

降级处理

java
@Service
public class UserService {
    
    @SentinelResource(
        value = "getUserById",
        blockHandler = "getUserByIdBlockHandler",
        fallback = "getUserByIdFallback"
    )
    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
    
    // 限流处理
    public User getUserByIdBlockHandler(Long id, BlockException ex) {
        logger.warn("用户查询被限流: {}", id);
        return new User(id, "限流用户", "blocked@example.com");
    }
    
    // 异常降级
    public User getUserByIdFallback(Long id, Throwable ex) {
        logger.error("用户查询异常: {}", id, ex);
        return new User(id, "降级用户", "fallback@example.com");
    }
}

Resilience4j

java
@Service
public class UserService {
    
    @CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
    @RateLimiter(name = "userService")
    @Retry(name = "userService")
    public User getUser(Long id) {
        return userClient.getUser(id);
    }
    
    public User getUserFallback(Long id, Exception ex) {
        return new User(id, "降级用户", "fallback@example.com");
    }
}
yaml
resilience4j:
  circuitbreaker:
    instances:
      userService:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 10000
  ratelimiter:
    instances:
      userService:
        limitForPeriod: 100
        limitRefreshPeriod: 1s
  retry:
    instances:
      userService:
        maxAttempts: 3
        waitDuration: 1000

分布式事务

Seata 配置

yaml
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: my_tx_group
  service:
    vgroup-mapping:
      my_tx_group: default
    grouplist:
      default: localhost:8091

全局事务

java
@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private UserClient userClient;
    
    @Autowired
    private InventoryClient inventoryClient;
    
    @GlobalTransactional(name = "createOrder", rollbackFor = Exception.class)
    public Order createOrder(OrderRequest request) {
        // 1. 扣减库存
        inventoryClient.deduct(request.getProductId(), request.getQuantity());
        
        // 2. 扣减余额
        userClient.deductBalance(request.getUserId(), request.getTotalAmount());
        
        // 3. 创建订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setTotalAmount(request.getTotalAmount());
        order.setStatus(OrderStatus.CREATED);
        
        return orderRepository.save(order);
    }
}

链路追踪

Sleuth + Zipkin

yaml
spring:
  sleuth:
    sampler:
      probability: 1.0
  zipkin:
    base-url: http://localhost:9411

自定义 Span

java
@Service
public class OrderService {
    
    @Autowired
    private Tracer tracer;
    
    public Order createOrder(Order order) {
        Span span = tracer.nextSpan().name("createOrder").start();
        try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
            span.tag("orderId", order.getId().toString());
            span.tag("userId", order.getUserId().toString());
            
            // 业务逻辑
            return orderRepository.save(order);
        } finally {
            span.end();
        }
    }
}

LCGYL 事件集成

分布式事件

java
@Component
public class DistributedEventPublisher {
    
    @Autowired
    private EventBus eventBus;
    
    @Autowired
    private StreamBridge streamBridge;
    
    public void publishEvent(Object event) {
        // 本地事件
        eventBus.publish(event);
        
        // 分布式事件
        streamBridge.send("events-out-0", event);
    }
}

@Component
public class DistributedEventListener {
    
    @Bean
    public Consumer<OrderCreatedEvent> orderCreatedConsumer() {
        return event -> {
            logger.info("收到分布式事件: {}", event);
            // 处理事件
        };
    }
}

最佳实践

1. 服务拆分

✅ 推荐:按业务领域拆分
- user-service
- order-service
- product-service
- payment-service

❌ 不推荐:按技术层拆分
- controller-service
- service-service
- dao-service

2. 服务通信

java
// ✅ 推荐:使用 Feign 声明式调用
@FeignClient(name = "user-service")
public interface UserClient {
    @GetMapping("/api/users/{id}")
    User getUser(@PathVariable Long id);
}

// ❌ 不推荐:硬编码 URL
restTemplate.getForObject("http://localhost:8081/api/users/1", User.class);

3. 容错处理

java
// ✅ 推荐:提供降级方案
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
}

// ❌ 不推荐:无降级处理
@FeignClient(name = "user-service")
public interface UserClient {
}

常见问题

Q: 如何处理服务间认证?

A: 使用 JWT 或 OAuth2,在 Feign 拦截器中传递 Token。

java
@Component
public class FeignAuthInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        String token = SecurityContextHolder.getContext()
            .getAuthentication().getCredentials().toString();
        template.header("Authorization", "Bearer " + token);
    }
}

Q: 如何处理分布式事务?

A:

  1. 使用 Seata 全局事务
  2. 使用 Saga 模式
  3. 使用消息最终一致性

Q: 如何监控微服务?

A:

  1. Spring Boot Actuator
  2. Prometheus + Grafana
  3. Sleuth + Zipkin

下一步

Released under the Apache License 2.0