不急不徐,持之以恒。

http://blog.gopersist.com/

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  24 随笔 :: 0 文章 :: 52 评论 :: 0 Trackbacks

#

并发编程时,必须考虑安全性问题,即线程安全,所谓线程安全就是可以同时被多个线程调用,调用者无须额外的操作,程序也不会出现错误的结果。
要使程序是线程安全的,必须考虑以下2点:
  1. 是否存在竞态条件,常见的是那些先检查后执行的操作行为,它的正确结果取决于运气。避免错误结果的方法是保证操作的原子性,通常使用加锁,也有一些原子变量类可以达到目的。
  2. 对象状态在内存中是否可见,即当一个线程修改了对象的状态后,其他线程不一定看到修改后的状态。保证其他线程总是能看到最新状态的方法有2种:一种是加锁,另一种是使用volatile变量。
于是得出几个结论:
  1. 加锁机制可保证可见性和原子性,所以能保证线程安全;
  2. 原子变量可保证原子性,但不能保证可见性,所以不能保证线程安全;
  3. volatile变量能保证可见性,但不能保证原子性,所以不能保证线程安全;
  4. volatile的原子变量能保证线程安全。
除此之外,还有一些对象一定是线程安全的:
  1. 无状态对象;
  2. 不可变对象。
但是加锁机制会产生活跃性问题,活跃性问题关注正确的行为是否一定会发生,主要有死锁问题。
死锁简单来讲是这样的:线程A持有锁L并想获得锁M,同时线程B持有锁M并想获得锁L,这时线程A和B都永久阻塞。
避免死锁的关键是要保证每个线程获取锁的顺序必须相同,如上面线程A和B获取锁的顺序如果都是先获取锁L再获取锁M,就不会发生死锁问题。
当持有锁时调用外部方法,会很难分析获取锁的顺序,要尽量避免。
编码参考:
  1. 将所有可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码进行同步;
  2. 扩展线程安全的容器类时,在新类中委托容器类的其他方法,使用新锁,不要关心原来的容器类是否线程安全。
参考资料:
《JAVA并发编程实战》
posted @ 2014-10-10 13:41 老林 阅读(1188) | 评论 (0)编辑 收藏

试用Keepalived来做双机热备,服务器信息如下:
服务器 操作系统 IP 虚拟IP
Server 1 Centos 192.168.18.20 192.168.18.22
Server 2 Centos 192.168.18.21 192.168.18.22

1. 安装Keepalived

2台Server都使用下面的命令安装Keepalived:
yum install keepalived -y

2. Server1 Keepalived 配置

$ vi /etc/keepalived/keepalived.conf

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100                   # 优先级
    advert_int 1                 # 心跳间隔(秒)
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.18.22         # 虚拟IP
    }
}

3. Server2 Keepalived 配置

$ vi /etc/keepalived/keepalived.conf

vrrp_instance VI_1 {
    state BACKUP              # 备份机
    interface eth0
    virtual_router_id 51
    priority 99                   # 优先级,比主服务器底
    advert_int 1                 # 心跳间隔(秒)
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.18.22         # 虚拟IP
    }
}

4. 启动Keepalived

$ service keepalived start
启动keepalived后,可看到2台Server都绑定了虚拟IP:
$ ip a

# Server 1:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:24:8c:8c:67:43 brd ff:ff:ff:ff:ff:ff
    inet 192.168.18.20/24 brd 192.168.18.255 scope global eth0
    inet 192.168.18.22/32 scope global eth0
    inet6 fe80::224:8cff:fe8c:6743/64 scope link 
       valid_lft forever preferred_lft forever

# Server 2:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:23:54:bf:ab:17 brd ff:ff:ff:ff:ff:ff
    inet 192.168.18.21/24 brd 192.168.18.255 scope global eth0
    inet 192.168.18.22/32 scope global eth0
    inet6 fe80::223:54ff:febf:ab17/64 scope link 
       valid_lft forever preferred_lft forever

5. 测试

