Redis的三种集群方案简介(Master, Slave, Sentinel, Cluster)

 redis  Redis的三种集群方案简介(Master, Slave, Sentinel, Cluster)已关闭评论
7月 292021
 

重温下redis集群的三种集群方案,分享下网络上的一篇文章,写的很好, 原文链接见文末。

 

在开发测试环境中,我们一般搭建Redis的单实例来应对开发测试需求,但是在生产环境,如果对可用性、可靠性要求较高,则需要引入Redis的集群方案。虽然现在各大云平台有提供缓存服务可以直接使用,但了解一下其背后的实现与原理总还是有些必要(比如面试), 本文就一起来学习一下Redis的几种集群方案。

Redis支持三种集群方案

  • 主从复制模式
  • Sentinel(哨兵)模式
  • Cluster模式

主从复制模式

1. 基本原理

主从复制模式中包含一个主数据库实例(master)与一个或多个从数据库实例(slave),如下图

redis-master-slave

客户端可对主数据库进行读写操作,对从数据库进行读操作,主数据库写入的数据会实时自动同步给从数据库。

具体工作机制为:

  1. slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照(即上文所介绍的RDB持久化),并使用缓冲区记录保存快照这段时间内执行的写命令
  2. master将保存的快照文件发送给slave,并继续记录执行的写命令
  3. slave接收到快照文件后,加载快照文件,载入数据
  4. master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化
  5. 此后master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性

2. 部署示例

本示例基于Redis 5.0.3版。

redis.conf的主要配置

###网络相关###
# bind 127.0.0.1 # 绑定监听的网卡IP,注释掉或配置成0.0.0.0可使任意IP均可访问
protected-mode no # 关闭保护模式,使用密码访问
port 6379  # 设置监听端口,建议生产环境均使用自定义端口
timeout 30 # 客户端连接空闲多久后断开连接,单位秒,0表示禁用

###通用配置###
daemonize yes # 在后台运行
pidfile /var/run/redis_6379.pid  # pid进程文件名
logfile /usr/local/redis/logs/redis.log # 日志文件的位置

###RDB持久化配置###
save 900 1 # 900s内至少一次写操作则执行bgsave进行RDB持久化
save 300 10
save 60 10000 
# 如果禁用RDB持久化,可在这里添加 save ""
rdbcompression yes #是否对RDB文件进行压缩,建议设置为no,以(磁盘)空间换(CPU)时间
dbfilename dump.rdb # RDB文件名称
dir /usr/local/redis/datas # RDB文件保存路径,AOF文件也保存在这里

###AOF配置###
appendonly yes # 默认值是no,表示不使用AOF增量持久化的方式,使用RDB全量持久化的方式
appendfsync everysec # 可选值 always, everysec,no,建议设置为everysec

###设置密码###
requirepass 123456 # 设置复杂一点的密码

部署主从复制模式只需稍微调整slave的配置,在redis.conf中添加

replicaof 127.0.0.1 6379 # master的ip,port
masterauth 123456 # master的密码
replica-serve-stale-data no # 如果slave无法与master同步,设置成slave不可读,方便监控脚本发现问题

本示例在单台服务器上配置master端口6379,两个slave端口分别为7001,7002,启动master,再启动两个slave

[root@dev-server-1 master-slave]# redis-server master.conf
[root@dev-server-1 master-slave]# redis-server slave1.conf
[root@dev-server-1 master-slave]# redis-server slave2.conf

进入master数据库,写入一个数据,再进入一个slave数据库,立即便可访问刚才写入master数据库的数据。如下所示

[root@dev-server-1 master-slave]# redis-cli 
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> set site blog.jboost.cn
OK
127.0.0.1:6379> get site
"blog.jboost.cn"
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7001,state=online,offset=13364738,lag=1
slave1:ip=127.0.0.1,port=7002,state=online,offset=13364738,lag=0
...
127.0.0.1:6379> exit

[root@dev-server-1 master-slave]# redis-cli -p 7001
127.0.0.1:7001> auth 123456
OK
127.0.0.1:7001> get site
"blog.jboost.cn"

执行info replication命令可以查看连接该数据库的其它库的信息,如上可看到有两个slave连接到master

3. 主从复制的优缺点

优点:

  1. master能自动将数据同步到slave,可以进行读写分离,分担master的读压力
  2. master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求

缺点:

  1. 不具备自动容错与恢复功能,master或slave的宕机都可能导致客户端请求失败,需要等待机器重启或手动切换客户端IP才能恢复
  2. master宕机,如果宕机前数据没有同步完,则切换IP后会存在数据不一致的问题
  3. 难以支持在线扩容,Redis的容量受限于单机配置

Sentinel(哨兵)模式

1. 基本原理

哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。如图

redis-sentinel

哨兵顾名思义,就是来为Redis集群站哨的,一旦发现问题能做出相应的应对处理。其功能包括

  1. 监控master、slave是否正常运行
  2. 当master出现故障时,能自动将一个slave转换为master(大哥挂了,选一个小弟上位)
  3. 多个哨兵可以监控同一个Redis,哨兵之间也会自动监控

哨兵模式的具体工作机制:

在配置文件中通过 sentinel monitor <master-name> <ip> <redis-port> <quorum> 来定位master的IP、端口,一个哨兵可以监控多个master数据库,只需要提供多个该配置项即可。哨兵启动后,会与要监控的master建立两条连接:

  1. 一条连接用来订阅master的_sentinel_:hello频道与获取其他监控该master的哨兵节点信息
  2. 另一条连接定期向master发送INFO等命令获取master本身的信息

与master建立连接后,哨兵会执行三个操作:

  1. 定期(一般10s一次,当master被标记为主观下线时,改为1s一次)向master和slave发送INFO命令
  2. 定期向master和slave的_sentinel_:hello频道发送自己的信息
  3. 定期(1s一次)向master、slave和其他哨兵发送PING命令

发送INFO命令可以获取当前数据库的相关信息从而实现新节点的自动发现。所以说哨兵只需要配置master数据库信息就可以自动发现其slave信息。获取到slave信息后,哨兵也会与slave建立两条连接执行监控。通过INFO命令,哨兵可以获取主从数据库的最新信息,并进行相应的操作,比如角色变更等。

接下来哨兵向主从数据库的_sentinel_:hello频道发送信息与同样监控这些数据库的哨兵共享自己的信息,发送内容为哨兵的ip端口、运行id、配置版本、master名字、master的ip端口还有master的配置版本。这些信息有以下用处:

  1. 其他哨兵可以通过该信息判断发送者是否是新发现的哨兵,如果是的话会创建一个到该哨兵的连接用于发送PING命令。
  2. 其他哨兵通过该信息可以判断master的版本,如果该版本高于直接记录的版本,将会更新
  3. 当实现了自动发现slave和其他哨兵节点后,哨兵就可以通过定期发送PING命令定时监控这些数据库和节点有没有停止服务。

如果被PING的数据库或者节点超时(通过 sentinel down-after-milliseconds master-name milliseconds 配置)未回复,哨兵认为其主观下线(sdown,s就是Subjectively —— 主观地)。如果下线的是master,哨兵会向其它哨兵发送命令询问它们是否也认为该master主观下线,如果达到一定数目(即配置文件中的quorum)投票,哨兵会认为该master已经客观下线(odown,o就是Objectively —— 客观地),并选举领头的哨兵节点对主从系统发起故障恢复。若没有足够的sentinel进程同意master下线,master的客观下线状态会被移除,若master重新向sentinel进程发送的PING命令返回有效回复,master的主观下线状态就会被移除

哨兵认为master客观下线后,故障恢复的操作需要由选举的领头哨兵来执行,选举采用Raft算法:

  1. 发现master下线的哨兵节点(我们称他为A)向每个哨兵发送命令,要求对方选自己为领头哨兵
  2. 如果目标哨兵节点没有选过其他人,则会同意选举A为领头哨兵
  3. 如果有超过一半的哨兵同意选举A为领头,则A当选
  4. 如果有多个哨兵节点同时参选领头,此时有可能存在一轮投票无竞选者胜出,此时每个参选的节点等待一个随机时间后再次发起参选请求,进行下一轮投票竞选,直至选举出领头哨兵

选出领头哨兵后,领头者开始对系统进行故障恢复,从出现故障的master的从数据库中挑选一个来当选新的master,选择规则如下:

  1. 所有在线的slave中选择优先级最高的,优先级可以通过slave-priority配置
  2. 如果有多个最高优先级的slave,则选取复制偏移量最大(即复制越完整)的当选
  3. 如果以上条件都一样,选取id最小的slave

挑选出需要继任的slave后,领头哨兵向该数据库发送命令使其升格为master,然后再向其他slave发送命令接受新的master,最后更新数据。将已经停止的旧的master更新为新的master的从数据库,使其恢复服务后以slave的身份继续运行。

2. 部署演示

本示例基于Redis 5.0.3版。

哨兵模式基于前文的主从复制模式。哨兵的配置文件为sentinel.conf,在文件中添加

sentinel monitor mymaster 127.0.0.1 6379 1 # mymaster定义一个master数据库的名称,后面是master的ip, port,1表示至少需要一个Sentinel进程同意才能将master判断为失效,如果不满足这个条件,则自动故障转移(failover)不会执行
sentinel auth-pass mymaster 123456 # master的密码

sentinel down-after-milliseconds mymaster 5000 # 5s未回复PING,则认为master主观下线,默认为30s
sentinel parallel-syncs mymaster 2  # 指定在执行故障转移时,最多可以有多少个slave实例在同步新的master实例,在slave实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长
sentinel failover-timeout mymaster 300000 # 如果在该时间(ms)内未能完成故障转移操作,则认为故障转移失败,生产环境需要根据数据量设置该值

一个哨兵可以监控多个master数据库,只需按上述配置添加多套

分别以26379,36379,46379端口启动三个sentinel

[root@dev-server-1 sentinel]# redis-server sentinel1.conf --sentinel
[root@dev-server-1 sentinel]# redis-server sentinel2.conf --sentinel
[root@dev-server-1 sentinel]# redis-server sentinel3.conf --sentinel

也可以使用redis-sentinel sentinel1.conf 命令启动。此时集群包含一个master、两个slave、三个sentinel,如图,

redis-cluster-instance

我们来模拟master挂掉的场景,执行 kill -9 3017 将master进程干掉,进入slave中执行 info replication查看,

[root@dev-server-1 sentinel]# redis-cli -p 7001
127.0.0.1:7001> auth 123456
OK
127.0.0.1:7001> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:7002
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
# 省略
127.0.0.1:7001> exit
[root@dev-server-1 sentinel]# redis-cli -p 7002
127.0.0.1:7002> auth 123456
OK
127.0.0.1:7002> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=7001,state=online,offset=13642721,lag=1
# 省略

可以看到slave 7002已经成功上位晋升为master(role:master),接收一个slave 7001的连接。此时查看slave2.conf配置文件,发现replicaof的配置已经被移除了,slave1.conf的配置文件里replicaof 127.0.0.1 6379 被改为 replicaof 127.0.0.1 7002。重新启动master,也可以看到master.conf配置文件中添加了replicaof 127.0.0.1 7002的配置项,可见大哥(master)下位后,再出来混就只能当当小弟(slave)了,三十年河东三十年河西。

3. 哨兵模式的优缺点

优点:

  1. 哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有
  2. 哨兵模式下,master挂掉可以自动进行切换,系统可用性更高

缺点:

  1. 同样也继承了主从模式难以在线扩容的缺点,Redis的容量受限于单机配置
  2. 需要额外的资源来启动sentinel进程,实现相对复杂一点,同时slave节点作为备份节点不提供服务

Cluster模式

1. 基本原理

哨兵模式解决了主从复制不能自动故障转移,达不到高可用的问题,但还是存在难以在线扩容,Redis容量受限于单机配置的问题。Cluster模式实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。如图

redis-cluster

Cluster采用无中心结构,它的特点如下:

  1. 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽
  2. 节点的fail是通过集群中超过半数的节点检测失效时才生效
  3. 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

Cluster模式的具体工作机制:

  1. 在Redis的每个节点上,都有一个插槽(slot),取值范围为0-16383
  2. 当我们存取key的时候,Redis会根据CRC16的算法得出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作
  3. 为了保证高可用,Cluster模式也引入主从复制模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点
  4. 当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点都宕机了,那么该集群就无法再提供服务了

Cluster模式集群节点最小配置6个节点(3主3从,因为需要半数以上),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

2. 部署演示

本示例基于Redis 5.0.3版。

Cluster模式的部署比较简单,首先在redis.conf中

port 7100 # 本示例6个节点端口分别为7100,7200,7300,7400,7500,7600 
daemonize yes # r后台运行 
pidfile /var/run/redis_7100.pid # pidfile文件对应7100,7200,7300,7400,7500,7600 
cluster-enabled yes # 开启集群模式 
masterauth passw0rd # 如果设置了密码,需要指定master密码
cluster-config-file nodes_7100.conf # 集群的配置文件,同样对应7100,7200等六个节点
cluster-node-timeout 15000 # 请求超时 默认15秒,可自行设置 

分别以端口7100,7200,7300,7400,7500,7600 启动六个实例(如果是每个服务器一个实例则配置可一样)

[root@dev-server-1 cluster]# redis-server redis_7100.conf
[root@dev-server-1 cluster]# redis-server redis_7200.conf
...

然后通过命令将这个6个实例组成一个3主节点3从节点的集群,

redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7100 127.0.0.1:7200 127.0.0.1:7300 127.0.0.1:7400 127.0.0.1:7500 127.0.0.1:7600 -a passw0rd

执行结果如图

redis-cluster-deploy

可以看到 7100, 7200, 7300 作为3个主节点,分配的slot分别为 0-5460, 5461-10922, 10923-16383, 7600作为7100的slave, 7500作为7300的slave,7400作为7200的slave。

我们连接7100设置一个值

[root@dev-server-1 cluster]# redis-cli -p 7100 -c -a passw0rd
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7100> set site blog.jboost.cn
-> Redirected to slot [9421] located at 127.0.0.1:7200
OK
127.0.0.1:7200> get site
"blog.jboost.cn"
127.0.0.1:7200>

注意添加 -c 参数表示以集群模式,否则报 (error) MOVED 9421 127.0.0.1:7200 错误, 以 -a 参数指定密码,否则报(error) NOAUTH Authentication required错误。

从上面命令看到key为site算出的slot为9421,落在7200节点上,所以有Redirected to slot [9421] located at 127.0.0.1:7200,集群会自动进行跳转。因此客户端可以连接任何一个节点来进行数据的存取。

通过cluster nodes可查看集群的节点信息

127.0.0.1:7200> cluster nodes
eb28aaf090ed1b6b05033335e3d90a202b422d6c 127.0.0.1:7500@17500 slave c1047de2a1b5d5fa4666d554376ca8960895a955 0 1584165266071 5 connected
4cc0463878ae00e5dcf0b36c4345182e021932bc 127.0.0.1:7400@17400 slave 5544aa5ff20f14c4c3665476de6e537d76316b4a 0 1584165267074 4 connected
dbbb6420d64db22f35a9b6fa460b0878c172a2fb 127.0.0.1:7100@17100 master - 0 1584165266000 1 connected 0-5460
d4b434f5829e73e7e779147e905eea6247ffa5a2 127.0.0.1:7600@17600 slave dbbb6420d64db22f35a9b6fa460b0878c172a2fb 0 1584165265000 6 connected
5544aa5ff20f14c4c3665476de6e537d76316b4a 127.0.0.1:7200@17200 myself,master - 0 1584165267000 2 connected 5461-10922
c1047de2a1b5d5fa4666d554376ca8960895a955 127.0.0.1:7300@17300 master - 0 1584165268076 3 connected 10923-16383

我们将7200通过 kill -9 pid杀死进程来验证集群的高可用,重新进入集群执行cluster nodes可以看到7200 fail了,但是7400成了master,重新启动7200,可以看到此时7200已经变成了slave。

3. Cluster模式的优缺点

优点:

  1. 无中心架构,数据按照slot分布在多个节点。
  2. 集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
  3. 可线性扩展到1000多个节点,节点可动态添加或删除
  4. 能够实现自动故障转移,节点之间通过gossip协议交换状态信息,用投票机制完成slave到master的角色转换

缺点:

  1. 客户端实现复杂,驱动要求实现Smart Client,缓存slots mapping信息并及时更新,提高了开发难度。目前仅JedisCluster相对成熟,异常处理还不完善,比如常见的“max redirect exception”
  2. 节点会因为某些原因发生阻塞(阻塞时间大于 cluster-node-timeout)被判断下线,这种failover是没有必要的
  3. 数据通过异步复制,不保证数据的强一致性
  4. slave充当“冷备”,不能缓解读压力
  5. 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好
  6. key事务操作支持有线,只支持多key在同一节点的事务操作,多key分布不同节点时无法使用事务功能
  7. 不支持多数据库空间,单机redis可以支持16个db,集群模式下只能使用一个,即db 0

Redis Cluster模式不建议使用pipeline和multi-keys操作,减少max redirect产生的场景。

总结

本文介绍了Redis集群方案的三种模式,其中主从复制模式能实现读写分离,但是不能自动故障转移;哨兵模式基于主从复制模式,能实现自动故障转移,达到高可用,但与主从复制模式一样,不能在线扩容,容量受限于单机的配置;Cluster模式通过无中心化架构,实现分布式存储,可进行线性扩展,也能高可用,但对于像批量操作、事务操作等的支持性不够好。三种模式各有优缺点,可根据实际场景进行选择。

 

转自:https://segmentfault.com/a/1190000022028642

Nginx配置basic_auth密码验证

 nginx  Nginx配置basic_auth密码验证已关闭评论
4月 292020
 

网上找到的一篇文章描述nginx配置basic_auth的,来自以下链接https://www.centos.bz/2017/07/nginx-basic_auth-password/

 

Nginx添加basic_auth,意思就是访问页面的时候需要弹出来一个用户和密码验证的东西,本文基于CentOS 6

1. 安装密码生成工具htpasswd并生成用户密码文件

yum install httpd-tools               #适用centos
sudo apt-get install apache2-utils    #适用ubuntu

生成用户密码文件

$ htpasswd -c /export/servers/.htpasswd user1  #回车会要求输入两遍密码,会清除所有用户!
$ htpasswd -bc /export/servers/.htpasswd user1 password  #不用回车,直接指定user1的密码为password
$ htpasswd -b /export/servers/.htpasswd user2 password   #添加一个用户,如果用户已存在,则是修改密码
$ htpasswd -D /export/servers/.htpasswd user2  #删除用户

2. 为Nginx添加basic_auth配置

server {
    listen        80;
#    root        /tmp;
#    index        index.html index.htm;
    server_name    o-u-u.com;

    location / {
        auth_basic        "input you user name and password";
        auth_basic_user_file    /export/servers/.htpasswd;
        proxy_pass http://127.0.0.1:9000;
    }
}

然后再次访问o-u-u.com时便会弹出验证框要求输入用户名和密码。

3. 可能遇到的问题

访问o-u-u.com没有弹出验证框怎么办?
首先修改nginx.conf,将日志级别调为info,如下

$ cat /export/servers/nginx-1.12.1/conf/nginx.conf
.......
user  admin;
worker_processes  8;

error_log  logs/error.log info;
......

然后再次访问让其产error_log
看到error_log时会发现有如下错误产生

*69 no user/password was provided for basic authentication, client: 10.12.138.126, server: www.zhukun.net, request: "GET /date_lateral HTTP/1.1", host: "www.zhukun.net"

原因在于

The HTTP Basic authentication works as following:
*) A browser requests a page without user/password.
*) A server response with 401 page, sending realm as well.
   At this stage the 401 code appears in access_log and the message
   “no user/password …” appears in error_log.
*) The browser shows a realm/login/password prompt.
*) If a user will press cancel, then the browser will show the received
   401 page.
*) If the user enters login/password, then the browser repeats the request
   with login/password.

Then until you will exit the browser, it will send these login/password
with all requests in protected hierarchy.

error_page配置的401页面不存在或者指向问题导致的,可以注释掉401配置或者保证401配置指向的文件可用,然后basic_auth便会生效。

mac os使用tunnelblick作为openvpn客户端

 mac  mac os使用tunnelblick作为openvpn客户端已关闭评论
12月 302019
 

openvpn官网有对应的windows客户端可以下载,但没有mac os的对应版本,而Tunnelblick 是适用于Mac OS X 的OpenVPN Client 的一个GUI 版本,相当好用。

下面介绍下Tunelblick的安装和配置:

1. 安装 Tunnelblick

从以下链接下载和安装 Tunnelblick。

https://tunnelblick.net/downloads.html#releases

选择stable稳定版,我这里选择的Tunnelblick3.8.1

 

2. 双击打开

将配置文件xxx.ovpn 按下图拖入配置框

完成后点击“连接”

 

网络上也有一些免费的公共vpn配置文件可以下载,但稳定性和有效性就自己考量了。

OpenVPN 连接设置文件(.ovpn)从 公共 VPN 中继服务器列表页面 下载。

云服务器EC2配置Swap分区解决Cannot Allocate Memory

 centos  云服务器EC2配置Swap分区解决Cannot Allocate Memory已关闭评论
9月 192019
 

今天在EC2(Centos 7)上使用nvm 安装node时出现下面错误提示:

# nvm install 8.16.1
Downloading and installing node v8.16.1…
Downloading https://nodejs.org/dist/v8.16.1/node-v8.16.1-linux-x64.tar.xz…
######################################################################## 100.0%
Computing checksum with sha256sum
Checksums matched!
xz: (stdin): Cannot allocate memory
tar: Child returned status 1
tar: Error is not recoverable: exiting now
Binary download failed, trying source.
Downloading https://nodejs.org/dist/v8.16.1/node-v8.16.1.tar.xz…
######################################################################## 100.0%
Computing checksum with sha256sum
Checksums matched!
xz: (stdin): Cannot allocate memory
tar: Child returned status 1
tar: Error is not recoverable: exiting now
nvm: install v8.16.1 failed!

这台EC2内存只有1G,看来需要配个交换分区。

 

步骤

# 创建一个全0填充的2GB文件。InputFile利用Linux系统的/dev/zero。bs是BlockSize。

sudo dd if=/dev/zero of=/mnt/2GB.swap bs=1M count=2048

 

# 把该文件格式化为Swap文件

sudo mkswap /mnt/2GB.swap

 

# 把该Swap文件挂载为Swap分区

sudo swapon /mnt/2GB.swap

 

# 检查是否成功挂载。Swap那一行现在就不是0了。

$ free -m

total        used        free      shared  buff/cache   available

Mem:            963         677          74          78         211          52

Swap:          2047          84        1963

# 配置fstab,使开机时自动挂载Swap文件为Swap分区

sudo vim /etc/fstab

 

# 在/ect/fstab中添加

/mnt/2GB.swap none swap sw 0 0

 

关于nagios配置简介

 nagios  关于nagios配置简介已关闭评论
4月 242018
 

一、nagios配置过程详解


1、nagios默认配置文件介绍
 nagios安装完毕后,默认的配置文件在/usr/local/nagios/etc目录下,每个文件或目录含义如下表所示:

 

2、配置文件之间的关系
 在nagios的配置过程中涉及到的几个定义有:主机、主机组,服务、服务组,联系人、联系人组,监控时间,监控命令等,从这些定义可以看出,nagios各个配置文件之间是互为关联,彼此引用的。
 成功配置出一台nagios监控系统,必须要弄清楚每个配置文件之间依赖与被依赖的关系,最重要的有四点:
 第一:定义监控哪些主机、主机组、服务和服务组
 第二:定义这个监控要用什么命令实现,
 第三:定义监控的时间段,
 第四:定义主机或服务出现问题时要通知的联系人和联系人组。

 

3、开始配置nagios
 为了能更清楚的说明问题,同时也为了维护方便,建议将nagios各个定义对象创建独立的配置文件:
 即为:
 创建hosts.cfg文件来定义主机和主机组
 创建services.cfg文件来定义服务
 用默认的contacts.cfg文件来定义联系人和联系人组
 用默认的commands.cfg文件来定义命令
 用默认的timeperiods.cfg来定义监控时间段
 用默认的templates.cfg文件作为资源引用文件

  (1)templates.cfg文件
 
 nagios主要用于监控主机资源以及服务,在nagios配置中称为对象,为了不必重复定义一些监控对象,Nagios引入了一个模板配置文件,将一些共性的属性定义成模板,以便于多次引用。这就是templates.cfg的作用。
 下面详细介绍下templates.cfg文件中每个参数的含义:

  1.  define contact{  
  2.         name                            generic-contact        #联系人名称,  
  3.         service_notification_period     24×7                   #当服务出现异常时,发送通知的时间段,这个时间段“7×24″在timeperiods.cfg文件中定义  
  4.         host_notification_period        24×7                   #当主机出现异常时,发送通知的时间段,这个时间段“7×24″在timeperiods.cfg文件中定义  
  5.         service_notification_options    w,u,c,r         #这个定义的是“通知可以被发出的情况”。w即warn,表示警告状态,u即unknown,表示不明状态,c即criticle,表示紧急状态,r即recover,表示恢复状态。也就是在服务出现警告状态、未知状态、紧急状态和重新恢复状态时都发送通知给使用者。  
  6.         host_notification_options       d,u,r         #定义主机在什么状态下需要发送通知给使用者,d即down,表示宕机状态,u即unreachable,表示不可到达状态,r即recovery,表示重新恢复状态。  
  7.         service_notification_commands   notify-service-by-email  #服务故障时,发送通知的方式,可以是邮件和短信,这里发送的方式是邮件,其中“notify-service-by-email”在commands.cfg文件中定义。  
  8.         host_notification_commands      notify-host-by-email     #主机故障时,发送通知的方式,可以是邮件和短信,这里发送的方式是邮件,其中“notify-host-by-email”在commands.cfg文件中定义。  
  9.         register                        0  
  10.         }  
  11.  define host{   
  12.         name                            generic-host    #主机名称,这里的主机名,并不是直接对应到真正机器的主机名,乃是对应到在主机配置文件里所设定的主机名。  
  13.         notifications_enabled           1              
  14.         event_handler_enabled           1               
  15.         flap_detection_enabled          1               
  16.         failure_prediction_enabled      1             
  17.         process_perf_data               1              
  18.         retain_status_information       1             
  19.         retain_nonstatus_information    1                
  20.         notification_period             24×7           #指定“发送通知”的时间段,也就是可以在什么时候发送通知给使用者。  
  21.         register                        0                
  22.         }  
  23.  
  24. define host{  
  25.         name                            linux-server    #主机名称  
  26.         use                             generic-host    #use表示引用,也就是将主机generic-host的所有属性引用到linux-server中来,在nagios配置中,很多情况下会用到引用。  
  27.         check_period                    24×7            #这里的check_period告诉nagios检查主机的时间段  
  28.         check_interval                  5                #nagios对主机的检查时间间隔,这里是5分钟。  
  29.         retry_interval                  1               #重试检查时间间隔,单位是分钟。  
  30.         max_check_attempts              10               #nagios对主机的最大检查次数,也就是nagios在检查发现某主机异常时,并不马上判断为异常状况,而是多试几次,因为有可能只是一时网络太拥挤,或是一些其他原因,让主机受到了一点影响,这里的10就是最多试10次的意思。  
  31.         check_command                   check-host-alive  #指定检查主机状态的命令,其中“check-host-alive”在commands.cfg文件中定义。  
  32.         notification_period             workhours      #主机故障时,发送通知的时间范围,其中“workhours”在timeperiods.cfg中进行了定义,下面会陆续讲到。  
  33.                                                          
  34.         notification_interval           120            #在主机出现异常后,故障一直没有解决,nagios再次对使用者发出通知的时间。单位是分钟。如果你觉得,所有的事件只需要一次通知就够了,可以把这里的选项设为0  
  35.         notification_options            d,u,r          #定义主机在什么状态下可以发送通知给使用者,d即down,表示宕机状态,u即unreachable,表示不可到达状态,r即recovery,表示重新恢复状态。  
  36.         contact_groups                  admins         #指定联系人组,这个“admins”在contacts.cfg文件中定义。  
  37.         register                        0  
  38.         }  
  39.  
  40. define service{  
  41.         name                            generic-service   #定义一个服务名称  
  42.         active_checks_enabled           1     
  43.         passive_checks_enabled          1      
  44.         parallelize_check                1      
  45.         obsess_over_service             1      
  46.         check_freshness                0  
  47.         notifications_enabled            1                 
  48.         event_handler_enabled           1                   
  49.         flap_detection_enabled           1                    
  50.         failure_prediction_enabled        1                   
  51.         process_perf_data               1                   
  52.         retain_status_information       1                     
  53.         retain_nonstatus_information    1                        
  54.         is_volatile                     0   
  55.         check_period                    24×7      #这里的check_period告诉nagios检查服务的时间段。              
  56.         max_check_attempts              3         #nagios对服务的最大检查次数。           
  57.         normal_check_interval           10       #此选项是用来设置服务检查时间间隔,也就是说,nagios这一次检查和下一次检查之间所隔的时间,这里是10分钟。   
  58.         retry_check_interval            2        #重试检查时间间隔,单位是分钟。            
  59.         contact_groups                  admins   #指定联系人组,同上。              
  60.         notification_options            w,u,c,r  #这个定义的是“通知可以被发出的情况”。w即warn,表示警告状态,u即unknown,表示不明状态,c即criticle,表示紧急状态,r即recover,表示恢复状态。也就是在服务出现警告状态、未知状态、紧急状态和重新恢复后都发送通知给使用者。  
  61.         notification_interval           60       #在服务出现异常后,故障一直没有解决,nagios再次对使用者发出通知的时间。单位是分钟。如果你认为,所有的事件只需要一次通知就够了,可以把这里的选项设为0。  
  62.         notification_period             24×7     #指定“发送通知”的时间段,也就是可以在什么时候发送通知给使用者。               
  63.          register                        0                       
  64.         }  
  65.  


(2)resource.cfg文件

 resource.cfg是nagios的变量定义文件,文件内容只有一行:
 

  1. $USER1$=/usr/local/nagios/libexec 
 其中,变量$USER1$指定了安装nagios插件的路径,如果把插件安装在了其它路径,只需在这里进行修改即可。需要注意的是,变量必须先定义,然后才能在其它配置文件中进行引用。

(3)理解Nagios宏及其工作机制

Nagios配置非常灵活,继承和引用是一大特征,另一个重要特征就是可以在命令行的定义里使用宏,通过定义宏,nagios可以灵活的获取主机、服务和其它对象的信息。
宏的工作机制
在执行命令之前,nagios将对命令里的每个宏替换成它们应当取得的值。这种宏替换发生在Nagios执行各种类型的宏时候。例如主机和服务的检测、通知、事件处理等。
 

宏的分类:
默认宏、按需而成的宏、用户自定制宏等。
 

默认宏
主机IP地址宏
当在命令定义中使用主机或服务宏时,宏将要执行所用的值指向主机或服务所带有值。看下面这个例子,假定在check_ping命令定义里使用了一个主机对象,例如这样:

  1. define host{  
  2.   host_name               ixdba  
  3.   address   192.168.12.246  
  4.   check_command               check_ping  
  5.   …  
  6.   }  
  7. define command{  
  8.   command_name    check_ping  
  9.   command_line    /usr/local/nagios/libexec/check_ping -H $HOSTADDRESS$ -w 100.0,90% -c 200.0,60%  
  10.   } 


那么执行这个主机检测命令时展开并最终执行的将是这样的:

  1. /usr/local/nagios/libexec/check_ping -H 192.168.12.246 -w 100.0,90% -c 200.0,60% 


命令参数宏
同样你可以向命令传递参数,这样可以保证命令定义更具通用性。参数指定在对象(象主机或服务)中定义,用一个“!”来分隔,例如这样:

  1. define service{  
  2.   host_name  linuxbox  
  3.   service_description PING  
  4.   check_command              check_ping!200.0,80%!400.0,40%  
  5.   …  
  6.   } 


在上例中,服务的检测命令中含有两个参数(请参考$ARGn$宏),而$ARG1$宏将是”200.0,80%”,同时$ARG2$将是”400.0,40%”(都不带引号)。假定使用之前的主机定义并这样来定义你的check_ping命令:

  1. define command{  
  2.   command_name    check_ping  
  3.   command_line    /usr/local/nagios/libexec/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$  
  4.   } 


那么对于服务的检测命令最终将是这样子的:

  1. /usr/local/nagios/libexec/check_ping -H 192.168.12.246 -w 200.0,80% -c 400.0,40% 

 

Nagios可用的全部的宏

主机宏
$HOSTNAME$  主机简称(如”web”),取自于主机定义里的host_name域
$HOSTADDRESS$  主机地址。取自于主机定义里的address域
服务宏
$SERVICESTATE$ 服务状态描述,有w,u,c
$SERVICEDESC$   对当前服务的描述  
联系人宏
$CONTACTNAME$   表示联系人,在联系人文件中定义
通知宏
$NOTIFICATIONTYPE$    返回下面信息:(“PROBLEM”, “RECOVERY”, “ACKNOWLEDGEMENT”, “FLAPPINGSTART”, “FLAPPINGSTOP”, “FLAPPINGDISABLED”, “DOWNTIMESTART”, “DOWNTIMEEND”, or “DOWNTIMECANCELLED”).
日期/时间宏
$LONGDATETIME$  当前的日期/时间戳
文件宏
$LOGFILE$  日志文件的保存位置。
$MAINCONFIGFILE$  主配置文件的保存位置。
其他宏
$ADMINEMAIL$  全局的管理员EMail地址
$ARGn$  指向第n个命令传递参数(通知、事件处理、服务检测等)。Nagios支持最多32个参数宏

 

(4)commands.cfg文件
 此文件默认是存在的,无需修改即可使用,当然如果有新的命令需要加入时,在此文件进行添加即可。这里并未列出文件的所有内容,仅仅介绍了配置中用到的一些命令。 

  1. #下面是notify-host-by-email命令的定义  
  2. define command{  
  3.         command_name    notify-host-by-email    #命令名称,即定义了一个主机异常时发送邮件的命令。  
  4.         command_line    /usr/bin/printf “%b” “***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n” | /bin/mail -s “** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **” $CONTACTEMAIL$    #命令具体的执行方式,“-H $HOSTADDRESS$” 是定义目标主机的地址,这个地址在hosts.cfg文件中定义了。  
  5.         }  
  6. #下面是notify-host-by-email命令的定义  
  7. define command{  
  8.         command_name    notify-service-by-email   #命令名称,即定义了一个服务异常时发送邮件的命令  
  9.         command_line    /usr/bin/printf “%b” “***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$” | /bin/mail -s “** $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$ **” $CONTACTEMAIL$  
  10.         }  
  11.  
  12. #下面是notify-host-by-email命令的定义  
  13.  define command{  
  14.         command_name    check-host-alive   #命令名称,用来检测主机状态。  
  15.        command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 5     #这里的变量$USER1$在resource.cfg文件中进行定义,即$USER1$=/usr/local/nagios/libexec  
  16. ,那么check_ping的完整路径为/usr/local/nagios/libexec/check_ping。 “-w 3000.0,80%”中“-w”说明后面的一对值对应的是“WARNING”状态,“80%”是其临界值。“-c 5000.0,100%”中“-c”说明后面的一对值对应的是“CRITICAL”,“100%”是其临界值。“-p 1”说明每次探测发送一个包。  
  17.         }  
  18. #下面是notify-host-by-email命令的定义  
  19.  define command{  
  20.         command_name    check_ftp  
  21.         command_line    $USER1$/check_ftp -H $HOSTADDRESS$ $ARG1$    #$ARG1$是指在调用这个命令的时候,命令后面的第一个参数。  
  22.         }  
  23. #下面是check_http命令的定义  
  24.  define command{  
  25.         command_name    check_http  
  26.         command_line    $USER1$/check_http -I $HOSTADDRESS$ $ARG1$  
  27.         }  
  28. #下面是check_ssh命令的定义  
  29.  define command{  
  30.         command_name    check_ssh  
  31.         command_line    $USER1$/check_ssh $ARG1$ $HOSTADDRESS$  
  32.         }  
  33.  


(5) hosts.cfg文件
 此文件默认不存在,需要手动创建,hosts.cfg主要用来指定被监控的主机地址以及相关属性信息,一个配置好的实例如下:

  1. define host{  
  2.         use                     linux-server  #引用主机linux-server的属性信息,linux-server主机在templates.cfg文件中进行了定义。  
  3.         host_name               web    #主机名  
  4.         alias                    ixdba-web  #主机别名  
  5.         address                 192.168.12.251  #被监控的主机地址,这个地址可以是ip,也可以是域名。  
  6.         }  
  7. ?  
  8.  define host{  
  9.         use                     linux-server  
  10.         host_name              mysql  
  11.         alias                    ixdba-mysql  
  12.         address                 192.168.12.237  
  13.         }  
  14. ?  
  15.  define hostgroup{      #定义一个主机组  
  16.  hostgroup_name  sa-servers        #主机组名称,可以随意指定。  
  17.  alias           sa servers    #主机组别名  
  18.  members         web,mysql   #主机组成员,其中“web”、“mysql”就是上面定义的两个主机。  
  19.  }  
  20.  


(6) services.cfg文件
 此文件默认也不存在,需要手动创建,services.cfg文件主要用于定义监控的服务和主机资源,例如监控http服务、ftp服务、主机磁盘空间、主机系统负载等等。

  1. #################  ixdba   web  #####################  
  2.  define service{  
  3.         use                             local-service  #引用local-service服务的属性值,local-service在templates.cfg文件中进行了定义。  
  4.         host_name                       web  #指定要监控哪个主机上的服务,“web”在hosts.cfg文件中进行了定义。  
  5.         service_description             PING  #对监控服务内容的描述,以供维护人员参考。  
  6.         check_command                   check_ping!100.0,20%!500.0,60% #指定检查的命令,check_ping命令在commands.cfg中定义,后跟两个参数,命令与参数间用!分割。         
  7.         }  
  8.  
  9.  define service{  
  10.         use                             local-service  
  11.         host_name                       web  
  12.         service_description              SSH  
  13.         check_command                  check_ssh    # check_ssh命令也在commands.cfg中定义。  
  14.         }  
  15.  
  16.  define service{  
  17.         use                             local-service  
  18.         host_name                       web  
  19.         service_description              SSHD  
  20.         check_command                  check_tcp!22  
  21.         }  
  22.  


(7) contacts.cfg文件
 contacts.cfg是一个定义联系人和联系人组的配置文件,当监控的主机或者服务出现故障,nagios会通过指定的通知方式(邮件或者短信)将信息发给这里指定的联系人或者使用者。

  1. define contact{  
  2.         contact_name                    sasystem  #联系人名称  
  3.         use                             generic-contact #引用generic-contact的属性信息,其中“generic-contact”在templates.cfg文件中进行定义  
  4.         alias                           sa-system  #联系人别名  
  5.         email                          [email protected]  #联系人的邮件地址  
  6.         }  
  7. define contactgroup {  
  8.       contactgroup_name    admins #联系人组名称  
  9.       alias                system administrator group #联系人组描述  
  10.       members              sasystem  #联系人组成员,其中“sasystem”就是上面定义的联系人  
  11. }  
  12.  


(8) timeperiods.cfg文件
 此文件只要用于定义监控的时间段,下面是一个配置好的实例:

  1. #下面是定义一个名为24×7的时间段,即监控所有时间段  
  2. define timeperiod{  
  3.         timeperiod_name 24×7     
  4.         alias           24 Hours A Day, 7 Days A Week  
  5.         sunday          00:00-24:00  
  6.         monday          00:00-24:00  
  7.         tuesday         00:00-24:00  
  8.         wednesday       00:00-24:00  
  9.         thursday        00:00-24:00  
  10.         friday          00:00-24:00  
  11.         saturday        00:00-24:00  
  12.         }  
  13. #下面是定义一个名为workhours的时间段,即工作时间段。  
  14. define timeperiod{  
  15.         timeperiod_name workhours   
  16.         alias           Normal Work Hours  
  17.         monday          09:00-17:00  
  18.         tuesday         09:00-17:00  
  19.         wednesday       09:00-17:00  
  20.         thursday        09:00-17:00  
  21.         friday          09:00-17:00  
  22.         }  
  23.  


(9) cgi.cfg文件
 此文件用来控制相关cgi脚本,如果想在nagios的web监控界面执行cgi脚本,例如重启nagios进程、关闭nagios通知、停止nagios主机检测等,这时就需要配置cgi.cfg文件了。
 由于nagios的web监控界面验证用户为ixdba,所以只需在cgi.cfg文件中添加此用户的执行权限就可以了,需要修改的配置信息如下:

  1. default_user_name=ixdba 
  2. authorized_for_system_information=nagiosadmin,ixdba  
  3. authorized_for_configuration_information=nagiosadmin,ixdba  
  4. authorized_for_system_commands=ixdba 
  5. authorized_for_all_services=nagiosadmin,ixdba  
  6. authorized_for_all_hosts=nagiosadmin,ixdba  
  7. authorized_for_all_service_commands=nagiosadmin,ixdba  
  8. authorized_for_all_host_commands=nagiosadmin,ixdba  
  9.  

(10) nagios.cfg文件
  Nagios.cfg默认的路径为/usr/local/nagios/etc/nagios.cfg,是nagios的核心配置文件,所有的对象配置文件都必须在这个文件中进行定义才能发挥其作用,这里只需将对象配置文件在Nagios.cfg文件中进行引用即可。

  1. log_file=/usr/local/nagios/var/nagios.log  
  2. “log_file”变量用来定义nagios日志文件的路径。  
  3. cfg_file=/usr/local/nagios/etc/hosts.cfg  
  4. cfg_file=/usr/local/nagios/etc/services.cfg  
  5. cfg_file=/usr/local/nagios/etc/commands.cfg  
  6. cfg_file=/usr/local/nagios/etc/contacts.cfg  
  7. cfg_file=/usr/local/nagios/etc/timeperiods.cfg  
  8. cfg_file=/usr/local/nagios/etc/templates.cfg  
  9.  “cfg_file”变量用来引用对象配置文件,如果有更多的对象配置文件,在这里依次添加即可。  
  10. object_cache_file=/usr/local/nagios/var/objects.cache  
  11. 该变量用于指定一个“所有对象配置文件”的副本文件,或者叫对象缓冲文件  
  12.  
  13. resource_file=/usr/local/nagios/etc/resource.cfg  
  14. 该变量用于指定nagios资源文件的路径,可以在Nagios.cfg中定义多个资源文件。  
  15. status_file=/usr/local/nagios/var/status.dat  
  16. 该变量用于定义一个状态文件,此文件用于保存nagios的当前状态、注释和宕机信息等。  
  17. status_update_interval=10 
  18.  该变量用于定义状态文件(即status.dat)的更新时间间隔,单位是秒,最小更新间隔是1秒。  
  19. nagios_user=nagios  
  20. 该变量指定了Nagios进程使用哪个用户运行。  
  21. nagios_group=nagios  
  22.  该变量用于指定Nagios使用哪个用户组运行。  
  23. check_external_commands=1 
  24.  该变量用于设置是否允许nagios在web监控界面运行cgi命令,也就是是否允许nagios在web界面下执行重启nagios、停止主机/服务检查等操作。“1”为运行,“0”为不允许。  
  25. command_check_interval=2 
  26.  该变量用于设置nagios对外部命令检测的时间间隔,如果指定了一个数字加一个”s”(如10s),那么外部检测命令的间隔是这个数值以秒为单位的时间间隔。如果没有用”s”,那么外部检测命令的间隔是以这个数值的“时间单位”的时间间隔。  
  27. interval_length=60 
  28.  该变量指定了nagios的时间单位,默认值是60秒,也就是1分钟,即在nagios配置中所有的时间单位都是分钟。  

==================================================================================================================================================================================

主配置文件 nagios.cfg 需要更改的地方:
 #cfg_file=/usr/local/nagios/etc/objects/localhost.cfg
 interval_length=1 ; 间隔时间基准由 60s 改为 1s
 command_check_interval=10s ; 命令检查时间间隔,-1 表示尽可能频繁的进行检查
 date_format=iso8601 ; 日期格式
 objects/contacts.cfg 用来定义联系人:

define contact {
 contact_name sa
 alias System Administrator
 service_notification_period 24×7
 host_notification_period 24×7
 service_notification_options w,u,c,r
 host_notification_options d,u,r
 service_notification_commands notify-service-by-email
 host_notification_commands notify-host-by-email
 email [email protected]
 } 
定义联系人组
define contactgroup {
 contactgroup_name admins
 alias Administrator Group
 members sa    ; 添加其它联系人用 “,” 分隔
 } 
主机监控的配置
define host {
 host_name host_name    ; 简短的主机名称。
 alias alias    ; 别名,可以更详细的说明主机。
 address address    ; IP 地址,也可以写主机名。如果不定义这个值, nagio 将会用 host_name 去寻找主机。
 parents host_names    ; 上一节点的名称,也就是指从 nagios 服务器到被监控主机之间经过的节点,可以是路由器、交换机、主机等等。
 hostgroups hostgroup_names    ; 简短的主机组名称。
 check_command command_name    ; 检查命令的简短名称,如果此项留空, nagios 将不会去判断主机是否 alive 。
 max_check_attempts 整数    ; 当检查命令的返回值不是 “OK” 时,重试的次数。
 check_interval 数字    ; 循环检查的间隔时间。
 active_checks_enabled [0/1]    ; 是否启用 “active_checks”
 passive_checks_enabled [0/1]    ; 是否启用 “passive_checks” ,及“被动检查”
 check_period timeperiod_name    ; 检测时间段简短名称,这只是个名称,具体的时间段要写在其他的配置文件中。
 obsess_over_host [0/1]    ; 是否启用主机操作系统探测。
 check_freshness [0/1]    ; 是否启用 freshness 检查。freshness 检查是对于启用被动检查模式的主机而言的,其作用是定期检查主机报告的状态信息,如果该状态信息已经过期,freshness 将会强制做主机检查。
 freshness_threshold 数字     ; fressness 的临界值,单位为秒。 如果定义为 “0″ ,则为自动定义。
 event_handler command_name    ; 当主机发生状态改变时,采用的处理命令的简短的名字(可以在 commands.cfg 中对其定义)
 event_handler_enabled [0/1]    ; 是否启用 event_handler
 low_flap_threshold 数字    ; 抖动的下限值。抖动,即在一段时间内,主机(或服务)的状态值频繁的发生变化。
 high_flap_threshold 数字   ; 抖动的上限值。
 flap_detection_enabled [0/1]    ; 是否启用抖动检查。
 process_perf_data [0/1]    ; 是否启用 processing of performance data
 retain_status_information [0/1]    ; 程序重启时,是否保持主机状态相关的信息。
 retain_nonstatus_information [0/1]    ; 程序重启时,是否保持主机状态无关的信息。
 contact_groups contact_groups    ; 联系人组,在此组中的联系人都会收到主机的提醒信息。
 notification_interval 整数    ; 重复发送提醒信息的最短间隔时间。默认间隔时间是 “60″ 分钟。如果这个值设置为 “0″ ,将不会发送重复提醒。
 notification_period timeperiod_name   ; 发送提醒的时间段。非常重要的主机(服务)定义为 24×7 ,一般的主机(服务)就定义为上班时间。如果不在定义的时间段内,无论发生什么问题,都不会发送提醒。
 notification_options [d,u,r,f]    ; 发送提醒包括的情况: d = 状态为 DOWN , u = 状态为 UNREACHABLE , r = 状态恢复为 OK , f = flapping
 notifications_enabled [0/1]    ; 是否开启提醒功能。”1″ 为开启,”0″ 为禁用。一般,这个选项会在主配置文件 (nagios.cfg) 中定义,效果相同。
 stalking_options [o,d,u]    ; 持续状态检测参数,o = 持续的 UP 状态 , d = 持续的 DOWN 状态 , u = 持续的 UNREACHABLE 状态
 } 
服务监控的配置

define service {
 host_name host_name
 service_description service_description
 servicegroups servicegroup_names
 is_volatile [0/1]
 check_command command_name
 max_check_attempts
 normal_check_interval
 retry_check_interval
 active_checks_enabled [0/1]
 passive_checks_enabled [0/1]
 check_period timeperiod_name
 parallelize_check [0/1]
 obsess_over_service [0/1]
 check_freshness [0/1]
 freshness_threshold
 event_handler command_name
 event_handler_enabled [0/1]
 low_flap_threshold
 high_flap_threshold
 flap_detection_enabled [0/1]
 process_perf_data [0/1]
 retain_status_information [0/1]
 retain_nonstatus_information [0/1]
 notification_interval
 notification_period timeperiod_name n
 otification_options [w,u,c,r,f]
 notifications_enabled [0/1]
 contact_groups contact_groups
 stalking_options [o,w,u,c]
 } 
服务监控的配置和主机监控的配置较为相似,就不一一说明了。 
间隔时间的计算方法为:
 normal_check_interval x interval_length 秒
 retry_check_interval x interval_length 秒
 notification_interval x interval_length 秒
 
主机监控配置的例子
define host {
 host_name web1
 alias web1
 address 192.168.0.101
 contact_groups admins
 check_command check-host-alive
 max_check_attempts 5
 notification_interval 0
 notification_period 24×7
 notification_options d,u,r
 } 
对主机 web1 进行 24×7 的监控,默认会每 10 秒检查一次状态,累计五次失败就发送提醒,并且不再重复发送提醒。
 服务监控配置的例子 
define service {
 host_name web1
 service_description check_http
 check_period 24×7
 max_check_attempts 3
 normal_check_interval 30
 contact_groups admins
 retry_check_interval 15
 notification_interval 3600
 notification_period 24×7
 notification_options w,u,c,r
 check_command check_http
 }
 
配置解释: 24×7 监控 web1 主机上的 HTTP 服务,检查间隔为 30 秒, 检查失败后每 15 秒再进行一次检查,累计三次失败就认定是故障并发送提醒。
 联系人组是 admins 。提醒后恢复到 30 秒一次的 normal_check_interval 检查。如果服务仍然没有被恢复,每个小时发送一次提醒。
 如果要检测其他服务,例如,要检查 ssh 服务是否开启,更改如下两行:
 service_description check_ssh
 check_command check_ssh 
为方便管理,对配置文件的分布做了如下修改:
 nagios.cfg 中增加了:
 cfg_dir=/usr/local/nagios/etc/hosts
 cfg_dir=/usr/local/nagios/etc/services 
在 hosts 目录中,为不同类型的主机创建了配置文件,如: app.cfg cache.cfg mysql.cfg web.cfg
 并创建了 hostgroup.cfg 文件对主机进行分组,如:

define hostgroup {
 hostgroup_name app-hosts
 alias APP Hosts
 members app1,app2
 } 
在 services 目录中创建了各种服务的配置文件,如: disk.cfg http.cfg load.cfg mysql.cfg
 并创建了 servicegroup.cfg 文件对服务进行分组,如:

define servicegroup {
 servicegroup_name disk
 alias DISK
 members cache1,check_disk,cache2,check_disk

 } 

转自: https://blog.csdn.net/lin_fs/article/details/8555960

zookeeper(单机+伪集群+集群)配置简介

 zookeeper  zookeeper(单机+伪集群+集群)配置简介已关闭评论
10月 312017
 


来自:http://www.cnblogs.com/sprinng/p/5976553.html

 #下载zookeeper:

#单机模式

解压到合适目录. 进入zookeeper目录下的conf子目录, 复制zoo_sample.cfg–>zoo.cfg(如果没有data和logs就新建):
tickTime=2000 
dataDir=/Users/apple/zookeeper/data 
dataLogDir=/Users/apple/zookeeper/logs 
clientPort=2180 
参数说明:
tickTime: zookeeper中使用的基本时间单位, 毫秒值.
dataDir: 数据目录. 可以是任意目录.
dataLogDir: log目录, 同样可以是任意目录. 如果没有设置该参数, 将使用和dataDir相同的设置.
clientPort: 监听client连接的端口号

#伪集群模式
解压到合适目录(zookeeper0).进入zookeeper0目录下的conf子目录, 复制zoo_sample.cfg–>zoo.cfg(如果没有data和logs就新建):
tickTime=2000 
initLimit=5 
syncLimit=2 
dataDir=/Users/apple/zookeeper0/data 
dataLogDir=/Users/apple/zookeeper0/logs 
clientPort=4180 
server.0=127.0.0.1:8880:7770 
server.1=127.0.0.1:8881:7771 
server.2=127.0.0.1:8882:7772 
#新增了几个参数, 其含义如下:
1 initLimit: zookeeper集群中的包含多台server, 其中一台为leader, 集群中其余的server为follower. initLimit参数配置初始化连接时, follower和leader之间的最长心跳时间. 此时该参数设置为5, 说明时间限制为5倍tickTime, 即5*2000=10000ms=10s.
2 syncLimit: 该参数配置leader和follower之间发送消息, 请求和应答的最大时间长度. 此时该参数设置为2, 说明时间限制为2倍tickTime, 即4000ms.
3 server.X=A:B:C 
其中X是一个数字, 表示这是第几号server. 
A是该server所在的IP地址. 
B配置该server和集群中的leader交换消息所使用的端口. 
C配置选举leader时所使用的端口. 
由于配置的是伪集群模式, 所以各个server的B, C参数必须不同.

a、将zookeeper0的目录拷贝2份:
参照zookeeper0/conf/zoo.cfg, 配置zookeeper1/conf/zoo.cfg, 和zookeeper2/conf/zoo.cfg文件. 只需更改dataDir, dataLogDir, clientPort参数即可.
b、在之前设置的dataDir中新建myid文件, 写入一个数字, 该数字表示这是第几号server. 该数字必须和zoo.cfg文件中的server.X中的X一一对应.
/Users/apple/zookeeper0/data/myid文件中写入0, /Users/apple/zookeeper1/data/myid文件中写入1, /Users/apple/zookeeper2/data/myid文件中写入2.
c、分别进入/Users/apple/zookeeper0/bin, /Users/apple/zookeeper1/bin, /Users/apple/zookeeper2/bin三个目录, 启动server.

#集群模式

解压到合适目录(zookeeper0).进入zookeeper0目录下的conf子目录, 复制zoo_sample.cfg–>zoo.cfg(如果没有data和logs就新建):
集群模式的配置和伪集群基本一致.
由于集群模式下, 各server部署在不同的机器上, 因此各server的conf/zoo.cfg文件可以完全一样.
下面是一个示例:
tickTime=2000 
initLimit=5 
syncLimit=2 
dataDir=/home/zookeeper/data 
dataLogDir=/home/zookeeper/logs 
clientPort=4180 
server.43=10.1.39.43:2888:3888 
server.47=10.1.39.47:2888:3888 
server.48=10.1.39.48:2888:3888 
示 例中部署了3台zookeeper server, 分别部署在10.1.39.43, 10.1.39.47, 10.1.39.48上. 需要注意的是, 各server的dataDir目录下的myid文件中的数字必须不同,10.1.39.43 server的myid为43, 10.1.39.47 server的myid为47, 10.1.39.48 server的myid为48.

#zookeeper常用命令
ZooKeeper服务命令:
1. 启动ZK服务: ./zkServer.sh start
2. 查看ZK服务状态: ./zkServer.sh status
3. 停止ZK服务: ./zkServer.sh stop
4. 重启ZK服务: ./zkServer.sh restart
zk客户端命令:
ZooKeeper 命令行工具类似于Linux的shell环境,使用它可以对ZooKeeper进行访问,数据创建,数据修改等操作. 使用 zkCli.sh -server 127.0.0.1:2181 连接到 ZooKeeper 服务,连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。
命令行工具的一些简单操作如下:
1. 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
2. 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
3. 创建文件,并设置初始内容: create /zk “test” 创建一个新的 znode节点“ zk ”以及与它关联的字符串
4. 获取文件内容: get /zk 确认 znode 是否包含我们所创建的字符串
5. 修改文件内容: set /zk “zkbak” 对 zk 所关联的字符串进行设置
6. 删除文件: delete /zk 将刚才创建的 znode 删除
7. 退出客户端: quit
8. 帮助命令: help
ZooKeeper 常用四字命令:
ZooKeeper 支持某些特定的四字命令字母与其的交互。它们大多是查询命令,用来获取 ZooKeeper 服务的当前状态及相关信息。用户在客户端可以通过 telnet 或 nc 向 ZooKeeper 提交相应的命令
1. 可以通过命令:echo stat|nc 127.0.0.1 2181 来查看哪个节点被选择作为follower或者leader
2. 使用echo ruok|nc 127.0.0.1 2181 测试是否启动了该Server,若回复imok表示已经启动。
3. echo dump| nc 127.0.0.1 2181 ,列出未经处理的会话和临时节点。
4. echo kill | nc 127.0.0.1 2181 ,关掉server
5. echo conf | nc 127.0.0.1 2181 ,输出相关服务配置的详细信息。
6. echo cons | nc 127.0.0.1 2181 ,列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息。
7. echo envi |nc 127.0.0.1 2181 ,输出关于服务环境的详细信息(区别于 conf 命令)。
8. echo reqs | nc 127.0.0.1 2181 ,列出未经处理的请求。
9. echo wchs | nc 127.0.0.1 2181 ,列出服务器 watch 的详细信息。
10. echo wchc | nc 127.0.0.1 2181 ,通过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。
11. echo wchp | nc 127.0.0.1 2181 ,通过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。

