zl程序教程

您现在的位置是:首页 >  其它

当前栏目

【原创】Erlang 之 entop 使用问题

原创 erlang 问题 使用
2023-09-14 08:59:46 时间
   工欲善其事,必先利其器。排查 erlang 系统问题时,肯定希望能有一个像 Unix top 一样的工具,entop 就是这么个东东。 

---------- 我是三月份发版本天天加班的分隔线 ----------- 

(以下内容翻译自 entop 的 README.md 文件) 

entop 

如同 Unix 中 top 一样的 Erlang 节点信息查看工具。 

简介 
      entop 是用来展示远端 Erlang 节点运行信息的工具,其信息显示的方式类似于 Unix 中的 top 命令。
      若要保证 entop 的正常运行,在 pre-R15 环境下,需要使用 cecho 0.3.0 版本;在 R15 或更高版本的环境下需要 cecho 0.4.0 版本。
      cecho 的 github 地址:这里 。 

编译 

清理和编译可以分别运行如下命令 
./rebar clean

./rebar compile
注意:如果你遇到和 cecho 依赖相关的问题,可以手动创建符号链接到 deps/ 下的 cecho (如果你的 cecho 放在其他目录也可以进行类似操作),或者运行 ./rebar get-deps 以下载最新版本。当通过 rebar 获取到最新版本后,不要忘记重新编译整个应用。 

用法 
      若想成功运行 entop ,首先要确保 Erlang 已经安装到你的系统之中,并且 cecho 库所在路径被 Erlang code path 所包含。 项目中默认提供的启动脚本(entop)假定了其在 entop 应用根目录下被执行,如果这与你的实际情况不符,请自行调整脚本的相应路径,或者直接确保 entop 的 ebin/ 目录包含在 Erlang code path 之中。详情请参考启动脚本具体内容。 
Usage: ./entop TARGETNODE [-name NAME |-sname SNAME ] [-setcookie COOKIE ]

entop 的运行示例 
 ./entop rmq_yoyo@YOYO -sname entop -setcookie yoyo

用户接口 
entop 的接口允许用户定制化,所以本节描述的接口均为“内置”接口。 

表头信息 
第一行 主要展示了节点的静态信息,例如节点名、操作系统类型、指定的 erl flag 、当前所运行的 erlang 版本信息。 
第二行 展示了(目标节点所在机器的)本地时间、目标节点已持续运行的时间(格式为 Days:Hours:Minutes:Seconds)、运行 entop 的节点与目标节点之间的网络延迟情况(即 net_adm:ping() 成功交互所需花费的时间) 
第三行 展示了系统中每个进程的具体信息、进程的总数、运行队列中的进程数量(由调度器进行调度的待运行进程数量)、reductions per interval (RpI) 值(自从上一次 called the node 后系统已经 reduction 的次数)、以及每个进程占用的内存量。 
第四行 展示了系统内存使用量、atom 内存占用量(当前使用量/总体分配量)、binary 内存占用量、code 内存占用量,以及 ets 内存占用量。 
第五行 为空白,目前作为预留。 
第六行 为和行内容展示相关的信息,例如信息获取时间间隔、信息展示排序方式,以及获取相关信息所耗费的时间。 

在 entop 运行状态下可以使用的控制命令: 
[1-N]: 根据指定列编号进行输出内容排序。第一列编号为 1 ,其他列按顺序增加。 
r: 在升序排序和降序排序之间进行切换。 
q: 从 entop 中退出返回 shell 命令行。 
Ctrl-C: 等价于 q 命令。 
 和  : 将当前排序列左移或者右移(注意:次数为小于和大于号,非箭头) 

---------- 我是三月份发版本天天加班的分隔线 ----------- 

      按照 README.md 中的说明 “entop 的正常运行在 pre-R15 情况下需要 cecho 0.3.0 的支持,在 R15 或更高版本的情况下需要 cecho 0.4.0 的支持” 做了如下配置变更。 
[root@Betty entop]# vi rebar.config 

{erl_opts, [fail_on_warning, debug_info]}.

{deps_dir, "deps"}.

{clean_files, ["ebin/*.beam"]}.

{deps, [{cecho, ".*", {git, "https://github.com/mazenharake/cecho.git", {tag, "0.4.0"}}}]}. -- 这里由原来的 "HEAD" 变更为 {tag, "0.4.0"}

{escript_name, "rebar_tmp"}.
编译 
[root@Betty entop]# ./rebar compile

== cecho (compile)

Compiled src/cecho.erl

Compiled src/cecho_srv.erl

Compiled src/cecho_example.erl

Compiling c_src/cecho.c

== entop (compile)

Compiled src/entop_collector.erl

Compiled src/entop_net.erl

Compiled src/entop.erl

Compiled src/entop_format.erl

Compiled src/entop_view.erl

[root@Betty entop]#
通过 entop 连接到 RabbitMQ 进程进行查看 
[root@Betty entop]# ./entop rmq_betty@Betty -sname entop

=INFO REPORT==== 9-Mar-2016::13:58:43 ===

 application: cecho

 exited: {{driver_error,"undefined symbol: scrollok"}, -- 问题出在这里

 {cecho,start,[normal,[]]}}

 type: temporary


[root@Betty entop]#
通过挂起回到前台,查看 entop 相关进程运行情况,并强杀 
[root@Betty entop]# ps aux|grep entop

root 31769 0.0 0.0 106092 1236 pts/2 T 13:58 0:00 /bin/bash ./entop rmq_betty@Betty -sname entop

root 31775 0.0 0.8 748760 33344 pts/2 Tl 13:58 0:00 /usr/local/lib/erlang/erts-6.0/bin/beam.smp -A 20 -Bc -- -root /usr/local/lib/erlang -progname erl -- -home /root -- -noshell -noinput -hidden -pa ./ebin -pa ./deps/cecho/ebin -eval entop:start(rmq_betty@Betty) -sname entop

root 31919 0.0 0.0 103252 856 pts/2 S+ 14:04 0:00 grep entop

[root@Betty entop]# 

[root@Betty entop]# 

[root@Betty entop]# kill -9 31769 31775
可以看到,按照上面的操作,我们失败了,排查错误的原因,我 查看了 cecho 的代码。 

