微服务实现高并发 秒杀系统,前后端实现

news2025/1/11 17:30:29

一、前端实现

前端项目初始化

首先,我们需要创建一个新的 Vue 3 项目。你可以使用 Vue CLI 来快速搭建项目。

安装 Vue CLI(如果尚未安装)

bash

npm install -g @vue/cli

 创建 Vue 项目

bash

vue create seckill-frontend
cd seckill-frontend

 

在创建过程中选择默认的 Vue 3 预设或其他你需要的配置。

安装 Axios

Axios 是一个常用的 HTTP 客户端,用于发送请求到后端 API。

bash

npm install axios

 

实现前端功能

1. 商品列表页面

创建一个 ProductList.vue 组件来显示商品列表,并提供秒杀按钮。

html

<template>
  <div class="product-list">
    <h1>Available Products</h1>
    <ul>
      <li v-for="product in products" :key="product.id" class="product-item">
        <h2>{{ product.name }}</h2>
        <p>Price: ${{ product.price }}</p>
        <p>Stock: {{ product.stock }}</p>
        <button @click="seckill(product.id)" :disabled="product.stock <= 0 || isSeckilling">
          {{ isSeckilling ? 'Secilling...' : 'Seckill Now' }}
        </button>
      </li>
    </ul>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      products: [],
      isSeckilling: false,
    };
  },
  created() {
    this.fetchProducts();
  },
  methods: {
    async fetchProducts() {
      try {
        const response = await axios.get('http://localhost:8080/api/products?page=0&size=10');
        this.products = response.data;
      } catch (error) {
        console.error('Error fetching products:', error);
      }
    },
    async seckill(productId) {
      if (this.isSeckilling) return;

      this.isSeckilling = true;
      try {
        const response = await axios.post(`http://localhost:8080/api/products/seckill/${productId}/1/1`);
        alert(response.data);
      } catch (error) {
        console.error('Seckill failed:', error);
        alert('Seckill failed! Please try again.');
      } finally {
        this.isSeckilling = false;
        this.fetchProducts(); // Refresh product list after attempting seckill
      }
    },
  },
};
</script>

<style scoped>
.product-list {
  padding: 20px;
}

.product-item {
  border-bottom: 1px solid #ccc;
  padding: 10px 0;
}

.product-item button {
  margin-top: 10px;
  padding: 5px 10px;
  cursor: pointer;
}
</style>

 

2. 主页路由配置

修改 src/router/index.js 文件以包含 ProductList 组件。

js

import { createRouter, createWebHistory } from 'vue-router';
import ProductList from '../components/ProductList.vue';

