前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

miaosha

原创
作者头像
大学里的混子
修改2019-03-06 14:19:27
4900
修改2019-03-06 14:19:27
举报
文章被收录于专栏:LeetCodeLeetCode
代码语言:javascript
复制
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;


@Bean
public JedisPool JedisPoolFactory() {
   JedisPoolConfig poolConfig = new JedisPoolConfig();
   poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
   poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
   poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000);
   JedisPool jp = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
         redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0);
   return jp;
}

maxTotal 资源池中最大连接数 默认值8 建议值

maxIdle 资源池允许最大空闲的连接数 默认值8 建议值

minIdle 资源池确保最少空闲的连接数 默认值0 建议值

代码语言:javascript
复制
private void returnToPool(Jedis jedis) {
    if(jedis != null) {
        jedis.close();
    }
}

代码语言:javascript
复制
   /**
     * 获取当个对象
     * */
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);
        }
    }

jedis = jedisPool.getResource();

代码语言:javascript
复制
  jedis.exists(realKey);
  long ret =  jedis.del(realKey);
      /**
     * 增加值
     * */
  jedis.incr(realKey);
      /**
     * 减少值
     * */
  jedis.decr(realKey);
  
代码语言:javascript
复制
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();
        }
    }
}
代码语言:javascript
复制
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;

在 Redis 里,所谓 SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果

SETEX key seconds value

可用版本: >= 2.0.0 时间复杂度: O(1)

将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟。

如果键 key 已经存在, 那么 SETEX 命令将覆盖已有的值。

SETEX 命令的效果和以下两个命令的效果类似:

代码语言:javascript
复制
SET key value
EXPIRE key seconds  # 设置生存时间

SETEX 和这两个命令的不同之处在于 SETEX 是一个原子(atomic)操作, 它可以在同一时间内完成设置值和设置过期时间这两个操作, 因此 SETEX 命令在储存缓存的时候非常实用。

DECR key

可用版本: >= 1.0.0 时间复杂度: O(1)

为键 key 储存的数字值减去一。

如果键 key 不存在, 那么键 key 的值会先被初始化为 0 , 然后再执行 DECR 操作。

如果键 key 储存的值不能被解释为数字, 那么 DECR 命令将返回一个错误。

本操作的值限制在 64 位(bit)有符号数字表示之内。

关于递增(increment) / 递减(decrement)操作的更多信息, 请参见 INCR 命令的文档。

返回值

DECR 命令会返回键 key 在执行减一操作之后的值。

是原子操作

代码语言:javascript
复制


import org.apache.commons.codec.digest.DigestUtils;
 
public class MD5Util {
    public static String md5(String src){
        return DigestUtils.md5Hex(src);
    }

    private static String salt = "1a2b3c4d";

    public static String inputPassToFormPass(String inputPass) {
        String str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);
        System.out.println(str);
        return md5(str);
    }

    public static String formPassToDBPass(String formPass, String salt) {
        String str = ""+salt.charAt(0)+salt.charAt(2) + formPass +salt.charAt(5) + salt.charAt(4);
        return md5(str);
    }

    public static String inputPassToDbPass(String inputPass, String saltDB) {
        String formPass = inputPassToFormPass(inputPass);
        String dbPass = formPassToDBPass(formPass, saltDB);
        return dbPass;
    }

因为明文传输,所以两次的MD5

DAO

需要Mapper

代码语言:javascript
复制
@Mapper
@Component
public interface GoodsDao {
    @Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id")
    public List<GoodsVo> listGoodsVo();

    @Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id where g.id = #{goodsId}")
    public GoodsVo getGoodsVoByGoodsId(@Param("goodsId")long goodsId);

    @Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0")
    public int reduceStock(MiaoshaGoods g);

    @Update("update miaosha_goods set stock_count = #{stockCount} where goods_id = #{goodsId}")
    public int resetStock(MiaoshaGoods g);
}

MiaoshaUserService里面进行登陆是否成功的验证

代码语言:javascript
复制
 public CodeMsg login(HttpServletResponse response, LoginVo loginVo){
        if (loginVo==null){
           throw new GlobalException(CodeMsg.SERVER_ERROR);
        }
        String mobile = loginVo.getMobile();
        String formPass = loginVo.getPassword();

        //验证手机号是否存在
        MiaoshaUser user = getById(Long.parseLong(mobile));
        if (user == null){
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }

        String dbPass = user.getPassword();
        String saltDB = user.getSalt();
        String calcPass = MD5Util.formPassToDBPass(formPass,saltDB);
//        System.out.println("frompass"+formPass);
//        System.out.println("calcPass"+calcPass);
//        System.out.println("dbPass"+dbPass);
        if (!calcPass.equals(dbPass)){
            throw new GlobalException(CodeMsg.PASSWORD_ERROR);
        }

        //生成Cookie
//        String token = UUIDUtil.uuid();
//        redisService.set(MiaoshaUserKey.token,token,user);
//
//        Cookie cookie = new Cookie(COOKI_NAME_TOKEN,token);
//        cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
//        cookie.setPath("/");
//        response.addCookie(cookie);
        String token = UUIDUtil.uuid();
        addCookie(response,token,user);
        return CodeMsg.SUCCESS;
    }

