sunlidea's Blog


  • 首页

  • 标签

  • 分类

  • 归档

defer or not defer, that is the question

发表于 2019-06-14 | 分类于 Go

defer是Go语言的一种特殊的流程控制机制,通常用于简化执行各种资源清理和释放操作,比如关闭文件,释放锁,断开连接等等。使用defer可以更加简洁清晰的实现这种清理操作,但同时就像任何事物都有两面性,这种简洁性有时也会带来性能的下降,有些场景因此应该避免使用defer。本文主要是关于对应于不同的场景,如何选择性得正确得使用defer。

defer概念

下面的叙述中,统一用外围函数代指包含defer的外层函数

defer语句调用一个函数,该函数的执行被推迟到外围函数返回的那一刻,外围函数的返回存在两种情况:正常执行到了return语句 或者 函数所在的goroutine发生了崩溃(panic)。也就是说如果外围函数通过显式return语句返回,defer语句真正执行的时机则是在该return语句设置任何结果参数之后,但在外围函数返回其调用者之前。

阅读全文 »

小心使用Go中的Time.After——记一次BUG解决过程

发表于 2019-06-06 | 分类于 Go

最近项目中遇到了一个time.After的错误使用问题,而在常见的gopher学习材料中time.After又常常被用到,以至于我们忽略了背后的性能问题。

问题

这一段时间用户增长很快,通讯服务程序出现一个现象: 最初两台配置较差的服务器上通讯进程的CPU使用率和内存占用率开始缓慢升高,之后加速度越来越大o(╯□╰)o,当内存占用足够高的时候就会被linux的OOM killer机制杀死,之后被supervisor拉起之后重复这一现象。

查找

考虑是由于用户增加快,这两台配置较差的服务器上触发了程序的隐藏bug。观察现象:通讯进程的CPU使用率和内存占用率缓慢升高,首先想到的是goroutine泄露。但是因为我们有监控每个进程的goroutine数量,发现随着时间的推进,goroutine数量是比较稳定的,并无暴涨的情况。利用pprof的goroutine分析,也未发现异常。

再想到,因为内存占有率的不断升高直到快达到100%而被杀死,那可能是因为垃圾回收方面的问题。利用pprof的heap分析,查看当前程序的内存占用情况:

阅读全文 »

Go语言中反射Reflection的原理及应用

发表于 2019-05-23 | 分类于 Go

作为一种静态编译型语言,Go语言中的反射Reflection特性提供了程序运行时检查,修改和创建变量和函数的功能。对于缺乏泛型generic支持的Go语言而言,通过Reflection也可以间接实现泛型的功能。本来主要探究Go语言中Reflection的原理及应用。

reflect.Type和reflect.Value

先来看一下Reflection包里提供的最基本的两个概念:reflect.Type 和 reflect.Value,对应的两个基本函数为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}

// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}

// TODO: Maybe allow contents of a Value to live on the stack.
// For now we make the contents always escape to the heap. It
// makes life easier in a few places (see chanrecv/mapassign
// comment below).
escapes(i)

return unpackEface(i)
}

可以看到,两个函数的参数都是空接口interface{}类型,可见反射Reflection与接口类型Interface密切相关。要了解reflect.Type 和 reflect.Value两个概念,我们需要先理清楚接口类型Interface。

阅读全文 »

字符集 字节编码 Unicode UTF-8

发表于 2019-04-18 | 分类于 字符

在MySQL的建表SQL里,在HTTP的请求和响应的头部里,我们常常见到Unicode与UTF-8的出现。但很多时候即使是作为软件开发者,也很难讲清楚Unicode与UTF-8具体的含义与原理。事实上,如果我们在开发软件的时候”惯性”得选了UTF-8的编码方式,基本不会遇到字符编码相关的问题。

可是了解Unicode与UTF-8,对于软件的国际化和理解字符串类型的存储,解释与传输等又是如此重要。所以我在学习清楚这些概念之后,在这边文章里做一下记录和说明。

阅读全文 »

基于令牌桶算法的流量控制

发表于 2018-11-26 | 分类于 Go , 数据结构

问题起源

