Skip to content

如何使用Wireshark对远程服务器数据进行分析

抓包对于后端工程师来说就是家常便饭的技巧,包分析软件里最牛B的是Wireshark,可是Wireshark只能在有X环境的机子上运行,当我们想知道服务器上的情况,又想用Wireshark该怎么办呢?
@MatheMatrix指出用管道就好了

ssh username@domain "sudo tcpdump -s 0 -U -n -i eth0 not port 22 -w -" | wireshark -k -i -

-–原文章—-

1. 在本地运行 mkfifo /tmp/remote 这样就创建了一个先进先出的文件

2. 在本地运行

ssh username@domain "sudo tcpdump -s 0 -U -n -w - -i eth0 not port 22" > /tmp/remote

将远端服务器的数据通过管道输送到刚才我们建立的文件中 tcpdump的规则里将22端口去掉了,因为我们传输的方法是ssh

3. 在本地运行 wireshark -k -i /tmp/remote 开启wireshark!

如何让Go程序更快

本文为部分翻译、整理。 原文为Go的开发者之一的Dave Cheney所做的 Five things that make go
fast

清晰赋值类型

例如,有一个绝对不会超过uint32的数值,就不要用int var gocon uint32 = 2015 这样gocon这个值只会占用4个字节
为啥呢? 因为如下图,CPU的处理速度已经远超内存的总线速度了 Gocon-2014-10 所以数值能用小的用小的,尽量让数值留在CPU
cache,而不是速度更慢的内存里

函数调用有overhead,为了内联,尽量消除编译器无法侦测的dead code

当函数调用时,始终是由overhead(额外开销)的,比如保存调用栈,CPU切出。
因此编译器会尝试进行内联,将小函数直接复制并编译。
举个栗子:

func Max(a,b int) int {
if a > b {
return a
}
return b
}
func DoubleMax(a, b) int {
return 2 * Max(a,b)
}

-m 查看内联状态 $go build -gcflags=-m main.go # utils src/utils/max.go:4: can inline Max src/utils/max.go:11: inlining call to Max
这样做的代价是可执行的二进制文件更大了,但由于内联,并不是函数调用,性能自然是更好了。 但有些函数,是不能内联的,比如下面这个

func Test() bool {return False}
func Expensive() {
if test(){ //接下来的Expensive没办法内联
// Expensive....
}
}

改成下面这样就可以内联了

const TEST = False
func Expensive() {
if TEST{
// Expensive....
}
}

逃逸检查

首先,要理解一个概念,stack 和 heap Gocon-2014-24
stack作用域是本地的(locals),在函数执行完之后会自动收回,CPU控制,效率高 而heap则需要由程序来管理,效率低 具体有篇文章讲这个:
Memory stack vs
heap

因此,就算有GC,也应该把不需要传出的参数尽量控制在函数内。 例如下图的程序
Gocon-2014-26 因为numbers 只在
Sum中,编译器也会自动分配100个int空间在stack中,而不是heap中。 正因为在stack 中,所以不需要GC参与,自动收回。
但这不意味着不能用指针引用,见第二个栗子: Gocon-2014-27
尽管变量c是通过new函数生成的,但是因为在center外没有c的引用,所以c也会被存储在stack上。 逃逸检查实例:
Gocon-2014-28

Goroutine

第四个,我觉得是提示吧: Goroutine是比进程、线程都小的执行单元 Goroutine中的会被调度器(scheduler)切出的操作:

  1. chan 收发
  2. go 语句调用函数
  3. 阻塞的syscall
  4. gc

一图胜千言,下图表示了调度器是如何在goroutine之间切换的。 Gocon-2014-35
第五个算是对Go1.3以后的stack分配机制的总结,我就不翻译了,有兴趣的同学自己可以看看原文:)

总结

  1. 我想补充的是: 逃逸对于channel也是成立的,因此,在channel之间,最好传递的也是对象,而不是引用。这个问题上我栽过一次了哈哈哈
  2. 之前我并不知道内联是啥,Golang、pypy这样的JIT、或者cpython真的是减轻了我的心智负担,当然,了解一下也是很不错的。
  3. 清晰的赋值个人感觉不是很必要,因为Go的int类型最大是2**31-1,性能调优的时候再认真地梳理其实也来得及

如何压缩Golang 编译出的可执行文件大小

先给结论:可以减少到原来的29%

最近在写一个TLScat小工具
Github.com/mengzhuo/tlscat
源文件仅仅2KB不到,但是用 go build tlscat.go 编译出来的有4.6MB!

屏幕快照 2015-05-04 23.39.08

贴纸

后来发现这个Golang的1.5才会解决的问题 Issue #6853 all: binaries too big and
growing
可是,我就不信这个邪,于是搜索到了go
build的一些用法 go build -ldflags "-s -w" ‘-s’ 相当于strip掉符号表,
但是以后就没办法在gdb里查看行号和文件了。 ‘-w’ flag to the linker to omit the debug information
告知连接器放弃所有debug信息 这样一来就只有3MB了

贴纸

然后发现在Mac平台下,还有upx这样神一般的存在。

UPX achieves an excellent compression ratio and offers very fast
decompression.

简而言之,upx就是对可执行文件进行压缩,然后可以已极快的速度解压并运行

可以用brew快速安装upx brew install upx upx 可执行文件 屏幕快照 2015-05-0523.41.40

参加某开发比赛后记

前些日子参加了某云的开发大赛,结果出来了~ 屏幕快照 2015-05-04 16.19.27 仅仅拿了一个三等奖,略桑心。
参赛过程具体见《[如何一下午写3000行?记某云的Golang API
SDK生产过程](/%e5%a6%82%e4%bd%95%e4%b8%80%e4%b8%8b%e5%8d%88%e5%86%993000%e8%a1%8c%ef%bc%9f%e8%ae%b0%e6%9f%90%e4%ba%91%e7%9a
%84golang-api-sdk%e7%94%9f%e4%ba%a7%e8%bf%87%e7%a8%8b)》

不过伤心之余,我理性一把,看了看前4的分别都是啥: 特等奖是移动客户端的解决方案:mu
MU (Mobile UCloud) 是一个基于 UCloud API 的手机版管理工具……
成熟度已经和产品可以媲美了,也解决了在移动端大潮之下,官方对移动端支持几乎是空白的痛点,所以只需要签个协议就可以用的产品,拿奖不奇怪。
而三个一等奖里,有另一个产品: ESS_For_Ucloud
这货就相当于AWS里的ELB,同样是改改就能投入线上使用的产品。 解决了……呃……官方有LB,但是需要人工调整这样"不云"的尴尬。 第二个一等奖是:
Ucloud CLI命令行接口
回想我司的运维、SA们用来控制AWS的工具,不是web控制端,不是各种写好的SDK,而是AWS CLI,因为他们最熟悉的就是各种脚本。
这也正是比赛举办方所缺少的! 虽然命令覆盖得并不是很完善,但也算是补上了个自动化运维的坑。 最后一个一等奖是: Python
SDK

在我吐槽官方的SDK用了**params这样的动态语言特性时,聪明的人自然会想到,举办方自然希望修补官方的SDK也是比赛的侧重方向了嘛~

虽然,我粗浅地看下去,这个SDK也仅仅是修补了官方SDK调用时不严谨的尴尬,而且由于API这么多,自然覆盖得不全,但是也算抓住了举办方的心了。
公布之后在交流群里,不少人也吐槽自己UT覆盖率比得上面的都高,为啥只能拿二等甚至是三等。反过来想,真的算是程序员典型的思维了:自己做的程序、工具很"牛B",但是为啥没有人用,甚至是不受欢迎。
都是因为: 没有抓住痛点!没有抓住痛点!!没有抓住痛点!!! 所以,下次做东西的时候,得多问问自己:

需求是啥!?

如何一下午写3000行?记某云的Golang API SDK生产过程

近日参加了某云的SDK编写比赛,官方给的样例是个Python版,请求直接用**params这样方法构造请求体

暴走漫画-你他妈的在逗我

因为尼玛Golang是静态语言,我们严谨!没有可变参数!还没有默认值!!

所以我苦逼地写了一个多小时类似这样的代码:

func (c *Client) GetHostInstance(id string, option string, count int) {
// bla....
}

每个API一遍遍地重复,真是苦不堪言,啥时候是个头啊,人生苦短啊,早知道还用Python了…… 于是我去睡觉了,嗯 梦中Rob
Pike托梦
醒来,突然想到,几天前学习的Go
reflect库,可以反射出调用的type,这样我只要构造struct,然后遍历一下每一项,不就可以省了很多时间了么? 于是,我开始构造各种struct
type GetHostInstance struct{ id string option string count int }
从json库中直接拉了些代码来遍历NumField,顿时感觉自己棒棒的!! 但是,写了三个以后我突然发现API里有些参数是optional的!!!

