preface
Redis. IO /clients Java client (redis. IO /clients)
The Spring operator Redis provides a template method called RedisTemplate. Is this an official Spring client? No, Spring defines a connection factory interface, RedisConnectionFactory. This interface has many implementations, such as: JedisConnectionFactory, JredisConnectionFactory, LettuceConnectionFactory, SrpConnectionFactory.
In other words, RedisTemplate encapsulates other existing clients. Prior to Spring Boot2.x, RedisTemplate uses Jedis by default, and after 2.x, it uses Lettuce by default. So the bottom line is ultimately these three clients
This article source at: github.com/xuhaoj/redi…
Jedis
Basic usage
The official website is github.com/redis/jedis. Jedis is the most familiar and commonly used client. You can create Jedis connections directly without using the RedisTemplate.
public static void main(String[] args) {
Jedis jedis = new Jedis("39.103.144.86".6379);
// If the password is set
//jedis.auth("xushuaige");
jedis.set("jackxu"."shuaige");
System.out.println(jedis.get("jackxu"));
jedis.close();
}
Copy the code
Source in the com/XHJ jedis/client/BasicTest. Java
The connection pool
One problem with Jedis, however, is that threads are not safe when multiple threads use the same connection. One solution is to use connection pooling and create a different connection for each request, based on the Apache Common Pool implementation.
Jedis’s connection pool has three implementations: JedisPool, ShardedJedisPool, and JedisSentinelPool, all of which use getResource to get a connection from the connection pool.
/** * Common connection pool */
public static void ordinaryPool(a) {
JedisPool pool = new JedisPool("39.103.144.86".6379);
Jedis jedis = pool.getResource();
jedis.set("jackxu"."Handsome boy");
System.out.println(jedis.get("jackxu"));
}
/** * Shard pool */
public static void shardedPool(a) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
// Redis server
JedisShardInfo shardInfo1 = new JedisShardInfo("192.168.44.181".6379);
/ / the connection pool
List<JedisShardInfo> infoList = Arrays.asList(shardInfo1);
ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, infoList);
ShardedJedis jedis = jedisPool.getResource();
jedis.set("jackxu"."Sharding test");
System.out.println(jedis.get("jackxu"));
}
/** * sentry connection pool */
public static void sentinelPool(a) {
String masterName = "redis-master";
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.44.186:26379");
sentinels.add("192.168.44.187:26379");
sentinels.add("192.168.44.188:26379");
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels);
pool.getResource().set("jackxu"."The sentinel" + System.currentTimeMillis() + "Connection pool");
System.out.println(pool.getResource().get("jackxu"));
}
Copy the code
Jedis function is relatively perfect, Redis official features all support, such as publish and subscribe, transaction, Lua script, client sharding, sentinel, cluster, pipeline and so on.
Jedis requires all Sentinel addresses to connect to Sentinel, whereas Cluster requires only any master or slave address to connect to Sentinel.
A distributed lock
Jedis distributed lock implementation, you can see the introduction of several common distributed lock writing method, this article is also written by me.
Pipeline
Redis. IO /topics/pipe…
When we say that Redis is single-threaded, we mean that Redis requests are single-threaded, and only after the corresponding end of the previous command will the next command be processed. If you need to operate 100,000 keys at a time, the client and server will interact 100,000 times, and the queuing time plus the network communication time will be very slow.
How to solve this problem? Similar to Kafka and MySQL, a pipeline assembles a set of commands and sends them to the Redis server for execution. Pipeline caches all commands in a queue and sends multiple commands to the server in a single connection.
Let’s do the experiment to test, using pipeline method in get and set to use pipeline method. Write the set comparison first:
public static void main(String[] args) {
Jedis jedis = new Jedis("39.103.144.86".6379);
long t1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
jedis.set("commonBatch" + i, "" + i);
}
long t2 = System.currentTimeMillis();
System.out.println("Normal set time:" + (t2 - t1) + "ms");
Pipeline pipelined = jedis.pipelined();
long t3 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipelined.set("pipelineBatch" + i, "" + i);
}
pipelined.syncAndReturnAll();
long t4 = System.currentTimeMillis();
System.out.println("Pipeline set time:" + (t4 - t3) + "ms");
}
Copy the code
I waited for a few minutes to finish the normal set, so I couldn’t wait to close it. Interested partners can try how long it took, but the pipeline method only took 4 seconds!
Next, test the speed of get
public static void main(String[] args) {
new Thread(() -> {
Jedis jedis = new Jedis("39.103.144.86".6379);
Set<String> keys = jedis.keys("pipelineBatch*");
List<String> result = new ArrayList();
long t1 = System.currentTimeMillis();
for (String key : keys) {
result.add(jedis.get(key));
}
for (String src : result) {
//System.out.println(src);
}
System.out.println("Direct get time:" + (System.currentTimeMillis() - t1));
}).start();
new Thread(() -> {
Jedis jedis = new Jedis("39.103.144.86".6379);
Set<String> keys = jedis.keys("pipelineBatch*");
List<Object> result = new ArrayList();
Pipeline pipelined = jedis.pipelined();
long t1 = System.currentTimeMillis();
for (String key : keys) {
pipelined.get(key);
}
result = pipelined.syncAndReturnAll();
for (Object src : result) {
//System.out.println(src);
}
System.out.println("Pipeline get time:" + (System.currentTimeMillis() - t1));
}).start();
}
Copy the code
The normal GET is still stuck, whereas the Pipeline get took only 14 seconds.
The client-buffer of jedis-pipeline has a limit of 8192bytes. If the client stack exceeds 8 meters, it will be sent to the server. Pipeline has no limit on the number of entries, but the command may be limited by the size of the TCP packet. If some operations require immediate redis operation results such scenarios are not suitable for pipeline, pipeline can be used for real-time and success requirements are not high.
Source in the com/XHJ jedis/pipeline/PipelineGet. Java, com/XHJ/jedis/pipeline/PipelineSet Java
Lettuce
IO, lettuce, compared to Jedis, lettuce overcomes the problem of thread insecurity. Lettuces is a scalable thread-safe Redis client that supports synchronous, asynchronous and Reactive modes.
Lettuce is built based on the Netty framework and supports all the advanced functions of Redis, such as publishing and subscription, transactions, Lua scripting, Sentinel, clustering, Pipeline support connection pooling.
A synchronous invocation
public static void main(String[] args) {
// Create a client
RedisClient client = RedisClient.create("Redis: / / 39.103.144.86:6379");
// Thread-safe long connection, automatically reconnection when lost
StatefulRedisConnection<String, String> connection = client.connect();
// Obtain the synchronization command. The default timeout period is 60 seconds
RedisCommands<String, String> sync = connection.sync();
// Send a get request to get the value
sync.set("jackxu"."shuaige");
String value = sync.get("jackxu");
System.out.println(value);
// Close the connection
connection.close();
// Close the client
client.shutdown();
}
Copy the code
Code in the com/XHJ/lettuce/LettuceSyncTest Java
The asynchronous call
Asynchronous results are wrapped in RedisFuture, which provides a number of callback methods
public static void main(String[] args) {
RedisClient client = RedisClient.create("Redis: / / 39.103.144.86:6379");
// Thread-safe long connection, automatically reconnection when lost
StatefulRedisConnection<String, String> connection = client.connect();
// Get the asynchronous execution command API
RedisAsyncCommands<String, String> commands = connection.async();
/ / get RedisFuture < T >
commands.set("jackxu"."shuaige");
RedisFuture<String> future = commands.get("jackxu");
try {
String value = future.get(60, TimeUnit.SECONDS);
System.out.println(value);
} catch(Exception e) { e.printStackTrace(); }}Copy the code
Code in the com/XHJ/lettuce/LettuceASyncTest Java
RedisTemplate
Oracle is the default client for Spring boot2. x, replacing Jedis. We don’t need to use it alone after the integration, we can just call Spring’s RedisTemplate operation, and we don’t need to worry about creating and closing the connection.
So let’s see how it works, let’s configure the RedisTemplate.
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// Serialize using fastjson
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
// fastJsonRedisSerializer is used for serialization of values
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// Use StringRedisSerializer for key serialization
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
returntemplate; }}Copy the code
Get set is simple to use when injected.
@Resource
private RedisTemplate<String, Object> redisTemplate;
//set
redisTemplate.opsForValue().set(key, value);
//get
redisTemplate.opsForValue().get(key);
Copy the code
Code in the com/XHJ/lettuce/RedisConfig. Java, com/XHJ/lettuce/RedisUtil Java
Redisson
Redisson.org, github.com/redisson/re…
introduce
Redisson is a Java in-memory Data Grid implemented on the basis of Redis. It provides distributed and extensible Java Data structures, such as distributed Map, List, Queue, Set, etc. You don’t need to run a service implementation yourself.
Netty based implementation, using non-blocking IO, high performance; Supports asynchronous requests. Support connection pooling, pipeline, LUA Scripting, Redis Sentinel, Redis Cluster; There is no support for transactions, and LUA Scripting is officially recommended to replace them. Master/slave, sentinel, and cluster are all supported. Spring can also configure and inject RedissonClient.
A distributed lock
See also the article Introduction to some common distributed lock writing methods.
Different from Jedis, Redisson is not a pure Redis client, but a distributed service based on Redis implementation, which can realize some distributed data structures.
Handwriting client
Three common and popular clients have been introduced. We have an idea. Can we write a client by hand? The answer is yes.
First of all, we know that Redis listens to the port number of 6379 by default, and we can establish a connection through TCP. In Java, we can communicate with TCP through Socket. The second is to communicate with the Redis server using RESP (Redis Serialization Protocol), which is a special message format in which each command ends with \r\ N (carriage return + newline).
The sending or responding message needs to be encoded in this format, and the receiving message needs to be decoded in this format. This encoding format is also used in AOF files. Let’s use the packet capture tool to see what the protocol looks like.
caught
DST ==39.103.144.86 and tcp.port in {6379}; Jedis = get set
Go ahead and open the packet capture tool
Let’s look at the set command
The actual packets sent are
Where *3 represents three parameters set Jackxu shuaige, $3 represents three characters set, $6 represents six characters Jackxu, $7 means that shuaige is seven characters *3\r\n$3\r\nSET\r\n$6\r\njackxu\r\n$7\r\nshuaigeCopy the code
Let’s look at the get command
The actual packets sent are
*2 here represents the three arguments get Jackxu, $3 means get is three characters, and $6 means Jackxu is six characters *2\r\n$3\r\nGET\r\n$6\r\njackxuCopy the code
In fact, the command, parameters and their length are concatenated with \r\n.
Handwriting client
Once we are familiar with the above principles, we can start writing the client by hand.
- Establishing a Socket Connection
- OutputStream writes data (sends commands to the server)
- InputStream reads data (receives data from the server)
/ * * *@author jackxu
*/
public class MyClient {
private Socket socket;
private OutputStream write;
private InputStream read;
public MyClient(String host, int port) throws IOException {
socket = new Socket(host, port);
write = socket.getOutputStream();
read = socket.getInputStream();
}
/** * implements the set method **@param key
* @param val
* @throws IOException
*/
public void set(String key, String val) throws IOException {
StringBuffer sb = new StringBuffer();
// Represent three parameters (set key value)
sb.append("* 3").append("\r\n");
// The length of the first argument (set)
sb.append("$3").append("\r\n");
// The contents of the first argument
sb.append("SET").append("\r\n");
// The length of the second key (variable, dynamically obtained)
sb.append("$").append(key.getBytes().length).append("\r\n");
// The contents of the second argument key
sb.append(key).append("\r\n");
// The length of the third argument value (variable, dynamic)
sb.append("$").append(val.getBytes().length).append("\r\n");
// The contents of the third argument value
sb.append(val).append("\r\n");
// Send the command
write.write(sb.toString().getBytes());
byte[] bytes = new byte[1024];
// Receive the response
read.read(bytes);
System.out.println("set response:" + new String(bytes));
}
/** * implements get method **@param key
* @throws IOException
*/
public void get(String key) throws IOException {
StringBuffer sb = new StringBuffer();
// Represents two parameters
sb.append("* 2").append("\r\n");
// The length of the first argument (get)
sb.append("$3").append("\r\n");
// The contents of the first argument
sb.append("GET").append("\r\n");
// The length of the second argument key
sb.append("$").append(key.getBytes().length).append("\r\n");
// The second argument
sb.append(key).append("\r\n");
write.write(sb.toString().getBytes());
byte[] bytes = new byte[1024];
read.read(bytes);
System.out.println("get response:" + new String(bytes));
}
public static void main(String[] args) throws IOException {
MyClient client = new MyClient("39.103.144.86".6379);
client.set("myclient"."666");
client.get("myclient"); }}Copy the code
Code in the com/XHJ/myclient/myclient. Java
Set and get are set and get.
At the end
The final handwritten client is equivalent to the underlying principle, knowledge expansion, just like we handwritten a Tomcat, handwritten a RPC framework, are a simple version of things, easy for us to understand, but can not be used in production oh.
This article introduces three kinds of clients, some are more detailed, some are more simple, the specific selection and specific scenes we need to go to the official website in the process of using, this article can only be said to be a primary entry popular science paste, finally thank you for watching!