Whoosy's Blog

藏巧于拙 用晦而明 寓清于浊 以屈为伸

0%

编码不易,转载请注意出处!

task.s.delay()task.delay()

假定您的Celery任务和API源代码位于同一项目中。在这种情况下,您只需将任务导入API模块并异步调用即可。

1
2
3
4
5
6
7
8
9
from flask import Flask, jsonify
from tasks import fetch_data

app = Flask(__name__)

@app.route('/', methods=['POST'])
def index():
fetch_data.s(url=request.json['url']).delay()
return jsonify({'url': request.json['url']}), 201

只有在Celery中注册了fetch_data任务,此方法才起作用。当Celery和API源代码不在同一项目中时,或者当您有多个Celery微服务并且需要从一个Celery微服务调用另一个Celery微服务中的任务时,您需要使用另外方式去调用任务。

阅读全文 »

编码不易,转载请注意出处!

在上一篇博客文章中,我们研究了自定义队列和任务路由。我们如果想要让某个任务路由到某个队列,配置任务路由是必要的,此方法对于简单的设置非常有效,但对于需要将许多Celery任务路由到许多不同队列的应用程序和微服务而言,明显配置要复杂的多。

实现动态路由

步骤1:Celery task_routes配置

celery配置中的task_routes参数支持传入一个自定义类,这个类里面要实现route_for_task函数,此类官方文档上叫做路由器,动态路由的核心就在于此。

celery 配置
1
2
3
4
5
6
7
8
9
10
app = Celery(__name__)
app.conf.update({
'broker_url': os.environ['CELERY_BROKER_URL'],
'imports': (
'tasks',
),
'task_routes': ('task_router.TaskRouter',), # 此自定义类的路径
'task_serializer': 'json',
'result_serializer': 'json',
'accept_content': ['json']})
阅读全文 »

编码不易,转载请注意出处!

默认情况下,Celery将所有任务路由到单个队列(celery)中,并且所有worker都监听此默认队列。 合理使用Celery队列,您就可以自定义控制哪些Celery worker 处理哪些任务。 如果您有慢任务和快任务,并且希望慢任务不干扰快任务,这会很有用。 或者,如果您需要将任务从一个微服务发送到另一种微服务(订阅发布)。

配置一个task_routes

可以为每个任务配置队列,那么将会把此任务发送到相应的队列:

1
2
3
4
5
6
7
8
9
10
11
12
13
app = Celery(__name__)
app.conf.update({
'broker_url': os.environ['CELERY_BROKER_URL'],
'imports': (
'tasks',
),
'task_routes': {
'fetch_bitcoin_price_index': {'queue': 'feeds'},
'calculate_moving_average': {'queue': 'filters'}
},
'task_serializer': 'json',
'result_serializer': 'json',
'accept_content': ['json']})

任务fetch_bitcoin_price_index就会被路由到feeds队列中,任务calculate_moving_average 将会发送到filters队列中。

阅读全文 »

编码不易,转载请注意出处!

前言

在接口开发过程中,对于简单功能的接口我们可能并不会对它编写单元测试,因为我们很容易就可以调用它判断结果是否是我们预期的。
但是对于执行接口逻辑相同的celery而言,我们想测试它并没有那么容易,因为celery任务在设计上是异步的,我们要遵循 “development driven development” 要困难的多。
测试驱动开发(TDD)也只是保证脑内的想法转成代码的时候逻辑自洽而已,并不能保证代码的真实质量, 但是涉及到celery任务时,我们想要保持高效的开发此过程是必不可少的,所以希望您保持理智😺,平静的测试,在这种情况下将您的代码发布到生产环境中。

阅读全文 »

从语言层面来说,Cython是一种拓展的Python,其文件的扩展名为.pyx。这种类型的文件通过编译之后可以变成供Python直接调用的动态链接库(Linux/Mac下是.so,Windows下是.pyd)。根据官方文档,主要如下几编译方式:

  • (推荐) 通过setup.py中调用Cython.Build进行编译
  • 使用pyximport调用.pyx文件,这种方法.pyx文件相当于普通的.py文件
  • 在命令行使用cython命令从.pyx文件生成.c文件,再使用外部编译器将.c文件编译成Python可用的库
  • 使用Jupyter Notebook或者Sage Notebook直接运行Cython代码

这上面四种方法里最简单的是第三种方法。运行cythonize -i <.pyx File>即可编译.pyx成二进制库,并保存在与.pyx文件相同的目录下。cythonize命令有其他的参数,可以通过命令行查看。这个命令也可以通过python -m Cython.Build.Cythonize -i <.pyx File>来完成。

在对Python 代码进行保护时,可以采用Cython对Python代码记性二进制转换,尽管可以对二进制文件进行逆向工程,但是依然会起到一定的保护作用.

对项目包进行编译

对以下结构的包进行编译:

tree

  • 通过setup.py文件进行简单编译

在使用该方法编译的时候,一个函数只有一个参数时,有可能会出现无法找到函数的情况,鉴于这种情况,请参考复杂编译的过程。以Cython官方实例为例:

setup.py文件:

1
2
3
4
5
6
7
from distutils.core import setup
from Cython.Build import cythonize

setup(
name = 'Hello world app',
ext_modules = cythonize("hello.py"),
)

hello.py文件:

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
def say_hello_to(name):
print("Hello %s!" % name)
```

<!--more-->


![example1](/images/Cython_example1.png)

运行`python setup.py build_ext --inplace`,生成`.so`文件,删除该文件夹中的`py文件`和`c文件`。
在终端导入使用:
![example2](/images/Cython_example2.png)

* 使用`setup.py`文件进行复杂编译

``` python
from setuptools import setup
from setuptools.extension import Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

setup(
ext_modules=cythonize(
[
Extension('mypkg.*', ['mypkg/*.py']),
Extension('mypkg.submypkg1.*', ['mypkg/submypkg1/*.py']),
Extension('mypkg.submypkg2.*', ['mypkg/submypkg2/*.py']),
Extension('mypkg2.*', ['mypkg2/*.py']),
],
build_dir='build',
compiler_directives=dict(
always_allow_keywords=True
)
),
cmdclass = dict(
build_ext=build_ext
),
)

结果:

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
.
├── build
│   ├── mypkg
│   │   ├── bar.c
│   │   ├── foo.c
│   │   ├── __init__.c
│   │   ├── submypkg1
│   │   │   ├── bar.c
│   │   │   ├── foo.c
│   │   │   └── __init__.c
│   │   └── submypkg2
│   │   ├── bar.c
│   │   ├── foo.c
│   │   └── __init__.c
│   ├── mypkg2
│   │   ├── bar.c
│   │   ├── foo.c
│   │   └── __init__.c
│   └── temp.linux-x86_64-3.7
│   └── build
│   ├── mypkg
│   │   ├── bar.o
│   │   ├── foo.o
│   │   ├── __init__.o
│   │   ├── submypkg1
│   │   │   ├── bar.o
│   │   │   ├── foo.o
│   │   │   └── __init__.o
│   │   └── submypkg2
│   │   ├── bar.o
│   │   ├── foo.o
│   │   └── __init__.o
│   └── mypkg2
│   ├── bar.o
│   ├── foo.o
│   └── __init__.o
├── mypkg
│   ├── bar.cpython-37m-x86_64-linux-gnu.so
│   ├── bar.py
│   ├── foo.cpython-37m-x86_64-linux-gnu.so
│   ├── foo.py
│   ├── __init__.cpython-37m-x86_64-linux-gnu.so
│   ├── __init__.py
│   ├── submypkg1
│   │   ├── bar.cpython-37m-x86_64-linux-gnu.so
│   │   ├── bar.py
│   │   ├── foo.cpython-37m-x86_64-linux-gnu.so
│   │   ├── foo.py
│   │   ├── __init__.cpython-37m-x86_64-linux-gnu.so
│   │   └── __init__.py
│   └── submypkg2
│   ├── bar.cpython-37m-x86_64-linux-gnu.so
│   ├── bar.py
│   ├── foo.cpython-37m-x86_64-linux-gnu.so
│   ├── foo.py
│   ├── __init__.cpython-37m-x86_64-linux-gnu.so
│   └── __init__.py
├── mypkg2
│   ├── bar.cpython-37m-x86_64-linux-gnu.so
│   ├── bar.py
│   ├── foo.cpython-37m-x86_64-linux-gnu.so
│   ├── foo.py
│   ├── __init__.cpython-37m-x86_64-linux-gnu.so
│   └── __init__.py
└── setup.py
  • 运行文件python setup.py build_ext --inplace
  • 最终会在当前文件夹中生成build文件夹,同时在build文件夹中生成相应的文件夹和.c文件,在主目录对应的文件夹中生成.so文件,此时的.so文件就可以通过导入的方式直接使用。
  • 如果需求有改动,需要删除相应的.so文件后重新生成该文件。

注意事项:

  • always_allow_keywords=True参数一定要添加,因为always_allow_keywords指令禁用具有大量参数的函数只允许使用关键字参数,如果不禁用,此处在传入一个参数时会找不到对应的函数。
  • 在使用第二种方法时,一定要注意一点,在有celery task任务的文件中,无法进行编译,否则在编译过后会出现celery无法启动的情况。
    • 对上文出现的问题进行给出一个解答:
      在上文中,如果编译了带有task任务的文件,会出现以下错误
      1
      AttributeError: 'method-wrapper' object has no attribute '__module__'
      解决方法为:
      • 创建第三方真实执行逻辑的文件,在进行加密时,加密第三方逻辑文件,对celery调度任务的文件不进行加密。
      • 见官方给出的解决方式Issues,不过此处不建议更改celery源文件。

