关于Handle、HandleFunc、Handler和HandlerFunc
在go中,可以很简单实现一个http服务器。
但是在使用过程中,遇到了一些容易使人迷惑的类型、接口或方法名。
于是决定深入了解并记录一下它们的区别。
ps: 本文的前置知识是,需要对go中的面向对象和接口有一定的了解。
我们可以这样创建一个http server
123456789101112131415161718192021222324package mainimport ( "fmt" "net/http")type Hello struct { name string}func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello %s", h.name)}func indexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world")& ...
关于tcp的”粘包“和“拆包”
第一次听到“粘包”这个名词,还是用 websocket 做游戏的时候,当时服务端收到的消息出现了乱码,因为中文汉字在utf-8中用三个字节表示,如果被截断了话,解析出来就是乱码。
后面就一直做的传统的http服务端开发,就没有太关注这个问题了。
最近在看一个《趣谈网络协议》课,就顺便把这个问题搞明白。
为什么会出现”粘包“和“拆包”知乎上有些说什么 “中式伪科学” 之类的来鄙视称呼“粘包”和“拆包”的人。
关于“粘包”这个名词,我们不去讨论它的正确性,我觉得只要能使大家达成共识,明白表达的意思就行。
关于tcp的知识也不详细说了,上一篇文章就是《趣谈网络协议的笔记》。
首先tcp是一个面向流的全双工协议,并且在发送端和接收端分别有一个 发送缓冲区和接收缓冲区。
因为 tcp 是一个面向流的协议,而流是没有边界的,使用流来传输数据时,tcp 并不能理解这些数据在业务层含义,并且有缓冲区的存在,tcp不会及时的读取和发送数据。
所以发生“粘包”和”拆包“的原因差不多就是:
要发送的数据大于tcp缓冲区的大小,会发生“拆包”
待发送数据大于MSS(最大报文长度),TCP在传输前将进行” ...
go的defer关键字
go 语言中的 defer 关键字常用语关闭文件描述符、数据库连接或解锁资源。(这是 node.js 没有的)
本文主要研究一下,defer 如何使用以及它的执行机制。
基本用法假设有如下方法,用于处理一个文件:
12345678910func DealFile () error { file, err := os.Open("test.txt") if err != nil { return err } // 省略业务操作 // ... file.Close() return nil}
这段程序有一个问题。。如果 os.Open() 返回了一个错误,那么将永远不会执行 file.Close(),当打开的文件数过多时,就会触发”too many open files“的系统错误,从而让整个系统陷入崩溃。
那么如何避免呢,可以使用 defer 关键字,在 os.Open() 之后马上调用 file.Close():
1234567891011func DealFile () error { f ...
趣谈网络协议-笔记
公司开了个极客时间企业版,一人挑五门课
看了下这个《趣谈网络协议》挺有意思的
只看了前两讲就感觉收获挺大,所以记记笔记方便以后查找知识
通信协议综述协议三要素
语法: 符合一定的规则和格式。例如括号要成对,换行要有分号。
语义: 代表某种意义。例如数字可以相间,字符串相减没有意义。
顺序: 就是先干什么后干什么。例如 1+1-1 就是 先执行 1+1 在用结果减 1。
网络协议和层级
层级
协议
应用层
DHCP HTTP HTTPS RTMP P2P DNS GTP RPC
传输层 (TCP/UDP 层)
UPD TCP
网络层 (IP层)
ICMP IP OSPF BGP IPSec GRE
链路层 (MAC层)
ARP VLAN STP
物理层
网络跳线
当网络包到达一个城关的时候,可以通过路由表得到下一个城关的 IP 地址,直接通过 IP 地址找就可以了,为什么还要通过本地的 MAC 地址呢?
mac地址 相当于身份证号(网卡厂商分配,理论上是唯一的但可以被修改),ip地址 相当于现居住地(连接到网络时,网络运营商分配)。
历史原因,网络发 ...
go程序设计语言-slice课后练习
本来想写一片关于 slice 切片的文章,但是又复习了一下《go程序设计语言》,并且网上类似的东西数不胜数。。
于是想着把课后练习题做了吧,(ps:当时看第一遍的时候不太明白所以没做)。
本文代码可以直接在 playground 中运行。
练习 4.3:
重写reverse函数,使用数组指针代替slice。
1234567891011121314151617package mainimport ( "fmt")func main() { nums:=[10]int{1,2,3,4,5,6,7,8,9,10} Reverse(&nums) fmt.Println(nums) // [10 9 8 7 6 5 4 3 2 1]}func Reverse(nums *[10]int) { for i, j := 0, len(nums)-1; i < j; i,j = i+1,j-1 { nums[i], nums[j] = nums[j], nums[i] }}
...
golang的gc
和node.js一样,go语言也是有 GC 的,但是 GC 的方式和 node.js 有很大的区别。
本片文章主要对照着 node.js ,分析一下 go 的 GC 机制。
常见的gc算法这里整理一下常见的几种垃圾回收算法,为后文做一下铺垫。。
引用计数引用计数 (Reference counting)会为每个对象维护一个计数器,当该对象被其他对象引用时加一,引用失效时减一,当引用次数归零后即可回收对象。
主要使用语言:
python、php、objective-C 和 C++ 标准库中的std::shared_ptr等。
优点:
原理和实现都比较简单
回收的即时性:当对象的引用计数为0时立即回收,不像其他GC机制需要等待特定时机再回收,提高了内存的利用率
不需要暂停应用即可完成回收
缺点:
无法解决循环引用的回收问题:当ObjA引用了ObjB,ObjB也引用ObjA时,这两个对象的引用次数使用大于0,从而占用的内存无法被回收。
时间和空间成本较高:一方面是因为每个对象需要额外的空间存储引用计数器变量,另一方面是在栈上的赋值时修改引用次数时间成本较高
引用计数是一种摊销算法,会 ...
go中的rune类型
在 java 等语言中,有char类型,而在go语言中,使用的是 rune 类型。
char 类型在 java中是16位的,因为用的是unicode编码。
而 rune 类型在 go中是32位的,因为go用的是utf-8编码。
可以在这个网站查看码点:UTF-8 encoding table and Unicode characters
什么是rune源码中,rune是这样定义的:
rune 是int32的别名,在所有方面都等同于int32
123// rune is an alias for int32 and is equivalent to int32 in all ways. It is// used, by convention, to distinguish character values from integer values.type rune = int32
我们做个测试:
12345678package mainimport ( "fmt")func main() { fmt.Println(len("z中国" ...
关于event-loop
首先,js 不同宿主环境对 event loop 的实现是有区别的,这里说的都是 node.js 中的 event loop
关于 node.js 的 event loop ,之前写过两篇文章,最近回头看的时候,看不下去给删了
其实我的理解主要来源于:
node官方文档
cnode论坛一位大佬的文章
由于cnode论坛挂过一次,并且部署在境外,如果上面的连接访问不了,可以访问我复制的这个
之前不过是照虎画猫,写的太拉胯了,这里就不在分析和探究原理了。。
这里主要是用代码来验证一下 event loop 的执行顺序,以便回顾知识的时候更加直观。
下面的代码都在github仓库
setTimeout 和 setInterval 的执行顺序?
根据代码输出,可以知道,timers阶段的 setTimeout 和 setInterval 执行顺序是一个 FIFO 的队列。
12345678910111213141516171819202122const time = 1000setTimeout(() => { console.log('timeout ...
golang的“占位符”整理
在 golang 中,格式化输出的占位符有很多种。这里整理一下方便用的时候查阅
通用占位符
占位符
说明
%v
值的默认格式表示
%+v
类似 %v,但是输出结构体时,会添加字段名(key)
%#v
值得go语法表示
%T
打印值得类型
%%
百分号
测试代码:
123456789101112131415161718192021222324252627package mainimport "fmt"func main() { fmt.Printf("%v\n", 123) fmt.Printf("%v\n", "hello") fmt.Printf("%v\n", false) obj := struct { name string }{"测试一下"} fmt.Printf("%v\n", obj) fmt.Printf("%+v\n", ob ...
WeekMap和WeekSet
在es6中,不仅引入了 Map 和 Set, 还有 WeekMap和WeekSet,不过我没怎么用过。
首先WeekMap 和 WeekSet 的键只能是对象;其次垃圾回收不会考虑它们对对象的引用,本文主要分析它们对于 gc 的区别。
测试代码地址:https://github.com/ruomuc/test_demos/tree/master/blog/WeekMap%E5%92%8CWeekSet
首先分析一下Map,看这样一段代码:
123456789101112131415const { printHeap } = require('./util')const map = new Map()function testMap () { for (let i = 0; i < 1000000; i++) { const obj = { [`key_${i}`]: i } map.set(obj, i) }}printHeap(' ...