├── README.md ├── code.txt ├── img ├── 01cee96e3952501d.jpeg ├── 0bd4d451a44cf2c9.jpeg ├── 14356223_weixin.jpeg ├── 2b7ece6a68414958.jpeg ├── 3b3bd23d3712575a3164189ddea27f4cc99.jpeg ├── 3b3bd23d3712575a3164189ddea27f4cc99.jpg ├── 3fd1dd8ab214413e.jpeg ├── 41a1eba8a26bcfe8.jpeg ├── 4557ecb3b2c33c06.jpeg ├── 7460961a4ad0d6ac.jpeg ├── 7644ae4924763d2c.jpeg ├── 7f5e6afd9f878904.jpeg ├── 91829576d53d9be3.jpeg ├── 95ddda34d5a9c517.jpeg ├── 96468d6b103c7154.jpeg ├── 9A1AA1E5ED9CE5F11CA0781DEC2D5AEC.jpeg ├── a4390cc43dfea7e10ab21a302938810617f.jpeg ├── a4390cc43dfea7e10ab21a302938810617f.jpg ├── a5a82174c29d6fc7.jpeg ├── a78a74bbfb6ba1e7.jpeg ├── ac35111fff96352e.jpeg ├── bd9dfb9a9e0078a9.jpeg ├── e17fb44cf6f5adc4.jpeg ├── e5f56c7dae8196cf.jpeg ├── f54e1d56dbf27a1e.jpeg ├── fc9914ad170360c4.jpeg └── 单机模块架构.png ├── source insight configure ├── GLOBAL.CF3 ├── README.md └── 使用说明.txt ├── 其他 ├── README.md └── linux内核协议栈TCP time_wait原理、优化、副作用.docx ├── 第一阶段-手把手教你做分布式缓存二次开发、性能优化 ├── README.md ├── block_noblock_demo │ ├── .gitignore │ ├── asyn_network.md │ ├── block_client.c │ ├── block_server.c │ ├── epoll 阻塞 非阻塞 同步 异步.docx │ ├── noblock_client.c │ └── noblock_server.c ├── redis源码分模块分析 │ ├── 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug │ │ ├── READE.md │ │ ├── backtrace │ │ ├── backtrace.c │ │ └── trace.log │ ├── 基于redis日志代码,快速实现日志同步写和异步写,体验同步写和异步写区别 │ │ ├── .gitignore │ │ └── main.c │ ├── 基于redis的bio代码,快速实现一个线程池demo │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── adlist.c │ │ ├── adlist.h │ │ ├── bio.c │ │ ├── bio.h │ │ ├── threadPoll_Main.c │ │ ├── zmalloc.c │ │ └── zmalloc.h │ └── 基于redis配置文件解析程序,快速实现一个配置文件解析程序demo │ │ ├── Makefile │ │ ├── config │ │ ├── config.c │ │ ├── config.h │ │ ├── config.o │ │ ├── main.c │ │ ├── main.o │ │ ├── sds.c │ │ ├── sds.h │ │ ├── sds.o │ │ ├── test.conf │ │ ├── zmalloc.c │ │ ├── zmalloc.h │ │ └── zmalloc.o ├── 异步网络框架零基础学习+客户端结构组织+网络协议解析等 │ ├── READE.md │ ├── asyn_network+clientManager+protocolParse │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── adlist.c │ │ ├── adlist.h │ │ ├── ae.c │ │ ├── ae.h │ │ ├── ae_epoll.c │ │ ├── anet.c │ │ ├── anet.h │ │ ├── bio.c │ │ ├── bio.h │ │ ├── main.c │ │ ├── middleware_server.c │ │ ├── middleware_server.h │ │ ├── networking.c │ │ ├── zmalloc.c │ │ └── zmalloc.h │ └── asyn_network.md └── 异步网络框架零基础学习 │ ├── READE.md │ ├── asyn_network.md │ └── asyn_network │ ├── .gitignore │ ├── Makefile │ ├── ae.c │ ├── ae.h │ ├── ae_epoll.c │ ├── anet.c │ ├── anet.h │ ├── main.c │ ├── zmalloc.c │ └── zmalloc.h ├── 第三阶段-手把手教你做wiredtiger、rocksdb存储引擎开发,大容量nosql存储系统二次开发 └── README.md ├── 第二阶段-手把手教你做高性能代理中间件开发 ├── README.md └── nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用 │ ├── img │ ├── 01cee96e3952501d.jpeg │ ├── 0bd4d451a44cf2c9.jpeg │ ├── 2b7ece6a68414958.jpeg │ ├── 3b3bd23d3712575a3164189ddea27f4cc99.jpg │ ├── 3fd1dd8ab214413e.jpeg │ ├── 41a1eba8a26bcfe8.jpeg │ ├── 4557ecb3b2c33c06.jpeg │ ├── 7460961a4ad0d6ac.jpeg │ ├── 7644ae4924763d2c.jpeg │ ├── 7f5e6afd9f878904.jpeg │ ├── 91829576d53d9be3.jpeg │ ├── 95ddda34d5a9c517.jpeg │ ├── 96468d6b103c7154.jpeg │ ├── a4390cc43dfea7e10ab21a302938810617f.jpg │ ├── a5a82174c29d6fc7.jpeg │ ├── a78a74bbfb6ba1e7.jpeg │ ├── ac35111fff96352e.jpeg │ ├── bd9dfb9a9e0078a9.jpeg │ ├── e17fb44cf6f5adc4.jpeg │ ├── e5f56c7dae8196cf.jpeg │ ├── f54e1d56dbf27a1e.jpeg │ └── fc9914ad170360c4.jpeg │ └── nginx_twemproxy.md └── 第四阶段-mongodb数据库 └── development_mongodb.md /README.md: -------------------------------------------------------------------------------- 1 | # middleware_development_learning 2 | rocksdb理解好文章: 一文科普 RocksDB 工作原理 https://zhuanlan.zhihu.com/p/632841342 3 | polardb村算分离好文章: 从架构层面分析PolarDB技术要点 https://www.modb.pro/db/1711317557817974784 4 | 中间件、高性能服务器、分布式存储等(redis、memcache、pika、rocksdb、mongodb、wiredtiger、高性能代理中间件)二次开发、性能优化,逐步整理文档说明并配合demo指导 5 | 6 | 近期抽业余时间来总结多年工作中学习到的各种中间件、分布式存储、高性能服务端等技术,通过文章博客和程序demo等方式呈现给大家,暂定分享思路如下: 7 | 8 | ### 对外演讲 9 | |#|对外演讲内容| 10 | |:-|:-| 11 | |1|[Qcon全球软件开发大会分享:OPPO万亿级文档数据库MongoDB集群性能优化实践](https://qcon.infoq.cn/2020/shenzhen/track/916)| 12 | |2|[2019年mongodb年终盛会:OPPO百万级高并发MongoDB集群性能数十倍提升优化实践](https://www.shangyexinzhi.com/article/428874.html)| 13 | |3|[2020年mongodb年终盛会:万亿级文档数据库集群性能优化实践](https://mongoing.com/archives/76151)| 14 | |4|[2021年dbaplus分享:万亿级文档数据库集群性能优化实践](http://dbaplus.cn/news-162-3666-1.html)| 15 | |5|[2021年度Gdevops全球敏捷运维峰会:PB级万亿数据库性能优化及最佳实践](https://gdevops.com/index.php?m=content&c=index&a=lists&catid=87)| 16 | 17 | ### 专栏 18 | |#|专栏名内容| 19 | |:-|:-| 20 | |1|[infoq专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://www.infoq.cn/profile/8D2D4D588D3D8A/publish)| 21 | |2|[oschina专栏:《mongodb内核源码中文注释详细分析及性能优化实践系列》](https://my.oschina.net/u/4087916)| 22 | |3|[知乎专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://www.zhihu.com/people/yang-ya-zhou-42/columns)| 23 | |4|[itpub专栏:《mongodb内核源码设计实现、性能优化、最佳运维实践》](http://blog.itpub.net/column/150)| 24 | 25 | 26 | 27 | 分阶段分享 28 | =================================== 29 | |#|阶段|内容|说明| 30 | |:-|:-|:-|:-| 31 | |1|[第一阶段|分布式缓存源码学习、二次开发、性能及稳定性优化|主要涉及网络实现、memcache redis重要模块源码分析、memcache redis性能稳定性优化及二次开发等| 32 | |2|[第二阶段|高性能代理中间件开发(nginx、wemproxy、dbproxy、mongos等源码进行二次开发)|主要涉及代理中间件源码分析、性能优化、二次开发等| 33 | |3|[第三阶段|分布式大容量nosql存储系统二次开发(突破缓存内存容量限制)|主要涉及pika、tendis源码、rocksdb存储引擎源码分析及pika性能优化等| 34 | |4|[第四阶段|mongodb数据库内核开发|主要涉及mongodb源码、mongos源码、rocksdb存储引擎源码、wiredtiger存储引擎源码分析及二次开发| 35 | 36 | 37 | 38 | ## 第一阶段:分布式缓存开发、性能稳定性优化: 39 | |#|内容| 40 | |:-|:-| 41 | |1|[memcached源码详细分析注释,带详尽中文注释及函数调用关系](https://github.com/y123456yz/Reading-and-comprehense-redis-cluster)| 42 | |2|[借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network)| 43 | |3|[借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo-文档说明](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network.md)| 44 | |4|[阻塞、非阻塞程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo)| 45 | |5|[阻塞、非阻塞、同步、异步、epoll说明](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network.md)| 46 | |6|[借助redis的配置解析模块,快速实现一个配置文件解析程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/redis%E6%BA%90%E7%A0%81%E5%88%86%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90/%E5%9F%BA%E4%BA%8Eredis%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%A3%E6%9E%90%E7%A8%8B%E5%BA%8F%EF%BC%8C%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%A3%E6%9E%90%E7%A8%8B%E5%BA%8Fdemo)| 47 | |7|[借助redis的日志模块,快速实现一个同步日志写、异步日志写程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/redis%E6%BA%90%E7%A0%81%E5%88%86%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90/%E5%9F%BA%E4%BA%8Eredis%E6%97%A5%E5%BF%97%E4%BB%A3%E7%A0%81%EF%BC%8C%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0%E6%97%A5%E5%BF%97%E5%90%8C%E6%AD%A5%E5%86%99%E5%92%8C%E5%BC%82%E6%AD%A5%E5%86%99%EF%BC%8C%E4%BD%93%E9%AA%8C%E5%90%8C%E6%AD%A5%E5%86%99%E5%92%8C%E5%BC%82%E6%AD%A5%E5%86%99%E5%8C%BA%E5%88%AB)| 48 | |8|[借助redis的bio模块,快速实现线程池组demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/redis%E6%BA%90%E7%A0%81%E5%88%86%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90/%E5%9F%BA%E4%BA%8Eredis%E7%9A%84bio%E4%BB%A3%E7%A0%81%EF%BC%8C%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%BA%BF%E7%A8%8B%E6%B1%A0demo)| 49 | |9|[常用高并发网络线程模型设计(最全高并发网络IO线程模型设计及优化)](https://my.oschina.net/u/4087916/blog/4431422) | 50 | 51 | 52 | ## 第二阶段:高性能代理中间件开发 53 | |#|内容| 54 | |:-|:-| 55 | |1|[redis、memcached缓存代理twemproxy源码详细分析注释,带详尽中文注释及函数调用关系](https://github.com/y123456yz/Reading-and-comprehense-twemproxy0.4.1)| 56 | |2|[nginx-1.9.2源码通读分析注释,带详尽函数中文分析注释](https://github.com/y123456yz/reading-code-of-nginx-1.9.2)| 57 | |3|[nginx多进程、高性能、低时延、高可靠机制应用于缓存中间件twemproxy,对twemproxy进行多进程优化改造,提升TPS,降低时延,代理中间件长连接百万TPS/短连接五十万TPS实现原理](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E9%AB%98%E6%80%A7%E8%83%BD%E4%BB%A3%E7%90%86%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%BC%80%E5%8F%91/nginx%E5%A4%9A%E8%BF%9B%E7%A8%8B%E9%AB%98%E5%B9%B6%E5%8F%91%E4%BD%8E%E6%97%B6%E5%BB%B6%E6%9C%BA%E5%88%B6%E5%9C%A8%E7%BC%93%E5%AD%98%E4%BB%A3%E7%90%86%E4%B8%AD%E9%97%B4%E4%BB%B6twemproxy%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/nginx_twemproxy.md)| 58 | |4|[常用高并发网络线程模型设计](https://my.oschina.net/u/4087916/blog/4431422)| 59 | 60 | 61 | 62 | ## 第三阶段:wiredtiger、rocksdb存储引擎开发,大容量nosql存储系统二次开发 63 | |#|内容| 64 | |:-|:-| 65 | |1|[文档数据库mongodb kv存储引擎wiredtiger源码详细分析注释](https://github.com/y123456yz/reading-and-annotate-wiredtiger-3.0.0)| 66 | |2|[rocksdb-6.1.2 KV存储引擎源码中文注释分析](https://github.com/y123456yz/reading-and-annotate-rocksdb-6.1.2)| 67 | |3|[百万级高并发mongodb集群性能数十倍提升优化实践(上篇)](https://my.oschina.net/u/4087916/blog/3141909)| 68 | 69 | 70 | ## 第四阶段:mongodb数据库源码学习,二次开发等 71 | ###《mongodb内核源码设计与实现》源码模块化分析 72 | #### 第一阶段:单机内核源码分析 73 | ![mongodb单机模块化架构图](/img/单机模块架构.png) 74 | |#|单机模块名|核心代码中文注释|说明|模块文档输出| 75 | |:-|:-|:-|:-|:-| 76 | |1|[网络收发处理(含工作线程模型)](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6/blob/master/mongo/README.md#L8)|网络处理模块核心代码实现(100%注释分析)|完成ASIO库、网络数据收发、同步线程模型、动态线程池模型等功能|[详见infoq专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://www.infoq.cn/profile/8D2D4D588D3D8A/publish)| 77 | |2|[command命令处理模块](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6/blob/master/mongo/README.md#L85)|命令处理相关模块源码分析(100%注释分析)|完成命令注册、命令执行、命令分析、命令统计等功能|[详见oschina专栏:《mongodb内核源码中文注释详细分析及性能优化实践系列》](https://www.infoq.cn/profile/8D2D4D588D3D8A/publish)| 78 | |3|[write写(增删改操作)模块](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6/blob/master/mongo/README.md#L115))|增删改写模块(100%注释分析)|完成增删改对应命令解析回调处理、事务封装、storage存储模块对接等功能|[详见知乎专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://www.zhihu.com/people/yang-ya-zhou-42/columns)| 79 | |4|[query查询引擎模块](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6/blob/master/mongo/README.md#L131))|query查询引擎模块(核心代码注释)|完成expression tree解析优化处理、querySolution生成、最优索引选择等功能|[详见知乎专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://www.zhihu.com/people/yang-ya-zhou-42/columns)| 80 | |5|[concurrency并发控制模块](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6/tree/master/mongo/src/mongo/db/concurrency)|并发控制模块(核心代码注释)|完成信号量、读写锁、读写意向锁相关实现及封装|[详见infoq专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://www.infoq.cn/profile/8D2D4D588D3D8A/publish)| 81 | |6|[index索引模块](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6/blob/master/mongo/README.md#L240)|index索引模块(100%注释分析)|完成索引解析、索引管理、索引创建、文件排序等功能|[详见oschina专栏:《mongodb内核源码中文注释详细分析及性能优化实践系列》](https://www.infoq.cn/profile/8D2D4D588D3D8A/publish)| 82 | |7|[storage存储模块](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6/blob/master/mongo/README.md#L115))|storage存储模块(100%注释分析)|完成存储引擎注册、引擎选择、中间层实现、KV实现、wiredtiger接口实现等功能|[详见知乎专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://www.zhihu.com/people/yang-ya-zhou-42/columns)| 83 | |8|[wiredtiger存储引擎](https://github.com/y123456yz/reading-and-annotate-wiredtiger-3.0.0)) |wiredtiger存储引擎设计与实现专栏分析(已分析部分)|完成expression tree解析优化处理、querySolution生成、最优索引选择等功能|[详见知乎专栏:《MongoDB内核源码设计、性能优化、最佳运维实践》](https://github.com/y123456yz/reading-and-annotate-wiredtiger-3.0.0)| 84 | 85 | #### 第二阶段:复制集内核源码分析(已分析部分源码,待整理) 86 | 87 | 88 | #### 第三阶段:sharding分片内核源码分析(已分析部分源码,待整理) 89 | 90 | 91 | ### <<千万级峰值tps/十万亿级数据量文档数据库内核研发及运维之路>> 92 | |#|文章内容| 93 | |:-|:-| 94 | |1|[盘点 2020 - 我要为分布式数据库 mongodb 在国内影响力提升及推广做点事](https://xie.infoq.cn/article/372320c6bb93ddc5b7ecd0b6b)| 95 | |2|[万亿级数据库 MongoDB 集群性能数十倍提升及机房多活容灾实践](https://xie.infoq.cn/article/304a748ad3dead035a449bd51)| 96 | |3|[Qcon现代数据架构 -《万亿级数据库 MongoDB 集群性能数十倍提升优化实践》核心 17 问详细解答](https://xie.infoq.cn/article/0c51f3951f3f10671d7d7123e)| 97 | |4|[话题讨论 - mongodb 相比 mysql 拥有十大核心优势,为何国内知名度不高?](https://xie.infoq.cn/article/180d98535bfa0c3e71aff1662)| 98 | |5|[万亿级数据库 MongoDB 集群性能数十倍提升及机房多活容灾实践](https://xie.infoq.cn/article/304a748ad3dead035a449bd51)| 99 | |6|[百万级高并发mongodb集群性能数十倍提升优化实践(上篇)](https://my.oschina.net/u/4087916/blog/3141909)| 100 | |7|[Mongodb网络传输处理源码实现及性能调优-体验内核性能极致设计](https://my.oschina.net/u/4087916/blog/4295038)| 101 | |8|[常用高并发网络线程模型设计及mongodb线程模型优化实践(最全高并发网络IO线程模型设计及优化)](https://my.oschina.net/u/4087916/blog/4431422) | 102 | |9|[Mongodb集群搭建一篇就够了-复制集模式、分片模式、带认证、不带认证等(带详细步骤说明)](https://my.oschina.net/u/4087916/blog/4661542)| 103 | |10|[Mongodb特定场景性能数十倍提升优化实践(记一次mongodb核心集群雪崩故障)](https://blog.51cto.com/14951246)| 104 | |11|[mongodb内核源码设计实现、性能优化、最佳运维系列-mongodb网络传输层模块源码实现二](https://zhuanlan.zhihu.com/p/265701877)| 105 | |12|[为何需要对开源mongodb社区版本做二次开发,需要做哪些必备二次开发](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6.1/blob/master/development_mongodb.md)| 106 | |13|[对开源mongodb社区版本做二次开发收益列表](https://my.oschina.net/u/4087916/blog/3063529)| 107 | |14|[盘点 2020 - 我要为分布式数据库 mongodb 在国内影响力提升及推广做点事](https://xie.infoq.cn/article/372320c6bb93ddc5b7ecd0b6b)| 108 | |15|[mongodb内核源码实现、性能调优、最佳运维实践系列-数百万行mongodb内核源码阅读经验分享](https://my.oschina.net/u/4087916/blog/4696104)| 109 | |16|[mongodb内核源码实现、性能调优、最佳运维实践系列-mongodb网络传输层模块源码实现一](https://my.oschina.net/u/4087916/blog/4295038)| 110 | |17|[mongodb内核源码实现、性能调优、最佳运维实践系列-mongodb网络传输层模块源码实现二](https://my.oschina.net/u/4087916/blog/4674521)| 111 | |18|[mongodb内核源码实现、性能调优、最佳运维实践系列-mongodb网络传输层模块源码实现三](https://my.oschina.net/u/4087916/blog/4678616)| 112 | |19|[mongodb内核源码实现、性能调优、最佳运维实践系列-mongodb网络传输层模块源码实现四](https://my.oschina.net/u/4087916/blog/4685419)| 113 | |20|[mongodb内核源码实现、性能调优、最佳运维实践系列-command命令处理模块源码实现一](https://my.oschina.net/u/4087916/blog/4709503)| 114 | |21|[mongodb内核源码实现、性能调优、最佳运维实践系列-command命令处理模块源码实现二](https://my.oschina.net/u/4087916/blog/4748286)| 115 | |22|[mongodb内核源码实现、性能调优、最佳运维实践系列-command命令处理模块源码实现三](https://my.oschina.net/u/4087916/blog/4782741)| 116 | |23|[mongodb内核源码实现、性能调优、最佳运维实践系列-记mongodb详细表级操作及详细时延统计实现原理(教你如何快速进行表级时延问题分析)](https://xie.infoq.cn/article/3184cdc42c26c86e2749c3e5c)| 117 | |24|[mongodb内核源码实现、性能调优、最佳运维实践系列-Mongodb write写(增、删、改)模块设计与实现](https://my.oschina.net/u/4087916/blog/4974132)| 118 | 119 | 120 | ## 其他分享 121 | |#|内容| 122 | |:-|:-| 123 | |1|[阿里巴巴分布式消息队列中间件rocketmq-3.4.6源码分析](https://github.com/y123456yz/reading-and-annotate-rocketmq-3.4.6)| 124 | |2|[服务器时延统计工具tcprstat,增加时延阈值统计,记录超过阈值的包个数,并把数据包时间戳记录到日志文件,这样可以根据时间戳快速定位到抓包文件中对应的包,从而可以快速定位到大时延包,避免了人肉搜索抓包文件,提高问题排查效率](https://github.com/y123456yz/tcprstat)| 125 | |3|[linux内核网络协议栈源码阅读分析注释](https://github.com/y123456yz/Reading-and-comprehense-linux-Kernel-network-protocol-stack)| 126 | |4|[docker-17.05.0源码中文注释详细分析](https://github.com/y123456yz/reading-and-annotate-docker-17.05.0)| 127 | |5|[lxc源码详细注释分析](https://github.com/y123456yz/reading-and-annotate-lxc-1.0.9)| 128 | |6|[source insight代码中文注释乱码、背景色等配置调整](https://github.com/y123456yz/middleware_development_learning/tree/master/source%20insight%20configure)| 129 | |7|[linux内核协议栈TCP time_wait原理、优化、副作用](https://my.oschina.net/u/4087916/blog/3051356)| 130 | |8|[为何需要对开源社区版本mongodb做二次开发,需要做哪些二次开发](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5-mongodb%E6%95%B0%E6%8D%AE%E5%BA%93/development_mongodb.md)| 131 | |9|[在线引流工具Tcpcopy原理、环境搭建、使用、采坑](https://my.oschina.net/u/4087916/blog/3064268)| 132 | 133 | 134 | 135 | ## 技术交流群 136 | 对linux c/c++ nginx redis memcache twemproxy mongodb 中间件 存储引擎 分布式 高并发 高性能服务端等技术敢兴趣的同学可以加群: QQ交流群1(针对在校生)-(群号:568892619) QQ交流群2(针对已工作)-(581089275) 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /code.txt: -------------------------------------------------------------------------------- 1 | /* 2 | 25. K 个一组翻转链表 60 https://study-cn.com/problems/reverse-nodes-in-k-group 3 | 3. 无重复字符的最长子串 57 https://study-cn.com/problems/longest-substring-without-repeating-characters 4 | 146. LRU缓存机制 53 https://study-cn.com/problems/lru-cache 5 | 215. 数组中的第K个最大元素 52 https://study-cn.com/problems/kth-largest-element-in-an-array 6 | 206. 反转链表 51 https://study-cn.com/problems/reverse-linked-list 7 | 103. 二叉树的锯齿形层次遍历 47 https://study-cn.com/problems/binary-tree-zigzag-level-order-traversal 8 | 15. 三数之和 42 https://study-cn.com/problems/3sum 9 | 121. 买卖股票的最佳时机 41 https://study-cn.com/problems/best-time-to-buy-and-sell-stock 10 | 160. 相交链表 32 https://study-cn.com/problems/intersection-of-two-linked-lists 11 | 236. 二叉树的最近公共祖先 32 https://study-cn.com/problems/lowest-common-ancestor-of-a-binary-tree 12 | 42. 接雨水 31 https://study-cn.com/problems/trapping-rain-water 13 | 33. 搜索旋转排序数组 27 https://study-cn.com/problems/search-in-rotated-sorted-array 14 | 31. 下一个排列 27 https://study-cn.com/problems/next-permutation 15 | 199. 二叉树的右视图 24 https://study-cn.com/problems/binary-tree-right-side-view 16 | 54. 螺旋矩阵 24 https://study-cn.com/problems/spiral-matrix 17 | 143. 重排链表 24 https://study-cn.com/problems/reorder-list 18 | 23. 合并K个排序链表 23 https://study-cn.com/problems/merge-k-sorted-lists 19 | 21. 合并两个有序链表 22 https://study-cn.com/problems/merge-two-sorted-lists 20 | 300. 最长上升子序列 21 https://study-cn.com/problems/longest-increasing-subsequence 21 | 69. x 的平方根 20 https://study-cn.com/problems/sqrtx 22 | 92. 反转链表 II 20 https://study-cn.com/problems/reverse-linked-list-ii 23 | 105. 从前序与中序遍历序列构造二叉树 19 https://study-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal 24 | 1. 两数之和 19 https://study-cn.com/problems/two-sum 25 | 124. 二叉树中的最大路径和 18 https://study-cn.com/problems/binary-tree-maximum-path-sum 26 | 200. 岛屿数量 18 https://study-cn.com/problems/number-of-islands 27 | 41. 缺失的第一个正数 17 https://study-cn.com/problems/first-missing-positive 28 | 101. 对称二叉树 16 https://study-cn.com/problems/symmetric-tree 29 | 56. 合并区间 16 https://study-cn.com/problems/merge-intervals 30 | 415. 字符串相加 16 https://study-cn.com/problems/add-strings 31 | 155. 最小栈 16 https://study-cn.com/problems/min-stack 32 | 46. 全排列 16 https://study-cn.com/problems/permutations 33 | 76. 最小覆盖子串 16 https://study-cn.com/problems/minimum-window-substring 34 | 补充题1. 排序奇升偶降链表 16 https://mp.weixin.qq.com/s/0WVa2wIAeG0nYnVndZiEXQ 35 | 141. 环形链表 14 https://study-cn.com/problems/linked-list-cycle 36 | 221. 最大正方形 14 https://study-cn.com/problems/maximal-square 37 | 20. 有效的括号 14 https://study-cn.com/problems/valid-parentheses 38 | 98. 验证二叉搜索树 14 https://study-cn.com/problems/validate-binary-search-tree 39 | 39. 组合总和 14 https://study-cn.com/problems/combination-sum 40 | 102. 二叉树的层序遍历 13 https://study-cn.com/problems/binary-tree-level-order-traversal 41 | 53. 最大子序和 13 https://study-cn.com/problems/maximum-subarray 42 | 322. 零钱兑换 13 https://study-cn.com/problems/coin-change 43 | 162. 寻找峰值 13 https://study-cn.com/problems/find-peak-element 44 | 122. 买卖股票的最佳时机 II 13 https://study-cn.com/problems/best-time-to-buy-and-sell-stock-ii 45 | 142. 环形链表 II 13 https://study-cn.com/problems/linked-list-cycle-ii 46 | 48. 旋转图像 13 https://study-cn.com/problems/rotate-image 47 | 470. 用 Rand7() 实现 Rand10() 13 https://study-cn.com/problems/implement-rand10-using-rand7 48 | 补充题2. 圆环回原点问题 13 https://mp.weixin.qq.com/s/NZPaFsFrTybO3K3s7p7EVg 49 | 234. 回文链表 12 https://study-cn.com/problems/palindrome-linked-list 50 | 518. 零钱兑换 II 12 https://study-cn.com/problems/coin-change-2 51 | 88. 合并两个有序数组 12 https://study-cn.com/problems/merge-sorted-array 52 | 2. 两数相加 12 https://study-cn.com/problems/add-two-numbers 53 | 32. 最长有效括号 12 https://study-cn.com/problems/longest-valid-parentheses 54 | 958. 二叉树的完全性检验 12 https://study-cn.com/problems/check-completeness-of-a-binary-tree 55 | 148. 排序链表 12 https://study-cn.com/problems/sort-list 56 | 198. 打家劫舍 12 https://study-cn.com/problems/house-robber 57 | 232. 用栈实现队列 12 https://study-cn.com/problems/implement-queue-using-stacks 58 | 补充题4. 手撕快速排序 12 https://study-cn.com/problems/sort-an-array 59 | 113. 路径总和 II 11 https://study-cn.com/problems/path-sum-ii 60 | 5. 最长回文子串 11 https://study-cn.com/problems/longest-palindromic-substring 61 | 543. 二叉树的直径 10 https://study-cn.com/problems/diameter-of-binary-tree 62 | 79. 单词搜索 10 https://study-cn.com/problems/word-search 63 | 82. 删除排序链表中的重复元素 II 10 https://study-cn.com/problems/remove-duplicates-from-sorted-list-ii 64 | 83. 删除排序链表中的重复元素 9 https://study-cn.com/problems/remove-duplicates-from-sorted-list 65 | 128. 最长连续序列 9 https://study-cn.com/problems/longest-consecutive-sequence 66 | 22. 括号生成 9 https://study-cn.com/problems/generate-parentheses 67 | 94. 二叉树的中序遍历 9 https://study-cn.com/problems/binary-tree-inorder-traversal 68 | 739. 每日温度 9 https://study-cn.com/problems/daily-temperatures 69 | 78. 子集 9 https://study-cn.com/problems/subsets 70 | 补充题9. 36进制加法 9 https://mp.weixin.qq.com/s/XcKQwnwCh5nZsz-DLHJwzQ 71 | 剑指 Offer 54. 二叉搜索树的第k大节点 8 https://study-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof 72 | 8. 字符串转换整数 (atoi) 8 https://study-cn.com/problems/string-to-integer-atoi 73 | 24. 两两交换链表中的节点 8 https://study-cn.com/problems/swap-nodes-in-pairs 74 | 114. 二叉树展开为链表 8 https://study-cn.com/problems/flatten-binary-tree-to-linked-list 75 | 剑指 Offer 22. 链表中倒数第k个节点 8 https://study-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof 76 | 93. 复原IP地址 8 https://study-cn.com/problems/restore-ip-addresses 77 | 440. 字典序的第K小数字 8 https://study-cn.com/problems/k-th-smallest-in-lexicographical-order 78 | 70. 爬楼梯 7 https://study-cn.com/problems/climbing-stairs 79 | 112. 路径总和 7 https://study-cn.com/problems/path-sum 80 | 695. 岛屿的最大面积 7 https://study-cn.com/problems/max-area-of-island 81 | 138. 复制带随机指针的链表 7 https://study-cn.com/problems/copy-list-with-random-pointer 82 | 19. 删除链表的倒数第N个节点 7 https://study-cn.com/problems/remove-nth-node-from-end-of-list 83 | 129. 求根到叶子节点数字之和 7 https://study-cn.com/problems/sum-root-to-leaf-numbers 84 | 662. 二叉树最大宽度 7 https://study-cn.com/problems/maximum-width-of-binary-tree 85 | 240. 搜索二维矩阵 II 7 https://study-cn.com/problems/search-a-2d-matrix-ii 86 | 556. 下一个更大元素 III 7 https://study-cn.com/problems/next-greater-element-iii 87 | 230. 二叉搜索树中第K小的元素 6 https://study-cn.com/problems/kth-smallest-element-in-a-bst 88 | 110. 平衡二叉树 6 https://study-cn.com/problems/balanced-binary-tree 89 | 328. 奇偶链表 6 https://study-cn.com/problems/odd-even-linked-list 90 | 460. LFU缓存 6 https://study-cn.com/problems/lfu-cache 91 | 64. 最小路径和 6 https://study-cn.com/problems/minimum-path-sum 92 | 61. 旋转链表 6 https://study-cn.com/problems/rotate-list 93 | 188. 买卖股票的最佳时机 IV 6 https://study-cn.com/problems/best-time-to-buy-and-sell-stock-iv 94 | 224. 基本计算器 6 https://study-cn.com/problems/basic-calculator 95 | 剑指 Offer 36. 二叉搜索树与双向链表 6 https://study-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof 96 | 226. 翻转二叉树 6 https://study-cn.com/problems/invert-binary-tree 97 | 209. 长度最小的子数组 6 https://study-cn.com/problems/minimum-size-subarray-sum 98 | 862. 和至少为 K 的最短子数组 6 https://study-cn.com/problems/shortest-subarray-with-sum-at-least-k 99 | 11. 盛最多水的容器 6 https://study-cn.com/problems/container-with-most-water 100 | 补充题7. 木头切割问题 6 https://mp.weixin.qq.com/s/FQma0bdAWbzLMmCKhZRk7w 101 | 补充题23. 检测循环依赖 6 https://mp.weixin.qq.com/s/pCRscwKqQdYYN7M1Sia7xA 102 | 108. 将有序数组转换为二叉搜索树 5 https://study-cn.com/problems/convert-sorted-array-to-binary-search-tree 103 | 1143. 最长公共子序列 5 https://study-cn.com/problems/longest-common-subsequence 104 | 297. 二叉树的序列化与反序列化 5 https://study-cn.com/problems/serialize-and-deserialize-binary-tree 105 | 560. 和为K的子数组 5 https://study-cn.com/problems/subarray-sum-equals-k 106 | 704. 二分查找 5 https://study-cn.com/problems/binary-search 107 | 670. 最大交换 5 https://study-cn.com/problems/maximum-swap 108 | 421. 数组中两个数的最大异或值 5 https://study-cn.com/problems/maximum-xor-of-two-numbers-in-an-array 109 | 104. 二叉树的最大深度 5 https://study-cn.com/problems/maximum-depth-of-binary-tree 110 | 135. 分发糖果 5 https://study-cn.com/problems/candy 111 | 151. 翻转字符串里的单词 5 https://study-cn.com/problems/reverse-words-in-a-string 112 | 287. 寻找重复数 5 https://study-cn.com/problems/find-the-duplicate-number 113 | 528. 按权重随机选择 5 https://study-cn.com/problems/random-pick-with-weight 114 | 91. 解码方法 5 https://study-cn.com/problems/decode-ways 115 | 59. 螺旋矩阵 II 5 https://study-cn.com/problems/spiral-matrix-ii 116 | 718. 最长重复子数组 5 https://study-cn.com/problems/maximum-length-of-repeated-subarray 117 | 139. 单词拆分 4 https://study-cn.com/problems/word-break 118 | 剑指 Offer 11. 旋转数组的最小数字 4 https://study-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof 119 | 62. 不同路径 4 https://study-cn.com/problems/unique-paths 120 | 剑指 Offer 51. 数组中的逆序对 4 https://study-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof 121 | 40. 组合总和 II 4 https://study-cn.com/problems/combination-sum-ii 122 | 1047. 删除字符串中的所有相邻重复项 4 https://study-cn.com/problems/remove-all-adjacent-duplicates-in-string 123 | 402. 移掉K位数字 4 https://study-cn.com/problems/remove-k-digits 124 | 169. 多数元素 4 https://study-cn.com/problems/majority-element 125 | 152. 乘积最大子数组 4 https://study-cn.com/problems/maximum-product-subarray 126 | 50. Pow(x, n) 4 https://study-cn.com/problems/powx-n 127 | 4. 寻找两个正序数组的中位数 4 https://study-cn.com/problems/median-of-two-sorted-arrays 128 | 456. 132模式 4 https://study-cn.com/problems/132-pattern 129 | 239. 滑动窗口最大值 4 https://study-cn.com/problems/sliding-window-maximum 130 | 722. 删除注释 4 https://study-cn.com/problems/remove-comments 131 | 1095. 山脉数组中查找目标值 4 https://study-cn.com/problems/find-in-mountain-array 132 | 72. 编辑距离 4 https://study-cn.com/problems/edit-distance 133 | 153. 寻找旋转排序数组中的最小值 4 https://study-cn.com/problems/find-minimum-in-rotated-sorted-array 134 | 补充题3. 求区间最小数乘区间和的最大值 4 https://mp.weixin.qq.com/s/UFv7pt_djjZoK_gzUBrRXA 135 | 补充题6. 手撕堆排序 4 https://study-cn.com/problems/sort-an-array 136 | 227. 基本计算器 II 4 https://study-cn.com/problems/basic-calculator-ii 137 | 763. 划分字母区间 4 https://study-cn.com/problems/partition-labels 138 | 剑指 Offer 53 - I. 在排序数组中查找数字 I 4 https://study-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof 139 | 剑指 Offer 61. 扑克牌中的顺子 3 https://study-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof 140 | 977. 有序数组的平方 3 https://study-cn.com/problems/squares-of-a-sorted-array 141 | 剑指 Offer 09. 用两个栈实现队列 3 https://study-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof 142 | 503. 下一个更大元素 II 3 https://study-cn.com/problems/next-greater-element-ii 143 | 283. 移动零 3 https://study-cn.com/problems/move-zeroes 144 | 166. 分数到小数 3 https://study-cn.com/problems/fraction-to-recurring-decimal 145 | 264. 丑数 II 3 https://study-cn.com/problems/ugly-number-ii 146 | 210. 课程表 II 3 https://study-cn.com/problems/course-schedule-ii 147 | 394. 字符串解码 3 https://study-cn.com/problems/decode-string 148 | 145. 二叉树的后序遍历 3 https://study-cn.com/problems/binary-tree-postorder-traversal 149 | 71. 简化路径 3 https://study-cn.com/problems/simplify-path 150 | 134. 加油站 3 https://study-cn.com/problems/gas-station 151 | 340. 至多包含 K 个不同字符的最长子串 3 https://study-cn.com/problems/longest-substring-with-at-most-k-distinct-characters 152 | 86. 分隔链表 3 https://study-cn.com/problems/partition-list 153 | 329. 矩阵中的最长递增路径 3 https://study-cn.com/problems/longest-increasing-path-in-a-matrix 154 | 144. 二叉树的前序遍历 3 https://study-cn.com/problems/binary-tree-preorder-traversal 155 | 10. 正则表达式匹配 3 https://study-cn.com/problems/regular-expression-matching 156 | 剑指 Offer 46. 把数字翻译成字符串 3 https://study-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof 157 | 136. 只出现一次的数字 3 https://study-cn.com/problems/single-number 158 | 18. 四数之和 3 https://study-cn.com/problems/4sum 159 | 剑指 Offer 27. 二叉树的镜像 3 https://study-cn.com/problems/er-cha-shu-de-jing-xiang-lcof 160 | 225. 用队列实现栈 3 https://study-cn.com/problems/implement-stack-using-queues 161 | 647. 回文子串 3 https://study-cn.com/problems/palindromic-substrings 162 | 34. 在排序数组中查找元素的第一个和最后一个位置 3 https://study-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array 163 | 165. 比较版本号 3 https://study-cn.com/problems/compare-version-numbers 164 | 887. 鸡蛋掉落 3 https://study-cn.com/problems/super-egg-drop 165 | 106. 从中序与后序遍历序列构造二叉树 3 https://study-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal 166 | 498. 对角线遍历 3 https://study-cn.com/problems/diagonal-traverse 167 | 354. 俄罗斯套娃信封问题 3 https://study-cn.com/problems/russian-doll-envelopes 168 | 767. 重构字符串 3 https://study-cn.com/problems/reorganize-string 169 | 1254. 统计封闭岛屿的数目 2 https://study-cn.com/problems/number-of-closed-islands 170 | 347. 前 K 个高频元素 2 https://study-cn.com/problems/top-k-frequent-elements 171 | 剑指 Offer 10- II. 青蛙跳台阶问题 2 https://study-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof 172 | 剑指 Offer 45. 把数组排成最小的数 2 https://study-cn.com/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof 173 | 99. 恢复二叉搜索树 2 https://study-cn.com/problems/recover-binary-search-tree 174 | 125. 验证回文串 2 https://study-cn.com/problems/valid-palindrome 175 | 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 2 https://study-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof 176 | 523. 连续的子数组和 2 https://study-cn.com/problems/continuous-subarray-sum 177 | 剑指 Offer 48. 最长不含重复字符的子字符串 2 https://study-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof 178 | 剑指 Offer 04. 二维数组中的查找 2 https://study-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof 179 | 补充题24. 双栈排序 2 https://mp.weixin.qq.com/s/g_AqwsSEUwlRSevnStPkEA 180 | 208. 实现 Trie (前缀树) 2 https://study-cn.com/problems/implement-trie-prefix-tree 181 | 剑指 Offer 19. 正则表达式匹配 2 https://study-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof 182 | 895. 最大频率栈 2 https://study-cn.com/problems/maximum-frequency-stack 183 | 398. 随机数索引 2 https://study-cn.com/problems/random-pick-index 184 | 45. 跳跃游戏 II 2 https://study-cn.com/problems/jump-game-ii 185 | 416. 分割等和子集 2 https://study-cn.com/problems/partition-equal-subset-sum 186 | 668. 乘法表中第k小的数 2 https://study-cn.com/problems/kth-smallest-number-in-multiplication-table 187 | 120. 三角形最小路径和 2 https://study-cn.com/problems/triangle 188 | 123. 买卖股票的最佳时机 III 2 https://study-cn.com/problems/best-time-to-buy-and-sell-stock-iii 189 | 154. 寻找旋转排序数组中的最小值 II 2 https://study-cn.com/problems/find-minimum-in-rotated-sorted-array-ii 190 | 147. 对链表进行插入排序 2 https://study-cn.com/problems/insertion-sort-list 191 | 785. 判断二分图 2 https://study-cn.com/problems/is-graph-bipartite 192 | 468. 验证IP地址 2 https://study-cn.com/problems/validate-ip-address 193 | 295. 数据流的中位数 2 https://study-cn.com/problems/find-median-from-data-stream 194 | 404. 左叶子之和 2 https://study-cn.com/problems/sum-of-left-leaves 195 | 84. 柱状图中最大的矩形 2 https://study-cn.com/problems/largest-rectangle-in-histogram 196 | 43. 字符串相乘 2 https://study-cn.com/problems/multiply-strings 197 | 14. 最长公共前缀 2 https://study-cn.com/problems/longest-common-prefix 198 | 974. 和可被 K 整除的子数组 2 https://study-cn.com/problems/subarray-sums-divisible-by-k 199 | 922. 按奇偶排序数组 II 2 https://study-cn.com/problems/sort-array-by-parity-ii 200 | 75. 颜色分类 2 https://study-cn.com/problems/sort-colors 201 | 191. 位1的个数 2 https://study-cn.com/problems/number-of-1-bits 202 | 60. 第k个排列 2 https://study-cn.com/problems/permutation-sequence 203 | 补充题10. 36进制减法 2 https://mp.weixin.qq.com/s/ub9GpTBjDF55hZld3V2rEA 204 | 补充题5. 手撕归并排序 2 https://study-cn.com/problems/sort-an-array 205 | 189. 旋转数组 2 https://study-cn.com/problems/rotate-array 206 | 74. 搜索二维矩阵 2 https://study-cn.com/problems/search-a-2d-matrix 207 | 补充题14. 阿拉伯数字转中文数字 2 208 | 剑指 Offer 03. 数组中重复的数字 2 https://study-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof 209 | 253. 会议室 II 2 https://study-cn.com/problems/meeting-rooms-ii 210 | 7. 整数反转 2 https://study-cn.com/problems/reverse-integer 211 | 47. 全排列 II 2 https://study-cn.com/problems/permutations-ii 212 | 85. 最大矩形 2 https://study-cn.com/problems/maximal-rectangle 213 | 81. 搜索旋转排序数组 II 2 https://study-cn.com/problems/search-in-rotated-sorted-array-ii 214 | 44. 通配符匹配 2 https://study-cn.com/problems/wildcard-matching 215 | 703. 数据流中的第K大元素 2 https://study-cn.com/problems/kth-largest-element-in-a-stream 216 | 443. 压缩字符串 2 https://study-cn.com/problems/string-compression 217 | 381. O(1) 时间插入、删除和获取随机元素 - 允许重复 2 https://study-cn.com/problems/insert-delete-getrandom-o1-duplicates-allowed 218 | 补充题20. 立方根 2 219 | 395. 至少有K个重复字符的最长子串 2 https://study-cn.com/problems/longest-substring-with-at-least-k-repeating-characters 220 | 剑指 Offer 52. 两个链表的第一个公共节点 1 https://study-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof 221 | 111. 二叉树的最小深度 1 https://study-cn.com/problems/minimum-depth-of-binary-tree 222 | 994. 腐烂的橘子 1 https://study-cn.com/problems/rotting-oranges 223 | 344. 反转字符串 1 https://study-cn.com/problems/reverse-string 224 | 1299. 将每个元素替换为右侧最大元素 1 https://study-cn.com/problems/replace-elements-with-greatest-element-on-right-side 225 | 67. 二进制求和 1 https://study-cn.com/problems/add-binary 226 | 515. 在每个树行中找最大值 1 https://study-cn.com/problems/find-largest-value-in-each-tree-row 227 | 1147. 段式回文 1 https://study-cn.com/problems/longest-chunked-palindrome-decomposition 228 | 876. 链表的中间结点 1 https://study-cn.com/problems/middle-of-the-linked-list 229 | 100. 相同的树 1 https://study-cn.com/problems/same-tree 230 | 842. 将数组拆分成斐波那契序列 1 https://study-cn.com/problems/split-array-into-fibonacci-sequence 231 | 剑指 Offer 33. 二叉搜索树的后序遍历序列 1 https://study-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof 232 | 剑指 Offer 38. 字符串的排列 1 https://study-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof 233 | 剑指 Offer 24. 反转链表 1 https://study-cn.com/problems/fan-zhuan-lian-biao-lcof 234 | 剑指 Offer 53 - II. 0~n-1中缺失的数字 1 https://study-cn.com/problems/que-shi-de-shu-zi-lcof 235 | 701. 二叉搜索树中的插入操作 1 https://study-cn.com/problems/insert-into-a-binary-search-tree 236 | 349. 两个数组的交集 1 https://study-cn.com/problems/intersection-of-two-arrays 237 | 1156. 单字符重复子串的最大长度 1 https://study-cn.com/problems/swap-for-longest-repeated-character-substring 238 | 449. 序列化和反序列化二叉搜索树 1 https://study-cn.com/problems/serialize-and-deserialize-bst 239 | 面试题 08.12. 八皇后 1 https://study-cn.com/problems/eight-queens-lcci 240 | 37. 解数独 1 https://study-cn.com/problems/sudoku-solver 241 | 410. 分割数组的最大值 1 https://study-cn.com/problems/split-array-largest-sum 242 | 694. 不同岛屿的数量 1 https://study-cn.com/problems/number-of-distinct-islands 243 | 剑指 Offer 18. 删除链表的节点 1 https://study-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof 244 | 912. 排序数组 1 https://study-cn.com/problems/sort-an-array 245 | 173. 二叉搜索树迭代器 1 https://study-cn.com/problems/binary-search-tree-iterator 246 | 1139. 最大的以 1 为边界的正方形 1 https://study-cn.com/problems/largest-1-bordered-square 247 | 剑指 Offer 25. 合并两个排序的链表 1 https://study-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof 248 | 325. 和等于 k 的最长子数组长度 1 https://study-cn.com/problems/maximum-size-subarray-sum-equals-k 249 | 1363. 形成三的最大倍数 1 https://study-cn.com/problems/largest-multiple-of-three 250 | 951. 翻转等价二叉树 1 https://study-cn.com/problems/flip-equivalent-binary-trees 251 | 107. 二叉树的层次遍历 II 1 https://study-cn.com/problems/binary-tree-level-order-traversal-ii 252 | 637. 二叉树的层平均值 1 https://study-cn.com/problems/average-of-levels-in-binary-tree 253 | 277. 搜寻名人 1 https://study-cn.com/problems/find-the-celebrity 254 | 321. 拼接最大数 1 https://study-cn.com/problems/create-maximum-number 255 | 525. 连续数组 1 https://study-cn.com/problems/contiguous-array 256 | 剑指 Offer 58 - II. 左旋转字符串 1 https://study-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof 257 | 97. 交错字符串 1 https://study-cn.com/problems/interleaving-string 258 | 204. 计数质数 1 https://study-cn.com/problems/count-primes 259 | 202. 快乐数 1 https://study-cn.com/problems/happy-number 260 | 1669. 合并两个链表 1 https://study-cn.com/problems/merge-in-between-linked-lists 261 | 807. 保持城市天际线 1 https://study-cn.com/problems/max-increase-to-keep-city-skyline 262 | 889. 根据前序和后序遍历构造二叉树 1 https://study-cn.com/problems/construct-binary-tree-from-preorder-and-postorder-traversal 263 | 剑指 Offer 05. 替换空格 1 https://study-cn.com/problems/ti-huan-kong-ge-lcof 264 | 279. 完全平方数 1 https://study-cn.com/problems/perfect-squares 265 | 17. 电话号码的字母组合 1 https://study-cn.com/problems/letter-combinations-of-a-phone-number 266 | 459. 重复的子字符串 1 https://study-cn.com/problems/repeated-substring-pattern 267 | 剑指 Offer 59 - II. 队列的最大值 1 https://study-cn.com/problems/dui-lie-de-zui-da-zhi-lcof 268 | 260. 只出现一次的数字 III 1 https://study-cn.com/problems/single-number-iii 269 | 1438. 绝对差不超过限制的最长连续子数组 1 https://study-cn.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit 270 | 剑指 Offer 62. 圆圈中最后剩下的数字 1 https://study-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof 271 | 836. 矩形重叠 1 https://study-cn.com/problems/rectangle-overlap 272 | 1172. 餐盘栈 1 https://study-cn.com/problems/dinner-plate-stacks 273 | 547. 省份数量(原朋友圈) 1 https://study-cn.com/problems/number-of-provinces 274 | 面试题 17.24. 最大子矩阵 1 https://study-cn.com/problems/max-submatrix-lcci 275 | 1302. 层数最深叶子节点的和 1 https://study-cn.com/problems/deepest-leaves-sum 276 | 448. 找到所有数组中消失的数字 1 https://study-cn.com/problems/find-all-numbers-disappeared-in-an-array 277 | 127. 单词接龙 1 https://study-cn.com/problems/word-ladder 278 | 剑指 Offer 55 - II. 平衡二叉树 1 https://study-cn.com/problems/ping-heng-er-cha-shu-lcof 279 | 剑指 Offer 55 - I. 二叉树的深度 1 https://study-cn.com/problems/er-cha-shu-de-shen-du-lcof 280 | 面试题 08.05. 递归乘法 1 https://study-cn.com/problems/recursive-mulitply-lcci 281 | 179. 最大数 1 https://study-cn.com/problems/largest-number 282 | 1107. 每日新用户统计 1 https://study-cn.com/problems/new-users-daily-count 283 | 剑指 Offer 10- I. 斐波那契数列 1 https://study-cn.com/problems/fei-bo-na-qi-shu-lie-lcof 284 | 63. 不同路径 II 1 https://study-cn.com/problems/unique-paths-ii 285 | 397. 整数替换 1 https://study-cn.com/problems/integer-replacement 286 | 564. 寻找最近的回文数 1 https://study-cn.com/problems/find-the-closest-palindrome 287 | 765. 情侣牵手 1 https://study-cn.com/problems/couples-holding-hands 288 | 80. 删除排序数组中的重复项 II 1 https://study-cn.com/problems/remove-duplicates-from-sorted-array-ii 289 | 77. 组合 1 https://study-cn.com/problems/combinations 290 | 378. 有序矩阵中第K小的元素 1 https://study-cn.com/problems/kth-smallest-element-in-a-sorted-matrix 291 | 384. 打乱数组 1 https://study-cn.com/problems/shuffle-an-array 292 | 剑指 Offer 56 - I. 数组中数字出现的次数 1 https://study-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof 293 | 28. 实现 strStr() 1 https://study-cn.com/problems/implement-strstr 294 | 剑指 Offer 32 - III. 从上到下打印二叉树 III 1 https://study-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof 295 | 207. 课程表 1 https://study-cn.com/problems/course-schedule 296 | 386. 字典序排数 1 https://study-cn.com/problems/lexicographical-numbers 297 | 面试题 03.03. 堆盘子 1 https://study-cn.com/problems/stack-of-plates-lcci 298 | 331. 验证二叉树的前序序列化 1 https://study-cn.com/problems/verify-preorder-serialization-of-a-binary-tree 299 | 剑指 Offer 28. 对称的二叉树 1 https://study-cn.com/problems/dui-cheng-de-er-cha-shu-lcof 300 | 剑指 Offer 68 - II. 二叉树的最近公共祖先 1 https://study-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof 301 | 848. 字母移位 1 https://study-cn.com/problems/shifting-letters 302 | 6. Z 字形变换 1 https://study-cn.com/problems/zigzag-conversion 303 | 990. 等式方程的可满足性 1 https://study-cn.com/problems/satisfiability-of-equality-equations 304 | 567. 字符串的排列 1 https://study-cn.com/problems/permutation-in-string 305 | 496. 下一个更大元素 I 1 https://study-cn.com/problems/next-greater-element-i 306 | 967. 连续差相同的数字 1 https://study-cn.com/problems/numbers-with-same-consecutive-differences 307 | 1405. 最长快乐字符串 1 https://study-cn.com/problems/longest-happy-string 308 | 1353. 最多可以参加的会议数目 1 https://study-cn.com/problems/maximum-number-of-events-that-can-be-attended 309 | 1574. 删除最短的子数组使剩余数组有序 1 https://study-cn.com/problems/shortest-subarray-to-be-removed-to-make-array-sorted 310 | 949. 给定数字能组成的最大时间 1 https://study-cn.com/problems/largest-time-for-given-digits 311 | 剑指 Offer 31. 栈的压入、弹出序列 1 https://study-cn.com/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof 312 | 213. 打家劫舍 II 1 https://study-cn.com/problems/house-robber-ii 313 | 剑指 Offer 40. 最小的k个数 1 https://study-cn.com/problems/zui-xiao-de-kge-shu-lcof 314 | 1475. 商品折扣后的最终价格 1 https://study-cn.com/problems/final-prices-with-a-special-discount-in-a-shop 315 | 剑指 Offer 43. 1~n整数中1出现的次数 1 https://study-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof 316 | 706. 设计哈希映射 1 https://study-cn.com/problems/design-hashmap 317 | 252. 会议室 1 https://study-cn.com/problems/meeting-rooms 318 | 剑指 Offer 63. 股票的最大利润 1 https://study-cn.com/problems/gu-piao-de-zui-da-li-run-lcof 319 | 680. 验证回文字符串 Ⅱ 1 https://study-cn.com/problems/valid-palindrome-ii 320 | 341. 扁平化嵌套列表迭代器 1 https://study-cn.com/problems/flatten-nested-list-iterator 321 | 140. 单词拆分 II 1 https://study-cn.com/problems/word-break-ii 322 | 716. 最大栈 1 https://study-cn.com/problems/max-stack 323 | 214. 最短回文串 1 https://study-cn.com/problems/shortest-palindrome 324 | 633. 平方数之和 1 https://study-cn.com/problems/sum-of-square-numbers 325 | 补充题17. 两个有序数组第k小的数 1 326 | 589. N叉树的前序遍历 1 https://study-cn.com/problems/n-ary-tree-preorder-traversal 327 | 1675. 数组的最小偏移量 1 https://study-cn.com/problems/minimize-deviation-in-array 328 | 485. 最大连续1的个数 1 https://study-cn.com/problems/max-consecutive-ones 329 | 补充题8. 计算数组的小和 1 https://mp.weixin.qq.com/s/rMsbcUf9ZPhvfRoyZGW6HA 330 | 剑指 Offer 34. 二叉树中和为某一值的路径 1 https://study-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof 331 | 剑指 Offer 17. 打印从1到最大的n位数 1 https://study-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof 332 | 861. 翻转矩阵后的得分 1 https://study-cn.com/problems/score-after-flipping-matrix 333 | 168. Excel表列名称 1 https://study-cn.com/problems/excel-sheet-column-title 334 | 剑指 Offer 14- I. 剪绳子 1 https://study-cn.com/problems/jian-sheng-zi-lcof 335 | 96. 不同的二叉搜索树 1 https://study-cn.com/problems/unique-binary-search-trees 336 | 13. 罗马数字转整数 1 https://study-cn.com/problems/roman-to-integer 337 | 1190. 反转每对括号间的子串 1 https://study-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses 338 | 16. 最接近的三数之和 1 https://study-cn.com/problems/3sum-closest 339 | 216. 组合总和 III 1 https://study-cn.com/problems/combination-sum-iii 340 | 剑指 Offer 32 - II. 从上到下打印二叉树 II 1 https://study-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof 341 | 736. Lisp 语法解析 1 https://study-cn.com/problems/parse-lisp-expression 342 | 137. 只出现一次的数字 II 1 https://study-cn.com/problems/single-number-ii 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 146. LRU缓存机制 32 https://study-cn.com/problems/lru-cache 363 | 206. 反转链表 30 https://study-cn.com/problems/reverse-linked-list 364 | 补充题4. 手撕快速排序 24 https://study-cn.com/problems/sort-an-array 365 | 21. 合并两个有序链表 18 https://study-cn.com/problems/merge-two-sorted-lists 366 | 470. 用 Rand7() 实现 Rand10() 15 https://study-cn.com/problems/implement-rand10-using-rand7 367 | 234. 回文链表 14 https://study-cn.com/problems/palindrome-linked-list 368 | 8. 字符串转换整数 (atoi) 13 https://study-cn.com/problems/string-to-integer-atoi 369 | 53. 最大子序和 12 https://study-cn.com/problems/maximum-subarray 370 | 460. LFU缓存 12 https://study-cn.com/problems/lfu-cache 371 | 215. 数组中的第K个最大元素 10 https://study-cn.com/problems/kth-largest-element-in-an-array 372 | 153. 寻找旋转排序数组中的最小值 10 https://study-cn.com/problems/find-minimum-in-rotated-sorted-array 373 | 3. 无重复字符的最长子串 10 https://study-cn.com/problems/longest-substring-without-repeating-characters 374 | 704. 二分查找 10 https://study-cn.com/problems/binary-search 375 | 415. 字符串相加 10 https://study-cn.com/problems/add-strings 376 | 补充题6. 手撕堆排序 9 https://study-cn.com/problems/sort-an-array 377 | 剑指 Offer 54. 二叉搜索树的第k大节点 9 https://study-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof 378 | 160. 相交链表 8 https://study-cn.com/problems/intersection-of-two-linked-lists 379 | 141. 环形链表 8 https://study-cn.com/problems/linked-list-cycle 380 | 151. 翻转字符串里的单词 8 https://study-cn.com/problems/reverse-words-in-a-string 381 | 4. 寻找两个正序数组的中位数 7 https://study-cn.com/problems/median-of-two-sorted-arrays 382 | 42. 接雨水 7 https://study-cn.com/problems/trapping-rain-water 383 | 102. 二叉树的层序遍历 7 https://study-cn.com/problems/binary-tree-level-order-traversal 384 | 144. 二叉树的前序遍历 7 https://study-cn.com/problems/binary-tree-preorder-traversal 385 | 300. 最长上升子序列 6 https://study-cn.com/problems/longest-increasing-subsequence 386 | 110. 平衡二叉树 6 https://study-cn.com/problems/balanced-binary-tree 387 | 2. 两数相加 6 https://study-cn.com/problems/add-two-numbers 388 | 70. 爬楼梯 6 https://study-cn.com/problems/climbing-stairs 389 | 213. 打家劫舍 II 6 https://study-cn.com/problems/house-robber-ii 390 | 5. 最长回文子串 6 https://study-cn.com/problems/longest-palindromic-substring 391 | 494. 目标和 6 https://study-cn.com/problems/target-sum 392 | 15. 三数之和 6 https://study-cn.com/problems/3sum 393 | 1. 两数之和 5 https://study-cn.com/problems/two-sum 394 | 239. 滑动窗口最大值 5 https://study-cn.com/problems/sliding-window-maximum 395 | 136. 只出现一次的数字 5 https://study-cn.com/problems/single-number 396 | 25. K 个一组翻转链表 5 https://study-cn.com/problems/reverse-nodes-in-k-group 397 | 232. 用栈实现队列 5 https://study-cn.com/problems/implement-queue-using-stacks 398 | 155. 最小栈 5 https://study-cn.com/problems/min-stack 399 | 104. 二叉树的最大深度 5 https://study-cn.com/problems/maximum-depth-of-binary-tree 400 | 剑指 Offer 22. 链表中倒数第k个节点 5 https://study-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof 401 | 322. 零钱兑换 5 https://study-cn.com/problems/coin-change 402 | 525. 连续数组 5 https://study-cn.com/problems/contiguous-array 403 | 169. 多数元素 4 https://study-cn.com/problems/majority-element 404 | 236. 二叉树的最近公共祖先 4 https://study-cn.com/problems/lowest-common-ancestor-of-a-binary-tree 405 | 328. 奇偶链表 4 https://study-cn.com/problems/odd-even-linked-list 406 | 9. 回文数 4 https://study-cn.com/problems/palindrome-number 407 | 112. 路径总和 4 https://study-cn.com/problems/path-sum 408 | 142. 环形链表 II 4 https://study-cn.com/problems/linked-list-cycle-ii 409 | 54. 螺旋矩阵 4 https://study-cn.com/problems/spiral-matrix 410 | 240. 搜索二维矩阵 II 4 https://study-cn.com/problems/search-a-2d-matrix-ii 411 | 20. 有效的括号 4 https://study-cn.com/problems/valid-parentheses 412 | 706. 设计哈希映射 4 https://study-cn.com/problems/design-hashmap 413 | 678. 有效的括号字符串 4 https://study-cn.com/problems/valid-parenthesis-string 414 | 补充题5. 手撕归并排序 4 https://study-cn.com/problems/sort-an-array 415 | 31. 下一个排列 4 https://study-cn.com/problems/next-permutation 416 | 33. 搜索旋转排序数组 4 https://study-cn.com/problems/search-in-rotated-sorted-array 417 | 199. 二叉树的右视图 4 https://study-cn.com/problems/binary-tree-right-side-view 418 | 887. 鸡蛋掉落 4 https://study-cn.com/problems/super-egg-drop 419 | 46. 全排列 4 https://study-cn.com/problems/permutations 420 | 88. 合并两个有序数组 4 https://study-cn.com/problems/merge-sorted-array 421 | 23. 合并K个排序链表 3 https://study-cn.com/problems/merge-k-sorted-lists 422 | 189. 旋转数组 3 https://study-cn.com/problems/rotate-array 423 | 59. 螺旋矩阵 II 3 https://study-cn.com/problems/spiral-matrix-ii 424 | 43. 字符串相乘 3 https://study-cn.com/problems/multiply-strings 425 | 384. 打乱数组 3 https://study-cn.com/problems/shuffle-an-array 426 | 172. 阶乘后的零 3 https://study-cn.com/problems/factorial-trailing-zeroes 427 | 227. 基本计算器 II 3 https://study-cn.com/problems/basic-calculator-ii 428 | 718. 最长重复子数组 3 https://study-cn.com/problems/maximum-length-of-repeated-subarray 429 | 394. 字符串解码 3 https://study-cn.com/problems/decode-string 430 | 69. x 的平方根 3 https://study-cn.com/problems/sqrtx 431 | 148. 排序链表 3 https://study-cn.com/problems/sort-list 432 | 143. 重排链表 3 https://study-cn.com/problems/reorder-list 433 | 121. 买卖股票的最佳时机 2 https://study-cn.com/problems/best-time-to-buy-and-sell-stock 434 | 37. 解数独 2 https://study-cn.com/problems/sudoku-solver 435 | 剑指 Offer 42. 连续子数组的最大和 2 https://study-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof 436 | 287. 寻找重复数 2 https://study-cn.com/problems/find-the-duplicate-number 437 | 129. 求根到叶子节点数字之和 2 https://study-cn.com/problems/sum-root-to-leaf-numbers 438 | 78. 子集 2 https://study-cn.com/problems/subsets 439 | 剑指 Offer 03. 数组中重复的数字 2 https://study-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof 440 | 93. 复原IP地址 2 https://study-cn.com/problems/restore-ip-addresses 441 | 82. 删除排序链表中的重复元素 II 2 https://study-cn.com/problems/remove-duplicates-from-sorted-list-ii 442 | 138. 复制带随机指针的链表 2 https://study-cn.com/problems/copy-list-with-random-pointer 443 | 113. 路径总和 II 2 https://study-cn.com/problems/path-sum-ii 444 | 剑指 Offer 65. 不用加减乘除做加法 2 https://study-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof 445 | 459. 重复的子字符串 2 https://study-cn.com/problems/repeated-substring-pattern 446 | 19. 删除链表的倒数第N个节点 2 https://study-cn.com/problems/remove-nth-node-from-end-of-list 447 | 11. 盛最多水的容器 2 https://study-cn.com/problems/container-with-most-water 448 | 26. 删除排序数组中的重复项 2 https://study-cn.com/problems/remove-duplicates-from-sorted-array 449 | 103. 二叉树的锯齿形层次遍历 2 https://study-cn.com/problems/binary-tree-zigzag-level-order-traversal 450 | 补充题21. 字符串相减 2 https://mp.weixin.qq.com/s/kCue4c0gnLSw0HosFl_t7w 451 | 907. 子数组的最小值之和 2 https://study-cn.com/problems/sum-of-subarray-minimums 452 | 补充题22. IP地址与整数的转换 2 https://mp.weixin.qq.com/s/u-RahFTB3JIqND41HqtotQ 453 | 692. 前K个高频单词 2 https://study-cn.com/problems/top-k-frequent-words 454 | 200. 岛屿数量 2 https://study-cn.com/problems/number-of-islands 455 | 14. 最长公共前缀 2 https://study-cn.com/problems/longest-common-prefix 456 | 118. 杨辉三角 2 https://study-cn.com/problems/pascals-triangle 457 | 剑指 Offer 57 - II. 和为s的连续正数序列 2 https://study-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof 458 | 剑指 Offer 40. 最小的k个数 2 https://study-cn.com/problems/zui-xiao-de-kge-shu-lcof 459 | 378. 有序矩阵中第K小的元素 2 https://study-cn.com/problems/kth-smallest-element-in-a-sorted-matrix 460 | 48. 旋转图像 2 https://study-cn.com/problems/rotate-image 461 | 480. 滑动窗口中位数 2 https://study-cn.com/problems/sliding-window-median 462 | 1095. 山脉数组中查找目标值 2 https://study-cn.com/problems/find-in-mountain-array 463 | 405. 数字转换为十六进制数 2 https://study-cn.com/problems/convert-a-number-to-hexadecimal 464 | 92. 反转链表 II 2 https://study-cn.com/problems/reverse-linked-list-ii 465 | 876. 链表的中间结点 2 https://study-cn.com/problems/middle-of-the-linked-list 466 | 剑指 Offer 24. 反转链表 1 https://study-cn.com/problems/fan-zhuan-lian-biao-lcof 467 | 409. 最长回文串 1 https://study-cn.com/problems/longest-palindrome 468 | 224. 基本计算器 1 https://study-cn.com/problems/basic-calculator 469 | 94. 二叉树的中序遍历 1 https://study-cn.com/problems/binary-tree-inorder-traversal 470 | 530. 二叉搜索树的最小绝对差 1 https://study-cn.com/problems/minimum-absolute-difference-in-bst 471 | 315. 计算右侧小于当前元素的个数 1 https://study-cn.com/problems/count-of-smaller-numbers-after-self 472 | 260. 只出现一次的数字 III 1 https://study-cn.com/problems/single-number-iii 473 | 679. 24 点游戏 1 https://study-cn.com/problems/24-game 474 | 994. 腐烂的橘子 1 https://study-cn.com/problems/rotting-oranges 475 | 588. 设计内存文件系统 1 https://study-cn.com/problems/design-in-memory-file-system 476 | 125. 验证回文串 1 https://study-cn.com/problems/valid-palindrome 477 | 41. 缺失的第一个正数 1 https://study-cn.com/problems/first-missing-positive 478 | 337. 打家劫舍 III 1 https://study-cn.com/problems/house-robber-iii 479 | 226. 翻转二叉树 1 https://study-cn.com/problems/invert-binary-tree 480 | 剑指 Offer 51. 数组中的逆序对 1 https://study-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof 481 | 补充题17. 两个有序数组第k小的数 1 482 | 217. 存在重复元素 1 https://study-cn.com/problems/contains-duplicate 483 | 242. 有效的字母异位词 1 https://study-cn.com/problems/valid-anagram 484 | 63. 不同路径 II 1 https://study-cn.com/problems/unique-paths-ii 485 | 74. 搜索二维矩阵 1 https://study-cn.com/problems/search-a-2d-matrix 486 | 701. 二叉搜索树中的插入操作 1 https://study-cn.com/problems/insert-into-a-binary-search-tree 487 | 1047. 删除字符串中的所有相邻重复项 1 https://study-cn.com/problems/remove-all-adjacent-duplicates-in-string 488 | 75. 颜色分类 1 https://study-cn.com/problems/sort-colors 489 | 补充题14. 阿拉伯数字转中文数字 1 490 | 134. 加油站 1 https://study-cn.com/problems/gas-station 491 | 231. 2的幂 1 https://study-cn.com/problems/power-of-two 492 | 451. 根据字符出现频率排序 1 https://study-cn.com/problems/sort-characters-by-frequency 493 | 24. 两两交换链表中的节点 1 https://study-cn.com/problems/swap-nodes-in-pairs 494 | 剑指 Offer 10- I. 斐波那契数列 1 https://study-cn.com/problems/fei-bo-na-qi-shu-lie-lcof 495 | 7. 整数反转 1 https://study-cn.com/problems/reverse-integer 496 | 316. 去除重复字母 1 https://study-cn.com/problems/remove-duplicate-letters 497 | 117. 填充每个节点的下一个右侧节点指针 II 1 https://study-cn.com/problems/populating-next-right-pointers-in-each-node-ii 498 | 61. 旋转链表 1 https://study-cn.com/problems/rotate-list 499 | 283. 移动零 1 https://study-cn.com/problems/move-zeroes 500 | 剑指 Offer 36. 二叉搜索树与双向链表 1 https://study-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof 501 | 128. 最长连续序列 1 https://study-cn.com/problems/longest-consecutive-sequence 502 | 1044. 最长重复子串 1 https://study-cn.com/problems/longest-duplicate-substring 503 | 76. 最小覆盖子串 1 https://study-cn.com/problems/minimum-window-substring 504 | 50. Pow(x, n) 1 https://study-cn.com/problems/powx-n 505 | 162. 寻找峰值 1 https://study-cn.com/problems/find-peak-element 506 | 208. 实现 Trie (前缀树) 1 https://study-cn.com/problems/implement-trie-prefix-tree 507 | 179. 最大数 1 https://study-cn.com/problems/largest-number 508 | 728. 自除数 1 https://study-cn.com/problems/self-dividing-numbers 509 | 391. 完美矩形 1 https://study-cn.com/problems/perfect-rectangle 510 | 523. 连续的子数组和 1 https://study-cn.com/problems/continuous-subarray-sum 511 | 137. 只出现一次的数字 II 1 https://study-cn.com/problems/single-number-ii 512 | 598. 范围求和 II 1 https://study-cn.com/problems/range-addition-ii 513 | 258. 各位相加 1 https://study-cn.com/problems/add-digits 514 | 443. 压缩字符串 1 https://study-cn.com/problems/string-compression 515 | 72. 编辑距离 1 https://study-cn.com/problems/edit-distance 516 | 292. Nim 游戏 1 https://study-cn.com/problems/nim-game 517 | 297. 二叉树的序列化与反序列化 1 https://study-cn.com/problems/serialize-and-deserialize-binary-tree 518 | 518. 零钱兑换 II 1 https://study-cn.com/problems/coin-change-2 519 | 剑指 Offer 11. 旋转数组的最小数字 1 https://study-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof 520 | 剑指 Offer 32 - III. 从上到下打印二叉树 III 1 https://study-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof 521 | 622. 设计循环队列 1 https://study-cn.com/problems/design-circular-queue 522 | 1299. 将每个元素替换为右侧最大元素 1 https://study-cn.com/problems/replace-elements-with-greatest-element-on-right-side 523 | 67. 二进制求和 1 https://study-cn.com/problems/add-binary 524 | 64. 最小路径和 1 https://study-cn.com/problems/minimum-path-sum 525 | 剑指 Offer 52. 两个链表的第一个公共节点 1 https://study-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof 526 | 560. 和为K的子数组 1 https://study-cn.com/problems/subarray-sum-equals-k 527 | 28. 实现 strStr() 1 https://study-cn.com/problems/implement-strstr 528 | 84. 柱状图中最大的矩形 1 https://study-cn.com/problems/largest-rectangle-in-histogram 529 | 295. 数据流的中位数 1 https://study-cn.com/problems/find-median-from-data-stream 530 | 225. 用队列实现栈 1 https://study-cn.com/problems/implement-stack-using-queues 531 | 剑指 Offer 09. 用两个栈实现队列 1 https://study-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof 532 | 442. 数组中重复的数据 1 https://study-cn.com/problems/find-all-duplicates-in-an-array 533 | 381. O(1) 时间插入、删除和获取随机元素 - 允许重复 1 https://study-cn.com/problems/insert-delete-getrandom-o1-duplicates-allowed 534 | 695. 岛屿的最大面积 1 https://study-cn.com/problems/max-area-of-island 535 | 111. 二叉树的最小深度 1 https://study-cn.com/problems/minimum-depth-of-binary-tree 536 | 567. 字符串的排列 1 https://study-cn.com/problems/permutation-in-string 537 | 107. 二叉树的层次遍历 II 1 https://study-cn.com/problems/binary-tree-level-order-traversal-ii 538 | 1219. 黄金矿工 1 https://study-cn.com/problems/path-with-maximum-gold 539 | 剑指 Offer 16. 数值的整数次方 1 https://study-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof 540 | 541 | */ 542 | 543 | //二分查找 快排 544 | struct list_node { 545 | struct list_node* next; 546 | int data; 547 | } 548 | 549 | struct list_node *g_head; 550 | 551 | void list_insert(struct list_node *new_node) { 552 | if (g_head == NULL) { 553 | g_head = new_node; 554 | break; 555 | } 556 | 557 | struct list_node* tmp = NULL; 558 | tmp = g_head; 559 | g_head->next = new_node; 560 | new_node->next = tmp->next; 561 | } 562 | 563 | 564 | void list_reverse(struct list_node *head) { 565 | 566 | } 567 | 568 | 569 | struct ListNode* reverseList(struct ListNode* head) { 570 | struct ListNode* newHead = NULL; 571 | while (head) { 572 | // 创建一个新节点 573 | struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode)); 574 | newNode->val = head->val; 575 | // 将新节点插入到新链表的头部 576 | newNode->next = newHead; 577 | newHead = newNode; 578 | // 移动到原链表的下一个节点 579 | head = head->next; 580 | } 581 | return newHead; 582 | } 583 | 584 | 585 | int main() 586 | { 587 | 588 | } 589 | 590 | /** 591 | * struct ListNode { 592 | * int val; 593 | * struct ListNode *next; 594 | * }; 595 | */ 596 | /** 597 | * 598 | * @param pHead ListNode类 599 | * @return ListNode类 600 | */ 601 | struct ListNode* ReverseList(struct ListNode* pHead ) { 602 | // write code here 603 | struct ListNode *pre = NULL; 604 | struct ListNode *cur = pHead; 605 | struct ListNode *next = NULL; 606 | while(cur) 607 | { 608 | next = cur->next; 609 | cur->next = pre; 610 | pre = cur; 611 | cur = next; 612 | } 613 | return pre; 614 | } 615 | 616 | 617 | 618 | //K 个一组翻转链表 619 | struct ListNode { 620 | int val; 621 | struct ListNode *next; 622 | }; 623 | /** 624 | * struct ListNode { 625 | * int val; 626 | * struct ListNode *next; 627 | * }; 628 | */ 629 | /** 630 | * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 631 | * 632 | * 633 | * @param head ListNode类 634 | * @param k int整型 635 | * @return ListNode类 636 | */ 637 | struct ListNode* reverseKGroup(struct ListNode* head, int k ) { 638 | // write code here 639 | //找到每次翻转的尾部 640 | struct ListNode* tail = head; 641 | //遍历k次到尾部 642 | for(int i = 0; i < k; i++){ 643 | //如果不足k到了链表尾,直接返回,不翻转 644 | if(tail == NULL) 645 | return head; 646 | tail = tail->next; 647 | } 648 | //翻转时需要的前序和当前节点 649 | struct ListNode* pre = NULL; 650 | struct ListNode* cur = head; 651 | //在到达当前段尾节点前 652 | while(cur != tail){ 653 | //翻转 654 | struct ListNode* temp = cur->next; 655 | cur->next = pre; 656 | pre = cur; 657 | cur = temp; 658 | } 659 | //当前尾指向下一段要翻转的链表 660 | head->next = reverseKGroup(tail, k); 661 | return pre; 662 | } 663 | 664 | 665 | //给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 https://zhuanlan.zhihu.com/p/649412389 666 | //s = "abcabcbb" 因为无重复字符的最长子串是 "abc",所以其长度为 3。 667 | // 1.首先如何判断我们取得的字串内有重复字符,利用哈希表,将每一个进窗口的字符进入哈希表,每当新字符进入时就判断一下哈希表上对应的值是否存在。 668 | // 2.如果存在的话,此时就要出窗口,将left不断右移,每右移一个,就将对应字符的哈希表减1,直到重复字符的哈希表值为1即可。 669 | // 3.此时更新结果。 670 | 671 | int lengthOfLongestSubstring(char* s) { 672 | int n = strlen(s); 673 | int maxLength = 0; 674 | int start = 0; 675 | int end = 0; 676 | int charSet[128] = {0}; // 用于记录字符是否出现过 677 | 678 | while (end < n) { 679 | char currentChar = s[end]; 680 | //说明出现过 681 | if (charSet[currentChar] == 0) { 682 | charSet[currentChar] = 1; 683 | end++; 684 | maxLength = (end - start > maxLength) ? (end - start) : maxLength; 685 | } else { 686 | charSet[s[start]] = 0; 687 | start++; 688 | } 689 | } 690 | 691 | return maxLength; 692 | } 693 | 694 | 695 | 696 | /* 697 | s1 = "ab" s2 = "eidbaooo" 返回true 698 | 699 | 700 | s2 包含 s1 的排列之一 ("ba"). 701 | */ 702 | bool checkInclusion(char* s1, char* s2) { 703 | int len1 = 0, len2 = 0; 704 | while (s1[len1]!= '\0') len1++; 705 | while (s2[len2]!= '\0') len2++; 706 | 707 | if (len1 > len2) return false; 708 | 709 | int count1[26] = {0}; 710 | int count2[26] = {0}; 711 | 712 | for (int i = 0; i < len1; i++) { 713 | count1[s1[i] - 'a']++; 714 | count2[s2[i] - 'a']++; 715 | } 716 | 717 | for (int i = len1; i < len2; i++) { 718 | if (memcmp(count1, count2, sizeof(count1)) == 0) return true; 719 | count2[s2[i] - 'a']++; 720 | count2[s2[i - len1] - 'a']--; 721 | } 722 | 723 | return memcmp(count1, count2, sizeof(count1)) == 0; 724 | } 725 | 726 | 727 | 728 | //LRU 729 | #include 730 | #include 731 | 732 | // 定义双向链表节点 733 | typedef struct ListNode { 734 | int key; 735 | int value; 736 | struct ListNode *prev; 737 | struct ListNode *next; 738 | } ListNode; 739 | 740 | // 定义 LRU 缓存结构体 741 | typedef struct { 742 | int capacity; 743 | int size; 744 | ListNode *head; 745 | ListNode *tail; 746 | ListNode **hashMap; 747 | } LRUCache; 748 | 749 | // 创建新的双向链表节点 750 | ListNode *createNode(int key, int value) { 751 | ListNode *node = (ListNode *)malloc(sizeof(ListNode)); 752 | node->key = key; 753 | node->value = value; 754 | node->prev = NULL; 755 | node->next = NULL; 756 | return node; 757 | } 758 | 759 | // 删除双向链表中的节点 760 | void deleteNode(ListNode *node) { 761 | if (node->prev) 762 | node->prev->next = node->next; 763 | if (node->next) 764 | node->next->prev = node->prev; 765 | free(node); 766 | } 767 | 768 | // 将节点移动到双向链表头部,表示最近使用 769 | void moveToHead(LRUCache *cache, ListNode *node) { 770 | if (node == cache->head) 771 | return; 772 | deleteNode(node); 773 | node->next = cache->head; 774 | if (cache->head) 775 | cache->head->prev = node; 776 | cache->head = node; 777 | if (!cache->tail) 778 | cache->tail = node; 779 | } 780 | 781 | // 初始化 LRU 缓存 782 | LRUCache *createLRUCache(int capacity) { 783 | LRUCache *cache = (LRUCache *)malloc(sizeof(LRUCache)); 784 | cache->capacity = capacity; 785 | cache->size = 0; 786 | cache->head = NULL; 787 | cache->tail = NULL; 788 | cache->hashMap = (ListNode **)malloc(capacity * sizeof(ListNode *)); 789 | for (int i = 0; i < capacity; i++) 790 | cache->hashMap[i] = NULL; 791 | return cache; 792 | } 793 | 794 | // 获取缓存中的值 795 | int get(LRUCache *cache, int key) { 796 | ListNode *node = cache->hashMap[key % cache->capacity]; 797 | while (node && node->key!= key) 798 | node = node->next; 799 | if (node) { 800 | moveToHead(cache, node); 801 | return node->value; 802 | } 803 | return -1; 804 | } 805 | 806 | // 设置缓存中的值 807 | void put(LRUCache *cache, int key, int value) { 808 | ListNode *node = cache->hashMap[key % cache->capacity]; 809 | while (node && node->key!= key) 810 | node = node->next; 811 | if (node) { 812 | node->value = value; 813 | moveToHead(cache, node); 814 | } else { 815 | ListNode *newNode = createNode(key, value); 816 | if (cache->size == cache->capacity) { 817 | ListNode *removed = cache->tail; 818 | cache->tail = cache->tail->prev; 819 | if (cache->tail) 820 | cache->tail->next = NULL; 821 | cache->hashMap[removed->key % cache->capacity] = NULL; 822 | deleteNode(removed); 823 | cache->size--; 824 | } 825 | newNode->next = cache->head; 826 | if (cache->head) 827 | cache->head->prev = newNode; 828 | cache->head = newNode; 829 | if (!cache->tail) 830 | cache->tail = newNode; 831 | cache->hashMap[key % cache->capacity] = newNode; 832 | cache->size++; 833 | } 834 | } 835 | 836 | // 释放 LRU 缓存占用的内存 837 | void freeLRUCache(LRUCache *cache) { 838 | ListNode *current = cache->head; 839 | while (current) { 840 | ListNode *next = current->next; 841 | free(current); 842 | current = next; 843 | } 844 | free(cache->hashMap); 845 | free(cache); 846 | } 847 | 848 | int main() { 849 | LRUCache *cache = createLRUCache(2); 850 | put(cache, 1, 1); 851 | put(cache, 2, 2); 852 | printf("%d\n", get(cache, 1)); 853 | put(cache, 3, 3); 854 | printf("%d\n", get(cache, 2)); 855 | put(cache, 4, 4); 856 | printf("%d\n", get(cache, 1)); 857 | printf("%d\n", get(cache, 3)); 858 | printf("%d\n", get(cache, 4)); 859 | freeLRUCache(cache); 860 | return 0; 861 | } 862 | 863 | //快排取第K个数 快排图解https://blog.csdn.net/2301_80401288/article/details/141501102 864 | #include 865 | 866 | // 划分函数,用于快速排序 867 | int partition(int arr[], int low, int high) { 868 | int pivot = arr[high]; 869 | int i = low - 1; 870 | for (int j = low; j < high; j++) { 871 | if (arr[j] >= pivot) { 872 | i++; 873 | int temp = arr[i]; 874 | arr[i] = arr[j]; 875 | arr[j] = temp; 876 | } 877 | } 878 | int temp = arr[i + 1]; 879 | arr[i + 1] = arr[high]; 880 | arr[high] = temp; 881 | return i + 1; 882 | } 883 | 884 | // 快速排序递归函数 885 | void quickSort(int arr[], int low, int high) { 886 | if (low < high) { 887 | int pivotIndex = partition(arr, low, high); 888 | quickSort(arr, low, pivotIndex - 1); 889 | quickSort(arr, pivotIndex + 1, high); 890 | } 891 | } 892 | 893 | // 找到数组中第 K 个最大元素 894 | int findKthLargest(int arr[], int n, int k) { 895 | quickSort(arr, 0, n - 1); 896 | return arr[k - 1]; 897 | } 898 | 899 | int main() { 900 | int arr[] = {3, 2, 1, 5, 6, 4}; 901 | int n = sizeof(arr) / sizeof(arr[0]); 902 | int k = 2; 903 | int result = findKthLargest(arr, n, k); 904 | printf("xxxxxxxxxxx %d\n", k, result); 905 | return 0; 906 | } 907 | 908 | 909 | 910 | 911 | 912 | 913 | #include 914 | #include 915 | 916 | // 二叉树节点结构体 917 | typedef struct TreeNode { 918 | int val; 919 | struct TreeNode *left; 920 | struct TreeNode *right; 921 | } TreeNode; 922 | 923 | // 创建新节点 924 | TreeNode* newTreeNode(int val) { 925 | TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); 926 | node->val = val; 927 | node->left = NULL; 928 | node->right = NULL; 929 | return node; 930 | } 931 | 932 | // 插入节点 933 | TreeNode* insertTreeNode(TreeNode* root, int val) { 934 | if (root == NULL) { 935 | return newTreeNode(val); 936 | } 937 | if (val < root->val) { 938 | root->left = insertTreeNode(root->left, val); 939 | } else { 940 | root->right = insertTreeNode(root->right, val); 941 | } 942 | return root; 943 | } 944 | 945 | // 前序遍历 946 | void preOrderTraversal(TreeNode* root) { 947 | if (root == NULL) return; 948 | printf("%d ", root->val); 949 | preOrderTraversal(root->left); 950 | preOrderTraversal(root->right); 951 | } 952 | 953 | // 中序遍历 954 | void inOrderTraversal(TreeNode* root) { 955 | if (root == NULL) return; 956 | inOrderTraversal(root->left); 957 | printf("%d ", root->val); 958 | inOrderTraversal(root->right); 959 | } 960 | 961 | // 后序遍历 962 | void postOrderTraversal(TreeNode* root) { 963 | if (root == NULL) return; 964 | postOrderTraversal(root->left); 965 | postOrderTraversal(root->right); 966 | printf("%d ", root->val); 967 | } 968 | 969 | 970 | // 释放二叉树内存 971 | void freeTree(TreeNode* root) { 972 | if (root == NULL) return; 973 | freeTree(root->left); 974 | freeTree(root->right); 975 | free(root); 976 | } 977 | 978 | // 锯齿形层次遍历二叉树 979 | void zigzagLevelOrder(TreeNode* root) { 980 | if (root == NULL) return; 981 | 982 | // 使用队列实现层次遍历 983 | TreeNode** queue = (TreeNode**)malloc(sizeof(TreeNode*) * 1000); 984 | int front = 0, rear = 0; 985 | queue[rear++] = root; 986 | int level = 0; 987 | int leftToRight = 1; 988 | 989 | while (front < rear) { 990 | int size = rear - front; 991 | int* levelArray = (int*)malloc(sizeof(int) * size); 992 | int index = 0; 993 | 994 | for (int i = 0; i < size; i++) { 995 | TreeNode* current = queue[front++]; 996 | levelArray[index++] = current->val; 997 | 998 | if (current->left) queue[rear++] = current->left; 999 | if (current->right) queue[rear++] = current->right; 1000 | } 1001 | 1002 | if (!leftToRight) { 1003 | for (int i = 0; i < index / 2; i++) { 1004 | int temp = levelArray[i]; 1005 | levelArray[i] = levelArray[index - i - 1]; 1006 | levelArray[index - i - 1] = temp; 1007 | } 1008 | } 1009 | 1010 | for (int i = 0; i < index; i++) { 1011 | printf("%d ", levelArray[i]); 1012 | } 1013 | printf("\n"); 1014 | 1015 | free(levelArray); 1016 | leftToRight =!leftToRight; 1017 | level++; 1018 | } 1019 | 1020 | free(queue); 1021 | } 1022 | 1023 | int main() { 1024 | TreeNode* root = NULL; 1025 | root = insertTreeNode(root, 3); 1026 | root = insertTreeNode(root, 9); 1027 | root = insertTreeNode(root, 20); 1028 | root = insertTreeNode(root, 15); 1029 | root = insertTreeNode(root, 7); 1030 | 1031 | zigzagLevelOrder(root); 1032 | 1033 | freeTree(root); 1034 | return 0; 1035 | } 1036 | 1037 | 1038 | //满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。 1039 | //三数之和 参考https://baijiahao.baidu.com/s?id=1764792807745816217&wfr=spider&for=pc 1040 | #include 1041 | #include 1042 | 1043 | void threeSum(int* nums, int numsSize, int*** result, int* returnSize, int** returnColumnSizes) { 1044 | *returnSize = 0; 1045 | if (numsSize < 3) { 1046 | return; 1047 | } 1048 | 1049 | qsort(nums, numsSize, sizeof(int), (__compar_fn_t)compare); 1050 | 1051 | for (int i = 0; i < numsSize - 2; i++) { 1052 | if (i > 0 && nums[i] == nums[i - 1]) continue; 1053 | 1054 | int left = i + 1; 1055 | int right = numsSize - 1; 1056 | 1057 | while (left < right) { 1058 | int sum = nums[i] + nums[left] + nums[right]; 1059 | if (sum == 0) { 1060 | (*returnSize)++; 1061 | *result = (int**)realloc(*result, *returnSize * sizeof(int*)); 1062 | (*result)[*returnSize - 1] = (int*)malloc(3 * sizeof(int)); 1063 | (*result)[*returnSize - 1][0] = nums[i]; 1064 | (*result)[*returnSize - 1][1] = nums[left]; 1065 | (*result)[*returnSize - 1][2] = nums[right]; 1066 | 1067 | while (left < right && nums[left] == nums[left + 1]) left++; 1068 | while (left < right && nums[right] == nums[right - 1]) right--; 1069 | 1070 | left++; 1071 | right--; 1072 | } else if (sum < 0) { 1073 | left++; 1074 | } else { 1075 | right--; 1076 | } 1077 | } 1078 | } 1079 | 1080 | *returnColumnSizes = (int*)malloc(*returnSize * sizeof(int)); 1081 | for (int i = 0; i < *returnSize; i++) { 1082 | (*returnColumnSizes)[i] = 3; 1083 | } 1084 | } 1085 | 1086 | int compare(const void* a, const void* b) { 1087 | return ( *(int*)a - *(int*)b ); 1088 | } 1089 | 1090 | int main() { 1091 | int nums[] = {-1, 0, 1, 2, -1, -4}; 1092 | int numsSize = sizeof(nums) / sizeof(nums[0]); 1093 | int** result; 1094 | int returnSize; 1095 | int* returnColumnSizes; 1096 | 1097 | threeSum(nums, numsSize, &result, &returnSize, &returnColumnSizes); 1098 | 1099 | for (int i = 0; i < returnSize; i++) { 1100 | for (int j = 0; j < returnColumnSizes[i]; j++) { 1101 | printf("%d ", result[i][j]); 1102 | } 1103 | printf("\n"); 1104 | } 1105 | 1106 | for (int i = 0; i < returnSize; i++) { 1107 | free(result[i]); 1108 | } 1109 | free(result); 1110 | free(returnColumnSizes); 1111 | 1112 | return 0; 1113 | } 1114 | 1115 | //三数之和 代码 java 1116 | class Solution { 1117 | public List> threeSum(int[] nums) { 1118 | List> result = new ArrayList(); 1119 | Arrays.sort(nums); 1120 | for (int i = 0; i < nums.length && nums[i] <= 0; i++) { 1121 | if (i > 0 && nums[i - 1] == nums[i]) continue; 1122 | int p = i + 1, q = nums.length - 1; 1123 | while (p < q && nums[q] >= 0) { 1124 | int iv = nums[i], qv = nums[q], pv = nums[p], sum = iv + pv + qv; 1125 | if (sum == 0) result.add(Arrays.asList(iv, pv, qv)); 1126 | if (sum > 0) while (q > 0 && nums[q] == qv) q--; 1127 | else while (p < nums.length - 1 && nums[p] == pv) p++; 1128 | } 1129 | } 1130 | return result; 1131 | } 1132 | } 1133 | 1134 | 1135 | 1136 | //卖股票的最佳时机 https://blog.csdn.net/m0_46202073/article/details/112395067 1137 | #include 1138 | #include 1139 | 1140 | int maxProfit(int* prices, int pricesSize) { 1141 | int minPrice = prices[0]; 1142 | int maxProfit = 0; 1143 | for (int i = 1; i < pricesSize; i++) { 1144 | if (prices[i] < minPrice) { 1145 | minPrice = prices[i]; 1146 | } else { 1147 | int currentProfit = prices[i] - minPrice; 1148 | if (currentProfit > maxProfit) { 1149 | maxProfit = currentProfit; 1150 | } 1151 | } 1152 | } 1153 | return maxProfit; 1154 | } 1155 | 1156 | int main() { 1157 | int prices[] = {7, 1, 5, 3, 6, 4}; 1158 | int pricesSize = sizeof(prices) / sizeof(prices[0]); 1159 | int result = maxProfit(prices, pricesSize); 1160 | printf("lirun:%d\n", result); 1161 | return 0; 1162 | } 1163 | 1164 | 1165 | //二叉树中两个节点的最近公共祖先 1166 | #include 1167 | #include 1168 | 1169 | // 二叉树节点结构 1170 | struct TreeNode { 1171 | int val; 1172 | struct TreeNode *left; 1173 | struct TreeNode *right; 1174 | }; 1175 | 1176 | // 创建新节点 1177 | struct TreeNode* newNode(int val) { 1178 | struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode)); 1179 | node->val = val; 1180 | node->left = NULL; 1181 | node->right = NULL; 1182 | return node; 1183 | } 1184 | 1185 | /* 1186 | 在这个代码中,我们首先检查根节点是否为空,然后检查根节点是否为给定的节点之一或者它们的父节点。如果不是, 1187 | 我们递归地在左子树和右子树中搜索。如果在左子树中找到了公共祖先,并且在右子树中也找到了公共祖先,那么根节 1188 | 点就是最近的公共祖先。否则,我们只需要返回找到的任一公共祖先。 1189 | */ 1190 | // 寻找最近公共祖先 1191 | struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) { 1192 | if (root == NULL || root == p || root == q) { 1193 | return root; 1194 | } 1195 | struct TreeNode* left = lowestCommonAncestor(root->left, p, q); 1196 | struct TreeNode* right = lowestCommonAncestor(root->right, p, q); 1197 | if (left && right) { 1198 | return root; 1199 | } 1200 | return left? left : right; 1201 | } 1202 | 1203 | // 释放二叉树内存 1204 | void freeTree(struct TreeNode* root) { 1205 | if (root == NULL) return; 1206 | freeTree(root->left); 1207 | freeTree(root->right); 1208 | free(root); 1209 | } 1210 | 1211 | int main() { 1212 | // 创建二叉树 1213 | struct TreeNode* root = newNode(3); 1214 | root->left = newNode(5); 1215 | root->right = newNode(1); 1216 | root->left->left = newNode(6); 1217 | root->left->right = newNode(2); 1218 | root->right->left = newNode(0); 1219 | root->right->right = newNode(8); 1220 | root->left->right->left = newNode(7); 1221 | root->left->right->right = newNode(4); 1222 | 1223 | struct TreeNode* p = root->left; 1224 | struct TreeNode* q = root->left->right->right; 1225 | 1226 | struct TreeNode* result = lowestCommonAncestor(root, p, q); 1227 | if (result) { 1228 | printf("最近公共祖先的值为:%d\n", result->val); 1229 | } else { 1230 | printf("未找到最近公共祖先\n"); 1231 | } 1232 | 1233 | // 释放内存 1234 | freeTree(root); 1235 | 1236 | return 0; 1237 | } 1238 | 1239 | 1240 | 1241 | #include 1242 | //接雨水 找到最高的那个柱子,把数组分成两部分,对于两部分都已经确定了一个边界高度了,所以对剩余的每个柱子至于确定一边的边界高度值,就可以直接计算出能接的雨水了。 1243 | int trap(int* height, int heightSize) { 1244 | if (heightSize <= 2) { 1245 | return 0; 1246 | } 1247 | int left = 0, right = heightSize - 1; 1248 | int leftMax = 0, rightMax = 0; 1249 | int ans = 0; 1250 | while (left < right) { 1251 | if (height[left] < height[right]) { 1252 | if (height[left] >= leftMax) { 1253 | leftMax = height[left]; 1254 | } else { 1255 | ans += leftMax - height[left]; 1256 | } 1257 | left++; 1258 | } else { 1259 | if (height[right] >= rightMax) { 1260 | rightMax = height[right]; 1261 | } else { 1262 | ans += rightMax - height[right]; 1263 | } 1264 | right--; 1265 | } 1266 | } 1267 | return ans; 1268 | } 1269 | 1270 | //https://zhuanlan.zhihu.com/p/79811305 1271 | public int trap(int[] height) { 1272 | int sum = 0; 1273 | //最两端的列不用考虑,因为一定不会有水。所以下标从 1 到 length - 2 1274 | for (int i = 1; i < height.length - 1; i++) { 1275 | int max_left = 0; 1276 | //找出左边最高 1277 | for (int j = i - 1; j >= 0; j--) { 1278 | if (height[j] > max_left) { 1279 | max_left = height[j]; 1280 | } 1281 | } 1282 | int max_right = 0; 1283 | //找出右边最高 1284 | for (int j = i + 1; j < height.length; j++) { 1285 | if (height[j] > max_right) { 1286 | max_right = height[j]; 1287 | } 1288 | } 1289 | //找出两端较小的 1290 | int min = Math.min(max_left, max_right); 1291 | //只有较小的一段大于当前列的高度才会有水,其他情况不会有水 1292 | if (min > height[i]) { 1293 | sum = sum + (min - height[i]); 1294 | } 1295 | } 1296 | return sum; 1297 | } 1298 | 1299 | int main() { 1300 | int height[] = { 0,1,0,2,1,0,1,3,2,1,2,1 }; 1301 | int heightSize = sizeof(height) / sizeof(height[0]); 1302 | int result = trap(height, heightSize); 1303 | printf("水量为:%d\n", result); 1304 | return 0; 1305 | } 1306 | 1307 | //搜索旋转排序数组 1308 | //https://baijiahao.baidu.com/s?id=1778879597494232635&wfr=spider&for=pc 1309 | #include 1310 | 1311 | // 判断是否升序 1312 | int isSeq(int* nums, int low, int high) { 1313 | if (low > high) return 0; // pivot 左边无元素 1314 | else if (nums[low] <= nums[high]) return 1; // 相等:pivot 左仅有一个元素 1315 | else return 0; 1316 | } 1317 | 1318 | // 判断 target 是否在区间中 1319 | int isIn(int* nums, int low, int high, int target) { 1320 | if (target >= nums[low] && target <= nums[high]) return 1; 1321 | else return 0; 1322 | } 1323 | 1324 | // 二分查找 1325 | int biSearch(int* nums, int low, int high, int target) { 1326 | int pivot = (low + high) / 2; 1327 | while (low <= high) { 1328 | pivot = (low + high) / 2; 1329 | if (nums[pivot] == target) return pivot; 1330 | else if (target < nums[pivot]) high = pivot - 1; 1331 | else if (target > nums[pivot]) low = pivot + 1; 1332 | } 1333 | if (nums[low] == target) 1334 | return low; 1335 | else 1336 | return -1; 1337 | } 1338 | 1339 | int search(int* nums, int numSize, int target) { 1340 | int low = 0, high = numSize - 1; 1341 | while (low < high) { 1342 | int pivot = (low + high) / 2; 1343 | // 递归出口 1344 | if (nums[pivot] == target) return pivot; 1345 | int tIsIn = 0; 1346 | // 判断左边是否升序 1347 | int leftIsSeq = isSeq(nums, low, pivot - 1); 1348 | if (leftIsSeq) { 1349 | // 左边有序 1350 | tIsIn = isIn(nums, low, pivot - 1, target); 1351 | if (tIsIn) 1352 | return biSearch(nums, low, pivot - 1, target); 1353 | else 1354 | low = pivot + 1; 1355 | } else { 1356 | // 右边有序 1357 | tIsIn = isIn(nums, pivot + 1, high, target); 1358 | if (tIsIn) 1359 | return biSearch(nums, pivot + 1, high, target); 1360 | else 1361 | high = pivot - 1; 1362 | } 1363 | } 1364 | if (nums[low] == target) return low; 1365 | else return -1; 1366 | } 1367 | 1368 | /* 1369 | 下一个排列 https://zhuanlan.zhihu.com/p/333504418 1370 | */ 1371 | 1372 | class Solution { 1373 | public: 1374 | void nextPermutation(vector& nums) { 1375 | int i = nums.size() - 2; 1376 | while (i >= 0 && nums[i] >= nums[i + 1]) { 1377 | i--; 1378 | } 1379 | if (i >= 0) { 1380 | int j = nums.size() - 1; 1381 | while (j >= 0 && nums[i] >= nums[j]) { 1382 | j--; 1383 | } 1384 | swap(nums[i], nums[j]); 1385 | } 1386 | reverse(nums.begin() + i + 1, nums.end()); 1387 | } 1388 | }; 1389 | 1390 | 1391 | //重排链表 https://blog.csdn.net/qq_44630255/article/details/137616260 1392 | #include 1393 | #include 1394 | 1395 | struct ListNode { 1396 | int val; 1397 | ListNode* next; 1398 | ListNode(int x) : val(x), next(nullptr) {} 1399 | }; 1400 | 1401 | class Solution { 1402 | public: 1403 | void reorderList(ListNode* head) { 1404 | if (!head || !head->next) return; // 如果链表为空或只有一个节点,无需重排 1405 | 1406 | // 找到链表的中间节点 1407 | ListNode* slow = head; 1408 | ListNode* fast = head; 1409 | while (fast->next && fast->next->next) { 1410 | slow = slow->next; 1411 | fast = fast->next->next; 1412 | } 1413 | 1414 | // 反转后半部分链表 1415 | ListNode* pre = nullptr; 1416 | ListNode* cur = slow->next; 1417 | slow->next = nullptr; // 断开前后两部分链表 1418 | while (cur) { 1419 | ListNode* next = cur->next; 1420 | cur->next = pre; 1421 | pre = cur; 1422 | cur = next; 1423 | } 1424 | 1425 | // 合并两部分链表 1426 | ListNode* p1 = head; 1427 | ListNode* p2 = pre; 1428 | while (p2) { 1429 | ListNode* tmp1 = p1->next; 1430 | ListNode* tmp2 = p2->next; 1431 | p1->next = p2; 1432 | p2->next = tmp1; 1433 | p1 = tmp1; 1434 | p2 = tmp2; 1435 | } 1436 | } 1437 | }; 1438 | 1439 | // 创建链表 1440 | ListNode* createList(std::vector& nums) { 1441 | if (nums.empty()) return nullptr; 1442 | ListNode* head = new ListNode(nums[0]); 1443 | ListNode* cur = head; 1444 | for (int i = 1; i < nums.size(); ++i) { 1445 | cur->next = new ListNode(nums[i]); 1446 | cur = cur->next; 1447 | } 1448 | return head; 1449 | } 1450 | 1451 | // 输出链表 1452 | void printList(ListNode* head) { 1453 | ListNode* cur = head; 1454 | while (cur) { 1455 | std::cout << cur->val << " "; 1456 | cur = cur->next; 1457 | } 1458 | std::cout << std::endl; 1459 | } 1460 | 1461 | int main() 1462 | { 1463 | std::vector nums = {1, 2, 3, 4, 5}; 1464 | ListNode* head = createList(nums); 1465 | 1466 | // 重排链表 1467 | Solution solution; 1468 | solution.reorderList(head); 1469 | 1470 | // 输出重排后的链表 1471 | printList(head); 1472 | 1473 | return 0; 1474 | } 1475 | 1476 | //合并两个有序链表 1477 | public ListNode mergeTwoLists(ListNode list1, ListNode list2) { 1478 | ListNode res = new ListNode(); 1479 | ListNode tempNode = res; 1480 | while (list1 != null && list2 != null) { 1481 | if (list1.val <= list2.val) { 1482 | tempNode.next = list1; 1483 | list1 = list1.next; 1484 | } else { 1485 | tempNode.next = list2; 1486 | list2 = list2.next; 1487 | } 1488 | tempNode = tempNode.next; 1489 | } 1490 | if (list1 == null) { 1491 | tempNode.next = list2; 1492 | } 1493 | if (list2 == null) { 1494 | tempNode.next = list1; 1495 | } 1496 | return res.next; 1497 | } 1498 | 1499 | 1500 | 1501 | -------------------------------------------------------------------------------- /img/01cee96e3952501d.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/01cee96e3952501d.jpeg -------------------------------------------------------------------------------- /img/0bd4d451a44cf2c9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/0bd4d451a44cf2c9.jpeg -------------------------------------------------------------------------------- /img/14356223_weixin.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/14356223_weixin.jpeg -------------------------------------------------------------------------------- /img/2b7ece6a68414958.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/2b7ece6a68414958.jpeg -------------------------------------------------------------------------------- /img/3b3bd23d3712575a3164189ddea27f4cc99.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/3b3bd23d3712575a3164189ddea27f4cc99.jpeg -------------------------------------------------------------------------------- /img/3b3bd23d3712575a3164189ddea27f4cc99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/3b3bd23d3712575a3164189ddea27f4cc99.jpg -------------------------------------------------------------------------------- /img/3fd1dd8ab214413e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/3fd1dd8ab214413e.jpeg -------------------------------------------------------------------------------- /img/41a1eba8a26bcfe8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/41a1eba8a26bcfe8.jpeg -------------------------------------------------------------------------------- /img/4557ecb3b2c33c06.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/4557ecb3b2c33c06.jpeg -------------------------------------------------------------------------------- /img/7460961a4ad0d6ac.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/7460961a4ad0d6ac.jpeg -------------------------------------------------------------------------------- /img/7644ae4924763d2c.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/7644ae4924763d2c.jpeg -------------------------------------------------------------------------------- /img/7f5e6afd9f878904.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/7f5e6afd9f878904.jpeg -------------------------------------------------------------------------------- /img/91829576d53d9be3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/91829576d53d9be3.jpeg -------------------------------------------------------------------------------- /img/95ddda34d5a9c517.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/95ddda34d5a9c517.jpeg -------------------------------------------------------------------------------- /img/96468d6b103c7154.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/96468d6b103c7154.jpeg -------------------------------------------------------------------------------- /img/9A1AA1E5ED9CE5F11CA0781DEC2D5AEC.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/9A1AA1E5ED9CE5F11CA0781DEC2D5AEC.jpeg -------------------------------------------------------------------------------- /img/a4390cc43dfea7e10ab21a302938810617f.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/a4390cc43dfea7e10ab21a302938810617f.jpeg -------------------------------------------------------------------------------- /img/a4390cc43dfea7e10ab21a302938810617f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/a4390cc43dfea7e10ab21a302938810617f.jpg -------------------------------------------------------------------------------- /img/a5a82174c29d6fc7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/a5a82174c29d6fc7.jpeg -------------------------------------------------------------------------------- /img/a78a74bbfb6ba1e7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/a78a74bbfb6ba1e7.jpeg -------------------------------------------------------------------------------- /img/ac35111fff96352e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/ac35111fff96352e.jpeg -------------------------------------------------------------------------------- /img/bd9dfb9a9e0078a9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/bd9dfb9a9e0078a9.jpeg -------------------------------------------------------------------------------- /img/e17fb44cf6f5adc4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/e17fb44cf6f5adc4.jpeg -------------------------------------------------------------------------------- /img/e5f56c7dae8196cf.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/e5f56c7dae8196cf.jpeg -------------------------------------------------------------------------------- /img/f54e1d56dbf27a1e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/f54e1d56dbf27a1e.jpeg -------------------------------------------------------------------------------- /img/fc9914ad170360c4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/fc9914ad170360c4.jpeg -------------------------------------------------------------------------------- /img/单机模块架构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/img/单机模块架构.png -------------------------------------------------------------------------------- /source insight configure/GLOBAL.CF3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/source insight configure/GLOBAL.CF3 -------------------------------------------------------------------------------- /source insight configure/README.md: -------------------------------------------------------------------------------- 1 | 加载本目录中已经配置好的GLOBAL配置文件方法步骤如下: 2 | 1. 点击Optiongs,选择Load Configuration,进入Load Configuration界面 3 | 2. 点击load,选择本目录中的GLOBAL配置文件即可 4 | -------------------------------------------------------------------------------- /source insight configure/使用说明.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/source insight configure/使用说明.txt -------------------------------------------------------------------------------- /其他/README.md: -------------------------------------------------------------------------------- 1 | ## 其他分享 2 | * [阿里巴巴分布式消息队列中间件rocketmq-3.4.6源码分析](https://github.com/y123456yz/reading-and-annotate-rocketmq-3.4.6) 3 | * [服务器时延统计工具tcprstat,增加时延阈值统计,记录超过阈值的包个数,并把数据包时间戳记录到日志文件,这样可以根据时间戳快速定位到抓包文件中对应的包,从而可以快速定位到大时延包,避免了人肉搜索抓包文件,提高问题排查效率](https://github.com/y123456yz/tcprstat) 4 | * [linux内核网络协议栈源码阅读分析注释](https://github.com/y123456yz/Reading-and-comprehense-linux-Kernel-network-protocol-stack) 5 | * [docker-17.05.0源码中文注释详细分析](https://github.com/y123456yz/reading-and-annotate-docker-17.05.0) 6 | * [lxc源码详细注释分析](https://github.com/y123456yz/reading-and-annotate-lxc-1.0.9) 7 | * [source insight代码中文注释乱码、背景色等配置调整](https://github.com/y123456yz/middleware_development_learning/tree/master/source%20insight%20configure) 8 | * [linux内核协议栈TCP time_wait原理、优化、副作用](https://my.oschina.net/u/4087916/blog/3051356) 9 | * [为何需要对开源社区版本mongodb做二次开发,需要做哪些二次开发](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5-mongodb%E6%95%B0%E6%8D%AE%E5%BA%93/development_mongodb.md) 10 | * [在线引流工具Tcpcopy原理、环境搭建、使用、采坑](https://my.oschina.net/u/4087916/blog/3064268) 11 | -------------------------------------------------------------------------------- /其他/linux内核协议栈TCP time_wait原理、优化、副作用.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/其他/linux内核协议栈TCP time_wait原理、优化、副作用.docx -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/README.md: -------------------------------------------------------------------------------- 1 | 分布式缓存相关学习、二次开发、性能优化 2 | 3 | ## 第一阶段:手把手教你做分布式缓存开发、性能稳定性优化: 4 | * [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network) 5 | * [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo-文档说明](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network.md) 6 | * [阻塞、非阻塞程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo) 7 | * [阻塞、非阻塞、同步、异步、epoll说明](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network.md) 8 | * [借助redis的配置解析模块,快速实现一个配置文件解析程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/redis%E6%BA%90%E7%A0%81%E5%88%86%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90/%E5%9F%BA%E4%BA%8Eredis%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%A3%E6%9E%90%E7%A8%8B%E5%BA%8F%EF%BC%8C%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%A3%E6%9E%90%E7%A8%8B%E5%BA%8Fdemo) 9 | * [借助redis的日志模块,快速实现一个同步日志写、异步日志写程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/redis%E6%BA%90%E7%A0%81%E5%88%86%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90/%E5%9F%BA%E4%BA%8Eredis%E6%97%A5%E5%BF%97%E4%BB%A3%E7%A0%81%EF%BC%8C%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0%E6%97%A5%E5%BF%97%E5%90%8C%E6%AD%A5%E5%86%99%E5%92%8C%E5%BC%82%E6%AD%A5%E5%86%99%EF%BC%8C%E4%BD%93%E9%AA%8C%E5%90%8C%E6%AD%A5%E5%86%99%E5%92%8C%E5%BC%82%E6%AD%A5%E5%86%99%E5%8C%BA%E5%88%AB) 10 | * [借助redis的StackTrace功能,快速StackTrace程序demo, 记录程序异常时的函数栈信息,便于快速定位bug](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/redis%E6%BA%90%E7%A0%81%E5%88%86%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90/%E5%9F%BA%E4%BA%8Eredis%E4%BF%A1%E5%8F%B7%E5%92%8Cbacktrace%E6%9C%BA%E5%88%B6%EF%BC%8C%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0backtrace%E4%BB%A3%E7%A0%81%EF%BC%8C%E8%AE%B0%E5%BD%95%E7%A8%8B%E5%BA%8Fbug%E5%BC%82%E5%B8%B8%E9%80%80%E5%87%BA%E7%9A%84%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E6%A0%88%EF%BC%8C%E4%BE%BF%E4%BA%8E%E5%BF%AB%E9%80%9F%E5%AE%9A%E4%BD%8Dbug) 11 | * [借助redis的bio模块,快速实现线程池组demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/redis%E6%BA%90%E7%A0%81%E5%88%86%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90/%E5%9F%BA%E4%BA%8Eredis%E7%9A%84bio%E4%BB%A3%E7%A0%81%EF%BC%8C%E5%BF%AB%E9%80%9F%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%BA%BF%E7%A8%8B%E6%B1%A0demo) 12 | 13 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | block_client 4 | block_server 5 | noblock_client 6 | noblock_server 7 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/asyn_network.md: -------------------------------------------------------------------------------- 1 |      本文档配合主要对如下demo进行配合说明: [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network) 2 | 3 | _**0\. 手把手教你做中间件、高性能服务器、分布式存储技术交流群**_ 4 | 5 | 手把手教你做中间件、高性能服务器、分布式存储等(redis、memcache、nginx、大容量redis pika、rocksdb、mongodb、wiredtiger存储引擎、高性能代理中间件),git地址如下: 6 | 7 | git地址:[https://github.com/y123456yz/middleware\_development\_learning](https://github.com/y123456yz/middleware_development_learning) 8 | 9 | **1\. epoll出现背景** 10 | 11 |     epoll 是 linux 内核为处理大批量文件描述符(网络文件描述符主要是socket返回的套接字fd和accept处理的新连接fd)而作了改进的 poll,是 linux 下多路复用 io接口 select/poll 的增强版本。在 linux 的网络编程中,很长时间都在使用 select 来做事件触发。在 2.6 内核中,有一种替换它的机制,就是 epoll。epoll替换select和poll的主要原因如下: 12 | 13 | 1. select最多处理1024(内核代码fd_setsize宏定义)个连接。 14 | 2. select和poll采用轮训方式检测内核网络事件,算法事件复杂度为o(n),n为连接数,效率低下。 15 | 16 |     epoll克服了select和poll的缺点,采用回调方式来检测就绪事件,算法时间复杂度o(1),相比于select和poll,效率得到了很大的提升。 17 | 18 |     借助epoll的事件回调通知机制,工作线程可以在没有网络事件通知的时候做其他工作,这样可以最大限度的利用系统cpu资源,服务端不用再阻塞等待客户端网络事件,而是依赖epoll事件通知机制来避免同步等待。 19 | 20 | # **2\. epoll系统调用接口** 21 | 22 | ## **2.1 epoll_create函数** 23 | 24 | 函数声明:int epoll_create(int size) 25 | 26 |     该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。在linux-2.4.32内核中根据size大小初始化哈希表的大小,在linux2.6.10内核中该参数无用,使用红黑树管理所有的文件描述符,而不是hash。 27 | 28 |     重点:该函数返回的fd将作为其他epoll系统接口的参数。 29 | 30 | 2.2 **epoll_ctl函数** 31 | 32 | 函数声明:int epoll\_ctl(int epfd, int op, int fd, struct epoll\_event event) 33 | 34 | 该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。epoll\_ctl的参数说明如下: 35 | 36 |      epfd: epoll事件集文件描述符,也就是epoll_create返回值 37 | 38 |      op: 对fd描述符进行的操作类型,可以是添加注册事件、修改事件、删除事件,分别对应宏定义: **epoll\_ctl\_add****、epoll\_ctl\_mod、epoll\_ctl\_del。** 39 | 40 |      fd: 操作的文件描述符。 41 | 42 |      event: 需要操作的fd对应的epoll_event事件对象,对象数据来源为fd和op。 43 | 44 | ## **2.3 epoll_wait函数** 45 | 46 | 函数声明:int epoll\_wait(int epfd, struct epoll\_event events, int maxevents, int timeout),该函数用于轮询i/o事件的发生,改函数参数说明如下: 47 | 48 | epfd: epoll事件集文件描述符,也就是epoll_create返回值。 49 | 50 |     Events:该epoll时间集上的所有epoll\_event信息,每个fd对应的epoll\_event都存入到该数组中,数组每个成员对应一个fd描述符。 51 | 52 | Maxevents: 也就是events数组长度。 53 | 54 | Timeout: 超时时间,如果在这个超时时间内内核没有I/O网络事件通知,则会超时返回,如果在超时时间内有时间通知,则立马返回 55 | 56 | # **3\. epoll I/O对路复用主要代码实现流程** 57 | 58 | 代码实现主要由以下几个阶段组成: 59 | 60 | 1. 创建套接字获取sd,bind然后listen该套接字sd。 61 | 2. 把步骤1中的sd添加到epoll事件集中,epoll只关注sd套接字上的新连接请求,新连接对应的事件为读事件AE_READABLE (EPOLLIN)。并设置新连接事件到来时对应的回调函数为MainAcceptTcpHandler。 62 | 3. 在新连接回调函数MainAcceptTcpHandler中,获取新连接,并返回该新连接对应的文件描述符fd,同时把新连接的fd添加到epoll事件集中,该fd开始关注epoll读事件,如果检测到该fd对应的读事件(客户端发送的数据服务端收到后,内核会触发该fd对应的epoll读事件),则触发读数据回调函数MainReadFromClient。多个连接,每个连接有各自的文件描述符事件结构(该结构记录了各自的私有数据、读写回调函数等),并且每个连接fd有各自的已就绪事件结构。不同连接有不同的结构信息,最终借助epoll实现I/O多路复用。 63 | 4. 进入aeMain事件循环函数中,循环检测步骤2中的新连接事件和步骤3中的数据读事件。如果有对应的epoll事件,则触发epoll_wait返回,并执行对应事件的回调。 64 | 65 | # **4\. epoll I/O多路复用主要数据结构及函数说明** 66 | 67 | ## **4.1 主要数据结构** 68 | 69 | struct aeEventLoop结构用于记录整个epoll事件的各种信息,主要成员如下: 70 | 71 | typedef struct aeEventLoop { 72 | 73 |     // 目前已注册的最大描述符 74 | 75 |     int maxfd;   /* highest file descriptor currently registered */ 76 | 77 |     // 目前已追踪的最大描述符 78 | 79 |     int setsize; /* max number of file descriptors tracked */ 80 | 81 |     // 用于生成时间事件 id 82 | 83 |     long long timeEventNextId; 84 | 85 |     // 最后一次执行时间事件的时间 86 | 87 |     time_t lastTime;     /* Used to detect system clock skew */ 88 | 89 |     // 已注册的文件事件,每个fd对应一个该结构,events实际上是一个数组 90 | 91 |     aeFileEvent \*events; /\* Registered events */ 92 | 93 |     // 已就绪的文件事件,参考aeApiPoll,数组结构 94 | 95 |     aeFiredEvent \*fired; /\* Fired events */ 96 | 97 |     // 时间事件,所有的定时器时间都添加到该链表中 98 | 99 | aeTimeEvent *timeEventHead; 100 | 101 | } 102 | 103 |       该结构主要由文件描述符事件(即网络I/O事件,包括socket/bind/listen对应的sd文件描述符和accept获取到的新连接文件描述符)和定时器事件组成,其中文件事件主要由events、fired、maxfd、setsize,其中events和fired为数组类型,数组大小为setsize。 104 | 105 |       events数组: 成员类型为aeFileEvent,每个成员代表一个注册的文件事件,文件描述符与数组游标对应,例如如果fd=10,则该fd对应的文件事件为event数组的第十个成员Events\[10\]。 106 | 107 |       fired数组: 成员类型为aeFiredEvent,每个成员代表一个就绪的文件事件,文件描述符和数组游标对应,例如如果fd=10,则该fd对应的已就绪的文件事件为fired数组的第十个成员fired \[10\]。 108 | 109 |     Setsize: 为events文件事件数组和fired就绪事件数组的长度,初始值为REDIS\_MAX\_CLIENTS + REDIS\_EVENTLOOP\_FDSET_INCR。aeCreateEventLoop中提前分配好events和fired数组空间。 110 | 111 |       maxfd: 为所有文件描述符中最大的文件描述符,该描述符的作用是调整setsize大小来扩大events和fired数组长度,从而保证存储所有的事件,不会出现数组越界。 112 | 113 | **何时扩大events和fireds数组长度?** 114 | 115 |       例如redis最开始设置的默认最大连接数为REDIS\_MAX\_CLIENTS,如果程序运行一段时间后,我们想调大最大连接数,这时候就需要调整数组长度。 116 | 117 | **为什么events和fireds数组长度需要加REDIS\_EVENTLOOP\_FDSET_INCR?** 118 | 119 |       因为redis程序中除了网络相关accept新连接的描述符外,程序中也会有普通文件描述符,例如套接字socket描述符、日志文件、rdb文件、aof文件、syslog等文件描述符,确保events和fireds数组长度大于配置的最大连接数,从而避免数组越界。 120 | 121 | ## **4.2 主要函数实现** 122 | 123 | 主要函数功能请参考以下几个函数: 124 | 125 | aeCreateFileEvent 126 | 127 | aeDeleteFileEvent 128 | 129 | aeProcessEvents 130 | 131 | aeApiAddEvent 132 | 133 | aeApiDelEvent 134 | 135 | aeApiPoll 136 | 137 | # **5\. 定时器实现原理** 138 | 139 | ## **5.1 定时器主要代码流程** 140 | 141 |       Redis的定时器实际上是借助epoll\_wait实现的,epoll\_wait的超时时间参数timeout是定时器链表中距离当前时间最少的时间差,例如现在是8点1分,我们有一个定时器需要8点1分5秒执行,那么这里epoll_wait的timeout参数就会设置为5s。 142 | 143 |      epoll\_wait函数默认等待网络I/O事件,如果8点1分到8点1分5秒这段时间内没有网络I/O事件到来,那么到了8点1分5秒的时候,epoll\_wait就会超时返回。Epoll_wait返回后就会在aeMain循环体中遍历定时器链表,获取到定时器到达时间比当前时间少的定时器,运行该定时器的对应回调函数。 144 | 145 |       如果在8点1分3秒过程中有网络事件到达,epoll\_wait会在3秒钟返回,返回后处理完对应的网络事件回调函数,然后继续aeMain循环体中遍历定时器链表,获取离当前时间最近的定时器时间为5-3=2秒,也就是还有2秒该定时器才会到期,于是在下一个epoll\_wait中,设置timeout超时时间为2秒,以此循环。 146 | 147 | ## **5.2 两种不同的定时器(周期性定时器、一次性定时器)** 148 | 149 |     周期性定时器: 指的是定时器到期对应的回调函数执行后,需要重新设置该定时器的超时时间,以备下一个周期继续执行。 150 | 151 |     一次性定时器: 本次定时时间到执行完对应的回调函数后,把该定时器从定时器链表删除。 152 | 153 |     两种定时器代码主要代码流程区别如下: 154 | 155 |       ![](https://oscimg.oschina.net/oscnet/7e91166c954a090301d1d61e96f66e40d4f.jpg) 156 | 157 | ## **5.3 主要数据结构及函数实现** 158 | 159 | 主要数据结构如下: 160 | 161 | typedef struct aeEventLoop { 162 | 163 |      // 时间事件,所有的定时器时间都添加到该链表中 164 | 165 |    aeTimeEvent *timeEventHead; 166 | 167 | } 168 | 169 | ![](https://oscimg.oschina.net/oscnet/176f7fbee6b195fd7b513345622a331c410.jpg) 170 | 171 | 主要函数实现参考: 172 | 173 | aeCreateTimeEvent 174 | 175 | aeDeleteTimeEvent 176 | 177 | aeSearchNearestTimer 178 | 179 | processTimeEvents 180 | 181 | # **6\. 常用套接字选项设置** 182 | 183 | 套接字选项可以通过setsockopt函数进行设置,函数声明如下: 184 | 185 |  int setsockopt( int socket, int level, int option\_name, const void *option\_value, size\_t option\_len); 186 | 187 | setsockopt参数说明如下: 188 | 189 |     socket: 可以是bind/listen对应的sd,也可以是accept获取到的新连接fd。 190 | 191 |     level: 参数level是被设置的选项的级别,套接字级别对应 SOL\_SOCKET,tcp网络设置级别对应SOL\_SOCKET. 192 | 193 |     option_name: 选项类型。 194 | 195 |     optval:[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88),指向存放选项待设置的新值的[缓冲区](https://baike.baidu.com/item/%E7%BC%93%E5%86%B2%E5%8C%BA)。 196 | 197 |     optlen:optval缓冲区长度。 198 | 199 | ## **6.1 SOL_SOCKET级别套接字选项** 200 | 201 |     Level级别为SOL\_SOCKET的option\_name常用类型如下(说明:网络I/O的文件描述符句柄有两类,一类是针对socket()/bind/listen对应的sd,一类是新连接到来后accept返回的新的连接句柄fd): 202 | 203 |     SO_REUSEADDR: 复用地址,针对socket()/bind/listen对应的sd,避免服务端进程退出再重启后出现error:98,Address already in use。 204 | 205 |     SO_RECVBUF: 设置连接fd对应的内核网络协议栈接收缓冲区buf大小,每个连接都会有一个recv buf来接收客户端发送的数据。实际应用中,使用默认值就可以,但如果连接过多,负载过大,内存可能吃不消,这时候可以调小该值。 206 | 207 |     SO_SNDBUF:设置连接fd对应的内核网络协议栈发送缓冲区buf大小,每个连接都会有一个send buf来缓存需要发送的连接数据。实际应用中,使用默认值就可以,但如果连接过多,负载过大,内存可能吃不消,这时候可以调小该值。 208 | 209 |     SO_KEEPALIVE:针对socket()/bind/listen对应的sd,设置TCP的keepalive机制,由内核网络协议栈实现连接保活。通过该设置可以判断对端异常断电、网络不通的连接问题(如网线松动)。因为客户端异常断电或者网线松动,服务端是不会有epoll异常事件通知的。如果没有设计应用层保活超时报文,则可以依赖协议栈keepalive来检测连接是否异常。 210 | 211 |     SO_LINGER:决定关闭连接fd的方式,因为关闭连接的时候,该fd对应的内核协议栈buf可能数据还没有发送出去,如果强制立即关闭可能会出现丢数据的情况。可以根据传入optval参数决定立即关闭连接(可能丢数据),还是等待数据发送完毕后关闭释放连接或者超时关闭连接。 212 | 213 |     SO_RCVTIMEO:针对sd和新连接fd,接收数据超时时间,这个针对阻塞读方式。如果read超过这么多时间还没有获取到内核协议栈数据,则超时返回。 214 | 215 |     SO_SNDTIMEO:针对sd和新连接fd,发送数据超时时间,这个针对阻塞写方式。如果write超过这么多时间还没有把数据成功写入到内核协议栈,则超时返回。 216 | 217 | ## **6.2 IPPROTO_TCP级别套接字选项** 218 | 219 | Level级别为IPPROTO\_TCP的option\_name常用类型如下: 220 | 221 |     TCP_NODELAY:针对连接fd,是否启用naggle算法,一般禁用,这样可以保证服务端快速回包,降低时延。 222 | 223 | _**7\. 阻塞、非阻塞**_ 224 | 225 | 服务端和网络I/O相关相关的几个系统函数主要是:accept()接收客户端连接、read()从内核网络协议栈读取客户端发送来的数据、write()写数据到内核协议栈buf,然后由内核调度发送出去。请参考阻塞demo和非阻塞demo。 226 | 227 | ## **7.1阻塞及其demo程序验证说明** 228 | 229 |       网上相关说明很多,但是都比较抽象。这里以服务端调用accept为例说明:accept()函数会进行系统调用,从应用层走到内核空间,最终调用内核函数SYSCALL_DEFINE3(),如果accept对应的sd(socket/bind/listen对应的文件描述符)是阻塞调用(如果不进行设置,默认就是阻塞调用),SYSCALL_DEFINE3()对应的函数会判断内核是否收到客户端新连接,如果没有则一直等待,直到有新连接到来或者超时才会返回。 230 | 231 |       从上面的描述可以看出,如果是阻塞方式,accept()所在的现场会一直等待,整个线程不能做其他事情,这就是阻塞。Accept()阻塞超时时间可以通过上面的SO_RCVTIMEO设置。 232 | 233 |       Read()和write()阻塞操作过程和acept()类似,只有在接收到数据和写数据到协议栈成功才会返回,或者超时返回,超时时间分别可以通过SO\_RCVTIMEO和SO\_SNDTIMEO设置。 234 | 235 |       下面以如下demo为例,来体验阻塞和非阻塞,以下是阻塞操作例子,分别对应服务端和客户端代码: 236 | 237 |       [服务端阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_server.c) 238 | 239 |       [对应客户端程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_client.c) 240 | 241 |         ![](https://oscimg.oschina.net/oscnet/e7274057bb6512e4aa82e47b2cf73e79579.jpg) 242 | 243 |         ![](https://oscimg.oschina.net/oscnet/924ae78a9ae7bc5f5f8cffdbaafce6f4a1f.jpg) 244 | 245 |       从上面的程序,服务端创建套接字后,绑定地址后开始监听,然后阻塞accept()等待客户端连接。如果客户端有连接,则开始阻塞等待read()读客户端发送来的数据,读到数据后打印返回,程序执行结束。 246 | 247 |       客户端程序创建好套接字,设置好需要连接的服务器Ip和端口,先延时10秒钟才开始连接服务器,连接成功后再次延时10秒,然后发送”block test message”字符串给服务端。 248 | 249 |       通过CRT开两个窗口,同时启动服务端和客户端程序,服务端打印信息如下: 250 | 251 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# gcc block\_server.c -o block\_server 252 | 253 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# 254 | 255 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# ./block_server 1234 256 | 257 |         begin accept //在这里阻塞等待客户端连接 258 | 259 |         accept successful from client 260 | 261 |         begin recv message //这里阻塞等待客户端发送数据过来 262 | 263 |         recv message:block test message from client 264 | 265 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# 266 | 267 |         客户端打印信息如下: 268 | 269 |          \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# gcc block\_client.c -o block\_client 270 | 271 |          \[root@localhost block\_noblock\_demo\]# ./block_client 127.0.0.1 1234 272 | 273 |          begin connect  //begin和end见有10s延时 274 | 275 |          end connect 276 | 277 |          begin send message //begin和end间有10s延时 278 | 279 |          end send message:block test message to server 280 | 281 |       从运行服务端程序和客户端程序的打印可以看出,如果客户端不发起连接,服务端accept()函数会阻塞等待,知道有新连接到来才会返回。同时启用服务端和客户端程序,服务端accept()函数10s才会返回,因为客户端我故意做了10s延时。Read()阻塞读函数过程和accept()类似。 282 | 283 |        Write()阻塞验证过程,服务端设置好该链接对应的内核网络协议栈发送缓存区大小,然后传递很大的一个数据给write函数,期望把这个大数据通过write函数写入到内核协议栈发送缓存区。如果内核协议栈缓存区可用buf空间比需要write的数据大,则数据通过write函数拷贝到内核发送缓存区后会立马返回。为了验证write的阻塞过程,我这里故意让客户端不去读数据,这样服务端write的数据就会缓冲到协议栈发送缓冲区,如果缓冲区空间没那么大。Write就会一直等待内核调度把发送缓冲区数据通过网卡发送出去,这样就会腾出空间,继续拷贝用户态write需要写的数据。由于这里我故意让客户端不读数据,该链接对应的发送缓冲区很快就会写满,由于我想要写的数据比这个buf缓冲区大很多,那么write函数就需要阻塞等待,直到把期望发送的数据全部写入到该发送缓存区才会返回,或者超过系统默认的write超时时间才会返回。 284 | 285 | ## **7.2 非阻塞及其demo程序验证说明** 286 | 287 |     非阻塞通过系统调用fcntl设置,函数代码如下: 288 | 289 |       ![](https://oscimg.oschina.net/oscnet/5adee1fb52632e285ebe6b9942cc1b8dace.jpg) 290 | 291 |     函数中的fd文件描述符可以是socket/bind/listen对应的sd,也可以是accept获取到的新连接fd。非阻塞程序demo github地址如下: 292 | 293 |     [服务端非阻塞程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_server.c) 294 | 295 |     [对应客户端非阻塞程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_client.c) 296 | 297 |     编译程序,先启动服务端程序,然后启动客户端程序,验证方法如下: 298 | 299 |        ![](https://oscimg.oschina.net/oscnet/baa69e68f2713a5d967fddfa2d605712302.jpg) 300 | 301 |     对应客户端: 302 | 303 |       ![](https://oscimg.oschina.net/oscnet/a149a4dd68010a8c05c9ae94c3fb97fee65.jpg) 304 | 305 |       客户端启动后,延迟10秒向服务端发起连接,连接建立成功后,延迟10秒向服务端发送数据。服务端启动后,设置sd为非阻塞,开始accept等待接收客户端连接,如果accept系统调用没有获取到客户端连接,则延时1秒钟,然后继续accept。由于客户端启动后要延迟10s钟才发起连接,因此accept会有十次accept return打印。read过程和accept类似,可以查看demo代码。 306 | 307 |       我们知道write操作是把用户态向要发送的数据拷贝到内核态连接对应的send buf缓冲区,如果是阻塞方式,如果内核缓存区空间不够,则write会阻塞等待。但是如果我们把新连接的fd设置为非阻塞,及时内核发送缓冲区空间不够,write也会立马返回,并返回本次写入到内核空间的数据量,不会阻塞等待。可以通过运行demo自己来体验这个过程。 308 | 309 |      大家应该注意到,则非阻塞操作服务端demo中,accept(),read()如果没有返回我们想要的连接或者数据,demo中做了sleep延时,为什么这里要做点延时呢? 310 | 311 |       原因是如果不做延时,这里会不停的进行accept read系统调用,系统调用过程是个非常消耗性能的过程,会造成CPU的大量浪费。假设我们不加sleep延时,通过top可以查看到如下现象: 312 | 313 |       ![](https://oscimg.oschina.net/oscnet/c28a21ae4e158326884a1e66ce309ad9543.jpg) 314 | 315 | **8\. 同步、异步** 316 | 317 |     同步和异步是比较抽象的概念,还是用程序demo来说明。 318 | 319 | ## **8.1 同步** 320 | 321 |     上面的[服务端阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_server.c)和[服务端非阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_server.c)实际上都是同步调用的过程。这两个demo都是单线程的,以accept()调用为例,不管是阻塞操作还是非阻塞操作,由于服务端不知道客户端合适发起连接,因此只能阻塞等待,或者非阻塞轮训查询。不管是阻塞等待还是轮训查询,效率都非常低下,整个线程不能做其他工作,CPU完全利用不起来。 322 | 323 | ## **8.2 异步** 324 | 325 |        [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network),这个demo是异步操作。还是以该demo的accept为例说明,从这个demo可以看出,sd设置为非阻塞,借助epoll机制,当有新的连接事件到来后,触发epoll_wait返回,并返回所有的文件描述符对应的读写事件,这样就触发执行对应的新连接回调函数MainAcceptTcpHandler。借助epoll事件通知机制,就避免了前面两个demo的阻塞等待过程和轮训查询过程,整个accept()操作由事件触发,不必轮训等待。本异步网络框架demo也是单线程,就不存在前面两个demo只能做accept这一件事,如果没有accept事件到来,本异步网络框架demo线程还可以处理其他已有连接的读写事件,这样线程CPU资源也就充分利用起来了。 326 | 327 |       打个形象的比喻,假设我们每年单位都有福利体检,体检后一到两周出体检结果,想要获取体检结果有两种方式。第一种方式: 体检结束一周后,你就坐在医院一直等,直到体检结果出来,整个过程你是无法正常去单位上班的(这就相当于前面的服务端阻塞demo方式)。第二种方式:你每天都跑去体检医院询问,我的体检结果出了吗,如果没有,第二天有去体检医院,以此重复,直到有一天你去体检医院拿到体检结果。在你每天去医院询问是否已经出体检结果的过程中,你是不能正常上班的(这种方式类似于前面的服务端非阻塞demo方式)。第三种方式:你每天正常上班,等体检医院打电话通知你拿体检结果,你再去拿,电话通知你拿体检结果的过程就相当于异步事件通知,这样你就可以正常上班了。第一、二种方式就是同步操作,第三种方式就是异步操作。 328 | 329 | **    总结: 同步和异步的区别就是异步操作借助epoll的事件通知机制,从而可以充分利用CPU资源。** 330 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/block_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void error_handling(char *message); 9 | 10 | int main(int argc,char *argv[]){ 11 | int sd; 12 | struct sockaddr_in serv_addr; 13 | int str_len; 14 | 15 | char message[]="block test message"; 16 | if(argc != 3){ 17 | printf("Usage : %s \n",argv[0]); 18 | exit(1); 19 | } 20 | 21 | sd = socket(PF_INET,SOCK_STREAM,0); 22 | if(sd == -1){ 23 | error_handling("socket() error"); 24 | } 25 | 26 | memset(&serv_addr,0,sizeof(serv_addr)); 27 | serv_addr.sin_family = AF_INET; 28 | serv_addr.sin_addr.s_addr = inet_addr(argv[1]); 29 | serv_addr.sin_port = htons(atoi(argv[2])); 30 | 31 | sleep(10); 32 | printf("begin connect\r\n"); 33 | if(connect(sd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) == -1){ 34 | error_handling("connect() error\r\n"); 35 | } 36 | printf("end connect\r\n\r\n"); 37 | 38 | sleep(10); 39 | printf("begin send message\r\n"); 40 | write(sd, message, sizeof(message)); 41 | printf("end send message:%s to server\r\n", message); 42 | 43 | sleep(10); 44 | close(sd); 45 | return 0; 46 | } 47 | 48 | void error_handling(char *message){ 49 | fputs(message,stderr); 50 | fputs("\n",stderr); 51 | exit(1); 52 | } 53 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/block_server.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/block_server.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/epoll 阻塞 非阻塞 同步 异步.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/epoll 阻塞 非阻塞 同步 异步.docx -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/noblock_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void error_handling(char *message); 9 | 10 | int main(int argc,char *argv[]){ 11 | int sd; 12 | struct sockaddr_in serv_addr; 13 | int str_len; 14 | 15 | char message[]="block test message"; 16 | if(argc != 3){ 17 | printf("Usage : %s \n",argv[0]); 18 | exit(1); 19 | } 20 | 21 | sd = socket(PF_INET,SOCK_STREAM,0); 22 | if(sd == -1){ 23 | error_handling("socket() error"); 24 | } 25 | 26 | memset(&serv_addr,0,sizeof(serv_addr)); 27 | serv_addr.sin_family = AF_INET; 28 | serv_addr.sin_addr.s_addr = inet_addr(argv[1]); 29 | serv_addr.sin_port = htons(atoi(argv[2])); 30 | 31 | sleep(10); 32 | printf("begin connect\r\n"); 33 | if(connect(sd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) == -1){ 34 | error_handling("connect() error\r\n"); 35 | } 36 | printf("end connect\r\n\r\n"); 37 | 38 | 39 | sleep(10); 40 | printf("begin send message\r\n"); 41 | write(sd, message, sizeof(message)); 42 | printf("end send message:%s to server\r\n", message); 43 | 44 | sleep(10); 45 | close(sd); 46 | return 0; 47 | } 48 | 49 | void error_handling(char *message){ 50 | fputs(message,stderr); 51 | fputs("\n",stderr); 52 | exit(1); 53 | } 54 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/noblock_server.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/block_noblock_demo/noblock_server.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug/READE.md: -------------------------------------------------------------------------------- 1 | 获取程序异常的调用栈信息,从而快速定位程序bug 2 | 测试方法如下: 3 | =================================== 4 | 5 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# gcc backtrace.c -o backtrace 6 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# 7 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# ./backtrace 8 | I am function1 9 | I am function2 10 | receive signal:11 11 | Segmentation fault (core dumped) 12 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# 13 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# cat trace.log 14 | ./backtrace[0x4007ba] 15 | ./backtrace[0x400900] 16 | /lib64/libc.so.6(+0x35270)[0x7f0b3ecb7270] 17 | ./backtrace[0x400900] 18 | ./backtrace[0x40091e] 19 | ./backtrace[0x400935] 20 | /lib64/libc.so.6(__libc_start_main+0xf5)[0x7f0b3eca3c05] 21 | ./backtrace[0x400689] 22 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# 23 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# addr2line 0x4007ba -e backtrace -f 24 | logStackTrace 25 | ??:? 26 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# addr2line 0x400900 -e backtrace -f 27 | function2 28 | ??:? 29 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# 30 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# addr2line 0x40091e -e backtrace -f 31 | function1 32 | ??:? 33 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# 34 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# addr2line 0x400935 -e backtrace -f 35 | main 36 | ??:? 37 | [root@localhost 基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug]# 38 | 39 | 40 | 工作原理 41 | =================================== 42 | 程序异常将会收到SIGSEGV等异常信号,收到信号后会触发对应回调sigsegvHandler,在这里面借助backtrace、backtrace_symbols_fd来捕捉程序调用栈信息 43 | 44 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug/backtrace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug/backtrace -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug/backtrace.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug/backtrace.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis信号和backtrace机制,快速实现backtrace代码,记录程序bug异常退出的函数调用栈,便于快速定位bug/trace.log: -------------------------------------------------------------------------------- 1 | ./backtrace[0x4007ba] 2 | ./backtrace[0x400900] 3 | /lib64/libc.so.6(+0x35270)[0x7f0b3ecb7270] 4 | ./backtrace[0x400900] 5 | ./backtrace[0x40091e] 6 | ./backtrace[0x400935] 7 | /lib64/libc.so.6(__libc_start_main+0xf5)[0x7f0b3eca3c05] 8 | ./backtrace[0x400689] 9 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis日志代码,快速实现日志同步写和异步写,体验同步写和异步写区别/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | main 4 | log 5 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis日志代码,快速实现日志同步写和异步写,体验同步写和异步写区别/main.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis日志代码,快速实现日志同步写和异步写,体验同步写和异步写区别/main.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | threadPoll 4 | 5 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = threadPoll 2 | OBJS = adlist.o bio.o zmalloc.o threadPoll_Main.o 3 | DEPS = adlist.h bio.h zmalloc.h 4 | CPPFLAGS = -g 5 | CC = gcc 6 | LIBLINK = -lpthread 7 | 8 | $(TARGET) : $(OBJS) 9 | $(CC) -o $(TARGET) $(OBJS) $(CFLAGS) $(LIBLINK) 10 | 11 | adlist.o : adlist.c 12 | bio.o: bio.c 13 | zmalloc.o: zmalloc.c 14 | threadPoll_Main.o : threadPoll_Main.c $(DEPS) 15 | 16 | clean : 17 | rm $(OBJS) $(TARGET) 18 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/adlist.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/adlist.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/adlist.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/adlist.h -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/bio.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/bio.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/bio.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/bio.h -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/threadPoll_Main.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/threadPoll_Main.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/zmalloc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/zmalloc.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis的bio代码,快速实现一个线程池demo/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | void *zmalloc(size_t size); 69 | void *zcalloc(size_t size); 70 | void *zrealloc(void *ptr, size_t size); 71 | void zfree(void *ptr); 72 | char *zstrdup(const char *s); 73 | size_t zmalloc_used_memory(void); 74 | void zmalloc_enable_thread_safeness(void); 75 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 76 | float zmalloc_get_fragmentation_ratio(size_t rss); 77 | size_t zmalloc_get_rss(void); 78 | size_t zmalloc_get_private_dirty(void); 79 | void zlibc_free(void *ptr); 80 | 81 | #ifndef HAVE_MALLOC_SIZE 82 | size_t zmalloc_size(void *ptr); 83 | #endif 84 | 85 | #endif /* __ZMALLOC_H */ 86 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = config 2 | OBJS = zmalloc.o sds.o config.o main.o 3 | DEPS = sds.h config.h zmalloc.h 4 | CPPFLAGS = -g 5 | CC = gcc 6 | 7 | $(TARGET) : $(OBJS) 8 | $(CC) -o $(TARGET) $(OBJS) $(CFLAGS) 9 | 10 | sds.o: sds.c 11 | config.o : config.c 12 | zmalloc.o: zmalloc.c 13 | main.o : main.c $(DEPS) 14 | 15 | clean : 16 | rm $(OBJS) $(TARGET) 17 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/config: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/config -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/config.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/config.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __CONFIG_H 31 | #define __CONFIG_H 32 | 33 | #include "sds.h" 34 | 35 | struct redisServer { 36 | int maxidletime; 37 | int port; 38 | int aof_fsync; 39 | }; 40 | 41 | #define REDIS_CONFIGLINE_MAX 1024 42 | 43 | /* Append only defines */ 44 | #define AOF_FSYNC_NO 0 45 | #define AOF_FSYNC_ALWAYS 1 46 | #define AOF_FSYNC_EVERYSEC 2 47 | #define REDIS_DEFAULT_AOF_FSYNC AOF_FSYNC_EVERYSEC 48 | 49 | sds getAbsolutePath(char *filename); 50 | void printfRedisConfig(void); 51 | void loadServerConfig(char *filename); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/config.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/config.o -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "config.h" 24 | 25 | int main(int argc, char **argv) { 26 | if (argc < 2) { 27 | printf("error, please run with config file\r\n"); 28 | exit(1); 29 | } 30 | 31 | char *configfile = argv[1]; 32 | printf("config file name:%s\r\n", configfile); 33 | 34 | configfile = getAbsolutePath(configfile); 35 | if (configfile == NULL) { 36 | printf("error, getAbsolutePath\r\n"); 37 | exit(1); 38 | } 39 | 40 | loadServerConfig(configfile); 41 | printfRedisConfig(); 42 | } 43 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/main.o -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/sds.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/sds.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/sds.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/sds.h -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/sds.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/sds.o -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/test.conf: -------------------------------------------------------------------------------- 1 | #afdafdafdafda 2 | maxidletime 222 3 | port 333 4 | appendfsync everysec 5 | #afafdafda 6 | #afdadfagdazvdxc 7 | 8 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/zmalloc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/zmalloc.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | void *zmalloc(size_t size); 69 | void *zcalloc(size_t size); 70 | void *zrealloc(void *ptr, size_t size); 71 | void zfree(void *ptr); 72 | char *zstrdup(const char *s); 73 | size_t zmalloc_used_memory(void); 74 | void zmalloc_enable_thread_safeness(void); 75 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 76 | float zmalloc_get_fragmentation_ratio(size_t rss); 77 | size_t zmalloc_get_rss(void); 78 | size_t zmalloc_get_private_dirty(void); 79 | void zlibc_free(void *ptr); 80 | 81 | #ifndef HAVE_MALLOC_SIZE 82 | size_t zmalloc_size(void *ptr); 83 | #endif 84 | 85 | #endif /* __ZMALLOC_H */ 86 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/zmalloc.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/redis源码分模块分析/基于redis配置文件解析程序,快速实现一个配置文件解析程序demo/zmalloc.o -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/READE.md: -------------------------------------------------------------------------------- 1 | 借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,包括网络收发,epoll注册过程,定时器实现原理 2 | 3 | linux安装编译测试 4 | =================================== 5 | make编译出可执行文件net,然后运行 6 | 7 | 服务端(运行net可执行文件) 8 | [root@bogon asyn_network]# ./net 9 | 10 | 客户端用telnet链接,发送字符串 11 | [root@bogon asyn_network]# telnet 127.0.0.1 6379 12 | Trying 127.0.0.1... 13 | Connected to 127.0.0.1. 14 | Escape character is '^]'. 15 | hello boy 16 | hello boy 17 | 18 | 看看服务端打印: 19 | [root@bogon asyn_network]# ./net 20 | process Start begin 21 | MainTimerExpire 22 | client 127.0.0.1:60044 Connected 23 | MainTimerExpire 24 | recv from client 127.0.0.1:60044, data:hello boy -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | net 4 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = net 2 | OBJS = ae.o zmalloc.o ae_epoll.o anet.o main.o 3 | DEPS = ae.h anet.h zmalloc.h 4 | CPPFLAGS = -g 5 | CC = gcc 6 | 7 | $(TARGET) : $(OBJS) 8 | $(CC) -o $(TARGET) $(OBJS) $(CFLAGS) 9 | 10 | zmalloc.o: zmalloc.c 11 | ae.o : ae.c 12 | ae_epoll.o : ae_epoll.c 13 | anet.o : anet.c 14 | main.o : main.c $(DEPS) 15 | 16 | clean : 17 | rm $(OBJS) $(TARGET) 18 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/adlist.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/adlist.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/adlist.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/adlist.h -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/ae.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/ae.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/ae.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/ae.h -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/ae_epoll.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/ae_epoll.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/anet.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/anet.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/anet.h: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef ANET_H 32 | #define ANET_H 33 | 34 | #define ANET_OK 0 35 | #define ANET_ERR -1 36 | #define ANET_ERR_LEN 256 37 | 38 | /* Flags used with certain functions. */ 39 | #define ANET_NONE 0 40 | #define ANET_IP_ONLY (1<<0) 41 | 42 | #if defined(__sun) 43 | #define AF_LOCAL AF_UNIX 44 | #endif 45 | 46 | int anetTcpConnect(char *err, char *addr, int port); 47 | int anetTcpNonBlockConnect(char *err, char *addr, int port); 48 | int anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr); 49 | int anetUnixConnect(char *err, char *path); 50 | int anetUnixNonBlockConnect(char *err, char *path); 51 | int anetRead(int fd, char *buf, int count); 52 | int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len); 53 | int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len); 54 | int anetTcpServer(char *err, int port, char *bindaddr, int backlog); 55 | int anetTcp6Server(char *err, int port, char *bindaddr, int backlog); 56 | int anetUnixServer(char *err, char *path, mode_t perm, int backlog); 57 | int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port); 58 | int anetUnixAccept(char *err, int serversock); 59 | int anetWrite(int fd, char *buf, int count); 60 | int anetNonBlock(char *err, int fd); 61 | int anetEnableTcpNoDelay(char *err, int fd); 62 | int anetDisableTcpNoDelay(char *err, int fd); 63 | int anetTcpKeepAlive(char *err, int fd); 64 | int anetPeerToString(int fd, char *ip, size_t ip_len, int *port); 65 | int anetKeepAlive(char *err, int fd, int interval); 66 | int anetSockName(int fd, char *ip, size_t ip_len, int *port); 67 | int anetSetReuseAddr(char *err, int fd); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/bio.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/bio.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/bio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /* Exported API */ 31 | void bioInit(void); 32 | void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3); 33 | unsigned long long bioPendingJobsOfType(int type); 34 | void bioWaitPendingJobsLE(int type, unsigned long long num); 35 | time_t bioOlderJobOfType(int type); 36 | void bioKillThreads(void); 37 | 38 | /* Background job opcodes */ 39 | #define REDIS_BIO_CLOSE_FILE 0 /* Deferred close(2) syscall. */ 40 | #define REDIS_BIO_AOF_FSYNC 1 /* Deferred AOF fsync. */ 41 | #define REDIS_BIO_NUM_OPS 2 42 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/main.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/main.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/middleware_server.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/middleware_server.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/middleware_server.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/middleware_server.h -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/networking.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/networking.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/zmalloc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/zmalloc.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network+clientManager+protocolParse/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | void *zmalloc(size_t size); 69 | void *zcalloc(size_t size); 70 | void *zrealloc(void *ptr, size_t size); 71 | void zfree(void *ptr); 72 | char *zstrdup(const char *s); 73 | size_t zmalloc_used_memory(void); 74 | void zmalloc_enable_thread_safeness(void); 75 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 76 | float zmalloc_get_fragmentation_ratio(size_t rss); 77 | size_t zmalloc_get_rss(void); 78 | size_t zmalloc_get_private_dirty(void); 79 | void zlibc_free(void *ptr); 80 | 81 | #ifndef HAVE_MALLOC_SIZE 82 | size_t zmalloc_size(void *ptr); 83 | #endif 84 | 85 | #endif /* __ZMALLOC_H */ 86 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习+客户端结构组织+网络协议解析等/asyn_network.md: -------------------------------------------------------------------------------- 1 |      本文档配合主要对如下demo进行配合说明: [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network) 2 | 3 | _**0\. 手把手教你做中间件、高性能服务器、分布式存储技术交流群**_ 4 | 5 | 手把手教你做中间件、高性能服务器、分布式存储等(redis、memcache、nginx、大容量redis pika、rocksdb、mongodb、wiredtiger存储引擎、高性能代理中间件),git地址如下: 6 | 7 | git地址:[https://github.com/y123456yz/middleware\_development\_learning](https://github.com/y123456yz/middleware_development_learning) 8 | 9 | **1\. epoll出现背景** 10 | 11 |     epoll 是 linux 内核为处理大批量文件描述符(网络文件描述符主要是socket返回的套接字fd和accept处理的新连接fd)而作了改进的 poll,是 linux 下多路复用 io接口 select/poll 的增强版本。在 linux 的网络编程中,很长时间都在使用 select 来做事件触发。在 2.6 内核中,有一种替换它的机制,就是 epoll。epoll替换select和poll的主要原因如下: 12 | 13 | 1. select最多处理1024(内核代码fd_setsize宏定义)个连接。 14 | 2. select和poll采用轮训方式检测内核网络事件,算法事件复杂度为o(n),n为连接数,效率低下。 15 | 16 |     epoll克服了select和poll的缺点,采用回调方式来检测就绪事件,算法时间复杂度o(1),相比于select和poll,效率得到了很大的提升。 17 | 18 |     借助epoll的事件回调通知机制,工作线程可以在没有网络事件通知的时候做其他工作,这样可以最大限度的利用系统cpu资源,服务端不用再阻塞等待客户端网络事件,而是依赖epoll事件通知机制来避免同步等待。 19 | 20 | # **2\. epoll系统调用接口** 21 | 22 | ## **2.1 epoll_create函数** 23 | 24 | 函数声明:int epoll_create(int size) 25 | 26 |     该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。在linux-2.4.32内核中根据size大小初始化哈希表的大小,在linux2.6.10内核中该参数无用,使用红黑树管理所有的文件描述符,而不是hash。 27 | 28 |     重点:该函数返回的fd将作为其他epoll系统接口的参数。 29 | 30 | 2.2 **epoll_ctl函数** 31 | 32 | 函数声明:int epoll\_ctl(int epfd, int op, int fd, struct epoll\_event event) 33 | 34 |     该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。epoll_wait个参数说明如下: 35 | 36 |      epfd: epoll事件集文件描述符,也就是epoll_create返回值 37 | 38 |      op: 对fd描述符进行的操作类型,可以是添加注册事件、修改事件、删除事件,分别对应宏定义: **epoll\_ctl\_add****、epoll\_ctl\_mod、epoll\_ctl\_del。** 39 | 40 |      fd: 操作的文件描述符。 41 | 42 |      event: 需要操作的fd对应的epoll_event事件对象,对象数据来源为fd和op。 43 | 44 | ## **2.3 epoll_wait函数** 45 | 46 | 函数声明:int epoll\_wait(int epfd, struct epoll\_event events, int maxevents, int timeout),该函数用于轮询i/o事件的发生,改函数参数说明如下: 47 | 48 | epfd: epoll事件集文件描述符,也就是epoll_create返回值。 49 | 50 |     Events:该epoll时间集上的所有epoll\_event信息,每个fd对应的epoll\_event都存入到该数组中,数组每个成员对应一个fd描述符。 51 | 52 | Maxevents: 也就是events数组长度。 53 | 54 | Timeout: 超时时间,如果在这个超时时间内内核没有I/O网络事件通知,则会超时返回,如果在超时时间内有时间通知,则立马返回 55 | 56 | # **3\. epoll I/O对路复用主要代码实现流程** 57 | 58 | 代码实现主要由以下几个阶段组成: 59 | 60 | 1. 创建套接字获取sd,bind然后listen该套接字sd。 61 | 2. 把步骤1中的sd添加到epoll事件集中,epoll只关注sd套接字上的新连接请求,新连接对应的事件为读事件AE_READABLE (EPOLLIN)。并设置新连接事件到来时对应的回调函数为MainAcceptTcpHandler。 62 | 3. 在新连接回调函数MainAcceptTcpHandler中,获取新连接,并返回该新连接对应的文件描述符fd,同时把新连接的fd添加到epoll事件集中,该fd开始关注epoll读事件,如果检测到该fd对应的读事件(客户端发送的数据服务端收到后,内核会触发该fd对应的epoll读事件),则触发读数据回调函数MainReadFromClient。多个连接,每个连接有各自的文件描述符事件结构(该结构记录了各自的私有数据、读写回调函数等),并且每个连接fd有各自的已就绪事件结构。不同连接有不同的结构信息,最终借助epoll实现I/O多路复用。 63 | 4. 进入aeMain事件循环函数中,循环检测步骤2中的新连接事件和步骤3中的数据读事件。如果有对应的epoll事件,则触发epoll_wait返回,并执行对应事件的回调。 64 | 65 | # **4\. epoll I/O多路复用主要数据结构及函数说明** 66 | 67 | ## **4.1 主要数据结构** 68 | 69 | struct aeEventLoop结构用于记录整个epoll事件的各种信息,主要成员如下: 70 | 71 | typedef struct aeEventLoop { 72 | 73 |     // 目前已注册的最大描述符 74 | 75 |     int maxfd;   /* highest file descriptor currently registered */ 76 | 77 |     // 目前已追踪的最大描述符 78 | 79 |     int setsize; /* max number of file descriptors tracked */ 80 | 81 |     // 用于生成时间事件 id 82 | 83 |     long long timeEventNextId; 84 | 85 |     // 最后一次执行时间事件的时间 86 | 87 |     time_t lastTime;     /* Used to detect system clock skew */ 88 | 89 |     // 已注册的文件事件,每个fd对应一个该结构,events实际上是一个数组 90 | 91 |     aeFileEvent \*events; /\* Registered events */ 92 | 93 |     // 已就绪的文件事件,参考aeApiPoll,数组结构 94 | 95 |     aeFiredEvent \*fired; /\* Fired events */ 96 | 97 |     // 时间事件,所有的定时器时间都添加到该链表中 98 | 99 | aeTimeEvent *timeEventHead; 100 | 101 | } 102 | 103 |       该结构主要由文件描述符事件(即网络I/O事件,包括socket/bind/listen对应的sd文件描述符和accept获取到的新连接文件描述符)和定时器事件组成,其中文件事件主要由events、fired、maxfd、setsize,其中events和fired为数组类型,数组大小为setsize。 104 | 105 |       events数组: 成员类型为aeFileEvent,每个成员代表一个注册的文件事件,文件描述符与数组游标对应,例如如果fd=10,则该fd对应的文件事件为event数组的第十个成员Events\[10\]。 106 | 107 |       fired数组: 成员类型为aeFiredEvent,每个成员代表一个就绪的文件事件,文件描述符和数组游标对应,例如如果fd=10,则该fd对应的已就绪的文件事件为fired数组的第十个成员fired \[10\]。 108 | 109 |     Setsize: 为events文件事件数组和fired就绪事件数组的长度,初始值为REDIS\_MAX\_CLIENTS + REDIS\_EVENTLOOP\_FDSET_INCR。aeCreateEventLoop中提前分配好events和fired数组空间。 110 | 111 |       maxfd: 为所有文件描述符中最大的文件描述符,该描述符的作用是调整setsize大小来扩大events和fired数组长度,从而保证存储所有的事件,不会出现数组越界。 112 | 113 | **何时扩大events和fireds数组长度?** 114 | 115 |       例如redis最开始设置的默认最大连接数为REDIS\_MAX\_CLIENTS,如果程序运行一段时间后,我们想调大最大连接数,这时候就需要调整数组长度。 116 | 117 | **为什么events和fireds数组长度需要加REDIS\_EVENTLOOP\_FDSET_INCR?** 118 | 119 |       因为redis程序中除了网络相关accept新连接的描述符外,程序中也会有普通文件描述符,例如套接字socket描述符、日志文件、rdb文件、aof文件、syslog等文件描述符,确保events和fireds数组长度大于配置的最大连接数,从而避免数组越界。 120 | 121 | ## **4.2 主要函数实现** 122 | 123 | 主要函数功能请参考以下几个函数: 124 | 125 | aeCreateFileEvent 126 | 127 | aeDeleteFileEvent 128 | 129 | aeProcessEvents 130 | 131 | aeApiAddEvent 132 | 133 | aeApiDelEvent 134 | 135 | aeApiPoll 136 | 137 | # **5\. 定时器实现原理** 138 | 139 | ## **5.1 定时器主要代码流程** 140 | 141 |       Redis的定时器实际上是借助epoll\_wait实现的,epoll\_wait的超时时间参数timeout是定时器链表中距离当前时间最少的时间差,例如现在是8点1分,我们有一个定时器需要8点1分5秒执行,那么这里epoll_wait的timeout参数就会设置为5s。 142 | 143 |      epoll\_wait函数默认等待网络I/O事件,如果8点1分到8点1分5秒这段时间内没有网络I/O事件到来,那么到了8点1分5秒的时候,epoll\_wait就会超时返回。Epoll_wait返回后就会在aeMain循环体中遍历定时器链表,获取到定时器到达时间比当前时间少的定时器,运行该定时器的对应回调函数。 144 | 145 |       如果在8点1分3秒过程中有网络事件到达,epoll\_wait会在3秒钟返回,返回后处理完对应的网络事件回调函数,然后继续aeMain循环体中遍历定时器链表,获取离当前时间最近的定时器时间为5-3=2秒,也就是还有2秒该定时器才会到期,于是在下一个epoll\_wait中,设置timeout超时时间为2秒,以此循环。 146 | 147 | ## **5.2 两种不同的定时器(周期性定时器、一次性定时器)** 148 | 149 |     周期性定时器: 指的是定时器到期对应的回调函数执行后,需要重新设置该定时器的超时时间,以备下一个周期继续执行。 150 | 151 |     一次性定时器: 本次定时时间到执行完对应的回调函数后,把该定时器从定时器链表删除。 152 | 153 |     两种定时器代码主要代码流程区别如下: 154 | 155 |       ![](https://oscimg.oschina.net/oscnet/7e91166c954a090301d1d61e96f66e40d4f.jpg) 156 | 157 | ## **5.3 主要数据结构及函数实现** 158 | 159 | 主要数据结构如下: 160 | 161 | typedef struct aeEventLoop { 162 | 163 |      // 时间事件,所有的定时器时间都添加到该链表中 164 | 165 |    aeTimeEvent *timeEventHead; 166 | 167 | } 168 | 169 | ![](https://oscimg.oschina.net/oscnet/176f7fbee6b195fd7b513345622a331c410.jpg) 170 | 171 | 主要函数实现参考: 172 | 173 | aeCreateTimeEvent 174 | 175 | aeDeleteTimeEvent 176 | 177 | aeSearchNearestTimer 178 | 179 | processTimeEvents 180 | 181 | # **6\. 常用套接字选项设置** 182 | 183 | 套接字选项可以通过setsockopt函数进行设置,函数声明如下: 184 | 185 |  int setsockopt( int socket, int level, int option\_name, const void *option\_value, size\_t option\_len); 186 | 187 | setsockopt参数说明如下: 188 | 189 |     socket: 可以是bind/listen对应的sd,也可以是accept获取到的新连接fd。 190 | 191 |     level: 参数level是被设置的选项的级别,套接字级别对应 SOL\_SOCKET,tcp网络设置级别对应SOL\_SOCKET. 192 | 193 |     option_name: 选项类型。 194 | 195 |     optval:[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88),指向存放选项待设置的新值的[缓冲区](https://baike.baidu.com/item/%E7%BC%93%E5%86%B2%E5%8C%BA)。 196 | 197 |     optlen:optval缓冲区长度。 198 | 199 | ## **6.1 SOL_SOCKET级别套接字选项** 200 | 201 |     Level级别为SOL\_SOCKET的option\_name常用类型如下(说明:网络I/O的文件描述符句柄有两类,一类是针对socket()/bind/listen对应的sd,一类是新连接到来后accept返回的新的连接句柄fd): 202 | 203 |     SO_REUSEADDR: 复用地址,针对socket()/bind/listen对应的sd,避免服务端进程退出再重启后出现error:98,Address already in use。 204 | 205 |     SO_RECVBUF: 设置连接fd对应的内核网络协议栈接收缓冲区buf大小,每个连接都会有一个recv buf来接收客户端发送的数据。实际应用中,使用默认值就可以,但如果连接过多,负载过大,内存可能吃不消,这时候可以调小该值。 206 | 207 |     SO_SNDBUF:设置连接fd对应的内核网络协议栈发送缓冲区buf大小,每个连接都会有一个send buf来缓存需要发送的连接数据。实际应用中,使用默认值就可以,但如果连接过多,负载过大,内存可能吃不消,这时候可以调小该值。 208 | 209 |     SO_KEEPALIVE:针对socket()/bind/listen对应的sd,设置TCP的keepalive机制,由内核网络协议栈实现连接保活。通过该设置可以判断对端异常断电、网络不通的连接问题(如网线松动)。因为客户端异常断电或者网线松动,服务端是不会有epoll异常事件通知的。如果没有设计应用层保活超时报文,则可以依赖协议栈keepalive来检测连接是否异常。 210 | 211 |     SO_LINGER:决定关闭连接fd的方式,因为关闭连接的时候,该fd对应的内核协议栈buf可能数据还没有发送出去,如果强制立即关闭可能会出现丢数据的情况。可以根据传入optval参数决定立即关闭连接(可能丢数据),还是等待数据发送完毕后关闭释放连接或者超时关闭连接。 212 | 213 |     SO_RCVTIMEO:针对sd和新连接fd,接收数据超时时间,这个针对阻塞读方式。如果read超过这么多时间还没有获取到内核协议栈数据,则超时返回。 214 | 215 |     SO_SNDTIMEO:针对sd和新连接fd,发送数据超时时间,这个针对阻塞写方式。如果write超过这么多时间还没有把数据成功写入到内核协议栈,则超时返回。 216 | 217 | ## **6.2 IPPROTO_TCP级别套接字选项** 218 | 219 | Level级别为IPPROTO\_TCP的option\_name常用类型如下: 220 | 221 |     TCP_NODELAY:针对连接fd,是否启用naggle算法,一般禁用,这样可以保证服务端快速回包,降低时延。 222 | 223 | _**7\. 阻塞、非阻塞**_ 224 | 225 | 服务端和网络I/O相关相关的几个系统函数主要是:accept()接收客户端连接、read()从内核网络协议栈读取客户端发送来的数据、write()写数据到内核协议栈buf,然后由内核调度发送出去。请参考阻塞demo和非阻塞demo。 226 | 227 | ## **7.1阻塞及其demo程序验证说明** 228 | 229 |       网上相关说明很多,但是都比较抽象。这里以服务端调用accept为例说明:accept()函数会进行系统调用,从应用层走到内核空间,最终调用内核函数SYSCALL_DEFINE3(),如果accept对应的sd(socket/bind/listen对应的文件描述符)是阻塞调用(如果不进行设置,默认就是阻塞调用),SYSCALL_DEFINE3()对应的函数会判断内核是否收到客户端新连接,如果没有则一直等待,直到有新连接到来或者超时才会返回。 230 | 231 |       从上面的描述可以看出,如果是阻塞方式,accept()所在的现场会一直等待,整个线程不能做其他事情,这就是阻塞。Accept()阻塞超时时间可以通过上面的SO_RCVTIMEO设置。 232 | 233 |       Read()和write()阻塞操作过程和acept()类似,只有在接收到数据和写数据到协议栈成功才会返回,或者超时返回,超时时间分别可以通过SO\_RCVTIMEO和SO\_SNDTIMEO设置。 234 | 235 |       下面以如下demo为例,来体验阻塞和非阻塞,以下是阻塞操作例子,分别对应服务端和客户端代码: 236 | 237 |       [服务端阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_server.c) 238 | 239 |       [对应客户端程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_client.c) 240 | 241 |         ![](https://oscimg.oschina.net/oscnet/e7274057bb6512e4aa82e47b2cf73e79579.jpg) 242 | 243 |         ![](https://oscimg.oschina.net/oscnet/924ae78a9ae7bc5f5f8cffdbaafce6f4a1f.jpg) 244 | 245 |       从上面的程序,服务端创建套接字后,绑定地址后开始监听,然后阻塞accept()等待客户端连接。如果客户端有连接,则开始阻塞等待read()读客户端发送来的数据,读到数据后打印返回,程序执行结束。 246 | 247 |       客户端程序创建好套接字,设置好需要连接的服务器Ip和端口,先延时10秒钟才开始连接服务器,连接成功后再次延时10秒,然后发送”block test message”字符串给服务端。 248 | 249 |       通过CRT开两个窗口,同时启动服务端和客户端程序,服务端打印信息如下: 250 | 251 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# gcc block\_server.c -o block\_server 252 | 253 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# 254 | 255 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# ./block_server 1234 256 | 257 |         begin accept //在这里阻塞等待客户端连接 258 | 259 |         accept successful from client 260 | 261 |         begin recv message //这里阻塞等待客户端发送数据过来 262 | 263 |         recv message:block test message from client 264 | 265 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# 266 | 267 |         客户端打印信息如下: 268 | 269 |          \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# gcc block\_client.c -o block\_client 270 | 271 |          \[root@localhost block\_noblock\_demo\]# ./block_client 127.0.0.1 1234 272 | 273 |          begin connect  //begin和end见有10s延时 274 | 275 |          end connect 276 | 277 |          begin send message //begin和end间有10s延时 278 | 279 |          end send message:block test message to server 280 | 281 |       从运行服务端程序和客户端程序的打印可以看出,如果客户端不发起连接,服务端accept()函数会阻塞等待,知道有新连接到来才会返回。同时启用服务端和客户端程序,服务端accept()函数10s才会返回,因为客户端我故意做了10s延时。Read()阻塞读函数过程和accept()类似。 282 | 283 |        Write()阻塞验证过程,服务端设置好该链接对应的内核网络协议栈发送缓存区大小,然后传递很大的一个数据给write函数,期望把这个大数据通过write函数写入到内核协议栈发送缓存区。如果内核协议栈缓存区可用buf空间比需要write的数据大,则数据通过write函数拷贝到内核发送缓存区后会立马返回。为了验证write的阻塞过程,我这里故意让客户端不去读数据,这样服务端write的数据就会缓冲到协议栈发送缓冲区,如果缓冲区空间没那么大。Write就会一直等待内核调度把发送缓冲区数据通过网卡发送出去,这样就会腾出空间,继续拷贝用户态write需要写的数据。由于这里我故意让客户端不读数据,该链接对应的发送缓冲区很快就会写满,由于我想要写的数据比这个buf缓冲区大很多,那么write函数就需要阻塞等待,直到把期望发送的数据全部写入到该发送缓存区才会返回,或者超过系统默认的write超时时间才会返回。 284 | 285 | ## **7.2 非阻塞及其demo程序验证说明** 286 | 287 |     非阻塞通过系统调用fcntl设置,函数代码如下: 288 | 289 |       ![](https://oscimg.oschina.net/oscnet/5adee1fb52632e285ebe6b9942cc1b8dace.jpg) 290 | 291 |     函数中的fd文件描述符可以是socket/bind/listen对应的sd,也可以是accept获取到的新连接fd。非阻塞程序demo github地址如下: 292 | 293 |     [服务端非阻塞程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_server.c) 294 | 295 |     [对应客户端非阻塞程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_client.c) 296 | 297 |     编译程序,先启动服务端程序,然后启动客户端程序,验证方法如下: 298 | 299 |        ![](https://oscimg.oschina.net/oscnet/baa69e68f2713a5d967fddfa2d605712302.jpg) 300 | 301 |     对应客户端: 302 | 303 |       ![](https://oscimg.oschina.net/oscnet/a149a4dd68010a8c05c9ae94c3fb97fee65.jpg) 304 | 305 |       客户端启动后,延迟10秒向服务端发起连接,连接建立成功后,延迟10秒向服务端发送数据。服务端启动后,设置sd为非阻塞,开始accept等待接收客户端连接,如果accept系统调用没有获取到客户端连接,则延时1秒钟,然后继续accept。由于客户端启动后要延迟10s钟才发起连接,因此accept会有十次accept return打印。read过程和accept类似,可以查看demo代码。 306 | 307 |       我们知道write操作是把用户态向要发送的数据拷贝到内核态连接对应的send buf缓冲区,如果是阻塞方式,如果内核缓存区空间不够,则write会阻塞等待。但是如果我们把新连接的fd设置为非阻塞,及时内核发送缓冲区空间不够,write也会立马返回,并返回本次写入到内核空间的数据量,不会阻塞等待。可以通过运行demo自己来体验这个过程。 308 | 309 |      大家应该注意到,则非阻塞操作服务端demo中,accept(),read()如果没有返回我们想要的连接或者数据,demo中做了sleep延时,为什么这里要做点延时呢? 310 | 311 |       原因是如果不做延时,这里会不停的进行accept read系统调用,系统调用过程是个非常消耗性能的过程,会造成CPU的大量浪费。假设我们不加sleep延时,通过top可以查看到如下现象: 312 | 313 |       ![](https://oscimg.oschina.net/oscnet/c28a21ae4e158326884a1e66ce309ad9543.jpg) 314 | 315 | **8\. 同步、异步** 316 | 317 |     同步和异步是比较抽象的概念,还是用程序demo来说明。 318 | 319 | ## **8.1 同步** 320 | 321 |     上面的[服务端阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_server.c)和[服务端非阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_server.c)实际上都是同步调用的过程。这两个demo都是单线程的,以accept()调用为例,不管是阻塞操作还是非阻塞操作,由于服务端不知道客户端合适发起连接,因此只能阻塞等待,或者非阻塞轮训查询。不管是阻塞等待还是轮训查询,效率都非常低下,整个线程不能做其他工作,CPU完全利用不起来。 322 | 323 | ## **8.2 异步** 324 | 325 |        [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network),这个demo是异步操作。还是以该demo的accept为例说明,从这个demo可以看出,sd设置为非阻塞,借助epoll机制,当有新的连接事件到来后,触发epoll_wait返回,并返回所有的文件描述符对应的读写事件,这样就触发执行对应的新连接回调函数MainAcceptTcpHandler。借助epoll事件通知机制,就避免了前面两个demo的阻塞等待过程和轮训查询过程,整个accept()操作由事件触发,不必轮训等待。本异步网络框架demo也是单线程,就不存在前面两个demo只能做accept这一件事,如果没有accept事件到来,本异步网络框架demo线程还可以处理其他已有连接的读写事件,这样线程CPU资源也就充分利用起来了。 326 | 327 |       打个形象的比喻,假设我们每年单位都有福利体检,体检后一到两周出体检结果,想要获取体检结果有两种方式。第一种方式: 体检结束一周后,你就坐在医院一直等,直到体检结果出来,整个过程你是无法正常去单位上班的(这就相当于前面的服务端阻塞demo方式)。第二种方式:你每天都跑去体检医院询问,我的体检结果出了吗,如果没有,第二天有去体检医院,以此重复,直到有一天你去体检医院拿到体检结果。在你每天去医院询问是否已经出体检结果的过程中,你是不能正常上班的(这种方式类似于前面的服务端非阻塞demo方式)。第三种方式:你每天正常上班,等体检医院打电话通知你拿体检结果,你再去拿,电话通知你拿体检结果的过程就相当于异步事件通知,这样你就可以正常上班了。第一、二种方式就是同步操作,第三种方式就是异步操作。 328 | 329 | **    总结: 同步和异步的区别就是异步操作借助epoll的事件通知机制,从而可以充分利用CPU资源。** 330 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/READE.md: -------------------------------------------------------------------------------- 1 | 借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,包括网络收发,epoll注册过程,定时器实现原理 2 | 3 | linux安装编译测试 4 | =================================== 5 | make编译出可执行文件net,然后运行 6 | 7 | 服务端(运行net可执行文件) 8 | [root@bogon asyn_network]# ./net 9 | 10 | 客户端用telnet链接,发送字符串 11 | [root@bogon asyn_network]# telnet 127.0.0.1 6379 12 | Trying 127.0.0.1... 13 | Connected to 127.0.0.1. 14 | Escape character is '^]'. 15 | hello boy 16 | hello boy 17 | 18 | 看看服务端打印: 19 | [root@bogon asyn_network]# ./net 20 | process Start begin 21 | MainTimerExpire 22 | client 127.0.0.1:60044 Connected 23 | MainTimerExpire 24 | recv from client 127.0.0.1:60044, data:hello boy -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network.md: -------------------------------------------------------------------------------- 1 |      本文档配合主要对如下demo进行配合说明: [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network) 2 | 3 | _**0\. 手把手教你做中间件、高性能服务器、分布式存储技术交流群**_ 4 | 5 | 手把手教你做中间件、高性能服务器、分布式存储等(redis、memcache、nginx、大容量redis pika、rocksdb、mongodb、wiredtiger存储引擎、高性能代理中间件),git地址如下: 6 | 7 | git地址:[https://github.com/y123456yz/middleware\_development\_learning](https://github.com/y123456yz/middleware_development_learning) 8 | 9 | **1\. epoll出现背景** 10 | 11 |     epoll 是 linux 内核为处理大批量文件描述符(网络文件描述符主要是socket返回的套接字fd和accept处理的新连接fd)而作了改进的 poll,是 linux 下多路复用 io接口 select/poll 的增强版本。在 linux 的网络编程中,很长时间都在使用 select 来做事件触发。在 2.6 内核中,有一种替换它的机制,就是 epoll。epoll替换select和poll的主要原因如下: 12 | 13 | 1. select最多处理1024(内核代码fd_setsize宏定义)个连接。 14 | 2. select和poll采用轮训方式检测内核网络事件,算法事件复杂度为o(n),n为连接数,效率低下。 15 | 16 |     epoll克服了select和poll的缺点,采用回调方式来检测就绪事件,算法时间复杂度o(1),相比于select和poll,效率得到了很大的提升。 17 | 18 |     借助epoll的事件回调通知机制,工作线程可以在没有网络事件通知的时候做其他工作,这样可以最大限度的利用系统cpu资源,服务端不用再阻塞等待客户端网络事件,而是依赖epoll事件通知机制来避免同步等待。 19 | 20 | # **2\. epoll系统调用接口** 21 | 22 | ## **2.1 epoll_create函数** 23 | 24 | 函数声明:int epoll_create(int size) 25 | 26 |     该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。在linux-2.4.32内核中根据size大小初始化哈希表的大小,在linux2.6.10内核中该参数无用,使用红黑树管理所有的文件描述符,而不是hash。 27 | 28 |     重点:该函数返回的fd将作为其他epoll系统接口的参数。 29 | 30 | 2.2 **epoll_ctl函数** 31 | 32 | 函数声明:int epoll\_ctl(int epfd, int op, int fd, struct epoll\_event event) 33 | 34 |     该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。epoll_wait个参数说明如下: 35 | 36 |      epfd: epoll事件集文件描述符,也就是epoll_create返回值 37 | 38 |      op: 对fd描述符进行的操作类型,可以是添加注册事件、修改事件、删除事件,分别对应宏定义: **epoll\_ctl\_add****、epoll\_ctl\_mod、epoll\_ctl\_del。** 39 | 40 |      fd: 操作的文件描述符。 41 | 42 |      event: 需要操作的fd对应的epoll_event事件对象,对象数据来源为fd和op。 43 | 44 | ## **2.3 epoll_wait函数** 45 | 46 | 函数声明:int epoll\_wait(int epfd, struct epoll\_event events, int maxevents, int timeout),该函数用于轮询i/o事件的发生,改函数参数说明如下: 47 | 48 | epfd: epoll事件集文件描述符,也就是epoll_create返回值。 49 | 50 |     Events:该epoll时间集上的所有epoll\_event信息,每个fd对应的epoll\_event都存入到该数组中,数组每个成员对应一个fd描述符。 51 | 52 | Maxevents: 也就是events数组长度。 53 | 54 | Timeout: 超时时间,如果在这个超时时间内内核没有I/O网络事件通知,则会超时返回,如果在超时时间内有时间通知,则立马返回 55 | 56 | # **3\. epoll I/O对路复用主要代码实现流程** 57 | 58 | 代码实现主要由以下几个阶段组成: 59 | 60 | 1. 创建套接字获取sd,bind然后listen该套接字sd。 61 | 2. 把步骤1中的sd添加到epoll事件集中,epoll只关注sd套接字上的新连接请求,新连接对应的事件为读事件AE_READABLE (EPOLLIN)。并设置新连接事件到来时对应的回调函数为MainAcceptTcpHandler。 62 | 3. 在新连接回调函数MainAcceptTcpHandler中,获取新连接,并返回该新连接对应的文件描述符fd,同时把新连接的fd添加到epoll事件集中,该fd开始关注epoll读事件,如果检测到该fd对应的读事件(客户端发送的数据服务端收到后,内核会触发该fd对应的epoll读事件),则触发读数据回调函数MainReadFromClient。多个连接,每个连接有各自的文件描述符事件结构(该结构记录了各自的私有数据、读写回调函数等),并且每个连接fd有各自的已就绪事件结构。不同连接有不同的结构信息,最终借助epoll实现I/O多路复用。 63 | 4. 进入aeMain事件循环函数中,循环检测步骤2中的新连接事件和步骤3中的数据读事件。如果有对应的epoll事件,则触发epoll_wait返回,并执行对应事件的回调。 64 | 65 | # **4\. epoll I/O多路复用主要数据结构及函数说明** 66 | 67 | ## **4.1 主要数据结构** 68 | 69 | struct aeEventLoop结构用于记录整个epoll事件的各种信息,主要成员如下: 70 | 71 | typedef struct aeEventLoop { 72 | 73 |     // 目前已注册的最大描述符 74 | 75 |     int maxfd;   /* highest file descriptor currently registered */ 76 | 77 |     // 目前已追踪的最大描述符 78 | 79 |     int setsize; /* max number of file descriptors tracked */ 80 | 81 |     // 用于生成时间事件 id 82 | 83 |     long long timeEventNextId; 84 | 85 |     // 最后一次执行时间事件的时间 86 | 87 |     time_t lastTime;     /* Used to detect system clock skew */ 88 | 89 |     // 已注册的文件事件,每个fd对应一个该结构,events实际上是一个数组 90 | 91 |     aeFileEvent \*events; /\* Registered events */ 92 | 93 |     // 已就绪的文件事件,参考aeApiPoll,数组结构 94 | 95 |     aeFiredEvent \*fired; /\* Fired events */ 96 | 97 |     // 时间事件,所有的定时器时间都添加到该链表中 98 | 99 | aeTimeEvent *timeEventHead; 100 | 101 | } 102 | 103 |       该结构主要由文件描述符事件(即网络I/O事件,包括socket/bind/listen对应的sd文件描述符和accept获取到的新连接文件描述符)和定时器事件组成,其中文件事件主要由events、fired、maxfd、setsize,其中events和fired为数组类型,数组大小为setsize。 104 | 105 |       events数组: 成员类型为aeFileEvent,每个成员代表一个注册的文件事件,文件描述符与数组游标对应,例如如果fd=10,则该fd对应的文件事件为event数组的第十个成员Events\[10\]。 106 | 107 |       fired数组: 成员类型为aeFiredEvent,每个成员代表一个就绪的文件事件,文件描述符和数组游标对应,例如如果fd=10,则该fd对应的已就绪的文件事件为fired数组的第十个成员fired \[10\]。 108 | 109 |     Setsize: 为events文件事件数组和fired就绪事件数组的长度,初始值为REDIS\_MAX\_CLIENTS + REDIS\_EVENTLOOP\_FDSET_INCR。aeCreateEventLoop中提前分配好events和fired数组空间。 110 | 111 |       maxfd: 为所有文件描述符中最大的文件描述符,该描述符的作用是调整setsize大小来扩大events和fired数组长度,从而保证存储所有的事件,不会出现数组越界。 112 | 113 | **何时扩大events和fireds数组长度?** 114 | 115 |       例如redis最开始设置的默认最大连接数为REDIS\_MAX\_CLIENTS,如果程序运行一段时间后,我们想调大最大连接数,这时候就需要调整数组长度。 116 | 117 | **为什么events和fireds数组长度需要加REDIS\_EVENTLOOP\_FDSET_INCR?** 118 | 119 |       因为redis程序中除了网络相关accept新连接的描述符外,程序中也会有普通文件描述符,例如套接字socket描述符、日志文件、rdb文件、aof文件、syslog等文件描述符,确保events和fireds数组长度大于配置的最大连接数,从而避免数组越界。 120 | 121 | ## **4.2 主要函数实现** 122 | 123 | 主要函数功能请参考以下几个函数: 124 | 125 | aeCreateFileEvent 126 | 127 | aeDeleteFileEvent 128 | 129 | aeProcessEvents 130 | 131 | aeApiAddEvent 132 | 133 | aeApiDelEvent 134 | 135 | aeApiPoll 136 | 137 | # **5\. 定时器实现原理** 138 | 139 | ## **5.1 定时器主要代码流程** 140 | 141 |       Redis的定时器实际上是借助epoll\_wait实现的,epoll\_wait的超时时间参数timeout是定时器链表中距离当前时间最少的时间差,例如现在是8点1分,我们有一个定时器需要8点1分5秒执行,那么这里epoll_wait的timeout参数就会设置为5s。 142 | 143 |      epoll\_wait函数默认等待网络I/O事件,如果8点1分到8点1分5秒这段时间内没有网络I/O事件到来,那么到了8点1分5秒的时候,epoll\_wait就会超时返回。Epoll_wait返回后就会在aeMain循环体中遍历定时器链表,获取到定时器到达时间比当前时间少的定时器,运行该定时器的对应回调函数。 144 | 145 |       如果在8点1分3秒过程中有网络事件到达,epoll\_wait会在3秒钟返回,返回后处理完对应的网络事件回调函数,然后继续aeMain循环体中遍历定时器链表,获取离当前时间最近的定时器时间为5-3=2秒,也就是还有2秒该定时器才会到期,于是在下一个epoll\_wait中,设置timeout超时时间为2秒,以此循环。 146 | 147 | ## **5.2 两种不同的定时器(周期性定时器、一次性定时器)** 148 | 149 |     周期性定时器: 指的是定时器到期对应的回调函数执行后,需要重新设置该定时器的超时时间,以备下一个周期继续执行。 150 | 151 |     一次性定时器: 本次定时时间到执行完对应的回调函数后,把该定时器从定时器链表删除。 152 | 153 |     两种定时器代码主要代码流程区别如下: 154 | 155 |       ![](https://oscimg.oschina.net/oscnet/7e91166c954a090301d1d61e96f66e40d4f.jpg) 156 | 157 | ## **5.3 主要数据结构及函数实现** 158 | 159 | 主要数据结构如下: 160 | 161 | typedef struct aeEventLoop { 162 | 163 |      // 时间事件,所有的定时器时间都添加到该链表中 164 | 165 |    aeTimeEvent *timeEventHead; 166 | 167 | } 168 | 169 | ![](https://oscimg.oschina.net/oscnet/176f7fbee6b195fd7b513345622a331c410.jpg) 170 | 171 | 主要函数实现参考: 172 | 173 | aeCreateTimeEvent 174 | 175 | aeDeleteTimeEvent 176 | 177 | aeSearchNearestTimer 178 | 179 | processTimeEvents 180 | 181 | # **6\. 常用套接字选项设置** 182 | 183 | 套接字选项可以通过setsockopt函数进行设置,函数声明如下: 184 | 185 |  int setsockopt( int socket, int level, int option\_name, const void *option\_value, size\_t option\_len); 186 | 187 | setsockopt参数说明如下: 188 | 189 |     socket: 可以是bind/listen对应的sd,也可以是accept获取到的新连接fd。 190 | 191 |     level: 参数level是被设置的选项的级别,套接字级别对应 SOL\_SOCKET,tcp网络设置级别对应IPPROTO\_TCP. 192 | 193 |     option_name: 选项类型。 194 | 195 |     optval:[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88),指向存放选项待设置的新值的[缓冲区](https://baike.baidu.com/item/%E7%BC%93%E5%86%B2%E5%8C%BA)。 196 | 197 |     optlen:optval缓冲区长度。 198 | 199 | ## **6.1 SOL_SOCKET级别套接字选项** 200 | 201 |     Level级别为SOL\_SOCKET的option\_name常用类型如下(说明:网络I/O的文件描述符句柄有两类,一类是针对socket()/bind/listen对应的sd,一类是新连接到来后accept返回的新的连接句柄fd): 202 | 203 |     SO_REUSEADDR: 复用地址,针对socket()/bind/listen对应的sd,避免服务端进程退出再重启后出现error:98,Address already in use。 204 | 205 |     SO_RECVBUF: 设置连接fd对应的内核网络协议栈接收缓冲区buf大小,每个连接都会有一个recv buf来接收客户端发送的数据。实际应用中,使用默认值就可以,但如果连接过多,负载过大,内存可能吃不消,这时候可以调小该值。 206 | 207 |     SO_SNDBUF:设置连接fd对应的内核网络协议栈发送缓冲区buf大小,每个连接都会有一个send buf来缓存需要发送的连接数据。实际应用中,使用默认值就可以,但如果连接过多,负载过大,内存可能吃不消,这时候可以调小该值。 208 | 209 |     SO_KEEPALIVE:针对socket()/bind/listen对应的sd,设置TCP的keepalive机制,由内核网络协议栈实现连接保活。通过该设置可以判断对端异常断电、网络不通的连接问题(如网线松动)。因为客户端异常断电或者网线松动,服务端是不会有epoll异常事件通知的。如果没有设计应用层保活超时报文,则可以依赖协议栈keepalive来检测连接是否异常。 210 | 211 |     SO_LINGER:决定关闭连接fd的方式,因为关闭连接的时候,该fd对应的内核协议栈buf可能数据还没有发送出去,如果强制立即关闭可能会出现丢数据的情况。可以根据传入optval参数决定立即关闭连接(可能丢数据),还是等待数据发送完毕后关闭释放连接或者超时关闭连接。 212 | 213 |     SO_RCVTIMEO:针对sd和新连接fd,接收数据超时时间,这个针对阻塞读方式。如果read超过这么多时间还没有获取到内核协议栈数据,则超时返回。 214 | 215 |     SO_SNDTIMEO:针对sd和新连接fd,发送数据超时时间,这个针对阻塞写方式。如果write超过这么多时间还没有把数据成功写入到内核协议栈,则超时返回。 216 | 217 | ## **6.2 IPPROTO_TCP级别套接字选项** 218 | 219 | Level级别为IPPROTO\_TCP的option\_name常用类型如下: 220 | 221 |     TCP_NODELAY:针对连接fd,是否启用naggle算法,一般禁用,这样可以保证服务端快速回包,降低时延。 222 | 223 | _**7\. 阻塞、非阻塞**_ 224 | 225 | 服务端和网络I/O相关相关的几个系统函数主要是:accept()接收客户端连接、read()从内核网络协议栈读取客户端发送来的数据、write()写数据到内核协议栈buf,然后由内核调度发送出去。请参考阻塞demo和非阻塞demo。 226 | 227 | ## **7.1阻塞及其demo程序验证说明** 228 | 229 |       网上相关说明很多,但是都比较抽象。这里以服务端调用accept为例说明:accept()函数会进行系统调用,从应用层走到内核空间,最终调用内核函数SYSCALL_DEFINE3(),如果accept对应的sd(socket/bind/listen对应的文件描述符)是阻塞调用(如果不进行设置,默认就是阻塞调用),SYSCALL_DEFINE3()对应的函数会判断内核是否收到客户端新连接,如果没有则一直等待,直到有新连接到来或者超时才会返回。 230 | 231 |       从上面的描述可以看出,如果是阻塞方式,accept()所在的现场会一直等待,整个线程不能做其他事情,这就是阻塞。Accept()阻塞超时时间可以通过上面的SO_RCVTIMEO设置。 232 | 233 |       Read()和write()阻塞操作过程和acept()类似,只有在接收到数据和写数据到协议栈成功才会返回,或者超时返回,超时时间分别可以通过SO\_RCVTIMEO和SO\_SNDTIMEO设置。 234 | 235 |       下面以如下demo为例,来体验阻塞和非阻塞,以下是阻塞操作例子,分别对应服务端和客户端代码: 236 | 237 |       [服务端阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_server.c) 238 | 239 |       [对应客户端程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_client.c) 240 | 241 |         ![](https://oscimg.oschina.net/oscnet/e7274057bb6512e4aa82e47b2cf73e79579.jpg) 242 | 243 |         ![](https://oscimg.oschina.net/oscnet/924ae78a9ae7bc5f5f8cffdbaafce6f4a1f.jpg) 244 | 245 |       从上面的程序,服务端创建套接字后,绑定地址后开始监听,然后阻塞accept()等待客户端连接。如果客户端有连接,则开始阻塞等待read()读客户端发送来的数据,读到数据后打印返回,程序执行结束。 246 | 247 |       客户端程序创建好套接字,设置好需要连接的服务器Ip和端口,先延时10秒钟才开始连接服务器,连接成功后再次延时10秒,然后发送”block test message”字符串给服务端。 248 | 249 |       通过CRT开两个窗口,同时启动服务端和客户端程序,服务端打印信息如下: 250 | 251 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# gcc block\_server.c -o block\_server 252 | 253 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# 254 | 255 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# ./block_server 1234 256 | 257 |         begin accept //在这里阻塞等待客户端连接 258 | 259 |         accept successful from client 260 | 261 |         begin recv message //这里阻塞等待客户端发送数据过来 262 | 263 |         recv message:block test message from client 264 | 265 |         \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# 266 | 267 |         客户端打印信息如下: 268 | 269 |          \[root[@localhost](https://my.oschina.net/u/570656) block\_noblock\_demo\]# gcc block\_client.c -o block\_client 270 | 271 |          \[root@localhost block\_noblock\_demo\]# ./block_client 127.0.0.1 1234 272 | 273 |          begin connect  //begin和end见有10s延时 274 | 275 |          end connect 276 | 277 |          begin send message //begin和end间有10s延时 278 | 279 |          end send message:block test message to server 280 | 281 |       从运行服务端程序和客户端程序的打印可以看出,如果客户端不发起连接,服务端accept()函数会阻塞等待,知道有新连接到来才会返回。同时启用服务端和客户端程序,服务端accept()函数10s才会返回,因为客户端我故意做了10s延时。Read()阻塞读函数过程和accept()类似。 282 | 283 |        Write()阻塞验证过程,服务端设置好该链接对应的内核网络协议栈发送缓存区大小,然后传递很大的一个数据给write函数,期望把这个大数据通过write函数写入到内核协议栈发送缓存区。如果内核协议栈缓存区可用buf空间比需要write的数据大,则数据通过write函数拷贝到内核发送缓存区后会立马返回。为了验证write的阻塞过程,我这里故意让客户端不去读数据,这样服务端write的数据就会缓冲到协议栈发送缓冲区,如果缓冲区空间没那么大。Write就会一直等待内核调度把发送缓冲区数据通过网卡发送出去,这样就会腾出空间,继续拷贝用户态write需要写的数据。由于这里我故意让客户端不读数据,该链接对应的发送缓冲区很快就会写满,由于我想要写的数据比这个buf缓冲区大很多,那么write函数就需要阻塞等待,直到把期望发送的数据全部写入到该发送缓存区才会返回,或者超过系统默认的write超时时间才会返回。 284 | 285 | ## **7.2 非阻塞及其demo程序验证说明** 286 | 287 |     非阻塞通过系统调用fcntl设置,函数代码如下: 288 | 289 |       ![](https://oscimg.oschina.net/oscnet/5adee1fb52632e285ebe6b9942cc1b8dace.jpg) 290 | 291 |     函数中的fd文件描述符可以是socket/bind/listen对应的sd,也可以是accept获取到的新连接fd。非阻塞程序demo github地址如下: 292 | 293 |     [服务端非阻塞程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_server.c) 294 | 295 |     [对应客户端非阻塞程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_client.c) 296 | 297 |     编译程序,先启动服务端程序,然后启动客户端程序,验证方法如下: 298 | 299 |        ![](https://oscimg.oschina.net/oscnet/baa69e68f2713a5d967fddfa2d605712302.jpg) 300 | 301 |     对应客户端: 302 | 303 |       ![](https://oscimg.oschina.net/oscnet/a149a4dd68010a8c05c9ae94c3fb97fee65.jpg) 304 | 305 |       客户端启动后,延迟10秒向服务端发起连接,连接建立成功后,延迟10秒向服务端发送数据。服务端启动后,设置sd为非阻塞,开始accept等待接收客户端连接,如果accept系统调用没有获取到客户端连接,则延时1秒钟,然后继续accept。由于客户端启动后要延迟10s钟才发起连接,因此accept会有十次accept return打印。read过程和accept类似,可以查看demo代码。 306 | 307 |       我们知道write操作是把用户态向要发送的数据拷贝到内核态连接对应的send buf缓冲区,如果是阻塞方式,如果内核缓存区空间不够,则write会阻塞等待。但是如果我们把新连接的fd设置为非阻塞,及时内核发送缓冲区空间不够,write也会立马返回,并返回本次写入到内核空间的数据量,不会阻塞等待。可以通过运行demo自己来体验这个过程。 308 | 309 |      大家应该注意到,则非阻塞操作服务端demo中,accept(),read()如果没有返回我们想要的连接或者数据,demo中做了sleep延时,为什么这里要做点延时呢? 310 | 311 |       原因是如果不做延时,这里会不停的进行accept read系统调用,系统调用过程是个非常消耗性能的过程,会造成CPU的大量浪费。假设我们不加sleep延时,通过top可以查看到如下现象: 312 | 313 |       ![](https://oscimg.oschina.net/oscnet/c28a21ae4e158326884a1e66ce309ad9543.jpg) 314 | 315 | **8\. 同步、异步** 316 | 317 |     同步和异步是比较抽象的概念,还是用程序demo来说明。 318 | 319 | ## **8.1 同步** 320 | 321 |     上面的[服务端阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/block_server.c)和[服务端非阻塞操作程序demo github地址](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/block_noblock_demo/noblock_server.c)实际上都是同步调用的过程。这两个demo都是单线程的,以accept()调用为例,不管是阻塞操作还是非阻塞操作,由于服务端不知道客户端合适发起连接,因此只能阻塞等待,或者非阻塞轮训查询。不管是阻塞等待还是轮训查询,效率都非常低下,整个线程不能做其他工作,CPU完全利用不起来。 322 | 323 | ## **8.2 异步** 324 | 325 |        [借助redis已有的网络相关.c和.h文件,半小时快速实现一个epoll异步网络框架,程序demo](https://github.com/y123456yz/middleware_development_learning/tree/master/%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E3%80%81%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E6%A1%86%E6%9E%B6%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/asyn_network),这个demo是异步操作。还是以该demo的accept为例说明,从这个demo可以看出,sd设置为非阻塞,借助epoll机制,当有新的连接事件到来后,触发epoll_wait返回,并返回所有的文件描述符对应的读写事件,这样就触发执行对应的新连接回调函数MainAcceptTcpHandler。借助epoll事件通知机制,就避免了前面两个demo的阻塞等待过程和轮训查询过程,整个accept()操作由事件触发,不必轮训等待。本异步网络框架demo也是单线程,就不存在前面两个demo只能做accept这一件事,如果没有accept事件到来,本异步网络框架demo线程还可以处理其他已有连接的读写事件,这样线程CPU资源也就充分利用起来了。 326 | 327 |       打个形象的比喻,假设我们每年单位都有福利体检,体检后一到两周出体检结果,想要获取体检结果有两种方式。第一种方式: 体检结束一周后,你就坐在医院一直等,直到体检结果出来,整个过程你是无法正常去单位上班的(这就相当于前面的服务端阻塞demo方式)。第二种方式:你每天都跑去体检医院询问,我的体检结果出了吗,如果没有,第二天有去体检医院,以此重复,直到有一天你去体检医院拿到体检结果。在你每天去医院询问是否已经出体检结果的过程中,你是不能正常上班的(这种方式类似于前面的服务端非阻塞demo方式)。第三种方式:你每天正常上班,等体检医院打电话通知你拿体检结果,你再去拿,电话通知你拿体检结果的过程就相当于异步事件通知,这样你就可以正常上班了。第一、二种方式就是同步操作,第三种方式就是异步操作。 328 | 329 | **    总结: 同步和异步的区别就是异步操作借助epoll的事件通知机制,从而可以充分利用CPU资源。** 330 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | net 4 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = net 2 | OBJS = ae.o zmalloc.o ae_epoll.o anet.o main.o 3 | DEPS = ae.h anet.h zmalloc.h 4 | CPPFLAGS = -g 5 | CC = gcc 6 | 7 | $(TARGET) : $(OBJS) 8 | $(CC) -o $(TARGET) $(OBJS) $(CFLAGS) 9 | 10 | zmalloc.o: zmalloc.c 11 | ae.o : ae.c 12 | ae_epoll.o : ae_epoll.c 13 | anet.o : anet.c 14 | main.o : main.c $(DEPS) 15 | 16 | clean : 17 | rm $(OBJS) $(TARGET) 18 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/ae.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/ae.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/ae.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/ae.h -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/ae_epoll.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/ae_epoll.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/anet.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/anet.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/anet.h: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef ANET_H 32 | #define ANET_H 33 | 34 | #define ANET_OK 0 35 | #define ANET_ERR -1 36 | #define ANET_ERR_LEN 256 37 | 38 | /* Flags used with certain functions. */ 39 | #define ANET_NONE 0 40 | #define ANET_IP_ONLY (1<<0) 41 | 42 | #if defined(__sun) 43 | #define AF_LOCAL AF_UNIX 44 | #endif 45 | 46 | int anetTcpConnect(char *err, char *addr, int port); 47 | int anetTcpNonBlockConnect(char *err, char *addr, int port); 48 | int anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr); 49 | int anetUnixConnect(char *err, char *path); 50 | int anetUnixNonBlockConnect(char *err, char *path); 51 | int anetRead(int fd, char *buf, int count); 52 | int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len); 53 | int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len); 54 | int anetTcpServer(char *err, int port, char *bindaddr, int backlog); 55 | int anetTcp6Server(char *err, int port, char *bindaddr, int backlog); 56 | int anetUnixServer(char *err, char *path, mode_t perm, int backlog); 57 | int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port); 58 | int anetUnixAccept(char *err, int serversock); 59 | int anetWrite(int fd, char *buf, int count); 60 | int anetNonBlock(char *err, int fd); 61 | int anetEnableTcpNoDelay(char *err, int fd); 62 | int anetDisableTcpNoDelay(char *err, int fd); 63 | int anetTcpKeepAlive(char *err, int fd); 64 | int anetPeerToString(int fd, char *ip, size_t ip_len, int *port); 65 | int anetKeepAlive(char *err, int fd, int interval); 66 | int anetSockName(int fd, char *ip, size_t ip_len, int *port); 67 | int anetSetReuseAddr(char *err, int fd); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/main.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/main.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/zmalloc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/zmalloc.c -------------------------------------------------------------------------------- /第一阶段-手把手教你做分布式缓存二次开发、性能优化/异步网络框架零基础学习/asyn_network/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | void *zmalloc(size_t size); 69 | void *zcalloc(size_t size); 70 | void *zrealloc(void *ptr, size_t size); 71 | void zfree(void *ptr); 72 | char *zstrdup(const char *s); 73 | size_t zmalloc_used_memory(void); 74 | void zmalloc_enable_thread_safeness(void); 75 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 76 | float zmalloc_get_fragmentation_ratio(size_t rss); 77 | size_t zmalloc_get_rss(void); 78 | size_t zmalloc_get_private_dirty(void); 79 | void zlibc_free(void *ptr); 80 | 81 | #ifndef HAVE_MALLOC_SIZE 82 | size_t zmalloc_size(void *ptr); 83 | #endif 84 | 85 | #endif /* __ZMALLOC_H */ 86 | -------------------------------------------------------------------------------- /第三阶段-手把手教你做wiredtiger、rocksdb存储引擎开发,大容量nosql存储系统二次开发/README.md: -------------------------------------------------------------------------------- 1 | ## 第三阶段:手把手教你做wiredtiger、rocksdb存储引擎开发,大容量nosql存储系统二次开发 2 | * [文档数据库mongodb kv存储引擎wiredtiger源码详细分析注释](https://github.com/y123456yz/reading-and-annotate-wiredtiger-3.0.0) 3 | * [rocksdb-6.1.2 KV存储引擎源码中文注释分析](https://github.com/y123456yz/reading-and-annotate-rocksdb-6.1.2) -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/README.md: -------------------------------------------------------------------------------- 1 | 手把手教你做高性能代理中间件开发 2 | 3 | ## 第二阶段:手把手教你做高性能代理中间件开发 4 | * [redis、memcached缓存代理twemproxy源码详细分析注释,带详尽中文注释及函数调用关系](https://github.com/y123456yz/Reading-and-comprehense-twemproxy0.4.1) 5 | * [nginx-1.9.2源码通读分析注释,带详尽函数中文分析注释](https://github.com/y123456yz/reading-code-of-nginx-1.9.2) 6 | * [nginx多进程、高性能、低时延、高可靠机制应用于缓存中间件twemproxy,对twemproxy进行多进程优化改造,提升TPS,降低时延,代理中间件长连接百万TPS/短连接五十万TPS实现原理](https://github.com/y123456yz/middleware_development_learning/blob/master/%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%81%9A%E9%AB%98%E6%80%A7%E8%83%BD%E4%BB%A3%E7%90%86%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%BC%80%E5%8F%91/nginx%E5%A4%9A%E8%BF%9B%E7%A8%8B%E9%AB%98%E5%B9%B6%E5%8F%91%E4%BD%8E%E6%97%B6%E5%BB%B6%E6%9C%BA%E5%88%B6%E5%9C%A8%E7%BC%93%E5%AD%98%E4%BB%A3%E7%90%86%E4%B8%AD%E9%97%B4%E4%BB%B6twemproxy%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/nginx_twemproxy.md) 7 | -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/01cee96e3952501d.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/01cee96e3952501d.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/0bd4d451a44cf2c9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/0bd4d451a44cf2c9.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/2b7ece6a68414958.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/2b7ece6a68414958.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/3b3bd23d3712575a3164189ddea27f4cc99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/3b3bd23d3712575a3164189ddea27f4cc99.jpg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/3fd1dd8ab214413e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/3fd1dd8ab214413e.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/41a1eba8a26bcfe8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/41a1eba8a26bcfe8.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/4557ecb3b2c33c06.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/4557ecb3b2c33c06.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/7460961a4ad0d6ac.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/7460961a4ad0d6ac.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/7644ae4924763d2c.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/7644ae4924763d2c.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/7f5e6afd9f878904.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/7f5e6afd9f878904.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/91829576d53d9be3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/91829576d53d9be3.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/95ddda34d5a9c517.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/95ddda34d5a9c517.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/96468d6b103c7154.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/96468d6b103c7154.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/a4390cc43dfea7e10ab21a302938810617f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/a4390cc43dfea7e10ab21a302938810617f.jpg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/a5a82174c29d6fc7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/a5a82174c29d6fc7.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/a78a74bbfb6ba1e7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/a78a74bbfb6ba1e7.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/ac35111fff96352e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/ac35111fff96352e.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/bd9dfb9a9e0078a9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/bd9dfb9a9e0078a9.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/e17fb44cf6f5adc4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/e17fb44cf6f5adc4.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/e5f56c7dae8196cf.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/e5f56c7dae8196cf.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/f54e1d56dbf27a1e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/f54e1d56dbf27a1e.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/fc9914ad170360c4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/y123456yz/middleware_development_learning/f597e73475f5fde92e3272b9d7e4d5940d946449/第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/img/fc9914ad170360c4.jpeg -------------------------------------------------------------------------------- /第二阶段-手把手教你做高性能代理中间件开发/nginx多进程高并发低时延机制在缓存代理中间件twemproxy中的应用/nginx_twemproxy.md: -------------------------------------------------------------------------------- 1 | _**0\. 技术交流群QQ**_ 2 | 对linux c/c++ nginx redis memcache twemproxy mongodb 中间件 存储引擎 分布式 高并发 高性能服务端等技术敢兴趣的同学可以加群:QQ交流群1(针对在校生)-(群号:568892619 ) QQ交流群2(针对已工作)-(581089275) 3 | 4 | 5 | 6 | * [nginx多进程、高性能、低时延、高可靠机制应用于缓存中间件twemproxy,对twemproxy进行多进程优化改造,提升TPS,降低时延,代理中间件长连接百万TPS/短连接五十万TPS实现原理-网页版](https://my.oschina.net/u/4087916/blog/3016162) 7 | 8 | _**1\. 开发背景**_ 9 | 10 |      现有开源缓存代理中间件有twemproxy、codis等,其中twemproxy为单进程单线程模型,只支持memcache单机版和redis单机版,都不支持集群版功能。 11 | 12 |      由于twemproxy无法利用多核特性,因此性能低下,短连接QPS大约为3W,长连接QPS大约为13W,同时某些场景时延抖动厉害。 13 | 14 |      为了适应公有云平台上业务方的高并发需求,因此决定借助于twemproxy来做二次开发,把nginx的高性能、高可靠、高并发机制引入到twemproxy中,通过master+多worker进程来实现七层转发功能。 15 | 16 | _**2    Twemproxy**_ 17 | 18 | _**2.1  Twemproxy简介**_ 19 | 20 | Twemproxy 是一个快速的单线程代理程序,支持 [Memcached](http://www.oschina.net/p/memcached) ASCII协议和更新的[Redis](http://www.oschina.net/p/redis)协议。它全部用C写成,使用Apache 2.0 License授权。支持以下特性: 21 | 22 | i)速度快 23 | 24 | ii)轻量级 25 | 26 | iii)维护持久的服务器连接 27 | 28 | iiii)启用请求和响应的管道 29 | 30 | iiiii)支持代理到多个后端缓存服务器 31 | 32 | iiiii)同时支持多个服务器池 33 | 34 | iiiiii)多个服务器自动分享数据 35 | 36 | iiiiiii)可同时连接后端多个缓存集群 37 | 38 | iiiiiiii)实现了完整的 [memcached ascii](https://github.com/twitter/twemproxy/blob/master/notes/memcache.txt) 和 [redis](https://github.com/twitter/twemproxy/blob/master/notes/redis.md) 协议. 39 | 40 | iiiiiiiii)服务器池配置简单,通过一个 YAML 文件即可 41 | 42 | iiiiiiiiii)一致性hash 43 | 44 | iiiiiiiiii)详细的监控统计信息 45 | 46 | iiiiiiiiiii)支持 Linux, *BSD, OS X and Solaris (SmartOS) 47 | 48 | iiiiiiiiiiii)支持设置HashTag 49 | 50 | iiiiiiiiiiiiiii)连接复用,内存复用,提高效率 51 | 52 | _**2.2 memcache缓存集群拓扑结构**_ 53 | 54 | ![](/img/3b3bd23d3712575a3164189ddea27f4cc99.jpeg) 55 | 56 | 图1 twemproxy缓存集群拓扑图 57 | 58 | 如上图所示,实际应用中业务程序通过轮询不同的twemproxy来提高qps,同时实现负载均衡。 59 | 60 | _**说明:官方memcache没有集群版和持久化功能,集群版和持久化功能由我们自己内部开发完成。**_ 61 | 62 | _**2.3 推特原生twemproxy瓶颈**_ 63 | 64 | 如今twemproxy凭借其高性能的优势, 在很多互联网公司得到了广泛的应用,已经占据了其不可动摇的地位, 然而在实际的生产环境中, 存在以下缺陷,如下: 65 | 66 | i)单进程单线程, 无法充分发挥服务器多核cpu的性能 67 | 68 | ii)当twemproxy qps短连接达到8000后,消耗cpu超过70%,时延陡增。 69 | 70 | iii)大流量下造成IO阻塞,无法处理更多请求,qps上不去,业务时延飙升 71 | 72 | iiii)维护成本高,如果想要充分发挥服务器的所有资源包括cpu、 网络io等,就必须建立多个twemproxy实例,维护成本高 73 | 74 | iiiii)扩容、升级不便 75 | 76 | 原生twemproxy进程呈现了下图现象:一个人干活,多个人围观。多核服务器只有一个cpu在工作,资源没有得到充分利用。 77 | 78 | ![](/img/a4390cc43dfea7e10ab21a302938810617f.jpeg) 79 | 80 | _**3\. Nginx**_ 81 | 82 | nginx是俄罗斯软件工程师Igor Sysoev开发的免费开源web服务器软件,聚焦于高性能,高并发和低内存消耗问题,因此成为业界公认的高性能服务器,并逐渐成为业内主流的web服务器。主要特点有: 83 | 84 | i)完全借助epoll机制实现异步操作,避免阻塞。 85 | 86 | ii)重复利用现有服务器的多核资源。 87 | 88 | iii)充分利用CPU 亲和性(affinity),把每个进程与固定CPU绑定在一起,给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性,减少进程调度开销。 89 | 90 | iiii)请求响应快 91 | 92 | iiiii)支持模块化开发,扩展性好 93 | 94 | iiiii)Master+多worker进程方式,确保worker进程可靠工作。当worker进程出错时,可以快速拉起新的worker子进程来提供服务。 95 | 96 | iiiiii)内存池、连接池等细节设计保障低内存消耗。 97 | 98 | iiiiii)热部署支持,master与worker进程分离设计模式,使其具有热部署功能。 99 | 100 | iiiiiii)升级方便,升级过程不会对业务造成任何伤害。 101 | 102 | Nginx多进程提供服务过程如下图所示: 103 | 104 | _**4    Nginx master+worker多进程机制在twemproxy中的应用**_ 105 | 106 | _**4.1  为什么选择nginx多进程机制做为参考?**_ 107 | 108 | Twemproxy和nginx都属于网络io密集型应用,都属于七层转发应用,时延要求较高,应用场景基本相同。 109 | 110 | Nginx充分利用了多核cpu资源,性能好,时延低。 111 | 112 | _**4.2  Master-worker多进程机制原理**_ 113 | 114 | Master-worker进程机制采用一个master进程来管理多个worker进程。每一个worker进程都是繁忙的,它们在真正地提供服务,master进程则很“清闲”,只负责监控管理worker进程, 包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。 115 | 116 | worker进程负责处理客户端的网络请求,多个worker进程同时处理来自客户端的不同请求,worker进程数可配置。 117 | 118 | _**4.3 多进程关键性能问题点**_ 119 | 120 | master-worker多进程模式需要解决的问题主要有: 121 | 122 | i)linux内核低版本(2.6以下版本), “惊群”问题 123 | 124 | ii) linux内核低版本(2.6以下版本),负载均衡问题 125 | 126 | iii)linux内核高版本(3.9以上版本)新特性如何利用 127 | 128 | iii)如何确保进程见高可靠通信 129 | 130 | iiii)如何减少worker进程在不同cpu切换的开销 131 | 132 | iiiii)master进程如何汇总各个工作进程的监控数据 133 | 134 | iiiiii)worker进程异常,如何快速恢复 135 | 136 | _ **4.3.1  linux内核低版本关键技术问题**_ 137 | 138 | 由于linux低内核版本缺陷,因此存在”惊群”、负载不均问题,解决办法完全依赖应用层代码保障。 139 | 140 | _** 4.3.1.1 如何解决“惊群”问题**_ 141 | 142 | 当客户端发起连接后,由于所有的worker子进程都监听着同一个端口,内核协议栈在检测到客户端连接后,会激活所有休眠的worker子进程,最终只会有一个子进程成功建立新连接,其他子进程都会accept失败。 143 | 144 | Accept失败的子进程是不应该被内核唤醒的,因为它们被唤醒的操作是多余的,占用本不应该被占用的系统资源,引起不必要的进程上下文切换,增加了系统开销,同时也影响了客户端连接的时延。 145 | 146 | “惊群”问题是多个子进程同时监听同一个端口引起的,因此解决的方法是同一时刻只让一个子进程监听服务器端口,这样新连接事件只会唤醒唯一正在监听端口的子进程。 147 | 148 | 因此“惊群”问题通过非阻塞的accept锁来实现进程互斥accept(),其原理是:在worker进程主循环中非阻塞trylock获取accept锁,如果trylock成功,则此进程把监听端口对应的fd通过epoll_ctl()加入到本进程自由的epoll事件集;如果trylock失败,则把监听fd从本进程对应的epoll事件集中清除。 149 | 150 | Nginx实现了两套互斥锁:基于原子操作和信号量实现的互斥锁、基于文件锁封装的互斥锁。考虑到锁的平台可移植性和通用性,改造twemproxy选择时,选择文件锁实现。 151 | 152 | 如果获取accept锁成功的进程占用锁时间过长,那么其他空闲进程在这段时间内无法获取到锁,从而无法接受新的连接。最终造成客户端连接相应时间变长,qps低,同时引起负载严重不均衡。为了解决该问题,选择通过post事件队列方式来提高性能,trylock获取到accept锁成功的进程,其工作流程如下: 153 | 154 | 1.trylock获取accept锁成功 155 | 156 | 2.通过epoll\_wait获取所有的事件信息,把监听到的所有accept事件信息加入accept\_post列表,把已有连接触发的读写事件信息加入read\_write\_post列表。 157 | 158 | 3.执行accept_post列表中的所有事件 159 | 160 | 4.Unlock锁 161 | 162 | 5.执行read\_write\_post列表中的事件。 163 | 164 | Worker进程主循环工作流程图如下: 165 | 166 | ![](/img/ac35111fff96352e.jpeg) 167 | 168 | 从上图可以看出,worker进程借助epoll来实现网络异步收发,客户端连接twemproxy的时候,worker进程循环检测客户端的各种网络事件和后端memcached的网络事件,并进行相应的处理。 169 | 170 | twemproxy各个进程整体网络i/o处理过程图如下: 171 | 172 | ![](/img/7f5e6afd9f878904.jpeg) 173 | 174 | _**4.3.1.2     如何解决“负载均衡“问题**_ 175 | 176 | 在多个子进程争抢处理同一个新连接事件时,一定只有一个worker子进程最终会成功建立连接,随后,它会一直处理这个连接直到连接关闭。这样,如果有的子进程“运气”很好,它们抢着建立并处理了大部分连接,其他子进程就只能处理少量连接,这对多核cpu架构下的应用很不利。理想情况下,每个子进程应该是平等的,每个worker子进程应该大致平均的处理客户端连接请求。如果worker子进程负载不均衡,必然影响整体服务的性能。 177 | 178 | nginx通过连接阈值机制来实现负载均衡,其原理如下:每个进程都有各自的最大连接数阈值max\_threshold和当前连接阈值数local\_threshold,和当前连接数阈值,进程每接收一个新的连接,local\_threshold增一,连接断开后,local\_threashold减一。如果local\_threshold超过max\_threshold,则不去获取accept锁,把accept机会留给其他进程,同时把local_threshold减1,这样下次就有机会获取accept锁,接收客户端连接了。 179 | 180 | 在实际业务应用中,有的业务采用长连接和twemproxy建立连接,连接数最大可能就几百连接,如果设置max_threshold阈值过大,多个连接如果同时压到twemproxy,则很容易引起所有连接被同一个进程获取从而造成不均衡。 181 | 182 | 为了尽量减少负载不均衡,在实际应用中,新增了epoll\_wait超时时间配置选项,把该超时时间设短,这样减少空闲进程在epoll\_wait上的等待事件,从而可以更快相应客户端连接,并有效避免负载不均衡。 183 | 184 | **4.3.2 Linux内核高版本TCP REUSEPORT特性如何利用** 185 | 186 | _**4.3.2.1 什么是reuseport?**_ 187 | 188 | reuseport是一种套接字复用机制,它允许你将多个套接字bind在同一个IP地址/端口对上,这样一来,就可以建立多个服务来接受到同一个端口的连接。 189 | 190 | _**4.3.2.2 支持reuseport和不支持reuseport的区别**_ 191 | 192 | 如果linux内核版本小于3.9,则不支持reuseport(注:部分centos发行版在低版本中已经打了reuseport patch,所以部分linux低版本发行版本也支持该特性)。 193 | 194 | 不支持该特性的内核,一个ip+port组合,只能被监听bind一次。这样在多核环境下,往往只能有一个线程(或者进程)是listener,也就是同一时刻只能由一个进程或者线程做accept处理,在高并发情况下,往往这就是性能瓶颈。其网络模型如下: 195 | 196 | ![](/img/0bd4d451a44cf2c9.jpeg) 197 | 198 | 在Linux kernel 3.9带来了reuseport特性,它可以解决上面的问题,其网络模型如下: 199 | 200 | ![](/img/01cee96e3952501d.jpeg) 201 | 202 | reuseport是支持多个进程或者线程绑定到同一端口,提高服务器程序的吞吐性能,其优点体现在如下几个方面: 203 | 204 | i)允许多个套接字 bind()/listen() 同一个TCP/UDP端口 205 | 206 | ii)每一个线程拥有自己的服务器套接字 207 | 208 | iii)在服务器套接字上没有了锁的竞争,因为每个进程一个服务器套接字 209 | 210 | iiii)内核层面实现负载均衡 211 | 212 | iiiii)安全层面,监听同一个端口的套接字只能位于同一个用户下面 213 | 214 | _**4.3.3 Master进程和worker进程如何通信?**_ 215 | 216 | 由于master进程需要实时获取worker进程的工作状态,并实时汇总worker进程的各种统计信息,所以选择一种可靠的进程间通信方式必不可少。 217 | 218 | 在twemproxy改造过程中,直接参考nginx的信号量机制和channel机制(依靠socketpair)来实现父子进程见通信。Master进程通过信号量机制来检测子进程是否异常,从而快速直接的反应出来;此外,借助socketpair,封装出channel接口来完成父子进程见异步通信,master进程依靠该机制来统计子进程的各种统计信息并汇总,通过获取来自master的汇总信息来判断整个twemproxy中间件的稳定性、可靠性。 219 | 220 | 配置下发过程:主进程接收实时配置信息,然后通过channel机制发送给所有的worker进程,各个worker进程收到配置信息后应答给工作进程。流程如下: 221 | 222 | ![](/img/e5f56c7dae8196cf.jpeg) 223 | 224 | 获取监控信息流程和配置下发流程基本相同,master进程收到各个工作进程的应答后,由master进程做统一汇总,然后发送给客户端。 225 | 226 | _**4.3.4 如何减少worker进程在不同cpu切换的开销**_ 227 | 228 | CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。 229 | 230 | Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。具体参考sched_setaffinity函数。 231 | 232 | _**4.3.5 worker进程异常如何减少对业务的影响?**_ 233 | 234 | 在实际线上环境中,经常出现这样的情况:某个多线程服务跑几个月后,因为未知原因进程挂了,最终造成整个服务都会不可用。 235 | 236 | 这时候,master+多worker的多进程模型就体现了它的优势,如果代码有隐藏的并且不容易触发的bug,某个时候如果某个请求触发了这个bug,则处理这个请求的worker进程会段错误退出。但是其他worker进程不会收到任何的影响,也就是说如果一个改造后的twemproxy起了20个worker进程,某个时候一个隐藏bug被某个请求触发,则只有处理该请求的进程段错误异常,其他19个进程不会受到任何影响,该隐藏bug触发后影响面仅为5%。如果是多线程模型,则影响面会是100%。 237 | 238 | 如果某个worker进程挂了,master父进程会感知到这个信号,然后重新拉起一个worker进程,实现瞬间无感知”拉起”恢复。以下为模拟触发异常段错误流程: 239 | 240 | ![](/img/2b7ece6a68414958.jpeg) 241 | 242 | 如上图所示,杀掉31420 worker进程后,master进程会立马在拉起一个31451工作进程,实现了快速恢复。 243 | 244 | 多进程异常,自动”拉起”功能源码,可以参考如下demo: 245 | 246 | https://github.com/y123456yz/reading-code-of-nginx-1.9.2/blob/master/nginx-1.9.2/src/demo.c 247 | 248 | _**5    网络优化**_ 249 | 250 | _**5.1 网卡多队列**_ 251 | 252 | 在实际上线后,发现软中断过高,几乎大部分都集中在一个或者几个CPU上,严重影响客户端连接和数据转发,qps上不去,时延抖动厉害。 253 | 254 | RSS(Receive Side Scaling)是网卡的硬件特性,实现了多队列,可以将不同的流分发到不同的CPU上。支持RSS的网卡,通过多队列技术,每个队列对应一个中断号,通过对每个中断的绑定,可以实现网卡中断在cpu多核上的分配,最终达到负载均衡的作用。 255 | 256 | _**5.2 可怕的40ms**_ 257 | 258 | 原生twemproxy在线上跑得过程中,发现时延波动很大,抓包发现其中部分数据包应答出现了40ms左右的时延,拉高了整体时延抓包如下(借助tcprstat工具): 259 | 260 | ![](/img/7644ae4924763d2c.jpeg) 261 | 262 | 解决办法如下:在recv系统调用后,调用一次setsockopt函数,设置TCP_QUICKACK。代码修改如下: 263 | 264 | ![](/img/95ddda34d5a9c517.jpeg) 265 | 266 | _**6   Twemproxy改造前后性能对比   (时延、qps对比)**_ 267 | 268 | _**6.1  线上真实流量时延对比**_ 269 | 270 | _**6.1.1  改造前线上twemproxy集群时延**_ 271 | 272 | 线上集群完全采用开源twemproxy做代理,架构如下: 273 | 274 | ![](/img/96468d6b103c7154.jpeg) 275 | 276 | 未改造前线上twemproxy+memcache集群,qps=5000~6000,长连接,客户端时延分布如下图所示: 277 | 278 | ![](/img/bd9dfb9a9e0078a9.jpeg) 279 | 280 | ![](/img/f54e1d56dbf27a1e.jpeg) 281 | 282 | 在twemproxy机器上使用tcprstat监控到的网卡时延如下: 283 | 284 | ![](/img/e17fb44cf6f5adc4.jpeg) 285 | 286 | 从上面两个图可以看出,采用原生twemproxy,时延高,同时抖动厉害。 287 | 288 | **_6.1.2 参照nginx改造后的twemproxy时延_** 289 | 290 | 线上集群一个twemproxy采用官方原生twemproxy,另一个为改造后的twemproxy,其中改造后的twemproxy配置worker进程数为1,保持和原生开源twemproxy进程数一致,架构如下: 291 | 292 | ![](/img/3fd1dd8ab214413e.jpeg) 293 | 294 | 替换线上集群两个代理中的一个后(影响50%流量),长连接,qps=5000~6000,客户端埋点监控时延分布如下: 295 | 296 | ![](/img/4557ecb3b2c33c06.jpeg) 297 | 298 | ![](/img/a78a74bbfb6ba1e7.jpeg) 299 | 300 | ![](/img/41a1eba8a26bcfe8.jpeg) 301 | 302 | 替换两个proxy中的一个后,使用tcprstat在代理集群上面查看两个代理的时延分布如下: 303 | 304 | 原生twemproxy节点机器上的时延分布: 305 | 306 | ![](/img/fc9914ad170360c4.jpeg) 307 | 308 | 另一个改造后的twemproxy节点机器上的时延分布: 309 | 310 | ![](/img/a5a82174c29d6fc7.jpeg) 311 | 312 | 总结:替换线上两个proxy中的一个后,客户端时间降低了一倍,如果线上集群两个代理都替换为改造后的twemproxy,客户端监控时延预计会再降低一倍,总体时延降低3倍左右。 313 | 314 | 此外,从监控可以看出,改造后的twemproxy时延更低,更加稳定,无任何波动。 315 | 316 | _**6.2 参考nginx多进程改造后的twemproxy线下压测结果(开启reuseport功能)**_ 317 | 318 | 监听同一个端口,数据长度100字节,压测结果如下: 319 | 320 |    linux内核版本:linux-3.10 321 | 322 |    物理机机型: M10(48 cpu) 323 | 324 | ![](/img/7460961a4ad0d6ac.jpeg) 325 | 326 | 多进程监听同一个端口,数据长度150字节,压测结果如下: 327 | 328 |    linux内核版本:linux-3.10 329 | 330 |    物理机机型: TS60 (24 cpu) 331 | 332 | ![](/img/91829576d53d9be3.jpeg) 333 | 334 | _**7   总结**_ 335 | 336 | _**7.1 多进程、多线程机制选择**_ 337 | 338 | 选择参照nginx多进程机制,而不选择多线程实现原因主要有: 339 | 340 | 1) 多进程机制无锁操作,实现更容易 341 | 342 | 2) 多进程的代理,整个worker进程无任何锁操作,性能更好 343 | 344 | 3) 如果是多线程方式,如果代码出现bug段错误,则整个进程挂掉,整个服务不可用。而如果是多进程方式,因为bug触发某个worker进程段错误异常,其他工作进程不会受到如何影响,20个worker进程,如果触发异常,同一时刻只有有1/20的流量受到影响。而如果是多线程模式,则100%的流量会受到影响。 345 | 346 | 4) worker进程异常退出后,master进程立马感知拉起一个新进程提供服务,可靠性更高。 347 | 348 | 5)  配置热加载、程序热升级功能实现更加容易 349 | 350 | _**7.2 参照nginx改造后的twemproxy特性**_ 351 | 352 | 支持nginx几乎所有的优秀特性,同时也根据自己实际情况新增加了自有特性: 353 | 354 | 1)  master+多worker进程机制 355 | 356 | 2) 适配所有linux内核版本,内核低版本惊群问题避免支持 357 | 358 | 3) quic_ack支持 359 | 360 | 4) reuser_port适配支持 361 | 362 | 5) worker进程异常,master进程自动拉起功能支持 363 | 364 | 6) 90%、95%、98%、100%平均时延统计功能支持 365 | 366 | 7) memcache单机版、集群版支持 367 | 368 | 8) redis单机版、集群版支持 369 | 370 | 9) 二进制协议、文本协议同时支持 371 | 372 | 10) redis、memcache集群在线扩容、缩容、数据迁移支持,扩缩容、数据迁移过程对业务无任何影响。 373 | 374 | 11) 多租户支持,一个代理可以接多个memcache、redis集群,并支持混部。 375 | 376 | 12) mget、gets、sets等批量处理命令优化处理 377 | 378 | 13) 慢响应日志记录功能支持 379 | 380 | 14) 内存参数实时修改支持 381 | 382 | 15) 详细的集群监控统计功能 383 | 384 | 16) CPU亲缘性自添加 385 | 386 | 17)内存配置动态实时修改 387 | 388 | _**7.3后期计划**_ 389 | 390 |    添加如下功能: 391 | 392 |      i) 配置文件热加载支持。 393 | 394 |      ii) 代码热升级功能支持。 395 | 396 | _**7.4  长远规划展望**_ 397 | 398 | 抽象出一款类似nginx的高性能代理软件,nginx支持http协议,我们的支持tcp协议代理,覆盖nginx所有功能,包括前面提到的所有功能,同时支持模块化开发。这样,很多的tcp协议代理就无需关心网络架构底层实现,只需要根据需要开发对应的协议解析模块,和自己关心的统计、审计等功能功能,降低开发成本。现有开源的中间件,很大一部分都是tcp的,有自己的私有tcp协议,把这个抽象出来,开发成本会更低 399 | 400 | 对nginx有兴趣的可以源码分析参考: 401 | 402 | [https://github.com/y123456yz/reading-code-of-nginx-1.9.2](https://github.com/y123456yz/reading-code-of-nginx-1.9.2) 403 | 404 | 内核网卡时延分析工具: 405 | 406 | [https://github.com/y123456yz/tcprstat](https://github.com/y123456yz/tcprstat) 407 | 408 | twemproxy源码分析: 409 | 410 | [https://github.com/y123456yz/Reading-and-comprehense-twemproxy0.4.1](https://github.com/y123456yz/Reading-and-comprehense-twemproxy0.4.1) 411 | 412 | 内核协议栈延迟确认机制: 413 | 414 | https://github.com/y123456yz/Reading-and-comprehense-linux-Kernel-network-protocol-stack 415 | 416 | 417 | 对linux c/c++ nginx redis memcache twemproxy mongodb 中间件 存储引擎 分布式 高并发 高性能服务端等技术敢兴趣的同学可以加群: 418 | QQ交流群1(针对在校生)-(群号:568892619 ) 419 | QQ交流群2(针对已工作)-(581089275) -------------------------------------------------------------------------------- /第四阶段-mongodb数据库/development_mongodb.md: -------------------------------------------------------------------------------- 1 |      Mongodb数据库版本包含企业版本和社区版本,他们的区别是企业版本相比有更多功能,使用企业版本必须购买付费,所以mongodb部分核心功能没有开源。为了增强mongodb集群稳定性,企业需要对开源版本内核进行二次开发,主要包括以下功能模块的开发(增加以下功能后,会有很好的收益): 2 | 3 | 1. 各种操作时延统计。 4 | 2. Mongos代理慢日志记录。 5 | 3. 普通用户权限细化控制,危险操作过滤。 6 | 4. 危险操作日志审计。 7 | 5. 所有增删改操作sql异步日志记录。 8 | 6. mongodb增加热备功能。 9 | ...... 10 | 11 | 12 |    如果对mongodb数据库和wiredtiger存储引擎源码感兴趣,可以参考如下注释: 13 | 14 | [https://github.com/y123456yz/reading-and-annotate-mongodb-3.6.1](https://github.com/y123456yz/reading-and-annotate-mongodb-3.6.1) --------------------------------------------------------------------------------