当前位置: 首页>>代码示例>>Java>>正文


Java Signal类代码示例

本文整理汇总了Java中org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal的典型用法代码示例。如果您正苦于以下问题:Java Signal类的具体用法?Java Signal怎么用?Java Signal使用的例子?那么恭喜您, 这里精选的类代码示例或许可以为您提供帮助。


Signal类属于org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor包,在下文中一共展示了Signal类的12个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的Java代码示例。

示例1: translateCommandToSignal

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
@VisibleForTesting
public static Signal translateCommandToSignal(
    SignalContainerCommand command) {
  Signal signal = Signal.NULL;
  switch (command) {
    case OUTPUT_THREAD_DUMP:
      // TODO for windows support.
      signal = Shell.WINDOWS ? Signal.NULL: Signal.QUIT;
      break;
    case GRACEFUL_SHUTDOWN:
      signal = Signal.TERM;
      break;
    case FORCEFUL_SHUTDOWN:
      signal = Signal.KILL;
      break;
  }
  return signal;
}
 
开发者ID:aliyun-beta,项目名称:aliyun-oss-hadoop-fs,代码行数:19,代码来源:ContainerLaunch.java

示例2: testContainerKill

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
@Test
public void testContainerKill() throws Exception {
  Assume.assumeTrue(shouldRun());

  final ContainerId sleepId = getNextContainerId();
  Thread t = new Thread() {
    public void run() {
      try {
        runAndBlock(sleepId, "sleep", "100");
      } catch (IOException e) {
        LOG.warn("Caught exception while running sleep", e);
      }
    };
  };
  t.setDaemon(true); // If it does not exit we shouldn't block the test.
  t.start();

  assertTrue(t.isAlive());

  String pid = null;
  int count = 10;
  while ((pid = exec.getProcessId(sleepId)) == null && count > 0) {
    LOG.info("Sleeping for 200 ms before checking for pid ");
    Thread.sleep(200);
    count--;
  }
  assertNotNull(pid);

  LOG.info("Going to killing the process.");
  exec.signalContainer(new ContainerSignalContext.Builder()
      .setUser(appSubmitter)
      .setPid(pid)
      .setSignal(Signal.TERM)
      .build());
  LOG.info("sleeping for 100ms to let the sleep be killed");
  Thread.sleep(100);
  
  assertFalse(t.isAlive());
   cleanupAppFiles(appSubmitter);
 }
 
开发者ID:naver,项目名称:hadoop,代码行数:41,代码来源:TestLinuxContainerExecutor.java

示例3: testContainerKill

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
@Test
public void testContainerKill() throws Exception {
  Assume.assumeTrue(shouldRun());

  final ContainerId sleepId = getNextContainerId();
  Thread t = new Thread() {
    public void run() {
      try {
        runAndBlock(sleepId, "sleep", "100");
      } catch (IOException e) {
        LOG.warn("Caught exception while running sleep", e);
      }
    };
  };
  t.setDaemon(true); // If it does not exit we shouldn't block the test.
  t.start();

  assertTrue(t.isAlive());

  String pid = null;
  int count = 10;
  while ((pid = exec.getProcessId(sleepId)) == null && count > 0) {
    LOG.info("Sleeping for 200 ms before checking for pid ");
    Thread.sleep(200);
    count--;
  }
  assertNotNull(pid);

  LOG.info("Going to killing the process.");
  exec.signalContainer(new ContainerSignalContext.Builder()
      .setUser(appSubmitter)
      .setPid(pid)
      .setSignal(Signal.TERM)
      .build());
  LOG.info("sleeping for 100ms to let the sleep be killed");
  Thread.sleep(100);

  assertFalse(t.isAlive());
  cleanupAppFiles(appSubmitter);
}
 
开发者ID:aliyun-beta,项目名称:aliyun-oss-hadoop-fs,代码行数:41,代码来源:TestLinuxContainerExecutor.java

