- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
本系列针对redis进阶学习,因此对于基础部分不会详细讲述
# 这里我们在 /home/docker 下创建
mkdir /home/docker/redis/{conf,data} -p
cd /home/docker/redis
注意:后面所有的操作命令都要在这个目录/home/docker/redis下进行
# 获取 redis 的默认配置模版
# 这里主要是想设置下 redis 的 log / password / appendonly
# redis 的 docker 运行参数提供了 --appendonly yes 但没 password
wget https://gitee.com/hanxt/boot-launch/raw/master/src/main/resources/otherconfig/redis.conf -O conf/redis.conf
# 直接替换编辑
sed -i 's/logfile ""/logfile "access.log"/' conf/redis.conf;
sed -i 's/# requirepass foobared/requirepass 123456/' conf/redis.conf;
sed -i 's/appendonly no/appendonly yes/' conf/redis.conf;
sed -i 's/bind 127.0.0.1/bind 0.0.0.0/' conf/redis.conf;
sed -i是linux文件替换命令,替换格式为s/被替换的内容/替换之后的内容/
替换logfile ""为logfile “access.log”,指定日志文件名称为access.log---->指定日志文件的名称
替换# requirepass foobared为requirepass 123456,指定访问密码为123456—>配置登录密码,auth 123456
替换“appendonly no“为”appendonly yes”,开启appendonly模式–》持久化配置
替换绑定IP“bind 127.0.0.1”为“bind 0.0.0.0”—>任意ip可以访问
创建容器
# 创建并运行一个名为 myredis 的容器
docker run \
-p 6379:6379 \
-v $PWD/data:/data \
-v $PWD/conf/redis.conf:/etc/redis/redis.conf \
--privileged=true \
--name myredis \
-d redis:5.0.5 redis-server /etc/redis/redis.conf
# 查看活跃的容器
docker ps
# 如果没有 myredis 说明启动失败 查看错误日志
docker logs myredis
# 查看 myredis 的 ip 挂载 端口映射等信息
docker inspect myredis
# 查看 myredis 的端口映射
docker port myredis
docker exec -it myredis bash
redis-cli
命令的官方链接介绍从此处开始,就不贴了,感兴趣的自己打开官网查看
redis没有mysql中的table概念,但是如果所有key的一股脑扔到redis的同一个数据库里面,那也太乱了,因此我们可以通过下面这种Key的层级表示方法,来管理不同的key,很好的将不同的key进行了区分
Redis的可以允许有多个单词形成层级结构,多个单词之间用":"隔开,格式如下:
项目名:业务名:类型:id
例如:项目名叫dhy,有user和peo两种不同类型的数据,我们可以这样定义key
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.1.1</version>
</dependency>
/**
* 1.建立连接
* 2.设置密码
* 3.选择库
*/
@BeforeEach
public void connect() {
jedis=new Jedis("redis服务器ip地址",6379);
jedis.auth("password");
jedis.select(0);
}
@Test
public void testString(){
String res = jedis.set("name", "dhy");
System.out.println("res= "+res);
String name = jedis.get("name");
System.out.println("name= "+name);
}
@AfterEach
public void close()
{
if(jedis!=null)
{
jedis.close();
}
}
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedsi直接连接的方式
public class JedsiConnectionFactory {
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接
jedisPoolConfig.setMaxTotal(8);
//最大空闲连接
jedisPoolConfig.setMaxIdle(8);
//最小空闲连接
jedisPoolConfig.setMinIdle(0);
//设置最长等待时间 ms
jedisPoolConfig.setMaxWaitMillis(200);
jedisPool=new JedisPool(jedisPoolConfig,"redis服务器ip地址",6380,1000,"password");
}
/**
* 返回一个jedis对象
*/
public static Jedis getJedis() {
return jedisPool.getResource();
}
}
public class JedisTest {
private Jedis jedis;
/**
* 1.建立连接
* 2.设置密码
* 3.选择库
*/
@BeforeEach
public void connect() {
jedis=JedsiConnectionFactory.getJedis();
jedis.auth("126433zdh");
jedis.select(0);
}
@Test
public void testString(){
String res = jedis.set("name", "dhy");
System.out.println("res= "+res);
String name = jedis.get("name");
System.out.println("name= "+name);
}
@AfterEach
public void close()
{
if(jedis!=null)
{
jedis.close();
}
}
}
为什么都用了jedis连接池,还要调用jedis的close方法呢?
public void close() {
//如果当前jedis与一个连接池关联,那么他会被放入连接池中
//否则销毁当前连接
if (this.dataSource != null) {
Pool<Jedis> pool = this.dataSource;
this.dataSource = null;
if (this.isBroken()) {
pool.returnBrokenResource(this);
} else {
pool.returnResource(this);
}
} else {
this.connection.close();
}
}
<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>
spring:
redis:
host: 110.40.155.17
port: 6380
password: 126433zdh
lettuce:
#只有自动配置连接池的依赖,连接池才会生效
pool:
max-active: 8 #最大连接
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
max-wait: 100 #连接等待时间
springboot默认使用lettuce作为jedis客户端,因为在引入spring-boot-satrt-redis依赖的时候,会自动引入lettuce的依赖
@Autowired
RedisTemplate redisTemplate;
@Test
void testString(){
redisTemplate.opsForValue().set("age",18);
Object age = redisTemplate.opsForValue().get("age");
System.out.println(age);
}
为什么通过RedisTemplate存入redis的key存在乱码?
因为RedisTemplate会分别使用key和value的序列化器,对key和value进行序列化后存入redis,又因为默认使用的序列化器时jdk的objectoutputStream所以才会有乱码存在
源码追踪:
RedisTemplate内部有四个序列化器:
private @Nullable RedisSerializer keySerializer = null;
private @Nullable RedisSerializer valueSerializer = null;
private @Nullable RedisSerializer hashKeySerializer = null;
private @Nullable RedisSerializer hashValueSerializer = null;
RedisTemplate实现了initlizeBean接口,因此重写了afterPropertiesSet方法,会在RedisTemplate创建后的初始化阶段被调用:
@Override
public void afterPropertiesSet() {
//父类只要是配置好RedisConnectionFactory
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
//默认的序列化器是JdkSerializationRedisSerializer
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
//如果我们没有手动去指定这四个序列花旗,默认都会使用jdk的序列化器
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<>(this);
}
initialized = true;
}
下面再来看一下具体的set方法:
@Override
public void set(K key, V value) {
//rawValue就是具体把传入的值序列化为字节数组的过程
byte[] rawValue = rawValue(value);
execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
connection.set(rawKey, rawValue);
return null;
}
}, true);
}
下面来看一下rawValue:
byte[] rawValue(Object value) {
if (valueSerializer() == null && value instanceof byte[]) {
return (byte[]) value;
}
//获取到值序列化器,也就是valueSerializer ,然后进行序列化
return valueSerializer().serialize(value);
}
public byte[] serialize(@Nullable Object object) {
if (object == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
//通过内部的类型转换器,完成值类型到字节数组的转化,也就是序列化的过程
return serializer.convert(object);
} catch (Exception ex) {
throw new SerializationException("Cannot serialize", ex);
}
}
JdkSerializationRedisSerializer默认的转换器是SerializingConverter
public JdkSerializationRedisSerializer(@Nullable ClassLoader classLoader) {
this(new SerializingConverter(), new DeserializingConverter(classLoader));
}
下面来看一下SerializingConverter的convert方法实现:
@Override
public byte[] convert(Object source) {
try {
//还有一层:
return this.serializer.serializeToByteArray(source);
}
catch (Throwable ex) {
throw new SerializationFailedException("Failed to serialize object using " +
this.serializer.getClass().getSimpleName(), ex);
}
}
最终效果如下:
default byte[] serializeToByteArray(T object) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
serialize(object, out);
return out.toByteArray();
}
我们需要替换redisTemplate的默认序列化器
上面已经知道了redisTemplate底层默认使用的是jdk序列化器,因此我们最好替换它,String类型可以使用StringRedisSerializer序列化器,底层就是直接调用String.getBytes(),然后对于javabean对象,我们可以使用jackson来进行序列化
我们下面来自定义RedisTemplate的序列化方式:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
{
//创建template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//key和hashKey采用String序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
//value和hashValue用JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
如果你的key也想存对象,那么就不要使用RedisSerializer.string(),转而使用jsonRedisSerializer
我们来测试一下bean对象的存储
@SpringBootTest(classes = com.dhy_zk.financialSystem.Main.class)
public class RedisTest {
@Autowired
RedisTemplate redisTemplate;
@Test
void testString(){
Peo peo = new Peo("大忽悠", 18);
redisTemplate.opsForValue().set("大忽悠",peo);
redisTemplate.opsForValue().get("大忽悠");
}
}
反序列化时,可以根据@Class字段将JSON字符串转换为指定的对象
Jackson2JsonRedisSerializer底层的反序列源码如下:
public T deserialize(@Nullable byte[] bytes) throws SerializationException {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
//objectMapper相信各位应该都使用过---这里javaType就是从上面的@Class属性解析而来的
return (T) this.objectMapper.readValue(bytes, 0, bytes.length, javaType);
} catch (Exception ex) {
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
}
}
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认都是String方式,我们可以直接使用
@SpringBootTest(classes = com.dhy_zk.financialSystem.Main.class)
public class RedisTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
//JSON
private static final ObjectMapper mapper=new ObjectMapper();
@Test
void testString() throws JsonProcessingException {
//准备对象
Peo peo = new Peo("大忽悠", 18);
//手动序列化
String string = mapper.writeValueAsString(peo);
//写入数据到redis
stringRedisTemplate.opsForValue().set("peo:1",string);
//读取数据
String str = stringRedisTemplate.opsForValue().get("peo:1");
//反序列化
Peo peo1 = mapper.readValue(str, Peo.class);
}
}
HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();
hashOperations.put("dhy","name","大忽悠");
hashOperations.put("dhy","age","19");
Map<Object, Object> eles = hashOperations.entries("dhy");
System.out.println(eles);
MySQL表的增删改查(进阶) 1. 数据库约束 约束类型 说明 示例 NULL约束 使用NOT NULL指定列不为空 name varchar(20) not null, UNIQUE唯一约束 指定
多线程(进阶) 1. 常见的锁策略 1.1 乐观锁 悲观锁 乐观锁 : 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改数据,但是在对数据提交更新的时候,再去判断这个数据在这个期间是否有别人对
我相信在正确编码的系统中-错误(作为错误或异常)应该是不可能的(DB/memcached服务器故障导致查询失败)。我们的代码不应依赖任何假设才能正常工作,并且应尽可能地证明其正确性。 但是,为了确保我
1. 前言 泛型代码让你能根据你所定义的要求写出可以用于任何类型的灵活的、可复用的函数。你可以编写出可复用、意图表达清晰、抽象的代码。 泛型是 Swift 最强大
一、创建质量配置及关联项目 1.新建一个java代码质量配置 2.为配置添加规则 确认有4条规则了 为项目更换扫描配置 二、创建质量阈关联项目 1.
完整jenkinsfile 、sharelibrary 及jenkins配置见最后 一、gitlab push分支自动匹配 1.添加Generic Webhook插件参数,获取本次提交的分支信息
1.gitlab创建新应用 2.jenkins安装gitlab插件 3.插件安装完成后全局安全配置中使用并配置gitlab认证 4.注销重新登录后自动使用gitlab当前登录
一、部署jenkins master 1.创建Deployment YAML文件 apiVersion: apps/v1 kind: Deployment metadata: name: je
一、docker安装nexus wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum clean all
一、新建library文件 build.groovy package org.devops // 构建类型 def Build(buildType,buildShell){
一、制品获取 1.安装及配置插件 配置插件(jenkins项目中) 2.选择对应的制品 3.修改jenkins file // 新增以下代码 String artifactU
1.github创建OAuth 2.jenkins安装并配置github认证插件 jenkins配置使用github认证 3.注销重新登录
一、添加测试Maven项目 1.新建一个gitlab项目 2.导入simple-java-maven-app仓库代码(可以去github或者Gittree上都有) 3.配置mvn 国内源
一、添加AnsiColor插件 二、查看插件语法 1.打开任意pipline项目配置,找到流水线语法,并点击 跳转连接,选择插件,查看帮助 三、修改sharelibrary脚本,优
一、Pipeline概念 1 node/agent(节点) 节点是一个机器,可以是Jenkins的master节点也可以是slave节点。通过node指定当前job运行的机器(这个是脚本式语法)。
一、插件备份和恢复 1.安装备份插件 重启系统后查看 2.配置周期备份 点击进入,点击Settings Backup only builds marked to keep
一、.部署LDAP 这里使用容器部署,手动部署参考:https://www.cnblogs.com/panwenbin-logs/p/16101045.html 1.安装docker wget -
由于sonarqube开源版本不支持多分支管理,在扫描所有分支的时候都会指定同一个sonar项目,不便于我们查看 一、下载开源插件 项目地址:https://github.com/mc1arke/
一、手动测试 注意此版本已经内置包含Java语言扫描插件,不再需要单独安装 1.clone代码 git clone git@192.168.1.128:root/demo-maven-serv
我有下一种情况。 从 PHP 表单中我只获得公司 ID 我需要使用该公司 ID 排列所有用户名 我需要数组并将具有该用户名的所有日志导出到表 我的问题是,当我尝试下一步时: $sql2 = "SELE
我是一名优秀的程序员,十分优秀!