-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
537 lines (537 loc) · 199 KB
/
search.xml
File metadata and controls
537 lines (537 loc) · 199 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[NFS 设置]]></title>
<url>%2F2019%2F01%2Fnfs.html</url>
<content type="text"><![CDATA[servercentos12345# installsudo yum install -y nfs-utils# 开机启动sudo systemctl start rpcbindsudo systemctl start nfs 防火墙需要打开 rpc-bind 和 nfs 的服务12345678$ sudo firewall-cmd --zone=public --permanent --add-service=rpc-bindsuccess$ sudo firewall-cmd --zone=public --permanent --add-service=mountdsuccess$ sudo firewall-cmd --zone=public --permanent --add-service=nfssuccess$ sudo firewall-cmd --reloadsuccess 配置共享目录服务启动之后,我们在服务端配置一个共享目录 12sudo mkdir /datasudo chmod 755 /data 根据这个目录,相应配置导出目录123sudo vi /etc/exports# 添加以下配置/data/ 192.168.0.0/24(rw,sync,no_root_squash,no_all_squash) /data: 共享目录位置。 192.168.0.0/24: 客户端 IP 范围,* 代表所有,即没有限制。 rw: 权限设置,可读可写。 sync: 同步共享目录。 no_root_squash: 可以使用 root 授权。 no_all_squash: 可以使用普通用户授权。 重启服务123sudo systemctl restart nfs# nfs 是否开启成功expoerfs 检查本地共享目录 123$ showmount -e localhostExport list for localhost:/sound 172.17.4.0/24 CentOS 7 下 yum 安装和配置 NFS clientraspberry123456789101112# 安装 nfs sudo apt-get install nfs-utils -y# 挂载sudo mount -t nfs ip:/path /localpath# 配置自动挂载sudo vim /etc/fstab# /etc/fstab 最后一行追加172.17.4.100:/sound /sound nfs rw# 修改 fstab 重新加载 systemctlsudo systemctl daemon-reload# 查看一下mount]]></content>
<tags>
<tag>nfs</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Dockerfile 设置镜像时区]]></title>
<url>%2F2019%2F01%2Fdockerfile-tz.html</url>
<content type="text"><![CDATA[ubuntu1RUN echo "Asia/Shanghai" > /etc/timezone RUN dpkg-reconfigure -f noninteractive tzdata 运行中的 container如果你不想新建镜像或者重启 container,那么也可以直接进入 container 修改。执行:12docker exec -it <CONTAINER NAME> bash``` 进入 container 之后执行: ```bash echo "Asia/Shanghai" > /etc/timezone dpkg-reconfigure -f noninteractive tzdata Docker 中如何设置 container 的时区 alpine12RUN apk add -U tzdataRUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 参考源代码123456789101112131415docker run -it --rm alpine /bin/sh/ # dateSun May 8 20:46:18 UTC 2016/ # apk add -U tzdatafetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gzfetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz(1/1) Installing tzdata (2015g-r0)Executing busybox-1.24.1-r7.triggerOK: 8 MiB in 12 packages/ # cp /usr/share/zoneinfo/Europe/Berlin /etc/localtime/ # dateSun May 8 22:46:45 CEST 2016/ # As a side docker 下 alpine 镜像设置时区的有效办法]]></content>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[gitlab-runner]]></title>
<url>%2F2018%2F12%2Fgitlab-runner.html</url>
<content type="text"><![CDATA[https://docs.gitlab.com/runner/install/docker.html]]></content>
</entry>
<entry>
<title><![CDATA[电信烽火光猫 HG2821T-U]]></title>
<url>%2F2018%2F12%2FHG2821T-U.html</url>
<content type="text"><![CDATA[USER: telecomadmin useradmin useradmin 的密码在路由器的背面[默认终端配置密码]telecomadmin 密码需要解密,密文在光猫参数API有。 相关链接: 后台登录页 光猫参数API telecomadmin 密码解密页面底部。 https://www.right.com.cn/forum/thread-327733-1-1.html LEDEvi /etc/config/network/etc/init.d/network restartip addr show eth0]]></content>
</entry>
<entry>
<title><![CDATA[ab 压力测试]]></title>
<url>%2F2018%2F12%2Fab-test.html</url>
<content type="text"><![CDATA[1ab -n 10000 -c 1000 http://localhost:8888/test Centos1yum -y install httpd-tools UbuntuMAC123httpd -vsudo apachectl stopsudo apachectl restart Windownload 12# 解压并在当前目录运行下面的命令httpd -k install]]></content>
<tags>
<tag>ab</tag>
</tags>
</entry>
<entry>
<title><![CDATA[awk 入门教程]]></title>
<url>%2F2018%2F11%2Flinux-awk.html</url>
<content type="text"><![CDATA[awk 入门教程 (阮一峰)]]></content>
<tags>
<tag>linux</tag>
<tag>awk</tag>
</tags>
</entry>
<entry>
<title><![CDATA[nginx 配置]]></title>
<url>%2F2018%2F10%2Fnginx-example.html</url>
<content type="text"><![CDATA[负载均衡123docker run -d -p 7001:80 --name nginx1 nginxdocker run -d -p 7002:80 --name nginx2 nginxdocker run -d -p 7003:80 --name nginx3 nginx 1echo '<h1>This is 1.</h1>' > /usr/share/nginx/html/index.html 1echo '<h1>This is 2.</h1>' > /usr/share/nginx/html/index.html 1echo '<h1>This is 3.</h1>' > /usr/share/nginx/html/index.html 12345upstream web{ server 172.17.7.163:7001; server 172.17.7.163:7002; server 172.17.7.163:7003;}]]></content>
<tags>
<tag>nginx</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Software list]]></title>
<url>%2F2018%2F10%2Fsoftware-list.html</url>
<content type="text"><![CDATA[苹果旧版本APP下载]]></content>
</entry>
<entry>
<title><![CDATA[ESXI 虚拟机安装 VMware Tools]]></title>
<url>%2F2018%2F09%2Fesxi-vmtools.html</url>
<content type="text"><![CDATA[CentOS先挂载 12345678910111213mkdir /mnt/cdromcp /mnt/cdrom/VMWarexxx.tar.gz /tmpcd /tmptar zxf VMWarexxx.tar.gzcd vmware-tools-disxx./vmware-install.plyum install -y perlyum install -y gccyum install -y kernel# 一路回车./vmware-install.plshutdown -r now CentOS 命令行安装 VMware Tools]]></content>
<tags>
<tag>esxi</tag>
</tags>
</entry>
<entry>
<title><![CDATA[docker 常用]]></title>
<url>%2F2018%2F09%2Fdocker-basic.html</url>
<content type="text"><![CDATA[基本设置12345678# 启动 Dockersudo systemctl start docker# 开机启动 Dockersystemctl enable docker# 立即重启shutdown -r now# 查看 Docker 状态systemctl status docker 操作 Docker 删除所有无名称的镜像(悬空镜像) 123docker rmi $(docker images -f "dangling=true" -q)# ordocker image prune -a -f 进入容器1docker exec -it CONTAINER_ID /bin/bash nsenter123456wget https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.33/util-linux-2.33-rc1.tar.gztar -xzvf util-linux-2.33-rc1.tar.gzcd util-linux-2.33-rc1/./configure --without-ncursesmake nsentercp nsenter /usr/local/bin 1234# 查看 CONTAINER_IDdocker ps -adocker inspect -f {{.State.Pid}} CONTAINER_IDnsenter --target 31611 --mount --uts --ipc --net --pid 查看日志12docker logs -f -t --tail 行数 容器名docker logs -f -t --tail 10 3da 免 sudo123sudo gpasswd -a ${USER} dockercat /etc/group | grep ^dockersudo serivce docker restart 问题IPv4 forwarding is disabled. Networking will not work.12echo 'net.ipv4.ip_forward=1' >> /usr/lib/sysctl.d/00-system.confsystemctl restart network && systemctl restart docker]]></content>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[通过 python 设置树莓派的音量]]></title>
<url>%2F2018%2F09%2Fraspberry-volume.html</url>
<content type="text"><![CDATA[15年写的的声音自增脚本,那时候发现这个问题但是没有解决掉。 123456789#!/bin/bashamixer set PCM 70% < /dev/null > /dev/null 2>&1 &declare i#loopsfor ((i=0; i<=18; i=i+1)) do amixer set PCM 100+ sleep 1done 这里不理解的可以看看参考资料的链接 1234567#!/usr/bin/env python # -* coding: utf-8 -*import subprocessimport mathdef set_volume(volume): """ 设置音量 Limits: Playback -10239 - 400 """ new_volume = 24.979 * math.log(int(volume)) - 14.581 cmd = 'sudo amixer set PCM {}%'.format(new_volume) subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 参考资料 树莓派通过命令行调音量百分比不正确的问]]></content>
<tags>
<tag>raspberry</tag>
</tags>
</entry>
<entry>
<title><![CDATA[LinkIt Smart 7688 初体验]]></title>
<url>%2F2018%2F09%2FLinkIt-Smart-7688.html</url>
<content type="text"><![CDATA[开机连接无线网 浏览器地址栏输入:192.168.100.1设定密码 ssh 登录配置完Wifi后连接76881234567891011121314151617BusyBox v1.23.2 (2015-11-18 16:34:33 CET) built-in shell (ash) _______ ________ __ | |.-----.-----.-----.| | | |.----.| |_ | - || _ | -__| || | | || _|| _| |_______|| __|_____|__|__||________||__| |____| |__| W I R E L E S S F R E E D O M ----------------------------------------------------- CHAOS CALMER (15.05+linkit, r47501) ----------------------------------------------------- * 1 1/2 oz Gin Shake with a glassful * 1/4 oz Triple Sec of broken ice and pour * 3/4 oz Lime Juice unstrained into a goblet. * 1 1/2 oz Orange Juice * 1 tsp. Grenadine Syrup -----------------------------------------------------root@mylinkit:~# 12345# ssh 7688ssh root@mylinkit.local# 关于声音madplay -o wav:- 1.wav | aplayaplay -Mq 1.wav 配置要连接Wifi 选择Station模式 选择无线网后输入密码 openwrt 界面 Wifi1root@mylinkit:~# cat /etc/config/wireless config wifi-device 'radio0' option type 'ralink' option variant 'mt7628' option country 'TW' option hwmode '11g' option htmode 'HT40' option channel 'auto' option disabled '0' config wifi-iface 'ap' option device 'radio0' option mode 'ap' option network 'lan' option ifname 'ra0' option encryption 'none' option ssid 'LinkIt_Smart_7688_23D00C' option seq '1' config wifi-iface 'sta' option device 'radio0' option mode 'sta' option network 'wan' option ifname 'apcli0' option led 'mediatek:orange:wifi' option disabled '0' option ssid 'WTMG659_CD_Users' option key 'WTMG_1357' option encryption 'psk2']]></content>
<tags>
<tag>openwrt</tag>
</tags>
</entry>
<entry>
<title><![CDATA[再遇 raspberry]]></title>
<url>%2F2018%2F08%2Fraspberry.html</url>
<content type="text"><![CDATA[123456789101112# 默认帐号密码- pi- raspberry# 树莓派设置sudo raspi-config# 调整音量alsamixer# 启用root账户sudo passwd rootsudo passwd --unlock root# 关机sudo halt 开启 ssh编辑 /etc/ssh/sshd_config文件1sudo nano /etc/ssh/sshd_config 找到#PasswordAuthentication yes 把#的注释删掉 将PermitRootLogin without-password修改为:PermitRootLogin yes 启动 ssh服务1sudo /etc/init.d/ssh start 开机启动ssh1sudo update-rc.d ssh enable 更换源根据教程,咱们来编辑 /etc/apt/sources.list 文件。这里推荐用 nano 命令编辑。命令如下12sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak #备份为 sources.list.baksudo nano /etc/apt/sources.list #编辑sources.list 文件 进入编辑界面,删除原有的内容或者用#注释掉原来的源,添加下方的源。12deb http://mirrors.aliyun.com/raspbian/raspbian/ jessie main non-free contribdeb-src http://mirrors.aliyun.com/raspbian/raspbian/ jessie main non-free contrib PS: 这里不必删除官方原来的源,如果注释了,安装软件的时候会出现各种依赖无法解决 CONFIG1sudo raspi-config 12345678910Raspberry Pi软件配置工具(raspi-config)1 更改用户密码 更改当前用户的密码2 网络选项 配置网络设置3 引导选项 配置启动选项4 本地化选项 设置语言和区域设置以匹配您的位置5 接口选项 配置与外围设备的连接6 Overc lock 配置Pi的超频7 高级选项 配置高级设置8 更新 将此工具更新到最新版本9关于raspi-config 有关此配置工具的信息 7z12sudo apt-get install p7zip-full7z x PACKAGE.7z How can I uncompress a *.7z file?]]></content>
<tags>
<tag>linux</tag>
<tag>raspberry</tag>
<tag>7z</tag>
</tags>
</entry>
<entry>
<title><![CDATA[python 字典转 excel 表格]]></title>
<url>%2F2018%2F08%2Fpython-dict-to-excel.html</url>
<content type="text"><![CDATA[依赖模块1pip install pandas xlwt openpyxl CODE123# -*- coding: utf-8 -*-import pandas as pd data = { 'name': 'Jon', 'age': '23', 'id': '123456789' } df = pd.DataFrame([data,]) df.to_excel('1.xlsx',) df.to_excel('2.xlsx', index=False) df.to_excel('3.xlsx', header=False) # 转置 df = pd.DataFrame([data,]).T df.to_excel('4.xlsx',) IMAGE 参考资料 stackoverflow pandas docs xlsxwriter]]></content>
<tags>
<tag>python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[树莓派通过终端连接 wifi]]></title>
<url>%2F2018%2F08%2Fraspi-terminal-wifi.html</url>
<content type="text"><![CDATA[扫描WIFI使用如下命令可以扫描附近的无线网:123sudo iwlist wlan0 scan# 过滤出自己的 wifiiwlist wlan0 scan | grep 目标WIFI 添加有密码的WIFI网络1sudo vim /etc/wpa_supplicant/wpa_supplicant.conf 在文件末尾出添加一下代码,并替换掉ssid_name以及password即可。12345678910111213141516network={ ssid="ssid_name" key_mgmt=WPA-PSK psk="password"}# 隐藏的 wifinetwork={ ssid="yourHiddenSSID" scan_ssid=1 psk="Your_wifi_password"}# 没有密码的 wifinetwork={ ssid="ssid_name" key_mgmt=NONE} 查看是否分配成功 1ifconfig wlan0 错误调试如果出现连接不成功的情况,有很大的可能是由于配置文件配置错误的原因。 1sudo wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -i wlan0 通过wpa_supplicant的直接连接,如果配置文件出现问题,则会直接提示配置文件的错误详情。 如果出现一下结果,则一般代表配置文件没有问题 123Successfully initialized wpa_supplicantnl80211: Driver does not support authentication/association or connect commandswlan0: Failed to initialize driver interface 参考资料 树莓派Raspberry命令行配置无线网络连接]]></content>
<tags>
<tag>raspberry</tag>
</tags>
</entry>
<entry>
<title><![CDATA[dnsmasq]]></title>
<url>%2F2018%2F08%2Fdnsmasq.html</url>
<content type="text"><![CDATA[通过Dnsmasq部署本地DNS服务 Docker 搭建 DNS 服务器 Docker 搭建 dnsmasq 过滤广告 DNSmasq 服务搭建]]></content>
<tags>
<tag>dnsmasq</tag>
</tags>
</entry>
<entry>
<title><![CDATA[alpine-linux]]></title>
<url>%2F2018%2F08%2Falpine-linux.html</url>
<content type="text"><![CDATA[Alpine Linux 常用参考]]></content>
<tags>
<tag>alpine</tag>
</tags>
</entry>
<entry>
<title><![CDATA[docker-compose 启动顺序]]></title>
<url>%2F2018%2F07%2Fdocker-compose-start-sequence.html</url>
<content type="text"><![CDATA[dockerizedockerize(git) | download 使用123# DockerfileRUN mv dockerize /usr/bin/ \ && chmod o+x /usr/bin/dockerize dcoekr-compose.yml 1command: ["dockerize", "-wait", "tcp://db:3306", "-timeout", "300s", "/bin/sh", "./start.sh"] 监听的协议和端口,超时时间,执行的命令 start.sh 12python manage.py makemigrations ...python manage.py runserver ... wait-for-itGithub]]></content>
<tags>
<tag>docker</tag>
<tag>docker-compose</tag>
<tag>dockerize</tag>
</tags>
</entry>
<entry>
<title><![CDATA[chocolatey 安装指定位置]]></title>
<url>%2F2018%2F07%2Fchocolatey.html</url>
<content type="text"><![CDATA[12# 安装在 D 盘setx ChocolateyInstall D:\Chocolatey /M && SET "ChocolateyInstall=D:\Chocolatey" && "%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" 参考 官网 Command Reference chocolatey的使用]]></content>
<tags>
<tag>chocolatey</tag>
</tags>
</entry>
<entry>
<title><![CDATA[kubernetes]]></title>
<url>%2F2018%2F07%2Fkubernetes.html</url>
<content type="text"><![CDATA[最困难的耗时间 | kubernetes-the-hard-way 工具: minikube - 本地单个节点 kubeadm 多个节点-集群 kops - 在Cloud删安装集群 tectonic 少量节点是免费的 Kubernetes 云上集群 过期自动消失 kubectl 安装 12345678910# 节点kubectl get node# 节点详细信息kubectl get node -o wide# 上下文kubectl context# 获取不同的 contextkubectl config get-contexts# 查看集群情况kubectl cluster-info 12# 查看当前 minikube 生成集群的基本信息kubectl config view minikube使用minikube创建虚拟机,在VirtualBox里面需要安装kubernetskubectl brew install kubectl 12345678# 创建单节点的 K8s 集群minikube startminikube start --bootstrapper=localkube # 上面运行不成功就这个minikube delete# 进入 minikube 创建的 virtualbox 虚拟机minikube ssh# minikube webminikube dashboard 阿里云安装minikube 1234# Mac OSX 验证curl -Lo minikube http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v0.28.1/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/# Linuxcurl -Lo minikube http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v0.28.1/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/ pod最小的调度单位 12345678910111213apiVersion: v1kind: Podmetadata: name: nginx labels: app: nginxspec: # 定义容器 最重要的部分 containers: - name: nginx image: nginx ports: - containerPort: 80 # 暴露的端口 123456kubectl create -f pod_nginx.ymlkubectl delete -f pod_ngins.ymlkubectl get podskubectl get pods -o wide # 显示容器的详细信息kubectl exec -it nginx sh # 进入容器kubectl port-forward nginx 8080:80 # 映射 8080 到容器里面的 80 123~ kubectl get podsNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 1m 123~ kubectl get pods -o wideNAME READY STATUS RESTARTS AGE IP NODEnginx 1/1 Running 0 4m 172.17.0.4 minikube 横向扩展 ReplicsSet: v1 版本不支持,扩展 ReplicationController 123456kubectl get pods# 删除 pod 之后 k8s 会自动重启一个 podkubectl delete pods <pods name># 调整 nginx pod 为 2kubectl scale rc nginx --replicas=2kubectl get rc 12345678910111213141516171819202122apiVersion: apps/v1kind: ReplicaSet # ReplicationControllermetadata: name: nginx labels: tier: frontendspec: replicas: 3 # 扩展 3 个 nginx selector: matchLabels: tier: frontend template: metadata: name: nginx labels: tier: frontend spec: containers: - name: nginx image: nginx ports: - containerPort: 80 Deployments example yaml file 123456789101112131415161718192021apiVersion: apps/v1kind: Deployment # 指定的类型metadata: name: nginx-deployment labels: app: nginxspec: replicas: 3 # 保证这个 pods 里面至少是 3 个 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.12.2 ports: - containerPort: 80 1234567# 操作示例# 创建kubectl create -f deployment_nginx.ymlkubectl get deployment # 查看数量kubectl get rs # 有 dep 也会有这个# 查看详细信息kubectl get deployment -o wide 看 NAME 会一层一层的加 升级 image实现平滑升级,过渡 12# 对 image 升级kubectl set image deployment nginx-deployment nginx=nginx:1.13 回滚123456789101112131415161718192021222324$ kubectl get rsNAME DESIRED CURRENT READY AGEnginx-deployment-7498dc98f8 0 0 0 17mnginx-deployment-86cd46c4d9 3 3 3 7m#----------------------------$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx-deployment-86cd46c4d9-8zvtk 1/1 Running 0 7mnginx-deployment-86cd46c4d9-bhn2d 1/1 Running 0 7mnginx-deployment-86cd46c4d9-ltmn9 1/1 Running 0 7m#----------------------------# 历史$ kubectl rollout history deployment nginx-deploymentdeployments "nginx-deployment"REVISION CHANGE-CAUSE1 <none>2 <none>#----------------------------# 回滚$ kubectl rollout undo deployment nginx-deploymentdeployment "nginx-deployment"$ kubectl get deployment -o wideNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTORnginx-deployment 3 3 3 3 20m nginx nginx:1.12.2 app=nginx Serviceport simple example 12345678# 创建kubectl expose deployment nginx-deployment --type=NodePort# --------------------# 监听了 minikube 的端口 32117kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1hnginx-deployment NodePort 10.102.175.227 <none> 80:32117/TCP 18s 对外服务的三种方式(端口) kubectl expose yaml文件定义service ClusterIP(外界无法访问) NodePort(绑定到Node,对外提供访问的) LoadBalancer(云服务商提供) DNS ClusterIP内部网络 1234567# ClusterIP# 创建kubectl expose deployment service-test# 单行 shellwhile true; do curl 10.3.120.168:80080; done# 无宕机更新? 这个会有一小段的中断kubectl edit deployment service-test Rolling Update todo,task NodePort对外提供访问 1234567891011121314# pod_nginx.ymlapiVersion: v1kind: Podmetadata: name: nginx-pod labels: app: nginxspec: containers: - name: nginx-container image: nginx ports: - name: nginx-port containerPort: 80 example 12345678910111213141516# 创建 podkubectl create -f pod_nginx.yml# 创建 NodePort kubectl expose pods nginx-pods --type=NodePort# 查看 node 详细信息kubectl describe node minikube# ----------------------------# 查看 NodePort$ kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19hnginx-deployment NodePort 10.102.175.227 <none> 80:32117/TCP 17h# ---------------------------$ kubectl get nodeNAME STATUS ROLES AGE VERSIONminikube Ready master 19h v1.10.0 通过yaml文件创建service1234567891011121314# service_nginx.ymlapiVersion: v1kind: Servicemetadata: name: nginx-servicespec: ports: - port: 33333 # 8080 nodePort: 32333 # 8080 targetPort: nginx-port protocol: TCP selector: # 过滤 选择暴露哪个 pod app: nginx type: NodePort 1234# lables$ kubectl get pods --show-labelsNAME READY STATUS RESTARTS AGE LABELSnginx-pod 1/1 Running 0 41m app=nginx lable12345678910111213141516# 先创建 podkubectl create -f # 设置lablebukectl label node minikube hardware=good# -------------------------------# 执行前$ kubectl get podsNAME READY STATUS RESTARTS AGEbusybox-pod 0/1 Pending 0 16snginx-pod 1/1 Running 0 1h# -----------------------------# 执行后$ kubectl get podsNAME READY STATUS RESTARTS AGEbusybox-pod 1/1 Running 0 1mnginx-pod 1/1 Running 0 1h 123456789101112131415161718# pod_busybox.yml# 创建之后它会是 pending# 它会寻找 lable 是 hardware: good 节点来运行apiVersion: v1kind: Podmetadata: name: busybox-pod labels: app: busyboxspec: nodeSelector: hardware: good containers: - name: busybox-container image: busybox command: - sleep - "360000" 生产环境一般不用ClusterIP、NodePort,而是LoadBalance、ExternalName。 LoadBalanceexternal-dns]]></content>
<tags>
<tag>docker</tag>
<tag>kubernetes</tag>
<tag>minikube</tag>
<tag>kubectl</tag>
</tags>
</entry>
<entry>
<title><![CDATA[docker-compose]]></title>
<url>%2F2018%2F07%2Fdocker-compose.html</url>
<content type="text"><![CDATA[基本命令及安装1234567891011docker-compose --verison# linuxsudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-compose# 启动docker-compose updocker-compose up -d # 后台docker-compose -f docker-compose.yml updocker-compose imagesdocker-compose down 部署一个 WordPress详细文档查看官方hub 1234# 启动一个 mysql containerdocker run -d --name mysql -v mysql-data://var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wordpress mysql# 启动 wordpress containerdocker run -d -e WORDPRESS_DB_HOST=mysql:3306 --link mysql -p 8080:80 wordpress docker-compose.yml概念 Services Networks Volumes Services一个Service代表一个container,这个container可以从dockerhub的image来创建,或者从本地的Dockerfile`build出来的image`来创建 Service的启动类似的docker run,我们可以给其指定network和volume,所以可以给service指定network和Volume的引用 wordpress example123456789101112131415161718192021222324252627282930version: '3'services: wordpress: image: wordpress ports: - 8080:80 environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_PASSWORD: root networks: - my-bridge mysql: image: mysql environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress volumes: - mysql-data:/var/lib/mysql networks: - my-bridgevolumes: mysql-data:networks: my-bridge: driver: bridge flask-redis example docker-compose.yml 123456789101112131415version: "3"services: redis: image: redis web: build: context: . dockerfile: Dockerfile ports: - 8080:5000 environment: REDIS_HOST: redis app.py 1234567891011121314151617from flask import Flaskfrom redis import Redisimport osimport socketapp = Flask(__name__)redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)@app.route('/')def hello(): redis.incr('hits') return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname())if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) Dockerfile 1234567FROM python:2.7LABEL maintaner="Peng Xiao xiaoquwl@gmail.com"COPY . /appWORKDIR /appRUN pip install flask redisEXPOSE 5000CMD [ "python", "app.py" ] 扩展+负载均衡123docker-compose up --scale web=3 -d# 后端运行三台 web container,可以随便扩展# 在上面的 flask 中会因为 ports 不能重复利率用二报错 删掉即可 Dockerfile 1234567FROM python:2.7LABEL maintaner="Peng Xiao xiaoquwl@gmail.com"COPY . /appWORKDIR /appRUN pip install flask redisEXPOSE 80CMD [ "python", "app.py" ] app.py 1234567891011121314151617from flask import Flaskfrom redis import Redisimport osimport socketapp = Flask(__name__)redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)@app.route('/')def hello(): redis.incr('hits') return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname())if __name__ == "__main__": app.run(host="0.0.0.0", port=80, debug=True) docker-compose.yml 12345678910111213141516171819202122version: "3"services: redis: image: redis web: build: context: . dockerfile: Dockerfile environment: REDIS_HOST: redis lb: image: dockercloud/haproxy links: - web ports: - 8080:80 volumes: - /var/run/docker.sock:/var/run/docker.sock run123456789101112docker-compose up -ddocker-compose pscurl 127.0.0.1:8080dcoker-compose up --scale web=3 -d # 运行 3 个在后台docker-compose pscurl 127.0.0.1:8080 # 多 run 几次curl 127.0.0.1:8080Hello Container World! I have been seen 3 times and my hostname is 54389c4f203a.curl 127.0.0.1:8080Hello Container World! I have been seen 4 times and my hostname is 121e4d6e88db.curl 127.0.0.1:8080Hello Container World! I have been seen 5 times and my hostname is 6d2929bbaae4. 部署复杂的应用1docker-compose build]]></content>
<tags>
<tag>docker</tag>
<tag>haproxy</tag>
</tags>
</entry>
<entry>
<title><![CDATA[docker 网络]]></title>
<url>%2F2018%2F07%2Fdocker-network.html</url>
<content type="text"><![CDATA[1docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done" linux netwoek namespacelinux namespace 实验(网络命名空间) 12345678910111213141516171819202122232425262728293031323334# linux netwoek namespace sudo ip netns listsudo ip netns delete test1sudo ip netns add test1sudo ip netns add test2# namespace 查看 ip 地址sudo ip netns exec test1 ip a# 查看本机 ip linksudo ip link# 查看 ip licksudo ip netns exec test1 ip link# 启动sudo ip netns exec test1 ip link set dev lo up# ------------ simple example ------------# link 是成对出现的sudo ip link add veth-test1 type veth peer name veth-test2# 把 veth-test1 添加到 test1 里面# 这里时候宿主机的 veth-test1 会消失,但它会出现在 test1里面sudo ip link set veth-test1 netns test1sudo ip netns exec test1 ip link# 继续添加 test2sudo ip link set veth-test2 netns test2# 为 veth-test1/2 分配 ip 地址,分配完成以后是不会立即出现 IP 的sudo ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1sudo ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2# 启动端口sudo ip netns exec test1 ip link set dev veth-test1 upsudo ip netns exec test2 ip link set dev veth-test2 up# 查看 IP 地址sudo ip netns exec test1 ip asudo ip netns exec test2 ip a# ping test2sudo ip netns exec test1 ping 192.168.1.2 Docker Container Network123456789# 查看 container 的网络信息docker network inspect <container id># 验证网络sudo yum install -y bridge-utils # brctlbrctl show # 显示网络状况ip a # 看本机网络 docker0 和上面的某条信息符合# 查看 docker network 信息docker network inspect bridge# 在启动一个 container 重复执行上面的命令,发现会多一个 containers host/none12345678# nonedocker run -d --name test1 --network none busybox /bin/sh -c "while true; do sleep 3600; done"dcoker exec -it test1 /bin/ship a # 没有 ip and mac# hostdocker run -d --name test1 --network host busybox /bin/sh -c "while true; do sleep 3600; done"dcoker exec -it test1 /bin/ship a # 和主机共享一套 network namespace 多容器互通 app.py 1234567891011121314151617from flask import Flaskfrom redis import Redisimport osimport socketapp = Flask(__name__)redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)@app.route('/')def hello(): redis.incr('hits') return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname())if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) 123456docker run -d --name redis redisdocker run -d --link redis --name flask-redis -e REDIS_HOST=redis cvno/flask-redisdocker exec -it flask-redis /bin/bash # 进入容器env # 会发现 REDIS_HOST# docker flask-redis example !!!docker run -d -p 5000:5000 --link redis --name flask-redis -e REDIS_HOST=redis cvno/flask-redis 多机通信4-10]]></content>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Docker 镜像和容器]]></title>
<url>%2F2018%2F07%2Fdocker-image-dockerfile-container.html</url>
<content type="text"><![CDATA[Centos12345# 如果出现: See 'docker run --help'# 原因是 docker 没有启动sudo service docker start# orsudo docker version 创建一个基础的 Docker image 创建文件夹,以及安装编译库 1234# install 编译 c 程序的 gcc ...sudo yum install gcc glibc-staticmkdir hello-worldcd hello-world && vim hello.c 123456// hello.c#include<stdio.h>int main(){ printf("hello docker\n");} 12# buildgcc -static hello.c -o hello 简单的 Dockerfile 1234# vim DockerfileFROM scratchADD hello /CMD ["/hello"] 12345# docker builddocker build -t cvno/hello-world .docker run cvno/hello-world# 看 image 层docker history d8** 12345# 列出 docker 所有的 containerdocker container ls -aqdocker rm $(docker container ls -sq)# 删除状态为退出的 containerdocker rm $(docker container ls -f "status=exited" -q) 构建一个 Docker 镜像根据已有的image创建一个新的image,不提倡,创建image的过程不透明123456docker run -it centosyum install -y vim && exitdocker container ls -a# 构建成为一个新的 image# docker commit <container name> <account id>/<new image name>docker commit upbeat_minsky cvno/centos-vim Dockerfile 语法123456789101112131415FROMLABEL maintainer=""LABEL version=""LABEL description=""RUN yum install -y vim \ rm -rf /var/lib/apt/lists/* # 清理了 cacheWORKDIR # 同 linux cdADD # *.tar.gz 会自动解压 COPYENVVOLUMEEXPOSE CMD # 多个会被覆盖掉ENTRYPOINT # 它一定会执行# shell exec 两种语法 12345WORKDIR /root # 没有会自动创建ADD hello test/ # /root/test/helloENV MYSQL_VERSION 5.6 # 设置常量RUN apt-get install -y mysql-server="${MYSQL_VERSION}"\ && rm -rf /var/lib/apt/lists/* # 引用常量 CMD/ENTRYPOINT 1234FROM centosENV name Docker# execENTRYPOINT ["/bin/bash", "-c", "echo hello $name"] Docker debug1docker run -it <build 镜像的临时 container> /bin/bash Example1234567# app.pyfrom flask import Flaskapp = Flask(__name__)@app.route('/')def hello() return "hello docker" 1234567FROM python:2.7LABEL maintainer="example@example.com"RUN pip install flaskCOPY app.py /app/appWORKDIR /appEXPOSE 5000CMD ["python", "app.py"] 12# builddocker build -t demo/flask-hello-docker exec123456# 进入容器docker exec -it <container id> /bin/bashdocker exec -it <container id> python docker rm $(docker ps -aq) # 删除所有的 container# 查看容器的详细信息docker inspect Docker Hub通过Docker Hub来自动构建镜像,推荐使用这种方式 上传image 登录Docker帐号docker login docker push cvno/hello-world:latest 私有 registry内部或者个人使用 服务器需要安装,并且5000这个端口可以进行通信 123456docker run -d -p 5000:5000 --restart always --name registry registry:2# 测试通信sudo yum install -y telnet # clenitsudo yum install -t telnet-server # server# 是否安装 telnetrpm -qa telnet-server 12345678910# 客户机sudo ls /etc/dcoker # 修改 docker 配置文件sudo vi /etc/docker/daemon.json # {"insecure-registeries":["11.11.11.11:5000"]}sudo vi /lib/systemd/system/docker.service# ExecStart=/usr/bin/dockerd# EnvironmentFile=-/etc/docker/daemon.json # add sudo systemctl daemon-reload / sudo service docker restart# pushdocker push 35.196.188.123:5000/hello-world 123# 服务端验证是否成功 push# docker registry apiip:5000/v2/_catalog stress一个压力测试的docker的image,build一个命令行的程序 1apt-get install stress 1234FROM ubuntuRUN apt-get update && apt-get install stressENTRYPOINT ["/usr/bin/stress"]CMD [] 资源限制1234567docker run --memory=200M cvno/ubuntu-stress --vm 1# 详细的 debug 信息docker run --memory=200M cvno/ubuntu-stress --vm 1 --verbosedocker run --memory=200M cvno/ubuntu-stress --vm 1 --verbose --vm-bytes500M# cpudocker run --cpu-shares=10 --name=test1 cvno/ubuntu-stress --cpu 1docker run --cpu-shares=5 --name=test2 cvno/ubuntu-stress --cpu 1]]></content>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[docker-machine 使用]]></title>
<url>%2F2018%2F07%2Fdocker-machine.html</url>
<content type="text"><![CDATA[姿势1:使用虚拟机中的 Docker 退出宿主机器的Docker 启动docker-machine创建的虚拟机 [传统姿势]:进入到虚拟机docker-machine ssh <虚拟机> 12345678910111213141516# 确保宿主机的 Docker 是退出的docker verison # 执行的结果是没有 Docker server 的# play !!!$ docker env demo # run > 这条命令会把 demo 里面的 Docker 给映射出来export DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.102:2376"export DOCKER_CERT_PATH="/Users/hyhnm/.docker/machine/machines/demo"export DOCKER_MACHINE_NAME="demo"# Run this command to configure your shell:# eval $(docker-machine env demo)$ eval $(docker-machine env demo) # run > 应用到宿主机的环境变量# 这样就可以在宿主的终端直接操作虚拟机里面的 Docker# 通过本地的 Docker 命令来管理远程 docker-machine# 卸载掉远程的 Docker Server$ docker-machine env --unset$ eval $(docker-machine env --unset) # run > 到这里就卸载掉远程的 Docker Server 了 姿势2:在 Cloud 中创建 Docker 虚拟机docker-machine官方支持的Cloud | Machine drivers第三方(国内云平台以及其它不在官方支持)| AVAILABLE_DRIVER_PLUGINS | 阿里云 开始Examples为阿里云。 推荐直接下载driver的二进制文件 Mac直接移动到bin目录下,并改名为docker-machine-driver-aliyunecs 验证安装是否成功 1docker-machine create -d aliyunecs --help # 即可 Acces key… 姿势3: 使用在线Docker playground https://labs.play-with-docker.com]]></content>
<tags>
<tag>docker</tag>
<tag>docker-machine</tag>
</tags>
</entry>
<entry>
<title><![CDATA[VirtualBox 使用]]></title>
<url>%2F2018%2F07%2Fvirtualbox.html</url>
<content type="text"><![CDATA[开源,免费 | VirtualBox VagrantVagrant 官网 | 使用Vagrant快速创建VirtualBox虚拟机。 12# 安装插件1vagrant plugin install vagrant-vbguest 创建Vagrant创建虚拟机依赖Vagrantfile文件,如果Dockerfile。 | Vagrantfile demo 1234# crate Vagrantfilevagrant init centos/7# dowload vagrant up ps: 安装完成以后会出现在VirtualBox的虚拟机列表中。 管理12345678# open centos/7vagrant ssh# 状态vagrant status# stopvagrant halt# deletevagrant destroy 安装好docker的虚拟机示例的Vagrantfile 123456789# add Vagrantfileconfig.vm.provision "shell", inline: <<-SHELL sudo yum remove docker docker-common docker-selinux docker-engine sudo yum install -y yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager -y --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce sudo systemctl start dockerSHEELend 12345678910111213141516# buildvagrant up# openvagrant ssh# cat status 出现这个是因为 docker 还没有启动 `sudo docker version` 就好啦$ docker versionClient: Version: 18.03.1-ce API version: 1.37 Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:20:16 2018 OS/Arch: linux/amd64 Experimental: false Orchestrator: swarmGot permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.37/version: dial unix /var/run/docker.sock: connect: permission denied docker-machineDocker提供的强大工具,使用docker-machine创建VitualBox虚拟机 能够自动在虚拟机上安装docker的工具,Mac和Windows安装Docker时候会自动安装,它也可以手动安装。 123456789101112131415# cat verisondocker-machine version # docker-machine version 0.14.0, build 89b8332# create,demo 是虚拟机的名称# 它会在 virtualbox 创建一个安装好 docker 的 linux 虚拟机。docker-machine create demo# list 运行和创建好的 docker-machine 列表docker-machine ls# open demodocker-machine ssh demo# stopdocker-machine stop demo# deletedocker-machina rm demo# cat cmd [option]docker]]></content>
<tags>
<tag>docker-machine</tag>
<tag>virtualbox</tag>
<tag>vagrant</tag>
</tags>
</entry>
<entry>
<title><![CDATA[odoo 生成单据编号]]></title>
<url>%2F2018%2F06%2Fodoo-number.html</url>
<content type="text"><![CDATA[在Odoo里面,默认提供了ir.sequence这样一个序号生成模块 创建编号打开xml文件,加入如下代码: 12345<record id="business_order_number" model="ir.sequence"> <field name="name">Order Number</field> <field name="code">business.order</field> <!-- 4 位年份 --> <field name="prefix">PO-%(year)s%(month)s%(day)s</field> <!-- 2 位年份 --> <field name="prefix">PO-%(y)s%(month)s%(day)s</field> <field name="suffix">号</field> <field name="padding">3</field> </record> 参数说明 name - 名字,随便叫什么都行 code - 调用生成编码的 Key,需保证唯一性 prefix - 前缀,可以是固定的字面量也可以是组合参数 suffix - 后缀 padding - 序列递增的位数1位1、2位01、3位001 调用1class Example(models.Model): ''' Example ''' _name = "Example.example" number = fields.Char(string='Number', required=True, copy=False, readonly=True, index=True, default=lambda self: _('New')) # 订单号 @api.model def create(self, vals): '''保存方法重写''' if not vals.get('number', False) or vals['number'] == _('New'): vals['number'] = self.env['ir.sequence'].next_by_code('business.order') or '/' print vals['number'] return super(SaleOrder, self).create(vals) 单据格式PO- + 年月日 + 自定义位数的顺序编号PO-20180621001PO-180621001 参考 Odoo 使用 ir.sequence 生成序列号 | Jalena Blog odoo10如何自定义自动生成单据编号 - 辰辰宝贝 - 博客园]]></content>
<tags>
<tag>odoo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[odoo-workflow]]></title>
<url>%2F2018%2F06%2Fodoo-workflow.html</url>
<content type="text"><![CDATA[在 odoo 中,workflow 是关联到某个 model 的对象,用来动态管理记录的生命周期。 | 10.0 workflow odoo后台设置激活开发者模式,技术>工作流 Activities定义一些由odoo服务器所要完成的任务,如果改变记录状态,发送邮件Transitions定义了如何从一个Activity到另外Activity example123456789101112131415161718# 这部分代码加入到定义的 models class 中state = fields.Selection([ ('draft', "Draft"), ('confirmed', "Confirmed"), ('done', "Done"),], default='draft') # default 默认字段@api.multidef action_draft(self): self.state = 'draft'@api.multidef action_confirm(self): self.state = 'confirmed'@api.multidef action_done(self): self.state = 'done' state 字段中元组的第0个元素是存储在数据库中的,第1元素是显示在页面上的 12345678910111213141516<!-- xml --><header> <button name="action_draft" type="object" <!-- 按钮显示的名称 --> string="Reset to draft" <!-- 在哪些状态中显示 --> states="confirmed,done"/> <button name="action_confirm" type="object" string="Confirm" states="draft" class="oe_highlight"/> <button name="action_done" type="object" string="Mark as done" states="confirmed" class="oe_highlight"/> <!-- 显示在状态栏 --> <field name="state" widget="statusbar"/></header> 参考 Odoo Workflow | Jeff’s Blog odoo 在原有工作流中添加审批流 - UniqueColor - 博客园 Odoo10模块开发(1) 工作流 - CSDN博客 官方文档]]></content>
<tags>
<tag>odoo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[odoo(二)]]></title>
<url>%2F2018%2F06%2Fodoo2.html</url>
<content type="text"><![CDATA[odoo视图(view: tree, form, search, notebook),模型(models: Many2one, One2many, Many2many) Menus docs菜单也是用xml来配置 12345<?xml version="1.0" encoding="UTF-8"?><!-- 如果有中文的话必须加上上面这个声明 --><odoo> <record model="ir.actions.act_window" id="course_list_action"> 定义视图 </record> <menuitem 定义菜单/></odoo> id识别用的,唯一 name在页面显示出来的文字 parent 上级菜单是谁(id) action 对应的菜单(属于一个集合) sequence菜单的排序 Views docsxml视图里面添加view的时候可以添加html标签,但必须是闭合的 继承视图文档和Models继承差不多,多了inherits_id指向要改写的view, 1<field name="inherits_id" ref="id_tags_list" /> 在arch里面用xpath定位改写元素 Models docs数据表模型,先自己的models中重写别的models的时候需要在__manifest__.py把依赖关系,加入到自己模块的__manifest__.py的depends中。 Many2one直接关联已经存在的表(文章与分类的关系FK)| 单一的记录One2many虚拟的关系()| 一系列的记录Many2manyodoo 会再建立一张单独的关系表(一个文章多个标签,标签下多个文章)ondelete='set null'如果关联的表删除了就把这个字段设置为空ondelete='cascade'如果它关联Many2one的表被删除了,当前这表就会清空相关的全部数据string 字段的别名index 字段是否创建索引 1name = fields.Char( string='Name', default=lambda self: self._get_default_name(), ) @api.model def _get_default_name(self): return "test" PS: res.users可以登录的用户信息表;res.partner这张表里面的用户没有登录权限;res.users 可以对应到 res.partner 某条记录 继承和扩展改写 Models 经典继承 原型继承(拷贝一个出来) 委托继承 12self.env.cr.commit() # 提交数据self.env['model'].search([]) # 取一条记录 inheritance视图(view),模型(models)的改写 domain在models中过滤Many2one字段 12domain=['|', ('instructor', '=', True), ('category_id.name', 'ilike', "Teacher")]) _compute_seats报名与座位的比例,xml的fiels需要加widget=”progressbar” 1234567@api.depends('seats', 'attendee_ids')def _taken_seats(self): for r in self: if not r.seats: r.taken_seats = 0.0 else: r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats 默认值default是在models的field中来定义 123name = fields.Char(default="Unknown")user_id = fields.Many2one('res.users', default=lambda self: self.env.user)start_date = fields.Date(default=fields.Date.today) active是odoo中的内置规则,可能是显示的tree视图中,或者根本不显示 1active = fields.Boolean(default=True) onchange乘积,比如商品数量和价格的关系,或者数据小于某个阀值的时候弹出警告`@api.onchange` 12345678910111213141516@api.onchange('seats', 'attendee_ids')def _verify_valid_seats(self): if self.seats < 0: return { 'warning': { 'title': "Incorrect 'seats' value", 'message': "The number of available seats may not be negative", }, } if self.seats < len(self.attendee_ids): return { 'warning': { 'title': "Too many attendees", 'message': "Increase seats or remove excess attendees", }, } `@api.constrains`模型限制,把值限制的可控的范围内 123456模型限制@api.constrains('instructor_id', 'attendee_ids')def _check_instructor_not_in_attendees(self): for r in self: if r.instructor_id and r.instructor_id in r.attendee_ids: raise exceptions.ValidationError("A session's instructor can't be an attendee") 12345678910# 在数据库中限制 _sql_constraints = [ ('name_description_check', 'CHECK(name != description)', "The title of the course should not be the description"), ('name_unique', 'UNIQUE(name)', # 唯一不可重复 "The course title must be unique"), ] 1234567891011121314# 拷贝的时候限制 @api.multi def copy(self, default=None): default = dict(default or {}) copied_count = self.search_count( [('name', '=like', u"Copy of {}%".format(self.name))]) if not copied_count: new_name = u"Copy of {}".format(self.name) else: new_name = u"Copy of {} ({})".format(self.name, copied_count) default['name'] = new_name return super(Course, self).copy(default) 高级视图 显示的时候不同的值显示不同的颜色 1234<!-- 在这里的比较符号要用 html 的 小于5 天的,或者大于 15 天的--><tree string="Session Tree" decoration-info="duration&lt;5" decoration-danger="duration&gt;15"><!-- 该字段不形式在 tree 视图中 --><field name="duration" invisible="1"/> 日历视图docs 1234567<!-- calendar view--><!-- color 不同的值颜色不一样 --><!-- date_start 日历中的开始时间 --><!-- end_date 结束时间 --><calendar string="Ideas" date_start="invent_date" color="inventor_id"> <field name="name"/></calendar> 搜索视图docs 123456789<!-- filter_domain 字段 --><field name="description" string="Name or description" filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/><!-- filter 标签 --><!-- domain 添加过滤条件 --><filter name="my_courses" string="My Courses" domain="[('responsible_id', '=', uid)]"/><!-- context 添加分组条件 要在 group 标签中--><filter name="by_responsible" string="Responsible" context="{'group_by': 'responsible_id'}"/> ps: Pycharm find in path 可以很方便的查找 甘特图docs 12345<gantt string="Session Gantt" date_start="start_date" date_delay="hours" default_group_by='instructor_id'> <!-- <field name="name"/> this is not required after Odoo 10.0 --> </gantt> ps: xml 某些配置在删掉的时候还是会在数据库中的,还是会报错,要给个空值给覆盖掉或者直接去数据库中删除 图表视图docs 123# 如果是计算字段必须加 store=Trueattendees_count = fields.Integer( string="Attendees count", compute='_get_attendees_count', store=True) 看板视图docs 改写视图1234567<!--# 直接重写--><field name='inherit_id' ref='需要改写的 view'><!--# 在后面添加--><xpath expr="field[@name='目标ID'" position="after"> <field name="idea_ids" string="Number of ideas"></xpath><!-- 还有替换、修改属性值、请看官网 View inheritance-->]]></content>
<tags>
<tag>odoo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[odoo(一)]]></title>
<url>%2F2018%2F06%2Fodoo1.html</url>
<content type="text"><![CDATA[Tiny ERP > OpenERP > odoo odoo 官网 | 代码仓库(Github) | 官网下载 | odoo 11.0 开发文档 12git clone https://www.github.com/odoo/odoo --depth 1 --branch 11.0 --single-branch odoo11pip install -r requirements.txt Node.js | Postgresql 下载 12npm install -g lessnpm install -g less less-plugin-clean-css ps:可能会出现的 error。 依赖 wkhtmltopdf ps: 需要加入系统环境变量 linux12345678# ubuntu wget https://builds.wkhtmltopdf.org/0.12.5-rc/wkhtmltox_0.12.5-0.20180604.140.rc\~6f77c46\~bionic_amd64.debsudo apt-get install libxfont1 xfonts-encodings xfonts-utils xfonts-base xfonts-75dpidpkg -i wkhtmlto...# 测试一下wkhtmltopdf https://www.baidu.com example.pdf# rpmrpm -ivh wkhtml... 启动脚本Pycharm 设置启动脚本 github: odoo-bin 官网下载版本: setup > odoo Error12345# There was a problem confirming the ssl certificate: [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:661) - skippingcurl https://bootstrap.pypa.io/get-pip.py | python# Werkzeug && PyYAMLpip install Werkzeug PyYAMLpip install --upgrade --force-reinstall reportlab 配置文件配置模板:odoo > debian > odoo.conf 1234567891011121314[options]; This is the password that allows database operations:; admin_passwd = admin; admin_passwd = admin // 生产环境必须要设置data_dir = data // 上传文件的目录db_host = 127.0.0.1 // 数据库地址db_port = 5433 // 端口db_user = odoo // 用户db_password = qweasdzxc // 密码addons_path = addons,myaddons // app 目录,如我我们自己开发 app 就新建一单独的目录; 多个 app 目录用 , 逗号隔开;addons_path = H:\code\odoo11\addons,H:\code\odoo11\myaddons;addons_path = /usr/lib/python3/dist-packages/odoo/addons # linuxbin_path=<nodejs path> 指定配置文件:在开发的过程中配置多个配置文件 123python odoo-bin -c <目录名>/<配置文件>.conf # 重启自动升级指定的模块,多个模块用 , 号来分隔python odoo-bin -c <目录名>/<配置文件>.conf -u <appname> 保存当前 odoo 全部配置和一些默认值 1python odoo-bin -s 创建 app12odoo-bin scaffold <module name> <where to put it>python odoo-bin scaffold firstapp myaddons 安装自定义模块 创建完成以后会在 odoo 的模块列表里面出现,直接安装 如果没有改动 .py 文件可直接点升级,否则就需要重启odoo 菜单是以 xml 的形式,包含应用在标题栏和菜单栏的名称,菜单的父子关系 models 已经存在数据,添加一个新的字段,并且设置了required=True,那么odoo在生成数据表的时候并不会真的把这个字段设置为not null,如果要改正这一状况,就需要把表删除,重新建表 odoo有很多配置是使用xml文件配置的,所有的xml文件都是保存在数据库中的,升级app的时候如果总是出现报错可以进入相应的数据库中删除相应的记录ir_ui_view表]]></content>
<tags>
<tag>odoo</tag>
<tag>node</tag>
<tag>postgresql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux 安装 python]]></title>
<url>%2F2018%2F05%2Flinux-install-python3.html</url>
<content type="text"><![CDATA[Python3 的安装方法 12which python# 可以查看位置,一般是位于/usr/bin/python目录下。 Ubuntu 18.04123456# py3sudo apt install python3sudo apt-get install python3-pip# py2sudo apt install pythonsudo apt-get install python-pip Centos依赖12yum -y groupinstall "Development tools"yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel 然后根据自己需求下载不同版本的Python3,我下载的是Python3.6.5 1wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz 如果速度不够快,可以直接去官网下载,利用WinSCP等软件传到服务器上指定位置,我的存放目录是/usr/local/python3,使用命令: 创建目录12mkdir /usr/local/python3 # 建立一个空文件夹 然后解压压缩包,进入该目录,安装Python3 编译12345tar -xvJf Python-3.6.5.tar.xzcd Python-3.6.5./configure --prefix=/usr/local/python3# 安装make && make install 创建软链接1234# python3ln -s /usr/local/python3/bin/python3 /usr/bin/python3# pip3ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3 Otherpython3.6程序的执行文件:/usr/local/bin/python3.6python3.6应用程序目录:/usr/local/lib/python3.6pip3的执行文件:/usr/local/bin/pip3.6pyenv3的执行文件:/usr/local/bin/pyenv-3.6 yum因为 yum 使用 Python 2,因此替换为 Python 3 后可能无法正常工作,因此修改 yum 配置文件 123456sudo vi /usr/bin/yum# ------------------------------cd /usr/binls yum*yum yum-config-manager yum-debug-restore yum-groups-manageryum-builddep yum-debug-dump yumdownloader 更改以上文件头,将第一行指定的 python 版本改为 python2.7(#!/usr/bin/python 改为 #!/usr/bin/python2.7) gnome12# 修改gnome-tweak-tool配置文件vi /usr/bin/gnome-tweak-tool #!/usr/bin/python 改为 #!/usr/bin/python2 urlgrabber1vi /usr/libexec/urlgrabber-ext-down #!/usr/bin/python 改为 #!/usr/bin/python2 pip12pip install --upgrade pippip3 install --upgrade pip 参考资料 WhiteBlackCat ( cnblog ) | Ehlxr’s Blog 文成小盆友 ( cnblog ) 一蓑风雨任平生的专栏 ( CSDN )]]></content>
<tags>
<tag>python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[node-express]]></title>
<url>%2F2018%2F03%2Fnode-express.html</url>
<content type="text"><![CDATA[需要用node写api,狗哥说express比较合适。come on. hello world123npm install -g express //安装 expressnpm install -g express-generator //安装 express 命令行工具express -V // 版本 npm list express 查看版本 导入12var express = require('express'); // 导入 expressvar router = express.Router(); 路由映射12345678var router = express.Router(); // 接上文// 子路由 关系对应 req: 用户请求 res:响应用户router.get('/', function(req, res) { res.send('<h1>Hello World</h1>'); // 回应内容});app.use('/home', router); // app 对应的路径 端口1234var port = process.env.PORT || 8080; // 服务端口app.listen(port); // 监听端口console.log('Magic happens on port ' + port); // 输出文字 启动12345678node app1.js# 指定端口启动 bash# Linux & MacPORT=7070 node app1.js# windowsset PORT=7070node app1.js 进阶访问路径1234567// :name 获取路径的参数router.get('/:name', function(req, res) { // 获取访问的路由 如果加了 / 并且后面有字符就识别不出来 res.send('<h1>Hello ' + req.params.name + '</h1>'); // 获取路由上的参数 /?name=gou // res.send('<h1>Hello ' + req.query.name + '</h1>');}); 更多请求方式12var bodyParser = require('body-parser');app.use(bodyParser.urlencoded({ extended: true })); body-parser模块的作用,是对POST、PUT、DELETE等 HTTP 方法的数据体进行解析。app.use用来将这个模块加载到当前应用。有了这两句,就可以处理POST、PUT、DELETE等请求了。 123456// post 映射router.post('/', function (req, res) { var name = req.body.name; // 获取用户发来的 name 字段 res.json({message: 'Hello ' + name}); // 序列化返回给用户 // 如果字段不正确将返回 undefined}); 中间件12345// 定义方式 next 下一个中间件router.use(function(req, res, next) { console.log('There is a requesting.'); // 业务逻辑 next();}); REST APIREST API 的基本用法 (1) 命令行进入demos/rest-api-demo目录,执行下面的命令。 1$ npm install -S json-server (2) 在项目根目录下,新建一个 JSON 文件db.json。 123456789{ "posts": [ { "id": 1, "title": "json-server", "author": "typicode" } ], "comments": [ { "id": 1, "body": "some comment", "postId": 1 } ], "profile": { "name": "typicode" }} (3) 打开package.json,在scripts字段添加一行。 1234"scripts": { "server": "json-server db.json", "test": "..."}, (4) 命令行下执行下面的命令,启动服务。 1$ npm run server (5)打开 Chrome 浏览器的 Postman 应用。依次向http://127.0.0.1:3000/posts、http://127.0.0.1:3000/posts/1发出GET请求,查看结果。 (6)向http://127.0.0.1:3000/comments发出POST请求。注意,数据体Body要选择x-www-form-urlencoded编码,然后依次添加下面两个字段。 12body: "hello world"postId: 1 发出该请求后,再向http://127.0.0.1:3000/comments发出GET请求,查看结果。 (7) 向http://127.0.0.1:3000/comments/2发出PUT请求,数据体Body要选择x-www-form-urlencoded编码,然后添加下面的字段。 1body: "hello react" 发出该请求后,再向http://127.0.0.1:3000/comments发出GET请求,查看结果。 (8)向http://127.0.0.1:3000/comments/2发出delete请求。 发出该请求后,再向http://127.0.0.1:3000/comments发出GET请求,查看结果。 参考 https://github.com/ruanyf/jstraining/blob/master/demos/README.md#rest-api https://github.com/ruanyf/jstraining/blob/master/demos/README.md#express]]></content>
<tags>
<tag>node</tag>
<tag>express</tag>
</tags>
</entry>
<entry>
<title><![CDATA[node:hello world!]]></title>
<url>%2F2018%2F03%2Fnode-hello.html</url>
<content type="text"><![CDATA[nvm:nodeJs版本管理工具,管理nodejs版本和npm版本nodeJs:reactNative开发过程中所需要的代码库。npm:是随同nodeJs一起安装的包管理工具,npm管理对应nodeJs的第三方插件 依赖关系nvm、nodejs、npm的关系: nvm管理构建nodejs和对应的npm,npm管理对应nodejs的第三方插件 一个reactNative项目只包含一个nodejs和npm,npm和nodejs是配套关系,一对一的关系。 To install or update nvm, you can use the [install script][2] using cURL: 安装1curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash or Wget: 1wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash The script clones the nvm repository to ~/.nvm and adds the source line to your profile (~/.bash_profile, ~/.zshrc, ~/.profile, or ~/.bashrc). 12export NVM_DIR="$HOME/.nvm"[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm 1234567# 安装 node 指定版本nvm i 8.11.1# 切换指定版本nvm use 8.11.1# 查看版本npm -vnode -v 更换源12345678910# 临时使用淘宝源npm --registry https://registry.npm.taobao.org install node-red-contrib-composer@latest# 全局配置切换到淘宝源npm config set registry https://registry.npm.taobao.org# 全局配置切回到官方源npm config set registry http://www.npmjs.org# 检测是否切换到了淘宝源npm info underscore# 查看源npm config get registry Simple App阮一峰教程 12cnpm i -g webpackcnpm i -g webpack-dev-server 参考 nvm、nodejs、npm的关系 nvm 安装(仓库地址) 阮一峰 node (git) JavaScript 全栈工程师培训教程 切换NPM源(淘宝源)]]></content>
<tags>
<tag>node</tag>
</tags>
</entry>
<entry>
<title><![CDATA[书签]]></title>
<url>%2F2018%2F03%2Fmarklinks.html</url>
<content type="text"><![CDATA[在线密码生成 Chrome 历史版本 Mac Chrome浏览器取消自动升级 Hexo next博客添加折叠块功能添加折叠代码块 ==> ME K2P 官方定制固件 phpMyAdmin安装图解 | PHPMYADMIN简明安装 | 安装phpMyAdmin方法 小米智能套装 — HomeKit bginfo 桌面显示 PC INFO 手把手教你用 Jenkins CI 自动部署 Docker(阿里云镜像服务自动构建+ webhook 触发) | 有道云 | 帖子 北京法院审批信息网 python Python Extension Packages for Windows - Christoph Gohlke Flask 教程(原书) | 简书 | github | 帖子 Python 中特殊变量/方法命名规则说明(特别是私有变量)及使用实例 dev MXTOOLBOX 查看域名的 MX 记录 前端 CDN Docker —— 从入门到实践 odoo Odoo二次开发基础培训视频教程 selenium selenium chromedriver || 淘宝镜像 Selenium with Python中文翻译文档 Tools Gif 制作 SVG图形在线编辑 MP4转GIF Markdown 在线工具大全 NiceTool好工具 SSL在线工具 | Github Project Cool 地图搜租房 | 帖子 Scylla——开源免费的代理 IP 池 | 帖子 动态IP代理 | 帖子 PHP 开发知识结构 | 帖子 水煮蛋 | 帖子 初中生做的,厉害厉害~ 觅漫者 | 帖子 | 初中生… 一堆初中生写的类库、框架 | 有道云备份 | 帖子 云端认证服务 authing.cn | 帖子 从 WP 移植到 Hexo 的主题 Hexo-theme-diaspora | 在线预览 | github Node.js 从入门到上线 | 帖子 自学者 Python 指南(翻译) | 帖子 cloudflare 免 ns 接入工具 | github | 帖子 composer 私有仓库建设 | 帖子 | [toran,] leetcode 刷题整理 | 帖子 基于正则验证中国手机号码(2017) | 帖子 1^1(?:70\d|(?:9[89]|8[0-24-9]|7[135-8]|66|5[0-35-9])\d|3(?:4[0-8]|[0-35-9]\d))\d{7}$ Enhancer 是专业的一站式信息系统开发云平台(免费) | 帖子 又一个面版 | 帖子 机器学习主题播客 | 帖子 开源简历 | 帖子 Life 使用 Excel 进行复式记账 TODO Python的pyautogui模块 ESXI Tasker:Android 上的自动化 | link Google 机器学习速成 12345678910111213141516171819202122232425~ ssh root@mylinkit.localThe authenticity of host 'mylinkit.local (fe80::9c65:f9ff:fe03:d00c%en0)' can't be established.RSA key fingerprint is SHA256:mRVKqls4JwE5TvYvBxRlIR99NAwhzrvqD+8oZ5HCmkA.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added 'mylinkit.local,fe80::9c65:f9ff:fe03:d00c%en0' (RSA) to the list of known hosts.root@mylinkit.local's password:BusyBox v1.23.2 (2015-11-18 16:34:33 CET) built-in shell (ash) _______ ________ __ | |.-----.-----.-----.| | | |.----.| |_ | - || _ | -__| || | | || _|| _| |_______|| __|_____|__|__||________||__| |____| |__| W I R E L E S S F R E E D O M ----------------------------------------------------- CHAOS CALMER (15.05+linkit, r47501) ----------------------------------------------------- * 1 1/2 oz Gin Shake with a glassful * 1/4 oz Triple Sec of broken ice and pour * 3/4 oz Lime Juice unstrained into a goblet. * 1 1/2 oz Orange Juice * 1 tsp. Grenadine Syrup -----------------------------------------------------root@mylinkit:~#]]></content>
<tags>
<tag>collect</tag>
<tag>selenium</tag>
<tag>chrome</tag>
</tags>
</entry>
<entry>
<title><![CDATA[google voice keep]]></title>
<url>%2F2018%2F01%2Fgoogle-voice-keep.html</url>
<content type="text"><![CDATA[项目地址: Google Voice Keep 项目更新记录2018-2-2 修复不可预料的 Google Voice 限制发送信息 bug 2018-1-29 基本功能完成 提供为期半年的 SMS 订阅服务]]></content>
<categories>
<category>Project</category>
</categories>
</entry>
<entry>
<title><![CDATA[API 加密解密-[AES-RSA]]]></title>
<url>%2F2018%2F01%2Faes-rsa-jiami-jiemi.html</url>
<content type="text"><![CDATA[AES It must be 16, 24 or 32 bytes long (respectively for AES-128, AES-192 or AES-256). 加密数据长度无限制, key必须是16的倍数, 加密的数据不够16需要填充至16的倍数 代码中使用BASE64混淆是为了兼容不同语言之间字节码的差异 Python12345#3.6 安装 pip3 install pycryptodome#mac pip3 install pycryptohttps://github.com/sfbahr/PyCrypto-Wheels 1234567891011121314151617181920212223242526272829303132333435363738394041424344# example 中 iv 也是 key#from Crypto.Cipher import AESkey = "qweasdzxcqweasdz"def encrypt(key, message): """ AES 数据加密 :param message: :return: BASE64 混淆数据 """ cipher = AES.new(key, AES.MODE_CBC, key) # 把数据转成bytearray(byte的数组),bytearray只能追加数字,默认把数字转成字节 ba_data = bytearray(message, encoding='utf-8') v1 = len(ba_data) v2 = v1 % 16 if v2 == 0: # 保证收据长度是 16 的时候数据还是加密的 v3 = 16 else: v3 = 16 - v2 for i in range(v3): # 追加 v3 长度 ba_data.append(v3) # final_data = ba_data.decode('utf-8') crypt_data = cipher.encrypt(ba_data) # 要加密的字符串,必须是16个字节或16个字节的倍数 base64_data = base64.b64encode(crypt_data) return base64_datadef decrypt(key, base64_bytes): """ AES 数据解密 :param base64_bytes: base64 的混淆字节数据 :param key: 解密 key :return: """ key = bytes(key, encoding='utf-8') msg = base64.b64decode(base64_bytes) cipher = AES.new(key, AES.MODE_CBC, key) try: result = cipher.decrypt(msg) except ValueError as e: return '' data = result[0:-result[-1]] return str(data, encoding='utf-8') Java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384private final static String ALGO = "AES"; // AES加密private final static String ALGO_MODE = "AES/CBC/NoPadding"; // (算法/模式/填充)//private final static String ALGO_MODE = "AES"; // (算法)private static String key = "qweasdzxcqweasdz"; // 固定的密钥private static String iv = "qweasdzxcqweasdz"; // *********************************// 加密// *********************************public static String encrypt(String text) { try { Cipher cipher = Cipher.getInstance(ALGO_MODE); // Cipher块的大小 16/32/... int blockSize = cipher.getBlockSize(); // 将待加密的数据转成字节 byte[] dataBytes = data.getBytes(); // 将加密字节数组填充至Cipher的倍数, // 填充规则16 - (70 % 16) = 10 ,在字节数组末尾添加10个10, // 具体的填充规则可以根据需要修改 int dataLength = dataBytes.length; int paddingLength = blockSize - dataLength % 16; int encryptDataLength = dataLength + paddingLength; // 待填充的新数组 byte[] encryptDataBytes = new byte[encryptDataLength]; // copy System.arraycopy(dataBytes, 0, encryptDataBytes, 0, dataBytes.length); // 填充数组至16的倍数 byte padding = Byte.valueOf(String.valueOf(paddingLength)); for (int i = encryptDataLength - 1; i >= dataLength; i--){ encryptDataBytes[i] = padding; } SecretKeySpec keyspec = new SecretKeySpec(key.getBytes("utf-8"), ALGO); IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("utf-8")); // 加密 cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); byte[] encrypted = cipher.doFinal(encryptDataBytes); // 加密后的字节码通过BASE64处理 String EncStr = Base64.encodeToString(encrypted, Base64.DEFAULT); Log.d(TAG, "encrypt: before" + Arrays.toString(encryptDataBytes)); Log.d(TAG, "encrypt: encrypt" + Arrays.toString(encrypted)); Log.d(TAG, "encrypt: BASE " + EncStr); return EncStr ; } catch (Exception e) { e.printStackTrace(); return null; } }// *********************************// 解密// *********************************public static String decrypt(String text) { try { // BASE64 解密 byte[] encrypt = Base64.decode(text, Base64.DEFAULT); Cipher cipher = Cipher.getInstance(ALGO_MODE); // AES 解密 SecretKeySpec keyspec = new SecretKeySpec(key.getBytes("utf-8"), ALGO); IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("utf-8")); cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); byte[] encryptData = cipher.doFinal(encrypt); int encryptDataLength = encryptData.length; // int removePadding = encryptData[encryptData.length-1]; int dataLength = encryptData.length - removePadding; byte[] dataBytes = new byte[dataLength]; for (int i = 0; i< dataLength; i++){ dataBytes[i] = encryptData[i]; } String data = new String(dataBytes); Log.d(TAG, "decrypt: " + Arrays.toString(encryptData)); Log.d(TAG, "decrypt: " + Arrays.toString(dataBytes)); Log.d(TAG, "decrypt: " + data); return data; } catch (Exception e) { e.printStackTrace(); return null; } } RSA加密数据长度是有限制的参考资料 123456789101112131415161718192021222324252627282930313233343536373839import rsaimport base64 # ######### 1. 生成公钥私钥 #########pub_key_obj, priv_key_obj = rsa.newkeys(256) pub_key_str = pub_key_obj.save_pkcs1()pub_key_code = base64.standard_b64encode(pub_key_str) priv_key_str = priv_key_obj.save_pkcs1()priv_key_code = base64.standard_b64encode(priv_key_str) print(pub_key_code)print(priv_key_code) # ######### 2. 加密 #########def encrypt(value): key_str = base64.standard_b64decode(pub_key_code) pk = rsa.PublicKey.load_pkcs1(key_str) val = rsa.encrypt(value.encode('utf-8'), pk) return val # ######### 3. 解密 #########def decrypt(value): key_str = base64.standard_b64decode(priv_key_code) pk = rsa.PrivateKey.load_pkcs1(key_str) val = rsa.decrypt(value, pk) return val # ######### 基本使用 #########if __name__ == '__main__': v = 'wupeiqi' v1 = encrypt(v) print(v1) v2 = decrypt(v1) print(v2)]]></content>
<tags>
<tag>AES</tag>
<tag>RSA</tag>
</tags>
</entry>
<entry>
<title><![CDATA[基于 GV 的 Python api]]></title>
<url>%2F2018%2F01%2FGV-python-api.html</url>
<content type="text"><![CDATA[使用 Python3 来操作 Google Voice 的 API。 Git: code here 2018/11/11 Docker 封装 封装到docekr 新增flask api 调用 登录成功后退出driver 2018-02-01 修复重写某些方法时的 bug 2018-01-15 代码发布功能列表: 发送 SMS 拨打电话 取消拨打电话 标记为已读或未读 下载语音留言 后台自动检测是否有新信息 根据 SMS 的设置自动回复 它也可能是一个廉价的短信验证码方案,目前国内至少有两家大型的互联网公司使用这种方案。 依赖模块: pip install selenium pip install requests pip install BeautifulSoup PhantomJS 一款无界面模拟浏览器, 不同的操作系统安装方法有差异]]></content>
<categories>
<category>Project</category>
</categories>
</entry>
<entry>
<title><![CDATA[Phantomjs]]></title>
<url>%2F2018%2F01%2FPhantomjs.html</url>
<content type="text"><![CDATA[无法打开https网站用 phantomjs 自动登陆并爬取一些数据,发现爬取 https 类型的网站的时候无法正常操作 Phantomjs中有个service_args参数可以忽略https错误 1driver = webdriver.PhantomJS(desired_capabilities=cap, service_args=['--ignore-ssl-errors=true']) 获取 cookie123456789101112131415from selenium import webdriverimport pickledriver=webdriver.PhantomJS()driver.get(url) #此处url填写需要访问的地址# 获得 cookie信息cookie_list = driver.get_cookies()print (cookie_list)cookie_dict = {}for cookie in cookie_list: #写入文件 f = open('cookie.txt','wb+') pickle.dump(cookie, f) f.close() cookie_dict[cookie['name']] = cookie['value'] 引用 http://www.cnblogs.com/fly-kaka/p/6656196.html https://www.cnblogs.com/Jacck/p/7675284.html]]></content>
<tags>
<tag>phantomjs</tag>
</tags>
</entry>
<entry>
<title><![CDATA[设计模式]]></title>
<url>%2F2018%2F01%2Fdesign-patterns.html</url>
<content type="text"><![CDATA[什么是设计模式Christopher Alexander:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样你就能一次又一次地使用该方案而不必做重复劳动。” 每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和重复出现的设计。 GoF(Gang of Four) 设计模式四个基本要素:模式名称、问题、解决方案、效果 面向对象的三大特性: 封装:把数据和函数包装在类里 类的边界限制了一些外界的访问 继承:复用 多态:多态语言 1234567891011121314151617181920212223242526272829303132# 封装class A: def __init__(self, x): self.__test = x # 私有变量 def gettest(self): return self.__test def settest(self, x): self.__test = x# 继承:复用class B(A): def __init__(self): pass # override 重写 覆写 def settest(self): self.__test = x + 1# 多态 python 是一种多态语言# 一个函数的多种表现# 重载 python 不支持def test(x, y): return x+ydef test(x, y, z): return x+y+ztest(2,3)test(2,3,4) 接口一种特殊的类(抽象类),声明了若干方法,要求继承该接口的类必须实现这些方法。目的就是对外保持一致 作用:限制继承接口的类的方法的名称及调用方式;隐藏了类的内部实现。 接口就是一种抽象的基类(父类),限制继承它的类必须实现接口中定义的某些方法 其实就是限制程序员的东西, 不能乱写, 按照定的规范去写 示例 12345678910111213141516171819202122232425262728# 接口的两种写法# 1.class A: def test(self): raise NotImplementedError # 限制 子类必须实现class B(A): def test(self): print('B.test')# 2. 抽象类不能实例化 from abs import bastractmethod,ABCMeta class A(metaclass=ABCMeta): # 抽象类 @abstractmethod def test(self): # 抽象方法 必须在子类实现 raise NotImplementedError # 限制 子类必须实现class B(A): passclass C(A): def test(self): print('B.test')a = B() # 不能实例化b = C() # 可以实例化 设计模式六大原则开闭原则一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。 里氏(Liskov)替换原则所有引用基类(父类)的地方必须能透明地使用其子类的对象。功能保持一致 依赖倒置原则高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口(抽象)编程,而不是针对实现(实例)编程。 接口隔离原则使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。(多继承)示例 迪米特法则一个软件实体应当尽可能少地与其他实体发生相互作用。解耦 五大原则没有这个迪米特法则 单一职责原则不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。 一个类(class)只干一件事 合成复用原则 尽量使用合成/聚合的方式,而不是继承。示例 合成 复用 继承 的使用分情况而定 设计模式创建型模式工厂方法模式定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。角色: 抽象工厂角色(Creator) 具体工厂角色(Concrete Creator) 抽象产品角色(Product) 具体产品角色(Concrete Product) 工厂方法模式相比简单工厂模式将每个具体产品都对应了一个具体工厂。 示例 适用场景: 需要生产多种、大量复杂对象的时候 需要降低耦合度的时候 当系统中的产品种类需要经常扩展的时候 优点: 每个具体产品都对应一个具体工厂类,不需要修改工厂类代码 隐藏了对象创建的实现细节 缺点: 每增加一个具体产品类,就必须增加一个相应的具体工厂类 简单工厂模式不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。角色: 工厂角色(Creator) 抽象产品角色(Product) 具体产品角色(Concrete Product) 优点: 隐藏了对象创建的实现细节 客户端不需要修改代码 缺点:违反了单一职责原则,将创建逻辑集中到一个工厂类里当添加新产品时,需要修改工厂类代码,违反了开闭原则 示例 单例模式比如: 数据库连接 好的单列模式会写一个基类(示例) 单例(Singleton): 保证一个类只有一个实例,并提供一个访问它的全局访问点。 适用场景: 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时 优点: 对唯一实例的受控访问 单例相当于全局变量,但防止了命名空间被污染 与单例模式功能相似的概念:全局变量、静态变量(方法) 1234567891011class A: test = 0 A.test = 1a = A()print(a.test)# a.test = 1 # 对象变量的修改不会影响到类的静态变量b = A()print(b.test) 抽象工厂模式定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。例:生产一部手机,需要手机壳、CPU、操作系统三类对象进行组装,其中每类对象都有不同的种类。对每个具体工厂,分别生产一部手机所需要的三个对象。 角色: 示例 抽象工厂角色(Creator) 具体工厂角色(Concrete Creator) 抽象产品角色(Product) 具体产品角色(Concrete Product) 客户端(Client) 相比工厂方法模式,抽象工厂模式中的每个具体工厂都生产一套产品。 适用场景: 系统要独立于产品的创建与组合时 强调一系列相关的产品对象的设计以便进行联合使用时 提供一个产品类库,想隐藏产品的具体实现时 优点: 将客户端与类的具体实现相分离 每个工厂创建了一个完整的产品系列,使得易于交换产品系列 有利于产品的一致性(即产品之间的约束关系) 缺点: 难以支持新种类的(抽象)产品 建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 角色: 抽象建造者(Builder) 具体建造者(Concrete Builder) 指挥者(Director) 产品(Product) 建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。 示例 适用场景: 当创建复杂对象的算法(Director)应该独立于该对象的组成部分(Builder)时 当构造过程允许被构造的对象有不同的表示时(不同Builder)。 优点: 隐藏了一个产品的内部结构和装配过程 将构造代码与表示代码分开 可以对构造过程进行更精细的控制 小结 使用 Abstract Factory(抽象工厂)、Prototype(原型模式) 或 Builder(建造者) 的设计甚至比使用 Factory Method(工厂方法) 的那些设计更灵活,但它们也更加复杂。通常,设计以使用 Factory Method(简单工厂也可以) 开始,并且当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。当你在设计标准之间进行权衡的时候,了解多个模式可以给你提供更多的选择余地。 也就是说,不用一开始就选好模式,先从简单的模式开始,如果需要频繁的改代码,就用工厂方法,等等…… 一步一步递进 依赖于继承的创建型模式:工厂方法模式依赖于组合的创建性模式:抽象工厂模式、创建者模式 结构型模式适配器模式将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。角色: 目标接口(Target) 待适配的类(Adaptee) 适配器(Adapter)(套壳) 两种实现方式: 类适配器:使用多继承 对象适配器:使用组合 示例 适用场景:想使用一个已经存在的类,而它的接口不符合你的要求(对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。 类适配器和对象适配器有不同的权衡。 类适配器 用一个具体的 Adapter 类对 Adaptee 和 Target 进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类 Adapter 将不能胜任工作。 使得 Adapter 可以重定义 Adaptee 的部分行为,因为 Adapter 是 Adaptee 的一个子类。 仅仅引入(继承)了一个对象,并不需要额外的指针以间接得到 adaptee。 对象适配器则 允许一个 Adapter 与多个 Adaptee-即 Adaptee 本身以及它的所有子类(如果有子类的话)一同时工作。Adapter 也可以一次给所有的 Adaptee 添加功能。 使得重定义 Adaptee 的行为比较困难。这就需要生成 Adaptee 的子类并且使得 Adapter 引用这个子类而不是引用 Adaptee 本身。 组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 二叉树的结构角色: 抽象组件(Component) 叶子组件(Leaf) 复合组件(Composite) 客户端(Client) 示例 适用场景: 表示对象的“部分-整体”层次结构(特别是结构是递归的) 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象 优点: 定义了包含基本对象和组合对象的类层次结构 简化客户端代码,即客户端可以一致地使用组合对象和单个对象 更容易增加新类型的组件 缺点: 很难限制组合中的组件 代理模式为其他对象提供一种代理以控制对这个对象的访问。角色: 抽象实体(Subject) 实体(RealSubject) 代理(Proxy) 示例 适用场景: 远程代理:为远程的对象提供代理 虚代理:根据需要创建很大的对象 保护代理:控制对原始对象的访问,用于对象有不同访问权限时 优点: 远程代理:可以隐藏对象位于远程地址空间的事实 虚代理:可以进行优化,例如根据要求创建对象 保护代理:允许在访问一个对象时有一些附加的内务处理 行为型模式责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 角色: 抽象处理者(Handler) 具体处理者(ConcreteHandler) 客户端(Client) 示例 例:请假部门批准:leader -> 部门经理 -> 总经理Javascript事件浮升机制 适用场景: 有多个对象可以处理一个请求,哪个对象处理由运行时决定 在不明确接收者的情况下,向多个对象中的一个提交一个请求 优点: 降低耦合度:一个对象无需知道是其他哪一个对象处理其请求 缺点: 请求不保证被接收:链的末端没有处理或链配置错误 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示 实现方法:__iter__、__next__ 示例 适用于封装数据结构,这种结构类似与列表,或树,封装数据类型,不让外人知道是怎么存的 观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式 角色: 抽象主题(Subject) 具体主题(ConcreteSubject)——发布者 抽象观察者(Observer) 具体观察者(ConcreteObserver)——订阅者 示例 适用场景: 当一个抽象模型有两方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立地改变和复用。 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。 优点: 目标和观察者之间的耦合最小 支持广播通信 缺点: 多个观察者之间互不知道对方存在,因此一个观察者对主题的修改可能造成错误的更新。 策略模式定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 角色: 抽象策略(Strategy) 具体策略(ConcreteStrategy) 上下文(Context) 示例 适用场景: 许多相关的类仅仅是行为有异 需要使用一个算法的不同变体 算法使用了客户端无需知道的数据 一个类中的多种行为以多个条件语句的形式存在,可以将这些行为封装如不同的策略类中。 优点: 定义了一系列可重用的算法和行为 消除了一些条件语句 可以提供相同行为的不同实现 缺点: 客户必须了解不同的策略 策略与上下文之间的通信开销 增加了对象的数目 模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 角色: 抽象类(AbstractClass):定义抽象的原子操作(钩子操作);实现一个模板方法作为算法的骨架。 具体类(ConcreteClass):实现原子操作 示例 适用场景: 一次性实现一个算法的不变的部分 各个子类中的公共行为应该被提取出来并集中到一个公共父类中以避免代码重复 控制子类扩展 总结设计模式可互相嵌套, 如: 策略模式的算法写成单例模式比较好; 创建型模式都是对象怎么创建 结构型模式是怎么把类组织在一起 适配器模式类写坏了, 和其他类不适配 组合模式是几个类怎么表现一样, 叶子节点和复合节点怎么表现一样 代理模式提供几种代理, 以控制加权限, 或加内容 行为型模式是怎么做事, 方法函数怎么做 责任链模式, 一个一个传下去 迭代器模式, 一个一个拿元素处理 (应用面狭小) 观察者模式, 一个一个更新 加深理解工厂模式是什么?有什么用?怎么用?什么好处? 哪个工厂模式,简单工厂模式,工厂方法模式还是抽象工厂模式。 简单工厂模式就是把所有产品的创建细节都隐藏在一个工厂里,也就是把要创建的这个类的对象的创建细节隐藏在工厂里,这就叫简单工厂。 问题:因为这个类所有的产品创建细节都隐藏在一个工厂里,那如果要加产品,就需要改工厂的代码,这个就不符合开闭原则。简单工厂的一个类,承载了很多产品的创建,所以不符合单一职责原则。就需要从简单工厂升级到工厂方法模式。 工厂方法模式就是是一个产品,一种产品的创建过程,它隐藏在一个单独的工厂里,每一个产品对应一个工厂,同样, 产品的创建过程隐藏在这个工厂里, 需要的注意的是:有多个工厂的时候,我们需要一个工厂的接口–抽象工厂,也就是工厂方法模式。工厂方法模式还是把对象的创建过程隐藏在了工厂里。它和之前简单工厂相比的话,一个产品对应一个工厂,加新产品的话,只需要再加一个工厂就可以了,不需要修改工厂代码。缺点就是加一个产品需要添加两类。类写的比较多,这是工厂方法模式。 抽象工厂模式和跟前面的两个就不太一样,它是生产一个产品系列,或者叫一套产品,生产一套产品的时候,一个工厂负责生产一套。好处: 第一,把对象的创建细节隐藏在工厂里, 第二, 可以保持产品系列的一致性,也就是加约束。 比如:苹果的IOS只能加苹果的手机壳,苹果CPU, 这就叫一致性,小产品之间的约束。 设计模式大全 创建型模式: 工厂方法模式 抽象工厂模式 创建者模式 原型模式 单例模式 结构型模式 适配器模式 桥模式 组合模式 装饰模式 外观模式 享元模式 代理模式 行为型模式 解释器模式 责任链模式 命令模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 访问者模式 模板方法模式]]></content>
<tags>
<tag>设计模式</tag>
</tags>
</entry>
<entry>
<title><![CDATA[设置django admin 显示为中文]]></title>
<url>%2F2018%2F01%2Fdjango-admin-cn.html</url>
<content type="text"><![CDATA[默认的 django admin 组件 显示为英文,设置 django admin 显示为中文很简单,只需在 settings.py 的 MIDDLEWARE_CLASSES 中添加一句 'django.middleware.locale.LocaleMiddleware' 即可。 如: 123456789MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.locale.LocaleMiddleware',) 注意: 'django.middleware.locale.LocaleMiddleware' 必须放在 'django.contrib.sessions.middleware.SessionMiddleware' 之后。 如果添加上面这句话后还是显示英文,则可能是浏览器语言设置问题,在浏览器语言设置中添加中文并放到首位试试。比如 Firefox 浏览器设置为:Firefox->Edit->Preferences-> Content->Languages 引用 https://my.oschina.net/means/blog/287753]]></content>
<tags>
<tag>django</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Docker 操作]]></title>
<url>%2F2018%2F01%2Fmy-docker-note.html</url>
<content type="text"><![CDATA[why? 更高效的利用系统资源 更快速的启动时间 一致的运行环境 持续交付和部署 更轻松的迁移 更轻松的维护和扩展 基本概念 镜像(Image) 容器(Container) 仓库(Repository) 理解了这三个概念,就理解了 Docker 的整个生命周期。 获取镜像从 Docker 镜像仓库获取镜像的命令是 docker pull。其命令格式为: 1docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] 如: 123456789$ docker pull ubuntu:16.0416.04: Pulling from library/ubuntubf5d46315322: Pull complete9f13e0ac480c: Pull completee8988b5b3097: Pull complete40af181810e7: Pull completee6f7c7e5c03e: Pull completeDigest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbeStatus: Downloaded newer image for ubuntu:16.04 运行1$ docker run -it --rm ubuntu:16.04 bash 说明一下上面用到的参数。 -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。 –rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 –rm 可以避免浪费空间。 ubuntu:16.04:这是指用 ubuntu:16.04 镜像为基础来启动容器。 bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。 进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat /etc/os-release,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 Ubuntu 16.04.4 LTS 系统。 启动容器启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。 因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。 新建并启动 无交互 12$ docker run ubuntu:14.04 /bin/echo 'Hello world'Hello world 允许交互 12$ docker run -t -i ubuntu:14.04 /bin/bashroot@af8bae53bdd3:/# 其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。 当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括: 检查本地是否存在指定的镜像,不存在就从公有仓库下载利用镜像创建并启动一个容器 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去 从地址池配置一个 ip 地址给容器 执行用户指定的应用程序 执行完毕后容器被终止 启动已终止容器可以利用 docker container start 命令,直接将一个已经终止的容器启动运行。 容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 ps 或 top 来查看进程信息。 1234root@ba267838cc1b:/# ps PID TTY TIME CMD 1 ? 00:00:00 bash 11 ? 00:00:00 ps 可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。 后台运行需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。可以通过添加 -d 参数来实现。 不使用 -d 参数运行容器。 容器会把输出的结果 (STDOUT) 打印到宿主机上面 使用了 -d 参数运行容器。 12$ docker run -d ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a 此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)。容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。 使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息。 123$ docker container lsCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES77b2dc01fe0f ubuntu:17.10 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright 要获取容器的输出信息,可以通过 docker container logs 命令。 12345$ docker container logs [container ID or NAMES]hello worldhello worldhello world. . . 终止容器可以使用 docker container stop 来终止一个运行中的容器。 此外,当 Docker 容器中指定的应用终结时,容器也自动终止。 用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。 终止状态的容器可以用 docker container ls -a 命令看到。例如 1234docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike 处于终止状态的容器,可以通过 docker container start 命令来重新启动。 重新启动1$ docker container restart 此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。 进入容器在使用 -d 参数时,容器启动后会进入后台。 某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令,因为使用 attach stdin 执行 exit 会导致容器停止。 attach 命令docker attach 是 Docker 自带的命令。下面示例如何使用该命令。 123456789$ docker run -dit ubuntu243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550$ docker container lsCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia$ docker attach 243croot@243c32535da7:/# exec 命令-i -t 参数docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。 只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。 当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。 12345678910111213141516$ docker run -dit ubuntu69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6$ docker container lsCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES69d137adef7a ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds zealous_swirles$ docker exec -i 69d1 bashlsbinbootdev...$ docker exec -it 69d1 bashroot@69d137adef7a:/# 导出和导入容器导出容器如果要导出本地某个容器,可以使用 docker export 命令。 1234$ docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test$ docker export 7691a814370e > ubuntu.tar 导入容器快照可以使用 docker import 从容器快照文件中再导入为镜像,例如 1234$ cat ubuntu.tar | docker import - test/ubuntu:v1.0$ docker image lsREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEtest/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB 此外,也可以通过指定 URL 或者某个目录来导入,例如 1$ docker import http://example.com/exampleimage.tgz example/imagerepo 注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。 删除删除容器12$ docker container rm trusting_newtontrusting_newton 如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。 清理所有处于终止状态的容器用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。 1$ docker container prune 常用操作1234$ docker --version # docker版本$ docker info # docker信息$ docker run --rm ubuntu:16.04 /bin/cat '/etc/os-release'$ docker run -it --name web --rm ubuntu:16.04 bash 停止容器12➜ docker stop myUbuntumyUbuntu 删除容器12➜ docker rm myUbuntumyUbuntu 列出镜像1234➜ docker images lsREPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 6b914bbcb89e 3 weeks ago 182 MBubuntu 14.04 7c09e61e9035 3 weeks ago 188 MB 列出部分镜像12$ docker image ls ubuntu$ docker image ls ubuntu:16.04 docker image ls 还支持强大的过滤器参数 –filter,或者简写 -f 12$ docker image ls -f since=mongo:3.2$ docker image ls -f 想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可。 如果定义了 LABEL,还可以通过 LABEL 来过滤 1label=com.example.version=0.1 以特定格式显示123$ docker image ls -q$ docker image ls --format "{{.ID}}: {{.Repository}}"$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" 删除镜像1234$ docker rmi 7c09e61e9035Untagged: ubuntu:14.04Untagged: ...$ docker image rm $(docker image ls -q redis) 查看容器修改内容1234$ docker diff webserverC /rootA /root/.bash_history... 查看docker进程12➜ ~ docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES nginx1$ docker run --name webserver -d -p 80:80 nginx 引用 http://blog.csdn.net/zhengyong15984285623/article/details/66971949 https://yeasy.gitbooks.io/docker_practice/]]></content>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[数据结构基础]]></title>
<url>%2F2017%2F12%2Fdata-structure-basis.html</url>
<content type="text"><![CDATA[概念数据结构是设计数据以何种方式组织并存储在计算机中。 比如:列表、集合与字典等都是一种数据结构。 详细的说: 物理层面:就是以什么样的物理存储方式 逻辑方式:列表,字典,集合,树 线性数据结构,树形数据结构,图形数据结构, 基本是线性数据结构 N.Wirth: “程序=数据结构+算法” 列表列表:在其他编程语言中称为“数组”,是一种基本的数据结构类型。如果更学术一点的说法是“线性表”。 数组与列表的不同之处:数组是定长的数组,如果开的内存空间长度为7,就不能再追加,只能存7个,并且这7个元素类型还必须是一样的 数组:定长,元素类型统一。 Python中的给一个列表,开一块连续内存空间,开的内存空间的长度不会刚好是列表的长度,一定会多,而内存中:变量指向的列表的元素存的是一个个内存地址(地址的格子才是真正存元素值的地方),每个元素占用的空间都是一样的,大部分编译型的语言直接存的值,而python存的不是值,而是值的内存地址。 Python中一直append值的原理:如果之前的空间不够,就再开时原来一倍的内存空间,然后把旧的删掉 列表li[2]寻址的时候是li+2*内存地址的字节 32位机器一个地址4个子节64位机器一个地址8个字节 关于列表的问题: 列表中元素使如何存储的?(上述) 列表提供了哪些基本的操作?(下标查找,插入,删除) 这些操作的时间复杂度是多少? O(1) O(n)(插入的时候,插入位置之后的值都需要往后挪) 链表链表中每一个元素都是一个对象,每个对象称为一个节点,包含有数据域key和指向下一个节点的指针next。通过各个节点之间的相互连接,最终串联成一个链表。 为什么用链表?链表的插入和删除特别快 节点定义: 1234567891011121314class Node(object): def __init__(self,item): self.item = item self.next = Nonen1 = Node(1)n2 = Node(2)n3 = Node(3)n1.next = n2n2.next = n3print(n1.next.item) # 2print(n1.next.next.item) # 3 头节点 链表的遍历 12345def traversal(head): curNode = head # 临时用指针 while curNode is not None: print(curNode.data) curNode = curNode.next 链表节点的插入和删除O1的时间复杂度 插入 12p.next = curNode.nextcurNode.next = p 删除 1234p = curNode.nextcurNode.next = curNode.next.next# 也可以 >> curNode.next = p.nextdel p 建立链表 头插法 1234567def createLinkList(li): l = Node() for num in li: s = Node(Num) s.next = l.next l.next = s return l 尾插法 1234567def createLinkList(li): l = Node() r = l # r 指向尾节点 for num in li : s = Node(num) r.next = s r = s 双链表双链表中每个节点有两个指针:一个指向后面节点、一个指向前面节点。 节点定义: 12345class Node(object): def __init__(self,item = None): self.item = item self.next = None self.prior = None 双链表节点的插入和删除 插入 1234p.next = curNode.nextcurNode.next.prior = pp.prior = curNodecurNode.next = p 删除 1234p = curNode.nextcurNode.next = p.nextp.next.prior = curNodedel p 链表分析列表与链表 按元素值查找(链表二分是On的复杂度) 按下标查找(链表没有法用下表查找,李列表:O1,链表On) 在某元素后插入(列表:On,链表:O1) 删除某元素(列表:On,链表:O1) 树也是以链表的形式存 栈栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。 特点: 后进先出(last-in, first-out)LIFO 概念: 栈顶 栈底 基本操作: 进栈(压栈):push 出栈:pop 取栈顶:gettop 应用实例: Word 的撤销操作(撤销的时候后边的操作) 重做 两个栈,撤销栈出栈,重做栈压栈;重做操作是记录撤销操作的。 栈的简单实现(Python)不需要自己定义,使用列表结构即可。 进栈函数:append 出栈函数:pop 查看栈顶函数:li[-1] 栈的应用 - 括号匹配问题给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。 ()()[]{} 匹配 ([{()}]) 匹配 []( 不匹配 [(]) 不匹配 代码补全,当栈是空的,栈才是合法的 代码实现括号匹配 队列队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。 进行插入的一端称为队尾(rear),插入动作称为进队或入队 进行删除的一端称为队头(front),删除动作称为出队 队列的性质:先进先出(First-in, First-out) 双向队列:队列的两端都允许进行进队和出队操作。 队列的实现用Python的列表来实现出队复杂度太高 123456使用方法:from collections import deque- 创建队列:queue = deque(li)- 进队:append- 出队:popleft- 双向队列队首进队:appendleft- 双向队列队尾进队:pop 实现原理 初步设想:列表+两个下标指针 创建一个列表和两个变量,front变量指向队首,rear变量指向队尾。初始时,front和rear都为0。 进队操作:元素写到li[rear]的位置,rear自增1。 出队操作:返回li[front]的元素,front自减1。 环形队列环形队列:当队尾指针front == Maxsize + 1时,再前进一个位置就自动到0。实现方式:求余数运算 队首指针前进1:front = (front + 1) % MaxSize 队尾指针前进1:rear = (rear + 1) % MaxSize 队空条件:rear == front 队满条件:(rear + 1) % MaxSize == front 代码实现 通过Python自带库 手写循环队列 123456789101112import queue # 线程同步######## 分割 ########from collections import dequequeue = deque()queue.append(1)queue.append(2)queue.append(3)print( ())print(queue.popleft())######## 分割 ########queue.appendleft(2)queue.pop() 哈希表Python中的集合与字典哈希表查找 哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。通过把每个对象的关键字k作为自变量,通过一个哈希函数h(k),将k映射到下标h(k)处,并将该对象存储在这个位置。 例如:数据集合{1,6,7,9},假设存在哈希函数h(x)使得h(1) = 0, h(6) = 2, h(7) = 4, h(9) = 5,那么这个哈希表被存储为[1,None, 6, None, 7, 9]。 当我们查找元素6所在的位置时,通过哈希函数h(x)获得该元素所在的下标(h(6) = 2),因此在2位置即可找到该元素。 12345li = [1,2,3,4]s = {1,2,3,4} # 集合的效率更高# 哈希函数设计的再好也避免不了碰撞## 哈希冲突哈希冲突:由于哈希表的下标范围是有限的,而元素关键字的值是接近无限的,因此可能会出现h(102) = 56, h(2003) = 56这种情况。此时,两个元素映射到同一个下标处,造成哈希冲突。 解决哈希冲突 拉链法(将所有冲突的元素用链表连接) 开放寻址法(通过哈希冲突函数得到新的地址) 字典在Python中的字典: 1a = {'name': 'Alex', 'age': 18, 'gender': 'Man'} 使用哈希表存储字典,通过哈希函数将字典的键映射为下标。假设h(‘name’) = 3, h(‘age’) = 1, h(‘gender’) = 4,则哈希表存储为[None, 18, None, ’Alex’, ‘Man’] 在字典键值对数量不多的情况下,几乎不会发生哈希冲突,此时查找一个元素的时间复杂度为O(1)。 迷宫问题给一个二维列表,表示迷宫(0表示通道,1表示围墙)。给出算法,求一条走出迷宫的路径。 栈 栈-方案 深度优先 DFS(Depth[栈]-First-Search) 队列 队列-方案 使用二维列表存储多条路径如何是打印出路径是难点 123456789101112maze = [ [1,1,1,1,1,1,1,1,1,1], [1,0,0,1,0,0,0,1,0,1], [1,0,0,1,0,0,0,1,0,1], [1,0,0,0,0,1,1,0,0,1], [1,0,1,1,1,0,0,0,0,1], [1,0,0,0,1,0,0,0,0,1], [1,0,1,0,0,0,1,0,0,1], [1,0,1,1,1,0,1,1,0,1], [1,1,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1]] 本文代码及 md 文件 Github]]></content>
<categories>
<category>数据结构</category>
</categories>
</entry>
<entry>
<title><![CDATA[TOP 榜单算法(nlargest)]]></title>
<url>%2F2017%2F12%2Ftop-list-algorithm.html</url>
<content type="text"><![CDATA[问题现在有n个数(n>10000),设计算法,按大小顺序得到前10m大的数。 应用场景:榜单TOP 10 解决方法 先排序,取前 10 个数 O(nlogn) 只留前 10 个数,开一个长度为 10 的列表,用插入排序取出 10 个数,来一个数和列表最后一个数比较,如果比它更小就扔掉 O(nm)不适用与 m 特别大的时候 堆 O(nlogm) 用堆解决思路: 取列表前m个元素建立一个小根堆。堆顶就是目前第m大的数。 依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整; 遍历列表所有元素后,倒序弹出堆顶。 12345678910111213141516171819202122232425262728293031323334353637def sift(data, low, high): """ 调整函数 data: 列表 low:待调整的子树的根位置 high:待调整的子树的最后一个节点的位置 """ i = low j = 2 * i + 1 tmp = data[i] # i指向空位置 while j<=high: #领导已经撸到底了 if j != high and data[j] < data[j+1]: j += 1 #j指向数值大的孩子 if tmp < data[j]: #如果小领导比撸下来的大领导能力值大 data[i] = data[j] i = j j = 2*i+1 else: break #撸下来的领导比候选的领导能力值大 data[i] = tmpdef topn(li, n): heap = li[0:n] # 建堆 for i in range(n // 2 - 1, -1, -1): sift(heap, i, n - 1) # 遍历 for i in range(n, len(li)): if li[i] > heap[0]: heap[0] = li[i] sift(heap, 0, n - 1) # 出数 for i in range(n - 1, -1, -1): heap[0], heap[i] = heap[i], heap[0] sift(heap, 0, i - 1) Python内置模块——heapq 12345678910111213import heapq# 利用heapq模块实现堆排序def heapsort(li): h = [] for value in li: heapq.heappush(h, value)# 建堆, 并自动排序 return [heappop(h) for i in range(len(h))]heapsort([6,8,1,9,3,0,7,2,4,5]) # [0,2,1,3,5,6,7,9,4,8]# ============== 分割线 ==============# 利用heapq模块实现取top-kheapq.nlargest(100, li) 优先队列:一些元素的集合,POP操作每次执行都会从优先队列中弹出最大(或最小)的元素。 堆——优先队列 参考 http://python.usyiyi.cn/translate/python_352/library/heapq.html 1234567# 位运算# >> 除以22 >> 1 # 1 4 >> 1 # 28 >> 1 # 4# << 乘以 22 << 1 # 4]]></content>
<categories>
<category>算法</category>
</categories>
</entry>
<entry>
<title><![CDATA[数据结构:树]]></title>
<url>%2F2017%2F12%2Fdata-structure-tree.html</url>
<content type="text"><![CDATA[树 是一种数据结构(如:目录结构) 是一种可以递归定义的数据结构 是由 n 个节点组成的集合 如果 n=0 ,那么是一颗空树 如果 n>0 ,那么存在 1 个节点作为树的根节点,其他节点可以分为 m 个集合,每个集合本身又是一棵树。 概念: 根节点(最顶端的节点)、叶子节点(没有孩子的节点,结构的最末端) 树的深度/高度(也就是树的层数) 节点度(也就是这个节点分了多少叉) 树的度(所有节点度的最大值) 孩子节点/父节点(看字面理解) 子树(根节点的字节点都是独立的树) 二叉树度不超过 2 的树(节点最多有两个叉),它的孩子是有顺序的:左孩子,右孩子。 重点:满二叉树,完全二叉树 二叉树的存储方式 链式存储方式 顺序存储方式(列表) 面向对象的存储方式 父节点和左孩子节点的编号下标有什么关系? 0-1 1-3 2-5 3-7 4-9规律:i = 2i+1 父节点和右孩子节点的编号下标有什么关系? 0-2 1-4 2-6 3-8 4-10规律:i = 2i+2 比如,我们要找根节点左孩子的左孩子:(0*2+1)*2+1 = 3 (下标) 所以是6 面向对象的存储方式1234567891011121314151617181920212223242526class BinTreeNode: def __init__(self, data): self.data = data self.lchild = None self.rchild = Nonek = BinTreeNode('K')g = BinTreeNode('G')c = BinTreeNode('C')a = BinTreeNode('A')b = BinTreeNode('B')d = BinTreeNode('D')e = BinTreeNode('E')f = BinTreeNode('F')h = BinTreeNode('H')root = aa.lchild = ba.rchild = eb.lchild = hb.rchild = ff.lchild = de.rchild = cc.lchild = kc.rchild = g 前序遍历 12345678910def PreBianli(root): p = root if p: print(p.data, end=' ') PreBianli(p.lchild) PreBianli(p.rchild)# PreBianli(root) # A B H F D E C K G 前序遍历 中序遍历 1234567def MidBianli(root): p = root if p: MidBianli(p.lchild) print(p.data, end=' ') MidBianli(p.rchild)# MidBianli(root) # H B D F A E K C G 中序遍历 后序遍历 1234567def PostBianli(root): p = root if p: PostBianli(p.lchild) PostBianli(p.rchild) print(p.data, end=' ')# PostBianli(root) # H D F B K G C E A 后序遍历 在根据任意两个序列来推测第三个序列的时候,有中序比较好推测,因为能一眼看出二叉树的根 层级遍历1234567891011121314def LevelBianli(root): curLevel = [root] nextLevel = [] while len(curLevel)>0: for node in curLevel: print(node.data, end=' ') if node.lchild: nextLevel.append(node.lchild) if node.rchild: nextLevel.append(node.rchild) curLevel = nextLevel nextLevel = []LevelBianli(root) # A B E H F C D K G 二叉树小结 二叉树是度不超过 2 的树 满二叉树与完全二叉树 (完全)二叉树可以用列表来存储,通过规律可以从父亲找到孩子或者孩子找到父亲 二叉树遍历方式 : 前序遍历 中序遍历 后序遍历]]></content>
<categories>
<category>数据结构</category>
</categories>
</entry>
<entry>
<title><![CDATA[递归]]></title>
<url>%2F2017%2F12%2Frecursion.html</url>
<content type="text"><![CDATA[在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。 特点: 调用自身 结束条件 1234567891011121314151617181920212223242526def func1(x): print(x) func1(x-1)# func1(3) # 死递归 没有结束条件def func2(x): if x > 0: print(x) func2(x+1)# func2(3) # 3,4,5,6... 有结束条件,如果是正数还是会陷入死递归def func3(x): if x > 0: print(x) func3(x-1)# func3(3) # 3,2,1 有结束条件def func4(x): if x > 0: func4(x-1) print(x)# func4(3) # 1,2,3 有结束条件 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出) 练习 123456789def func(depth): if depth == 0: print('我的小鲤鱼',end='') # 取消换行 else: print('抱着',end='') func(depth-1) print('的我',end='')func(3) # 抱着抱着抱着我的小鲤鱼的我的我的我 123456789101112131415161718192021# 利用递归函数计算阶乘# N! = 1 * 2 * 3 * ... * Ndef fact(n): if n == 1: return 1 return n * fact(n-1)print('fact(1) =', fact(1))print('fact(5) =', fact(5))print('fact(10) =', fact(10))# 利用递归函数移动汉诺塔:def move(n, a, b, c): if n == 1: print('move', a, '-->', c) else: move(n-1, a, c, b) move(1, a, b, c) move(n-1, b, a, c)move(4, 'A', 'B', 'C')]]></content>
<categories>
<category>算法</category>
</categories>
</entry>
<entry>
<title><![CDATA[常用排序算法]]></title>
<url>%2F2017%2F12%2Flist-algorithm.html</url>
<content type="text"><![CDATA[重点: 有序区 无序区 冒泡排序(BUB) 列表每两个相邻的数, 如果前边的比后边的大, 那么交换这两个数 冒泡排序算法的流程如下: 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 针对所有的元素重复以上的步骤,除了最后一个。 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 关键点: 趟, 无序区 code1234567891011121314# O(n²) 时间复杂度def bubble_sort(li): if len(li) <= 1: return li for i in range(len(li)-1): # i 是趟 for j in range(len(li)-i - 1): # j 是指针 if li[j] > li[j+1]: li[j], li[j+1] = li[j+1] , li[j] return lili = list(range(10000))import random as rdrd.shuffle(li) # 打乱顺序print(li)print(bubble_sort(li)) 优化版123456789101112def bubble_sort(li): if len(li) <= 1: return li for i in range(len(li) - 1): # i 是趟 exchange = Flase for j in range(len(li) - i - 1): # j 是指针 if li[j] > li[j + 1]: li[j], li[j + 1] = li[j + 1], li[j] exchange = True if not exchange: break return li 空间时间复杂度 O(1) 最坏时间复杂度 O(n²) 最优时间复杂度 O(n) 平均时间复杂度 O(n²) 选择排序(SEL)每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 123456789101112131415def select_sort(li): for i in range(len(li) - 1): # i 是趟 min_doc = i # 找i位置到最后位置范围内最小的数 for j in range(i, len(li)): # i可以换成i+1,省去和自己比 if li[j] < li[min_doc]: min_doc = j # 和无序区第一个数作交换 # 可以加上 i==min_loc 的判断,省去和自己换 if min_doc != i: li[min_doc], li[i] = li[i], li[min_doc] ''' i, j, min_doc 都是下标 ''' return li 空间时间复杂度 O(1) 最坏时间复杂度 O(n²) 最优时间复杂度 O(n²) 平均时间复杂度 O(n²) 插入排序(INS)插入排序每次取出数组后半部分的第一个元素,在排好序的前半部分中,为其找到最合适的位置并进行插入(扑克牌) 列表被分为有序区和无序区两个部分。最初有序区只有一个元素。 每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空。 插入排序算法的流程如下: 从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果该元素(已排序)大于新元素,将该元素移到下一位置 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置 将新元素插入到该位置后 重复步骤 2~5 关键点: 摸到的牌 手里的牌 (有序) code1234567891011def insert_sort(li): if len(li) == 1: return li for i in range(1, len(li)): # i代表每次摸到的牌的下标 tmp = li[i] j = i - 1 # j代表手里最后一张牌的下标 while j >= 0 and tmp < li[j]: # 摸到的牌比手牌最后的牌小 li[j + 1] = li[j] # 把最大的手牌往后挪动 j -= 1 li[j + 1] = tmp # 摸到的牌比手牌最后的牌大 return li 空间时间复杂度 O(1) 最坏时间复杂度 O(n²) 最优时间复杂度 O(n²) 平均时间复杂度 O(n²) 快速排序(QUI)博主看动图不是很理解, 建议看 这里 快速排序算法的流程如下: 取一个元素p(第一个元素),使元素p归位; 列表被p分成两部分,左边都比p小,右边都比p大; 递归完成排序。 关键点: 整理(让元素归位) 递归 1234567891011121314151617181920212223242526272829def partition(data, left, right): ''' partition:归位函数 右手左手一个慢动作 右手左手慢动作重播 ''' tmp = data[left] # 取基准数 while left < right: # 如果需要降序排序的话, 就把 data[right] >= tmp 中的小于等于改为大于等于 while left < right and data[right] >= tmp: right -= 1 # 左移 if left < right: # 如果上面的循环是因为找到了 right 小于 tmp 的数而跳出循环 data[left] = data[right] # 把小于 tmp 的这个元素放到 tmp 的位置上 # 如果需要降序排序的话, 就把 data[right] >= tmp 中的大于等于改为小于等于 while left < right and data[left] <= tmp: left += 1 # 右移 data[right] = data[left] # 把大于 tmp 的这个元素放到 tmp 的位置上 data[left] = tmp # 那个 mid 回来 return left def _quick_sort(data, left, right): if left < right: mid = partition(data, left, right) _quick_sort(data, left, mid - 1) _quick_sort(data, mid + 1, right)@cal_timedef quick_sort(data): return _quick_sort(data,0,len(data)-1) 优化版12345678910111213141516171819# 来自知乎 @风满楼def quick_sort(lists, left, right): if left > right: return lists low, high = left, right key = lists[left] # key即是基准数 while left < right: while left < right and lists[right] >= key: right -= 1 # 左移 lists[left] = lists[right] while left < right and lists[left] <= key: left += 1 # 右移 lists[right] = lists[left] lists[right] = key quick_sort(lists, low, left - 1) quick_sort(lists, right + 1, high) return listsquick(data,0,len(data)-1) 123456789# 快排精简版def quick(data): if data == []: return [] else: pivot = data[0] lesser = [x for x in data[1:] if x <= pivot] greater = [x for x in data[1:] if x > pivot] return quick(lesser) + [pivot] + quick(greater) 问题某些极端的情况下复杂度非常高, 如: 19 8 7 6 5 4 3 2 1 出现的概率不多, 属于极端情况, 解决方法: 选基准的时候随机选一个数与第一个数交换。 空间时间复杂度 根据实现的方式不同而不同 最坏时间复杂度 O(n²) 最优时间复杂度 O(nlogn) 平均时间复杂度 O(nlogn) PS: 看到一个最狠的快排 12# https://github.com/qiwsir/algorithm/blob/master/quick_sort.mdqs = lambda xs : ( (len(xs) <= 1 and [xs]) or [ qs( [x for x in xs[1:] if x < xs[0]] ) + [xs[0]] + qs( [x for x in xs[1:] if x >= xs[0]] ) ] )[0] 参考资料 Ele - A面 http://bbs.ahalei.com/thread-4419-1-1.html http://blog.csdn.net/v_july_v/article/details/6116297 https://www.zhihu.com/question/26786398 https://hellolynn.hpd.io/2017/08/03/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F-quick-sort/ https://github.com/qiwsir/algorithm/blob/master/quick_sort.md 堆排序(HEAP)堆排序用的是树的结构 堆 大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大 小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小 假设:节点的左右子树都是堆,但自身不是堆 当根节点的左右子树都是堆时,可以通过一次向下的调整来将其变换成一个堆。 堆排序过程: 建立堆 得到堆顶元素,为最大元素 去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序。 堆顶元素为第二大元素。 重复步骤3,直到堆变空。 构建堆 先从最小的子树开始看, 最后一步看整个的堆; 从最后一个非叶子节点为根的子树开始做调整 挨个出数 code12345678910111213141516171819202122232425262728293031323334def sift(data, low, high): """ 调整函数 data: 列表 low:待调整的子树的根位置 high:待调整的子树的最后一个节点的位置 """ i = low j = 2 * i + 1 tmp = data[i] # i指向空位置 while j<=high: #领导已经撸到底了 if j != high and data[j] < data[j+1]: j += 1 #j指向数值大的孩子 if tmp < data[j]: #如果小领导比撸下来的大领导能力值大 data[i] = data[j] i = j j = 2*i+1 else: break #撸下来的领导比候选的领导能力值大 data[i] = tmpdef heap_sort(data): n = len(data) # 建堆 从最后一个非叶子节点所以是 -1(2) 列表倒序 # n//2-1 找最后一个非叶子节点 # -1(1) 顾前不顾后 for i in range(n//2-1, -1, -1): sift(data, i, n - 1) # 这里的 n-1 是把所有子树的 high 都设置成整个堆的 high # 挨个出数 for high in range(n - 1, -1, -1): data[0], data[high] = data[high], data[0] sift(data, 0, high - 1) 空间时间复杂度 O(n),O(1) 最坏时间复杂度 O(nlogn) 最优时间复杂度 O(nlogn) 平均时间复杂度 O(nlogn) 引用 数据结构:树 https://www.cnblogs.com/chengxiao/p/6129630.html http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/ http://wuchong.me/blog/2014/02/09/algorithm-sort-summary/ 归并排序(MER)归并排序思路: 分解:将列表越分越小,直至分成一个元素。 一个元素是有序的。 合并:将两个有序列表归并,列表越来越大。 递归地将数组划分为两部分 直到两个子数组元素都为1时,返回并将两个数组进行排序融合 逐步返回,并递归融合,最终使得数组有序 code1234567891011121314151617181920212223242526272829def merge(data, low, mid, high): '''一次归并''' i = low j = mid + 1 ltmp = [] # 临时列表 while i <= mid and j <= high: if data[i] <= data[j]: ltmp.append((data[i])) i += 1 else: # data[i] > data[j] ltmp.append(data[j]) j += 1 while i <= mid: ltmp.append(data[i]) i += 1 while j <= high: ltmp.append(data[j]) j += 1 data[low:high + 1] = ltmpdef mergesort(data, low, high): '''归并排序''' if low < high: mid = (low + high) // 2 # 获取中间位置 mergesort(data, low, mid) # 分解左半部分 mergesort(data, mid + 1, high) # 分解右半部分 merge(data, low, mid, high) # 归并 return data 加深理解12345678def func(x): if x > 1: y = x // 2 func(y) func(y) print(y)func(20)# 看最后的输出 画图 或者结合递归 空间时间复杂度 O(n) 最坏时间复杂度 O(nlogn) 最优时间复杂度 O(n) 平均时间复杂度 O(nlogn) 快速排序、堆排序、归并排序 - 小结 三种排序算法的时间复杂度都是O(nlogn) 运行时间: 快速排序 < 归并排序 < 堆排序 三种排序算法的缺点: 快速排序 极端情况下排序效率低 归并排序 需要额外的内存开销 堆排序 在快的排序算法中相对较慢 计数排序(COU) 题: 现在有一个列表,列表中的数范围都在 0 到 100 之间,列表长度大约为 100 万。设计算法在 O(n) 时间复杂度内将列表进行排序。 12345678910111213141516171819def count_sort(data, maxnum = 100): '''计数排序 O(n)''' count = [0 for i in range(maxnum+1)] result = [] for i in data: count[i] += 1 for num,count in enumerate(count): for i in range(count): result.append(num)# 或def count_sort(data, max_num): count = [0 for i in range(max_num + 1)] for num in data: count[num] += 1 i = 0 for num, m in enumerate(count): for j in range(m): data[i] = num i += 1 因为要开额外的内存空间,所以使用并不多。计数排序限定元素不会太大的时候,如:年龄可以使用计数排序 希尔排序(SHE)希尔排序是一种分组插入排序算法。O(1.3n) 以数组元素长度的一半做为初始步长gap,将数组划分为gap个子数组 循环切换遍历子数组,在子数组内分别进行插入排序 将gap更新为gap/2,重复上述步骤1,2,直到gap为1 希尔排序思路: 先取一个正整数 d1(d1 < n),把全部记录分成 d1 个组,所有距离为 d1 的倍数的记录看成一组,然后在各组内进行插入排序 然后取 d2(d2 < d1) 重复上述分组和排序操作;直到取 di = 1(i >= 1) 位置,即所有记录成为一个组,最后对这个组进行插入排序。一般选 d1 约为 n/2,d2 为 d1 /2, d3 为 d2/2 ,…, di = 1。 希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序。 1234567891011121314151617# 修改插入排序def insert_sort_gap(data, gap): for i in range(gap, len(data)): tmp = data[i] j = i - gap while j >= 0 and tmp < data[j]: data[j + gap] = data[j] j = j - gap data[j + gap] = tmpdef shell_sort(data): '''希尔排序''' d = len(data) // 2 while d > 0: insert_sort_gap(data,d) d = d // 2 return data 优化版12345678910111213def shell_sort(data): n = len(data) gap = len(data) // 2 while gap > 0: for i in range(gap, n): tmp = data[i] j = i - gap while j >= 0 and tmp < data[j]: data[j + gap] = data[j] j -= gap data[j + gap] = tmp gap = gap // 2 return data 后记排序算法指标 排序的稳定性排序关键字相同的情况下,对象的相对位置不变 计时装饰器12345678def cal_time(func): def wrapper(*args, **kwargs): t1 = time.time() x = func(*args, **kwargs) t2 = time.time() print("%s running time %s secs." % (func.__name__, t2 - t1)) return x return wrapper 参考资料 博客部分图片截取自 https://visualgo.net/zh/sorting http://bubkoo.com http://chenyvehtung.github.io/2017/02/26/sort-algorithms.html 维基百科-排序算法]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hexo: ERROR Process failed: _posts/*]]></title>
<url>%2F2017%2F12%2Fhexo-ERROR-Process-failed-posts.html</url>
<content type="text"><![CDATA[文章的格式出现错误了 错误提示 1234$ hexo sERROR Process failed: _posts/*Error at ..... 原因 123title: Hexodate: 2017-12-13 12:21:33tags:Hexo #tags冒号后面应当有个空格,其他地方也应当注意 正确格式(加上那个空格就好了) 123title: Hexodate: 2017-12-13 12:21:33tags: Hexo 参考资料 http://shitaibin.github.io/2015/12/13/hexo-errors/]]></content>
<tags>
<tag>hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python:消息队列Rabbitmq基本使用]]></title>
<url>%2F2017%2F12%2Frabbitmq-apply.html</url>
<content type="text"><![CDATA[为什么用Rabbitmq instead of python queue ? 是因为python queue 不能跨进程 队列的作用: 1. 存储消息、数据 2. 保证消息顺序 3. 保证数据的交付 12345678910111213141516171819# 斐波那契数列1 1 2 3 5 8 13 ...# 启动rabbitmq,并验证启动情况 rabbitmq-server --detached &ps aux |grep rabbitmq# 以服务的方式启动service rabbitmq-server start# 启用维护插件rabbitmq-plugins enable rabbitmq_management # 重启service rabbitmq-server restart# erroepika.exceptions.ProbableAuthenticationError# 解决方法# 写入信息 并保存/关闭防火墙 'systemctl stop filewallf'vim /etc/rabbitmq/rabbitmq.config[{rabbit, [{loopback_users, []}]}].# 查看当前队列rabbitmqctl list_queues 基本使用实现发送端1234567891011121314151617181920212223import pika# 创建连接connection = pika.BlockingConnection(pika.ConnectionParameters('10.211.55.10'))channel = connection.channel()# 声明消息队列channel.queue_declare(queue='hello')# 发送消息到上面声明的hello队列,# 其中exchange表示交换器,能精确指定消息应该发送到哪个队列,# routing_key设置为队列的名称,# body就是发送的内容,channel.basic_publish(exchange='',routing_key='hello',body='Hello World!')print('[x] Sent "Hello World!"')# sh命令# 用 rabbitmqctl list_queues 查看队列'''Listing queueshello 1''' 接收端1234567891011121314151617181920212223import pika# 创建连接connection = pika.BlockingConnection(pika.ConnectionParameters('10.211.55.10'))channel = connection.channel()# 声明消息队列channel.queue_declare(queue='hello')# 接收消息 回调函数def callbcak(ch,method,properties,body): print("Received %r"%(body))# 告诉rabbitmq使用callback来接收信息channel.basic_consume(callbcak,queue='hello',no_ack=True)#开始接收信息,并进入阻塞状态,队列里有信息才会调用callback进行处理。按ctrl+c退出。channel.start_consuming()# 终端会阻塞住'''Received b'Hello World!'''' 工作队列消息不丢失生产者12345for i in range(5): msg = ' '.join(sys.argv[1:])or 'Hello World! %s' % time.time() channel.basic_publish(exchange='', routing_key='hello', body=bytes(msg,encoding='utf8'),) 消费者 no_ack=False 消费者退出不消息不丢失 12345# 修改回调函数def callbcak(ch, method, properties, body): print("Received %r" % (body)) time.sleep(5) print("[x] Done") 消息持久化 消息持久化存储, 虽然消息反馈机制,但是如果rabbitmq自身挂掉的话,那么任务还是会丢失。所以需要将任务持久化存储起来。声明持久化存储: 12# 原队列channel.queue_declare(queue='hello', durable=True) 但是这个程序会执行错误,因为hello这个队列已经存在,并且是非持久化的,rabbitmq不允许使用不同的参数来重新定义存在的队列。重新定义一个队列 12# 重新定义一个队列channel.queue_declare(queue='task_queue', durable=True) 在发送任务的时候,用delivery_mode=2来标记任务为持久化存储: 123456channel.basic_publish(exchange='', routing_key="task_queue", body=message, properties=pika.BasicProperties( delivery_mode = 2, # make message persistent )) 公平调度prefetch_count = 1 虽然每个工作者是依次分配到任务,但是每个任务不一定一样。可能有的任务比较重,执行时间比较久;有的任务比较轻,执行时间比较短。如果能公平调度就最好了,使用basic_qos设置prefetch_count=1,使得rabbitmq不会在同一时间给工作者分配多个任务,即只有工作者完成任务之后,才会再次接收到任务。 1channel.basic_qos(prefetch_count=1) new_task.py完整代码 发送者/生产者 123456789101112131415161718import pikaimport sysconnection = pika.BlockingConnection(pika.ConnectionParameters( host='10.211.55.10'))channel = connection.channel()channel.queue_declare(queue='task_queue', durable=True)message = ' '.join(sys.argv[1:]) or "Hello World!"channel.basic_publish(exchange='', routing_key='task_queue', body=message, properties=pika.BasicProperties( delivery_mode=2, # make message persistent ))print(" [x] Sent %r" % (message,))connection.close() worker.py完整代码 接受者/消费者 123456789101112131415161718192021import pikaimport timeconnection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.10'))channel = connection.channel()channel.queue_declare(queue='task_queue', durable=True)print(' [*] Waiting for messages. To exit press CTRL+C')def callback(ch, method, properties, body): print(" [x] Received %r" % (body,)) time.sleep(6) print(" [x] Done",ch.basic_ack(delivery_tag=method.delivery_tag))channel.basic_qos(prefetch_count=1)channel.basic_consume(callback, queue='task_queue')channel.start_consuming() 广播广播交换机的工作原理:消息发送端先将消息发送给交换机,交换机再将消息发送到绑定的消息队列,而后每个接收端都能从各自的消息队列里接收到信息。 消费者/接收端receive.py代码分析和最早的receive.py相比,主要是做了两个改动: 定义交换机 不使用hello队列了,随机生成一个临时队列,并绑定到交换机上 123456789101112131415161718192021222324import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters( '10.211.55.10'))channel = connection.channel()# 定义交换机channel.exchange_declare(exchange='messages', type='fanout')# 随机生成队列,并绑定到交换机上# 参数'exclusive=True'表示当接收端退出时,销毁临时产生的队列,这样就不会占用资源。result = channel.queue_declare(exclusive=True)queue_name = result.method.queuechannel.queue_bind(exchange='messages', queue=queue_name)def callback(ch, method, properties, body): print(" [x] Received %r" % (body,))channel.basic_consume(callback, queue=queue_name, no_ack=True)print(' [*] Waiting for messages. To exit press CTRL+C')channel.start_consuming() 执行rabbitmqctl list_queues 1234task_queue 0hello 5# 定义了交换机amq.gen-K0M17k_3LVYO0b7m0s-K1g 0 生产者/发送端send.py代码分析和最早的send.py相比,也只做了两个改动: 定义交换机 不是将消息发送到hello队列,而是发送到交换机 12345678910111213141516import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters( '10.211.55.10'))channel = connection.channel()# 定义交换机# type='fanout' 表示广播的意思channel.exchange_declare(exchange='messages', type='fanout')# 将消息发送到交换机# basic_publish方法的参数exchange被设定为相应交换机,# 因为是要广播出去,发送到所有队列,所以routing_key就不需要设定了。channel.basic_publish(exchange='messages', routing_key='', body='Hello World!')print(" [x] Sent 'Hello World!'")connection.close() exchange如果为空,表示是使用匿名的交换机,在上面交换机信息的图片中可以看到有amq.*这样的交换机,就是系统默认的交换机了。routing_key在使用匿名交换机的时候才需要指定,表示发送到哪个队列的意思。第一篇的例子演示了这个功能。 打开另外一个终端,执行send.py,可以观察到receive.py接收到了消息。如果有多个终端执行receive.py,那么每个receive.py都会接收到消息。 组播/路由生产者/send.py代码分析和广播相比,改动点主要在两个方面: 设定交换机的类型(type)为direct。上一篇是设置为fanout,表示广播的意思,会将消息发送到所有接收端,这里设置为direct表示要根据设定的路由键来发送消息。 发送信息时设置发送的路由键。 123456789101112131415161718192021import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters( '10.211.55.10'))channel = connection.channel()# 定义交换机,设置类型为directchannel.exchange_declare(exchange='messages', type='direct')# 定义三个路由键routings = ['warning', 'error']# 将消息依次发送到交换机,并设置路由键for routing in routings: message = '%s message.' % routing channel.basic_publish(exchange='messages', routing_key=routing, body=message) print(message)connection.close() 消费者/receive.py代码分析和广播相比,改动点主要在三个方面: 设定交换机的类型(type)为direct。 增加命令行获取参数功能,参数即为路由键。 将队列绑定到交换机上时,设定路由键。 12345678910111213141516171819202122232425262728293031import pika, sysconnection = pika.BlockingConnection(pika.ConnectionParameters( '10.211.55.10'))channel = connection.channel()# 定义交换机,设置类型为directchannel.exchange_declare(exchange='messages', type='direct')# 从命令行获取路由键参数,如果没有,则设置为inforoutings = sys.argv[1:]if not routings: routings = ['info']# 生成临时队列,并绑定到交换机上,设置路由键result = channel.queue_declare(exclusive=True)queue_name = result.method.queuefor routing in routings: channel.queue_bind(exchange='messages', queue=queue_name, routing_key=routing)def callback(ch, method, properties, body): print(" [x] Received %r" % (body,))channel.basic_consume(callback, queue=queue_name, no_ack=True)print(' [*] Waiting for messages. To exit press CTRL+C')channel.start_consuming() 打开两个终端,一个运行代码python receive.py info warning,表示只接收info和warning的消息。另外一个终端运行send.py,可以观察到接收终端只接收到了info和warning的消息。如果打开多个终端运行receive.py,并传入不同的路由键参数,可以看到更明显的效果。 当接收端正在运行时,可以使用rabbitmqctl list_bindings来查看绑定情况。 按规则发送/正则上面路由键/组播的功能,通过设置路由键,可以将消息发送到相应的队列,这里的路由键是要完全匹配,比如info消息的只能发到路由键为info的消息队列。 路由键模糊匹配,就是可以使用正则表达式,和常用的正则表示式不同,这里的话“#”表示所有、全部的意思;“*”只匹配到一个词。看完示例就能明白了。 send.py代码分析因为要进行路由键模糊匹配,所以交换机的类型要设置为topic,设置为topic,就可以使用#,*的匹配符号了。 12345678910111213141516171819202122import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters( '10.211.55.10'))channel = connection.channel()# 定义交换机,设置类型为topicchannel.exchange_declare(exchange='messages', type='topic')# 定义路由键# 四种类型的消息routings = ['happy.work', 'happy.life', 'sad.work', 'sad.life']# 将消息依次发送到交换机,并设定路由键for routing in routings: message = '%s message.' % routing channel.basic_publish(exchange='messages', routing_key=routing, body=message) print(message)connection.close() receive.py代码分析类型要设定为topic就可以了。从命令行接收参数的功能稍微调整了一下,没有参数时报错退出。 123456789101112131415161718192021222324252627282930313233import pika, sysconnection = pika.BlockingConnection(pika.ConnectionParameters( '10.211.55.10'))channel = connection.channel()# 定义交换机,设置类型为topicchannel.exchange_declare(exchange='messages', type='topic')# 从命令行获取路由参数,如果没有,则报错退出routings = sys.argv[1:]if not routings: # print(>> sys.stderr, "Usage: %s [routing_key]..." % (sys.argv[0],)) print(sys.stderr, "Usage: %s [routing_key]..." % (sys.argv[0],)) exit()# 生成临时队列,并绑定到交换机上,设置路由键result = channel.queue_declare(exclusive=True)queue_name = result.method.queuefor routing in routings: channel.queue_bind(exchange='messages', queue=queue_name, routing_key=routing)def callback(ch, method, properties, body): print(" [x] Received %r" % (body,))channel.basic_consume(callback, queue=queue_name, no_ack=True)print(' [*] Waiting for messages. To exit press CTRL+C')channel.start_consuming() 实验运行打开多个终端,分别传入不同的规则,观察结果如: 123python3 receive_topic.py "#"python3 receive_topic.py "happy.*"python3 receive_topic.py "*.work" 难点1、发送信息时,如果不设置路由键,那么路由键设置为”*”的接收端是否能接收到消息? 发送信息时,如果不设置路由键,默认是表示广播出去,理论上所有接收端都可以收到消息,但是笔者试了下,路由键设置为”*”的接收端收不到任何消息。 只有发送消息时,设置路由键为一个词,路由键设置为”*”的接收端才能收到消息。在这里,每个词使用”.”符号分开的。 2、发送消息时,如果路由键设置为”..”,那么路由键设置为”#.*”的接收端是否能接收到消息?如果发送消息时,路由键设置为一个词呢? 两种情况,笔者都测试过了,可以的。 3、”a.*.#” 和”a.#”的区别 “a.#”只要字符串开头的一个词是a就可以了,比如a、a.haha、a.haha.haha。而这样的词是不行的,如abs、abc、abc.haha。 “a..#”必须要满足a.的字符串才可以,比如a.、a.haha、a.haha.haha。而这样的词是不行的,如a。 远程结果返回RPCRemote Producre Call处理方法描述: 发送端在发送信息前,产生一个接收消息的临时队列,该队列用来接收返回的结果。其实在这里接收端、发送端的概念已经比较模糊了,因为发送端也同样要接收消息,接收端同样也要发送消息,所以这里笔者使用另外的示例来演示这一过程。 compute.py代码分析12345678910111213141516171819202122232425262728293031323334import pika# 连接rabbitmq服务器connection = pika.BlockingConnection(pika.ConnectionParameters( host='10.211.55.10'))channel = connection.channel()# 定义队列channel.queue_declare(queue='compute_queue')print(' [*] Waiting for n')# 将n值加1def increase(n): return n + 1# 定义接收到消息的处理方法def request(ch, method, properties, body): print(" [.] increase(%s)" % (body,)) response = increase(int(body)) # 将计算结果发送回控制中心 ch.basic_publish(exchange='', routing_key=properties.reply_to, body=str(response)) ch.basic_ack(delivery_tag=method.delivery_tag)channel.basic_qos(prefetch_count=1)channel.basic_consume(request, queue='compute_queue')channel.start_consuming() center.py代码分析123456789101112131415161718192021222324252627282930313233343536373839404142import pikaclass Center(object): def __init__(self): self.connection = pika.BlockingConnection(pika.ConnectionParameters( host='10.211.55.10')) self.channel = self.connection.channel() # 定义接收返回消息的队列 result = self.channel.queue_declare(exclusive=True) self.callback_queue = result.method.queue self.channel.basic_consume(self.on_response, no_ack=True, queue=self.callback_queue) # 定义接收到返回消息的处理方法 def on_response(self, ch, method, props, body): self.response = body def request(self, n): self.response = None # 发送计算请求,并声明返回队列 self.channel.basic_publish(exchange='', routing_key='compute_queue', properties=pika.BasicProperties( reply_to=self.callback_queue, ), body=str(n)) # 接收返回的数据 while self.response is None: self.connection.process_data_events() return int(self.response)center = Center()print(" [x] Requesting increase(30)")response = center.request(30)print(" [.] Got %r" % (response,)) 上面代码定义了接收返回数据的队列和处理方法,并且在发送请求的时候将该队列赋值给reply_to,在计算节点代码中就是通过这个参数来获取返回队列的。 相互关联编号correlation idcorrelation id运行原理: 控制中心发送计算请求时设置correlation id,而后计算节点将计算结果,连同接收到的correlation id一起返回,这样控制中心就能通过correlation id来标识请求。其实correlation id也可以理解为请求的唯一标识码。 示例内容: 控制中心开启多个线程,每个线程都发起一次计算请求,通过correlation id,每个线程都能准确收到相应的计算结果。 compute.py代码分析和上面相比,只需修改一个地方: 将计算结果发送回控制中心时,增加参数correlation_id的设定,该参数的值其实是从控制中心发送过来的,这里只是再次发送回去。代码如下: 123456789101112131415161718192021222324252627282930313233343536import pika# 连接rabbitmq服务器connection = pika.BlockingConnection(pika.ConnectionParameters( host='10.211.55.10'))channel = connection.channel()# 定义队列channel.queue_declare(queue='compute_queue')print(' [*] Waiting for n')# 将n值加1def increase(n): return n + 1# 定义接收到消息的处理方法def request(ch, method, props, body): print(" [.] increase(%s)" % (body,)) response = increase(int(body)) # 将计算结果发送回控制中心,增加correlation_id的设定 ch.basic_publish(exchange='', routing_key=props.reply_to, properties=pika.BasicProperties(correlation_id= \ props.correlation_id), body=str(response)) ch.basic_ack(delivery_tag=method.delivery_tag)channel.basic_qos(prefetch_count=1)channel.basic_consume(request, queue='compute_queue')channel.start_consuming() center.py代码分析控制中心代码稍微复杂些,其中比较关键的有三个地方: 使用python的uuid来产生唯一的correlation_id。 发送计算请求时,设定参数correlation_id。 定义一个字典来保存返回的数据,并且键值为相应线程产生的correlation_id。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667import pika, threading, uuid# 自定义线程类,继承threading.Threadclass MyThread(threading.Thread): def __init__(self, func, num): super(MyThread, self).__init__() self.func = func self.num = num def run(self): print(" [x] Requesting increase(%d)" % self.num) response = self.func(self.num) print(" [.] increase(%d)=%d" % (self.num, response))# 控制中心类class Center(object): def __init__(self): self.connection = pika.BlockingConnection(pika.ConnectionParameters( host='10.211.55.10')) self.channel = self.connection.channel() # 定义接收返回消息的队列 result = self.channel.queue_declare(exclusive=True) self.callback_queue = result.method.queue self.channel.basic_consume(self.on_response, no_ack=True, queue=self.callback_queue) # 返回的结果都会存储在该字典里 self.response = {} # 定义接收到返回消息的处理方法 def on_response(self, ch, method, props, body): self.response[props.correlation_id] = body def request(self, n): corr_id = str(uuid.uuid4()) # 产生 id self.response[corr_id] = None # 发送计算请求,并设定返回队列和correlation_id self.channel.basic_publish(exchange='', routing_key='compute_queue', properties=pika.BasicProperties( reply_to=self.callback_queue, correlation_id=corr_id, ), body=str(n)) # 接收返回的数据 while self.response[corr_id] is None: self.connection.process_data_events() return int(self.response[corr_id])center = Center()# 发起5次计算请求nums = [10, 20, 30, 40, 50]threads = []for num in nums: threads.append(MyThread(center.request, num))for thread in threads: thread.start()for thread in threads: thread.join() 参考资料http://blog.csdn.net/chenjiebin/article/details/8253433]]></content>
<tags>
<tag>rabbitmq</tag>
</tags>
</entry>
<entry>
<title><![CDATA[密码学基础]]></title>
<url>%2F2017%2F11%2Fbasis-of-cryptography.html</url>
<content type="text"><![CDATA[平常用的登录密码不叫密码,是登录口令 密码有一个加密和解密的过程 video: https://youtu.be/loJ62rvH8aE 古典密码凯撒密码123ABCCDE// 往后移动3位或者多位 非常容易破解 维吉尼亚密码 不知道密钥是非常非常难破解的 RSA 公钥 私钥 12明文--> 公钥 --> 密文密文--> 私钥 --> 明文 基于大数难分解两个质数相乘 12345678910111213p1 = 53p2 = 59n = 3127 # 53 * 59 = 3127# 欧拉函数Φ(n)(p1-1)*(p2-1)= 3016 # fai 小写 φe = 3 # e 和欧拉函数互质 只要互质随便取# 公钥是 n,ed = (k*φ(n)+1)/e = 2011 # 私钥 (2**3016+1)/3 = 2011# 验证m = 89 # 加密信息 c = (m**e)%n = 1394 # 密文 (89**3)%3127 = 1394明文 = (x**d)%n = 89 # 明文 (1394**2011)%3127 = 89 扩展: 费马小定理来验证.]]></content>
</entry>
<entry>
<title><![CDATA[django 跨域]]></title>
<url>%2F2017%2F09%2Fdjango-cors-headers.html</url>
<content type="text"><![CDATA[Setup1pip install django-cors-headers 12345INSTALLED_APPS = ( ... 'corsheaders', ...) 1234567# 在 csrf 之前MIDDLEWARE = [ # Or MIDDLEWARE_CLASSES on Django < 1.10 ... 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ...] Configuration12...CORS_ORIGIN_ALLOW_ALL = True 参考 django-cors-headers]]></content>
<tags>
<tag>django</tag>
</tags>
</entry>
<entry>
<title><![CDATA[列表查找]]></title>
<url>%2F2017%2F09%2Flist-search.html</url>
<content type="text"><![CDATA[顺序查找 从列表第一个元素开始,顺序进行搜索,直到找到为止。 12345678910li = [1,2,3]index(1) # 顺序查找# 顺序查找 ipython O(n) 复杂度import randomn = 10000li = list(range(n))random.shuffle(li)%timeit li.index(3200)#221 µs ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 二分查找 只能用于有序列表 从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。O(logn) 复杂度 12345678910111213141516171819202122232425262728293031323334353637# 二分查找# 循环版本# def bin_search(li,low,high):def bin_search(li, val): '''循环二分 时间复杂度 O(logn)''' low = 0 high = len(li) - 1 while low <= high: mid = (low + high) // 2 if li[mid] == val: return mid elif li[mid] < val: low = mid + 1 else: # > high = mid - 1 return None# 5.6 µs ± 441 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)# 递归版本 递归需要切换进出栈def bin_search_rec(data_set,value,low,high): ''' 尾递归 和 非递归的效率基本一样 ''' if low <= high: mid = (low+high) // 2 if data_set[mid] == value: return mid elif data_set[mid] > value: # 尾递归不用切换出栈 return bin_search_rec(data_set,value,low,mid-1) else: return bin_search_rec(data_set,value,mid+1,high) else: return# %timeit l1.index(3200)# l1.sort() # 排序# %timeit bin_search(l1,3200)print(bin_search(l1,4000)) 刷题:Letcode34. Search for a Range (二分查找升级版)1. Two Sum 习题1 34. Search for a Range (二分查找升级版) 12345678910111213141516171819def bin_search(li, val): '''循环二分 时间复杂度 O(logn)''' low = 0 high = len(li) - 1 while low <= high: mid = (low + high) // 2 if li[mid] == val: a = mid b = mid while li[a] = value and a >= 1: a -= 1 while li[b] = value and b < len(li): # b <= len(li) - 1 b += 1 return (a+1,b-1) elif li[mid] < val: low = mid + 1 else: # > high = mid - 1 return None 2 . 1. Two Sum 12345678910def two_sum(nums, target): l = len(nums) for i in range(l): for j in range(i+1,l): print(nums[i],nums[j]) if nums[i] + nums[j] == target: return (i,j) return None print(two_sum([2, 7, 11, 15],9)) 或者 1234567891011121314151617181920212223242526272829303132333435def bin_search(data_set, value): low = 0 high = len(data_set) - 1 while low <= high: mid = (low + high) // 2 if data_set[mid] == value: return mid elif data_set[mid] > value: high = mid - 1 else: low = mid + 1def two_sum_2(li, target): li.sort() for i in range(len(li)): b = target - li[i] j = bin_search(li, b) if j != None and i != j: return i, jprint(two_sum_2([2, 7, 11], 14))def two_sum_3(li, target): li.sort() # nlogn i = 0 j = len(li) - 1 while i<j: sum = li[i]+li[j] if sum > target: j-=1 elif sum < target: i+=1 else: #sum==target return (i,j) return None 扩展1. Two Sum 如果是 3 个数 就把第一个数固定, 后面的列表用 two_sum_3 来计算 如果这样时间复杂度nlogn + n²最终的时间复杂度是 n² 如果用二分查找, 就需要先排序, 定住两个数, 排序(nlogn) + 定住两个数(n²) 二分(n²logn) 最终的复杂度是 n²logn]]></content>
<categories>
<category>算法</category>
</categories>
</entry>
<entry>
<title><![CDATA[时间复杂度与空间复杂度]]></title>
<url>%2F2017%2F09%2Falgorithm-complexity.html</url>
<content type="text"><![CDATA[算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。用来评估算法运行效率的单位。 时间复杂度 时间复杂度是用来估计算法运行时间的一个式子(单位) T(n) = O(n^2) 按数量级递增排列,常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3),…, k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。 一般来说,时间复杂度高的算法比复杂度底的算法慢. 12345678910111213print('Hello World') # O(1)for i in range(n): # O(n) print('Hello World')for i in range(n): # O(n²) for i in range(n): print('Hello World')for i in range(n): # O(n³) for j in rnage(n): for k in range(n): print('Hello World') 如何一眼判断时间复杂度 循环减半的过程 –>O(logn) 几次循环就是n的几次方的复杂度 时间复杂度排序 常见的时间复杂度 1O(1)<O(logn)<O(n)<O(nlogn)<O(n²)<O(n²logn)<O(n³) 不常见的时间复杂度 1O(n!) O(2n) O(nn) … 几秒钟/O(1) 几分钟/O(n) 几小时/O(n²) // 2 平方 高级: 函数 见进阶 判断时间复杂度123456789101112131415# 1 O(1) 时间复杂度print('Hello World')print('Hello Python')print('Hello Algorithm')# 2 O(n²) 时间复杂度for i in range(n): print('Hello World') for i in range(n): print('Hello World')# 3 O(n²) 时间复杂度for i in range(n): for j in range(i): print('Hello World') 1234567# 以2为底64的对数n = 64while n > 1: print(n) n = n // 2# 时间复杂度 O(log2n) / O(logn)# 每次少一半 空间复杂度 用来评估算法内存占用大小的式子 S(n) = O(n^2) 表示内存占用时间复杂度比空间复杂度更重要 空间换时间 一个列表就是 O(n)二维列表复杂度就是 O(n²)…]]></content>
<categories>
<category>算法</category>
</categories>
</entry>
<entry>
<title><![CDATA[xadmin 基本使用]]></title>
<url>%2F2017%2F09%2Fxadmin-basic.html</url>
<content type="text"><![CDATA[下载最近的项目用django2,这里就用支持django2的版本1234# 指定 django2 分支git clone -b django2 https://github.com/sshwsfc/xadmin.git# orpip install git+git://github.com/sshwsfc/xadmin.git@django2 下载完成以后把xadmin这个目录拷贝到项目的根目录中。 配置 settings.py 1INSTALLED_APPS = [ ... 'xadmin', 'crispy_forms', ... ] urls.py 123import xadminurlpatterns = [ ... path('xadmin/', xadmin.site.urls), ] 注册 Model和Django Admin 一样的 1234567import xadminfrom app.model import exampleclass ExamplexAdmin(): passxadmin.site.register(example, ExampleXAdmin) 站点配置12345from xadmin import view# 创建xadmin的最基本管理器配置,并与view绑定 class BaseSetting(object): # 开启主题功能 enable_themes = True use_bootswatch = True # 全局修改,固定写法 class GlobalSettings(object): # 修改title site_title = 'XX管理平台' # 修改footer site_footer = 'XX有限公司' # 收起菜单 menu_style = 'accordion'# 将基本配置管理与view绑定 xadmin.site.register(views.BaseAdminView,BaseSetting) # 将title和footer信息进行注册 xadmin.site.register(views.CommAdminView,GlobalSettings) 菜单配置也就是App名称配置,和菜单组名称配置 app > apps.py 1from django.apps import AppConfig class MyAppConfig(AppConfig): name = 'myapp' verbose_name = 'App 名称' 菜单组名称__init__.py 1default_app_config = 'myapp.apps.MyappConfig']]></content>
<tags>
<tag>xadmin</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Charles: Response 出现乱码]]></title>
<url>%2F2017%2F09%2FCharles-Response-error.html</url>
<content type="text"><![CDATA[使用 Charles 抓 https 包的时候, Reponse 出现乱码 系统环境: MAC软件版本: 4.2.1浏览器: Google Chrome 原因没有信任 Charles 的证书,信任 Charles 的证书就好了 解决方法安装 Charles 证书Help > SSL Proxying > Install Charles Root Certificate 信任 Charles 证书安装完成后会自动弹出钥匙串,找到 Charles 的证书信任即可 Charles 设置Proxy > SSL Proxying Settings ok后记发布这篇文章的时候问题已经被解决,但是没有问题截图,有时间的话再补上.]]></content>
<tags>
<tag>charles</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux 初始化]]></title>
<url>%2F2017%2F09%2Flinux-init.html</url>
<content type="text"><![CDATA[IP配置静态IP 12# 编辑这个文件vim /etc/sysconfig/network-scripts/ifcfg-enp0s5 配置DNS1234# 修改DNS~ vim /etc/resolv.conf# Generated by NetworkManagernameserver 114.114.114.114 123456789101112131415161718192021TYPE=Ethernet # 协议BOOTPROTO=static #! dhcp(动态获取) 静态ipIPADDR=192.168.16.57 #! IP地址NETMASK=255.255.255.0 #! 子网掩码GATEWAY=192.168.16.254 #! 网关DNS1=192.168.16.254 #! DNSDEFROUTE=yesPEERDNS=yesPEERROUTES=yesIPV4_FAILURE_FATAL=noIPV6INIT=yesIPV6_AUTOCONF=yesIPV6_DEFROUTE=yesIPV6_PEERDNS=yesIPV6_PEERROUTES=yesIPV6_FAILURE_FATAL=noIPV6_ADDR_GEN_MODE=stable-privacyNAME=enp0s5UUID=151b7ea3-6585-49b4-8442-e25a9acfbeb6DEVICE=enp0s5 # 设备名称ONBOOT=yes # ! 开机自启动 SSH修改ssh服务配置文件1sudo vi /etc/ssh/sshd_config 12# 远程登录PermitRootLogin yes ifconfig1yum install -y net-tools]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[mysql 姿势]]></title>
<url>%2F2017%2F09%2Fmysql.html</url>
<content type="text"><![CDATA[MySql navivat for mysql BASIC123sudo apt-get install mysql-serverps aux | grep mysqldmysql -uroot -p CONF123# 配置文件sudo vim /etc/mysql/mysql.conf.d/mysqld.confbind-address = 127.0.0.1 # 这种情况外面是链接不进来的 0.0.0.0 123456# 授权grant all privileges on *.* to 'root'@'%';# 授权并设置密码grant all privileges on *.* to 'root'@'%' identified by '***';# mysql cmdflush privileges; # 立即刷新权限 MariadbMariaDB 配置文件位于 /etc/my.cnf 安装 1yum -y install mariadb mariadb-server 12345678# 启动systemctl start mariadb# 开机启动systemctl enable mariadb# 停止systemctl stop mariadb# 重启systemctl restart mariadb 123SELECT User, Host, Password FROM mysql.user; -- 查看所有用户SELECT DISTINCT User FROM mysql.user; -- 查看所有用户show grants for test; -- 查看用户权限 数据库操作忘记密码1234567# 启动免授权服务端 > shellmysqld --skip-grant-tables# 客户端 > shellmysql -u root -p# 修改用户名密码 > sqlupdate mysql.user set authentication_string=password('666') where user='root';flush privileges; 修改密码1234567-- method 1 mysql rootupdata user set password=password('新密码') where user= 'root';FLUSH PRIVILEGES; -- 将数据读取到内存中,从而立即生效-- method 2 mysql rootSET PASSWORD FOR root=PASSWORD('新密码');-- method 3 shellmysqladmin -u root -p password 新密码 添加用户123456789101112131415161718192021222324-- 创建用户create user '用户名'@'IP地址' identified by '密码';-- 删除用户;drop user '用户名'@'IP地址';-- 修改用户;rename user '用户名'@'IP地址' to '新用户名'@'IP地址';-- 修改密码set password for '用户名'@'IP地址' = Password('新密码')-- method 1-- 格式:grant select on 数据库.* to 用户名@登录主机 identified by “密码”-- 允许从所有地址访问 非常危险 grant select,insert,update,delete on *.* to test1@“%” Identified by “abc”;-- 密码abcgrant select, insert, update, delete on mydb.* to test2@localhost identified by “abc”;-- 无密码grant select, insert, update, delete on mydb.* to test2@localhost identified by “”;-- method 2create user 'alex'@'192.168.1.1' identified by '123123'; -- alex 只能通过192.168.1.1地址访问数据库create user 'alex'@'192.168.1.%' identified by '123123'; -- alex 只能在该网段内访问数据库create user 'alex'@'%' identified by '123123'; -- alex 可以从所有地址访问数据库delete from user where user='hzq' and host='192.168.11.%'; -- 删除用户 授权1234567-- 权限 人 grant select,insert,update on db1.t1 to 'alex'@'%';grant all privileges on db1.t1 to 'alex'@'%';revoke all privileges on db1.t1 from 'alex'@'%';show grants for '用户'@'IP地址' -- 查看权限grant 权限 on 数据库.表 to '用户'@'IP地址' -- 授权revoke 权限 on 数据库.表 from '用户'@'IP地址' -- 取消权限 12345对于目标数据库以及内部其他: 数据库名.* 数据库中的所有 数据库名.表 指定数据库中的某张表 数据库名.存储过程 指定数据库中的存储过程 *.* 所有数据库 123用户名@IP地址 用户只能在改IP下才能访问用户名@192.168.1.% 用户只能在改IP段下才能访问(通配符%表示任意)用户名@% 用户可以再任意IP下访问(默认IP地址为%) 基础命令12345678910111213141516171819show databases; -- 显示数据库列表use mysql; -- 打开库show tables; -- 显示库中的数据表describe t1; -- or desc t1; 显示数据表的结构create database db1; -- 新建库create database db2 default charset utf8; -- !!! 新建库,编码为utf8-- 创建库 编码为utf8 支持中文CREATE DATABASE db2 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;-- 创建库 gbk编码CREATE DATABASE db3 DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;drop database db1; -- 删库drop table t1; -- 删表delete from t1; -- 清空表,主键递增不会复位truncate table t1; -- 清空表,主键递增复位select * from t1; -- 显示表中的记录show warnings; -- 显示最后一个执行的语句所产生的错误、警告和通知show errors; -- 只显示最后一个执行语句所产生的错误show create table t10 \G; -- ?flush privileges; -- 将数据读取到内存中,从而立即生效。 创建表123456789101112131415161718192021-- 创建表create table t1(id int auto_increment,name char(10))engine=innodb default charset=utf8;-- innodb 支持事务,原子性操作-- myisam 存储更快些create table t1( 列名 类型 null, 列名 类型 not null, 列名 类型 not null auto_increment primary key, id int, name char(10) )engine=innodb default charset=utf8;-- 是否可以为空not null -- 不可为空null -- 可为空auto_increment -- 自增primary key; -- 表示 约束(不能重复且不能为空); 加速查找nid int not null defalut 2, -- 默认值 2 自增 自增,如果为某列设置自增列,插入数据时无需设置此列,默认将自增(表中只能有一个自增列) 1234567891011121314151617181920create table tb1( nid int not null auto_increment primary key, num int null)-- orcreate table tb1( nid int not null auto_increment, num int null, index(nid)) -- 主键-- 注意:-- 1、对于自增列,必须是索引(含主键)。-- 2、对于自增可以设置步长和起始值show session variables like 'auto_inc%'; -- 会话变量set session auto_increment_increment=2; -- 自增步长set session auto_increment_offset=10; -- 自增起始show global variables like 'auto_inc%'; -- 全局变量set global auto_increment_increment=2;set global auto_increment_offset=10; 主键 主键,一种特殊的唯一索引,不允许有空值,如果主键使用单个列,则它的值必须唯一,如果是多列,则其组合必须唯一。 12345678910create table tb1( nid int not null auto_increment primary key, num int null)-- orcreate table tb1( nid int not null, num int not null, primary key(nid,num) -- 多列主键) 外键 外键,一个特殊的索引,只能是指定内容 12345678910create table color( nid int not null primary key, name char(16) not null)create table fruit(nid int not null primary key,smt char(32) null ,color_id int not null,constraint fk_cc foreign key (color_id) references color(nid)) 修改表1234567891011121314151617181920-- 添加列:alter table 表名 add 列名 类型-- 删除列:alter table 表名 drop column 列名-- 修改列:alter table 表名 modify column 列名 类型; -- 类型alter table 表名 change 原列名 新列名 类型; -- 列名,类型-- 添加主键:alter table 表名 add primary key(列名);-- 删除主键:alter table 表名 drop primary key;alter table 表名 modify 列名 int, drop primary key;-- 添加外键:alter table 从表 add constraint 外键名称(形如:FK_从表_主表) foreign key 从表(外键字段) references 主表(主键字段);-- 删除外键:alter table 表名 drop foreign key 外键名称-- 修改默认值:ALTER TABLE testalter_tbl ALTER i SET DEFAULT 1000;-- 删除默认值:ALTER TABLE testalter_tbl ALTER i DROP DEFAULT;]]></content>
<tags>
<tag>mysql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python 包管理]]></title>
<url>%2F2017%2F08%2Fpython-package.html</url>
<content type="text"><![CDATA[pip12# 安装 pip3curl https://bootstrap.pypa.io/get-pip.py | python3 install123456# download get-pip.pywget https://bootstrap.pypa.io/get-pip.py --no-check-certificatepython get-pip.py# 查看 pip 版本pip -Vrm -rf get-pip.py 1234# -r 参数指定包列表文件pip install -r requirements.txt# 指定源pip install -r requirements.txt -i https://pypi.doubanio.com/simple easy_install支持 .exe 文件]]></content>
<tags>
<tag>python</tag>
<tag>pip</tag>
<tag>easy_install</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python 虚拟环境]]></title>
<url>%2F2017%2F08%2Fpython-virtual.html</url>
<content type="text"><![CDATA[virtualenvInstall1pip install virtualenv Create1234567# 创建虚拟环境virtualenv <名称>virtualenv venv# 指定 python 版本virtualenv -p python3 venv --no-site-packages# 不带包virtualenv venv --no-site-packages Load1234567source venv/Script/activatesource venv/bin/activate# windows./activate.bat# install packagepip install -r requirements.txtpip install -r requirements.txt -i https://pypi.doubanio.com/simple 1pip freeze -l > packages.txt Exit退出虚拟环境 1deactivate wrappervirtualenv虚拟环境管理 1234pip install virtualenvwrapperworkon # 列出 virtualenv 虚拟环境mkvirtualenv test # 新建虚拟环境mkvirtualenv --python=* test # 新建虚拟环境 Win AnacondaWindows环境下的Anaconda双版本共存,以及环境切换 windows下安装双版本anaconda Windows解决anaconda下双python版本安装TensorFlow Anaconda多环境多版本python配置指导 第二个版本安装在主版本的py+2或3, 并且安装的时候不要勾选两个选项 123456# 安装路径D:\Anaconda2\envs\py3\# 进入 python3activate py3# 退出deactivate py3 Anaconda 使用123456789101112131415161718# UPDATEconda update conda# 检查哪个版本的python可以被安装conda search --full --name python# 查看版本conda --version# 系统当前已有的Python环境conda info --envs# 创建指定的 python 环境conda create --name py3 python=3.6.5# 切换 python 环境activate py3source activate py3# EXITdeactivate py3source deactivate py3# 删除刚刚创建的虚拟环境conda remove --name py3 --all ps: 在Windos PowerShell中Anaconda无法进入切换的环境(2018/6/13) | 参考这里]]></content>
<tags>
<tag>python</tag>
<tag>virtualenv</tag>
<tag>anaconda</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Django rest framwork 快速使用]]></title>
<url>%2F2017%2F07%2Fdjango-rest-framwork.html</url>
<content type="text"><![CDATA[RESTful API : 面向资源编程 DEMO DDjango rest framework 123pip install djangorestframeworkpip install markdown # Markdown support for the browsable API.pip install django-filter # Filtering support look docs 快速使用123456789101112131415161718192021222324252627282930313233343536# step 1:注册 App -> setting.pyINSTALLED_APPS = [ ... 'rest_framework', # <---]# step 2:注册路由 api--->urls.py(单独App)from rest_framework import routersfrom . import viewsrouter = routers.DefaultRouter()router.register('users',Views.UserInfoViewSet)urlpatterns = [ # 登录功能 url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) url(r'^'),include(router.urls)),]# step 3:view.pyfrom app01.models import *from rest_framework.viewsets import ModelViewSetfrom rest_framework.serializers import HyperlinkedModelSerializer# Create your views here.class UsersSerializer(HyperlinkedModelSerializer): ''' rest api 数据定制 指定显示的数据 ''' class Meta: model = User fields = ('id','username') # 字段 # exclude = ('password',) # 除这个字段以外 depth = 0 # 0 <= depth <= 10 查询外键 0 只查自己的表 1 关联的下一层也会拿到 ...class UsersViewSet(ModelViewSet): queryset = User.objects.all().order_by('-id') serializer_class = UsersSerializer # 验证数据库操作 相当于 Form的功能 CBV urls.py 12345urlpatterns = [ url(r'user/$', views.UserView.as_view()), # CBV # url(r'user/(?P<pk>[0-9]+)/$',views.UserDetail.as_view()), # CBV url(r'user/(\d+)/$', views.UserDetail.as_view()), # CBV] view.py 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657from rest_framework.views import APIViewfrom app01 import serializersfrom rest_framework.parsers import JSONParser # json -> loadsclass UserView(APIView): def get(self, request, *args, **kwargs): """ 获取多条数据 """ data_list = User.objects.all() # 1. django 序列化 # from django.core import serializers # data = serializers.serialize('json',data_list) # return HttpResponse(data) # 2. rest framework 序列化 + form 验证 + 自定义 serializer = serializers.MySerializer(instance=data_list, many=True) # many -> 单条 or 多条 # return HttpResponse(json.dumps(serializer.data,ensure_ascii=False)) # 正常显示中文 return JsonResponse(serializer.data,safe=False,json_dumps_params={"ensure_ascii":False}) def post(self, request, *args, **kwargs): """ 创建数据 """ # old # print(request.data) # Blog.objects.create(**request.data) # rest framework data = JSONParser().parse(request) serializer = serializers.MySerializer(data=data) if serializer.is_valid(): # print(serializer.data) # print(serializer.errors) # print(serializer.validated_data) # 如果有 instance,则执行 update 方法;否则执行 create serializer.save() return HttpResponse('...')class UserDetail(APIView): def get(self, request, nid): # *args,**kwargs -> pk` """ 获取单条数据 """ obj = User.objects.filter(nid=nid).first() serializer = serializers.MySerializer(instance=obj) return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii": False}) def put(self, request, nid): # *args,**kwargs -> pk` ''' 修改单条数据 ''' obj = User.objects.filter(nid=nid).first() data = JSONParser().parse(request) serializer = serializers.MySerializer(instance=obj,data=data) if serializer.is_valid(): serializer.save() return HttpResponse(200) # TODO def delete(self, request, nid): # *args,**kwargs -> pk` """ 删除数据 """ obj = User.objects.filter(nid=nid).delete() return HttpResponse(status=204) new file serializers.py 123456789101112131415161718192021222324from rest_framework import serializersfrom app01 import modelsclass MySerializer(serializers.Serializer): nid = serializers.IntegerField(read_only=True) username = serializers.CharField(max_length=64) password = serializers.CharField(max_length=32) email = serializers.CharField(max_length=32) # 和 Form 表单一样 def validate_username(self, value): return value def validate_email(self, value): return value def update(self, instance, validated_data): instance.name = validated_data['name'] instance.user = validated_data['user'] instance.save() def create(self, validated_data): models.User.objects.create(**validated_data) 总结 2 个 url 5 个 方法 最多进行跨表查询,综合数据展示出来参考 http://www.cnblogs.com/wupeiqi/articles/7281134.html http://www.cnblogs.com/OldJack/p/7294222.html DRF doc123from rest_framework.documentation import include_docs_urls# urls.pyurl(r'docs/', include_docs_urls(title="example title")) Django View使用Django序列化数据123456from django.views.generic.base import View, ListView, DateDetailView, DeleteView, DatailView, TemplateView# django 序列化from django.forms.models import model_to_dictfrom dhango.core.serializers import serializejson_data = serialize('json', <query>)from django.http import JsonResponse DRF viewSerializer数据序列化,如果需要序列化外键的数据就再创建一个Serializer.1234567891011121314151617181920212223242526272829from rest_framework import serializersfrom snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICESclass SnippetSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(required=False, allow_blank=True, max_length=100) code = serializers.CharField(style={'base_template': 'textarea.html'}) linenos = serializers.BooleanField(required=False) language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python') style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly') def create(self, validated_data): """ Create and return a new `Snippet` instance, given the validated data. """ return Snippet.objects.create(**validated_data) def update(self, instance, validated_data): """ Update and return an existing `Snippet` instance, given the validated data. """ instance.title = validated_data.get('title', instance.title) instance.code = validated_data.get('code', instance.code) instance.linenos = validated_data.get('linenos', instance.linenos) instance.language = validated_data.get('language', instance.language) instance.style = validated_data.get('style', instance.style) instance.save() return instance ModelSerializer这个可以把所有的字段给省略掉,create,update也可以省掉 APIView数据列表,继承自View Using mixins | generics使用mixins,generics更高级的封装来更容易的写接口,继承generics这个文件里面的方法,可以只使用其中某一个功能,但是generics这个不论什么组合都不能少 mixins : 数据的操作 generics : http的操作 drf 就是这些的组合 自定义页码1234567from rest_framework.pagination import PageNumberPaginationclass ExamplePagination(PageNumberPagination): page_size = 10 page_size_query_param = 'page_size' page_query_param = 'p' max_page_size = 100 viewsets各种组合 mixins, GenericViewSet这个是推荐,常用的,仅限于 view class,它是通过路由绑定。更高级的封装 viewsets.GenericViewSet 12345class Example(mixins.ListModelMinxin, viewsets.GenericViewSet) '''GenericViewSet 没有继承 get post 方法,所以还是需要 mixins''' pass# 在 urls 里面来绑定函数 action# https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#binding-viewsets-to-urls-explicitly 分析 viewgenerics 里有各种组合view, 12345GenericViewSet(viewset) # drfGenericAPIView # drfAPIView # drfView # django# 越往后的 View 越底层 mixin12345CreateModelMixinListModelMixinUpdateModelMixinRetrieveModelMixinDestoryModelMixin req | repDRF 中的requests,response Filter1pip install django-filter Django Filter django filter docs 12345678910111213141516import django_filtersfrom app import model_nameclass ExampleFilter(django_filters.rest_framework.FilterSet): # 大于和小于 price_min = django_filters.NumberFilter(name='price', lookup_expr='gt') price_max = django_filters.NumberFilter(name='price', lookup_expr='lt') name = django_fileter.CharFilter(name='name', lookup_expr='icontains') class Meta: model = model_name fields = ['price_min', 'price_max', 'name']class ModelNameListViewSet(...): ... filter_class = ExampleFilter DRF Search Filter | Order Filter DRF Filter 12345678from rest_framework import filtersExampleDRFilter(...): ... filter_backends = (..., filters.SearchFilter) # 下面的可以加正则 search_fields = ('username', 'eamil',) ordering_field = ('age', 'add_time') HTTP12# 状态from rest_framework import status]]></content>
<tags>
<tag>django</tag>
<tag>rest_framwork</tag>
</tags>
</entry>
<entry>
<title><![CDATA[git 操作]]></title>
<url>%2F2017%2F06%2Fgit.html</url>
<content type="text"><![CDATA[分支123456789101112# 删除本地分支git branch -d bug# 删除远程分支git push origin --delete bug# 重命名分支git branch -m old new# 撤销 commitgit reset HEAD~# clone 指定分支git clone -b branchname https://**# 添加远程地址git remote add origin https://***.git stash 开发紧急模式,用来临时修复bug 123456# 保存到临时的地方git stash# 查看临时暂存的地方git stash list# 回归正常的开发git stash pop 创建标签123456789101112# 创建 taggit tag -a v1.6 -m 'msg: my version 1.4'# 查看 taggit tag# 提交git push origin v1.6# 提交所有 taggit push origin --tags# 删除本地 taggit tag -d v1.6# 删除线上 taggit push origin --delete v1.6 指定密钥文件位置1~/.ssh/config 1234# 不同的地址指定不同的私钥Host <domain>User <user>IdentityFile <rsa path> example 123Host github.comUser exampleIdentityFile ~/.ssh/example 12# linuxchmod 600 ~/.ssh/example other1git clone https://github.com/odoo/odoo.git --depth 1 --branch 11.0 --single-branch odoo11 解析:--depth 深度;--branch 分支;--single-branch 单一分支]]></content>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux 权限]]></title>
<url>%2F2017%2F05%2Flinux-permissions.html</url>
<content type="text"><![CDATA[系统中每个文件都拥有特定的权限、所属用户及所属组,通过这样的机制来限制哪些用户、哪些组可以对特定的文件进行什么样的操作。 每个进程都是以某个用户的身份运行,所以进程的权限与该用户的权限一样,用户的权限越大,该进程所拥有的权限也就越大。 12345drwxr-xr-x 22 hyhnm staff 748B 2 24 19:41 .drwxr-xr-x 9 hyhnm staff 306B 12 27 19:22 ..-rw-r--r--@ 1 hyhnm staff 913B 12 20 21:18 Charles-Response-error.md-rw-r--r--@ 1 hyhnm staff 895B 2 1 14:59 GV-python-api.md-rw-r--r--@ 1 hyhnm staff 1.0K 1 4 11:57 Phantomjs.md 12ll -dl /test # 查看指定目录权限ll a.txt # 指定文件权限 文件标识符drwxr-xr-x d代表的是目录-rwxr-xr-x -代表的是普通文件lrwxr-xr-x l代表的是符号链接(快捷方式)crw-rw-rw- c代表的是专门设备文件brw-r----- b代表的是块专门设备文件srwxr-xr-x s代表的是套接字prwxr-xr-x p代表的是命名管道文件 权限标识权限三个一组(rwx),对应 UGO 分别设置(总共有 3 个组 9 个权限) 可读 r 可写 w 可执行x 4 2 1 rwx rwx rwx = 777 rwx rwx rwx 属主 U 属组 G 其他人 O Linux 权限基于 UGO 模型进行控制:U 代表 User,是文件或文件夹所属用户的权限;G 代表 Group,是文件或文件夹所属组的权限;O 代表 Other,是其他用户对文件或文件夹的权限 修改权限命令 chmod 用以修改文件的权限: 示例:chmod 777 mode 参数格式如下: 12345u、g、o 分别代表用户、组、其他a 代表ugo+、- 代表加入或删除对应权限r、w、x 代表三种权限-R 递归地修改 示例: 12345chmod u +rw test.md 给文件的所属用户添加rw权限chmod g -x test.md 给文件的所属组移除x权限chmod go +r test.md 给文件的所属组和其他用户添加r权限chmod a -x test.md 给文件的所属UGO三个模型均移除x权限chmod u=rw,g=-,o=rwx test.md 命令 chmod 也支持以三位八进制数值的方式修改权限,rwx 权限值分别由数字表示如下: 123r = 4 (2 ^ 2)w = 2 (2 ^ 1)x = 1 (2 ^ 0) 使用数字表示权限时,每组权限分别为对应数字之和: 123rw = 4 + 2 = 6rwx = 4 + 2 + 1 = 7r-x = 4 + 1 = 5 所以,使用数字表示 UGO 权限时,可以用如下方式表示: 12chmod 0660 test.md 设置 UGO 权限为 rw-rw----chmod 0775 test.md 设置 UGO 权限为 rwxrwxr-x 修改文件所属用户和组命令 chown 用以改变文件的所属用户: 1chown username filename 常用参数: 1-R 归地修改目录下所有文件的所属用户 命令 chgrp 用以改变文件的所属组: 1chgrp groupname filename 常用参数: 1-R 递归地修改目录下所有文件的所属组 实例如何修改文件的拥有者的权限为可读可写不可执行? 1chmod u=rw <file name> 总结 文件 read:查看文件内容 write:修改文件内容 x:执行文件(把文件当做命令一样执行) 目录 read:浏览目录下的子目录名,子文件名 write:创建、重命名、删除主目录、子文件 x:可以 cd 目录(有这个权限才可以修改目录下面的文件) 参考 http://blog.sina.com.cn/s/blog_81aecb810102waaq.html http://blog.csdn.net/qq_36221862/article/details/56012469 https://www.cnblogs.com/mingc/p/7591287.html http://blog.csdn.net/fan_zhen_hua/article/details/2050009]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux bash]]></title>
<url>%2F2017%2F05%2Flinux-shell.html</url>
<content type="text"><![CDATA[man builtin 查看内置命令source 执行 sh 文件unset 卸载函数alias 别名unalias 卸载别名hash 缓存命令路径touch {1..10}.txt 建立 10 个文件 12345678function test() { # 交互,输入的字符赋值给 name read -p 'please input your hostname' name # 设置主机名为 name 的值,shell 中 $name 来使用变量 hostnamectl set-hostname $name; # 查看主机名 hostname;} 元字符\ 取命令的执行结果12345ls# anaconda-post.log bin dev etc home lib lib64 media mnt opt proc Python-3.6.1res=`ls` # 把 ls 命令的结果赋值给 resecho $res # 显示 res 这个变量的值# anaconda-post.log bin dev etc home lib lib64 media mnt opt proc Python-3.6.1 $() 同 `(两个) 弥补缺憾12345res=`echo `ls`` #嵌套使用后无法达到预想的效果:取echo 一堆文件名的效果。echo $res # lsres=$(echo $(ls)) #替代方案echo $res# anaconda-post.log bin dev etc home lib lib64 media mnt opt proc Python-3.6.1 ~家目录12cd ~pwd # /root [ ] 字符通配,匹配括号内之一1234ls [abc].txt # a.txt B.txt c.txtls [abc][a].txt # aa.txtls [a-z].txt # A.txt ... z.txtls [a-Z][0-9][a-x] # a1C.txt { } 范围 括号内的开头和结尾必须是空格 12345touch {1..10}.txt# 10.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txttouch {a..z}.txt#a.txt c.txt e.txt g.txt i.txt k.txt m.txt o.txt q.txt s.txt u.txt w.txt y.txt#b.txt d.txt f.txt h.txt j.txt l.txt n.txt p.txt r.txt t.txt v.txt x.txt z.txt ! 非,取反12ls [!0-9].txta.txt b.txt c.txt d.txt e.txt f.txt $? 上一条码命令是否执行成功1234lsecho $? # 0 执行成功sssecho $? # 127 $1234$ # 变量echo $resecho $[1+10] # 11echo $[10%3] # 1 取模 & 后台运行 && 与运算 固化命令的方式1234567# 执行顺序/etc/profile # 1 环境变量/etc/profile.d/<可执行文件> # 2/root/.bash_profile # 3 当前用户家目录下/root/.bashrc # 4/root/bashrc # 5/etc/bashrc # 6 正则表达式grep-n 显示行号-o 只显示匹配的内容-i 忽略大小写-q 静默-v 取反,其它行-w 单词-l 过滤成功显示文件路径-A 显示匹配成功后的几行 <要加参数 n>-B 显示匹配成功前的几行 <要加参数 n>-C 显示匹配成功的附近几行 <要加参数 n>-c 显示匹配成功的数目 1234567891011grep 'root' /etc/passwd#root:x:0:0:root:/root:/bin/bash#operator:x:11:0:operator:/root:/sbin/nologingrep -i 'hello' a.txt# 正则grep 'bash$' /etc/passwd # bash 结尾grep '^root' /etc/passwd # root 开头grep '^b.n' /etc/passwd # b 开头 中间任意一个字符 n 结尾的egrep -e 'he' -e 'ha' a.txt # egrep 'h(e|a)' a.txtgrep -r 'root' /test # 查找包含 root 内容文件及内容显示出来grep -r 'root' /test # 查找包含 root 文件的文件路径 注:最好用 egrep 参考 http://www.cnblogs.com/OldJack/p/6607155.html http://www.cnblogs.com/linhaifeng/p/6592572.html sed流式编辑器 -n 静默模式,不输出-e 指定多个规则,同grep-i 对文件直接进行修改 文件形式规则pattern.sed记录规则 123456789101112131415161718192021222324252627282930# 核心sed '' test # 没有修改sed -n '' test # 静默模式,不输出sed -f pattern.sed test # 文件形式sed '3d' test # 删除第 3 行sed '1,3d' test # 删除 1 2 3 行sed '1d;3d;' test # 删除 1 行和 3行sed -n '3p' test # 打印第三行sed '3c 111' test # 修改第三行为 111sed '3a 222' test # 追加一行 222sed '3i 333' test # 在第 3 行前面插入 333# 正则sed '/^root/d' /etc/passwd # 删除以 root 开头的行sed '/ai$/d' test # 删除以 ai 结尾的行sed '/ai/d' test # 删除有 ai 的行sed '/ai.*$/d' test # 同上sed '/^s/c 1111' test # 把 s 开头的行改为 1111# 常用sed 's/root/sb/' test # 匹配所有的 root 改为 sb;如果一行有多个,只改一个sed 's/root/sb/g' test # 匹配所有的 root 改为 sb;更改多个sed '/blex/s/sb/SB/g' test # 把blex 这一行的 sb 改为 SB# 所有 sed 都使用 sed -r# 把 blex 这一行的 sb 改为 SB sed -r '/^[0-9][a-Z]xsb$/s/sb/SB/g' textsed -r '/^[0-9]([a-Z]+)xsb$/s/sb/SB/g' text # 效果同上sed -r '/^[0-9]([a-Z]{3})xsb$/s/sb/SB/g' text # 效果同上# 正则 ()部分概念 \1\2 正则匹配多个部分概念sed -r 's/^([a-Z]+)([^a-Z])/\2/g' /etc/passwd # 匹配第一个单词并去掉sed -r 's/([^a-Z])([a-Z]+)$/\1/g' /etc/passwd # 匹配最后一个单词sed -r 's/^([a-Z])([^a-Z]+)([a-Z])([^a-Z]+)/\3\2\1\4/g' # 第一个单词和第二个单词换位置 注意: 正则分多个部分 不一定非得 /// 只要是无意义的三个(如:###) awk-F 执行分隔符,默认为空格或多个空格NF 分成几段内容$NF 取最后一段内容NR 行号 12345678910111213141516171819202122awk -F: '{print $1}' /etc/passwd # 取第一个部分# $0 是取所有awk -F: '{print $1,NF}' /etc/passwd# root 7awk -F: '{print $NF}' /etc/passwd # 取最后一段内容awk -F: 'NR==1{print $1,NR}' /etc/passwd # 行定位 只显示第一行# root 1awk -F: 'NR<=3{print $1,NR}' /etc/passwd # 只要前 3 行awk -F: 'NR<=3{print $1,"---",NR}' /etc/passwd # 小段内容之间的分隔# andawk -F: 'NR>=3 && NR<=5{print $1,"---",NR}' /etc/passwd # 取几行到几行之间的# orawk -F: 'NR<=2 || NR>=7{print $1,"---",NR}' /etc/passwd# 正则定址awk -F: '/nologin$/{print $1}' /etc/passwd # 获取所有不能登录的用户名awk -F: '$1~/^r.*t$/{print $3}' /etc/passwd # 某一段内容来匹配正则awk -F: '$1=="root"/^r.*t$/{print $3}' /etc/passwd # 第一段内容匹配 rootawk -F: '$3>=7{print NR,$1}' /etc/passwd # uid 大于 7 的用户名# 通过变量类传递count=7awk -v x=$count -F: '$3>=x{print NR,$1}' /etc/passwd # 活 的代码 示例 1234# 获取网卡地址ifconfig | awk 'NR==2{print $2}' # 172.17.0.2ip=`ifconfig | awk 'NR==2{print $2}'`echo $ip # 172.17.0.2 shell 脚本 系统变量 env 系统环境变量set 所有变量PS1 shell 显示的用户 1export money=1000 # 定义全局变量 变量 12345678varname = value # 声明变量echo $varanme # 显示变量值unset varname # 删除变量money=10echo $moneyecho ${money}0000 # 100000 # 变量边界测试命令 test [][ -d /etc ] # echo $? 查看结果 if12345678910111213141516171819#!/bin/bashvar='/etc/passwd'# 接收用户输入# read -p 'please input you file path:' varif [ -f $var ] then echo "$var is regular file"elif [ -b $var] then echo "$var is block"elif [ -d $var ] then echo "$var is direcotry"elif [ -h $var ] then echo "$var is symlink"else echo "$var is unkown"fi while1234567# 不停的看系统内存状态while :do free sleep 0.5 cleardone 123456count=1while [ $count -le 10]do echo $count ((count++))done 12345678910while :do read -p 'username' name read -p 'passwd' pwd if [ $name = 'hehe' -a $pwd = '123' ] then echo 'login successful,welcome' breakdoneecho 'END' for1234for i in {1..100}do echo $idone ?shell 并发线程 123456789# 检测可用ipfor i in {1..253}do ping -c1 192.168.1.$i &> /dev/null if [ $? -ne 0] then echo "192.168.1.$i" fidone case1234567891011121314151617181920ead -p "username: " -t 5 unameechoif [[ -z $uname ]]thenuname=defaultficase $uname inroot)echo "welcome $uname";;seker)echo "welcome $uname";;default)echo "welcome $uname";;*)echo "no user $uname"esac 函数12345678910function abc(){ echo 'aaa'; echo 'bbb';}function start(){ echo '----' res=$[1+2] return $res}start # 3 脚本接收参数1234567#!/bin/bashecho $1echo $2echo $3echo $4echo $5echo ${10} 123456789101112131415161718192021222324#test.shecho $0echo $1echo $2echo $3echo ${11}echo '$$' $$echo '$*' $*echo '$@' $@echo '$#' $#echo '$?' $?'''测试:python test.sh 1 2 3 4 5 6 7 8 9 10 11输出结果:./test.sh211$$ 14312$* 1 2 3 4 5 6 7 8 9 10 11$@ 1 2 3 4 5 6 7 8 9 10 11$# 11$? 0''' 示例 nginx 进程自动重启 123456#!/bin/bashps aux | grep nginx | grep -v 'grep'if [ $? -ne 0] then systemctl start nginxfi 计划任务1systemctl status crond 参考 http://www.cnblogs.com/linhaifeng/p/6602149.html]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux 进程管理]]></title>
<url>%2F2017%2F05%2Flinux-process.html</url>
<content type="text"><![CDATA[123456less | ps auxkill -9 <进程ID>pkill -9 <firefox># 工作号jobskill -9 %1 # 结束某个工作号 pstree 查看进程树 pgrep 只获取 PID]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux 基础命令]]></title>
<url>%2F2017%2F05%2Flinux-cmd.html</url>
<content type="text"><![CDATA[基础命令linux 命令分为三部分:命令本身 选项 参数 12345678910111213141516171819202122232425262728293031lsof -i:8000 # 查看端口占用whowhoamidatecalclearsupasswdman # 帮助passwd --help # 查看帮助ls -dl /tmp # 查看目录本身的详细信息 !cat # 执行此命令上一次的执行echo 123 > /tmp/a.txt # 覆盖写echo 123 >> /tmp/b.txt # 追加写dffirefox & # 后台运行程序echo /etc/passwd &> /dev/null # 定向到黑洞文件cat a.txt | sort | uniq # 去重cat a.txt | sort | uniq -c # 查看去了多少重cat /etc/passwd | cut -d: -f1,3 # 指定分隔符 :,输出第 1,3 部分du -sh /boot/ # 62M /root 查看文件夹大小# findfind / -type f # 查找普通文件find / -name '*.txt' # 查找以 txt 后缀的文件find / -size +30M # 找大小大于 30M 的文件find / -size -30M # 找大小小于 30M 的文件find / -size +10M -size -30M # 大于 10M 小于 30M 的文件find / -size +2M -type f -name \*.txt# 创建一个指定类型的文件dd if=/dev/zero of=/a.txt bs=20M count=1echo -e "asdf\nwerwer" # 解析换行符 tar(打包)在 linux 系统中,文件的后缀名没有任何特殊意义 1234567891011121314151617181920212223242526touch a.txt b.txt c.txt# 打包 c->新建 f->文件名 v->打包过程tar cvf text.tar a.txt b.txt c.txt# 查看 tar 文件内容 -t# f-> 查看哪一个文件tar -tf test.tar# 解包 x->解 f->需要解包的文件 v->解包过程# 默认为当前目录tar xvf test.tar# 解压缩 -C(大写)指定解压目录tar xvf test.tar -C /bak # 解压到指定目录# ------ 压缩 gzip(效率) -------gzip a.txt # 压缩gunzip a.txt.gz # 解压缩# 打包并压缩tar cvzf test1.tar.gz a.txt b.txt c.txt# 解压缩包tar xvf test1.tar.gz -C /test1# ------ 压缩 bzip2(质量,压缩率高) -------bzip2 a.txt# 打包并压缩tar cvjf test2.tar.bz2 a.txt b.txt c.txt# 解压缩包tar xvf test1.tar.gz -C /test2 打包文件夹不带目录名,只打包下面的文件 1234# 需要先切换到需要打包的文件夹下面# *->当前目录下面的所有文件tar czf /tmp/etc_bak.tar.gz * # 打包tar xf etc_bak.tar.gz -C /etc_bak 硬盘满的话有两种情况 一个文件太大占满了 都是小文件,太小了(一个萝卜一个坑) --stdin123echo 123 | passwd --stdin root# --stdin 是将 echo 显示的结果作为下一个命令的输入,root 这里指的是修改哪一个用户的密码# 回车后直接把用户名密码改掉了 vivim(vi modiy) 是 vi 的增强版本 a 编辑模式(append模式):当前位置后移一位开始编辑i 插入模式(insert模式):在当前位置插入o 新建行插入O 基于上一行开始写(当前位置的后面整体后移)$ 当前行的结尾0 当前行头部dd 删除行 n ddD 删除空行dd p 剪切P 当前行的上一行yy p 复制到当前行的下一行 n yyu 撤销操作control + r 恢复操作G 文件的最后一行n G 跳到指定行gg 文件的首行H 当前屏幕的头部M 当前屏幕中间L 当前屏幕的最后一行 硬盘 fdisk (小于2T的硬盘) parted (大于2T) 12345678df -hls /dev/sdafdisk /dev/sdb # 分区 sdb 硬盘设备# 格式化磁盘mkfs.ext3 /dev/sdb1 # 格式化主分区为ext3系统mkfs.ext4 /dev/sdb5 && mkfs.ext4 /dev/sdb6 # mkfs.ext4 /dev/sdb5 && mkfs.ext4 /dev/sdb6mount /dev/sdb1 /sdb1 # 挂载分区 ifconfig网卡配置 12345678910111213141516171819202122232425262728293031323334ifconfig...ifconfig <网卡> 192.168.16.48/24 # 修改 ip# 修改 DNSvim /etc/resolv.conf # 这个文件route add default gw 192.168.1.1 netmask 255.255.255.0 # 添加默认网管route -m # 查看网关route del default gw 192.168.1.1 netmask 255.255.255.0 # 删除默认网管# ---- 永久修改 ----cd /etc/stsconfig/network-sctiptsvim <网卡名>TYPE=Ethernet //协议BOOTPROTO=static //dhcp(动态获取) 静态ipIPADDR=192.168.16.57 // IP地址NETMASK=255.255.255.0 //子网掩码GATEWAY=192.168.16.254 //网关DNS1=192.168.16.254 //DNSDEFROUTE=yes //PEERDNS=yesPEERROUTES=yesIPV4_FAILURE_FATAL=noIPV6INIT=yesIPV6_AUTOCONF=yesIPV6_DEFROUTE=yesIPV6_PEERDNS=yesIPV6_PEERROUTES=yesIPV6_FAILURE_FATAL=noIPV6_ADDR_GEN_MODE=stable-privacyNAME=enp0s5UUID=151b7ea3-6585-49b4-8442-e25a9acfbeb6DEVICE=enp0s5 //设备名称ONBOOT=yes //开机自启动 ssh1234567ssh 192.168.1.123 -p 8888 # 指定端口# 上传scp /etc/hosts 192.168.1.123:/root# 下载scp 192.168.1.123:/root/aaa.txt /tmp/ 免密码1234567891011121314ssh-keygen # 一路回车ls /root/.ssh/# id_rsa 钥匙# id_rsa.pub 锁 发给别人# 传送到服务器ssh-copy-id -i 192.168.1.123 # 传送完成后会自动重命名为 authorized_keys# 配置文件vim /etc/ssh/sshd-configsystemctl restart sshdnetstat -an | grep 8888 # 查看端口ssh-copy-id -i id_rsa.pub 192.168.1.123 计算器12yum install bc -yecho "scale=3;10/3"|bc -l]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux 用户]]></title>
<url>%2F2017%2F05%2Flinux-user.html</url>
<content type="text"><![CDATA[linux 下添加一个用户,就相当于公司招聘一个人,linux 每健一个用户,就会以这个用户名新建一个组。 用户12345678910111213id <user> # 查看用户信息uid=1000(aaa) gid=1000(aaa) groups=1000(aaa)# 用户 id,组 id(初始组),# /etc/passwd 用户信息文件aaa:x:1000:1000::/home/aaa:/bin/bash# x 用户密码占位符,代表这个用户需要密码登录,如果没有这个 x ,直接就可以登录系统# 1000 uid# 1000 gid# 两个冒号空的那一段是用户描述信息# /home/aaa 指定用户的家目录# /bin/bash 用户的登录 shell;可登录的用户# /sbin/nologin 不可登录的用户# /etc/shadow 记录用户密码文件 12345678910111213mkdir /test_user# 添加用户useradd -u 1200 -g root -d /test_user -c 'test user' -s /bin/bash test_user# -u 指定用户 uid# -g 指定主组# -d 指定用户家目录# -c 用户描述信息# -s 指定用户登录 shell# -G 指定附加组# 如果新添加的用户指定 root 为默认组,那么在 group 中 root 中就不会有新添加的这个用户# 修改用户usermod -G root test_user 组1234567891011121314151617181920# 添加组groupadd group1# 删除组groupdel group1# 修改组groupmod -g 2014 group1 # 修改组 idgroupmod -n group2 group1 # 修改组名# /etc/gshadow 记录组密码文件# /etc/group 记录用户组文件gpasswd aaa # 设置组密码#编辑文件每一行最后面的字符就是组内的成员,可以通过修改文件来改变用户的组或添加组--- 修改文件来添加组员root:x:0:aaa~ id aaauid=1000(aaa) gid=1000(aaa) groups=1000(aaa),0(root)---gpasswd -r aaa # 清除组密码 加深理解linux 里面一切皆文件,在创建用户的时候同样是修改文件。所以,也可以直接修改文件来创建用户。 vim /etc/passwd 追加一行所添加的用户 mkdir /home/rose 创建家目录 vim /etc/shadow 追加一行,同样的格式 vim /etc/group 追加一行,和uid需要一致,名字可以不一样 vim /etc/gshadow 追加一行 touch /var/spool/rose 创建用户系统邮箱文件 cp -r /etc/skel/.[!.]* /home/rose 拷贝指定目录下所有以.开头的文件,-r递归着拷贝 chown -R rose.rose /home/rose 修改为自己的权限 chown rose.mail /var/spool/mail/rose 把邮箱文件权限改为自己的 selinux 这个软件如果开启了,开启后所创建的文件权限后面都会加上. 12vim /etc/sysconfig/selinux# SELINUX = enforcing 改为 disabled 关掉,然后重启才会生效]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux 目录和文件]]></title>
<url>%2F2017%2F05%2Flinux-dir.html</url>
<content type="text"><![CDATA[linux 系统中,一切皆文件 winsows 中目录结构是多根模式 C: D: E:而 linux 中 / 代表的就是根 linux 目录 参考:http://www.runoob.com/linux/linux-system-contents.html media mnt 挂载目录 目录12345678910111213cd . # 切换目录# 增(创建目录)mkdir /amkdir /a/bmkdir -p /b/c # 递归创建目录# 查ls -d /a/e # 查看目录ls # 查看当前文件夹所有内容(不包含隐藏文件)# 改mv /a/f /a/l# 删rm -r /a/d # 递归着删下面的东西(包含提示)rm -rf /a/b # 递归删(没有提示)尽量避免使用 文件1234567891011121314151617181920# 增(创建文件)touch a.txt# 改mv a.txt b.txt# 查cat b.txt # 查看文件内容# 删rm b.txt # -rf 无提示less /tmp/aaa.txt # 翻页查看more /tmp/aaa.txt # 也是翻页head /tmp/aaa.txt # 默认查看前 10 行内容head -n 1 /aaa.txt # 指定查看的行数tail -n 1 /a.txt # 查看最后 1 行内容tail -f /a.txt # 动态查看最后添加的内容;如果是用编辑器打开的文件,那么保存后,这个命令看到的是这个文件的所有内容,打开文件这个操作是把文件所有的内容加载到内存,保存的时候是覆盖写进去。echo asdf > /a.txt # 覆盖到文件末尾echo asdf >> /a.txt # 追加文件内容 分页查看1ls --help | less # 分页查看 前面命令的结果]]></content>
<tags>
<tag>linux</tag>
</tags>
</entry>
</search>