标签归档:hhvm

岁月如歌,那些在华中大网络中心当协管的日子(部署篇)

作者: dplord, 访问量 3134





伴着夜晚的一点点凉意,在这熟悉的黑暗的夜色中,冷笔勾勒我在华中科大网络中心当协管的那些年月。想想突然离开学校也很久了,那个曾经承载我几年的欢声笑语、伴我在孤寒岁月中成长的学校以及渐渐远离我生命中了,不过那些曾经的烙在我生命中的印记,却是那么的强而有力,挥之不去。不过想想我过段时间要回学校办手续的,欣喜跃上心头。

记得最初的跟网络中心的打交道是,大一的时候刚连校园网的时候,我的windows下自动开了bluetooth调制解调器模式,跟校园网的拨号冲突导致死活无法上网,最后几通电话之后无果只能带电脑去网络中心解决了,想想大一进校的时候自己是多么的青涩,这也许是跟网络中心打交道的最早的起源了。

开始切入正题了,我是从大二暑假开始跟网路中心有些业务、项目上的交道的。从我做的cs.hust.edu.cn为例开始,从网络中心申请了1个测试机器,开始开发项目。最后等项目上线后,自然作为cs.hust.edu.cn的作者跟维护者,自然成了网络中心的协管了,其实就是在网络中心看管3台服务器,保证安全与服务可靠,当然作为作者,我是一度那么想优化ta的性能,以前也不断的做了各种折腾,以前年轻真是好,永远那么有精力,为了心中一点点的想法而通宵达旦地验证, 追求那么丁点的问心无愧跟坦坦荡荡把。

cs.hust.edu.cn的技术结构跟开发过程,这里以前写在了本博客的另一篇文章中,这里不做赘述。一言以蔽之,cs.hust.edu.cn是目前上线的部署代码在1万4千2百行大概的php站点,(运维的、php library的没算,中间修改的、废弃的代码没算)。其中我管理有3台机器,但是真实情况下cs.hust.edu.cn是部署在一个新的单独的机器上。机器的配置跟我用的核心软件如下表:

CPU 2 physical cpu, 24 processor
内存 48G内存
硬盘 4T机械硬盘(做了raid5)
网络状况 教育网,华中科技大学网络中心托管
operating system Centos 6.3(Final), 64bit
linux kernel 2.6
web server nginx 1.7.4(compile from source)
database server mysql 5.6.21(compile from source)
php php 5.5.9(compile from source)
hhvm HipHop VM 3.5.0-dev+2014.12.11 (rel)
Compiler: heads/master-0-g546087bf1b0560c4a9e254fcad46a9212e42ccc2
Repo schema: cf1780b3cc3857e091e924935ae6267e9794de9c
Extension API: 20140829
其他软件 ruby 2.2.1、rust 1.1.0、erlang、fail2ban

maybe someone will feel strange about why I use Centos 6.3 which is so old, 我当时准备好了给ta去装个opensuse text mode 12.3的(我当时是opensuse铁粉, 12.3是当时的最新版的opensuse)了,为了试验opensuse text mode的操作(当时仅仅是opensuse桌面版很熟悉,opensuse的很多配置网络、防火墙有的跟debian、ubuntu、centos等等不同,要单独学习, 还有在纯粹的命令行下配置网络连接啊等等、专门抱了一个台式机过来装opensuse text mode学习试试), 我把一个台式机装了opensuse text mode用了一段时间, 感觉挺好的。都已经做好了准备等新服务器到达的时候,去网络中心亲自给ta装上opensuse text mode 12.3,都跟学院说好了,他们也同意了。结果等机器来的那天,是上午10点对来的, 我当时在上大三上学期的《软件工程》的最后一节课,不能不去,不去上最后这节课老师大概的意思是说就直接这门课不给过,结果没有亲自装机,网络中心的老师就随便装了个centos 6.3, 算了也不吐槽网络中心给装的这么旧的发行版了,因为上一个我管理的机器是readhat5的,估计还很多人没见过redhat5把,那古老的东西,放图大家感受下。

6

7

部署工作

当初cs.hust.edu.cn开发的时候是用的测试机器在自申请的一个网络中心的测试机器上,开发进行,对外服务,当初是用ip直接访问的。测试机器性能记得很渣,大概是1GHz CPU + 2G内存+50G机械硬盘的样子,还是在一个windows server上装vmvare虚拟出来的一个vm。测试机器系统是centos6.4,用的apache + mysql + php提供服务。项目部署需要web server提供rewrite支持,其他的也没什么很特殊的。就是centos6.3上面的所有东西太老了于是很自然就开始了我的编译nginx+mysql+php+hhvm之旅。

1编译nginx

nginx编译很简单,没什么依赖,编译参数如下。

./configure \
--prefix=/home/dengpan/opt/nginx-1.7.3 \
--sbin-path=/home/dengpan/opt/nginx-1.7.3/sbin/nginx \
--conf-path=/home/dengpan/opt/nginx-1.7.3/conf/nginx.conf \
--error-log-path=/home/dengpan/opt/nginx-1.7.3/var/log/error.log \
--http-log-path=/home/dengpan/opt/nginx-1.7.3/var/log/access.log \
--pid-path=/home/dengpan/opt/nginx-1.7.3/var/run/nginx.pid \
--lock-path=/home/dengpan/opt/nginx-1.7.3/var/run/nginx.lock \
--http-client-body-temp-path=/home/dengpan/opt/nginx-1.7.3/var/cache/client_temp \
--http-proxy-temp-path=/home/dengpan/opt/nginx-1.7.3/var/cache/proxy_temp \
--http-fastcgi-temp-path=/home/dengpan/opt/nginx-1.7.3/var/cache/fastcgi_temp \
--http-uwsgi-temp-path=/home/dengpan/opt/nginx-1.7.3/var/cache/uwsgi_temp \
--http-scgi-temp-path=/home/dengpan/opt/nginx-1.7.3/var/cache/scgi_temp \
--user=www \
--group=www \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-mail \
--with-mail_ssl_module \
--with-file-aio \
--with-ipv6 \
--add-module=../mod_strip

基本按照官方编译参数(http://nginx.org/en/docs/configure.html)来的,只是加了mod_strip这个非官方模块,当时是想把所有页面压缩成一行的html, 节省下行传输带宽,提高速度。后面发现这点点html相比图片(我把首页的大图已经内页的logo图换成了webp格式,browser支持webp就显示webp格式,不支持就用jpeg格式)简直不值一提,其实也是因为这个mod_strip压缩html处理有bug, 处理有些页面有问题,标签会闭合到一起去,无法正常展示。

2编译php

php编译比较麻烦,一堆的依赖,第一次编译的时候,我编译了2天时间。后面编译就容易多了。在我的archlinux(i5 cpu, 机械硬盘)编译php 5.5.9大概需要8min。服务器make -j24大概需要10min。编译参数如下:

./configure \
--prefix=/home/dengpan/opt/php-5.5.9 \
--with-config-file-path=/home/dengpan/opt/php-5.5.9/etc \
--with-config-file-scan-dir=/home/dengpan/opt/php-5.5.9/etc/php.d\
--with-libdir=lib64 \
--with-curl \
--with-freetype-dir=/home/dengpan/opt/freetype-2.4.2/  \
--with-gd \
--with-gettext \
--with-iconv-dir=/home/dengpan/opt/libiconv-1.14   \
--with-jpeg-dir  \
--with-kerberos \
--with-ldap \
--with-ldap-sasl \
--with-libxml-dir  \
--with-mcrypt=/home/dengpan/opt/libmcrypt-2.5.7 \
--with-mhash \
--with-mysql=mysqlnd  \
--with-mysqli=mysqlnd  \
--with-openssl \
--with-pcre-regex \
--with-pdo-mysql=mysqlnd \
--with-pdo-sqlite=shared \
--with-pear \
--with-png-dir \
--with-xmlrpc \
--with-xsl \
--with-zlib \
--enable-fpm \
--enable-bcmath \
--enable-libxml \
--enable-inline-optimization \
--enable-gd-native-ttf \
--enable-mbregex \
--enable-mbstring \
--enable-opcache \
--enable-pcntl \
--enable-shmop \
--enable-soap \
--enable-sockets \
--enable-sysvsem \
--enable-xml \
--enable-zip \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--disable-rpath

其中编译的时候缺少很多lib请自行用yum安装, 但是centos6.3上编译php,其中的libmcrypt、libiconv、freetype2需要自己编译安装,有些bug, 时间久了我也没记下来,记得编译的时候一堆问题,其中libiconv还需要手动修改一些代码之类的,具体的若有人碰到了,请自行stackoverflow一下。

3编译mysql

mysql编译比较容易,没太多依赖。编译参数如下:

cmake \
-DCMAKE_INSTALL_PREFIX=/home/dengpan/opt/mysql-5.6.21 \
-DMYSQL_DATADIR=/home/dengpan/opt/mysql-5.6.21/data \
-DSYSCONFDIR=/home/dengpan/opt/mysql-5.6.21/etc \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_READLINE=1 \
-DMYSQL_UNIX_ADDR=/home/dengpan/opt/mysql-5.6.21/tmp/mysql.sock \
-DMYSQL_TCP_PORT=3306 \
-DENABLED_LOCAL_INFILE=1 \
-DWITH_PARTITION_STORAGE_ENGINE=1 \
-DEXTRA_CHARSETS=all \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci

编译完了之后mysql需要初始化用户名用户组与mysql目录。

  1.  检查mysql用户组
    cat /etc/passwd|grep mysql
    cat /etc/group|grep mysql

    没有就添加

    1 groupadd mysql
    2 useradd -g mysql mysql
    
  2. 给mysql目录设置权限
    chown -R mysql:mysql /home/dengpan/opt/mysql-5.6.21
    
  3. 初始化数据库
    scripts/mysql_install_db --basedir=/home/dengpan/opt/mysql-5.6.21 --datadir=/home/dengpan/opt/mysql-5.6.21/data --user=mysql
    
  4. 直接启动数据库
    /home/dengpan/opt/mysql-5.6.21/support-files/mysql.server start
    

4编译hhvm

hhvm,可能大家用的少,这里简介一下,hhvm是facebook开发的一个php jit解释器,在hhvm + nginx部署网站中,hhvm此时等同于php-fpm的角色,作用就是监听端口(一般是9000端口)或者本地unix sock,然后nginx通过fastcgi建立socket与之通信。centos 6.3编译hhvm有点麻烦,内核卡在那里,glibc上不去,一堆编译错误。编译过程按照github hhvm的官方wiki(Building and installing HHVM on CentOS 6.3)编译的,我在我的opensuse 12.3跟13.1跟后来的archlinux都编译成功了, 在centos6.3编译了3天没有成功只好安装了 facebook提供的prebuilt package 。hhvm有一点点不稳定,但是性能根据我当时的使用情况,的确惊人。

由于此篇文章可能太长, 太多感悟与踩坑经历要写,因而拆分几篇来写, 本篇是部署篇。

岁月如歌,那些在华中大网络中心当协管的日子(部署篇)

岁月如歌,那些在华中大网络中心当协管的日子(运维工作篇)

岁月如歌,那些在华中大网络中心当协管的日子(其他总结篇)

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

作者: dplord, 访问量 1572





周五晚上闲着无聊跟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;
		}
	}
}

结果内存溢出跑不完

继续阅读