秒杀初交

This commit is contained in:
小柒2012
2018-05-15 18:30:33 +08:00
parent efa656ec9e
commit 786dc6c311
51 changed files with 2861 additions and 0 deletions

32
.classpath Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<attributes>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

42
.project Normal file
View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>spring-boot-seckill</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>

13
.settings/.jsdtscope Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/webapp"/>
<classpathentry kind="src" path="target/m2e-wtp/web-resources"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
<attributes>
<attribute name="hide" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
<classpathentry kind="output" path=""/>
</classpath>

View File

@@ -0,0 +1,13 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="spring-boot-seckill">
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java"/>
<property name="context-root" value="spring-boot-seckill"/>
<property name="java-output-path" value="/spring-boot-seckill/target/classes"/>
</wb-module>
</project-modules>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<fixed facet="wst.jsdt.web"/>
<installed facet="jst.web" version="2.3"/>
<installed facet="wst.jsdt.web" version="1.0"/>
<installed facet="java" version="1.7"/>
</faceted-project>

View File

@@ -0,0 +1 @@
org.eclipse.wst.jsdt.launching.baseBrowserLibrary

View File

@@ -0,0 +1 @@
Window

View File

@@ -0,0 +1,2 @@
disabled=06target
eclipse.preferences.version=1

106
pom.xml Normal file
View File

@@ -0,0 +1,106 @@
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itstyle.seckill</groupId>
<artifactId>spring-boot-seckill</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-seckill Maven</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<docker.image.prefix>springboot</docker.image.prefix>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<!-- 实现web功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 模版 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- spring-boot-starter-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- 构建Restful API -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!-- Redisson实现分布式锁 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.11.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<!--kafka支持-->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>1.3.5.RELEASE</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<!-- zookeeper 分布式锁、注意zookeeper版本 这里对应的是3.4.6-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-seckill</finalName><plugins>
<!-- 打包项目 mvn clean package -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<!-- mvn spring-boot:run 热部署启动 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.7.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,24 @@
package com.itstyle.seckill;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
* 创建者 科帮网
* 创建时间 2018年5月12日
* API接口测试http://localhost:8080/seckill/swagger-ui.html
*/
@SpringBootApplication
public class Application {
private final static Logger LOGGER = LoggerFactory.getLogger(Application.class);
/**
* 1. 数据库乐观锁2. 基于Redis的分布式锁3. 基于ZooKeeper的分布式锁
* 4. redis 订阅监听5.kafka消息队列
*/
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(Application.class, args);
LOGGER.info("项目启动 ");
}
}

View File

@@ -0,0 +1,27 @@
package com.itstyle.seckill.common.api;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
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 userApi() {
return new Docket(DocumentationType.SWAGGER_2).groupName("秒杀案例").apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.basePackage("com.itstyle.seckill.web")).paths(PathSelectors.any()).build();
}
// 预览地址:swagger-ui.html
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("Spring 中使用Swagger2构建文档").termsOfServiceUrl("https://blog.52itstyle.com")
.contact(new Contact("科帮网 ", "https://blog.52itstyle.com/", "345849402@qq.com")).version("1.1").build();
}
}

View File

@@ -0,0 +1,35 @@
package com.itstyle.seckill.common.config;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 通用访问拦截匹配
* 创建者 科帮网
* 创建时间 2018年4月3日
*/
@Controller
public class IndexController {
/**
* 页面跳转
* @param module
* @param url
* @return
*/
@RequestMapping("{url}.shtml")
public String page(@PathVariable("url") String url) {
return url;
}
/**
* 页面跳转(二级目录)
* @param module
* @param function
* @param url
* @return
*/
@RequestMapping("{module}/{url}.shtml")
public String page(@PathVariable("module") String module,@PathVariable("url") String url) {
return module + "/" + url;
}
}

View File

@@ -0,0 +1,80 @@
package com.itstyle.seckill.common.dynamicquery;
import java.util.List;
/**
* 扩展SpringDataJpa, 支持动态jpql/nativesql查询并支持分页查询
* 使用方法注入ServiceImpl
* 创建者 张志朋
* 创建时间 2018年3月8日
*/
public interface DynamicQuery {
public void save(Object entity);
public void update(Object entity);
public <T> void delete(Class<T> entityClass, Object entityid);
public <T> void delete(Class<T> entityClass, Object[] entityids);
/**
* 查询对象列表返回List
* @param resultClass
* @param nativeSql
* @param params
* @return List<T>
* @Date 2018年3月15日
* 更新日志
* 2018年3月15日 张志朋 首次创建
*
*/
<T> List<T> nativeQueryList(String nativeSql, Object... params);
/**
* 查询对象列表返回List<Map<key,value>>
* @param nativeSql
* @param params
* @return List<T>
* @Date 2018年3月15日
* 更新日志
* 2018年3月15日 张志朋 首次创建
*
*/
<T> List<T> nativeQueryListMap(String nativeSql,Object... params);
/**
* 查询对象列表返回List<组合对象>
* @param resultClass
* @param nativeSql
* @param params
* @return List<T>
* @Date 2018年3月15日
* 更新日志
* 2018年3月15日 张志朋 首次创建
*
*/
<T> List<T> nativeQueryListModel(Class<T> resultClass, String nativeSql, Object... params);
/**
* 执行nativeSql统计查询
* @param nativeSql
* @param params 占位符参数(例如?1)绑定的参数值
* @return 统计条数
*/
Object nativeQueryObject(String nativeSql, Object... params);
/**
* 执行nativeSql统计查询
* @param nativeSql
* @param params 占位符参数(例如?1)绑定的参数值
* @return 统计条数
*/
Object[] nativeQueryArray(String nativeSql, Object... params);
/**
* 执行nativeSql的update,delete操作
* @param nativeSql
* @param params
* @return
*/
int nativeExecuteUpdate(String nativeSql, Object... params);
}

View File

@@ -0,0 +1,101 @@
package com.itstyle.seckill.common.dynamicquery;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.hibernate.SQLQuery;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
/**
* 动态jpql/nativesql查询的实现类
* 创建者 张志朋
* 创建时间 2018年3月8日
*/
@Repository
public class DynamicQueryImpl implements DynamicQuery {
Logger logger = LoggerFactory.getLogger(DynamicQueryImpl.class);
@PersistenceContext
private EntityManager em;
public EntityManager getEntityManager() {
return em;
}
@Override
public void save(Object entity) {
em.persist(entity);
}
@Override
public void update(Object entity) {
em.merge(entity);
}
@Override
public <T> void delete(Class<T> entityClass, Object entityid) {
delete(entityClass, new Object[] { entityid });
}
@Override
public <T> void delete(Class<T> entityClass, Object[] entityids) {
for (Object id : entityids) {
em.remove(em.getReference(entityClass, id));
}
}
private Query createNativeQuery(String sql, Object... params) {
Query q = em.createNativeQuery(sql);
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
q.setParameter(i + 1, params[i]); // 与Hiberante不同,jpa
// query从位置1开始
}
}
return q;
}
@SuppressWarnings("unchecked")
@Override
public <T> List<T> nativeQueryList(String nativeSql, Object... params) {
Query q = createNativeQuery(nativeSql, params);
q.unwrap(SQLQuery.class).setResultTransformer(Transformers.TO_LIST);
return q.getResultList();
}
@SuppressWarnings("unchecked")
@Override
public <T> List<T> nativeQueryListModel(Class<T> resultClass,
String nativeSql, Object... params) {
Query q = createNativeQuery(nativeSql, params);;
q.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(resultClass));
return q.getResultList();
}
@SuppressWarnings("unchecked")
@Override
public <T> List<T> nativeQueryListMap(String nativeSql, Object... params) {
Query q = createNativeQuery(nativeSql, params);
q.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
return q.getResultList();
}
@Override
public Object nativeQueryObject(String nativeSql, Object... params) {
return createNativeQuery(nativeSql, params).getSingleResult();
}
@Override
public int nativeExecuteUpdate(String nativeSql, Object... params) {
return createNativeQuery(nativeSql, params).executeUpdate();
}
@Override
public Object[] nativeQueryArray(String nativeSql, Object... params) {
return (Object[]) createNativeQuery(nativeSql, params).getSingleResult();
}
}

