@ -1,54 +0,0 @@ | |||
%%%--------------------------------------------- | |||
%%% @module : cast_and_call | |||
%%% @Author : csj | |||
%%% @Created : 2010.10.05 | |||
%%% @description: gen_server cast and call 测试 | |||
%%% @result:cast 要比call 快0.006毫秒 | |||
%%%--------------------------------------------- | |||
-module(cast_and_call). | |||
-behaviour(gen_server). | |||
%%Interface functions. | |||
-export([start/0, test/0]). | |||
%%gen_server callbacks | |||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | |||
start() -> | |||
gen_server:start(?MODULE, [], []). | |||
init([]) -> | |||
{ok, 1}. | |||
handle_cast('TEST', Status) -> | |||
%%io:format("This is cast. ~n"), | |||
{noreply, Status}. | |||
handle_call('TEST', _FROM, Status) -> | |||
%%io:format("This is call. ~n"), | |||
{reply, ok, Status}. | |||
handle_info(_Info, Status) -> | |||
{noreply, Status}. | |||
terminate(normal, Status) -> | |||
{ok, Status}. | |||
code_change(_OldVsn, Status, _Extra) -> | |||
{ok, Status}. | |||
test() -> | |||
{ok, Pid} = start(), | |||
fprof:apply(gen_server, call, [Pid, 'TEST']), | |||
fprof:profile(), | |||
fprof:analyse({dest, "call.analysis"}), | |||
fprof:apply(gen_server, cast, [Pid, 'TEST']), | |||
fprof:profile(), | |||
fprof:analyse({dest, "cast.analysis"}), | |||
F1 = fun() -> gen_server:call(Pid, 'TEST') end, | |||
F2 = fun() -> gen_server:cast(Pid, 'TEST') end, | |||
prof:run(F1, 100000), | |||
prof:run(F2, 100000). | |||
@ -1,34 +0,0 @@ | |||
%%%--------------------------------------------- | |||
%%% @Module : loop | |||
%%% @Author : csj | |||
%%% @Created : 2010.10.05 | |||
%%% @Description: 测试循环性能 | |||
%%% @Resule : F2 比 F1 快20毫秒 不到10000基本可以忽略不计 | |||
%%%--------------------------------------------- | |||
-module(loop). | |||
-compile(export_all). | |||
do_loop([], _Data) -> ok; | |||
do_loop([[_S] | T], Data) -> | |||
do_loop(T, Data). | |||
do_loop2([], _Data, _X) -> ok; | |||
do_loop2([[S] | T], Data, X) when S == X -> | |||
do_loop2(T, Data, X); | |||
do_loop2([[_S] | T], Data, X) -> | |||
do_loop2(T, Data, X). | |||
test() -> | |||
F1 = fun() -> | |||
L = [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], | |||
do_loop2(L, <<16:16>>, [8]) | |||
end, | |||
F2 = fun() -> | |||
L = [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], | |||
do_loop(lists:delete([8], L), <<16:16>>) | |||
end, | |||
prof:run(F1, 100000), | |||
prof:run(F2, 100000). |
@ -1,390 +0,0 @@ | |||
-module(misc). | |||
%% | |||
%% Exported Functions | |||
%% | |||
-export([ | |||
whereis_name/1, | |||
register/3, | |||
unregister/2, | |||
is_process_alive/1, | |||
% t/1, | |||
% t/2, | |||
% tr/1, | |||
% tun/1, | |||
% tan/1, | |||
% t_clear/0, | |||
dump_process_info/1 | |||
, ets_mem/0 | |||
, tcp_links/0 | |||
, top_back/0 | |||
, top/0 | |||
, request/1 | |||
]). | |||
-export([ | |||
check_mem/1 | |||
]). | |||
-export([ | |||
pstack/1 | |||
, etop/0 | |||
, etop_mem/0 | |||
, etop_stop/0 | |||
, gc_all/0 | |||
, fprof/3 | |||
, eprof_all/1 | |||
, eprof/2 | |||
, scheduler_usage/0 | |||
, scheduler_usage/1 | |||
, scheduler_stat/0 | |||
, scheduler_stat/1 | |||
, trace/1 | |||
, trace/2 | |||
, trace_stop/0 | |||
, crash_dump/0 | |||
, process_infos/0 | |||
]). | |||
%% @doc 节点所有进程信息 | |||
process_infos() -> | |||
filelib:ensure_dir("./logs/"), | |||
File = "./logs/processes_infos.log", | |||
{ok, Fd} = file:open(File, [write, raw, binary, append]), | |||
Fun = fun(Pi) -> | |||
Info = io_lib:format("=>~p \n\n", [Pi]), | |||
case filelib:is_file(File) of | |||
true -> file:write(Fd, Info); | |||
false -> | |||
file:close(Fd), | |||
{ok, NewFd} = file:open(File, [write, raw, binary, append]), | |||
file:write(NewFd, Info) | |||
end, | |||
timer:sleep(20) | |||
end, | |||
[Fun(erlang:process_info(P)) || P <- erlang:processes()]. | |||
%% @doc erlang_dump | |||
crash_dump() -> | |||
Date = erlang:list_to_binary(rfc1123_local_date()), | |||
Header = binary:list_to_bin([<<"=erl_crash_dump:0.2\n">>, Date, <<"\nSystem version: ">>]), | |||
Ets = ets_info(), | |||
Report = binary:list_to_bin([Header, erlang:list_to_binary(erlang:system_info(system_version)), | |||
erlang:system_info(info), erlang:system_info(procs), Ets, erlang:system_info(dist), | |||
<<"=loaded_modules\n">>, binary:replace(erlang:system_info(loaded), | |||
<<"\n">>, <<"\n=mod:">>, [global])]), | |||
file:write_file("erl_crash.dump", Report). | |||
ets_info() -> | |||
binary:list_to_bin([ets_table_info(T) || T <- ets:all()]). | |||
ets_table_info(Table) -> | |||
Info = ets:info(Table), | |||
Owner = erlang:list_to_binary(erlang:pid_to_list(proplists:get_value(owner, Info))), | |||
TableN = erlang:list_to_binary(erlang:atom_to_list(proplists:get_value(name, Info))), | |||
Name = erlang:list_to_binary(erlang:atom_to_list(proplists:get_value(name, Info))), | |||
Objects = erlang:list_to_binary(erlang:integer_to_list(proplists:get_value(size, Info))), | |||
binary:list_to_bin([<<"=ets:">>, Owner, <<"\nTable: ">>, TableN, <<"\nName: ">>, Name, | |||
<<"\nObjects: ">>, Objects, <<"\n">>]). | |||
rfc1123_local_date() -> | |||
rfc1123_local_date(os:timestamp()). | |||
rfc1123_local_date({A, B, C}) -> | |||
rfc1123_local_date(calendar:now_to_local_time({A, B, C})); | |||
rfc1123_local_date({{YYYY, MM, DD}, {Hour, Min, Sec}}) -> | |||
DayNumber = calendar:day_of_the_week({YYYY, MM, DD}), | |||
lists:flatten( | |||
io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT", | |||
[httpd_util:day(DayNumber), DD, httpd_util:month(MM), YYYY, Hour, Min, Sec])); | |||
rfc1123_local_date(Epoch) when erlang:is_integer(Epoch) -> | |||
rfc1123_local_date(calendar:gregorian_seconds_to_datetime(Epoch + 62167219200)). | |||
%trace Mod 所有方法的调用 | |||
trace(Mod) -> | |||
dbg:tracer(), | |||
dbg:tpl(Mod, '_', []), | |||
dbg:p(all, c). | |||
%trace Node上指定 Mod 所有方法的调用, 结果将输出到本地shell | |||
trace(Node, Mod) -> | |||
dbg:tracer(), | |||
dbg:n(Node), | |||
dbg:tpl(Mod, '_', []), | |||
dbg:p(all, c). | |||
%停止trace | |||
trace_stop() -> | |||
dbg:stop_clear(). | |||
% 统计下1s内调度进程数量(含义:第一个数字执行进程数量,第二个数字迁移进程数量) | |||
scheduler_stat() -> | |||
scheduler_stat(1000). | |||
scheduler_stat(RunMs) -> | |||
erlang:system_flag(scheduling_statistics, enable), | |||
Ts0 = erlang:system_info(total_scheduling_statistics), | |||
timer:sleep(RunMs), | |||
Ts1 = erlang:system_info(total_scheduling_statistics), | |||
erlang:system_flag(scheduling_statistics, disable), | |||
lists:map(fun({{Key, In0, Out0}, {Key, In1, Out1}}) -> | |||
{Key, In1 - In0, Out1 - Out0} end, lists:zip(Ts0, Ts1)). | |||
% 统计下1s每个调度器CPU的实际利用率(因为有spin wait、调度工作, 可能usage 比top显示低很多) | |||
scheduler_usage() -> | |||
scheduler_usage(1000). | |||
scheduler_usage(RunMs) -> | |||
erlang:system_flag(scheduler_wall_time, true), | |||
Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), | |||
timer:sleep(RunMs), | |||
Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), | |||
erlang:system_flag(scheduler_wall_time, false), | |||
Cores = lists:map(fun({{I, A0, T0}, {I, A1, T1}}) -> | |||
{I, (A1 - A0) / (T1 - T0)} end, lists:zip(Ts0, Ts1)), | |||
{A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai, Ti}) -> | |||
{Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0, Ts1)), | |||
Total = A / T, | |||
io:format("~p~n", [[{total, Total} | Cores]]). | |||
% 对整个节点内所有进程执行eprof, eprof 对线上业务有一定影响,慎用! | |||
% 建议TimeoutSec<10s,且进程数< 1000,否则可能导致节点crash | |||
% 结果: | |||
% 输出每个方法实际执行时间(不会累计方法内其他mod调用执行时间) | |||
% 只能得到mod - Fun 执行次数 执行耗时 | |||
eprof_all(TimeoutSec) -> | |||
eprof(processes() -- [whereis(eprof)], TimeoutSec). | |||
eprof(Pids, TimeoutSec) -> | |||
eprof:start(), | |||
eprof:start_profiling(Pids), | |||
timer:sleep(TimeoutSec), | |||
eprof:stop_profiling(), | |||
eprof:analyze(total), | |||
eprof:stop(). | |||
% @doc 对MFA 执行分析,会严重减缓运行,建议只对小量业务执行 | |||
% 结果: | |||
% fprof 结果比较详细,能够输出热点调用路径 | |||
fprof(M, F, A) -> | |||
fprof:start(), | |||
fprof:apply(M, F, A), | |||
fprof:profile(), | |||
fprof:analyse([{dest, "fprof.analysis"}, {sort, own}]), | |||
fprof:stop(). | |||
% @doc 对所有process做gc | |||
gc_all() -> | |||
[erlang:garbage_collect(Pid) || Pid <- processes()]. | |||
% @doc 进程CPU占用排名 | |||
etop() -> | |||
spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, runtime}]) end). | |||
% @doc 进程Mem占用排名 | |||
etop_mem() -> | |||
spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, memory}]) end). | |||
% @doc 停止etop | |||
etop_stop() -> | |||
etop:stop(). | |||
%% @doc 类似于jstack,发现大量进程挂起,进程数过高,运行慢,hang住等问题用到 | |||
pstack(Reg) when is_atom(Reg) -> | |||
case whereis(Reg) of | |||
undefined -> undefined; | |||
Pid -> pstack(Pid) | |||
end; | |||
pstack(Pid) -> | |||
io:format("~s~n", [element(2, process_info(Pid, backtrace))]). | |||
%% 检查溢出的内存,强制gc, 并写入日志分析 | |||
check_mem(MemLim) -> | |||
lists:foreach( | |||
fun(P) -> | |||
case is_pid(P) andalso erlang:is_process_alive(P) of | |||
true -> | |||
{memory, Mem} = erlang:process_info(P, memory), | |||
case Mem > MemLim of | |||
true -> | |||
erlang:garbage_collect(P); | |||
false -> | |||
[] | |||
end; | |||
false -> | |||
[] | |||
end | |||
end, erlang:processes()). | |||
%% @doc 寻找PID | |||
whereis_name({local, Atom}) -> | |||
erlang:whereis(Atom); | |||
whereis_name({global, Atom}) -> | |||
global:whereis_name(Atom). | |||
register(local, Name, Pid) -> | |||
erlang:register(Name, Pid); | |||
register(global, Name, Pid) -> | |||
global:re_register_name(Name, Pid). | |||
unregister(local, Name) -> | |||
erlang:unregister(Name); | |||
unregister(global, Name) -> | |||
global:unregister_name(Name). | |||
is_process_alive(Pid) -> | |||
try | |||
case Pid of | |||
_ when is_pid(Pid) -> | |||
Node = node(), | |||
Result = case node(Pid) of | |||
Node -> erlang:is_process_alive(Pid); | |||
Node1 -> rpc:call(Node1, erlang, is_process_alive, [Pid]) | |||
end, | |||
case Result of | |||
{badrpc, _Reason} -> false; | |||
Res -> Res | |||
end; | |||
_ -> false | |||
end | |||
catch | |||
_:_ -> false | |||
end. | |||
%% @spec top() -> ok | |||
%% @doc 查看系统当前的综合信息 | |||
top() -> | |||
Release = erlang:system_info(otp_release), | |||
SchedNum = erlang:system_info(schedulers), | |||
ProcCount = erlang:system_info(process_count), | |||
ProcLimit = erlang:system_info(process_limit), | |||
ProcMemUsed = erlang:memory(processes_used), | |||
EtsMemAlc = erlang:memory(ets), | |||
MemTot = erlang:memory(total), | |||
%PetNum = all_pets(), | |||
io:format( | |||
"++++++++++++++++++++++++++++++++++++++++++~n" | |||
" Node: ~p~n" | |||
" Erlang Ver: ~p~n" | |||
" Free Threads: ~p~n" | |||
" Process Used Memory: ~pMb~n" | |||
" Ets Used Memory: ~pMb~n" | |||
" Erlang VM Used Memory: ~pMb~n" | |||
" Process Limit: ~p~n" | |||
" Process Used: ~p~n" | |||
"++++++++++++++++++++++++++++++++++++++++++~n" | |||
, [node(), Release, SchedNum, ProcMemUsed / 1024 / 1024, EtsMemAlc / 1024 / 1024, MemTot / 1024 / 1024, ProcLimit, ProcCount]), | |||
ok. | |||
%% @doc 运维要用 | |||
top_back() -> | |||
Release = erlang:system_info(otp_release), | |||
SchedNum = erlang:system_info(schedulers), | |||
ProcCount = erlang:system_info(process_count), | |||
ProcLimit = erlang:system_info(process_limit), | |||
ProcMemUsed = erlang:memory(processes_used), | |||
EtsMemAlc = erlang:memory(ets), | |||
MemTot = erlang:memory(total), | |||
Str = io_lib:format( | |||
" Erlang 版本: ~p~n" | |||
" 可使用的调度线程: ~p~n" | |||
" 所有进程使用的内存: ~pMb~n" | |||
" 所有ets使用的内存: ~pMb~n" | |||
" Erlang系统占用内存: ~pMb~n" | |||
" 可创建进程数量上限: ~p~n" | |||
" 当前进程数: ~p~n" | |||
, [Release, SchedNum, ProcMemUsed / 1024 / 1024, EtsMemAlc / 1024 / 1024, MemTot / 1024 / 1024, ProcLimit, ProcCount]), | |||
binary_to_list(list_to_binary(Str)). | |||
%% @spec ets_mem() -> term() | |||
%% @doc 查看内存占用最多的30张ets表 | |||
ets_mem() -> | |||
L = ets:all(), | |||
Mems = lists:map(fun(Tab) -> | |||
Info = ets:info(Tab), | |||
case lists:keyfind(memory, 1, Info) of | |||
{memory, Mem} -> {Tab, Mem}; | |||
_ -> {Tab, 0} | |||
end | |||
end, L), | |||
L1 = lists:sublist(lists:reverse(lists:keysort(2, Mems)), 30), | |||
io:format("~n--------------------------------------------------~n" | |||
"~-30w ~w~n--------------------------------------------------~n" | |||
, [table, used_memory]), | |||
lists:foreach( | |||
fun({Tab, Mem}) -> | |||
io:format("~-30w ~wKb~n", [Tab, Mem / 1024]) | |||
end, L1). | |||
%% @spec tcp_links() -> Info | |||
%% @doc 统计tcp链接 | |||
tcp_links() -> | |||
L = erlang:ports(), | |||
F = fun(P) -> | |||
Pinfo = erlang:port_info(P), | |||
case lists:keyfind(name, 1, Pinfo) of | |||
{name, "tcp_inet"} -> true; | |||
_ -> false | |||
end | |||
end, | |||
L1 = lists:filter(F, L), | |||
io:format("~n当前socket数量(包括链接数据库的socket): ~w~n", [length(L1)]). | |||
%% @doc 备份进程信息 | |||
dump_process_info(Pid) -> | |||
{{Year, Month, Day}, {Hour, Minutes, Second}} = util:local_time(), | |||
{ok, FileHandle} = file:open(util:fbin("~s-~w-~w-~w-~w-~w-~w", [<<"../logs/pid_info.dump">>, Year, Month, Day, Hour, Minutes, Second]), write), | |||
case erlang:process_info(Pid) of | |||
Info when is_list(Info) -> | |||
lists:foreach(fun({messages, Messages}) -> | |||
case Messages =:= [] of | |||
true -> | |||
io:format(FileHandle, "~w~n", [{messages, Messages}]); | |||
_ -> | |||
io:format(FileHandle, "{messages,~n", []), | |||
lists:foreach(fun(M) -> | |||
io:format(FileHandle, " ~w~n", [M]) | |||
end, Messages), | |||
io:format(FileHandle, "}~n", []) | |||
end; | |||
({dictionary, Dics}) -> | |||
case Dics =:= [] of | |||
true -> | |||
io:format(FileHandle, "~w~n", [{dictionary, Dics}]); | |||
_ -> | |||
io:format(FileHandle, "{dictionary,~n", []), | |||
lists:foreach(fun(M) -> | |||
io:format(FileHandle, " ~w~n", [M]) | |||
end, Dics), | |||
io:format(FileHandle, "}~n", []) | |||
end; | |||
(E) -> | |||
io:format(FileHandle, "~w~n", [E]) | |||
end, Info); | |||
_ -> | |||
io:format("not find pid info") | |||
end, | |||
file:close(FileHandle). | |||
request(Url) -> | |||
{ok, RequestId} = httpc:request(get, {Url, []}, [], [{sync, false}]), | |||
receive | |||
{http, {RequestId, {{_Version, 200, _ReasonPhrase}, _Headers, Body}}} -> | |||
Body; | |||
E -> | |||
E | |||
after 500 -> | |||
timeout | |||
end. |
@ -1,355 +0,0 @@ | |||
-module(misc_admin). | |||
%%返回码 | |||
-define(PARAM_ERROR_CODE, <<"param_error">>). % 参数错误 | |||
-define(FLAG_ERROR_CODE, <<"flag_error">>). % 验证失败 | |||
-define(FAILED_CODE, <<"failed">>). % 发送消息失败,服务器异常 | |||
-define(SUCCESS_CODE, <<"success">>). % 成功 | |||
-define(INFO_MSG, io:format). | |||
-define(TRACE, io:format). | |||
-compile(export_all). | |||
%% 消息广播 | |||
do_handle_request("send_sys_bulletin", KvList, Socket) -> | |||
MsgType = list_to_integer(http_util:get_param("msg_type", KvList)), | |||
Content = http_util:get_param("content", KvList), | |||
cast_to_server(lib_chat, broadcast_sys_msg, [MsgType, Content]), | |||
%% lib_chat:broadcast_sys_msg("test"), | |||
?INFO_MSG("type:~p content:~ts ~n", [MsgType, Content]), | |||
gen_tcp:send(Socket, <<"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nhello world!">>); | |||
% gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 发送邮件 | |||
do_handle_request("send_mail", KvList, Socket) -> | |||
ok; | |||
%% Action = http_util:get_param("action", KvList), | |||
%% UserNames = http_util:get_param("user_names", KvList), | |||
%% UserIds = http_util:get_param("user_ids", KvList), | |||
%% MailTitle = http_util:get_param("mail_title", KvList), | |||
%% MailConten = http_util:get_param("mail_content", KvList), | |||
%% UserNameList = string:tokens(UserNames, ","), | |||
%% UserIdList = string:tokens(UserIds, ","), | |||
%% if | |||
%% Action =:= 1 -> % 只对参数 user_names 和 user_ids 指定的用户发送 | |||
%% cast_to_server(lib_mail, broadcast_sys_msg, [Action, UserNameList, UserIdList, MailTitle, MailConten]); | |||
%% Action =:= 2 -> % 只对符合"条件参数"的所有用户发送,“条件参数”包括下列条件 | |||
%% cast_to_server(lib_mail, broadcast_sys_msg, [MsgType, Content]); | |||
%% Action =:= 3 -> % 只对当前在线玩家发送 | |||
%% cast_to_server(lib_mail, broadcast_sys_msg, [Action, MailTitle, MailConten]); | |||
%% true -> | |||
%% ok | |||
%% end, | |||
%% ?INFO_MSG("Action:~p title:~ts content:~ts ~n", [Action, MailTitle, MailConten]), | |||
%% gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% GM回复玩家接口 | |||
do_handle_request("complain_reply", KvList, Socket) -> | |||
UserName = http_util:get_param("user_name", KvList), | |||
Conten = http_util:get_param("content", KvList), | |||
CompainId = http_util:get_param("compain_id", KvList), | |||
cast_to_server(lib_mail, broadcast_sys_msg, [CompainId, UserName, Conten]), | |||
?INFO_MSG("CompainId:~p UserName::~ts content:~ts ~n", [CompainId, UserName, Conten]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 封禁/解封 账号 | |||
do_handle_request("forbid_login", KvList, Socket) -> | |||
UserNames = http_util:get_param("user_names", KvList), | |||
IsForbid = http_util:get_param("is_forbid", KvList), | |||
ForbidTime = | |||
case http_util:get_param("forbid_time", KvList) of | |||
[] -> 0; | |||
ForbidTime1 -> list_to_integer(ForbidTime1) | |||
end, | |||
Reason = http_util:get_param("reason", KvList), | |||
UserNameList = string:tokens(UserNames, ","), | |||
cast_to_server(lib_admin, ban_role, [UserNameList, list_to_integer(IsForbid), ForbidTime, Reason]), | |||
?INFO_MSG("IsForbid:~p ~n ForbidTime:~p ~n UserName:~ts ~n list:~ts ~n Reason:~ts ~n", | |||
[IsForbid, ForbidTime, UserNames, UserNameList, Reason]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 封禁/解禁 IP | |||
do_handle_request("ip_ban", KvList, Socket) -> | |||
IP = http_util:get_param("ip", KvList), | |||
IsForbid = http_util:get_param("is_forbid", KvList), | |||
ForbidTime = | |||
case http_util:get_param("forbid_time", KvList) of | |||
[] -> 0; | |||
ForbidTime1 -> list_to_integer(ForbidTime1) | |||
end, | |||
Reason = http_util:get_param("reason", KvList), | |||
IpList = string:tokens(IP, ","), | |||
cast_to_server(lib_admin, ban_ip, [IpList, list_to_integer(IsForbid), ForbidTime, Reason]), | |||
?INFO_MSG("IsForbid:~p ForbidTime:~p IP:~ts Reason:~ts ~n", [IsForbid, ForbidTime, IP, Reason]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 踢人接口 | |||
do_handle_request("kick_user", KvList, Socket) -> | |||
UserNames = http_util:get_param("user_names", KvList), | |||
KillFlag = list_to_integer(http_util:get_param("kick_all", KvList)), | |||
Reason = http_util:get_param("reason", KvList), | |||
if | |||
KillFlag =:= 0 -> % 存在多个以逗号分隔 | |||
UserNameList = string:tokens(UserNames, ","), | |||
cast_to_server(lib_admin, kick_user, [UserNameList, Reason]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
KillFlag =:= 1 -> % 踢出所有玩家 | |||
cast_to_server(lib_admin, kick_all_user, []), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
true -> | |||
gen_tcp:send(Socket, ?PARAM_ERROR_CODE) | |||
end, | |||
?INFO_MSG("KillFlag:~p Reason:~ts UserNames:~ts ~n", [KillFlag, Reason]); | |||
%% 禁言 / 解禁 | |||
do_handle_request("ban_chat", KvList, Socket) -> | |||
UserNames = http_util:get_param("user_names", KvList), | |||
BanFlag = list_to_integer(http_util:get_param("is_ban", KvList)), | |||
BanTime1 = http_util:get_param("ban_date", KvList), % 0=永久禁言,否则以此作为时间戳,代表封号结束时间 | |||
BanTime = | |||
case BanTime1 =:= [] of | |||
true -> 0; | |||
false -> list_to_integer(BanTime1) | |||
end, | |||
Reason = http_util:get_param("reason", KvList), | |||
UserNameList = string:tokens(UserNames, ","), | |||
cast_to_server(lib_admin, donttalk, [UserNameList, BanFlag, BanTime, Reason]), %1=禁言; 0=解禁 | |||
?INFO_MSG("BanFlag:~p BanTime:~p Reason:~ts UserNames:~ts ~n", [BanFlag, BanTime, Reason, UserNames]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 新手指导员接口 | |||
do_handle_request("game_instructor_manage", KvList, Socket) -> | |||
UserName = http_util:get_param("user_name", KvList), | |||
Type = http_util:get_param("type", KvList), %1=禁言 0=解禁 | |||
InstructorType = http_util:get_param("instructor_type", KvList), %1.菜鸟指导员 2.指导员达人3.新手导师4.长期指导员5.GM | |||
StartTime = http_util:get_param("start_time", KvList), | |||
EndTime = http_util:get_param("end_time", KvList), | |||
%% cast_to_server(lib_mail, broadcast_sys_msg, [UserName, Type, InstructorType, StartTime, EndTime]), | |||
?INFO_MSG("Type:~p InstructorType:~p StartTime:~p EndTime:~p UserName:~ts ~n", [Type, InstructorType, StartTime, EndTime, UserName]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 重置玩家位置 | |||
do_handle_request("reset_user_pos", KvList, Socket) -> | |||
UserName = http_util:get_param("user_name", KvList), | |||
%% cast_to_server(lib_mail, broadcast_sys_msg, UserName), | |||
?INFO_MSG("content:~ts ~n", [UserName]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 查询满足条件的玩家数量 | |||
do_handle_request("count_user", KvList, Socket) -> | |||
ok; | |||
%% 单个玩家详细信息接口 | |||
do_handle_request("user_info_detail", KvList, Socket) -> | |||
UserId = http_util:get_param("user_id", KvList), | |||
UserName = http_util:get_param("user_name", KvList), | |||
Account = http_util:get_param("account", KvList), | |||
% 三个参数并不是互斥的关系,有可以三个都传,有可能只传两个。它们之间在SQL语句里面是and的关系 | |||
%% case call_to_server(lib_admin, get_player_info, [UserId, UserName, Account]) of | |||
%% {badrpc, _} -> | |||
%% gen_tcp:send(Socket, ?FAILED_CODE); | |||
%% Player -> | |||
%% RetMsg = <<>>, | |||
%% gen_tcp:send(Socket, ?SUCCESS_CODE) | |||
%% end, | |||
?INFO_MSG("UserId:~p UserName:~ts Account:~ts ~n", [UserId, UserName, Account]); | |||
% desc {"字段名":"字段中文含义"} 如:{"user_name":"角色名称",user_id":"角色ID"} | |||
% data data 中必须包含以下基本信息: | |||
% 字段 含义 | |||
% account 玩家平台账号 | |||
% user_id 玩家ID | |||
% user_name 玩家角色名 | |||
% reg_time 角色创建时间 | |||
% level 玩家等级 | |||
% last_login_ip 玩家最后登陆IP | |||
% last_login_time 玩家最后登陆时间 | |||
% country 玩家阵营名称(若玩家没有阵营,则返回-1) | |||
% guild 玩家帮派名称(若玩家没有帮派,则返回-1) | |||
% career 玩家职业名称(若玩家没有职业,则返回-1) | |||
% 自定义信息(下面标红部分),游戏方可自行添加。 自定义信息与基本信息返回方式一致: | |||
% | |||
% { | |||
% "account":"gfsyra", | |||
% "user_id":"221", | |||
% "user_name":"高富帅有人爱", | |||
% "reg_time":"1371928400", | |||
% "level":"22", | |||
% "reg_time":"22", | |||
% "last_login_ip":"94.123.22.123", | |||
% "last_login_time":"1388928400", | |||
% "country":"魏国", | |||
% "guild":"超级兵马俑", | |||
% "career":"魏将", | |||
% "is_vip":"y", | |||
% "sex":"1", | |||
% ...... | |||
% | |||
%% 玩家信息列表 | |||
do_handle_request("user_info_list", KvList, Socket) -> | |||
ok; | |||
%% 单个帮派详细信息接口 | |||
do_handle_request("guild_info_detail", KvList, Socket) -> | |||
GuildId = http_util:get_param("guild_id", KvList), | |||
GuildName = http_util:get_param("guild_name", KvList), | |||
%% call_to_server(lib_mail, broadcast_sys_msg, [GuildId, GuildName]), | |||
?INFO_MSG("type:~p content:~ts ~n", [GuildId, GuildName]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
% data 中必须包含以下基本信息: | |||
% 字段 含义 | |||
% guild_id 帮派ID | |||
% guild_name 帮派名称 | |||
% guild_level 帮派等级 | |||
% guild_ranking 帮派排名 | |||
% leader 帮忙创建者 | |||
% create_time 创建时间 | |||
% member_count 帮派人数 | |||
% member_list 玩家列表,json数组格式 | |||
% 自定义信息(下面标红部分),游戏方可自行添加。 自定义信息与基本信息返回方式一致: | |||
% | |||
% { | |||
% "guild_id":"1234", | |||
% "guild_name":"高富帅帮", | |||
% "guild_level":"2", | |||
% "guild_ranking":"1", | |||
% "member_count":"3", | |||
% "member_list":[ | |||
% "高", "富","帅","高富帅有人爱" | |||
% ], | |||
% "leader":"高富帅有人爱", | |||
% "create_time":"1388928400", | |||
% "is_vip":"y", | |||
% ...... | |||
% } | |||
%% 玩家帮派信息列表 | |||
do_handle_request("guild_info_list", KvList, Socket) -> | |||
GuildId = http_util:get_param("guild_id", KvList), | |||
GuildName = http_util:get_param("guild_name", KvList), | |||
%% call_to_server(lib_mail, broadcast_sys_msg, [GuildId, GuildName]), | |||
?INFO_MSG("type:~p content:~ts ~n", [GuildId, GuildName]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 刷新在线玩家信息 | |||
do_handle_request("freshen_online_user", KvList, Socket) -> | |||
%% cast_to_server(lib_mail, broadcast_sys_msg, []), | |||
?INFO_MSG("freshen_online_user ~p ~n", []), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 玩家道具查询接口 | |||
do_handle_request("user_props_list", KvList, Socket) -> | |||
UserId = http_util:get_param("user_id", KvList), | |||
UserName = http_util:get_param("user_name", KvList), | |||
Account = http_util:get_param("account", KvList), | |||
%% call_to_server(lib_mail, broadcast_sys_msg, [UserId, UserName, Account]), | |||
?INFO_MSG("UserId:~p UserName:~ts Account:~ts ~n", [UserId, UserName, Account]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 玩家坐骑查询接口 | |||
do_handle_request("user_horse_list", KvList, Socket) -> | |||
UserId = http_util:get_param("user_id", KvList), | |||
UserName = http_util:get_param("user_name", KvList), | |||
Account = http_util:get_param("account", KvList), | |||
%% call_to_server(lib_mail, broadcast_sys_msg, [UserId, UserName, Account]), | |||
?INFO_MSG("UserId:~p UserName:~ts Account:~ts ~n", [UserId, UserName, Account]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 玩家宠物查询接口 | |||
do_handle_request("user_pet_list", KvList, Socket) -> | |||
UserId = http_util:get_param("user_id", KvList), | |||
UserName = http_util:get_param("user_name", KvList), | |||
Account = http_util:get_param("account", KvList), | |||
%% call_to_server(lib_mail, broadcast_sys_msg, [UserId, UserName, Account]), | |||
?INFO_MSG("UserId:~p UserName:~ts Account:~ts ~n", [UserId, UserName, Account]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 玩家技能查询接口 | |||
do_handle_request("user_skill_list", KvList, Socket) -> | |||
UserId = http_util:get_param("user_id", KvList), | |||
UserName = http_util:get_param("user_name", KvList), | |||
Account = http_util:get_param("account", KvList), | |||
%% call_to_server(lib_mail, broadcast_sys_msg, [UserId, UserName, Account]), | |||
?INFO_MSG("UserId:~p UserName:~ts Account:~ts ~n", [UserId, UserName, Account]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
do_handle_request("broad", KvList, Socket) -> | |||
AnnId = http_util:get_param("annid", KvList), | |||
cast_to_server(mod_misc, load_sys_announce, [AnnId, annid]), | |||
%% lib_chat:broadcast_sys_msg("test"), | |||
?INFO_MSG("type:~p content ~n", [AnnId]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
%% 充值 | |||
do_handle_request("charge", KvList, Socket) -> | |||
AccountId = list_to_integer(http_util:get_param("account_id", KvList)), | |||
OrderId = list_to_integer(http_util:get_param("order_id", KvList)), | |||
cast_to_server(lib_admin, handle_charge, [AccountId, OrderId]), | |||
?INFO_MSG("Account:~p OrderId:~p ~n", [AccountId, OrderId]), | |||
gen_tcp:send(Socket, ?SUCCESS_CODE); | |||
do_handle_request(Other, Kvlist, Socket) -> | |||
?INFO_MSG("admin unknown cmd ~p, ~p", [Other, Kvlist]), | |||
gen_tcp:send(Socket, ?PARAM_ERROR_CODE). | |||
%% ===================针对玩家的各类操作===================================== | |||
cast_to_server(Module, Method, Args) -> | |||
GameSvrNode = config:get_server_node(local_gateway), | |||
rpc:cast(GameSvrNode, Module, Method, Args). | |||
call_to_server(Module, Method, Args) -> | |||
GameSvrNode = config:get_server_node(local_gateway), | |||
rpc:call(GameSvrNode, Module, Method, Args). | |||
%% 安全退出当前游戏服务器(在游戏服务器节点执行) | |||
safe_quit() -> | |||
timer:sleep(10 * 1000), | |||
mod_guild:safe_quit(), | |||
main:server_stop(), | |||
ok. | |||
stop_server_node([NodeName]) -> | |||
rpc:cast(NodeName, main, server_stop, []). | |||
stop_local_gateway_node([NodeName]) -> | |||
rpc:cast(NodeName, main, local_gateway_stop, []). | |||
stop_game_node([NodeName]) -> | |||
rpc:cast(NodeName, main, gateway_stop, []). | |||
%% 清数据接口,非请勿用,出问题了别找我 | |||
clear_data() -> | |||
AllTables = db_esql:get_all("show tables"), | |||
Fun = fun(Item) -> | |||
[TName | _] = Item, | |||
TableName = util:bitstring_to_term(TName), | |||
ListTableName = atom_to_list(TableName), | |||
case lists:sublist(ListTableName, 1, 5) =:= "temp_" orelse | |||
lists:sublist(ListTableName, 1, 7) =:= "config_" of | |||
true -> | |||
skip; | |||
false -> | |||
TruncatSql = lists:concat(["truncate table ", TableName]), | |||
db_esql:execute_sql(TruncatSql), | |||
io:format("==truncated table:~p~n", [TableName]) | |||
end | |||
end, | |||
lists:foreach(Fun, AllTables), | |||
timer:sleep(1000), | |||
erlang:halt(). | |||
%% Sql = lists:concat(["show tables"]), | |||
%% case db_esql:get_row(Sql) of | |||
%% {db_error, _} -> | |||
%% error; | |||
%% [_, A|_]-> | |||
%% CreateTableList = re:split(A,"[\n]",[{return, binary}]), | |||
%% search_auto_increment(CreateTableList) | |||
%% end. |
@ -1,25 +0,0 @@ | |||
%%%--------------------------------------------- | |||
%%% @Module : prof | |||
%%% @Author : csj | |||
%%% @Created : 2010.10.05 | |||
%%% @Description: 性能测试工具 | |||
%%%--------------------------------------------- | |||
-module(prof). | |||
-compile(export_all). | |||
%%性能测试 | |||
%%Fun:函数 | |||
%%Loop:执行次数 | |||
run(Fun, Loop) -> | |||
statistics(wall_clock), | |||
for(1, Loop, Fun), | |||
{_, T1} = statistics(wall_clock), | |||
io:format("~p loops, using time: ~pms~n", [Loop, T1]), | |||
ok. | |||
for(Max, Max, Fun) -> | |||
Fun(); | |||
for(I, Max, Fun) -> | |||
Fun(), for(I + 1, Max, Fun). | |||
@ -1,651 +0,0 @@ | |||
-module(tool). | |||
-compile(export_all). | |||
-define(IOFILE(Str, Args), (fun() -> | |||
Command = io_lib:format(Str, Args), | |||
file:write_file("../logs/data/proto_data.log", Command, [append]) | |||
end)()). | |||
%%导出协议统计结果 | |||
stat_proto_data() -> | |||
List = ets:tab2list(proto_stat), | |||
NewList = lists:map(fun({Cmd, ProtoNum}) -> | |||
{ProtoNum, Cmd} | |||
end, List), | |||
lists:foreach(fun({ProtoNum, Cmd}) -> | |||
?IOFILE("协议->~p使用次数为->~p ~n", [Cmd, ProtoNum]) | |||
end | |||
, lists:sort(NewList)). | |||
%%打印函数调用测试结果 | |||
trace_profile_result() -> | |||
eprof:stop_profiling(), | |||
eprof:analyze(total). | |||
%% 分割列表的函数 | |||
split(N, SrcList) -> | |||
case length(SrcList) > N of | |||
true -> | |||
lists:split(N, SrcList); | |||
false -> | |||
{SrcList, []} | |||
end. | |||
%% @doc get IP address string from Socket | |||
ip(Socket) -> | |||
{ok, {IP, _Port}} = inet:peername(Socket), | |||
{Ip0, Ip1, Ip2, Ip3} = IP, | |||
list_to_binary(integer_to_list(Ip0) ++ "." ++ integer_to_list(Ip1) ++ "." ++ integer_to_list(Ip2) ++ "." ++ integer_to_list(Ip3)). | |||
%% @doc quick sort | |||
sort([]) -> | |||
[]; | |||
sort([H | T]) -> | |||
sort([X || X <- T, X < H]) ++ [H] ++ sort([X || X <- T, X >= H]). | |||
%% for | |||
for(Max, Max, F) -> [F(Max)]; | |||
for(I, Max, F) -> [F(I) | for(I + 1, Max, F)]. | |||
%%--------------------------------------------------- | |||
%%给列表中元素加下标 by chenzm | |||
%%@spec for(n,m,fun()) -> [] | |||
%%--------------------------------------------------- | |||
add_index([]) -> | |||
[]; | |||
add_index(List) -> | |||
for(1, length(List), fun(I) -> | |||
Elem = lists:nth(I, List), | |||
if | |||
is_tuple(Elem) -> | |||
list_to_tuple([I] ++ tuple_to_list(Elem)); | |||
true -> | |||
{I, Elem} | |||
end | |||
end). | |||
add_index_to_record(List) -> | |||
case List of | |||
[] -> | |||
[]; | |||
_ -> | |||
for(1, length(List), fun(I) -> | |||
Elem = lists:nth(I, List), | |||
{I, Elem} | |||
end) | |||
end. | |||
%% @doc convert float to string, f2s(1.5678) -> 1.57 | |||
f2s(N) when is_integer(N) -> | |||
integer_to_list(N) ++ ".00"; | |||
f2s(F) when is_float(F) -> | |||
[A] = io_lib:format("~.2f", [F]), | |||
A. | |||
%% @doc convert other type to atom | |||
to_atom(Msg) when is_atom(Msg) -> | |||
Msg; | |||
to_atom(Msg) when is_binary(Msg) -> | |||
tool:list_to_atom2(binary_to_list(Msg)); | |||
to_atom(Msg) when is_list(Msg) -> | |||
tool:list_to_atom2(Msg); | |||
to_atom(_) -> | |||
throw(other_value). %%list_to_atom(""). | |||
%% @doc convert other type to list | |||
to_list(Msg) when is_list(Msg) -> | |||
Msg; | |||
to_list(Msg) when is_atom(Msg) -> | |||
atom_to_list(Msg); | |||
to_list(Msg) when is_binary(Msg) -> | |||
binary_to_list(Msg); | |||
to_list(Msg) when is_integer(Msg) -> | |||
integer_to_list(Msg); | |||
to_list(Msg) when is_float(Msg) -> | |||
f2s(Msg); | |||
to_list(_) -> | |||
throw(other_value). | |||
%% @doc convert other type to binary | |||
to_binary(Msg) when is_binary(Msg) -> | |||
Msg; | |||
to_binary(Msg) when is_atom(Msg) -> | |||
list_to_binary(atom_to_list(Msg)); | |||
%%atom_to_binary(Msg, utf8); | |||
to_binary(Msg) when is_list(Msg) -> | |||
list_to_binary(Msg); | |||
to_binary(Msg) when is_integer(Msg) -> | |||
list_to_binary(integer_to_list(Msg)); | |||
to_binary(Msg) when is_float(Msg) -> | |||
list_to_binary(f2s(Msg)); | |||
to_binary(_Msg) -> | |||
throw(other_value). | |||
%% @doc convert other type to float | |||
to_float(Msg) -> | |||
Msg2 = to_list(Msg), | |||
list_to_float(Msg2). | |||
%% @doc convert other type to integer | |||
%% -spec to_integer(Msg :: any()) -> integer(). %%liujing 2012-8-9 cancel | |||
to_integer(Msg) when is_integer(Msg) -> | |||
Msg; | |||
to_integer(Msg) when is_binary(Msg) -> | |||
Msg2 = binary_to_list(Msg), | |||
list_to_integer(Msg2); | |||
to_integer(Msg) when is_list(Msg) -> | |||
list_to_integer(Msg); | |||
to_integer(Msg) when is_float(Msg) -> | |||
round(Msg); | |||
to_integer(_Msg) -> | |||
throw(other_value). | |||
to_bool(D) when is_integer(D) -> | |||
D =/= 0; | |||
to_bool(D) when is_list(D) -> | |||
length(D) =/= 0; | |||
to_bool(D) when is_binary(D) -> | |||
to_bool(binary_to_list(D)); | |||
to_bool(D) when is_boolean(D) -> | |||
D; | |||
to_bool(_D) -> | |||
throw(other_value). | |||
%% @doc convert other type to tuple | |||
to_tuple(T) when is_tuple(T) -> T; | |||
to_tuple(T) -> {T}. | |||
%% @doc get data type {0=integer,1=list,2=atom,3=binary} | |||
get_type(DataValue, DataType) -> | |||
case DataType of | |||
0 -> | |||
DataValue2 = binary_to_list(DataValue), | |||
list_to_integer(DataValue2); | |||
1 -> | |||
binary_to_list(DataValue); | |||
2 -> | |||
DataValue2 = binary_to_list(DataValue), | |||
list_to_atom(DataValue2); | |||
3 -> | |||
DataValue | |||
end. | |||
%% @spec is_string(List)-> yes|no|unicode | |||
is_string([]) -> yes; | |||
is_string(List) -> is_string(List, non_unicode). | |||
is_string([C | Rest], non_unicode) when C >= 0, C =< 255 -> is_string(Rest, non_unicode); | |||
is_string([C | Rest], _) when C =< 65000 -> is_string(Rest, unicode); | |||
is_string([], non_unicode) -> yes; | |||
is_string([], unicode) -> unicode; | |||
is_string(_, _) -> no. | |||
%% @doc get random list | |||
list_random(List) -> | |||
case List of | |||
[] -> | |||
{}; | |||
_ -> | |||
RS = lists:nth(random:uniform(length(List)), List), | |||
ListTail = lists:delete(RS, List), | |||
{RS, ListTail} | |||
end. | |||
%% @doc get a random integer between Min and Max | |||
random(Min, Max) -> | |||
Min2 = Min - 1, | |||
random:uniform(Max - Min2) + Min2. | |||
%% @doc 掷骰子 | |||
random_dice(Face, Times) -> | |||
if | |||
Times == 1 -> | |||
random(1, Face); | |||
true -> | |||
lists:sum(for(1, Times, fun(_) -> random(1, Face) end)) | |||
end. | |||
%% @doc 机率 | |||
odds(Numerator, Denominator) -> | |||
Odds = random:uniform(Denominator), | |||
if | |||
Odds =< Numerator -> | |||
true; | |||
true -> | |||
false | |||
end. | |||
odds_list(List) -> | |||
Sum = odds_list_sum(List), | |||
odds_list(List, Sum). | |||
odds_list([{Id, Odds} | List], Sum) -> | |||
case odds(Odds, Sum) of | |||
true -> | |||
Id; | |||
false -> | |||
odds_list(List, Sum - Odds) | |||
end. | |||
odds_list_sum(List) -> | |||
{_List1, List2} = lists:unzip(List), | |||
lists:sum(List2). | |||
%% @doc 取整 大于X的最小整数 | |||
ceil(X) -> | |||
T = trunc(X), | |||
if | |||
X - T == 0 -> | |||
T; | |||
true -> | |||
if | |||
X > 0 -> | |||
T + 1; | |||
true -> | |||
T | |||
end | |||
end. | |||
%% @doc 取整 小于X的最大整数 | |||
floor(X) -> | |||
T = trunc(X), | |||
if | |||
X - T == 0 -> | |||
T; | |||
true -> | |||
if | |||
X > 0 -> | |||
T; | |||
true -> | |||
T - 1 | |||
end | |||
end. | |||
%% 4舍5入 | |||
%% round(X) | |||
%% subatom | |||
subatom(Atom, Len) -> | |||
list_to_atom(lists:sublist(atom_to_list(Atom), Len)). | |||
%% @doc 暂停多少毫秒 | |||
sleep(Msec) -> | |||
receive | |||
after Msec -> | |||
true | |||
end. | |||
md5(S) -> | |||
Md5_bin = erlang:md5(S), | |||
Md5_list = binary_to_list(Md5_bin), | |||
lists:flatten(list_to_hex(Md5_list)). | |||
list_to_hex(L) -> | |||
lists:map(fun(X) -> int_to_hex(X) end, L). | |||
int_to_hex(N) when N < 256 -> | |||
[hex(N div 16), hex(N rem 16)]. | |||
hex(N) when N < 10 -> | |||
$0 + N; | |||
hex(N) when N >= 10, N < 16 -> | |||
$a + (N - 10). | |||
list_to_atom2(List) when is_list(List) -> | |||
case catch (list_to_existing_atom(List)) of | |||
{'EXIT', _} -> erlang:list_to_atom(List); | |||
Atom when is_atom(Atom) -> Atom | |||
end. | |||
combine_lists(L1, L2) -> | |||
Rtn = | |||
lists:foldl( | |||
fun(T, Acc) -> | |||
case lists:member(T, Acc) of | |||
true -> | |||
Acc; | |||
false -> | |||
[T | Acc] | |||
end | |||
end, lists:reverse(L1), L2), | |||
lists:reverse(Rtn). | |||
get_process_info_and_zero_value(InfoName) -> | |||
PList = erlang:processes(), | |||
ZList = lists:filter( | |||
fun(T) -> | |||
case erlang:process_info(T, InfoName) of | |||
{InfoName, 0} -> false; | |||
_ -> true | |||
end | |||
end, PList), | |||
ZZList = lists:map( | |||
fun(T) -> {T, erlang:process_info(T, InfoName), erlang:process_info(T, registered_name)} | |||
end, ZList), | |||
[length(PList), InfoName, length(ZZList), ZZList]. | |||
get_process_info_and_large_than_value(InfoName, Value) -> | |||
PList = erlang:processes(), | |||
ZList = lists:filter( | |||
fun(T) -> | |||
case erlang:process_info(T, InfoName) of | |||
{InfoName, VV} -> | |||
if VV > Value -> true; | |||
true -> false | |||
end; | |||
_ -> true | |||
end | |||
end, PList), | |||
ZZList = lists:map( | |||
fun(T) -> {T, erlang:process_info(T, InfoName), erlang:process_info(T, registered_name)} | |||
end, ZList), | |||
[length(PList), InfoName, Value, length(ZZList), ZZList]. | |||
get_msg_queue() -> | |||
io:fwrite("process count:~p~n~p value is not 0 count:~p~nLists:~p~n", | |||
get_process_info_and_zero_value(message_queue_len)). | |||
get_memory() -> | |||
io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n", | |||
get_process_info_and_large_than_value(memory, 1048576)). | |||
get_memory(Value) -> | |||
io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n", | |||
get_process_info_and_large_than_value(memory, Value)). | |||
get_heap() -> | |||
io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n", | |||
get_process_info_and_large_than_value(heap_size, 1048576)). | |||
get_heap(Value) -> | |||
io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n", | |||
get_process_info_and_large_than_value(heap_size, Value)). | |||
get_processes() -> | |||
io:fwrite("process count:~p~n~p value is large than ~p count:~p~nLists:~p~n", | |||
get_process_info_and_large_than_value(memory, 0)). | |||
list_to_term(String) -> | |||
{ok, T, _} = erl_scan:string(String ++ "."), | |||
case erl_parse:parse_term(T) of | |||
{ok, Term} -> | |||
Term; | |||
{error, Error} -> | |||
Error | |||
end. | |||
substr_utf8(Utf8EncodedString, Length) -> | |||
substr_utf8(Utf8EncodedString, 1, Length). | |||
substr_utf8(Utf8EncodedString, Start, Length) -> | |||
ByteLength = 2 * Length, | |||
Ucs = xmerl_ucs:from_utf8(Utf8EncodedString), | |||
Utf16Bytes = xmerl_ucs:to_utf16be(Ucs), | |||
SubStringUtf16 = lists:sublist(Utf16Bytes, Start, ByteLength), | |||
Ucs1 = xmerl_ucs:from_utf16be(SubStringUtf16), | |||
xmerl_ucs:to_utf8(Ucs1). | |||
ip_str(IP) -> | |||
case IP of | |||
{A, B, C, D} -> | |||
lists:concat([A, ".", B, ".", C, ".", D]); | |||
{A, B, C, D, E, F, G, H} -> | |||
lists:concat([A, ":", B, ":", C, ":", D, ":", E, ":", F, ":", G, ":", H]); | |||
Str when is_list(Str) -> | |||
Str; | |||
_ -> | |||
[] | |||
end. | |||
%%对正负进行调整:负数变为0,正数保持不变 | |||
int_format(Num) -> | |||
if Num >= 0 -> | |||
Num; | |||
true -> | |||
0 | |||
end. | |||
%%去掉字符串空格 | |||
remove_string_black(L) -> | |||
F = fun(S) -> | |||
if S == 32 -> []; | |||
true -> S | |||
end | |||
end, | |||
Result = [F(lists:nth(I, L)) || I <- lists:seq(1, length(L))], | |||
lists:filter(fun(T) -> T =/= [] end, Result). | |||
%%获取协议操作的时间戳,true->允许;false -> 直接丢弃该条数据 | |||
%%spec is_operate_ok/1 param: Type -> 添加的协议类型(atom); return: true->允许;false -> 直接丢弃该条数据 | |||
is_operate_ok(Type, TimeStamp) -> | |||
NowTime = util:longunixtime(), | |||
case get(Type) of | |||
undefined -> | |||
put(Type, NowTime), | |||
true; | |||
Value -> | |||
case (NowTime - Value) >= TimeStamp of | |||
true -> | |||
put(Type, NowTime), | |||
true; | |||
false -> | |||
false | |||
end | |||
end. | |||
%%打包字符串数据 | |||
pack_string(Str) -> | |||
StrBin = tool:to_binary(Str), | |||
Len = byte_size(StrBin), | |||
{Len, StrBin}. | |||
%%对[{GetName, Rate},..]结构类型的单项随机获取的通用处理,空列表返回undefined | |||
get_rand_single(RateList) -> | |||
Fun = fun({_Tmp, R}, RNum) -> | |||
RNum + R | |||
end, | |||
AllR = lists:foldl(Fun, 0, RateList), | |||
GetRNum = util:rand(1, AllR), | |||
Fun1 = fun({Atom, Rat}, [BGet, Ra, FirstNum, GetAtom1]) -> | |||
EndNum = FirstNum + Rat, | |||
if BGet =:= 0 andalso Ra =< EndNum -> | |||
[1, Ra, EndNum, Atom]; | |||
true -> | |||
[BGet, Ra, EndNum, GetAtom1] | |||
end | |||
end, | |||
[_NewBGet, _NewRa, _FirstNum, GetAtom] = lists:foldl(Fun1, [0, GetRNum, 0, undefined], RateList), | |||
GetAtom. | |||
%%对[{GetName, Rate, MinNum, MaxNum},..]结构类型的单项随机获取的通用处理,空列表返回undefined | |||
get_rand_single2(RateList) -> | |||
Fun = fun({_Tmp, R, _Mn, _Mx}, RNum) -> | |||
RNum + R | |||
end, | |||
AllR = lists:foldl(Fun, 0, RateList), | |||
GetRNum = util:rand(1, AllR), | |||
Fun1 = fun({Atom, Rat, MinNum, MaxNum}, [BGet, Ra, FirstNum, GetAtom1]) -> | |||
EndNum = FirstNum + Rat, | |||
if BGet =:= 0 andalso Ra =< EndNum -> | |||
[1, Ra, EndNum, {Atom, MinNum, MaxNum}]; | |||
true -> | |||
[BGet, Ra, EndNum, GetAtom1] | |||
end | |||
end, | |||
[_NewBGet, _NewRa, _FirstNum, GetAtom] = lists:foldl(Fun1, [0, GetRNum, 0, undefined], RateList), | |||
GetAtom. | |||
%%对单个玩家数据回档的操作函数======2012-9-17 from liujing====== | |||
%%获取需要处理的表名(返回字符串结构的表名) | |||
get_handle_table_name() -> | |||
TableList = lib_player_rw:get_all_tables(), | |||
F = fun(TableName, GetList) -> | |||
TableName1 = util:term_to_string(TableName), | |||
case TableName1 =/= "cards" andalso TableName1 =/= "sys_acm" andalso string:str(TableName1, "admin") =/= 1 | |||
andalso TableName1 =/= "auto_ids" andalso TableName1 =/= "shop" andalso string:str(TableName1, "base") =/= 1 | |||
andalso TableName1 =/= "slaves" andalso TableName1 =/= "battle_cache_data" andalso string:str(TableName1, "arena") =/= 1 | |||
andalso string:str(TableName1, "log_") =/= 1 andalso string:str(TableName1, "th") =/= 1 | |||
andalso TableName1 =/= "rela" of | |||
false -> GetList; | |||
true -> | |||
GetList ++ [util:string_to_term(tool:to_list(TableName1))] | |||
end | |||
end, | |||
lists:foldl(F, [], TableList). | |||
ini_faraway_mongo_db(PoolId, Num) -> | |||
Host = | |||
case Num of | |||
1 -> | |||
"113.105.250.125"; | |||
2 -> | |||
"113.105.251.123"; | |||
4 -> | |||
"183.61.130.69"; | |||
_ -> | |||
%%连接内部192.168.51.174服务器 | |||
"192.168.51.174" | |||
end, | |||
%% Host = io:get_line("remote database ip:") , | |||
Port = 27017, | |||
case Num of | |||
0 -> | |||
DB = "csj_dev_S1"; | |||
_ -> | |||
DB = lists:concat(["csj_dev_S", Num]) | |||
end, | |||
io:format("====dest db:~p~n", [[Host, DB]]), | |||
EmongoSize = 1, | |||
emongo_sup:start_link(), | |||
emongo_app:initialize_pools([PoolId, Host, Port, DB, EmongoSize]). | |||
ini_local_mongo_db(PoolId, Num) -> | |||
Host = "192.168.51.174", | |||
%% Host = io:get_line("sorce database ip:") , | |||
Port = 27017, | |||
case Num of | |||
0 -> | |||
DB = "csj_dev_src_S1"; | |||
_ -> | |||
DB = lists:concat(["csj_dev_src_S", Num]) | |||
end, | |||
io:format("====src db:~p~n", [DB]), | |||
EmongoSize = 1, | |||
emongo_sup:start_link(), | |||
emongo_app:initialize_pools([PoolId, Host, Port, DB, EmongoSize]). | |||
get_mongo_to_mysql(UidList, ServerNum) -> | |||
%% CONFIG_FILE = "../config/gateway.config", | |||
FarPoolId = lists:concat(["master_mongo_tmp", ServerNum]), | |||
LocalPoolId = "master_mongo_l", | |||
TableList = get_handle_table_name(), | |||
ini_faraway_mongo_db(FarPoolId, ServerNum), | |||
ini_local_mongo_db(LocalPoolId, ServerNum), | |||
Fun1 = fun(TableName, GetUid) -> | |||
case TableName of | |||
player -> | |||
io:format("========_1_~n"), | |||
[WhereOpertion, FieldOpertion] = db_mongoutil:make_select_opertion(db_mongo:transfer_fields(TableName, "*"), [{id, GetUid}], [], []), | |||
L = emongo:find_all(LocalPoolId, tool:to_list(TableName), WhereOpertion, FieldOpertion), | |||
io:format("========_1_~p~n", [L]), | |||
RList = db_mongo:handle_all_result(TableName, db_mongo:transfer_fields(TableName, "*"), L), | |||
[DelOpertion] = db_mongoutil:make_delete_opertion([{id, GetUid}]), | |||
emongo:delete(FarPoolId, tool:to_list(TableName), DelOpertion), | |||
case RList of | |||
[] -> | |||
GetUid; | |||
_ -> | |||
FieldList = db_mongo:get_all_fields(TableName), | |||
Fun2 = fun(RL) -> | |||
FullKeyValuelist = db_mongo:fullKeyValue(TableName, lists:zip(FieldList, RL)), | |||
FullKeyValuelist1 = checkF(FullKeyValuelist), | |||
Opertion = db_mongoutil:make_insert_opertion(FullKeyValuelist1), | |||
emongo:insert(FarPoolId, tool:to_list(TableName), Opertion) | |||
end, | |||
lists:foreach(Fun2, RList), | |||
GetUid | |||
end; | |||
_ -> | |||
io:format("========_3_[~p]~n", [TableName]), | |||
FieldList = db_mongo:get_all_fields(TableName), | |||
case FieldList of | |||
undefined -> | |||
GetUid; | |||
[] -> | |||
GetUid; | |||
_ -> | |||
[WhereOpertion, FieldOpertion] = db_mongoutil:make_select_opertion(db_mongo:transfer_fields(TableName, "*"), [{uid, GetUid}], [], []), | |||
L = emongo:find_all(LocalPoolId, tool:to_list(TableName), WhereOpertion, FieldOpertion), | |||
RList = db_mongo:handle_all_result(TableName, db_mongo:transfer_fields(TableName, "*"), L), | |||
[DelOpertion] = db_mongoutil:make_delete_opertion([{uid, GetUid}]), | |||
emongo:delete(FarPoolId, tool:to_list(TableName), DelOpertion), | |||
case RList of | |||
[] -> | |||
GetUid; | |||
_ -> | |||
Fun2 = fun(RL) -> | |||
FullKeyValuelist = db_mongo:fullKeyValue(TableName, lists:zip(FieldList, RL)), | |||
FullKeyValuelist1 = checkF(FullKeyValuelist), | |||
Opertion = db_mongoutil:make_insert_opertion(FullKeyValuelist1), | |||
emongo:insert(FarPoolId, tool:to_list(TableName), Opertion) | |||
end, | |||
lists:foreach(Fun2, RList), | |||
GetUid | |||
end | |||
end | |||
end | |||
end, | |||
Fun = fun(Uid) -> | |||
lists:foldl(Fun1, Uid, TableList) | |||
end, | |||
lists:foreach(Fun, UidList). | |||
checkF(KeyVList) -> | |||
Fun = fun({Key, Val}) -> | |||
case Val of | |||
[] -> | |||
{Key, <<"[]">>}; | |||
undefined -> | |||
{Key, <<"[]">>}; | |||
_ -> | |||
{Key, Val} | |||
end | |||
end, | |||
lists:map(Fun, KeyVList). | |||
%%打印结构体,哥只能做到这样的 | |||
%%dynamic_compile死活编译不过 | |||
recinfo(FieldList, Rec) -> | |||
RecordName = lists:nth(1, tuple_to_list(Rec)), | |||
ValueList = lists:nthtail(1, tuple_to_list(Rec)), | |||
Length = min(length(FieldList), length(ValueList)), | |||
OutStr = "#" ++ tool:to_list(RecordName) ++ "{\r\n", | |||
OutStr1 = lists:foldl(fun(Idx, Str) -> | |||
Str ++ io_lib:format(" ~p = ~p,~n", [lists:nth(Idx, FieldList), lists:nth(Idx, ValueList)]) | |||
end, OutStr, lists:seq(1, Length - 1)), | |||
OutStr1 ++ io_lib:format(" ~p = ~p}~n", [lists:nth(Length, FieldList), lists:nth(Length, ValueList)]). | |||
gcd(A, 0) -> | |||
A; | |||
gcd(A, B) when A >= B -> | |||
gcd(A rem B, B); | |||
gcd(A, B) -> | |||
gcd(B, A). | |||
%%求最小公约数 | |||
hcd([H | T]) -> | |||
gcd(H, hcd(T)); | |||
hcd([]) -> | |||
0. | |||
@ -0,0 +1,45 @@ | |||
-module(utProf). | |||
%trace Mod 所有方法的调用 | |||
trace(Mod) -> | |||
dbg:tracer(), | |||
dbg:tpl(Mod, '_', []), | |||
dbg:p(all, c). | |||
%trace Node上指定 Mod 所有方法的调用, 结果将输出到本地shell | |||
trace(Node, Mod) -> | |||
dbg:tracer(), | |||
dbg:n(Node), | |||
dbg:tpl(Mod, '_', []), | |||
dbg:p(all, c). | |||
%停止trace | |||
trace_stop() -> | |||
dbg:stop_clear(). | |||
% 对整个节点内所有进程执行eprof, eprof 对线上业务有一定影响,慎用! | |||
% 建议TimeoutSec<10s,且进程数< 1000,否则可能导致节点crash | |||
% 结果: | |||
% 输出每个方法实际执行时间(不会累计方法内其他mod调用执行时间) | |||
% 只能得到mod - Fun 执行次数 执行耗时 | |||
eprof_all(TimeoutSec) -> | |||
eprof(processes() -- [whereis(eprof)], TimeoutSec). | |||
eprof(Pids, TimeoutSec) -> | |||
eprof:start(), | |||
eprof:start_profiling(Pids), | |||
timer:sleep(TimeoutSec), | |||
eprof:stop_profiling(), | |||
eprof:analyze(total), | |||
eprof:stop(). | |||
% @doc 对MFA 执行分析,会严重减缓运行,建议只对小量业务执行 | |||
% 结果: | |||
% fprof 结果比较详细,能够输出热点调用路径 | |||
fprof(M, F, A) -> | |||
fprof:start(), | |||
fprof:apply(M, F, A), | |||
fprof:profile(), | |||
fprof:analyse([{dest, "fprof.analysis"}, {sort, own}]), | |||
fprof:stop(). |