项目 / WEB相关 - 面试准备

项目的一些思考

基础知识

1.常见的反爬虫和应对方法

  • 同一个ip段时间多次访问同一页面
    • 利用代理ip,构建ip池
  • 基于用户行为,请求头里的user-agent
    • 构建user-agent池(操作系统、浏览器不同,模拟不同用户)
  • 动态加载,异步请求,js渲染
    • 模拟ajax请求,返回json形式的数据
    • selenium / webdriver 模拟浏览器加载
  • 请求频率过高
    • 容错机制

2.分布式任务管理和数据处理:Redis

  • 其实很有意思的一件事是因为Redis本身单线程的特性,自然实现了任务队列的并发安全,并且由于他的高效性,我们也大可不必担心类似串行化的过程中性能不足。
  • 任务容错:对于任务队列和结果队列之间,加入了检测列表:
    • 分机将任务完成时,同时删除检测列表中的对应任务
    • 在任务队列空的情况下,将对检测队列和任务队列进行对比,若检测队列不为空,说明出现了发布任务但是未爬取完成的情况,则将检测队列中的任务重新发布到任务队列。
  • Redis中的结果集主要作为中间件,将数据持久化,存储到sqlite上。

3.数据处理:sqlite

  • SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,不需要在系统中配置。就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 直接访问其存储文件。所以说,与其说他是传统SQL的替代品,不如说他是fopoen的加强版。
    • 一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。
    • SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。
  • 局限性和一些特性:
    • SQLite在做CRDU操作时都默认开启了事务,然后把SQL语句翻译成对应的SQLiteStatement并调用其相应的CRUD方法,此时整个操作还是在rollback journal这个临时文件上进行,只有操作顺利完成才会更新db数据库,否则会被回滚。CRUD:增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)
    • SQLite数据库只允许增加字段而不允许修改和删除表字段,只能创建新表保留原有字段,删除原表

中间件:Nginx、gunicorn

1.Nginx

  • IO 多路复用机制:可以监视多个描述符的读 / 写等事件,一旦某个描述符就绪(一般是读或者写事件发生了),就能够将发生的事件通知给关心的应用程序去处理该事件。
    • select+poll:fd需要每次都从用户态发送给内核态,每次调用 select 都需要在内核遍历传递进来的所有 fd
    • epoll:现在是线程安全的,而 select 和 poll 不是。内部使用了 mmap 共享了用户和内核的部分空间,避免了数据的来回拷贝。基于事件驱动,epoll_ctl 注册事件并注册 callback 回调函数,epoll_wait 只返回发生的事件避免了像 select 和 poll 对事件的整个轮寻操作。
  • Nginx 返回 502 错误的可能原因?
    • 这个得分情况分类讨论,一般可能是后端服务器挂了,也有可能是 Proxy Buffer 不够
  • 正向代理和反向代理之间的区别是什么?
    • 正向代理:代理端代理的是客户端
    • 反向代理:代理端代理的是服务端
  • 什么是负载均衡?
    • 代理服务器将接收的请求均衡的分发到各服务器
    • 轮询 (round-robin):轮询为负载均衡中较为基础也较为简单的算法,它不需要配置额外参数。假设配置文件中共有 台服务器,该算法遍历服务器节点列表,并按节点次序每轮选择一台服务器处理请求。当所有节点均被调用过一次后,该算法将从第一个节点开始重新一轮遍历。特点:由于该算法中每个请求按时间顺序逐一分配到不同的服务器处理,因此适用于服务器性能相近的集群情况,其中每个服务器承载相同的负载。但对于服务器性能不同的集群而言,该算法容易引发资源分配不合理等问题。
    • 加权轮询:为了避免普通轮询带来的弊端,加权轮询应运而生。在加权轮询中,每个服务器会有各自的 weight。一般情况下,weight 的值越大意味着该服务器的性能越好,可以承载更多的请求。该算法中,客户端的请求按权值比例分配,当一个请求到达时,优先为其分配权值最大的服务器。特点:加权轮询可以应用于服务器性能不等的集群中,使资源分配更加合理化。
    • IP 哈希(IP hash):ip_hash 依据发出请求的客户端 IP 的 hash 值来分配服务器,该算法可以保证同 IP 发出的请求映射到同一服务器,或者具有相同 hash 值的不同 IP 映射到同一服务器。特点:该算法在一定程度上解决了集群部署环境下 Session 不共享的问题。实际应用中,我们可以利用 ip_hash,将一部分 IP 下的请求转发到运行新版本服务的服务器,另一部分转发到旧版本服务器上,实现灰度发布。再者,如遇到文件过大导致请求超时的情况,也可以利用 ip_hash 进行文件的分片上传,它可以保证同客户端发出的文件切片转发到同一服务器,利于其接收切片以及后续的文件合并操作。
    • URL hash:url_hash 是根据请求的 URL 的 hash 值来分配服务器。该算法的特点是,相同 URL 的请求会分配给固定的服务器,当存在缓存的时候,效率一般较高。然而 Nginx 默认不支持这种负载均衡算法,需要依赖第三方库。
    • 最小连接数(Least Connections):假设共有 台服务器,当有新的请求出现时,遍历服务器节点列表并选取其中连接数最小的一台服务器来响应当前请求。连接数可以理解为当前处理的请求数。

2.gunicorn

  • Flask 作为一个 Web 框架,内置了一个 webserver, 但这自带的 Server 到底能不能用?显然是可以的,但不要这么做。
    • 『单 Worker』只有一个进程在跑所有的请求,而由于实现的简陋性,内置 webserver 很容易卡死。并且只有一个 Worker 在跑请求。在多核 CPU 下,仅仅占用一核。当然,其实也可以多起几个进程。
    • 『缺乏 Worker 的管理』接上,加入负载量上来了,Gunicorn 可以调节 Worker 的数量。而这个东西,内置的 Webserver 是不适合做这种事情的。
  • Gunicorn 作为 Server 相对而言可以有什么提升。
    • 帮我 scale worker, 进程挂了帮我重启用 python 的框架 flask/django/webpy 配置起来都差不多。
    • 信号机制。可以支持多种配置。
    • 管理 worker 上,使用了 pre-fork 模型,即一个 master 进程管理多个 worker 进程,所有请求和响应均由 Worker 处理。Master 进程是一个简单的 loop, 监听 worker 不同进程信号并且作出响应。比如接受到 TTIN 提升 worker 数量,TTOU 降低运行 Worker 数量。如果 worker 挂了,发出 CHLD, 则重启失败的 worker, 同步的 Worker 一次处理一个请求。

3.中间件总结

  • Flask 内置 WebServer + Flask App = 弱鸡版本的 Server, 单进程(单 worker) / 失败挂掉 / 不易 Scale
  • Gunicorn + Flask App = 多进程(多 worker) / 多线程 / 失败自动帮你重启 Worker / 可简单Scale
  • 多 Nginx + 多 Gunicorn + Flask App = 小型多实例 Web 应用,一般也会给 gunicorn 挂 supervisor

Redis相关

1.数据结构概述

  • 简单字符串SDS:结构体{free:int,len:int,buf:char[]},空间机制:空间预分配+惰性空间释放
  • 链表:双端,无环,头尾指针,长度计数器,多态
  • 字典:哈希算法(键值对的键计算出哈希值和索引值),拉链法,渐进式rehash
  • 跳跃表:表结构体{head,tail,level,length},节点结构体{level,backward,forward,obj},平均O(logn),最坏O(n)
  • 整数集合

2.跳表&有序集合

3.哈希集合

4.持久化

设计模式的应用

  • 如何将设计模式的基本原则贯彻到项目开发中
  • 策略模式、装饰器模式、观察者模式的具体使用
  • 如何利用设计模式将一个已有的框架进行优雅的扩展。

数据结构的优化

  • dict占用内存的情况是否会直接影响到运行速度(每一个网页的对象数据是通过dict存储,最终返回给总循环中的list)
    • hash函数是需要时间计算的
    • 直接插入list中?缺失键值的情况如何处理?->默认值?

如何提速

  • 目前检测到的情况为带宽和硬件资源都充足,也安排了多线程,速度依然不理想(10万个网页/30min),初步考虑仍然是io效率的问题
  • 考虑下一步加入multiprocessing?
  • 是不是考虑不使用BS4建立虚拟dom树,而是转变成自己使用正则表达式进行匹配

多线程的问题

  • 考虑把建立线程的操作建立为工厂模式+策略模式,目前是纯手工建立线程

目前解决的困难

  • 对页面内容换行符和特殊符号的处理:文本字符串replace,正则
  • 分机效率:多进程
  • 异步数据:直接构造ajax请求,得到json数据
  • 分布式任务同步和结果检测:Redis集合,哈希表

今后的拓展方向

  • 爬虫:
    • 配合Flask开发爬虫看板,部署到生产环境下
    • 重构,使其Python风格明显化
    • 查询优化
  • Flask:
    • 将登录模块增加上OAuth和邮箱验证,改进原有JWT登陆验证

一些经典问题

1.接口速度过慢,如何排查

1.是不是资源层面的瓶颈,硬件、配置环境之类的问题?
2.针对查询类接口,是不是没有添加缓存,如果加了,是不是热点数据导致负载不均衡?
3.是不是有依赖于第三方接口,导致因第三方请求拖慢了本地请求?
4.是不是接口涉及业务太多,导致程序执行跑很久?
5.是不是sql层面的问题导致的数据等待加长,进而拖慢接口?
6.网络层面的原因?带宽不足?DNS解析慢?
7.确实是代码质量差导致的,如出现内存泄漏,重复循环读取之类?

2.服务降级,服务熔断,服务限流

服务降级
  • 概念:服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
  • 服务接口拒绝服务:页面能访问,但是添加删除提示服务器繁忙。页面内容也可在Varnish或CDN内获取。
  • 页面拒绝服务:页面提示由于服务繁忙此服务暂停。跳转到varnish或nginx的一个静态页面。
    延迟持久化:页面访问照常,但是涉及记录变更,会提示稍晚能看到结果,将数据记录到异步队列或log,服务恢复后执行。
  • 随机拒绝服务:服务接口随机拒绝服务,让用户重试,目前较少有人采用。因为用户体验不佳。
服务熔断
  • 如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。
  • 熔断设计
    • 三个模块:熔断请求判断算法、熔断恢复机制、熔断报警
      (1)熔断请求判断机制算法:使用无锁循环队列计数,每个熔断器默认维护10个bucket,每1秒一个bucket,每个blucket记录请求的成功、失败、超时、拒绝的状态,默认错误超过50%且10秒内超过20个请求进行中断拦截。
      (2)熔断恢复:对于被熔断的请求,每隔5s允许部分请求通过,若请求都是健康的(RT<250ms)则对请求健康恢复。
      (3)熔断报警:对于熔断的请求打日志,异常请求超过某些设定则报警
服务限流

限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值