const routes = [
  {
    path: '/',
    name: 'ProductList',
    component: ProductList,
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;
3. 修改 App.vue

确保 App.vue 正确引入并使用路由。

html

<template>
  <router-view></router-view>
</template>

<script>
export default {
  name: 'App',
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

运行前端应用

确保你的后端服务正在运行,然后启动前端应用:

bash

npm run serve

打开浏览器访问 http://localhost:8081(假设前端服务运行在 8081 端口),你应该能看到商品列表和秒杀按钮。

总结

通过以上步骤,我们成功创建了一个简单的前端应用使用 Vue 3.0 来与后端秒杀系统进行交互。以下是整个前端项目的概览图:

二、后端实现

实现一个高并发秒杀系统是一个复杂的任务,需要仔细设计和优化以确保系统的稳定性和性能。以下是一个详细的步骤指南,帮助你构建一个基于微服务架构的高并发秒杀系统。

1. 技术选型

选择合适的技术栈对于提高系统的性能和可维护性至关重要。以下是一些推荐的技术:

  • 框架: Spring Boot
  • 数据库: MySQL 或 PostgreSQL
  • 缓存: Redis
  • 消息队列: RabbitMQ 或 Kafka(如果需要异步处理)
  • 负载均衡: Nginx
  • 容器化: Docker
  • 部署: Kubernetes
  • 分布式锁: Redisson 或 Zookeeper

2. 架构设计

2.1 微服务架构

将应用分解为多个微服务,每个微服务负责特定的功能。例如:

  • User Service: 用户管理
  • Product Service: 商品管理
  • Order Service: 订单管理
  • Seckill Service: 秒杀活动管理
  • Inventory Service: 库存管理
  • Gateway Service: API Gateway
2.2 API Gateway

使用 Spring Cloud Gateway 或 Netflix Zuul 作为 API Gateway,负责路由请求到相应的微服务,并提供统一的身份验证和日志记录。

2.3 数据库设计

设计合理的数据库模式,确保查询高效。可以使用关系型数据库(如 MySQL)或 NoSQL 数据库(如 MongoDB),具体取决于需求。

2.4 缓存机制

使用 Redis 进行缓存,减少对数据库的直接访问,提高响应速度。

2.5 异步处理

使用消息队列(如 RabbitMQ 或 Kafka)进行异步处理,提高系统的吞吐量和响应速度。

2.6 分布式锁

使用 Redisson 或 Zookeeper 实现分布式锁,确保库存扣减的原子性。

3. 实施步骤

3.1 创建 Spring Boot 项目

使用 Spring Initializr 创建一个新的 Spring Boot 项目。

https://start.spring.io/

选择以下依赖:

  • Spring Web
  • Spring Data JPA
  • MySQL Driver
  • Lombok
  • Spring Security (如果需要身份验证)
  • Spring Cloud Gateway
  • Spring Cloud Eureka (服务注册与发现)
  • Spring AMQP (RabbitMQ)
  • Redis
  • Resilience4j (容错)
3.2 配置数据库连接

application.properties 中配置数据库连接:

spring.datasource.url=jdbc:mysql://localhost:3306/seckill_system?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

3.3 创建实体类

创建实体类来表示数据库中的表。例如,创建 ProductOrder 实体类:

java

package com.seckillsystem.model;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
@Data
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Double price;
    private Integer stock;
}



package com.seckillsystem.model;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
@Data
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long productId;
    private Long userId;
    private Integer quantity;
}



3.4 创建 Repository

创建 Repository 接口来操作数据库。例如,创建 ProductRepositoryOrderRepository

java

package com.seckillsystem.repository;

import com.seckillsystem.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Long> {
}



package com.seckillsystem.repository;

import com.seckillsystem.model.Order;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, Long> {
}



3.5 创建 Service 层

创建 Service 层来处理业务逻辑。例如,创建 ProductServiceOrderService

java

package com.seckillsystem.service;

import com.seckillsystem.model.Product;
import com.seckillsystem.repository.ProductRepository;
import org.redisson.api.RedissonClient;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private RedissonClient redissonClient;

    public Optional<Product> getProductById(Long id) {
        return productRepository.findById(id);
    }

    public boolean reduceStock(Long productId) {
        RLock lock = redissonClient.getLock("product:" + productId + ":lock");
        try {
            if (lock.tryLock()) {
                Product product = productRepository.findById(productId).orElse(null);
                if (product != null && product.getStock() > 0) {
                    product.setStock(product.getStock() - 1);
                    productRepository.save(product);
                    return true;
                }
            }
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return false;
    }
}



package com.seckillsystem.service;

import com.seckillsystem.model.Order;
import com.seckillsystem.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    public Order createOrder(Order order) {
        return orderRepository.save(order);
    }
}



3.6 创建 Controller 层

创建 Controller 层来处理 HTTP 请求。例如,创建 ProductControllerOrderController

java

package com.seckillsystem.controller;

import com.seckillsystem.model.Product;
import com.seckillsystem.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping
    public List<Product> getAllProducts() {
        return productService.getAllProducts();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Product> getProductById(@PathVariable Long id) {
        return productService.getProductById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping("/seckill/{productId}/{userId}/{quantity}")
    public ResponseEntity<String> seckill(@PathVariable Long productId, @PathVariable Long userId, @PathVariable Integer quantity) {
        if (productService.reduceStock(productId)) {
            Order order = new Order();
            order.setProductId(productId);
            order.setUserId(userId);
            order.setQuantity(quantity);
            productService.createOrder(order);
            return ResponseEntity.ok("Seckill successful!");
        } else {
            return ResponseEntity.badRequest().body("Seckill failed! Out of stock.");
        }
    }
}



package com.seckillsystem.controller;

import com.seckillsystem.model.Order;
import com.seckillsystem.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping
    public Order createOrder(@RequestBody Order order) {
        return orderService.createOrder(order);
    }
}



3.7 配置 Redis 缓存

pom.xml 中添加 Redis 依赖:

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.16.8</version>
</dependency>



application.properties 中配置 Redis 连接:

spring.redis.host=localhost
spring.redis.port=6379

# Redisson configuration
spring.redisson.config=classpath:redisson-config.yml



创建 redisson-config.yml 文件:

singleServerConfig:
  address: "redis://127.0.0.1:6379"
threads: 0
nettyThreads: 0
codec:
  class: "org.redisson.codec.JsonJacksonCodec"
transportMode: "NIO"



3.8 使用 Swagger 文档

添加 Swagger 依赖以生成 API 文档:

pom.xml 中添加 Swagger 依赖:

xml

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>



创建 Swagger 配置类:

java

package com.seckillsystem.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.seckillsystem.controller"))
                .paths(PathSelectors.any())
                .build();
    }
}



3.9 容器化和部署

创建 Dockerfile 来容器化应用:

为每个微服务创建一个 Dockerfile。这里以 Product Service 为例:

Dockerfile

# Use an official OpenJDK runtime as a parent image
FROM openjdk:17-jdk-slim

# Set the working directory in the container
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY target/product-service.jar /app/product-service.jar

# Make port 8080 available to the world outside this container
EXPOSE 8080

# Run the application
CMD ["java", "-jar", "product-service.jar"]

假设其他微服务也有类似的结构,你可以分别为它们创建相应的 Dockerfile

3.9.2 构建 Docker 镜像

在每个微服务目录中运行以下命令来构建 Docker 镜像:

bash

mvn clean package
docker build -t product-service .

重复上述步骤为其他微服务构建镜像。

3.9.3 使用 Docker Compose 进行本地开发

为了简化多容器应用的开发、测试和部署,可以使用 Docker Compose。

创建 docker-compose.yml 文件:

Yaml

version: '3.8'

services:
  product-service:
    image: product-service
    ports:
      - "8081:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/seckill_system?useSSL=false&serverTimezone=UTC
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: password
    depends_on:
      - db
      - redis

  order-service:
    image: order-service
    ports:
      - "8082:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/seckill_system?useSSL=false&serverTimezone=UTC
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: password
    depends_on:
      - db
      - redis
      - rabbitmq

  seckill-service:
    image: seckill-service
    ports:
      - "8083:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/seckill_system?useSSL=false&serverTimezone=UTC
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: password
    depends_on:
      - db
      - redis
      - rabbitmq

  gateway-service:
    image: gateway-service
    ports:
      - "8080:8080"
    depends_on:
      - product-service
      - order-service
      - seckill-service

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: seckill_system
    volumes:
      - db_data:/var/lib/mysql

  redis:
    image: redis:latest

  rabbitmq:
    image: rabbitmq:management

volumes:
  db_data:

启动所有服务:

bash

docker-compose up --build
3.9.4 使用 Kubernetes 进行生产部署

为了在生产环境中部署,我们可以使用 Kubernetes。首先,确保你已经安装了 Kubernetes 和 kubectl 工具。

3.9.4.1 创建 Kubernetes 部署文件

为每个微服务创建一个 Kubernetes 部署文件。这里以 Product Service 为例:

Yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: product-service
  template:
    metadata:
      labels:
        app: product-service
    spec:
      containers:
        - name: product-service
          image: product-service:latest
          ports:
            - containerPort: 8080
          env:
            - name: SPRING_DATASOURCE_URL
              value: jdbc:mysql://mysql:3306/seckill_system?useSSL=false&serverTimezone=UTC
            - name: SPRING_DATASOURCE_USERNAME
              value: root
            - name: SPRING_DATASOURCE_PASSWORD
              value: password
---
apiVersion: v1
kind: Service
metadata:
  name: product-service
spec:
  type: ClusterIP
  selector:
    app: product-service
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

为其他微服务创建类似的部署文件。

3.9.4.2 创建 ConfigMap 和 Secret

创建 ConfigMap 和 Secret 来管理配置和敏感信息。

Yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: database-config
data:
  spring.datasource.url: jdbc:mysql://mysql:3306/seckill_system?useSSL=false&serverTimezone=UTC
  spring.datasource.username: root

---
apiVersion: v1
kind: Secret
metadata:
  name: database-secret
type: Opaque
data:
  spring.datasource.password: cGFzc3dvcmQ= # base64 encoded 'password'
3.9.4.3 创建 MySQL 和 Redis 的 StatefulSet 和 Headless Service

Yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: "mysql"
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: database-secret
                  key: spring.datasource.password
            - name: MYSQL_DATABASE
              valueFrom:
                configMapKeyRef:
                  name: database-config
                  key: spring.datasource.database
          ports:
            - containerPort: 3306
          volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: mysql-persistent-storage
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 10Gi

---
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
    - port: 3306
  clusterIP: None
  selector:
    app: mysql

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: "redis"
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:latest
          ports:
            - containerPort: 6379
          volumeMounts:
            - name: redis-persistent-storage
              mountPath: /data
  volumeClaimTemplates:
    - metadata:
        name: redis-persistent-storage
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 5Gi

---
apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  ports:
    - port: 6379
  clusterIP: None
  selector:
    app: redis
3.9.4.4 创建 RabbitMQ 的 StatefulSet 和 Headless Service

Yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: rabbitmq
spec:
  serviceName: "rabbitmq"
  replicas: 1
  selector:
    matchLabels:
      app: rabbitmq
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
      containers:
        - name: rabbitmq
          image: rabbitmq:management
          ports:
            - containerPort: 5672
            - containerPort: 15672
          volumeMounts:
            - name: rabbitmq-persistent-storage
              mountPath: /var/lib/rabbitmq
  volumeClaimTemplates:
    - metadata:
        name: rabbitmq-persistent-storage
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 5Gi

---
apiVersion: v1
kind: Service
metadata:
  name: rabbitmq
spec:
  ports:
    - port: 5672
    - port: 15672
  clusterIP: None
  selector:
    app: rabbitmq
3.9.4.5 应用 Kubernetes 配置

将所有 YAML 文件保存到一个目录中,然后应用这些配置:

bash

kubectl apply -f .

4. 性能优化

4.1 数据库索引

确保数据库表上有适当的索引,以加速查询。例如,在 Product 表的 idstock 字段上创建索引。

sql

CREATE INDEX idx_product_id ON product(id);
CREATE INDEX idx_product_stock ON product(stock);
4.2 分页查询

对于大量数据的查询,使用分页查询来减少每次查询的数据量。在 ProductService 中添加分页查询方法:

java

package com.seckillsystem.service;

import com.seckillsystem.model.Product;
import com.seckillsystem.repository.ProductRepository;
import org.redisson.api.RedissonClient;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private RedissonClient redissonClient;

    public Optional<Product> getProductById(Long id) {
        return productRepository.findById(id);
    }

    public boolean reduceStock(Long productId) {
        RLock lock = redissonClient.getLock("product:" + productId + ":lock");
        try {
            if (lock.tryLock()) {
                Product product = productRepository.findById(productId).orElse(null);
                if (product != null && product.getStock() > 0) {
                    product.setStock(product.getStock() - 1);
                    productRepository.save(product);
                    return true;
                }
            }
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return false;
    }

    public Page<Product> getAllProducts(Pageable pageable) {
        return productRepository.findAll(pageable);
    }
}
4.3 响应式编程

使用 Spring WebFlux 进行响应式编程,提高非阻塞 I/O 的性能。首先,将项目转换为 Spring WebFlux 项目。

pom.xml 中添加 Spring WebFlux 依赖:

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

修改 ProductController 使用 WebFlux:

java

package com.seckillsystem.controller;

import com.seckillsystem.model.Product;
import com.seckillsystem.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<Product> getAllProducts(@RequestParam int page, @RequestParam int size) {
        return Flux.fromIterable(productService.getAllProducts(PageRequest.of(page, size)).getContent());
    }

    @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<Product> getProductById(@PathVariable Long id) {
        return Mono.justOrEmpty(productService.getProductById(id));
    }

    @PostMapping(value = "/seckill/{productId}/{userId}/{quantity}", produces = MediaType.TEXT_PLAIN_VALUE)
    public Mono<String> seckill(@PathVariable Long productId, @PathVariable Long userId, @PathVariable Integer quantity) {
        if (productService.reduceStock(productId)) {
            Order order = new Order();
            order.setProductId(productId);
            order.setUserId(userId);
            order.setQuantity(quantity);
            productService.createOrder(order);
            return Mono.just("Seckill successful!");
        } else {
            return Mono.just("Seckill failed! Out of stock.");
        }
    }
}
4.4 日志监控

使用 ELK Stack (Elasticsearch, Logstash, Kibana) 或 Prometheus + Grafana 进行日志收集和监控。

4.4.1 配置 ELK Stack

在 Kubernetes 中部署 Elasticsearch、Logstash 和 Kibana。

Elasticsearch Deployment YAML:

Yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
spec:
  serviceName: "elasticsearch"
  replicas: 1
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: docker.elastic.co/elasticsearch/elasticsearch:7.10.1
          ports:
            - containerPort: 9200
            - containerPort: 9300
          env:
            - name: discovery.type
              value: single-node
          volumeMounts:
            - name: elasticsearch-persistent-storage
              mountPath: /usr/share/elasticsearch/data
  volumeClaimTemplates:
    - metadata:
        name: elasticsearch-persistent-storage
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 10Gi

---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
spec:
  ports:
    - port: 9200
  clusterIP: None
  selector:
    app: elasticsearch

Logstash Deployment YAML:

Yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: logstash
spec:
  replicas: 1
  selector:
    matchLabels:
      app: logstash
  template:
    metadata:
      labels:
        app: logstash
    spec:
      containers:
        - name: logstash
          image: docker.elastic.co/logstash/logstash:7.10.1
          ports:
            - containerPort: 5044
          volumeMounts:
            - name: logstash-config
              mountPath: /usr/share/logstash/pipeline/
      volumes:
        - name: logstash-config
          configMap:
            name: logstash-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: logstash-config
data:
  logstash.conf: |
    input {
      beats {
        port => 5044
      }
    }
    output {
      elasticsearch {
        hosts => ["http://elasticsearch:9200"]
        index => "logstash-%{+YYYY.MM.dd}"
      }
      stdout { codec => rubydebug }
    }

Kibana Deployment YAML:

Yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
        - name: kibana
          image: docker.elastic.co/kibana/kibana:7.10.1
          ports:
            - containerPort: 5601
          env:
            - name: ELASTICSEARCH_HOSTS
              value: http://elasticsearch:9200

---
apiVersion: v1
kind: Service
metadata:
  name: kibana
spec:
  ports:
    - port: 5601
  type: LoadBalancer
  selector:
    app: kibana
4.4.2 配置 Prometheus and Grafana

在 Kubernetes 中部署 Prometheus 和 Grafana。

Prometheus Deployment YAML:

Yaml

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceAccountName: prometheus
  serviceMonitorSelector:
    matchLabels:
      team: frontend
  ruleSelector:
    matchLabels:
      role: alert-rules
      team: frontend
  alertingRulesNamespaceSelector: {}
  alertingRulesSelector: {}
  enableAdminAPI: false
  externalUrl: ""
  routePrefix: /
  webRoutePrefix: /

---
apiVersion: v1
kind: Service
metadata:
  name: prometheus
spec:
  ports:
    - name: web
      port: 9090
      targetPort: web
  selector:
    app: prometheus-server
  type: LoadBalancer

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: prometheus-clusterrole-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus-clusterrole
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: default

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: prometheus-clusterrole
rules:
- apiGroups: [""]
  resources:
  - nodes
  - services
  - endpoints
  - pods
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources:
  - configmaps
  verbs: ["get"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
  namespace: default

Grafana Deployment YAML:

Yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
        - name: grafana
          image: grafana/grafana:latest
          ports:
            - containerPort: 3000
          env:
            - name: GF_SECURITY_ADMIN_PASSWORD
              value: admin

---
apiVersion: v1
kind: Service
metadata:
  name: grafana
spec:
  ports:
    - port: 3000
  type: LoadBalancer
  selector:
    app: grafana
4.5 消息队列

使用 RabbitMQ 处理订单创建等耗时操作,避免阻塞主线程。这部分已经在前面的章节中详细说明了。

5. 测试

编写单元测试和集成测试来确保代码质量和稳定性。

5.1 单元测试

使用 JUnit 和 Mockito 进行单元测试。以下是一个简单的示例:

java

package com.seckillsystem.service;

import com.seckillsystem.model.Product;
import com.seckillsystem.repository.ProductRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.redisson.api.RedissonClient;
import org.redisson.api.RLock;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.*;

class ProductServiceTest {

    @Mock
    private ProductRepository productRepository;

    @Mock
    private RedissonClient redissonClient;

    @Mock
    private RLock rLock;

    @InjectMocks
    private ProductService productService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        when(redissonClient.getLock(anyString())).thenReturn(rLock);
    }

    @Test
    void testReduceStock_Success() {
        Product product = new Product();
        product.setId(1L);
        product.setStock(10);

        when(productRepository.findById(1L)).thenReturn(Optional.of(product));

        assertTrue(productService.reduceStock(1L));

        verify(productRepository, times(1)).findById(1L);
        verify(productRepository, times(1)).save(product);
        verify(rLock, times(1)).tryLock();
        verify(rLock, times(1)).unlock();
    }

    @Test
    void testReduceStock_OutOfStock() {
        Product product = new Product();
        product.setId(1L);
        product.setStock(0);

        when(productRepository.findById(1L)).thenReturn(Optional.of(product));

        assertFalse(productService.reduceStock(1L));

        verify(productRepository, times(1)).findById(1L);
        verify(productRepository, never()).save(product);
        verify(rLock, times(1)).tryLock();
        verify(rLock, times(1)).unlock();
    }

    @Test
    void testReduceStock_ProductNotFound() {
        when(productRepository.findById(1L)).thenReturn(Optional.empty());

        assertFalse(productService.reduceStock(1L));

        verify(productRepository, times(1)).findById(1L);
        verify(productRepository, never()).save(any(Product.class));
        verify(rLock, times(1)).tryLock();
        verify(rLock, times(1)).unlock();
    }
}
5.2 集成测试

使用 Spring Boot Test 进行集成测试。以下是一个简单的示例:

java

package com.seckillsystem.service;

import com.seckillsystem.SeckillSystemApplication;
import com.seckillsystem.model.Product;
import com.seckillsystem.repository.ProductRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SpringBootTest(classes = SeckillSystemApplication.class)
@Transactional
class ProductServiceIntegrationTest {

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductRepository productRepository;

    @BeforeEach
    void setUp() {
        Product product = new Product();
        product.setName("Sample Product");
        product.setPrice(100.0);
        product.setStock(10);
        productRepository.save(product);
    }

    @Test
    void testReduceStock_Success() {
        Long productId = productRepository.findByName("Sample Product").getId();

        assertTrue(productService.reduceStock(productId));

        Product updatedProduct = productRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found"));
        assertEquals(9, updatedProduct.getStock());
    }

    @Test
    void testReduceStock_OutOfStock() {
        Long productId = productRepository.findByName("Sample Product").getId();

        for (int i = 0; i < 10; i++) {
            assertTrue(productService.reduceStock(productId));
        }

        assertFalse(productService.reduceStock(productId));

        Product updatedProduct = productRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found"));
        assertEquals(0, updatedProduct.getStock());
    }
}
6.1 添加限流

注册拦截器:

java

package com.seckillsystem.config;

import com.seckillsystem.interceptor.RateLimitingInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private RateLimitingInterceptor rateLimitingInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(rateLimitingInterceptor).addPathPatterns("/api/products/seckill/**");
    }
}
6.2 使用消息队列处理订单

使用 RabbitMQ 处理订单创建等耗时操作,避免阻塞主线程。

pom.xml 中添加 RabbitMQ 依赖:

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

配置 RabbitMQ 连接:

properties

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

创建消息队列和交换机配置:

java

package com.seckillsystem.config;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMqConfig {

    public static final String ORDER_QUEUE = "order_queue";

    @Bean
    public Queue orderQueue() {
        return new Queue(ORDER_QUEUE, true);
    }
}

