PowerJob源码学习

本文深入分析了PowerJob的服务端启动类Initializer和客户端启动类PowerJobWorker,讲解了其初始化过程,包括日志上报、健康上报等。介绍了scheduleAtFixedRate和scheduleWithFixedDelay的区别,并探讨了客户端与服务端的Akka服务交互,如TaskTrackerActor、ProcessorTrackerActor和WorkerActor的角色与功能。此外,还提到了服务端Vert服务和日志模块的实现细节,如Actor消息模式、重试工具类及Google Cache的使用。

*******************************PowerJob学习*********************************************************
服务端启动类:tech.powerjob.server.core.handler.impl.Initializer:
知识点:
@Component
@ConditionalOnExpression("'${execution.env}'!='test'") + @PostConstruct 程序启动后会初始化加载这个注释下的方法

客户端启动类:tech.powerjob.worker.PowerJobWorker :
知识点:实现了InitializingBean类的afterPropertiesSet()方法
初始化包括了【日志上报,健康上报,客户端发送http请求获取appName是否可用】。

上报方式:
workerRuntime.getExecutorManager().getCoreExecutor().scheduleAtFixedRate(new WorkerHealthReporter(workerRuntime), 0, config.getHealthReportInterval(), TimeUnit.SECONDS);
workerRuntime.getExecutorManager().getCoreExecutor().scheduleWithFixedDelay(omsLogHandler.logSubmitter, 0, 5, TimeUnit.SECONDS);

