• redis 乐观锁实践秒杀


    需求:有一个标(理解成抢红包也行,accountBalance预赋值1000元),大家可以抢购,每个用户抢购成功后,更新最后标的总数,在并发情况下,使用redis的乐观锁,保证更新标总值正确性,先往redis放一个标的金额:

    set accountBalance "1000"

    实现方式如下:

    pom.xml

    <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/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>mybatisPage</groupId>
        <artifactId>page</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <name>PageHelperSample</name>
        <url>http://git.oschina.net/free/Mybatis-Sample</url>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <!-- jstl -->
            <dependency>
                <groupId>jstl</groupId>
                <artifactId>jstl</artifactId>
                <version>1.1.2</version>
            </dependency>
            <dependency>
                <groupId>taglibs</groupId>
                <artifactId>standard</artifactId>
                <version>1.1.2</version>
            </dependency>
            <!-- jstl -->
            <dependency>
                <groupId>commons-pool</groupId>
                <artifactId>commons-pool</artifactId>
                <version>1.6</version>
            </dependency>
    
            <!-- log mybatis sql -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.5</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.5</version>
            </dependency>
            <!-- log mybatis sql -->
    
            <!-- fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.5</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.4</version>
            </dependency>
            <!-- web -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.1</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>taglibs</groupId>
                <artifactId>standard</artifactId>
                <version>1.1.2</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
    
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>3.7.4</version>
            </dependency>
            <dependency>
                <groupId>com.github.jsqlparser</groupId>
                <artifactId>jsqlparser</artifactId>
                <version>0.9.1</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.2.5</version>
            </dependency>
            <!-- util -->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.1</version>
            </dependency>
            <!-- mysql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.35</version>
            </dependency>
            <!-- redis -->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>2.1.0</version>
                <type>jar</type>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                        <encoding>utf-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.mortbay.jetty</groupId>
                    <artifactId>jetty-maven-plugin</artifactId>
                    <version>8.0.0.M3</version>
                </plugin>
            </plugins>
        </build>
    </project>

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
        
        <servlet>
            <servlet-name>bid</servlet-name>
            <servlet-class>com.heli.mybatis.page.servlet.ReidsMatchServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>bid</servlet-name>
            <url-pattern>/bid</url-pattern>
        </servlet-mapping>
        <servlet>
            <servlet-name>list</servlet-name>
            <servlet-class>com.heli.mybatis.page.servlet.ReidsMatchListServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>list</servlet-name>
            <url-pattern>/list</url-pattern>
        </servlet-mapping>
    </web-app>

    servlet

    package com.heli.mybatis.page.servlet;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Random;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.lang3.StringUtils;
    
    import com.commnon.RedisAPI;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.Transaction;
    
    public class ReidsMatchServlet extends HttpServlet {
        public static JedisPool pool = RedisAPI.getPool();
    
        // RedisAPI.set("accountBalance", "999999999");// 标还剩999999999块钱
    
        private static final long serialVersionUID = 1L;
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            Jedis jedis = pool.getResource();
            long start = System.currentTimeMillis();
            int flag = 0;
            try {
                flag = bid(request, response, jedis);
            } catch (Exception e) {
                e.printStackTrace();
                response.getWriter().write("fail buy");
            } finally {
                pool.returnBrokenResource(jedis);
                RedisAPI.returnResource(pool, jedis);
            }
            if (flag == 1) {
                response.getWriter().write("success buy");
            } else if (flag == 2) {
                response.getWriter().write("have buy");
            } else if (flag == 0) {
                response.getWriter().write("bid is zero ,you can not buy");
            }else{
                response.getWriter().write("fail buy");
            }
            long end = System.currentTimeMillis();
            System.out.println("--------------------------------------------请求耗时:" + (end - start) + "毫秒");
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    
        private int bid(HttpServletRequest request, HttpServletResponse response, Jedis jedis) throws Exception {
            int flag = 0;// 1,成功,2已经购买,3已经没钱了,其他異常
            // 每个请求对应一个userId
            int userId = new Random().nextInt(999999);
            
            // 观察 总标值,每人抢购一元
            while ("OK".equals(jedis.watch("accountBalance"))) {
                    // 判断是否购买过
                    Boolean isBuy = RedisAPI.sismember("userIdSet", userId + "");
                    if (isBuy) {
                        flag = 2;
                        return flag;
                    }
                    //投资额
                int r = 1;// new Random().nextInt(2);
                int lastAccount = 0;
                String balance = RedisAPI.get("accountBalance");
                if (StringUtils.isNotBlank(balance)) {
                    lastAccount = Integer.valueOf(balance) - r;
                }
                if (lastAccount < 0) {
                    flag = 3;
                    break;
                }
                Transaction tx = jedis.multi();
                tx.set("accountBalance", lastAccount + "");
                List<Object> result = tx.exec();
                if (result == null || result.isEmpty()) {
                    jedis.unwatch();
                } else {
                    System.out.println("恭喜您," + userId + "已经中标" + r + "元,标余额" + lastAccount + "元");
                    RedisAPI.set(Thread.currentThread().getName(), r + "");
                    RedisAPI.sadd("userIdSet", userId + "");
                    flag = 1;
                    break;
                }
            }
            return flag;
        }
    }
    package com.heli.mybatis.page.servlet;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.commnon.RedisAPI;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    public class ReidsMatchListServlet extends HttpServlet {
        public static JedisPool pool= RedisAPI.getPool();;
        public static Jedis jedis;
        static {
            jedis = pool.getResource();
        }
    
        private static final long serialVersionUID = 1L;
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            list(request, response);
            try {
                response.sendRedirect("list.jsp");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    
        private void list(HttpServletRequest request, HttpServletResponse response) {
            Set set = jedis.smembers("userIdSet");
            Iterator ite = set.iterator();
            System.out.println("中标名单-------------------------");
            int i = 0;
            Map<String, String> map = new HashMap<String, String>();
            while (ite.hasNext()) {
                i++;
                Object obj1 = ite.next();
                System.out.println("第" + i + "名:" + obj1);
                map.put("第" + i + "名:", obj1 + "");
            }
            request.getSession().setAttribute("user", map);
            System.out.println("中标名单-------------------------");
        }
    
    }

    工具类

    package com.commnon;
    
    import java.util.ResourceBundle;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    /**
     * Redis操作接口
     *
     * @author 林计钦
     * @version 1.0 2013-6-14 上午08:54:14
     */
    public class RedisAPI {
        private static JedisPool pool = null;
        private static ThreadLocal<JedisPool> poolThreadLocal = new ThreadLocal<JedisPool>();
    
        /**
         * 构建redis连接池
         * 
         * @param ip
         * @param port
         * @return JedisPool
         */
        public static JedisPool getPool() {
            if (pool == null) {
                ResourceBundle bundle = ResourceBundle.getBundle("redis");
                if (bundle == null) {
                    throw new IllegalArgumentException(
                            "[redis.properties] is not found!");
                }
                JedisPoolConfig config = new JedisPoolConfig();
                config.setMaxActive(Integer.valueOf(bundle
                        .getString("redis.pool.maxActive")));
                config.setMaxIdle(Integer.valueOf(bundle
                        .getString("redis.pool.maxIdle")));
                config.setMaxWait(Long.valueOf(bundle.getString("redis.pool.maxWait")));
                config.setTestOnBorrow(Boolean.valueOf(bundle
                        .getString("redis.pool.testOnBorrow")));
                config.setTestOnReturn(Boolean.valueOf(bundle
                        .getString("redis.pool.testOnReturn")));
                pool = new JedisPool(config, bundle.getString("redis.ip"),
                        Integer.valueOf(bundle.getString("redis.port")));
            }
            return pool;
        }
        
        public static JedisPool getConnection() {
            // ②如果poolThreadLocal没有本线程对应的JedisPool创建一个新的JedisPool,将其保存到线程本地变量中。
            if (poolThreadLocal.get() == null) {
                pool = RedisAPI.getPool();
                poolThreadLocal.set(pool);
                return pool;
            } else {
                return poolThreadLocal.get();// ③直接返回线程本地变量
            }
        }
    
        /**
         * 返还到连接池
         * 
         * @param pool
         * @param redis
         */
        public static void returnResource(JedisPool pool, Jedis redis) {
            if (redis != null) {
                pool.returnResource(redis);
            }
        }
    
        /**
         * 获取数据
         * 
         * @param key
         * @return
         */
        public static String get(String key) {
            String value = null;
    
            JedisPool pool = null;
            Jedis jedis = null;
            try {
                pool = getPool();
                jedis = pool.getResource();
                value = jedis.get(key);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放redis对象
                pool.returnBrokenResource(jedis);
                // 返还到连接池
                returnResource(pool, jedis);
            }
    
            return value;
        }
    
        /**
         * 赋值数据
         * 
         * @param key
         * @return
         */
        public static String set(String key, String value) {
            String result = null;
            JedisPool pool = null;
            Jedis jedis = null;
            try {
                pool = getPool();
                jedis = pool.getResource();
                result = jedis.set(key, value);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放redis对象
                pool.returnBrokenResource(jedis);
                // 返还到连接池
                returnResource(pool, jedis);
            }
    
            return result;
        }
    
        /**
         * 赋值数据
         * 
         * @param key
         * @return
         */
        public static Long sadd(String key, String value) {
            Long result = null;
            JedisPool pool = null;
            Jedis jedis = null;
            try {
                pool = getPool();
                jedis = pool.getResource();
                result = jedis.sadd(key, value);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放redis对象
                pool.returnBrokenResource(jedis);
                // 返还到连接池
                returnResource(pool, jedis);
            }
    
            return result;
        }
        
        /**
         * 判断set中是否有值
         * 
         * @param key
         * @return
         */
        public static Boolean sismember(String key, String member) {
            Boolean result = null;
            JedisPool pool = null;
            Jedis jedis = null;
            try {
                pool = getPool();
                jedis = pool.getResource();
                result = jedis.sismember(key, member);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放redis对象
                pool.returnBrokenResource(jedis);
                // 返还到连接池
                returnResource(pool, jedis);
            }
    
            return result;
        }
    
    }

    redis.properties

    #u6700u5927u5206u914du7684u5bf9u8c61u6570
    redis.pool.maxActive=1024
    #u6700u5927u80fdu591fu4fddu6301idelu72b6u6001u7684u5bf9u8c61u6570
    redis.pool.maxIdle=200
    #u5f53u6c60u5185u6ca1u6709u8fd4u56deu5bf9u8c61u65f6uff0cu6700u5927u7b49u5f85u65f6u95f4
    redis.pool.maxWait=1000
    #u5f53u8c03u7528borrow Objectu65b9u6cd5u65f6uff0cu662fu5426u8fdbu884cu6709u6548u6027u68c0u67e5
    redis.pool.testOnBorrow=true
    #u5f53u8c03u7528return Objectu65b9u6cd5u65f6uff0cu662fu5426u8fdbu884cu6709u6548u6027u68c0u67e5
    redis.pool.testOnReturn=true
    #IP
    redis.ip=127.0.0.1
    #Port
    redis.port=6379

    bid.jsp

    <%@ page language="java" contentType="text/html; charset=utf-8"
        pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>抢标秒杀</title>
    <style type="text/css">
    * {
        margin: 0;
    }
    
    html, body {
        height: 100%;
    }
    
    .wrapper {
        min-height: 100%;
        height: auto !important;
        height: 100%;
        margin: 0 auto -155px;
    }
    
    .footer, .push {
        height: 155px;
    }
    
    .middle {
        text-align: center;
        margin: 0 auto;
         600px;
        height: auto;
    }
    </style>
    </head>
    <body>
        <form name="formBid" action="bid" method="get">
            <div class="wrapper">
                <div class="middle">
                    <h1 style="padding: 0px 0 10px;">秒标</h1>
                    <br> <br> <br> <br> <br> <br> <br><br> <br> <br> <input type="submit"style=" 600px; height: 200px" value="秒杀" /> <br> <br><br> <br> <br></div></div></form><form name="formList" action="list" method="post"><div class="wrapper"><div class="middle"><br> <br> <br> <br> <br> <inputtype="submit" style=" 200px; height: 50px" value="查看秒杀结果" /></div></div></form></body></html>

    list.jsp

    <%@ page language="java" contentType="text/html; charset=utf-8"
        pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>抢标秒杀</title>
    <style type="text/css">
    * {
        margin: 0;
    }
    
    html, body {
        height: 100%;
    }
    
    .wrapper {
        min-height: 100%;
        height: auto !important;
        height: 100%;
        margin: 0 auto -155px;
    }
    
    .footer, .push {
        height: 155px;
    }
    
    .middle {
        text-align: center;
        margin: 0 auto;
         600px;
        height: auto;
    }
    </style>
    </head>
    <body>
        <%
            java.util.Map<String, String> mapBean = (java.util.Map<String, String>) request.getSession()
                    .getAttribute("user");
        %>
    
        <form name="formList" action="list" method="post">
            <div class="wrapper">
                <div class="middle">
                    <br> <br> <br> <br> <br> <br> <br>
                    <br>
                    <h1 style="padding: 0px 0 10px;">
                        中奖名单<%if (mapBean != null) {
                    %><%=mapBean.size()%></h1><%
                        }
                    %><br><%if (mapBean != null) {
                            for (String key : mapBean.keySet()) {
                    %><%=key%>--<%=mapBean.get(key)%><br><%
                        }
                        }
                    %></div></div></form></body></html>
  • 相关阅读:
    RPi.GPIO
    Linux(Ubuntu)下查看摄像头是否挂载及挂载的USB端口号
    python--python脚本中调用shell命令
    pip
    MQTT
    Ubuntu安装pip
    python Opencv开启USB摄像头并录像保存
    视频 客服
    Android Test
    MAC系统下ADB入门与简单使用
  • 原文地址:https://www.cnblogs.com/sha0830/p/6133094.html
Copyright © 2020-2023  润新知