Java使用redis实现虚拟游戏商店功能

package redisTest.code4;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;


/** 《Redis实战》 4.4 Redis事务
 *
 * 使用hash保存用户信息(users:userId name userName funds money)
 * 使用set保存用户包裹信息(inventory:userId item1 item2...)
 * 使用zset保存商场所有商品,商品价格作为分数(market: money itemId.userId)
 * @author wss
 * @created 2020/9/25 14:40
 * @since 1.0
 */
public class fakeMarketTest {
    private Jedis conn;

    public fakeMarketTest(Jedis jedis) {
        this.conn = jedis;
        clean(this.conn);  // 初始化之前清除数据
        init(this.conn);
    }

    /**
     * 初始化
     */
    private void init(Jedis conn) {
        String userKey = "users:";
        String marketKey = "market:";
        String inventoryKey = "inventory:";
        String[] s = new String[6];
        s[0] = "A";s[1]="B";s[2]="C";s[3]="D";s[4]="E";s[5]="F";
        for (int i = 1; i <= 10; i++) {
       		// 添加用户信息
            conn.hset(userKey+i, "name", "A"+i);
            conn.hset(userKey+i, "funds", String.valueOf(i*100));
           	// 添加用户包裹信息
            for (int j = 1; j <= 5; j++) {
                conn.sadd(inventoryKey+i, "item"+s[j]+i);
            }
        }
       	// 使用split()函数分割"itemC1.2"时,要注意转义字符.
       	// System.out.println("itemC1.2".split("\\.")[0]);
        conn.zadd(marketKey, 24, "itemC1.2");
        conn.zadd(marketKey, 34, "itemD2.4");
        conn.zadd(marketKey, 44, "itemB4.1");
        conn.zadd(marketKey, 54, "itemE1.3");
        conn.zadd(marketKey, 37, "itemF3.2");

    }

