采坑日志
这篇文章关于本人的采坑记录,常见的就不加了,相信也能百度的到。
Rsa加密解密
在获取rsa加密后拿到的秘钥(分为公钥和私钥)作为解密秘钥。在window中运行解密没有问题,但是在Linux服务器上解密就变成了带有乱码的数据,导致数据不对引发错误。
加密解密都是用RSA,秘钥也是对的。问题就出在这里
Cipher cipher = Cipher.getInstance("RSA");
这是不完全的RSA方式,应该将完整RSA的填充方式加上
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
解决!
线程池卡死问题
在一个用定时任务驱动的方法体中,有三四个网络请求与多个数据库更新方法。测试时发现服务器上跑完整个流程是十秒左右,但是数据量稍大的情况下又很紧急,就显得慢。于是我使用了线程池技术来驱动,刚开始的时候还好,后面发现有卡死的情况。
代码如下:
private static volatile ThreadPoolExecutor singleExecutor = null;
private static Object ojb = new Object();
public static synchronized ThreadPoolExecutor getInstanceThreadPool() {
if (null == singleExecutor) {
singleExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(400));
singleExecutor.allowCoreThreadTimeOut(true);
}
return singleExecutor;
}
public List<String> getXXX(List<String> ids) {
Thread thread = null;
ThreadPoolExecutor executor = getInstanceThreadPool();
List<Pair<Date, Thread>> activeThreads = new ArrayList<>();
try{
for(String id:ids) {
thread = new Thread(() -> {
synchronized (ojb) {
activeThreads.add(Pair.of(DateUtils.getDate(new Date(), "25s", 1), Thread.currentThread()));
}
// 逻辑xxxx
}
executor.execute(thread);
}
int blockTime = 0;
while (true) {
Threads.sleep(3000);
BlockingQueue<Runnable> queue = executor.getQueue();
int queueSize = CollectionUtils.size(queue);
logger.info("当前排队线程数 " + queueSize);
logger.info("当前活动线程数 " + executor.getActiveCount());
if (activeThreads.size() > 0) {
logger.info("thread size is " + activeThreads.size());
for (int i = activeThreads.size() - 1; i >= 0; i--) {
Pair<Date, Thread> pair = activeThreads.get(i);
if (null != pair && new Date().getTime() - pair.getKey().getTime() > 1000) {
if (pair.getValue().isAlive()) {
pair.getValue().stop();
}
activeThreads.remove(i);
logger.info("stop the thread");
}
}
}
if (queueSize == 0 && activeThreads.size() == 0) {
break;
}
if (queueSize > 0 && executor.getActiveCount() == 0 && activeThreads.size() == 0) {
blockTime ++;
} else {
blockTime = 0;
}
logger.info("block time is: " + blockTime);
if (blockTime > 100) {
for (Runnable runnable : queue) {
executor.remove(runnable);
}
logger.info("thread over time");
break;
}
}
} catch (Exception e) {
logger.error("fk schedule error:", e);
}
}
看到这里,发现有死循环检测。本以为是这里发生问题,但是有卡死退出检查:
即:if (blockTime > 100)
也有等待队列移除:
for (Runnable runnable : queue) {
executor.remove(runnable);
}
所以不在检测中。
我也想到资源竞争问题,但是由于时间问题,排查起来比较麻烦。
后来我发现这线程池是个单例方法~~ 好家伙,如果卡死的话不会像对象一样可以初始化。当机立断,直接改成了即用即建。
将如下:
// 将单例移除,把ThreadPoolExecutor executor = getInstanceThreadPool(); 改成如下
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200));
// 后增加如下代码
finally {
executor.shutdownNow();
}
MySQL 索引失效问题
在一次表数据关联分页查询的时候,发现查询耗时比较长。本以为是数据量的问题,然后看了下才两万多条数据,根据MySQL性能不可能慢,主要是执行sql没有任何异常。我是建了多个普通索引,后面排查是否sql有not in等关键字使索引失效,没有发现。用EXPLAIN 来解析sql 发现主表type 是All,性能巨差。查了很多次,问了AI,根据列出的可能发生问题列表进行排查。后来发现 表字段的字符集也会导致索引失效问题。
如下排查两张表的关联字段:
发现有一个为utf8bm4_bin 另一个为utf8bm4_general_ci,立即都改为utf8bm4_general_ci,然后发现快了数倍
MySQL8 数据库迁移并且数据与服务分离
在新的服务器上安装MySQL8 后发现磁盘比较少,经过商量后,决定用数据分离。
首先停止MySQL服务 service mysql stop
在MySQL的配置文件/etc/mysql/mysql.conf.d/mysqld.cnf 中配置datadir = /xxx/xxx/mysql
然后把mysql数据 从/var/lib/mysql复制到/xxx/xxx/mysql
cp -R /var/lib/mysql/* /xxx/xxx/mysql
后启动 service mysql start
发现启动报错:
无权限!!
我可是root用户,然后查了资料,博主都说是文件权限不够,但是发现拷贝过来的全部都是mysql用户组的目录与文件
各种更改权限与配置 发现毫无卵用
到这里已经花了一天半的时间了。。。
快绝望的时候 点开了一篇文章:
https://blog.csdn.net/weixin_41555271/article/details/125501219
救命稻草
发现里面有个文件是为mysql文件权限配置,Fxxk,只怪自己没有去官方参透。
执行以下指令:
vim /etc/apparmor.d/usr.sbin.mysqld
#将 Allow data dir access下的
/var/lib/mysql r,
/var/lib/mysql/** rwk,
#修改成
/xxx/xxx/mysql r,
/xxx/xxx/mysql/** rwk,
#重新启动mysql服务
/etc/init.d/apparmor restart
重启apparmor很重要!!
最后 我把全部步骤代码贴出来:
#停止mysql 服务
service mysql start
#创建新目录
mkdir -p /xxx/xxx/mysql
#拷贝所有数据到新目录
cp -R /var/lib/mysql/* /xxx/xxx/mysql
#调整目录所有者为用户mysql
chown -R mysql:mysql /xxx/xxx/mysql
#修改mysq配置文件
vim /etc/mysql/mysql.conf.d/mysqld.cnf
修改 datadir = /xxx/xxx/mysql
vim /etc/apparmor.d/usr.sbin.mysqld
#将 Allow data dir access下的
/var/lib/mysql r,
/var/lib/mysql/** rwk,
#修改成
/xxx/xxx/mysql r,
/xxx/xxx/mysql/** rwk,
#重新启动mysql服务
/etc/init.d/apparmor restart
service mysql start
批量修改mysql 表与列的字符集和排序规则
SELECT CONCAT('ALTER TABLE `', table_name, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;') AS sql_query
FROM information_schema.TABLES
WHERE table_schema = 'your_database_name'; -- 修改为你的数据库名
utf8mb4 与utf8mb4_general_ci可以改为你想要改成的字符集与排序规则
查询出来,复制生成的SQL并在数据库中执行它们。
vm虚拟机装其他服务 apt无效解决
今天自己在装MySQL8的时候遇到一个问题解决下:
在执行如下命令时:
sudo apt install mysql-server
结果等了很久,也没看到运行完毕。看到如下提示:
Media change: please insert the disc labeled
‘Ubuntu-Server 16.04.7 LTS Xenial Xerus - Release amd64 (20200810)’
in the drive ‘/media/cdrom/’ and press [Enter]
我一个劲的按 回车。没卵用
上网!
原来是:
导致这个原因因为 Ubuntu 的安装源没有配置对,找到 Ubuntu 的安装配置源 /etc/apt/sources.list (注意:是 sources 而不是 source)
把该配置文件中的cdrom注释掉
然后执行 sudo apt-get update
解决-- keep going
layer 弹窗关闭刷新表单
// 返回当前激活的Tab页面关联的iframe的Windows对象
function activeWindow() {
var topWindow = $(window.parent.document);
var currentId = $('.page-tabs-content', topWindow).find('.active').attr('data-id');
if (!currentId) {
return window.parent;
}
return $('.Eleboat_iframe[data-id="' + currentId + '"]', topWindow)[0].contentWindow;
}
// 刷新表单
var parentDoc = activeWindow();
parentDoc.$.table.refresh();
MySQL事务分布提交
使用spring-boot和MySQL + mybatis p,通常情况下直接在ServiceImpl层或Controller层加@Transactional注解就行,但是涉及大量更新和统计时,既要保证数据一致性也要保证统计的准确性,这个时候需要我们手动创建事务并提交,把更新与统计隔离开来。当然你也可以使用拆分代码的形式来做
// 1.注解
@Autowired
private TransactionTemplate tranTemplate;
// 2.手动开启事务
DefaultTransactionDefinition defaultTransaction = new DefaultTransactionDefinition ();
TransactionStatus trans = tranTemplate.getTransactionManager().getTransaction(defaultTransaction);
// 3.手动提交事务
tranTemplate.getTransactionManager().commit(trans);
spring-boot 临时文件夹冲突
今天在部署测试环境的时候发现问题:
java.io.FileNotFoundException: D:\tomcat\temp\.ehcache-diskstore.lock (拒绝访问。)
这里显示是临时文件夹中的文件访问权限受限。
修改文件的权限为所有用户可读写
行不通!!!
于是另辟蹊径。
既然tmp文件没有访问权限,就自定义tmp路径
自定义tmp路径
在启动参数中增加:
-Djava.io.tmpdir=/xxx/xxxx/tmp
然后启动,不再报错 。
如果还是报错的话,双重保险:
在配置文件中增加:
spring.servlet.multipart.location=/xxx/xxx/tmp/
解决!!
持续更新中。。。。