2021-03-24 WXG二面 and 美团二面

1.CPU执行一条指令的过程?

冯·诺伊曼体系:计算机的数制采用二进制;计算机应该按照程序顺序执行。
在这里插入图片描述

1.取指令阶段

取指令(Instruction Fetch,IF)阶段是将一条指令从主存中取到指令寄存器的过程。

程序计数器PC中的数值,用来指示当前指令在主存中的位置。当一条指令被取出后,PC中的数值将根据指令字长度而自动递增:若为单字长指令,则(PC)+1àPC;若为双字长指令,则(PC)+2àPC,依此类推。

2.指令译码阶段

取出指令后,计算机立即进入指令译码(Instruction Decode,ID)阶段。

在指令译码阶段,指令译码器按照预定的指令格式,对取回的指令进行拆分和解释,识别区分出不同的指令类别以及各种获取操作数的方法

在组合逻辑控制的计算机中,指令译码器对不同的指令操作码产生不同的控制电位,以形成不同的微操作序列;在微程序控制的计算机中,指令译码器用指令操作码来找到执行该指令的微程序的入口,并从此入口开始执行。

在传统的设计里,CPU中负责指令译码的部分是无法改变的。不过,在众多运用微程序控制技术的新型CPU中,微程序有时是可重写的,可以通过修改成品CPU来改变CPU的译码方式。

3.执行指令阶段

在取指令和指令译码阶段之后,接着进入执行指令(Execute,EX)阶段。

此阶段的任务是完成指令所规定的各种操作,具体实现指令的功能。为此,CPU的不同部分被连接起来,以执行所需的操作。

例如,如果要求完成一个加法运算,算术逻辑单元ALU将被连接到一组输入和一组输出,输入端提供需要相加的数值,输出端将含有最后的运算结果。

4.访存取数阶段

根据指令需要,有可能要访问主存,读取操作数,这样就进入了访存取数(Memory,MEM)阶段。

此阶段的任务是:根据指令地址码,得到操作数在主存中的地址,并从主存中读取该操作数用于运算

5.结果写回阶段

作为最后一个阶段,结果写回(Writeback,WB)阶段把执行指令阶段的运行结果数据“写回”到某种存储形式:结果数据经常被写到CPU的内部寄存器中,以便被后续的指令快速地存取;在有些情况下,结果数据也可被写入相对较慢、但较廉价且容量较大的主存。许多指令还会改变程序状态字寄存器中标志位的状态,这些标志位标识着不同的操作结果,可被用来影响程序的动作。

在指令执行完毕、结果数据写回之后,若无意外事件(如结果溢出等)发生,计算机就接着从程序计数器PC中取得下一条指令地址,开始新一轮的循环,下一个指令周期将顺序取出下一条指令。

https://blog.csdn.net/aigoogle/article/details/23750257


2.中断介绍?嵌套中断?中断保护?

1.什么是中断?
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

分类
硬件中断:
可屏蔽中断。硬件中断的一类,可通过在中断屏蔽寄存器中设定位掩码来关闭。
非可屏蔽中断。硬件中断的一类,无法通过在中断屏蔽寄存器中设定位掩码来关闭。典型例子是时钟中断(一个硬件时钟以恒定频率—如50Hz—发出的中断)。
处理器间中断。一种特殊的硬件中断。由处理器发出,被其它处理器接收。仅见于多处理器系统,以便于处理器间通信或同步。
伪中断。一类不希望被产生的硬件中断。发生的原因有很多种,如中断线路上电气信号异常,或是中断请求设备本身有问题。

软件中断:
软件中断。是一条CPU指令,用以自陷一个中断。由于软中断指令通常要运行一个切换CPU至内核态(Kernel Mode/Ring 0)的子例程,它常被用作实现系统调用(System call)。

按照事件发生的顺序,中断过程包括:
①中断源发出中断请求;
②判断当前处理机是否允许中断和该中断源是否被屏蔽;
③优先权排队;
④处理机执行完当前指令或当前指令无法执行完,则立即停止当前程序,保护断点地址和处理机当前状态,转入相应的中断服务程序;
⑤执行中断服务程序;
⑥恢复被保护的状态,执行“中断返回”指令回到被中断的程序或转入其他程序。
上述过程中前四项操作是由硬件完成的,后两项是由软件完成的。
在这里插入图片描述

向量中断
对应每个中断源设置一个向量。这些向量顺序存在主存储器的特定存储区。向量的内容是相应中断服务程序的起始地址和处理机状态字。在响应中断时,由中断系统硬件提供向量地址,处理机根据该地址取得向量,并转入相应的中断服务程序。

2.中断可否继续被中断?
可以。

在某一时刻有几个中断源同时发出中断请求时,处理器只响应其中优先权最高的中断源。当处理机正在运行某个中断服务程序期间出现另一个中断源的请求时,如果后者的优先权低于前者,处理机不予理睬,反之,处理机立即响应后者,进入所谓的“嵌套中断”。中断优先权的排序按其性质、重要性以及处理的方便性决定,由硬件的优先权仲裁逻辑或软件的顺序询问程序来实现

3.中断如何保护?
IRQ(中断请求)。

中断请求是由某个设备发出并同时请求CPU产生中断的一个请求响应信号。通常情况下,计算机是根据中断请求的优先级来决定究竟应当首先响应哪一个设备的中断请求的(中断请求IRQ0~1RQ15中,IRQ0的优先级别最高,IRQ15最低)。原则上,计算机中每个设备自身只有惟一的一个中断请求通道,即IRQ值(又叫IRQ号)。当两个硬件设备同时使用同一个中断通道(IRQ值)时,就会发生IRQ冲突,因为这个时候处理器已经无法准确判断收到的中断请求究竟来自于哪个设备了。

如何解决中断冲突
1、硬件“挪移”法:此方法主要用来解决中断冲突造成的死机和较难排除的IRQ冲突。即在主板BIOS默认的IRQ资源分配下,通过调整板卡(声卡、Modem、网卡、电视卡、显卡等)于插槽的安装位置来避开IRQ冲突。采取这种方法时,需要用户掌握主板BIOS默认状态下的IRQ资源分配情况,然后在此基础上调整板卡的位置,从而避开IRQ冲突。

2、主板BIOS、操作系统的“软配置”法:对于不太严重的中断冲突(不死机),我们可以通过手动调整BIOS和操作系统中的IRQ值来避开IRQ冲突。

https://baike.baidu.com/item/%E4%B8%AD%E6%96%AD/3933007?fr=aladdin

https://blog.csdn.net/weixin_45705783/article/details/106689937

pci 中断冲突_如何解决IRQ冲突


3.请求分页、分段、段页内存管理系统介绍?各自优缺点?

内存空间的分配和回收

内部碎片 : 分配给某进程的内存区域有一部分没有用上, 即存在" 内部碎片 ".
外部碎片 : 内存中的某些空闲分区由于太小而难以利用.

连续分配管理方式:

①单一连续分配
在单一连续分配的方式中, 内存被分为系统区和用户区, 系统区用于存放操作系统的相关数据, 用户区用于存放用户进程的相关数据,内存中只能有一道用户程序, 用户程序独占整个用户区空间.

优点 : 实现简单, 无外部碎片; 可以采用覆盖技术扩充内存; 不一定需要采取内存保护
缺点 : 只能用于单用户, 单任务的操作系统中; 有内部碎片; 存储器利用率极低

②固定分区分配
在产生了支持多道程序的系统后, 为了能在内存中装入多道程序而互相之间不产生干扰,将整个用户区划分为若干个固定大小的分区(分区大小可以相等也可以不相等),在每个分区中只能装入一道作业,形成了最早的可运行多道程序的内存管理方式.

操作系统建立一个数据结构----分区说明表, 来实现各个分区的分配和回收, 每个表对应一个分区, 通常按分区大小排列. 每个表项包括对应分区的大小, 起始地址, 状态.

优点 : 实现简单, 无外部碎片;
缺点 : 有内部碎片; 存储器利用率不高;

③可变分区分配(动态分配)

动态分配方式是在进程装入内存时根据进程大小动态地建立分区, 并使得分区的大小正好适合进程的需要。

四种动态分配的算法:

①首次适应算法:每次都从低地址开始查找, 找到第一个能满足大小的空闲分区
实现 : 把空闲分区按地址递增的次序排列.
每次分配内存时顺序地查找空闲分区链, 找到大小能满足要求的第一个空闲分区.

②最佳适应算法 :优先使用小的空闲分区
实现 : 空闲分区按容量递增次序链接.
每次分配内存时顺序查找空闲分区链, 找到大小能满足要求的第一个空闲分区

