Python知识学习回顾

01.celery

必备:
    - celery模块
        - pip3 install celery 
    - 放置消息的队列:rabbmitMQ/redis

总结:
    1. celery是一个基于Python实现的用于完成任务处理的组件。
    2. celery依赖:
            - redis/rabbmitMQ
            - Python操作redis/rabbmitMQ的模块
    3. 至少有两个程序:
        - worker,处理任务
            - 命令:celery worker -A s1 -l info
            - 命令:celery worker -A celery_tasks -l info
            
        - 程序
            - 添加任务
                - 获取任务ID
            - 检查任务状态+ 获取任务结果
                - 根据任务ID
    4. 应用:
        - 上述示例可以应用到任务框架
        - 特殊的django
        
    5. @shared_task/@app1.task的区别?
        
            
    6. flask示例
        
公司开发环境:
    1. windows
        - 在windows上开发【坑】
        - 代码部署在linux :centos
    2. 双系统
        - windows
        - linux: ubuntu+桌面版
        - linux: centos+桌面版
        
        - 代码部署在linux :centos
    3. mac 
        - linux:mac 
        - 代码部署在linux :centos
    4. vim开发
        - 通过vim在:centos
        - 代码部署在linux :centos
celery

 

02.Linux

Linux - CentOS 7.4 
基础命令 
系统优化+定时任务
nginx
nginx+py搭建网站

Linux  
vmware fusion(mac) vmware workstation 12.0 (windows)
                                       8.0 
远程连接工具:xshell (windows)  mac(iterm2)
              android  juiceSSH
              IOS      termius 

Linux 
Fedora
Red Hat Enterprise Linux  RHEL  7.5
CentOS  红帽收费去掉,logo去掉

Debian 
Ubuntu 
SUSE  OpenSUSE


https://mirrors.aliyun.com/centos/


F1 
F2 
F10 
F12

intel  virtual  technology    disabled(关闭)   
                               enabled(开启)
intel vt


网卡名称 :  eth0 
             ensxxx


vmware相关服务 要开启
win+r  输入 services.msc

VMware Authorization Service   正在运行/已启动     自动 
 VMware DHCP Service           正在运行/已启动     自动 
 VMware NAT Service            正在运行/已启动     自动 


远程连接服务器 :  10.0.0.128 
Xshell       免费
SecureCRT    
putty

屌丝去洗浴中心之路

1.道路是否通畅  你到服务器之间
本地Shell-CMD-windows 

2.是否有人劫财劫色

3.是否提供特殊服务

1.道路是否通畅  你到服务器之间
本地Shell-CMD-windows 

[d:\~]$ ping 10.0.0.128 

正在 Ping 10.0.0.128 具有 32 字节的数据:
来自 10.0.0.128 的回复: 字节=32 时间<1ms TTL=64
来自 10.0.0.128 的回复: 字节=32 时间<1ms TTL=64
来自 10.0.0.128 的回复: 字节=32 时间<1ms TTL=64
来自 10.0.0.128 的回复: 字节=32 时间<1ms TTL=64

10.0.0.128 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 0ms,最长 = 0ms,平均 = 0ms

[d:\~]$ ping 10.0.0.130

正在 Ping 10.0.0.130 具有 32 字节的数据:
来自 10.0.0.1 的回复: 无法访问目标主机。
请求超时。
请求超时。
请求超时。

10.0.0.130 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 1,丢失 = 3 (75% 丢失),



2.是否有人劫财劫色
SeLinux 
防火墙

3.

服务名称         搓澡     按摩      特殊服务
暗号(端口号)     80       443       22 

远程连接服务  sshd   22 
telnet  10.0.0.128 22 
[d:\~]$ telnet  10.0.0.128 22 

Connecting to 10.0.0.128:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
SSH-2.0-OpenSSH_7.4

Protocol mismatch.

Connection closed by foreign host.

Disconnected from remote host(10.0.0.128:22) at 12:17:03.

Type `help' to learn how to use Xshell prompt.
[d:\~]$ telnet  10.0.0.128 25


Connecting to 10.0.0.128:25...
Could not connect to '10.0.0.128' (port 25): Connection failed.

Type `help' to learn how to use Xshell prompt.








道路不通排查过程
1.ip地址
2.vmware 编辑-虚拟网络编辑器
3.windows 服务 
vmware相关服务 要开启
win+r  输入 services.msc

VMware Authorization Service   正在运行/已启动     自动 
 VMware DHCP Service           正在运行/已启动     自动 
 VMware NAT Service            正在运行/已启动     自动 

4.我的电脑/此电脑/文件夹  输入  网络连接

通过安装包修复

CCleaner 把vmware相关信息 清理掉 重新安装

h
总结:
1.创建虚拟机-部署CentOS 7.4 
2.配置VMware虚拟网络编辑器
3.通过Xshell连接虚拟机 
4.无法连接服务器排查过程(屌丝去洗浴中心之路)
5.xshell优化 


下午:
操作与命令 


空格和tab键




[root@oldboyedu-s8 ~]# #mkdir  make directory 
[root@oldboyedu-s8 ~]# mkdir   /data
[root@oldboyedu-s8 ~]# #显示目录的内容
[root@oldboyedu-s8 ~]# #ls  list 
[root@oldboyedu-s8 ~]# ls /data/
[root@oldboyedu-s8 ~]# ls -l  /data/
total 0

相对路径与绝对路径:
绝对路径:从根开始的路径(位置) /data  etc/hosts
从根开始的路径就是绝对路径。




[root@oldboyedu-s8 data]# touch /data/oldboy.txt
[root@oldboyedu-s8 data]# ls -l /data/
total 0
-rw-r--r--. 1 root root 0 Apr 25 15:16 oldboy.txt


第1个里程碑-打开文件 
vi  /data/oldboy.txt

第2个里程碑-编辑文件
按i 进入到编辑模式

第3个里程碑-退出编辑模式
按esc退出编辑模式 

第4个里程碑-保存退出
:wq      write   quit 
#保存退出
:q!
#强制退出不保存 


第5个里程碑-显示文件内容
[root@oldboyedu-s8 data]# cat  /data/oldboy.txt 
I am studying linux.

vi/vim 快捷键 
复制                           yy 
粘贴                           p 
删除、剪切                     dd 
撤销                           u 
把光标所在行到文件最后一行删除 dG

移动光标
把光标移动到文件的最后一行     G 
把光标移动到文件的第一行       gg  



#I am studying linux.I am studying linux.I am studying linux.
#I am studying linux.I am studying linux.I am studying linux.
#I am studying linux.I am studying linux.I am studying linux.
#I am studying linux.I am studying linux.I am studying linux.

批量删除 

第1个里程碑-按ctrl + v 进入批量编辑模式 

第2个里程碑-选择 

第3个里程碑-删除 d  



[root@oldboyedu-s8 data]# #copy cp
[root@oldboyedu-s8 data]# 
[root@oldboyedu-s8 data]# cp   /data/oldboy.txt   /tmp/
[root@oldboyedu-s8 data]# ls -l /tmp/
total 8
-rwx------. 1 root root 836 Apr 25 11:03 ks-script-6cg4Xy
-rw-r--r--. 1 root root 183 Apr 25 15:56 oldboy.txt
drwx------. 3 root root  17 Apr 25 11:18 systemd-private-241350d318404b8eb4e0324ead618b12-chronyd.service-tG7NpS
drwx------. 3 root root  17 Apr 25 11:18 systemd-private-241350d318404b8eb4e0324ead618b12-vgauthd.service-bhBRbW
drwx------. 3 root root  17 Apr 25 11:18 systemd-private-241350d318404b8eb4e0324ead618b12-vmtoolsd.service-mHP4YZ
-rw-------. 1 root root   0 Apr 25 10:58 yum.log




2.5 把 /data 移动到 /root目录下面

move  mv 

[root@oldboyedu-s8 data]# mv /data/   /root/
[root@oldboyedu-s8 data]# ls -l /data
ls: cannot access /data: No such file or directory
[root@oldboyedu-s8 data]# ls -l /r
root/ run/  
[root@oldboyedu-s8 data]# ls -l /root/
total 4
-rw-------. 1 root root 1233 Apr 25 11:03 anaconda-ks.cfg
drwxr-xr-x. 2 root root   46 Apr 25 16:00 data

进入/root目录下的data目录,删除oldboy.txt文件
remove  
rm 

[root@oldboyedu-s8 tmp]# cd /root/
[root@oldboyedu-s8 ~]# ls -l
total 4
-rw-------. 1 root root 1233 Apr 25 11:03 anaconda-ks.cfg
drwxr-xr-x. 2 root root   28 Apr 25 16:32 data
[root@oldboyedu-s8 ~]# pwd
/root
[root@oldboyedu-s8 ~]# rm -f data 
rm: cannot remove ‘data’: Is a directory
[root@oldboyedu-s8 ~]# ls -l
total 4
-rw-------. 1 root root 1233 Apr 25 11:03 anaconda-ks.cfg
drwxr-xr-x. 2 root root   28 Apr 25 16:32 data
[root@oldboyedu-s8 ~]# rm -r data 
rm: descend into directory ‘data’? y
rm: remove regular file ‘data/oldboy.txt.bak’? n
rm: remove directory ‘data’? n
[root@oldboyedu-s8 ~]# rm -rf data 



[root@oldboyedu-s8 ~]# find /root/   -type f  -name "oldboy.txt"
/root/oldboy.txt
[root@oldboyedu-s8 ~]# 
[root@oldboyedu-s8 ~]# 
[root@oldboyedu-s8 ~]# 
[root@oldboyedu-s8 ~]# find /root/   -type f  -name "*.txt"
/root/oldboy.txt
#*.txt 以.txt 结尾的文件

#find /root/   -type f  -name "*.txt"
#     在哪里找 -什么类型 f(file)
#                        d(dir)
/etc 找出以eth0结尾的文件 


http://blog.51cto.com/lidao/1927347 

老男孩教育每日一题-2017年5月18日-说说|(管道)与|xargs(管道xargs)的区别



[root@oldboyedu-s8 ~]# find /root/ -type f  -name "*.txt" 
/root/oldboy.txt
/root/alex.txt
[root@oldboyedu-s8 ~]# find /root/ -type f  -name "*.txt" |xargs   ls -l
-rw-r--r--. 1 root root 0 Apr 25 17:01 /root/alex.txt
-rw-r--r--. 1 root root 0 Apr 25 17:01 /root/oldboy.txt

# .. 当前目录的上一级目录 
# .  当前目录
[root@oldboyedu-s8 sysconfig]# cd /etc/sysconfig/network-scripts/
[root@oldboyedu-s8 network-scripts]# cd ../../../../../../../../../

[root@oldboyedu-s8 ~]# cat /data/test.txt 
test
liyao
oldboy
[root@oldboyedu-s8 ~]# #在文件中过滤 
[root@oldboyedu-s8 ~]# #显示出文件中 你想要的或不想要的内容
[root@oldboyedu-s8 ~]# grep "oldboy"  /data/test.txt 
oldboy
[root@oldboyedu-s8 ~]# grep -v "oldboy"  /data/test.txt 
test
liyao
[root@oldboyedu-s8 ~]# head -2 /data/test.txt 
test
liyao


2.10 已知/tmp下已经存在test.txt文件,如何执行命令才能把/mnt/test.txt拷贝到/tmp下覆盖掉/tmp/test.txt,而让系统不提示是否覆盖(root权限下)。

\cp /tmp/test.txt /mnt/

#cp  cp -i 
#别名
[root@oldboyedu-s8 ~]# alias 
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
#\cp 临时取消cp命令的别名
#别名相当于给命令设置了 昵称 或 快捷键

2.11 只查看ett.txt文件(共100行)内第20到第30行的内容
     
     seq 40  >   /oldboy/ett.txt
     cat /oldboy/ett.txt 
     
#sed默认输出文件的每一行 
[root@oldboyedu-s8 oldboy]# sed -n  '20p'   ett.txt 
20
#-n 取消默认输出
[root@oldboyedu-s8 oldboy]# 
[root@oldboyedu-s8 oldboy]# sed -n  '20,30p'   ett.txt 
20
21
22
23
24
25
26
27
28
29
30



2.12 把/oldboy目录及其子目录下所有以扩展名 .sh结尾的文件中,文件包含./hostlists.txt(oldboy)的字符串全部替换为../idctest_iplist(oldgirl)
[root@oldboyedu-s8 oldboy]# find /oldboy/ -type f -name "*.sh"
/oldboy/test/del.sh
/oldboy/test.sh
/oldboy/t.sh
[root@oldboyedu-s8 oldboy]# #把一个文件的oldboy替换为 oldgirl 
[root@oldboyedu-s8 oldboy]# #替换 
[root@oldboyedu-s8 oldboy]# sed   's#oldboy#oldgirl#g' /oldboy/t.sh 
oldgirl
[root@oldboyedu-s8 oldboy]# cat /oldboy/t.sh 
[root@oldboyedu-s8 oldboy]# cat /oldboy/t.sh 
oldboy
[root@oldboyedu-s8 oldboy]# sed   's#oldboy#oldgirl#g' /oldboy/t.sh 
oldgirl


第1个里程碑-找出想要的文件 
find /oldboy/ -type f -name "*.sh"
第2个里程碑-替换1个文件的内容 
sed   's#oldboy#oldgirl#g' /oldboy/t.sh 
第3个里程碑-把find命令找出的文件交给sed
find /oldboy/ -type f -name "*.sh" |xargs sed     's#oldboy#oldgirl#g'


[root@oldboyedu-s8 oldboy]# find /oldboy/ -type f -name "*.sh"
/oldboy/test/del.sh
/oldboy/test.sh
/oldboy/t.sh
[root@oldboyedu-s8 oldboy]# sed   's#oldboy#oldgirl#g' /oldboy/t.sh 
oldgirl
[root@oldboyedu-s8 oldboy]# find /oldboy/ -type f -name "*.sh" |xargs sed   's#oldboy#oldgirl#g'
oldgirl
oldgirl
oldgirl
[root@oldboyedu-s8 oldboy]# cat /oldboy/t.sh 
oldboy
[root@oldboyedu-s8 oldboy]# find /oldboy/ -type f -name "*.sh" |xargs sed  -i  's#oldboy#oldgirl#g'
[root@oldboyedu-s8 oldboy]# find /oldboy/ -type f -name "*.sh" |xargs cat
oldgirl
oldgirl
oldgirl






[root@oldboyedu-s8 network-scripts]# cd /etc/sysconfig/network-scripts/
[root@oldboyedu-s8 network-scripts]# pwd
/etc/sysconfig/network-scripts
[root@oldboyedu-s8 network-scripts]# cd /tmp/
[root@oldboyedu-s8 tmp]# cd -
/etc/sysconfig/network-scripts
[root@oldboyedu-s8 network-scripts]# #cd - 快速进入你上一次的位置  从哪里来回哪里去 
[root@oldboyedu-s8 network-scripts]# cd -
/tmp



[root@oldboyedu-s8 tmp]# cat -n /root/oldboy.txt
     1    oldboy
     2    oldboy
     3    oldboy
     4    oldboy
     5    oldboy

vi/vim 
nu==== number 
:set nu        显示行号 
:set nonu  取消显示行号


windows  创建压缩包  
linux        打包压缩

创建一个压缩包 
tar  zcf  /tmp/etc.tar.gz  /etc 

z=== 通过gzip软件进行压缩
c=== create 创建
f=== file 指定压缩包