浏览器访问http://192.168.18.22,出现 This is Server 1.
将192.168.18.20关闭,再访问http://192.168.18.22,出现This is Server 2.

微信订阅号:
源文地址:http://blog.gopersist.com/2014/09/24/keepalived/
posted @ 2014-09-24 11:34 老林 阅读(1668) | 评论 (2)编辑 收藏

试用IPVS的直接路由方式来做负载均衡。服务器信息如下:


 

IP配置信息如下:

服务器

操作系统

IP

IP别名

网关

调度服务器

Centos

192.168.2.90

192.168.2.99

192.168.2.1

实际服务器

Centos

192.168.2.71

192.168.2.99

192.168.2.1

Centos

192.168.2.72

192.168.2.99

192.168.2.1

 

直接路由方式工作在数据链路层,通过修改数据包的MAC地址,将数据包转发到实际服务器上。实际服务器响应时直接发送给用户端,而不经过调度器。

 

因为调度服务器并没有修改数据包的IP地址,所以我们需要为实际服务器设置与调度服务器相同的IP别名,以使实际服务器接受数据包。

 

为调度服务器设置IP别名:

ifconfig eth1:0 192.168.2.99

IP别名与原来的IP地址在使用上并没有什么不同,这里可以ping9099两个IP

 

为实际服务器设置IP别名:

ifconfig lo:0 192.168.2.99 broadcast 192.168.2.99 netmask 255.255.255.255 up

为实际服务器添加路由规则,使它不去寻找其他拥有这个IP的服务器:

route add -host 192.168.2.99 dev lo:0

防止实际服务器响应针对IP别名的ARP广播:

echo 1>/proc/sys/net/ipv4/conf/lo/arp_ignore

echo 2>/proc/sys/net/ipv4/conf/lo/arp_announce

echo 1>/proc/sys/net/ipv4/conf/all/arp_ignore

echo 2>/proc/sys/net/ipv4/conf/all/arp_announce

 

使用ipvsadm配置调度服务器:

ipvsadm -A -t 192.168.2.99:8888 -s rr

ipvsadm -a -t 192.168.2.99:8888 -r 192.168.2.71:8888 -g

ipvsadm -a -t 192.168.2.99:8888 -r 192.168.2.72:8888 -g

 

使用下面的命令将连接有效时间改为1秒来测试,:

ipvsadm --set 1 120 300

 

浏览器访问http://192.168.2.99:8888,每隔1秒多点击刷新,就会交替出现192.168.2.71192.168.2.72

 

posted @ 2014-04-28 11:15 老林 阅读(775) | 评论 (0)编辑 收藏

试用IPVS来做负载均衡,使用了1台双网卡服务器和2台单网卡服务器,2个网段。服务器信息如下:



IP配置信息如下:

服务器

操作系统

网卡

IP

调度服务器

Centos

eth0

192.168.18.58

eth1

192.168.2.90

实际服务器

Centos

eth0

192.168.2.71

Centos

eth0

192.168.2.72

 

1.         首先配置调度服务器:

 

a)         IPVS模块已经内置到linux2.6.x内核中,可以通过下面的命令查看是否已安装:

modprobe -l | grep ipvs

看到类似下面的输出,表示已经安装了

kernel/net/netfilter/ipvs/ip_vs.ko

kernel/net/netfilter/ipvs/ip_vs_rr.ko

kernel/net/netfilter/ipvs/ip_vs_wrr.ko

kernel/net/netfilter/ipvs/ip_vs_lc.ko

kernel/net/netfilter/ipvs/ip_vs_wlc.ko

kernel/net/netfilter/ipvs/ip_vs_lblc.ko

kernel/net/netfilter/ipvs/ip_vs_lblcr.ko

kernel/net/netfilter/ipvs/ip_vs_dh.ko

kernel/net/netfilter/ipvs/ip_vs_sh.ko

kernel/net/netfilter/ipvs/ip_vs_sed.ko

kernel/net/netfilter/ipvs/ip_vs_nq.ko

kernel/net/netfilter/ipvs/ip_vs_ftp.ko

kernel/net/netfilter/ipvs/ip_vs_pe_sip.ko

 

b)         安装IPVS的管理工具ipvsadm

yum install -y ipvsadm

 

c)         清除表中所有记录:

ipvsadm -C

使用下面的命令增加虚拟服务器,采用轮询调度策略:

ipvsadm -A -t 192.168.18.58:8888 -s rr

 

使用下面的命令添加实际服务器,并采用NAT方式转发数据包:

ipvsadm -a -t 192.168.18.58:8888 -r 192.168.2.71:9999 -m

ipvsadm -a -t 192.168.18.58:8888 -r 192.168.2.72:9999 -m

 

d)         打开数据包转发:

echo 1 > /proc/sys/net/ipv4/ip_forward

 

2.         接下来配置2台实际服务器,分别做以下工作:

 

a)         9999端口上启动一个web服务:

配置好web服务后,当访问http://192.168.2.71:9999时,页面返回:This is 192.168.2.71.;当访问http://192.168.2.72:9999时,页面返回:This is 192.168.2.72.

 

b)         设置默认网关指向调度服务器

route del default

route add default gw 192.168.2.90

 

3.         测试

 

访问192.168.18.58:8888,会显示This is 192.168.2.71This is 192.168.2.72,多次刷新应该要交替出现7172,但实际上并没有这样,浏览器只显示与第一次相同的内容,也就是ipvsadm每次都选择了同一台服务器。这是因为当一个TCP连接的初始SYN报文到达时,IPVS就选择了一台服务器,后继报文会被转发到相同的服务器。这个TCP连接在ipvsadm中默认有效时间为15分钟,可以通过下面的命令查看:

ipvsadm -L --timeout

Timeout (tcp tcpfin udp): 900 120 300

现在将有效时间改为1秒来测试,使用下面的命令:

ipvsadm --set 1 120 300

 

再到浏览器中每隔1秒多点击刷新,就会交替出现7172,说明轮询调度正在正常工作。

 

 

posted @ 2014-04-25 14:32 老林 阅读(1238) | 评论 (0)编辑 收藏

为了搞清楚iptables NAT的过程,做了这个实验。使用了1台双网卡服务器和1台单网卡服务器,2个网段。服务器信息如下:


IP配置信息如下:

服务器

操作系统

网卡

IP

调度服务器

Centos

eth0

192.168.18.58

eth1

192.168.2.90

实际服务器

Centos

eth0

192.168.2.73

 

1.       为了看到调度服务器上的数据转发过程,首先在调度服务器上分出内核的debug日志:

/etc/rsyslog.conf最后增加:kern.debug /var/log/iptables.log

重启日志服务:/etc/init.d/rsyslog restart

 

2.       启动调度服务器的iptables并清空规则

service iptables start

iptables -F

 

3.       增加调度服务器的iptables特定日志输出

假设要将对调度服务器8888端口的访问转发给实际服务器的9999端口处理,在iptables中增加与这2个端口相关的日志输出:

iptables -t mangle -A PREROUTING -p tcp --dport 8888 -j LOG --log-level debug --log-prefix "<<<<< PER IN:"

iptables -t mangle -A PREROUTING -p tcp --sport 9999 -j LOG --log-level debug --log-prefix "<<<<< PER IN:"

iptables -t mangle -A POSTROUTING -p tcp --sport 8888 -j LOG --log-level debug --log-prefix ">>>>> POST OUT:"

iptables -t mangle -A POSTROUTING -p tcp --dport 9999 -j LOG --log-level debug --log-prefix ">>>>> POST OUT:"

iptables -t mangle -A POSTROUTING -p tcp --sport 9999 -j LOG --log-level debug --log-prefix ">>>>> POST OUT:"

 

这时,通过浏览器访问http://192.168.18.58:8888可以看到iptables.log中打印出下面的日志:

Apr 24 16:24:35 route-server1 kernel: <<<<< PER IN:IN=eth0 OUT= MAC=00:1f:c6:cb:eb:e0:00:1f:33:de:29:ad:08:00 SRC=192.168.18.25 DST=192.168.18.58 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=28721 DF PROTO=TCP SPT=50270 DPT=8888 WINDOW=14600 RES=0x00 SYN URGP=0

Apr 24 16:24:35 route-server1 kernel: <<<<< POST OUT:IN= OUT=eth0 SRC=192.168.18.58 DST=192.168.18.25 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=8888 DPT=50270 WINDOW=0 RES=0x00 ACK RST URGP=0

 

虽然这个端口上即没有应用,也没有将请求转发出去,但日志打印出了内核获取到的对这个端口的请求。

 

4.       配置iptables将对8888的请求转发到192.168.2.73:9999

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8888 -j DNAT --to-destination 192.168.2.73:9999

 

访问http://192.168.18.58:8888,日志中打印如下信息:

Apr 24 16:39:21 route-server1 kernel: <<<<< PER IN:IN=eth0 OUT= MAC=00:1f:c6:cb:eb:e0:00:1f:33:de:29:ad:08:00 SRC=192.168.18.25 DST=192.168.18.58 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=56888 DF PROTO=TCP SPT=50274 DPT=8888 WINDOW=14600 RES=0x00 SYN URGP=0

 

日志中只打印了从eth0收到的对8888端口的请求,这是因为当数据要被转发到192.168.2.73:9999时,默认情况下被禁止了。

 

5.       打开数据包转发功能

echo 1 > /proc/sys/net/ipv4/ip_forward

 

访问http://192.168.18.58:8888,日志中打印如下信息:

Apr 24 16:39:21 route-server1 kernel: <<<<< PER IN:IN=eth0 OUT= MAC=00:1f:c6:cb:eb:e0:00:1f:33:de:29:ad:08:00 SRC=192.168.18.25 DST=192.168.18.58 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=56888 DF PROTO=TCP SPT=50274 DPT=8888 WINDOW=14600 RES=0x00 SYN URGP=0

Apr 24 16:39:21 route-server1 kernel: <<<<< POST OUT:IN= OUT=eth1 SRC=192.168.18.25 DST=192.168.2.73 LEN=60 TOS=0x00 PREC=0x00 TTL=62 ID=56888 DF PROTO=TCP SPT=50274 DPT=9999 WINDOW=14600 RES=0x00 SYN URGP=0

 

第一条日志显示从eth0收到了对8888端口的请求,第二条日志显示iptables已经更改了数据包的目的地为192.168.2.73:9999,并通过eth1发出去。

 

但这时请求虽然已经被转发到实际处理的服务器,但调度服务器收不到响应,浏览器仍在不停重试,日志也在不断打印。这是因为实际服务器收到的数据包的来源IP是另一个网段的,实际服务器回复时,发现不是本网段的就把数据包发给网关,网关设置的是192.168.2.1,这时数据就丢了。

 

6.       将实际服务器的默认网关设置为192.168.2.90

在实际服务器上执行以下命令:

route del default

route add default gw 192.168.2.90

 

再次访问http://192.168.18.58:8888,日志打印如下信息:

Apr 24 16:47:27 route-server1 kernel: <<<<< PER IN:IN=eth0 OUT= MAC=00:1f:c6:cb:eb:e0:00:1f:33:de:29:ad:08:00 SRC=192.168.18.25 DST=192.168.18.58 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=37000 DF PROTO=TCP SPT=50279 DPT=8888 WINDOW=14600 RES=0x00 SYN URGP=0

Apr 24 16:47:27 route-server1 kernel: <<<<< POST OUT:IN= OUT=eth1 SRC=192.168.18.25 DST=192.168.2.73 LEN=60 TOS=0x00 PREC=0x00 TTL=62 ID=37000 DF PROTO=TCP SPT=50279 DPT=9999 WINDOW=14600 RES=0x00 SYN URGP=0