示例4: testContainerKill

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
@Test
public void testContainerKill() throws Exception {
  if (!shouldRun()) {
    return;
  }
  
  final ContainerId sleepId = getNextContainerId();   
  Thread t = new Thread() {
    public void run() {
      try {
        runAndBlock(sleepId, "sleep", "100");
      } catch (IOException e) {
        LOG.warn("Caught exception while running sleep",e);
      }
    };
  };
  t.setDaemon(true); //If it does not exit we shouldn't block the test.
  t.start();

  assertTrue(t.isAlive());
 
  String pid = null;
  int count = 10;
  while ((pid = exec.getProcessId(sleepId)) == null && count > 0) {
    LOG.info("Sleeping for 200 ms before checking for pid ");
    Thread.sleep(200);
    count--;
  }
  assertNotNull(pid);

  LOG.info("Going to killing the process.");
  exec.signalContainer(appSubmitter, pid, Signal.TERM);
  LOG.info("sleeping for 100ms to let the sleep be killed");
  Thread.sleep(100);
  
  assertFalse(t.isAlive());
}
 
开发者ID:yncxcw,项目名称:big-c,代码行数:38,代码来源:TestLinuxContainerExecutor.java

示例5: setSignal

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
public Builder setSignal(Signal signal) {
  this.signal = signal;
  return this;
}
 
开发者ID:naver,项目名称:hadoop,代码行数:5,代码来源:ContainerSignalContext.java

示例6: getSignal

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
public Signal getSignal() {
  return this.signal;
}
 
开发者ID:naver,项目名称:hadoop,代码行数:4,代码来源:ContainerSignalContext.java

示例7: signalContainer

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
/**
 * Send a signal to the container.
 *
 *
 * @throws IOException
 */
@SuppressWarnings("unchecked") // dispatcher not typed
public void signalContainer(SignalContainerCommand command)
    throws IOException {
  ContainerId containerId =
      container.getContainerTokenIdentifier().getContainerID();
  String containerIdStr = ConverterUtils.toString(containerId);
  String user = container.getUser();
  Signal signal = translateCommandToSignal(command);
  if (signal.equals(Signal.NULL)) {
    LOG.info("ignore signal command " + command);
    return;
  }

  LOG.info("Sending signal " + command + " to container " + containerIdStr);

  boolean alreadyLaunched = !shouldLaunchContainer.compareAndSet(false, true);
  if (!alreadyLaunched) {
    LOG.info("Container " + containerIdStr + " not launched."
        + " Not sending the signal");
    return;
  }

  if (LOG.isDebugEnabled()) {
    LOG.debug("Getting pid for container " + containerIdStr
        + " to send signal to from pid file "
        + (pidFilePath != null ? pidFilePath.toString() : "null"));
  }

  try {
    // get process id from pid file if available
    // else if shell is still active, get it from the shell
    String processId = null;
    if (pidFilePath != null) {
      processId = getContainerPid(pidFilePath);
    }

    if (processId != null) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Sending signal to pid " + processId
            + " as user " + user
            + " for container " + containerIdStr);
      }

      boolean result = exec.signalContainer(
          new ContainerSignalContext.Builder()
              .setContainer(container)
              .setUser(user)
              .setPid(processId)
              .setSignal(signal)
              .build());

      String diagnostics = "Sent signal " + command
          + " (" + signal + ") to pid " + processId
          + " as user " + user
          + " for container " + containerIdStr
          + ", result=" + (result ? "success" : "failed");
      LOG.info(diagnostics);

      dispatcher.getEventHandler().handle(
          new ContainerDiagnosticsUpdateEvent(containerId, diagnostics));
    }
  } catch (Exception e) {
    String message =
        "Exception when sending signal to container " + containerIdStr
            + ": " + StringUtils.stringifyException(e);
    LOG.warn(message);
  }
}
 
开发者ID:aliyun-beta,项目名称:aliyun-oss-hadoop-fs,代码行数:75,代码来源:ContainerLaunch.java