参考链接:

编码不易,转载请注意出处!

前言

一直一来,我对Celery最不满意的一点除了有无数的bug之外就是不能在代码更新时重新启动Celery,其实在Celery3.1.0 之前有这样一个参数 --autoreload 是可以自动重启的,但是不知道是因为什么原因,在之后的版本中,此参数被干掉了。所以我决定在Celery之外对它进行自动重启策略。

watchdog

watchdog 直译过来叫看门狗,事实上它就叫看门狗😁,那看门狗有什么用? 屋外有动静狗叫两声,屋里人就听到了。那在程序里面就相当于Celery工作实例代码有变动,就会通知celery负责启动的实例,从而做出响应(重启),没错,你很轻松就了解了它的工作原理😬。

阅读全文 »

编码不易,转载请注意出处!

1、存储结构

InnoDB:底层是B+树,主键索引上的叶子结点存入的是一行所有列的数据,所以是索引组织表,数据和索引都放在一个文件。

MyISAM:底层也是B+树,不过叶子结点上存的是实际数据的指针地址,所以是堆表。MyISAM在磁盘上存储成三个文件,.frm文件存储表定义,.MYD 文件存储每行数据。.MYI 文件存储索引。

2、事务支持

MyISAM:强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。

InnoDB:提供事务支持事务,外部键等高级数据库功能。

3、表锁差异

MyISAM:只支持表级锁,用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。

InnoDB:支持事务和行级锁,是innodb的最大特色。行锁大幅度提高了多用户并发操作的新能。但是InnoDB的行锁,只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的。

4、全文索引

MyISAM:支持 FULLTEXT类型的全文索引。

InnoDB:5.7之后才支持FULLTEXT类型的全文索引。

5、表主键

MyISAM:允许没有任何索引和主键的表存在,索引都是保存行的地址。

nnoDB:如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。

6、表的具体行数

MyISAM:保存有表的总行数,如果select count(*) from table;会直接取出该值。

InnoDB:没有保存表的总行数,如果使用select count(*) from table,就是便利成本最小的索引树查询总行数,一般情况下是基于主键索引树去找,但是如果有搜索成本更小的,比如某个索引列上都是1字节的数据,那么就会走次索引。

编码不易,转载请注意出处!

介绍

Source code: Lib/collections/init.py

Collections(容器数据类型)模块是Python的内置库,在python2.4版本被首次引入。
python的内置数据容器dict、list、set、tuple有的场景并不适用,Collections则提供更为复杂的数据容器的一些操作,可以认为它是Python内置数据容器的替代选择。
在本文章中,我将向您介绍Python Collections模块中的6种最常用的数据结构。它们如下:

阅读全文 »

Docker容器日志输出到syslog

检查是否安装syslog

syslog 是 linux 强大的日志系统,Rsyslog 是 syslog 的标准实现,linux 已经默认安装,查看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ rsyslogd -v
rsyslogd 8.32.0, compiled with:
PLATFORM: x86_64-pc-linux-gnu
PLATFORM (lsb_release -d):
FEATURE_REGEXP: Yes
GSSAPI Kerberos 5 support: Yes
FEATURE_DEBUG (debug build, slow code): No
32bit Atomic operations supported: Yes
64bit Atomic operations supported: Yes
memory allocator: system default
Runtime Instrumentation (slow code): No
uuid support: Yes
systemd support: Yes
Number of Bits in RainerScript integers: 64

See http://www.rsyslog.com for more information.
阅读全文 »

编码不易,转载请注意出处!

本文按照下面图片中的问题依次深入了解tcp相关知识
本次文章不涉及 TCP 流量控制、拥塞控制、可靠性传输等方面知识,这些留在下篇!

最近在温习TCP的相关知识,不得不说,真是一个伟大的协议,为了对抗网络的不确定性用了很多机制去保证,尽最大努力去保障数据安全传输,真是受益匪浅。

正文

本文将描述TCP作为一个可靠的数据传输协议是如何保证其可靠性的。

为了实现可靠性传输,需要考虑很多事情,例如数据的破坏、丢包、重复以及分片顺序混乱等问题。如不能解决这些问题,也就无从谈起可靠传输。

那么,TCP 是通过序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输的。

今天,将重点介绍 TCP 的重传机制、滑动窗口、流量控制、拥塞控制。

阅读全文 »