02--Redis在Java中的应用

1. 创建Maven父工程

例如:工程名为:07-jt-redis。
在父工程下再创建两个子工程。

1.2 创建 jt-jedis 子工程

添加依赖:

<dependencies>
        <!--jedis是java中操作redis的一组API-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--此依赖中的-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
    </dependencies>

1.3 创建 jt-template 子工程

添加依赖:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2. Jedis的应用

2.1 概述

Jedis是Java中操作redis的一个客户端,类似通过jdbc访问mysql数据库。

2.2 修改redis.conf配置文件

在java中远程连接redis,需要将redis.conf配置文件中的bind 127.0.0.1元素注释掉,并且将其保护模式(protected-mode)设置为no(redis3.0之后默认开启了这个策略) ,当修改了配置以后,一定要记得重启redis,然后再进行访问。

2.3 基础类型操作

在Jedis工程中定义单元测试类,进行测试方法。

package com.jt;

import com.google.gson.Gson;
import org.junit.Test;
import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class JedisTest {
    @Test
    public void testStringType() throws InterruptedException {
        //1.建立链接(与redis建立链接)
        Jedis jedis = new Jedis("192.168.64.129",6379);
        jedis.auth("123456");//密码认证
        String result = jedis.ping();
        System.out.println(result);
        //2.存储数据
        jedis.set("count", "1");
        //3.更新数据
        jedis.incr("count");
        jedis.expire("count", 2);//设置有效期为1秒
        //获取数据
       TimeUnit.SECONDS.sleep(1);//休眠1秒
        String count = jedis.get("count");
        System.out.println("购物车的数量:"+count);
        System.out.println("数据库的数据:"+jedis.get("a"));

        //释放资源
        jedis.close();
    }

    //String类型操作
    @Test
    public void testStringObject(){
        //1.建立链接(与redis建立链接)
        Jedis jedis = new Jedis("192.168.64.129",6379);
        jedis.auth("123456");//密码认证
        String result = jedis.ping();
        System.out.println(result);

        //2.基于字符串类型存储对象
        Map<String,Object> map = new HashMap<>();
        map.put("id",1001);
        map.put("name", "googleyyds");
        //将map对象转换为json格式的字符串(借用google公司的gson将对象转换为json字符串)
        Gson gson = new Gson();
        String jm = gson.toJson(map);
        jedis.set("Obj", jm);
        jm = jedis.get("obj");
        System.out.println("获得字符串为:"+jm);

        //map = gson.fromJson(jm, );
        jedis.close();
    }
    @Test
    //Hash散列表类型操作
    public void testHashType(){
        Jedis jedis = new Jedis("192.168.64.129",6379);
        jedis.auth("123456");//密码认证
        String result = jedis.ping();
        System.out.println(result);

        Map<String,String> map = new HashMap<>();
        map.put("id","1001");
        map.put("name", "googleyyds");
        jedis.hset("person", map);
        map = jedis.hgetAll("person");
        System.out.println(map);
        jedis.close();
    }

    //list列表类型操作
   @Test
    public void testListType(){
        Jedis jedis = new Jedis("192.168.64.129",6379);
        jedis.auth("123456");//密码认证
        String result = jedis.ping();
        System.out.println(result);

        jedis.lpush("l1","A","B","C");
        jedis.brpop(60, "l1");
        jedis.brpop(60, "l1");
        jedis.brpop(60, "l1");
        System.out.println("集合已空");
        jedis.brpop(60, "l1");
        System.out.println("已被阻塞60s");
        jedis.close();
    }

    //Set集合类型操作
    @Test
    public void testSetType(){
        Jedis jedis = new Jedis("192.168.64.129",6379);
        jedis.auth("123456");//密码认证
        String result = jedis.ping();
        System.out.println(result);

        jedis.sadd("set1","A","B","C");
        Set<String> set = jedis.smembers("set1");
        System.out.println(set);
    }
}

3. JedisPool连接池的应用

我们直接基于Jedis访问redis时,每次获取连接,释放连接会带来很大的性能开销。所以,我们可以借助Jedis连接池,重用创建好的连接,来提高其性能,简易应用方式如下。

       //定义连接池的配置
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);//最大连接数
        config.setMaxIdle(20);//最大空闲时间
        //创建连接池
        JedisPool jedisPool = new JedisPool(config,"192.168.64.129",6379);
        //从池中获取一个链接
        Jedis resoure = jedisPool.getResource();
        //通过jedis连接存取数据
        resoure.auth("123456");
        resoure.set("class", "cgb2104");
        String clazz = resoure.get("class");
        System.out.println(clazz);
        //将链接返回池中
        resoure.close();
        //关闭连接池
        jedisPool.close();