示例8: testContainerLaunchAndSignal

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
private void testContainerLaunchAndSignal(SignalContainerCommand command)
    throws IOException, InterruptedException, YarnException {

  Signal signal = ContainerLaunch.translateCommandToSignal(command);
  containerManager.start();

  File scriptFile = new File(tmpDir, "scriptFile.sh");
  PrintWriter fileWriter = new PrintWriter(scriptFile);
  File processStartFile =
      new File(tmpDir, "start_file.txt").getAbsoluteFile();
  fileWriter.write("\numask 0"); // So that start file is readable by the test
  fileWriter.write("\necho Hello World! > " + processStartFile);
  fileWriter.write("\necho $$ >> " + processStartFile);
  fileWriter.write("\nexec sleep 1000s");
  fileWriter.close();

  ContainerLaunchContext containerLaunchContext =
      recordFactory.newRecordInstance(ContainerLaunchContext.class);

  // ////// Construct the Container-id
  ContainerId cId = createContainerId(0);

  URL resource_alpha =
      ConverterUtils.getYarnUrlFromPath(localFS
          .makeQualified(new Path(scriptFile.getAbsolutePath())));
  LocalResource rsrc_alpha =
      recordFactory.newRecordInstance(LocalResource.class);
  rsrc_alpha.setResource(resource_alpha);
  rsrc_alpha.setSize(-1);
  rsrc_alpha.setVisibility(LocalResourceVisibility.APPLICATION);
  rsrc_alpha.setType(LocalResourceType.FILE);
  rsrc_alpha.setTimestamp(scriptFile.lastModified());
  String destinationFile = "dest_file";
  Map<String, LocalResource> localResources =
      new HashMap<String, LocalResource>();
  localResources.put(destinationFile, rsrc_alpha);
  containerLaunchContext.setLocalResources(localResources);
  List<String> commands = new ArrayList<String>();
  commands.add("/bin/bash");
  commands.add(scriptFile.getAbsolutePath());
  containerLaunchContext.setCommands(commands);
  StartContainerRequest scRequest =
      StartContainerRequest.newInstance(
          containerLaunchContext,
          createContainerToken(cId, DUMMY_RM_IDENTIFIER, context.getNodeId(),
          user, context.getContainerTokenSecretManager()));
  List<StartContainerRequest> list = new ArrayList<StartContainerRequest>();
  list.add(scRequest);
  StartContainersRequest allRequests =
      StartContainersRequest.newInstance(list);
  containerManager.startContainers(allRequests);

  int timeoutSecs = 0;
  while (!processStartFile.exists() && timeoutSecs++ < 20) {
    Thread.sleep(1000);
    LOG.info("Waiting for process start-file to be created");
  }
  Assert.assertTrue("ProcessStartFile doesn't exist!",
      processStartFile.exists());

  // Simulate NodeStatusUpdaterImpl sending CMgrSignalContainersEvent
  SignalContainerRequest signalReq =
      SignalContainerRequest.newInstance(cId, command);
  List<SignalContainerRequest> reqs = new ArrayList<SignalContainerRequest>();
  reqs.add(signalReq);
  containerManager.handle(new CMgrSignalContainersEvent(reqs));

  final ArgumentCaptor<ContainerSignalContext> signalContextCaptor =
      ArgumentCaptor.forClass(ContainerSignalContext.class);
  if (signal.equals(Signal.NULL)) {
    verify(exec, never()).signalContainer(signalContextCaptor.capture());
  } else {
    verify(exec, timeout(10000).atLeastOnce()).signalContainer(signalContextCaptor.capture());
    ContainerSignalContext signalContext = signalContextCaptor.getAllValues().get(0);
    Assert.assertEquals(cId, signalContext.getContainer().getContainerId());
    Assert.assertEquals(signal, signalContext.getSignal());
  }
}
 
开发者ID:aliyun-beta,项目名称:aliyun-oss-hadoop-fs,代码行数:79,代码来源:TestContainerManager.java

示例9: cleanupContainer

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
/**
 * Cleanup the container.
 * Cancels the launch if launch has not started yet or signals
 * the executor to not execute the process if not already done so.
 * Also, sends a SIGTERM followed by a SIGKILL to the process if
 * the process id is available.
 * @throws IOException
 */
@SuppressWarnings("unchecked") // dispatcher not typed
public void cleanupContainer() throws IOException {
  ContainerId containerId = container.getContainerId();
  String containerIdStr = ConverterUtils.toString(containerId);
  LOG.info("Cleaning up container " + containerIdStr);

  // launch flag will be set to true if process already launched
  boolean alreadyLaunched = !shouldLaunchContainer.compareAndSet(false, true);
  if (!alreadyLaunched) {
    LOG.info("Container " + containerIdStr + " not launched."
        + " No cleanup needed to be done");
    return;
  }

  LOG.debug("Marking container " + containerIdStr + " as inactive");
  // this should ensure that if the container process has not launched 
  // by this time, it will never be launched
  exec.deactivateContainer(containerId);

  if (LOG.isDebugEnabled()) {
    LOG.debug("Getting pid for container " + containerIdStr + " to kill"
        + " from pid file " 
        + (pidFilePath != null ? pidFilePath.toString() : "null"));
  }
  
  // however the container process may have already started
  try {

    // get process id from pid file if available
    // else if shell is still active, get it from the shell
    String processId = null;
    if (pidFilePath != null) {
      processId = getContainerPid(pidFilePath);
    }

    // kill process
    if (processId != null) {
      String user = container.getUser();
      LOG.debug("Sending signal to pid " + processId
          + " as user " + user
          + " for container " + containerIdStr);
      if (sleepDelayBeforeSigKill > 0) {
        boolean result = exec.signalContainer(user,
            processId, Signal.TERM);
        LOG.debug("Sent signal to pid " + processId
            + " as user " + user
            + " for container " + containerIdStr
            + ", result=" + (result? "success" : "failed"));
        new DelayedProcessKiller(container, user,
            processId, sleepDelayBeforeSigKill, Signal.KILL, exec).start();
      }
    }
  } catch (Exception e) {
    String message =
        "Exception when trying to cleanup container " + containerIdStr
            + ": " + StringUtils.stringifyException(e);
    LOG.warn(message);
    dispatcher.getEventHandler().handle(
      new ContainerDiagnosticsUpdateEvent(containerId, message));
  } finally {
    // cleanup pid file if present
    if (pidFilePath != null) {
      FileContext lfs = FileContext.getLocalFSFileContext();
      lfs.delete(pidFilePath, false);
    }
  }
}
 
开发者ID:ict-carch,项目名称:hadoop-plus,代码行数:76,代码来源:ContainerLaunch.java

示例10: signalContainer

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
/**
 * Send a signal to the container.
 *
 *
 * @throws IOException
 */
@SuppressWarnings("unchecked") // dispatcher not typed
public void signalContainer(SignalContainerCommand command)
    throws IOException {
  ContainerId containerId =
      container.getContainerTokenIdentifier().getContainerID();
  String containerIdStr = containerId.toString();
  String user = container.getUser();
  Signal signal = translateCommandToSignal(command);
  if (signal.equals(Signal.NULL)) {
    LOG.info("ignore signal command " + command);
    return;
  }

  LOG.info("Sending signal " + command + " to container " + containerIdStr);

  boolean alreadyLaunched = !shouldLaunchContainer.compareAndSet(false, true);
  if (!alreadyLaunched) {
    LOG.info("Container " + containerIdStr + " not launched."
        + " Not sending the signal");
    return;
  }

  if (LOG.isDebugEnabled()) {
    LOG.debug("Getting pid for container " + containerIdStr
        + " to send signal to from pid file "
        + (pidFilePath != null ? pidFilePath.toString() : "null"));
  }

  try {
    // get process id from pid file if available
    // else if shell is still active, get it from the shell
    String processId = null;
    if (pidFilePath != null) {
      processId = getContainerPid(pidFilePath);
    }

    if (processId != null) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Sending signal to pid " + processId
            + " as user " + user
            + " for container " + containerIdStr);
      }

      boolean result = exec.signalContainer(
          new ContainerSignalContext.Builder()
              .setContainer(container)
              .setUser(user)
              .setPid(processId)
              .setSignal(signal)
              .build());

      String diagnostics = "Sent signal " + command
          + " (" + signal + ") to pid " + processId
          + " as user " + user
          + " for container " + containerIdStr
          + ", result=" + (result ? "success" : "failed");
      LOG.info(diagnostics);

      dispatcher.getEventHandler().handle(
          new ContainerDiagnosticsUpdateEvent(containerId, diagnostics));
    }
  } catch (Exception e) {
    String message =
        "Exception when sending signal to container " + containerIdStr
            + ": " + StringUtils.stringifyException(e);
    LOG.warn(message);
  }
}
 