    /**
     * 用户将包裹中的物品放在商场售卖
     * @param itemId    物品Id
     * @param sellerId  用户Id
     * @param price     物品价格
     * @return
     */
    public boolean listItem(Jedis conn, String itemId, String sellerId, long price) {

        String inventoryKey = "inventory:" + sellerId;
        String itemKey      = itemId + "." + sellerId;
        long endTime        = System.currentTimeMillis() + 5000;  // 加5秒
        Pipeline pipe = conn.pipelined();
        while (System.currentTimeMillis() < endTime) {
            // 监视卖家的包裹
            pipe.watch(inventoryKey);
            // 判断指定商品是否在用户包裹中,否则取消监视
            pipe.sismember(inventoryKey, itemId);
            // 使用pipe.syncAndReturnAll()统一获取所有读操作的结果,如果单独获取的话会报错(JedisDataException)
            List<Object> list = pipe.syncAndReturnAll();
            System.out.println(list);  // 输出:[OK, true](OK指的是pipe.watch(inventoryKey)返回的结果)
            if (list.size() > 0 && (list.get(1) == null || !(boolean)list.get(1))) {
                conn.unwatch();
                return false;
            }
            pipe.multi();
            pipe.zadd("market:", price, itemKey);   // 将该商品加入市场
            pipe.srem(inventoryKey, itemId);            // 从卖家的包裹中移除该商品
            pipe.exec();
            pipe.sync();
            return true;
        }
        try {
            pipe.clear();
            pipe.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 购买商品
     * @param buyerId  买家Id
     * @param itemKey  待售商品key(形式为itemId.userId)
     * @return
     */
    public boolean purchaseItem(Jedis conn, String buyerId, String itemKey) {

        String marketKey    = "market:";                    // 商场key
        String buyerKey     = "users:" + buyerId;           // 购买者key
        String sellerKey    = "users:" + itemKey.split("\\.")[1];  // 售者key
        String itemId       = itemKey.split("\\.")[0];  // 待售商品id
        String inventoryKey = "inventory:" + buyerId;       // 购买者包裹key
        long end            = System.currentTimeMillis() + 10000;  // 加10秒
        Pipeline pipe       = conn.pipelined();
        // 如果失败,进行重试,最大重试时间为10秒
        while (System.currentTimeMillis() < end) {
            // 监视商场和买家包裹信息
            pipe.watch(marketKey, buyerKey);
            // 获取欲购买的商品价格
//            double price = pipe.zscore(marketKey, itemKey).get();  // 会报错
            pipe.zscore(marketKey, itemKey);  // double类型
            // 获取购买者的金币余额
            pipe.hget(buyerKey, "funds"); // long类型
            List<Object> list = pipe.syncAndReturnAll();  // 将查询结果按顺序存储在列表中
            // 判断,前者条件判断商店是否有该商品,后者判断是否有该用户
            if (list.get(1) == null || list.get(2) == null){ return false;}

            long price = new Double((double) list.get(1)).longValue();
            long funds = Long.valueOf((String) list.get(2));

            // 如果购买者金币小于售价或者商店已无该件商品,取消监视,返回false
            if (price > funds) {
                conn.unwatch();
                return false;
            }
            pipe.multi();
            // 购买者的金币减少
            pipe.hincrBy(buyerKey, "funds", -price);
            // 售卖者的金币增加
            pipe.hincrBy(sellerKey, "funds", price);
            // 将该商品放入购买者的包裹
            pipe.sadd(inventoryKey, itemId);
            // 将该商品从商场删除
            pipe.zrem("market:", itemKey);
            pipe.exec();
            pipe.sync();
        }
        try {
        	pipe.clear();
            pipe.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 清除数据
     */
    public void clean(Jedis conn) {
        conn.flushAll();
    }

    /**
     * 商品拍卖
     * @return
     */
    public boolean goodsAuction(Jedis conn) {
		// 待更新...
        return true;
    }
    /**
     * 商品限时售卖
     * @return
     */
    public boolean goodsSoldInLimitTime(Jedis conn) {
		// 待更新...
        return true;
    }
    public static void main(String[] args) {

        Jedis conn = new Jedis("localhost");
        fakeMarketTest t = new fakeMarketTest(conn);
        // 打印用户id为1的用户信息(售卖者)
        System.out.println("用户1的信息:" + conn.hgetAll("users:1"));
        // 购买者
        System.out.println("用户2的信息:" + conn.hgetAll("users:2"));
        // 打印用户id为1的用户包裹信息
        System.out.println("用户1的包裹:" + conn.smembers("inventory:1"));
        System.out.println("用户2的包裹:" + conn.smembers("inventory:2"));
        // 打印商店货架上的商品id(id形式为:商品id.用户id)
        System.out.println("商店信息:" + conn.zrange("market:", 0, -1));

        // id=1的用户将itemC1商品放入商店售卖
        t.listItem(conn,"itemC1", "1", 99);
        System.out.println("用户1的信息:" +conn.hgetAll("users:1"));
        System.out.println("用户1的包裹:" +conn.smembers("inventory:1"));
        System.out.println("商店信息:" + conn.zrange("market:", 0, -1));

        // id=2的用户购买id=1的用户在商场售卖的id=itemC1的商品
        t.purchaseItem(conn, "2", "itemC1.1");
        System.out.println("用户1的信息:" + conn.hgetAll("users:1"));
        System.out.println("用户2的信息:" + conn.hgetAll("users:2"));
        System.out.println("用户2的包裹:" + conn.smembers("inventory:2"));
        System.out.println("商店信息:" + conn.zrange("market:", 0, -1));
    }
}

运行,结果如下:

D:\Java\jdk1.8.0_152\bin\java.exe "
用户1的信息:{name=A1, funds=100}
用户2的信息:{name=A2, funds=200}
用户1的包裹:[itemC1, itemB1, itemE1, itemD1, itemF1]
用户2的包裹:[itemC2, itemB2, itemE2, itemD2, itemF2]
商店信息:[itemC1.2, itemD2.4, itemF3.2, itemB4.1, itemE1.3]
[OK, true]
用户1的信息:{name=A1, funds=100}
用户1的包裹:[itemE1, itemD1, itemF1, itemB1]
商店信息:[itemC1.2, itemD2.4, itemF3.2, itemB4.1, itemE1.3, itemC1.1]
用户1的信息:{name=A1, funds=199}
用户2的信息:{name=A2, funds=101}
用户2的包裹:[itemE2, itemD2, itemC2, itemF2, itemC1, itemB2]
商店信息:[itemC1.2, itemD2.4, itemF3.2, itemB4.1, itemE1.3]
Process finished with exit code 0

备注:
可以将待售的商品价格调高,高于购买者的金币数量,结果就是售方,买方,商店的相应数据都不变

Java中,可以使用Redis的Sorted Set(有序集合)来实现延时发布任务。下面是一个简单的示例代码: ```java import redis.clients.jedis.Jedis; import java.util.Set; public class DelayedTaskPublisher { private static final String REDIS_KEY = "delayed_tasks"; public static void main(String[] args) { // 创建Redis连接 Jedis jedis = new Jedis("localhost"); // 模拟发布4个延时任务 publishDelayedTask(jedis, "task1", 5000); publishDelayedTask(jedis, "task2", 10000); publishDelayedTask(jedis, "task3", 15000); publishDelayedTask(jedis, "task4", 20000); // 开始处理延时任务 processDelayedTasks(jedis); // 关闭Redis连接 jedis.close(); } private static void publishDelayedTask(Jedis jedis, String task, long delayMillis) { // 计算任务的过期时间 long expireTime = System.currentTimeMillis() + delayMillis; // 将任务添加到有序集合中,分数为过期时间,值为任务内容 jedis.zadd(REDIS_KEY, expireTime, task); } private static void processDelayedTasks(Jedis jedis) { while (true) { // 获取当前时间戳 long currentTime = System.currentTimeMillis(); // 获取所有已过期的任务 Set<String> expiredTasks = jedis.zrangeByScore(REDIS_KEY, 0, currentTime); // 处理已过期的任务 for (String task : expiredTasks) { System.out.println("处理延时任务:" + task); // 从有序集合中移除已处理的任务 jedis.zrem(REDIS_KEY, task); } // 休眠一段时间后再次检查是否有延时任务到期 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 这个示例代码演示了如何使用Redis的Sorted Set来实现延时发布任务。首先,通过`publishDelayedTask`方法发布了4个延时任务,每个任务设定了不同的延时时间。然后,在`processDelayedTasks`方法中,通过循环和睡眠,不断检查是否有任务已经过期,并进行处理。处理完成后,将任务从有序集合中移除。 请注意,在实际生产环境中,可能需要进行更多的优化和异常处理。此示例仅为演示基本概念和流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值