开发中报错异常解决
开发中报错异常解决
2022 年 2 月 6 日 开始整理开发中遇到的问题
vue 打包部署 刷新 404
2022 年 3 月 20 日 22:14:21
vue hash
模式下,URL
中存在'#'
,用'history'
模式就能解决这个问题。但是history
模式会出现刷新页面后,页面出现 404。解决的办法是用nginx
配置一下。 在nginx
的配置文件中修改
方法一:
location /{
root /data/nginx/html;
index index.html index.htm;
if (!-e $request_filename) {
rewrite ^/(.*) /index.html last;
break;
}
}
方法二: vue.js 官方教程里提到的
https://router.vuejs.org/zh/guide/essentials/history-mode.html
server {
listen 80;
server_name localhost;
location / {
root html;
# 第一种方法
try_files $uri $uri/ /index.html;
# 第二种方法,需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
#try_files $uri $uri/ @router;
index index.html index.htm;
}
#对应上面的@router,主要原因是路由的路径资源并不是一个真实的路径,所以无法找到具体的文件
#因此需要rewrite到index.html中,然后交给路由在处理请求资源
location @router {
rewrite ^.*$ /index.html last;
}
}
方案三
去掉这行代码。url 上会出现带有#的地址。
mode: 'history',
1.Spring Boot 集成 Druid 异常 discard long time none received connection
2022 年 2 月 6 日 15:54:03
Spring Boot 集成 Druid 异常
在 Spring Boot 集成 Druid 项目中,发现错误日志中频繁的出现如下错误信息:
discard long time none received connection. , jdbcUrl : jdbc:mysql://******?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8, version : 1.2.3, lastPacketReceivedIdleMillis : 172675
经过排查发现是 Druid 版本导致的异常,在 1.2.2 及以前版本并未出现如此异常。而在其以上版本均存在此问题,下面就来分析一下异常原因及解决方案。
异常分析
首先上面的异常并不影响程序的正常运行,但作为程序员看到程序中不停的出现异常还是难以忍受的。所以还是要刨根问底的解决一下的。
跟踪堆栈信息会发现对应的异常是从 com.alibaba.druid.pool.DruidAbstractDataSource#testConnectionInternal 方法中抛出的,对应的代码如下:
if (valid && isMySql) { // unexcepted branch
long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
if (lastPacketReceivedTimeMs > 0) {
long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
if (lastPacketReceivedTimeMs > 0 //
&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {
discardConnection(holder);
String errorMsg = "discard long time none received connection. "
+ ", jdbcUrl : " + jdbcUrl
+ ", jdbcUrl : " + jdbcUrl
+ ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;
LOG.error(errorMsg);
return false;
}
}
}
上述代码中,MySqlUtils.getLastPacketReceivedTimeMs(conn) 是获取上一次使用的时间,mysqlIdleMillis 就是计算出来空闲的时间,timeBetweenEvictionRunsMillis 是常量 60 秒。如果连接空闲了 60 秒以上,那就 discardConnection( holder) 丢弃这个旧连接并顺带打印了一个日志 LOG.warn(errorMsg)。
原理追踪
在上述代码中,我们看到进入该业务逻辑是有前提条件的,也就是 valid 和 isMySql 变量同时为 true。isMySql 为 true 是必须的,我们使用的本身就是Mysql数据库 。那么是否可以让 valid 为 false 呢?这样不就不会进入该业务处理了吗?
来看看 valid 的来源,还是在该方法的上面:
boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
我们找到 validConnectionChecker 的 Mysql 实现子类 MySqlValidConnectionChecker,该类中对 isValidConnection 的实现如下:
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
if (conn.isClosed()) {
return false;
}
if (usePingMethod) {
if (conn instanceof DruidPooledConnection) {
conn = ((DruidPooledConnection) conn).getConnection();
}
if (conn instanceof ConnectionProxy) {
conn = ((ConnectionProxy) conn).getRawObject();
}
if (clazz.isAssignableFrom(conn.getClass())) {
if (validationQueryTimeout <= 0) {
validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
}
try {
ping.invoke(conn, true, validationQueryTimeout * 1000);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof SQLException) {
throw (SQLException) cause;
}
throw e;
}
return true;
}
}
String query = validateQuery;
if (validateQuery == null || validateQuery.isEmpty()) {
query = DEFAULT_VALIDATION_QUERY;
}
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
if (validationQueryTimeout > 0) {
stmt.setQueryTimeout(validationQueryTimeout);
}
rs = stmt.executeQuery(query);
return true;
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
}
我们可以看到上述方法中有三个返回的地方:第一个连接已关闭;第二个使用 ping 的形式进行检查;第三,使用 select 1 的方式进行检查。而使用 ping 的形式检查时,无论是否抛异常都会返回 true。这里我们禁用该模式即可。
进入 ping 的业务逻辑主要靠变量 usePingMethod 来判断,追踪代码会发现在这里进行的设置:
public void configFromProperties(Properties properties) {
String property = properties.getProperty("druid.mysql.usePingMethod");
if ("true".equals(property)) {
setUsePingMethod(true);
} else if ("false".equals(property)) {
setUsePingMethod(false);
}
}
那么,也就是说,当我们把系统属性 druid.mysql.usePingMethod 设置为 false 即可禁用该功能。
禁用 Ping Method
找到了问题的根源,那么剩下的就是如何禁用了,通常有三种形式。
第一,在启动程序时在运行参数中增加:-Ddruid.mysql.usePingMethod=false。
第二,在 Spring Boot 项目中,可在启动类中添加如下静态代码快:
static {
System.setProperty("druid.mysql.usePingMethod","false");
}
第三,类文件配置。在项目的 DruidConfig 类中新增加:
/*
* 解决druid 日志报错:discard long time none received connection:xxx
* */
@PostConstruct
public void setProperties(){
System.setProperty("druid.mysql.usePingMethod","false");
}
至此,已可以成功关闭该功能,异常信息再也不会出现了。
为什么要清空空闲 60 秒以上的连接
猜测,阿里给数据库设置的数据库空闲等待时间是 60 秒,mysql 数据库到了空闲等待时间将关闭空闲的连接,以提升数据库服务器的处理能力。
MySQL 的默认空闲等待时间是 8 小时,就是「wait_timeout」的配置值。如果数据库主动关闭了空闲的连接,而连接池并不知道,还在使用这个连接,就会产生异常。
2.数据库语句错误
1.limit 放在 where 后面
正确:
select * from table where delete=1 limit 1,5;
错误:
select * from table limit 1,5 where delete=1;