Akawa

ETY001的博客

转自:http://www.cnblogs.com/zhuyue/archive/2012/07/04/2576971.html

一、导入数据

1、确定 数据库默认编码,比如编码 为gbk,将读入途径编码同样设为gbk,命令为:
set names gbk;
2、source d:/20080613.sql 导入数据。验证 数据库 中的数据是否存在乱码。

3、如果仍然存在乱码问题,这时候就要考虑改变导入文件的编码,试着 导入,直至没有乱码出现。

网页数据存入乱码问题依照以上方法同样可以解决。可将网页编码改为与 数据库 相同的编码。问题自 然解决。

4、查看字符集

mysql>   show   variables   like   “%char%”;

阅读全文 »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
验证数字:^[0-9]*$
验证n位的数字:^\d{n}$
验证至少n位数字:^\d{n,}$
验证m-n位的数字:^\d{m,n}$
验证零和非零开头的数字:^(0|[1-9][0-9]*)$
验证有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
验证有1-3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
验证非零的正整数:^\+?[1-9][0-9]*$
验证非零的负整数:^\-[1-9][0-9]*$
验证非负整数(正整数 + 0) ^\d+$
验证非正整数(负整数 + 0) ^((-\d+)|(0+))$
验证长度为3的字符:^.{3}$
验证由26个英文字母组成的字符串:^[A-Za-z]+$
验证由26个大写英文字母组成的字符串:^[A-Z]+$
验证由26个小写英文字母组成的字符串:^[a-z]+$
验证由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
验证由数字、26个英文字母或者下划线组成的字符串:^\w+$
验证用户密码:^[a-zA-Z]\w{5,17}$ 正确格式为:以字母开头,长度在6-18之间,只能包含字符、数字和下划线。
验证是否含有 ^%&',;=?$\" 等字符:[^%&',;=?$\x22]+
验证汉字:^[\u4e00-\u9fa5],{0,}$
验证Email地址:^\w+[-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
验证InternetURL:^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ ;^[a-zA-z]+://(w+(-w+)*)(.(w+(-w+)*))*(?S*)?$
验证电话号码:^(\(\d{3,4}\)|\d{3,4}-)?\d{7,8}$:--正确格式为:XXXX-XXXXXXX,XXXX-XXXXXXXX,XXX-XXXXXXX,XXX-XXXXXXXX,XXXXXXX,XXXXXXXX。
验证身份证号(15位或18位数字):^\d{15}|\d{}18$
验证一年的12个月:^(0?[1-9]|1[0-2])$ 正确格式为:“01”-“09”和“1”“12”
验证一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$ 正确格式为:01、09和1、31。
整数:^-?\d+$
非负浮点数(正浮点数 + 0):^\d+(\.\d+)?$
正浮点数 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
非正浮点数(负浮点数 + 0) ^((-\d+(\.\d+)?)|(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+)?$
××××××××××××××××××××××××××××××××××××××
以下未经测试,请验证后使用
1.只能输入数字和英文的:
<input onkeyup="value=value.replace(/[\W]/g,'') " onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))" ID="Text1" NAME="Text1">
2.只能输入数字的:
<input onkeyup="value=value.replace(/[^\d]/g,'') " onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))" ID="Text2" NAME="Text2">
3.只能输入全角的:
<input onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))" ID="Text3" NAME="Text3">
4.只能输入汉字的:
<input onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))" ID="Text4" NAME="Text4">
5.邮件地址验证:
var regu = "^(([0-9a-zA-Z]+)|([0-9a-zA-Z]+[_.0-9a-zA-Z-]*[0-9a-zA-Z]+))@([a-zA-Z0-9-]+[.])+([a-zA-Z]{2}|net|NET|com|COM|gov|GOV|mil|MIL|org|ORG|edu|EDU|int|INT)$"
var re = new RegExp(regu);
if (s.search(re) != -1) {
return true;
} else {
window.alert ("请输入有效合法的E-mail地址 !")
return false;
}
6.身份证:
"^\\d{17}(\\d|x)$"
7.17种正则表达式
"^\\d+$" //非负整数(正整数 + 0)
"^[0-9]*[1-9][0-9]*$" //正整数
"^((-\\d+)|(0+))$" //非正整数(负整数 + 0)
"^-[0-9]*[1-9][0-9]*$" //负整数
"^-?\\d+$" //整数
"^\\d+([url=file://.//d+)?$]\\.\\d+)?$[/url]" //非负浮点数(正浮点数 + 0)
"^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$" //正浮点数
"^((-\\d+([url=file://.//d+)?)%7C(0+(//.0+)?))$]\\.\\d+)?)|(0+(\\.0+)?))$[/url]" //非正浮点数(负浮点数 + 0)
"^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$" //负浮点数
"^(-?\\d+)([url=file://.//d+)?$]\\.\\d+)?$[/url]" //浮点数
"^[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
=============================================
1.取消按钮按下时的虚线框
在input里添加属性值 hideFocus 或者 HideFocus=true
2.只读文本框内容
在input里添加属性值 readonly
3.防止退后清空的TEXT文档(可把style内容做做为类引用)
<INPUT style=behavior:url(#default#savehistory); type=text id=oPersistInput>
4.ENTER键可以让光标移到下一个输入框
<input onkeydown="if(event.keyCode==13)event.keyCode=9" >
5.只能为中文(有闪动)
<input onkeyup="value="/value.replace(/[" -~]/g,’’)" onkeydown="if(event.keyCode==13)event.keyCode=9">
6.只能为数字(有闪动)
<input onkeyup="value="/value.replace(/["^\d]/g,’’) "onbeforepaste="clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^\d]/g,’’))">
7.只能为数字(无闪动)
<input ime-mode:disabled" onkeydown="if(event.keyCode==13)event.keyCode=9" onKeyPress="if ((event.keyCode<48 || event.keyCode>57)) event.returnValue=false">
8.只能输入英文和数字(有闪动)
<input onkeyup="value="/value.replace(/[\W]/g,"’’)" onbeforepaste="clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^\d]/g,’’))">
9.屏蔽输入法
<input type="text" name="url" ime-mode:disabled" onkeydown="if(event.keyCode==13)event.keyCode=9">
10. 只能输入 数字,小数点,减号(-) 字符(无闪动)
<input onKeyPress="if (event.keyCode!=46 && event.keyCode!=45 && (event.keyCode<48 || event.keyCode>57)) event.returnValue=false">
11. 只能输入两位小数,三位小数(有闪动)
<input maxlength=9 onkeyup="if(value.match(/^\d{3}$/))value="/value.replace(value,parseInt(value/10))" ;value="/value.replace(/\.\d*\./g,’."’)" onKeyPress="if((event.keyCode<48 || event.keyCode>57) && event.keyCode!=46 && event.keyCode!=45 || value.match(/^\d{3}$/) || /\.\d{3}$/.test(value)) {event.returnValue=false}" id=text_kfxe name=text_kfxe>
"^\\d+$" //非负整数(正整数 + 0)
"^[0-9]*[1-9][0-9]*$" //正整数
"^((-\\d+)|(0+))$" //非正整数(负整数 + 0)
"^-[0-9]*[1-9][0-9]*$" //负整数
"^-?\\d+$" //整数
"^\\d+([url=file://\\.\\d+)?$]\\.\\d+)?$[/url]" //非负浮点数(正浮点数 + 0)
"^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$" //正浮点数
"^((-\\d+([url=file://\\.\\d+)?)|(0+(\\.0+)?))$]\\.\\d+)?)|(0+(\\.0+)?))$[/url]" //非正浮点数(负浮点数 + 0)
"^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$" //负浮点数
"^(-?\\d+)([url=file://\\.\\d+)?$]\\.\\d+)?$[/url]" //浮点数
"^[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
"^((\d{1,3}(,\d{3})*?)|\d+)(\.\d+)?$ //带逗号的decimal
具体的使用
<SCRIPT language=JavaScript>
var mm=/^\d+$/;
function formCheck()
{
if(!mm.test(document.f1.PropertyAmount.value))
{
alert("请输入合法的数字");
document.f1.PropertyAmount.focus();
return false;
}
return true;
}
</SCRIPT>

转自Laruence大神的博客:http://www.laruence.com/2012/04/01/2571.html

今天是愚人节, 但我这个文章标题可不是和大家开玩笑.

首先, 大家都知道, PHP也是一种编译型脚本语言, 和其他的预编译型语言不同, 它不是编译成中间代码, 然后发布.. 而是每次运行都需要编译..

为此, 也就有了一些Opcode Cache, 比如开源的APC, eacc. 还有商业的Zend O+等.

那么为什么PHP不把编译/执行分开呢?

PHP虽然是一种编译型脚本语言, 但是它的编译速度非常快, 它的编译不做任何语义优化, 就是简单的忠实的把你所写的代码翻译成对应的Opcodes. 而其他语言因为在编译器做很多的优化工作, 会造成编译比较重, 也一定程度上要求它们分离.

所以, 理论上来说, 通过编译执行分离, 想达到源码加密, 是不会有什么太大收效的, 因为它很容易被反向.

另外, 编译直接分离, 并不会带来特别大的收益, 反而会降低调试部署的效率(想想, 修改, 编译, 发布, 看效果), 并且APC等Opcode Cache工具, 已经很成熟了..

到这里, 请大家注意这句:”它的编译不做任何语义优化”….

这也就是我为什么说, PHP对程序员的要求更高, 不同于其他的编译型语言, PHP在编译的时候不会帮你做一些优化, 比如对于如下的代码:

  1. $j = “laruence”;

  2. for ($i=0;$i<strlen($j);$i++) {

  3. }

如果是其他预编译语言, 它的编译器也许会帮你做优化, 把strlen提取到前面去, 只做一次就够了. 而对于PHP来说, 它在编译的时候不做任何优化, 也就是说, 你的strlen, 会忠实的被调用8次.

再比如:

  1. $table = “table”;

  2. while($i++ < 1000) {

  3.   $sql = “select * from “ . $table . “ where id = “ . $i;

  4. }

没错, “select * from ” . $table会被concat 1000次..

可见, PHP的程序员, 需要认真的想好, 你的代码会怎么被执行, 你怎么写代码, 最终的执行效率才最高. 而不像其他的语言, 程序员可以把一部分优化工作交给编译器.

这也就是我为什么说:”PHP对程序员的要求更高” 的原因. 当然, 这个是好是坏, 那就是见仁见智了.

转自:http://www.smuwcwt.com/archives/673

select备受鄙视,前端中会碰到各式各样的问题,例如:

1,弹出层无法阻挡select
2,宽度,高度受系统影响,甚至连系统主题都影响select的宽高
等等。

今天碰到这样一个问题,后台系统再做3级联动的时候,用jquery的append向select中推拼合好的option,在IE6/7下居然无法自适应宽度。找到的唯一理由是,低版本浏览器无法重绘界面,导致宽度无法自适应,使用的解决方案:

对select先隐藏,后再显示。例如:

$(“#SelectID”).hide().show();

另外一种可能的解决方案是:

使用原生js的options.add向select中添加。

安装完archlinux + xfce后发现笔记本键盘的音量调节不管用了,从archlinux的wiki上找了下,发现可以自己设置快捷键,在“设置”–》“键盘”–》“应用程序快捷键”中,选择“添加”,就可以添加新的快捷键了。

由于我用的ALSA,所以我按照下面的来设置即可,

升高音量:

1
amixer set Master 5%+

降低音量:

1
amixer set Master 5%-

静音:

1
amixer set Master toggle

你如果使用的是标准的XF86Audio 快捷键,在在终端输入以下内容:

1
2
3
xfconf-query -c xfce4-keyboard-shortcuts -p /commands/custom/XF86AudioRaiseVolume -n -t string -s "amixer set Master 5%+"
xfconf-query -c xfce4-keyboard-shortcuts -p /commands/custom/XF86AudioLowerVolume -n -t string -s "amixer set Master 5%-"
xfconf-query -c xfce4-keyboard-shortcuts -p /commands/custom/XF86AudioMute -n -t string -s "amixer set Master toggle"

若 amixer set Master toggle 不工作,尝试使用调节PCM直接调节音量(amixer set PCM toggle) 。

或者使用另外一条命令

1
pactl set-sink-mute 0 toggle

其他的内容请参考wiki,https://wiki.archlinux.org/index.php/Xfce_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#ALSA

转自:http://www.php100.com/html/webkaifa/PHP/PHPyingyong/2009/0927/3349.html

先看看下边这段PHP代码。这段使用ip2long函数,对同一个IP进行转换。当然,也有人认为58.99.011.1和058.99.011.1算不上合法的

IP,那就Return,此文对你没有帮助。

为什么要使用带前导零的ip:为了在数据库中查询,这个可以在IP库中定位到ip所对应的位置信息。虽然没有整型的IP查询效率高,但毕竟直观啊。

1
2
3
4
5
6
7
echo ip2long('58.99.11.1'),"<br/>";   //输出是 979569409  
echo ip2long('58.99.011.1'),"<br/>"; //输出是 979568897
echo ip2long('058.99.11.1'),"<br/>"; //输出是空

echo ip2long('58.99.11.1'),"<br/>"; //输出是 979569409
echo ip2long('58.99.011.1'),"<br/>"; //输出是 979568897
echo ip2long('058.99.11.1'),"<br/>"; //输出是空

在PHP 4.x,5.x中, 有前导零的ip转换的结果都不正确。

解决办法,使用写自己的函数:

1
2
3
4
5
function myip2long($ip){  
$ip_arr = split('\.',$ip);
$iplong = (16777216 * intval($ip_arr[0])) + (65536 * intval($ip_arr[1])) + (256 * intval($ip_arr[2])) + intval($ip_arr[3]);
return $iplong;
}

获取地理位置的方式及其优缺点:

  1、ip地址
  书上说不准确,很多时候获取的是ISP机房的位置,但是获取非常方便,没有什么限制。但是实际上我觉得在中国,ip地址还是比较准确的,基本上上能精确到小区或大楼的标准。
  2、GPS
  非常准确,但是需要在户外,且需要很长时间搜索卫星。最主要的很多设备比如笔记本电脑基本都是不带GPS的,新的智能手机倒是都有。
  3、WiFi基站的mac地址。(猜测是连接位置已知的公共WiFi的时候,通过Mac地址识别WiFi接入点,从而定位)
  这种定位的精度还是很不错的,而且还可以在室内定位。不过由于这种位置公开的wifi比较少,此种方法的适用范围比较少。
  4、 GSM或CDMA基站
  通过基站定位,精度随基站密度变化,精度一般,还是只有手机能用。看来地理位置API还是手机上比较有实用性。
  5、用户指定位置
  晕,这个就不是HTML5的范畴了。

地理位置获取流程:

  1、用户打开需要获取地理位置的web应用。
  2、应用向浏览器请求地理位置,浏览器弹出询问窗口,询问用户是否共享地理位置。
  3、假设用户允许,浏览器从设别查询相关信息。
  4、浏览器将相关信息发送到一个信任的位置服务器,服务器返回具体的地理位置。

检测浏览器支持:

  function loadDemo() {
  if(navigator.geolocation) {
   document.getElementById(“support”).innerHTML = “HTML5 Geolocation supported.”;
  } else {
   document.getElementById(“support”).innerHTML = “HTML5 Geolocation is not supported in your browser.”;
  }
  }
位置请求方式:

单次请求navigator.geolocation.getCurrentPosition(updateLocation, handleLocationError, options);
回调函数updateLocation接受一个对象参数,表示当前的地理位置,它有如下属性:
  latitude——纬度
  longitude——精度
  accuracy——精确度,单位米
  altitude——高度,单位米
  altitudeAccuracy——高度的精确地,单位米
  heading—运动的方向,相对于正北方向的角度
  speed——运动的速度(假设你在地平线上运动),单位米/秒

回调函数handleLocationError接受错误对象,error.code是如下错误号。
  UNKNOWN_ERROR (error code 0) —— 错误不在如下三种之内,你可以使用error.message获取错误详细信息。
  PERMISSION_DENIED (error code 1)—— 用不选择不共享地理位置
  POSITION_UNAVAILABLE (error code 2) ——无法获取当前位置
  TIMEOUT (error code 3) ——在指定时间无法获取位置会触发此错误。
  第三个参数options是可选参数,属性如下:
  enableHighAccuracy——指示浏览器获取高精度的位置,默认为false。当开启后,可能没有任何影响,也可能使浏览器花费更长的时间获取更精确的位置数据。
  timeout——指定获取地理位置的超时时间,默认不限时。单位为毫秒。
  maximumAge——最长有效期,在重复获取地理位置时,此参数指定多久再次获取位置。默认为0,表示浏览器需要立刻重新计算位置。
  参数使用的例子如下:
  navigator.geolocation.getCurrentPosition(updateLocation,handleLocationError,
  {timeout:10000});
  重复请求navigator.geolocation.watchPosition(updateLocation, handleLocationError);
  使用 watchPosition可以持续获取地理位置,浏览器或多次调用updateLocation函数以便传递最新的位置。该函数返回一个watchID,使用navigator.geolocation.clearWatch(watchId)可以清除此次回调,使用不带参数的navigator.geolocation.clearWatch()清除说有watchPosition。

地址转换:

由于地理位置API返回的是经纬度,如果要计算两个位置之间的距离,可以使用著名的Haversine公式计算两个坐标在地平线上的距离。

  Listing 4-7. A JavaScript Haversine implementation
  function toRadians(degree) {
  return degree * Math.PI / 180;
  }
  function distance(latitude1, longitude1, latitude2, longitude2) {
  // R is the radius of the earth in kilometers
  var R = 6371;
  var deltaLatitude = toRadians(latitude2-latitude1);
  var deltaLongitude = toRadians(longitude2-longitude1);
  latitude1 =toRadians(latitude1);
  latitude2 =toRadians(latitude2);
  var a = Math.sin(deltaLatitude/2) *
  Math.sin(deltaLatitude/2) +
  Math.cos(latitude1) *
  Math.cos(latitude2) *
  Math.sin(deltaLongitude/2) *
  Math.sin(deltaLongitude/2);
  var c = 2 * Math.atan2(Math.sqrt(a),
  Math.sqrt(1-a));
  var d = R * c;
  return d;
  }

喜欢折腾的人是闲不住的,这几天就在自己的笔记本上把原来的LinuxDeepin干掉,然后安装上了archlinux+xfce4的环境。现在基本是能用了,就说下流程,一次也写不全,先写下来,以后有时间再补充,也算是自己做个笔记,方便日后再次重装。

1、系统的安装。我用的是U盘安装,整个流程在archlinux的wiki上有详细介绍。

2、系统升级。我习惯安装完系统后第一时间升级,由于archlinux采用的是滚动升级的模式,所以只需要一条命令即可。

pacman -Syu

升级可能遇到的问题:执行命令后,可能系统会问pacman版本过低应先升级pacman,是否跳过升级pacman,这里一定要选择N。另外一个问题就是会有类似下面的错误:

error: failed to commit transaction (conflicting files)
filesystem: /etc/mtab exists in filesystem
Errors occurred, no packages were upgraded.

阅读全文 »

fakeroot不能获得root的权限,sudo可以。

fakeroot只是伪装成root,它不能改变需要root权限才能改变的文件,它只是让程序执行时按照有root权限的情况来运行,而对文件的操作实际上是在普通用户下进行的。

1 2 fakeroot tar cvf /tmp/local.tar /usr/local sudo tar cvf /tmp/local.tar /usr/local
上面两条命令都会在/tmp下建立local.tar,tar内的文件名都会以/开头,但前一条命令生成的文件属于当前用户,后一条命令生成的文件是root的。

转自:http://blog.codingnow.com/2011/02/zeromq_message_patterns.html

在需要并行化处理数据的时候,采用消息队列通讯的方式来协作,比采用共享状态的方式要好的多。Erlang ,Go 都使用这一手段来让并行任务之间协同工作。

最近读完了 ZeroMQ 的 Guide。写的很不错。前几年一直有做类似的工作,但是自己总结的不好。而 ZeroMQ 把消息通讯方面的模式总结的很不错。

ZeroMQ 并不是一个对 socket 的封装,不能用它去实现已有的网络协议。它有自己的模式,不同于更底层的点对点通讯模式。它有比 tcp 协议更高一级的协议。(当然 ZeroMQ 不一定基于 TCP 协议,它也可以用于进程间和进程内通讯。)它改变了通讯都基于一对一的连接这个假设。

ZeroMQ 把通讯的需求看成四类。其中一类是一对一结对通讯,用来支持传统的 TCP socket 模型,但并不推荐使用。常用的通讯模式只有三类。

  1. 请求回应模型。由请求端发起请求,并等待回应端回应请求。从请求端来看,一定是一对对收发配对的;反之,在回应端一定是发收对。请求端和回应端都可以是 1:N 的模型。通常把 1 认为是 server ,N 认为是 Client 。ZeroMQ 可以很好的支持路由功能(实现路由功能的组件叫作 Device),把 1:N 扩展为 N:M (只需要加入若干路由节点)。从这个模型看,更底层的端点地址是对上层隐藏的。每个请求都隐含有回应地址,而应用则不关心它。

  2. 发布订阅模型。这个模型里,发布端是单向只发送数据的,且不关心是否把全部的信息都发送给订阅端。如果发布端开始发布信息的时候,订阅端尚未连接上来,这些信息直接丢弃。不过一旦订阅端连接上来,中间会保证没有信息丢失。同样,订阅端则只负责接收,而不能反馈。如果发布端和订阅端需要交互(比如要确认订阅者是否已经连接上),则使用额外的 socket 采用请求回应模型满足这个需求。

  3. 管道模型。这个模型里,管道是单向的,从 PUSH 端单向的向 PULL 端单向的推送数据流。

任何分布式,并行的需求,都可以用这三种模型组合起来解决问题。ZeroMQ 只专注和解决了消息通讯这一基本问题,干的非常漂亮。

基于定义好的模型,我们可以看到,api 可以实现的非常简单易用。我们不再需要 bind/listen/accept 来架设服务器,因为这个模型天然是 1:N 而不是 1:1 的,不需要为每个通道保留一个句柄。我们也不必在意 server 是否先启动(bind),而后才能让 client 工作起来(connect)。

这以上模型中,关注的是通讯双方的职责,而不是实现的方式:监听端口还是连接对方端口。对于复杂的多进程协同工作的系统, 不必纠结于进程启动的次序(在我前几年设计的系统中,启动脚本写起来就非常麻烦)。

使用 ZeroMQ 不必在意底层实现是使用短连接还是长连接方式。ZeroMQ 中的 Transient (短暂) 和 Durable (持久) socket 也并非区别于实现层是否保持了 tcp 连接。而是概念上的不同。对于 Durable socket ,其生命期可以长于一个进程的生命期,即使进程退出,再次启动后依旧可以维持继续之前的 socket 。当然,这并不是帮助你挽救你的程序因出错而崩溃的。它只是提出这个模式,让你根据设计需要可以实现。对于 ZeroMQ ,如有需求(若内存有限),甚至把数据传输的 buffer 放到磁盘上。

对于网络游戏,我觉得基于 ZeroMQ 来架构服务器非常合适。对于玩家 Client - Server 部分倒不必使用 ZeroMQ ,而可以写一个前端程序,比如前些年写过的一篇 blog 中提到的连接服务器依然适用。这个连接服务器对内的服务集群则可以用 ZeroMQ 的协议通讯。

0%