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

com.alibaba.jvm.sandbox.module.debug.DebugRalphModule Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
package com.alibaba.jvm.sandbox.module.debug;

import com.alibaba.jvm.sandbox.api.Information;
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.ProcessController;
import com.alibaba.jvm.sandbox.api.event.BeforeEvent;
import com.alibaba.jvm.sandbox.api.event.Event;
import com.alibaba.jvm.sandbox.api.event.InvokeEvent;
import com.alibaba.jvm.sandbox.api.http.Http;
import com.alibaba.jvm.sandbox.api.http.printer.ConcurrentLinkedQueuePrinter;
import com.alibaba.jvm.sandbox.api.http.printer.Printer;
import com.alibaba.jvm.sandbox.api.listener.EventListener;
import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder;
import com.alibaba.jvm.sandbox.api.listener.ext.EventWatcher;
import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.commons.lang3.EnumUtils;
import org.kohsuke.MetaInfServices;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import static com.alibaba.jvm.sandbox.api.event.Event.Type.*;

/**
 * 故障模拟
 * 

* 模块名字取自我喜欢的一部动画《Wreck-It Ralph》(无敌破坏王), * 能对任意方法模拟指定的故障类型: *

    *
  • 方法延时:delay
  • *
  • 方法异常:exception
  • *
  • 速率控制:r-limit
  • *
  • 并发控制:c-limit
  • *
*/ @MetaInfServices(Module.class) @Information(id = "debug-ralph", version = "0.0.1", author = "[email protected]") public class DebugRalphModule extends HttpSupported implements Module { @Resource private ModuleEventWatcher moduleEventWatcher; /* * 并发控制 * -d 'debug-ralph/c-limit?class=&method=&c=' */ @Http("/c-limit") public void concurrentLimit(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { final Printer printer = new ConcurrentLinkedQueuePrinter(resp.getWriter()); try { // --- 解析参数 --- final String cnPattern = getParameter(req, "class"); final String mnPattern = getParameter(req, "method"); final int concurrent = getParameter(req, "c", int.class); final EventWatcher watcher = new EventWatchBuilder(moduleEventWatcher) .onClass(cnPattern) .includeSubClasses() .includeBootstrap() .onBehavior(mnPattern) .onWatching() .withProgress(new ProgressPrinter(printer)) .onWatch(new EventListener() { // 设定一个本次拦截共享的并发限制器,所有被匹配上的类的入口 // 将会共同被同一个并发限制! final Semaphore sph = new Semaphore(concurrent); // 是否一次拦截调用链的入口 private boolean isProcessTop(InvokeEvent event) { return event.processId == event.invokeId; } @Override public void onEvent(Event event) throws Throwable { final InvokeEvent iEvent = (InvokeEvent) event; // 不是顶层调用,说明之前已经通过并发控制的闸门,可以不受到并发的制约 if (!isProcessTop(iEvent)) { return; } switch (event.type) { case BEFORE: { final BeforeEvent bEvent = (BeforeEvent) event; // 如果是顶层的调用,必须通过流控获取继续调用的门票 // 没有拿到门票的就让他快速失败掉 if (!sph.tryAcquire()) { printer.println(String.format( "%s.%s will be limit by concurrent: %s on %s", bEvent.javaClassName, bEvent.javaMethodName, concurrent, Thread.currentThread().getName() )); ProcessController.throwsImmediately(new RuntimeException("concurrent-limit by Ralph!!!")); } break; } case RETURN: case THROWS: { sph.release(); break; } default: // ignore... }//switch }//onEvent }, BEFORE, RETURN, THROWS); // --- 等待结束 --- try { printer.println(String.format( "concurrent-limit on [%s#%s] concurrent:%s.\nPress CTRL_C abort it!", cnPattern, mnPattern, concurrent )); printer.waitingForBroken(); } finally { watcher.onUnWatched(); } } catch (HttpErrorCodeException hece) { resp.sendError(hece.getCode(), hece.getMessage()); return; } } /* * 速率控制 * -d 'debug-ralph/r-limit?class=&method=&c=' */ @Http("/r-limit") public void rateLimit(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { final Printer printer = new ConcurrentLinkedQueuePrinter(resp.getWriter()); try { // --- 解析参数 --- final String cnPattern = getParameter(req, "class"); final String mnPattern = getParameter(req, "method"); final double rate = getParameter(req, "r", double.class); final EventWatcher watcher = new EventWatchBuilder(moduleEventWatcher) .onClass(cnPattern) .includeSubClasses() .includeBootstrap() .onBehavior(mnPattern) .onWatching() .withProgress(new ProgressPrinter(printer)) .onWatch(new EventListener() { // 设定一个本次拦截共享的速率限制器,所有被匹配上的类的入口 // 将会共同被同一个速率限速! final RateLimiter limiter = RateLimiter.create(rate); // 是否一次拦截调用链的入口 private boolean isProcessTop(InvokeEvent event) { return event.processId == event.invokeId; } @Override public void onEvent(Event event) throws Throwable { final BeforeEvent bEvent = (BeforeEvent) event; // 不是顶层调用,说明之前已经通过流控的闸门,可以不受到流控的制约 if (!isProcessTop(bEvent)) { return; } // 如果是顶层的调用,必须通过流控获取继续调用的门票 // 没有拿到门票的就让他快速失败掉 if (!limiter.tryAcquire()) { printer.println(String.format( "%s.%s will be limit by rate: %s on %s", bEvent.javaClassName, bEvent.javaMethodName, rate, Thread.currentThread().getName() )); ProcessController.throwsImmediately(new RuntimeException("rate-limit by Ralph!!!")); } } }, BEFORE); // --- 等待结束 --- try { printer.println(String.format( "rate-limit on [%s#%s] rate:%.2f(TPS).\nPress CTRL_C abort it!", cnPattern, mnPattern, rate )); printer.waitingForBroken(); } finally { watcher.onUnWatched(); } } catch (HttpErrorCodeException hece) { resp.sendError(hece.getCode(), hece.getMessage()); return; } } /** * 异常工厂 */ interface ExceptionFactory { Exception newInstance(String message); } /** * 异常类型 */ enum ExceptionType { IOException(new ExceptionFactory() { @Override public Exception newInstance(String message) { return new IOException(message); } }), NullPointException(new ExceptionFactory() { @Override public Exception newInstance(String message) { return new NullPointerException(message); } }), RuntimeException(new ExceptionFactory() { @Override public Exception newInstance(String message) { return new RuntimeException(message); } }), TimeoutException(new ExceptionFactory() { @Override public Exception newInstance(String message) { return new TimeoutException(message); } }); private final ExceptionFactory factory; ExceptionType(final ExceptionFactory factory) { this.factory = factory; } public Exception throwIt(final String message) throws Exception { return factory.newInstance(message); } } /* * 注入异常 * -d 'debug-ralph/wreck?class=&method=&type=' */ @Http("/wreck") public void exception(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { final Printer printer = new ConcurrentLinkedQueuePrinter(resp.getWriter()); try { // --- 解析参数 --- final String cnPattern = getParameter(req, "class"); final String mnPattern = getParameter(req, "method"); final ExceptionType exType = getParameter( req, "type", new Converter() { @Override public ExceptionType convert(String string) { return EnumUtils.getEnum(ExceptionType.class, string); } }, ExceptionType.RuntimeException ); // --- 开始增强 --- final EventWatcher watcher = new EventWatchBuilder(moduleEventWatcher) .onClass(cnPattern) .includeSubClasses() .includeBootstrap() .onBehavior(mnPattern) .onWatching() .withProgress(new ProgressPrinter(printer)) .onWatch(new EventListener() { @Override public void onEvent(Event event) throws Throwable { final BeforeEvent bEvent = (BeforeEvent) event; printer.println(String.format( "%s.%s will be wreck by exception: %s on %s", bEvent.javaClassName, bEvent.javaMethodName, exType.name(), Thread.currentThread().getName() )); ProcessController.throwsImmediately(exType.throwIt("wreck-it by Ralph!!!")); } }, BEFORE); // --- 等待结束 --- try { printer.println(String.format( "exception on [%s#%s] exception: %s.\nPress CTRL_C abort it!", cnPattern, mnPattern, exType.name() )); printer.waitingForBroken(); } finally { watcher.onUnWatched(); } } catch (HttpErrorCodeException hece) { resp.sendError(hece.getCode(), hece.getMessage()); return; } } /* * 注入延时 * -d 'debug-ralph/delay?class=&method=&delay=' */ @Http("/delay") public void delay(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { final ReentrantLock delayLock = new ReentrantLock(); final Condition delayCondition = delayLock.newCondition(); final Printer printer = new ConcurrentLinkedQueuePrinter(resp.getWriter()); final AtomicBoolean isFinishRef = new AtomicBoolean(false); try { // --- 解析参数 --- final String cnPattern = getParameter(req, "class"); final String mnPattern = getParameter(req, "method"); final long delayMs = getParameter(req, "delay", long.class); // --- 开始增强 --- final EventWatcher watcher = new EventWatchBuilder(moduleEventWatcher) .onClass(cnPattern) .includeSubClasses() .includeBootstrap() .onBehavior(mnPattern) .onWatching() .withProgress(new ProgressPrinter(printer)) .onWatch(new EventListener() { @Override public void onEvent(Event event) throws Throwable { final BeforeEvent bEvent = (BeforeEvent) event; printer.println(String.format( "%s.%s will be delay %s(ms) on %s", bEvent.javaClassName, bEvent.javaMethodName, delayMs, Thread.currentThread().getName() )); delayLock.lock(); try { // 如果已经结束,则放弃本次请求 if (isFinishRef.get()) { return; } delayCondition.await(delayMs, TimeUnit.MILLISECONDS); } finally { delayLock.unlock(); } } }, BEFORE); // --- 等待结束 --- try { printer.println(String.format( "delay on [%s#%s] %s(ms).\nPress CTRL_C abort it!", cnPattern, mnPattern, delayMs )); printer.waitingForBroken(); } finally { // 释放锁 delayLock.lock(); try { isFinishRef.set(true); delayCondition.signalAll(); } finally { delayLock.unlock(); } watcher.onUnWatched(); } } catch (HttpErrorCodeException hece) { resp.sendError(hece.getCode(), hece.getMessage()); return; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy