@ -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(). |