Ubuntu下OpenVPN客户端配置教程

 安全  Ubuntu下OpenVPN客户端配置教程已关闭评论
1月 152017
 

一般来说,提供Web服务的Linux服务器是很少需要连接OpenVPN的,但是个人Linux计算机在很多时候就需要连接OpenVPN了。比如以Linux为开发环境,需要连接公司的OpenVPN等等。 

本文将以Ubuntu系统为例,介绍在终端下连接远程OpenVPN服务的方法。 

安装OpenVPN

首先需要安装OpenVPN客户端。一般来说直接使用apt-get即可。执行如下命令安装:

1 [root@www ~]# apt-get install openvpn

稍等片刻将自动安装好openvpn需要的软件包。安装完成后,应该出现

/etc/openvpn/

文件夹。 

配置OpenVPN

作为客户端,OpenVPN并没有特定的配置文件,而是由服务器提供方给出一个配置文件。对于认证,OpenVPN提供了两种认证方法:基于用户名/密码的认证与SSL证书认证。用户名/密码的认证方法无法(或较难)限制一个账号同时连接多个客户端,而采用证书,则可保证同一证书同一时间只能有一个客户端连接。当然,这些都是由服务器端决定的,不需要客户端进行选择。

首先将OpenVPN服务器提供商发给你的配置文件解压,并将所有文件都复制到 /etc/openvpn/中。

这些文件中至少包含一个.ovpn文件,需要手动创建该文件,如:client.ovpn;如果服务器需要证书认证,则应该还存在另外三个证书文件。

看懂OpenVPN配置格式。下面是一个.ovpn配置示例:

client
dev tap
proto tcp-client
remote 192.168.135.75 1194
resolv-retry infinite
nobind
mute-replay-warnings
redirect-gateway
ca /etc/openvpn/ca.crt
cert /etc/openvpn/client.crt
key /etc/openvpn/client.key
comp-lzo
verb 4

一般来说,红色的内容可能需要你进行修改。将红色的内容修改成这三个文件的实际位置。然后保存即可。 

连接OpenVPN

在配置好.ovpn文件后,执行

openvpn /etc/openvpn/client.ovpn

即可连接服务器了(注意该目录下对应文件的权限)。注意,上面的参数应该换成你的配置文件实际位置。

此时,终端会回显很多连接日志。如果连接不成功,则可以通过这些日志来确定出错位置。如果要断开,只需要通过Ctrl+C强制终止即可。

上面的命令在实际中并不方便,因为它要占用一个独立的终端。在测试成功后,使用以下命令即可在后台连接OpenVPN:

openvpn /etc/openvpn/client.ovpn > /dev/null &

值得称赞的是,openvpn非常智能,在连接异常中断、无法连接服务器、断网的情况下,它会自动重连。因此,如果希望开机即自动连接OpenVPN,或者是VPN常年在线,则可将上述命令行加入

/etc/rc.local

中。注意,命令末尾的&符号不能省略,否则将可能阻塞系统的正常启动。

转自:http://www.linuxidc.com/Linux/2013-06/86562.htm

tomcat 安装配置apr

 tomcat  tomcat 安装配置apr已关闭评论
12月 302016
 

tomcat的运行模式有3种.修改他们的运行模式.3种模式的运行是否成功,可以看他的启动控制台,或者启动日志.或者登录他们的默认页面http://localhost:8080/查看其中的服务器状态。 

1)bio 

默认的模式,性能非常低下,没有经过任何优化处理和支持. 

2)nio 

利用java的异步io护理技术,no blocking IO技术. 

想运行在该模式下,直接修改server.xml里的Connector节点,修改protocol为 

 <Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol" 
	connectionTimeout="20000" 
	URIEncoding="UTF-8" 
	useBodyEncodingForURI="true" 
	enableLookups="false" 
	redirectPort="8443" /> 

启动后,就可以生效。 

3)apr 

安装起来最困难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能. 

必须要安装apr和native,直接启动就支持apr。下面的修改纯属多余,仅供大家扩充知识,但仍然需要安装apr和native 

如nio修改模式,修改protocol为org.apache.coyote.http11.Http11AprProtoco

tomcat如何支持apr?

需要安装apr, apr-util,   openssl, tomcat-native(tomcat-native包在tomcat目录的bin目录下就有源码的压缩包,解压这个包,注意下版本,也可以在http://tomcat.apache.org/download-native.cgi下载,并在http://tomcat.apache.org/native-doc/miscellaneous/changelog.html中查看下支持的openssl和apr版本,然后到对应的网站下载适合的版本)

1. 从网站 http://apache.spd.co.il/apr/ 中下载apr-xxx.tar.gz

tar zxvf apr-1.xx.xx.tar.gz

cd apr-xxx

./configure

make && make install

默认安装在/usr/local/apr目录下,可以通过命令行参数 –prefix 指定,建议不要改。

2. 从网站 http://apache.spd.co.il/apr/ 中下载apr-util-xxx.tar.gz

tar zxvf apr-util-xxx.tar.gz

cd apr-util-xxx

./configure –with-apr=/usr/local/apr     (此处路径就是上面俺转apr的路径)

make && make install


3.  openssl安装,其实linux版本一般都自带openssl,可以通过命令: openssl version查看版本,如果太低,可以

https://www.openssl.org/source/下载openssl-xx.tar.gz包编译安装

tar zxvf openssl-xx.tar.gz

cd openssl-xx

./config

make && make install (默认安装在/usr/local/ssl/)


4.  安装tomcat-native ,建议使用tomcat自己bin目录下的tomcat-native.tar.gz,

 解压, 

tomcat-native-1.1.27-src的版本进入jni/native目录,查看 BUILDING文件,可以有详细的说明

tomcat-native-1.2.10-src的版本直接进入native目录,查看BUILDING文件,有详细的说明

在native目录下,输入

configure –with-apr=/usr/local/apr –with-ssl=/usr/local/ssl  

其中/usr/local/apr 和 /usr/local/ssl分别是上面安装的apr和openssl目录。

安装完成有提示文字告诉你安装到的位置, 上面路径默认安装在/usr/local/apr/lib


配置tomcat,其实在BUILDING文件有说明,具体是:

Using it in Tomcat
——————

1. In <Connector> use of conf/server.xml:
   protocol=”org.apache.coyote.http11.Http11AprProtocol”
2. In bin/setenv.sh add the following:
   CATALINA_OPTS=”$CATALINA_OPTS -Djava.library.path=/usr/local/apr/lib”
   

 tomcat下没有bin/setenv.sh 文件就新建一个,并有可执行权。



启动tomcat , 看到如下信息: 

信息: Starting ProtocolHandler [“http-apr-8080”]

成功!

关于openssl配置(单向):

单向SSL的概念:

客户端向服务器发送消息,服务器接到消息后,用服务器端的密钥库中的私钥对数据进行加密,然后把加密后的数据和服务器端的公钥一起发送到客户端,客户端用服务器发送来的公钥对数据解密,然后在用传到客户端的服务器公钥对数据加密传给服务器端,服务器用私钥对数据进行解密,这就完成了客户端和服务器之间通信的安全问题,但是单向认证没有验证客户端的合法性。

 

不使用apr情况:

(1)产生密钥库
keytool -genkeypair -alias tomcat -keyalg RSA -keysize 1024 -validity 365 -keystore /usr/local/apache-tomcat-6.0.18/keystore

(2)在Connector上配置密钥库
<Connector port=”8443″ protocol=”HTTP/1.1″ SSLEnabled=”true”
               maxThreads=”150″ scheme=”https” secure=”true”
               clientAuth=”false” sslProtocol=”TLS” keystoreFile=”/usr/local/tomcat/conf/keystore” keystorePass=”123456″/>

 

使用apr情况:

(1)产生密钥库

openssl genrsa -out rsa-private-key.pem 1024
openssl req -new -x509 -nodes -sha1 -days 365 -key rsa-private-key.pem -out self-signed-cert.pem

(2)在Connector上配置密钥库

<Connector port=”8443″ protocol=”HTTP/1.1″ SSLEnabled=”true”
               maxThreads=”150″ scheme=”https” secure=”true”
               clientAuth=”false” sslProtocol=”TLS” 
               SSLCertificateKeyFile=”/usr/local/tomat/rsa-private-key.pem”
               SSLCertificateFile=”/usr/local/tomat/self-signed-cert.pem”/>

 

出现:

Failed to initialize end point associated with ProtocolHandler ["http-apr-8443"]
java.lang.Exception: Connector attribute SSLCertificateFile must be defined when using SSL with APR

可以将: protocol=“HTTP/1.1″修改为  protocol=”org.apache.coyote.http11.Http11Protocol”, 之修改https的端口,不要修改原来支持apr的http的protocol

Jackson配置属性(objectMapper.configure)

 json  Jackson配置属性(objectMapper.configure)已关闭评论
11月 032016
 

Jackson配置属性
如果上面的工具类实例,在Jackson中存在一些属性配置,这些配置决定了最后在解析或者编码后数据视图。因此,在分析Jackson之前,先了解下,Jackson具有的一些配置含义。
JsonParser解析相关配置属性
JsonParser将JSON 数据格式的String字符串,解析成为Java对象。Jackson在解析的时候,对于一些非JSON官方文档支持的属性,则需要通过一些配置才可以被Jackson工具解析成对象。
/**
     * Enumeration that defines all togglable features for parsers.
     */
    public enum Feature {

        // // // Low-level I/O handling features:

        /**
         * 这个特性,决定了解析器是否将自动关闭那些不属于parser自己的输入源。 如果禁止,则调用应用不得不分别去关闭那些被用来创建parser的基础输入流InputStream和reader;
         * 如果允许,parser只要自己需要获取closed方法(当遇到输入流结束,或者parser自己调用 JsonParder#close方法),就会处理流关闭。
         *
         * 注意:这个属性默认是true,即允许自动关闭流
         *
         */
        AUTO_CLOSE_SOURCE(true),

        // // // Support for non-standard data format constructs

        /**
         * 该特性决定parser将是否允许解析使用Java/C++ 样式的注释(包括’/’+’*’ 和’//’ 变量)。 由于JSON标准说明书上面没有提到注释是否是合法的组成,所以这是一个非标准的特性;
         * 尽管如此,这个特性还是被广泛地使用。
         *
         * 注意:该属性默认是false,因此必须显式允许,即通过JsonParser.Feature.ALLOW_COMMENTS 配置为true。
         *
         */
        ALLOW_COMMENTS(false),

        /**
         * 这个特性决定parser是否将允许使用非双引号属性名字, (这种形式在Javascript中被允许,但是JSON标准说明书中没有)。
         *
         * 注意:由于JSON标准上需要为属性名称使用双引号,所以这也是一个非标准特性,默认是false的。
         * 同样,需要设置JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES为true,打开该特性。
         *
         */
        ALLOW_UNQUOTED_FIELD_NAMES(false),

        /**
         * 该特性决定parser是否允许单引号来包住属性名称和字符串值。
         *
         * 注意:默认下,该属性也是关闭的。需要设置JsonParser.Feature.ALLOW_SINGLE_QUOTES为true
         *
         */
        ALLOW_SINGLE_QUOTES(false),

        /**
         * 该特性决定parser是否允许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符和换行符)。 如果该属性关闭,则如果遇到这些字符,则会抛出异常。
         * JSON标准说明书要求所有控制符必须使用引号,因此这是一个非标准的特性。
         *
         * 注意:默认时候,该属性关闭的。需要设置:JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS为true。
         *
         */
        ALLOW_UNQUOTED_CONTROL_CHARS(false),

        /**
         * 该特性可以允许接受所有引号引起来的字符,使用‘反斜杠\’机制:如果不允许,只有JSON标准说明书中 列出来的字符可以被避开约束。
         *
         * 由于JSON标准说明中要求为所有控制字符使用引号,这是一个非标准的特性,所以默认是关闭的。
         *
         * 注意:一般在设置ALLOW_SINGLE_QUOTES属性时,也设置了ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER属性,
         * 所以,有时候,你会看到不设置ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER为true,但是依然可以正常运行。
         *
         * @since 1.6
         */
        ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false),

        /**
         * 该特性决定parser是否允许JSON整数以多个0开始(比如,如果000001赋值给json某变量,
         * 如果不设置该属性,则解析成int会抛异常报错:org.codehaus.jackson.JsonParseException: Invalid numeric value: Leading zeroes not
         * allowed)
         *
         * 注意:该属性默认是关闭的,如果需要打开,则设置JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS为true。
         *
         * @since 1.8
         */
        ALLOW_NUMERIC_LEADING_ZEROS(false),

        /**
         * 该特性允许parser可以识别”Not-a-Number” (NaN)标识集合作为一个合法的浮点数。 例如: allows (tokens are quoted contents, not including
         * quotes):
         * <ul>
         * <li>”INF” (for positive infinity), as well as alias of “Infinity”
         * <li>”-INF” (for negative infinity), alias “-Infinity”
         * <li>”NaN” (for other not-a-numbers, like result of division by zero)
         * </ul>
         */

        ALLOW_NON_NUMERIC_NUMBERS(false),

        // // // Controlling canonicalization (interning etc)

        /**
         * 该特性决定JSON对象属性名称是否可以被String#intern 规范化表示。
         *
         * 如果允许,则JSON所有的属性名将会 intern() ;如果不设置,则不会规范化,
         *
         * 默认下,该属性是开放的。此外,必须设置CANONICALIZE_FIELD_NAMES为true
         *
         * 关于intern方法作用:当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串 (该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String
         * 对象添加到池中, 并且返回此 String 对象的引用。
         *
         * @since 1.3
         */
        INTERN_FIELD_NAMES(true),

        /**
         * 该特性决定JSON对象的属性名称是否被规范化。
         *
         * @since 1.5
         */
        CANONICALIZE_FIELD_NAMES(true),

        ;

        final boolean _defaultState;

        /**
         * Method that calculates bit set (flags) of all features that are enabled by default.
         */
        public static int collectDefaults() {
            int flags = 0;
            for (Feature f : values()) {
                if (f.enabledByDefault()) {
                    flags |= f.getMask();
                }
            }
            return flags;
        }

        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        public boolean enabledByDefault() {
            return _defaultState;
        }

        public boolean enabledIn(int flags) {
            return (flags & getMask()) != 0;
        }

        public int getMask() {
            return (1 << ordinal());
        }
    };
Note: 在枚举最后有一个公共静态方法collectDefaults(),这个方法返回一个整形,整形包含的是所有枚举项对应位bit为初始默认值(true:1;false:0),如果默认属性为true,则通过对1 << ordinal()的值和flags进行亦或来置位。
DeserializationConfig反序列化相关配置属性
将Java 对象序列化为Json字符串。Jackson在序列化Java对象的时候,对于有些不存在的属性处理,以及一些类型转换等,都可以通过配置来设置。
/**
     * Enumeration that defines togglable features that guide the serialization feature.
     */
    public enum Feature implements MapperConfig.ConfigFeature {
        /*
         * /****************************************************** Introspection features
         * /******************************************************
         */

        /**
         * 该特性决定是否启动内部注解功能支持配置;如果允许,则使用AnnotationIntrospector扫描配置,否则,不考了注解配置。
         *
         * 默认启动该功能配置属性。
         * 
         * @since 1.2
         */
        USE_ANNOTATIONS(true),

        /**
         * 该特性决定是否使用“getter”方法来根据标准bean命名转换方式来自动检测。如果true,则所有公共的带有一个参数
         * 并且前缀为set的方法都将被当做setter方法。如果false,只会把显式注解的作为setter方法。
         *
         * 注意: 这个特性的优先级低于显式注解,并且只会在获取不到更细粒度配置的情况下。
         *
         */
        AUTO_DETECT_GETTERS(true),                        DETECT_IS_GETTERS(true),

        /**
         * 该特性决定是否使用creator方法来根据公共构造函数以及名字为“valueOf”的静态单参数方法自动检测。
         *
         * 注意:这个特性比每个类上注解的优先级要低。
         *
         */
        AUTO_DETECT_CREATORS(true),

        /**
         * 这个特性决定是否非静态field被当做属性。如果true,则所有公共成员field都被当做属性, 否则只有注解,才会被当做属性field。
         *
         */
        AUTO_DETECT_FIELDS(true),

        /**
         * 使用getter方法来作为setter方法(一般只处理集合和Maps,和其他没有setter的类型)。 该属性决定是否不需要setter方法,而只需要getter方法来修改属性。
         * 
         * 注意:该配置优先级低于setter。
         */
        USE_GETTERS_AS_SETTERS(true),

        /**
         * 该特性决定当访问属性时候,方法和field访问是否修改设置。 如果设置为true,则通过反射调用方法AccessibleObject#setAccessible 来允许访问不能访问的对象。
         * 
         */
        CAN_OVERRIDE_ACCESS_MODIFIERS(true),                GETTERS(false),

        /*
         * /****************************************************** /* Type conversion features
         * /******************************************************
         */

        /**
         * 该特性决定对于json浮点数,是否使用BigDecimal来序列化。如果不允许,则使用Double序列化。
         * 
         * 注意:该特性默认是关闭的,因为性能上来说,BigDecimal低于Double。
         *
         */
        USE_BIG_DECIMAL_FOR_FLOATS(false),

        /**
         * 该特性决定对于json整形(非浮点),是否使用BigInteger来序列化。如果不允许,则根据数值大小来确定 是使用Integer}, {@link Long} 或者
         * {@link java.math.BigInteger}
         *
         */
        USE_BIG_INTEGER_FOR_INTS(false),

        // [JACKSON-652]
        /**
         * 该特性决定JSON ARRAY是映射为Object[]还是List<Object>。如果开启,都为Object[],false时,则使用List。
         *
         * @since 1.9
         */
        USE_JAVA_ARRAY_FOR_JSON_ARRAY(false),

        /**
         * 该特性决定了使用枚举值的标准序列化机制:如果允许,则枚举假定使用Enum.toString()返回的值作为序列化结构;如果禁止, 则返回Enum.name()的值。
         *
         * 注意:默认使用的时Enum.name()的值作为枚举序列化结果。这个的设置和WRITE_ENUMS_USING_TO_STRING需要一致。
         *
         * For further details, check out [JACKSON-212]
         * 
         * @since 1.6
         */
        READ_ENUMS_USING_TO_STRING(false),

        /*
         * /****************************************************** Error handling features
         * /****************************************************** 错误处理特性
         */

        /**
         * 该特性决定了当遇到未知属性(没有映射到属性,没有任何setter或者任何可以处理它的handler),是否应该抛出一个
         * JsonMappingException异常。这个特性一般式所有其他处理方法对未知属性处理都无效后才被尝试,属性保留未处理状态。
         *
         * 默认情况下,该设置是被打开的。
         *
         * @since 1.2
         */
        FAIL_ON_UNKNOWN_PROPERTIES(true),

        /**
         * 该特性决定当遇到JSON null的对象是java 原始类型,则是否抛出异常。当false时,则使用0 for ‘int’, 0.0 for double 来设定原始对象初始值。
         *
         * 默认情况下,允许原始类型可以使用null。
         *
         * @since 1.7
         */
        FAIL_ON_NULL_FOR_PRIMITIVES(false),

        /**
         * 该特性决定JSON 整数是否是一个有效的值,当被用来反序列化Java枚举值。如果false,数字可以接受,并且映射为枚举的值ordinal();
         * 如果true,则数字不允许并且抛出JsonMappingException异常。后面一种行为原因是因为大部分情况下,枚举被反序列化为 JSON 字符串, 从而造成从整形到枚举的意外映射关系。
         *
         * Feature is disabled by default (to be consistent with behavior of Jackson 1.6), i.e. to allow use of JSON
         * integers for Java enums.
         * 
         * @since 1.7
         */
        FAIL_ON_NUMBERS_FOR_ENUMS(false),

        /**
         * 异常封装,不封装Error,catch异常之后,抛出IOException。默认封装异常。
         *
         * @since 1.7
         */
        WRAP_EXCEPTIONS(true),

        /*
         * /****************************************************** Structural conversion features
         * /****************************************************** 数据结构转换特性
         */

        /**
         * 该特性决定是否接受强制非数组(JSON)值到Java集合类型。如果允许,集合反序列化将尝试处理非数组值。
         *
         * Feature that determines whether it is acceptable to coerce non-array (in JSON) values to work with Java
         * collection (arrays, java.util.Collection) types. If enabled, collection deserializers will try to handle
         * non-array values as if they had “implicit” surrounding JSON array. This feature is meant to be used for
         * compatibility/interoperability reasons, to work with packages (such as XML-to-JSON converters) that leave out
         * JSON array in cases where there is just a single element in array.
         * 
         * @since 1.8
         */
        ACCEPT_SINGLE_VALUE_AS_ARRAY(false),

        /**
         * 该特征允许 unwrap根级别JSON 值,来匹配WRAP_ROOT_VALUE 序列化设置。
         *
         * @since 1.9
         */
        UNWRAP_ROOT_VALUE(false),

        /*
         * /****************************************************** Value conversion features
         * /****************************************************** 值转换特性
         */

        /**
         * 该特性可以允许JSON空字符串转换为POJO对象为null。如果禁用,则标准POJO只会从JSON null或者JSON对象转换过来;
         * 如果允许,则空JSON字符串可以等价于JSON null。
         * @since 1.8
         */
        ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false)

        ;

        final boolean _defaultState;

        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        @Override
        public boolean enabledByDefault() {
            return _defaultState;
        }

        @Override
        public int getMask() {
            return (1 << ordinal());
        }
    }
