All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.sinri.keel.maids.gatling.KeelGatling Maven / Gradle / Ivy

Go to download

A website framework with VERT.X for ex-PHP-ers, exactly Ark Framework Users.

The newest version!
package io.github.sinri.keel.maids.gatling;

import io.github.sinri.keel.facade.async.KeelAsyncKit;
import io.github.sinri.keel.logger.event.KeelEventLogger;
import io.github.sinri.keel.verticles.KeelVerticleImplWithEventLogger;
import io.vertx.core.*;
import io.vertx.core.shareddata.Counter;

import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

import static io.github.sinri.keel.facade.KeelInstance.Keel;

/**
 * Gatling Gun with multi barrels, for parallel tasks in clustered vertx runtime.
 *
 * @since 2.9.1
 * @since 2.9.3 change to VERTICLE
 */
public class KeelGatling extends KeelVerticleImplWithEventLogger {
    private final Options options;
    private final AtomicInteger barrelUsed = new AtomicInteger(0);

    private KeelGatling(Options options) {
        this.options = options;
    }

    public static Future deploy(String gatlingName, Handler optionHandler) {
        Options options = new Options(gatlingName);
        optionHandler.handle(options);
        KeelGatling keelGatling = new KeelGatling(options);
        return Keel.getVertx().deployVerticle(keelGatling, new DeploymentOptions().setThreadingModel(ThreadingModel.WORKER));
    }

    protected Future rest() {
        int actualRestInterval = new Random().nextInt(
                Math.toIntExact(options.getAverageRestInterval() / 2)
        ) + options.getAverageRestInterval();
        return KeelAsyncKit.sleep(actualRestInterval);
    }

    @Override
    protected void startAsKeelVerticle(Promise startPromise) {
        barrelUsed.set(0);
        KeelAsyncKit.repeatedlyCall(routineResult -> {
                    return fireOnce();
                })
                .andThen(ar -> {
                    super.startAsKeelVerticle(startPromise);
                });
    }

    private Future fireOnce() {
        if (barrelUsed.get() >= options.getBarrels()) {
            getLogger().debug(r -> r.message("BARREL FULL"));
            return rest();
        }
        return Future.succeededFuture()
                .compose(v -> loadOneBullet())
                .compose(bullet -> {
                    if (bullet == null) {
                        return rest();
                    }

                    barrelUsed.incrementAndGet();

                    fireBullet(bullet, firedAR -> {
                        if (firedAR.failed()) {
                            getLogger().exception(firedAR.cause(), r -> r.message("BULLET FIRED ERROR"));
                        } else {
                            getLogger().info(r -> r.message("BULLET FIRED DONE"));
                        }
                        barrelUsed.decrementAndGet();
                    });

                    return KeelAsyncKit.sleep(10L);
                })
                .recover(throwable -> {
                    getLogger().exception(throwable, r -> r.message("FAILED TO LOAD BULLET"));
                    return rest();
                });
    }

    /**
     * Seek one bullet from anywhere with a certain rule.
     *
     * @return Future of a runnable bullet, or null.
     */
    private Future loadOneBullet() {
        return Keel.getVertx().sharedData()
                .getLock("KeelGatling-" + this.options.getGatlingName() + "-Load")
                .compose(lock -> this.options.getBulletLoader().get().andThen(ar -> lock.release()));
    }

    protected Future requireExclusiveLocksOfBullet(Bullet bullet) {
        if (bullet.exclusiveLockSet() != null && !bullet.exclusiveLockSet().isEmpty()) {
            AtomicBoolean blocked = new AtomicBoolean(false);
            return KeelAsyncKit.iterativelyCall(
                            bullet.exclusiveLockSet(),
                            exclusiveLock -> {
                                String exclusiveLockName = "KeelGatling-Bullet-Exclusive-Lock-" + exclusiveLock;
                                return Keel.getVertx().sharedData()
                                        .getCounter(exclusiveLockName)
                                        .compose(Counter::incrementAndGet)
                                        .compose(increased -> {
                                            if (increased > 1) {
                                                blocked.set(true);
                                            }
                                            return Future.succeededFuture();
                                        });
                            })
                    .compose(v -> {
                        if (blocked.get()) {
                            return releaseExclusiveLocksOfBullet(bullet)
                                    .eventually(() -> Future.failedFuture(new Exception("This bullet met Exclusive Lock Block.")));
                        }
                        return Future.succeededFuture();
                    });
        } else {
            return Future.succeededFuture();
        }
    }

    protected Future releaseExclusiveLocksOfBullet(Bullet bullet) {
        if (bullet.exclusiveLockSet() != null && !bullet.exclusiveLockSet().isEmpty()) {
            return KeelAsyncKit.iterativelyCall(bullet.exclusiveLockSet(), exclusiveLock -> {
                String exclusiveLockName = "KeelGatling-Bullet-Exclusive-Lock-" + exclusiveLock;
                return Keel.getVertx().sharedData().getCounter(exclusiveLockName)
                        .compose(counter -> counter.decrementAndGet()
                                .compose(x -> Future.succeededFuture()));
            });
        } else {
            return Future.succeededFuture();
        }
    }

    private void fireBullet(Bullet bullet, Handler> handler) {
        Promise promise = Promise.promise();
        Future.succeededFuture()
                .compose(v -> requireExclusiveLocksOfBullet(bullet)
                        .compose(locked -> bullet.fire()
                                .andThen(fired -> releaseExclusiveLocksOfBullet(bullet)))
                )
                .andThen(firedAR -> bullet.ejectShell(firedAR)
                        .onComplete(ejected -> {
                            if (firedAR.failed()) {
                                promise.fail(firedAR.cause());
                            } else {
                                promise.complete();
                            }
                        })
                );

        promise.future().andThen(handler);
    }

    @Override
    protected KeelEventLogger buildEventLogger() {
        return null;
    }

    public static class Options {
        private final String gatlingName;
        private int barrels;
        private int averageRestInterval;
        private Supplier> bulletLoader;
//        private KeelEventLogger logger;

        public Options(String gatlingName) {
            this.gatlingName = gatlingName;
            this.barrels = 1;
            this.averageRestInterval = 1000;
            this.bulletLoader = () -> Future.succeededFuture(null);
//            this.logger = KeelEventLogger.silentLogger();
        }

        /**
         * @return 加特林机枪名称(集群中各节点之间的识别同一组加特林机枪类的实例用)
         */
        public String getGatlingName() {
            return gatlingName;
        }

        /**
         * @return 枪管数量(并发任务数)
         */
        public int getBarrels() {
            return barrels;
        }

        /**
         * @param barrels 枪管数量(并发任务数)
         */
        public Options setBarrels(int barrels) {
            this.barrels = barrels;
            return this;
        }

        /**
         * @return 弹带更换平均等待时长(没有新任务时的休眠期,单位0.001秒)
         */
        public int getAverageRestInterval() {
            return averageRestInterval;
        }

        /**
         * @param averageRestInterval 弹带更换平均等待时长(没有新任务时的休眠期,单位0.001秒)
         */
        public Options setAverageRestInterval(int averageRestInterval) {
            this.averageRestInterval = averageRestInterval;
            return this;
        }

        /**
         * @return 供弹器(新任务生成器)
         */
        public Supplier> getBulletLoader() {
            return bulletLoader;
        }

        /**
         * @param bulletLoader 供弹器(新任务生成器)
         */
        public Options setBulletLoader(Supplier> bulletLoader) {
            this.bulletLoader = bulletLoader;
            return this;
        }

//        /**
//         * @return 日志记录仪
//         */
//        public KeelEventLogger getLogger() {
//            return logger;
//        }
//
//        /**
//         * @param logger 日志记录仪
//         */
//        public Options setLogger(KeelEventLogger logger) {
//            this.logger = logger;
//            return this;
//        }


    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy