無限風域 https://blog.windawings.com/ zh-CN 風立ちぬ、いざ生きめやも Sat, 02 Jun 2018 02:41:00 +0800 Sat, 02 Jun 2018 02:41:00 +0800 Kubernetes 1.10.2 部署Cloudera 5.14.2 https://www.windawings.com/research/kubernetes-cloudera.html https://www.windawings.com/research/kubernetes-cloudera.html Sat, 02 Jun 2018 02:41:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

前言(Preface)

  最初尝试Kubernetes直接部署云计算相关组件, 但是要在Docker中完成自动配置很麻烦, 再者follower无法做到动态扩容, 比如Hadoop中新增的Datanode容器, NameNode容器是无法感知的。节点的存储与网络通信也存在一定问题。

BlueData

  Bluedata在《Big Data and Container Orchestration with Kubernetes (K8s)》中提到了自己产品BlueData EPIC未来在Kubernetes上的发展。

Kubernetes and BlueData

  Bluedata设置了StateFulSet的EPIC Controller, 提供了REST和Web UI支持, 且在其Pod上安装Kubectl从而控制Kubernetes以增加新容器, 完成Cloudera或其他镜像的部署, 具体可参见以下视频中的演示。

  如视频中展示, 计算节点以StatefulSet和对应的Service拓展, PersistVolume由cePersistentDisk中配置gke完成存储部署。鉴于BlueData仍然处于开发测试中, 自研违背搭建云环境的初衷过于花费时间, 本作者更加青睐于下面的第二种方式来搭建有状态的云计算服务。

loicmathieu/docker-cdh

  这是github上的一个开源docker项目loicmathieu/docker-cdh, 通过所需install所需cloudera组件, 并且预先准备好相关配置, 即可直接搭建出容器化的云计算环境, 作者也通过实践在Kubernetes上成功依据此项目搭建出了HDFS + MR + Pig + Hive + Spark + Flume的实验环境, 并且datanode部分可自动拓展, 部署yaml如下:

  具体能做什么可参考loicmathieu/docker-cdh中的说明, yaml中的镜像和nfs的ip等需要做替换, 这里不再赘述。虽然部署结果还算满意, 但是和apache spot的实验环境有一定区别, 未来组件的拓展性也很差, 需要把Dockerfile全部重写; 而且因为新加组件需要额外配置, 所以Kubernetes的ConfigMap需求也会变得更大, 且可能因新组件的配置难以同步, 要么在DN节点配置kubectl可动态更改ConfigMap, 要么使用Puppet, Chef或Ansible之类的工具向Master节点注册自己, 这样做也很麻烦。最后选择重写Dockerfile, 并尝试在Kubernetes中配置Cloudera Master和Clouder Agent来搭建出更自定义的云计算环境, 并且享受Cloudera带来的集成上的遍历, ConfigMap也只需要管理Cloudera即可。

部署(Deployment)

  其实与其说是部署, 不如说是错误日志记录合适一些, 因为没有拓展出REST或者别的API, 只是单纯地使用Dockerfile完成镜像配置和Kubernetes的部署相结合, 实际遇到的问题主要来源于Docker和Cloudera的适配。具体内容其实看yaml配置和Dockerfile内容就明白了, 先放上配置再说明部署过程中遇到的麻烦。

日志(Log)

  部署在5月初就已经做好了, 但当时没有对错误一一做笔记, 所以如今想起什么就补充。

  • Cloudera自带了Supervisor, 如果pip install supervisor Cloudera Agent会报错说无法运行supervisord
  • 不知道为什么Kubernetes中对CPU和内存做了limit后, 即使设置此前CPU或内存的最大值, 容器会无法正常启动, 只好暂时不做设置
  • Kubernetes启动容器后会刷新一遍容器的Environment, 而此时开机脚本是读取不到这个新环境内容的(我做出了很多努力, 都无法获取到新环境配置信息……), 最后采取设置Kubernetes的hostname, 故意使其和对应Service的name相同, 从而达成访问Service与Master相连通的方式(因为实在不想使用Pod的部署模式, 力求以Service通信, 以StatefulSet或Deployment完成节点部署), 这里之所以需要读取新环境配置是为了让Cloudera Master服务可见, 因为Cloudera Master以Deployment部署, 其Pod会分配一个Deployment的name-[随机字符]的取名形式, 需要获取到后设置hostshostname为master-svc, 完成容器重启后使其服务可被Cloudera Agent发现(其实完全可以用正则匹配到这个随机码然后替换完成重启, 但因为尝试以开机启动脚本读取新环境配置浪费太多时间, 而且考虑可能有更改hostname的需要, 所以选择了配置Deployment.spec.template.spec.hostnameService.metadata.name相同的取巧方式完成部署)
  • PersistVolume需要配置哪些运行目录暂时未知, 这个部分难以让Cloudera进行管理, 算是目前自定义云环境中比较尴尬的部分。也就是说需要自己知道各个组件需要做持久化的路径, 将其以PersistVolume存储到容器生命周期之外。目前仅对Cloudera组件的parcel压缩包进行了本地NFS挂载和存储, HDFS算是可以自己配置nn, dn这些内容的位置, zk的myid和日志等, 这个部分暂时没有好的动态做持久化分配的点子, 也不可能直接挂载根目录或者挂载一个过大的位置
  • 目前仍然卡在Cloudera分法检验完agent的parcel之后, 配置角色时hue的数据库连接失败上(其他都能成功, 似乎是其python的某个xml模块有报错), 这个需要更改master的Dockerfile引入缺失配置, 暂时未解决
]]>
0 https://www.windawings.com/research/kubernetes-cloudera.html#comments https://www.windawings.com/feed/research/kubernetes-cloudera.html
Typecho Google Authetication Plugin https://www.windawings.com/default/typecho-google-authetication-plugin.html https://www.windawings.com/default/typecho-google-authetication-plugin.html Sat, 19 May 2018 19:13:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

引言(Preface)

  Typecho is a PHP Blogging Platform. Simple and Powerful. Typecho 是由两个单词type和echo组成的,在发音的时候也发这两个音 /taɪpˌ'ekoʊ/. —— 摘自Typecho Github和Typecho Docs

typecho
typecho-adventages

  Google Authetication与两步验证功能配合, 可在登录使用Google Authentication的帐户时为平添一重安全保障。启用两步验证之后, 当登录帐户时, 需要提供密码和此应用生成的验证码。配置完成后, 无需网络连接或蜂窝连接即可获得验证码。—— 摘自Google Authenticator from App Store of Apple.

  Google Authetication APP功能包括:—— 摘自Google Authenticator from App Store of Apple.

  • 通过 QR 码自动设置
  • 支持多帐户登录
  • 支持基于时间和基于计数器生成验证码
Google-Authetication

介绍(Introduction)

  Typecho原生登陆没有设置验证码, 也没有其他登陆接口防范措施容易在密码猜解, 密码爆破, 或社会工程学的围攻下被攻破。安全防范一方面可以设置具有隐藏性的后台访问路径, 一方面使得登陆口令满足足够抵御密码猜解和爆破的复杂性和安全性, 再者, 就是接触typecho优秀的生态圈, 选择合适的插件(Plugin)设防。
  本博客即采用Typecho作为博客的构建基础, 一开始考虑采用验证码, 但现有搜集到的Free or Open的plugin难以满足需求(比方介入极客验证码条, 但是验证时可能因为插件设置或代码业务逻辑问题不能正常工作, 或者只配置了过于简陋的验证码进行图灵测试, 这对安全防范依然不够理想), 最后是采用GAuthentication插件用于后台登陆, Github: https://github.com/weicno/typecho-Authenticator.
  不过GAuthentication插件在设置无法看到二维码, 另一方面要看到二维码还必须借助Google API才能查看, 即https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/Company or Email?secret=SecretCode, 这又带来另一方面的安全性问题(虽然比较牵强, 但是还是希望图片自己显示出来, 也有不希望爬梯子才能看的意图)。原作者使用的是https://github.com/PHPGangsta/GoogleAuthenticator作为验证基础的php插件, 是2016年的最新版本(PHP class to generate and verify Google Authenticator 2-factor authentication http://phpgangsta.de/4376), 代码简短功能倒是够用, 但是没有满足二维码本地生成的需求, 于是在Github上找到了另一个项目Google2FA, 此篇博文发表前两个月依然保持更新, 使用composer require pragmarx/google2facomposer require bacon/bacon-qr-code即可安装, 虽然库大了很多, 不过功能上显得更强大。

Google2FA

  于是, 基于GAuthentication插件Google2FA结合与替换, 以及给Typecho加上Image的Element作为表单, 本文的插件应运而生(其实没改多少, 只是因为作者未接触过php所以改得比较难过)。

下载(Download)

  

技巧(Tips)

  • var文件夹覆盖typecho根目录即可, 新增了Typecho_Widget_Helper_Form_Element_Image
  • GAuthenticator文件夹放入typecho的/usr/plugin中即可被识别
  • 关于Google2FA中对于时间window的时间间隔描述没有看太明白, 只能从其举例中得知0指30s, 1指60s, 2指120s
]]>
2 https://www.windawings.com/default/typecho-google-authetication-plugin.html#comments https://www.windawings.com/feed/default/typecho-google-authetication-plugin.html
CentOS7部署Kubernetes 1.10.2 https://www.windawings.com/research/centos7-kubernetes.html https://www.windawings.com/research/centos7-kubernetes.html Mon, 07 May 2018 20:42:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

scope

准备(Prepare)

  准备docker和Kubernetes的国内镜像源, 把源文件加入到/etc/yum.repo.d/即可。
  源采用中科大的docker源和阿里的Kubernetes源, 若能访问"外网"请自行更换baseurl.
  如果可访问"外网"即可不用关心, 如果使用代理联网可参考配置(Config)章节进行proxy设置。

Docker Repository

[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-stable-debuginfo]
name=Docker CE Stable - Debuginfo $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/debug-$basearch/stable
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-stable-source]
name=Docker CE Stable - Sources
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/source/stable
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-edge]
name=Docker CE Edge - $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/$basearch/edge
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-edge-debuginfo]
name=Docker CE Edge - Debuginfo $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/debug-$basearch/edge
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-edge-source]
name=Docker CE Edge - Sources
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/source/edge
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-test]
name=Docker CE Test - $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/$basearch/test
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-test-debuginfo]
name=Docker CE Test - Debuginfo $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/debug-$basearch/test
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-test-source]
name=Docker CE Test - Sources
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/source/test
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-nightly]
name=Docker CE Nightly - $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/$basearch/nightly
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-nightly-debuginfo]
name=Docker CE Nightly - Debuginfo $basearch
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/debug-$basearch/nightly
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

[docker-ce-nightly-source]
name=Docker CE Nightly - Sources
baseurl=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/7/source/nightly
enabled=0
gpgcheck=1
gpgkey=http://mirrors.ustc.edu.cn/docker-ce/linux/centos/gpg

Kubernetes Repository

  若能访问外网可替换baseurl为:https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64, 阿里的源更新最差会比官方同步慢一天(如果本作者没有理解错本站点所有镜像仓库每天同步一次的意思的话)。

[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0

CentOS Repository

  CentOS的源可按需搜索获取, 或者就使用默认也没问题。

安装(Install)

  主要安装必备组件和docker预pull镜像。

Yum Install

# kubernetes 1.10版本要求docker-ce版本为17.03, 然而其依赖docker-ce-selinux按照对应版本一安装就会自动安装最新版本的docker-ce, 所以无视该警告
# 可以通过yum list docker-ce --showduplicates | sort -r查看其他版本

# 若安装过旧版本docker, 可执行如下语句进行卸载
sudo yum remove -y docker docker-common container-selinux docker-selinux docker-engine

# 更新 安装
sudo yum clean all && yum makecache
sudo yum update -y
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum install -y kubectl kubeadm kubelet iptables-services ntp docker-ce

Docker Install

  Docker镜像提前安装准备, 因为网络原因作者把相关镜像pull进了Docker Hub做中转, 方便在kubernetes pull相关镜像失败时, 通过更改yaml或者以kubectl edit的方式更改镜像源以完成安装。
  如果不打算采用作者准备的镜像, 或镜像版本过期太多, 可自行写Dockerfile放到国内镜像仓库构建再拉回来(比如Docker Hub, DaoCloud或者阿里容器仓库), 或者可以配置docker代理来访问(此处可参考配置(Config)一节内容)。

#!/bin/bash

# 必备镜像
docker pull windawings/etcd-amd64:3.2.18
docker pull windawings/k8s-dns-dnsmasq-nanny-amd64:1.14.10
docker pull windawings/k8s-dns-kube-dns-amd64:1.14.10
docker pull windawings/k8s-dns-sidecar-amd64:1.14.10
docker pull windawings/kube-apiserver-amd64:v1.10.2
docker pull windawings/kube-controller-manager-amd64:v1.10.2
docker pull windawings/kube-proxy-amd64:v1.10.2
docker pull windawings/kube-scheduler-amd64:v1.10.2
docker pull windawings/pause-amd64:3.1

# kubernetes dashboard (非SSL版本, 因为port forward没有使用成功过, 采用traefik代理访问界面节点, 而SSL的反向代理比较麻烦需要配置secret等证书信息, 故暂时使用非SSL的部署版本)
docker pull windawings/kubernetes-dashboard-amd64:v1.8.3

# kubernetes 监控相关
docker pull windawings/heapster-amd64:v1.4.2
docker pull windawings/heapster-grafana-amd64:v4.4.3
docker pull windawings/heapster-influxdb-amd64:v1.3.3

# Weave Scope
docker pull weaveworks/scope:1.9.0

# traefik (作代理, 通过nodePort类型的Service与外部建立连接, 用域名访问各节点)
docker pull traefik:latest

# 如果使用calico作为kubernetets cni的话
docker pull quay.io/coreos/etcd:v3.1.10
docker pull quay.io/calico/node:v3.1.1
docker pull quay.io/calico/cni:v3.1.1
docker pull quay.io/calico/kube-controllers:v3.1.1

配置(Config)

  配置CentOS的网络, 以及CentOS下NFS的配置(如果需要kubernetets中nfs服务器的配置项的话, host path的storage class暂时没有尝试成功, 该步骤主要用于配置StatefulSet中Persistent Volumn Claim部分, 使得PV可以自动生成)。

Centos Config

  配置网络, 关闭selinux和firewall, 启用iptables(如果不采用kubernetets的proxy作为网络配置, 使用其他方式实现kubernetets中的网络, 可以考虑不执行以下步骤)。

# 禁用selinux
setenforce 0
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/sysconfig/selinux 
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config 
sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/sysconfig/selinux 
sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/selinux/config  

# 禁用firwall 启用iptables
sudo systemctl stop firewalld.service
sudo systemctl disable firewalld.service
sudo systemctl start iptables.service
sudo systemctl enable iptables.service

# 启用网桥中iptables的规则过滤, 因为一者docker默认网桥docker0可能需要, 二者kubernetets自己也是采用iptables做的网络隔离
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.conf.all.forwarding=1
EOF
sysctl --system

sudo systemctl start ntpd 
sudo systemctl enable ntpd

CentOS Proxy

  配置全局代理, 话是说全局代理, 其实对yum和docker都无效, 甚至wget等……

export HTTPS_PROXY=http://192.168.99.1:1080
export HTTP_PROXY=http://192.168.99.1:1080
export NO_PROXY=localhost,192.168.0.0/16

  取消代理

unset HTTPS_PROXY HTTP_PROXY NO_PROXY

Swap Config

  kubelet禁止在启用swap的环境下运行, 不过可以通过配置kubelet来使其无视, 如果不想无视, 可以通过以下访问禁用swap.

# 卸载swap
swapoff -a && sysctl -w vm.swappiness=0

# 删除swap内容
rm -rf $(swapon -s | awk 'NR>1{print $1}')

# 注释掉swap的挂载
sed -i 's/.*swap.*/#&/' /etc/fstab

NFS Config

  配置CentOS7 NFS. 如果不需要用到kubernetets的nfs挂载可以不用配置。

# 安装
yum install -y nfs-utils nfs-utils-lib rpcbind

# 启用
systemctl enable rpcbind
systemctl enable nfs
systemctl restart rpcbind
systemctl restart nfs

# 创建挂载路径
mkdir -p /pv/cloudera/repo && mkdir /pv/cloudera/master && mkdir /pv/cloudera/agent

# 配置挂载路径到NFS配置中
echo -e "/pv/ *(rw,sync,no_root_squash,no_subtree_check)" >> /etc/exports

# 以上/etc/exports中的配置参数, 这里照搬别人博客的内容, 虽然讲道理没写全
# /pv/ 192.168.0.0/16(rw,sync,no_root_squash,no_subtree_check)
# ro: 目录只读
# rw: 目录读写
# sync: 将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性
# async: 将数据先保存在内存缓冲区中,必要时才写入磁盘
# all_squash: 将远程访问的所有普通用户及所属组都映射为匿名用户或用户组(nfsnobody)
# no_all_squash: 与all_squash取反(默认设置)
# root_squash: 将root用户及所属组都映射为匿名用户或用户组(默认设置)
# no_root_squash: 与rootsquash取反
# anonuid=xxx: 将远程访问的所有用户都映射为匿名用户,并指定该用户为本地用户(UID=xxx)
# anongid=xxx: 将远程访问的所有用户组都映射为匿名用户组账户

# 更改共享目录权限
chown -R nfsnobody.nfsnobody /pv/

# 重载配置
exportfs -rv

# 以下为相关测试项
# 挂载
mount -t nfs 10.0.2.15:/pv/ /root/share

# 卸载
umount /root/share

# 查看挂载情况
showmount -e localhost

# 查看RPC注册情况
rpcinfo -p localhost

Kernel Config

  如果需要的话, 可以升级下CentOS的kernel. 注意, 如果是vagrant虚拟机启动的CentOS box, 升级后vagrant默认的共享挂载会挂(本来出于谨慎想说可能会挂, 但是作者遇到情况就是直接不可用了, 所以如实描述)。

# 准备源
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

# 安装
yum --enablerepo=elrepo-kernel install -y kernel-ml

# 编辑, 设置其中的GRUB_DEFAULT=0
vi /etc/default/grub

grub2-mkconfig -o /boot/grub2/grub.cfg

# 这里请使用yum list或rpm -qa查询自己旧版本的kernel, 然后删除
yum autoremove kernel-3.10.0-693.11.1.el7.x86_64

# 重启后即可观察到kernel版本的更新

Yum Proxy Config

  如果不采用国内源而试图使用代理, 以下为配置yum的代理。

# 设置proxy=http://<proxy ip>:<proxy port>
vi /etc/yum.conf

Docker Proxy Config

  配置docker代理, NO_PROXY部分把CentOS所在局域网网段加入比较好。当然, 如果能访问k8s.gcr.io谷歌的镜像站点, 则不用配置代理。

sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf <<-'EOF'
[Service]
Environment="HTTP_PROXY=http://<proxy ip>:<proxy port>" "HTTPS_PROXY=http://<proxy ip>:<proxy port>" "NO_PROXY=localhost,127.0.0.1,<local ip>/<local mask>, registry.docker-cn.com"
EOF

# 重载配置
sudo systemctl daemon-reload

# 重启docker使得新配置载入
sudo systemctl restart docker

# 配置docker service开机启动
sudo systemctl enable docker

Docker Source Config

  配置docker的镜像源, 可参考daocloud.io, aliyuncs以及其他公有或私有镜像源的配置, 把地址加入registry-mirrors中, 此处使用docker hub作为镜像源。如果默认访问的镜像站点速度不差可无视此项配置。

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF

# 重载配置
sudo systemctl daemon-reload

# 重启docker使得新配置载入
sudo systemctl restart docker

# 配置docker service开机启动
sudo systemctl enable docker

Kubernetes Config

# If the Docker cgroup driver and the kubelet config don’t match, change the kubelet config to match the Docker cgroup driver. The flag you need to change is --cgroup-driver
docker info | grep -i cgroup

# 如果docker info中cgroup=cgroupfs的话, 即可直接使用以下语句进行替换, 不然可按需进行更改
sed -i "s/cgroup-driver=systemd/cgroup-driver=cgroupfs/g" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

# 配置swap下kubelet的启动, 以及设置pause的镜像
Environment="KUBELET_EXTRA_ARGS=--v=2 --fail-swap-on=false --pod-infra-container-image=windawings/pause-amd64:3.1"

# 若需要修改etcd, apiserver, controller-manager, scheduler镜像位置, 可以稍后启动kubelet, 当kubeadm卡住时替换/etc/kubernetes/manifests中的yaml, 再启动kubelet即可
sudo systemctl restart kubelet

# kubelet开机启动
sudo systemctl enable kubelet.service

运行(Run)

  完成kubeadm init和相关镜像源的修改, 以及其他组件的安装说明。

Kubernetes Net

Tips: 很多时候会存在dns无法连接apiserver的情况, 我只能选择iptables --flush

  • Calico

    kubeadm init --pod-network-cidr=192.168.0.0/16
    kubectl apply -f "https://docs.projectcalico.org/v3.0/getting-started/kubernetes/installation/hosted/kubeadm/1.7/calico.yaml"
  • Canal

    kubeadm init --pod-network-cidr=10.244.0.0/16
    kubectl apply -f "https://raw.githubusercontent.com/projectcalico/canal/master/k8s-install/1.7/rbac.yaml"
    kubectl apply -f "https://raw.githubusercontent.com/projectcalico/canal/master/k8s-install/1.7/canal.yaml"
  • Flannel

    kubeadm init --pod-network-cidr=10.244.0.0/16
    sysctl net.bridge.bridge-nf-call-iptables=1
    kubectl apply -f "https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml"
    #不知道为什么, 不按照官方用0.9.1版本而用最新的DNS就会报错, 以下为报错示范
    kubectl apply -f "https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml"
  • Kube-router

    sysctl net.bridge.bridge-nf-call-iptables=1
    kubeadm init --pod-network-cidr
  • Romana

    sysctl net.bridge.bridge-nf-call-iptables=1
    kubectl apply -f "https://raw.githubusercontent.com/romana/romana/master/containerize/specs/romana-kubeadm.yml"
  • Weave Net
    sysctl net.bridge.bridge-nf-call-iptables=1
    kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

Kubeadm Init

  需要参照上一节得到需要的pod network cidr. 如果部署出错, 可通过以下指令回滚到初始或者更改配置。

# 回滚到初始
kubeadm reset

# 提交新配置
kubeadm config upload from-file --config <file_name>

# 更新配置(作者更新配置时会遭遇scheduler无限等待重启, 暂时不知如何解决)
kubeadm upgrade apply <k8s_version>

  正常流程如下:

# 作者使用calico维护kubernetets net, 需要注意的是kubeadm init会刷新/etc/kubernetes/中的配置, 所以执行后再去覆盖更换镜像的manifests, 再开启kubelet
# 如果不指定kubernetes-version, 它会去访问google查最新版本, 因为访问不到外网所以最好指定
# token-ttl用于保持连接, 其实没啥连接好保存的, 反正都访问不到
# apiserver-advertise-address可以不要, 设置自己CentOS系统的内网IP即可, 或者CentOS虚拟机的宿主网关IP + 1(指CentOS里面再开虚拟机的操作)
# ignore-preflight-errors忽略开启swap的错误, 包括manifests已存在的警告都忽略
kubeadm init --pod-network-cidr=192.168.0.0/16 --token-ttl 0 --kubernetes-version=1.10.2 --apiserver-advertise-address=192.168.99.100 --ignore-preflight-errors=all

# kubectl config
mkdir -p $HOME/.kube
sudo cp -f /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

sudo tee /etc/profile.d/k8s.sh <<-'EOF'
export KUBECONFIG=/etc/kubernetes/admin.conf
EOF

source /etc/profile.d/k8s.sh

Kubernetes Dashboard

Tips: Dashboard原配的Role和RoleBinding经过traefit之后会没有权限访问任何资源(不经过traefit没有试过, 可能也会被rbac的权限机制干扰), 建议删除Role和RoleBinding部分改为如下直接拿cluster-admin权限岂不美哉。

# ------------------- Dashboard Role & Role Binding ------------------- #
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system
  • port 9090 non-SSL

    kubectl create -f "https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/alternative/kubernetes-dashboard.yaml"
  • port 443 SSL
    kubectl create -f "https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml"

Influxdb

Tips: 如果单节点的话可能无法部署Influxdb和Scope, 会提示node已tainted, pod无法调度部署, 在deployment上加入调度方案即可。或者更新master node的tainted值NoSchedule, 改为PreferNoSchedule或者把tainted删了。

  spec:
    tolerations:
    - key: node-role.kubernetes.io/master # 其实我也不知道这里key代表了什么
    effect: NoSchedule
kubectl apply -f "https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/grafana.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/heapster.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/influxdb.yaml"
kubectl apply -f "https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/rbac/heapster-rbac.yaml"

Weave Scope

  动态界面展示Pod, Service, Controller等Kubernetes实体, 颗粒度从Host到Process, 展示同层实体的网络关联, 实时监控CPU和Memory, 可直观手动操作实体, 提供web界面类似docker exec -it /bin/bash的访问模式, 很nice.

kubectl apply -f "https://cloud.weave.works/k8s/scope.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')"

Traefik

  作者port forward尝试无果后的继承者, 配置Ingress使用域名访问Service.

kubectl apply -f "https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-rbac.yaml"
kubectl apply -f "https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-deployment.yaml"
kubectl apply -f "https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/ui.yaml"

Traefik Config Template

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-k8s-traefik
  namespace: kube-system
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
  - host: dashboard.k8s.ing
    http:
      paths:
      - path: /  
        backend:
          serviceName: kubernetes-dashboard
          servicePort: 80

其他(Others)

]]>
0 https://www.windawings.com/research/centos7-kubernetes.html#comments https://www.windawings.com/feed/research/centos7-kubernetes.html
How To Ask Questions The Smart Way https://www.windawings.com/default/how-to-ask-questions-the-smart-way.html https://www.windawings.com/default/how-to-ask-questions-the-smart-way.html Wed, 14 Mar 2018 00:07:00 +0800 windawings   来源于Github: https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way


  提问的智慧

  How To Ask Questions The Smart Way

  Copyright © 2001,2006,2014 Eric S. Raymond, Rick Moen

  本指南英文版版权为 Eric S. Raymond, Rick Moen 所有。

  原文网址:http://www.catb.org/~esr/faqs/smart-questions.html

  Copyleft 2001 by D.H.Grand(nOBODY/Ginux), 2010 by Gasolin, 2015 by Ryan Wu

  本中文指南是基于原文 3.10 版以及 2010 年由 Gasolin 所翻译版本的最新翻译;

  协助指出翻译问题,发 Issue,或直接发 Pull Request 给我。

  本文另有繁體中文版

  原文版本历史