4. RedisTemplate 应用

4.1 概述

RedisTemplate为SpringBoot工程中操作redis数据库的一个Java对象,此对象封装了对redis的一些基本操作。

4.2 创建启动类和配置文件

第一步:创建配置文件

spring:
  redis:
    host: 192.168.64.129
    port: 6379
    password: 123456

第二步:创建启动类

@SpringBootApplication
public class RedisApp {
    public static void main(String[] args) {
        SpringApplication.run(RedisApp.class, args);
    }
}

4.3 StringRedisTemplate 应用

StringRedisTemplate 是一个专门用于操作redis字符串类型数据的对象,其应用如下:

@SpringBootTest
public class StringRedisTemplateTest {
    @Autowired
    private StringRedisTemplate redisTemplate;  //主要用于操作字符串类型的数据

  // 测试是否连接上redis
   @Test
    public void testConnection(){
       RedisConnection connection =  redisTemplate.getConnectionFactory().getConnection();
       String ping = connection.ping();
       System.out.println(ping);
    }

    //测试String类型数据
    @Test
    public void testStringOper() {
        //1.获取字符串操作对象
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        //2.向redis存储数据
        valueOperations.set("id", "0");
        //3.更新redis中的数据
        valueOperations.increment("id");
        //4.获取redis中的数据
        String id = valueOperations.get("id");
        System.out.println(id);
    }
    @Test
    //测试list类型数据
    public void testListOper(){
        ListOperations<String, String> listOperations = redisTemplate.opsForList();
        listOperations.leftPush("list1", "100");
        listOperations.leftPushAll("list1","200","300","400","500");
        List list = listOperations.range("list1", 0, -1);
        System.out.println(list);
        Object ele = listOperations.rightPop("list1");//FIFO先进先出
        System.out.println(ele);
    }
    //测试set类型数据
    @Test
    public void testSetOper(){
         SetOperations setOperations= redisTemplate.opsForSet();
         setOperations.add("set1", "200","300","400","500","300");
         Set set = setOperations.members("set1");
         System.out.println(set);
         Long count =  setOperations.remove("set1","200","500");
         System.out.println("已移除元素:"+count+"个");
    }

    @Test
    //测试hash类型数据
    public void testHashOper(){
       HashOperations hashOperations =  redisTemplate.opsForHash();
       //存数据
        hashOperations.put("hash", "name","小张");
        hashOperations.put("hash", "age", "20");
       //取数据
        Object name =  hashOperations.get("hash","name");
        Object hh = hashOperations.entries("hash");
        System.out.println(name);
        System.out.println(hh);
    }
}

5. 项目工程的实践

5.1 单点登录系统

在分布式系统中,通过会有多个服务,我们登录了一个服务以后,再访问其它服务时,不想再登录,就需要有一套单独的认证系统,我们通常会称之为单点登录系统,在这套系统中提供一个认证服务器,服务完成用户身份认证,在一些中小型分布式系统中中,我们通常会借助redis存储用户的认证信息,例如:

实现代码

package com.jt.demos;
public class SSODemo01 {
    //校验session的有效性
    public static boolean isValidSession(String token){
        if(token==null||"".equals(token)){
            return false;
        }
        //1.建立连接
        Jedis jedis=new Jedis("192.168.126.130",6379);
        jedis.auth("123456");
        //2.从redis获取会话信息
        //2.1用户token对应的用户信息,将来可以是一个json格式的字符串
        String user=jedis.hget("session",token);//session这里表示会话对象
        System.out.println("user="+user);
        if(user==null||"".equals(user)){
            System.out.println("还没有登录");
            jedis.close();
            return false;
        }
        //3.判定是否登录超时
        String dateStr=jedis.hget("session",token+":expired");
        Date expired=new Date(Long.valueOf(dateStr));
        if(dateStr==null||"".equals(dateStr)||expired.before(new Date())) {
            System.out.println("登录超时");
            jedis.close();
            return false;
        }
        return true;
    };