scheduleAtFixedRate:执行任务开始计时,不算是否执行完,间隔一定时间,执行下一次。
scheduleWithFixedDelay:执行完上一次任务,间隔一定时间,执行下一次。
四个参数说明:(
command:执行线程
initialDelay:初始化延时
period:两次开始执行最小间隔时间
unit:计时单位


客户端akka服务:
1:[【TaskTrackerActor】worker 上报 task 执行情况,服务端调度任务请求(一次任务处理的入口),WorkerMapTaskRequest
ProcessorTracker 定时向 TaskTracker 上报健康状态,服务器要求任务实例停止执行请求,
服务器查询实例运行状态、需要返回详细的运行数据
                .match(ProcessorReportTaskStatusReq.class, this::onReceiveProcessorReportTaskStatusReq)
                .match(ServerScheduleJobReq.class, this::onReceiveServerScheduleJobReq)
调度中心分发任务请求  .match(ProcessorMapTaskRequest.class, this::onReceiveProcessorMapTaskRequest)
                .match(ProcessorTrackerStatusReportReq.class, this::onReceiveProcessorTrackerStatusReportReq)
                .match(ServerStopInstanceReq.class, this::onReceiveServerStopInstanceReq)
                .match(ServerQueryInstanceStatusReq.class, this::onReceiveServerQueryInstanceStatusReq)
2:【ProcessorTrackerActor】 TaskTracker 派发 task 进行执行,TaskTracker 停止 ProcessorTracker,释放相关资源
                .match(TaskTrackerStartTaskReq.class, this::onReceiveTaskTrackerStartTaskReq)
                .match(TaskTrackerStopInstanceReq.class, this::onReceiveTaskTrackerStopInstanceReq)

3:【WorkerActor】Worker部署Container请求,服务器销毁容器请求,服务端调度任务请求(一次任务处理的入口)
服务器要求任务实例停止执行请求,服务器查询实例运行状态、需要返回详细的运行数据
                .match(ServerDeployContainerRequest.class, this::onReceiveServerDeployContainerRequest)
                .match(ServerDestroyContainerRequest.class, this::onReceiveServerDestroyContainerRequest)
                .match(ServerScheduleJobReq.class, this::forward2TaskTracker)
                .match(ServerStopInstanceReq.class, this::forward2TaskTracker)
                .match(ServerQueryInstanceStatusReq.class, this::forward2TaskTracker)

客户端发送akka请求:
tech.powerjob.server.remote.transport.starter.AkkaStarter.getFriendActor(String):
获取friendAcctor中的服务,对应下面服务端akka服务:1
tech.powerjob.server.remote.transport.starter.AkkaStarter.getWorkerActor(String)
获取WorkerActor中的服务,对应下面服务端akka服务:2

服务端akka服务:
1:ping , 原创执行命令【friendAcctor】
                .match(Ping.class, this::onReceivePing)
                .match(RemoteProcessReq.class, this::onReceiveRemoteProcessReq)

2:心跳检测,TaskTracker 将状态上报给服务器, 日志上报请求,
Worker需要部署容器、主动向Server请求信息, worker 查询 执行器集群(动态上线需要)
                .match(WorkerHeartbeat.class, hb -> fetchWorkerRequestHandler().processWorkerHeartbeat(hb))
                .match(TaskTrackerReportInstanceStatusReq.class, this::onReceiveTaskTrackerReportInstanceStatusReq)
                .match(WorkerLogReportReq.class, req -> fetchWorkerRequestHandler().processWorkerLogReport(req))
                .match(WorkerNeedDeployContainerRequest.class, this::onReceiveWorkerNeedDeployContainerRequest)
                .match(WorkerQueryExecutorClusterReq.class, this::onReceiveWorkerQueryExecutorClusterReq)

服务端发送akka请求
【tech.powerjob.server.core.DispatchService.dispatch(JobInfoDO, Long, Optional<InstanceInfoDO>, Optional<Holder<Boolean>>)】
服务器分发任务给客户端请求:【ServerScheduleJobReq】 
transportService.tell(Protocol.of(taskTracker.getProtocol()), taskTrackerAddress, req);

【ServerStopInstanceReq】
 transportService.tell(Protocol.of(workerInfo.getProtocol()), workerInfo.getAddress(), req);

服务器查询实例运行状态、需要返回详细的运行数据
【服务器查询实例运行状态、需要返回详细的运行数据】               
 AskResponse askResponse = transportService.ask(Protocol.of(workerInfo.getProtocol()), workerInfo.getAddress(), req);


服务端vert服务:
1:/server/heartbeat
2:/server/statusReport
3:/server/logReport


日志模块分析:
从页面开始分析:
详情:/detail 不是运行状态就查数据库instance_info  【客户端3;ServerQueryInstanceStatusReq】

重试工具类:String resultDTOStr = CommonUtils.executeWithRetry0(() -> HttpUtils.get(realUrl));


   private final Lock reportLock = new ReentrantLock();加锁
   
   ArrayBlockingQueue阻塞队列 不支持多线程,单锁
   
   LinkedBlockingQueue  支持多线程,双锁与ArrayBlockingQueue不同的是,LinkedBlockingQueue拥有两把锁,一把读锁,一把写锁,可以在多线程情况下,满足同时出队和入队的操作。
   设置原数:
   put(E e)是阻塞式插入,如果队列中的元素与链表长度相同,则此线程等待,直到有空余空间时,才执行。
   offer(E e)是非阻塞式插入,队列中的元素与链表长度相同时,直接返回false,不会阻塞线程。
   获取原数:
   take():阻塞式出队,获取队列头部元素,如果队列中没有元素,则此线程的等待,直到队列中有元素才执行。
   poll():非阻塞式出队,当队列中没有元素,则返回null.
****************************************************************************************
1、学习AKKa
1,用actorsystem来创建actor,
ActorSystem.actorOf()是用来生成actor,
ActorSystem.actorSelection()用来查找actor(远程调用,只要查找到和调用本地服务使用是一样)

actorOf:创建一个新的Actor。创建的Actor为调用该方法时所属的Context下的直接子Actor;
actorSelection:当消息传递来时,只查找现有的Actor,而不会创建新的Actor;在创建了selection时,也不会验证目标Actors是否存在;
actorFor(已经被actorSelection所deprecated):只会查找现有的Actor,而不会创建新的Actor。

Akka 有 4 种核心的 Actor 消息模式: tell 、ask 、forward 和 pipe。

Ask:向 Actor 发送一条消息,返回一个 Future。当 Actor 返回响应时,会返回 Future。不会向消息发送者的邮箱返回任何消息。
Tell:向 Actor 发送一条消息。所有发送至 sender()的响应都会返回给发送消息的 Actor。
Forward:将接收到的消息再发送给另一个 Actor。所有发送至 sender() 的响应都会返回给原始消息的发送者。
Pipe:用于将 Future 的结果返回给 sender() 或 另外一个 Actor。如果正在使用 Ask 或是处理一个 Future,那么使用 Pipe 可以正确地返回 Future 的结果。


2、学习vertx

 CommonUtils.executeWithRetry(4, 100); 错误重试
 
 String str="1=a&2=b&3=c";
        Map<String, String> paramsMap = Splitter.on("&").withKeyValueSeparator("=").split(str);

@Component
@ConditionalOnExpression("'${execution.env}'!='test'")
public class Initializer {

    @PostConstruct //注解在程序加载完执行,相当于初始化。
*********************************使用InitializingBean三个地方:*****************************************************
package tech.powerjob.server.core.instance;【InstanceMetadataService】使用google 的cache,来缓存JobInfo
package tech.powerjob.server.core.scheduler【CoreScheduleTaskManager】启动一些必要的线程(定时清理,数据清理,状态检查)
package tech.powerjob.server.persistence.mongodb【GridFsManager】动态根据配置文件查询是否开启mongodb来初始化mongodb数据库
package tech.powerjob.worker【PowerJobWorker】客户端启动类

**************************文件下载到本地临时目录*********************************************
 FileUtils.forceMkdirParent(tmpFile);
            file.transferTo(tmpFile);
***********************************************************************

    Google cache 的使用
     private Cache<Long, JobInfoDO> instanceId2JobInfoCache;
创建:  instanceId2JobInfoCache = CacheBuilder.newBuilder()
                .concurrencyLevel(CACHE_CONCURRENCY_LEVEL)
                .maximumSize(instanceMetadataCacheSize)
                .softValues()
                .build();
添加:instanceId2JobInfoCache.put(instanceId, jobInfoDO);
删除: instanceId2JobInfoCache.invalidate(instanceId);    
*****************************【lombok】@RequiredArgsConstructor*******************************************    
相当于构造器注入了,不需要@Autowired

使用说明:作用于类,用于生成包含 final 和 @NonNull 注解的成员变量的构造方法,构造参数按先后顺序。
    
#############################步骤@Async使用步骤2步:####################
@Slf4j
@EnableAsync
@Configuration
public class ThreadPoolConfig {

    @Bean(PJThreadPool.TIMING_POOL)
    public TaskExecutor getTimingPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 4);
        // use SynchronousQueue
        executor.setQueueCapacity(0);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("PJ-TIMING-");
        executor.setRejectedExecutionHandler(new NewThreadRunRejectedExecutionHandler(PJThreadPool.TIMING_POOL));
        return executor;
    }
}
    
      @Async(value = PJThreadPool.LOCAL_DB_POOL)
    public void submitLogs(String workerAddress, List<InstanceLogContent> logs) {

        List<LocalInstanceLogDO> logList = logs.stream().map(x -> {
            instanceId2LastReportTime.put(x.getInstanceId(), System.currentTimeMillis());

            LocalInstanceLogDO y = new LocalInstanceLogDO();
            BeanUtils.copyProperties(x, y);
            y.setWorkerAddress(workerAddress);
            return y;
        }).collect(Collectors.toList());

        try {
            CommonUtils.executeWithRetry0(() -> localInstanceLogRepository.saveAll(logList));
        }catch (Exception e) {
            log.warn("[InstanceLogService] persistent instance logs failed, these logs will be dropped: {}.", logs, e);
        }
    }
####################################################################    
 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值