目录
python知识点
数据结构
Linux操作系统
计算机网络
mysql数据库
缓存redis:内存数据库
web知识点
python特点:
(1)python是动态强类型语言,强语言不会发生隐式的类型转换
(2)胶水语言,轮子多,应用广泛
(3)性能问题,代码维护问题,python2、3兼容问题
(4)python中一切皆对象
python2/3之间的差异:
(1)print改为函数,python2中是一个关键字
(2)编码,python3不再有unicode对象,默认str就是unicode
(3)除法,python3返回浮点数
(4)3中可以用super()直接调用父类函数
(5)高级解包,*args、**kwargs,分别返回tuple、dict
(6)新增,yield产生生成器,内置库asyncio、enum、mock、ipaddress、concurrent.futures等
python函数传递:
(1)传递可变类型参数
def do_ls(l):
l.append(0)
print(l)
l = []
# 执行结果
do_ls(l) # [0]
do_ls(l) # [0,0]
do_ls(l) # [0,0,0]
(2)传递不可变类型参数
def do_str(s):
s += "a"
print(s)
s = "hh"
# 执行结果
do_str(s) # hha
do_str(s) # hha
因为在python中,对一个变量赋值,只是将对象的引用赋给变量,比如:a=1,本质上是1这个对象的引用赋予了a。回到上面的解析,传递可变参数给函数的时候,本质上将可变参数的引用传入函数,所以在函数内使用的变量引用和外部的是一样的,又因为是参数是可变类型,所以在函数内部发生修改时,外部的变量也一样发生改变。不可变类型的参数,之所以没有变化,是因为引用指向了其他对象。
def do_str(s):
print("这是刚刚传入的s,id为:", id(s)) # 这是刚刚传入的s,id为: 140338339314032
s += "a"
print("这是发生改变后的s, id为:", id(s)) # 这是发生改变后的s, id为: 140338338859376
print(s)
s = "hh"
print("这是外部s, id为:", id(s)) # 这是外部s, id为: 140338339314032
# 执行结果
do_str(s) # hha
def do_ls(s):
print("这是刚刚传入的s,id为:", id(s)) # 这是刚刚传入的s,id为: 139688067340160
s += "a"
print("这是发生改变后的s, id为:", id(s)) # 这是发生改变后的s, id为: 139688067340160
print(s)
s = []
print("这是外部s, id为:", id(s)) # 这是外部s, id为: 139688067340160
# 执行结果
do_ls(s) # ["a"]
(3)注意点:函数中,默认参数只计算一次
def do_ls(l = [1]):
l.append(1)
print(l)
do_ls() # [1,1]
do_ls() # [1,1,1]
python中异常处理:
try:
# 无异常执行
except:
# 发生异常执行
else:
# 异常没有发生,即执行
finally:
# 无论是否发生异常都执行
注意:可以研究raise库,出现异常的时候,可以自带自定义的错误信息
python中的GIL锁:
(1)影响:
a、同一时间只能有一个线程执行字节码
b、CPU密集计算,难用到多核优势
c、IO期间会释放GIL,对IO密集程序影响不大
(2)避免GIL影响的方式:
a、CPU密集可以使用多线程+进程池
b、IO密集使用多线程/协程
c、cython扩展
(3)有GIL锁也要关注线程安全,因为非原子操作存在,在IO的时候,GIL锁会释放,其他线程开始执行,有可能会出现线程不安全
服务端性能优化措施:
(1)web应用一般语言不会成为瓶颈,数据结构与算法优化
(2)数据库:索引优化、慢查询消除、批量操作减少IO,NoSQL
(3)网络IO:批量操作、pipeline操作减少IO
(4)缓存:内存数据库redis、memcached
(5)异步:asyncio、celery
(6)并发:gevent、多线程
生成器与迭代器:
(1)生成器:
生成器本质上就是一个函数,它记住了上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。而且记录了程序执行的上下文。生成器不仅“记住”了它的数据状态,生成还记住了程序执行的位置。
(2)迭代器:
迭代器是一种支持next()操作的对象。它包含了一组元素,当执行next()操作时,返回其中一个元素。当所有元素都被返回后,再执行next()报异常—StopIteration生成器一定是可迭代的,也一定是迭代器对象
(3)区别:
a、生成器是生成元素的,迭代器是访问集合元素的一中方式
b、迭代输出生成器的内容
c、迭代器是一种支持next()操作的对象
d、迭代器(iterator):其中iterator对象表示的是一个数据流,可以把它看做一个有序序列,但我们不能提前知道序列的长度,只有通过nex()函数实现需要计算的下一个数据。可以看做生成器的一个子集。
python面向对象:
(1)创建一个对象的时候,会先调用__new__方法,再调用__init__进行初始化
(2)类变量:可以通过类或实例直接访问的变量,可以修改
class Person:
Country = 'China'
def __init__(self, name):
self.name = name
def print_name(self):
print(self.name)
wjf = Person("wjf")
wjf.print_name() # wjf
print(wjf.Country) # China
print(Person.Country) # China
Person.Country = "CHINA"
print(wjf.Country) # CHINA
print(Person.Country) # CHINA
(3)classmethod和staticmethod的区别
a、都可以class.method()的方式使用
b、classmethod第一个参数是cls,可以引用类变量
c、staticmethod用法和普通函数一样,只是放到类里面组织
class Person:
Country = 'China'
def __init__(self, name):
self.name = name
@classmethod
def print_country(cls):
print(cls.Country)
@staticmethod
def join_name(first_name, last_name):
print(first_name + last_name)
Person.print_country() # China
Person.join_name("vick", "·vi") # vick·vi
(4)__call__函数:将对象变成函数的使用方式,例如:可用于用户改变状态的场景
class Person:
def __call__(self,name):
print("my name is ", name)
wjf = Person()
wjf("wjf") # my name is wjf
python装饰器decorator:
(1)python中一切皆对象,函数也可以作为参数传递
(2)装饰器是接受函数作为参数,添加功能后返回一个新函数的函数(类)
(3)@语法糖的方式使用装饰器
(4)注意装饰器的调用顺序:装饰顺序,就近原则;执行顺序,就远原则;执行结束:就近原则。也可以理解为,就近入栈,执行时按出栈顺序入栈,结束时按顺序出栈,最终才结束函数调用。
def test1(func):
print("test1被调用")
def test1_1(*args, **kwargs):
print("test1_1被调用")
res = func(*args, **kwargs)
print("test1_1被调用-end")
return res
return test1_1
def test2(func):
print("test2被调用")
def test2_2(*args, **kwargs):
print("test2_2被调用")
res = func(*args, **kwargs)
print("test2_2被调用-end")
return res
return test2_2
@test1
@test2
def test():
print("test被调用")
return "结束"
print(test())
"""
test2被调用
test1被调用
test1_1被调用
test2_2被调用
test被调用
test2_2被调用-end
test1_1被调用-end
结束
"""
(5)带参数的装饰器
def test2(x):
print("test2被调用")
def test1(func):
print("test1被调用")
def test1_1(*args, **kwargs):
print(x+1)
res = func(*args, **kwargs)
print("test1_1被调用-end")
return res
return test1_1
return test1
@test2(1)
def test():
print("test被调用")
return "结束"
print(test())
"""
test2被调用
test1被调用
2
test被调用
test1_1被调用-end
结束
"""
(6)类装饰器
class test:
def __init__(self, use_int=False) -> None:
self.use_int = use_int
def __call__(self, func):
def _log(*args, **kwargs):
if self.use_int:
print("打开")
else:
print("不打开")
res = func(*args, **kwargs)
return res
return _log
@test(True)
def mydo():
print("mydo执行了")
return "结束"
print(mydo())
"""
打开
mydo执行了
结束
"""
python的设计模式:
(1)工厂模式:解决对象创建问题
(2)构造模式:控制复杂对象的创建(比如组装电脑,这个模式允许自定义电脑的配置),创建和表示分离
(3)原型模式:通过原型的克隆创建新的实例
(4)单例模式:一个类只能创建同一个对象(容易被问到,甚至实现,比如说python的模块)
class Singleton:
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
_instance = super().__new__(cls, *args, **kwargs)
cls._instance = _instance
return cls._instance
class MyClass(Singleton):
pass
c1 = MyClass()
c2 = MyClass()
print(c1 is c2) # true
# is 和 == 的区别:is判断引用是否一样;==判断值是否一样
(5)对象池模式:预先分配同一个类型的一组实例
(6)惰性计算模式:延迟计算(python的property)
多线程threading模块:
(1)threading.Thread类用来创建线程;
(2)start()方法启动线程;
(3)可以用join()等待线程结束。
python多进程:
(1)multiprocessing多进程模块;
(2)Multiprocessing.Process类实现多进程;
(3)一般在cpu密集程序里,可以使用,提高程序效率。
python的垃圾回收机制:
(1)引用计数为主(缺点:循环引用无法解决);
(2)引入标记清除和分代回收解决引用技术的问题。
常用内置库数据结构和算法:
(1) 线性结构:语言内置,list、tuple;内置库:array、collections.namedtuple
(2)链式结构:内置库,collections.deque(双端队列)
(3)字典结构:语言内置,dict;内置,collections.Counter(计数器)、collections.OrderedDict(有序字典)
(4)集合:语言内置,set、frozenset(不可变)
(5)排序算法:语言内置,sorted
(6)二分算法:内置库,bisect
(7)堆算法:heapq模块
collections模块用法:
(1)Counter:字典的子类,提供了可哈希对象的计数功能
(2)defaultdict:字典的子类,提供了一个工厂函数,为字典查询提供了默认值
(3)OrderedDict:字典的子类,保留了他们被添加的顺序
(4)namedtuple:创建命名元组子类的工厂函数
(5)deque:类似列表容器,实现了在两端快速添加(append)和弹出(pop)
dict底层使用的哈希表:
(1)为了支持快速查找,使用了哈希表作为底层
(2)哈希表平均查找时间复杂度O(1)
(3)使用二次探查法解决哈希冲突问题
其他部分有关算法会重新更新。
日常开发用到命令:
根据进程名查看进程号:ps -ef | grep 进程名
根据进程号查看占用的端口:netstat -apn | grep 进程号 -m 显示多少条
根据端口号查看使用的进程:lsof -i:端口号
实时抓取某个文件的内容:tail -f 文件名
Ubuntu的定时任务:crontab -l:显示定时任务有哪些;crontab -e进入编辑
window下需要用到命令:
查看所有进程占用的端口: netstat -ano
查看占用指定端口的进程信息:netstat -aon|findstr "8000"
根据进程号查看进程信息: tasklist|findstr "10076"
结束进程:taskkill /T /F /PID 10076
进程与线程的区别:
(1) 进程是对运行时程序的封装,是系统资源调度和分配的基本单位;
(2)线程是进程的子任务,cpu调度和分配的基本单位,实现进程内的并发;
(3)一个进程可以包含多个线程,线程依赖进程存在,并共享进程内存。
线程同步的方式:
(1)互斥量(锁):通过互斥机制防止多个线程同时访问公共资源;
(2)信号量:控制同一时刻多个线程访问同一个资源的线程数;
(3)事件(信号):通过通知的方式保持多个线程同步。
进程间通信的方式:
(1)管道/匿名管道/有名管道(pipe)
(2)信号:比如用户使用ctrl+c产生SIGINT程序终止信号;
(3)消息队列(Message);
(4)共享内存;
(5)信号量;
(6)套接字(socket):最常用的方式,我们的web应用都是这种方式。
分页机制:逻辑地址和物理地址分离的内存分配管理方案
(1)程序的逻辑地址划分为固定大小的页;
(2)物理地址划分为同样大小的帧;
(3)通过页表对应逻辑地址和物理地址;
分段机制:为了满足代码的一些逻辑需求
(1)数据共享,数据保护,动态链接等;
(2)通过段表实现逻辑地址和物理地址的映射;
(3)每个段内部是连续内存分配,段和段之间是离散分配的
分段和分页的区别:
(1) 页是出于内存利用率的角度提出的离散分配机制;
(2)段是出于用户角度,用于数据保护、数据隔离等用途的管理机制;
(3)页的大小是固定的,操作系统决定的;段大小不确定,用户程序决定。
虚拟内存:把一些暂时不用的内存信息放到硬盘上
(1)局部性原理,程序运行时只有部分必要的信息装入内存;
(2)内存中暂时不需要的内容放到硬盘上;
(3)系统好像提供了比实际内存大得多的容量,为虚拟内存。
内存抖动:
(1)频繁的页调度,进程不断产生缺页中段;
(2)置换一个页,又不断再次需要这个页;
(3)运行程序太多;页面替换策略不好;
(4)解决方案,终止进程或者增加物理内存。
死锁:一组进程中,每个进程都无限等待被该组进程中另一进程所占有的资源,因而永远无法得到的资源,这种现象称为进程死锁,这一组进程就称为死锁进程
(1)互斥使用(资源独占):一个资源每次只能给一个进程使用;
(2)占有且等待(请求和保持,部分分配):进程在申请新的资源的同时保持对原有资源的占有;
(3)不可抢占(不可剥夺):资源申请者不能强行的从资源占有者手中夺取资源,资源只能由占有者自愿释放;
(4)循环等待:存在一个进程等待队列 {P1 , P2 , … , Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路。
当死锁产生的时候一定会有这四个条件,有一个条件不成立都不会造成死锁。
浏览器输入一个url中间经历的过程:
(1)DNS查询(域名解析)
(2)TCP握手(3次握手)
(3)HTTP请求
(4)反向代理Nginx(负载均衡)
(5)uwsgi/gunicom
(6)web app响应
(7)TCP挥手(4次挥手)
TCP 和 UDP的区别:
(1)tcp,面向连接、可靠的、基于字节流
(2)无连接、不可靠、面向报文
5中IO模型:
(1)Blocking IO(阻塞io)
(2)Nonblocking IO(非阻塞io)
(3)IO multiplexing(io多路复用)
(4)Signal Driven IO(信号驱动io)
(5)Asynchronous IO(异步io)
io多路复用:
(1)为了实现高并发需要一种机制并发处理多个socket
(2)Linux常见的是select/poll/epoll
(3)可以使用单线程单进程处理多个socket
三握四挥:
(1)TCP/IP 协议是传输层的一个面向连接的安全可靠的一个传输协议,三次握手的机制是为了保证能建立一个安全可靠的连接,那么第一次握手是由客户端发起,客户端会向服务端发送一个报文,在报文里面:SYN标志位置为1,表示发起新的连接。当服务端收到这个报文之后就知道客户端要和我建立一个新的连接,于是服务端就向客户端发送一个确认消息包,在这个消息包里面:ack标志位置为1,表示确认客户端发起的第一次连接请求。以上两次握手之后,对于客户端而言:已经明确了我既能给服务端成功发消息,也能成功收到服务端的响应。但是对于服务端而言:两次握手是不够的,因为到目前为止,服务端只知道一件事,客户端发给我的消息我能收到,但是我响应给客户端的消息,客户端能不能收到我是不知道的。所以,还需要进行第三次握手,第三次握手就是当客户端收到服务端发送的确认响应报文之后,还要继续去给服务端进行回应,也是一个ack标志位置1的确认消息。通过以上三次连接,不管是客户端还是服务端,都知道我既能给对方发送消息,也能收到对方的响应。那么,这个连接就被安全的建了。
(2)四次握手机制也是由客户端去发起,客户端会发送一个报文,在报文里面FIN位标志位置一,当服务端收到这个报文之后,我就知道了客户端想要和我断开连接,但是此时服务端不一定能做好准备,因为当客户端发起断开连接的这个消息的时候,对于服务端而言,他和还有可能有未发送完的消息,他还要继续发送,所以呢,此时对于服务端而言,我只能进行一个消息确认,就是我先告诉服务端,我知道你要给我断开连接了,但是我这里边还可能没有做好准备,你需要等我一下,等会儿我会告诉你,于是呢,发完这个消息确认包之后,可能稍过片刻它就会继续发送一个断开连接的一个报文啊,也是一个FIN位置1的报文也是由服务端发给客户端的啊,这个报文表示服务端已经做好了断开连接的准备,那么当这个报文发给客户端的时候,客户端同样要给服务端继续发送一个消息确认的报文一共有四次,那么,通过这四次的相互沟通和连接,我就知道了,不管是服务端还是客户端都已经做好了断开连接的
https加密过程
①https加密过程,为了防止中间人攻击,所以先使用非对称加密,产生了会话密钥后,再使用会话密钥进行对称加密进行消息的传输。
②非对称加密,就是服务端发送公钥s给客户端后,客户端使用公钥s加密了数据后,再发送给服务端,服务端可以使用私钥进行解密。
事务是什么:
(1)事务是数据库并发控制的基本单位;
(2)事务1可以看作是一系列sql语句的集合;
(3)事务必须要么全部执行成功,要么全部执行失败(回滚);
事务的4个特性ACID:
(1)原子性:一个事务中所有操作全部完成或失败;
(2)一致性:事务开始和结束之后数据完整新没被破坏;
(3)隔离性:允许多个事务同时对数据库修改和读写;
(4)持久性:事务结束之后,修改是永远的不会丢失。
事务并发会出现4种问题:
(1)幻读:一个事务第二次查出现第一次没有的结果;
(2)非重复读:一个事务重复读两次得到不同结果(获得结果不是最新的);
(3)脏读:一个事务读取到另一个事务没有提交的修改;
(4)丢失修改:并发写入造成其中一些修改丢失。
事务的4种隔离级别:
(1)读未提交:别的事务读到未提交的数据;
(2)读已提交:只能读取已经提交的数据;
(3)可重复读:同一个事务先后查询结果一页(默认);
(4)串行读:事务完成串行化的执行,隔离级别最高,执行效率最低。
防止高并发下插入重复值:
(1)使用数据库的唯一索引;
(2)使用队列异步写入;
(3)使用redis实现分布式锁;
乐观锁和悲观锁:
(1)悲观锁,先获取锁再进行操作。一锁二查三更新;
(2)乐观锁,先修改,更新的时候发现数据变了就回滚。
InnoDB和MyISAM引擎的区别:
(1)MyISAM不支持事务,InnoDB支持事务;
(2)MyISAM不支持外键,InnoDB支持外键;
(3)MyISAM只支持表锁,InnoDB支持行锁和表锁;
B-Tree:
(1)多路平衡查找树(每个节点最多m(m>=2)个孩子,称为m阶或者度);
(2)叶节点具有相同的深度;
(3)节点中的数据key从左到右是递增的。
B+Tree:
(1)mysql实际使用的B+Tree作为索引的数据结构;
(2)只在叶子节点带有指向记录的指针(可以增加树的度);
(3)叶子节点通过指针相连(实现范围查询)。
mysql索引的类型:
(1)普通索引(create index);
(2)唯一索引,索引列的值必须是唯一的(create unique index);
(3)多列索引;
(4)主键索引(primary key),一个表只能有一个;
(5)全文索引(fulltext index),InnDB不支持。
什么时候创建索引:
(1)经常用作查询条件的字段(where条件);
(2)经常用作表连接的字段;
(3)经常出现在order by,group by 之后的字段。
哪些字段适合当索引:
(1)非空字段;
(2)区分度高,离散度大,作为索引的字段值尽量不要有大量相同值;
(3)索引的长度不要太长(比较耗费时间);
索引失效:口诀:模糊匹配、类型隐转、最左匹配
(1)以%开头的like语句,模糊搜索;
(2)出现隐式类型转换(在python这种动态语言查询中需要注意);
(3)没有满足最左匹配原则;
比如:a, b, c为联合索引
如果:
where a b c;
where a b;
where a;
where b c; 这个会导致索引失效
聚集索引和非聚集索引:
(1)聚集和非聚集:是B+Tree叶节点存的是指针还是数据记录;
(2)MyISAM索引和数据分离,使用的非聚集;
(3)InnoDB数据文件就是索引文件,主键索引就是聚集索引。
用该两表来探究mysql连接的区别
内连接:
(1)将左表和右表关联起来的数据连接后返回;
(2)类似求两个表的交集
(3)select * from A inner join B on A.id=B.id;
外连接:
(1)左连接返回左表中所有记录,即使右表中没有匹配的记录(left join);
(2)右连接返回右表中所有记录,即使左表中没有匹配的记录(right join)
mysql分区:
(1)概念:分区就是把一张表分成N多个区块。分区表是一个独立的逻辑表,但是底层由多个物理子表组成。当查询条件的数据分布在某一个分区的时候,查询引擎只会去某一个分区查询,而不是遍历整个表。如果需要删除某一个分区的数据,只需要删除对应的分区即可。
(2)分区表的类型:
①range分区:按照范围分区。比如按照时间范围分区。
②list分区:和range分区相似,主要区别在于list是枚举值列表的集合,range是连续的区间值的集合。对于list分区,分区字段必须是已知的,如果插入的字段不在分区时的枚举值中,将无法插入。
③hash分区:可以将数据均匀地分布到预先定义的分区中。
(3)分区可能出现的问题:
①打开和锁住所有底层表的成本可能很高。当查询访问分区表时,mysql需要打开并锁住所有的底层表,这个操作在分区过滤前发生,所以无法通过分区过滤来降低此开销,会影响到查询速度,可以通过批量操作来降低此类开销,比如批量插入、LOAD DATA INFILE和一次删除多条数据。
②维护分区的成本可能很高,例如重组分区,会先创建一个临时分区,然后将数据复制到其中,最后再删除原分区。
③所有分区必须使用相同的存储引擎。
redis的作用:
(1)缓解关系数据库(常见的是mysql)并发访问的压力:热点数据;
(2)减少响应时间:内存IO速度比磁盘快;
(3)提升吞吐量:redis等内存数据库单机就可以支撑很大的并发;
(4)redis和memcached主要区别,就是redis可以持久化。
redis的数据类型:
(1)String(字符串):用来实现简单的kv键值对存储,比如计数器;
(2)List(链表):实现双向链表,比如用户关注,粉丝列表;
(3)hash(哈希表):用来存储彼此相关的键值对;
(4)set(集合):存储不重复元素,比如用户的关注者;
(5)sorted set(有序集合):实时信息排行榜
什么是WSGI:
(1)解决python web server乱象;
(2)描述了web server(Gunicorn/uWSGI)如何与web框架(Flask/Django)交互,web框架如何处理请求;
web框架对比:
(1)Djingo:大而全,内置ORM、Admin等组件,第三方插件比较多;
(2)Flask:微框架,插件机制,比较灵活;
(3)Tornado:异步支持的微框架和异步网络库。
MVC是什么:
(1)Model:负责业务对象和数据库的交互(ORM)
(2)View:负责与用户的交互展示;
(3)Controller:接收请求参数调用模型和视图完成请求。
常见的web安全问题:
(1)sql注入;
(2)xss(跨站脚本攻击);
(3)csrf(跨站请求伪造)。
sql注入:
(1)通过构造特殊的输入参数传入web应用,导致后端执行了恶意sql;
(2)通常由于程序员未对输入进行过滤,直接动态拼接sql产生;
(3)可以使用开源工具sqlmap、sqlninja检测。
如何防范sql注入:永远不要相信用户的任何输入
(1)对输入参数做好检查(类型和范围);过滤和转义特殊字符;
(2)不要直接拼接使用sql,使用ORM可以大大降低sql注入风险;
(3)数据库层:做好权限管理配置;不要明文存敏感信息。
xss攻击:
(1)恶意用户将代码植入到提供给其他用户使用的页面中,未经转义的恶意代码输出到其他用户的浏览器被执行;
(2)用户浏览页面的时候嵌入页面的脚本(js)会被执行,攻击用户;
(3)主要分未为:反射型(非持久型)、存储型(持久型)。
前后端分离是什么:
后端只负责提供数据接口,不再渲染模板,前端获取数据并呈现
前后端分离的优点:
(1)前后端解耦,接口复用(前端和客户端共用接口),减少并发量;
(2)各司其职,前后端同步开发,提升工作效率,定义号接口规范;
(3)更有利于调试(mock),测试和运维部署。
通俗易懂的RESTful:
本质上设计出来是为了解决url膨胀的问题,因为曾经的url,一个url对应一个操作、状态等,后续会出现过多的url难以管理,所以出现了restful风格。通过在representation增加更多的描述,然后后端再根据描述返回对应的结果。所以restful风格没有解决前后端的工作量,只是解决了url膨胀的问题。
什么是RESTful:
(1)表现层状态转移,由于http协议的主要设计者提出;
(2)资源(resources),表现层(representation),状态转化(state transfer)
(3)是一种以资源为中心的web软件架构风格,可以用Ajax和RESTful web服务构建应用;
(4)Resources(资源):使用URI指向的一个实体;
(5)Representation(表现层):资源的表现形式,比如图片、html文本;
(6)State Transfer(状态转化):get、post、put、delete http动词来操作资源,实现资源状态的改变。
RESTful的准则:
(1)所有事物抽象为资源(resource),资源对应唯一的标识(identifier);
(2)资源通过接口进行操作实现状态转移,操作本身是无状态的;
(3)对资源的操作不会改变资源的标识。
RESTful API:
(1)通过http get、post、put、delete 获取、新建、更新、删除 资源;
(2)一般使用json格式返回数据;
(3)一般web框架都有相应的插件支持RESTfu API。
python 运行后无法停止,查端口的方式
linux
(1)ps -ef | grep python
(2)ps aux | grep python
window
(1)wmic process where name="python.exe" list full
session、cookie、token:
共同点:都是用于鉴权、记录用户信息、保持用户操作
区别:
(1)session:存于服务端,记录用户信息,客户端只存sessionId,缺点大量用户需要session的时候,增加服务器的存储成本
(2)cookie:存于客户端,记录用户信息,缺点较为不安全
(3)token:基于jwt,记录用户信息,客户端存token,服务端只存token的密钥
nginx分包方式(负载均衡):
操作数据库分页
host = $host;
$this->port = $port;
$this->user = $user;
$this->passwd = $passwd;
$this->db = $db;
$this->conn = $this->getConn();
}
private function getConn() {
$conn = new mysqli($this->host,$this->user, $this->passwd, $this->db, $this->port);
if ($conn->connect_error) {
throw new Exception($conn->connect_error);
}
return $conn;
}
function readRow($offset, $limit, $table, $where) {
$sql = "select * from $table ";
if (count($where) != 0) {
$sql .= "where ";
$lenNum = count($where);
foreach ($where as $k=>$value) {
$lenNum -= 1;
if ($lenNum < 1) {
$sql .= "$k = $value ";
} else {
$sql .= "$k = $value and ";
}
}
}
if ($limit != 0) {
$sql .= "limit $limit ";
}
if ($offset != 0) {
$sql .= "offset $offset";
}
echo $sql;
$res = $this->conn->query($sql);
if ($res) {
while ($arr = $res->fetch_assoc()) {
echo "
";
echo var_dump($arr);
}
} else {
echo "no row";
}
}
function __destruct() {
$this->conn->close();
}
}
try {
$conn = new MysqlConn("127.0.0.1", 3306, "root", "123456", "reward");
echo "连接成功";
$where = [
// "id"=>19
];
$table = "users";
$offset = 5;
$limit = 5;
$conn->readRow($offset, $limit, $table, $where);
} catch (Exception $e) {
echo $e->getMessage();
}
通用分页类:
maxNum = $num;
}
function getPageNum() {
$pageNum = $_GET["pagenum"];
return $pageNum;
}
function getData($pageNum) {
$preNum = $pageNum-1;
$preStr = "<<< pre";
$preStrGrep = "<<< pre";
$nextNum = $pageNum+1;
$nextStr = "next >>>";
$nextStrGrep = "next >>>";
// 小于等于0
if ($this->maxNum <= 0) {
echo $preStrGrep . " " . $nextStrGrep;
return;
}
$temA = "  %s  ";
$temB = "  %s  ";
// [1,10]
if ($this->maxNum <= 10) {
$endStr = "";
if ($pageNum == 1) {
$endStr .= $preStrGrep;
} else {
$endStr .= $preStr;
}
for ($i=1; $i<=$this->maxNum;$i++) {
if ($i == $pageNum) {
$tempStr = sprintf($temB, $i);
$endStr .= $tempStr;
} else {
$tempStr = sprintf($temA, $i, $i);
$endStr .= $tempStr;
}
}
if ($pageNum == $this->maxNum) {
$endStr .= $nextStrGrep;
} else {
$endStr .= $nextStr;
}
echo $endStr;
return;
}
// (10, +无穷)
if ($this->maxNum > 10) {
$endStr = "";
if ($pageNum == 1) {
$endStr .= $preStrGrep;
} else {
$endStr .= $preStr;
}
$startNum = 0; $endNum = 0;
// 找起始, 加前缀
if ($pageNum < 10) {
$startNum = 1; $endNum = 10;
} elseif ($pageNum >= 10 and $pageNum <= $this->maxNum-10) {
$startNum = $pageNum -5;
$endNum = $pageNum + 5;
$endStr .= sprintf($temA, 1, 1);
$endStr .= sprintf($temA, 2, 2);
$endStr .= " ... ";
} elseif ($pageNum > $this->maxNum-10) {
$startNum = $this->maxNum-10;
$endNum = $this->maxNum;
$endStr .= sprintf($temA, 1, 1);
$endStr .= sprintf($temA, 2, 2);
$endStr .= " ... ";
}
for ($i=$startNum; $i<=$endNum;$i++) {
if ($i == $pageNum) {
$tempStr = sprintf($temB, $i);
$endStr .= $tempStr;
} else {
$tempStr = sprintf($temA, $i, $i);
$endStr .= $tempStr;
}
}
// 加后缀
if ($pageNum < 10) {
$endStr .= " ... ";
$endStr .= sprintf($temA, $this->maxNum-1, $this->maxNum-1);
$endStr .= sprintf($temA, $this->maxNum, $this->maxNum);
} elseif ($pageNum >= 10 and $pageNum <= $this->maxNum-10) {
$endStr .= " ... ";
$endStr .= sprintf($temA, $this->maxNum-1, $this->maxNum-1);
$endStr .= sprintf($temA, $this->maxNum, $this->maxNum);
} elseif ($pageNum > $this->maxNum-10) {
// $endStr .= " ... ";
}
if ($pageNum == $this->maxNum) {
$endStr .= $nextStrGrep;
} else {
$endStr .= $nextStr;
}
echo $endStr;
return;
}
}
}
function getMaxPage() {
$maxPage = $_GET["maxpage"];
return $maxPage;
}
$maxPage = getMaxPage();
$obj = new FY($maxPage);
$pageNum = $obj->getPageNum();
$obj->getData($pageNum);
效果如上