在开始秒杀业务代码编写之前首先需要准备一些环境。比如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正常,如下所示
具体可以参考博客: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本次项目使用的是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>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的连接池
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); } }执行命令
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。说明当前项目部署完成了
学习秒杀技术最关键的就是提高QPS。通过压测工具来进行压测也是必不可少的,这次主要通过jmeter工具进行压测。 此处并不进行这个工具使用的介绍。不了解的可以参考这个博客:Jmeter压力测试工具安装及使用教程
首先创建线程组并配置线程数为1000 然后创建http请求 然后再添加http请求的监听器 开始测试
以上测试的QPS为713。如果使用的为自己的虚拟机或者直接在windows上测试,会更低。这个值与服务器性能有很大关系。
秒杀业务的特点是:秒杀是一个典型的读多写少的业务, 大量用户参与秒杀, 真正能抢到商品的微乎其微, 所以大部分的用户只是浏览查看到了商品并没有抢购成功、服务层核心设计思想, 尽量把大量的请求不要瞬时落到数据库层。因此必须使用到缓存。通过以上的步骤,我们准备好了一个秒杀项目的环境。
