Category Archives: PHP

PHP

替换wordpress全站为相对路径

作者: dplord, 访问量 1726





近来有时候在自己的电脑上,单机捣鼓wordpress, 甚至有时候我想离线写一篇文章,到时候传上去。发现wordpress所有资源都是带全路径的。因此研究把自己的wordpress, 整站替换为相对路径。这样于我而言有几个好处。

① 当网站是http的时候,请求的资源是http的、当网站是https的时候,请求的资源是https的。

②  部署的时候好部署,直接替换url之类的。

③  127.0.0.1上的wordpress跟我的域名https://dplord.com上的没有任何区别,都可以本地写文章,想本地覆盖服务器可以直接覆盖。这里记下来我替换wordpress全站为相对路径的方法。

④ 当访问https://dplord.com时候,原来的历史post里面是/wp-content/uploads/2015/12/xxxxx.jpg这样的资源,由于我在nginx开启了http的80端口强制301到443的https,使用relative path可以直接命中https资源。减少多次301跳转。节省服务器资源,同时也让客户端访问更快一点。

一、哪些资源需要被替换为相对路径

想了想,大概有这些可能需要被替换

① 引用的图片资源, 比如src=”/wp-content/uploads/2015/12/cropped-banner_cosmos_20151214.jpg” 替换为src=”/wp-content/uploads/2015/12/cropped-banner_cosmos_20151214.jpg”

② 各种目录、菜单栏、近期文章、文章详情、标签链接、评论链接为相对路径

二、更新wp_posts表, 处理旧文章(附件)里面的链接

由于有些历史的wp_post已经的一些包含src=”/wp-content/uploads/2015/12/cropped-banner_cosmos_20151214.jpg”的html已经存进数据库wp_posts表了。是静态写进去了。因此要先写段程序替换所有已经存在的posts的链接。

比如这里查询一下看看有哪些历史post包含了绝对路径,

select count(*) from wp_posts where (post_content like “%/wp-content%”) or (post_content like “%/wp-content%”) or (post_content like “%/wp-content%”) or (post_content like “%/wp-content%”);

5344626

为了方便,这里写了一个ruby程序,更新下数据表。代码如下:

wordpress_update_url.rb

require 'active_record'

ActiveRecord::Base.establish_connection(
    :adapter => 'mysql2',
    :host => '127.0.0.1',
    :username => 'your-db-username',
    :password => 'your-db-password',
    :database => 'your-db-database-name'
)

class Wp_posts < ActiveRecord::Base
  self.inheritance_column = nil
  self.table_name = "wp_posts"
end

replaceStr = ['/wp-content', '/wp-content', '/wp-content', '/wp-content']

res = Wp_posts.find_by_sql('select * from wp_posts where (post_content like "%http://www.dplord.com/wp-content%") or (post_content like "%http://dplord.com/wp-content%") or (post_content like "%https://www.dplord.com/wp-content%") or (post_content like "%https://dplord.com/wp-content%")')
res.each{|post|
  replaceStr.each{|str|
    if post.post_content.include? str
      post.post_content = post.post_content.gsub! str, "/wp-content"
    end
  }
  post.save
}

更新之后, 历史post里面都是相对路径了。

三、更新wp_options里面的siteurl跟home

wp_options这个表,存了一些选项值与配置相关的。原先写入数据库的历史post的siteurl  http://www.dplord.com的值就是存在这个表里面。

update wp_options set option_value=’/’ where option_name=’siteurl’ or option_name = ‘home';

更新完了之后,查看首页的html基本都是相对路径,但是剩下来一点点残余的。比如主题资源、插件里面的资源引用、首页header image地址等。

四、替换主题资源里面的绝对路径资源引用及其他

① 修改引用的绝对路径js资源

QQ20160605-0@2x

编辑 wp-includes/script-loader.php 第65行$scripts->base_url = $guessurl; 为$scripts->base_url = ”; (该行在wp_default_scripts函数里)

修改完了刷新,

QQ20160605-1@2x

发现已经改过来了。

② 修改wordpress自定义背景图的url

这里custom-background生成了一段html

QQ20160605-2@2x

这个在你的wordpress仪表盘,重新设置下background-image即可。

③修改引用的绝对路径css资源

QQ20160605-3@2x

修改wp-includes/script-loader.php function wp_default_styles()下的第6行$styles->base_url = $guessurl;为$styles->base_url = ”;

④ 修改wordpress顶部header-image

在后台重新选择一下图片即可。

关于php mysql长连接、连接池的一些探索

作者: dplord, 访问量 4919





php连接mysql的方式,用的多的是mysql扩展、mysqli扩展、pdo_mysql扩展,是官方提供的。php的运行机制是页面执行完会释放所有该php进程中的所有资源的,如果有多个并发访问本地的测试页面 http://127.0.0.1/1.php 根据php跟web服务器的不同,会开相应的线程或者进程去处理该请求,请求完了会释放结果的。也就是php无法从语言层面从页面到页面之间传递一些数据,但是mysql_pconnect跟pdo中的ATTR, 设置array(PDO::ATTR_PERSISTENT => true)如下是可以实现长连接的。

$conn = new PDO($dsn, DB_USER, DB_PASSWORD,
    array(PDO::ATTR_PERSISTENT => true)
);

长连接的作用我觉得是在高负载的情况下,通过复用长连接,减少了每个页面的建立数据库连接的时间,而这个建立mysql connection的时间,在我的机器上

  • 在数据库connnections < 10的情况下 , mysql pdo 建立connection time 为0.003ms, mysqli建立connection time为0.14ms
  • 在数据库connection接近满的时候,mysql pdo建立connection time为0.13ms, mysqli建立connection time为0.13ms

以上样本都是在大概估测时间,时间太小不好估计。其实建立连接的时间并不长,那这样为什么需要mysql长连接、连接池这样的东西呢。那是在高负载下,比如server单机可以接受的mysql并发在200左右,web server的单机并发在700左右,那么当大批量500并发连接压过来的时候, web server没到满负荷, mysql提前到了满负荷,就会导致所有页面无法响应、或者已经建立好数据库连接的页面执行很慢。

php中的mysql长连接由于php的运行方式有多种,因而长连接实现也有多种。需要web服务器支持才可以实现长连接,因为php是没有进程池跟连接池这种概念的,绝大多数情况下php应用本身不是一个应用服务器(后起之秀swoole, 是一个优秀的php应用服务器,不过是在c层面做的)。因而php的长连接其实是搭载apache这样的带有mpm模块的webserver, linux 下apache会维护一个进程池,开启了apache mpm功能之后,apache会默认维持一个进程池,mysql长连接之后的连接,并没有作为socet连接关闭,而是作为一个不释放的东西,放进了进程池/线程池里面去。等需要连接的时,apache从它维护的进程池/线程池里面取出mysql  socket connnection, 然后就可以复用此连接了。

这里测试一下,首先本机环境是archlinux , 后文所用mysql httpd php都是自己编译的源代码,都在/home/dengpan/opt目录。httpd的mpm模型这里采用的是worker, httpd的mpm(apache用于并行方面功能的,俗称多路处理模块)其实有perfork、worker、event三种。mpm的好处是让apache随时有些备用的spare或者空闲的子进程(服务器线程池),随时等待新过来的请求,这样客户端不需要在请求服务之前等待子进程的产生。

使用什么mpm,需要单独指定编译进去apache里面去,比如编译work mpm到apache里面去,比如我的最简化httpd的编译参数是

./configure \
--with-apr=/home/dengpan/opt/apr-1.5.2 \
--with-apr-util=/home/dengpan/opt/apr-util-1.5.4 \
--prefix=/home/dengpan/opt/httpd-2.4.16 \
--with-mpm=worker

查看httpd加载的模块,

wp_1

看到worker.c 已经编译过去了,

mpm的配置参数为

<IfModule mpm_worker_module>
    StartServers             15
    MinSpareThreads         75
    MaxSpareThreads        250
    ThreadsPerChild         10
    MaxRequestWorkers      400
    MaxConnectionsPerChild   0
</IfModule>

启动apache用pstree看到 |-httpd—15*[httpd—11*[{httpd}]],说明起了15个server进程,每个server起了10个子线程。整个mpm要维持的最小的闲置线程数量在75,最大的闲置线程在250。满载的最大的工作线程在400个。下面准备一个shell脚本,每1秒输出下当前mysql的active连接数量, 查看mysql current连接数我用的较多的有2个方法

  • 进mysql shell, 执行SHOW STATUS WHERE `variable_name` = ‘Threads_connected'; 不过这个方法得mysql shell进的去才对,当connections很多的时候,mysql shell进不去也就无法查询了
  • shell直接查询,  find /proc/`pidof mysqld`/fd/ -follow -type s | wc -l , 需要root权限,好处是即使mysql因为too many connections无法进入shell的时候还是可以连接进去。

这里用方法2,因为后面回到机器的mysql满载负荷的。写一个shell如下:

#!/bin/bash
while(true)
do
    find /proc/`pidof mysqld`/fd/ -follow -type s | wc -l
	sleep 1
done

后面执行该shell不断的输出当前连接数,测试可得

  1. cli下执行php,长连接无效,cli下脚本一退出,连接即释放
  2. apche+mod_php不开启mpm模块的话,无论mysql mysql_pconnect、pdo_mysql长连接, 页面访问完毕, mysql连接即释放。
  3. apche+mod_php开启mpm模块(worker模式)的话,无论mysql mysql_pconnect、pdo_mysql长连接, 页面访问完毕, mysql连接+1,直到达到最大的mysql连接数,不在增加,但是访问页面还是可以复用连接查询到相应数据。
  4. nginx+php-fpm下mysql长连接基本无效果。

apache之所以能够复用mysql连接,说明apache肯定为mysql自己实现了一些功能函数、模块,否则不可能把一个未知类型的socket指针存下来的。用ldd查看,

➜  mysql_persist  ldd /home/dengpan/opt/httpd-2.4.16/bin/httpd
        linux-vdso.so.1 (0x00007ffffcbde000)
        libpcre.so.1 => /usr/lib/libpcre.so.1 (0x00007f8e8d17c000)
        libaprutil-1.so.0 => /home/dengpan/opt/apr-util-1.5.4/lib/libaprutil-1.so.0 (0x00007f8e8cf57000)
        libexpat.so.1 => /usr/lib/libexpat.so.1 (0x00007f8e8cd2d000)
        libapr-1.so.0 => /home/dengpan/opt/apr-1.5.2/lib/libapr-1.so.0 (0x00007f8e8cafb000)
        libuuid.so.1 => /usr/lib/libuuid.so.1 (0x00007f8e8c8f6000)
        librt.so.1 => /usr/lib/librt.so.1 (0x00007f8e8c6ee000)
        libcrypt.so.1 => /usr/lib/libcrypt.so.1 (0x00007f8e8c4b6000)
        libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f8e8c299000)
        libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f8e8c095000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f8e8bcf3000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8e8d3ec000)

可以猜测出/home/dengpan/opt/apr-util-1.5.4/lib/libaprutil-1.so.0跟/home/dengpan/opt/apr-1.5.2/lib/libapr-1.so.0应实现了跟mysql相关的代码段。由于我是本地编译的,很方便找到函数入口,/home/dengpan/github/apache-httpd/apr-util-1.5.4/dbd/apr_dbd_mysql.c这个文件,其实是apache的mod_dbd对常见的数据库都做了长连接支持。而nginx跟php-fpm的关系并不像php跟apache那样,所以nginx+php-fpm无法实现对应的长连接。大概是php-fpm并没有做mysql的进程、线程池。 Continue reading

关于一些language的部分性能对比

作者: dplord, 访问量 921





周五晚上闲着无聊跟lys 讨论语言性能

然后我随机一想想出了一个测试用例。

  1. 例子如下:
  2. 创建 10000 个文本,文本在当前目录的 out目录下,文本名为 r_1、r_2、r_3..
  3. 每个文本都有 10000 行,每一行是用 \t  间隔的5个数,每个数是1-10000 之间的随机数