20130422212028-23861660

幸好,Rob大大已经替我等想好了,那就是struct的TagField,于是我TM也构造了一个自己的解析器

type GetHostInstance struct{
id string `cloud:"optional"`
option string
count int }
// .....解析器部分代码
// .....获取tag tag := typ.Field(i).Tag.Get("cloud")

取出tag值之后,依靠检查struct这项是否为nil来确定是否传不传参数!
终于可变参数这么蛋疼的事情都让我解决了,紧接着,我又写了3个struct,累得不行了……年纪轻轻就体力不支了
但是我决定写完去,因为这么漂亮的解决方案不用来拿个马克杯做奖品怎么行?!
于是又写了3个API,我发现我就是在复制粘贴官方的API文档,于是我祭出我的复制粘帖大法+Vim宏
顿时快了很多,变成5分钟一个API,我看了看文档里……TM的一共50多个接口!! 5*50 = 250 分钟 = 4个多小时 =
我都可以拿来看《黑鹰坠落》+ 5集《GTA 5搞笑视频》了!!
于是我开始无聊地点着官方API文档,我突然发现,官方的API文档是用Sphinx写的,只是套了层自己的css而已 –>
因为我的请求体都只是struct,官方还把请求的类型都已经标注好了,这时耳边响起了:人生苦短,我用Python 的标语
我可以用python写个程序,将官方文档直接转成SDK 代码啊!!! 于是,说干就干!! 找了requests等库,人生顿时快乐了很多有没有~
大法 壮观地生成,连注释都有( ̄▽ ̄) 哈哈哈

生成器代码:https://gist.github.com/mengzhuo/f1b07decb69eea6e7dab

生成好的SDK:https://github.com/mengzhuo/ucloud-go-sdk

涠洲岛之旅

春节去了趟涠洲岛,今天才整理照片,发现除了发朋友圈,还是有些可以拿出来分享的。

涠洲岛火山岩海滩
火山岩海滩

涠洲岛 教堂 教堂

码头 码头

灯塔 灯塔

海滩日落 海滩日落

渔船船队 渔船船队

无名海滩 无名海滩

如何用ZeroMQ实现UDP组播Pub-Sub(基于Golang)

ZeroMQ有一对Pub/Sub socket 类型,但是网上的教程一般侧重于使用TCP版本的……
虽然TCP版本的也能组网,但是略显麻烦,今天我来给大家介绍一下基于PGM协议的ZeroMQ Pub/Sub模型 首先要编译安装OpenPGM brew install libpgm 接着是zmq brew install zmq --with-pgm 这样就准备好了环境了,
这里需要了解一下PGM网络的原理,很简单,如下图 Udp_encapsulated_ports
绿色的就是发送方,向目的组播地址239.192.0.1 端口3055(黑色粗线)发送数据,然后所有监听此端口的接收者(Receiver)都收到了。
就这么简单。 然后就是写代码咯:

soc = zmq.NewSocket(zmq.PUB)
soc.Connect("epgm://192.168.1.100;239.192.0.1:3055")
soc.SendMessage("Hi")

呃……192.168.1.100? 这个是pgm的特点,你需要指定发送组播包的网卡名,一般人记不住网卡名……所以用此网卡所持有的IP来标示。 p.s.

  1. PGM有个特点,就是发送方进行流量控制,zmq中使用的是setrate,切记在Connect之前使用
  2. zmq会整理包,所以再散的数据,都会组合成一个message发送出来(不愧是智能网络)
  3. 实际测试时……16Mbps的流量根本不是问题
  4. 需要debug PGM时export PGM_MIN_LOG_LEVEL=TRACE

Golang--新的方向?

![go-11-is-released_gopherbiplane5](/2014/12/go-11-is-
released_gopherbiplane5.jpg)

很早就听说了Golang的大名,只是远观(当时觉得太难了= =)
刚到公司的时候,研究过一段时间的coreos—-大部分都是Golang写的,还是觉得语法很啰嗦……
几个月之前,老大就跟我谈过新系统可能用Golang来写,弥补我司只有Python这条技术线的弊端。
所以买了个app学Golang,但是没有开始上手做东西,只是跟着教程了解了一些特性,然后就荒废了。

由于最近开始研究Python性能提升的可能,异步库gevent,直接的epoll,非拷贝的meoryview各种黑科技都用上,但是并发10K
rps实在是没办法跨过的一道坎,索性,反正都是epoll/kevent,直接换Golang试试。

