python后端知识点的自我复习

目录

python知识点

数据结构

Linux操作系统

计算机网络

mysql数据库

缓存redis:内存数据库

web知识点


python知识点

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)使用二次探查法解决哈希冲突问题

其他部分有关算法会重新更新。


Linux操作系统

日常开发用到命令:

根据进程名查看进程号: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加密了数据后,再发送给服务端,服务端可以使用私钥进行解密。


mysql数据库

事务是什么:

(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:内存数据库

redis的作用:

(1)缓解关系数据库(常见的是mysql)并发访问的压力:热点数据;

(2)减少响应时间:内存IO速度比磁盘快;

(3)提升吞吐量:redis等内存数据库单机就可以支撑很大的并发;

(4)redis和memcached主要区别,就是redis可以持久化。

redis的数据类型:

(1)String(字符串):用来实现简单的kv键值对存储,比如计数器;

(2)List(链表):实现双向链表,比如用户关注,粉丝列表;

(3)hash(哈希表):用来存储彼此相关的键值对;

(4)set(集合):存储不重复元素,比如用户的关注者;

(5)sorted set(有序集合):实时信息排行榜


web知识点

什么是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分包方式(负载均衡): 

  1. 源地址哈希法:根据获取客户端的IP地址,通过哈希函数计算得到一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
  2. 轮询法:将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
  3. 随机法:通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。
  4. 加权轮询法:不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
  5. 加权随机法:与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。
  6. 最小连接数法:由于后端服务器的配置不尽相同,对于请求的处理有快有慢,最小连接数法根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。

php 

操作数据库分页

<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<?php

class MysqlConn {
    public $host, $port, $user, $passwd, $db;
    public $conn;
    
    function __construct($host, $port, $user, $passwd, $db){
        $this->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 "<br>";
                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();
}

 通用分页类:

<?php

Class FY {

    public $maxNum;

    function __construct($num) {
        $this->maxNum = $num;
    }

    function getPageNum() {
        $pageNum = $_GET["pagenum"];
        return $pageNum;
    }
    
    function getData($pageNum) {
        $preNum = $pageNum-1;
        $preStr = "<a href='http://localhost:8080/php_stu/fy/fengye.php?pagenum=$preNum&&maxpage=$this->maxNum'><<< pre</a>";
        $preStrGrep = "<a style='color:gray;'><<< pre</a>";

        $nextNum = $pageNum+1;
        $nextStr = "<a href='http://localhost:8080/php_stu/fy/fengye.php?pagenum=$nextNum&&maxpage=$this->maxNum'>next >>></a>";
        $nextStrGrep = "<a style='color:gray;'>next >>></a>";
        // 小于等于0
        if ($this->maxNum <= 0) {
            echo $preStrGrep . "&nbsp" . $nextStrGrep;
            return;
        }
        
        $temA = "<a href='http://localhost:8080/php_stu/fy/fengye.php?pagenum=%s&&maxpage=$this->maxNum'>&nbsp %s &nbsp</a>";
        $temB = "<b>&nbsp %s &nbsp</b>";
        // [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 .= "&nbsp...&nbsp";
            } elseif ($pageNum > $this->maxNum-10) {
                $startNum = $this->maxNum-10;
                $endNum = $this->maxNum;
                $endStr .= sprintf($temA, 1, 1);
                $endStr .= sprintf($temA, 2, 2);
                $endStr .= "&nbsp...&nbsp";
            }

            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 .= "&nbsp...&nbsp";
                $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 .= "&nbsp...&nbsp";
                $endStr .= sprintf($temA, $this->maxNum-1, $this->maxNum-1);
                $endStr .= sprintf($temA, $this->maxNum, $this->maxNum);
            } elseif ($pageNum > $this->maxNum-10) {
                // $endStr .= "&nbsp...&nbsp";
            }

            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);

 效果如上

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值