broker可以禁用吗 time_皮皮调度(6)—— Gearman:你真的是传说中的“齿轮超人”吗?...

ea08630cdc50f8eab602ca6801a34c5d.png

在前文“皮皮调度(5)—— Jep With Py4j?”中,虽然发现 Jep + Py4j 的组合确实能实现Java和Python在同一个进程中互相调用(皮皮调度的重要能力)。但是其组合稍微有些怪,并且我不禁问自己,是否真的有必要一定限制Java和Python只能跑在同一进程中,我是否能放松这个限制?

接下来,我们来发散一下思维。

1. 微服务

是否可以把Java作为平台应用,而各个Python做成微服务。Java和Python之间通过HTTP REST服务进行通信。

不过这样做也有新的问题,首先便是“注册中心”的问题,也就是Java端平台程序如何找到某个功能所对应的服务。

这种由服务名找到服务所对应的具体地址(IP + 端口),让我们很自然的想到网络中“域名解析”。同样,我们也可以将域名解析用于服务发现。如果我们已经运行在 kubernetes 中,那么DNS解析将会是非常简单的,只需要将每个Python服务注册为一个k8s的service即可。

但是,同样不用什么都要使用kubernetes这种重型武器,(虽然k3s能大大简化kubernetes的安装部署),但是我们要支持个人使用,支持包含windows在内的操作系统,那kubernetes就不是很适合。

如果没有kubernetes,那么单独装一个DNS服务也不太适合,主要是每加一个Python服务都需要同时增加DNS服务中的解析记录,工作量太大,而且完全无法享受到编写Python插件的快乐。

那么,如果没有kubernetes,我们也可以引入“注册中心”的概念,比如:可以使用zookeeper、etcd、Eureka、Consul等,我们的Python服务在启动后,可以主动注册自己的服务和对应的IP + Port到注册中心,而Java平台则会监视注册中心中的记录的变化。当Java平台收到某个请求,可以通过在注册中心中找到的服务对应地址进行调用。当一个服务对应多个地址时,Java平台可以基于一些策略做负载均衡。

这些应该都能跑通,甚至我们能进一步使用更复杂点的“Service Mesh”比如:Istio和Linkerd来实现更多的监控和负载均衡等。但是,这些好像对于“皮皮调度”显得太过复杂了。而且在单机情况下,对于多个服务,其IP地址是同样的,我们只能换不同的端口。当服务多了,选择一个合适的端口就有点捉襟见肘了。

2. 消息队列

微服务的问题:

  • 每个Python微服务都要占据一个端口,如果微服务中实现的功能很小,那么占据一个网络端口的代价有点大

  • 而且当Java平台想调用某个服务时,它需要知道它调用的是哪个服务,耦合度仍然高了点

  • 对于微服务来说,其更主要是处理“Request”级别的任务,但是对于调度平台来说,我们主要处理的是“Task”级别的任务。对于一个Task来说,它可能会发送多个Request(比如:提交任务、取消任务、查询状态、获取结果等),所以,即使我们使用微服务,我们仍需要有高一层的SDK来方便实现Task相关任务

那么我们是否可以不用每个Python服务都启动一个监听端口呢?答案是可以的。

一种比较流行的做法是使用消息队列,比如稳定又小巧的 RabbitMQ https://www.rabbitmq.com/, (当然也可以用 Apache Pulsa, Apache Kafka等,但它们不如RabbitMQ简单和省资源)

使用了消息队列,比如RabbitMQ后,那么Python服务则不用再启动网络监听端口,而只是和 RabbitMQ 打交道即可,Python服务订阅RabbitMQ上由Java平台发起的任务请求,并把任务状态、结果等通过RabbitMQ返回给Java平台。

这样,有了“RabbitMQ”这个中间接线员后,我们的Java平台和Python服务都可以只关心自己的业务了,两边的逻辑有了一定隔离。另外,带了的一个更好的副产品是:我们有了系统任务情况的监控,通过监控RabbitMQ的消息,我们可以知道系统中当前正在执行哪些任务,以及它们都什么状态。也就是“系统状态白盒化”,这个对于理解和运维系统非常有帮助。

不过使用消息队列,意味着,我们又要额外增加一个复杂的组件,这不光是增加了机器上的资源使用,而且,消息队列本身的运维也是有挑战的。

3. 遇事不决问什么?

遇事不决时,尤其是当你在不熟悉的领域时,肯定不是问“百度”,也不是问“谷歌”。而现在更流行的办法是:参考类似开源代码中的实现。(当然,需要仔细阅读开源代码的协议,如果是AGPL协议,或者一些明确指定不能商用的协议,比如fair-code协议等,尽量不看吧。建议看一些 Apache、MIT、BSD等开放协议的代码)

那首先,对于"皮皮调度",我们可以看看Apache Airflow中的任务分发,Airflow支持好多不同的执行器。Airflow最早主要支持“Local Executor”和“Celery Executor”,分别用于单机调度和集群调度。当然现在增加了额外的“Dask Executor”和“Kubernetes Executor”等。

“Local Executor”是单机调度,主要逻辑就是启动子进程来执行任务。而 ”Celery Executor“则是利用 ”Celery“这个”分布式任务队列“(Distributed Task Queue),这个似乎是Task维度的”消息队列“,好像是我们要找的工具!

