PHP,DDD,CQRS,Event Sourcing,Kubernetes,Docker,Golang

0%

6310d41cjw1em0lsear2xg20b408cn22

起初,网页设计者都会设计固定宽度的页面,最开始的电脑显示器分辨率种类不多,因为当时电脑本来就少,即使有变化也是 800 850 870 880。 后来随着显示器越来越多,以及笔记本的普及,这种方式的页面出现了问题。于是出现了一种新的布局方式宽度自适应布局。我们平时谈论的自适应布局,大多指的就是宽度自适应布局。 在这种布局下,出现了两派:

  • 百分比宽度布局
  • 流式布局

题主说的是第一派,宽度使用百分比,文字使用em。第二派的布局以 iGoogle 为代表(已经停止)。 再后来,浏览器大战 时代,firefox、Oparo、Chrome …… 出现,结束了 IE 一统江湖的局面,N 年没有更新的 IE6 发布了新版本,以前已 IE 为标准的 CSS 向 W3C 标准趋近,随后各种针对浏览器的 css hack 技术出现。 虽然浏览器这么多,但是响应式布局依然不是主流,人们还在使用 css hack 技术。注意我的用词——「不是主流」,虽然不是主流,不代表当时不被使用。 比如一向超前的伟大的 Google。当时没有响应式布局这个词语,但是慢慢出现了一个词——渐进增强,新词的出现总是伴随的旧词一起出现。就好比 3G 出现之前,没人管自己的手机叫 2G,所以,3G 和 2G 两个词是一起出现的(技术上当然2G技术先出现)。同理,渐进增强出现后,另一个词「优雅降级」也随之出现了。 词的意思可以自己看 wiki、Google,我只在这儿举一个例子,gmail 和 qqmail。 他俩的宽度都是 100%,都是自适应。但是: qqmail 就是 css hack 的完美体现,你用任何一个浏览器,几乎可以看到同一个样子的邮箱,腾讯的前端工程师们用各种 css hack 技术来展示邮箱页面,为的是统一的用户体验。 而 gmail 使用了渐进增强,你的浏览器越强,你看到的效果就越好,用户体验就越好。 再后来,就是大家都熟知的 Google 发布了 android,于是互联网大战从 PC 打到了手机。还有 HTML5 标准的发布。 手机虽然屏幕变小了,但是却提供了更丰富的功能。还记得以前用诺基亚上 QQ 的事儿吗?我们访问的是 3g.qq.com,当时我使用的是中兴的手机,访问 wap.qq.com,在后来的职能手机都是访问 m.qq.com。 不禁有人问「真的需要为每个手机分别设计一个网页吗?」、「真的需要为手机和电脑设计不同的网页吗?」,解决方法当然有很多种,可以看看 css zen garden 相信做过前端的都看过这个网站,一个神奇的网站。 最终的解决方案胜出者是响应式布局。 响应式布局被大家熟知的一个重要原因就是 twitter 开源了 bootstrap。Google 第一次完成了从先驱到烈士。 好了,上面介绍的是从从“自适应”到“响应式”,以下 Responsive design = RWD,Adaptive design = AWD。 先说共同点,两者都是因为越来越多的 移动设备( mobile, tablet device )加入到互联网中来而出现的为移动设备提供更好的体验的技术。用技术来使网页适应从小到大(现在到超大)的不同分辨率的屏幕。有人说,RWD 是 AWD 包含的一个子集。 RWD:Ethan Marcote 的文章是大家认为 RWD 的起源。他提出的 RWD 是采用 CSS 的 media query 技术,配合流体布局( fluid grids )和同样可以自适应的图片/视频等资源素材。以上所说,都是通过 HTML 和 CSS 就能完成的。一般来说,RWD 倾向于只改变元素的外观布局,而不大幅度改变内容。Jeffrey Zeldman 总结说,我们就把 RWD 定义为一切能用来为各种分辨率和设备性能优化视觉体验的技术吧。 AWD:Adaptive Design 是 Aaron Gustafson 的书的标题。他认为 AWD 在包括 RWD 的 CSS media query 技术以外,也要用 Javascript 来操作 HTML 来更适应移动设备的能力。AWD 有可能会针对移动端用户减去内容,减去功能。AWD 可以在服务器端就进行优化,把优化过的内容送到终端上。AWD 通常会牵扯到另外一个词 “progressive enhancement” 。 progressive enhancement(渐进增强):从针对最低端的,最低分辨率的设备的设计做起,逐步逐步为更高阶的设备增加功能和效果的做法。(换个角度说,也就是相当于为移动设备减去功能) RWD 和 AWD 在断点( break point )的区别: RWD 采用流体+断点,在断点之间,页面依然会随窗口大小自动缩放(通过 fluid grid ),直到遇到断点改变界面样式。相对的,AWD 只在针对几种分辨率(如1280,800,640,320px)进行优化,在断点之间的自动过渡比较少。 还有一种说法: RWD 一般来说需要在网页设计初期就开始(通常采用 mobile first 策略),所以旧的网站要做 RWD 很可能要完全重建。而 AWD 则采用保留现有桌面网站( desktop version )而对于更小的分辨率做针对性的优化(适应),这点对于很多老的网站来说很重要,因为重构成本太大。 另外,对于 mobile 用户的优化到底应该怎么做,有两方的说法各不相让。有人说,不应该因为用户使用的是 mobile device 就删去内容,限制他们的功能,应该平等对待。也有人说,正因为是移动设备,有其流量,性能,网速的局限性,用移动设备登录网站的目的也会更有针对性,要为 用户精简文字,精简最常用的功能放在首页,服务器端的优化才是真的针对 mobile 的优化。 最后: 在网上的各种说法里确实是有很多相互干扰相互矛盾的地方,但是其实技术都是摆在那里的。其实可以认为,AWD 在针对布局的优化中,可以采用 RWD 的策略,但是AWD 着力于更多其他的 JS 或者服务器上的优化,来强化移动端体验。 不用纠结于词汇,根据网站功能复杂度,预算和资源等,选择要使用的技术,从客户端的展现,到 JS,到服务器的优化等等。

今天试用了一下facebook的php性能调试工具xhprof,在安装的时候是一波三折,虽说从百度了安装方法,但也折腾了半天,不知是说明没写全还是我个人操作失误,那么我在这把我的安装方法讲述一下。环境是:Linux+Nginx+PHP

