商汇粹外网资源平台

搜索
查看: 2010|回复: 5

有哪些轻量级适合阅读的优秀 C++ 开源项目?

[复制链接]

该用户从未签到

2

主题

3

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2022-10-22 10:43:04 | 显示全部楼层 |阅读模式
代码风格比较好,代码量在5000左右,短小精悍,结构清晰,谢谢推荐。
回复

使用道具 举报

该用户从未签到

3

主题

4

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-10-22 10:54:44 | 显示全部楼层
我关注编译领域的项目比较多,我就简单介绍几个编译的小项目:
1、LLVM的tutorial文档(LLVM Tutorial: Table of Contents)中,有一个My First Language Frontend with LLVM Tutorial的小项目,用了很少的代码覆盖了编译器的各个环节,适合没有编译器经验的人用来熟悉编译器的各个环节:
My First Language Frontend with LLVM Tutorial
这个项目只要求有C++经验,并不要求有编译器开发经验。
2、shecc : self-hosting and educational C compiler
一个非常小巧的编译器,学习用非常好,而且有arm和riscv版本。
sheccis built from scratch, targeted at 32-bit Arm and RISC-V architecture, as a self-compiling compiler for a subset of the C language.
https://github.com/jserv/shecc
3、llvm-tutor
llvm-tutor是一个学习LLVM pass的小项目,列出了一系列可以运行并且适合学习的pass,这对于想了解和熟悉LLVM pass机制和其实现的小伙伴非常方便。
https://github.com/banach-space/llvm-tutor
回复

使用道具 举报

该用户从未签到

2

主题

7

帖子

56

积分

注册会员

Rank: 2

积分
56
发表于 2022-10-22 11:06:24 | 显示全部楼层
要聊轻量级那就可以点名Workflow,不仅开发使用轻便简洁,还非常适合作为入门后端的工业级项目来阅读。接口精简、内部实现轻巧、架构对称清晰、对C++的使用非常克制,是去年开源的并行计算调度和高性能网络通讯融为一体的异步框架: GitHub - sogou/workflow: C++ Parallel Computing and Asynchronous Networking Engine

  • 无依赖,网络基于epoll/kqueue/iocp
  • 上手快,编译快,体积小,适合嵌入式
  • 不做全家桶,功能非常克制
  • 代码风格小清新
  • 主打高性能计算通信的场景
所以,基于以上特点,我们通过阅读代码,可以获得什么:

  • 快速实现简单的Server/Client;
  • 了解异步,通信、计算、文件等的融合;
  • 工业级项目如何实现高性能网络调度;
  • 新一代任务流模型的思路:程序 = 协议 + 算法 + 任务流;
  • 现有协议全部自实现,可直接使用:Http/Redis/MySQL/Kafka/DNS/WebSocket,不需要网络的同学无需关心这部分,计算模块也可以独立拿出来学习或使用。

以及,tutorial教程系列是从01-13循序渐进由浅入深地编排,每一个都可以独立run起来,欢迎到GitHub围观,感受写C++代码也可以如此简洁优雅的体验~✪ ω ✪
回复

使用道具 举报

该用户从未签到

2

主题

7

帖子

29

积分

新手上路

Rank: 1

积分
29
发表于 2022-10-22 11:18:04 | 显示全部楼层
给大家整理了一些github上热门C++开源项目,非常适合基础性练习,收藏之后不要忘记练习!!!
一、基础
1.1 一个项目入门 C++ 足以:CPlusPlusThings
CPlusPlusThings 是国人开源一个 C++ 学习项目。它系统地将 C++ 学习分为了【基础进阶】、【实战系列】、【C++2.0 新特性】、【设计模式】和【STL 源码剖析】、【并发编程】、【C++ 惯用法】、【学习课程】、【工具】、【拓展】。
作为一个全面系统的 C++ 学习项目,CPlusPlusThings 是优秀的,它合理地安排了 10 Days 的实战部分,在实战中了解语法和函数用法,唯一不足的是,在注释部分有些不尽人意,对部分新手程序员并不是很友好。【基础进阶】部分内容:

  • const 那些事
  • static 那些事
  • decltype 那些事
  • 引用与指针那些事
  • 宏那些事
GitHub 地址→https://github.com/Light-City/CPlusPlusThings

1.2 基础部分之算法:C-Plus-Plus
C-Plus-Plus 是收录用 C++ 实现的各种算法的集合,并按照 MIT 许可协议进行授权。这些算法涵盖了计算机科学、数学和统计学、数据科学、机器学习、工程等各种主题。除外,你可能会发现针对同一目标的多个实现使用不同的算法策略和优化。
GitHub 地址→https://github.com/TheAlgorithms/C-Plus-Plus

二、进阶
2.1 现代 C++:modern-cpp-tutorial
modern-cpp-tutorial 是现代 C++ 教程,它的目的是提供关于现代 C++(2020 年前)的相关特性的全面介绍。除了介绍了代码之外,它还尽可能简单地介绍了其技术需求的历史背景,这对理解为什么会出现这些特性提供了很大的帮助。
GitHub 地址→https://github.com/changkun/modern-cpp-tutorial