目录(Index)

声明


  许多项目在他们的使用协助/说明网页中链接了本指南,这么做很好,我们也鼓励大家都这么做。但如果你是负责管理这个项目网页的人,请在超链接附近的显著位置上注明:

本指南不提供此项目的实际支持服务!

  我们已经深刻领教到少了上述声明所带来的痛苦。因为少了这点声明,我们不停地被一些白痴纠缠。这些白痴认为既然我们发布了这本指南,那么我们就有责任解决世上所有的技术问题。

  如果你是因为需要某些协助而正在阅读这本指南,并且最后离开是因为发现从本指南作者们身上得不到直接的协助,那么你就是我们所说的那些白痴之一。别问我们问题,我们只会忽略你。我们在这本指南中是教你如何从那些真正懂得你所遇到软件或硬件问题的人取得协助,而 99% 的情况下那不会是我们。除非你确定本指南的作者之一刚好是你所遇到的问题领域的专家,否则请不要打扰我们,这样大家都会开心一点。

简介


  在黑客的世界里,当你拋出一个技术问题时,最终是否能得到有用的回答,往往取决于你所提问和追问的方式。本指南将教你如何正确的提问以获得你满意的答案。

  不只是黑客,现在开放源代码(Open Source)软件已经相当盛行,你常常也可以由其他有经验的使用者身上得到好答案,这是件好事;使用者比起黑客来,往往对那些新手常遇到的问题更宽容一些。然而,将有经验的使用者视为黑客,并采用本指南所提的方法与他们沟通,同样也是能从他们身上得到满意回答的最有效方式。

  首先你应该明白,黑客们喜爱有挑战性的问题,或者能激发他们思维的好问题。如果我们并非如此,那我们也不会成为你想询问的对象。如果你给了我们一个值得反复咀嚼玩味的好问题,我们自会对你感激不尽。好问题是激励,是厚礼。好问题可以提高我们的理解力,而且通常会暴露我们以前从没意识到或者思考过的问题。对黑客而言,"好问题!"是诚挚的大力称赞。

  尽管如此,黑客们有着蔑视或傲慢面对简单问题的坏名声,这有时让我们看起来对新手、无知者似乎较有敌意,但其实不是那样的。

  我们不讳言我们对那些不愿思考、或者在发问前不做他们该做的事的人的蔑视。那些人是时间杀手 -– 他们只想索取,从不付出,消耗我们可用在更有趣的问题或更值得回答的人身上的时间。我们称这样的人为 失败者(撸瑟) (由于历史原因,我们有时把它拼作 lusers)。

  我们意识到许多人只是想使用我们写的软件,他们对学习技术细节没有兴趣。对大多数人而言,电脑只是种工具,是种达到目的的手段而已。他们有自己的生活并且有更要紧的事要做。我们了解这点,也从不指望每个人都对这些让我们着迷的技术问题感兴趣。尽管如此,我们回答问题的风格是指向那些真正对此有兴趣并愿意主动参与解决问题的人,这一点不会变,也不该变。如果连这都变了,我们就是在降低做自己最擅长的事情上的效率。

  我们(在很大程度上)是自愿的,从繁忙的生活中抽出时间来解答疑惑,而且时常被提问淹没。所以我们无情的滤掉一些话题,特别是拋弃那些看起来像失败者的家伙,以便更高效的利用时间来回答赢家(winner)的问题。

  如果你厌恶我们的态度,高高在上,或过于傲慢,不妨也设身处地想想。我们并没有要求你向我们屈服 -- 事实上,我们大多数人非常乐意与你平等地交流,只要你付出小小努力来满足基本要求,我们就会欢迎你加入我们的文化。但让我们帮助那些不愿意帮助自己的人是没有效率的。无知没有关系,但装白痴就是不行。

  所以,你不必在技术上很在行才能吸引我们的注意,但你必须表现出能引导你变得在行的特质 -- 机敏、有想法、善于观察、乐于主动参与解决问题。如果你做不到这些使你与众不同的事情,我们建议你花点钱找家商业公司签个技术支持服务合同,而不是要求黑客个人无偿地帮助你。

  如果你决定向我们求助,当然你也不希望被视为失败者,更不愿成为失败者中的一员。能立刻得到快速并有效答案的最好方法,就是像赢家那样提问 -- 聪明、自信、有解决问题的思路,只是偶尔在特定的问题上需要获得一点帮助。

  (欢迎对本指南提出改进意见。你可以 email 你的建议至 esr@thyrsus.comrespond-auto@linuxmafia.com。然而请注意,本文并非网络礼节的通用指南,而我们通常会拒绝无助于在技术论坛得到有用答案的建议。)

在提问之前


  在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前,请先做到以下事情:

  • 尝试在你准备提问的论坛的旧文章中搜索答案
  • 尝试上网搜索以找到答案
  • 尝试阅读手册以找到答案
  • 尝试阅读常见问题文件(FAQ)以找到答案
  • 尝试自己检查或试验以找到答案
  • 向你身边的强者朋友打听以找到答案
  • 如果你是程序开发者,请尝试阅读源代码以找到答案

  当你提出问题的时候,请先表明你已经做了上述的努力;这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所学到的东西会更好,因为我们更乐于回答那些表现出能从答案中学习的人的问题。

  运用某些策略,比如先用Google搜索你所遇到的各种错误信息(既搜索 Google 论坛,也搜索网页),这样很可能直接就找到了能解决问题的文件或邮件列表线索。即使没有结果,在邮件列表或新闻组寻求帮助时加上一句 我在 Google 中搜过下列句子但没有找到什么有用的东西 也是件好事,即使它只是表明了搜索引擎不能提供哪些帮助。这么做(加上搜索过的字串)也让遇到相似问题的其他人能被搜索引擎引导到你的提问来。

  别着急,不要指望几秒钟的Google搜索就能解决一个复杂的问题。在向专家求助之前,再阅读一下常见问题文件(FAQ)、放轻松、坐舒服一些,再花点时间思考一下这个问题。相信我们,他们能从你的提问看出你做了多少阅读与思考,如果你是有备而来,将更有可能得到解答。不要将所有问题一股脑拋出,只因你的第一次搜索没有找到答案(或者找到太多答案)。

  准备好你的问题,再将问题仔细的思考过一遍,因为草率的发问只能得到草率的回答,或者根本得不到任何答案。越是能表现出在寻求帮助前你为解决问题所付出的努力,你越有可能得到实质性的帮助。

  小心别问错了问题。如果你的问题基于错误的假设,某个普通黑客(J. Random Hacker)多半会一边在心里想着蠢问题…, 一边用无意义的字面解释来答复你,希望着你会从问题的回答(而非你想得到的答案)中汲取教训。

  绝不要自以为够格得到答案,你没有;你并没有。毕竟你没有为这种服务支付任何报酬。你将会是自己去挣到一个答案,靠提出有内涵的、有趣的、有思维激励作用的问题 --一个有潜力能贡献社区经验的问题,而不仅仅是被动的从他人处索取知识。

  另一方面,表明你愿意在找答案的过程中做点什么是一个非常好的开端。谁能给点提示?我的这个例子里缺了什么?以及我应该检查什么地方请把我需要的确切的过程贴出来更容易得到答复。因为你表现出只要有人能指个正确方向,你就有完成它的能力和决心。

当你提问时


慎选提问的论坛

  小心选择你要提问的场合。如果你做了下述的事情,你很可能被忽略掉或者被看作失败者:

  • 在与主题不合的论坛上贴出你的问题
  • 在探讨进阶技术问题的论坛张贴非常初级的问题;反之亦然
  • 在太多的不同新闻群组上重复转贴同样的问题(cross-post)
  • 向既非熟人也没有义务解决你问题的人发送私人电邮

  黑客会剔除掉那些搞错场合的问题,以保护他们沟通的渠道不被无关的东西淹没。你不会想让这种事发生在自己身上的。

  因此,第一步是找到对的论坛。再说一次,Google 和其它搜索引擎还是你的朋友,用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题(FAQ)、邮件列表及相关说明文件的链接。如果你的努力(包括阅读 FAQ)都没有结果,网站上也许还有报告 Bug(Bug-reporting)的流程或链接,如果是这样,链过去看看。

  向陌生的人或论坛发送邮件最可能是风险最大的事情。举例来说,别假设一个提供丰富内容的网页的作者会想充当你的免费顾问。不要对你的问题是否会受到欢迎做太乐观的估计 -- 如果你不确定,那就向别处发送,或者压根别发。

  在选择论坛、新闻群组或邮件列表时,别太相信名字,先看看 FAQ 或者许可书以弄清楚你的问题是否切题。发文前先翻翻已有的话题,这样可以让你感受一下那里的文化。事实上,事先在新闻组或邮件列表的历史记录中搜索与你问题相关的关键词是个极好的主意,也许这样就找到答案了。即使没有,也能帮助你归纳出更好的问题。

  别像机关枪似的一次"扫射"所有的帮助渠道,这就像大喊大叫一样会使人不快。要一个一个地来。

  搞清楚你的主题!最典型的错误之一是在某种致力于跨平台可移植的语言、套件或工具的论坛中提关于 Unix 或 Windows 操作系统程序界面的问题。如果你不明白为什么这是大错,最好在搞清楚这之间差异之前什么也别问。

  一般来说,在仔细挑选的公共论坛中提问,会比在私有论坛中提同样的问题更容易得到有用的回答。有几个理由可以支持这点,一是看潜在的回复者有多少,二是看观众有多少。黑客较愿意回答那些能帮助到许多人的问题。

  可以理解的是,老练的黑客和一些热门软件的作者正在接受过多的错发信息。就像那根最后压垮骆驼背的稻草一样,你的加入也有可能使情况走向极端 -- 已经好几次了,一些热门软件的作者从自己软件的支持中抽身出来,因为伴随而来涌入其私人邮箱的无用邮件变得无法忍受。

Stack Overflow

  搜索,然后 在Stack Exchange问。

  近年来,Stack Exchange community社区已经成为回答技术及其他问题的主要渠道,尤其是那些开放源码的项目。

  因为Google索引是即时的,在看Stack Exchange之前先在Google搜索。有很高的机率某人已经问了一个类似的问题,而且Stack Exchange网站们往往会是搜索结果中最前面几个。如果你在Google上没有找到任何答案,你再到特定相关主题的网站去找。用标签(Tag)搜索能让你更缩小你的搜索结果。

  Stack Exchange 已经成长到超过一百个网站,以下是最常用的几个站:

  • Super User 是问一些通用的电脑问题,如果你的问题跟代码或是写程序无关,只是一些网络连线之类的,请到这里。
  • Stack Overflow 是问写程序有关的问题。
  • Server Fault 是问服务器和网管相关的问题。

网站和 IRC 论坛

  本地的使用者群组(user group),或者你所用的 Linux 发行版本也许正在宣传他们的网页论坛或 IRC 频道,并提供新手帮助(在一些非英语国家,新手论坛很可能还是邮件列表), 这些地方是开始提问的好首选,特别是当你觉得遇到的也许只是相对简单或者很普通的问题时。有广告赞助的IRC频道是公开欢迎提问的地方,通常可以即时得到回应。

  事实上,如果程序出的问题只发生在特定Linux发行版提供的版本(这很常见),最好先去该发行版的论坛或邮件列表中提问,再到程序本身的论坛或邮件列表提问。(否则)该项目的黑客可能仅仅回复 "用我们的版本"。

  在任何论坛发文以前,先确认一下有没有搜索功能。如果有,就试着搜索一下问题的几个关键词,也许这会有帮助。如果在此之前你已做过通用的网页搜索(你也该这样做),还是再搜索一下论坛,搜索引擎有可能没来得及索引此论坛的全部内容。

  通过论坛或IRC频道来提供使用者支持服务有增长的趋势,电子邮件则大多为项目开发者间的交流而保留。所以最好先在论坛或IRC中寻求与该项目相关的协助。

  在使用IRC的时候,首先最好不要发布很长的问题描述,有些人称之为频道洪水。最好通过一句话的问题描述来开始聊天。

第二步,使用项目邮件列表

  当某个项目提供开发者邮件列表时,要向列表而不是其中的个别成员提问,即使你确信他能最好地回答你的问题。查一查项目的文件和首页,找到项目的邮件列表并使用它。有几个很好的理由支持我们采用这种办法:

  • 任何好到需要向个别开发者提出的问题,也将对整个项目群组有益。反之,如果你认为自己的问题对整个项目群组来说太愚蠢,也不能成为骚扰个别开发者的理由。
  • 向列表提问可以分散开发者的负担,个别开发者(尤其是项目领导人)也许太忙以至于没法回答你的问题。
  • 大多数邮件列表都会被存档,那些被存档的内容将被搜索引擎索引。如果你向列表提问并得到解答,将来其它人可以通过网页搜索找到你的问题和答案,也就不用再次发问了。
  • 如果某些问题经常被问到,开发者可以利用此信息来改进说明文件或软件本身,以使其更清楚。如果只是私下提问,就没有人能看到最常见问题的完整场景。

  如果一个项目既有"使用者" 也有"开发者"(或"黑客")邮件列表或论坛,而你又不会动到那些源代码,那么就向"使用者"列表或论坛提问。不要假设自己会在开发者列表中受到欢迎,那些人多半会将你的提问视为干扰他们开发的噪音。

  然而,如果你确信你的问题很特别,而且在"使用者" 列表或论坛中几天都没有回复,可以试试前往"开发者"列表或论坛发问。建议你在张贴前最好先暗地里观察几天以了解那里的行事方式(事实上这是参与任何私有或半私有列表的好主意)

  如果你找不到一个项目的邮件列表,而只能查到项目维护者的电子邮件地址,尽管向他发信。即使是在这种情况下,也别假设(项目)邮件列表不存在。在你的电子邮件中,请陈述你已经试过但没有找到合适的邮件列表,也提及你不反对将自己的邮件转发给他人(许多人认为,即使没什么秘密,私人电子邮件也不应该被公开。通过允许将你的电子邮件转发他人,你给了相应人员处置你邮件的选择)。

使用有意义且描述明确的标题

  在邮件列表、新闻群组或论坛中,大约 50 字以内的标题是抓住资深专家注意力的好机会。别用喋喋不休的帮帮忙跪求(更别说救命啊!!!!这样让人反感的话,用这种标题会被条件反射式地忽略)来浪费这个机会。不要妄想用你的痛苦程度来打动我们,而应该是在这点空间中使用极简单扼要的描述方式来提出问题。

  一个好标题范例是目标 -- 差异式的描述,许多技术支持组织就是这样做的。在目标部分指出是哪一个或哪一组东西有问题,在差异部分则描述与期望的行为不一致的地方。

蠢问题:救命啊!我的笔记本电脑不能正常显示了!
聪明问题:X.org 6.8.1 的鼠标光标会变形,某牌显卡 MV1005 芯片组。
更聪明问题:X.org 6.8.1 的鼠标光标,在某牌显卡 MV1005 芯片组环境下 - 会变形。

  编写目标 -- 差异 式描述的过程有助于你组织对问题的细致思考。是什么被影响了? 仅仅是鼠标光标或者还有其它图形?只在 X.org 的 X 版中出现?或只是出现在 6.8.1 版中? 是针对某牌显卡芯片组?或者只是其中的 MV1005 型号? 一个黑客只需瞄一眼就能够立即明白你的环境你遇到的问题。

  总而言之,请想像一下你正在一个只显示标题的存档讨论串(Thread)索引中查寻。让你的标题更好地反映问题,可使下一个搜索类似问题的人能够关注这个讨论串,而不用再次提问相同的问题。

  如果你想在回复中提出问题,记得要修改内容标题,以表明你是在问一个问题, 一个看起来像 Re: 测试 或者 Re: 新 bug 的标题很难引起足够重视。另外,在不影响连贯性之下,适当引用并删减前文的内容,能给新来的读者留下线索。

  对于讨论串,不要直接点击回复来开始一个全新的讨论串,这将限制你的观众。因为有些邮件阅读程序,比如 mutt ,允许使用者按讨论串排序并通过折叠讨论串来隐藏消息,这样做的人永远看不到你发的消息。

  仅仅改变标题还不够。mutt和其它一些邮件阅读程序还会检查邮件标题以外的其它信息,以便为其指定讨论串。所以宁可发一个全新的邮件。

  在网页论坛上,好的提问方式稍有不同,因为讨论串与特定的信息紧密结合,并且通常在讨论串外就看不到里面的内容,故通过回复提问,而非改变标题是可接受的。不是所有论坛都允许在回复中出现分离的标题,而且这样做了基本上没有人会去看。不过,通过回复提问,这本身就是暧昧的做法,因为它们只会被正在查看该标题的人读到。所以,除非你只想在该讨论串当前活跃的人群中提问,不然还是另起炉灶比较好。

使问题容易回复

  以请将你的回复寄到……来结束你的问题多半会使你得不到回答。如果你觉得花几秒钟在邮件客户端设置一下回复地址都麻烦,我们也觉得花几秒钟思考你的问题更麻烦。如果你的邮件程序不支持这样做,换个好点的;如果是操作系统不支持这种邮件程序,也换个好点的。

  在论坛,要求通过电子邮件回复是非常无礼的,除非你相信回复的信息可能比较敏感(而且有人会为了某些未知的原因,只让你而不是整个论坛知道答案)。如果你只是想在有人回复讨论串时得到电子邮件提醒,可以要求网页论坛发送给你。几乎所有论坛都支持诸如追踪此讨论串有回复时发送邮件提醒等功能。

用清晰、正确、精准并语法正确的语句

  我们从经验中发现,粗心的提问者通常也会粗心的写程序与思考(我敢打包票)。回答粗心大意者的问题很不值得,我们宁愿把时间耗在别处。

  正确的拼字、标点符号和大小写是很重要的。一般来说,如果你觉得这样做很麻烦,不想在乎这些,那我们也觉得麻烦,不想在乎你的提问。花点额外的精力斟酌一下字句,用不着太僵硬与正式 -- 事实上,黑客文化很看重能准确地使用非正式、俚语和幽默的语句。但它必须很准确,而且有迹象表明你是在思考和关注问题。

  正确地拼写、使用标点和大小写,不要将its混淆为it'sloose搞成lose或者将discrete弄成discreet。不要全部用大写,这会被视为无礼的大声嚷嚷(全部小写也好不到哪去,因为不易阅读。Alan Cox 也许可以这样做,但你不行。)

  更白话的说,如果你写得像是个半文盲[译注:小白],那多半得不到理睬。也不要使用即时通讯中的简写或火星文,如将简化为会使你看起来像一个为了少打几个键而省字的小白。更糟的是,如果像个小孩似地鬼画符那绝对是在找死,可以肯定没人会理你(或者最多是给你一大堆指责与挖苦)。

  如果在使用非母语的论坛提问,你可以犯点拼写和语法上的小错,但决不能在思考上马虎(没错,我们通常能弄清两者的分别)。同时,除非你知道回复者使用的语言,否则请使用英语书写。繁忙的黑客一般会直接删除用他们看不懂语言写的消息。在网络上英语是通用语言,用英语书写可以将你的问题在尚未被阅读就被直接删除的可能性降到最低。

  如果英文是你的外语(Second language),提示潜在回复者你有潜在的语言困难是很好的:
  [译注:以下附上原文以供使用]

English is not my native language; please excuse typing errors.

  • 英文不是我的母语,请原谅我的错字或语法

If you speak $LANGUAGE, please email/PM me;
I may need assistance translating my question.

  • 如果你说某语言,请寄信/私讯给我;我需要有人协助我翻译我的问题

I am familiar with the technical terms,
but some slang expressions and idioms are difficult for me.

  • 我对技术名词很熟悉,但对于俗语或是特别用法比较不甚了解。

I've posted my question in $LANGUAGE and English.
I'll be glad to translate responses, if you only use one or the other.

  • 我把我的问题用某语言和英文写出来,如果你只用一种语言回答,我会乐意将其翻译成另一种。

使用易于读取且标准的文件格式发送问题

  如果你人为地将问题搞得难以阅读,它多半会被忽略,人们更愿读易懂的问题,所以:

  • 使用纯文字而不是 HTML (关闭 HTML 并不难)。
  • 使用 MIME 附件通常是可以的,前提是真正有内容(譬如附带的源代码或 patch),而不仅仅是邮件程序生成的模板(譬如只是信件内容的拷贝)。
  • 不要发送一段文字只是一行句子但自动换行后会变成多行的邮件(这使得回复部分内容非常困难)。设想你的读者是在 80 个字符宽的终端机上阅读邮件,最好设置你的换行分割点小于 80 字。
  • 但是,对一些特殊的文件不要设置固定宽度(譬如日志档案拷贝或会话记录)。数据应该原样包含,让回复者有信心他们看到的是和你看到的一样的东西。
  • 在英语论坛中,不要使用Quoted-Printable MIME 编码发送消息。这种编码对于张贴非 ASCII 语言可能是必须的,但很多邮件程序并不支持这种编码。当它们处理换行时,那些文本中四处散布的=20符号既难看也分散注意力,甚至有可能破坏内容的语意。
  • 绝对,永远不要指望黑客们阅读使用封闭格式编写的文档,像微软公司的 Word 或 Excel 文件等。大多数黑客对此的反应就像有人将还在冒热气的猪粪倒在你家门口时你的反应一样。即便他们能够处理,他们也很厌恶这么做。
  • 如果你从使用 Windows 的电脑发送电子邮件,关闭微软愚蠢的智能引号功能 (从[选项] > [校订] > [自动校正选项],勾选掉智能引号单选框),以免在你的邮件中到处散布垃圾字符。
  • 在论坛,勿滥用表情符号HTML功能(当它们提供时)。一两个表情符号通常没有问题,但花哨的彩色文本倾向于使人认为你是个无能之辈。过滥地使用表情符号、色彩和字体会使你看来像个傻笑的小姑娘。这通常不是个好主意,除非你只是对性而不是对答案感兴趣。

  如果你使用图形用户界面的邮件程序(如微软公司的 Outlook 或者其它类似的),注意它们的默认设置不一定满足这些要求。大多数这类程序有基于选单的查看源代码命令,用它来检查发送文件夹中的邮件,以确保发送的是纯文本文件同时没有一些奇怪的字符。

精确的描述问题并言之有物

  • 仔细、清楚地描述你的问题或 Bug 的症状。
  • 描述问题发生的环境(机器配置、操作系统、应用程序、以及相关的信息),提供经销商的发行版和版本号(如:Fedora Core 4Slackware 9.1等)。
  • 描述在提问前你是怎样去研究和理解这个问题的。
  • 描述在提问前为确定问题而采取的诊断步骤。
  • 描述最近做过什么可能相关的硬件或软件变更。
  • 尽可能的提供一个可以重现这个问题的可控环境的方法。

  尽量去揣测一个黑客会怎样反问你,在你提问之前预先将黑客们可能遇到的问题回答一遍。

  以上几点中,当你报告的是你认为可能在代码中的问题时,给黑客一个可以重现你的问题的环境尤其重要。当你这么做时,你得到有效的回答的机会和速度都会大大的提升。

  Simon Tatham 写过一篇名为《如何有效的报告 Bug》的出色文章。强力推荐你也读一读。

话不在多而在精

  你需要提供精确有内容的信息。这并不是要求你简单的把成堆的出错代码或者资料完全转录到你的提问中。如果你有庞大而复杂的测试样例能重现程序挂掉的情境,尽量将它剪裁得越小越好。

  这样做的用处至少有三点。
  第一,表现出你为简化问题付出了努力,这可以使你得到回答的机会增加;
  第二,简化问题使你更有可能得到有用的答案;
  第三,在精炼你的 bug 报告的过程中,你很可能就自己找到了解决方法或权宜之计。

别动辄声称找到Bug

  当你在使用软件中遇到问题,除非你非常、非常的有根据,不要动辄声称找到了Bug。提示:除非你能提供解决问题的源代码补丁,或者提供回归测试来表明前一版本中行为不正确,否则你都多半不够完全确信。这同样适用在网页和文件,如果你(声称)发现了文件的Bug,你应该能提供相应位置的修正或替代文件。

  请记得,还有许多其它使用者没遇到你发现的问题,否则你在阅读文件或搜索网页时就应该发现了(你在抱怨前已经做了这些,是吧?)。这也意味着很有可能是你弄错了而不是软件本身有问题。

  编写软件的人总是非常辛苦地使它尽可能完美。如果你声称找到了 Bug,也就是在质疑他们的能力,即使你是对的,也有可能会冒犯到其中某部分人。当你在标题中嚷嚷着有Bug时,这尤其严重。

  提问时,即使你私下非常确信已经发现一个真正的 Bug,最好写得像是做错了什么。如果真的有 Bug,你会在回复中看到这点。这样做的话,如果真有 Bug,维护者就会向你道歉,这总比你惹恼别人然后欠别人一个道歉要好一点。

低声下气不能代替你的功课

  有些人明白他们不该粗鲁或傲慢的提问并要求得到答复,但他们选择另一个极端 -- 低声下气:我知道我只是个可悲的新手,一个撸瑟,但...。这既使人困扰,也没有用,尤其是伴随着与实际问题含糊不清的描述时更令人反感。

  别用原始灵长类动物的把戏来浪费你我的时间。取而代之的是,尽可能清楚地描述背景条件和你的问题情况。这比低声下气更好地定位了你的位置。

  有时网页论坛会设有专为新手提问的版面,如果你真的认为遇到了初学者的问题,到那去就是了,但一样别那么低声下气。

描述问题症状而非你的猜测

  告诉黑客们你认为问题是怎样造成的并没什么帮助。(如果你的推断如此有效,还用向别人求助吗?),因此要确信你原原本本告诉了他们问题的症状,而不是你的解释和理论;让黑客们来推测和诊断。如果你认为陈述自己的猜测很重要,清楚地说明这只是你的猜测,并描述为什么它们不起作用。

蠢问题

我在编译内核时接连遇到 SIG11 错误,
我怀疑某条飞线搭在主板的走线上了,这种情况应该怎样检查最好?

聪明问题

我的组装电脑是 FIC-PA2007 主机板搭载 AMD K6/233 CPU(威盛 Apollo VP2 芯片组),
256MB Corsair PC133 SDRAM 内存,在编译内核时,从开机 20 分钟以后就频频产生 SIG11 错误,
但是在头 20 分钟内从没发生过相同的问题。重新启动也没有用,但是关机一晚上就又能工作 20 分钟。
所有内存都换过了,没有效果。相关部分的标准编译记录如下…。

  由于以上这点似乎让许多人觉得难以配合,这里有句话可以提醒你:所有的诊断专家都来自密苏里州。 美国国务院的官方座右铭则是:让我看看(出自国会议员 Willard D. Vandiver 在 1899 年时的讲话:我来自一个出产玉米,棉花,牛蒡和民主党人的国家,滔滔雄辩既不能说服我,也不会让我满意。我来自密苏里州,你必须让我看看。) 针对诊断者而言,这并不是一种怀疑,而只是一种真实而有用的需求,以便让他们看到的是与你看到的原始证据尽可能一致的东西,而不是你的猜测与归纳的结论。所以,大方的展示给我们看吧!

按发生时间先后列出问题症状

  问题发生前的一系列操作,往往就是对找出问题最有帮助的线索。因此,你的说明里应该包含你的操作步骤,以及机器和软件的反应,直到问题发生。在命令行处理的情况下,提供一段操作记录(例如运行脚本工具所生成的),并引用相关的若干行(如 20 行)记录会非常有帮助。

  如果挂掉的程序有诊断选项(如 -v 的详述开关),试着选择这些能在记录中增加调试信息的选项。记住,不等于。试着选取适当的调试级别以便提供有用的信息而不是让读者淹没在垃圾中。

  如果你的说明很长(如超过四个段落),在开头简述问题,接下来再按时间顺序详述会有所帮助。这样黑客们在读你的记录时就知道该注意哪些内容了。

描述目标而不是过程

  如果你想弄清楚如何做某事(而不是报告一个 Bug),在开头就描述你的目标,然后才陈述重现你所卡住的特定步骤。

  经常寻求技术帮助的人在心中有个更高层次的目标,而他们在自以为能达到目标的特定道路上被卡住了,然后跑来问该怎么走,但没有意识到这条路本身就有问题。结果要费很大的劲才能搞定。

蠢问题

我怎样才能从某绘图程序的颜色选择器中取得十六进制的的 RGB 值?

聪明问题

我正试着用替换一幅图片的色码(color table)成自己选定的色码,我现在知道的唯一方法是编辑每个色码区块(table slot),
但却无法从某绘图程序的颜色选择器取得十六进制的的 RGB 值。

  第二种提问法比较聪明,你可能得到像是建议采用另一个更合适的工具的回复。

别要求使用私人电邮回复

  黑客们认为问题的解决过程应该公开、透明,此过程中如果更有经验的人注意到不完整或者不当之处,最初的回复才能够、也应该被纠正。同时,作为提供帮助者可以得到一些奖励,奖励就是他的能力和学识被其他同行看到。

  当你要求私下回复时,这个过程和奖励都被中止。别这样做,让回复者来决定是否私下回答 -- 如果他真这么做了,通常是因为他认为问题编写太差或者太肤浅,以至于对其它人没有兴趣。

  这条规则存在一条有限的例外,如果你确信提问可能会引来大量雷同的回复时,那么这个神奇的提问句会是向我发电邮,我将为论坛归纳这些回复。试着将邮件列表或新闻群组从洪水般的雷同回复中解救出来是非常有礼貌的 -- 但你必须信守诺言。

清楚明确的表达你的问题以及需求

  漫无边际的提问是近乎无休无止的时间黑洞。最有可能给你有用答案的人通常也正是最忙的人(他们忙是因为要亲自完成大部分工作)。这样的人对无节制的时间黑洞相当厌恶,所以他们也倾向于厌恶那些漫无边际的提问。

  如果你明确表述需要回答者做什么(如提供指点、发送一段代码、检查你的补丁、或是其他等等),就最有可能得到有用的答案。因为这会定出一个时间和精力的上限,便于回答者能集中精力来帮你。这么做很棒。

  要理解专家们所处的世界,请把专业技能想像为充裕的资源,而回复的时间则是稀缺的资源。你要求他们奉献的时间越少,你越有可能从真正专业而且很忙的专家那里得到解答。

  所以,界定一下你的问题,使专家花在辨识你的问题和回答所需要付出的时间减到最少,这技巧对你有用答案相当有帮助 -- 但这技巧通常和简化问题有所区别。因此,问我想更好的理解 X,可否指点一下哪有好一点说明?通常比问你能解释一下 X 吗?更好。如果你的代码不能运作,通常请别人看看哪里有问题,比要求别人替你改正要明智得多。

询问有关代码的问题时

  别要求他人帮你调试有问题的代码,不提示一下应该从何入手。张贴几百行的代码,然后说一声:它不能工作会让你完全被忽略。只贴几十行代码,然后说一句:在第七行以后,我期待它显示 <x>,但实际出现的是 <y>比较有可能让你得到回应。

  最有效描述程序问题的方法是提供最精简的 Bug 展示测试用例(bug-demonstrating test case)。什么是最精简的测试用例?那是问题的缩影;一小个程序片段能刚好展示出程序的异常行为,而不包含其他令人分散注意力的内容。怎么制作最精简的测试用例?如果你知道哪一行或哪一段代码会造成异常的行为,复制下来并加入足够重现这个状况的代码(例如,足以让这段代码能被编译/直译/被应用程序处理)。如果你无法将问题缩减到一个特定区块,就复制一份代码并移除不影响产生问题行为的部分。总之,测试用例越小越好(查看话不在多而在精一节)。

  一般而言,要得到一段相当精简的测试用例并不太容易,但永远先尝试这样做的是种好习惯。这种方式可以帮助你了解如何自行解决这个问题 —- 而且即使你的尝试不成功,黑客们也会看到你在尝试取得答案的过程中付出了努力,这可以让他们更愿意与你合作。

  如果你只是想让别人帮忙审查(Review)一下代码,在信的开头就要说出来,并且一定要提到你认为哪一部分特别需要关注以及为什么。

别把自己家庭作业的问题贴上来

  黑客们很擅长分辨哪些问题是家庭作业式的问题;因为我们中的大多数都曾自己解决这类问题。同样,这些问题得由来搞定,你会从中学到东西。你可以要求给点提示,但别要求得到完整的解决方案。

  如果你怀疑自己碰到了一个家庭作业式的问题,但仍然无法解决,试试在使用者群组,论坛或(最后一招)在项目的使用者邮件列表或论坛中提问。尽管黑客们看出来,但一些有经验的使用者也许仍会给你一些提示。

去掉无意义的提问句

  避免用无意义的话结束提问,例如有人能帮我吗?或者这有答案吗?

  首先:如果你对问题的描述不是很好,这样问更是画蛇添足。

  其次:由于这样问是画蛇添足,黑客们会很厌烦你 -- 而且通常会用逻辑上正确,但毫无意义的回答来表示他们的蔑视, 例如:没错,有人能帮你或者不,没答案

  一般来说,避免用 是或否对或错有或没有类型的问句,除非你想得到是或否类型的回答

即使你很急也不要在标题写紧急

  这是你的问题,不是我们的。宣称紧急极有可能事与愿违:大多数黑客会直接删除无礼和自私地企图即时引起关注的问题。更严重的是,紧急这个字(或是其他企图引起关注的标题)通常会被垃圾信过滤器过滤掉 -- 你希望能看到你问题的人可能永远也看不到。

  有半个例外的情况是,如果你是在一些很高调,会使黑客们兴奋的地方,也许值得这样去做。在这种情况下,如果你有时间压力,也很有礼貌地提到这点,人们也许会有兴趣回答快一点。

  当然,这风险很大,因为黑客们兴奋的点多半与你的不同。譬如从 NASA 国际空间站(International Space Station)发这样的标题没有问题,但用自我感觉良好的慈善行为或政治原因发肯定不行。事实上,张贴诸如紧急:帮我救救这个毛绒绒的小海豹!肯定让你被黑客忽略或惹恼他们,即使他们认为毛绒绒的小海豹很重要。

  如果你觉得这点很不可思议,最好再把这份指南剩下的内容多读几遍,直到你弄懂了再发文。

礼多人不怪,而且有时还很有帮助

  彬彬有礼,多用谢谢您的关注,或谢谢你的关照。让大家都知道你对他们花时间免费提供帮助心存感激。

  坦白说,这一点并没有比清晰、正确、精准并合法语法和避免使用专用格式重要(也不能取而代之)。黑客们一般宁可读有点唐突但技术上鲜明的 Bug 报告,而不是那种有礼但含糊的报告。(如果这点让你不解,记住我们是按问题能教给我们什么来评价问题的价值的)

  然而,如果你有一串的问题待解决,客气一点肯定会增加你得到有用回应的机会。

  (我们注意到,自从本指南发布后,从资深黑客那里得到的唯一严重缺陷反馈,就是对预先道谢这一条。一些黑客觉得先谢了意味着事后就不用再感谢任何人的暗示。我们的建议是要么先说先谢了然后事后再对回复者表示感谢,或者换种方式表达感激,譬如用谢谢你的关注谢谢你的关照。)

问题解决后,加个简短的补充说明

  问题解决后,向所有帮助过你的人发个说明,让他们知道问题是怎样解决的,并再一次向他们表示感谢。如果问题在新闻组或者邮件列表中引起了广泛关注,应该在那里贴一个说明比较恰当。

  最理想的方式是向最初提问的话题回复此消息,并在标题中包含已修正已解决或其它同等含义的明显标记。在人来人往的邮件列表里,一个看见讨论串问题 X问题 X - 已解决的潜在回复者就明白不用再浪费时间了(除非他个人觉得问题 X的有趣),因此可以利用此时间去解决其它问题。

  补充说明不必很长或是很深入;简单的一句你好,原来是网线出了问题!谢谢大家 – Bill比什么也不说要来的好。事实上,除非结论真的很有技术含量,否则简短可爱的小结比长篇大论更好。说明问题是怎样解决的,但大可不必将解决问题的过程复述一遍。

  对于有深度的问题,张贴调试记录的摘要是有帮助的。描述问题的最终状态,说明是什么解决了问题,在此之后才指明可以避免的盲点。避免盲点的部分应放在正确的解决方案和其它总结材料之后,而不要将此信息搞成侦探推理小说。列出那些帮助过你的名字,会让你交到更多朋友。

  除了有礼貌和有内涵以外,这种类型的补充也有助于他人在邮件列表/新闻群组/论坛中搜索到真正解决你问题的方案,让他们也从中受益。

  至少,这种补充有助于让每位参与协助的人因问题的解决而从中得到满足感。如果你自己不是技术专家或者黑客,那就相信我们,这种感觉对于那些你向他们求助的大师或者专家而言,是非常重要的。问题悬而未决会让人灰心;黑客们渴望看到问题被解决。好人有好报,满足他们的渴望,你会在下次提问时尝到甜头。

  思考一下怎样才能避免他人将来也遇到类似的问题,自问写一份文件或加个常见问题(FAQ)会不会有帮助。如果是的话就将它们发给维护者。

  在黑客中,这种良好的后继行动实际上比传统的礼节更为重要,也是你如何透过善待他人而赢得声誉的方式,这是非常有价值的资产。

如何解读答案


RTFM 和 STFW:如何知道你已完全搞砸了

  有一个古老而神圣的传统:如果你收到RTFM (Read The Fucking Manual)的回应,回答者认为你应该去读他妈的手册。当然,基本上他是对的,你应该去读一读。

  RTFM 有一个年轻的亲戚。如果你收到STFW(Search The Fucking Web)的回应,回答者认为你应该到他妈的网上搜索过了。那人多半也是对的,去搜索一下吧。(更温和一点的说法是 Google 是你的朋友!)

  在论坛,你也可能被要求去爬爬论坛的旧文。事实上,有人甚至可能热心地为你提供以前解决此问题的讨论串。但不要依赖这种关照,提问前应该先搜索一下旧文。

  通常,用这两句之一回答你的人会给你一份包含你需要内容的手册或者一个网址,而且他们打这些字的时候也正在读着。这些答复意味着回答者认为

  • 你需要的信息非常容易获得
  • 你自己去搜索这些信息比灌给你,能让你学到更多

  你不应该因此不爽;依照黑客的标准,他已经表示了对你一定程度的关注,而没有对你的要求视而不见。你应该对他祖母般的慈祥表示感谢。

如果还是搞不懂

  如果你看不懂回应,别立刻要求对方解释。像你以前试着自己解决问题时那样(利用手册,FAQ,网络,身边的高手),先试着去搞懂他的回应。如果你真的需要对方解释,记得表现出你已经从中学到了点什么。

  比方说,如果我回答你:看来似乎是 zentry 卡住了;你应该先清除它。,然后,这是一个很糟的后续问题回应:zentry 是什么? 的问法应该是这样:哦~~~我看过说明了但是只有 -z 和 -p 两个参数中提到了 zentries,而且还都没有清楚的解释如何清除它。你是指这两个中的哪一个吗?还是我看漏了什么?

处理无礼的回应

  很多黑客圈子中看似无礼的行为并不是存心冒犯。相反,它是直接了当,一针见血式的交流风格,这种风格更注重解决问题,而不是使人感觉舒服而却模模糊糊。

  如果你觉得被冒犯了,试着平静地反应。如果有人真的做了出格的事,邮件列表、新闻群组或论坛中的前辈多半会招呼他。如果这没有发生而你却发火了,那么你发火对象的言语可能在黑客社区中看起来是正常的,而将被视为有错的一方,这将伤害到你获取信息或帮助的机会。

  另一方面,你偶尔真的会碰到无礼和无聊的言行。与上述相反,对真正的冒犯者狠狠地打击,用犀利的语言将其驳得体无完肤都是可以接受的。然而,在行事之前一定要非常非常的有根据。纠正无礼的言论与开始一场毫无意义的口水战仅一线之隔,黑客们自己莽撞地越线的情况并不鲜见。如果你是新手或外人,避开这种莽撞的机会并不高。如果你想得到的是信息而不是消磨时光,这时最好不要把手放在键盘上以免冒险。

  (有些人断言很多黑客都有轻度的自闭症或亚斯伯格综合症,缺少用于润滑人类社会正常交往所需的神经。这既可能是真也可能是假的。如果你自己不是黑客,兴许你认为我们脑袋有问题还能帮助你应付我们的古怪行为。只管这么干好了,我们不在乎。我们喜欢我们现在这个样子,并且通常对病患标记都有站得住脚的怀疑。)

  Jeff Bigler 的观察总结和这个相关也值得一读 (tact filters)。

  在下一节,我们会谈到另一个问题,当行为不当时所会受到的冒犯

如何避免扮演失败者


  在黑客社区的论坛中有那么几次你可能会搞砸 -- 以本指南所描述到的或类似的方式。而你会在公开场合中被告知你是如何搞砸的,也许攻击的言语中还会带点夹七夹八的颜色。

  这种事发生以后,你能做的最糟糕的事莫过于哀嚎你的遭遇、宣称被口头攻击、要求道歉、高声尖叫、憋闷气、威胁诉诸法律、向其雇主报怨、忘了关马桶盖等等。相反地,你该这么做:

  熬过去,这很正常。事实上,它是有益健康且合理的。

  社区的标准不会自行维持,它们是通过参与者积极而公开地执行来维持的。不要哭嚎所有的批评都应该通过私下的邮件传送,它不是这样运作的。当有人评论你的一个说法有误或者提出不同看法时,坚持声称受到个人攻击也毫无益处,这些都是失败者的态度。

  也有其它的黑客论坛,受过高礼节要求的误导,禁止参与者张贴任何对别人帖子挑毛病的消息,并声称如果你不想帮助用户就闭嘴。 结果造成有想法的参与者纷纷离开,这么做只会使它们沦为毫无意义的唠叨与无用的技术论坛。

  夸张的讲法是:你要的是友善(以上述方式)还是有用?两个里面挑一个。

  记着:当黑客说你搞砸了,并且(无论多么刺耳)告诉你别再这样做时,他正在为关心他的社区而行动。对他而言,不理你并将你从他的生活中滤掉更简单。如果你无法做到感谢,至少要表现得有点尊严,别大声哀嚎,也别因为自己是个有戏剧性超级敏感的灵魂和自以为有资格的新来者,就指望别人像对待脆弱的洋娃娃那样对你。

  有时候,即使你没有搞砸(或者只是在他的想像中你搞砸了),有些人也会无缘无故地攻击你本人。在这种情况下,抱怨倒是真的会把问题搞砸。

  这些来找麻烦的人要么是毫无办法但自以为是专家的不中用家伙,要么就是测试你是否真会搞砸的心理专家。其它读者要么不理睬,要么用自己的方式对付他们。这些来找麻烦的人在给他们自己找麻烦,这点你不用操心。

  也别让自己卷入口水战,最好不要理睬大多数的口水战 -- 当然,这是在你检验它们只是口水战,并且未指出你有搞砸的地方,同时也没有巧妙地将问题真正的答案藏于其后(这也是有可能的)。

不该问的问题


  以下是几个经典蠢问题,以及黑客没回答时心中所想的:

  问题:我能在哪找到 X 程序或 X 资源?

  问题:我怎样用 X 做 Y?

  问题:如何设定我的 shell 提示?

  问题:我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗?

  问题:我的程序/设定/SQL 语句没有用

  问题:我的 Windows 电脑有问题,你能帮我吗?

  问题:我的程序不会动了,我认为系统工具 X 有问题

  问题:我在安装 Linux(或者 X )时有问题,你能帮我吗?

  问题:我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?


问题:我能在哪找到 X 程序或 X 资源?

  回答:就在我找到它的地方啊,白痴 -- 搜索引擎的那一头。天哪!难道还有人不会用 Google 吗?

问题:我怎样用 X 做 Y?

  回答:如果你想解决的是 Y ,提问时别给出可能并不恰当的方法。这种问题说明提问者不但对 X 完全无知,也对 Y 要解决的问题糊涂,还被特定形势禁锢了思维。最好忽略这种人,等他们把问题搞清楚了再说。

问题:如何设定我的 shell 提示??

  回答:如果你有足够的智慧提这个问题,你也该有足够的智慧去 RTFM,然后自己去找出来。

问题:我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗?

  回答:试试看就知道了。如果你试过,你既知道了答案,就不用浪费我的时间了。

问题:我的{程序/设定/SQL 语句}不工作

  回答:这不算是问题吧,我对要我问你二十个问题才找得出你真正问题的问题没兴趣 -- 我有更有意思的事要做呢。在看到这类问题的时候,我的反应通常不外如下三种

  • 你还有什么要补充的吗?
  • 真糟糕,希望你能搞定。
  • 这关我有什么屁事?

问题:我的 Windows 电脑有问题,你能帮我吗?

  回答:能啊,扔掉微软的垃圾,换个像 Linux 或 BSD 的开放源代码操作系统吧。

  注意:如果程序有官方版 Windows 或者与 Windows 有互动(如 Samba),你可以问与 Windows 相关的问题, 只是别对问题是由 Windows 操作系统而不是程序本身造成的回复感到惊讶, 因为 Windows 一般来说实在太烂,这种说法通常都是对的。

问题:我的程序不会动了,我认为系统工具 X 有问题

  回答:你完全有可能是第一个注意到被成千上万用户反复使用的系统调用与函数库档案有明显缺陷的人,更有可能的是你完全没有根据。不同凡响的说法需要不同凡响的证据,当你这样声称时,你必须有清楚而详尽的缺陷说明文件作后盾。

问题:我在安装 Linux(或者 X )时有问题,你能帮我吗?

  回答:不能,我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux 使用群组者寻求实际的指导吧(你能在这儿找到使用者群组的清单)。

  注意:如果安装问题与某 Linux 的发行版有关,在它的邮件列表、论坛或本地使用者群组中提问也许是恰当的。此时,应描述问题的准确细节。在此之前,先用 Linux所有被怀疑的硬件作关键词仔细搜索。

问题:我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢?

  回答:想要这样做,说明了你是个卑鄙小人;想找个黑客帮你,说明你是个白痴!

好问题与蠢问题


  最后,我将透过举一些例子,来说明怎样聪明的提问;同一个问题的两种问法被放在一起,一种是愚蠢的,另一种才是明智的。

蠢问题

我可以在哪儿找到关于 Foonly Flurbamatic 的资料?

  这种问法无非想得到 STFW 这样的回答。

聪明问题

我用 Google 搜索过 "Foonly Flurbamatic 2600",但是没找到有用的结果。谁知道上哪儿去找对这种设备编程的资料?

  这个问题已经 STFW 过了,看起来他真的遇到了麻烦。

蠢问题

我从 foo 项目找来的源码没法编译。它怎么这么烂?

  他觉得都是别人的错,这个傲慢自大的提问者。

聪明问题

foo 项目代码在 Nulix 6.2 版下无法编译通过。我读过了 FAQ,但里面没有提到跟 Nulix 有关的问题。这是我编译过程的记录,我有什么做的不对的地方吗?

  提问者已经指明了环境,也读过了 FAQ,还列出了错误,并且他没有把问题的责任推到别人头上,他的问题值得被关注。

蠢问题

我的主机板有问题了,谁来帮我?

  某黑客对这类问题的回答通常是:好的,还要帮你拍拍背和换尿布吗?,然后按下删除键。

聪明问题

我在 S2464 主机板上试过了 X 、 Y 和 Z ,但没什么作用,我又试了 A 、 B 和 C 。请注意当我尝试 C 时的奇怪现象。显然 florbish 正在 grommicking,但结果出人意料。通常在 Athlon MP 主机板上引起 grommicking 的原因是什么?有谁知道接下来我该做些什么测试才能找出问题?

  这个家伙,从另一个角度来看,值得去回答他。他表现出了解决问题的能力,而不是坐等天上掉答案。

  在最后一个问题中,注意告诉我答案给我启示,指出我还应该做什么诊断工作之间微妙而又重要的区别。

  事实上,后一个问题源自于 2001 年 8 月在 Linux 内核邮件列表(lkml)上的一个真实的提问。我(Eric)就是那个提出问题的人。我在 Tyan S2464 主板上观察到了这种无法解释的锁定现象,列表成员们提供了解决这一问题的重要信息。

  通过我的提问方法,我给了别人可以咀嚼玩味的东西;我设法让人们很容易参与并且被吸引进来。我显示了自己具备和他们同等的能力,并邀请他们与我共同探讨。通过告诉他们我所走过的弯路,以避免他们再浪费时间,我也表明了对他们宝贵时间的尊重。

  事后,当我向每个人表示感谢,并且赞赏这次良好的讨论经历的时候, 一个 Linux 内核邮件列表的成员表示,他觉得我的问题得到解决并非由于我是这个列表中的人,而是因为我用了正确的方式来提问。

  黑客从某种角度来说是拥有丰富知识但缺乏人情味的家伙;我相信他是对的,如果我个乞讨者那样提问,不论我是谁,一定会惹恼某些人或者被他们忽视。他建议我记下这件事,这直接导致了本指南的出现。

如果得不到回答


  如果仍得不到回答,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。

  总的来说,简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心,知道你问题答案的人可能生活在不同的时区,可能正在睡觉,也有可能你的问题一开始就没有组织好。

  你可以通过其他渠道获得帮助,这些渠道通常更适合初学者的需要。

  有许多网上的以及本地的使用者群组,由热情的软件爱好者(即使他们可能从没亲自写过任何软件)组成。通常人们组建这样的团体来互相帮助并帮助新手。

  另外,你可以向很多商业公司寻求帮助,不论公司大还是小。别为要付费才能获得帮助而感到沮丧!毕竟,假使你的汽车发动机汽缸密封圈爆掉了-- 完全可能如此 --你还得把它送到修车铺,并且为维修付费。就算软件没花费你一分钱,你也不能强求技术支持总是免费的。

  对像是 Linux 这种大众化的软件,每个开发者至少会对应到上万名使用者。根本不可能由一个人来处理来自上万名使用者的求助电话。要知道,即使你要为这些协助付费,和你所购买的同类软件相比,你所付出的也是微不足道的(通常封闭源代码软件的技术支持费用比开放源代码软件的要高得多,且内容也没那么丰富)。

如何更好地回答问题


  态度和善一点。问题带来的压力常使人显得无礼或愚蠢,其实并不是这样。

  对初犯者私下回复。对那些坦诚犯错之人没有必要当众羞辱,一个真正的新手也许连怎么搜索或在哪找常见问题都不知道。

  如果你不确定,一定要说出来!一个听起来权威的错误回复比没有还要糟,别因为听起来像个专家很好玩,就给别人乱指路。要谦虚和诚实,给提问者与同行都树个好榜样。

  如果帮不了忙,也别妨碍他。不要在实际步骤上开玩笑,那样也许会毁了使用者的设置 --有些可怜的呆瓜会把它当成真的指令。

  试探性的反问以引出更多的细节。如果你做得好,提问者可以学到点东西 --你也可以。试试将蠢问题转变成好问题,别忘了我们都曾是新手。

  尽管对那些懒虫抱怨一声 RTFM 是正当的,能指出文件的位置(即使只是建议个 Google 搜索关键词)会更好。

  如果你决定回答,就请给出好的答案。当别人正在用错误的工具或方法时别建议笨拙的权宜之计(wordaround),应推荐更好的工具,重新界定问题。

  正面的回答问题!如果这个提问者已经很深入的研究而且也表明已经试过 X 、 Y 、 Z 、 A 、 B 、 C 但没得到结果,回答 试试看 A 或是 B 或者 试试 X 、 Y 、 Z 、 A 、 B 、 C 并附上一个链接一点用都没有。

  帮助你的社区从问题中学习。当回复一个好问题时,问问自己如何修改相关文件或常见问题文件以免再次解答同样的问题?,接着再向文件维护者发一份补丁。

  如果你是在研究一番后才做出的回答,展现你的技巧而不是直接端出结果。毕竟授人以鱼不如授人以渔

相关资源


  如果你需要个人电脑、Unix 系统和网络如何运作的基础知识,参阅 Unix 系统和网络基本原理

  当你发布软件或补丁时,试着按软件发布实践操作。

鸣谢


  Evelyn Mitchel 贡献了一些愚蠢问题例子并启发了编写如何更好地回答问题这一节, Mikhail Ramendik 贡献了一些特别有价值的建议和改进。

]]>
0 https://www.windawings.com/default/how-to-ask-questions-the-smart-way.html#comments https://www.windawings.com/feed/default/how-to-ask-questions-the-smart-way.html
Java Virtual Machine(JVM) https://www.windawings.com/java/jvm.html https://www.windawings.com/java/jvm.html Mon, 12 Mar 2018 00:21:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

jvm architecture

运行时数据区域(Runtime Memory Aera)


  包括共享内存区(方法区, 堆, GC主要关注区域)和线程隔离内存区(虚拟机栈, 本地方法栈和程序计数器)。

程序计数器(Program Counter Register)

  字节码解释器通过程序计时器选取下一条执行的字节码指令(分支, 循环, 跳转, 异常处理以及线程恢复等)。

  • 执行Java方法时, 计数器记录线程正在执行的虚拟机字节码指令地址; Native方法时, 计数器为空
  • 计数器内存区是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

虚拟机栈(Stack)

  虚拟机栈是Java方法执行的内存模型, Java方法执行需要创建一个栈帧存储局部变量表, 操作数栈, 动态连接以及方法出口等信息。

  • 栈内存指虚拟机栈中局部变量表(基本数据类型, 对象引用, return地址类型(指向一条字节码指令的地址))。其中64位长度longdouble占用两个局部变量空间, 其余数据类型占用一个。
  • 线程请求大于虚拟机所允许深度抛出StackOverflowError; 虚拟机拓展时无法申请到足够内存抛出OutOfMemoryError.本地方法栈同理。

(Heap)

  Java堆唯一目的为分配几乎所有对象实例, 是垃圾回收(garbage collector)的主要管理区域, 细分为新生代和老年代以便更好更快得回收和分配内存。

  • 堆在逻辑内存中连续(即物理内存上可不连续)
  • 堆分配实例失败或堆无法拓展时抛出OutOfMemoryError

方法区(Method Area)

  方法区存储虚拟机加载的类信息, 常量, 静态变量以及即时编译器编译后的代码等数据。

  • 不需要物理连续的内存, 可以选择固定内存大小, 可拓展
  • 可以选择不实现GC, 该区域主要回收常量池和类型卸载
  • 方法区分配内存失败抛出OutOfMemoryError
  • class文件包含版本, 字段, 方法, 接口以及常量池(存放编译期生成的字面量和符号引用, 类加载后存入方法区的运行时常量池中)等描述信息
  • 运行时常量池为方法区的一部分, Java常量不一定只在编译期产生, 也可以在运行中产生, 比如String类的intern方法

Hotspot


  • new → 检查参数(是否需要类加载) → 分配内存(指针碰撞法和空闲列表法) → Init方法执行对象初始化
  • 内存中的对象包含对象头, 实例数据对齐填充三块区域
  • 对象访问有直接指针访问法和句柄访问法(先访问句柄池获取到对象内存地址, 再访问到对象)

对象创建(Object Creation)

  对象创建即new指令时, 先检查指令参数是否定位到一个类的符号引用, 符号引用的类是否已被加载解析和初始化过(若没有需要执行类加载); 后为新对象分配内存, 即从堆中划分确定大小的内存(有指针碰撞空闲列表分配方式), GC带有压缩整理功能则Java堆规整, 即可采用指针碰撞分配方式(即已用与空闲泾渭分明, 只需要向空闲内存区移动指针), 反之空闲列表(记录已用与空闲内存区的列表); new指令结束后执行Init方法进行对象初始化。

对象内存(Object Memory)

  • 对象头$
    \begin{cases}
    \text{对象运行时数据(如哈希码, GC分代年龄, 锁状态标志, 线程持有锁, 偏向线程ID, 偏向时间戳), 64位64bits, 32位32bits}\\
    \text{类型指针(对象指向其类元数据的指针, 用于虚拟机判断对象实例的类, 确定其Class指针), 64位64bits(压缩开启32bits), 32位32bits}
    \end{cases}
    $

  • 对齐填充: 要求8bytes对齐, 不足需要补位
  • 实例数据
基本数据类型 占用空间(bytes)
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8
reference 4(32位)/8(64位)

对象访问(Object Access)

  • 句柄方式: Java堆中会划分出句柄池(包含对象实例数据和类型数据的具体地址)提供对象的句柄访问方式, 引用中存储的即是对象的句柄地址(对象被移动时只需要改变句柄中的实例数据指针而不需要修改引用的句柄地址值)
  • 指针方式: 直接指针访问方式则使得引用存储的是对象的地址, 节省了一次指针定位的时间开销, 对于访问频繁的对象可减少可观的执行时间成本

内存溢出(Out Of Memory)


堆溢出(Heap Overflow)

  在不断创建对象过程中保证GC Roots与对象间有可达路径避免GC清除, 此时对象实例数量撑到最大堆容量限制时抛出内存溢出异常。

  • 可通过工具查看溢出对象到GC Roots的引用链, 掌握溢出对象类型信息和GC Roots引用链信息可定位溢出代码
  • 若最大堆限制设置过低, 可检查虚拟机的堆参数(-Xmx-Xms)是否可调大, 可在代码上检查是否存在对象生命周期过长, 持有状态时间过长等情况以减少内存消耗

栈溢出(Stack Overflow)

  在Hotspot中-Xoss参数(设置本地方法栈大小)实际上是无效的, 栈容量只由-Xss决定。

  • 单线程时无论栈帧过大还是虚拟机栈容量过小, 分配内存失败都抛出StackOverflowError
  • 多线程更容易引起内存溢出(多个栈容量叠加), 通过减少线程或更换64位虚拟机解决, 若无法解决则只能减少最大堆和减少栈容量从而获取更多线程

方法区溢出(Method Area Overflow)

  String.intern是一个native方法, 方法逻辑: 返回一个字符串常量池中与此String对象相等的字符串对象引用, 若常量池没有相等的则把次String对象加入常量池中并返回其对象引用。
  常量池分配在永久代中, 可通过-XX:PermSize-XX:MaxPermSize设置方法区大小, 可间接影响到常量池的容量。

  • JDK1.6intern中把首次遇到的字符串复制到永久代并返回永久代中实例的引用, StringBuilder则放在Java堆中
  • JDK1.7intern中不再复制实例, 只在常量池中记录首次出现的实例引用, 即internStringBuilder创建返回的是同一个字符串实例引用

垃圾收集(Garbage Collecting)


  线程隔离的三个区域(程序计数器, 虚拟机栈和本地方法栈)与线程共生共灭, 当方法或线程结束时其内存自然回收, 所以无需过多考虑回收问题。

存活(Dead or Live)

  主要有引用计数器法, 跟踪式分析法(或可达性分析法)。

引用计数(Reference Counting)

  每引用同一对象一次其计数器加一, 引用失效减一, 计数器为0则对象可回收。一般用哈希表管理计数, 以被管理的对象地址为key, 计数为value.可手动控制(MRC), 也可以自动管理(ARC)。
  缺点是需要频繁更新引用计数, 引用形成环路造成循环引用时难以处理(一般通过介入弱引用解决)。
  引用计数不仅可以用来管理内容, 操作系统的文件资源也可以适应(如文件描述符fd)。

跟踪式(Trace)

  把所有被管理对象的引用关系组织为一张有向图, 从必定无需回收的节点作为根节点触发跟踪其引用关系, 从而遍历所有可达节点, 可达节点就无需回收, 不可达就可以回收。跟踪式为自动管理。
  JVM中通过多个GC Roots的对象起始向下搜索所经过的路径为引用链, 当一个对象到任何GC Roots没有任何引用链时, 则此对象可回收。

  • 可达性判断: 单条引用链最弱的一条引用决定对象的可达性; 多条引用链最强的一条引用决定对象的可达性
  • GC Roots: 包括虚拟机栈栈帧的本地变量表中(即栈内存), 方法区中类静态属性和常量, 本地方法栈JNI这几处所引用的对象
GC Roots

引用(Reference)

  $强引用 \gt 软引用 \gt 弱引用 \gt 虚引用$。

  • 强引用(Strong Reference) 类似Object obj = new Object(); GC不回收存在强引用的对象(宁愿抛出OutOfMemoryError)
  • 软引用(Soft Reference)$
    \begin{cases}
    \text{描述一些有用但非必须的元素, 系统将要发生内存溢出前会回收软引用对象, 仍不足抛异常}\\
    \text{可用于对内存敏感的高速缓存}\\
    \text{与引用队列(ReferenceQueue)联合使用, 若引用对象被回收则JVM将其加入到关联引用队列中}
    \end{cases}
    $

  • 弱引用(Weak Reference)$
    \begin{cases}
    \text{描述非必须对象, 弱引用对象只存活到下一次GC前(无论内存是否足够)}\\
    \text{可以和引用队列联合使用, 若引用对象被回收, 则JVM将其加入到关联引用队列中}
    \end{cases}
    $

  • 虚引用(Phantom Reference)$
    \begin{cases}
    \text{虚引用是否存在不影响对象生命周期, 也无法通过虚引用获取对象实例}\\
    \text{仅用于GC回收虚引用对象时收到系统通知, 跟踪对象被GC回收的活动}\\
    \text{虚引用必须和引用队列联合使用}\\
    \text{可通过判断引用队列中是否加入虚引用来得知引用对象是否将被回收, 即可在其被回收前做操作}
    \end{cases}
    $
ReferenceQueue queue = new ReferenceQueue();
PhantomReference pr = new PhantomReference(object, queue);

软引用(Soft Reference)

  SoftReference保存对一个java对象的软引用, 对象被回收前get方法返回强引用, 对象被回收后get方法返回null.

/* 在触发OutOfMemoryError前, JVM尽可能优先回收长时间闲置不用的软引用对象, 刚创建的新软引用对象尽可能保留 */
MyObject aRef = new MyObject();
SoftReference aSoftRef = new SoftReference(aRef);
// 此时(MyObject)aSoftRef.get()返回强引用
aRef = null;
// 此时(MyObject)aSoftRef.get()可能返回null

ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(aRef, queue);

// 当软引用对象aRef被回收时, ref强引用的SoftReference对象被列入ReferenceQueue中
// poll方法: 队列为空返回null; 非空返回前一个SoftReference对象

// 常用以下方式清理SoftReference对象
SoftReference ref = null;
while ((ref = (SoftReference) q.poll()) != null) {
   // 清除ref
}

  通过软引用构造Java对象的高速缓存, 避免重复构建同一个对象(比如查询同一条数据库数据)。

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Hashtable;

public class EmployeeCache {
    /** 一个Cache实例 */
    static private EmployeeCache cache;

    /** 用于Chche内容的存储 */
    private Hashtable<String, EmployeeRef> employeeRefs;

    /** 垃圾Reference的队列 */
    private ReferenceQueue<Employee> q;

    // 构建一个缓存器实例
    private EmployeeCache() {
        employeeRefs = new Hashtable<String,EmployeeRef>();
        q = new ReferenceQueue<Employee>();
    }

    // 取得缓存器实例
    public static EmployeeCache getInstance() {
        if (cache == null) {
            cache = new EmployeeCache();
        }
        return cache;
    }

    /** 依据所指定的ID号,重新获取相应Employee对象的实例 */
    public Employee getEmployee(String ID) {
        Employee em = null;

        // 缓存中是否有该Employee实例的软引用,如果有,从软引用中取得。
        if (employeeRefs.containsKey(ID)) {
            EmployeeRef ref = (EmployeeRef) employeeRefs.get(ID);
            em = (Employee) ref.get();
        }

        // 如果没有软引用,或者从软引用中得到的实例是null,重新构建一个实例,
        // 并保存对这个新建实例的软引用
        if (em == null) {
            em = new Employee(ID);
            System.out.println("Retrieve From EmployeeInfoCenter. ID = " + ID);
            this.cacheEmployee(em);
        }

        return em;
    }

    /** 清除Cache内的全部内容 */
    public void clearCache() {
        cleanCache();
        //employeeRefs.clear(); 上一步选择性清除了一遍这里又整体清除一遍?
        System.gc();
        System.runFinalization();
    }

    /** 以软引用的方式对一个Employee对象的实例进行引用并保存该引用 */
    private void cacheEmployee(Employee em) {
        cleanCache();// 清除垃圾引用
        EmployeeRef ref = new EmployeeRef(em, q);
        employeeRefs.put(em.getID(), ref);
    }

    private void cleanCache() {
        EmployeeRef ref = null;

        // ReferenceQueue起到监听器的作用, 可监听到对象存活
        while ((ref = (EmployeeRef) q.poll()) != null) {
           employeeRefs.remove(ref._key);
        }
    }

    /** 继承SoftReference,使得每一个实例都具有可识别的标识 */
    private class EmployeeRef extends SoftReference<Employee> {
        private String _key = "";

        public EmployeeRef(Employee em, ReferenceQueue<Employee> q) {
            super(em, q);
            _key = em.getID();
        }
    }
}

弱引用(Weak Reference)

  弱引用较之软引用有更短暂的生命周期。GC线程一旦在其管辖内存区域扫描到弱引用对象, 不管当前内存空间是否足够都会将其内存回收。不过GC线程优先级很低, 所以不一定能很快遍历到弱引用对象。

  • 通常用于Debug, 内存监视工具等程序中
  • Proxy类中会把动态生成的Class实例暂存与一个Map<WeakReference>中作为Cache
  • 可用于规范化映射WeakHashMap, WeakHashMap使用WeakReference作为value, 当key的引用被置为null时, map内容会被很快GC.
  • 当想引用一个对象但又不想介入其生命周期时可使用弱引用, 以避免对其GC过程判断中造成干扰

Finalize

  FinalizeObjectprotected方法(没有任何操作), 可被子类覆盖, GC回收前会调用且只调用一次。建议用于清理本地对象(JNI创建的对象)和显示调用其他非内存资源(如Socket和文件)的释放方法。

问题(Problem)

  • Finalize方法不能保证执行(即使调用System.runFinalizersOnExit()Runtime.runFinalizersOnExit(true)(二者已被JDK弃用), 且可能导致finalizer方法被活对象调用时, 其他线程正在对该对象并行操作致使不正确行为或死锁, 即本质上不安全), 调用System.gc()System.runFinalization()只增加其执行机会
  • 超类的Finalize需要显示调用
  • Finalize中的异常会被GC忽略且不会向上传递, 也没有日志记录
  • Finalize给性能增加负担
  • Finalize中可将待回收对象赋值到GC Roots可达的对象引用, 导致该对象再生

过程(Process)

$$
\text{对象GC Roots不可达} \to \text{GC判断} \begin{cases}
\text{未写finalize方法 } \to \text{对象被回收}\\
\text{复写finalize方法} \to \text{进入F-Queue队列} \to \text{低优先级线程执行队列中的finalize方法} \to \text{GC判断} \begin{cases}
\text{GC Roots不可达} \to \text{回收对象}\\
\text{GC Roots可达}\;\;\,\, \to \text{对象再生}
\end{cases}
\end{cases}
$$

  对象有终结状态空间$F = {unfinalized(未准备), finalizable(准备), finalized(已执行)}$和可达状态空间$R = {reachable(可达), finalizer\text{-}reachable(由finalizable可达), unreachable(不可达)}$.

finalize lifycycle
  • 新建对象由路径A持有状态[reachable, unfinalized]
  • 引用关系变迁 → 路径BCD变为finalizer-reachable或路径EF变为unreachable
  • 当unfinalized对象的可达状态变为finalizer-reachable或unreachable时, 由路径GH: unfinalized → finalizable
  • JVM取出某finalizable对象标记为finalized并执行其finalize方法(由活线程调用, 则再次reachable), 该对象经由KJ变化状态为[reachable, finalized], 同时影响其他对象经由LMN可达状态由finalizer-reachable → reachable
  • 没有[unreachable, finalizable]的状态, 不然也无法调用这个对象的finalize方法了
  • 显示调用finalize方法不会影响上述状态变迁, 但JVM最多调用一次该方法(即使对象再生)
  • 处于[unreachable, finalized]的对象其内存将会被回收, JVM会优化未复写该方法的对象的GC, 直接经由路径O回收对象
  • System.runFinalizersOnExit等方法可以使对象即使处于reachable状态, JVM仍对其执行finalize方法

模板(Template)

  复写Finalize请遵循一下模板:

@Override
protected void finalize() throws Throwable {
    try {
        // release resources here
    } catch (Throwable t) {
        throw t;
    } finally {
        super.finalize();
    }
}

方法区回收(Method Area Collector)

  永久代的GC主要回收废弃常量和无用的类。

  • 废弃常量: 当常量池中某个常量没有任何对象引用, 也没有任何地方引用该字面量时, 该常量会被GC清除
  • 无用的类$
    \begin{cases}
    \text{堆中不存在类实例}\\
    \text{加载该类的类加载器(ClassLoader)被回收}\\
    \text{所用的java.lang.Class对象未被任何地方引用, 即无法通过反射访问该类方法}
    \end{cases}, \text{需要同时满足}
    $

  HotSpot提供-Xnoclassgc参数表示不对方法区进行GC. -verbose:class -XX:+TraceClassLoading XX:+TraceClassUnLoading可查看类的加载卸载信息。
  在大量使用反射, 动态代理以及CGLib等bytecode框架场景, 动态生成JSP以及OSGI这类频繁自定义ClassLoader的场景下需要虚拟机具备类卸载功能以保证永久代不会溢出。

收集算法(Collect Algorithem)

  GC会监控每个对象的运行状况(申请, 引用被引用, 赋值等), 通过有向图实时监测对象是否可达来管理内存。
  Java语言中判断一个内存空间是否符合GC标准:

  • obj = null; // 以下再没有调用
  • obj = new Object(); // 重新分配内存空间

  • 标记-清除算法$
    \begin{cases}
    \text{先标记待回收对象, 标记完后统一回收所有对象}\\
    \text{标记清除效率不高}\\
    \text{标记清除产生大量不连续内存碎片, 可能导致分配较大对象无足够连续内存触发另一次GC}
    \end{cases}
    $
mark-sweep
  • 标记整理算法 $
    \begin{cases}
    \text{也叫标记压缩(mark-compact), mark过程和mark-sweep一样}\\
    \text{不直接清理可回收对象, 让所有活对象向一端移动并清理端边界外的内存}\\
    \text{针对老年代的特点和复制算法在100%存活的极端情况}
    \end{cases}
    $
mark-compact
  • 复制算法
    $$
    \text{原理}
    \begin{cases}
    \text{内存平分两块, 每次使用一块, 一块不足时复制到另一块并一次性清理已使用过的内存}\\
    \text{不用考虑内存碎片等复杂情况, 但内存缩小为原来的一半}\\
    \text{持续复制长生存期对象效率低}
    \end{cases}
    $$
    $$
    \text{实现}
    \begin{cases}
    \text{内存分一较大Eden空间和两较小Survivor空间, 每次使用Eden和其中一块Survivor}\\
    \text{不足存放到Survivor的新生代的活对象直接通过分配担保机制进入老年代}
    \end{cases}
    $$
copying
  • 增量算法 $
    \begin{cases}
    \text{把内存分为多个空间, 使用多线程每次只回收部分空间, 下次回收在原有基础上继续}\\
    \text{使得跟踪式变为并发式而非独占式}\\
    \text{由于切换线程等开销, 会加大跟踪式的整体开销}
    \end{cases}
    $

  • 分代收集算法$
    \begin{cases}
    \text{堆分为新生代和老年代(根据对象存活周期划分, 不一定只有两代)}\\
    \begin{cases}
    \text{新生代存活率低, 选用复制算法}\\
    \text{老年代存活率高, 选用标记清理或标记整理算法}
    \end{cases}
    \end{cases}
    $

复制算法(Copying Algorithm)

  声明: 本人并不清楚这里young GC和minor GC有什么区别
  一般针对新生代采用复制算法。上述复制算法的实现中提到复制算法并不是简单地把空间分为两个区域, 而是分为eden和survivor空间, survivor又分为s0和s1两个子空间。

eden survivor

  新分配对象加入eden(若eden放不下, eden复制存活对象到from后, 再存放新对象) → eden要溢出时触发young GC → eden和from存活对象copy进to中(to中也放不下, 全部进入老年代)

  • 一个对象多次触发young GC依然存活进入老年代(触发一次还活着, "续一秒"直到阈值)
  • 新生代触发的GC叫minor GC
  • 除了新生代的eden和survivor以外, 还有老年代的Tenured和永久代的Permanent区域
  • 老年代也会进行young GC, 在Tenured要溢出时触发Full GC, Tenured一般采用CMS策略
  • 永久代包含类和对象的元数据, 也会被回收, 触发的GC叫major GC
  • major GC至少伴随一次young GC, 一般比young GC慢十倍以上
  • Eden:from:to = 8:1:1

  企业开发-Xmx = -Xms, 通常设置-Xmx为2048m, Resin服务器中配置如下:

<jvm-arg>-Xms2048m</jvm-arg>
<jvm-arg>-Xmx2048m</jvm-arg>
<jvm-arg>-Xmn512m</jvm-arg>
<jvm-arg>-XX:SurvivorRatio=8</jvm-arg>
<jvm-arg>-XX:MaxTenuringThreshold=15</jvm-arg>
  • 年轻代:年老代为1:3
  • -XX:MaxTenuringThreshold=15: 默认, 年轻代对象经过15次的复制后进入到年老代
  • -XX:PretenureSizeThreshold: 指定大对象以直接进入老年代

(Card)(Table)

  为了支持新生代的高频回收, 通过使用卡表数据结构加快新生代回收速度。

  • 比特位集合, 每个比特位表示老年代某区域是否持有新生代引用
  • 标记位1则持有引用, 0为一定不含有新生代引用
  • 新生代GC时只需扫描卡表位为1的老年代区域即可

垃圾收集器(Garbage Collector)


  • 新生代: Serial, ParNew, Parallel Scavenge, G1
  • 老年代: CMS(Concurrent Mark Sweep), Serial Old(MSC), Parallel Old, G1
  • 连线的两个收集器可配合工作

Stop The World

  编译代码时在每个方法循环结束或执行结束的点注入safepoint, 需要等待所有用户线程进入safepoint后暂停所有线程, 之后进行GC.

Serial Collector

  Serial是一个单线程收集器(串行收集器), 即只用一个CPU只用一个收集线程去完成GC.
  再者, 它进行GC时必须暂停其他所有工作线程, 直到它GC工作完成。

  • 最稳定, 效率高
  • 可能产生较长停顿
  • 复制算法
  • -XX:+UseSerialGC: 使用Serial GC

ParNew Collector

  Serial多线程版本, 其余特性与Serial一致。

  • 并行: 多条GC线程并行工作, 但用户线程仍被暂停等待
  • 复制算法

  参数控制:

  • -XX:+UseParNewGC: 使用ParNew GC
  • -XX:ParallelGCThreads: 限制线程数量

Parallel Scavenge

  Parallel Scavenge是一个新生代收集器, 使用多线程复制算法。CMS等收集器目标是尽可能缩短GC时用户线程停顿时间, 而Parallel Scavenge目标是达到一个可控的吞吐量(Throughout), 常称"吞吐量优先"收集器, 同时与ParNew重要区别是具备自适应调节策略

  • 吞吐量: 用户线程耗时与CPU总耗时比值。$
    \text{吞吐量} = {\text{运行用户代码时间} \over \text{运行用户代码时间} + \text{GC时间}}
    $
  • 响应时间: 短时间停顿适合用户交互场景, 提升用户体验
  • 高吞吐量: 高效利用CPU, 适合后台运算无过多交互场景

  参数控制:

  • -XX:+UseParallelGC: 使用Parallel Scavenge GC + Serial Old GC
  • -XX:MaxGCPauseMillis: 最大GC停顿时间(毫秒 ms), 牺牲吞吐量
  • -XX:GCTimeRatio: 吞吐量大小(0 ~ 100 整数), GC时间与总时间比率, 如设置19则${\text{GC时间} \over \text{总时间}} = {1 \over {1+19}} = 5\% $
  • -XX:+UseAdaptiveSizePolicy: 开启后不用手动设置新生代大小-Xmn, Eden与Survivor比例-XX:SurvivorRatio, 晋升老年带对象年龄-XX:PretenureSizeThreshold等细节参数, 自适应动态调整策略
  • -XX:GCTimeRatio-XX:MaxGCPauseMillis只设置其中一个就好

Serial Old Collector

  Serial老年代版本, 使用单线程标记整理算法
  主要用途:

  • Client: 虚机使用
  • Server: JDK1.5及其旧版本中与Parallel Scavenge搭配使用; CMS后备预案, 并发收集触发Concurrent Mode Failure时使用

Parallel Old Collector

  Parallel Scavenge老年代版本, 使用多线程标记整理算法。源于JDK1.5及其以前Parallel Scavenge只能和Serial Old搭配在多CPU下的糟糕表现, JDK1.6提供Parallel Old以获得"吞吐量优先"组合。

  • -XX:+UseParallelOldGC: 使用Parallel Scavenge GC + Parallel Old GC并行

  resin服务器配置如下:

<jvm-arg>-Xms2048m</jvm-arg>
<jvm-arg>-Xmx2048m</jvm-arg>
<jvm-arg>-Xmn512m</jvm-arg>
<jvm-arg>-Xss1m</jvm-arg>
<jvm-arg>-XX:PermSize=256M</jvm-arg>
<jvm-arg>-XX:MaxPermSize=256M</jvm-arg>
<jvm-arg>-XX:SurvivorRatio=8</jvm-arg>
<jvm-arg>-XX:MaxTenuringThreshold=15</jvm-arg>

<jvm-arg>-XX:+UseParallelOldGC</jvm-arg>
<jvm-arg>-XX:GCTimeRatio=19</jvm-arg>

<jvm-arg>-XX:+PrintGCDetails</jvm-arg>
<jvm-arg>-XX:+PrintGCTimeStamps</jvm-arg>

CMS Collector

  CMS(Concurrent Mark Sweep)收集器以最短回收停顿时间为目标, 与ParNew新生代收集器搭配, 适应互联网站或B/S系统的服务端, 强调服务响应速度。对待老年代的回收尽力做到能多concurrent就多concurrent.
  步骤: 初始标记(initial mark)并发标记(concurrent mark)重新标记(remark)并发清除(concurrent sweep)

  • 初始标记和重新标记仍需要噢~在这儿停顿!(STW, stop-the-world)
  • 并发清除和并发标记耗时最长, 期间用户线程并发执行
  • 初始标记: 只标记GC Roots直接关联的对象
  • 并发标记: GC Roots Tracing
  • 重新标记: 修正并发标记时用户线程继续运作使得一部分对象标记产生变动的标记记录
  • 消耗时间: $\text{初始标记} \lt \text{重新标记} \lt \text{并发标记}$

  • 优点: 并发收集低停顿
  • 缺点: $
    \begin{cases}
    \text{若老年代对象太多, STW会过于耗时}\\
    \text{CPU资源敏感, 默认回收线程} = {\text{CPU数量} + 3 \over 4}\\
    \text{无法处理浮动垃圾, Concurrent Mode Failure后可能导致一次Full GC}\\
    \text{产生大量内存空间碎片, 并发阶段会降低吞吐量}
    \end{cases}
    $

  参数控制:

  • -XX:+UseConcMarkSweepGC: 使用CMS GC
  • -XX:+UseCMSCompactAtFullCollection: 默认开启, Full GC后进行一次碎片整理, 独占线程变长停顿时间
  • -XX:+CMSFullGCsBeforeCompaction: 设置每多少次Full GC后进行一次碎片整理
  • -XX:+ParallelCMSThreads: 设置CMS线程数, 一般约等于可用CPU数, 默认$\text{CPU数量} + 3 \over 4$
  • -XX:CMSInitiatingOccupancyFraction: 指定当年老代空间满了多少后进行垃圾回收

G1 Collector

G1

  G1(Garbage-First)收集器面向服务端应用, 使用增量算法, 目的是替换JDK1.5的CMS收集器, 于JDK6u14有Experimental试用, JDK7u4移除实验性标记(又一文说要求7u14以上)

  与CMS相比有以下特点:

  • 标记整理算法: 不会产生空闲碎片。
  • 可预测停顿: 建立可预测的停顿时间模型, 在指定时间内回收部分价值最大的空间
  • 独立区域(Region): 堆分为多个大小相等区域, 新生代老年代不再物理隔离而是一部分Region集合
  • 筛选回收: 只回收部分region, 可控STW, 无需与用户线程抢CPU, CMS并发清理需要抢导致降低吞吐

  步骤: 初始标记 → 并发标记 → 最终标记 → 筛选回收

  • 并发标记: 此期间发生引用关系变化的对象会被记录在remember set logs中
  • 筛选回收: 通过跟踪各region垃圾堆积价值(回收量和回收时间), 在后台维护优先列表, 每次根据允许的收集时间优先回收价值最大的region, 即此阶段根据用户期望回收时间回收价值较大的对象

  适用:

  • 追求高响应: ParNew/CMS或G1
  • 追求高吞吐: Parallel Scavenge/Parallel Old, G1吞吐量无优势

Remember Set

  当引用对象被赋值, JVM发出write barrier暂时中断写操作, 检查是否老年代引用了新生代对象, 是则记录remember set, GC扫描时会从根集合 + remeber set向下扫描, 以避免老年代引用新生代时, GC需要扫描整个老年代来确认可达路径。
  G1中对每个region分配一个remember set, 引用对象被赋值时检查是否跨region引用, 跨则记录到当前region的remember set中用于之后扫描。
  并发标记期间发生引用变化的对象, 将被记录进remember set logs.在重新标记或最终标记阶段把该logs加入remember set中再trace节点。

Full GC

  2016年的文章认为ParNew/CMS和Parallel Scavenge/Parallel Old的组合用得最多。
  触发Full GC及其解决办法:

  • 老年代空间不足$
    \begin{cases}
    \text{少创建大对象大数组}\\
    \text{让对象在新生代中多苟一会儿, 尽量使其在minor GC中被回收}
    \end{cases}
    $
  • 方法区空间不足$
    \begin{cases}
    \text{增大方法区空间}\\
    \text{对方法区使用CMS}
    \end{cases}
    $
  • CMS GC时promotion failed(minor GC时新生代和老年代空间都不足)或concurrent mode failure$
    \begin{cases}
    \text{增大survivor空间和老年代空间}\\
    \text{调低-XX:CMSInitiatingOccupancyFraction}\\
    \text{-XX:CMSMaxAbortablePrecleanTime}=5(ms) \text{, 避免重新标记很久后才执行并发清理}
    \end{cases}
    $
  • 空间分配担保机制

空间分配担保机制

  Minor GC时, 若老年代最大可用连续空间大于新生代所有对象总空间, 则GC安全; 若不大于, 查看HandlePromotionFailure是否设置允许担保失败。若允许, 检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小, 若大于则尝试Minor GC; 若小于或不允许担保失败, 则需要进行Full GC.
  jdk6u24后, HandlePromotionFailure不再影响分配担保策略, 只要老年代连续空间大于新生代总空间或历次晋升平均大小则进行Minor GC, 否则Full GC.

JVM优化

  主要调节各代大小和选择collector.
  调节各代大小:

  • -Xmx: 一般-Xmx = -Xms防止堆内存频繁调整
  • -Xms
  • -Xmn: -Xmn = -Xmx/4-Xmx/3, 过小minor GC频繁, 过大老年代变小, 都会导致提前Full GC. 按照调大-Xmx → 调大-Xmn → 调大-XX:SurvivorRatio策略调节
  • -XX:SurvivorRatio: 默认8, 过大Eden变大Survivor变小, minor GC减少, minor GC存活可能直接进入老年代; 过小Eden变小Survivor变大, minor GC增多, 进入老年代对象可能减少
  • -XX:MaxTenuringThreshold: 默认15
  • -XX:PermSize: 一般-XX:PermSize = -XX:MaxPermSize, 实际开发中尽量不适用jsp而使用velocity等模版引擎技术, 不要引入无关jar(讲道理maven也可以裁剪)

  垃圾收集器:

  • Parallel Scavenge/Parallel Old: 关注吞吐, 用于密集CPU低交互场景, 也可用于JVM自动调优场景(-XX:+UseParallelOldGC-XX:GCTimeRatio-Xmx-XX:+UseAdaptiveSizePolicy). 使用-XX:+UseParallelOldGC指定
  • ParNew/CMS: 关注低STW, 用于高交互场景, -XX:+UseConcMarkSweepGC指定, -XX:CMSInitiatingOccupancyFraction调节老年代满多少百分比后GC
  • 企业级CPU下G1不能用时, ParNew/CMS是首选, Parallel用于JVM自动管理内存

参考(Reference)


  [01]  深入理解 Java 虚拟机 精华总结(面试), 刘金辉, 2016-05-21 17:58
  [02]  java finalize 方法总结、GC 执行 finalize 的过程, Smina 俊, 2017-07-16 01:29
  [03]  Why Not to Use finalize() Method in Java, Lokesh Gupta, 2012-10-31 11:32
  [04]  CMS 垃圾收集器介绍, Mark__Zeng, 2015-09-26 13:20
  [05]  GC 之一 --GC 的算法分析、垃圾收集器、内存分配策略介绍, duanxz, 2016-03-01 11:16
  [06]  对象的强、软、弱和虚引用, duanxz, 2015-04-18 18:38
  [07]  JVM 中 G1 垃圾回收器详细解析, stackvoid, 2015-03-16
  [08]  计算机体系 – 垃圾收集器, ImportNew, 2018-02-23
  [09]  第五章 JVM 垃圾收集器(1), 赵计刚, 2016-02-05 22:15

To be Continued...

]]>
0 https://www.windawings.com/java/jvm.html#comments https://www.windawings.com/feed/java/jvm.html
Prezi Crack https://www.windawings.com/reverse/prezi-crack.html https://www.windawings.com/reverse/prezi-crack.html Fri, 09 Mar 2018 12:36:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

前言(Preface)

  Prezi是一家美国软件服务商, 他们设计了一款让用户在虚拟画布上创作的"故事情节描述工具"式的云端展示软件, 并借助它向第三方表述个体的想法。这一软件(也就是Prezi)采用了自由缩放界面, 允许用户放大或缩小他们的演示文档, 并且通过建立在Z轴上的伪3D和视差3D任意展现和穿梭于不同信息之间。该公司由Adam Somlai-Fischer, Peter Halacsy以及Peter Arvai几位于2009年正式合创。

介绍(Introduction)

  介绍内容摘自百度百科官网产品介绍

  Prezi 是个云端的演示文稿制作软件,使用者既可以在 Prezi 网站上在线创建编辑,也可以在客户端(Windows、Mac、ipad、iphone)上离线编辑制作。Prezi 采用故事板(storyboard)格式让演示者可以缩放图片,通过快捷的动画演示关键点。Zooming User Interface(缩放式用户界面,简称 Zooming UI)的特点是界面可缩放,zoom out 就能纵观全局,zoom in 则可以明察细节。实现了由整体到局部的 Mindmap 的开放性思维方式。除了平移和缩放,Prezi 还支持图片、视频、PDF 等各种媒体素材的嵌入,可以多人在线编辑,生成的演示文稿既可以在本地观看,也可以上传到服务器或嵌入网页在线查看。

创造(Create)

  无论个人还是团队, 都可以很轻松得构建出精美的展示。

创建(Creation)

creation
  • 提供简单直观的界面, 在线时可在prezi上创建与协同文档, 离线时可使用Desktop版本继续工作(*要求Plus以上许可证)

个性化(Customization)

customization
  • 可选择设计模板(官网上可通过Explorer寻找到很多模板并copy到自己的prezi云中)
  • 可导入自己的媒体(图片, 视频, PDF)

图表(Chart)

chart
  • 可通过"zoom in"等动画效果展示数据背后的精彩故事, 可通过文本, 图像或者视频创建绚丽的图表

协同(Collaboration)

collaboration
  • 基于安全云下的协同模式, 意味着你在大陆另一端也可以在同一个页面上协同工作
  • 在展示页面中合适的位置放置的反馈(comment, *要求Teams许可证)

展示(Presentation)

  提供视觉上令人震撼的互动体验。

移动端(Mobile)

mobile
  • 在任何地方登陆你的帐户
  • Prezi Next提供多设备, 流畅高清的展示
  • 可使用你的手机或平板远程操控你正在做的展示

桌面端(Desktop)

desktop
  • 桌面端可在离线的状态下, 编辑完善你的故事展示

备注(Comment)

presenter view
  • 可以设置只有展示者自己才能看到的笔记, 记号或其他提示, 避免丢掉展示的节奏, 让展示者cool, cooler, coolest

分析(Analyze)

analyze
  • 在展示完成后, Analyze功能将给你的展示提供全方位的分析, 包括但不限于展示的浏览次数, 浏览中访客实时浏览进度以及追踪分享。

价位(Price)

basic license
What you get Basic Plus
Unlimited presentations
Privacy control
Revocable share links 
Offline access 
Portable presentations 
Presenter view 
Voice-over 
Export to PDF
Video upload storage 
Prezi Analytics
Advanced online training 
Phone support
individual license
What you get Standard Plus Premium
Unlimited presentations
Privacy control
Revocable share links 
Offline access 
Portable presentations 
Presenter view 
Voice-over 
Export to PDF
Video upload storage 
Prezi Analytics
Advanced online training 
Phone support
edu license
What you get EDU Standard EDU Plus EDU Teams
Unlimited presentations
Privacy control
Revocable share links 
Offline access 
Portable presentations 
Presenter view 
Voice-over 
Export to PDF
Video upload storage 
Prezi Analytics
Advanced online training 
Phone support

下载(Download)

  安装程序可在官网登陆账户后, 进入dashboard, 左下角即可根据提示下载桌面端。

Product Link
Prezi Next 1.7.1 https://pan.baidu.com/s/1jBflYX7z6BTkmOPVhFNuOA
Prezi Classic 6.21.0 https://pan.baidu.com/s/16Z7pMQ62udhwpKlhaIWySA
Prezi Crack     压缩包密码: windawings

破解补充(Crack Support)

目录 说明
Crack 对应有Classic和Next版本的破解补丁, 找到安装目录覆盖即可, Next版本位置在C:\Users\<Windows用户名>\AppData\Local\PreziPitch_x64\app-1.7.1
Lang 简体中文字体支持, 可找到Prezi字体资源库后对里面的keg文件进行替换使用
Tutorial Prezi Classic相关教程, 没有Prezi Next的
Help 求助, 请有Prezi使用经验的热心人前往Github链接提供界面的中文翻译支持, 若不会使用github可下载resx后缀的资源文件翻译完善后发往windawings@foxmail.com邮箱
]]>
7 https://www.windawings.com/reverse/prezi-crack.html#comments https://www.windawings.com/feed/reverse/prezi-crack.html
Apache Spot https://www.windawings.com/research/apache-spot.html https://www.windawings.com/research/apache-spot.html Sun, 11 Feb 2018 18:48:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

引言(Preface)


  Apache Spot由Intel和Cloudera向Apache基金会贡献, 其前身为Intel在Cloudera平台上开发的开源专案Open Network Insight(ONI), 采用了Open Data Model(ODM)规格化安全资料。Spot基于高效的流量与数据包分析和机器学习, 提供识别云环境下的潜在安全威胁和未知的网络攻击的能力。考虑其使用规格化的安全数据集, 未来具有开源共享安全数据集提高机器识别能力的潜质。
  目前Spot支持NetFlow, sFlow, DNS和Proxy的网络流量分析, 以HDFS和Hive提供存储, 由Spark提供计算, 基于LDA算法提供无监督机器学习(unsupervised)以及Jupyter提供图形化支持。
  Spot尚处于孵化阶段(Incubate), 由Centrify, Cloudera, Cybraics, Endgame, Intel, Jask, Streamsets, Webroot等厂商投入开发与贡献。

介绍(Introduction)


优势(Advantages)

  Apache Spot基于以上四种网络流量进行分析, 其实是三种, 它把网站流量划分了内外网两种, 另外两个分为代理流量和DNS. 值得一提的是, 官网上表示需要一天后才初具功能, 也就是需要初始的训练集来进行机器学习。

advantages
  • Telemetry: 计量数据采集, 这里指Flows, DNS和Proxy的抓包和日志记录
  • Perimeter Flows: 周边流量, 即外网流量(相对于内网流量)

机制(Works)

  Apache Spot通过使用机器学习来识别流量特征, 并标识出各个流量簇的唯一行为。其主要对网络流量采用富文集, 噪声过滤, 白名单和启发式算法的方式进行处理,从而生成可能造成安全威胁的名单。

advantages 2

并行框架(Parallel Ingest Framework)

  目前其采用kafka和flume对流量数据批量加载到HDFS和Hive中, 数据由多种格式存储以便于搜索, 机器学习, 转移到法律执行过程模型或者导入其他系统。

Kafka

kafka logo

  kafka分布式发布订阅消息系统(基于zookeeper, scala和java编写, 原LinkedIn开发), 可通过Hadoop的并行加载机制统一线上和离线消息处理, kafka可把消息种子(feed)分成多个主题(topic), 比如spot-ingest划分出来的flow, dns和proxy三个主题. 每个消息(record)由一个key, 一个value和时间戳构成。

kafka diagram
生产者(Producer)

  生产者可发布(push)消息到(append, 顺序写磁盘, 经验证, 顺序写磁盘效率比随机写内存还要高, 这是Kafka高吞吐率的一个很重要的保证)一个或多个主题中的分区(partition)里(即发布到broker集群里该消息主题分区所在的leader中(通过查询zookeeper/brokers/..../state节点找到leader), 分区选择(即消息路由)由key进行hash运算, 若指定了分区则直接路由, 若key和分区都未指定, 则使用round-robin). producer.type默认为sync同步push消息, 为async异步时, 生产者可以以batch形式push消息, 提高broker性能的同时消息失去可靠性。

消费者(Consumer)

  消费者可订阅一个或多个主题, 从broker(一个kafka节点为一个broker, 一个或多个broker组成一个kafka集群)拉取(pull)已发布的消息, 一条消息只有一个消费者处理。可创建消费者组(consumer group)作为逻辑上的订阅者, 组内多个消费者可拓展性能和容错。组内消费者共享Group ID, 与主题一一对应; 主题有多个分区, 一个分区可由组内的一个消费者消费, 分区是消费的最小单位; 一般来说, 分区数可大于等于组内消费者数, 多的消费者永远也无法分配到可供消费的分区, 但多个分区可由同一个消费者消费。当需求多线程消费者实例时, 需要生产者也以批量模式发布消息到broker的随机分区中(若分区不够随机, 其他线程的消费者也无法消费到自己以外的分区); 消费者会提交自己的消费偏移量(offset), 0.8版本偏移量由broker管理(提交到zookeeper节点/consumers//offsets//), 0.9版本后由消费者组管理(提交到__consumeroffsets topic摆脱对zookeeper的依赖, offsets.topic.num.partitions设置其分区数, 默认50, 分区位置由Group Id的哈希值对其求余)。

协调者(Coordinator)

  0.8版本的协调者借助zookeeper对消费者组进行管理, 协调者监听zk节点变化, 消费者自己决定分区分配方案并抢占注册; 0.9以后版本则每个消费者组分配一个协调者, 消费者组的第一个消费者启动后向Kafka Server确定其组的协调者, 之后则与协调者进行协调通信。协调者监听主题和消费者组判断是否做rebalance(一组协议, 规定消费者组基于range, round-robin或一个待开发的新分配器分配分区(分配在消费者组 leader完成), 有三种触发条件: 组成员变更; 订阅主题数变更; 订阅主题分区数变更。有五个协议处理协调者问题: Heartbeat, LeaveGroup, JoinGroup: 消费者 → 协调者; SyncGroup: 消费者组 leader → 协调者 → 消费者组 memeber; DescribeGroup:显示组的所有信息, 包括成员信息, 协议名称, 分配方案, 订阅信息等)。一个消费者组对应一个协调者, 分区leader所在broker即为被选定的协调者, 每次rebalance产生新的rebalance generation(递增整数)。消费者组有五种状态: Dead(组内没有任何成员的最终状态, 组的元数据被协调者移除, 请求结果:UNKNOWN_MEMBER_ID); Empty(组内无成员但是位移信息还没有过期, 能响应JoinGroup); PreparingRebalance(组准备开启新的rebalance, 等待成员加入); AwaitingSync(等待消费者组 leader将分配方案传给各成员); Stable(rebalance完成, 可开始进行消费)。

分区(Partition)

  每个主题下存在多个分区, 分区是一个不可变的顺序消费队列, 分区中存在一个唯一表示的偏移量, 该偏移量由唯一消费者控制, 而各个消费者间的偏移量互不影响。分区使得一个主题可分布到broker的多个服务器中, 同时自身可拷贝多份作为备份容错。既然涉及到备份容错, 则分区由leader和0...N个follower存储, leader处理该分区所有读写请求, follower备份数据, 从而使broker通过分区容错(主题配置复制因子(replication facto)为N, 即可允许N - 1个服务器宕机而不丢失已提交的消息)。

分段(Segment)

  分区由多个分段组成, 分段由.index索引文件和.log数据文件组成。.index记录消息序号和物理地址偏移量, 第一个消息序号为1, 偏移量为0, 是该分区全局的第一个消息, .index文件名由其上一个分段的.index文件的最后一个消息的物理偏移量确定, 这样消费者使用偏移量(.index文件名数值+消息序号)即可通过线性复杂度查找, 根据对应分段的.index文件, 再通过.index的索引位置找到对应数据。(消费者通过3712的偏移量, 找到<$i$>.index ($3712 \le i \le$ <下一个.index文件名 >), 找到里面序号为<$i$ - 3712>的索引, 获得索引值, 即物理偏移量, 即可根据物理偏移量获取到此消息)

AR ISR OSR

$$\begin{equation}\begin{split}
\text{AR(assigned replicas所有副本)} &= \text{ISR(in-sync replicas副本同步队列, 由分区leader维护的列表)}\\
&  +\text{ OSR(outof-sync replicas超时副本同步队列,}\\
&    \text{由超过replica.lag.time.max.ms和新加入的follower加入)}
\end{split}\end{equation}$$

LEO HW

  取一个分区对应ISR中最小的LEO(log end offset, 分区最后一个消息的偏移量)作为HW(hight water mark高水位, leader和follower都有自己的HW), 消费者能获取到的最新消息即为高水位(HW)所在的消息, 内部broker的读取请求则没有高水位限制。

Controller

  kafka集群中的一个broker将会被选举为Controller负责分区管理和副本状态管理, zookeeper中/brokers/topics//partitions//state对ISR进行管理, 该节点由Controller和Leader共同维护。 分区改变leader, 则Controller发起LeaderAndIsrRequest通知所有replicas; ISR变化, 则由leader更新该节点的ISR信息。当ISR中副本的LEO不一致时, 如果此时leader挂掉, 选举新的leader时并不是按照LEO的高低进行选举而是按照ISR中的顺序选举。

Acks Min-Insync-Relicas Unclean-Leader-Election-Enable

$$\begin{cases}
request.required.acks = -1\text{时和min.insync.replicas配合使用, 表示生产者需要等待ISR中所有follower的ack才确认消息已被接收;}\\
request.required.acks = 0\text{时, 生产者不等待ack确认; 为1时只等待leader的ack(leader宕机数据丢失);}\\
\begin{cases}
request.required.acks = -1\\
min.insync.replicas = 2\\
unclean.leader.election.enable = false\\
AR = 3
\end{cases}\text{时, 则ISR中任意一个broker宕机, 则消息只能读取, 都宕机, 则该replicas(即该分区)失去可用性。}
\end{cases}$$

Delivery Guarantee

  a) At most once: 消息可能会丢,但绝不会重复传输;
  b) At least once: 消息绝不会丢,但可能会重复传输 (目前kafka采用的生产者发布消息的形式, 引起消息去重问题, 要求消息具备幂等性, 设置GUID(globally unique identifier)标记消息, 在客户端中做去重则要求集中式缓存(redis, memcached...));
  c) Exactly once: 每条消息肯定会被传输一次且仅传输一次。

Flume

flume logo

  flume可分布式日志收集系统(java1.6+编写, 由Cloudera开发, 2009年捐赠给Apache基金会, Hadoop相关组件)将不同源((fan)(in))的海量日志进行收集,提供对数据的简单处理, 并写到不同目的地((fan)(out))。flume由原来的OG版本到现在NG版本, 进行了架构重构,两个版本互不兼容。经过架构重构后, NG版本演变为了一个轻量工具, 适应各种方式收集日志, 支持failover和负载均衡。

flume diagram
flume multi-agent diagram
事件(Event)

  事件是flume内部数据传输的基本单元, 也是事务的基本单元。event由可选headers(key-value键值对, 不同的source生成不同的header, 允许修改event添加header, 需要拦截器和选择器机制的支持从而对event进行路由)和body(字节数组, 事件的实际内容)组成(只支持如下格式:[{header:{"key": "value"}, body:"context"},])。事件由client产生, 经由client → agent{source(netcat, http...) → channel(memory, jdbc, file...) → sink(hdfs, hbase, null...)} → destination的路线到达目的地, 在送达sink前需要在channel缓存event, 数据达到sink后才会删除channel中的缓存,而在event从源到目的的迁移的抽象成为(flow)

数据源(Source)
flume multi-source diagram

  source从client接收数据, 并把数据以event的格式临时存放在一个或多个channel中, 直到event被sink成功发送到destination后, channel才将临时数据删除。在Spooling Directory Source中, 被发送过的数据被标记.COMPLETED(后缀名, 可自定义). source支持的类型如下:

类型 说明 实现类
Avro 支持 Avro协议(实际上是Avro RPC), AvroLegacySource(0.9.x OG版本兼容), 内置支持 AvroSource, AvroLegacySource
Thrift 支持Thrift 协议, ThriftLegacySource(0.9.x OG版本兼容), 内置支持 ThriftLegacySource
Exec 基于Unix Command在标准输出上生产数据 ExecSource
JMS 从JMS系统(消息, 主题)中读取数据, ActiveMQ已测
Spooling Directory 监控指定目录内数据变更
Netcat 监听端口(官网上只写了tcp type), 将流经端口的每一个文本行数据作为event输入 NetcatSource
Twitter 1% firehose 通过API持续下载Twitter数据(experimental)
Sequence Generator 序列生成器数据源, 生产序列数据 SequenceGeneratorSource
Syslog 读取syslog数据产生Event, 支持UDP和TCP两种协议 SyslogTcpSource, SyslogUDPSource
HTTP 基于HTTP POST或GET方式的数据源, 支持 JSON和BLOB 表示形式
ScribeSource ScribeSource
StressSource 主要用于测试, 由连续的具有相同payload的Event构成, 不用于生产环境 StressSource
(custom type as FQCN) 自定义Source (custom FQCN)
管道(Channel)

  Channel是一个临时的存储容器, 它把从source接收到的数据以event格式存储起来, 直到event被sink消费后才把临时数据删除。Channel可和多个source和sink连接, 成为source和sink的桥梁, 提供数据收发的一致性保证。常见类型为Memory, JDBC和File. Memory提供高速吞吐但无法保证数据的完整性, File通过持久化数据至磁盘保证数据的完整性。

类型 说明 实现类
Memory 存储在内存中, 高速但非持久化的管道 MemoryChannel
File 可读写、映射和操作文件的管道, 存储Event在磁盘中 FileChannel
JDBC 基于JDBC的可持久化的管道, 内置支持Derby JDBCChannel
Recoverable Memory 基于本地文件存储系统提供持久化的管道(据说建议用FileChannel代替) RecoverableMemoryChannel
Spillable Memory 存储在内存和磁盘中, 内存队列满则持久化到磁盘上(Experimental)
PseudoTxnMemory 测试用Channel, 不用于生产环境 PseudoTxnMemoryChannel
(custom type as FQCN) 自定义Channel (custom FQCN)
水槽(Sink)
flume multi-sink diagram

  Sink从channel中读取并删除event, 将event传递到Hbase、HDFS或flow pipeline的下一个agent等。Sink类型如下:

类型 说明 实现类
HDFS 写入所有Event到HDFS, 支持Rolling, Bucketing, HDFS-200 Append等 HDFSEventSink
Hbase 从Channel读取Event并写入Hbase(从Async的实现类来看似乎可以异步) HBaseSink, AsyncHBaseSink
Logger 基于配置好的日志子系统以info日志级别记录events(默认log4j) LoggerSink
Avro 对所有Event使用预置的Avro协议发送到配置的RPC端口,当同配置Avro Source时, 形成分层的集合 AvroSink
File Roll 存储在内存和磁盘中, 内存队列满则持久化到磁盘上(Experimental) RollingFileSink 
IRC event在IRC回放 IRCSink
Null 写入/dev/null, 即丢弃所有Event NullSink
Morphline Solr 写入Solr搜索服务器(Clustered)
ElasticSearch 写入Elastic Search搜索服务器(Clustered) ElasticSearchSink
Thrift 写入Thrift ThriftSink
Kite Dataset 写入到Kite Dataset(Experimental)
(custom type as FQCN) 自定义Sink (custom FQCN)
拦截器(Interceptor)
flume architecture
来源于 http://blog.csdn.net/ty_laurel/article/details/54585726

  拦截器用于source与channel之间对event数据进行处理, 主要对event的header进行CUD, 拦截器有如下类型:

类型 说明 实现类
Host 加入Agent主机名或IP地址 HostInterceptor$Builder
Timestamp 加入当前时间戳(ms) TimestampInterceptor$Builder
Static 加入一组静态的键值对(key-value) StaticInterceptor$Builder
Regex Filter 对Event Body中做正则提取出需要的匹配项 RegexFilteringInterceptor$Builder
UUID 加入UUID
Morphline Cloudera开源ETL(ElasticSearch, Logstash, Kibana)框架(目前由Kite主导开发), 富配置转化链, Grok正则解析
(custom type as FQCN) 自定义Interceptor (custom FQCN)

  关于Morphline框架在Flume中的作用, 由以下两个图展示:

morphline
来源于 https://allthingshadoop.com/2014/05
flume and morphline architecture
来源于 http://vinoyang.com/2015/11/20/build-log-sys-with-flume-and-morphline
管道选择器(Channel Selector)
类型 说明 实现类
Replicating 把从Source接收到的所有Event发往所有的Channel(default) ReplicatingChannelSelector
multiplexing 根据Event Header中的Key选择管道 MultiplexingChannelSelector
(custom type as FQCN) 自定义Channel Selector (custom FQCN)
水槽处理器(Sink Processor)

  Sink Processtor可用于激活sinks中特定sink用于负载均衡(load balance)

类型 说明 实现类
Default 单独的Sink DefaultSinkProcessor
Failover 维护Sinks优先级列表, 保证只要有一个Sink即可处理Event(故障转移)(若没有设置优先级则按声明顺序) FailoverSinkProcessor
Load Balance 维护Active Sinks索引列表, 选择机制有Round Robin(default)和Random, 也可以自定义继承AbstractSinkSelector LoadBalancingSinkProcessor
(custom type as FQCN) 自定义Sink Processor (custom FQCN)
事件序列器(Event Serializer)

  Event需要对其Body进行序列化, 主要有以下类型: EventSerializer$Builder(Text, Avro Event), EventSerializer(SimpleHbaseEventSerializer, SimpleAsyncHbaseEventSerializer, RegexHbaseEventSerializer), HbaseEventSerializer(HbaseSink的自定义序列化), AsyncHbaseEventSerializer(AsyncHbaseSink的自定义序列化), EventSerializer$Builder(除了HbaseSink和AsyncHbaseSink外其他Sink的自定义序列化)。

HDFS

hadoop logo

  Hadoop分布式文件系统(distributed fileSystem)为Hadoop(java, 前yahoo主导)核心子项目, 基于流式数据访问模式处理超大文件需求开发, 适用于一次写入多次读取的应用场景, 不适合低延迟数据访问需求, 小文件存储, 并发写入(不允许多线程同时写入同一文件)和随机修改(仅支持数据追加(append))。主要有Client(调用HDFS API, 从NameNode获取文件元数据与DataNode交互数据读写), NameNode(元数据节点, 管理元数据, 分配数据存储节点), DataNode(数据存储节点, 负责数据存储读写与冗余备份)。此项目主要采用Spark计算和Hive, Impala的查询, MapReduce使用不明显, 暂时不写MapReduce, ResourceManager(Yarn), TaskTracker以及JobTrackter计算任务框架。

  • RPC通信
  • 可构建在廉价机器上
  • 提供统一抽象目录树
  • 高容错(默认3副本, 由机架感知(rack-aware)副本在DN本地存一份, 同机架存一份, 不同机架存一份, 每个DN最多存一个副本, 每个机架最多存两个副本)
  • 2.x版本后提供高可用性, 对NN配置secondary NN, 根据checkpoint机制合并NN上的editslog(记录元数据操作)到fsimage(磁盘元数据镜像, NN宕机时快速恢复元数据用)
  • 分块(block)存储(2.x版本默认128M, 此前为64M), 有DN承担, DN发送心跳告知NN存活(alive)状态, 也汇报自身block信息(自身数据校验不通过block的不汇报)
  • NN启动先进入安全模式, 当NN确认一定百分比的block安全(block副本数达到最小值为副本安全)的30s后, NN退出安全模式可进行block备份操作
  • 数据访问模式: client(可配置数据副本数与block大小) → NN(检查, 取数据元数据和block分布(由距离client远近排序), 返回FSDataOutputStream, FSDataInputStream 对象), client(通过NN返回的对象与DN交互) → DN(读写数据)
hdfs architecture

Hive

hive logo

  Hive(java, Facebook)是构建在Hadoop上的数据仓库框架, 使用HQL(类SQL语法(不支持主键外键, 可创建索引), 转化SQL为分布式作业, 支持MapReduce(最稳), Spark或Tez)对数据执行CRUD, 提供流式API, 适用于传统数据仓库业务, 不适用于低延迟的交互访问。Hive几乎是Hadoop上的SQL标准, 适合离线ETL与大数据离线Ad-hoc查询, 以及特大规模数据集合精准结果的查询。对于需要交互式的Ad-hoc查询方案, 通常选择Impala, Presto等。

hive architecture
组件(Compenent)
hive architecture 2
  • UI: 用户向系统提交查询和其他操作的接口, 目前有Cli, Hive Web Server提供
  • Driver: 处理查询, 实现会话交互, 提供基于JDBC/ODBC接口上的执行与获取的API
  • MetaStore: 存储hive元数据(表格(table)元数据, 分区(partition)元数据, (bucket)元数据...)到关系型数据库(支持MySQL, Derby...)
  • Compiler: 基于语义分析(semantic analyzer)解析查询块和查询语句, 最终通过metastore中查询表和分区的元数据, 生成可供MapReduce, Spark或Tez执行的执行计划。HQL ———→(Antlr) 抽象语法树(AST) ——————→(semantic analyzer) 最简查询块(QB)(from子句递归生成一个QB) ——————→(logical plan) OP Tree(有向无环图) ——————→(logical optimizer) OP Tree(谓词下推(predicate push down), 分区剪裁(partition prunner), 关联排序(join reorder)) ——————→(physical plan) Task Tree(遇到分发则切一刀生成MapReduce作业, 如group by, join, distributed by, distinct...etc, 分出多个子图, 每个子图构成一个节点, 节点连成执行图) ——————→(physical optimizer) Task Tree(基于输入选择路径, 增加备份作业etc)(该过程可通过explain + statement查看)
  • ExecutionEngine: 通过管理不同阶段性计划中的依赖, 在对应的系统中(HDFS)执行Compiler生成的基于阶段性DAG的执行计划, 新版本有多种实现(MapReduce, Spark或Tez)
hive explain
来源于 http://www.codedata.cn/hacknews/146917894602293594
数据模型(Data Model)
  • Table: 类似传统关系型数据库的表, 能进行过滤, 映射, Join和Union. Table中的数据全部存储在HDFS的一个目录下。支持(external)(table), 即table可由HDFS中已存在的文件或目录创建。Column, Row等概念与传统关系型数据库类似
  • Partition: 分区基于HDFS子目录实现, 每个Table可根据partition key决定数据的存储方式。分区是表中部分列的集合。分区可以减少每次遍历的数据量改善性能。但HDFS不支持大量的子目录, 所以需要考虑分区数量避免过多子目录创建。有静态分区和动态分区: 静态分区在创建表时使用partitioned by定义(创建时需要指定column和数据类型, 加载数据(load data)时需要指定具体为该column的哪个值); 动态分区由设置set hive.exec.dynamic.partition=true;加载数据时不需要指定column的值, hive.exec.dynamic.partition.mode默认值为strick, 即不允许所有分区都是动态的, 如果需要父目录及其子目录全部为动态分区, 需要指定该参数为nostrick
  • Bucket(or Cluster): 每个分区可进一步被分桶, 分桶通过对指定column进行哈希实现(hash结果对桶数求余, 保证每个bucket都有数据), 在数据量足够大时, 分桶比分区有更高的查询效率。需要设置hive.enforce.bucketiong=true;使用关键字clustered by指定column和分桶数
hive data model
来源于 http://www.codedata.cn/hacknews/146917894602293594
数据类型(Data Type)
类型 支持 说明
TinyInt 1-byte signed integer, -128 ~ 127 后缀Y
SmallInt 2-byte signed integer, -32,768 ~ 32,767 后缀S
Int/Integer(默认) 4-byte signed integer, -2147483648 ~ 2147483647
BigInt 8-byte signed integer, -9223372036854775808 ~ 9223372036854775807 后缀L, 大于BIGINT的值需要使用BD后缀和Decimal(38,0)处理
Float 4-byte single precision floating point number
Double 8-byte double precision floating point number
Decimal 在Hive 0.11.0介绍 (HIVE-2693), 在Hive 0.13.0改进 (HIVE-3976)
TimeStamp Hive 0.8.0及其以上支持 支持传统的 UNIX 时间戳和可选的纳秒精度
Ddate Hive 0.12.0及其以上支持
Interval Hive 1.2.0及其以上支持
String 可用单双引号表示, 以C风格转义
VarChar 1 ~ 65535, 超过varchar(length)中length的长度的字串将被截断, 尾部空格影响比较结果
Char 1~255, 固定长度, 字串比指定长度短的部分由空格代替, 尾部空格不影响比较结果
Boolean true or false
Binary Hive 0.8.0及其以上支持
Array 由相同类型元素组成, 下标从0开始
Map 键值对(key-value)
Struct 可包含不同类型元素, dot(.)符号访问语法
Union Hive 0.7.0及其以上支持, 可综合以上数据类型 据说还没有完全支持,官方建议只用于查看

  复杂类型的使用方法:

create table employees (
    name string,
    salary float,
    subordinates array<string>,
    deductions map<string, float>,
    address struct<street:string, city:string, state:string, zip:int>
) partitioned by (country string, state string);
优化(Optimization)

  Hive中最重要的部分是Group By和Join, MapReduce就是Group By或者Join的过程。

  • Group By: 可在执行SQL前设置hive.map.aggr = true, 使Map端发往Reduce端前部分聚合减少数据量; 也可以设置hive.groupby.skewindata = true把一个MR job转化为两个, 第一道MR job输出结果随机分发Reduce端, 每个Reduce端按Group By的key部分聚合, 第二道MR job根据预处理结果按Group By的key分布到Reduce端完成最终聚合。二者原理都是通过部分聚合减少Reduce端的数据量, 而聚合的有效性和效率通常与UDAF(user-defined aggregation funcation)有关, 且只对代数聚合函数(count, sum...)有效, 对整体聚合函数(avg, mean...)无效
  • Join: 应该尽可能把分布均匀的表放在左边, 把倾斜表放在右边; 通过使用MAPJOIN(<小表名>)把小表打成一个哈希表序列化文件的压缩包, 通过分布式缓存均匀分发到各个job执行节点上, 然后在节点解压在内存中完成关联(此处没有用到Reduce, 也不存在数据倾斜), 默认情况下小表 $\le$ 25M, MAPJOIN通常不会手动设置, 而是通过Hive中的physical optimizer把join优化为auto map join, 通过给Task设置一个conditional task把任务划分为$\le$ 25M和$\gt$ 25M, 新版Hive中默认hive.auto.convert.join = true

Impala

impala logo

  Impala(c++)在Cloudera受Google dremel启发下开发的实时SQL查询引擎(分布式大规模并行处理(MPP)数据库引擎), 功能上与shark(依赖Hive)和Drill(apache)类似。它脱离了Hive中MR批处理的缓慢, 采用与商用并行关系数据库类似的由planner, coordinator, exec engine组成的分布式查询引擎降低延迟。

impala architecture

Impala特性:

  • 基于内存, 对内存依赖大
  • 不再转化为MR
  • C++编写, 运行时代码生成LLVM IR(low level virtual machine intermediate representation)提高效率
  • 完全依赖于Hive, 直接使用Hive的元数据, 兼容HQL, 具有数据仓库特性, 可对Hive数据进行查询, 稳定性低于Hive
  • 支持data local, 列式存储, JDBC/ODBC查询以及SQL92标准, 拥有自己的解析器和优化器
  • Parquet列式存储
Impala Daemon

  Impala Daemon是一个在集群的每个DN上运行的守护进程(impalad), 主要接收client, hue, jdbc/odbc请求, 执行query并返回给协调节点, 负责与statestore通信汇报节点状态。在2.9+版本中, 可以分配集群中coordinator和exec engine角色给不同主机从而提高高并发负载的可伸缩性。impalad包含了planner, coordinator, exec engine, 一般impalad与DN在同一节点(data local)。当某个分发执行的impalad失败时, 整个计划任务都返回失败, 不过再次提交一次查询也没有多少消耗。

  • Query Planner: 解析SQL为执行计划
  • Query Coordinator: 指定查询的主节点(某个impalad), 通知其他节点主节点的信息, 主节点待其他节点查询结果返回给它后, 主节点再返回给中心协调节点
  • Query Exec Engine: 做查询工作

  Impalad的查询执行分为frontend(java, 以JNI嵌入impalad, 生成查询计划)和backend(c++, 执行查询)。Frontend先生成单机查询计划(与关系型数据库执行计划相同, 查询优化方法类似), 后生成分布式查询计划(减少数据移动, 数据与计算尽量放一起)。

impala query

  上图为三张表join后做聚合再排序取topN的例子。Impala查询优化器支持代价模型(以表和分区为基数, 每列distinct值个数做统计估算执行计划代价, 从而生成较优执行计划)。可以看到上图左侧则为frontend生成的单击查询计划, 进而转换为6个segment(彩色无边框圆角矩形背景, 每个segment可由单个主机独立执行, 为计划子树)的分布式查询计划。Impala支持表广播(把join中一个表广播到相关节点, 如图t3)和哈希重分布(根据join字段哈希值重新分布两张表数据, 如图t1, t2)两种分布式join方式。分布式查询计划中的聚合函数先对本地数据进行分组聚合(如图Pre-Agg)降低数据量和数据重分布, 再把上一步的结果在汇总聚合(如图MergeAgg)计算出最终结果, topN计划过程同理。BackEnd从frontend接收plan segment执行, 执行性能优化方面, 有向量执行(getNext处理一批数据, 多个操作符做pipeline), LLVM IR, IO本地化以及parquet列式存储。

Impala Statestore

  Impala Statestore负责收集各个impalad进程的资源信息, 各节点健康情况以及同步节点信息。只需要在一个主机上运行这样的一个进程即可(statestored), 如果某个impalad因为硬件故障, 网络错误, 软件问题或其他原因脱机, statestore会通知其他impalad避免向不可访问节点发出查询请求。statestore在impala集群中并不是一个关键进程, 如果statestore未运行或无法访问, impalad照常继续运行并分配工作, 相对的集群会缺少健壮性。

Impala Catalog

  Impala Catalog(Impala 1.2+)把Impala表的元数据分法到各个impalad中, 实现DDL。当impalad节点插入或查询时, impalad把自己的操作结果通知statestore, 之后statestore通知catalogd更新元数据信息。所以一般把catalogd和statestored放在同一个主机上运行, 且该主机不再运行impalad提供查询服务, 避免集群管理出现问题。

Impala文件格式
类型 格式 压缩 Create Insert
Parquet Structured Snappy
GZIP
Text Unstructured LZO
如果建表时没有指定存储类型,默认采用未压缩的 text,字段由 ASCII 编码的 0x01 字符串分割

如果使用了 LZO 压缩,则只能通过 Hive 建表和插入数据
Avro Structured Snappy
GZIP
Deflate
BZIP2

Impala 1.4.0+支持,之前的版本只能通过 Hive 来建表。

只能通过 LOAD DATA 的方式将已经转换好格式的数据加载进去,或者使用 Hive 来插入数据
RCFile Structured Snappy
GZIP
Deflate
BZIP2

只能通过 LOAD DATA 的方式将已经转换好格式的数据加载进去,或者使用 Hive 来插入数据
SequenceFile Structured Snappy
GZIP
Deflate
BZIP2

只能通过 LOAD DATA 的方式将已经转换好格式的数据加载进去,或者使用 Hive 来插入数据
Impala压缩编码
类型 说明
Snappy 推荐编码, 压缩率和压缩速度有很好的平衡性, snappy压缩速度很快, 但比gzip压缩率低, Impala不支持snappy压缩的text file
GZIP 压缩率高, Impala不支持gzip格式的text file
Deflate Impala不支持deflate压缩的text file
BZIP2 Impala不支持bzip2压缩的text file
LZO 只用于text file, Impala可查询lzo压缩的text格式数据表但不支持insert, 只能通过Hive完成insert

Spark

机器学习(Machine Learning)

Scala

操作分析(Operational Analytics)

开放数据模型(Open Data Model)

用例(UseCase)

分析(Analysis)


模块(Module)

Spot-Ingest

spot-ingest framework

  Spot-Ingest基于分布式架构, 使用spot-collector守护进程针对不同采集源产生数据时, 通过kafka, spark streaming, hive和hdfs提供近99.99999%的数据完整性。

Spot-Collector

  Spot-Collector基于文件系统在后台监视文件系统的新文件。当网络工具产生新文件或较早产生的数据留在监视路径上时, collectors使用解析工具(比如nfdump和tshark)转化为可读格式, 并把其原始格式存储到Hadoop用于取证, 以avro-parquet格式存储在Hive中以便做SQL查询。$\gt$ 1MB的文件提供其文件名和hdfs路径给kafka, $\lt$ 1MB的文件提供其data event给kafka并在之后由spark streaming做进一步处理(在其github上描述是proxy的pipeline才用spark streaming处理, 不知道一般proxy数据是否是$\lt$ 1MB的)。

Spot-Ingest Kafka
spot-ingest framework

  从图上看是生成flow, dns和proxy三个主题, 各主题分区数由spot-worker的数量决定, spot-collectors作为提供者传输数据到kafka存储, spot-worker作为消费者消费kafka中三个主题的数据。

Spot-Worker

  Spot-Worker作为后台守护进程订阅指定的kafka主题和分区, 并在特定的Hive表中读取解析存储数据, 该数据在将来由ML算法消费。当前有两种worker, 通过定义的解析器多线程处理数据的python worker和使用spark-streaming context(micro batching)执行spark应用处理来自kafka数据的spark-streaming worker.

Spot-ML

spot-ml

  Spot-ML包含执行可疑连接的例程, 分析采集自网络中的netflow, dns或proxy日志。通过分析一系列网络事件, 生成一个最不可能和最可疑的事件列表, 该过程依赖于spot-ingest来加载netflow, dns和proxy记录。它使用主题建模(topic model)来发现正常和异常行为, 把IP关联的日志集合作为文档, 并使用Latent Dirichlet Allocation(LDA)来发现这些文档集合中隐藏的语义结构。Spot-ML为每个IP地址的网络行为提供概率模型(probabilistic model), 即赋予每个网络日志条目被该模型一个估算的概率(或得分), 得分较低的事件被标记为可疑以便进一步分析。
  LDA基于三层的贝叶斯模型, 是一个用于离散(discrete)数据的生成概率模型(generative probabilistic model), 例如文本全集。在这个模型中文档的每个单词(word)都是由一组基础主题混合生成的。LDA在网络流量中, 通过聚合和离散将网络日志条目转换为单词。这样, 文档对应IP地址, 日志条目的单词(与一个IP地址相关)和主题对应公共网络活动的概要文档。

Spot-OA

Spot-Setup

环境(Environment)

environment

演示(Demo)

参考(Reference)


  [01]  kafka 基础知识梳理, Go_小易, 2017-08-14 17:57
  [02]  kafka 学习笔记:知识点整理, cyfonly, 2016-10-12 22:13
  [03]  kafka 数据可靠性深度解读, 朱小厮, 2017-05-02 19:19
  [04]  Kafka 消费组 (consumer group), heidsoft, 2017-10-20 09:53
  [05]  Flume NG 基本架构及原理, dantezhao, 2016-09-14 21:52
  [06]  Flume 架构以及应用介绍, 安静的技术控, 2016-05-31 12:35
  [07]  日志系统之 Flume 采集加 morphline 解析, yanghua, 2015-11-20
  [08]  flume 拦截器及问题解决, ty_laurel, 2017-01-17 18:50
  [09]  【漫画解读】HDFS 存储原理, 雪飘飘, 2016-02-22 13:50
  [10]  深刻理解 HDFS 工作机制, Pickle, 2017-01-11 08:59
  [11]  Apache Hive Design, Administrator, 2015-11-08
  [12]  杨卓荦:Hive 原理及查询优化, anand, 2016-07-07
  [13]  Impala(多图手机用户慎入,理论 + 实践), SET, 2016-09-21 23:33
  [14]  怎么理解 impala(impala 工作原理是什么), 邱明成, 2017-02-12 09:28
  [15]  Apache Spot, 2016-03-29

To be Continued...

]]>
0 https://www.windawings.com/research/apache-spot.html#comments https://www.windawings.com/feed/research/apache-spot.html
Atomineer Pro Documentation Trial 9.36 Crack https://www.windawings.com/reverse/atomineer-crack.html https://www.windawings.com/reverse/atomineer-crack.html Fri, 02 Feb 2018 22:10:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

atomineer

引言(Preface)

  AtomineerUtils 是国外的一款用于生成源代码注释的一款 VS 插件工具
  官方网站: http://www.atomineerutils.com/products.php
  通过链接,可以看出这款插件的功能十分强大,不像 GhostDoc 那样,只支持 C#,这款插件,支持 C、C++、C++/CLI、C#、Java 语言等,由此可以看出其强大,注释的风格可以灵活配置。
  虽然它功能十分强大,但是由于是收费的软件,所以当使用超过 30 天后,就无法再继续适用了。个人并不是做破解的,之所以破解它只是因为我写代码时会用到,网上搜索也只有 8.x 的破解,也没法正常使用。

  http://www.cnblogs.com/hbccdf/p/atomineerutilscrack.html
  这里我参照了下上面原文的作者的论述对最新版做了点修改。目前 9.25 版本后官方的 VS2015 版本的主要 DLL 部分不再做代码混淆,而是很清晰的类名称,所以破解反而更容易了,当然其下非 *.vsix 安装的版本依然是做了混淆,要一个一个去找,找不到只能下着断点去慢慢找了。
  网上我也没有找到它的完整版,源程序只是混淆了一下,所以我就只是把试用版的使用时间限制和强制更新限制破解了。

介绍(Introduction)

  下面是官网的截取:

  This extension for Visual Studio provides unsurpassed code documentation comment generation and updating, smart word wrapping of text within comments, live typing aids to make editing comments much easier, tools to quickly convert legacy documentation to new formats, a command to instantly align code (such as assignments, parameters and tabular data) into columns, and several handy outlining, code generation and clipboard commands.
  1. Save time - save effort - save money.
  2. Eliminate almost all the drudgery of filling in documentation.
  3. Effortlessly keep code and documentation in sync.
  4. Encourage better coding, naming and documentation practices.
  5. Speed up development and reduce errors with intellisense help on all
  6. your own classes and methods.
  7. Easily satisfy Code Analysis and Static Analysis requirements for
  8. documentation.
  9. Accelerate editing of comments with automated formatting, word
  10. wrapping, pasting and bullet-list entry aids, and (for VS 2010, 2012,2013, 2015) spell checking as you edit comments.

  Atomineer Pro Documentation Development Summary

  This extension began development in 1996 (for Visual C++ 4.0), and has been evolving and improving through constant use by software professionals ever since. More recent developments are listed below…

How It Works Supported Languages

版本(ChangeLog)

9.36 9.35 9.34

下载(Download)

  

技巧(Tips)

  注意断网进入 VS 后,在拓展和更新里把它的自动更新勾给去掉,不然 VS 还是会让它自动更新。

]]>
0 https://www.windawings.com/reverse/atomineer-crack.html#comments https://www.windawings.com/feed/reverse/atomineer-crack.html
ManicTime v4.1.4 Pro Crack https://www.windawings.com/reverse/manictime-crack.html https://www.windawings.com/reverse/manictime-crack.html Thu, 01 Feb 2018 19:30:00 +0800 windawings #tree{min-height:20px;padding:19px;margin-bottom:20px;border:1px solid #999;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}#tree li{list-style-type:none;margin:0;padding:10px 5px 0;position:relative}#tree li:after,#tree li:before{content:"";left:-20px;position:absolute;right:auto}#tree li:before{border-left:1px solid #999;bottom:50px;height:100%;top:0;width:1px}#tree li:after{border-top:1px solid #999;height:20px;top:25px;width:25px}#tree>ul>li:after,#tree>ul>li:before{border:0}#tree li:last-child:before{height:25px}#tree li.parent_li{margin:0 5px -21px 0}

目录(Index)

引言(Preface)

  ManicTime 是一款采用 .Net 写成的用于跟踪每天在电脑前做什么的时间管理软件, ManicTime 会在后台静默运行并跟踪记录每天在各种程序上所花费的时间然后生成报表,最终改善工作效率。
  官方网址: https://www.manictime.com
  破解参考: https://www.52pojie.cn/thread-614943-1-1.html
  注意: 软件支持简体中文, 仅能在Win10下激活成功, Win7根据前次发布经验似乎没有人成功, 原因未知

介绍(Introduction)

个体需求
个体解决方案
团队需求
团队解决方案
免费版与专业版区别
关于专业版

版本(ChangeLog)

  主要是和4.0.x版本的区别, 其他都是稳健性bug的fix. 详细内容可参考https://www.manictime.com/Releases/Version-v4-1

  • Shortcuts for billable
  • Copy autotags to tags for multiple days
  • Publish screenshots to server (works with server v3.7)
  • Current screenshot cursor
  • Screenshot tagging
  • Command line export
  • Moves plugin, Imperial units and open stationary location
  • If you tagged based on Details (bottom left part of day view) while showing only untagged activities, then after you tagged scroll bar jumped around to some random activity. It will now jump to the next untagged activity
  • Bug fixes

下载(DownLoad)

  

鸣谢(Thanks)

Enjoy Yourself
Thanks for tchivs from 52pojie.cn
]]>
4 https://www.windawings.com/reverse/manictime-crack.html#comments https://www.windawings.com/feed/reverse/manictime-crack.html
初记 https://www.windawings.com/default/start.html https://www.windawings.com/default/start.html Sun, 28 Jan 2018 19:25:00 +0800 windawings   自上次购买搬瓦工的OpenVZ的服务器也过了将近一年,当时20美刀也就只能买到个256M的内存,数据库和网站基本处于一天一崩的状态,所以也没有怎么想写东西,写了也会崩 = =,近期那个服务器甚至被美帝封了IP,大概是前段时间弄了个BT的代理上去,站也就尘封了,解开还得要8美刀,也就弃了。
  所以就索性换了台,现在同样的价格可以购买到512M的KVM了,无论BBR也好、SWAP也好,性能上搭个简单的CMS足矣,索性就记录些自己的学习历程。之所以写东西,还是本科一位大佬安利着要本渣写博客,就当是个public evernote吧 =。=

]]>
0 https://www.windawings.com/default/start.html#comments https://www.windawings.com/feed/default/start.html