Spring Cloud微服务代码示例仓库

Spring Cloud微服务代码示例仓库

Java微服务实战:Spring Cloud全家桶构建企业级分布式系统Spring Cloud微服务架构图解文章中,我们详细介绍了Spring Cloud微服务架构的设计和实现。为了帮助读者更好地理解和实践这些技术,本文将提供一个完整的代码示例仓库,包含所有核心组件的实现代码。

代码仓库地址

GitHub仓库地址:https://github.com/xueyise/spring-cloud-microservice-example

仓库结构说明

spring-cloud-microservice-example/
├── doc/                          # 文档目录
│   ├── architecture.md           # 架构设计文档
│   ├── api-design.md             # API设计规范
│   └── deployment.md             # 部署指南
├── infrastructure/               # 基础设施配置
│   ├── docker-compose.yml        # Docker Compose配置
│   ├── k8s/                      # Kubernetes部署配置
│   │   ├── nacos.yaml
│   │   ├── mysql.yaml
│   │   ├── redis.yaml
│   │   └── services/
│   └── sql/                      # 数据库脚本
│       ├── schema.sql
│       └── data.sql
├── platform/                     # 平台服务
│   ├── eureka-server/            # Eureka注册中心
│   ├── gateway-service/          # API网关服务
│   └── monitor-service/          # 监控服务
├── services/                     # 业务服务
│   ├── auth-service/             # 认证授权服务
│   │   ├── src/main/java/com/example/auth/
│   │   │   ├── AuthApplication.java
│   │   │   ├── config/           # 配置类
│   │   │   ├── controller/       # 控制器
│   │   │   ├── service/          # 业务服务
│   │   │   ├── repository/       # 数据访问层
│   │   │   ├── entity/           # 实体类
│   │   │   ├── dto/              # 数据传输对象
│   │   │   ├── exception/        # 异常处理
│   │   │   └── util/             # 工具类
│   │   └── pom.xml
│   ├── user-service/             # 用户服务
│   ├── order-service/            # 订单服务
│   ├── product-service/          # 商品服务
│   └── payment-service/          # 支付服务
├── common/                       # 公共模块
│   ├── common-core/              # 核心公共模块
│   ├── common-web/               # Web公共模块
│   ├── common-security/          # 安全公共模块
│   └── common-swagger/           # Swagger公共模块
├── pom.xml                       # 父工程POM
├── README.md                     # 项目说明文档
└── LICENSE                       # 开源许可证

核心模块代码示例

1. 父工程POM配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>spring-cloud-microservice-example</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>platform/eureka-server</module>
        <module>platform/gateway-service</module>
        <module>services/auth-service</module>
        <module>services/user-service</module>
        <module>services/order-service</module>
        <module>services/product-service</module>
        <module>common</module>
    </modules>

    <properties>
        <java.version>11</java.version>
        <spring-boot.version>2.7.0</spring-boot.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Spring Cloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Spring Cloud Alibaba -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- 公共模块 -->
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>common-core</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>common-web</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>common-security</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. Nacos配置中心示例

# application.yml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848}
      config:
        server-addr: ${NACOS_CONFIG_ADDR:127.0.0.1:8848}
        file-extension: yaml
        group: DEFAULT_GROUP
        namespace: public
        timeout: 3000
  datasource:
    url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DB:microservice}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: ${MYSQL_USERNAME:root}
    password: ${MYSQL_PASSWORD:root}
    driver-class-name: com.mysql.cj.jdbc.Driver
  redis:
    host: ${REDIS_HOST:localhost}
    port: ${REDIS_PORT:6379}
    password: ${REDIS_PASSWORD:}
    database: ${REDIS_DATABASE:0}
    timeout: 2000ms

server:
  port: ${SERVER_PORT:8080}

logging:
  level:
    com.example: DEBUG
    org.springframework.web: INFO

3. API网关配置示例

// GatewayApplication.java
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}
# application.yml
server:
  port: 9000

spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
        - id: auth-service
          uri: lb://auth-service
          predicates:
            - Path=/api/auth/**
          filters:
            - StripPrefix=2
            
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=2
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                key-resolver: "#{@userKeyResolver}"
                
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=2
            
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/api/products/**
          filters:
            - StripPrefix=2
      
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
        - AddResponseHeader=X-Response-Default-Foo, Default-Bar

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    gateway:
      enabled: true

4. RBAC权限控制示例

// SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Autowired
    private JwtAccessDeniedHandler accessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(unauthorizedHandler)
                .accessDeniedHandler(accessDeniedHandler)
            .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
                .antMatchers("/auth/**").permitAll()
                .antMatchers("/actuator/**").permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationFilter(), 
            UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}
// JwtUtil.java
@Component
public class JwtUtil {

    @Value("${jwt.secret:mySecretKey}")
    private String secret;

    @Value("${jwt.expiration:86400}")
    private Long expiration;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }
}

5. 分布式事务示例

// OrderService.java
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private ProductServiceFeignClient productServiceFeignClient;

    @Autowired
    private AccountServiceFeignClient accountServiceFeignClient;

    @GlobalTransactional
    @Override
    public OrderDTO createOrder(CreateOrderRequest request) {
        log.info("开始创建订单,用户ID: {}, 商品ID: {}", 
                request.getUserId(), request.getProductId());

        // 1. 验证用户和商品
        Result<UserDTO> userResult = accountServiceFeignClient.getUserById(request.getUserId());
        if (!userResult.isSuccess() || userResult.getData() == null) {
            throw new BusinessException("用户不存在");
        }

        Result<ProductDTO> productResult = productServiceFeignClient.getProductById(request.getProductId());
        if (!productResult.isSuccess() || productResult.getData() == null) {
            throw new BusinessException("商品不存在");
        }

        ProductDTO product = productResult.getData();

        // 2. 检查库存
        if (product.getStock() < request.getQuantity()) {
            throw new BusinessException("库存不足");
        }

        // 3. 创建订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setAmount(product.getPrice().multiply(BigDecimal.valueOf(request.getQuantity())));
        order.setStatus(OrderStatus.CREATED);
        orderRepository.save(order);

        // 4. 调用商品服务扣减库存
        DecreaseStockRequest decreaseStockRequest = new DecreaseStockRequest();
        decreaseStockRequest.setProductId(request.getProductId());
        decreaseStockRequest.setQuantity(request.getQuantity());
        
        Result<Boolean> stockResult = productServiceFeignClient.decreaseStock(decreaseStockRequest);
        if (!stockResult.isSuccess() || !stockResult.getData()) {
            throw new BusinessException("扣减库存失败");
        }

        // 5. 调用账户服务扣减余额
        DecreaseBalanceRequest decreaseBalanceRequest = new DecreaseBalanceRequest();
        decreaseBalanceRequest.setUserId(request.getUserId());
        decreaseBalanceRequest.setAmount(order.getAmount());
        
        Result<Boolean> balanceResult = accountServiceFeignClient.decreaseBalance(decreaseBalanceRequest);
        if (!balanceResult.isSuccess() || !balanceResult.getData()) {
            throw new BusinessException("扣减余额失败");
        }

        log.info("订单创建成功,订单ID: {}", order.getId());
        return convertToDTO(order);
    }
}

部署说明

Docker部署

# 构建所有服务镜像
mvn clean package -DskipTests

# 启动基础设施
docker-compose -f infrastructure/docker-compose.yml up -d

# 启动各个服务
docker-compose -f services/auth-service/docker-compose.yml up -d
docker-compose -f services/user-service/docker-compose.yml up -d
docker-compose -f services/order-service/docker-compose.yml up -d
docker-compose -f services/product-service/docker-compose.yml up -d

Kubernetes部署

# 部署基础设施
kubectl apply -f infrastructure/k8s/nacos.yaml
kubectl apply -f infrastructure/k8s/mysql.yaml
kubectl apply -f infrastructure/k8s/redis.yaml

# 部署服务
kubectl apply -f infrastructure/k8s/services/auth-service.yaml
kubectl apply -f infrastructure/k8s/services/user-service.yaml
kubectl apply -f infrastructure/k8s/services/order-service.yaml
kubectl apply -f infrastructure/k8s/services/product-service.yaml

运行环境要求

  1. JDK: 11或更高版本
  2. Maven: 3.6.0或更高版本
  3. Docker: 20.10或更高版本(可选)
  4. Kubernetes: 1.20或更高版本(可选)
  5. MySQL: 8.0或更高版本
  6. Redis: 6.0或更高版本
  7. Nacos: 2.0或更高版本

使用说明

  1. 克隆代码仓库:

    git clone https://github.com/xueyise/spring-cloud-microservice-example.git
    
  2. 初始化数据库:

    mysql -u root -p < infrastructure/sql/schema.sql
    mysql -u root -p < infrastructure/sql/data.sql
    
  3. 启动Nacos服务:

    cd infrastructure/nacos
    sh startup.sh -m standalone
    
  4. 编译打包:

    mvn clean package -DskipTests
    
  5. 启动各个服务:

    java -jar platform/eureka-server/target/eureka-server-1.0.0.jar
    java -jar platform/gateway-service/target/gateway-service-1.0.0.jar
    java -jar services/auth-service/target/auth-service-1.0.0.jar
    java -jar services/user-service/target/user-service-1.0.0.jar
    

贡献指南

欢迎对本项目进行贡献!请遵循以下步骤:

  1. Fork本仓库
  2. 创建功能分支 (git checkout -b feature/AmazingFeature)
  3. 提交更改 (git commit -m 'Add some AmazingFeature')
  4. 推送到分支 (git push origin feature/AmazingFeature)
  5. 开启Pull Request

许可证

本项目采用MIT许可证,详情请参见LICENSE文件。


本文由xueyise创作,提供Spring Cloud微服务代码示例仓库