Posts

简述https过程

Redis高可用方案

Redis持久化

Redis数据结构相关

字符串 redis字符串是一个二进制安全的,并自带有长度信息。字符串叫做「SDS」,也就是Simple Dynamic String. struct SDS<T> { T capacity; // 数组容量 T len; // 数组长度 byte flags; // 特殊标识位,不理睬它 byte[] content; // 数组内容 } 当字符串长度很短时,使用embstr的格式,当长度超过44字节时,使用raw形式存储。raw形式字符串内容与RedisObject对象的不是在一个连续的内存空间中。 字典 dict有两个hasttable,通常情况下只有一个hashtable有值,但在dict扩容缩容时,要分配新的hashtable。 渐进式rehash: 当redis处于rehash进程中,redis的增删修改等操作都会触发rehash小步搬迁,并且redis还会在定时任务中对词典进行主动搬迁。 压缩列表 zset和hash容器对象在元素个数较少的时候,采用压缩列表(ziplist)进行存储。 当set集合容纳的元素都是整数并且元素个数较小时,Redis会使用intset来存储结合元素。 快速列表 redis为了节省普通列表因pre,next指针所带来的内存开销和碎片化,采用快速列表的来实现列表。 quicklist时ziplist和linkedlist的混合体,它将linkedlist按段切分,每一段使用ziplist来紧凑存储,多个ziplist之间使用双向指针串接起来。 struct ziplist { … } struct ziplist_compressed { int32 size; byte[] compressed_data; } struct quicklistNode { quicklistNode* prev; quicklistNode* next; ziplist* zl; // 指向压缩列表 int32 size; // ziplist 的字节总数 int16 count; // ziplist 中的元素数量 int2 encoding; // 存储形式 2bit,原生字节数组还是 LZF 压缩存储 .

Kafka简单总结

Golang Interface

引入 func Foo(x interface{}) { if x == nil { fmt.Println("empty interface") return } fmt.Println("non-empty interface") } func main() { var x *int = nil Foo(x) } 上面的例子的输出结果如下 $ go run test_interface.go non-empty interface interface底层结构 根据 interface 是否包含有 method,底层实现上用两种 struct 来表示:iface 和 eface。eface表示不含 method 的 interface 结构,或者叫 empty interface。对于 Golang 中的大部分数据类型都可以抽象出来 _type 结构,同时针对不同的类型还会有一些其他信息。 type eface struct { _type *_type data unsafe.Pointer } type _type struct { size uintptr // type size ptrdata uintptr // size of memory prefix holding all pointers hash uint32 // hash of type; avoids computation in hash tables tflag tflag // extra type information flags align uint8 // alignment of variable with this type fieldalign uint8 // alignment of struct field with this type kind uint8 // enumeration for C alg *typeAlg // algorithm table gcdata *byte // garbage collection data str nameOff // string form ptrToThis typeOff // type for pointer to this type, may be zero } iface 表示 non-empty interface 的底层实现。相比于 empty interface,non-empty 要包含一些 method。method 的具体实现存放在 itab.

Golang array and slice

Golang初始化顺序

一个例子 package main import ( "fmt" ) func init() { a := "xxx" fmt.Printf("hello, %s, %s", "world!", a) } func main() { fmt.Println("main…") } 编译之后执行:go tool objdump -s "main.init" main。可以看到 TEXT main.init.0(SB) /Users/chenkang/learn/go/go_under_the_hook/03/main1.go … main1.go:9 0x108f25a e801caf7ff CALL runtime.convT2Estring(SB) … main1.go:9 0x108f2f2 e89981ffff CALL fmt.Printf(SB) … main1.go:7 0x108f30e e83dc3fbff CALL runtime.morestack_noctxt(SB) main1.go:7 0x108f313 e998feffff JMP main.init.0(SB) … TEXT main.init(SB) <autogenerated> … <autogenerated>:1 0x108f3fe e89d56f9ff CALL runtime.throwinit(SB) … <autogenerated>:1 0x108f40c e84ffbffff CALL fmt.

Websocket协议总结

概述 WebSocket协议被设计来取代现有的使用HTTP作为传输层的双向通信技术,并受益于现有的基础设施(代理、过滤、身份验证)。协议有两部分:握手和数据传输。 来自客户端的握手看起来像如下形式: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 重点请求首部意义如下: * Connection: Upgrade:表示要升级协议 * Upgrade: websocket:表示要升级到 websocket 协议。 * Sec-WebSocket-Version: 13:表示 websocket 的版本。如果服务端不支持该版本,需要返回一个 Sec-WebSocket-Versionheader ,里面包含服务端支持的版本号。 * Sec-WebSocket-Key:与后面服务端响应首部的 Sec-WebSocket-Accept 是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。 来自服务器的握手看起来像如下形式: HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat Sec-WebSocket-Accept 根据客户端请求首部的 Sec-WebSocket-Key 计算出来。 计算公式为: toBase64(sha1(Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11)) 一旦客户端和服务器都发送了它们的握手,且如果握手成功,接着开始数据传输部分。 这是一个每一端都可以的双向通信信道,彼此独立,随意发生数据。

阅读长链接服务架构总结

背景 目前项目中用到了公司的长链接服务,抽空研究了一下公司长链接服务源码。 如果没有长链接服务,一些消息推送等功能只能用短链接轮询,或者长链接轮询的方式。长链接能够很好的解决短轮询和长轮询的一些不足。 整体架构 !架构图 数据流交互 !数据流交互 客户端传送fpid, aid, device_id, access_key等参数与grontier建立websocket连接, websocket 的subprotocol为pbbp2。 grontier维护webscoket连接池, 维护在内存中。 grontier向backbone注册映射, backbone将注册关系存入redis中。 客户端发送业务消息到grontier,grontier根据路由service, method参数进行路由调用backservice, backservice处理业务逻辑,将下行消息通过调用backbone接口返回,backbone将下行消息返回给grontier, 最终由grontier 返回给客户端。 源码分析 grontier/main.go 主要是启动两个server, 一个是thrift server, 一个是websocket server, 端口不一样。 其中thrift server对是用于与backbone和backservice交互的, websocket server是与客户端交互的。 rontier/thrift.go grontierThriftServer主要有3个函数,一个Start, 一个Close和handle。 Start是启动一个tcp监听端口,等待连接,如果有链接请求来了,则起一个协程调用handle处理这个链接。 handle 函数首先创建一个grontierServerhandler h,然后调用h.Serve,并传入BufferReader r, BufferWriter w。 在grontierServerHandler中,Serve函数循环从r中读取数据包,首先读取MessageHeader,包括messageType (int32),name(string),seq(int32)。其中name目前支持”PushByUUID” , messageType只支持CALL 。 读取正确的messageHeader后读取参数grontierPushByUUIDArgs。 读取完整的请求后,向request channel 塞入一条记录。 Serve函数还启动了一个协程,遍历channel 中的请求(request), 调用grontierHandler的PushByUUID函数,也就是调用grontierClient的PushByUUID接口(是业务应用调用grontier对外提供的接口吗?还是backbone调用grontier?) Close是关闭tcp链接, 是在服务被shutdown的时候调用的。 grontier/grontier.go grontierWebsocketServer的结构体定义如下: type grontierWebsocketServer struct { mu sync.Mutex listeners []net.