@Valid 参数校验 省去了一大堆的参数校验

LoginVo loginVo 这个是从页面上获取到的数据

代码语言:javascript
复制
 @RequestMapping("/do_login")
    @ResponseBody
    public Result<Boolean> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) {
        log.info(loginVo.toString());
        //登录
//        String passInput = loginVo.getPassword();
//        String mobile = loginVo.getMobile();
//        if (StringUtils.isEmpty(passInput)){
//            return Result.error(CodeMsg.PASSWORD_EMPTY);
//        }
//        if (StringUtils.isEmpty(mobile)){
//            return Result.error(CodeMsg.MOBILE_EMPTY);
//        }
//        if (!ValidatorUtil.isMobile(mobile)){
//            return Result.error(CodeMsg.MOBILE_ERROR);
//        }

        CodeMsg cm = userService.login(response,loginVo);
        if (cm.getCode()==0){
            return Result.success(true);
        }else {
            return Result.error(cm);
        }
    }

Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。

当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。

UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。

cookie和session的区别和用法

flask中cookie和session介绍

一、cookie:

在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。

二、session:

session和cookie的作用有点类似,都是为了存储用户相关的信息。不同的是,cookie是存储在本地浏览器,而session存储在服务器。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些session信息还是绰绰有余的。

三、cookie和session结合使用:

web开发发展至今,cookie和session的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式:

1、存储在服务端:通过cookie存储一个session_id,然后具体的数据则是保存在session中。如果用户已经登录,则服务器会在cookie中保存一个session_id,下次再次请求的时候,会把该session_id携带上来,服务器根据session_id在session库中获取用户的session数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做server side session。

2、将session数据加密,然后存储在cookie中。这种专业术语叫做client side session。flask采用的就是这种方式,但是也可以替换成其他形式。

Token代表用户 session存放在redis里面

代码语言:javascript
复制
 {
 //生成cookie
   String token = UUIDUtil.uuid();
   addCookie(response, token, user);
   return true;
}

private void addCookie(HttpServletResponse response, String token, MiaoshaUser user) {
   redisService.set(MiaoshaUserKey.token, token, user);
   Cookie cookie = new Cookie(COOKI_NAME_TOKEN, token);
   cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
   cookie.setPath("/");
   response.addCookie(cookie);
}

1.UUID 简介    UUID 含义是通用唯一识别码 (Universally Unique Identifier),这是一个软件建构的标准。   也是被开源软件基金会 (Open Software Foundation, OSF) 的组织应用在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部分。   UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。   如此一来,每个人都可以建立不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库建立时的名称重复问题。

2.UUID 组成   UUID保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。   按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。UUID由以下几部分的组合:(1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。(2)时钟序列。(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。   UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。   标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)。

根据服务端把一个token写到了cookie之中 客户端在随后的访问之中 携带这个cookie 那么服务端根据这个cookie里面的token就能找到用户

页面缓存

代码语言:javascript
复制
 public String list(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user) {
        model.addAttribute("user",user);

        //取缓存
        String html = redisService.get(GoodsKey.getGoodsList,"",String.class);
        if (!StringUtils.isEmpty(html)){
            return html;
        }
        //查询商品列表
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        model.addAttribute("goodsList",goodsList);
//        return "goods_list";

        SpringWebContext ctx = new SpringWebContext(request,response,request.getServletContext(),request.getLocale(), model.asMap() ,applicationContext);
        //手动渲染
        html = thymeleafViewResolver.getTemplateEngine().process("goods_list",ctx);
        if (!StringUtils.isEmpty(html)){
            redisService.set(GoodsKey.getGoodsList,"",html);
        }
        return html;
    }

对象级别的缓存

代码语言:javascript
复制
public boolean updatePassword(String token,long id,String formPassword){
    //取user
    MiaoshaUser user = getById(id);
    if (user==null){
        throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
    }
    //更新数据库
    MiaoshaUser toBeUpdate = new MiaoshaUser();
    toBeUpdate.setId(id);
    toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPassword,user.getSalt()));
    miaoshaUserDao.update(toBeUpdate);
    //detail the cache
    redisService.delete(MiaoshaUserKey.getById,""+id);
    user.setPassword(toBeUpdate.getPassword());
    redisService.set(MiaoshaUserKey.token,token,user);
    return true;
}

数据一但有更新 ,那么缓存里面的东西也需要更新

前:

加入缓存之后:

这两个顺序不可以调换

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SETEX key seconds value
  • DECR key
    • 返回值
    • DAO
    • MiaoshaUserService里面进行登陆是否成功的验证
    • cookie和session的区别和用法
    • flask中cookie和session介绍
      • 一、cookie:
        • 二、session:
          • 三、cookie和session结合使用:
          • 页面缓存
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档