修改 OrderService 以支持消息队列:

java

package com.seckillsystem.service;

import com.seckillsystem.model.Order;
import com.seckillsystem.repository.OrderRepository;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void createOrder(Order order) {
        rabbitTemplate.convertAndSend(RabbitMqConfig.ORDER_QUEUE, order);
    }

    public Order saveOrder(Order order) {
        return orderRepository.save(order);
    }
}

创建消息监听器来处理订单:

java

package com.seckillsystem.listener;

import com.seckillsystem.model.Order;
import com.seckillsystem.service.OrderService;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class OrderListener {

    @Autowired
    private OrderService orderService;

    @RabbitListener(queues = RabbitMqConfig.ORDER_QUEUE)
    public void processOrder(Order order) {
        orderService.saveOrder(order);
    }
}

8. 总结

通过以上步骤,我们成功搭建了一个基于微服务架构的高并发秒杀系统后端,并实现了容器化和部署。以下是整个架构的概览图:

这个架构展示了各个组件之间的关系和交互。你可以根据实际需求进一步扩展和完善这个架构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275011.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Http请求响应——请求

Http概述 Http协议&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;&#xff0c;是一种用于传输网页数据的协议&#xff0c;规定了浏览器和服务器之间进行数据传输的规则&#xff0c;简单说来就是客户端与服务器端数据交互的数据格式。 客户端…

Vue3 + Vite + Electron + Ts 项目快速创建

