清除指定squid缓存文件的脚本

Squid
web缓存加速软件目前已经是新浪、搜狐、网易等各大网站广泛应用。Squid会在设置的缓存目录下建立多个目录,每一个目录下又建立多个目录,然后才在最里层的目录中存放缓存文件(object)。squid会根据用户请求网页的URL进行哈希,生成缓存文件,存放在某一个目录中。squid启动之后,将在内存中建立一个哈希表,记录硬盘中缓存文件配置的情形。

  对于类似http://you.video.sina.com.cn/index.html
之类的网页,squid只会生成一个缓存文件。可以用squid附带的squidclient工具清除:
 

引用
squidclient -m PURGE -p 80 "http://you.video.sina.com.cn/index.html"

  而对于带有参数的网页,例如新浪播客的Flash播放器http://vhead.blog.sina.com.cn/player/outer_player.swf?auto=0&vid=4469852&uid=1278987704
,因“?”后面的参数不同,导致URL也不同,squid会生成多个缓存文件,哈希分散存放在不同的目录。如果修改了这个outer_player.swf文件,要更新squid缓存就要去清除不同目录下及内存中的很多个缓存文件,十分麻烦,于是我编写了一个Linux下的shell脚本,去完成这件麻烦的事:

  脚本文件名:clear_squid_cache.sh(8月2日修正了UC网友“城市中的寂寞”反馈的BUG)
 

引用
#!/bin/sh
squidcache_path="/data1/squid/var/cache"
squidclient_path="/usr/local/squid/bin/squidclient"
grep -a -r $1 $squidcache_path/* | strings | grep "http:" | awk -F'http:' '{print "http:"$2;}' > cache_list.txt
for url in `cat cache_list.txt`; do
$squidclient_path -m PURGE -p 80 $url
done

  注意:请赋予clear_squid_cache.sh可执行权限(命令:chmod +x ./clear_squid_cache.sh)。请确保脚本所在目录可写。

  设置:
  squidcache_path= 表示squid缓存目录的路径
  squidclient_path= 表示squidclient程序所在的路径,默认为squid安装目录下的bin/squidclient

  用法:
  1、清除所有Flash缓存(扩展名.swf):
  ./clear_squid_cache.sh swf

  2、清除URL中包含sina.com.cn的所有缓存:
  ./clear_squid_cache.sh sina.com.cn

  3、清除文件名为zhangyan.jpg的所有缓存:
  ./clear_squid_cache.sh zhangyan.jpg

  效率:
  经测试,在DELL 2950上清除26000个缓存文件用时2分钟左右。平均每秒可清除缓存文件177个。

发表在 article | 标签为 | 清除指定squid缓存文件的脚本已关闭评论

电信/联通/全国DNS列表/PUBLIC DNS

Global DNS Server List

  • 1.1.1.1 – CloudFlare
  • 2.2.2.2 – 法国电信/ Orange
  • 3.3.3.3 – 通用电气
  • 4.4.4.4 – Level3 通信
  • 5.5.5.5 – E-Plus(德国第三大的手机运营商)
  • 6.6.6.6 – 美国陆军
  • 7.7.7.7 – 未使用的理论上属于美国国防部
  • 8.8.8.8 – 谷歌
  • 9.9.9.9 – IBM

教育网DNS服务器:

北京邮电大学DNS服务器
2001:da8:202:10::36
2001:da8:202:10::37

北京科技大学DNS服务器
2001:da8:208:10::6

”Google Over IPv6”计划的DNS:
Hurricane Electric DNS

ordns.he.net 2001:470:20::2 74.82.42.42
tserv1.fmt2.he.net 2001:470:0:45::2 72.52.104.74
tserv1.dal1.he.net 2001:470:0:78::2 216.218.224.42
tserv1.ams1.he.net 2001:470:0:7d::2 216.66.84.46
tserv1.mia1.he.net 2001:470:0:8c::2 209.51.161.58
tserv1.tor1.he.net 2001:470:0:c0::2 216.66.38.58
ns.ipv6.uni-leipzig.de 2001:638:902:1::10 139.18.25.34

Google Public DNS

google-public-dns-a.google.com 2001:4860:4860::8888 8.8.8.8
google-public-dns-b.google.com 2001:4860:4860::8844 8.8.4.4

Cloudflare DNS

2606:4700:4700::1111 1.1.1.1
2606:4700:4700::1001 1.0.0.1

公众 DNS 测试:

Google 8.8.8.8 私有和未过滤,最受欢迎。
CloudFlare 1.1.1.1 私有和未过滤的新玩家。
Quad 9 9.9.9.9 私有和安全意识很高的新玩家。
OpenDNS 208.67.222.222 阻止恶意网域并提供阻止成人内容的旧玩家。
Norton DNS 199.85.126.20 阻止恶意域名并与其防病毒软件集成的旧玩家。
CleanBrowsing 185.228.168.168 私有和阻止访问成人内容,新玩家。
Yandex DNS 77.88.8.7 阻止恶意域名的旧玩家,在俄罗斯非常受欢迎。
Comodo DNS 8.26.56.26 阻止恶意域名的旧玩家

结果摘要

测试非常简单,针对不同流行的域名(google, facebook, twitter, gmail 等)在整个一小时内执行了 70 次DNS 查询。对每个位置的所有请求进行平均,以获得每个 DNS 解析程序的整体性能指标。

TLDR /摘要

  • 所有供应商(Yandex 除外)在北美和欧洲表现非常好。他们在美国,加拿大和欧洲的响应时间均小于 15 毫秒。然而,亚洲和南美洲在总体平均数上有所不同,因为一些提供商在那里连接不好。
  • CloudFlare 是所有位置中最快的 DNS。它在全球的平均值为 4.98 毫秒,令人惊叹。
  • 谷歌和 Quad9 分别接近第二和第三。Quad9 在北美和欧洲比谷歌速度快,但在亚洲/南美洲表现不佳。
  • CloudFlare 在任何地方都有强大的存在。尽管 Google 和 Quad9 在某些地方的响应时间很高,但CloudFlare 在各个地方都表现良好。
  • Yandex 仅适用于俄罗斯。它不像其他提供商那样使用 Anycast,并且在任何地方都非常缓慢。
  • CleanBrowsing 是成人(色情)内容过滤的最快速的提供商。

全球平均

#1 CloudFlare:4.98 ms
#2 Google:16.44 ms
#3 Quad9:18.25 ms
#4 CleanBrowsing:19.14 ms
#5 Norton:34.75 ms
#6 OpenDNS:46.51 ms
#7 Comodo:71.90
#8 Yandex:169.91

北美平均水平

#1 CloudFlare:3.93 ms
#2 Quad9:7.21 ms
#3 Norton:8.32 ms
#4 Google:8.53 ms
#5 CleanBrowsing:11.83 ms
#6 OpenDNS:14.66 ms
#7 Comodo:25.91 ms
#8 Yandex:119.09 ms

欧洲平均

#1 CloudFlare:2.96
#2 Quad9:4.35
#3 CleanBrowsing:5.74
#4 Google:7.17
#5 OpenDNS:8.99
#6 Norton:10.35
#7 Comodo:13.06
#8 Yandex:35.74

测试结果来自:medium

China Global DNS Server List

全国各地电信 DNS 服务器 IP 地址

名称 DNS 服务器 IP 地址
安徽电信 DNS 61.132.163.68 202.102.213.68
北京电信 DNS 219.141.136.10 219.141.140.10
重庆电信 DNS 61.128.192.68 61.128.128.68
福建电信 DNS 218.85.152.99 218.85.157.99
甘肃电信 DNS 202.100.64.68 61.178.0.93
广东电信 DNS 202.96.128.86 202.96.128.166
202.96.134.33 202.96.128.68
广西电信 DNS 202.103.225.68 202.103.224.68
贵州电信 DNS 202.98.192.67 202.98.198.167
河南电信 DNS 222.88.88.88 222.85.85.85
黑龙江电信 219.147.198.230 219.147.198.242
湖北电信 DNS 202.103.24.68 202.103.0.68
湖南电信 DNS 222.246.129.80 59.51.78.211
江苏电信 DNS 218.2.2.2 218.4.4.4
61.147.37.1 218.2.135.1
江西电信 DNS 202.101.224.69 202.101.226.68
内蒙古电信 219.148.162.31 222.74.39.50
山东电信 DNS 219.146.1.66 219.147.1.66
陕西电信 DNS 218.30.19.40 61.134.1.4
上海电信 DNS 202.96.209.133 116.228.111.118
202.96.209.5 108.168.255.118
四川电信 DNS 61.139.2.69 218.6.200.139
天津电信 DNS 219.150.32.132 219.146.0.132
云南电信 DNS 222.172.200.68 61.166.150.123
浙江电信 DNS 202.101.172.35 61.153.177.196
61.153.81.75 60.191.244.5

全国各地联通 DNS 服务器 IP 地址

名称 DNS 服务器 IP 地址
北京联通 DNS 202.106.196.115 202.106.46.151
202.106.0.20 202.106.195.68
重庆联通 DNS 221.5.203.98 221.7.92.98
广东联通 DNS 210.21.196.6 221.5.88.88
河北联通 DNS 202.99.160.68 202.99.166.4
河南联通 DNS 202.102.224.68 202.102.227.68
黑龙江联通 202.97.224.69 202.97.224.68
吉林联通 DNS 202.98.0.68 202.98.5.68
江苏联通 DNS 221.6.4.66 221.6.4.67
内蒙古联通 202.99.224.68 202.99.224.8
山东联通 DNS 202.102.128.68 202.102.152.3
202.102.134.68 202.102.154.3
山西联通 DNS 202.99.192.66 202.99.192.68
陕西联通 DNS 221.11.1.67 221.11.1.68
上海联通 DNS 210.22.70.3 210.22.84.3
四川联通 DNS 119.6.6.6 124.161.87.155
天津联通 DNS 202.99.104.68 202.99.96.68
浙江联通 DNS 221.12.1.227 221.12.33.227
辽宁联通 DNS 202.96.69.38 202.96.64.68

全国各地移动 DNS 服务器 IP 地址

名称 DNS 服务器 IP 地址
江苏移动 DNS 221.131.143.69 112.4.0.55
安徽移动 DNS 211.138.180.2 211.138.180.3
山东移动 DNS 218.201.96.130 211.137.191.26

Apple TV DNS 服务器 IP 地址

名称 DNS 服务器 IP 地址
上海电信 180.153.225.136
杭州电信 115.29.189.118
广东电信 203.195.182.150
北方联通 118.244.224.124

全国电信DNS列表

58.217.249.160 58.217.249.161 江苏省南京市 南京信风网络科技有限公司DNS服务器电信节点
59.51.78.210 59.51.78.211 湖南省 电信DNS服务器
60.191.244.5 60.191.244.5 浙江省金华市 电信DNS服务器
60.207.196.198 60.207.196.198 北京市 电信通DNS服务器
61.128.114.133 61.128.114.134 新疆乌鲁木齐市 电信DNS服务器
61.128.114.166 61.128.114.167 新疆乌鲁木齐市 电信DNS服务器
61.128.128.68 61.128.128.68 重庆市 电信DNS服务器
61.128.192.68 61.128.192.68 重庆市 电信DNS服务器
61.130.254.34 61.130.254.36 浙江省湖州市 电信DNS服务器
61.132.163.68 61.132.163.68 安徽省 电信DNS服务器
61.134.1.4 61.134.1.4 陕西省西安市 电信DNS
61.134.1.5 61.134.1.5 陕西省西安市 电信DNS服务器
61.139.2.69 61.139.2.69 四川省成都市 电信DNS服务器
61.139.39.73 61.139.39.73 四川省泸州市 电信DNS服务器
61.139.54.66 61.139.54.66 四川省凉山州西昌市 电信DNS服务器
61.147.37.1 61.147.37.1 江苏省徐州市 电信DNS服务器
61.166.150.101 61.166.150.101 云南省昆明市 电信DNS服务器
61.166.150.123 61.166.150.123 云南省 电信DNS服务器
61.166.150.139 61.166.150.139 云南省昆明市 电信DNS服务器
61.177.7.1 61.177.7.1 江苏省苏州市 电信DNS服务器
61.187.98.3 61.187.98.3 湖南省株洲市 电信DNS服务器
61.187.98.6 61.187.98.6 湖南省株洲市 电信DNS服务器
67.138.116.2 67.138.116.2 美国 俄勒冈州波特兰市Integra电信股份有限公司DNS服务器
76.10.67.2 76.10.67.2 美国 北达科他州卡斯县法戈市IdeaOne电信公司DNS服务器
76.14.0.8 76.14.0.9 美国 北达科他州卡斯县法戈市IdeaOne电信公司DNS服务器
76.14.96.13 76.14.96.14 美国 北达科他州卡斯县法戈市IdeaOne电信公司DNS服务器
76.14.192.8 76.14.192.9 美国 北达科他州卡斯县法戈市IdeaOne电信公司DNS服务器
106.59.255.234 106.59.255.239 云南省昆明市 电信DNS服务器
106.59.255.241 106.59.255.242 云南省昆明市 电信DNS服务器
112.100.100.100 112.100.100.100 黑龙江省 电信DNS
113.111.211.22 113.111.211.22 广东省广州市 电信DNS服务器
116.228.111.18 116.228.111.18 上海市 电信DNS服务器
116.228.111.118 116.228.111.118 上海市 电信DNS服务器
118.118.118.1 118.118.118.1 四川省雅安市 电信DNS服务器
118.123.202.215 118.123.202.218 四川省成都市 阿里巴巴(中国)有限公司阿里公共DNS电信节点
119.84.104.215 119.84.104.218 重庆市 阿里巴巴(中国)有限公司阿里公共DNS电信节点
124.207.160.106 124.207.160.106 北京市 电信通DNS服务器
124.207.160.110 124.207.160.110 北京市 电信通DNS服务器
143.90.130.11 143.90.130.11日本 软银电信公司DNS服务器
143.90.130.166 143.90.130.166 日本 软银电信公司DNS服务器
165.21.83.88 165.21.83.88 新加坡 新加坡电信数字媒体私人有限公司DNS服务器
165.21.83.100 165.21.83.100 新加坡 新加坡电信数字媒体私人有限公司DNS服务器
165.21.100.88 165.21.100.88 新加坡 新加坡电信数字媒体私人有限公司DNS服务器
168.126.63.1 168.126.63.2 韩国 KT电信DNS服务器
180.168.255.18 180.168.255.18 上海市 电信DNS服务器
202.96.96.68 202.96.96.68 浙江省杭州市 电信DNS服务器
202.96.103.36 202.96.103.36 浙江省 电信DNS服务器
202.96.104.15 202.96.104.16 浙江省宁波市 电信DNS服务器
202.96.104.26 202.96.104.26 浙江省宁波市 电信DNS服务器
202.96.107.27 202.96.107.29 浙江省绍兴市 电信DNS服务器
202.96.128.68 202.96.128.68 广东省广州市 电信DNS服务器
202.96.128.86 202.96.128.86 广东省广州市 电信DNS服务器
202.96.128.166 202.96.128.166 广东省广州市 电信DNS服务器
202.96.134.33 202.96.134.33 广东省深圳市 电信DNS服务器
202.96.134.133 202.96.134.133 广东省深圳市 电信DNS服务器
202.96.144.47 202.96.144.47 广东省汕头市 电信DNS服务器
202.96.154.15 202.96.154.15 广东省深圳市 电信DNS服务器
202.96.209.5 202.96.209.6 上海市 电信DNS服务器
202.96.209.133 202.96.209.133 上海市 电信DNS服务器
202.98.96.68 202.98.96.68 四川省成都市 电信DNS服务器
202.98.192.67 202.98.192.67 贵州省 电信DNS服务器
202.98.198.167 202.98.198.167 贵州省 电信DNS服务器
202.98.224.68 202.98.224.68 西藏拉萨市 电信DNS服务器
202.100.96.68 202.100.96.68 宁夏银川市 电信DNS服务器
202.100.192.68 202.100.192.68 海南省 电信DNS服务器
202.100.199.8 202.100.199.8 海南省三亚市 电信DNS服务器
202.101.6.2 202.101.6.2 上海市 电信DNS服务器
202.101.98.55 202.101.98.55 福建省福州市 电信DNS服务器
202.101.107.85 202.101.107.85 福建省泉州市 电信DNS服务器
202.101.172.35 202.101.172.35 浙江省杭州市 电信DNS服务器
202.101.172.47 202.101.172.47 浙江省杭州市 电信DNS服务器
202.101.224.68 202.101.224.71 江西省 电信DNS服务器
202.101.226.68 202.101.226.69 江西省九江市 电信DNS服务器
202.102.3.141 202.102.3.141 江苏省常州市 电信DNS服务器
202.102.3.144 202.102.3.144 江苏省常州市 电信DNS服务器
202.102.7.90 202.102.7.90 江苏省扬州市 电信DNS服务器
202.102.8.141 202.102.8.141 江苏省南通市 电信DNS服务器
202.102.199.68 202.102.199.68 安徽省芜湖市 电信DNS服务器
202.102.200.101 202.102.200.101 安徽省蚌埠市 电信DNS服务器
202.102.213.68 202.102.213.68 安徽省 电信DNS服务器
202.103.0.68 202.103.0.68 湖北省武汉市 电信DNS服务器
202.103.0.117 202.103.0.117 湖北省武汉市 电信DNS服务器
202.103.24.68 202.103.24.68 湖北省武汉市 电信DNS服务器
202.103.44.150 202.103.44.150 湖北省武汉市 电信DNS服务器
202.103.176.22 202.103.176.22 广东省茂名市 电信DNS服务器
202.103.224.68 202.103.224.68 广西南宁市 电信DNS服务器
202.103.225.68 202.103.225.68 广西柳州市 电信DNS服务器
210.200.211.193 210.200.211.193 台湾省 亚太电信股份有限公司DNS服务器
210.200.211.225 210.200.211.225 台湾省 亚太电信股份有限公司DNS服务器
211.147.6.3 211.147.6.4 北京市 电信通DNS服务器
211.167.242.34 211.167.242.34 北京市 电信通DNS服务器
216.8.196.88 216.8.196.89 美国 印第安纳州里普利县亚当斯地区桑曼镇Enhanced电信集团公司DNS服务器
216.8.209.88 216.8.209.88 美国 印第安纳州里普利县亚当斯地区桑曼镇Enhanced电信集团公司DNS服务器
216.8.228.88 216.8.228.88 美国 印第安纳州里普利县亚当斯地区桑曼镇Enhanced电信集团公司DNS服务器
218.2.2.2 218.2.2.2 江苏省淮安市 电信DNS服务器
218.2.135.1 218.2.135.1 江苏省南京市 电信DNS服务器
218.4.4.4 218.4.4.4 江苏省苏州市 电信DNS服务器
218.6.200.139 218.6.200.139 四川省成都市 电信DNS服务器
218.30.19.50 218.30.19.50 陕西省西安市 电信DNS服务器
218.76.192.100 218.76.192.101 湖南省邵阳市 电信DNS服务器
218.85.152.99 218.85.152.99 福建省福州市 电信DNS服务器
218.85.157.99 218.85.157.99 福建省福州市 电信DNS服务器
218.89.0.124 218.89.0.124 四川省乐山市 电信DNS服务器
219.141.136.10 219.141.136.10 北京市 电信DNS服务器
219.141.140.10 219.141.140.10 北京市 电信DNS服务器
219.141.148.37 219.141.148.37 北京市 电信DNS服务器
219.141.148.39 219.141.148.39 北京市 电信DNS服务器
219.146.0.130 219.146.0.130 山东省济南市 电信DNS服务器
219.146.1.66 219.146.1.66 山东省济南市 电信DNS服务器
219.147.198.230 219.147.198.230 黑龙江省齐齐哈尔市 电信DNS服务器
219.148.204.66 219.148.204.66 辽宁省沈阳市 电信DNS服务器
219.149.6.99 219.149.6.99 辽宁省大连市 电信DNS服务器
219.149.194.55 219.149.194.56 吉林省长春市 电信DNS服务器
219.150.32.132 219.150.32.132 天津市 电信DNS服务器
219.239.26.42 219.239.26.42 北京市 电信通DNS服务器
220.168.208.3 220.168.208.3 湖南省常德市 电信DNS服务器
220.168.208.6 220.168.208.6 湖南省常德市 电信DNS服务器
220.170.64.68 220.170.64.68 湖南省衡阳市 电信DNS服务器
220.181.70.210 220.181.70.210 北京市 电信互联网数据中心公众DNS服务器
220.187.24.2 220.187.24.2 浙江省舟山市 电信DNS服务器
220.187.24.6 220.187.24.6 浙江省舟山市 电信DNS服务器
221.228.255.1 221.228.255.1 江苏省无锡市 电信DNS服务器
221.232.129.30 221.232.129.30 湖北省武汉市 电信DNS服务器
222.75.152.129 222.75.152.129 宁夏银川市 电信DNS服务器
222.85.85.85 222.85.85.85 河南省 电信DNS服务器
222.88.88.88 222.88.88.88 河南省洛阳市 电信DNS服务器
222.172.200.68 222.172.200.68 云南省 电信DNS服务器
222.221.0.9 222.221.0.12 云南省昆明市 电信DNS服务器
222.221.5.240 222.221.5.241 云南省昆明市 云南电信公众信息产业有限公司DNS服务器
222.222.222.222 222.222.222.222 河北省石家庄市 电信DNS
222.243.129.81 222.243.129.81 湖南省 电信DNS服务器
222.246.129.80 222.246.129.81 湖南省 电信DNS服务器

全国联通DNS列表

58.22.96.66 58.22.96.66 福建省 联通DNS服务器
58.240.57.33 58.240.57.33 江苏省南京市 联通DNS服务器
58.241.208.46 58.241.208.46 江苏省盐城市 联通DNS服务器
58.242.2.2 58.242.2.2 安徽省合肥市 联通DNS服务器
60.12.166.166 60.12.166.166 浙江省金华市 联通DNS服务器
61.135.164.13 61.135.164.18 北京市 联通DNS服务器
113.207.20.15 113.207.20.18 重庆市 阿里巴巴(中国)有限公司公共DNS服务器联通节点
119.6.6.6 119.6.6.6 四川省成都市 联通DNS服务器
123.125.81.6 123.125.81.6 北京市 上海聚流软件科技有限公司公共DNS服务器联通节点
124.161.97.234 124.161.97.234 四川省 联通DNS服务器
124.161.97.238 124.161.97.238 四川省 联通DNS服务器
124.161.97.242 124.161.97.242 四川省 联通DNS服务器
140.207.198.6 140.207.198.6 上海市 上海聚流软件科技有限公司公共DNS服务器联通节点
202.96.64.68 202.96.64.68 辽宁省沈阳市 联通DNS服务器
202.96.69.38 202.96.69.38 辽宁省大连市 联通DNS服务器
202.96.75.68 202.96.75.68 辽宁省沈阳市 联通DNS服务器
202.96.86.18 202.96.86.18 辽宁省抚顺市 联通DNS服务器
202.97.224.68 202.97.224.69 黑龙江省 联通DNS服务器
202.98.0.68 202.98.0.68 吉林省长春市 联通DNS服务器
202.98.1.11 202.98.1.11 吉林省长春市 联通DNS服务器
202.98.5.68 202.98.5.68 吉林省长春市 联通DNS服务器
202.99.96.68 202.99.96.68 天津市 联通DNS
202.99.104.68 202.99.104.68 天津市 联通DNS
202.99.160.68 202.99.160.68 河北省 联通DNS服务器
202.99.166.4 202.99.166.4 河北省 联通DNS服务器
202.99.168.8 202.99.168.8 河北省保定市 联通DNS服务器
202.99.192.66 202.99.192.66 山西省太原市 联通DNS服务器
202.99.192.68 202.99.192.68 山西省太原市 联通DNS服务器
202.99.224.8 202.99.224.8 内蒙古呼和浩特市 联通DNS服务器(netcool)
202.99.224.67 202.99.224.67 内蒙古呼和浩特市 联通DNS服务器(cns)
202.99.224.68 202.99.224.68 内蒙古呼和浩特市 联通DNS服务器(nmdns)
202.102.134.68 202.102.134.70 山东省青岛市 联通DNS服务器
202.102.152.3 202.102.152.3 山东省济南市 联通DNS服务器
202.102.154.3 202.102.154.3 山东省济南市 联通DNS服务器
202.102.224.68 202.102.224.68 河南省 联通DNS服务器
202.102.227.68 202.102.227.68 河南联通xDSL DNS
202.106.0.20 202.106.0.20 北京市 联通DNS服务器
202.106.46.151 202.106.46.151 北京市 联通DNS服务器
202.106.196.233 202.106.196.234 北京市 联通DNS服务器
210.21.196.6 210.21.196.6 广东省深圳市 联通DNS服务器
210.22.70.3 210.22.70.3 上海市 联通DNS服务器
211.90.72.65 211.90.72.65 内蒙古呼和浩特市 联通DNS服务器
211.90.80.65 211.90.80.65 山西省太原市 联通DNS服务器
211.91.88.129 211.91.88.129 安徽省合肥市 联通DNS服务器
211.92.136.81 211.92.136.81 贵州省贵阳市 联通DNS服务器
211.92.144.161 211.92.144.161 云南省昆明市 联通DNS服务器
211.93.0.81 211.93.0.81 宁夏银川市 联通DNS服务器
211.93.24.129 211.93.24.129 黑龙江省哈尔滨市 联通DNS服务器
211.93.64.129 211.93.64.129 吉林省 联通DNS服务器(全省通用)
211.95.1.97 211.95.1.97 上海市 联通DNS服务器
211.95.72.1 211.95.72.1 上海市 联通DNS服务器
211.95.193.97 211.95.193.97 广东省广州市 联通DNS服务器
211.97.64.129 211.97.64.130 广西南宁市 联通DNS服务器
211.97.96.65 211.97.96.65 海南省海口市 联通DNS服务器
218.24.249.2 218.24.249.2 辽宁省盘锦市 联通DNS服务器
218.24.249.18 218.24.249.18 辽宁省盘锦市 联通DNS服务器
218.29.4.227 218.29.4.227 河南省南阳市 联通DNS服务器
218.29.4.249 218.29.4.249 河南省南阳市 联通DNS服务器
218.29.5.148 218.29.5.148 河南省南阳市 联通DNS服务器
218.29.7.233 218.29.7.233 河南省南阳市 联通DNS服务器
218.29.12.166 218.29.12.166 河南省南阳市 联通DNS服务器
218.29.39.19 218.29.39.19 河南省南阳市 联通DNS服务器
218.29.42.233 218.29.42.233 河南省南阳市 联通DNS服务器
218.29.72.22 218.29.72.22 河南省郑州市 联通DNS服务器
218.29.90.149 218.29.90.149 河南省郑州市 联通DNS服务器
218.29.106.250 218.29.106.250 河南省郑州市 联通DNS服务器
218.29.117.126 218.29.117.126 河南省郑州市 联通DNS服务器
218.29.122.70 218.29.122.70 河南省郑州市 联通DNS服务器
218.29.156.138 218.29.156.138 河南省三门峡市 联通DNS服务器
218.29.172.149 218.29.172.149 河南省濮阳市 联通DNS服务器
218.29.174.166 218.29.174.166 河南省濮阳市 联通DNS服务器
218.29.188.210 218.29.188.210 河南省郑州市 联通DNS服务器
218.29.188.243 218.29.188.243 河南省郑州市 联通DNS服务器
218.29.193.118 218.29.193.118 河南省郑州市 联通DNS服务器
218.29.203.18 218.29.203.18 河南省郑州市 联通DNS服务器
218.29.209.106 218.29.209.106 河南省郑州市 联通DNS服务器
218.29.222.108 218.29.222.108 河南省郑州市新密市 联通DNS服务器
218.29.223.150 218.29.223.150 河南省郑州市新密市 联通DNS服务器
218.29.228.70 218.29.228.70 河南省郑州市巩义市 联通DNS服务器
218.29.236.14 218.29.236.14 河南省郑州市 联通DNS服务器
218.29.237.82 218.29.237.82 河南省郑州市 联通DNS服务器
218.29.254.154 218.29.254.154 河南省漯河市 联通DNS服务器
218.104.32.106 218.104.32.106 江苏省苏州市 联通DNS服务器
218.104.78.2 218.104.78.2 安徽省合肥市 联通DNS服务器
218.104.111.114 218.104.111.114 湖北省武汉市 联通DNS服务器
218.104.111.122 218.104.111.122 湖北省武汉市 联通DNS服务器
218.104.128.106 218.104.128.106 福建省 联通DNS服务器
220.248.192.12 220.248.192.13 江西省南昌市 联通DNS服务器
221.3.131.11 221.3.131.12 云南省 联通DNS服务器
221.4.66.66 221.4.66.66 广东省广州市 联通DNS服务器
221.5.88.88 221.5.88.88 广东省肇庆市 联通DNS服务器
221.5.203.86 221.5.203.86 重庆市 联通DNS服务器
221.5.203.90 221.5.203.90 重庆市 联通DNS服务器
221.5.203.98 221.5.203.99 重庆市 联通DNS服务器
221.6.4.66 221.6.4.67 江苏省南京市 联通DNS服务器
221.7.1.20 221.7.1.21 新疆 联通DNS服务器
221.7.34.10 221.7.34.10 甘肃省兰州市 联通DNS服务器
221.7.92.86 221.7.92.86 重庆市 联通DNS服务器
221.7.92.98 221.7.92.98 重庆市 联通DNS服务器
221.7.128.68 221.7.128.68 广西南宁市 联通DNS服务器
221.7.136.68 221.7.136.68 广西柳州市 联通DNS服务器
221.11.132.2 221.11.132.2 海南省海口市 联通DNS服务器
221.12.1.226 221.12.1.228 浙江省杭州市 联通DNS服务器
221.12.33.227 221.12.33.228 浙江省宁波市 联通DNS服务器
221.12.65.227 221.12.65.228 浙江省温州市 联通DNS服务器
发表在 article | 标签为 , | 电信/联通/全国DNS列表/PUBLIC DNS已关闭评论

常用正则表达式

 

表达式全集

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\\”匹配“\”而“\(”则匹配“(”。
^ 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo*能匹配“z”以及“zoo”。*等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。
{n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。
. 匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(.|\n)”的模式。
(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。
(?:pattern) 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
(?=pattern) 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
(?<=pattern) 反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。
(?<!pattern) 反向否定预查,与正向否定预查类拟,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
x|y 匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。
[xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
\B 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\cx 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
\d 匹配一个数字字符。等价于[0-9]。
\D 匹配一个非数字字符。等价于[^0-9]。
\f 匹配一个换页符。等价于\x0c和\cL。
\n 匹配一个换行符。等价于\x0a和\cJ。
\r 匹配一个回车符。等价于\x0d和\cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于[^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于\x09和\cI。
\v 匹配一个垂直制表符。等价于\x0b和\cK。
\w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。
\W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
\xn 匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。.
\num 匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。
\nml 如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
\un 匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。

 

常用正则表达式

用户名 /^[a-z0-9_-]{3,16}$/
密码 /^[a-z0-9_-]{6,18}$/
十六进制值 /^#?([a-f0-9]{6}|[a-f0-9]{3})$/
电子邮箱 /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
/^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/
URL /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
IP 地址 /((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
HTML 标签 /^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/
删除代码\\注释 (?<!http:|\S)//.*$
Unicode编码中的汉字范围 /^[\u2E80-\u9FFF]+$/

 

正则表达式是一种通用的标准,大部分计算机语言都支持正则表达式,包括as3,这里转摘出了一些常用的正则表达式语句,大家用到的时候就不用自己写了

^\d+$  //匹配非负整数(正整数 + 0)
^[0-9]*[1-9][0-9]*$  //匹配正整数
^((-\d+)|(0+))$  //匹配非正整数(负整数 + 0)
^-[0-9]*[1-9][0-9]*$  //匹配负整数
^-?\d+$    //匹配整数
^\d+(\.\d+)?$  //匹配非负浮点数(正浮点数 + 0)
^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$  //匹配正浮点数
^((-\d+(\.\d+)?)|(0+(\.0+)?))$  //匹配非正浮点数(负浮点数 + 0)
^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$  //匹配负浮点数
^(-?\d+)(\.\d+)?$  //匹配浮点数
^[A-Za-z]+$  //匹配由26个英文字母组成的字符串
^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串
^[a-z]+$  //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串
^\w+$  //匹配由数字、26个英文字母或者下划线组成的字符串
^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$    //匹配email地址
^[a-zA-z]+://匹配(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$  //匹配url

匹配中文字符的正则表达式: [\u4e00-\u9fa5]
匹配双字节字符(包括汉字在内):[^\x00-\xff]
匹配空行的正则表达式:\n[\s| ]*\r
匹配HTML标记的正则表达式:/<(.*)>.*<\/>|<(.*) \/>/
匹配首尾空格的正则表达式:(^\s*)|(\s*$)
匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
匹配网址URL的正则表达式:^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
匹配国内电话号码:(\d{3}-|\d{4}-)?(\d{8}|\d{7})?
匹配腾讯QQ号:^[1-9]*[1-9][0-9]*$

 

 

发表在 article | 标签为 | 92条评论

windows下memcached的安装

Memcached是什么:
Memcached是高性能的,分布式的内存对象缓存系统 ,用于在动态应用中减少数据

负载 ,提升访问速度。
Memcached由Danga Interactive开发,用于提升LiveJournal.com访问速度的。LJ

每秒动态页面访问量几千次,用户700万。Memcached将数据库 负载大幅度降低,

更好的分配资源,更快速访问。
首先我在windows下实现它,过后再在linux试试.
下载memcached-win32 和php_memcache.dll(要和php的版本对应上)
1.memcahced下载后,压缩之前发现不到100K,压缩后也不到200K,这东西居然有这

么神奇,放到C盘,进入目录里面有一个memcached.exe,双击就启动了,让窗口开着,

或者在cmd里面c:\memcached\memcached.exe -d start 都可以启动
2.修改php.ini的配制文件
加入extension=php_memcache.dll 这一行代码,位置无所谓
3.php_memcache.dll放到php的安装文件中,一般在php源码的ext目录下
4.重启apache后,查看一下phpinfo(写一个phpinfo()函数就可以看到),如果有

memcache,那么就说明安装成功
开始测试一下代码
$mem = new Memcache;
$mem->connect(”127.0.0.1″, 11211);
$mem->set(’key’, ‘This is a memcached test!’, 0, 60);
$val = $mem->get(’key’);
echo $val;
如果输出This is a memcached test!刚表明安装成功

--------------------------------------------------------------------------------
也在本地做了一下测试,用的是windows系统
1.下载Memcache for win32

下载地址: http://jehiah.cz/projects/memcached-win32/
选择memcached 1.2.1 for Win32 binaries (Dec 23, 2006) 是exe的程序

2.下载php_memcache.dll

下载地址: http://pecl4win.php.net/ext.php/php_memcache.dll
选择和Php版本对应的,我php版本是5.2.6的,里边没有,索性down了个5.2.1的也能用

打开php.ini文件,添加扩展extension=php_memcache.dll(没有分号)
重启apache
phpinfo看一下,现在应该有memcached的了

memcache主要应用方法:
(1) Memcache::getVersion 返回memcache的版本信息.

(2) Memcache::connect 创建一个memcache连接对象.

(3) Memcache::pconnect 创建一个memcacher持久连接对象.

(4) Memcache::close 关闭一个Memcache对象.

(5) Memcache::set 用来添加一个值.
有四个参数,第一个参数是key,第二个参数是value,第三个参数可选,表示是否压缩保存,第四个参数可选,用来设

置一个过期自动销毁的时间.

(6) Memcache::add 作用和Memcache::set方法类似.
两个方法的区别是如果Memcache::add方法的返回值为false,表示这个key已经存在,而Memcache::set方法则会直

接覆写.

(7) Memcache::get 用来获取一个值.
只有一个参数(key,在Memcache::set时设置)

(8) Memcache::replace 对一个已有的key进行覆写操作.
有四个参数,与 Memcache::set 相同.

(9) Memcache::increment 对保存的某个key中的值进行加法操作.

(10) Memcache::decremen 对保存的某个key中的值进行减法操作.

(11) Memcache::setCompressThreshold 对大于某一大小的数据进行压缩。

(12) Memcache::delete 删除一个key
有两个参数,第一个是key名称.第二个是删除延迟时间

(13) Memcache::flush 清除所有缓存的数据,但是不会削去使用的内存空间.

(14) Memcache::addServer 添加一个可供使用的服务器地址.

(15) Memcache::setServerParams 在运行时修改服务器的参数.
写个php程序测试一下:

复制PHP 内容到剪贴板

PHP代码:


<?php
    
/*创建memcached对象*/
    
$mem  = new  Memcache ();
    
/*创建一个链接,memcache默认端口是11211*/
    
$mem -> connect ( '127.0.0.1' , 11211 );
    
/*查询数据*/
    
$sql  "select `username` from `hao_users` where 1=1 limit 5000" ;
    
/*缓存键*/
    
$key  md5 ( $sql );
    if(!(
$mem -> get ( $key )))
    {
        
$link  mysql _connect ( "219.153.41.215" , 'root' , '**' );
        
mysql_select_db ( 'oldhaocpc' );
        
$res  mysql_query ( $sql );
        while(
$row  mysql_fetch_array ( $res ))
        {
            
$arrdata [] =  $row ;
        }
        
$mem -> set ( $key , $arrdata );
    }
    
$data  $mem -> get ( $key )? $mem -> get ( $key ): $arrdata ;
    
/*输出测试*/
    
echo  '<pre>' ;
    
print_r ( $data );
    
/*关闭memcached连接*/
    
$mem -> close ();
?>

 

为了做测试,连接的是远程数据库 。看到的效果比较明显。

 

---------------------------------------------------------------------

要想在windows中使用memcached,必须先下载memcached for win32安装。

 

下载Windows的Server端

下载地址:http://code.jellycan.com/memcached/

安装Memcache Server(也可以不安装直接启动)

1. 下载memcached的windows稳定版,解压放某个盘下面,比如在c:\memcached
2. 在CMD下输入 "c:\memcached\memcached.exe -d install" 安装.
3. 再输入:"c:\memcached\memcached.exe -d start" 启动。NOTE: 以后memcached将作为windows的一个服务每次开机时自动启动。这样服务器端已经安装完毕了。

如果下载的是二进制的版本,直接运行就可以了,可以加上参数来加以设置。

常用设置:
-p <num>          监听的端口
-l <ip_addr>      连接的IP地址, 默认是本机
-d start          启动memcached服务
-d restart        重起memcached服务
-d stop|shutdown  关闭正在运行的memcached服务
-d install        安装memcached服务
-d uninstall      卸载memcached服务
-u <username>     以<username>的身份运行 (仅在以root运行的时候有效)
-m <num>          最大内存使用,单位MB。默认64MB
-M                内存耗尽时返回错误,而不是删除项
-c <num>          最大同时连接数,默认是1024
-f <factor>       块大小增长因子,默认是1.25
-n <bytes>        最小分配空间,key+value+flags默认是48
-h                显示帮助

 

启动该服务后,memcached服务默认占用的端口是11211,占用的最大内存默认是64M。

  在修改这2个配置选项就碰到了一些问题,网上搜索了很多资料,都说使用下面的命令启动服务就可以:

  c:memcachedmemcached.exe -p 12345 -m 1024 -d start  ,  -p 表示要修改的端口, -m表示占用的最大内存(单位为M)。

  但是无论怎么调用这个命令,发现端口一直还是11211。

  打开windows服务控制面板一看,发现memcached.exe 默认安装的服务器启动参数中根本没写-p -m的参数,只有1个 -d runservice参数。

  所以不管用什么命令启动服务都是没用的,见下图:

点击查看原图

  于是就想到直接修改windows服务的启动参数,操作如下,打开注册表,找到:

  HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\memcached Server

  其中的ImagePath项的值为:"c:memcachedmemcached.exe" -d runservice

  改成:"c:memcachedmemcached.exe" -p 12345 -m 128 -d runservice

  保存后重新启动memcached服务,然后在命令行中输入netstat -n -a  看看现在端口是不是改啦,^_^。

 

 

 

 

发表在 article | 标签为 | 29条评论

多服务器session共享之mysql共享

一、PHP SESSION 的工作原理

在解决问题之前,先来了解一下 PHP SESSION 的工作原理。在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ID(此 ID 可通过函数 session_id() 获取/设置)。SESSION ID 可以通过两种方式保留在客户端,使得请求不同的页面时,PHP 程序可以获知客户端的 SESSION ID;一种是将 SESSION ID 自动加入到 GET 的 URL 中,或者 POST 的表单中,默认情况下,变量名为 PHPSESSID;另一种是通过 COOKIE,将 SESSION ID 保存在 COOKIE 中,默认情况下,这个 COOKIE 的名字为 PHPSESSID。这里我们主要以 COOKIE 方式进行说明,因为应用比较广泛。

那么 SESSION 的数据保存在哪里呢?当然是在服务器端,但不是保存在内存中,而是保存在文件或数据库中。默认情况下,php.ini 中设置的 SESSION 保存方式是 files(session.save_handler = files),即使用读写文件的方式保存 SESSION 数据,而 SESSION 文件保存的目录由 session.save_path 指定,文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的数据即是序列化之后的 SESSION 数据了。如果访问量大,可能产生的 SESSION 文件会比较多,这时可以设置分级目录进行 SESSION 文件的保存,效率会提高很多,设置方法为:session.save_path=”N;/save_path”,N 为分级的级数,save_path 为开始目录。当写入 SESSION 数据的时候,PHP 会获取到客户端的 SESSION_ID,然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件,不存在则创建之,最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程,对读出来的数据需要进行解序列化,生成相应的 SESSION 变量。

二、多服务器共享 SESSION 的主要障碍及解决办法

通过了解 SESSION 的工作原理,我们可以发现,在默认情况下,各个服务器会各自分别对同一个客户端产生 SESSION ID,如对于同一个用户浏览器,A 服务器产生的 SESSION ID 是 30de1e9de3192ba6ce2992d27a1b6a0a,而 B 服务器生成的则是 c72665af28a8b14c0fe11afe3b59b51b。另外,PHP 的 SESSION 数据都是分别保存在本服务器的文件系统中。如下图所示:

 

确定了问题所在之后,就可以着手进行解决了。想要共享 SESSION 数据,那就必须实现两个目标:一个是各个服务器对同一个客户端产生的 SESSION ID 必须相同,并且可通过同一个 COOKIE 进行传递,也就是说各个服务器必须可以读取同一个名为 PHPSESSID 的 COOKIE;另一个是 SESSION 数据的存储方式/位置必须保证各个服务器都能够访问到。简单地说就是多服务器共享客户端的 SESSION ID,同时还必须共享服务器端的 SESSION 数据。

第一个目标的实现其实很简单,只需要对 COOKIE 的域(domain)进行特殊地设置即可,默认情况下,COOKIE 的域是当前服务器的域名/IP 地址,而域不同的话,各个服务器所设置的 COOKIE 是不能相互访问的,如 www.aaa.com

的服务器是不能读写 www.bbb.com

服务器设置的 COOKIE 的。这里我们所说的同一网站的服务器有其特殊性,那就是他们同属于同一个一级域,如:aaa.infor96.com 和 www.infor96.com

都属于域 .infor96.com,那么我们就可以设置 COOKIE 的域为 .infor96.com,这样 aaa.infor96.com、www.infor96.com

等等都可以访问此 COOKIE。PHP 代码中的设置方法如下:

<?php
ini_set(’session.cookie_domain’, ‘.infor96.com’);
?>

这样各个服务器共享同一客户端 SESSION ID 的目的就达到了。

第二个目标的实现可以使用文件共享方式,如 NFS 方式,但设置、操作上有些复杂。我们可以参考先前所说的统一用户系统的方式,即使用数据库来保存 SESSION 数据,这样各个服务器就可以方便地访问同一个数据源,获取相同的 SESSION 数据了。

解决办法如下图所示:

三、代码实现

