标签归档:原创

数据过万后wordpress优化过程记录

数据过万后wordpress优化过程记录

导读

当文章数到3万之后,网站巨慢无比,无法忍受(各种缓存插件都试过了)。为了改善这个情况,

服务器升级

  1. 立马买了rds。 然后感觉好了点。。
  2. 升级服务器配置改成2核4G内存
    但是直觉是错的。运行top命令看了下,发现CPU占比太高了,然后升级服务器配置,升级成2核4G内存。
    感觉还是没用啊。。用ab测试工具 ab -n 1000 -c 100 http://test.com/indexphp
    测试了下,QPS居然只有1.75。

替换apache为nginx

  1. 更换apache为nginx,nginx的并发能力比apache高不少。
    安装nginx和配置方法
    再测试
内容 apache nginx
CPU 100% 100%
负载 100 50
内存 耗光 几乎耗光
QPS 1.78 4.2

降请求,降IO

把图片等通过CDN加速,把请求转移走,可以使用阿里云的CDN也可以使用七牛的。
七牛免费,而且有现成的插件。

大概说下CDN的原理。

1. 你原网站的图片不动,该怎么还是怎么样。譬如我的站点是http://go2live.cn,访问图片是http://www.go2live.cn/wp-content/uploads/2016/09/warticle_26361472786988577-150x1501.jpg
2. 找个CDN,如七牛的。需要实名认证,并有10元余额才可以开通自定义域名的CDN。
我创建的加速域名是  http://img.go2live.cn
3. wordpress中更改图片链接的域名为img.go2live.cn
4. 当用户从img.go2live.cn去访问图片时,图片是不存在的,这时CDN会从源站,既http://go2live.cn去拉取资源再显示,下次其他用户再访问同样的资源就有图片了。

配置时间比较长,我先拿阿里云的CDN做个测试。

大概说下CNAME配置

1. 不管是七牛还是阿里云CDN,都会给你一个CNAME记录。
2. 同时记得你设置过一个加速域名。我的是img.go2live.cn
3. 在域名配置里加一个CNAME记录。主机记录,我的为img, 记录值,即为CNAME值。

再测试一下:效果不大

内容 apache nginx
CPU 100% 94%
负载 100 45
内存 耗光 余74M
QPS 1.78 4.08

参考
ab -n 1000 -c 100 http://www.go2live.cn/wp-content/uploads/2016/09/warticle_26361472786988577-150×1501.jpg
得出的静态图片QPS是11698

程序问题

从rds中找到如下slow sql

1. SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND ((wp_posts.post_status = 'publish')) ORDER BY wp_posts.post_date DESC
耗时32秒
2. SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (((wp_posts.post_title LIKE '%互联网产品技术热点一角-关注成长%') OR (wp_posts.post_excerpt LIKE '%互联网产品技术热点一角-关注成长%') OR (wp_posts.post_content LIKE '%互联网产品技术热点一角-关注成长%'))) AND wp_posts.post_type IN ('post', 'page', 'attachment') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_title LIKE '%互联网产品技术热点一角-关注成长%' DESC, wp_posts.post_date DESC LIMIT 0, 10
耗时17秒

这不只是sql 查询慢的问题,他把整个数据都查到内存里,也造成了内存基本耗光。

优化方法只能针对这些使用场景:

  1. 加缓存。
  2. 分批次读取,而不是一次性读取,减少内存的使用。
  3. 预加载,把目标数据放到cron里先跑出来。

有个插件【Import External Images】,我就是用方法2优化的,在csdn上有下载

推荐一个插件【Query Monitor】,可以用来查找程序的性能瓶颈。

加大并发数一定是加各种缓存

缓存插件

仔细检查了下,发现我的【WP Super Cache】并没有生效。弄好之后,重新测试QPS到了155

这个是磁盘缓存,换成memcache缓存肯定更好。参考这个.
不知道为啥,没有生效。

用上阿里的OCS看看,参考文章

看起来是生效了。再做下ab测试。

内容 apache nginx
CPU 100% 94%
负载 100 43
内存 耗光 余1G
QPS 1.78 4.03

原来memcache是开在本机的,现在转到OCS,内存是降了,但也看出了真正的瓶颈是CPU。这不得不说是代码问题,有太多的计算量了。
真的除了加各种缓存把动态的php变成静态的页面,没有更好的方法了。
从这里其实也看出来了, 【WP Super Cache】的对象缓存功能并没有生效。通过Debug和看代码,发现原因。

日志

Non empty GET request. Not serving request from object cache. {"url":"/archives/92943.html"}

代码wp-cache-phase2.php

elseif ( $wp_cache_object_cache && !empty( $_GET ) ) {
        wp_cache_debug( 'Not caching GET request while object cache storage enabled.', 5 );
        $cache_this_page = false;
    }

代码wp-cache-phase1.php

if ( !empty( $_GET ) ) { 
            wp_cache_debug( "Non empty GET request. Not serving request from object cache. " . json_encode( $_GET ), 1 );
            return false;
        }  

从日志里可以看到,总会有一个url的值。所以都会判断为Get请求。
把!empty((_GET) 改成 count()_GET)>1。

后记:后来发现是nginx的rewrite规则问题。其它人在这个地方应该不会有事。

第2个问题是, $meta 存的时候是

$serial = '<?php die(); ?>' . json_encode( $wp_cache_meta );

取的时候是

$meta = json_decode( wp_cache_get( $meta_filename, 'supercache' ), true );

结果导致meta无效,不启用缓存。

终于搞定。bug误人啊。。现在的QPS是128

nginx前端缓存

参考这个
这个肯定是最有效的,因为它会动态的页面变成了静态页面,前文静态图片的并发量有11698,他同时带来的问题是缓存失效之后怎么更新。(文章更新、有新的评论数据,原来的缓存就是有问题的,需要干掉,重新建立缓存)

