Java: 操作Redis

推荐使用Docker来搭建Redis环境.

连接的两种方式

会使用以下方式来操作Redis:

  1. Lettuce:Lettuce是基于Netty和Reactor的可伸缩线程安全的Redis客户端。Lettuce提供了同步,异步和反应的API来与Redis进行交互。
  2. Spring Data Redis:可以与Lettuce及其它Redis客户端整合.(暂未提供,待补充)

注意: 在Lettuce中,目前只提供了同步/异步的方式与Redis交互,暂无提供反应式方式与Redis交互的计划.

版本

  1. Redis: 4.*
  2. Lettuce:5.*
  3. Java:1.8 +(未在Java 1.9 中测试,不保证不会出现问题 – 2018/01/17)
  4. Maven:3.5

完整源码地址在: Gitee仓库

java标志
image-2863

单独使用Lettuce

请注意,该示例为一个Maven项目.

异步请求

异步使用的部分代码如下:

	/**
     * Redis事务使用异步执行
     * <p>
     * 方法会删除在本方法内添加的部分数据,具体看方法内部.
     */
    public static void asyncTransactionsSetAndGetData() throws ExecutionException, InterruptedException {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();

        // 开启事务
        RedisFuture<String> stringRedisFuture = asyncCommands.multi();
        System.out.println("开启事务的执行反馈:" + stringRedisFuture.get());

        RedisFuture<String> rf = asyncCommands.set("MUL-" + AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        int count = 20;
        for (int i = 0; i < count; i++) {
            asyncCommands.incr("MUL-COUNT");
            asyncCommands.set(i + "-MUL-" + AppConfig.DEFAULT_KEY, i + "," + AppConfig.DEFAULT_VALUE);
        }

        // 删除刚才添加的数据
        for (int i = 0; i < count; i++) {
            asyncCommands.del(i + "-MUL-" + AppConfig.DEFAULT_KEY);
        }

        // 执行事务
        RedisFuture<TransactionResult> result = asyncCommands.exec();

        System.out.println("事务执行结果:" + result.get());
        System.out.println("命令执行结果:" + rf.get());

        redisClient.shutdownAsync();
    }


    /**
     * 异步执行批量设置及获取数据
     * <p>
     * <p>
     * 未删除本方法内添加的数据.
     */
    public static void asyncBatchSetAndGetData() {

        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();
        int count = 10;
        List<RedisFuture<String>> futures = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            futures.add(asyncCommands.set(AppConfig.DEFAULT_KEY + "-" + i, AppConfig.DEFAULT_VALUE + i));
        }

        System.out.println(LettuceFutures.awaitAll(1, TimeUnit.MINUTES, futures.toArray(new RedisFuture[futures.size()])));

        List<RedisFuture<String>> lists = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            lists.add(asyncCommands.get(AppConfig.DEFAULT_KEY + "-" + i));
        }
        lists.stream().filter(x -> !"".equals(x.getError())).forEach(y -> {
            try {
                System.out.println(y.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        redisClient.shutdownAsync();

    }

    /**
     * 异步设置与获取数据的基本用法
     * <p>
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void asyncSetAndGetData() throws ExecutionException, InterruptedException {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();

        // 设置数据
        Future<String> setResult = asyncCommands.set(AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        System.out.println(setResult.get());


        // 读取数据
        Future<String> getResult = asyncCommands.get(AppConfig.DEFAULT_KEY);
        System.out.println(getResult.get());

        // 删除数据,可以传递多个key.
        asyncCommands.del(AppConfig.DEFAULT_KEY);

        /*
         * 关闭此客户端并异步关闭所有打开的连接。
         * 调用此方法后客户端的操作被丢弃。
         * 关机时间为2秒,超时时间为15秒。
         */
        redisClient.shutdownAsync();
    }


    /**
     * 异步设置与获取数据的高级用法.
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void asyncAdvancedSetAndGetData() throws ExecutionException, InterruptedException {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);
        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();

        // 编解码器
        StringCodec codec = StringCodec.UTF8;
        // 状态消息输出
        StatusOutput sot = new StatusOutput<>(codec);

        // 添加命令
        RedisFuture<String> response = asyncCommands.dispatch
                (CommandType.SET, sot,
                        new CommandArgs<>(codec).addKey(AppConfig.DEFAULT_KEY)
                                .addValue(AppConfig.DEFAULT_VALUE));


        // 可以检查是否已经执行完成(执行完成不表示执行成功与否)
        if (response.isDone()) {
            // 会返回执行结果
            System.out.println(response.get());
        }

        // 获取刚才设置的结果
        RedisFuture<String> getInfo = asyncCommands.dispatch(CommandType.GET, sot,
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY));

        // 下面会显示上面设置的值:
        System.out.println(getInfo.get());

        // 删除键值
        System.out.println("删除结果(已删除的键数量):"+asyncCommands.dispatch(CommandType.DEL, new IntegerOutput<>(codec),
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY)).get());

        /*
         * 关闭这个客户端并关闭所有打开的连接。 调用关机后应该丢弃客户端。 关机没有安静的时间和2秒的超时。
         */
        redisClient.shutdown();
    }

image-2864

同步请求

同步请求处理的部分代码:

	/**
     * Redis事务使用同步执行
     * <p>
     * 方法会删除在本方法内添加的部分数据,具体看方法内部.
     */
    public static void syncTransactionsSetAndGetData() {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取同步执行命令(这一步很关键)
        RedisCommands<String, String> redisCommands = redisClient.connect().sync();

        // 开启事务
        String stringRedisFuture = redisCommands.multi();
        System.out.println("开启事务的执行反馈:" + stringRedisFuture);

        String rf = redisCommands.set("MUL-" + AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        int count = 20;
        for (int i = 0; i < count; i++) {
            redisCommands.incr("MUL-COUNT");
            redisCommands.set(i + "-MUL-" + AppConfig.DEFAULT_KEY, i + "," + AppConfig.DEFAULT_VALUE);
        }


        // 删除刚才添加的数据
        for (int i = 0; i < count; i++) {
            redisCommands.del(i + "-MUL-" + AppConfig.DEFAULT_KEY);
        }


        // 执行事务
        TransactionResult result = redisCommands.exec();

        System.out.println("事务执行结果:" + result.get(0));
        System.out.println("命令执行结果:" + rf);

        redisClient.shutdown();
    }


    /**
     * 同步设置与获取数据的基本用法
     * <p>
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void syncSetAndGetData(){
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取同步执行命令(这一步很关键)
        RedisCommands<String, String> redisCommands = redisClient.connect().sync();

        // 设置数据
        String setResult = redisCommands.set(AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        System.out.println(setResult);


        // 读取数据
        String getResult = redisCommands.get(AppConfig.DEFAULT_KEY);
        System.out.println(getResult);

        // 删除数据,可以传递多个key.
        redisCommands.del(AppConfig.DEFAULT_KEY);

        /*
         * 关闭此客户端并同步关闭所有打开的连接。
         * 调用此方法后客户端的操作被丢弃。
         * 关机时间为2秒,超时时间为15秒。
         */
        redisClient.shutdown();
    }


    /**
     * 同步设置与获取数据的高级用法.
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void syncAdvancedSetAndGetData() {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);
        // 获取同步执行命令(这一步很关键)
        RedisCommands<String, String> redisCommands = redisClient.connect().sync();

        // 编解码器
        StringCodec codec = StringCodec.UTF8;
        // 状态消息输出
        StatusOutput sot = new StatusOutput<>(codec);

        // 添加命令

        Object response = redisCommands.dispatch
                (CommandType.SET, sot,
                        new CommandArgs<>(codec).addKey(AppConfig.DEFAULT_KEY)
                                .addValue(AppConfig.DEFAULT_VALUE));


        System.out.println(response);

        ///

        // 获取刚才设置的结果
        Object getInfo = redisCommands.dispatch(CommandType.GET, sot,
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY));

        // 下面会显示上面设置的值:
        System.out.println(getInfo);

        // 删除键值
        System.out.println("删除结果(已删除的键数量): " + redisCommands.dispatch(CommandType.DEL, new IntegerOutput(codec),
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY)).toString());

        /*
         * 关闭这个客户端并关闭所有打开的连接。 调用关机后应该丢弃客户端。 关机没有安静的时间和2秒的超时。
         */
        redisClient.shutdown();
    }