作为一款IM软件,负责收发消息的通讯服务器需要仔细处理QPS的压力。为了应对潜在的恶意流量攻击,比如第三方短时间发送大流量到服务器,引起通讯服务器瘫痪,最近以每个会话(例如a<->b的会话)为基本单位加上了流量控制。

限流算法

常用算法比较

常用限流算法包含计时器,漏桶,令牌桶算法。具体的比较与分析,在谈谈高并发系统的限流这篇文章中有比较详细的介绍。

需要注意的是漏桶算法和令牌桶算法的区别:

  • 漏桶算法强制限制了数据的通过速率,并不负责处理突发的大流量;
  • 令牌桶算法则既平均了数据的通过速率,又能够处理突发的流量;
阅读全文 »

基于最小堆的定时器的实现

发表于 2018-11-26 | 分类于 数据结构

概述

定时器(Timer Facility)被用于处理延时任务。

有多种数据结构和算法可以实现定时器,包括最小堆,哈希轮,分级时间轮等。在这篇文章中主要介绍基于最小堆的定时器的实现。

原理

最小堆指的是满足除了根节点以外的每个节点都不小于其父节点的堆。这样,堆中的最小值就存放在根节点中,并且在以某个结点为根的子树中,各节点的值都不小于该子树根节点的值。

最小堆其实就是最小优先队列,将新的定时器添加到堆中,根据绝对到期时间完成最小堆的有序化。

在每个时钟周期比较堆顶的定时器的到期时间和当前的时钟,如果定时器的到期时间是小于当前时间,则执行并删除到期定时器; 继续这样做这样的比较,直到堆顶包含一个过期时间大于当前时间的计时器。

阅读全文 »

[译]Go闭包函数并发处理循环迭代变量的问题

发表于 2018-09-29 | 分类于 Go

总结这一段时间团队在使用Go语言编程时,遇到最多的问题就是在闭包函数并发处理循环迭代变量时处理不当的问题。所以就想总结下这种情况,查资料时看到Go在Github的wiki页面上有一篇专门介绍该问题的文章,可见这个问题被询问的频率之高。这篇文章总结的挺好的,就把它翻译了下来,留作之后查资料值之用。

原文地址: CommonMistakes

相关资料: closures_and_goroutines

简介

当新手程序员开始使用Go或熟练的Go程序员开始使用新概念时,他们中的许多人都会犯一些常见的错误。目前为止,在Go的邮件列表中出现最频繁的错误之一,就是在循环迭代时将迭代数据传入并发的goroutines引发的错误。

阅读全文 »

二叉树的遍历详解

发表于 2018-09-11 | 分类于 数据结构 , 算法 , Java

本文主要对二叉树的各种遍历方式进行详细的描述,包括深度优先遍历(前序遍历|中序遍历|后序遍历)和广度优先遍历(也叫层序遍历),同时从递归和非递归的两种角度进行实现,采用Java语言编写。

详细代码实现

首先给出基础的树节点定义,包括一个获取二叉树总节点个数的方法,后面在后序遍历的非递归实现中会用到。

阅读全文 »

Java Notes-ClassesAndObjects

发表于 2018-08-27 | 分类于 Java

本文是学习Oracle The Java Tutorials中ClassesAndObjects篇章的笔记,记录了中间重点的内容,也方便后面自己查阅。


阅读全文 »

以素数生成器为例阐述Go语言的并发机制

发表于 2018-07-10 | 分类于 Go

并发Concurrency机制可以说是Go语言最重要的特性之一,Go语言的命名就来源于创建并发操作的关键字Go。理解并运用好Go语言的并发就显得十分关键了。本文结合素数生成器的具体实例,从通过通信共享内存的角度初探Go语言的并发机制。

通过沟通共享内存

“Do not communicate by sharing memory; instead, share memory by communicating.”

上面这句话可以说是Go语言并发模型的中心思想了,翻译过来就是:不要通过共享内存进行通信,要通过通信来共享内存。

这句话该怎样理解呢?

阅读全文 »
12

sun li

11 日志
7 分类
5 标签
GitHub E-Mail
© 2019 sun li
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4