GoLang Martian lib 之应用解析(二)

GoLang Martian lib 之应用解析(二)

GoLang Martian lib 之应用解析(一)中,我们介绍了Martian lib提供的功能,同时对其使用做了简单的介绍,这篇文档将通过Martian lib的代码解析,来分析Martian lib是如何实现Proxy、Request、Response的Modifier的。

Martian代码结构

GoLang Martian lib 之应用解析(二)

GoLang Martian lib 之应用解析(一)中,我们介绍了Martian lib提供的功能,同时对其使用做了简单的介绍,这篇文档将通过Martian lib的代码解析,来分析Martian lib是如何实现Proxy、Request、Response的Modifier的。

Martian代码结构

在martian的根目录中,还有几个文件比较重要:

martian.go :package martian 提供了HTTP/1.1 proxy,通过API来配置Request和Response 的Modifier

init.go : maritan命令初始化参数

proxy.go : 通过 martian框架来实现的一个代理服务器

multierror.go:实现了 error 接口的一个 errors集合

context.go:提供了 Contex 和 Session模型

解析

martian框架提供了 Modifier 的插件机制,其实现标准是:在新的插件中实现这两个接口之一就可以了:

下面我们来看一个例子:

当然,一个Modifier如果要生效,需要在代码的 init func中对这个Modifier进行注册,比如:

在 varify package中,同样定义了另外一对接口,用来实现对Request和Response的verify操作:

如上,如果需要对Request和Response进行校验操作,可以通过实现上述接口来达成。

发表在 同步技术 | 留下评论

GoLang Martian lib 之应用解析(一)

前段时间阅读 KrakenD(https://www.krakend.io)的代码,发现KrakenD的代码中,使用了gin(https://gin-gonic.github.io/gin/)作为http(s)的route engine,使用了google发布的Martian (https://github.com/google/martian)作为proxy engine,结合前端时间写过的一个小业务层proxy项目,就非常感兴趣的对Martian做一些了解。

Martian(https://github.com/google/martian)是google发布的用来做proxy的lib:

Martian is a library for building custom HTTP/S proxies

那么Martian都支持什么样的功能呢?

从google发布这个lib的本意来讲,其目的是为了“testing”:

Martian Proxy is a programmable HTTP proxy designed to be used for testing.

Martian is a great tool to use if you want to:

  • Verify that all (or some subset) of requests are secure

  • Mock external services at the network layer

  • Inject headers, modify cookies or perform other mutations of HTTP requests and responses

  • Verify that pingbacks happen when you think they should

  • Unwrap encrypted traffic (requires install of CA certificate in browser)

By taking advantage of Go cross-compilation, Martian can be deployed anywhere that Go can target.

可以通过下面的方式来快速实现一个proxy的功能:

Start the Proxy

Assuming you've installed Martian, running the proxy is as simple as

By default, Martian will be running on port 8080, and the Martian API will be running on 8181 . The port can be specified via flags:

除此之外,Martian还支持HAR格式的logging

Logging

For logging of requests and responses a [logging modifier](https://github.com/google/martian/wiki/Modifier-Reference#logging) is available or [HAR](http://www.softwareishard.com/blog/har-12-spec/) logs are available if the -har flag is used.

最为强大的是,Martian支持强大的配置,在proxy的过程中,来修改request和response:

详情可以参考 https://github.com/google/martian/wiki/Modifier-Reference

最为可贵的是:

Martian can also be included into any Go program and used as a library. 这也是KrakenD选择Martian作为后端proxy的一个关键原因。

Modifiers All The Way Down

Martian's request and response modification system is designed to be general and extensible. The design objective is to provide individual modifier behaviors that can arranged to build out nearly any desired modification.

When working with Martian to compose behaviors, you'll need to be familiar with these different types of interactions:

  • Modifiers: Changes the state of a request or a response

  • Filters: Conditionally allows a contained Modifier to execute

  • Groups: Bundles multiple modifiers to be executed in the order specified in the group

  • Verifiers: Tracks network traffic against expectations

Modifiers, filters and groups all implement RequestModifer, ResponseModifier or RequestResponseModifier (defined inmartian.go).

这是一个非常有价值的功能,对于部分中台业务来讲,对请求以及响应内容的过滤、修改、组合等逻辑往往会是强需求,在不修改中台后端服务模型(请求、响应)的情况下,对中台接口输出结果进行调整,只输出必要的内容,或是通过修正响应内容,兼容前端的需求。

上面是对 martian lib 的简单解析,更多的功能需要深入到文档中进行挖掘。

发表在 GoLang, MircroService | GoLang Martian lib 之应用解析(一)已关闭评论

PHP micro service服务治理与监控

这是一篇相对古老的文章,大概两三年前整理的,一直没有发出来。酌情参考。

基本价值 & 解决的问题

1,PHP中资源的定义,注册和发现

 

2,PHP中定义资源的方式,自然对于多IDC部署来讲是个难题。常见的解决办法是:

 

3,与监控的配合,清晰化资源依赖图谱,可以更好的实现报警信息的收敛,更快的发现和定位问题,解决监控报警、问题定位时,混乱的寻找监控metric的状况,更快的评估问题的影响度,对问题响应提供更好的决策依据

使用机制

1,系统级别资源 (MySQL/Redis 等)由相应的平台接口或插件提供注册

2,业务级别的接口的注册和发现可由业务接口提供方使用插件的方式进行注册

服务定义 & 服务监控之痛

与DNS进行服务注册、发现的优势劣势

dns服务发现的问题

1,dns的注册、发现服务非常成熟、稳定、高效,其协议、实现环节,提供了完善的机制,包括自动注册的api、包括dns协议中专为服务发现所提供的SRV记录类型,以及在高频访问下,dnsmasq提供的高性能的本地dns cache机制。

2,dns协议作为互联网最基础的发现协议,其用途广泛,遍布互联网的所有角落,其采用UDP协议使得传输更佳高效。

但是:

3,dns协议作为服务发现,只能提供类似的机制:

 

4,无法提供URL接口的发现,尽管URL接口的变更会比较少,并且往往会通过域名的方式提供

5,无论是系统级资源还是接口级资源,如果需要修改,那么就需要在代码中进行修改

我们知道,一个业务接口,运行在服务器上,可能会有不同的接口运行在不同的服务器,也有可能运行的相同的服务器上。并且,这些接口以及服务依赖于服务器操作系统的很多因素,比如CPU、内存、网卡,在一个相对稳定的业务上,一个接口、服务出现问题,往往会系统的因素发生了变化,或是请求量发生了变化所引起的。正是由于这些系统因素与业务/服务的关系,因此,我们在监控过程中,往往会同时收到业务问题报警和系统的问题报警,比如MySQL的慢查询变多,往往是由于磁盘能力不满足引起的,如果同时发送磁盘繁忙以及MySQL的报警,DBA就会同时收到比较多的报警信息。又比如我们的一组HTTP接口,部署在同一组服务器中,其依赖的服务器的某一台发生问题,如网络丢包、CPU过载等,也可能会由于某一个接口的请求量过大,引起CPU暴涨,进而影响其他的接口响应。

是否有一种机制能够将业务、服务、系统中的依赖关系定义出来,从而我们可以结合依赖关系来减轻监控报警的泛滥呢?我们期待的情况是,在一至两条短信中,即可获得问题的影响范围、程度,以及问题的原因或尽可能提供可能的排查方向。运维人员会根据影响范围、程度来快速的决策响应的时间,以及响应的人群范围,而问题的原因以及尽可能的排查方向,可以为我们的提供有效的信息帮我们快速定位深知解决问题。

实现方式 & 机制:

1,PHP Module ,注册的服务名称在PHP中全局可见,通过 get(“/foo”)的方式获取相应的服务地址 与ZK、etc的使用方式类似(包括在其他语言中),其实现可参考 https://github.com/laruence/yaconf ,提供对etcd的支持,同时支持 etcd 中某目录的变量的export ,watch 等行为,可以在php.ini 中配置目录

2,在etcd(或其他的服务管理工具,如consul、sky net)等的基础上进行改进,主要增加一个功能,即DNS bind server的 zone/view 功能,在能够提供全局注册的同时,提供不同请求来源获取不同 value的功能

3,在ETCD的描述的资源中,定义资源的依赖关系,并提供依赖关系的添加、修改、删除、输出接口

4,根据Nginx+PHP-FPM的运行机制,在Nginx中通过Nginx模块对服务所依赖的服务注册数据进行管理、维护,其数据集中管理通过etcd等方式进行维护,Nginx模块将数据注入到例如:_SERVER[] _ENV[] 变量数据中,PHP代码通过内置方式对数据进行获取和使用

其他问题

1,对其他语言的支持 2,参考soa

发表在 MircroService, System | 11条评论

人脸识别和追踪的一次工程实践

前段时间抽空做了一个在视频中做人脸识别并标记(马赛克)的事情,简单记录一下,以供参考。

从Google中检索人脸识别(face recognition)可以得到的技术方案大体上会有如下几种:OpenCV、faceNet 、TensorFlow、CCV、Dlib等,

我们知道,视频是由一帧一帧的连续的图像进行播放进行展示,尽管现代视频编码算法做了非常多的优化,通过提取相同的视觉像素进行复用来降低视频的体积,但是,还原到视频的每一帧,依然是一张完整的图像,视频中人脸识别的比较通俗的套路是对视频的每一帧图像进行识别,辨识出需要识别的人脸,但是由于视频的场景的变化的,尤其是一些实时、实事类型的视频,场景切换、人物极速运动的情形都不可避免,而由于此次视频中人脸识别的精度要求很高(不能漏帧,要求每一帧都识别出来),因此,对于难度也就更大了一些。我在成都双流机场的安检区体验过商汤的人脸识别,其对于双头像、偏脸、低头等情况识别的并不是很好。

由于项目紧迫,工程进度压力较大,因此快速的找到一个可用的方式是很重要的事情。从Google中检索到的方案中,我们发现,每一种支持CNN的深度学习模型,而大多数深度学习模型是需要通过训练数据的,因此,在对TensorFlow的数据训练了近一个月后,我们发现一个有趣的结果,就是视频的连续帧中,总会有一些帧无法正确的识别的人脸,要么是识别到相似的人脸上,要么是没有识别出来。这是一个极度头痛的事情。

换一个思路,加上tracking逻辑。也就是说,首先在某一帧中识别出来所需要的人脸,然后通过tracking的方式,对人脸在视频中的移动进行追踪,只要人脸还在,那么无论是人脸的角度如何变化,都是可以找到对应的目标的。说干就干。     在开头提到的方案中,FaceNet和TensorFlow是纯粹的深度学习框架,只负责识别,CCV也提供了识别和追踪的算法,但是文档甚少。因此我们在OpenCV和DLIB中进行选择。OpenCV 和 Dlib 的作者都非常勤奋,并且都支持比较完整的Python API。

首先使用Dlib做了个Demo:

 

由于项目前期的经验,我们发现无论是使用两者的任何一种,都需要深度学习模型的支持,非常便利的找到Dlib 的Python实现的 face_recognition 项目(https://pypi.org/project/face_recognition/ https://github.com/ageitgey/face_recognition),这个项目的优势在于自带 Dlib 的CNN模型数据,face_recognition_models(https://pypi.org/project/face_recognition_models/)。

关于face_recognition,补充一个信息是:

Built using dlib’s state-of-the-art face recognition,built with deep learning. The model has an accuracy of 99.38% on the Labeled Faces in the Wild benchmark.

参考例子face_recognition 的Sample,我们可以很快速的建立一个对视频中人脸进行识别的demo。


这里需要说明的是:face_recognition是来做人脸识别的,而不是人脸检测。两者的区别是前者能够识别出人脸是谁(基准人脸),而后者是发现目标区域的图像是否是人脸。

所谓的人脸是谁,是通过 distance进行计算的,在dlib和 face_recognition的项目中,通过对人脸区域的rgb数据进行encoding,以及compare,获知两张人脸图像是否标识了同一个人:

优化:

在face_recognition 的项目中,face compare的默认 tolerance的值为0.6

由于项目中加入了追踪算法,因此,在不同的情况下,我们考虑优化这个值,比如:

  1. 第一次识别出人脸,tolerance是否需要适当严格一些,确保识别的首帧准确
  2. Tracking的过程中,我们同样发现,当追踪的目标在图像的边缘消失的情况下,tracking结果返回的失败数据并不足以判断是否追踪失败,以及在两张脸进行交叉的过程中,追踪算法有可能会判断失误,因此,我们在追踪的过程,需要在每次追踪到目标后,同基准目标(基准脸)进行对比,此时的 tolerance 的值需要小一些。

综上,我们在整个对视频的人脸识别(recognition)和追踪的过程中,使用了增加了三个机制:

  1. 在未发现识别目标的情况下,对每一帧的人脸进行多次识别
  2. 发现目标人脸后,对每一帧识别的结果,进行人脸距离的判断
  3. 输入多张基准目标(不同角度)

最终的识别追踪效果如下:

补充:

关于人脸识别和追踪业内有很多种方案,这里仅仅描述了一个我亲身实践并基本满足要求的一个方案,其他方案我也做了一些简单的收集,可以参考如下:

  1. https://github.com/opencv/opencv
  2. https://github.com/ageitgey/face_recognition
  3. http://dlib.net/
  4. https://www.one-tab.com/page/LZfYvxL_QdWEzfO-VauCfA
  5. https://www.one-tab.com/page/xqpn3eADRS6t_K3huTzzPw

发表在 同步技术 | 标签为 , , | 人脸识别和追踪的一次工程实践已关闭评论

Go-kit 入门(四)服务调用

调用服务

存在“真空”(即极其独立,与其他任何服务无互相调用的关系)中的服务是罕见的。而我们常见的是,我们需要调用其他的服务。这也是 Go kit 的闪光点 ,我们提供了 传输中间件 机制来解决可能出现的很多问题。

下面我们将实现一个代理功能的中间件,作为一个服务中间件。在这里我们只代理一个方法,Uppercase。

客户端端点

我们已经有了一个跟我们所知道的完全相同的端点,但是我们将使用它来调用一个请求,而不是提供服务。按照这种方式来使用它的时候,我们称它为客户端端点。为了调用客户端端点,我们需要做一些简单的转换。

现在,我们为了构造一个代理中间件,我们将一个代理URL字符串转换为一个端点。加入我们使用 HTTP 协议之上的 JSON,我们可以使用 transport/http 包中的一个辅助(helper)函数。

服务发现和负载均衡

如果我们只使用一个远程的服务就好了。但是实际上,我们往往 会有很多个服务实例。我们希望通过一些服务发现算法来发现它们,然后将我们的负载分散到它们上面,并且如果这些实例中的任何一个变得糟糕,我们能够处理它,并且不影响我们服务的可用性。

Go kit 为不同的服务发现系统提供了适配器,为了获取最新的服务实例集合,暴露端点个体。这些适配器被称为 发布器(publishers)。

在发布器内部,它使用一个私有的工厂函数,将被发现的每一个 host:port 字符串 转换成一个可用的端点。

目前,我们的工程方法,makeUppercaseEndpoint,只是直接请求 URL。但是,在工厂函数中加入一些安全的中间件方法是很重要的,比如 回路断路器 和 限流器。

现在,我们已经有了一系列的端点,我们需要在其中选择一个。负载均衡器包装了 发布器,并且从端点集合中选择其中的某一个。Go kit 提供了一组基本的负载均衡器,并且,如果你希望更多的高级的算法,也可以很容易的自己来写一个。

现在,我们可以根据一下算法来选择端点。我们能够使用它为消费器提供一个单一的、逻辑的可靠的端点。通过重试的策略包装负载均衡器,并且返回一个可用的端点。重试的策略可以将一个失败的请求进行重试,直到达到最大的可重试次数或是达到超时时间。

现在,让我们将最后的代理中间件加入到代码中。为了简洁,我们假设用户会为逗号(,)分隔的多个实例端点指定一个标记。

stringsvc3

截止目前,这个完整的服务是 stringsvc3$ go get github.com/go-kit/kit/examples/stringsvc3 $ stringsvc3 -listen=:8001 & listen=:8001 caller=proxying.go:25 proxy_to=none listen=:8001 caller=main.go:72 msg=HTTP addr=:8001 $ stringsvc3 -listen=:8002 & listen=:8002 caller=proxying.go:25 proxy_to=none listen=:8002 caller=main.go:72 msg=HTTP addr=:8002 $ stringsvc3 -listen=:8003 & listen=:8003 caller=proxying.go:25 proxy_to=none listen=:8003 caller=main.go:72 msg=HTTP addr=:8003 $ stringsvc3 -listen=:8080 -proxy=localhost:8001,localhost:8002,localhost:8003 listen=:8080 caller=proxying.go:29 proxy_to="[localhost:8001 localhost:8002 localhost:8003]" listen=:8080 caller=main.go:72 msg=HTTP addr=:8080

高级话题

线程上下文

上下文对象用来在单个请求中携带那些需要跨越概念性边界的信息。在我们的例子中,我们还没有在业务逻辑中使用线程上下文。但是,这种方式几乎会永远都是一个好方案。它允许我们在业务逻辑和中间件中传递请求范围内的信息,并且对于复杂的任务(比如,分布式系统的细粒度追踪信息)也是很必要的。

具体点来讲,这也意味着你的业务逻辑接口会这样:

 

请求跟踪

一旦你的基础设施增长超过一定的规模,在多个服务中跟踪请求是非常必要的,这样,你就能够分析并解决故障热点。参见 package tracing 获取更多信息。

创建一个客户端软件包

使用 Go kit 为你的服务创建一个客户端软件包是很可能的事情,让你的服务能够很容易对其他的 Go 程序进行调用。实际上,你的客户端package会提供你的服务接口,这个接口会使用指定的传输方式来调用远程服务。参见 package addsvc/client 作为参考例子.

其他例子

addsvc

addsvc 是原来的一个例子。它公开 所有支持的传输方式 的系列操作。它完整地做了日志记录、仪表盘化,并且使用 Zipkin 来跟踪请求。同样,它也示范了如何创建和使用客户端package。它是一个非常棒的例子,为 Go kit 服务提供了完整的功能示例。

profilesvc

profilesvc 示范了如何使用 Go kit 来打造 REST 风格的微服务。

apigateway

apigateway 示范了如何实现一个 API 网关模式,通过 Consul 作为服务发现系统。

shipping

shipping 是一个完整的,真实的应用,由多个微服务组成,基于领域驱动设计原则。

发表在 MircroService | 标签为 , | 7,005条评论

go-kit 入门(三)日志和仪表化

日志和仪表化

任何服务在日志和仪表化没有就绪的情况下,都不能说是生产环境就绪的。

传输日志

任何需要日志记录的组件都需要将 logger 作为依赖,就像数据库连接一样。因此,我们在 main 函数中构造 logger 对象,然后将其传入需要使用它的组件中。我们始终不去使用一个全局的 logger 对象。

我们可以直接将 logger 传入到 stringService 的实现代码中,但是,还有一个更好的方式。我们可以使用 中间件 (middleware) ,也常常被称为 装饰者。

middleware 是一个函数,它接收一个 endpoint 作为参数,并且返回一个 endpoint。

在函数中,它可以做任何事情。下面就让我们来创建一个基本的日志中间件。

然后,我们将它加入到每一个处理函数中。

事实证明,这项技术是非常有价值的,远远不止于记录日志,Go kit 的很多模块都被实现为端点中间件。

应用日志

那么,在我们的应用中,应该如何记录日志呢?比如那些需要被传入的参数等。事实上,我们能够为我们的服务定义一个中间件,从而获得同样好的组合效果。由于我们的 StringService被定义为一个接口,我们只需要作出一个新的类型,来包装先有的 StringService,让它来执行扩充的记录日志的任务。

然后,将新的类型引入到下面的代码中:

在传输环节使用端点中间件,比如回路断路器和速率限制。在业务环节使用服务中间件,比如日志和仪表化。

仪表化

在 Go kit 中,仪表化意味着使用 包指标 来记录关于服务运行行为的状态。统计执行的任务的数量,在请求完成后记录消耗的时间,以及跟踪所有正在执行的操作的数量,都被认为是 仪表化。

我们可以使用同样的中间件模式,在记录日志的环节我们曾经用过。

然后将其引入到服务中:

stringsvc2

目前位置,完整的服务是 stringsvc2.

发表在 GoLang, MircroService | 518条评论

go-kit 入门 (二) 第一个 Go kit 程序

下面让我来们创建一个非常精简的 Go kit 服务

业务逻辑逻辑

服务(Service)是从业务逻辑开始的,在 Go kit 中,我们将服务以 interface 作为模型

这个 interface 需要有一个“实现”

请求和响应

在 Go kit 中,主要的消息模式是 RPC。因此,接口( interface )的每一个方法都会被模型化为远程过程调用(RPC)。对于每一个方法,我们都定义了请求和响应的结构体,捕获输入、输出各自的所有参数。

端点 (endpoint)

Go kit 通过 endpoint 提供了非常丰富的功能。

一个端点代表一个RPC,也就是我们服务接口中的一个函数。我们将编写简单的适配器,将我们的服务的每一个方法转换成端点。

传输(Transports)

现在我们需要将服务暴露给外界,这样它们才能被调用。对于服务如何与外界交互,你的组织可能已经有了定论。可能你会使用 Thrift、基于 HTTP 的自定义 JSON。Go kit支持多种开箱即用的 传输 方式。(Adding support for new ones is easy—just 对新方式的支持是非常简单的。参见 这里

针对我们现在的这个微型的服务例子,我们使用基于 HTTP 的 JSON。Go kit 中提供了一个辅助结构体,在 transport/http 中。

发表在 GoLang, MircroService | 514条评论

go-kit 入门(一)

go-kit 入门

1. microservice

Go-Kit

go kit 是一个分布式的开发工具集,在大型的组织(业务)中可以用来构建微服务。其解决了分布式系统中的大多数常见问题,因此,使用者可以将精力集中在业务逻辑上。

2. go-kit 组件介绍

2.1 Endpoint(端点)

Go kit首先解决了RPC消息模式。其使用了一个抽象的 endpoint 来为每一个RPC建立模型。

endpoint通过被一个server进行实现(implement),或是被一个client调用。这是很多 Go kit组件的基本构建代码块。

2.2 Circuit breaker(回路断路器)

Circuitbreaker(回路断路器) 模块提供了很多流行的回路断路lib的端点(endpoint)适配器。回路断路器可以避免雪崩,并且提高了针对间歇性错误的弹性。每一个client的端点都应该封装(wrapped)在回路断路器中。

2.3 Rate limiter(限流器)

ratelimit模块提供了到限流器代码包的端点适配器。限流器对服务端(server-client)和客户端(client-side)同等生效。使用限流器可以强制进、出请求量在阈值上限以下。

2.4 Transport(传输层)

transport 模块提供了将特定的序列化算法绑定到端点的辅助方法。当前,Go kit只针对JSON和HTTP提供了辅助方法。如果你的组织使用完整功能的传输层,典型的方案是使用Go在传输层提供的函数库,Go kit并不需要来做太多的事情。这些情况,可以查阅代码例子来理解如何为你的端点写一个适配器。目前,可以查看 addsvc的代码来理解Transport绑定是如何工作的。我们还提供了针对Thirft,gRPC,net/rpc,和http json的特殊例子。对JSON/RPC和Swagger的支持在计划中。

2.5 Logging(日志)

服务产生的日志是会被延迟消费(使用)的,或者是人或者是机器(来使用)。人可能会对调试错误、跟踪特殊的请求感兴趣。机器可能会对统计那些有趣的事件,或是对离线处理的结果进行聚合。这两种情况,日志消息的结构化和可操作性是很重要的。Go kit的 log 模块针对这些实践提供了最好的设计。

2.6 Metrics(Instrumentation)度量/仪表盘

直到服务经过了跟踪计数、延迟、健康状况和其他的周期性的或针对每个请求信息的仪表盘化,才能被认为是“生产环境”完备的。Go kit 的 metric 模块为你的服务提供了通用并健壮的接口集合。可以绑定到常用的后端服务,比如 expvarstatsdPrometheus

2.7 Request tracing(请求跟踪)

随着你的基础设施的增长,能够跟踪一个请求变得越来越重要,因为它可以在多个服务中进行穿梭并回到用户。Go kit的 tracing 模块提供了为端点和传输的增强性的绑定功能,以捕捉关于请求的信息,并把它们发送到跟踪系统中。(当前支持 Zipkin,计划支持Appdash

2.8 Service discovery and load balancing(服务发现和负载均衡)

如果你的服务调用了其他的服务,需要知道如何找到它(另一个服务),并且应该智能的将负载在这些发现的实例上铺开(即,让被发现的实例智能的分担服务压力)。Go kit的 loadbalancer模块提供了客户端端点的中间件来解决这类问题,无论你是使用的静态的主机名还是IP地址,或是 DNS的 SRV 记录,Consul,etcd 或是 Zookeeper。并且,如果你使用定制的系统,也可以非常容易的编写你自己的 Publisher,以使用 Go kit 提供的负载均衡策略。(目前,支持静态主机名、etcd、Consul、Zookeeper)

3 目标

  • 在各种SOA架构中操作–预期会与各种非Go kit服务进行交互
  • 使用RPC作为最主要的消息模式
  • 可插拔的序列化和传输–不仅仅只有JSON和HTTP
  • 简单便可融入现有的架构–没有任何特殊工具、技术的相关指令

4 目标之外(不考虑做的事情)

  • 支持除RPC之外的消息模式(至少目前是)–比如 MPI、pub/sub,CQRS,等
  • 除适配现有软件外,重新实现一些功能
  • 在运维方面进行评论:部署、配置、进程管理、服务编排等

5 依赖管理

Go kit 是一个函数库,设计的目标是引入到二进制文件中。对于二进制软件包的作者来讲,Vendoring是目前用来确保软件可靠、可重新构建的最好的机制。因此,我们强烈的建议我们的用户使用vendoring机制来管理他们软件的依赖,包括Go kit。

为了避免兼容性和可用性的问题,Go kit没有vendor它自己的依赖,并且并不推荐使用第三方的引用代理。

有一些工具可以让vendor机制更简单,包括 gbglidegvtgovendorvendetta。另外,Go kit使用了一系列的持续集成的机制来确保在尽快地修复那些复杂问题。

5 相关项目

标注有 ★ 的项目对 Go kit 的设计有着特别的影响 (反之亦然)

  1. 服务框架

    • gizmo, a microservice toolkit from The New York Times ★
    • go-micro, a microservices client/server library ★
    • gocircuit, dynamic cloud orchestration
    • gotalk, async peer communication protocol & library
    • h2, a microservices framework ★
    • Kite, a micro-service framework
  2. 独立组件

    afex/hystrix-go, client-side latency and fault tolerance library

    armon/go-metrics, library for exporting performance and runtime metrics to external metrics systems

    codahale/lunk, structured logging in the style of Google’s Dapper or Twitter’s Zipkin

    eapache/go-resiliency, resiliency patterns

    sasbury/logging, a tagged style of logging

    grpc/grpc-go, HTTP/2 based RPC

    inconshreveable/log15, simple, powerful logging for Go ★

    mailgun/vulcand, programmatic load balancer backed by etcd

    mattheath/phosphor, distributed system tracing

    pivotal-golang/lager, an opinionated logging library

    rubyist/circuitbreaker, circuit breaker library

    Sirupsen/logrus, structured, pluggable logging for Go ★

    sourcegraph/appdash, application tracing system based on Google’s Dapper

    spacemonkeygo/monitor, data collection, monitoring, instrumentation, and Zipkin client library

    streadway/handy, net/http handler filters

    vitess/rpcplus, package rpc + context.Context

    gdamore/mangos, nanomsg implementation in pure Go

  3. Web 框架

    Beego

    Gin

    Goji

    Gorilla

    Martini

    Negroni

    Revel (considered harmful)

  4. ###其他参考

    Architecting for the Cloud — Netflix

    Dapper, a Large-Scale Distributed Systems Tracing Infrastructure — Google

    Your Server as a Function (PDF) — Twitter

发表在 GoLang, MircroService | 标签为 , , , | 616条评论

关于移动互联网TCP协议栈优化的一些材料总结

  1. http://blog.chunshengster.me/2013/12/optimizing_your_linux_stack_for_maximum_mobile_web_performance.html [翻译,原文见:http://blog.cloudflare.com/optimizing-the-linux-stack-for-mobile-web-per
  2. https://docs.google.com/presentation/d/1f2J_HrzMNvVHhsB3f7DKJFPl2N0Q_QR2ZEECWQu6oV8/present#slide=id.p19  google大神关于 high performance browser networking 的简版PPT
  3. http://chimera.labs.oreilly.com/books/1230000000545/index.html   high performance browser networking 的在线电子书
  4. https://developers.google.com/speed/protocols/tcpm-IW10?csw=1  协议栈优化中IW10 的所有关联文档
  5. http://lwn.net/Articles/427104/  LWN中关于TCP initial congestion window 的解释
  6. http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=356f039822b8d802138f7121c80d2a9286976dbd  kernel git中关于 TCP: increase default initial receive window 的文档和代码
  7. https://datatracker.ietf.org/doc/rfc6928/?include_text=1 rfc 6928关于 initial congestion window 的文档
  8. http://research.csc.ncsu.edu/netsrv/?q=content/iw10  Experimental Evaluation of Increasing TCP Initial Congestion Window to 10 Segments
  9. 其他http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36640.pdf
  10. http://lwn.net/Articles/426883/
  11. http://www.ietf.org/proceedings/10mar/slides/iccrg-4.pdf
  12. http://tools.ietf.org/html/rfc3390
  13. http://tools.ietf.org/html/draft-mathis-tcpm-proportional-rate-reduction-01
  14. http://monolight.cc/2010/12/increasing-tcp-initial-congestion-window/
  15. http://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/
  16. http://blog.habets.pp.se/2011/10/Optimizing-TCP-slow-start
  17. http://idning.github.io/tcp_ip_increasing_init_cwnd.html
  18. http://itindex.net/detail/46633-tcp-%E4%BC%98%E5%8C%96
  19. http://blog.csdn.net/zhangskd/article/details/7608343
  20. http://blog.csdn.net/zhangskd/article/details/7609465
发表在 System, TCP | 一条评论

Optimizing Your Linux Stack for Maximum Mobile Web Performance[优化Linux 协议栈提升移动互联网性能]

Published on December 31, 2012 12:29AM by Matthew Prince.

原文请见:  http://blog.cloudflare.com/optimizing-the-linux-stack-for-mobile-web-per

 

下面是这篇是我们系统工程师团队的lan Applegate (@AppealingTea)写的技术文章,关于如何优化Linux TCP 栈来优化手机访问性能。这篇文章最早发布在  2012 Web Performance Calendar. 在 CloudFlare,我们花了大量的时间来优化网络栈以保证无论什么设备从无论什么网络访问的我们都是最优化的。我们想分享这些技术细节来帮助那些正在寻找移动网络性能优化的公司,即便他们不使用CloudFlare。当然,如果你正在使用CloudFlare,当手机用户访问你的网站的时候你已经获得了最快的TCP效率。

 

We spend a lot of time at CloudFlare thinking about how to make the Internet fast on mobile devices. Currently there are over 1.2 billion active mobile users and that number is growing rapidly. Earlier this year mobile Internet access passed fixed Internet access in India and that’s likely to be repeated the world over. So, mobile network performance will only become more and more important.

我们(CloudFlare)用了很多时间来思考手机设备如何获得最快的网络速度。现在,已经有12亿活跃的手机用户并且还在迅速增长。今年(2012)早些时候,印度的移动互联网接入用户已经超过固网接入用户,这样的情况将在全球蔓延。因此,手机网络的性能优化变得越来越重要。

Most of the focus today on improving mobile performance is on Layer 7 with front end optimizations(最佳化) (FEO). At CloudFlare, we’ve done significant work in this area with front end optimization technologies like Rocket Loader, Mirage, and Polish that dynamically(动态地) modify(修改) web content to make it load quickly whatever device is being used. However, while FEO is important to make mobile fast, the unique(独特的) characteristics(特征) of mobile networks also means we have to pay attention to the underlying(潜在的) performance of the technologies down at Layer 4 of the network stack.

目前大多数移动网络的优化聚焦于7层(前端服务器,FEO)的优化。在CloudFlare,我们在前端服务器上也花了很多的时间来左右话,比如说:Rocket Loader, Mirage, and Polish,无论什么样的设备来访问,我们都能动态的修改页面使得内容能够被快速的展现。然而,尽管在手机访问优化方面FEO很重要,但是移动网络的特征同样意味着我们必须在4层的网络栈优化上,也许会有一些技术方式能够提升性能。

This article is about the challenges mobile devices present, how the default TCP configuration(配置) is ill-suited for optimal(最佳的) mobile performance, and what you can do to improve performance for visitors connecting via mobile networks. Before diving into the details, a quick technical note. At CloudFlare, we’ve build most of our systems on top of a custom version of Linux so, while the underlying technologies can apply to other operating systems, the examples I’ll use are from Linux.

这篇文章针对于现在的移动设备思考,什么样的默认TCP策略,能够适应手机获得最佳的网络性能,并且针对通过移动网络访问的用户我们如何操作来提升性能。在深入细节之前,我们先来快速了解一下这个:在CloudFlare,我们大多数系统都是在定制的Linux上构建的,虽然底层的技术可以适用于其他的操作系统。不过,下面的例子都是来源于Linux环境。

TCP Congestion Control

TCP拥塞控制

To understand the challenges of mobile network performance at Layer 4 of the networking stack you need to understand TCP Congestion(拥挤) Control. TCP Congestion Control is the gatekeeper that determines how to control the flow of packets from your server to your clients. Its goal is to prevent Internet congestion by detecting(察觉) when congestion occurs and slowing down the rate data is transmitted(传输). This helps ensure that the Internet is available to everyone, but can cause problems on mobile network when TCP mistakes mobile network problems for congestion.

为了更好的理解4层网络栈对移动网络性能优化的挑战,我们需要首先理解TCP的拥塞控制机制。TCP的拥塞控制机制是控制数据包如何从你的服务器到客户端流动的看门人。他的目标就是在拥塞发生时发现它并且减慢数据包的传输速率,来达到防止网络拥塞发生的目的。这种机制帮助我们确保网络对于每个节点都是可用的,但这在移动移动网络中,当TCP机制误解了移动网络拥塞问题的时候却会引发一些问题。

 

TCP Congestion Control holds back the floodgates if it detects congestion (i.e. packet loss) on the remote end. A network is, inherently, a shared resource. The purpose of TCP Congestion Control was to ensure that every device on the network cooperates to not overwhelm its resource. On a wired network, if packet loss is detected it is a fairly reliable indicator that a port along the connection is overburdened. What is typically going on in these cases is that a memory buffer in a switch somewhere has filled beyond its capacity because packets are coming in faster than they can be sent out and data is being discarded. TCP Congestion Control on clients and servers is setup to “back off” in these cases in order to ensure that the network remains available for all its users.

TCP拥塞会在察觉与远端通讯产生拥塞的时候控制住数据闸门。而网络,是一个共享的资源。TCP的目标就是确保网络中的每一台设备都是合作状态,而不会压跨它占有的资源。在有线网络中,当丢包发生时,可以非常肯定的认为是这个连接中的某一个端口负载过重了。典型的状况是某台交换设备的流入速率远大于流出速率,导致它的内存缓冲用满了,从而数据被丢弃掉。TCP拥塞控制就是在这种情况下,通过服务器和客户端的控制“回退”,以确保网络对于其他的所有用户仍然可用。

But figuring out what packet loss means on a mobile network is a different matter. Radio networks are inherently susceptible to interference which results in packet loss. If pakcets are being dropped does that mean a switch is overburdened, like we can infer on a wired network? Or did someone travel from an undersubscribed wireless cell to an oversubscribed one? Or did someone just turn on a microwave? Or maybe it was just a random solar flare? Since it’s not as clear what packet loss means on a mobile network, it’s not clear what action a TCP Congestion Control algorithm should take.

但是,必须要指出的是,移动网络中发生的丢包现象却意味着其他的可能性。无线网络天然就很容易因为收到影响、干扰而产生丢包。如果数据包被丢弃确实意味着交换机过载,我们能否在无线网络中也这样推断呢?或者是,一个人从一个不过载的无线基站转移到了一个过载的无线基站?或者是某人刚好这时打开了无线微波?或者可能很随机的?正因为无线网络中的丢包原因不是那样的明确,所以,我们同样不能够明确TCP拥塞控制算法应该如何处理这个问题。

A Series of Leaky Tubes

一系列崩漏的水管

To optimize networks for lossy networks like those on mobile networks, it’s important to understand exactly how TCP Congestion Control algorithms are designed. While the high level concept makes sense, the details of TCP Congestion Control are not widely understood by most people working in the web performance industry. That said, it is an important core part of what makes the Internet reliable and the subject of very active research and development.

为了优化像移动网络这样的容易受到干扰而产生损耗的网络,正确的理解TCP拥塞算法的设计是非常重要的。从一个更高层次的角度来讲,TCP拥塞控制算法的细节并不为广大从事WEB性能优化的人们所广泛知晓。这就是说,这是网络可靠性中是非常重要、核心的部分,也是当前研究和开发的活跃话题。

 

To understand how TCP Congestion Control algorithms work, imagine the following analogy. Think of your web server as your local water utility plant. You’ve built on a large network of pipes in your hometown and you need to guarantee that each pipe is as pressurized as possible for delivery, but you don’t want to burst the pipes. (Note: I recognize the late Senator Ted Stevens got a lot of flack for describing the Internet as a “series of tubes,” but the metaphor is surprisingly accurate.)

为了理解TCP拥塞控制算法的工作方式,想象一下下面的一个可以类比的场景。想像你的WEB服务器就像当地的自来水厂。你已经在你的家乡建立了非常庞大的管线网络,并且你必须确保你的管线尽可能的密封起来,以保证水的传送,但是,你并不愿意水管发生甭裂。(备注:我了解到 Ted Stevens在很多的宣传中都说互联网就像“一系列的水管”,不过,这比喻实在是太恰当了)

Your client, Crazy Arty, runs a local water bottling plant that connects to your pipe network. Crazy Arty’s infrastructure is built on old pipes that are leaky and brittle. For you to get water to them without bursting his pipes, you need to infer the capability of Crazy Arty’s system. If you don’t know in advance then you do a test — you send a known amount of water to the line and then measure the pressure. If the pressure is suddenly lost then you can infer that you broke a pipe. If not, then that level is likely safe and you can add more water pressure and repeat the test. You can iterate this test until you burst a pipe, see the drop in pressure, write down the maximum water volume, and going forward ensure you never exceed it.

你的客户,Crazy Arty,在当地经营一家水灌装厂,连接在你的水管网络中。Crazy Arty的基础设施是用老旧的水管建造的,非常容易崩漏。所有,如果你希望你的水传送到他那里而不崩裂掉他的管道,你就必须能够获知Crazy Arty的水管的能力。如果你事先不知道这些,那么你会做一个测试:你会发送已知流量的水到管线中,并且测试它的压力。如果测试过程中压力突然消失掉,你就能推断出你已经崩裂了水管。如果还没有,这就意味着当前的流量水平是安全的,并且你可以继续增加水的流量压力,以此方式重复测试。你可以重复这样的测试直到你崩掉了水管,发现水管突然没有了压力,然后记下最大的水量,以后确保不再超过这个量。

Imagine, however, that there’s some exogenous factor that could decrease the pressure in the pipe without actually indicating a pipe had burst. What if, for example, Crazy Arty ran a pump that he only turned on randomly from time to time and you didn’t know about. If the only signal you have is observing a loss in pressure, you’d have no way of knowing whether you’d burst a pipe or if Crazy Arty had just plugged in the pump. The effect would be that you’d likely record a pressure level much less than the amount the pipes could actually withstand — leading to all your customers on the network potentially having lower water pressure than they should.

再想象一下,一定还有一些外在的因素,可以使得水管中的压力降低,但是实际上却并不是水管崩裂。例如,Crazy Arty在你并不知道的情况下,随机的使用抽水泵从管道中进行抽水。如果你只能够通过观察水的压力变化来获取信息,那么你就没有办法知道到底是水管崩裂了还是Crazy Arty接上了抽水泵。而结果将会是,你所有记录的压力值很可能远远低于水管所能承受的–导致你所有的用户很可能获得了远小于它本该获得的水流量压力。

Optimizing for Congestion or Loss

拥塞和丢包优化

If you’ve been following up to this point then you already know more about TCP Congestion Control than you would guess. The initial amount of water we talked about in TCP is known as the Initial Congestion Window (initcwnd) it is the initial number of packets in flight across the network. The congestion window (cwnd) either shrinks, grows, or stays the same depending on how many packets make it back and how fast (in ACK trains) they return after the initial burst. In essence, TCP Congestion Control is just like the water utility — measuring the pressure a network can withstand and then adjusting the volume in an attempt to maximize flow without bursting any pipes.

如果你接受这个观点,你就已经知道了很多关于TCP拥塞控制的知识,然后我们就可以更多的进行猜测。我们所谈论的TCP的初始水量,就是初始化拥塞窗口(initcwnd),它是网络传输过程中的初始的数据包个数。拥塞窗口(cwnd)或是降低,或是增长,或是保持同样的大小,这些依赖于在网络开始崩溃后有多少数据包能够返回来以及有多快(在ACK环节)。本质上来讲,TCP拥塞控制就像自来水工厂–测量网络能够经受的压力然后调节开关的大小以取得最大的流量,但是却并不崩裂任何的管道。

When a TCP connection is first established it attempts to ramp up the cwnd quickly. This phase of the connection, where TCP grows the cwnd rapidly, is called Slow Start. That’s a bit of a misnomer since it is generally an exponential growth function which is quite fast and aggressive. Just like when the water utility in the example above detects a drop in pressure it turns down the volume of water, when TCP detects packets are lost it reduces the size of the cwnd and delays the time before another burst of packets is delivered. The time between packet bursts is known as the Retransmission Timeout (RTO). The algorithm within TCP that controls these processes is called the Congestion Control Algorithm. There are many congestion control algorithms and clients and servers can use different strategies based in the characteristics of their networks. Most of Congestion Control Algorithms focus on optimizing for one type of network loss or another: congestive loss (like you see on wired networks) or random loss (like you see on mobile networks).

当TCP连接第一次建立后,它就马上开始增加拥塞窗口。这个阶段中,TCP不断升高拥塞窗口,这个过程叫“慢启动”(Slow Start)。这个词有一些不够恰当,因为,这个过程往往是快速并激进的指数级增长。就像上面提到的自来水工厂一样,当它检测到压力的下降就开始调小水的阀门,当TCP检测到数据包丢失的时候,它也开始减小拥塞窗口的大小,并延迟下一个可能崩裂网络的数据包的发送。这个等待数据包发送的时间被称为“重发延时”(RTO)。TCP中的这个算法控制着这一系列的过程,被成为拥塞控制算法。有很多的拥塞控制算法,并且客户端和服务器之间可以根据它们网络不同的特性使用不同的算法。大多数拥塞控制算法聚焦于优化网络丢包中的一种,比如过载丢包(如有线网络),或是随机丢包(比如移动网络)。

In the example above, a pipe bursting would be an indication of congestive loss. There was a physical limit to the pipes, it is exceeded, and the appropriate response is to back off. On the other hand, Crazy Arty’s pump is analogous to random loss. The capacity is still available on the network and only a temporary disturbance causes the water utility to see the pipes as overfull. The Internet started as a network of wired devices, and, as its name suggests, congestion control was largely designed to optimize for congestive loss. As a result, the default Congestion Control Algorithm in many operating systems is good for communicating wired networks but not as good for communicating with mobile networks.

在上面的例子中,一条数据管道的崩裂可能是因为过载丢包。在管道中有一个物理限制,如果超过了这个限制,就会有对应的回退响应机制。从另一个角度来说,Crazy Arty的抽水机类似于随机丢包。网络的能力依然可用的情况下,仅仅是一个短时间的失衡,导致自来水厂认为管道已经过载。互联网是从有线设备开始的,正如它的名字一样,拥塞控制在很大程度上来讲是为过载丢包而设计。因此,在很多操作系统上默认的拥塞控制算法对于有线网络的数据通讯来讲是非常有用的,但是在手机网络通讯中却并不是那么的适合。

A few Congestion Control algorithms try to bridge the gap by using the time of the delay in the “pressure increase” to “expected capacity” to figure out the cause of the loss. These are known as bandwidth estimation algorithms, and examples include Vegas, Venoand Westwood+. Unfortunately, all of these methods are reactive and reuse no information across two similar streams.

一些拥塞控制算法试图通过缩小“压力增长”至“预期压力”阶段的重发延迟的时间间隔来确认丢包的原因。这些称为带宽评估算法,包括 VegasVenoWestwood+。不幸的是,所有的这些方法都没有效果,并且在两条相似的数据流中无法找到可重用的信息。

At companies that see a significant amount of network traffic, like CloudFlare or Google, it is possible to map the characteristics of the Internet’s networks and choose a specific congestion control algorithm in order to maximize performance for that network. Unfortunately, unless you are seeing the large amounts of traffic as we do and can record data on network performance, the ability to instrument your congestion control or build a “weather forecast” is usually impossible. Fortunately, there are still several things you can do to make your server more responsive to visitors even when they’re coming from lossy, mobile devices.

在一些能够观察到巨量网络流量的公司内部,比如 CloudFlare和Google,是非常有机会通过定位互联网络的特征来选择一个特殊的拥塞控制算法从而达到网络性能最大化的。不幸的是,除非你像我们一样观察大量的网络流量并且记录网络性能数据,否则,建立你自己的拥塞控制就像自己建立“天气预报”机制一样不太可能。但是,还是有很多方式,可以让你的服务面对那些来自于容易丢包的移动设备响应更加良好。

Compelling Reasons to Upgrade You Kernel

升级内核的必要原因

The Linux network stack has been under extensive development to bring about some sensible defaults and mechanisms for dealing with the network topology of 2012. A mixed network of high bandwidth low latency and high bandwidth, high latency, lossy connections was never fully anticipated by the kernel developers of 2009 and if you check your server’s kernel version chances are it’s running a 2.6.32.x kernel from that era.

Linux的网络协议栈已在广泛的开发中带来了一些合理的默认值和机制来处理当今(2012)网络拓扑。在2009年,一个高带宽、低延迟和高带宽、高延迟、易丢包的混合网络是内核开发工作从来没有预期过的,如果你检查你服务器的内核版本碰巧它运行着 2.6.32.x的版本的话,这个内核就是在那个年代开发的。

uname -a

There are a number of reasons that if you’re running an old kernel on your web server and want to increase web performance, especially for mobile devices, you should investigate upgrading. To begin, Linux 2.6.38 bumps the default initcwnd and initrwnd (inital receive window) from 3 to 10. This is an easy, big win. It allows for 14.2KB (vs 5.7KB) of data to be sent or received in the initial round trip before slow start grows the cwnd further. This is important for HTTP and SSL because it gives you more room to fit the header in the initial set of packets. If you are running an older kernel you may be able to run the following command on a bash shell (use caution) to set all of your routes’ initcwnd and initrwnd to 10. On average, this small change can be one of the biggest boosts when you’re trying to maximize web performance.

如果还在运行着老版本的内核并且希望提升web的性能,尤其是针对移动设备,有很多原因使得你应该开始考虑升级了。首先,Linux 2.6.38 跳跃性的将初始拥塞窗口和初始接收窗口(inital receive window)从3升到10.这是个非常简单但是有很大效果的方式。它允许在慢启动提升拥塞窗口前就可以在第一个回环中发送或接收14.2KB(vs 5.7KB)的数据。这对于HTTP和SSL来讲是非常重要的,因为它给了更多的空间在初始阶段的数据包中填充协议头。如果你在一个老版本的内核中,你可能需要通过运行下面的命令来设置所有的路由使得它们的初始化拥塞窗口和初始化接收窗口为10.通常来讲,这个很小的变化对于提升web服务器性能来讲,可能是效果最大的一个。

ip route | while read p; do ip route change $p initcwnd 10 initrwnd 10; done

Linux kernel 3.2 implements Proportional Rate Reduction (PRR). PRR decreases the time it takes for a lossy connection to recover its full speed, potentially improving HTTP response times by 3-10%. The benefits of PRR are significant for mobile networks. To understand why, it’s worth diving back into the details of how previous congestion control strategies interacted with loss.

在Linux 内核 3.2版本中,实现了 Proportional Rate Reduction (PRR)。PRR减小了从丢包网络中恢复到全速的时间,有可能提升3-10%的HTTP的响应时间。这个收益对于手机网络来讲是尤其重要的。为了理解为什么,我们有必要回到前面关于拥塞控制策略和丢包是如何相互影响这个细节的。

Many congestion control algorithms halve the cwnd when a loss is detected. When multiple losses occur this can result in a case where the cwnd is lower than the slow start threshold. Unfortunately, the connection never goes through slow start again. The result is that a few network interruptions can result in TCP slowing to a crawl for all the connections in the session.

很多拥塞控制算法在发现丢包的时候会将拥塞窗口减半(1/2)。当多次丢包发生的时候,可能带来一个结果:拥塞窗口小于慢启动时候的阈值。不幸的是,连接从来不会再来一次慢启动。结果就是,一些网络中断的能够使得会话中的所有连接都慢的像爬一样。

This is even more deadly when combined with tcp_no_metrics_save=0 sysctl setting on unpatched kernels before 3.2. This setting will save data on connections and attempt to use it to optimize the network. Unfortunately, this can actually make performance worse because TCP will apply the exception case to every new connection from a client within a window of a few minutes. In other words, in some cases, one person surfing your site from a mobile phone who has some random packet loss can reduce your server’s performance to this visitor even when their temporary loss has cleared.

更悲催的是,在没有打补丁的3.2版本以前的内核中,当sysctl配置中tcp_no_metrics_save=0 的状况下。这个设置会在连接时保存配置,并且尝试用它来优化网络。不幸的是,这往往会让网络的性能更加糟糕,因为在一段时间周期内的新建连接TCP也会使用这个配置。换句话来讲,某些情况下,一个使用手机访问你站点的人,如果他偶尔发生了丢包现象,就会一直降低他访问你服务器的性能,即便明确知道他们仅仅是偶尔的丢包。

If you expect your visitors to be coming from mobile, lossy connections and you cannot upgrade or patch your kernel I recommend setting tcp_no_metrics_save=1. If you’re comfortable doing some hacking, you can patch older kernels.

如果你预料有从手机设备、不稳定网络来的访客,并且你不能够升级或给你的内核打补丁的情况下,我建议你设置 tcp_no_metrics_save=1。如果你乐于做一些hacking,你可以给你的内核打补丁

The good news is that Linux 3.2 implements PRR, which decreases the amount of time that a lossy connection will impact TCP performance. If you can upgrade, it may be one of the most significant things you can do in order to increase your web performance.

好消息是 Linux 3.2 的内核实现了 PRR,减小了一个低效的连接对于TCP性能影响的时间。如果你能够升级内核,这是一个提升web性能的很重要的事情。

More Improvements Ahead

更多提升

Linux 3.2 also has another important improvement with RFC2099bis. The initial Retransmission Timeout (initRTO) has been changed to 1s from 3s. If loss happens after sending the initcwnd two seconds waiting time are saved when trying to resend the data. With TCP streams being so short this can have a very noticeable improvement if a connection experiences loss at the beginning of the stream. Like the PRR patch this can also be applied (with modification) to older kernels if for some reason you cannot upgrade (here’s the patch).

Linux 3.2 还有另一个对于RFC2099bis的重要的提升。默认的初始重传的延时从3s变成了1s。如果在发送了初始化拥塞窗口后发生了丢包,在等待数据重新发送的时候可以节省2s的等待时间。在数据流开始的阶段经历丢包的情况下,由于TCP流非常短小,因此,这次可带来非常明显的提升。像PRR补丁一样,这个功能也可以在老版本的内核上生效(需要一些修改),如果你不能够升级内核的话。

Looking forward, Linux 3.3 has Byte Queue Limits when teamed with CoDel (controlled delay) in the 3.5 kernel helps fight the long standing issue of Bufferbloat by intelligently managing packet queues. Bufferbloat is when the caching overhead on TCP becomes inefficient because it’s littered with stale data. Linux 3.3 has features to auto QoS important packets (SYN/DNS/ARP/etc.,) keep down buffer queues thereby reducing bufferbloat and improving latency on loaded servers.

紧接着,Linux 3.3实现的字节队列控制同实现了CoDel(可控延迟)功能的3.5的内核一起工作的时候通过智能的管理数据包队列能够帮助系统解决BufferBloat的问题。Bufferbloat的是指因为TCP缓存中掺杂一些陈旧的垃圾数据导致其低效的情况。在Linux 3.3具有针对重要的数据包(SYN/DNS/ARP/等等)自动QoS的功能,控制缓冲队列来减少bufferbloat,降低高负载服务器的延迟。

Linux 3.5 implements TCP Early Retransmit with some safeguards for connections that have a small amount of packet reordering. This allows connections, under certain conditions, to trigger fast retransmit and bypass the costly Retransmission Timeout (RTO) mentioned earlier. By default it is enabled in the failsafe mode tcp_early_retrans=2. If for some reason you are sure your clients have loss but no reordering then you could set tcp_early_retrans=1 to save one quarter a RTT on recovery.

Linux 3.5实现了TCP快速重传,针对那些少量包重排序的连接实现了一些保障措施。这允许在一些情况下的连接可以触发绕过重传延时(RTO)快速重传。默认是在非安全模式 tcp_early_retrans=2下启用的。如果你有足够的理由确认你的客户端会发生丢包但是不需要重排序,你可以配置 tcp_early_retrans=1在恢复重传时节省 ¼ RTT的时间。

One of the most extensive changes to 3.6 that hasn’t got much press is the removal of the IPv4 routing cache. In a nutshell it was an extraneous caching layer in the kernel that mapped interfaces to routes to IPs and saved a lookup to the Forward Information Base (FIB). The FIB is a routing table within the network stack. The IPv4 routing cache was intended to eliminate a FIB lookup and increase performance. While a good idea in principle, unfortunately it provided a very small performance boost in less than 10% of connections. In the 3.2.x-3.5.x kernels it was extremely vulnerable to certain DDoS techniques so it has been removed.

在3.6版本中被大量改进但并没有太多资料的是去除 IPv4的路由缓存。简单来讲,它是内核中额外的一个缓存层,映射网络接口(网卡)到路由、到IP地址,用以节省一次到转发信息库(FIB)的查询操作。FIB是网络栈内部的一个路由表。IPv4的路由缓存希望通过节省一次FIB查询来提升性能。虽然从原理上来讲是一个好的点子,不过,它仅仅对不到10%的连接有少量的提升。在3.2.x-3.5.x的内核中,它非常容易被一些DDoS技术攻击,所以它在新版的内核中已经被去掉了。

Finally, one important setting you should check, regardless of the Linux kernel you are running: tcp_slow_start_after_idle. If you’re concerned about web performance, it has been proclaimed sysctl setting of the year. It can be enabled in almost any kernel. By default this is set to 1 which will aggressively reduce cwnd on idle connections and negatively impact any long lived connections such as SSL. The following command will set it to 0 and can significantly improve performance:

sysctl -w tcp_slow_start_after_idle=0

最后,无论运行什么版本的Linux内核,你都需要检查的一项重要配置:tcp_slow_start_after_idle。如果你关注WEB性能,这是今年被公开宣告的sysctl配置。这个配置在几乎所有版本的内核中都可以启用。默认情况下,它被设置成1,这将会针对闲置的连接积极的降低拥塞窗口,对长连接比如SSL产生非常消极的影响。下面的命令将会设置它为0,并且显著的提升性能:

sysctl -w tcp_slow_start_after_idle=0

The Missing Congestion Control Algorithm

那些漏掉的拥塞控制算法

You may be curious as to why I haven’t made a recommendation as far as a quick and easy change of congestion control algorithms. Since Linux 2.6.19, the default congestion control algorithm in the Linux kernel is CUBIC, which is time based and optimized for high speed and high latency networks. It’s killer feature, known as called Hybrid Slow Start (HyStart), allows it to safely exit slow start by measuring the ACK trains and not overshoot the cwnd. It can improve startup throughput by up to 200-300%.

你可能会好奇为什么我没有推荐一个 快速和容易改变的拥塞控制算法。 从Linux 2.6.19开始,默认的Linux内核的拥塞控制算法是CUBIC,这是一个基于时间并针对高速和高延迟网络进行优化的。 它的杀手锏,称为混合慢启动机制 (HyStart),使其通过测量ACK 冲击和不超过cwnd实现安全地退出慢启动。 它可以提高启动吞吐量200 – 300%。

 

While other Congestion Control Algorithms may seem like performance wins on connections experiencing high amounts of loss (>.1%) (e.g., TCP Westwood+ or Hybla), unfortunately these algorithms don’t include HyStart. The net effect is that, in our tests, they underperform CUBIC for general network performance. Unless a majority of your clients are on lossy connections, I recommend staying with CUBIC.

而其他拥塞控制算法看起来是在连接经历大量的丢包后(> .1%)获得的性能提升 (如 ,TCP Westwood+ or Hybla),不幸的是这些算法不包括 HyStart。在我们的测试中,他们在一般网络中性能表现不如CUBIC。 除非你的大部分客户端都是在受干扰的有损网络中,否则我建议使用CUBIC。

Of course the real answer here is to dynamically swap out congestion control algorithms based on historical data to better serve these edge cases. Unfortunately, that is difficult for the average web server unless you’re seeing a very high volume of traffic and are able to record and analyze network characteristics across multiple connections. The good news is that loss predictors and hybrid congestion control algorithms are continuing to mature, so maybe we will have an answer in an upcoming kernel.

当然最好的答案是基于历史数据动态交换拥塞控制算法为这些偶发状况提供更好地地服务。 不幸的是,这对于普通的web服务器是非常困难的, 除非你能够看到一个非常大的流量并且能记录和分析网络中很多个连接的特性。 好消息是,损失预测和混合拥塞控制 算法继续成熟,也许我们在即将到来的内核中会有答案。

发表在 System, TCP | 标签为 | 469条评论