之前服务是用 Tornado 启动,但响应速度很慢,有时候会被一些任务给卡死,于是一直想提高性能,后来发现是没有使用多进程。看到 tornado.httpserver — Non-blocking HTTP server 中的配置,于是加上多进程。

1
2
3
4
5
sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(4)
server = HTTPServer(app, xheaders=True)
server.add_sockets(sockets)
IOLoop.current().start()

但是在 debug 模式下,上面的配置是会报错的,所以 debug 模式下还是使用单进程配置。

1
2
3
server = HTTPServer(app)
server.listen(8888, xheaders=True)
IOLoop.current().start()

现在响应速度有了质的飞跃。

至于 xheaders 配置,是为了获取用户的真实 IP。

联系作者

随着机器数量增多,单个 Salt master 已经无法满足性能需求,于是需要采用多 master, 多 syndic 结构,于是需要升级 Salt Minion 。升级过程中出现了一些问题。

在升级 Salt inion 的时候,需要关闭当前的 Salt minion,发现以前通过 Salt 启动的一些进程,如 redis 等等,也跟着关闭了,这是个问题。

查找之后,在 Salt 上也提到这个问题,salt-minion restart causes all spawned daemons to die on centos7 (systemd),主要原因是 systemd 里, 杀死进程的默认方式是 control-group ,也就是同一个组下主进程被杀死,其它进程都会被杀死。而通过 Salt 启动的那些服务与 Salt 是同一个组,Salt 是主进程。

解决办法是更改 salt-minion.service 的 KillMode 为process

联系作者

项目里有一个脚本列表接口响应非常慢,需要10秒以上,看程序,认为是访问数据库过多,优化之后还是很慢。继续看代码,没看出什么名堂。同事推荐使用 Profile 看看,于是使用,解决了问题。

根据 Profiling a Werkzeug (flask) app 这篇文章,在代码里加上

1
2
3
4
from werkzeug.contrib.profiler import ProfilerMiddleware
from myapp import app # This is your Flask app
app.wsgi_app = ProfilerMiddleware(app.wsgi_app)
app.run(debug=True) # Standard run call

再次调用接口时,终端上就显示了一些调用信息,根据这些信息去代码里找原因,解决问题。

最后发现是接口里返回的最后修改时间和修改原因都是从 git 仓库里访问,脚本多的话,就很慢了。将这两个信息保存在数据库中就可以解决问题。

联系作者

使用salt-minion时,会遇到文件数不够的问题。一个常见的误区是认为修改/etc/security/limits.conf即可,后来发现这是不对的。man pam_limits就会发现/etc/security/limits.conf主要作用于user-session的情况,如sshd, login, su等等。而对于使用sysvinit和systemd启动的服务,/etc/security/limits.conf是不起作用的,此时可以在相应服务的启动配置里修改文件数配置。

对于salt-minion文件数的修改需要根据系统版本而定。

CentOS 7

修改/usr/lib/systemd/system/salt-minion.service文件,修改其中的LimitNOFILE配置
之后重启 salt-minion

  • systemctl daemon-reload
  • systemctl restart salt-minion.service

CentOS 6

修改/etc/init.d/salt-minion启动脚本
添加文件数ulimit配置。例如设置最大100000,可以在启动脚本前面添加ulimit -HSn 100000,之后重启 salt-minion

  • service salt-minion restart

参考资料:

联系作者

Pylint

Pylint 用于 Python 静态代码检查。默认代码风格遵循 PEP08

安装

执行 pip install pylint 即可

使用配置文件

配置文件可以通过如下命令生成pylint --generate-rcfile > .pylintrc。执行 pylint 时,可以通过指定 –rcfile 参数来加载配置文件。而默认配置文件加载顺序可以参考命令行参数这节。例如,对当前目录下所有 Python 文件作代码检查,执行 pylint --rcfile .pylintrc *.py 即可

Flask代码检查

对于Flask, 有pylint-flask 这个 Pylint 插件用来代码检查。pip install pylint-flask安装后,添加 –load-plugins 参数即可启用,如pylint --load-plugins pylint_flask

警告忽略

有时 Pylint 的检查不满足需求,太繁琐,此时可以忽略它。如在for d in data:里,会报Invalid variable错误,即, 此时加上# pylint: disable=invalid-name可以忽略这个警告。暴力一点的方法是在文件开头添加# pylint: disable=invalid-name,这样会对整个文件忽略检查。更暴力的方法是修改 .pylintrc 文件,在disable这项里添加 invalid-name , 这样就会对所有文件忽略这个检查。

pre-commit

pre-commit 用来配置 commit 代码时检查代码。

安装

执行 pip install pre-commit 即可。

添加移除 git hooks

执行 pre-commit install , 将 pre-commit 添加到 git hooks 中

配置

在项目根目录下,添加.pre-commit-config.yaml文件即可进行配置,如下就是一个配置。

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
-   repo: https://github.com/pre-commit/pre-commit-hooks
sha: v0.7.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: autopep8-wrapper
- id: check-docstring-first
- id: check-json
- id: check-added-large-files
- id: check-yaml
- id: debug-statements

- id: requirements-txt-fixer
- repo: https://github.com/pre-commit/pre-commit
sha: v0.11.0
hooks:
- id: validate_config
- id: validate_manifest
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
files: \.py$
exclude: test_gevent.py|app/rpc/notify_manager
args: [--rcfile=.pylintrc, --load-plugins=pylint_flask]

此后,每次提交代码时,都会进行代码规范检查。

移除 git hooks

执行 pre-commit uninstall, 将 pre-commit 从git hooks中移除

联系作者

