Redis(4)客户端连接:Java

Redis

Redis(1)安装 & 配置

Redis(2)基本键值操作

Redis(3)常用维护操作

Redis(4)客户端连接:Java

Spring 集成 Reids(Spring-data-redis)



Redis 客户端连接:Java


Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:
  • 客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
  • 为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
  • 创建一个可读的文件事件用于监听这个客户端 socket 的数据发送

Redis 对于客户端连接相关的命令如下:
 
config get maxclients                  # 获取 redis-server 的客户端连接上限
config set maxclients millseconds_num  # 修改 redis-server 客户端连接上限
client list                   # 返回连接到 redis-server 的客户端列表
client getname                # 返回当前连接名称
client setname                # 设置当前的连名称
client pause 10000            # 挂起当前客户端连接 10000 毫秒
client kill addr ip_port        # 关闭指定端口的客户端连接,该 ip_port 必须是 client list 中列出的
client kill id client_id        # 关闭指定 id 的客户端连接,该 client_id 必须是 client list 中列出的

如果需要允许所有外部 IP 访问 Redis ,需要注销掉 redis 配置文件中的   bind 127.0.0.1(此时 redis 最好设置终端登陆密码);
当然也可以通过 bind 设置指定的多个 ip 访问 redis;


Java 客户端连接的基本代码

在编写运行 Java 客户端之前,请下载安装配置 redis 服务端: 01. Redis 安装 & 配置
Java 连接 redis-server,读写 redis-server 缓存数据的基本代码如下:
 
public class RedisJavaTest {
    private final static Logger log = LogManager.getLogger();
    public static void main(String[] args) throws IOException {
        /* 创建 redis 客户端 */
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.auth("assad");    //密码登陆
        log.debug(jedis.ping());          //进行登陆测试
        /* 从 redis-server 读写数据 */
        
        // 写入 Java String 和 基本类型,对应 redis string 储存类型
        jedis.set("key1","Hello world!");
        jedis.set("key2",2333+"");
        jedis.set("key3",2.333+"");
        jedis.set("key4",false+"");
        // 获取 redis string 类型
        String val1 = jedis.get("key1");
        int val2 = Integer.parseInt(jedis.get("key2"));
        double val3 = Double.parseDouble(jedis.get("key3"));
        boolean val4 = Boolean.parseBoolean(jedis.get("key4"));
        log.debug("key1: " + val1);
        log.debug("key2: " + val2);
        log.debug("key3: " + val3);
        log.debug("key4: " + val4);
        // 写入 Java List 类型, 对应 redis list 类型
        List<String> staffs = Arrays.asList("assad","vancy","canndy");
        for(String staff : staffs)
            jedis.rpush("staffs",staff);
        // 获取 redis list 类型
        List<String> staffList = jedis.lrange("staffs",0,-1);
        log.debug("staffs: " + staffList.stream().collect(Collectors.joining(", ")));
        // 写入 Java Set 类型,对应 redis set 类型
        Set<String> citys = new HashSet<>(Arrays.asList("Guangzhou", "Shanghai", "Beijing"));
        for(String city : citys)
            jedis.sadd("citys",city);
        // 获取 redis set 类型
        Set<String> cityList  = jedis.smembers("citys");
        log.debug("citys: " + cityList.stream().collect(Collectors.joining(", ")));
        // 写入Java Bean :写入为 redis hash 类型
        User user1 = new User("assad",22,"Guangzou");
        Map<String,String> userMap = new HashMap<>();
        userMap.put("name",user1.getName());
        userMap.put("age",user1.getAge()+"");
        userMap.put("city",user1.getCity());
        jedis.hmset("user:assad",userMap);
        // 获取 redis hash 类型(Java bean)
        Map<String,String> userMapped = jedis.hgetAll("user:assad");
        User user2 = new User(userMapped.get("name"), Integer.parseInt(userMapped.get("age")), userMapped.get("city"));
        log.debug("user: " + user2);
        // 获取所有 keys
        Set<String> keyset = jedis.keys("*");
        log.debug("keys: " + keyset.stream().collect(Collectors.joining(", ")));
    }
    static class User implements Serializable{
        public final static long serialVersionUID = 2333L;
        private String name;
        private int age;
        private String city;
        // getter,setter,contructor,toString
    }
}
运行输出如下:
 
19:41:51.189 [main] DEBUG redis.RedisJavaTest - PONG
19:41:51.192 [main] DEBUG redis.RedisJavaTest - key1: Hello world!
19:41:51.192 [main] DEBUG redis.RedisJavaTest - key2: 2333
19:41:51.192 [main] DEBUG redis.RedisJavaTest - key3: 2.333
19:41:51.192 [main] DEBUG redis.RedisJavaTest - key4: false
19:41:51.232 [main] DEBUG redis.RedisJavaTest - staffs: assad, vancy, canndy
19:41:51.234 [main] DEBUG redis.RedisJavaTest - citys: Guangzhou, Beijing, Shanghai
19:41:51.235 [main] DEBUG redis.RedisJavaTest - user: User{name='assad', age=22, city='Guangzou'}
19:41:51.235 [main] DEBUG redis.RedisJavaTest - keys: key1, key2, user:assad, key3, key4, citys, staffs