总结

  1. wordpress运算量太大,特耗CPU,在升级服务器硬件时,加强CPU。
  2. 改由单独的mysql感觉作用不大,不过用阿里云的服务的好处是,他有备份,有性能优化,独立的服务器不会受网站影响。
  3. CDN的好处是降低了请求量,对并发的提升有一定的提升,更主要的是把网站服务器的带宽分离出去了。
  4. OCS同样是独立的memcache, 服务能更健壮,而且有命中率什么的数据图片,心里有底。同时可以降低数据的访问量,Wordpress本是为个人定的博客程序,数据库的设计并非为大量数据而设计。可以在slow query中看到好多慢查询。
  5. nginx确实比apache的并发数更高,而且可以弄反向代理,搞伪静态化。
  6. 插件【WP Super Cache】,是降低了PHP的运算量,但是对于我几万文章的博客站来说,感觉作用不大。开始了对象缓存,看到ocs的数据是增加了,但是测试的时候QPS没啥变化。还是静态页面有效,能测试到。

参考

这个讲的全

第四章 文件和目录

第四章 文件和目录

stat、fstat和lstat函数

#include <sys/stat.h>
int stat(const char *restrict pathname ,struct stat *restrict buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);

restrict是C99引入,用来告诉编译器,指针是唯一操作对象的方式。
如果pathname是符号链接,lstat返回的符号链接本身的有关信息,stat返回的是符号链接引用文件的信息。

struct stat {
mode<sub>t</sub> st<sub>mode</sub>;/\* file type & mode **/
ino<sub>t</sub> st<sub>ino</sub>; /** i-node number **/
dev<sub>t</sub> st<sub>dev</sub>; /** device number (file system) **/
dev<sub>t</sub> st<sub>rdev</sub>; /** device number for special files\*/
nlink<sub>t</sub> st<sub>nlink</sub>; *\* number of links \**
uid<sub>t</sub> st<sub>uid</sub>; *\* user ID of owner \**
gid<sub>t</sub> st<sub>gid</sub>; *\* group ID of owner \**
off<sub>t</sub> st<sub>size</sub>; *\* size in bytes, for regular files \**
time<sub>t</sub> st<sub>atime</sub>;/\* time of last access\*/
time<sub>t</sub> st<sub>mtime</sub>;/\* time of last modification **/
time<sub>t</sub> st<sub>ctime</sub>; /** time of last file status change **/
blksize<sub>t</sub> st<sub>blksize</sub>; /** best I/O block size **/
blkcnt<sub>t</sub> st<sub>blocks</sub>; /** number of disk blocks allocated \*/
}

文件类型

文件类型有:
1. 普通文件
2. 目录文件
3. 块特殊文件。这种文件类型提供对设备带缓冲的访问,每次访问以固定长度为单位进行。
4. 字符特殊文件。不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
5. FIFO。用于进程间通信,也叫命名管道。
6. 套接字,用于进程间网络通信,也可用于本机上进程间非网络通信。
7. 符号链接

设置用户ID和设置组ID

与每个进程相关的用户ID和组ID

项目 说明
实际用户ID 我们实际上是谁
实际组ID
———- ————————-
有效用户ID 用于文件访问权限检查
有效组ID
附加组ID
———- ——————————–
保存的设置用户ID 由exec函数保存
保存的设置组ID

一般来说,有效用户ID就是实际用户ID,有效组ID就是实际组ID。但在文件模式字(stmode)中有两个特殊标志。
1. 设置用户ID, 当设置时,有效用户ID设置为文件所有者用户ID
2. 设置组ID,当设置时,有效组ID设置为文件所有者组ID

这两个设置过程是由exec操作的。

文件访问权限

目录的执行权限位被称为搜索位。

对于目录的读权限和执行权限意义是不同的。读权限允许我们读目录,获得在该目录中所有文件名的列表。当一个目录是我们要访问文件的路径名的一个组成部分时,
对该目录的执行权限使我们可以通过该目录(也就是搜索该目录,寻找一个特定的文件名)

内核进行的文件访问权限测试:
1. 若进程的有效用户ID是0,则允许访问。
2. 若进程的有效用户ID等于文件所有者ID.那么,若所有者有权限,则允许。
3. 若进程的有效组ID或进程有附加组ID之一等于文件的组ID,则依赖于组ID的权限位。
4. 若其它用户适当的访问权限位被设置,则允许访问。

说明:2,3,4按顺序检查时,如进程拥有此文件,则只检查第2步,3,4步不检查。其它情况类似。

新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID。
关于组ID,有如下设定:
1. 新文件的组ID可以是进程的有效组ID。
2. 新文件的组ID可以是它所在目录的组ID。

不同系统选择不一样,mac是第2个。

access函数

access函数是按实际用户ID和实际组ID进行访问权限测试的。

#include
int access(const char *pathname, int mode);

其中,mode是按下表按位或。

mode 说明
ROK 测试读权限
WOK 测试写权限
XOK 测试执行权限
FOK 测试文件是否存在

umask函数

umask函数为进程设置文件模式创建屏蔽字,并返回以前的值。(少数几个没有出错返回函数中的一个)

#include <sys/stat.h>
modet umask(modet cmask);

对于任何在文件模式创建屏蔽字中为1的位,在文件mode中的相应位则一定被关闭。

在登录时,会由shell启动文件设置一次umask,然后就不变了。
在编写创建新文件的程序时,如果要确保指定的访问权限位已激活,那么必须在进程运行时修改umask值。
更改进程的文件模式创建屏蔽字并不影响父进程。

chmod和fchmod函数

#include <sys/stat.h>
int chmod(const char *pathname, modet mode);
int fchmod(int filedes, modet mode);

为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID, 或者该进程必须有超级用户权限。

chmod函数更新的只是i节点最近一次被更改的时间。

设置用户ID和设置组ID由于安全原因。chmod有特殊处理, 在下列两个情况下会自动清除两个权限位。
1. 在Solaris等系统上,如果设置普通文件的粘住位(SISVTX),而且又没有超级权限,那么mode中的粘住位将自动被关闭。
2. 新创建文件的组ID可能不是调用进程所属的组(可能是父目录的组ID)。

特别地,如果新文件的组ID不等于进程的有效组ID或者进程附加组ID中的一个,以及用户没有超级用户权限,那么设置组ID位将会自动被关闭。

粘住位

历史技术,在UNIX还没有分页技术之前使用的。当时是用于可执行文件。
在一般UNIX文件系统中,文件的各数据块很可能是随机存放的。为了加速程序的载入,会在第一次执行后,把程序的正文部分存放到交换区,文件在交换区是连续的,这就是称为粘住位的原因,
也叫保存正文位(SISVTX)。
现在由于配置有虚拟存储系统以及快速文件系统,所以不再需要这种技术。

现今的系统扩展了粘住位的使用范围, 针对目录。如果对一个目录设置了粘住位,则只有对该目录有写权限的用户在满足下列条件之一的情况下,才能删除或更名该目录下的文件:
1. 拥有该文件
2. 拥有该目录
3. 是超级用户

可以共享一个目录,每个人都能修改自己的文件,但只能看别人的文件。

chown、fchown和lchown函数

#include
int chown(const char *pathname, uidt owner, gidt group);
int fchown(int filedes, uidt owner, gidt group);
int lchown(const char *pathname, uidt owner, gidt group);

在符号链接的情况下,lchown更改符号链接本身的所有者,而不是该符号链接所指向的文件。

如若两个参数owner或group中的任意一个是-1, 则对应的ID不变。
基于BSD的系统一直规定只有超级用户才能更改一个文件的所有者。这样做的原因是防止用户改变其文件的所有者从而摆脱磁盘空间限额对他们的限制。
系统V则允许任一用户更改他们所拥有的文件的所有者。

POSIXCHOWNRESTRICTED对指定的文件起作用,则
1. 只有超级用户进程能更改该文件的用户ID。
2. 若满足下列条件,一个非超级用户进程就可以更改该文件的组ID:
1. 进程拥有此文件(其有效用户ID等于该文件的用户ID)。
2. 参数owner等于-1或文件的用户ID,并且参数group等于进程的有效组ID或进程的附加组ID之一。

如果这些函数由非超级用户进程调用,则在成功返回时,该文件的设置用户ID位和设置组ID位都会被清除。

文件长度

对于目录,文件长度通常是一个数(例如16或512)的倍数。
对于符号链接,文件长度是文件名中的实际字节数。

文件中的空洞

ls 看的是文件长度,du看的是文件所使用的磁盘空间总量。
正常的I/O操作读取整个文件长度,会把空洞读成0,如果使用实用程序(如cat)复制这种文件,那么所有空洞会被填满,填写为0。从而实际占用磁盘空间。

大文件,du报告的长度比ls的长,因为文件系统使用了若干块以存放指向实际数据块的各个指针。

文件截短

#include
int truncate(const char *pathname, offt length);
int ftruncate(int filedes, offt length);

这两个函数将把现有的文件长度截短为length字节。
如果文件以前的长度>length,则length以后的数据不能再访问,
如果以前的长度短于length,则其效果与系统有关。遵循XSI的系统会增加文件长度,产生空洞。

文件系统

文件系统分类的多种实现:
1. UFS, UNIX file system, 传统的基于BSD的UNIX文件系统。
2. PCFS, 读、写DOS格式化软盘的文件系统。
3. HSFS, 读CD的文件系统。

下面说的是UFS。
1个磁盘分成一个或多个分区,每个分区可以包含一个文件系统。
文件系统由自举块、超级块和若干个柱面组 组成。
每个柱面由 超级块副本、配置信息、i节点图、块位图、i节点和数据块组成。

i节点是固定长度的记录项,它包含有关文件的大部分信息:
文件类型、文件访问权限位、文件长度和指向该文件所占用的数据块的指针等等。

只有两项数据存放在目录项中:文件名和i节点编号。

每个文件系统各自对它们的i结点进行编号,因此目录项中的i节点编号数指向同一文件系统中的相应i节点,不能使一个目录项指向另一个文件系统的i节点。

link、unlink、remove和rename函数

#include
int link(const char *existingpath, const char *newpath);

创建新目录项newpath, 它引用现有的文件existingpath。
创建新目录项以及增加链接计数应当是个原子操作。
很多文件系统不允许对于目录的硬链接(可能形成循环)

删除目录项,可以调用unlink函数。

#include
int unlink(const char *pathname);

只有当链接计数达到0时,该文件的内容才可以被删除。另一个条件也会阻止删除文件的内容–只要有进程打开了该文件,其内容也不能删除。
关闭一个文件时,内核首先检查打开该文件的进程数。如果该数达到0,然后内核检查其链接数,如果这个数也是0,那么就删除该文件的内容。

上面的特性经常用来确保即使是在该程序崩溃时,它所创建的临时文件也不会遗留下来。
进程用open或creat创建一个文件,然后立即调用unlink。

remove函数解除对一个文件或目录的链接。对于文件,remove = unlink, 对于目录, remove = rmdir
#include
int remove(const char *pathname);

文件或目录用rename函数更名。
#include
int rename(const char *oldname, const char *newname);

  1. 如果oldname指的是一个文件而不是目录,那么为该文件/符号链接更名。
  2. 如若oldname指的是一个目录,则为该目录更名。
  3. 如若oldname或newname引用符号链接,则处理的是符号链接本身,而不是它所引用的文件。
  4. 作为一个特例,如果oldname=newname,函数直接返回。

如若newname已存在,则要求进程对其有写权限。另外,调用进程需要对包含oldname以及包含newname的目录具有写和执行权限。

符号链接

符号链接是指向一个文件的间接指针,它与硬链接不同,硬链接直接指向文件的i结点。引入符号链接的原因是为了避免硬链接的一些限制:
1. 硬链接通常要求链接和文件位于同一文件系统
2. 只有超级用户才能创建指向目录的硬链接

各个函数对符号链接的处理

函数 不跟随符号链接 跟随符号链接
access \*
chdir \*
chmod \*
chown \*
creat \*
lchown \*
link \*
lstat \*
open \*
opendir \*
pathconf \*
readlink \*
remove \*
rename \*
stat \*
truncate \*
unlink \*

有个特例,同时用OCREATE和OEXCL两者调用open函数。在某些情况下,若路径名引用符号链接,open将出错返回,并将errno设置为EEXIST。这种处理方式的意图是堵塞一个安全性漏洞,
使具有特权的进程不会被诱骗对不适当的文件进行写操作。

symlink和readlink函数