SerializationConfig 序列化相关配置属性
/**
     * 定义序列化对象所需配置的一些枚举.
     */
    public enum Feature implements MapperConfig.ConfigFeature
    {
        /*
        /******************************************************
        /*  Introspection features
        /******************************************************
         */
        
        /**
         * 注解扫描配置
         */
        USE_ANNOTATIONS(true),

        /**
         * 获取getter方法,前缀为get
         */
        AUTO_DETECT_GETTERS(true),

        /**
         * 获取getter方法,前缀为is
         */
        AUTO_DETECT_IS_GETTERS(true),

        /**
         * 将对象所有的field作为json属性
         */
         AUTO_DETECT_FIELDS(true),

        /**
         * 该特性决定当访问属性时候,方法和field访问是否修改设置。 如果设置为true,
         * 则通过反射调用方法AccessibleObject#setAccessible 来允许访问不能访问的对象。
         */
        CAN_OVERRIDE_ACCESS_MODIFIERS(true),

        /**
         * 获取的getter方法需要setter方法,否则,所有发现的getter都可以作为getter方法。
         */
        REQUIRE_SETTERS_FOR_GETTERS(false),
        
        /*
        /******************************************************
        /* Generic output features
        /******************************************************
         */

        /**
         * 属性对应的值为null,是否需要写出来,write out。
         */
        @Deprecated
        WRITE_NULL_PROPERTIES(true),

        /**
         * 特征决定是使用运行时动态类型,还是声明的静态类型。
         * 也可以使用{@link JsonSerialize#typing} 注解属性
         */
        USE_STATIC_TYPING(false),

        /**
         * 该特性决定拥有view注解{@link org.codehaus.jackson.map.annotate.JsonView}的属性是否在JSON序列化视图中。如果true,则非注解视图,也包含;
         * 否则,它们将会被排除在外。
         *
         */
        DEFAULT_VIEW_INCLUSION(true),
        
        /**
         * 在JAVA中配置XML root{@XmlRootElement.name}注解,最后xml数据中会出现对应root根name.
         */
        WRAP_ROOT_VALUE(false),

        /**
         * 该特性对于最基础的生成器,使用默认pretty printer {@link org.codehaus.jackson.JsonGenerator#useDefaultPrettyPrinter}
         * 这只会对{@link org.codehaus.jackson.JsonGenerator}有影响.该属性值允许使用默认的实现。
         */
        INDENT_OUTPUT(false),

        /**
         * 是否对属性使用排序,默认排序按照字母顺序。
         */
        SORT_PROPERTIES_ALPHABETICALLY(false),
        
        /*
        /******************************************************
        /*  Error handling features
        /******************************************************
         */
        
        /**
         * 是否允许一个类型没有注解表明打算被序列化。默认true,抛出一个异常;否则序列化一个空对象,比如没有任何属性。
         *
         * Note that empty types that this feature has only effect on
         * those “empty” beans that do not have any recognized annotations
         * (like <code>@JsonSerialize</code>): ones that do have annotations
         * do not result in an exception being thrown.
         *
         * @since 1.4
         */
        FAIL_ON_EMPTY_BEANS(true),

        /**
         * 封装所有异常
         */
        WRAP_EXCEPTIONS(true),

        /*
        /******************************************************
        /* Output life cycle features
        /******************************************************
         */
        
         /**
          * 该特性决定序列化root级对象的实现closeable接口的close方法是否在序列化后被调用。
          * 
          * 注意:如果true,则完成序列化后就关闭;如果,你可以在处理最后,调用排序操作等,则为false。
          * 
          */
        CLOSE_CLOSEABLE(false),

        /**
         * 该特性决定是否在writeValue()方法之后就调用JsonGenerator.flush()方法。
         * 当我们需要先压缩,然后再flush,则可能需要false。
         * 
         */
        FLUSH_AFTER_WRITE_VALUE(true),
         
        /*
        /******************************************************
        /* Data type – specific serialization configuration
        /******************************************************
         */

        /**
         * 该特性决定是否将基于Date的值序列化为timestamp数字式的值,或者作为文本表示。
         * 如果文本表示,则实际格式化的时候会调用{@link #getDateFormat}方法。
         * 
         * 该特性可能会影响其他date相关类型的处理,虽然我们理想情况是只对date起作用。
         * 
         */
        WRITE_DATES_AS_TIMESTAMPS(true),

        /**
         * 是否将Map中得key为Date的值,也序列化为timestamps形式(否则,会被序列化为文本形式的值)。
         */
        WRITE_DATE_KEYS_AS_TIMESTAMPS(false),

        /**
         * 该特性决定怎样处理类型char[]序列化,是否序列化为一个显式的JSON数组,还是默认作为一个字符串。
         *
         */
        WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false),

        /**
         * 该特性决定对Enum 枚举值使用标准的序列化机制。如果true,则返回Enum.toString()值,否则为Enum.name()
         *
         */
        WRITE_ENUMS_USING_TO_STRING(false),

        /**
         * 这个特性决定Java枚举值是否序列化为数字(true)或者文本值(false).如果是值的话,则使用Enum.ordinal().
         * 该特性优先级高于上面的那个。
         * 
         * @since 1.9
         */
        WRITE_ENUMS_USING_INDEX(false),
        
        /**
         * 决定是否Map的带有null值的entry被序列化(true)
         *
         */
        WRITE_NULL_MAP_VALUES(true),

        /**
         * 决定容器空的属性(声明为Collection或者array的值)是否被序列化为空的JSON数组(true),否则强制输出。
         *
         * Note that this does not change behavior of {@link java.util.Map}s, or
         * “Collection-like” types.
         * 
         * @since 1.9
         */
        WRITE_EMPTY_JSON_ARRAYS(true)
        
            ;

        final boolean _defaultState;
        
        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        @Override
        public boolean enabledByDefault() { return _defaultState; }
    
        @Override
        public int getMask() { return (1 << ordinal()); }
    }

配置sonar、jenkins进行持续审查

 java  配置sonar、jenkins进行持续审查已关闭评论
4月 032015
 

资料整理学习,转自:http://www.cnblogs.com/gao241/p/3190701.html

本文以CentOS操作系统为例介绍Sonar的安装配置,以及如何与Jenkins进行集成,通过pmd-cpd、checkstyle、findbugs等工具对代码进行持续审查。

一、安装配置sonar

1、Sonar介绍

Sonar是一个用于代码质量管理的开源平台,用于管理Java源代码的质量。通过插件机制,Sonar 可以集成不同的测试工具,代码分析工具,以及持续集成工具,比如pmd-cpd、checkstyle、findbugs、Jenkins。通过不同的插件对这些结果进行再加工处理,通过量化的方式度量代码质量的变化,从而可以方便地对不同规模和种类的工程进行代码质量管理。

同时 Sonar 还对大量的持续集成工具提供了接口支持,可以很方便地在持续集成中使用 Sonar。

此外,Sonar 的插件还可以对 Java 以外的其他编程语言提供支持,对国际化以及报告文档化也有良好的支持。

2、配置数据库

Apache Derby 是Sonar自带并且默认安装使用的数据库,此外Sonar对如下数据库提供支持:MySQL 5.x, Oracle 10g XE, Postgresql, MS SqlServer等,本文以mysql为例介绍如何配置数据库: 

1)创建数据库

在mysql中执行如下脚本创建数据库及mysql用户

CREATE DATABASE sonarCHARACTER SET utf8COLLATE utf8_general_ci;
 
CREATE USER ‘sonar’ IDENTIFIEDBY ‘sonar’;
GRANT ALL ON sonar.*TO ‘sonar’@’%’ IDENTIFIEDBY ‘sonar’;
GRANT ALL ON sonar.*TO ‘sonar’@’localhost’ IDENTIFIEDBY ‘sonar’;
FLUSHPRIVILEGES;

2)编辑${SONAR_HOME}/conf/sonar.properties配置数据库:

复制代码
sonar.jdbc.username:                       sonar
sonar.jdbc.password:                       sonar
sonar.jdbc.url:                            jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true

# Optional properties
sonar.jdbc.driverClassName:                com.mysql.jdbc.Driver
复制代码

3)配置DB驱动包

如果使用Oracle数据库,必须手动复制驱动类到${SONAR_HOME}/extensions/jdbc-driver/oracle/目录下。其它支持的数据库默认提供了驱动,http://docs.codehaus.org/display/SONAR/Analysis+Parameters 列举了一些常用的配置及默认值.

4)常见错误及解决方法

添加语言包后,启动报错分析:

下载安装 sonar-l10n-zh-plugin-1.4.jar 语言包(http://docs.codehaus.org/display/SONAR/Chinese+Pack),重新打包部署后,后台报错如下:

Error in Sonar.log : 2012.10.25 14:39:15 INFO org.sonar.INFO Register rules [squid/java]…2012.10.25 14:39:15 ERROR o.s.s.p.Platform The following rule (repository: squid) must have a description: Rule[id=<null>,name=<null>,key=ParsingError,configKey=ParsingError,plugin=squid,enabled=true,severity=MAJOR,cardinality=SINGLE]org.sonar.api.utils.SonarException: The following rule (repository: squid) must have a description: Rule[id=<null>,name=<null>,key=ParsingError,configKey=ParsingError,plugin=squid,enabled=true,severity=MAJOR,cardinality=SINGLE]at org.sonar.server.startup.RegisterRules.validateRule(RegisterRules.java:131) ~[classes/:na]at org.sonar.server.startup.RegisterRules.registerRepository(RegisterRules.java:103) ~[classes/:na]at…

(可参考http://jira.codehaus.org/browse/SONAR-3910)

解决方法:将 extensionsplugins 目录下的jar包全部删除,重新加入本地语言包后,重新打包部署即可。

3、安装、配置Sonar

Sonar的运行需要 JDK 1.5+ , 从 http://www.sonarqube.org/downloads/ 下载sonar zip文件,本文以3.6版本为例。

创建运行sonar的CentOS账户sonar,并设置账户密码:

# useradd sonar # passwd sonar

使用sonar账户登录CentOS。

Sonar默认集成了jetty容器,可以直接启动提供服务,也可以通过脚本构建为war包,部署在tomcat容器中。

1)直接启动

编辑.bash_profile,添加环境变量SONAR_HOME

$ vi $HOME/.bash_profile

修改成如下内容:

PATH=$PATH:$HOME/bin
SONAR_HOME=$HOME/sonar

export PATH SONAR_HOME

使环境变量生效

Source $HOME/.bash_profile

运行如下命令启动sonar,其它操作系统sonar均提供了启动脚本

$ ${SONAR_HOME}/bin/linux-x86-64/sonar.sh start

在浏览器中访问: http://localhost:9000/ ,运行界面如下:

 

Sonar默认的端口是”9000”、默认的上下文路径是”/”、默认的网络接口是”0.0.0.0”,默认的管理员帐号和密码为:admin/admin,这些参数都可以在配置文件中修改:

$ vi ${SONAR_HOME}/conf/sonar.properties

2)作为Web项目,部署到Tomcat等应用服务器中

a. 确保conf/sonar.properties、conf/wrapper.conf未被修改使用过

b. 执行如下命令生成war包,将生成的sonar.war部署到应用服务器中

$ ${SONAR_HOME}/war/build-war.sh

c. 启动Tomcat, 通过 http://localhost:8080/sonar 访问.

Tomcat安装配置参见:CenOS系统中安装Tomcat7并设置为自启动服务 

4、配置为自启动服务

使用root账户或者开启sudo权限操作。

创建自启动脚本文件/etc/init.d/sonar

# vi /etc/init.d/sonar

添加如下内容

复制代码
#!/bin/sh # # rc file for SonarQube # # chkconfig: 345 96 10 # description: SonarQube system (www.sonarsource.org) # ### BEGIN INIT INFO # Provides: sonar # Required-Start: $network # Required-Stop: $network # Default-Start: 3 4 5 # Default-Stop: 0 1 2 6 # Short-Description: SonarQube system (www.sonarsource.org) # Description: SonarQube system (www.sonarsource.org) ### END INIT INFO /usr/bin/sonar $*
复制代码

添加启动服务

# ln -s $SONAR_HOME/bin/linux-x86-64/sonar.sh /usr/bin/sonar # chmod 755 /etc/init.d/sonar # chkconfig --add sonar

5、配置插件

a)插件介绍

Sonar支持多种插件,插件的下载地址为:http://docs.codehaus.org/display/SONAR/Plugin+Library

将下载后的插件上传到${SONAR_HOME}extensionsplugins目录下,重新启动sonar。

sonar默认集成了Java Ecosystem插件,该插件是一组插件的合集

  • Java [sonar-java-plugin]:java源代码解析,计算指标等
  • Squid [sonar-squid-java-plugin]:检查违反Sonar定义规则的代码
  • Checkstyle [sonar-checkstyle-plugin]:使用CheckStyle检查违反统一代码编写风格的代码
  • FindBugs [sonar-findbugs-plugin]:使用FindBugs检查违反规则的缺陷代码
  • PMD [sonar-pmd-plugin]:使用pmd检查违反规则的代码
  • Surefire [sonar-surefire-plugin]:使用Surefire执行单元测试
  • Cobertura [sonar-cobertura-plugin]:使用Cobertura获取代码覆盖率
  • JaCoCo [sonar-jacoco-plugin]:使用JaCOCO获取代码覆盖率

下面列出了一些常用的插件:

b)插件配置示例(本段内容来自http://www.ibm.com/developerworks/cn/java/j-lo-sonar/

Sonar 的主要特色是对不同工具产生的检查结果进行再加工处理,Sonar 还向用户提供了对数据进行个性化处理的方法。

本节以 Technical Debt 插件为例说明如何通过设置参数影响最后的报告结果。首先了解一下这个插件中的“技术债务”的概念,这个概念最早是在 1992 年由 Ward Cunningham 在他的论文“The WyCash Portfolio Management System”中提出的,之后被软件工程界接受并推广,《重构》的作者 Martin Fowler 也在其 网站上对技术债务有所介绍。其实原理可以理解为“出来混早晚要还的”,当前不规范的代码,会对以后产品修改的成本造成影响。

Soanr 的 Technical Debt 插件提供了默认的计算公式,通过对其中的权重参数进行配置,可以适应不同公司和项目对技术债务的计算。

 

以上的各项数据指标,可以根据自己公司和项目的不同情况进行设置,如图所示:

例如默认参数下同一个项目的技术债务指标如下:

修改了参数后的结果为:

可见将 Average time to cover complexity of one (in hours) 从 0.2 修改为 0.01 后,Coverage 的权重变小了,从而达到忽略单元测试覆盖率的作用。不同的公司和项目可以根据需要调整各自的参数,参数的调优和策略不在本文的讨论范围之内。

通过以上的示例可以看出,Sonar 使用不同类型的图表显示给用户代码质量的结果,并且这些图表不是简单地对单元测试覆盖率或者静态检测工具的结果进行显示,而是根据软件工程理论进行了二次加工后的结果,更加科学和直观。

c)更新中心

以管理员用户登录Sonar,进入配置->系统,选择更新中心,如图:

其中Available Plugins选项卡提供了可以选择安装的插件,System Updates可以在线更新Sonar。

下载插件需要注意其中有些插件是需要购买才能使用的,其License类型为Commercial。

 

二、与jenkins集成

1、通过Maven进行集成

修改maven的主配置文件(${MAVEN_HOME}/conf/settings.xml文件或者 ~/.m2/settings.xml文件),在其中增加访问Sonar数据库及Sonar服务地址,添加如下配置:

复制代码
<profile> <id>sonar</id> <properties> <sonar.jdbc.url>jdbc:mysql://localhost:3306/sonar</sonar.jdbc.url> <sonar.jdbc.driver>com.mysql.jdbc.Driver</sonar.jdbc.driver> <sonar.jdbc.username>sonar</sonar.jdbc.username> <sonar.jdbc.password>sonar</sonar.jdbc.password> <sonar.host.url>http://localhost:9000</sonar.host.url> <!-- Sonar服务器访问地址 --> </properties> </profile> <activeProfiles> <activeProfile>sonar</activeProfile> </activeProfiles>
复制代码

此处注意sonar.host.url地址应根据sonar部署情况修改

同样,为了避免内存溢出,推荐增加内存堆栈的大小。设置MAVEN_OPTS环境变量:

set MAVEN_OPTS=”-Xmx512m -XX:MaxPermSize=256m”  

使用Sonar

a. 运行Sonar服务器;

b. 通过 mvn sonar:sonar 将代码注入到Sonar中进行分析处理,并将处理结果以XML的形式保存在数据库中;

c. 通过浏览器访问,显示分析结果;

d. 持续运行Maven构建,会迭代显示分析结果;

e. 可以显式指定sonar插件的版本,如下:

复制代码
        <project> <build> <plugins> <plugin> <groupId>org.codehaus.sonar</groupId> <artifactId>sonar-maven-plugin</artifactId> <version>3.5.1</version> </plugin> </plugins> </build> </project> 
复制代码

f. 可以显式的将sonar绑定到Maven生命周期中,如下: 

复制代码
        <plugin>   <groupId>org.codehaus.sonar</groupId> <artifactId>sonar-maven-plugin</artifactId> <version>3.5.1</version> <executions> <execution> <id>sonar</id> <phase>site</phase> <goals> <goal>sonar</goal> </goals> </execution> </executions> </plugin>
复制代码

此时,指定Maven的site声明周期时,则会自动调用sonar.sonar 命令.

2、直接与Jenkins集成

在jenkins的插件管理中选择安装sonar jenkins plugin,该插件可以使项目每次构建都调用sonar进行代码度量。

进入配置页面对sonar插件进行配置,如下图:

配置构建项目,增加Post Build Action:

 

应用程序构建时就会自动触发Sonar对代码的检查


rabbitmq在spring中配置

 spring  rabbitmq在spring中配置已关闭评论
3月 132015
 

rabbitmq在spring中配置,记录下备用

本文侧重介绍如何将rabbitmq整合到spring项目中

maven 依赖包配置如下: 

<dependencies>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>1.2.0.RELEASE</version>
    </dependency>
</dependencies>

1.首先是生产者配置

?
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
33
34
35
36
37
<?xmlversion=”1.0″encoding=”UTF-8″?>
<beansxmlns=”http://www.springframework.org/schema/beans”
       xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
       xmlns:context=”http://www.springframework.org/schema/context”
       xmlns:rabbit=”http://www.springframework.org/schema/rabbit”
       xsi:schemaLocation=”
            http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/rabbit
                http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd”>
 
     
   <!– 连接服务配置  –>
   <rabbit:connection-factoryid=”connectionFactory”host=”localhost”username=”guest”
        password=”guest”port=”5672″ />
         
   <rabbit:adminconnection-factory=”connectionFactory”/>
    
   <!– queue 队列声明–>
   <rabbit:queueid=”queue_one”durable=”true”auto-delete=”false”exclusive=”false”name=”queue_one”/>
    
    
   <!– exchange queue binging key 绑定 –>
    <rabbit:direct-exchangename=”my-mq-exchange”durable=”true”auto-delete=”false”id=”my-mq-exchange<span></span>”>
        <rabbit:bindings>
            <rabbit:bindingqueue=”queue_one”key=”queue_one_key”/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
     
    <– spring amqp默认的是jackson 的一个插件,目的将生产者生产的数据转换为json存入消息队列,由于fastjson的速度快于jackson,这里替换为fastjson的一个实现 –>
    <beanid=”jsonMessageConverter” class=”mq.convert.FastJsonMessageConverter”></bean>
     
    <– spring template声明–>
    <rabbit:templateexchange=”my-mq-exchange”id=”amqpTemplate” connection-factory=”connectionFactory” message-converter=”jsonMessageConverter”/>
</beans>

2.fastjson messageconver插件实现

?
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
importorg.apache.commons.logging.Log;
importorg.apache.commons.logging.LogFactory;
importorg.springframework.amqp.core.Message;
importorg.springframework.amqp.core.MessageProperties;
importorg.springframework.amqp.support.converter.AbstractMessageConverter;
importorg.springframework.amqp.support.converter.MessageConversionException;
 
importfe.json.FastJson;
 
publicclassFastJsonMessageConverter extendsAbstractMessageConverter {
    privatestaticLog log = LogFactory.getLog(FastJsonMessageConverter.class);
 
    publicstaticfinalString DEFAULT_CHARSET =”UTF-8″;
 
    privatevolatileString defaultCharset = DEFAULT_CHARSET;
     
    publicFastJsonMessageConverter() {
        super();
        //init();
    }
     
    publicvoidsetDefaultCharset(String defaultCharset) {
        this.defaultCharset = (defaultCharset !=null) ? defaultCharset
                : DEFAULT_CHARSET;
    }
     
    publicObject fromMessage(Message message)
            throwsMessageConversionException {
        returnnull;
    }
     
    public<T> T fromMessage(Message message,T t) {
        String json =””;
        try{
            json =newString(message.getBody(),defaultCharset);
        }catch(UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return(T) FastJson.fromJson(json, t.getClass());
    }  
     
 
    protectedMessage createMessage(Object objectToConvert,
            MessageProperties messageProperties)
            throwsMessageConversionException {
        byte[] bytes =null;
        try{
            String jsonString = FastJson.toJson(objectToConvert);
            bytes = jsonString.getBytes(this.defaultCharset);
        }catch(UnsupportedEncodingException e) {
            thrownewMessageConversionException(
                    “Failed to convert Message content”, e);
        }
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
        messageProperties.setContentEncoding(this.defaultCharset);
        if(bytes !=null) {
            messageProperties.setContentLength(bytes.length);
        }
        returnnewMessage(bytes, messageProperties);
 
    }
}

3.生产者端调用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
importjava.util.List;
 
importorg.springframework.amqp.core.AmqpTemplate;
 
 
publicclassMyMqGatway {
     
    @Autowired
    privateAmqpTemplate amqpTemplate;
     
    publicvoidsendDataToCrQueue(Object obj) {
        amqpTemplate.convertAndSend(“queue_one_key”, obj);
    }  
}

4.消费者端配置(与生产者端大同小异)

?
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
33
34
35
36
37
38
<?xmlversion=”1.0″encoding=”UTF-8″?>
<beansxmlns=”http://www.springframework.org/schema/beans”
       xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
       xmlns:context=”http://www.springframework.org/schema/context”
       xmlns:rabbit=”http://www.springframework.org/schema/rabbit”
       xsi:schemaLocation=”
            http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/rabbit
                http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd”>
 
     
   <!– 连接服务配置  –>
   <rabbit:connection-factoryid=”connectionFactory”host=”localhost”username=”guest”
        password=”guest”port=”5672″ />
         
   <rabbit:adminconnection-factory=”connectionFactory”/>
    
   <!– queue 队列声明–>
   <rabbit:queueid=”queue_one”durable=”true”auto-delete=”false”exclusive=”false”name=”queue_one”/>
    
    
   <!– exchange queue binging key 绑定 –>
    <rabbit:direct-exchangename=”my-mq-exchange”durable=”true”auto-delete=”false”id=”my-mq-exchange”>
        <rabbit:bindings>
            <rabbit:bindingqueue=”queue_one”key=”queue_one_key”/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
 
     
      
    <!– queue litener  观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象–>
    <rabbit:listener-containerconnection-factory=”connectionFactory”acknowledge=”auto”task-executor=”taskExecutor”>
        <rabbit:listenerqueues=”queue_one”ref=”queueOneLitener”/>
    </rabbit:listener-container>
</beans>

5.消费者端调用

?
1
2
3
4
5
6
7
8
9
importorg.springframework.amqp.core.Message;
importorg.springframework.amqp.core.MessageListener;
 
publicclassQueueOneLitenerimplements MessageListener{
    @Override
    publicvoidonMessage(Message message) {
        System.out.println(” data :”+ message.getBody());
    }
}

6.由于消费端当队列有数据到达时,对应监听的对象就会被通知到,无法做到批量获取,批量入库,因此可以在消费端缓存一个临时队列,将mq取出来的数据存入本地队列,后台线程定时批量处理即可

转自:http://my.oschina.net/never/blog/140368

python fabric 资料汇总

 python  python fabric 资料汇总已关闭评论
10月 302014
 

自动化运维工具Fabric – Fabfile 的创建和使用

该篇文章来源于 Fabric 的官方文档,原文为Fabfile construction and use

这篇文章包含了关于 fabfiles 的以下两项

  1. 怎样编写最好的 fabfile
  2. 写好后怎样使用 fabfile

Fabfile 发现

Fabric 能够加载 Python 模块(比如:fabfile.py)或者 包(比如:一个包含__init__.py文件的名叫fabfile的目录)。它默认寻找命名为 fabfile 的文件或者目录,要么是 fabfile/,要么是fabfile.py。

fabfile 发现算法搜索调用用户的当前工作目录或者是任何父目录。因此,它是面向“工程”使用的,这个工程的源码树的根目录有一个 fabfile.py文件。无论在这个树的哪里,当使用 fab 命令的调用的时候,这个 fabfile 文件将被发现。

可以使用命令行参数 -f 来重写指定名字搜索到的 fabfile文件,或者是加一个 fabricrc 行来设置 fabfile 的值。例如,你想命名你的 fabfile 为 fab_tasks.py,你可以创建这样一个文件,然后调用命令 fab -f fab_tasks.py <task name> ,或者是添加如下 fabfile = fab_tasks.py 到 ~/.fabricrc文件里面。

如果给定的 fabfile 名字中除了文件名之外还包含路径(比如:../fabfile.py 或者是 /dir1/dir2/custom_fabfile),它将被作为一个文件路径对待,并且没有任何排序搜索就直接验证是否存在。当在这种模式下,波浪线扩展将被应用。因此你可以参考:~/personal_fabfile.py

注:为了访问你的 fabfile文件的内容, Fabric 做了一个正常的 import (实际是一个 import) – it does not do any eval-ing or similar. 为了完成这个工作, Fabric 临时把发现的 fabfile 文件的路径添加进 Python 的加载路径中 (并且然后立即移除了它)

注:Changed in version 0.9.2: The ability to load package fabfiles

Importing Fabric

因为 Fabric 是 Python 编写的。你可以以任何的你想的方式 import 它的组件。尽管如此,为了封装以及便利(使得更加容易的包装 Fabric 的脚本),Fabric 的公共 API 是维护在 fabric.api 模块中。

所有的 Fabric 的 OperationsContext ManagersDecorators 和 Utils 被包含在这个模块中,作为一个单独的标记命名空间。这可以使你的 fabfiles 文件有一个相当简单以及方便的接口调用。

from fabric.api import * # call run(), sudo(), etc etc 

这不是最好的技术实践(因为一些原因),你仅仅是使用了一些 Fabric API 的调用。一个更好的方法是精确的使用 from fabric.api import env, run 或者是类似的方法。尽管如此,在大多数重要的 fabfiles ,你可以通过使用 import 星号来使用所有的或者大部分的 API。

from fabric.api import *

主要包括如下:

from fabric.api import abort, cd, env, get, hide, hosts, local, prompt,
    put, require, roles, run, runs_once, settings, show, sudo, warn 

在这个示例我们感觉到实用主义重写了最佳实践。

自动化运维工具Fabric – 智能的执行任务(roles、execute)


在 Fabric 1.3 版本中,你可以通过 roles 来给服务器定义一组角色,然后根据角色 使用 execute 来执行不同的操作。

代码一

from fabric.api import run, roles

env.roledefs = { 'db': ['db1', 'db2'], 'web': ['web1', 'web2', 'web3'],
} @roles('db') def migrate(): # Database stuff here. pass @roles('web') def update(): # Code updates here. pass 

在 Fabric <= 1.2 版本的时候,这唯一让 migrate操作 在 db组 服务器生效, update 操作在 web 组服务器生效的方法如下:

$ fab migrate update

代码二

而在 Fabric 1.3 版本中,你可以使用 execute 来启动一个元任务,你可以修改代码如下:

from fabric.api import run, roles, execute
env.roledefs = { 'db': ['db1', 'db2'], 'web': ['web1', 'web2', 'web3'],
} @roles('db') def migrate(): # Database stuff here. pass @roles('web') def update(): # Code updates here. pass # 新增的 execute 模块 def deploy(): execute(migrate)
    execute(update)

然后执行如下命令:

fab deploy

这样的话,roles 装饰符会如预期的那样生效。执行的结果如下:

migrate on db1
migrate on db2 update on web1 update on web2 update on web3 

注意

这个技巧让任务仅仅只运行一次,是因为它们自己没有主机列表(包含全局主机列表设置),如果将在多个主机上运行使用 一个 ‘regular’ 任务,调用 execute 将多次运行,结果就是成子任务调用数量乘数级的增加 — 小心

注:主机数量很大,容易造成 “执行风暴”?多次重复执行?把本机弄死?还是客户端的任务会被重复执行。需要找一组测试机测试下,目前还未测试。有测试过的同学可以给个最终的答案。
注: reguar 翻译为 普通?定期的?合格的。欢迎各位指正下。

如果你想让你的 exeute 调用 仅仅只执行一次,你可以使用 runs_once 装饰符。

This technique works because tasks that themselves have no host list (this includes the global host list settings) only run one time. If used inside a “regular” task that is going to run on multiple hosts, calls to execute will also run multiple times, resulting in multiplicative numbers of subtask calls – be careful!

If you would like your execute calls to only be called once, you may use the runs_once decorator.

自动化运维工具Fabric – 密码管理(env.password and ssh key)

在使用 Fabric 的过程中,如果大批量服务器处理的话,我们就需要针对配置主机的密码,每台主机的密码相同还好,不同的话,就需要针对不同的主机做配置了,以下有两种配置方式

注:本文主要参考官方文档 Password management 以及 grimnes 的文章 Using SSH keys with Fabric

env.password

Fabric 在某些场合下通过保持在内存中以及双重缓存来帮助你记录登录密码以及 sudo 密码。当多个系统的密码一样的时候,这可以避免让你重复乏味的输入密码。或者是一个远程系统的 sudo 配置文件没有缓存它自己的密码时候。

第一层是一个默认简单的或是备用的密码缓存。 env.password (它也可以通过命令行参数 –password 或是 –initial-password-prompt 来设定)。这个 env 变量存储单个密码,它会咋当前的 host string 没有指定的主机缓存条目的事件的时候尝试。

env.passwords (复数,表示多个密码),作为每一个主机用户的缓存,为唯一的 user/host/port 组合存储了最近输入的密码1。由于这个缓存,连接具有相同 session 的不同用户和主机,仅仅只要求单个的密码。

依赖于你的配置文件以及你 session 连接的主机的数目,你可能会发现设置这些 env 是非常有用的。尽管如此, Fabric 会在必要的时候自动填充他们,不需要你多余的配置。

特别说明,每次对用户提出的密码提示,这值都会被用于更新这默认的密码缓存以及 env.host_string 当前值的缓存值。

没代码说的球,上代码。NO CODE NO BB

  1. 所有主机密码一样的代码,下面代码的几台主机是用户名和密码一样的,主要做的事情是批量注释,批量停止应用,以及批量关机
#!/usr/bin/python env # -*- coding: utf-8 -*- from fabric.api import env from fabric.api import cd from fabric.api import run from fabric.api import local from fabric.api import get from fabric.api import put



env.user = 'username' env.password = 'passwd' env.hosts = ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'] def get_version(): local('cat /etc/issue')
    run('cat /etc/issue') #with cd('/root/'): #    put('/home/libaoyin/test.txt', 'test.txt', mode=0755) #    get('hello_world.txt') run('ls') def get_host_name(): run('hostname') #kill all stockd'service def kill_apps_stockd(): run('killall stockd') #discharge the crontab def comment_crontabl(): put ('/home/apps/ykq/crontab.txt','crontab.txt')
    run('crontab crontab.txt') # offline stockd's service def offline_stockd(): kill_apps_stockd()
    comment_crontabl() #shutdown all stockd server def shutdown_stockd_server(): run('sudo poweroff')
  1. 所有主机的用户名一样,但密码不一样
    这里感谢@Kollin 的提示,修正下错误,并且以他的示例程序为例。
    不过感觉这个Fabric的这方面这样做的原因应该是“基于不同的用户名和不同的密码考虑的”
    没有考虑过相同用户名,不同密码的情况。如果要这样做,个人感觉可以修改源码,然后支持env.user 和 env.passwords拼接的方式。不然每个值都要输入 user@ 这样要多写很多代码

错误的示例程序。

env.user = 'username' env.passwords = {'192.168.1.1':'passwd1','192.168.1.2':'passwd2','192.168.1.3':'passwd3'}
env.hosts = ['192.168.1.1', '192.168.1.2', '192.168.1.3']

正确的 @Kollin的示例程序

from fabric.api import *
env.hosts = [ '[email protected]', '[email protected]',
]

env.passwords = { '[email protected]:22': 'password1', '[email protected]:22': 'password2',
} @task def echo(): run('echo "hello,world"')

注:后续会专门写一篇关于 Fabric 中角色的文章

SSH KEY

官方文档中建议,为了安全起见,最好是使用 SSH KEY 的方式来批量执行主机,操作方式如下:

  1. 登录服务器,生成 SSH Key
$ ssh-keygen -t rsa -b 4096 

键入以上命令后,会出现一连串的提示,忽略它,一直按回车键即可。
执行完成后,将在用户的 ~/.ssh/ 目录生成以下两个文件:

~/.ssh/id_rsa  私钥

~/.ssh/id_rsa.pub 公钥
  1. 公钥填充

    把生成的公钥文件 ~/.ssh/id_rsa.pub里面的数据添加进远程服务器的 authorized_keys file 文件中

    注:如果远程服务器没有 .ssh 文件夹,需要创建

scp ~/.ssh/id_rsa.pub user@host:~/.ssh/
cat ~/.ssh/id_rsa.pub >> authorized_keys 

然后准备我们的用我们的 SSH Key 代替我们的密码:

#!/usr/bin/python env from fabric.api import * from fabric.colors import * from fabric.context_managers import *
env.hosts=['168.192.1.10','168.192.1.12'] # env.password='xxxxxx' env.key_filename = "~/.ssh/id_rsa" def ls_path(): print(green("I'm local  /home/apps/")) with cd('/home/apps'):
        run('ls -l') def put_path(): print(green("I'm put local's test file to 10 and 12"))
    put('/home/apps/test','/home/apps/')
    print(yellow("I'm 10 or 12 /home/apps/")) with cd('/home/apps'):
        run('ls -l') def deploy(): execute(ls_path)
    execute(put_path)

  1. 我们强烈推荐使用基于 SSH key 的访问方式代替依靠同样的密码,SSH key 方式是更加安全的 

参考: http://segmentfault.com/blog/yexiaobai/1190000000494159

————————————————————————————————————————————————————————————————-

fabric常用运维资料

fabric的安装方法:
$ pip install fabric
这会把fabric依赖模块一起安装好
在命令行打fab命令,如果能看到这样的输出,就表明fabric已经安装好
Usage: fab [options] [:arg1,arg2=val2,host=foo,hosts=’h1;h2′,…] …

使用fabric:
1. 新建一个文件夹,作为我们的开发目录
$ cd ~
$ mkdir -p develop/python/fabricDev
$ cd develop/python/fabricDev

2. 新建 fabfile.py 文件,添加如下代码并保存:
#encoding=utf-8
from fabric.api import run

def host_type():
run(‘uname -s’)

解释:
fabric的fab命令会在当前目录下寻找fabfile.py这个文件
fabfile.py定义的每一个函数,就是一个命令
比如这里定义了host_type命令,用来获取操作系统名字

3. 在本机运行fabric,获取本机host_type
$ fab -H localhost host_type
解释:-H localhost指定在本机运行
打印输出:
liuyufan@liumatoMacBook-Pro ~/github/python/fabric % fab -H localhost host_type
[liuyufan@localhost:22] Executing task ‘host_type’
[liuyufan@localhost:22] run: uname -s
[liuyufan@localhost:22] out: Darwin
[liuyufan@localhost:22] out:

Done.
Disconnecting from localhost… done.

4. 获取hadoop集群4台机器的host_type
我在自己的电脑上搭了4台虚拟机做hadoop集群,现在我想用fabric获取这四台机器的host_type。我只需要将这4台机器ssh连接的ip, port, username, password设置好即可
编辑fabfile.py并保存

#encoding=utf-8
from fabric.api import run, env #(新增)env模块,指定运行环境信息

env.hosts=[ #(新增)
[email protected]:22’, #(新增)
[email protected]:22’, #(新增)
[email protected]:22’, #(新增)
[email protected]:22’, #(新增)
] #(新增)
env.password = ‘patrick’ #(新增)

def host_type():
run(‘uname -s’)

执行命令:
$ fab host_type

打印输出:
liuyufan@liumatoMacBook-Pro ~/github/python/fabric % fab host_type
[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] run: uname -s
[[email protected]:22] out: Linux
[[email protected]:22] out:

[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] run: uname -s
[[email protected]:22] out: Linux
[[email protected]:22] out:

[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] run: uname -s
[[email protected]:22] out: Linux
[[email protected]:22] out:

[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] run: uname -s
[[email protected]:22] out: Linux
[[email protected]:22] out:

Done.
Disconnecting from [email protected]… done.
Disconnecting from [email protected]… done.
Disconnecting from [email protected]… done.
Disconnecting from [email protected]… done.

可以看到fabric根据机器在env.hosts数组的顺序,顺序执行host_type命令。其实fabric还支持并行执行,下面用并行方式运行一下

5. 并行获取4台机器的host_type信息
编辑fabfile.py并保存
#encoding=utf-8
from fabric.api import run, env, parallel#(新增)

env.hosts=[
[email protected]:22’,
[email protected]:22’,
[email protected]:22’,
[email protected]:22’,
]
env.password = ‘patrick’

@parallel#(新增)
def host_type():
run(‘uname -s’)

解释:
每个host的每个task会启动一个新的进程,默认采用划窗算法保证不会启动过多进程。可以使用@parallel(pool_size=5)指定进程池的最大进程数

执行命令:
$ fab host_type

打印输出:
liuyufan@liumatoMacBook-Pro ~/github/python/fabric % fab host_type
[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] Executing task ‘host_type’
[[email protected]:22] run: uname -s
[[email protected]:22] run: uname -s
[[email protected]:22] run: uname -s
[[email protected]:22] run: uname -s
[[email protected]:22] out: Linux
[[email protected]:22] out:

[[email protected]:22] out: Linux
[[email protected]:22] out:

[[email protected]:22] out: Linux
[[email protected]:22] out:

[[email protected]:22] out: Linux
[[email protected]:22] out:

Done.

到此fabric就可以在集群上运行了
是否需要parallel功能,可以根据具体功能做权衡。顺序执行具有fail-fast的优点,比如上面4台机器,假如第3台正好网络断开,这样fabric在连接到第三台机器的时候就会报错并退出。这样下次执行的时候只需要从第3台开始,将3、4添加到执行列表中即可
而并行执行有可能是第1,2,4台机器执行成功,而第3台却没有。这就需要从输出里查看哪台失败。假设有100台机器,碰巧是第42,57,69台机器没有执行成功,这就需要从100台输出里查看失败的机器,查找过程将会非常麻烦

6. fabric 其他有用的命令
# 1. 直接执行 shell 命令
# run(‘cat /var/crawl/client.xml |grep ‘)
# run(‘cmd 2′)
# run(‘cmd 3′)

# 2. 切换目录并执行
# with cd(‘/var/crawl’):
# run(‘echo hi >> test.txt’)

# 3. 判断文件或者目录是否存在
# if exists(‘var/crawl/client.xml’):
# print ‘Config file exists’
# else:
# print ‘Config file not exist’

# 4. 从远端服务器下载文件
# get(‘/remote/path/to/file’,’/local/path/’)

# 5. 上传文件到远端服务器
# put(‘/local/path/to/file’,’/remote/path’)

# 6. 嵌套运行
# with prefix(‘cd ~/shark-0.9.1/bin/’):
# with prefix(‘chmod +x *.sh’):
# run(‘shark-shell.sh’)

# 7. sudo
# sudo(“mkdir /var/www/new_docroot”, user=”www-data”)

# 8. 获取返回值并执行命令
# files = run(‘ls’)
# run(‘ls -l’, files)

上面这些操作基本可以满足日常运维需求

参考:http://blog.csdn.net/sijiazhaiyuan/article/details/23884873

Squid 缓存代理服务器的完整配置

 squid  Squid 缓存代理服务器的完整配置已关闭评论
2月 192014
 

一篇很好的文章,看完对squid服务器配置及测试将大致了解:

Squid 缓存代理服务器

Squid 的作用

1.通过缓存的方式为用户提供web访问加速

2.对用户的web访问进行过滤控制

缓存代理服务器又分为普通代理服务器,透明代理服务器,和反向代理服务器。

普通代理服务即指标准的,传统的代理服务,需要客户机在浏览器中指定代理服务器的地址,端口

透明代理服务器适合企业的网关主机,客户机不需要指定代理服务器地址,端口等信息,需要设置防火墙策略将客户机的web访问数据转交给代理服务器

反向代理服务为INTERNET用户访问网络内网站点提供缓存加速。提高访问效率。

squid 缓存代理服务的软件安装包名为 squid-2.6.STABLE21-3.el5(版本可能不同),默认监听端口为TCP 3128 默认访问日志文件 :/var/log/squid/access.log 进程名为squid , 主程序:/usr/sbin/squid ,配置目录:/etc/squid 主配置文件是 /etc/squid/squid.conf

我们已经对squid有了初步的认识,现在了解一下主配置文件squid.conf 的几个常用配置项

http_port 3128 此项设定默认监听端口,可以改变IP与监听端口

cache_mem 64 MB 设定缓存大小,一般情况下建议将物理内存的1/3划给它

maximum_object_size 4096 KB 定义最大缓存对象

reply_body_max_size 10240000 allow all 访问控制规则,对响应数据做限定,如果把这个值设定为0 就表示不做限定

access_log /var/log/squid/access.log squid 为squid做访问日志

visible_hostname proxy.test.com 可见主机名,默认配置文档中并没有定义,建议设定,否则影响squid启动

cache_dir ufs /var/spool/squid 1024 16 256

(缓存文件放置位置,ufs是文件系统类型,1024指定缓存目录大小, 16 缓存空间一级子目录个数, 256 指缓存空间的二级子目录个数)

cache_mgr [email protected] 定义服务器管理员邮箱

cache_effective_user squid

ache_effective_group squid 定义squid的UID GID

error_directory /usr/share/squid/errors/Simplify_Chinese 定义错误信息显示为中文,squid错误信息支持多种语言。可以在/usr/share/squid/errors 下查看

http_access allow localhost

http_access deny all 访问控制策略,

在没有设置任何规则时,将拒绝所有客户端的访问请求

有规则但是找不到相匹配的项时,将采取与最后一条规则相反的权限,即如果最后一条规则是allow 那么就拒绝客户端的请求,否则允许该请求

ACL(Access Control List )访问控制列表,可以从客户机的IP地址,请求访问的URL/域名/文件类型/访问时间/并发请求等方面进行控制

ACL的格式是

acl 列表名称 列表类型 列表内容

acl列表的使用在squid的官方网站上有详细的介绍,下面只介绍几种常用的acl列表类型

src 基于客户端IP地址做控制, 源ip

dst 基于访问目的IP做控制

srcdomain 基于域名的源地址解析

port 基于端口控制

proto 基于协议类型做控制

browser 对浏览器的做控制 # acl notfirefox browser MOZILIA

time 基于时间做控制,acl worktime time 10:00-23:59

acl working time 0:00-5:59

(注意:time时间控制时,前一个时间点要小于后一个时间点,若是要过0点设定,就仿写成上面例子,列表名称可以一样,acl匹配时取它们的并集)

maxconn 最大并发连接数

url_regex [-i ] 统一资源,针对url做正则表达式匹配 -i 不区分大小写

acl notpdf url_regex –i ^http.*.pdf$

urlpath_regex [-i] 可不指定前端只对url 尾部做匹配

acl notpdf urlpath_regex –i .*.pdf$

下面先来配置一个传统的代理服务

clip_image002

要求做一个普通代理,pc机能通过squid服务器访问外网192.168.0.254的web服务,但是在18:30-8:30之间内网用户不能上网,不能访问下载以.rmvb .mp3为后缀的文件,单个文件最大不能超过10M 代理端口为8080,错误提示呈现为中文,管理员邮箱为[email protected], 内存大小为64M, 缓存目录大小为1024M, 并为squid配置可见主机名

1 安装squid软件包

#yum install squid

2 编辑/etc/squid/squid.conf

配置可见主机名

clip_image004

指定缓存目录大小

clip_image006

配置管理员邮箱

clip_image008

定义内存大小

clip_image010

指定 缓存目录为1024M

clip_image006[1]

定义错误信息显示

clip_image012

定义最大单个文件大小

clip_image014

修改监听端口

clip_image016

最acl控制

clip_image018

3.现在 到内网pc机上将为浏览器配置代理服务,将ip指向192.168.0.72 端口指向8080

clip_image020

 

clip_image022

现在连上0.254 了测试一下你做的acl 控制吧

 

现在我们把题目修改一下,把上面的普通代理做成透明代理

1 修改squid.conf 配置文件,并重新加载该配置项

只需要修改一下监听端口

clip_image024

2.在squid主机上添加iptables 规则

clip_image026

3 客户机浏览器不需要指定代理服务器的地址,端口

clip_image028

做完透明代理访问也是一样的

clip_image030

下面再介绍一下反向代理

clip_image032

1.修改squid.conf 文件,并重启加载配置项

(cache_peer web 服务器 服务器类型 http端口 icp端口 可选项)

http_port 192.168.0.74:80 vhost 修改监听端口

cache_peer 192.168.10.2 parent 80 0 originserver weight=5 max-conn=30

cache_peer 192.168.10.3 parent 80 0 originserver weight=5 max-conn=30

cache_peer 192.168.10.4 parent 80 0 originserver weight=5 max-conn=30

cache_peer 192.168.10.5 parent 80 0 originserver weight=1 max-conn=8

2,好了现在可以验证了,在外网的客户机访问反向代理服务器192.168.0.74能够看到web提供的网页

(注意透明代理服务器和反向代理服务器不能配置在一台squid服务器上)

转自:http://sunting.blog.51cto.com/1244382/281653/

Spring ApplicationContext.xml配置的12个技巧和Bean属性说明(转)

 java, spring  Spring ApplicationContext.xml配置的12个技巧和Bean属性说明(转)已关闭评论
2月 282013
 

转自:http://blog.csdn.net/mqboss/article/details/5868113

Spring ApplicationContext.xml配置的12个技巧

 

Spring是一个强有力的java程序框架,其被广泛应用于java的程序中。它用POJO提供了企业级服务。 Spring利用依赖注入可以获得简单而有效的测试能力。Spring beans,依赖关系,以及服务所需要的bean都将在配置文件中予以描述,配置文件一般采用XML格式。然而XML配置文件冗长而不易使用,在你进行一 个使用了大量bean的大项目中它将变得难以阅读和控制。

 

在这篇文章中我将给你展示12种的有关Spring XML配置文件的最佳技巧。它们中的一些具有更多的实际意义,而不仅是最好的技巧。请注意另外一些因素,例如域模型的设计,会影响到XML配置,但是这篇文章更关注于XML配置的可读性和可操控性。

 

1.避免使用自动装配

Spring 可以通过bean类的自省来实现自动装配依赖,这样的话你就不必明确地描述bean的属性或者构造函数的参数。根据属性名称活匹配类型,bean属性可以 自动进行装配。而构造函数可以根据匹配类型自动装配。你甚至可以设置自动装配进行自动侦测,这样Spring替你就会选择一个合适的机制。请看下面的例 子:

 

 

Spring 可以通过bean类的自省来实现自动装配依赖,这样的话你就不必明确地描述bean的属性或者构造函数的参数。根据属性名称活匹配类型,bean属性可以 自动进行装配。而构造函数可以根据匹配类型自动装配。你甚至可以设置自动装配进行自动侦测,这样Spring替你就会选择一个合适的机制。请看下面的例 子:

 

<bean id=”orderService”

class=”com.lizjason.spring.OrderService”

autowire=”byName”/>

 

OrderService 类的属性名被用来和容器中的一个bean实例进行匹配。自动装配会默默的保存一些类型信息并降低混乱。然而,由于它会牺牲掉这种配置的直观性和可维护性, 你在实际的项目中将不会用到它。许多指南和陈述材料都把它吹捧为Spring的一个非常cool的特性,而没有提到它的这个缺点。依我之见,就像 Spring的对象池一样,它更多了一些商业味道。它看起来好像可以使XML配置文件更精简一些,但实际上却增加其复杂性,尤其是在你的较大规模的工程中 已经定义了很多bean的时候更是如此。Spring允许你混合使用自动和手动装配,但是这种矛盾会使XML配置更加的令人费解。

 

2.使用命名规范

和Java 编码的理念一样,在项目中始终用清晰的,描述性的,一致的命名规范对开发人员理解XML配置非常有用。拿bean ID举例来说,你可以遵循Java类中属性的命名规范。比如说,OrderServiceDAO的bean ID应该是orderServiceDAO。对于大项目来说,在bean ID前加包名来作为前缀。

 

3.使用简化格式

简化格式有利于减少冗余,因为它把属性值和引用作为属性,而不是子元素。看下面的例子:

<bean id=”orderService”

class=”com.lizjason.spring.OrderService”>

<property name=”companyName”>

<value>lizjason</value>

</property>

<constructor-arg>

<ref bean=”orderDAO”>

</constructor-arg>

</bean>

以上程序可以重新以简化格式书写为:

<bean id=”orderService”

class=”com.lizjason.spring.OrderService”>

<property name=”companyName”

value=”lizjason”/>

<constructor-arg ref=”orderDAO”/>

</bean>

简化格式在1.2版本时已经可用了,但请注意不存在<ref local=”…”>这种简化格式不仅可以较少你的代码输入量,而且可以使XML配置更加的清晰。当你的配置文件中存在大量的bean定义时,它可以显著地提高可读性。

 

4.尽量使用type而不是index去解决构造函数参数的匹配问题

当构造函数中有多个同类型的参数时,Spring只允许你使用从0开始的index或者value标签来解决这个问题。请看下面的例子:

<bean id=”billingService”

class=”com.lizjason.spring.BillingService”>

<constructor-arg index=”0″ value=”lizjason”/>

<constructor-arg index=”1″ value=”100″/>

</bean>

最好用type属性取代上面的做法:

<bean id=”billingService”

class=”com.lizjason.spring.BillingService”>

<constructor-arg type=”java.lang.String”

value=”lizjason”/>

<constructor-arg type=”int” value=”100″/>

</bean>

 

用index可以稍微减少冗余,但是它更容易出错且不如type属性可读性高。你应该仅在构造函数中有参数冲突时使用index。

 

5.如可能,尽量复用bean定义

Spring 提供了一种类似于继承的机制来降低配置信息的重复并使XML配置更加的简单。一个子bean可以从它的父bean继承配置信息,本质上这个父bean就像 它的子bean的一个模板。这是一个在大型项目中必须使用的特性。所有你要做的就是把父bean的abstract属性置为true,并在子bean中加 以引用。例如:

<bean id=”abstractService” abstract=”true”

class=”com.lizjason.spring.AbstractService”>

<property name=”companyName”

value=”lizjason”/>

</bean>

 

<bean id=”shippingService”

parent=”abstractService”

class=”com.lizjason.spring.ShippingService”>

<property name=”shippedBy” value=”lizjason”/>

</bean>

shippingService bean继承了abstractService bean的属性companyName的值lizjason。注意,如果你为bean声名一个class或工厂方法,这个bean将会默认为abstract

 

6.尽量使用ApplicationContext装配bean,而不是用import

像Ant脚本中imports一样,Spring的import 元素对于模块化bean的装配非常有用,例如:

<beans>

<import resource=”billingServices.xml”/>

<import resource=”shippingServices.xml”/>

<bean id=”orderService”

class=”com.lizjason.spring.OrderService”/>

<beans>

然而,比起在XML中用imports预装配这些bean,利用ApplicationContext来配置它们将更加灵活,也可以使XML配置更加的易于管理。你可以像下面这样传递一个bean定义数组到ApplicationContext的构造函数中:

String[] serviceResources =

{“orderServices.xml”,

“billingServices.xml”,

“shippingServices.xml”};

ApplicationContext orderServiceContext = new

ClassPathXmlApplicationContext(serviceResources);

 

7.用id来标识bean

你可以用id 或名字作为bean的标识。用id可读性较差,但是它可以影响XML分析器使bean的reference有效。如果id由于XML IDREF约束而无法使用,你可以用name作为bean的标识。XML IDREF约束是指id必须以字母开始(或者是在XML声名了的一个标点符号),后面可以是字母,数字,连字符,下划线,冒号或full stops(不知道怎么翻译好)。在实际应用中很少会遇到XML IDREF约束问题。

 

8.在开发阶段使用依赖检查

你可以为bean的dependency-check属性设置一个值来取代默认的none,比如说simple,objects或者all,这样的话容器将替你做依赖有效性的检查。当一个bean的所有属性(或者某些属性目录)都被明确设置,或利用自动装配时将会非常有用。

<bean id=”orderService”

class=”com.lizjason.spring.OrderService”

dependency-check=”objects”>

<property name=”companyName”

value=”lizjason”/>

<constructor-arg ref=”orderDAO”/>

</bean>

在这个例子中,容器将确保这些属性不是privitives或者保证collections是为orderService bean设置的。为所有的bean设置默认的依赖检查是可能的,但这个特性由于有些bean的属性不需要设置而很少使用。

 

9.为每个配置文件加一个描述注释

在XML配置文件中最好使用有描述性的id和name,而不是成堆的注释。另外,加一个文件描述头将会非常有用,这个描述可以概括文件中定义的bean。另一个选择,你可以在description元素中加入描述信息。例如:

<beans>

<description>

This file defines billing service

related beans and it depends on

baseServices.xml,which provides

service bean templates…

</description>

</beans>

用description元素的一个好处就是工具可以很容易的把描述信息从这个元素中提取出来。

 

10.   和team members沟通变更

当你修改java源码后,要确保更改了配置文件中的相应部分并把这个情况告知你的team members。XML配置文件也是代码,它们是程序的重要组成部分,但它们很难阅读和维护。大多数时间里,你需要同时看XML配置文件和java代码才能知道是怎么回事。

 

11.   setter注入和构造函数注入,优先使用前者

Spring提供了三种注入方式:构造函数注入,setter注入和方法注入。一般我们使用前两种。

<bean id=”orderService”

class=”com.lizjason.spring.OrderService”>

<constructor-arg ref=”orderDAO”/>

</bean>

 

<bean id=”billingService”

class=”com.lizjason.spring.BillingService”>

<property name=”billingDAO”

ref=”billingDAO”>

</bean>

在这个例子中,orderService bean用了构造函数注入,而BillingService bean用了setter注入。构造函数注入可以确保bean正确地构建,但是setter注入更加的灵活和易于控制,特别是当class有多个属性并且它们中的一些是可选的情况是更是如此。

 

12.   不要滥用注入

就像前面提到的,Spring的ApplicationContextEclipse and IntelliJ,java代码更加的易于阅读,维护和管理比使XML文件可以替你创建java对象,但不是所有的java对象都应该通过注入创建。例如,域对象就不应该通过ApplicationContext创建。Spring是一个优秀的框架,但是考虑到可读性和可操控性,基于XML配置的配置会在定义很多bean的时候出现麻烦。过渡使用依赖注入将会使XML配置更加的复杂和冗长。切记,当使用高效的IDE时,例如

 

结论

XML是Spring 流行的配置格式。存在大量bean定义时,基于XML的配置会变得冗长而不易使用。Spring提供了丰富的配置选项。适当地使用这些选项可以使XML配 置更加的清晰,但其它的一些选项,例如自动装配,可能会降低可读性和可维护性。参考本文中提到的这些技巧可能会帮助你创建干净而易读的XML配置文件

 

 

 

<bean

 

id=”beanId”(1)

 

name=”beanName”(2)

 

class=”beanClass”(3)

 

parent=”parentBean”(4)

 

abstract=”true | false”(5)

 

singleton=”true | false”(6)

 

lazy-init=”true | false | default”(7)

 

autowire=”no | byName | byType | constructor | autodetect | default”(8)

 

dependency-check = “none | objects | simple | all | default”(9)

 

depends-on=”dependsOnBean”(10)

 

init-method=”method”(11)

 

destroy-method=”method”(12)

 

factory-method=”method”(13)

 

factory-bean=”bean”>(14)

 

</bean>

 

 

(1)、id: Bean的唯一标识名。它必须是合法的XML ID,在整个XML文档中唯一。

 

(2)、name: 用来为id创建一个或多个别名。它可以是任意的字母符合。多个别名之间用逗号或空格分开。

 

(3)、class: 用来定义类的全限定名(包名+类名)。只有子类Bean不用定义该属性。

 

(4)、parent: 子类Bean定义它所引用它的父类Bean。这时前面的class属性失效。子类Bean会继承父类Bean的所有属性,子类Bean也可以覆盖父类Bean的属性。注意:子类Bean和父类Bean是同一个Java类。

 

(5)、abstract(默认为”false”):用来定义Bean是否为抽象Bean。它表示这个Bean将不会被实例化,一般用于父类Bean,因为父类Bean主要是供子类Bean继承使用。

 

(6)、singleton(默认为“true”):定义Bean是否是Singleton(单例)。如果设为“true”,则在BeanFactory作用范围内,只维护此Bean的一个实例。如果设为“flase”,Bean将是Prototype(原型)状态,BeanFactory将为每次Bean请求创建一个新的Bean实例。

 

(7)、lazy-init(默认为“default”):用来定义这个Bean是否实现懒初始化。如果为“true”,它将在BeanFactory启动时初始化所有的Singleton Bean。反之,如果为“false”,它只在Bean请求时才开始创建Singleton Bean。

 

(8)、autowire(自动装配,默认为“default”):它定义了Bean的自动装载方式。

 

1、“no”:不使用自动装配功能。

 

2、“byName”:通过Bean的属性名实现自动装配。

 

3、“byType”:通过Bean的类型实现自动装配。

 

4、“constructor”:类似于byType,但它是用于构造函数的参数的自动组装。

 

5、“autodetect”:通过Bean类的反省机制(introspection)决定是使用“constructor”还是使用“byType”。

 

(9)、dependency-check(依赖检查,默认为“default”):它用来确保Bean组件通过JavaBean描述的所以依赖关系都得到满足。在与自动装配功能一起使用时,它特别有用。

 

1、 none:不进行依赖检查。

 

2、 objects:只做对象间依赖的检查。

 

3、 simple:只做原始类型和String类型依赖的检查

 

4、 all:对所有类型的依赖进行检查。它包括了前面的objects和simple。

 

(10)、depends-on(依赖对象):这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始化之前创建。

 

(11)、init-method:用来定义Bean的初始化方法,它会在Bean组装之后调用。它必须是一个无参数的方法。

 

(12)、destroy-method:用来定义Bean的销毁方法,它在BeanFactory关闭时调用。同样,它也必须是一个无参数的方法。它只能应用于singleton Bean。

 

(13)、factory-method:定义创建该Bean对象的工厂方法。它用于下面的“factory-bean”,表示这个Bean是通过工厂方法创建。此时,“class”属性失效。

 

(14)、factory-bean:定义创建该Bean对象的工厂类。如果使用了“factory-bean”则“class”属性失效。

 

 

 

 

 

 

 

下面列出<ref>元素的所有可用的指定方式:

 

bean:可以在当前文件中查找依赖对象,也可以在应用上下文(ApplicationContext)中查找其它配置文件的对象。

 

local:只在当前文件中查找依赖对象。这个属性是一个XML IDREF,所以它指定的对象必须存在,否则它的验证检查会报错。

 

external:在其它文件中查找依赖对象,而不在当前文件中查找。

 

总的来说,<ref bean=”…”/>和<ref local=”…”/>大部分的时候可以通用。“bean”是最灵活的方式,它允许你在多个文件之间共享Bean。而“local”则提供了便利的XML验证。

 

 

 

如何使用spring的作用域:

<bean id=”role” scope=”singleton”/>

这里的scope就是用来配置spring bean的作用域,它标识bean的作用域。

在spring2.0之前bean只有2种作用域即:singleton(单例)、non-singleton(也称 prototype), Spring2.0以后,增加了session、request、global session三种专用于Web应用程序上下文的Bean。因此,默认情况下Spring2.0现在有五种类型的Bean。当然,Spring2.0对 Bean的类型的设计进行了重构,并设计出灵活的Bean类型支持,理论上可以有无数多种类型的Bean,用户可以根据自己的需要,增加新的Bean类 型,满足实际应用需求。

1、singleton作用域

当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把 一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都 将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中 只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时 候,spring的IOC容器中只会存在一个该bean。

配置实例:

<bean id=”role” scope=”singleton”/>

或者

<bean id=”role” singleton=”true”/>

2、prototype

prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。 清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)