写了3天之后,我发现要接触一门新的语言,还是直接写小项目来得快,我之前看的教程在这个时候虽然有帮助,但是还是没有自己写过记忆来得深。
越写越爽,直接写了一个玩具级的多聊天室的聊天程序。
github.com/mengzhuo/go-chatroom

几点感触:

  • Golang真的是侧重于服务端编程的语言,大部分语言哪个语法层面支持协程?
  • 有静态语言的优点,又有动态语言的编译速度和灵活度(和Python比)
  • 没了显形的OOP,我竟然很喜欢,原来很讨厌的特性之一,但是接触时间长了,Golang才是KISS,减少了多重继承导致的不确定性
  • 动态语言经常会在函数内部更改实参,静态不允许这个,逻辑上出错的概率降低了
  • share memory by communication用得好的话,能最大程度简化竞态(当然渣的话,就悲剧了),不过有race detector(太方便了!)
  • err起初真的很讨厌,但是后来发现,这里面就是KISS,如果和JS、Python这样,从中间直接raise,那么处理异常的函数就会特别复杂……还不如果简单地在边上就处理了~
  • 随便就用一个环境变量GOMAXPROCS就解决了利用CPU多核的问题,我们Python程序员各种泪目啊

Golang语言是真的为我们服务端程序员量身定制的!

2014年终总结

2014年 —- 变

职业上处于转型做游戏服务端的时期 感觉下来,Web服务端和游戏服务端最大的不同就是状态的维持和在线用户之间数据的共享和广播。

虽然学习了很多游戏服务端相关的知识,转型期还是有代价的,钱就不说了,web端学习到的很多知识基本就荒废了,特别是javascript相关的。
接触下来,我发现用什么语言并不是很重要了,毕竟后面的思路,算法,基本上是一样的。
书,基本没有看,但是我认为这不代表不学习,因为大部分时间都花在了学习读博客/文章/问答,写代码上了。

语言方面,没有进一步发掘Python的性能了,因为再深入,就是C了。学习了Golang,现在算是个初学者了吧。

还上线了一个"一次性邮箱" https://34nm.com 为啥叫这个奇怪的名字?
是因为@符号正好在34之前,nm正好在".“之前,就是这么简单的原因。 目前发送了451封邮件,阻止了893封,用户数166
作为一个小工具,我觉得达到了我当初100个用户的期望

同时,通过这个网站,学习到了很多TLS,加密相关的知识,SMTP指令相关的知识。
Github上记录的commit有149个,不过大部分都是给其他程序修修补补。

工具方面,换成了Mac,效率提升了不是一个档次。

今年你所完成的最重要的事情是什么? 完了……我觉得没有 今年你所学到的最有用的是什么? 花了3个星期学个Golang,太值当了

满分10分,你在这一年对自己的满意度有几分?

今年不及格,4分,应该花更多的时间来学习和整理知识,不能老是说各种借口的 你明年想要实现什么,要不要来个前所未有最棒的一年?
做一个手机App游戏,自娱自乐一下:)

#年终总结

Python标准库小窥[2]: memoryview

要讲memoryview,就要讲它的主要应用场合—-缓冲区(buffer)
要讲buffer前就要讲讲Python的字符处理:Python中的字符是不能修改的 例如
当你需要收集从socket.recv上来的代码时,大部分的人都选择了拼接字符串

while not self.sock.closed:
data = ''
while len(data) < 1024:
d = self.sock.recv(12)
if not d: return;
data += d

殊不知,由于Python字符串的特点,这实际上是将data复制一份,然后于d进行拼接,然后再赋值给data。
当字符大小有1M左右的时候,这种复制的效率会下降10%左右(其他大小没有测试过,但是应该是字符越大,复制的效率就越低,毕竟要重新分配内存区域并填满是很费事的)
而且网络传输有个特点,就是客户端想传多大都可以,所以就会出现只传输部分数据的情况,这时,socket.recv循环次数增多,导致字符缓冲区复制的操作次数增多。
所以,对于已知缓冲区大小下,可以先分配大小的缓冲区,然后依次写入相应数据即可(不需要复制了)。 改装上面的栗子,我们假定已知缓冲区应该有1KB

buf = bytearray(1024)
mv = memoryview(buf)
i = 0
while i < 1024:
i = self.sock.recv_into(mv)
if not i: return; # remote closed
if i < 1024:
mv = mv[i:1024]
i += i

即可