symlink创建符号链接

#include
int symlink(const char *actualpath, const chat *sympath);

因为open函数会跟随符号链接,所以需要一种方法打开链接本身,并读该链接中的名字。readlink提供此功能。

#include
ssizet readlink(const char * restrict pathname, char *restrict buf, sizet bufsize);
此函数组成了open、read、close。成功返回读入buf的字节数。。
**需要注意的是buf不以null字符终止**。

文件的时间

与每个文件相关的三个时间值

字段 说明 例子 ls选项
statime 文件数据的最后访问时间 read -u
stmtime 文件数据的最后修改时间 write 默认
stctime i节点状态的最后修改时间 chmod、chown -c

注意,系统并不保存对一个i节点的最后一次访问时间,所以access和stat函数并不更改这三个时间中的任一个。

utime函数

一个文件的访问和修改时间可用utime函数更改。

#include
int utime(const char *pathname, const struct utimbuf *times);

数据结构如下:
struct utimbuf {
timet actime;/* access time /
timet modtime;/
modification time */
}

此函数的操作以及执行它所要求的特权取决于times参数是否是NULL。
1. 如果times是一个空指针,则访问时间和修改时间两者都设置为当前时间。为了执行此操作必须满足下列两条件之一:进程的有效用户ID必须等于该文件的所有者ID;或者进程对该文件有写权限。
2. 如果times是非空指针,则访问时间和修改时间被设置为times所指向结构中的值。此时,进程的有效用户ID必须等于该文件的所有者ID,或者进程必须是一个超级用户进程。

mkdir和rmdir函数

#include <sys/stat.h>
int mkdir(const char *pathname, modet mode);

此函数创建一个新的空目录。其中,.和..目录项是自动创建的。所指定的文件访问权限mode由进程的文件模式创建屏蔽字修改。

对于目录通常至少要设置1个执行权限位,以允许访问该目录中的文件名.

用rmdir函数可以删除一个空目录。空目录是指只包含.和..这两项的目录。

#include
int rmdir(const char *pathname);

如果调用此函数使目录的链接计数成为0,并且也没有其他进程打开此目录,则释放由此目录占用的空间。
如果在链接计数达到0时,有一个或几个进程打开了此目录,则在此函数返回前删除最后一个链接及.和..项。
另外,在此目录中不能再创建新文件。但是在最后一个进程关闭它之前并不释放此目录。

读目录

为了防止文件系统产生混乱,只有内核才能写目录。一个目录的写和执行权限位,决定了在该目录下能否创建新文件和删除文件,它们并不表示能否写目录本身。

目录的实际格式依赖UNIX系统,特别是其文件系统的具体设计和实现。
很多实现阻止应用程序使用read函数读取目录的内容,从而进一步将应用程序与目录格式中与实现相关的细节隔离开。

#include
DIR *opendir(const char *pathname);
struct dirent *readdir(DIR *dp);

void rewinddir(DIR *dp);
int closedir(DIR *dp);

long telldir(DIR *dp);

void seekdir(DIR *dp, long loc);

最基础的定义:
struct dirent {
inot dino;
char dname[NAMEMAX + 1];
}

chdir、fchdir和getcwd函数

每个进程都有一个当前工作目录,此目录是搜索所有相对路径名的起点。当前工作目录是进程的一个属性,起始目录则是登录名的一个属性。

进程通过chdir或fchdir函数可以更改当前工作目录。
#include
int chdir(const char *pathname);
int lchdir(int filedes);

内核为每个进程只保存指向该目录v节点的指针等目录本身的信息,并不保存该目录的完整路径名。
通过getcwd可以取得完整路径名。
#include
char *getcwd(char *buf, sizet size);

设备特殊文件

stdev 与 strdev容易引起混淆。
1. 每个文件系统所在的存储设备都由其主、次设备号表示。主设备号标识设备驱动程序,有时编码为与其通信的外设板;次设备号标识特定的子设备。次设备号标识分区?
2. 可以用宏major和minor来访问主、次设备号。
3. 系统中与每个文件名关联的stdev值是文件系统的设备号,该文件系统包含了这一文件名以及与其对应的i节点。
4. 只有字符特殊文件和块特殊文件才有strdev值,此值包含实际设备的设备号。

php curl模拟post请求提交数据例子总结

导读

在php中要模拟post请求数据提交我们会使用到curl函数,下面我来给大家举几个curl模拟post请求提交数据例子,有需要的朋友可参考参考。
注意:curl函数在php中默认是不被支持的,如果需要使用curl函数我们需在改一改你的php.ini文件的设置,找到php_curl.dll去掉前面的”;”就行了

情景

  1. 简单post数据
<?php
$uri = "http://the_site_to_test/test.php";
// 参数数组
$data = array (
        'name' => 'youname' 
// 'password' => 'password'
);
 
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $uri );//地址
curl_setopt ( $ch, CURLOPT_POST, 1 );//请求方式为post
curl_setopt ( $ch, CURLOPT_HEADER, 0 );//不打印header信息
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );//返回结果转成字符串
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $data );//post传输的数据。
$return = curl_exec ( $ch );
curl_close ( $ch );
 
print_r($return);

接受php页面远程服务器:

<?php
if(isset($_POST['name'])){
    if(!empty($_POST['name'])){
        echo '您好,',$_POST['name'].'!';
    }
}
?>

2.用CURL模拟POST请求抓取邮编与地址,需要cookie做身份认证。

完整代码:

<?php
$runtime = new runtime ();
$runtime->start ();
$cookie_jar = tempnam('/tmp','cookie');
$filename = $argv[1];
$start_num= $argv[2];
$end_num  = $argv[3];

for($i=$start_num; $i<$end_num; $i++){
    $zip = sprintf('6s',$i);
    $fields_post = array(
            'postcode' => $zip, 
            'queryKind' => 2, 
            'reqCode' => 'gotoSearch', 
            'search_button.x'=>37,
            'search_button.y'=>12);


    $fields_string = http_build_query ( $fields_post, '&' );
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "URL?reqCode=gotoSearch&queryKind=2&postcode=".$zip);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120 );
    curl_setopt($ch, CURLOPT_REFERER, $refer );//有些网站会检查refer来屏蔽到程序的请求。所有需要模拟一个合法的。
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers_login );
    curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar );
    curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar );
    curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); 
    curl_setopt($ch, CURLOPT_POST, 1); // 发送一个常规的Post请求 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string );


    $data = curl_exec($ch);
    
    preg_match_all('/id="table1">[s]*?<tr>[s]*?<td class="maintext">[sS]*?</td>[s]*?</tr>/', $data, $matches);
    if (!$handle = fopen($filename, 'a+')) {
        echo "不能打开文件 $filename";
        echo "n";
        exit;
    }


    if (fwrite($handle, $matches[0][1]) === FALSE) {
        echo "不能写入到文件 $filename";
        echo "n";
        exit;
    }


    echo "成功地将 $somecontent 写入到文件$filename";
    echo "n";


    fclose($handle);
    curl_close($ch);
}




class runtime
{
    var $StartTime = 0;
    var $StopTime = 0;
    function get_microtime()
    {
        list($usec,$sec)=explode(' ',microtime());return((float)$usec+(float)$sec);
    }
    function start()
    {
        $this->StartTime=$this->get_microtime();
    }
    function stop(){
        $this->StopTime=$this->get_microtime();
    }
    function spent()
    {
        return ($this->StopTime-$this->StartTime);
    }
}




$runtime->stop ();


$con = 'Processed in'.$runtime->spent().'seconds';
echo 'Processed in'. $runtime->spent().'seconds';

3.模拟POST请求 提交数据或上传文件 .

代码如下

http://www.a.com/a.php 发送POST请求

function execUpload(){


$file = '/doucment/Readme.txt';
$ch = curl_init();
$post_data = array(
    'loginfield' => 'username',
    'username' => 'ybb',
    'password' => '123456',
'file' => '@d:usrwwwtranslatedocumentReadme.txt'
);
curl_setopt($ch, CURLOPT_HEADER, false);
//启用时会发送一个常规的POST请求,类型为:application/x-www-form-urlencoded,就像表单提交的一样。
curl_setopt($ch, CURLOPT_POST, true);  
curl_setopt($ch,CURLOPT_BINARYTRANSFER,true);
curl_setopt($ch, CURLOPT_POSTFIELDS,$post_data);
curl_setopt($ch, CURLOPT_URL, 'http://www.b.com/handleUpload.php');
$info= curl_exec($ch);
curl_close($ch);
   
print_r($info);

}

2.http://www.b.com/handleUpload.php

function handleUpload(){
    print_r($_POST);
    echo '===file upload info:';
    print_r($_FILES);
}

4. post json数据

$data = array("name" => "Hagrid", "age" => "36");
$data_string = json_encode($data);

$ch = curl_init('http://api.local/rest/users');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS,$data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Content-Length: ' . strlen($data_string))
);
 
$result = curl_exec($ch);

 

■cURL 函数

■curl_close — 关闭一个cURL会话
■curl_copy_handle — 复制一个cURL句柄和它的所有选项
■curl_errno — 返回最后一次的错误号
■curl_error — 返回一个保护当前会话最近一次错误的字符串
■curl_exec — 执行一个cURL会话
■curl_getinfo — 获取一个cURL连接资源句柄的信息
■curl_init — 初始化一个cURL会话
■curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄
■curl_multi_close — 关闭一组cURL句柄
■curl_multi_exec — 运行当前 cURL 句柄的子连接
■curl_multi_getcontent — 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流
■curl_multi_info_read — 获取当前解析的cURL的相关传输信息
■curl_multi_init — 返回一个新cURL批处理句柄
■curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源
■curl_multi_select — 等待所有cURL批处理中的活动连接
■curl_setopt_array — 为cURL传输会话批量设置选项
■curl_setopt — 设置一个cURL传输选项
■curl_version — 获取cURL版本信息

总结

结合这篇和通过php写wordpress博客就可以抓取别人的文章 然后自动发博客。当然这里面其实还少了一讲,正则表达式。不过 可以用php-readability,git上就有源码。

mac上建议安装Dash,看各种文档很方便。

第三章 文件I/O

第三章 文件I/O

引言

UNIX系统中的大多数文件I/O只需要用到5个函数:open、read、write、lseek以及close。

本章说明的函数经常被称为不带缓冲的I/O。术语 ****不带缓冲**** 指的是每个read和write都调用内核的一个系统调用。

多个进程共享文件 相关的函数:dup、fcntl、sync、fsync和ioctl

文件描述符

文件描述符的变化范围是0~OPENMAX。

open函数

#include
int open(const char pathname, int oflag, … / modet mode */);

ODSYNC和OSYNC标志有微妙的区别。仅当文件属性需要更新以反映文件数据变化(例如,更新文件大小以反映文件中包含了更多的数据)时,ODSYNC标志
才影响文件属性。而设置OSYNC标志后,数据和属性总是同步更新。当文件用ODSYNC标志打开,在重写其现有的部分内容时,文件时间属性不会同步更新。与此相反,如果文件是用OSYNC标志打开,那么对该文件的每一次
write操作都将在write返回前更新文件时间, 这与是否改写现有字节或增写文件无关。

由open返回的文件描述符一定是最小的未用描述符数值。

POSIXNNOTRUNC有效,则在整个路径名超过PATHMAX, 或路径名中任一文件名超过NAMEMAX时,返回出错状态,并将errno设置为ENAMETOOLONG。

create函数

#include
int create(const char *pathname, modet mode);
此函数等效于open(pathname, OWRONLY | OCREATE | OTRUNC, mode);

close函数

#include
int close(int filedes);

关闭一个文件时还会释放该进程加在该文件上的所有 ****记录锁****
当一个进程终止时,内核自动关闭它所有打开的文件。

lseek函数

#include
offt lseek(int filedes, offt offset, int whence);

可以用以下方式确认当前文件偏移量
offt currpos;
currpos = lseek(fd,0,SEEKCUR);

这种方法也可以用来确定所涉及的文件是否可以设置偏移量。如果文件描述符引用的是一个管道、FIFO或网络套接字,则lseek返回-1,并将errno设置为ESPIPE。