可以看到,其实 Jedis 对于 redis-server 键值操作的成员方法大部分都对应 redis 的键值操作指令,关于 redis 基本的键值操作指令: 02. Redis 基本键值操作

以上写入 Java Bean 是将 Java Bean 转换为 redis hash 的方案,此外还可以将 POJO 直接转换为二进制字节写入,如下编写一个工具类:
 
public class RedisUnit {
    // 序列化对象为字节
    public static byte[] serialize(Object object){
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    // 反序列化字节为对象
    public static Object unserialize(byte[] bytes){
        ByteArrayInputStream bais = null;
        ObjectInputStream ois  = null;
        try {
            bais = new ByteArrayInputStream(bytes);
            ois  = new ObjectInputStream(bais);
            Object obj = ois.readObject();
            return obj;
        }
        catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
向 redis-server 读写 POJO 的代码:
 
// 写入 POJO
User user = new User("assad",22,"Guangzou");
jedis.setbit(user.getName(), 0, RedisUnit.serialize(user));
        
// 获取 POJO
User user2 =  RedisUnit.unserialize(jedis.getbit("assad",0));



Java 客户端使用 Redis 管道技术

Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应,由此极大地提高数据传输速度;
以下使用 jedis 客户端使用和没有使用 pipeline 传输 100000 条 ping 指令的性能对比:
 
public class RedisPipeLineTest {
    private final static Logger log = LogManager.getLogger();
    public static void main(String[] args) throws IOException {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.auth("assad");
        final int commandCount = 100000;
        
        //没有使用 pipeline 的情况;
        long start = System.currentTimeMillis();
        for(int i = 0; i < commandCount; i++){
            jedis.ping();
        }
        long end = System.currentTimeMillis();
        log.debug("without pipeline: " + (end - start) / 1000f + "s");
        //使用 pipline 的情况;
        start = System.currentTimeMillis();
        Pipeline pipe = jedis.pipelined();
        for(int i = 0; i < commandCount; i++)
            pipe.ping();
        end = System.currentTimeMillis();
        log.debug("with pipeline: " + (end - start) / 1000f + "s");
        pipe.clear();
        pipe.close();
    }
}
结果输出如下,可以看到使用 pipeline 和不使用 pipeline 之间的巨大性能差异;
 
20:11:59.045 [main] DEBUG redis.RedisJavaTest - without pipeline: 3.908s
20:11:59.081 [main] DEBUG redis.RedisJavaTest - with pipeline: 0.033s


使用 JedisPool 管理 Jedis 资源

当程序中不只使用一个 Jedis 实例时,可以使用 JedisPool 资源池对 Jedis 资源进行管理,示例代码如下:
 
//JedisPool 参数对象
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50);
config.setMaxIdle(10);
config.setMaxWaitMillis(1000);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
String redisIp = "127.0.0.1";   //redis-server ip
int redisPort = 6379;           //redis-server port
//创建 Jedis 连接池
JedisPool jedisPool = new JedisPool(config, redisIp, redisPort);
//从 JedisPool 中获取 Jedis
Jedis jedis = jedisPool.getResource();
//use jedis ......
//关闭 Jedis,将 Jedis 资源归还给 JedisPool
jedis.close();
JedisPoolConfig 的常用设置参数如下:
  • blockWhenExhausted: 当连接池连接数耗尽时是否阻塞,默认 true(阻塞到超时),false 会报错;
  • lifo:  对连接是否启用 LIFO 后进先出策略,默认 true;
  • maxTotal: 最大连接数,默认 8;
  • maxIdle : 最大空闲连接数,默认 8;
  • maxWaitMillis: 获取连接时的最大等待毫秒数,默认 -1;
  • minIdle: 最小空闲连接数,默认 0;
  • minEvictableIdleTimeMillis: 逐出连接的最小空闲毫秒数,默认 1800000 ms(30 min);
  • numTestsPerEvictionRun: 每次逐出检查时,逐出的最大数目,默认 3;
  • softMinEvictableIdleTimeMillis:当连接对象空闲时间 > 该值,且 空闲连接 > 最大连接时,直接逐出该连接对象,不经过 minEvictableIdleTimeMillis 检查;
  • timeBetweenEvictionRunsMillis:逐出扫描的时间间隔,默认 -1(不运行逐出线程);
  • testOnBorrow:在获取连接时,是否检查其有效性,默认 false;
  • testOnReturn:在归还连接对象时,是否检查有效性,默认 false;
  • testWhileIdle在连接空闲时,是否检查有效性,默认 false;


当然可以将这些启动参数编写到一个 properties 资源文件中,通过 Java Properties 对象获取该文件中的属性,以保护连接数据隔离,类似如下:
 
Properties props = new Properties();
props.load(getClassLoader().getResourceAsStream("./jedisPool.properties"));
JedisPool jedisPool = new JedisPool(props.getProperties("redisServer.ip"),props.getProperties("redisServer.port"));
......





你可能感兴趣的:(Java,Redis)