#cd /tmp
#mkdir xhprof
#cd xhprof
#wget http://pecl.php.net/get/xhprof-0.9.4.tgz
//解压
#tar zxf xhprof-0.9.4.tgz
#cd xhprof-0.9.4/extension
//拷贝xhprof_html和xhprof_lib两个文件夹至可访问的web目录下,我的web根目录为/home/www
#cp -r xhprof_html xhprof_lib /home/www
//运行phpize
#/usr/local/webserver/php/bin/phpize
//运行configure为下一步编译做准备,详情了解Linux下的configure命令
#./configure –with-php-config=/usr/local/php/bin/php-config
#make
#make install

安装完后你会看到一个提示Installing shared extensions /usr/local/php/lib/php/extensions/no-debug-non-zts-20060626/xhprof.so,说明xhprof.so这个模块被生成了 接下来修改php.ini文件,添加:

//添加xhprof.so这个扩展,位置就是刚才生成给你的
extension=/usr/local/php/lib/php/extensions/no-debug-non-zts-20060626/xhprof.so
//指定生成测试报告分析日志的目录,并保证可写权限
xhprof.output_dir=/home/www/tmp

重启php-fpm重新加载php配置文件 #/etc/init.d/php-fpm restart 至此安装完毕,可以搞个phpinfo();页面看看,如果能看到下图则说明安装成功了QQ截图20141103143752

xhprof的使用

写一个php脚本如

xhprof_enable();
function test()
{
return ‘this is a test demo’;
}
test();
$xhprofData = xhprof_disable();
include_once ‘/home/www/houseinfo/xhprof_lib/utils/xhprof_lib.php’;
include_once ‘/home/www/houseinfo/xhprof_lib/utils/xhprof_runs.php’;
$xhprofRuns = new XHProfRuns_Default();
$xhprofRuns->save_run($xhprofData,’xhprof_foo’);
?>

通过URL访问该脚本后,会在之前设定的/home/www/tmp(php.ini中设定的)目录下生成分析日志,如“5457280fd8500.xhprof_foo.xhprof” 其中5457280fd8500为日志生成的id,通过地址http://YOUR\_URL/xhprof_html/index.php?run=**5457315580b0e**&source=xhprof_foo即可查看性能分析(如下图) QQ截图20141103154222 你可以将上面php代码最后一行改为这样,以便直接点击链接到分析页查看,而无需自己再查看id并拼接成URL访问

save\_run($xhprofData,'xhprof_foo'); echo '查看分析报告'; ?>

xhprof提供3种报告: 一、单一运行报告:通过http://YOUR\_URL/xhprof\_html/index.php?run=**5457315580b0e**&source=xhprof\_foo地址查看的单一运行时的报告 二、diff报告:地址如http://YOUR\_URL/xhprof_html/index.php?run1=**xxxxxx**&run2=**xxxxxxx**&source=xhprof\_foo,提供两次对比id即可。 三、汇总报告,指定一组run id来汇总得到您想要的报告视图。如果你有三个XHProf运行,都在”xhprof_foo‘命名空间下,run id分别是1,2,3。要查看这些运行的汇总报告:http://YOUR\_URL/xhprof\_html/index.php?run**=1,2,3**&source=xhprof_foo XHProf输出说明 1. Inclusive Time : 包括子函数所有执行时间。 2. Exclusive Time/Self Time : 函数执行本身花费的时间,不包括子树执行时间。 3. Wall Time : 花去了的时间或挂钟时间。 4. CPU Time : 用户耗的时间+ 内核耗的时间 5. Inclusive CPU : 包括子函数一起所占用的CPU 6. Exclusive CPU : 函数自身所占用的CPU 可是这个界面看起来不是很直观也不爽,我们还可以装一个graphviz画图工具

#wget http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.28.0.tar.gz
#tar -zxvf graphviz-2.28.0.tar.gz
#cd graphviz-2.28.0
#./configure
#make
#make install

安装完成后,会生成/usr/local/bin/dot文件,确保路径在PATH环境变量里,以便XHProf能找到它,graphviz处于/usr/local/lib/graphviz。 #vi ~/.bash_profile QQ截图20141104133618#echo $PATH 输出一下看看应该有了这个路径 QQ截图20141104135151 之后进入分析页面点击[View Full Callgraph]就能看到类似下图了

callgraph

一、inode是什么?

理解inode,要从文件储存说起。

文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。

操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。

文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。

二、inode的内容

inode包含文件的元信息,具体来说有以下内容:

  * 文件的字节数

  * 文件拥有者的User ID

  * 文件的Group ID

  * 文件的读、写、执行权限

  * 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。

  * 链接数,即有多少文件名指向这个inode

  * 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:

stat example.txt

总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。

三、inode的大小

inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

每 个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定 在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。

df -i

查看每个inode节点的大小,可以用如下命令:

sudo dumpe2fs -h /dev/hda | grep “Inode size”

由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。

四、inode号码

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。

这 里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或 者绰号。表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号 码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

使用ls -i命令,可以看到文件名对应的inode号码:

ls -i example.txt

五、目录文件

Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。

目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。

ls命令只列出目录文件中的所有文件名:

ls /etc

ls -i命令列出整个目录文件,即文件名和inode号码:

ls -i /etc

如果要查看文件的详细信息,就必须根据inode号码,访问inode节点,读取信息。ls -l命令列出文件的详细信息。

ls -l /etc

六、硬链接

一 般情况下,文件名和inode号码是”一一对应”关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个 inode号码。这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访 问。这种情况就被称为”硬链接”(hard link)。

ln命令可以创建硬链接:

ln 源文件 目标文件

运 行上面这条命令以后,源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中有一项叫做”链接数”,记录指向该inode的文 件名总数,这时就会增加1。反过来,删除一个文件名,就会使得inode节点中的”链接数”减1。当这个值减到0,表明没有文件名指向这个inode,系 统就会回收这个inode号码,以及其所对应block区域。

这里顺便说一下目录文件的”链接数”。创建目录时, 默认会生成两个目录项:”.”和”..”。前者的inode号码就是当前目录的inode号码,等同于当前目录的”硬链接”;后者的inode号码就是当 前目录的父目录的inode号码,等同于父目录的”硬链接”。所以,任何一个目录的”硬链接”总数,总是等于2加上它的子目录总数(含隐藏目录),这里的 2是父目录对其的“硬链接”和当前目录下的”.硬链接“。

七、软链接