Celery本身对于任务的分发、获取等也是利用了 broker ”中间人“:Celery最常用的broker就是 RabbitMQ!和我们的想法也很类似。

除了Broker来进行消息传递外,Celery封装了很多Task相关功能:

  • Result Stores:虽然 RabbitMQ等broker可以在多方传递消息,但是其并不适合存储大数据量的结果,result store则是用于存储结果的,可以存储在SQL数据库、NoSQL、或文件系统中

  • 提供监控

  • Work-flows支持

  • Time & Rate Limits

  • Scheduling 定时调度等

Celery好像和我们需要寻觅的工具很类似,不过:

  • Celery主要是用于Python, 不支持Java

  • Celery也比较复杂

4. Celery的替代品——Gearman

我们是否能找到一个Celery的替代品呢,其需要:

  • 更好的Java支持

  • 更简单的架构

于是经过多次搜索,我发现了:Gearman。http://gearman.org/

Gearman的几个特点:

  • 多语言支持:支持广泛的语言(当然不是所有的语言都支持的一样好),比如:C、Java、Python、Node.js、PHP、Ruby、Go等

  • 执行效率高:简单的协议

  • 架构简单,支持单机,支持内嵌,也支持高可用

4846d2de0344bd5d773634f7262d2009.png

Gearman主要分为3个角色:

  • Job Server 任务服务器

  • Client API 也就是任务的客户端,在皮皮调度中就是 Java平台,其通过Client API向Job Server提交任务请求,获取任务状态,获取任务结果

  • Worker API 任务的具体执行端, 在皮皮调度中是Python服务,其在启动后会主动把自己注册到Job Server中,而当Client有对应请求时,JobServer则把任务转发给对应的Worker

Gearman的详细通信协议可以看:http://gearman.org/protocol/ 正是有了标准的通信协议,才使得各种语言都可以实现对应的协议,真正做到了:Client 和 Worker分别可以使用不同的编程语言进行协作。

5. Gearman初体验

可以参考 http://gearman.org/getting-started/ 上的示例。比如:

首先,启动一个Gearman Job Server,这里使用了C语言编写的 gearmand 命令行程序。

gearmand

然后,我们注册一个worker,这个worker用来启动 wc 这个命令行工具来统计行数

gearman -w -f wc – wc -l

这里 -w 说明其为 worker, -f 指定这个worker提供的服务名字为 wc, – 后面的则是具体执行的命令行工具

最后,我们来发起一个客户端来请求wc服务

gearman -f wc < /etc/passwd

马上就会返回该文件对应的行数!几乎和直接执行如下命令一样

wc -l /etc/passwd

另外,简单提一下Gearman最初解决的场景。最早的时候,LAMP (Linux, Apache, MySQL, PHP/Perl/Python) 流行的时候,网站可能是PHP写的,它可能比较适合于做些快速的请求,而不适合于需要长时间的任务。但是经常有些非常耗费时间和CPU的任务,比如:对于上传的图片产生缩微图等。这个时候,Gearman就发挥作用了。主要服务器上还是跑PHP主服务,但是可以启动几个小的服务器,上面来跑另外的命令行程序来处理上传的图像。最早的穷人版(poor man’s)的分布式处理就出现了。

更多示例和具体API可以参考Gearman的文档。

6. Gearman总结

好的地方:

  • Gearman支持多语言之间的互相调用,使得我们的皮皮调度未来不光是能支持Python的插件,还可以支持其它类型的插件,比如Go、Rust、shell等。

  • 更好的是:Gearman的Job Server不光是有C语言版本的实现,还有一个Java版本的实现 https://github.com/gearman/java-service/ 我大概试了一下,可以运行并通过GraalVM编译成原生可执行程序。并且Python或者Shell的Worker可以在Java的Job Server下工作!这样的话,我们就可以在皮皮调度的Java平台端进程中启动 Gearman 的Job Server和Client。而在Python端中启动Worker。只需要两个进程,并且不用修改C语言的代码。

  • 代码相对简单,并且启动的线程数也不多,省系统资源。

所以,不夸张的说,见到Gearman以后,真的是眼前一亮,觉得挺适合皮皮调度的场景!

但是实际中也有一些不太好的地方:

  • Gearman在9年前可能还比较流行,所以开发主要都在 2010~2012年间,后面就变动很少了。C语言端还相对好点,断断续续有人在维护(不是很频繁)。但是 Java端的代码,Python端的代码基本都没有再更新了。

  • Gearman Python端的代码之前只支持Python2.7, 不支持Python 3,虽然有个项目把它移植到了Python3,但是移植的有些问题。所以,我也花了一点时间把Gearman的Python client移植到了Python3(参考之前另一个人的实现),并解决了一些unicode的问题。可以参考我fork的项目:https://github.com/Baoqi/python-gearman

当然,对于老的项目,也不能说不好,或者完全不能采用。而还是要看是否能适合自己的应用场景。并且代码简单的好处是自己也可以进行修改,要知道,无论使用哪个开源项目,用得深了,最终都还是要看自己对那个项目是否可以掌控和修改。

Gearman,值得深入研究,期待Gearman能从”齿轮男“变为”齿轮超人“!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值