SpringBoot建立SSH通道整合S3Elasticache(Redis),并实现Redis多库切换

本文主要是介绍SpringBoot建立SSH通道整合S3Elasticache(Redis),并实现Redis多库切换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 一、背景
    • 1、问题
    • 2、解决
  • 二、建立SSH通道
    • 1、pom引入依赖
    • 2、创建sshconfig
    • 3、SSHConnection 程序
  • 三、Spring boot整合Redis
    • 1、引入依赖
    • 2、配置信息
    • 3、RedisConfig的编写(切库处理配置)
    • 4、Redis操作的工具类
  • 四、两个大坑
    • 1、 长时间未操作,连接重置
    • 2、长时间未操作,无法获取resource
  • 五、总结


一、背景

使用Spring Boot自带的redis框架,访问S3的Elasticache(Redis),并从Redis的多个DB中同时取数据。

1、问题

  • S3的Redis缓存服务,官方文档中指出Elasticache不能从外部访问(复杂、不成功)
  • 但是可以通过同一个VPC下的AWS EC2来进行访问
  • 本地开发调试的时候怎么去连redis呢?

在这里插入图片描述

在这里插入图片描述

2、解决

  • 可以建立ssh通道,通过EC2作为跳板机进行端口转发,来访问AWS的Redis缓存服务

二、建立SSH通道

1、pom引入依赖

        <!-- ssh --><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency>

2、创建sshconfig

  • ssh.yml
sshconfig:#监听的本地端口local-port: 10010#远程的redis地址remote-host: xxxxxxxxxxxx.cache.amazonaws.com.cn#远程redis端口号remote-port: 6379ssh:#EC2实例的地址host: xxxxxxxxxxxxxx.compute.amazonaws.com.cnport: 22user: ubuntupassword:#EC2的秘钥对pem_file_path: /root/.aws/xxxxxxx-devops.pem

3、SSHConnection 程序

  • 这样当从程序启动的时候,可以将createSSH() 写入静态代码快,直接加载开启通道
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;import java.io.InputStream;
import java.util.Properties;/*** Through EC2 as a jumpServer, create SSH tunnel to access redis service.*/@Slf4j
public class SSHConnection {private static Integer localPort;private static String remoteHost;private static int remotePort;private static String user;private static String password;private static String path;private static String host;private static int port;private static Session session = null;static {try {// Get ss configuration file path.InputStream is = SSHConnection.class.getClassLoader().getResourceAsStream("ssh.yml");Properties prop = new Properties();prop.load(is);// Get each value.localPort = Integer.valueOf(prop.getProperty("local-port"));remoteHost = prop.getProperty("remote-host");remotePort = Integer.valueOf(prop.getProperty("remote-port"));user = prop.getProperty("user");password = prop.getProperty("password");path = prop.getProperty("pem_file_path");host = prop.getProperty("host");port = Integer.valueOf(prop.getProperty("port"));} catch (Exception e) {log.error("File not found exception: " + e);}}/*** Create ssh connection and set port forwarding.*/public static void createSSH() {JSch jsch = new JSch();try {if (path != null) {jsch.addIdentity(path);}session = jsch.getSession(user, host, port);if (path == null) {session.setPassword(password);}session.setConfig("StrictHostKeyChecking", "no");session.connect();int assinged_port = session.setPortForwardingL(localPort, remoteHost, remotePort);log.info("The ssh connection is OK.");} catch (Exception e) {if (null != session) {//close ssh connection.session.disconnect();}log.error("Create ssh connection exception: " + e);}}/*** Close ssh connection.*/public static void closeSSH() {log.info("The ssh connection is closed ! ");session.disconnect();}
}

三、Spring boot整合Redis

1、引入依赖

刚开始pool使用的spring boot默认的lettuce连接池,但是程序有时候出现错误,支持不太友好,又换回了Jedis连接池。

 <!-- redis 去除默认的lettuce连接池--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><!-- commons-pool2  连接池工具包 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.6.0</version></dependency><!-- redis.clients/jedis客户端--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.3</version></dependency>

2、配置信息

