CI_Knight

且行善举,莫问前程。

0%

rsync 使用

当我还在使用 pycharm 做开发的时候,经常会使用 rsync 来同步本地和服务器上的代码,因为比传统的 scp 更为节省流量,只上传有改动的部分。

Usage

只简单介绍,毕竟文档是最好的老师。rsync 包括服务器端和客户端,服务器可以指定相应配置文件。

rsync –daemon –config=/path/to/rsyncd.conf

可以配置权限以及应用的启动信息。使用默认配置即可。

rsync 支持 ssh、scp 等文件传输方式,也可以直接使用 socket 连接来传输。那么举个例子(Unix 环境下)

rsync -avz -e “ssh -p $portNumber” user@remoteip:/path/to/ /local/path/

我使用 ssh 协议,将本地的 /local/path/ 同步到 远端服务器 /patg/to/ 下。

  • a 相当于 rlptgoD,r 是递归、l 是链接文件、p 表示保持文件原有权限、t 保持文件原有时间、g 保持文件原有用户组、o 保持文件原有属主、D 相当于块设备文件
  • z 传输时压缩
  • v 传输时的进度信息
  • e 制定使用 rsh,ssh 方式进行数据传输

rsync 算法

rsync 核心算法是 rolling checksum,核心在 rolling 上,把文件按照相同大小分块,然后分块 checksum,来比较每一块的 checksum 有没有改变。

如果这样,改变文件中间的一个字节,后面的每块都会改变。所以关键在意 rolling 上。当找不到匹配时,算法就要向后移一个字节。知道找到匹配为止。

不盗图了,传送门 https://coolshell.cn/articles/7425.html

如果是这样的话,当文件越多时,所需要花费的时间越长。同步代码使用还是可以的。如果是需要发布的代码还是建议使用 git hook。

部署工具介绍

Python 是可以直接运行一个 Python Web Server 的,Python -m SimpleHTTPServer 8000就可以启动一个监听 8000 端口的 Web Server,不像是 php 需要一个 Web Server来配合开发。

Python 的 Web 框架有很多,最具名气的是 Django,flask,tornado,web.py 等,当写一个小的项目的时候我们会直接通过框架提供的启动方式。当要正式部署一个 Web Server时并不能使用如此粗暴的方式,Server 很容易出现异常就挂掉。那么解决方案有很多。

以下是软件介绍,都可使用 pip 直接安装。

Supervisor

Supervisor 是很强大的进程管理工具,也是守护进程,帮你管理服务器上所运行的程序,守护程序防止被杀掉。可以通过写配置文件来批量启动你的 Web Server。

Gunicorn

一款 Wsgi HTTP Server,据说是比 uwsgi 更加高效,但是实测并没有 uwsgi 强。但是部署应用简单。

example: test.py

1
2
3
4
5
6
7
8
9
10
def app(environ, start_response):
"""Simplest possible application object"""
data = 'Hello, World!\n'
status = '200 OK'
response_headers = [
('Content-type','text/plain'),
('Content-Length', str(len(data)))
]
start_response(status, response_headers)
return iter([data])

gunicorn –workers=2 test:app

启动方便,具体参数请看 gunicorn 文档 http://docs.gunicorn.org/en/latest/

uWsgi

这是我要说的重点,自测吞吐量是其他两种部署方式的两倍。

使用 Flask,uWsgi 和 Nginx 来部署 Server

flask run.py

1
2
3
4
5
6
7
import Flask

app = Flask(__name__)

@app.route('/')
def index():
return "Hello World!"

uwsgi 支持 xml,json,ini 等多种配置文件,我们来配置 testapp.ini

uwsgi testapp.ini

1
2
3
4
5
6
[uwsgi]
socket = /var/run/testapp.sock
venv = /path/to/python
wsgi-file = run.py
callable = app
chmod-socket = 666

/var/run 必须有可写权限,chmod-socket 是避免 nginx 无法读写导致502错误,接下来配置nginx

nginx server.conf

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name localhost;

location / {
include uwsgi_params;
uwsgi_pass unix:/var/run/testapp.sock;
}
}

这样可以成功部署了,访问 curl http://localhost 输出 Hello World! 部署成功。

更多可以看文档https://uwsgi-docs.readthedocs.io/en/latest/

与 Nginx 配合使用效果更好

