相关文章推荐
玩篮球的火锅  ·  日常点滴 - 一抹浅笑 ·  2 天前    · 
玩篮球的火锅  ·  在Windows ...·  3 月前    · 
玩篮球的火锅  ·  python importlib ...·  10 月前    · 
玩篮球的火锅  ·  Class JToken | ...·  11 月前    · 
玩足球的斑马  ·  System.Runtime.Interop ...·  4 分钟前    · 

开发:前端,后端,产品经理     UI:1人    测试 、运维、运营、销售、人事、财务

5.认证方式和权限方式

四种认证方式:session、cookie、token、jwt(json web token)--将用户信息加密,加密后作为token,https://blog.csdn.net/qq_40081976/article/details/79046825       https://www.jianshu.com/p/af8360b83a9f        https://www.cnblogs.com/zaixiuxing/p/6005968.html

什么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分

单点登录的原理:https://www.cnblogs.com/ywlaker/p/6113927.html

6.将去重规则放在redis中的意义?

意义:除了快,最主要的是可以实现类似于分布式的构造,将调度器和去重规则剖离出来,可以实现每一个爬虫应用的调度。

7.在Python环境下用尽可能多的方法反转字符串,例如将s = "abcdef"反转成 "fedcba"

第一种:使用字符串切片
result = s[::-1]
第二种:使用列表的reverse方法
l = list(s)
result = "".join(l.reverse())
当然下面也行
l = list(s)
result = "".join(l[::-1])
第三种:使用reduce
result = reduce(lambda x,y:y+x,s)
第四种:使用递归函数
def func(s):
    if len(s) <1:
        return s
    return func(s[1:])+s[0]
result = func(s)
第五种:使用栈
def func(s):
    l = list(s) #模拟全部入栈
    result = ""
    while len(l)>0:
        result += l.pop() #模拟出栈
    return result
result = func(s)
第六种:for循环
def func(s):
    result = ""
    max_index = len(s)-1
    for index,value in enumerate(s):
        result += s[max_index-index]
    return result
result = func(s)
View Code

8.django和flask的区别

django  大而全     flask 小而精

如果应用非常简单,使用flask就会非常的快捷;如果应用比较复杂,就使用django,因为flask把所有的第三方组件拼起来就是个django。

9.python中的垃圾回收机制

python作为一门动态语言,一个简单的赋值语句也是很值得研究的,重要特点就是 引用和对象分离

python中使用 引入计数为主,标记清除,和分代回收为辅

每次创建对象都会分配内存,容器对象跟数字不一样呢?这是因为Python的内存池机制。

Python内存池

如果频繁的调用 malloc 与 free 时,是会产生性能问题的.再加上频繁的分配与释放小块的内存会产生内存碎片.

Python 在这里主要干的工作有:

如果请求分配的内存在1~256字节之间就使用自己的内存管理系统,否则直接使用 malloc.

引用计数增加的情况:

1.对象被创建

2.对象被当成参数传入函数。

3.对象的引用被创建   比如:a=1   b=a

4.作为容器对象的一个元素

引用计数减少的情况:

1.对象被删除   比如:del a

2.对象从容器中删除   比如:li.remove(a)

3.对象被重新赋值

对于循环引用的对象,比如:a=[1,2],b = a.append(a),a.append(b),会 采用标记清除

引用计数是采用轮询的方式查看那些引用为0,这样就会出现一个问题,效率非常的低,为了解决效率低的问题,python引入了分代回收的机制。

分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。同时,分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象

创建对象和取消创建对象的差值700的时候
# gc.get_threshold()
# 链表 0代 1代 2代
# 每一代被触发垃圾回收的时候 剩下没有被回收的对象会被移到上一代里面
# 当0代被触发10次的时候 会触发1代的回收 也会回收0代的
# 当1代被触发10次的时候 会触发2代的回收 全部回收

10.flask-session的 作用:

将flask中的session由原来放置在加密cookie中,改为放置到其他数据源。

11.flask-session的原理

在flask中,请求进来后,会先执行open_session方法,该方法读取用户cookie中的session_id对应的随机字符串;如果获取到,根据随机字符串去redis中获取原来设置的值,并创建一个包含该值的字典。如果未获取到,就生成一个随机字符串并在内存中创建一个空字典。

在视图函数中,可以对该字典进行操作。

当请求结束时,会执行save_session方法,该方法去内存中读取这个特殊的字典,并将字典序列化为字符串,然后写到redis中,在将随机字符串写到用户的cookie中返回。

12.DBUtils的作用和原理

作用:创建数据库连接池。

原理: 启动时会在内存中维护一个连接池,当请求连接数据库时则去连接池中获取一个连接,如果有则获取,没有则等待或报错。使用完毕后,将连接归还到连接池中

13.写代码的过程中有没有令你印象深刻的事情。

比如:使用flask文件上传的时候,使用flask定制上传文件大小的错误信息时,使用mac是没有问题的,而使用Windows就会报错。系flask中的一个bug。

from gevent.pywsgi import WSGIServer    
if __name__ == '__main__':
    # app.run(host='127.0.0.1',port=5000)
    http_server = WSGIServer(('127.0.0.1', 5000), app)
    http_server.serve_forever()

14.上传一个excle文件并读取每一行的内容

# 上传一个Excel文件并读取其中每一行的内容 xlrd 模块
import xlrd
f = xlrd.open_workbook(r'D:\新建文件夹\工作簿1.xlsx')
ret = f.sheets()[0] # 获取第一个表格对象
print(type(ret))   # <class 'xlrd.sheet.Sheet'>
n = ret.nrows   #获取行数
m = ret.ncols   # 获取列数
# 获取所有的数据
for i in range(n):
    for j in range(m):
        print(ret.row_values(i)[j],end=' ')      # ret.row_values(i)  获取第几行的数据
        # print(ret.col_values(j))
    print()

15. 请在Context类下完成实现

执行 with-as 时,会先执行 __enter__方法,并将该方法的返回值,赋值给as 后的对象,然后执行with-as 中的代码块,不管代码块中的代码是否有异常,都会在离开执行__exit__方法,可以在__exit__方法中做一些收尾工作或者捕捉异常的处理操作。

所以,使用with - as 方法时,对象必须实现__enter__和__exit__方法。

class Context(object):
    def __enter__(self):
        return self
    def do_something(self):
    def __exit__(self, exc_type, exc_val, exc_tb):
with Context() as ctx:
    ctx.do_something()

16.简述SQl注入的攻击原理以及如何在代码层面防止?

SQL的攻击原理: 通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令

代码层面:引起的主要原因在于后台写SQL语句的时候用了字符串拼接

解决方法:1 采用预编译语句集  2,对传过来的参数进行过滤 比如正则检查是否包含非法字符等

17.django内置的缓存机制。
django的缓存有六种
-- memchched缓存
-- 数据库缓存
-- 文件系统缓存
-- 本地内存缓存
-- 虚拟缓存
-- 自定义缓存
-- 应用
-- 全栈缓存 粒度大 中间件配置
-- 单独视图缓存 粒度适中 视图上方加装饰器或者在路由器配置

18.什么是xss攻击。

-- XSS攻击是向网页中注入恶意脚本,用在用户浏览网页时,在用户浏览器中执行恶意脚本的攻击。
-- XSS分类,反射型xss ,存储型xss
-- 反射型xss又称为非持久型xss,攻击者通过电子邮件等方式将包含注入脚本的链接发送给受害者,
受害者通过点击链接,执行注入脚本,达到攻击目的。
-- 持久型xss跟反射型的最大不同是攻击脚本将被永久的存放在目标服务器的数据库和文件中,多见于论坛
攻击脚本连同正常信息一同注入到帖子内容当中,当浏览这个被注入恶意脚本的帖子的时候,恶意脚本会被执行
-- 防范措施 1 输入过滤  2 输出编码  3 cookie防盗
1,输入过滤 用户输入进行检测 不允许带有js代码
2,输出编码 就是把我们的脚本代码变成字符串形式输出出来
3,cookie加密

19.偏函数的作用和好处

把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

当函数的参数个数太多,需要简化时,使用 functools.partial 可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

20.判断101-200之间有多少个质数(一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫质数),并输出所有的质数。

解题思路:首先这个数肯定得大于1,而且能被其他自然数整除,就意味着整除它的数必然小于它本身,(如果大于其本身,得到是一个小数),所以可以写一个函数,将这个数被所有的数(大于1,小于本身)整除,(取余数,如果余数为0,表示可以整除,就返回一个true),根据这个函数的返回值,就可以过滤出所有的质数。

def func(x):
    if x >1:
        if x==2:
            return True
        for i in range(2,x):
            if x % i ==0:
                return True
for i in range(101,201):
    ret = func(i)
    if not ret:
        print(i)

21.websocket是什么?

websocket是一套基于TCP连接的协议,这个协议规定:客户端向服务端创建连接后不断开。现象:客户端向服务端发送请求,服务端也可以主动向客户端推送消息。 它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。

22.websocket建立连接时,会先进行握手,认证的过程,

注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11

发起websocket请求时,会先进行一个认证握手的过程,这个过程为:

1.用户发起websocket请求后,会先在请求头中获取Sec-WebSocket-Key对应的值,再将这个值与majic_string(魔法字符串<这个字符串是固定的>)相加,将相加的结果先通过hashlib.sha1加密,在通过base64加密,然后在将加密后的结果返回给客户端。客户端收到后校验是不是采用的这张加密,通过后,建立起连接,否则就会拒绝连接。

握手成功后才能发送数据,而且收发数据是加密的。

客户端和服务端收发数据

客户端和服务端传输数据时,需要对数据进行【封包】和【解包】。

客户端的JavaScript类库已经封装【封包】和【解包】过程,但Socket服务端需要手动实现。

解包过程:

服务端接收到客户端发送的数据后,后先取出第二个字节的后七位,然后,对这后七位进行一个判断:(后七位转化为数字最大为127),如果后七位<=125,那么前两个字节就是报文;如果后七位=126,则继续往后读16个字节,也就是取前四个字节作为报文;如果后七位=127,则往后读64位,也就是取前10个字节作为报文。然后剩余的数据取前四个字节作为masking_key(掩码key),对剩下的数据在每个字节的与masking_key做位运算,最终得到真实的数据。

websocket的使用场景

websocket主要用于页面数据的实时更新。

web实现数据的实时更新的方案:

1.长轮询           兼容性好

2.websocket          性能更优

23.给装饰器写log

def logwrap(wrap):
    def log_inner(func):
        print('装饰器执行之前')
        ret = wrap(func)
        return ret
    return log_inner
@logwrap   #  wrapper = logwrap(wrapper) = log_inner
def wrapper(func):
    def inner(*args,**kwargs):
        print('before')
        ret = func(*args,**kwargs)
        print('after')
        return ret
    return inner
@wrapper    # func = wrapper(func) = log_inner(func)
def func():
    print('哈哈哈')
func()
View Code
v = [lambda:x for x in range(10)]
print( v ) # 含有多个函数名的列表
print( v[0] ) # 第一个函数名
print( v[0]() ) # 9 等同于下面
for i in range(10):
def f():
return i
print(f())


25.mysql表的水平分表和垂直分表

水平分表用于表数据量很大时,垂直分表用于表中的字段很多

1,水平分割:

例:QQ的登录表。假设QQ的用户有100亿,如果只有一张表,每个用户登录的时候数据库都要从这100亿中查找,会很慢很慢。如果将这一张表分成100份,每张表有1亿条,就小了很多,比如qq0,qq1,qq1...qq99表。

用户登录的时候,可以将用户的id%100,那么会得到0-99的数,查询表的时候,将表名qq跟取模的数连接起来,就构建了表名。比如123456789用户,取模的89,那么就到qq89表查询,查询的时间将会大大缩短。

这就是水平分割。

2,垂直分割:

垂直分割指的是:表的记录并不多,但是字段却很长,表占用空间很大,检索表的时候需要执行大量的IO,严重降低了性能。这时需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。

26.(HBase)列存储

27、简述一致性哈希原理和它要解决的问题

详情参考:https://blog.csdn.net/lihao21/article/details/54193868

原理:一致性hash算法通过一个叫作一致性hash环的数据结构实现。这个环的起点是0,终点是2^32 - 1,并且起点与终点连接,环的中间的整数按逆时针分布,故这个环的整数分布范围是[0, 2^32-1]。将对象和机器均匀的放置在hash环上,在hash环上顺时针查找距离这个对象的hash值最近的机器,即是这个对象所属的机器。当增加或者减少机器,不会缓存命中的命中率

一致性hash算法解决了分布式环境下机器增加或者减少时,简单的取模运算无法获取较高命中率的问题。通过虚拟节点的使用,一致性hash算法可以均匀分担机器的负载,使得这一算法更具现实的意义。正因如此,一致性hash算法被广泛应用于分布式系统中。

28、C10K问题和解决方案

C10K问题(即单机1万个并发连接问题) C10M问题 (即单机1千万个并发连接问题)

解决方案:epoll和协程。

29、classmethod和staticmethod的使用场景

应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)
a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间
print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)
staticmethod的应用场景

classmethod的使用场景:工厂模式

30.RabbitMQ的应用场景

1,异步的处理:比如注册后,将注册信息持久化后,需要发送注册邮件和注册短信,然后返回注册成功,而持久化数据后,发送邮件和短信不是必须的,就可以返回注册成功,只需要在持久化到数据库后,将发送邮件和短信放到消息队列中即可。然后返回注册成功。

2,应用的解耦:比如订单系统和仓库系统,当订单量很大时,将订单系统持久化后,就可以加入到消息队列中,仓库系统可以根据消息队列进行操作,而不会出现系统的宕机造成数据的丢失。

3,流量的削峰:比如秒杀活动,建立一个长度限制的消息队列,请求进来后,将请求加入到消息队列中,超过阈值后直接将后续的请求返回

详情请参考:https://baijiahao.baidu.com/s?id=1608348586147010884&wfr=spider&for=pc

 RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较
RabbitMQ中,所有生产者提交的消息都由Exchange来接受,然后Exchange按照特定的策略转发到Queue进行存储
RabbitMQ提供了四种Exchange:fanout,direct,topic,header
header模式在实际使用中较少,本文只对前三种模式进行比较。
性能排序:fanout > direct >> topic。比例大约为11:10:6
一.Direct Exchange
0ec0f465-49c6-361c-ae2b-dd951a6ed1a9
任何发送到Direct Exchange的消息都会被转发到RouteKey中指定的Queue。
1.一般情况可以使用rabbitMQ自带的Exchange:”"(该Exchange的名字为空字符串,下文称其为default Exchange)。
2.这种模式下不需要将Exchange进行任何绑定(binding)操作
3.消息传递时需要一个“RouteKey”,可以简单的理解为要发送到的队列名字。
4.如果vhost中不存在RouteKey中指定的队列名,则该消息会被抛弃。
二.Fanout Exchange 
0bbdcd3d-9fc6-3107-b7e0-db67c174d46a
任何发送到Fanout Exchange的消息都会被转发到与该Exchange绑定(Binding)的所有Queue上。
1.可以理解为路由表的模式
2.这种模式不需要RouteKey
3.这种模式需要提前将Exchange与Queue进行绑定,一个Exchange可以绑定多个Queue,一个Queue可以同多个Exchange进行绑定。
4.如果接受到消息的Exchange没有与任何Queue绑定,则消息会被抛弃。
三.Topic Exchange
11171ab4-af07-3ff6-bdf6-d1febda679c3
任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上
1.这种模式较为复杂,简单来说,就是每个队列都有其关心的主题,所有的消息都带有一个“标题”(RouteKey),Exchange会将消息转发到所有关注主题能与RouteKey模糊匹配的队列。
2.这种模式需要RouteKey,也许要提前绑定Exchange与Queue。
3.在进行绑定时,要提供一个该队列关心的主题,如“#.log.#”表示该队列关心所有涉及log的消息(一个RouteKey为”MQ.log.error”的消息会被转发到该队列)。
4.“#”表示0个或若干个关键字,“*”表示一个关键字。如“log.*”能与“log.warn”匹配,无法与“log.warn.timeout”匹配;但是“log.#”能与上述两者匹配。
5.同样,如果Exchange没有发现能够与RouteKey匹配的Queue,则会抛弃此消息。
RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较

31.HTTP协议怎么区分长连接和短连接

HTTP的长连接和短连接本质上是TCP长连接和短连接。

在HTTP/1.0中默认使用短连接。而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码: Connection :keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

32、redis中持久化的两种方式,区别,如何开启

redis中持久化的功能默认是不开启的。

redis的持久化分为两种:

RDB    (point in time snapshot) 基于时间点的快照技术,优点:持久化速度快,需要的磁盘较少。缺点:只能将数据定格在一个时间  点,不能记录数据的变化过程。应用场景:常用于备份,主从复制就是基于RDB功能的。

AOF:Append Only Logfile   只追加的日志文件,保存所有键值对修改的变化过程 。优点:记录数据的变化过程,更加安全。缺点:持久  化速度相对慢,需要更多的磁盘空间。用处:需要数据安全性和一致性要求更高业务场景,Redis分布式架构基于AOF来实现高可用。

33、什么是响应式布局?

通过页面窗口的大小做出响应不同的展示效果,通过@media属性    @media (max-size:123) {...}

34、MySQL数据库中开启锁的方式:begin;操作语句加for update;释放锁:commit;

django中开启锁的方式: from api import models
from django.db import transaction
with transaction.atomic():
res = models.Comment.objects.all().for_update()

35.为什么要有消息队列

为了应用之间的通信(rpc),解决供求关系(生产者消费者模型)

36. 程序之间如何实现通信

1. 使用restful api(json数据格式)     2.webservice(xml数据格式)     3. rpc(基于消息队列实现应用程序之间进行通信)

37.令你印象最深刻的官方文档:rabbitmq  写得通俗易懂,对于没有接触过的人来说,非常容易上手。

38.tcp是如何保证不丢包的

39, 多进程的部署

40,使用列表推导式实现快排

 
推荐文章