2.2 进阶指南:CppTemplateTutorial
CppTemplateTutorial 为中文的 C++ Template 的教学指南。与知名书籍 C++ Templates 不同,该系列教程将 C++ Templates 作为一门图灵完备的语言来讲授,以求帮助读者对 Meta-Programming 融会贯通。本项目写作初衷,就是通过 “编程语言” 的视角,介绍一个简单、清晰的 “模板语言”。我会尽可能地将模板的诸多要素连串起来,用一些简单的例子帮助读者学习这门 “语言”,让读者在编写、阅读模板代码的时候,能像 if(exp) { dosomething(); } 一样的信手拈来,让 “模板元编程” 技术成为读者牢固掌握、可举一反三的有用技能。
适合熟悉 C++ 的基本语法、使用过 STL、熟悉一些常用的算法,以及递归等程序设计方法的 C++ 学习者阅读。虽然项目章节文章写的深入浅出,不过唯一的遗憾是尚未完成所有章节内容。进度如下:

  • 0.前言
  • 1.Template 的基本语法
  • 2.模板元编程基础
  • 3.深入理解特化与偏特化
  • 4.元编程下的数据结构与算法 (尚未开始)
  • 5.模板的进阶技巧(尚未开始)
  • 6.模板的威力:从 foreach, transform 到 Linq(尚未开始)
  • 7.结语:讨论有益,争端无用(尚未开始)
GitHub 地址→https://github.com/wuye9036/CppTemplateTutorial

三、动手实战
3.1 来实践一下:MyTinySTL
当你学习完 C++ 的“书本”知识后,是不是有些手痒了呢?MyTinySTL 这个注释详细、实践夯实基础的项目便是你 C++ 学习之旅的下一站。作为新手练习用途,MyTinySTL 的作者 Alinshans 用 C++11 重新复写了一个小型 STL(容器库+算法库)。代码结构清晰规范、包含中文文档与注释,并且自带一个简单的测试框架,适合 C++ 新手来实践一番。
GitHub 地址→https://github.com/Alinshans/MyTinySTL

3.2 重温同年俄罗斯方块:Tinytetris
Tinytetris 是一个用 C++ 编写的终端版俄罗斯方块游戏。它提供了两个版本的源码,分为注释版和库版,注释较多易于理解和学习。
GitHub 地址→https://github.com/taylorconor/tinytetris


3.3 学习下大佬们的代码:calculator
微软开源的 Windows 系统预装的计算器工具。该工具提供标准、科学、程序员计算器的功能,以及各种度量单位和货币之间的转换功能。快来看看微软工程师编写的代码吧!学习大厂的编码规范、项目结构之类的,提高阅读源码的能力。英文的项目且没有讲解部分,需要通过阅读源码学习,难度较高。
GitHub 地址→https://github.com/microsoft/calculator

作者:HelloGithub 链接:https://www.zhihu.com/question/332778359/answer/2228040329
下面谈下适合C++入门级保姆教程,只要你肯坚持,没有什么学不好。
书籍+视频+项目!
视频推荐黑马基础+侯捷大佬全集进阶!
按照这个路线去学习C++,每个阶段都帮你规划好了学习时间,只要你努力且认真的去学了, 保证帮你既高效又扎实的学好C++:

一、C++基础(3个月)

1、面向对象的三大特性:封装、继承、多态
2、类的访问权限:private、protected、public
3、类的构造函数、析构函数、赋值函数、拷贝函数
4、移动构造函数与拷贝构造函数对比
5、深拷贝与浅拷贝的区别
6、空类有哪些函数?空类的大小?
7、内存分区:全局区、堆区、栈区、常量区、代码区
8、C++与C的区别
9、struct与class的区别
10、struct内存对齐
11、new/delete与malloc/free的区别
12、内存泄露的情况
13、sizeof与strlen对比
14、指针与引用的区别
15、野指针产生与避免
16、多态:动态多态、静态多态
17、虚函数实现动态多态的原理、虚函数与纯虚函数的区别
18、继承时,父类的析构函数是否为虚函数?构造函数能不能为虚函数?为什么?
19、静态多态:重写、重载、模板
20、static关键字:修饰局部变量、全局变量、类中成员变量、类中成员函数
21、const关键字:修饰变量、指针、类对象、类中成员函数
22、extern关键字:修饰全局变量
23、volatile关键字:避免编译器指令优化
24、四种类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast
25、右值引用
26、std::move函数
27、四种智能指针及底层实现:auto_ptr、unique_ptr、shared_ptr、weak_ptr
28、shared_ptr中的循环引用怎么解决?(weak_ptr)
29、vector与list比较
30、vector迭代器失效的情况
31、map与unordered_map对比
32、set与unordered_set对比
33、STL容器空间配置器
参考书籍:《C++ Primer》(第5版)、《STL源码剖析》、《深度探索C++对象模型》

下载地址:

  • 链接:https://pan.baidu.com/s/1qqAR6iqjur1sfmzeZjcrwg
  • 提取码:m6gx
二、计算机网络(1个月)

1、OSI7层网络模型:应用层、表示层、会话层、运输层、网络层、链路层、物理层
2、TCP/IP四层网络模型:应用层、运输层、网际层、接口层
综合OSI与TCP/IP模型,学习五层网络模型:
从上向下架构:应用层、运输层、网络层、链路层、物理层
链路层:
3、MTU
4、MAC地址
网络层:
5、地址解析协议
6、为啥有IP地址还需要MAC地址?同理,为啥有了MAC地址还需要IP地址?
7、网络层转发数据报的流程
8、子网划分、子网掩码
9、网络控制报文协议ICMP
10、ICMP应用举例:PING、traceroute
运输层:
11、TCP与UDP的区别及应用场景
12、TCP首部报文格式(SYN、ACK、FIN、RST必须知道)
13、TCP滑动窗口原理
14、TCP超时重传时间选择
15、TCP流程控制
16、TCP拥塞控制(一定要弄清楚与流量控制的区别)
17、TCP三次握手及状态变化。为啥不是两次握手?
18、TCP四次挥手及状态变化。为啥不是三次挥手?
19、TCP连接释放中TIME_WAIT状态的作用
20、SYN泛洪攻击。如何解决?
21、TCP粘包
22、TCP心跳包
23、路由器与交换机的区别
24、UDP如何实现可靠传输
应用层:
25、DNS域名系统。采用TCP还是UDP协议?为什么?
26、FTP协议(了解)
27、HTTP请求报文与响应报文首部结构
28、HTTP1.0、HTTP1.1、HTTP2.0对比
29、HTTP与HTTPS对比
30、HTTPS加密流程
31、方法:GET、HEAD、POST、PUT、DELETE
32、状态码:1、2、3、4、5**
33、cookie与session区别
34、输入一个URL到显示页面的流程(越详细越好,搞明白这个,网络这块就差不多了)
参考书籍:《计算机网络》(第5版)、《TCP/IP详解卷1:协议》、《图解HTTP》
下载地址:

  • 链接:https://pan.baidu.com/s/1fRYNn3E0yEOLiQUSyBlxKg
  • 提取码:69dc
三、操作系统(1个月)

1、进程与线程区别
2、线程同步的方式:互斥锁、自旋锁、读写锁、条件变量
3、互斥锁与自旋锁的底层区别
4、孤儿进程与僵尸进程
5、死锁及避免
6、多线程与多进程比较
7、进程间通信:PIPE、FIFO、消息队列、信号量、共享内存、socket
8、管道与消息队列对比
9、fork进程的底层:读时共享,写时复制
10、线程上下文切换的流程
11、进程上下文切换的流程
12、进程的调度算法
13、阻塞IO与非阻塞IO
14、同步与异步的概念
15、静态链接与动态链接的过程
16、虚拟内存概念(非常重要)
17、MMU地址翻译的具体流程
18、缺页处理过程
19、缺页置换算法:最久未使用算法、先进先出算法、最佳置换算法
ps:操作系统的内容看起来不是很多,实际上每个问题答案的底层原理要弄懂还是很考验基本功的。比如:互斥锁与自旋锁的区别,实际上涉及到阻塞时线程的状态是不一样的。互斥锁阻塞的线程是挂起的,此时系统会优先执行其它可执行的线程,就会将阻塞的线程切换到可执行线程,而当临界区执行的时间非常短时,此时线程切换频繁、开销较大,此时就会采用自旋锁的方式,让阻塞的线程处于忙等状态。


参考书籍:《深入理解计算机系统》
下载地址:

  • 链接:https://pan.baidu.com/s/1RoDN317X-C6izxY6CwuxTA
  • 提取码:iy8u

四、网络编程(1个月)

1、IO多路复用:select、poll、epoll的区别(非常重要,几乎必问,回答得越底层越好,要会使用)
2、手撕一个最简单的server端服务器(socket、bind、listen、accept这四个API一定要非常熟练)
3、线程池
4、基于事件驱动的reactor模式
5、边沿触发与水平触发的区别
6、非阻塞IO与阻塞IO区别
参考书籍:《Unix网络编程》
ps:网络编程掌握以上几点就够了,要搞明白还是要花很久时间的。
下载地址:

  • 链接:https://pan.baidu.com/s/1MD9WAAmSOCz5fhlUMU0bsg
  • 密码:bwyt

五、数据结构与算法及刷题(2个月)

1、数组
2、链表
3、栈
4、队列
5、堆
6、二叉树:二叉搜索树、平衡树、红黑树
7、B树、B+树
8、哈希表及哈希冲突
9、排序算法:冒泡排序、简单选择排序、插入排序、希尔排序、归并排序、堆排序、快速排序
(要求能够面试时手写出堆排序和快速排序)
10、二分法:旋转数组找target
11、回溯法:全排列、复原IP地址
12、动态规划(掌握基本的动态规划的几个题其实就够了,如:斐波那契数列、接雨水、股票的最佳买入时机)
参考书籍:《图解算法》《剑指offer》
ps:建议刷题与数据结构算法同时进行,这样理解得更深入。刷题网站leetcode,刷完《剑指offer》其实就能解决大部分面试手撕了。
书籍下载:

  • 链接:https://pan.baidu.com/s/1GZEp8KI1Fm0U4Uek7BOWAw
  • 提取码:am4o
