redis+websocket(秒杀,模拟客户端)

一.设计背景

最近学了redis,websocket这些知识,想着弄出个

目录

一.设计背景

二.websocket实现:

1.Java代码:

2.前端HTML(简单写了一个文本框)

三.redis部分的代码

1.处理redis的key类:

2.redis缓存数据类型枚举类

3.redis数据操作封装类

4.抢购会员的信息实体类

四.截图展示

五.总结


啥来体验一下学习成果,便想到秒杀系统,然后一结合,就弄出了一个demo(写的不多,毕竟只有一个下午的时间)

废话不多说,直接上代码---这几个知识点的定义用法就不用介绍了,有疑问的兄弟请自行百度,或者可以提问(虽然我不一定回答)

二.websocket实现:

1.Java代码:

package com.server;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import com.xiaoleilu.hutool.util.StrUtil;

import redis.LinkToRedis;
import redis.RedisDataEnum;
import redis.RedisKeyBuilder;
import redis.clients.jedis.Jedis;
/**
 * 在tomcat7中存在WebSocketServlet类(但已经过时),在tomcat8中彻底删除
 * 此处使用@ServerEndpoint注解,主要是将目前的类定义成一个websocket服务器端
 * 注解的值将被用于监听用户连接的终端访问URL地址
 * onMessage|onOpen方法可能包含一个javax.websocket.Session可选参数
 * 如果有这个参数,容器将会把当前发送消息客户端的连接Session注入进去
 */
@ServerEndpoint("/websocketSecKill")
public class WebSocketSecKill {
	/*上线人数*/
	private static AtomicInteger appOnlineAcount = new AtomicInteger(0);
	/*会员编号和session对应的集合*/
	private static Map<Session, String> appSessionMap = new ConcurrentHashMap<>();
	/**
	 * 当服务器接收到客户端发送的消息时所调用的方法
	 * @param message
	 * @param session
	 * @throws IOException
	 * @throws InterruptedException
	 */
	@OnMessage
	public void onMessage(String message,Session session) throws IOException, InterruptedException {
		// 打印从客户端获取到的信息
		System.out.println("从客户端接收到的信息: " + message);
		System.out.println("当前会员序号=="+session.getId());
		System.out.println("当前会员编号=="+appSessionMap.get(session));
		String[] str=message.toString().split("\\|");
		if(str[0].toString().equals("抢购".toString())) {
			Jedis jedis = LinkToRedis.open();
			String key = RedisKeyBuilder.buildKey(RedisDataEnum.COUNT);
			System.out.println("==="+key);
			if(Integer.valueOf(jedis.get(key))<=4){//库存仅5,从0开始
			String count=LinkToRedis.addCount(appSessionMap.get(session));
			session.getBasicRemote().sendText("即时发送信息,当前是第 " + count+"次...");
			}else{
				session.getBasicRemote().sendText("即时发送信息,您的手速慢了");
			}
		}
		
		if(str[0].toString().equals("0".toString())) {//当前已抢购数目清零
			LinkToRedis.returnCount();
			session.getBasicRemote().sendText("即时发送信息,抢购数目已清零" );
		}
		if(str[0].toString().equals("1".toString())) {//删除redis上面抢购会员信息,释放内存
			LinkToRedis.del();
			session.getBasicRemote().sendText("即时发送信息,抢购会员信息已清空" );
		}
	}
	/**
	 * 当一个新用户连接时所调用的方法
	 * @param session
	 */
	@OnOpen
	public void onOpen(Session session) {
		System.out.println("客户端连接成功");
		System.out.println(session.getId());
		connect(session.getId(),session);
	}
	/**
	 * 当一个用户断开连接时所调用的方法
	 * @param session
	 */
	@OnClose
	public void onClose(Session session) {
		System.out.println("客户端关闭");
		appSessionMap.remove(session);
		closeSession(session);
	}
	
	/**
	 * websocket连接
	 * @param memberNo 会员编号
	 * @param session 对应的session
	 * @description
	 */
	public static void connect(String memberNo, Session session) {	
		if(StrUtil.isEmpty(memberNo) || session == null) {
			System.out.println("app websocket连接,但参数为空");
			return;
		}
		String oldSession = appSessionMap.get(session);
		if(oldSession != null) {
			System.out.println("app的websocket删除旧session");
			closeSession(session);
			appSessionMap.remove(session);
		}
		int count = appOnlineAcount.incrementAndGet();
		appSessionMap.put(session, memberNo+"520500");
		System.out.println("app用户websocket连接上来,当前连接数:" + count+"--"+session.toString());
	}
	
	/**
	 * 关闭session
	 * @param session
	 * @description
	 */
	public static void closeSession(Session session) {
		try{
			session.close();
		}catch(Exception ex){
			System.out.printf("关闭websocket出现异常",ex);
		}
	}
}

2.前端HTML(简单写了一个文本框)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket测试</title>  
</head>  
<body>  
    <div>  
        <input type="button" value="stop" onclick="stop()" />  
        <input type="button" value="send" onclick="clock()" /> 
    </div>  
    <input id="message" type="text" value="">
    <input id="time" type="hidden" value="5000">
    <div id="messages"></div>  
    <script type="text/javascript"> 
    	
        var webSocket = new WebSocket('ws://localhost:8080/websocketDemo/websocketSecKill');  
        webSocket.onerror = function(event) {  
            alert(event.data);  
        };  
        //与WebSocket建立连接  
        webSocket.onopen = function(event) {  
            document.getElementById('messages').innerHTML = '与服务器端建立连接';  
        };  
        //处理服务器返回的信息  
        webSocket.onmessage = function(event) {  
            document.getElementById('messages').innerHTML += '<br />'+ event.data;  
        };  
        function stop() {  
        	
            //向服务器发送请求 
            document.getElementById("time").value=1000; 
            alert(document.getElementById("time").value);
        }  
         
        //var int=self.setInterval("clock()",document.getElementById("time").value);
        function clock()
        {
        	//向服务器发送请求 
            var message=document.getElementById("message").value;
            webSocket.send(message);
        }
    </script>  
