自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(37)
  • 收藏
  • 关注

原创 使用 ucontext 实现用户态线程(下)

前面的文章介绍了 ucontext 相关函数的基本作用,我们已经知道如何用 getcontext 等函数实现基本的函数 yield & resume 的操作了。下面来看看如何实现一个简陋的用户态线程库吧!首先来看我们 thread 的定义:#define STACK_SIZE 4096typedef struct thread thread_t;struct thread { char stack[STACK_SIZE]; /* 协程运行栈 */ ucontext_t c

2021-02-06 12:04:21 311 1

原创 使用 ucontext 实现用户态线程(上)

用户态线程,或者称之为协程,因为具有更好的扩展性,在现在的服务端编程中越来越受到重视。今天的文章会介绍在 linux 系统下,如何使用 ucontext 来实现协程库。用户态实现线程库其实有两个常见的选择,其一是 setjmp,但是因为其要想做到协程之间的栈隔离,需要使用汇编语言,实在非大多数程序员所长,这里就不展开介绍了。另一个方案就是本文的主角 ucontext。来看一下今天的主角: #include <ucontext.h> int getcon

2021-02-05 21:37:30 364

原创  c 中的完美封装

面向对象编程为程序员的兵器库中添加了很多重型武器,比如:封装,多态等。通过封装,我们的模块可以强制所有的访问都使用其提供的接口,而不用对外暴露模块内部实现细节,同时也免于其自身状态被"出乎意料"的破坏。典型的做法就是将一个结构体的所有数据字段声明为 private,让其他模块只能通过其提供的 public 函数接口访问:class MyClass {public: int get_data() const { return data_; } int s...

2021-02-04 15:13:52 688 1

原创 一个优雅的 c 链表实现、使用方式

在阅读一些开源的 c 语言项目的时候,发现其实现链表的方式特别有趣且优雅,特此记录学习一下其本质是用一种类似继承的方式来实现 generic 的链表操作,但是我们都知道,c 是没有继承的,那么怎么做呢?首先来看我们的 "base class", 以及其的一些基本操作// circular list,双向循环链表typedef struct clist { clist_t *next; clist_t *prev;} clist_t;/* Insert element "...

2021-02-03 23:30:59 214

原创 手撸一个用户态线程库-为什么要使用协程(用户态线程)

本系列文章旨在带大家手撸一个简单的用户态线程(协程)库。本系列文章假设大家对多线程编程、EDSM(reactor 架构)有一定了解,所以并不会花太多精力去讲解这些内容。用户态线程,或者叫协程,越来越受到重视。这是为什么呢?多线程怎么就不香了呢?用户态为什么要重新造轮子,费劲的去实现一个用户态线程库呢?这背后到底是到的的沦丧,还是 KPI 的扭曲?下面我们来简单探讨一下。多线程编程的局限传统的多线程网络编程中,往往会使用一个内核线程来完整处理一个用户请求,大概伪代码如下:void HandleConn

2020-12-16 23:35:52 435

原创 别让 GC 给你的缓存系统拖后腿

golang 是一门自带 GC(垃圾回收) 的语言,GC 帮助程序员从手动管理内存的繁琐工作中解放出来,不仅提升了程序员的生产效率,还保护了程序员的发际线。天下没有免费的午餐,GC 有时候却可能成为你系统性能下降的罪魁祸首,在系统内存负载较大的情况下,对象数量很多的情况下,尤其容易发生。本文介绍一种降低 GC 开销的的优化措施,借鉴自 bigcache,一个优秀的开源 golang 内存缓存库...

2020-03-25 20:16:27 268

原创 duplication suppression --- 让你的缓存更聪明

在引入缓存组件的业务体系中,下面代码是很常见的:v = cache.get('key')if v is None: v = fetch_from_db('key') cache.set('key', v)# 本例与语言无关尽管上面代码在单线程代码中工作的很好,但是在多线程、或者多协程服务中则还存在一定的优化空间。考虑如下请求序列:# without duplication v|...

2020-02-27 18:21:25 291

原创 redis 源码系列(18):和单点说再见 --- sentinel

有了在主从节点之间同步数据的解决方案之后,我们已经有了运行多个 redis 服务的能力,但是我们仍然缺乏自动处理故障转移的能力。sentinel 是 redis 自带的高可用解决方案。在这个方案中有三种不同角色:masterslavesentinel主从节点的作用不需赘述,他们是数据库服务的直接提供者。sentinel 则主要有以下作用:monitoring,sentinel 会持...

2020-01-27 23:42:07 343

原创 redis 源码系列(17):分身术 --- replication

截至上篇文章,我们对单机版 redis 需要学习的内容已经大体掌握了。但是生产环境中是绝对不能允许某个服务是单点的,所以后续我们来学习关于高可用、扩展性等高级内容。今天要学习的数据同步相关机制,是 redis 高可用和集群模式的基石。数据库服务的扩容和数据备份,是一个非常复杂的问题。因为数据库服务是 有状态的,所以其迁移、扩展都有很多需要仔细考量的地方。redis 采用 master-slav...

2020-01-17 15:18:56 356

原创 redis 源码系列(16):We will call you --- PUB/SUB命令

redis 源代码系列到现在,已经基本上介绍完了单节点 redis 服务的整体架构以及一些特别重要的特性(RDB,AOF 等),但是对于其支持的 commands 还未加以介绍。鉴于这些 commands 大多数都是建立在之前介绍过的几种数据结构之上,其实现逻辑并未有太多值得大书特书之处,这里就不再对每个命令加以讲解,仅挑选 PUB/SUB 和 lua script 脚本相关命令进一步学习。今天...

2020-01-14 22:12:44 222

原创 redis 源码系列(15):快照 + 增量记录 --- AOF

在介绍 rdb 的文章中,我们提到 rdb 并不能完美的解决 redis 持久化的问题。因为其只是 redis 数据库的某一时刻的快照,而且因为 rdb 的 dump 过程往往会消耗大量的服务器资源,所以一般都是在业务低峰期进行,而且不会很频繁,一般都是以天为单位来进行。redis 在 dump 两次 rdb 之间,可能会丢失大量的数据。所以 redis 提供了另一种持久化手段,即 append...

2020-01-14 12:51:39 475

原创 redis 源码系列(14):1,2,3茄子 --- 数据库快照 rdb

Redis 是一个内存数据库,但是通过使用 rdb 和 aof 机制,redis 也支持数据持久化。今天我们来看一下 rdb 相关代码。redis 的 *.rdb 文件是对 redis 内存数据的二进制表示(快照),通过 *.rdb 文件,我们可以恢复 redis 的所有状态。rdbRdb Format我们来看一下一个 rdb 文件的格式,大家千万注意,虽然我们在下面的例子中将 rdb 文...

2020-01-13 17:56:47 276

原创 redis 源码系列(13):我的地盘我做主--- database

redis 内部也像传统的关系型数据库一样,有 database 的概念。主要用于实现数据隔离,这个在生产环境中是非常必要的。多个进程共同使用一个 redis 服务,如果没有数据隔离的机制,对于开发、运维都会有很大的挑战。今天来看一下 redis 中 database 的实现databaseredisDbtypedef struct redisDb { // 数据库键空间,保存着数...

2020-01-10 18:53:41 125

原创 redis 源码系列(12):你们一起上吧 --- redis 事务

作为一个数据库,对于事务的支持是一个绕不过去的话题。今天我们来看一下 redis 中的事务相关代码。multiStateredis 中使用 multiState 结构体来表示一个事务:typedef struct multiCmd { // 参数 robj **argv; // 参数数量 int argc; // 命令指针 struct red...

2020-01-09 23:55:41 104

原创 redis 源码系列(11):command 处理流程浅析

经过上一篇文章的分析,我们已经知道 redis 是如何处理 client 的请求,解析处一个完整的 command,进一步处理这个 command,并且向 client 发送响应。今天来学习一下 redis 的 command 相关代码。redisCommandstruct redisCommand { // 命令名字 char *name; // command 对应...

2020-01-08 22:23:13 1055 1

原创 redis 源码系列(10):事件驱动编程下的网络 IO --- 读写 client

今天的文章讲一下 redis 是如何读取 client 发送的数据,以及如何发送响应给 client。主要讲述网络 IO 相关内容,不会涉及到具体的 command 处理逻辑。Why non-blocking IO首先我们先来说一下为什么 IO 复用一定要和非阻塞 IO 一起使用。IO 复用本质上在复用线程,它用单一的线程并发的处理多个 IO 事件的手段。我们知道阻塞 IO 在 read (...

2020-01-07 18:29:28 261

原创 redis 源码系列(9):百尺高楼平地起 --- redis服务启动

前面已经介绍了 redis 的网络架构、通信协议,我们今天就具体看一下代码,看看 redis 服务是如何启动,服务 client 的。今天不会涵盖 sentinel、cluster 等高级概念,我们仅看看最简单的单节点 redis 服务启动的流程是怎么样的。讲解的代码中,会删除部分与可移植性相关、和不影响立刻主体流程的代码。启动// redis 以守护进程运行void daemonize(...

2020-01-06 22:50:06 179

原创 redis 源码系列(8):Do you speak RESP ?--- redis 网络协议

redis 使用的 client/server 架构。c/s 架构下的 client 和 server 第一个要解决的问题就是进程间通信问题。这个的解决方案很多,最常见的当属网络套接字,而且大多数都采用 TCP 协议通信。建立在 TCP 协议上的应用程序都有自己的应用层协议,常见的比如有 HTTP 协议等。redis 与 client 通信的时候,使用自己定义的 RESP(REdis Seria...

2020-01-05 21:45:09 136

原创 redis 源码系列(7):单线程也可以很diao --- eventloop

redis 本身是一个单线程程序,但是其并发能力在开源的非关系型数据库中却名列前茅,这是如何做到的呢?一个单线程程序,如何能并发处理上万请求?redis 给出的答案是 IO 复用 + 非阻塞 IO 这对神兵利器。在 linux 平台下,常见的 IO 复用手段有:select、poll、epoll 三种,一般在并发很大的情况下 epoll 的性能最佳,select 和 pool 都有其局限性(s...

2020-01-03 11:56:28 255

原创 redis 源码系列(7):简单的对象系统 --- robj

c 语言本身并不是一门面向对象编程的语言,但是很多面向对象的语言(比如 python,c++)都是用 c 来实现的。redis 代码中,也实现了一个简单对象系统。今天就让我们来学习一下,相关代码在 src/object.c,src/redis.h 中。robjredis 中所有对象,都使用一个统一的数据结构表示:#define REDIS_LRU_BITS 24typedef stru...

2020-01-02 18:24:33 229

原创 redis 源码系列(6):实现简单又能打 --- skiplist

跳跃表(skiplist)是一种高级数据结构,其插入、检索、删除的时间复杂度均为 O(logN)。因为其实实现简单,很多项目将其作为红黑树的一个替代品。今天我们来看一下 redis 中 skiplist 的实现,涉及到的文件在 src/t_zset.c 和 src/redis.h。redis 中的跳表基本上与常见跳表有以下几点区别:节点的 key 其实相当于 score + obj,所以在...

2020-01-02 18:19:58 142 1

原创 redis 源码系列(5):能省一点是一点 --- intset

整数是计算机程序打交道最多的数据类型之一,对整数集合的存储和检索,往往会对程序的性能有至关重要的影响。redis 中用 zset 数据结构来处理整数集合(去重),并使用了一定的手段来优化器内存占用,同时又可以以 O(logN) 的时间复杂度来查找数据(ziplist 是 O(N)),而且可以以 O(1) 时间复杂度索引 set 内的某个位置的值。但是因为其底层存储是一个数组,所以插入和删除的时间...

2019-12-31 00:15:17 160

原创 redis 源码系列(4):通用双向链表 --- list

今天我们来学习一下 redis 中 通用双向链表 list 的实现,涉及到的代码有 src/adlist.h src/adlist.c首先来看一下主要结构体的定义,有 node,iterator、list 三个typedef struct listNode { // 前置节点 struct listNode *prev; // 后置节点 struct list...

2019-12-27 21:33:20 117

原创 redis 源码系列(3):最不像链表的链表 --- ziplist

本文讲解 redis 中的 ziplist 数据结构。涉及到的代码文件有 src/ziplist.c src/ziplist.h关于 ziplist 的实现,源代码中作者添加了非常详细明了的注释,这些注释会成为我们后面学习这份代码的臂助。what/* *The ziplist is a special encoded dually linked list that is designed ...

2019-12-27 00:22:41 147

原创 redis 源码系列(2):一看就懂的 string 实现 --- sds

操纵字符串(string)对于大多数的程序,都是一项主要工作。但是 c 语言中没有内置 string 类型,所以 redis 封装了自己的 string 类型 sds。今天我们就看一下 redis 中的 string 是如何实现的。其对应的代码在 src/sds.h 和 src/sds.c 中。柔性数组在分析 redis 的字符串实现前,我们先岔开话题聊一下柔性数组。柔性数组或者叫做 Ar...

2019-12-24 13:00:59 154

原创 redis 源码系列(1):船新的渐进式 hash 表 --- dict

今天我们来分析一下 redis 中的 dict(hashtable)的实现原理redis dict首先来看几个重要的结构体定义// hash table entrytypedef struct dictEntry { void *key; union { void *val; uint64_t u64; int64_t s64; } v; // red...

2019-12-23 15:47:00 140

原创 glog 学习笔记(2)

glog 中有一个 Mutex 的简单封装(仅供内部使用,代码见 ./src/base/mutext.h)。其实按道理 mutex 封装是一个非常常见以及非常简单的 class,我相信任何有一定多线程开发经验的 cpp-er 都应该自己写过这个轮子。但是,glog 中这个 mutex 确提供了一个不同寻常的特性:支持 dynamic-initialization (即在 main() 调用前的全局构造函数中使用)。首先这里先简单解释下 Mutex 其要解决的场景:我们知道,因为 c++ 中构造函数的存在

2021-04-07 06:08:19 214

原创 glog 学习笔记(1)

glog 中,定义了两个宏DECLARE_VARIABLE 和 DEFINE_VARIABLE 用于支持声明、引入一些参数,来控制 glog 的运行时行为。其实现如下:#define DECLARE_VARIABLE(type, shorttype, name, tn) \ namespace fL##shorttype { \ extern GOOGLE_GLOG

2021-04-07 05:42:30 347

原创 leveldb深入浅出

leveldb简介LevelDB 是一个快速的提供持久化存储的 key/value(byte array) 存储数据库,可以把它理解为一个存储在本地磁盘的超大 map<string, string>。其局限性有:不是一个关系型数据库,不支持 sql 查询,不支持索引不支持进程级别的并发不支持类似 memcached、redis 的 c/s 架构leveldb 的整体架...

2019-09-15 11:48:46 432

原创 坑出没 --- Python 中引用型类型浅拷贝

Python 中的对象有两种类型,一种是值类型,一种是引用类型。值类型的代表有 int,而今天的主角引用类型有 list、set、dict 等。引用类型指的是:a = [1, 2, 3]在对象 a 中存储的是一个指针,这个指针指向数组 [1, 2, 3] 的底层数据,类似与 c++ 中的 vector。那么什么叫浅拷贝呢?以下代码shallow_cpy = ashall...

2019-05-18 15:55:45 105

原创 golang 并发(1) --- goroutine 泄漏

最近在工作中使用 golang 编程,因为语言内置对并发的支持(go 关键字),所以 golang 越来越受到服务端开发的青睐。今天的文章给大家分享一下 go 并发编程中一个比较隐晦的 bug --- goroutine 泄漏。注意本文不是 golang 语法的科普贴,如果对 golang channel、go、error 等基础语法不了解的话,推荐大家先去看一下 《go 语言圣经》。...

2019-05-13 11:07:52 1493

原创 一个线程安全的 lrucache 实现 --- 读 leveldb 源码

缓存是计算机的每一个层次中都是一个非常重要的概念,缓存的存在可以大大提高软件的运行速度。Least Recently Used(lru) cache 即最近最久未使用的缓存,多见与页面置换算法,lru 缓存算法在缓存的大小达到最大值之后,换出最早未被使用的缓存。在阅读  leveldb 的源代码的时候,发现其中的 cache 类正是一个线程安全的 lru-cache 实现,代码非常优雅。笔者读完之...

2018-03-10 23:49:24 1614

原创 对象工厂(2)---一个泛化的工厂类

在我的上一篇博客对象工厂(1)---和万恶的 switch 说再见中,我们通过使用函数指针索引的方法,为我们的工厂类代码中消除了 switch 语句。本篇博客的目标是将实现一个泛化的工厂类,实现代码复用。下面让我们先分析一下在对象工厂(1)---和万恶的 switch 说再见中的工厂类的几个主要角色:Abstract 基类(文中的 Shape),通过操作这个基类的指针,整个系统实现了运行期多态Co...

2018-02-28 11:33:32 216

原创 对象工厂(1)---和万恶的 switch 说再见

当系统中存在某抽象基类中有很多具体子类,一个简单实用的策略是创建对象的逻辑封装到一个工厂方法中。这样,可以在不影响客户端代码的情况下扩展具体子类。但是一个低质量的实现(比如像下面的代码,使用了 switch 语句),会导致编译的高耦合以及扩展的高成本,通过阅读 《modern c++ design》一书,看到了一个比较优雅的解决方法。现在假设我们要实现一个图形管理系统,其中 Shape 是抽象基类...

2018-02-27 23:01:17 467

原创 Python 中 property 的使用

    在类似于 c++/java 这样的语言中,为了实现对类内部成员的封装。往往不会直接将一个变量作为公共成员暴露出来,而是会提供一对 get/set 函数来操作需要暴露的成员变量,这样不单提高了封装性, 减小了日后代码修改的影响范围,还可以在操作成员变量的时候提供一些必要的类似监控记录、校验等功能,是非常有用的。体现在代码中就是这样子:class Person {public:    Pe...

2018-02-26 22:28:30 614

原创 并发编程:当IO复用遇到消息队列

    在并发编程中,多线程模型占有举足轻重的地位,而消息队列,又是多线程编程中解决数据竞争的一个神兵利器。通过一个多线程安全的队列,可以简单而高效的实现线程之间的交互,同时不会引入太复杂的编码逻辑。    而IO复用,不管是 select,poll 还是 epoll,解决的都是同一个问题---在一个线程内非阻塞的去处理多个 IO 事件(值得注意的是,非阻塞IO与异步IO不是一个概念,其中亦有区别...

2018-02-25 23:00:13 534

原创 c++ compile-time 检测两个类型之间是否可以转化

    在学习《modern c++ design》的时候,学习到了如何在 compile-time 检测两个类型之间是否可以转化。    这里的转换,既包括 int,long,double 这些数据类型之间的转换,也包括基类和子类之间的转换(也就是两个类之前是否存在 class A 继承自 class B)。但是这篇文章最核心的点在于 compile-time 这个限定词。因为这些检测是在 co...

2018-02-23 22:18:23 449

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除