配置实例:

<bean id=”role” scope=”prototype”/>

或者

<beanid=”role” singleton=”false”/>

3、request

request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例:

request、session、global session使用的时候首先要在初始化web的web.xml中做如下配置:

如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可:

<web-app>

<listener>

<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>

</listener>

</web-app>

 

 

,如果是Servlet2.4以前的web容器,那么你要使用一个javax.servlet.Filter的实现:

<web-app>

..

<filter>

<filter-name>requestContextFilter</filter-name>

<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>requestContextFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

</web-app>

 

 

接着既可以配置bean的作用域了:

<bean id=”role” scope=”request”/>

4、session

session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效,配置实例:

配置实例:

和request配置实例的前提一样,配置好web启动文件就可以如下配置:

<bean id=”role” scope=”session”/>

5、global session

global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。

配置实例:

和request配置实例的前提一样,配置好web启动文件就可以如下配置:

<bean id=”role” scope=”global session”/>

6、自定义bean装配作用域

在spring2.0中作用域是可以任意扩展的,你可以自定义作用域,甚至你也可以重新定义已有的作用域(但是你不能覆盖singleton和 prototype),spring的作用域由接口org.springframework.beans.factory.config.Scope来定 义,自定义自己的作用域只要实现该接口即可,下面给个实例:

我们建立一个线程的scope,该scope在表示一个线程中有效,代码如下:

publicclass MyScope implements Scope {

privatefinal ThreadLocal threadScope = new ThreadLocal() {

protected Object initialValue() {

returnnew HashMap();

}

};

public Object get(String name, ObjectFactory objectFactory) {

Map scope = (Map) threadScope.get();

Object object = scope.get(name);

if(object==null) {

object = objectFactory.getObject();

scope.put(name, object);

}

return object;

}

public Object remove(String name) {

Map scope = (Map) threadScope.get();

return scope.remove(name);

}

publicvoid registerDestructionCallback(String name, Runnable callback) {

}

public String getConversationId() {

// TODO Auto-generated method stub

returnnull;

}

}

理解Tomcat的Classpath-常见问题以及如何解决(转)

 java, tomcat  理解Tomcat的Classpath-常见问题以及如何解决(转)已关闭评论
2月 282013
 

转自: http://www.linuxidc.com/Linux/2011-08/41684.htm

在很多Apache Tomcat用户论坛,一个问题经常被提出,那就是如何配置Tomcat的classpath,使得一个web应用程序能够找到类或者jar文件,从而可以正常工作。就像许多困扰Tomcat新用户的问题一样,这个问题也很容易解决。在这篇文章中,我们将会介绍Tomcat是如何产生和利用classpath的,然后一个一个解决大多数常见的与classpath相关的问题。

为什么Classpaths给Tomcat用户带来了麻烦

一个classpath就是一个参数,来告诉java虚拟机在哪里可以找到类和包去运行一个程序。classpath总是在程序源码外设置的,将其同程序分开可以允许java代码以一种抽象的方式来引用类和包,允许程序可以在任何系统上被配置。为什么那些很有经验的java用户,他们已经非常清楚classpath是什么了,但是在Tomcat运行程序的时候,还是会遇到这样那样的问题呢?
可能有以下三个原因:
1、Tomcat处理classpaths的方式与其他java程序是不同的
2、Tomcat的版本不同,其处理classpaths的方式也可能不同
3、Tomcat的文档和默认的配置要求以一种特定的方式来完成某些事情。如果你不遵循这种方式,那么你就会陷入麻烦之中。关于如何解决常见的classpath问题,没有信息可以提供,比如外部依赖,共享依赖或者多版本的相同依赖。
Tomcat的Classpath如何不同于标准的Classpath

Apache Tomcat目的是尽可能的独立,直观和自动化,为了有效的管理,标准化web应用程序的配置和部署过程,为了安全和命名控件的考虑,限制对不同库的访问。这就是为什么不使用java classpath环境变量的原因了,java的classpath是声明依赖类库默认的地方,Tomcat的start脚本忽略了这个变量,在创建Tomcat系统classloader的时候产生了自己的classpaths。
想要理解Tomcat如何处理classpath的,看看以下Tomcat6的启动过程:
1、java虚拟机的bootstrap loader加载java的核心类库。java虚拟机使用JAVA_HOME环境变量来定位核心库的位置。
2、Startup.sh,使用start参数调用Catalina.sh,重写系统的classpath并加载bootstrap.jar和tomcat-juli.jar。这些资源仅对Tomcat可见。
3、为每一个部署的Context创建Class loader,加载位于每个web应用程序WEB-INF/classes和WEB-INF/lib目录下的所有类和jar文件。每个web应用程序仅仅可见自己目录下的资源。
4、Common class loader加载位于$CATALINA_HOME/lib目录下的所有类和jar文件,这些资源对所有应用程序和Tomcat可见。
Tomcat的类加载机制是如何在不同版本之间变化的
之前的Tomcat版本,其类加载机制有一些不同

Tomcat 4.x以及更早的版本,一个server loader负责加载Catalina类,现在由commons loader负责了。
Tomcat 5.x,一个shared loader负责加载在应用程序间共享的类,这些类位于$CATALINA_HOME/shared/lib。在Tomcat6中,这种方式被取消了。
Tomcat 5.x也包括了一个Catalina loader,加载所有的Catalina组件,现在也被Common loader取代了。
当你不能按照Tomcat要求的方式做事的时候,怎么办

如果你使用Tomcat文档推荐的方式做事,你不应该有关于classpath的问题。你的wars包含了所有库和包的复本,你没有任何理由去在多个应用程序间共享一个jar文件,你不需要调用任何外在的资源,你也将不会遇到复杂的情况,例如一个web应用程序运行的时候需要一个jar文件的多个版本。但是如果你确实不能按照推荐的方式来做的时候,一个文件可以解决你所有的问题:catalina.properties。
使用catalina.properties来配置Tomcat Classpath

对于那些不想使用默认来加载方式的用户来说,幸运的是,Tomcat的classpath选项不是硬编码的,它们是从$CATALINA_HOME/conf/catalina.properties文件中读取的。
这个文件包含了除bootstrap和system loader之外的所有其他的loaders,检查这个文件,你会有一些新发现:
1、Server以及Shared loader还没有被删除,如果它们的属性没有定义,Commons loader负责处理。
2、被各种loaders加载的类和jar文件不会被自动加载,它们只是用一个简单的通配符语法指定为一组。
3、这里没有说你不能指定一个额外的仓库,事实上就是说你是可以的。
server loader不应该改动,但是shared loader还是有许多有用的应用。(shared loader将会在启动过程的最后阶段加载它的类,在Commons loader之后)。现在让我们看看一些常见的问题以及如何解决。
问题、解决方案和最佳实践

问题:我的应用程序依赖一个外部的仓库,我不能引用它。
让Tomcat知道一个外部的仓库,在catalina.properties文件的shared loader位置,使用正确的语法,声明这个仓库。语法基于你要配置的文件或仓库的类型:
1、增加一个文件夹作为类仓库,使用“path/to/foldername”
2、增加一个文件夹下的所有jar文件作为类仓库,使用”path/to/foldername/*.jar”
3、增加单个jar文件作为类仓库,使用”file://path/to/foldername/jarname.jar”
4、调用环境变量,使用${}格式,例如${VARIABLE_NAME}
5、声明多个资源,用逗号分隔开
6、所有的路径相对于CATALINA_BASE或CATALINA_HOME,或者是绝对路径
问题:我想多个应用程序共享一个jar文件,这个jar文件在Tomcat里面。
除了一些常见的第三方库(比如JDBC drivers),最好不要在$CATALINA_HOME/lib目录下包含额外的库,即使这样在一些情况下是可行的。应该重新创建比如/shared/lib和/shared/classes目录,然后在catalina.properties配置shared.loader属性:
“shared/classes,shared/lib/*.jar”
问题:除了另一个框架,我在一个应用中使用了一个嵌入式Tomcat server,当我访问框架组件的时候遇到classpath errors。
这个问题好像超出了这篇文章的范畴,但是作为一个常见的classpath相关的问题,这里对如何引起你的错误作一个简单的介绍。
当嵌入到包含另外核心框架(Wicket或者Spring)的应用中时,Tomcat将在启动框架的时候,使用System classloader加载核心类,而不是从应用的WEB-INF/lib目录下加载。
java的类加载是懒加载,就是说请求某个类的第一个加载器拥有这个类剩下的生命周期。如果System classloader,它的类对web应用是不可见的,首先加载了框架相关的类,java虚拟机将会阻止来的其他实例被创建,这样就引起了classpath错误。
解决这个问题的一种方式就是增加一个自定义的bootstrap classloader,使得这个classloader加载合适的库,然后对程序剩下部分的启动正常对待。
问题:我使用一个标准的应用程序,程序的war包含了依赖的所有包,但是我仍然遇到类定义错误。
这个问题可能是由许多事情引起的,包括编译或部署过程不是很正确,但是最有可能是web应用程序的目录结构不对造成的。
java命名转换要求类名映射到存放这个类的目录结构。例如,一个类com.mycompany.mygreat.class需要被存放到目录WEB-INF/classes/com/mycompany/。
经常代码中丢失一个点号就会引起classpath相关的问题。
问题:我的web应用程序的不同模块需要使用同一个jar包的两个不同版本。
这种情况常常出现在一个应用程序中使用多个web框架,这些web框架依赖一个库的不同版本。
有几种解决方案,但是它们都和classpath不相关。我们在这里说这个问题,是因为一些用户试图在框架的jar文件中的Manifest文件中指定依赖的库的不同版本的classpath,来解决这个问题。
这种方式在一些web应用服务器中是支持的,所以一些用户想要在Tomcat中也使用这种方式。但是在Manifest文件中指定classpath在Tomcat中是不支持的,这也不是Servlet说明的一部分。
有以下四种方式来解决这个问题:
1、你可以更新框架的版本,如果这里能够使得与其他框架依赖相同的版本。
2、你可以创建两个或更多的自定义classloaders,每个jar文件一个,在WEB/INF/context.xml文件中配置,创建你所需要的两个不同版本的类的实例。
3、你可以使用jarjar工具将框架和它依赖的库打包成一个jar文件,那么它们会一起被加载。
4、如果你发现你每隔一天就要处理这种情况,你应该考虑使用OSGI框架,这个框架有许多方法专门就是用来处理这种一个类的多个版本需要运行在同一个java虚拟机里的情况的。
最佳实践
1、避免在Tomcat里使用Commons loader加载不属于Tomcat标准发布的库和包,这可能会引起兼容错误。如果你需要在多个应用程序间共享一个库或包,创建shared/lib和shared/classes目录,然后配置到catalina.properties文件的Shared loader。
2、以上规则的一个例外就是常用的第三方共享库,例如JDBC driver。这些应该放到$CATALINA_HOME/lib目录下。
3、可能的话,尽可能按照Tomcat的开发者推荐的方式使用Tomcat,如果你发现你不得不频繁配置classpath,你可能需要重新考虑你的开发过程了。