本文主要是介绍日常工作检查表-----Check List,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
坑
日常开发,如果没有考虑周全,没有经验的指引,或者先行者的指导,很容易掉进各种各样的坑里。我们可以依靠尽量的多思多问来避免坑;尽早部署在生产一致的DEMO环境演练,来尽早发现坑。总结一下技术的、业务的、环境的坑:
1、仓储系统中的无线手持PDA设备,无线在仓储覆盖不全,比如站起系统正常、蹲下系统就没反应了。这个需要尽早用DEMO的功能,或者前期演示的功能,尽早投入试用。
2、支付系统中,出于安全的考虑,会在应用和第三方环境之间,有一道墙,所有应用对于第三方的访问,需要通过一台机器转发(NGINX转发、RINETD转发之类),如果之前没有考虑到,则上线时就会有麻烦。
3、Nginx设置了一个5秒超时重发,导致出现问题,全部重发了,如果这是支付,后果是很严重的。
2015/03/30 17:08:05 [error] 24919#0: *404832 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 101.254.166.184, server: epay.yixin.com, request: "POST /fastPayGetMsg.htm HTTP/1.1", upstream: "http://10.150.179.151:8080/fastPayGetMsg.htm", host: "epay.yixin.com"
2015/03/30 17:09:21 [error] 24919#0: *404920 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 101.254.166.184, server: epay.yixin.com, request: "POST /fastPayGetMsg.htm HTTP/1.1", upstream: "http://10.150.179.196:8080/fastPayGetMsg.htm", host: "epay.yixin.com"
联调
很多时候,需要联调,这个时候如何来处理?
最重要的一条:不要想当然,用一个最小的范例,保证合作方打成一致。
例如:js进行deflate/encrypt等压缩/加密处理,java进行inflate/decrypt等解压/解密处理,这个时候可以通过对一个最简单字符串:hello的处理,达成前后端的一致。
因为,js的框架非常多,很多js类库的处理方式都不是标准的,用这个类库自身进行 操作和反操作 都是OK的,拿到其他语言,其他框架,就未必OK了。
压缩、解压算法,会把原始文本,按照一定算法,组合成新的字符序列,很多不能显示和打印的字符,为了传输,很多时候需要进行Base64编码,由于Base64的算法,会导致长度增加1/3。
通用
界面要有统一的风格。
SVN Commit前要先Update,遇见冲突不能随便处理,不能放过任何一个可疑。
单据数据,不能进行物理删除,只能进行逻辑删除,物理删除实际审计上也是不允许的。
关于逻辑删除,一般有两种处理办法:一种是增加一个标记删除字段,一种是增加一个delete表,用来存储删除数据,各有优劣,自己自考一下。
关于程序,我们可能尽可能的让各个环节都不出问题,但是我们的程序,要对各种可能出现的问题hold住。
CSharp
C#引用Java WebService:
如果参数是简单类型的列表,则参数是不允许为空的,比如:@WebParam(name = "soldOutDays1") Integer soldOutDays1,则C#客户端生成的代理类的类型是int,是不允许为空的;
如果参数是对象类型,则对于简单类型列,会生成两个属性:比如Integer,会生成bool类型的加后缀specified的列,和int类型的列;specified列用来标记是否传值,如果不传值,则服务器端获取到的就是NULL
Java
1、jsp页面中文乱码问题,是因为默认jsp是采用ISO-8859-1编码的。解决办法,在每个jsp的头部增加如下内容:
<%@ page pageEncoding="UTF-8" %>
<%@ page contentType="text/html; charset=UTF-8" %>
2、for(String s:ss),加入ss是个list,会按照什么顺序循环这个list呢?就是信息add进list的顺序。关于这一类问题,其实全部是建立在对JDK源码的理解,对JDK底层的数据结构和算法的理解基础上的,理解了,就会了
3、java命令的classpath参数的问题,例如
java -classpath .:/work/xx.jar xxclass ,如果jar包中一个配置文件,当前目录也有一个配置文件,则会读取当前目录的配置文件,因为.:/work/x.jar指定的顺序.(代表当前目录)的优先级高;
如果改成,java -classpath /work/xx.jar:. xxclass,则会执行jar包里面的配置文件,因为jar包在前面了。
另:如果是断点调试,当前项目有文件,同一工作区存在同包同名的文件(有断点),则在调试过程中会走有断点的文件。
4、2014-05~07,一个CPU负载爆满的问题
场景:Thrift推荐服务上线之后,几台机器(24核心CPU机器)中的不定某几台,CPU负载会负载100%不下降,Load Average会持续增长,到达120,最高的时候到达过600多,这个时候访问服务,还能正常提供服务(不过我估计应该会有服务超时等不提供服务的情况了,不过没人监控,没看日志)。
解决步骤:
开始开发者(Leader)怀疑是F5负载不均衡,让运维的同事调整了多次F5策略,1个多月,问题没有解决,后来开发者离职,代码交接出来之后,怀疑是代码问题,开始尝试解决问题。
通过命令:jstack -l -F 进程ID,具体命令:/usr/java/jdk1.6.0_26/bin/jstack -l -F 20134 >> 169.txt ,这个命令只要有root权限就能执行,如果不是root账户,这样执行:sudo -u root /usr/java/jdk1.6.0_26/bin/jstack -F 15348 ,查看程序运行堆栈执行情况,这个开始看不懂,逐渐查资料,逐渐看。
开始怀疑是Thrift协议或者垃圾回收线程占用了大量的CPU,解决办法采用了两个:把Thrift的传输协议从TCompactProtocol改成以前正常提供服务时的TBinaryProtocol,把30分钟一次的Sysgem.gc()(定时任务30分钟一次执行,执行后会调用System.gc())去掉。上线后问题依旧。
继续通过jstack分析,很多线程处于RUNNABLE、TIMED_WAITING (parking),这个时候如果仔细观察,应该会发现不死的线程(死循环),但当时没注意到,关于线程状态的参考文章:http://www.cnblogs.com/zhengyun_ustc/archive/2013/03/18/tda.html
继续分析。
把Thrift服务开在本机跑,调用推荐接口,发现线程开启的特别频繁,并且不死,一直RUNNABLE,这个时候潜下心来,深入的看代码,最终在代码中找出了死循环及错误逻辑。摘录如下:
List<String> tmpSkus = null;
List<Map<String, String>> goodsList = new ArrayList<Map<String, String>>();
Set<String> skuSet = new HashSet<String>();
for(int i=0; i<ConstConf.maxRecomNum; ){for(String sno : styleNoList){tmpSkus = RecomDataTimer.getWantWantResut().get(sno);if(tmpSkus!=null){if(i<tmpSkus.size() && !skuSet.contains(tmpSkus.get(i)) && goodsList.size()<ConstConf.maxRecomNum){skuSet.add(tmpSkus.get(i));goodsList.add(GoodsDataTimer.getGoodsInfo().get(tmpSkus.get(i)));}}}i += skuSet.size();
}
比如当styleNoList只有一个商品,当tmpSkus获取到的推荐商品信息为空时,skuSet.size()=0,则线程进入死循环;并且i += skuSet.size(); 这个,是错误逻辑,导致推荐商品过少。
这里实际是写错了,应该是:i +=styleNoList.size();
总结:
遇到问题不要慌,一步一步分析,可以采取的步骤:a、增加日志;b、本机环境、测试环境搭建,调用它们并跟踪监控;c、深入代码,找出代码中的问题。
问题必然会被解决。
关于进程dump日志,正常的进程日志,应该只有很少的BLOCKED状态的等待监视器锁的线程(这是正常的,CPU就那么几个核,等待是正常的,关键是一直等待阻塞的不是同一个线程);但是不正常的进程日志,有很多RUNNABLE、WAITING状态的线程,不死,这肯定是有问题。用root账户,正常的dump文件20K,不正常的dump文件是200K。
因为开始没有加接口调用日志,为了看看每个机器的负载是否均衡,还曾经试着使用netstat -nat|grep -i "1405"|wc -l,查看每个机器的链接数来判断。当时以为:thrift是短链接,链接是瞬间就完成消失了,所以这个命令实际没有效果;其实thrift是长连接。
关于Thread.State的更多参考文章:
http://shihaiyang.iteye.com/blog/437902
http://blog.csdn.net/wanyanxgf/article/details/6944987
http://jameswxx.iteye.com/blog/1041173
http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html
补充一句关于System.gc()是否应该显示调用的问题:这个应该看具体的程序场景,总结为一句话,显示调用System.gc()会增大CPU负担,但是会加快内存回收的效率。在代码中,我们使用了System.gc(),会立刻进行一次垃圾回收。
5、transient关键字;volatile vs synchronized
transient只能修饰变量,不能修饰类和方法,被其修饰的变量不能被序列化;静态变量不论是否被其修饰,均不能被系列化。
以上是在看jdk源码过程中发现,并网上查了下资料:http://www.cnblogs.com/lanxuezaipiao/p/3369962.html
volatile vs synchronized:http://sakyone.iteye.com/blog/668091
6、时间戳:System.currentTimeMillis()
在java等语言中,时间戳是一个13位的数字,包含3位毫秒;在mysql数据库中,unix_timestamp()是一个10位的数字,不含毫秒。
在一些语言,比如ruby,关于时间戳用:System.out.println(l); 得出这样一个浮点数:1404875251.667048 ,这实际是在微秒之后还多出3位,存入数据库时,一般到微秒就行了,所以我们就要自行处理成一个13位的数字。
7、关于易造成死循环的一种写法,需要尤为关注一下
for( int j = index; j < skus.size(); ){if( ... ) {j += 1;continue;}if( ...) {j += 1;continue;}j += ...;
}
这种写法中,如果在某个逻辑分支判断中,j没有增加,则就造成死循环。曾经遇到过某个高手犯过这个错误,造成服务器CPU负载一直100%,Load Average居高不下。所以对于循环来说,要尽可能的使用for(String no:sno)这种形式。
8、Java写入MySQL乱码问题
如果连接串配置在propertyies文件中,那参数之间的分隔用 & 符号,例如:
jdbc.url=jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=utf8&autoReconnect=true
如果连接串配置在xml文件中,要用 & 分隔,例如:
jdbc.url=jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=utf8&autoReconnect=true
9、Timer定时器
如果使用JDK自带的Timer实现定时器,则Run方法中应该对功能代码加try/catche,否则如果代码有错误,定时器就停了。
10、占用大内存的推荐结果计算
开始的运行方式,java -Xms2048m -Xmx21504m -classpath $CLASSPATH XX.XXMain >> "$stats_log" ,一直正常运行,由于店庆,访问量增大,程序不能正常运行,本来正常20分钟计算完成的任务,逐渐变成4个小时,甚至10个小时都玩不成;
解决步骤:
增加更多监控日志,发现前面的几万条计算依然飞快,但是从某条开始,开始变得非常缓慢,top命令查看,进程占用的内存是21G,已经达到命令启动配置的堆内存的最大值,于是尝试继续增大堆内存大小:
java -Xms2048m -Xmx46080m -classpath $CLASSPATH XX.XXMain >> "$stats_log" ,问题解决。
这个问题的原因,是程序使用内存达到进程启动时设定的最大允许内存,这个时候,只有等垃圾回收到无用内存时,有可用内存了,计算才能继续,所以会异常缓慢。
问题初步解决,但是当数据量继续增大呢?服务器内存不能满足要求呢?这个就需要分布式计算框架,或者优化当前的这种计算逻辑,看能不能改成不占内存的方式。
11、我们在日常的开发中,经常会对异常进行处理,try {} catch(Exception e),这种方式,只能捕获异常,不能捕获Error,比如java.lang.NoClassDefError,就是捕获不到的,如果某些地方,需要捕获Error,可以写成 try {} catch(Throwable e),Exception和Error都是继承自Throwable的。
12、Junit,类加@RunWith(Parameterized.class)注解之后,就不能单独对测试类中单个测试函数进行测试。因为单独运行单个测试函数,不会在创建测试类前执行@Parameters注解的函数,不能为参数化测试提供参数,所以会提示initializationError的错误。参考网址:http://www.aiuxian.com/article/p-128630.html 。而用:@RunWith(SpringJUnit4ClassRunner.class)是可以单个函数测试的。
13、Base64,com.sun.org.apache.xml.internal.security.utils.Base64,maven编译报错,是因为这个包不属于标准的JDK范畴,可以使用这个bcprov-jdk14-1.38.jar中的org.bouncycastle.util.encoders.Base64。
14、一个写Oracle被锁住的问题,和这个帖子类似:http://blog.csdn.net/arkblue/article/details/7870518
15、java比较器,这么来记:当条件成立时,返回1,代表需要交换;返回-1,代表不需要交换。比较器不要用“-”的方式,存在溢出的可能,要用比较的方式。
16、写单元测试时,用了如下的写法,注意,当时ctx之间close了,虽然功能性的静态类,设置成了静态,没有问题,但是这个类使用的连接池等资源,都随着ctx的关闭,相当于关闭了Spring容器,都释放了,必然出问题。
17、某个线程池采用的hook的方式进行资源清理,但这种方式不能保证它依赖资源优先释放。
结果可能导致:这个线程池已经处于terminate状态,但还有线程拥有它的引用继续向其添加任务,导致线程池调用默认的拒绝策略AbortPolicy抛出RejectedExecutionException。
18、应用启动不起来,看日志找不出启动报错的原因,jstack 进程号 > ~stack01,也许就能看出原因,有可能主线程启动就报异常了。
19、应用启动不来了,日志文件中找不到错误,后来把root日志级别调成DEBUG,然后tail -300f logfile | grep Exception,找到错误原因,如下:
原来是Mybatis Mapper XML文件格式错误,导致应用启动不起来。
20、Eclipse Jetty插件启动报错的一个场景:
http://stackoverflow.com/questions/25504930/lambda-expression-throws-exception
用黄的插件,不用绿的插件,解决问题。
框架
Ameoba
有一定概率,会把你的update/insert语句扔掉,但是告诉你执行成功了,实际数据库中并没有执行成功,让你:把程序流程看了很多遍也找不到问题...
JBoss
Linux主机名不能包含_等特殊字符,否则会导致WebService等调用连接不上
https://issues.jboss.org/browse/JBREM-1180
https://community.jboss.org/thread/148479?_sscc=t
Thrift
Thrift里面,Map的Key是不允许为Null的,返回的类型也是不允许为Null的,否则客户端调用时报如下异常:
可以对于Thrift生成的Java文件,做修改如下:
return null;
//throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getCollectAndCollect failed: unknown result");
用return null;代替抛出异常。
对于idl里面,定义为required类型的属性,也是必须赋值的,否则客户端调用时报如下异常:
并且,这种异常,会导致CPU Load增强很多,这个时候的zabbix监控,截图如下:
Tcp PassiveOpens:
http://www.tcpipguide.com/free/t_TCPConnectionPreparationTransmissionControlBlocksT-2.htm
http://stackoverflow.com/questions/4696812/passive-and-active-sockets
Solr
关于Apache Solr索引文件的问题:
主的Solr Commit完毕后,是优化过之后,从才会从主进行同步,查看索引文件的大小,比如索引是在:
/usr/local/apache-solr-3.6.2/example/multicore/goods,则索引存在data目录下面,可以通过:du -sh data 查看。
Log4j
只要配置了log4j.xml或者log4j.properties,然后就可以实例化Log4j来使用了,比如:private static final Logger logger = Logger.getLogger( MainTest.class );
Mybatis
xml里面,对于<要这么写:<![CDATA[AND a.version_date<=#{maxVersionDate}]]>,对于like,可以这么写:AND picture.pic_name LIKE '%${likePicName}%',也可以这么写:and a.channel like CONCAT(#{channel},'%')。
对于返回自增列的值,如下的写法无效:
<selectKeyresultType="java.lang.Integer"keyProperty="id"order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
自增的值,直接被填充到对象的id列了。
有时,XML映射文件有格式错误,比如哪里多了一个逗号之类,Eclipse启动时,打来log4j.xml的debug模式,会看到一直刷屏,前几个没有问题的一直刷,没有刷的就是有问题的。
Linux
启动Tomcat/JBoss等容器,如果不用root,没有加sudo,可能日志目录没有权限,就写不进去;所以最好用root,否则加sudo
Nagios监控:
ps ax|grep nrpe
/usr/local/nagios/bin/nrpe -c /usr/local/nagios/etc/nrpe.cfg -d
转向操作(2>&1,代表不但把标准输出System.out.println转向,同时也把错误输出e.printStackTrace也转向):
java -Xms6144m -Xmx20480m -XX:MaxNewSize=128m -XX:MaxPermSize=128m -classpath $CLASSPATH com.yougou.recommend.server.DataCenterServer >> dc_server_run.log 2>&1 &
java -Xms6144m -Xmx20480m -XX:MaxNewSize=128m -XX:MaxPermSize=128m -classpath $CLASSPATH com.yougou.recommend.server.DataCenterServer >> dc_server_run.log &
清除一个大文件,用echo '' > filename,不要用rm -rf,因为:http://www.cnblogs.com/mfryf/p/3334451.html 。 lsof | grep deleted 可以看到,正在用的,没写入磁盘。
supervise:监控程序,在程序down掉之后自动重启
假设需要supervise管理的应用都放在/service目录下:
初始化:nohup sudo svscan /service/ &
在第一次配置 supervise,或者机器重启后 supervise 没有自动启动的时候,可以用上面命令启动 supervise
查看 service 运行情况: sudo svstat /service/XXX
有一种情况是 up 0秒,或者1秒,并且 pid 一直在变,说明你的应用启动之后由于某种异常马上就挂掉了,然后一直被 supervise 重启,这是就要检查启动 log,看看出现什么问题了(有时候磁盘空间满了也会这样)
启动、停止某个 service
sudo svc -u /service/XXX
sudo svc -d /service/XXX
如果遇到一个应用启动了多个实例的问题,先把 supervise 停掉,然后把进程都 kill 掉,然后再启动 supervise。
数据库
时时要考虑哪些查询是应该连只读库的,尽量去减少主库的压力,尽量分担主库的压力,也有的公司是用ameoba、corba之类软件强制实现的,这个时候,主从问题要注意解决
主从问题,引起的很多问题,改了又改等等,问题很多,多注意
数据库大小写不敏感,但是程序大小写敏感,多注意
只读库不能执行update delete insert这些SQL,所有的表都是从主库同步到只读库的,否则,会把同步搞死,只读库只能查询,特别是复杂的查询,务必放在只读库
对于MySQL,如何设置主键?最好不要用uuid()(太长),也不要用MySQL的自增主键(不利于分布),而是用程序自定义一个全局的自增主键为好
关于SQL的优化问题,有种观点认为,从技术上对SQL的优化空间是不大的,一般也就是5-20%,应该更多地考虑从业务上,从结构上,从框架上进行优化
项目管理
原则上,没有经过用户现场Release测试的项目,不允许上线,就是说所有准备上线项目必须在DEMO环境先测试成功,这个过程实际就是"UAT"验收
到用户一线去,多倾听第一线用户的声音,亲身去体验各流程的操作,尊重一线用户对系统的反馈意见
技术驱动业务,技术帮助业务发展,技术可以理解整体,但业务方的很多人往往只懂某一块内容
越能控制情绪的人沟通水平越高,越有前途
以人为本,吸收优点,摒弃缺点
WEB
1、ajax是不能跨域的,在html中,只有src标记才是可以跨域的。
2、jsTree3.0插件ajax方式使用:它是根据服务器返回的Content-Type来进行工作的,只支持两种类型:html和json,在nginx服务器下,.json返回的时json类型,.html返回的是html类型;在jetty下,.html返回的是html类型,.json返回的是null类型,如果想让某个方法返回的Content-Type是json,需要增加:response.setContentType("application/json;charset=UTF-8");
3、Nginx不允许向静态文件POST,会返回错误:405,Not Allowed
4、对于页面中的JS,如果改动频繁,可以单独抽出来做成一个文件,比如如下,用第二行替换第一行:
<#--<script type="text/javascript" src="/xx.js"></script>-->
<!--#include virtual="/inc/xx.shtml"-->
xx.shtml内如如下:
<script type="text/javascript" src="/xx.js"></script>
监控
Nagios监控:在被监控的机器上以及监控服务器上要分别安装客户端和服务器端。发报警短信。
配置文件:/usr/local/nagios/etc/nrpe.cfg
command[check load]=/usr/local/nagios/libexec/check load -w 80,85,86 -c 85,87,88
w warning c critical
3个数字代表:user,system,iowait 用户占用,系统占用,IO占用
zabbix监控:提供Web界面查看监控效果。
这篇关于日常工作检查表-----Check List的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!