resin

org.springframework.dao.IncorrectResultSizeDataAccessException

今天在写一个游戏接口的时候,以为数据库中就一条记录,想当然的写下了

select appid, code, name, url from hall_games where code=:code

测试机上一运行就出现这个异常。
org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 0 or 1, actual 8: com.xiaonei.in.dao.LoginUserDAO#getHallGameInfo

于是查看文档。
Data access exception thrown when a result was not of the expected size, for example when expecting a single row but getting 0 or more than 1 rows。
当期望返回的结果记录是1时,如果返回值为0或者>1会抛此异常。于是修改sql,当需要一条数据的时候,还是老实加上limit 1比较好。

select appid, code, name, url from hall_games where code=:code limit 1

Use crontab to finish works which will deal with access logs and load into infobright database

在昨天的文章中,我们讲述了如何使用Python来访问Java程序,在今天我们继续聊聊脚本的知识。最近由于业务的需要,给数据运维人员搞了个先进的数据查询引擎-Infobright,有关infobright的鼎鼎大名早已经是如雷贯耳,数据压缩上确实很牛叉,可以参考《Infobright数据仓库搭建》《Infobright的数据类型以及优化相关》。这几篇都跟mysql优化类似,但是略有不同还是注意的好,为了更好的优化效果,比如我在创建数据库表的时候就把原来的Varchar类型的字段创建成了Char,因为varchar的效率很低。
数据引擎搭建起来了,接下来我们需要自动的把数据加载到我们的数据库,由于众所周知的原因,目前社区办的ICE只支持load加载数据,所以我们得把我们的日志文件先处理成Infobright能够接受的类型,比如常见的csv格式。然后使用脚本自动的把我们的结果数据搞到数据库里面就可以了。
第一个脚本,完成日志目录的创建,日志合并,日志处理

#!/bin/sh

export LOG_DIR=/data1/remote-log-server/log/
today=`date "+%Y-%m-%d"`
yestoday=`date -d '1 days ago' "+%Y-%m-%d"`

GameLoginPath=$LOG_DIR/gamelogin/$today

#merge yesterday log
/bin/cat $LOG_DIR/gamelogin/$yestoday/gamelogin.log.* > $LOG_DIR/gamelogin/$yestoday/gamelogin.log
#use python script deal with access log to infobright data format
/usr/bin/python /data1/weige/python/gamelogin_weige.py $yestoday 99
#auto load data which can be accepted by infobright engine
/bin/sh /data1/remote-log-server/update_data_to_db.sh $yestoday

#create today log dir
if [ ! -d "$GameLoginPath" ]; then
        /bin/mkdir -p "$GameLoginPath"
fi

接下来的章节,我们将详细分拆解释我们这个脚本中间运行的这三个脚本,同时进一步对resin容器进行优化。

Resin服务相关-日志统计SocketServer

在前面的章节中,我们讲述了Resin本地部署服务脚本Resin更新服务脚本,本章中,我们重点讲述下对log的收集,在日常工作中我们对一些不重要的数据往往不会保存到数据库,而是直接打印到文件的方式,具体到应用的时候在对log文件进行分析,但是线上运行的机器往往都是多台,每台分析相同的事情或者对所有数据做合并处理的时候就显得很不方便,于是我们可以采用一种集中管理的方式,log4j的包为我们提供了丰富的功能,不用再自己实现,远程log用的是SocketServer。

假设10.2.1.100,10.2.1.200是客户端机器,10.2.1.500是log server.

在客户端log4j的配置文件中,设置远程记录log,并设置好端口

        
                
                
                
                
                
        

100和200做同样的设置,对于服务器端500 server需要进行区分。
服务器端默认的配置文件记录在lcf文件夹中,每个文件代表一个客户端机器,目录如下:

[root@SJSWT45-26 lcf]# ls -l
总计 36
-rw-r--r-- 1 root root  893 2010-08-03 10.2.1.100.lcf
-rw-r--r-- 1 root root  895 2010-08-03 10.2.1.200.lcf