Apr 24 16:47:27 route-server1 kernel: <<<<< PER IN:IN=eth1 OUT= MAC=00:22:b0:de:f7:49:00:24:8c:b4:a1:8c:08:00 SRC=192.168.2.73 DST=192.168.18.25 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=9999 DPT=50279 WINDOW=0 RES=0x00 ACK RST URGP=0

Apr 24 16:47:27 route-server1 kernel: <<<<< POST OUT:IN= OUT=eth0 SRC=192.168.2.73 DST=192.168.18.25 LEN=40 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=9999 DPT=50279 WINDOW=0 RES=0x00 ACK RST URGP=0

 

上面第一条第二条日志和之前一样,iptables将目地址更改后,通过eth1网卡发送出去。第三条日志通过eth1网卡接收到了实际服务器发送过来的数据,并在第四条日志中通过eth0发回请求方。

 

posted @ 2014-04-24 17:15 老林 阅读(3218) | 评论 (0)编辑 收藏

一、准备
1. 安装apache,http://httpd.apache.org/
2. 安装perl, http://www.perl.org/
3. 安装awstat, http://awstats.sourceforge.net/
awstat安装会自动进行一些配置,可能不需要下面的设置。
二、apache配置
在apache安装目录conf\httpd.conf中增加以下配置:
Alias /awstatsclasses "C:/Program Files/AWStats/wwwroot/classes/"
Alias /awstatscss "C:/Program Files/AWStats/wwwroot/wwwroot/css/"
Alias /awstatsicons "C:/Program Files/AWStats/wwwroot/icon/"
ScriptAlias /awstats/ "C:/Program Files/AWStats/wwwroot/cgi-bin/"

<Directory "C:/Program Files/AWStats/wwwroot/cgi-bin/">
    AllowOverride None
    Options ExecCGI
    Order allow,deny
    Allow from all
</Directory>

将common注释掉,放开combined的注释
#CustomLog "logs/access.log" common
CustomLog "logs/access.log" combined
三、awstat配置
1. 修改{awstats_home}/wwwroot/cgi-bin/下的awredir.plawstats.pl文件的第一行从#!/usr/bin/perl改成#!C:\strawberry\perl\bin\perl.exe。
3. 
修改{awstats_home}/wwwroot/cgi-bin/下的awstats.demo.conf
LogFile="C:\Program Files\Apache Software Foundation\Apache2.2\logs\access.log"
LogType=W
LogFormat=1
SiteDomain="demo"
四、更新报表
命令行进行{awstats_home}\wwwroot\cgi-bin
perl awstats.pl -config=demo –update
五、访问统计报表
http://localhost/awstats/awstats.pl?config=demo
注意:httpd.conf更改后要重启apache服务
posted @ 2010-11-11 23:36 老林 阅读(548) | 评论 (0)编辑 收藏

     摘要: Play Framework Eclipse 开发入门  阅读全文
posted @ 2010-10-30 13:18 老林 阅读(5568) | 评论 (4)编辑 收藏

     摘要: 一个软件实体,应当对扩展开放,对修改关闭。
要求一个软件系统可以在不修改原有代码的情况下,通过扩展达到增强其功能的目的。  阅读全文
posted @ 2010-03-30 08:44 老林 阅读(1945) | 评论 (1)编辑 收藏

     摘要: Struts2与JasperReport无缝整合,使用iReport设计报表,取JavaBean数据源、类导航取值、排序、分组、参数传递、分页...  阅读全文
posted @ 2010-03-12 23:54 老林 阅读(6149) | 评论 (8)编辑 收藏

     摘要: 之前整合的框架中struts部分有问题,现将其升级到strtus2.1.8.1,可以解决两个问题:
1. 程序工作正常但后台报错,Unable to set parameter [location] in result of type [com.googlecode.jsonplugin.JSONResult]。
2. 无法提交double为0值的问题。 ...  阅读全文
posted @ 2010-01-29 23:34 老林 阅读(958) | 评论 (0)编辑 收藏

仅列出标题
共3页: 上一页 1 2 3 下一页