-
Notifications
You must be signed in to change notification settings - Fork 32
Feat: add batch install biz handler #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f2acaf0
0fbbdb4
f5390a8
fc6cd49
7ad883c
742ed44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package com.alipay.sofa.koupleless.arklet.core.command.builtin.handler; | ||
|
|
||
| import com.alipay.sofa.ark.api.ResponseCode; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.builtin.handler.BatchInstallBizHandler.BatchInstallInput; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.builtin.handler.BatchInstallBizHandler.BatchInstallResponse; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.builtin.handler.InstallBizHandler.Input; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.meta.AbstractCommandHandler; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.meta.Command; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.meta.Output; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.meta.bizops.ArkBizMeta; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.meta.bizops.ArkBizOps; | ||
| import com.alipay.sofa.koupleless.arklet.core.common.exception.ArkletRuntimeException; | ||
| import com.alipay.sofa.koupleless.arklet.core.common.exception.CommandValidationException; | ||
| import com.alipay.sofa.koupleless.arklet.core.common.model.BatchInstallRequest; | ||
| import com.alipay.sofa.koupleless.arklet.core.common.model.InstallRequest; | ||
| import com.alipay.sofa.koupleless.arklet.core.util.ResourceUtils; | ||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
|
|
||
| import java.lang.management.MemoryPoolMXBean; | ||
| import java.util.ArrayList; | ||
|
|
||
| import static com.alipay.sofa.koupleless.arklet.core.command.builtin.BuiltinCommand.BATCH_INSTALL_BIZ; | ||
|
|
||
| /** | ||
| * @author lianglipeng.llp@alibaba-inc.com | ||
| * @version $Id: BatchInstallBizHandler.java, v 0.1 2024年12月18日 15:04 立蓬 Exp $ | ||
| */ | ||
| public class BatchInstallBizHandler extends | ||
| AbstractCommandHandler<BatchInstallInput, BatchInstallResponse> | ||
| implements ArkBizOps { | ||
|
|
||
| @Override | ||
| public void validate(BatchInstallInput batchInstallInput) throws CommandValidationException { | ||
| notNull(batchInstallInput.getBizList(), "bizList is null"); | ||
|
|
||
| for (Input bizInput : batchInstallInput.bizList) { | ||
| bizInput.setAsync(false); | ||
| InstallBizHandler.validateInput(bizInput); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Output<BatchInstallResponse> handle(BatchInstallInput batchInstallInput) { | ||
| MemoryPoolMXBean metaSpaceMXBean = ResourceUtils.getMetaSpaceMXBean(); | ||
| long startSpace = metaSpaceMXBean.getUsage().getUsed(); | ||
| try { | ||
| BatchInstallResponse response = convertClientResponse( | ||
| getOperationService().batchInstall(convertBatchInstallRequest(batchInstallInput))); | ||
| response.setElapsedSpace(metaSpaceMXBean.getUsage().getUsed() - startSpace); | ||
| if (ResponseCode.SUCCESS.equals(response.getCode())) { | ||
| return Output.ofSuccess(response); | ||
| } else { | ||
| return Output.ofFailed(response, "install biz not success!"); | ||
| } | ||
| } catch (Throwable e) { | ||
| throw new ArkletRuntimeException(e); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Command command() { | ||
| return BATCH_INSTALL_BIZ; | ||
| } | ||
|
|
||
| private BatchInstallRequest convertBatchInstallRequest(BatchInstallInput input) { | ||
| ArrayList<InstallRequest> installRequestList = new ArrayList<>(); | ||
|
|
||
| for (Input bizInput : input.getBizList()) { | ||
| installRequestList.add(InstallRequest.builder().bizName(bizInput.getBizName()) | ||
| .bizVersion(bizInput.getBizVersion()).bizUrl(bizInput.getBizUrl()) | ||
| .args(bizInput.getArgs()).envs(bizInput.getEnvs()) | ||
| .installStrategy(bizInput.getInstallStrategy()).build()); | ||
| } | ||
| return BatchInstallRequest.builder() | ||
| .installRequests(installRequestList.toArray(new InstallRequest[0])).build(); | ||
| } | ||
|
|
||
| private BatchInstallResponse convertClientResponse(com.alipay.sofa.koupleless.arklet.core.common.model.BatchInstallResponse res) { | ||
| BatchInstallResponse response = new BatchInstallResponse(); | ||
| response.setBizUrlToResponse(res.getBizUrlToResponse()); | ||
| response.setCode(res.getCode()); | ||
| response.setMessage(res.getMessage()); | ||
| return response; | ||
| } | ||
|
|
||
| @Getter | ||
| @Setter | ||
| public static class BatchInstallInput extends ArkBizMeta { | ||
| private Input[] bizList; | ||
| } | ||
|
|
||
| @Getter | ||
| @Setter | ||
| public static class BatchInstallResponse extends | ||
| com.alipay.sofa.koupleless.arklet.core.common.model.BatchInstallResponse { | ||
| private long elapsedSpace; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -37,10 +37,15 @@ public class BatchInstallRequest { | |||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * 本地文件系统目录。 | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| private String bizDirAbsolutePath; | ||||||||||||||||||||||
| private String bizDirAbsolutePath; | ||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * 静态合并部署,默认没有老版本模块,可以直接使用普通安装策略。 | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| @Builder.Default | ||||||||||||||||||||||
| private String installStrategy = STRATEGY_INSTALL_ONLY_STRATEGY; | ||||||||||||||||||||||
| private String installStrategy = STRATEGY_INSTALL_ONLY_STRATEGY; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * 模块批量发布请求。 | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| private InstallRequest[] installRequests = new InstallRequest[0]; | ||||||||||||||||||||||
|
Comment on lines
+47
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add validation constraints for installRequests The field lacks validation annotations. Consider adding:
/**
* 模块批量发布请求。
*/
+ @NotNull
+ @Size(min = 1, message = "At least one install request must be provided")
private InstallRequest[] installRequests = new InstallRequest[0];📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,11 @@ | |
| import com.alipay.sofa.ark.api.ResponseCode; | ||
| import com.alipay.sofa.ark.spi.constant.Constants; | ||
| import com.alipay.sofa.ark.spi.model.Biz; | ||
| import com.alipay.sofa.common.utils.StringUtil; | ||
| import com.alipay.sofa.koupleless.arklet.core.command.executor.ExecutorServiceManager; | ||
| import com.alipay.sofa.koupleless.arklet.core.ops.strategy.BatchInstallBizInDirAbsolutePathStrategy; | ||
| import com.alipay.sofa.koupleless.arklet.core.ops.strategy.BatchInstallBizInRequestStrategy; | ||
| import com.alipay.sofa.koupleless.arklet.core.ops.strategy.BatchInstallStrategy; | ||
| import com.alipay.sofa.koupleless.common.log.ArkletLoggerFactory; | ||
| import com.alipay.sofa.koupleless.arklet.core.common.model.BatchInstallRequest; | ||
| import com.alipay.sofa.koupleless.arklet.core.common.model.BatchInstallResponse; | ||
|
|
@@ -35,6 +39,7 @@ | |
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Map.Entry; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.concurrent.ThreadPoolExecutor; | ||
|
|
||
|
|
@@ -47,8 +52,9 @@ | |
| */ | ||
| @Singleton | ||
| public class UnifiedOperationServiceImpl implements UnifiedOperationService { | ||
| private final BatchInstallStrategy batchInstallBizInDirAbsolutePathStrategy = new BatchInstallBizInDirAbsolutePathStrategy(); | ||
|
|
||
| private BatchInstallHelper batchInstallHelper = new BatchInstallHelper(); | ||
| private final BatchInstallStrategy batchInstallBizInRequestStrategy = new BatchInstallBizInRequestStrategy(); | ||
|
|
||
| /** {@inheritDoc} */ | ||
| @Override | ||
|
|
@@ -72,20 +78,12 @@ public ClientResponse install(InstallRequest request) throws Throwable { | |
| /** | ||
| * <p>safeBatchInstall.</p> | ||
| * | ||
| * @param bizAbsolutePath a {@link java.lang.String} object | ||
| * @param bizRequest a {@link InstallRequest} object | ||
| * @return a {@link com.alipay.sofa.ark.api.ClientResponse} object | ||
| */ | ||
| public ClientResponse safeBatchInstall(String bizAbsolutePath, String installStrategy) { | ||
| public ClientResponse safeBatchInstall(InstallRequest bizRequest) { | ||
| try { | ||
| String bizUrl = OSUtils.getLocalFileProtocolPrefix() + bizAbsolutePath; | ||
| Map<String, Object> mainAttributes = batchInstallHelper | ||
| .getMainAttributes(bizAbsolutePath); | ||
| String bizName = (String) mainAttributes.get(Constants.ARK_BIZ_NAME); | ||
| String bizVersion = (String) mainAttributes.get(Constants.ARK_BIZ_VERSION); | ||
|
|
||
| InstallRequest installRequest = InstallRequest.builder().bizUrl(bizUrl).bizName(bizName) | ||
| .bizVersion(bizVersion).installStrategy(installStrategy).build(); | ||
| return install(installRequest); | ||
| return install(bizRequest); | ||
| } catch (Throwable throwable) { | ||
| throwable.printStackTrace(); | ||
| return new ClientResponse().setCode(ResponseCode.FAILED) | ||
|
|
@@ -103,27 +101,29 @@ public ClientResponse uninstall(String bizName, String bizVersion) throws Throwa | |
| @Override | ||
| public BatchInstallResponse batchInstall(BatchInstallRequest request) throws Throwable { | ||
| long startTimestamp = System.currentTimeMillis(); | ||
| Map<Integer, List<String>> bizUrls = batchInstallHelper | ||
| .getBizUrlsFromLocalFileSystem(request.getBizDirAbsolutePath()); | ||
| ThreadPoolExecutor executorService = ExecutorServiceManager.getArkBizOpsExecutor(); | ||
|
|
||
| BatchInstallStrategy batchInstallStrategy = getBatchInstallStrategy(request); | ||
| Map<Integer, List<InstallRequest>> installRequestsWithOrder = batchInstallStrategy | ||
| .convertToInstallInput(request); | ||
|
|
||
| ThreadPoolExecutor executorService = ExecutorServiceManager.getArkBizOpsExecutor(); | ||
| Map<String, ClientResponse> bizUrlToInstallResult = new HashMap<>(); | ||
| boolean hasFailed = false; | ||
| for (Map.Entry<Integer, List<String>> entry : bizUrls.entrySet()) { | ||
| List<String> bizUrlsInSameOrder = entry.getValue(); | ||
| for (Entry<Integer, List<InstallRequest>> entry : installRequestsWithOrder.entrySet()) { | ||
| List<InstallRequest> bizRequestInSameOrder = entry.getValue(); | ||
| List<CompletableFuture<ClientResponse>> futures = new ArrayList<>(); | ||
| for (String bizUrl : bizUrlsInSameOrder) { | ||
| futures.add(CompletableFuture.supplyAsync( | ||
| () -> safeBatchInstall(bizUrl, request.getInstallStrategy()), executorService)); | ||
| for (InstallRequest bizRequest : bizRequestInSameOrder) { | ||
| futures.add(CompletableFuture.supplyAsync(() -> safeBatchInstall(bizRequest), | ||
| executorService)); | ||
|
Comment on lines
+109
to
+117
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add concurrent execution limits for batch installations The current implementation might overwhelm the system if there are too many concurrent installations. Consider adding a limit to the number of concurrent installations. ThreadPoolExecutor executorService = ExecutorServiceManager.getArkBizOpsExecutor();
+int maxConcurrentInstalls = Math.min(bizRequestInSameOrder.size(), 5); // Adjust the limit as needed
+List<List<InstallRequest>> batches = Lists.partition(bizRequestInSameOrder, maxConcurrentInstalls);
Map<String, ClientResponse> bizUrlToInstallResult = new HashMap<>();
boolean hasFailed = false;
-for (InstallRequest bizRequest : bizRequestInSameOrder) {
- futures.add(CompletableFuture.supplyAsync(() -> safeBatchInstall(bizRequest),
- executorService));
+for (List<InstallRequest> batch : batches) {
+ for (InstallRequest bizRequest : batch) {
+ futures.add(CompletableFuture.supplyAsync(() -> safeBatchInstall(bizRequest),
+ executorService));
+ }
Comment on lines
+115
to
+117
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Safeguard exceptions in async tasks |
||
| } | ||
|
|
||
| // wait for all install futures done | ||
| CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(); | ||
| int counter = 0; | ||
| for (CompletableFuture<ClientResponse> future : futures) { | ||
| ClientResponse clientResponse = future.get(); | ||
| String bizUrl = bizUrlsInSameOrder.get(counter); | ||
| bizUrlToInstallResult.put(bizUrl, clientResponse); | ||
| InstallRequest bizRequest = bizRequestInSameOrder.get(counter); | ||
| bizUrlToInstallResult.put(bizRequest.getBizUrl(), clientResponse); | ||
| hasFailed = hasFailed || clientResponse.getCode() != ResponseCode.SUCCESS; | ||
| counter++; | ||
| } | ||
|
|
@@ -139,6 +139,13 @@ public BatchInstallResponse batchInstall(BatchInstallRequest request) throws Thr | |
| .bizUrlToResponse(bizUrlToInstallResult).build(); | ||
| } | ||
|
|
||
| private BatchInstallStrategy getBatchInstallStrategy(BatchInstallRequest request) { | ||
| if (StringUtil.isNotEmpty(request.getBizDirAbsolutePath())) { | ||
| return batchInstallBizInDirAbsolutePathStrategy; | ||
| } | ||
| return batchInstallBizInRequestStrategy; | ||
| } | ||
|
|
||
| /** {@inheritDoc} */ | ||
| @Override | ||
| public List<Biz> queryBizList() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect description for BATCH_INSTALL_BIZ
The description "install one ark biz" is incorrect for a batch operation. It should reflect that this command handles multiple installations.
📝 Committable suggestion