通常,文件的当前偏移量是一个非负整数,但是,某些设备也可能允许负的偏移量。但对于普通文件,则其偏移量必须是非负值 。因为偏移量可能是负值,所以在比较lseek的返回值时
应当谨慎,不要测试它是否小于0,而是要测试它是否等于-1。

lseek仅将当前文件的偏移量记录在内核中,它并不引起任何I/O操作。然后该偏移量用于下一个读/写操作。

文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写操作将加长该文件,并在文件中构成一个空洞。位于文件中但没有写过的字节都被 ****读为0****

文件中的空洞并不要求在磁盘上占用存储区。具体处理方式与文件系统的实现有关,当定位到超出文件尾端之后写时,对于新写的数据需要分配磁盘快,
但是对于原文件尾端和新开始写位置之间的部分则不需要分配磁盘块。

read函数

#include
ssizet read(int filedes, void *buf, sizet nbytes);

有多种情况可使实际读到的字节数少于要求读的字节数:
1. 读普通文件时,在读到的要求字节数之前已达到了文件尾端。
2. 从终端设备读时,通常一次最多读一行。
3. 当从网络读时,网络中的缓冲机制可能造成返回值小于所要求读的字节数。
4. 当从管道或从FIFO读时,如若管道包含的字节少于所需要的字节数,那么read将只返回实际可用的字节数。
5. 当从某些面向记录的设备(如磁带)读时,一次最多返回一个记录。
6. 当某一信号造成中断,而已经读了部分数据量时。

write函数

#include
ssizet write(int filedes, const void *buf, sizet nbytes);
返回值通常和nbytes相同,不相同则出错。
出错的原因可能是:
1. 磁盘满
2. 超过了一个给定进程的文件长度限制

I/O效率

BUFFSIZE的选取,和块大小sbblksize一致,最高效。
操作系统检测到顺序读时,会采取某种预读技术(read ahead)

文件共享

内核使用三种数据结构表示打开的文件:
1. 每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件描述符表,每个描述符占一项。与每个描述符相关联的是:
1. 文件描述符标志(closeonexit)。
2. 指向一个文件表项的指针。
2. 内核为所有打开文件维持一张文件表。每个文件表项包含:
1. 文件状态标志
2. 当前文件偏移量
3. 指向该文件v节点表项的指针
3. 每个打开文件都有一个v节点结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针。对于大多数文件,v节点还包含了该文件的i节点。
这些信息是在打开文件时从磁盘上读入内存的,所以所有关于文件的信息都是快速可供使用的。

如果两个独立进程各自打开了同一个文件。打开该文件的每个进程都得到一个文件表项,但对一个给定的文件只有一个v节点表项。
每个进程都有自己的文件表项的一个理由是:这种安排使每个进程都有它自己的对该文件的当前偏移量。

可能有多个文件描述符项指向同一个文件表项。譬如dup,fork。

文件描述符标志和文件状态标志在作用域方面的区别,前者只用于一个进程的一个文件描述符,而后者则适用于指向该文件表项的任何进程中的所有描述符。

当多个进程写同一个文件时,可能产生预期不到的效果。解决办法,参考下面的原子操作的概念。

原子操作

添写至一个文件

任何一个需要多个函数调用的操作都不可能是原子操作,因为在两个函数调用之间,内核可能会临时挂起该进程。
UNIX提供了OAPPEND标志,内核在写之前会将偏移量设置为文件尾端处,而不用调用lseek。

pread和pwrite函数

把lseek和I/O读写捆绑成了原子操作。由内核提供。

#include
ssizet pread(int filedes, void *buf, sizet nbytes, offt offset);
ssizet pwrite(int filedes, const void *buf, sizet nbytes, offt offset);

创建一个文件

open提供OCREATE和OEXCL选项。当同时指定这两个选项,而该文件又已经存在时,open将失败。
一般而言,原子操作指的是由多步组成的操作。如果该操作原子地执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤中的一个子集。
* 内核是怎么实现的?当执行其中一个时,发生信号中断呢?或者两个语句中,第一个语句执行时间过长导致CPU时间片用完呢?*

dup和dup2函数

这两个函数都可用来复制一个现存的文件描述符,返回的新文件描述符与参数fieldes共享同一个文件表项。

#include
int dup(int fieldes);
int dup2(int fieldes, int fieldes2);

由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。用dup2则可以用fieldes2参数指定新描述符的数值。如果filedes2已经打开,则先将其关闭。如若filedes等于filedes等于
filedes2,则dup2返回filedes2,而不关闭它。

复制一个描述符的另一种方法是fcntl函数。
调用
dup(filedes);
等效于
fcntl(filedes, FDUPFD, 0)

而调用
dup2(filedes, filedes2);
等效于
close(filedes);
fcntl(filedes, FDUPFD, filedes2);
第二个有区别,主要在于一个是原子操作,另一个不是。

sync、fsync和fdatasync函数

传统的UNIX实现在内核中设有缓冲区高速缓存或页面高速缓存,大多数磁盘I/O都通过缓冲进行。写->缓冲区->输出队列->队首时,实际的I/O操作。这种方式称为延迟写。
内核已经提供了缓冲机制,不过这个缓冲机制是为了减少频繁的I/O操作。
之后说的标准I/O函数库的缓冲是指对系统调用的数据做了缓冲,这个缓冲的目的是为了减少系统调用次数。系统调用由于涉及到内核态和用户态的切换,是有一定的开销的。

延迟写减少了磁盘读写次数,但是却降低了文件内容的更新速度。当系统发生故障时,这种延迟可能造成文件更新内容的丢失。
为了保证磁盘上实际文件系统与缓冲区高速缓存中内容的一致性,UNIX系统提供了sync、fsync和fdatasync三个函数。

#include
int fsync(int filedes);
int fdatasync(int filedes);

void sync(void);

sync函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。
通过称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数。这就保证了定期冲写内核的块缓冲区。

fsync会等待磁盘操作结束,适合数据库应用。

fcntl函数

  • Note taken on [2016-04-05 Tue 06:56]

#include
int fcntl(int filedes, int cmd, … * int arg *);

fcntl函数有5种功能:
1. 复制一个现有的描述符(cmd = FDUPFD)
2. 获得/设置文件描述符标记(cmd = FGETFD或FSETFD)
3. 获得/设置文件状态标志(cmd = FGETFL或FSETFL)
4. 获得/设置异步I/O所有权(cmd = FGETOWN或FSETOWN)
5. 获得/设置记录锁(cmd = FGETLK、FSETLK或FSETLW)

由磁盘驱动器将队列数据写到磁盘上。
在UNIX系统中,通常write只是将数据排入队列,而实际的写磁盘操作则可能在以后的某个时刻进行。
程序运行时,设置OSYNC标志会增加时钟时间(等待磁盘IO操作结束).

ioctl函数

ioctl函数是I/O操作的杂物箱。终端I/O是ioctl的最大使用方面。

#include * System V *
\#include <sys/ioctl.h> * BSD and linux *
\#include * XSI STREAMS *

int ioctl(int filedes, int request, … );

每个设备驱动程序都可以定义它自己专用的一组ioctl命令。系统则为不同种类的设备提供通用的ioctl命令。

/dev/fd

打开文件/dev/fd/n 等效于复制描述符n。

/dev/fd文件主要由shell使用,它允许使用路径名作为调用参数的程序,能用处理其它路径名的相同方式处理标准输入和输出。

filter file2 | cat file1 – file3 | lpr
在命令行中用”-” 作为一个参数,特指标准输入或标准输出,这已由很多程序采用。但是这会带来一些问题,例如若用”-“指定第一个文件名,
那么它看起来就像指定了命令行中的一个选项,/dev/fd则提高了文件名参数的一致性,也更加清晰。

第一章 UNIX基础知识

操作系统有两个含义:
1. 仅指内核,它控制计算机硬件资源,提供程序运行环境。
2. 广义上,包括内核和一些其它软件,如系统实用程序、应用软件、shell以及公用函数库等。

文件与目录

UNIX文件系统的大多数实现并不在目录项中存放属性。有inode就够。

程序的正确性是由硬件->内核->系统调用->标准库函数->第三方库->程序员 逐步保证的。

程序与进程

程序是存放在磁盘上,处于某个目录中的一个可执行文件。使用6个exec函数中的一个由内核将程序读入存储器,并使其执行。

有三个用于进程控制的主要函数:fork、exec和waitpid。

线程和线程ID

通常,一个进程只有一个控制线程,同一时刻只执行一组机器指令。
在一个进程内的所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。因为它们能访问同一存储区,所以各线程在访问共享数据时需要采取同步措施以避免不一致性。

出错处理

当UNIX函数出错时,一般返回一个负值,而且整型变量errno通常被设置为含有附加信息的一个值。
返回一个指向对象指针的大多数函数,在出错时,将返回一个null指针。

POSIX和ISO C将errno定义为这样一个符号,它扩展成为一个可修改的整型左值(lvalue)。这可以是包含出错编号的一个函数,或者是一个返回出错编号指针的函数。
extern int * _errnolocation(void);
#define errno (*_errnolocation())
在支持多线程的环境中,多个线程共享进程地址空间,每个线程都有属于它自己的局部errno以避免一个线程干扰另一个线程。
对于errno应当知道两条规则:
1. 如果没有出错,则其值不会被一个例程清除。因此,仅当函数的返回值指明出错时,才检验其值。
2. 任一函数都不会将errno值设为0,在中定义的所有常量都不为0。

用户标识

对于权限,使用数值用户ID和数值值ID是历史上形成的。1是数字比字符串的存储空间小,每个文件都会存放用户ID和组ID;2是查验权限期间,比较整数更快。

信号

信号是通知进程已发生某种情况的一种技术。

进程处理信息有三种选择:
1. 忽略信号。
2. 按系统默认方式处理。
3. 提供一个信号捕捉函数,自定义处理。

在键盘上产生信号的办法,中断键盘(Ctrl+C)和退出键(Ctrl+)。

时间值

长期以来,UNIX系统一直使用两种不同的时间值。
1. 日历时间。自1970年1月1日00:00:00以来UTC所经过的秒数。
2. 进程时间。也被称为CPU时间,用来度量进程使用的中央处理器资源。
1. 时钟时间
2. 用户CPU时间
3. 系统CPU时间。

通过php写wordpress博客

前言

vim,emacs什么的都有写wordpress的插件。
这篇就提供php版本的发博客的代码。

关键代码

require 'config.inc.php';
require 'common.inc.php';
require 'lib/Readability.inc.php';
require 'lib/class-IXR.php';
require 'lib/plugin.php';
require 'lib/xmlrpc.inc';

$xmlrpcurl='http://go2live.cn/xmlrpc.php';//请填入你自己的博客地址。

$blogid='1';
$username='xxxx';//请填入你的wordpress的用户名
$password='xxxxx';//请填入你的Wordpress的密码

$postTitle='博客标题';//要发布的博客的标题
$postContent='博客内容';//要发布的博客的内容

//$GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';
define ('DOMAIN', 'go2live.cn'); // 博客的域名,请填入你自己的博客的域名
// 创建 xml-rpc client 
$cl = new xmlrpc_client ( "/xmlrpc.php", DOMAIN, 80); 
// 准备请求 
$req = new xmlrpcmsg('metaWeblog.newPost'); 
// 逐个列出请求的参数: 
$req->addParam ( new xmlrpcval ( 1, 'int')); // 博客ID 
$req->addParam ( new xmlrpcval ( $username, 'string' )); // 用户名 
$req->addParam ( new xmlrpcval ( $password, 'string' )); // 密码 
$struct = new xmlrpcval (
    array ( "title" => new xmlrpcval ( $postTitle, 'string' ), // 标题 
    "description" => new xmlrpcval ($postContent , 'string'), // 内容
    "author"=> new xmlrpcval("bjmayor","string"),//作者,可不填
    "post_type"=>new xmlrpcval("post",'string'),//发布类型,是页面还是博客。post为博客
    "post_status"=>new xmlrpcval("publish",'string'),//发布状态,
    "dateCreated"=>new xmlrpcval(strtotime("2015-12-12 09:23:22"),"dateTime.iso8601"),//发布时间,可不填,默认为当前时间。
    "categories"=>new xmlrpcval(array(new xmlrpcval("美文赏析","string")),"array")//分类信息,分类信息是需要已经存在的分类。
),
"struct" );
$req->addParam ( $struct ); 
$req->addParam ( new xmlrpcval (1, 'int')); // 立即发布
// 发送请求 
$ans = $cl->send($req); 
var_dump ( $ans );