在 cecho_srv.erl 中 
init(no_args) - 

 process_flag(trap_exit, true),

 case load_driver() of

 ok - 

 Port = erlang:open_port({spawn, "cecho"}, [binary]),

 ok = do_call(Port, ?INITSCR),

 ok = do_call(Port, ?WERASE, 0),

 ok = do_call(Port, ?REFRESH),

 {ok, #state{ port = Port }};

 {error, ErrorCode} - 

 exit({driver_error, erl_ddll:format_error(ErrorCode)}) -- 可以看出,上面的错误信息来自这里

 end.

load_driver() - 

 Dir = case code:priv_dir(cecho) of

 {error, bad_name} - 

 filename:dirname(code:which(?MODULE)) ++ "/../priv";

 D - 

 end,

 erl_ddll:load(Dir, "cecho"). -- 其他代码都不会出错,只能是这里

查看手册,针对 erl_ddll/load/2 有如下说明 

---------- 我是三月份发版本天天加班的分隔线 -----------

(以下内容翻译自 kernel-2.15.2)

load(Path, Name) - ok | {error, ErrorDesc}
Types: 
Path = path() 
Name = driver() 
ErrorDesc = term()

加载并链接名为 Name 的动态 driver 。Path 为包含该 driver 的目录。Name 指定的对象必须为共享对象或动态链接库。 
若两个 driver 具有不同的 Path 参数(即在不同路径下),则无法通过相同的 Name 进行加载。 

Name 的值对应 Path 目录下的动态加载对象文件,但是去除了扩展名(例如,移除了 .so 后缀)。 
在 driver 初始化函数中指定的 driver 名字方式,在很大程度上,与指定对应了 .beam 文件的 erlang 模块名一样。 

如果对 driver 执行了卸载动作,但由于 port 仍旧处于 open 状态,故此 driver 实际上仍旧存在,此时若调用 load/2 ,则会停止针对 driver 的卸载行为,使得该 driver 得以保留(只要 Path 没有发生过变更),并会返回 ok 。 
如果确实打算重新加载对象代码(driver),则可以使用 reload/2 或者底层的接口 try_load/3 进行操作。 
针对不同的场景下的加载/卸载行为描述请参考具体说明。 

如果超过一个进程想要使用相同的 Path 加载一个已经加载过的 driver ,或者如果相同的进程想要加载同一个 driver 多次,该函数调用都会返回 ok 。 
模拟器会跟踪 load/2 被调用的次数,以便在相同数量的 unload/2 被调用后才真正卸载该 driver 。 
如此,才能保证一个应用安全的加载一个 driver ,无论该 driver 是在多 erlang 进程间共享,还是在多 erlang 应用间共享。同样能保证 driver 的安全卸载,而不会对系统的其他部分产生影响。 

以相同的 Name 但不同的 Path 加载多个 driver 是不允许的; 

注意: 
需要注意的是,Path 参数的值是按字面量解析的,所以针对统一 driver 的多次加载都需要指定具有相同字面量的 Path 字符串,即使不同的路径表达均指向相同的文件系统目录也不行(比如使用相对路径或链接的情况)。 

函数执行成功后返回 ok ;函数执行失败后返回 {error, ErrorDesc} ,其中 ErrorDesc 为 opaque term ,可以通过 format_error/1 翻译成人可读的格式。 
若希望对错误处理有更多控制,则需要使用 try_load/3 接口。 
该函数会在入口参数不符合要求的情况下,抛出 badarg 异常。 

---------- 我是三月份发版本天天加班的分隔线 ----------- 

根据上述信息,查看源码目录 priv 下,会在编译 cecho 后生成的 cecho.so 文件 
[root@Betty priv]# ll

总用量 132

-rwxr-xr-x 1 root root 135113 3月 9 13:57 cecho.so

[root@Betty priv]# nm -C cecho.so |grep scrollok

00000000000042ed T do_scrollok

 U scrollok

[root@Betty priv]#
我擦,果然其中没有定义 scrollok 符号……Erlang 果不欺我~~~ 

ok ,变回原始配置再再做一次挑战... 
[root@Betty entop]# vi rebar.config 

{erl_opts, [fail_on_warning, debug_info]}.

{deps_dir, "deps"}.

{clean_files, ["ebin/*.beam"]}.

%%{deps, [{cecho, ".*", {git, "https://github.com/mazenharake/cecho.git", {tag, "0.4.0"}}}]}.

{deps, [{cecho, ".*", {git, "https://github.com/mazenharake/cecho.git", "HEAD"}}]}.

{escript_name, "rebar_tmp"}.


drwxr-xr-x 1 root root 4096 3月 9 13:57 ebin -rwxr-xr-x 1 root root 1723 3月 9 10:16 entop -rwxr-xr-x 1 root root 10175 3月 9 10:16 LICENSE -rwxr-xr-x 1 root root 132 3月 9 10:16 NOTICE -rwxr-xr-x 1 root root 3410 3月 9 13:53 README.md -rwxr-xr-x 1 root root 114109 3月 9 10:16 rebar -rwxr-xr-x 1 root root 302 3月 9 14:48 rebar.config drwxr-xr-x 1 root root 4096 3月 9 10:18 src [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# rm -rf deps/ [root@Betty entop]# ll 总用量 141 drwxr-xr-x 1 root root 4096 3月 9 13:57 ebin -rwxr-xr-x 1 root root 1723 3月 9 10:16 entop -rwxr-xr-x 1 root root 10175 3月 9 10:16 LICENSE -rwxr-xr-x 1 root root 132 3月 9 10:16 NOTICE -rwxr-xr-x 1 root root 3410 3月 9 13:53 README.md -rwxr-xr-x 1 root root 114109 3月 9 10:16 rebar -rwxr-xr-x 1 root root 302 3月 9 14:48 rebar.config drwxr-xr-x 1 root root 4096 3月 9 10:18 src [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# ./rebar get-deps == entop (get-deps) Pulling cecho from {git,"https://github.com/mazenharake/cecho.git","HEAD"} 正克隆到 cecho... == cecho (get-deps) [root@Betty entop]# [root@Betty entop]# ./rebar clean == cecho (clean) == entop (clean) [root@Betty entop]# [root@Betty entop]# ./rebar compile == cecho (compile) Compiled src/cecho.erl Compiled src/cecho_srv.erl Compiled src/cecho_example.erl Compiling c_src/cecho.c == entop (compile) Compiled src/entop_collector.erl Compiled src/entop_net.erl Compiled src/entop.erl Compiled src/entop_format.erl Compiled src/entop_view.erl [root@Betty entop]# [root@Betty entop]# ll deps/cecho/priv/ 总用量 132 -rwxr-xr-x 1 root root 135145 3月 9 14:51 cecho.so [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# nm -C deps/cecho/priv/cecho.so |grep scrollok 00000000000042fd T do_scrollok U scrollok [root@Betty entop]# [root@Betty entop]# ./entop Usage: ./entop TARGETNODE [-name NAME |-sname SNAME ] [-setcookie COOKIE ] [root@Betty entop]# ./entop rmq_betty@Betty -sname entop {error_logger,{{2016,3,9},{14,53,0}},"Protocol: ~tp: the name entop@Betty seems to be in use by another Erlang node",["inet_tcp"]} {error_logger,{{2016,3,9},{14,53,0}},crash_report,[[{initial_call,{net_kernel,init,[Argument__1]}},{pid, 0.21.0 },{registered_name,[]},{error_info,{exit,{error,badarg},[{gen_server,init_it,6,[{file,"gen_server.erl"},{line,322}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}},{ancestors,[net_sup,kernel_sup, 0.10.0 ]},{messages,[]},{links,[#Port 0.190 , 0.18.0 ]},{dictionary,[{longnames,false}]},{trap_exit,true},{status,running},{heap_size,376},{stack_size,27},{reductions,735}],[]]} {error_logger,{{2016,3,9},{14,53,0}},supervisor_report,[{supervisor,{local,net_sup}},{errorContext,start_error},{reason,{EXIT,nodistribution}},{offender,[{pid,undefined},{name,net_kernel},{mfargs,{net_kernel,start_link,[[entop,shortnames]]}},{restart_type,permanent},{shutdown,2000},{child_type,worker}]}]} {error_logger,{{2016,3,9},{14,53,0}},supervisor_report,[{supervisor,{local,kernel_sup}},{errorContext,start_error},{reason,{shutdown,{failed_to_start_child,net_kernel,{EXIT,nodistribution}}}},{offender,[{pid,undefined},{name,net_sup},{mfargs,{erl_distribution,start_link,[]}},{restart_type,permanent},{shutdown,infinity},{child_type,supervisor}]}]} {error_logger,{{2016,3,9},{14,53,0}},crash_report,[[{initial_call,{application_master,init,[Argument__1,Argument__2,Argument__3,Argument__4]}},{pid, 0.9.0 },{registered_name,[]},{error_info,{exit,{{shutdown,{failed_to_start_child,net_sup,{shutdown,{failed_to_start_child,net_kernel,{EXIT,nodistribution}}}}},{kernel,start,[normal,[]]}},[{application_master,init,4,[{file,"application_master.erl"},{line,133}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}},{ancestors,[ 0.8.0 ]},{messages,[{EXIT, 0.10.0 ,normal}]},{links,[ 0.8.0 , 0.7.0 ]},{dictionary,[]},{trap_exit,true},{status,running},{heap_size,376},{stack_size,27},{reductions,117}],[]]} {error_logger,{{2016,3,9},{14,53,0}},std_info,[{application,kernel},{exited,{{shutdown,{failed_to_start_child,net_sup,{shutdown,{failed_to_start_child,net_kernel,{EXIT,nodistribution}}}}},{kernel,start,[normal,[]]}}},{type,permanent}]} {"Kernel pid terminated",application_controller,"{application_start_failure,kernel,{{shutdown,{failed_to_start_child,net_sup,{shutdown,{failed_to_start_child,net_kernel,{EXIT,nodistribution}}}}},{kernel,start,[normal,[]]}}}"} Crash dump was written to: erl_crash.dump Kernel pid terminated (application_controller) ({application_start_failure,kernel,{{shutdown,{failed_to_start_child,net_sup,{shutdown,{failed_to_start_child,net_kernel,{EXIT,nodistribution}}}}},{k Something wrong. Code: 1 [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# ps aux|grep entop root 463 0.0 0.0 103252 840 pts/2 S+ 14:53 0:00 grep entop root 32043 0.0 0.0 106092 1236 pts/2 T 14:06 0:00 /bin/bash ./entop rmq_betty@Betty -sname entop root 32049 0.0 0.9 748760 34824 pts/2 Tl 14:06 0:00 /usr/local/lib/erlang/erts-6.0/bin/beam.smp -A 20 -Bc -- -root /usr/local/lib/erlang -progname erl -- -home /root -- -noshell -noinput -hidden -pa ./ebin -pa ./deps/cecho/ebin -eval entop:start(rmq_betty@Betty) -sname entop [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# kill -9 32043 32049 [root@Betty entop]# [1]+ 已杀死 ./entop rmq_betty@Betty -sname entop [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# ps aux|grep entop root 467 0.0 0.0 103252 844 pts/2 S+ 14:53 0:00 grep entop [root@Betty entop]# [root@Betty entop]# [root@Betty entop]# ./entop rmq_betty@Betty -sname entop Node: rmq_betty@Betty (Connected) (17/6.0) unix (linux 2.6.32) CPU:4 SMP +A:30 +K Time: local time 14:53:34, up for 000:22:27:49, 3ms latency, Processes: total 189 (RQ 0) at 124011 RpI using 13860.0k (13892.3k allocated) Interval 1000ms, Sorting on "Reductions" (Descending), Retrieved in 4ms 1170.1k Pid Registered Name Reductions MQueue HSize SSize HTot 0.179.0 vm_memory_monitor 77427487 06772 97382 0.230.0 background_gc 59304009 0233 7233 0.282.0 rabbit_mgmt_db 44805986 02586 74184 0.208.0 rabbit_memory_monito 31871301 04185 74561 0.243.0 rabbit_mgmt_external 31362904 06772 913544 0.256.0 rabbit_web_dispatch_ 14034430 010958 921916 0.182.0 rabbit_disk_monitor 752889602586 94184 0.25.0 file_server_2 70139900376 9752 0.3.0 erl_prim_loader42681980987 6987 0.7.0 application_controll 282319702586 76771 0.180.0 timer_server 228609901598 91974 0.276.0 - 13847170376 13 752 0.273.0 - 13844820376 13 752 0.275.0 - 13842510376 13 752 0.266.0 - 13838640376 13 752 0.265.0 - 13837490376 13 752 0.268.0 - 13837340376 13 752 0.262.0 - 13836540376 13 752 0.261.0 - 13836280376 13 752 0.267.0 - 13836160376 13 752 0.264.0 - 13835610376 13 752 0.263.0 - 13834900376 13 752 0.270.0 - 13834830376 13 752 0.271.0 - 13828050376 13 752 0.272.0 - 13825730376 13 752 0.269.0 - 13824750376 13 752 0.274.0 - 13823870376 13 752 0.0.0 init 12066000987 2987 0.184.0 os_cmd_port_creator 8509590610 1986 0.148.0 file_handle_cache 8355150610 7986 0.12.0 rex 8075230610 9986 0.26.0 code_server 804759017731 317731 0.173.0 rabbit_event 76952902586 82962 0.177.0 rabbit_alarm 3456710376 8752 0.66.0 mnesia_recover2134950233 9233 0.187.0 rabbit_node_monitor 1169900376 9376 0.21.0 net_kernel 99217 0376 9376 0.145.0 rabbit 77518 0233 5233 0.8965.0 - 68682 0233 11 233 0.11.0 kernel_sup 67885 0376 9376 [root@Betty entop]# ogger 55368 0610 8610 [root@Betty entop]#
整个过程下来,没发现有啥具体区别啊!!如何破?! 

      在没有其他思路的请款下,就让我们简单粗暴一点吧,直接拉下来 master 和 0.4.0 两个版本的代码进行比较~~ 
 
 
 
结果很明显,只有 rebar.config 中的不同才是问题关键。 
{port_envs, [{"LDFLAGS", "$LDFLAGS -lncurses"}]}.
这条配置信息从字面上就可以理解,LDFLAGS 是用来设置 link 选项的,所以上面是指定了对 ncurses 库的链接依赖。 

回头再看依赖 cecho.so 的库依赖关系(之前少看了该信息,5555...) 
[root@Betty priv]# ldd cecho.so 

 linux-vdso.so.1 = (0x00007fff10eee000)

 libncurses.so.5 = /lib64/libncurses.so.5 (0x00007fa6c1aef000)

 libc.so.6 = /lib64/libc.so.6 (0x00007fa6c175b000)

 libdl.so.2 = /lib64/libdl.so.2 (0x00007fa6c1556000)

 libtinfo.so.5 = /lib64/libtinfo.so.5 (0x00007fa6c1335000)

 /lib64/ld-linux-x86-64.so.2 (0x000000388c400000)

[root@Betty priv]#
而在引用 0.4.0 版本的 cecho 时,信息如下 
[root@Betty entop]# ldd deps/cecho/priv/cecho.so 

 linux-vdso.so.1 = (0x00007fff8f0c5000)

 libc.so.6 = /lib64/libc.so.6 (0x00007f4fb65bd000)

 /lib64/ld-linux-x86-64.so.2 (0x000000388c400000)

[root@Betty entop]#
果然存在差别!这也就解释了为何 scrollok 符号在两次结果中虽然都是 U 状态,但基于 master 的编译却可用的原因,因为 scrollok 符号在 libncurses.so.5 中~~ 
[root@Betty entop]# nm -D /lib64/libncurses.so.5.7 | grep scrollok 

000000389940a9b0 T is_scrollok

0000003899412980 T scrollok

[root@Betty entop]#
手动在 0.4.0 版本的 cecho 的 rebar.config 文件中添加 {port_envs, [{"LDFLAGS", "$LDFLAGS -lncurses"}]}. 后,重新编译运行,一切正常~~ 



Go语言基础系列博客用到的所有示例代码 上一篇文章 主要学习了Go语言的切片以及Map。本篇文章主要学习的是Go语言中的接口、反射以及错误和异常处理。 回想一下,在Java中,有父类和子类的说法,父类主要是提供抽象的公共方法让子类基于此拓展自身的功能。
Go语言基础系列博客用到的所有示例代码 上一篇文章 主要学习了Go语言的结构体以及结构体指针,本篇文章主要学习Go语言的切片以及Map。 Go语言数组的长度不可改变,但是这在一些特定场景中就不太适用,比如我们现在有一款电商商品,想统计用户的消费记录(每个用户的消费记录可能不一样,有的一天几十单有的一个季度就几单)Go语言团队基于此中提供了相较于一种灵活,功能强悍的内置类型切片( 也称动态数组 ,这个可以理解为Java的List)。
Go语言基础系列博客用到的所有示例代码 在上一篇文章 中,主要学习了Go语言的算术运算符、关系运算符 、逻辑运算符 、赋值运算符以及运算符优先级,本篇文章主要学习Go语言的条件判断语句以及循环语句。
Go语言基础系列博客用到的所有示例代码 在 上一篇文章 中,主要学习了Go语言的条件判断语句以及循环语句,本篇文章主要学习Go语言的函数定义、值传递和引用传递以及闭包函数。
Go语言基础系列博客用到的所有示例代码 在 上一篇文章 主要学习了Go语言的函数定义、值传递和引用传递以及闭包函数。本篇文章主要介绍的是Go 语言变量以及变量的作用域 Go 语言中变量跟Java的变量一样,它可以在三个地方声明: A:函数内定义的变量称为局部变量 B:函数外定义的变量称为全局变量 C:函数定义中的变量称为形式参数 说完了变量我们在说说变量的作用域。
Go语言基础系列博客用到的所有示例代码 在上一篇文章 中,主要学习了Go语言的变量、指针、指针变量、常量以及类型转换。这一篇主要学习Go语言中的多种运算符。
Go语言基础系列博客用到的所有示例代码 在 上一篇文章 中,我们主要学习了Go语言的编程基础。这些基础内容包括注释、分隔符、标识符、空格、包结构、语法常用规则、数据类型等。
Go语言基础系列博客用到的所有示例代码 本篇文章主要的介绍的是,Go语言环境配置,IDE的安装及使用(编码工具),简单的效果展示。 目前,Go语言支持以下系统:Linux、Mac 、Windows  笔者是基于Windows平台进行开发的,所以开发环境和IDE都是基于Windows的。
Go语言是什么? Go语言是谷歌2009年发布的第二款开源编程语言。(也就是说谷歌大帝是这们语言的后台。。。) Go语言专门针对多处理器应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。