spring-redis.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:redis/redis.properties" />
    <!--注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive
        而是maxTotal,而且没有maxWait属性 -->
    <!--redis连接池配置-->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--最大空闲数-->
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <!--连接池的最大数据库连接数-->
        <property name="maxTotal" value="${redis.maxTotal}"/>
        <!--建立连接的等待时间-->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
        <!--去除连接的最小空闲时间,默认1800000毫秒(30分钟)-->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 -->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 -->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
        <property name="testOnBorrow" value="true"></property>
        <property name="testOnReturn" value="true"></property>
        <property name="testWhileIdle" value="true"></property>
    </bean>

    <!--redis连接工厂-->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
           destroy-method="destroy">
        <property name="poolConfig" ref="jedisPoolConfig"></property>
        <!--IP地址-->
        <property name="hostName" value="${redis.host.ip}"></property>
        <!--端口号-->
        <property name="port" value="${redis.port}"/>
        <!--如果Redis设置有密码 -->
        <property name="password" value="${redis.password}"/>
        <!--客户端超时时间单位是毫秒 -->
        <property name="timeout" value="${redis.timeout}"></property>
        <property name="usePool" value="true"/>
    </bean>

    <!-- 键值序列化器设置为String 类型 -->
    <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    <bean id="jackson2JsonRedisSerializer"
          class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
        p:connection-factory-ref="jedisConnectionFactory"
        p:keySerializer-ref="stringRedisSerializer"
        p:valueSerializer-ref="stringRedisSerializer"
        p:hashKeySerializer-ref="stringRedisSerializer"
        p:hashValueSerializer-ref="jackson2JsonRedisSerializer">
    </bean>
</beans>

pom.xml依赖文件

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.smart</groupId>
  <artifactId>RedisDemo</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>RedisDemo</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <spring.version>4.3.1.RELEASE</spring.version>
    <redis.version>2.9.0</redis.version>
    <spring.data.redis.version>2.0.10.RELEASE</spring.data.redis.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>${spring.data.redis.version}</version>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>${redis.version}</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.10</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.1.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.1.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.1.0</version>
    </dependency>

  </dependencies>

</project>

View Code

redisDemo

package com.smart;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;

public class RedisStringDemo {

    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:redis/spring-redis.xml");

        // redisTemplate.opsForValue()所返回的对象可以操作简单的键值对,可以是字符串,也可以是对象,具体依据所配置的序列化方案
        // 在spring-redis-string.xml中key和value是指定的 stringRedisSerializer
        RedisTemplate<String,String> redisTemplate= (RedisTemplate<String, String>) context.getBean("redisTemplate");
        //> set key1 value1
        redisTemplate.opsForValue().set("key1","value1");
        redisTemplate.opsForValue().set("key2","value2");

        String value1 = redisTemplate.opsForValue().get("key1");
        System.out.println(value1);

        Boolean success = redisTemplate.delete("key1");
        System.out.println("删除key1是否成功"+success);

        Long size = redisTemplate.opsForValue().size("key2");
        System.out.println("key2的长度"+size);

        //设置新值并返回旧值
        String oldValue = redisTemplate.opsForValue().getAndSet("key2", "new_value2");
        System.out.println("key2的旧值"+oldValue);

        String newValue = redisTemplate.opsForValue().get("key2");
        System.out.println("key2的新值"+newValue);

        //获取子字符串
        String subStr = redisTemplate.opsForValue().get("key2", 0, 3);
        System.out.println("subString :"+subStr);

        //将新的字符串value加入到原来key指向的字符串末尾
        Integer value = redisTemplate.opsForValue().append("key2", "_app");
        System.out.println("valie:"+value);

        String newValue2=redisTemplate.opsForValue().get("key2");
        System.out.println("key2: "+newValue2);

