结合使用AWS SQS和Golang的最有效方法

时间:2019-04-16 14:36:10

标签: amazon-web-services go gzip amazon-sqs

使用AWS SQS(简单队列服务)时,您需要为对服务的每个请求(推,拉,...)付费。您可以发送到队列的每条消息最大为256kb。

为了节省金钱,我想在发送到SQS之前缓冲发送到Go应用程序的消息,直到我有足够的数据来有效使用256kb的限制。

由于我的Go应用程序是一个Web服务器,所以我目前的想法是使用字符串互斥锁并附加消息,只要我超过256kb的限制,然后发出SQS推送事件即可。为了节省更多空间,我可以先对每条消息进行gzip压缩,然后再将其附加到字符串互斥锁。

我想知道是否可以使用某种gzip流。我的假设是,将所有串联的消息gzip压缩在一起会产生较小的大小,然后将每条消息gzip压缩后再附加到字符串互斥锁。一种方法是在每次追加之后对字符串互斥锁进行gzip压缩以验证其大小。但这可能很慢。

有更好的方法吗?还是有一个更好的涉及渠道的方法?我必须承认我还是Go语言的新手。

3 个答案:

答案 0 :(得分:0)

我会采用以下方法

  • 使用频道接受进入go例程的“内部”消息
  • 在该go例程中,将消息保留为“原始”格式,因此10条消息是10个未压缩的原始项目
  • 每次有新的原始项目到达时,将所有原始消息压缩为一个。如果包含新消息的大小> 256k,则压缩除最后一条消息以外的消息并推送到SQS

这在计算上是昂贵的。每个单独的消息导致所有未决消息的完全压缩。但是,对于SQS使用来说是有效的

答案 1 :(得分:0)

您可以估计压缩后的邮件的大小,并计算您是否已达到最大大小阈值。跟踪消息大小计数器,对于每个新消息,将计数器增加其预期的压缩大小。仅当您的计数器超过256kb时,才进行实际压缩并发送到SQS。因此,您可以避免每次收到新邮件时都进行压缩。 对于这样的用例,对一组消息示例进行一些测试应该可以得到预期的压缩百分比。

答案 2 :(得分:0)

在专注于压缩之前,请消除双方都知道的冗余数据。像msgpackprotobufAVRO之类的编码就是这样。

假设您的所有消息都是这样的结构:

type Foo struct {
    bar string
    qux int
}

,您正在考虑将其编码为JSON。然后,您可以做的最有效的操作是:

{"bar":"whatever","qux",123}

如果您只想将所有这些内容附加到内存中,则可能会得到以下内容:

{"bar":"whatever","qux",123}{"bar":"hello world","qux",0}{"bar":"answer to life, the universe, and everything","qux",42}{"bar":"nice","qux",69}

一个非常好的压缩算法可能会查看数百条消息,并确定{"bar":"","qux",的重复性。

但是压缩必须起作用才能每次从数据中找出来。

如果接收代码已经知道每个消息具有什么“模式”(数据的{"bar": some_string, "qux": some_int}“形状”),那么您可以像这样序列化消息:

"whatever"123"hello world"0"answer to life, the universe, and everything"42"nice"69

请注意,在此示例编码中,您不能只是从数据的中间开始就清楚地找到自己的位置。如果您有一堆消息,例如{"bar":"1","qux":2}{"bar":"2","qux":3}{"bar":"3","qux":4},则编码将产生:"1"2"2"3"3"4,而您不能只从中间开始,确定要查看的是数字还是字符串-必须从头算起。是否重要取决于您的用例。

您可以提出其他更明确的简单方案,或者使编写或读取消息的代码更容易或更简单,例如使用在其他数据的编码中转义的字段分隔符或消息分隔符(就像\"如何在带引号的JSON字符串中转义)。

如果您不能让接收者仅仅知道/对期望的消息模式进行硬编码-如果您需要JSON之类的完全灵活性,并且始终将其编组为map[string]interface{}之类的东西-那么您应该考虑使用类似BSON之类的东西。

当然,您不能直接使用msgpack,protobuf,AVRO或BSON-它们需要一种允许0x0之类的任意字节的介质。并根据AWS SQS FAQ

问:我可以在邮件中包含哪种数据?

Amazon SQS消息最多可包含256 KB文本数据,包括XML,JSON和未格式化的文本。接受以下Unicode字符:

#x9 | #xA | #xD | [#x20至#xD7FF] | [#xE000到#xFFFD] | [#x10000至#x10FFFF]

因此,如果要针对您的确切用例寻求最大的空间效率,则必须编写自己的代码,这些代码使用那些编码方案中的技术,但只使用SQS消息中允许使用的字节。

相对地,如果您有很多整数,并且您知道它们中的大多数都是小整数(或者聚集在数字线的某个位置附近,那么通过向所有整数添加恒定的偏移量,您就可以制成大多数整数)小),则可以使用variable length quantity技术之一对所有这些整数进行编码。实际上,上面提到的那些普通编码方案中的几种在整数编码中使用可变长度量。如果您使用六(6)位的“片段大小”(而不是标准的隐式假定的八(8)位片段大小来匹配一个字节),则可以使用base64。 不是完整的base64编码,因为填充将完全破坏此目的-只需将六位的64个可能值映射到base64使用的64个不同的ASCII字符即可。

无论如何,除非您知道您的数据有很多重复(但不是您不能发送的那种,就像每条消息中的相同字段名称一样),否则我将以所有这些开头,然后我才能查看压缩。

即使如此,如果您想要最小的尺寸,我将目标放在LZMA上;如果您想要最小的计算开销,我将使用LZ4。 Gzip本身并不坏-如果使用gzip容易得多,那么就使用它-但是,如果您要针对大小或速度进行优化,则有更好的选择。我不知道gzip甚至不是速度,输出大小和工作内存大小的良好“中间地带”-它已经很老了,也许到现在为止压缩算法在速度,输出和内存大小上都严格地优越。我认为gzip根据实现的不同,还包括标头和框架信息(例如版本元数据,大小,校验和等),如果您确实需要最小化大小,则可能在SQS上下文中将其包括在内您可能不需要的消息。