[转载]provider:命名管道提供程序,error:40 - 无法打开到SQL Server的连接 (Microsoft SQL Server, 错误:53) - laga516的专栏 - 博客频道 - CSDN.NET

mikel阅读(1243)

[转载]provider:命名管道提供程序,error:40 – 无法打开到SQL Server的连接 (Microsoft SQL Server, 错误:53) – laga516的专栏 – 博客频道 – CSDN.NET.

最近一直在配置服务器, 这当中最头疼的就是配置数据库

我们用的是SQL Server 数据库 2008 版本,数据库配置完之后从另一台电脑访问数据库死活连接不上,提示信息如下

“ 无法连接到 *.*.*.*。

在于SQL Server建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。(proveder:命名管道提供程序,error:40 – 无法打开到 SQL Server的连接)(Microsoft SQL Server,错误:53)”

下面说说数据库配置步骤

1,如果安装的时候 已经选择 “混合模式(SQL Server 身份验证和Windows 身份验证)(M)”, 并且已经为 SQL Server 系统管理员分配了账户和密码

则不用重新配置。  配置方法如下:

用Windows 验证方法登录数据库  选择实例->右键 ->属性 ->安全性 -> 选择 “SQL Server 和  Windows 身份验证模式” -> 确定

用Windows 验证方法登录数据库  选择实例->右键 ->属性 -> 安全性->登录名->双击用户名(一般为sa)->设置密码

 

2,开始菜单->所有程序->Microsoft SQL Server 2008 ->配置工具 ->

SQL Server 配置管理器->网络配置->MSSQLServer2008->双击“TCP/IP”  ->协议->已启用-> 选 “是”

SQL Server 配置管理器->网络配置->MSSQLServer2008->双击“TCP/IP”  ->IP地址->IPAll->TCP端口->输入”1433″ 点击确定

3,开始菜单->所有程序->Microsoft SQL Server 2008 ->配置工具 ->SQL Server 配置管理器->SQL Server服务-> SQL Server (MSSQLSERVER2008)->右键重新启动

4,在命令行下输入netstat –an,如果找到有“0.0.0.0:1433”,就说明SqlServer在监听了。

5,操作系统->安全中心->Windows 防火墙-> 例外->添加程序

C:\Program Files\Microsoft SQLServer\90\Shared\sqlbrowser.exe

C:\Program Files\Microsoft SQLServer\MSSQL10.MSSQLSERVER\MSSQL\Binn\sqlservr.exe

6,操作系统->安全中心->Windows 防火墙-> 例外->添加端口   1433 

一般在局域网里访问的话做到这里就应该没问题了

(我做的是公网访问 所以设置到这里还是访问不了 郁闷的查了半天资料还是没有头绪, 最后得到已高人指点解决问题~ )

7,如果你做公网访问那么还有一个非常重要的步骤要做 ,如下:

在”网络联接” 找到自己用的”本地连接”-> 右键->属性-> 双击”Internet 协议(TCP/IP)”->高级

->选中”TCP/IP筛选”->点击右下方”属性”->在”TCP端口”中将1433添加进去, 然后一路确定。

8,重启服务器。

[转载]win7下的PHP+IIS配置,找不到php5isapi.dll的问题,版本5.4.9_nano闹_新浪博客

mikel阅读(931)

[转载]win7下的PHP+IIS配置,找不到php5isapi.dll的问题,版本5.4.9_nano闹_新浪博客.

    问题:PHP新手配置,在官网上下载的压缩包。按网上的找的教程配置IIS时发现,在解压包里找不到php5isapi.dll文件,没办法添加脚本映射。

 

解答:

PHP5.3版本开始,在PHP文件里面没有php5isapi.dll了,不能在IIS6上面通过ISAPI扩展运行。

就是说PHP5.3以后的版本不再支持用ISAPI进行扩展在IIS里运行了,应该用FastCGI方法配置。

 

FastCGI执行方式是以单一线程来执行操作,所以不需要进行线程的安全检查,除去线程安全检查的防护反而可以提高执行效率,所以,以FastCGI来执行PHP建议选择Non Thread Safe版本

 

解压下载回来的phpzip压缩包。比如放在D:/

 

配置IISFastCGI

internet 信息服务(IIS)管理器中,添加添加FastCgi模块映射

求路径:*.php。模块:FastCGIModule

可执行文件:选择解压包下的php-cgi.exe。名称:phpcgi。

 

php.ini配置:

首先将php.ini-development重命名为php.ini,并修改此配置文件保存

fastcgi.impersonate=1  默认为0

cgi.fix_pathinfo=1 如果使用IIS,需要开启

cgi.force_redirect=0  默认为1开启,如果使用IIS,可以将其关闭

cgi.rfc2616_headers = 1

 

 其次指定extension_dir目录和date.timezone,即

extension_dir = “D:/php/ext”(自己的php所在目录)

date.timezone= Asia/Shanghai

    其他PHP.INI配置与PHP5.2的配置一样,区别在于,在Windows7 IIS7上配置安装PHP时,并不需要将php.ini及其他文件复制到C:/windowsC:/windows/System32目录下,简单很多。

 

最后: 重启IIS7服务器
可以访问http://localhost:8080/

FastCGI进程意外退出如何解决?

在使用Windows7 IIS7进行PHP配置安装过程中,如果PHP配置不正确,会出现FastCGI进程意外退出出错信息,可以在DOS下使用

Cphp53iisphp.exe –v

进行调试查看,一般情况下会将PHP配置的错误信息报出,只要根据此信息修改相关PHP配置即可。

 

详见:http://www.2cto.com/os/201102/83093.html

[转载]htaccess语法教程 - 站长圈

mikel阅读(1251)

[转载]htaccess语法教程 – 站长圈.这是几条重定向规则

代码如下:

RewriteEngine on
#52jscn全局切换
Rewritecond %{HTTP_HOST} ^(www\.)?52jscn.com$ [nc]
Rewriterule ^(.*)$ http://52jscn.com/$1 [r=301,nc]
#blog对应切换
Rewritecond %{HTTP_HOST} ^blog.52jscn.com$ [nc]
Rewriterule ^(.*)$ http://52jscn.com/blog [r=301,nc]
#app对应切换
Rewritecond %{HTTP_HOST} ^app.52jscn.com$ [nc]
Rewriterule ^(.*)$ http://52jscn.com/app/$1 [r=301,nc]
#lab对应切换
Rewritecond %{HTTP_HOST} ^lab.52jscn.com$ [nc]
Rewriterule ^(.*)$ http://52jscn.com/lab/$1 [r=301,nc]
#photo对应切换
Rewritecond %{HTTP_HOST} ^photo.52jscn.com$ [nc]
Rewriterule ^(.*)$ http://52jscn.com/photo/$1 [r=301,nc]
#只访问主域的先切换到博客
Rewritecond %{HTTP_HOST} ^(www.)?52jscn.com$ [nc]
Rewritecond %{REQUEST_URI} ^(\/)?$ [nc]
Rewriterule ^(.*)$ http://52jscn.com/blog [r=301,nc]

下面简单解说下上面的意思:

【RewriteEngine On】表示重写引擎开,关闭off,关闭的话当然就不能做转发了。

【Rewritecond %{HTTP_HOST} ^(www\.)?52jscn.com$ [nc]】
这是重写条件,前面%{HTTP_HOST}表示当前访问的网址,只是指前缀部分,格式是www.52jscn.com不包括“http://”和“ /”,^表示字符串开始,$表示字符串结尾,\.表示转义的. ,如果不转义也行,推荐转义,防止有些服务器不支持,?表示前面括号www\.出现0次或1次,这句规则的意思就是如果访问的网址是52jscn.com 或者www.52jscn.com就执行以下的语句,不符合就跳过。

【Rewriterule ^(.*)$ http://52jscn.com/$1 [r=301,nc]】
这个根据之前的条件执行相应的规则,在这里的话就是让重定向到52jscn.com域下面相应地址,比如来源地址是 http://www.52jscn.com/abc/1.html,前部分的^(.*)$将会匹配当前请求的url,不过这里到底是匹配整个http: //www.52jscn.com/abc/1.html,还是只匹配/abc/1.html即反斜杠后面的成分,还是只匹配abc/1.html?

