C++后端开发知识点总结

C++后端开发知识点总结

C++后端开发知识点总结

C++

1. 全局变量的作用域与初始化

全局变量作用于是从声明开始到整个文件结束,初始化可以在声明时直接初始化。

2. 字节对齐的计算方法

首先你要知道为什么会出现字节对齐,这是一种用空间换时间的做法,因为对齐之后可以提高取数的效率。结构体的大小一般是4或者8的倍数,具体是以最大的变量类型的大小为基数的。也就是说在内存中,数据一般是放在一个4的整数倍的起始地址。
首先看结构体中哪一个变量所占的字节数最大,然后后面的对齐计算都以这个为基数(我这里以4字节为例)。接着,你把每一个成员变量的大小依次相加(按照结构体定义中的顺序,从第一个加到最后一个)。当你加到某一个变量的时候,发现超过了4个字节,那么就把这个变量之前的内容当作是一个整体,它们一共点4个字节,后面的再继续这个过程
例子
参考

3. 类的静态成员

静态成员的功能

  • 静态成员变量属于整个类所有,所有对象共享类的静态成员变量
  • 静态成员变量的生命周期不依赖于任何对象
    可以通过类名和对象名访问public静态成员变量

静态成员变量的定义

  • 静态成员变量在类的内部声明,声明时直接通过static关键字修饰
  • 静态成员变量在类的外部定义与初始化,语法规则为Type ClassName::VarName = value;
  • 静态成员变量不占用类的大小,而是在类外(全局数据区)单独分配空间
    参考

4. 纯虚函数的用法

  • 虚函数和纯虚函数可以定义在同一个类(class),含有纯虚函数的类被称为抽象类(abstract class)。
  • 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用 。
  • 纯虚函数在抽象类中只有声明而没有定义。
  • 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。
  • 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。
  • 虚函数的定义形式:virtual type function();
  • 纯虚函数的定义形式:virtual type function() = 0;
  • 在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定。
  • 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类实现。
    参考

5. 构造函数和析构函数的执行顺序

  • 构造函数的调用顺序
    基类构造函数、对象成员构造函数、派生类本身的构造函数
  • 析构函数的调用顺序
    派生类本身的析构函数、对象成员析构函数、基类析构函数(与构造顺序正好相反)
  • 特例
    局部对象,在退出程序块时析构
    静态对象,在定义所在文件结束时析构
    全局对象,在程序结束时析构
    继承对象,先析构派生类,再析构父类
    对象成员,先析构类对象,再析构对象成员
    参考

6. 类的作用域

  • 每个类都会定义自己的作用域,在类的作用域之外,普通数据和函数只能由对象、引用、指针或者成员访问符来访问。
  • 一个类就是一个作用域的事实很好地解释了为什么类外定义成员函数时必须提供类名和函数名。在类的外部,成员的名字被隐藏起来了。
  • 一旦遇到类名,定义的剩余部分就在类的作用域之内,这里的剩余部分包含参数列表和函数体,结果就是可以直接使用类的其它成员而无须再次授权了。
    参考

7. STL相关数据结构的基本用法

  • vector,向量,动态数组。push_back向后插入一个元素。pop_back弹出最后一个元素。
  • array,定长数组,使用迭代器,可以使用算法库的算法。
  • deque,双端队列。
  • forward_list,单向链表。
  • list,双向链表。
  • queue,队列,先进先出。
  • stack,栈,先进后出。
  • map,不可重复,快速查找(底层红黑树),key和value对应。
  • set,不可重复,排序,快速查找(底层红黑树),key和value对应。
  • unordered_map,和map相同,底层使用hash查找,使用链表解决冲突。
  • unordered_set,和set相同,底层使用hash查找,使用链表解决冲突。

8. 文件的读写操作

  • fstream类。
    void open(const char* filename,int mode,int access);
    << 输出(写入),>> 输入(读出)。
  • istream类。只写。声明时初始化路径。
  • ostream类。只读。声明时初始化路径。
    参考

9. 内联函数

内联函数与一般函数的区别

  • 内联含函数比一般函数在前面多一个inline修饰符
  • 内联函数是直接复制“镶嵌”到主函数中去的,就是将内联函数的代码直接放在内联函数的位置上,这与一般函数不同,主函数在调用一般函数的时候,是指令跳转到被调用函数的入口地址,执行完被调用函数后,指令再跳转回主函数上继续执行后面的代码;而由于内联函数是将函数的代码直接放在了函数的位置上,所以没有指令跳转,指令按顺序执行
  • 一般函数的代码段只有一份,放在内存中的某个位置上,当程序调用它是,指令就跳转过来;当下一次程序调用它是,指令又跳转过来;而内联函数是程序中调用几次内联函数,内联函数的代码就会复制几份放在对应的位置上
  • 内联函数一般在头文件中定义,而一般函数在头文件中声明,在cpp中定义
    参考

