/*
* (C) Radim Kolar 1997-2004
* This is free software, see GNU Public License version 2 for
* details.
*
* Simple forking WWW Server benchmark:
*
* Usage:
* webbench --help
*
* Return codes:
* 0 - sucess
* 1 - benchmark failed (server is not on-line)
* 2 - bad param
* 3 - internal error, fork failed
*
*/
#include "socket.c"
#include <unistd.h>
#include <sys/param.h>
#include <rpc/types.h>
#include <getopt.h>
#include <strings.h>
#include <time.h>
#include <signal.h> /* values */
volatile int timerexpired=;//判断测压市场是否已经达到设定的时间
int speed=;//记录进程成功得到服务器相应的数量
int failed=;//记录失败的数量(speed表示成功数,failed表示失败数)
int bytes=;//记录进程成功读取的字节数
/* globals */
int http10=; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 *///HTTP版本
/* Allow: GET, HEAD, OPTIONS, TRACE */
#define METHOD_GET 0
#define METHOD_HEAD 1
#define METHOD_OPTIONS 2
#define METHOD_TRACE 3
#define PROGRAM_VERSION "1.5"
int method=METHOD_GET;//定义HTTP请求方法:默认方式GET请求
int clients=;//并发数目,默认只有一个进程发送请求,通过 -c 参数设置
int force=;//是否需要等待读取从server返回的数据,0表示要等待读取
int force_reload=;//是否使用缓存,1表示不缓存,0表示可以缓存页面
int proxyport=;//代理服务器的端口
char *proxyhost=NULL;//代理服务器的端口
int benchtime=;//测压时间,默认30秒,通过 -t 参数设置
/* internal */
int mypipe[];//使用管道进行父进程和子进程的通信
char host[MAXHOSTNAMELEN];//服务器端IP
#define REQUEST_SIZE 2048
char request[REQUEST_SIZE];//发送HTTP请求的内容 static const struct option long_options[]=
{
{"force",no_argument,&force,},
{"reload",no_argument,&force_reload,},
{"time",required_argument,NULL,'t'},
{"help",no_argument,NULL,'?'},
{"http09",no_argument,NULL,''},
{"http10",no_argument,NULL,''},
{"http11",no_argument,NULL,''},
{"get",no_argument,&method,METHOD_GET},
{"head",no_argument,&method,METHOD_HEAD},
{"options",no_argument,&method,METHOD_OPTIONS},
{"trace",no_argument,&method,METHOD_TRACE},
{"version",no_argument,NULL,'V'},
{"proxy",required_argument,NULL,'p'},
{"clients",required_argument,NULL,'c'},
{NULL,,NULL,}
}; /* prototypes */
static void benchcore(const char* host,const int port, const char *request);
static int bench(void);
static void build_request(const char *url); /*
webbench在运行时可以设定压测的持续时间,以秒为单位。
例如我们希望测试30秒,也就意味着压测30秒后程序应该退出了。
webbench中使用信号(signal)来控制程序结束。
函数1是在到达结束时间时运行的信号处理函数。
它仅仅是将一个记录是否超时的变量timerexpired标记为true。
后面会看到,在程序的while循环中会不断检测此值,
只有timerexpired=1,程序才会跳出while循环并返回。
*/
static void alarm_handler(int signal)
{
timerexpired=;
} /*
教你如何使用webbench的函数,
在linux命令行调用webbench方法不对的时候运行,作为提示。
有一些比较常用的,比如-c来指定并发进程的多少;
-t指定压测的时间,以秒为单位;
支持HTTP0.9,HTTP1.0,HTTP1.1三个版本;
支持GET,HEAD,OPTIONS,TRACE四种请求方式。
不要忘了调用时,命令行最后还应该附上要测的服务端URL。
*/
static void usage(void)
{
fprintf(stderr,
"webbench [option]... URL\n"
" -f|--force Don't wait for reply from server.\n"
" -r|--reload Send reload request - Pragma: no-cache.\n"
" -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n"
" -p|--proxy <server:port> Use proxy server for request.\n"
" -c|--clients <n> Run <n> HTTP clients at once. Default one.\n"
" -9|--http09 Use HTTP/0.9 style requests.\n"
" -1|--http10 Use HTTP/1.0 protocol.\n"
" -2|--http11 Use HTTP/1.1 protocol.\n"
" --get Use GET request method.\n"
" --head Use HEAD request method.\n"
" --options Use OPTIONS request method.\n"
" --trace Use TRACE request method.\n"
" -?|-h|--help This information.\n"
" -V|--version Display program version.\n"
);
};
int main(int argc, char *argv[])
{
int opt=;
int options_index=;
char *tmp=NULL; if(argc==)
{
usage();
return ;
} while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
{
switch(opt)
{
case : break;
case 'f': force=;break;
case 'r': force_reload=;break;
case '': http10=;break;
case '': http10=;break;
case '': http10=;break;
case 'V': printf(PROGRAM_VERSION"\n");exit();
/**
*C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)
*int atoi(const char *str)
*str -- 要转换为整数的字符串。
*该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。
*/
case 't': benchtime=atoi(optarg);break;
case 'p':
/* proxy server parsing server:port */
/**
*strrchr() 函数用于查找某字符在字符串中最后一次出现的位置,其原型为:
*char * strrchr(const char *str, int c);
*【参数】str 为要查找的字符串,c 为要查找的字符。
*strrchr() 将会找出 str 字符串中最后一次出现的字符 c 的地址,然后将该地址返回。
*注意:字符串 str 的结束标志 NUL 也会被纳入检索范围,所以 str 的组后一个字符也可以被定位。
*【返回值】如果找到就返回该字符最后一次出现的位置,否则返回 NULL。
*返回的地址是字符串在内存中随机分配的地址再加上你所搜索的字符在字符串位置。设字符在字符串中首次出现的位置为 i,那么返回的地址可以理解为 str + i。
*/
/**
*optarg : char *optarg; //选项的参数指针
*/
tmp=strrchr(optarg,':');
proxyhost=optarg;
if(tmp==NULL)
{
break;
}
if(tmp==optarg)
{
fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
return ;
}
/**
*C 库函数 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。
*size_t strlen(const char *str)
*参数:str -- 要计算长度的字符串。
*该函数返回字符串的长度。
*/
if(tmp==optarg+strlen(optarg)-)
{
fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
return ;
}
*tmp='\0';//把:替换为\0
//从\0之后到\0之前
proxyport=atoi(tmp+);break;
case ':':
case 'h':
case '?': usage();return ;break;
case 'c': clients=atoi(optarg);break;
}
}
//int optind:argv的当前索引值。当getopt函数在while循环中使用时,剩下的字符串为操作数,下标从optind到argc-1
//argc,argv 参考:https://www.cnblogs.com/lanshanxiao/p/11568037.html
//getopt_long()中的函数,参考:https://www.cnblogs.com/xhg940420/p/7016574.html
//扫描完毕后,optind指向非长选项和非短选项和非参数的字段,这里应该指向URL
if(optind==argc) {
fprintf(stderr,"webbench: Missing URL!\n");
usage();
return ;
} if(clients==) clients=;
if(benchtime==) benchtime=;
/* Copyright */
fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
"Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
);
/**
*命令读取完成后,argv[optind]中应该存放着URL,
*建立完整的http请求,http请求存放在变量char request[REQUEST_SIZE]中
*/
build_request(argv[optind]);
/* print bench info *///输出平台信息
printf("\nBenchmarking: ");
switch(method)
{
case METHOD_GET:
default:
printf("GET");break;
case METHOD_OPTIONS:
printf("OPTIONS");break;
case METHOD_HEAD:
printf("HEAD");break;
case METHOD_TRACE:
printf("TRACE");break;
}
printf(" %s",argv[optind]);//打印出URL
switch(http10)
{
case : printf(" (using HTTP/0.9)");break;
case : printf(" (using HTTP/1.1)");break;
}
printf("\n");
if(clients==) printf("1 client");
else
printf("%d clients",clients); printf(", running %d sec", benchtime);
if(force) printf(", early socket close");
if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
if(force_reload) printf(", forcing reload");
printf(".\n");
//压力测试最后一句话,所有的压力测试都在bench函数中实现
return bench();
} /*
函数主要操作全局变量char request[REQUEST_SIZE],根据url填充其内容。
典型的HTTP的GET请求:
GET /test.jpg HTTP/1.1
User-Agent: WebBench 1.5
Host:192.168.10.1
Pragma: no-cache
Connection: close build_request函数的目的就是要把
类似于以上这一大坨信息全部存到全局变量request[REQUEST_SIZE]中,
其中换行操作使用的是”\r\n”。
而以上这一大坨信息的具体内容是要根据命令行输入的参数,以及url来确定的。
该函数使用了大量的字符串操作函数,
例如strcpy,strstr,strncasecmp,strlen,strchr,index,strncpy,strcat。
对这些基础函数不太熟悉的同学可以借这个函数复习一下。
*/
void build_request(const char *url)
{
char tmp[];
int i; bzero(host,MAXHOSTNAMELEN);//bzero():置host(字节字符串)前MAXHOSTNAMELEN个字节为0,包括'\0')
bzero(request,REQUEST_SIZE); if(force_reload && proxyhost!=NULL && http10<) http10=;//满足一定条件,更换HTTP协议
if(method==METHOD_HEAD && http10<) http10=;
if(method==METHOD_OPTIONS && http10<) http10=;
if(method==METHOD_TRACE && http10<) http10=; switch(method)
{
default:
//strcpy() 函数用于对字符串进行复制(拷贝)。
//char* strcpy(char* strDestination, const char* strSource);
//strSource 指向的字符串复制到 strDestination
case METHOD_GET: strcpy(request,"GET");break;
case METHOD_HEAD: strcpy(request,"HEAD");break;
case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
case METHOD_TRACE: strcpy(request,"TRACE");break;
} //char*strcat(char* strDestination, const char* strSource);
/*
strcat() 函数用来将两个字符串连接(拼接)起来。
strcat() 函数把 strSource 所指向的字符串追加到 strDestination 所指向的字符串的结尾,
所以必须要保证 strDestination 有足够的内存空间来容纳两个字符串,否则会导致溢出错误。
注意:strDestination 末尾的\0会被覆盖,strSource 末尾的\0会一起被复制过去,最终的字符串只有一个\0。
*/
strcat(request," "); /*
char *strstr(const char *haystack, const char *needle)
在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 '\0'。
该函数返回在 haystack 中第一次出现 needle 的地址,如果未找到则返回 null。
*/
if(NULL==strstr(url,"://"))
{
fprintf(stderr, "\n%s: is not a valid URL.\n",url);
exit();
} /*
strlen(char *);
检测字符串实际长度。
strlen(char *)检测的是'\0',strlen(char *)碰到'\0'就返回'\0'以前的字符数(不包括'\0')。
strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个'\0',
如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到'\0'停止。
*/
if(strlen(url)>)
{
fprintf(stderr,"URL is too long.\n");
exit();
}
if(proxyhost==NULL)
/*
int strncasecmp(const char *s1, const char *s2, size_t n);
strncasecmp()用来比较参数s1 和s2 字符串前n个字符,比较时会自动忽略大小写的差异。
若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值,s1 若小于s2 则返回小于0 的值。
*/
if (!=strncasecmp("http://",url,))
{
fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
exit();
}
/* protocol/host delimiter */
i=strstr(url,"://")-url+;
/* printf("%d\n",i); */ /*
char *strchr(const char *str, char c)
该函数返回在字符串 str 中第一次出现字符 c 的地址,如果未找到该字符则返回 NULL。
*/
if(strchr(url+i,'/')==NULL) {
fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
exit();
}
if(proxyhost==NULL)
{
/* get port from hostname */
if(index(url+i,':')!=NULL &&
index(url+i,':')<index(url+i,'/'))
{
/*
char * strncpy(char *s1,char *s2,size_t n);
   将字符串s2中最多n个字符复制到字符数组s1中,返回指向s1的指针。
   注意:如果源串长度大于n,则strncpy不复制最后的'\0'结束符,
所以是不安全的,复制完后需要手动添加字符串的结束符才行。
*/
strncpy(host,url+i,strchr(url+i,':')-url-i);
bzero(tmp,);
strncpy(tmp,index(url+i,':')+,strchr(url+i,'/')-index(url+i,':')-);
/* printf("tmp=%s\n",tmp); */ /*
C语言库函数名: atoi
   功 能: 把字符串转换成整型数.
   名字来源:array to integer 的缩写.
   函数说明: atoi()会扫描参数nptr字符串,如果第一个字符不是数字也不是正负号返回零,
否则开始做类型转换,之后检测到非数字或结束符 \0 时停止转换,返回整型数。
   原型: int atoi(const char *nptr);
*/
proxyport=atoi(tmp);
if(proxyport==) proxyport=;
} else
{
/*
size_t strcspn(const char *s, const char * reject);
函数说明:strcspn()从参数s 字符串的开头计算连续的字符,
而这些字符都完全不在参数reject 所指的字符串中。
简单地说, 若strcspn()返回的数值为n,则代表字符串s 开头连续有n 个字符都不含字符串reject 内的字符。
返回值:返回字符串s 开头连续不含字符串reject 内的字符数目。
*/
strncpy(host,url+i,strcspn(url+i,"/"));
}
// printf("Host=%s\n",host);
strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
} else
{
// printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
strcat(request,url);
}
if(http10==)
strcat(request," HTTP/1.0");
else if (http10==)
strcat(request," HTTP/1.1");
strcat(request,"\r\n");
if(http10>)
strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
if(proxyhost==NULL && http10>)
{
strcat(request,"Host: ");
strcat(request,host);
strcat(request,"\r\n");
}
if(force_reload && proxyhost!=NULL)
{
strcat(request,"Pragma: no-cache\r\n");
}
if(http10>)
strcat(request,"Connection: close\r\n");
/* add empty line at end */
if(http10>) strcat(request,"\r\n");
// printf("Req=%s\n",request);
} /**
*先进行了一次socket连接,确认能连通以后,才进行后续步骤。
*调用pipe函数初始化一个管道,用于子进行向父进程汇报测试数据。
*子进程根据clients数量fork出来。
*每个子进程都调用函数5进行测试,并将结果输出到管道,供父进程读取。
*父进程负责收集所有子进程的测试数据,并汇总输出。
*/
/* vraci system rc error kod */
static int bench(void)
{
int i,j,k;
pid_t pid=;
FILE *f; /* check avaibility of target server */
//检测目标服务器的可用性,调用socket.c文件中的函数
i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
if(i<) {//处理错误
fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
return ;
}
close(i);
/* create pipe */
if(pipe(mypipe))//管道用于子进程向父进程回报数据
{//错误处理
perror("pipe failed.");
return ;
} /* not needed, since we have alarm() in childrens */
/* wait 4 next system clock tick */
/*
cas=time(NULL);
while(time(NULL)==cas)
sched_yield();
*/ /* fork childs */
for(i=;i<clients;i++)//根据clients大小fork出来足够的子进程进行测试
{
pid=fork();
if(pid <= (pid_t) )
{
/* child process or error*/
sleep(); /* make childs faster */
break;
}
} if( pid< (pid_t) )
{//错误处理
fprintf(stderr,"problems forking worker no. %d\n",i);
perror("fork failed.");
return ;
} if(pid== (pid_t) )//若是子进程,调用benchcore进行测试
{
/* I am a child */
if(proxyhost==NULL)
benchcore(host,proxyport,request);
else
benchcore(proxyhost,proxyport,request); /* write results to pipe */
f=fdopen(mypipe[],"w");//子进程将测试结果输出到管道
if(f==NULL)
{//错误处理
perror("open pipe for writing failed.");
return ;
}
/* fprintf(stderr,"Child - %d %d\n",speed,failed); */
fprintf(f,"%d %d %d\n",speed,failed,bytes);
fclose(f);
return ;
} else
{//若是父进程,则从管道读取子进程输出,并做汇总
f=fdopen(mypipe[],"r");
if(f==NULL)
{//错误处理
perror("open pipe for reading failed.");
return ;
}
setvbuf(f,NULL,_IONBF,);
speed=;
failed=;
bytes=; while()
{//从管道读取数据,fscanf为阻塞式函数
pid=fscanf(f,"%d %d %d",&i,&j,&k);
if(pid<)
{//错误处理
fprintf(stderr,"Some of our childrens died.\n");
break;
}
speed+=i;
failed+=j;
bytes+=k;
/* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
if(--clients==) break;//这句用于记录已经读了多少个子进程的数据,读完就退出
}
fclose(f);
//最后将结果打印到屏幕上
printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
(int)((speed+failed)/(benchtime/60.0f)),
(int)(bytes/(float)benchtime),
speed,
failed);
}
return i;
} /**
*benchcore是子进程进行压力测试的函数,被每个子进程调用。
*这里使用了SIGALRM信号来控制时间,
*alarm函数设置了多少时间之后产生SIGALRM信号,一旦产生此信号,将运行alarm_handler(),
*使得timerexpired=1,这样可以通过判断timerexpired值来退出程序。
*另外,全局变量force表示我们是否在发出请求后需要等待服务器的响应结果
*/
void benchcore(const char *host,const int port,const char *req)
{
int rlen;
char buf[];//记录服务器响应请求所返回的数据
int s,i;
struct sigaction sa; /* setup alarm signal handler */
sa.sa_handler=alarm_handler;//将函数alarm_handler地址赋值给sa.alarm_handler,作为信号处理函数
sa.sa_flags=;
if(sigaction(SIGALRM,&sa,NULL))//超时会产生信号SIGALRM,用sa中的指定函数处理
exit();
alarm(benchtime);//开始计时 rlen=strlen(req);
nexttry:while()
{
if(timerexpired)//一旦超时则返回
{
if(failed>)
{
/* fprintf(stderr,"Correcting failed by signal\n"); */
failed--;
}
return;
}
s=Socket(host,port);//调用socket建立TCP连接
if(s<) { failed++;continue;}
if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}//发出请求
if(http10==) //针对http0.9做的特殊处理
if(shutdown(s,)) { failed++;close(s);continue;}
if(force==) //全局变量force表示是否要等待服务器返回的数据
{
/* read all available data from socket */
while()
{
if(timerexpired) break;
i=read(s,buf,);//从socket读取返回数据
/* fprintf(stderr,"%d\n",i); */
if(i<)
{
failed++;
close(s);
goto nexttry;
}
else
if(i==) break;
else
bytes+=i;
}
}
if(close(s)) {failed++;continue;}
speed++;
}
}

webbench网站测压工具源码分析的更多相关文章

  1. ab webbench 网站测压解决

    ab 网站测压解决 ab –c 100 –n 100 http://192.168.1.117/forum.php (测试方式) 同时发100人发100个请求 ab –c 100 –n 1000 ht ...

  2. bootstrap_栅格系统_响应式工具_源码分析

    -----------------------------------------------------------------------------margin 为负 ​使盒子重叠 ​等高 等高 ...

  3. [软件测试]网站压测工具Webbench源码分析

    一.我与webbench二三事 Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能.Webbench ...

  4. 网站(Web)压测工具Webbench源码分析

    一.我与webbench二三事 Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能.Webbench ...

  5. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  6. 多渠道打包工具Walle源码分析

    一.背景 首先了解多渠道打包工具Walle之前,我们需要先明确一个概念,什么是渠道包. 我们要知道在国内有无数大大小小的APP Store,每一个APP Store就是一个渠道.当我们把APP上传到A ...

  7. 开源网站流量统计系统Piwik源码分析——参数统计(一)

    Piwik现已改名为Matomo,这是一套国外著名的开源网站统计系统,类似于百度统计.Google Analytics等系统.最大的区别就是可以看到其中的源码,这正合我意.因为我一直对统计的系统很好奇 ...

  8. WebBench源码分析

    源码分析共享地址:https://github.com/fivezh/WebBench 下载源码后编译源程序后即可执行: sudo make clean sudo make & make in ...

  9. Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第1节: FastThreadLocal的使用和创建

    Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 概述: FastThreadLocal我们在剖析堆外内存分配的时候简单介绍过, 它类似于JDK的ThreadL ...

随机推荐

  1. ESA2GJK1DH1K微信小程序篇: 测试微信小程序APUConfig给WI-Fi模块配网并绑定设备,并通过MQTT控制设备

    前言(源码使用介绍在最后) 一,微信小程序篇小程序下载(该源码为这节测试源代码) 二.有多少人一直在期盼着小程序可以实现SmartConfig或者Airkiss的功能? 来吧!我的这种方式包您满意. ...

  2. css实现块级元素水平垂直居中的方法?

    父级给相对定位,子级给绝对定位,margin设置为auto,上下左右值设为0. 父级给相对定位,子级给绝对定位,设置left和top为50%,再向左和向上移动负的子级一半. 父级设置display:f ...

  3. 中心极限定理(Central Limit Theorem)

    中心极限定理:每次从总体中抽取容量为n的简单随机样本,这样抽取很多次后,如果样本容量很大,样本均值的抽样分布近似服从正态分布(期望为  ,标准差为 ). (注:总体数据需独立同分布) 那么样本容量n应 ...

  4. Linux 和 windows下查看运行命令的位置

    经常遇到要查看某个命令的运行文件在哪儿! 比如说vue cli,经常使用vue命令创建项目,如果你对nodejs的全局包安装目录了解可能一下就找到了, 蛋疼的是不一定每个命令都是nodejs下的,有可 ...

  5. ERA-Interim数据学习

    1.气象再分析数据有很多种,看文献里用到的主要有这几种 ECWRF——ERA-Interim,分辨率0.125°,欧洲的 MERRA-2,分辨率0.625°*0.5°,NASA的 GEOS-5FP,分 ...

  6. ArrayMap和HashMap区别

    什么是Map? Map的三个特点 1.包含键值对 2.键唯一 3.键对应的值唯一 一:hash 什么是Hash Hash,也可以称为“散列”,就是把任意长度的输入,通过散列算法,变换成固定长度的输出, ...

  7. Hawq架构

    Hawq采用分层架构,将MPP shared-nothing的计算层架在HDFS之上. Hawq集群中有三种角色:master, namenode和segment hosts. 1.Master负责认 ...

  8. 用Python画一颗特别的心送给她

    import numpy as np import matplotlib.pyplot as plt x_coords = np.linspace(-100, 100, 500) y_coords = ...

  9. Java手机号隐藏中间4位和邮箱隐藏,身份证隐藏

    1.Java代码中隐藏 //隐藏手机号码中间四位 String phoneNumber = "15567893456"; String resultPhone= phoneNumb ...

  10. Three.js 快速上手以及在 React 中运用[转]

    https://juejin.im/post/5ca22692f265da30a53d6656 github 的地址 欢迎 star! 之前项目中用到了 3D 模型演示的问题,整理了一下之前学习总结以 ...