  • application.yml
spring:redis:host: localhost#要和SSH监听的本地端口一致port: 10010 #超时时间设置 这里要ms 因为后面用到的是Durationtimeout: 0msjedis:pool:max-active: 20 #最大连接数max-wait: 3000 #最大连接等待超时时间max-idle: 20 #最大连接空闲数# 0:Could not get a resource from the poolmin-idle: 10 #最小空闲数
#最大活动对象数     
redis.pool.maxTotal=1000    
#最大能够保持idel状态的对象数      
redis.pool.maxIdle=100  
#最小能够保持idel状态的对象数   
redis.pool.minIdle=50    
#当池内没有返回对象时,最大等待时间    
redis.pool.maxWaitMillis=10000    
#当调用borrow Object方法时,是否进行有效性检查    
redis.pool.testOnBorrow=true    
#当调用return Object方法时,是否进行有效性检查    
redis.pool.testOnReturn=true  
#“空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.  
redis.pool.timeBetweenEvictionRunsMillis=30000  
#向调用者输出“链接”对象时,是否检测它的空闲超时;  
redis.pool.testWhileIdle=true  
# 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.  
redis.pool.numTestsPerEvictionRun=50  
#redis服务器的IP    
redis.ip=xxxxxx  
#redis服务器的Port    
redis1.port=6379   详解:
maxActive:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态就成exhausted了,在JedisPoolConfig
maxIdle:控制一个pool最多有多少个状态为idle的jedis实例;
whenExhaustedAction:表示当pool中的jedis实例都被allocated完时,pool要采取的操作;默认有三种WHEN_EXHAUSTED_FAIL(表示无jedis实例时,直接抛出NoSuchElementException)、WHEN_EXHAUSTED_BLOCK(则表示阻塞住,或者达到maxWait时抛出JedisConnectionException)、WHEN_EXHAUSTED_GROW(则表示新建一个jedis实例,也就说设置的maxActive无用);
maxWait:表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
testOnBorrow:在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
testOnReturn:在return给pool时,是否提前进行validate操作;
testWhileIdle:如果为true,表示有一个idle object evitor线程对idle object进行扫描,如果validate失败,此object会被从pool中drop掉;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
timeBetweenEvictionRunsMillis:表示idle object evitor两次扫描之间要sleep的毫秒数;
numTestsPerEvictionRun:表示idle object evitor每次扫描的最多的对象数;
minEvictableIdleTimeMillis:表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
softMinEvictableIdleTimeMillis:在minEvictableIdleTimeMillis基础上,加入了至少minIdle个对象已经在pool里面了。如果为-1,evicted不会根据idle time驱逐任何对象。如果minEvictableIdleTimeMillis>0,则此项设置无意义,且只有在timeBetweenEvictionRunsMillis大于0时才有意义;
lifo:borrowObject返回对象时,是采用DEFAULT_LIFO(last in first out,即类似cache的最频繁使用队列),如果为False,则表示FIFO队列;其中JedisPoolConfig对一些参数的默认设置如下:
testWhileIdle=true
minEvictableIdleTimeMills=60000
timeBetweenEvictionRunsMillis=30000
numTestsPerEvictionRun=-1

3、RedisConfig的编写(切库处理配置)

之前使用lettuce设定切库,但是有多个请求时,取出的库中的数据是混乱的,是线程不安全的,加了锁还是不行,索性就写了多个redisTemplat,每一个redisTemplat都只负责一个库

  • 同时操作Redis的三个库
  • 多个connectionFactory设定不同的DB
  • 建立多个 RedisTemplate<String, Object> redisTemplat
import com.bmw.fs.hp.service.hva.utils.SSHConnection;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;import java.time.Duration;@Configuration
@EnableAutoConfiguration(exclude = {RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class}) // 注意exclude
public class RedisConfig {//开启ssh通道static {SSHConnection.createSSH();}@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.timeout}")private Duration timeout;@Value("${spring.redis.jedis.pool.max-active}")private int maxActive;@Value("${spring.redis.jedis.pool.max-wait}")private long maxWait;@Value("${spring.redis.jedis.pool.max-idle}")private int maxIdle;@Value("${spring.redis.jedis.pool.min-idle}")private int minIdle;/** * DB11 * DB12 * DB13*/@Bean(name = "redisConnectionFactory11")public RedisConnectionFactory redisConnectionFactory11() {//Redis环境配置(单机、哨兵、集群)RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();standaloneConfiguration.setHostName(host);standaloneConfiguration.setPort(port);//设定这个factory访问的DBstandaloneConfiguration.setDatabase(11);// Jedis客户端配置JedisClientConfiguration jedisClientConfiguration = getJedisClientConfiguration();return new JedisConnectionFactory(standaloneConfiguration, jedisClientConfiguration);}@Bean(name = "redisConnectionFactory12")public RedisConnectionFactory redisConnectionFactory12() {RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();standaloneConfiguration.setHostName(host);standaloneConfiguration.setPort(port);standaloneConfiguration.setDatabase(12);JedisClientConfiguration jedisClientConfiguration = getJedisClientConfiguration();return new JedisConnectionFactory(standaloneConfiguration, jedisClientConfiguration);}@Bean(name = "redisConnectionFactory13")public RedisConnectionFactory redisConnectionFactory13() {RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();standaloneConfiguration.setHostName(host);standaloneConfiguration.setPort(port);standaloneConfiguration.setDatabase(13);JedisClientConfiguration jedisClientConfiguration = getJedisClientConfiguration();return new JedisConnectionFactory(standaloneConfiguration, jedisClientConfiguration);}@Bean(name = "redisTemplate11")public RedisTemplate<String, Object> redisTemplate11() {RedisTemplate<String, Object> redisTemplateObject = new RedisTemplate<String, Object>();redisTemplateObject.setConnectionFactory(redisConnectionFactory11());//进行序列化setSerializer(redisTemplateObject);redisTemplateObject.afterPropertiesSet();return redisTemplateObject;}@Bean(name = "redisTemplate12")public RedisTemplate<String, Object> redisTemplat12() {RedisTemplate<String, Object> redisTemplateObject = new RedisTemplate<String, Object>();redisTemplateObject.setConnectionFactory(redisConnectionFactory12());setSerializer(redisTemplateObject);redisTemplateObject.afterPropertiesSet();return redisTemplateObject;}@Bean(name = "redisTemplate13")public RedisTemplate<String, Object> redisTemplate13() {RedisTemplate<String, Object> redisTemplateObject = new RedisTemplate<String, Object>();redisTemplateObject.setConnectionFactory(redisConnectionFactory13());setSerializer(redisTemplateObject);redisTemplateObject.afterPropertiesSet();return redisTemplateObject;}// 必须配置这个默认的,否则程序报错:匹配到多个Bean -> connectionFactory@Bean(name = "redisTemplate")public RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> redisTemplateObject = new RedisTemplate<String, Object>();redisTemplateObject.setConnectionFactory(redisConnectionFactory11());setSerializer(redisTemplateObject);redisTemplateObject.afterPropertiesSet();return redisTemplateObject;}/*** Set configuration file for reids connection pool.** @return JedisPoolConfig*/private JedisPoolConfig jedisPoolConfig() {JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(maxActive);poolConfig.setMaxIdle(maxIdle);poolConfig.setMinIdle(minIdle);//当池子内没用可用连接时,最大的等待时间poolConfig.setMaxWaitMillis(maxWait);//在获取连接时,检查有效性poolConfig.setTestOnBorrow(true);poolConfig.setTestOnReturn(true);//在空时检查连接有效性poolConfig.setTestWhileIdle(true);return poolConfig;}/*** Building Jedis client.** @return JedisClientConfiguration*/private JedisClientConfiguration getJedisClientConfiguration() {//通过构造器来构造客户端配置JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder();//  加入超时配置,不加的话,Jedis长时间不操作,连接会关闭,导致异常://org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; //nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Connection resetif (timeout != null) {builder.readTimeout(timeout).connectTimeout(timeout);}//修改连接池配置builder.usePooling().poolConfig(jedisPoolConfig());return builder.build();}/*** Serializing values.** @param template*/private void setSerializer(RedisTemplate<String, Object> template) {RedisSerializer<String> stringSerializer = new StringRedisSerializer();template.setKeySerializer(stringSerializer);template.setValueSerializer(stringSerializer);}
}

4、Redis操作的工具类

  • 设定不同库的RedisUtils
  • 引入不同库的 redisTemplate
  • 这里只写了DB9的工具类,其他的也是一样的就不再赘述
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** Get values from redisDB_9*/
@Component
public class RedisSerDB9 {@Resource(name = "redisTemplate9")private RedisTemplate redisTemplate;/*** Take out all the keys of the prefix.** @param prefix* @return Set<String> : all keys*/public Set<String> getAllKey(String prefix) {Set keys = redisTemplate.keys(prefix + "*");RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());return keys;}/*** Add value to set.** @param key* @param value* @param expireTime* @return boolean*/public boolean add(String key, Object value, Long expireTime) {boolean result = false;try {SetOperations<String, Object> set = redisTemplate.opsForSet();set.add(key, value);//设定过期时间,单位是秒redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);result = true;} catch (Exception e) {e.printStackTrace();} finally {// 解除连接RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());}return result;}/*** Get values from set.** @param key* @return Set<Object>: All values of the current key.*/public Set<Object> setMembers(String key) {SetOperations<String, Object> set = redisTemplate.opsForSet();Set<Object> members = set.members(key);RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());return members;}
}

四、两个大坑

1、 长时间未操作,连接重置

  • org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Connection reset

在RedisConfig刚开始写的connectionFactory中,没有配置timeout导致的

        if (timeout != null) {builder.readTimeout(timeout).connectTimeout(timeout);}

链接:这位博主的博客中有写到 ->

2、长时间未操作,无法获取resource

  • Error: Could not get a resource from the pool

connectionFactory,配置timeout之后,长时间未操作,还是显示报错:Could not get a resource from the pool
我这里是因为pool中配置的参数有问题:

之前min-idle配置的是0,导致没有空闲的连接数。 改成非0就行了

    jedis:pool:max-active: 20max-wait: 3000max-idle: 20#0:Could not get a resource from the poolmin-idle: 10

五、总结

  • 多想多做多尝试,不懂就问
  • 别钻牛角尖,这个方法不行,就赶紧换下个方法,掌握方法论,在短时间内找到最有效的解决方法
  • 技术基于业务场景,多思考应用场景,拓展思维

这篇关于SpringBoot建立SSH通道整合S3Elasticache(Redis),并实现Redis多库切换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/976580

相关文章

MyBatis-Plus逻辑删除实现过程

《MyBatis-Plus逻辑删除实现过程》本文介绍了MyBatis-Plus如何实现逻辑删除功能,包括自动填充字段、配置与实现步骤、常见应用场景,并展示了如何使用remove方法进行逻辑删除,逻辑删... 目录1. 逻辑删除的必要性编程1.1 逻辑删除的定义1.2 逻辑删php除的优点1.3 适用场景2.

SpringBoot简单整合ElasticSearch实践

《SpringBoot简单整合ElasticSearch实践》Elasticsearch支持结构化和非结构化数据检索,通过索引创建和倒排索引文档,提高搜索效率,它基于Lucene封装,分为索引库、类型... 目录一:ElasticSearch支持对结构化和非结构化的数据进行检索二:ES的核心概念Index:

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Java中ArrayList与顺序表示例详解

《Java中ArrayList与顺序表示例详解》顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构,:本文主要介绍Java中ArrayList与... 目录前言一、Java集合框架核心接口与分类ArrayList二、顺序表数据结构中的顺序表三、常用代码手动

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过