[root@oldboyedu-s8 tmp]# tar zcf /tmp/etc.tar.gz  /etc/
tar: Removing leading `/' from member names
[root@oldboyedu-s8 tmp]# ll /tmp/etc.tar.gz 
-rw-r--r--. 1 root root 9916889 Apr 25 19:20 /tmp/etc.tar.gz

查看压缩包内容
tar tf  /tmp/etc.tar.gz
t === list 显示压缩包内容 

解压
[root@oldboyedu-s8 tmp]# cd /tmp/
[root@oldboyedu-s8 tmp]# tar xf etc.tar.gz 

解压到指定位置 
[root@oldboyedu-s8 tmp]# tar xf etc.tar.gz  -C /mnt/
[root@oldboyedu-s8 tmp]# ls -l /mnt/
total 12
drwxr-xr-x. 80 root root 8192 Apr 25 16:30 etc
-rw-r--r--.  1 root root    0 Apr 25 18:06 test.txt

总结:
1.创建虚拟机-部署CentOS 7.4 
2.配置VMware虚拟网络编辑器
3.通过Xshell连接虚拟机 
4.无法连接服务器排查过程(屌丝去洗浴中心之路)
5.xshell优化 
6.必知必会命令 根据题目练习 
7.find命令 
8.打包压缩 
9.三剑客 grep sed 

预习:
0.ping baidu.com 
1.单引号 双引号 区别
2.linux启动过程 运行级别
3.如何关闭Selinux和防火墙
4.定时任务 同步系统时间
5.nginx
Linux-01
Linux - CentOS 7.4 
基础命令 
系统优化+定时任务
nginx


1.虚拟机可以联网 
ping baidu.com 


2.linux下面安装软件

1)通过yum安装软件 需要你联网 


2) 更改系统的yum源  阿里云 
#https://opsx.alibaba.com/mirror   来源 

mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

3)安装常用软件 

yum install -y  tree bash-completion  wget vim 





Linux无法上网排查过程 
1. 查看是否能上网 
[root@bigdata ~]# ping baidu.com
connect: 网络不可达

2. 验证是否DNS 域名解析 域名---->ip地址 
[root@bigdata ~]# ping 223.5.5.5
connect: 网络不可达

3. 网关-验证你的网络配置(网卡 虚拟机vmwarexxx)是否有问题
ip   r     
[root@bigdata ~]# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=128 time=0.206 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=128 time=0.170 ms
^C
--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.170/0.188/0.206/0.018 ms



使用CentOS7.4 光盘镜像

1.把光盘插入到系统

2.在linux使用光盘 
[root@oldboyedu-s8 ~]# mount  /dev/cdrom   /mnt/
mount: /dev/sr0 is write-protected, mounting read-only
[root@oldboyedu-s8 ~]# #mount 给/dev/cdrom 创建一个入口 /mnt 
[root@oldboyedu-s8 ~]# cd /mnt/
[root@oldboyedu-s8 mnt]# ls -l
total 664
-rw-rw-r--. 3 root root     14 Sep  5  2017 CentOS_BuildTag
drwxr-xr-x. 3 root root   2048 Sep  5  2017 EFI
-rw-rw-r--. 3 root root    227 Aug 30  2017 EULA
-rw-rw-r--. 3 root root  18009 Dec 10  2015 GPL
drwxr-xr-x. 3 root root   2048 Sep  5  2017 images
drwxr-xr-x. 2 root root   2048 Sep  5  2017 isolinux
drwxr-xr-x. 2 root root   2048 Sep  5  2017 LiveOS
drwxrwxr-x. 2 root root 641024 Sep  5  2017 Packages
drwxr-xr-x. 2 root root   4096 Sep  5  2017 repodata
-rw-rw-r--. 3 root root   1690 Dec 10  2015 RPM-GPG-KEY-CentOS-7
-rw-rw-r--. 3 root root   1690 Dec 10  2015 RPM-GPG-KEY-CentOS-Testing-7
-r--r--r--. 1 root root   2883 Sep  6  2017 TRANS.TBL

[root@oldboyedu-s8 mnt]# ls -l /mnt/Packages/
Display all 3895 possibilities? (y or n)
[root@oldboyedu-s8 mnt]# ls -l /mnt/Packages/bash-completion-2.1-6.el7.noarch.rpm 
-rw-rw-r--. 2 root root 87272 Jul  4  2014 /mnt/Packages/bash-completion-2.1-6.el7.noarch.rpm
[root@oldboyedu-s8 mnt]# rpm -ivh  /mnt/Packages/bash-completion-2.1-6.el7.noarch.rpm 
Preparing...                          ################################# [100%]
    package bash-completion-1:2.1-6.el7.noarch is already installed
[root@oldboyedu-s8 mnt]# ll /mnt/Packages/telnet-
telnet-0.17-64.el7.x86_64.rpm         telnet-server-0.17-64.el7.x86_64.rpm
[root@oldboyedu-s8 mnt]# ll /mnt/Packages/telnet-0.17-64.el7.x86_64.rpm 
-rw-rw-r--. 2 root root 65632 Aug 11  2017 /mnt/Packages/telnet-0.17-64.el7.x86_64.rpm


1.Linux下面安装软件: yum 通过光盘安装

yum 
yum install 

rpm -ivh 

编译安装
三部曲: 备菜切菜    做菜    上菜
nginx    ./configure make    make install 

2.如何使用光盘安装软件

3.Linux无法上网排查过程

系统优化+定时任务
1.更改系统的yum源
#阿里云 mirrors.aliyun.com 
https://opsx.alibaba.com/mirror

#清华 
https://mirrors.tuna.tsinghua.edu.cn/help/centos/

#网易 
http://mirrors.163.com/.help/centos.html

云服务 物理服务器

2.关闭SElinux 
rpm -qa 
 #-qa query all 
 rpm -qa 

1#永久 修改配置文件  重启服务器之后生效

#     enforcing     - 已开启 正在运行 
#     permissive     - selinux关闭 警告提示
#     disabled         - 彻底关闭
SELINUX=enforcing
        |
        ↓
SELINUX=disabled

cp  /etc/selinux/config  /etc/selinux/config.bak
#快捷键:esc + .(点)  使用上一个命令的最后一个东西(参数)

sed  's#SELINUX=enforcing#SELINUX=disabled#g'  /etc/selinux/config
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g'  /etc/selinux/config
[root@oldboyedu-s8 ~]# cat /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of three two values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected. 
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted 

2#临时  重启服务器之后失效
[root@oldboyedu-s8 ~]# getenforce 
Enforcing
[root@oldboyedu-s8 ~]# #显示当前selinux
[root@oldboyedu-s8 ~]# #显示当前selinux的运行状态
[root@oldboyedu-s8 ~]# setenforce 
usage:  setenforce [ Enforcing | Permissive | 1 | 0 ]
[root@oldboyedu-s8 ~]# setenforce 0
[root@oldboyedu-s8 ~]# getenforce 
Permissive

小结:
selinux关闭 
0.操作前备份操作后检查
1.修改配置文件
2.命令行

防火墙

iptables 
firewalld 

[root@oldboyedu-s8 ~]# rpm -qa bash-completion
bash-completion-2.1-6.el7.noarch
#查询防火墙状态 
systemctl status  firewalld.service 

#当前正在运行的防火墙  --- 临时 
systemctl stop firewalld.service

#让防火墙不会开机自启动 --- 永久
systemctl disable firewalld.service

#systemctl is-active firewalld.service 
#is-active  是否正在运行 是否健在 
#systemctl is-enabled firewalld.service 
#is-enabled   是否开机自启动 
[root@oldboyedu-s8 ~]# 
[root@oldboyedu-s8 ~]# systemctl is-active firewalld.service 
unknown
[root@oldboyedu-s8 ~]# systemctl is-active crond.service 
active
[root@oldboyedu-s8 ~]# systemctl is-enabled firewalld.service 
disabled


[root@oldboyedu-s8 ~]# systemctl start firewalld.service 
[root@oldboyedu-s8 ~]# systemctl is-active firewalld.service
active
[root@oldboyedu-s8 ~]# 
[root@oldboyedu-s8 ~]# systemctl enable firewalld.service 
Created symlink from /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service to /usr/lib/systemd/system/firewalld.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/firewalld.service to /usr/lib/systemd/system/firewalld.service.
[root@oldboyedu-s8 ~]# systemctl is-enabled firewalld.service
enabled

小结:
systemctl 管理服务
如何关闭防火墙
systemctl  stop     firewalld
systemctl  disable  firewalld

systemctl  status       firewalld
systemctl  is-active    firewalld
systemctl  is-enabled   firewalld

systemctl enable firewalld.service

crond 定时任务  
同步系统时间 √
备份


检查crond状态
[root@oldboyedu-s8 ~]# rpm -qa   cronie
cronie-1.4.11-17.el7.x86_64
[root@oldboyedu-s8 ~]# systemctl is-active crond.service 
active
[root@oldboyedu-s8 ~]# systemctl is-enabled crond.service 
enabled

#定时任务管理命令 
[root@oldboyedu-s8 ~]# crontab -l
no crontab for root

[root@oldboyedu-s8 ~]# crontab -e
#oldboyedu.com

#-e  edit 
#-l  list 

定时任务格式


每天的早上8:30到学校上车(go to school)
30 08 * * *   go to school 

每天的晚上12点整回家自己开车(go to bed)
00 00 * * *  go to bed


如何修改系统时间
[root@oldboyedu-s8 ~]# date 
Thu Apr 26 12:13:53 CST 2018
[root@oldboyedu-s8 ~]# date -s "20180101 01:01:01"
Mon Jan  1 01:01:01 CST 2018
[root@oldboyedu-s8 ~]# date 
Mon Jan  1 01:01:05 CST 2018

让系统自动同步时间 
yum install ntpdate -y
[root@oldboyedu-s8 ~]# ntpdate  ntp1.aliyun.com 
26 Apr 12:19:53 ntpdate[18819]: step time server 182.92.12.11 offset 9976502.795919 sec
[root@oldboyedu-s8 ~]# date
Thu Apr 26 12:20:24 CST 2018


ntpdate  ntp1.aliyun.com 
ntpdate  ntp2.aliyun.com 
ntpdate  ntp3.aliyun.com 
ntpdate  ntp4.aliyun.com 
ntpdate  ntp5.aliyun.com 
ntpdate  ntp6.aliyun.com 
ntpdate  ntp7.aliyun.com 


#显示命令的绝对路径
[root@oldboyedu-s8 ~]# which ntpdate 
/usr/sbin/ntpdate
[root@oldboyedu-s8 ~]# find / -type f -name "ntpdate"
/etc/sysconfig/ntpdate
/usr/sbin/ntpdate

#每2分钟同步一次系统时间

###1.命令行测试
[root@oldboyedu-s8 ~]# /usr/sbin/ntpdate ntp1.aliyun.com 
26 Apr 12:28:11 ntpdate[19018]: adjust time server 182.92.12.11 offset 0.000723 sec

###2.命令写入定时任务 
[root@oldboyedu-s8 ~]# crontab -l 
#sync time 
*/2 * * * *  /usr/sbin/ntpdate ntp1.aliyun.com

###3.进行检查与测试
[root@oldboyedu-s8 ~]# date -s "20190101"
Tue Jan  1 00:00:00 CST 2019
[root@oldboyedu-s8 ~]# date 
Tue Jan  1 00:00:01 CST 2019
[root@oldboyedu-s8 ~]# date
Thu Apr 26 12:31:25 CST 2018


总结:
1.安装常用软件
yum rpm 编译安装
2.linux优化 
1)yum源配置
增加常用yum源 : 
epel源
yum install  wget -y 
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum repolist
yum install sl cowsay -y


2)关闭selinux和防火墙
3.定时任务 执行命令

下午: 
nginx

作用:
1.web网站
2.nginx+其他fastcgi(php)/ uwsgi(python)

部署nginx
1#下载nginx

wget http://nginx.org/download/nginx-1.12.2.tar.gz

yum install pcre-devel  openssl-devel -y 

#编译安装三部曲 : ./configure   make     make install 

tar xf nginx-1.12.2.tar.gz 
cd nginx-1.12.2 

./configure --prefix=/application/nginx-1.12.2 --with-http_stub_status_module  --with-http_ssl_module
make
make install 


故障1: 
error: the HTTP rewrite module requires the PCRE library.
yum install pcre-devel  -y 


[root@oldboyedu-s8 nginx-1.12.2]# ll /application/nginx-1.12.2/
total 4
drwxr-xr-x. 2 root root 4096 Apr 26 15:55 conf       #configure nginx配置文件 
drwxr-xr-x. 2 root root   40 Apr 26 15:55 html       #站点目录 网站根目录
drwxr-xr-x. 2 root root    6 Apr 26 15:55 logs       #日志
drwxr-xr-x. 2 root root   19 Apr 26 15:55 sbin       #nginx管理命令 

#检查语法 
/application/nginx-1.12.2/sbin/nginx  -t

#启动nginx
/application/nginx-1.12.2/sbin/nginx  


/application/nginx-1.12.2/sbin/nginx  -s reload


#nginx配置说明

nginx.conf           #nginx配置文件   
nginx.conf.default   #

#对比两个文件区别
diff conf/nginx.conf  conf/nginx.conf.default

egrep -v "#|^$" /application/nginx-1.12.2/conf/nginx.conf.default >/application/nginx-1.12.2/conf/nginx.conf


 1    worker_processes  1;          
 2    events {
 3        worker_connections  1024;
 4    }
 5    http {
 6        include       mime.types;                #媒体类型
 7        default_type  application/octet-stream;
 8        sendfile        on;                      #开启高效的传输模式
 9        keepalive_timeout  65;                   #超时时间
10        server {                               #一个server相当于是一个网站 虚拟主机 
11            listen       80;                   #监听的端口
12            server_name  www.etiantian.org;            #网站名字 域名 
13            location / {        
14                root   html;                   #根 站点的根目录
15                index  index.html index.htm;   #首页文件 
16            }
21        }
22    }



总结:
1.nginx部署 配置
2.定时任务
3.系统优化 配置和增加yum源  关闭selinux和防火墙
4.安装软件方法 
5.linux下面如何使用光盘

再约:
1.redis
2.lnmp(python)
Linux-02

 

03.redis

操作系统本身的内存管理机制:
1、分配内存        slab
2、浪费            buddy system
3、回收               buddy system        LRU

=========================================
redis安装配置


下载:
wget http://download.redis.io/releases/redis-3.2.10.tar.gz
解压:
tar xzf redis-3.2.10.tar.gz
mv redis-3.2.10 redis
安装:
cd redis
make
启动:
src/redis-server &


--------
配置文件的使用
vi /etc/redis.conf
是否后台运行:
daemonize  yes
默认端口:
port 6379
日志文件位置
logfile /var/log/redis.log
RDB持久化数据文件:
dbfilename dump.rdb
持久化文件的位置:
dir /data/redis



mkdir -p /data/redis

/application/redis/src/redis-server /etc/redis.conf 
/application/redis/src/redis-cli  -p 1111



安全配置

Bind
    指定IP进行监听 bind 10.0.0.200 127.0.0.1
禁止protected-mode
    protected-mode yes/no (保护模式,是否只允许本地访问)
增加requirepass  {password}
    requirepass root
    
    
    
在redis-cli中使用
    auth {password} 进行认证
    
    
修改安全配置    
vi /etc/redis.conf
添加以下配置:
bind 10.0.0.200 127.0.0.1
requirepass 123

添加完成后重启redis        
[root@oldboyedu-s6 ~]# /application/redis/src/redis-cli  -p 1111
127.0.0.1:1111> shutdown 
/application/redis/src/redis-server /etc/redis.conf 

登录测试:
 /application/redis/src/redis-cli  -h 10.0.0.200 -p 1111

 /application/redis/src/redis-cli  -h 10.0.0.200 -a 123  -p 1111
或者:
 /application/redis/src/redis-cli  -h 10.0.0.200 -p 1111
 
 auth 123

 
在线修改配置 
获取当前配置
CONFIG GET *
变更运行配置
CONFIG SET requirepass 123456

/application/redis/src/redis-cli  -a 123456 -h 10.0.0.200  -p 1111


-------------------
redis持久化

RDB持久化  
基于时间点快照的方式,复用方式进行数据持久化
比较常用的方式,效率较高,安全性相对较低

在 /etc/redis.conf中添加以下内容
dbfilename dump.rdb
dir /data/redis
save 900 1
save 300 10
save 60 10000


rdb文件名
rdb的放置路径
900秒(15分钟)内有1个更改
300秒(5分钟)内有10个更改
60秒内有10000个更改

AOF
只追加的方式记录所有redis中执行过的修改类命令
效率相对较低,安全性较高

appendonly yes
appendfsync always或者everysec或者no

------------------------------

数据类型:
Key:value(string\hash\list\set\Sset)

-----全局类操作
KEYS *                         查看KEY支持通配符
DEL                            删除给定的一个或多个key
EXISTS                         检查是否存在
RENAME                         变更KEY名
TYPE                        返回键所存储值的类型
EXPIRE\ PEXPIRE             以秒\毫秒设定生存时间
TTL\ PTTL                     以秒\毫秒为单位返回生存时间
PERSIST                     取消生存实现设置

--------string---------
增
set mykey "test"               为键设置新值,并覆盖原有值
getset mycounter 0           设置值,取值同时进行
setex mykey 10 "hello"      设置指定 Key 的过期时间为10秒,在存活时间可以获取value
setnx mykey "hello"           若该键不存在,则为键设置新值
mset key3  "zyx"  key4 "xyz" 批量设置键

删
del mykey                      删除已有键

改
append mykey "hello"          若该键并不存在,返回当前 Value 的长度 该键已经存在,返回追加后 Value的长度
incr mykey                   值增加1,若该key不存在,创建key,初始值设为0,增加后结果为1
decrby  mykey  5               值减少5
setrange mykey 20 dd          把第21和22个字节,替换为dd, 超过value长度,自动补0

查  
exists mykey                 判断该键是否存在,存在返回 1,否则返回0
get mykey                    获取Key对应的value
strlen mykey                  获取指定 Key 的字符长度
ttl mykey                     查看一下指定 Key 的剩余存活时间(秒数)
getrange mykey 1 20          获取第2到第20个字节,若20超过value长度,则截取第2个和后面所有的
mget key3 key4               批量获取键

-----------------------------
应用场景
常规计数:微博数,粉丝数等
incr
incrby
decr
decrby
-----------------------------
HASH类型

增
hset myhash field1 "s"    
若字段field1不存在,创建该键及与其关联的Hashes, Hashes中,key为field1 ,并设value为s ,若存在会覆盖原value
hsetnx myhash field1 s    
若字段field1不存在,创建该键及与其关联的Hashes, Hashes中,key为field1 ,并设value为s, 若字段field1存在,则无效
hmset myhash field1 "hello" field2 "world       一次性设置多个字段
删
hdel myhash field1                       删除 myhash 键中字段名为 field1 的字段
del myhash                              删除键
改  
hincrby myhash field 1                  给field的值加1

查
hget myhash field1                       获取键值为 myhash,字段为 field1 的值
hlen myhash                               获取myhash键的字段数量
hexists myhash field1                     判断 myhash 键中是否存在字段名为 field1 的字段
hmget myhash field1 field2 field3          一次性获取多个字段
hgetall myhash                           返回 myhash 键的所有字段及其值
hkeys myhash                              获取myhash 键中所有字段的名字
hvals myhash                               获取 myhash 键中所有字段的值


应用场景:
存储部分变更的数据,如用户信息等。需要将MySQL表数据进行缓存时,可以使用此种数据类型。

--------------------------------

增 
lpush mykey a b              若key不存在,创建该键及与其关联的List,依次插入a ,b, 若List类型的key存在,则插入value中
lpushx mykey2 e              若key不存在,此命令无效, 若key存在,则插入value中
linsert mykey before a a1      在 a 的前面插入新元素 a1
linsert mykey after e e2       在e 的后面插入新元素 e2
rpush mykey a b             在链表尾部先插入b,在插入a
rpushx mykey e              若key存在,在尾部插入e, 若key不存在,则无效
rpoplpush mykey mykey2       将mykey的尾部元素弹出,再插入到mykey2 的头部(原子性的操作)
删
del mykey                      删除已有键 
lrem mykey 2 a               从头部开始找,按先后顺序,值为a的元素,删除数量为2个,若存在第3个,则不删除
ltrim mykey 0 2              从头开始,索引为0,1,2的3个元素,其余全部删除
改
lset mykey 1 e               从头开始, 将索引为1的元素值,设置为新值 e,若索引越界,则返回错误信息
rpoplpush mykey mykey          将 mykey 中的尾部元素移到其头部
查
lrange mykey 0 -1              取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素。
lrange mykey 0 2            从头开始,取索引为0,1,2的元素
lrange mykey 0 0            从头开始,取第一个元素,从第0个开始,到第0个结束
lpop mykey                  获取头部元素,并且弹出头部元素,出栈
lindex mykey 6              从头开始,获取索引为6的元素 若下标越界,则返回nil 


应用场景
消息队列系统
比如sina微博: 
在Redis中我们的最新微博ID使用了常驻缓存,这是一直更新的。
但是做了限制不能超过5000个ID,因此获取ID的函数会一直询问Redis。只有在start/count参数超出了这个范围的时候,才需要去访问数据库。
系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。
SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发,而主页或第一个评论页是不会麻烦到硬盘上的数据库了。

------------------------

集合

增
sadd myset a b c  
若key不存在,创建该键及与其关联的set,依次插入a ,b,若key存在,则插入value中,若a 在myset中已经存在,则插入了 d 和 e 两个新成员。
删
spop myset              尾部的b被移出,事实上b并不是之前插入的第一个或最后一个成员
srem myset a d f          若f不存在, 移出 a、d ,并返回2
改
smove myset myset2 a        将a从 myset 移到 myset2,
查
sismember myset a            判断 a 是否已经存在,返回值为 1 表示存在。
smembers myset            查看set中的内容
scard myset                获取Set 集合中元素的数量
srandmember myset          随机的返回某一成员
sdiff myset1 myset2 myset3      1和2得到一个结果,拿这个集合和3比较,获得每个独有的值
sdiffstore diffkey myset myset2 myset3      3个集和比较,获取独有的元素,并存入diffkey 关联的Set中
sinter myset myset2 myset3               获得3个集合中都有的元素
sinterstore interkey myset myset2 myset3  把交集存入interkey 关联的Set中
sunion myset myset2 myset3               获取3个集合中的成员的并集
sunionstore unionkey myset myset2 myset3  把并集存入unionkey 关联的Set中



应用场景:
案例: 
在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。
Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,
对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

---------------------------
有序集合

增
zadd myzset 2 "two" 3 "three"           添加两个分数分别是 2 和 3 的两个成员
删
zrem myzset one two                  删除多个成员变量,返回删除的数量
改
zincrby myzset 2 one                  将成员 one 的分数增加 2,并返回该成员更新后的分数
查 
zrange myzset 0 -1 WITHSCORES          返回所有成员和分数,不加WITHSCORES,只返回成员
zrank myzset one                   获取成员one在Sorted-Set中的位置索引值。0表示第一个位置
zcard myzset                        获取 myzset 键中成员的数量
zcount myzset 1 2                   获取分数满足表达式 1 <= score <= 2 的成员的数量
zscore myzset three                  获取成员 three 的分数
zrangebyscore myzset  1 2               获取分数满足表达式 1 < score <= 2 的成员
#-inf 表示第一个成员,+inf最后一个成员
#limit限制关键字
#2  3  是索引号
zrangebyscore myzset -inf +inf limit 2 3  返回索引是2和3的成员
zremrangebyscore myzset 1 2           删除分数 1<= score <= 2 的成员,并返回实际删除的数量
zremrangebyrank myzset 0 1              删除位置索引满足表达式 0 <= rank <= 1 的成员
zrevrange myzset 0 -1 WITHSCORES           按位置索引从高到低,获取所有成员和分数
#原始成员:位置索引从小到大
      one  0  
      two  1
#执行顺序:把索引反转
      位置索引:从大到小
      one 1
      two 0
#输出结果: two  
       one
zrevrange myzset 1 3                  获取位置索引,为1,2,3的成员
#相反的顺序:从高到低的顺序
zrevrangebyscore myzset 3 0              获取分数 3>=score>=0的成员并以相反的顺序输出
zrevrangebyscore myzset 4 0 limit 1 2      获取索引是1和2的成员,并反转位置索引



应用场景:
排行榜应用,取TOP N操作 
这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,
比如按顶的次数排序,这时候就需要我们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。

--------------------------------------

PUBLISH channel msg
    将信息 message 发送到指定的频道 channel 
SUBSCRIBE channel [channel ...]
    订阅频道,可以同时订阅多个频道
PSUBSCRIBE pattern [pattern ...]
    订阅一个或多个符合给定模式的频道,每个模式以 * 作为匹配符,比如 it* 匹配所    有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有    以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类
PUNSUBSCRIBE [pattern [pattern ...]]
    退订指定的规则, 如果没有参数则会退订所有规则
PUBSUB sub command [argument [argument ...]]
    查看订阅与发布系统状态
注意:使用发布订阅模式实现的消息队列,当有客户端订阅channel后只能收到后续发布到该频道的消息,之前发送的不会缓存,必须Provider和Consumer同时在线。



------------------

DISCARD 
取消事务,放弃执行事务块内的所有命令。
EXEC 
执行所有事务块内的命令。
MULTI 
标记一个事务块的开始。
UNWATCH 
取消 WATCH 命令对所有 key 的监视。
WATCH key [key ...] 
监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

---------------

具体的命令----  watch命令
例: 
redis 127.0.0.1:6379> watch ticket
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> decr ticket
QUEUED
redis 127.0.0.1:6379> decrby money 100
QUEUED
//现在已经对ticket进行了监控,另外一个窗口将ticket改动了
另一个窗口:> decr ticket    
redis 127.0.0.1:6379> exec
(nil)   // 返回nil,说明监视的ticket已经改动了,事务就取消了.队列就不执行了。
redis 127.0.0.1:6379>mget ticket money
"0"
"200"

watch key1 key2  ... keyN
作用:监听key1 key2..keyN有没有变化,如果任意一个有变, 则事务取消
unwatch 
作用: 取消所有watch监听

-----------
redis管理类操作

Info
Clinet list
Client kill ip:port
config get *
CONFIG RESETSTAT 重置统计
CONFIG GET/SET 动态修改
Dbsize
FLUSHALL 清空所有数据 
select 1
FLUSHDB 清空当前库
MONITOR 监控实时指令

SHUTDOWN 关闭服务器
save将当前数据保存
SLAVEOF host port 主从配置
SLAVEOF NO ONE
SYNC 主从同步
ROLE返回主从角色
------------------
redis主从复制:基于RDB的快照技术,但是不依赖于RDB持久化。
环境:
准备两个或两个以上redis实例

    mkdir -p /data/6380/
    mkdir -p /data/6381/
    mkdir -p /data/6382/
-------------
/data/6380/redis.conf
/data/6381/redis.conf
/data/6382/redis.conf

三套配置文件示例:
vim /data/6380/redis.conf
----------------
bind 127.0.0.1 10.0.0.200
port 6380
daemonize yes
pidfile /data/6380/redis.pid
loglevel notice
logfile "/data/6380/redis.log"
dbfilename dump.rdb
dir /data/6380
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
slowlog-log-slower-than 10000
slowlog-max-len 128
protected-mode no


vim /data/6381/redis.conf
----------------
bind 127.0.0.1 10.0.0.200
port 6381
daemonize yes
pidfile /data/6381/redis.pid
loglevel notice
logfile "/data/6381/redis.log"
dbfilename dump.rdb
dir /data/6381
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
slowlog-log-slower-than 10000
slowlog-max-len 128
protected-mode no



vim /data/6382/redis.conf
----------------
bind 127.0.0.1 10.0.0.200
port 6382
daemonize yes
pidfile /data/6382/redis.pid
loglevel notice
logfile "/data/6382/redis.log"
dbfilename dump.rdb
dir /data/6382
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
slowlog-log-slower-than 10000
slowlog-max-len 128
protected-mode no


启动:
/application/redis/src/redis-server   /data/6380/redis.conf
/application/redis/src/redis-server  /data/6381/redis.conf
/application/redis/src/redis-server /data/6382/redis.conf


查看启动状态
netstat -lnp|grep 638



主节点:6380
从节点:6381、6382

开启主从:

6381/6382命令行:

redis-cli -p 6381
SLAVEOF 127.0.0.1 6380


redis-cli -p 6382
SLAVEOF 127.0.0.1 6380


-------------------

mkdir /data/26380
cp /application/redis/src/redis-sentinel /data/26380
cd /data/26380
 ./redis-sentinel ./sentinel.conf
vim sentinel.conf

port 26380
dir "/tmp"
sentinel monitor mymaster 127.0.0.1 6380 1
sentinel down-after-milliseconds mymaster 5000
sentinel config-epoch mymaster 0

启动
 ./redis-sentinel ./sentinel.conf

Python连接redis-sentinel集群

1、安装python3.5
 tar xf Python-3.5.2.tar.xz 

cd Python-3.5.2

./configure

make && make install

2、安装redis的python驱动
unzip redis-py-master.zip
cd redis-py-master
python3 setup.py install


3、python3连接redis  sentinel集群

python3
>>>from redis.sentinel import Sentinel  
>>>sentinel = Sentinel([('localhost', 26380)], socket_timeout=0.1)  
>>>sentinel.discover_master('mymaster')  
>>>sentinel.discover_slaves('mymaster')  
>>>master = sentinel.master_for('mymaster', socket_timeout=0.1)  
>>>slave = sentinel.slave_for('mymaster', socket_timeout=0.1)  
>>>master.set('foo', 'bar')  
>>>slave.get('foo')  

-----------------

redis cluser

安装集群软件


EPEL源安装ruby支持
yum install ruby rubygems -y
使用国内源
gem sources -a http://mirrors.aliyun.com/rubygems/ 
gem sources  --remove http://rubygems.org/
gem sources -l
gem install redis -v 3.3.3


--------------
集群节点准备

规划:
端口:7000-7005
路径:

创建节点目录:
mkdir -p /data/7000
mkdir -p /data/7001
mkdir -p /data/7002
mkdir -p /data/7003
mkdir -p /data/7004
mkdir -p /data/7005


配置文件添加:
-------------------------
vim /data/7000/redis.conf

port 7000
daemonize yes
pidfile /data/7000/redis.pid
logfile "/var/log/redis7000.log"
dbfilename dump.rdb
dir /data/7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes


vim /data/7001/redis.conf

port 7001
daemonize yes
pidfile /data/7001/redis.pid
logfile "/var/log/redis7001.log"
dbfilename dump.rdb
dir /data/7001
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

vim /data/7002/redis.conf

port 7002
daemonize yes
pidfile /data/7002/redis.pid
logfile "/var/log/redis7002.log"
dbfilename dump.rdb
dir /data/7002
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes



vim /data/7003/redis.conf

port 7003
daemonize yes
pidfile /data/7003/redis.pid
logfile "/var/log/redis7003.log"
dbfilename dump.rdb
dir /data/7003
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes


vim /data/7004/redis.conf

port 7004
daemonize yes
pidfile /data/7004/redis.pid
logfile "/var/log/redis7004.log"
dbfilename dump.rdb
dir /data/7004
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes



vim /data/7005/redis.conf

port 7005
daemonize yes
pidfile /data/7005/redis.pid
logfile "/var/log/redis7005.log"
dbfilename dump.rdb
dir /data/7005
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes


启动所有节点
/application/redis/src/redis-server /data/7000/redis.conf
/application/redis/src/redis-server /data/7001/redis.conf
/application/redis/src/redis-server /data/7002/redis.conf
/application/redis/src/redis-server /data/7003/redis.conf
/application/redis/src/redis-server /data/7004/redis.conf
/application/redis/src/redis-server /data/7005/redis.conf


ps -ef |grep 700
-------------------------
3、集群创建

/application/redis/src/redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

注意要输入yes

---------------------
4、集群状态查看

/application/redis/src/redis-cli -p 7000 cluster nodes | grep master
/application/redis/src/redis-cli -p 7000 cluster nodes | grep slave


+++++++++++++++Python 连接 redis cluster+++++++++++++++++

(1) redis-py并没有提供redis-cluster的支持,去github找了一下,有个叫redis-py-cluster的源码,
但是和redis-py不是一个作者,地址为:https://github.com/Grokzen/redis-py-cluster
watch,star,fork还算可以。
(2) 安装

unzip redis-py-cluster-unstable.zip
cd redis-py-cluster-unstable
python3 setup.py install

(3) 使用
>>>from rediscluster import StrictRedisCluster  
>>>startup_nodes = [{"host": "127.0.0.1", "port": "7000"}]  
>>># Note: decode_responses must be set to True when used with python3  
>>>rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)
  
>>>rc.set("foo", "bar")  
>>>print(rc.get("foo"))  
redis

 

day145

s8day145
    - 内容回顾:
        1. 为什么要使用redis?
        
        2. redis和memcached的区别?
            - 数据类型
            - 持久化 
            - 高可用
            - 分布式
            
            
           快慢:https://www.oschina.net/news/26691/memcached-timeout
        
        3. redis数据类型  
            
            
        4. 使用连接池
        
        5. 支持事务
        
        6. watch 
        
        7. 主从复制
            
        8. sentinel
        
        9. 将集群按照分布式(分片)的方式安排:
            - cluster
            - codis
            - twemproxy
            - 程序
                - 一致性哈希 hash_ring
                
        10. 分布式锁redlock
                
        
        11. Tornado
        
今日内容:
    - 项目部署,代码上线 https://www.cnblogs.com/wupeiqi/articles/8591782.html
    
    
内容详细:
    - 上线
        1. 本地代码配置相关操作
        2. 打包上传
            测试版:
                windows: 
                    yum install lrzsz
                    压缩zip包,拖进来。
                mac:
                    scp /home/xx/s8day145.zip  root@192.11.11.11:/data/
        
        3. 安装Python3
            a. 下载Python:https://www.python.org/ftp/python/3.5.4/
            b. 解压 tar -xvf Python-3.5.4.tgz
            c. cd Python-3.5.4
            d. 先装依赖:
                - yum install openssl-devel
                - yum install sqlite-devel
            e. 编译安装
                - ./configure
                - make 
                - make install 
                
                /usr/local/bin python3 
            f. 安装django 
            
        4. 第一版本上线:简单粗暴
        
            python3 manage.py runserver 0.0.0.0:8000
        
        5. 第二版本上线:uwsgi
            
            - 使用uwsgi启动一个脚本:
                app.py 
                    def application(env, start_response):
                        start_response('200 OK', [('Content-Type','text/html')])
                        return [b"Hello World"]
            
                uwsgi --http :9001 --wsgi-file app.py
                
            - 如果是flask程序:
                app.py 
                    app = Flask(....)
                    
                    @app.
                    ...
                    
                uwsgi --http :9002 --wsgi-file app.py  --callable app
            
            - 如果是django程序
                方式一:
                    uwsgi --http :9003 --chdir /data/s8day145/ --wsgi-file s8day145/wsgi.py
                方式二:
                    s8day145_uwsgi.ini
                        [uwsgi]
                        http = 0.0.0.0:9005
                        chdir = /data/oldboy/
                        wsgi-file = oldboy/wsgi.py
                        processes = 4
                        static-map = /static=/data/oldboy/allstatic
                    
                    uwsgi --ini s8day145_uwsgi.ini
                
                收集静态文件:
                    - settings.py 
                        DEBUG = False
                        STATIC_ROOT = "allstatic"
                    - python3 manage.py collectstatic 
                
        6. 第三版本上线:uwsgi + nginx 
            
            nginx作用:
                - 处理静态文件
                - 反向代理
                - 负载均衡(LVS、haproxy)
            uwsig作用:
                - 处理动态请求
                
            
            nginx配置:/etc/nginx/nginx.conf 
                
            
                user root;
                worker_processes 4;

                error_log /var/log/nginx/error.log;
                pid /var/run/nginx.pid;

                events {
                    worker_connections  1024;
                }


                http {
                    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                                      '$status $body_bytes_sent "$http_referer" '
                                      '"$http_user_agent" "$http_x_forwarded_for"';

                    access_log  /var/log/nginx/access.log  main;

                    sendfile            on;
                    tcp_nopush          on;
                    tcp_nodelay         on;
                    keepalive_timeout   65;

                    include             /etc/nginx/mime.types;
                    default_type        application/octet-stream;

                    upstream django {
                        server 127.0.0.1:8001; 
                        server 127.0.0.1:8002; 
                    }
                    server {
                        listen      80;

                        charset     utf-8;

                        # max upload size
                        client_max_body_size 75M;

                        location /static {
                            alias  /data/s8day145/allstatic; 
                        }

                        location / {
                            uwsgi_pass  django;
                            include     uwsgi_params;
                        }
                    }
                }

        
            uwsgi配置:/data/s8day145/
                s8day145_uwsgi_8001.ini 
                    [uwsgi]
                    socket = 127.0.0.1:8001
                    chdir = /data/s8day145/
                    wsgi-file = s8day145/wsgi.py
                    processes = 1
                    
                    
                s8day145_uwsgi_8002.ini 
                    [uwsgi]
                    socket = 127.0.0.1:8002
                    chdir = /data/s8day145/
                    wsgi-file = s8day145/wsgi.py
                    processes = 1
                
            
            启动:
                nginx:
                    /etc/init.d/nginx start 
                    /bin/systemctl restart nginx.service
                    /bin/systemctl start nginx.service
                    /bin/systemctl stop nginx.service
                
                uwsgi:
                    uwsgi --ini s8day145_uwsgi_8001.ini &
                    uwsgi --ini s8day145_uwsgi_8002.ini & 
                    
        7. 如果进程挂了,supervisor帮助你自动将服务重新启动。
            
            /etc/supervisord.conf
                [program:s8day145]
                command=/usr/local/bin/uwsgi --ini /data/s8day145/s8day145_uwsgi_8001.ini ;命令
                priority=999                ; 优先级(越小越优先)
                autostart=true              ; supervisord启动时,该程序也启动
                autorestart=true            ; 异常退出时,自动启动
                startsecs=10                ; 启动后持续10s后未发生异常,才表示启动成功
                startretries=3              ; 异常后,自动重启次数
                exitcodes=0,2               ; exit异常抛出的是0、2时才认为是异常
                stopsignal=QUIT             ; 杀进程的信号
                stopwaitsecs=10             ; 向进程发出stopsignal后等待OS向supervisord返回SIGCHILD 的时间。若超时则supervisord将
                使用SIGKILL杀进程
                user=root                 ; 设置启动该程序的用户
                log_stdout=true             ; 如果为True,则记录程序日志
                log_stderr=false            ; 如果为True,则记录程序错误日志
                logfile=/var/log/cat.log    ; 程序日志路径
                logfile_maxbytes=1MB        ; 日志文件最大大小
                logfile_backups=10          ; 日志文件最大数量

            systemctl start supervisord.service
            
    - 公司中的部署步骤及负责人:
        - 开发提交代码
        - 测试人员开始测试
        - 运维人员进行上线
            使用软件:jenkins+自己写脚本,运维人员点点点。
            
                - 从git上啦代码到一个服务器,在该服务器上对代码进行编译(c/vue/java)
                - 通过以下工具 将代码同步到每台服务器上
                    - saltstack
                    - ansible
                - 执行命令启动程序
    
    - 自己搭建yum源
day145

 

day144

s8day144 

内容回顾:
    1. Flask和其他框架比较?
         
         

今日内容:
    1. redis
    2. 异步非阻塞
    3. 代码部署
    
内容详细:
    1. redis 
        你了解的redis?
    
        你用redis做过什么?
            - 配合django做缓存,常用且不易修改的数据放进来(博客)
            - 购物车信息
            - Session 
                - 缓存配置文件
                - session配置文件中指定使用缓存
            - rest api中访问频率控制
            - 基于flask、websocket实现的投票系统(redis做消息队列)
            - scrapy中
                - 去重规则
                - 调度器:先进先出、后进先出、优先级队列
                - pipelines 
                - 起始URL
            - 商品热点信息
            - 计数器
            - 排行
        
        为什么redis要做主从复制?
            目的是对redis做高可用,为每一个redis实例创建一个备份称为slave,让主和备之间进行数据同步,save/bsave。
                主:写
                从:读
            优点:
                - 性能提高,从分担读的压力。
                - 高可用,一旦主redis挂了,从可以直接代替。
            
            存在问题:当主挂了之后,需要人为手工将从变成主。
            
        redis的sentinel是什么?
            帮助我们自动在主从之间进行切换
            检测主从中 主是否挂掉,且超过一半的sentinel检测到挂了之后才进行进行切换。
            如果主修复好了,再次启动时候,会变成从。
            
        redis的cluster是什么?
            集群方案:
                - redis cluster 官方提供的集群方案。
                - codis,豌豆荚技术团队。
                - tweproxy,Twiter技术团队。
            redis cluster的原理?
                - 基于分片来完成。
                - redis将所有能放置数据的地方创建了 16384 个哈希槽。
                - 如果设置集群的话,就可以为每个实例分配哈希槽:
                    - 192.168.1.20【0-5000- 192.168.1.21【5001-10000- 192.168.1.22【10001-16384- 以后想要在redis中写值时,
                    set k1 123 
                  将k1通过crc16的算法,将k1转换成一个数字。然后再将该数字和16384求余,如果得到的余数 3000,那么就将该值写入到 192.168.1.20 实例中。
                    
        redis是否可以做持久化?
            RDB:每隔一段时间对redis进行一次持久化。
                 - 缺点:数据不完整
                 - 优点:速度快
            AOF:把所有命令保存起来,如果想到重新生成到redis,那么就要把命令重新执行一次。
                 - 缺点:速度慢,文件比较大
                 - 优点:数据完整
                
        redis的过期策略。
            voltile-lru:    从已设置过期时间的数据集(server.db[i].expires)中挑选最近频率最少数据淘汰
            volatile-ttl:   从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
            volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

            
            allkeys-lru:       从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
            allkeys-random:    从数据集(server.db[i].dict)中任意选择数据淘汰
            no-enviction(驱逐):禁止驱逐数据
                        
        redis的分布式锁实现。
            - 写值并设置超时时间
            - 超过一半的redis实例设置成功,就表示加锁完成。
            - 使用:安装redlock-py 
                from redlock import Redlock

                dlm = Redlock(
                    [
                        {"host": "localhost", "port": 6379, "db": 0},
                        {"host": "localhost", "port": 6379, "db": 0},
                        {"host": "localhost", "port": 6379, "db": 0},
                    ]
                )

                # 加锁,acquire
                my_lock = dlm.lock("my_resource_name",10000)
                if  my_lock:
                    # J进行操作
                    # 解锁,release
                    dlm.unlock(my_lock)
                else:
                    print('获取锁失败')
            
        http://www.redis.cn/
        
    2. 异步非阻塞框架:Tornado
        目标:通过一个线程处理N个并发请求。
                - 处理请求IO  :牛逼起来
                - 处理非请求IO:傻逼起来
        
        使用支持tornado异步非阻塞的模块:
            MySQL
            Redis
            SQLALchemy
        
        Tornado异步非阻塞本质:
            视图函数yield一个futrue对象,futrue对象默认:
                self._done = False   ,请求未完成
                self._result = None  ,请求完成后返回值,用于传递给回调函数使用。
            
            tornado就会一直去检测futrue对象的_done是否已经变成True。
            
            如果IO请求执行完毕,自动会调用future的set_result方法:
                        self._result = result
                        self._done = True
        
        参考:http://www.cnblogs.com/wupeiqi/p/6536518.html
day144-redis

 

day143

s8day143

内容回顾:
    面试题 
        1. 谈谈你对restful 规范的理解?
            - restful其实就是一套编写接口的协议,协议规定如何编写以及如何设置返回值、状态码等信息。
            - 最显著的特点:
                restful: 给用户一个url,根据method不同在后端做不同的处理,比如:post 创建数据、get获取数据、put和patch修改数据、delete删除数据。
                no rest: 给调用者很多url,每个url代表一个功能,比如:add_user/delte_user/edit_user/
            - 当然,还有协议其他的,比如:
                - 版本,来控制让程序有多个版本共存的情况,版本可以放在 url、请求头(accept/自定义)、GET参数
                - 状态码,200/300/400/500
                - url中尽量使用名词,restful也可以称为“面向资源编程”
                - api标示:
                    api.luffycity.com
                    www.luffycity.com/api/
            ...
            .....
        2. 你的restful是怎么学的?
            - 因为之前公司要写这样项目
                - 接口
                - 公司要做前后端分离的项目
                - 公司要做微信小程序的开发
            - 所以就开始学习restful规范,看的技术文章 阮一峰的博客学到的规范。
            
        3. 状态码都有哪些?
        
        4. method都有哪些?
        
        5. 常见请求头有哪些?
        
        6. 你是用什么开发的restful接口?
            使用django rest framework框架。
            
        7. 为什么要使用django rest framework框架?
            在编写接口时可以不适用django rest framework框架,
            
            如果不使用:也可以做,那么就可以django的CBV来实现,开发者编写的代码会更多一些。
            如果  使用:内部帮助我们提供了很多方便的组件,我们通过配置就可以完成相应操作,如:
                        - 序列化,可以做用户请求数据校验+queryset对象的序列化称为json
                        - 解析器,获取用户请求数据request.data,会自动根据content-type请求头的不能对数据进行解析
                        - 分页,将从数据库获取到的数据在页面进行分页显示。
                        还有其他:
                            - 认证
                            - 权限
                            - 访问频率控制
                            - ... 
                
        8. rest framework 视图你都用过哪些基类?
            
            a. 继承 APIView
                这个类属于rest framework中顶层类,内部帮助我们实现了只是基本功能:认证、权限、频率控制,但凡是数据库、分页等操作都需要手动去完成,比较原始。
                
            
            
               class GenericAPIView(APIView)
                    
                    def post(...):
                        pass 
                    
                    
                    
            b. 继承 GenericViewSet(ViewSetMixin, generics.GenericAPIView)
                如果继承它之后,路由中的as_view需要填写对应关系    .as_view({'get':'list','post':'create'})
                在内部也帮助我们提供了一些方便的方法:
                    - get_queryset
                    - get_object
                    - get_serializer
                
                注意:要设置queryset字段,否则会跑出断言的异常。
                # 只提供增加功能
                class TestView(GenericViewSet):
                    serializer_class = XXXXXXX

                    def create(self,*args,**kwargs):
                        pass # 获取数据并对数据进行操作
        
                
            c. 继承 
                    - ModelViewSet
                    - mixins.CreateModelMixin,GenericViewSet
                    - mixins.CreateModelMixin,DestroyModelMixin,GenericViewSet
            
                对数据库和分页等操作不用我们在编写,只需要继承相关类即可。
                
                示例:只提供增加功能
                class TestView(mixins.CreateModelMixin,GenericViewSet):
                    serializer_class = XXXXXXX
        
        
            类的继承关系
        
        
        9. 认证流程?
            - 如何编写?写类并实现authticate
            - 方法中可以定义三种返回值:
                - (user,auth),认证成功
                - None , 匿名用户
                - 异常 ,认证失败
            - 流程:
                - dispatch 
                - 再去request中进行认证处理
            
        10. 访问频率控制 
            - 匿名用户,根据用户IP或代理IP作为标识进行记录,为每一个用户在redis中创建一个列表
                {
                    throttle_1.1.1.1:[1526868876.497521,152686885.497521...]
                    throttle_1.1.1.2:[1526868876.497521,152686885.497521...]
                    throttle_1.1.1.3:[1526868876.497521,152686885.497521...]
                    throttle_1.1.1.4:[1526868876.497521,152686885.497521...]
                    throttle_1.1.1.5:[1526868876.497521,152686885.497521...]
                }
                
                每个用户再来访问时,需要先去记录中剔除以及过期时间,再根据列表的长度判断是否可以继续访问。
                
                如何封IP:在防火墙中进行设置
            - 注册用户,根据用户名或邮箱进行判断。
                {
                    throttle_xxxx1:[1526868876.497521,152686885.497521...]
                    throttle_xxxx2:[1526868876.497521,152686885.497521...]
                    throttle_xxxx3:[1526868876.497521,152686885.497521...]
                    throttle_xxxx4:[1526868876.497521,152686885.497521...]
                
                }
                
                每个用户再来访问时,需要先去记录中剔除以及过期时间,再根据列表的长度判断是否可以继续访问。
        
            1分钟:40次
        
        
        11. 接口的幂等性?(是否会造成2次伤害)
            一个接口通过1次相同的访问,再对该接口进行N次相同的访问时候,对资源不造影响,那么就认为接口具有幂等性。
            比如:
                GET, 第一次获取结果、第二次也是获取结果对资源都不会造成影响,幂等。
                POST,第一次新增数据,第二次也会再次新增,非幂等。
                PUT, 第一次更新数据,第二次不会再次更新,幂等。
                PATCH,第一次更新数据,第二次不会再次更新,非幂等。
                DELTE,第一次删除数据,第二次不在再删除,幂等。
            
            
        12. Https和Http区别?
        
            端口:
                http:80
                https: 443 
            流程:
                - 自定义证书
                - 认证机构 
        
        
        13. Flask框架的优势?
            
        14. Flask内置功能依赖?
            
        15. Flask第三方组件?
            - flask-session
            - flask-migrate
            - flask-script 
            - flask-sqlalchemy(SQLAlchemy)
            - blinker
            
            - wtforms
            - dbutils
            - gevent-websocket
            
            - flask-login自定义组件
            
            
        16. threading.local 以及作用?
        
        17. Flask上下文管理流程以及和django比较?
        
        18. Flask中的g的作用?
        
        19. Flask中上下文管理主要涉及到了那些相关的类?并描述类主要作用?
        
        20. 为什么要把Local的值维护成一个列表?
        
        21. Flask中多app应用是怎么完成?
        
        22. 原生SQL和ORM的区别?
        
        23. SQLAlchemy中的 session 的创建有几种方式?
            https://www.cnblogs.com/wupeiqi/articles/8259356.html
            
            - 直接创建Session对象
                engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
                Session = sessionmaker(bind=engine)


                def task(arg):
                    session = Session()

                    obj1 = Users(name="alex1")
                    session.add(obj1)

                    session.commit()


                for i in range(10):
                    t = threading.Thread(target=task, args=(i,))
                    t.start()
                                
            - 基于scoped_session(Flask-SQLAlchemy中的连接默认使用该方法)
                engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
                Session = sessionmaker(bind=engine)
                
                session = scoped_session(Session)

                def task(arg):

                    obj1 = Users(name="alex1")
                    session.add(obj1)

                    session.commit()


                for i in range(10):
                    t = threading.Thread(target=task, args=(i,))
                    t.start()
        


日后内容:
    1. redis
    2. tornado异步非阻塞
    3. 部署:nginx+uwsgi+django+supervisor
    4. 面试和简历相关
    5. 技术分享:代码发布系统
        - saltstack
        - celery
        - git/gitlab
        - jekins
        
        
内容详细:
    1. redis
        http://www.cnblogs.com/wupeiqi/articles/5132791.html
        
        - 基本操作
        
            - 连接
                - 直接连接:
                    import redis 
                    r = redis.Redis(host='10.211.55.4', port=6379)
                    r.set('foo', 'Bar')
                    print r.get('foo')
                - 连接池:
                    import redis
                    pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
                     
                    r = redis.Redis(connection_pool=pool)
                    r.set('foo', 'Bar')
                    print r.get('foo')
            
            - 5大数据类型
                - 字符串 "sadf"
                    - set('k1','123',ex=10)
                    - get 
                    - mset
                    - mget
                    - incr
                    - 超时时间:
                        import redis 
                        r = redis.Redis(host='10.211.55.4', port=6379)
                        r.set('foo', 'Bar',ex=10)
                - 字典 {'k1':'v1'}
                    - hset(name, key, value)
                    - hmset(name, mapping)
                    - hget(name,key)
                    - 超时时间(字典,列表、集合、有序结合相同):
                        import redis 
                        conn = redis.Redis(host='10.211.55.4', port=6379)
                        conn.hset('n1', 'k1','123123')
                        conn.expire('n1',10)
                    - 如果一个字典在redis中保存了10w个值,我需要将所有值全部循环并显示,请问如何实现?
                        
                        for item in r.hscan_iter('k2',count=100):
                            print item
                            
                - 列表 [11,11,22,33]
                    - lpush
                    - rpush
                    - lpop
                    - blpop
                    - rpop
                    - brpop
                    - llen
                    - lrange
                    - 如果一个列表在redis中保存了10w个值,我需要将所有值全部循环并显示,请问如何实现?
                        def list_scan_iter(name,count=3):
                            start = 0
                            while True:
                                result = conn.lrange(name, start, start+count-1)
                                start += count
                                if not result:
                                    break
                                for item in result:
                                    yield item

                        for val in list_scan_iter('num_list'):
                            print(val)
                        
                - 集合 {'alex','oldboy','日天'}
                    
                - 有序集合 {('alex',59),('oldboy',100),('日天',1)}
            
            - 公共操作:
                - delete(*names)
                - keys(pattern='*')
                expire(name ,time)
                ...
        
        - 事务 
            import redis

            pool = redis.ConnectionPool(host='10.211.55.4', port=6379)

            conn = redis.Redis(connection_pool=pool)

            # pipe = r.pipeline(transaction=False)
            pipe = conn.pipeline(transaction=True)
            # 开始事务
            pipe.multi()

            pipe.set('name', 'alex')
            pipe.set('role', 'sb')
            pipe.lpush('roless', 'sb')

            # 提交
            pipe.execute()
            
            注意:咨询是否当前分布式redis是否支持事务
        
        - 检测,watch
            面试题:你如何控制剩余的数量不会出问题?
                - 通过redis的watch实现
                    import redis
                    conn = redis.Redis(host='127.0.0.1',port=6379)

                    # conn.set('count',1000)
                    val = conn.get('count')
                    print(val)

                    with conn.pipeline(transaction=True) as pipe:

                        # 先监视,自己的值没有被修改过
                        conn.watch('count')

                        # 事务开始
                        pipe.multi()
                        old_count = conn.get('count')
                        count = int(old_count)
                        print('现在剩余的商品有:%s',count)
                        input("问媳妇让不让买?")
                        pipe.set('count', count - 1)

                        # 执行,把所有命令一次性推送过去
                        pipe.execute()
                - 数据库的锁 
                
        - 发布和订阅,只要有任务就所有订阅者每人一份。
            发布者:
                import redis

                conn = redis.Redis(host='127.0.0.1',port=6379)
                conn.publish('104.9MH', "hahahahahaha")
            订阅者:
                import redis

                conn = redis.Redis(host='127.0.0.1',port=6379)
                pub = conn.pubsub()
                pub.subscribe('104.9MH')

                while True:
                    msg= pub.parse_response()
                    print(msg)
                        
        - 主从复制
            优势:
                - 高可用
                - 分担主压力
            注意: 
                - slave设置只读
            
            
            从的配置文件添加以下记录,即可:
                slaveof 1.1.1.1 3306 
        
        - sentinel,哨兵
            启动主redis:
                redis-server /etc/redis-6379.conf  启动主redis
                redis-server /etc/redis-6380.conf  启动从redis
                
            在linux中:
                找到 /etc/redis-sentinel-8001.conf  配置文件,在内部:
                    - 哨兵的端口 port = 8001
                    - 主redis的IP,哨兵个数的一半/1
                
                找到 /etc/redis-sentinel-8002.conf  配置文件,在内部:
                    - 哨兵的端口 port = 8002
                    - 主redis的IP, 1 
            
                启动两个哨兵
                
        
        - redis-cluster
        
           redis集群、分片、分布式redis        
           redis-py-cluster

        问题:
            目前你们公司项目1000用户,QPS=1000 ,如果用户猛增10000w?
            项目如何提高的并发?
            
            1. 数据库读写分离
            2. 设置缓存
            3. 负载均衡
            
    2. 预习,项目部署
        https://www.cnblogs.com/wupeiqi/articles/8591782.html
day143

 

day141

s8day141

内容回顾:
    面试题:    
        1. django和flask区别?
        
        2. django请求生命周期?
        
        3. wsgi?
            
        4. 中间件都有哪些方法?以及执行流程?
        
        5. 中间件的应用?
            - 权限
            - cors
            - csrf
            - 缓存(中间件2/46. 视图函数
            - FBV
            - CBV
            FBV和CBV的区别?
                - 没什么区别,因为他们的本质都是函数。CBV的.as_view()返回的view函数,view函数中调用类的dispatch方法,在dispatch方法中通过反射执行get/post/delete/put等方法。
                - CBV比较简洁,GET/POST等业务功能分别放在不同get/post函数中。FBV自己做判断进行区分。
                
            CBV时的注意事项?
                - 装饰器
                    from django.views import View
                    from django.utils.decorators import method_decorator

                    def auth(func):
                        def inner(*args,**kwargs):
                            return func(*args,**kwargs)
                        return inner

                    class UserView(View):
                        
                        @method_decorator(auth)
                        def get(self,request,*args,**kwargs):
                            return HttpResponse('...')
                - csrf的装饰器要加到dispath
                        from django.views import View
                        from django.utils.decorators import method_decorator
                        from django.views.decorators.csrf import csrf_exempt,csrf_protect


                        class UserView(View):

                            @method_decorator(csrf_exempt)
                            def dispatch(self, request, *args, **kwargs):
                                return HttpResponse('...')
                            
                    或
                        from django.views import View
                        from django.utils.decorators import method_decorator
                        from django.views.decorators.csrf import csrf_exempt,csrf_protect

                        @method_decorator(csrf_exempt,name='dispatch')
                        class UserView(View):

                            def dispatch(self, request, *args, **kwargs):
                                return HttpResponse('...')
                    
        7. ORM操作
            models.UserInfo.objects.create()
            
            obj = models.UserInfo(name='xx')
            obj.save()
            
            models.UserInfo.objects.bulk_create([models.UserInfo(name='xx'),models.UserInfo(name='xx')])
        
            models.UserInfo.objects.all().delete()
            models.UserInfo.objects.all().update(age=18)
            models.UserInfo.objects.all().update(salary=F('salary')+1000)
            
            .filter(id__gt=1,)
            .exclude()
            
            .values()
            .values_list()
            
            .order_by()
            .order_by('-id')
            
            
            .anotate()
            
            .first()
            .laste()
            .get()
            .exsit()
            
            .reverse()
            .distinct()
            
            .extra()
            .raw()
            
            F和Q
            
            .select_related()
            
                title = models.CharField(max_length=32)

                class UserInfo(models.Model):
                    name = models.CharField(max_length=32)
                    email = models.CharField(max_length=32)
                    ut = models.ForeignKey(to='UserType')
                    ut = models.ForeignKey(to='UserType')
                    ut = models.ForeignKey(to='UserType')
                    ut = models.ForeignKey(to='UserType')
                    
                    
                # 1次SQL
                # select * from userinfo
                objs = UserInfo.obejcts.all()
                for item in objs:
                    print(item.name)
                    
                # n+1次SQL
                # select * from userinfo
                objs = UserInfo.obejcts.all()
                for item in objs:
                    # select * from usertype where id = item.id 
                    print(item.name,item.ut.title)
                    

                # 1次SQL
                # select * from userinfo inner join usertype on userinfo.ut_id = usertype.id 
                objs = UserInfo.obejcts.all().select_related('ut')
                for item in objs:
                    print(item.name,item.ut.title)
            
            .prefetch_related()
                # select * from userinfo where id <= 8
                # 计算:[1,2]
                # select * from usertype where id in [1,2]
                objs = UserInfo.obejcts.filter(id__lte=8).prefetch_related('ut')
                for obj in objs:
                    print(obj.name,obj.ut.title)
            
            
            补充:
                无约束:
                    class UserType(models.Model):
                        title = models.CharField(max_length=32)

                    class UserInfo(models.Model):
                        name = models.CharField(max_length=32)
                        email = models.CharField(max_length=32)
                        # 无数据库约束,但可以进行链表
                        ut = models.ForeignKey(to='UserType',db_constraint=False)
                    
                有约束:
                    class UserType(models.Model):
                        title = models.CharField(max_length=32)

                    class UserInfo(models.Model):
                        name = models.CharField(max_length=32)
                        email = models.CharField(max_length=32)
                        # 有数据库约束,可以进行链表
                        ut = models.ForeignKey(to='UserType')
                
            .using 
            .count 
            
            
            .only
            .defer
            
            .[1,100]
            
            .aggregate
            
            .fiter(id__in=[1,2])
            
            # 多对多 
            .add 
            .set 
            .remove 
            
        8. django的Form组件的作用?
            - 对用户请求的数据进行校验
            - 生成HTML标签
            
            PS:
                - form对象是一个可迭代对象。
                - 问题:choice的数据如果从数据库获取可能会造成数据无法实时更新
                        - 重写构造方法,在构造方法中重新去数据库获取值。
                        - ModelChoiceField字段
                            from django.forms import Form
                            from django.forms import fields
                            from django.forms.models import ModelChoiceField
                            class UserForm(Form):
                                name = fields.CharField(label='用户名',max_length=32)
                                email = fields.EmailField(label='邮箱')
                                ut_id = ModelChoiceField(queryset=models.UserType.objects.all())    
                            
                            依赖:
                                class UserType(models.Model):
                                    title = models.CharField(max_length=32)

                                    def __str__(self):
                                        return self.title
        
        9. 多数据库相关操作
            
            python manage.py makemigraions
            
            python manage.py migrate app名称 --databse=配置文件数据名称的别名
            
            手动操作:
                models.UserType.objects.using('db1').create(title='普通用户')
                result = models.UserType.objects.all().using('default')
                
            自动操作:
                class Router1:
                    def db_for_read(self, model, **hints):
                        """
                        Attempts to read auth models go to auth_db.
                        """
                        return 'db1'

                    def db_for_write(self, model, **hints):
                        """
                        Attempts to write auth models go to auth_db.
                        """
                        return 'default'

                配置:
                    DATABASES = {
                        'default': {
                            'ENGINE': 'django.db.backends.sqlite3',
                            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
                        },
                        'db1': {
                            'ENGINE': 'django.db.backends.sqlite3',
                            'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
                        },
                    }
                    DATABASE_ROUTERS = ['db_router.Router1',]
                    
                使用:
                    models.UserType.objects.create(title='VVIP')

                    result = models.UserType.objects.all()
                    print(result)
                                        
                补充:粒度更细
                    class Router1:
                        def db_for_read(self, model, **hints):
                            """
                            Attempts to read auth models go to auth_db.
                            """
                            if model._meta.model_name == 'usertype':
                                return 'db1'
                            else:
                                return 'default'

                        def db_for_write(self, model, **hints):
                            """
                            Attempts to write auth models go to auth_db.
                            """
                            return 'default'
            问题: 
                app01中的表在default数据库创建
                app02中的表在db1数据库创建
                
                # 第一步:
                    python manage.py makemigraions 
                
                # 第二步:
                    app01中的表在default数据库创建
                    python manage.py migrate app01 --database=default
                
                # 第三步:
                    app02中的表在db1数据库创建
                    python manage.py migrate app02 --database=db1
                    
                # 手动操作:
                    m1.UserType.objects.using('default').create(title='VVIP')
                    m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
                # 自动操作:
                    配置: 
                        class Router1:
                            def db_for_read(self, model, **hints):
                                """
                                Attempts to read auth models go to auth_db.
                                """
                                if model._meta.app_label == 'app01':
                                    return 'default'
                                else:
                                    return 'db1'

                            def db_for_write(self, model, **hints):
                                """
                                Attempts to write auth models go to auth_db.
                                """
                                if model._meta.app_label == 'app01':
                                    return 'default'
                                else:
                                    return 'db1'

                        DATABASE_ROUTERS = ['db_router.Router1',]
                    
                    使用: 
                        m1.UserType.objects.using('default').create(title='VVIP')
                        m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
            其他:
                数据库迁移时进行约束:
                    class Router1:
                        def allow_migrate(self, db, app_label, model_name=None, **hints):
                            """
                            All non-auth models end up in this pool.
                            """
                            if db=='db1' and app_label == 'app02':
                                return True
                            elif db == 'default' and app_label == 'app01':
                                return True
                            else:
                                return False
                            
                            # 如果返回None,那么表示交给后续的router,如果后续没有router,则相当于返回True
                            
                        def db_for_read(self, model, **hints):
                            """
                            Attempts to read auth models go to auth_db.
                            """
                            if model._meta.app_label == 'app01':
                                return 'default'
                            else:
                                return 'db1'

                        def db_for_write(self, model, **hints):
                            """
                            Attempts to write auth models go to auth_db.
                            """
                            if model._meta.app_label == 'app01':
                                return 'default'
                            else:
                                return 'db1'

                                
今日内容:            
    1. websocket
    2. tornado
    3. redis
    
内容详细:
    1. websocket 
        回顾:
            - 什么是轮训?
                - 通过定时器让程序每隔n秒执行一次操作。
            - 什么是长轮训?
                - 浏览器向后端发起请求,后端会将请求 hang 住,最多hang 30s。
                    如果一直不返回数据:则最多等待30s,紧接着用户立即再发送请求。
                    如果有数据返回:则操作数据并立即再发送请求。
                PS:后台可以使用队列或redis的列表来hang主请求。
                
            - 轮训和长轮训目的?
                由于Http请求是无状态、短连接所以服务端无法向客户端实时推送消息,
                所以,我们就是可以使用:轮训和长轮训去服务端获取实时数据。
    
        作业:基于redis和长轮询实现投票。
        
        
        websocket是什么?
            websocket是一套类似于http的协议。
            扩展:
                     http协议:\r\n分割、请求头和请求体\r\n分割、无状态、短连接。
                websocket协议:\r\n分割、创建连接后不断开、 验证+数据加密;
                
            websocket本质:
                - 就是一个创建连接后不断开的socket
                - 当连接成功之后:
                    - 客户端(浏览器)会自动向服务端发送消息,包含: Sec-WebSocket-Key: iyRe1KMHi4S4QXzcoboMmw==
                    - 服务端接收之后,会对于该数据进行加密:
                        base64(sha1(swk + magic_string))
                        
                    - 构造响应头:
                        HTTP/1.1 101 Switching Protocols\r\n
                        Upgrade:websocket\r\n
                        Connection: Upgrade\r\n
                        Sec-WebSocket-Accept: 加密后的值\r\n
                        WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n
            
                    - 发给客户端(浏览器)
                - 建立:双工通道,接下来就可以进行收发数据
                    - 发送的数据是加密,解密,根据payload_len的值进行处理:
                        - payload_len <=125
                        - payload_len ==126
                        - payload_len ==127
                    - 获取内容:
                        - mask_key 
                        - 数据 
                        根据mask_key和数据进行位运算,就可以把值解析出来。
                        
            
            面试:
                a. 什么是websocket?
                    websocket是给浏览器新建一套协议。协议规定:浏览器和服务端连接之后不断开,以此可以完成:服务端向客户端主动推送消息。
                    websocket协议额外做的一些前天操作:
                        - 握手,连接前进行校验
                        - 发送数据加密
                    
                b. websocket本质
                    - socket
                    - 握手,魔法字符串+加密 
                    - 加密,payload_len=127/126/<=125 -> mask key 
            
            
            
        在项目中使用:
            - django: channel
            - flask:  gevent-websocket 
            - tornado: 内置
            
        Flask示例:
            a. 安装 
                pip3 install gevent-websocket 
                
                作用:
                    - 处理Http、Websocket协议的请求 -> socket 
                    - 封装Http、Websocket相关数据   -> request
            b. 基本结构
                from geventwebsocket.handler import WebSocketHandler
                from gevent.pywsgi import WSGIServer
                
                
                @app.route('/test')
                def test():
                    ws = request.environ.get('wsgi.websocket')
                    ws.receive()
                    ws.send(message)
                    ws.close()
                    return render_template('index.html')
                                
                if __name__ == '__main__':
                    http_server = WSGIServer(('0.0.0.0', 5000,), app, handler_class=WebSocketHandler)
                    http_server.serve_forever()

                
            c. WEB聊天室:
                后端:
                    from geventwebsocket.handler import WebSocketHandler
                    from gevent.pywsgi import WSGIServer
                    from flask import Flask,render_template,request
                    import pickle

                    app = Flask(__name__)
                    app.secret_key = 'xfsdfqw'


                    @app.route('/index')
                    def index():
                        return render_template('index.html')


                    WS_LIST = []

                    @app.route('/test')
                    def test():
                        ws = request.environ.get('wsgi.websocket')
                        if not ws:
                            return '请使用WebSocket协议'
                        # websocket连接已经成功
                        WS_LIST.append(ws)
                        while True:
                            # 等待用户发送消息,并接受
                            message = ws.receive()

                            # 关闭:message=None
                            if not message:
                                WS_LIST.remove(ws)
                                break

                            for item in WS_LIST:
                                item.send(message)

                        return "asdfasdf"


                    if __name__ == '__main__':
                        http_server = WSGIServer(('0.0.0.0', 5000,), app, handler_class=WebSocketHandler)
                        http_server.serve_forever()
                
                前端:
                    <!DOCTYPE html>
                    <html lang="zh-cn">
                    <head>
                        <meta charset="UTF-8">
                        <meta http-equiv="X-UA-Compatible" content="IE=edge">
                        <meta name="viewport" content="width=device-width, initial-scale=1">
                        <title>Title</title>
                        <style>
                            .msg-item{
                                padding: 5px;
                                border: 1px;
                                margin: 0 5px;
                            }
                        </style>
                    </head>
                    <body>
                        <h1>首页</h1>
                        <div>
                            <h2>发送消息</h2>
                            <input id="msg" type="text"  /> <input type="button" value="发送" onclick="sendMsg()">
                            <h2>接收消息</h2>
                            <div id="container">

                            </div>
                        </div>

                        <script src="/static/jquery-3.3.1.min.js"></script>
                        <script>

                            ws = new WebSocket('ws://192.168.12.42:5000/test');
                            ws.onmessage = function (event) {
                                var tag = document.createElement('div');
                                tag.className = 'msg-item';
                                tag.innerText = event.data;
                                $('#container').append(tag);
                            }

                            function sendMsg() {
                                ws.send($('#msg').val());
                            }

                        </script>
                    </body>
                    </html>
            
                
        
        问题: 
            a. 实时消息推送,利用什么技术实现?
                - 轮训         ,优点:简单;                       缺点:请求次数多,服务器压力大,消息延迟。
                - 长轮训    ,优点:实时接收数据,兼容性好;     缺点:请求次数比原来少,但是相对来也不少。
                - websocket ,优点:代码简单,不再反复创建连接。 缺点:兼容性。 
            b. 在框架中使用WebSocket:
                - django,channel
                - flask,gevent-websocket 
                - tornado,内置 
                
    2. 初始tornado以及tornado websocket 
        什么是Tornado?
            Tornado是一个轻量级的Web框架,异步非阻塞+内置WebSocket功能。
        安装:
            pip3 install tornado 
            
        示例:
            import tornado
            from tornado.web import Application
            from tornado.web import RequestHandler
            from tornado.websocket import WebSocketHandler


            class IndexHandler(RequestHandler):

                def get(self, *args, **kwargs):
                    # self.write('Hello World')
                    self.render('index.html')

                def post(self, *args, **kwargs):
                    user = self.get_argument('user')
                    self.write('成功')

            WS_LIST = []
            class MessageHandler(WebSocketHandler):

                def open(self, *args, **kwargs):
                    WS_LIST.append(self)

                def on_message(self, message):
                    for ws in WS_LIST:
                        ws.write_message(message)

                def on_close(self):
                    WS_LIST.remove(self)



            settings = {
                'template_path':'templates',
                'static_path':'static',
            }

            app = Application([
                (r"/index", IndexHandler),
                (r"/message", MessageHandler),
            ],**settings)

            if __name__ == '__main__':
                app.listen(address='0.0.0.0',port=9999)
                tornado.ioloop.IOLoop.instance().start()
day141-websocket

 

day140

s8day141 

内容回顾:
    面试题:
        1. django和flask框架的区别?
            django:大而全的全的框架,重武器;内置很多组件:ORM、admin、Form、ModelForm、中间件、信号、缓存、csrf等
             flask: 微型框架、可扩展强,如果开发简单程序使用flask比较快速,如果实现负责功能就需要引入一些组件:flask-session/flask-SQLAlchemy/wtforms/flask-migrate/flask-script/blinker
            
            这两个框架都是基于wsgi协议实现的,默认使用的wsgi模块不一样。
            还有一个显著的特点,他们处理请求的方式不同:
                django: 通过将请求封装成Request对象,再通过参数进行传递。
                 flask:通过上下文管理实现。
            
            延伸:
                - django组件 
                - flask组件,用途 
                - wsgi
                - 上下文管理
        
        2. wsgi作用?
            web服务网关接口,一套协议。
            实现了wsgi协议的模块本质上就是编写了socket服务端,用来监听用户请求,如果有请求到来,则将请求进行一次封装,然后将【请求】交给 web框架来进行下一步处理。
            目前接触:
                wsgiref 
                werkzurg
                uwsgi 
                
            
            
            from wsgiref.simple_server import make_server
 
            def run_server(environ, start_response):
                """
                environ: 封装了请求相关的数据
                start_response:用于设置响应头相关数据
                """
                start_response('200 OK', [('Content-Type', 'text/html')])
                return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
             
             
            if __name__ == '__main__':
                httpd = make_server('', 8000, run_server)
                httpd.serve_forever()
                        
                        
            Django源码:
                class WSGIHandler(base.BaseHandler):
                    request_class = WSGIRequest

                    def __init__(self, *args, **kwargs):
                        super(WSGIHandler, self).__init__(*args, **kwargs)
                        self.load_middleware()

                    def __call__(self, environ, start_response):
                        # 请求刚进来之后 #
                        
                        set_script_prefix(get_script_name(environ))
                        signals.request_started.send(sender=self.__class__, environ=environ)
                        request = self.request_class(environ)
                        response = self.get_response(request)

                        response._handler_class = self.__class__

                        status = '%d %s' % (response.status_code, response.reason_phrase)
                        response_headers = [(str(k), str(v)) for k, v in response.items()]
                        for c in response.cookies.values():
                            response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
                        start_response(force_str(status), response_headers)
                        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
                            response = environ['wsgi.file_wrapper'](response.file_to_stream)
                        return response
        
        3. django 请求生命周期
            
            a. wsgi, 创建socket服务端,用于接收用户请求并对请求进行初次封装。
            b. 中间件,对所有请求到来之前,响应之前定制一些操作。
            c. 路由匹配,在url和视图函数对应关系中,根据当前请求url找到相应的函数。
            d. 执行视图函数,业务处理【通过ORM去数据库中获取数据,再去拿到模板,然后将数据和模板进行渲染】
            e. 再经过所有中间件
            f. 通过wsgi将响应返回给用户。
            【图】
            
        4. 中间件的执行流程?
            
            所有方法:
                - process_request
                - process_view
                - process_template_response , 当视图函数的返回值对象中有render方法时,该方法才会被调用。
                - process_response
                - process_excaption
            
        5. 中间件的应用?
            - 登录验证,为什么:如果不适用就需要为每个函数添加装饰器,太繁琐。
            - 权限处理,为什么:用户登录后,将权限放到session中,然后再每次请求时需要判断当前用户是否有权访问当前url,这检查的东西就可以放到中间件中进行统一处理。
            - 还有一些内置: 
                - csrf,为什么
                - session,为什么
                - 全站缓存 ,为什么
            - 另外,还有一个就是处理:跨域 (前后端分离时,本地测试开发时使用的。)
            
        6. csrf原理
            目标:防止用户直接向服务端发起POST请求。
            方案:先发送GET请求时,将token保存到:cookie、Form表单中(隐藏的input标签),以后再发送请求时只要携带过来即可。
            
            问题:如果想后台发送POST请求?
                
                form表单提交:
                    <form method="POST">
                        {% csrf_token %}
                        <input type='text' name='user' />
                        <input type='submit' />
                    </form>
                    
                ajax提交:
                    $.ajax({
                        url:'/index',
                        type:'POST',
                        data:{csrfmiddlewaretoken:'{{ csrf_token }}',name:'alex'}
                    })
                    
                    前提:引入jquery + 引入jquery.cookie 
                    $.ajax({
                        url: 'xx',
                        type:'POST',
                        data:{name:'oldboyedu'},
                        headers:{
                            X-CSRFToken: $.cookie('csrftoken')
                        },
                        dataType:'json', // arg = JSON.parse('{"k1":123}')
                        success:function(arg){
                            
                        }
                    })
                    
                    优化方案:
                        <body>
                            <input type="button" onclick="Do1();"  value="Do it"/>
                            <input type="button" onclick="Do2();"  value="Do it"/>
                            <input type="button" onclick="Do3();"  value="Do it"/>

                            <script src="/static/jquery-3.3.1.min.js"></script>
                            <script src="/static/jquery.cookie.js"></script>
                            <script>
                                $.ajaxSetup({
                                    beforeSend: function(xhr, settings) {
                                        xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
                                    }
                                });

                                 function Do1(){
                                    $.ajax({
                                        url:"/index/",
                                        data:{id:1},
                                        type:'POST',
                                        success:function(data){
                                            console.log(data);
                                        }
                                    });
                                }

                                 function Do2(){
                                    $.ajax({
                                        url:"/index/",
                                        data:{id:1},
                                        type:'POST',
                                        success:function(data){
                                            console.log(data);
                                        }
                                    });
                                }

                                 function Do3(){
                                    $.ajax({
                                        url:"/index/",
                                        data:{id:1},
                                        type:'POST',
                                        success:function(data){
                                            console.log(data);
                                        }
                                    });
                                }
                            </script>
                        </body>
                        
                
                爬虫:
                    reqeusts.post()
                
        
        7. 视图函数
            
            FBV:
                def index(request):
                    pass 
            
            CBV:    
                class IndexView(View):
                    
                    # 如果是crsf相关,必须放在此处
                    def dispach(self,request):
                        # 通过反射执行post/get 
                    
                    @method_decoretor(装饰器函数)
                    def get(self,request):
                        pass
                        
                    def post(self,request):
                        pass 
                    
                路由:IndexView.as_view()
            
        8. ORM 
            a. 增删改查
            b. 常用 
                order_by 
                
                group_by 
                
                limit 
                
                练表/跨表 
            c. 靠近原生SQL
                - extra
                    def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
                        # 构造额外的查询条件或者映射,如:子查询

                        Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
                        Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
                        Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
                        Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
                - raw 
                    def raw(self, raw_query, params=None, translations=None, using=None):
                        # 执行原生SQL
                        models.UserInfo.objects.raw('select * from userinfo')

                        # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
                        models.UserInfo.objects.raw('select id as nid,name as title  from 其他表')

                        # 为原生SQL设置参数
                        models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

                        # 将获取的到列名转换为指定列名
                        name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
                        Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

                        # 指定数据库
                        models.UserInfo.objects.raw('select * from userinfo', using="default")
                - 原生
                        from django.db import connection, connections
                        cursor = connection.cursor()  # cursor = connections['default'].cursor()
                        cursor.execute("""SELECT * from auth_user where id = %s""", [1])
                        row = cursor.fetchone() # fetchall()/fetchmany(..)
            d. 高级一点 
                - F 
                - Q
                - select_related
                - prefech_related
            e. 其他:
                ##################################################################
                # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
                ##################################################################

                def all(self)
                    # 获取所有的数据对象

                def filter(self, *args, **kwargs)
                    # 条件查询
                    # 条件可以是:参数,字典,Q

                def exclude(self, *args, **kwargs)
                    # 条件查询
                    # 条件可以是:参数,字典,Q

                def select_related(self, *fields)
                     性能相关:表之间进行join连表操作,一次性获取关联的数据。
                     model.tb.objects.all().select_related()
                     model.tb.objects.all().select_related('外键字段')
                     model.tb.objects.all().select_related('外键字段__外键字段')

                def prefetch_related(self, *lookups)
                    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
                            # 获取所有用户表
                            # 获取用户类型表where id in (用户表中的查到的所有用户ID)
                            models.UserInfo.objects.prefetch_related('外键字段')



                            from django.db.models import Count, Case, When, IntegerField
                            Article.objects.annotate(
                                numviews=Count(Case(
                                    When(readership__what_time__lt=treshold, then=1),
                                    output_field=CharField(),
                                ))
                            )

                            students = Student.objects.all().annotate(num_excused_absences=models.Sum(
                                models.Case(
                                    models.When(absence__type='Excused', then=1),
                                default=0,
                                output_field=models.IntegerField()
                            )))

                def annotate(self, *args, **kwargs)
                    # 用于实现聚合group by查询

                    from django.db.models import Count, Avg, Max, Min, Sum

                    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
                    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

                    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
                    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

                    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
                    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

                def distinct(self, *field_names)
                    # 用于distinct去重
                    models.UserInfo.objects.values('nid').distinct()
                    # select distinct nid from userinfo

                    注:只有在PostgreSQL中才能使用distinct进行去重

                def order_by(self, *field_names)
                    # 用于排序
                    models.UserInfo.objects.all().order_by('-id','age')

                def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
                    # 构造额外的查询条件或者映射,如:子查询

                    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
                    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
                    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
                    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

                 def reverse(self):
                    # 倒序
                    models.UserInfo.objects.all().order_by('-nid').reverse()
                    # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


                 def defer(self, *fields):
                    models.UserInfo.objects.defer('username','id')
                    或
                    models.UserInfo.objects.filter(...).defer('username','id')
                    #映射中排除某列数据

                 def only(self, *fields):
                    #仅取某个表中的数据
                     models.UserInfo.objects.only('username','id')
                     或
                     models.UserInfo.objects.filter(...).only('username','id')

                 def using(self, alias):
                     指定使用的数据库,参数为别名(setting中的设置)


                ##################################################
                # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
                ##################################################

                def raw(self, raw_query, params=None, translations=None, using=None):
                    # 执行原生SQL
                    models.UserInfo.objects.raw('select * from userinfo')

                    # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
                    models.UserInfo.objects.raw('select id as nid from 其他表')

                    # 为原生SQL设置参数
                    models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

                    # 将获取的到列名转换为指定列名
                    name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
                    Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

                    # 指定数据库
                    models.UserInfo.objects.raw('select * from userinfo', using="default")

                    ################### 原生SQL ###################
                    from django.db import connection, connections
                    cursor = connection.cursor()  # cursor = connections['default'].cursor()
                    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
                    row = cursor.fetchone() # fetchall()/fetchmany(..)


                def values(self, *fields):
                    # 获取每行数据为字典格式

                def values_list(self, *fields, **kwargs):
                    # 获取每行数据为元祖

                def dates(self, field_name, kind, order='ASC'):
                    # 根据时间进行某一部分进行去重查找并截取指定内容
                    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
                    # order只能是:"ASC"  "DESC"
                    # 并获取转换后的时间
                        - year : 年-01-01
                        - month: 年-月-01
                        - day  : 年-月-日

                    models.DatePlus.objects.dates('ctime','day','DESC')

                def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
                    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
                    # kind只能是 "year", "month", "day", "hour", "minute", "second"
                    # order只能是:"ASC"  "DESC"
                    # tzinfo时区对象
                    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
                    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

                    """
                    pip3 install pytz
                    import pytz
                    pytz.all_timezones
                    pytz.timezone(‘Asia/Shanghai’)
                    """

                def none(self):
                    # 空QuerySet对象


                ####################################
                # METHODS THAT DO DATABASE QUERIES #
                ####################################

                def aggregate(self, *args, **kwargs):
                   # 聚合函数,获取字典类型聚合结果
                   from django.db.models import Count, Avg, Max, Min, Sum
                   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
                   ===> {'k': 3, 'n': 4}

                def count(self):
                   # 获取个数

                def get(self, *args, **kwargs):
                   # 获取单个对象

                def create(self, **kwargs):
                   # 创建对象

                def bulk_create(self, objs, batch_size=None):
                    # 批量插入
                    # batch_size表示一次插入的个数
                    objs = [
                        models.DDD(name='r11'),
                        models.DDD(name='r22')
                    ]
                    models.DDD.objects.bulk_create(objs, 10)

                def get_or_create(self, defaults=None, **kwargs):
                    # 如果存在,则获取,否则,创建
                    # defaults 指定创建时,其他字段的值
                    obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

                def update_or_create(self, defaults=None, **kwargs):
                    # 如果存在,则更新,否则,创建
                    # defaults 指定创建时或更新时的其他字段
                    obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

                def first(self):
                   # 获取第一个

                def last(self):
                   # 获取最后一个

                def in_bulk(self, id_list=None):
                   # 根据主键ID进行查找
                   id_list = [11,21,31]
                   models.DDD.objects.in_bulk(id_list)

                def delete(self):
                   # 删除

                def update(self, **kwargs):
                    # 更新

                def exists(self):
    
    爬虫相关:
        - request/bs4
            - requests模块
                - 参数:
                    - url
                    - headers 
                    - cookies 
                    - data 
                    - json 
                    - params 
                    - proxy
                - 返回值:
                    - content
                    - iter_content
                    - text 
                    - encoding="utf-8"
                    - cookie.get_dict()
            - bs4 
                - 解析:html.parser  -> lxml 
                - find 
                - find_all
                - text 
                - attrs
                - get 
            - 其他:
                常见请求头:
                    - user-agent
                    - host
                    - referer
                    - cookie 
                    - content-type 
                套路:
                    - 先给你cookie,然后再给你授权。
                    - 凭证
                轮询+长轮询
        - scrapy 
            - 高性能相关,单线程并发发送Http请求
                - twisted
                - gevent 
                - asyncio
              本质:基于IO多路复用+非阻塞的socket客户端实现
              问题:异步非阻塞?
              
              问题:什么是协程?
              
            - scrapy框架
                - scrapy执行流程(包含所有组件)
                - 记录爬虫爬取数据深度(层级),request.meta['depth']
                - 传递cookie
                    - 手动 
                    - 自动:meta={'cookiejar':True}
                    
                - 起始URL
                - 持久化:pipelines/items 
                - 去重
                - 调度器
                - 中间件 
                    - 下载中间件
                        - agent
                        - proxy 
                    - 爬虫中间件
                        - depth 
                - 扩展+信号 
                - 自定义命令
            - scrapy-redis组件,本质:去重、调度器任务、pipeline、起始URL放到redis中。
                - 去重,使用的redis的集合。
                - 调度器,
                        - redis列表
                            - 先进先出队列
                            - 后进先出栈
                        - redis有序集合
                            - 优先级队列
                        
                        PS:深度和广度优先
                - pipelines
                        - redis列表 
                
                - 起始URL 
                        - redis列表
                        - redis集合
                        
                补充:
                    自定义encoder实现序列化时间等特殊类型:
                        json.dumps(xx,cls=MyEncoder)


            - scrapy 
                        
                            
        
        
今日内容:
    - django 
    - websocket
    - redis
    
    
    
今日内容:
    1. django Form:
        - 用于对用户请求的数据校验。
        - 生成HTML标签
        
        from django.forms import Form
        from django.forms import fields
        class UserForm(Form):
            name = fields.CharField(label='用户名',max_length=32)
            email = fields.EmailField(label='邮箱')
            ut_id = fields.ChoiceField(
                # choices=[(1,'二笔用户'),(2,'闷骚')]
                choices=[]
            )

            def __init__(self,*args,**kwargs):
                super(UserForm,self).__init__(*args,**kwargs)
                
                # 每次实例化,重新去数据库获取数据并更新
                self.fields['ut_id'].choices = models.UserType.objects.all().values_list('id','title')

        def user(request):
            if request.method == "GET":
                form = UserForm()
                return render(request,'user.html',{'form':form})
    
    2. 在线实时投票系统
        方案一:用户手动刷新
        
        方案二:轮询实现票数实时显示
            
        方案三:长轮询实现票数实时显示
                
                作业:基于redis的列表实现。
day140

 

day139

s8day139 爬虫
内容回顾:
    面试题:
        前端:    
            1. HTML、CSS、JS
            2. 框架和类库:
                - jQuery
                - BootStrap
                - Vue.js 
            3. 响应式布局
                @media (min-width: 768px){
                    .pg-header{
                        background-color: green;
                    }
                }
                @media (min-width: 992px){
                    .pg-header{
                        background-color: pink;
                    }
                }
            
            4. jQuery
                
            5. jQuery Ajax 和 原生Ajax
                jQuery ajax: $.ajax(...)
                  原生 ajax:XMLHttpRequest
                  
            6. 跨域 
                - JSONP
                - CORS
                    - 简单请求
                    - 复杂请求
                
        Web框架:
            1. 你了解哪些Web框架和区别?
            
            2. Django请求生命周期?
            
            3. 什么是wsgi?
                是web服务网关接口,是一套协议。以下模块实现了wsgi协议:
                    - wsgiref
                    - werkzurg
                    - uwsgi
                以上模块本质:实现socket监听请求,获取请求后将数据封装,然后交给web框架处理。
            
            4. 中间件 
                中间件的作用?对所有的请求进行批量处理,在视图函数执行前后进行自定义操作。
                中间件的应用?cors跨域/用户登录校验/权限处理/CSRF/session/缓存
                中间件中方法?5个方法
                
                PS:
                    csrf本质?
                        - 用户先发送GET获取csrf token: Form表单中一个隐藏的标签 + cookie
                        - 发起POST请求时,需要携带之前发送给用户的csrf token;
                        - 在中间件的process_view方法中进行校验。
            5. 路由系统
            
            6. 视图 
                FBV
                CBV
            
        scrapy框架
        
            
        
        
        
今日内容:
    - scrapy-redis
    - scrapy实现机制
    
内容详细:
    1. scrapy-redis
        pip3 install scrapy-redis 
        
        目标:帮助开发者实现分布式爬虫程序。
    
    
        a. 利用scrapy-redis做去重规则:
            # ############ 连接redis 信息 #################
            REDIS_HOST = '127.0.0.1'                            # 主机名
            REDIS_PORT = 6379                                   # 端口
            # REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
            REDIS_PARAMS  = {}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
            # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # 指定连接Redis的Python模块  默认:redis.StrictRedis
            REDIS_ENCODING = "utf-8"


            # 自定义去重规则
            DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
                
            
            如果想要对redis-scrapy的去重规则进行扩展:
                from scrapy_redis.dupefilter import RFPDupeFilter


                class MyRFPDupeFilter(RFPDupeFilter):
                    pass
                    
                    
                # 自定义去重规则
                DUPEFILTER_CLASS = "wenwen.dup.MyRFPDupeFilter"
    
        b. 调度器 
        
            # 有引擎来执行:自定义调度器
            SCHEDULER = "scrapy_redis.scheduler.Scheduler"
            
            SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'  # 默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
            SCHEDULER_QUEUE_KEY = '%(spider)s:requests'  # 调度器中请求存放在redis中的key
            SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  # 对保存到redis中的数据进行序列化,默认使用pickle
            SCHEDULER_PERSIST = True  # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
            SCHEDULER_FLUSH_ON_START = True  # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
            # SCHEDULER_IDLE_BEFORE_CLOSE = 10  # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。
            SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'  # 去重规则,在redis中保存时对应的key  chouti:dupefilter
            SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'  # 去重规则对应处理的类
            DUPEFILTER_DEBUG = False
        
        
        1. scrapy中去重规则是如何实现?
            class RFPDupeFilter(BaseDupeFilter):
                """Request Fingerprint duplicates filter"""

                def __init__(self, path=None, debug=False):
                    self.fingerprints = set()
                    

                @classmethod
                def from_settings(cls, settings):
                    debug = settings.getbool('DUPEFILTER_DEBUG')
                    return cls(job_dir(settings), debug)

                def request_seen(self, request):
                    # 将request对象转换成唯一标识。
                    fp = self.request_fingerprint(request)
                    # 判断在集合中是否存在,如果存在则返回True,表示已经访问过。
                    if fp in self.fingerprints:
                        return True
                    # 之前未访问过,将url添加到访问记录中。
                    self.fingerprints.add(fp)

                def request_fingerprint(self, request):
                    return request_fingerprint(request)

                    
        2. scrapy-redis中去重规则是如何实现?
            class RFPDupeFilter(BaseDupeFilter):
                """Redis-based request duplicates filter.

                This class can also be used with default Scrapy's scheduler.

                """

                logger = logger

                def __init__(self, server, key, debug=False):
                    
                    # self.server = redis连接
                    self.server = server
                    # self.key = dupefilter:123912873234
                    self.key = key
                    

                @classmethod
                def from_settings(cls, settings):
                    
                    # 读取配置,连接redis
                    server = get_redis_from_settings(settings)

                    #  key = dupefilter:123912873234
                    key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}
                    debug = settings.getbool('DUPEFILTER_DEBUG')
                    return cls(server, key=key, debug=debug)

                @classmethod
                def from_crawler(cls, crawler):
                    
                    return cls.from_settings(crawler.settings)

                def request_seen(self, request):
                    
                    fp = self.request_fingerprint(request)
                    # This returns the number of values added, zero if already exists.
                    # self.server=redis连接
                    # 添加到redis集合中:1,添加工程;0,已经存在
                    added = self.server.sadd(self.key, fp)
                    return added == 0

                def request_fingerprint(self, request):
                    
                    return request_fingerprint(request)

                def close(self, reason=''):
                    
                    self.clear()

                def clear(self):
                    """Clears fingerprints data."""
                    self.server.delete(self.key)

                
        3. scrapy中的调度器是如何实现?
            将request对象全部放到内存维护的队列:self.q = deque()
            将request对象全部放到硬盘维护的队列:文件操作
            
            
            SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleLifoDiskQueue'
            SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.LifoMemoryQueue'
            SCHEDULER_PRIORITY_QUEUE = 'queuelib.PriorityQueue'
            
            
                        
            class Scheduler(object):

                def __init__(self, dupefilter, jobdir=None, dqclass=None, mqclass=None,
                             logunser=False, stats=None, pqclass=None):
                    self.df = dupefilter
                    self.dqdir = self._dqdir(jobdir)
                    self.pqclass = pqclass
                    self.dqclass = dqclass
                    self.mqclass = mqclass
                    self.logunser = logunser
                    self.stats = stats

                @classmethod
                def from_crawler(cls, crawler):
                    settings = crawler.settings
                    dupefilter_cls = load_object(settings['DUPEFILTER_CLASS'])
                    dupefilter = dupefilter_cls.from_settings(settings)
                    
                    pqclass = load_object(settings['SCHEDULER_PRIORITY_QUEUE'])
                    dqclass = load_object(settings['SCHEDULER_DISK_QUEUE'])
                    mqclass = load_object(settings['SCHEDULER_MEMORY_QUEUE'])
                    
                    
                    logunser = settings.getbool('LOG_UNSERIALIZABLE_REQUESTS', settings.getbool('SCHEDULER_DEBUG'))
                    return cls(dupefilter, jobdir=job_dir(settings), logunser=logunser,
                               stats=crawler.stats, pqclass=pqclass, dqclass=dqclass, mqclass=mqclass)

                def has_pending_requests(self):
                    return len(self) > 0

                def open(self, spider):
                    self.spider = spider
                    self.mqs = self.pqclass(self._newmq)
                    self.dqs = self._dq() if self.dqdir else None
                    return self.df.open()

                def close(self, reason):
                    if self.dqs:
                        prios = self.dqs.close()
                        with open(join(self.dqdir, 'active.json'), 'w') as f:
                            json.dump(prios, f)
                    return self.df.close(reason)

                def enqueue_request(self, request):
                    # request.dont_filter=False
                        # self.df.request_seen(request):
                        #   - True,已经访问
                        #   - False,未访问
                    # request.dont_filter=True,全部加入到调度器
                    if not request.dont_filter and self.df.request_seen(request):
                        self.df.log(request, self.spider)
                        return False
                    # 如果往下走,把请求加入调度器
                    dqok = self._dqpush(request)
                    if dqok:
                        self.stats.inc_value('scheduler/enqueued/disk', spider=self.spider)
                    else:
                        self._mqpush(request)
                        self.stats.inc_value('scheduler/enqueued/memory', spider=self.spider)
                    self.stats.inc_value('scheduler/enqueued', spider=self.spider)
                    return True

                def next_request(self):
                    request = self.mqs.pop()
                    if request:
                        self.stats.inc_value('scheduler/dequeued/memory', spider=self.spider)
                    else:
                        request = self._dqpop()
                        if request:
                            self.stats.inc_value('scheduler/dequeued/disk', spider=self.spider)
                    if request:
                        self.stats.inc_value('scheduler/dequeued', spider=self.spider)
                    return request

                def __len__(self):
                    return len(self.dqs) + len(self.mqs) if self.dqs else len(self.mqs)

                def _dqpush(self, request):
                    if self.dqs is None:
                        return
                    try:
                        reqd = request_to_dict(request, self.spider)
                        self.dqs.push(reqd, -request.priority)
                    except ValueError as e:  # non serializable request
                        if self.logunser:
                            msg = ("Unable to serialize request: %(request)s - reason:"
                                   " %(reason)s - no more unserializable requests will be"
                                   " logged (stats being collected)")
                            logger.warning(msg, {'request': request, 'reason': e},
                                           exc_info=True, extra={'spider': self.spider})
                            self.logunser = False
                        self.stats.inc_value('scheduler/unserializable',
                                             spider=self.spider)
                        return
                    else:
                        return True

                def _mqpush(self, request):
                    self.mqs.push(request, -request.priority)

                def _dqpop(self):
                    if self.dqs:
                        d = self.dqs.pop()
                        if d:
                            return request_from_dict(d, self.spider)

                def _newmq(self, priority):
                    return self.mqclass()

                def _newdq(self, priority):
                    return self.dqclass(join(self.dqdir, 'p%s' % priority))

                def _dq(self):
                    activef = join(self.dqdir, 'active.json')
                    if exists(activef):
                        with open(activef) as f:
                            prios = json.load(f)
                    else:
                        prios = ()
                    q = self.pqclass(self._newdq, startprios=prios)
                    if q:
                        logger.info("Resuming crawl (%(queuesize)d requests scheduled)",
                                    {'queuesize': len(q)}, extra={'spider': self.spider})
                    return q

                def _dqdir(self, jobdir):
                    if jobdir:
                        dqdir = join(jobdir, 'requests.queue')
                        if not exists(dqdir):
                            os.makedirs(dqdir)
                        return dqdir

        
        4. scrapy-redis中的调度器是如何实现?
            将请求通过pickle进行序列化,然后添加到redis: 列表或有序结合中。
                SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue' 
                
            class Scheduler(object):
                

                def __init__(self, server,
                             persist=False,
                             flush_on_start=False,
                             queue_key=defaults.SCHEDULER_QUEUE_KEY,
                             queue_cls=defaults.SCHEDULER_QUEUE_CLASS,
                             dupefilter_key=defaults.SCHEDULER_DUPEFILTER_KEY,
                             dupefilter_cls=defaults.SCHEDULER_DUPEFILTER_CLASS,
                             idle_before_close=0,
                             serializer=None):
                    
                    if idle_before_close < 0:
                        raise TypeError("idle_before_close cannot be negative")

                    self.server = server
                    self.persist = persist
                    self.flush_on_start = flush_on_start
                    self.queue_key = queue_key
                    self.queue_cls = queue_cls
                    self.dupefilter_cls = dupefilter_cls
                    self.dupefilter_key = dupefilter_key
                    self.idle_before_close = idle_before_close
                    self.serializer = serializer
                    self.stats = None

                def __len__(self):
                    return len(self.queue)

                @classmethod
                def from_settings(cls, settings):
                    kwargs = {
                        'persist': settings.getbool('SCHEDULER_PERSIST'),
                        'flush_on_start': settings.getbool('SCHEDULER_FLUSH_ON_START'),
                        'idle_before_close': settings.getint('SCHEDULER_IDLE_BEFORE_CLOSE'),
                    }

                    # If these values are missing, it means we want to use the defaults.
                    optional = {
                        # TODO: Use custom prefixes for this settings to note that are
                        # specific to scrapy-redis.
                        'queue_key': 'SCHEDULER_QUEUE_KEY',
                        'queue_cls': 'SCHEDULER_QUEUE_CLASS',
                        'dupefilter_key': 'SCHEDULER_DUPEFILTER_KEY',
                        # We use the default setting name to keep compatibility.
                        'dupefilter_cls': 'DUPEFILTER_CLASS',
                        'serializer': 'SCHEDULER_SERIALIZER',
                    }
                    for name, setting_name in optional.items():
                        val = settings.get(setting_name)
                        if val:
                            kwargs[name] = val

                    # Support serializer as a path to a module.
                    if isinstance(kwargs.get('serializer'), six.string_types):
                        kwargs['serializer'] = importlib.import_module(kwargs['serializer'])

                    server = connection.from_settings(settings)
                    # Ensure the connection is working.
                    server.ping()

                    return cls(server=server, **kwargs)

                @classmethod
                def from_crawler(cls, crawler):
                    instance = cls.from_settings(crawler.settings)
                    # FIXME: for now, stats are only supported from this constructor
                    instance.stats = crawler.stats
                    return instance

                def open(self, spider):
                    self.spider = spider

                    try:
                        self.queue = load_object(self.queue_cls)(
                            server=self.server,
                            spider=spider,
                            key=self.queue_key % {'spider': spider.name},
                            serializer=self.serializer,
                        )
                    except TypeError as e:
                        raise ValueError("Failed to instantiate queue class '%s': %s",
                                         self.queue_cls, e)

                    try:
                        self.df = load_object(self.dupefilter_cls)(
                            server=self.server,
                            key=self.dupefilter_key % {'spider': spider.name},
                            debug=spider.settings.getbool('DUPEFILTER_DEBUG'),
                        )
                    except TypeError as e:
                        raise ValueError("Failed to instantiate dupefilter class '%s': %s",
                                         self.dupefilter_cls, e)

                    if self.flush_on_start:
                        self.flush()
                    # notice if there are requests already in the queue to resume the crawl
                    if len(self.queue):
                        spider.log("Resuming crawl (%d requests scheduled)" % len(self.queue))

                def close(self, reason):
                    if not self.persist:
                        self.flush()

                def flush(self):
                    self.df.clear()
                    self.queue.clear()

                def enqueue_request(self, request):
                    if not request.dont_filter and self.df.request_seen(request):
                        self.df.log(request, self.spider)
                        return False
                    if self.stats:
                        self.stats.inc_value('scheduler/enqueued/redis', spider=self.spider)
                    self.queue.push(request)
                    return True

                def next_request(self):
                    block_pop_timeout = self.idle_before_close
                    request = self.queue.pop(block_pop_timeout)
                    if request and self.stats:
                        self.stats.inc_value('scheduler/dequeued/redis', spider=self.spider)
                    return request

                def has_pending_requests(self):
                    return len(self) > 0

                
            相关Queue源码:
                class Base(object):
                    """Per-spider base queue class"""

                    def __init__(self, server, spider, key, serializer=None):
                        """Initialize per-spider redis queue.

                        Parameters
                        ----------
                        server : StrictRedis
                            Redis client instance.
                        spider : Spider
                            Scrapy spider instance.
                        key: str
                            Redis key where to put and get messages.
                        serializer : object
                            Serializer object with ``loads`` and ``dumps`` methods.

                        """
                        if serializer is None:
                            # Backward compatibility.
                            # TODO: deprecate pickle.
                            serializer = picklecompat
                        if not hasattr(serializer, 'loads'):
                            raise TypeError("serializer does not implement 'loads' function: %r"
                                            % serializer)
                        if not hasattr(serializer, 'dumps'):
                            raise TypeError("serializer '%s' does not implement 'dumps' function: %r"
                                            % serializer)

                        self.server = server
                        self.spider = spider
                        self.key = key % {'spider': spider.name}
                        self.serializer = serializer

                    def _encode_request(self, request):
                        """Encode a request object"""
                        obj = request_to_dict(request, self.spider)
                        return self.serializer.dumps(obj)

                    def _decode_request(self, encoded_request):
                        """Decode an request previously encoded"""
                        obj = self.serializer.loads(encoded_request)
                        return request_from_dict(obj, self.spider)

                    def __len__(self):
                        """Return the length of the queue"""
                        raise NotImplementedError

                    def push(self, request):
                        """Push a request"""
                        raise NotImplementedError

                    def pop(self, timeout=0):
                        """Pop a request"""
                        raise NotImplementedError

                    def clear(self):
                        """Clear queue/stack"""
                        self.server.delete(self.key)


                class FifoQueue(Base):
                    """Per-spider FIFO queue"""

                    def __len__(self):
                        """Return the length of the queue"""
                        return self.server.llen(self.key)

                    def push(self, request):
                        """Push a request"""
                        self.server.lpush(self.key, self._encode_request(request))

                    def pop(self, timeout=0):
                        """Pop a request"""
                        if timeout > 0:
                            data = self.server.brpop(self.key, timeout)
                            if isinstance(data, tuple):
                                data = data[1]
                        else:
                            data = self.server.rpop(self.key)
                        if data:
                            return self._decode_request(data)


                class PriorityQueue(Base):
                    """Per-spider priority queue abstraction using redis' sorted set"""

                    def __len__(self):
                        """Return the length of the queue"""
                        return self.server.zcard(self.key)

                    def push(self, request):
                        """Push a request"""
                        data = self._encode_request(request)
                        score = -request.priority
                        # We don't use zadd method as the order of arguments change depending on
                        # whether the class is Redis or StrictRedis, and the option of using
                        # kwargs only accepts strings, not bytes.
                        self.server.execute_command('ZADD', self.key, score, data)

                    def pop(self, timeout=0):
                        """
                        Pop a request
                        timeout not support in this queue class
                        """
                        # use atomic range/remove using multi/exec
                        pipe = self.server.pipeline()
                        pipe.multi()
                        pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0)
                        results, count = pipe.execute()
                        if results:
                            return self._decode_request(results[0])


                class LifoQueue(Base):
                    """Per-spider LIFO queue."""

                    def __len__(self):
                        """Return the length of the stack"""
                        return self.server.llen(self.key)

                    def push(self, request):
                        """Push a request"""
                        self.server.lpush(self.key, self._encode_request(request))

                    def pop(self, timeout=0):
                        """Pop a request"""
                        if timeout > 0:
                            data = self.server.blpop(self.key, timeout)
                            if isinstance(data, tuple):
                                data = data[1]
                        else:
                            data = self.server.lpop(self.key)

                        if data:
                            return self._decode_request(data)


                # TODO: Deprecate the use of these names.
                SpiderQueue = FifoQueue
                SpiderStack = LifoQueue
                SpiderPriorityQueue = PriorityQueue

            爬虫爬取数据时存在层级和优先级:爬虫中间件实现
        
        
        
    使用scrapy-redis组件:
        情况一:只用它的去重规则功能
                
                配置:
                    # ############ 连接redis 信息 #################
                    REDIS_HOST = '127.0.0.1'                            # 主机名
                    REDIS_PORT = 6379                                   # 端口
                    # REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
                    REDIS_PARAMS  = {}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
                    # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # 指定连接Redis的Python模块  默认:redis.StrictRedis
                    REDIS_ENCODING = "utf-8"


                    # 自定义去重规则
                    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
        
        情况二:只用它的调度器
        
                # ############ 连接redis 信息 #################
                REDIS_HOST = '127.0.0.1'                            # 主机名
                REDIS_PORT = 6379                                   # 端口
                # REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
                REDIS_PARAMS  = {}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
                # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # 指定连接Redis的Python模块  默认:redis.StrictRedis
                REDIS_ENCODING = "utf-8"


                # 有引擎来执行:自定义调度器
                SCHEDULER = "scrapy_redis.scheduler.Scheduler"
                SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'  # 默认使用优先级队列(默认广度优先),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
                SCHEDULER_QUEUE_KEY = '%(spider)s:requests'  # 调度器中请求存放在redis中的key
                SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  # 对保存到redis中的数据进行序列化,默认使用pickle
                SCHEDULER_PERSIST = True  # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
                SCHEDULER_FLUSH_ON_START = False  # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
                # SCHEDULER_IDLE_BEFORE_CLOSE = 10  # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。
                SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'  # 去重规则,在redis中保存时对应的key  chouti:dupefilter
                SCHEDULER_DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'  # 去重规则对应处理的类
                #去重规则对应处理的类
                DUPEFILTER_DEBUG = False
                
        情况三:去重+调度去 
                # ############ 连接redis 信息 #################
                REDIS_HOST = '127.0.0.1'                            # 主机名
                REDIS_PORT = 6379                                   # 端口
                # REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
                REDIS_PARAMS  = {}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
                # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # 指定连接Redis的Python模块  默认:redis.StrictRedis
                REDIS_ENCODING = "utf-8"


                # 有引擎来执行:自定义调度器
                SCHEDULER = "scrapy_redis.scheduler.Scheduler"
                SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'  # 默认使用优先级队列(默认广度优先),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
                SCHEDULER_QUEUE_KEY = '%(spider)s:requests'  # 调度器中请求存放在redis中的key
                SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  # 对保存到redis中的数据进行序列化,默认使用pickle
                SCHEDULER_PERSIST = True  # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
                SCHEDULER_FLUSH_ON_START = False  # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
                # SCHEDULER_IDLE_BEFORE_CLOSE = 10  # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。
                SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'  # 去重规则,在redis中保存时对应的key  chouti:dupefilter
                SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'  # 去重规则对应处理的类
                DUPEFILTER_DEBUG = False
                        
        情况四:使用scrapy-redis内置的pipeline做持久化:就是将item对象保存到redis的列表中。
                
                配置:
                    # ############ 连接redis 信息 #################
                    REDIS_HOST = '127.0.0.1'                            # 主机名
                    REDIS_PORT = 6379                                   # 端口
                    # REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
                    REDIS_PARAMS  = {}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
                    # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # 指定连接Redis的Python模块  默认:redis.StrictRedis
                    REDIS_ENCODING = "utf-8"
                    ITEM_PIPELINES = {
                       'scrapy_redis.pipelines.RedisPipeline': 300,
                    }
                以上功能全部应用的配置:
                    # ############ 连接redis 信息 #################
                    REDIS_HOST = '127.0.0.1'                            # 主机名
                    REDIS_PORT = 6379                                   # 端口
                    # REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
                    REDIS_PARAMS  = {}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
                    # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # 指定连接Redis的Python模块  默认:redis.StrictRedis
                    REDIS_ENCODING = "utf-8"

                    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

                    # 有引擎来执行:自定义调度器
                    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
                    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'  # 默认使用优先级队列(默认广度优先),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
                    SCHEDULER_QUEUE_KEY = '%(spider)s:requests'  # 调度器中请求存放在redis中的key
                    SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  # 对保存到redis中的数据进行序列化,默认使用pickle
                    SCHEDULER_PERSIST = True  # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
                    SCHEDULER_FLUSH_ON_START = False  # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
                    # SCHEDULER_IDLE_BEFORE_CLOSE = 10  # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。
                    SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'  # 去重规则,在redis中保存时对应的key  chouti:dupefilter
                    SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'  # 去重规则对应处理的类
                    DUPEFILTER_DEBUG = False


                    # 深度和优先级相关
                    DEPTH_PRIORITY = 1
            
        
        情况五:让scrapy-redis的起始URL不再通过start_reuqests执行,而是去redis中获取。
        
            配置:
                REDIS_START_URLS_BATCH_SIZE = 1
                # REDIS_START_URLS_AS_SET = True # 把起始url放到redis的集合
                REDIS_START_URLS_AS_SET = False # 把起始url放到redis的列表
            
            爬虫:
                from scrapy_redis.spiders import RedisSpider
                from scrapy.http import Request
                from ..items import WenwenItem

                class ChoutiSpider(RedisSpider):
                    name = 'chouti'
                    allowed_domains = ['chouti.com']

                    def parse(self, response):
                        # 随着深度的增加、优先级一直在递减
                        print(response)
    
    
            放置起始URL:
                import redis

                conn = redis.Redis(host='127.0.0.1',port=6379)

                # 起始url的Key: chouti:start_urls
                conn.lpush("chouti:start_urls",'https://dig.chouti.com/r/ask/hot/12')
day139

 

day138

2s8day138 scrapy框架

内容回顾:
    面试题:
        1. 引擎
        2. 设计表:FK、M2M 
        3. 操作表:
            select id from tb 
            select id from tb where id > 2
            select id from tb where id in (select id from tb)
            
            select 部门ID,max(id) from 用户表 group 部门ID
            select 部门ID,max(id) from 用户表 group 部门ID having count(id)>3
    
            select * from tb1 left join tb2 on tb1.xx = tb2.xx  # tb1所有都显示,如果tb2中未找到对应项则显示null
            select * from tb1 inner join tb2 on tb1.xx = tb2.xx # null行全部去掉
            
            select 
                id,
                1,
                (select max(id) from tb ) as b
            from 
                tb 
        4. 触发器、函数、存储过程、视图
            触发器:在对表进行 增删改 前后进行自定义一些SQL操作。
              函数:
                    select max(id),xx(name) from tb 
                    select sleep(2)
                    
                    - 聚合 
                    - 字符串
                    - 时间 
          存储过程:在数据库中定义一些操作SQL语句,以后在代码中通过名称即可调用。
          
                    问题:存储过程和函数的区别?
                          函数:
                            - 参数
                            - 返回值: return 
                          存储过程:
                            - 参数
                            - 返回值:out/inout 
                            - 返回结果集:select * from tb;
              视图:虚拟表->通过sql帮助我们实时查询的数据。
        
        5. 索引:5+2 
        
        6.其他:
            慢日志
            执行计划
        
        7.优化:
            - 命中索引
            - 不用select * 
            - 固定长度放在前面
            - 读写分离
            - 分库
            - 分表
                - 水平
                - 垂直 
                
            - 查询单条数据:limit 1
            - 缓存 
            - 分页 
            
    
    scrapy: 
        第一步:
            scrapy startproject xxxx 
            cd xxxx 
            scrapy genspider chouti chouti.com 
            
            # 编写代码
            
            scrapy crawl chouti --nolog 
        
        第二步:start_requests
            
        第三步:parse函数
            - 解析器
            - yield item 
            - yield Request()
            
        第四步:携带cookie
            - 手动
            - 自动
            
        第五步:去重规则
        
        
        第六步:下载中间件 

          

今日内容:
    1. 下载中间件
    2. 爬虫中间件
    3. 扩展:信号
    4. 其他
    5. 自定义命令
    6. 自定义scrapy框架
    7. scrapy-redis组件
    
内容详细:
    1. 下载中间件
        问题:scrapy中如何添加代理?
        解决方案:
            方式一:内置添加代理功能
                # -*- coding: utf-8 -*-
                import os
                import scrapy
                from scrapy.http import Request

                class ChoutiSpider(scrapy.Spider):
                    name = 'chouti'
                    allowed_domains = ['chouti.com']
                    start_urls = ['https://dig.chouti.com/']

                    def start_requests(self):
                        os.environ['HTTP_PROXY'] = "http://192.168.11.11"

                        for url in self.start_urls:
                            yield Request(url=url,callback=self.parse)

                    def parse(self, response):
                        print(response)

            方式二:自定义下载中间件
                import random
                import base64
                import six
                def to_bytes(text, encoding=None, errors='strict'):
                    """Return the binary representation of `text`. If `text`
                    is already a bytes object, return it as-is."""
                    if isinstance(text, bytes):
                        return text
                    if not isinstance(text, six.string_types):
                        raise TypeError('to_bytes must receive a unicode, str or bytes '
                                        'object, got %s' % type(text).__name__)
                    if encoding is None:
                        encoding = 'utf-8'
                    return text.encode(encoding, errors)
                    
                class MyProxyDownloaderMiddleware(object):
                    def process_request(self, request, spider):
                        proxy_list = [
                            {'ip_port': '111.11.228.75:80', 'user_pass': 'xxx:123'},
                            {'ip_port': '120.198.243.22:80', 'user_pass': ''},
                            {'ip_port': '111.8.60.9:8123', 'user_pass': ''},
                            {'ip_port': '101.71.27.120:80', 'user_pass': ''},
                            {'ip_port': '122.96.59.104:80', 'user_pass': ''},
                            {'ip_port': '122.224.249.122:8088', 'user_pass': ''},
                        ]
                        proxy = random.choice(proxy_list)
                        if proxy['user_pass'] is not None:
                            request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
                            encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))
                            request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)
                        else:
                            request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
    
    
    
                配置:
                    DOWNLOADER_MIDDLEWARES = {
                       # 'xiaohan.middlewares.MyProxyDownloaderMiddleware': 543,
                    }
                        
                        
        问题:scrapy中如何处理https
            掏钱:
                pass 
            不掏钱:
                from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
                from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate)

                class MySSLFactory(ScrapyClientContextFactory):
                    def getCertificateOptions(self):
                        from OpenSSL import crypto
                        v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.key.unsecure', mode='r').read())
                        v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read())
                        return CertificateOptions(
                            privateKey=v1,  # pKey对象
                            certificate=v2,  # X509对象
                            verify=False,
                            method=getattr(self, 'method', getattr(self, '_ssl_method', None))
                        )
    
                配置: 
                    DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
                    DOWNLOADER_CLIENTCONTEXTFACTORY = "xiaohan.middlewares.MySSLFactory"
            
    
        总结:
            问:下载中间件的作用?
            答:在每次下载前和下载后对请求和响应可以定制功能。例如:user-agent/代理/cookie
    
    2. 爬虫中间件 
        编写:
            middlewares.py
                class XiaohanSpiderMiddleware(object):
                    # Not all methods need to be defined. If a method is not defined,
                    # scrapy acts as if the spider middleware does not modify the
                    # passed objects.
                    def __init__(self):
                        pass
                    @classmethod
                    def from_crawler(cls, crawler):
                        # This method is used by Scrapy to create your spiders.
                        s = cls()
                        return s

                    # 每次下载完成之后,未执行parse函数之前。
                    def process_spider_input(self, response, spider):
                        # Called for each response that goes through the spider
                        # middleware and into the spider.

                        # Should return None or raise an exception.
                        print('process_spider_input',response)
                        return None

                    def process_spider_output(self, response, result, spider):
                        # Called with the results returned from the Spider, after
                        # it has processed the response.

                        # Must return an iterable of Request, dict or Item objects.
                        print('process_spider_output',response)
                        for i in result:
                            yield i

                    def process_spider_exception(self, response, exception, spider):
                        # Called when a spider or process_spider_input() method
                        # (from other spider middleware) raises an exception.

                        # Should return either None or an iterable of Response, dict
                        # or Item objects.
                        pass

                    # 爬虫启动时,第一次执行start_requests时,触发。(只执行一次)
                    def process_start_requests(self, start_requests, spider):
                        # Called with the start requests of the spider, and works
                        # similarly to the process_spider_output() method, except
                        # that it doesn’t have a response associated.

                        # Must return only requests (not items).

                        print('process_start_requests')
                        for r in start_requests:
                            yield r

        应用:
            SPIDER_MIDDLEWARES = {
               'xiaohan.middlewares.XiaohanSpiderMiddleware': 543,
            }
                
    3. 扩展:信号 
        单纯扩展:
            extends.py 
                class MyExtension(object):
                    def __init__(self):
                        pass

                    @classmethod
                    def from_crawler(cls, crawler):
                        obj = cls()
                        return obj
            配置:
                EXTENSIONS = {
                    'xiaohan.extends.MyExtension':500,
                }
        
        扩展+信号:
            extends.py 
                from scrapy import signals


                class MyExtension(object):
                    def __init__(self):
                        pass

                    @classmethod
                    def from_crawler(cls, crawler):
                        obj = cls()
                        # 在爬虫打开时,触发spider_opened信号相关的所有函数:xxxxxxxxxxx
                        crawler.signals.connect(obj.xxxxxxxxxxx1, signal=signals.spider_opened)
                        # 在爬虫关闭时,触发spider_closed信号相关的所有函数:xxxxxxxxxxx
                        crawler.signals.connect(obj.uuuuuuuuuu, signal=signals.spider_closed)
                        return obj

                    def xxxxxxxxxxx1(self, spider):
                        print('open')

                    def uuuuuuuuuu(self, spider):
                        print('close')
        
                            return obj
            配置:
            EXTENSIONS = {
                'xiaohan.extends.MyExtension':500,
            }

    4. 其他:配置文件 
        
    5. 自定义命令 
        from scrapy.commands import ScrapyCommand
        from scrapy.utils.project import get_project_settings


        class Command(ScrapyCommand):
            requires_project = True

            def syntax(self):
                return '[options]'

            def short_desc(self):
                return 'Runs all of the spiders'

            def run(self, args, opts):
                spider_list = self.crawler_process.spiders.list()
                for name in spider_list:
                    self.crawler_process.crawl(name, **opts.__dict__)
                self.crawler_process.start()
                
                
            PS:源码
                def run(self, args, opts):
                    from scrapy.crawler import CrawlerProcess
                    CrawlerProcess.crawl
                    CrawlerProcess.start
                    """
                    self.crawler_process对象中含有:_active = {d,}
                    """
                    self.crawler_process.crawl('chouti',**opts.__dict__)
                    self.crawler_process.crawl('cnblogs',**opts.__dict__)
                    #
                    self.crawler_process.start()
            分享:源码 
        
        
    总结:
        下载中间件(*****)
        爬虫中间件(***)
        扩展:信号(***)
        配置(*****)
        自定义命令(*****)
day138-scrapy

 

day136

s8day136

内容回顾:
    1. 面试题部分
        - 引擎
        - 设计表
            - FK
            - M2M
        - 基本操作
            --- 存储过程、视图、函数、触发器
            - 触发器,在数据库某个表进行‘增删改’前后定义一些操作。
            - 函数,在SQL语句中使用函数。
                    - select sleep(5)
                    - select date_format(ctime,'%m') from tb;
            - 存储过程,将SQL语句保存到数据库中,并命名;以后在代码中调用时,直接发送名称。
                参数:
                    - in 
                    - out
                    - inout 
                代码:
                    #!/usr/bin/env python
                    # -*- coding:utf-8 -*-
                    import pymysql

                    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
                    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
                    # 执行存储过程
                    cursor.callproc('p1', args=(1, 22, 3, 4))
                    # 获取执行完存储的参数
                    cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
                    result = cursor.fetchall()

                    conn.commit()
                    cursor.close()
                    conn.close()


                    print(result)
            
            - 视图,对某些表进行SQL查询,将结果显示实时显示出来(只能查)
                    v = select * from tb where id <1000
                    
                    select * from v 
                    
                    select * from (select * from tb where id <1000) as v 
                    
            PS: 都是保存在数据库
        
        - 索引 
            
        - 命中索引
            
        - 优化 
            - 不用 select * 
            - 固定长度字段列,往前放
            - char和varchar
            - 固定数据放入内存:choice 
            - 读写分离,利用数据库的主从进行分离:主,用于删除、修改更新;从,查。
            - 分库,当数据库中表太多,将表分到不同的数据库;例如:1w张表
            - 分表,
                - 水平分表,将某些列拆分到另外一张表;例如:博客+博客详细
                - 垂直分表,将历史信息分到另外一张表中;例如:账单
            - 缓存:利用redis、memcache
        - 其它:
            - 慢日志
            - 执行计划 
            
        - 分页 
        
        补充:
            text类型,创建索引;
        
    2. 高性能相关
        - 通过异步非阻塞模块实现。
        
        - 常见单线程异步非阻塞模块实现:
            - asyncio
            - gevent
            - twisted (scrapy内部依赖)
        
        - 什么是异步非阻塞?
        
        - IO多路复用
            
    3. scrapy框架 
        
        基本命令:
            scrapy startproject sp1 
            cd sp1
            scrapy genspider chouti chouti.com 
            
            scrapy crawl chouti --nolog 
            
        编写:
            a. 起始URL
            
            b. parse函数
            
            c. pipelines 
            
            d. 解析器
            
        
今日内容:
    1. start_requests
    
    2. 数据解析
    
    3. pipelines
    
    4. POST/请求头/Cookie
    
    5. 去重规则
    
    6. 下载中间件/爬虫中间件
    
    7. 扩展 
    
    8. 自定义命令
    
    
    
内容详细:
    1. start_requests
    
        def start_requests(self):
            for url in self.start_urls:
                yield Request(url=url,callback=self.parse2)
    
        def start_requests(self):
            req_list = []
            for url in self.start_urls:
                req_list.append(Request(url=url,callback=self.parse2))
            return req_list
        
        因为scrapy内部会将返回值转换成迭代器。
        
    2. 解析器 
        将字符串转换成对象:
            - 方式一:
                response.xpath('//div[@id='content-list']/div[@class='item']')
            - 方式二:
                hxs = HtmlXPathSelector(response=response)
                items = hxs.xpath("//div[@id='content-list']/div[@class='item']")
        查找规则:
            //a
            //div/a
            //a[re:test(@id, "i\d+")]            
            
            items = hxs.xpath("//div[@id='content-list']/div[@class='item']")
            for item in items:
                item.xpath('.//div')
        
        解析:
            标签对象:xpath('/html/body/ul/li/a/@href')
            列表:    xpath('/html/body/ul/li/a/@href').extract()
            值:      xpath('//body/ul/li/a/@href').extract_first()
        
    
        PS: 
            单独应用
                from scrapy.selector import Selector, HtmlXPathSelector
                from scrapy.http import HtmlResponse
                html = """<!DOCTYPE html>
                <html>
                    <head lang="en">
                        <meta charset="UTF-8">
                        <title></title>
                    </head>
                    <body>
                        <ul>
                            <li class="item-"><a id='i1' href="link.html">first item</a></li>
                            <li class="item-0"><a id='i2' href="llink.html">first item</a></li>
                            <li class="item-1"><a href="llink2.html">second item<span>vv</span></a></li>
                        </ul>
                        <div><a href="llink2.html">second item</a></div>
                    </body>
                </html>
                """
                response = HtmlResponse(url='http://example.com', body=html,encoding='utf-8')
                obj = response.xpath('//a[@id="i1"]/text()').extract_first()
                print(obj)
            
            chrome
                xpath 
                
            
    3. pipelines 
    
        - pipelines基础 
            
            class FilePipeline(object):

                def process_item(self, item, spider):
                    print('写入文件',item['href'])

                    return item

                def open_spider(self, spider):
                    """
                    爬虫开始执行时,调用
                    :param spider:
                    :return:
                    """
                    print('打开文件')

                def close_spider(self, spider):
                    """
                    爬虫关闭时,被调用
                    :param spider:
                    :return:
                    """
                    print('关闭文件')
        
        - 多pipelines(值越小优先级越高)
    
    
        - 多pipelines,返回值会传递给下一个pipelines的process_item

        
            PS:如果想要丢弃,不给后续pipeline使用:
                from scrapy.exceptions import DropItem
                class FilePipeline(object):

                    def process_item(self, item, spider):
                        print('写入文件',item['href'])

                        # return item
                        raise DropItem()
        - 根据配置文件读取相关值,再进行pipeline处理
            
            class FilePipeline(object):
                def __init__(self,path):
                    self.path = path
                    self.f = None

                @classmethod
                def from_crawler(cls, crawler):
                    """
                    初始化时候,用于创建pipeline对象
                    :param crawler:
                    :return:
                    """
                    path = crawler.settings.get('XL_FILE_PATH')
                    return cls(path)

                def process_item(self, item, spider):
                    self.f.write(item['href']+'\n')
                    return item

                def open_spider(self, spider):
                    """
                    爬虫开始执行时,调用
                    :param spider:
                    :return:
                    """
                    self.f = open(self.path,'w')

                def close_spider(self, spider):
                    """
                    爬虫关闭时,被调用
                    :param spider:
                    :return:
                    """
                    self.f.close()


    4. POST/请求头/Cookie
        自动登录抽屉+点赞
        
        POST+请求头: 
            from scrapy.http import Request 
            req = Request(
                url='http://dig.chouti.com/login',
                method='POST',
                body='phone=8613121758648&password=woshiniba&oneMonth=1',
                headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
                cookies={},
                callback=self.parse_check_login,
            )
        
        cookies:
            手动:
                cookie_dict = {}
                cookie_jar = CookieJar()
                cookie_jar.extract_cookies(response, response.request)
                for k, v in cookie_jar._cookies.items():
                    for i, j in v.items():
                        for m, n in j.items():
                            cookie_dict[m] = n.value
                            
                req = Request(
                    url='http://dig.chouti.com/login',
                    method='POST',
                    headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
                    body='phone=8615131255089&password=pppppppp&oneMonth=1',
                    cookies=cookie_dict, # 手动携带
                    callback=self.check_login
                )
                yield req
            
            自动:
                class ChoutiSpider(scrapy.Spider):
                    name = 'chouti'
                    allowed_domains = ['chouti.com']
                    start_urls = ['http://dig.chouti.com/',]

                    def start_requests(self):
                        for url in self.start_urls:
                            yield Request(url=url,callback=self.parse_index,meta={'cookiejar':True})

                    def parse_index(self,response):
                        req = Request(
                            url='http://dig.chouti.com/login',
                            method='POST',
                            headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
                            body='phone=8613121758648&password=woshiniba&oneMonth=1',
                            callback=self.parse_check_login,
                            meta={'cookiejar': True}
                        )
                        yield req

                    def parse_check_login(self,response):
                        # print(response.text)
                        yield Request(
                            url='https://dig.chouti.com/link/vote?linksId=19440976',
                            method='POST',
                            callback=self.parse_show_result,
                            meta={'cookiejar': True}
                        )

                    def parse_show_result(self,response):
                        print(response.text)
    
        配置文件制定是否允许操作cookie:
            # Disable cookies (enabled by default)
            # COOKIES_ENABLED = False
    
    5. 去重规则
    
        配置:
            DUPEFILTER_CLASS = 'xianglong.dupe.MyDupeFilter'
        
        编写类:
            class MyDupeFilter(BaseDupeFilter):

                def __init__(self):
                    self.record = set()

                @classmethod
                def from_settings(cls, settings):
                    return cls()

                def request_seen(self, request):
                    if request.url in self.record:
                        print('已经访问过了', request.url)
                        return True
                    self.record.add(request.url)

                def open(self):  # can return deferred
                    pass

                def close(self, reason):  # can return a deferred
                    pass
        
        问题:为请求创建唯一标识
        
            
            http://www.oldboyedu.com?id=1&age=2
            http://www.oldboyedu.com?age=2&id=1
            
            from scrapy.utils.request import request_fingerprint
            from scrapy.http import Request


            u1 = Request(url='http://www.oldboyedu.com?id=1&age=2')
            u2 = Request(url='http://www.oldboyedu.com?age=2&id=1')

            result1 = request_fingerprint(u1)
            result2 = request_fingerprint(u2)
            print(result1,result2)
        
        问题:记录到低要不要放在数据库?【使用redis集合存储】
            访问记录可以放在redis中。
    
        
        补充:dont_filter到低在哪里?
            from scrapy.core.scheduler import Scheduler
            
             def enqueue_request(self, request):
                # request.dont_filter=False
                    # self.df.request_seen(request):
                    #   - True,已经访问
                    #   - False,未访问
                # request.dont_filter=True,全部加入到调度器
                if not request.dont_filter and self.df.request_seen(request):
                    self.df.log(request, self.spider)
                    return False
                # 如果往下走,把请求加入调度器
                dqok = self._dqpush(request)
        
    
    6. 中间件 
        问题:对爬虫中所有请求发送时,携带请求头?
            方案一:在每个Request对象中添加一个请求头
            
            方案二:下载中间件
                配置: 
                
                    DOWNLOADER_MIDDLEWARES = {
                       'xianglong.middlewares.UserAgentDownloaderMiddleware': 543,
                    }
                编写类:
                    
                    class UserAgentDownloaderMiddleware(object):

                        @classmethod
                        def from_crawler(cls, crawler):
                            # This method is used by Scrapy to create your spiders.
                            s = cls()
                            return s

                        def process_request(self, request, spider):
                            # Called for each request that goes through the downloader
                            # middleware.

                            # Must either:
                            # - return None: continue processing this request
                            # - or return a Response object
                            # - or return a Request object
                            # - or raise IgnoreRequest: process_exception() methods of
                            #   installed downloader middleware will be called

                            request.headers['User-Agent'] = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"

                            # return None # 继续执行后续的中间件的process_request

                            # from scrapy.http import Request
                            # return Request(url='www.baidu.com') # 重新放入调度器中,当前请求不再继续处理

                            # from scrapy.http import HtmlResponse # 执行从最后一个开始执行所有的process_response
                            # return HtmlResponse(url='www.baidu.com',body=b'asdfuowjelrjaspdoifualskdjf;lajsdf')

                        def process_response(self, request, response, spider):
                            # Called with the response returned from the downloader.

                            # Must either;
                            # - return a Response object
                            # - return a Request object
                            # - or raise IgnoreRequest
                            return response

                        def process_exception(self, request, exception, spider):
                            # Called when a download handler or a process_request()
                            # (from other downloader middleware) raises an exception.

                            # Must either:
                            # - return None: continue processing this exception
                            # - return a Response object: stops process_exception() chain
                            # - return a Request object: stops process_exception() chain
                            pass

            
            方案三:内置下载中间件
                配置文件:
                    USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        
            
    总结:
        
        1. 存储过程、触发器函数等作用?
        
        2. 优化补充:
                - 读写分离,利用数据库的主从进行分离:主,用于删除、修改更新;从,查。
                    原生SQL: 
                        select * from db.tb 
                    ORM:
                        model.User.objects.all().using("default")
                        
                    PS: 路由 db router 
                
                - 分库,当数据库中表太多,将表分到不同的数据库;例如:1w张表
                - 分表
                - 缓存:利用redis、memcache
        
        3. 创建索引 
            text列如果想要创建索引,必须执行长度。
            
        4. start_requests 
            - 可迭代对象
            - 生成器 
            
        5. pipelines 
            - 配置 
                ITEM_PIPELINES = {
                   'xianglong.pipelines.FilePipeline': 300,
                }
            
            - 写类
                class FilePipeline(object):
                    def __init__(self,path):pass

                    @classmethod
                    def from_crawler(cls, crawler):
                        pass

                    def process_item(self, item, spider):
                        pass
                        return item 

                    def open_spider(self, spider):
                        pass

                    def close_spider(self, spider):
                        pass
        
        6. 去重 
            - 配置 
                DUPEFILTER_CLASS = 'xianglong.dupe.MyDupeFilter'
            - 写类 
                class MyDupeFilter(BaseDupeFilter):

                    def __init__(self):
                        pass

                    @classmethod
                    def from_settings(cls, settings):
                        pass

                    def request_seen(self, request):
                        pass

                    def open(self):  # can return deferred
                        pass

                    def close(self, reason):  # can return a deferred
                        pass

        7. 下载中间件 
            - 配置 
                DOWNLOADER_MIDDLEWARES = {
                   'xianglong.middlewares.UserAgentDownloaderMiddleware': 543,
                }
                        
            -class UserAgentDownloaderMiddleware(object):

                    @classmethod
                    def from_crawler(cls, crawler):
                        pass

                    def process_request(self, request, spider):
                        pass

                    def process_response(self, request, response, spider):
                        pass

                    def process_exception(self, request, exception, spider):
                        pass

        8. POST/请求头/Cookie 
day136

 

posted @ 2018-06-01 14:55  Coufusion  阅读(278)  评论(0)    收藏  举报