         Object result = redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                //Delete all keys of the currently selected database.
                connection.flushDb();
                return "flush db";
            }
        });
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        valueOperations.set("name","Jim");
        valueOperations.setIfAbsent("name","Jack");

        Map<String, String> map = new HashMap<>();
        map.put("alias","Jim");
        map.put("age","18");
        valueOperations.multiSet(map);
        valueOperations.append("alias","_data");
        valueOperations.increment("age",2);
    }
}

  实际工作中并不是那么用的,因为每一 个操作会尝试从连接池里获取 一 个新的 Redis 连接,多个命令应该使用SessionCallback 接口进行操作 。使用SessionCallBack这个接口,通过这个接口就可以把属于多个同一套命令放在同一个Redis连接中去执行

常见场景

缓存

  Redis作为缓存层, 绝大部分请求的数据都是从Redis中获取。由于Redis具有支撑高并发的特性, 所以缓存通常能起到加速读写和降低后端压力的作用

  

spring-data-redis 对应版本 spring data redis配置_spring_03

    springboot+ redis实现token机制

     user域

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.domain;

import lombok.Data;

@Data
public class User {
    private Integer id;

    private String username;

    private String password;

    public User(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public User() {

    }
}

View Code

     Dto域

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.domain;

public class Dto {
    private String token;

    private Long tokenCreatedDate;

    private Long tokenExpiryDate;

    private String isLogin;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public Long getTokenCreatedDate() {
        return tokenCreatedDate;
    }

    public void setTokenCreatedDate(Long tokenCreatedDate) {
        this.tokenCreatedDate = tokenCreatedDate;
    }

    public Long getTokenExpiryDate() {
        return tokenExpiryDate;
    }

    public void setTokenExpiryDate(Long tokenExpiryDate) {
        this.tokenExpiryDate = tokenExpiryDate;
    }

    public String getIsLogin() {
        return isLogin;
    }

    public void setIsLogin(String isLogin) {
        this.isLogin = isLogin;
    }
}

View Code

    RedisConfig

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String,String> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }
}

View Code

   RedisUtil

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    public void set(String key,String value){
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(key,value);
    }

    public void setex(String key,String value,int seconds){
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(key,value,seconds);
    }
}

View Code

   application.yml

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

##指定使用redis数据库索引(默认为0)
spring:
    redis:
      database: 0
      host: 127.0.0.1
      port: 6379
      password:

View Code

   UserService

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.service;

import com.smart.domain.User;

public interface UserService {
    User login(String username,String password);
}

View Code

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.service.impl;

import com.smart.domain.User;
import com.smart.service.UserService;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public User login(String username, String password) {
        return new User(1,username,password);
    }
}

View Code

   TokenService

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.service;

import com.smart.domain.User;

public interface TokenService {
    String generateToken(String userAgentStr,String username);

    void save(String token,User user);
}

View Code

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.smart.domain.User;
import com.smart.service.TokenService;
import com.smart.util.RedisUtil;
import nl.bitwalker.useragentutils.UserAgent;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

@Service("tokenService")
public class TokenServiceImpl implements TokenService {

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public String generateToken(String userAgentStr, String username) {
        //生成token(格式为token:设备-加密的用户名-时间-六位随机数)
        StringBuilder token = new StringBuilder("token:");
        UserAgent userAgent = UserAgent.parseUserAgentString(userAgentStr);
        if(userAgent.getOperatingSystem().isMobileDevice()){
            token.append("MOBILE-");
        }else{
            token.append("PC-");
        }
        token.append(DigestUtils.md5Hex(username)+"-");
        token.append(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())+"-");
        token.append(String.format("%06d",new Random().nextInt(100000)+1));
        return token.toString();
    }

    @Override
    public void save(String token, User user) {
        if(token.startsWith("token:PC")){
            redisUtil.setex(token, JSONObject.toJSONString(user),2*60*60);
        }else{
            redisUtil.set(token,JSONObject.toJSONString(user));
        }
    }
}

View Code

   UserController

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

package com.smart.controller;

