平滑的基于权重的轮询算法

轮询算法是非常常用的一种调度/负载均衡的算法。依照百度百科上的解释:

Round-Robin,轮询调度,通信中信道调度的一种策略,该调度策略使用户轮流使用共享资源,不会考虑瞬时信道条件。从相同数量无线资源(相同调度时间段)被分配给每条通信链路的角度讲,轮询调度可以被视为公平调度。然而,从提供相同服务质量给所有通信链路的角度而言,轮询调度是不公平的,此时,必须为带有较差信道条件的通信链路分配更多无线资源(更多时间)。此外,由于轮询调度在调度过程中不考虑瞬时信道条件,因此它将导致较低的整体系统性能,但与最大载干比调度相比,在各通信链路间具有更为均衡的服务质量。

更广泛的轮询调度应用在广度的服务调度上面,尤其在面向服务或者是面向微服务的架构中,比可以在很多知名的软件中看到它的身影,比如LVS、Nginx、Dubblo等。但是正如上面的百度百科中的介绍一样,轮询调度有一个很大的问题,那就是它认为所有的服务的性能都是一样的,每个服务器都被公平的调度,在服务器的性能有显著差别的环境中,性能比较差的服务器被调度了相同的次数,这不是我们所期望的。所以本文要介绍的是加权的轮询算法,轮询算法可以看成是加权的轮询算法的一个特例,在这种情况下,每个服务器的权重都是一样的。

本文介绍了Nginx和LVS的两种算法,比较了它们的优缺点,并提供了一个通用的 Go 语言实现的加权轮询算法库: weighted,可以用在负载均衡/调度/微服务网关等场合。

阅读全文

微服务的反模式和陷阱

前几天我写了篇读书笔记: 《产品级微服务的八大原则》,介绍了Uber的SRE工程师 Susan J. Fowler 的免费书: Microservices in Production,文中提出了一个微服务成功与否的唯一标准就是可用性,非常有实践意义。但是这本书偏向于从 SRE (site reliability engineer)的视角看待微服务,对于开发工程师 (SWE, software engineer)来说,更关注的是如何正确地从单体程序重构到微服务架构,或者从头设计微服务架构, 这篇读书笔记主要就是介绍这方面的实践和经验。

Oreilly 的 的这本免费小书 Microservices AntiPatterns and Pitfalls由经验丰富的 Mark Richards 编写。书中将反模式(AntiPattern)定义为"起初看起来很美好,做到最后麻烦不断的实践模式",而将陷阱(Pitfall)定义为“起初看起来就不是一个好的设计”,书中列举了微服务开发中几种常见的反模式和陷阱,这些经验非常的接地气.他还提供了视频教程

阅读全文

优雅地实现 TCP 压缩传输

集群式、负载均衡的RPC框架 rpcx支持多种的序列化库,可以有效的减少消息体的大小,但是对于字符串或者图片的字节slice,明显还可以进一步的压缩,正如fasthttp作者valyala在他的新的开源项目httpteleport中描述的: 通过1G的带宽传输10G的数据 (夸张)。

为了在RPC的传输中减少传输的数据大小,我在不影响rpcx整体框架的基础上,参考了httpteleport的实现,对net.TCPConn进行了封装,实现了压缩/解压缩功能的net.Conn,可以有效的减少带宽,节省公司在带宽上的花费, 以下就是具体的实现。

阅读全文

Go的函数参数总是传值

依照Go的FQA, 函数的参数传递总是传值的(pass-by-value):

As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to. (See a later section for a discussion of how this affects method receivers.)

Map and slice values behave like pointers: they are descriptors that contain pointers to the underlying map or slice data. Copying a map or slice value doesn't copy the data it points to. Copying an interface value makes a copy of the thing stored in the interface value. If the interface value holds a struct, copying the interface value makes a copy of the struct. If the interface value holds a pointer, copying the interface value makes a copy of the pointer, but again not the data it points to.

阅读全文

深入Go UDP编程

用户数据报协议(User Datagram Protocol,缩写为UDP),又称用户数据报文协议,是一个简单的面向数据报(package-oriented)的传输层协议,正式规范为RFC 768。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以UDP有时候也被认为是不可靠的数据报协议)。UDP在IP数据报的头部仅仅加入了复用和数据校验。

由于缺乏可靠性且属于非连接导向协议,UDP应用一般必须允许一定量的丢包、出错和复制粘贴。但有些应用,比如TFTP,如果需要则必须在应用层增加根本的可靠机制。但是绝大多数UDP应用都不需要可靠机制,甚至可能因为引入可靠机制而降低性能。流媒体(流技术)、即时多媒体游戏和IP电话(VoIP)一定就是典型的UDP应用。如果某个应用需要很高的可靠性,那么可以用传输控制协议(TCP协议)来代替UDP。

由于缺乏拥塞控制(congestion control),需要基于网络的机制来减少因失控和高速UDP流量负荷而导致的拥塞崩溃效应。换句话说,因为UDP发送者不能够检测拥塞,所以像使用包队列和丢弃技术的路由器这样的网络基本设备往往就成为降低UDP过大通信量的有效工具。数据报拥塞控制协议(DCCP)设计成通过在诸如流媒体类型的高速率UDP流中,增加主机拥塞控制,来减小这个潜在的问题。
典型网络上的众多使用UDP协议的关键应用一定程度上是相似的。这些应用包括域名系统(DNS)、简单网络管理协议(SNMP)、动态主机配置协议(DHCP)、路由信息协议(RIP)和某些影音流服务等等。

UDP报头
偏移字节0123
字节 0 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031
00来源连接端口目的连接端口
432报长检查码

IPv4伪头部

0 – 78 – 1516 – 2324 – 31
0来源地址
32目的地址
64全零协议名UDP报文长度
96来源连接端口目的连接端口
128报文长度检验和
160+ 
数据
 

IPv6伪头部

0 – 78 – 1516 – 2324 – 31
0来源地址
32
64
96
128目的地址
160
192
224
256UDP报文长
288全零下一个指针位置
320来源连接端口目的连接端口
352报文长校验和
384+ 
数据
 

以上大段的背景介绍引自维基百科。

而TCP是面向连接(connection-oriented)的协议,可以提供可靠的数据传输。

本文讲介绍Go语言的UDP库及其使用方法,以及了解使用过程中的细节和陷阱。

阅读全文

godef的一个bug

godef可以打印出Go源代码中的符号,很多集成开发工具都集成了它,并且提供了Go to Definition的命令和快捷键,可以迅速的跳转到方法和变量的声明的地方,非常的方便。

比如我常用的vscode-go,就提供了这样的功能,但是目前godef有一个bug,就是不能跳转标准库net包下的方法定义,这个bug已经提交到github上: #18, 大约在6月的时候就发现了这个bug,不清楚作者为什么没有fix,事实上早有人提供了修改的pull request。这个bug产生的原因是godef没有处理好"C"库。

我在这里把修改方法记录一下,你可以手工的修改编译godef。

找到godef文件夹下的parser文件(go/parser/parser.go),在 1970行增加一个判断。

1
2
3
4
5
6
7
name, err := p.pathToName(litToString(path), filepath.Dir(filename))
if litToString(path) == "C" {
//下面三行新为修改这个bug新增加的行
if name == "" && litToString(path) == "C" {
name = "C"
}
if name == "" {

这样你的godef就可以正常工作了。

另外,如果你升级到Go1.7发现vscode代码自动完成功能不起作用的话,可以尝试在终端中执行gocode close,这个bug可以参考:441

update on 2017-03-16:
查看相关的isses: #856#496#662#440 #442 #515 #567 #459 #517 #667

#706

你可以使用gogetdoc代替godef,在配置中加上 "go.docsTool": "gogetdoc"

或者使用实验性的go-languageServer: "go.useLanguageServer": true

Go 模板嵌套最佳实践

Go 官方库提供了两个模板库: text/templatehtml/template。这两个库类似,只不过 html/template对html格式做了特别的处理,当需要输出html格式的代码时需要使用html/template

使用模版,可以帮助我们写一些通用的代码,或者提供清晰的文件布局, 或者提供一个代码生成器。

官方文档提供了很好的模版的使用方法, 其中 text/template提供了基础的模版的使用方法,比如 Action、 Argument、Pipeline、Variable、Function、模版嵌套的介绍, html/template对 Context 进行了介绍。 本文假定你已经了解了这些基础知识。如果你还不清楚,或者还没有用过模版,可以参考文末的参考文档进行学习。

虽然text/template官方文档对模版嵌套简单了介绍,但是对于如何使用嵌套模版进行实际开发,以及注意事项并没有详细的介绍,所以本文着重介绍嵌套模版的使用。

阅读全文

Go官方库RPC开发指南

Go官方提供了一个RPC库: net/rpc。包rpc提供了通过网络访问一个对象的方法的能力。服务器需要注册对象, 通过对象的类型名暴露这个服务。注册后这个对象的输出方法就可以远程调用,这个库封装了底层传输的细节,包括序列化。服务器可以注册多个不同类型的对象,但是注册相同类型的多个对象的时候回出错。

我最近写了一本电子书: Go RPC开发指南,介绍Go RPC开发的相关技术,这是其中的一章,专门介绍官方库 net/rpc的使用。

阅读全文