    //执行登录操作
    public static String login(String username,String password){
        System.out.println("基于"+username+"/"+password+"进行登录");
        //1.创建token(一般可以用一个随机的字符串)
       String token= UUID.randomUUID().toString()
               .replace("-","");
       System.out.println(token);
       //2.创建有效登录时长
        Calendar calendar=Calendar.getInstance();
        calendar.add(Calendar.MINUTE,30);
       //3.存储会话信息
        Map<String,String> map=new HashMap<>();
        map.put(token,username);//username这里可以是包含更多用户信息的json字符串
        map.put(token+":expired",String.valueOf(calendar.getTimeInMillis()));
        Jedis jedis=new Jedis("192.168.126.130",6379);
        jedis.auth("123456");
        jedis.hset("session",map);
        jedis.close();
        return token;
    }
    public static void main(String[] args) {
        System.out.println("===访问系统资源===");
        //第一访问:访问redis,检查是否有有效的会话信息
        String token=null;
        boolean flag=isValidSession(token);
        System.out.println(flag?"第一次:已登录":"第一次:未登录");
        //执行登录
        if(!flag){
            System.out.print("执行登录:");
            token=login("jack","123456");
            //登录成功的这个token将来响应到客户端
        }
       //第二次访问:
        flag=isValidSession(token);
        System.out.println(flag?"第二次:已登录":"第二次:未登录");
        //第三次访问:
        flag=isValidSession(token);
        System.out.println(flag?"第三次:已登录":"第三次:未登录");
    }
}

5.2 投票系统

在很多系统设计中,都会有一个活动设计,开启一个活动之前,可以对这个活动的支持力度先进行一个调查,例如基于这个活动设计一个投票系统。

实现代码

package com.jt.demos;

import redis.clients.jedis.Jedis;

import java.util.Set;

/**
 * 投票系统演示:模拟基于某个活动的投票程序
 * 业务说明:一个用户只能投票一次(不允许重复)
 * 数据结构设计:基于redis的set类型进行数据存储
 */
public class VoteDemo01 {
    private static Jedis jedis=
            new Jedis("192.168.126.130",6379);
    static{
        jedis.auth("123456");
    }
    //进行投票(key为活动id,value为userId)
    static void vote(String activityId,String userId){
         jedis.sadd(activityId,userId);
    }
    //查看投票次数
    static Long getVoteCount(String activityId){
        Long count = jedis.scard(activityId);
        return count;
    }
    //查看这个活动被哪写用户投过票
    static Set<String> getVoteUsers(String activityId){
        Set<String> smembers = jedis.smembers(activityId);
        return smembers;
    }
    //检查这个用户是否已对这个活动投过票
    static Boolean checkVote(String activityId,String userId){
        Boolean flag = jedis.sismember(activityId, userId);
        return flag;
    }

    public static void main(String[] args) {
        //0.初始化
        String activityId="10001";
        String user1="201";
        String user2="202";
        String user3="203";
        //1.投票
        vote(activityId,user1);
        vote(activityId,user2);
        vote(activityId,user3);
        //2.获取投票次数
        Long voteCount = getVoteCount(activityId);
        System.out.println("投票次数:"+voteCount);
        //3.输出哪些人投过票
        Set<String> users=getVoteUsers(activityId);
        System.out.println(users);
        //4.检查用户是否投过票
        boolean flag=checkVote(activityId,user1);
        System.out.println(user1+":"+(flag?"已投过票":"还没投票"));
    }

}

5.3 秒杀队列

在设计一个秒杀或抢购系统时,为了提高系统的响应速度,通常会将用户的秒杀或抢购请求先存储到一个redis队列,这里我们就基于redis实现一个先进先出队列。

实现代码

package com.jt.demos;

import redis.clients.jedis.Jedis;

//秒杀队列演示
//描述逻辑中会将商品抢购信息先写到redis(以队列形式进行存储),
//因为写redis内存数据库要比写你的mysql数据库快很多倍
//算法:先进先出(FIFO)-体现公平性
public class SecondKillDemo01 {

    //商品抢购首先是入队
    static void enque(String msg){//入队
         Jedis jedis=new Jedis("192.168.126.130",6379);
         jedis.auth("123456");//没有认证不需要写这个语句
         jedis.lpush("queue",msg);
         jedis.close();
    }

    //底层异步出队(基于这个消息,生成订单,扣减库存,...)
    static String deque(){//出队
        Jedis jedis=new Jedis("192.168.126.130",6379);
        jedis.auth("123456");//没有认证不需要写这个语句
        String result=jedis.rpop("queue");
        jedis.close();
        return result;
    }
    public static void main(String[] args){
        //1.多次抢购(模拟在界面上多次点击操作)
        new Thread(){
            @Override
            public void run() {
                for(int i=1;i<=10;i++){//模拟页面上按钮点击
                    enque(String.valueOf(i));
                    try{Thread.sleep(100);}catch(Exception e){}
                }
            }
        }.start();

        //2.从队列取内容(模拟后台从队列取数据)
        new Thread(){
            @Override
            public void run() {
                for(;;){
                    String msg=deque();
                    if(msg==null)continue;
                    System.out.print(msg);
                }
            }
        }.start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值