随便我们用什么语言实现,暂时仅仅对比完成目标的时间

其实这个测试用例对比的就是以下几个方面:

  1. 文件IO性能
  2. 文件名需要拼接,字符串concat性能
  3. 大量的循环以及随机数的产生比拼语言的速度

我先顺手写了一个C的,代码如下:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define MAX 10000

char * one_mil();
int create_one(int filename);
int main()
{
	int i;
	for(i=0;i<MAX;i++)
	{
		create_one(i);
	}
	return 0;
}

int create_one(int filename)
{
	int f;
	char *file_name;
	file_name=malloc(12);
	sprintf(file_name,"out/r_1_%d",filename);
	mode_t f_attr;
	f_attr=S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
	f=open(file_name,O_RDWR | O_CREAT,f_attr);
	if(f==-1)
	{
		printf("创建%d出错\n",filename);	
	}else{
		char *temp_b=one_mil();
		write(f,temp_b,strlen(temp_b));
		close(f);
		free(temp_b);
	}
	return 0;
}

char * one_mil()
{
	char *p;
	int i,p_size=0;
	p=malloc(MAX*100);
	char *temp_a;
	temp_a=malloc(100);
	for(i=0;i<10000;i++)
	{
	    sprintf(temp_a,"%d\t%d\t%d\t%d\t%d\n",rand()%10000+1,rand()%10000+1,rand()%10000+1,rand()%10000+1,rand()%10000+1,rand()%10000+1);
	    memcpy(p+p_size,temp_a,strlen(temp_a));
	    p_size+=strlen(temp_a);	
	}	
	free(temp_a);
	return p;
}

编译为:

/home/dengpan/opt/gcc-4.9.1/bin/gcc -std=c99 myfile.c -o a.out -O2

注:后续C代码编译都是最新的gcc 4.9.1 -std=c99 -O2

执行时间为:

20

看来还是很快的,但是还有几个很大的优化点第一个就是频繁申请大块的内存与释放,第二就是sprintf与memcpy执行过多

 

下面来了一个JAVA版本的,几乎默认写法,测试结果惊人

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;

/**
 * Created by dengpan on 14-10-25.
 */
public class MyFile {
    private static final int MAX = 10000;

    private static final Random sRandom = new Random();

    private static String one_mil() {
        StringBuilder builder = new StringBuilder(MAX * 100);
        for(int i = 0; i < MAX; i++) {
            builder.append(sRandom.nextInt(10000) + 1);
            builder.append('\t');
            builder.append(sRandom.nextInt(10000) + 1);
            builder.append('\t');
            builder.append(sRandom.nextInt(10000) + 1);
            builder.append('\t');
            builder.append(sRandom.nextInt(10000) + 1);
            builder.append('\t');
            builder.append(sRandom.nextInt(10000) + 1);
            builder.append('\n');
        }

        return builder.toString();
    }

    private static void create_one(File dir, int filename) throws IOException {
        File out = new File(dir, "r_java_" + filename);
        if (!out.exists())
            out.createNewFile();
        String content = one_mil();
        FileOutputStream os = new FileOutputStream(out);
        os.write(content.getBytes());
        os.close();
    }

