本文目录导读:

我为您精心设计一个完整的Spring Cloud微服务电商案例,包含服务注册、配置中心、网关、熔断等核心组件。
项目架构总览
springcloud-demo/
├── pom.xml (父POM)
├── eureka-server/ # 服务注册中心 (端口:8761)
├── config-server/ # 配置中心 (端口:8888)
├── api-gateway/ # API网关 (端口:8080)
├── user-service/ # 用户服务 (端口:8081)
├── order-service/ # 订单服务 (端口:8082)
├── product-service/ # 商品服务 (端口:8083)
└── common/ # 公共模块
代码实现
父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>springcloud-demo</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.5</spring-cloud.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<module>common</module>
<module>eureka-server</module>
<module>config-server</module>
<module>api-gateway</module>
<module>user-service</module>
<module>order-service</module>
<module>product-service</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 公共模块依赖 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
公共模块 (common)
// Result.java - 统一响应结果
package com.example.common;
import lombok.Data;
@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static <T> Result<T> error(Integer code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
}
// User.java - 用户实体
package com.example.common;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private Long id;
private String username;
private String email;
private String phone;
}
// Order.java - 订单实体
package com.example.common;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class Order implements Serializable {
private Long id;
private Long userId;
private Long productId;
private Integer quantity;
private BigDecimal totalAmount;
private String status;
private LocalDateTime createTime;
}
// Product.java - 商品实体
package com.example.common;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class Product implements Serializable {
private Long id;
private String name;
private String description;
private BigDecimal price;
private Integer stock;
}
Eureka服务注册中心
<?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>
<parent>
<groupId>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>eureka-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
# application.yml
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 5000
package com.example.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
配置中心 (Config Server)
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>config-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
</project>
# application.yml
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/your-config-repo # 配置仓库地址
search-paths: config-repo
default-label: main
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
package com.example.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
API网关 (Spring Cloud Gateway)
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>api-gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
</project>
# bootstrap.yml
spring:
application:
name: api-gateway
cloud:
config:
uri: http://localhost:8888
fail-fast: true
---
# application.yml
server:
port: 8080
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/product/**
filters:
- StripPrefix=1
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
management:
endpoints:
web:
exposure:
include: '*'
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
// 编程式路由配置 (可选)
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r
.path("/api/user/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://user-service"))
.build();
}
}
// JWT过滤器 (网关认证)
package com.example.gateway.filter;
import io.jsonwebtoken.Jwts;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class JwtAuthFilter implements GatewayFilter, Ordered {
private static final String SECRET_KEY = "your-secret-key";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 白名单路径放行
String path = request.getURI().getPath();
if (path.contains("/api/user/login") || path.contains("/api/user/register")) {
return chain.filter(exchange);
}
// 获取token
String token = request.getHeaders().getFirst("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
try {
// 验证token
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return chain.filter(exchange);
} catch (Exception e) {
// token无效
return unauthorized(exchange);
}
}
return unauthorized(exchange);
}
private Mono<Void> unauthorized(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
@Override
public int getOrder() {
return -100;
}
}
用户服务 (User Service)
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>user-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
</project>
# bootstrap.yml
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8888
fail-fast: true
---
# application.yml
server:
port: 8081
spring:
application:
name: user-service
datasource:
url: jdbc:h2:mem:userdb;DB_CLOSE_DELAY=-1
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
management:
endpoints:
web:
exposure:
include: '*'
package com.example.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
// UserRepository
package com.example.user.repository;
import com.example.user.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByUsername(String username);
}
// UserEntity
package com.example.user.entity;
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
@Table(name = "users")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String username;
private String password;
private String email;
private String phone;
}
// UserController
package com.example.user.controller;
import com.example.common.Result;
import com.example.user.entity.UserEntity;
import com.example.user.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping
public Result<List<UserEntity>> getAllUsers() {
return Result.success(userRepository.findAll());
}
@GetMapping("/{id}")
public Result<UserEntity> getUserById(@PathVariable Long id) {
Optional<UserEntity> user = userRepository.findById(id);
return user.map(Result::success)
.orElse(Result.error(404, "用户不存在"));
}
@PostMapping
public Result<UserEntity> createUser(@RequestBody UserEntity user) {
return Result.success(userRepository.save(user));
}
@PutMapping("/{id}")
public Result<UserEntity> updateUser(@PathVariable Long id, @RequestBody UserEntity user) {
user.setId(id);
return Result.success(userRepository.save(user));
}
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {
userRepository.deleteById(id);
return Result.success(null);
}
}
订单服务 (Order Service)
package com.example.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// Feign客户端接口
package com.example.order.client;
import com.example.common.Product;
import com.example.common.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "product-service", fallback = ProductClientFallback.class)
public interface ProductClient {
@GetMapping("/products/{id}")
Result<Product> getProductById(@PathVariable("id") Long id);
@GetMapping("/products/stock/{id}")
Result<Boolean> checkStock(@PathVariable("id") Long id, @RequestParam("quantity") Integer quantity);
}
// Feign容错实现
package com.example.order.client;
import com.example.common.Product;
import com.example.common.Result;
import org.springframework.stereotype.Component;
@Component
public class ProductClientFallback implements ProductClient {
@Override
public Result<Product> getProductById(Long id) {
return Result.error(500, "Product service is unavailable");
}
@Override
public Result<Boolean> checkStock(Long id, Integer quantity) {
return Result.error(500, "Stock check failed");
}
}
// OrderController
package com.example.order.controller;
import com.example.common.Order;
import com.example.common.Product;
import com.example.common.Result;
import com.example.order.client.ProductClient;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/orders")
public class OrderController {
private final ConcurrentHashMap<Long, Order> orders = new ConcurrentHashMap<>();
@Autowired
private ProductClient productClient;
@Autowired
private RestTemplate restTemplate;
@PostMapping
@CircuitBreaker(name = "createOrder", fallbackMethod = "createOrderFallback")
public Result<Order> createOrder(@RequestParam("userId") Long userId,
@RequestParam("productId") Long productId,
@RequestParam("quantity") Integer quantity) {
// 调用商品服务获取商品信息
Result<Product> productResult = productClient.getProductById(productId);
if (productResult.getCode() != 200) {
return Result.error(500, "商品服务调用失败");
}
Product product = productResult.getData();
// 创建订单
Order order = new Order();
order.setId(System.currentTimeMillis());
order.setUserId(userId);
order.setProductId(productId);
order.setQuantity(quantity);
order.setTotalAmount(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
order.setStatus("CREATED");
order.setCreateTime(LocalDateTime.now());
orders.put(order.getId(), order);
return Result.success(order);
}
public Result<Order> createOrderFallback(Long userId, Long productId, Integer quantity, Throwable throwable) {
// 降级处理
Order fallbackOrder = new Order();
fallbackOrder.setId(System.currentTimeMillis());
fallbackOrder.setUserId(userId);
fallbackOrder.setProductId(productId);
fallbackOrder.setQuantity(quantity);
fallbackOrder.setStatus("FALLBACK");
fallbackOrder.setCreateTime(LocalDateTime.now());
return Result.success(fallbackOrder);
}
@GetMapping("/{id}")
public Result<Order> getOrderById(@PathVariable Long id) {
Order order = orders.get(id);
if (order != null) {
return Result.success(order);
}
return Result.error(404, "订单不存在");
}
}
商品服务 (Product Service)
package com.example.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
// ProductController
package com.example.product.controller;
import com.example.common.Product;
import com.example.common.Result;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/products")
public class ProductController {
private final Map<Long, Product> products = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
// 初始化商品数据
Product p1 = new Product();
p1.setId(1L);
p1.setName("iPhone 15");
p1.setDescription("苹果最新款手机");
p1.setPrice(new BigDecimal("6999"));
p1.setStock(100);
products.put(p1.getId(), p1);
Product p2 = new Product();
p2.setId(2L);
p2.setName("MacBook Pro");
p2.setDescription("苹果笔记本");
p2.setPrice(new BigDecimal("14999"));
p2.setStock(50);
products.put(p2.getId(), p2);
}
@GetMapping
public Result<List<Product>> getAllProducts() {
return Result.success(products.values().stream().toList());
}
@GetMapping("/{id}")
public Result<Product> getProductById(@PathVariable Long id) {
Product product = products.get(id);
if (product != null) {
return Result.success(product);
}
return Result.error(404, "商品不存在");
}
@GetMapping("/stock/{id}")
public Result<Boolean> checkStock(@PathVariable Long id, @RequestParam("quantity") Integer quantity) {
Product product = products.get(id);
if (product != null && product.getStock() >= quantity) {
return Result.success(true);
}
return Result.success(false);
}
@PutMapping("/stock/{id}")
public Result<Product> updateStock(@PathVariable Long id, @RequestParam("quantity") Integer quantity) {
Product product = products.get(id);
if (product != null) {
product.setStock(product.getStock() - quantity);
return Result.success(product);
}
return Result.error(404, "商品不存在");
}
}
启动顺序和测试
启动顺序:
- Eureka Server (8761)
- Config Server (8888)
- User Service (8081)
- Product Service (8083)
- Order Service (8082)
- API Gateway (8080)
测试接口:
# 1. 通过网关访问用户服务
curl http://localhost:8080/api/user/users
# 2. 创建用户
curl -X POST http://localhost:8080/api/user/users \
-H "Content-Type: application/json" \
-d '{"username":"test", "password":"123456", "email":"test@example.com"}'
# 3. 查看商品(通过网关)
curl http://localhost:8080/api/product/products
# 4. 创建订单(通过网关)
curl -X POST "http://localhost:8080/api/order/orders?userId=1&productId=1&quantity=2"
# 5. 查看Eureka注册中心
open http://localhost:8761
核心功能演示
服务注册发现
- 所有服务自动注册到Eureka
- 通过服务名进行负载均衡调用
负载均衡
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
熔断降级
# application.yml
resilience4j:
circuitbreaker:
instances:
createOrder:
slidingWindowSize: 10
failureRateThreshold: 50
waitDurationInOpenState: 10000
分布式配置
# bootstrap.yml
spring:
cloud:
config:
name: user-service
profile: dev
label: main
uri: http://localhost:8888
这就是一个完整的Spring Cloud微服务电商案例,包含了微服务架构的核心组件:服务注册、配置中心、API网关、熔断降级、负载均衡等,您可以根据实际需求进行扩展和修改。