参考

MetaWeblog API中文说明
demo代码下载

Android中Activity和Intent之间的联系

A场景简单地跳转到B场景

无参数Activity跳转

Intent it = new Intent(Activity.Main.this, Activity2.class);
startActivity(it);   

A场景跳转到B场景,有数据关联。

譬如从列表页到详情页,需要携带数据到下一个场景。向下一个Activity传递数据(使用Bundle和Intent.putExtras)。

Intent it = new Intent(Activity.Main.this, Activity2.class);
Bundle bundle=new Bundle();
bundle.putString("name", "This is from MainActivity!");
it.putExtras(bundle);       // it.putExtra(“test”, "shuju”);
startActivity(it);            // startActivityForResult(it,REQUEST_CODE);

对于数据的获取可以采用:

Bundle bundle=getIntent().getExtras();
String name=bundle.getString("name");

A场景要求B场景做些事,并汇报结果。譬如从相册读取一张照片。

  1. A要求B做事
startActivityForResult(it,REQUEST_CODE);
  1. B做完事汇报结果
    向上一个Activity返回结果(使用setResult,针对startActivityForResult(it,REQUEST_CODE)启动的Activity)
Intent intent=getIntent();
Bundle bundle2=new Bundle();
bundle2.putString("name", "This is from ShowMsg!");
intent.putExtras(bundle2);
setResult(RESULT_OK, intent);

3.A根据结果做进一步处理。
回调上一个Activity的结果处理函数(onActivityResult)

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode==REQUEST_CODE){
            if(resultCode==RESULT_CANCELED)
                  setTitle("cancle");
            else if (resultCode==RESULT_OK) {
                 String temp=null;
                 Bundle bundle=data.getExtras();
                 if(bundle!=null)   temp=bundle.getString("name");
                 setTitle(temp);
            }
        }
    }

结论

Activity就一个场景。
intent提供不同场景之间的数据传递。

Android TextView 动态设置颜色

问题描述

最近写程序就遇到了这么个难题,在TextView 上,正常字体颜色显示是黑色。如果在activity中动态的改变字体颜色(预期目标是字体颜色变成红色),无论如何都是灰色,简单代码如下所示。郁闷了好些时日。

private TextView mTextDisp;
mTextDisp = (TextView) findViewById(R.id.textDisp_mian);
mTextDisp.setTextColor(R.color.red);//(使用color.xml文件中的颜色值)

这样写是怎么也变不成红色的,而且程序不报错,不知道朋友们有没有试过。而且debug所走的分支也是正确的。
我就单独写了一个Demo来测试,结果还是灰色。
有的朋友问,是不是red的颜色值写错了。不是,color中的颜色值配置对着呢。

解决

其实,答案很简单,就错在mTextDisp.setTextColor(R.color.red);这行代码上。

  1. 首先,在xml中不要写默认的字体颜色值,即android:textColor="xxx"
  2. 其次,在activity中mTextDisp.setTextColor(context.getResources().getColor(R.color.red));(使用color.xml文件中的颜色值)
    这样就OK了。或者直接使用Color类中的值:mTextDisp.setTextColor(Color.RED);(使用系统自带的颜色类Color类中的颜色值)
    或者直接使用颜色值:mTextDisp.setTextColor(0xffff00ff);//0xffff00ff是int类型的数据,分组一下0x|ff|ff00ff,0x是代表颜色整数的标记,ff是表示透明度,ff00ff表示颜色,注意:这里ffff00ff必须是8个的颜色表示,不接受ff00ff这种6个的颜色表示。

ps

点进去看下文档就知道了。需要的是颜色值,而R.color.red只是一个int。类似于一个key,value才是真正的颜色值。key->value表就是对应的xml文件。

svn命令

标准目录

trunk主开发代码。
branches分支代码。一般以版本或者功能feature为一个branche。
release发布版本。
tags备份版本。永远不做修改。

这些目录只是研发过程中约定俗成后的规范。便于版本控制和管理。
所以他们的命令都一样,都是用svn copy

svn copy svn://server/trunk svn://server/branches/ep -m "init ep"  

使用场景如下:

  1. trunk是个持续更新的稳定版本。
  2. 有新需求时从trunk copy到branches,开启一个分支。
  3. 测试完成。再从branch copy到release。封版发布。同时merge代码回到trunk。再copy为tag作为备份。
  4. 如果上述发布的release有紧急bug需要修复。而trunk代码已经比release还要新了。这时从上述release copy变成branch。之后回到步骤3.

代码合并

如何合并svn分支到主干上?

1.进入主干的svn目录
2.首先查询出创建分支的开始版本

命令:svn -q --stop-on-copy 分支URL,这条命令会查询出自创建分支以后
分支上的所有修改,最下面的那个版本号就是我们要找的版本号.

示例:


svn log -q --stop-on-copy svn://192.168.1.177/tags/beta_2009_12_24

3.使用svn merge命令合并分支。

命令:
svn -r 分支版本号:HEAD 分支的URL

解释:HEAD为当前主干上的最新版本

示例:
svn merge -r 12:HEAD svn://192.168.1.177/tags/beta_2009_12_24

4.使用svn st | grep "C "查找合并时的冲突文件,手工解决冲突
5.使用svn commit -m "" 提示合并后的版本

互联网思考

财富->流量->价值

有价值才有流量,再加上好的推广手段,流量可以暴增,然后再利用广告变现。

直接的商业就是最好的商业,从免费模式回到付费模式?

大众娱乐和三俗内容才是流量的把持者。

互联网并没有直接创造财富。
真正创造财富的还是实体制造业,互联网是加强了信息的透明度,整合资源,减去了很多中间环节,节约了成本。
目前的中国,财富集中到了0.02%的人手里,有钱无处投资,通过互联网投资,算是换钱于民。