View File

@@ -0,0 +1,11 @@
package com.itstyle.seckill.common.dynamicquery;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultEntity {
}

View File

@@ -0,0 +1,59 @@
package com.itstyle.seckill.common.entity;
import java.util.HashMap;
import java.util.Map;
/**
* 页面响应entity
* 创建者 张志朋
* 创建时间 2018年3月8日
*/
public class Result extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public Result() {
put("code", 0);
}
public static Result error() {
return error(500, "未知异常,请联系管理员");
}
public static Result error(String msg) {
return error(500, msg);
}
public static Result error(int code, String msg) {
Result r = new Result();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static Result error(Object msg) {
Result r = new Result();
r.put("msg", msg);
return r;
}
public static Result ok(Object msg) {
Result r = new Result();
r.put("msg", msg);
return r;
}
public static Result ok(Map<String, Object> map) {
Result r = new Result();
r.putAll(map);
return r;
}
public static Result ok() {
return new Result();
}
@Override
public Result put(String key, Object value) {
super.put(key, value);
return this;
}
}

View File

@@ -0,0 +1,81 @@
package com.itstyle.seckill.common.entity;
import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;
@Entity
@Table(name = "seckill")
public class Seckill implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "seckill_id", nullable = false)
private long seckillId;
private String name;
private int number;
private Timestamp startTime;
private Timestamp endTime;
private Timestamp createTime;
@Version
private int version;
public long getSeckillId() {
return seckillId;
}
public void setSeckillId(long seckillId) {
this.seckillId = seckillId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Timestamp getStartTime() {
return startTime;
}
public void setStartTime(Timestamp startTime) {
this.startTime = startTime;
}
public Timestamp getEndTime() {
return endTime;
}
public void setEndTime(Timestamp endTime) {
this.endTime = endTime;
}
public Timestamp getCreateTime() {
return createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}

View File

@@ -0,0 +1,54 @@
package com.itstyle.seckill.common.entity;
import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "success_killed")
public class SuccessKilled implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name = "seckill_id", nullable = false)
private long seckillId;
@Id
private long userId;
private short state;
private Timestamp createTime;
public long getSeckillId() {
return seckillId;
}
public void setSeckillId(long seckillId) {
this.seckillId = seckillId;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public short getState() {
return state;
}
public void setState(short state) {
this.state = state;
}
public Timestamp getCreateTime() {
return createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
}

View File

@@ -0,0 +1,40 @@
package com.itstyle.seckill.common.enums;
public enum SeckillStatEnum {
SUCCESS(1,"秒杀成功"),
END(0,"秒杀结束"),
REPEAT_KILL(-1,"重复秒杀"),
INNER_ERROR(-2,"系统异常"),
DATE_REWRITE(-3,"数据篡改");
private int state;
private String info;
SeckillStatEnum(int state, String info) {
this.state = state;
this.info = info;
}
public int getState() {
return state;
}
public String getInfo() {
return info;
}
public static SeckillStatEnum stateOf(int index)
{
for (SeckillStatEnum state : values())
{
if (state.getState()==index)
{
return state;
}
}
return null;
}
}

View File

@@ -0,0 +1,19 @@
package com.itstyle.seckill.common.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 配置首页
* 创建者 小柒2012
* 创建时间 2017年9月7日
*/
@Configuration
public class MyAdapter extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers( ViewControllerRegistry registry ) {
registry.addViewController( "/" ).setViewName( "forward:/login.shtml" );
registry.setOrder( Ordered.HIGHEST_PRECEDENCE );
super.addViewControllers( registry );
}
}

View File

@@ -0,0 +1,90 @@
package com.itstyle.seckill.common.redis;
import java.lang.reflect.Method;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* 自定义key(消息队列 暂时用不到 自行忽略)
* 此方法将会根据类名+方法名+所有参数的值生成唯一的一个key,即使@Cacheable中的value属性一样key也会不一样。
* @Author 科帮网
* @return
* @Date 2017年8月13日
* 更新日志
* 2017年8月13日 科帮网 首次创建
*
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method,
Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 缓存管理器
* @Author 科帮网
* @param redisTemplate
* @return CacheManager
* @Date 2017年8月13日
* 更新日志
* 2017年8月13日 科帮网 首次创建
*/
@SuppressWarnings("rawtypes")
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
return new RedisCacheManager(redisTemplate);
}
/**
* 序列化Java对象
* @Author 科帮网
* @param factory
* @return RedisTemplate<Object, Object>
* @Date 2017年8月13日
* 更新日志
* 2017年8月13日 科帮网 首次创建
*
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setConnectionFactory(connectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值默认使用JDK的序列化方式
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}

View File

@@ -0,0 +1,130 @@
package com.itstyle.seckill.common.redis;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
* 缓存工具类
* 创建者 科帮网
* 创建时间 2018年4月8日
*/
@Component
public class RedisUtil {
private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
@Resource
private RedisTemplate<Serializable, Serializable> redisTemplate;
/**
* 前缀
*/
public static final String KEY_PREFIX_VALUE = "itstyle:seckill:value:";
/**
* 缓存value操作
* @param k
* @param v
* @param time
* @return
*/
public boolean cacheValue(String k, Serializable v, long time) {
String key = KEY_PREFIX_VALUE + k;
try {
ValueOperations<Serializable, Serializable> valueOps = redisTemplate.opsForValue();
valueOps.set(key, v);
if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
return true;
} catch (Throwable t) {
logger.error("缓存[{}]失败, value[{}]",key,v,t);
}
return false;
}
/**
* 缓存value操作
* @Author 科帮网
* @param k
* @param v
* @param time
* @param unit
* @return boolean
* @Date 2017年12月23日
* 更新日志
* 2017年12月23日 科帮网 首次创建
*
*/
public boolean cacheValue(String k, Serializable v, long time,TimeUnit unit) {
String key = KEY_PREFIX_VALUE + k;
try {
ValueOperations<Serializable, Serializable> valueOps = redisTemplate.opsForValue();
valueOps.set(key, v);
if (time > 0) redisTemplate.expire(key, time, unit);
return true;
} catch (Throwable t) {
logger.error("缓存[{}]失败, value[{}]",key,v,t);
}
return false;
}
/**
* 缓存value操作
* @param k
* @param v
* @return
*/
public boolean cacheValue(String k, Serializable v) {
return cacheValue(k, v, -1);
}
/**
* 判断缓存是否存在
* @param k
* @return
*/
public boolean containsValueKey(String k) {
String key = KEY_PREFIX_VALUE + k;
try {
return redisTemplate.hasKey(key);
} catch (Throwable t) {
logger.error("判断缓存存在失败key[" + key + ", error[" + t + "]");
}
return false;
}
/**
* 获取缓存
* @param k
* @return
*/
public Serializable getValue(String k) {
try {
ValueOperations<Serializable, Serializable> valueOps = redisTemplate.opsForValue();
return valueOps.get(KEY_PREFIX_VALUE + k);
} catch (Throwable t) {
logger.error("获取缓存失败key[" + KEY_PREFIX_VALUE + k + ", error[" + t + "]");
}
return null;
}
/**
* 移除缓存
* @param k
* @return
*/
public boolean removeValue(String k) {
String key = KEY_PREFIX_VALUE + k;
try {
redisTemplate.delete(key);
return true;
} catch (Throwable t) {
logger.error("获取缓存失败key[" + key + ", error[" + t + "]");
}
return false;
}
}

View File

@@ -0,0 +1,143 @@
package com.itstyle.seckill.distributedlock.redis;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
/**
* redis分布式锁Demo
* @author 科帮网 By https://blog.52itstyle.com
*/
public class RedissLockDemo {
/**
* 可重入锁Reentrant Lock
* Redisson的分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口同时还支持自动过期解锁
* @param redisson
*/
public void testReentrantLock(RedissonClient redisson) {
RLock lock = redisson.getLock("anyLock");
try {
// 1. 最常见的使用方法
// lock.lock();
// 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
// lock.lock(10, TimeUnit.SECONDS);
// 3. 尝试加锁最多等待3秒上锁以后10秒自动解锁
boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (res) { // 成功
// do your business
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* Redisson同时还为分布式锁提供了异步执行的相关方法
* @param redisson
*/
public void testAsyncReentrantLock(RedissonClient redisson) {
RLock lock = redisson.getLock("anyLock");
try {
lock.lockAsync();
lock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = lock.tryLockAsync(3, 10, TimeUnit.SECONDS);
if (res.get()) {
// do your business
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 公平锁Fair Lock
* Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。
* 在提供了自动过期解锁功能的同时保证了当多个Redisson客户端线程同时请求加锁时优先分配给先发出请求的线程。
* @param redisson
*/
public void testFairLock(RedissonClient redisson){
RLock fairLock = redisson.getFairLock("anyLock");
try{
// 最常见的使用方法
fairLock.lock();
// 支持过期解锁功能, 10秒钟以后自动解锁,无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS);
// 尝试加锁最多等待100秒上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
// do your business
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
fairLock.unlock();
}
// Redisson同时还为分布式可重入公平锁提供了异步执行的相关方法
// RLock fairLock = redisson.getFairLock("anyLock");
// fairLock.lockAsync();
// fairLock.lockAsync(10, TimeUnit.SECONDS);
// Future<Boolean> res = fairLock.tryLockAsync(100, 10, TimeUnit.SECONDS);
}
/**
* 联锁MultiLock
* Redisson的RedissonMultiLock对象可以将多个RLock对象关联为一个联锁每个RLock对象实例可以来自于不同的Redisson实例
* @param redisson1
* @param redisson2
* @param redisson3
*/
public void testMultiLock(RedissonClient redisson1,RedissonClient redisson2, RedissonClient redisson3){
RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
try {
// 同时加锁lock1 lock2 lock3, 所有的锁都上锁成功才算成功。
lock.lock();
// 尝试加锁最多等待100秒上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
// do your business
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 红锁RedLock
* Redisson的RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁每个RLock对象实例可以来自于不同的Redisson实例
* @param redisson1
* @param redisson2
* @param redisson3
*/
public void testRedLock(RedissonClient redisson1,RedissonClient redisson2, RedissonClient redisson3){
RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
try {
// 同时加锁lock1 lock2 lock3, 红锁在大部分节点上加锁成功就算成功。
lock.lock();
// 尝试加锁最多等待100秒上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
// do your business
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//读写锁ReadWriteLock、信号量Semaphore、可过期性信号量PermitExpirableSemaphore、闭锁CountDownLatch
}

View File

@@ -0,0 +1,101 @@
package com.itstyle.seckill.distributedlock.redis;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
/**
* redis分布式锁帮助类
* @author 科帮网 By https://blog.52itstyle.com
*/
public class RedissLockUtil {
private static RedissonClient redissonClient;
public void setRedissonClient(RedissonClient locker) {
redissonClient = locker;
}
/**
* 加锁
* @param lockKey
* @return
*/
public static RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
/**
* 释放锁
* @param lockKey
*/
public static void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
/**
* 释放锁
* @param lock
*/
public static void unlock(RLock lock) {
lock.unlock();
}
/**
* 带超时的锁
* @param lockKey
* @param timeout 超时时间 单位:秒
*/
public static RLock lock(String lockKey, int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, TimeUnit.SECONDS);
return lock;
}
/**
* 带超时的锁
* @param lockKey
* @param unit 时间单位
* @param timeout 超时时间
*/
public static RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
/**
* 尝试获取锁
* @param lockKey
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return false;
}
}
/**
* 尝试获取锁
* @param lockKey
* @param unit 时间单位
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
}

View File

@@ -0,0 +1,75 @@
package com.itstyle.seckill.distributedlock.redis;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
//import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(Config.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redssionProperties;
/**
* 哨兵模式自动装配
* @return
*/
// @Bean
// @ConditionalOnProperty(name="redisson.master-name")
// RedissonClient redissonSentinel() {
// Config config = new Config();
// SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redssionProperties.getSentinelAddresses())
// .setMasterName(redssionProperties.getMasterName())
// .setTimeout(redssionProperties.getTimeout())
// .setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize())
// .setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize());
//
// if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
// serverConfig.setPassword(redssionProperties.getPassword());
// }
// return Redisson.create(config);
// }
/**
* 单机模式自动装配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.address")
RedissonClient redissonSingle() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(redssionProperties.getAddress())
.setTimeout(redssionProperties.getTimeout())
.setConnectionPoolSize(redssionProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize());
if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 装配locker类并将实例注入到RedissLockUtil中
* @return
*/
@Bean
RedissLockUtil redissLockUtil(RedissonClient redissonClient) {
RedissLockUtil redissLockUtil = new RedissLockUtil();
redissLockUtil.setRedissonClient(redissonClient);
return redissLockUtil;
}
}

View File

@@ -0,0 +1,97 @@
package com.itstyle.seckill.distributedlock.redis;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
private int timeout = 3000;
private String address;
private String password;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize=10;
private int slaveConnectionPoolSize = 250;
private int masterConnectionPoolSize = 250;
private String[] sentinelAddresses;
private String masterName;
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getSlaveConnectionPoolSize() {
return slaveConnectionPoolSize;
}
public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
this.slaveConnectionPoolSize = slaveConnectionPoolSize;
}
public int getMasterConnectionPoolSize() {
return masterConnectionPoolSize;
}
public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
this.masterConnectionPoolSize = masterConnectionPoolSize;
}
public String[] getSentinelAddresses() {
return sentinelAddresses;
}
public void setSentinelAddresses(String sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses.split(",");
}
public String getMasterName() {
return masterName;
}
public void setMasterName(String masterName) {
this.masterName = masterName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public int getConnectionMinimumIdleSize() {
return connectionMinimumIdleSize;
}
public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) {
this.connectionMinimumIdleSize = connectionMinimumIdleSize;
}
}

View File

@@ -0,0 +1,59 @@
package com.itstyle.seckill.distributedlock.zookeeper;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.beans.factory.annotation.Value;
/**
* zookeeper 分布式锁
* @author 科帮网 By https://blog.52itstyle.com
*/
public class ZkLockUtil{
@Value("${zookeeper.address}")
private static String address;
public static CuratorFramework client;
static{
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.newClient(address, retryPolicy);
client.start();
}
/**
* 私有的默认构造子,保证外界无法直接实例化
*/
private ZkLockUtil(){};
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder{
/**
* 静态初始化器由JVM来保证线程安全
*/
private static InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
}
public static InterProcessMutex getMutex(){
return SingletonHolder.mutex;
}
//获得了锁
public static void acquire(){
try {
getMutex().acquire();
} catch (Exception e) {
e.printStackTrace();
}
}
//释放锁
public static void release(){
try {
getMutex().release();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,55 @@
package com.itstyle.seckill.queue.jvm;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import com.itstyle.seckill.common.entity.SuccessKilled;
/**
* 秒杀队列(固定长度为100)
* @author 科帮网 By https://blog.52itstyle.com
* 创建时间 2018年5月10日
*/
public class SeckillQueue {
//队列大小
static final int QUEUE_MAX_SIZE = 100;
static BlockingQueue<SuccessKilled> blockingQueue = new LinkedBlockingQueue<SuccessKilled>(QUEUE_MAX_SIZE);
/**
* 私有的默认构造子,保证外界无法直接实例化
*/
private SeckillQueue(){};
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder{
/**
* 静态初始化器由JVM来保证线程安全
*/
private static SeckillQueue queue = new SeckillQueue();
}
//单例队列
public static SeckillQueue getMailQueue(){
return SingletonHolder.queue;
}
/**
* 生产入队
* @param kill
* @throws InterruptedException
* add(e) 队列未满时返回true队列满则抛出IllegalStateException(“Queue full”)异常——AbstractQueue
* put(e) 队列未满时,直接插入没有返回值;队列满时会阻塞等待,一直等到队列未满时再插入。
* offer(e) 队列未满时返回true队列满时返回false。非阻塞立即返回。
* offer(e, time, unit) 设定等待的时间如果在指定时间内还不能往队列中插入数据则返回false插入成功返回true。
*/
public Boolean produce(SuccessKilled kill) throws InterruptedException {
return blockingQueue.offer(kill);
}
//消费出队
public SuccessKilled consume() throws InterruptedException {
return blockingQueue.take();
}
// 获取队列大小
public int size() {
return blockingQueue.size();
}
}

View File

@@ -0,0 +1,31 @@
package com.itstyle.seckill.queue.jvm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import com.itstyle.seckill.common.entity.SuccessKilled;
import com.itstyle.seckill.service.ISeckillService;
/**
* 消费秒杀队列
* 创建者 科帮网
* 创建时间 2018年4月3日
*/
@Component
public class TaskRunner implements ApplicationRunner{
@Autowired
private ISeckillService seckillService;
@Override
public void run(ApplicationArguments var) throws Exception{
while(true){
//进程内队列
SuccessKilled kill = SeckillQueue.getMailQueue().consume();
if(kill!=null){
seckillService.startSeckil(kill.getSeckillId(), kill.getUserId());
}
}
}
}

View File

@@ -0,0 +1,26 @@
package com.itstyle.seckill.queue.kafka;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import com.itstyle.seckill.service.ISeckillService;
/**
* 消费者 spring-kafka 2.0 + 依赖JDK8
* @author 科帮网 By https://blog.52itstyle.com
*/
@Component
public class KafkaConsumer {
@Autowired
private ISeckillService seckillService;
/**
* 监听seckill主题,有消息就读取
* @param message
*/
@KafkaListener(topics = {"seckill"})
public void receiveMessage(String message){
//收到通道的消息之后执行秒杀操作
String[] array = message.split(";");
seckillService.startSeckil(Long.parseLong(array[0]), Long.parseLong(array[1]));
}
}

View File

@@ -0,0 +1,21 @@
package com.itstyle.seckill.queue.kafka;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
/**
* 生产者
* @author 科帮网 By https://blog.52itstyle.com
*/
@Component
public class KafkaSender {
@Autowired
private KafkaTemplate<String,String> kafkaTemplate;
/**
* 发送消息到kafka
*/
public void sendChannelMess(String channel, String message){
kafkaTemplate.send(channel,message);
}
}

View File

@@ -0,0 +1,22 @@
package com.itstyle.seckill.queue.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.itstyle.seckill.service.ISeckillService;
/**
* 消费者
* @author 科帮网 By https://blog.52itstyle.com
*/
@Service
public class RedisConsumer {
@Autowired
private ISeckillService seckillService;
public void receiveMessage(String message) {
//收到通道的消息之后执行秒杀操作(超卖)
String[] array = message.split(";");
seckillService.startSeckil(Long.parseLong(array[0]), Long.parseLong(array[1]));
}
}

View File

@@ -0,0 +1,18 @@
package com.itstyle.seckill.queue.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
/**
* 生产者
* @author 科帮网 By https://blog.52itstyle.com
*/
@Service
public class RedisSender {
@Autowired
private StringRedisTemplate stringRedisTemplate;
//向通道发送消息的方法
public void sendChannelMess(String channel, String message) {
stringRedisTemplate.convertAndSend(channel, message);
}
}

View File

@@ -0,0 +1,32 @@
package com.itstyle.seckill.queue.redis;
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.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
@Configuration
public class RedisSubListenerConfig {
//初始化监听器
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic("seckill"));
return container;
}
//利用反射来创建监听到消息之后的执行方法
@Bean
MessageListenerAdapter listenerAdapter(RedisConsumer redisReceiver) {
return new MessageListenerAdapter(redisReceiver, "receiveMessage");
}
//使用默认的工厂初始化redis操作模板
@Bean
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
}

View File

@@ -0,0 +1,9 @@
package com.itstyle.seckill.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.itstyle.seckill.common.entity.Seckill;
public interface SeckillRepository extends JpaRepository<Seckill, Long> {
}

View File

@@ -0,0 +1,31 @@
package com.itstyle.seckill.service;
import com.itstyle.seckill.common.entity.Result;
public interface ISeckillDistributedService {
/**
* 秒杀 一 单个商品
* @param seckillId 秒杀商品ID
* @param userId 用户ID
* @return
*/
Result startSeckilRedisLock(long seckillId,long userId);
/**
* 秒杀 一 单个商品
* @param seckillId 秒杀商品ID
* @param userId 用户ID
* @return
*/
Result startSeckilZksLock(long seckillId,long userId);
/**
* 秒杀 二 多个商品
* @param seckillId 秒杀商品ID
* @param userId 用户ID
* @param number 秒杀商品数量
* @return
*/
Result startSeckilLock(long seckillId,long userId,long number);
}

View File

@@ -0,0 +1,73 @@
package com.itstyle.seckill.service;
import java.util.List;
import com.itstyle.seckill.common.entity.Result;
import com.itstyle.seckill.common.entity.Seckill;
public interface ISeckillService {
/**
* 查询全部的秒杀记录
* @return
*/
List<Seckill> getSeckillList();
/**
* 查询单个秒杀记录
* @param seckillId
* @return
*/
Seckill getById(long seckillId);
/**
* 查询秒杀售卖商品
* @param seckillId
* @return
*/
Long getSeckillCount(long seckillId);
/**
* 删除秒杀售卖商品记录
* @param seckillId
* @return
*/
void deleteSeckill(long seckillId);
/**
* 秒杀 一、会出现数量错误
* @param seckillId
* @param userId
* @return
*/
Result startSeckil(long seckillId,long userId);
/**
* 秒杀 二、程序锁
* @param seckillId
* @param userId
* @return
*/
Result startSeckilLock(long seckillId,long userId);
/**
* 秒杀 二、数据库悲观锁
* @param seckillId
* @param userId
* @return
*/
Result startSeckilDBPCC_ONE(long seckillId,long userId);
/**
* 秒杀 三、数据库悲观锁
* @param seckillId
* @param userId
* @return
*/
Result startSeckilDBPCC_TWO(long seckillId,long userId);
/**
* 秒杀 三、数据库悲观锁
* @param seckillId
* @param userId
* @return
*/
Result startSeckilDBOCC(long seckillId,long userId,long number);
}

View File

@@ -0,0 +1,114 @@
package com.itstyle.seckill.service.impl;
import java.sql.Timestamp;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.itstyle.seckill.common.dynamicquery.DynamicQuery;
import com.itstyle.seckill.common.entity.Result;
import com.itstyle.seckill.common.entity.SuccessKilled;
import com.itstyle.seckill.common.enums.SeckillStatEnum;
import com.itstyle.seckill.distributedlock.redis.RedissLockUtil;
import com.itstyle.seckill.distributedlock.zookeeper.ZkLockUtil;
import com.itstyle.seckill.service.ISeckillDistributedService;
@Service
public class SeckillDistributedServiceImpl implements ISeckillDistributedService {
@Autowired
private DynamicQuery dynamicQuery;
@Override
@Transactional
public Result startSeckilRedisLock(long seckillId,long userId) {
boolean res=false;
try {
//尝试获取锁最多等待10秒上锁以后10秒自动解锁实际项目中推荐这种以防出现死锁
res = RedissLockUtil.tryLock(seckillId+"", TimeUnit.SECONDS, 20, 10);
String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
Long number = ((Number) object).longValue();
if(number>0){
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
}else{
return Result.error(SeckillStatEnum.END);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
if(res){//释放锁
RedissLockUtil.unlock(seckillId+"");
}
}
return Result.ok(SeckillStatEnum.SUCCESS);
}
@Override
@Transactional
public Result startSeckilZksLock(long seckillId, long userId) {
try {
ZkLockUtil.acquire();
String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
Long number = ((Number) object).longValue();
if(number>0){
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
}else{
return Result.error(SeckillStatEnum.END);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
ZkLockUtil.release();
}
return Result.ok(SeckillStatEnum.SUCCESS);
}
@Override
public Result startSeckilLock(long seckillId, long userId, long number) {
boolean res=false;
try {
//尝试获取锁最多等待3秒上锁以后10秒自动解锁实际项目中推荐这种以防出现死锁
res = RedissLockUtil.tryLock(seckillId+"", TimeUnit.SECONDS, 3, 10);
String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
Long count = ((Number) object).longValue();
if(count>=number){
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
nativeSql = "UPDATE seckill SET number=number-? WHERE seckill_id=? AND number>0";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{number,seckillId});
}else{
return Result.error(SeckillStatEnum.END);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
if(res){//释放锁
RedissLockUtil.unlock(seckillId+"");
}
}
return Result.ok(SeckillStatEnum.SUCCESS);
}
}

View File

@@ -0,0 +1,173 @@
package com.itstyle.seckill.service.impl;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.itstyle.seckill.common.dynamicquery.DynamicQuery;
import com.itstyle.seckill.common.entity.Result;
import com.itstyle.seckill.common.entity.Seckill;
import com.itstyle.seckill.common.entity.SuccessKilled;
import com.itstyle.seckill.common.enums.SeckillStatEnum;
import com.itstyle.seckill.repository.SeckillRepository;
import com.itstyle.seckill.service.ISeckillService;
@Service
public class SeckillServiceImpl implements ISeckillService {
/**
* 思考为什么不用synchronized
* service 默认是单例的并发下lock只有一个实例
*/
private Lock lock = new ReentrantLock(true);//互斥锁 参数默认false不公平锁
@Autowired
private DynamicQuery dynamicQuery;
@Autowired
private SeckillRepository seckillRepository;
@Override
public List<Seckill> getSeckillList() {
return seckillRepository.findAll();
}
@Override
public Seckill getById(long seckillId) {
return seckillRepository.findOne(seckillId);
}
@Override
public Long getSeckillCount(long seckillId) {
String nativeSql = "SELECT count(*) FROM success_killed WHERE seckill_id=?";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
return ((Number) object).longValue();
}
@Override
@Transactional
public void deleteSeckill(long seckillId) {
String nativeSql = "DELETE FROM success_killed WHERE seckill_id=?";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
nativeSql = "UPDATE seckill SET number =100 WHERE seckill_id=?";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
}
@Override
@Transactional
public Result startSeckil(long seckillId,long userId) {
//校验库存
String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
Long number = ((Number) object).longValue();
if(number>0){
//扣库存
nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=?";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
//创建订单
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
//支付
return Result.ok(SeckillStatEnum.SUCCESS);
}else{
return Result.error(SeckillStatEnum.END);
}
}
@Override
@Transactional
public Result startSeckilLock(long seckillId, long userId) {
try {
lock.lock();
//这里、不清楚为啥、总是会被超卖101、难道锁不起作用、lock是同一个对象
String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
Long number = ((Number) object).longValue();
if(number>0){
nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=?";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState(Short.parseShort(number+""));
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
}else{
return Result.error(SeckillStatEnum.END);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
return Result.ok(SeckillStatEnum.SUCCESS);
}
@Override
@Transactional
public Result startSeckilDBPCC_ONE(long seckillId, long userId) {
//单用户抢购一件商品或者多件都没有问题
String nativeSql = "SELECT number FROM seckill WHERE seckill_id=? FOR UPDATE";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
Long number = ((Number) object).longValue();
if(number>0){
nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=?";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
return Result.ok(SeckillStatEnum.SUCCESS);
}else{
return Result.error(SeckillStatEnum.END);
}
}
@Override
@Transactional
public Result startSeckilDBPCC_TWO(long seckillId, long userId) {
//单用户抢购一件商品没有问题、但是抢购多件商品不建议这种写法
String nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0";//UPDATE锁表
int count = dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
if(count>0){
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
return Result.ok(SeckillStatEnum.SUCCESS);
}else{
return Result.error(SeckillStatEnum.END);
}
}
@Override
@Transactional
public Result startSeckilDBOCC(long seckillId, long userId, long number) {
Seckill kill = seckillRepository.findOne(seckillId);
if(kill.getNumber()>0){
//乐观锁
String nativeSql = "UPDATE seckill SET number=number-?,version=version+1 WHERE seckill_id=? AND version = ?";
int count = dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{number,seckillId,kill.getVersion()});
if(count>0){
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
return Result.ok(SeckillStatEnum.SUCCESS);
}else{
return Result.error(SeckillStatEnum.END);
}
}else{
return Result.error(SeckillStatEnum.END);
}
}
}

View File

@@ -0,0 +1,203 @@
package com.itstyle.seckill.web;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.itstyle.seckill.common.entity.Result;
import com.itstyle.seckill.common.entity.SuccessKilled;
import com.itstyle.seckill.queue.jvm.SeckillQueue;
import com.itstyle.seckill.service.ISeckillService;
@Api(tags ="秒杀")
@RestController
@RequestMapping("/seckill")
public class SeckillController {
private final static Logger LOGGER = LoggerFactory.getLogger(SeckillController.class);
private static int corePoolSize = Runtime.getRuntime().availableProcessors();
//调整队列数 拒绝服务
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(1000));
@Autowired
private ISeckillService seckillService;
@ApiOperation(value="秒杀一(最low实现)",nickname="科帮网")
@PostMapping("/start")
public Result start(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀一(会出现超卖)");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
Result result = seckillService.startSeckil(killId, userId);
LOGGER.info("用户:{}{}",userId,result.get("msg"));
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀二(程序锁)",nickname="科帮网")
@PostMapping("/startLock")
public Result startLock(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀二(正常)");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
Result result = seckillService.startSeckilLock(killId, userId);
LOGGER.info("用户:{}{}",userId,result.get("msg"));
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀三(数据库悲观锁)",nickname="科帮网")
@PostMapping("/startDBPCC_ONE")
public Result startDBPCC_ONE(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀三(正常)");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
Result result = seckillService.startSeckilDBPCC_ONE(killId, userId);
LOGGER.info("用户:{}{}",userId,result.get("msg"));
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀四(数据库悲观锁)",nickname="科帮网")
@PostMapping("/startDPCC_TWO")
public Result startDPCC_TWO(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀四(正常、数据库锁最优实现)");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
Result result = seckillService.startSeckilDBPCC_TWO(killId, userId);
LOGGER.info("用户:{}{}",userId,result.get("msg"));
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀五(数据库乐观锁)",nickname="科帮网")
@PostMapping("/startDBOCC")
public Result startDBOCC(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀五(正常、数据库锁最优实现)");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
Result result = seckillService.startSeckilDBOCC(killId, userId,4);
LOGGER.info("用户:{}{}",userId,result.get("msg"));
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀六(进程内队列)",nickname="科帮网")
@PostMapping("/startQueue")
public Result startQueue(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀六(正常)");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
SuccessKilled kill = new SuccessKilled();
kill.setSeckillId(killId);
kill.setUserId(userId);
try {
Boolean flag = SeckillQueue.getMailQueue().produce(kill);
if(flag){
LOGGER.info("用户:{}{}",kill.getUserId(),"秒杀成功");
}else{
LOGGER.info("用户:{}{}",userId,"秒杀失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
LOGGER.info("用户:{}{}",userId,"秒杀失败");
}
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
}

View File

@@ -0,0 +1,145 @@
package com.itstyle.seckill.web;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.itstyle.seckill.common.entity.Result;
import com.itstyle.seckill.queue.kafka.KafkaSender;
import com.itstyle.seckill.queue.redis.RedisSender;
import com.itstyle.seckill.service.ISeckillDistributedService;
import com.itstyle.seckill.service.ISeckillService;
@Api(tags ="分布式秒杀")
@RestController
@RequestMapping("/seckillDistributed")
public class SeckillDistributedController {
private final static Logger LOGGER = LoggerFactory.getLogger(SeckillDistributedController.class);
private static int corePoolSize = Runtime.getRuntime().availableProcessors();
//调整队列数 拒绝服务
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(10000));
@Autowired
private ISeckillService seckillService;
@Autowired
private ISeckillDistributedService seckillDistributedService;
@Autowired
private RedisSender redisSender;
@Autowired
private KafkaSender kafkaSender;
@ApiOperation(value="秒杀一(Rediss分布式锁)",nickname="科帮网")
@PostMapping("/startRedisLock")
public Result startRedisLock(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀一");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
Result result = seckillDistributedService.startSeckilRedisLock(killId, userId);
LOGGER.info("用户:{}{}",userId,result.get("msg"));
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀二(zookeeper分布式锁)",nickname="科帮网")
@PostMapping("/startZkLock")
public Result startZkLock(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀二");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
Result result = seckillDistributedService.startSeckilZksLock(killId, userId);
LOGGER.info("用户:{}{}",userId,result.get("msg"));
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀三(Redis分布式队列-订阅监听)",nickname="科帮网")
@PostMapping("/startRedisQueue")
public Result startRedisQueue(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀三");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
//思考如何返回给用户信息ws
redisSender.sendChannelMess("seckill",killId+";"+userId);
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
@ApiOperation(value="秒杀四(Kafka分布式队列)",nickname="科帮网")
@PostMapping("/startKafkaQueue")
public Result startKafkaQueue(long seckillId){
seckillService.deleteSeckill(seckillId);
final long killId = seckillId;
LOGGER.info("开始秒杀四");
for(int i=0;i<1000;i++){
final long userId = i;
Runnable task = new Runnable() {
@Override
public void run() {
//思考如何返回给用户信息ws
kafkaSender.sendChannelMess("seckill",killId+";"+userId);
}
};
executor.execute(task);
}
try {
Thread.sleep(10000);
Long seckillCount = seckillService.getSeckillCount(seckillId);
LOGGER.info("一共秒杀出{}件商品",seckillCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.ok();
}
}

View File

@@ -0,0 +1,82 @@
# \u9879\u76eecontextPath \u79d1\u5e2e\u7f51https://blog.52itstyle.com
server.context-path=/seckill
# \u670d\u52a1\u7aef\u53e3
server.port=8080
# session\u6700\u5927\u8d85\u65f6\u65f6\u95f4(\u5206\u949f)\uff0c\u9ed8\u8ba4\u4e3a30
server.session-timeout=60
# tomcat\u6700\u5927\u7ebf\u7a0b\u6570\uff0c\u9ed8\u8ba4\u4e3a200
server.tomcat.max-threads=100
# tomcat\u7684URI\u7f16\u7801
server.tomcat.uri-encoding=UTF-8
#spring boot\u4ece\u63a7\u5236\u53f0\u6253\u5370\u51fa\u6765\u7684\u65e5\u5fd7\u7ea7\u522b\u53ea\u6709ERROR, WARN \u8fd8\u6709INFO\uff0c\u5982\u679c\u4f60\u60f3\u8981\u6253\u5370debug\u7ea7\u522b\u7684\u65e5\u5fd7
#debug=true
logging.level.root=INFO
spring.thymeleaf.mode=LEGACYHTML5
#dev tools
spring.devtools.livereload.enabled=true
spring.thymeleaf.cache=false
spring.thymeleaf.cache-period=0
spring.thymeleaf.template.cache=false
# \u9759\u6001\u6587\u4ef6\u8bf7\u6c42\u5339\u914d\u65b9\u5f0f
spring.mvc.static-path-pattern=/**
#\u6ce8\u610f\u4e2d\u6587\u4e71\u7801
spring.datasource.url=jdbc:mysql://localhost:3306/seckill?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = false
# DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Default to "create-drop" when using an embedded database, "none" otherwise.
spring.jpa.hibernate.ddl-auto = update
# Hibernate 4 naming strategy fully qualified name. Not supported with Hibernate 5.
spring.jpa.hibernate.naming.strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
# Redis
# \u6570\u636e\u5e93\u7d22\u5f15\uff08\u9ed8\u8ba4\u4e3a0\uff09
spring.redis.database=0
# \u670d\u52a1\u5668\u5730\u5740 \u53d8\u66f4\u4e3a\u81ea\u5df1\u7684
spring.redis.host=192.168.1.180
# \u670d\u52a1\u5668\u8fde\u63a5\u7aef\u53e3
spring.redis.port=6379
# \u670d\u52a1\u5668\u8fde\u63a5\u5bc6\u7801\uff08\u9ed8\u8ba4\u4e3a\u7a7a\uff09\u5982\u679c\u6709\u53d8\u66f4\u4e3a\u81ea\u5df1\u7684
spring.redis.password=6347097
# \u8fde\u63a5\u6c60\u6700\u5927\u8fde\u63a5\u6570\uff08\u4f7f\u7528\u8d1f\u503c\u8868\u793a\u6ca1\u6709\u9650\u5236\uff09
spring.redis.pool.max-active=8
# \u8fde\u63a5\u6c60\u6700\u5927\u963b\u585e\u7b49\u5f85\u65f6\u95f4\uff08\u4f7f\u7528\u8d1f\u503c\u8868\u793a\u6ca1\u6709\u9650\u5236\uff09
spring.redis.pool.max-wait=-1
# \u8fde\u63a5\u6c60\u4e2d\u7684\u6700\u5927\u7a7a\u95f2\u8fde\u63a5
spring.redis.pool.max-idle=8
# \u8fde\u63a5\u6c60\u4e2d\u7684\u6700\u5c0f\u7a7a\u95f2\u8fde\u63a5
spring.redis.pool.min-idle=0
# \u8fde\u63a5\u8d85\u65f6\u65f6\u95f4\uff08\u6beb\u79d2\uff09
spring.redis.timeout=30000
# redisson lock
redisson.address=redis://192.168.1.180:6379
redisson.password=6347097
#kafka\u76f8\u5173\u914d\u7f6e
spring.kafka.bootstrap-servers=192.168.1.180:9092
#\u8bbe\u7f6e\u4e00\u4e2a\u9ed8\u8ba4\u7ec4
spring.kafka.consumer.group-id=0
#key-value\u5e8f\u5217\u5316\u53cd\u5e8f\u5217\u5316
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
#\u6bcf\u6b21\u6279\u91cf\u53d1\u9001\u6d88\u606f\u7684\u6570\u91cf
spring.kafka.producer.batch-size=65536
spring.kafka.producer.buffer-memory=524288
#zookeeper.address
zookeeper.address = 192.168.1.180:2181

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan 配置文件如果发生改变,将会被重新加载 scanPeriod 检测间隔时间 BY 科帮网 小柒2012 欢迎关注博客https://blog.52itstyle.com-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>spring-boot-seckill</contextName>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<!-- 普通日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>log/spring-boot-seckill-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志命名:单个文件大于128MB 按照时间+自增i 生成log文件 -->
<fileNamePattern>log/spring-boot-seckill-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>128MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 最大保存时间30天-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 错误日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>log/spring-boot-seckill-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志命名:单个文件大于2MB 按照时间+自增i 生成log文件 -->
<fileNamePattern>log/spring-boot-seckill-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 最大保存时间180天-->
<maxHistory>180</maxHistory>
</rollingPolicy>
<append>true</append>
<!-- 日志格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!-- 日志级别过滤器 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 控制台 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志格式 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--此日志appender是为开发使用只配置最底级别控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<!-- additivity 避免执行2次 -->
<logger name="com.itstyle" level="INFO" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</configuration>

View File

@@ -0,0 +1,55 @@
/*
SQLyog 企业版 - MySQL GUI v8.14
MySQL - 5.7.17-log : Database - seckill
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Table structure for table `seckill` */
DROP TABLE IF EXISTS `seckill`;
CREATE TABLE `seckill` (
`seckill_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品库存id',
`name` varchar(120) NOT NULL COMMENT '商品名称',
`number` int(11) NOT NULL COMMENT '库存数量',
`start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒杀开启时间',
`end_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒杀结束时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`version` int(11) NOT NULL COMMENT '版本号',
PRIMARY KEY (`seckill_id`),
KEY `idx_start_time` (`start_time`),
KEY `idx_end_time` (`end_time`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=1004 DEFAULT CHARSET=utf8 COMMENT='秒杀库存表';
/*Data for the table `seckill` */
insert into `seckill`(`seckill_id`,`name`,`number`,`start_time`,`end_time`,`create_time`,`version`) values (1000,'1000元秒杀iphone8',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0),(1001,'500元秒杀ipad2',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0),(1002,'300元秒杀小米4',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0),(1003,'200元秒杀红米note',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0);
/*Table structure for table `success_killed` */
DROP TABLE IF EXISTS `success_killed`;
CREATE TABLE `success_killed` (
`seckill_id` bigint(20) NOT NULL COMMENT '秒杀商品id',
`user_id` bigint(20) NOT NULL COMMENT '用户Id',
`state` tinyint(4) NOT NULL COMMENT '状态标示:-1指无效0指成功1指已付款',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`seckill_id`,`user_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表';
/*Data for the table `success_killed` */
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;

View File

@@ -0,0 +1,7 @@
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>

View File

@@ -0,0 +1,5 @@
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

View File

@@ -0,0 +1,82 @@
# \u9879\u76eecontextPath \u79d1\u5e2e\u7f51https://blog.52itstyle.com
server.context-path=/seckill
# \u670d\u52a1\u7aef\u53e3
server.port=8080
# session\u6700\u5927\u8d85\u65f6\u65f6\u95f4(\u5206\u949f)\uff0c\u9ed8\u8ba4\u4e3a30
server.session-timeout=60
# tomcat\u6700\u5927\u7ebf\u7a0b\u6570\uff0c\u9ed8\u8ba4\u4e3a200
server.tomcat.max-threads=100
# tomcat\u7684URI\u7f16\u7801
server.tomcat.uri-encoding=UTF-8
#spring boot\u4ece\u63a7\u5236\u53f0\u6253\u5370\u51fa\u6765\u7684\u65e5\u5fd7\u7ea7\u522b\u53ea\u6709ERROR, WARN \u8fd8\u6709INFO\uff0c\u5982\u679c\u4f60\u60f3\u8981\u6253\u5370debug\u7ea7\u522b\u7684\u65e5\u5fd7
#debug=true
logging.level.root=INFO
spring.thymeleaf.mode=LEGACYHTML5
#dev tools
spring.devtools.livereload.enabled=true
spring.thymeleaf.cache=false
spring.thymeleaf.cache-period=0
spring.thymeleaf.template.cache=false
# \u9759\u6001\u6587\u4ef6\u8bf7\u6c42\u5339\u914d\u65b9\u5f0f
spring.mvc.static-path-pattern=/**
#\u6ce8\u610f\u4e2d\u6587\u4e71\u7801
spring.datasource.url=jdbc:mysql://localhost:3306/seckill?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = false
# DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Default to "create-drop" when using an embedded database, "none" otherwise.
spring.jpa.hibernate.ddl-auto = update
# Hibernate 4 naming strategy fully qualified name. Not supported with Hibernate 5.
spring.jpa.hibernate.naming.strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
# Redis
# \u6570\u636e\u5e93\u7d22\u5f15\uff08\u9ed8\u8ba4\u4e3a0\uff09
spring.redis.database=0
# \u670d\u52a1\u5668\u5730\u5740 \u53d8\u66f4\u4e3a\u81ea\u5df1\u7684
spring.redis.host=192.168.1.180
# \u670d\u52a1\u5668\u8fde\u63a5\u7aef\u53e3
spring.redis.port=6379
# \u670d\u52a1\u5668\u8fde\u63a5\u5bc6\u7801\uff08\u9ed8\u8ba4\u4e3a\u7a7a\uff09\u5982\u679c\u6709\u53d8\u66f4\u4e3a\u81ea\u5df1\u7684
spring.redis.password=6347097
# \u8fde\u63a5\u6c60\u6700\u5927\u8fde\u63a5\u6570\uff08\u4f7f\u7528\u8d1f\u503c\u8868\u793a\u6ca1\u6709\u9650\u5236\uff09
spring.redis.pool.max-active=8
# \u8fde\u63a5\u6c60\u6700\u5927\u963b\u585e\u7b49\u5f85\u65f6\u95f4\uff08\u4f7f\u7528\u8d1f\u503c\u8868\u793a\u6ca1\u6709\u9650\u5236\uff09
spring.redis.pool.max-wait=-1
# \u8fde\u63a5\u6c60\u4e2d\u7684\u6700\u5927\u7a7a\u95f2\u8fde\u63a5
spring.redis.pool.max-idle=8
# \u8fde\u63a5\u6c60\u4e2d\u7684\u6700\u5c0f\u7a7a\u95f2\u8fde\u63a5
spring.redis.pool.min-idle=0
# \u8fde\u63a5\u8d85\u65f6\u65f6\u95f4\uff08\u6beb\u79d2\uff09
spring.redis.timeout=30000
# redisson lock
redisson.address=redis://192.168.1.180:6379
redisson.password=6347097
#kafka\u76f8\u5173\u914d\u7f6e
spring.kafka.bootstrap-servers=192.168.1.180:9092
#\u8bbe\u7f6e\u4e00\u4e2a\u9ed8\u8ba4\u7ec4
spring.kafka.consumer.group-id=0
#key-value\u5e8f\u5217\u5316\u53cd\u5e8f\u5217\u5316
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
#\u6bcf\u6b21\u6279\u91cf\u53d1\u9001\u6d88\u606f\u7684\u6570\u91cf
spring.kafka.producer.batch-size=65536
spring.kafka.producer.buffer-memory=524288
#zookeeper.address
zookeeper.address = 192.168.1.180:2181

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan 配置文件如果发生改变,将会被重新加载 scanPeriod 检测间隔时间 BY 科帮网 小柒2012 欢迎关注博客https://blog.52itstyle.com-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>spring-boot-seckill</contextName>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<!-- 普通日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>log/spring-boot-seckill-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志命名:单个文件大于128MB 按照时间+自增i 生成log文件 -->
<fileNamePattern>log/spring-boot-seckill-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>128MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 最大保存时间30天-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 错误日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>log/spring-boot-seckill-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志命名:单个文件大于2MB 按照时间+自增i 生成log文件 -->
<fileNamePattern>log/spring-boot-seckill-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 最大保存时间180天-->
<maxHistory>180</maxHistory>
</rollingPolicy>
<append>true</append>
<!-- 日志格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!-- 日志级别过滤器 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 控制台 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志格式 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--此日志appender是为开发使用只配置最底级别控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<!-- additivity 避免执行2次 -->
<logger name="com.itstyle" level="INFO" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</configuration>

View File

@@ -0,0 +1,55 @@
/*
SQLyog 企业版 - MySQL GUI v8.14
MySQL - 5.7.17-log : Database - seckill
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Table structure for table `seckill` */
DROP TABLE IF EXISTS `seckill`;
CREATE TABLE `seckill` (
`seckill_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品库存id',
`name` varchar(120) NOT NULL COMMENT '商品名称',
`number` int(11) NOT NULL COMMENT '库存数量',
`start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒杀开启时间',
`end_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒杀结束时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`version` int(11) NOT NULL COMMENT '版本号',
PRIMARY KEY (`seckill_id`),
KEY `idx_start_time` (`start_time`),
KEY `idx_end_time` (`end_time`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=1004 DEFAULT CHARSET=utf8 COMMENT='秒杀库存表';
/*Data for the table `seckill` */
insert into `seckill`(`seckill_id`,`name`,`number`,`start_time`,`end_time`,`create_time`,`version`) values (1000,'1000元秒杀iphone8',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0),(1001,'500元秒杀ipad2',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0),(1002,'300元秒杀小米4',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0),(1003,'200元秒杀红米note',100,'2018-05-10 15:31:53','2018-05-10 15:31:53','2018-05-10 15:31:53',0);
/*Table structure for table `success_killed` */
DROP TABLE IF EXISTS `success_killed`;
CREATE TABLE `success_killed` (
`seckill_id` bigint(20) NOT NULL COMMENT '秒杀商品id',
`user_id` bigint(20) NOT NULL COMMENT '用户Id',
`state` tinyint(4) NOT NULL COMMENT '状态标示:-1指无效0指成功1指已付款',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`seckill_id`,`user_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表';
/*Data for the table `success_killed` */
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;