有些时候,需要通过Zabbix的API添加维护,挂起Zabbix的报警,简单的API调用如下

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
from zabbix_client import ZabbixServerProxy
zapi = ZabbixServerProxy("http://127.0.0.1/")
print zapi.user.login(user="test", password="password")
print zapi.host.get(output=['hostid', 'host'])
import time
params = {
"name": "madfsdfsadsdfsdf",
"active_since": int(time.time()),
"active_till": int(time.time()) + 3600,
"hostids": ["10109"],
"timeperiods": [
{
"start_time": 64800,
"period": 3600
}
]
}
# print zapi.maintenance.create(**params)
params = {
"hostids": ["10109"]
}
print zapi.maintenance.get(**params)

params = ('19',)
print zapi.maintenance.delete(*params)

联系作者

PyCharm的使用可以参看PyCharm的帮助文档

跳转

  • Command + B(或Command + 单击) 跳转到声明处
  • Command + [ 光标之前的位置
  • Command + ] 光标之后的位置

编辑查找

  • Command + F 当前文件搜索(回车下一个 shift回车上一个)
  • Command + R 当前文件替换
  • Shift + Command + F 全局搜索
  • Shift + Command + R 全局替换
  • Command + O 查找类
  • Shift + Command + O 查找文件
  • Option + Enter 导入包
  • Control + Option + O 整理包导入
  • Option + F7 查找类使用情况

执行

  • Control + Option + R 执行脚本
  • Command + Option + R Debug启动脚本

联系作者

Python中内置的一些属性相关的函数,如dir, getattr, hasattr, setattr, vars, 这里主要来看看一些特殊方法

编写测试代码如下:

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
class Class:
def __delattr__(self, name):
print('## delattr')
super().__delattr__(name)

def __getattr__(self, name):
print('## getattr')
return 'default'

def __getattribute__(self, name):
print('## getattribute')
super().__getattribute__(name)

def __setattr__(self, name, value):
print('## setattr')
super().__setattr__(name, value)


if __name__ == "__main__":
c = Class()
getattr(c, 'name')
print("-----")
c.name
print("-----")
c.data = 'test'
print("-----")
c.data
print("-----")
del c.data

得到输出结果

1
2
3
4
5
6
7
8
9
10
11
## getattribute
## getattr
-----

## getattribute
## getattr
-----

## setattr
-----

## getattribute
-----

## delattr

可以看到c.name和getattr(c, ‘name’)都会调用__getattribute__方法,之后调用__getattr__方法,c.data = ‘test’会调用__setattr__方法,之后c.data只调用了__getattribute__方法, del c.data调用了__delattr__方法。

所以可以总结如下

  • __getattribute__在获取属性时都会被调用到
  • __getattr__当对象没有相应的属性时,会被调用
  • __setattr__在设置属性时会被调用
  • __delattr__在删除属性时会被调用

联系作者

在Python中property是一个常用的装饰器,一个常用的用途是将方法变成属性访问。另一个需要注意的是,使用它可以改变对象中属性的访问顺序。

编写如下测试类

1
2
3
4
5
6
class Class:
data = 'the class data attr'

@property
def prop(self):
return 'the prop value'

然后在交互环境下测试如下

1
2
3
4
5
6
7
8
9
10
11
12
>>> obj = Class()
>>> vars(obj) #
{}
>>> obj.data #
'the class data attr'
>>> obj.data = 'bar' #
>>> vars(obj) #
{'data': 'bar'}
>>> obj.data #
'bar'
>>> Class.data #
'the class data attr'

可以看到在obj.data = ‘bar’后,对象中的data覆盖了类中的data

当对于proptery,对象中的attribute无法覆盖类中的attribute,在交互条件下输入如下操作可以看到结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> Class.prop #
<property object at 0x1072b7408>
>>> obj.prop #
'the prop value'
>>> obj.prop = 'foo' #
Traceback (most recent call last):
...
AttributeError: can't set attribute
>>> obj.__dict__['prop'] = 'foo' #
>>> vars(obj) #
{'prop': 'foo', 'data': 'bar'}
>>> obj.prop #
'the prop value'
>>> Class.prop = 'baz' #
>>> obj.prop #
'foo'

在对象中,obj.prop = ‘foo’这个操作会报错,这是因为property会覆盖对象的属性,而此时prop还未实现set方法。即便是通过obj.dict设置了prop属性,obj.prop获取到的依然是类中的prop

也就是property会覆盖对象的属性,看下面的例子就很清楚了

1
2
3
4
5
6
7
8
9
10
>>> obj.data #
'bar'
>>> Class.data #
'the class data attr'
>>> Class.data = property(lambda self: 'the "data" prop value') #
>>> obj.data #
'the "data" prop value'
>>> del Class.data #
>>> obj.data #
'bar'

在类属性data变成了property后,即Class.data = property(lambda self: 'the "data" prop value') #, 对象中的data的读取也跟着改变了。

简单来说,就是对象中的属性会覆盖类中的属性,而类中的property会覆盖对象中的属性。同样的,类中的descriptor也会覆盖对象中的属性。

参考资料

联系作者

使用jQuery操作时,发现jQuery常用问答, 都是一些很使用的功能。

如何设置或取消一个checkbox或者radio按钮

这里主要用到prop方法

1
2
3
4
5
6
7
8
// 设置 #x
$( "#x" ).prop( "checked", true );

// 取消 #x
$( "#x" ).prop( "checked", false );

// 获取是否选择 #x
$('#x').prop("checked")

如何从select框中获取所选项的值和文本

1
2
3
4
5
6
7
<select id="myselect">
<option value="1">Mr</option>
<option value="2">Mrs</option>
<option value="3">Ms</option>
<option value="4">Dr</option>
<option value="5">Prof</option>
</select>

这里主要是val和text方法

1
2
3
4
5
// 获取值
$( "#myselect" ).val();

// 获取文本
$( "#myselect option:selected" ).text();

联系作者