除了硬链接以外,还有 一种特殊情况。文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打 开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的”软链接”(soft link)或者”符号链接(symbolic link)。

这 意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:”No such file or directory”。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode”链接数”不会因此 发生变化。

ln -s命令可以创建软链接。

ln -s 源文文件或目录 目标文件或目录

八、inode的特殊作用

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。

  1. 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。

  2. 移动文件或重命名文件,只是改变文件名,不影响inode号码。

  3. 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。

第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时 候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的 inode则被回收。

九 实际问题 在一台配置较低的Linux服务器(内存、硬盘比较小)的/data分区内创建文件时,系统提示磁盘空间不足,用df -h命令查看了一下磁盘使用情况,发现/data分区只使用了66%,还有12G的剩余空间,按理说不会出现这种问题。 后来用df -i查看了一下/data分区的索引节点(inode),发现已经用满(IUsed=100%),导致系统无法创建新目录和文件。 查找原因: /data/cache目录中存在数量非常多的小字节缓存文件,占用的Block不多,但是占用了大量的inode。 解决方案: 1、删除/data/cache目录中的部分文件,释放出/data分区的一部分inode。 2、用软连接将空闲分区/opt中的newcache目录连接到/data/cache,使用/opt分区的inode来缓解/data分区inode不足的问题: ln -s /opt/newcache /data/cache 转自: http://www.ruanyifeng.com/blog/2011/12/inode.html http://blog.s135.com/post/295/ http://hi.baidu.com/leejun_2005/blog/item/d9aa13a53b3af6e99152ee7e.html

 100除以7的余数是2,意思就是说把100个东西七个七个分成一组的话最后还剩2个。余数有一个严格的定义:假如被除数是a,除数是b(假设它们 均为正整数),那么我们总能够找到一个小于b的自然数r和一个整数m,使得a=bm+r。这个r就是a除以b的余数,m被称作商。我们经常用mod来表示 取余,a除以b余r就写成a mod b = r。

如果两个数a和b之差能被m整除,那么我们就说a和b对模数m同余(关于m同余)。比 如,100-60除以8正好除尽,我们就说100和60对于模数8同余。它的另一层含义就是说,100和60除以8的余数相同。a和b对m同余,我们记作 a≡b(mod m)。比如,刚才的例子可以写成100≡60(mod 8)。你会发现这种记号到处都在用,比如和数论相关的书中就经常把a mod 3 = 1写作a≡1(mod 3)。

之所以把同余当作一种运算,是因为同余满足运算的诸多性质。比如,同余满足等价关系。具体地说,它满足自反性(一个数永远和自己同余)、对称性(a和b同余,b和a也就同余)和传递性(a和b同余,b和c同余可以推出a和c同余)。这三个性质都是显然的。

 同余运算里还有稍微复杂一些的性质。比如,同余运算和整数加减法一样满足“等量加等量,其和不变”。小学我们就知道,等式两边可以同时加上一个相等的数。例 如,a=b可以推出a+100=b+100。这样的性质在同余运算中也有:对于同一个模数m,如果a和b同余,x和y同余,那么a+x和b+y也同余。在 我看来,这个结论几乎是显然的。当然,我们也可以严格证明这个定理。这个定理对减法同样有效。 **性质:如果a≡b(mod m),x≡y(mod m),则a+x≡b+y(mod m)。** 证 明:条件告诉我们,可以找到p和q使得a-mp = b-mq,也存在r和s使得x-mr = y-ms。于是a-mp + x-mr = b-mq + y-ms,即a+x-m(p+r) = b+y-m(q+s),这就告诉我们a+x和b+y除以m的余数相同。 容易想到,两个同余式对应相乘,同余式两边仍然相等: **如果a≡b(mod m),x≡y(mod m),则ax≡by(mod m)。** 证明:条件告诉我们,a-mp = b-mq,x-mr = y-ms。于是(a-mp)(x-mr) = (b-mq)(y-ms),等式两边分别展开后必然是ax-m(…) = by-m(…)的形式,这就说明ax≡by(mod m)。 现在你知道为什么有的题要 叫你“输出答案mod xxxxx的结果”了吧,那是为了避免高精度运算,因为这里的结论告诉我们在运算过程中边算边mod和算完后再mod的结果一样。假如a是一个很大的数, 令b=a mod m,那么(a * 100) mod m和(b * 100) mod m的结果是完全一样的,这相当于是在a≡b (mod m)的两边同时乘以100。这些结论其实都很显然,因为同余运算只关心余数(不关心“整的部分”),完全可以每一次运算后都只保留余数。因此,整个运算过 程中参与运算的数都不超过m,避免了高精度的出现。 在证明**_Fermat小定理_**时,我们用到了这样一个定理: **如果ac≡bc(mod m),且c和m互质,则a≡b(mod m)** (就是说同余式两边可以同时除以一个和模数互质的数)。 证明:条件告诉我们,ac-mp = bc-mq,移项可得ac-bc = mp-mq,也就是说(a-b)c = m(p-q)。这表明,(a-b)c里需要含有因子m,但c和m互质,因此只有可能是a-b被m整除,也即a≡b(mod m)。

原文地址:http://www.matrix67.com/blog/archives/236

数据库事务概念

数据库事务必须同时满足 4 个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabiliy),简称为ACID。下面是对每个特性的说明。

  • 原子性:表示组成一个事务的多个数据库操作要么全部成功、要么全部失败。
  • 一致性:事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。如从A账户转账100元到B账户,不管操作成功与否,A和B的存款总额是不变的。
  • 隔离性:在并发数据操作时,不同的事务拥有各自的数据空间,它们的操作不会对对方产生干扰。准确地说,并非要求做到完全无干扰,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。
  • 持久性:一旦事务提交成功后,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证能够通过某种机制恢复数据。

其实这四个特性,原子性是最终目的。

数据并发的问题

一个数据库可能拥有多个访问客户端,这些客户端都可以并发方式访问数据库。数据库中的相同数据可能同时被多个事务访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。这些问题可以归结为5类,包括3类数据读问题( 脏读、 不可重复读和 幻象读)以及2类数据更新问题( 第一类丢失更新和 第二类丢失更新)。下面,我们分别通过实例讲解引发问题的场景。

脏读(dirty read)

