YARN

本文介绍了YARN(另一种资源协调者)的概念、架构及其在Hadoop中的作用。详细阐述了YARN的组成部分如ResourceManager、NodeManager和ApplicationMaster的功能,并提供了编写YARN应用程序的具体步骤。此外,还探讨了YARN相较于MapReduce v1的优势,包括更好的可扩展性、支持多种编程模型、简化框架升级及提高集群资源利用率。

1. 什么是YARN

Yet Another Resource Negotiator(另一种资源协调者),是一种新的Hadoop资源管理器,它是一个通用资源管理系统,可为上层应用提供统一的资源管理和调度

2. YARN架构

  1. ResurceManager(RM):一个纯粹的调度器,专门负责集群中可用资源的分配和管理。
  2. Container :分配给具体应用的资源抽象表现形式,包括内存、cpu、disk
  3. NodeManager(NM) :负责节点本地资源的管理,包括启动应用程序的Container,监控它们的资源使用情况,并报告给RM
  4. App Master (ApplicationMaster(AM)):特定框架库的一个实例,负责有RM协商资源,并和NM协调工作来执行和监控Container以及它们的资源消耗。AM也是以一个的Container身份运行。
  5. 客户端(Client):是集群中一个能向RM提交应用的实例,并且指定了执行应用所需要的AM类型

YARN架构

MR Client和Drill Client

