pomelo-robot 使用指南

pomelo-robot 是用于 pomelo 框架的性能测试工具。也可以用于其它同类应用的测试。

安装

通过命令:

npm install pomelo-robot

安装需要有 visual studio 2010 安装(vc 2010 也可以),总之能编译 pomelo 的环境是绝对可以的。

使用

官方的 wiki 中提供基本的写法参考,地址见 pomelo-robot 使用文档
同时官方有提供一个使用的 demo,不过代码写的不是很好看。地址见: lord of pomelo 测试 demo
一些能够从官方渠道获取的细节,博主就不赘述了。博主简单介绍下使用形式。
通过这个 demo 我们可以了解到 pomelo-robot 的基本用法。首先是两种执行方式,一种是 master 一种是 client,通过:

node app [master]

可以搭建一个测试的服务器,该服务端主体是一个 web 网站,用于汇总、统计、计算数据并生成走势图像。其次是通过:

node app client

来开启一个客户端。客户端用于运行测试脚本,可以重复多个并且可以布置在多台机器上。其架构如图:
pomelo-robot 架构

需要介绍的地方就是关于图形数据的生成的一个比较重要的方法。在 lord 的测试 demo 脚本(脚本地址)中有写到:

 // 初始化socketClient
 pomelo.init({
 	host: host,
 	port: port,
 	log: true
 }, function() {
 	// monitor(START, 'entry', ActFlagType.ENTRY);
 	pomelo.request('connector.entryHandler.entry', {
 		token: token
 	}, function(data) {
 		// monitor(END, 'entry', ActFlagType.ENTRY);
 		if (callback) {
 			callback(data.code);
 		}
 		if (data.code == 1001) {
 			console.log('Login fail!');
 			return;
 		} else if (data.code == 1003) {
 			console.log('Username not exists!');
 			return;
 		}
 		if (data.code != 200) {
 			console.log('Login Fail!');
 			return;
 		}
 		afterLogin(pomelo, data);
 	});
 });

其中的被注释的 monitor 函数就是用来统计数据生成图像的方法。该函数的定义位于第415行

var monitor = function(type, name, reqId) {
	if (typeof actor !== 'undefined') {
		actor.emit(type, name, reqId);
	} else {
		console.error(Array.prototype.slice.call(arguments, 0));
	}
}

可以看到这个 monitor 函数其实是 pomelo-robot 提供的 actor 对象的一个封装。作用是对一个事件进行计时,随后把数据发送到 pomelo-robot 的 master 服务器。其所提供的事件一共有4中分别是: start、end、incr、decr, 其中 start 和 end 是成对的, 类似 console.time 和 console.timeEnd, 后面的 incr 和 decr 分别是累计增加和累计减少. 函数调用的写法参见其 demo, 这里就不赘述了.

配置

在官方 wiki 中有提到配置项, 在 lord 的 demo 中也有现成的配置文件,不过其中之配置了 master 字段, 除了 master 之外其他还可以配置的有:

{
  // robot 服务器地址, socket 端口 和 http 端口 
  "master":{"host": "192.168.1.30","port":8888,"webport":8889},
  // 要测试的游戏 ip 地址和端口
  "apps":[{"host":"210.14.xxx.xxx","port":3050}],
  // 指定客户端地址 (其实客户端可以自动指定分配)
  "clients":["127.0.0.1"]
}

数据

关于上述 lord 的 性能测试 demo 官方还有给出一个测试报告 (报告地址), 各位可以去参考一下他们的数据统计和分析:
测试数据统计
其中的 Per client Agents 是你当前链接服务端的客户端数目, 随后的 Per Agent Users 是每个 client 所代理的用户数目 (也可以简单看做一个客户端执行几次, 不过要等到所有都执行结束才会断线), 在 Current Agents 后面列举的 ip 就是当前链接上来的客户端:
统计数据摘要
随后的摘要 (summary) 中简单介绍几个单词的意思:
Max, Min 统计操作所消耗的最大时间和最小时间, 单位 ms
Avg 请求平均消耗时间, 单位 ms
Qps 每秒查询率, 是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准
Count 请求执行次数

其他

关于如何使用 pomelo-robot 来调试其他同类程序, 大家观察 官方给的 demo 脚本地址 就可以发现其中前半部分大部分都是 pomelo 客户端请求所用的方法, 考虑把这些方法换成你自己的客户端连接方法即可, 后方统计没有太大变化.

node.js options 模块

为了避免每次都检查参数选项,然后设置默认参数,你可以使用 options 模块来一次解决问题。通过 npm 简单安装:

npm install options

使用:

var Options = require('options')

function test(opt) {
	opt = new Options({
		site: "default site",
		type: "default type",
		data: "defatul data"
	}).merge(opt);

	console.log(opt.value.site);
	console.log(opt.value.type);
	console.log(opt.value.data);
}

test({
	site: "www.lellansin.com",
	type: "blog"
});

输出:

www.lellansin.com
blog
defatul data

node.js event 与 bind 事件转发

event 与 bind

var util = require('util');
var EventEmitter = require('events').EventEmitter; 

var event = new EventEmitter(); 

function MyClass() {

}

util.inherits(MyClass, EventEmitter);

MyClass.prototype.myEventFunc = function() {
	console.log('自定义事件 my_event 被触发.'); 
};

var obj = new MyClass();

obj.on('my_event', obj.myEventFunc.bind(this)); 

setTimeout(function() { 
	obj.emit('my_event'); 
}, 1000);

执行结果, 一秒钟之后输出:

自定义事件 my_event 被触发.

事件转发

var util = require('util');
var EventEmitter = require('events').EventEmitter; 

var event = new EventEmitter(); 