打开看一下,具体的配置文件,以100.lcf为例子

log4j.logger.promotion=DEBUG,FILELOGER2
log4j.additivity.promotion=false
log4j.appender.FILELOGER2=org.apache.log4j.DailyRollingFileAppender
log4j.appender.FILELOGER2.DatePattern='.'yyyy-MM-dd-HH
log4j.appender.FILELOGER2.encoding=UTF-8
log4j.appender.FILELOGER2.File=/data/remote-log-server/log/promition1.log      
log4j.appender.FILELOGER2.layout=org.apache.log4j.PatternLayout  
log4j.appender.FILELOGER2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss},%m %n

大功告成,现在我们把服务器500服务器的log服务启动起来,需要activation-1.1.jar 和log4j-1.2.16.jar两个包,如果加入自动发邮件的功能话,还需要引入mail-1.4.1.jar 。启动脚本如下:

/opt/j2sdk/bin/java -Xmx1G -cp /data/remote-log-server/lib/activation-1.1.jar:/data/remote-log-server/lib/mail-1.4.1.jar:/data/remote-log-server/lib/log4j-1.2.16.jar org.apache.log4j.net.SocketServer 9123 /data/remote-log-server/logserver.properties /data/remote-log-server/lcf

为了方便改动后,重启服务生效,我们可以写个shell脚本:

#!/bin/sh

ulimit -n 1000000 -s unlimited -u unlimited
export LIB_DIR=/data/remote-log-server/lib
#set -x
/opt/j2sdk/bin/java -Xmx1G -cp $LIB_DIR/activation-1.1.jar:$LIB_DIR/mail-1.4.1.jar:$LIB_DIR/log4j-1.2.16.jar org.apache.log4j.net.SocketServer "$@" &
SPID=$!
sleep 1
echo $SPID > /var/run/log4j.pid

其中,”$@”代表shell脚本传入的参数,在该实例中,需要传入三个参数,分别对应端口port服务器日志配置客户端收集log的配置信息目录lcf.

然后写个脚本来检测我们的服务端口9123就行了。

#!/bin/sh

out=`/bin/netstat -nlt | grep :9123`
if [ -n "$out" ]; then
        echo 'PORT 9123 OK !!! '`date`
else
        echo 'PORT 9123 HAS DOWN...... RESTART...... '`date`
        /sbin/service log4j start
fi

配置log服务的重启脚本如下,放在/ect/init.d/目录下,这儿我们命名为remote_log4j, vim /etc/init.d/remote_log4j

#!/bin/bash
#
# Startup script for Log4J Socket Server
#
# chkconfig: - 86 15
# description: Log4J Socket Server
# processname: log4j

ulimit -n 1000000 -s unlimited -u unlimited

JAVA_HOME=/opt/j2sdk
export JAVA_HOME
start_log4j="/data/remote-log-server/bin/startSockerServer.sh  9123 /data/remote-log-server/logserver.properties /data/remote-log-server/lcf "
stop_log4j="kill -9 `cat /var/run/log4j.pid`"

start() {
        echo -n "Starting log4j Socket Server:"
        ${start_log4j}
        echo "done."
}
stop() {
        echo -n "Shutting down log4j: "
        ${stop_log4j}
        echo "done."
}

# See how we were called
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        stop
        sleep 1
        start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
esac

exit 0

Resin相关服务脚本(2)

前面的章节(1)中,我们发布了本地部署脚本,可以轻松一键把源代码从svn按照既定规则打包部署到测试机器上,在本节中,我们接着讲述,怎么把代码从测试机更新到线上机器。
基本原理很简单,主要使用scp的功能,scp大家都熟能生巧。scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的。我们需要获得远程服务器上的某个文件,远程服务器既没有配置ftp服务器,没有开启web服务器,也没有做共享,无法通过常规途径获得文件时,只需要通过scp命令便可轻松的达到目的。
更新脚本如下:

#!/bin/sh
TARGET_HOST=$1
USER=root
ssh $USER@$TARGET_HOST " rm -rf /opt/www/*; "
scp -r /opt/xei_register/www/* $USER@$TARGET_HOST:/opt/www/
ssh $USER@$TARGET_HOST " killall -9 perl java; sh /opt/resin/bin/httpd.sh start "
ssh $USER@$TARGET_HOST " tail -f /opt/resin/log/stdout.log;tail -f /opt/resin/log/stderr.log"

Resin本地部署服务脚本(1)

在工作中,写好的程序一般会丢到测试机器上运行,等待测试彻底通过后再部署到线上机器。为了方便操作,我们一般会有统一的部署脚本,对服务器的管理,一般情况下得需要以下几个:

  1. 备份脚本,备份线上当前使用版本,方便更新后有问题直接回滚;

  2. 本地部署脚本,一般情况下会从代码管理机器,如svn,cvs等发布到测试机器,测试机器实际上就是类似线上服务器的配置,当测试没问题后,直接把代码做scp的操作即可。

  3. 代码更新脚本,把以上2的代码更新到线上机器。

  4. 代码回滚脚本,如果3有问题,则回滚到1备份的状态。

本系列的文章中,我们将逐一分析各个脚本,本次重点讲述部署脚本。

  1 #!/bin/sh
  2 ########################################################################
  3 _project_package_name="somebiz-project"
  4 _project_svn="http://svn.d.google.com/sns/xei/xei-reg-guide-usertrace/trunk/"
  5 _project_local="/data/staging/xei_minihome"
  6 _project_deploy="/opt/xei_minihome"
  7 _static_local="/data/staging/xn.static"
  8 _static_deploy="/opt/static"
  9 _inc_local="/data/staging/xei_minihome/src/main/webapp/inc"
 10 ########################################################################
 11 
 12 echo "Best wishes for you *_*"
 13 today=`date +%F-%H:%M:%S`
 14 #check deploy is doing or not
 15 
 16 echo "开始时间" >> time.log
 17 `echo date` >> time.log
 18 
 19 echo "step 1. update maven project.."
 20 if [ ! -d $_project_local/.svn/ ] ;then
 21     svn co $_project_svn  $_project_local
 22 else
 23     svn up $_project_local
 24 fi
 25 
 26 echo "step 1.1 begin update inc to project.."
 27 if [ ! -d $_inc_local/.svn/ ] ;then
 28     svn co http://svn.d.google.com/frontend/xn.inc  $_inc_local
 29 else
 30     svn up $_inc_local
 31 fi
 32 
 33 echo "step 2. maven project"
 34 p="`pwd`"
 35 cd $_project_local
 36 mvn -f pom.xml -U clean package
 37 cd - >> $p/./time.log 2>&1
if [ "`ls $_project_local/target/*.war 2>/dev/null`" == "" ]; then echo "Oops, an ERROR !!! Send the file "time.log" to  
    [webmaster@sohu-inc.com]"; exit ; fi
 39 
 40 
 41 echo "step 3. update static..."
 42 if [ ! -d $_static_local/.svn/ ] ;then
 43     svn co http://svn.d.google.com/frontend/xn.static $_static_local
 44 else
 45     svn up $_static_local
 46 fi
 47 
 48 echo "step 4. replace static compress js css html..." 
 49 test -d $_static_deploy || mkdir -p $_static_deploy
 50 #thanks for xiaoai
 51 java -cp `pwd`/xei-split-version.jar com/xei/deploy/tools/Worker $_static_local $_static_deploy $_project_local/
    target/$_project_package_name/
 52 
 53 echo "step 5. cp maven project"
 54 rsync -rtzvl --delete $_project_local/target/$_project_package_name/ $_project_deploy/
 55 rsync -rtzvlC $_static_local/ $_static_deploy/
 56 
 57 echo "So fast,so NB!!!"
 58 echo "结束时间" >> time.log
 59 `echo date` >> time.log
 60 
 61 today=`date +%F-%H:%M:%S`
 62 echo "test mechine is now running normally! Last deployed time is $today"