缺点 : 每次都选择最小的分区进行分配, 会留下越来越多的容量很小难以利用的内存块, 即产生很多的外部碎片

③最坏适应算法 :优先使用大的空闲分区
实现 : 空闲分区按容量递减次序链接

缺点 : 每次都选用最大的分区进行分配, 当较大的连续空闲区被小号之后, 如果有大进程到来则没有内存分区可以利用

④邻近适应算法 :在首次适应算法的基础上, 每次都从上次查找结束的位置开始查找空闲分区链(表), 找到大小能满足的第一个空闲分区

缺点 : 邻近适应算法导致无论低地址还是高地址的空闲分区都有相同的概率被使用, 也就导致了高地址部分的大分区更可能被使用划分为小分区, 最后导致没有大分区可用

非连续分配管理方式:

① 基本分页存储管理
页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页。把内存空间按页的大小划分为片或者页面,然后把页式虚拟地址与内存地址建立一一对应的页表,并用相应的硬件地址转换机构来解决离散地址变换问题。页式管理采用请求调页和预调页技术来实现内外存存储器的统一管理。

虚拟地址的组成: 页号 + 页内偏移量 (页:一块内存段)

优点:没有外碎片,每个内碎片不超过页的大小。
缺点:程序全部装入内存,要求有相应的硬件支持,如地址变换机构缺页中断的产生和选择淘汰页面等都要求有相应的硬件支持。增加了机器成本和系统开销。

② 基本分段存储管理
段式管理的基本思想是把程序按内容或过程函数关系分成段,每段有自己的名字。一个用户作业或者进程所包含的段对应一个二维线性虚拟空间,也就是一个二维虚拟存储器。段式管理程序以段为单位分配内存,然后通过地址映射机构把段式虚拟地址转换为实际内存物理地址。

虚拟地址的组成:段表 + 段内的偏移量

优点:可以分别编写和编译,可以针对不同类型的段采取不同的保护,可以按段为单位来进行共享,包括通过动态链接进行代码共享。
缺点:会产生碎片。

③ 段页存储管理
段页式管理,系统必须为每个作业或者进程建立一张段表以管理内存分配与释放、缺段处理等。另外由于一个段又被划分为若干个页,每个段必须建立一张页表以把段中的虚页变换为内存中的实际页面。显然与页式管理时相同,页表也要有相应的实现缺页中断处理和页面保护等功能的表项。

虚拟地址的组成:段号 + 段内页号 + 页内偏移量

优点:段页式管理是段式管理和页式管理相结合而成,具有两者的优点。
缺点:由于管理软件的增加,复杂性和开销也增加。另外需要的硬件以及占用的内存也有所增加,使得执行速度下降。

操作系统基础知识
https://blog.csdn.net/wolfGuiDao/article/details/107694581
https://blog.csdn.net/smilesundream/article/details/70148878


4.TCP握手建立连接时的初始序号为什么是随机的?

TCP在开始传输数据前,客户端和服务器需要随机生成自己的初始序列号(initial sequence number-ISN),然后通过三次握手进行交换确认。

ISN的具体的数值得由操作系统的源码来决定:
RFC1948中提出了一个较好的初始化序列号ISN随机生成算法:
ISN = M + F(localhost, localport, remotehost, remoteport)
M是一个计时器,这个计时器每隔4毫秒加1。
F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证hash算法不能被外部轻易推算得出,用MD5算法是一个比较好的选择。

TCP的ISN为什么不能固定?
① 从攻击的角度:
TCP初始化序列号不能设置为一个固定值,因为这样容易被攻击者猜出后续的序列号,从而遭到攻击。

② 从TCP连接稳定角度:
广域网的随机性,复杂性都很高,假设client与server连接状况不好,不停的断开又重连。那么之前交互的报文很可能在连接已断开时还没到达server。

如果ISN是固定的,那很可能在新连接建立后,上次连接通信的报文才到达server,这种情况有概率发生上次连接发送的报文的seq序列号正好是server希望收到的新连接的报文seq序列号。这就全乱了。


5. 一次完整的http请求过程

1、TCP建立连接

HTTP协议是基于TCP协议来实现的,因此首先就是要通过TCP三次握手与服务器端建立连接,一般HTTP默认的端口号为80。

2、浏览器发送请求命令

在与服务器建立连接后,Web浏览器会想服务器发送请求命令。

3、浏览器发送请求头消息

