淘宝的Tedis组件究竟是个啥呢?可能有一些朋友没有听过这个名字,有一些朋友会经常使用,那么今天我就来和大家深入分析一下,它的使用和原理。
一、Tedis简介
Tedis是另一个redis的java客户端,Tedis的目标是打造一个可在生产环境直接使用的高可用Redis解决方案。
特性如下: * 高可用:Tedis使用多写随机读做HA确保redis的高可用 * 高性能:使用特殊的线程模型,使redis的性能不限制在客户端 * 多种使用方式:如果你只有一个redis实例,并不需要tedis的HA功能,可以直接使用tedis-atomic;使用tedis的高可用功能需要部署多个redis实例使用tedis-group * 两种API:包括针对byte的底层api和面向object的高层api * 多种方便使用redis的工具集合:包括mysql数据同步到redis工具,利用redis做搜索工具等
二、Tedis能做啥
1、Tedis的原理 Tedis是对开源的Redis客户端组件Jedis进行的封装,在Jedis的基础上封装了一套更易于使用的byte api和object api接口,在部署上使用的是master-master结构,实现了多写与随机读的机制。既:每个写请求都会发到所有服务器上面,每个读请求随机读取一个服务器,当在某个服务器读取失败后,将该台服务器加到重试队列中,直到服务器恢复正常客户端请求才会重新访问到该服务器。
2、典型的应用场景
本图选自周成的《聚划算架构演化与系统优化》
二、Tedis的简单使用
可以先在Maven中依赖下面的jar包,当然还有更高版本的。
<dependency><groupId>com.taobao.common</groupId><artifactId>tedis-group</artifactId><version>1.1.0</version></dependency>
最简单的使用方式就是这样,只需要声明一个TedisGroup类并且初始化,然后就可以用ValueCommands进行set,get等操作了。唯一有点别扭的可能就是Tedis自己封装了一些命令类,需要我们自己去定义然后使用。
Group tedisGroup = new TedisGroup(appName, version);tedisGroup.init();ValueCommands valueCommands = new DefaultValueCommands(tedisGroup.getTedis());// 写入一条数据valueCommands.set(1, “test”, “test value object”);// 读取一条数据valueCommands.get(1, “test”);
三、Tedis的源码分析
1、Tedis的包结构
注:由于可以看出Tedis的结构实际分为了四个工程: tedis-atomic、tedis-common、tedis-group、tedis-replicator、tedis-search。
tedis-atomic
注:这个工程主要是集中一些Tedis的初始化类,像客户端类,连接类,初始化工厂类等等。
tedis-common
注:这个工程主要就是集中了整个Tedis的工具类,异常类,线程类和监控类等。
tedis-group
注:这个工程也是我们在源码分析中重点介绍的,里面汇集了Tedis最核心的代码,像集群,多写随机读等功能都在此实现。
tedis-relicator
注:不是本文重点,不做解释,感兴趣的读者可以自己了解。
tedis-search
注:不是本文重点,不做解释,感兴趣的读者可以自己了解。
2、tedis-group源码解析 我们看源码往往是通过使用的类开始的,也就是上面提到的TedisGroup类开始, * 我们先来看TedisGroup类结构图:
**注:**TedisGroup类是一个非常核心的类,有一个很重要的方法就是init和一个很重要的内部类RedisGroupInvocationHandler,在TedisGroup中依赖了二个重要的类,ConfigManager和RedisCommands。
ConfigManager解析
注:这个类的作用是: A、通过从Zookeeper、File、Diamond中解析有哪些Redis服务器,一共有几台服务器,超时时间和失败策略是如何。 B、写入多个Redis和读取Redis的策略是怎么样的,在这个类中是用的Router路由的方式。
RedisCommands解析
**注:**Tedis组件把Redis的操作命令全部封装到RedisCommands接口中,最底层的实现类是Tedis,通过这个类可以实现多个Redis的写和随机读等策略,Tedis类的部分源码如下:
@Overridepublic List<byte[]> sort(byte[] key, SortParams params) {try {if (params == null) {client.sort(key);} else {client.sort(key, params);}return client.getBinaryMultiBulkReply();} catch (Exception ex) {throw new TedisException(ex);}}@Overridepublic Boolean ping() {client.ping();return PONG.equals(client.getStatusCodeReply());}@Overridepublic Long del(byte[]… keys) {client.del(keys);return client.getIntegerReply();}@Overridepublic Boolean exists(byte[] key) {client.exists(key);return client.getIntegerReply() == 1;}@Overridepublic Set<byte[]> keys(byte[] pattern) {client.keys(pattern);return new HashSet<byte[]>(client.getBinaryMultiBulkReply());}@Overridepublic Boolean persist(byte[] key) {client.persist(key);return client.getIntegerReply() == 1;}@Overridepublic Boolean move(byte[] key, int dbIndex) {client.move(key, dbIndex);return client.getIntegerReply() == 1;}@Overridepublic byte[] randomKey() {client.randomKey();return client.getBinaryBulkReply();}@Overridepublic byte[] get(byte[] key) {client.get(key);return client.getBinaryBulkReply();}@Overridepublic Boolean set(byte[] key, byte[] value) {client.set(key, value);return OK.equals(client.getStatusCodeReply());}
RedisGroupInvocationHandler内部类解析
才会看到属于自己的那一片晴朗的天空。