Skip to content

Commit 4405a82

Browse files
committed
add PowerJob
1 parent ee10284 commit 4405a82

File tree

14 files changed

+327
-2
lines changed

14 files changed

+327
-2
lines changed

contents/Java/PowerJob/3/1.png

121 KB
Loading

contents/Java/PowerJob/3/content.md

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# PowerJob 技术综述,能领悟多少就看你下多少功夫了~
2+
> 本文适合有 Java 基础知识的人群
3+
4+
![](../cover.png)
5+
6+
作者:HelloGitHub-**Salieri**
7+
8+
HelloGitHub 推出的[《讲解开源项目》](https://github.com/HelloGitHub-Team/Article)系列。从本章开始,就正式进入 PowerJob 框架的技术剖析环节了。作为技术系列文章开篇的第一章,本文会讲述 PowerJob 整体的架构设计,同时介绍相关的技术,以便于后面的讲解。
9+
10+
> 项目地址:
11+
>
12+
> https://github.com/KFCFans/PowerJob
13+
14+
15+
## 一、架构设计
16+
17+
前面说过,PowerJob 的设计目标是企业级的分布式任务调度框架,适合统一部署调度中心成为公司内部的任务调度中间件。因此,在架构设计时,不同于 QuartZ 这种自产自销一个 Jar 包搞定一切的模式,PowerJob 引入了调度中心来统一解决任务的配置和调度,具体的架构如下图所示:
18+
19+
![](1.png)
20+
21+
由图可见,整个 PowerJob 系统由调度中心(powerjob-server)和执行器(powerjob-worker)构成。
22+
23+
调度中心是一个基于 SpringBoot 的 Web 应用,根据提供服务的对象可以划分为对外和对内两层。对外部分面向用户,即提供 HTTP 服务,允许开发者在前端界面上可视化得完成任务、工作流等信息的配置与管理;对内部分则负责完成开发者所录入任务的调度和派发,同时维护注册到本注册中心所有执行器集群的状态。
24+
25+
执行器是一个普通的 Jar 包,需要接入调度中心的应用依赖该 Jar 包并完成初始化后,powerjob-worker 便正式启动并提供服务。执行器的整体逻辑非常简单(复杂的是MapReduce、广播等高级处理任务的实现,敬请期待后面的文章),就是监听来自调度中心的任务执行请求,一旦接收到任务就开始分配资源、初始化执行器开始处理,同时维护着一组后台线程定期上报自身的健康状态、任务执行状态。
26+
27+
调度中心和执行器之间通过 akka-remote 进行通讯。调度中心可以多实例部署来进行水平扩展,提升调度性能的同时做到调度中心高可用,执行器也可以通过集群部署实现高可用,同时,如果开发者实现了 MapReduce 这一具有分布式处理能力的处理器,也可以调动整个集群的计算资源完成任务的分布式计算。
28+
29+
## 二、知识点概览
30+
31+
总体来讲,PowerJob 中主要涉及了以下的知识点,通过阅读源码和之后的一系列技术剖析文章,你将能学到:
32+
33+
- Java 基础:Java 8 新特性(Stream、Optional、Lambda、FunctionalInterface)
34+
- Java 进阶:多线程与并发安全(线程池、并发容器、可重入锁、分段锁、ThreadLocal 等)、Java I/O(网络操作、文件流操作)、热加载(自定义类加载器、Jar包操作)
35+
- Java Web:主要是 SpringBoot 相关的 Web 知识,包括基础 Controller 用法、WebSocket、文件上传下载、ControllerAdvice 全局处理异常、跨域 *CORS 等*
36+
- Spring 相关:AOP(记录 Web 日志)、异步方法(@Async)、定时任务(@Scheduled)、自建容器(ClassPathXmlApplicationContext)、上下文使用(各种 Aware)
37+
- 数据库:编写数据库无关的持久化层代码(Spring Data JPA)、数据库基础理论(各种SQL、索引用法等)、多数据源配置、MongoDB (GridFS)的使用
38+
- 算法知识:图(DAG)、引用计数器(实现小型 GC)、分布式唯一 ID 算法(snowflake)、时间轮
39+
- 分布式知识:远程通讯、集群高可用、服务发现、故障转移与恢复、分布式一致性、分布式锁(基于数据库实现可靠的分布式锁)
40+
- 序列化相关:kryo、jackson-cbor、对象池技术
41+
- Akka 基础:Actor 模型、akka-remote、akka-serialization
42+
43+
如果你是初学的萌新,通过本项目和本教程,相信你能更好地掌握 Java 相关的基础知识。
44+
45+
如果你是轻车熟路的老司机,通过本项目和本教程,相信你也会在分布式计算、任务调度等方面有所启发。
46+
47+
## 三、总结与预告
48+
49+
本章介绍了 PowerJob 整体的架构设计以及项目中所涉及的相关技术知识点。下一章,我将会为大家带来 PowerJob 的基石:Akka Toolkit 的介绍与使用教程。
50+
51+
那我们下期再见喽~预告:下期内容很干,需要带足水!

contents/Java/PowerJob/4/1.png

21.2 KB
Loading

contents/Java/PowerJob/4/2.png

6.44 MB
Loading

contents/Java/PowerJob/4/content.md

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# 哇咔咔干货来啦:PowerJob 原理剖析之 Akka Toolkit
2+
> 本文适合有 Java 基础知识的人群
3+
4+
![](../cover.png)
5+
6+
作者:HelloGitHub-**Salieri**
7+
8+
HelloGitHub 推出的[《讲解开源项目》](https://github.com/HelloGitHub-Team/Article)系列。
9+
10+
> Akka is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala.
11+
12+
上面这段文字摘抄自 Akka 官网(akka.io),翻译成中文也就是:“Akka 是一个为 Java 和 Scala 构建高并发、分布式和弹性消息驱动应用程序的工具包”。而 Akka 具有的一切特性,其实都源自于一个用于处理并发计算问题的模型——Actor 模型。
13+
14+
> PowerJob 项目地址:
15+
>
16+
> https://github.com/KFCFans/PowerJob
17+
18+
## 一、Actor 模型
19+
20+
Actor 模型在 1973 年于 Carl Hewitt、Peter Bishop 及 Richard Steiger 的论文中提出,现在已经被用作并发计算的理论理解框架和并发系统的实际实现基础。
21+
22+
在计算机科学中,Actor 模型是一种并发运算上的模型。Actor 是一种程序上的抽象概念,被视为并发运算的基本单元:当一个 Actor 接收到一则消息,它可以做出一些决策、创建更多的 Actor 、发送更多的消息、决定要如何处理接下来的消息。Actors 可以修改它们自己的私有状态,但是只能通过消息间接的相互影响(避免了基于锁的同步)。
23+
24+
每一个 Actor 都由状态(State)、行为(Behavior)和邮箱(MailBox,其实就是一个消息队列)三部分组成:
25+
26+
- 状态:Actor 中的状态指 Actor 对象的变量信息,状态由 Actor 自己管理,避免了并发环境下的锁和内存原子性等问题。
27+
- 行为:Actor 中的计算逻辑,通过 Actor 接收到的消息来改变 Actor 的状态。
28+
- 邮箱:邮箱是 Actor 和 Actor 之间的通信桥梁,邮箱内部通过 FIFO(先入先出)消息队列来存储发送方 Actor 消息,接受方 Actor 从邮箱队列中获取消息。
29+
30+
![](1.png)
31+
32+
前面说了一大堆晦涩难懂的概念,相信大家看的也都云里雾里的。这里结合我自己的理解用白话文讲一下:其实 Actor 模型的设计思想就是事件驱动,可以简单理解为线程级的消息中间件。所有 Actor 之间不共享数据,只通过消息沟通,因此不用关心传统并发程序编写过程中的并发安全问题(因为根本没有共享的数据)。同时,得益于 Actor 底层轻巧的设计(这部分其实属于具体实现了,不过目前所有的实现 Actor 设计都很轻量),使得单机可以存在百万量级的 Actor,因此能够带来极好的并发性能。
33+
34+
此外,由于 Actor 模型中万物都是 Actor,所以它是天然支持分布式的,即不同机器之间的 Actor 通讯和本地 Actor 之间的通讯没有实质上的区别。
35+
36+
因此,只要你掌握了事件驱动的编程思想,利用 Actor 模型,结合具体的实现框架(比如 JVM 系的 Akka),能够轻松编写出高性能的分布式应用。
37+
38+
## 二、Akka Toolkits
39+
40+
Akka Toolkit 也就是 Akka 工具包,其实就是 JVM 平台上对 Actor 模型的一种实现。Akka 本身提供了完整的 Actor 模型支持,包括对并发/并行程序的简单的、高级别的抽象、异步、非阻塞、高性能的事件驱动编程模型和非常轻量的事件驱动处理。同时,作为一个“工具包”,Akka 还额外提供了许多功能,由于篇幅有限,这里就简单介绍几个包,有兴趣可以前往官网(见参考文档)详细了解~
41+
42+
* akka-streams:流处理组件,提供直观、安全的方式来进行异步、非阻塞的背压流处理。
43+
44+
* akka-http:HTTP 组件,现代、快速、异步、流媒体优先的 HTTP 服务器和客户端。
45+
46+
* akka-cluster:集群组件,包括集群成员管理、弹性路由等。
47+
48+
* akka-remote(artery-remoting):通讯组件,也是 PowerJob 所使用的核心组件,然而官网并不推荐直接使用(直接使用 remote 启动还会警告使用了过于底层的 API),普通分布式应用推荐直接使用 cluster。
49+
50+
* akka-persistence:持久化组件,提供“至少投递一次”的能力来保证消息的可靠送达。
51+
52+
## 三、Akka 简单使用
53+
54+
接下来是关于 Akka 的一个超简明教程,帮助大家初步理解并入门 Akka,其内容涵盖了所有 PowerJob 中用到的 API,也就是说,看懂这部分,源码中的 Akka 就不再可怕喽~
55+
56+
### 3.1 开发 Actor
57+
58+
首先,不得不提的一点是,Akka 从 2.6 版本开始,维护了 2 套 API(算上 Scala 和 Java 版本就 4 套了...看着IDE的智能提示就头大...),分别叫 classic 和 typed。typed 与原先的 classic 相比,最大的特色就是其具有了类型(Java 范型)。每一个 Actor 处理的消息类型可以直接由范型规定,从而有效限制程序 bug(将错误从运行期提前到了编译期)。然而,对于复杂系统要处理的消息不胜枚举,强类型就限制了一个 Actor 只能处理一种类型的消息。虽然从逻辑上来讲确实清晰,但实际工程实现中,必然导致代码阅读困难,整体结构松散(个人感觉这一点也是计算机科学与工程之间存在分歧的表现,当然也可能是我学艺不精,不了解正确的用法)。解释了那么多,终于可以点明主旨了~作者比较喜欢 classic,因此 PowerJob 只使用 AKKA classic API,本文也只涉及 AKKA classic API,反正官网说了会长期维护~
59+
60+
前面说过,对于 Actor 模型个人认为最简单的理解方式就是消息中间件。Actor 的本质是事件驱动,即接收消息并处理。反映到编程上,Actor 的开发也类似于消息中间件 consumer 的开发,无非是换了个接口、多几个功能罢了。
61+
62+
话不多说,看代码:
63+
64+
```java
65+
public class FriendActor extends AbstractActor {
66+
67+
@Override
68+
public Receive createReceive() {
69+
return receiveBuilder()
70+
.match(Ping.class, this::onReceivePing)
71+
.matchAny(obj -> log.warn("unknown request: {}.", obj))
72+
.build();
73+
}
74+
75+
private void onReceivePing(Ping ping) {
76+
getSender().tell(AskResponse.succeed(null), getSelf());
77+
}
78+
}
79+
```
80+
81+
首先自然是新建类并实现接口 `AbstractActor`,该接口需要重写 `createReceive` 方法,该方法需要一个 `Receive` 对象作为返回值。对于开发者而言,需要做的就是构建这个 `Receive` 对象,也就是指明该 Actor 接受到什么类型的消息时进行什么样的处理。
82+
83+
### 3.2 初始化 ActorSystem
84+
85+
Actor 作为处理消息的“角色”,就像工厂中的一个个工人,每个人各司其职,兢兢业业地接收指令完成任务。然而群龙不能无首,就像现实生活中工人需要由工厂来组织管理一样,Actor 也需要自己的工厂—— ActorSystem。为此,创建 Actor 之前,首先需要创建 ActorSystem。
86+
87+
PowerJob 使用以下方法创建 ActorSystem。其中,第一个参数指明了该 ActorSystem 的名称,第二个参数则传入了该 ActorSystem 所使用的配置信息,包括工作端口、序列化方式、日志级别等。
88+
89+
```java
90+
actorSystem = ActorSystem.create("powerjob-server", akkaConfig);
91+
```
92+
93+
完成 ActorSystem 这个“工厂”的创建后,就可以正式开始创建 Actor 了,代码如下所示:
94+
95+
```java
96+
actorSystem.actorOf(Props.create(FriendActor.class), "friend_actor");
97+
```
98+
99+
其中,第一个参数Props是一个用来在创建 Actor 时指定选项的配置类;
100+
101+
第二个参数则指定了该 Actor 的名称,通过该 Actor 的名称和其 ActorSystem 的名称,我们就可以构建出路径 `akka://powerjob-server/user/server_actor`(本地路径,远程路径需要变更协议并添加地址),然后轻松得根据该路径找到该 Actor,实现通信。
102+
103+
### 3.3 信息交互
104+
105+
完成 ActorSystem 的初始化和 Actor 的创建后,就可以正式使用 Akka 框架了。PowerJob 主要使用 Akka 框架的 remote 组件,用于完成系统中各个分布式节点的通讯。
106+
107+
```java
108+
String actorPath = "akka://[email protected]/user/friend_actor";
109+
ActorSelection actorSelect = actorSystem.actorSelection(actorPath);
110+
actorSelect.tell(startTaskReq, null);
111+
```
112+
113+
和其他通讯方式一样,进行通讯前,需要首先获取目标地址。根据 akka-remote 的语法规范,指定目标 Actor 的名称、其所在的 ActorSystem 名称和目标机器地址,即可获取用于通讯的 URI。得到 URI 后,便可通过 `actorselection()` 方法获取 Actorselection 对象。通过 Actorselection 对象,调用 tell 方法就可以向目标 Actor 发送消息了。
114+
115+
那么细心的小伙伴肯定要问了,PowerJob 之所以采用 akka-remote 作为底层通讯框架,是看上了它极简的通讯 API,看到这里,也没发现有多简单啊。发送一个 HTTP 请求,用高层封装库其实也就差不多三行代码的样子,你这用个 Akka 前置准备工作还那么多,说好的简单呢?那么下面就带大家来一探究竟,akka-remote 到底简单在哪里~
116+
117+
首先,如果不选择现有的协议,自己用 Netty 造轮子,那光 server、client、listener、codec 就一大堆代码了。如果使用现有协议如 HTTP,发送也许 3 行代码能搞定,但接收一定远不止三行。HTTP 全称超文本传输协议,那么传输的自然已经是经过序列化的文本数据了,所以接收方需要自行进行解码、解析,更别提异常处理、失败重试等功能了。而 akka-remote 呢?从刚刚 Actor 的代码中可以看出,match 方法后面跟的是一个具体的类,也就是说 Akka 自动帮你完成了反序列化,作为消息的接收方,是真正的拿到就能用,没有任何多余代码。同时,Akka 已经帮你搞定了各种异常后的处理。也就是说,使用 akka-remote,可以让数据接收方非常的简单,只专注逻辑的实现。
118+
119+
其次,在分布式环境中,通讯往往不是单向的。尤其是 PowerJob 这种追求高可用的框架,有时候为了确认消息送达,往往需要应答机制。akka-remote 提供了难以置信的 API 来回复请求:
120+
121+
```java
122+
AskResponse response = new AskResponse(true, "success");
123+
getSender().tell(response, getSelf());
124+
```
125+
126+
通过 `getSender()` 方法,就能获取到消息发送方的 Actor 引用对象,并通过该对象回复信息。
127+
128+
## 四、最后
129+
那么以上就是本篇文章全部的内容啦~
130+
131+
通过本篇文章,我相信大家已经了解了 Actor 模型的基础概念,同时掌握了 JVM 上 Actor 模型的实现——Akka 框架的简单使用。
132+
133+
下一篇文章,就是万众期待的 PowerJob 调度层原理分析啦(小伙伴进群必问榜 TOP 1)~我将会为大家揭秘是什么支撑着 PowerJob 的调度,让我能放肆“吹牛”说调度性能秒杀现有一切框架~
134+
135+
那我们下起再见喽~拜拜~
136+
137+
## 五、参考文献
138+
139+
- [官方文档](https://akka.io/docs/)
140+
141+
- [Actor_model wiki](https://en.wikipedia.org/wiki/Actor_model)
142+
143+
- [Actor 编程模型浅谈](http://jiangew.me/actor-model/)
144+
145+
146+
## 作者游记
147+
148+
![](2.png)

contents/Java/PowerJob/5/1.png

580 KB
Loading

contents/Java/PowerJob/5/2.png

2.22 MB
Loading

contents/Java/PowerJob/5/3.png

1.14 MB
Loading

contents/Java/PowerJob/5/4.png

239 KB
Loading

contents/Java/PowerJob/5/5.png

2.91 MB
Loading

contents/Java/PowerJob/5/6.png

257 KB
Loading

contents/Java/PowerJob/5/7.png

1.25 MB
Loading

0 commit comments

Comments
 (0)