function MyClassOne() {

}

util.inherits(MyClassOne, EventEmitter);

MyClassOne.prototype.myFirstEventFunc = function() {
	console.log('自定义事件 first_event 被触发.'); 
};

function MyClassTwo() {

}

util.inherits(MyClassTwo, EventEmitter);

var one = new MyClassOne();
var two = new MyClassTwo();

// 将 ClassOne 的 first_event 事件转发给 ClassTwo 的 second_event 触发
one.on('first_event', two.emit.bind(two, 'second_event'));

two.on('second_event', function() {
	console.log('自定义事件 second_event 被触发.'); 
}); 

setTimeout(function() { 
	one.emit('first_event'); 
}, 1000);

执行结果, 一秒钟之后输出:

自定义事件 second_event 被触发.

使用 bind 指定事件触发函数以及事件转发的应用实例,可以参见 Pomelo 中的 connector 操作,应用代码具体:

// ...
this.server.on('connection', this.newSocket.bind(this)); // 指定 socket 服务器连接事件的回调函数为 this.newSocket
this.wsprocessor.on('connection', this.emit.bind(this, 'connection')); // 将 websocket 处理器的连接事件转发给当前类的 connection 事件
this.tcpprocessor.on('connection', this.emit.bind(this, 'connection')); // 将 tcp 处理器的连接事件转发给当前类的 connection 事件
// ...

node.js 获取本机ip

var os = require('os');

function getLocalIps(flagIpv6) {
    var ifaces = os.networkInterfaces();
    var ips = [];
    var func = function(details) {
        if (!flagIpv6 && details.family === 'IPv6') {
            return;
        }
        ips.push(details.address);
    };
    for (var dev in ifaces) {
        ifaces[dev].forEach(func);
    }
    return ips;
};

console.log('本机ip地址(不包括Ipv6):', getLocalIps());
console.log('本机ip地址(包括Ipv6):', getLocalIps(true));

查看进程 socket 连接的简单办法

起因是想知道一款游戏连接的服务器地址。虽然可以考虑用抓包工具之类的做到,但是感觉有点麻烦,要说监控的话还是挺难实现的。不过思考之后,发现可以用系统自带的 netstat 命令来做。比如想知道一个游戏连接的服务器地址是多少,可以先找到这个游戏的进程 pid,然后通过:

netstat -ano | findstr [pid]

来过滤查找。

简单写了个 C语言的程序调用这个 cmd 命令:

#include <windows.h>
#include <stdio.h>

void help( );

int main(int argc, char const *argv[])
{
	INT i;
	HWND hWnd;
	CHAR command[256];
	DWORD dwProcessId;

	if( argc == 1)
	{
		help();
		return;
	}

	if (argc == 3)
	{
		if ( strcmp(argv[1], "/p" ) == 0 )
		{
			wsprintf(command, "netstat -ano | findstr %s", argv[2]);
			system(command);
		}

		if ( strcmp(argv[1], "/w" ) == 0 )
		{
			hWnd = FindWindow(NULL, argv[2]);
			if (hWnd <= 0)
			{
				printf("Can't find process by window name %s", argv[2]);
				return 0;
			}
			GetWindowThreadProcessId(hWnd, &dwProcessId);
			if (dwProcessId > (DWORD)0)
			{
				printf("Pid: %d n", dwProcessId);
				wsprintf(command, "netstat -ano | findstr %d", dwProcessId);
				printf("  协议   本地地址              外部地址        状态           PIDn");
				system(command);
			} else {
				printf("Can't find process by window name %s", argv[2]);
			}
		}
	}
	return 0;
}

void help()
{
	printf("getport [options] [param] n");
	printf("/p 通过 pid 查找 n");
	printf("/w 通过窗口名称查找 n");
}

获取程序的 socket 连接

就这样简单的拿到了一个程序连接的 socket 信息(包括 ip 和 端口)。

当然并不是所有程序都能通过这个拿到,有的程序可能是通过子进程来发的 socket。有的程序(比如QQ)会通过服务器中转,所以发送文件的时候只会拿到腾讯中转服务器的 ip(不过早期的QQ确实是可以通过拿到聊天对象的 ip)。

php 递归遍历数组

复习PHP中, 简单写下, 练练手。

<?php

/*
*  -------------------------------------------------
*   Author : lellansin
*   Url    : www.lellansin.net
*   Date   : 2014-08-13
*  -------------------------------------------------
*/
function deep_foreach ($arr, $k='', $pre_indent = '') {
	if (!is_array ($arr)) {
		return false;
	}

	$str = $k ? "[$k] => " : '';
	$cur_indent = $pre_indent . "    ";

	echo $pre_indent.$str."Array<br/>$pre_indent(<br/>";

	foreach ($arr as $key => $val ) {
		if (is_array ($val)) {
			deep_foreach ($val, $key, $cur_indent);
		} else {
			echo $cur_indent."[$key] = > ".$val.'<br/>';
		}
	}

	echo $pre_indent.")<br/>";
}

$arr1 = array(array(2,2,8,4=>array(array(5,6,7,8),1)),5,array(5,6,8),3,4);

echo '<pre>';
deep_foreach ($arr1);
echo '</pre>';

输出:

Array
(
    Array
    (
        [0] = > 2
        [1] = > 2
        [2] = > 8
        [4] => Array
        (
            Array
            (
                [0] = > 5
                [1] = > 6
                [2] = > 7
                [3] = > 8
            )
            [1] = > 1
        )
    )
    [1] = > 5
    [2] => Array
    (
        [0] = > 5
        [1] = > 6
        [2] = > 8
    )
    [3] = > 3
    [4] = > 4
)