</body>  
</html>

三.redis部分的代码

1.处理redis的key类:

package redis;

import java.util.Collections;
import java.util.List;


import redis.RedisDataEnum;
import com.xiaoleilu.hutool.util.StrUtil;

public class RedisKeyBuilder {
	/*分隔符*/
    protected final static String SEPARATOR = ":";
    /**
     * 生成redis键
     * @param redisData 保存到redis中的类型
     * @param patterns 组成键的元素
     * @return String
     * @date 2018年7月26日
     */
    public static String buildKey(RedisDataEnum redisData ,Object... patterns) {
    	StringBuilder key = new StringBuilder(redisData.getPrefix()).append(SEPARATOR);
		for(int i = 0 , size = patterns.length ; i < size; i++) {
			key.append(patterns[i]);
			if(i != size - 1) {
				key.append(SEPARATOR);
			}
		}
		return key.toString();
	}
    /**
     * 拆分键
     * @param key 需要拆分的键
     * @return List<String>
     * @description
     */
    public List<String> splitKey(String key){
    	if(StrUtil.isEmpty(key)) {
    		return Collections.emptyList();
    	}
    	return StrUtil.split(key, ':');
    }
    

}

2.redis缓存数据类型枚举类

package redis;

/**
 * redis缓存数据类型
 */
public enum RedisDataEnum {
	COUNT("COUNT","当前抢购数量"),
	Buyer("Buyer","买家");
	
	
	private String prefix;
	private String description;
	
	private RedisDataEnum(String prefix, String description) {
		this.description = description;
		this.prefix = prefix;
	}
	public String getPrefix() {
		return prefix;
	}
	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	
}

3.redis数据操作封装类

import bean.FastBuyer;
import redis.clients.jedis.Jedis;

public class LinkToRedis {
	public static Jedis open() {
		Jedis jedis = new Jedis("127.0.0.1",端口号);//本地存储,---已测
		jedis.auth("---密码--");
		System.out.println("Connection to server sucessfully");
		return jedis;
	}
	public static String addCount(String memberNo) {
		Jedis jedis = LinkToRedis.open();
		String key = RedisKeyBuilder.buildKey(RedisDataEnum.COUNT);
		System.out.println("==="+key);
		if(StrUtil.isEmpty(jedis.get(key))){
			System.out.println("初始化,插入第一条数据");
			jedis.set(key, "1");
		}

		String countNew=String.valueOf(Integer.valueOf(jedis.get(key))+1);
		System.out.println("======"+countNew);
	      //设置 redis 字符串数据
	    jedis.set(key, countNew);
	    String keybuyer = RedisKeyBuilder.buildKey(RedisDataEnum.Buyer,memberNo);
	    FastBuyer fastBuyer=new FastBuyer();
	    fastBuyer.setMemberNo(memberNo);
	    fastBuyer.setTime((new Date()).toString());
	    jedis.set(keybuyer, fastBuyer.toString());
	     // 获取存储的数据并输出
	    System.out.println("Stored string in redis:: "+ jedis.get(keybuyer));
		return countNew;
	}
	public static void returnCount() {
		Jedis jedis = LinkToRedis.open();
		String key = RedisKeyBuilder.buildKey(RedisDataEnum.COUNT);
		System.out.println("抢购数目清零");
		jedis.set(key, "0");
	}
	/**
	 * 入mysql(oracle)数据库后
	 * 删除redis上面抢购会员信息,释放内存
	 */
	public static void del() {

    	Jedis jedis = LinkToRedis.open();

        Set<String> set = jedis.keys("Buyer*");

        Iterator it = set.iterator();
        while (it.hasNext()) {
            String key = (String) it.next();
            jedis.del(key);
            System.out.println(key);
        }
    }
    
//    public static void main(String[] args) {
//    	del();
//	}
}

4.抢购会员的信息实体类

package bean;

import java.io.Serializable;

public class FastBuyer implements Serializable{
	private static final long serialVersionUID = 1L;
	private String memberNo;
	private String time;
	public String getMemberNo() {
		return memberNo;
	}
	public void setMemberNo(String memberNo) {
		this.memberNo = memberNo;
	}
	public String getTime() {
		return time;
	}
	public void setTime(String time) {
		this.time = time;
	}
	@Override
	public String toString() {
		return "FastBuyer [memberNo=" + memberNo + ", time=" + time + "]";
	}
	
}

四.截图展示

1.首先运行项目,连续在浏览器开出6个同样的页面(主要是为了模拟客户端多用户)来测试,对redis数据库进行清空初始化(页面设置了个0.1来实现,主要是为了清除我之前测试的数据)

对应的后台tomcat输出日志:

2.下面就是测试截图了:

图1:

图2:

图3:

图4:

图5:(tomcat部分测试运行日志)

3.既然是对redis高速缓存数据库操作,那么肯定有记录:

如下图,redis客户端可以看到我本地的数据:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值