2019独角兽企业重金招聘Python工程师标准>>>

  • 摘要:概述Jedis是Redis官方推荐的Java连接开发工具。要在Java开发中使用好Redis中间件,必须对Jedis熟悉才能写成漂亮的代码。这篇文章不描述怎么安装Redis和Reids的命令,只对Jedis的使用进行对介绍。1.基本使用Jedis的基本使用非常简单,只需要创建Jedis对象的时候指定host,port,password即可。当然,Jedis对象又很多构造方法,都大同小异,只是对应和Redis连接的socket的参数不一样而已。Jedisjedis=newJed
  • 概述 

    Jedis是Redis官方推荐的Java连接开发工具。要在Java开发中使用好Redis中间件,必须对Jedis熟悉才能写成漂亮的代码。这篇文章不描述怎么安装Redis和Reids的命令,只对Jedis的使用进行对介绍。

    1. 基本使用

    Jedis的基本使用非常简单,只需要创建Jedis对象的时候指定host,port, password即可。当然,Jedis对象又很多构造方法,都大同小异,只是对应和Redis连接的socket的参数不一样而已。

    Jedis jedis = new Jedis("localhost", 6379);//指定Redis服务Host和port 
    jedis.auth("xxxx"); //如果Redis服务连接需要密码,制定密码 
    String value = jedis.get("key"); //访问Redis服务 
    jedis.close(); //使用完关闭连接

    Jedis基本使用十分简单,在每次使用时,构建Jedis对象即可。在Jedis对象构建好之后,Jedis底层会打开一条Socket通道和Redis服务进行连接。所以在使用完Jedis对象之后,需要调用Jedis.close()方法把连接关闭,不如会占用系统资源。当然,如果应用非常平凡的创建和销毁Jedis对象,对应用的性能是很大影响的,因为构建Socket的通道是很耗时的(类似数据库连接)。我们应该使用连接池来减少Socket对象的创建和销毁过程。

    2. 连接池使用

    Jedis连接池是基于apache-commons pool2实现的。在构建连接池对象的时候,需要提供池对象的配置对象,及JedisPoolConfig(继承自GenericObjectPoolConfig)。我们可以通过这个配置对象对连接池进行相关参数的配置(如最大连接数,最大空数等)。

    JedisPoolConfig config = new JedisPoolConfig(); 
    config.setMaxIdle(8); 
    config.setMaxTotal(18); 
    JedisPool pool = new JedisPool(config, "127.0.0.1", 6379, 2000, "password"); 
    Jedis jedis = pool.getResource(); 
    String value = jedis.get("key"); 
    ...... 
    jedis.close(); 
    pool.close();

    使用Jedis连接池之后,在每次用完连接对象后一定要记得把连接归还给连接池。Jedis对close方法进行了改造,如果是连接池中的连接对象,调用Close方法将会是把连接对象返回到对象池,若不是则关闭连接。可以查看如下代码

    @Override 
    public void close() { //Jedis的close方法 
    if (dataSource != null) { 
    if (client.isBroken()) { 
    this.dataSource.returnBrokenResource(this); 
    } else { 
    this.dataSource.returnResource(this); 

    } else { 
    client.close(); 


    //另外从对象池中获取Jedis链接时,将会对dataSource进行设置 
    // JedisPool.getResource()方法 
    public Jedis getResource() { 
    Jedis jedis = super.getResource(); 
    jedis.setDataSource(this); 
    return jedis; 

    3. 高可用连接

    我们知道,连接池可以大大提高应用访问Reids服务的性能,减去大量的Socket的创建和销毁过程。但是Redis为了保障高可用,服务一般都是Sentinel部署方式(可以查看我的文章详细了解)。当Redis服务中的主服务挂掉之后,会仲裁出另外一台Slaves服务充当Master。这个时候,我们的应用即使使用了Jedis连接池,Master服务挂了,我们的应用奖还是无法连接新的Master服务。为了解决这个问题,Jedis也提供了相应的Sentinel实现,能够在Redis Sentinel主从切换时候,通知我们的应用,把我们的应用连接到新的 Master服务。先看下怎么使用。

    注意:Jedis版本必须2.4.2或更新版本

    Set sentinels = new HashSet<>(); 
    sentinels.add("172.18.18.207:26379"); 
    sentinels.add("172.18.18.208:26379"); 
    JedisPoolConfig config = new JedisPoolConfig(); 
    config.setMaxIdle(5); 
    config.setMaxTotal(20); 
    JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels, config); 
    Jedis jedis = pool.getResource(); 
    jedis.set("jedis", "jedis"); 
    ...... 
    jedis.close(); 
    pool.close();

    Jedis Sentinel的使用也是十分简单的,只是在JedisPool中添加了Sentinel和MasterName参数。Jedis Sentinel底层基于Redis订阅实现Redis主从服务的切换通知。当Reids发生主从切换时,Sentinel会发送通知主动通知Jedis进行连接的切换。JedisSentinelPool在每次从连接池中获取链接对象的时候,都要对连接对象进行检测,如果此链接和Sentinel的Master服务连接参数不一致,则会关闭此连接,重新获取新的Jedis连接对象。

    public Jedis getResource() { 
    while (true) { 
    Jedis jedis = super.getResource(); 
    jedis.setDataSource(this); 
    // get a reference because it can change concurrently 
    final HostAndPort master = currentHostMaster; 
    final HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient().getPort()); 
    if (master.equals(connection)) { 
    // connected to the correct master 
    return jedis; 
    } else { 
    returnBrokenResource(jedis); 


    }

    当然,JedisSentinelPool对象要时时监控RedisSentinel的主从切换。在其内部通过Reids的订阅实现。具体的实现看JedisSentinelPool的两个方法就很清晰

    private HostAndPort initSentinels(Set sentinels, final String masterName) { 
    HostAndPort master = null; 
    boolean sentinelAvailable = false; 
    log.info("Trying to find master from available Sentinels..."); 
    for (String sentinel : sentinels) { 
    final HostAndPort hap = HostAndPort.parseString(sentinel); 
    log.fine("Connecting to Sentinel " + hap); 
    Jedis jedis = null; 
    try { 
    jedis = new Jedis(hap.getHost(), hap.getPort()); 
    //从RedisSentinel中获取Master信息 
    List masterAddr = jedis.sentinelGetMasterAddrByName(masterName); 
    sentinelAvailable = true; // connected to sentinel... 
    if (masterAddr == null || masterAddr.size() != 2) { 
    log.warning("Can not get master addr, master name: " + masterName + ". Sentinel: " + hap + "."); 
    continue; 

    master = toHostAndPort(masterAddr); 
    log.fine("Found Redis master at " + master); 
    break; 
    } catch (JedisException e) { 
    // it should handle JedisException there's another chance of raising JedisDataException 
    log.warning("Cannot get master address from sentinel running @ " + hap + ". Reason: " + e + ". Trying next one."); 
    } finally { 
    if (jedis != null) { 
    jedis.close(); 



    if (master == null) { 
    if (sentinelAvailable) { 
    // can connect to sentinel, but master name seems to not monitored 
    throw new JedisException("Can connect to sentinel, but " + masterName + " seems to be not monitored..."); 
    } else { 
    throw new JedisConnectionException("All sentinels down, cannot determine where is " + masterName + " master is running..."); 


    log.info("Redis master running at " + master + ", starting Sentinel listeners..."); 
    //启动后台线程监控RedisSentinal的主从切换通知 
    for (String sentinel : sentinels) { 
    final HostAndPort hap = HostAndPort.parseString(sentinel); 
    MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort()); 
    // whether MasterListener threads are alive or not, process can be stopped 
    masterListener.setDaemon(true); 
    masterListeners.add(masterListener); 
    masterListener.start(); 

    return master; 
    }private void initPool(HostAndPort master) { 
    if (!master.equals(currentHostMaster)) { 
    currentHostMaster = master; 
    if (factory == null) { 
    factory = new JedisFactory(master.getHost(), master.getPort(), connectionTimeout, soTimeout, password, database, clientName, false, null, null, null); 
    initPool(poolConfig, factory); 
    } else { 
    factory.setHostAndPort(currentHostMaster); 
    // although we clear the pool, we still have to check the returned object 
    // in getResource, this call only clears idle instances, not 
    // borrowed instances 
    internalPool.clear(); 

    log.info("Created JedisPool to master at " + master); 

    }

    可以看到,JedisSentinel的监控时使用MasterListener这个对象来实现的。看对应源码可以发现是基于Redis的订阅实现的,其订阅频道为"+switch-master"。当MasterListener接收到switch-master消息时候,会使用新的Host和port进行initPool。这样对连接池中的连接对象清除,重新创建新的连接指向新的Master服务。

    4. 客户端分片

    对于大应用来说,单台Redis服务器肯定满足不了应用的需求。在Redis3.0之前,是不支持集群的。如果要使用多台Reids服务器,必须采用其他方式。很多公司使用了代理方式来解决Redis集群。对于Jedis,也提供了客户端分片的模式来连接“Redis集群”。其内部是采用Key的一致性hash算法来区分key存储在哪个Redis实例上的。

    JedisPoolConfig config = new JedisPoolConfig(); 
    config.setMaxTotal(500); 
    config.setTestOnBorrow(true); 
    List jdsInfoList = new ArrayList<>(2); 
    jdsInfoList.add(new JedisShardInfo("192.168.2.128", 6379)); 
    jdsInfoList.add(new JedisShardInfo("192.168.2.108", 6379)); 
    pool = new ShardedJedisPool(config, jdsInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN); 
    jds.set(key, value); 
    ...... 
    jds.close(); 
    pool.close();

    当然,采用这种方式也存在两个问题

    扩容问题: 因为使用了一致性哈稀进行分片,那么不同的key分布到不同的Redis-Server上,当我们需要扩容时,需要增加机器到分片列表中,这时候会使得同样的key算出来落到跟原来不同的机器上,这样如果要取某一个值,会出现取不到的情况。 
    单点故障问题: 当集群中的某一台服务挂掉之后,客户端在根据一致性hash无法从这台服务器取数据。

    对于扩容问题,Redis的作者提出了一种名为Pre-Sharding的方式。即事先部署足够多的Redis服务。 对于单点故障问题,我们可以使用Redis的HA高可用来实现。利用Redis-Sentinal来通知主从服务的切换。当然,Jedis没有实现这块。我将会在下一篇文章进行介绍。

    5. 小结

    对于Jedis的基本使用还是很简单的。要根据不用的应用场景选择对于的使用方式。 另外,Spring也提供了Spring-data-redis包来整合Jedis的操作,另外Spring也单独分装了Jedis(我将会在另外一篇文章介绍)。

    作者:曹金桂 链接:http://www.jianshu.com/p/a1038eed6d44 來源:简书

  • 以上是Jedis使用教程完整版的内容,更多 整版 使用 教程 jedis 的内容,请您使用右上方搜索功能获取相关信息。

转载于:https://my.oschina.net/airship/blog/2875170

Jedis使用教程完整版相关推荐

  1. python入门教程完整版(懂中文就能学会)-Python入门教程完整版(懂中文就能学会)...

    不过小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今天又给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,小编该给大家介绍一下这套教程了,希望每个小伙伴都沉迷学习, ...

  2. python3入门与进阶笔记_16_变量进阶 — 黑马程序员《Python入门教程完整版》笔记...

    变量进阶(理解) - 黑马程序员<Python入门教程完整版>笔记 目标变量的引用 可变和不可变类型 局部变量和全局变量 01. 变量的引用变量 和 数据 都是保存在 内存 中的 在 Py ...

  3. JavaScript(基础、高级)笔记汇总表【尚硅谷JavaScript全套教程完整版】

    目   录 前言 JavaScript(基础+高级)配套资料下载 JavaScript 基础 学习地址 学习笔记 day 05(P001-P006)[2016.11.22] day 06(P007-P ...

  4. android+客户端+教程,Android新浪客户端开发教程完整版.pdf

    Android新浪客户端开发教程完整版 Android 新浪客户端开发教程新浪客户端开发教程 (完整版(完整版)) 新浪客户端开发教程新浪客户端开发教程 ((完整版完整版)) android开发我的新 ...

  5. VMware虚拟机去虚拟化完整版教程|永久过强壳VMP、SE壳、GK盾、TMD教程|VMware去虚拟化吾爱汇编论坛教程完整版

    VMware去虚拟化完整版教程|永久过强壳VMP.SE壳.GK盾.TMD|VMware去虚拟化吾爱汇编论坛教程完整版 教程内容: 实际效果:

  6. 阿哈c语言教程pdf,C++教程-完整版.pdf

    C教程-完整版 c++基础教程Beta 版 原作: Juan Soulie 翻译: Jing Xu (aqua) 英文原版 本教程根据Juan Soulie 的英文版c++教程翻译并改编. 本版为最新 ...

  7. python新手教程全套_Python入门教程完整版(懂中文就能学会)

    前几天给大家分享视频<python基础教程>受到了广泛的关注,有人不知道怎么领取,居然称小编为"骗子". 不过小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今 ...

  8. React-高级教程完整版

    React 高级教程完整版 这标题可能有点不太贴切或符合内容,从官方上来区分这部分内容确实属于高级部分,只是由于个人原因,在后面的一些章节并没有记录在列. 也为了承接上一篇,因此勉强将标题定位:&qu ...

  9. Python入门教程完整版

    今天本宝宝给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,我来给大家介绍一下这套教程,希望每个小伙伴都沉迷学习,无法自拔! 本套教程学习时间15天 1-3天内容:为Linu ...

最新文章

  1. CTFshow php特性 web131
  2. Java多线程(一):Runnable和Thread的基本用法
  3. 安装MySQL之后,在cmd中MySQL命令不能识别
  4. Redis Scan 命令
  5. JFrame小练习1
  6. 窗体之间传递值的几种方法
  7. SVProgressHUD的使用
  8. idea 导入spring 源码 踩坑记总结整理
  9. oracle 表空间达到32g,oracle表空间到32G后扩容
  10. 关于websocket兼容IE版本
  11. 用安卓软件MT管理器破解元气骑士内购,小白照着也可以成功!
  12. 【JavaScript】三种方式入手JS弹窗
  13. python读取yml文件
  14. Matlab 实现两种读取文件夹内所有图像的方法
  15. 计算机音乐吧粉刷匠,奥尔夫小班音乐活动:《粉刷匠》
  16. 微信支付:appid 与 openId 不配
  17. Windows_XP SP3 Profession 正版密钥
  18. 《University Calculus》-chaper8-无穷序列和无穷级数-比值审敛法
  19. 用户登陆成功修改SessionId
  20. C语言实现可伸缩的栈结构

热门文章

  1. 从茶叶蛋到互联网思维
  2. SLF4j+LOG4j
  3. ACCESS TOKEN
  4. Html篇-fieldset标签演示
  5. 研究项目: JBoss架构分析
  6. 史记.饭岛爱列传(转)
  7. 统计学习方法|逻辑斯蒂原理剖析及实现
  8. php wget,Linux_Linux下载工具wget和axel简介,Wget Wget是一个十分常用命令 - phpStudy
  9. win7一直提示格式化磁盘_win10磁盘分区操作步骤
  10. ICPC2008哈尔滨-A-Array Without Local Maximums