答案是:根据RewriteBase规则规定,如果rewritebase 为/,将会匹配abc/1.html,默认应该是abc/1.html。后面的$1是正则匹配出来的值,经过组合,之前的url会被重定向为http: //52jscn.com/abc/1.html,达到了域名转换的目的。

后面几行大同小异。

还有一个问题是,不能保证每个人输入的网址都是小写的,如果输入大写的呢,linux系统是区分大小写的,所以应该在RewriteCond后添加[NC]忽略大小写的。

下面附上简单的语法规则和flags:
【RewriteCond语法:】
RewriteCond TestString CondPattern [flags]
rewritecond的其他用法:
‘-d'(目录)
将TestString视为一个路径名并测试它是否为一个存在的目录。
‘-f'(常规文件)
将TestString视为一个路径名并测试它是否为一个存在的常规文件。
‘-s'(非空的常规文件)
将TestString视为一个路径名并测试它是否为一个存在的、尺寸大于0的常规文件。
‘-l'(符号连接)
将TestString视为一个路径名并测试它是否为一个存在的符号连接。
‘-x'(可执行)
将TestString视为一个路径名并测试它是否为一个存在的、具有可执行权限的文件。该权限由操作系统检测。
‘-F'(对子请求存在的文件)
检查TestString是否为一个有效的文件,而且可以在服务器当前的访问控制配置下被访问。它使用一个内部子请求来做检查,由于会降低服务器的性能,所以请谨慎使用!
‘-U'(对子请求存在的URL)
检查TestString是否为一个有效的URL,而且可以在服务器当前的访问控制配置下被访问。它使用一个内部子请求来做检查,由于会降低服务器的性能,所以请谨慎使用!

【RewriteRule语法:】
RewriteRule Pattern Substitution [flags]

【flags】:

‘chain|C'(链接下一规则)
此标记使当前规则与下一个规则相链接。它产生这样的效果:如果一个规则被匹配,则继续处理其后继规则,也就是这个标记不起作用;如果该规则不被匹配,则其 后继规则将被跳过。比如,在一个目录级规则中执行一个外部重定向时,你可能需要删除”.www”(此处不应该出现”.www”)。