A事务读取B事务尚未提交的更改数据,并在这个数据的基础上操作。如果恰巧B事务回滚,那么A事务读到的数据根本是不被承认的。来看取款事务和转账事务并发时引发的脏读场景: 15150645_B8Se 在这个场景中,B希望取款500元而后又撤销了动作,而A往相同的账户中转账100元,就因为A事务读取了B事务尚未提交的数据,因而造成账户白白丢失了500元。在Oracle数据库中,不会发生脏读的情况。

不可重复读(unrepeatable read)

不可重复读是指 A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程中,B往该账户转账100元,A两次读取账户的余额发生不一致: 15150645_B8Se 在同一事务中,T4时间点和T7时间点读取账户存款余额不一样。

幻象读(phantom read)

A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题。幻象读一般发生在计算统计数据的事务中,举一个例子,假设银行系统在同一个事务中,两次统计存款账户的总金额,在两次统计过程中,刚好新增了一个存款账户,并存入100元,这时,两次统计的总金额将不一致: 15150645_d6yw 如果新增数据刚好满足事务的查询条件,这个新数据就进入了事务的视野,因而产生了两个统计不一致的情况。 幻象读和不可重复读是两个容易混淆的概念,前者是指读到了其他已经提交事务的新增数据,而后者是指读到了已经提交事务的更改数据(更改或删除),为了避免这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据(Oracle使用多版本数据的方式实现)。

第一类丢失更新

A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来: 15150645_d6yw A事务在撤销时,“不小心”将B事务已经转入账户的金额给抹去了。

第二类丢失更新

A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失: 15150645_d6yw 上面的例子里由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户账户将损失100元。

四种隔离级别

尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提高系统的运行性能,而这一过程对用户来说完全是透明的。 ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别: 15150645_d6yw 事务的隔离级别和数据库并发性是对立的,两者此增彼长。一般来说,使用READ UNCOMMITED隔离级别的数据库拥有最高的并发性和吞吐量,而使用SERIALIZABLE隔离级别的数据库并发性最低。 Mysql的默认隔离级别时Repeatable Read,即可重复读。

我们知道当插入多条数据的时候insert支持多条语句:

INSERT INTO t_member (id, name, email) VALUES
(1, ‘nick’, ‘nick@126.com’),
(4, ‘angel’,‘angel@163.com’),
(7, ‘brank’,‘ba198@126.com’);

但是对于更新记录,由于update语法不支持一次更新多条记录,只能一条一条执行:

UPDATE t_member SET name=’nick’, email=’nick@126.com’ WHERE id=1;
UPDATE t_member SET name=’angel’, email=’angel@163.com’ WHERE id=4;
UPDATE t_member SET name=’brank’, email=’ba198@126.com’ WHERE id=7;

这里问题就出现了,倘若这个update list非常大时(譬如说5000条),这个执行率可想而知。 这就要介绍一下在MySql中INSERT语法具有一个条件DUPLICATE KEY UPDATE,这个语法和适合用在需要判断记录是否存在,不存在则插入存在则更新的记录。 具体的语法可以参见:http://dev.mysql.com/doc/refman/5.0/en/insert.html 基于上面这种情况,针对更新记录,仍然使用insert语句,不过限制主键重复时,更新字段。如下:

INSERT INTO t_member (id, name, email) VALUES
(1, ‘nick’, ‘nick@126.com’),
(4, ‘angel’,‘angel@163.com’),
(7, ‘brank’,‘ba198@126.com’)
ON DUPLICATE KEY UPDATE name=VALUES(name), email=VALUES(email);

注意:ON DUPLICATE KEY UPDATE只是MySQL的特有语法,并不是SQL标准语法! 原文地址:http://www.crackedzone.com/mysql-muti-sql-not-sugguest-update.html

1.创建分支的意义 创 建分支的意义,比如我们在一个基础平台上进行开发,每个技术小组负责一个子项目,而基础平台也是有可能会继续更改的,这个时候,如果不创建分支,子项目之 间会相互影响,影响最大的就是后期的测试和版本发布,子项目A已经结束,但测试却受到正在进行的子项目B的影响,测试通不过,就别说版本发布了。所以,我 们需要从目前的项目(主干trunk)中创建分支(branch),隔离子项目间的相互影响。 2.svn创建分支原理 在 svn中,创建分支,实际上就是一个版本拷贝(对应copy to…注意:绝不是简单在客户端上copy一个目录,而是svn仓库中copy,文件版本号会增加。),两边做任何修改发生的版本变化,是一套机制。 举例:目前主干版本是100,分支版本是101,主干中增加一个文件,版本为102,分支中再增加一个文件,版本就为103了。两边的版本号是一套,不会 重复。 3.svn创建分支的方法 TortoiseSVN:右键点击工程目录->TortoiseSVN->Branch/tag..菜单,From WC at Url自动为工程svn url,比如https://localhost:8443/svn/fbysss/prj1/trunk,to Url填写https://localhost:8443/svn/fbysss/prj1/branches/branch1。点OK按钮,分支就创建好了。 Subclipse:Team->Branch/tag..,跟上面类似. SVN命令模式:svn copy trunk_path branch_path -m ‘描述’ 举例:svn copy https://localhost:8443/svn/fbysss/prj1/trunk https://localhost:8443/svn/fbysss/prj1/branches/branch1 -m “第一个分支” 注意一点:trunk和branch不能互为子目录,否则就乱套了。 4.分支合并 1)从分支合并到主干 分支开发结束之后,往往需要合并回主干去测试、发布,但分支和主干可能有很多冲突的地方,在合并时经常需要手工解决。 被操作对象:主干 From**主干的打出分支时的版本 **To:分支的Head版本(最新版本) 怎么理解这个From和To呢?似乎跟我们的想当然不太一样:因为我们理解,把分支合并到主干,肯定是From分支,To主干。怎么搞反了呢? 实际上,Svn**认为,我们要合并的,是从主干的某个版本开始,到分支的某个版本结束。两边的版本号实际上是一套系统,不会有重复。**我们从TortoiseSVN Help中也能找到证据:

If you are using this method to merge a feature branch back to trunk, you need to ……..
In the From: field enter the full folder URL of the trunk. This may sound wrong, but remember that the trunk is the start point to which you want to add the branch changes. You may also click … to browse the repository.
In the To: field enter the full folder URL of the feature branch.