一、创建 Vue 项目 1. 创建项目 pnpm create vite 2. 安装依赖 cd excel-electron pnpm install 3. 运行项目 pnpm dev 二、添加 Electron 1. 安装 electron pnpm add electron -D 2. 修改 package.json 添加入口 js 和执行命令。 {"main": "dist-ele…

socket网络编程-TC/IP方式

网络编程 1.概念&#xff1a;两台设备之间通过网络数据传输。 2.网络通信&#xff1a;将数据通过网络从一台设备传输另外一台设备。 3.java.net包下提供了一系列的类和接口&#xff0c;提供程序员使用&#xff0c;完成网络通信。 TCP和UDP TCP协议&#xff1a; 1.使用TCP协…

RabbitMQ基本介绍及简单上手

&#xff08;一&#xff09;什么是MQ MQ&#xff08;message queue&#xff09;本质上是队列&#xff0c;满足先入先出&#xff0c;只不过队列中存放的内容是消息而已&#xff0c;那什么是消息呢&#xff1f; 消息可以是字符串&#xff0c;json也可以是一些复杂对象 我们应用场…

【Rust自学】11.3. 自定义错误信息

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 11.3.1. 添加错误信息 在 11.2. 断言(Assert) 中我们学习了assert!、assert_eq!和assert_ne!这三个宏&#xff0c;而这篇文章讲的就是它…

某漫画网站JS逆向反混淆流程分析

文章目录 1. 写在前面1. 接口分析2. 反混淆分析 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Pyth…

网络分析与监控:阿里云拨测方案解密

作者&#xff1a;俞嵩(榆松) 随着互联网的蓬勃发展&#xff0c;网络和服务的稳定性已成为社会秩序中不可或缺的一部分。一旦网络和服务发生故障&#xff0c;其带来的后果将波及整个社会、企业和民众的生活质量&#xff0c;造成难以估量的损失。 2020 年 12 月&#xff1a; Ak…

STL——二叉搜索树

目录 二叉搜索树的概念 ⼆叉搜索树的性能分析 ⼆叉搜索树的插⼊ ⼆叉搜索树的查找 ⼆叉搜索树的删除 中序遍历结果为升序序列 二叉搜索树的概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树 • 若它的左⼦树不为空&#…

【文件I/O】UNIX文件基础

IO编程的本质是通过 API 操作 文件。 什么是 IO I - Input 输入O - Output 输出 这里的输入和输出都是站在应用&#xff08;运行中的程序&#xff09;的角度。外部特指文件。 这里的文件是泛指&#xff0c;并不是只表示存在存盘中的常规文件。还有设备、套接字、管道、链接…

VS调试MFC进入系统源代码配置

调试MFC代码有时候能进入MFC的源代码,有时候不能.之前一直没有深入研究.后面经过查资料发现每次调试必能进入源代码的配置.很简单,只需要3步. 1.打开工具->选项->调试->符号,勾选Microsoft符号服务器. 2.打开项目->属性->配置属性->常规,MFC的使用修改成&qu…

车载网络:现代汽车的数字心跳

在汽车领域&#xff0c;“智能汽车”一词毫不夸张。如今的汽车已不再是原始的机械工程&#xff0c;而是通过先进的车载网络无缝连接的精密数字生态系统。这些滚动计算机由复杂的电子控制单元(ECU)网络提供动力&#xff0c;ECU是负责管理从发动机性能到信息娱乐系统等一切事务的…

mycat介绍与操作步骤

文章目录 1.分库分表2.mycat 入门2.1 概述2.2 案例&#xff1a;水平分表1&#xff09;准备工作2&#xff09;配置3&#xff09;启动并测试 3.mycat 配置详解3.1 schema.xml3.2 rule.xml3.3 server.xml 4.mycat 分片&#xff1a;垂直拆分1&#xff09;准备工作2&#xff09;配置…

【Python】Python之Selenium基础教程+实战demo:提升你的测试+测试数据构造的效率!

这里写目录标题 什么是Selenium&#xff1f;Selenium基础用法详解环境搭建编写第一个Selenium脚本解析脚本脚本执行结果常用的元素定位方法常用的WebDriver方法等待机制 Selenium高级技巧详解页面元素操作处理弹窗和警告框截图和日志记录多窗口和多标签页操作 一个实战的小demo…

Apache XMLBeans 一个强大的 XML 数据处理框架

Apache XMLBeans 是一个用于处理 XML 数据的 Java 框架&#xff0c;它提供了一种方式将 XML Schema (XSD) 映射到 Java 类&#xff0c;从而使得开发者可以通过强类型化的 Java 对象来访问和操作 XML 文档。下面将以一个简单的案例说明如何使用 Apache XMLBeans 来解析、生成和验…

带格式 pdf 翻译

支持 openAI 接口&#xff0c;国内 deepseek 接口兼容 openAI 接口&#xff0c; deepseek api 又非常便宜 https://pdf2zh.com/ https://github.com/Byaidu/PDFMathTranslate

ubuntu22.04降级安装CUDA11.3

环境&#xff1a;主机x64的ubuntu22.04&#xff0c;原有CUDA12.1&#xff0c;但是现在需要CUDA11.3&#xff0c;本篇文章介绍步骤。 一、下载CUDA11.3的run文件 下载网址&#xff1a;https://developer.nvidia.com/cuda-11-3-1-download-archive?target_osLinux&target_…

9 异常

如果你希望在软件调试上有所突破,或者想了解如何通过异常进行反调试,或者想自己写一个调试器,那么就必须要深入了解异常,异常与调试是紧密相连的,异常是调试的基础。 异常产生后,首先是要记录异常信息(异常的类型、异常发生的位置等),然后要寻找异常的处理函数,我们…

springBoot整合ELK Windowsb版本 (elasticsearch+logstash+kibana)

springBoot整合ELK Windowsb版本 【elasticsearchlogstashkibana】 下载软件启动服务1、elasticsearch2、kibana3、logstash 集成springboot1、添加依赖2、在logback.xml添加相关配置3、修改logstash 配置4、重启logstash 最后测试 下载软件 elasticsearch 官网 https://www.…

详解Sonar与Jenkins 的集成使用!

本文阅读前提 本文假设读者熟悉Jenkins和SonarQube的基础操作。 核心实现功能 Jenkins中运行的job来调用SonarScanner&#xff0c;最后可实现测试结果与SonarQube中同步查看。 Jenkins中安装Sonar相关插件 配置Sonarqube Dashboard>Manage Jenkins>Systems 指定son…

鸿蒙面试 2025-01-10

写了鉴权工具&#xff0c;你在项目中申请了那些权限&#xff1f;&#xff08;常用权限&#xff09; 位置权限 &#xff1a; ohos.permission.LOCATION_IN_BACKGROUND&#xff1a;允许应用在后台访问位置信息。 ohos.permission.LOCATION&#xff1a;允许应用访问精确的位置信息…