前言
在开始秒杀业务代码编写之前首先需要准备一些环境。比如redis环境,项目依赖等等
一、环境准备
1. 搭建redis服务器
此处直接使用docker来准备一个redis服务器
docker run -d --name redis -p 6379:6379 redis --requirepass "123456"
运行以上命令可以创建一个名称为redis的redis服务器。
登录redis服务器
docker exec -it redis /bin/bash
登录客户端并输入认证密码
redis-cli
auth 123456
然后再设置key、获取key保证redis正常,如下所示
2. 搭建mysql数据库
具体可以参考博客:docker安装mysql5.7
此次使用的mysql信息如下
spring.datasource.druid.url=jdbc:mysql://192.168.1.14:3307/kill
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
3. 创建maven项目
本次项目使用的是spring-boot的web项目,相关依赖为spring-boot-starter-web
,数据库使用mysql,相关依赖mysql-connector-java
、spring-boot-starter-data-jdbc
、mybatis-spring-boot-starter
,数据库连接池使用的是druid
,相关依赖druid-spring-boot-starter
。另外在插件spring-boot-maven-plugin
当中通过mainClass
配置项目的主类。而插件docker-maven-plugin
则用于将spring-boot项目打包为镜像上传到服务器。这样在服务器上面可以通过这个镜像创建容器。可以参考博客:IDEA发布Spring Boot项目到docker
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>kill-simple-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>kill-simple-app</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.22.RELEASE</version>
<configuration>
<!--springboot启动类-->
<mainClass>com.example.kill.KillStartMain</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.13</version>
<!-- <version>1.0.0</version>-->
<!--将插件绑定在某个phase执行-->
<executions>
<execution>
<id>build-image</id>
<!--用户只需执行mvn package ,就会自动执行mvn docker:build-->
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<imageName>${project.artifactId}</imageName>
<baseImage>openjdk:8</baseImage>
<entryPoint>["java", "-jar","-Duser.timezone=GMT+8 -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8","/${project.build.finalName}.jar"]
</entryPoint>
<!--指定远程 docker api地址-->
<dockerHost>http://191.168.1.14:2375</dockerHost>
<resources>
<resource>
<targetPath>/</targetPath>
<!--jar 包所在的路径 此处配置的 即对应 target 目录-->
<directory>${project.build.directory}</directory>
<!--用于指定需要复制的文件 需要包含的 jar包 -->
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
</project>
4. 参数配置
src/main/resources/application.properties
server.port=8080
server.tomcat.max-threads=1000
server.tomcat.accept-count=1000
server.tomcat.max-connections=2000
server.tomcat.uri-encoding=UTF-8
# mybatis配置
mybatis.mapper-locations=classpath*:sqlMapper/**/*.xml
mybatis.type-aliases-package=com.example.kill.entity
# 健康监控配置
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# 数据源配置
spring.datasource.druid.url=jdbc:mysql://191.168.1.15:3306/kill
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
# 配置redis
spring.redis.host=191.168.1.15
spring.redis.port=6379
spring.redis.password=123456
spring.redis.jedis.pool.max-active=100
spring.redis.jedis.pool.max-idle=100
spring.redis.jedis.pool.max-wait=10000
spring.redis.jedis.pool.min-idle=50
logging.level.root=info
logging.level.com.example.kill.mapper=trace
通过spring-boot的自动依赖配置和以上的参数配置就配置好了数据库连接池、redis的连接池
5. 创建配置类、启动类
com/example/kill/config/RedisConfig.java
package com.example.kill.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//使用jdk的序列化
// template.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));
template.setValueSerializer(new StringRedisSerializer());
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
com/example/kill/controller/KillController.java
package com.example.kill.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class KillController {
private static final Logger logger = LoggerFactory.getLogger(KillController.class);
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("kill/{goodsId}")
public String kill(@PathVariable("goodsId") Integer goodsId) {
Object name = redisTemplate.opsForValue().get("name");
return (String) name;
}
}
com/example/kill/KillStartMain.java
package com.example.kill;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class KillStartMain {
public static void main(String[] args) {
SpringApplication.run(KillStartMain.class);
}
}
二、项目部署和压测
1.部署
执行命令
mvn clean install -Dmaven.test.skip=true
就可以将项目的镜像上传到服务器。
在对应服务器上面查看镜像信息
创建一个脚本用于删除和创建容器
docker rm -f kill-server-8080
docker run --env server.port=8080 -d --name kill-server-8080 -p 8080:8080 -v /etc/localtime:/etc/localtime -v /docker-log:/logs kill-simple-app
首先强制删除名称为kill-server-8080
的容器,然后再创建,通过 --env设置spring-boot项目的端口为8080。并将项目的日志映射到宿主机的/docker-log
目录.
通过浏览器访问服务器:http://191.168.1.14:8080/kill/2
返回结果为mark。此处通过controller访问了redis服务器并请求name属性。返回了mark。说明当前项目部署完成了
2.压测
学习秒杀技术最关键的就是提高QPS。通过压测工具来进行压测也是必不可少的,这次主要通过jmeter工具进行压测。
此处并不进行这个工具使用的介绍。不了解的可以参考这个博客:Jmeter压力测试工具安装及使用教程
首先创建线程组并配置线程数为1000
然后创建http请求
然后再添加http请求的监听器
开始测试
以上测试的QPS为713。如果使用的为自己的虚拟机或者直接在windows上测试,会更低。这个值与服务器性能有很大关系。
总结
秒杀业务的特点是:秒杀是一个典型的读多写少的业务, 大量用户参与秒杀, 真正能抢到商品的微乎其微, 所以大部分的用户只是浏览查看到了商品并没有抢购成功、服务层核心设计思想, 尽量把大量的请求不要瞬时落到数据库层。因此必须使用到缓存。通过以上的步骤,我们准备好了一个秒杀项目的环境。