2)从主干合并到分支 试想这样的情况:一个项目里面,要独立出来一个子项目,需要单独发布版本,用到了基础框架代码,而基础框架在主干中不断修改完善,这就需要从主干合并到分支。 被操作对象:分支 From:分支的第一个版本(最旧版本) To:主干的Head版本(最新版本) 相当于从分支的第一个版本开始一直到主干最后一个版本结束合并之后,替换分支。 3)从分支合并到分支 有 这样的需求:一个项目中有很多分支,这些分支需要分期上线,有多个工作并行,但每一期之间不能相互影响,这就可以打出几个tag(也是分支),从主干 copy而来。其他主干根据排期分别合并到这些tag中来。比如有prjTag1和prjTag2,model1、model2需要合并到prjTag1 中,model3、model4需要合并到prjTag2中。拿prjTag1举例: 在prjTag1的work copy中,merge From**主干的打出分支时的版本 **To:分支的Head版本(最新版本) 注意:From不是本Tag的某个版本,而是之前主干打出分支时的版本,最终Merge到prjTag1的work copy,而prjTag1是找不到当初打分支时的版本的。 原文地址:http://blog.csdn.net/fbysss/article/details/5437157

前言

随着2012年苹果发布第一款Retina Macbook Pro(以下简称RMBP),Retina屏幕开始进入笔记本行业。两年过去了,RMBP的市场占有率越来越高,且获得了一大批设计师朋友的青睐,网站对于Retina屏幕的适配越来越重要。 如果大家对于Retina适配的重要性不是特别清楚,请看我的两个截图: QQ20140827-1@2x 上图是Google的首页LOGO,我们对比下图SOSO的LOGO: QQ20140827-2@2x 如果大家还是看不出来,请自行访问这两个网站或者下载附件的截图对比。 那么说完了重要性,适配Retina的原理又是什么呢?我们知道,当一个图像在标准设备下全屏显示时,一位图像素对应的就是一设备像素,导致一个完全保 真的显示,因为一个位置像素不能进一步分裂。而当在Retina屏幕下时,他要放大四倍来保持相同的物理像素的大小,这样就会丢失很多细节,造成失真的情 形。换句话说,每一位图像素被乘以四填补相同的物理表面在视网膜屏幕下显示。(摘自《走向视网膜(Retina)的Web时代》) 那么,解决方法相信大家也都听过,就是通过手动制图或以编程的方式制作两种不同的图形,一张是普通屏幕的图片,另外一种是Retina屏幕的图形,而且Retina屏幕下的图片是普通屏幕的两倍像素。 原理虽然简单,在现实中要实现就不仅仅如此,需综合考虑加载速度,浏览器适配等多方面因素,本文就是教大家如何对Retina的屏幕进行适配。

正文

1.直接加载2倍大小的图片。

假如要显示的图片大小为200px*300px,你准备的实际图片大小应该为400px*600px,并且使用以下代码控制即可:

这种方法就解决了Retina显示不清楚的问题,但是在普通屏幕下,这种图片要经过浏览器的压缩,在IE6和IE7上有十分差得显示效果,同时,两倍大小的图片势必会导致页面加载时间加长,用户体验下降,此时,我们可以通过Retina.js(http://retinajs.com/)文件解决:

<img class="pic" src="pic.png" height="200px" width="300px"/>
<script type="text/javascript">
$(document).ready(function () {
if (window.devicePixelRatio > 1) {
var images = $("img.pic");
images.each(function(i) {
var x1 = $(this).attr('src');
var x2 = x1.replace(/(.*)(.w+)/, "$1@2x$2");
$(this).attr('src', x2);
});
}
});
</script>

2.Image-set控制

假如要显示的图片大小为200px*300px,你准备的图片应有两张:一张大小为200px*300px,命名为pic.png;另一张大小为 400px*600px,命名为pic@2x.png(@2x是Retina图标的标准命名方式),然后使用以下css代码控制:

#logo {
background: url(pic.png) 0 0 no-repeat;
background-image: -webkit-image-set(url(pic.png) 1x, url(pic@2x.png) 2x);
background-image: -moz-image-set(url(pic.png) 1x,url(images/pic@2x.png) 2x);
background-image: -ms-image-set(url(pic.png) 1x,url(images/pic@2x.png) 2x);
background-image: -o-image-set(url(url(pic.png) 1x,url(images/pic@2x.png) 2x);
}

或者使用HTML代码控制亦可:

3.使用@media控制

实际是判断屏幕的像素比来取舍是否显示高分辨率图像,代码如下:

@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
       only screen and (min--moz-device-pixel-ratio: 1.5), /* 注意这里的写法比较特殊 */
       only screen and (-o-min-device-pixel-ratio: 3/2),
       only screen and (min-device-pixel-ratio: 1.5) {
#logo {
background-image: url(pic@2x.png);
background-size: 100px auto;
}
}

使用这个的确定就是IE6、7、8不支持@media,所以无效。但是如果你只是支持苹果的RMBP的话,不存在兼容问题,因为MacOS X上压根没有IE!哈哈哈! OK,本文到这里就结束了,介绍了上面的三个办法大家可以各有取舍的使用吧~ 附件:附件 原文地址:http://www.ui.cn/project.php?id=24556

Linux系统中的wget是一个下载文件的工具,它用在命令行下。对于Linux用户是必不可少的工具,我们经常要下载一些软件或从远程服务器恢复备份到本地服务器。wget支持HTTP,HTTPS和FTP协议,可以使用HTTP代理。所谓的自动下载是指,wget可以在用户退出系统的之后在后台执行。这意味这你可以登录系统,启动一个wget下载任务,然后退出系统,wget将在后台执行直到任务完成,相对于其它大部分浏览器在下载大量数据时需要用户一直的参与,这省去了极大的麻烦。 wget 可 以跟踪HTML页面上的链接依次下载来创建远程服务器的本地版本,完全重建原始站点的目录结构。这又常被称作”递归下载”。在递归下载的时 候,wget 遵循Robot Exclusion标准(/robots.txt). wget可以在下载的同时,将链接转换成指向本地文件,以方便离线 浏览。 wget 非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性.如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。这对从那些限定了链接时间的服务器上下载大文件非常有用。 1.命令格式: wget [参数] [URL地址] 2.命令功能: 用于从网络上下载资源,没有指定目录,下载资源回默认为当前目录。wget虽然功能强大,但是使用起来还是比较简单: 1)支持断点下传功能;这一点,也是网络蚂蚁和FlashGet当年最大的卖点,现在,Wget也可以使用此功能,那些网络不是太好的用户可以放心了; 2)同时支持FTP和HTTP下载方式;尽管现在大部分软件可以使用HTTP方式下载,但是,有些时候,仍然需要使用FTP方式下载软件; 3)支持代理服务器;对安全强度很高的系统而言,一般不会将自己的系统直接暴露在互联网上,所以,支持代理是下载软件必须有的功能; 4)设置方便简单;可能,习惯图形界面的用户已经不是太习惯命令行了,但是,命令行在设置上其实有更多的优点,最少,鼠标可以少点很多次,也不要担心是否错点鼠标; 5)程序小,完全免费;程序小可以考虑不计,因为现在的硬盘实在太大了;完全免费就不得不考虑了,即使网络上有很多所谓的免费软件,但是,这些软件的广告却不是我们喜欢的。 3.命令参数: 启动参数: -V, –version 显示wget的版本后退出 -h, –help 打印语法帮助 -b, –background 启动后转入后台执行 -e, –execute=COMMAND 执行.wgetrc’格式的命令,wgetrc格式参见/etc/wgetrc或~/.wgetrc 记录和输入文件参数: -o, –output-file=FILE 把记录写到FILE文件中 -a, –append-output=FILE 把记录追加到FILE文件中 -d, –debug 打印调试输出 -q, –quiet 安静模式(没有输出) -v, –verbose 冗长模式(这是缺省设置) -nv, –non-verbose 关掉冗长模式,但不是安静模式 -i, –input-file=FILE 下载在FILE文件中出现的URLs -F, –force-html 把输入文件当作HTML格式文件对待 -B, –base=URL 将URL作为在-F -i参数指定的文件中出现的相对链接的前缀 –sslcertfile=FILE 可选客户端证书 –sslcertkey=KEYFILE 可选客户端证书的KEYFILE –egd-file=FILE 指定EGD socket的文件名 下载参数: –bind-address=ADDRESS 指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用) -t, –tries=NUMBER 设定最大尝试链接次数(0 表示无限制). -O –output-document=FILE 把文档写到FILE文件中 -nc, –no-clobber 不要覆盖存在的文件或使用.#前缀 -c, –continue 接着下载没下载完的文件 –progress=TYPE 设定进程条标记 -N, –timestamping 不要重新下载文件除非比本地文件新 -S, –server-response 打印服务器的回应 –spider 不下载任何东西 -T, –timeout=SECONDS 设定响应超时的秒数 -w, –wait=SECONDS 两次尝试之间间隔SECONDS秒 –waitretry=SECONDS 在重新链接之间等待1…SECONDS秒 –random-wait 在下载之间等待0…2*WAIT秒 -Y, –proxy=on/off 打开或关闭代理 -Q, –quota=NUMBER 设置下载的容量限制 –limit-rate=RATE 限定下载输率 目录参数: -nd –no-directories 不创建目录 -x, –force-directories 强制创建目录 -nH, –no-host-directories 不创建主机目录 -P, –directory-prefix=PREFIX 将文件保存到目录 PREFIX/… –cut-dirs=NUMBER 忽略 NUMBER层远程目录 HTTP 选项参数: –http-user=USER 设定HTTP用户名为 USER. –http-passwd=PASS 设定http密码为 PASS -C, –cache=on/off 允许/不允许服务器端的数据缓存 (一般情况下允许) -E, –html-extension 将所有text/html文档以.html扩展名保存 –ignore-length 忽略 \Content-Length’头域 –header=STRING 在headers中插入字符串 STRING –proxy-user=USER 设定代理的用户名为 USER –proxy-passwd=PASS 设定代理的密码为 PASS –referer=URL 在HTTP请求中包含 `Referer: URL’头 -s, –save-headers 保存HTTP头到文件 -U, –user-agent=AGENT 设定代理的名称为 AGENT而不是 Wget/VERSION –no-http-keep-alive 关闭 HTTP活动链接 (永远链接) –cookies=off 不使用 cookies –load-cookies=FILE 在开始会话前从文件 FILE中加载cookie –save-cookies=FILE 在会话结束后将 cookies保存到 FILE文件中 FTP 选项参数: -nr, –dont-remove-listing 不移走 .listing’文件 -g, –glob=on/off 打开或关闭文件名的 globbing机制 –passive-ftp 使用被动传输模式 (缺省值). –active-ftp 使用主动传输模式 –retr-symlinks 在递归的时候,将链接指向文件(而不是目录) 递归下载参数: -r, –recursive 递归下载--慎用! -l, –level=NUMBER 最大递归深度 (inf 或 0 代表无穷) –delete-after 在现在完毕后局部删除文件 -k, –convert-links 转换非相对链接为相对链接 -K, –backup-converted 在转换文件X之前,将之备份为 X.orig -m, –mirror 等价于 -r -N -l inf -nr -p, –page-requisites 下载显示HTML文件的所有图片 递归下载中的包含和不包含(accept/reject): -A, –accept=LIST 分号分隔的被接受扩展名的列表 -R, –reject=LIST 分号分隔的不被接受的扩展名的列表 -D, –domains=LIST 分号分隔的被接受域的列表 –exclude-domains=LIST 分号分隔的不被接受的域的列表 –follow-ftp 跟踪HTML文档中的FTP链接 –follow-tags=LIST 分号分隔的被跟踪的HTML标签的列表 -G, –ignore-tags=LIST 分号分隔的被忽略的HTML标签的列表 -H, –span-hosts 当递归时转到外部主机 -L, –relative 仅仅跟踪相对链接 -I, –include-directories=LIST 允许目录的列表 -X, –exclude-directories=LIST 不被包含目录的列表 -np, –no-parent 不要追溯到父目录 wget -S –spider url 不下载只显示过程 4.使用实例: 实例1:使用wget下载单个文件 命令: wget http://www.minjieren.com/wordpress-3.1-zh\_CN.zip 说明: 以下的例子是从网络下载一个文件并保存在当前目录,在下载的过程中会显示进度条,包含(下载完成百分比,已经下载的字节,当前下载速度,剩余下载时间)。 实例2:使用wget -O下载并以不同的文件名保存 命令: : wget -O wordpress.zip http://www.minjieren.com/download.aspx?id=1080 说明: wget默认会以最后一个符合”/”的后面的字符来命令,对于动态链接的下载通常文件名会不正确。 错误:下面的例子会下载一个文件并以名称download.aspx?id=1080保存 wget http://www.minjieren.com/download?id=1 即使下载的文件是zip格式,它仍然以download.php?id=1080命令。 正确:为了解决这个问题,我们可以使用参数-O来指定一个文件名: wget -O wordpress.zip http://www.minjieren.com/download.aspx?id=1080 实例3:使用wget –limit -rate限速下载 命令: wget --limit-rate=300k http://www.minjieren.com/wordpress-3.1-zh\_CN.zip 说明: 当你执行wget的时候,它默认会占用全部可能的宽带下载。但是当你准备下载一个大文件,而你还需要下载其它文件时就有必要限速了。 实例4:使用wget -c断点续传 命令: wget -c http://www.minjieren.com/wordpress-3.1-zh\_CN.zip 说明: 使用wget -c重新启动下载中断的文件,对于我们下载大文件时突然由于网络等原因中断非常有帮助,我们可以继续接着下载而不是重新下载一个文件。需要继续中断的下载时可以使用-c参数。 实例5:使用wget -b后台下载 命令: wget -b http://www.minjieren.com/wordpress-3.1-zh\_CN.zip 说明: 对于下载非常大的文件的时候,我们可以使用参数-b进行后台下载。 wget -b http://www.minjieren.com/wordpress-3.1-zh\_CN.zip Continuing in background, pid 1840. Output will be written towget-log’. 你可以使用以下命令来察看下载进度: tail -f wget-log 实例6:伪装代理名称下载 命令: wget –user-agent=”Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16” http://www.minjieren.com/wordpress-3.1-zh\_CN.zip 说明: 有些网站能通过根据判断代理名称不是浏览器而拒绝你的下载请求。不过你可以通过–user-agent参数伪装。 实例7:使用wget –spider测试下载链接 命令: wget –spider URL 说明: 当你打算进行定时下载,你应该在预定时间测试下载链接是否有效。我们可以增加–spider参数进行检查。 wget –spider URL 如果下载链接正确,将会显示 wget –spider URL Spider mode enabled. Check if remote file exists. HTTP request sent, awaiting response… 200 OK Length: unspecified [text/html] Remote file exists and could contain further links, but recursion is disabled – not retrieving. 这保证了下载能在预定的时间进行,但当你给错了一个链接,将会显示如下错误 wget –spider url Spider mode enabled. Check if remote file exists. HTTP request sent, awaiting response… 404 Not Found Remote file does not exist – broken link!!! 你可以在以下几种情况下使用spider参数: 定时下载之前进行检查 间隔检测网站是否可用 检查网站页面的死链接 实例8:使用wget –tries增加重试次数 命令: wget –tries=40 URL 说明: 如果网络有问题或下载一个大文件也有可能失败。wget默认重试20次连接下载文件。如果需要,你可以使用–tries增加重试次数。 实例9:使用wget -i下载多个文件 命令: wget -i filelist.txt 说明: 首先,保存一份下载链接文件 cat > filelist.txt url1 url2 url3 url4 接着使用这个文件和参数-i下载 实例10:使用wget –mirror镜像网站 命令: wget –mirror -p –convert-links -P ./LOCAL URL 说明: 下载整个网站到本地。 –miror:开户镜像下载 -p:下载所有为了html页面显示正常的文件 –convert-links:下载后,转换成本地的链接 -P ./LOCAL:保存所有文件和目录到本地指定目录 实例11:使用wget –reject过滤指定格式下载 命令: wget –reject=gif ur 说明: 下载一个网站,但你不希望下载图片,可以使用以下命令。 实例12:使用wget -o把下载信息存入日志文件 命令: wget -o download.log URL 说明: 不希望下载信息直接显示在终端而是在一个日志文件,可以使用 实例13:使用wget -Q限制总下载文件大小 命令: wget -Q5m -i filelist.txt 说明: 当你想要下载的文件超过5M而退出下载,你可以使用。注意:这个参数对单个文件下载不起作用,只能递归下载时才有效。 实例14:使用wget -r -A下载指定格式文件 命令: wget -r -A.pdf url 说明: 可以在以下情况使用该功能: 下载一个网站的所有图片 下载一个网站的所有视频 下载一个网站的所有PDF文件 实例15:使用wget FTP下载 命令: wget ftp-url wget –ftp-user=USERNAME –ftp-password=PASSWORD url 说明: 可以使用wget来完成ftp链接的下载。 使用wget匿名ftp下载: wget ftp-url 使用wget用户名和密码认证的ftp下载 wget –ftp-user=USERNAME –ftp-password=PASSWORD url 备注:编译安装 使用如下命令编译安装: # tar zxvf wget-1.9.1.tar.gz # cd wget-1.9.1 # ./configure # make # make install

