前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jenkins源码分析 —— 执行主节点的远程请求(四)

jenkins源码分析 —— 执行主节点的远程请求(四)

作者头像
lovelife110
发布2021-01-14 10:50:35
4560
发布2021-01-14 10:50:35
举报
文章被收录于专栏:爱生活爱编程爱生活爱编程

本文解析jenkins从节点执行主节点的远程请求过程的源码

从上一篇文章“接受主节点的远程请求(三)”得知,执行UserRequst是通过创建Runnable并提交到Executor,而Executor负责执行构建

执行请求

具体执行请求的代码是UserRequest类的perform()方法

代码语言:javascript
复制
protected UserResponse<RSP,EXC> perform(Channel channel) throws EXC {
   try {
        ClassLoader cl = channel.importedClassLoaders.get(classLoaderProxy);

        RSP r = null;
        Channel oldc = Channel.setCurrent(channel);
       try {
            Object o;
           try {
                o = deserialize(channel,request,cl);
           } catch (ClassNotFoundException e) {
               throw new ClassNotFoundException("Failed to deserialize the Callable object. Perhaps you needed to implement DelegatingCallable?",e);
           } catch (RuntimeException e) {
               throw new Error("Failed to deserialize the Callable object.",e);
           }

            Callable<RSP,EXC> callable = (Callable<RSP,EXC>)o;
           if(!channel.isArbitraryCallableAllowed() && !(callable instanceof RPCRequest))
               throw new SecurityException("Execution of "+callable.toString()+" is prohibited because the channel is restricted");

            callable = channel.decorators.wrapUserRequest(callable);

            ClassLoader old = Thread.currentThread().getContextClassLoader();
            Thread.currentThread().setContextClassLoader(cl);
           // execute the service
           try {
                r = callable.call();
           } finally {
                Thread.currentThread().setContextClassLoader(old);
           }
       } finally {
            Channel.setCurrent(oldc);
       }

       return new UserResponse<RSP,EXC>(serialize(r,channel),false);
       ...
}

创建ClassLoader

通过UserRequest类的perform()方法里面的ClassLoader cl = channel.importedClassLoaders.get(classLoaderProxy)创建ClassLoader

反序列化Callable

通过UserRequest类的perform()方法里面Object o = deserialize(channel,request,cl)以及Callable<RSP,EXC> callable = (Callable<RSP,EXC>)o得到Callable对象,即“发送远程请求(二)”发送的RemoteLaunchCallable

deserialize()方法代码:

/*package*/ static Object deserialize(final Channel channel, byte[] data, ClassLoader defaultClassLoader) throws IOException, ClassNotFoundException { ByteArrayInputStream in = new ByteArrayInputStream(data); ObjectInputStream ois; if (channel.remoteCapability.supportsMultiClassLoaderRPC()) { // this code is coupled with the ObjectOutputStream subtype above ois = new MultiClassLoaderSerializer.Input(channel, in); } else { ois = new ObjectInputStreamEx(in, defaultClassLoader); } return ois.readObject(); }

MultiClassLoaderSerializer.Input继承ObjectInputStream,通过重写resolveClass,resolveProxyClass方法以及readClassLoader方法来获得ObjectInputStream对象

这么做的原因:反序列化时,如果在本地找不到这个对象的类的sourceCode,则序列化就会失败。但是可以通过覆盖ObjectOutputStream.annotateClass和ObjectInputStream.resolveClass来实现从主节点将类的sourceCode传到从节点,并运用ClassLoader来载入这个类。

class MultiClassLoaderSerializer { ... static final class Input extends ObjectInputStream { private final Channel channel; private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(); Input(Channel channel, InputStream in) throws IOException { super(in); this.channel = channel; } private ClassLoader readClassLoader() throws IOException, ClassNotFoundException { ... } @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { ... } @Override protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { ... } } ... }

执行callable.call

通过UserRequest类的perform()方法里面callable.call()调用jenkins-core项目的Launcher类的内部类RemoteLaunchCallable类的call方法

private static class RemoteLaunchCallable extends MasterToSlaveCallable<RemoteProcess,IOException> { ... public RemoteProcess call() throws IOException { Launcher.ProcStarter ps = new LocalLauncher(listener).launch(); ps.cmds(cmd).masks(masks).envs(env).stdin(in).stdout(out).stderr(err).quiet(quiet); if(workDir!=null) ps.pwd(workDir); if (reverseStdin) ps.writeStdin(); if (reverseStdout) ps.readStdout(); if (reverseStderr) ps.readStderr(); final Proc p = ps.start(); ... }

通过上面代码的Launcher.ProcStarter ps = new LocalLauncher(listener).launch()以及final Proc p = ps.start(),最终调用Launcher类的LocalLauncher内部类的launch方法

public static class LocalLauncher extends Launcher { ... @Override public Proc launch(ProcStarter ps) throws IOException { if (!ps.quiet) { maskedPrintCommandLine(ps.commands, ps.masks, ps.pwd); } EnvVars jobEnv = inherit(ps.envs); // replace variables in command line String[] jobCmd = new String[ps.commands.size()]; for ( int idx = 0 ; idx < jobCmd.length; idx++ ) jobCmd[idx] = jobEnv.expand(ps.commands.get(idx)); return new LocalProc(jobCmd, Util.mapToEnv(jobEnv), ps.reverseStdin ?LocalProc.SELFPUMP_INPUT:ps.stdin, ps.reverseStdout?LocalProc.SELFPUMP_OUTPUT:ps.stdout, ps.reverseStderr?LocalProc.SELFPUMP_OUTPUT:ps.stderr, toFile(ps.pwd)); } ... }

而LocalProc构造方法

public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out,OutputStream err,File workDir) throws IOException { this( calcName(cmd), stderr(environment(new ProcessBuilder(cmd),env).directory(workDir), err==null || err== SELFPUMP_OUTPUT), in, out, err ); }

里面的java.lang.ProcessBuilder才是jenkins job里面Execute Shell的最根本的代码。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-05-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 执行请求
  • 创建ClassLoader
  • 反序列化Callable
  • 执行callable.call
相关产品与服务
命令行工具
腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档