Spring Data Redis和Lettuce搭配使用

请注意,该示例为一个Spring Boot项目.

部分示例代码如下:

   private static final String DEFAULT_KEY = "SUANCAIYU.XYZ-SPRINGBOOT";


    @Autowired
    private StringRedisTemplate stringRedisTemplate;



    @RequestMapping("writeSync")
    private String writeSync(){
        int length=5000;
        for (int i = 0; i <length ; i++) {
            stringRedisTemplate.opsForValue().set(DEFAULT_KEY+i,i+DEFAULT_KEY+i);
        }
        System.out.println("abc");
        return "sync";
    }



    @RequestMapping("asyncRead")
    private String read() {
        return stringRedisTemplate.execute((RedisCallback<String>) redisConnection -> {
            System.out.println(redisConnection.setCommands().sMembers(DEFAULT_KEY.getBytes()).stream().map(x->new String(x)).collect(Collectors.joining(",")));
            return "success";
        });
    }

    @RequestMapping("asyncWrite")
    private String write() {
        stringRedisTemplate.execute((RedisCallback<String>) redisConnection -> {
            Long add = redisConnection.setCommands().sAdd(DEFAULT_KEY.getBytes(), "test123456".getBytes());
            if (add > 0) {
                return "success";
            }
            return "faild";
        });
        return "write";
    }

完整源码地址在: Gitee仓库