- 准备工作
- 创建工程
- 添加项目依赖
- Jedis的应用
- 简介
- 准备工作
- 快速入门实现
- 基础类型操作
- 连接池JedisPool应用
- RedisTemplate应用
- 简介
- 准备工作
- 快速入门实现
- StringRedisTemplate 应用
- RedisTemplate 应用
- 定制RedisTemplate对象(拓展)
- 项目工程实践
- 单点登陆
- 业务描述
- 关键代码实现
- 投票系统
- 业务描述
- 关键代码实现
- 秒杀队列
- 业务描述
- 关键代码实现
- 分布式id
- 业务描述
- 关键代码实现
- 购物车简易实现
- 业务描述
- 关键代码实现
- 总结(Summary)
创建maven父工程,例如03-sca-redis,并在此工程下创建两个子工程,一个为sca-jedis,一个为sca-tempate,例如:
添加项目依赖sca-jedis 工程依赖
redis.clients jedis 3.5.2 junit junit 4.12 test com.google.code.gson gson 2.8.6
添加sca-template工程依赖
Jedis的应用 简介org.springframework.boot spring-boot-dependencies 2.3.2.RELEASE import pom org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-test test
Jedis是Java中操作redis的一个客户端,类似通过jdbc访问mysql数据库。
准备工作第一步:从redis.io官方下载对应版本的redis.conf文件,地址如下:
https://redis.io/topics/config/
第二步:停止redis并删除挂载目录下(/usr/local/docker/redis01/conf)的redis.conf配置文件.-> (通过 docker inspect redis01 查看挂载目录)
第三步:将下载的redis.conf文件拷贝到redis挂载目录(/usr/local/docker/redis01/conf)
第四步:基于vim打开redis.conf文件,
注释 bind 127.0.0.1这一行,
并修改protected-mode的值修改为no或者直接注释掉.
第五步:重启redis服务,并检查启动日志(docker logs 容器id)
在Jedis工程中定义单元测试类,在类中定义单元测试方法:
基础类型操作在项目的src/test/java目录创建单元测类,例如:
package com.jt;
import com.google.gson.Gson;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class JedisTests {
@Test
public void testGetConnection(){//JDK8
Jedis jedis = new Jedis("192.168.126.129",6379);
//jedis.auth("123456");假如在redis.conf中设置了密码,还需执行这条语句
String ping = jedis.ping();
System.out.println(ping);///PONG
}
@Test
public void testStringOper() throws InterruptedException {
//1.建立链接
Jedis jedis = new Jedis("192.168.126.129",6379);
//2.向redis中存储数据
jedis.set("id", "1");
jedis.set("name", "Toney");
jedis.set("token", UUID.randomUUID().toString());
jedis.expire("token", 2);
//3.更新redis中指定数据
jedis.set("id", "100");
jedis.incr("id");
jedis.incrBy("id", 2);
jedis.decr("id");
//4.删除redis中指定数据
jedis.del("name");
//5.查看redis中的数据
String id = jedis.get("id");
String name = jedis.get("name");
String token = jedis.get("token");
Long tokenStrLen = jedis.strlen("token");
System.out.println("id = "+id);
System.out.println("name = "+name);
System.out.println("token = "+token);
System.out.println("token.length = "+tokenStrLen);
//Thread.sleep(2000);
//TimeUnit是Java中枚举类型,SECONDS为枚举类型的实例,sleep底层会调用Thread.sleep()方法
//TimeUnit.SECONDS.sleep(2);//休眠2秒
TimeUnit.SECONDS.sleep(2);
token = jedis.get("token");
System.out.println("token = "+token);
//6.释放资源
jedis.close();
}
@Test
public void testHashOper01(){
//1.建立链接
Jedis jedis = new Jedis("192.168.126.129",6379);
//2.向redis中存储数据
jedis.hset("user","id","100");
jedis.hset("user", "name","jack");
jedis.hset("user", "mobile","111111");
//3.更新redis中指定数据
jedis.hset("user", "id","101");
//4.删除redis中指定数据
jedis.hdel("user", "mobile");
//5.查看redis中的数据
String id = jedis.hget("user", "id");
System.out.println("user id = "+id);
Map user = jedis.hgetAll("user");
System.out.println("user = "+user);
//6.释放资源
jedis.close();
}
@Test
public void testStringJsonOper(){
//1.建立链接
Jedis jedis = new Jedis("192.168.126.129",6379);
//2.将对象转换为json字符串
Map map = new HashMap<>();
map.put("id", "101");
map.put("name", "tony");
Gson gson = new Gson();//Google公司提供
String jsonStr = gson.toJson(map);//将map转换为json串
System.out.println("jsonStr = "+jsonStr);
//3.将json字符串存储到redis
jedis.set("member",jsonStr);
//4.取出member对象的值
String member = jedis.get("member");
System.out.println("redis.member = "+member);
//5.将json字符串转换成map对象
map = gson.fromJson(member, Map.class);
System.out.println(map);
//6.释放资源
jedis.close();
}
@Test
public void testHashOper02() {
//1.建立链接
Jedis jedis = new Jedis("192.168.126.129", 6379);
//2.存储一个map对象
Map map = new HashMap<>();
map.put("x", "100");
map.put("y", "200");
jedis.hset("point", map);
//3.读取一个map对象
Map point = jedis.hgetAll("point");
System.out.println(point);//{x=100, y=200}
//4.释放资源
jedis.close();
}
@Test
public void testListOper01(){
//1.建立链接
Jedis jedis = new Jedis("192.168.126.129", 6379);
//2.存储数据(list集合有顺序,允许重复)
jedis.lpush("lst1", "A","B","C","C");
//3.修改数据
//3.1 首先获取要修改的元素的下标
Long index = jedis.lpos("lst1", "A");
//3.2 基于下标,修改元素内容
jedis.lset("lst1", index,"D");
//4.删除数据
jedis.rpop("lst1",20);
//5.查询数据
List lst1 = jedis.lrange("lst1", 0, -1);
System.out.println(lst1);
//6.释放资源
jedis.close();
}
@Test
public void testListOper02() {
//1.建立链接
Jedis jedis = new Jedis("192.168.126.129", 6379);
//2.存储数据(list集合有顺序,允许重复)
jedis.lpush("lst2", "A","B","C");
//3.基于阻塞式方式获取数据
jedis.brpop(50, "lst2");
jedis.brpop(50, "lst2");
List lst1 = jedis.brpop(50, "lst2");
System.out.println(lst1);//[lst2, C] 输入的内容为key和pop处的值
jedis.brpop(50, "lst2");
//4.释放资源
jedis.close();
}
@Test
public void testSetOper01(){
//1.建立链接
Jedis jedis=new Jedis("192.168.126.129",6379);
//2.存储数据(Set集合没有顺序,不允许重复)
jedis.sadd("set1", "A","A","B","C","D");
//3.修改数据
//4.删除数据
//jedis.spop("set1");
jedis.srem("set1", "B");
//5.查询数据
Set set1 = jedis.smembers("set1");
System.out.println(set1);
Long cards = jedis.scard("set1");
System.out.println(cards);
//5.释放资源
jedis.close();
}
}
连接池JedisPool应用
我们直接基于Jedis访问redis时,每次获取连接,释放连接会带来很大的性能开销,可以借助Jedis连接池,重用创建好的连接,来提高其性能,简易应用方式如下:
package com.jt;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolTests {
@Test
public void testJedisPool(){
//1.构建jedis连接池(JedisPool对象)
//1.1定义连接池配置
JedisPoolConfig config=new JedisPoolConfig();
config.setMaxTotal(16);//最大连接数,默认为8
config.setMaxIdle(60);//最大空闲时间(连接后续不用了,超出一定空闲时间要释放)
//config.setxxxx
//1.2定义连接的url和端口
String host="192.168.126.129";
int port=6379;
//1.3创建连接池
JedisPool jedisPool=
new JedisPool(config,host,port);
//2.从池中获取连接(jedis对象)
Jedis resource = jedisPool.getResource();
//3.执行redis操作
resource.set("pool", "JedisPool");
String pool = resource.get("pool");
System.out.println(pool);
//4.释放资源(不是关,而是将连接还回池中)
resource.close();
//5.关闭池(一般服务停止时关)
//jedisPool.close();
}
}
RedisTemplate应用
简介
RedisTemplate为SpringBoot工程中操作redis数据库的一个Java对象,此对象封装了对redis的一些基本操作。
准备工作第一步:创建工程配置文件application.yml,其内容如下:
spring:
redis:
host: 192.168.64.129 #写自己的ip
port: 6379
第二步:创建工程启动类,例如:
package com.jt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class,args);
}
}
快速入门实现
StringRedisTemplate 应用
StringRedisTemplate 是一个专门用于操作redis字符串类型数据的一个对象,其应用方式如下:
RedisTemplate 应用
RedisTemplate是一个专门用于实现对远端redis中复杂数据的操作的对应,应用案例如下:
定制RedisTemplate对象(拓展)
对于系统默认的RedisTemplate默认采用的是JDK的序列化机制,假如我们不希望实用JDK的序列化,可以采用的定制RedisTemplate,并采用自己指定的的序列化方式,例如:
项目工程实践 单点登陆 业务描述
在分布式系统中,通过会有多个服务,我们登录了一个服务以后,再访问其它服务时,不想再登录,就需要有一套单独的认证系统,我们通常会称之为单点登录系统,在这套系统中提供一个认证服务器,服务完成用户身份认证,在一些中小型分布式系统中中,我们通常会借助redis存储用户的认证信息,例如:
SSODemo01.java
package com.jt.redis;
import redis.clients.jedis.Jedis;
import java.util.UUID;
public class SSODemo01 {
public static String doLogin(String username,String password){
//1.执行用户身份校验
if(!"jack".equals(username))//将来这个jack来自数据库
throw new IllegalArgumentException("这个用户不存在");
//2.用户存在并且密码正确,表示用户是系统的合法用户
if(!"123456".equals(password))
throw new IllegalArgumentException("密码不正确");
//3.将合法用户信息存储到redis
Jedis jedis = new Jedis("192.168.126.129", 6379);
String token = UUID.randomUUID().toString();
jedis.set(token,username);
jedis.expire(token, 2);
jedis.close();
return token;//token
}
public static Object doGetResource(String token){
//1.检查用户是否已经登录
if(token == null || "".equals(token))
throw new IllegalArgumentException("请先登录");
Jedis jedis = new Jedis("192.168.126.129", 6379);
String username = jedis.get(token);
jedis.close();
//2.假如没有登录,则提示先登录
if(username == null)
throw new RuntimeException("登录超时或token无效,请重新登录");
//3.已登录则可以访问资源
System.out.println("继续访问资源");
//.....
return "the resource of user";
}
//假设这里的main方法为客户端
public static void main(String[] args) throws InterruptedException {
String token = null;
//第一次访问资源
// doGetResource(token);
//执行登录操作(将来认证要在认证中心实现)
token = doLogin("jack", "123456");
//第二次访问资源
doGetResource(token);
}
}
SSODemo02.java
package com.jt.redis;
import redis.clients.jedis.Jedis;
import java.util.Map;
import java.util.UUID;
public class SSODemo02 {
private static String token;
public static String doLogin(String username,String password){
//1.执行用户身份校验
if(!"jack".equals(username))//将来这个jack来自数据库
throw new IllegalArgumentException("这个用户不存在");
//2.用户存在并且密码正确,表示用户是系统的合法用户
if(!"123456".equals(password))
throw new IllegalArgumentException("密码不正确");
//3.将合法用户信息存储到redis
Jedis jedis = new Jedis("192.168.126.129", 6379);
String token = UUID.randomUUID().toString();
jedis.hset(token,"username",username);
jedis.hset(token,"status","1");
//...
jedis.expire(token, 2);
jedis.close();
return token;//token
}
public static Object doGetResource(String token){
//1.检查用户是否已经登录
if(token == null || "".equals(token))
throw new IllegalArgumentException("请先登录");
Jedis jedis = new Jedis("192.168.126.129", 6379);
Map map = jedis.hgetAll(token);
jedis.close();
//2.假如没有登录,则提示先登录
System.out.println("map = "+map);//map = {username=jack, status=1}
if(map.isEmpty())
throw new RuntimeException("登录超时或token无效,请重新登录");
//3.已登录则可以访问资源
System.out.println("继续访问资源");
//.....
return "the resource of user";
}
//假设这里的main方法为客户端
public static void main(String[] args) throws InterruptedException {
//第一次访问资源
// doGetResource(token);
//执行登录操作(将来认证要在认证中心实现)
token = doLogin("jack", "123456");
//第二次访问资源
//Thread.sleep(2000);
doGetResource(token);
}
}
投票系统
业务描述
在很多系统中设计中,都会有一个活动设计,开启一个活动之前,可以对这个活动的支持力度先进行一个调查,例如基于这个活动设计一个投票系统,例如:
package com.jt.redis;
import redis.clients.jedis.Jedis;
import java.util.Set;
public class VoteDemo01 {
static final String IP = "192.168.126.129";
static final int PORT = 6379;
public static Boolean isVote(String activityId,String userId){
//1.创建Jedis对象
Jedis jedis = new Jedis(IP,PORT);
//2.检查是否投票
Boolean flag = jedis.sismember(activityId, userId);
//3.释放资源
jedis.close();
//4.返回结果
return flag;
}
public static void doVote(String activityId,String userId){
//1.创建Jedis对象
Jedis jedis = new Jedis(IP,PORT);
//2.执行投票
jedis.sadd(activityId, userId);
//3.释放资源
jedis.close();
}
public static Long doGetVote(String activityId){
//1.创建Jedis对象
Jedis jedis = new Jedis(IP,PORT);
//2.查看活动的投票数
Long count = jedis.scard(activityId);
//3.释放资源
jedis.close();
//4.返回投票数量
return count;
}
public static Set doGetUser(String activityId){
//1.创建Jedis对象
Jedis jedis = new Jedis(IP,PORT);
//2.查看活动的投票数
Set smembers = jedis.smembers(activityId);
//3.释放资源
jedis.close();
//4.返回投票数量
return smembers;
}
public static void main(String[] args) {
//1.定义活动id
String activityId = "1001";
String user1 = "301";
String user2 = "302";
//2.进行投票
doVote(activityId, user1);
//3.进行投票检查
Boolean flag = isVote(activityId, user1);
Boolean flag1 = isVote(activityId, user2);
//4.获取投票的总数
Long aLong = doGetVote(activityId);
System.out.println(activityId + " 的总投票数为 " + aLong);
//5.获取参与投票的用户
Set users = doGetUser(activityId);
System.out.println(users);
//6.检查用户是否投过票
System.out.println(user1 + ":" + (flag?"已投过票":"还没投票"));
System.out.println(user2 + ":" + (flag1?"已投过票":"还没投票"));
}
}
秒杀队列
业务描述
在设计一个秒杀或抢购系统时,为了提高系统的响应速度,通常会将用户的秒杀或抢购请求先存储到一个redis队列,这里我们就基于redis实现一个先进先出队列,例如:
package com.jt.redis;
import redis.clients.jedis.Jedis;
import java.util.List;
public class SecondsKillDemo01 {
public static void enque(String request){
Jedis jedis = new Jedis("192.168.126.129", 6379);
//jedis.auth("123456");若有密码,则写此语句
jedis.lpush("queue-1",request);
jedis.close();
}
public static String deque(){
Jedis jedis = new Jedis("192.168.126.129", 6379);
//非阻塞式取数据
//return jedis.rpop("queue-1");
//阻塞式取数据
List list = jedis.brpop(60, "queue-1");
jedis.close();
return list!=null?list.get(1):null;//0为key,1为value
}
public static void main(String[] args) {
//1.构建生产者对象(购买商品的用户)
Thread t1 = new Thread(){
@Override
public void run() {
int i = 1;
for(;;){
enque("request-1"+i++);
try { Thread.sleep(1000);
}catch (Exception e){}
}
}
};
//2.构建消费者对象(处理请求的对象)
Thread t2 = new Thread(){
@Override
public void run() {
for(;;){
String request = deque();
if(request == null) continue;
System.out.println("process"+request);
}
}
};
//3.开启抢购活动
t1.start();
t2.start();
}
}
分布式id
业务描述
在分布式系统中,数据量将越来越大时,就需要对数据进行分表操作,但是,分表后,每个表中的数据都会按自己的节奏进行自增,很有可能出现ID冲突。这时就需要一个单独的机制来负责生成唯一ID,生成出来的ID也可以叫做 分布式ID,这里我们借助redis实现一个简易的分布式id进行实现,当然还有一些第三方的系统,可以帮你生成这样的id,可以自己进行拓展学习.
关键代码实现package com.jt.redis;
import redis.clients.jedis.Jedis;
public class IdGenerator {
public static Long getId(){
Jedis jedis=new Jedis("192.168.126.129", 6379);
//jedis.auth("123456");//密码
//incr方法用于对指定key的值进行递增,假如key不存在则创建
long id = jedis.incr("incrementId");
jedis.close();
return id;
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
new Thread(){
@Override
public void run() {
System.out.println(IdGenerator.getId());
}
}.start();
}
}
}
购物车简易实现
业务描述
简易购物车业务设计如图所示:
基础指令操作,例如:
1)向购物车添加商品 hset cart:101 2001 1 hset cart:101 2002 1 hset cart:101 2003 2 2)查看购物车商品 hgetall cart:101 3)删除购物车商品 hdel cart:101 2003 4)改变购物车某个商品的购买数量 hincrby cart:101 2002 2 hincrby cart:101 2002 -2关键代码实现
package com.jt.redis;
import redis.clients.jedis.Jedis;
import java.util.Map;
public class CartDemo01 {
static final String IP = "192.168.126.129";
static final int PORT = 6379;
public static void doAddCart(String userId,String productId,int num){
//1.创建Jedis对象
Jedis jedis = new Jedis(IP,PORT);
//2.基于hash结构存储商品信息
//hincrBy这个函数在key不存在时会自动创建key
jedis.hincrBy("cart:"+userId, productId, num);
//3.释放资源
jedis.close();
}
public static Map doViewCart(String userId){
//1.创建Jedis对象
Jedis jedis = new Jedis(IP,PORT);
//2.产看购物车信息
Map cart = jedis.hgetAll("cart:" + userId);
//3.释放资源
jedis.close();
//4.返回购物车商品信息
return cart;
}
public static void doClearCart(String userId,String... productIds){
//1.创建Jedis对象
Jedis jedis = new Jedis(IP,PORT);
//2.删除指定商品
jedis.hdel("cart:"+userId,productIds);
//3.释放资源
jedis.close();
}
public static void main(String args[]){
String userId = "101";
//1.购买商品时,将商品信息添加到购物车
doAddCart(userId,"2001",1);
doAddCart(userId,"2002",1);
doAddCart(userId,"2003",1);
//2.修改购物车商品的数量
doAddCart(userId, "2002", 1);
doAddCart(userId, "2003", 2);
//3.查看购物车商品信息
Map cart = doViewCart(userId);
System.out.println(cart);
//4.清空购物车
doClearCart(userId,"2001","2002","2003");
cart = doViewCart(userId);
System.out.println(cart);
}
}
总结(Summary)
本章节主要对Java中操作redis数据库的方式及API应用进行了分析和实践,具体方法的理解可以在实践中基于结果进行分析,逐步进行强化记忆。



