0%

最近在看《架构整洁之道》这本书,记录一下一些笔记。

编程范式

  • 面向对象编程:多态是跨越架构边界的手段

  • 函数式编程:是规范和限制数据存放位置与访问权限的手段

  • 结构化编程:是各个模块的算法实现基础

系统设计原则

系统设计中,模块,组件的关系应该是单向依赖的关系。同时,如果A组件不想被B组件上发生的修改所影响,那么就让B组件依赖于A组件,实现方式是依赖反转

系统设计原则

系统在具体实现上,可以分为两部分组件:抽象接口(抽象层)和具体实现(实现层)。

两者之间的边界被称为架构边界,所有跨越这条边界的源代码级别的依赖关系应该都是单向的,即具体实现层依赖抽象层(依赖守则,依赖方向和控制流方向相反,也就是所谓控制反转)。

阅读全文 »

背景

最近重构了我们业务上的一个服务,该服务的功能是为终端提供一些版本文件信息和素材数据信息。

具体到代码逻辑上也非常简单,就是把配置在数据库表里的数据都捞到本地缓存,每次请求的时候,根据终端的版本信息做一些过滤判断,然后把本地缓存的所有文件数据都下发下去。

由于终端请求机制等等历史原因,该服务存在两个问题:一个是每天0点的时候请求流量会暴涨,工作日会涨1倍流量,而节假日有时候可能会3-4倍;另一个是每次请求时,会把数据库中的所有数据全部下发下去,也就导致每次请求返回的包体都很大。

在实际线上监控中发现,每到0点的时候,就会出现耗时猛涨一波的情况,而此时CPU和内存使用率虽然也有上涨,但基本也不会到很高的级别,所以对这里出现的原因进行了具体的分析。

而要说 Go 服务的性能分析,那就不得不提到 pprof 这个性能分析利器,本文也借着本次性能分析优化记录一下使用 pprof 的实战经验。

阅读全文 »

背景

嵌入式数据库 RocksDB 是 Facebook 基于 LevelDB 开发的一种嵌入式 Key-value 存储系统,该数据库能够充分利用闪存的性能,大大提升应用服务器的速度。

Rocksdb 这个开源引擎是基于 Google 的 leveldb 1.5 版本,据说对其做了许多优化,性能相对 leveldb 有了很大的提升,而且解决了 leveldb 主动限制写的问题。

Facebook 使用 RocksDB 来驱动一些面向用户的应用,这些应用由于需要通过网络访问外部存储而性能低下,此外 Facebook 还用 RocksDB 来解决固态硬盘 IO利用率不高相关的一些问题。Facebook 的数据库工程师 Dhruba Borthakur 在其个人博客介绍了 RocksDB 的设计原由和原理,但实际上催生 RocksDB 的最大动力来自服务器闪存存储卡的价格大幅下滑,Facebook 的定制服务器已经开始全面采用闪存。

随着闪存存储时代的到来,一些新的应用可以在闪存中管理并快速访问自己的数据集,无需通过网络访问外部数据。这些新应用使用的就是这种嵌入式数据库

数据库查询如果在本地闪存中进行,速度理论上会比通过数据中心内部网络查询快一倍,因为数据库中心内部网络有50微妙的延迟。

RocksDB 的能够充分利用闪存的高IOPS性能,同时也能利用多核服务器的计算性能。

阅读全文 »

简介

Kafka 被定位为一个分布式流式处理平台,它以高吞吐、可持久化、可水平扩展、支持流数据处理等多种特性而被广泛使用。Kafka 在现代的系统中主要承担三大角色:

  • 消息系统:Kafka 和传统的消息系统(也称作消息中间件)都具备系统解耦、冗余存储、流量削峰、缓冲、异步通信、扩展性、可恢复性等功能。与此同时,Kafka 还提供了大多数消息系统难以实现的消息顺序性保障及回溯消费的功能。
  • 存储系统:Kafka 把消息持久化到磁盘,相比于其他基于内存存储的系统而言,有效地降低了数据丢失的风险。也正是得益于Kafka 的消息持久化功能和多副本机制,我们可以把Kafka作为长期的数据存储系统来使用,只需要把对应的数据保留策略设置为“永久”或启用主题的日志压缩功能即可。
  • 流式处理平台:Kafka 不仅为每个流行的流式处理框架提供了可靠的数据来源,还提供了一个完整的流式处理类库,比如窗口、连接、变换和聚合等各类操作。
阅读全文 »

计算 go 结构体占用的空间

在 go 中,可以使用 unsafe.Sizeof 计算出一个数据类型实例所占用的字节数。

package main

import (
    "fmt"
    "unsafe"
)

type Args struct {
    num1 int
    num2 int
}

type Flag struct {
    num1 int16
    num2 int32
}

func main() {
    fmt.Println(unsafe.Sizeof(Args{}))
    fmt.Println(unsafe.Sizeof(Flag{}))
}
阅读全文 »

业务背景

我们有一款产品跟海外某个国家的客户有业务合作,因此,我们在这个国家的服务器上单独部署了一整套的服务(大概有八九个微服务),这些服务的宿主机大概都集中在三、四台宿主机上。这些服务日复一日,年复一年的并肩作战着,直到有一天……

初见端倪

前阵子,我们在一个服务进行扩容、重启的时候,总是概率性的出现如下错误导致的 panic:

端口用尽panic

Google 的结果是说由于 Linux 分配的客户端连接端口用尽,无法建立 socket 连接导致的。

这时候还有点懵逼,单纯以为是容器的宿主机有问题,于是,我重新在另外一台机器进行了节点扩容,这次很幸运,服务启动成功了,于是乎以为问题已经顺利解决,可以美美的下班了。

阅读全文 »

Go 结构体是否可以比较,为什么?

Go 的结构体是否可以比较,需要视具体情况而定,在某些情况下是可以比较的,而某些情况下比较会不符合预期,甚至直接报错。

先来看看几个例子:

阅读全文 »

使用 channel 导致的死锁和内存泄漏问题

死锁出现的条件

死锁有三个必要条件他们分别是循环等待、资源共享、非抢占式,在 go 并发中出现通道死锁只有两种情况:

  1. 数据要发送,但是没有人接收;
  2. 数据要接收,但是没有人发送;

发送单个值的死锁

在下面这种情况下,就会出现死锁的情况:

a := make(chan int)
a <- 1   //将数据写入channel
z := <-a //从channel中读取数据

原因在于:

  • 有且只有一个协程时,无缓冲的通道
  • 先发送会阻塞在发送,先接收会阻塞在接收处。
  • 发送操作在接收者准备好之前是阻塞的,接收操作在发送之前是阻塞的。

解决办法就是改为缓冲通道,或者使用协程配对

阅读全文 »

Connection reset by peer

最近使用go 中 http 包的默认服务端发起 get 请求,个别请求出现了:Connection reset by peer 的错误。大致的报错长这个样子:

read tcp xx.xxx.xxx.xxx:xx->xx.xxx.xxx.xxx:xx: read: connection reset by peer

在网上查找了一些资料,这里归结使用 http 包的注意事项以及上述错误出现的原因。

阅读全文 »

之前了解了 gomock 的一些基本使用技巧,但是对于单元测试这一 part 一直都是云里雾里,借着最近正好工作上要求写单测,学习一下 go 语言如何进行单元测试。

单元测试的含义

单元测试通常来讲是对代码中的某一个功能单元进行测试,这里的单元可能是一个函数,也可能对应实际业务需求的某一小块功能。通过单元测试,可以检测我们的代码在某一单元功能里存在的问题,减少 bug 的产生。

阅读全文 »