早起最流行的是 LAMP,记得初学 Linux 让我们编译 LAMP,一干就是一天。慢慢的 Shell 用的都很熟练了。但是 Apache 用起来就很复杂,第一次使用 Apache 和 Django 部署搞 wsgi_mod 花了很长时间。之后使用了 Nginx 才算是彻底在部署 Web Server 上解放。配置简单,部署方便,轻量且强大。

Nginx 是不可少的,路由管理、静态文件管理,Gzip,HTTPS、HTTP2/SPDY、ETAG 都需要 Nginx 来 handle。

测试

使用 ab 测试工具 ab -c 10 -n 100 http://localhost/ 模拟10人请求 localhost 100次。

守护进程

Web Server 使用 uwsgi 或者 gunicorn ,都是需要守护进程来守护 Server 不被杀掉。这时再使用 supervisor 就可以了。

这篇文章不是讲教你怎么搭建无线热点的,毕竟不是很难的东西,网上都可以搜到相关的博客,毕竟搭建一次写个脚本就可以了。

从 Mac 讲起

公司刚刚搬家,周围 WiFi 信号颇多,Mac 并没有有线网卡,无线真的是卡的要死,ping网关有时都能达到两万毫秒时,这种延迟是要命的,一般都会连到内网服务器上写代码,这种延迟都是会被卡掉的,要是玩游戏的话都可以摔键盘了。

使用网卡转USB

leader 一直是这样上网的,毕竟会比无线稳定很多,但是买转换器也是一笔不必要的支出。网上搜搜一般在三十块钱左右,并不知道好不好用。引入一样新的东西就会增加事物的复杂度。

使用无线路由器

家中有个闲置的无线路由器,在家只是做WDS桥接网络,未免有些浪费。想拿过来连接有线再单独开出来个私有的 WiFi,这样会比之前的稳定多,但是发射出来的 WiFi 信号未免会干扰其他信号,使得别人网络更加差劲。

树莓派搭建无线热点

这不是教程,我找到一篇不错的教程,也不至于再写一遍,只做一些提示。

我的是 raspberry pi 2,安装的 debian 的系统,这样的搭建方法也适用于基于 debian的一些系统,比如 ubuntu。

还有在设置 WIFI 信道的时候是很有讲究的,尽量使用范围内 WIFI 信号岔开5个信道的值,比如1,6,11。

搭建热点使用的软件基本上都是 hostapd,搭建方法应该也适用大多的 linux 发行版,在搭建前要注意自己的无线网卡型号,可以使用 lsusb、lsmod 来查看无线网卡的具体型号,我的是 RTL8188C,是支持开启 WIFI 热点的,但是 hotspot 是不支持的,需要替换成兼容版本。

下面这个教程步骤非常详细,特别提示也很多。

有疑问的话,可以直接在评论中讨论。

with 关键字

在 python 关于 with 关键字,中我们一般会这样写。

1
2
with open('file.txt', 'w') as f:
f.write('hello world!')

在这段代码中,无论with中的代码块在执行的过程中发生任何情况,文件最终都会被关闭。如果代码块在执行的过程中发生了一个异常,那么在这个异常被抛出前,程序会先将被打开的文件关闭。

with 关键字一般执行过程

一段基本的 with 表达式其结构是这样的:

1
2
with EXPR as VAR:
BLOCK

其中:EXPR可以是任意表达式;as VAR是可选的。其一般的执行过程是这样的:

  • 计算EXPR,并获取一个上下文管理器。
  • 上下文管理器的__exit()__方法被保存起来用于之后的调用。
  • 调用上下文管理器的__enter()__方法。
  • 如果with表达式包含as VAR,那么EXPR的返回值被赋值给VAR。
  • 执行BLOCK中的表达式。
  • 调用上下文管理器的__exit()__方法。如果BLOCK的执行过程中发生了一个异常导

致程序退出,那么异常的type、value和traceback(即sys.exc_info()的返回值)将作为参数传递给__exit()__方法。否则,将传递三个None。

将这个过程用代码表示,是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mgr = (EXPR)
exit = type(mgr).__exit__ # 这里没有执行
value = type(mgr).__enter__(mgr)
exc = True

try:
try:
VAR = value # 如果有 as VAR
BLOCK
except:
exc = False
if not exit(mgr, *sys.exc_info()):
raise
finally:
if exc:
exit(mgr, None, None, None)

这个过程有几个细节:

  • 如果上下文管理器中没有__enter()__或者__exit()__中的任意一个方法,那么解释器会抛出一个AttributeError。
  • 在BLOCK中发生异常后,如果__exit()__方法返回一个可被看成是True的值,那么这个异常就不会被抛出,后面的代码会继续执行。

实现上下文管理器类

实现一个类

第一种方法是实现一个类,其含有一个实例属性db和上下文管理器所需要的方法__enter()__和__exit()__。

1
2
3
4
5
6
7
8
9
10
11
12
class transaction(object):
def __init__(self, db):
self.db = db

def __enter__(self):
self.db.begin()

def __exit__(self, type, value, traceback):
if type is None:
db.commit()
else:
db.rollback()

使用生成器装饰器

在 Python 的标准库中,有一个装饰器可以通过生成器获取上下文管理器。使用生成器装饰器的实现过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from contextlib import contextmanager

@contextmanager
def transaction(db):
db.begin()

try:
yield db
except:
db.rollback()
raise
else:
db.commit()

contextmanager 实现

详解

  • Python 解释器识别到yield关键字后,def会创建一个生成器函数替代常规的函数(在类定义之外我喜欢用函数代替方法)。
  • 装饰器contextmanager被调用并返回一个帮助函数,这个帮助函数在被调用后会生成一个GeneratorContextManager实例。最终with表达式中的EXPR调用的是由contentmanager装饰器返回的帮助函数。
  • with表达式调用transaction(db),实际上是调用帮助函数。帮助函数调用生成器函数,生成器函数创建一个生成器。
  • 帮助函数将这个生成器传递给GeneratorContextManager,并创建一个GeneratorContextManager的实例对象作为上下文管理器。
  • with表达式调用实例对象的上下文管理器的__enter()__方法。
  • __enter()__方法中会调用这个生成器的next()方法。这时候,生成器方法会执行到yield db处停止,并将db作为next()的返回值。如果有as VAR,那么它将会被赋值给VAR。
  • with中的BLOCK被执行。
  • BLOCK执行结束后,调用上下文管理器的__exit()__方法。__exit()__方法会再次调用生成器的next()方法。如果发生StopIteration异常,则pass。
  • 如果没有发生异常生成器方法将会执行db.commit(),否则会执行db.rollback()。

代码实现

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
def contextmanager(func):
def helper(*args, **kwargs):
return GeneratorContextManager(func(*args, **kwargs))
return helper

class GeneratorContextManager(object):
def __init__(self, gen):
self.gen = gen

def __enter__(self):
try:
return self.gen.next()
except StopIteration:
raise RuntimeError("generator didn't yield")

def __exit__(self, type, value, traceback):
if type is None:
try:
self.gen.next()
except StopIteration:
pass
else:
raise RuntimeError("generator didn't stop")
else:
try:
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw()")
except StopIteration:
return True
except:
if sys.exc_info()[1] is not value:
raise

一些例子

锁机制

1
2
3
4
5
6
7
@contextmanager
def locked(lock):
lock.acquired()
try:
yield
finally:
lock.release()

标准输出重定向

1
2
3
4
5
6
7
8
9
10
11
12
@contextmanager
def stdout_redirect(new_stdout):
old_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield
finally:
sys.stdout = old_stdout

with open("file.txt", "w") as f:
with stdout_redirect(f):
print "hello world"

前言

修修改改总算把第三方登录,遇到很多坑,很多在网上也搜不到。涉及有QQ、微信、微博,我想读完这文章能少走很多弯路,还有注意文章的时间。那么从坑最多的来讲。

当有多个网站需求但是只有一个 appid 的时候可以考虑下 proxy 的实现方式了。

关于测试

如果说是为公司开发,公司没有提供appid,那么就可以自己在平台注册一个开发者,这样拿到开发账号就可以直接测试了。

如果遇到问题,移动开发可以看是否提供sdk源码,如果在 github 上,可以在 issue中看到很多别人遇到的问题,这样也能少走很多弯路,比如最近 ios10,开发者们都已早早更新,微博出现了 https 错误的问题,在 issue 里就找到了答案。

QQ 登录

QQ 坑是最多的,QQ 分为互联和开放平台,开放平台和互联管理的各不同,开放平台管的是空间微博朋友网授权以及移动授权,并不支持 PC 上 Web 授权登录,想要申请 Web授权就需要到互联申请,但是千万不要将这两个平台的账号管理起来,不然就需要全部审核成功后才能上线。

当然授权是为了拿到用户信息,还有就是openid,也是就是用户的唯一标识,但是互联和开放平台拿到的openid 是不同的,这点要做全平台登录的一定要注意了。后来咨询 QQ 那边,互联改版后支持移动和Web 两种授权方式了,所以可以直接在互联申请了。也是很坑,这是我在做完之后遇到问题才改版的。结果是我们只有移动端的注册。

QQ 的文档很老旧,接口返回还有 jsonp 和 query 的形式。要自己写解析的。

微信登录

微信比较好,但是也分为开放平台和公众平台,公众平台绑定开放平台后能拿到 unionid这点也是要注意的。所以,在写登录的时候就要看需不需要这个 unionid 了,不然拿到的就是 openid。

登录分两种,一种是扫码登录,这个是在开放平台的。还有种是在微信客户端内登录,这个是公众平台的,所以看文档注意自己看的是哪个平台的文档。

微信公众平台提供了测试工具,还算比较方便的,但是有很多隐患,比如在真机上出现的问题会在测试工具上无法复现,也许是被测试工具吞掉了抛错。

另一点是登录请求的 url 的 query 一定要是排序的,不然也会抛错。

微信的 get_user_info 拿到的信息会有问题,在存储的时候可能会是乱码,因为它给你的是一个 utf8的 encode 位串,这点最好注意一下。

记住一定要把微信的文档全都复印一遍,然后烧掉它,这只是一种仪式感。

微博登录

应该是最好的了,没有太多的问题,认真看文档。

后记

一个人做完了微信、微博、QQ第三方登录,这也是我第一次做,经验不多犯了很多错,写这篇文章分享一下,让别人能少走一些弯路。还有设计 API 的时候一定要求同,保证接口一致,只是微信移动端 SDK 里出的一些问题。

如果有其他问题都可以在留言中提出。

RaspberryPi

树莓派是我大学的时候接触的,刚刚买的时候就把它当 linux 玩了,用的但是最熟悉的 debian,虽然说最早接触的系统是 redhat 系列的,还记得大学在 linux 社团,学完命令后学长就让我们开始去编译 Lump,还有些人都开始搞 gentoo 了,也做好了镜像,为了方便还是选择了 debian。

说树莓派是嵌入式方面的也不为过,因为有很强大的 GPIO,但对于嵌入来讲又过于庞大。总的来说 Raspberry 的用处是相当广的,目前我的树莓派充当着我的网络数据中心,也跑着很多的服务,还有一些 传感器,比如温湿度等等。

可玩性最高的其实还是 GPIO 了。

Raspberry-pi GPIO

我的是 raspberry-pi 2,总共40个针脚,每个针脚有不同的作用,具体可以参考下面的网站。

Raspberry Pinout

准备什么当然最好是买个扩展板还有面包版和N多子母、子子头的插线,最好电阻也买些(我用的是一个可变电阻,通过电路控制电压)对,当然还有电压表。

买一些传感器,还有放着传感器的小盒子,温度传感器的话推荐用DHT22,比DHT11高了0.1的精度值,价格上也更贵一些。使用的时候一定要加上电阻,不要烧坏传感器。还有来一些不同颜色的发光二极管,可以练习跑马灯什么的。

这些东西某宝上都能买到,最重要的是你得有个树莓派。目前最新的是 Raspberry pi 3

GPIO 编程

树莓派支持的编程语言很多,C族编程语言,Python(效率也是最高的,也很简单),还有Shell等等。我主要使用 Python,玩 GPIO 的话用的是 C,用的是 Wiring Pi 驱动。python 话应该有很多,pip就可以搞定了,shell 应该需要重新配置下树莓派的 /dev 支持吧(shell的可以令查)

Wiring pi

针脚封装成8个从 0-7 一一对应 Raspberry pi 的GPIO。

对应关系如下图

Wiring Pi 下载

我的 Github 中有 DHT11 和 DHT22 的实现,可以作为参考。

自己瞎搞