1392117921664 今天终于能把个人博客上的文章及文中图片一起同步到新浪微博了,在此发文纪念一下,并带图同步到我的微博测试。并且与大家一起分享一下经验,想必也有很多朋友在同步到微博的时候,遇到了传图的问题。 此前我都是使用多说评论框的接口同步到微博的,但为了能拥有自己的微博来源小尾巴,就自己开发一个。 首先你当然得有微博开放平台的帐号,并申请通过了网站应用审核。其次就是调用API接口了。 新浪微博有个高级接口,能发布一条微博并指定上传图片的url,这个接口由于需要申请使用,比较麻烦,还不如自己用另一个接口来实现锻炼一下自己。这里我使用的是https://upload.api.weibo.com/2/statuses/upload.json 这个接口(详情见这

必选

类型及范围

说明

source

false

string

采用OAuth授权方式不需要此参数,其他授权方式为必填参数,数值为应用的AppKey。

access_token

false

string

采用OAuth授权方式为必填参数,其他授权方式不需要此参数,OAuth授权后获得。

status

true

string

要发布的微博文本内容,必须做URLencode,内容不超过140个汉字。

visible

false

int

微博的可见性,0:所有人能看,1:仅自己可见,2:密友可见,3:指定分组可见,默认为0。

list_id

false

string

微博的保护投递指定分组ID,只有当visible参数为3时生效且必选。

pic

true

binary

要上传的图片,仅支持JPEG、GIF、PNG格式,图片大小小于5M。

lat

false

float

纬度,有效范围:-90.0到+90.0,+表示北纬,默认为0.0。

long

false

float

经度,有效范围:-180.0到+180.0,+表示东经,默认为0.0。

annotations

false

string

元数据,主要是为了方便第三方应用记录一些适合于自己使用的信息,每条微博可以包含一个或者多个元数据,必须以json字串的形式提交,字串长度不超过512个字符,具体内容可以自定。

rip

false

string

开发者上报的操作用户真实IP,形如:211.156.0.1。

