2020 面试记录
# 存储
# 文件存储和块存储的区别
存储方式 | 技术实现 | 优势 | 劣势 | 典型代表 |
---|---|---|---|---|
块存储 | 裸盘上划分逻辑卷,逻辑卷格式化成任意文件系统 | 支持多种文件系统,传输速度快,提供硬件容错机制 | 无法实现网络共享 | FC-SAN,iSCSI |
文件存储 | 在格式化的磁盘上存储文件 | 提供网络共享 | 网络传输速度制约读写速度,分层目录结构限制可扩展性 | NFS,FAT,EXT3 |
对象存储 | 以灵活可定制的对象为存储单元,元数据服务器提供快速并发寻址 | 读写速度较快的同时支持网络共享,对象灵活定义 | 管理软件的购买、使用和运维成本高 | Swift |
# 存储设备不同
对象存储:对象存储的对应存储设备为 swift,键值存储,CEPH 的 RADOS。
文件存储:文件存储的对应存储设备为 FTP,NAS,NFS 服务器,Ceph 的 CephFS。
块存储:块存储的对应存储设备为 Cinder,硬盘,IPSAN、FCSAN、CEPH 的 RBD。
# 特点不同
对象存储:对象存储的特点是具备块存储的高速以及文件存储的共享等特性,只能进行全写全读,存储数据以大文件为主,要求足够的 IO 带宽。
文件存储:文件存储的特点是一个具有目录树结构的大文件夹,大家都可以获取文件。
块存储:块存储的特点是不能直接被操作系统访问,在分区、创建逻辑卷、格式化为指定文件系统后才可以使用,与平常主机内置硬盘的方式完全无异。
这三者的本质差别是使用数据的“用户”不同:
块存储的用户是可以读写块设备的软件系统,例如传统的文件系统、数据库;
文件存储的用户是自然人(电脑客户机);
对象存储的用户则是其它计算机软件。
参见:块存储、文件存储、对象存储这三者的本质差别是什么? - 知乎 (opens new window)
# RAID 是什么?各类型之间的区别
# Python
# 可变/不可变、引用类型/传值类型、深拷贝/浅拷贝
dict、list、set 是可变类型
# 中国电信云
TCP 的粘包问题
TCP 是一种面向连接的可靠传输协议,它将数据分割成小的数据块进行传输,并在接收端重新组装。但是,由于网络传输的不确定性,TCP 协议无法保证每个数据块都能准确地按照发送顺序到达接收端。这就导致了粘包问题的产生。
粘包问题主要有两种情况:
发送端粘包:发送端连续发送多个小的数据包时,TCP 协议可能会将它们合并成一个大的数据包进行传输。这样接收端就无法得知原始数据包的边界,从而导致粘包问题。
接收端粘包:接收端在接收数据时,由于 TCP 协议的缓冲区大小限制或者应用程序读取数据的速度较慢,可能会导致多个数据包被合并成一个大的数据包进行处理。这也会导致粘包问题的发生。
为了解决粘包问题,可以采取以下方法:
使用消息边界:在数据包中添加消息边界标识,接收端通过解析边界标识来分割数据包。
使用固定长度:发送端在发送数据包时,将每个数据包的长度固定为固定值,接收端按照该长度进行分割。
使用消息头:在数据包中添加消息头,包含数据包的长度信息,接收端根据消息头中的长度信息来进行分割。
以上方法都需要发送端和接收端进行配合,以确保数据的正确传输和解析。
import struct bytes_len = struct.pack('i',len(send_msg))
1
2- Socket 中粘包问题浅析及其解决方案 - 代码星冰乐 (opens new window)
- Socket 粘包问题 - liuslayer - 博客园 (opens new window)
- socket 粘包问题解决 - 要一直走下去 - 博客园 (opens new window)
- 对于 Socket 粘包的困惑? - 知乎 (opens new window)
- Socket 编程(4)TCP 粘包问题及解决方案 - melonstreet - 博客园 (opens new window)
- python--(socket 与粘包解决方案) - 孔辉 - 博客园 (opens new window)
- Python 中的粘包、socket 初识 - 知乎 (opens new window)
- 怎么解决 TCP 网络传输「粘包」问题? - 知乎 (opens new window)
后端数据校验怎么做的?
一般就是用装饰器或者 assert,网上查到一个JSON Schema | The home of JSON Schema (opens new window)和 Flask 专用的扩展sanjeevan/flask-json-schema: Flask extension to validate JSON requests using the jsonschema spec (opens new window)
- Pydantic
- Marshmallow
MySQL 事务语法
磁盘点灯对盘位使用什么工具?
smartctl
与sas2ircu
RESTful 的缺点是什么?
RESTful 的缺点包括:
缺乏标准化:RESTful 并没有明确的标准,因此在实践中可能存在一些差异,导致不同的实现方式和理解方式。 状态码,有的时候你需要“不存在的”一个状态码来描述你的操作;
可扩展性限制:RESTful 在设计时主要关注资源的表达和状态的转换,对于复杂的业务逻辑和数据处理可能存在限制。不是所有的东西都是“资源”,尤其是在业务系统中;
无状态性:RESTful 架构要求每个请求都是独立的,不依赖于之前的请求状态。这可能导致一些操作的复杂性增加,因为需要在每个请求中包含所有必要的信息。
安全性:RESTful 并没有内置的安全机制,需要在实际应用中进行额外的安全性设计和实施。
性能问题:由于 RESTful 架构需要通过 HTTP 协议进行通信,而 HTTP 协议本身存在一些性能上的限制,如每次请求都需要建立连接、传输的数据量较大等。
缺乏灵活性:RESTful 的资源和操作是固定的,不够灵活,对于一些特定的业务需求可能需要额外的处理和设计。 过度获取(overfetching),在一个 RESTful 架构下,因为后端开发人员定义在各个 URL 的资源上返回的数据,而不是前端开发人员来提出数据需求,使得按需获取数据会非常困难。经常前端需要请求一个资源中所有的信息,即便只需要其中的一部分数据。
缺乏约束:RESTful 并没有强制执行某些约束,如输入参数的验证、错误处理等,这可能导致一些安全和可靠性的问题。
一个适用于简单操作的接口规范而已,无规矩不成方圆,复杂操作并不适用,还是看业务发展需求的。
gRPC 与 RESTful 有什么区别?
gRPC 和 RESTful 是两种不同的通信协议和架构风格。
通信协议:gRPC 使用的是基于 HTTP/2 的二进制协议,而 RESTful 使用的是基于 HTTP/1.1 的文本协议。gRPC 的二进制协议可以更高效地序列化和传输数据,提供更快的网络通信速度和更小的网络开销。
数据格式:gRPC 使用 Protocol Buffers 作为默认的数据序列化和编码格式,而 RESTful 一般使用 JSON 或 XML。Protocol Buffers 相比 JSON 和 XML 具有更小的数据体积和更高的编解码效率。
API 设计:gRPC 使用基于服务定义的接口描述语言(IDL)来定义 API,然后通过自动生成的客户端和服务器端代码来实现通信。而 RESTful 使用 URI(统一资源标识符)和 HTTP 方法来定义资源和操作。
支持语言:gRPC 支持多种编程语言,包括 Java、C++、Python 等,而 RESTful 可以使用任何支持 HTTP 协议的编程语言。
安全性:gRPC 提供了基于 TLS 的安全传输,而 RESTful 需要通过 HTTPS 来实现安全传输。
总的来说,gRPC 相比 RESTful 在性能、效率和安全性方面有一定的优势,特别适合于高性能、分布式和复杂的系统。而 RESTful 更加简单和灵活,适用于简单的 API 设计和开发。选择使用哪种通信协议取决于具体的需求和场景。
- 优点
- protobuf 二进制消息,性能好/效率高(空间和时间效率都很不错)
- proto 文件生成目标代码,简单易用
- 序列化反序列化直接对应程序中的数据类,不需要解析后再进行映射(XML,JSON 都是这种方式)
- 支持向前兼容(新加字段采用默认值)和向后兼容(忽略新加字段),简化升级
- 支持多种语言(可以把 proto 文件看做 IDL 文件)
- 缺点
- gRPC 尚未提供连接池,需要自行实现
- 尚未提供“服务发现”、“负载均衡”机制
- 因为基于 HTTP2,绝大部多数 HTTP Server、Nginx 都尚不支持,即 Nginx 不能将 GRPC 请求作为 HTTP 请求来负载均衡,而是作为普通的 TCP 请求。(nginx1.9 版本已支持)
- Protobuf 二进制可读性差(貌似提供了 Text_Fromat 功能)
- 默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持)
# unitedStack(同方有云)
跳出循环的方式
- break
- return
- raise Exception
代码调试
- logging
- pdb
- pycharm debug
Python 调试代码的 4 种方法:print、log、pdb、PyCharm 的 debug_xiemanR 的专栏-CSDN 博客 (opens new window)
索引优缺点
- 优点
- 索引可以减少服务器需要扫描的数据量,从而大大提高查询效率。
- 唯一索引能保证表中数据的唯一性。
- 利用索引对数据存储的特性,可以使查询语句避免排序和创建临时表。
- 索引可以将随机 I/O 变为顺序 I/O。
- 缺点
- 索引的创建和维护会造成工作量的增加。
- 索引会造成数据量的增加,除了数据表中数据占数据空间之外,每一个索引还要占一定的物理空间。
- 不恰当的使用索引会造成服务器重复扫描数据,造成查询浪费。
- 优点
class 实现上下文管理器
要实现一个上下文管理器,需要定义一个类,并在类中实现__enter__()和__exit__()方法。
__enter__()
方法在进入上下文之前被调用,通常用于初始化资源或执行一些准备工作。该方法可以返回一个值,该值将被赋值给as
子句中的变量。
__exit__()
方法在离开上下文时被调用,通常用于清理资源或处理异常。该方法接收三个参数,分别是异常类型、异常对象和追溯信息。如果在上下文中没有发生异常,这三个参数都为 None。如果想要处理异常,可以在该方法中返回 True,否则异常将被重新抛出。
下面是一个示例,演示如何实现一个简单的上下文管理器:
class MyContextManager:
def __enter__(self):
print("Entering context")
return "Hello"
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting context")
if exc_type is None:
print("No exception occurred")
else:
print(f"Exception occurred: {exc_type}, {exc_value}")
with MyContextManager() as value:
print(value)
raise ValueError("Something went wrong")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输出结果为:
Entering context
Hello
Exiting context
Exception occurred: <class 'ValueError'>, Something went wrong
Traceback (most recent call last):
File "<stdin>", line 10, in <module>
ValueError: Something went wrong
2
3
4
5
6
7
在上面的示例中,__enter__()
方法打印了"Entering context"并返回了字符串"Hello"。在with
语句中,返回值被赋值给了变量 value。然后,我们手动抛出了一个 ValueError 异常。在__exit__()
方法中,它打印了"Exiting context"和异常信息。
请注意,在上下文管理器中,__enter__()
方法的返回值可以在 with 语句块中使用,而__exit__()
方法的返回值不会被使用。
js 异步编程返回的对象类型
Promise 对象
什么是 RESTful?哪些操作是幂等的?
HTTP 幂等方法是指无论调用多少次都不会有不同结果的 HTTP 方法。它无论是调用一次,还是十次都无关紧要。结果仍应相同。再次强调,它只作用于结果而非资源本身。它仍可能被操纵(如一个更新的 timestamp),提供这一信息并不影响(当前)资源的表现形式。
HTTP Method | Idempotent | Safe |
---|---|---|
OPTIONS | yes | yes |
GET | yes | yes |
HEAD | yes | yes |
PUT | yes | no |
POST | no | no |
DELETE | yes | no |
PATCH | no | no |
ref:哪些是幂等或/且安全的方法? - RESTful 手册 (opens new window)
- Python 中如何动态调用类方法?
参阅Python 中动态调用函数或类的方法 | 别院牧志 (opens new window)
- 项目中实际使用继承和多态的例子?
# 志翔科技
- 简述一次 web 请求的过程?
一次完整的 HTTP 请求过程从 TCP 三次握手建立连接成功后开始,客户端按照指定的格式开始向服务端发送 HTTP 请求。
- 服务端接收请求后,对 HTTP 请求中的参数进行解析,包括请求的 URL,请求方法,参数以及 Cookie 等参数,将其置于框架的一个内部数据结构中,便于后续的使用。
- 在处理完请求参数后,会在请求正式进入视图函数之前做一些额外处理,例如:验证 CSRF-Token,验证用户 Cookie 是否合法,请求的 IP 是否处于白名单中,如果验证信息未通过,则直接返回相应的 HTTP 状态码以及相关信息,增强网站的安全性。在所有的验证通过之后,Web 框架根据 URL 找到对应的视图函数并进行处理,在处理过程中可能会涉及数据库,Redis 以及消息队列的使用,并很可能存在异步任务的触发。
- 之后根据请求类型和请求 url 中的路由与视图函数映射关系获取到底请求的是哪个视图,匹配完成之后,到 view 层的具体视图中执行特定的视图函数。在视图执行过程中,需要先到 template 模板层找到特定的 html 文件,在此过程中可能会操作 model 层(数据库),然后对视图进行渲染。
- 在视图函数处理过程中,很有可能因为某些操作而导致异常的产生,此时 Web 应用应该判断异常产生的由来,并进行统一的异常处理。 不管是数据库连接异常,还是用户表单验证未通过,都应该给出一个统一的应答,这样便于前端的数据处理,也能够让用户知道到底发生了什么。
- 处理完业务逻辑,最后返回一个 HTTP 的响应给客户端,HTTP 的响应内容同样有标准的格式。无论是什么客户端或者是什么服务端,大家只要按照 HTTP 的协议标准来实现的话,那么它一定是通用的。
# 参考链接
SOLID 原则?
SOLID 是 5 个设计原则开头字母的缩写,其本身就有“稳定的”的意思,寓意是“遵从 SOLID 原则可以建立稳定、灵活、健壮的系统”。5 个原则分别如下:
Single Responsibility Principle(SRP):单一职责原则。
一个类只应该有一种被修改的原因(比如爬虫类,应该将数据抓取和数据持久化封装到两个类中)
Open Close Principle(OCP):开闭原则。
类应该对改动关闭,对扩展开放。比如频繁修改的
if
可以封装为一个方法,然后继承并实现Liskov Substitution Principle(LSP):里氏替换原则。
继承父类的子类应该可以完全替换父类而不影响调用父类的程序执行,子类的方法参数应该和父类同名方法完全一致,或者更为宽松
Interface Segregation Principle(ISP):接口隔离原则。
客户端程序不应该依赖它不需要的接口方法,一个接口所提供的方法,应该就是调用方所需要的方法
Dependency Inversion Principle(DIP):依赖倒置原则。
程序要依赖于抽象接口,不要依赖于具体实现。高层模块和低层模块都应该依赖于抽象。 面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。 面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。
依赖倒置前:
依赖倒置后:
# 佳讯飞鸿
- Python2 与 Python3 区别?
Python 2 和 Python 3 有哪些主要区别? - 知乎 (opens new window)
- 谈谈你对 Pythonic 的理解?
Pythonic 是指遵循 Python 语言的风格和原则的编程方式。它强调简洁、可读性和可维护性,以及利用 Python 的特性和内置函数来实现代码的高效性。
Pythonic 的特点包括:
易读性:Pythonic 代码应该易于阅读和理解,遵循明确的命名规范和代码结构。例如,使用有意义的变量名和函数名,以及遵循一致的缩进和代码布局。
简洁性:Pythonic 代码应该尽量简洁,避免冗余的代码和复杂的逻辑。Python 提供了许多简洁的语法特性,例如列表推导式、生成器表达式和装饰器,可以帮助我们用更少的代码实现相同的功能。
利用内置函数和库:Python 提供了大量的内置函数和库,可以帮助我们实现常见的任务。Pythonic 的代码应该善于利用这些内置函数和库,而不是重复造轮子。
面向对象:Python 是一门面向对象的语言,Pythonic 代码应该充分利用面向对象的特性,例如类、继承和多态。同时,Python 还支持面向过程和函数式编程,Pythonic 的代码可以根据具体情况选择合适的编程范式。
鸭子类型:Python 是一门动态类型语言,鸭子类型是其特色之一。Pythonic 的代码应该充分利用鸭子类型,而不是过度关注对象的具体类型。这样可以使代码更灵活、可复用和可扩展。
总之,Pythonic 的编程方式是一种符合 Python 语言特性和设计哲学的风格,它能够使代码更加简洁、可读性更强、易于维护,并且充分利用 Python 提供的各种功能和库。
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22