5

Redis的使用(二) - 童话述说我的结局

 2 years ago
source link: https://www.cnblogs.com/xing1/p/16365042.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、redis简单应用

       其实在写这个redis专题时我想了很久,我觉得redis没什么好说的,因为现在是个人都会用redis,但是我在写netty专题时发现,netty里面很多东西和概念有很多跟redis的很多应用和底层很相似和可以借鉴的地方,所以后来想想,还是写个专题来简单聊聊。按照我以前的习惯在写应用前我是要写一篇中间件的安装,但redis的安装这次不写了,因为安装过于简单,但是看这专题的朋友记得,我后面所写的所有内容是基于redis6版本的基础上进行写的。如果看过官网的朋友可以知道,redis6和以往版本最大的区别在于他引入了多线程IO,对于6以前的单线程redis来说,性能瓶颈主要在于网络的 IO 消耗, 所以新版本优化主要有两个方向:

  • 提高网络 IO 性能,典型的实现像使用 DPDK 来替代内核网络栈的方式
  • 使用多线程充分利用多核,典型的实现像 Memcached
协议栈优化的这种方式跟 Redis 关系不大,多线程特性在社区也被反复提了很久后终于在 Redis 6 加入多线程,Salvatore 在自己的博客 An update about Redis developments in 2019 也有简单的说明。但跟 Memcached 这种从 IO 处理到数据访问多线程的实现模式有些差异。Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua(一种轻量级脚本语言)、事务,LPUSH/LPOP(redis语法:将一个或多个值插入到列表头部(左边)、移出并获取列表的第一个元素(左边)) 等等的并发问题。整体的设计大体如下:
1287410-20220610224228236-1889451226.png

官网:https://spring.io/projects/spring-data-redis

 具体底层实现我会在后面篇幅会写,这里就不过多说明,下面就将springboot项目集成redis作一个简单的过程演示。

引入pom文件

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

修改application.yml文件

spring:
  redis:
    # redis数据库索引(默认为0),我们使用索引为3的数据库,避免和其他数据库冲突
    database: 0
    host: 192.168.0.23
    port: 6379
    password: 123456
    # redis连接超时时间(单位毫秒)
    timeout: 0
    # redis连接池配置
    jedis:
      pool:
        # 最大可用连接数(默认为8,负数表示无限)
        max-active: -1
        # 最大空闲连接数(默认为8,负数表示无限)
        max-idle: 2000
        # 最小空闲连接数(默认为0,该值只有为正数才有用)
        min-idle: 1
        # 从连接池中获取连接最大等待时间(默认为-1,单位为毫秒,负数表示无限)
        max-wait: -1
        # 配置空闲连接回收间隔时间,min-idle才会生效,否则不生效
        time-between-eviction-runs: 5000

RedisTemplate

1287410-20220610231527833-1081321010.png
@SpringBootTest
class SpringRedisApplicationTests {

    // 注入 RedisTemplate
    @Autowired
    private RedisTemplate redisTemplate;

    // String 类型
    @Test
    void testString () {
        redisTemplate.opsForValue().set("name", "ljx");
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println(name);
    }

    // Hash 类型
    @Test
    public void testHash () {
        redisTemplate.opsForHash().put("user1", "name", "clarence");
        redisTemplate.opsForHash().put("user1", "age", "25");
        Map map = redisTemplate.opsForHash().entries("user1");
        System.out.println(map);
    }

    // List 类型
    @Test
    public void testList () {
        redisTemplate.opsForList().leftPushAll("names", "xiaobai", "xiaohei", "xiaolan");
        List<String> names = redisTemplate.opsForList().range("names", 0, 3);
        System.out.println(names);
    }

    // Set 类型
    @Test
    public void testSet () {
        redisTemplate.opsForSet().add("set", "a", "b", "c");
        Set<String> set = redisTemplate.opsForSet().members("set");
        System.out.println(set);
    }

    // SortedSet 类型
    @Test
    public void testSortedSet () {
        redisTemplate.opsForZSet().add("class", "xiaobai", 90);
        Set aClass = redisTemplate.opsForZSet().rangeByScore("class", 90, 100);
        System.out.println(aClass);
        Set<ZSetOperations.TypedTuple<String>> set = new HashSet<> ();
        set.add(new DefaultTypedTuple<> ("xiaohei", 88.0));
        set.add(new DefaultTypedTuple<>("xiaohui", 94.0));
        set.add(new DefaultTypedTuple<>("xiaolan", 84.0));
        set.add(new DefaultTypedTuple<>("xiaolv", 82.0));
        set.add(new DefaultTypedTuple<>("xiaohong", 99.0));
        redisTemplate.opsForZSet().add("class", set);
        Set aClass1 = redisTemplate.opsForZSet().range("class", 0, 6);
        System.out.println(aClass1);
    }


}

二、序列化

1、默认是 JdkSerializationRedisSerializer

  • RedisTemplate 可以接收任意 Object 作为值写入 Redis,不过在写入前会把 Object 序列化为字节形式,默认是采用 JDK 序列化,上面的 demo 运行后得到的结果如下:
  • 缺点:可读性差;内存占用较大

2.1、添加配置文件,使用 String 序列化、Json 序列化

Redis 配置文件

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        // 创建 RedisTemplate 对象
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置连接工厂
        redisTemplate.setConnectionFactory(connectionFactory);
        // 设置 Key 的序列化 - String 序列化 RedisSerializer.string() => StringRedisSerializer.UTF_8
        redisTemplate.setKeySerializer( RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // 设置 Value 的序列化 - JSON 序列化 RedisSerializer.json() => GenericJackson2JsonRedisSerializer
        redisTemplate.setValueSerializer(RedisSerializer.json());
        redisTemplate.setHashValueSerializer(RedisSerializer.json());
        // 返回
        return redisTemplate;
    }
}

引入 Jackson 依赖

        <!--Jackson依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.4</version>
        </dependency>

添加 User 实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private Integer age;
}

案例

@SpringBootTest
public class RedisDemoApplicationTest2 {

    // 注入 RedisTemplate
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    void testString() {
        redisTemplate.opsForValue().set("name", "小白");
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println(name);
    }

    @Test
    void testSaveUser() {
        redisTemplate.opsForValue().set("user", new User("小白", 23));
        User user = (User) redisTemplate.opsForValue().get("user");
        System.out.println(user);
    }
}

运行结果

1287410-20220610233214846-503750542.png

StringRedisTemplate

  • 从上述 Demo 的运行结果可以看到,为了在反序列化时知道对象的类型,Json 序列化会将类的 class 类型写入 json 结果中存入 Redis,会带来额外的内存开销
  • 为了节省内存空间,我们并不会使用 json 序列化器来处理 value,而是统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化
  • spring 提供了一个 StringRedisTemplate 类,其 key 和 value 的序列化方式默认为 String 方式
引入 fastjson 依赖
<!--fastjson依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.62</version>
</dependency>

案例

@SpringBootTest
public class RedisDemoApplicationTest2 {

    // 注入 RedisTemplate
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    void testString() {
        redisTemplate.opsForValue().set("name", "小白");
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println(name);
    }

    @Test
    void testSaveUser() {
        redisTemplate.opsForValue().set("user", new User("小白", 23));
        User user = (User) redisTemplate.opsForValue().get("user");
        System.out.println(user);
    }
}
1287410-20220610233638239-727180445.png

 三、redis的请求通信协议

上面简单的演示了下redis的操作,接下来首先详细了解一下Redis Serialization Protocol(Redis序列化协议),这个是Redis提供的一种,客户端和Redis服务端通信传输的编码协议,服务端收到后,会基于这个约定编码进行解码。首先打开Wireshark工具,对VMnet8这个网络进行抓包(没有这工具可以自己下个),先在连接工具加一个假数据

1287410-20220611125600797-1052012692.png
  • 打开Wireshark工具,对VMnet8这个网络进行抓包

  • 增加过滤条件

    ip.dst_host==ip and tcp.port in {6379}
1287410-20220611125819454-1812667760.png

使用RDM工具连接到Redis Server进行key-value操作,比如执行 set name ljx通过Wireshark工具监控数据包内容,可以通过上图看到实际发出的数据包是:*3\r\n$3\r\nSET\r\n$4\r\nname\r\n$3\r\nljx

其中开头的*3这个数字中代表参数个数,我是set name ljx,所以表示三个参数;接着就是$3表示属性长度,$表示包含了3个字符。客户端和服务器发送的命令或数据一律以 \r\n (CRLF回车+换行)结尾。了解了这格式的意思接下来自己实现一个java客户端就非常容易了。

 3.1、客户端实现

在手写之前先看下Jedis源码是怎么实现的手写客户端代码,在Jedis中就有的,先看一下Jedis内部的实现源码:

protected Connection sendCommand(Protocol.Command cmd, byte[]... args) {
    try {
        this.connect();//建立Socket连接
        Protocol.sendCommand(this.outputStream, cmd, args);//封装报文并将报文写入流中
        ++this.pipelinedCommands;
        return this;
    } catch (JedisConnectionException var6) {
        JedisConnectionException ex = var6;
        try {
            String errorMessage = Protocol.readErrorLineIfPossible(this.inputStream);
            if (errorMessage != null && errorMessage.length() > 0) {
                ex = new JedisConnectionException(errorMessage, ex.getCause());
            }
        } catch (Exception var5) {
        }
        this.broken = true;
        throw ex;
    }
}

这段源码并不难找,使用Jedis的set方法,然后一直跟进去就可以。最终方法的位置是redis.clients.jedis.Connection.sebdCommand()。

从这个方法的内部实现就可以看出来其实就是通过Socket建立tcp连接,然后将命令和数据转换成RESP协议规范的报文格式,最后通过Socket将数据传入过去。知道这些对于自己写一个Jedis客户端是不是就有思路啦。基于对源码的借鉴,简易的Jedis实现如下:

public class CustomJedis {
    public static void main(String[] args) throws IOException {
        //建立socket连接
        Socket socket = new Socket();
        InetSocketAddress socketAddress = new InetSocketAddress("106.12.75.86", 6379);
        socket.connect(socketAddress, 10000);
        //获取scoket输出流,将报文转换成byte[]传入流中
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(command());
        //获取返回的输出流,并打印输出数据
        InputStream inputStream = socket.getInputStream();
        byte[] buffer = new byte[1024];
        inputStream.read(buffer);
        System.out.println("返回执行结果:" + new String(buffer));
    }
    //组装报文信息
    private static byte[] command() {
        return "*3\r\n$3\r\nSET\r\n$9\r\nuser:name\r\n$6\r\nitcrud\r\n".getBytes();
    }
}

但是这里需要注意,上面的实现方式是直接建立socket连接,Redis很多时候是设置密码认证的,如果这样的话上面的代码就需要改动啦。

改动后如下:

public class CustomJedisProd {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        InetSocketAddress socketAddress = new InetSocketAddress("106.12.75.86", 6379);
        socket.connect(socketAddress, 10000);
        OutputStream outputStream = socket.getOutputStream();
        //验证密码
        outputStream.write(auth());
        InputStream inputStream = socket.getInputStream();
        byte[] buffer = new byte[1024];
        inputStream.read(buffer);
        System.out.println("返回执行结果:" + new String(buffer));
        //发送数据
        outputStream.write(command());
        inputStream.read(buffer);
        System.out.println("返回执行结果:" + new String(buffer));
        inputStream.close();
        outputStream.close();
    }
    //验证
    private static byte[] auth(){
        return "*2\r\n$4\r\nAUTH\r\n$6\r\n123456\r\n".getBytes();
    }
    //组装报文信息
    private static byte[] command() {
        return "*3\r\n$3\r\nSET\r\n$9\r\nuser:name\r\n$6\r\nitcrud\r\n".getBytes();
    }
}
1287410-20220611132357429-541039116.png

 

1287410-20220611132645788-1651004528.png

 这样一个最简单版本就实现了,但是这里面的编码是写死的,每次报问要自己组装太麻烦,下面来进一步优化下:

定义常量池

public class CommandConstant {

    public static final String START="*";

    public static final String LENGTH="$";

    public static final String LINE="\r\n";

    public enum CommandEnum{
        SET,
        GET
    }
}

CustomClientSocket用来建立网络通信连接,并且发送数据指定到RedisServer。

public class CustomerRedisClientSocket {

    private Socket socket;

    private InputStream inputStream;

    private OutputStream outputStream;

    public CustomerRedisClientSocket(String ip,int port,String password){
        try {
            socket=new Socket(ip,port);
            inputStream=socket.getInputStream();
            outputStream=socket.getOutputStream();

            outputStream.write (  password.getBytes ());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void send(String cmd){
        try {
            outputStream.write(cmd.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public String read(){
        byte[] bytes=new byte[1024];
        int count=0;
        try {
            count=inputStream.read(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(bytes,0,count);
    }
}

封装客户端

public class CustomerRedisClient {

    private CustomerRedisClientSocket customerRedisClientSocket;

    public CustomerRedisClient(String host,int port,String password) {
        customerRedisClientSocket=new CustomerRedisClientSocket(host,port,password ("AUTH",password));

    }
    public String password(String key,String value){
        convertToCommand(null,key.getBytes(),value.getBytes());
        return convertToCommand(null,key.getBytes(),value.getBytes());
    }

    public String set(String key,String value){
        customerRedisClientSocket.send(convertToCommand(CommandConstant.CommandEnum.SET,key.getBytes(),value.getBytes()));
        return customerRedisClientSocket.read(); //在等待返回结果的时候,是阻塞的
    }

    public String get(String key){
        customerRedisClientSocket.send(convertToCommand(CommandConstant.CommandEnum.GET,key.getBytes()));
        return customerRedisClientSocket.read();
    }

    public static String convertToCommand(CommandConstant.CommandEnum commandEnum,byte[]... bytes){

        StringBuilder stringBuilder=new StringBuilder();

        if (commandEnum==null){
            stringBuilder.append(CommandConstant.START).append(bytes.length).append(CommandConstant.LINE);
        }else{
            stringBuilder.append(CommandConstant.START).append(bytes.length+1).append(CommandConstant.LINE);
            stringBuilder.append(CommandConstant.LENGTH).append(commandEnum.toString().length()).append(CommandConstant.LINE);
            stringBuilder.append(commandEnum.toString()).append(CommandConstant.LINE);
        }

        for (byte[] by:bytes){
            stringBuilder.append(CommandConstant.LENGTH).append(by.length).append(CommandConstant.LINE);
            stringBuilder.append(new String(by)).append(CommandConstant.LINE);
        }

        return stringBuilder.toString();
    }
}
public class MainClient {
    public static void main(String[] args) {
        CustomerRedisClient customerRedisClient=new CustomerRedisClient("124.71.33.75",6379,"ghy20200707redis");
        System.out.println(customerRedisClient.set("name","ljx"));
        System.out.println(customerRedisClient.get("ljx"));
    }
}

结果

1287410-20220611142035655-1681522878.png

 

1287410-20220611142044388-1980665795.png

 所有事物理解了本质后,实现起来其实一点都不难,通过上面两次优化,就实现了一个自己版本的客户端,但是实际开发过程中,客户端我们不用自己开发,官方推荐了以下三种客户端

 四、Reids的java客户端

4.1、客户端对比

 4.2.1、概念

  • Jedis:是老牌的Redis的Java实现客户端,提供了比较全面的Redis命令的支持,
  • Redisson:实现了分布式和可扩展的Java数据结构。
  • Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

4.2.2、优点

  • Jedis:比较全面的提供了Redis的操作特性
  • Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列
  • Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作

4.2.3、可伸缩

  • Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。
  • Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作
  • Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作
  • lettuce能够支持redis4,需要java8及以上。
  • lettuce是基于netty实现的与redis进行同步和异步的通信。

4.2.4、lettuce和jedis比较

  • jedis使直接连接redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个jedis实例增加物理连接 ;
  • lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,StatefulRedisConnection是线程安全的,所以一个连接实例可以满足多线程环境下的并发访问,当然这也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
  • Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
4.2.5、集成Jedis
引入pom
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
public class JedisTest {

    private Jedis jedis;

    @BeforeEach
    void setUp(){
        // 1、建立连接
        jedis = new Jedis("ip", 6379);
        // 2、设置密码
        jedis.auth("123456");
        // 3、选择库
        jedis.select(0);
    }


    @Test
    public void testString(){
        // 存入数据
        String result = jedis.set("name", "张三");
        System.out.println("result = " + result);
        // 获取数据
        String name = jedis.get("name");
        System.out.println(name);
    }

    @Test
    public void testHash(){
        // 插入 hash 数据
        jedis.hset("user:1", "name", "lisi");
        jedis.hset("user:1", "age", "21");

        // 获取
        Map<String, String> map = jedis.hgetAll("user:1");
        System.out.println(map);
    }

    @AfterEach
    void closeJedis(){
        if(jedis != null){
            jedis.close();
        }
    }
}

4.2.6、集成Lettuce

引入pom

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

配置yml

#Redis配置
spring:
  redis:
    database: 6  #Redis索引0~15,默认为0
    host: 127.0.0.1
    port: 6379
    password:  #密码(默认为空)
    lettuce: # 这里标明使用lettuce配置
      pool:
        max-active: 8   #连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms  #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 5     #连接池中的最大空闲连接
        min-idle: 0     #连接池中的最小空闲连接
    timeout: 10000ms    #连接超时时间(毫秒)

添加Redisson的配置参数读取类RedisConfig

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * RedisTemplate配置
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer ());//key序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer ());//value序列化
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

编写测试类RedisTest

@SpringBootTest
class SpringRedisApplicationTests {

    // 注入 RedisTemplate
    @Autowired
    private RedisTemplate redisTemplate;

    // String 类型
    @Test
    void testString () {
        redisTemplate.opsForValue().set("name", "ljx");
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println(name);
    }

    // Hash 类型
    @Test
    public void testHash () {
        redisTemplate.opsForHash().put("user1", "name", "clarence");
        redisTemplate.opsForHash().put("user1", "age", "25");
        Map map = redisTemplate.opsForHash().entries("user1");
        System.out.println(map);
    }

    // List 类型
    @Test
    public void testList () {
        redisTemplate.opsForList().leftPushAll("names", "xiaobai", "xiaohei", "xiaolan");
        List<String> names = redisTemplate.opsForList().range("names", 0, 3);
        System.out.println(names);
    }

    // Set 类型
    @Test
    public void testSet () {
        redisTemplate.opsForSet().add("set", "a", "b", "c");
        Set<String> set = redisTemplate.opsForSet().members("set");
        System.out.println(set);
    }

    // SortedSet 类型
    @Test
    public void testSortedSet () {
        redisTemplate.opsForZSet().add("class", "xiaobai", 90);
        Set aClass = redisTemplate.opsForZSet().rangeByScore("class", 90, 100);
        System.out.println(aClass);
        Set<ZSetOperations.TypedTuple<String>> set = new HashSet<> ();
        set.add(new DefaultTypedTuple<> ("xiaohei", 88.0));
        set.add(new DefaultTypedTuple<>("xiaohui", 94.0));
        set.add(new DefaultTypedTuple<>("xiaolan", 84.0));
        set.add(new DefaultTypedTuple<>("xiaolv", 82.0));
        set.add(new DefaultTypedTuple<>("xiaohong", 99.0));
        redisTemplate.opsForZSet().add("class", set);
        Set aClass1 = redisTemplate.opsForZSet().range("class", 0, 6);
        System.out.println(aClass1);
    }


}

4.2.7、集成Redisson

引入pom

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.16.0</version>
        </dependency>

yml文件我就不写了,和上面一样,下面简单写下测试类

@Configuration
public class RedissonConfig {

    @Autowired
    private RedisProperties redisProperties;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        String redisUrl = String.format("redis://%s:%s", redisProperties.getHost() + "", redisProperties.getPort() + "");
        config.useSingleServer().setAddress(redisUrl).setPassword(redisProperties.getPassword());
        config.useSingleServer().setDatabase(3);
        return Redisson.create(config);
    }

}
@RestController
@RequestMapping("/redisson")
public class RedissonController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/save")
    public String save(){
        stringRedisTemplate.opsForValue().set("key","redisson");
        return "save ok";
    }

    @GetMapping("/get")
    public String get(){
        return stringRedisTemplate.opsForValue().get("key");
    }

}

源码地址:https://gitee.com/TongHuaShuShuoWoDeJieJu/redis.git


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK