-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 388 KB
/
content.json
1
{"meta":{"title":"钢铁锅","subtitle":"应尽便须尽,无复独多虑","description":"纵浪大化中,不喜亦不悲","author":"GTG","url":"http://gangtieguo.cn","root":"/"},"pages":[{"title":"","date":"2021-08-25T10:27:20.372Z","updated":"2018-05-17T01:53:01.988Z","comments":true,"path":"baidu_verify_WHXmBFaAkY.html","permalink":"http://gangtieguo.cn/baidu_verify_WHXmBFaAkY.html","excerpt":"","text":"WHXmBFaAkY"},{"title":"","date":"2021-08-25T10:29:15.518Z","updated":"2018-05-17T02:15:40.515Z","comments":true,"path":"google00655d7c846aab3a.html","permalink":"http://gangtieguo.cn/google00655d7c846aab3a.html","excerpt":"","text":"google-site-verification: google00655d7c846aab3a.html"},{"title":"关于","date":"2018-05-08T07:52:07.000Z","updated":"2018-08-07T07:27:50.398Z","comments":true,"path":"about/index.html","permalink":"http://gangtieguo.cn/about/index.html","excerpt":"","text":"Nothing"},{"title":"404 Not Found:该页无法显示","date":"2021-08-25T10:30:16.898Z","updated":"2018-05-08T10:00:51.508Z","comments":false,"path":"/404.html","permalink":"http://gangtieguo.cn//404.html","excerpt":"","text":""},{"title":"分类","date":"2021-08-25T10:31:28.045Z","updated":"2018-08-07T07:28:04.841Z","comments":true,"path":"categories/index.html","permalink":"http://gangtieguo.cn/categories/index.html","excerpt":"","text":""},{"title":"标签","date":"2021-08-25T10:30:32.815Z","updated":"2018-08-07T07:27:29.135Z","comments":true,"path":"tags/index.html","permalink":"http://gangtieguo.cn/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"Hive解析任务-将json的多个属性拆分成多条记录","slug":"Hive解析任务","date":"2019-03-12T08:07:30.918Z","updated":"2019-06-17T04:40:09.387Z","comments":true,"path":"2019/03/12/Hive解析任务/","link":"","permalink":"http://gangtieguo.cn/2019/03/12/Hive解析任务/","excerpt":"[TOC] 需求环境: 在hive表dwb.dwb_r_thrid_data中,data字段存放有json字符串","text":"[TOC] 需求环境: 在hive表dwb.dwb_r_thrid_data中,data字段存放有json字符串 需要从json字符串中,解析到需要的字段:将一个json里面的属性data.loanInfo.mobile.timeScopes.D360、data.loanInfo.mobile.timeScopes.D90所包含的字段分别解析成一条记录,并且将D360、D90也作为字段timeScope的值解析到该条记录中。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122{ \"code\":\"0\", \"data\":{ \"loanInfo\":{ \"mobile\":{ \"timeScopes\":{ \"D360\":{ \"maxOverdueDays\":-1, \"loanTenantCount\":0, \"monthsFromFirstLoan\":-1, \"averageLoanGapDays\":-1, \"averageLoanAmount\":0, \"averageTenantGapDays\":-1, \"loanCount\":0, \"maxLoanAmount\":0, \"daysFromLastLoan\":-1, \"overdueTenantCount\":-1, \"queryCount\":0, \"monthsFromLastOverdue\":-1, \"maxLoanPeriodDays\":0, \"remainingAmount\":-1, \"monthsForNormalRepay\":-1, \"overdueLoanCount\":-1, \"overdueFor2TermTenantCount\":-1 }, \"D90\":{ \"maxOverdueDays\":-1, \"loanTenantCount\":0, \"averageLoanGapDays\":-1, \"averageLoanAmount\":0, \"averageTenantGapDays\":-1, \"overdueLoanCount\":-1, \"overdueFor2TermTenantCount\":-1, \"loanCount\":0, \"maxLoanAmount\":0, \"overdueTenantCount\":-1, \"queryCount\":0, \"maxLoanPeriodDays\":0 } } }, \"pid\":{ \"timeScopes\":{ \"D360\":{ \"maxOverdueDays\":-1, \"loanTenantCount\":0, \"monthsFromFirstLoan\":-1, \"averageLoanGapDays\":-1, \"averageLoanAmount\":0, \"averageTenantGapDays\":-1, \"loanCount\":0, \"maxLoanAmount\":0, \"daysFromLastLoan\":-1, \"overdueTenantCount\":-1, \"queryCount\":0, \"monthsFromLastOverdue\":-1, \"maxLoanPeriodDays\":0, \"remainingAmount\":-1, \"monthsForNormalRepay\":-1, \"overdueLoanCount\":-1, \"overdueFor2TermTenantCount\":-1 }, \"D90\":{ \"maxOverdueDays\":-1, \"loanTenantCount\":0, \"averageLoanGapDays\":-1, \"averageLoanAmount\":0, \"averageTenantGapDays\":-1, \"overdueLoanCount\":-1, \"overdueFor2TermTenantCount\":-1, \"loanCount\":0, \"maxLoanAmount\":0, \"overdueTenantCount\":-1, \"queryCount\":0, \"maxLoanPeriodDays\":0 } } }, \"deviceId\":{ \"timeScopes\":{ \"D360\":{ \"loanTenantCount\":0, \"loanCount\":0, \"queryCount\":0 }, \"D90\":{ \"loanTenantCount\":0, \"loanCount\":0, \"queryCount\":0 } } } }, \"blacklist\":{ \"mobile\":{ \"lastConfirmAtDays\":-1, \"lastConfirmStatus\":\"\", \"blackLevel\":\"none\", \"last6MTenantCount\":0, \"last6MQueryCount\":0, \"last12MMaxConfirmStatus\":\"\" }, \"pid\":{ \"lastConfirmAtDays\":-1, \"lastConfirmStatus\":\"\", \"blackLevel\":\"none\", \"last6MTenantCount\":0, \"last6MQueryCount\":0, \"last12MMaxConfirmStatus\":\"\" }, \"deviceId\":{ \"lastConfirmAtDays\":-1, \"lastConfirmStatus\":\"\", \"blackLevel\":\"none\", \"last6MTenantCount\":0, \"last6MQueryCount\":0, \"last12MMaxConfirmStatus\":\"\" } } }, \"message\":\"请求成功\"} 表结构形如: 12345678910111213141516171819202122232425262728create table if not exists dwb.dwb_r_morpho_loaninfo_mobile( apply_risk_id string comment \"风控ID\", dp_data_id string comment \"dp_dataID\", maxOverdueDays string, loanTenantCount string, monthsFromFirstLoan string, averageLoanGapDays string, averageLoanAmount string, averageTenantGapDays string, loanCount string, maxLoanAmount string, daysFromLastLoan string, overdueTenantCount string, queryCount string, monthsFromLastOverdue string, maxLoanPeriodDays string, remainingAmount string, monthsForNormalRepay string, overdueLoanCount string, overdueFor2TermTenantCount string, timeScope string comment \"时间期限\", morpho_created_at string comment \"创建时间\", etl_time string comment \"etl处理时间\") comment 'moblie' PARTITIONED BY (dt string comment '分区日期') row format delimited fields terminated by '\\001' NULL DEFINED AS '' stored as orc; 接下来就开始表演吧。 如果是json数组,可以很方便拆分我们都知道对于一条json里面值为json数组的属性,hive可以将其获取到并且进行拆分成多条记录: 如以下infoquerybean属性: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748{ \"overduemoreamt\":\"0\", \"loancount\":\"0\", \"loanbal\":\"0\", \"outstandcount\":\"0\", \"queryatotalorg\":\"最近***********\", \"loanamt\":\"0\", \"overdueamt\":\"0\", \"generationcount\":\"0\", \"msgContent\":\"成功!\", \"generationamount\":\"0\", \"overduemorecount\":\"0\", \"totalorg\":\"*************\", \"infoquerybean\":[ { \"s_value\":\"审批\", \"ddate\":\"2018-09-10\", \"ordernum\":\"1\" }, { \"s_value\":\"审批\", \"ddate\":\"2018-09-06\", \"ordernum\":\"2\" }, { \"s_value\":\"审批\", \"ddate\":\"2018-08-21\", \"ordernum\":\"3\" }, { \"s_value\":\"审批\", \"ddate\":\"2018-08-09\", \"ordernum\":\"4\" }, { \"s_value\":\"审批\", \"ddate\":\"2018-07-28\", \"ordernum\":\"5\" }, { \"s_value\":\"审批\", \"ddate\":\"2018-07-27\", \"ordernum\":\"6\" } ], \"overduecount\":\"0\", \"msgCode\":\"200\"} 可以通过split拆分成结果,插入到形状如下的表中: 12345678910111213create table if not exists dwb.dwb_r_nifa_share_detail_n( apply_risk_id string comment \"风控ID\", dp_data_id string comment \"dp_dataID\", nifa_share_detail_ordernum string comment '序号', nifa_share_detail_ddate string comment '查询日期', nifa_share_detail_s_value string comment '查询原因', nifa_share_created_at string comment \"创建时间\", etl_time string comment \"etl处理时间\") comment 'table test' PARTITIONED BY (dt string comment '分区日期') row format delimited fields terminated by '\\001' NULL DEFINED AS '' stored as orc; 通过 1explode(split(default.get_json_path(a.data,'infoquerybean'),'@\\\\|@')) 将其拆分成多条记录,完整sql见下: 123456789101112131415161718192021dt=$1hive<<!set mapreduce.job.queuename=root.dw;set hive.support.concurrency=false;insert overwrite table dwb.dwb_r_nifa_share_detail_n partition(dt='$dt')select a.apply_risk_id, a.dp_data_id, nifa_share_detail_ordernum, nifa_share_detail_ddate, nifa_share_detail_s_value, from_unixtime(cast(a.timestamp/1000 as bigint),'yyyy-MM-dd HH:mm:ss') as nifa_share_created_at, current_timestamp() as etl_timefrom (select dwb_r_thrid_data.apply_risk_id,dwb_r_thrid_data.dp_data_id,dwb_r_thrid_data.data,dwb_r_thrid_data.timestamp from dwb.dwb_r_thrid_data where channel_name = 'nifa_prod' and interface_name = 'share' and get_json_object(data,'$.msgCode') = '200' and dwb_r_thrid_data.dt='$dt' ) a lateral view explode(split(default.get_json_path(a.data,'infoquerybean'),'@\\\\\\\\|@')) b as infoquerybean lateral view default.json_tuple2(b.infoquerybean,'ordernum','ddate','ordernum') c as nifa_share_detail_ordernu,nifa_share_detail_ddate, nifa_share_detail_s_value;! 得到结果: 顺着json数组思路,改造json样式通过get_json_object()方法,得到两个json属性,通过concat拼接成json数组,就可以像上面那样拆分成多条记录。(测试阶段的样例都使用了设定分区dt,限制条数,因为这样测试起来很快,只需要三秒!!!!😁😁😁😁) 1.通过get_json_object方法1select get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D360\") d3,get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D90\") d9 from dwb.dwb_r_thrid_data td where channel_name ='morpho' and interface_name ='query' and dt='20190218' limit 5 得到 2.拼接获取的D360和D90字段1select td.*,concat(regexp_replace(get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D360\"),'}',',\"timeScope\":\"D360\"}'),\"|\",regexp_replace(get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D90\"),'}',',\"timeScope\":\"D90\"}')) ts from dwb.dwb_r_thrid_data td where channel_name ='morpho' and interface_name ='query' and dt='20190218' limit 5 拼接的字符串样式,通过”|”分隔两个对象 1{\"maxOverdueDays\":-1,\"monthsFromFirstLoan\":-1,\"loanTenantCount\":0,\"averageLoanGapDays\":-1,\"averageTenantGapDays\":-1,\"averageLoanAmount\":0,\"loanCount\":0,\"maxLoanAmount\":0,\"overdueTenantCount\":-1,\"daysFromLastLoan\":-1,\"queryCount\":0,\"monthsFromLastOverdue\":-1,\"maxLoanPeriodDays\":0,\"remainingAmount\":-1,\"monthsForNormalRepay\":-1,\"overdueLoanCount\":-1,\"overdueFor2TermTenantCount\":-1,\"timeScope\":\"D360\"}|{\"maxOverdueDays\":-1,\"loanTenantCount\":0,\"averageLoanGapDays\":-1,\"averageTenantGapDays\":-1,\"averageLoanAmount\":0,\"overdueLoanCount\":-1,\"overdueFor2TermTenantCount\":-1,\"loanCount\":0,\"overdueTenantCount\":-1,\"maxLoanAmount\":0,\"queryCount\":0,\"maxLoanPeriodDays\":0,\"timeScope\":\"D90\"} 3. 最终通过分隔符进行切分对于涉及到分隔符,转义字符的个数,请参考该文章数仓-解决hive处理异常json命令行转义字符的问题 1234567891011121314select a.apply_risk_id, a.dp_data_id, c.*, from_unixtime(cast(a.timestamp/1000 as bigint),'yyyy-MM-dd HH:mm:ss') as morpho_created_at, current_timestamp() as etl_timefrom (select td.*,concat(regexp_replace(get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D360\"),'}',',\"timeScope\":\"D360\"}'),\"|\",regexp_replace(get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D90\"),'}',',\"timeScope\":\"D90\"}')) ts from dwb.dwb_r_thrid_data td where channel_name ='morpho' and interface_name ='query' and td.dt='20190218' limit 5 ) alateral view explode(split(a.ts,'\\\\\\\\|')) b as listlateral view default.json_tuple2(b.list,'maxOverdueDays','loanTenantCount','monthsFromFirstLoan','averageLoanGapDays','averageLoanAmount','averageTenantGapDays','loanCount','maxLoanAmount','daysFromLastLoan','overdueTenantCount','queryCount','monthsFromLastOverdue','maxLoanPeriodDays','remainingAmount','monthsForNormalRepay','overdueLoanCount','overdueFor2TermTenantCount','timeScope') c as maxOverdueDays,loanTenantCount,monthsFromFirstLoan,averageLoanGapDays,averageLoanAmount,averageTenantGapDays,loanCount,maxLoanAmount,daysFromLastLoan,overdueTenantCount,queryCount,monthsFromLastOverdue,maxLoanPeriodDays,remainingAmount,monthsForNormalRepay,overdueLoanCount,overdueFor2TermTenantCount,timeScope 搞定 完整样例123456789101112131415161718192021dt=$1hive<<!set mapreduce.job.queuename=root.dw;set hive.support.concurrency=false;insert overwrite table dwb.dwb_r_morpho_loaninfo_mobile partition(dt='$dt')select a.apply_risk_id, a.dp_data_id, c.*, from_unixtime(cast(a.timestamp/1000 as bigint),'yyyy-MM-dd HH:mm:ss') as morpho_created_at, current_timestamp() as etl_timefrom (select td.*,concat(regexp_replace(get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D360\"),'}',',\"timeScope\":\"D360\"}'),\"|\",regexp_replace(get_json_object(td.data,\"$.data.loanInfo.mobile.timeScopes.D90\"),'}',',\"timeScope\":\"D90\"}')) ts from dwb.dwb_r_thrid_data td where channel_name ='morpho' and interface_name ='query' ) alateral view explode(split(a.ts,'\\\\\\\\|')) b as listlateral view default.json_tuple2(b.list,'maxOverdueDays','loanTenantCount','monthsFromFirstLoan','averageLoanGapDays','averageLoanAmount','averageTenantGapDays','loanCount','maxLoanAmount','daysFromLastLoan','overdueTenantCount','queryCount','monthsFromLastOverdue','maxLoanPeriodDays','remainingAmount','monthsForNormalRepay','overdueLoanCount','overdueFor2TermTenantCount','timeScope') c as maxOverdueDays,loanTenantCount,monthsFromFirstLoan,averageLoanGapDays,averageLoanAmount,averageTenantGapDays,loanCount,maxLoanAmount,daysFromLastLoan,overdueTenantCount,queryCount,monthsFromLastOverdue,maxLoanPeriodDays,remainingAmount,monthsForNormalRepay,overdueLoanCount,overdueFor2TermTenantCount,timeScope;! 参考:HIVE: lateral view explode & json_turpe 实现 json数组行转列&字段拆分","categories":[{"name":"实际问题","slug":"实际问题","permalink":"http://gangtieguo.cn/categories/实际问题/"}],"tags":[{"name":"Hive","slug":"Hive","permalink":"http://gangtieguo.cn/tags/Hive/"},{"name":"数仓","slug":"数仓","permalink":"http://gangtieguo.cn/tags/数仓/"}]},{"title":"mac与virtualbox共享文件夹,挂载到docker容器中文件目录权限解决","slug":"mac与virtualbox共享文件夹,挂载到docker容器中文件目录权限解决","date":"2019-03-12T01:49:35.656Z","updated":"2019-06-17T04:40:09.392Z","comments":true,"path":"2019/03/12/mac与virtualbox共享文件夹,挂载到docker容器中文件目录权限解决/","link":"","permalink":"http://gangtieguo.cn/2019/03/12/mac与virtualbox共享文件夹,挂载到docker容器中文件目录权限解决/","excerpt":"[TOC] 共享文件夹的权限问题参考:Virtualbox设置共享文件夹 由于共享文件夹并不是虚拟机的本地目录,我们在虚拟机中可以配置共享文件夹的权限是有限的。 手动挂载或自动挂载的目录,所属用户默认为root,组为vboxsf,并且使用 chmod chown 等命令是无法改变的。","text":"[TOC] 共享文件夹的权限问题参考:Virtualbox设置共享文件夹 由于共享文件夹并不是虚拟机的本地目录,我们在虚拟机中可以配置共享文件夹的权限是有限的。 手动挂载或自动挂载的目录,所属用户默认为root,组为vboxsf,并且使用 chmod chown 等命令是无法改变的。 如果想要配置挂载目录的权限,需要在手动挂载的时候指定一些选项: 12345// uid gid指定挂载目录的所属用户和组sudo mount -t vboxsf -o uid=500,gid=500 <folder name given in VirtualBox>// fmode指定文件权限,dmode指定目录权限// 注意,若同时指定挂载目录的所属用户和组,则fmode和dmode选项失效sudo mount -t vboxsf -o fmode=700,dmode=700 <folder name given in VirtualBox> 我使用的命令为: 1sudo mount -t vboxsf -o uid=500,gid=500 Y /Users/yaosong/Yao 此处Y为我设置的共享路径 踩过的坑: 之前由于在挂载时,使用的 sudo mount -t vboxsf Yao /Users/yaosong/Yao 并未设置所属用户组及所属用户,然后在虚拟机中使用chown,无论如何都未成功, 看到上一篇博文,茅塞顿开 使用sudo mount -t vboxsf -o uid=500,gid=500 Y /Users/yaosong/Yao过后 由于elk用户对应用户id,用户组id为500, 可以得到一下所属用户和用户组: 此处设置用户id和组id,下文也要用到 挂载到容器的文件目录与宿主机的设置关系 参考:关于Docker目录挂载的总结,Docker Volume - 目录挂载以及文件共享 之所以设置共享文件夹权限时设置用户id和组id,还有一个重要原因: 原来,宿主与容器的UID有关系,UID,即“用户标识号”,是一个整数,系统内部用它来标识用户。一般情况下它与用户名是一一对应的。即为:在宿主中UID为多少,那么在容器中的所属用户所属组就为容器中和宿主UID相对应的用户。 启动容器时挂载目录 1docker run -itd --net=br --name elk1 --privileged=true -v /Users/yaosong/Yao/share/source/es1:/usr/es --hostname elk1 --ip=192.168.33.16 yaosong5/elk:1.0 &> /dev/null 可知宿主机/Users/yaosong/Yao/share/source/es1目录挂载到容器/usr/es中 在容器内设置权限: 1chown -R elk.elk $ES_HOME 注意是点不是冒号,区别我还没研究 查看结果 参考文章Virtualbox设置共享文件夹 关于Docker目录挂载的总结 Docker Volume - 目录挂载以及文件共享","categories":[{"name":"问题解决","slug":"问题解决","permalink":"http://gangtieguo.cn/categories/问题解决/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"Mac","slug":"Mac","permalink":"http://gangtieguo.cn/tags/Mac/"}]},{"title":"Docker-machine的创建,mac宿主机和docker容器网络互通Docker容器与宿主机在同一ip段下","slug":"Docker-machine的搭建(与宿主机在同一ip段下)","date":"2019-03-05T08:14:31.000Z","updated":"2019-06-17T04:40:09.360Z","comments":true,"path":"2019/03/05/Docker-machine的搭建(与宿主机在同一ip段下)/","link":"","permalink":"http://gangtieguo.cn/2019/03/05/Docker-machine的搭建(与宿主机在同一ip段下)/","excerpt":"此文纯属命令记录,后续更新原理解说","text":"此文纯属命令记录,后续更新原理解说 更改virtual0的ip VBoxManage hostonlyif ipconfig vboxnet0 –ip 192.168.33.253 –netmask 255.255.255.0 ifconfig 查看创建虚拟机配置文件 Vagrantfile 也可以vagrant init 会生成一个空白的Vagrantfile vi Vagrantfile1234567891011121314151617181920212223Vagrant.configure(2) do |config| config.vm.box = \"dolbager/centos-7-docker\" config.vm.hostname = \"default\" config.vm.network \"private_network\", ip: \"192.168.33.1\",netmask: \"255.255.255.0\" config.vm.provider \"virtualbox\" do |v| v.name = \"default\" v.memory = \"2048\" # Change the network adapter type and promiscuous mode v.customize ['modifyvm', :id, '--nictype1', 'Am79C973'] v.customize ['modifyvm', :id, '--nicpromisc1', 'allow-all'] v.customize ['modifyvm', :id, '--nictype2', 'Am79C973'] v.customize ['modifyvm', :id, '--nicpromisc2', 'allow-all'] end # Install bridge-utils config.vm.provision \"shell\", inline: <<-SHELL curl -o /etc/yum.repos.d/CentOS-Base.repohttp://mirrors.aliyun.com/repo/Centos-7.repo curl -o /etc/yum.repos.d/epel.repohttp://mirrors.aliyun.com/repo/epel-7.repo yum clean all yum makecache yum update -y yum install bridge-utils net-tools -y SHELLend vagrant upvagrant ssh vagrant ssh-config 1scp ~/.vagrant.d/boxes/dolbager-VAGRANTSLASH-centos-7-docker/0.2/virtualbox/vagrant_private_key .vagrant/machines/default/virtualbox/private_key vagrant exit 1234567docker-machine create \\ --driver \"generic\" \\ --generic-ip-address 192.168.33.1 \\ --generic-ssh-user vagrant \\ --generic-ssh-key .vagrant/machines/default/virtualbox/private_key \\ --generic-ssh-port 22 \\ default 创建网桥docker1 和 docker network br通过vagrant 从虚拟机的 eth0 登录到虚拟机 vagrant sship -4 addr 创建 docker network br 123456789sudo docker network create \\ --driver bridge \\ --subnet=192.168.33.0/24 \\ --gateway=192.168.33.1 \\ --opt \"com.docker.network.bridge.enable_icc\"=\"true\" \\ --opt \"com.docker.network.bridge.enable_ip_masquerade\"=\"true\" \\ --opt \"com.docker.network.bridge.name\"=\"docker1\" \\ --opt \"com.docker.network.driver.mtu\"=\"1500\" \\ br 创建网桥配置文件docker1 vim /etc/sysconfig/network-scripts/ifcfg-docker1 123456789DEVICE=docker1TYPE=BridgeBOOTPROTO=staticONBOOT=yesSTP=onIPADDR=NETMASK=GATEWAY=DNS1= 修改网卡配置 eth1 :sudo vi /etc/sysconfig/network-scripts/ifcfg-eth1 12345678DEVICE=eth1BOOTPROTO=staticHWADDR=ONBOOT=yesNETMASK=GATEWAY=BRIDGE=docker1TYPE=Ethernet ip -4 addr 初始化docker-machine变量 提示报错Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? 123eval $(docker-machine env default) 如果要取消变量 eval $(docker-machine env -u) 参考:docker-install-mac-vm-centos","categories":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/categories/Docker/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"Docker-machine","slug":"Docker-machine","permalink":"http://gangtieguo.cn/tags/Docker-machine/"},{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/tags/安装部署/"}]},{"title":"ELK-Logstash->kafka->es流程实例","slug":"ELK流程小实例","date":"2018-09-08T14:30:24.772Z","updated":"2019-06-17T04:40:09.374Z","comments":true,"path":"2018/09/08/ELK流程小实例/","link":"","permalink":"http://gangtieguo.cn/2018/09/08/ELK流程小实例/","excerpt":"elk流程实例 [TOC] 前言:一般日志收集,我们通过logstash来接入,写入到kafka,再通过logstash将kafka的数据写入到es 那么就通过一个菜的抠脚的实例来看看 LOGSTASH->KAFKA->ES1.启动logstash将日志写入到kafka①准备工作创建topic先创建对应topic,(按照机器配置,集群情况配置,不然默认创建的话,不是最优效果)","text":"elk流程实例 [TOC] 前言:一般日志收集,我们通过logstash来接入,写入到kafka,再通过logstash将kafka的数据写入到es 那么就通过一个菜的抠脚的实例来看看 LOGSTASH->KAFKA->ES1.启动logstash将日志写入到kafka①准备工作创建topic先创建对应topic,(按照机器配置,集群情况配置,不然默认创建的话,不是最优效果) 1$KAFKA_HOME/bin/kafka-topics.sh --create --zookeeper zk1:2181,zk2:2181,zk3:2181 --replication-factor 2 --partitions 3 --topic gamekafka 查看topic是否创建成功 1$KAFKA_HOME/bin/kafka-topics.sh --list --zookeeper zk1:2181,zk2:2181,zk3:2181 ②编辑写入kafka的logstash配置文件 vim $LOGSTASH_HOME/conf/logstash-game-kafka.conf 123456789101112131415161718192021222324input { file { codec=>plain { charset => "UTF-8" } #这是日志的路径 path => "/BaseDir/2016-02-01/*.txt" discover_interval => 5 start_position => "beginning" }}output { kafka { topic_id => "gamekafka" codec => plain { format => "%{message}" charset => "UTF-8" } #kafka的地址,端口为9092 bootstrap_servers => "kafka1:9092,kafka2:9092,kafka3:9092" } stdout {codec => rubydebug}} ③启动将文件写入kafka的logstash1234567891011$LOGSTASH_HOME/bin/logstash -f $LOGSTASH_HOME/conf/logstash-game-kafka.conf#封装过一个小脚本logstash-start.sh#!/bin/bashCONF_PATH=/usr/logstash/conf/$1echo $CONF_PATHnohup $LOGSTASH_HOME/bin/logstash -f $CONF_PATH &# 那么执行命令换成sh logstash-start.sh logstash-game-kafka.conf 若启动成功 查看日志文件是否写入了kafka1$KAFKA_HOME/bin/kafka-console-consumer.sh --bootstrap-server "kafka1:9092,kafka2:9092,kafka3:9092" --topic gamekafka --from-beginning --group testGroup 若logstash报错若测试报错内存不够,参考 elasticsearch6.2和logstash启动出现的错误 在es和logstash的配置目录jvm.options中设置更小内存 12-Xms400m -Xmx400m 2.将kafka中的数据通过logstash写入到es当然要先启动es啦(此处省略) 1$ES_HOME/bin/elasticsearch -d ①编辑写入到es的logstash配置文件配置文件 vim $LOGSTASH_HOME/conf/logstash-game-kafka-es.conf 1234567891011121314151617181920212223242526272829303132333435363738input { kafka { codec => "plain" group_id => "es2" bootstrap_servers => ["kafka1:9092,kafka2:9092,kafka3:9092"] # 注意这里配置的kafka的broker地址不是zk的地址 auto_offset_reset => "earliest" topics => ["gamekafka"] }}filter { mutate { split => { "message" => " " } add_field => { "event_type" => "%{message[3]}" "current_map" => "%{message[4]}" "current_X" => "%{message[5]}" "current_y" => "%{message[6]}" "user" => "%{message[7]}" "item" => "%{message[8]}" "item_id" => "%{message[9]}" "current_time" => "%{message[12]}" } remove_field => [ "message" ] }}output { elasticsearch { index => "gamelogs" codec => plain { charset => "UTF-8" } hosts => ["elk1:9200", "elk2:9200", "elk3:9200"] } stdout {codec => rubydebug}} ②启动写入到es的logstash1$LOGSTASH_HOME/bin/logstash -f $LOGSTASH_HOME/conf/logstash-game-kafka-es.conf 查看es里面是否有数据 如果是介个样子,那么大功告成 也可以直接从logstash->es由于logstash的output形式多样,也可直接通过logstash将日志数据写入到es当中 ①配置文件vim logstash-game-file-es.conf 1234567891011121314151617181920212223242526272829303132333435363738input { file { codec=>plain { charset => "UTF-8" } path => "/BaseDir/2016-02-01/*.txt" discover_interval => 5 start_position => "beginning" }}filter { mutate { split => { "message" => " " } add_field => { "event_type" => "%{message[3]}" "current_map" => "%{message[4]}" "current_X" => "%{message[5]}" "current_y" => "%{message[6]}" "user" => "%{message[7]}" "item" => "%{message[8]}" "item_id" => "%{message[9]}" "current_time" => "%{message[12]}" } remove_field => [ "message" ] }}output { elasticsearch { index => "kafkagamelogs" codec => plain { charset => "UTF-8" } hosts => ["elk1:9200", "elk2:9200", "elk3:9200"] } stdout {codec => rubydebug}} ②启动logstash1$LOGSTASH_HOME/bin/logstash -f $LOGSTASH_HOME/conf/logstash-game-file-es.conf","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"ELK","slug":"ELK","permalink":"http://gangtieguo.cn/tags/ELK/"}]},{"title":"DataStream DataSet介绍","slug":"DataFrame DataSet","date":"2018-09-05T17:20:33.287Z","updated":"2018-12-25T03:51:31.046Z","comments":true,"path":"2018/09/06/DataFrame DataSet/","link":"","permalink":"http://gangtieguo.cn/2018/09/06/DataFrame DataSet/","excerpt":"[TOC] 什么是DataStreamDiscretized Stream是Spark Streaming的基础抽象,代表持续性的数据流和经过各种Spark原语操作后的结果数据流。在内部实现上,DStream是一系列连续的RDD来表示。每个RDD含有一段时间间隔内的数据,如下图:","text":"[TOC] 什么是DataStreamDiscretized Stream是Spark Streaming的基础抽象,代表持续性的数据流和经过各种Spark原语操作后的结果数据流。在内部实现上,DStream是一系列连续的RDD来表示。每个RDD含有一段时间间隔内的数据,如下图: 与RDD类似,DataFrame也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。从API易用性的角度上 看,DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。由于与R和Pandas的DataFrame类似,Spark DataFrame很好地继承了传统单机数据分析的开发体验。 对数据的操作也是按照RDD为单位来进行的 计算过程由Spark engine来完成 Datasets 与DataFrames 与RDDs的关系 Spark引入DataFrame,它可以提供high-level functions让Spark更好的处理结构数据的计算。这让Catalyst optimizer 和Tungsten(钨丝) execution engine自动加速大数据分析。发布DataFrame之后开发者收到了很多反馈,其中一个主要的是大家反映缺乏编译时类型安全。为了解决这个问题,Spark采用新的Dataset API (DataFrame API的类型扩展)。Dataset API扩展DataFrame API支持静态类型和运行已经存在的Scala或Java语言的用户自定义函数。对比传统的RDD API,Dataset API提供更好的内存管理,特别是在长任务中有更好的性能提升 DataStream相关操作DStream上的原语与RDD的类似,分为Transformations(转换)和OutputOperations(输出)两种,此外转换操作中还有一些比较特殊的原语,如:updateStateByKey()、transform()以及各种Window相关的原语。 Transformations on DStreams Transformation Meaning map(func) Return a new DStream by passing each element of the source DStream through a function func. flatMap(func) Similar to map, but each input item can be mapped to 0 or more output items. filter(func) Return a new DStream by selecting only the records of the source DStream on which func returns true. repartition(numPartitions) Changes the level of parallelism in this DStream by creating more or fewer partitions. union(otherStream) Return a new DStream that contains the union of the elements in the source DStream and otherDStream. count() Return a new DStream of single-element RDDs by counting the number of elements in each RDD of the source DStream. reduce(func) Return a new DStream of single-element RDDs by aggregating the elements in each RDD of the source DStream using a function func (which takes two arguments and returns one). The function should be associative so that it can be computed in parallel. countByValue() When called on a DStream of elements of type K, return a new DStream of (K, Long) pairs where the value of each key is its frequency in each RDD of the source DStream. reduceByKey(func, [numTasks]) When called on a DStream of (K, V) pairs, return a new DStream of (K, V) pairs where the values for each key are aggregated using the given reduce function. Note: By default, this uses Spark’s default number of parallel tasks (2 for local mode, and in cluster mode the number is determined by the config property spark.default.parallelism) to do the grouping. You can pass an optional numTasks argument to set a different number of tasks. join(otherStream, [numTasks]) When called on two DStreams of (K, V) and (K, W) pairs, return a new DStream of (K, (V, W)) pairs with all pairs of elements for each key. cogroup(otherStream, [numTasks]) When called on a DStream of (K, V) and (K, W) pairs, return a new DStream of (K, Seq[V], Seq[W]) tuples. transform(func) Return a new DStream by applying a RDD-to-RDD function to every RDD of the source DStream. This can be used to do arbitrary RDD operations on the DStream. updateStateByKey(func) Return a new “state” DStream where the state for each key is updated by applying the given function on the previous state of the key and the new values for the key. This can be used to maintain arbitrary state data for each key. 特殊的Transformations UpdateStateByKeyOperation UpdateStateByKey原语用于记录历史记录,上文中Word Count示例中就用到了该特性。若不用UpdateStateByKey来更新状态,那么每次数据进来后分析完成后,结果输出后将不在保存 TransformOperation Transform原语允许DStream上执行任意的RDD-to-RDD函数。通过该函数可以方便的扩展Spark API。此外,MLlib(机器学习)以及Graphx也是通过本函数来进行结合的。 WindowOperations Window Operations有点类似于Storm中的State,可以设置窗口的大小和滑动窗口的间隔来动态的获取当前Steaming的允许状态 Output Operations on DStreamsOutput Operations可以将DStream的数据输出到外部的数据库或文件系统,当某个Output Operations原语被调用时(与RDD的Action相同),streaming程序才会开始真正的计算过程。 Output Operation Meaning print() Prints the first ten elements of every batch of data in a DStream on the driver node running the streaming application. This is useful for development and debugging. saveAsTextFiles(prefix, [suffix]) Save this DStream’s contents as text files. The file name at each batch interval is generated based on prefix and suffix: “prefix-TIME_IN_MS[.suffix]”. saveAsObjectFiles(prefix, [suffix]) Save this DStream’s contents as SequenceFiles of serialized Java objects. The file name at each batch interval is generated based on prefix and suffix: “prefix-TIME_IN_MS[.suffix]”. saveAsHadoopFiles(prefix, [suffix]) Save this DStream’s contents as Hadoop files. The file name at each batch interval is generated based on prefix and suffix: “prefix-TIME_IN_MS[.suffix]”. foreachRDD(func) The most generic output operator that applies a function, func, to each RDD generated from the stream. This function should push the data in each RDD to an external system, such as saving the RDD to files, or writing it over the network to a database. Note that the function func is executed in the driver process running the streaming application, and will usually have RDD actions in it that will force the computation of the streaming RDDs. 用Spark Streaming实现实时WordCount架构图: 1.安装并启动生成者 首先在一台Linux(ip:192.168.10.101)上用YUM安装nc工具 yum install -y nc 启动一个服务端并监听9999端口 nc -lk 9999 2.编写Spark Streaming程序 1234567891011121314151617package me.yao.spark.streamingimport org.apache.spark.SparkConfimport org.apache.spark.streaming.{Seconds, StreamingContext}object NetworkWordCount { def main(args: Array[String]) { //设置日志级别 LoggerLevel.setStreamingLogLevels() //创建SparkConf并设置为本地模式运行 //注意local[2]代表开两个线程 val conf = new SparkConf().setMaster(\"local[2]\").setAppName(\"NetworkWordCount\") //设置DStream批次时间间隔为2秒 val ssc = new StreamingContext(conf, Seconds(2)) //通过网络读取数据 val lines = ssc.socketTextStream(\"192.168.10.101\", 9999) //将读到的数据用空格切成单词 val words = lines.flatMap(_.split(\" \")) //将单词和1组成一个pair val pairs = words.map(word => (word, 1)) //按单词进行分组求相同单词出现的次数 val wordCounts = pairs.reduceByKey(_ + _) //打印结果到控制台 wordCounts.print() //开始计算 ssc.start() //等待停止 ssc.awaitTermination() } } 问题:结果每次在Linux端输入的单词次数都被正确的统计出来,但是结果不能累加!如果需要累加需要使用updateStateByKey(func)来更新状态,下面给出一个例子: 12345678910111213141516171819202122232425262728 package me.yao.spark.streaming import org.apache.spark.{HashPartitioner, SparkConf} import org.apache.spark.streaming.{StreamingContext, Seconds} object NetworkUpdateStateWordCount { val updateFunc = (iter: Iterator[(String, Seq[Int], Option[Int])]) => { //iter.flatMap(it=>Some(it._2.sum + it._3.getOrElse(0)).map(x=>(it._1,x))) iter.flatMap{ case(x,y,z)=>Some(y.sum + z.getOrElse(0)).map(m=>(x, m))} } def main(args: Array[String]) { LoggerLevel.setStreamingLogLevels*() val conf = new SparkConf().setMaster(\"local[2]\").setAppName(\"NetworkUpdateStateWordCount\") val ssc = new StreamingContext(conf, Seconds(5)) //做checkpoint 写入共享存储中 ssc.checkpoint(\"c://aaa\") **val **lines = ssc.socketTextStream(\"192.168.10.100\", 9999) //reduceByKey **结果不累加 //val result = lines.flatMap(_.split(\" \")).map((_, 1)).reduceByKey(_+_) //updateStateByKey结果可以累加但是需要传入一个自定义的累加函数:updateFunc val results = lines.flatMap(_.split(\" \")).map((_,1)).updateStateByKey(updateFunc, new HashPartitioner(ssc.sparkContext.defaultParallelism), true) results.print() ssc.start() ssc.awaitTermination() }} Dataset比RDD执行速度快很多倍,占用的内存更小,是从dataFrame发展而来,包含dataFramedataFrame是处理结构化数据,有表头,有类型, dataSet从1.6.0开始出现,2.0做了重大改进,对dataFrame进行了整合dataFrame在1.4系列出现的,现在很多公司都是用的RDD 在spark的命令行里面:将dataFrame转成dataSetval ds = df.as[person]调用dataSet的方法 1234ds.map ds.show val ds = sqlContext.read.text(\"hdfs://bigdata1:9000/wc/).as[String] val res5 = ds.flatmap(.split(\" \")).map((,1)) flatmap将文本里面的每一行进行切分,rest.reduceByKey();会发现dataSet里面没有这个方法,在dataSet里面应该调用更高级的做法ds.flatmap(_.split(“ “)).groupBy($””value).count.show 或者collect 在import里面打开idea查看类里面有哪些方法。在spark1.6里面sqlContext.read….读取的就是dataFrame,和dataSet还未统一,需要将dataFrame用as转为dataSet Spark引入DataFrame,它可以提供high-level functions让Spark更好的处理结构数据的计算。这让Catalyst optimizer 和Tungsten(钨丝) execution engine自动加速大数据分析。发布DataFrame之后开发者收到了很多反馈,其中一个主要的是大家反映缺乏编译时类型安全。为了解决这个问题,Spark采用新的Dataset API (DataFrame API的类型扩展)。Dataset API扩展DataFrame API支持静态类型和运行已经存在的Scala或Java语言的用户自定义函数。对比传统的RDD API,Dataset API提供更好的内存管理,特别是在长任务中有更好的性能提升 #创建DataSetcase class Data(a: Int, b: String)val ds = Seq(Data(1, “one”), Data(2, “two”)).toDS()ds.collect()ds.show() #创建DataSet 1234case class Person(name: String, zip: Long)val df = sqlContext.read.json(sc.parallelize(\"\"\"{\"zip\": 94709, \"name\": \"Michael\"}\"\"\" :: Nil))df.as[Person].collect()df.as[Person].show() #DataSet的WordCount 123456import org.apache.spark.sql.functions._val ds = sqlContext.read.text(\"hdfs://node-1.itcast.cn:9000/wc\").as[String]val result = ds.flatMap(_.split(\" \")).filter(_ != \"\").toDF().groupBy($\"value\").agg(count(\"*\") as \"numOccurances\").orderBy($\"numOccurances\" desc)val wordCount = ds.flatMap(_.split(\" \")).filter(_ != \"\").groupBy(_.toLowerCase()).count() #创建DataSet 1val lines = sqlContext.read.text(\"hdfs://node-1.itcast.cn:9000/wc\").as[String] #对DataSet进行操作 1val words = lines.flatMap(_.split(\" \")).filter(_ != \"\") #查看DataSet中的内容 12words.collectwords.show #分组求和 12345val counts = words.groupBy(_.toLowerCase).count()--------------------------------------------------------------------------------------------------------------{\"name\": \"UC Berkeley\", \"yearFounded\": 1868, \"numStudents\": 37581}{\"name\": \"MIT\", \"yearFounded\": 1860, \"numStudents\": 11318} #向hdfs中上传数据 1/usr/local/hadoop-2.6.4/bin/hdfs dfs -put schools.json / #定义case class 1case class University(name: String, numStudents: Long, yearFounded: Long) #创建DataSet 1val schools = sqlContext.read.json(\"hdfs://node-1.itcast.cn:9000/schools.json\").as[University] #操作DataSet 1schools.map(sc => s\"${sc.name} is ${2015 - sc.yearFounded} years old\").show #JSON -> DataFrame 12345val df = sqlContext.read.json(\"hdfs://node-1.itcast.cn:9000/person.json\")df.where($\"age\" >= 20).showdf.where(col(\"age\") >= 20).showdf.printSchema #DataFrame -> Dataset 1234567891011121314151617181920212223242526272829303132333435363738case class Person(age: Long, name: String)val ds = df.as[Person]ds.filter(_.age >= 20).show// Dataset -> DataFrameval df2 = ds.toDFimport org.apache.spark.sql.types._df.where($\"age\" > 0).groupBy((($\"age\" / 10) cast IntegerType) * 10 as \"decade\").agg(count(\"*\")).orderBy($\"decade\").show ds.filter(_.age > 0).groupBy(p => (p.age / 10) * 10).agg(count(\"name\")).toDF().withColumnRenamed(\"value\", \"decade\").orderBy(\"decade\") .show val df = sqlContext.read.json(\"hdfs://node-1.itcast.cn:9000/student.json\")case class Student(name: String, age: Long, major: String)val studentDS = df.as[Student]studentDS.select($\"name\".as[String], $\"age\".as[Long]).filter(_._2 > 19).collect()studentDS.groupBy(_.major).count().collect()import org.apache.spark.sql.functions._studentDS.groupBy(_.major).agg(avg($\"age\").as[Double]).collect()case class Major(shortName: String, fullName: String)val majors = Seq(Major(\"CS\", \"Computer Science\"), Major(\"Math\", \"Mathematics\")).toDS()val joined = studentDS.joinWith(majors, $\"major\" === $\"shortName\")joined.map(s => (s._1.name, s._2.fullName)).show()joined.explain()","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"}]},{"title":"SparkSQL使用","slug":"SparkSql的使用","date":"2018-09-05T17:16:31.239Z","updated":"2018-09-05T17:28:42.768Z","comments":true,"path":"2018/09/06/SparkSql的使用/","link":"","permalink":"http://gangtieguo.cn/2018/09/06/SparkSql的使用/","excerpt":"[TOC] //1.读取数据,将每一行的数据使用列分隔符分割 val lineRDD = sc.textFile(“hdfs://bigdata1:9000/person.txt”, 1).map(_.split(“ “))","text":"[TOC] //1.读取数据,将每一行的数据使用列分隔符分割 val lineRDD = sc.textFile(“hdfs://bigdata1:9000/person.txt”, 1).map(_.split(“ “)) //2.定义case class(相当于表的schema) case class Person(id:Int, name:String, age:Int) //3.导入隐式转换,在当前版本中可以不用导入 import sqlContext.implicits._ //4.将lineRDD转换成personRDD val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt)) //5.将personRDD转换成DataFrame val personDF = personRDD.toDF 6.对personDF进行处理 #(SQL风格语法) personDF.registerTempTable(“t_person”) sqlContext.sql(“select * from t_person order by age desc limit 2”).show sqlContext.sql(“desc t_person”).show val result = sqlContext.sql(“select * from t_person order by age desc”) 7.保存结果 result.save(“hdfs://bigdata1:9000/sql/res1”) result.save(“hdfs://bigdata1:9000/sql/res2”, “json”) #以JSON文件格式覆写HDFS上的JSON文件 import org.apache.spark.sql.SaveMode._ result.save(“hdfs://bigdata1:9000/sql/res2”, “json” , Overwrite) 8.重新加载以前的处理结果(可选) sqlContext.load(“hdfs://bigdata1:9000/sql/res1”) sqlContext.load(“hdfs://bigdata1:9000/sql/res2”, “json”)","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"SparkSQL","slug":"SparkSQL","permalink":"http://gangtieguo.cn/tags/SparkSQL/"}]},{"title":"Spark-on-Yarn源码解析(四)Spark业务代码的执行及其任务分配调度stage划分","slug":"Spark-on-Yarn源码解析④Spark业务代码的执行及其任务分配调度stage划分","date":"2018-09-04T08:16:39.931Z","updated":"2019-03-11T07:45:16.244Z","comments":true,"path":"2018/09/04/Spark-on-Yarn源码解析④Spark业务代码的执行及其任务分配调度stage划分/","link":"","permalink":"http://gangtieguo.cn/2018/09/04/Spark-on-Yarn源码解析④Spark业务代码的执行及其任务分配调度stage划分/","excerpt":"spark-on-yarn系列 Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 看看自定义的类 1234567891011121314151617181920object WordCount { def main(args: Array[String]): Unit = { val conf = new SparkConf().setAppName(\"yaoWordCount\").setMaster(\"local[2]\") val sc = new SparkContext(conf) var hadoopRDD: RDD[String] = sc.textFile(args(0)) var hdfsRDD: RDD[String] = hadoopRDD.flatMap(_.split(\"\")) //单词和出现的次数,构建RDD并且调用了他的Transformation //返回的是一个hadoopRDD //transFormation都是返回的RDD var wordAndCount: RDD[(String, Int)] = hdfsRDD.map((_, 1)) //创建RDD 这里面有两个RDD,一个是hadoopRDD,然后会生成一个paritionRDD //savaasTextfile还会产生一个RDD,因为会调用mapPartitons //调用RDD的action 开始真正提交任务 var reducedRDD: RDD[(String, Int)] = wordAndCount.reduceByKey(_ + _) reducedRDD.saveAsTextFile(args(1)) //关闭saprkContext资源 sc.stop() }}","text":"spark-on-yarn系列 Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 看看自定义的类 1234567891011121314151617181920object WordCount { def main(args: Array[String]): Unit = { val conf = new SparkConf().setAppName(\"yaoWordCount\").setMaster(\"local[2]\") val sc = new SparkContext(conf) var hadoopRDD: RDD[String] = sc.textFile(args(0)) var hdfsRDD: RDD[String] = hadoopRDD.flatMap(_.split(\"\")) //单词和出现的次数,构建RDD并且调用了他的Transformation //返回的是一个hadoopRDD //transFormation都是返回的RDD var wordAndCount: RDD[(String, Int)] = hdfsRDD.map((_, 1)) //创建RDD 这里面有两个RDD,一个是hadoopRDD,然后会生成一个paritionRDD //savaasTextfile还会产生一个RDD,因为会调用mapPartitons //调用RDD的action 开始真正提交任务 var reducedRDD: RDD[(String, Int)] = wordAndCount.reduceByKey(_ + _) reducedRDD.saveAsTextFile(args(1)) //关闭saprkContext资源 sc.stop() }} sparkContext的初始化对于Spark程序入口为SparkContext,当我们使用spark-submit/spark-shell等命令来启动一个客户端,客户端与集群需要建立链接,建立的这个链接对象就叫做sparkContext,只有这个对象创建成功才标志这这个客户端与spark集群链接成功。现就将从SparkContext展开来描述一下Spark的任务启动和执行流程。SparkContext 完成了以下几个主要的功能:(1)创建 RDD,通过类似 textFile 等的方法。(2)与资源管理器交互,通过 runJob 等方法启动应用。(3)创建 DAGScheduler、TaskScheduler 等。 在SparkContext类中,SparkContext主构造器主要做 我们看一下SparkContext的主构造器 调用CreateSparkEnv方法创建SparkEnv(将driver的信息,url,ip等都封装),SparkEnv中有一个对象ActorSystem 创建TaskScheduler ,根据提交任务的URL(如:spark://(.*)”,local[1]等,去创建TaskSchedulerImpl ,然后再创建SparkDeploySchedulerBackend(先后创建driverActor和clientActor) 创建DAGScheduler TaskScheduler启动,TaskScheduler.start() SparkEnv最终将driver的host,port端口等各种信息都封装到里面 123456789101112131415161718new SparkEnv( executorId, actorSystem, serializer, closureSerializer, cacheManager, mapOutputTracker, shuffleManager, broadcastManager, blockTransferService, blockManager, securityManager, httpFileServer, sparkFilesDir, metricsSystem, shuffleMemoryManager, outputCommitCoordinator, conf) TaskScheduler在SparkContext类中可以看到,TaskScheduler根据url类型匹配创建TaskSchedulerImpl 1234567891011121314151617181920212223 //TODO 根据提交任务时指定的URL创建相应的TaskScheduler private def createTaskScheduler( sc: SparkContext, master: String): (SchedulerBackend, TaskScheduler) = { ... case \"yarn-standalone\" | \"yarn-cluster\" =>... val scheduler = try { val clazz = Class.forName(\"org.apache.spark.scheduler.cluster.YarnClusterScheduler\") val cons = clazz.getConstructor(classOf[SparkContext]) cons.newInstance(sc).asInstanceOf[TaskSchedulerImpl] } ... val backend = try { val clazz = Class.forName(\"org.apache.spark.scheduler.cluster.YarnClusterSchedulerBackend\") val cons = clazz.getConstructor(classOf[TaskSchedulerImpl], classOf[SparkContext]) cons.newInstance(scheduler, sc).asInstanceOf[CoarseGrainedSchedulerBackend] } scheduler.initialize(backend) (backend, scheduler) .... } 可知TaskScheduler 的实现类org.apache.spark.scheduler.cluster.YarnSchedulerTaskSchedulerBacked 的实现类为org.apache.spark.scheduler.cluster.YarnClientSchedulerBackend且TaskScheduler对TaskSchedulerBacked保持了引用scheduler.initialize(backend) 启动TaskScheduler在Spark的构造函数中,会启动TaskScheduler 1taskScheduler.start() 可以看到继承关系 12private[spark] class YarnClusterScheduler(sc: SparkContext) extends YarnScheduler(sc) private[spark] class YarnScheduler(sc: SparkContext) extends TaskSchedulerImpl(sc) 可以跟踪到,start方法最终调用的是TaskSchedulerImpl里面start方法,在start方法里面 12345 override def start() { //TODO 首先调用SparkDeploySchedulerBackend的start方法 backend.start() ......} ,这里的backend就是YarnClusterSchedulerBackend,而这个最终继承的是CoarseGrainedSchedulerBackend中start方法 12345override def start() {... driverActor = actorSystem.actorOf( Props(new DriverActor(properties)), name = CoarseGrainedSchedulerBackend.ACTOR_NAME)} 获取到spark的配置信息后,会创建driverActor DAGScheduler在SparkContext的构造函数中,会创建DAGScheduler 1dagScheduler= new DAGScheduler(this) 在DAGScheduler构造函数中 1def this(sc: SparkContext) = this(sc, sc.taskScheduler) 可以看到DAGScheduler对TaskScheduler保持了引用 1234567891011class DAGScheduler( private[scheduler] val sc: SparkContext, private[scheduler] val taskScheduler: TaskScheduler, listenerBus: LiveListenerBus, mapOutputTracker: MapOutputTrackerMaster, blockManagerMaster: BlockManagerMaster, env: SparkEnv, clock: Clock = new SystemClock()) extends Logging { ...... } mapOutputTracker:是运行在 Driver 端管理 shuffle 的中间输出位置信息的。 blockManagerMaster:也是运行在 Driver 端的,它是管理整个 Job 的 Bolck 信息。 RDD的构建过程其中hadoopRDD,hdfsRDD,wordRDD,reduceRDD是经过一系列transformation装换rdd,只有等到action时,才会触发数据的流转 该例的action为saveAsTextFile调用链为 123456saveAsTextFile() saveAsHadoopFile() saveAsHadoopFile(重载函数) saveAsHadoopDataset() runJob()之间会调用几个重载函数 dagScheduler.runJob()最终调用 作业提交任务流转首先注意区分 2 个概述:job: 每个 action 都是执行 runJob 方法,可以将之视为一个 job。stage:在这个 job 内部,会根据宽依赖,划分成多个 stage。 在action触发后,最最终调用的是DAGScheduler.runJob() 1dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, resultHandler, localProperties.get) 而runJob() 的核心代码为: 1val waiter = submitJob(rdd, func, partitions, callSite, resultHandler, properties) 即调用 submitJob 方法,我们进一步看看 submitJob() 1234567891011121314151617 def submitJob[T, U]( rdd: RDD[T], func: (TaskContext, Iterator[T]) => U, partitions: Seq[Int], callSite: CallSite, resultHandler: (Int, U) => Unit, properties: Properties): JobWaiter[U] = {.... val jobId = nextJobId.getAndIncrement()..... val waiter = new JobWaiter(this, jobId, partitions.size, resultHandler) eventProcessLoop.post(JobSubmitted( jobId, rdd, func2, partitions.toArray, callSite, waiter, SerializationUtils.clone(properties))) waiter } submitJob() 方法主要完成了以下 3 个工作: 获取一个新的 jobId 生成一个 JobWaiter,它会监听 Job 的执行状态,而 Job 是由多个 Task 组成的,因此只有当 Job 的所有 Task 均已完成,Job 才会标记成功 最后调用 eventProcessLoop.post() 将 Job 提交到一个队列中,等待处理。这是一个典型的生产者消费者模式。这些消息都是通过 handleJobSubmitted 来处理。 简单看一下 handleJobSubmitted 是如何被调用的。首先是 DAGSchedulerEventProcessLoop#onReceive 调用 1234567//TODO 通过模式匹配判断事件的类型 比如任务提交,作业取消 ...override def onReceive(event: DAGSchedulerEvent): Unit = event match { //TODO 提交计算任务 case JobSubmitted(jobId, rdd, func, partitions, allowLocal, callSite, listener, properties) => //todo 调用dagScheduler的handlerJobSubmitted方法处理 dagScheduler.handleJobSubmitted(jobId, rdd, func, partitions, allowLocal, callSite, listener, properties) ... ... DAGSchedulerEventProcessLoop 是 EventLoop 的子类,它重写了 EventLoop 的 onReceive 方法。以后再分析这个 EventLoop。onReceive 会调用 handleJobSubmitted。 stage 的划分刚才说到 handleJobSubmitted 会从 eventProcessLoop 中取出 Job 来进行处理,处理的第一步就是将 Job 划分成不同的 stage。handleJobSubmitted 主要 2 个工作,一是进行 stage 的划分,这是这部分要介绍的内容;二是创建一个 activeJob,并生成一个任务,这在下一小节介绍。 还是先看看调用链 12345handleJobSubmitted ->newStage() ->getParentStages()//此处会遍历RDD所有依赖 ->getShuffleMapStage()//如果是ShuffleDependency(宽依赖,获取到一个Map) ->newOrUsedStage()//这就可以解释我们常说的遇到宽依赖就会划分stage,并且返回stage 所以最终返回的是一个拥有款依赖的 1234567891011121314151617181920private[scheduler] def handleJobSubmitted(jobId: Int, finalRDD: RDD[_], func: (TaskContext, Iterator[_]) => _, partitions: Array[Int], callSite: CallSite, listener: JobListener, properties: Properties) { ... //todo 重要:该方法用于划分stage,主要依赖的是finalStage finalStage = newStage(finalRDD, partitions.size, None, jobId, callSite) ..... //TODO 集群模式 activeJobs += job ...... //todo 提交stage submitStage(finalStage) } //TODO 开始向集群提交还在等待的stage submitWaitingStages()} getParentStages()。因为是从最终的 stage 往回推算的,这需要计算最终 stage 所依赖的各个 stage。 123456789101112131415161718192021222324//TODO 用于获取父stage private def getParentStages(rdd: RDD[_], jobId: Int): List[Stage] = { val parents = new HashSet[Stage] val waitingForVisit = new Stack[RDD[_]] def visit(r: RDD[_]) { if (!visited(r)) { visited + r for (dep <- r.dependencies) { dep match { case shufDep: ShuffleDependency[_, _, _] => //TODO 把宽依赖传进去,获得父stage parents += getShuffleMapStage(shufDep, jobId) case _ => waitingForVisit.push(dep.rdd) } } } } waitingForVisit.push(rdd) while (!waitingForVisit.isEmpty) { visit(waitingForVisit.pop()) } parents.toList } 任务的生成回到 handleJobSubmitted 中的代码: 1submitStage(finalStage) submitStage 会提交 finalStage,如果这个 stage 的某些 parentStage 未提交,则递归调用 submitStage(),直至所有的 stage 均已计算完成。 submitStage() 会调用 submitMissingTasks(): submitMissingTasks(stage, jobId.get) 而 submitMissingTasks() 会完成 DAGScheduler 最后的工作:它判断出哪些 Partition 需要计算,为每个 Partition 生成 Task,然后这些 Task 就会封闭到 TaskSet 12345678910111213141516171819202122232425//TODO DAG提交stage 根据最后一个stage 开始找到第一个stage递归提交stage /** Submits stage, but first recursively submits any missing parents. */ private def submitStage(stage: Stage) { val jobId = activeJobForStage(stage) if (jobId.isDefined) { if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) { //TODO 获取他的父stage 没有提交的stage val missing = getMissingParentStages(stage).sortBy(_.id) //todo 判断父stage是否为空,为空就以为着他是第一stage if (missing == Nil) { //TODO 开始提交最前面的stage, DAG提交stage给TaskScheduler 会将stage转换成taskSet submitMissingTasks(stage, jobId.get) } else { //TODO 有父stage 就递归提交 for (parent <- missing) { submitStage(parent) } waitingStages += stage } } } else { abortStage(stage, \"No active job for stage \" + stage.id) } } submitMissingTasks在最后提交给 TaskScheduler 进行处理 123456789101112131415161718192021222324252627282930313233343536 //TODO DAG提交stage给TaskScheduler 会将stage转换成taskSet private def submitMissingTasks(stage: Stage, jobId: Int) {...//TODO 创建多少个Task val tasks: Seq[Task[_]] = if (stage.isShuffleMap) { partitionsToCompute.map { id => //TODO 数据存储的最佳位置 移动计算,而不是移动数据 val locs = getPreferredLocs(stage.rdd, id) val part = stage.rdd.partitions(id) //TODO 从上游拉取数据 new ShuffleMapTask(stage.id, taskBinary, part, locs) } } else { val job = stage.resultOfJob.get partitionsToCompute.map { id => val p: Int = job.partitions(id) val part = stage.rdd.partitions(p) val locs = getPreferredLocs(stage.rdd, p) //TODO 将数据写入某个介质里面,nosql hdfs 等等 new ResultTask(stage.id, taskBinary, part, locs, id) } }//TODO task的数量最好和分区数一样 如果分区数大于0 //TODO task的数量最好和分区数一样 如果分区数大于0 if (tasks.size > 0) { logInfo(\"Submitting \" + tasks.size + \" missing tasks from \" + stage + \" (\" + stage.rdd + \")\") stage.pendingTasks ++= tasks //TODO 调用taskScheduler的submitTasks提交taskSet 现在将task转换成一个arraytaskScheduler.submitTasks(new TaskSet( tasks.toArray, stage.id, stage.latestInfo.attemptId, stage.firstJobId, properties)) stage.latestInfo.submissionTime = Some(clock.getTimeMillis()) .....} TaskScheduler && TaskSchedulerBackend上文分析到在 DAGScheduler 中最终会执行 taskScheduler.submitTasks() 方法,我们先简单看一下从这里开始往下的执行逻辑: 12345678①taskScheduler.submitTasks() ->②schedulableBuilder.addTaskSetManager() 调度模式,是先来先服务还是公平调度模式 ->③CoarseGrainedSchedulerBackend.reviveOffers() 这个是向driverActor发送消息driverActor ! ReviveOffers ->④CoarseGrainedSchedulerBackend.receiveWithLogging 这是driverActor接收消息的部分 ->⑤CoarseGrainedSchedulerBackend.makeOffers() //case ReviveOffers =>makeOffers() 这个模式匹配会调用maksOffers方法 ->⑥launchTasks()调用launchTask向Executor提交task ->⑦ executorData.executorActor ! LaunchTask(new SerializableBuffer(serializedTask))向executor发送序列化好的task,发送一个Task 步骤一、二中主要将这组任务的 TaskSet 加入到一个 TaskSetManager 中。TaskSetManager 会根据数据就近原则为 task 分配计算资源,监控 task 的执行状态等,比如失败重试,推测执行等。步骤三、四逻辑较为简单。步骤五为每个 task 具体分配资源,它的输入是一个 Executor 的列表,输出是 TaskDescription 的二维数组。TaskDescription 包含了 TaskID, Executor ID 和 task 执行的依赖信息等。步骤六、七就是将任务真正的发送到 executor 中执行了,并等待 executor 的状态返回。 ","categories":[{"name":"Spark-On-Yarn","slug":"Spark-On-Yarn","permalink":"http://gangtieguo.cn/categories/Spark-On-Yarn/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"}]},{"title":"Spark-on-Yarn源码解析(三)client做的事情","slug":"Spark-on-Yarn源码解析③client做的事情","date":"2018-09-04T08:13:34.283Z","updated":"2019-03-11T07:44:55.663Z","comments":true,"path":"2018/09/04/Spark-on-Yarn源码解析③client做的事情/","link":"","permalink":"http://gangtieguo.cn/2018/09/04/Spark-on-Yarn源码解析③client做的事情/","excerpt":"[TOC] spark-on-yarn系列 Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 org.apache.spark.deploy.yarn.Client 话不多说,先上源码,当然还是简洁版本的 这儿我先上一下最简洁的调用链。 Client.main() ->new Client().run() ->monitorApplication(submitApplication()) ->submitApplication() ->createContainerLaunchContext()会封装一些启动信息如我们启动的类 --class ->userClass ->amArgs ->commands ->printableCommands ->amClass applicationMaster启动的真实类 ->createApplicationSubmissionContext() ->Records.newRecord(classOf[Resource])启动 ->yarnClientImpl.submitApplication(appContext)","text":"[TOC] spark-on-yarn系列 Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 org.apache.spark.deploy.yarn.Client 话不多说,先上源码,当然还是简洁版本的 这儿我先上一下最简洁的调用链。 Client.main() ->new Client().run() ->monitorApplication(submitApplication()) ->submitApplication() ->createContainerLaunchContext()会封装一些启动信息如我们启动的类 --class ->userClass ->amArgs ->commands ->printableCommands ->amClass applicationMaster启动的真实类 ->createApplicationSubmissionContext() ->Records.newRecord(classOf[Resource])启动 ->yarnClientImpl.submitApplication(appContext) 最终是调用的client里面main方法->run-> monitorApplication(submitApplication()) object Client extends Logging { def main(argStrings: Array[String]) { ... ... val sparkConf = new SparkConf val args = new ClientArguments(argStrings, sparkConf) new Client(args, sparkConf).run() ... ... } } ... ... def run(): Unit = { val (yarnApplicationState, finalApplicationStatus) = monitorApplication(submitApplication()) } ... ... def submitApplication(): ApplicationId = { // TODO: 初始化并且启动client yarnClient.init(yarnConf) yarnClient.start() // TODO: 准备提交请求到resouceManager val newApp = yarnClient.createApplication() val newAppResponse = newApp.getNewApplicationResponse() val appId = newAppResponse.getApplicationId() // TODO: 检查集群的内存是否满足当前的任务要求 verifyClusterResources(newAppResponse) // TODO: 设置适当上下文环境来启动applicationMaster val containerContext = createContainerLaunchContext(newAppResponse) val appContext = createApplicationSubmissionContext(newApp, containerContext) // TODO: 提交application yarnClient.submitApplication(appContext) appId } private def createContainerLaunchContext(newAppResponse: GetNewApplicationResponse) : ContainerLaunchContext = { ... ... val userClass = if (isClusterMode) { Seq("--class", YarnSparkHadoopUtil.escapeForShell(args.userClass)) } else { Nil } ... val amClass = if (isClusterMode) { Class.forName("org.apache.spark.deploy.yarn.ApplicationMaster").getName } else { Class.forName("org.apache.spark.deploy.yarn.ExecutorLauncher").getName } val amArgs = Seq(amClass) ++ userClass ++ userJar ++ primaryPyFile ++ pyFiles ++ userArgs ++ Seq( "--executor-memory", args.executorMemory.toString + "m", "--executor-cores", args.executorCores.toString, "--num-executors ", args.numExecutors.toString) val commands = prefixEnv ++ Seq(YarnSparkHadoopUtil.expandEnvironment(Environment.JAVA_HOME) + "/bin/java", "-server" ) ++ javaOpts ++ amArgs ++ ... ... val printableCommands = commands.map(s => if (s == null) "null" else s).toList amContainer.setCommands(printableCommands) } ... ... def createApplicationSubmissionContext( newApp: YarnClientApplication, containerContext: ContainerLaunchContext): ApplicationSubmissionContext = { val appContext = newApp.getApplicationSubmissionContext appContext.setApplicationName(args.appName) appContext.setQueue(args.amQueue) appContext.setAMContainerSpec(containerContext) appContext.setApplicationType("SPARK") sparkConf.getOption("spark.yarn.maxAppAttempts").map(_.toInt) match { case Some(v) => appContext.setMaxAppAttempts(v) case None => logDebug("spark.yarn.maxAppAttempts is not set. " + "Cluster's default value will be used.") } val capability = Records.newRecord(classOf[Resource]) capability.setMemory(args.amMemory + amMemoryOverhead) capability.setVirtualCores(args.amCores) appContext.setResource(capability) appContext } //yarnClient.submitApplication(appContext)提交的真实处 @Override public ApplicationId submitApplication(ApplicationSubmissionContext appContext) throws YarnException, IOException { ... //此处通过yarn的协议对applicationMaster进行提交和启动 (此处为个人理解有疑惑,如有错误,还望留言分享,会立即作出更正) SubmitApplicationRequest request = Records.newRecord(SubmitApplicationRequest.class); request.setApplicationSubmissionContext(appContext); ... 此处client的事情都已经做完了,请摄影师将镜头切换到applicationMaster 小细节用户业务代码信息的封装及流转 我们提交的class的封装流程 ->sublimit的prepareSubmitEnvironment中封装到childArgs中--class ->传入到client的构造函数里面作为clientArgs,将其封装到userClass属性里面 在submitApplication中createContainerLaunchContext会将其通过重新封到userClass userClass->amArgs->commands->printableCommands ->amContainer.setCommands(printableCommands) 在此,createContainerLaunchContext方法接收到amContainer赋名为containerContext传递给createApplicationSubmissionContext(..,containerContext) 那么在createApplicationSubmissionContext中又有哪些惊天变化(其实并没有) appContext.setAMContainerSpec(containerContext) 那么appContext作为createApplicationSubmissionContext方法返回值,由appContext接收,看码 appContext = createApplicationSubmissionContext(newApp, containerContext) 最后,由yarnClientImpl提交 yarnClient.submitApplication(appContext) 码又来了,最终执行的是 SubmitApplicationRequest request =Records.newRecord(SubmitApplicationRequest.class); 启动applicationMaster 对于client的封装,对于applicationMaster需要启动的信息(如资源信息)及用户提交的业务代码(wordcount的类信息)信息都已经封装到appContext,并且传递到applicationmaster,那么来看看applicationMaster的执行流程。 程序调用结构 ApplicationMaster.main() ->run() ->runDriver() ->run() ->startUserApplication() //启动userClass ->userClassLoader.loadClass(args.userClass) .getMethod("main", classOf[Array[String]]) ->mainMethod.invoke(null, mainArgs) runAMActor() registerAM() ->yarnRmClient.register()->return new YarnAllocater(......) ->yarnAllocator.allocateResources() ->yarnAllocator.handleAllocatedContainers() //启动executor ->yarnAllocator.runAllocatedContainers(containersToUse) runAllocatedContainers(containersToUse)是去启动 executor,最终真正执行启动Container的是在 ExecutorRunnable.run()中。 创建了 NMClient 客户端调用提供的 API 最终实现在 NM 上启动 Container,具体如何启动 Container 将在后文中进行介绍。 launcherPool线程池会将container,driver等相关信息封装成ExecutorRunnable对象,通过ExecutorRunnable启动新的container以运行executor。在此过程中,指定启动executor的类是 org.apache.spark.executor.CoarseGrainedExecutorBackend。spark yarn cluster 模式下任务提交和计算流程分析 程序的细节 def main(args: Array[String]) = { SignalLogger.register(log) val amArgs = new ApplicationMasterArguments(args) SparkHadoopUtil.get.runAsSparkUser { () => master = new ApplicationMaster(amArgs, new YarnRMClient(amArgs)) System.exit(master.run()) } } ...... final def run(): Int = { .... if (isClusterMode) { runDriver(securityMgr) } else { runExecutorLauncher(securityMgr) } ... } private def runDriver(securityMgr: SecurityManager): Unit = { addAmIpFilter() // TODO: 启动我们自定的类,也就是启动submit里面的--class的东西 userClassThread = startUserApplication() val sc = waitForSparkContextInitialized() ... actorSystem = sc.env.actorSystem runAMActor( sc.getConf.get("spark.driver.host"), sc.getConf.get("spark.driver.port"), isClusterMode = true) registerAM(sc.ui.map(_.appUIAddress).getOrElse(""), securityMgr) userClassThread.join() ... } 在ApplicationMasterArguments设置了要启动的信息 class ApplicationMasterArguments(val args: Array[String]) { var userJar: String = null var userClass: String = null var primaryPyFile: String = null var pyFiles: String = null var userArgs: Seq[String] = Seq[String]() var executorMemory = 1024 var executorCores = 1 var numExecutors = DEFAULT_NUMBER_EXECUTORS ...... } startUserApplication 主要执行了调用用户的代码,以及创建了一个 spark driver 的进程。 Start the user class, which contains the spark driver, in a separate Thread. private def startUserApplication(): Thread = { val classpath = Client.getUserClasspath(sparkConf) val urls = classpath.map { entry => new URL("file:" + new File(entry.getPath()).getAbsolutePath()) } val userClassLoader = ... // TODO: userClass就是submit里面的--class 提交的类 val mainMethod = userClassLoader.loadClass(args.userClass) .getMethod("main", classOf[Array[String]]) userThread.setContextClassLoader(userClassLoader) userThread.setName("Driver") userThread.start() userThread } 从userThread.setName(“Driver”)也可以看出创建的是名为driver的进程 registerAM 向 resourceManager 中正式注册 applicationMaster。注册applicationMaster 以后,并且分配资源,这样,用户代码就可以执行了,任务切分、调度、执行。 然后,用户代码中的 action 会调用 SparkContext 的 runJob,SparkContext 中有很多个 runJob,但最后都是调用 DAGScheduler 的 runJob // registerAM private def registerAM(uiAddress: String, securityMgr: SecurityManager) = { ..... allocator = client.register(yarnConf, if (sc != null) sc.getConf else sparkConf, if (sc != null) sc.preferredNodeLocationData else Map(), uiAddress, historyAddress, securityMgr) //为exector分配资源 allocator.allocateResources() reporterThread = launchReporterThread() ...... }","categories":[{"name":"Spark-On-Yarn","slug":"Spark-On-Yarn","permalink":"http://gangtieguo.cn/categories/Spark-On-Yarn/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"}]},{"title":"Spark-on-Yarn源码解析(二)Spark-Submit解析","slug":"Spark-on-Yarn源码解析②Spark-Submit解析","date":"2018-09-04T08:03:50.700Z","updated":"2019-03-11T07:44:48.567Z","comments":true,"path":"2018/09/04/Spark-on-Yarn源码解析②Spark-Submit解析/","link":"","permalink":"http://gangtieguo.cn/2018/09/04/Spark-on-Yarn源码解析②Spark-Submit解析/","excerpt":"[TOC] spark-on-yarn系列 Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 上文我们了解到了yarn的架构和执行任务的流程,接下来我们看看 spark-submit命令$SPARK_HOME/bin/spark-submit \\ --master yarn \\ //提交模式 yarn --deploy-mode cluster \\ //运行的模式,还有一种client模式,但大多用于调试,此处使用cluster模式 --class me.yao.spark.me.yao.spark.WordCount \\ //提交的任务 --name "wc" \\ //任务名字 --queue root.default \\ //提交的队列 --driver-memory 3g \\ //为driver申请的内存 --num-executors 1 \\ //executors的数量,可以理解为线程数,对应yarn中的Container个数 --executor-memory 6g \\ //为每一个executor申请的内存 --executor-cores 4 \\ //为每一个executor申请的core --conf spark.yarn.driver.memoryOverhead=1g \\ //driver可使用的非堆内存,这些内存用于如VM,字符 串常量池以及其他额外本地开销等 --conf spark.yarn.executor.memoryOverhead=2g \\ //每个executor可使用的非堆内存,这些内存用于如 VM,字符串常量池以及其他额外本地开销等 这是通常我们提交spark程序的submit命令,以此为切入点,对spark程序的运行流程做一个跟踪和分析。","text":"[TOC] spark-on-yarn系列 Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 上文我们了解到了yarn的架构和执行任务的流程,接下来我们看看 spark-submit命令$SPARK_HOME/bin/spark-submit \\ --master yarn \\ //提交模式 yarn --deploy-mode cluster \\ //运行的模式,还有一种client模式,但大多用于调试,此处使用cluster模式 --class me.yao.spark.me.yao.spark.WordCount \\ //提交的任务 --name "wc" \\ //任务名字 --queue root.default \\ //提交的队列 --driver-memory 3g \\ //为driver申请的内存 --num-executors 1 \\ //executors的数量,可以理解为线程数,对应yarn中的Container个数 --executor-memory 6g \\ //为每一个executor申请的内存 --executor-cores 4 \\ //为每一个executor申请的core --conf spark.yarn.driver.memoryOverhead=1g \\ //driver可使用的非堆内存,这些内存用于如VM,字符 串常量池以及其他额外本地开销等 --conf spark.yarn.executor.memoryOverhead=2g \\ //每个executor可使用的非堆内存,这些内存用于如 VM,字符串常量池以及其他额外本地开销等 这是通常我们提交spark程序的submit命令,以此为切入点,对spark程序的运行流程做一个跟踪和分析。查看spark-submit脚本 查看spark-submit脚本的信息,初步可以看到submit启动的类为org.apache.spark.deploy.SparkSubmit,更多细节其实不重要(开个开玩,极客可以求甚解)如果觉得要深究一下为什么是submit的main方法的可以参考一下spark on yarn 作业提交源码分析 接下来查看该类内部的处理逻辑 SparkSumbmit的类(为了简洁和文章篇幅,只保留了关键流程的信息) def main(args: Array[String]): Unit = { val appArgs = new SparkSubmitArguments(args) if (appArgs.verbose) { printStream.println(appArgs) } appArgs.action match { case SparkSubmitAction.SUBMIT => submit(appArgs) case SparkSubmitAction.KILL => kill(appArgs) case SparkSubmitAction.REQUEST_STATUS => requestStatus(appArgs) } } ...... private[spark] def submit(args: SparkSubmitArguments): Unit = { val (childArgs, childClasspath, sysProps, childMainClass) = prepareSubmitEnvironment(args) ..... ..... runMain(childArgs, childClasspath, sysProps, childMainClass, args.verbose) } private[spark] def prepareSubmitEnvironment(args: SparkSubmitArguments) : (Seq[String], Seq[String], Map[String, String], String) = { ...... // In yarn-cluster mode, use yarn.Client as a wrapper around the user class if (isYarnCluster) { childMainClass = "org.apache.spark.deploy.yarn.Client" ....... } //在submit方法中最终调用的是 runMain(childArgs, childClasspath, sysProps, childMainClass, args.verbose) try { mainClass = Class.forName(childMainClass, true, loader) } catch { ...... System.exit(CLASS_NOT_FOUND_EXIT_STATUS) } // SPARK-4170 private def runMain( childArgs: Seq[String], childClasspath: Seq[String], sysProps: Map[String, String], childMainClass: String, verbose: Boolean): Unit = { ... ... mainClass = Class.forName(childMainClass, true, loader) ... ... val mainMethod = mainClass.getMethod("main", new Array[String](0).getClass) ... ... mainMethod.invoke(null, childArgs.toArray) ... ... } 通过上面的流程可以看到,这样一个调用链(未特殊表明类名,表明为该步上一步的同一类),我们代码简化一下,看得舒心明了,再配上解说 submit.main() ->submit()模式匹配到该方法,因为我们就是submit提交任务 ->prepareSubmitEnvironment()该方法中指明了要启动的类,就是大明湖畔的Client ->runMain()通过上步指定的类,然后通过反射调用main方法 既然我们的线路走到org.apache.spark.deploy.yarn.Client ,那我们再去这个类一看究竟,且听下回分解","categories":[{"name":"Spark-On-Yarn","slug":"Spark-On-Yarn","permalink":"http://gangtieguo.cn/categories/Spark-On-Yarn/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"}]},{"title":"Spark-on-Yarn源码解析(一)Yarn任务解析","slug":"Spark-on-Yarn源码解析①Yarn任务解析","date":"2018-09-04T07:44:58.661Z","updated":"2019-06-17T04:40:09.397Z","comments":true,"path":"2018/09/04/Spark-on-Yarn源码解析①Yarn任务解析/","link":"","permalink":"http://gangtieguo.cn/2018/09/04/Spark-on-Yarn源码解析①Yarn任务解析/","excerpt":"[TOC] spark-on-yarn系列Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 了解spark-on-yarn,首先我们了解一下yarn提交的流程,俗话说,欲练此功,错了,我们还是先看吧 yarn任务的提交YARN 的基本架构和工作流程 YARN 的基本架构如上图所示,由三大功能模块组成,分别是 1) RM (ResourceManager) 2) NM (Node Manager) 3) AM(Application Master)","text":"[TOC] spark-on-yarn系列Spark-on-Yarn 源码解析①Yarn 任务解析Spark-on-Yarn 源码解析②Spark-Submit 解析Spark-on-Yarn 源码解析③client 做的事情Spark-on-Yarn 源码解析④Spark 业务代码的执行及其任务分配调度 stage 划分 了解spark-on-yarn,首先我们了解一下yarn提交的流程,俗话说,欲练此功,错了,我们还是先看吧 yarn任务的提交YARN 的基本架构和工作流程 YARN 的基本架构如上图所示,由三大功能模块组成,分别是 1) RM (ResourceManager) 2) NM (Node Manager) 3) AM(Application Master)作业提交 用户通过 Client 向 ResourceManager 提交 Application, ResourceManager 根据用户请求分配合适的 Container, 然后在指定的 NodeManager 上运行 Container 以启动 ApplicationMaster ApplicationMaster 启动完成后,向 ResourceManager 注册自己 对于用户的 Task,ApplicationMaster 需要首先跟 ResourceManager 进行协商以获取运行用户 Task 所需要的 Container,在获取成功后,ApplicationMaster 将任务发送给指定的 NodeManager NodeManager 启动相应的 Container,并运行用户 Task Spark-On-Yarn的流程提交在 yarn-cluster 模式下,Spark driver 运行在 application master 进程中,这个进程被集群中的 YARN 所管理,客户端会在初始化应用程序 之后关闭。在 yarn-client 模式下,driver 运行在客户端进程中,application master 仅仅用来向 YARN 请求资源 Spark Driver首先作为一个ApplicationMaster在YARN集群中启动,客户端提交给ResourceManager的时候,每一个job都会在集群的NodeManager节点上分配一个唯一的ApplicationMaster,由该ApplicationMaster管理全生命周期的应用。具体过程: 由client向ResourceManager提交请求,并上传jar到HDFS上这期间包括四个步骤:a).连接到RMb).从RM的ASM(ApplicationsManager )中获得metric、queue和resource等信息。c). upload app jar and spark-assembly jard).设置运行环境和container上下文(launch-container.sh等脚本) ResouceManager向NodeManager申请资源,创建Spark ApplicationMaster(每个SparkContext都有一个ApplicationMaster) NodeManager启动ApplicationMaster,并向ResourceManager AsM注册 ApplicationMaster从HDFS中找到jar文件,启动SparkContext、DAGscheduler和YARN Cluster Scheduler ResourceManager向ResourceManager AsM注册申请container资源 ResourceManager通知NodeManager分配Container,这时可以收到来自ASM关于container的报告。(每个container对应一个executor) Spark ApplicationMaster直接和container(executor)进行交互,完成这个分布式任务。 ApplicationMaster和Driver的区别首先区分下 AppMaster 和 Driver,任何一个 yarn 上运行的任务都必须有一个 AppMaster,而任何一个 Spark 任务都会有一个 Driver,Driver 就是运行 SparkContext(它会构建 TaskScheduler 和 DAGScheduler) 的进程,当然在 Driver 上你也可以做很多非 Spark 的事情,这些事情只会在 Driver 上面执行,而由 SparkContext 上牵引出来的代码则会由 DAGScheduler 分析,并形成 Job 和 Stage 交由 TaskScheduler,再由 TaskScheduler 交由各 Executor 分布式执行。 所以 Driver 和 AppMaster 是两个完全不同的东西,Driver 是控制 Spark 计算和任务资源的,而 AppMaster 是控制 yarn app 运行和任务资源的,只不过在 Spark on Yarn 上,这两者就出现了交叉,而在 standalone 模式下,资源则由 Driver 管理。在 Spark on Yarn 上,Driver 会和 AppMaster 通信,资源的申请由 AppMaster 来完成,而任务的调度和执行则由 Driver 完成,Driver 会通过与 AppMaster 通信来让 Executor 的执行具体的任务。 Spark on Yarn","categories":[{"name":"Spark-On-Yarn","slug":"Spark-On-Yarn","permalink":"http://gangtieguo.cn/categories/Spark-On-Yarn/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"Yarn","slug":"Yarn","permalink":"http://gangtieguo.cn/tags/Yarn/"}]},{"title":"MapReduce中Shuffle中的机制","slug":"MapReduce中Shuffle中的机制","date":"2018-08-20T02:12:26.791Z","updated":"2019-06-17T04:40:09.393Z","comments":true,"path":"2018/08/20/MapReduce中Shuffle中的机制/","link":"","permalink":"http://gangtieguo.cn/2018/08/20/MapReduce中Shuffle中的机制/","excerpt":"[TOC] 官方的shuffle流程 shuffle原理提到MapReduce,就不得不提一下shuffle。 MapReduce 框架的核心步骤主要分两部分:Map 和Reduce,一个是独立并发,一个是汇聚。当你向MapReduce 框架提交一个计算作业时,它会首先把计算作业拆分成若干个Map 任务,然后分配到不同的节点上去执行,每一个Map 任务处理输入数据中的一部分,当Map 任务完成后,它会生成一些中间文件,这些中间文件将会作为Reduce 任务的输入数据。Reduce 任务的主要目标就是把前面若干个Map 的输出汇总到一起并输出。","text":"[TOC] 官方的shuffle流程 shuffle原理提到MapReduce,就不得不提一下shuffle。 MapReduce 框架的核心步骤主要分两部分:Map 和Reduce,一个是独立并发,一个是汇聚。当你向MapReduce 框架提交一个计算作业时,它会首先把计算作业拆分成若干个Map 任务,然后分配到不同的节点上去执行,每一个Map 任务处理输入数据中的一部分,当Map 任务完成后,它会生成一些中间文件,这些中间文件将会作为Reduce 任务的输入数据。Reduce 任务的主要目标就是把前面若干个Map 的输出汇总到一起并输出。 我们知道每个reduce task输入的key都是按照key排序的。但是每个map的输出只是简单的key-value而非key-valuelist,所以shuffle的工作就是将map输出转化为reducer的输入的过程。Shuffle过程是指map产生输出结果开始,包括系统执行分区partition,排序sort,聚合Combiner(如有)以及传送map的输出到Reducer作为输入的过程。 shuffle过程分析 map端 从map段开始分析,当Map开始产生输出的时候,并不是简单吧数据写到磁盘,因为频繁的操作会导致性能严重下降,首先将数据写入到一个环形缓冲区(每个maptask都会有一个环形缓冲区,默认100M,可以通过io.sort.mb属性来设置具体的大小)并做一些预排序,以提升效率,当缓冲区中的数据量达到一个特定的阀值(io.sort.mb * io.sort.spill.percent,其中io.sort.spill.percent 默认是0.80,即默认为 80MB),溢写线程启动。 系统将会启动一个后台线程把缓冲区中的内容spill 到磁盘。即会锁定这80MB的内存,执行溢写过程。Map task的输出结果还可以往剩下的20MB内存中写,互不影响。在spill过程中,Map的输出将会继续写入到缓冲区,但如果缓冲区已经满了,Map就会被阻塞直到spill完成。spill线程在把缓冲区的数据写到磁盘前,会对他 进行一个二次排序,首先根据数据所属的partition排序(快速排序),然后每个partition中再按Key排序。输出包括一个索引文件和数据文件。 如果设定了Combiner,将在排序输出的基础上进行。Combiner就是一个Mini Reducer,它在执行Map任务的节点本身运行,先对Map的输出作一次简单的Reduce,有些数据可能像这样:“a”/1, “a”/1, “a”/1,会合并成 “a”/3,使得更少的数据会被写入磁盘和传送到Reducer。 Spill文件保存在由mapred.local.dir指定的目录中,Map任务结束后删除。每当内存中的数据达到spill阀值的时候,都会产生一个新的spill文件,所以在Map任务写完他的最后一个输出记录的时候,可能会有多个spill文件,在Map任务完成前,所有的spill文件将会被归并排序为一个索引文件和数据文件。这是一个多路归并过程,最大归并路数由io.sort.factor 控制(默认是10)。比如:“a”从某个map task读取过来时值是7,从另外一个map 读取时值是6,因为它们有相同的key,所以得merge成group。什么是group。对于“a”就是像这样的:{“a”, [7, 6, 2, …]},数组中的值就是从不同溢写文件中读取出来的,然后再把这些值加起来。请注意,因为merge是将多个溢写文件合并到一个文件,所以可能也有相同的key存在。如果设定了Combiner,会使用combiner来合并相同key,这是map端的结果。 map端与reduce端的交互Reduce是怎么知道从哪些TaskTrackers中获取Map的输出呢?当Map任务完成之后,会通知他们的父TaskTracker,告知状态更新,然后TaskTracker再转告JobTracker,这些通知信息是通过心跳通信机制传输的,因此针对以一个特定的作业,jobtracker知道Map输出与tasktrackers的映射关系。Reducer中有一个线程会间歇的向JobTracker询问Map输出的地址,直到把所有的数据都取到。在Reducer取走了Map输出之后,TaskTracker不会立即删除这些数据,因为Reducer可能会失败,他们会在整个作业完成之后,JobTracker告知他们要删除的时候才去删除 map端的所有工作结束后,最终生成的这个文件也存放在TaskTracker够得着的某个本地目录内。每个reduce task不断地通过RPC从JobTracker那里获取map task是否完成的信息,如果reduce task得到通知,获知某台TaskTracker上的map task执行完成,Shuffle的后半段过程开始启动。简单地说,reduce task在执行之前的工作就是不断地拉取当前job里每个map task的最终结果,然后对从不同地方拉取过来的数据不断地做merge,也最终形成一个文件作为reduce task的输入文件。 reduce端过程 Copy过程,简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求map task所在的TaskTracker获取map task的输出文件。因为map task早已结束,这些文件就归TaskTracker管理在本地磁盘中。 Merge阶段。这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活,它基于JVM的heap size设置,因为Shuffle阶段Reducer不运行,所以应该把绝大部分的内存都给Shuffle用。这里需要强调的是,merge有三种形式:1)内存到内存 (默认不启用) 2)内存到磁盘 3)磁盘到磁盘。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。 Reducer的输入文件。不断地merge后,最后会生成一个文件。这个文件可能存在于磁盘上,也可能存在于内存中。就读取速度来说对于内存中,直接作为Reducer的输入,但默认情况下,这个文件是存放于磁盘中的。当Reducer的输入文件已定,整个Shuffle才最终结束。然后就是Reducer执行,一般是把结果放到HDFS上。 Speculative Execution是指当一个job的所有task都在running的时候,当某个task的进度比平均进度慢时才会启动一个和当前Task一模一样的任务,当其中一个task完成之后另外一个会被中止,所以Speculative Task不是重复Task而是对Task执行时候的一种优化策略 任务分片与hdfs文件大小及文件块之间的关系在上面mapReduce中可以看到,任务的执行是基于hdfs文件的,任务分片和文件大小,文件块大小都有一定的联系。 任务切片:将任务划分成切片,一个切片交给一个task实例处理,只是一个逻辑的偏移量划分而已 1.在map task执行时,它的输入数据来源于HDFS的block,当然在MapReduce概念中,map task只读取split。Split与block的对应关系可能是多对一,默认是一对一。 采用的算法是: 分片大小范围可以在 mapred-site.xml 中设置,mapred.min.split.size mapred.max.split.size,minSplitSize 大小默认为 1B,maxSplitSize 大小默认为 Long.MAX_VALUE = 9223372036854775807 123miniSize = 1maxSize = Long.MAXVALUEsplitSize = Math.max(miniSize,Math.min(maxSize,blockSize)) 默认情况下,任务分片的大小为hdfs的blocksize 也就是块大小 所以在我们没有设置分片的范围的时候,分片大小是由 block 块大小决定的,和它的大小一样。比如把一个 258MB 的文件上传到 HDFS 上,假设 block 块大小是 128MB,那么它就会被分成三个 block 块,与之对应产生三个 split,所以最终会产生三个 map task。我又发现了另一个问题,第三个 block 块里存的文件大小只有 2MB,而它的 block 块大小是 128MB,那它实际占用 Linux file system 的多大空间?** 答案是实际的文件大小,而非一个块的大小 切片的流程 遍历输入目录下的文件,得到文件集合 list 遍历文件集合list,循环操作集合下的文件 获取文件的blocksize 文件块,获取文件的长度,得到切片信息(split[文件路径,切片编号,偏移量范围]),将各切片对象放入到一个splitList里面 遍历完成后,将切片信息splitList序列化到一个split描述文件中 默认情况下,默认的TextInputFormat对任务切片是按文件规划切片,不管文件多小,都会是一个单独的切片,这样如果有大量小文件,就会产生大量的maptask,处理效率极其低下 对于以上原理,如果读取的是很多小文件,会产生大量的小切片,造成大量的maptask运行,对应的解决方法: 将小文件合并之后再上传到hdfs 如果小文件已经上传了,可以写MapReduce程序将小文件合并 可以用另一种InputFormat:CombineInputFormat(它可以将多个文件划分到一个切片中)","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://gangtieguo.cn/tags/Hadoop/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"}]},{"title":"SparkSQL介绍","slug":"SparkSQL介绍","date":"2018-08-15T18:04:32.317Z","updated":"2018-08-20T01:38:13.848Z","comments":true,"path":"2018/08/16/SparkSQL介绍/","link":"","permalink":"http://gangtieguo.cn/2018/08/16/SparkSQL介绍/","excerpt":"[TOC] Hive,它是将Hive SQL转换成MapReduce然后提交到集群上执行,大大简化了编写MapReduce的程序的复杂性,由于MapReduce这种计算模型执行效率比较慢。所有Spark SQL的应运而生,它是将Spark SQL转换成RDD,然后提交到集群执行,执行效率非常快!","text":"[TOC] Hive,它是将Hive SQL转换成MapReduce然后提交到集群上执行,大大简化了编写MapReduce的程序的复杂性,由于MapReduce这种计算模型执行效率比较慢。所有Spark SQL的应运而生,它是将Spark SQL转换成RDD,然后提交到集群执行,执行效率非常快! 需要将hive-site.xml拷到spark的配置文件夹 hive只认latin1编码linux下的mysql编码是latin1windows下的也设置成latin1.如果要和hive搭配使用的话 进入sparksql和hive连接的命令1/home/bigdata/apps/spark/bin/spark-sql --master spark://bigdata1:7077 --driver-class-path /home/bigdata/apps/hive/lib/mysql-connector-java-5.1.31-bin.jar 需要注意的是,需要是集群模式,–master 等等,还要指定一个jdbc的连接驱动sparksql也会走hive的元数据库 hive语法在spark-sql下 1'>create table person(id bigint,name string,age int) row format delimited fields terminated by ','; 在hive下: 1load data inpath \"hdfs://master:9000/person.txt\" into table person;","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"SparkSQL","slug":"SparkSQL","permalink":"http://gangtieguo.cn/tags/SparkSQL/"}]},{"title":"Spark-On-Yarn模式","slug":"Spark-On-yarn","date":"2018-08-15T17:58:01.513Z","updated":"2019-06-17T04:40:09.396Z","comments":true,"path":"2018/08/16/Spark-On-yarn/","link":"","permalink":"http://gangtieguo.cn/2018/08/16/Spark-On-yarn/","excerpt":"[TOC] SparkOnYarn两种模式区别cluster模式:Driver程序在YARN中运行,应用的运行结果不能在客户端显示,所以最好运行那些将结果最终保存在外部存储介质(如HDFS、Redis、Mysql)而非stdout输出的应用程序,客户端的终端显示的仅是作为YARN的job的简单运行状况。 12345678910111213141516171819./bin/spark-submit --class org.apache.spark.examples.SparkPi \\--master yarn \\--deploy-mode cluster \\--driver-memory 1g \\--executor-memory 1g \\--executor-cores 2 \\--queue default \\lib/spark-examples*.jar \\10./bin/spark-submit --class cn.itcast.spark.day1.WordCount \\--master yarn \\--deploy-mode cluster \\--driver-memory 1g \\--executor-memory 1g \\--executor-cores 2 \\--queue default \\/home/bigdata/hello-spark-1.0.jar \\hdfs://master:9000/wc hdfs://master:9000/out-yarn-1","text":"[TOC] SparkOnYarn两种模式区别cluster模式:Driver程序在YARN中运行,应用的运行结果不能在客户端显示,所以最好运行那些将结果最终保存在外部存储介质(如HDFS、Redis、Mysql)而非stdout输出的应用程序,客户端的终端显示的仅是作为YARN的job的简单运行状况。 12345678910111213141516171819./bin/spark-submit --class org.apache.spark.examples.SparkPi \\--master yarn \\--deploy-mode cluster \\--driver-memory 1g \\--executor-memory 1g \\--executor-cores 2 \\--queue default \\lib/spark-examples*.jar \\10./bin/spark-submit --class cn.itcast.spark.day1.WordCount \\--master yarn \\--deploy-mode cluster \\--driver-memory 1g \\--executor-memory 1g \\--executor-cores 2 \\--queue default \\/home/bigdata/hello-spark-1.0.jar \\hdfs://master:9000/wc hdfs://master:9000/out-yarn-1 client模式:Driver运行在Client上,应用程序运行结果会在客户端显示,所有适合运行结果有输出的应用程序(如spark-shell) 12345678910111213client模式./bin/spark-submit --class org.apache.spark.examples.SparkPi \\--master yarn \\--deploy-mode client \\--driver-memory 1g \\--executor-memory 1g \\--executor-cores 2 \\--queue default \\lib/spark-examples*.jar \\10spark-shell必须使用client模式./bin/spark-shell --master yarn --deploy-mode client 原理cluster模式: Spark Driver首先作为一个ApplicationMaster在YARN集群中启动,客户端提交给ResourceManager的每一个job都会在集群的NodeManager节点上分配一个唯一的ApplicationMaster,由该ApplicationMaster管理全生命周期的应用。具体过程: 由client向ResourceManager提交请求,并上传jar到HDFS上这期间包括四个步骤:a).连接到RMb).从RM的ASM(ApplicationsManager )中获得metric、queue和resource等信息。c). upload app jar and spark-assembly jard).设置运行环境和container上下文(launch-container.sh等脚本) ResouceManager向NodeManager申请资源,创建Spark ApplicationMaster(每个SparkContext都有一个ApplicationMaster) NodeManager启动ApplicationMaster,并向ResourceManager AsM注册 ApplicationMaster从HDFS中找到jar文件,启动SparkContext、DAGscheduler和YARN Cluster Scheduler ResourceManager向ResourceManager AsM注册申请container资源 ResourceManager通知NodeManager分配Container,这时可以收到来自ASM关于container的报告。(每个container对应一个executor) Spark ApplicationMaster直接和container(executor)进行交互,完成这个分布式任务。 client模式 在client模式下,Driver运行在Client上,通过ApplicationMaster向RM获取资源。本地Driver负责与所有的executor container进行交互,并将最后的结果汇总。结束掉终端,相当于kill掉这个spark应用。一般来说,如果运行的结果仅仅返回到terminal上时需要配置这个。 客户端的Driver将应用提交给Yarn后,Yarn会先后启动ApplicationMaster和executor,另外ApplicationMaster和executor都 是装载在container里运行,container默认的内存是1G,ApplicationMaster分配的内存是driver- memory,executor分配的内存是executor-memory。同时,因为Driver在客户端,所以程序的运行结果可以在客户端显 示,Driver以进程名为SparkSubmit的形式存在。","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"使用","slug":"使用","permalink":"http://gangtieguo.cn/tags/使用/"}]},{"title":"SparkStreaming介绍","slug":"SparkStreaming介绍","date":"2018-08-15T17:50:47.842Z","updated":"2019-06-17T04:40:09.399Z","comments":true,"path":"2018/08/16/SparkStreaming介绍/","link":"","permalink":"http://gangtieguo.cn/2018/08/16/SparkStreaming介绍/","excerpt":"[TOC] 大数据领域,分为离线计算和实时计算","text":"[TOC] 大数据领域,分为离线计算和实时计算 Streaming和Storm比较在时效性上比storm弱,在吞吐量上比storm大streaming需要设置时间间隔,设置多长时间产生一个批次记录到streaming放在RDD里面比如设置5s,每隔5s就会产生一个RDDRDD需要是有序的","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"SparkStreaming","slug":"SparkStreaming","permalink":"http://gangtieguo.cn/tags/SparkStreaming/"}]},{"title":"SparkRDD介绍","slug":"SparkRDD介绍","date":"2018-08-15T16:49:49.734Z","updated":"2019-06-17T04:40:09.398Z","comments":true,"path":"2018/08/16/SparkRDD介绍/","link":"","permalink":"http://gangtieguo.cn/2018/08/16/SparkRDD介绍/","excerpt":"[TOC] 1sc.textfile(\"hdfs://master:9000/wc\").flatMap(_.split(\"分隔符\")).map((_,1)).reduceByKey(_+_).saveAsTextFile(\"hdfs://master:9000/wcResult\")","text":"[TOC] 1sc.textfile(\"hdfs://master:9000/wc\").flatMap(_.split(\"分隔符\")).map((_,1)).reduceByKey(_+_).saveAsTextFile(\"hdfs://master:9000/wcResult\") 当rdd形成过程中,worker的分区中只是预留了存放数据的位置,只有当action触发的时候,worker的分区中才会存在数据,sparkSubmit submit的命令行默认的是driver ,RDD的创建都是在在driver上创建的 spark的分区与hdfs数据块的关系Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。 SparkRDDRDD(ResilientDistributed Dataset)叫做分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。 1)一组分片(Partition),即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。 2)一个计算每个分区的函数。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。 3)RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。 4)一个Partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。 5)一个列表,存储存取每个Partition的优先位置(preferredlocation)。对于一个HDFS文件来说,这个列表保存的就是每个**Partition所在的块的位置**。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。 血缘依赖 RDD 5个特性一个function作用一个partition如果是key-value格式的有一个默认的partitioner 默认是hashpartitioner如果是从hdfs这种文件系统类型读取的数据,会有一个prefered location,因为在大数据领域宁愿移动计算,也不愿移动数据,通常叫做数据本地化, RDD数据读取rdd向hdfs中读取数据是一行一行读取放在迭代器里面,而不是一下子全部读取数据 rdd向hdfs中读取数据,hdfs文件有几个数据块就会创建几个分区 读取数据还是用的hadoop的inputFormat来读取的 RDD的生成方式RDD算子TransformationRDD中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。这种设计让Spark更加有效率地运行。 常用的Transformation: 转换 含义 map(func) 返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成 filter(func) 返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成 flatMap(func) 类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该返回一个序列,而不是单一元素) mapPartitions(func) 类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U] mapPartitionsWithIndex(func) 类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是 (Int, Interator[T]) => Iterator[U] sample(withReplacement, fraction, seed) 根据fraction指定的比例对数据进行采样,可以选择是否使用随机数进行替换,seed用于指定随机数生成器种子 union(otherDataset) 对源RDD和参数RDD求并集后返回一个新的RDD intersection(otherDataset) 对源RDD和参数RDD求交集后返回一个新的RDD distinct([numTasks])) 对源RDD进行去重后返回一个新的RDD groupByKey([numTasks]) 在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDD reduceByKey(func, [numTasks]) 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,与groupByKey类似,reduce任务的个数可以通过第二个可选的参数来设置 aggregateByKey(zeroValue)(seqOp, combOp, [numTasks]) sortByKey([ascending], [numTasks]) 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD sortBy(func,[ascending], [numTasks]) 与sortByKey类似,但是更灵活 join(otherDataset, [numTasks]) 在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD cogroup(otherDataset, [numTasks]) 在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD cartesian(otherDataset) 笛卡尔积 pipe(command, [envVars]) coalesce(numPartitions) repartition(numPartitions) repartitionAndSortWithinPartitions(partitioner) Action 动作 含义 reduce(func) 通过func函数聚集RDD中的所有元素,这个功能必须是可交换且可并联的 collect() 在驱动程序中,以数组的形式返回数据集的所有元素 count() 返回RDD的元素个数 first() 返回RDD的第一个元素(类似于take(1)) take(n) 返回一个由数据集的前n个元素组成的数组 takeSample(withReplacement,num, [seed]) 返回一个数组,该数组由从数据集中随机采样的num个元素组成,可以选择是否用随机数替换不足的部分,seed用于指定随机数生成器种子 takeOrdered(n, [ordering]) saveAsTextFile(path) 将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本 saveAsSequenceFile(path) 将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。 saveAsObjectFile(path) countByKey() 针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。 宽依赖窄依赖区分 窄依赖 narrow dependencies三个小分块是RDD的分区,组合起来的大框是RDD,后面的是子rdd的分区,一个父rdd的分区只对应一个子rdd的分区(类比独生子女) ,一个子可以对应多个父分区(可以类比父母分区) 如map,filter,union等算子都是操作的原来分区里面的数据,操作之后也在原来的分区join大多数情况下是宽依赖,在一种特殊情况下是窄依赖 (join是针对key value形式的rdd,相同key的会join在一起) 宽依赖 wide dependencies父rdd一个分区会流向多个子rdd的分区类比多子女情况 groupBy ,reduceByKey ,join等 下图b到g不是一个stage是因为,提前已经分好组,所以是窄依赖,没有stage LineageRDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(即血统)记录下来,以便恢复丢失的分区。RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。 RDD的缓存Spark速度非常快的原因之一,就是在不同操作中可以在内存中持久化或缓存个数据集。当持久化某个RDD后,每一个节点都将把计算的分片结果保存在内存中,并在对此RDD或衍生出的RDD进行的其他动作中重用。这使得后续的动作变得更加迅速。RDD相关的持久化和缓存,是Spark最重要的特征之一。可以说,缓存是Spark构建迭代式算法和快速交互式查询的关键。 缓存方式RDD通过persist方法或cache方法可以将前面的计算结果缓存,但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。 通过查看源码发现cache最终也是调用了persist方法,默认的存储级别都是仅在内存存储一份,Spark的存储级别还有好多种,存储级别在object StorageLevel中定义的。 缓存有可能丢失,或者存储存储于内存的数据由于内存不足而被删除,RDD的缓存容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于RDD的一系列转换,丢失的数据会被重算,由于RDD的各个Partition是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部Partition。 RDD缓存val rdd = sc.textFile(“hdfs:hadoop1:9000/yao”).cache();cache是trancsformation也是懒加载,遇到action 如count.collect才会,缓存到内存里面,而不是文件系统中读取cache()调用的persist() rdd.unpersist() 就会将内存中的缓存释放掉rdd.unpersist(true) CheckPoint的背景checkpoint属于transaction 云计算一边要将中间结果进行产生多个RDD和多次运算,特别是机器学习,需要中间结果计算很多很多次迭代,有可能上百次这样就需要将中间RDD结果保存下来,这就是我们的checkpoint,一般保存在高可用中,比如hdfs就是高可用的。 只有rdd才能checkPoint缓存cache到内存中,直接到内存中拿checkPoint是到hdfs CheckPoint命令设定目录,创建目录 ,必须指定缓存到哪个目录 1234val rdd = sc.setCheckpointDir(\"hdfs://master:9000/ckpoint\")val rdd = sc.textFile(\"hdfs://master:9000/yao\")rdd.checkpointrdd.count 会触发两个任务,一个任务计算,一个任务写入到ck指定的hdfs目录 为减小持久化的数据量,最好将RDD过滤出有节点意义的数据再进行ck操作,直接ck会把文件记录起来到hdfs中 ,但是count产生的数据不能ck,因为返回的是Long类型的,单数据类型的数据不能checkpoint 在ck操作以后,RDD和数据的关联都取消了,ck成功以后,数据直接从ckpoint里面读取即可,由于ck属于transaction故ck必须在触发action之前执行 如果把RDD缓存到内存(即在ck之前有cache rdd到内存的操作)就不会另起一个作业一步一步从原始数据运行,然后再ck到hdfs目录,而是直接从内存中读取数据 广播变量为了提高效率,比如mapreduce 使用join。当map段所需要的数据量不是很大,避免网络浪费,使用mapAsJoin把规则加入map端内存当中,这样mapreduce在map端可以直接在缓存中拿到规则,这样可以提高效率。广播变量的原理也是如此 1val bd = sc.broadcast(ruleArray) 广播出去 广播之后所有的executer都能收到,而且是相当于在每个executor中都存有这一小部分数据,不用通过网络传输,提高效率 在rdd中拿到广播中的数据, 123val arr = bd.value //将数据展示arr.toBuffer","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"RDD","slug":"RDD","permalink":"http://gangtieguo.cn/tags/RDD/"}]},{"title":"Hadoop零碎知识点","slug":"Hadoop零碎知识点","date":"2018-08-15T16:34:55.243Z","updated":"2019-06-17T04:40:09.377Z","comments":true,"path":"2018/08/16/Hadoop零碎知识点/","link":"","permalink":"http://gangtieguo.cn/2018/08/16/Hadoop零碎知识点/","excerpt":"[TOC] 查看元数据信息可以通过hdfs的一个工具来查看edits中的信息 12bin/hdfs oev -i edits -o edits.xmlbin/hdfs oiv -i fsimage_0000000000000000087 -p XML -o fsimage.xml","text":"[TOC] 查看元数据信息可以通过hdfs的一个工具来查看edits中的信息 12bin/hdfs oev -i edits -o edits.xmlbin/hdfs oiv -i fsimage_0000000000000000087 -p XML -o fsimage.xml 查看目录树12hdfs会在配置文件中配置一个datanode的工作目录元数据 查看目录结构 tree hddata/ 安全模式 这是因为在分布式文件系统启动的时候,开始的时候会有安全模式,当分布式文件系统处于安全模式的情况下,文件系统中的内容不允许修改也不允许删除,直到安全模式结束。安全模式主要是为了系统启动的时候检查各个DataNode上数据块的有效性,同时根据策略必要的复制或者删除部分数据块。运行期通过命令也可以进入安全模式。在实践过程中,系统启动的时候去修改和删除文件也会有安全模式不允许修改的出错提示,只需要等待一会儿即可。 安全模式的出现为甚么集群启动的时候要进入安全模式,因为不进入安全模式,nameNode不知道数据块存放在哪儿 为什么一开始,namenode不记录下这些数据的地址呢?因为数据位置信息会发生变化 这些数据记录在datanode的block块下的元数据下。fodaration模式是记录在juoinrnode下的 juoinrnode也是一个硬盘版的zookeeper 在hdfs启动的时候,处于安全模式,datanode向namenode汇报自己的ip和持有的block块信息,这样,安全模式结束后,文件块和datanode的ip关联上,存放数据的位置也就可以找到 可以通过以下命令来手动离开安全模式: 1bin/hadoop dfsadmin -safemode leave 用户可以通过dfsadmin -safemode value 来操作安全模式,参数value的说明如下:enter - 进入安全模式leave - 强制NameNode离开安全模式get - 返回安全模式是否开启的信息wait - 等待,一直到安全模式结束。 还有另外一种情况当namenode发现集群中的block丢失数量达到一个阀值时,namenode就进入安全模式状态,不再接受客户端的数据更新请求 在正常情况下,namenode也有可能进入安全模式:集群启动时(namenode启动时)必定会进入安全模式,然后过一段时间会自动退出安全模式(原因是datanode汇报的过程有一段持续时间) 也确实有异常情况下导致的安全模式原因:block确实有缺失措施:可以手动让namenode退出安全模式,bin/hdfs dfsadmin -safemode leave或者:调整safemode门限值: dfs.safemode.threshold.pct=0.999f Hadoop的调度器FIFO: 默认,先进先出的原则 Capacity: 计算能力调度器,选择占用最小、优先级高的执行 Fair: 公平调度,所有的job具有相同的资源 (可以在core-site.xml中配置,spark是有两种调度器,fifo 公平调度)","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://gangtieguo.cn/tags/Hadoop/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"HDFS","slug":"HDFS","permalink":"http://gangtieguo.cn/tags/HDFS/"}]},{"title":"HDFS元数据备份流程-Namenode与SeceondaryNamenode之间的关系","slug":"HDFS元数据备份流程","date":"2018-08-15T15:56:42.315Z","updated":"2019-07-05T10:51:49.962Z","comments":true,"path":"2018/08/15/HDFS元数据备份流程/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/HDFS元数据备份流程/","excerpt":"hdfs元数据管理namenode对数据的管理采用了三种存储形式: 内存元数据(NameSystem) 磁盘元数据镜像文件fsimagefsimage保存着文件的名字、id、分块信息、大小等,但是不保存datanode名称对应的ip 数据操作日志文件edits(可通过日志运算出元数据)","text":"hdfs元数据管理namenode对数据的管理采用了三种存储形式: 内存元数据(NameSystem) 磁盘元数据镜像文件fsimagefsimage保存着文件的名字、id、分块信息、大小等,但是不保存datanode名称对应的ip 数据操作日志文件edits(可通过日志运算出元数据) datanode和namenode交互在hdfs启动的时候,处于安全模式,datanode向namenode汇报自己的ip和持有的block块信息,这样,安全模式结束后,文件块和datanode的ip关联上,存放数据的位置也就可以找到 元数据管理流程 注意:secondaryNamenode虽然持有元数据,但是不能将其作为namenode使用,因为secondnary不能更新元数据 磁盘文件我们可以通过查看namenode的目录结构看到: A、内存中有一份完整的元数据(内存meta data)B、磁盘有一个“准完整”的元数据镜像(fsimage)文件(在namenode的工作目录中)C、用于衔接内存metadata和持久化元数据镜像fsimage之间的操作日志(edits文件注:当客户端对hdfs中的文件进行新增或者修改操作,操作记录首先被记入edits日志文件中,当客户端操作成功后,相应的元数据会更新到内存meta.data中 inprogress文件就是正在写的文件 namenode管理元数据解析namenode会将元数据放在内存里面,这样方便快速对数据的请求,由于放在内存中是不安全的,所以就序列化到fsimage里面,就像jvm中dump,将内存中所有数据dump出去,由于一条元数据大小为150byte,hdfs不利于存储小块文件,所以存大文件划算,因为元数据消耗的内存都是一样的,但是内存中的数据量太大,不可能经常序列化,所以需要定时序列化 所以引入了secondaryNameNode更新元数据的时候,不可能去直接跟更改元数据fsimage文件,因为文件是线性结构,假如遇到更改中间内容会很不方便,所有就将操作信息记录在edits日志文件中,只是记录操作信息 edits文件定期转为元数据为了防止edits过多,导致在启动hdfs集群datanode的时候会很慢,因为需要将edits通过转化形成为元数据fsimage文件,所以应该定期将edits文件转换为fsimage元数据,然后将fsimage替换掉 secondaryNameNode的出现如果nameNode来做上面的edits转换为元数据的话,由于消耗的资源太大,就不能为其他比如从hdfs中读取数据服务提供资源,或者提供服务的效果不好所以这个时候就把合并操作交给secondNameNode来做这个过程叫做checkpoint namenode和secondaryNameNode最好不要放在一台机器上 宕机可能导致数据不能恢复测试环境或者学习环境可以弄在一台机器上 secondarynamenode只能恢复大部分数据,因为有正在写的部分还未更新持久化过去。 为了防止namenode宕机导致了数据丢失所作配置可以在hdfs-site.xml文件中在多个机器上的目录来保存name的edits,fsimage文件 1234<property> <name>dfs.name.dir</name> <value>/home/bigdata/names1,/home/bigdata/names2</value></property> 配置的多个的话,会同时往这两个目录中写 如果不配置这个默认的目录是core-site.xml文件中配置的hadoop的临时文件 1234<property> <name>hadoop.tmp.dir</name> <value>/home/bigdata/apps/hadoop-2.6.4/tmp</value> </property> checkpoint触发条件12345678dfs.namenode.checkpoint.check.period=60 #检查触发条件是否满足的频率,60秒dfs.namenode.checkpoint.dir=file://${hadoop.tmp.dir}/dfs/namesecondary#以上两个参数做checkpoint操作时,secondary namenode的本地工作目录dfs.namenode.checkpoint.edits.dir=${dfs.namenode.checkpoint.dir}dfs.namenode.checkpoint.max-retries=3 #最大重试次数dfs.namenode.checkpoint.period=3600 #两次checkpoint之间的时间间隔3600秒dfs.namenode.checkpoint.txns=1000000 #两次checkpoint之间最大的操作记录","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://gangtieguo.cn/tags/Hadoop/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"HDFS","slug":"HDFS","permalink":"http://gangtieguo.cn/tags/HDFS/"}]},{"title":"Hdfs结构性能分析及读写流程","slug":"Hdfs结构性能分析及读写流程","date":"2018-08-15T15:55:15.892Z","updated":"2018-12-25T03:53:56.755Z","comments":true,"path":"2018/08/15/Hdfs结构性能分析及读写流程/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/Hdfs结构性能分析及读写流程/","excerpt":"[TOC] hdfs的设计思想分而治之:将大文件、大批量文件,分布式存放在大量服务器上,以便于采取分而治之的方式对海量数据进行运算分析首先,它是一个文件系统,用于存储文件,通过统一的命名空间——目录树来定位文件其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色;","text":"[TOC] hdfs的设计思想分而治之:将大文件、大批量文件,分布式存放在大量服务器上,以便于采取分而治之的方式对海量数据进行运算分析首先,它是一个文件系统,用于存储文件,通过统一的命名空间——目录树来定位文件其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色; hdfs功能和特点hdfs的重要特性如下: HDFS中的文件在物理上是分块存储(block),块的大小可以通过配置参数( dfs.blocksize)来规定,默认大小在hadoop2.x版本中是128M,老版本中是64M HDFS文件系统会给客户端提供一个统一的抽象目录树,客户端通过路径来访问文件,形如:hdfs://namenode:port/dir-a/dir-b/dir-c/file.data 目录结构及文件分块信息(元数据)的管理由namenode节点承担——namenode是HDFS集群主节点,负责维护整个hdfs文件系统的目录树,以及每一个路径(文件)所对应的block块信息(block的id,及所在的datanode服务器) 文件的各个block的存储管理由datanode节点承担—- datanode是HDFS集群从节点,每一个block都可以在多个datanode上存储多个副本(副本数量也可以通过参数设置dfs.replication) HDFS是设计成适应一次写入,多次读出的场景,且不支持文件的修改 Hdfs的结构1.HDFS集群分为两大角色:NameNode、DataNode (secondary NameNode)2.NameNode负责管理整个文件系统的元数据:记录文件在哪里3.DataNode 负责管理用户的文件数据块:不负责切块,负责保管4.文件会按照固定的大小(blocksize)切成若干块后分布式存储在若干台datanode上5.每一个文件块可以有多个副本,并存放在不同的datanode上副本不会放在同一个机器上,因为副本就是防止宕机,6.Datanode会定期向Namenode汇报自身所保存的文件block信息,而namenode则会负责保持文件的副本数量因为datanode如果宕机的话,name该机器上的对应的副本数据将会消失,这样需要将其在其他机器上进行恢复,恢复的话,就需要上面就需要数据和未宕机时的数据尽量保持一致,所以需要依赖于datanode定期汇报,不然差距的数据会很大7.HDFS的内部工作机制对客户端保持透明,客户端请求访问HDFS都是通过向namenode申请来进行 Hdfs写操作 详细步骤解析 1、根namenode通信请求上传文件,namenode检查目标文件是否已存在,父目录是否存在 不存在则会返回path not exist异常 2、namenode返回是否可以上传 3、client请求第一个 block(0-128m)该传输到哪些datanode服务器上 返回该block存放的位置,及其副本的信息存放的位置 4、namenode返回3个datanode服务器ABC 副本选择策略(如果设置为被分数为2的话) 考虑空间和距离的因素,网络跳转的跳数,比如说机架的位置, 第一台是看谁比较近(机架),因为传输比较快,副本则是是看谁比较远,防止机架出问题(如断电),干扰性更小 而集群全线崩塌 5、client请求3台dn中的一台A上传数据(本质上是一个RPC调用,建立pipeline),A收到请求会继续调用B,然后B调用C,将真个pipeline建立完成,逐级返回客户端 这样是防止整个流程变慢,同时创建通道,先建立通道pipeline,通道 6、client开始往A上传第一个block(先从磁盘读取数据放到一个本地内存缓存bytebuf),以packet为单位,A收到一个packet就会传给B,B传给C;A每传一个packet会放入一个应答队列等待应答 因为等一个block写满之后再传送,速度会很慢,所以是接收一个packet就会写入到管道流pipeline中。 只要上传一个成功,则客户端视为上传成功,因为如果没上传成功,namenode会进行异步的复制副本的信息 7、当一个block传输完成之后,client再次请求namenode上传第二个block的服务器。 注:写的过程中,namenode记录下来了文件路径,文件有几个block也记录下来了,每个block分配到哪些机器上也记录下到了,及其每个block的副本信息,副本在那几个机器上。 校验的时候不是一个packet(一批chunk,共64k)校验,而是以一个chunk来校验,一个chunk是512byte(字节) Hdfs读操作 客户端将要读取的文件路径发送给namenode,namenode获取文件的元信息(主要是block的存放位置信息)返回给客户端,客户端根据返回的信息找到相应datanode逐个获取文件的block并在客户端本地进行数据追加合并从而获得整个文件 1、跟namenode通信查询元数据,找到文件块所在的datanode服务器 2、挑选一台datanode(就近原则,然后随机)服务器,请求建立socket流 3、datanode开始发送数据(从磁盘里面读取数据放入流,以packet为单位来做校验) 4、客户端以packet为单位接收,现在本地缓存,然后写入目标文件 对此,我们了解了hdfs的读写流程,那么我们再来看看hdfs元数据的管理 小贴士hdfs中datanode的初始化 hdfs会在配置文件中配置一个namenode的工作目录元数据 查看目录结构 tree $DATANODE/ datanode的工作目录是在datanode启动后初始化的而hadoop namenode format 只会初始name的工作目录,和datanode没有关系 如何把一个hdfs的一个节点加入到另一个集群因为在原来的目录中会有原来集群的信息如:ClusterID 必须要将hdfs datanode的工作目录删除,不然持有上一个集群的datanode的工作目录,会认为是一个误操作,为了防止丢失数据,不会让其连接上","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://gangtieguo.cn/tags/Hadoop/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"HDFS","slug":"HDFS","permalink":"http://gangtieguo.cn/tags/HDFS/"}]},{"title":"Spark启动流程及一些小总结","slug":"Spark启动流程及一些小总结","date":"2018-08-15T15:54:20.258Z","updated":"2019-06-17T04:40:09.400Z","comments":true,"path":"2018/08/15/Spark启动流程及一些小总结/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/Spark启动流程及一些小总结/","excerpt":"[TOC] spark的架构模型 角色功能Driver:以spark-submit提交程序为例,执行该命令的主机为driver(在任意一台安装了spark(spark submit)的机器上启动一个任务的客户端也就是Driver 。客户端与集群需要建立链接,建立的这个链接对象叫做sparkContext,只有这个对象创建成功才标志这这个客户端与spark集群链接成功。SparkContext是driver进程中的一个对象,提交任务的时候,指定了每台机器需要多少个核cores,需要的内存 ) Master: 给任务提供资源,分配资源,master跟worker通信,报活和更新资源 Worker: 以子进程的方式启动executor Executor:Driver提交程序到executor(CoarseGrainedExecutorBankend),执行任务的进程,exector是task运行的容器","text":"[TOC] spark的架构模型 角色功能Driver:以spark-submit提交程序为例,执行该命令的主机为driver(在任意一台安装了spark(spark submit)的机器上启动一个任务的客户端也就是Driver 。客户端与集群需要建立链接,建立的这个链接对象叫做sparkContext,只有这个对象创建成功才标志这这个客户端与spark集群链接成功。SparkContext是driver进程中的一个对象,提交任务的时候,指定了每台机器需要多少个核cores,需要的内存 ) Master: 给任务提供资源,分配资源,master跟worker通信,报活和更新资源 Worker: 以子进程的方式启动executor Executor:Driver提交程序到executor(CoarseGrainedExecutorBankend),执行任务的进程,exector是task运行的容器 Driver端driver创建sparkContext于spark集群建立连接,然后向master申请资源,master来调度决定向worker的哪台机器上执行 ,在合适的worker(资源分配策略)上以子进程的方式启动excutor来执行 (Class.forName()会只在自己的进程里面执行,反射是在同一进程 ,启动java子进程的方法 是另一个进程,debug跳不到子进程中去,进程之间进行方法调用只有rpc才可以 )此过程只是启动executor,最终任务的执行需要等到action触发,提交程序最终也是提交到executor上,在整个过程中, driver提交一个application时就会在driver主机生成一个sparkSubmit进程来监控任务的执行情况。action触发程序运行后,executor只会通过driverClinet与driver交互,applicationMaster 不负责具体某个任务的进度 ,只负责资源的调度和监控。driver在action的时候才会把任务提交给executor。 master与worker交互worker和master以进程方式启动后 ,worker会向master进行注册,worker在启动的时候,会在spark env中指定worker的资源,如果没指定的话,会默认使用机器的所有核数,所有内存-1G的内存,预留1G给操作系统,然后worker会告知master自己拥有的资源,如核数cores,内存等。在之后的过程中,worker会实时向master发送心跳报活。 资源分配策略spark任务资源分配有两种策略:尽量打散 和尽量集中 以task1需要2core为例: 尽量打散是尽可能的将任务分散到不同的worker来启动exector,机器1分配1core,机器2分配1core 尽量集中是尽可能在更少的机器上来启动worker并启动exector,在分配的机器上尽可能多的分配资源,达到集中在更少的机器上的目的。如在机器1上分配2core(前提是机器1 剩余core数>=2) RDD整个运行流程首先rdd产生过后,经过一系列的处理,构成相互依赖,在有宽依赖的情况下会划分stage,构成一个有向无环图也就是DAG,整个rdd的构建,完成在action触发之前,action触发就会将task提交到executor容器中运行 Driver与executor交互在任务提交的时候,会将driver信息封装,先告诉给master,master再告诉给worker,然后worker启动executor进程的时候,会将信息告诉给executor,故而最终exector能获取到driver的信息 在executor创建成功后,就会和driver建立连接,而不再与Master通信,这样是为了减少Master压力,也不让Master成为性能瓶颈。 由于executor有可能在很多台机器上启动,driver无法知晓在哪台机器上启动有exector,所以需要executor和driver进行rpc通信(netty或者akka)主动建立连接。建立通信后exector向driver汇报状态及其执行进度,driver向executor提交计算任务,然后在executor中执行,只有当RDD触发action的时候,driver才将taskset以stage的形式提交任务给executor,任务的最细粒度是task 然后executor就跟driver进行通信,Rdd执行逻辑的时候就不再通过Master,这是为了减少Master压力,也不让Master成为性能瓶颈。 RDD的构建详细阶段 第一阶段 rdd创建rdd创建都是在driver中执行,只有运行时有数据流向rdd才会提交到executor。如hdfs中读取数据,会产生很多的RDD….,RDD之间存在着依赖关系, RDD之间的转换都是由transformation产生的(故transformation返回对象为RDD),在action触发后,DAG就确定了RDD就形成了数据的流向hdfs->RDD1->RDD2->RDD3….->RDDn 第二阶段 DAGSchedulerstage的划分,也就是DAGScheduler的执行 stage切分依据:有宽依赖(后面对宽窄依赖有说明)就会切分,更明确的说就是有shuffle的时候切分 宽依赖:遇到shuffle就是宽依赖窄依赖:没有shuffle就是窄依赖 把DAG切分成stage,然后以taskSet(流水线task的集合,所有的task业务逻辑都是一样的,只是计算的数据块不同,因为每一个task只计算一个分区,且taskSet集合中的任务会并行操作) task由master决定在哪个executor上运行,之后由driver提交task到executor来执行 前面的stage先提交,因为stage的划分意味着有shuffle,那么后面stage会依赖前面stage的数据,故此前面stage先提交。 Action触发的时候,DAG就可以确定了,transformation的调用都是在driver中完成的,一旦调用了action,会提交这个任务。 第三阶段 TaskSchedulerTaskScheduler cluster Manager :就是Master,启动work上的executorstragling tasks:例子:当100个任务,99都完成了,剩下了一个任务1,会再启动和剩下的一模一样的任务2,任务1和任务2谁先完成,就用谁的结果 第四阶段 任务执行Block Manager管理数据 也就是action触发,数据开始流通,driver提交task到worker上的executor,执行真正的业务逻辑 计算完成之后,若将这些结果数据collect聚合,则会将所有executor的部分结果聚合在一起,比如count、sum的,多个worker中每一个worker只会保存一部分数据,driver保存了所有数据,、都是在driver里面 task与读取hdfs数据的关系task提交之后,如果向hdfs中拉取数据,driver已经将要获取数据的元数据都已经获取到了,task会和分区数挂钩,在executor上一个task读取一个分区。task读取数据不是一下将所有数据加载到内存里面,是先构建一个迭代器,拿到一条处理一条。处理完成之后会将数据保存在executor的内存中,如果内存放不下,就将数据持久化到磁盘上","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"}]},{"title":"SparkStreaming消费Kafka数据","slug":"SparkStreaming消费Kafka数据","date":"2018-08-15T15:53:30.330Z","updated":"2018-08-20T01:39:18.490Z","comments":true,"path":"2018/08/15/SparkStreaming消费Kafka数据/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/SparkStreaming消费Kafka数据/","excerpt":"[TOC] Streaming消费Kafka有两种方式 1、reciver方式根据时间来划分批次,缺点:有可能一个时间段会出现数据爆炸,有保存log到hdfs机制,但消耗大(zk来管理偏移量) 2、direct方式 1.3.6后推出executor和kafka的partition是一一对应的(是rdd的分区和kafka对应,如果一个executor的rdd有多个分区,那么一个executor可以对应多个partition)必须自己来管理偏移量,最好把偏移量写在zk或者其他第三方介质里面","text":"[TOC] Streaming消费Kafka有两种方式 1、reciver方式根据时间来划分批次,缺点:有可能一个时间段会出现数据爆炸,有保存log到hdfs机制,但消耗大(zk来管理偏移量) 2、direct方式 1.3.6后推出executor和kafka的partition是一一对应的(是rdd的分区和kafka对应,如果一个executor的rdd有多个分区,那么一个executor可以对应多个partition)必须自己来管理偏移量,最好把偏移量写在zk或者其他第三方介质里面 非直连方式 receive方式spark cluster先与kafka集群建立连接在每一个executor中创建一个Reciver当executor启动kafka启动后,reciver会一直消费kafka中的数据,一直拉,直连是每隔一个时间段去拉取这种方式是zookeeper来管理数据的偏移量问题:在时间间隔中,executor接收的数据超过了Executor的内存数,会造成数据的丢失为了防止数据丢失,可以做checkpoint,或者是记录日志可以多个reciver,一个reciver可以指定多个线程可以读读文章 Kafka Intergration Guide 官网链接 直连Direct Approach (No Receives)B-*代表broker,每一个broker中有三个分区(假设),但是每个broker里面只有一个分区是活着的直连是每隔一个时间段去拉取对消费数据的位置保证会定期实时查询kafka topic+partition的偏移量,会根据偏移量的范围来处理每一个批次, executor不会接收超出接收范围的数据,而是记录下偏移量,下次接着拉取一个kafka的partiton对应rdd的一个partition 问题:一个partition只有一个Executor连接上,不能并行读去数据。解决办法,可以repartition,分散到多个partition上去读取。怎样让executor读取kafka分区里面的数据的速度快?将boker的分区数创建成和worker的数目一样,也就是executor的数目一样,一个executor消费一个分区,这样数据读取比较快。并行读取数据,也可以控制读取的速度。这样需要自己管理偏移量,以前的方式是zk管理偏移量最好是将偏移量保存到zk里面,不过是自己控制的,防止将偏移量保存到本地宕机无法恢复。需要读博客 Spark streaming整合kafka官方文档显示,一个数据保证只会消费一次,不会重复消费,更加高效简化并行,一对一消费,高效,没有reciver作为消费者","categories":[],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"SparkStreaming","slug":"SparkStreaming","permalink":"http://gangtieguo.cn/tags/SparkStreaming/"}]},{"title":"Spark算子案例","slug":"Spark算子","date":"2018-08-15T15:52:40.133Z","updated":"2019-06-17T04:40:09.401Z","comments":true,"path":"2018/08/15/Spark算子/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/Spark算子/","excerpt":"[TOC] HelloWord?WorldCount1sc.textfile(\"hdfs://master:9000/wc\").flatMap(_.split(\"分隔符\")).map((_,1)).reduceByKey(_+_).saveAsTextFile(\"hdfs://master:9000/wcResult\") 数据最开始在Driver,计算的时候数据会流入worker当rdd形成过程中,worker的分区中只是预留了存放数据的位置,只有当action触发的时候,worker的分区中才会存在数据 Spark的运算都是通过算子进行RDD的转换及运算,那我们对算子进行简单熟悉参考RDD算子实例","text":"[TOC] HelloWord?WorldCount1sc.textfile(\"hdfs://master:9000/wc\").flatMap(_.split(\"分隔符\")).map((_,1)).reduceByKey(_+_).saveAsTextFile(\"hdfs://master:9000/wcResult\") 数据最开始在Driver,计算的时候数据会流入worker当rdd形成过程中,worker的分区中只是预留了存放数据的位置,只有当action触发的时候,worker的分区中才会存在数据 Spark的运算都是通过算子进行RDD的转换及运算,那我们对算子进行简单熟悉参考RDD算子实例 reduceByKey先进行一下combineer 移动计算 groupByKey不好 reduceByKey会在局部先进行一下求和 groupByKey是会将所有的数据放在一个大集合里面,然后再求和 ,会消耗更多的网络带宽,不符合计算本地化 一下一些RDD是给予rdd1来操作的 1val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 2) mapPartitionsmap 是对 rdd 中的每一个元素进行操作,而 mapPartitions(foreachPartition) 则是对 rdd 中的每个分区的迭代器进行操作。如果在 map 过程中需要频繁创建额外的对象 (例如将 rdd 中的数据通过 jdbc 写入数据库, map 需要为每个元素创建一个链接而 mapPartition 为每个 partition 创建一个链接), 则 mapPartitions 效率比 map 高的多。 SparkSql 或 DataFrame 默认会对程序进行 mapPartition 的优化。 mapPartitionsWithIndexmapPartitionWithIndex与mapPartition类似,只是会带上分区的序号 把每个partition中的分区号和对应的值拿出来, 源码中方法的形式: 123val func(index,Int,iter:Interator[(Int)]):Interator[String] = {iter.toList.map(x => \"[partID:\" + index + \", val: \" + x + \"]\").iterator} 会转换成函数函数的形式 1234val func = (index: Int, iter: Iterator[(Int)]) => { iter.toList.map(x => \"[partID:\" + index + \", val: \" + x + \"]\").iterator}rdd1.mapPartitionsWithIndex(func).collect aggregate (action)aggregate是一个action操作 源码定义 1def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]): U eqOp 操作会聚合各分区中的元素,然后 combOp 操作把所有分区的聚合结果再次聚合,两个操作的初始值都是 zeroValue. seqOp 的操作是遍历分区中的所有元素 (T),第一个 T 跟 zeroValue 做操作,结果再作为与第二个 T 做操作的 zeroValue,直到遍历完整个分区。combOp 操作是把各分区聚合的结果,再聚合。aggregate 函数返回一个跟 RDD 不同类型的值。因此,需要一个操作 seqOp 来把分区中的元素 T 合并成一个 U,另外一个操作 combOp 把所有 U 聚合。 参考理解 Spark RDD 中的 aggregate 函数 第一个参数:初始值(在进行操作的时候,会默认带入该值进行)第二个参数: 是两个函数[每个函数都是2个参数(第一个函数:先对各个分区进行合并, 第二个函数:对各个分区合并后的结果再进行合并)] 最后得到返回值 rdd1为上面的rdd1分区函数的结果 1rdd1.aggregate(0)(_+_, _+_) 0 + (0+1+2+3+4 + 0+5+6+7+8+9) 1rdd1.aggregate(7)(_+_, _+_) 7 + (7+1+2+3+4 + 7+5+6+7+8+9) 1rdd1.aggregate(0)(math.max(_, _), _ + _) 1rdd1.aggregate(5)(math.max(_, _), _ + _) 5和1比, 得5再和234比得5 –> 5和6789比,得9 –> 5 + (5+9) 1val rdd2 = sc.parallelize(List(\"q\",\"w\",\"e\",\"r\",\"t\",\"y\",\"u\",\"i\",\"o\",\"p\"),2) 可以用更加直接的方式验证操作 123def func2(index: Int, iter: Iterator[(String)]) : Iterator[String] = { iter.toList.map(x => \"[partID:\" + index + \", val: \" + x + \"]\").iterator} 123rdd2.aggregate(\"\")(_ + _, _ + _)rdd2.aggregate(\"=\")(_ + _, _ + _)rdd2.aggregate(\"|\")(_ + _, _ + _) 12val rdd3 = sc.parallelize(List(\"qazqqw7\",\"jishhrwe9\",\"sdfwezsddf12\",\"12esdww8\"),2)rdd3.aggregate(\"\")((x,y) => math.max(x.length, y.length).toString, (x,y) => x + y) 12val rdd4 = sc.parallelize(List(\"qazqqw7\",\"jishhrwe9\",\"sdfwezsddf12\",\"\"),2)rdd4.aggregate(\"\")((x,y) => math.min(x.length, y.length).toString, (x,y) => x + y) aggregateByKey对每个分区进行计算 12345val pairRDD = sc.parallelize(List( (\"a\",1), (\"a\", 12), (\"b\", 4),(\"c\", 17), (\"c\", 12), (\"b\", 2)), 2)def func2(index: Int, iter: Iterator[(String, Int)]) : Iterator[String] = { iter.toList.map(x => \"[partID:\" + index + \", val: \" + x + \"]\").iterator}pairRDD.mapPartitionsWithIndex(func2).collect combineByKeyreduceByKey aggregateByKey底层都是依赖的combineByKey,combineByKey比较底层的算子和reduceByKey是相同的效果 combineByKey有三个参数 第一个参数x: 原封不动取出来 第二个参数:是函数, 局部运算, 第三个:是函数, 对局部运算后的结果再做运算 12345val rdd4 = sc.parallelize(List(\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\"g\",\"h\",\"i\"),2)val rdd5 = sc.parallelize(List(1,1,2,2,2,1,2,2,2),2)val rdd6 = rdd5.zip(rdd4)val rdd7 = rdd6.combineByKey(List(_),(x:List[String],y:String)=>x:+y,(m:List[String],n:List[String])=>m ++ n)rdd7.collect reduceByKeyreduceByKey 用于对每个 key 对应的多个 value 进行 merge 操作,最重要的是它能够在本地先进行 merge 操作,并且 merge 操作可以通过函数自定义。 groupByKeygroupByKey 也是对每个 key 进行操作,但只生成一个 sequence。不会再进行 需要特别注意 “Note” 中的话,它告诉我们:如果需要对 sequence 进行 aggregation 操作(注意,groupByKey 本身不能自定义操作函数),那么,选择 reduceByKey/aggregateByKey 更好。这是因为 groupByKey 不能自定义函数,我们需要先用 groupByKey 生成 RDD,然后才能对此 RDD 通过 map 进行自定义函数操作。 checkpoint将rdd内容持久化 1234567sc.setCheckpointDir(\"hdfs://master:9000/ck\")val rdd = sc.textFile(\"hdfs://master:9000/wc\").flatMap(_.split(\" \")).map((_, 1)).reduceByKey(_+_)rdd.checkpointrdd.isCheckpointedrdd.countrdd.isCheckpointedrdd.getCheckpointFile coalesce, repartition有时候需要重新设置 Rdd 的分区数量,比如 Rdd 的分区中,Rdd 分区比较多,但是每个 Rdd 的数据量比较小,需要设置一个比较合理的分区。或者需要把 Rdd 的分区数量调大。还有就是通过设置一个 Rdd 的分区来达到设置生成的文件的数量。 如果分区的数量发生激烈的变化,如设置 numPartitions = 1,这可能会造成运行计算的节点比你想象的要少,为了避免这个情况,可以设置 shuffle=true, 那么这会增加 shuffle 操作。 关于这个分区的激烈的变化情况,比如分区数量从父 Rdd 的几千个分区设置成几个,有可能会遇到这么一个错误。 1java.io.IOException: Unable to acquire 16777216 bytes of memory 这个错误只要把 shuffle 设置成 true 即可解决。 当把父 Rdd 的分区数量增大时,比如 Rdd 的分区是 100,设置成 1000,如果 shuffle 为 false,并不会起作用。 这时候就需要设置 shuffle 为 true 了,那么 Rdd 将在 shuffle 之后返回一个 1000 个分区的 Rdd,数据分区方式默认是采用 hash partitioner。 最后来看看 repartition() 方法的源码: coalesce() 方法的作用是返回指定一个新的指定分区的 Rdd。 123val rdd1 = sc.parallelize(1 to 10, 10)val rdd2 = rdd1.coalesce(2, false)rdd2.partitions.length collectAsMap将其他集合保存为map结构 1234val rdd = sc.parallelize(List((\"a\", 1), (\"b\", 2)))rdd.collectAsMap得到结果Map(b -> 2, a -> 1) countByKey1234val rdd1 = sc.parallelize(List((\"a\", 1), (\"b\", 2), (\"b\", 2), (\"c\", 2), (\"c\", 1)))rdd1.countByKey 统计Key出现的次数结果 Map(b -> 2, a -> 1, c -> 2) countByValue1234val rdd1 = sc.parallelize(List((\"a\", 1), (\"b\", 2), (\"b\", 2), (\"c\", 2), (\"c\", 1)))rdd1.countByValue结果 (将整个元组作为key)Map((b,2) -> 2, (c,2) -> 1, (a,1) -> 1, (c,1) -> 1) filterByRange12345val rdd1 = sc.parallelize(List((\"a\", 5), (\"b\", 3), (\"c\", 4), (\"d\", 2), (\"e\", 1)))val rdd2 = rdd1.filterByRange(\"b\", \"d\")rdd2.collectArray[(String, Int)] = Array((b,3), (c,4), (d,2)) flatMapValues压平 12345val rdd3 = sc.parallelize(List((\"a\", \"1 2\"), (\"b\", \"3 4\")))val rdd4 = rdd3.flatMapValues(_.split(\" \"))rdd4.collectArray[(String, String)] = Array((a,1), (a,2), (b,3), (b,4)) foldByKey12345678910111213val rdd1 = sc.parallelize(List(\"a22\", \"b232\", \"c\", \"d\"), 2)val rdd2 = rdd1.map(x => (x.length, x))rdd2.collect结果: Array[(Int, String)] = Array((3,a22), (4,b232), (1,c), (1,d))val rdd3 = rdd2.foldByKey(\"\")(_+_)rdd3.collect结果:将相同key的元组合并在一起,Array[(Int, String)] = Array((4,b232), (1,cd), (3,a22)) foreachforeach是针对于每一个元素,foreachPartition是针对每一个分区,foreachPartition是写入数据库时,可以将在foreachPartition时获得一个数据库连接,通过map方法来将每个分区的全部元素写入到数据库 foreachPartition3个分区 12val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)rdd1.foreachPartition(x => println(x.reduce(_ + _))) keyBy以传入的参数做key 1234val rdd1 = sc.parallelize(List(\"dog\", \"salmon\", \"salmon\", \"rat\", \"elephant\"), 3)val rdd2 = rdd1.keyBy(_.length)rdd2.collect结果 Array((3,dog), (6,salmon), (6,salmon), (3,rat), (8,elephant)) keys values1234val rdd1 = sc.parallelize(List(\"dog\", \"tiger\", \"lion\", \"cat\", \"panther\", \"eagle\"), 2)val rdd2 = rdd1.map(x => (x.length, x))rdd2.keys.collectrdd2.values.collect","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"}]},{"title":"Scala基本使用-杂记","slug":"Scala基本使用","date":"2018-08-15T15:52:20.469Z","updated":"2019-06-17T04:40:09.395Z","comments":true,"path":"2018/08/15/Scala基本使用/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/Scala基本使用/","excerpt":"[TOC] Var和val var 修饰的变量可改变,val 修饰的变量不可改变;但真的如此吗?事实上,var 修饰的对象引用可以改变,val 修饰的则不可改变,但对象的状态却是可以改变的。 定义方法1def m1(x:Int,y:Int):Int=x*y","text":"[TOC] Var和val var 修饰的变量可改变,val 修饰的变量不可改变;但真的如此吗?事实上,var 修饰的对象引用可以改变,val 修饰的则不可改变,但对象的状态却是可以改变的。 定义方法1def m1(x:Int,y:Int):Int=x*y 函数的定义=>123val func:Int =>String={x=x.toString}也可以写成val func1=(x:Int)=>x.toString 函数和方法的区别123456定义一个方法def m2(f:(Int,Int)=>Int) =f(2,6)定义一个函数val f2 =(x:Int,y:Int) =>x-y调用f2(m2) 神奇的下划线将方法转换成函数 123def m2(x:Int,y:Int):Int = x+yval f2(a:Int,y:Int)=>x+yval f2 = m2 _ 数组、映射、元组、集合数组123val arr = new Array[Int](10) //创建数组,无值val arr1 = Array(1,2,3,4,5) //直接实例化for(e<- arr) **yield** e*2; 用yield关键字可以生成一个新的数组 生成的类型和循环的类型是一样的 map方法map方法是将每一个元素拿来操作arr.map(_*2) map方法更好用 map是排序? 12val a = Array(1,2,3,4,5)a.map((x:Int)=>x*10)//匿名函数 由于知道a中的数据类型 可以将Int省略a.map(x=>x10)还可以用占位符,进行进一步的省略a.map( 10) 所有偶数取出来然后再乘以1012345arr.filter((x:Int)=>x%2==0)arr.filter(x=>x%2 == 0)arr.filter(_%2==0)arr.filter(_%2==0).map(_*10)val p = println _ 可以将方法转换成函数用利用 _ trait 关键字数组的常用函数在超类TraversableLike中定义了这些函数12345678val arr = Array(1,2,3,4,5,6)arr.sum 总数arr.sorted 排序arr.sorted.reverse 逆序arr.sortBy(x=>x)按照本身来排序arr.sortWith(_>_)从大到小排序 < 从小到大排序arr.sortWith((x,y))## 映射 类比java中的map12val m = Map(\"a\"->1,\"b\"->2)m(\"a\") 取值 里边的值不能更改如果是mulitble的包就可以更改了1m.getOrElse(\"c\",0) 找c 如果没有c就创建0 apply方法元组val t = (1,”spark”,2.0); 值类型都不一定 m.+=((“c”,1)) 与 m+=(“m”->1) 写法相同val t,(x,y,z) = (“a”,1,2.0)t是变量,x y z 是键 对应三个值,取值的时候 直接用x就可以取值 将对偶的转成映射12val arr = Array((\"a\",1),(\"b\",2))arr.toMap 就可以转成映射 拉链操作123val a = Array(\"a\",\"b\",\"c\")val b = Array(1,2,3);a.zip(b) 将其拉在一起变成数组,元素为元组 集合序列Seq,集Set,映射Map集合分为可变和不可变的 mutable 和 immutable注意和val的对比将0插入来lst前面生成一个新的集合 1234val lst2 = 1 :: lst1; val lst3 = lst1.::(0)val lst4 = 0 +: lst1val lst5 = lst1.+:(0) 将一个元素添加到lst后面产生一个新的集合 12val lst6 = lst1:+3val lst0 = List(4,5,6) 将2个list合并成一个新的List 1val lst7 = lst1 ++ lst0 将lst0插入到lst前面生成一个新的集合 1val lst8 = lst1 ++: lst0 foreachforeach是将其值取出来,不会新生成一个集合map将值取出来会新生成一个集合 MapMap本身不支持排序但是有toList方法 无括号,可以支持排序 List1val list = List(1,2,3,4,5,6); list.par转成并行化集合list.par.reduce(_+_) 将其放在多个reduce中执行,数据量大的时候将会变得很快reduce不是并行集合的话,就是调用的底层reduceLeft(不能并行了) fold可以指定初始值1list.par.fold(0)(_+_) aggregate 聚合需要传两个函数,第一个函数是对元素进行操作,第二函数是对局部操作的结果进行操作 12List(List(1,7,9,8),List(0,1,2,3))list.aggregate(0)(_+_.sum,_+_) union 并集,intersect交集,diff差集 flatten 将数据压缩123List(List(1,7,9,8),List(0,1,2,3))listAll.flattenList(1,7,9,8,0,1,2,3) 产生新的集合 对象主构造器里面的所有方法都会被执行 单例对象所有的object都是一个单例(把class替换成object)不要new,直接等于类名就是调用的一个单例对象 12345object Dog{ def main(){ val d = Dog }} 伴生对象就是对象名和类名一样,并且在一个scala文件中可以和类互相访问私有属性scala中返回的就是unit就是返回的一个括号 apply方法1234567891011121314Object Dog{ def apply():Unit = { print (); } def apply(name:String):Unit = { print(name) } def main(){ //会调用第一个无参数的apply方法 val d1 = Dog() //会调用第有参数的apply方法 val d2 = Dog(\"haha\"); }} 应用程序对象没有什么实际作用 构造函数用this关键字定义辅助构造器1234def this(name:String,age:Int,gender:String){//每个服务构造器必须以主构造器或者其他的辅助构造器的调用开始//主构造器就是类名上直接填写参数} 函数与方法的互换, 神奇的下划线要想传到map里面,必须得是函数 123456789def fangfa方法val func 函数val arr = Array(1,2,3,4,5,6)arr.map(func(5))arr.map(func())val m = fangfa _ 方法func转成函数fangfa() 也可以转函数def m(x:Int) = (y:int)=>x*y 柯里化的两种表达方式柯里化是主要是通过类型类匹配的 12def m1(x:Int) = (y:Int)=>x*ydef m2(x:Int)(y:Int)=>x*y 柯里化会先执行一部分,返回一个函数1234567891011 def multi= (x:Int) =>{ x*x } def main(args: Array[String]): Unit = { val arr = Array(1,2,3,5,4) val a1 = multi(10) println(s\"平方式:${a1}\") //按照规则 map只能参数只能是函数,multi是一个方法,但是在柯里化的时候,会先返回一个中间结果是函数 val a2 = arr.map(multi) } 继承 代理 装饰 之间的区别继承是类的增强 代理是对实例,方法的增强 装饰也是对方法的增强 implicit def 隐式的,隐式转化的包在predef中 泛型12<? extends clazz> 传入的数据是clazz的子类 <? super clazz> 传入的数据是clazz的父类 > < >= <=以上操作符,在scala中都是方法 视图定界 view bound <%scala泛型 12345class Person[T] { def chooser[T <: Comparable[T]](firit: T, second: T): T = { first } } 隐式转换:我自己的隐式上下文 1234object MyPredef{ implicit 函数 implicit 值 } viewbound要求传入一个隐式转换函数 123456789101112class Chooser[T <% Ordered[T]] { def bigger(first: T, second: T) : T = { if(first > second) first else second } } class Chooser[T] { def bigger(first: T, second: T)(implicit ord: T => Ordered[T]) : T = { if(first > second) first else second } } contextbound要求传入一个隐式转换值 1234567891011class Chooser[T: Ordering] { def bigger(first: T, second: T) : T = { val ord = implicitly[Ordering[T]] if(ord.gt(first, second)) first else second } } class Chooser[T] { def bigger(first: T, second: T)(implicit ord : Ordering[T]) : T = { if(ord.gt(first, second)) first else second } } [+T][-T] 相当于传入了一个隐式转换的函数一定要传入一个隐式转换函数 1234567class Chooser [t <% Order[T]]{ def choose(first T,second T) :T ={ //val ord = implicitly[Ordering[T]] // if(ord.gt(first,second))) first else second; if(first.compare(second) > 0) first else second; }} 12345678910implict object girlOrdering extends Ordering[girl]{override def compare(x:girl,y:girl):Int = {}}== 和这个实现的效果是一样的,只是取了一个名字implicit val girlOrder = new Ordering[Girl]{} 上下文定界 : content bound相当于传入了一个隐式转换的值 关于实体类Predef的关系","categories":[{"name":"语言","slug":"语言","permalink":"http://gangtieguo.cn/categories/语言/"}],"tags":[{"name":"Scala","slug":"Scala","permalink":"http://gangtieguo.cn/tags/Scala/"}]},{"title":"Hadoop-HA-Federation机制","slug":"Hadoop-构成及HA-","date":"2018-08-15T15:21:34.817Z","updated":"2018-12-25T03:53:59.560Z","comments":true,"path":"2018/08/15/Hadoop-构成及HA-/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/Hadoop-构成及HA-/","excerpt":"[TOC] (1)hadoop-HA集群运作机制介绍 所谓HA,即高可用(7*24小时不中断服务)实现高可用最关键的是消除单点故障,hadoop-ha严格来说应该分成各个组件的HA机制——HDFS的HA、YARN的HA","text":"[TOC] (1)hadoop-HA集群运作机制介绍 所谓HA,即高可用(7*24小时不中断服务)实现高可用最关键的是消除单点故障,hadoop-ha严格来说应该分成各个组件的HA机制——HDFS的HA、YARN的HA (2)HDFS的HA机制详解通过双namenode消除单点故障双namenode协调工作的要点: A、元数据管理方式需要改变: 内存中各自保存一份元数据 Edits日志只能有一份,只有Active状态的namenode节点可以做写操作,两个namenode都可以读取edits,共享的edits放在一个共享存储中管理(qjournal和NFS两个主流实现) B、需要一个状态管理功能模块 实现了一个zkfailover,常驻在每一个namenode所在的节点 每一个zkfailover负责监控自己所在namenode节点,利用zk进行状态标识 当需要进行状态切换时,由zkfailover来负责切换 切换时需要防止brain split现象的发生 Hadoop-HA的主要思想是有两个NameNode,一个作为主NameNode,一个作为standby,两个NameNode使用同一个命名空间。通过zookeepr(JournalNode)来进行协调,实现NameNode的主备切换。 真正的架构和流程如上图所示 Hadoop中federation机制共同运行多个active的namenode(多套主备的namenode集群),且公用用一套datanode","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://gangtieguo.cn/tags/Hadoop/"}]},{"title":"","slug":"HBase性能分析","date":"2018-08-15T07:24:37.471Z","updated":"2019-06-17T04:40:09.381Z","comments":true,"path":"2018/08/15/HBase性能分析/","link":"","permalink":"http://gangtieguo.cn/2018/08/15/HBase性能分析/","excerpt":"[TOC] HBase介绍HBase表很大:一个表可以有数十亿行,上百万列; HBase的表将会分成很多个分区,每个分区部分会存在不同的机器上分区是为了便于查询,放在不同机器上,io也增大,假如一个机器的io的是100m,两个就为200m,读取速度就变快了==>多台机器的io能得到充分利用","text":"[TOC] HBase介绍HBase表很大:一个表可以有数十亿行,上百万列; HBase的表将会分成很多个分区,每个分区部分会存在不同的机器上分区是为了便于查询,放在不同机器上,io也增大,假如一个机器的io的是100m,两个就为200m,读取速度就变快了==>多台机器的io能得到充分利用 HBase表无模式:每行都有一个可排序的主键好任意多的列,列可以根据需要动态的增加,同一张表中不同的行可以有不同的列; 面向列:列独立检索; 稀疏:空列并不占用存储空间,表可以设计的非常稀疏; 数据类型单一:HBase中的数据都是字符串,没有类型 HBase采用类LSM的架构体系,数据写入并没有直接写入数据文件,而是会先写入缓存(Memstore),在满足一定条件下缓存数据再会异步刷新到硬盘。为了防止数据写入缓存之后不会因为RegionServer进程发生异常导致数据丢失,在写入缓存之前会首先将数据顺序写入HLog中。如果不幸一旦发生RegionServer宕机或者其他异常,这种设计可以从HLog中进行日志回放进行数据补救,保证数据不丢失。HBase故障恢复的最大看点就在于如何通过HLog回放补救丢失数据。 HBase结构HBase进行存储的服务器 HRegion是HBase当中的一个类,一个表分区的类,按照行分区一个HRegion只会在一个HBase上,一个HBase上可以有多个HRegionHBase表每个分区(按照行来分区)的数据被封装到一个类HRegion内如:HBase存在user表,role表,共4个regionServer,HRegion1存储管理user表的一部分,HRegion2存储管理user表的一部分,HRegion3存储管理role表的一部分,HRegion4存储管理role表的一部分 HRegionserver管理用户对Table的增、删、改、查操作;记录region在哪台Hregion server上在Region Split后,负责新Region的分配;新机器加入时,管理HRegion Server的负载均衡,调整Region分布在HRegion Server宕机后,负责失效HRegion Server 上的Regions迁移。 HBaseHRegion Server主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。 HRegion Server管理了很多table的分区,也就是region。 HRegion构成HRegion类中有HLog,store成员,分别代表硬盘和内存 Store每个Region包含着多个Store对象,一个列簇对应一个store 。每个Store包含一个MemStore和若干StoreFile,StoreFile包含一个或多个HFile,StoreFile是对HFile的一种封装。MemStore存放在内存中,StoreFile存储在HDFS上。 HLogHLog最终是放在hdfs上。 当我们客户端上传一个表名,一个列簇,一个值,这条命令的值会原封不动的将其写入到HLog里面, 这个是一个appendLog,只可以从底部追加,不允许修改,写到HLog之后,再将数据写入到内存(memstore)当中,HLog里面是存储的操作信息的数据 写在HLog中是因为,防止在写入到内存中的时候,宕机 Region的划分Region按大小分割的,随着数据增多,Region不断增大,当增大到一个阀值(默认256m)的时候,Region就会分成两个新的Region HRegion的存储ROOT表和META表HBase的所有Region元数据被存储在.META.表中,随着Region的增多,.META.表中的数据也会增大,并分裂成多个新的Region。为了定位.META.表中各个Region的位置,把.META.表中所有Region的元数据保存在-ROOT-表中,最后由Zookeeper记录-ROOT-表的位置信息。所有客户端访问用户数据前,需要首先访问Zookeeper获得-ROOT-的位置,然后访问-ROOT-表获得.META.表的位置,最后根据.META.表中的信息确定用户数据存放的位置,如下图所示。 -ROOT-表永远不会被分割,它只有一个Region,这样可以保证最多只需要三次跳转就可以定位任意一个Region。为了加快访问速度,.META.表的所有Region全部保存在内存中。客户端会将查询过的位置信息缓存起来,且缓存不会主动失效。如果客户端根据缓存信息还访问不到数据,则询问相关.META.表的Region服务器,试图获取数据的位置,如果还是失败,则询问-ROOT-表相关的.META.表在哪里。最后,如果前面的信息全部失效,则通过ZooKeeper重新定位Region的信息。所以如果客户端上的缓存全部是失效,则需要进行6次网络来回,才能定位到正确的Region。 root表,mate表都不会很大因为root表,只是记录位置,本身就不会太大,meta表,和root表在过程中都会被加载到内存中经过3次来回,总共六次,会得到数据表的位置1,client向zookeeper获取root表的位置 2,zookeeper返回root表地址信息3,client读取root表,获得table1的meta表的地址,4 root表所在机器返回meta表地址5,client向mete表读取table地址,6 client向table插入数据读取和写入都会经历上面的过程 HBase写数据流程client向HRegionserver发送写请求。HRegionserver将操作信息数据写到hlog(write ahead log)。为了数据的持久化和恢复。 HLog记录的是操作数据 HRegionserver将实际数据写到内存(memstore)反馈client写成功。 数据flush当memstore数据达到阈值64(新版本默认是128M),将内存集合中的数据刷到硬盘,将内存中的数据删除,同时删除Hlog中的历史数据。并将数据存储到hdfs中。以数据块的形式存储。在hlog中做标记点。 flush的说明当内存文件memstore文件达到64m的时候,会将数据合并刷新写入到StroeFile文件里面,再将数据写入到HFile里面,HFile文件是一个hdfs文件,序列化到hdfs里面,再通过hdfs的api写入到hdfs集群里面提交到hdfs集群后,HLog,memstore的数据将会被清除 数据合并1、当(hdfs中)数据块达到4块,hmaster将数据块加载到本地(HRegionserver),进行合并注:这个数据块单块没有大小限制2、当合并的数据超过256M,进行拆分,将拆分后的region分配给不同的hregionserver管理注:如果不大于256M,将数据原封不动写回hdfs3、当hregionsever宕机后,将该hregionserver上的hlog拆分(按表拆分),然后分配给不同的hregionserver加载,修改.META.注:由Hmaster来更改.META.文件,不会对HRegionserver的性能造成影响4、注意:hlog会同步到hdfs注:合并的数据是对相同rowkey的分组合并,如user表中,对id=’1’的操作内容合并,对id=’2’的合并 合并的好处合并操作是由hmaster来工作合并操作是针对一个表来说,user表合并user表,role表合并role表1、清理了垃圾数据2、将大的数据块拆分后,给多个机器管理,优化读取操作等速率 HBase的读流程通过zookeeper和-ROOT- .META.表定位HBase。数据从内存和硬盘合并后返回给client数据块会缓存 ClientHBase Client使用HBase的RPC机制与HRegionserver和RegionServer进行通信管理类操作:Client与HRegionserver进行RPC;数据读写类操作:Client与HBase进行RPC。 读取数据与写入数据流程的不同除了在表中读取数据,还要在内存磁盘上去搜寻还未存到hdfs里面的数据。所以数据块太大的应该拆分,可以加快查询速度经常查询的数据块还应该放在内存中(HRegionserver的内存中) HBase的出现hdfs是分布式文件系统,只能保存整个文件,如果一行一行的保存数据,namenode的压力会很大假如有一个文件下有100w小个文件,每个文件都是1k,在datanode中不会占用128m的分块大,但是每个文件元数据所占的大小是一样的,这样的话,namenode空间占满时,datanode中的数据实际上很少。HBase就会很好的解决这个问题,一个文件一个文件写的时候,是先写入到HBase中,先写入到HBase集群中(HBase也分为主从,主为HMaster,从为HRegionserver),HRegionserver的内存中,当数据量达到128m的时候,将数据写入到hdfs中,这样128m数据的元数据只有一条 HBase细节HBase实际上是一个缓存层,存储的数据量很少,存的一部分缓存的数据,HBase需要zookeeper来定位HBase查找数据的偏移量 HBase 主从之间的关系hmaster只是一个管理者,而且只管理,当HBase集群挂掉之后,数据偏移信息和表的信息,而不管理数据信息,所以有一种极端情况,当HBase集群启动之后,表创建完成,正常运行之后,将hmaster关闭也不会影响整个集群的运行。不像namenode挂了之后不能响应了 note:HBase写快读慢,读慢是相对于写来说的,但是跟mysql相比,也不是一个量级的","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"HBase","slug":"HBase","permalink":"http://gangtieguo.cn/tags/HBase/"},{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"}]},{"title":"Kafka读写数据","slug":"Kafka读取数据性能","date":"2018-08-14T11:47:22.892Z","updated":"2018-08-15T07:23:24.298Z","comments":true,"path":"2018/08/14/Kafka读取数据性能/","link":"","permalink":"http://gangtieguo.cn/2018/08/14/Kafka读取数据性能/","excerpt":"[TOC] 首先kafka依赖于操作系统的pageCache机制,尽可能的把空闲的内存作为一个磁盘,只有发生缺页的才会放在磁盘中","text":"[TOC] 首先kafka依赖于操作系统的pageCache机制,尽可能的把空闲的内存作为一个磁盘,只有发生缺页的才会放在磁盘中 那么落在磁盘上的话,还有就是采用的是sendfile机制:主要思想就是在内核中进行拷贝,再写socket流,传统io是先将数据读到内核在写到用户区,在写到内核,再是socket流,这样就算磁盘的话,也是相当快的","categories":[{"name":"组件","slug":"组件","permalink":"http://gangtieguo.cn/categories/组件/"}],"tags":[{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"Kafka","slug":"Kafka","permalink":"http://gangtieguo.cn/tags/Kafka/"}]},{"title":"Kafka深入解析","slug":"Kafka深入解析","date":"2018-08-14T07:43:27.968Z","updated":"2019-06-17T04:40:09.390Z","comments":true,"path":"2018/08/14/Kafka深入解析/","link":"","permalink":"http://gangtieguo.cn/2018/08/14/Kafka深入解析/","excerpt":"[TOC] Kafka结构 Producer :消息生产者,就是向kafka broker发消息的客户端。 Consumer :消息消费者,向kafka broker取消息的客户端 Topic :可以理解为一个队列","text":"[TOC] Kafka结构 Producer :消息生产者,就是向kafka broker发消息的客户端。 Consumer :消息消费者,向kafka broker取消息的客户端 Topic :可以理解为一个队列 Consumer Group (CG): (消费组)这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。 一个topic可以有多个CG。topic的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个partion只会把消息发给该CG中的一个consumer。如果需要实现广播,只要每个consumer有一个独立的CG就可以了(即是需要消费消息的消费者,属于不同的消费组)。要实现单播只要所有的consumer在同一个CG(即是需要消费消息的消费者,属于同一个消费组)。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。 Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。 broker的作用就是帮你把消息从发送端传送到接收端 Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。 Offset:kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka Consumer与topic关系本质上kafka只支持Topic; 每个group中可以有多个consumer,每个consumer属于一个consumer group; 通常情况下,一个group中会包含多个consumer,这样不仅可以提高topic中消息的并发消费能力,而且还能提高”故障容错”性,如果group中的某个consumer失效那么其消费的partitions将会有其他consumer自动接管。 对于Topic中的一条特定的消息,只会被订阅此Topic的每个group中的其中一个consumer消费,此消息不会发送给一个group的多个consumer; 那么一个group中所有的consumer将会交错的消费整个Topic,每个group中consumer消息消费互相独立,我们可以认为一个group是一个”订阅”者。 在kafka中,一个partition中的消息只会被group中的一个consumer消费(同一时刻); 一个Topic中的每个partions,只会被一个”订阅者”中的一个consumer消费,不过一个consumer可以同时消费多个partitions中的消息。 kafka的设计原理决定,对于一个topic,同一个group中不能有多于partitions个数的consumer同时消费,否则将意味着某些consumer将无法得到消息。 kafka只能保证一个partition中的消息被某个consumer消费时是顺序的;事实上,从Topic角度来说,当有多个partitions时,消息仍不是全局有序的。 Kafka消息的分发Producer客户端负责消息的分发 kafka集群中的任何一个broker都可以向producer提供metadata信息,这些metadata中包含”集群中存活的servers列表”/“partitions leader列表”等信息; 当producer获取到metadata信息之后, producer将会和Topic下所有partition leader保持socket连接; 消息由producer直接通过socket发送到broker,中间不会经过任何”路由层”,事实上,消息被路由到哪个partition上由producer客户端决定; 比如可以采用”random””key-hash””轮询”等,如果一个topic中有多个partitions,那么在producer端实现”消息均衡分发”是必要的。 在producer端的配置文件中,开发者可以指定partition路由的方式。 Producer消息发送的应答机制设置发送数据是否需要服务端的反馈,有三个值0,1,-1 0: producer不会等待broker发送ack 1: 当leader接收到消息之后发送ack -1: 当所有的follower都同步消息成功后发送ack request.required.acks=0 Consumer的负载均衡当一个group中,有consumer加入或者离开时,会触发partitions均衡.均衡的最终目的,是提升topic的并发消费能力,步骤如下: 假如topic1,具有如下partitions: P0,P1,P2,P3 加入group中,有如下consumer: C1,C2 首先根据partition索引号对partitions排序: P0,P1,P2,P3 根据consumer.id排序: C0,C1 计算倍数: M = [P0,P1,P2,P3].size / [C0,C1].size,本例值M=2(向上取整) 然后依次分配partitions: C0 = [P0,P1],C1=[P2,P3],即Ci = [P(i M),P((i + 1) M -1)] Kafka文件存储机制Kafka文件存储基本结构在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个目录,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。 每个partion(目录)相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件中。但每个段**segmentfile**消息数量不一定相等,这种特性方便oldsegment file快速被删除。默认保留7天的数据。 每个partiton只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定。(什么时候创建,什么时候删除) Kafka Partition SegmentSegment file组成:由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀”.index”和“.log”分别表示为segment索引文件、数据文件。 l Segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。 l 索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。 3,497:当前log文件中的第几条信息,存放在磁盘上的那个地方 上述图中索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。 其中以索引文件中元数据3,497为例,依次在数据文件中表示第3个message(在全局partiton表示第368772个message)、以及该消息的物理偏移地址为497。 l segment data file由许多message组成, qq物理结构如下: 关键字** 解释说明** 8 byte offset 在parition(分区)内的每条消息都有一个有序的id号,这个id号被称为偏移(offset),它可以唯一确定每条消息在parition(分区)内的位置。即offset表示partiion的第多少message 4 byte message size message大小 4 byte CRC32 用crc32校验message 1 byte “magic” 表示本次发布Kafka服务程序协议版本号 1 byte “attributes” 表示为独立版本、或标识压缩类型、或编码类型。 4 byte key length 表示key的长度,当key为-1时,K byte key字段不填 K byte key 可选 value bytes payload 表示实际消息数据。 kafka查找message,先查找segment file 00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0 00000000000000368769.index的消息量起始偏移量为368770= 368769 + 1 00000000000000737337.index的起始偏移量为737338=737337+ 1 其他后续文件依次类推。 以起始偏移量命名并排序这些文件,只要根据offset二分查找文件列表,就可以快速定位到具体文件。当offset=368776时定位到00000000000000368769.index和对应log文件。 再通过segment file查找message 当offset=368776时,依次定位到00000000000000368769.index的元数据物理位置和00000000000000368769.log的物理偏移地址 然后再通过00000000000000368769.log顺序查找直到offset=368776为止。","categories":[{"name":"组件","slug":"组件","permalink":"http://gangtieguo.cn/categories/组件/"}],"tags":[{"name":"原理","slug":"原理","permalink":"http://gangtieguo.cn/tags/原理/"},{"name":"Kafka","slug":"Kafka","permalink":"http://gangtieguo.cn/tags/Kafka/"}]},{"title":"Kafka集群配置及配置文件","slug":"Kafka集群配置及配置文件","date":"2018-08-13T06:54:35.320Z","updated":"2018-08-13T07:21:34.526Z","comments":true,"path":"2018/08/13/Kafka集群配置及配置文件/","link":"","permalink":"http://gangtieguo.cn/2018/08/13/Kafka集群配置及配置文件/","excerpt":"[TOC] Kafka集群部署kafka默认推荐的是2.11开头的,如果系统中没有其他软件依赖于Scala的话,就使用2.11版本的scala是依赖于zookeeper的,所以需要给zookeeper配置地址","text":"[TOC] Kafka集群部署kafka默认推荐的是2.11开头的,如果系统中没有其他软件依赖于Scala的话,就使用2.11版本的scala是依赖于zookeeper的,所以需要给zookeeper配置地址 1、下载安装包http://kafka.apache.org/downloads.html在linux中使用wget命令下载安装包wget http://mirrors.hust.edu.cn/apache/kafka/0.8.2.2/kafka_2.11-0.8.2.2.tgz 2、解压安装包123tar -zxvf kafka_2.11-0.8.2.2.tgz -C /home/bigdata/apps/kafka/cd /home/bigdata/apps/kafka/ln -s kafka_2.11-0.8.2.2 kafka 3、修改配置文件配置文件有4个点 hostname应该保持一致 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061broker.id =0#每一个broker在集群中的唯一表示,要求是正数。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况 log.dirs=/home/bigdata/logs/kafka-logs#kafka数据的存放地址,多个地址的话用逗号分割 /data/kafka-logs-1,/data/kafka-logs-2 port =9092#broker server服务端口 message.max.bytes =6525000#表示消息体的最大大小,单位是字节 num.network.threads =3#broker处理消息的最大线程数,一般情况下不需要去修改 配置了三台服务器,所以选择三个 #num.io.threads =8#broker处理磁盘IO的线程数,数值应该大于你的硬盘数 #background.threads =4#一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改 queued.max.requests =500#等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息,应该是一种自我保护机制。#broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置 socket.send.buffer.bytes=102400#socket的发送缓冲区,socket的调优参数SO_SNDBUFF socket.receive.buffer.bytes =102400#socket的接受缓冲区,socket的调优参数SO_RCVBUFF socket.request.max.bytes =104857600#socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖 #log.segment.bytes =1024*1024*1024#topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,会被topic创建时的指定参数覆盖 log.roll.hours =168 zookeeper.connect = bigdata1:2181,bigdata2:2181,bigdata3:2181#zookeeper集群的地址,可以是多个,多个之间用逗号分割 hostname1:port1,hostname2:port2,hostname3:port3 zookeeper.session.timeout.ms=6000#ZooKeeper的最大超时时间,就是心跳的间隔,若是没有反映,那么认为已经死了,不易过大 zookeeper.connection.timeout.ms =6000#ZooKeeper的连接超时时间 zookeeper.sync.time.ms =2000#host.name=bigdata1 #broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置,hostname为主机 # 这个是轻量的配置文件#broker的全局唯一编号,不能重复broker.id=0#用来监听连接的端口,producer或consumer将在此端口建立连接port=9092#处理网络请求的线程数量,集群中有几个节点就设置几个num.network.threads=3#用来处理磁盘io的线程数量num.io.threads=8#发送套接字的缓冲区大小socket.send.buffer.bytes=102400#接受套接字的缓冲区大小socket.receive.buffer.bytes=102400#请求套接字的缓冲区大小socket.request.max.bytes=104857600#kafka运行日志存放的路径log.dirs=/home/hadoop/logs/kafka#topic在当前broker上的分片个数num.partitions=2#用来恢复和清理data下数据的线程数量num.recovery.threads.per.data.dir=1#segment文件保留的最长时间。超时将会被删除log.retention.hours=168#滚动删除生成心得segment文件的最大时间log.roll.hour=168 ------------------ 12ip(重要),如果不改,则客户端会抛出:producer connection to localhost:9092 unsuccessful错误,advertised.host.name=192.168.11.11 \\12 cp /home/bigdata/apps/kafka/config/server.properties /home/bigdata/apps/kafka/config/server.properties.bakvi /home/bigdata/apps/kafka/config/server.properties 12# 4、分发安装包 scp -r /home/bigdata/apps/kafka/ bigdata2:/home/bigdata/apps/ 12然后分别在各机器上创建软连 cd /home/bigdata/apps/kafkaln -s kafka_2.11-0.8.2.2 kafka 12# 5、再次修改配置文件(重要) 依次修改各服务器上配置文件的的broker.id,分别是0,1,2不得重复。12345678910# 需要配置kafka的环境变量# 6、启动集群**依次在各节点上启动kafka** 后台启动 `nohup最后加一个&` ```bashKAFKA_HOME/bin/kafka-server-start.sh KAFKA_HOME/config/server.properties & 配置文件及注释123456789101112131415161718192021222324252627282930313233343536373839broker.id=0#当前机器在集群中的唯一标识,和zookeeper的myid性质一样port=9092#当前kafka对外提供服务的端口默认是9092host.name=192.168.11.11advertised.host.name=192.168.11.11#这个参数默认是关闭的,在0.8.1有个bug,DNS解析问题,失败率的问题。num.network.threads=3#这个是borker进行网络处理的线程数num.io.threads=8#这个是borker进行I/O处理的线程数log.dirs=/home/bigdata/apps/kafka/kafkalogs/#消息存放的目录,这个目录可以配置为“,”逗号分割的表达式,上面的num.io.threads要大于这个目录的个数这个目录,如果配置多个目录,新创建的topic他把消息持久化的地方是,当前以逗号分割的目录中,那个分区数最少就放那一个socket.send.buffer.bytes=102400#发送缓冲区buffer大小,数据不是一下子就发送的,先回存储到缓冲区了到达一定的大小后在发送,能提高性能socket.receive.buffer.bytes=102400#kafka接收缓冲区大小,当数据到达一定大小后在序列化到磁盘socket.request.max.bytes=104857600#这个参数是向kafka请求消息或者向kafka发送消息的请请求的最大数,这个值不能超过java的堆栈大小num.partitions=1#默认的分区数,一个topic默认1个分区数log.retention.hours=168#默认消息的最大持久化时间,168小时,7天message.max.bytes=5242880#消息保存的最大值5Mdefault.replication.factor=2#kafka保存消息的副本数,如果一个副本失效了,另一个还可以继续提供服务replica.fetch.max.bytes=5242880#取消息的最大直接数log.segment.bytes=1073741824#这个参数是:因为kafka的消息是以追加的形式落地到文件,当超过这个值的时候,kafka会新起一个文件log.retention.check.interval.ms=300000#每隔300000毫秒去检查上面配置的log失效时间(log.retention.hours=168 ),到目录查看是否有过期的消息如果有,删除log.cleaner.enable=false#是否启用log压缩,一般不用启用,启用的话可以提高性能zookeeper.connect=192.168.11.11:2181,192.168.11.12:2181,192.168.11.13:2181#设置zookeeper的连接端口 常用12345678910111213141516171819202122232425broker.id =0#每一个broker在集群中的唯一表示,要求是正数。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况log.dirs=/data/kafka-logs#kafka数据的存放地址,多个地址的话用逗号分割 /data/kafka-logs-1,/data/kafka-logs-2port =9092#broker server服务端口message.max.bytes =6525000#表示消息体的最大大小,单位是字节num.network.threads =3#broker处理消息的最大线程数,一般情况下不需要去修改 配置了三台服务器,所以选择三个num.io.threads =8#broker处理磁盘IO的线程数,数值应该大于你的硬盘数background.threads =4#一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改queued.max.requests =500#等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息,应该是一种自我保护机制。socket.send.buffer.bytes=100*1024#socket的发送缓冲区,socket的调优参数SO_SNDBUFFsocket.receive.buffer.bytes =100*1024#socket的接受缓冲区,socket的调优参数SO_RCVBUFFsocket.request.max.bytes =100*1024*1024#socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖log.segment.bytes =1024*1024*1024#topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,会被topic创建时的指定参数覆盖log.roll.hours =24*7 详解123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114broker.id =0#每一个broker在集群中的唯一表示,要求是正数。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况log.dirs=/data/kafka-logs#kafka数据的存放地址,多个地址的话用逗号分割 /data/kafka-logs-1,/data/kafka-logs-2port =9092#broker server服务端口message.max.bytes =6525000#表示消息体的最大大小,单位是字节num.network.threads =4#broker处理消息的最大线程数,一般情况下不需要去修改num.io.threads =8#broker处理磁盘IO的线程数,数值应该大于你的硬盘数background.threads =4#一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改queued.max.requests =500#等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息,应该是一种自我保护机制。host.name#broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置socket.send.buffer.bytes=100*1024#socket的发送缓冲区,socket的调优参数SO_SNDBUFFsocket.receive.buffer.bytes =100*1024#socket的接受缓冲区,socket的调优参数SO_RCVBUFFsocket.request.max.bytes =100*1024*1024#socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖log.segment.bytes =1024*1024*1024#topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,会被topic创建时的指定参数覆盖log.roll.hours =24*7#这个参数会在日志segment没有达到log.segment.bytes设置的大小,也会强制新建一个segment会被 topic创建时的指定参数覆盖log.cleanup.policy = delete#日志清理策略选择有:delete和compact主要针对过期数据的处理,或是日志文件达到限制的额度,会被 topic创建时的指定参数覆盖log.retention.minutes=60*24 # 一天后删除#数据存储的最大时间超过这个时间会根据log.cleanup.policy设置的策略处理数据,也就是消费端能够多久去消费数据#log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖log.retention.bytes=-1#topic每个分区的最大文件大小,一个topic的大小限制 = 分区数*log.retention.bytes。-1没有大小限log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖log.retention.check.interval.ms=5minutes#文件大小检查的周期时间,是否处罚 log.cleanup.policy中设置的策略log.cleaner.enable=false#是否开启日志压缩log.cleaner.threads = 2#日志压缩运行的线程数log.cleaner.io.max.bytes.per.second=None#日志压缩时候处理的最大大小log.cleaner.dedupe.buffer.size=500*1024*1024#日志压缩去重时候的缓存空间,在空间允许的情况下,越大越好log.cleaner.io.buffer.size=512*1024#日志清理时候用到的IO块大小一般不需要修改log.cleaner.io.buffer.load.factor =0.9#日志清理中hash表的扩大因子一般不需要修改log.cleaner.backoff.ms =15000#检查是否处罚日志清理的间隔log.cleaner.min.cleanable.ratio=0.5#日志清理的频率控制,越大意味着更高效的清理,同时会存在一些空间上的浪费,会被topic创建时的指定参数覆盖log.cleaner.delete.retention.ms =1day#对于压缩的日志保留的最长时间,也是客户端消费消息的最长时间,同log.retention.minutes的区别在于一个控制未压缩数据,一个控制压缩后的数据。会被topic创建时的指定参数覆盖log.index.size.max.bytes =10*1024*1024#对于segment日志的索引文件大小限制,会被topic创建时的指定参数覆盖log.index.interval.bytes =4096#当执行一个fetch操作后,需要一定的空间来扫描最近的offset大小,设置越大,代表扫描速度越快,但是也更好内存,一般情况下不需要搭理这个参数log.flush.interval.messages=None#log文件”sync”到磁盘之前累积的消息条数,因为磁盘IO操作是一个慢操作,但又是一个”数据可靠性"的必要手段,所以此参数的设置,需要在"数据可靠性"与"性能"之间做必要的权衡.如果此值过大,将会导致每次"fsync"的时间较长(IO阻塞),如果此值过小,将会导致"fsync"的次数较多,这也意味着整体的client请求有一定的延迟.物理server故障,将会导致没有fsync的消息丢失.log.flush.scheduler.interval.ms =3000#检查是否需要固化到硬盘的时间间隔log.flush.interval.ms = None#仅仅通过interval来控制消息的磁盘写入时机,是不足的.此参数用于控制"fsync"的时间间隔,如果消息量始终没有达到阀值,但是离上一次磁盘同步的时间间隔达到阀值,也将触发.log.delete.delay.ms =60000#文件在索引中清除后保留的时间一般不需要去修改log.flush.offset.checkpoint.interval.ms =60000#控制上次固化硬盘的时间点,以便于数据恢复一般不需要去修改auto.create.topics.enable =true#是否允许自动创建topic,若是false,就需要通过命令创建topicdefault.replication.factor =1#是否允许自动创建topic,若是false,就需要通过命令创建topicnum.partitions =1#每个topic的分区个数,若是在topic创建时候没有指定的话会被topic创建时的指定参数覆盖##这是轻量级的配置文件broker.id=0#当前机器在集群中的唯一标识,和zookeeper的myid性质一样port=9092#当前kafka对外提供服务的端口默认是9092host.name=192.168.11.11#这个参数默认是关闭的,在0.8.1有个bug,DNS解析问题,失败率的问题。num.network.threads=3#这个是borker进行网络处理的线程数num.io.threads=8#这个是borker进行I/O处理的线程数log.dirs=/home/hadoop/logs/kafka-logs #消息存放的目录,这个目录可以配置为“,”逗号分割的表达式,上面的num.io.threads要大于这个目录的个数这个目录,如果配置多个目录,新创建的topic他把消息持久化的地方是,当前以逗号分割的目录中,那个分区数最少就放那一个socket.send.buffer.bytes=102400#发送缓冲区buffer大小,数据不是一下子就发送的,先回存储到缓冲区了到达一定的大小后在发送,能提高性能socket.receive.buffer.bytes=102400#kafka接收缓冲区大小,当数据到达一定大小后在序列化到磁盘socket.request.max.bytes=104857600#这个参数是向kafka请求消息或者向kafka发送消息的请请求的最大数,这个值不能超过java的堆栈大小num.partitions=1#默认的分区数,一个topic默认1个分区数log.retention.hours=168#默认消息的最大持久化时间,168小时,7天message.max.byte=5242880#消息保存的最大值5Mdefault.replication.factor=2#kafka保存消息的副本数,如果一个副本失效了,另一个还可以继续提供服务replica.fetch.max.bytes=5242880#取消息的最大直接数log.segment.bytes=1073741824#这个参数是:因为kafka的消息是以追加的形式落地到文件,当超过这个值的时候,kafka会新起一个文件log.retention.check.interval.ms=300000#每隔300000毫秒去检查上面配置的log失效时间(log.retention.hours=168 ),到目录查看是否有过期的消息如果有,删除log.cleaner.enable=false#是否启用log压缩,一般不用启用,启用的话可以提高性能zookeeper.connect=192.168.11.11:2181,192.168.11.12:2181,192.168.11.13:2181#设置zookeeper的连接端口 以下是kafka中Leader,replicas配置参数1234567891011121314151617181920212223242526272829303132333435363738controller.socket.timeout.ms =30000#partition leader与replicas之间通讯时,socket的超时时间controller.message.queue.size=10#partition leader与replicas数据同步时,消息的队列尺寸replica.lag.time.max.ms =10000#replicas响应partition leader的最长等待时间,若是超过这个时间,就将replicas列入ISR(in-sync replicas),并认为它是死的,不会再加入管理中replica.lag.max.messages =4000#如果follower落后与leader太多,将会认为此follower[或者说partition relicas]已经失效###通常,在follower与leader通讯时,因为网络延迟或者链接断开,总会导致replicas中消息同步滞后##如果消息之后太多,leader将认为此follower网络延迟较大或者消息吞吐能力有限,将会把此replicas迁移##到其他follower中.##在broker数量较少,或者网络不足的环境中,建议提高此值.replica.socket.timeout.ms=30*1000#follower与leader之间的socket超时时间replica.socket.receive.buffer.bytes=64*1024#leader复制时候的socket缓存大小replica.fetch.max.bytes =1024*1024#replicas每次获取数据的最大大小replica.fetch.wait.max.ms =500#replicas同leader之间通信的最大等待时间,失败了会重试replica.fetch.min.bytes =1#fetch的最小数据尺寸,如果leader中尚未同步的数据不足此值,将会阻塞,直到满足条件num.replica.fetchers=1#leader进行复制的线程数,增大这个数值会增加follower的IOreplica.high.watermark.checkpoint.interval.ms =5000#每个replica检查是否将最高水位进行固化的频率controlled.shutdown.enable =false#是否允许控制器关闭broker ,若是设置为true,会关闭所有在这个broker上的leader,并转移到其他brokercontrolled.shutdown.max.retries =3#控制器关闭的尝试次数controlled.shutdown.retry.backoff.ms =5000#每次关闭尝试的时间间隔leader.imbalance.per.broker.percentage =10#leader的不平衡比例,若是超过这个数值,会对分区进行重新的平衡leader.imbalance.check.interval.seconds =300#检查leader是否不平衡的时间间隔offset.metadata.max.bytes#客户端保留offset信息的最大空间大小 kafka中zookeeper参数配置1234567zookeeper.connect = bigdata1:2181,bigdata2:2181,bigdata3:2181#zookeeper集群的地址,可以是多个,多个之间用逗号分割 hostname1:port1,hostname2:port2,hostname3:port3zookeeper.session.timeout.ms=6000#ZooKeeper的最大超时时间,就是心跳的间隔,若是没有反映,那么认为已经死了,不易过大zookeeper.connection.timeout.ms =6000#ZooKeeper的连接超时时间zookeeper.sync.time.ms =2000","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Kafka","slug":"Kafka","permalink":"http://gangtieguo.cn/tags/Kafka/"},{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/tags/大数据/"}]},{"title":"Kafka详细解析","slug":"Kafka详细解析","date":"2018-08-13T06:50:40.913Z","updated":"2019-07-04T04:46:51.548Z","comments":true,"path":"2018/08/13/Kafka详细解析/","link":"","permalink":"http://gangtieguo.cn/2018/08/13/Kafka详细解析/","excerpt":"[TOC] Kafka是一个分布式消息队列:生产者、消费者的功能。它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现。 Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)成为broker。 无论是kafka集群,还是producer和consumer都依赖于zookeeper集群保存一些meta信息,来保证系统可用性 JMS的基础JMS是什么?:JMS是Java提供的一套技术规范 JMS做什么?:用来异构系统 集成通信,缓解系统瓶颈,提高系统的伸缩性增强系统用户体验,使得系统模块化和组件化变得可行并更加灵活 JMS通过什么方式?:生产消费者模式(生产者、服务器、消费者) jdk,kafka,activemq……","text":"[TOC] Kafka是一个分布式消息队列:生产者、消费者的功能。它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现。 Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)成为broker。 无论是kafka集群,还是producer和consumer都依赖于zookeeper集群保存一些meta信息,来保证系统可用性 JMS的基础JMS是什么?:JMS是Java提供的一套技术规范 JMS做什么?:用来异构系统 集成通信,缓解系统瓶颈,提高系统的伸缩性增强系统用户体验,使得系统模块化和组件化变得可行并更加灵活 JMS通过什么方式?:生产消费者模式(生产者、服务器、消费者) jdk,kafka,activemq…… JMS消息传输模型点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)点对点模型通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端。这个模型的特点是发送到队列的消息被一个且只有一个接收者接收处理,即使有多个消息监听者也是如此。 发布/订阅模式(一对多,数据生产后,推送给所有订阅者)发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅者,临时订阅者只在主动监听主题时才接收消息,而持久订阅者则监听主题的所有消息,即时当前订阅者不可用,处于离线状态。 queue.put(object) 数据生产 queue.take(object) 数据消费 kafka是采用的类jms模式,与类jms模式区别是: jms两种模式: 推送的话可以多个 拉取的话只能一个消费者,因为消费完,消息数据就会不存在了。kafka解决了这种弊端,拉取模式下也可以多个消费者,因为消息可以持久化到硬盘,就算消费了也是存在的。Kafka中ack机制也可以保证消息完整被处理。 kafka是什么类JMS消息队列,结合JMS中的两种模式,可以有多个消费者主动拉取数据,在JMS中只有点对点模式才有消费者主动拉取数据。kafka是一个生产-消费模型。 Producer:生产者,只负责数据生产,生产者的代码可以集成到任务系统中。 数据的分发策略由producer决定,默认是defaultPartition:策略为Utils.abs(key.hashCode) % numPartitions Broker:当前服务器上的Kafka进程。只管数据存储,不管是谁生产,不管是谁消费。在集群中每个broker都有一个唯一brokerid,不得重复。 Topic: 目标发送的目的地,这是一个逻辑上的概念,落到磁盘上是一个partition的目录。partition的目录中有多个segment组合(index,log) 一个Topic对应多个partition[0,1,2,3],一个partition对应多个segment组合。一个segment有默认的大小是1G。 每个partition可以设置多个副本(replication-factor 1),会从所有的副本中选取一个leader出来。所有读写操作都是通过leader来进行的。 ConsumerGroup:数据消费者组,ConsumerGroup可以有多个,每个ConsumerGroup消费的数据都是一样的。可以把多个consumer线程划分为一个组,组里面所有成员共同消费一个topic的数据,组员之间不能重复消费。 特别强调,和mysql中主从有区别,mysql做主从是为了读写分离,在kafka中读写操作都是leader。 Kafka核心组件Topic :消息根据Topic进行归类Producer:发送消息者Consumer:消息接受者broker:每个kafka实例(server)Zookeeper:依赖集群保存meta信息。 为什么需要消息队列消息系统的核心作用就是三点:解耦,异步和并行 消息队列和rpc调用区别消息队列并不关心是哪个消费者消费了数据,发布成功后就不必管消息队列的内容是否被消费,但是rpc调用的话,必须要给调用的系统返回一个状态码 以下为针对Kafka的一些总结 kafka生产数据时的分组策略-partition与broker关系,分区策略 partition与broker关系 数据生产到集群中的哪一个partition,由生产者决定。 通俗的说就是:默认使用hash算法,由parition的数量和broker的数量做一个hash partition的分配 将所有Broker(假设共n个Broker)和待分配的Partition排序 将第i个Partition分配到第(i mod n)个Broker上 (这个就是leader) 将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上 123456int i = 0list{kafka01,kafka02,kafka03}for(int i=0;i<5;i++){ brIndex = i%broker; hostName = list.get(brIndex)} 分区策略 默认是defaultPartition Utils.abs(key.hashCode) % numPartitions key是producer在发送数据时传入的,然后发送produer.send(KeyedMessage(topic,myPartitionKey,messageContent)) kafka如何保证数据的完全生产Producer消息发送的应答ack机制:broker表示发来的数据已确认接收无误,表示数据已经保存到磁盘。有三个选项 0:不等待broker返回确认消息 1:等待topic中某个partition leader保存成功的状态反馈 -1:等待topic中某个partition 所有副本都保存成功的状态反馈 broker如何保存数据在理论环境下,broker按照顺序读写的机制,可以每秒保存600M的数据。主要通过pagecache机制,尽可能的利用当前物理机器上的空闲内存来做缓存。 kafka的数据是如何保存到硬盘的当前topic所属的broker,必定有一个该topic的partition,partition是一个磁盘目录。partition的目录中有多个segment组合(index,log) 每个topic被分为多个partition,每个partition为一个目录,partition目录的规则为topic+有序的序号,每个partition都是一个大的目录,相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件中。但每个段segment file消息数量不一定相等,这种特性方便old segment file快速被删除,默认保留7天的数据。 Segment file组成:由2大部分组成,分别为index file和data file,索引文件、数据文件。索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址 查找消息文件步骤:先通过二分查找查找到index文件,然后在通过index文件,找到log文件,在定位到偏移量文件 消费组组员和partition之间如何做负载均衡最好是一一对应,一个partition对应一个consumer,如果consumer的数量过多,必然有空闲的consumer。算法理论如下: (消费者数量少的话:有一个内部的机制做负载均衡,就是首先有一个基数M,消费者数量除以分区数) 当一个group中,有consumer加入或者离开时,会触发partitions均衡.均衡的最终目的,是提升topic的并发消费能力,步骤如下: 123456假如topic1,具有如下partitions: P0,P1,P2,P3加入group中,有如下consumer: C1,C2首先根据partition索引号对partitions排序: P0,P1,P2,P3根据consumer.id排序: C0,C1计算倍数: M = [P0,P1,P2,P3].size / [C0,C1].size,本例值M=2(向上取整)然后依次分配partitions: C0 = [P0,P1],C1=[P2,P3],即Ci = [P(i * M),P((i + 1) * M -1)] 消费者通过zk记录offset来标记消费状态 消息如何保证在partition内有序partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。 如何保证kafka消费者消费数据是全局有序的如果要全局有序的,必须保证生产有序,存储有序,消费有序。由于生产可以做集群,存储可以分片,消费可以设置为一个consumerGroup,要保证全局有序,就需要保证每个环节都有序。只有一个可能,就是一个生产者,一个partition,一个消费者。这种场景和大数据应用场景相悖。 生产者是集群模式–》全局序号管理器 broker断只设置一个partion-》kafka的高并发下的负载均衡 消费者如果是一个组,用一个线程来消费(并且自定义一个数据结构来排序),才能保障消息有序消费来一个线程 kafka为什么这么快?首先kafka依赖于操作系统的pageCache机制,尽可能的把空闲的内存作为一个磁盘,只有发生缺页的才会放在磁盘中,那么落在磁盘上的话,还有就是采用的是sendfile机制:主要思想就是在内核中进行拷贝,再写socket流,也就是:内核->内核->socket,传统io是数据流通:内核->用户区->内核->socket流,这样就算磁盘的话,也是相当快的 不被完整处理,会造成结果?是否开启ack-fail机制需要根据业务场景来 在大数据操作点击流数据基本上是不开启的,点击流日志中一条pv,uv数据丢失不会造成什么影响。","categories":[{"name":"总结","slug":"总结","permalink":"http://gangtieguo.cn/categories/总结/"}],"tags":[{"name":"Kafka","slug":"Kafka","permalink":"http://gangtieguo.cn/tags/Kafka/"}]},{"title":"Hive累计报表","slug":"Hive累计报表","date":"2018-08-13T03:49:13.857Z","updated":"2019-06-17T04:40:09.385Z","comments":true,"path":"2018/08/13/Hive累计报表/","link":"","permalink":"http://gangtieguo.cn/2018/08/13/Hive累计报表/","excerpt":"[TOC] 在 hive 做统计的时候,总是涉及到做累计的报表处理,下面案列就是来做相应处理 准备创建数据文件(在hadoop所在的机器) vim /usr/hadoop/hivedata/t_sales.dat 12345678910舒肤佳,2018-06,5舒肤佳,2018-06,15美姿,2018-06,5舒肤佳,2018-06,8美姿,2018-06,25舒肤佳,2018-06,5舒肤佳,2018-07,4舒肤佳,2018-07,6美姿,2018-07,10美姿,2018-07,5 上传到hdfs 1hadoop fs -put /usr/hadoop/hivedata/t_sales.dat /local/hivedata/t_sales.dat","text":"[TOC] 在 hive 做统计的时候,总是涉及到做累计的报表处理,下面案列就是来做相应处理 准备创建数据文件(在hadoop所在的机器) vim /usr/hadoop/hivedata/t_sales.dat 12345678910舒肤佳,2018-06,5舒肤佳,2018-06,15美姿,2018-06,5舒肤佳,2018-06,8美姿,2018-06,25舒肤佳,2018-06,5舒肤佳,2018-07,4舒肤佳,2018-07,6美姿,2018-07,10美姿,2018-07,5 上传到hdfs 1hadoop fs -put /usr/hadoop/hivedata/t_sales.dat /local/hivedata/t_sales.dat 创建表及读取数据123create table t_sales(brandname string,month string,sales int)row format delimited fields terminated by ',';load data inpath '/local/hivedata/t_sales.dat' into table t_sales; 如果是上传本地文件(如果在hive所在主机上) 则在load data 后加 local,如 load data local inpath '/usr/hadoop/hivedata/t_sales.dat' into table t_sales; 1、先求每个品牌的月总金额1select brandname,month,sum(sales) as all_sales from t_sales group by brandname,month 2、将月总金额自连接1234567select * from (select brandname,month,sum(sales) as sal from t_sales group by brandname,month) A inner join (select brandname,month,sum(sales) as sal from t_sales group by brandname,month) B onA.brandname=B.brandnamewhere B.month <= A.month; 3、从上一步的结果中进行分组查询分组的字段是 a.brandname a.month 求月累计值: 将 b.month <= a.month 的所有 b.sals求和即可 12345678910select A.brandname,A.month,max(A.sales) as sales,sum(B.sales) as accumulatefrom (select brandname,month,sum(sales) as sales from t_sales group by brandname,month) A inner join (select brandname,month,sum(sales) as sales from t_sales group by brandname,month) BonA.brandname=B.brandnamewhere B.month <= A.monthgroup by A.brandname,A.monthorder by A.brandname,A.month;","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hive","slug":"Hive","permalink":"http://gangtieguo.cn/tags/Hive/"},{"name":"报表","slug":"报表","permalink":"http://gangtieguo.cn/tags/报表/"}]},{"title":"Hive分桶表,分区表简单分析","slug":"Hive分桶表相关","date":"2018-08-11T03:21:32.532Z","updated":"2019-06-17T04:40:09.384Z","comments":true,"path":"2018/08/11/Hive分桶表相关/","link":"","permalink":"http://gangtieguo.cn/2018/08/11/Hive分桶表相关/","excerpt":"[TOC] 对于每一个表或者是分区,Hive 可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive 是针对某一列进行分桶。Hive 采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶中。分桶的好处是可以获得更高的查询处理效率。使取样更高效。 分桶依赖于yarn的所以分桶的时候需要启动yarn","text":"[TOC] 对于每一个表或者是分区,Hive 可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive 是针对某一列进行分桶。Hive 采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶中。分桶的好处是可以获得更高的查询处理效率。使取样更高效。 分桶依赖于yarn的所以分桶的时候需要启动yarn 分桶表创建#设置变量,设置分桶为true, 设置reduce数量是分桶的数量个数 12set hive.enforce.bucketing = true;set mapreduce.job.reduces=4; 创建表 123456create table person_buck(id int,name string,sex string,age int)clustered by(id) sorted by(id DESC)into 4 bucketsrow format delimitedfields terminated by ','; 123开会往创建的分桶表插入数据(插入数据需要是已分桶, 且排序的)可以使用distribute by(id) sort by(id asc) 或是排序和分桶的字段相同的时候使用Cluster by(字段)注意使用cluster by 就等同于分桶+排序(sort) 1insert into table person_buck select id,name,sex,age from student distribute by(id) sort by(id asc); 分桶模式的参数设置变量,设置分桶为true, 设置reduce数量是分桶的数量个数 set hive.enforce.bucketing = true; 不设置reduce的数量会使用默认的数量,默认的数量会和分桶的数量不一致,则不能分出正确分桶 set mapreduce.job.reduces=4; 本地模式 set hive.exec.mode.local.auto=true 动态分区 –设置为true表示开启动态分区功能(默认为false) set hive.exec.dynamic.partition=true; –设置为nonstrict,表示允许所有分区都是动态的(默认为strict) set hive.exec.dynamic.partition.mode=nonstrict; Update与分桶表关系Hive对使用Update功能的表有特定的语法要求, 语法要求如下:(1)要执行Update的表中, 建表时必须带有buckets(分桶)属性(2)要执行Update的表中, 需要指定格式,其余格式目前赞不支持, 如:parquet格式, 目前只支持ORCFileformat和AcidOutputFormat(3)要执行Update的表中, 建表时必须指定参数(‘transactional’ = true);举例: 1create table student (id bigint,name string) clustered by (name) into 2 buckets stored as orc TBLPROPERTIES('transactional'='true'); 更新语句:1update student set id='444' where name='tom'; 分桶表测试这个例子就是将分区的字段进行hash散列将数据分桶到分桶数个文件中去 导入一个文件到分桶表里面 创建表1create table t_buk(id int,name string) clustered by(id) sorted by(id DESC) into 4 buckets row format delimited``fields terminated by ','; 创建数据cd /usr/hive/hivedata/ vim buk.txt 1234567891011121,数据库2,数学3,信息系统4,操作系统5,数据结构6,数据no7,数据other8,数据time9,数据操作10,数据挖掘11,数据挖机12,数据信号 读取本地文件1load data local inpath '/usr/hive/hivedata/buk.txt' into table t_buk; load方式这样导入数据到一个分桶表里面,是不会作出分桶的操作的,不会分成桶数个文件,还是一个文件在hdfs系统中 注意 要想导入到数据到分桶表里面,必须是一个是已经是分桶的数据,比如已经形成了分桶数据个文件,才可以导入到分桶表里面,导数据的时候是不会将原来的数据形式变成分桶的数据形式 hive分桶表的使用场景所以一般是在一个表中查询了数据然后在塞入到一个分区表里面,查询是走mapReduce程序,然后将数据按分桶表照分桶的策略写入到分桶表中 形如 1insert into t_buk select * from other … …; 后面的 清除数据 1truncate table t_buk; 创建一个表来读取数据 12345create table t_p(id int,name string)row format delimitedfields terminated by ',';load data local inpath '/usr/hive/hivedata/buk.txt' into table t_p; insert into table t_buk select id,name from t_p; insert overwirte 也可以 结果如下1234567891011121314151617Number of reduce tasks is set to 0 since there's no reduce operatorINFO : number of splits:1INFO : Submitting tokens for job: job_1502537431423_0011INFO : The url to track the job: http://bigdata1:8088/proxy/application_1502537431423_0011/INFO : Starting Job = job_1502537431423_0011, Tracking URL = http://bigdata1:8088/proxy/application_1502537431423_0011/INFO : Kill Command = /home/bigdata/apps/hadoop/bin/hadoop job -kill job_1502537431423_0011INFO : Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 0INFO : 2017-08-15 21:12:15,669 Stage-1 map = 0%, reduce = 0%INFO : 2017-08-15 21:12:32,386 Stage-1 map = 100%, reduce = 0%, Cumulative CPU 1.27 secINFO : MapReduce Total cumulative CPU time: 1 seconds 270 msecINFO : Ended Job = job_1502537431423_0011INFO : Stage-4 is selected by condition resolver.INFO : Stage-3 is filtered out by condition resolver.INFO : Stage-5 is filtered out by condition resolver.INFO : Moving data to: hdfs://bigdata1:9000/user/hive/warehouse/t_buk/.hive-staging_hive_2017-08-15_21-12-02_490_4088487413275551800-3/-ext-10000 from hdfs://bigdata1:9000/user/hive/warehouse/t_buk/.hive-staging_hive_2017-08-15_21-12-02_490_4088487413275551800-3/-ext-10002INFO : Loading data to table default.t_buk from hdfs://bigdata1:9000/user/hive/warehouse/t_buk/.hive-staging_hive_2017-08-15_21-12-02_490_4088487413275551800-3/-ext-10000INFO : Table default.t_buk stats: [numFiles=1, numRows=12, totalSize=167, rawDataSize=155] 查看hdfs管理页面 50070 还是只有一文件,表示分桶不成功,没设reduce数量,使用默认的数量1,和我们期望分桶数量不一致 设置分桶参数因为没有启动模式的开关,如下 设置变量,设置分桶为true, 设置reduce数量是分桶的数量个数 12345set hive.enforce.bucketing = true;set mapreduce.job.reduces=4;set hive.exec.mode.local.auto=true;set hive.exec.dynamic.partition=true;set hive.exec.dynamic.partition.mode=nonstrict; 重新创建表可以通过set hive.enforce.bucketing查看是否设置成功 先查看sort by (id); 根据4个reduce来局部有序,每个reduce有序,但是从哪儿截断每个reduce并不确定 1234567891011121314151617181920212223242526272829303132333435363738select id,name from t_p sort by (id);INFO : Number of reduce tasks not specified. Defaulting to jobconf value of: 4INFO : In order to change the average load for a reducer (in bytes):INFO : set hive.exec.reducers.bytes.per.reducer=<number>INFO : In order to limit the maximum number of reducers:INFO : set hive.exec.reducers.max=<number>INFO : In order to set a constant number of reducers:INFO : set mapreduce.job.reduces=<number>INFO : number of splits:1INFO : Submitting tokens for job: job_1502537431423_0012INFO : The url to track the job: http://bigdata1:8088/proxy/application_1502537431423_0012/INFO : Starting Job = job_1502537431423_0012, Tracking URL = http://bigdata1:8088/proxy/application_1502537431423_0012/INFO : Kill Command = /home/bigdata/apps/hadoop/bin/hadoop job -kill job_1502537431423_0012INFO : Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 4INFO : 2017-08-15 21:26:14,284 Stage-1 map = 0%, reduce = 0%INFO : 2017-08-15 21:26:24,633 Stage-1 map = 100%, reduce = 0%, Cumulative CPU 2.29 secINFO : 2017-08-15 21:26:38,745 Stage-1 map = 100%, reduce = 50%, Cumulative CPU 6.99 secINFO : 2017-08-15 21:26:43,887 Stage-1 map = 100%, reduce = 67%, Cumulative CPU 6.99 secINFO : 2017-08-15 21:26:46,970 Stage-1 map = 100%, reduce = 75%, Cumulative CPU 9.06 secINFO : 2017-08-15 21:26:50,081 Stage-1 map = 100%, reduce = 100%, Cumulative CPU 10.8 secINFO : MapReduce Total cumulative CPU time: 10 seconds 800 msecINFO : Ended Job = job_1502537431423_0012+-----+----------+--+| id | name |+-----+----------+--+| 4 | 操作系统 || 8 | 数据time || 12 | 数据信号 || 2 | 数学 || 6 | 数据no || 1 | 数据库 || 3 | 信息系统 || 5 | 数据结构 || 10 | 数据挖掘 || 11 | 数据挖机 || 7 | 数据other || 9 | 数据操作 |+-----+----------+--+ 再试一次select 插入(将t_buk truncate也可,也可使用overwrite关键字) insert overwrite table t_buk select id,name from t_p cluster by (id); 再查看hdfs ui页面50070 再分别查看这几个文件1234$HADOOP_HOME/bin/hadoop fs -cat /user/hive/warehouse/t_buk/000000_0$HADOOP_HOME/bin/hadoop fs -cat /user/hive/warehouse/t_buk/000001_0 $HADOOP_HOME/bin/hadoop fs -cat /user/hive/warehouse/t_buk/000002_0 $HADOOP_HOME/bin/hadoop fs -cat /user/hive/warehouse/t_buk/000003_0 得到结果12345678910111213141516[bigdata@master hivedata]$ hadoop fs -cat /user/hive/warehouse/t_buk/000000_0 4,操作系统8,数据time12,数据信号[bigdata@master hivedata]$ hadoop fs -cat /user/hive/warehouse/t_buk/000001_0 1,数据库5,数据结构9,数据操作[bigdata@master hivedata]$ hadoop fs -cat /user/hive/warehouse/t_buk/000002_0 2,数学6,数据no10,数据挖掘[bigdata@master hivedata]$ hadoop fs -cat /user/hive/warehouse/t_buk/000003_03,信息系统7,数据other11,数据挖机 分桶表疑问为什么每个桶里面的数据条数不一样1hash散列的时候数据可能将数据有的分的多,有的分的少 cluster by (id) 根据id分桶,桶内根据id排序,相当于 distribute by 和 sort by的集合,只是指定的字段都是同一个用两个组合更加强大,分桶字段排序字段可以设置为不同 分桶表的意义:提高join操作的效率案例 如果a表和b表已经是分桶表,而且分桶的字段都是是id字段做这个join操作是,还需要做笛卡尔积吗? 这样不需要,因为同一id哈希后的数据是一致的,这就是分桶表存在的意义 注意 在分桶表中使用order by 是非常不建议的,这样会设置成一个reduce,强行将数据写入,一个reduce的内存会爆炸 使用cluster by 就等同于分桶+排序(sort) insert overwrite table student_buck select * from student cluster by(Sno) sort by(Sage); 报错,cluster 和 sort 不能共存","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hive","slug":"Hive","permalink":"http://gangtieguo.cn/tags/Hive/"}]},{"title":"Hive自定义函数UDF相关","slug":"Hive自定义函数流程","date":"2018-08-11T03:16:01.806Z","updated":"2018-08-13T03:19:44.441Z","comments":true,"path":"2018/08/11/Hive自定义函数流程/","link":"","permalink":"http://gangtieguo.cn/2018/08/11/Hive自定义函数流程/","excerpt":"[TOC] UDF开发及使用 打成jar包上传到服务器,将jar包添加到hive的classpath 1或者 hive>add JAR /home/hadoop/udf.jar; 创建临时函数与开发好的java class关联 1Hive>create temporary function runTime as 'me.yao.bigdata.udf.RunTime'; 即可在hql中使用自定义的函数time() Select time(name),age from t_test; add jar只在一次会话中生效","text":"[TOC] UDF开发及使用 打成jar包上传到服务器,将jar包添加到hive的classpath 1或者 hive>add JAR /home/hadoop/udf.jar; 创建临时函数与开发好的java class关联 1Hive>create temporary function runTime as 'me.yao.bigdata.udf.RunTime'; 即可在hql中使用自定义的函数time() Select time(name),age from t_test; add jar只在一次会话中生效 transform案例:可以不需要上传jar包1、加载数据先加载rating.json(链接)文件到hive的一个原始表 rat_json 12create table rat_json(line string) row format delimited;load data local inpath '/home/bigdata/apps/hive/hivedata/rating.json' into table rat_json; 2、解析字段需要解析json数据成四个字段,插入一张新的表 t_rating 12create table t_rating asselect get_json_object(line,'$.movie') as movieid,get_json_object(line,'$.rate')as rate,get_json_object(line,'$.timeStamp')as timestring,get_json_object(line,'$.uid')as uid from rat_json; 或者 12insert overwrite table t_ratingselect get_json_object(line,'$.movie') as movieid,get_json_object(line,'$.rate')as rate,get_json_object(line,'$.timeStamp')as timestring,get_json_object(line,'$.uid')as uid from rat_json; 3、转换weekday使用transform+python的方式去转换unixtime为weekday 先编辑一个python脚本文件 cd /usr/hive/hivedata/ vim weekday_mapper.py 123456789101112131415#!/bin/pythonimport sysimport datetimefor line in sys.stdin: line = line.strip() movieid, rating, unixtime,userid = line.split('\\t') weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday() print '\\t'.join([movieid, rating, str(weekday),userid]) 将文件加入classpath保存文件 然后,将文件加入hive的classpath: 1hive>add FILE /usr/hive/hivedata/weekday_mapper.py; 再创建表123456create TABLE u_data_new asSELECT TRANSFORM (movieid, rate, timestring,uid) USING 'python weekday_mapper.py' AS (movieid, rate, weekday,uid)FROM t_rating; 查询表1select distinct(weekday) from u_data_new limit 10;","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hive","slug":"Hive","permalink":"http://gangtieguo.cn/tags/Hive/"}]},{"title":"Hive建表及sql相关","slug":"Hive sql相关","date":"2018-08-11T02:31:24.062Z","updated":"2019-06-17T04:40:09.383Z","comments":true,"path":"2018/08/11/Hive sql相关/","link":"","permalink":"http://gangtieguo.cn/2018/08/11/Hive sql相关/","excerpt":"[TOC] hive主要是做离线日志分析的,不是为了做单行的事务控制的数据新版hive也支持单行数据的读取,但是效率非常低,所以也没有什么updata语句","text":"[TOC] hive主要是做离线日志分析的,不是为了做单行的事务控制的数据新版hive也支持单行数据的读取,但是效率非常低,所以也没有什么updata语句 hdfs的数据是放在hdfs里面的,表的描述的结构元数据信息是放在mysql里面hdfs中数据的信息在以下类似目录/user/hive/warehouse/thishive.db/book/country=japan 可以在hive的客户端直接敲用hdfs的命令查看到1hdfs dfs -ls /hive目录 本地模式set hive.exec.mode.local.auto=true; 建表(默认是内部表)1create table inner_table(id bigint, account string, income double, expenses double, time string) row format delimited fields terminated by '\\t'; 建分区表1create table outter_table(id bigint, account string, income double, expenses double, time string) partitioned by (logdate string) row format delimited fields terminated by '\\t'; 建外部表1create external table td_ext(id bigint, account string, income double, expenses double, time string) row format delimited fields terminated by '\\t' location '/td_ext'; localtion是表示存放的位置 复制表1create table 表1 like 表2 ; 将表2的结构复制到表1将文件的数据导入到表中 导入数据到表中 给表导入数据(若是分区表,则导入的时候需要加partition(#####)) 1load data local inpath '/home/hadoop/mylog.log' into table 表名 partition(datestr='2013-09-18') ; 如果是导入本地文件,需要加参数local,如果是hdfs上的话,则不加 导出hive表中数据1insert overwrite local directory '/home/hadoop/student.txt' select * from 表名; 不加local表示导出到hdfs 外部表和内部表的区别drop table 外部表; 只会将外部表的结构 元数据信息删除,而不会删除外表的数据drop table 内部表;会将内部表的结构元数据信息及其数据信息全部删除 保存select查询结果的几种方式:1234567891011121314151617181920211、将查询结果保存到一张新的hive表中create table t_tmpasselect * from t_p;2、将查询结果保存到一张已经存在的hive表中insert into table t_tmp select * from t_p;3、将查询结果保存到指定的文件目录(可以是本地,也可以是hdfs)本地insert overwrite local directory '/home/hadoop/student.txt'select * from student;导入到mysqlinsert overwrite directory '/aaa/test'select * from t_p; 分区表普通表和分区表区别:有大量数据增加的需要建分区表分区的字段会自动加在表结构上 这个是将导入到fruit的分区里面 1load data local inpath '/home/bigdata/food.txt' overwrite into table book partition (type='fruit'); 在hdfs里面,分区表会存在多个不同的目录,但是在查询的时候,还是将多个分区表的信息融入到一个表中 使用如果是使用overwrite命令,必须加stored as textfile; 小操作以下资源来自网络(若有不合适,请联系我) students.txt1234567891011121314151617181920212295001,李勇,男,20,CS95002,刘晨,女,19,IS95003,王敏,女,22,MA95004,张立,男,19,IS95005,刘刚,男,18,MA95006,孙庆,男,23,CS95007,易思玲,女,19,MA95008,李娜,女,18,CS95009,梦圆圆,女,18,MA95010,孔小涛,男,19,CS95011,包小柏,男,18,MA95012,孙花,女,20,CS95013,冯伟,男,21,CS95014,王小丽,女,19,CS95015,王君,男,18,MA95016,钱国,男,21,MA95017,王风娟,女,18,IS95018,王一,女,19,IS95019,邢小丽,女,19,IS95020,赵钱,男,21,IS95021,周二,男,17,MA95022,郑明,男,20,MA sc.txt1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818295001,1,8195001,2,8595001,3,8895001,4,7095002,2,9095002,3,8095002,4,7195002,5,6095003,1,8295003,3,9095003,5,10095004,1,8095004,2,9295004,4,9195004,5,7095005,1,7095005,2,9295005,3,9995005,6,8795006,1,7295006,2,6295006,3,10095006,4,5995006,5,6095006,6,9895007,3,6895007,4,9195007,5,9495007,6,7895008,1,9895008,3,8995008,6,9195009,2,8195009,4,8995009,6,10095010,2,9895010,5,9095010,6,8095011,1,8195011,2,9195011,3,8195011,4,8695012,1,8195012,3,7895012,4,8595012,6,9895013,1,9895013,2,5895013,4,8895013,5,9395014,1,9195014,2,10095014,4,9895015,1,9195015,3,5995015,4,10095015,6,9595016,1,9295016,2,9995016,4,8295017,4,8295017,5,10095017,6,5895018,1,9595018,2,10095018,3,6795018,4,7895019,1,7795019,2,9095019,3,9195019,4,6795019,5,8795020,1,6695020,2,9995020,5,9395021,2,9395021,5,9195021,6,9995022,3,6995022,4,9395022,5,8295022,6,100 course.txt1234561,数据库2,数学3,信息系统4,操作系统5,数据结构6,数据处理 建表1234567create table student(Sno int,Sname string,Sex string,Sage int,Sdept string)row format delimited fields terminated by ','stored as textfile;create table course(Cno int,Cname string) row format delimited fields terminated by ',' stored as textfile;create table sc(Sno int,Cno int,Grade int)row format delimited fields terminated by ',' stored as textfile;load data local inpath '/home/bigdata/apps/hive/hivedata/students.txt' overwrite into table student;load data local inpath '/home/bigdata/apps/hive/hivedata/sc.txt' overwrite into table sc;load data local inpath '/home/bigdata/apps/hive/hivedata/course.txt' overwrite into table course; sql需求123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081查询全体学生的学号与姓名 hive> select Sno,Sname from student;查询选修了课程的学生姓名 hive> select distinct Sname from student inner join sc on student.Sno=Sc.Sno;----hive的group by 和集合函数查询学生的总人数 hive> select count(distinct Sno)count from student;计算1号课程的学生平均成绩 hive> select avg(distinct Grade) from sc where Cno=1;查询各科成绩平均分 hive> select Cno,avg(Grade) from sc group by Cno; 查询选修1号课程的学生最高分数 select Grade from sc where Cno=1 sort by Grade desc limit 1; (注意比较:select * from sc where Cno=1 sort by Grade select Grade from sc where Cno=1 order by Grade) 求各个课程号及相应的选课人数 hive> select Cno,count(1) from sc group by Cno;查询选修了3门以上的课程的学生学号 hive> select Sno from (select Sno,count(Cno) CountCno from sc group by Sno)a where a.CountCno>3;或 hive> select Sno from sc group by Sno having count(Cno)>3; ----hive的Order By/Sort By/Distribute By Order By ,在strict 模式下(hive.mapred.mode=strict),order by 语句必须跟着limit语句,但是在nonstrict下就不是必须的,这样做的理由是必须有一个reduce对最终的结果进行排序,如果最后输出的行数过多,一个reduce需要花费很长的时间。查询学生信息,结果按学号全局有序 hive> set hive.mapred.mode=strict; <默认nonstrict>hive> select Sno from student order by Sno;FAILED: Error in semantic analysis: 1:33 In strict mode, if ORDER BY is specified, LIMIT must also be specified. Error encountered near token 'Sno' Sort By,它通常发生在每一个redcue里,“order by” 和“sort by”的区别在于,前者能给保证输出都是有顺序的,而后者如果有多个reduce的时候只是保证了输出的部分有序。set mapred.reduce.tasks=<number>在sort by可以指定,在用sort by的时候,如果没有指定列,它会随机的分配到不同的reduce里去。distribute by 按照指定的字段对数据进行划分到不同的输出reduce中 此方法会根据性别划分到不同的reduce中 ,然后按年龄排序并输出到不同的文件中。查询学生信息,按性别分区,在分区内按年龄有序 hive> set mapred.reduce.tasks=2; hive> insert overwrite local directory '/home/hadoop/out' select * from student distribute by Sex sort by Sage;----Join查询,join只支持等值连接 查询每个学生及其选修课程的情况 hive> select student.*,sc.* from student join sc on (student.Sno =sc.Sno);查询学生的得分情况。 hive>select student.Sname,course.Cname,sc.Grade from student join sc on student.Sno=sc.Sno join course on sc.cno=course.cno;查询选修2号课程且成绩在90分以上的所有学生。 hive> select student.Sname,sc.Grade from student join sc on student.Sno=sc.Sno where sc.Cno=2 and sc.Grade>90; ----LEFT,RIGHT 和 FULL OUTER JOIN ,inner join, left semi join查询所有学生的信息,如果在成绩表中有成绩,则输出成绩表中的课程号 hive> select student.Sname,sc.Cno from student left outer join sc on student.Sno=sc.Sno; 如果student的sno值对应的sc在中没有值,则会输出student.Sname null.如果用right out join会保留右边的值,左边的为null。 Join 发生在WHERE 子句之前。如果你想限制 join 的输出,应该在 WHERE 子句中写过滤条件——或是在join 子句中写。 ----LEFT SEMI JOIN Hive 当前没有实现 IN/EXISTS 子查询,可以用 LEFT SEMI JOIN 重写子查询语句重写以下子查询为LEFT SEMI JOIN SELECT a.key, a.value FROM a WHERE a.key exist in (SELECT b.key FROM B);可以被重写为: SELECT a.key, a.val FROM a LEFT SEMI JOIN b on (a.key = b.key)查询与“刘晨”在同一个系学习的学生 hive> select s1.Sname from student s1 left semi join student s2 on s1.Sdept=s2.Sdept and s2.Sname='刘晨';注意比较:select * from student s1 left join student s2 on s1.Sdept=s2.Sdept and s2.Sname='刘晨';select * from student s1 right join student s2 on s1.Sdept=s2.Sdept and s2.Sname='刘晨';select * from student s1 inner join student s2 on s1.Sdept=s2.Sdept and s2.Sname='刘晨';select * from student s1 left semi join student s2 on s1.Sdept=s2.Sdept and s2.Sname='刘晨';select s1.Sname from student s1 right semi join student s2 on s1.Sdept=s2.Sdept and s2.Sname='刘晨';","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Hive","slug":"Hive","permalink":"http://gangtieguo.cn/tags/Hive/"},{"name":"使用","slug":"使用","permalink":"http://gangtieguo.cn/tags/使用/"}]},{"title":"Json与Scala类型的相互转换处理","slug":"Json与Scala类型的一些互相转换处理","date":"2018-08-10T17:19:05.443Z","updated":"2019-06-17T04:40:09.388Z","comments":true,"path":"2018/08/11/Json与Scala类型的一些互相转换处理/","link":"","permalink":"http://gangtieguo.cn/2018/08/11/Json与Scala类型的一些互相转换处理/","excerpt":"[TOC] 在开发过程中时常会有对json数据的一些处理,现做一些记录","text":"[TOC] 在开发过程中时常会有对json数据的一些处理,现做一些记录 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283import com.alibaba.fastjson.{JSON, JSONArray, JSONObject}import com.fasterxml.jackson.databind.ObjectMapperimport com.fasterxml.jackson.module.scala.DefaultScalaModuleimport net.minidev.json.parser.JSONParserimport scala.collection.JavaConversions.mapAsScalaMapimport scala.collection.mutableimport java.util/** * json utils */object JsonUtils { val mapper: ObjectMapper = new ObjectMapper() def toJsonString(T: Object): String = { mapper.registerModule(DefaultScalaModule) mapper.writeValueAsString(T) } def getArrayFromJson(jsonStr: String) = { JSON.parseArray(jsonStr) } def getObjectFromJson(jsonStr: String): JSONObject = { JSON.parseObject(jsonStr) } /** * 配合getObjectFromJson 使用把 JSONObject 变为 map * @param jsonObj * @return */ def jsonObj2Map(jsonObj:JSONObject): mutable.Map[String, String] = { var map = mutable.Map[String, String]() val itr: util.Iterator[String] = jsonObj.keySet().iterator() while (itr.hasNext) { val key = itr.next() map += ((key, jsonObj.getString(key))) } map } /** * json 字符串转成 Map * #############有些情况下转换会有问题############### * @param json * @return */ def json2Map(json: String): mutable.HashMap[String,String] ={ val map : mutable.HashMap[String,String]= mutable.HashMap() val jsonParser =new JSONParser() //将string转化为jsonObject val jsonObj: JSONObject = jsonParser.parse(json).asInstanceOf[JSONObject] //获取所有键 val jsonKey = jsonObj.keySet() val iter = jsonKey.iterator() while (iter.hasNext){ val field = iter.next() val value = jsonObj.get(field).toString if(value.startsWith(\"{\")&&value.endsWith(\"}\")){ val value = mapAsScalaMap(jsonObj.get(field).asInstanceOf[util.HashMap[String, String]]) map.put(field,value.toString()) }else{ map.put(field,value) } } map } /** * map 转换成 json 字符串 * @param map * @return */ def map2Json(map : mutable.Map[String,String]): String = { import net.minidev.json.{JSONObject} import scala.collection.JavaConversions.mutableMapAsJavaMap val jsonString = JSONObject.toJSONString(map) jsonString }} 测试实例123456789101112131415161718def main(args: Array[String]) { val json = \"[{\\\"batchid\\\":305322456,\\\"amount\\\":20.0,\\\"count\\\":20},{\\\"batchid\\\":305322488,\\\"amount\\\":\\\"10.0\\\",\\\"count\\\":\\\"10\\\"}]\" val array: JSONArray = JsonUtils.getArrayFromJson(json) println(array) array.toArray().foreach(json=>{ println(json) val jobj = json.asInstanceOf[JSONObject] println(jobj.get(\"batchid\")) }) val jsonStr = \"{\\\"batchid\\\":119,\\\"amount\\\":200.0,\\\"count\\\":200}\" val jsonObj: JSONObject = JsonUtils.getObjectFromJson(jsonStr) println(jsonObj) val jsonObj2: JSONObject = JsonUtils.getObjectFromJson(\"{'name':'Wang','age':18,'tag1':[{'tn1':'100','tn2':'101','ts':'ts01'},{'tn1':'100','tn2':'101','ts':'ts02'},{'tn1':'100','tn2':'101','ts':'ts03'}]}\") println(jsonObj2)}","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Json","slug":"Json","permalink":"http://gangtieguo.cn/tags/Json/"},{"name":"Scala","slug":"Scala","permalink":"http://gangtieguo.cn/tags/Scala/"}]},{"title":"Spark读取HBase","slug":"Spark读取Hbase","date":"2018-08-10T16:18:11.706Z","updated":"2019-06-17T04:40:09.402Z","comments":true,"path":"2018/08/11/Spark读取Hbase/","link":"","permalink":"http://gangtieguo.cn/2018/08/11/Spark读取Hbase/","excerpt":"[TOC] Spark读取Hbase","text":"[TOC] Spark读取Hbase spark配置首先spark的配置 12345678910111213141516171819202122val array = Array( (\"spark.serializer\", \"org.apache.spark.serializer.KryoSerializer\"), (\"spark.storage.memoryFraction\", \"0.3\"), (\"spark.memory.useLegacyMode\", \"true\"), (\"spark.shuffle.memoryFraction\", \"0.6\"), (\"spark.shuffle.file.buffer\", \"128k\"), (\"spark.reducer.maxSizeInFlight\", \"96m\"), (\"spark.sql.shuffle.partitions\", \"500\"), (\"spark.default.parallelism\", \"180\"), (\"spark.dynamicAllocation.enabled\", \"false\") ) val conf = new SparkConf().setAll(array) .setJars(Array(\"your.jar\")) val sparkSession: SparkSession = SparkSession .builder .appName(applicationName) .enableHiveSupport() .master(\"spark://master:7077\") .config(conf) .getOrCreate() val sqlContext = sparkSession.sqlContext val sparkContext: SparkContext = sparkSession.sparkContext Hbase配置1234567891011121314151617181920212223242526272829303132333435363738394041424344val hBaseConf = HBaseConfiguration.create()var scan = new Scan();scan.addFamily(Bytes.toBytes(\"cf\"));var proto = ProtobufUtil.toScan(scan)var scanToString = Base64.encodeBytes(proto.toByteArray())//以为全局扫描的方式hBaseConf.set(TableInputFormat.SCAN,scanToString)//如需要设置起止行的话//scan.setStartRow(Bytes.toBytes(\"1111111111111\"))//scan.setStopRow(Bytes.toBytes(\"999999999999999\"))hBaseConf.set(\"hbase.zookeeper.quorum\",\"zk1,zk2,zk3\")hBaseConf.set(\"phoenix.query.timeoutMs\",\"1800000\")hBaseConf.set(\"hbase.regionserver.lease.period\",\"1200000\")hBaseConf.set(\"hbase.rpc.timeout\",\"1200000\")hBaseConf.set(\"hbase.client.scanner.caching\",\"1000\")hBaseConf.set(\"hbase.client.scanner.timeout.period\",\"1200000\")//表名配置hBaseConf.set(TableInputFormat.INPUT_TABLE,\"beehive:a_up_rawdata\")// 从数据源获取数据val hbaseRDD = sparkContext.newAPIHadoopRDD(hBaseConf,classOf[TableInputFormat],classOf[org.apache.hadoop.hbase.io.ImmutableBytesWritable],classOf[org.apache.hadoop.hbase.client.Result])//即可得到读取Hbase查询的RDD val hbaseJsonRdd: RDD[String] = hbaseRDD.filter(t => broadCast.value.contains(Bytes.toString(t._2.getRow)) //********************************操作每个分区的数据******************************** ).mapPartitions( it=>{ it.map(x=>x._2).map(hbaseValue => { var listBuffer = new ListBuffer[String]() //对应的值 val rowkey = Bytes.toString(hbaseValue.getRow) val value: String = Bytes.toString(hbaseValue.getValue(Bytes.toBytes(\"cf\"), Bytes.toBytes(\"填写获取哪一列\"))) if (null != value ) { //如果value不为空则再进行操作 } listBuffer }) }).flatMap(r => r)//注意map操作是需要函数内部有返回值的,如果只是打印的话,换成foreach算子 println(s\"hbaseJsonRdd.size为:${hbaseJsonRdd.count()}\") sparkContext.stop() sparkSession.close() println(\"ALL 已经关闭,程序终止\")","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"HBase","slug":"HBase","permalink":"http://gangtieguo.cn/tags/HBase/"}]},{"title":"Spark读取HBase解析json创建临时表录入到Hive表","slug":"Spark读取HBase解析json创建临时表录入到Hive表","date":"2018-08-10T16:09:46.585Z","updated":"2019-06-17T04:40:09.402Z","comments":true,"path":"2018/08/11/Spark读取HBase解析json创建临时表录入到Hive表/","link":"","permalink":"http://gangtieguo.cn/2018/08/11/Spark读取HBase解析json创建临时表录入到Hive表/","excerpt":"[TOC] 介绍:主要是读取通过mysql查到关联关系然后读取HBASE里面存放的Json,通过解析json将json数组对象里的元素拆分成单条json,再将json映射成临时表,查询临时表将数据落入到hive表中 注意:查询HBASE的时候,HBase集群的HMaster,HRegionServer需要是正常运行 主要将内容拆分成几块,spark读取HBase,spark解析json将json数组中每个元素拆成一条(比如json数组有10个元素,需要解析平铺成19个json,那么对应临时表中就是19条记录,对应查询插入到hive也就是19条记录) spark读取本地HBase","text":"[TOC] 介绍:主要是读取通过mysql查到关联关系然后读取HBASE里面存放的Json,通过解析json将json数组对象里的元素拆分成单条json,再将json映射成临时表,查询临时表将数据落入到hive表中 注意:查询HBASE的时候,HBase集群的HMaster,HRegionServer需要是正常运行 主要将内容拆分成几块,spark读取HBase,spark解析json将json数组中每个元素拆成一条(比如json数组有10个元素,需要解析平铺成19个json,那么对应临时表中就是19条记录,对应查询插入到hive也就是19条记录) spark读取本地HBase 参考 Spark读取HBase json样例 读取hbasehbase里面存放的是身份id作为rowkey来存放的数据 JSON、JSONObject类包是引用的com.alibaba.fastjson包下的 123456789101112131415161718192021222324252627282930313233343536val hbaseJsonRdd: RDD[String] = hbaseRDD.mapPartitions( it=>{ it.map(x=>x._2).map(hbaseValue => { var listBuffer = new ListBuffer[String]() //对应的值 //获取key,也就是身份证号,通过身份证号在广播map中的值 也就是risk_request_id val idNum = Bytes.toString(hbaseValue.getRow) val json: String = Bytes.toString(hbaseValue.getValue(Bytes.toBytes(\"cf\"), Bytes.toBytes(s\"273468436_data\"))) if (null != json ) { //********************************获取到json之后进行解析******************************** try { val jSONObject: JSONObject = JSON.parseObject(json) if (jSONObject != null) { val contactRegion = repostData.getJSONArray(\"contact_region\") if (contactRegion != null) { contactRegion.toArray().foreach(v => { val arrays = JSON.parseObject(v.toString) //val map = JSON.toJavaObject(arrays,classOf[util.Map[String,String]]) val map: mutable.Map[String, String] = JsonUtils.jsonObj2Map(arrays) //将json 转为Map //将******************************** 日期和requestId request_id封装到 map里面********************************,再将map转为json map.put(\"region_id\", \"2\") map.put(\"request_id\", \"1\") map.put(\"region_create_at\", \"0000\") map.put(\"region_update_at\", \"0000\") listBuffer += (JsonUtils.map2Json(map)) }) } } } }catch { case e: Exception => e.printStackTrace() } } listBuffer }) }).flatMap(r => r) 代码中的 jsonObj2Map,map2Json 方法参照 Json与Scala类型的相互转换处理 这里拆分json数组每一个元素为一个json,存放在ListBuffer里面,通过flatMap压平rdd里面的内容。 映射临时表最后将得到的json通过sparkSql创建成临时表 1234567 val dataFrame: DataFrame = sqlContext.read.json(hbaseJsonRdd)dataFrame.createOrReplaceTempView(\"tmp_hbase\")//// 测试println(\"++++++++++++++++++++++++++++++hbaseJsonRdd.....创建临时表 测试查询数据 ......++++++++++++++++++++++++++++++\")val df = sqlContext.sql(\"select * from tmp_hbase limit 1\")df.show(1) 插入Hive1234567sqlContext.sql(\"insert into ods.ods_r_juxinli_region_n partition(dt='20180101') select region_id as juxinli_region_id,request_id as juxinli_request_id,\" + \"region_loc as juxinli_rejion_loc ,region_uniq_num_cnt as juxinli_region_uniq_num_cnt ,\" + \"region_call_out_time as juxinli_region_call_out_time,region_call_in_time as juxinli_region_call_in_time,region_call_out_cnt as juxinli_region_call_out_cnt,\" + \"region_call_in_cnt as juxinli_region_call_in_cnt,region_avg_call_in_time as juxinli_region_avg_call_in_time,region_avg_call_out_time as juxinli_region_avg_call_out_time,\" + \"region_call_in_time_pct as juxinli_region_call_in_time_pct,region_call_out_time_pct as juxinli_region_call_out_time_pct ,region_call_in_cnt_pct as juxinli_region_call_in_cnt_pct,\" + \"region_call_out_cnt_pct as juxinli_region_call_out_cnt_pct,region_create_at as juxinli_region_create_at,region_update_at as juxinli_region_update_at from tmp_hbase\") } 关闭资源 12sparkContext.stop()sparkSession.close()","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"SparkSQL","slug":"SparkSQL","permalink":"http://gangtieguo.cn/tags/SparkSQL/"}]},{"title":"HBase拷贝生产环境数据到本地Spark解析运行调试","slug":"HBase拷贝生产环境数据到本地运行调试","date":"2018-08-10T10:19:15.453Z","updated":"2019-06-17T04:40:09.382Z","comments":true,"path":"2018/08/10/HBase拷贝生产环境数据到本地运行调试/","link":"","permalink":"http://gangtieguo.cn/2018/08/10/HBase拷贝生产环境数据到本地运行调试/","excerpt":"[TOC] 由于线上环境要经过跳板机跳转,并且打包测试,上传jar包步骤多,不然的话,要进行各种端口转发,且有权限控制,不易在本地idea编辑器上进行程序运行及调试 现在想法是,将线上测试环境的数据拷贝小部分到本地自己搭建的集群,进行程序的逻辑和初期调试 此贴就是记录一些操作 这都是要基于本地有HBASE及其依赖组件的。 主要思路是,拷贝线上查询的结果到文件hbaseout1.txt,将hbaseout1.txt文件sz导入本地 再在本地集群上将数据插入到hbase","text":"[TOC] 由于线上环境要经过跳板机跳转,并且打包测试,上传jar包步骤多,不然的话,要进行各种端口转发,且有权限控制,不易在本地idea编辑器上进行程序运行及调试 现在想法是,将线上测试环境的数据拷贝小部分到本地自己搭建的集群,进行程序的逻辑和初期调试 此贴就是记录一些操作 这都是要基于本地有HBASE及其依赖组件的。 主要思路是,拷贝线上查询的结果到文件hbaseout1.txt,将hbaseout1.txt文件sz导入本地 再在本地集群上将数据插入到hbase 1、创建和线上同名通结构的表在线上执行 describe 'beehive:a_up_rawdata' 得到 在本地执行 1234hbase shellcreate_namespace 'beehive'create 'beehive:a_up_rawdata',{NAME => 'cf', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '1'} 2、拷贝线上hbase数据查询结果导入文件在线上机器任意目录执行 1echo \"get 'beehive:a_up_rawdata','530111199211287371',{COLUMN=>'cf:273468436_data'}\"| hbase shell> hbaseout1.txt 解析: get 'beehive:a_up_rawdata','530111199211287371',{COLUMN=>'cf:273468436_data'}是执行的hbase的查询语句,将查询的结果存入到当前目录 hbaseout1.txt文件中 查询文件下载到本地sz hbaseout1.txt 修改文件内容可以查看hbaseout1.txt中可以看到会有表头 需要将这部分表头数据删除,组成标准的导入文件 修改文件编码 在通过 hbase shell 查看中文值时, 是 unicode 编码格式,使得直接查看中文值不太方便。如果要查看需要把 unicode 编码进行 decode 将查询结果导出来 1234567print ('需要转码内容'.decode('utf-8'))命令样例python 2.7 print ('***\\xE4\\xBD\\xA010009 '.decode('utf-8'))python 3 print '***\\xE4\\xBD\\xA010009 '.decode('utf-8') 可以有更友好的将内容设置为文件名,然后将转码后重新写入到一个文件,后续会更新 转码过后,文字显示正确 重新组合文件由于导入到hbase命令为 **格式:hbase [类][分隔符] [行键,列族][表] [导入文件] 由于我这次导入的文件里面有“,”,所有将分隔符设置为“|” 更改后的文件格式为 将文件上传到hdfs 1hadoop fs -put hadoop fs -put hbaseout1.txt /local/ 将数据导入到本地hbase1hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.separator=\"|\" -Dimporttsv.columns=HBASE_ROW_KEY,cf:273468436_data beehive:a_up_rawdata /local/hbaseout2.txt 3、校验查看在hue上查看hbase内容,显示有数据 在hbase shell 查看","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"HBase","slug":"HBase","permalink":"http://gangtieguo.cn/tags/HBase/"},{"name":"操作","slug":"操作","permalink":"http://gangtieguo.cn/tags/操作/"}]},{"title":"Hbase-shell操作","slug":"Hbase-shell操作","date":"2018-08-10T09:27:05.100Z","updated":"2019-06-17T04:40:09.378Z","comments":true,"path":"2018/08/10/Hbase-shell操作/","link":"","permalink":"http://gangtieguo.cn/2018/08/10/Hbase-shell操作/","excerpt":"[TOC] hbase使用命令行操作,简单直接,方便快捷,掌握一点必备的基础命令。 HBase启动命令行 1$HBASE_HOME/bin/hbase shell","text":"[TOC] hbase使用命令行操作,简单直接,方便快捷,掌握一点必备的基础命令。 HBase启动命令行 1$HBASE_HOME/bin/hbase shell 创建表1create 'testtable',{NAME=>'cf',VERSIONS=>2},{NAME=>'cf2',VERSIONS=>2} 创建namespace1create_namespace 'beehive' 查看表结构1disable 'testtable' 删除表1drop 'testtable' 修改表1234disable 'testtable'alter 'testtable',{NAME=>'cf',TTL=>'10000000'},{NAME=>'cf2',TTL=>'10000000'}enable 'testtable'修改表必须先 disable 表 表数据的增删查改:添加数据:1put 'testtable','rowkey1','cf:key1','val1' 查询数据:12get 'testtable','rowkey1','cf:key1'get 'testtable','rowkey1', {COLUMN=>'cf:key1'} 扫描表:1scan 'testtable',{COLUMNS=>cf:col1,LIMIT=>5} #可以添加STARTROW、TIMERANGE和FITLER等高级功能 查询表中的数据行数:语法:count <table>, {INTERVAL => intervalNum, CACHE => cacheNum} 1count 'testtable',{INTERVAL => 100, CACHE => 500} 删除数据:12delete 'testtable','rowkey1','cf:key1'truncate 'testtable'","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"HBase","slug":"HBase","permalink":"http://gangtieguo.cn/tags/HBase/"},{"name":"Shell","slug":"Shell","permalink":"http://gangtieguo.cn/tags/Shell/"}]},{"title":"Spark本地调试远程集群程序","slug":"Spark本地调试远程集群程序","date":"2018-08-07T08:09:43.314Z","updated":"2019-06-17T04:40:09.400Z","comments":true,"path":"2018/08/07/Spark本地调试远程集群程序/","link":"","permalink":"http://gangtieguo.cn/2018/08/07/Spark本地调试远程集群程序/","excerpt":"[TOC] 由于在生产环境中进行调试spark程序需要进行打包和各种跳板机跳转,最好在本地搭一套集群来进行一些代码基础检查。","text":"[TOC] 由于在生产环境中进行调试spark程序需要进行打包和各种跳板机跳转,最好在本地搭一套集群来进行一些代码基础检查。 需要将 本地集群中hdfs-site.xml core.site.xml 拷贝到本地工程的resource文件夹下,这样会应用这些配置,注意要在提交到生产环境的时候,替换成对应环境的配置文件 12345678910111213141516171819202122val array = Array( (\"spark.serializer\", \"org.apache.spark.serializer.KryoSerializer\"), (\"spark.storage.memoryFraction\", \"0.3\"), (\"spark.memory.useLegacyMode\", \"true\"), (\"spark.shuffle.memoryFraction\", \"0.6\"), (\"spark.shuffle.file.buffer\", \"128k\"), (\"spark.reducer.maxSizeInFlight\", \"96m\"), (\"spark.sql.shuffle.partitions\", \"500\"), (\"spark.default.parallelism\", \"180\"), (\"spark.dynamicAllocation.enabled\", \"false\") ) val conf = new SparkConf().setAll(array) .setJars(Array(\"your.jar\")) val sparkSession: SparkSession = SparkSession .builder .appName(applicationName) .enableHiveSupport() .master(\"spark://master:7077\") .config(conf) .getOrCreate() val sqlContext = sparkSession.sqlContext val sparkContext: SparkContext = sparkSession.sparkContext","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"}]},{"title":"Zookeeper的配置容器的搭建","slug":"zookeeper配置","date":"2018-08-06T19:06:03.170Z","updated":"2019-06-17T04:40:09.403Z","comments":true,"path":"2018/08/07/zookeeper配置/","link":"","permalink":"http://gangtieguo.cn/2018/08/07/zookeeper配置/","excerpt":"[TOC] 在usr目录下下载zk包,并且解压到/usr/目录,改名为zk,所以$ZK_HOME为/usr/zk 创建目录123mkdir -p /usr/zk/datamkdir -p /usr/zk/logstouch /usr/zk/data/myid","text":"[TOC] 在usr目录下下载zk包,并且解压到/usr/目录,改名为zk,所以$ZK_HOME为/usr/zk 创建目录123mkdir -p /usr/zk/datamkdir -p /usr/zk/logstouch /usr/zk/data/myid 更改配置文件vim $ZK_HOME/conf/zoo.cfg 12345dataDir=/usr/zk/datadataLogDir=/usr/zk/logsserver.1=zk1:2888:3888server.2=zk2:2888:3888server.3=zk3:2888:3888 zookeeper需要全部左右节点都启动才会选举leader,follower 所有节点启动的脚本 1234567# !/bin/bashecho 1 > $ZK_HOME/data/myid$ZK_HOME/bin/zkServer.sh startssh root@zk2 \"echo 2 > /usr/zk/data/myid\"ssh root@zk2 \"/usr/zk/bin/zkServer.sh start\"ssh root@zk3 \"echo 3 > /usr/zk/data/myid\"ssh root@zk3 \"/usr/zk/bin/zkServer.sh start\" zk问题1、由于 zk 运行一段时间后,会产生大量的日志文件,把磁盘空间占满,导致整个机器进程都不能活动了,所以需要定期清理这些日志文件,方法如下: 1)、写一个脚本文件 cleanup.sh 内容如下: 12345678java -cp zookeeper.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-log4j12-1.6.1.jar:lib/log4j-1.2.15.jar:conf org.apache.zookeeper.server.PurgeTxnLog <dataDir> <snapDir> -n <count>其中: dataDir:即上面配置的 dataDir 的目录 snapDir:即上面配置的 dataLogDir 的目录 count:保留前几个日志文件,默认为 3 2)、通过 crontab 写定时任务,来完成定时清理日志的需求 123crontab -e 0 0 * * /opt/zookeeper-3.4.10/bin/cleanup.shHBase Master 高可用(HA)(http://www.cnblogs.com/captainlucky/p/4710642.html)HMaster 没有单点问题,HBase 中可以启动多个 HMaster,通过 Zookeeper 的 Master Election 机制保证总有一个 Master 运行。 所以这里要配置 HBase 高可用的话,只需要启动两个 HMaster,让 Zookeeper 自己去选择一个 Master Acitve。","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"zk","slug":"zk","permalink":"http://gangtieguo.cn/tags/zk/"}]},{"title":"","slug":"docker-machine异常问题","date":"2018-08-01T12:20:03.591Z","updated":"2018-08-01T18:24:04.223Z","comments":true,"path":"2018/08/01/docker-machine异常问题/","link":"","permalink":"http://gangtieguo.cn/2018/08/01/docker-machine异常问题/","excerpt":"","text":"Docker-machine莫名不能访问? 1Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.29/containers/json: dial unix /var/run/docker.sock: connect: permission denied 12Starting "default"...generic driver does not support start","categories":[],"tags":[]},{"title":"Linux安装mysql","slug":"Linux安装mysql","date":"2018-07-24T15:42:18.647Z","updated":"2019-06-17T04:40:09.391Z","comments":true,"path":"2018/07/24/Linux安装mysql/","link":"","permalink":"http://gangtieguo.cn/2018/07/24/Linux安装mysql/","excerpt":"在linux yum安装mysql","text":"在linux yum安装mysql 12345678910111213141516171819yum install -y mysql-serverchkconfig --add mysqldchkconfig mysqld onchkconfig --list mysqldservice mysqld startmysql -u root -pEnter password: //默认密码为空,输入后回车即可set password for root@localhost=password('root'); 密码设置为rootset password for root@=password('root');默认情况下Mysql只允许本地登录,所以只需配置root@localhost就好设置所有ip访问密码为rootset password for root@%=password('root'); 密码设置为root (其实这一步可以不配)设置master访问密码为rootset password for root@master=password('root'); 密码设置为root (其实这一步可以不配)查询密码select user,host,password from mysql.user; 查看密码是否设置成功设置所有ip可以通过root访问GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;GRANT ALL PRIVILEGES ON *.* TO 'hive'@'%' IDENTIFIED BY 'hive' WITH GRANT OPTION;","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Linux","slug":"Linux","permalink":"http://gangtieguo.cn/tags/Linux/"},{"name":"Mysql","slug":"Mysql","permalink":"http://gangtieguo.cn/tags/Mysql/"}]},{"title":"Docker-yaosong5/bigdatabase镜像制作搭建及其运行命令","slug":"Docker-yaosong5:bigdatabase镜像制作搭建及其运行命令","date":"2018-07-23T15:40:07.620Z","updated":"2019-03-11T07:53:05.815Z","comments":true,"path":"2018/07/23/Docker-yaosong5:bigdatabase镜像制作搭建及其运行命令/","link":"","permalink":"http://gangtieguo.cn/2018/07/23/Docker-yaosong5:bigdatabase镜像制作搭建及其运行命令/","excerpt":"","text":"[TOC] 在centosbase1.0的基础上设置变量在centosbase1.0的基础上安装,mysql(mysql安装参考mysql安装文章)里面安装hive用户,hue用户等等, 创建数据库hue,hive等(为后续做准备) 12create database hive;create database hue; 开机自启动mysql `chkconfig --add mysqld` 保存为镜像yaosong5/centosbase:2.0docker commit -m "centos ssh mysql \b创建了hive库,hue库" ctos yaosong5/centosbase:2.0docker commit -m "centos ssh mysql \b创建了hive库,hue库" ctos yaosong5/centosbase:3.0docker commit -m "centos ssh mysql \b创建了hive库,hue库,更改了/JAVA_HOME" ctos yaosong5/centosbase:4.0后更改为3.0重新配置了java_home将标签4.0 更改为 centosbase:1.0还是创建 bigdatabase:1.0 准备创建软连接123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263ln -s /Users/yaosong/Yao/DockerSource/sparkSource/hadoop /Users/yaosong/Yao/share/source/hadoopln -s /Users/yaosong/Yao/DockerSource/sparkSource/hbase /Users/yaosong/Yao/share/source/hbaseln -s /Users/yaosong/Yao/DockerSource/sparkSource/hive /Users/yaosong/Yao/share/source/hiveln -s /Users/yaosong/Yao/DockerSource/sparkSource/kafka /Users/yaosong/Yao/share/source/kafkaln -s /Users/yaosong/Yao/DockerSource/sparkSource/scala /Users/yaosong/Yao/share/source/scalaln -s /Users/yaosong/Yao/DockerSource/sparkSource/spark /Users/yaosong/Yao/share/source/sparkln -s /Users/yaosong/Yao/DockerSource/sparkSource/zk /Users/yaosong/Yao/share/source/zkln -s /Users/yaosong/Yao/DockerSource/hueSource/ant /Users/yaosong/Yao/share/source/antln -s /Users/yaosong/Yao/DockerSource/hueSource/hue4 /Users/yaosong/Yao/share/source/hue4ln -s /Users/yaosong/Yao/DockerSource/hueSource/maven /Users/yaosong/Yao/share/source/mavenln -s /Users/yaosong/Yao/DockerSource/flinkSource/flink /Users/yaosong/Yao/share/source/flinkln -s /Users/yaosong/Yao/DockerSource/flinkSource/flink-hadoop /Users/yaosong/Yao/share/source/flink-hadoopln -s /Users/yaosong/Yao/DockerSource/elkSource/es /Users/yaosong/Yao/share/source/esln -s /Users/yaosong/Yao/DockerSource/elkSource/filebeat /Users/yaosong/Yao/share/source/filebeatln -s /Users/yaosong/Yao/DockerSource/elkSource/kibana /Users/yaosong/Yao/share/source/kibanaln -s /Users/yaosong/Yao/DockerSource/elkSource/logstash /Users/yaosong/Yao/share/source/logstashln -s /Users/yaosong/Yao/DockerSource/elkSource/node /Users/yaosong/Yao/share/source/nodeln -s /Users/yaosong/Yao/DockerSource/hueSource/jdk /Users/yaosong/Yao/share/source/jdk后调整ln -s /Users/yaosong/Yao/DockerSource/sparkSource/spark-2.3.1-bin-hadoop2.7 /Users/yaosong/Yao/share/source/sparkln -s /Users/yaosong/Yao/DockerSource/sparkSource/hadoop-3.0.3 /Users/yaosong/Yao/share/source/hadooprm -rf /Users/yaosong/Yao/share/source/hadoopln -s /Users/yaosong/Yao/DockerSource/sparkSource/hadoop-3.1.0 /Users/yaosong/Yao/share/source/hadooprm -rf /Users/yaosong/Yao/share/source/hadoopln -s /Users/yaosong/Yao/DockerSource/sparkSource/hadoop-2.9.0 /Users/yaosong/Yao/share/source/hadooprm -rf /Users/yaosong/Yao/share/source/hadoopln -s /Users/yaosong/Yao/DockerSource/sparkSource/hadoop /Users/yaosong/Yao/share/source/hadoopln -s /Users/yaosong/Yao/DockerSource/elkSource/elasticsearch-head-master /Users/yaosong/Yao/share/source/elasticsearch-head-masterln -s /Users/yaosong/Yao/DockerSource/sparkSource/kafkaall/kafka1 /Users/yaosong/Yao/share/source/ln -s /Users/yaosong/Yao/DockerSource/sparkSource/kafkaall/kafka2 /Users/yaosong/Yao/share/source/ln -s /Users/yaosong/Yao/DockerSource/sparkSource/kafkaall/kafka3 /Users/yaosong/Yao/share/source/ln -s /Users/yaosong/Yao/DockerSource/elkSource/es1/ /Users/yaosong/Yao/share/source/ln -s /Users/yaosong/Yao/DockerSource/elkSource/es2/ /Users/yaosong/Yao/share/source/ln -s /Users/yaosong/Yao/DockerSource/elkSource/es3/ /Users/yaosong/Yao/share/source/ln -s /Users/yaosong/Yao/启动脚本/kafka-start-all.sh /Users/yaosong/Yao/share/shell/kafka-start-all.shln -s /Users/yaosong/Yao/启动脚本/es-start.sh /Users/yaosong/Yao/share/shell/es-start.shln -s /Users/yaosong/Yao/启动脚本/filebeat-start.sh /Users/yaosong/Yao/share/shell/filebeat-start.shln -s /Users/yaosong/Yao/启动脚本/hadoop-start.sh /Users/yaosong/Yao/share/shell/hadoop-start.shln -s /Users/yaosong/Yao/启动脚本/hbase-start.sh /Users/yaosong/Yao/share/shell/hbase-start.shln -s /Users/yaosong/Yao/启动脚本/hbasezkStart.sh /Users/yaosong/Yao/share/shell/hbasezkStart.shln -s /Users/yaosong/Yao/启动脚本/header-start.sh /Users/yaosong/Yao/share/shell/header-start.shln -s /Users/yaosong/Yao/启动脚本/hue-start.sh /Users/yaosong/Yao/share/shell/hue-start.shln -s /Users/yaosong/Yao/启动脚本/kibana-start.sh /Users/yaosong/Yao/share/shell/kibana-start.shln -s /Users/yaosong/Yao/启动脚本/logstash-start.sh /Users/yaosong/Yao/share/shell/logstash-start.shln -s /Users/yaosong/Yao/启动脚本/spark-start.sh /Users/yaosong/Yao/share/shell/spark-start.shln -s /Users/yaosong/Yao/启动脚本/zk-start.sh /Users/yaosong/Yao/share/shell/zk-start.shln -s /Users/yaosong/Yao/启动脚本/hivebeeline.sh /Users/yaosong/Yao/share/shell/hivebeeline.shln -s /Users/yaosong/Yao/启动脚本/hivemetastore.sh /Users/yaosong/Yao/share/shell/hivemetastore.shln -s /Users/yaosong/Yao/启动脚本/hiveservers2.sh /Users/yaosong/Yao/share/shell/hiveservers2.shln -s /Users/yaosong/Yao/启动脚本/hivestart.sh /Users/yaosong/Yao/share/shell/hivestart.sh 在virtualbox中设置共享文件夹的share名称对应mac的目录虚拟机中的目录sudo mount -t vboxsf share /Users/yaosong/Yao/share/sudo mount -t vboxsf yaosong /Users/yaosong sudo mount -t vboxsf vagrant /Users/yaosong 以后相当于操作/Users/yaosong/Yao/share/就是操作的mac系统/Users/yaosong/Yao/share/ 创建dockerfile123456789101112131415161718192021222324252627282930313233343536373839404142FROM yaosong5/centosbase:3.0ENV JAVA_HOME=/usr/java/jdkENV PATH=$JAVA_HOME/bin:$PATHENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarENV SCALA_HOME=/usr/scala/ENV HADOOP_HOME=/usr/hadoopENV HADOOP_CONFIG_HOME=$HADOOP_HOME/etc/hadoopENV HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoopENV PATH=$PATH:$HADOOP_HOME/binENV PATH=$PATH:$HADOOP_HOME/sbinENV SPARK_LOCAL_DIRS=/usr/sparkENV SPARK_DRIVER_MEMORY=1GENV SPARK_HOME=/usr/sparkENV PATH=$SPARK_HOME/bin:$PATHENV PATH=$SPARK_HOME/sbin:$PATHENV MAVEN_HOME=/usr/mavenENV PATH=${PATH}:${MAVEN_HOME}/binENV HIVE_HOME=/usr/hiveENV PATH=$HIVE_HOME/bin:$PATHENV KAFKA_HOME=/usr/kafkaENV PATH=$KAFKA_HOME/bin:$PATHENV ANT_HOME=/usr/antENV PATH=$JAVA_HOME/bin:$ANT_HOME/bin:$PATHENV ANT_HOME PATHENV HUE_HOME=/usr/hue4ENV ZK_HOME=/usr/zkENV HBASE_HOME=/usr/hbaseENV PATH=$HBASE_HOME/bin:$PATHENV PATH=$ZK_HOME/bin:$PATH#elkENV ES_HOME=/usr/esENV PATH=$ES_HOME/bin:$PATHENV KIBANA_HOME=/usr/kibanaENV PATH=$KIBANA_HOME/bin:$PATHENV LOGSTASH_HOME=/usr/logstashENV PATH=$LOGSTASH_HOME/bin:$PATHENV NODE_HOME=/usr/node-v6.12.0-linux-x64ENV PATH=$NODE_HOME/bin:$PATHENV NODE_PATH=$NODE_HOME/lib/node_modules#flinkENV FLINK_HOME=/usr/flinkENV PATH=$FLINK_HOME/bin:$PATH 构建镜像docker build -f Dockerfile -t yaosong5/bigdatabase:1.0 . 分别构建容器和镜像 (采用挂载的方式)分别的启动命令 先加载后启动一个hadoop集群创建一个master容器不含启动参数,只是用原始的配置文件,只启动容器,不使用任何命令启动组件 由于下面三个组件需要挂载相同的目录那么创建一个共享的数据卷镜像 参考 :https://blog.csdn.net/magerguo/article/details/72514813https://blog.csdn.net/u013571243/article/details/51751367 docker run -it –rm –name datavol -h datavol -v /Users/yaosong/Yao/share/source/:/usr/ busybox:latest /bin/shdocker run -it –rm –name datavol -h datavol -v /Users/yaosong/Yao/share/source/:/usr/ busybox:latest /bin/shrm详解http://dockone.io/article/152 https://blog.csdn.net/taiyangdao/article/details/73076770 1234docker run -itd --name datavol -h datavol \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop \\-v /Users/yaosong/Yao/share/data/hadoop/name:/usr/hadoop/namenode \\busybox:latest /bin/sh 数据容器常见的使用场景是使用纯数据容器来持久化数据库、配置文件或者数据文件等。 官方的文档 上有详细的解释。例如: 123> $ docker run --name dbdata postgres echo "Data-only container for postgres">> 该命令将会创建一个已经包含在 Dockerfile 里定义过 Volume 的 postgres 镜像,运行 12> echo> 命令然后退出。当我们运行 12> docker ps> 命令时, 12> echo> 可以帮助我们识别某镜像的用途。我们可以用 12> -volumes-from> 使用数据容器的两个注意点: 不要运行数据容器,这纯粹是在浪费资源。 不要为了数据容器而使用 “最小的镜像”,如busybox或scratch,只使用数据库镜像本身就可以了。你已经拥有该镜像,所以并不需要占用额外的空间。 参考http://dockone.io/article/128 再使用 –volumes-from datavol示例 :docker run -it –net=br –volumes-from dataVol ubuntu64 /bin/bash 使用公用挂载镜像创建基础镜像 12345678docker run -itd --name datavol -h datavol \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop \\-v /Users/yaosong/Yao/share/data/hadoop/name:/usr/hadoop/namenode \\-v /Users/yaosong/Yao/share/source/spark:/usr/spark \\-v /Users/yaosong/Yao/share/source/flink:/usr/flink \\-v /Users/yaosong/Yao/share/source/ant:/usr/ant \\-v /Users/yaosong/Yao/share/source/maven:/usr/maven \\busybox:latest /bin/sh master slave01 slave021234567891011121314151617181920212223docker run -itd --net=br --name master -h master --volumes-from datavol \\-v /Users/yaosong/Yao/share/shell/hadoop-start.sh:/hadoop-start.sh yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name slave01 --net=br -h slave01 --volumes-from datavol \\-v /Users/yaosong/Yao/share/data/hadoop/data1:/usr/hadoop/datanode \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name slave02 --net=br -h slave02 --volumes-from datavol \\-v /Users/yaosong/Yao/share/data/hadoop/data2:/usr/hadoop/datanode \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --net=br --name master -h master --volumes-from datavol \\-v /Users/yaosong/Yao/share/shell/hadoop-start.sh:/hadoop-start.sh yaosong5/centosbase:1.0 &> /dev/nulldocker run -itd --name slave01 --net=br -h slave01 --volumes-from datavol \\-v /Users/yaosong/Yao/share/data/hadoop/data1:/usr/hadoop/datanode \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop yaosong5/centosbase:1.0 &> /dev/nulldocker run -itd --name slave02 --net=br -h slave02 --volumes-from datavol \\-v /Users/yaosong/Yao/share/data/hadoop/data2:/usr/hadoop/datanode \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop yaosong5/centosbase:1.0 &> /dev/null 创建大节点 使用的1.1封装了hue 1.1就是2.0 12345678910111213docker run -itd --net=br --name master -h master --volumes-from datavol \\-v /Users/yaosong/Yao/share/shell/spark-start.sh:/spark-start.sh \\-v /Users/yaosong/Yao/share/shell/hadoop-start.sh:/hadoop-start.sh \\-v /Users/yaosong/Yao/share/source/hue4/desktop/conf/hue.ini:/usr/hue4/desktop/conf/hue.ini \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name slave01 --net=br -h slave01 --volumes-from datavol \\-v /Users/yaosong/Yao/share/hadoop/data1:/usr/hadoop/datanode \\ yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name slave02 --net=br -h slave02 --volumes-from datavol \\-v /Users/yaosong/Yao/share/hadoop/data2:/usr/hadoop/datanode \\yaosong5/bigadatabase:1.0 &> /dev/null 原生容器自身挂载镜像master slave01 slave0212345678910111213141516171819docker run -itd --net=br --name master -h master \\-v /Users/yaosong/Yao/share/data/hadoop/name:/usr/hadoop/namenode \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop \\-v /Users/yaosong/Yao/share/shell/hadoop-start.sh:/hadoop-start.sh yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name slave01 \\--net=br -h slave01 \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop \\-v /Users/yaosong/Yao/share/data/hadoop/name:/usr/hadoop/namenode \\-v /Users/yaosong/Yao/share/data/hadoop/data1:/usr/hadoop/datanode \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name slave02 \\--net=br -h slave02 \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop \\-v /Users/yaosong/Yao/share/data/hadoop/name:/usr/hadoop/namenode \\-v /Users/yaosong/Yao/share/data/hadoop/data2:/usr/hadoop/datanode \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop yaosong5/bigadatabase:1.0 &> /dev/null zk/Users/yaosong/Yao/share/data/zk/zk1/data/myid/Users/yaosong/Yao/share/data/zk/zk2/data/myid/Users/yaosong/Yao/share/data/zk/zk3/data/myid/Users/yaosong/Yao/share/shell/zk-start.sh 123456789101112131415docker run -itd --name zk1 --net=br -h zk1 \\-v /Users/yaosong/Yao/share/source/zk:/usr/zk \\-v /Users/yaosong/Yao/share/shell/zk-start.sh:/zk-start.sh \\-v /Users/yaosong/Yao/share/data/zk/zk1/data/myid:/usr/zk/data/myid \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name zk2 --net=br -h zk2 \\-v /Users/yaosong/Yao/share/source/zk:/usr/zk \\-v /Users/yaosong/Yao/share/data/zk/zk2/data/myid:/usr/zk/data/myid \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name zk3 --net=br -h zk3 \\-v /Users/yaosong/Yao/share/source/zk:/usr/zk \\-v /Users/yaosong/Yao/share/data/zk/zk3/data/myid:/usr/zk/data/myid \\yaosong5/bigadatabase:1.0 &> /dev/null \bhbase1234567891011121314151617docker run -itd --name hbase1 --net=br -h hbase1 \\-v /Users/yaosong/Yao/share/source/hbase:/usr/hbase \\-v /Users/yaosong/Yao/share/shell/hbase-start.sh:/hbase-start.sh \\-v /Users/yaosong/Yao/share/shell/hbasezkStart.sh:/hbasezkStart.sh \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name hbase2 --net=br -h hbase2 \\-v /Users/yaosong/Yao/share/source/hbase:/usr/hbase \\-v /Users/yaosong/Yao/share/shell/hbase-start.sh:/hbase-start.sh \\-v /Users/yaosong/Yao/share/shell/hbasezkStart.sh:/hbasezkStart.sh \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name hbase3 --net=br -h hbase3 \\-v /Users/yaosong/Yao/share/source/hbase:/usr/hbase \\-v /Users/yaosong/Yao/share/shell/hbase-start.sh:/hbase-start.sh \\-v /Users/yaosong/Yao/share/shell/hbasezkStart.sh:/hbasezkStart.sh \\yaosong5/bigadatabase:1.0 &> /dev/null hive12345678910111213141516docker run -itd --name hive --net=br -h hive --volumes-from hivedatavol \\-v /Users/yaosong/Yao/share/source/hive:/usr/hive \\-v /Users/yaosong/Yao/share/shell/hive-start-metastore.sh:/hive-start-metastore.sh \\-v /Users/yaosong/Yao/share/shell/hive-start-servers2.sh:/hive-start-servers2.sh \\-v /Users/yaosong/Yao/share/shell/hive-start.sh:/hive-start.sh \\-v /Users/yaosong/Yao/share/shell/hivebeeline.sh:/hivebeeline.sh \\ yaosong5/bigadatabase:1.0 &> /dev/null docker run -itd --name hive --net=br -h hive \\-v /Users/yaosong/Yao/share/source/hive:/usr/hive \\-v /Users/yaosong/Yao/share/source/hadoop:/usr/hadoop \\-v /Users/yaosong/Yao/share/shell/hive-start-metastore.sh:/hive-start-metastore.sh \\-v /Users/yaosong/Yao/share/shell/hive-start-servers2.sh:/hive-start-servers2.sh \\-v /Users/yaosong/Yao/share/shell/hive-start.sh:/hive-start.sh \\-v /Users/yaosong/Yao/share/shell/hivebeeline.sh:/hivebeeline.sh \\ yaosong5/bigadatabase:1.0 &> /dev/null kafka/Users/yaosong/Yao/share/shell/kafka-start-all.sh /Users/yaosong/Yao/share/source/kafka 123456789101112docker run -itd --name kafka1 --net=br -h kafka1 \\-v /Users/yaosong/Yao/share/source/kafka:/usr/kafka \\-v /Users/yaosong/Yao/share/shell/kafka-start-all.sh:/kafka-start-all.sh \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name kafka2 --net=br -h kafka2 \\-v /Users/yaosong/Yao/share/source/kafka:/usr/kafka \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name kafka3 --net=br -h kafka3 \\-v /Users/yaosong/Yao/share/source/kafka:/usr/kafka \\yaosong5/bigadatabase:1.0 &> /dev/null spark12345678910111213docker run -itd --name spark1 --net=br -h spark1 --volumes-from datavol \\-v /Users/yaosong/Yao/share/source/spark:/usr/spark \\-v /Users/yaosong/Yao/share/shell/spark-start.sh:/spark-start.sh \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name spark2 --net=br -h spark2 --volumes-from datavol \\-v /Users/yaosong/Yao/share/source/spark:/usr/spark \\yaosong5/bigadatabase:1.0 &> /dev/nulldocker run -itd --name spark3 --net=br -h spark3 --volumes-from datavol \\-v /Users/yaosong/Yao/share/source/spark:/usr/spark \\yaosong5/bigadatabase:1.0 &> /dev/null 用 &> /dev/null这种方式sshd服务才会启动 停止 and 删除容器 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566docker start masterdocker start slave01docker start slave02docker stop masterdocker stop slave01docker stop slave02docker rm masterdocker rm slave01docker rm slave02docker stop datavol && docker rm datavoldocker stop hivedocker rm hivedocker stop zk1docker stop zk2docker stop zk3docker rm zk1docker rm zk2docker rm zk3docker stop hbase1docker stop hbase2docker stop hbase3docker rm hbase1docker rm hbase2docker rm hbase3docker stop kafka1docker stop kafka2docker stop kafka3docker rm kafka1docker rm kafka2docker rm kafka3docker start spark1docker start spark2docker start spark3docker stop spark1docker stop spark2docker stop spark3docker rm spark1docker rm spark2docker rm spark3docker stop hadoop1docker stop hadoop2docker stop hadoop3docker rm hadoop1docker rm hadoop2docker rm hadoop3docker stop elk1docker stop elk2docker stop elk3docker rm elk1docker rm elk2docker rm elk3 compose编排说的","categories":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/categories/Docker/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"}]},{"title":"Hive搭建及启动","slug":"Hive搭建及启动","date":"2018-07-19T18:51:21.978Z","updated":"2018-08-15T18:14:43.446Z","comments":true,"path":"2018/07/20/Hive搭建及启动/","link":"","permalink":"http://gangtieguo.cn/2018/07/20/Hive搭建及启动/","excerpt":"[TOC] 配置HOME下载hive包,并解压 1http://archive.apache.org/dist/ ln -s hive-2.1.1 /usr/hive vi ~/.bashrc 1234567export PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar$HIVE_HOME/bin/hiveexport HIVE_HOME=/usr/hivePATH=$HIVE_HOME/bin:$PATH#hive依赖于hadoop(可以不运行在同一主机,但是需要hadoop的配置)$HADOOP_HOME=/usr/hadoop source ~/.bashrc","text":"[TOC] 配置HOME下载hive包,并解压 1http://archive.apache.org/dist/ ln -s hive-2.1.1 /usr/hive vi ~/.bashrc 1234567export PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar$HIVE_HOME/bin/hiveexport HIVE_HOME=/usr/hivePATH=$HIVE_HOME/bin:$PATH#hive依赖于hadoop(可以不运行在同一主机,但是需要hadoop的配置)$HADOOP_HOME=/usr/hadoop source ~/.bashrc 安装mysql Hive元数据介绍Hive 将元数据存储在 RDBMS 中,一般常用 MySQL 和 Derby。默认情况下,Hive 元数据保存在内嵌的 Derby 数据库中,只能允许一个会话连接,只适合简单的测试。实际生产环境中不适用, 为了支持多用户会话,则需要一个独立的元数据库,使用 MySQL 作为元数据库,Hive 内部对 MySQL 提供了很好的支持,配置一个独立的元数据库 1234567891011121314151617181920212223242526yum install -y mysql-serverchkconfig --add mysqldchkconfig mysqld onchkconfig --list mysqldservice mysqld startmysql -u root -pEnter password: //默认密码为空,输入后回车即可set password for root@localhost=password('root'); 密码设置为rootset password for root@=password('root');默认情况下Mysql只允许本地登录,所以只需配置root@localhost就好设置所有ip访问密码为rootset password for root@%=password('root'); 密码设置为root (其实这一步可以不配)设置master访问密码为rootset password for root@master=password('root'); 密码设置为root (其实这一步可以不配)查询密码select user,host,password from mysql.user; 查看密码是否设置成功设置所有ip可以通过root访问GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;GRANT ALL PRIVILEGES ON *.* TO 'hive'@'%' IDENTIFIED BY 'hive' WITH GRANT OPTION;mysql -uroot -prootcreate user 'hive' identified by 'hive';create user 'hive'@'%' identified by 'hive';create database hive; 配置Hivemkdir iotmp cp hive-default.xml.template hive-site.xml vim hive-site.xml12345678910111213141516171819202122232425262728293031323334353637383940414243444546<configuration><property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.jdbc.Driver</value> <description>Driver class name for a JDBC metastore</description></property><property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://localhost:3306/hive?characterEncoding=UTF-8</value> <description>JDBC connect string for a JDBC metastore</description></property><property> <name>javax.jdo.option.ConnectionUserName</name> <value>root</value> <description>Username to use against metastore database</description></property><property> <name>javax.jdo.option.ConnectionPassword</name> <value>root</value> <description>password to use against metastore database</description></property><property> <name>hive.querylog.location</name> <value>/usr/hive/iotmp</value> <description>Location of Hive run time structured log file</description></property><property> <name>hive.exec.local.scratchdir</name> <value>/usr/hive/iotmp</value> <description>Local scratch space for Hive jobs</description></property><property> <name>hive.downloaded.resources.dir</name> <value>/usr/hive/iotmp</value> <description>Temporary local directory for added resources in the remote file system.</description></property><property> <name>hive.metastore.uris</name> <value>thrift://master:9083</value></property></configuration> Hive 的元数据可以存储在本地的 MySQL 中,但是大多数情况会是一个 mysql 集群,而且不在本地。所以在 hive 中需要开启远程 metastore。由于我是本地的 mysql,我就不配置下列属性了,但是如果是远程的 metastore,配置下面的属性。123456789101112<property> <name>hive.metastore.uris</name> <value></value> <description>Thrift URI for the remote metastore. Used by metastore client to connect to remote metastore.</description></property><property> <name>hive.server2.transport.mode</name> <value>http</value> <description>Server transport mode. \"binary\" or \"http\".</description></property>链接:https://www.jianshu.com/p/87b76a686216 Hive命令启动hive123$HIVE_HOME/bin/hive如果调试,可以加上参数$HIVE_HOME/bin/hivehive -hiveconf hive.root.logger=DEBUG,console 启动hiveserver2123$HIVE_HOME/bin/hive --service hiveserver2or nohup $HIVE_HOME/bin/hiveserver2 1>/var/log/hiveserver.log 2>/var/log/hiveserver.err & hiveserver端口号默认是10000 hiveserver2是否启动netstat -nl|grep 10000 beeline工具测试使用jdbc方式连接可以在部署了hive任意节点上用beeline去连接 12345678$HIVE_HOME/bin/beeline -u jdbc:hive2://hive:10000or$HIVE_HOME/bin/beeline -u jdbc:hive2://hive:10000 -n bigdata 最后一个参数是用户or $HIVE_HOME/bin/beeline 回车,进入beeline的命令界面 输入命令连接hiveserver2 beeline> !connect jdbc:hive2://hive:10000 使用beeline通过jdbc连接上之后就可以像client一样操作。 hiveserver2会同时启动一个webui,端口号默认为10002,可以通过http://localhost:10002/访问界面中可以看到Session/Query/Software等信息。(此网页只可查看,不可以操作hive数据仓库) 参考https://blog.csdn.net/lblblblblzdx/article/details/79760959 参考https://www.cnblogs.com/netuml/p/7841387.html hive元数据库(就是hive库)中有几个表(有好几十个,但只需要记住其中几个就可以了),有哪些字段元数据库最重要的作用是保存一些信息,hive的描述信息,比如说hive有几个database,有几个表,这些表对应的hdfs的地址在哪儿,表有几个字段,建了几个分区,创建了几个自定义函数 columns_v2表记录是主外键关系表,funcs表,是存的创建了什么函数partition表是记录的创建了什么分区 报错 报错 Hive 2.3.3 MetaException(message:Version information not found in metastore.) 1schematool -initSchema -dbType mysql 参考https://stackoverflow.com/questions/50230515/hive-2-3-3-metaexceptionmessageversion-information-not-found-in-metastore http://sishuok.com/forum/blogPost/list/6221.html https://blog.csdn.net/nokia_hp/article/details/79054079","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"Hive","slug":"Hive","permalink":"http://gangtieguo.cn/tags/Hive/"}]},{"title":"Docker-构建免密ssh免密镜像","slug":"Docker-构建免密登录镜像","date":"2018-07-19T18:49:08.996Z","updated":"2019-03-06T03:08:51.862Z","comments":true,"path":"2018/07/20/Docker-构建免密登录镜像/","link":"","permalink":"http://gangtieguo.cn/2018/07/20/Docker-构建免密登录镜像/","excerpt":"免密登录参考的是http://www.shushilvshe.com/data/docker-ssh.html文中涉及命令12345sudo yum -y install openssh-server openssh-clientsssh-keygen -t rsacp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keyschmod 700 ~/.sshchmod 600 ~/.ssh/authorized_keys","text":"免密登录参考的是http://www.shushilvshe.com/data/docker-ssh.html文中涉及命令12345sudo yum -y install openssh-server openssh-clientsssh-keygen -t rsacp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keyschmod 700 ~/.sshchmod 600 ~/.ssh/authorized_keys vim /etc/ssh/sshd_config 找到以下内容,并去掉注释符”#“ 123RSAAuthentication yesPubkeyAuthentication yesAuthorizedKeysFile .ssh/authorized_keys vim /etc/ssh/ssh_config 123Host * StrictHostKeyChecking no UserKnownHostsFile=/dev/null 此文也可参考 http://www.voidcn.com/article/p-gxkeusey-ma.html https://blog.csdn.net/a85820069/article/details/78745899坑使用 docker run -i -t –name c1 centos6.6:basic /bin/bash 运行容器,sshd 服务是不开启的,必须先 - d 在用 exec 切入。 https://www.cnblogs.com/aiweixiao/p/5516974.html 1.【查看是否启动】 启动 SSH 服务 “/etc/init.d/sshd start”。然后用 netstat -antulp | grep ssh 看是否能看到相关信息就可以了。 2.【设置自动启动】 如何设置把 ssh 等一些服务随系统开机自动启动? 方法一:[root@localhost ~]# vi /etc/rc.local 加入:service sshd start 或 /etc/init.d/sshd start chmod 777 /etc/ssh/ssh_host_ecdsa_key 12345678910111213141516# 免密登录mkdir -p /root/.ssh touch /root/.ssh/config echo "StrictHostKeyChecking no" > /root/.ssh/config sed -i "a UserKnownHostsFile /dev/null" /root/.ssh/config # 开机启动RUN yum install -y openssh-server sudo RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config # 下面这两句比较特殊,在centos6上必须要有,否则创建出来的容器sshd不能登录RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key ssh服务文章当中的12345678910111213curl http://mirrors.aliyun.com/repo/Centos-6.repo > /etc/yum.repos.d/CentOS-Base-6-aliyun.repomv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bakyum makecacheyum install -y net-tools which openssh-clients openssh-server iproute.x86_64 wgetservice sshd startsed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_configsed -ri 's/session required pam_loginuid.so/#session required pam_loginuid.so/g' /etc/pam.d/sshdchkconfig sshd oncd ~;ssh-keygen -t rsa -P '' -f ~/.ssh/id_dsa;cd .ssh;cat id_dsa.pub >> authorized_keys","categories":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/categories/Docker/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"}]},{"title":"Docker-machine、engine、swarm集群等相关","slug":"docker-machine等的关系","date":"2018-07-18T06:26:45.439Z","updated":"2018-07-27T08:34:52.084Z","comments":true,"path":"2018/07/18/docker-machine等的关系/","link":"","permalink":"http://gangtieguo.cn/2018/07/18/docker-machine等的关系/","excerpt":"","text":"[TOC] 目标: 搞懂docker的工作机制,(网络机制后面来搞懂) docker的集群,监控机制 如何对镜像更改更小的配置,对外部文件进行修改读取就可以运行docker容器【参考】https://toutiao.io/posts/rgo564/preview,有创建swarm集群的命令,通过ip加入集群的命令 Docker Engine:作为 Docker 镜像构建与容器化启动的工作引擎;Docker Machine:安装与管理 Docker Engine 的工具;Docker Swarm:是 Docker 自 1.12 后自带的集群技术,将多个独立的 Docker Engine 利用 Swarm 技术进行集群化管理;简单来讲,Docker 集群的实现是通过 Docker Machine 在多主机或虚拟机上创建多个 Docker Engine,在利用 Docker Swarm 以某个 Engine 作为集群的初始管理节点,其他 Engine 以管理或工作节点的方式加入,形成完成的集群环境。 swarm 是集群工具,compose 是运行容器的快捷工具 本来就能结合使用,他们之间不冲突,再加 machine 可以创建多个 docker 虚拟环境(如果你没有多台服务器测试的话)三者一起,建立开发测试集群环境简单方便快捷! http://dockone.io/question/160Machine:解决的是操作系统异构安装 Docker 困难的问题,没有 Machine 的时候,CentOS 是一种,Ubuntu 又是一种,AWS 又是一种。有了 Machine,所有的系统都是一样的安装方式。 Swarm:我们有了 Machine 就意味着有了 docker 环境,但是那是单机的,而通常我们的应用都是集群的。这正是 Swarm 要做的事情,给你提供 docker 集群环境和调度策略等。 Compose:有了环境,我们下一步要做什么?部署应用啊。然后我们需要 docker run image1、docker run image2… 一次一次不厌其烦的重复这些操作,每次都写大量的命令参数。Compose 简化了这个流程,只需要把这些内容固话到 docker-compose.yml 中。 目前 Machine、Swarm、Compose 已经可以结合使用,创建集群环境,简单的在上面部署应用。但是还不完善,比如对于有 link 的应用,它们只能跑在 Swarm 集群的一个机器上,即使你的集群有很多机器。可以参考我的另一个问题。 SocketPlane 是 Docker 最近收购的产品,猜想应该是为了强化 Docker 的网络功能,比如提供原生跨主机的网络定制、强化 Swarm 和 Compose 的结合等。 docker挂载https://www.cnblogs.com/ivictor/p/4834864.html 宿主机目录和容器目录的关系 Docker-engine : 当人们说 “Docker” 时,他们通常意味着 Docker Engine,即由 Docker 守护程序组成的客户端 - 服务器应用程序,指定用于与守护程序进行交互的接口的 REST API 以及与守护程序(通过 REST API 包装器)通信的命令行界面(CLI)客户端。Docker Engine 从 CLI 接受docker命令,例如docker run ,docker ps列出运行的容器,docker images以列出镜像等 Docker-machine: Mac OS X 默认是没有虚拟机的,所以要使用 Docker Machine,需要事先安装好 VirtualBox,这在 Docker 的官方文档上有相关说明Docker Machine 是一个工具,用来在虚拟主机上安装 Docker Engine并使用 docker-machine 命令来管理这些虚拟主机 Docker MachineDocker Machine 是用来配置和管理 Docker Engine 的工具。Docker Machine 主要有两个用途:一是为了能在旧的 Mac 和 Windows 系统上使用 Dokcer。二是为了可以管理远程的 Docker。 要知道,Docker Engine 是只能在 Linux 内核上运行的。为什么 Docker Machine 可以使得在旧的 Mac 和 Windows 系统上也能使用 Docker 呢?因为,安装 Docker Toolbox 会安装 Docker Machine 和 Virtual Box 虚拟机,它会配置一个 Docker Engine 到一个虚拟机里的主机上(Linux 内核,默认名字是 default,又称托管主机),后续 Docker 容器就是运行在这个托管主机里的 Docker Engine 之上。 将 Machine CLI 指向正在运行的托管主机,就可以直接该运行docker命令到该主机上。这里貌似东西不少:docker-machine env default会显示 default 主机的环境信息以及对应的配置命令指引: 1234export DOCKER_TLS_VERIFY=\"1\"export DOCKER_HOST=\"tcp://192.168.99.100:2376\"export DOCKER_CERT_PATH=\"/Users/Eason/.docker/machine/machines/default\"export DOCKER_MACHINE_NAME=\"default\" Run this command to configure your shell:1eval "$(docker-machine env default)" 提示很明白了,在 SHELL 里运行 eval “$(docker-machine env default) 就可以把当前 Machine CLI 指向 default 主机了。好处就是,当我们有多个主机时,这样一行命令就能切换环境指向不同的主机,然后后续运行的docker命名也是运行到对应的 Docker Enigne 上。 注:Docker for Mac 和 Docker for Windows 以及 (https://docs.docker.com/toolbox/overview/) 都是自带 Docker Machine 工具的。 这一节的个人理解,也不知道我写得大家看不看得懂。其实主要辨析点就是:Container 容器是 Image 镜像运行在内存中的实例,Container 容器要运行在 Docker Engine 之上,然而 Docker Engine 仅限运行在 Linux 内核之上。Docker Machie 是配置和管理 Docker Engine 的工具,并且可以为旧版的 Mac 和 Windows 系统提供 Docker Engine 的运行环境\b Docker-machine:12Docker Machine 是一个工具,用来在虚拟主机上安装 Docker Engine并使用 docker-machine 命令来管理这些虚拟主机 swarmswarm 是集群工具,compose 是运行容器的快捷工具 本来就能结合使用,他们之间不冲突,再加 machine 可以创建多个 docker 虚拟环境(如果你没有多台服务器测试的话)三者一起, http://dockone.io/question/160 Machine:解决的是操作系统异构安装 Docker 困难的问题,没有 Machine 的时候,CentOS 是一种,Ubuntu 又是一种,AWS 又是一种。有了 Machine,所有的系统都是一样的安装方式。 Swarm:我们有了 Machine 就意味着有了 docker 环境,但是那是单机的,而通常我们的应用都是集群的。这正是 Swarm 要做的事情,给你提供 docker 集群环境和调度策略等。 Compose:有了环境,我们下一步要做什么?部署应用啊。然后我们需要 docker run image1、docker run image2… 一次一次不厌其烦的重复这些操作,每次都写大量的命令参数。Compose 简化了这个流程,只需要把这些内容固话到 docker-compose.yml 中。 目前 Machine、Swarm、Compose 已经可以结合使用,创建集群环境,简单的在上面部署应用。但是还不完善,比如对于有 link 的应用,它们只能跑在 Swarm 集群的一个机器上,即使你的集群有很多机器。可以参考我的另一个问题。 SocketPlane 是 Docker 最近收购的产品,猜想应该是为了强化 Docker 的网络功能,比如提供原生跨主机的网络定制、强化 Swarm 和 Compose 的结合等。 docker挂载https://www.cnblogs.com/ivictor/p/4834864.html 宿主机目录和容器目录的关系 swarm集群搭建的步骤首先 docker-machine ssh在default 使用命令 docker swarm init","categories":[{"name":"原理理解","slug":"原理理解","permalink":"http://gangtieguo.cn/categories/原理理解/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"}]},{"title":"CoudearManager搭建","slug":"CoudearManager搭建","date":"2018-07-10T14:10:04.881Z","updated":"2018-08-10T19:05:49.436Z","comments":true,"path":"2018/07/10/CoudearManager搭建/","link":"","permalink":"http://gangtieguo.cn/2018/07/10/CoudearManager搭建/","excerpt":"[TOC] 创建基础容器1docker run -itd --net=br --name cm --hostname cm yaosong5/centosbase:1.0 &> /dev/null 将下载的包进行解压然后进行拷贝12345docker cp /Users/yaosong/Yao/cloudera-manager-el6-cm5.9.0_x86_64/cloudera cm:/opt/docker cp /Users/yaosong/Yao/cloudera-manager-el6-cm5.9.0_x86_64/cm-5.9.0 cm:/opt/docker cp /Users/yaosong/Yao/mysql-connector-java-5.1.40-bin.jar cm:/opt/cm-5.9.0/share/cmf/lib/docker cp /Users/yaosong/Yao/mysql-connector-java.jar cm:/usr/share/java/docker cp /Users/yaosong/Yao/jdk1.8 cm:/usr/local/","text":"[TOC] 创建基础容器1docker run -itd --net=br --name cm --hostname cm yaosong5/centosbase:1.0 &> /dev/null 将下载的包进行解压然后进行拷贝12345docker cp /Users/yaosong/Yao/cloudera-manager-el6-cm5.9.0_x86_64/cloudera cm:/opt/docker cp /Users/yaosong/Yao/cloudera-manager-el6-cm5.9.0_x86_64/cm-5.9.0 cm:/opt/docker cp /Users/yaosong/Yao/mysql-connector-java-5.1.40-bin.jar cm:/opt/cm-5.9.0/share/cmf/lib/docker cp /Users/yaosong/Yao/mysql-connector-java.jar cm:/usr/share/java/docker cp /Users/yaosong/Yao/jdk1.8 cm:/usr/local/ 将 parcel 文件放至 /opt/cloudera/parcel-repo123docker cp /Users/yaosong/Yao/CDH-5.9.0-1.cdh5.9.0.p0.23-el6.parcel cm:/opt/cloudera/parcel-repodocker cp /Users/yaosong/Yao/CDH-5.9.0-1.cdh5.9.0.p0.23-el6.parcel.sha cm:/opt/cloudera/parcel-repodocker cp /Users/yaosong/Yao/manifest.json cm:/opt/cloudera/parcel-repo vim /etc/profile 123export JAVA_HOME=/usr/local/jdk1.8export PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 初始化 mysql 库1/opt/cm-5.9.0/share/cmf/schema/scm_prepare_database.sh mysql cm -hlocalhost -uroot -proot --scm-host localhost scm scm scm 创建用户(所有节点执行)1useradd --system --home=/opt/cm-5.9.0/run/cloudera-scm-server/ --no-create-home --shell=/bin/false --comment "Cloudera SCM User" cloudera-scm Agent 配置vim /opt/cm-5.9.0/etc/cloudera-scm-agent/config.ini 将 server_host 改为主节点主机名 1server_host=cm1 安装mysqlchkconfig mysqld on 设置允许远程登录 12mysql -u root -p GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; 创建CM用的数据库安装集群时按需创建,详见第七章第13步 12345678910--hive数据库create database hive DEFAULT CHARSET utf8 COLLATE utf8_general_ci;--oozie数据库create database oozie DEFAULT CHARSET utf8 COLLATE utf8_general_ci;--hue数据库create database hue DEFAULT CHARSET utf8 COLLATE utf8_general_ci;create database amon DEFAULT CHARSET utf8 COLLATE utf8_general_ci;create database monitor DEFAULT CHARSET utf8 COLLATE utf8_general_ci; Cloudera推荐设置在试安装的过程,发现Cloudera给出了一些警告 身为一个有洁癖的码农,自然是连黄色的感叹号都要消灭的。因此在安装CM/CDH之前就先全部设置好。 1、设置swap空间vim /etc/sysctl.conf末尾加上vm.swappiness=10 2、关闭大页面压缩试过只设置defrag,但貌似个别节点还是会有警告,干脆全部设置 vim /etc/rc.local末尾加上(永久生效)echo never > /sys/kernel/mm/transparent_hugepage/enabledecho never > /sys/kernel/mm/transparent_hugepage/defrag 启动cloudera manager 服务/opt/cm-5.9.0/etc/init.d/cloudera-scm-server start /opt/cm-5.9.0/etc/init.d/cloudera-scm-agent start 端口 7180 保存为镜像docker commit -m "cloudera manger image" cm yaosong5/cm59:1.0 创建容器docker run -itd --net=br --name cm1 --hostname cm1 yaosong5/cm59:1.0 &> /dev/null docker run -itd --net=br --name cm2 --hostname cm2 yaosong5/cm59:1.0 &> /dev/null docker run -itd --net=br --name cm3 --hostname cm3 yaosong5/cm59:1.0 &> /dev/null 1234567docker stop cm1docker stop cm2docker stop cm3docker rm cm1docker rm cm2docker rm cm3 调错 .SearchRepositoryManager: No read permission to the server storage directory [/var/lib/cloudera-scm-server]2018-07-11 10:20:39,788 ERROR SearchRepositoryManager-0:com.cloudera.server.web.cmf.search.components.SearchRepositoryManager: No write permission to the server storage directory [/var/lib/cloudera-scm-server] 链接hue连接不上节点的 cm-5.x.0/log/cloudera-scm-server/cloudera-scm-server.log,一般情况下应该会说到 ImportError:libxslt.so.1:cannot open shared object file:No such file ordirectory 1yum -y install libxml2-python 提示hue测试连接连接不上,安装依赖:1yum install libxml2-python mod_ssl install krb5-devel cyrus-sasl-gssapi cyrus-sasl-deve libxml2-devel libxslt-devel mysql mysql-devel openldap-devel python-devel python-simplejson sqlite-devel -y","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"CDH","slug":"CDH","permalink":"http://gangtieguo.cn/tags/CDH/"}]},{"title":"Docker-Ambari镜像搭建","slug":"Docker-yaosong5:Ambar镜像i制作","date":"2018-07-09T17:08:44.939Z","updated":"2019-03-06T03:07:21.049Z","comments":true,"path":"2018/07/10/Docker-yaosong5:Ambar镜像i制作/","link":"","permalink":"http://gangtieguo.cn/2018/07/10/Docker-yaosong5:Ambar镜像i制作/","excerpt":"[TOC] 创建基础容器1docker run -itd --net=br --name ambari-agent --hostname ambari-agent yaosong5/centosbase:1.0 &> /dev/null 关闭 selinux , 需要重启vim /etc/selinux/config 1SELINUX=disabled","text":"[TOC] 创建基础容器1docker run -itd --net=br --name ambari-agent --hostname ambari-agent yaosong5/centosbase:1.0 &> /dev/null 关闭 selinux , 需要重启vim /etc/selinux/config 1SELINUX=disabled server端更换yum源12wget http://public-repo-1.hortonworks.com/ambari/centos6/2.x/updates/2.0.1/ambari.repocp ambari.repo /etc/yum.repos.d 安装依赖及其server123yum install epel-release yum repolistyum install ambari-server 启动初始化ambari-server setup会有一连串的提示 会提示安装 jdk,网速好的可以确定,否则可以下载 jdk-6u31-linux-x64.bin,放到 /var/lib/ambari-server/resources/ 下面,可以指定已经安装的jdk接着会提示配置用的数据库,可以选择 Oracle 或 postgresql,选择 n 会按默认配置数据库类型:postgresql数据库:ambari用户名:ambari密码:bigdata如果提示 Oracle JDK license,yes等待安装完成 agent端安装 ambari-agent 12yum install -y ambari-agentchkconfig --add ambari-agent 将 ambari.server 上的 3 个. repo 文件复制到 hadoop 集群的三台服务器上;并完成 yum 源更新的命令。 安装 ambari-agent:在集群的 3 台电脑上执行添加,并添加成开机自启动服务: yum install -y ambari-agent chkconfig –add ambari-agent sudo ambari-agent start 分别启动server agent在server和agent上分别执行 12ambari-agent startambari-server start 访问http://192.168.1.133:8080 用户名密码: admin,admin 保存容器为镜像1docker commit -m \"bigdata:ambari-server\" --author=\"yaosong\" ambr yaosong5/ambari-server:1.0 根据镜像创建容器123docker run -itd --net=br --name ambari1 --hostname ambari1 yaosong5/ambari-server:1.0 &> /dev/nulldocker run -itd --net=br --name ambari2 --hostname ambari2 yaosong5/ambari-server:1.0 &> /dev/nulldocker run -itd --net=br --name ambari3 --hostname ambari3 yaosong5/ambari-server:1.0 &> /dev/null 停止and删除容器1234567docker stop ambari1docker stop ambari2docker stop ambari3docker rm ambari1docker rm ambari2docker rm ambari3","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"Ambari","slug":"Ambari","permalink":"http://gangtieguo.cn/tags/Ambari/"}]},{"title":"Docker-yaosong5/Hue搭建及运行命令","slug":"Docker-yaosongt:hue搭建及其运行命令","date":"2018-07-09T16:37:50.903Z","updated":"2019-06-17T04:40:09.370Z","comments":true,"path":"2018/07/10/Docker-yaosongt:hue搭建及其运行命令/","link":"","permalink":"http://gangtieguo.cn/2018/07/10/Docker-yaosongt:hue搭建及其运行命令/","excerpt":"[TOC] 本次采用的ant maven来编译hue启动一个基础容器docker run -itd --net=br --name hue --hostname hue yaosong5/centosbase:1.0 &> /dev/null","text":"[TOC] 本次采用的ant maven来编译hue启动一个基础容器docker run -itd --net=br --name hue --hostname hue yaosong5/centosbase:1.0 &> /dev/null 拷贝源包将ant、hue4.0.0、ant、maven等下载到本地结业后,再拷贝到容器(这样更快速) docker cp /Users/yaosong/Yao/ant 4115ea59088e:/ docker cp /Users/yaosong/Yao/maven 4115ea59088e:/ docker cp /Users/yaosong/Yao/hue4 4115ea59088e:/usr/ 配置HOME12345678910vim ~/.bashrc加入MAVEN_HOME=/mavenexport MAVEN_HOMEexport PATH=${PATH}:${MAVEN_HOME}/binANT_HOME=/antPATH=$JAVA_HOME/bin:$ANT_HOME/bin:$PATHHUE_HOME=/hue4使其生效source ~/.bashrc 安装依赖,编译hue需要安装一些依赖yum install gmp-devel -y 参考 http://www.aizhuanji.com/a/0Vo0qEMW.html 若解决不了1yum install asciidoc cyrus-sasl-devel cyrus-sasl-gssapi cyrus-sasl-plain gcc gcc-c++ krb5-devel libffi-devel libtidy libxml2-devel libxslt-devel make mysql-devel openldap-devel sqlite-devel openssl-devel gmp-devel -y 参考链接:https://www.jianshu.com/p/417788238e3d \b编译安装hue首先编译 Hue,并在要安装 Hue 的节点上创建 Hue 用户和 hue 组 创建 Hue 用户1234groupadd hueuseradd hue -g huecd $HUE_HOMEchown -R hue:hue * 注:需要注意的是 hue 在编译时有两种方式:1.通过maven、ant编译 2.通过python编译(在centos6.5因为自身python为2.6.6版本和hue编译需要2.7版本会有一点小冲突,故采用1)两种方式都是在hue目录下 make apps,只是第一种方式要先配置maven、ant的环境而已 12cd $HUE_HOMEmake apps 参考 :https://blog.csdn.net/u012802702/article/details/68071244 如果报错 1/usr/hue4/Makefile.vars:42: *** "Error: must have python development packages for 2.6 or 2.7. Could not find Python.h. Please install python2.6-devel or python2.7-devel". Stop. 需执行 1234可以先查看一下含 python-devel 的包yum search python | grep python-devel64 位安装 python-devel.x86_64,32 位安装 python-devel.i686,我这里安装:sudo yum install python-devel.x86_64 -y 更改hue的配置文件vim $HUE_HOME/desktop/conf/hue.ini mysql1234567[[database]] host=master port=3306 engine=mysql user=root password=root name=hue hive123hive_server_host=masterhive_server_port=10000hive_conf_dir=$HIVE_HOME/conf hadoop-hdfs12345fs_defaultfs=hdfs://master:9000logical_name=masterwebhdfs_url=http://master:50070/webhdfs/v1hadoop_hdfs_home=$HADOOP_HOMEhadoop_conf_dir=$HADOOP_HOME/etc/hadoop hadoop-yarn在 [hadoop].[[yarn_clusters]].[[[default]]] 下 1234resourcemanager_host=masterresourcemanager_port=8032resourcemanager_api_url=http://master:8088proxy_api_url=http://master:8088 hbase在 [hbase] 节点下 123hbase_clusters=(HBASE|master:9090)hbase_conf_dir=$HBASE_HOME/confuse_doas=true 大数据各组件满足hue进行相应配置安装mysql由于需要hue需要存放一些元数据故安装mysql 12345678910111213yum install -y mysql-serverservice mysqld startmysql -u root -pEnter password: //默认密码为空,输入后回车即可set password for root@localhost=password('root'); 密码设置为root默认情况下Mysql只允许本地登录,所以只需配置root@localhost就好set password for root@%=password('root'); 密码设置为root (其实这一步可以不配)set password for root@master=password('root'); 密码设置为root (其实这一步可以不配)select user,host,password from mysql.user; 查看密码是否设置成功GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;create database hue; 报错DatabaseError DatabaseError:(1146,”Table ‘hue.desktop_settings’ doesn’t exist”)-初始化mysql 完成以上的这个配置,启动 Hue, 通过浏览器访问,会发生错误,原因是 mysql 数据库没有被初始化DatabaseError: (1146,”Table ‘hue.desktop_settings’ doesn’t exist”)执行以下指令对 hue 数据库进行初始化 123cd $HUE_HOME/build/env/bin/hue syncdbbin/hue migrate 此外需要注意的是如果使用的是:$HUE_HOME/build/env/bin/hue syncdb --noinput 则不会让输入初始的用户名和密码,只有在首次登录时才会让输入,作为超级管理员账户。\b hdfshdfs-site.xml增加一个值开启 hdfs 的 web 交互123456 <!--HUE 增加一个值开启 hdfs 的 web 交互--><property> <name>dfs.webhdfs.enabled</name> <value>true</value></property> <!--HUE 增加一个值开启 hdfs 的 web 交互--> core-site.xml12345678910<!--《为了让 hue 能够访问 hdfs,需要在 hdfs-site.xml 里面配置一些内容--><property> <name>hadoop.proxyuser.hue.hosts</name> <value>*</value></property><property> <name>hadoop.proxyuser.hue.groups</name> <value>*</value></property><!--《为了让 hue 能够访问 hdfs,需要在 hdfs-site.xml 里面配置一些内容--> hbasehbase-site.xml 12345678910<!-- hue支持 --><property> <name>hbase.thrift.support.proxyuser</name> <value>true</value></property><property> <name>hbase.regionserver.thrift.http</name> <value>true</value></property><!-- hue支持 --> hue 访问 hbase 是用的 thriftserver,并且是 thrift1,不是 thrift2,所以要在 master 上面启动 thrif1 1$HBASE_HOME/bin/hbase-daemon.sh start thrift 参考 https://blog.csdn.net/Dante_003/article/details/78889084 读取hbase问题为解决访问Failed to authenticate to HBase Thrift Server, check authentication configurations.需要在hue的配置文件中配置 1use_doas=true 参考http://gethue.com/hbase-browsing-with-doas-impersonation-and-kerberos/ 若以上配置未能解决问题,还需要将core-site.xml拷贝到hbase/conf,并添加以下内容 12345678<property> <name>hadoop.proxyuser.hbase.hosts</name> <value>*</value></property><property> <name>hadoop.proxyuser.hbase.groups</name> <value>*</value></property> [参考]https://blog.csdn.net/u012802702/article/details/68071244 hiveHue 与框架 Hive 的集成开启 Hive Remote MetaStorenohup $HIVE_HOME/bin/hive --service metastore & hive 只需启动 hiveserver2,thriftserver 的 10000 端口启动即可123nohup $HIVE_HOME/bin/hiveserver2 &或者nohup HIVE_HOME/bin/hive --service hiveserver2 & 解决 hue ui 界面查询中文乱码问题在 [[[mysql]]]节点下 1options={\"init_command\":\"SET NAMES'utf8'\"} [参考]https://blog.csdn.net/u012802702/article/details/68071244 依赖的组件启动Mysqlservice mysqld start hadoopstart-all.sh hive然后需要同时启动 hive 的 metastore 和 hiveserve2 12nohup hive --service metastore &nohup hive --service hiveserver2 & hbaseHue 需要读取 HBase 的数据是使用 thrift 的方式,默认 HBase 的 thrift 服务没有开启,所有需要手动额外开启 thrift 服务。 启动 thrift service$HBASE_HOME/bin/hbase-daemon.sh start thrift thrift service 默认使用的是 9090 端口,使用如下命令查看端口是否被占用 netstat -nl|grep 9090 依赖启动的脚本1234567891011#!/bin/bash#启动mysqlservice mysqld start#启动hadoopsh /hadoop-start.sh#启动hivesh /hive-start-servers2.sh#启动 thrift service$HBASE_HOME/bin/hbase-daemon.sh start thrift#启动huenohup $HUE_HOME/build/env/bin/supervisor & hue启动命令1$HUE_HOME/build/env/bin/supervisor & (注:想要后台执行就是 $HUE_HOME/build/env/bin/supervisor & ) 或者 1$HUE_HOME/build/env/bin/hue runserver_plus 0.0.0.0:8888 【参考】https://blog.csdn.net/hexinghua0126/article/details/80338779 hue的\bweb服务端口:8888 hue停止命令pkill -U hue 报错1、如果修改配置文件后,启动后无法进人 hue 界面 12345可能是配置文件被锁住了cd $HUE_HOME/desktop/confls –arm –rf hue.ini.swp或者 hadoop、hive 等服务没有启动起来 2、在 hue\b界面异常,导致 hive 无法使用安装插件:yum install cyrus-sasl-plain cyrus-sasl-devel cyrus-sasl-gssapi 操作镜像保存为镜像docker commit -m "hue" hue yaosong5/hue4:1.0 创建容器docker run -itd --net=br --name gethue --hostname gethue gethue/hue:latest &> /dev/null\b映射宿主机的\b\bhosts文件及其hue的配置文件方式启动容器12docker run --name=hue -d --net=br -v /etc/hosts/:/etc/hosts -v $PWD/pseudo-distributed.ini:/hue/desktop/conf/pseudo-distributed.ini yaosong5/hue4:1.0 –net=br为\b了宿主机和容器之前ip自由访问所搭建的网络模式,如有需求\b请参考 其他参考 1docker run --name=hue -d --net=br -v /etc/hosts/:/etc/hosts -v $PWD/pseudo-distributed.ini:/hue/desktop/conf/pseudo-distributed.ini gethue/hue:latest 参考:https://blog.csdn.net/Dante_003/article/details/78889084","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"Hue","slug":"Hue","permalink":"http://gangtieguo.cn/tags/Hue/"}]},{"title":"ES测试命令","slug":"ES测试命令","date":"2018-07-09T16:11:03.158Z","updated":"2019-06-17T04:40:09.375Z","comments":true,"path":"2018/07/10/ES测试命令/","link":"","permalink":"http://gangtieguo.cn/2018/07/10/ES测试命令/","excerpt":"[TOC] 简单命令测试和展示es的功能","text":"[TOC] 简单命令测试和展示es的功能 插入记录 1234567891011curl -H "Content-Type: application/json" -XPUT 'http://localhost:9200/store/books/1' -d '{"title": "Elasticsearch: The Definitive Guide","name" : { "first" : "Zachary", "last" : "Tong"},"publish_date":"2015-02-06","price":"49.99"}' 在添加一个书的信息123456789curl -H "Content-Type: application/json" -XPUT 'http://elk1:9200/store/books/2' -d '{"title": "Elasticsearch Blueprints","name" : { "first" : "Vineeth", "last" : "Mohan"},"publish_date":"2015-06-06","price":"35.99"}' 通过ID获得文档信息 1curl -H "Content-Type: application/json" -XGET 'http://elk1:9200/store/books/1' 12345678910111213141516curl -H "Content-Type: application/json" -XGET 'http://elk1:9200/store/books/_search' -d '{"query" : {"filtered" : { "query" : { "match_all" : {} }, "filter" : { "term" : { "price" : 35.99 } } }}}' 在浏览 1234567891011curl -H "Content-Type: application/json" -XPUT 'http://elk1:9200/store/books/1' -d '{"title": "Elasticsearch: The Definitive Guide","name" : { "first" : "Zachary", "last" : "Tong"},"publish_date":"2015-02-06","price":"49.99"}' 1curl -H "Content-Type: application/json" -XPUT 'http://127.0.0.1:9200/kc22k2_test' -d 1curl -XPUT elk1:9200/test 12345678910111213141516curl -XGET 'http://elk1:9200/_cluster/state?pretty'{ "error" : {"root_cause" : [ { "type" : "master_not_discovered_exception", "reason" : null }],"type" : "master_not_discovered_exception","reason" : null }, "status" : 503}","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"ELK","slug":"ELK","permalink":"http://gangtieguo.cn/tags/ELK/"},{"name":"es","slug":"es","permalink":"http://gangtieguo.cn/tags/es/"}]},{"title":"yaosong5/bigdata:2.0-Hadoop&Spark&hive&zk&hue组合容器的搭建","slug":"Docker-yaosong5:bigdata镜像制作","date":"2018-07-08T17:06:20.114Z","updated":"2019-03-12T02:48:25.729Z","comments":true,"path":"2018/07/09/Docker-yaosong5:bigdata镜像制作/","link":"","permalink":"http://gangtieguo.cn/2018/07/09/Docker-yaosong5:bigdata镜像制作/","excerpt":"[TOC] 配置centos集群 hadoop spark组件启动容器各组件版本对应hbase1.2 hive 版本 2.0.0 hbase1.x ZooKeeper 3.4.x is required as of HBase 1.0.0 新建容器,为减少工作量,引用的是有ssh服务的Docker镜像kinogmt/centos-ssh:6.7,生成容器os为基准。 1docker run -itd --name bigdata --hostname bigdata kinogmt/centos-ssh:6.7 &> /dev/null 注意必须要以-d方式启动,不然sshd服务不会启动,这算是一个小bug","text":"[TOC] 配置centos集群 hadoop spark组件启动容器各组件版本对应hbase1.2 hive 版本 2.0.0 hbase1.x ZooKeeper 3.4.x is required as of HBase 1.0.0 新建容器,为减少工作量,引用的是有ssh服务的Docker镜像kinogmt/centos-ssh:6.7,生成容器os为基准。 1docker run -itd --name bigdata --hostname bigdata kinogmt/centos-ssh:6.7 &> /dev/null 注意必须要以-d方式启动,不然sshd服务不会启动,这算是一个小bug 在容器中下载需要的elk的源包,做解压就不赘述,很多案例教程。 我是采用的下载到宿主机,解压后,用 “docker cp 解压包目录 os:/usr/loca/“来传到容器内,比在容器内下载速度更快 拷贝文件到容器 命令格式docker cp 本地文件路径 容器id或者容器名称: 将所有组件下载解压并拷贝到容器 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849docker cp /Users/yaosong/Downloads/hadoop-2.8.0.tar.gz bigdata:/docker cp /Users/yaosong/Downloads/spark-2.2.0-bin-without-hadoop.tgz bigdata:/docker cp /Users/yaosong/Downloads/jdk-8u144-linux-x64.rpm bigdata:/docker cp /Users/yaosong/Downloads/spark-2.1.0-bin-hadoop2.6.tgz bigdata:/docker cp /Users/yaosong/Yao/spark源包/hive bigdata:/usrdocker cp /Users/yaosong/Downloads/jdk-8u144-linux-x64.rpm bigdata:/docker cp /Users/yaosong/Downloads/hadoop-2.8.0.tar.gz bigdata:/docker cp /Users/yaosong/Downloads/spark-2.2.0-bin-without-hadoop.tgz bigdata:/docker cp /Users/yaosong/Downloads/spark-2.1.0-bin-hadoop2.6.tgz bigdata:/docker cp /Users/yaosong/Yao/spark源包/hbase bigdata:/usrdocker cp /Users/yaosong/Yao/spark源包/zk bigdata:/usrdocker cp /Users/yaosong/Yao/ant bigdata:/usrdocker cp /Users/yaosong/Yao/maven bigdata:/usrdocker cp /Users/yaosong/Yao/hue4 bigdata:/usr创建homevim /etc/profilemac: vim ~/.bashrc添加以下内容 export JAVA_HOME=/usr/java/jdk export PATH=$JAVA_HOME:$PATH export SCALA_HOME=/usr/scala-2.12.3/ export HADOOP_HOME=/usr/hadoop export HADOOP_CONFIG_HOME=$HADOOP_HOME/etc/hadoop export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export PATH=$PATH:$HADOOP_HOME/bin export PATH=$PATH:$HADOOP_HOME/sbin export SPARK_DIST_CLASSPATH=$(hadoop classpath) SPARK_MASTER_IP=master SPARK_LOCAL_DIRS=/usr/spark SPARK_DRIVER_MEMORY=1G export SPARK_HOME=/usr/spark export PATH=$SPARK_HOME/bin:$PATH export PATH=$SPARK_HOME/sbin:$PATH MAVEN_HOME=/usr/maven export MAVEN_HOME export PATH=${PATH}:${MAVEN_HOME}/bin ANT_HOME=/usr/ant PATH=$JAVA_HOME/bin:$ANT_HOME/bin:$PATH export ANT_HOME PATH HUE_HOME=/usr/hue4 export ZK_HOME=/usr/zk export HBASE_HOME=/usr/hbase export PATH=$HBASE_HOME/bin:$PATH export PATH=$ZK_HOME/bin:$PATH 安装创建 hadoop 集群所需目录:在以下配置文件中会有以下目录 12345cd $HADOOP_HOME;mkdir tmpmkdir namenodemkdir datanodecd $HADOOP_CONFIG_HOME/ 更改配置文件cd $HADOOP_CONFIG_HOME/ or cd $HADOOP_HOME/etc/hadoop hdfs slaves12slave01slave02 core-site.xml:12345678910111213141516171819202122232425262728293031323334<property> <name>hadoop.tmp.dir</name> <value>/usr/hadoop/tmp</value> <description>A base for other temporary directories.</description></property><property> <name>fs.default.name</name> <value>hdfs://master:9000</value> <final>true</final> <description>The name of the default file system. A URI whose scheme and authority determine the FileSystem implementation. </description> </property> <!--hive的配置,参考https://blog.csdn.net/lblblblblzdx/article/details/79760959--> <property> <name>hive.server2.authentication</name> <value>NONE</value> </property> <!--hive的配置hadoop代理用户 root用户提交的任务可以在任意机器上以任意组的所有用户的身份执行。--> <property> <name>hadoop.proxyuser.root.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.root.groups</name> <value>*</value> </property> <!--HUE 增加一个值开启 hdfs 的 web 交互--> <property> <name>dfs.webhdfs.enabled</name> <value>true</value> </property> <!--HUE 增加一个值开启 hdfs 的 web 交互--> hdfs-site.xml:12345678910111213141516171819202122232425262728293031<property> <name>dfs.replication</name> <value>2</value> <final>true</final> <description>Default block replication. The actual number of replications can be specified when the file is created. The default is used if replication is not specified in create time. </description></property><property> <name>dfs.namenode.name.dir</name> <value>/usr/hadoop/namenode</value> <final>true</final></property><property> <name>dfs.datanode.data.dir</name> <value>/usr/hadoop/datanode</value> <final>true</final></property><!--《为了让 hue 能够访问 hdfs,需要在 hdfs-site.xml 里面配置一些内容--><property> <name>hadoop.proxyuser.hue.hosts</name> <value>*</value></property><property> <name>hadoop.proxyuser.hue.groups</name> <value>*</value></property><!--《为了让 hue 能够访问 hdfs,需要在 hdfs-site.xml 里面配置一些内容--> mapred-site.xml:12345678<property> <name>mapred.job.tracker</name> <value>master:9001</value> <description>The host and port that the MapReduce job tracker runs at. If \"local\", then jobs are run in-process as a single map and reduce task. </description></property> yarn-site.xml:12345678910111213141516171819202122232425<property> <name>yarn.nodemanager.pmem-check-enabled</name> <value>false</value></property><property> <name>yarn.nodemanager.vmem-check-enabled</name> <value>false</value> <description>Whether virtual memory limits will be enforced for containers</description></property><property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>256</value></property><property> <name>yarn.resourcemanager.address</name> <value>master:8032</value></property><property> <name>yarn.resourcemanager.scheduler.address</name> <value>master:8030</value></property><property> <name>yarn.resourcemanager.resource-tracker.address</name> <value>master:8031</value></property> 如果是hadoop3以上版本,需要在start-dfs.sh start-yarn.sh中开头空白处分别配置一下内容 12345678910111213vim $HADOOP_HOME/sbin/start-dfs.shHDFS_DATANODE_USER=rootHADOOP_SECURE_DN_USER=hdfsHDFS_NAMENODE_USER=rootHDFS_SECONDARYNAMENODE_USER=rootvim $HADOOP_HOME/sbin/start-yarn.shYARN_RESOURCEMANAGER_USER=rootHADOOP_SECURE_DN_USER=rootYARN_NODEMANAGER_USER=yarnYARN_PROXYSERVER_USER=root 格式化namenode1$HADOOP_HOME/bin/hadoop namenode -format 启动集群$HADOOP_HOME/sbin/start-all.sh 测试 1yarn 8088端口 http://yourip:8088 1hdfs 50070端口 hdfs3.0为9870 http://yourip:50070 spark只需要在slaves中添加12slave01slave02 sparkUI端口8080 测试spark集群启动spark 1$HADOOP_HOME/bin/start-all.sh 官网命令12345678$SPARK_HOME/bin/spark-submit --class org.apache.spark.examples.SparkPi \\--master yarn \\--deploy-mode cluster \\--driver-memory 512m \\--executor-memory 512m \\--executor-cores 1 \\$SPARK_HOME/examples/jars/spark-examples*.jar \\10 执行spark on yarn命令行模式123456789spark-shell --master yarn --deploy-mode client --driver-memory 1g --executor-memory 1g --executor-cores 1spark-shell --master yarn --deploy-mode client --driver-memory 512m --executor-memory 512m --executor-cores 1spark-shell --master yarn --deploy-mode client --driver-memory 475m --executor-memory 475m --executor-cores 1spark-shell --master yarn --deploy-mode client --driver-memory 350m --executor-memory 350m --executor-cores 1spark-shell --master yarn --deploy-mode client --driver-memory 650m --executor-memory 650m --executor-cores 1 创建镜像1docker commit -m \"bigdata基础组件镜像\" bigdata yaosong5/bigdata:2.0 2.0镜像对比1.0:1.0只有hadoop-待确定 创建容器123docker run -itd --net=br --name master --hostname master yaosong5/bigdata:2.0 &> /dev/nulldocker run -itd --net=br --name slave01 --hostname slave01 yaosong5/bigdata:2.0 &> /dev/nulldocker run -itd --net=br --name slave02 --hostname slave02 yaosong5/bigdata:2.0 &> /dev/null 停止and 删除容器123456docker stop masterdocker stop slave01docker stop slave02docker rm masterdocker rm slave01docker rm slave02 w","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Spark","slug":"Spark","permalink":"http://gangtieguo.cn/tags/Spark/"},{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"Hadoop","slug":"Hadoop","permalink":"http://gangtieguo.cn/tags/Hadoop/"}]},{"title":"HBase容器的搭建","slug":"hbasezk容器的搭建","date":"2018-07-08T16:01:32.819Z","updated":"2019-06-17T04:40:09.380Z","comments":true,"path":"2018/07/09/hbasezk容器的搭建/","link":"","permalink":"http://gangtieguo.cn/2018/07/09/hbasezk容器的搭建/","excerpt":"[TOC] 创建hbase镜像拷贝源码12345docker cp /Users/yaosong/Yao/hbase 8019587d559b:/usr/docker cp /Users/yaosong/Yao/zk 8019587d559b:/usr/docker cp /Users/yaosong/Yao/hbasezkStart.sh 8019587d559b:/usr/docker cp /Users/yaosong/Yao/hbase-start.sh 8019587d559b:/usr/docker cp /Users/yaosong/Yao/zk-start.sh 8019587d559b:/usr/ 参考https://www.cnblogs.com/netbloomy/p/6677883.html","text":"[TOC] 创建hbase镜像拷贝源码12345docker cp /Users/yaosong/Yao/hbase 8019587d559b:/usr/docker cp /Users/yaosong/Yao/zk 8019587d559b:/usr/docker cp /Users/yaosong/Yao/hbasezkStart.sh 8019587d559b:/usr/docker cp /Users/yaosong/Yao/hbase-start.sh 8019587d559b:/usr/docker cp /Users/yaosong/Yao/zk-start.sh 8019587d559b:/usr/ 参考https://www.cnblogs.com/netbloomy/p/6677883.html 解压tar -zxvf hbase-1.3.0-bin.tar.gz进入 hbase 的配置目录,在 hbase-env.sh 文件里面加入 java 环境变量. 即: 12vim hbase-env.shexport JAVA_HOME=JAVA_HOME=/usr/java/jdk 关闭 HBase 自带的 Zookeeper, 使用 Zookeeper 集群: 12vim hbase-env.shexport HBASE_MANAGES_ZK=false hbase-site.xml 123456789101112131415161718192021222324<configuration> <property> <name>hbase.rootdir</name> <value>hdfs://master:9000/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>hbasezk1,hbasezk2,hbasezk3</value> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/usr/hbase/tmp/zk/data</value> </property> <!-- webui的配置 --> <property> <name>hbase.master.info.port</name> <value>60010</value> </property> <!-- webui新增的配置 --></configuration> 创建zk的datadir目录 mkdir -p /usr/hbase/tmp/zk/data 编辑配置目录下面的文件 regionservers. 命令: vim $HBASE_HOME/config/regionservers 加入如下内容: hbasezk1 hbasezk2 hbasezk3 把 Hbase 复制到其他机器scp 开启 hbase 服务。命令如下: 哪台上运行哪台就为hmaster $HBASE_HOME/bin/start-hbase.sh 在 hbasezk1,2,3 中的任意一台机器使用 $HBASE_HOME/bin/hbase shell 进入 hbase 自带的 shell 环境,然后使用命令 version 等,进行查看 hbase 信息及建立表等操作。 要配置 HBase 高可用的话,只需要启动两个 HMaster,让 Zookeeper 自己去选择一个 Master Acitve。","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"HBase","slug":"HBase","permalink":"http://gangtieguo.cn/tags/HBase/"}]},{"title":"FLINK容器的搭建","slug":"Docker-yaosong5:flink制作及其运行命令","date":"2018-07-08T16:00:22.635Z","updated":"2018-08-07T02:52:58.322Z","comments":true,"path":"2018/07/09/Docker-yaosong5:flink制作及其运行命令/","link":"","permalink":"http://gangtieguo.cn/2018/07/09/Docker-yaosong5:flink制作及其运行命令/","excerpt":"[TOC] 来源容器 flk新建容器,为减少工作量,引用的是有ssh服务的Docker镜像kinogmt/centos-ssh:6.7,生成容器os为基准。 1docker run -itd --name flk --hostname flk kinogmt/centos-ssh:6.7 &> /dev/null 注意必须要以-d方式启动,不然sshd服务不会启动,这算是一个小bug","text":"[TOC] 来源容器 flk新建容器,为减少工作量,引用的是有ssh服务的Docker镜像kinogmt/centos-ssh:6.7,生成容器os为基准。 1docker run -itd --name flk --hostname flk kinogmt/centos-ssh:6.7 &> /dev/null 注意必须要以-d方式启动,不然sshd服务不会启动,这算是一个小bug 在容器中下载需要的elk的源包。做解压就不赘述,很多案例教程。 我是采用的下载到宿主机,解压后,用 “docker cp 解压包目录 os:/usr/loca/“来传到容器内,比在容器内下载速度更快 复制源包123> docker cp /Users/yaosong/Yao/hadoop flk:/usr/> docker cp /Users/yaosong/Yao/flink flk:/usr/> 配置homeexport JAVA_HOME=/usr/java/jdk1.8.0_144/ export PATH=$JAVA_HOME/bin:$PATH export FLINK_HOME=/usr/flink export HADOOP_HOME=/usr/hadoop export HADOOP_CONFIG_HOME=$HADOOP_HOME/etc/hadoop export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export PATH=$PATH:$HADOOP_HOME/bin export PATH=$PATH:$HADOOP_HOME/sbin export PATH=$PATH:$FLINK_HOME/bin 配置hadoop 参考: []: http://gangtieguo.cn/2018/07/20/Docker%E4%B8%ADhadoop%20spark%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA/ “Docker 中 hadoop,spark 镜像搭建” flink.yml 12345678910111213141516 high-availability: zookeeper# The path where metadata for master recovery is persisted. While ZooKeeper stores# the small ground truth for checkpoint and leader election, this location stores# the larger objects, like persisted dataflow graphs.# # Must be a durable file system that is accessible from all nodes# (like HDFS, S3, Ceph, nfs, ...) # high-availability.storageDir: hdfs:///flink/ha/# The list of ZooKeeper quorum peers that coordinate the high-availability# setup. This must be a list of the form:# "host1:clientPort,host2:clientPort,..." (default clientPort: 2181)#high-availability.zookeeper.quorum: zk1:2181,zk2:2181,zk3:2181 Master 1master:8081 slave 12slave01slave02 zoo.cfg 123server.1=zk1:2888:3888server.2=zk2:2888:3888server.3=zk3:2888:3888 需要在yarn-site.xml中配置 12345678<property> <name>yarn.resourcemanager.am.max-attempts</name> <value>4</value></property><property> <name>yarn.nodemanager.resource.cpu-vcores</name> <value>8</value></property> 保存镜像1docker commit -m \"bigdata:flink,hadoop\" --author=\"yaosong\" flk yao/flinkonyarn:1.0 获得flk 容器123docker run -itd --net=br --name flk1 --hostname flk1 yao/flinkonyarn:1.0 &> /dev/nulldocker run -itd --net=br --name flk2 --hostname flk2 yao/flinkonyarn:1.0 &> /dev/nulldocker run -itd --net=br --name flk3 --hostname flk3 yao/flinkonyarn:1.0 &> /dev/null 停止/删除flk 容器123456docker stop flk1docker stop flk2docker stop flk3docker rm flk1docker rm flk2docker rm flk3 官方:wordcountflink测试命令由于在本地搭建,机器配置有限,故设置不同参数命令来运行官方wordcount 1234567891011flink run -m yarn-cluster $FLINK_HOME/examples/batch/WordCount.jarflink run -m yarn-cluster -ynd 2 $FLINK_HOME/examples/batch/WordCount.jarflink run -m yarn-cluster -yn 4 $FLINK_HOME/examples/batch/WordCount.jarflink run -m yarn-cluster -yn 6 $FLINK_HOME/examples/batch/WordCount.jarflink run -m yarn-cluster -yn 8 $FLINK_HOME/examples/batch/WordCount.jarflink run -m yarn-cluster -yn 10 $FLINK_HOME/examples/batch/WordCount.jar","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"FLINK","slug":"FLINK","permalink":"http://gangtieguo.cn/tags/FLINK/"}]},{"title":"kafka的启动及常用命令","slug":"kafka启动脚本及命令","date":"2018-07-08T15:58:00.558Z","updated":"2018-09-09T03:51:50.714Z","comments":true,"path":"2018/07/08/kafka启动脚本及命令/","link":"","permalink":"http://gangtieguo.cn/2018/07/08/kafka启动脚本及命令/","excerpt":"kafka的启动脚本 kafka集群没有脚本能直接启动所有的节点,所以为了繁杂去每台机器启动,所以编写了脚本启动所有 注意参照替换 vim kafka-startall.sh 1234567891011#!/bin/bashsed -e '1c borker.id=0' $KAFKA_HOME/config/server.properties$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.propertiesssh root@slave01 \"sed -i '1c borker.id=1 ' $KAFKA_HOME/config/server.properties\"ssh root@slave01 \"sed -i '5c host.name=slave01 ' $KAFKA_HOME/config/server.properties\"ssh root@slave01 \"sed -i '6c advertised.host.name=slave01 ' $KAFKA_HOME/config/server.properties\"ssh root@slave01 \"$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"sed -i '1c borker.id=2 ' $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"sed -i '5c host.name=slave02 ' $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"sed -i '6c advertised.host.name=slave02 ' $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties\"","text":"kafka的启动脚本 kafka集群没有脚本能直接启动所有的节点,所以为了繁杂去每台机器启动,所以编写了脚本启动所有 注意参照替换 vim kafka-startall.sh 1234567891011#!/bin/bashsed -e '1c borker.id=0' $KAFKA_HOME/config/server.properties$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.propertiesssh root@slave01 \"sed -i '1c borker.id=1 ' $KAFKA_HOME/config/server.properties\"ssh root@slave01 \"sed -i '5c host.name=slave01 ' $KAFKA_HOME/config/server.properties\"ssh root@slave01 \"sed -i '6c advertised.host.name=slave01 ' $KAFKA_HOME/config/server.properties\"ssh root@slave01 \"$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"sed -i '1c borker.id=2 ' $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"sed -i '5c host.name=slave02 ' $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"sed -i '6c advertised.host.name=slave02 ' $KAFKA_HOME/config/server.properties\"ssh root@slave02 \"$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties\" kafka的shell命令jps -ml 查看kafka的运行情况 启动12nohup $KAFKA_HOME/bin/kafka-server-start.sh $KAFKA_HOME/config/server.properties > /dev/null 2>&1 &/usr/kafka/bin/kafka-server-start.sh -daemon /usr/kafka/config/server.properties 创建 topic1$KAFKA_HOME/bin/kafka-topics.sh --create --zookeeper zk1:2181,zk2:2181,zk3:2181 --replication-factor 2 --partitions 3 --topic shuaige 查看消费位置1$KAFKA_HOME/bin/kafka-run-class.sh kafka.tools.ConsumerOffsetChecker --zookeeper zk1:2181 --group testGroup 查看某个 Topic 的详情1$KAFKA_HOME/bin/kafka-topics.sh --topic test --describe --zookeeper zk1:2181,zk2:2181,zk3:2181 producer创建命令行消息生产者1$KAFKA_HOME/bin/kafka-console-producer.sh --broker-list kafka1:9092,kafka2:9092,kafka3:9092 --topic shuaige Consumer创建命令行消费者也可以说是查看message 注意:从 kafka-0.9 版本及以后,kafka 的消费者组和 offset 信息就不存 zookeeper 了,而是存到 broker 服务器上,所以,如果你为某个消费者指定了一个消费者组名称(group.id),那么,一旦这个消费者启动,这个消费者组名和它要消费的那个 topic 的 offset 信息就会被记录在 broker 服务器上。 old版本1$KAFKA_HOME/bin/kafka-console-consumer.sh --zookeeper zk1:2181,zk2:2181,zk3:2181 --topic shuaige --from-beginning --group testGroup new版本1$KAFKA_HOME/bin/kafka-console-consumer.sh --bootstrap-server kafka1:9092,kafka2:9092,kafka3:9092 --topic shuaige --from-beginning --group testGroup 消费者要从头开始消费某个 topic 的全量数据,需要满足 2 个条件(spring-kafka): 12(1)使用一个全新的"group.id"(就是之前没有被任何消费者使用过);(2)指定"auto.offset.reset"参数的值为earliest; 对应的 spring-kafka 消费者客户端配置参数为: 1234<!-- 指定消费组名 --><entry key="group.id" value="fg11"/><!-- 从何处开始消费,latest 表示消费最新消息,earliest 表示从头开始消费,none表示抛出异常,默认latest --><entry key="auto.offset.reset" value="earliest"/> 查看所有 topiczk 表示 zk 的地址 地址表示 zookeeper 的地址 1$KAFKA_HOME/bin/kafka-topics.sh --list --zookeeper zk1:2181,zk2:2181,zk3:2181 删除 topic1sh $KAFKA_HOME/bin/kafka-topics.sh --delete --zookeeper zk1:2181,zk2:2181,zk3:2181 --topic test","categories":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/categories/大数据/"}],"tags":[{"name":"Kafka","slug":"Kafka","permalink":"http://gangtieguo.cn/tags/Kafka/"}]},{"title":"Docker-yaosong5/elk镜像-ELK容器的搭建","slug":"Docker-yaosong5:elk镜像制作","date":"2018-07-08T15:43:05.950Z","updated":"2019-06-17T04:40:09.368Z","comments":true,"path":"2018/07/08/Docker-yaosong5:elk镜像制作/","link":"","permalink":"http://gangtieguo.cn/2018/07/08/Docker-yaosong5:elk镜像制作/","excerpt":"[TOC] 来源容器 elk新建容器,为减少工作量,引用的是有ssh服务的Docker镜像kinogmt/centos-ssh:6.7,生成容器os为基准。 1docker run -itd --name elk --hostname elk kinogmt/centos-ssh:6.7 &> /dev/null 注意必须要以-d方式启动,不然sshd服务不会启动,这算是一个小bug","text":"[TOC] 来源容器 elk新建容器,为减少工作量,引用的是有ssh服务的Docker镜像kinogmt/centos-ssh:6.7,生成容器os为基准。 1docker run -itd --name elk --hostname elk kinogmt/centos-ssh:6.7 &> /dev/null 注意必须要以-d方式启动,不然sshd服务不会启动,这算是一个小bug 在容器中下载需要的elk的源包。做解压就不赘述,很多案例教程。 我是采用的下载到宿主机,解压后,用 “docker cp 解压包目录 os:/usr/loca/“来传到容器内,比在容器内下载速度更快 设置Home vim ~/bashrc 123456789export ES_HOME=/usr/esexport PATH=$ES_HOME/bin:$PATHexport KIBANA_HOME=/usr/kibanaexport PATH=$KIBANA_HOME/bin:$PATHexport LOGSTASH_HOME=/usr/logstashexport PATH=$LOGSTASH_HOME/bin:$PATHexport NODE_HOME=/usr/nodeexport PATH=$NODE_HOME/bin:$PATHexport NODE_PATH=$NODE_HOME/lib/node_modules source ~/.bashrc es配置加启动es的配置如下 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455# 这里指定的是集群名称,需要修改为对应的,开启了自发现功能后,ES 会按照此集群名称进行集群发现 #集群名称,通过组播的方式通信,通过名称判断属于哪个集群cluster.name: elk#节点名称,要唯一(每个节点不一样)node.name: elk1#如果是master节点设置成true node.master: true# 数据目录path.data: /usr/es/data# log 目录 path.logs: /usr/es/logs# 修改一下 ES 的监听地址,这样别的机器也可以访问 network.host: 0.0.0.0# 默认的端口号 http.port: 9200 # 设置节点间交互的tcp端口,默认是9300 #transport.tcp.port: 9300#Elasticsearch将绑定到可用的环回地址,并将扫描端口9300到9305以尝试连接到运行在同一台服务器上的其他节点。#这提供了自动集群体验,而无需进行任何配置。数组设置或逗号分隔的设置。每个值的形式应该是host:port或host#(如果没有设置,port默认设置会transport.profiles.default.port 回落到transport.tcp.port)。#请注意,IPv6主机必须放在括号内。默认为127.0.0.1, [::1]discovery.zen.ping.unicast.hosts: [\"elk1\",\"elk2\",\"elk3\"] #如果没有这种设置,遭受网络故障的集群就有可能将集群分成两个独立的集群 - 分裂的大脑 - 这将导致数据丢失# discovery.zen.minimum_master_nodes: 3 # enable cors,保证_site 类的插件可以访问 es #避免出现跨域问题 要设置之后 header插件才可以用http.cors.enabled: true http.cors.allow-origin: \"*\"# Centos6 不支持 SecComp,而 ES5.2.0 默认 bootstrap.system_call_filter 为 true 进行检测,所以导致检测失败,失败后直接导致 ES 不能启动。 bootstrap.memory_lock: false bootstrap.system_call_filter: false#在chorem中 当elasticsearch安装x-pack后还可以访问#http.cors.allow-headers: Authorization#启用审核以跟踪与您的Elasticsearch群集进行的尝试和成功的交互#xpack.security.audit.enabled: true#设置为true来锁住内存。因为内存交换到磁盘对服务器性能来说是致命的,当jvm开始swapping时es的效率会降低,所以要保证它不swap#bootstrap.memory_lock: true 不能通过root启动创建用户elk1234useradd elkgroupadd elkusermod -a -G elk elkecho elk | passwd --stdin elk 将elk添加到sudoers12echo \"elk ALL = (root) NOPASSWD:ALL\" | tee /etc/sudoers.d/elkchmod 0440 /etc/sudoers.d/elk 解决sudo: sorry, you must have a tty to run sudo问题,在/etc/sudoer注释掉 Default requiretty 一行1sudo sed -i 's/Defaults requiretty/Defaults:elk !requiretty/' /etc/sudoers 修改文件所有者为elk用户1chown -R elk:elk /usr/es/ 设置资源参数由于es启动会有资源要求 123sudo vim /etc/security/limits.d/90-nproc.conf添加elk soft nproc 4096 再在docker-machine设置参数 12docker-machine sshsudo sysctl -w vm.max_map_count=655360 es启动脚本本机 su elk -c "$ES_HOME/bin/elasticsearch -d" ssh远程启动其他主机 ssh elk@elk1 " $ES_HOME/bin/elasticsearch -d" ssh root@elk1 " su elk -c $ES_HOME/bin/elasticsearch " 安装es插件header安装nodejs一般预装的版本不对 123yum erase nodejs npm -y # 卸载旧版本的nodejsrpm -qa 'node|npm' | grep -v nodesource # 确认nodejs是否卸载干净yum install nodejs -y # 安装npm 安装的版本会有不对 下载合适版本 1234cd /usrwget https://npm.taobao.org/mirrors/node/latest-v4.x/node-v4.4.7-llinux-x64.tar.gztar -zxvf node-v4.4.7-linux-x64.tar.gzmv node-v8.9.1-linux-x64 node 直接将node目录配置到home即可 12export NODE_HOME=/usr/nodeexport PATH=$NODE_HOME/bin:$PATH 下载 header,安装grunt(所有命令在hear的所在目录执行) wget https://github.com/mobz/elasticsearch-head/archive/master.zip unzip master.zip 看当前 head 插件目录下有无 node_modules/grunt 目录:没有:执行命令创建: 1npm install grunt --save 安装 grunt:grunt 是基于 Node.js 的项目构建工具,可以进行打包压缩、测试、执行等等的工作,head 插件就是通过 grunt 启动 1npm install -g grunt-cli 参考https://blog.csdn.net/ggwxk1990/article/details/78698648 npm install 安装所下载的header包 1npm install 配置header修改服务器监听地址: Gruntfile.jsvi $HEADER_HOME/Gruntfile.js 在第 93 行添加: 1hostname:'*', g) 修改连接地址:vim $HEADER_HOME/_site/app.js 1285行 1this.base_uri = this.config.base_uri || this.prefs.get(\"app-base_uri\") || \"http://192.168.33.16:9200\" header启动在 elasticsearch-head-master 目录下 1grunt server 或者 npm run start header的默认端口为9100 header的停止命令elk集群启动由于logstash和kibana都不需要其他设置,直接用预设的配置 elasticSearch脚本启动脚本 vim es-start.sh 12345678#!/bin/bashsed -i '6c node.name: es1 '$ES_HOME/config/elasticsearch.ymlsu - elk -c \"$ES_HOME/bin/elasticsearch -d\"ssh root@elk2 \"sed -i '6c node.name: es2 ' $ES_HOME/config/elasticsearch.yml\"ssh root@elk2 ' su - elk -c \"$ES_HOME/bin/elasticsearch -d\" 'ssh root@elk3 \"sed -i '6c node.name: es3 ' $ES_HOME/config/elasticsearch.yml\"ssh root@elk3 ' su - elk -c \"$ES_HOME/bin/elasticsearch -d\" ' kibana脚本启动(端口为5601) 启动单机(只需要启动单机) $KIBANA_HOME/bin/kibana12345678#!/bin/bashsed -i '3c http://elk1:9200 '$KIBANA_HOME/config/kibana.ymlnohup $KIBANA_HOME/bin/kibana &ssh root@elk2 \"sed -i '3c http://elk2:9200 ' $KIBANA_HOME/config/kibana.yml\"ssh root@elk2 \"nohup $KIBANA_HOME/bin/kibana & \"ssh root@elk3 \"sed -i '3c http://elk3:9200 ' $KIBANA_HOME/config/kibana.yml\"ssh root@elk3 \"nohup $KIBANA_HOME/bin/kibana & \" logstash脚本启动单机启动 12#$LOGSTASH_HOME/bin/logstash -f 配置文件的目录$LOGSTASH_HOME/bin/logstash -f logstash.conf \b 集群启动脚本 logstash-start.sh1234#!/bin/bashnohup $LOGSTASH_HOME/bin/logstash -f $LOGSTASH_HOME/conf/$1 &ssh root@elk2 \"nohup $LOGSTASH_HOME/bin/logstash -f $LOGSTASH_HOME/conf/$1 & \"ssh root@elk3 \"nohup $LOGSTASH_HOME/bin/logstash -f $LOGSTASH_HOME/conf/$1 & \" 保存容器为镜像1docker commit -m \"elk镜像\" --author=\"yaosong\" os yaosong5/elk:1.0 os是你配置的容器名 生成elk 容器123docker run -itd --net=br --name elk1 --hostname elk1 yaosong5/elk:1.0 &> /dev/nulldocker run -itd --net=br --name elk2 --hostname elk2 yaosong5/elk:1.0 &> /dev/nulldocker run -itd --net=br --name elk3 --hostname elk3 yaosong5/elk:1.0 &> /dev/nulls 停止/删除elk 容器1234567docker stop elk1docker stop elk2docker stop elk3docker rm elk1docker rm elk2docker rm elk3 参考elk 操作命令es操作命令http://www.yfshare.vip/2017/11/04/%E9%83%A8%E7%BD%B2FileBeat-logstash-elasticsearch%E9%9B%86%E7%BE%A4-kibana/#%E9%85%8D%E7%BD%AE-filebeart 其他yum erase nodejs npm -y # 卸载旧版本的nodejsrpm -qa ‘node|npm’ | grep -v nodesource # 确认nodejs是否卸载干净yum install nodejs -y","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"ELK","slug":"ELK","permalink":"http://gangtieguo.cn/tags/ELK/"},{"name":"es","slug":"es","permalink":"http://gangtieguo.cn/tags/es/"}]},{"title":"Docker常用命令汇集","slug":"Docker命令汇集","date":"2018-06-26T02:33:37.714Z","updated":"2019-06-17T04:40:09.372Z","comments":true,"path":"2018/06/26/Docker命令汇集/","link":"","permalink":"http://gangtieguo.cn/2018/06/26/Docker命令汇集/","excerpt":"","text":"[TOC] Docker源配置 安装过程中需要重国外 docker 仓库下载文件,速度太慢,建议配置 docker 国内镜像仓库: vi /etc/docker/daemon.json 1{\"registry-mirrors\":[\"http://c1f0a193.m.daocloud.io\"] } 常用命令启动容器(也是创建) 123docker run -itd --net=br --name master --hostname master yaosong5/bigdata:1.0 &> /dev/null如果以 /bin/bash启动的话,sshd服务不会启动(docker未知bug)用 &> /dev/null这种方式sshd服务才会启动 创建容器-run12345--name --hostname (同-h) --net= -d表示后台启动此命令不会打印出容器iddocker run -itd --net=br --name hm --hostname hadoop-master kiwenlau/hadoop:1.0 &> /dev/null (hadoop镜像)设置静态固定ipdocker run -d --net=br --name=c6 --ip=192.168.33.6 nginx 12345自动分配Ipdocker run -d --net=br --name=c1 nginx设置docker默认ip段命令docker run -itd -P -p 50070:50070 -p 8088:8088 -p 8080:8080 --name master -h master --add-host slave01:172.17.0.3 --add-host slave02:172.17.0.4 centos:ssh-spark-hadoop 创建容器-Dockerfile在dockerfille文件所在的目录中执行以下命令 123docker build -f Dockerfile -t hadoop:v1 .#也可以指定Dockerfile如下:docker build -t centos:tt - < Dockerfile 容器挂载目录分两种类型 compose文件: 12volumes: - /Users/yaosong/Yao/dev/hadoop/dfs/name:/root/hadoop/dfs/name shell命令: 12-v : docker run -it -v /test:/soft centos /bin/bash “:”前目录为宿主机目录,后目录为容器目录 引用 基于 docker 的大数据架构 如果是mac或者win机器,需要在virtualbox虚拟机中设置共享文件夹的share名称对应mac的目录虚拟机中的目录 sudo mount -t vboxsf vagrant /Users/yaosong sudo mount -t vboxsf Yao /Users/yaosong/Yao/ 取消挂载 sudo umount vagrant 删除所有未用的 Data volumes1docker volume prune run 命令解释12345-d 是后台启动docker run -itd --net=br --name spark --hostname spark yaosong5/spark:2.1.0 &> /dev/nullsudo docker exec -it spark bash(进入后台启动的容器)和下面一样(直接进入)docker run -it --net=br --name spark --hostname spark yaosong5/spark:2.1.0 bash pause暂停恢复容器123docker pause 容器名恢复数据库容器db01提供服务。docker unpause 容器名 exec 进入后台容器123docker exec -it spark bashdocker exec -it 容器名 bash执行命令 docker exec -it 容器名 ip addr 可以拿到 a0 容器的 ip logs查看容器启动日志1docker logs -f -t --tail 100 kanbigdata_namenode_1 查看容器信息12docker inspect hm执行命令 docker exec -it a0 ip addr 可以拿到 a0 容器的 ip 启动 关闭 删除容器123docker start docker stop 容器名docker rm 容器名 cp容器宿主互拷文件12docker cp /Users/yaosong/Yao/etc.tar f7e795c0fddd:/后为容器id:/目录 删除镜像1234(根据镜像id删除)docker rmi 00de07ebadff用docker images -a 查看image id,也可docker rmi 镜像名:版本号 保存镜像123docker commit -m \"centos-6.9 with spark 2.2.0 and hadoop 2.8.0\" os centos:hadoop-sparkdocker commit -m \"bigdata:spark,hadoop,hive,mysql and shell foundation\" --author=\"yaosong\" master yao/os/bigdata:2.1 Docker用DockerFile创建镜像123docker build -t hadoop:v1- <Dockerfiledocker build -t=\"hadoop:v1\" . (.表示是当前文件夹,也就是dockerfile所在文件夹)docker build -f Dockerfile -t hadoop:v1 . 此命令也可 一键启动docker-compose.yml编排的所有服务1docker-compose -f docker-compose.yml up -d Docker改变标签docker tag IMAGEID(镜像id) REPOSITORY:TAG(仓库:标签) 1docker tag b7a66cb0e8ba yaosong5/bigdata:1.0 搜索docker镜像1docker search yaosong5 登录docker账户1docker login 登录docker hub中注册的账户 上传仓库1docker push yaosong5/elk:1.0 容器保存为镜像,加载本地镜像 引用1234567docker save imageID > filenamedocker load <filename如:docker save 4f9e92e56941> /Users/yaosong/centosSparkHadoop.tardocker load </Users/yaosong/centosSparkHadoop.tar通过 image 保存的镜像会保存操作历史,可以回滚到历史版本。 保存,加载容器命令:12docker export containID > filenamedocker import filename [newname] 通过容器保存的镜像不会保存操作历史,所以文件小一点。如果要运行通过容器加载的镜像, 需要在运行的时候加上相关命令。 高阶命令参考:清理Docker占用的磁盘空间 删除所有关闭的容器 1docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs docker rm 删除所有dangling镜像(即无tag的镜像): 1docker rmi $(docker images | grep \"^<none>\" | awk \"{print $3}\") 删除所有dangling数据卷(即无用的volume): 1docker volume rm $(docker volume ls -qf dangling=true) docker system它可以用于管理磁盘空间 磁盘使用情况 docker system df命令,类似于Linux上的df命令,用于查看Docker的磁盘使用情况 清理磁盘 docker system prune 命令可以用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及dangling镜像(即无tag的镜像)。docker system prune -a命令清理得更加彻底,可以将没有容器使用Docker镜像都删掉。注意,这两个命令会把你暂时关闭的容器,以及暂时没有用到的Docker镜像都删掉了…所以使用之前一定要想清楚吶。 Docker-machine命令列出docker-machine1docker-machine ls 开启虚拟机1docker-machine start default 关闭虚拟机1docker-machine stop default 重启虚拟机1docker-machine restart default 删除虚拟机1docker-machine rm default 设置环境变量docker-machine1eval $(docker-machine env default) # Setup the environment 其他参考ip -4 addr chkconfig sshd on rpm -qa|grep ssh。 为了快速实现我们就不自己装 SSH 服务了,hub.docker.com 上的 kinogmt/centos-ssh:6.7 这个镜像就能满足我们的要求","categories":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/categories/Docker/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"}]},{"title":"各种快捷键","slug":"各种快捷键","date":"2018-06-21T15:40:13.306Z","updated":"2019-06-17T04:40:09.404Z","comments":true,"path":"2018/06/21/各种快捷键/","link":"","permalink":"http://gangtieguo.cn/2018/06/21/各种快捷键/","excerpt":"一些常用快捷键让人事半功倍","text":"一些常用快捷键让人事半功倍 |iterm|Command + Shift + h iterms2复制历史 分屏 command + option + 方向键 command + [ 或 command + ] 分屏切换屏幕 Control + a 到行首 Control + u 清除当前行 Control + e 到行尾 Control + p / !! 上一条命令 Control + k 从光标处删至命令行尾 (本来 Control + u 是删至命令行首,但iterm中是删掉整行) Control + w A + d 从光标处删至字首/尾 Control + k 删除到文本末尾 Control + h 删掉光标前的自负 Control + d 删掉光标后的自负 Control + r 搜索命令历史,这个较常用 |Alfred|Command + Option + C afrend剪切板历史 Command + Option + / afrend路径历史 Command + Option + \\ afrend对搜索的路径进行操作 如复制等等 |sublime|Command + Shift + d 复制一行 Command + Option + f 查找并替换 CTRL + - 上个打开的文件 |idea|Command + Shift + F12 编栏全屏 其实就是 Hide All Tool Windows (隐藏所有工具窗口) 这个操作的快捷键。 Command + Option + Space 类名或接口名提示 Control + ; 是什么 代替鼠标 Command + l 跳到指定行 Command + w 关闭标签页 Option + 上 和windows的ctrl+w相同 递进选中代码块 Alt + Insert Command + N 代码自动生成,如生成对象的 set / get 方法,构造函数,toString() 等 Alt + 前方向键 Control + 前方向键 当前光标跳转到当前文件的前一个方法名位置 Ctrl + Alt + Enter Command + Option + Enter 光标所在行上空出一行,光标定位到新行 Ctrl + Alt + 左方向键 Command + Option + 左方向键 退回到上一个操作的地方 Ctrl + Alt + 右方向键 Command + Option + 右方向键 前进到上一个操作的地方 Command + Option + T 包围代码 Command + Shift +v 历史 Find usage 查看变量方法的类的直接使用情况 Shift + Enter 开始新的一行 Command + P 方法参数提示 Command + U 前往父类 Command + +/- 展开/折叠代码 Alt + 1,2,3...9 Command + 1,2,3...9 显示对应数值的选项卡,其中 1 是 Project 用得最多 Control + Option + O 优化导入的类,可以对当前文件和整个包目录使用 Ctrl + Alt + T 对选中的代码弹出环绕选项弹出层 Control + Option + H 继承关系 Control + H 接口到实现类 Control + Shift + J 智能的将代码拼接成一行 Command+Alt+V 引入变量,自动导入变量 Option + F7 查询所选对象/变量被引用 4、类、方法、文件定位 查找类 ctr + N 查找文件 Ctrl + Shift + N 符号定位 Ctrl + Alt + Shift + N 查看文件结构 ctrl + F12 最近打开的文件 ctr + E 定位下一个方法 alt + down 定位上一个方法 alt + up 查看方法参数信息 ctr + p 查看方法、类的 doc ctr + Q 行数 Command + l 5、类、方法的结构查看、定位 跳到类或方法的声明 ctr + B 定位到类的父类、接口 ctr + U 画图 ommand+Option+u 查看类的继承结构 ctr + H 查看方法的继承结构 ctr + Shift + H 查看类或方法被调用情况 ctr + alt +H 原地参看类、方法的声明 Ctrl + Shift + I Control + H 显示当前类的层次结构 继承 Command + Shift + H 显示方法层次结构 Control + Option + H 显示调用层次结构 Command + L 在当前文件跳转到某一行的指定处 Command + B / Command + 鼠标点击 进入光标所在的方法/变量的接口或是定义处 Command + Option + B 跳转到实现处 Command + G 查找模式下,向下查找 Command + Shift + U 大小写切换 |Mac| Shift+Command+G 跳转打开文件夹 Command + Shift + . 显示隐藏文件 Command + Control + 空格 emoji表情 Shift + Option)+ K Apple logo 「 」 Command+i 简介 Shift+Control+d 搜狗表情包 Shift + Command + 空格 历史文件 Control + Command +F 全屏模式 Command + z 打开safari下最近关闭tab页面 Command + Option + Shift + Esc 强制退出活跃的 Command + Option + Esc 强制退出 Command + ` 切换同一应用的窗口 Shift + Command + d alt + cmd + space 快速打开finder cmd + Option + Shift + v 去格式粘贴(亲测大部分软件都可以) Command + Tab 切换应用的时候,可以松开Tab,然后按Q退出选中的应用。 三指点击网页链接,可以预览链接网页。 按住右上角的新建选项卡按钮能快速浏览并选择最近关闭的窗口 |finder|Command + 1,2,3 图标,列表,分栏显示文件夹 Command + Control + P 复制路径 自己配置 Command + Option + B 快捷键标记 自己配置 Command + O 打开文件夹 Command + 下 进入文件夹 Command + 上 返回文件夹 Command +[ 返回 Command + ] 前进 | mac命令|根据 asker 提示 作补充: command + fn + 左/右,可以调整到文件开头 / 结尾。 fn + 左/右相当于home/end在 网页和多数文档中适用。 defaults write com.apple.finder QuitMenuItem -bool YES 设置finder可以关闭 open -n /Applications/WizNote.app 多次打开一个应用 mac 没有声音 sudo kill -9 `ps ax|grep 'coreaudio[a-z]' |awk '{print $1}'` sudo killall coreaudiod 使用后的效果,可以说是非常明显了,再也不会有在「挤牙膏」的感觉。让它回到最开始的状态: defaults delete com.apple.dock; killall Dock defaults write com.apple.Dock autohide-delay -float 0 && killall Dock 打开开机声音 sudo nvram -d SystemAudioVolume 双屏分任务工作!只要按住窗口左上角的绿色+即可 去掉资源库文件夹的隐藏属性 chflags nohidden ~/Library/ 打开隐藏属性 chflags hidden ~/Library/ 调节音量的同时按住 Option + Shift键 显示“隐藏文件” Command + Shift + . defaults write com.apple.finder AppleShowAllFiles -bool true;killall Finder 隐藏 defaults write com.apple.finder AppleShowAllFiles -bool true;killall Finder 关闭开机声音 sudo nvram SystemAudioVolume=%80, 省略号 1、依次按 ⌃ ⌘ 空格 2、⇧ 数字6 Option 点击 Dock 图标,按住 Option 点击 Dock 中的图标,则会在桌面显示该应用所有窗口 Option + 左:向左移动一个单词 Option + 右:向右移动一个单词 Option + Delete:删除左边一个单词 Option + Fn + Delete:删除右边一个单词 设置 dock 显示时间命令 打开终端输入以下命令 #先修改停留时间(后面数字为停留时间)如: defaults write com.apple.dock autohide-delay -int 0 ##(时间设为最短) defaults write com.apple.dock autohide-delay -int 0.5 ##(时间设为 0.5s) defaults write com.apple.dock autohide-delay -int 10 ##(时间设为 10s) #使设置生效 killall Dock |推荐|再推荐个人 池健强 《人生元编程》作者 他的博客和微信上有很多干货 ","categories":[{"name":"快捷键","slug":"快捷键","permalink":"http://gangtieguo.cn/categories/快捷键/"}],"tags":[{"name":"快捷键","slug":"快捷键","permalink":"http://gangtieguo.cn/tags/快捷键/"},{"name":"Mac","slug":"Mac","permalink":"http://gangtieguo.cn/tags/Mac/"},{"name":"Idea","slug":"Idea","permalink":"http://gangtieguo.cn/tags/Idea/"},{"name":"Finder","slug":"Finder","permalink":"http://gangtieguo.cn/tags/Finder/"}]},{"title":"Centos7上搭建Jenkins","slug":"Centos7上搭建Jenkins","date":"2018-06-21T14:11:28.272Z","updated":"2019-06-17T03:26:31.988Z","comments":true,"path":"2018/06/21/Centos7上搭建Jenkins/","link":"","permalink":"http://gangtieguo.cn/2018/06/21/Centos7上搭建Jenkins/","excerpt":"之前用yum模式安装,总是启动报错,解决了一番,未找到解决方案,后直接下载war包进行安装部署 默认安装了Java","text":"之前用yum模式安装,总是启动报错,解决了一番,未找到解决方案,后直接下载war包进行安装部署 默认安装了Java 1. 安装 jenkins 123456cd /optmkdir /jenkinscd jenkinsmkdir jenkins_homemkdir jenkins_nodewget http://mirrors.jenkins-ci.org/war/latest/jenkins.war 2. 编写可执行文件 vim start_jenkins.sh1234#!/bin/bashJENKINS_ROOT=/opt/jenkinsexport JENKINS_HOME=$JENKINS_ROOT/jenkins_homejava -jar $JENKINS_ROOT/jenkins.war --httpPort=8000 修改文件的权限: chmod a+x start_jenkins.sh 启动 jenkins: nohup ./start_jenkins.sh > jenkins.log 2>& 1& 3 访问 jenkins 输入 http:// 服务器地址: 8000 注意:在启动日志中会出现初始密码,这个用来首次登陆Jenkins使用 参考在 Centos7 上搭建 jenkins","categories":[{"name":"安装部署","slug":"安装部署","permalink":"http://gangtieguo.cn/categories/安装部署/"}],"tags":[{"name":"Jenkins","slug":"Jenkins","permalink":"http://gangtieguo.cn/tags/Jenkins/"}]},{"title":"Docker-Hadoop集群【引用】","slug":"Docker-安装Hadoop集群【引用】","date":"2018-06-03T06:36:21.404Z","updated":"2019-03-06T03:06:19.369Z","comments":true,"path":"2018/06/03/Docker-安装Hadoop集群【引用】/","link":"","permalink":"http://gangtieguo.cn/2018/06/03/Docker-安装Hadoop集群【引用】/","excerpt":"Docker配置Hadoop集群环境 在网上找到一个网友自制的镜像,拉取配置都是参考的,记录一下。","text":"Docker配置Hadoop集群环境 在网上找到一个网友自制的镜像,拉取配置都是参考的,记录一下。 拉取镜像 sudo docker pull kiwenlau/hadoop-master:0.1.0sudo docker pull kiwenlau/hadoop-slave:0.1.0sudo docker pull kiwenlau/hadoop-base:0.1.0sudo docker pull kiwenlau/serf-dnsmasq:0.1.0 查看下载的镜像 sudo docker images 在github中拉取源代码(或者在oschina中拉取)git clone https://github.com/kiwenlau/hadoop-cluster-docker开源中国git clone http://git.oschina.net/kiwenlau/hadoop-cluster-docker 运行容器拉取镜像后,打开源代码文件夹,并且运行脚本 cd hadoop-cluster-docker 注意:运行脚本时,需要先启动docker服务 ./start-container.sh 一共开启了 3 个容器,1 个 master, 2 个 slave。开启容器后就进入了 master 容器 root 用户的根目录(/root) 查看root目录下文件 测试容器是否正常运行serf members 参考:基于 Docker 快速搭建多节点 Hadoop 集群","categories":[{"name":"环境配置","slug":"环境配置","permalink":"http://gangtieguo.cn/categories/环境配置/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"http://gangtieguo.cn/tags/Docker/"},{"name":"Hadoop","slug":"Hadoop","permalink":"http://gangtieguo.cn/tags/Hadoop/"}]},{"title":"命令积累","slug":"大数据命令积累","date":"2018-05-31T06:10:40.710Z","updated":"2018-09-05T14:30:30.285Z","comments":true,"path":"2018/05/31/大数据命令积累/","link":"","permalink":"http://gangtieguo.cn/2018/05/31/大数据命令积累/","excerpt":"$HADOOP_HOME/bin/hdfs oev -i edits -o edits.xml 查看元数据","text":"$HADOOP_HOME/bin/hdfs oev -i edits -o edits.xml 查看元数据","categories":[{"name":"碎片知识","slug":"碎片知识","permalink":"http://gangtieguo.cn/categories/碎片知识/"}],"tags":[{"name":"大数据","slug":"大数据","permalink":"http://gangtieguo.cn/tags/大数据/"},{"name":"命令","slug":"命令","permalink":"http://gangtieguo.cn/tags/命令/"}]},{"title":"jsontool使用","slug":"json-tool使用","date":"2018-05-30T01:36:02.864Z","updated":"2018-08-10T17:46:56.174Z","comments":true,"path":"2018/05/30/json-tool使用/","link":"","permalink":"http://gangtieguo.cn/2018/05/30/json-tool使用/","excerpt":"","text":"json-tool使用:java -jar json-tool.jar "json文件目录" "jsonPath路径"示例: 1java -jar /Users/yaosong/Documents/json-tool.jar "/Users/yaosong/tmp/access_report_data_by_token.json" "$.report_data.behavior_check[?(@.check_point_cn == '朋友圈在哪里')].evidence"","categories":[{"name":"tool","slug":"tool","permalink":"http://gangtieguo.cn/categories/tool/"}],"tags":[{"name":"命令","slug":"命令","permalink":"http://gangtieguo.cn/tags/命令/"}]},{"title":"git命令总结","slug":"git命令总结","date":"2018-05-21T17:43:07.025Z","updated":"2019-07-10T06:34:22.313Z","comments":true,"path":"2018/05/22/git命令总结/","link":"","permalink":"http://gangtieguo.cn/2018/05/22/git命令总结/","excerpt":"提交 git add .git commit -m “ “git push origin mastergit push origin master -f 拉取git pull <远程主机名> <远程分支名>:<本地分支名>如拉取远程的 master 分支到本地 wy 分支:git pull origin master:wy git 放弃本地修改 强制更新12git fetch --allgit reset --hard origin/master git fetch 只是下载远程的库的内容,不做任何的合并 git reset 把HEAD指向刚刚下载的最新的版本 分支切换","text":"提交 git add .git commit -m “ “git push origin mastergit push origin master -f 拉取git pull <远程主机名> <远程分支名>:<本地分支名>如拉取远程的 master 分支到本地 wy 分支:git pull origin master:wy git 放弃本地修改 强制更新12git fetch --allgit reset --hard origin/master git fetch 只是下载远程的库的内容,不做任何的合并 git reset 把HEAD指向刚刚下载的最新的版本 分支切换 查看分支:git branch创建分支:git branch 切换分支:git checkout 创建 + 切换分支:git checkout -b 合并某分支到当前分支:git merge 删除分支:git branch -d","categories":[{"name":"工程框架","slug":"工程框架","permalink":"http://gangtieguo.cn/categories/工程框架/"}],"tags":[{"name":"git","slug":"git","permalink":"http://gangtieguo.cn/tags/git/"}]},{"title":"部署博客到云服务器","slug":"转移Github博客到云服务器","date":"2018-05-20T16:56:17.069Z","updated":"2019-06-17T04:40:09.405Z","comments":true,"path":"2018/05/21/转移Github博客到云服务器/","link":"","permalink":"http://gangtieguo.cn/2018/05/21/转移Github博客到云服务器/","excerpt":"简单记录转移到博客到云服务器","text":"简单记录转移到博客到云服务器 原理及准备 我们在自己的电脑上写好博客, 使用 git 发布到代码仓库进行备份, git 仓库接收到 push 请求后, 使用 webhook 配合 nodejs 自动进行服务器端页面的更新. 准备安装Git和NodeJS (CentOS 环境) 1yum install git 安装NodeJS 1curl --silent --location https://rpm.nodesource.com/setup_5.x | bash - 服务器构建webhook方式服务器端的” 钩子”我们借助一个 node 插件 github-webhook-handler 来快速完成配合 github webhook 的操作, 其他 git 平台也有相应的插件, 如配合 coding 的 coding-webhook-handler. 监听脚本 我们借助一个 node 插件 github webhook-handler来快速完成配合 github webhook 的操作, 其他 git 平台也有相应的插件, 如配合 coding 的 coding-webhook-handler. 使用 npm install -g github-webhook-handler 命令来安装到服务器端.conding则为npm install -g coding-webhook-handler 切换到服务器站点目录,如我的是 /root/blog,新建一个public目录,将你的github仓库中的master分支pull到该目录中,这个目录作为这个博客的根目录了 123456cd /root/blogmkdir public cd public git initgit remote add origin https://github.com/yaosong5/yaosong5.github.iogit pull origin master 然后我们创建一个webhooks.js文件,将以下的内容粘贴,这相当于Node.js 服务器的代码构建 123456789101112131415161718192021222324252627282930var http = require('http')var createHandler = require('github-webhook-handler')var handler = createHandler({ path: '/', secret: 'yao' })function run_cmd(cmd, args, callback) { var spawn = require('child_process').spawn; var child = spawn(cmd, args); var resp = \"\"; child.stdout.on('data', function(buffer) { resp += buffer.toString(); }); child.stdout.on('end', function() { callback (resp) });}http.createServer(function (req, res) { handler(req, res, function (err) { res.statusCode = 404 res.end('no such location') })}).listen(7777)handler.on('error', function (err) { console.error('Error:', err.message)})handler.on('push', function (event) { console.log('Received a push event for %s to %s', event.payload.repository.name, event.payload.ref); run_cmd('sh', ['./deploy.sh',event.payload.repository.name], function(text){ console.log(text) });}) 注意上段代码中第 3 行 { path: ‘/‘, secret: ‘改为你的secret’ } 中 secret 可以改为你喜欢的口令, 这口令将在下面的步骤中起到作用 ,配置github webhooks的时候填入的口令, 请留意. 第 19 行 listen(7777) 中 7777 为监听程序需要使用的端口. 执行脚本上面的 javascript 代码是用来捕捉 github 发来的信号并发起一个执行 ./deploy.sh 的脚本, 接下来我们还需要写 deploy.sh 的内容. 123456789101112#!/bin/bashWEB_PATH='/root/blog/public'echo \"Start deployment\"cd $WEB_PATHecho \"pulling source code...\"git reset --hard origin/mastergit clean -fgit pullgit checkout masterecho \"Finished.\" 将以上代码的第 3 行改为你服务器中的实际目录. 接下来只需要开启监听就可以了. tips: 在此之前你可以使用 node webhook.js 来测试一下监听程序是否能够正常运行.我在这里碰到了一个 node 环境变量的问题, 读取不到 github-webhook-handler 这个模块, 找了很多办法也没有解决, 后来我直接在项目根目录的上级目录安装了这个模块, 问题就解决了. cd /root/blognpm install github-webhook-handlernpm 会从当前目录依次向上寻找含有 node_modules 目录并访问该模块. 普通方式运行 webhook.js利用 Linux 提供的 nohup 命令,让 webhooks.js 运行在后台 1nohup node webhook.js > deploy.log & Forever方式运行webhook.js 我在实际使用的时候发现,我的 Node 服务器时不时会自动停掉,具体原因我暂时还没有弄清楚。不过似乎很多人都遇到了这样的困扰,要解决这个问题,forever 是个不错的选择。借助 forever 这个库,它可以保证 Node 持续运行下去,一旦服务器挂了,它都会重启服务器。 安装 forever:npm install -g forever运行:12cd { 部署服务器的根目录 } nohup forever start webhook.js > deploy.log & Ubuntu 中原本就有一个叫 node 的包。为了避免冲突,在 Ubuntu 上安装或使用 Node 得用 nodejs 这个名字。而 forever 默认是使用 node 作为执行脚本的程序名。所以为了处理 Ubuntu 存在的这种特殊情况,在启动 forever 时得另外添加一个参数:(其它则忽略) forever start webhook.js -c nodejs Github配置webhooks配置好 Webhook 后,Github 会发送一个 ping 来测试这个地址。如果成功了,那么这个 Webhook 前就会加上一个绿色的勾;如果你得到的是一个红色的叉,那就好好检查一下哪儿出问题了吧! git-hook方式可采用一种更为简单的部署方式 这种方式和webhook可二选一 服务器上建立git裸库创建一个裸仓库,裸仓库就是只保存git信息的Repository, 首先切换到git用户确保git用户拥有仓库所有权一定要加 –bare,这样才是一个裸库。 12cd git init --bare blog.git 使用 git-hooks 同步网站根目录在这里我们使用的是 post-receive这个钩子,当git有收发的时候就会调用这个钩子。 在 ~/blog.git 裸库的 hooks文件夹中,新建post-receive文件。 vim ~/blog.git/hooks/post-receive 填入以下内容 12#!/bin/shgit --work-tree=/root/blog/public --git-dir=/root/blog.git checkout -f work-tree=/root/blog/public这个目录是网站的网页文件目录,–git-dir=/root/blog.git目录为裸库地址,裸库监听git提交会将文件提交到网页目录保存后,要赋予这个文件可执行权限chmod +x post-receive 配置博客根目录_config.yml完成自动化部署打开 _config.yml, 找到 deploy12345deploy: type: git repo: 用户名@SERVER名:/home/git/blog.git(裸库地址) //<repository url> branch: master //这里填写分支 [branch] message: 提交的信息 //自定义提交信息 (默认为 Site updated: {{ now('YYYY-MM-DD HH:mm:ss') }}) Nginx服务npm 安装nginx启动nginx 1service nginx start nginx -t 查看nginx配置文件若nginx服务启动,访问报403错误 则将首行 user nginx 改为user root 123456789vim /etc/nginx/nginx.confserver { listen 80; # 监听端口 server_name 47.98.141.252:80 gangtieguo.cn wwww.gangtieguo.cn; # 你的域名 location / { root /root/blog/public; index index.html; }} 重载 nginx,使配置生效 nginx -s reload 参考Hexo 静态博客搭建并实现自动部署到远程 vps将 Hexo 博客发布到自己的服务器上利用 Github 的 Webhook 功能和 Node.js 完成项目的自动部署Webhook 实践 —— 自动部署Hexo 快速搭建静态博客并实现远程 VPS 自动部署阿里云 VPS 搭建自己的的 Hexo 博客","categories":[{"name":"博客","slug":"博客","permalink":"http://gangtieguo.cn/categories/博客/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"http://gangtieguo.cn/tags/Hexo/"}]},{"title":"MyBatis相关注解","slug":"MyBatis注解","date":"2018-05-17T12:09:55.000Z","updated":"2019-06-17T04:40:09.394Z","comments":true,"path":"2018/05/17/MyBatis注解/","link":"","permalink":"http://gangtieguo.cn/2018/05/17/MyBatis注解/","excerpt":"现接触MyBatic记录一些注解","text":"现接触MyBatic记录一些注解自动生成主键 可以使用 @Options 注解的 userGeneratedKeys 和 keyProperty 属性让数据库产生 auto_increment(自增长)列的值,然后将生成的值设置到输入参数对象的属性中。 123@Insert(\"insert into students(name,sex,age) values(#{name},#{sex},#{age}\") @Options(useGeneratedKeys = true, keyProperty =\"userId\") int insertUser(User user); 将自增的Id存入到userId属性中","categories":[{"name":"框架","slug":"框架","permalink":"http://gangtieguo.cn/categories/框架/"}],"tags":[{"name":"Java","slug":"Java","permalink":"http://gangtieguo.cn/tags/Java/"},{"name":"SSH","slug":"SSH","permalink":"http://gangtieguo.cn/tags/SSH/"}]},{"title":"linux命令","slug":"Linux命令积累","date":"2018-05-10T07:37:28.983Z","updated":"2019-04-09T09:02:15.234Z","comments":true,"path":"2018/05/10/Linux命令积累/","link":"","permalink":"http://gangtieguo.cn/2018/05/10/Linux命令积累/","excerpt":"简单linux命令 nohup &后台运行","text":"简单linux命令 nohup &后台运行 文件查找find / -type f -size +10G在Linux下如何让文件让按大小单位为M,G等易读格式,S size大小排序。 ls -lhSdu -h * | sort -n当然您也可以结合管道文件夹内最大的几个文件 du -h * | sort -n|head动态显示机器各端口的链接情况while :; do netstat -apn | grep ":80" | wc -l; sleep 1; done sed更改第一行 sed -i '1s/.*//' sed -i ‘1s/.*/想更改的内容/‘ 1ssh root@slave01 \"sed -i '6c advertised.host.name=slave01 ' $KAFKA_HOME/config/server.properties\" 删除第一行sed -i '1d' sed -i ‘1d’ 文件名插入第一行 sed -i '1i\\' sed -i ‘1i\\内容‘ 文件名 cpucat /proc/cpuinfo | grep processor | wc -llscpu sz rz与服务器交互上传下载文件sudo yum install lrzsz -y 挂载 sshfs [email protected]:/home/ /csdn/win10/ 即:sshfs 用户名@远程主机IP:远程主机路径 本地挂载点sshfs root@master:/usr/hadoop /usr/hive/hadoop 查看端口是否被监听也可验证对应端口程序是否启动 12netstat -nl|grep 10000netstat -antp| grep 1000 tree1yum install -y tree tree 可以查看目录结构 虚拟机共享文件夹在virtualbox中设置共享文件夹的share名称对应mac的目录虚拟机中的目录sudo mount -t vboxsf vagrant /Users/yaosong centos查看版本 cat /etc/redhat-release GLIBC2.14 not foundlibc.so.6: version ‘GLIBC_2.14’ not found报错提示的解决方案 【工作】Centos6.5 升级glibc解决“libc.so.6: version GLIBC_2.14 not found”报错问题 参考:Docker-PostgresSQL 其他ip -4 addr 查看ip chkconfig sshd on rpm -qa|grep ssh。 为了快速实现我们就不自己装 SSH 服务了,hub.docker.com 上的 kinogmt/centos-ssh:6.7 这个镜像就能满足我们的要求","categories":[{"name":"Linux","slug":"Linux","permalink":"http://gangtieguo.cn/categories/Linux/"}],"tags":[{"name":"linux","slug":"linux","permalink":"http://gangtieguo.cn/tags/linux/"},{"name":"开发","slug":"开发","permalink":"http://gangtieguo.cn/tags/开发/"}]},{"title":"spring集成权限校验","slug":"spring集成权限校验","date":"2018-05-08T10:34:55.000Z","updated":"2018-05-17T12:21:22.941Z","comments":true,"path":"2018/05/08/spring集成权限校验/","link":"","permalink":"http://gangtieguo.cn/2018/05/08/spring集成权限校验/","excerpt":"shiro简介shiro是权限控制的一个框架是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。","text":"shiro简介shiro是权限控制的一个框架是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。 权限控制的方式权限有四种实现方式注解(基于代理),url拦截(基于过滤器),shiro标签库(基于标签),编写代码(及其不推荐)不论哪种方式:都需要引入spring用于整合shiro的过滤器 web.xml中:DelegatingFilterProxy=>spring整合shiro配置spring提供的用于整合shiro框架的过滤器123456789101112131415 <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy </fileter>``` filet-name需要和**spring配置文件**中的一个BEAN对象的id保持一致**非常重要** ### 配置 I. 注解方式,注解是利用生成的代理对象来完成权限校验: spring框架会为当前action对象(加注解的action)创建一个代理对象,如果有权限,就执行这个方法,不然就会报**异常**(将spring,Strust配置文件丰富:添加权限的注解,struts添加捕获异常,跳转页面) 1. 需要在spring配置文件中进行配置开启注解**DefaultAdvisorAutoProxyCreator**, 并配置成cjlib方式的注解 ```xml<property name=\"proxyTargetClass\" value=\"true\">\\</property> 注解实现权限当为jdk模式的时候方法注解实现权限过滤抛异常的原因:因为如果是jdk方式的话,实现的接口modelDriven只有一个getModel方法所以不能进行对除该方法外其他方法进行注解 定义切面类AuthorizationAttributeSourceAdvisor 1<bean id=\"authorizationAttributeSourceAdvisor\" class=\"org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor\"></bean> 在需要权限才能访问的方法上添加注解 1234567891011121314151617181920212223242526272829 @RequiresPermissions(\"relo_delete这是权限名称\") ``` II. url拦截(springxml) 基于过滤器或者拦截器实现 ```xml<bean id=\"shiroFilter\" class=\"org.apache.shiro.spring.web.ShiroFilterFactoryBean\"> <property name=\"securityManager\" ref=\"securityManager\"/> <property name=\"loginUrl\" value=\"/login.jsp\"/> <property name=\"unauthorizedUrl\" value=\"/unauthorized.jsp\"/> <property name=\"filterChainDefinitions\"> <value> /css/** = anon /js/** = anon /images/** = anon /validatecode.jsp* = anon /login.jsp* = anon /userAction_login.action = anon /page_base_staff.action = perms[\"staff\"] /** = authc <!--/** = authc--> </value> </property> </bean> <!--开启自动代理,并且将代理代理模式设置为cjlib--> <bean id=\"defaultAdvisorAutoProxyCreator\" class=\"org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator\"> <!--设置成cglib方式--> <property name=\"proxyTargetClass\" value=\"true\"></property> </bean> shiro的使用 在web.xml中引入用于创建shiro框架的过滤器web.xml中:DelegatingFilterProxy=>spring整合shiro注意引入的位置:要在struts核心过滤器的前面,StrutsPrepareAndExcutFilter,不然,所有请求会通过struts过滤器获直接访问得到,shiro的过滤器将不会起到作用 在Spring中整合shiro2.1). shiro框架过滤器:ShiroFilterFactoryBean 需要声明那些过滤器,那些资源需要匹配那些过滤器,采用url拦截方式进行的路径对应的拦截器2.2). 配置安全管理器:DefaultWebSecurityManager 需要注入 自定义的Realm bean对象 1234567891011121314151617181920212223242526272829303132333435363738 <!--配置一个shiro框架的过滤器工厂bean,用于创建shiro框架的过滤器--> <bean id=\"shiroFilter\" class=\"org.apache.shiro.spring.web.ShiroFilterFactoryBean\"> <property name=\"securityManager\" ref=\"securityManager\"/> <property name=\"loginUrl\" value=\"/login.jsp\"/> <property name=\"unauthorizedUrl\" value=\"/unauthorized.jsp\"/> <property name=\"filterChainDefinitions\"> <value> /css/** = anon /js/** = anon /images/** = anon /validatecode.jsp* = anon /login.jsp* = anon /userAction_login.action = anon /page_base_staff.action = perms[\"staff\"] /** = authc <!--/** 表示所有/下所有路径,包括下面的所有路径--> <!--/validatecode.jsp* 表示所有除了validatecode.jsp,还包括jsp后追加其他内容的.如validatecode.jsp?'+Math.random();防止验证码读取缓存 </value> </property> </bean> <!--开启自动代理,并且将代理代理模式设置为cjlib 动态代理分为两类 基于jdk 创建的类必须要实现一个接口,这是面向接口的动态代理 基于cjlib 创建的类不能用final修饰--> <bean id=\"defaultAdvisorAutoProxyCreator\" class=\"org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator\"> <!--设置成cglib方式--> <property name=\"proxyTargetClass\" value=\"true\"></property> </bean> <!--定义aop通知+切入点--> <bean id=\"authorizationAttributeSourceAdvisor\" class=\"org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor\"></bean> <!--注入安全管理器--> <bean id=\"securityManager\" class=\"org.apache.shiro.web.mgt.DefaultWebSecurityManager\"> <property name=\"realm\" ref=\"bosRealm\"></property> <property name=\"cacheManager\" ref=\"ehCacheManager\"></property> </bean> 在登陆认证的方法中加入subject controller中的login方法 1234567891011121314151617181920212223242526272829303132333435363738394041424344public String login(){Subject subject = SecurityUtils.getSubject(); //创建一个用户名密码令牌 AuthenticationToken token = new UsernamePasswordToken(getModel().getUsername(), MD5Utils.md5( getModel().getPassword())); try { //认证 subject.login(token); } catch (Exception e) { this.addActionError(\"用户名或者密码错误\"); return LOGIN; } /*当通过认证,跳入主页*/ User user = (User) subject.getPrincipal(); /*将用户信息存入session*/ ServletActionContext.getRequest().getSession().setAttribute(\"currentUser\", user); /*返回主页*/ return \"\";}``` 4. 自定义Realm(用于权限的具体实施,即认证和授权)一般实现Realm接口的 **AuthorizingRealm** 实例 4.1实现认证 重写doGetAuthenticationInfo方法必须继承*AuthorizingRealm* 在需要交付给spring生成,并需要在安全注册管理器中注入属性Realm```javaprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken mytoken = (UsernamePasswordToken) token; String username = mytoken.getUsername(); DetachedCriteria dc = DetachedCriteria.forClass(User.class); dc.add(Restrictions.eq(\"username\",username)); List<User> list = userDao.findByCriteria(dc); if(list != null && list.size() >0){ User user = list.get(0); String dbPassword = user.getPassword(); AuthenticationInfo info = new SimpleAuthenticationInfo(user,dbPassword,this.getName()); return info; }else{ return null; } } 4.2实现授权 重写doGetAuthorizationInfo方法 12345678910111213141516171819202122232425262728293031323334353637383940protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {/*获的简单授权对象,用于授权的*/ SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); /*授权staff权限*/ //info.addStringPermission(\"staff\"); //步骤获得授权对象,获得当前用户,获得当前用户的权限(若为admin即授予所有权限),当前用户授权 //获得对象 User user = (User)principals.getPrimaryPrincipal(); List<Function> fList = null; //获得权限 if(user.getUsername().equals(\"admin\")){ fList = functionDao.findAll(); }else{ fList = functionDao.findFunctionByUserId(user.getId()); } //授予权限 for(Function f : fList){ info.addStringPermission(f.getCode());}``` ## 关于Shiro中使用 **encache** 1.引入包 `在spring配置文件中配置以下` 2.配置文件ehcache.xml ```xml<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"../config/ehcache.xsd\"> <defaultCache maxElementsInMemory=\"10000\" eternal=\"false\" timeToIdleSeconds=\"120\" timeToLiveSeconds=\"120\" overflowToDisk=\"true\" maxElementsOnDisk=\"10000000\" diskPersistent=\"false\" diskExpiryThreadIntervalSeconds=\"120\" memoryStoreEvictionPolicy=\"LRU\" /></ehcache> <!--eternal是否永久有效--> 3.引入缓存管理器EhCacheManager(shiro包中的),并设置配置文件;4.将缓存管理器注入安全管理器DefaultWebSecurityManager12345678910<!--注册安全管理器--><bean id=\"securityManager\" class=\"org.apache.shiro.web.mgt.DefaultWebSecurityManager\"> <property name=\"realm\" ref=\"bosRealm\"></property> <property name=\"cacheManager\" ref=\"ehCacheManager\"></property> </bean> <bean id=\"bosRealm\" class=\"org.yao.bos.web.action.realm.BOSRealm\"></bean> <!--注入缓存管理器--> <bean id=\"ehCacheManager\" class=\"org.apache.shiro.cache.ehcache.EhCacheManager\"> <property name=\"cacheManagerConfigFile\" value=\"classpath:ehcache.xml\"></property> </bean>","categories":[{"name":"Spring","slug":"Spring","permalink":"http://gangtieguo.cn/categories/Spring/"}],"tags":[{"name":"开发","slug":"开发","permalink":"http://gangtieguo.cn/tags/开发/"},{"name":"Java","slug":"Java","permalink":"http://gangtieguo.cn/tags/Java/"},{"name":"技术","slug":"技术","permalink":"http://gangtieguo.cn/tags/技术/"}]}]}