‘cookie|CO=NAME:VAL:domain[:lifetime[:path]]'(设置cookie)
在客户端设置一个cookie。cookie的名称是NAME,值是VAL。domain是该cookie的域,比如’.apache.org’,可选的lifetime是cookie的有效期(分钟),可选的path是cookie的路径。
‘env|E=VAR:VAL'(设置环境变量)
此标记将环境变量VAR的值为VAL,VAL可以包含可扩展的正则表达式反向引用($N和%N)。此标记可以多次使用以设置多个变量。这些变量可以在其后 许多情况下被间接引用,通常是在XSSI(<!–#echo var=”VAR”–>)或CGI($ENV{‘VAR’})中,也可以在后继的RewriteCond指令的CondPattern参数中通过% {ENV:VAR}引用。使用它可以记住从URL中剥离的信息。

‘forbidden|F'(强制禁止URL)
强制禁止当前URL,也就是立即反馈一个HTTP响应码403(被禁止的)。使用这个标记,可以链接若干个RewriteConds来有条件地阻塞某些URL。

‘gone|G'(强制废弃URL)
强制当前URL为已废弃,也就是立即反馈一个HTTP响应码410(已废弃的)。使用这个标记,可以标明页面已经被废弃而不存在了。

‘handler|H=Content-handler'(强制指定内容处理器)
强自制定目标文件的内容处理器为Content-handler。例如,用来模拟mod_alias模块的ScriptAlias指令,以强制映射文件夹内的所有文件都由”cgi-script”处理器处理。

‘last|L'(结尾规则)
立即停止重写操作,并不再应用其他重写规则。它对应于Perl中的last命令或C语言中的break命令。这个标记用于阻止当前已被重写的URL被后继规则再次重写。例如,使用它可以重写根路径的URL(‘/’)为实际存在的URL(比如:’/e/www/’)。

‘next|N'(从头再来)
重新执行重写操作(从第一个规则重新开始)。此时再次进行处理的URL已经不是原始的URL了,而是经最后一个重写规则处理过的URL。它对应于Perl 中的next命令或C语言中的continue命令。此标记可以重新开始重写操作(立即回到循环的开头)。但是要小心,不要制造死循环!

‘nocase|NC'(忽略大小写)
它使Pattern忽略大小写,也就是在Pattern与当前URL匹配时,’A-Z’和’a-z’没有区别。

‘noescape|NE'(在输出中不对URI进行转义)
此标记阻止mod_rewrite对重写结果应用常规的URI转义规则。 一般情况下,特殊字符(‘%’, ‘$’, ‘;’等)会被转义为等值的十六进制编码(‘%25′, ‘%24′, ‘%3B’等)。此标记可以阻止这样的转义,以允许百分号等符号出现在输出中,比如:
RewriteRule /foo/(.*) /bar?arg=P1\%3d$1 [R,NE]
可以使’/foo/zed转向到一个安全的请求’/bar?arg=P1=zed’。

‘nosubreq|NS'(不对内部子请求进行处理)
在当前请求是一个内部子请求时,此标记强制重写引擎跳过该重写规则。比如,在mod_include试图搜索目录默认文件(index.xxx) 时,Apache会在内部产生子请求。对于子请求,重写规则不一定有用,而且如果整个规则集都起作用,它甚至可能会引发错误。所以,可以用这个标记来排除 某些规则。
使用原则:如果你为URL添加了CGI脚本前缀,以强制它们由CGI脚本处理,但对子请求处理的出错率(或者资源开销)很高,在这种情况下,可以使用这个标记。

‘proxy|P'(强制为代理)
此标记使替换成分被内部地强制作为代理请求发送,并立即中断重写处理,然后把处理移交给mod_proxy模块。你必须确保此替换串是一个能够被 mod_proxy处理的有效URI(比如以http://hostname开头),否则将得到一个代理模块返回的错误。使用这个标记,可以把某些远程成 分映射到本地服务器域名空间,从而增强了ProxyPass指令的功能。
注意:要使用这个功能,必须已经启用了mod_proxy模块。

‘passthrough|PT'(移交给下一个处理器)
此标记强制重写引擎将内部request_rec结构中的uri字段设置为filename字段的值,这个小小的修改使得RewriteRule指令的输 出能够被(从URI转换到文件名的)Alias, ScriptAlias, Redirect等指令进行后续处理[原文:This flag is just a hack to enable post-processing of the output of RewriteRule directives, using Alias, ScriptAlias, Redirect, and other directives from various URI-to-filename translators.]。举一个能说明其含义的例子: 如果要将/abc重写为/def, 然后再使用mod_alias将/def转换为/ghi,可以这样:
RewriteRule ^/abc(.*) /def$1 [PT]
Alias /def /ghi
如果省略了PT标记,虽然将uri=/abc/…重写为filename=/def/…的部分运作正常,但是后续的mod_alias在试图将URI转换到文件名时会遭遇失效。
注意:如果需要混合使用多个将URI转换到文件名的模块时,就必须使用这个标记。。此处混合使用mod_alias和mod_rewrite就是个典型的例子。

‘qsappend|QSA'(追加查询字符串)
此标记强制重写引擎在已有的替换字符串中追加一个查询字符串,而不是简单的替换。如果需要通过重写规则在请求串中增加信息,就可以使用这个标记。

‘redirect|R [=code]‘(强制重定向)
若Substitution以http://thishost[:thisport]/(使新的URL成为一个URI)开头,可以强制性执行一个外部重定 向。如果没有指定code,则产生一个HTTP响应码302(临时性移动)。如果需要使用在300-400范围内的其他响应代码,只需在此指定即可(或使 用下列符号名称之一:temp(默认), permanent, seeother)。使用它可以把规范化的URL反馈给客户端,如将”/~”重写为”/u/”,或始终对/u/user加上斜杠,等等。
注意:在使用这个标记时,必须确保该替换字段是一个有效的URL。否则,它会指向一个无效的位置!并且要记住,此标记本身只是对URL加上 http://thishost[:thisport]/前缀,重写操作仍然会继续进行。通常,你还会希望停止重写操作而立即重定向,那么就还需要使用 ‘L’标记。

‘skip|S=num'(跳过后继规则)
此标记强制重写引擎跳过当前匹配规则之后的num个规则。它可以模拟if-then-else结构:最后一个规则是then从句,而被跳过的skip=N个规则是else从句。注意:它和’chain|C’标记是不同的!

‘type|T=MIME-type'(强制MIME类型)
强制目标文件的MIME类型为MIME-type,可以用来基于某些特定条件强制设置内容类型。比如,下面的指令可以让.php文件在以.phps扩展名 调用的情况下由mod_php按照PHP源代码的MIME类型(application/x-httpd-php-source)显示:
RewriteRule ^(.+\.php)s$ $1 [T=application/x-httpd-php-source]

[转载]IIS7伪静态化URL Rewrite模块-逍遥峡谷

mikel阅读(913)

[转载]IIS7伪静态化URL Rewrite模块-逍遥峡谷.

Win7安装了IIS7.5之后,搭建一些网站或者博客,但是IIS7.5本身没有URL Rewrite功能,也就是无法实现网址的伪静态化。

 

从网上找了一下,原来微软IIS官方网站给IIS7及以后续版本提供了个URL重写组件。

 

下载地址:http://www.iis.net/download/URLRewrite

 

首先,打开上面网址,到IIS官方网站下载模块。

 

根据需要,点击右侧的下载链接(操作系统是64位的,就下载x64版本;32位的系统,就下载x86版本)

 

 

下载完成之后,安装重写模块。
下载的是本地msi包(rewrite_2.0_rtw_x64.msi或者rewrite_2.0_rtw_x86.msi),双击安装即可(安装之前最好先停止IIS服务,如果IIS服务没停的话,安装完成后会要求重启系统。)

 

 

安装完成后,打开“Internet 信息服务(IIS)管理器”,就可以看见模块中多了一个Url Rewrite 模块。

 

 

进入需要设置的站点,双击 Url Rewrite 图标,进入设置界面。

 

 

点击Add Rule(s)输入Rewrite重写规则

确认无误后,点击右栏的“应用”按钮,大功告成

无线运维必备10个Wi-Fi故障排除工具

mikel阅读(1105)

. 用户都喜欢使用无线网络,因为其便利性和易于使用。但在企业中每个无缝运行的无线网络背后,都会有几个网络人员努力来确保WLAN正常运作。
  如果你正在想办法来优化你的Wi-Fi,这里有10个工具可以帮助你解决通道冲突、找出流氓无线局域网、执行网络诊断等。这些WiFi故障排除工具和实用程序可以帮助简化无线局域网的管理,并确保用户满意和高效率。
  大多数IT专业人员都非常熟悉经典的免费NetStumbler工具,它可以用 来检测802.11 a/ b / g标准的无线局域网,并可以帮助验证配置和识别无线网络内的弱信号。虽然其他工具已经完善了这个发现过程,但NetStumbler仍然是网络管理员的不 错选择。我们还有很多其他工具值得一试,甚至有些是免费的。

  ▲inSSIDer
  通过使用这个工具,管理员可以轻松地扫描网络来找出信号重叠、信道冲突和可能导致性能降低的配置问题。inSSIDer提供了方法来确保AP在正确的信道,以及信号强度随着时间的推移保持持续强劲,并还可以可视化需要解决的信道冲突问题。

  ▲Wireshark
  通过使用这个嗅探工具,我们可以在数据包水平收集和分析无线流量。该工具最出名的是其以太网分析功能,它还提供对802.11的支持,并提供了功能来解决无线问题,还可以锁定安全配置。

  ▲Tamosoft Throughput Test
  通过使用这个免费的工具,我们可以快速获取无线和有线网络的性能数据。该工具发送连续的TCP和UDP数据来测量上行和下行吞吐量,以及数据包丢失信息和往返时间。

  ▲Ekahau HeatMapper
  对于小型办公室和远程位置,Ekahau提供了免费的规划和现场勘察工具,以在 地图和平面图展示关于WiFi覆盖范围的一栏信息。对于更广泛的使用,Ekahau还通过其Ekahau Site Survey(ESS)工具提供了更全面的企业级功能,设计用于预部署使用和设计调整。

  ▲Kismet
  这是一个开源无线网络发现和数据包嗅探工具,它为IT专业人员提供了一个很好的 方法来发现可能干扰其合法网络的隐藏无线网络。与NetStumbler不同,Kismet通过坚挺来自AP的信标传输来找到网络,而不是广播SSID, 这让它可能找到隐藏其SSID的流氓网络。

  ▲WiFi Survey
  通过使用这个方便的工具,我们可以直接从iOS设备对内部和外部网络执行WiFi现场调查。通过WiFi Survey的诊断可以检查802.11 a/b/g/n的速度,并让用户能够在数字平面图(来自设备摄像头、图像库或Dropbox)上放置速度测试指标。

  ▲WifiEagle
  这是一个基于电脑的加密狗和软件组合,它提供WiFi诊断,使用接微波干扰测量(IMMI)技术来获取关于射频干扰对802.11信道吞吐量的影响数据。除了信道分析,该工具还支持802.11发现工具。

  ▲RF Explorer
  这是物美价廉(最低119美元)的手持式设备,该设备允许网络管理员从该设备运行简单的RF频谱分析,或者挂接到电脑来执行更复杂的诊断分析。该工具能够检测RF干扰源或者发现流氓变送器以及执行多种无线故障排除任务。

  ▲AirMagnet WiFi Analyzer
  这是多功能无线故障排除工具,Fluke Network公司的这个工具为管理员提供了企业级的功能,这些管理员可能不愿意大费周章去凑齐各种免费工具。它能够测试和诊断吞吐量和连接性问题、设备 冲突和信号多路径问题。此外,它还提供了全面的安全检查功能和报告功能,帮助IT专业人员更好地追踪合规信息。

  ▲TravelHawk Pro
  对私人LTE设置和优化感兴趣的企业,这个工具提供了对现场LTE网络故障排除的便携式装置。这包括端至端网络分析、IP应用程序数据分析和数据采集,并能够在本地存储采集30Gb/s的数据。

[转载]wifidog 认证 - 逆雪寒的天坑 - 博客频道 - CSDN.NET

mikel阅读(994)

[转载]wifidog 认证 – 逆雪寒的天坑 – 博客频道 – CSDN.NET.Wifi有一种web方式认证方案,当连接到某些不加密的热点之后,会跳转到一个网页来认证登陆,大家熟悉的CMCC就采用了这种web的验证方式。

它的原理是在得到正确的认证之前,会把所有的流量重定向到认证服务器上,通过认证后,便可以正常使用。
如果说仅仅想获取web验证时其他用户的用户名和密码,arp欺骗然后嗅探足够了。因为此时攻击者已经分配到了ip,且同一网关下产生的流量是不会重定向的。

 

但是目前的情况是,认证服务器用的https加密传输,无法嗅探到明文密码。

 

于是萌生了伪造热点及web认证服务器,然后记录密码的想法。

 


客户端在接受WiFi信号的时候有一个特点,在ssid相同的时候,会只保留信号强的那一个无线路由的ssid。

 

这样,只要伪造热点的ssid与原热点的相同,会有部分人搜到伪造的热点,从而登陆,记录密码。

 


本无线路由用的ddwrt的系统,装了wifidog来进行辅助web认证。

 


至于如何搭建web认证系统,百度一大把,但主要是用了wiwiz和wifiap这两个成熟的网站提供的方案。

 

但是,利用第三方的网站无法拦截到用户名和密码,而且无法控制认证的过程。

 

最好的解决方法是自己搭建一个简单的系统。

 


Wifidog的认证流程如下:
1、客户端发出一个http请求(http://www.xxx.com)
2、网关将该请求信息以及网关本身的一些信息作为参数,将原始的请求重定向到web认证服务器(http://auth_server/login/)
3、Web认证服务器通过客户端的认证之后,返回一个一次性的token,客户端带着这个token去网关上的wifidog开放的端口去做验证 (http://GatewayIP:GatewayPort/wifidog/auth?token=[auth token])
4、Wifidog拿到token后,到web认证服务器检测token是否有效,如果有效则通过客户端的验证,开放访问权限,并将客户端重定向到web认证服务器的欢迎界面(http://auth_server/portal/);如果token无效,则需要继续验证

 


Wifidog官方推荐的web认证服务软件为authpuppy (http://www.authpuppy.org),不过其代码比较复杂,可以参考wifidog之前的web认证服务软件。获取方式为:

 

svn checkout https://dev.wifidog.org/svn/trunk/wifidog-auth

web认证服务软件用php写成,重点文件为wifidog-auth\wifidog\login\index.php(客户端web认证、产生 token以及重定向到wifidog的开放端口)、wifidog-auth\wifidog\auth\index.php(wifidog验证 token)、wifidog-auth\wifidog\portal\index.php(认证成功后页面重定向)。宏定义在wifidog- auth\wifidog\include\common.php文件中。

 


了解了基本流程就可以DIY出一个简单的web认证服务器了。在认证的过程中可以顺便记录下客户端的密码。
路由器上Wifidog配置如下图。重点配置的地方为端口号(port),认证服务器(AuthServer Hostname), 认证服务器web端口(AuthServer HTTP Port),路径(AuthServer Path)。

 


1.png

 


web认证服务器端代码大家自己发挥吧。我个人只是实现了记录用户名密码这样一个简单的功能,如果要做的好的话可以用用户提交的密码到真正的认证服务器做 一次认证来返回合适的结果,以及自己搭建dns服务器伪装的更加逼真,但是对于那些比较敏感的用户,还是不容易进行欺骗的,比如用回会发现ssl加密不见 了。

考虑到功耗和实用问题,我的web认证服务器是搭建在树莓派上的。配置好无线路由的WLAN确保能联网之后,设置路由器的ip为10.1.1.1,手工配 置树莓派静态ip为10.1.1.2。树莓派上安装nginx和php,配置好webserver的环境,上传自己的代码。开启无线路由的wifidog 就可以守株待兔了。

当用户连接到自己搭建的无线路由器之后,可以说所有的网络流量都在控制之中了。不过怎么拿到这些流量成了一个问题。在此有三种拿到流量的方法。

 


1、ARP欺骗
这个不多说,大家都懂。不过有种偏离正题的感觉。
2、网线嗅探
当所处的环境通过网线来连到互联网时可用这个方法。将网线接入自制的硬件并将另一端插到无线路由的WLAN口,做好相应的配置。所需硬件参见我之前的一个帖子。原理类似于Throwing star lan tap,直接监听网线上的数据(无线路由的WLAN口)

 

3、通过笔记本做中介
当所处的环境只有无线网连到Internet时,可用笔记本来搭建一个中介。
其连接关系为:
AP—无线网卡—有线网卡—自己的无线路由—受害者
这时用笔记本就可以直接嗅探到所有的数据。

 


这里以windows环境为例,演示如何搭建这个数据流链条。
在无线网卡连接到无线网之后,在属性中选择Internet连接共享,共享给以太网卡(有线网卡)。

 


2.png

 


此时有线网卡的ip会被设置为192.168.137.1

 


3.png

 


用网线连接有线网卡的网口和无线路由的WLAN。在无线路由的配置页面,将WLAN口配置静态ip为192.168.137.2,子网掩码255.255.255.0,网关为192.168.137.1。
这时整个数据流链条便搭建成功。

 


至于在linux下的搭建,注意打开ip_forward功能,并配置好iptables。因为没有linux环境,不在此详细演示。

对于流经网卡的数据包,可以收集的信息主要有两种:密码和session。
windows下的cain用来嗅探并提取得到的用户名密码,改下规则也能得到特定的cookie。
linux下的ettercap设置好规则能获取到几乎所有想要的信息,还能用来更改返回的web页面、挂马、添加cookie等等,可谓神器。


至于开启了ssl加密的服务器,可以用ssltrip来得到明文传送的数据。
如果不怕麻烦的话,还可以自己搭设dns服务器来钓鱼,不过这样就有些杀鸡用牛刀了。

PS:最近出了个叫极路由的东西,号称自动翻墙。目测是内置了一个vpn。大家有兴趣可以去了解下~
PPS:利用web认证方式的热点是挂马利器哦

[转载]wifidog 认证 php - 逆雪寒的天坑 - 博客频道 - CSDN.NET

mikel阅读(1083)

[转载]wifidog 认证 php – 逆雪寒的天坑 – 博客频道 – CSDN.NET.

<p style="margin-top: 0px; margin-bottom: 10px; padding-top: 0px; padding-bottom: 0px;">1.首先简单说说wifidog认证的过程


客户端首次连接到wifi后,浏览器请求将会被重定向到:


login/?gw_address=%s&gw_port=%d&gw_id=%s&url=%s


验证通过后,客户端被重定向到网关,url格式如下:


http://网关地址:网关端口/wifidog/auth?token=


wifidong会启动一个线程周期性地报告每一个用户的状态信息,并通过如下地址发送给认证


服务器:


auth_server:/auth/?stage=


ip=


mac=


token=


incoming=


outgoing=


认证服务器根据该状态信息决定是否允许该用户继续连接,并回复网关,回复格式为:Auth:状态码,


如:Auth:1


常用状态码:


0:AUTH_DENIED,表示拒绝


1:AUTH_ALLOWED,验证通过


验证通过后,将重定向到如下地址:


portal/?gw_id=%s


wifidog的ping协议


wifidog通过ping协议将当前状态信息发送给认证服务器,发送地址为:


http://auth_sever/ping/?


gw_id=%s


sys_uptime=%lu


sys_memfree=%u


sys_load=%.2f


wifidog_uptime=%lu


认证服务器须返回一个“Pong”作为回应。


具体php实现代码如下


<pre code_snippet_id="335795" snippet_file_name="blog_20140509_1_6007550" name="code">public function auth()
&nbsp; &nbsp; {
&nbsp; &nbsp; <span style="white-space:pre">	</span>//响应客户端的定时认证,可在此处做各种统计、<a title="计费" href="index.php?c=search&amp;key=%E8%AE%A1%E8%B4%B9" target="_blank">计费</a>等等
&nbsp; &nbsp; <span style="white-space:pre">	</span>/*
&nbsp; &nbsp; <span style="white-space:pre">		</span>wifidog 会通过这个接口传递连接客户端的信息,然后根据返回,对客户端做<a title="开通" href="index.php?c=search&amp;key=%E5%BC%80%E9%80%9A" target="_blank">开通</a>、断开等处理,具体返回值可以看wifidog的文档
&nbsp; &nbsp; <span style="white-space:pre">	</span>wifidog主要提交如下参数
&nbsp; &nbsp; <span style="white-space:pre">	</span>1.ip
&nbsp; &nbsp; <span style="white-space:pre">	</span>2. mac
&nbsp; &nbsp; <span style="white-space:pre">	</span>3. token(login页面<a title="下发" href="index.php?c=search&amp;key=%E4%B8%8B%E5%8F%91" target="_blank">下发</a>的token)
&nbsp; &nbsp; <span style="white-space:pre">	</span>4.incoming 下载流量
&nbsp; &nbsp; <span style="white-space:pre">	</span>5.outgoing 上传流量
&nbsp; &nbsp; <span style="white-space:pre">	</span>6.stage &nbsp;认证阶段,就两种 login 和 counters
&nbsp; &nbsp; <span style="white-space:pre">	</span>*/
&nbsp; &nbsp;&nbsp;
&nbsp; &nbsp;&nbsp;
&nbsp; &nbsp; <span style="white-space:pre">	</span>$stage = $_GET['stage'] == 'counters'?'counters':'login';
&nbsp; &nbsp; <span style="white-space:pre">	</span>if($stage == 'login')
&nbsp; &nbsp; <span style="white-space:pre">	</span>{
&nbsp; &nbsp; <span style="white-space:pre">		</span>//XXXX跳过login 阶段的处理XXXX不能随便跳过的
&nbsp; &nbsp; <span style="white-space:pre">		</span>//默认返回 允许
&nbsp; &nbsp; <span style="white-space:pre">		</span>echo "Auth: 1";
&nbsp; &nbsp; <span style="white-space:pre">	</span>}
&nbsp; &nbsp; <span style="white-space:pre">	</span>else if($stage == 'counters')
&nbsp; &nbsp; <span style="white-space:pre">	</span>{
&nbsp; &nbsp;&nbsp;
&nbsp; &nbsp; <span style="white-space:pre">		</span>//做一个简单的流量判断验证,下载流量超值时,返回<a title="下线" href="index.php?c=search&amp;key=%E4%B8%8B%E7%BA%BF" target="_blank">下线</a>通知,否则保持在线
&nbsp; &nbsp; <span style="white-space:pre">		</span>if(!empty($_GET['incoming']) and $_GET['incoming'] &gt; 10000000)
&nbsp; &nbsp; <span style="white-space:pre">		</span>{
&nbsp; &nbsp; <span style="white-space:pre">			</span>echo "Auth: 0";
&nbsp; &nbsp; <span style="white-space:pre">		</span>}else{
&nbsp; &nbsp; <span style="white-space:pre">			</span>echo "Auth: 1\n";
&nbsp; &nbsp; <span style="white-space:pre">		</span>}
&nbsp; &nbsp; <span style="white-space:pre">	</span>}
&nbsp; &nbsp; <span style="white-space:pre">	</span>else
&nbsp; &nbsp; <span style="white-space:pre">		</span>echo "Auth: 0"; //其他情况都返回拒绝
&nbsp; &nbsp; <span style="white-space:pre">		</span>
&nbsp; &nbsp; <span style="white-space:pre">		</span> <span style="white-space:pre">	</span>
&nbsp; &nbsp; <span style="white-space:pre">	</span>/*
&nbsp; &nbsp; <span style="white-space:pre">		</span>返回值:主要有这两种就够了
&nbsp; &nbsp; <span style="white-space:pre">	</span>0 - 拒绝
&nbsp; &nbsp; <span style="white-space:pre">	</span>1 - <a title="放行" href="index.php?c=search&amp;key=%E6%94%BE%E8%A1%8C" target="_blank">放行</a>
&nbsp; &nbsp;&nbsp;
&nbsp; &nbsp; <span style="white-space:pre">	</span>官方文档如下
&nbsp; &nbsp; <span style="white-space:pre">	</span>0 - AUTH_DENIED - User firewall users are deleted and the user removed.
&nbsp; &nbsp; <span style="white-space:pre">	</span>6 - AUTH_VALIDATION_FAILED - User email validation timeout has occured and user/firewall is deleted(用户邮件验证超时,防火墙关闭该用户)
&nbsp; &nbsp; <span style="white-space:pre">	</span>1 - AUTH_ALLOWED - User was valid, add firewall rules if not present
&nbsp; &nbsp; <span style="white-space:pre">	</span>5 - AUTH_VALIDATION - Permit user access to email to get validation email under default rules (用户邮件验证时,向用户开放email)
&nbsp; &nbsp; <span style="white-space:pre">	</span>-1 - AUTH_ERROR - An error occurred during the validation process
&nbsp; &nbsp; <span style="white-space:pre">	</span>*/
&nbsp; &nbsp; }
&nbsp; &nbsp; public function portal()
&nbsp; &nbsp; {
&nbsp; &nbsp; <span style="white-space:pre">	</span>/*
&nbsp; &nbsp; <span style="white-space:pre">	</span> wifidog 带过来的参数 如下
&nbsp; &nbsp; <span style="white-space:pre">	</span>1. gw_id
&nbsp; &nbsp; <span style="white-space:pre">	</span>*/
&nbsp; &nbsp; <span style="white-space:pre">	</span>//重定到指定网站 或者 显示splash广告页面
&nbsp; &nbsp; <span style="white-space:pre">	</span>redirect('http://www.baidu.com', 'location', 302);
&nbsp; &nbsp; <span style="white-space:pre">		</span>
&nbsp; &nbsp; }
&nbsp; &nbsp; public function ping()
&nbsp; &nbsp; {
&nbsp; &nbsp; <span style="white-space:pre">	</span>//url请求 "gw_id=$gw_id&amp;sys_uptime=$sys_uptime&amp;sys_memfree=$sys_memfree&amp;sys_load=$sys_load&amp;wifidog_uptime=$wifidog_uptime";
&nbsp; &nbsp; <span style="white-space:pre">	</span>//log_message($this-&gt;config-&gt;item('MY_log_threshold'), __CLASS__.':'.__FUNCTION__.':'.debug_printarray($_GET));
&nbsp; &nbsp;&nbsp;
&nbsp; &nbsp; <span style="white-space:pre">	</span>//判断各种参数是否为空
&nbsp; &nbsp; <span style="white-space:pre">	</span>if( !(isset($_GET['gw_id']) and isset($_GET['sys_uptime']) and isset($_GET['sys_memfree']) and isset($_GET['sys_load']) and isset($_GET['wifidog_uptime']) ) )
&nbsp; &nbsp; <span style="white-space:pre">	</span>{
&nbsp; &nbsp; <span style="white-space:pre">		</span>echo '{"error":"2"}';
&nbsp; &nbsp; <span style="white-space:pre">		</span>return;
&nbsp; &nbsp; <span style="white-space:pre">	</span>}
&nbsp; &nbsp; <span style="white-space:pre">	</span>//添加<a title="心跳" href="index.php?c=search&amp;key=%E5%BF%83%E8%B7%B3" target="_blank">心跳</a>日志处理功能
&nbsp; &nbsp; <span style="white-space:pre">	</span>/*
&nbsp; &nbsp; <span style="white-space:pre">		</span>此处可获取 wififog提供的 如下参数
&nbsp; &nbsp; <span style="white-space:pre">	</span>1.gw_id &nbsp;来自wifidog 配置文件中,用来区分不同的路由设备
&nbsp; &nbsp; <span style="white-space:pre">	</span>2.sys_uptime 路由器的系统启动时间
&nbsp; &nbsp; <span style="white-space:pre">	</span>3.sys_memfree 系统内存使用百分比
&nbsp; &nbsp; <span style="white-space:pre">	</span>4.wifidog_uptime wifidog持续运行时间(这个数据经常会有问题)
&nbsp; &nbsp; <span style="white-space:pre">	</span>*/
&nbsp; &nbsp;&nbsp;
&nbsp; &nbsp; <span style="white-space:pre">	</span>//返回值
&nbsp; &nbsp; <span style="white-space:pre">	</span>echo 'Pong';
&nbsp; &nbsp; }
&nbsp; &nbsp; /**
&nbsp; &nbsp; &nbsp;* wifidog 的gw_message 接口,信息提示页面
&nbsp; &nbsp; &nbsp;*/
&nbsp; &nbsp; function gw_message()
&nbsp; &nbsp; {
&nbsp; &nbsp; <span style="white-space:pre">	</span>if (isset($_REQUEST["message"])) {
&nbsp; &nbsp; <span style="white-space:pre">		</span>switch ($_REQUEST["message"]) {
&nbsp; &nbsp; <span style="white-space:pre">			</span>case 'failed_validation':
&nbsp; &nbsp; <span style="white-space:pre">				</span>//auth的stage为login时,被服务器返回AUTH_VALIDATION_FAILED时,<a title="来到" href="index.php?c=search&amp;key=%E6%9D%A5%E5%88%B0" target="_blank">来到</a><a title="该处" href="index.php?c=search&amp;key=%E8%AF%A5%E5%A4%84" target="_blank">该处</a>处理
&nbsp; &nbsp; <span style="white-space:pre">				</span>//认证失败,请重新认证
&nbsp; &nbsp; <span style="white-space:pre">				</span>break;
&nbsp; &nbsp; <span style="white-space:pre">			</span>case 'denied':
&nbsp; &nbsp; <span style="white-space:pre">				</span>//auth的stage为login时,被服务器返回AUTH_DENIED时,来到该处处理
&nbsp; &nbsp; <span style="white-space:pre">				</span>//认证被拒
&nbsp; &nbsp; <span style="white-space:pre">				</span>break;
&nbsp; &nbsp; <span style="white-space:pre">			</span>case 'activate':
&nbsp; &nbsp; <span style="white-space:pre">				</span>//auth的stage为login时,被服务器返回AUTH_VALIDATION时,来到该处处理
&nbsp; &nbsp; <span style="white-space:pre">				</span>//待激活
&nbsp; &nbsp; <span style="white-space:pre">				</span>break;
&nbsp; &nbsp; <span style="white-space:pre">			</span>default:
&nbsp; &nbsp; <span style="white-space:pre">				</span>break;
&nbsp; &nbsp; <span style="white-space:pre">		</span>}
&nbsp; &nbsp; <span style="white-space:pre">	</span>}else{
&nbsp; &nbsp; <span style="white-space:pre">		</span>//不回显任何信息
&nbsp; &nbsp; <span style="white-space:pre">	</span>}
&nbsp; &nbsp; }</pre>
</p>

[转载]修改WIN2008下IIS调用FASTCGI进程数! - 漠北怪叟的日志 - 网易博客

mikel阅读(1272)

[转载]修改WIN2008下IIS调用FASTCGI进程数! – 漠北怪叟的日志 – 网易博客.

在WIN2008的IIS7上使用FASTCGI调用PHP-CGI.EXE,默认只有4个进程,这样对于大流量的网站为说,进程数不足带来的进程排队现象十分严重,解决方案如下。
32位系统 http://www.iis.net/Downloads/files/AdminPack/TP2/AdminPack_x86.msi
64位系统 http://www.iis.net/Downloads/files/AdminPack/TP2/AdminPack_amd64.msi
下载如下工具,安装后,如下图。
修改WIN2008下IIS调用FASTCGI进程数! - 小三子 - 怪叟博客

修改WIN2008下IIS调用FASTCGI进程数! - 小三子 - 怪叟博客
添加
PHP_FCGI_MAX_REQUESTS = 10000
添加后,不用重启IIS,就能在进程管理器看到效果。
修改WIN2008下IIS调用FASTCGI进程数! - 小三子 - 怪叟博客
似乎并没有达到设定的值,不过浏览网站明显感觉到性能的提示。
修改WIN2008下IIS调用FASTCGI进程数! - 小三子 - 怪叟博客
内存占用~~ 其实微软的系统有一个不好处在于, 服务器的内存占用,要尽量让服务去使用,而并不像客户端使用一样,留足够多的内存给用户的应用程序。

不过,跑PHP还是LINUX用着顺手,WIN上面跑确实不怎么样。

[转载]java unix时间戳转换 - 坏混混 - 博客园

mikel阅读(1343)

转载java unix时间戳转换 – 坏混混 – 博客园.

把java时间戳转换成unix时间戳:

 

Timestamp appointTime=Timestamp.valueOf(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                Date date = df.parse(String.valueOf(appointTime));
                long s=date.getTime();

 

String unixDate=String.valueOf(s).substring(0, 10);

把unix时间戳转换成java时间戳:

String date = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new java.util.Date(UnixTimestamp * 1000))
如何在不同编程语言中获取现在的Unix时间戳(Unix timestamp)?
Java time
JavaScript Math.round(new Date().getTime()/1000) getTime()返回数值的单位是毫秒
Microsoft .NET / C# epoch = (DateTime.Now.ToUniversalTime().Ticks – 621355968000000000) / 10000000
MySQL SELECT unix_timestamp(now())
Perl time
PHP time()
PostgreSQL SELECT extract(epoch FROM now())
Python 先 import time 然后 time.time()
Ruby 获取Unix时间戳:Time.now 或 Time.new 显示Unix时间戳:Time.now.to_i
SQL Server SELECT DATEDIFF(s, ‘1970-01-01 00:00:00’, GETUTCDATE())
Unix / Linux date +%s
VBScript / ASP DateDiff(“s”, “01/01/1970 00:00:00”, Now())
其他操作系统 (如果Perl被安装在系统中) 命令行状态:perl -e “print time”
如何在不同编程语言中实现Unix时间戳(Unix timestamp) → 普通时间?
JavaScript 先 var unixTimestamp = new Date(Unix timestamp * 1000) 然后 commonTime = unixTimestamp.toLocaleString()
Linux date -d @Unix timestamp
MySQL from_unixtime(Unix timestamp)
Perl 先 my $time = Unix timestamp 然后 my ($sec, $min, $hour, $day, $month, $year) = (localtime($time))[0,1,2,3,4,5,6]
PHP date(‘r’, Unix timestamp)
PostgreSQL SELECT TIMESTAMP WITH TIME ZONE ‘epoch’ + Unix timestamp) * INTERVAL ‘1 second’;
Python 先 import time 然后 time.gmtime(Unix timestamp)
Ruby Time.at(Unix timestamp)
SQL Server DATEADD(s, Unix timestamp, ‘1970-01-01 00:00:00’)
VBScript / ASP DateAdd(“s”, Unix timestamp, “01/01/1970 00:00:00”)
其他操作系统 (如果Perl被安装在系统中) 命令行状态:perl -e “print scalar(localtime(Unix timestamp))”
如何在不同编程语言中实现普通时间 → Unix时间戳(Unix timestamp)?
JavaScript var commonTime = new Date(Date.UTC(year, month – 1, day, hour, minute, second))
MySQL SELECT unix_timestamp(time) 时间格式: YYYY-MM-DD HH:MM:SS 或 YYMMDD 或 YYYYMMDD
Perl 先 use Time::Local 然后 my $time = timelocal($sec, $min, $hour, $day, $month, $year);
PHP mktime(hour, minute, second, day, month, year)
PostgreSQL SELECT extract(epoch FROM date(‘YYYY-MM-DD HH:MM:SS‘));
Python 先 import time 然后 int(time.mktime(time.strptime(‘YYYY-MM-DD HH:MM:SS‘, ‘%Y-%m-%d %H:%M:%S’)))
Ruby Time.local(year, month, day, hour, minute, second)
SQL Server SELECT DATEDIFF(s, ‘1970-01-01 00:00:00’, time)
Unix / Linux date +%s -d”Jan 1, 1970 00:00:01″
VBScript / ASP DateDiff(“s”, “01/01/1970 00:00:00”, time)

 

门户级UGC系统的技术进化路线——新浪新闻评论系统的架构演进和经验总结

mikel阅读(1068)

.

评论系统,或者称为跟帖、留言板,是所有门户网站的核心标准服务组件之一。与论坛、博客等其他互联网UGC系统相比, 评论系统虽然从产品功能角度衡量相对简单,但因为需要能够在突发热点新闻事件时,在没有任何预警和准备的前提下支撑住短短几分钟内上百倍甚至更高的访问量 暴涨,而评论系统既无法像静态新闻内容业务那样通过CDN和反向代理等中间缓存手段化解冲击,也不可能在平时储备大量冗余设备应对突发新闻,所以如何在有 限的设备资源条件下提升系统的抗压性和伸缩性,也是对一个貌似简单的UGC系统的不小考验。

新闻评论系统的起源

新浪网很早就在新闻中提供了评论功能,最开始是使用Perl语言开发的简单脚本,目前能找到的最早具备评论功能的新闻是2000年4月7日的,经过多次系统升级,2014年前的评论地址已经失效了,但数据仍保存在数据库中。直到今天,评论仍是国内所有新闻网站的标配功能。

评论系统3.0

2003年左右,我接手负责评论系统,系统版本为3.0。当时的评论系统运行在单机环境,一台x86版本Solaris系统的Dell 6300服务器提供了全部服务,包括MySQL和Apache,以及所有前后台CGI程序,使用C++开发。

图1  3.0系统流程和架构

3.0系统的缓存模块设计得比较巧妙,以显示页面为单位缓存数据,因为评论页面依照提交时间降序排列,每新增一条评论,所有帖子都需要向下移动一位,所以缓存格式设计为每两页数据一个文件,前后相邻的两个文件有一页数据重复,最新的缓存文件通常情况下不满两页数据。

图2  页面缓存算法示意图

图2是假设评论总数95条,每页显示20条时的页面缓存结构,此时用户看到的第一页数据读取自“缓存页4”的95~76,第二页数据读取自“缓存页3”的75~56,以此类推。

这 样发帖动作对应的缓存更新可简化为一次文件追加写操作,效率最高。而且可保证任意评论总量和显示顺序下的翻页动作,都可在一个缓存文件中读到所需的全部数 据,而不需要跨页读取再合并。缺点是更新评论状态时(如删除),需要清空自被删除帖子开始的所有后续缓存文件。缓存模块采取主动+被动更新模式,发帖为主 动,每次发帖后触发一次页面缓存追加写操作。更新评论状态为被动,所涉及缓存页面文件会被清空,直到下一次用户读取页面缓存时再连接数据库完成查询,然后 更新页面缓存,以备下次读取。这个针对发帖优化的页面缓存算法继续沿用到了后续版本的评论系统中。

此时的评论系统就已具备了将同一专题事件下所有新闻评论汇总显示的能力,在很长一段时间内这都是新浪评论系统的独有功能。

虽 然3.0系统基本满足了当时的产品需求,但毕竟是单机系统,热点新闻时瞬间涌来的大量发帖和读取操作,经常会压垮这台当时已属高配的4U服务器,频繁显示 资源耗尽的错误页面。我接手后的首要任务就是尽量在最短时间内最大限度降低系统的宕机频率,通过观察分析确定主要性能瓶颈在数据库层面。

3.0 系统中,每个新闻频道的全部评论数据都保存在一张MyISAM表中,部分频道的数据量已经超过百万,在当时已属海量规模,而且只有一个数据库实例,读写竞 争非常严重。一旦有评论状态更新,就会导致很多缓存页面失效,瞬间引发大量数据库查询,进一步加剧了读写竞争。当所有CGI进程都阻塞在数据库环节无法退 出时,殃及Apache,进而导致系统Load值急剧上升无法响应任何操作,只有重启才能恢复。

解决方案是增加了一台 FreeBSD系统的低配服务器用于数据库分流,当时MySQL的版本是3.23,Replication主从同步还未发布,采取的办法是每天给数据表减 肥,把超过一周的评论数据搬到2号服务器上,保证主服务器的评论表数据量维持在合理范围,在这样的临时方案下,3.0系统又撑了几个月。

现在看来,在相当简陋的系统架构下,新浪评论系统3.0与中国互联网产业的门户时代一起经历了南海撞机、911劫机、非典、孙志刚等新闻事件。

评论系统4.0启动

2004年左右,运行了近三年的3.0系统已无法支撑新浪新闻流量的持续上涨,技术部门启动了4.0计划,核心需求就是三个字:不宕机。

因为当时我还负责了新浪聊天系统的工作,不得不分身应对新旧系统的开发维护和其他项目任务,所以在现有评论系统线上服务不能中断的前提下,制定了数据库结构不变,历史数据全部保留,双系统逐步无缝切换,升级期间新旧系统并存的大方针。

第一阶段:文件系统代替数据库,基于ICE的分布式系统

既然3.0系统数据库结构不可变,除了把数据库升级到MySQL 4.0启用Repliaction分解读写压力以外,最开始的设计重点是如何把数据库与用户行为隔离开。

解 决方案是在MySQL数据库和页面缓存模块之间,新建一个带索引的数据文件层,每条新闻的所有评论都单独保存在一个索引文件和一个数据文件中,期望通过把 对数据库单一表文件的读写操作,分解为文件系统上互不干涉可并发执行的读写操作,来提高系统并发处理能力。在新的索引数据模块中,查询评论总数、追加评 论、更新评论状态都是针对性优化过的高效率操作。从这时起,MySQL数据库就降到了只提供归档备份和内部管理查询的角色,不再直接承载任何用户更新和查 询请求了。

同时引入了数据库更新队列来缓解数据库并发写操作的压力,因为当时消息队列中间件远不如现在百花齐放,自行实现了一个简单的文件方式消息队列模块,逐步应用到4.0系统各个模块间异步通信场合中。

图3  4.0系统流程

选用了ICE作为RPC组件,用于所有的模块间调用和网络通信,这大概是刚设计4.0系统时唯一没做错的选择,在整个4.0系统项目生命周期,ICE的稳定性和性能表现从未成为过问题。

图4  4.0索引缓存结构

4.0系统开发语言仍为C++,因为同时选用了MySQL 4.0、ICE、Linux系统和新文件系统等多项应用经验不足的新技术,也为后来的系统表现动荡埋下了伏笔(新浪到2005年左右才逐步从FreeBSD和Solaris迁移到了CentOS系统)。

图5  4.0系统架构

此时的4.0评论系统已从双机互备扩容到五机集群,进入小范围试用阶段,虽然扛过了刘翔第一次夺金时创纪录的发帖高峰,但倒在了2004年亚洲杯中国队1 : 3败于日本队的那个夜晚。

当时系统在进入宕机之前的最高发帖速度大约是每分钟千帖量级,在十年前还算得上是业界同类系统的峰值,最终确认问题出在文件系统的I/O负载上。

设 计索引缓存模块时的设想过于理想化,虽然把单一数据表的读写操作分解到了文件系统的多个文件上,但不可避免地带来了对机械磁盘的大量随机读写操作,在 CentOS默认的Ext3文件系统上,每条新闻对应两个文件的设计(2004年新浪新闻总量为千万左右),虽然已采取了128×256的两层目录 HASH来预防单目录下文件过多隐患,但刚上线时还表现良好的系统,稍过几个月后就把文件系统彻底拖垮了。

既然 Ext3无法应对大数量文件的频繁随机读写,当时我们还可以选择使用B*树数据结构专为海量文件优化的ReiserFS文件系统,在与系统部同事配合反复 对比测试,解决了ReiserFS与特定Linux Kernel版本搭配时的kswapd进程大量消耗CPU资源的问题后,终于选定了可以正常工作的Kernel和ReiserFS对应版本,当然这也埋下 了ReiserFS作者杀妻入狱后新装的CentOS服务器找不到可用的ReiserFS安装包这个大隐患。

第二阶段:全系统异步化,索引分页算法优化

直 到这个阶段,新浪评论系统的前端页面仍是传统的Apache+CGI模式,随着剩余频道的逐步切换,新浪评论系统升级为静态HTML页面使用 XMLHTTP组件异步加载XML数据的AJAX模式,当时跨域限制更少的JSON还未流行。升级为当时刚刚开始流行的AJAX模式并不是盲目追新,而是 为了实现一个非常重要的目标:缓存被动更新的异步化。

随着消息队列的普遍应用,4.0系统中所有的数据库写操作和缓存 主动更新(即后台程序逻辑触发的更新)都异步化了,当时已在实践中证明,系统访问量大幅波动时,模块间异步化通信是解决系统伸缩性和保证系统响应性的唯一 途径。但在CGI页面模式下,由用户动作触发的缓存被动更新,只能阻塞在等待状态,直到查询数据和更新缓存完成后才能返回,会导致前端服务器Apache CGI进程的堆积。

使用AJAX模式异步加载数据,可在几乎不影响用户体验的前提下完成等待和循环重试动作,接收缓 存更新请求的支持优先级的消息队列还可合并对同一页面的重复请求,也隔离了用户行为对前端服务器的直接冲击,极大提高了前端服务器的伸缩性和适应能力,甚 至连低硬件配置的客户端电脑在AJAX模式加载数据时都明显更顺畅了。前端页面静态化还可将全部数据组装和渲染逻辑,包括分页计算都转移到了客户端浏览器 上,充分借用用户端资源,唯一的缺点是对SEO不友好。

通过以上各项措施,此时的4.0系统抗冲击能力已有明显改善,但是接下来出现了新的问题。在3.0系统时代,上万条评论的新闻已属少见,随着业务的增长,类似2005年超女专题或者体育频道NBA专题这样千万评论数级别的巨无霸留言板开始出现。

为 了提高分页操作时定位和读取索引的效率,4.0系统的算法是先通过mmap操作把一个评论的索引文件加载到内存,然后按照评论状态(通过或者删除)和评论 时间进行快速排序,筛选出通过状态的帖子并按时间降序排列,这样读取任意一页的索引数据,都是内存中一次常量时间成本的偏移量定位和读取操作。几百条或者 几千条评论时,上述方案运作得很好,但在千万留言数量的索引文件上进行全量排序,占用大量内存和CPU资源,严重影响系统性能。我们曾尝试改用 BerkeleyDB的Btree模式来存储评论索引,但性能不升反降。

为避免大数据量排序操作的成本,只能改为简单 遍历方式,从头开始依次读取,直到获取所需的数据。虽可通过从索引文件的两端分别作为起点,来提升较新和较早页面的定位效率,但遍历读取本身就是一个随着 请求页数增大越来越慢的线性算法,并且随着4.0系统滑动翻页功能的上线,原本用户无法轻易访问到的中间页面数据也开始被频繁请求,因此最终改为了两端精 确分页,中间模糊分页的方式。模糊分页就是根据评论帖子的通过比例,假设可显示帖子均匀分布,一步跳到估算的索引偏移位置。毕竟在数十万甚至上百万页的评 论里,精确计算分页偏移量没有太大实际意义。

图6  异步缓存更新流程

2005 年非常受关注的日本申请加入联合国常任理事国事件,引发了各家网站的民意沸腾,新浪推出了征集反日入常签名活动并在短短几天内征集到2000多万签名。因 为没有预计到会有如此多的网民参与,最开始简单实现的PHP+MySQL系统在很短时间内就无法响应了,然后基于4.0评论系统紧急加班开发了一个签名请 愿功能,系统表现稳定。

评论系统4.0第三阶段:简化缓存策略,进一步降低文件系统I/O

到 了这个阶段,硬件资源进一步扩容,评论系统的服务器数量终于达到了两位数,4.0系统已实现了当初的“不宕机”设计目标,随着网站的改版,所有新闻页面 (包括网站首页)都开始实时加载和显示最新的评论数量和最新的帖子列表,此时4.0系统承受的Hits量级已接近新浪新闻静态池的水平。从这时起,新浪评 论系统再没有因为流量压力宕机或者暂停服务过。

前面提到,新装的CentOS系统很难找到足够新版本的ReiserFS安装包,甚至不得不降级系统版本,一直困扰性能表现的文件系统也接近了优化的极限,这时候Memcached出现了。

图7  系统架构

2006 年左右Memcached取代了4.0系统中索引缓存模块的实体数据部分(主要是评论正文),索引缓存模块在文件系统上只存储索引数据,评论文本都改用 Memcached存储,极大降低了文件系统的I/O压力。因为系统流量与热点事件的时间相关性,仅保存最近几周的评论就足以保证系统性能,极少量过期数 据访问即使穿透到MySQL也问题不大,当然服务器宕机重启和新装服务器上线时要非常留意数据的加载预热。

之后4.0系统进入稳定状态,小修小补,又坚持服役了若干年,并逐步拓展到股票社区、签名活动、三方辩论、专家答疑、观点投票等产品线,直到2010年之后5.0系统的上线。

2008年5月12日,我发现很多网友在地震新闻评论中询问亲友信息,就立即开发了基于评论系统的地震寻亲功能并于当晚上线。大约一周后为了配合Google发起的寻亲数据汇总项目,还专门为Google爬虫提供了非异步加载模式的数据页面以方便其抓取。

2004年上线的4.0系统,2010~2011年后被5.0系统取代逐步下线,从上线到下线期间系统处理的用户提交数据量变化趋势如图8所示。

图8  系统流量变化图

高访问量UGC系统设计总结

纵观整个4.0系统的设计和优化过程,在硬件资源有限的约束下,依靠过渡设计的多层缓冲,完成了流量剧烈波动时保障服务稳定的最基本目标,但也确实影响到了UGC系统最重要的数据更新实时性指标,数据更新的实时性也是之后5.0系统的重点改进方向。

总结下来,一般UGC系统的设计方针就是通过降低系统次要环节的实时一致性,在合理的成本范围内,尽量提高系统响应性能,而提高响应性能的手段归根结底就是三板斧:队列(Queue)、缓存(Cache)和分区(Sharding)。

  • 队列:可以缓解并发写操作的压力,提高系统伸缩性,同时也是异步化系统的最常见实现手段。
  • 缓存:从文件系统到数据库再到内存的各级缓存模块,解决了数据就近读取的需求。
  • 分区:保证了系统规模扩张和长期数据积累时,频繁操作的数据集规模在合理范围。

关于数据库,区分冷热数据,按照读写操作规律合理拆分存储,一般UGC系统近期数据才是热点,历史数据是冷数据。

  • 区分索引和实体数据,索引数据是Key,易变,一般用于筛选和定位,要保证充分的拆分存储,极端情况下要把关系数据库当NoSQL用;实体数据是Value,一般是正文文本,通常不变,一般业务下只按主键查询;两者要分开。
  • 区分核心业务和附加业务数据,每一项附加的新业务数据都单独存储,与核心业务数据表分开,既可降低核心业务数据库的变更成本,还可避免新业务频繁调整上下线时影响核心业务。

目前的互联网系统大都严重依赖MySQL的Replication主从同步来实现系统横向扩展,虽然MySQL在新版本中陆续加入RBR复制和半同步等机制,但从库的单线程写操作限制还是最大的制约因素,到现在还没有看到很理想的革新性解决方案。

关于缓存,从浏览器到文件系统很多环节都有涉及,这里主要说的是应用系统自己的部分。

  • 最好的缓存方案是不用缓存,缓存带来的问题往往多于它解决的问题。
  • 只有一次更新多次读取的数据才有必要缓存,个性化的冷数据没必要缓存。
  • 缓 存分为主动(Server推)和被动(Client拉)两种更新方式,各自适用于不用场景。主动更新方式一般适用于更新频率较高的热数据,可保证缓存未命 中时,失控的用户行为不会引发系统连锁反应,导致雪崩。被动更新方式一般适用于更新频率相对较低的数据,也可以通过上文提到的异步更新模式,避免连锁反应 和雪崩。
  • 缓存的更新操作尽量设计为覆盖方式,避免偶发数据错误的累积效应。

一 个UGC系统流量刚开始上涨时,初期的表面性能瓶颈一般会表现在Web Server层面,而实际上大多是数据库的原因,但经充分优化后,最终会落在文件系统或网络通信的I/O瓶颈上。直接承载用户访问冲击的前端服务器最好尽 量设计为无状态模式,降低宕机重启后的修复工作量。

顺带提及,我在新浪聊天和评论系统的开发过程中,逐步积累了一个Web应用开发组件库,在新浪全面转向PHP之前,曾用于新浪的内容管理(CMS)、用户注册和通行证、日志分析和论坛等使用C++的系统,目前发布于github.com/pi1ot/webapplib。

评论系统5.0方案

2010 年后针对4.0系统的缺陷,启动了5.0系统工作。因为工作的交接,5.0系统我只负责了方案设计,具体开发是交给其他同事负责的,线上的具体实现与原始 设计方案可能会有区别。5.0系统极大简化了系统层次,在保证抵抗突发流量波动性能的前提下,数据更新的及时性有了明显提高。

图9  4.5系统流程

图10  5.0系统流程

设计方案上的主要变化有以下几点。

  • 评论帖子ID从数据库自增整数改为UUID,提交时即可确定,消除了必须等待主库写入后才能确定评论ID的瓶颈,对各个层面的缓存逻辑优化有极大帮助。
  • 重新设计数据库结构,通过充分的数据切分,保证了所有高频业务操作都可在一个有限数据量的数据表中的一次简单读取操作完成,索引和文本数据隔离存储,在数据库中实现了原4.0系统中索引模块的功能,取消了4.0系统的索引缓存层。
  • 改用内存NoSQL缓存用户频繁读取的最新10~20页数据,取消了原4.0系统文件方式的页面缓存层。
  • 系统运行环境迁移到新浪云的内部版本:新浪动态平台,设备资源富裕度有了极大改善。
  • 改为Python语言开发,不用再像4.0系统那样每次更新时都要等待半个小时的编译过程,也不用再打包几百兆的执行文件同步到几十台服务器上,而语言层面的性能损失可以忽略不计。

新闻评论产品总结

新 闻评论作为微博之前最能反映舆情民意的UGC平台,长期承载了国内互联网用户对时事新闻的匿名表达欲望,曾经一度成为上到政府下到网民的关注焦点。虽然面 临了相对其他社区系统更为严厉的管控力度,也错过了实施实名制改造时迈向社区化的最佳时机,但无论如何,在21世纪的前十年,国内门户网站的新闻评论服 务,都是中国互联网产品和技术发展历史上绝对不能错过的一笔。

作者刘立,2000年毕业于哈尔滨工业大学计算机系,2000-2013年工作于新浪网研发中心和门户技术部门,目前在一家社交电商平台创业团队任技术负责人。