首先创建数据表,MySQL 的 SQL 语句如下:

   CREATE TABLE `sess` (
     `sesskey` varchar(32) NOT NULL default ”,
      `expiry` bigint(20) NOT NULL default ‘0′,
      `data` longtext NOT NULL,
      PRIMARY KEY  (`sesskey`),
      KEY `expiry` (`expiry`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

sesskey 为 SESSION ID,expiry 为 SESSION 过期时间,data 用于保存 SESSION 数据。

默认情况下 SESSION 数据是以文件方式保存,想要使用数据库方式保存,就必须重新定义 SESSION 各个操作的处理函数。PHP 提供了session_set_save_handle() 函数,可以用此函数自定义 SESSION 的处理过程,当然首先要先将 session.save_handler 改成 user,可在 PHP 中进行设置:
<?php
session_module_name(’user’);
?>

接下来着重讲一下 session_set_save_handle() 函数,此函数有六个参数:
session_set_save_handler ( string open, string close, string read, string write, string destroy, string gc )

各个参数为各项操作的函数名,这些操作依次是:打开、关闭、读取、写入、销毁、垃圾回收。PHP 手册中有详细的例子,在这里我们使用 OO 的方式来实现这些操作,详细代码如下:
<?php
define(’MY_SESS_TIME’, 3600);   //SESSION 生存时长
//类定义
class My_Sess
{
    function init()
    {
        $domain = ‘.infor96.com’;
        //不使用 GET/POST 变量方式
        ini_set(’session.use_trans_sid’,    0);
        //设置垃圾回收最大生存时间
        ini_set(’session.gc_maxlifetime’,   MY_SESS_TIME);

        //使用 COOKIE 保存 SESSION ID 的方式
        ini_set(’session.use_cookies’,      1);
        ini_set(’session.cookie_path’,      ‘/’);
        //多主机共享保存 SESSION ID 的 COOKIE
        ini_set(’session.cookie_domain’,    $domain);

        //将 session.save_handler 设置为 user,而不是默认的 files
        session_module_name(’user’);
        //定义 SESSION 各项操作所对应的方法名:
        session_set_save_handler(
            array(’My_Sess’, ‘open’),   //对应于静态方法 My_Sess::open(),下同。
            array(’My_Sess’, ‘close’),
            array(’My_Sess’, ‘read’),
            array(’My_Sess’, ‘write’),
            array(’My_Sess’, ‘destroy’),
            array(’My_Sess’, ‘gc’)
        );
    }   //end function

    function open($save_path, $session_name) {
        return true;
    }   //end function

    function close() {
        global $MY_SESS_CONN;

        if ($MY_SESS_CONN) {    //关闭数据库连接
            $MY_SESS_CONN->Close();
        }
        return true;
    }   //end function

    function read($sesskey) {
        global $MY_SESS_CONN;

        $sql = ‘SELECT data FROM sess WHERE sesskey=’ . $MY_SESS_CONN->qstr($sesskey) . ‘ AND expiry>=’ . time();
        $rs =& $MY_SESS_CONN->Execute($sql);
        if ($rs) {
            if ($rs->EOF) {
                return “;
            } else {   
  //读取到对应于 SESSION ID 的 SESSION 数据
                $v = $rs->fields[0];
                $rs->Close();
  //返回的数据是类似name|s:5:”wujun”;这种格式,不需要处理,PHP会处理。
                return $v;
            }   //end if
        }   //end if
        return “;
    }   //end function

    function write($sesskey, $data) {
        global $MY_SESS_CONN;
       
        $qkey = $MY_SESS_CONN->qstr($sesskey);
        $expiry = time() + My_SESS_TIME;    //设置过期时间
       
        //写入 SESSION
        $arr = array(
            ’sesskey’ => $qkey,
            ‘expiry’  => $expiry,
            ‘data’    => $data);
        $MY_SESS_CONN->Replace(’sess’, $arr, ’sesskey’, $autoQuote = true);
        return true;
    }   //end function

    function destroy($sesskey) {
        global $MY_SESS_CONN;

        $sql = ‘DELETE FROM sess WHERE sesskey=’ . $MY_SESS_CONN->qstr($sesskey);
        $rs =& $MY_SESS_CONN->Execute($sql);
        return true;
    }   //end function

    function gc($maxlifetime = null) {
        global $MY_SESS_CONN;

        $sql = ‘DELETE FROM sess WHERE expiry<’ . time();
        $MY_SESS_CONN->Execute($sql);
        //由于经常性的对表 sess 做删除操作,容易产生碎片,
        //所以在垃圾回收中对该表进行优化操作。
        $sql = ‘OPTIMIZE TABLE sess’;
        $MY_SESS_CONN->Execute($sql);
        return true;
    }   //end function
}   ///:~

//使用 ADOdb 作为数据库抽象层。
require_once(’adodb/adodb.inc.php’);
//数据库配置项,可放入配置文件中(如:config.inc.php)。
$db_type = ‘mysql’;
$db_host = ‘192.168.212.1′;
$db_user = ’sess_user’;
$db_pass = ’sess_pass’;
$db_name = ’sess_db’;
//创建数据库连接,这是一个全局变量。
$GLOBALS['MY_SESS_CONN'] =& ADONewConnection($db_type);
$GLOBALS['MY_SESS_CONN']->Connect( $db_host, $db_user, $db_pass, $db_name);
//初始化 SESSION 设置,必须在 session_start() 之前运行!!
My_Sess::init();
?>

五、遗留问题

如果网站的访问量很大的话,SESSION 的读写会频繁地对数据库进行操作,这样效率就会明显降低。建议使用mysql的memory表,这样效率会高很多。

发表在 article | 标签为 , | 41条评论

sql server 语句执行时间

set statistics profile on
set statistics io on
set statistics time on
go
/*你要执行的语句*/
go
set statistics profile off
set statistics io off
set statistics time off

发表在 article | 标签为 , | sql server 语句执行时间已关闭评论

UUID

######### PHP ##############

/**
* Generates an UUID
*
* @author Anis uddin Ahmad <admin@ajaxray.com>
* @param string an optional prefix
* @return string the formatted uuid
*/
function uuid($prefix='')
{
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr($chars,0,8) . '-';
$uuid .= substr($chars,8,4) . '-';
$uuid .= substr($chars,12,4) . '-';
$uuid .= substr($chars,16,4) . '-';
$uuid .= substr($chars,20,12);
return $prefix . $uuid;
}

Example of using the function
-
//Using without prefix.

echo uuid();
//Returns like ‘1225c695-cfb8-4ebb-aaaa-80da344e8352′

//Using with prefix
echo uuid(‘urn:uuid:’);
//Returns like ‘urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344e8352′

 

######### Javascript ##############



function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}

..

function guid() {
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}

..

function uuid(len, radix) {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
var uuid = [], i;
radix = radix || chars.length;

if (len) {
// Compact form
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
} else {
// rfc4122, version 4 form
var r;

// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';

// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random()*16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}

return uuid.join('');
}

 

// 8 character ID (base=2)
uuid(8, 2) // "01001010"
// 8 character ID (base=10)
uuid(8, 10) // "47473046"
// 8 character ID (base=16)
uuid(8, 16) // "098F4D35"

 

..

 

function uuid() {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";

var uuid = s.join("");
return uuid;
}

 

 

..

 

 

 

 

发表在 article | 标签为 | UUID已关闭评论

在网页中加高亮显示源代码的工具

刚刚发现一个老外用Javascrīpt编写的好东西:dp.SyntaxHighlighter。它可以在网页中对各种程序源代码
语法进行加亮显示。支持当前流行的各种编程语言:C#、CSS
、C++、Delphi、Java
、Javascrīpt、PHP
、Python、Ruby、SQL、 Visual Basic、XML
/ HTML

  下载地址:http://www.dreamprojections.com/syntaxhighlighter/

  演示地址:http://www.dreamprojections.com/syntaxhighlighter/Tests/PHP.html

下载
1

下载
2

  使用方法:
  1、假设网页文件test.htm存放在一个目录,则将dp.SyntaxHighlighter解压缩到该目录下的子目录,假设为images
  2、在网页的<head></head>之间插入以下代码:

 

 
  1. <link type=
    "text/css"
     rel=
    "stylesheet"
     href=
    "images/Styles/SyntaxHighlighter.css"
    ></link>  


      3、在网页要显示程序源代码的地方插入以下代码(其中的class="js"表示以js语法显示源代码,其他可设定的class值分别为c#、css、c、delphi、java、js、php、python、ruby、sql、vb、xml):
  1. <textarea name=
    "code"
     
    class
    =
    "js"
     rows=
    "15"
     cols=
    "100"
    >  
  2. 程序源代码放在这儿  
  3. </textarea>  



  4、在网页尾部的</body>之前插入以下代码:

  1. <scrīpt 
    class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shCore.js"
    ></scrīpt>  
  2. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushCSharp.js"
    ></scrīpt>  
  3. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushPhp.js"
    ></scrīpt>  
  4. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushJscrīpt.js"
    ></scrīpt>  
  5. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushJava.js"
    ></scrīpt>  
  6. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushVb.js"
    ></scrīpt>  
  7. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushSql.js"
    ></scrīpt>  
  8. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushXml.js"
    ></scrīpt>  
  9. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushDelphi.js"
    ></scrīpt>  
  10. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushPython.js"
    ></scrīpt>  
  11. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushRuby.js"
    ></scrīpt>  
  12. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushCss.js"
    ></scrīpt>  
  13. <scrīpt class
    =
    "javascrīpt"
     src=
    "images/scrīpts/shBrushCpp.js"
    ></scrīpt>  
  14. <scrīpt class
    =
    "javascrīpt"
    >  
  15. dp.SyntaxHighlighter.HighlightAll('code'
    );  
  16. </scrīpt> 
发表在 article | 标签为 | 在网页中加高亮显示源代码的工具已关闭评论

Apache虚拟主机配置

Apache虚拟主机配置

虚拟主机的几种实现方法
虚拟主机有许多方法来实现,比如多IP,多域名,多端口等。这里主要将多域名和多端口的实现步骤。
第一种:多端口的实现

#监听的端口号,如果想一个IP地址多端口控制访问的话必须填写端口
Listen 

81

Listen 

82

#端口81的虚拟主机

<
VirtualHost 
*
:
81
>

    DocumentRoot 

"
Webroot/port01
"


</
VirtualHost
>

#端口82的虚拟主机

<
VirtualHost 
*
:
82
>

    DocumentRoot 

"
Webroot/port02
"


</
VirtualHost
>

第二种:多域名的实现

NameVirtualHost 
*
:
80

#域名www.domain01.com的虚拟主机

<
VirtualHost 
*
:
80
>

    DocumentRoot "Webroot/domain01"


    ServerName www.domain01.com

</
VirtualHost
>


#域名www.domain02.com的虚拟主机

<
VirtualHost 
*
:
80
>

    DocumentRoot "Webroot/domain02"


    ServerName www.domain02.com

</
VirtualHost
>

 

如何使虚拟主机生效
第一种:使扩展文件httpd-vhosts.conf生效:
1. 打开 apache/conf/httpd.conf
文件
2. 找到 # Include conf/extra/httpd-vhosts.conf

3. 去掉前面的注释符#号
4. 打开 apache/conf/extra/httpd-vhosts.conf

5. 添加以上的多端口或多域名代码(这里以多域名为例):

NameVirtualHost 
*
:
80


<
VirtualHost 
*
:
80
>

    DocumentRoot "Webroot/domain01"


    ServerName www.domain01.com

</
VirtualHost
>


<
VirtualHost 
*
:
80
>

    DocumentRoot "Webroot/domain02"


    ServerName www.domain02.com

</
VirtualHost
>

 

第二种:另类的办法
1. 在conf
目录中建立vhosts
目录,并建立两个文件,分别为host1.com
host2.com

2. 在 apache/conf/httpd.conf
适当的位置添加下面两行代码
NameVirtualHost *:80
Include conf/vhosts
3. 修改 host1.com
文件

<
VirtualHost 
*
:
80
>

    DocumentRoot "Webroot/domain01"


    ServerName www.domain01.com

</
VirtualHost
>

4. 修改 host2.com
文件

<
VirtualHost 
*
:
80
>

    DocumentRoot "Webroot/domain02"


    ServerName www.domain02.com

</
VirtualHost
>

 

关于虚拟主机的扩展属性
 

<
VirtualHost 
*
:
80
>

    ServerAdmin webmaster@admin.com        

//
管理员邮箱,当出现域名相关的错误时则会发送到此邮箱


    DocumentRoot 
"
/Webroot/host
"
              
//
网站文档所在位置,可以是绝对位置


    ServerName www.host.com                   
//
绑定域名


    ServerAlias www.host.com                    
//
CNAME别名


    ErrorLog logs
/
host.com
-
error_log            
//
错误日志


    CustomLog logs
/
host.com
-
access_log common 
//
访问日志



    #虚拟主机目录的访问权限
    

<
Directory 
"
/Webroot/host
"
>

        Options Indexes FollowSymLinks
        AllowOverride None
        Order allow,deny
        Allow from all
    

</
Directory
>


</
VirtualHost
>

学习 Apache 的一些心得体会

Apache 将目录作为单元来进行存取控制,每个目录在/etc/httpd/conf/httpd.conf 中

  使用一个段落,首先的是/目录,这实际是设置缺省值:

  Options FollowSymLinks
  AllowOverride None

  每个段落都是由这样的两组尖括号构成的行夹起来的,其中最主要的句子是Options,AllowOverride,Allow/Deny,Order 等。

  Options这个子句用来说明一些主要的设置,目前可以使用的设置有Indexes,Includes,FollowSymLinks,ExecCGI,MultiView,当然还有两个最简单的选择就是None和All。

  None是禁止所有选择,而All 允许上面的所有Options。一般我们主要关心的是Indexes 和FollowSymLinks。Indexes 是设定是否允许在目录下面没有index.html 的时候显示目录,而FollowSymLinks 决定是否可以通过符号连接跨越DocumentRoot。例如,尽管/ftp 不在/home/httpd/html 下面,但是我们仍然可以使用符号连接建立一个/home/httpd/html/ftp使得可以直接输入http://mydomain.com/ftp
来访问这个目录。

  使用FollowSymLinks 的办法很简单,就是首先在合适的目录段落里面Options Follow SymLinks,(符号连接的上层就可以)然后建立一个别名:

  Alias /ftp/ “home/httpd/html/ftp/”

  后面的是你建立的到/ftp 的符号连接。注意这一行应该位于所有目录段落之外。

  AllowOverride

  AllowOverride 定义是否允许各个目录用目录中的.htaccess(后面解释)覆盖这里设定的Options。它的选择有Options,FileInfo,AuthConfig,Limit 或者它们的组合,当然还有None 和All。

  由于/是缺省设置,所以这里没有设置太多的内容,相反,我们应该在/之后独立设置各个目录的控制,例如:

  Options Indexes FollowSymLinks
  AllowOverride None
  Order allow,deny
  Allow from all

  出现了两个新的选项:Order 和Allow。

  Order

  它有两种用法,即Order allow,deny 或者Order deny,allow。简单地说,它用来设置是先执行deny 还是先执行allow,例如,Order deny,allow 意味着先看deny 行,再看allow 行,这样如果deny from all 再allow from 202.112.58.0/24 后来的allow子句就会超越deny 而对202.112.58.x 打开访问。

  Allow/Deny from

  这两个选项和Order 一起使用,Allow 允许某个地址来的连接请求,Deny 则禁止这个请求,例如在Order deny,allow 的情况下,Deny from all,Allow from 192.168.12.0/24 表示只有192.168.12.x 的用户可以访问这个目录。

  现在我们来看看下面的一个设定:

  UserDir public_html
  AllowOverride FileInfo AuthConfig Limit
  Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
  Order allow,deny
  Allow from all
  Order deny,allow
  Deny from all

  这个有什么用处?UserDir 是一个特殊的选项,用来在用户的宿主目录下设置主页,比如,有一个名字叫wanghy 的用户,其宿主目录是/home/wanghy,而且UserDir 象我们刚才那样设定,那么,你可以在浏览器中输入http://www.mydomain.com/~wanghy
,它将对应的是宿主目录+UserDir,也就是/home/wanghy/public_html。当然,为了使这个功能生效,你得将这个目录设置为大家都可以读。(现在知道http://xxx.net/~yourname
是怎么回事了吧,P)

  下面的行就是对这个功能的限制,注意要使得某人的这个功能生效,必须有这个段落,这个段落里别的东西都是熟知的,唯一新的东西是Limit 段,它用来提供存取限制,例如对应的段落设置GET 和POST 权限,诸如此类。上面的段落表示所有人都可以通过www 进行GET 和POST,而任何人都不能PUT 和DELETE、MOVE 等等。

  Limit

  对某个路径进行存取限制。

  现在我们来看看下面的一个设定:

  UserDir public_html
  AllowOverride FileInfo AuthConfig Limit
  Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
  Order allow,deny
  Allow from all
  Order deny,allow
  Deny from all

  这个有什么用处?UserDir 是一个特殊的选项,用来在用户的宿主目录下设置主页,比如,有一个名字叫wanghy 的用户,其宿主目录是/home/wanghy,而且UserDir 象我们刚才那样设定,那么,你可以在浏览器中输入http://www.mydomain.com/~wanghy
,它将对应的是宿主目录+UserDir,也就是/home/wanghy/public_html。当然,为了使这个功能生效,你得将这个目录设置为大家都可以读。(现在知道http://xxx.net/~yourname
是怎么回事了吧,P)

  下面的行就是对这个功能的限制,注意要使得某人的这个功能生效,必须有这个段落,这个段落里别的东西都是熟知的,唯一新的东西是Limit 段,它用来提供存取限制,例如对应的段落设置GET 和POST 权限,诸如此类。上面的段落表示所有人都可以通过www 进行GET 和POST,而任何人都不能PUT 和DELETE、MOVE 等等。

  Limit

  对某个路径进行存取限制。

  实际配置如下:

  mkdir /home/rd2/leon
  chmod 755 /home/rd2
  chmod 755 /home/rd2/leon
  cp index.html /home/rd2/leon

  然后就可以在浏览器中输入http://10.12.2.82/~rd2
,它将对应的是/宿主目录/UserDir(我们设置位leon),也就是显示/home/wanghy/leon下的index.html。

  <IfModule mod_userdir.c>
  #
  # UserDir is disabled by default since it can confirm the presence
  # of a username on the system (depending on home directory
  # permissions).
  #
  #UserDir disable 注释掉
  #
  # To enable requests to /~user/ to serve the user's public_html
  # directory, remove the "UserDir disable" line above, and uncomment
  # the following line instead:
  #
  UserDir leon 改为自己喜欢的
  # UserDir public_html 注视掉原句
  </IfModule>
  #
  # Control access to UserDir directories. The following is an example
  # for a site where these directories are restricted to read-only.
  #
  <Directory /home/*/leon> 改为自己喜欢的
  AllowOverride FileInfo AuthConfig Limit
  Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
  <Limit GET POST OPTIONS>
  Order allow,deny
  Allow from all
  </Limit>
  <LimitExcept GET POST OPTIONS>
  Order deny,allow
  Deny from all
  </LimitExcept>
  </Directory>

  FollowSymLinks决定是否可以通过符号连接跨越DocumentRoot。例如,尽管/home不在/var/www/html下面,但是我们仍然可以使用符号连接也就是说可以直接输入http://mydomain.com/myhome
来访问这个目录。

  使用FollowSymLinks的办法很简单,就是目录段落里面Options Follow SymLinks,然后建立一个别名:

  Alias /myhome “/home”
  Alias /myhome "/home"(不能是/myhome/)
  <Directory "/home">
  Options Indexes FollowSymLinks MultiViews
  AllowOverride None
  Order allow,deny
  Allow from all
  </Directory>

  虚拟主机:

  NameVirtualHost 10.12.2.82
  <VirtualHost 10.12.2.84> 需要复制一个eth0:0 #访问时候用:10.12.2.84,不可用leon1.abit.cn???????
  DocumentRoot /home/leon
  ServerName leon1.abit.cn
  </VirtualHost>
  <VirtualHost 10.12.2.82>访问时候用:leon(leon1).abit.cn
  DocumentRoot /var/www/html
  ServerName leon.abit.cn
  </VirtualHost>
  <VirtualHost 10.12.2.82>
  DocumentRoot /home
  ServerName leon2.abit.cn
  </VirtualHost>

发表在 web server | 标签为 | Apache虚拟主机配置已关闭评论

squid纯内存缓存

看见网上不少介绍squid的使用经验,说可以用ramdisk/tmpfs内存文件系统来加速squid. 个人认为完全没有必要,直接简单配置一下,就可以让Squid直接充分利用大内存:
maximum_object_size 32768 KB
cache_mem 1512 MB # Cache使用的最大内存
maximum_object_size_in_memory 128 KB # Cache在memory中最大Object的大小
memory_pools_limit 150 MB # 小对象内存池的大小
cache_dir null /tmp # 使用null disk
一些需要注意的事项: squid在编译的时候,要支持nullfs. maximum_object_size_in_memory应该尽量大于最大的object的尺寸,但是也不要太大,否则会影响内存分配的效率.

考虑一下实际应用的情况: 平均页面对象都不是很大,对象个数也不是很多,同时又有充足的内存余量,内容跟新还算频繁,这个时候,1.5G和20G或者40G的cache的请求命中率其实相差非常细微,不过是99%和99.9%的区别,这个时候,完全有理由,抛下缓慢的disk cache,直接使用memory cache,这样不但能有效降低系统load,还能使squid自身更加轻快便捷.


 

Comments»

1.
Frank Liu - January 10, 2007

非常好

2.
slls - January 16, 2007

受教!
但不知cache_mem和memory_pools_limit的大小是何关系。请指教!
谢谢!

3.
scaner - January 20, 2007

我以前对memory_pool_limit理解也有错误, memory_pool_limit并不是小对象池的大小,相当于malloc的cache,squid并不直接对释放的对象调用free操作,而是等到这个大小超过到这个大小超过memory_pool_limit才会真正释放. 感觉比较合适的大小是cache_mem + memory_pool_limit < 实际内存量, memory_pool_limit在150M左右就差不多了.当然还有为系统别的程序留一点余量。比如说如果是2G内存我的cache_mem是 1648M,memory_pool_limit是120M

4.
charles
- March 10, 2007

不知道你有没有留意到,如果用 null 方式的存储,全部放内存后,SQUID 的 response time 会变成非常高。

在我的应用,是 2ms 升到 30ms,这有可能是因为 SQUID 主进程不单止要接受请求,还要去从存储(null 的方式是存在内存)里面查找相应的数据,再返回。

而用 aufs 或 diskd 方式,squid 可以马上响应下一个请求,而将查找的工作交给 diskd 进程去处理。

5.
scaner - March 13, 2007

真还没注意过这个现象。30ms这个好像有点夸张啊,我现在一个主squid的峰值请求处理数能到1200hits/s了。对象信息都是保存在内存中的,直接内存处理,再怎么也要比aufs/diskd再倒一次手要快很多吧,我的意思是说,有submit任务到aufs/diskd的功夫,内存查找早就搞定了。

6.
charles
- March 19, 2007

你可能架一个 MRTG 来查看一下就知道了。只要压力很大,response 的值就会非常高。

在我的应用中,也是将数据保存到内存里面的,使用 diskd 直接读写 /dev/shm 等。

7.
scaner - March 19, 2007

赫赫,你看得是那一个图阿?

client_http.requests = 1271.172566/sec
client_http.hits = 1209.615937/sec
client_http.all_median_svc_time = 0.000000 seconds
client_http.miss_median_svc_time = 0.000911 seconds
client_http.nm_median_svc_time = 0.000000 seconds
client_http.nh_median_svc_time = 0.001789 seconds
client_http.hit_median_svc_time = 0.000000 seconds

8.
charles
- March 31, 2007

你先确定一下,是不是用了 epoll ?

如果是用了 epoll,我觉得有这样的响应时间,我觉得正常。在不支持 epoll 的环境下,squid 的响应时间会慢,而且 CPU 占用较高。

另外,我现在在关注这个:http://varnish.projects.linpro.no

用squid就是因为epoll阿.没有epoll连接数一多CPU就跑上去了.varnish这个东西看着不错啊,不过看了上面一个评测的链接,lighttpd居然是吞吐量最强的,寒一个.还是祈求上天Light 1.5赶快release吧.

9.
scaner - April 3, 2007
发表在 article | 标签为 | squid纯内存缓存已关闭评论

mysql数据库受到破坏的修复

修复数据表

多数情况下,数据库被破坏只是指索引文件受到了破坏,真正的数据被破坏掉的情况非常少。大多数形式的数据库破坏的的修复相当简单。
和前面的校验一样,修复的方式也有三种。

下面讲的方法只对MyISAM格式的表有效。其他类型的损坏需要从备份中恢复。

1。REPAIR TABLE SQL statement(mysql服务必须处于运行状态)。
2。命令mysqlcheck(mysql服务可以处于运行状态)。
3。命令myisamchk(必须停掉mysql服务,或者所操作的表处于不活动状态)。

在修复表的时候,最好先作一下备份。所以你需要两倍于原始表大小的硬盘空间。请确保在进行修复前你的硬盘空间还没有用完。

1>用”repair table”方式修复
语法:repair table 表名 [选项]
选项如下:
QUICK 用在数据表还没被修改的情况下,速度最快
EXTENDED 试图去恢复每个数据行,会产生一些垃圾数据行,万般无奈的情况下用
USE_FRM 用在.MYI文件丢失或者头部受到破坏的情况下。利用.frm的定义来重建索引

多数情况下,简单得用”repair table tablename”不加选项就可以搞定问题。但是当.MYI文件丢失或者头部受到破坏时,这样的方式不管用,例如:
mysql> REPAIR TABLE fixtures;
+————————-+——–+———-+———————————————+
| Table | Op | Msg_type | Msg_text |
+————————-+——–+———-+———————————————+
| sports_results.fixtures | repair | error | Can’t find file: ‘fixtures.MYI’ (errno: 2) |
+————————-+——–+———-+———————————————+

修复失败的原因时索引文件丢失或者其头部遭到了破坏,为了利用相关定义文件来修复,需要用USE_FRM选项。例如:
mysql> REPAIR TABLE fixtures USE_FRM;
+————————-+——–+———-+————————————+
| Table | Op | Msg_type | Msg_text |
+————————-+——–+———-+————————————+
| sports_results.fixtures | repair | warning | Number of rows changed from 0 to 2 |
| sports_results.fixtures | repair | status | OK |
+————————-+——–+———-+————————————+

我们可以看到Msg_test表项的输出信息”ok”,表名已经成功修复受损表。

2>用mysql内建命令mysqlcheck来修复
当mysql服务在运行时,也可以用mysql内建命令mysqlcheck来修复。
语法:mysqlcheck -r 数据库名 表名 -uuser -ppass
%mysqlcheck -r sports_results fixtures -uuser -ppass
sports_results.fixtures OK

利用mysqlcheck可以一次性修复多个表。只要在数据库名后列出相应表名即可(用空格隔开)。或者数据库名后不加表名,将会修复数据库中的所有表,例如:

%mysqlcheck -r sports_results fixtures events -uuser -ppass
sports_results.fixtures OK
sports_results.events OK

%mysqlcheck -r sports_results -uuser -ppass
sports_results.fixtures OK
sports_results.events OK

3>用myisamchk修复
用这种方式时,mysql服务必须停掉,或者所操作的表处于不活动状态(选项skip-external-locking没被使用)。记着一定要在相关.MYI文件的路径下或者自己定义其路径。
语法:myisamchk [选项] [表名]
下面是其选项和描述
–backup, -B 在进行修复前作相关表得备份
–correct-checksum 纠正校验和
–data-file-length=#, -D # 重建表时,指定数据文件得最大长度
–extend-check, -e 试图去恢复每个数据行,会产生一些垃圾数据行,万般无奈的情况下用
–force, -f 当遇到文件名相同的.TMD文件时,将其覆盖掉。
keys-used=#, -k # 指定所用的keys可加快处理速度,每个二进制位代表一个key.第一个key为0
–recover, -r 最常用的选项,大多数破坏都可以通过它来修复。如果你的内存足够大,可以增大
参数sort_buffer_size的值来加快恢复的速度。但是遇到唯一键由于破坏而不唯一
的表时,这种方式不管用。
–safe-recover -o 最彻底的修复方式,但是比-r方式慢,一般在-r修复失败后才使用。这种方式读出所有的行,并以行为基础来重建索引。它的硬盘空间需求比-r方式稍微小一点,因为它没创建分类缓存。你可以增加key_buffer_size的值来加快修复的速度。
–sort-recover, -n mysql用它类分类索引,尽管结果是临时文件会非常大
–character-sets-dir=… 包含字符集设置的目录
–set-character-set=name 为索引定义一个新的字符集
–tmpdir=path, -t 如果你不想用环境变量TMPDIR的值的话,可以自定义临时文件的存放位置
–quick, -q 最快的修复方式,当数据文件没有被修改时用,当存在多键时,第二个-q将会修改 数据文件
–unpack, -u 解开被myisampack打包的文件

myisamchk应用的一个例子

% myisamchk -r fixtures
- recovering (with keycache) MyISAM-table ‘fixtures.MYI’
Data records: 0

 

------------------------------------------------------------------------------------------------------

假设有个myisam表:tbl,为了备份方便,直接把 frm 和 MYD 文件拷贝到其他目录。在还原时,就需要重新下创建索引,只需要执行以下命令:

mysql> REPAIR TABLE `tbl` USE_FRM;

即可根据 frm 和 MYD 文件,产生一个新的 MYI 索引文件。这在索引文件较大时备份还原比较有用。

---------------------------------------------------------------------------------------------------------

防止客户机的请求互相干扰或者服务器与维护程序相互干扰的方法主要有多种。如果你关闭数据库,就可以保证服务器和myisamchk和isamchk之间没有交互作用。但是停止服务器的运行并不是一个好注意,因为这样做会使得没有故障的数据库和表也不可用。本节主要讨论的过程,是避免服务器和myisamchk或isamchk之间的交互作用。实现这种功能的方法是对表进行锁定。

服务器由两种表的锁定方法:

1.内部锁定

内部锁定可以避免客户机的请求相互干扰——例如,避免客户机的SELECT查询被另一个客户机的UPDATE查询所干扰。也可以利用内部锁定机制防止服务器在利用myisamchk或isamchk检查或修复表时对表的访问。

语法:

锁定表:LOCK TABLES tbl_name {READ | WRITE},[ tbl_name {READ | WRITE},…]

解锁表:UNLOCK TABLES

LOCK TABLES为当前线程锁定表。UNLOCK TABLES释放被当前线程持有的任何锁。当线程发出另外一个LOCK TABLES时,或当服务器的连接被关闭时,当前线程锁定的所有表自动被解锁。

如果一个线程获得在一个表上的一个READ锁,该线程(和所有其他线程)只能从表中读。如果一个线程获得一个表上的一个WRITE锁,那么只有持锁的线程READ或WRITE表,其他线程被阻止。

每个线程等待(没有超时)直到它获得它请求的所有锁。

WRITE锁通常比READ锁有更高的优先级,以确保更改尽快被处理。这意味着,如果一个线程获得READ锁,并且然后另外一个线程请求一个WRITE锁, 随后的READ锁请求将等待直到WRITE线程得到了锁并且释放了它。

显然对于检查,你只需要获得读锁。再者钟情跨下,只能读取表,但不能修改它,因此他也允许其它客户机读取表。对于修复,你必须获得些所以防止任何客户机在你对表进行操作时修改它。

2.外部锁定

服务器还可以使用外部锁定(文件级锁)来防止其它程序在服务器使用表时修改文件。通常,在表的检查操作中服务器将外部锁定与myisamchk或isamchk作合使用。但是,外部锁定在某些系统中是禁用的,因为他不能可靠的进行工作。对运行myisamchk或isamchk所选择的过程取决于服务器是否能使用外部锁定。如果不使用,则必修使用内部锁定协议。
如果服务器用--skip-locking选项运行,则外部锁定禁用。该选项在某些系统中是缺省的,如Linux。可以通过运行mysqladmin variables命令确定服务器是否能够使用外部锁定。检查skip_locking变量的值并按以下方法进行:

◆ 如果skip_locking为off,则外部锁定有效您可以继续并运行人和一个实用程序来检查表。服务器和实用程序将合作对表进行访问。但是,运行任何一个实用程序之前,应该使用mysqladmin flush-tables。为了修复表,应该使用表的修复锁定协议。

◆ 如果skip_locaking为on,则禁用外部锁定,所以在myisamchk或isamchk检查修复表示服务器并不知道,最好关闭服务器。如果坚持是服务器保持开启状态,月确保在您使用此表示没有客户机来访问它。必须使用卡党的锁定协议告诉服务器是该表不被其他客户机访问。

检查表的锁定协议

本节只介绍如果使用表的内部锁定。对于检查表的锁定协议,此过程只针对表的检查,不针对表的修复。

1.调用mysql发布下列语句:

$mysql –u root –p db_namemysql>LOCK TABLE tbl_name READ;mysql>FLUSH TABLES;

该锁防止其它客户机在检查时写入该表和修改该表。FLUSH语句导致服务器关闭表的文件,它将刷新仍在告诉缓存中的任何为写入的改变。

2.执行检查过程

$myisamchk tbl_name$ isamchk tbl_name

3.释放表锁

mysql>UNLOCK TABLES;

如果myisamchk或isamchk指出发现该表的问题,将需要执行表的修复。

修复表的锁定协议

这里只介绍如果使用表的内部锁定。修复表的锁定过程类似于检查表的锁定过程,但有两个区别。第一,你必须得到写锁而非读锁。由于你需要修改表,因此根本不允许客户机对其进行访问。第二,必须在执行修复之后发布FLUSH TABLE语句,因为myisamchk和isamchk建立的新的索引文件,除非再次刷新改表的高速缓存,否则服务器不会注意到这个改变。本例同样适合优化表的过程。

1.调用mysql发布下列语句:

$mysql –u root –p db_namemysql>LOCK TABLE tbl_name WRITE;mysql>FLUSH TABLES;

2.做数据表的拷贝,然后运行myisamchk和isamchk:

$cp tbl_name.* /some/other/dir$myisamchk --recover tbl_name$ isamchk --recover tbl_name

--recover选项只是针对安装而设置的。这些特殊选项的选择将取决与你执行修复的类型。

3.再次刷新高速缓存,并释放表锁:

mysql>FLUSH TABLES;mysql>UNLOCK TABLES;

-----------------------------------------------------------------------------------------------

问题1: mysql 磁盘满

问题2: mysql repair 多个表

repair table: 需要按周以下步骤

1: lock table EE write ;

2: flush table EE ;

3: repair table EE ;

4: flush table EE ;

5: unlock table EE ;

// 在使用mysql过程中发现了一个奇怪的问题:

我的磁盘空间快要满了(%90),df du 看了一下, 是mysql 占用了大量的空间,于是我把mysql中每个表中的数据删除了一半,但是硬盘空间仍然被占用%90 。

我但是没有管, 但是过了半个月,磁盘仍然没有满,按理说很快就会超过%90 报警的 。

于是我想找出原因:

办法一: 怀疑是有进程在想里面些数据, 不能delete 。
./mysqladmin shutdown
./mysqld-safe

重启mysql , 没有任何效果

办法二: flush 每个表, 也不行

办法三: repair 其中一个表, 看来以下, 原来700M 的空间编程了200M左右。

但是问题又来了, 我有256个表, 我写了C++程序, 顺序repair 每一个表,但是会产生Commands out of sync; you can't run this command now 错误, 不能同步

后来, 我随便试了以下, repair table1 , table2, table3 ; 这样是可以的。

原来 repair 可以一次可以repair 多个表, 问题解决了。

发表在 article | 标签为 , | mysql数据库受到破坏的修复已关闭评论

C# 线程池示例

以下三个代码示例演示 QueueUserWorkItem 和 RegisterWaitForSingleObject 方法。

第一个示例使用 QueueUserWorkItem 方法将一个由 ThreadProc 方法表示的非常简单的任务排入队列。

using System;
using System.Threading;
public class Example {
    public static void Main() {
        // 将任务排队
        ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
       
        Console.WriteLine("Main thread does some work, then sleeps.");
        // 如果你注释掉这个Sleep, 而线程池使用后台线程,
         // 那么主线程将会停止运行,并在线程池任务运行之前退出
         // (这是一个简单的线程池运行环境例子)
        Thread.Sleep(1000);

        Console.WriteLine("Main thread exits.");
    }

    // 这是执行任务的线程子程序
    static void ThreadProc(Object stateInfo) {
        // 没有state对象被传递到QueueUserWorkItem, 所以
        // stateInfo是null.
        Console.WriteLine("Hello from the thread pool.");
    }
}

为 QueueUserWorkItem 提供任务数据
下面的代码示例使用 QueueUserWorkItem 方法将一个任务排队并为该任务提供数据。

using System;
using System.Threading;

// TaskInfo 类 为任务提供将在线程池线程中执行的 state 信息
public class TaskInfo {
    // 任务所需的State信息,这些类成员可以是任务需要的确定的只读属性,
    // 可读写属性,或者其它。
    public string Boilerplate;
    public int Value;

    // Public 构造函数提供一个非常简单途径用来接收任务所需要的信息
    public TaskInfo(string text, int number) {
        Boilerplate = text;
        Value = number;
    }
}

public class Example {
    public static void Main() {
        // 创建一个包含任务所需要信息的对象。
        TaskInfo ti = new TaskInfo("This report displays the number {0}.", 42);

        // 将任务排队并传入所需信息
        if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), ti)) {   
            Console.WriteLine("Main thread does some work, then sleeps.");

            // 如果你注释掉这个Sleep, 而线程池使用后台线程,
              // 那么主线程将会停止运行,并在线程池任务有机会运行之前退出
              // (这是一个简单的线程池运行环境例子)
            Thread.Sleep(1000);

            Console.WriteLine("Main thread exits.");
        }
        else {
            Console.WriteLine("Unable to queue ThreadPool request.");
        }
    }

    // 这个线程子程序执行独立的任务,在这里转换并打印一个非常简单的报告
    static void ThreadProc(Object stateInfo) {
        TaskInfo ti = (TaskInfo) stateInfo;
        Console.WriteLine(ti.Boilerplate, ti.Value);
    }
}

RegisterWaitForSingleObject
下面的示例演示几种线程处理功能。

· 使用 RegisterWaitForSingleObject 方法将任务排队,以由 ThreadPool 线程执行。

· 使用 AutoResetEvent 发出信号,通知执行任务。请参见 EventWaitHandle、AutoResetEvent 和 ManualResetEvent。

· 使用 WaitOrTimerCallback 委托处理超时和信号。

· 使用 RegisteredWaitHandle 取消排入队列的任务。

 

using System;
using System.Threading;

// TaskInfo 包含将要被回调(callback)的数据
public class TaskInfo {
    public RegisteredWaitHandle Handle = null;
    public string OtherInfo = "default";
}

public class Example {
    public static void Main(string[] args) {
        // 主线程使用 AutoResetEvent 通知执行回调方法的 wait 句柄(handle)
        AutoResetEvent ev = new AutoResetEvent(false);

        TaskInfo ti = new TaskInfo();
        ti.OtherInfo = "First task";
        // TaskInfo对象包含了为任务返回RegisterWaitForSingleObject的注册等待句柄(handle)
        // 这允许当对象一旦被通知就终止等待操作。(见 WaitProc).
        ti.Handle = ThreadPool.RegisterWaitForSingleObject(
            ev,
            new WaitOrTimerCallback(WaitProc),
            ti,
            1000,
            false
        );

        // 主线程等待三秒钟,用以示范排队线程超时的情况,然后通知它。
        Thread.Sleep(3100);
        Console.WriteLine("Main thread signals.");
        ev.Set();

        // 主线程休眠(sleeps),应该能给回调(callback)方法足够的时候去执行它。
        // 如果你注释掉这行,程序通常会在线程池线程被执行前退出。
        Thread.Sleep(1000);
        // 如果你开始一个自己的线程,你可以调用 Thread.Join() 来等待它完成。
        // 这个选项在线程池线程里不可用。
    }
  
    // 当注册等待超时,或者当等待句柄(WaitHandle——在这个例子里是AutoResetEvent)被通知时,
    // 将会执行这个回调(callback)方法,
    // WaitProc 反注册等待句柄(WaitHandle) the first time the event is
    // signaled.
    public static void WaitProc(object state, bool timedOut) {
        // state 对象必须传入正确的类型,因为 WaitOrTimerCallback 委托(delegate)
        // 明确指出的对象的类型。
        TaskInfo ti = (TaskInfo) state;

        string cause = "TIMED OUT";
        if (!timedOut) {
            cause = "SIGNALED";
            // 如果这个回调(callback)方法被执行是因为等待句柄(WaitHandle)通知,
            // 停止执行接下来的回调(callback )方法, 并反注册等待句柄(WaitHandle)
            if (ti.Handle != null)
                ti.Handle.Unregister(null);
        }

        Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
            ti.OtherInfo,
            Thread.CurrentThread.GetHashCode().ToString(),
            cause
        );
    }
}

 

---------------------------------------------------------------------------------------------------

c#.net多线程编程教学(3):线程同步

随着对多线程学习的深入,你可能觉得需要了解一些有关线程共享资源的问题. .NET framework提供了很多的类和数据类型来控制对共享资源的访问。

  考虑一种我们经常遇到的情况:有一些全局变量和共享的类变量,我们需要从不同的线程来更新它们,可以通过使用System.Threading.Interlocked类完成这样的任务,它提供了原子的,非模块化的整数更新操作。

  还有你可以使用System.Threading.Monitor类锁定对象的方法的一段代码,使其暂时不能被别的线程访问。

  System.Threading.WaitHandle类的实例可以用来封装等待对共享资源的独占访问权的操作系统特定的对象。尤其对于非受管代码的互操作问题。

  System.Threading.Mutex用于对多个复杂的线程同步的问题,它也允许单线程的访问。

  像ManualResetEvent和AutoResetEvent这样的同步事件类支持一个类通知其他事件的线程。

  不讨论线程的同步问题,等于对多线程编程知之甚少,但是我们要十分谨慎的使用多线程的同步。在使用线程同步时,我们事先就要要能够正确的确定是那个对象和方法有可能造成死锁(死锁就是所有的线程都停止了相应,都在等者对方释放资源)。还有赃数据的问题(指的是同一时间多个线程对数据作了操作而造成的不一致),这个不容易理解,这么说吧,有X和Y两个线程,线程X从文件读取数据并且写数据到数据结构,线程Y从这个数据结构读数据并将数据送到其他的计算机。假设在Y读数据的同时,X写入数据,那么显然Y读取的数据与实际存储的数据是不一致的。这种情况显然是我们应该避免发生的。少量的线程将使得刚才的问题发生的几率要少的多,对共享资源的访问也更好的同步。

  .NET Framework的CLR提供了三种方法来完成对共享资源 ,诸如全局变量域,特定的代码段,静态的和实例化的方法和域。

  (1) 代码域同步:使用Monitor类可以同步静态/实例化的方法的全部代码或者部分代码段。不支持静态域的同步。在实例化的方法中,this指针用于同步;而在静态的方法中,类用于同步,这在后面会讲到。

  (2) 手工同步:使用不同的同步类(诸如WaitHandle, Mutex, ReaderWriterLock, ManualResetEvent, AutoResetEvent 和Interlocked等)创建自己的同步机制。这种同步方式要求你自己手动的为不同的域和方法同步,这种同步方式也可以用于进程间的同步和对共享资源的等待而造成的死锁解除。

  (3) 上下文同步:使用SynchronizationAttribute为ContextBoundObject对象创建简单的,自动的同步。这种同步方式仅用于实例化的方法和域的同步。所有在同一个上下文域的对象共享同一个锁。
Monitor Class

  在给定的时间和指定的代码段只能被一个线程访问,Monitor 类非常适合于这种情况的线程同步。这个类中的方法都是静态的,所以不需要实例化这个类。下面一些静态的方法提供了一种机制用来同步对象的访问从而避免死锁和维护数据的一致性。

  Monitor.Enter 方法:在指定对象上获取排他锁。

  Monitor.TryEnter 方法:试图获取指定对象的排他锁。

  Monitor.Exit 方法:释放指定对象上的排他锁。

  Monitor.Wait 方法:释放对象上的锁并阻塞当前线程,直到它重新获取该锁。

  Monitor.Pulse 方法:通知等待队列中的线程锁定对象状态的更改。

  Monitor.PulseAll 方法:通知所有的等待线程对象状态的更改。

  通过对指定对象的加锁和解锁可以同步代码段的访问。Monitor.Enter, Monitor.TryEnter 和 Monitor.Exit用来对指定对象的加锁和解锁。一旦获取(调用了Monitor.Enter)指定对象(代码段)的锁,其他的线程都不能获取该锁。举个例子来说吧,线程X获得了一个对象锁,这个对象锁可以释放的(调用Monitor.Exit(object) or Monitor.Wait)。当这个对象锁被释放后,Monitor.Pulse方法和 Monitor.PulseAll方法通知就绪队列的下一个线程进行和其他所有就绪队列的线程将有机会获取排他锁。线程X释放了锁而线程Y获得了锁,同时调用Monitor.Wait的线程X进入等待队列。当从当前锁定对象的线程(线程Y)受到了Pulse或PulseAll,等待队列的线程就进入就绪队列。线程X重新得到对象锁时,Monitor.Wait才返回。如果拥有锁的线程(线程Y)不调用Pulse或PulseAll,方法可能被不确定的锁定。Pulse, PulseAll and Wait必须是被同步的代码段鄂被调用。对每一个同步的对象,你需要有当前拥有锁的线程的指针,就绪队列和等待队列(包含需要被通知锁定对象的状态变化的线程)的指针。

  你也许会问,当两个线程同时调用Monitor.Enter会发生什么事情?无论这两个线程地调用Monitor.Enter是多么地接近,实际上肯定有一个在前,一个在后,因此永远只会有一个获得对象锁。既然Monitor.Enter是原子操作,那么CPU是不可能偏好一个线程而不喜欢另外一个线程的。为了获取更好的性能,你应该延迟后一个线程的获取锁调用和立即释放前一个线程的对象锁。对于private和internal的对象,加锁是可行的,但是对于external对象有可能导致死锁,因为不相关的代码可能因为不同的目的而对同一个对象加锁。

  如果你要对一段代码加锁,最好的是在try语句里面加入设置锁的语句,而将Monitor.Exit放在finally语句里面。对于整个代码段的加锁,你可以使用MethodImplAttribute(在System.Runtime.CompilerServices命名空间)类在其构造器中设置同步值。这是一种可以替代的方法,当加锁的方法返回时,锁也就被释放了。如果需要要很快释放锁,你可以使用Monitor类和C# lock的声明代替上述的方法。

  让我们来看一段使用Monitor类的代码:
 

public void some_method()
{

int a=100;

int b=0;

Monitor.Enter(this);

//say we do something here.

int c=a/b;

Monitor.Exit(this);

}

  上面的代码运行会产生问题。当代码运行到int c=a/b; 的时候,会抛出一个异常,Monitor.Exit将不会返回。因此这段程序将挂起,其他的线程也将得不到锁。有两种方法可以解决上面的问题。第一个方法是:将代码放入try…finally内,在finally调用Monitor.Exit,这样的话最后一定会释放锁。第二种方法是:利用C#的lock()方法。调用这个方法和调用Monitoy.Enter的作用效果是一样的。但是这种方法一旦代码执行超出范围,释放锁将不会自动的发生。见下面的代码:
 

public void some_method()
{

int a=100;

int b=0;

lock(this);

//say we do something here.

int c=a/b;

}

  C# lock申明提供了与Monitoy.Enter和Monitoy.Exit同样的功能,这种方法用在你的代码段不能被其他独立的线程中断的情况。
WaitHandle Class

  WaitHandle类作为基类来使用的,它允许多个等待操作。这个类封装了win32的同步处理方法。WaitHandle对象通知其他的线程它需要对资源排他性的访问,其他的线程必须等待,直到WaitHandle不再使用资源和等待句柄没有被使用。下面是从它继承来的几个类:

  Mutex 类:同步基元也可用于进程间同步。

  AutoResetEvent:通知一个或多个正在等待的线程已发生事件。无法继承此类。

  ManualResetEvent:当通知一个或多个正在等待的线程事件已发生时出现。无法继承此类。

  这些类定义了一些信号机制使得对资源排他性访问的占有和释放。他们有两种状态:signaled 和 nonsignaled。Signaled状态的等待句柄不属于任何线程,除非是nonsignaled状态。拥有等待句柄的线程不再使用等待句柄时用set方法,其他的线程可以调用Reset方法来改变状态或者任意一个WaitHandle方法要求拥有等待句柄,这些方法见下面:

  WaitAll:等待指定数组中的所有元素收到信号。

  WaitAny:等待指定数组中的任一元素收到信号。

  WaitOne:当在派生类中重写时,阻塞当前线程,直到当前的 WaitHandle 收到信号。

  这些wait方法阻塞线程直到一个或者更多的同步对象收到信号。

  WaitHandle对象封装等待对共享资源的独占访问权的操作系统特定的对象无论是收管代码还是非受管代码都可以使用。但是它没有Monitor使用轻便,Monitor是完全的受管代码而且对操作系统资源的使用非常有效率。

Mutex Class

  Mutex是另外一种完成线程间和跨进程同步的方法,它同时也提供进程间的同步。它允许一个线程独占共享资源的同时阻止其他线程和进程的访问。Mutex的名字就很好的说明了它的所有者对资源的排他性的占有。一旦一个线程拥有了Mutex,想得到Mutex的其他线程都将挂起直到占有线程释放它。Mutex.ReleaseMutex方法用于释放Mutex,一个线程可以多次调用wait方法来请求同一个Mutex,但是在释放Mutex的时候必须调用同样次数的Mutex.ReleaseMutex。如果没有线程占有Mutex,那么Mutex的状态就变为signaled,否则为nosignaled。一旦Mutex的状态变为signaled,等待队列的下一个线程将会得到Mutex。Mutex类对应与win32的CreateMutex,创建Mutex对象的方法非常简单,常用的有下面几种方法:

  一个线程可以通过调用WaitHandle.WaitOne 或 WaitHandle.WaitAny 或 WaitHandle.WaitAll得到Mutex的拥有权。如果Mutex不属于任何线程,上述调用将使得线程拥有Mutex,而且WaitOne会立即返回。但是如果有其他的线程拥有Mutex,WaitOne将陷入无限期的等待直到获取Mutex。你可以在WaitOne方法中指定参数即等待的时间而避免无限期的等待Mutex。调用Close作用于Mutex将释放拥有。一旦Mutex被创建,你可以通过GetHandle方法获得Mutex的句柄而给WaitHandle.WaitAny 或 WaitHandle.WaitAll 方法使用。

  下面是一个示例:
 

public void some_method()
{

int a=100;

int b=20;

Mutex firstMutex = new Mutex(false);

FirstMutex.WaitOne();

//some kind of processing can be done here.

Int x=a/b;

FirstMutex.Close();

}

  在上面的例子中,线程创建了Mutex,但是开始并没有申明拥有它,通过调用WaitOne方法拥有Mutex。
Synchronization Events

  同步时间是一些等待句柄用来通知其他的线程发生了什么事情和资源是可用的。他们有两个状态:signaled and nonsignaled。AutoResetEvent 和 ManualResetEvent就是这种同步事件。

AutoResetEvent Class

  这个类可以通知一个或多个线程发生事件。当一个等待线程得到释放时,它将状态转换为signaled。用set方法使它的实例状态变为signaled。但是一旦等待的线程被通知时间变为signaled,它的转台将自动的变为nonsignaled。如果没有线程侦听事件,转台将保持为signaled。此类不能被继承。

ManualResetEvent Class

  这个类也用来通知一个或多个线程事件发生了。它的状态可以手动的被设置和重置。手动重置时间将保持signaled状态直到ManualResetEvent.Reset设置其状态为nonsignaled,或保持状态为nonsignaled直到ManualResetEvent.Set设置其状态为signaled。这个类不能被继承。

Interlocked Class

  它提供了在线程之间共享的变量访问的同步,它的操作时原子操作,且被线程共享.你可以通过Interlocked.Increment 或 Interlocked.Decrement来增加或减少共享变量.它的有点在于是原子操作,也就是说这些方法可以代一个整型的参数增量并且返回新的值,所有的操作就是一步.你也可以使用它来指定变量的值或者检查两个变量是否相等,如果相等,将用指定的值代替其中一个变量的值.

ReaderWriterLock class

  它定义了一种锁,提供唯一写/多读的机制,使得读写的同步.任意数目的线程都可以读数据,数据锁在有线程更新数据时将是需要的.读的线程可以获取锁,当且仅当这里没有写的线程.当没有读线程和其他的写线程时,写线程可以得到锁.因此,一旦writer-lock被请求,所有的读线程将不能读取数据直到写线程访问完毕.它支持暂停而避免死锁.它也支持嵌套的读/写锁.支持嵌套的读锁的方法是ReaderWriterLock.AcquireReaderLock,如果一个线程有写锁则该线程将暂停;

  支持嵌套的写锁的方法是ReaderWriterLock.AcquireWriterLock,如果一个线程有读锁则该线程暂停.如果有读锁将容易倒是死锁.安全的办法是使用ReaderWriterLock.UpgradeToWriterLock方法,这将使读者升级到写者.你可以用ReaderWriterLock.DowngradeFromWriterLock方法使写者降级为读者.调用ReaderWriterLock.ReleaseLock将释放锁, ReaderWriterLock.RestoreLock将重新装载锁的状态到调用ReaderWriterLock.ReleaseLock以前.

结论:

  这部分讲述了.NET平台上的线程同步的问题.造接下来的系列文章中我将给出一些例子来更进一步的说明这些使用的方法和技巧.虽然线程同步的使用会给我们的程序带来很大的价值,但是我们最好能够小心使用这些方法.否则带来的不是受益,而将倒是性能下降甚至程序崩溃.只有大量的联系和体会才能使你驾驭这些技巧.尽量少使用那些在同步代码块完成不了或者不确定的阻塞的东西,尤其是I/O操作;尽可能的使用局部变量来代替全局变量;同步用在那些部分代码被多个线程和进程访问和状态被不同的进程共享的地方;安排你的代码使得每一个数据在一个线程里得到精确的控制;不是共享在线程之间的代码是安全的;在下一篇文章中我们将学习线程池有关的知识.

 

----------------------------------------------------------------------------------------------------------

C# 多线程控制控件实例(例程简单,注释详细)

该实例功能为“多线程控制UI控件”,线程函数实现自动加1。界面如下:

 

 

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace treadTest
{   
    //定义委托
    public delegate void ListBoxDelegate();
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //委托处理方法(关联与ListBoxDelegate)
        private void ListBox()
        {
            if (!listBox1.InvokeRequired)//如果在UI主线程操作ListBox,
            {
                listBox1.Items.Add(++CommonData.num);//则直接进行控件操作,“与UI主线程相关联”
                listBox1.SelectedItem = listBox1.Items[listBox1.Items.Count - 1];
            }
            else//如果是在另一线程操作ListBox,则启用委托
                listBox1.Invoke(new ListBoxDelegate(listShow));
        }
       
        //定义对UI主线程控件的操作,“与AddAuto相关联”。
        private void listShow()
        {
            listBox1.Items.Add(CommonData.num);
            listBox1.SelectedItem = listBox1.Items[listBox1.Items.Count - 1];
        }
        //定义线程函数
        private void AddAuto()
        {
            while (CommonData.Flag == 0)
            {
                CommonData.num++;
                Thread.Sleep(1000);
                ListBox();//不能直接控制UI上的控件,所以用该方法选择使用委托
            }
        }
        //在click事件中启动多线程
        private void btnStart_Click(object sender, EventArgs e)
        {
            //线程标志置0,表示开启线程
            CommonData.Flag = 0;
            //定义 ThreadStart的委托类型的参数,并使该委托指向线程函数
            ListBoxDelegate mycn = new ListBoxDelegate(AddAuto);
            //实例化线程
            Thread insertTxt = new Thread(new ThreadStart(mycn));
            //启动线程
            insertTxt.Start();     
        }

        private void btnAbort_Click(object sender, EventArgs e)
        {
            CommonData.Flag = 1;
        }
        private void btnCtrlMain_Click(object sender, EventArgs e)
        {
            ListBox();
        }
        private void btnReset_Click(object sender, EventArgs e)
        {
            CommonData.num = 0;
        }
        private void btnClear_Click(object sender, EventArgs e)
        {
            listBox1.Items.Clear();
        }
        private void btnQuit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

    }
   
    //全局变量解决方案
    public class CommonData
    {
        private static int _Flag = 0;
        private static int _num = 0;
        public static int Flag
        {
            get { return _Flag; }
            set { _Flag = value; }
        }
        public static int num
        {
            get { return _num; }
            set { _num = value; }
        }
    }
}

 

总结:

        要使用多线程控制UI控件,必须用委托实现。调用控件的Invoke方法(Invoke方法的参数是一个委托类型的参数)。

实现步骤:

         1.声明委托。

          2.声明委托处理函数(判断是主线程控制UI控件,还是Invoke(多线程)控制UI控件)。

         3.声明一个线程实例,将线程函数的委托传入ThreadStart()。

         4.开启该线程。

         5.定义该线程函数,欲控制UI控件,则调用第2步的委托处理函数,他将自己判断选择用Invoke。

         6.定义Invoke需要调用的函数(如本例的listShow函数)

//*********************************************************************************************************************************

      在上例中,只是完成了多线程控制主线程控件的功能,如果能手动和自动同时访问全局变量时,就有可能出现线程不同步的问题。以下主要利用lock线程锁来修改解决方案,使线程同步,注意代码带动的地方。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace treadTest
{   
    //定义委托
    public delegate void ListBoxDelegate();
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //委托处理方法(关联与ListBoxDelegate)
        private void ListBox()
        {
            if (!listBox1.InvokeRequired)//如果在UI主线程操作ListBox,
            {
                listBox1.Items.Add(CommonData.plus());//则直接进行控件操作,“与UI主线程相关联”
                listBox1.SelectedItem = listBox1.Items[listBox1.Items.Count - 1];
            }
            else//如果是在另一线程操作ListBox,则启用委托
                listBox1.Invoke(new ListBoxDelegate(listShow));
        }
       
        //定义对UI主线程控件的操作,“与AddAuto相关联”。
        private void listShow()
        {
            listBox1.Items.Add(CommonData.plus());
            listBox1.SelectedItem = listBox1.Items[listBox1.Items.Count - 1];
        }
        //定义线程函数
        private void AddAuto()
        {
            while (CommonData.Flag == 0)
            {
                Thread.Sleep(1000);
                ListBox();//不能直接控制UI上的控件,所以用该方法选择使用委托
            }
        }
        //在click事件中启动多线程
        private void btnStart_Click(object sender, EventArgs e)
        {
            //线程标志置0,表示开启线程
            CommonData.Flag = 0;
            //定义 ThreadStart的委托类型的参数,并使该委托指向线程函数
            ListBoxDelegate mycn = new ListBoxDelegate(AddAuto);
            //实例化线程
            Thread insertTxt = new Thread(new ThreadStart(mycn));
            //启动线程
            insertTxt.Start();     
        }

        private void btnAbort_Click(object sender, EventArgs e)
        {
            CommonData.Flag = 1;
        }
        private void btnCtrlMain_Click(object sender, EventArgs e)
        {
            ListBox();
        }
        private void btnReset_Click(object sender, EventArgs e)
        {
            CommonData.num = 0;
        }
        private void btnClear_Click(object sender, EventArgs e)
        {
            listBox1.Items.Clear();
        }
        private void btnQuit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
   
    //全局变量解决方案
    public class CommonData
    {
        private static int _Flag = 0;
        private static int _num = 0;
        public static int plus()
        {
            lock (new object())
            {
                return _num++;
            }
        }
        public static int Flag
        {
            get { return _Flag; }
            set { _Flag = value; }
        }
        public static int num
        {
            get { return _num; }
            set { _num = value; }
        }
    }
}

点击查看原图
发表在 article | 标签为 , | C# 线程池示例已关闭评论

.Net线程问题解答

目录

 

 

基础篇

怎样创建一个线程
受托管的线程与 Windows线程
前台线程与后台线程
名为BeginXXX和EndXXX的方法是做什么用的
异步和多线程有什么关联
WinForm多线程编程篇

我的多线程WinForm程序老是抛出InvalidOperationException ,怎么解决?
Invoke,BeginInvoke干什么用的,内部是怎么实现的
每个线程都有消息队列吗?
为什么Winform不允许跨线程修改UI线程控件的值
有没有什么办法可以简化WinForm多线程的开发
线程池

线程池的作用是什么?
所有进程使用一个共享的线程池,还是每个进程使用独立的线程池?
为什么不要手动线程池设置最大值?
.Net线程池有什么不足?
同步

CLR怎样实现lock(obj)锁定?
WaitHandle是什么,他和他的派生类怎么使用
什么是用双锁实现Singleton,为什么要这样做,为什么有人说双锁检验是不安全的
互斥对象(Mutex)、事件(Event)对象与lock语句的比较
什么时候需要锁定

只有共享资源才需要锁定
把锁定交给数据库
了解你的程序是怎么运行的
业务逻辑对事务和线程安全的要求
计算一下冲突的可能性
请多使用lock,少用Mutex
Web和IIS

应用程序池,WebApplication,和线程池之间有什么关系
Web页面怎么调用异步WebService
 

基础篇

 怎样创建一个线程

我只简单列举几种常用的方法,详细可参考.Net多线程总结(一)

一)使用Thread类

ThreadStart threadStart=new ThreadStart(Calculate);//通过ThreadStart委托告诉子线程讲执行什么方法,这里执行一个计算圆周长的方法
Thread thread=new Thread(threadStart);
thread.Start(); //启动新线程

public void Calculate(){
double Diameter=0.5;
Console.Write("The perimeter Of Circle with a Diameter of {0} is {1}"Diameter,Diameter*Math.PI);
}

二)使用Delegate.BeginInvoke

delegate double CalculateMethod(double Diameter); //申明一个委托,表明需要在子线程上执行的方法的函数签名
static CalculateMethod calcMethod = new CalculateMethod(Calculate);//把委托和具体的方法关联起来
static void Main(string[] args)
{
//此处开始异步执行,并且可以给出一个回调函数(如果不需要执行什么后续操作也可以不使用回调)
calcMethod.BeginInvoke(5, new AsyncCallback(TaskFinished), null);
Console.ReadLine();
}

//线程调用的函数,给出直径作为参数,计算周长
public static double Calculate(double Diameter)
{
    return Diameter * Math.PI;
}

//线程完成之后回调的函数
public static void TaskFinished(IAsyncResult result)
{
    double re = 0;
    re = calcMethod.EndInvoke(result);
    Console.WriteLine(re);
}

三)使用ThreadPool.QueueworkItem

WaitCallback w = new WaitCallback(Calculate);
//下面启动四个线程,计算四个直径下的圆周长
ThreadPool.QueueUserWorkItem(w, 1.0);
ThreadPool.QueueUserWorkItem(w, 2.0);
ThreadPool.QueueUserWorkItem(w, 3.0);
ThreadPool.QueueUserWorkItem(w, 4.0);
public static void Calculate(double Diameter)
{
return Diameter * Math.PI;
}

下面两条来自于http://www.cnblogs.com/tonyman/archive/2007/09/13/891912.html

  受托管的线程与 Windows线程

必须要了解,执行.NET应用的线程实际上仍然是Windows线程。但是,当某个线程被CLR所知时,我们将它称为受托管的线程。具体来说,由受托管的代码创建出来的线程就是受托管的线程。如果一个线程由非托管的代码所创建,那么它就是非托管的线程。不过,一旦该线程执行了受托管的代码它就变成了受托管的线程。

一个受托管的线程和非托管的线程的区别在于,CLR将创建一个System.Threading.Thread类的实例来代表并操作前者。在内部实现中,CLR将一个包含了所有受托管线程的列表保存在一个叫做ThreadStore地方。

CLR确保每一个受托管的线程在任意时刻都在一个AppDomain中执行,但是这并不代表一个线程将永远处在一个AppDomain中,它可以随着时间的推移转到其他的AppDomain中。

从安全的角度来看,一个受托管的线程的主用户与底层的非托管线程中的Windows主用户是无关的。
 

  前台线程与后台线程

启动了多个线程的程序在关闭的时候却出现了问题,如果程序退出的时候不关闭线程,那么线程就会一直的存在,但是大多启动的线程都是局部变量,不能一一的关闭,如果调用Thread.CurrentThread.Abort()方法关闭主线程的话,就会出现ThreadAbortException 异常,因此这样不行。
后来找到了这个办法: Thread.IsBackground 设置线程为后台线程。
 
msdn对前台线程和后台线程的解释:托管线程或者是后台线程,或者是前台线程。后台线程不会使托管执行环境处于活动状态,除此之外,后台线程与前台线程是一样的。一旦所有前台线程在托管进程(其中 .exe 文件是托管程序集)中被停止,系统将停止所有后台线程并关闭。通过设置 Thread.IsBackground 属性,可以将一个线程指定为后台线程或前台线程。例如,通过将 Thread.IsBackground 设置为 true,就可以将线程指定为后台线程。同样,通过将 IsBackground 设置为 false,就可以将线程指定为前台线程。从非托管代码进入托管执行环境的所有线程都被标记为后台线程。通过创建并启动新的 Thread 对象而生成的所有线程都是前台线程。如果要创建希望用来侦听某些活动(如套接字连接)的前台线程,则应将 Thread.IsBackground 设置为 true,以便进程可以终止。
所以解决办法就是在主线程初始化的时候,设置:Thread.CurrentThread.IsBackground = true;

这样,主线程就是后台线程,在关闭主程序的时候就会关闭主线程,从而关闭所有线程。但是这样的话,就会强制关闭所有正在执行的线程,所以在关闭的时候要对线程工作的结果保存。

 

经常看到名为BeginXXX和EndXXX的方法,他们是做什么用的

这是.net的一个异步方法名称规范
.Net在设计的时候为异步编程设计了一个异步编程模型(APM),这个模型不仅是使用.NET的开发人员使用,.Net内部也频繁用到,比如所有的Stream就有BeginRead,EndRead,Socket,WebRequet,SqlCommand都运用到了这个模式,一般来讲,调用BegionXXX的时候,一般会启动一个异步过程去执行一个操作,EndEnvoke可以接收这个异步操作的返回,当然如果异步操作在EndEnvoke调用的时候还没有执行完成,EndInvoke会一直等待异步操作完成或者超时

.Net的异步编程模型(APM)一般包含BeginXXX,EndXXX,IAsyncResult这三个元素,BeginXXX方法都要返回一个IAsyncResult,而EndXXX都需要接收一个IAsyncResult作为参数,他们的函数签名模式如下

IAsyncResult BeginXXX(...);

<返回类型> EndXXX(IAsyncResult ar);

BeginXXX和EndXXX中的XXX,一般都对应一个同步的方法,比如FileStream的Read方法是一个同步方法,相应的BeginRead(),EndRead()就是他的异步版本,HttpRequest有GetResponse来同步接收一个响应,也提供了BeginGetResponse和EndGetResponse这个异步版本,而IAsynResult是二者联系的纽带,只有把BeginXXX所返回的IAsyncResult传给对应的EndXXX,EndXXX才知道需要去接收哪个BeginXXX发起的异步操作的返回值。

这个模式在实际使用时稍显繁琐,虽然原则上我们可以随时调用EndInvoke来获得返回值,并且可以同步多个线程,但是大多数情况下当我们不需要同步很多线程的时候使用回调是更好的选择,在这种情况下三个元素中的IAsynResult就显得多余,我们一不需要用其中的线程完结标志来判断线程是否成功完成(回调的时候线程应该已经完成了),二不需要他来传递数据,因为数据可以写在任何变量里,并且回调时应该已经填充,所以可以看到微软在新的.Net Framework中已经加强了对回调事件的支持,这总模型下,典型的回调程序应该这样写

a.DoWork+=new SomeEventHandler(Caculate);
a.CallBack+=new SomeEventHandler(callback);
a.Run();

(注:我上面讲的是普遍的用法,然而BeginXXX,EndXXX仅仅是一种模式,而对这个模式的实现完全取决于使用他的开发人员,具体实现的时候你可以使用另外一个线程来实现异步,也可能使用硬件的支持来实现异步,甚至可能根本和异步没有关系(尽管几乎没有人会这样做)-----比如直接在Beginxxx里直接输出一个"Helloworld",如果是这种极端的情况,那么上面说的一切都是废话,所以上面的探讨并不涉及内部实现,只是告诉大家微软的模式,和框架中对这个模式的经典实现)

 

 

异步和多线程有什么关联

有一句话总结的很好:多线程是实现异步的一种手段和工具

我们通常把多线程和异步等同起来,实际是一种误解,在实际实现的时候,异步有许多种实现方法,我们可以用进程来做异步,或者使用纤程,或者硬件的一些特性,比如在实现异步IO的时候,可以有下面两个方案:

1)可以通过初始化一个子线程,然后在子线程里进行IO,而让主线程顺利往下执行,当子线程执行完毕就回调

2)也可以根本不使用新线程,而使用硬件的支持(现在许多硬件都有自己的处理器),来实现完全的异步,这是我们只需要将IO请求告知硬件驱动程序,然后迅速返回,然后等着硬件IO就绪通知我们就可以了

实际上DotNet Framework里面就有这样的例子,当我们使用文件流的时候,如果制定文件流属性为同步,则使用BeginRead进行读取时,就是用一个子线程来调用同步的Read方法,而如果指定其为异步,则同样操作时就使用了需要硬件和操作系统支持的所谓IOCP的机制

 

 

WinForm多线程编程篇

 

我的多线程WinForm程序老是抛出InvalidOperationException ,怎么解决?

在WinForm中使用多线程时,常常遇到一个问题,当在子线程(非UI线程)中修改一个控件的值:比如修改进度条进度,时会抛出如下错误

Cross-thread operation not valid: Control 'XXX' accessed from a thread other than the thread it was created on.

在VS2005或者更高版本中,只要不是在控件的创建线程(一般就是指UI主线程)上访问控件的属性就会抛出这个错误,解决方法就是利用控件提供的Invoke和BeginInvoke把调用封送回UI线程,也就是让控件属性修改在UI线程上执行,下面列出会报错的代码和他的修改版本

ThreadStart threadStart=new ThreadStart(Calculate);//通过ThreadStart委托告诉子线程讲执行什么方法
Thread thread=new Thread(threadStart);
thread.Start();
public void Calculate(){
    double Diameter=0.5;
    double result=Diameter*Math.PI;
    CalcFinished(result);//计算完成需要在一个文本框里显示
}
public void CalcFinished(double result){
    this.TextBox1.Text=result.ToString();//会抛出错误
}
上面加粗的地方在debug的时候会报错,最直接的修改方法是修改Calculate这个方法如下

delegate void changeText(double result);

public void Calculate(){
    double Diameter=0.5;
    double result=Diameter*Math.PI;
    this.BeginInvoke(new changeText(CalcFinished),t.Result);//计算完成需要在一个文本框里显示
}

这样就ok了,但是最漂亮的方法是不去修改Calculate,而去修改CalcFinished这个方法,因为程序里调用这个方法的地方可能很多,由于加了是否需要封送的判断,这样修改还能提高非跨线程调用时的性能

delegate void changeText(double result);

public void CalcFinished(double result){
    if(this.InvokeRequired){
        this.BeginInvoke(new changeText(CalcFinished),t.Result);
    }
    else{
        this.TextBox1.Text=result.ToString();
    }
}
上面的做法用到了Control的一个属性InvokeRequired(这个属性是可以在其他线程里访问的),这个属性表明调用是否来自另非UI线程,如果是,则使用BeginInvoke来调用这个函数,否则就直接调用,省去线程封送的过程

 

Invoke,BeginInvoke干什么用的,内部是怎么实现的?
这两个方法主要是让给出的方法在控件创建的线程上执行

Invoke使用了Win32API的SendMessage,

UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);

BeginInvoke使用了Win32API的PostMessage

UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);

这两个方法向UI线程的消息队列中放入一个消息,当UI线程处理这个消息时,就会在自己的上下文中执行传入的方法,换句话说凡是使用BeginInvoke和Invoke调用的线程都是在UI主线程中执行的,所以如果这些方法里涉及一些静态变量,不用考虑加锁的问题

 

每个线程都有消息队列吗?
不是,只有创建了窗体对象的线程才会有消息队列(下面给出<Windows 核心编程>关于这一段的描述)

当一个线程第一次被建立时,系统假定线程不会被用于任何与用户相关的任务。这样可以减少线程对系统资源的要求。但是,一旦这个线程调用一个与图形用户界面有关的函数(例如检查它的消息队列或建立一个窗口),系统就会为该线程分配一些另外的资源,以便它能够执行与用户界面有关的任务。特别是,系统分配一个T H R E A D I N F O结构,并将这个数据结构与线程联系起来。

这个T H R E A D I N F O结构包含一组成员变量,利用这组成员,线程可以认为它是在自己独占的环境中运行。T H R E A D I N F O是一个内部的、未公开的数据结构,用来指定线程的登记消息队列(posted-message queue)、发送消息队列( send-message queue)、应答消息队列( r e p l y -message queue)、虚拟输入队列(virtualized-input queue)、唤醒标志(wake flag)、以及用来描述线程局部输入状态的若干变量。图2 6 - 1描述了T H R E A D I N F O结构和与之相联系的三个线程。

 

 

为什么Winform不允许跨线程修改UI线程控件的值
在vs2003下,使用子线程调用ui线程创建的控件的属性是不会有问题的,但是编译的时候会出现警告,但是vs2005及以上版本就会有这样的问题,下面是msdn上的描述

"当您在 Visual Studio 调试器中运行代码时,如果您从一个线程访问某个 UI 元素,而该线程不是创建该 UI 元素时所在的线程,则会引发 InvalidOperationException。调试器引发该异常以警告您存在危险的编程操作。UI 元素不是线程安全的,所以只应在创建它们的线程上进行访问"

从上面可以看出,这个异常实际是debugger耍的花招,也就是说,如果你直接运行程序的exe文件,或者利用运行而不调试(Ctrl+F5)来运行你的程序,是不会抛出这样的异常的.大概ms发现v2003的警告对广大开发者不起作用,所以用了一个比较狠一点的方法.

不过问题依然存在:既然这样设计的原因主要是因为控件的值非线程安全,那么DotNet framework中非线程安全的类千千万万,为什么偏偏跨线程修改Control的属性会有这样严格的限制策略呢?

这个问题我还回答不好,希望博友们能够予以补充

 

有没有什么办法可以简化WinForm多线程的开发
使用backgroundworker,使用这个组建可以避免回调时的Invoke和BeginInvoke,并且提供了许多丰富的方法和事件

参见.Net多线程总结(二)-BackgroundWorker,我在这里不再赘诉

 

线程池

线程池的作用是什么

作用是减小线程创建和销毁的开销

创建线程涉及用户模式和内核模式的切换,内存分配,dll通知等一系列过程,线程销毁的步骤也是开销很大的,所以如果应用程序使用了完一个线程,我们能把线程暂时存放起来,以备下次使用,就可以减小这些开销

所有进程使用一个共享的线程池,还是每个进程使用独立的线程池?

每个进程都有一个线程池,一个Process中只能有一个实例,它在各个应用程序域(AppDomain)是共享的,.Net2.0 中默认线程池的大小为工作线程25个,IO线程1000个,有一个比较普遍的误解是线程池中会有1000个线程等着你去取,其实不然, ThreadPool仅仅保留相当少的线程,保留的线程可以用SetMinThread这个方法来设置,当程序的某个地方需要创建一个线程来完成工作时,而线程池中又没有空闲线程时,线程池就会负责创建这个线程,并且在调用完毕后,不会立刻销毁,而是把他放在池子里,预备下次使用,同时如果线程超过一定时间没有被使用,线程池将会回收线程,所以线程池里存在的线程数实际是个动态的过程

为什么不要手动线程池设置最大值?

当我首次看到线程池的时候,脑袋里的第一个念头就是给他设定一个最大值,然而当我们查看ThreadPool的SetMaxThreads文档时往往会看到一条警告:不要手动更改线程池的大小,这是为什么呢?

其实无论FileStream的异步读写,异步发送接受Web请求,甚至使用delegate的beginInvoke都会默认调用 ThreadPool,也就是说不仅你的代码可能使用到线程池,框架内部也可能使用到,更改的后果影响就非常大,特别在iis中,一个应用程序池中的所有 WebApplication会共享一个线程池,对最大值的设定会带来很多意想不到的麻烦

线程池的线程为何要分类?

线程池有一个方法可以让我们看到线程池中可用的线程数量:GetAvaliableThread(out workerThreadCount,out iocompletedThreadCount),对于我来说,第一次看到这个函数的参数时十分困惑,因为我期望这个函数直接返回一个整形,表明还剩多少线程,这个函数居然一次返回了两个变量.

原来线程池里的线程按照公用被分成了两大类:工作线程和IO线程,或者IO完成线程,前者用于执行普通的操作,后者专用于异步IO,比如文件和网络请求,注意,分类并不说明两种线程本身有差别,线程就是线程,是一种执行单元,从本质上来讲都是一样的,线程池这样分类,举例来说,就好像某施工工地现在有1000把铁锹,规定其中25把给后勤部门用,其他都给施工部门,施工部门需要大量使用铁锹来挖地基(例子土了点,不过说明问题还是有效的),后勤部门用铁锹也就是铲铲雪,铲铲垃圾,给工人师傅修修临时住房,所以用量不大,显然两个部门的铁锹本身没有区别,但是这样的划分就为管理两个部门的铁锹提供了方便

线程池中两种线程分别在什么情况下被使用,二者工作原理有什么不同?

下面这个例子直接说明了二者的区别,我们用一个流读出一个很大的文件(大一点操作的时间长,便于观察),然后用另一个输出流把所读出的文件的一部分写到磁盘上

我们用两种方法创建输出流,分别是

创建了一个异步的流(注意构造函数最后那个true)

FileStream outputfs=new FileStream(writepath, FileMode.Create, FileAccess.Write, FileShare.None,256,true);

创建了一个同步的流

FileStream outputfs = File.OpenWrite(writepath);

 然后在写文件期间查看线程池的状况

string readpath = "e:\\RHEL4-U4-i386-AS-disc1.iso";
string writepath = "e:\\kakakak.iso";
byte[] buffer = new byte[90000000];

//FileStream outputfs=new FileStream(writepath, FileMode.Create, FileAccess.Write, FileShare.None,256,true);
//Console.WriteLine("异步流");
//创建了一个同步的流

FileStream outputfs = File.OpenWrite(writepath);
Console.WriteLine("同步流");

 //然后在写文件期间查看线程池的状况

ShowThreadDetail("初始状态");

FileStream fs = File.OpenRead(readpath);

fs.BeginRead(buffer, 0, 90000000, delegate(IAsyncResult o)
{

    outputfs.BeginWrite(buffer, 0, buffer.Length,

    delegate(IAsyncResult o1)
    {

        Thread.Sleep(1000);

        ShowThreadDetail("BeginWrite的回调线程");

    }, null);

    Thread.Sleep(500);//this is important cause without this, this Thread and the one used for BeginRead May seem to be same one
},

null);

Console.ReadLine();

public static void ShowThreadDetail(string caller)
{
    int IO;
    int Worker;
    ThreadPool.GetAvailableThreads(out Worker, out IO);
    Console.WriteLine("Worker: {0}; IO: {1}", Worker, IO);
}

输出结果
异步流
Worker: 500; IO: 1000
Worker: 500; IO: 999
同步流
Worker: 500; IO: 1000
Worker: 499; IO: 1000
 
这两个构造函数创建的流都可以使用BeginWrite来异步写数据,但是二者行为不同,当使用同步的流进行异步写时,通过回调的输出我们可以看到,他使用的是工作线程,而非IO线程,而异步流使用了IO线程而非工作线程

其实当没有制定异步属性的时候,.Net实现异步IO是用一个子线程调用fs的同步Write方法来实现的,这时这个子线程会一直阻塞直到调用完成.这个子线程其实就是线程池的一个工作线程,所以我们可以看到,同步流的异步写回调中输出的工作线程数少了一,而使用异步流,在进行异步写时,采用了 IOCP方法,简单说来,就是当BeginWrite执行时,把信息传给硬件驱动程序,然后立即往下执行(注意这里没有额外的线程),而当硬件准备就绪, 就会通知线程池,使用一个IO线程来读取

.Net线程池有什么不足
没有提供方法控制加入线程池的线程:一旦加入线程池,我们没有办法挂起,终止这些线程,唯一可以做的就是等他自己执行

1)不能为线程设置优先级
2)一个Process中只能有一个实例,它在各个AppDomain是共享的。ThreadPool只提供了静态方法,不仅我们自己添加进去的WorkItem使用这个Pool,而且.net framework中那些BeginXXX、EndXXX之类的方法都会使用此Pool。
3)所支持的Callback不能有返回值。WaitCallback只能带一个object类型的参数,没有任何返回值。
4)不适合用在长期执行某任务的场合。我们常常需要做一个Service来提供不间断的服务(除非服务器down掉),但是使用ThreadPool并不合适。

下面是另外一个网友总结的什么不需要使用线程池,我觉得挺好,引用下来
如果您需要使一个任务具有特定的优先级。
如果您具有可能会长时间运行(并因此阻塞其他任务)的任务。
如果您需要将线程放置到单线程单元中(所有 ThreadPool 线程均处于多线程单元中)。
如果您需要与该线程关联的稳定标识。例如,您应使用一个专用线程来中止该线程、将其挂起或按名称发现它。

 

 

锁定与同步

CLR怎样实现lock(obj)锁定?

从原理上讲,lock和Syncronized Attribute都是用Moniter.Enter实现的,比如如下代码

 

object lockobj=new object();
lock(obj){ 
//do things
}

在编译时,会被编译为类似

try{
  Moniter.Enter(obj){
   //do things
  }
}
catch{}
finally{
  Moniter.Exit(obj);
}

而[MethodImpl(MethodImplOptions.Synchronized)]标记为同步的方法会在编译时被lock(this)语句所环绕
所以我们只简单探讨Moniter.Enter的实现

(注:DotNet并非使用Win32API的CriticalSection来实现Moniter.Enter,不过他为托管对象提供了一个类似的结构叫做Syncblk)

每个对象实例头部都有一个指针,这个指针指向的结构,包含了对象的锁定信息,当第一次使用Moniter.Enter(obj)时,这个obj对象的锁定结构就会被初时化,第二次调用Moniter.Enter时,会检验这个object的锁定结构,如果锁没有被释放,则调用会阻塞

 

 

WaitHandle是什么,他和他的派生类怎么使用

  WaitHandle是Mutex,Semaphore,EventWaitHandler,AutoResetEvent,ManualResetEvent共同的祖先,他们包装了用于同步的内核对象,也就是说是这些内核对象的托管版本。

  Mutex:类似于一个接力棒,拿到接力棒的线程才可以开始跑,当然接力棒一次只属于一个线程(Thread Affinity),如果这个线程不释放接力棒(Mutex.ReleaseMutex),那么没办法,其他所有需要接力棒运行的线程都知道能等着看热闹

Semaphore:类似于一个小桶,里面装了几个小球,凡是拿到小球就可以跑,比如指定小桶里最初有四个小球,那么开始的四个线程就可以直接拿着自己的小球开跑,但是第五个线程一看,小球被拿光了,就只好乖乖的等着有谁放一个小球到小桶里(Semophore.Release),他才能跑,但是这里的游戏规则比较特殊,我们可以随意向小桶里放入小球,也就是说我可以拿走一个小球,放回去俩,甚至一个都不拿,放回去5个,这样就有五个线程可以拿着这些小球运行了.我们可以规定小桶里有开始有几个小球(构造函数的第一个参数),也可以规定最多不能超过多少小球(构造函数的第二个参数)

  ManualResetEvent,AutoResetEvent可以参考http://www.cnblogs.com/uubox/archive/2007/12/18/1003953.html

什么是用双锁实现Singleton,为什么要这样做,双锁检验是不安全的吗?

使用双锁检验技巧来实现单件,来自于Java社区

public static MySingleton Instance{
get{
    if(_instance!=null)}{
        lock(_instance){
            if(s_value==null){
                _instance= new MySingleton();
            }
        }
    }
}
}

 

这样做其实是为了提高效率,比起
public static MySingleton Instance{

get{

lock(_instance){

if(s_value==null){

_instance= new MySingleton();

}

}

前一种方法在instance创建的时候不需要用lock同步,从而增进了效率

在java中这种技巧被证明是不安全的详细见http://www.cs.umd.edu/~pugh/java/memoryModel/

但是在.Net下,这样的技巧是成立的,因为.Net使用了改进的内存模型

并且在.Net下,我们可以使用LazyInit来实现单件

private static readonly _instance=new MySingleton()

public static MySingleton Instance{

get{return _instance}

}

当第一此使用_instance时,CLR会生成这个对象,以后再访问这个字段,将会直接返回

互斥对象(Mutex),信号量(Semaphore),事件(Event)对象与lock语句的比较

首先这里所谓的事件对象不是System.Event,而是一种用于同步的内核机制

互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,线程必须要在用户模式和内核模式间切换,所以一般效率很低,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。

lock或者Moniter是.net用一个特殊结构实现的,不涉及模式切换,也就是说工作在用户方式下,同步速度较快,但是不能跨进程同步

 

什么时候需要锁定?
刚刚接触锁定的程序员往往觉得这个世界非常的危险,每个静态变量似乎都有可能产生竞争

首先锁定是解决竞争条件的,也就是多个线程同时访问某个资源,造成意想不到的结果,比如,最简单的情况,一个计数器,如果两个线程同时加一,后果就是损失了一个计数,但是频繁的锁定又可能带来性能上的消耗,还有最可怕的情况,死锁

到底什么情况下我们需要使用锁,什么情况下不用呢?

只有共享资源才需要锁定
首先,只有可以被多线程访问的共享资源才需要考虑锁定,比如静态变量,再比如某些缓存中的值,属于线程内部的变量不需要锁定

把锁定交给数据库
数据库除了存储数据之外,还有一个重要的用途就是同步,数据库本身用了一套复杂的机制来保证数据的可靠和一致性,这就为我们节省了很多的精力.保证了数据源头上的同步,我们多数的精力就可以集中在缓存等其他一些资源的同步访问上了

了解你的程序是怎么运行的
实际上在web开发中大多数逻辑都是在单个线程中展开的,无论asp.net还是php,一个请求都会在一个单独的线程中处理,其中的大部分变量都是属于这个线程的,根本没有必要考虑锁定,当然对于asp.net中的application对象中的数据,我们就要小心一些了

WinForm中凡是使用BeginInvoke和Invoke调用的方法也都不需要考虑同步,因为这用这两个方法调用的方法会在UI线程中执行,因此实际是同步的,所以如果调用的方法中存在某些静态变量,不需要考虑锁定

业务逻辑对事务和线程安全的要求
这条是最根本的东西,开发完全线程安全的程序是件很费时费力的事情,在电子商务等涉及金融系统的案例中,许多逻辑都必须严格的线程安全,所以我们不得不牺牲一些性能,和很多的开发时间来做这方面的工作,而一般的应用中,许多情况下虽然程序有竞争的危险,我们还是可以不使用锁定,比如有的时候计数器少一多一,对结果无伤大雅的情况下,我们就可以不用去管他

计算一下冲突的可能性
我以前曾经谈到过,架构不要过设计,其实在这里也一样,假如你的全局缓存里的某个值每天只有几百或者几千个访问,并且访问时间很短,并且分布均匀(实际上这是大多数的情况),那么冲突的可能性就非常的少,也许每500天才会出现一次或者更长,从7*24小时安全服务的角度来看,也完全符合要求,那么你还会为这样万分之一的可能性花80%的精力去设计吗?

请多使用lock,少用Mutex
如果你一定要使用锁定,请尽量不要使用内核模块的锁定机制,比如.net的Mutex,Semaphore,AutoResetEvent,ManuResetEvent,使用这样的机制涉及到了系统在用户模式和内核模式间的切换,所以性能差很多,但是他们的优点是可以跨进程同步线程,所以应该清楚的了解到他们的不同和适用范围

 

 

Web和IIS应用程序池,WebApplication,和线程池之间有什么关系
一个应用程序池是一个独立的进程,拥有一个线程池,应用程序池中可以有多个WebApplication,每个运行在一个单独的AppDomain中,这些WebApplication公用一个线程池

不同的AppDomain保证了每个WebApplication的静态变量不会互相干扰,不同的应用程序池保证了一个网站瘫痪,其他不同进程中的站点还能正常运行

 下图说明了他们的关系

 

Web页面怎么调用异步WebService
把Page的Async属性设置为true,就可以调用异步的方法,但是这样调用的效果可能并不如我们的相像,请参考Web中使用多线程来增强用户体验

 

 

推荐文章

http://www.cnblogs.com/uubox/archive/2007/12/18/1003953.html
(内核对象同步,讲的很通俗易懂ManuResetEvent,AutoResetEvent) 

http://alang79.blogdriver.com/alang79/456761.html

A low-level Look at the ASP.NET Architecture

参考资料

<Windows 核心编程>这本书里对内核对象的描述比较详尽
<.Net框架程序设计>和上面一本一样也是大牛Jeffery Richard的作品
 

发表在 article | 标签为 | 32条评论

12 个月份的英语单词的来历(帮你记住12个英语单词)

公历一年有12个月,但不少人并不知道12 个月的英语名称的来历。公历起源于古罗马历法。罗马的英语原来只有10 个月,古罗马皇帝决定增加两个月放在年尾,
后来朱里斯*凯撒大帝把这两个月移到年初,成为1月.2月,原来的1月.2月便成了3月.4月,依次类推。这就是今天世界沿用的公历。

January——1月

在罗马传说中,有一位名叫雅努斯的守护神,生有先后两副脸,一副回顾过去,一副要眺望未来。人们认为选择他的名字作为除旧迎新的第一个月月名,很有意
义。英语January,便是由这位守护神的拉丁文名字January演变而来的。

February——2月
每年2 月初,罗马人民都要杀牲饮酒 ,欢庆菲勃卢姆节。这一天,人们常用一种牛、草制成的名叫Februa的鞭子,抽打不育的妇女,以求怀孕生子。 这一天,人们还要忏悔自己过去一年的罪过,洗刷自己的灵魂,求得神明的饶恕,使自己成为一个贞洁的人。英语2月Feb ruary,便是由拉丁文Februar-ius(即菲勃卢姆
节)演变而来。

March-----3月

3月,原是罗马旧历法的1 月,新年的开始。凯撒大帝改革历法后,原来的1月变成3月,但罗马人仍然把3 月看做是一年的开始。另外,按照传统习惯,3月是每
年出征远战的季节。为了纪念战神玛尔斯,人们便把这位战神的拉丁名字作为3月的月名。英语3月 March,便是由这位战神的名字演变而来的。

April——4月

罗马的4月,正是大地回春.鲜花初绽的美好季节。英文4月April便由拉丁文April(即开花的日子)演变而来。

May——5月

罗马神话中的女神玛雅,专门司管春天和生命。为了纪念这位女神,罗马人便用她的名字——拉丁文Maius命名5月,英文5月May便由这位女神的名字演变而来。

June——6月

罗马神话中的裘诺,是众神之王,又是司管生育和保护妇女的神。古罗马对她十分崇敬,便把6月奉献给她,以她的名字——拉丁文Junius来命名6 月。英语6
月June便由这位女神的名字演变而来。也有学者认为,Junius可能是个代拉丁家族中一个显赫贵族的姓氏。

July——7月

罗马统治者朱里斯*凯撒大帝被刺死后,著名的罗马将军马克*按东尼建议将凯撒大帝诞生的7月,用凯撒的名字——拉丁文Julius(即朱里斯)命名之。这一建
议得到了元老院的通过。英语7月July由此演变而来。

August——8月

朱里斯*凯撒死后,由他的甥孙屋大维续任罗马皇帝。为了和凯撒齐名,他也想用自己的名字来命名一个月份。他的生日在9月,但他选定8月。因为他登基后,
罗马元老院在8 月授予他Augustus(奥古斯都)的尊号。于是,他决定用这个尊号来命名8月。原来8月比7月少一天,为了和凯撒平起平坐,他又决定从2月中
抽出一天加在8月上。从此,2月便少了一天。英语8月August便由这位皇帝的拉丁语尊号演变而来。

September——9月

老历法的7月,正是凯撒大帝改革历法后的9月,拉丁文Septem是“7”月的意思。虽然历法改革了,但人们仍袭用旧名称来称呼9月。英语9月September,便由
此演变而来。

October——10月

英语10月,来自拉丁文Octo,即“8”的意思。它和上面讲的9月一样,历法改了,称呼仍然沿用未变。

November——11月

罗马皇帝奥古斯都和凯撒都有了自己名字命名的月份,罗马市民和元老院要求当时的罗马皇帝梯比里乌斯用其名命名11月。但梯比里乌斯没有同意,他明智地
对大家说,如果罗马每个皇帝都用自己的名字来命名月份,那么出现了第13个皇帝怎么办?于是,11月仍然保留着旧称Novem,即拉丁文“9”的意思。英语11月
November便由此演变而来。

December——12月罗马皇帝琉西乌斯要把一年中最后一个月用他情妇的Amagonius的名字来命名,但遭但元老院的反对。于是,12月仍然沿用旧名Decem,即拉丁
文“10”的意思。英语12月December,便由此演变而来。

星期的单词
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
 

发表在 english | 标签为 | 12 个月份的英语单词的来历(帮你记住12个英语单词)已关闭评论

nginx和squid配合搭建的web服务器前端系统

点击查看原图

 

前端的lvs和squid,按照安装方法,把epoll打开,配置文件照搬,基本上问题不多。

这个架构和app_squid架构的区别,也是关键点就是:加入了一级中层代理,中层代理的好处实在太多了:

1、gzip压缩

压缩可以通过nginx做,这样,后台应用服务器不管是apache、resin、lighttpd甚至iis或其他古怪服务器,都不用考虑压缩的功能问题。

2、负载均衡和故障屏蔽

nginx可以作为负载均衡代理使用,并有故障屏蔽功能,这样,根据目录甚至一个正则表达式来制定负载均衡策略变成了小case。

3、方便的运维管理,在各种情况下可以灵活制订方案。

例如,如果有人用轻量级的ddos穿透squid进行攻击,可以在中层代理想办法处理掉;访问量和后台负载突变时,可以随时把一个域名或一个目录的请求扔入二级cache服务器;可以很容易地控制no-cache和expires等header。等等功能。。。

4、权限清晰

这台机器就是不写程序的维护人员负责,程序员一般不需要管理这台机器,这样假如出现故障,很容易能找到正确的人。

对于应用服务器和数据库服务器,最好是从维护人员的视线中消失,我的目标是,这些服务只要能跑得起来就可以了,其它的事情全部可以在外部处理掉。

发表在 web server | 标签为 , | nginx和squid配合搭建的web服务器前端系统已关闭评论