日常工作检查表-----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文件中,要用 &amp; 分隔,例如:

jdbc.url=jdbc:mysql://localhost:3306/user_db?useUnicode=true&amp;characterEncoding=utf8&amp;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界面查看监控效果。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值