开发者ID:hopshadoop,项目名称:hops,代码行数:75,代码来源:ContainerLaunch.java

示例11: testContainerLaunchAndSignal

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
private void testContainerLaunchAndSignal(SignalContainerCommand command)
    throws IOException, InterruptedException, YarnException {

  Signal signal = ContainerLaunch.translateCommandToSignal(command);
  containerManager.start();

  File scriptFile = new File(tmpDir, "scriptFile.sh");
  PrintWriter fileWriter = new PrintWriter(scriptFile);
  File processStartFile =
      new File(tmpDir, "start_file.txt").getAbsoluteFile();
  fileWriter.write("\numask 0"); // So that start file is readable by the test
  fileWriter.write("\necho Hello World! > " + processStartFile);
  fileWriter.write("\necho $$ >> " + processStartFile);
  fileWriter.write("\nexec sleep 1000s");
  fileWriter.close();

  ContainerLaunchContext containerLaunchContext =
      recordFactory.newRecordInstance(ContainerLaunchContext.class);

  // ////// Construct the Container-id
  ContainerId cId = createContainerId(0);

  URL resource_alpha =
      URL.fromPath(localFS
          .makeQualified(new Path(scriptFile.getAbsolutePath())));
  LocalResource rsrc_alpha =
      recordFactory.newRecordInstance(LocalResource.class);
  rsrc_alpha.setResource(resource_alpha);
  rsrc_alpha.setSize(-1);
  rsrc_alpha.setVisibility(LocalResourceVisibility.APPLICATION);
  rsrc_alpha.setType(LocalResourceType.FILE);
  rsrc_alpha.setTimestamp(scriptFile.lastModified());
  String destinationFile = "dest_file";
  Map<String, LocalResource> localResources =
      new HashMap<String, LocalResource>();
  localResources.put(destinationFile, rsrc_alpha);
  containerLaunchContext.setLocalResources(localResources);
  List<String> commands = new ArrayList<String>();
  commands.add("/bin/bash");
  commands.add(scriptFile.getAbsolutePath());
  containerLaunchContext.setCommands(commands);
  StartContainerRequest scRequest =
      StartContainerRequest.newInstance(
          containerLaunchContext,
          createContainerToken(cId, DUMMY_RM_IDENTIFIER, context.getNodeId(),
          user, context.getContainerTokenSecretManager(), userFolder));
  List<StartContainerRequest> list = new ArrayList<StartContainerRequest>();
  list.add(scRequest);
  StartContainersRequest allRequests =
      StartContainersRequest.newInstance(list);
  containerManager.startContainers(allRequests);

  int timeoutSecs = 0;
  while (!processStartFile.exists() && timeoutSecs++ < 20) {
    Thread.sleep(1000);
    LOG.info("Waiting for process start-file to be created");
  }
  Assert.assertTrue("ProcessStartFile doesn't exist!",
      processStartFile.exists());

  // Simulate NodeStatusUpdaterImpl sending CMgrSignalContainersEvent
  SignalContainerRequest signalReq =
      SignalContainerRequest.newInstance(cId, command);
  List<SignalContainerRequest> reqs = new ArrayList<SignalContainerRequest>();
  reqs.add(signalReq);
  containerManager.handle(new CMgrSignalContainersEvent(reqs));

  final ArgumentCaptor<ContainerSignalContext> signalContextCaptor =
      ArgumentCaptor.forClass(ContainerSignalContext.class);
  if (signal.equals(Signal.NULL)) {
    verify(exec, never()).signalContainer(signalContextCaptor.capture());
  } else {
    verify(exec, timeout(10000).atLeastOnce()).signalContainer(signalContextCaptor.capture());
    ContainerSignalContext signalContext = signalContextCaptor.getAllValues().get(0);
    Assert.assertEquals(cId, signalContext.getContainer().getContainerId());
    Assert.assertEquals(signal, signalContext.getSignal());
  }
}
 
开发者ID:hopshadoop,项目名称:hops,代码行数:79,代码来源:TestContainerManager.java

示例12: cleanupContainer

import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; //导入依赖的package包/类
/**
 * Cleanup the container.
 * Cancels the launch if launch has not started yet or signals
 * the executor to not execute the process if not already done so.
 * Also, sends a SIGTERM followed by a SIGKILL to the process if
 * the process id is available.
 * @throws IOException
 */
@SuppressWarnings("unchecked") // dispatcher not typed
public void cleanupContainer() throws IOException {
  ContainerId containerId = container.getContainerId();
  String containerIdStr = ConverterUtils.toString(containerId);
  LOG.info("Cleaning up container " + containerIdStr);

  // launch flag will be set to true if process already launched
  boolean alreadyLaunched = !shouldLaunchContainer.compareAndSet(false, true);
  if (!alreadyLaunched) {
    LOG.info("Container " + containerIdStr + " not launched."
        + " No cleanup needed to be done");
    return;
  }

  LOG.debug("Marking container " + containerIdStr + " as inactive");
  // this should ensure that if the container process has not launched 
  // by this time, it will never be launched
  exec.deactivateContainer(containerId);

  if (LOG.isDebugEnabled()) {
    LOG.debug("Getting pid for container " + containerIdStr + " to kill"
        + " from pid file " 
        + (pidFilePath != null ? pidFilePath.toString() : "null"));
  }
  
  // however the container process may have already started
  try {

    // get process id from pid file if available
    // else if shell is still active, get it from the shell
    String processId = null;
    if (pidFilePath != null) {
      processId = getContainerPid(pidFilePath);
    }

    // kill process
    if (processId != null) {
      String user = container.getUser();
      LOG.debug("Sending signal to pid " + processId
          + " as user " + user
          + " for container " + containerIdStr);

      final Signal signal = sleepDelayBeforeSigKill > 0
        ? Signal.TERM
        : Signal.KILL;

      boolean result = exec.signalContainer(user, processId, signal);

      LOG.debug("Sent signal " + signal + " to pid " + processId
        + " as user " + user
        + " for container " + containerIdStr
        + ", result=" + (result? "success" : "failed"));

      if (sleepDelayBeforeSigKill > 0) {
        new DelayedProcessKiller(container, user,
            processId, sleepDelayBeforeSigKill, Signal.KILL, exec).start();
      }
    }
  } catch (Exception e) {
    String message =
        "Exception when trying to cleanup container " + containerIdStr
            + ": " + StringUtils.stringifyException(e);
    LOG.warn(message);
    dispatcher.getEventHandler().handle(
      new ContainerDiagnosticsUpdateEvent(containerId, message));
  } finally {
    // cleanup pid file if present
    if (pidFilePath != null) {
      FileContext lfs = FileContext.getLocalFSFileContext();
      lfs.delete(pidFilePath, false);
    }
  }
}
 
开发者ID:Seagate,项目名称:hadoop-on-lustre2,代码行数:82,代码来源:ContainerLaunch.java


注:本文中的org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal类示例由纯净天空整理自Github/MSDocs等开源代码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。