    public static void main(String[] args) {
        File dir = new File("out");
        if (!dir.exists())
            dir.mkdirs();
        for (int i = 0; i < MAX; i++) {
            try {
                create_one(dir, i);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

结果

1

但是多次测试结果不稳定,结果在39s-47s 之间,果然是JVM Snapshot复杂的动态调度有关,不过性能很吓人

因为linux系统所有的对文件读写,内存使用都是系统调用实现的,只不过不同的系统对系统调用有了封装。有了封装必然慢,java简直吓人

我觉得纯粹在文件操作,C用了open write绝对在IO上比JAVA快,只不过在buffer处理上还有sprintf这个函数比java慢抵消了IO优势

 

我决定处理一下刚才的那段C代码

优化方向为:

  • 减少函数调用
  • 少用sprintf把一行5个的sprintf合并
  • 减少内存malloc跟free的次数

处理后的代码为:

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#define FILE_COUNT 10000

int main()
{
	char filename[100];
	char *text_buf;
	char *p;
	int i, j;
	int fd;
	size_t count = 0;
	int nbytes;

	mode_t f_attr;
	f_attr = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

	text_buf = malloc(FILE_COUNT * 100);
	for (i = 0; i < FILE_COUNT; i++) {
		sprintf(filename, "out/r_1_%d", i);		
		fd = open(filename, O_RDWR | O_CREAT, f_attr);
		if (fd < 0) {
			perror("create failed");
			exit(EXIT_FAILURE);
		}

		p = text_buf;
		count = 0;
		for (j = 0; j < FILE_COUNT; j++) {
			nbytes = sprintf(p, "%d\t%d\t%d\t%d\n", 
					rand() % FILE_COUNT + 1,
					rand() % FILE_COUNT + 1,
					rand() % FILE_COUNT + 1,
					rand() % FILE_COUNT + 1,
					rand() % FILE_COUNT + 1);
			if (nbytes <= 0) {
				perror("oome");
				exit(EXIT_FAILURE);
			}

			p += nbytes;
			count += nbytes;
		}
		write(fd, text_buf, count);
		close(fd);
	}
	free(text_buf);
	return 0;
}

编译以后执行

21

优化明显

但是还是比JAVA的慢或者不相上下,按理来说C在这种疯狂调用系统IO绝对比JAVA快,我觉得这段代码的慢在sprintf上,由于我们的格式化仅仅需要格式化%d 都可以自己写一个,于是决定改写sprintf

改写后代码如下:

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#define FILE_COUNT 10000

size_t sprint_int(char *buf, int val)
{
	size_t nbytes = 0;
	int tmp = val;
	char *p;

	do {
		tmp /= 10;
		nbytes++;
	} while (tmp != 0);
	
	p = buf + nbytes - 1;
	while (p >= buf) {
		tmp = val % 10;
		val /= 10;
		*p-- = '0' + tmp;
	}
	
	return nbytes;
}

int main()
{
	char filename[100];
	char *text_buf;
	char *p;
	int i, j;
	int fd;
	size_t count = 0;
	int nbytes;

	mode_t f_attr;
	f_attr = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

	text_buf = malloc(FILE_COUNT * 100);
	for (i = 0; i < FILE_COUNT; i++) {
		sprintf(filename, "out/r_c_%d", i);		
		fd = open(filename, O_RDWR | O_CREAT, f_attr);
		if (fd < 0) {
			perror("create failed");
			exit(EXIT_FAILURE);
		}

		p = text_buf;
		count = 0;
		for (j = 0; j < FILE_COUNT; j++) {
			nbytes = sprint_int(p, rand() % FILE_COUNT + 1);
			count += nbytes + 1;
			p += nbytes;
			*p++ = '\t';

			nbytes = sprint_int(p, rand() % FILE_COUNT + 1);
			count += nbytes + 1;
			p += nbytes;
			*p++ = '\t';

			nbytes = sprint_int(p, rand() % FILE_COUNT + 1);
			count += nbytes + 1;
			p += nbytes;
			*p++ = '\t';

			nbytes = sprint_int(p, rand() % FILE_COUNT + 1);
			count += nbytes + 1;
			p += nbytes;
			*p++ = '\t';

			nbytes = sprint_int(p, rand() % FILE_COUNT + 1);
			count += nbytes + 1;
			p += nbytes;
			*p++ = '\n';

		}
		write(fd, text_buf, count);
		close(fd);
	}
	free(text_buf);
	return 0;
}

结果:

1

果然很不错

本来测试C的,以前学过glib

用glib写了一个版本,看看效果

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>

#define MAX_NUM 10000

int main()
{
	mode_t f_attr;
	f_attr = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

	for(int i=0; i < MAX_NUM; i++)
	{
		GString *s;
		s = g_string_new("");
		for(int j = 0; j < MAX_NUM; j++)			
		{
			g_string_append_printf(s,"%d",rand() % MAX_NUM + 1);
			g_string_append(s,"\t");
			g_string_append_printf(s,"%d",rand() % MAX_NUM + 1); 
			g_string_append(s,"\t");
			g_string_append_printf(s,"%d",rand() % MAX_NUM + 1); 
			g_string_append(s,"\t");
			g_string_append_printf(s,"%d",rand() % MAX_NUM + 1); 
			g_string_append(s,"\t");
			g_string_append_printf(s,"%d",rand() % MAX_NUM + 1); 
			g_string_append(s,"\n");
		}
		
		char *fname;
		fname = malloc(12);
		sprintf(fname,"out/r_%d",i);
		int f;
		f = open(fname,O_RDWR | O_CREAT,f_attr);
		write(f,s->str,s->len);
		close(f);
		free(fname);
		g_string_free(s,TRUE);
	}
}

结果:

1

看来glib在疯狂的内存释放跟申请方面很弱,所以各大服务器端软件基本都是自己用C实现一套基本数据结构

后面闲着没事都写了一个版本

  • PHP版本,拼接字符串用了
  • concat
  • 拼接成数组,再implode
  • 在github找php StringBuffer的实现

等,最快的如下,下面python ruby  nodejs也仅仅展现测试出来的 最好的版本

<?php
for($i = 0 ; $i < 10000 ; $i++)
{
	$text='';		
	for($j = 0 ; $j < 10000 ; $j++)
	{ 
		$temp='';
		//$text.= rand(1,10000)."\t".rand(1,10000)."\t".rand(1,10000)."\t".rand(1,10000)."\t".rand(1,10000)."\n";
		sprintf($temp,"%d\t%d\t%d\t%d\t%d\n",rand(1,10000),rand(1,10000),rand(1,10000),rand(1,10000),rand(1,10000));
		$text.=$temp;
	}
	file_put_contents("out/r_php_$i",$text);
}

时间

PHP5.6.0  1m51s

HHVM 3.3.0  58s

算是很快很快的

 

python版本的

#encoding:utf-8

import random
for i in xrange(10000):
	f = open('out/r_py_' + str(i) ,'w+')
	a = []
	for j in xrange(10000):
		a.append(random.randint(1,10000))
		a.append('\t')
		a.append(random.randint(1,10000))
		a.append('\t')
		a.append(random.randint(1,10000))
		a.append('\t')
		a.append(random.randint(1,10000))
		a.append('\t')
		a.append(random.randint(1,10000))
		a.append('\n')
	f.write(''.join(a))
	f.close()
	
	

结果 20+ min

ruby版本

#encoding:utf-8
	
require "stringio"

(1..10000).each do |i|
	a = ''
	(1..10000).each do |j|
		s = StringIO.new
		s << rand(1000)+1 << "\t" << rand(1000)+1 << "\t"<< rand(1000)+1 << "\t"<< rand(1000)+1 << "\t"<< rand(1000)+1 << "\n"
	end
	f = File.new("out/r_rb_"+i.to_s,"w+")
	f.syswrite(s.string)
	f.close()
end

结果 20+ min

 

lys写了一个nodejs版本的

var fs = require('fs');
var buf = new Buffer(10000);
var len = 0;
for (var i = 0; i < 10000; i++) {
	for (var j = 0; j < 10000; j++) {
		len += buf.write(Math.floor((Math.random() * 10000) + 1) + "\t", len);
		len += buf.write(Math.floor((Math.random() * 10000) + 1) + "\t", len);
		len += buf.write(Math.floor((Math.random() * 10000) + 1) + "\t", len);
		len += buf.write(Math.floor((Math.random() * 10000) + 1) + "\t", len);
		len += buf.write(Math.floor((Math.random() * 10000) + 1) + "\n", len);
		if (j % 100 == 0) {
			fs.writeFile("out/r_js_" + i, buf.toString('ascii', 0, len), function(err){
				if (err)
					console.log(err);
			});
			len = 0;
		}
	}
}

结果内存溢出跑不完

Continue reading