storm源码分析(十一)


2021SC@SDUSC

首先再介绍一些有关Worker的知识。
然后再分析代码。

有关Worker

worker, executor,task之间的关系

worker即进程,一个worker就是一个进程,进程里面包含一个或多个线程,一个线程就是一个executor,一个线程会处理一个或多个任务,一个任务就是一个task,一个task就是一个节点类的实例对象。

storm集群的一个节点可能有一个或者多个工作进程(worker)运行在一个多个拓扑上,一个工作进程执行拓扑的一个子集。工作进程(worker)属于一个特定的拓扑,并可能为这个拓扑的一个或者多个组件(spout/bolt)运行一个或多个执行器(executor线程)。一个运行中的拓扑包括多个运行在storm集群内多个节点的进程。

1个worker进程执行的是1个topology的子集(注:不会出现1个worker为多个topology服务)。1个worker进程会启动1个或多个executor线程来执行1个topology的component(spout或bolt)。因此,1个运行中的topology就是由集群中多台物理机上的多个worker进程组成的。

executor是1个被worker进程启动的单独线程。每个executor只会运行1个topology的1个component(spout或bolt)的task(注:task可以是1个或多个,storm默认是1个component只生成1个task,executor线程里会在每次循环里顺序调用所有task实例)。

task是最终运行spout或bolt中代码的单元(注:1个task即为spout或bolt的1个实例,executor线程在执行期间会调用该task的nextTuple或execute方法)。topology启动后,1个component(spout或bolt)的task数目是固定不变的,但该component使用的executor线程数可以动态调整(例如:1个executor线程可以执行该component的1个或多个task实例)。这意味着,对于1个component存在这样的条件:#threads<=#tasks(即:线程数小于等于task数目)。默认情况下task的数目等于executor线程数目,即1个executor线程只运行1个task。

管理Worker进程的事件线程

由Supervisor来管理Worker进程的事件线程。

每个工作节点运行Supervisor守护进程,负责监听工作节点上已经分配的主机作业,启动和停止Nimbus已经分配的工作进程。

supervisor会定时从zookeeper获取拓补信息topologies、任务分配信息assignments及各类心跳信息,以此为依据进行任务分配。

在supervisor同步时,会根据新的任务分配情况来启动新的worker或者关闭旧的worker并进行负载均衡。

处理流程如下:
在这里插入图片描述

代码分析worker-launcher.c

setup_permissions()

static int setup_permissions(FTSENT* entry, uid_t euser, int user_write, boolean setgid_on_dir) {
  if (lchown(entry->fts_path, euser, launcher_gid) != 0) {
    fprintf(ERRORFILE, "ERROR: Failure to exec app initialization process - %s, fts_path=%s\n",
            strerror(errno), entry->fts_path);
    return -1;
  }
  mode_t mode = entry->fts_statp->st_mode;
  // Preserve user read and execute and set group read and write.
  mode_t new_mode = (mode & (S_IRUSR | S_IXUSR)) | S_IRGRP | S_IWGRP;
  if (user_write) {
    new_mode = new_mode | S_IWUSR;
  }
  // If the entry is a directory, Add group execute and setGID bits.
  if ((mode & S_IFDIR) == S_IFDIR) {
    new_mode = new_mode | S_IXGRP;
    if (setgid_on_dir) {
      new_mode = new_mode | S_ISGID;
    }
  }
  if (chmod(entry->fts_path, new_mode) != 0) {
    fprintf(ERRORFILE, "ERROR: Failure to exec app initialization process - %s, fts_path=%s\n",
            strerror(errno), entry->fts_path);
    return -1;
  }
  return 0;
}

可选地为目录设置权限,使其可由用户来写。

我们设置了权限r(w)xrws——这样文件组(应该是Storm的用户组)对目录有完全的访问权限,文件用户(拓扑所有者的用户)能够读取和执行,并在某些目录中写入。设置setGID位以确保storm的用户可以访问在该目录下创建的任何文件,以便进行清理。如果setgid_on_dir为FALSE,则目录的组权限不要设置sticky位。

mkdirs()

int mkdirs(const char* path, mode_t perm) {
  struct stat sb;
  char * npath;
  char * p;
  if (stat(path, &sb) == 0) {
    return check_dir(path, sb.st_mode, perm, 1);
  }
  npath = strdup(path);
  if (npath == NULL) {
    fprintf(LOGFILE, "ERROR: Not enough memory to copy path string");
    return -1;
  }
  /* Skip leading slashes. */
  p = npath;
  while (*p == '/') {
    p++;
  }

  while (NULL != (p = strchr(p, '/'))) {
    *p = '\0';
    if (create_validate_dir(npath, perm, path, 0) == -1) {
      free(npath);
      return -1;
    }
    *p++ = '/'; /* restore slash */
    while (*p == '/')
      p++;
  }

  /* Create the final directory component. */
  if (create_validate_dir(npath, perm, path, 1) == -1) {
    free(npath);
    return -1;
  }
  free(npath);
  return 0;
}

确保给定的路径和所有父目录都以所需的权限创建。

create_validate_dir()

int create_validate_dir(const char* npath, mode_t perm, const char* path,
                        int finalComponent) {
  struct stat sb;
  if (stat(npath, &sb) != 0) {
    if (mkdir(npath, perm) != 0) {
      if (errno != EEXIST || stat(npath, &sb) != 0) {
        fprintf(LOGFILE, "ERROR: Can't create directory %s - %s\n", npath,
                strerror(errno));
        return -1;
      }
      // The directory npath should exist.
      if (check_dir(npath, sb.st_mode, perm, finalComponent) == -1) {
        return -1;
      }
    }
  } else {
    if (check_dir(npath, sb.st_mode, perm, finalComponent) == -1) {
      return -1;
    }
  }
  return 0;
}

如果父目录不存在,就创建。如果发生竞争条件,就检查权限。用0或1表示这是否是最后的分量。如果是,我们需要检查权限。

setup_worker_tmp_permissions()

int setup_worker_tmp_permissions(const char *worker_dir) {
  char* worker_tmp = concatenate("%s/tmp", "worker tmp dir", 1, worker_dir);
  if (worker_tmp != NULL) {
    int exit_code = setup_dir_permissions(worker_tmp, 1, FALSE);
    if (exit_code != 0) {
      fprintf(ERRORFILE, "ERROR: setup_dir_permissions on %s failed\n", worker_tmp);
      fflush(ERRORFILE);
    } 
    return exit_code;
  } else {
    return -1;
  }
}

移除worker-id/tmp目录上的setgid以便java剖析能够工作。这对于non-container workers来说是不需要的。但最好保持一致。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值