在浏览器发送请求命令后,还会发送一些其它信息,最后以一行空白内容告知服务器已经完成头信息的发送。

4、服务器应答

在收到浏览器发送的请求后,服务器会对其进行回应,应答的第一部分是协议的版本号和应答状态码。

5、服务器回应头信息

与浏览器端同理,服务器端也会将自身的信息发送一份至浏览器。

6、服务器发送数据

在完成所有应答后,会以Content-Type应答头信息所描述的格式发送用户所需求的数据信息。

7、断开TCP连接

在完成此次数据通信后,服务器会通过TCP四次挥手主动断开连接。但若此次连接为长连接,那么浏览器或服务器的头信息会加入keep-alive的信息,会保持此连接状态,在有其它数据发送时,可以节省建立连接的时间。


6.redis的内存淘汰策略

Redis中共有下面八种内存淘汰策略:

volatile-lru:设置了过期时间的key使用LRU算法淘汰;
allkeys-lru:所有key使用LRU算法淘汰;
volatile-lfu:设置了过期时间的key使用LFU算法淘汰;
allkeys-lfu:所有key使用LFU算法淘汰;
volatile-random:设置了过期时间的key使用随机淘汰;
allkeys-random:所有key使用随机淘汰;
volatile-ttl:设置了过期时间的key根据过期时间淘汰,越早过期越早淘汰;
noeviction:默认策略,当内存达到设置的最大值时,所有申请内存的操作都会报错(如set,lpush等),只读操作如get命令可以正常执行。

使用下面的参数maxmemory-policy配置淘汰策略:

#配置文件
maxmemory-policy noeviction
 
#命令行
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
127.0.0.1:6379> config set maxmemory-policy allkeys-random
OK
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "allkeys-random"

https://blog.csdn.net/wsdc0521/article/details/106997623


7.如何实现LRU?

原理
LRU 算法根据数据的历史访问记录来进行淘汰数据,其核心思想是「如果数据最近被访问过,那么将来被访问的几率也更高」。

我们可以通过一个有内存限制的栈来表示这个算法:
get(key):如果 key 在栈中,则返回对应的 value 值,否则返回 -1
put(key):如果 key 不在栈中,则将该 key 、value put 进栈中(注意,如果栈内存已满,则必须把最近最久未使用的元素从栈中删除);如果 key 在栈中,则重置value的值
在这里插入图片描述
实现
如果通过一个栈来实现,每次 get 值后都需要进行排序,会带来一些额外的时间复杂度。如果需要从 O(1) 时间复杂度内解决问题,一般会使用 Hash table + Doubly linked list 的方式。

Hash table:O(1) 时间复杂度查找元素。
Doubly linked list:O(1) 时间复杂度增删改元素。

①Doubly linked list(双项链表):

  1. 定义一个初始的双项链表,当需要往内存中 put 值时,把它放到双项链表 Dummy Tail 之前的位置。
  2. 如果已经超过内存限制,则需要先把 Dummy Head 之后的元素移除掉,然后再添加新的值。
  3. 当需要从双项链表中获取对应的值时,获取之后把对应的值移到 Dummy Tail 之前的位置(使用过的数据避免先被 delete)。
  4. 如果往内存中 put 的数据已经存在,则先把该数据从双项链表中移动到 Dummy Tail 之前的位置。然后更新对应的值。

②Hash table(哈希表):
在每次往双项链表中 put 值时我们都需要往 Hash table 里面存储对应 key、value。这样当我们需要查找双项链表中是否已存在对应的值。

C++实现LRU源代码


8.设计模式

软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性

学习设计模式的四个关键要素

  1. 模式名称
    每一个模式都有自己的名字,通常用一两个词来描述,可以根据模式的问题、特点、解决方案、功能和效果来命名。模式名称(PatternName)有助于我们理解和记忆该模式,也方便我们来讨论自己的设计。
  2. 问题
    问题(Problem)描述了该模式的应用环境,即何时使用该模式。它解释了设计问题和问题存在的前因后果,以及必须满足的一系列先决条件。
  3. 解决方案
    模式问题的解决方案(Solution)包括设计的组成成分、它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象的 组合)来解决这个问题。
  4. 效果
    描述了模式的应用效果以及使用该模式应该权衡的问题,即模式的优缺点。主要是对时间和空间的衡量,以及该模式对系统的灵活性、扩充性、可移植性的影响,也考虑其实现问题。显式地列出这些效果(Consequence)对理解和评价这些模式有很大的帮助。