文档规定的参数有以上这些,我只用到了source、status和pic三个参数就行了,当然你可以获得access_token来代替source(本文使用source讲解)。 上传图片等流媒体文件需要使用multipart/form-data编码方式,这和我们平常使用的表单上传文件类似。提交时会向服务器端发出这样的数据(如下代码,已经去除部分不相关的头信息)。

POST / HTTP/1.1
Content-Type:application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: weibo.com
Content-Length: 21
Connection: Keep-Alive
Cache-Control: no-cache
txt1=hello&txt2=world

对于普通的HTML Form POST请求,它会在头信息里使用Content-Length注明内容长度。头信息每行一条,空行之后便是Body,即“内容”(entity)。它的Content-Type是application/x-www-form-urlencoded,这意味着消息内容会经过URL编码,就像在GET请 求时URL里的QueryString那样。txt1=hello&txt2=world 最早的HTTP POST是不支持文件上传的,给编程开发带来很多问题。但是在1995年,ietf出台了rfc1867,也就是《RFC 1867 -Form-based File Upload in HTML》,用以支持文件上传。所以Content-Type的类型扩充了multipart/form-data用以支持向服务器发送二进制数据。因此发送post请求时候,表单 属性enctype共有二个值可选,这个属性管理的是表单的MIME编码: ①application/x-www-form-urlencoded(默认值) ②multipart/form-data 其实form表单在你不写enctype属性时,也默认为其添加了enctype属性值,默认值是enctype=”application/x- www-form-urlencoded”. 通过form表单提交文件操作如下:

浏览器会发送以下数据:

POST /t2/upload.do HTTP/1.1
User-Agent: SOHUWapRebot
Accept-Language: zh-cn,zh;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Length: 60408
Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Host: weibo.com
–ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name=”desc”
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
–ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name=”pic”; filename=”photo.jpg”
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
[图片二进制数据]
–ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC–

先分析一下上面的内容,然后我们来根据这内容来构造发送的数据。 第7行指定发送编码是multipart/form-data,并且指定boundary值为“ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC”,这个值可以随机产生,不难理解,boundary就是来分隔数据的。并且每段分隔时boundary值前面需要加“–”,最后结尾处在boundary值的前后加”–”(看上面代码最后一行)。 每段boundary之内,先是数据描述,再是数据的主体。 QQ截图20140818215404 现在放上我构造的代码,自己琢磨一下就明白了:

function post_to_sina_weibo($post_ID) {
if( wp_is_post_revision($post_ID) ) return;
$get_post_info = get_post($post_ID);
//发布的文章内容,带所有标签格式
$get_post_centent = get_post($post_ID)->post_content;
//用正则表达式抠图
preg_match_all(‘/<img.?(?: |\\t|\\r|\\n)?src=[\‘“]?(.+?)[\‘“]?(?:(?: |\\t|\\r|\\n)+.?)?>/sim’, $get_post_centent, $strResult, PREG_PATTERN_ORDER);
if(count($strResult[1]) > 0)
$imgUrl = $strResult[1][0];//获得第一张图url地址
else
$imgUrl = null;
//去掉文章内的html编码的空格、换行、tab等符号
$get_post_centent = str_replace(“\t”, “ “, str_replace(“\n”, “ “, str_replace(“ ”, “ “, $get_post_centent)));
//获取文章标题
$get_post_title = get_post($post_ID)->post_title;
if ( $get_post_info->post_status == ‘publish’ && $_POST[‘original_post_status’] != ‘publish’ ) {
//使用wordpress内置request请求类,在wp_includes/wp_http.php里
$request = new WP_Http;
//微博文字,格式为“【文章标题】文章摘要132字,全文地址:XXXX”
$status = ‘【’ . strip_tags( $get_post_title ) . ‘】 ‘ . mb_strimwidth(strip_tags( apply_filters(‘the_content’, $get_post_centent)),0, 132,’…’) . ‘ 全文地址:’ . get_permalink($post_ID) ;
//如果没图片则请求这个api
$api_url = ‘https://api.weibo.com/2/statuses/update.json';
//body内容,source参数为你的appkey,详情见wordpress的WP_http类
$body = array( ‘status’ => $status, ‘source’=>’706960568’);
//请将xxxxxxxxx替换成你的,xxxxxxxxxx为“用户名:密码”的base64编码
$headers = array( ‘Authorization’ => ‘Basic ‘ . ‘xxxxxxxxxxxxxxxx’ );
//重要步骤,如果文章有图片则构造发送数据包
if($imgUrl!==null)
{
$body[‘pic’] = $imgUrl;
uksort($body, ‘strcmp’);
$str_b=uniqid(‘——————‘);
$str_m=’–’.$str_b;
$str_e=$str_m. ‘–’;
$tmpbody=’’;
//对参数遍历,进行构造内容,仔细阅读,你会发现和上面讲的类似
foreach($body as $k=>$v){
if($k==’pic’){
$img_c=file_get_contents($imgUrl);
$url_a=explode(‘?’, basename($imgUrl));
$img_n=$url_a[0];
$tmpbody.=$str_m.”\r\n”;
$tmpbody.=’Content-Disposition: form-data; name=”‘.$k.’”; filename=”‘.$img_n.’”‘.”\r\n”;
$tmpbody.=”Content-Type: image/unknown\r\n\r\n”;
$tmpbody.=$img_c.”\r\n”;
}else{
$tmpbody.=$str_m.”\r\n”;
$tmpbody.=’Content-Disposition: form-data; name=”‘.$k.”\“\r\n\r\n”;
$tmpbody.=$v.”\r\n”;
}
}
$tmpbody.=$str_e;
$body = $tmpbody;
//图片处理结束,使用uploade API
$api_url = ‘https://upload.api.weibo.com/2/statuses/upload.json';
//设置Content-Type编码为multipart/form-data
$headers[‘Content-Type’] = ‘multipart/form-data; boundary=’.$str_b;
}
//请求接口发送数据,详见wordpress自带的WP_http类
$result = $request->post( $api_url , array( ‘body’ => $body, ‘headers’ => $headers ) );
}
//最后将这个函数挂到pulish_post钩子上,发表文章后就会触发这个函数
add_action(‘publish_post’, ‘post_to_sina_weibo’, 0);

这段代码理论上来说你可以拿去直接用了,只需按注释上说的修改几个参数为自己的就行了。 这也是我研究琢磨出来的,有任何疑问可以一起探讨。