深信服一面总结
1 项目
项目是C++集群聊天服务器里面涉及到主要的技术栈有 nginx负载均衡器、基于redis的发布订阅序列、muduo网络库(使用的时候特别的简单,但是面试官一般会问它的底层代码是怎么实现的,最主要的思想是epoll+线程池)、mysql、json序列化和反序列化
(1)怎么处理多个客户端的连接
epoll去监听客户端的连接,当有多个客户端到来之后条件变量利用广播的方式去唤醒多个线程,线程来任务队列取走任务。
线程池的创建
a 设置一个生产者消费者队列作为临界资源
b 初始话n个线程 并让其运行起来,加锁去队列去任务运行(可以设置管理线程,根据任务的需要适时的增加和销毁线程,增加和销毁线程是有一定的步长,通过唤醒线程让其自杀的方式去销毁线程)
c 当任务队列为空的时候,所有线程阻塞
d 当生产者队列来了一个任务的时候,先对队列加锁,把任务挂在队列上,然后使用条件变量去通知阻塞中的一个线程。
再来补充一下条件变量的知识贯通一下。
条件变量不是锁,但是也可以使线程阻塞,经常与互斥锁联合使用,条件变量一般会做三件事情
a 阻塞等待条件变量满足
b 释放已经掌握的互斥锁相当于 pthread_mutex_unlock(&mutex)(a ,b两步为一个原子操作)
c 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁 pthread_mutex_lock(&mutex);
条件变量可以减少生产者内部之间的竞争,减少资源的浪费。
(2)如果一个线程一直读一个socket怎么办
将多个socket保存下来,设置定时器,按照优先级去读socket。
(3)nginx负载均衡策略
轮询,ip_hash,ip_least.ip_weight;
企业常采用 ip_hash加一致性哈希算法的策略,每个客户最好连接固定的服务器,每个服务器上有固定的缓存。一致性哈希算法目的就是某台服务器挂掉之后尽量减少影响。
一致性哈希算法
如果是32位系统。用缓存数据除以2的32 次方取余数,通过这个余数把服务器映射到一个圆上,但是有可能会出现哈希倾斜的问题,针对此问题,最好虚拟出一些节点。使服务器均衡的映射到服务器。
nginx如何配置基于http或者基于tcp的负载均衡
Nginx使用了一个新的stream模块来实现TCP负载均衡,这个模块,类似于http和mail模块,允许我们配置一组监听TCP连接的服务。允许你配置多个服务的TCP连接,通过在upstream的server组中配置proxy_pass指令。修改nginx.conf文件,在http模块的统计目录,添加一个stream模块(和http等同级):
(4)redis的五大数据类型
(a)string :最基本的是 string 可以包含任何数据 比如 图片或者序列化的对象。一个redis中字符串value最多可以是512M
(b) hash 是一个string类型的field和value的映射表,hash特别适合与存储对象
(c) list 常作为消息队列使用
(d)set 适合去重 求交集
(e)zset
redis是基于内存的操作,所以要比直接访问数据库快
Redis 持久化机制
Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。
实现:单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。
RDB是redis默认的持久化方式。
AOF:redis会将每一个收到的写命令都通过write函数追加到文件最后
当两种方式同时开启时,数据恢复redis会优先选择AOF恢复。
缓存雪崩:原有缓存失效,新缓存未到,采用了相同的过期时间,再同一时刻出现大面积的缓存过期。
解决办法:
(1)对数据进行加锁或者队列的方式保证不会有大量的线程对数据库进行一次性读写
(2)设置不同的缓存失效时间
缓存击穿:
指一个key非常热点,高并发集中访问这个key,当这个key在失效瞬间,仍然持续的高并发访问穿破缓存。
解决方法:
在访问key之前,采用setnx 来设置另一个短期key来锁住当前key的访问,访问结束后再删除该短期key
关于redis的总结暂且写到这里,还有好多关于redis的问题
2 linux
(1)linux常见的信号
Ctrl + c → 2) SIGINT(终止/中断) "INT" ----Interrupt
Ctrl + z → 20) SIGTSTP(暂停/停止) "T" ----Terminal 终端。
Ctrl + \ → 3) SIGQUIT(退出)
除0操作 → 8) SIGFPE (浮点数例外) "F" -----float 浮点数。
非法访问内存 → 11) SIGSEGV (段错误)
总线错误 → 7) SIGBUS
(2) linux查看进程占用内存的命令 top
(3)linux 查看cpu占用资源情况的命令 top
(4)僵尸进程:子进程退出 之后,还残留一部分的PCB资源。父进程未对其进行回收
(5)僵尸进程存在的意义:父进程可以知道子进程结束时的状态信息。
(6)linux 查看连接数
a 查看网络状态 netstat -an
b 查看 8080端口连接的情况 netstat -an|grep 80
c 统计80端口的连接数 netstat -nat | grep -i "80" |wc -l
d 统计httpd协议连接数 ps -ef | grep httpd | wc -l
e 统计已连接上的,状态为“established” netstat -na | grep ESTABLISHED | wc -l
f 查出每个IP地址连接数
netstat -na | grep ESTABLISHED | awk '{print$5}' | awk -F : '{print$1}' |sort |uniq -c | sort -r
(7) ping 百度发送icmp包
(8)路由是怎么传递的
(9)TCP与UDP的区别
TCP: 面向连接的可靠的数据包传递
数据稳定、速率稳定、流量稳定
使用场景:大文件,重要文件的传输
UDP :无连接的不可靠的报文传递;
效率高,速度快,不稳定
使用场景,对实时性要求比较高的,视频会议和视频电话。
3 C++
(1)map的底层是什么?为什么
map的底层是红黑树,为什么用红黑树;红黑树在查找、插入、删除的复杂度都是o(logn)且性能稳定。
(2)c++内存的模型
操作系统将C语言代码分为四个区:
1、栈区
2、堆区
3、全局区
4、程序代码区
(3)malloc的原理
a 当开辟的空间小于 128K 时,调用 brk()函数,malloc 的底层实现是系统调用函数 brk(),其主要移动指针 _enddata(此时的 _enddata 指的是 Linux 地址空间中堆段的末尾地址,不是数据段的末尾地址)
b 当开辟的空间大于 128K 时,mmap()系统调用函数来在虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空间来开辟。
(4)堆的概念
a 堆中某个节点的值总是不大于或不小于其父节点的值;
b 堆总是一棵完全二叉树。
4 算法题
1 LRU 已经写过答案
2 循环移动K个元素
3 堆排序