23种设计模式

  1. 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  2. 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  3. 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  4. 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  5. 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
  6. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
  7. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
  8. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
  9. 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
  10. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
  11. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
  12. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
  13. 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
  14. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
  15. 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
  16. 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
  17. 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
  18. 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
  19. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
  20. 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
  21. 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
  22. 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
  23. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

23种设计模式详细介绍


9.UML

UML(Unified Modeling Language,统一建模语言)是用来设计软件蓝图的可视化建模语言,是一种为面向对象系统的产品进行说明、可视化和编制文档的标准语言,独立于任何一种具体的程序设计语言。

UML系统开发中有三个主要的模型:
功能模型:从用户的角度展示系统的功能,包括用例图。
对象模型:采用对象,属性,操作,关联等概念展示系统的结构和基础,包括类别图、对象图。
动态模型:展现系统的内部行为。包括序列图,活动图,状态图。

http://c.biancheng.net/view/8373.html


10.代码整洁或者说代码规范

《代码整洁之道》

1.什么样的代码才是整洁的?
从字面意思上理解,整洁的代码,对于程序员来说非常的一目了然,简单、整洁,结构清晰,逻辑清楚。代码其实是一种语言,传递的是逻辑,如果这份代码可以像我们说话一样快速的将逻辑传递给读者,那么这样一份代码就是一份整洁的代码。可以借助沃德原则:“如果每段代码欧让你感到深合已意,那就是整洁代码”

2.如何保持代码整洁?
首先便是要有保持代码整洁的意识,书中反复提到的提到的一条童子军军规:让营地比你来时更干净。要写好整洁的代码,需要我们心中有着一个意识,我要写好我的代码,我的代码要让自己和别人看着舒服。

代码整洁之道 总结


11.如何考虑对代码重构的支持?

重构是在不改变软件可观察行为的前提下改善其内部结构

重构的节奏

  1. 以微小的步伐修改程序。如果你犯下错误,很容易便可发现它。
  2. 一个方法里面,不应该有很多的代码,我们可以通过分解后重组。
  3. 好的代码应该清楚的表达出自己的功能,变量名称是代码清晰的关键。
  4. 尽量减少临时变量,大量参数被传来传去,很容易跟丢,可读性差。
  5. 提炼出逻辑代码,以便功能复用。

如何实施代码重构


12.web框架为什么选型Gin?拥有类似功能的框架还有哪些,对比这些,Gin框架有什么优势?Gin的底层实现?Gin的不足与短处在哪里?

Gin 是一个 go 写的 web 框架,具有高性能的优点。

https://blog.csdn.net/weixin_43202635/article/details/115215690


13.同理,传输协议为什么选择gRPC?

HTTP和RPC的异同:

  • 传输协议
    RPC,可以基于TCP协议,也可以基于HTTP协议
    HTTP,基于HTTP协议
  • 传输效率
    RPC,使用自定义的TCP协议,可以让请求报文体积更小,或者使用HTTP2协议,也可以很好的减少报文的体积,提高传输效率
    HTTP,如果是基于HTTP1.1的协议,请求中会包含很多无用的内容,如果是基于HTTP2.0,那么简单的封装一下是可以作为一个RPC来使用的,这时标准RPC框架更多的是服务治理
  • 性能消耗,主要在于序列化和反序列化的耗时
    RPC,可以基于thrift实现高效的二进制传输
    HTTP,大部分是通过json来实现的,字节大小和序列化耗时都比thrift要更消耗性能
  • 负载均衡
    RPC,基本都自带了负载均衡策略
    HTTP,需要配置Nginx,HAProxy来实现
  • 服务治理(下游服务新增,重启,下线时如何不影响上游调用者)
    RPC,能做到自动通知,不影响上游
    HTTP,需要事先通知,修改Nginx/HAProxy配置

总结:
RPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。

https://blog.csdn.net/qq_34147021/article/details/85240628

https://blog.csdn.net/weixin_43202635/article/details/115219176


14.同理,缓存数据库为什么选择badger?

选择的原因

https://blog.csdn.net/weixin_43202635/article/details/115223010


15.同理,关系数据库为什么选择sqlite?

选择的原因

https://blog.csdn.net/weixin_43202635/article/details/115223091


猜你喜欢

转载自blog.csdn.net/weixin_43202635/article/details/115192082