10. 修改指针和修改指针指向的数据

  • 修改指针,将指针指向的地址修改为另一个地址,即将指针指向另一个数据。
  • 修改指针指向的数据,指针指向的地址不发生改变,指针指向地址的数据发生改变,修改为新的数据。

11. void指针类型

  • 指针有两个属性:指向变量/对象的地址和长度。但是指针只存储地址,长度则取决于指针的类型。编译器根据指针的类型从指针指向的地址向后寻址。指针类型不同则寻址范围也不同
  • void指针是一种没有类型的指针,不能判断出指向对象的长度。
  • 任何指针都可以赋值给void指针,不需要转换,只获得对象地址而不获得大小。
  • void指针赋值给其他类型的指针时都要进行转换,告诉类型对象大小。
  • void指针不能复引用,因为不知道大小,所以不能正确引用。
  • void指针不能参与指针运算,除非进行转换。不能地址偏移。
    参考

数据结构

1. 二分查找

有序数组中(默认升序),每次查找区间中间节点,小于则将改点变为右封闭节点,大于则将改点右边设置为下次查找区间。
参考

2. 简单位运算

  • &,按位与,两者都为真,则真。
  • |,按位或,有真,则真。
  • ^,按位异或,不同则为真。
  • ~,按位取反。
  • <<,左移。
  • >>,右移。

3. 图论算法(dijkstra算法、最小生成树、最短路径)

贪心算法

  • Prim算法:每次选取顶点中权值最小的点(从已选取的节点到该节点的距离,取最小值),然后根据选取节点更新其它节点的距离,取最小值。可得到最小生成树。
  • Kruskal算法:每次选取所有未选取边中最小的边(排除让已选取的边产生环路的边)。
  • dijkstra算法:每次选取顶点中的路径最小的点,然后根据选取节点更新其它节点路径。记录的是路径总长度。可得到最小生成树。

动态规划

  • Floyd算法:将每一个节点都当作一次中间节点,然后计算其它节点通过该节点到另一个节点的距离能不能减少,如果能减少则将改点作为两点之间的中间点。可以得到所有点之间的最短路径。

4. 堆(大顶堆、小顶堆)的构建与使用,如topK场景

  • 树的形状——这棵二叉树是基本完备(完全二叉树),树的每一层都是满的,除了最后一层最右边的元素有可能缺位。
  • 父母优势——每一个节点的键都要大于或等于它子女的键,叶节点除外。

堆的构建

  • 自底向上的构建方法,从最后一个元素开始(最下层,总个数为单数则最后一个元素为右节点,否则为左节点),将其父节点和父节点的左右子节点进行比较,依照堆的特性(大顶堆选最大的元素,小顶堆选最小的元素)选取值进行交换作为父节点。

堆的使用

  • 取出顶上的元素,将顶上的元素与最后一个元素进行交换。然后依照性质(大顶堆、小顶堆)将顶置位父节点,进行调整,如果没产生交换则任务结束,产生交换则将被交换的子节点置位父节点继续循环,直到父节点为叶节点为止(没有左子节点)。

topK场景

  • 堆替换
    第一步: 求最小的前k个数,我们就建立一个含有k个数据的大堆(记住:求最小的建大堆,求最大的 建立小堆)
    第二步:然后将剩余的数据和这个堆的堆顶比较,如果小于堆顶,交换数据,向下调整入堆,直到所有 数据比较完毕。
    参考

5. BFS、DFS搜索算法

  • BFS, 广度搜索,每一次搜索最近的一层,使用队列将待搜索节点存储起来。
  • DFS,深度搜索,每一次将一条路探索完,使用栈将待搜索的节点存储起来。

6. 递归、分治、回溯算法

  • 分治算法,是将大的问题逐步分解为小的问题,然后通过合并小的问题的解得到大问题的解。
  • 递归算法,是分治算法的具体实现手段,可以将问题细分并合并。
  • 回溯算法,是递归过程中返回上一种状态的手段,可以在试探当前路走不通时回到上一次的状态。
    参考

7. 树、二叉树、红黑树原理与使用场景

  • 原理:有根,每个父节点有n个子节点。
    每个节点有零个或多个子节点;
    没有父节点的节点称为根节点;
    每一个非根节点有且只有一个父节点;
    除了根节点外,每个子节点可以分为多个不相交的子树;
  • 使用场景:文件系统的目录结构,二叉树等。
    参考

二叉树

  • 原理: 每个父节点最多只有两个子节点。完全二叉树只有最下面一层可以不满, 而且最下层必须是从左到右。
  • 使用场景:二叉搜索树,平衡树,红黑树,堆。