3. 如何编写YARN应用程序

  1. Client

    • 初始化并启动一个YarnClient
    Configuration yarnConfig = new YarnConfiguration(getConf());
    YarnClient client = YarnClient.createYarnClient();
    client.init(yarnConfig);
    client.start();
    
    • 创建一个应用程序
    YarnClientApplication app = client.createApplication();
    GetNewApplicationResponse appResponse = app.getNewApplicationResponse();
    
    • 设置应用程序提交上下文
    // 1. 设置应用程序提交上下文基本信息
    ApplicationSubmissionContext appContext = app.getApplicationSubmissionContext();
    appContext.setApplicationId(appResponse.getApplicationId());
    appContext.setApplicationName(config.getProperty("app.name"));
    appContext.setApplicationType(config.getProperty("app.type"));
    appContext.setApplicationTags(new LinkedHashSet<>(Arrays.asList(config.getProperty("app.tags").split(","))));
    // queue:默认是default
    appContext.setQueue(config.getProperty("app.queue"));
    appContext.setPriority(Priority.newInstance(Integer.parseInt(config.getProperty("app.priority"))));
    appContext.setResource(Resource.newInstance(Integer.parseInt(config.getProperty("am.memory")),
        Integer.parseInt(config.getProperty("am.vCores"))));
    
    //2. 设置am container启动上下文 
    ContainerLaunchContext amContainer = Records.newRecord(ContainerLaunchContext.class);
    
    // 3. 设置am localResources
    Map<String, LocalResource> amLocalResources = new LinkedHashMap<>();
    LocalResource drillArchive = Records.newRecord(LocalResource.class);
    drillArchive.setResource(ConverterUtils.getYarnUrlFromPath(drillArchiveFileStatus.getPath()));
    drillArchive.setSize(drillArchiveFileStatus.getLen());
    drillArchive.setTimestamp(drillArchiveFileStatus.getModificationTime());
    drillArchive.setType(LocalResourceType.ARCHIVE);
    drillArchive.setVisibility(LocalResourceVisibility.PUBLIC);
    amLocalResources.put(config.getProperty("drill.archive.name"), drillArchive);
    amContainer.setLocalResources(amLocalResources);
    
    // 4. 设置am environment
     Map<String, String> amEnvironment = new LinkedHashMap<>();
    // add Hadoop Classpath
    for (String classpath : yarnConfig.getStrings(YarnConfiguration.YARN_APPLICATION_CLASSPATH,
        YarnConfiguration.DEFAULT_YARN_APPLICATION_CLASSPATH)) {
        Apps.addToEnvironment(amEnvironment, Environment.CLASSPATH.name(),
            classpath.trim(), ApplicationConstants.CLASS_PATH_SEPARATOR);
    }
    Apps.addToEnvironment(amEnvironment, Environment.CLASSPATH.name(),
        Environment.PWD.$() + File.separator + "*", ApplicationConstants.CLASS_PATH_SEPARATOR);
    StringWriter sw = new StringWriter();
    config.store(sw, "");
    String configBase64Binary = DatatypeConverter.printBase64Binary(sw.toString().getBytes("UTF-8"));
    Apps.addToEnvironment(amEnvironment, "DRILL_ON_YARN_CONFIG", configBase64Binary,
        ApplicationConstants.CLASS_PATH_SEPARATOR);
    amContainer.setEnvironment(amEnvironment);
    
    // 5. 设置am command
    
    List<String> commands = new ArrayList<>();
    commands.add(Environment.SHELL.$$());
    commands.add(config.getProperty("drill.archive.name") + "/bin/drill-am.sh");
    commands.add("1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + ApplicationConstants.STDOUT);
    commands.add("2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + ApplicationConstants.STDERR);
    StringBuilder amCommand = new StringBuilder();
    for (String str : commands) {
        amCommand.append(str).append(" ");
    }
    amCommand.setLength(amCommand.length() - " ".length());
    amContainer.setCommands(Collections.singletonList(amCommand.toString()));
    
    // 6. 设置安全令牌
    if (UserGroupInformation.isSecurityEnabled()) {
        Credentials credentials = new Credentials();
        String tokenRenewer = yarnConfig.get(YarnConfiguration.RM_PRINCIPAL);
        final Token<?> tokens[] = fileSystem.addDelegationTokens(tokenRenewer, credentials);
        DataOutputBuffer dob = new DataOutputBuffer();
        credentials.writeTokenStorageToStream(dob);
        ByteBuffer fsTokens = ByteBuffer.wrap(dob.getData(), 0, dob.getLength());
        amContainer.setTokens(fsTokens);
    }
    
    appContext.setAMContainerSpec(amContainer);
    
    • 提交应用程序
    client.submitApplication(appContext);
    
  2. ApplicationMaster(AM)

    1. 初始化AMRMClientAsync
    YarnConfiguration yarnConfig = new YarnConfiguration();
    AMRMClientAsync amrmClientAsync = AMRMClientAsync.createAMRMClientAsync(5000, new AMRMCallbackHandler());
    amrmClientAsync.init(yarnConfig);
    amrmClientAsync.start();
    
    1. 初始化NMClientAsync
    YarnConfiguration yarnConfig = new YarnConfiguration();
    NMClientAsync nmClientAsync = NMClientAsync.createNMClientAsync(new NMCallbackHandler());
    nmClientAsync.init(yarnConfig);
    nmClientAsync.start();
    
    1. 注册ApplicationMaster(AM)
    String thisHostName = InetAddress.getLocalHost();
    amrmClientAsync.registerApplicationMaster(thisHostName, 0, "");
    
    1. 添加ContainerRequest
    for (NodeReport containerReport : containerReports) {
        ContainerRequest containerRequest = new ContainerRequest(capability,
            new String[] {containerReport.getNodeId().getHost()},
            null, priority, false);
        amrmClientAsync.addContainerRequest(containerRequest);
    }
    
    1. 启动容器
    private static class AMRMCallbackHandler implements AMRMClientAsync.CallbackHandler {
        @Override
        public void onContainersAllocated(List<Container> containers) {
            for (Container container : containers) {
                ContainerLaunchContext containerContext = Records.newRecord(ContainerLaunchContext.class);
    
                // setEnvironment
                Map<String, String> containerEnvironment = new LinkedHashMap<>();
                // add Hadoop Classpath
                for (String classpath : yarnConfig.getStrings(YarnConfiguration.YARN_APPLICATION_CLASSPATH,
                    YarnConfiguration.DEFAULT_YARN_APPLICATION_CLASSPATH)) {
                    Apps.addToEnvironment(containerEnvironment, Environment.CLASSPATH.name(),
                        classpath.trim(), ApplicationConstants.CLASS_PATH_SEPARATOR);
                }
                Apps.addToEnvironment(containerEnvironment, Environment.CLASSPATH.name(),
                    Environment.PWD.$() + File.separator + "*", ApplicationConstants.CLASS_PATH_SEPARATOR);
                containerContext.setEnvironment(containerEnvironment);
    
                // setContainerResource
                Map<String, LocalResource> containerResources = new LinkedHashMap<>();
                LocalResource drillArchive = Records.newRecord(LocalResource.class);
                String drillArchivePath = appConfig.getProperty("fs.upload.dir") + appConfig.getProperty(
                    "drill.archive.name");
                Path path = new Path(drillArchivePath);
                FileStatus fileStatus = FileSystem.get(yarnConfig).getFileStatus(path);
                drillArchive.setResource(ConverterUtils.getYarnUrlFromPath(fileStatus.getPath()));
                drillArchive.setSize(fileStatus.getLen());
                drillArchive.setTimestamp(fileStatus.getModificationTime());
                drillArchive.setType(LocalResourceType.ARCHIVE);
                drillArchive.setVisibility(LocalResourceVisibility.PUBLIC);
                containerResources.put(appConfig.getProperty("drill.archive.name"), drillArchive);
                containerContext.setLocalResources(containerResources);
    
                // setContainerCommand
                List<String> commands = new ArrayList<>();
                commands.add(Environment.SHELL.$$());
                commands.add(appConfig.getProperty("drill.archive.name") + "/bin/drillbit.sh run");
                commands.add("1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + ApplicationConstants.STDOUT);
                commands.add("2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/" + ApplicationConstants.STDERR);
                StringBuilder containerCommand = new StringBuilder();
                for (String str : commands) {
                    containerCommand.append(str).append(" ");
                }
                containerCommand.setLength(containerCommand.length() - " ".length());
                containerContext.setCommands(Collections.singletonList(containerCommand.toString()));
    
                nmClientAsync.startContainerAsync(container, containerContext);
            }
    
        }
    }
    
    1. unregisterApplicationMaster(AM)
    amrmClientAsync.unregisterApplicationMaster(appStatus, appMessage, null);
    

客户端发给ResourceManager的资源请求示例

ApplicationMaster和NodeManager的交互

5. 为什么会有YARN

  1. 可扩展性:实践证明在MRv1中要将JobTracker扩展至4000个节点规模是极度困难,因为JobTarcker承担太多的职责,包括资源的调度,任务的跟踪和监控,当节点规模越来越大时,JobTracker编程越来越不堪重负。而在YARN中,JobTarcker被拆分为了RM和AM,职责更清晰,每个组件承担的职责变少,更加的轻量级。
  2. 对编程模型多样性的支持:由于MRv1中JobTarcker和TaskTarckerde的设计和MR框架耦合验证,导致MRv1仅仅支持mapreduce计算框架,对并行计算、流式计算、内存计算不支持。而在YARN中具体的框架由AM负责,是应用程序自己控制,YARN提供了统一的资源调度。
  3. 框架升级更容易:在MRv1中若要对MR框架升级,则需要重启整个Hadoop集群,风险很大。而在YARN中,具体的框架由AM负责,是应用程序自己控制,所以只需升级用户程序库即可。
  4. 集群资源利用率:MRv1引入了“slot”概念表示各个节点上的计算资源,将各个节点上的资源(CPU、内存和磁盘等)等量切分成若干份,每一份用一个slot表示,同时规定一个task可根据实际需要占用多个slot,slot又被分为Mapslot和Reduceslot两种,但不允许共享。对于一个作业,刚开始运行时,Map slot资源紧缺而Reduce slot空闲,当Map Task全部运行完成后,Reduce slot紧缺而Map slot空闲,很明显,降低了资源的利用率。而在YARN中,资源以Container形式表现,包含了内存、cpu等,相比MRv1,资源抽象粒度更细,其次,通过RM的Scheduler(FIFO、Capacity、Fair),保障资源的弹性。

6. 参考资料

  1. 《HadoopYARN权限指南》
  2. 《Hadoop技术内幕 深入解析YARN架构设计与实现原理》
<think>嗯,用户想了解Yarn包管理器的介绍和使用方法。首先,我需要回忆一下Yarn的基本信息。Yarn是由Facebook、Google等公司合作开发的,主要是为了解决npm早期版本的一些问题,比如依赖安装速度慢和版本不一致。然后,用户提到参考了站内的两个引用,里面提到了Yarn的工作空间和monorepo管理,还有和npm的区别,以及安装配置指南。 用户的问题分为两部分:介绍和使用方法。我需要先介绍Yarn是什么,它的优势,和npm的区别。然后详细说明使用方法,包括安装、初始化项目、添加依赖、安装依赖、运行脚本、工作空间这些方面。根据引用[1]和[2],要强调Yarn的效率和一致性,特别是工作空间的功能,这是用户可能感兴趣的点。 接下来,我需要检查是否有遗漏的要点。比如,Yarn的离线模式、版本锁定文件yarn.lock,这些也是关键点。另外,使用方法部分需要具体命令示例,比如yarn init、yarn add等,让用户能直接操作。工作空间的配置步骤也很重要,引用[1]里提到了,所以必须包含。 可能用户是开发者,正在考虑是否从npm切换到Yarn,或者开始新项目选择包管理工具。因此,需要对比Yarn和npm的优势,帮助用户做决定。同时,用户可能关心实际使用中的常见操作,比如安装依赖、管理依赖版本,以及如何利用工作空间管理多个包。 需要确保回答结构清晰,分点列出,符合用户要求的“结构清晰,帮助逐步解决问题”。同时,根据系统指令,数学公式要用$...$,但这里可能涉及代码块,所以要注意代码格式的正确性。不过问题里没有数学表达式,所以可能用不上,但生成的示例中的代码需要用正确格式。 最后,生成相关问题,可能需要考虑用户接下来会问什么,比如性能优化、工作空间详细配置、与npm的迁移等。根据引用内容,相关问题可以包括Yarn与npm的具体差异、工作空间的使用场景、如何确保依赖一致性等。 总结回答结构:先介绍Yarn,包括背景、优势和与npm的区别。然后使用方法分步骤说明,包括安装、项目初始化、依赖管理、脚本运行、工作空间。最后相关问题部分要覆盖这些方面,帮助用户深入了解更多内容。</think>### Yarn 包管理器介绍 Yarn 是由 Facebook、Google 等公司联合开发的 JavaScript 包管理工具,旨在解决早期 NPM 的依赖安装速度慢、版本管理不一致等问题。其核心优势包括: 1. **依赖版本锁定**:通过 `yarn.lock` 文件确保依赖版本一致性[^2]; 2. **并行安装**:加快依赖下载与安装速度; 3. **离线模式**:支持从本地缓存安装依赖; 4. **工作空间(Workspaces)**:支持 monorepo 项目的多包管理[^1]。 #### Yarn 与 NPM 的区别 | 特性 | Yarn | NPM | |--------------------|---------------------------|----------------------| | 安装速度 | 并行下载更快 | 早期版本较慢 | | 版本锁定 | 默认生成 `yarn.lock` | 需手动生成 `package-lock.json` | | 安全性 | 依赖校验机制更严格 | 依赖校验较弱 | --- ### Yarn 使用方法 #### 1. 安装 Yarn 通过 NPM 全局安装: ```bash npm install -g yarn ``` #### 2. 初始化项目 ```bash yarn init # 生成 package.json ``` #### 3. 添加依赖 ```bash yarn add [package] # 添加生产依赖 yarn add [package]@[version] # 指定版本 yarn add -D [package] # 添加开发依赖 ``` #### 4. 安装所有依赖 ```bash yarn install # 或简写为 yarn ``` #### 5. 运行脚本 ```bash yarn run [script-name] # 执行 package.json 中定义的脚本 ``` #### 6. 使用工作空间(Monorepo) 1. 在根目录 `package.json` 中启用工作空间: ```json { "private": true, "workspaces": ["packages/*"] } ``` 2. 在子包目录中分别管理依赖,Yarn 会自动提升公共依赖到根目录[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值