一、C++11的新特性html
(1)新的关键字node
auto的自动类型推导,用于从初始化表达式中推断出变量的数据类型。经过auto的自动类型推导,能够大大简化咱们的编程工做。auto实际上实在编译时对变量进行了类型推导,因此不会对程序的运行效率形成不良影响。另外,彷佛auto并不会影响编译速度,由于编译时原本也要右侧推导而后判断与左侧是否匹配。linux
decltype实际上有点像auto的反函数,auto可让你声明一个变量,而decltype则能够从一个变量或表达式中获得类型。nginx
nullptr是为了解决原来C++中NULL的二义性问题而引进的一种新的类型,由于NULL实际上表明的是0c++
(2)lambda表达式程序员
lambda表达式相似Javascript中的闭包,它能够用于建立并定义匿名的函数对象,以简化编程工做。Lambda的语法以下:redis
[函数对象参数](操做符重载函数参数)->返回值类型{函数体}算法
下面是各类变量截取的选项:数据库
(3)变长参数的模板apache
因为在C++11中引入了变长参数模板,因此发明了新的数据类型:tuple,tuple是一个N元组,能够传入1个, 2个甚至多个不一样类型的数据
(4)更加优雅的初始化方法
在C++11中,咱们能够使用如下语法来进行替换:
二、C++的构造函数explict,用来干吗的
explicit关键字的做用就是防止类构造函数的隐式自动转换.(https://www.cnblogs.com/ymy124/p/3632634.html)
三、C++多态的实现机制
(1)静态多态(重载,模板)
是在编译的时候,就肯定调用函数的类型。
(2)动态多态(覆盖,虚函数实现)
在运行的时候,才肯定调用的是哪一个函数,动态绑定。运行基类指针指向派生类的对象,并调用派生类的函数。
虚函数实现原理:虚函数表和虚函数指针。
纯虚函数: virtual int fun() = 0;
那么,多态的做用是什么呢?咱们知道,封装能够隐藏实现细节,使得代码模块化;继承能够扩展已存在的代码模块(类);它们的目的都是为了——代码重用。
而多态则是为了实现另外一个目的——接口重用!并且现实每每是,要有效重用代码很难,而真正最具备价值的重用是接口重用,由于"接口是公司最有价值的资源。设计接口比用一堆类来实现这个接口更费时间。并且接口须要耗费更昂贵的人力的时间。"
https://www.cnblogs.com/Allen-rg/p/6927129.html
四、有用过什么设计模式
http://www.javashuo.com/article/p-hvfvkbcl-gw.html
五、Linux系统里代码编译成汇编以后,指令和寄存器有哪些
http://blog.sina.com.cn/s/blog_87c063060101bcwt.html
六、Redis 里String类型有什么特点,Redis数据类型的应用场景
https://redisbook.readthedocs.io/en/latest/index.html
数据类型:
(1)简单动态字符串
sds:“包含字符串值的字符串对象”,这种说法初听上去可能会有点奇怪, 可是在 Redis 中, 一个字符串对象除了能够保存字符串值以外, 还能够保存 long
类型的值, 因此为了严谨起见, 这里须要强调一下: 当字符串对象保存的是字符串时, 它包含的才是 sds 值, 不然的话, 它就是一个 long
类型的值。
(2)双端链表
双端链表主要有两个做用:
双端链表及其节点的性能特性以下:
节点带有前驱和后继指针,访问前驱节点和后继节点的复杂度为 O(1) ,而且对链表的迭代能够在从表头到表尾和从表尾到表头两个方向进行;
链表带有指向表头和表尾的指针,所以对表头和表尾进行处理的复杂度为 O(1)
链表带有记录节点数量的属性,因此能够在 O(1)复杂度内返回链表的节点数量(长度);
(3)字典
字典的用途:
实现字典的方法有不少种:
在众多可能的实现中, Redis 选择了高效、实现简单的哈希表,做为字典的底层实现。
字典的 rehash 操做实际上就是执行如下任务:
ht[0]->table
更大的 ht[1]->table
;ht[0]->table
中的全部键值对迁移到 ht[1]->table
;ht[0]
的数据清空,并将 ht[1]
替换为新的 ht[0]
;通过以上步骤以后, 程序就在不改变原有键值对数据的基础上, 增大了哈希表的大小。
rehash完毕后执行:
ht[0]
的空间;ht[1]
来代替 ht[0]
,使原来的 ht[1]
成为新的 ht[0]
;ht[1]
;rehashidx
属性设置为 -1
,标识 rehash 已中止;(4)跳跃表
为了知足自身的功能须要, Redis 基于 William Pugh 论文中描述的跳跃表进行了如下修改:
score
值:多个不一样的 member
的 score
值能够相同。score
值,还要检查 member
:当 score
值能够重复时,单靠 score
值没法判断一个元素的身份,因此须要连 member
域都一并检查才行。应用:
和字典、链表或者字符串这几种在 Redis 中大量使用的数据结构不一样, 跳跃表在 Redis 的惟一做用, 就是实现有序集数据类型。
七、多路IO复用,select和epoll的区别,Epoll相对来讲的好处
引入:
支持高并发链接.官方测试的是5w并发链接但在实际生产中可制成2-4w并发链接数,得益于nginx使用最新的epoll(linux 2.6内核)和kqueue(freebsd)网络I/O模型.而apache使用的则是传统的select模型,其比较稳定的prefork模式为多进程模式,须要常常派生子进程,所消耗的CPU等服务器资源要比nginx高的多.
select 和epoll效率差的缘由: select是轮询、epoll是触发式的,因此效率高。
select:
1.Socket数量限制:该模式可操做的Socket数由FD_SETSIZE决定,内核默认32*32=1024.
2.操做限制:经过遍历FD_SETSIZE(1024)个Socket来完成调度,无论哪一个Socket是活跃的,都遍历一遍.
poll:
1.Socket数量几乎无限制:该模式下的Socket对应的fd列表由一个数组来保存,大小不限(默认4k).
2.操做限制:同Select.
epoll:
1.操做限制:同Select.
2.操做无限制:基于内核提供的反射模式,有活跃Socket时,内核访问该Socket的callback,不须要遍历轮询. 可是当全部Socket都活跃的时候,这时候全部的callback都被唤醒,会致使资源的竞争.既然都是要处理全部的Socket,那么遍历是最简单最有效的实现方式.
九、共享内存是怎么实现的
nmap函数要求内核建立一个新额虚拟存储器区域,最好是从地质start开始的一个区域,并将文件描述符fd指定对象的一个连续的片(chunk)映射到这个新的区域。
SHMMNI为128,表示系统中最多能够有128个共享内存对象。
1、内核怎样保证各个进程寻址到同一个共享内存区域的内存页面
一、page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的全部信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cache或swap cache中的全部页面就是根据address_space结构以及一个偏移量来区分的。
二、文件与address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之创建一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个address_space与一个偏移量可以肯定一个page cache 或swap cache中的一个页面。所以,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。
三、进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并无创建进程空间到物理页面的映射。所以,第一次访问该空间时,会引起一个缺页异常。
四、对于共享内存映射状况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),若是找到,则直接返回地址;若是没有找到,则判断该页是否在交换区(swap area),若是在,则执行一个换入操做;若是上述两种状况都不知足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表。
注:对于映射普通文件状况(非共享映射),缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。若是没有找到,则说明文件数据尚未读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新。
五、全部进程在映射同一个共享内存区域时,状况都同样,在创建线性地址与物理地址之间的映射以后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。
十、讲一下ARP协议运做的过程
ARP(Address Resolution Protocol,地址解析协议)是将IP地址解析为以太网MAC地址(或称物理地址)的协议。
在局域网中,当主机或其它网络设备有数据要发送给另外一个主机或设备时,它必须知道对方的网络层地址(即IP地址)。可是仅仅有IP地址是不够的,由于IP数据报文必须封装成帧才能经过物理网络发送,所以发送站还必须有接收站的物理地址,因此须要一个从IP地址到物理地址的映射。APR就是实现这个功能的协议。
假设主机A和B在同一个网段,主机A要向主机B发送信息。具体的地址解析过程以下
(1) 主机A首先查看本身的ARP表,肯定其中是否包含有主机B对应的ARP表项。若是找到了对应的MAC地址,则主机A直接利用ARP表中的MAC地址,对IP数据包进行帧封装,并将数据包发送给主机B。
(2) 若是主机A在ARP表中找不到对应的MAC地址,则将缓存该数据报文,而后以广播方式发送一个ARP请求报文。ARP请求报文中的发送端IP地址和发送端MAC地址为主机A的IP地址和MAC地址,目标IP地址和目标MAC地址为主机B的IP地址和全0的MAC地址。因为ARP请求报文以广播方式发送,该网段上的全部主机均可以接收到该请求,但只有被请求的主机(即主机B)会对该请求进行处理。
(3) 主机B比较本身的IP地址和ARP请求报文中的目标IP地址,当二者相同时进行以下处理:将ARP请求报文中的发送端(即主机A)的IP地址和MAC地址存入本身的ARP表中。以后以单播方式发送ARP响应报文给主机A,其中包含了本身的MAC地址。
(4) 主机A收到ARP响应报文后,将主机B的MAC地址加入到本身的ARP表中以用于后续报文的转发,同时将IP数据包进行封装后发送出去。
当主机A和主机B不在同一网段时,主机A就会先向网关发出ARP请求,ARP请求报文中的目标IP地址为网关的IP地址。当主机A从收到的响应报文中得到网关的MAC地址后,将报文封装并发给网关。若是网关没有主机B的ARP表项,网关会广播ARP请求,目标IP地址为主机B的IP地址,当网关从收到的响应报文中得到主机B的MAC地址后,就能够将报文发给主机B;若是网关已经有主机B的ARP表项,网关直接把报文发给主机B。
十二、你了解分布式设计和开发是哪些
滴滴出行消息服务团队近日开源了其内部普遍使用的分布式消息中间件产品 DDMQ,这是一款致力于提供低延迟、高并发、高可用、高可靠消息服务的企业级消息队列产品。
DDMQ 具备以下的优秀特性:
低延迟高吞吐:毫秒级延迟,单机百万条消息吞吐。
丰富的消息类型:具有实时消息、延时消息和分布式事务消息。
海量消息存储,支持消息回溯消费:支持 RocketMQ 和 Kafka 做为实时消息的存储引擎,使用RocksDB 做为延时消息的存储引擎。
秒级延时消息:支持单条消息设置精确到秒级的延迟时间,提供普通延时消息和循环延时消息。
多语言客户端,提供了主流开发语言SDK,包括PHP, Java, Go, C/C++, Python,在API 上保持着最易使用的 High Level 形式。
多种消费方式:支持经过 Thrift RPC拉取、HTTP 推送和第三方存储直写的方式消费消息。
支持灵活的消息过滤和转换功能:经过使用 Groovy 脚本在服务端进行消息体的转换和过滤,能作有效减小客户端和服务器的数据传输量,减轻客户端处理消息的负载。
统一的Web控制台:方便用户管理Topic等资源,经过控制台能够实现配置生产和消费的限流值、消费方式、Groovy脚本、启停消费、重置消费进度等功能。
完善的监控配套:提供模块的健康检查和消息堆积告警功能。
适用场景;
消息队列做为构建现代分布式应用所必备的基础设施,有着普遍的应用场景。
削峰填谷
在秒杀等场景下会致使短期流量的暴涨,下游系统会由于缺乏保护而过载甚至崩溃。DDMQ提供的海量堆积能力和消费限流可以确保下游系统的平稳运行。
异步解耦
经过上下游系统的松耦合设计,能够保证上游系统不会由于下游系统的宕机而不可用。确保主流程的正常稳定运行。
顺序消息
现实中须要保证顺序的场景不少,好比订单系统中订单建立、支付、退款等流程,均须要保证顺序。 DDMQ提供的顺序消费功能能够保证消息的先进先出。
事务消息
在微服务的场景下,经过DDMQ的事务消息可以达到分布式事务的最终一致性
架构设计
下面这张图描述了DDMQ 的整体架构。主要包括 Broker Cluster、Producer Proxy Cluster(如下简称 PProxy),Consumer Proxy Cluster(如下简称CProxy),SDK,Console 等模块。
Broker Cluster 是DDMQ的消息存储层。使用 RocketMQ做为实时消息的存储引擎(同时也支持使用Kafka),Chronos则是咱们基于 RocksDB自研的延时消息存储引擎。
PProxy 是DDMQ的生产代理服务, 内置 Thrift RPC Server,生产 SDK 经过RPC 调用将消息发送给 PProxy,而后再由PProxy负责将消息生产到具体的 Broker 中去,在 PProxy 中咱们实现了生产限流、重试和消息批量生产等功能。
CProxy 是DDMQ的消费代理服务,也内置了Thrift RPC Server,当选择SDK消费时,消费方以 pull 的方式从 CProxy 中拉取消息,因为 CProxy 中的PullBuffer提早缓存了必定数量的待消费消息,所以消费的延迟很低。若是选择HTTP方式消费,则直接由CProxy将消息推送到业务指定的回调URL地址。在CProxy 中,咱们实现了消息过滤(经过编写Groovy脚本)、消息体转换(Transit)、重试、消费限流、顺序消费内部排序等功能。
Console是DDMQ的控制台,用户经过控制台申请Topic、Group等资源。Topic等数据会持久化到MySQL并推送到 Zookeeper;PProxy和CProxy经过读取、监听 Zookeeper 上的Topic和Group 数据来实时控制消息的生产和消费逻辑。
DDMQ选择Proxy+SDK的架构,主要有这几个好处:
方便实现多语言SDK的实现,因为滴滴内部使用的技术栈比较多,将主要逻辑放在 Proxy 上有利于下降 SDK的复杂度,让SDK的开发速度大大加快。目前在滴滴内部支持PHP, Go , C/C++, Java, Python, Node.js等语言的SDK实现。
存储层业务无感知,因为Proxy层屏蔽了后面的RocketMQ或Kafka,使得存储层的切换能够作到业务无感知。
加快新功能迭代速度,新功能的开发都在 Proxy 层实现,下降了SDK的升级频率。
DDMQ 已经在滴滴内部稳定运行了两年多时间,支撑了网约车、小桔车服、地图、金融、智能驾驶、智慧交通、外卖等业务的稳定运行。日消息流水达到千亿级别,总体服务可用性超过5个9。
1三、怎么作负载均衡
RocketMQ是一个分布式具备高度可扩展性的消息中间件。本文旨在探索在broker端,生产端,以及消费端是如何作到横向扩展以及负载均衡的。
Broker是以group为单位提供服务。一个group里面分master和slave,master和slave存储的数据同样,slave从master同步数据(同步双写或异步复制看配置)。
经过nameserver暴露给客户端后,只是客户端关心(注册或发送)一个个的topic路由信息。路由信息中会细化为message queue的路由信息。而message queue会分布在不一样的broker group。因此对于客户端来讲,分布在不一样broker group的message queue为成为一个服务集群,但客户端会把请求分摊到不一样的queue。
而因为压力分摊到了不一样的queue,不一样的queue实际上分布在不一样的Broker group,也就是说压力会分摊到不一样的broker进程,这样消息的存储和转发均起到了负载均衡的做用。
Broker一旦须要横向扩展,只须要增长broker group,而后把对应的topic建上,客户端的message queue集合即会变大,这样对于broker的负载则由更多的broker group来进行分担。
而且因为每一个group下面的topic的配置都是独立的,也就说可让group1下面的那个topic的queue数量是4,其余group下的topic queue数量是2,这样group1则获得更大的负载。
在集群消费模式下,每条消息只须要投递到订阅这个topic的Consumer Group下的一个实例便可。RocketMQ采用主动拉取的方式拉取并消费消息,在拉取的时候须要明确指定拉取哪一条message queue。
而每当实例的数量有变动,都会触发一次全部实例的负载均衡,这时候会按照queue的数量和实例的数量平均分配queue给每一个实例。
默认的分配算法是AllocateMessageQueueAveragely
因为广播模式下要求一条消息须要投递到一个消费组下面全部的消费者实例,因此也就没有消息被分摊消费的说法。
在实现上,其中一个不一样就是在consumer分配queue的时候,会所有consumer都分到全部的queue。
1四、负载均衡多进程的分配算法
Toeplitz Hash
一、轮询法
将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的链接数和当前的系统负载。
二、随机法
经过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由几率统计理论能够得知,随着客户端调用服务端的次数增多,
其实际效果愈来愈接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。
三、源地址哈希法
源地址哈希的思想是根据获取客户端的IP地址,经过哈希函数计算获得的一个数值,用该数值对服务器列表的大小进行取模运算,获得的结果即是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
四、加权轮询法
不一样的后端服务器可能机器的配置和当前系统的负载并不相同,所以它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,下降其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
五、加权随机法
与加权轮询法同样,加权随机法也根据后端机器的配置,系统的负载分配不一样的权重。不一样的是,它是按照权重随机请求后端服务器,而非顺序。
六、最小链接数法
最小链接数算法比较灵活和智能,因为后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的链接状况,动态地选取其中当前
积压链接数最少的一台服务器来处理当前的请求,尽量地提升后端服务的利用效率,将负责合理地分流到每一台服务器。
1五、内存池怎么设计的,为何设计内存池
内存池的做用在于消除频繁调用系统默认的内存分配和释放函数所带来的开销问题。
因为每次要求分配的内存大小不等,使用默认的内存分配函数的话,可能给系统带来大量的碎片问题,因此,将内存配置问题交给底层的内存池去处理,是一个不错的选择。
考虑一个常规的空间配置器(或者说内存池)的做用是什么?分配内存和释放内存。
使用了空间配置器后,咱们将函数调用malloc/free、new/delete的工做包装到配置器中,而后内存池提供相关的分配内存和释放内存接口。这就是最简单的空间配置器
内存池细节考虑
因为每一次分配内存的大小不定,因此考虑到管理方便,STL内存池采用两级内存配置器,若要求的内存大于128bytes,使用malloc/free直接分配和回收。若小于等于128bytes时,由内存池分配和回收。
在内存池中,以8bytes为单位,即维护从8,16,24,..,128bytes的16个链表,每一个链表上连接的是对应大小的区块。每一次分配内存时,先将要求的内存大小扩展为8的倍数,而后从对应链表上取走一个区块,释放时,将区块从新连接到对应链表上。
1七、守护进程怎么设计的,父进程不是负责通知子进程么
Linux daemon是运行于后台常驻内存的一种特殊进程,周期性的执行或者等待trigger执行某个任务,与用户交互断开,独立于控制终端。一个守护进程的父进程是init进程,它是一个孤儿进程,没有控制终端,因此任何输出,不管是向标准输出设备stdout仍是标准出错设备stderr的输出都被丢到了/dev/null中。守护进程通常用做服务器进程,如httpd,syslogd等。
用fork建立子进程,父进程退出,子进程成为孤儿进程被init接管,子进程变为后台进程。
调用setsid()让子进程成为新会话的组长,脱离父进程的会话期。setsid()在调用者是某进程组组长时会失败,可是A已经保证了子进程不会是组长,B以后子进程变成了新会话组的组长。
由于会话组的组长有权限从新打开控制终端,因此这里第二次fork将子进程结束,留着孙进程,孙进程不是会话组的组长因此没有权利再打开控制终端,这样整个程序就与控制终端隔离了。
进程从建立它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,形成进程所在的文件系统没法卸下以及引发没法预料的错误。
将三个标准文件描述符定向到/dev/null中
进程活动时,其工做目录所在的文件系统不能卸下(好比工做目录在一个NFS中,运行一个daemon会致使umount没法成功)。通常须要将工做目录改变到根目录。对于须要转储核心,写运行日志的进程将工做目录改变到特定目录如chdir("/tmp"),进程从建立它的父进程那里继承了文件建立掩模。它可能修改守护进程所建立的文件的存取位。为防止这一点,将文件建立掩模清除:umask(0);
1八、打开网页比较详细的过程(dns解析过程)
什么是DNS解析?当用户输入一个网址并按下回车键的时候,浏览器获得了一个域名。而在实际通讯过程当中,咱们须要的是一个IP地址。所以咱们须要先把域名转换成相应的IP地址,这个过程称做DNS解析。
1) 浏览器首先搜索浏览器自身缓存的DNS记录。
或许不少人不知道,浏览器自身也带有一层DNS缓存。Chrome 缓存1000条DNS解析结果,缓存时间大概在一分钟左右。
2) 若是浏览器缓存中没有找到须要的记录或记录已通过期,则搜索hosts文件和操做系统缓存。
在Windows操做系统中,能够经过 ipconfig /displaydns 命令查看本机当前的缓存。
经过hosts文件,你能够手动指定一个域名和其对应的IP解析结果,而且该结果一旦被使用,一样会被缓存到操做系统缓存中。
Windows系统的hosts文件在%systemroot%\system32\drivers\etc下,linux系统的hosts文件在/etc/hosts下。
3) 若是在hosts文件和操做系统缓存中没有找到须要的记录或记录已通过期,则向域名解析服务器发送解析请求。
其实第一台被访问的域名解析服务器就是咱们平时在设置中填写的DNS服务器一项,当操做系统缓存中也没有命中的时候,系统会向DNS服务器正式发出解析请求。这里是真正意义上开始解析一个未知的域名。
通常一台域名解析服务器会被地理位置临近的大量用户使用(特别是ISP的DNS),通常常见的网站域名解析都能在这里命中。
4) 若是域名解析服务器也没有该域名的记录,则开始递归+迭代解析。
这里咱们举个例子,若是咱们要解析的是mail.google.com。
首先咱们的域名解析服务器会向根域服务器(全球只有13台)发出请求。显然,仅凭13台服务器不可能把全球全部IP都记录下来。因此根域服务器记录的是com域服务器的IP、cn域服务器的IP、org域服务器的IP……。若是咱们要查找.com结尾的域名,那么咱们能够到com域服务器去进一步解析。因此其实这部分的域名解析过程是一个树形的搜索过程。
根域服务器告诉咱们com域服务器的IP。
接着咱们的域名解析服务器会向com域服务器发出请求。根域服务器并无mail.google.com的IP,可是却有google.com域服务器的IP。
接着咱们的域名解析服务器会向google.com域服务器发出请求。...
如此重复,直到得到mail.google.com的IP地址。
为何是递归:问题由一开始的本机要解析mail.google.com变成域名解析服务器要解析mail.google.com,这是递归。
为何是迭代:问题由向根域服务器发出请求变成向com域服务器发出请求再变成向google.com域发出请求,这是迭代。
5) 获取域名对应的IP后,一步步向上返回,直到返回给浏览器。
发起TCP请求
浏览器会选择一个大于1024的本机端口向目标IP地址的80端口发起TCP链接请求。通过标准的TCP握手流程,创建TCP链接。
关于TCP协议的细节,这里就再也不阐述。这里只是简单地用一张图说明一下TCP的握手过程。若是不了解TCP,能够选择跳过此段,不影响本文其余部分的浏览。
什么是负载均衡?当一台服务器没法支持大量的用户访问时,将用户分摊到两个或多个服务器上的方法叫负载均衡。
什么是Nginx?Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具备占有内存少,稳定性高等优点。
负载均衡的方法不少,Nginx负载均衡、LVS-NAT、LVS-DR等。这里,咱们以简单的Nginx负载均衡为例。关于负载均衡的多种方法详情你们能够Google一下。
1) 通常,若是咱们的平台配备了负载均衡的话,前一步DNS解析得到的IP地址应该是咱们Nginx负载均衡服务器的IP地址。因此,咱们的浏览器将咱们的网页请求发送到了Nginx负载均衡服务器上。
2) Nginx根据咱们设定的分配算法和规则,选择一台后端的真实Web服务器,与之创建TCP链接、并转发咱们浏览器发出去的网页请求。
Nginx默认支持 RR轮转法 和 ip_hash法 这2种分配算法。
前者会从头至尾一个个轮询全部Web服务器,然后者则对源IP使用hash函数肯定应该转发到哪一个Web服务器上,也能保证同一个IP的请求能发送到同一个Web服务器上实现会话粘连。
也有其余扩展分配算法,如:
fair:这种算法会选择相应时间最短的Web服务器
url_hash:这种算法会使得相同的url发送到同一个Web服务器
3) Web服务器收到请求,产生响应,并将网页发送给Nginx负载均衡服务器。
4) Nginx负载均衡服务器将网页传递给filters链处理,以后发回给咱们的浏览器。
浏览器渲染
1) 浏览器根据页面内容,生成DOM Tree。根据CSS内容,生成CSS Rule Tree(规则树)。调用JS执行引擎执行JS代码。
2) 根据DOM Tree和CSS Rule Tree生成Render Tree(呈现树)
3) 根据Render Tree渲染网页
1九、c++异常底层的实现
在具体实现异常捕获时,首先,C++ 异常处理器检查发生异常的位置是否在当前函数的某个 try 块以内。这项工做能够经过将当前函数的 nStep 值依次在 piHandler 指向 tblTryBlocks[] 表的条目中进行范围为 [nBeginStep, nEndStep) 的比对来完成。
其次,若是异常发生的位置在当前函数中的某个 try 块内,则尝试匹配该 tblTryBlocks[] 相应条目中的 tblCatchBlocks[] 表。tblCatchBlocks[] 表中记录了与指定 try 块配套出现的全部 catch 块相关信息,包括这个 catch 块所能捕获的异常类型及其起始地址等信息。
若找到了一个匹配的 catch 块,则复制当前异常对象到此 catch 块,而后跳转到其入口地址执行块内代码。
不然,则说明异常发生位置不在当前函数的 try 块内,或者这个 try 块中没有与当前异常相匹配的 catch 块,此时则沿着函数栈框架中 piPrev 所指地址(即:异常处理链中的上一个节点)逐级重复以上过程,直至找到一个匹配的 catch 块或到达异常处理链的首节点。对于后者,咱们称为发生了未捕获的异常,对于 C++ 异常处理器而言,未捕获的异常是一个严重错误,将致使当前进程被强制结束。
在编译一段 C++ 代码时,编译器会将全部 throw 语句替换为其 C++ 运行时库中的某一指定函数,这里咱们叫它 __CxxRTThrowExp(与本文提到的全部其它数据结构和属性名同样,在实际应用中它能够是任意名称)。该函数接收一个编译器承认的内部结构(咱们叫它 EXCEPTION 结构)。这个结构中包含了待抛出异常对象的起始地址、用于销毁它的析构函数,以及它的 type_info 信息。对于没有启用 RTTI 机制(编译器禁用了 RTTI 机制或没有在类层次结构中使用虚表)的异常类层次结构,可能还要包含其全部基类的 type_info 信息,以便与相应的 catch 块进行匹配。
在图5中的深灰色框图内,咱们使用 C++ 伪代码展现了函数 FuncA 中的 “throw myExp(1);” 语句将被编译器最终翻译成的样子。实际上在多数状况下,__CxxRTThrowExp 函数即咱们前面曾屡次提到的“异常处理器”,异常捕获和栈回退等各项重要工做都由它来完成。
__CxxRTThrowExp 首先接收(并保存)EXCEPTION 对象;而后从 TLS:Current ExpHdl 处找到与当前函数对应的 piHandler、nStep 等异常处理相关数据;并按照前文所述的机制完成异常捕获和栈回退。由此完成了包括“抛出”->“捕获”->“回退”等步骤的整套异常处理机制。 |
20、内存分区
在C++中,内存分红5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈,就是那些由编译器在须要的时候分配,在不须要的时候自动清楚的变量的存储区。里面的变量一般是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由咱们的应用程序去控制,通常一个new就要对应一个delete。若是程序员没有释放掉,那么在程序结束后,操做系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分类似的,不过它是用free来结束本身的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在之前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不容许修改
通常认为在c中分为这几个存储区:
1. 栈 --有编译器自动分配释放
2. 堆 -- 通常由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
3. 全局区(静态区) -- 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另外一块区域。程序结束释放。
4. 另外还有一个专门放常量的地方。程序结束释放
2一、IO阻塞非阻塞同步异步问题
先说阻塞与非阻塞的区别,recv()函数默认是阻塞的,什么是阻塞呢?就是当你调用recv()函数时,整个进程或者线程就等待在这里了,直到你recv的fd的全部信息都被send过来,这么作好处就是保证全部信息都可以完整的读取了,但劣势也很明显,就是在recv()的过程当中你的进程或线程作不了其它事情,由此,引入了非阻塞IO。
非阻塞IO是什么呢,仍是以recv()函数为例,当你将其设置为非阻塞时,每次当你recv()时,就直接返回,无论信息有没有彻底send进来,好处很明显,recv()了以后进程立刻能处理下一行代码,坏处也很明显,就是你不知道你的消息是否读完了,这种问题就是TCP中大名鼎鼎的半包问题(解决办法主要是经过一个buffer缓存全部读进来的消息)
下面再说同步与异步的区别,在POSIX定义中把同步IO操做定义为致使进程阻塞直到IO完成的操做,反之则是异步IO,看概念感受异步跟非阻塞好像也没有什么区别,要好好理解同步和异步,就要详细说明下IO过程:
1.数据准备阶段
2.内核空间复制回用户进程缓冲区空间
不管阻塞式IO仍是非阻塞式IO,都是同步IO模型,区别就在与第一步是否完成后才返回,但第二步都须要当前进程去完成,异步IO呢,就是从第一步开始就返回,直到第二步完成后才会返回一个消息,也就是说,非阻塞可以让你在第一步时去作其它的事情,而真正的异步IO能让你第二步的过程也能去作其它事情。这里就在说一下select,poll和epoll这几个IO复用方式,这时你就会了解它们为何是同步IO了,以epoll为例,在epoll开发的服务器模型中,epoll_wait()这个函数会阻塞等待就绪的fd,将就绪的fd拷贝到epoll_events集合这个过程当中也不能作其它事(虽然这段时间很短,因此epoll配合非阻塞IO是很高效也是很广泛的服务器开发模式--同步非阻塞IO模型)。有人把epoll这种方式叫作同步非阻塞(NIO),由于用户线程须要不停地轮询,本身读取数据,看上去好像只有一个线程在作事情,也有人把这种方式叫作异步非阻塞(AIO),由于毕竟是内核线程负责扫描fd列表,并填充事件链表的,我的认为真正理想的异步非阻塞,应该是内核线程填充事件链表后,主动通知用户线程,或者调用应用程序事先注册的回调函数来处理数据,若是还须要用户线程不停的轮询来获取事件信息,就不是太完美了,因此也有很多人认为epoll是伪AIO,仍是有道理的。
2二、tcp发送窗口接收窗口拥塞窗口的区别
一:滑动窗口是接受数据端使用的窗口大小,用来告知发送端接收端的缓存大小,以此能够控制发送端发送数据的大小,从而达到流量控制的目的,对应==>rwnd:接收端窗口(receiver window)
对于流量控制,是一个端对端的概念。由接收端返回的rwnd控制。
二:那么对于数据的发送端就是拥塞窗口了,拥塞窗口不表明缓存,拥塞窗口指某一源端数据流在一个RTT内能够最多发送的数据包数,cwnd:发送端窗口( congestion window )。
拥塞控制: 发送端主动控制控制cwnd,有慢启动(从cwnd初始为1开始启动,指数启动),拥塞避免(到达ssthresh
后,为了不拥塞开始尝试线性增加),快重传(接收方每收到一个报文段都要回复一个当前最大连续位置的确认,
发送方只要一连收到三个重复确认就知道接收方丢包了,快速重传丢包的报文,并TCP立刻把拥塞窗口 cwnd 减少到
1),快恢复(直接从ssthresh线性增加)。
快重传的机制是:
-1. 接收方创建这样的机制,若是一个包丢失,则对后续的包继续发送针对该包的重传请求;
-2. 一旦发送方接收到三个同样的确认,就知道该包以后出现了错误,马上重传该包;
-3. 此时发送方开始执行“快恢复”算法:
*1. 慢开始门限减半;
*2. cwnd设为慢开始门限减半后的数值;
*3. 执行拥塞避免算法(高起点,线性增加);
三:发送方窗口是相互影响的,具体以下:
发送方窗口的上限值 = Min [ rwnd, cwnd ]
当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。
当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。
2三、数据库索引的实现
https://blog.csdn.net/sdgihshdv/article/details/75039825
2四、数据库如何实现数据一致性问题(锁)乐观锁和悲观锁的概念
先说悲观锁和乐观锁吧。并发控制通常采用三种方法,分别是乐观锁和悲观锁以及时间戳。乐观锁认为一个用户读数据的时候,别人不会去写本身所读的数据;悲观锁就恰好相反,以为本身读数据库的时候,别人可能恰好在写本身刚读的数据,其实就是持一种比较保守的态度;时间戳就是不加锁,经过时间戳来控制并发出现的问题。悲观锁就是在读取数据的时候,为了避免让别人修改本身读取的数据,就会先对本身读取的数据加锁,只有本身把数据读完了,才容许别人修改那部分数据,或者反过来讲,就是本身修改某条数据的时候,不容许别人读取该数据,只有等本身的整个事务提交了,才释放本身加上的锁,才容许其余用户访问那部分数据。乐观锁就比较简单了,就是不作控制,这只是一部分人对于并发所持有的一种态度而已。时间戳就是在数据库表中单独加一列时间戳,好比“TimeStamp”,每次读出来的时候,把该字段也读出来,当写回去的时候,把该字段加1,提交以前 ,跟数据库的该字段比较一次,若是比数据库的值大的话,就容许保存,不然不容许保存,这种处理方法虽然不使用数据库系统提供的锁机制,可是这种方法能够大大提升数据库处理的并发量,由于这种方法能够避免了长事务中的数据库加锁开销(操做员A 和操做员B操做过程当中,都没有对数据库数据加锁),大大提高了大并发量下的系 统总体性能表现。 须要注意的是,乐观锁机制每每基于系统中的数据存储逻辑,所以也具有必定的局 限性,如在上例中,因为乐观锁机制是在咱们的系统中实现,来自外部系统的用户 余额更新操做不受咱们系统的控制,所以可能会形成脏数据被更新到数据库中。在 系统设计阶段,咱们应该充分考虑到这些状况出现的可能性,并进行相应调整(如 将乐观锁策略在数据库存储过程当中实现,对外只开放基于此存储过程的数据更新途 径,而不是将数据库表直接对外公开)。以上悲观锁所说的加“锁”,其实分为几种锁,分别是:排它锁和共享锁,其中排它锁又称为写锁,共享锁又称为读锁。(ps.能够参考此文 “http://blog.sina.com.cn/s/blog_548bd2090100ir7k.html”)
2六、范式和反范式
1NF:无重复的列.表中的每一列都是不可分割的基本数据项.不知足1NF的数据库不是关系数据库.
2NF:属性彻底依赖于主键.不能存在仅依赖于关键一部分的属性.
3NF:属性不传递依赖于其它非主属性.非主键列必须直接依赖于主键,而不能传递依赖。即不能是:非主键A依赖于非主键B,非主键B依赖于主键.
范式能够避免数据冗余,减小数据库的空间,减轻维护数据完整性的麻烦.但等级越高的范式设计出来的表越多,可能会增长查询所需时间.当咱们的业务所涉及的表很是多,常常会有多表链接,而且咱们对表的操做要时间上要尽可能的快,这时能够考虑咱们使用“反范式”.也就是用空间来换取时间,把数据冗余在多个表中,当查询时能够减小或者是避免表之间的关联.
好比两个表(用户id,好友id)和(用户id,用户昵称,用户邮箱,联系电话)符合3NF,若是需查询某个用户的好友(昵称)名单,此时需对2个表进行链接查询,能够把第一个表修改为(用户id,好友id,好友昵称)这样只须要查询第一个表就可获取全部好友昵称.