我们将以一个简单的电商系统为例,实现微服务架构,逐步用Java代码详细实现每个模块,并配合注释帮助小白理解。在这个实现中,我们使用以下工具和框架:
- Spring Boot:用于构建微服务。
- Spring Cloud:用于服务注册、发现、负载均衡等。
- Docker:用于容器化服务。
- Kubernetes (K8s):用于部署到生产环境。
- MySQL/MongoDB/PostgreSQL:不同服务独立数据库。
- RabbitMQ:用于消息队列。
- Prometheus + Grafana:用于监控。
以下是完整的实现步骤。
一、用户服务 (User Service)的实现
1. 环境搭建
1.1 必备工具安装
确保安装以下工具:
- Java 17+
- Maven
- Docker
- Kubernetes (minikube 或云上的K8s服务)
- RabbitMQ
- 数据库(MySQL/PostgreSQL/MongoDB)
- IDE(IntelliJ IDEA 或 VS Code)
2. 功能:
管理用户信息(用户注册、查询)。
2.1 数据库准备
我们使用 PostgreSQL 来存储用户数据。
- 创建数据库和表:
sql
CREATE DATABASE user_service;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
2.2 创建 Spring Boot 项目
生成项目 :Spring Initializr
依赖:
- Spring Web
- Spring Data JPA
- PostgreSQL Driver
2.3 实现用户服务
application.yml
配置文件
配置数据库连接:
bash
server:
port: 8081 # 服务运行的端口号
spring:
datasource:
url: jdbc:postgresql://localhost:5432/user_service
username: postgres # 数据库用户名
password: password # 数据库密码
jpa:
hibernate:
ddl-auto: update # 自动更新数据库结构
show-sql: true # 显示 SQL 查询日志
User
实体类
java
package com.example.userservice.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
/**
* 用户实体类,对应数据库表 users。
*/
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 用户唯一 ID
@Column(nullable = false, unique = true)
private String username; // 用户名(唯一)
@Column(nullable = false, unique = true)
private String email; // 用户邮箱(唯一)
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt = LocalDateTime.now(); // 用户注册时间
// Getters 和 Setters,用于封装字段访问
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
}
UserRepository
接口
java
package com.example.userservice.repository;
import com.example.userservice.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* 用户数据访问接口,提供 CRUD 操作。
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username); // 根据用户名查询用户
}
UserService
服务层
java
package com.example.userservice.service;
import com.example.userservice.model.User;
import com.example.userservice.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.Optional;
/**
* 用户服务类,包含业务逻辑。
*/
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
/**
* 注册新用户。
*
* @param user 包含用户名和邮箱的用户对象
* @return 保存后的用户对象,包含生成的 ID 和注册时间
*/
public User registerUser(User user) {
return userRepository.save(user); // 保存到数据库
}
/**
* 根据用户名查找用户。
*
* @param username 要查询的用户名
* @return 包含用户对象的 Optional,如果未找到则为空
*/
public Optional<User> findUserByUsername(String username) {
return Optional.ofNullable(userRepository.findByUsername(username));
}
}
UserController
控制器
java
package com.example.userservice.controller;
import com.example.userservice.model.User;
import com.example.userservice.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
/**
* 用户服务的 REST 控制器。
*/
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 注册新用户的 API。
*
* @param user 从请求体接收的用户对象
* @return 返回注册成功的用户
*/
@PostMapping
public ResponseEntity<User> registerUser(@RequestBody User user) {
return ResponseEntity.ok(userService.registerUser(user));
}
/**
* 根据用户名查找用户的 API。
*
* @param username 请求路径中的用户名
* @return 返回找到的用户或 404
*/
@GetMapping("/{username}")
public ResponseEntity<User> findUserByUsername(@PathVariable String username) {
Optional<User> user = userService.findUserByUsername(username);
return user.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
}
2.4 测试用户服务
启动服务后:
-
注册用户:
bashcurl -X POST -H "Content-Type: application/json" \ -d '{"username": "haha_jam", "email": "haha@example.com"}' \ http://localhost:8081/api/users
响应:
bash{ "id": 1, "username": "haha_jam", "email": "haha@example.com", "createdAt": "2024-12-20T10:00:00" }
-
查询用户:
bashcurl http://localhost:8081/api/users/haha_jam
响应:
bash{ "id": 1, "username": "haha_jam", "email": "haha@example.com", "createdAt": "2024-12-20T10:00:00" }
3. 服务注册与发现
在完整微服务中,我们使用 Spring Cloud Eureka 来实现服务注册和发现。
3.1 什么是服务注册与发现?
在微服务架构中,服务是分布式的,每个服务都可能运行在不同的主机或容器中。为了让服务之间能够互相通信,需要一种机制来找到其他服务的地址和端口。
服务注册与发现原理:
- Eureka Server:服务注册中心,用来维护所有服务的地址列表。每个服务(如用户服务、订单服务)都会向 Eureka Server 注册自己。
- Eureka Client:微服务实例本身会向 Eureka Server 注册,并定期发送心跳来告诉注册中心它是健康的。
- 服务发现:当一个服务需要调用另一个服务时,它会向 Eureka Server 查询目标服务的地址列表。
配置步骤包括:
- 创建 Eureka Server。
- 将用户服务注册到 Eureka。
- 添加 API 网关。
3.2 创建 Eureka Server
Eureka Server 是服务注册中心,所有服务都会向它注册。
3.2.1 生成 Eureka Server 项目
-
配置项目:
- Project: Maven
- Language: Java
- Spring Boot: 3.0.0+
- Dependencies :
- Spring Cloud Eureka Server
-
下载生成的项目并解压。
3.2.2 配置 Eureka Server
1. 添加依赖
在 pom.xml
中确认以下依赖已经存在:
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2. 配置 application.yml
在 src/main/resources/application.yml
中配置 Eureka Server:
bash
server:
port: 8761 # Eureka Server 的默认端口
spring:
application:
name: eureka-server # 应用名称
eureka:
client:
register-with-eureka: false # Eureka Server 本身不需要向其他注册中心注册
fetch-registry: false # Eureka Server 不需要获取注册信息
server:
enable-self-preservation: false # 关闭自我保护模式(测试环境可以关闭,生产环境建议打开)
3. 启动类
在 src/main/java/com/example/eurekaserver/EurekaServerApplication.java
文件中添加 @EnableEurekaServer
注解。
java
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* Eureka Server Application - 服务注册中心
*/
@SpringBootApplication
@EnableEurekaServer // 启用 Eureka Server 功能
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3.2.3 启动 Eureka Server
- 使用 IDE 或命令行运行
EurekaServerApplication
。 - 打开浏览器访问:http://localhost:8761
- 你会看到 Eureka Server 的管理页面,显示 "No instances available"。
3.3 将用户服务注册到 Eureka
现在我们需要将前面实现的 用户服务(User Service) 注册到 Eureka Server。
3.3.1 添加依赖
在用户服务的 pom.xml
中添加 Eureka Client 依赖:
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.3.2 配置 application.yml
在 src/main/resources/application.yml
中添加 Eureka Client 配置:
bash
server:
port: 8081 # 用户服务运行的端口
spring:
application:
name: user-service # 服务名称,Eureka 注册时的标识
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # Eureka Server 的地址
3.3.3 修改启动类
在用户服务的启动类中添加 @EnableEurekaClient
注解:
java
package com.example.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* User Service Application - 用户服务
*/
@SpringBootApplication
@EnableEurekaClient // 启用 Eureka Client 功能
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
3.3.4 验证注册
- 启动 Eureka Server。
- 启动 用户服务。
- 在浏览器访问:http://localhost:8761
- 你会看到
user-service
出现在 "Instances currently registered with Eureka" 列表中。
- 你会看到
3.4 添加 API 网关
API 网关用于转发客户端请求到具体的微服务,并支持负载均衡。
3.4.1 创建 API 网关项目
- 打开 Spring Initializr。
- 配置项目:
- Project: Maven
- Language: Java
- Spring Boot: 3.0.0+
- Dependencies :
- Spring Cloud Gateway
- Spring Cloud Eureka Discovery
3.4.2 配置 API 网关
1. 添加依赖
在 pom.xml
中确认以下依赖:
XML
<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>
2. 配置 application.yml
在 src/main/resources/application.yml
中配置网关:
bash
server:
port: 8080 # API 网关运行的端口
spring:
application:
name: api-gateway # 应用名称
cloud:
gateway:
routes:
- id: user-service # 路由 ID
uri: lb://user-service # 用户服务(通过 Eureka 注册的服务名)
predicates:
- Path=/api/users/** # 匹配以 /api/users 开头的请求
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # Eureka Server 地址
3. 启动类
在 src/main/java/com/example/apigateway/ApiGatewayApplication.java
文件中添加 @EnableEurekaClient
注解。
java
package com.example.apigateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* API Gateway Application - 网关服务
*/
@SpringBootApplication
@EnableEurekaClient // 启用 Eureka Client 功能
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
3.4.3 验证 API 网关
-
启动 Eureka Server。
-
启动 用户服务。
-
启动 API 网关。
-
测试 API:
bashcurl http://localhost:8080/api/users/john_doe
- API 网关会将请求转发到用户服务,返回用户数据。
4. 部署到K8S
4.1 容器化服务 (Docker)
在项目根目录下创建 Dockerfile
:
bash
FROM openjdk:17-jdk-slim
COPY target/user-service-0.0.1-SNAPSHOT.jar user-service.jar
ENTRYPOINT ["java", "-jar", "/user-service.jar"]
4.2 构建Docker镜像:
-
打包项目:
bashmvn clean package -DskipTests
-
构建镜像:
bashdocker build -t user-service:1.0 .
-
运行容器:
bashdocker run -d -p 8081:8081 user-service:1.0
4.3 部署到 Kubernetes
创建 user-service-deployment.yml
:
bash
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: user-service:1.0
ports:
- containerPort: 8081
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- protocol: TCP
port: 80
targetPort: 8081
type: LoadBalancer
4.4 部署到 K8s 集群
-
应用配置:
kubectl apply -f user-service-deployment.yml
-
查看服务:
kubectl get services
二、产品服务 (Product Service) 和订单服务 (Order Service)的实现
每个服务有自己的数据库,提供REST API,并实现服务注册和发现,确保服务之间的解耦和可扩展性。
1. 产品服务 (Product Service)
功能:管理商品信息,包括商品的添加、修改和查询。
1.1 数据库准备
我们使用 MongoDB 来存储商品数据。
- 创建数据库和集合:
sql
use product_service;
db.createCollection("products");
1.2 创建 Spring Boot 项目
生成项目 :Spring Initializr
依赖:
- Spring Web
- Spring Data MongoDB
1.3 实现产品服务
application.yml
配置文件
配置MongoDB连接:
server:
port: 8082
spring:
data:
mongodb:
uri: mongodb://localhost:27017/product_service
Product
实体类
java
package com.example.productservice.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* 在MongoDB中表示产品文档的产品实体类。
*/
@Document(collection = "products")
public class Product {
@Id
private String id; // 商品ID,由MongoDB生成
private String name; // 商品名称
private String description; // 商品描述
private double price; // 商品价格
private int stock; // 商品库存
// Getters and Setters
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
}
ProductRepository
接口
java
package com.example.productservice.repository;
import com.example.productservice.model.Product;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
/**
* 用于在Product实体上执行CRUD操作的存储库接口。
*/
@Repository
public interface ProductRepository extends MongoRepository<Product, String> {
Product findByName(String name); // 根据商品名称查询商品
}
ProductService
服务层
java
package com.example.productservice.service;
import com.example.productservice.model.Product;
import com.example.productservice.repository.ProductRepository;
import org.springframework.stereotype.Service;
import java.util.Optional;
/**
* 管理产品的服务类。包含产品相关操作的业务逻辑。
*/
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
/**
* 添加新产品。
*
* @param product 包含产品详细信息的产品对象。
* @return Saved 带生成ID的产品对象
*/
public Product addProduct(Product product) {
return productRepository.save(product);
}
/**
* 按名称查找产品。
*
* @param name 产品名称。
* @return 含有找到的产品,如果没有找到则为空。
*/
public Optional<Product> findProductByName(String name) {
return Optional.ofNullable(productRepository.findByName(name));
}
/**
* 按指定数量减少库存。
*
* @param productId 产品ID。
* @param quantity 数量减少。
* @return 更新了Product对象。
*/
public Product decreaseStock(String productId, int quantity) {
Product product = productRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found"));
if (product.getStock() >= quantity) {
product.setStock(product.getStock() - quantity);
return productRepository.save(product);
} else {
throw new RuntimeException("Insufficient stock");
}
}
}
ProductController
控制器
java
package com.example.productservice.controller;
import com.example.productservice.model.Product;
import com.example.productservice.service.ProductService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
* 与产品相关的端点的REST控制器。
*/
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
/**
* 添加新产品的端点。
*
* @param product 请求体中的产品对象。
* @return 包含已保存产品的responseentity。
*/
@PostMapping
public ResponseEntity<Product> addProduct(@RequestBody Product product) {
return ResponseEntity.ok(productService.addProduct(product));
}
/**
* 根据名称查找产品的端点。
*
* @param name 产品名称作为路径变量传递。
* @return 包含产品,如果找到。
*/
@GetMapping("/{name}")
public ResponseEntity<Product> findProductByName(@PathVariable String name) {
return productService.findProductByName(name)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 减少产品库存的终点。
*
* @param productId 产品ID。
* @param quantity 数量减少。
* @return 包含更新后的产品。
*/
@PutMapping("/{productId}/decrease-stock/{quantity}")
public ResponseEntity<Product> decreaseStock(@PathVariable String productId, @PathVariable int quantity) {
return ResponseEntity.ok(productService.decreaseStock(productId, quantity));
}
}
1.4 测试产品服务
启动服务后:
-
添加商品:
bashcurl -X POST -H "Content-Type: application/json" \ -d '{"name": "Laptop", "description": "High-performance laptop", "price": 1000, "stock": 50}' \ http://localhost:8082/api/products
响应:
bash{ "id": "605c72ef1532072a20b0f119", "name": "Laptop", "description": "High-performance laptop", "price": 1000, "stock": 50 }
-
查询商品:
bashcurl http://localhost:8082/api/products/Laptop
响应:
bash{ "id": "605c72ef1532072a20b0f119", "name": "Laptop", "description": "High-performance laptop", "price": 1000, "stock": 50 }
2. 订单服务 (Order Service)
功能:管理用户的订单,包括创建订单、查询订单、更新订单状态等。
2.1 数据库准备
我们使用 MySQL 来存储订单数据。
- 创建数据库和表:
sql
CREATE DATABASE order_service;
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
product_id VARCHAR(255) NOT NULL,
quantity INT NOT NULL,
total_price DOUBLE NOT NULL,
order_status VARCHAR(255) DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
2.2 创建 Spring Boot 项目
生成项目 :Spring Initializr
依赖:
- Spring Web
- Spring Data JPA
- MySQL Driver
2.3 实现订单服务
application.yml
配置文件
配置MySQL连接:
bash
server:
port: 8083
spring:
datasource:
url: jdbc:mysql://localhost:3306/order_service
username: root
password: root
jpa:
hibernate:
ddl-auto: update
show-sql: true
Order
实体类
java
package com.example.orderservice.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
/**
* 表示数据库中的订单表的订单实体类。
*/
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long userId; // 用户ID
@Column(nullable = false)
private String productId; // 商品ID
@Column(nullable = false)
private int quantity; // 购买数量
@Column(nullable = false)
private double totalPrice; // 总金额
@Column(name = "order_status", nullable = false)
private String orderStatus = "PENDING"; // 订单状态
@Column(name = "created_at", updatable = false
)
private LocalDateTime createdAt = LocalDateTime.now(); // 订单创建时间
// Getters and Setters
}
OrderRepository
接口
java
package com.example.orderservice.repository;
import com.example.orderservice.model.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* 用于在Order实体上执行CRUD操作的存储库接口。
*/
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
Order findByUserIdAndOrderStatus(Long userId, String orderStatus); // 根据用户ID和订单状态查询订单
}
OrderService
服务层
java
package com.example.orderservice.service;
import com.example.orderservice.model.Order;
import com.example.orderservice.repository.OrderRepository;
import org.springframework.stereotype.Service;
import java.util.Optional;
/**
* 用于管理订单的服务类。包含订单相关操作的业务逻辑。
*/
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
/**
* 创建一个新订单。
*
* @param order 包含订单详细信息的订单对象。
* @return 保存订单对象。
*/
public Order createOrder(Order order) {
return orderRepository.save(order);
}
/**
* 通过用户ID和状态查找订单。
*
* @param userId 用户ID。
* @param orderStatus 订单状态(例如,'PENDING')。
* @return 可选,包含找到的订单,如果没有找到则为空。
*/
public Optional<Order> findOrderByUserIdAndStatus(Long userId, String orderStatus) {
return Optional.ofNullable(orderRepository.findByUserIdAndOrderStatus(userId, orderStatus));
}
}
OrderController
控制器
java
package com.example.orderservice.controller;
import com.example.orderservice.model.Order;
import com.example.orderservice.service.OrderService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
* 与订单相关的端点的REST控制器。
*/
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
/**
* 创建新订单的端点。
*
* @param order 从请求体中订购对象。
* @return ResponseEntity包含保存的订单。
*/
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
return ResponseEntity.ok(orderService.createOrder(order));
}
/**
* 根据用户ID和状态查找订单的端点。
*
* @param userId 用户ID。
* @param orderStatus 订单状态作为路径变量传递。
* @return ResponseEntity包含找到的订单,如果存在。
*/
@GetMapping("/{userId}/{orderStatus}")
public ResponseEntity<Order> findOrderByUserIdAndStatus(@PathVariable Long userId, @PathVariable String orderStatus) {
return orderService.findOrderByUserIdAndStatus(userId, orderStatus)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
总结
在这个扩展中,我们设计了三个服务:用户服务,产品服务 和订单服务。每个服务具有自己的数据库,提供REST API,能够完成各自的功能,如商品管理、订单创建与查询。服务之间通过HTTP协议相互通信,服务之间通过微服务架构进行解耦,确保系统可扩展和维护。