您的位置:首页 > 其它

简洁和高效是我们永恒的眷恋

2015-11-28 20:54 239 查看

0x00 申明

本文并非水文,不会对Go语言做任何推广性宣传,只是陈述一个事实!

0x01 缘起

对 于我们猿(媛)类来说,写出的每一本程序都希望是:对于阅读代码或者维护代码的人是简洁的,对于服务方来说是高效的!简洁和高效这两个词,自从计算机出现 那一天起,一直都是一个至高无上的目标。个人认为面向对象提出来的“可维护性,可扩展性”只是“简洁”的引申义,而“可靠性,稳定性”则是“高效”的引申 义而已。

不知道别人是怎么认为的,至少我是一直这么追求的。所以几乎每次打开电脑第一件事都是关注一下业内新闻趣事,总希望能在万花丛中,找出自己欣赏的那一朵
(因为自己渣渣,种不出花来)。大概是2011年,在国内第一次听说Go语言(事实上2009年就诞生了),那个时候四月份平民(真名不知道)他们一帮子
在积极的学习研究Go语言,翻译国外相关文献(一个小插曲,Go语言里的chan关键字,他们本来翻译成频道,我觉得太不形象了,建议修改成通道,后来就
一直延续下来了),我也因为她的显著特性而臣服,把玩了一段时间后,因为那个时候她还没有成熟,标准库功能又弱,加之她“蹩脚”的臭脾气(语法),非得大
家去迎合她,不像C/C++那样,随意摆弄,你要不怕被同事们拍死,完全可以写出只有空格一种字符的程序来!后来就远离了她,直到去年,一次意外的邂逅,
发现她已经出落的如此美丽可人。

0x02 问题


何才能最大程度上挖掘服务器的处理能力?答案显而易见:并发。并发原语从unix诞生那一天起经过三代进化,分别是多进程->多线程->多协
程,协程概念不清楚的,请自行脑补。如今各大语言工具都支持这个概念,然后实现难易繁简程度各有不同,下文着重比对C/C++与Go之间的差别。

实例:并发100万协程,每个协程处理从0累加至10000,然后阻塞

关注结果:1:并发100W,所需的时间 2:每个协程处理是否正常

(有的童鞋会奇怪为什么每个协程要处理从0累加至10000,有什么意义? 我是这么设计的,个人认为每一个功能从开始到结束,平均大概需要10000个指令周期,这么测,肯定不可能覆盖所有案例,但足以说明一个大概了)

0x03 手起

测试环境: centos 7

内核版本号: 3.10.0-229.20.1.el7.x86_64

CPU: 24核 MEM: 32G

Go语言版本号:1.4.2

Go测试代码

package main




import (


"net/http"


"runtime"


"runtime/debug"


"runtime/pprof"


"time"


)




var maxRoutine int = 1000000


var quit = make(chan struct{})




func routine(index int) {


//  println("OK,Over I'm routine NO. ", index)


sum := 0


for i := 0; i < 10000; i++ {


sum += i


}


//  println("routine No.[", index, "] sum=", sum)


<-quit


}




func handler(w http.ResponseWriter, r *http.Request) {


w.Header().Set("Content-Type", "text/plain")


p := pprof.Lookup("goroutine")


p.WriteTo(w, 1)


}




func main() {


println("threads : ", debug.SetMaxThreads(maxRoutine))


runtime.GOMAXPROCS(runtime.NumCPU())


go func() {


tick := time.Tick(10 * time.Second)


for {


select {


case <-tick:


func() {


runtime.GC()


}()


}


}


}()




println("test max routines!")


var i int = 0




begin := time.Now()


for i < maxRoutine {


i++


go routine(i)


}


println("test max routines end. escape time:", time.Now().Sub(begin).Seconds())


//  select {}


http.HandleFunc("/", handler)


http.ListenAndServe(":9090", nil)


}


C/C++测试思路简述:

统 计平台CPU核心数,根据核心数开辟等同的线程数,每一个线程绑定在一个CPU核心上,然后再在线程上开辟大量的协程来处理事务。在目前测试环境下,总共 24核,开辟24个线程绑定在24个核上,每个线程开辟4166个协程,第一线程开辟的协程数为:41682,最后让每一个协程处理累加事务,统计测试结 果!

(具体代码测试结果,随后补上,这一阵子,有点事,没空整这个)

0x04 刀落

测试结果:

程序运行中协程数:



创建100万个协程所需要的时间(单位:秒):



程序创建完成的协程是否仍然正常运行,根据打印出的日志可以看出是正常运行的,因为是并发,量大且凌乱,在这里就没有贴出图来。

说明

本文的一次验证为了下一篇实现C1M(connection one million)级打下基础

若清除去运行时监测代码,代码总量仅为50行,流程非常清晰

两版代码相比,难易繁简度立分高下,更重要的C/C++实现的那一版,就看看思路就觉得心力交瘁,尚且不说这里面还有很多需要细致打磨的地方了

0x05 题外

按照现行的主流服务器配置一般情况下,一个功能处理时间都该定界在10微秒以下,上到几十,几百微妙,就该考虑优化了,如果你跟我提建立一个数据库连接就需要几百毫秒了,那这就钻牛角尖纯扯淡了

一台服务器的并发数到底能有多大,这个具体数字准确的讲是要依赖于你的应用到底占用了多少资源。在一个服务尚未实现,但又想知道一个概要值参考的
话,可以查看/proc/sys/fs/file-max这个文件里的数值(当然这个值是可以修改的),这个数值的默认值是系统根据标准堆栈数值以及硬件
资源自动计算出的一个数字,实际应用中可以根据具体情况做适当的调整。

0x06 责任

我们应该竭尽全力让各种资源发挥它应有的光芒。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: