读redis-py客户端源码

前言 看别人的代码也是对自己思维和经验的学习丰富过程 作为一个初学者,之前写过的代码量较少,很少涉及到完整的项目开发,看完redis-py库后,get到其中的 连接保活机制 连接池的实现 开辟buffer存入从socket接收来的数据及buffer管理 熟悉了RESP协议 多进程多线程的情况下,利用锁确保连接池数据结构安全 我读的过程中觉得值得注意的是,_in_use_connections使用集合结构;此连接池不是在初始化时创建好一定数量的tcp连接;其中用了两个互斥锁,一个保护连接池,一个保护多进程的池;较多的连接重连,确保连接可用 执行过程 该库主要有Redis、Connection、ConnectionPool、PythonParse、SocketBuffer几个类,下面大概理了一下redis-py的执行过程 开始使用redis-py客户端 class Redis实例化 可以关注下面几个参数: socket_timeout=None, socket_connect_timeout=None, socket_keepalive=None, socket_keepalive_options=None, connection_pool=None, retry_on_timeout=False, max_connections=None, single_connection_client=False, # 是否单个连接,不用连接池 health_check_interval=0, class ConnectionPool连接池初始化ConnectionPool(),此时尚未创建连接 # 此处定义连接池最大连接数 max_connections = max_connections or 2 ** 31 # fork_safe,在_checkpid()方法中用到,保护临界区的锁。这个锁是在进程id改变时获得的。比如fork出一个子进程后,子进程id和池对象中保存的id不一致,那么子进程中的多个线程都可能会先获取此锁,第一个获得锁的线程将重置此池的数据结构并最终释放锁对象,后续的线程再执行时,pid已于子线程池中的pid熟悉一致,不再做其他操作,在下面也会有提到 self._fork_lock = threading.Lock() # 定义了并初始化已创建连接数、使用中的链接、可用的连接等数据结构 self._lock = threading.Lock() self._created_connections = 0 self._available_connections = [] self._in_use_connections = set() 注意此处_in_use_connections使用了集合存储池中的连接对象,这个与python数据类型时间复杂度有关,可点此参考官网,集合的内部实现与字典极为相似,此集合对象只用到两个操作,add和remove,时间复杂度均为O(1),(有误烦请指正🤡🤡🤡) 初始化Redis-Client状态信息完毕,此时还没有任何连接被创建 假设开始执行 r.set('foo', 'baiqi'),此方法返回r.excute_command()的结果 首先尝试从池中获取一个Connection对象 pool.get_connection(command_name, **options) 获取时得先执行下_checkpid()方法,再执行get_connection() 在现代操作系统下保证ConnectionPool fork-safe,连接池的所有方法都会先调用此方法再来操纵连接池的状态。如果当前pid和池对象中保存的pid不一致,可以假设当前进程是fork出来的子进程,子进程不能用父进程的文件描述符(比如sockets),因此它会继续调用self.reset()方法重新初始化当前进程的连接池;如果pids都一致那么直接pass。 而self._fork_lock就是确保了在子进程中的多个线程不会多次执行self.reset()方法。因为在子进程中,第一个调用_checkpid的线程调用了reset()方法使得self.pid置为当前子进程的id。 从池中获取连接时加锁保护,如果池中_available_connections.pop()没有连接,那么开始创建连接make_connection(),并将此连接加入到池的已使用连接集合中,即self._in_use_connections集合 创建连接时得先确认下连接数是不是超过了最大连接数配置,没有则继续创建,返回Connection对象。所以这里的tcp连接数不是在应用一初始化就按照池配置中的连接数来直接一次性创建好多少个连接...

March 6, 2020 · 1 min · 208 words · erpan

Jenkins Pipeline初体验

使用jenkins pipeline共享库,各应用都可以引用共享库方法,更改共享库即可应用到所有使用此库的jenkins-job。我目前没有用到vars目录,但完全能够满足我们日常需求,使用方式上可能较low,直接开始体验。 下面列出了定义的部分方法,以作参考。 共享库目录结构: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 jenkins-pipeline-libraries git:(master) ✗ tree . . ├── jenkins-ci │ └── jenkinsfile-java ├── out │ └── production ├── src │ ├── ops │ │ └── jk │ │ ├── appDetail.groovy │ │ └── tools.groovy │ └── pipeline.gdsl └── vars └── pipelineCfg.groovy 7 directories, 6 files appDetail.groovy文件里面部分函数如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 // 获取时间 格式:20201208200419 def getTime() { return new Date()....

January 12, 2020 · 6 min · 1266 words · erpan

vim 常用操作及配置