六、mySQL数据库(7天~15天)

1、数据存储引擎:InnoDB、myISAM、Memory
2、数据库索引类型及原理:B+树索引、哈希表索引
3、锁:悲观锁、乐观锁
4、事务:事务的四大特性(ACID)、事务并发的三大问题、事务隔离级别及实现原理
5、多版本并发控制实现机制(MCVV)原理
参考书籍:《高性能MySQL》
ps:这里也可以参考本人写的博客:mysql知识点总结。
下载地址:

  • 链接:https://pan.baidu.com/s/1-_9Cwq1qCxAr041nDWe0sg
  • 提取码:fy0y
七、项目(2个月)

如果时间够的话就可以写一个项目,当然大部分人写的项目都是一个烂大街的项目,也就是“web高性能服务器”。其实就是根据陈硕大神写的《Linux高性能服务器编程:使用muduo C++网络库》进行改编,当然啦,读懂这本书还是很耗时的,学习其中的思想也会受益匪浅的。
总结
按照上面推荐的内容来学习的话,要学习得深入一点的话1年的时间肯定需要的,甚至2年也不足为其。当然对于非科班的学生来说,大部分都没有充足的时间的,这时候建议尽量把C++基础、计算机网络、操作系统、网络编程、数据结构与算法这五个部分的内容学得很扎实,大概6个月的时间。
然后说一下我本人的学习情况:
2020年3~8月:疫情在家大概3月份开始准备学习C++,因为在家效率极低,玩三天学一天,到7、8月份也只是会用C++,然后写了个MFC的小项目练了练手。
2020年9月:前半个月学习计算机网络,后半个月学习深入理解计算机系统(当然第一遍只看懂个大概)
2020年10月-12月:写多线程服务器项目(即改编muduo),这个过程中伴随学习网络编程、操作系统、C++各种知识(之前C++只学了皮毛)
2021年1月-2月:学习数据结构与算法并刷题,刷了不到200题。(其中寒假在家玩了半个月)
2021年3月:开启海投模式,先投了一批小公司,基本都简历挂,总算有几个小厂给了面试机会,边面试边复习。3月中旬开始投大厂,除了美团一面挂,字节、腾讯、百度面试都很顺利,没挂过,清明之前拿到了字节的口头offer。
2021年4月:字节的正式offer邮件。(说实话我的学习效率不是很高,因为我喜欢睡懒觉。)
资料推荐:华为最牛逼的c++ 基础与提高PDF

作者:华中大守门员
来源:实习总结及C++后端开发学习路线总结_笔经面经_牛客网
八、推荐阅读书单
正如侯捷老师所说C++ 相关的书籍也非常多,如天上繁星。 广博如四库全书者有 《The C++Programming Language》 《C++ Primer》,深奥如山重水复者有《The Annotated C++ Reference Manual》、《Inside The C++ Object Model》,细说历史者有《The Design And Evolution Of C++》、《Rumiations on C++》,独沽一味者有《Polymorphism in C++》、《Genericity in C++》,独树一帜者有《Design Patterns》、《C++ FAQs》,程序库大全有之《The C++ Standard Library》.....
至于书单的话,可以看下这个C++ 最全书单,包含几十本经典的PDF和下载方式:

(PS:侯捷 C++ 视频课程一直都被看过的同学广为推荐,最近发现 B 站关于侯捷老师的 C++ 视频课程几乎全部被下架了,于是自己搜集了一套非常全的视频,大家赶紧去下载保存备用

他们让知识的传播变得更加的便捷,降低了后来者的学习门槛,当然啦也变相加重了内卷化.
最后给大家介绍一份计算机经典书籍,送大家一份硬核书籍资源:读大学跟工作期间,我买了很多书,大部分是一些技术书籍,也收集了这些书籍的电子版,都在这里,现在免费share给大家,包括了计算机类常用电子书,包括C,C++,Java,Python,Java,Linux,Go等,希望大家能认真阅读。点击下方链接直达获取:


祝大家前程似锦,在编码的道路上一马平川。
码字不易,硬核码字更难,希望大家不要吝啬自己的鼓励,给我 :
@码农出击
一个点赞,鼓励下我!
回复

使用道具 举报

该用户从未签到

2

主题

8

帖子

34

积分

新手上路

Rank: 1

积分
34
发表于 2022-10-22 11:29:44 | 显示全部楼层
1. FreeNOS

深入理解C++,以及操作系统原理。FreeNOS(Free Niek的操作系统)是一个基于C++的基于微内核的学习操作系统。这是一个用C++实现的微内核的操作系统,各种宏内核中的服务作为一个独立的services在微内核中,基于消息的通信方式,这点其实跟mac内核中的mach那部分机制相似。
除了是一个操作系统的实现,另外从中也能很好的学习到OOP的设计方式,整个代码风格特别好,完全基于面相对象,还有一些常见的设计模式,在接触了这个开源项目之后,才了解,代码风格,注释,doxygen,scons,设计模式。

  • Intel x86(PC)和ARMv6/ARMv7体系结构(Raspberry Pi 1,2,3,Allwinner H2+/H3)
  • Virtual memory
  • 简单任务调度
  • 进程间通信(IPC)
  • 支持MPI的对称多处理
  • 设备:VGA/键盘控制台(也支持Ed的libtekenhttp://80386.nl/projects/libteken/)、i8250串行UART PCI主机控制器CMOS RTC时钟ATA主机控制器USB控制器和(根)集线器(仅限Raspberry Pi)环回网络和SMSC95xx以太网(仅限Raspberry Pi)
  • 文件系统:虚拟文件系统(VFS)临时文件系统(TmpFS)Linnenbank文件系统(LinnFS)
  • 网络(IP、UDP、ICMP,仅适用于Raspberry pi 1)
  • POSIX、ansic库
  • 动态共享内存
  • 全自动检测
  • 使用jenkins的自动化连续集成(http://www.jenkins.io)还有Vagrant(http://www.vagrantup.com)
  • 所有来源都记录在Doxygen中(http://www.doxygen.org/)
  • C++中从头开始编写的用户和内核代码
  • 非常小的微内核(~2k行的C++代码,包括汇编中的一小部分)
  • 使用最近的GCC生成(http://gcc.gnu.org/),LLVM(http://www.llvm.org/)还有烤饼(http://www.scons.org/)POSIX系统上的版本
2.Boost
Boost 库是一个经过千锤百炼、可移植、提供源代码的C++ 库,作为标准库的后备,是C++ 标准化进程的发动机之一。 Boost 库由C++标准委员会库工作组成员发起,在C++ 社区中影响甚大,其成员已近2000 人。 Boost 库为我们带来了最新、最酷、最实用的技术,是不折不扣的“ 准” 标准库。
网址:http://www.boost.org/
3 muduo
一个基于 Reactor 模式的现代 C++ 网络库,它采用非阻塞 IO 模型,基于事件驱动和回调,支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。
地址:GitHub - AlexStocks/muduo: a muduo branch without boost and cmake, just using c++11 and makefile
4 ACE 和 libevent  一个是重量级的网络库, 一个是轻量级的网络库。仔细想想,现在那个程序不用网络啊。不懂网络, 你将寸步难行啊。 熟悉这两个开源库的前提是你必须懂socket的原理, 给大家推荐的好书就是 《UNIX网络编程》,看懂这本书就可以了, 其他的什么《windows网络编程》这些都不用看, 因为网络编程,你学会了伯克利的套接字, 你就可以在任何平台上进行网络编程了, 不需要学什么windows下的网络编程, 因为windows下的网络也是从伯克利套接字搞过来的, 如果你学习《windows网络编程》, 那么你那天在linux,unix下进行网络编程,你还得在学习一下。没有那个必要。
地址:libevent
5 log4cpp
这个日志库, 支持多线程, 日志重定向到网络等都有, 反正你能想到的日志的功能, 都有。
日志嘛,一个是方便查找问题, 方便记录程序运行的一些情况。这是必须的。
网址:http://log4cplus.sourceforge.net/
6  OpenSSL
OpenSSL 包含一个命令行工具用来完成OpenSSL 库中的所有功能,更好的是,它可能已经安装到你的系统中了。
OpenSSL 是一个强大的安全套接字层密码库,Apache 使用它加密HTTPS ,OpenSSH 使用它加密SSH ,但是,你不应该只将其作为一个库来使用,它还是一个多用途的、跨平台的密码工具。
网址:http://www.openssl.org/source/


另外还有:
STLport-------SGI STL库的跨平台可移植版本,在以前有些编译器离符合 标准比较远的情况下 那时还是有用的,当然目前vc71已经比较接近标准了, 故目前不怎么用它了。
WxWindows-----功能强大的跨平台GUI库 ,它的功能和结构都类似 MFC,故原则上 可以通过WxWindows把现有MFC程序移植到非Win平台下
Blitz---------高效率的数值计算函数库 ,你可以订制补充你需要的算法
Crypto++ -----加/解密算法库, 非常专业的C++ 密码学函式库
CppUnit --- 一个c++的单元测试框架 类似 java 的JUnit
Loki ------- 一个实验性质的库,尝试把类似设计模式这样思想层面的东西通过 库来提供,他是C++的一个模板库,系C++"贵族", 它把C++模板的功能发挥到了极致
FC++ --------The Functional C++ Library ,用库来扩充语言的一个代表作 ,模板库
CGAL ------- Computational Geometry Algorithms Library计算几何方面的大部分重要的
欢迎关注 @程序员编程指南
回复

使用道具 举报

该用户从未签到

3

主题

24

帖子

94

积分

注册会员

Rank: 2

积分
94
发表于 2022-10-22 11:41:24 | 显示全部楼层
自荐一手自己写的 rpc 框架,不算什么优秀作品,但我想对于从事linux后台开发的人员来说绝对值得一看的。
开源地址:
https://github.com/Gooddbird/tinyrpc
TinyRPC 就是一个基于c++11开发的rpc框架,他能帮助你非常简单的就搭建一个高性能 rpc 服务,而且代码量不多,非常适合学习。
TinyRPC 的特点如下:

  • 最方便的异步rpc调用,无须任何回调函数。通过引入协程,用同步的写法就能实现异步RPC调用的性能。简言之就是调用rpc时协程自动让出cpu,让其他协程执行。当rpc调用返回数据时自动切换回来。
  • 主从Reactor架构,底层使用 epoll 进行套接字管理。多线程支持。性能不是问题,目前http echo 单机qps可以达到10w左右
  • 快速构建rpc服务,只需要一份简单配置文件以及几行代码即可。
  • 支持简单的 HTTP 协议;也支持基于Protobuf 序列化的协议。
TinyRPC的开源地址如下:
https://github.com/Gooddbird/tinyrpc
有兴趣的可以给个star,respect!
下面简单介绍下tinyrpc框架的使用方法:
1. 概述

上次介绍了如何使用 TinyRPC 框架搭建 HTTP 协议的 RPC 服务。

这次将带来基于 Protobuf 的 RPC 服务搭建实际上在现在的互联网大厂中,通常会使用这种 Protobuf 这种格式来实现 RPC ,而不是 HTTP。 HTTP 通常用来对外提供接口。
TinyRPC 框架是我目前使用 C++ 开发的一个基于协程的高性能RPC框架。目前差不多进入开发尾声了,基本的功能已经实现和测试过了。当然,最基本的是 RPC 功能,我将在本篇文章中介绍如何搭建 RPC 服务。
TinyRPC 的项目地址为:
GitHub - Gooddbird/tinyrpc: c++ rpc
如果你觉得项目不错,麻烦给个 Star 支持下,万分感谢。
此篇文章为应用篇,涉及的原理知识不多。更多关于 TinyRPC 框架的原理性文章请参考,我会持续更新:

2. 搭建 RPC 服务

2.1 TinyPB 协议

TinyRPC 框架自定义了一种基于 protobuf 的协议报文格式,即 TinyPB (Tiny protobuf protocal) 协议。这个协议的格式参考了陈硕的《Linux 多线程编程--使用 muduo 网络库》一节内容,并扩充了一些必要的字段。
TinyPB 单个协议包报文用 c++ 伪代码描述如下:
/***  min of package is: 1 + 4 + 4 + 4 + 4 + 4 + 4 + 1 = 26 bytes***/char start;                         // 代表报文的开始, 一般是 0x02int32_t pk_len {0};                 // 整个包长度,单位 byteint32_t msg_req_len {0};            // msg_req 字符串长度std::string msg_req;                // msg_req,标识一个 rpc 请求或响应。 一般来说 请求 和 响应使用同一个 msg_req.int32_t service_name_len {0};       // service_name 长度std::string service_full_name;      // 完整的 rpc 方法名, 如 QueryService.query_nameint32_t err_code {0};               // 框架级错误代码. 0 代表调用正常,非 0 代表调用失败int32_t err_info_len {0};           // err_info 长度std::string err_info;               // 详细错误信息, err_code 非0时会设置该字段值std::string pb_data;                // 业务 protobuf 数据,由 google 的 protobuf 序列化后得到int32_t check_num {0};             // 包检验和,用于检验包数据是否有损坏char end;                           // 代表报文结束,一般是 0x03
最小的数据包报文长度为 26 字节。
注释信息已经很完整了。另外几个需要特殊说明的字段如下:
err_code:  err_code 框架级别的错误码,即代表调用 RPC 过程中发生的错误,如对端关闭调用超时等。为0 代表此次 RPC 调用正常,即正常发送数据且接收到回包非 0 值代表调用失败,此时会设置 err_info 为详细的错误信息。
service_full_name : 是指的调用的完整方法名。即 servicename.methodname。一般来说,我们需要提前注册一个 Service (这里的 Service 指的继承了google::protobuf::Service 的类),而一个 Service 下包含多个方法。
pb_data这是最重要的数据,存储的是结果 Protobuf 序列化后的数据。
TinyPB 协议报文中包含了多个 len 字段,这主要是为了用空间换时间,接收方在提前知道长度的情况下,更方便解码各个字段,从而提升了 decode 效率。
可以看到,这个协议非常简单,只是个基本格式。 如果要正式运用在生产环境,还需要自己扩充字段才可。
2.2 定义 Protobuf 文件

前面介绍的 TinyPB 协议只需要简单知道即可, TinyRPC 内部已经实现了编码和解码。
当然,定义一个 Protobuf 文件是必须的。
如 tinypb.proto
syntax = "proto3";option cc_generic_services = true;message queryAgeReq {  int32 req_no = 1;  int32 id = 2;}message queryAgeRes {  int32 ret_code = 1;  string res_info = 2;  int32 req_no = 3;  int32 id = 4;  int32 age = 5;}message queryNameReq {  int32 req_no = 1;  int32 id = 2;  int32 type = 3;}message queryNameRes {  int32 ret_code = 1;  string res_info = 2;  int32 req_no = 3;  int32 id = 4;  string name = 5;}service QueryService {  // rpc method name  rpc query_name(queryNameReq) returns (queryNameRes);  // rpc method name  rpc query_age(queryAgeReq) returns (queryAgeRes);}
2.3 生成 pb 桩文件

没什么好说的,感谢 google 的 ProtoBuf。直接使用命令行生成即可:
protoc --cpp_out=./ tinypb.proto
执行完后可以发现当前路径下新增了两个文件:
tnypb.pb.cc    tinypb.pb.h
这是 Protobuf 自动根据我们的proto 文件生成的 C++ 代码。这两个文件包含了一些 RPC 主要类,如: queryAgeReq、QueryService、QueryService_Stub。
2.4 实现 RPC 方法

从生成的代码可以看到,QueryService 是个抽象基类,因此必须要继承然后重写 "query_name" 和 "query_age" 这两个方法才行。这个时候仅仅需要在这两个方法里面实现业务逻辑就行了。例如:
class QueryServiceImpl : public QueryService { public:  QueryServiceImpl() {}  ~QueryServiceImpl() {}  void query_name(google::protobuf::RpcController* controller,                       const ::queryNameReq* request,                       ::queryNameRes* response,                       ::google::protobuf::Closure* done) {        // DebugLog << "========================";    // DebugLog << "this is query_name func";    // DebugLog << "first begin to sleep 6s";    // sleep_hook(6);    // DebugLog << "sleep 6s end";    response->set_ret_code(0);    response->set_res_info("OK");    tinyrpc::MySQLInstase* instase =  tinyrpc::MySQLInstaseFactroy::GetThreadMySQLFactory()->GetMySQLInstase("test_db_key1");    if (!instase->isInitSuccess()) {      response->set_ret_code(-1);      response->set_res_info("faild to init mysql");      ErrorLog << "mysql instase init failed";      return;    }    char query_sql[512];    sprintf(query_sql, "select user_id, user_name, user_gender from user_db.t_user_information where user_id = '%s';", std::to_string(request->id()).c_str());    int rt = instase->query(std::string(query_sql));    if (rt != 0) {      response->set_ret_code(-1);      response->set_res_info(instase->getMySQLErrorInfo());      ErrorLog << "failed to excute sql" << std::string(query_sql);      return;    }    MYSQL_RES* res = instase->storeResult();    MYSQL_ROW row = instase->fetchRow(res);    if (row) {      int i = 0;      DebugLog << "query success";      response->set_id(std::atoi(row[i++]));      response->set_name(std::string(row[i++]));    } else {      DebugLog << "query empty";      response->set_ret_code(-1);      response->set_res_info("this user not exist");    }    if (done) {      done->Run();    }  }  void query_age(google::protobuf::RpcController* controller,                       const ::queryAgeReq* request,                       ::queryAgeRes* response,                       ::google::protobuf::Closure* done) {    DebugLog << "========================";    DebugLog << "this is query_age func";    response->set_ret_code(0);    response->set_res_info("OK");    response->set_req_no(request->req_no());    response->set_id(request->id());    response->set_age(20);    DebugLog << "========================";    done->Run();  }};
这里很简单,就是根据 request id 参数去 MySQL 数据库里面查询用户信息,然后返回到 response 对象。
2.5 配置文件

跟前一篇文章一样的,一个配置文件是必须的。
<?xml version="1.0" encoding="UTF-8" ?><root>  <!--log config-->  <log>    <!--日志文件路径,这个是相对运行时候 server 所在文件的路径-->    <log_path>./</log_path>    <!--日志文件前缀,一般跟 服务的名字相同-->    <log_prefix>test_rpc_server1</log_prefix>    <!--identify max size of single log file, MB-->    <log_max_file_size>5</log_max_file_size>    <!--log level: DEBUG < INFO < WARN < ERROR-->    <log_level>DEBUG</log_level>    <!--inteval that put log info to async logger, s-->    <log_sync_inteval>1</log_sync_inteval>  </log>  <coroutine>    <!--coroutine stack size (KB)-->    <coroutine_stack_size>128</coroutine_stack_size>    <!--default coroutine pool size-->    <coroutine_pool_size>5000</coroutine_pool_size>  </coroutine>  <msg_req_len>20</msg_req_len>  <!--max time when call connect, s-->  <max_connect_timeout>75</max_connect_timeout>  <!--count of io threads, at least 1-->  <iothread_num>2</iothread_num>  <time_wheel>    <bucket_num>6</bucket_num>    <!--inteval that destroy bad TcpConnection, s-->    <inteval>10</inteval>  </time_wheel>  <server>    <!--服务器绑定的 ip-->    <ip>192.168.245.7</ip>    <!--服务器监听的端口-->    <port>39999</port>    <!--服务器协议-->    <protocal>TinyPB</protocal>  </server>  <database>    <!--这里需要换成自己的 MySQL 配置信息-->    <db_key name="test_db_key1">      <!-- <ip>127.0.0.1</ip> -->      <ip>192.168.245.7</ip>      <port>3306</port>      <user>root</user>      <passwd>Ikerli20220517!!</passwd>      <select_db></select_db>      <char_set>utf8mb4</char_set>    </db_key>  </database></root>
注意这里 protocal 选择的是 TinyPB 协议,而不是 HTTP。 说明这个 RPC 服务的协议类型是 TinyPB。
另外要注意下 database 节点,因为这里使用了 MySQL, 需要将其配置信息写入到此文件中。
2.6 实现 RPC 服务

没什么好说的,几行代码即可:
int main(int argc, char* argv[]) {  if (argc != 2) {    printf("Start TinyRPC server error, input argc is not 2!");    printf("Start TinyRPC server like this: \n");    printf("./server a.xml\n");    return 0;  }  tinyrpc::InitConfig(argv[1]);  tinyrpc::GetServer()->registerService(std::make_shared<QueryServiceImpl>());  tinyrpc::StartRpcServer();    return 0;}
注意这里需要将 QueryServiceImpl 注册到 RPC 中,否则无法调用这个方法。
然后编译启动即可:
nohup ./test_rpc_server1 ../conf/test_rpc_server1.xml &
此时,就成功建立了 一个基于 TinyPB 协议的 RPC 服务了。那么怎么测试呢,还记得上一节的 HTTP 服务吗,修改下代码,让 HTTP 服务调用这个 RPC 服务。
2.7 修改 HTTP 服务实现异步 RPC 调用

void handle(tinyrpc::HttpRequest* req, tinyrpc::HttpResponse* res) {    DebugLog << "success recive http request, now to get http response";    setHttpCode(res, tinyrpc::HTTP_OK);    setHttpContentType(res, "text/html;charset=utf-8");    queryNameReq rpc_req;    queryNameRes rpc_res;    DebugLog << "now to call QueryServer TinyRPC server to query who's id is " << req->m_query_maps["id"];    rpc_req.set_id(std::atoi(req->m_query_maps["id"].c_str()));    tinyrpc::TinyPbRpcChannel channel(std::make_shared<tinyrpc::IPAddress>("127.0.0.1", 39999));    QueryService_Stub stub(&channel);    tinyrpc::TinyPbRpcController rpc_controller;    rpc_controller.SetTimeout(5000);    stub.query_name(&rpc_controller, &rpc_req, &rpc_res, NULL);    if (rpc_controller.ErrorCode() != 0) {      ErrorLog << "failed to call QueryServer rpc server";      char buf[512];      sprintf(buf, html, "failed to call QueryServer rpc server");      setHttpBody(res, std::string(buf));      return;    }    if (rpc_res.ret_code() != 0) {      std::stringstream ss;      ss << "QueryServer rpc server return bad result, ret = " << rpc_res.ret_code() << ", and res_info = " << rpc_res.res_info();      ErrorLog << ss.str();      char buf[512];      sprintf(buf, html, ss.str().c_str());      setHttpBody(res, std::string(buf));      return;    }    std::stringstream ss;    ss << "Success!! Your name is " << rpc_res.name() << ", and Your id is " << rpc_res.id();    char buf[512];    sprintf(buf, html, ss.str().c_str());    setHttpBody(res, std::string(buf));  }
这一段代码,几乎算是 RPC 调用的标准模板了。在 TinyRPC 框架中,调用 RPC 服务就仅仅只需要这几行代码即可。真正核心的调用原理都被封装在 RpcChannel 里面了。
此外,这里调用 RPC 是异步的,而不是传统的阻塞式的调用。 为什么说是异步的,其实在:
stub.query_name(&rpc_controller, &rpc_req, &rpc_res, NULL);
这一行代码中,由于 RPC 调用肯定是需要耗时的,这一段代码不可能立即返回。但是由于协程的特性,TinyRPC 框架在内部封装了这些细节,会主动 yield 当前协程,释放 CPU 资源,线程转而切换到其他协程去执行。而当调用完成后,会自动 Resume 当前协程,然后 query_name 函数返回,继续往下执行。
也就是说,这里同步的写法确实现了异步的性能。这也是 TinyRPC 框架的最大亮点。
想一下,如果是以前,要做到异步  RPC 我们要怎么做,怕不是只能用回调函数,类似这样:
std::function call_back = [](){  // ..........   // .......... }stub.query_name(&rpc_controller, &rpc_req, &rpc_res, call_back);
我们要把 query_name 返回后所有的剩下代码写到回调函数 call_back 里面去,这代码看上去确实挺难受的。毕竟谁都不想看这种异步代码,不容易看出执行顺序。此外,有可能回调函数又嵌套回调函数,最终陷入回调地狱,代码支离破碎,难以理解。
这里只是提一嘴,关于异步 RPC 的细节原理,将在下一篇文章介绍。
注意,这里的调用链路。从 test_http_sever 调用了 test_rpc_server1 服务的  query_name RPC 方法,tets_rpc_srver1 的 query_name 方法从 MySQL 中查询到用户数据并且返回给 test_http_server。调用链路如图:

2.8 简单测试

ok,先将两个 RPC 服务运行起来,然后同样访问 url
http://192.168.245.7:19999/user?id=1100110001
同样能得到正确的测试结果,读者可自行测试。本文代码和文档位于:
tinyrpc/quick_rpc_test.md at main · Gooddbird/tinyrpc
欢迎自行测试,有问题请提 issue。




TinyRPC的开源地址如下:
https://github.com/Gooddbird/tinyrpc
有兴趣的可以给个star,respect!


<hr>如果您觉得对你有所帮助,请点个赞加关注支持一下。同时 GitHub 项目也麻烦给个 Star 支持下开源精神,感谢各位大佬!!!


更多内容欢迎关注我的个人公众号,微信搜索 ikerli
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表