一些后端问题解决方案整理
一些后端问题解决方案整理。
-
Cannot create JDBC driver of class '' for connect URL ‘null’,从文章中得到启示,是tomcat没有配置数据库驱动信息,在
{tomcat_home}/conf/context.xml
中加入配置信息,如oracle配置信息:1 2 3 4 5 6 7 8 9 10 11
<Resource name="jdbc/sanchi" auth="Container" type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@ip.ip.ip.ip:port/orcl1" username="username" password="password" maxActive="20" maxIdle="10" maxWait="10000" />
-
Cannot load JDBC driver class ‘oracle.jdbc.driver.OracleDriver’,从链接中文章得到启示,是缺失了oracle的驱动文件,网上找了半天,最后从这个地方下载到了驱动文件,放到
{tomcat_home}/lib
文件夹下,再次启动tomcat,运行正常。 -
intellij idea运行项目正常,但是debug运行的时候卡在卡面这个地方:
[ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/Library/Tomcat/webapps/manager]
。一个解决方案,另一个说的解决方案。但是神奇的是根据以上两篇文章进行修改之后并没有正常,过一会之后自动变正常了,总的来说问题未解决,困惑。 -
很奇怪后端tomcat记录的访问者ip一直是
127.0.0.1
,无法获取到访问者的真实ip,查了一番是nginx反向代理导致,Tomcat Access Log记录X-Forwarded-For,使用Nginx后web服务器如何获得真实的用户IP1 2 3 4
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%{X-Forwarded-For}i %h %l %u %t "%r" %s %b" />
1 2 3 4 5 6 7 8 9
server { ... location / { ... proxy_set_header Host $host; proxy_set_header X-real-ip $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
-
Oracle varchar2格式存储不了超过设定值的字符串长度了,想把字段修改为更大的clob,但是发现报错无法修改:
[99999][22858] ORA-22858: 数据类型的变更无效. and 1 duplicate reports
,经查询找到这篇文章,将RP_TM_SHARE
表中TM_ID_LIST
字段修改为clob格式,Column_B
为中转字段。1 2 3 4
alter table RP_TM_SHARE add Column_B clob; update RP_TM_SHARE set Column_B = TM_ID_LIST; alter table RP_TM_SHARE drop column TM_ID_LIST; alter table RP_TM_SHARE rename column Column_B to TM_ID_LIST;
-
调试http协议应用的api接口:
http://httpbin.org/
-
获取客户端的真实ip,参考文章。但是在微信内置浏览器及其他tbsx5内核的浏览器上面用如上的后端方案总是无法获取,这部分浏览器貌似走了很多个代理导致每次获取到的真实ip都在变化。最后决定使用前端方案,通过前端请求三方接口获取到该客户端的真实ip,再传到后端。如上方式使用了一段时间之后收到反馈:使用这个链接的页面在部分微信浏览器内核、qq浏览器、360浏览器、锤子浏览器等上面打不开,多方排查发现并不是打不开,而是打开时间很长,需要将近20s。之后怀疑是这个https请求走到国外的服务器,再回来的耗时导致。用pc浏览器调试发现打开正常的页面请求数据:
DNS Lookup:195.20ms; Initial connection:640.96ms; SSL:430.95ms; TTFB:327.07ms;
,且每次请求都在变动状态,耗时不稳定,猜测得到证实。只有部分手机不行的话,怀疑是不同手机内置webview对ssl的连接处理不同,导致耗时不同。最后替换为淘宝的真实ip获取链接:https://www.taobao.com/help/getip.php
,问题机型上的页面恢复到可用状态 -
tomcat在自动运行一段时间后(一般为十几个小时)会莫名其妙地自己关闭掉,经查询,发现hs_err_pid.log日志,分析如下。更多的tomcat配置调优方案参考[tomcat配置调优](./[tomcat] tomcat配置调优.md)。最后使用jvisualvm监控之后发现是cmd中mybatis巨量打印字符串引起,在正式服关闭mybatis打印之后恢复正常,gc可正常回收内存。
-
Spring升级,从4.x升级5.x问题
-
JSONP请求支持被彻底弃用,完全使用CORS进行跨域请求。因此项目中继承自
AbstractJsonpResponseBodyAdvice
的@ControllerAdvice
注解文件需要被删除,新增如下CORS配置1 2 3 4 5 6 7
<!-- 从Spring4.2开始支持CORS跨域访问;从Spring Framework 4.3.18开始,JSONP的支持被废弃了,而JSONP设计的主要目的是为了跨域访问,但是带来了不安全性 --> <!-- 从Spring Framework 5.1开始,删除JSONP支持,全部转而使用CORS,因此无法使用AbstractJsonpResponseBodyAdvice --> <!-- 跨域请求配置,[官方文档](http://spring.io/blog/2015/06/08/cors-support-in-spring-framework) --> <mvc:cors> <!-- 所有带`/cross/**`的请求都支持跨域访问,更多的配置参数参考官方文档 --> <mvc:mapping path="/cross/**" allowed-origins="" max-age="2500"/> </mvc:cors>
-
AnnotationMethodHandlerAdapter
在4.x中被Deprecated,在5.x中被彻底弃用了,转而使用RequestMappingHandlerAdapter
进行代替 -
其他一些Annotation的报错(
org.apache.catalina.core.StandardContext.startInternal Context [] startup failed due to previous errors
)- 将
jackson-databind
升级到2.9.5
(jackson-databind
中包含jackson-annotations
和jackson-core
,因此无需再次引入),并将spring版本升级到5.1.1.RELEASE
之后就正常了,暂时推测为5.x中需要使用高版本的jackson,且jackson版本互相冲突引发该问题。 - 一开始按照如上
划掉的内容进行修改的时候正常了,后来在另一个项目中进行修改时,依然出现该报错,没有详细的报错信息,于是根据这篇文章中的信息在resources
中添加日志打印控制,打印出了详细的报错信息:Exception sending context initialized event to listener instance of class [org.springframework.web.context.ContextLoaderListener] java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/Object;Ljava/lang/Throwable;)V
,初步排除应该是库重复引用的问题,根据这篇文章打印maven依赖树再搜索org.slf4j.spi
,可以看到是一些库中重复引用了低版本的slf4j造成的,将slf4j的高版本依赖引用前置即可
- 将
-
-
服务器使用同一个tomcat容器启动两个项目,长时间运行时导致jvm内存不足而崩溃,因此配置了多tomcat容器,启动后一切正常,但是发现一个生成验证码图片的接口无法使用,报错信息为
javax.imageio.IIOException: Can't create output stream!
,经过一番面对StackOverflow编程之后发现原来是配置tomcat的时候手贱把根目录下的temp
文件夹给删了,这个文件夹是用于存放临时文件的,但是系统不能自动生成,所以报错,奇葩的bug😂 -
Spring项目中修改为统一使用jackson进行json解析,去除了重复引用的gson和json-lib库,同时手贱删除了mybatis mapper xml中的一个
requestMap
,这时候之后有一个接口报错:Could not write content: Direct self-reference leading to cycle (through reference chain: java.util.HashMap["data"]->java.util.HashMap["sameTradeMarkList"]->java.util.ArrayList[0]->java.util.HashMap["same_type"]->oracle.sql.CLOB["dbaccess"]->oracle.jdbc.driver.T4CConnection["wrapper"])
,一开始没明白这个报错是什么意思,查了一些资料,再仔细看报错信息并debug,发现是接口返回的same_type
字段是clob
格式,jackson不支持解析返回该类型,于是报错,根据提示信息进行修改即可。但是如何修改着实费了一番功夫,尝试方案如下:- 方案一:mybatis的resultMap使用bean对象代替map,这样的好处是返回的数据是结构清晰,且易管理的,并且可进行级联查询,在bean对象中对clob类型数据进行代码转换
clob.getSubString(1, (int) clob.length());
(但是项目中全是map返回,修改为bean的成本太大了) - 方案二:沿用方案一,直接使用resultMap配置javaType和jdbcType,指定转换为java数据的格式,无需在bean对象中进行处理(遇到一个奇怪的问题,在resultMap中指定转换格式之后,前端这个字段莫名其妙就不返回了,查了好久,才发现resultMap中column的字段名称要和select语句中的查询字段大小写完全一致!!! 修改之后恢复正常,这是个大坑千万要注意。最后使用该方案)
- 方案三:不在mapper xml中进行配置,而是在mybatis全局配置文件中指定
ObjectMapper
,将返回数据中的所有指定格式统一转换为指定的格式,找到一篇文章,与我的构想非常接近,但是没找到更详细的资料,效果也有待验证,如果这个能实现将是最完美的方式 - 方案四:在查询结束后,将返回值中的值手动进行转换,另外,一些int及long型的数据格式及精度转换也可以在这个地方进行。查到一个使用fastjson进行格式转换的方案
- 方案一:mybatis的resultMap使用bean对象代替map,这样的好处是返回的数据是结构清晰,且易管理的,并且可进行级联查询,在bean对象中对clob类型数据进行代码转换
-
查看maven依赖🌲并解决依赖冲突:
mvn -Dverbose dependency:tree
-
oracle数据库报错:Resolve error – ORA-28000: the account is locked 使用admin权限运行如下命令,accountName是要解锁的帐号名称:
1 2
alter user accountName account unlock; grant connect, resource to accountName;
-
代码更新之后突然所有的后端接口都失效了,急忙打开命令行发现一直在报错:
Caused by: java.sql.SQLException: ORA-28001: the password has expired
。查询后发现是oracle密码过期了:Oracle提示错误消息ORA-28001: the password has expired,是由于Oracle11G的新特性所致, Oracle11G创建用户时缺省密码过期限制是180天(即6个月), 如果超过180天用户密码未做修改则该用户无法登录。 Oracle公司是为了数据库的安全性默认在11G中引入了这个默认功能,但是这个默认的功能很容易被DBA或者是开发人员给疏忽,一旦密码180天未修改过,就会出现这样的问题。
解决方案。 -
oracle默认null值为最大,在对包含null值的字段进行排序时,升序的话会排到最后,降序的话会排到最前。不过oracle提供了对null值自定义排序的关键字,需配合升降序关键字进行使用:
desc/asc nulls last/first
,不过如果要对null进行特殊处理的话,可以配合nvl()
,将null值转换成指定的值进行排序 -
windows服务器莫名其妙重启之后oracle数据库账户被锁。第二天早上服务器莫名其妙又卡死最后重启发现数据库又被锁,查了半天发现是有一个开机自启的tomcat,里面的db连接密码是错的,重试次数过多给锁账户了,删除这个错误配置的自启动tomcat之后恢复正常。另外附一些解决过程中的踩坑项
-
远程桌面到服务器,使用
SQL PLUS
登录oracle数据库sys
账户,默认密码为change_on_install as sysdba
。(各默认账户的权限与区别) -
解锁指定的账户:
alert user 账户名称 account unlock;
-
修改指定账户的密码:
alert user 账户名称 identify by "新密码";
-
Oracle用户被锁的原因以及解决办法:查看
$ORACLE_HOME/network/admin/log/listener.log
日志,因为配置不同该路径可能不同,因此使用everything全局检索listener.log
即可。
-
-
mybatis使用test语句判断
isPay == 'Y'
的时候一直报错org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.lang.NumberFormatException
,但是检查了各种类型,没有任何问题,直到查到这篇文章,提到如果在mybatis中对字符串数字或者'Y'
进行判断的时候要使用isPay == 'Y'.toString()
;或者根据这篇文章中提到的,使用<if test='isPay == "Y"'>not</if>
这种写法 -
前后端通信加密方案
-
后端突然所有接口都无法使用,报错日志如下:
1 2 3
Caused by: org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Listener refused the connection with the following error: ORA-12514, TNS:listener does not currently know of service requested in connect descriptor
查看发现是oracle连接报错了。远程桌面进入服务器,使用SQLPlus进入oracle,发现当前用户跟
sys
都登录不上,命令行报错:ORA-12560: TNS: 协议适配器错误
,根据这篇文章,发现OracleServiceORCL1
服务未启动,启动之后便可以用sys
账号进行登录。但是用用户账号进行登录的时候依然提示:1 2 3 4 5
ERROR: ORA-01034: ORACLE not available ORA-27101: shared memory realm does not exist 进程 ID: 0 会话 ID: 0 序列号: 0
搜索到这篇文章,是oracle未完全启动导致,进行如下操作之后恢复正常:
- 使用cmd进入命令行
sqlplus /nolog
conn / as sysdba
startup
-
[2019.5.16] restful接口开发中有一个重要的概念叫做幂等,就是同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的,实际上就是接口的可重复调用(包括时间和空间上两个维度),于是研究了一下幂等的实现方案,文章提到有三种方式
- 多版本并发控制:这是乐观锁的一种实现,用于在数据库并发访问时的情况。当数据更新时需要去判断版本号是否一致,如果不一致,则无法对数据进行更新。例如请求支付接口进行扣款时:
update table_name set deposit = deposit-#{payment}, version = version + 1 where orderId = #{orderId} and version = #{version}
。这里orderId可进一步设为主键或唯一索引,因为这样是行锁,否则更新操作时会锁表。乐观锁在对已有数据进行更新时既能保证效率,又能保证幂等。 - 数据库去重表:这是利用数据库表单的特性来实现幂等。以订单请求支付场景为例:将订单号orderId设为去重表的唯一索引,每次请求支付都根据订单号向去重表中插入一条数据,只有插入成功才继续执行支付操作,相当于在事务的开始阶段加锁。考虑两种失败的情况: 1. Insert去重表失败,事务回滚,无任何影响;2. Insert去重表成功,支付业务操作失败,事务回滚,删除之前插入去重表的记录,无任何影响;以上两种失败的情况下,事务的幂等性是可以保持的,避免了单个订单同时多次进行支付的情况。操作成功之后删除该orderId
- redis key+操作id 锁定:与去重表思路相同,只是将对数据库的的访问转移到了对缓存(如redis)的访问,提高了效率。具体操作如下:订单发起支付请求,支付系统会去redis缓存中查询是否存在该订单号orderId的key,如果不存在,则向redis增加key为订单号,然后开始实际支付操作;如果查询到存在该订单号的key,则不进行实际支付操作。无论支付操作成功或失败,在支付操作结果返回后,在缓存中删除该订单号key。
- 多版本并发控制:这是乐观锁的一种实现,用于在数据库并发访问时的情况。当数据更新时需要去判断版本号是否一致,如果不一致,则无法对数据进行更新。例如请求支付接口进行扣款时:
-
[2019.5.21] 项目中写了一个工具类,从
import com.sun.istack.internal.NotNull;
包导入了@NotNull
注解,本地运行良好,但是到服务器运行不起来,提示没有找到这个方法,后来发现本地jdk版本是Java SE Development Kit 8,运行java -version
可以看到java version "1.8.0_201" <br> Java(TM) SE Runtime Environment (build 1.8.0_201-b09) <br> Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)
,但是centOS服务器运行的是自带的Java版本:openjdk version "1.8.0_191" <br> OpenJDK Runtime Environment (build 1.8.0_191-b12) <br> OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
,建议保持本地与服务器jdk版本的一致。可同步安装AdoptOpenJDK中对应的openjdk版本。 -
[2019.5.29] 有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务。
-
[2019.7.19] 使用三方jar包发起api请求,将jar包放在本地的时候发起https请求可以在main函数中进行测试,不会报任何错误信息;但是将jar包放在远程maven仓库,再使用main函数进行测试的话发现有如下报错信息:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
尝试暴力绕过校验,代码如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/** * Trust every server - dont check for any certificate */ private static void trustAllHosts() { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[]{}; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } }}; // Install the all-trusting trust manager // 忽略HTTPS请求的SSL证书,必须在openConnection之前调用 try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { logger.error("trustAllHosts is error", e); } }
上面代码虽然可行,但是没有找到问题的根源,springboot项目使用controller-service方式调用之后可执行,问题待研究。
- 后续更新:出现该问题为本地开发环境使用抓包工具如charls、fiddler等导致,关闭抓包工具后项目可正常运行
-
[2019.9.3] 项目需求扩展,因此将订单导入功能拆分出来了,阿里云买了两台2核4G 8M带宽的ECS实例,并买了一个SLB负载均衡,绑定这两台订单服务器。原先的订单服务是嵌在应用主服务里面的,配置是SLB中20台8核16G ECS实例,部分机子是2M按固定带宽收费的,部分是8M按使用流量计费的。迁移到新的机组之后出现了两个问题:
- 新的机组也是进行consul注册进行调用,并且都是内网访问,因此新的机组使用了一个新的安全策略组,打算进行调用区分,但是测试发现consul一直无法调用成功,报connect time out错误。阿里云工单咨询之后给出答复:部署consul集群时,需要注意client及server实例机器都在同一个安全策略组内,如果不在同一个安全策略组,那么需要互相进行放行彼此的地址,之后才能访问。内网访问时,如果是不同的安全策略组,也需要进行端口出入规则的配置。
- 新的机组部署同步代码之后,python会定时进行订单文件的上传,但是发现一个类型的订单总是出现预警,文件大小为4m左右,springboot代码
MultipartFile.isEmpty()
总是true,提示上传了一个空文件。检查了新机组的nginx配置和springboot文件上传配置均没有问题,本地使用postman上传同样的文件也没问题,其他类型的订单都能上传成功,只有一个类型的订单上传不了,登录到部署爬虫代码的服务器上之后,使用curl -X POST "http://order-api.fenxianglife.com/order/thirdOrder/upload?orderSource=vipshop" -H "Content-Type: multipart/form-data" -F "file=@wph_test_20190808_20190809_1.csv;type=text/csv"
上传也没问题,最后询问爬虫发现设置的请求超时时长为10s,改成30s之后文件上传正常了。最后得出结论:文件较大,爬虫服务器带宽较小,上传文件时超出了设置的超时时间,因为python的上传策略,文件没有传上去,但是发起了一次请求,最后到文件接收服务器之后提示空文件。那么有两个修复措施:1、修改爬虫的超时时长为60s;2、升级python爬虫服务器配置,从2M带宽升级到5M带宽。
-
[2019.9.18] 项目需求扩展,将订单导入功能拆分出来了,之前淘宝订单没有进行切换,用的还是旧工程中的导入,这次把淘宝订单整个切换到新工程之后,服务器压力瞬间上来了。发现问题是因为爬虫上传的时候报502,到应用探针中看的时候发现一台机器上的服务挂掉了,挂之前CPU飙升到了100%,进程被系统自动kill了。重启服务之后升级配置到4核4G,发现内存还是会飙到很高,于是再次升级配置到8核8G。升级之后发现还是会出现内存到99%的情况,手动执行gc之后内存能够降下来,但是过一段时间还是会飙上去。最奇怪的是,按照正常情况来讲,要是真的负载比较高,两台机器的内存都会飙上去,但实际上只有一台到99%,另一台一直平稳在20%。dump了一个1G左右的文件,内存异常的具体原因待分析。内存99%时的症状是新生代堆内存占用高达4G,老年代堆内存只有100M左右,期间yonggc触发次数较少。最后发现每次不触发gc的原因是:新切出来的工程从旧工程中复制了一份部署脚本,旧工程的部署服务器是8核16G,设置的启动参数为
-Xmx13g
,新工程只有8G,最大堆内存设置为13G之后,根本就触发不了gc的阈值,因此调整部署参数为-Xmx7g
。内存飙升的原因待查。 排查过程中用到的一些命令:ps -ef | grep java
:查询进程id,使用该命令也能看到通过java -jar
方式启动服务的参数。或使用top
命令jmap -histo:live {进程id}
:手动进行一次fullgcjmap -dump:live,format=b,file=heap.bin {进程id}
:执行一次fullgc,并将gc之后的dump文件存储为heap.bin
文件。jmap -dump:format=b,file=heap.bin {进程id}
命令为不进行gc直接导出dump文件jmap -heap {进程id}
:查看进程堆信息
-
[2019.9.18] 项目需求扩展,将订单导入功能拆分出来了,但是部分活动订单的处理还是在主工程中,依托consul的feign调用,将需要处理的活动订单数据提交到主工程进行处理。因此主工程提供了feign api供订单服务调用,在开发测试环境一切正常,但是到正式环境之后,调用方订单工程不停地报
feign.FeignException$NotFound: status 404 reading OrderSyncApi#pushJdRewardOrder(String)
,因为订单量较大,15分钟内报错可能高达2w+,但是订单依然提交成功,感觉非常奇怪。检查了consul注册中心,各服务正常;检查了consul及feign配置,一切ok,同样配置的consul调用其他例如佣金服务时没有任何问题。最后请教了架构师,检查线上consul之后发现,线上注册sanchi-service-server
这个服务的consul有20台机器,但是其中两台机器用作他用,没有部署主应用代码,但是没有从consul中移除。所以当负载轮询调用到这两台机子的时候会报404,熔断ribbon重试之后调用到其他机器,数据就处理成功了,解决了困扰两天的难题,线上复杂环境总是能踩到各种坑😂 -
[2019.9.20] 订单工程的内存优化策略:两台服务器,一开始都使用g1gc,发现内存上去之后就再也下不来了,于是对比之下将其中一台去掉了g1gc,使用默认gc,发现heap满之后会自动执行full gc将内存降下来,于是查询资料,美团Java Hotspot G1 GC的一些关键技术中提到是SATB策略引起的。且G1是不提供full GC的。
- gc日志参数(备注:-Xloggc的目录需要提前建好):G1 垃圾收集器配置参数
java -server -Xms5g -Xmx5g -Xmn2g -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:$GC_DIR/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=2 -XX:GCLogFileSize=2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$GC_DIR -XX:OnOutOfMemoryError=$HOME/deploy-jar-prod.sh -jar $jarPath >> $LOG_DIR"/order-console.log" 2>&1 &
- 从实际案例聊聊Java应用的GC优化
- Java8默认使用的GC类型
- 谁是JDK8中最快的GC
- 关于JVM的垃圾回收(GC) 这可能是你想了解的
- Java G1垃圾收集器(GC) 2018/08/05
- JVM性能优化(调优)2018/09/06
- JVM调优总结
- JVM 配置GC日志
- JVM老年代和新生代的比例
- JVM调优及参数设置
- 重要:Java JVM 参数设置大全
- 重要:Linux堆内存管理深入分析(上)
- 重要:当Java虚拟机遇上Linux Arena内存池
- 重要:Java应用Top命令RES内存占用高分析
- JAVA进程占用高内存原因分析与优化方法
- jvm疯狂吞占内存,罪魁祸首是谁?
- Java程序在Linux上运行虚拟内存耗用很大
- GC Ergonomics间接引发的锁等待超时问题排查分析
-
[2019.9.29] 项目原先是单module的,后来由于业务需求,拆分为多个module,将其中一个module打包为jar包供其他工程调用,但是拆分之后发现主运行module单元测试不通过。单元测试类是项目创建时自动生成的:
1 2 3
@RunWith(SpringRunner.class) @SpringBootTest public class TestServerApplicationTests {}
单元测试构建时报错:
java.lang.IllegalStateException: Failed to load ApplicationContext. Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'thirdOrderController' defined in file
。查询一番之后,发现将@SpringBootTest
注解中类修改为确切的service之后单元测试就正常运行了。 -
[2019.9.30] 还是上面微服务和module拆分的问题:新拆出来的订单服务通过consul+feign为其他工程提供订单导入功能,feign接口中使用
@RequestBody
直接提供一个bean对象供调用,但是最后微服务间进行测试的时候,LocalDateTime解析报错,后来改为@RequestParam
post方式提交字符串,使用json格式进行中转,最后再序列化解析为对象解决问题。 -
【Spring Boot问题】–Singleton bean creation not allowed while singletons of,根据文章建议,需要创建一个bean的processor。不过线上出现一次这个状况,后面再没有出现类似的情况,后续停止关注这个问题。
-
spring线程池ThreadPoolExecutor有若干配置参数,配置时需要注意:
corePoolSize < queueCapacity < maxPoolSize
;另外,注意不要在循环中一次性创建大量的线程,因为超出👈配置的边界大小之后,会有指定的抛弃策略,可能导致部分任务无法执行。如果有频繁创建线程的场景,需要根据实际线程处理情况来配置线程池,或者控制线程创建速度。 -
[2019.10.30] 现有订单同步策略为
下单-->订单同步工程--解析处理为list数据-->订单处理工程
,整个链路是微服务调用,测试时发现一个订单很长时间都未同步处理完成,下单时间是10:45:32,10:45:42同步到order工程,订单工程解析之后,10:46:15通过feign调用订单处理工程,中间并无较长的延时。根据请求去订单处理工程查询访问日志,该订单最早出现在10:59:47,拿着该traceId在order工程反查,发现这是10:59:47的一次调用,从10:46:15-10:59:47之间的几次consul请求订单处理工程没有收到。查看错误日志发现存在报错feign.FeignException$BadRequest:status 400 reading OrderSyncApi#pushJdActivityOrder(String)
,检索找到Feign status 400 reading 问题分析/Spring Boot app rejects HTTP request with total header size larger than 8KB/Increase HTTP Post maxPostSize in Spring Boot,文章中称内嵌tomcat对参数的默认限制是8K,需要在在Feign接口提供端的微服务中配置参数
,可以得知是feign传递参数内容太长了,接收方的tomcat无法处理因此报错了。修改方案:1. 将订单的list批量提交方式修改为单个提交;2. 后续修改各微服务工程的max-http-header-size
。更多关于springboot内嵌tomcat的参数配置参考How to Configure Spring Boot Tomcat,以及springboot内嵌其他容器的说明Spring Boot 内嵌容器Undertow参数设置1 2 3 4
# 配置最大请求头大小为1m server.tomcat.max-http-header-size=1048576 # 配置最大请求体大小为6m server.tomcat.max-http-post-size=6291456
-
[2019.11.18] 同事在进行sql处理时,有一个场景是执行如下操作:判断表中是否有唯一索引的值,如果有,则删除该记录并重新插入,在并发情况下出现了死锁的情况。问题原因及处理方案:并发delete+insert duplicate-key冲突导致死锁
-
[2019.11.28] 前几日的时候其他部门的同事遇到一个问题:负载均衡上出现了很多请求超时情况,较长的有20s的请求,但是去看此时机器的并发数,及机器的健康状态的话,又没有任何问题。最后给出了一个阿里团队的解决方案,关于tcp半连接队列和全连接队列引起的问题。
-
[2019.11.29] 同事给过来一个SQL,
select * from tb_item where item_online_status in (1,2) and id > 1000 and shop_type in ('J') order by id limit 500;
,使用id做分页查询判断,发现在MySQL 5.6中查询正常,在5.7中查询很慢。查询之后确定是索引问题,但奇怪的是使用id asc查询的时候很慢,但是使用id desc倒序查询的时候效率又很快,原因待查。 -
[2019.12.10] 使用consul+feign进行系统间api调用的时候,偶现传参中出现很多
\\u0000\\u0000
的情况,整个字符串可能到500多k的长度,其中大部分内容全是u0000,但是同样的环境和参数,第二次调用的时候就没有任何问题,感觉十分诡异。感觉是json序列化的问题,但是一直没有稳定的复现方式。feign内部使用jackson进行对象序列化,先改成使用fastjson进行序列化尝试一下。[2020.03.30 经测试自从修改为fastjson之后再未出现改问题,序列化问题解决] -
[2020.03.30] spring的service类调用自己方法事务无效,SpringBoot 内部方法调用,事务不起作用的原因及解决办法。三个解决方法如下:
- [方案一] 引入自身bean:在类内部通过
@Autowired @Lazy
将本身bean引入,然后通过调用自身bean,从而实现使用AOP代理操作 - [方案二] 通过ApplicationContext引入bean:通过ApplicationContext获取bean,通过bean调用内部方法,就使用了bean的代理类。
@Autowired ApplicationContext applicationContext; ((UserService)applicationContext.getBean("userService")).invokeInsertUser(user);
- [方案三] 添加
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
启用cglib代理,并修改为((AcRefundOrderDetailThirdService) AopContext.currentProxy()).dealRefundOrder(refundOrder);
调用方式之后可以进行正常调用。不过有文章提到使用cglib之后如果频繁创建动态代理类,可能会引起pem区的内存溢出,JDK 1.8 JVM内部结构改变_元空间(Metaspace)取代永久代(PermGen),for循环10w次调用监控Metaspace及loadclass数量发现并无异常情况
- [方案一] 引入自身bean:在类内部通过
-
[2020.03.31] 使用
ThreadPoolTaskExecutor
异步处理一些任务时,注意对异常的try-catch。一些情况下任务可能抛出异常并且退出了,但是没有自动拦截到任何错误日志,添加try-catch之后才会记录错误日志 -
[2020.06.29] linux上查看服务被kill掉的原因:
dmesg | egrep -i -B100 'killed process'
,查看日志输出如下1
[14409.960167] Out of memory: Killed process 4259 (java) total-vm:7100116kB, anon-rss:3221416kB, file-rss:0kB, shmem-rss:0kB