vim常用操作汇总 光标移动 上下左右 k/j/h/l 行首 0 或者 ^ 行尾 $ 单词和字符串间移动 指令 动作 w/W 正向移动到下一个单词开头,大写的忽略特殊字符、符号等 b/B 和w相对应的反向移动,移动到上一个单词开头 e/E 正向移动到下一个单词结尾 ge 反向移动 {num+} f/F+空格 正向/反向移动到本行空格的地方,num相当于移动几次 跳转 指令 动作 ctrl+f 向下翻页 ctrl+b 向上翻页 ctrl+d/u 向下/向上翻半页 gg 文件行首 G 最后一行 行号+gg/G 都是跳到指定行号的行 g+ctrl+g/G 查看光标位置,输出包含行、列、词、字节位置 缩进操作符 指令 动作 » 和 « 或者 :> 和 :< 当前行右缩进和左缩进,后者命令行模式 :line_num1,line_num1+x> 或者 :line_num>x 命令行模式,批量缩进,缩进x行 复制粘贴 复制剪贴内容存于寄存器, :reg可以查看寄存器内容 指令 动作 yy / 8yy 复制/8行 dd / 8dd 剪贴/8行 p/P 粘贴到光标前/后 u 撤销 ctrl+r 重做 yw 复制当前光标单词 y8w / y8W 复制含当前光标的正向8个单词,大写W的含义和W移动含义相同(忽略一些符号) x/4x/X 剪贴单个字符/4个字符,大写就是反向 d{w/h/j/k/l} 可以各种组合 d$/D 删除光标到行尾 d^ 删除光标到行首 插入、查找、替换 插入 指令 动作 I/A i/a 行首、行尾插入,向前向后插入 o/O 下一行、上一行插入 num + (i/I/A/a/o/O) 多次插入,即重复插入多个字符或者多行 转换 指令 动作 ~ 单个字符大小写转换 g~w 单词大小写反转 g~$ 或 g~~ 整行大小写反转 gU或gu + 回车 整行转大写或小写 gU/uw 单词转大写或小写,转换光标到词尾的部分 ’....

June 20, 2019 · 4 min · 704 words · erpan

Linux虚拟内存与物理内存

概念 在linux中,内存分为物理内存和虚拟内存,即ps命令中的RSS和VSZ。虚拟内存存在的原因是为了解决在物理内存上直接划分内存存在的一些问题,比如: 频繁申请和回收内存导致的空间碎片化 可以随意访问用于其他用途的内存区域,不安全 难以执行多任务 进程能看见的是虚拟地址空间,且地址段时连续的,而系统上搭载的内存的实际的地址是物理地址。通过命令 readelf和cat /proc/<pid>/maps输出的就是进程的虚拟地址。 虚拟内存以页为单位进行划分的,在x86_64的架构中页大小默认是4KB,getconf PAGESIZE可以查看页大小。通过内核管理的页表可以完成从虚拟地址到物理地址的转换,每个进程拥有独立的虚拟地址空间,进程的虚拟内存是连续的,但是在物理内存中不一定是连续,且进程只能访问自己的虚拟内存段,没法访问到其他进程的虚拟内存空间和物理内存。 利用虚拟内存机制的重要功能: 文件映射 请求分页 写时复制 swap 多级页表 标准大页 请求分页 如果内核直接从物理内存中获取需要的区域,然后设置页表并关联虚拟地址空间与物理地址空间,这样会导致内存的浪费,有一部分内存获取后可能进程到运行结束都不会使用,如: 用于大规模程序中的、程序运行时未使用的功能代码段和数据段 由glibc保留的内存池中未被用户利用的部分 所以利用请求分页来解决这个问题。对于虚拟地址空间内的各个页面,只有在进程初次访问页面时才会为这个页面分配物理内存。 过程: ① 进程访问入口点 ② CPU 参照页表,筛选出入口点所属的页面中哪些虚拟地址未关联物理地址 ③ 在CPU中引发缺页中断 ④ 内核中的缺页中断机构为页面分配物理内存并更新页表 ⑤ 回到用户模式继续运行进程 测试 现用以下测试代码观察虚拟内存和物理内存的分配关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <unistd....

August 12, 2018 · 2 min · 370 words · erpan

限制svn上传文件大小

为了方便管理和避免一些问题,svn上传文件需要限制文件大小 svn有几种钩子,资料一搜一大把,限制上传文件大小需要用到 pre-commit 在仓库hooks目录下有示例配置文件,新建一个名为 pre-commit 的脚本,内容如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/bin/bash REPOS="$1" TXN="$2" SVNLOOK=/usr/bin/svnlook MAX_SIZE=512000 files=$($SVNLOOK changed -t $TXN $REPOS | awk '{print $2}') # check check if [[ $files =~ "project_nuli" ]];then for f in $files do # check file size filesize=$($SVNLOOK cat -t $TXN $REPOS $f | wc -c) if [ $filesize -gt $MAX_SIZE ] ; then echo "File $f is too large (must <= $MAX_SIZE)" >> /dev/stderr exit 1 fi done fi exit 0 客户端提交大于500K文件会返回 File $f is too large (must <= $MAX_SIZE)...

December 13, 2017 · 1 min · 178 words · erpan