zoukankan      html  css  js  c++  java
  • (一) Java 秒杀系统方案优化-环境搭建

    Java 秒杀系统方案优化

    一、项目介绍

    目标

    使用浏览器缓存/Nginx缓存/ 页面缓存/ 对象缓存/RabbitMQ队列异步下单,减少网络流量,减轻数据库压力,全面提升系统并发处理能力。

    Java 微服务框架

    SpringBoot/ RabbitMQ /Redis/ MySQL

    安全策略

    图形验证码、限流防刷、接口地址隐藏,多种安全机制拒绝机器刷单

    1.2 可以学习到

    应对大并发

    • 如何利用缓存
    • 如何使用异步

    SpringBoot 环境搭建

    • 集成 Thymeleaf,Result 结果封装

    • 集成 Mybatis+Druid

    • 集成 Jedis+Redis 安装+通用缓存 Key 封装

    实现登录功能

    • 数据库设计
    • 明文密码两次 MD5 处理
    • JSR303 参数检验+全局异常处理器
    • 分布式 Session

    实现秒杀功能

    • 数据库设计
    • 商品列表页
    • 商品详情页
    • 订单详情页

    JMeter 压测

    • JMeter 入门
    • 自定义变量模拟多用户
    • JMeter 命令行使用
    • Spring Boot 打 war 包

    页面优化技术

    • 页面缓存+URL缓存+对象缓存
    • 页面静态化,前后端分离
    • 静态资源优化
    • CDN 优化

    接口优化

    • Redis 预减库存减少数据库访问
    • 内存标记减少 Redis 访问
    • RabbitMQ 队列缓冲,异步下单,增强用户体验
    • RabbitMQ 安装与 Spring Boot 集成
    • 访问 Nginx 水平扩展
    • 压测

    安全优化

    • 秒杀接口地址隐藏
    • 数学公式验证码
    • 接口防刷

    二、SpringBoot 环境搭建

    2.1 创建工程项目,导入对应坐标

    <?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.gx</groupId>
    	<artifactId>seckill</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>seckill</name>
    	<description>Seckill project for Spring Boot</description>
    	<properties>
    		<java.version>1.8</java.version>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	</properties>
    	<dependencies>
    		<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>
    	</dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    </project>
    

    2.2 封装结果类

    2.2.1 封装状态码与信息类 CodeMsg

    package com.gx.seckill.result;
    
    public class CodeMsg {
    
        private int code;
        private String msg;
    
        //通用的错误码
        public static CodeMsg SUCCESS = new CodeMsg(0, "success");
        public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常");
        public static CodeMsg BIND_ERROR = new CodeMsg(500101, "参数校验异常:%s");
        public static CodeMsg REQUEST_ILLEGAL = new CodeMsg(500102, "请求非法");
        public static CodeMsg ACCESS_LIMIT_REACHED= new CodeMsg(500104, "访问太频繁!");
    
        //登录模块 5002XX
        public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效");
        public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空");
        public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空");
        public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误");
        public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在");
        public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误");
    
        //商品模块 5003XX
    
        //订单模块 5004XX
        public static CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在");
    
        //秒杀模块 5005XX
        public static CodeMsg MIAO_SHA_OVER = new CodeMsg(500500, "商品已经秒杀完毕");
        public static CodeMsg REPEATE_MIAOSHA = new CodeMsg(500501, "不能重复秒杀");
        public static CodeMsg MIAOSHA_FAIL = new CodeMsg(500502, "秒杀失败");
    
        private CodeMsg( ) {
        }
    
        /**
         * 私有构造函数
         * @param code 状态码
         * @param msg 信息
         */
        private CodeMsg( int code,String msg ) {
            this.code = code;
            this.msg = msg;
        }
    
        public int getCode() {
            return code;
        }
    //    public void setCode(int code) {
    //        this.code = code;
    //    }
        public String getMsg() {
            return msg;
        }
    //    public void setMsg(String msg) {
    //        this.msg = msg;
    //    }
    
        public CodeMsg fillArgs(Object... args) {
            int code = this.code;
            String message = String.format(this.msg, args);
            return new CodeMsg(code, message);
        }
    
        @Override
        public String toString() {
            return "CodeMsg [code=" + code + ", msg=" + msg + "]";
        }
    }
    

    2.2.2 封装返回结果类 Result<T>

    package com.gx.seckill.result;
    
    public class Result<T> {
        private int code;
        private String msg;
        private T data;
    
        /**
         * 成功时调用
         */
        public static <T> Result<T> success(T data  ){
            return new Result<T>(data);
        }
    
        /**
         * 失败时候的调用
         */
        public static <T> Result<T> error(CodeMsg codeMsg){
            return new Result<T>(codeMsg);
        }
    
        /**
         * 构造函数要私有,不希望其他类可以创建 Result 对象
         * @param data
         */
        private Result(T data) {
            this.code = 0;
            this.msg = "success";
            this.data = data;
        }
    
        private Result(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        private Result(CodeMsg codeMsg) {
            if(codeMsg != null) {
                this.code = codeMsg.getCode();
                this.msg = codeMsg.getMsg();
            }
        }
    
        public int getCode() {
            return code;
        }
        public String getMsg() {
            return msg;
        }
        public T getData() {
            return data;
        }
    }
    

    2.3 集成 Thymeleaf

    1)导入依赖坐标

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

    2)添加配置信息

    application.properties 中添加配置信息

    #thymeleaf
    spring.thymeleaf.prefix=classpath:/templates/
    spring.thymeleaf.suffix=.html
    spring.thymeleaf.cache=false
    spring.thymeleaf.servlet.content-type=text/html
    spring.thymeleaf.enabled=true
    spring.thymeleaf.encoding=UTF-8
    spring.thymeleaf.mode=HTML5
    

    3)Controller 中验证

    package com.gx.seckill.controller;
    
    import com.gx.seckill.result.CodeMsg;
    import com.gx.seckill.result.Result;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    @RequestMapping("/demo")
    public class DemoController {
    
        @RequestMapping("/")
        @ResponseBody
        String home() {
            return "Hello World!";
        }
    
        //1.rest api json 输出 2.页面
        @RequestMapping("/hello")
        @ResponseBody
        public Result<String> hello() {
            return Result.success("hello,imooc");
        }
    
        @RequestMapping("/helloError")
        @ResponseBody
        public Result<String> helloError() {
            return Result.error(CodeMsg.SERVER_ERROR);
        }
    
        @RequestMapping("/thymeleaf")
        public String  thymeleaf(Model model) {
            model.addAttribute("name", "Joshua");
            return "hello";
        }
    }
    

    2.4 集成 Mybatis

    1)导入依赖坐标

    <dependency>
    	<groupId>org.mybatis.spring.boot</groupId>
    	<artifactId>mybatis-spring-boot-starter</artifactId>
    	<version>1.3.1</version>
    </dependency>
    
    <dependency>
    	<groupId>mysql</groupId>
    	<artifactId>mysql-connector-java</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>druid</artifactId>
    	<version>1.1.10</version>
    </dependency>
    

    2)添加配置信息

    application.properties 中添加配置信息

    # mybatis
    mybatis.type-aliases-package=com.gx.seckill.domain
    mybatis.configuration.map-underscore-to-camel-case=true
    mybatis.configuration.default-fetch-size=100
    mybatis.configuration.default-statement-timeout=3000
    mybatis.mapperLocations = classpath:com/gx/seckill/dao/*.xml
    # druid
    spring.datasource.url=jdbc:mysql://192.168.56.10:3306/seckill?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    spring.datasource.username=root
    spring.datasource.password=root
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.filters=stat
    spring.datasource.maxActive=1000
    spring.datasource.initialSize=100
    spring.datasource.maxWait=60000
    spring.datasource.minIdle=500
    spring.datasource.timeBetweenEvictionRunsMillis=60000
    spring.datasource.minEvictableIdleTimeMillis=300000
    spring.datasource.validationQuery=select 'x'
    spring.datasource.testWhileIdle=true
    spring.datasource.testOnBorrow=false
    spring.datasource.testOnReturn=false
    spring.datasource.poolPreparedStatements=true
    spring.datasource.maxOpenPreparedStatements=20
    

    3)测试

    3.1)创建数据库

    创建数据库 scekill,并创建 user

    3.2)创建 user 表对应的实体类
    package com.gx.seckill.domain;
    
    public class User {
    	private int id;
    	private String name;
    	public int getId() {
    		return id;
    	}
    	public void setId(int id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    
    3.3)创建 UserDao 查询数据库
    package com.gx.seckill.dao;
    
    import com.gx.seckill.domain.User;
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    
    @Mapper
    public interface UserDao {
    	
    	@Select("select * from user where id = #{id}")
    	public User getById(@Param("id") int id);
    
    	@Insert("insert into user(id, name)values(#{id}, #{name})")
    	public int insert(User user);	
    }
    
    3.4)创建 UserService 用户通过 dao 查询数据库的类
    package com.gx.seckill.service;
    
    import com.gx.seckill.dao.UserDao;
    import com.gx.seckill.domain.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    public class UserService {
    	
    	@Autowired
    	UserDao userDao;
    	
    	public User getById(int id) {
    		 return userDao.getById(id);
    	}
    
    	@Transactional
    	public boolean tx() {
    		User u1= new User();
    		u1.setId(2);
    		u1.setName("2222");
    		userDao.insert(u1);
    		
    		User u2= new User();
    		u2.setId(1);
    		u2.setName("11111");
    		userDao.insert(u2);
    		
    		return true;
    	}
    }
    
    3.5)Controller 中测试
    package com.gx.seckill.controller;
    
    
    import com.gx.seckill.domain.User;
    import com.gx.seckill.result.CodeMsg;
    import com.gx.seckill.result.Result;
    import com.gx.seckill.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    @RequestMapping("/demo")
    public class DemoController {
    
        @Autowired
        UserService userService;
    
        @RequestMapping("/db/get")
        @ResponseBody
        public Result<User> dbGet() {
            User user = userService.getById(1);
            return Result.success(user);
        }
    
        @RequestMapping("/db/tx")
        @ResponseBody
        public Result<Boolean> dbTx() {
            userService.tx();
            return Result.success(true);
        }
    }
    

    2.5 集成 Redis

    1)导入依赖坐标

    <dependency>
    	<groupId>redis.clients</groupId>
    	<artifactId>jedis</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>fastjson</artifactId>
    	<version>1.2.38</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-configuration-processor</artifactId>
    	<optional>true</optional>
    </dependency>
    

    2)application.properties

    添加配置信息

    #redis
    redis.host=192.168.56.10
    redis.port=6379
    redis.timeout=10
    #redis.password=123456
    redis.poolMaxTotal=1000
    redis.poolMaxIdle=500
    redis.poolMaxWait=500
    

    3)创建 RedisConfig 类

    用于读取 application.properties 中 redis 的配置信息

    package com.gx.seckill.redis;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @Component
    @ConfigurationProperties(prefix="redis")
    public class RedisConfig {
    	private String host;
    	private int port;
    	private int timeout;//秒
    	private String password;
    	private int poolMaxTotal;
    	private int poolMaxIdle;
    	private int poolMaxWait;//秒
    	public String getHost() {
    		return host;
    	}
    	public void setHost(String host) {
    		this.host = host;
    	}
    	public int getPort() {
    		return port;
    	}
    	public void setPort(int port) {
    		this.port = port;
    	}
    	public int getTimeout() {
    		return timeout;
    	}
    	public void setTimeout(int timeout) {
    		this.timeout = timeout;
    	}
    	public String getPassword() {
    		return password;
    	}
    	public void setPassword(String password) {
    		this.password = password;
    	}
    	public int getPoolMaxTotal() {
    		return poolMaxTotal;
    	}
    	public void setPoolMaxTotal(int poolMaxTotal) {
    		this.poolMaxTotal = poolMaxTotal;
    	}
    	public int getPoolMaxIdle() {
    		return poolMaxIdle;
    	}
    	public void setPoolMaxIdle(int poolMaxIdle) {
    		this.poolMaxIdle = poolMaxIdle;
    	}
    	public int getPoolMaxWait() {
    		return poolMaxWait;
    	}
    	public void setPoolMaxWait(int poolMaxWait) {
    		this.poolMaxWait = poolMaxWait;
    	}
    }
    

    4)创建 RedisPoolFactory 类

    用于创建 JedisPool 对象

    package com.gx.seckill.redis;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Service;
    
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    @Service
    public class RedisPoolFactory {
    
    	@Autowired
    	RedisConfig redisConfig;
    	
    	@Bean
    	public JedisPool JedisPoolFactory() {
    		JedisPoolConfig poolConfig = new JedisPoolConfig();
    		poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
    		poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
    		//unit: ms
    		poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000);
    		JedisPool jp = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
    				redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0);
    		return jp;
    	}
    }
    

    5)通用缓存 Key 封装

    作用:为缓存中 key 添加前缀

    设计:接口<-抽象类<-实现类

    5.1)创建 KeyPrefix 接口
    package com.gx.seckill.redis;
    
    public interface KeyPrefix {		
    	public int expireSeconds();
    	public String getPrefix();
    }
    
    5.2)创建 BasePrefix 抽象类
    package com.gx.seckill.redis;
    
    public abstract class BasePrefix implements KeyPrefix{
    	
    	private int expireSeconds;
    	
    	private String prefix;
    	
    	public BasePrefix(String prefix) {//0代表永不过期
    		this(0, prefix);
    	}
    	
    	public BasePrefix( int expireSeconds, String prefix) {
    		this.expireSeconds = expireSeconds;
    		this.prefix = prefix;
    	}
    
    	@Override
    	public int expireSeconds() {//默认0代表永不过期
    		return expireSeconds;
    	}
    
    	@Override
    	public String getPrefix() {
    		String className = getClass().getSimpleName();
    		return className+":" + prefix;
    	}
    }
    
    5.3)UserKey
    package com.gx.seckill.redis;
    
    public class UserKey extends BasePrefix{
    	private UserKey(String prefix) {
    		super(prefix);
    	}
    	public static UserKey getById = new UserKey("id");
    	public static UserKey getByName = new UserKey("name");
    }
    
    5.4)OrderKey
    package com.gx.seckill.redis;
    
    public class OrderKey extends BasePrefix {
    
    	public OrderKey(String prefix) {
    		super(prefix);
    	}
    	public static OrderKey getMiaoshaOrderByUidGid = new OrderKey("moug");
    }
    
    5.5)GoodsKey
    package com.gx.seckill.redis;
    
    public class GoodsKey extends BasePrefix{
    
    	private GoodsKey(int expireSeconds, String prefix) {
    		super(expireSeconds, prefix);
    	}
    	public static GoodsKey getGoodsList = new GoodsKey(60, "gl");
    	public static GoodsKey getGoodsDetail = new GoodsKey(60, "gd");
    	public static GoodsKey getMiaoshaGoodsStock= new GoodsKey(0, "gs");
    }
    
    5.6)AccessKey
    package com.gx.seckill.redis;
    
    public class AccessKey extends BasePrefix{
    
    	private AccessKey( int expireSeconds, String prefix) {
    		super(expireSeconds, prefix);
    	}
    	
    	public static AccessKey withExpire(int expireSeconds) {
    		return new AccessKey(expireSeconds, "access");
    	}
    	
    }
    
    5.7)MiaoshaKey
    package com.gx.seckill.redis;
    
    public class MiaoshaKey extends BasePrefix{
    
    	private MiaoshaKey( int expireSeconds, String prefix) {
    		super(expireSeconds, prefix);
    	}
    	public static MiaoshaKey isGoodsOver = new MiaoshaKey(0, "go");
    	public static MiaoshaKey getMiaoshaPath = new MiaoshaKey(60, "mp");
    	public static MiaoshaKey getMiaoshaVerifyCode = new MiaoshaKey(300, "vc");
    }
    
    5.7)MiaoshaUserKey
    package com.gx.seckill.redis;
    
    public class MiaoshaUserKey extends BasePrefix{
    
    	public static final int TOKEN_EXPIRE = 3600*24 * 2;
    	private MiaoshaUserKey(int expireSeconds, String prefix) {
    		super(expireSeconds, prefix);
    	}
    	public static MiaoshaUserKey token = new MiaoshaUserKey(TOKEN_EXPIRE, "tk");
    	public static MiaoshaUserKey getById = new MiaoshaUserKey(0, "id");
    }
    

    6)RedisService

    实现 Redis 的具体操作

    package com.gx.seckill.redis;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.alibaba.fastjson.JSON;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.ScanParams;
    import redis.clients.jedis.ScanResult;
    
    @Service
    public class RedisService {
    	
    	@Autowired
    	JedisPool jedisPool;
    	
    	/**
    	 * 获取当个对象
    	 * */
    	public <T> T get(KeyPrefix prefix, String key,  Class<T> clazz) {
    		 Jedis jedis = null;
    		 try {
    			 jedis =  jedisPool.getResource();
    			 //生成真正的key
    			 String realKey  = prefix.getPrefix() + key;
    			 String  str = jedis.get(realKey);
    			 T t =  stringToBean(str, clazz);
    			 return t;
    		 }finally {
    			  returnToPool(jedis);
    		 }
    	}
    	
    	/**
    	 * 设置对象
    	 * */
    	public <T> boolean set(KeyPrefix prefix, String key,  T value) {
    		 Jedis jedis = null;
    		 try {
    			 jedis =  jedisPool.getResource();
    			 String str = beanToString(value);
    			 if(str == null || str.length() <= 0) {
    				 return false;
    			 }
    			//生成真正的key
    			 String realKey  = prefix.getPrefix() + key;
    			 int seconds =  prefix.expireSeconds();
    			 if(seconds <= 0) {
    				 jedis.set(realKey, str);
    			 }else {
    				 jedis.setex(realKey, seconds, str);
    			 }
    			 return true;
    		 }finally {
    			  returnToPool(jedis);
    		 }
    	}
    	
    	/**
    	 * 判断key是否存在
    	 * */
    	public <T> boolean exists(KeyPrefix prefix, String key) {
    		 Jedis jedis = null;
    		 try {
    			 jedis =  jedisPool.getResource();
    			//生成真正的key
    			 String realKey  = prefix.getPrefix() + key;
    			return  jedis.exists(realKey);
    		 }finally {
    			  returnToPool(jedis);
    		 }
    	}
    	
    	/**
    	 * 删除
    	 * */
    	public boolean delete(KeyPrefix prefix, String key) {
    		 Jedis jedis = null;
    		 try {
    			 jedis =  jedisPool.getResource();
    			//生成真正的key
    			String realKey  = prefix.getPrefix() + key;
    			long ret =  jedis.del(realKey);
    			return ret > 0;
    		 }finally {
    			  returnToPool(jedis);
    		 }
    	}
    	
    	/**
    	 * 增加值
    	 * */
    	public <T> Long incr(KeyPrefix prefix, String key) {
    		 Jedis jedis = null;
    		 try {
    			 jedis =  jedisPool.getResource();
    			//生成真正的key
    			 String realKey  = prefix.getPrefix() + key;
    			return  jedis.incr(realKey);
    		 }finally {
    			  returnToPool(jedis);
    		 }
    	}
    	
    	/**
    	 * 减少值
    	 * */
    	public <T> Long decr(KeyPrefix prefix, String key) {
    		 Jedis jedis = null;
    		 try {
    			 jedis =  jedisPool.getResource();
    			//生成真正的key
    			 String realKey  = prefix.getPrefix() + key;
    			return  jedis.decr(realKey);
    		 }finally {
    			  returnToPool(jedis);
    		 }
    	}
    	
    	public boolean delete(KeyPrefix prefix) {
    		if(prefix == null) {
    			return false;
    		}
    		List<String> keys = scanKeys(prefix.getPrefix());
    		if(keys==null || keys.size() <= 0) {
    			return true;
    		}
    		Jedis jedis = null;
    		try {
    			jedis = jedisPool.getResource();
    			jedis.del(keys.toArray(new String[0]));
    			return true;
    		} catch (final Exception e) {
    			e.printStackTrace();
    			return false;
    		} finally {
    			if(jedis != null) {
    				jedis.close();
    			}
    		}
    	}
    	
        /**
    	 * 命令格式:SCAN cursor [MATCH pattern] [COUNT count]
    	 * @cursor 游标
    	 * @[MATCH pattern] 返回和给定模式相匹配的元素
    	 * @count 每次迭代所返回的元素数量
    	 * @return SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。
    	 */
    	public List<String> scanKeys(String key) {
    		Jedis jedis = null;
    		try {
    			jedis = jedisPool.getResource();
    			List<String> keys = new ArrayList<String>();
    			String cursor = "0";
    			ScanParams sp = new ScanParams();
    			sp.match("*"+key+"*");
    			sp.count(100);
    			do{
    				ScanResult<String> ret = jedis.scan(cursor, sp);
    				List<String> result = ret.getResult();
    				if(result!=null && result.size() > 0){
    					keys.addAll(result);
    				}
    				//再处理cursor
    				cursor = ret.getStringCursor();
    			}while(!cursor.equals("0"));
    			return keys;
    		} finally {
    			if (jedis != null) {
    				jedis.close();
    			}
    		}
    	}
    
    	//TODO 转换类型不全 Boolean、Double、Float等
    	public static <T> String beanToString(T value) {
    		if(value == null) {
    			return null;
    		}
    		Class<?> clazz = value.getClass();
    		if(clazz == int.class || clazz == Integer.class) {
    			 return ""+value;
    		}else if(clazz == String.class) {
    			 return (String)value;
    		}else if(clazz == long.class || clazz == Long.class) {
    			return ""+value;
    		}else {
    			return JSON.toJSONString(value);
    		}
    	}
    
    	//TODO 转换类型不全 Boolean、Double、Float等
    	@SuppressWarnings("unchecked")
    	public static <T> T stringToBean(String str, Class<T> clazz) {
    		if(str == null || str.length() <= 0 || clazz == null) {
    			 return null;
    		}
    		if(clazz == int.class || clazz == Integer.class) {
    			 return (T)Integer.valueOf(str);
    		}else if(clazz == String.class) {
    			 return (T)str;
    		}else if(clazz == long.class || clazz == Long.class) {
    			return  (T)Long.valueOf(str);
    		}else {
    			return JSON.toJavaObject(JSON.parseObject(str), clazz);
    		}
    	}
    
    	private void returnToPool(Jedis jedis) {
    		 if(jedis != null) {
    			 jedis.close();
    		 }
    	}
    }
    

    7)Controller 中测试

    package com.gx.seckill.controller;
    
    
    import com.gx.seckill.domain.User;
    import com.gx.seckill.redis.RedisService;
    import com.gx.seckill.redis.UserKey;
    import com.gx.seckill.result.CodeMsg;
    import com.gx.seckill.result.Result;
    import com.gx.seckill.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    @RequestMapping("/demo")
    public class DemoController {
        @Autowired
        RedisService redisService;
    
        @RequestMapping("/redis/get")
        @ResponseBody
        public Result<User> redisGet() {
            User user = redisService.get(UserKey.getById, ""+1, User.class);
            return Result.success(user);
        }
    
        @RequestMapping("/redis/set")
        @ResponseBody
        public Result<Boolean> redisSet() {
            User user = new User();
            user.setId(1);
            user.setName("1111");
            redisService.set(UserKey.getById, ""+1, user);//UserKey:id1
            return Result.success(true);
        }
    }
    
  • 相关阅读:
    android模拟器速度问题
    input.nextLine() 问题出错!
    android中的“visible ”、“invisible”、“gone”的区别(转载)
    为什么匿名内部类参数必须为final类型(转载)
    转载------------------关于android的一些技巧
    关于数据库的数据类型
    关于几个新的快捷键
    目标

    巨大bug
  • 原文地址:https://www.cnblogs.com/miaomiaowu/p/14915792.html
Copyright © 2011-2022 走看看