计算机

在 IPython 中自动重新导入包

在使用 IPython 交互性测试编写的函数的时候,可以打开自动重新导入包的功能,这样每次保存后就可以直接测试了。

In [1]: %load_ext autoreload

In [2]: %autoreload 2

其中三个数字的含义是:

  • %autoreload 0 – 关闭自动重新导入
  • %autoreload 1 – 只在 import 语句重新导入
  • %autoreload 2 – 调用的时候自动重新导入

如果想要在 IPython 中自动启用

$ ipython profile create
$ vim ~/.ipython/profile_default/ipython_config.py
c.InteractiveShellApp.extensions = ['autoreload']
c.InteractiveShellApp.exec_lines = ['%autoreload 2']

Go 语言 Map 实战

相比 Rust 中尚未实现 IndexMut 的 Hash 类型来说,Go 中的 Map 实现度可以说是非常高了。

基本用法

Map 的类型是 map[KeyType]ValueType 的。也就是由 Key 类型和 Value 类型同时决定的。声明一个 Map:

var m map[string]int

不过一般很少有人这样写,还是生命并赋值比较常见,还是使用我们的 make 函数:

m = make(map[string]int)
commits := map[string]int{
    "rsc": 3711,
    "r":   2138,
    "gri": 1908,
    "adg": 912,
}

基本上除了 slice,map 和 function 以外,都可以做 map 的键。

赋值

m["route"] = 66

获取值

i := m["route"]  // 如果 route 不存在,那么获取的就是对应的零值
j := m["non-exist"]

删除值

delete(m, "route")  // 如果不存在的话,也不会抛出异常。这里和 Python 不一样

判断是否存在

i, ok := m["route"]

遍历

for key, value := range m {
    fmt.Println("Key:", key, "Value:", value)
}

并发性

map 不是线程安全的。

使用 ssh 反向隧道登录没有 IP 的服务器

假设我们家里的服务器叫做 homeserver,没有公网 IP。然后我们有一台服务器叫做 relayserver,拥有公网 IP。

在家里执行

homeserver~$ ssh -fN -R 10022:localhost:22 relayserver_user@1.1.1.1

在服务器上就可以登陆啦

relayserver~$ ssh -p 10022 homeserver_user@localhost

参考

http://xmodulo.com/access-linux-server-behind-nat-reverse-ssh-tunnel.html

Sequel Pro cannot create a JSON value from a string with CHARACTER SET ‘binary’

I had this problem dealing with exports made by Sequel Pro. I unchecked the Output BLOB fields as hex option and the problem went away. Visually inspecting the export showed legible JSON instead of binary.

导出数据的时候把 “Output BLOB fields as hex” 这个选项取消就可以了。

参考:https://stackoverflow.com/questions/38078119/mysql-5-7-12-import-cannot-create-a-json-value-from-a-string-with-character-set

如何导出 Docker 镜像

可以使用 docker save 和 docker export 导出 docker 镜像。那么两者有什么区别呢?

  • export 是用来导出一个容器的,也就是运行中的镜像。
  • save 是用来导出镜像的,也就是还没有运行的镜像。

这里我们需要用的显然是 docker save。

语法是:

docker save [OPTIONS] IMAGE [IMAGE...]

其中的 options 也就一个参数 -o 文件名。如果不指定 -o 的话直接输出到 stdout,方便使用管道串联。

如果需要压缩的话还可以这样

docker save myimage:latest | gzip > myimage_latest.tar.gz

导出之后,可以使用 docker load 导入镜像。不使用 -i 的话直接从 stdin 读取。

docker load -i FILE

macOS 中如何正确安装 pycurl

Reinstall the curl libraries

brew install curl --with-openssl

Install pycurl with correct environment and paths

export PYCURL_SSL_LIBRARY=openssl
pip uninstall pycurl 
pip install --no-cache-dir --global-option=build_ext --global-option="-L/usr/local/opt/openssl/lib" --global-option="-I/usr/local/opt/openssl/include"  pycurl

Python 微型ORM Peewee 教程

Python 中最著名的 ORM 自然是 sqlalchemy 了,但是 sqlalchemy 有些年头了,体积庞大,略显笨重。Peewee 还比较年轻,历史包袱比较少,也仅仅支持 Postgres、MySQL、Sqlite 这三种互联网公司最常见的数据库,所以整体上来说是比较轻量的。

peewee 在创建模型的时候就设定了数据库链接,个人感觉这个设计似乎不是很好。不过好在可以先不指定参数,而在实际使用的时候再链接数据库。

创建模型

from peewee import *

db = SqliteDatabase(None)

class Person(Model):
    name = CharField()
    birthday = DateField()

    class Meta:
        database = db # This model uses the "people.db" database.

class Pet(Model):
    owner = ForeignKeyField(Person, backref='pets')
    name = CharField()
    animal_type = CharField()

    class Meta:
        database = db # this model uses the "people.db" database

在 django 的 ORM 中,我们可以直接使用 FIELD_id 这样来访问一个外键的 id。这个在 peewee 中也是支持的。但是在设置的时候却不需要加上 _id 的后缀。在使用 where 语句的时候也不需要使用后缀。

event_id = ticket.event_id
ticket.event = new_event_id
Ticket.select().where(event == desired_event_id)

连接和创建数据库

db.init(**args)
db.connect()
db.create_table([Person])

增删改查

存储数据

跟 django 的 ORM 貌似是一样的。

from datetime import date
uncle_bob = Person(name='Bob', birthday=date(1960, 1, 15))
uncle_bob.save() # bob is now stored in the database
grandma = Person.create(name='Grandma', birthday=date(1935, 3, 1))
bob_kitty = Pet.create(owner=uncle_bob, name='Kitty', animal_type='cat')  # 带有外键的宠物

删除数据

herb_mittens.delete_instance() # he had a great life
# Returns: 1

更新数据

herb_fido.owner = uncle_bob
herb_fido.save()

读取数据

基本的语法是 Model.select().where(**coditions).get()

query = Pet.select().where(Pet.animal_type == 'cat')
for pet in query:
    print(pet.name, pet.owner.name)  # 注意这里有 N+1 问题,N 指的是获取 owner.name 

grandma = Person.get(Person.name == 'Grandma L.')

# 可以使用 join 解决 N+1 问题
query = (Pet
         .select(Pet, Person)
         .join(Person)
         .where(Pet.animal_type == 'cat'))
         .order_by(Pet.name)  # 或者 Pet.name.desc() 逆序排列

for pet in query:
    print(pet.name, pet.owner.name)

# prints:
# Kitty Bob
# Mittens Jr Herb

可以直接使用 | 来作为查询条件,这个相比 django 需要使用 Q 来说,设计地非常优雅。

d1940 = date(1940, 1, 1)
d1960 = date(1960, 1, 1)
query = (Person
         .select()
         .where((Person.birthday < d1940) | (Person.birthday > d1960)))

for person in query:
    print(person.name, person.birthday)

# prints:
# Bob 1960-01-15
# Grandma L. 1935-03-01

peewee 模仿 django 实现了 get_or_create 的方法。

保持连接

在使用数据库的时候,可能会遇到连接丢失的问题。peewee 提供了一个 Mixin 可以在连接丢失时候重连,这点比 django 方便多了。

from peewee import MySQLDatabase
from playhouse.shortcuts import ReconnectMixin

class ReconnectMySQLDatabase(ReconnectMixin, MySQLDatabase):
    pass

db = ReconnectMySQLDatabase('my_app', ...)

拓展

最后也是最牛逼的一点,可以使用 pwiz 工具从已有的数据库产生 peewee 的模型文件:

python -m pwiz -e postgresql charles_blog > blog_models.py

参考

  1. https://stackoverflow.com/questions/45345549/peewee-mysql-server-has-gone-away-error/57797698#57797698

flask 全家桶学习笔记(未完待续)

)看到标题有的同学可能就问了,flask 是一个微框架,哪儿来的全家桶啊。其实作为一个框架来说,除非你提供的只有静态页面,那么肯定要和数据库打交道的,那么这篇文章里介绍的就是 flask + ORM + login + uwsgi 等等一系列的工具。

flask

flask-login

https://flask-login.readthedocs.io/en/latest/

flask admin

https://github.com/flask-admin/flask-admin/blob/master/examples/peewee/app.py

swagger+flasgger

swagger 是一套定义 API 的工具,可以实现 API 的文档化和可交互。flasgger 是 flask 的一个插件,可以实现在注释中使用 swagger 语法。

swagger 本身是一套工具,但是后来被社区发展成了 OpenAPI 规范。最新版本是 OpenAPI 3.0,而现在用的最多的是 swagger 2.0。我们这里

完整的例子

https://github.com/coleifer/peewee/blob/master/examples/twitter/app.py

参考文献

  1. https://blog.csdn.net/u010466329/article/details/78522992
  2. https://blog.csdn.net/qq_21794823/article/details/78194164
  3. http://www.manongjc.com/article/48448.html
  4. https://juejin.im/post/5964ce816fb9a06bb21abb23
  5. https://www.cnblogs.com/whitewolf/p/4686154.html

uwsgi

uwsgi 的使用和性能优化配置

假设我们编写了如下的 flask 应用,要用 uwsgi 部署,希望性能越高越好,那么下面是一份还说得过去的配置。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "world"

if __name__ == "__main__":
    app.run()

对应的 uwsgi 配置

[uwsgi]
wsgi-file=app.py  # 应用的主文件
callable=app  # 应用中的 flask 实例
chdir=/opt/app  # chdir 到给定目录
env= XXX=XXX  # 额外的环境变量

# 以下三者任选其一
http=0.0.0.0:5000  # 如果直接暴露 uwsgi 的话用这个
http-socekt=0.0.0.0:5001  # 如果用nginx反向代理的话,用这个
socket=:3031  # 在 3031 使用 uwsgi 协议,nginx 中使用 uwsgi_pass 更高效

chmod-socket = 664

pidfile=xxx  # pid 文件路径
venv=xxx  # 虚拟环境路径
logto = /var/log/www.log

# 并发设置
workers = 2  # 一般为 CPU 核数 * 2
threads = 2  # 线程比进程开销更小一点。如果没有使用 threads 那么 thread 直接不工作的,必须使用 enable_threads。
max-requests = 100000  # 处理过多少个请求后重启进程,目的是防止内存泄露
master = true  # 使用 max-requests 必须采用这个选项
listen = 65536  # 每个进程排队的请求数量,默认为 100 太小了。并发数 = procsses * threads * listen
buffer-size = 65536  # header 的 buffer 大小,默认是 4 k
thunder-lock = true  # 避免惊群效应
uid=www-data
gid=www-data
harakiri=30  # 所有进程在 30s 没有响应后傻屌
log-slow=3000  # 记录满于 3000 毫秒的请求
# lazy-apps  # 不使用 prefork,而是在需要时才启动进程

# 监控设置
stats = 127.0.0.1:9191  # 可以使用 uwsgi top 监控
python-autoreload=1  # 自动重载,开发时非常方便

# 静态文件
check-static = /var/static  # 尝试从该目录下加载静态文件
static-map = /static=/var/static  # 把对应目录映射
route = /static/(.*)\.png static:/var/www/images/pngs/$1/highres.png  # 使用高级路由模式
offload-threads = 10  # 让 uwsgi 启动额外的进程处理

参考

  1. https://blog.zengrong.net/post/2568.html
  2. https://stackoverflow.com/questions/34255044/why-use-uwsgi-max-requests-option/34255744
  3. https://blog.csdn.net/apple9005/article/details/76232852
  4. https://mhl.xyz/Python/uwsgi.html
  5. https://stackoverflow.com/questions/34824487/when-is-thunder-lock-beneficial