红黑树

  • 原理:将二叉搜索树的深度降到最低,减少比较次数。
    (1)每个节点或者是黑色,或者是红色。
    (2)根节点是黑色。
    (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
    (4)如果一个节点是红色的,则它的子节点必须是黑色的。
    (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
  • 使用场景: set、map,epoll。

参考

8. 字典树原理与使用场景

  • 原理:最大限度地减少无畏的字符串比较,查询效率比哈希表高。
    Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
    它有3个基本性质:
    根节点不包含字符,除根节点外每一个节点都只包含一个字符。
    从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
    每个节点的所有子节点包含的字符都不相同。
  • 使用场景:字符串检索,文本预测,拼写检查,统计,排序大量的字符串,文本词频统计。
    参考
    应用场景

数据库

1. 复杂SQL(连表、函数、排序统计)的写法

  • 一般使用内连接(inner join)。
  • 排序使用order by,默认升序,降序使用desc关键字。distinct删除重复字段。
  • 函数
    参考

2. SQL的性能分析

分析索引的利用率。
参考

3. Innodb和MyIsam存储引擎的区别

  • 事务安全(MyISAM不支持事务,INNODB支持事务)
  • 外键 MyISAM 不支持外键, INNODB支持外键.
  • 锁机制(MyISAM时表锁,innodb是行锁)
  • 查询和添加速度(MyISAM批量插入速度快)
  • 支持全文索引(MyISAM支持全文索引,INNODB不支持全文索引)
  • MyISAM内存空间使用率比InnoDB低
    参考

网络原理

1. TCP协议的流量控制和拥塞控制

流量控制

  • 防止发送方发得太快,耗尽接收方的资源,从而使接收方来不及处理。
  • 使用滑动窗口实现流量控制。
  • 流量控制的机制是丢包。

拥塞控制

  • 防止发送方发得太快,使得网络来不及处理,从而导致网络拥塞。
  • 拥塞控制包括四部分:慢启动、拥塞避免、快速重传、快速恢复。

区别

  • 相同点
    现象都是丢包
    实现机制都是让发送方发得慢一点,发的少一点。
  • 不同点
    丢包位置不同,流量控制丢包位置是在接收端上,拥塞控制丢包位置是在路由器上。
    作用的对象不同,流量控制的对象是接收方,怕发送方发得太快,使得收方来不及处理。拥塞控制的对象是网络,怕发送方发得太快,造成网络拥塞,使得网络来不及处理。

参考

2. Session、Cookie与Application

Session

  • Session采用键值对 , 也就是说ID存放客户端 , 而值放在服务器端 , 是通过用户的ID去找服务器上对应的值 , 这种方式值放置在服务器端 ,有个时间限制 ,时间到则服务器自动释放.
  • Session中的信息保存在服务器的内存中,当然你也可以设置它的保存方法(如存在SQL数据库中)
  • Session用于保存每个用户的专用信息.她的生存期是用户持续请求时间再加上一段时间(一般是20分钟左右).
  • Session中的信息保存在Web服务器内容中,保存的数据量可大可小.当Session超时或被关闭时将自动释放保存的数据信息.由于用户停止使用应用程序后它仍然在内存中保持一段时间,因此使用Session对象使保存用户数据的方法效率很低.对于小量的数据,使用Session对象保存还是一个不错的选择.

Cookie

  • Cookie对象保存在客户端,Session和Application对象保存在服务器端.
  • 所有Cookie对象能够长期保存,Web应用程序可以通过获取客户端的Cookie值来判断用户的身份来进行验证。无需任何服务器资源。缺点,大小限制,如果客户端配置禁止Cookie设置,则被限制使用,安全风险,可以伪装。

Application

  • Application用于保存所有用户的公共的数据信息,如果使用Application对象,一个需要考虑的问题是任何写操作都要在Application_OnStart事件(global.asax)中完成.尽管使用Application.Lock和Applicaiton.Unlock方法来避免写操作的同步,但是它串行化了对Application对象的请求,当网站访问量大的时候会产生严重的性能瓶颈.因此最好不要用此对象保存大的数据集合。

区别

  • 大小
    Application:任意大小
    Session:小量,简单的数据
    Cookie:小量,简单的数据
  • 保存时间
    Application:整个应用程序的生命周期
    Session:用户活动时间+一段延迟时间(一般为20分钟)
    Cookie:可以根据需要设定
  • 应用范围
    Application:所有用户
    Session:单个用户
    Cookie:单个用户
  • 保存位置
    Application:服务器端
    Session:服务器端
    Cookie:客户端

参考

3. http2.0与http3.0特性

http2.0

  • 二进制分帧协议
    以二进制方式传输和Header压缩来减少传输数据量。http2.0将请求和响应数据分割为更小的帧,并且它们采用二进制编码。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。
  • 头部压缩
    HTTP/2并没有使用传统的压缩算法,而是开发了专门的"HPACK”算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还采用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的高压缩率。
  • 多路复用
    在 HTTP/2 中引入了多路复用的技术。多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也接更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。
  • 服务推送
    HTTP2还在一定程度上改变了传统的“请求-应答”工作模式,服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求HTML的时候就提前把可能会用到的JS、CSS文件发给客户端,减少等待的延迟,这被称为"服务器推送"( Server Push,也叫 Cache push)。
  • 提高安全性
    http2.0是加密的。使用https协议名,跑在TLS上面。HTTP/2协议定义了两个字符串标识符:“h2"表示加密的HTTP/2,“h2c”表示明文的HTTP/2。
    http1.0和http2.0

http3.0

  • 无队头阻塞问题
    QUIC协议是基于UDP协议实现的,在一条链接上可以有多个流,流与流之间是互不影响的,当一个流出现丢包影响范围非常小,从而解决队头阻塞问题。
  • 0RTT建链
    QUIC的0RTT也是需要条件的,对于第一次交互的客户端和服务端0RTT也是做不到的,毕竟双方完全陌生。因此,QUIC协议可以分为首次连接和非首次连接,两种情况进行讨论。
  • 首次连接和非首次连接
    使用QUIC协议的客户端和服务端要使用1RTT进行密钥交换,使用的交换算法是DH(Diffie-Hellman)迪菲-赫尔曼算法。
    客户端和服务端首次连接时服务端传递了config包,里面包含了服务端公钥和两个随机数,客户端会将config存储下来,后续再连接时可以直接使用,从而跳过这个1RTT,实现0RTT的业务数据交互。客户端保存config是有时间期限的,在config失效之后仍然需要进行首次连接时的密钥交换。
  • 前向安全问题
    前向安全指的是密钥泄漏也不会让之前加密的数据被泄漏,影响的只有当前,对之前的数据无影响。
  • 前向纠错
    QUIC每发送一组数据就对这组数据进行异或运算,并将结果作为一个FEC包发送出去,接收方收到这一组数据后根据数据包和FEC包即可进行校验和纠错。
  • 连接迁移
    QUIC协议基于UDP实现摒弃了五元组的概念,使用64位的随机数作为连接的ID,并使用该ID表示连接。在日常wifi和4G切换时,或者不同基站之间切换都不会重连,从而提高业务层的体验。
    http3.0

操作系统

1. 批处理系统

  • 单道批处理系统
    由监督程序将队列中的任务进行调度执行。一次执行一个任务,将执行的任务调入内存。
    缺点:系统中的资源得不到充分的利用,这是因为在内存中仅有一道程序,每逢该程序在运行中发出I/O请求后,CPU便处于等待状态,必须在其I/O完成后才继续运行
  • 多道批处理系统
    由监督程序按照算法一次放入多个任务进入内存,利用正在执行的任务IO等待时间执行其他任务,保持CPU处于忙碌状态。
    优缺点:(1)资源利用率高(2)系统吞吐量大(3)平均周转时间长(4)无交互能力
    参考

2. 分时系统与实时系统

分时系统

  • 原理
    使一台计算机同时为几个、几十个甚至几百个用户服务的一种操作系统。把计算机与许多终端用户连接起来,分时操作系统将系统处理机时间与内存空间按一定的时间间隔,轮流地切换给各终端用户的程序使用(时间片的概念)。由于时间间隔很短,每个用户的感觉就像他独占计算机一样。
  • 特征
    交互性,多路性,独立性,及时性。

实时系统

  • 原理
    实时操作系统(RTOS)是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统作出快速响应,并控制所有实时任务协调一致运行的操作系统。其特点是及时响应和高可靠性。实时系统又分为硬实时系统和软实时系统,硬实时系统要求在规定的时间内必须完成操作,这是在操作系统设计时保证的;软实时则只要按照任务的优先级,尽可能快地完成操作即可。
  • 特征
    多任务,抢占调度,任务间的通讯与同步,任务与中断之间的通信。

参考

3. 物理内存和虚拟内存

  • 物理内存就是内存本身的地址映射。
  • 虚拟内存是一个连续的地址空间(这也只是进程认为),而实际上,它通常是被分隔成多个物理内存碎片,还有一部分存储在外部磁盘存储器上,在需要时进行数据交换。

4. 文件系统、虚拟文件系统、缓存

  • 文件系统,把每个文件都看作一个独立的地址空间。
    文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。
  • 虚拟文件系统
    是Linux内核的子系统之一,它为用户程序提供文件和文件系统操作的统一接口,屏蔽不同文件系统的差异和操作细节。借助VFS可以直接使用open()、read()、write()这样的系统调用操作文件,而无须考虑具体的文件系统和实际的存储介质。
  • 文件缓存
    对于写操作来讲,操作系统会先将数据从用户空间复制到内核空间的缓存中。这时对用户程序来说,写操作就已经完成。至于什么时候再写到磁盘中由操作系统决定,除非显式地调用了sync同步命令。
    带缓存的写入,并没有真正写入硬盘,仅仅是写入缓存后,标记为脏页。发现脏页的数目超过了规定的数目就启动线程开始回写。
    查找是否有缓存页,没有则进行异步预读。有缓存页也要判断是否预读。最后从内核缓存页拷贝到用户内存空间。
    参考

5. CPU的上下文切换、中断处理

  • CPU上下文切换,把上一个任务的寄存器和计数器保存起来,然后加载新任务的寄存器和计数器。
  • 中断本身就是一个无法预料的事情,程序无法预料何时发生中断,何时执行中断处理函数,所以只能在某个地址写上一段中断处理代码,等中断到来时由硬件将程序强行跳转到你的中断处理函数去处理中断。
    CPU上下文切换
    CPU中断处理

6. 线程、进程的概念、创建于调度

  • 进程本质上是正在执行的一个程序,是容纳运行一个程序所需要所有信息的容器。
  • 线程,线程是执行任务的最小单位,不同的进程之间有独立的地址空间,而线程之间有完全一样的地址空间,这意味着它们共享同样的全局变量。每个线程都有自己的堆栈、程序计数器、寄存器等信息,这些不是共享的。
  • 线程是把进程的两项功能——独立分配资源与被调度分派执行分离开来。进程作为系统资源分配和保护的独立单位,不需要频繁地切换。线程作为系统调度和分派的基本单位,能轻装运行,会被频繁地调度和切换。

参考

7. 进程的创建于撤销

  • 创建线程:clone(), fork(), vfork()。
  • 撤销进程:exit_group(),exit()。

参考

8. 进程的阻塞与唤醒

进程的阻塞

  • 向系统申请资源时失败。如一个进程(A)申请打印机但是此时打印机被其他进程(B)正在使用,此时A进程则处于阻塞状态。
  • 等待某种操作:进程A启动了某I/O设备,如果只有完成了指定的I/o任务后进程A才能执行,则进程A启动了I/O设备后会自动进入阻塞。
  • 新数据尚未到达:对于相互合作的进程,如果一个进程需要先获得另一个进程的数据后才能对该数据进行处理,只要数据尚未到达其便会进入阻塞状态。
  • 等待新任务的到达:每当这种进程完成自己的任务便把自己阻塞起来,等待新任务到达,才将其唤醒。
  • 阻塞的过程,正在执行的进程,如果遇到上面阻塞的事件后,进程便调用阻塞原语block将自己阻塞,(阻塞是进程自身的一种主动行为)并将正在运行的进程立即停止运行,并把PCB中进程状态信息改为阻塞,并将PCB插入阻塞队列,如果系统设置了不同阻塞原因的队列,则应将其插入到对应原因引起的阻塞队列中。

进程的唤醒

  • 当被阻塞进程所期待的事件发生时则有关的进程会调用唤醒原语(wakeup),将等待的进程唤醒,首先把阻塞的进程从阻塞队列中移除,将其PCB插入就绪队列中

参考

9. 进程的同步与互斥

  • 进程同步:进程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系来源于他们之间的合作。
  • 进程互斥:进程之间的间接制约关系。当一个进程进入临界区使用临界资源时,另一个进程必须等待。只有当使用临界资源的进程退出临界区后,这个进程才会解除阻塞状态。

10. 用户态、内核态

  • 用户态:运行用户程序。
  • 内核态:运行操作系统程序,操作硬件。
  • 运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态(比如操作硬件)。
  • 处于用户态执行时,进程所能访问的内存空间和对象受到限制,其所处于占有的处理器是可被抢占的处于内核态执行时,则能访问所有的内存空间和对象,且所占有的处理器是不允许被抢占的。
    参考

11. 程序的编译与运行

  • 预处理:完成符号后面的内容到源文件的替换。此阶段产生.i文件
  • 编译:此阶段完成语法和语义分析,然后生成中间代码,此中间代码是汇编代码,但是还不可执行,gcc编译的中间文件是[.s]文件。
  • 汇编:此阶段主要完成将汇编代码翻译成机器码指令,并将这些指令打包形成可重定位的目标文件,[.O]文件,是二进制文件。
  • 链接:此阶段完成文件中调用的各种函数跟静态库和动态库的连接,并将它们一起打包合并形成目标文件,即可执行文件。

参考

网络编程

1. time_wait、close_wait状态产生的原因、危害与避免方法发生

  • time_wait:time_wait是主动关闭连接的一方保持的状态,是确保被动关闭的一方收到确认关闭的消息产生的。大量产生time_wait的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源。
  • close_wait:在对方关闭连接之后服务器程序自己没有进一步发出FIN信号,一般原因都是TCP连接没有调用关闭方法。
  • 危害:会占用内存,占用大量文件句柄。
  • 可以调用setsockopt函数使TCP不经历time_wait状态。

参考

2. I/O多路复用技术

  • select机制,程序呼叫select,然后整个程序就阻塞状态,这时候,kernel内核就会轮询检查所有select负责的文件描述符fd,当找到其中那个的数据准备好了文件描述符,会返回给select,select通知系统调用,将数据从kernel内核复制到进程缓冲区(用户空间)。
  • poll,poll的原理与select非常相似,差别如下:
    描述fd集合的方式不同,poll使用 pollfd 结构而不是select结构fd_set结构,所以poll是链式的,没有最大连接数的限制
    poll有一个特点是水平触发,也就是通知程序fd就绪后,这次没有被处理,那么下次poll的时候会再次通知同个fd已经就绪。
  • epoll,epoll没有fd数量限制,底层使用红黑树,fd首次调用epoll_ctl拷贝,每次调用epoll_wait不拷贝。回调时间复杂度O(1)。

参考

3. 熟练掌握各种I/O模型的运用场景

  • 阻塞I/O模型:进程会一直阻塞,直到数据拷贝完成。
  • 非阻塞IO模型:当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。
  • IO复用模型:select、poll和epoll。
  • 信号驱动IO:首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
  • 异步IO模型:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。

参考

分布式架构

1. 单体架构和微服务架构的区别

单体架构

  • 一个归档包(可以是JAR、WAR、EAR或其它归档格式)包含所有功能的应用程序,通常称为单体应用。而架构单体应用的方法论,就是单体应用架构。
  • 优点
    便于共享:单个归档文件包含所有功能,便于在团队之间以及不同的部署阶段之间共享。
    易于测试:单体应用一旦部署,所有的服务或特性就都可以使用了,这简化了测试过程,因为没有额外的依赖,每项测试都可以在部署完成后立刻开始。
    易于部署:只需将单个归档文件复制到单个目录下。
  • 缺点
    复杂性高:由于是单个归档文件,所以整个项目文件包含的模块非常多,导致模块的边界模糊、依赖关系不清晰、代码的质量参差不齐,混乱的堆在一起,使得整个项目非常复杂。以致每次修改代码,都非常小心,可能添加一个简单的功能,或者修改一个Bug都会带来隐藏的缺陷。
    技术债务:随着时间的推移、需求的变更和技术人员的更替,会逐渐形成应用程序的技术债务,并且越积越多。
    扩展能力受限:单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。
    阻碍技术创新:对于单体应用来说,技术是在开发之前经过慎重评估后选定的,每个团队成员都必须使用相同的开发语言、持久化存储及消息系统。

微服务架构

  • 微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
  • 优点
    易于开发和维护:一个微服务只会关注一个特定的业务功能,所以业务清晰、代码量较少。开发和维护单个微服务相对简单。
    单个微服务启动较快
    局部修改容易部署:单体应用只要有修改,就得重新部署整个应用。微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可。
    技术栈不受限制:在微服务架构中,可以结合项目业务及团队的特点,合理的选择技术栈。
    按需伸缩:可根据需求,实现细粒度的扩展。
  • 缺点
    运维要求高:更多的服务意味着要投入更多的运维。
    分布式固有的复杂性:使用微服务构建的是分布式系统。对于一个分布式系统,系统容错、网络延迟、分布式事务等都会带来巨大的问题。
    接口调整成本高:微服务之间通过接口进行通信。如果修改某一个微服务的API,可能所有用到这个接口的微服务都需要进行调整。

参考

2. 微服务架构的难点和优势

难点

  • 依赖服务变更很难跟踪,其他团队的服务接口文档过期怎么办?依赖的服务没有准备好,如何验证我开发的功能。
  • 部分模块重复构建,跨团队、跨系统、跨语言会有很多的重复建设。
  • 微服务放大了分布式架构的系列问题,如分布式事务怎么处理?依赖服务不稳定怎么办?
  • 运维复杂度陡增,如:部署物数量多、监控进程多导致整体运维复杂度提升。
  • 会有一些解决方案,比如提供文档管理、服务治理、服务模拟的工具和框架; 实现统一认证、统一配置、统一日志框架、分布式汇总分析; 采用全局事务方案、采用异步模拟同步;搭建持续集成平台、统一监控平台等等。

优势

  • 是每个微服务组件都是简单灵活的,能够独立部署。不再像以前一样,应用需要一个庞大的应用服务器来支撑。
  • 可以由一个小团队负责更专注专业,相应的也就更高效可靠。
  • 微服务之间是松耦合的,微服务内部是高内聚的,每个微服务很容易按需扩展。
  • 微服务架构与语言工具无关,自由选择合适的语言和工具,高效的完成业务目标即可。

参考

3. 服务注册和发现的实现方法

服务注册

  • 首先是需要维护一个注册表,通常是用的redis,hash存储。
  • 方案A:提供一个获取所有IP的接口,和获取单个topic的IP的接口。消费者端自己实现负载均衡算法,可以自己定制。
  • 方案B:直接实现负载均衡算法,让消费者在接口中传参选择用哪一种,提供一个每个topic的IP的接口。
  • 然后消费者端需要进行30秒的长连接,一旦有人下线,则广播给正在连接的消费端。
  • 为生产端提供一个上线一个下线的接口。

服务发现

  • 既然想统一负载均衡算法,那么我加一个反向代理就好了,比如nginx。消费者服务不再关心IP,只需要提供topic+接口名,代理方用一定的负载均衡算法得到最优IP,直接路由过去。

参考

4. 限流和熔断的作用以及实现方法

限流和熔断的目的

  • 限流:限流还是比较好理解,例如一个项目在上线之前经过性能测试评估,例如服务在 TPS 达到 1w/s 时系统资源利用率飙升,与此同时响应时间急剧增大,那我们就要控制该服务的调用TPS,超过该 TPS 的流量就需要进行干预,可以采取拒绝、排队等策略,实现流量的削峰填谷。
    还有一个场景,例如一下开放平台,对接口进行收费,免费用户要控制调用TPS,账户的等级不同,允许调用的TPS也不同,这种情况就非常适合限流。
  • 熔断:某一个应用有多台服务器,其中一台产生故障但并未宕机,但是负载均衡算法还是会请求故障的机器,导致超时报错。发现发往某一台机器的错误数或错误率达到设定的值,就在一定的世界间隔内不继续发往该机器,转而发送给集群内正常的节点,这样就实现了高可用,这就是所谓的熔断机制。

参考

5. rpc协议的设计和选择、调用超时时间的合理设置

rpc协议的设计

  • 特殊分隔符:使用\r\n之类的特殊分割符来分割两条消息,不适用于二进制,可读性强。
  • 长度前缀:在每个消息体前面加上4个字节的长度整数值,标记消息体的长度。适用于二进制。
  • 消息压缩:可以减轻网络带宽压力,但是会增加CPU的负担。
    参考

选择、调用超时时间的合理设置

  • 关于超时时间和重试次数的设置,需要考虑整个调用链中所有依赖服务的耗时、各个服务是否是核心服务等很多因素。
  • 设置调用方的超时时间之前,先了解清楚依赖服务的TP99响应时间是多少(如果依赖服务性能波动大,也可以看TP95),调用方的超时时间可以在此基础上加50%
  • 如果RPC框架支持多粒度的超时设置,则:全局超时时间应该要略大于接口级别最长的耗时时间,每个接口的超时时间应该要略大于方法级别最长的耗时时间,每个方法的超时时间应该要略大于实际的方法执行时间
    参考

6. 失败重试的合理选择

  • 区分是可重试服务还是不可重试服务,如果接口没实现幂等则不允许设置重试次数。注意:读接口是天然幂等的,写接口则可以使用业务单据ID或者在调用方生成唯一ID传递给服务端,通过此ID进行防重避免引入脏数据
  • 如果RPC框架支持服务端的超时设置,同样基于前面3条规则依次进行设置,这样能避免客户端不设置的情况下配置是合理的,减少隐患
  • 如果从业务角度来看,服务可用性要求不用那么高(比如偏内部的应用系统),则可以不用设置超时重试次数,直接人工重试即可,这样能减少接口实现的复杂度,反而更利于后期维护
  • 重试次数设置越大,服务可用性越高,业务损失也能进一步降低,但是性能隐患也会更大,这个需要综合考虑设置成几次(一般是2次,最多3次)
  • 如果调用方是高QPS服务,则必须考虑服务方超时情况下的降级和熔断策略。(比如超过10%的请求出错,则停止重试机制直接熔断,改成调用其他服务、异步MQ机制、或者使用调用方的缓存数据)
    参考

7. 如何提升单机故障的容忍度

  • 主备方式:一台主机,一台或多台备机,在正常情况下主机对外提供服务,并把数据同步到备机,当主机宕机后,备机立刻开始服务。优点是对客户端毫无影响,缺点也很明显,在绝大多数时间内备机是一直没使用,被浪费着的。
  • 主从方式:这种采取一主多从的办法,主从之间进行数据同步。 当Master宕机后,通过选举算法(Paxos、Raft)从slave中选举出新Master继续对外提供服务,主机恢复后以slave的身份重新加入。主从另一个目的是进行读写分离,这是当单机读写压力过高的一种通用型解决方案。 其主机的角色只提供写操作或少量的读,把多余读请求通过负载均衡算法分流到单个或多个slave服务器上。
    缺点是主机宕机后,Slave虽然被选举成新Master了,但对外提供的IP服务地址却发生变化了,意味着会影响到客户端。 解决这种情况需要一些额外的工作,在当主机地址发生变化后及时通知到客户端,客户端收到新地址后,使用新地址继续发送新请求。

参考

8. 如何进行日志的上报、存储、查询

ELK

  • 将日志进行集中化管理(beats)
  • 将日志格式化(logstash)
  • 对格式化后的数据进行索引和存储(elasticsearch)
  • 前端数据的展示(kibana)

参考

9. 如何合理的进行服务质量的监控

通过收集日志,对系统和各个服务的运行状态进行监控
通过收集量度(Metrics),对系统和各个服务的性能进行监控
通过分布式追踪,追踪服务请求是如何在各个分布的组件中进行处理的细节

参考

云原生

1. 容器和虚拟机的区别

  • 多个容器之间共用一个操作系统,虚拟机的操作系统是独立的。
  • 容器架构降低了硬件成本
  • 更快速的部署开发/测试/生产环境
  • 更简便的维护开发/测试/生产环境
  • 与微服务架构更为契合

参考

2. docker命令的基本使用方法

  • 载入镜像:docker pull 镜像名
  • 启动容器:docker run 镜像名
  • 查看所有容器:docker ps -a
  • 停止容器:docker stop 容器ID
  • 进入容器:docker exec,退出容器终端,不会导致容器停止。
  • 删除容器:docker rm -f 容器ID
    参考1
    参考2

3. K8S中核心模块的作用

  • etcd 保存了整个集群的状态;
  • API Server 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;
  • Controller Manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
  • Scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;
  • Kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理;
  • Container Runtime 负责镜像管理以及 Pod 和容器的真正运行(CRI);
  • Kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡;

参考

4. sidecar模式的概念

  • 将应用程序的组件部署到单独的进程或容器中以提供隔离和封装,这种模式被视为Sidecar模式。
  • Sidecar设计模式允许你为应用程序添加许多功能,而无需额外第三方组件的配置和代码。Sidecar还与父应用程序共享相同的生命周期,并与父应用程序一起创建和退出。

参考

5. 调用链追踪的实现原理

  • 在谷歌的Dapper论文中,每个节点都对应一个Span,节点之间的连线表示Span和它的父Span之间的关系,具体表现为一次调用请求和响应的调用关系。
    我们先关注两个服务之间的同学,两个服务之间有成千上万次通信,服务1与服务2进行交互时,会发送一个请求1,并接收到一个响应1,那么我们通过什么手段标示响应和请求是一对呢?
  • 通过HTTP协议头携带标记信息,标记信息包括标示调用链的唯一ID,这里叫作TraceID,以及标示调用层次和顺序的SpanID和ParentSpanID。
  • 当系统出现故障时,只需4步即可:
    (1)通过TraceID把一整条调用链的所有调用信息收集到一个集合中,包括请求和响应
    (2)通过SpanID和ParentSpanID恢复树形的调用树,ParentSpanID为-1的节点为根节点
    (3)识别调用链中出错或超时的节点,做出标记
    (4)把恢复的调用树和出错的节点信息通过某种图形显示到UI界面上。
    有多种策略产生SpanID
    (1)使用随机数产生SpanID,理论上有可能重复,但是由于64位长整型,重复的可能性微乎其微,并且本地生成随机数的效率高于其他方法。
    (2)使用分布式的全局唯一的流水号生成方式,可参考互联网发好器Vesta。
    (3)每个SpanID包含所有父亲及前辈节点的SpanID,使用圆点符号作为分隔符,不再需要ParentSpanID字段,这种方案实现起来简单,但是如果调用链有太多的节点和层次时,SpanID会携带太多的冗余信息,导致服务间调用的性能下降。

参考

  • 15
    点赞
  • 193
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对于C++后端开发技术路线,以下是一些主要的技术和步骤: 1. 掌握C++基础知识:了解C++语法、面向对象编程和数据结构等基本概念。 2. 学习网络编程:掌握Socket编程,了解TCP/IP协议栈和HTTP协议等网络通信基础知识。 3. 数据库开发:学习SQL语言和关系型数据库,如MySQL、Oracle等,了解数据库设计和优化原则。 4. 多线程和并发编程:熟悉多线程编程技术和同步机制,了解并发编程的相关概念和工具,如线程池、锁、条件变量等。 5. 分布式系统:了解分布式系统的基本原理和常用技术,如分布式存储、负载均衡、分布式缓存等。 6. 消息队列和异步编程:了解消息队列的使用场景和常见的消息中间件,如RabbitMQ、Kafka等,并掌握异步编程的基本思想和技术。 7. Web开发框架:熟悉C++的Web开发框架,如CppCMS、Wt、Crow等,并了解HTTP服务器的原理和常用技术。 8. 性能优化和调优:学习性能优化的方法和工具,如代码剖析、性能分析工具等,提升程序的性能和稳定性。 9. 安全防护:了解常见的Web安全漏洞和攻击手段,学习安全防护的基本原理和技术,如SQL注入、XSS攻击等。 10. 持续集成和部署:学习使用版本控制工具(如Git)、自动化构建工具(如Jenkins)、容器化技术(如Docker)等,实现持续集成和部署。 以上是C++后端开发技术路线的一些主要方向,根据个人兴趣和需求可以选择深入学习相应的领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殇弑天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值