sentinel-tansport-SPI-CommandSPI
说明
我们引入以下
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>1.8.6</version> </dependency>
通过初始化com.alibaba.csp.sentinel.init.InitExecutor#doInit 之后就可以通过sentinel暴露的端口调用相关api
如果使用了alibaba-starter-sentinel则不需要手动调用因为com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration#init在这里面执行了自动调用
他是如何实现呢
<1>
com.alibaba.csp.sentinel.init.InitExecutor#doInit
public static void doInit() { //原子性set和判断是否已经初始化 if (!initialized.compareAndSet(false, true)) { return; } try { //SPI获取InitFunc ServiceLoader<InitFunc> loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class); List<OrderWrapper> initList = new ArrayList<OrderWrapper>(); for (InitFunc initFunc : loader) { RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName()); insertSorted(initList, initFunc); } for (OrderWrapper w : initList) { //<2>执行初始化 针对commnad是com.alibaba.csp.sentinel.transport.init.CommandCenterInitFunc#init w.func.init(); RecordLog.info(String.format("[InitExecutor] Executing %s with order %d", w.func.getClass().getCanonicalName(), w.order)); } } catch (Exception ex) { RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex); ex.printStackTrace(); } catch (Error error) { RecordLog.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error); error.printStackTrace(); } }
<2>
@InitOrder(-1) public class CommandCenterInitFunc implements InitFunc { @Override public void init() throws Exception { //这里也是SPI的方式获取CommandCenter 我们可以自定义,默认是 com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter CommandCenter commandCenter = CommandCenterProvider.getCommandCenter(); if (commandCenter == null) { RecordLog.warn("[CommandCenterInitFunc] Cannot resolve CommandCenter"); return; } //<3>执行command的初始化内部也是SPI的方式,我们可以动态新增 commandCenter.beforeStart();
//<6>开启http service commandCenter.start(); RecordLog.info("[CommandCenterInit] Starting command center: " + commandCenter.getClass().getCanonicalName()); } }
<3>
com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter#beforeStart
public void beforeStart() throws Exception { //<4>SPI的方式获取 Map<String, CommandHandler> handlers = CommandHandlerProvider.getInstance().namedHandlers(); //<5>注册command registerCommands(handlers); }
<4>
com.alibaba.csp.sentinel.command.CommandHandlerProvider#namedHandlers
public class CommandHandlerProvider implements Iterable<CommandHandler> { //SPI的方式获取CommandHandler private final ServiceLoader<CommandHandler> serviceLoader = ServiceLoaderUtil.getServiceLoader( CommandHandler.class); /** * Get all command handlers annotated with {@link CommandMapping} with command name. * * @return list of all named command handlers */ public Map<String, CommandHandler> namedHandlers() { Map<String, CommandHandler> map = new HashMap<String, CommandHandler>(); for (CommandHandler handler : serviceLoader) { String name = parseCommandName(handler); if (!StringUtil.isEmpty(name)) { map.put(name, handler); } } return map; } private String parseCommandName(CommandHandler handler) { //需要打了CommandMapping注解 name为url映射 CommandMapping commandMapping = handler.getClass().getAnnotation(CommandMapping.class); if (commandMapping != null) { return commandMapping.name(); } else { return null; } } ...... }
<5>
注册到static变量
com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter#registerCommands
private static final Map<String, CommandHandler> handlerMap = new ConcurrentHashMap(); public static void registerCommand(String commandName, CommandHandler handler) { if (!StringUtil.isEmpty(commandName)) { if (handlerMap.containsKey(commandName)) { CommandCenterLog.warn("Register failed (duplicate command): " + commandName, new Object[0]); } else { handlerMap.put(commandName, handler); } } }
<6>
com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter#start
public void start() throws Exception { //获取cpu核数 作为请求处理线程数 int nThreads = Runtime.getRuntime().availableProcessors(); //初始化http请求处理的线程池 this.bizExecutor = new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(10), new NamedThreadFactory("sentinel-command-center-service-executor"), new RejectedExecutionHandler() { public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { CommandCenterLog.info("EventTask rejected", new Object[0]); throw new RejectedExecutionException(); } }); //处理认为 Runnable serverInitTask = new Runnable() { int port; { try { //获取配置的端口信息 this.port = Integer.parseInt(TransportConfig.getPort()); } catch (Exception var3) { //出现异常8719 this.port = 8719; } } public void run() { boolean success = false; //初始化ServerSocket 内部会通过port进行监听,如果冲突则自动+1往上找 ServerSocket serverSocket = SimpleHttpCommandCenter.getServerSocketFromBasePort(this.port); if (serverSocket != null) { CommandCenterLog.info("[CommandCenter] Begin listening at port " + serverSocket.getLocalPort(), new Object[0]); SimpleHttpCommandCenter.this.socketReference = serverSocket; //<7>开启监听http线程处理器 监听器为ServerThread SimpleHttpCommandCenter.this.executor.submit(SimpleHttpCommandCenter.this.new ServerThread(serverSocket)); success = true; this.port = serverSocket.getLocalPort(); } else { CommandCenterLog.info("[CommandCenter] chooses port fail, http command center will not work", new Object[0]); } if (!success) { this.port = -1; } TransportConfig.setRuntimePort(this.port); SimpleHttpCommandCenter.this.executor.shutdown(); } }; (new Thread(serverInitTask)).start();
<7>
com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter.ServerThread#run
public void run() { while(true) { Socket socket = null; try { //监听请求 socket = this.serverSocket.accept(); SimpleHttpCommandCenter.this.setSocketSoTimeout(socket); HttpEventTask eventTask = new HttpEventTask(socket); //<8>监听到请求交给HttpEventTask 处理 SimpleHttpCommandCenter.this.bizExecutor.submit(eventTask); } catch (Exception var6) { CommandCenterLog.info("Server error", var6); if (socket != null) { try { socket.close(); } catch (Exception var5) { CommandCenterLog.info("Error when closing an opened socket", var5); } } try { Thread.sleep(10L); } catch (InterruptedException var4) { return; } } } }
com.alibaba.csp.sentinel.transport.command.http.HttpEventTask#run
public void run() { if (this.socket != null) { PrintWriter printWriter = null; InputStream inputStream = null; try { long start = System.currentTimeMillis(); inputStream = new BufferedInputStream(this.socket.getInputStream()); OutputStream outputStream = this.socket.getOutputStream(); printWriter = new PrintWriter(new OutputStreamWriter(outputStream, Charset.forName(SentinelConfig.charset()))); String firstLine = readLine(inputStream); CommandCenterLog.info("[SimpleHttpCommandCenter] Socket income: " + firstLine + ", addr: " + this.socket.getInetAddress(), new Object[0]); CommandRequest request = processQueryString(firstLine); if (firstLine.length() > 4 && StringUtil.equalsIgnoreCase("POST", firstLine.substring(0, 4))) { processPostRequest(inputStream, request); } String commandName = HttpCommandUtils.getTarget(request); if (StringUtil.isBlank(commandName)) { this.writeResponse(printWriter, StatusCode.BAD_REQUEST, "Invalid command"); return; } //核心代码 获取对应的Handler就是前面SPI初始化的 CommandHandler<?> commandHandler = SimpleHttpCommandCenter.getHandler(commandName); if (commandHandler != null) { CommandResponse<?> response = commandHandler.handle(request); this.handleResponse(response, printWriter); } else { this.writeResponse(printWriter, StatusCode.BAD_REQUEST, "Unknown command `" + commandName + '`'); } long cost = System.currentTimeMillis() - start; CommandCenterLog.info("[SimpleHttpCommandCenter] Deal a socket task: " + firstLine + ", address: " + this.socket.getInetAddress() + ", time cost: " + cost + " ms", new Object[0]); } catch (RequestException var18) { this.writeResponse(printWriter, var18.getStatusCode(), var18.getMessage()); } catch (Throwable var19) { Throwable e = var19; CommandCenterLog.warn("[SimpleHttpCommandCenter] CommandCenter error", var19); try { if (printWriter != null) { String errorMessage = "Command server error"; e.printStackTrace(); if (!this.writtenHead) { this.writeResponse(printWriter, StatusCode.INTERNAL_SERVER_ERROR, errorMessage); } else { printWriter.println(errorMessage); } printWriter.flush(); } } catch (Exception var17) { CommandCenterLog.warn("Failed to write error response", var17); } } finally { this.closeResource(inputStream); this.closeResource(printWriter); this.closeResource(this.socket); } } }