import com.alibaba.fastjson.JSONObject;
import com.smart.domain.Dto;
import com.smart.domain.User;
import com.smart.service.TokenService;
import com.smart.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private TokenService tokenService;

    @GetMapping("/login")
    public String login(@RequestParam("username") String username,@RequestParam("password") String password, HttpServletRequest request){
        Dto dto = new Dto();
        User user = userService.login(username, password);
        if(user !=null){
            String userAgent = request.getHeader("user-agent");
            String token = tokenService.generateToken(userAgent, username);
            tokenService.save(token,user);

            dto.setIsLogin("true");
            dto.setToken(token);
            dto.setTokenCreatedDate(System.currentTimeMillis());
            dto.setTokenExpiryDate(System.currentTimeMillis()+2*60*60);
        }else{
            dto.setIsLogin("false");
        }
        return JSONObject.toJSONString(dto);
    }

}

View Code

   pom

spring-data-redis 对应版本 spring data redis配置_xml

spring-data-redis 对应版本 spring data redis配置_spring_02

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.smart</groupId>
    <artifactId>redistoken</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redistoken</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>nl.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.2.4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

View Code

计数器

  使用Redis作为计数的基础工具, 可以实现快速计数、查询缓存的功能, 同时数据可以异步落地到其他数据源。

限流

  举个例子,对某个接口在1分钟内限制调用10次

session共享

  使用Redis将用户的Session进行集中管理,在这种模式下只要保证Redis是高可用和扩展性的, 每次用户更新或者查询登录信息都直接从Redis中集中获取

   

spring-data-redis 对应版本 spring data redis配置_redis_26

Lettuce

  Lettuce 和 Jedis 的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。

  在 pom.xml 中spring-boot-starter-data-redis的依赖,Spring Boot2.x 后底层不在是Jedis

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

  由于Spring Boot2.x 的改动,连接池相关配置需要通过spring.redis.lettuce.pool或者 spring.redis.jedis.pool 进行配置

spring:
  #指定配置环境
  profiles:
     active: test
  devtools:
      restart:
        enabled: true
        #添加需要restart目录文件
        additional-paths: src/main/java
        #排除不需要restart的目录文件
        exclude: static/**
  redis:
      host: localhost
      password:
      database: 0
      timeout: 10000ms
#     Redis默认有16个分片,配置具体使用的分片,默认值为0
      database: 0
#     连接池最大的连接数(使用负值表示没有限制)默认 8
      lettuce:
          pool:
            max-active: 8
            #     连接池最大阻塞等待时间(使用负值表示没有限制)默认 -1
            max-wait: -1ms
            #     连接池中最大空闲连接 默认 8
            max-idle: 8
            #      连接池中最小空闲连接 默认 0
            min-idle: 0

自定义Template

  默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,开发中是不友好的,所以自定义模板是很有必要的,当自定义模板又想使用String存储这时候就可以使用StringRedisTemplate的方式,它们并不冲突

package com.smart.boot_mybatis.configuration;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisCacheAutoConfiguration {

    @Bean
    public RedisTemplate<String,Serializable> redisTemplate(LettuceConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

 测试:

package com.smart.boot_mybatis;

import com.smart.boot_mybatis.domain.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.Serializable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

@RunWith(SpringRunner.class)
@SpringBootTest
public class redisTest {
    private static final Logger log = LoggerFactory.getLogger(redisTest.class);
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate<String, Serializable> redisCacheTemplate;

    @Test
    public void get(){
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        IntStream.range(0,1000).forEach(i->executorService.execute(()->
                stringRedisTemplate.opsForValue().increment("aa",1)));
        stringRedisTemplate.opsForValue().set("k1","v1");
        String v1=stringRedisTemplate.opsForValue().get("k1");
        log.info("[字符缓存结果] - [{}]", v1);
        String key="user:1";
        redisCacheTemplate.opsForValue().set(key,new User(1L,"u1","jing"));
        User user = (User) redisCacheTemplate.opsForValue().get(key);
        log.info("[对象缓存结果] - [{}]", user);
    }
}