30分钟无坑部署K8S单Master集群

Jesse导读:11月9号,我在中国.NET开发者峰会(.NET Conf China 2019)上分享了之前ASP.NET Core和Kubernetes做微服务的经验,在10号的时候又联合张善友、陈计节两位大佬一起做了一个6个小时的动手实践,得到了非常好的反馈。学习K8S需要首先拥有一个K8S的操作环境,由于它的复杂性以及不可描述的原因导致这很多同学直接被卡在了这里。redz(人称小红)是我们团队中的成员之一,这篇文章是他在自己用VMWare虚拟机上搭了3台虚拟机并且在这3台虚拟机上搭建K8S 1.16版本的过程 。

相关环境

  • CentOS 7.6 (2-3台)
  • K8s 1.16.2
  • Docker 18.09.3

需要自己准备2-3台虚拟机来安装CentOS7.6系统,在开始之前先修改主机名称(主机名称不能重复-如果重复即使work节点加入成功,master看不到work节点)

hostnamevi /etc/hostname

在vi中编辑hostname之后输入:wq保存,之后重启系统即可。

关闭防火墙

systemctl stop firewalldsystemctl disable firewalld

禁用swap

swapoff -a
  • 修改etc/fstab
  • 在行首加 #,注释/dev/mapper/centos-swap swap

安装Docker

  • k8s支持的Docker版本

sudo yum install -y yum-utils device-mapper-persistent-data lvm2sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.reposudo yum install docker-ce-18.09.1 docker-ce-cli-18.09.1 containerd.iosystemctl start docker.servicesystemctl enable docker.service

添加K8s 国内镜像源

cat>>/etc/yum.repos.d/kubrenetes.repo<<EOF[kubernetes]name=Kubernetes Repobaseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/gpgcheck=0gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpgEOF
安装K8s所需要的3个组件
yum install -y kubeadm  kubelet kubectl

设置k8s开机自启动


systemctl enable kubelet.service

K8s集群

创建K8s集群 Master Node

在master节点上执行以下命令


kubeadm init --image-repository registry.aliyuncs.com/google_containers \    --pod-network-cidr=10.244.0.0/16 \    --ignore-preflight-errors=cri \    --kubernetes-version=1.16.2
  • 输出以下信息表示,K8s与Docker版本不一致
[WARNING SystemVerification]: this Docker version is not on the list of validated versions: 19.03.4. Latest validated version: 18.09

  • 输出以下信息表示,swap没有被禁用
[ERROR Swap]: running with swap on is not supported. Please disable swap

  • 输出以下信息表示,请执行echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables
[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1
  • 安装成功后, 会显示如下的信息 按步执行即可

mkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/config

Init Network Flannel 初始化网络插件

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
  • 由于某种不可抗力 请先下载yml至本机后执行 kubectl apply -f kube-flannel.yml

加入K8s集群 Slave Node

  • 对于从节点 执行步骤至安装K8s 无需init
  • 在master节点上执行, 获取join-token
kubeadm token create --print-join-commandkubeadm join 192.168.69.128:6443 --token uc826o.l9na62a2ckmf5rjo --discovery-token-ca-cert-hash sha256:2be9656d7b4c3e9aab2b1259b0eea948f0b0b05d082267aa5676967cc495c168
  • 从节点执行join-token

K8s集群相关命令

  • 集群获取节点 kubectl get nodes
  • 集群删除节点 kubectl delete node <node-name>
  • 被删除的节点重新加入集群 先执行 kubeadm reset

 

K8s UI kubernetes-dashboard

部署yaml

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommended.yaml
  • 此部署文档镜像来源于docker hub
  • 假设部署文档由于众所周知的原因不可访问 请先通过技术手段获取文档后执行部署命令
kubectl apply -f recommended.yaml
  • 使用NodePort方式暴露30065端口 用于非k8s节点中访问 修改yaml以下内容 重新执行部署命令
 kind: Service apiVersion: v1 metadata:   labels:     k8s-app: kubernetes-dashboard   name: kubernetes-dashboard   namespace: kubernetes-dashboard spec:   type: NodePort  ports:    - port: 443      targetPort: 8443      nodePort: 30065  selector:      k8s-app: kubernetes-dashboard
  • NodePort方式默认端口范围30000-32767
  • 获取kubernetes-dashboard访问token
kubectl -n kube-system describe $(kubectl -n kube-system get secret -n kube-system -o name | grep namespace) | grep token
  • 在firefox中访问该网址 https://[k8s节点]:30065选择token登录并输入访问token
  • kubernetes-dashboard要求使用HTTPS Chrome不能访问此类没有认证的web,可以使用firefox进入访问

本机添加kubectl

在windows安装 kubectl

1:choco install kubernetes-cli

2:在master 主机上进入/etc/kubernetes/admin.conf 把admin.conf 下载下来,拷贝到本机当前用户的 .kube\config里面 如果当前用户下没有.kube 使用powershell 建立一个文件夹和config里面
ASP.NET Core微服务 on K8S

如果你对asp.net core微服务和k8s感兴趣,可以看看我录制的视频。涉及到K8S管理、微服务设计以及一些中间件在k8s上的运维,点击左下角的【阅读原文】查看详情。 如果有疑问也可以添加我的微信:280113562进行咨询。

一次线上CPU高的问题排查实践

https://www.cnblogs.com/lanxuan826/p/11074848.html

 

一次线上CPU高的问题排查实践

前言

近期某一天上班一开电脑,就收到了运维警报,有两台服务CPU负载很高,同时收到一线同事反馈 系统访问速度非常慢,几乎无响应。

一个美好的早晨,最怕什么就来什么。只好推掉其他会议,专心搞定问题。

排查

登录系统一看,后端的接口访问果然全部超时。

  1. 先使用top命令查看下是由哪个进程占用CPU较高。

从图上可看出,pid=26481的Java进程占用了385%的CPU,机器是4核8G的配置。

2.然后查看下是哪个Java应用 ps -ef | grep java

发现是我们后端request服务的应用。

3.获取pid后,可以查看该进程下的线程列表。 命令: ps -mp pid -o THREAD,tid,time。

.png)

这里我们可以看出有4个线程,长时间占用较高的CPU资源。拿到一个tid=26496,printf “%x\n” tid , 转为16进制:6780

4.使用jstack打印线程的堆栈信息,./jstack pid | grep tid -A 30

从图上可看出,是由于该应用的GC异常导致CPU占用较高。

5.导出堆栈日志分析。 jstack 26481 >> jstat.out

解决问题

分析导出jstat.out,查找下RUNNABLE状态的线程。

ps:这里推荐一个分析日志的网站,超级好用,能生成图形界面的分析日志。只需要将生产的日志上传。Java Thread Dump Analyzer https://fastthread.io/

最终发现是Excel大量导出的问题。用户导出一年的财务报表,差不多近60万条数据,由于系统导出较慢,然后进行了多次点击下载,导致应用内存不足,频繁GC, 导致CPU超高,系统假死。

至此问题发现,解决问题就变得简单了。

  • 重启应用,让用户使用优先。 最快捷、最有效的解决方式,哈哈
  • 前端限制按钮点击次数。
  • 后端Excel下载优化(考虑阿里的easyexcel,详细请看后续分享),以及将文件下载独立出一个微服务。
分类: JavaWeb

招募[上海]龙珠直播 – iOS 开发工程师

岗位职责:

  1. 负责 IOS 终端框架设计,需求开发和预研工作;
  2. 负责实现产品经理提出的需求,按照设计师的设计实现 UI 及交互效果;
  3. 负责 App 性能调优、重点 /难点技术攻坚等;
  4. 负责部分跨平台技术( RN、H5 等)的研究、落地、应用。

岗位要求:

1、本科及以上学历,计算机或通信相关专业,3 年以上移动 app 开发经验;

2、具有扎实的 Objective-C/Swift 语言基础,熟练掌握 RN/H5 等跨平台技术者优先;  3、熟练掌握 iOS 开发、测试、调优工具的使用;

4、能够熟练编写 iOS 平台自定义控件和动画效果;

  5、深入理解 Objective-C Runtime 运行机制和内存管理机制;

  6、深入了解各个不同 iOS 版本的特性与差异;

7、熟悉网络通信机制及常用数据传输协议;

8、熟悉 Xcode,并对 Instrument 的使用有丰富经验;

9、思路清晰,自我驱动,团队意识强。

职位诱惑 : 很多软妹子同事,新的办公场所,你值得拥有

薪资:15k 以上。

可以联系 jiqiu@pptv.com , 17010983@suning.com (注明 person168)

公司地址:上海 浦东新区 峨山路 101 号 陆家嘴软件园 C2

龙珠直播(上海)-Android 开发工程师

岗位要求:

1、按照产品经理的需求研发龙珠直播 APP 产品;

2、负责相关 Android 各项性能的优化和瘦身;

3、负责调研 /跟进系统兼容性及 UI 适配等相应问题;

4、负责部分跨平台技术( RN、H5 等)的研究、落地、应用。

任职要求:

1、1 ~ 5 年工作经验,本科及以上学历;

2、熟练掌握 Java,设计模式,网络,多线程等技术;

3、熟悉 Android SDK 的特性,了解 Android 系统运行原理;

4、熟悉 Android APP 架构设计,有模块化,组件化,插件等开发经验者优先;

5、熟悉 Android 性能分析工具,有 UI 卡顿、APP 启动、包瘦身和内存等性能优化经验者优先;

6、具备良好的技术热情,主动跟进新的热点或技术,参与或独立开发开源项目者优先;

7、具有良好的责任心、沟通协调和团队合作精神;

8、认真负责,热爱学习,善于思考,乐于创新。

职位诱惑 : 很多软妹子同事,新的办公场所,你值得拥有

薪资:15k 以上。

可以联系 jiqiu@pptv.com , 17010983@suning.com (注明 person168)

公司地址:上海 浦东新区 峨山路 101 号 陆家嘴软件园 C2

招募[上海]龙珠直播 – 前端开发

工作职责:

  1. 负责公司 Web 前端产品的相关开发任务
  2. 维护现有产品并持续的优化前端体验和页面响应速度,提高前端的用户体验及性能,不断提升网站性能及开发质量等
  3. 对工作认真负责,工作主动性强,具有积极的自我驱动能力
  4. 具备良好的学习能力、沟通能力、分析及解决问题能力,优秀的团队协作精神
  5. 热爱技术发展潮流,一起研究新技术,并用新技术推动业务发展

岗位要求:

1、掌握 WEB 开发核心知识 HTML,CSS, JS。

2、使用 AngularJS,Vue.js ,React.js 等其中一种前端框架。

3、使用过 gulp,webpack,babel 等构建工具,对前端工程化有一定的认知。

4、持续关注业界的新话题和新技术,对 ES next 及浏览器新的 API 有一定研究。

5、了解 HTTP、websocket 等通讯协议,能够使用如 Fiddler 等工具,掌握浏览器开发者工具主要功能。

6、熟悉 git 常用操作及 workflow。

[符合以下条件者优先]

1、精通 JS,熟悉 Node.js ,并有 Node.js 服务端开发经验。

2、有微信生态圈 web 开发经验。

3、注重代码规范,文档注释习惯。

4、了解 OOP、FP 等编程范式。

职位诱惑 : 很多软妹子同事,新的办公场所,你值得拥有

薪资:15k 以上。

可以联系 17010799@suning.com, linengchen@pptv.com (注明 person168)

公司地址:上海 浦东新区 峨山路 101 号 陆家嘴软件园 C2

招募[上海]龙珠直播 – Java 开发

工作职责:

  1. 负责公司后端接口的相关开发任务,搭建稳固的 java 系统架构
  2. 对工作认真负责,工作主动性强,具有积极的自我驱动能力
  3. 具备良好的学习能力、沟通能力、分析及解决问题能力,优秀的团队协作精神
  4. 热爱技术发展潮流,一起研究新技术,并用新技术推动业务发展

岗位要求:

  1. Java 基础扎实:熟悉 IO、多线程、集合操作等基础技能; 熟悉分布式、缓存、消息队列等中间件技术; 熟悉 mysql 数据库的基本操作
  2. 对 J2EE 有较清晰的认识,熟悉主流 Java Web 框架,熟悉各种常用设计模式
  3. 熟悉 spring、MyBatis 等常用 Java 开源框架,对其运行原理有较好的理解
  4. 具备一定的领域建模能力,参与过完整的应用系统研发
  5. 有较强的学习和沟通能力,良好的团队协作精神,极强的责任心,工作踏实、勤恳,有钻研精神
  6. 工作经验 3 年
  7. 有 go,python 等经验更好

职位诱惑 : 很多软妹子同事,新的办公场所,你值得拥有

薪资:15k 以上。

可以联系 linengchen@pptv.com , 17010815@suning.com (注明 person168)

公司地址:上海 浦东新区 峨山路 101 号 陆家嘴软件园 C2

我爬了链家青岛市北3000套二手房得出一个结论

前言

青岛的房价这两年翻了一番,举个栗子,如果你在2016年在市区买了100万的房子,2018年价值200万,净增100万;如果你2016年没有买这100万的房子,2018年买房将多付100万,机会成本100万。而这100万可能是青岛白领不吃不喝十年的收入。

自2018年第二季度起,限价限购限售与金融市场去杠杆两大行政令双管齐下,包括青岛在内的一二线城市房价明显遇冷,成交寥寥,投资客杳无踪影,刚需驻足观望,着急出售的不得不主动降价,价格普遍下跌,三四线城市也受到不同程度的影响。根据博主的卖房经历,初始挂牌价同小区同期房最低,依然许久无人问津,在中介的提议下骤降X%才出手。但是,从长期来看,除非政府放弃干预,实行完全的市场经济模式,否则一二线城市的房价跌幅不会太大,尤其青岛,各地铁线路的开通将再次拉高沿线房产价格。对于刚需群体而言,买房的主要目的不是投资,而是居住。购房时间越晚,成本越高。

爬取数据

博主最近有买房的计划,房价稳中下跌,不失为一个买房好机会。于是,我这个之前不懂房的人,硬着头皮用 Python 爬了些数据分析了一丢丢。

创建基本的数据库:

CREATE TABLE `house` (
   `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
   `url` int(11) NOT NULL COMMENT '二手房地址',
   `housing_estate` varchar(20) NOT NULL COMMENT '小区',
   `position` varchar(20) NOT NULL COMMENT '位置',
   `square_metre` decimal(10,2) NOT NULL COMMENT '大小 平米',
   `unit_Price` int(11) NOT NULL COMMENT '单价元 基本都是整数',
   `total_price` int(11) NOT NULL COMMENT '单价万元 基本都是整数',
   `follow` int(11) NOT NULL COMMENT '关注量',
   `take_look` int(11) NOT NULL COMMENT '带看量',
   `pub_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '发布日期',
   PRIMARY KEY (`url`),
   UNIQUE KEY `id` (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

爬取代码:

#coding=utf-8
#!/usr/bin/python
__author__ = "小柒"
__blog__ = "https://blog.52itstyle.com/"
# 导入requests库
import requests
# 导入文件操作库
import os
import re
import bs4
from bs4 import BeautifulSoup
import sys
from util.mysql_DBUtils import mysql


# 写入数据库
def write_db(param):
    try:
        sql = "insert into house (url,housing_estate,position,square_metre,unit_price,total_price,follow,take_look,pub_date) "
        sql = sql + "VALUES(%(url)s,%(housing_estate)s, %(position)s,%(square_metre)s,"
        sql = sql + "%(unit_price)s,%(total_price)s,%(follow)s,%(take_look)s,%(pub_date)s)"
        mysql.insert(sql, param)
    except Exception as e:
        print(e)


# 主方法
def main():
    # 给请求指定一个请求头来模拟chrome浏览器
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'}
    page_max = 100
    for i in range(1, int(page_max) + 1):
        if i == 1:
            house = 'https://qd.lianjia.com/ershoufang/shibei/'
        else:
            house = 'https://qd.lianjia.com/ershoufang/shibei/pg'+str(i)
        res = requests.get(house, headers=headers)
        soup = BeautifulSoup(res.text, 'html.parser')
        li_max = soup.find('ul', class_='sellListContent').find_all('li')
        for li in li_max:
            try:
                house_param = {}
                #  荣馨苑  | 3室2厅 | 115.91平米 | 南 北 | 毛坯 | 无电梯
                content = li.find('div', class_='houseInfo').text
                content = content.split("|")
                house_param['housing_estate'] = content[0]
                house_param['square_metre'] = re.findall(r'-?\d+\.?\d*e?-?\d*?', content[2])[0]
                # --------------------------------------------------------#
                #  位置 水清沟
                position = li.find('div', class_='positionInfo').find('a').text
                house_param['position'] = position
                # --------------------------------------------------------#
                totalprice = li.find('div', class_='totalPrice').text
                house_param['total_price'] = re.sub("\D", "", totalprice)
                unitprice = li.find('div', class_='unitPrice').text
                house_param['unit_price'] = re.sub("\D", "", unitprice)
                # --------------------------------------------------------#
                # 57人关注 / 共13次带看 / 6个月以前发布
                follow = li.find('div', class_='followInfo').text
                follow = follow.split("/")
                house_param['follow'] = re.sub("\D", "", follow[0])
                house_param['take_look'] = re.sub("\D", "", follow[1])
                # --------------------------------------------------------#
                # 二手房地址
                title_src = li.find('div', class_='title').find('a').attrs['href']
                house_param['url'] = re.sub("\D", "", title_src)
                res = requests.get(title_src, headers=headers)
                soup = BeautifulSoup(res.text, 'html.parser')
                # --------------------------------------------------------#
                # 挂牌时间(重要数据)
                pub_date = soup.find('div', class_='transaction').find_all('li')[0].find_all('span')[1].text
                house_param['pub_date'] = pub_date
                write_db(house_param)
            except Exception as e:
                print(e)
        mysql.end("commit")
    mysql.dispose()


if __name__ == '__main__':
    main()

通过链家网页搜索,青岛市北共找到 5105 套二手房,但是看下分页,有100页,每页30条数据,博主用 Python也只爬下2994条数据。个人侦察能力有限,至今仍未找到被藏起来的两千多套房子。若有知晓内幕者,望不吝赐教,柒某愿闻其详。

分析数据

数据库设计方面,博主目前只关注总价、均价、关注、带看量以及发布时间。

总体概况

总数 均价 均关注 均带看
2994 27721 17 1

我们先来看一下链家提供的青岛市北的二手房价格(成交价)走势:

爬取得到的平均单价与链家统计的挂牌平均单价相差无几,成交单价比挂牌平均单价低3k左右。目前二手房市场交易冷冷清清,部分二手房房主无意出售或者不急于出售,挂牌一两年仍未成交,这里后面会有数据说明。

只有少部分房主出于房产置换或套现等需求,着急出售,因而愿意大幅降价。

受首付难凑,房贷难申,房价下跌,尤其是房产市场前景不明朗等多重因素影响,本就为数不多的准买家持续观望,尽管各中介频繁推荐房源,但是准买家并不为所动。

位置分布

从以上两张图可以很直观地看出二手房集中在几个区域——海云庵、台东、新都心。为何这些区域存在如此多的待售二手房呢?

  • 海云庵:以前属于四方区,后现划分至市北,位置略偏,多层房老旧,还有部分拆迁还建房,高至三十几层,物业管理混乱。原房主出售房产谋求换房改善居住环境。
  • 台东:老市北,典型的开放多层老旧小区,商住混合,人员杂乱,挂牌出售的房产中尤以待拆房居多。
  • 新都心:属于市北新商业区,多为2010年以后的高层电梯房,房子基数大,且此区域配套设施完善,在这个区域投资的炒房客多,挂牌出售以套现。

带看量

带看量为零的房源高达六成,多为同小区房源中单价偏高者,显然,鲜有人问津是此时挂牌出售的大部分房源的现状,准买家不仅不急于购买,看房子也不着急了。或许不仅仅是不着急,而是基于目前家庭经济状况考虑,购房目标转向被限价的新楼盘和价格较低的红岛、黄岛等区域了。

挂牌大于一年未出售的房子:

总数 单均价 均关注 均带看
124 28169 47 0.48

挂牌大于半年未出售的房子:

总数 单均价 均关注 均带看
908 27795 31 0.92

挂牌大于三个月未出售的房子:

总数 单均价 均关注 均带看
2345 27712 20 1.07

在带看量为0或1的房源中,不排除部分炒房客无意出售或者不急于出售,有意高价挂牌,潜移默化地拉高整个小区的房价。

贷款

此次房地产市场遇冷,与贷款利率提高不无关系,房价与房贷利率犹如坐在跷跷板两端,当房价出现下滑迹象时,房贷利率优惠就难觅踪影,而当首套房贷利率有所松动时,房价将进入上升通道。对刚需而言,房价略降并不意味着购房成本降低。房贷利率上浮了百分之二十左右,但房价仅仅是略降而已,如果购房者贷款比例高,房价下降的部分不多于多付的利息,购房总成本并未降低。总而言之,利率高,房价低,对于全款买房的买家来说,成本才是真的低了。

商业贷款贷款额度100万,等额本息还款差距表:

优惠 利率 月供
基准 4.9 5307
九折 4.41 5014
上浮20% 5.88 5919

如果你打算公积金贷款,不要做白日梦了,漫长的申请周期和可怜的额度足以让买卖双方都抓狂。

注:2017年青岛就业人员平均工资出炉 月均5253元。

房产税

至于房产税,还在酝酿提案中,神马时候冲破重重关卡仍未可知。

那么哪些人惧怕房产税?

人群 伤害指数
在中心城市囤积大量住宅的人 ★★★
盲目购买旅游物业、养老地产的人 ★★★★
盲目购买三四线城市郊区、新区住宅的人 ★★★★
加杠杆、超承受能力买多套房的白领 ★★★★
在三四线城市囤积了大量住宅的人 ★★★★★★
手中有多套房、负债率非常高的炒房者 ★★★★★★

主要取决于房产税的具体条款,包括征税比例、起征房产套数、起征面积、异地房产统计、家庭成员统计等诸多因素。

趋势态度

其实扯了这么多,总的来说,房价大概不会继续翻着番地涨,同时,指望购房成本下降也是不现实的。如果你是炒房客,换个市场炒吧;如果你是刚需,看好房子就要尽早下手,不要期待任何人慷慨解囊或赠予。规则认知和运用能力是个体生存的基本能力。

当然,肯定有一些人,斥责现在的年轻人以买房为目标,别无他求。有的是有人帮忙负重,生而无忧,从不为其所困;有的是无欲无求,赡养、抚育与他毫无干系;更多的是想要而不得,迫不得已放弃。房地产自商业化之日起就绑架了太多,下一代的教育、良好的居住环境、货币贬值与资产保值……万事有因果,存在即合理。如果真的要“修正”价值观,洗脑活动需尝试从下一代出生之日开始。

小结

这仅仅房地产市场冰山一角,不具备广泛代表性。但是下面一句话与诸位共勉:买房要趁早,没有条件也要创造条件,哪怕以后你卖了呢!

源码:https://gitee.com/52itstyle/Python/tree/master/Day11

作者: 小柒

出处: https://blog.52itstyle.com

从面试官的角度谈谈大数据面试

作为一只老鸟,我的面试经验还算丰富,无论是作为面试者还是面试官。其实这篇对于面试者来说也是有意义的,毕竟知己知彼,百战不殆,知道对方会从哪些方面问问题,从哪些方面考核,才能更好地提前做好准备。

首先,我觉得面试官有责任保证面试过程是一次高效的交流。你要获取到你需要的信息,对面试者做全方位的考量;面试者也要获取到他需要的信息,面试官(若面试成功很大可能是自己的上级)的水平,公司技术要求水平,自己是否适合这家公司,公司是否需要自己。

面试是一个双向选择的过程,面试官在选人,面试者在选公司。而面试者了解这家公司最直接的途径就是通过面试官。

说说面试官

我先说几个面试官常会有的问题。

  • 问题问得太跳跃,想到什么问什么
  • 抓住一个面试官自己很熟的知识点或者方向往死里问 ,完全不会根据面试者的回答情况做调整(我是来面试的,不是来看你炫技的)
  • 只问技术,不问业务
  • 技术问题问得太表面

当然我也见过不错的面试官,问题问得很有水平。那有水平的面试官会给人什么样的感觉?

  • 答得很舒服,不管结果怎么样,总之能展现出自己应有的水平
  • 面试过程是有收获的,没有白来,知道了自己的欠缺
  • 如果面试者是个到处抢着要的高手,那你有水平的提问会给这个面试者留下深刻印象,毕竟大家都是喜欢和厉害的人当同事的
说说提问

思路想法,表达能力,技术功底,热情。这几个点我是比较看重的。很多问题都是围绕着这几个点展开的,大家看下有没有借鉴意义。

01

技术能力

这个是硬指标,不过关的基本是可以一票否决的,当然技术能力的标准是根据工作年限,面试职位和薪资要求共同来决定的。面试官要根据实际情况有自己的判断。

那技术能力如何考察?我提几个方面

基础能力

java 的 jvm、多线程、类加载等

scala 伴生对象,偏函数,柯里化等

还有shell和python的就不举例了

HBase读写流程

Yarn任务提交流程等等

底层原理

Hbase是如何存数据的,为什么读得快

spark为什么就算不在内存跑也比mr快

zookeeper数据怎么保证一致性

说说选举机制

等等

源码

有没有读过源码?

详细说下你从源码中获取到了什么信息,有什么帮助

架构设计能力

如何技术选型,考虑哪些因素?

设计一个同时满足实时和离线分析需求的平台

为什么这么设计?

另外

以上问题如果回答得不太好,可以再给个机会让他说下自己最熟悉的技术,不限制从哪些方面讲。

02

解决问题能力

如何排查hbase集群cpu过高问题

如何优化spark任务

……

03

方案设计能力

说说数据仓库设计建模过程

说说数据质量监控系统怎么设计

……

04

想法

这是一道开放题

对数据治理有什么想法

对职业生涯的规划

……

05

还可以再问些偏向管理的问题

如何调动组员的技术学习积极性

如何高效地跨部门协作

……

06

唠嗑

上面的问题问完觉得感觉可以的话可以,可以唠唠嗑,问些其他问题。

为何离职?

觉得自己是什么样的性格等等

当然这些都不太重要了主要就是考察下你的语言表达能力和三观是不是正的。

提问的技巧

问问题要有技巧,循循善诱而不是想到什么问什么

举个简单的例子

问:zookeeper加大量节点会对文件写入速度有什么影响?为什么?

答:不清楚

问:你觉得zookeeper作为分布式协调系统对一致性有什么要求呢

答:强一致性

问:那你觉得要如何保证强一致性,或者说保证强一致会不会对其他方面的性能有影响

……

不知道大家有没有看出来,最后一个问题其实是第一个问题的答案,当面试者回答不出来的时候不用急着换其他方面的问题,毕竟很多东西没接触过确实就是不知道。

你可以适当地引导他回答的方向,这样很能看出他的思维能力,如果他能把这两个问题立马关联起来回答,那我觉得还是可以加分的。

面试者如果听出了这两个问题的关联,恍然大悟,也会觉得面试官提问很有技巧,提升好感,对他来说选公司方面也是有加分的。

最后

上面的内容希望能对一些面试者或者面试官有帮助。当今时代,跳槽确实是大部分程序猿升职加薪最快的方式,特别是职业生涯初期。说起来也是很无奈,公司经常是宁愿花更多的钱来请个新人也不愿意加薪留住老人。

因此现在很多大公司的管理者都很喜欢强调文档落地,需求方案,技术方案,解决方案等等都要有记录,这样可以保证新人能快速上手,即插即用。

说白了就是保证这个项目组没了任何一个人,都可以继续正常运作。这个先不说了扯远了,有空再聊。

这个行业就是这样我们没有办法改变TA那就只能适TA。

觉得有帮助的话点个赞吧,如果点赞量多的话,我再写一篇 [从面试者的角度谈谈大数据面试]。

awesome list

收集各种awesome。

awesome的源头在这里:awesome,后来发展了各种各样的avesome项目。不过资源都是英文的,不太方便国内同学查看,我这里整理了一些中文的avesome项目。

参与贡献

目录

语言

书籍

前端

后端

其它

分布式唯一ID的几种生成方案

前言

在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一下,不一定全部适合,这些解决方案仅供你参考,或许对你有用。
大家可以关注一下公众号“Java架构师秘籍

正文

分布式ID的特性

唯一性:确保生成的ID是全网唯一的。
有序递增性:确保生成的ID是对于某个用户或者业务是按一定的数字有序递增的。
高可用性:确保任何时候都能正确的生成ID。
带时间:ID里面包含时间,一眼扫过去就知道哪天的交易。

分布式ID的生成方案
1. UUID
算法的核心思想是结合机器的网卡、当地时间、一个随记数来生成UUID。

优点:本地生成,生成简单,性能好,没有高可用风险
缺点:长度过长,存储冗余,且无序不可读,查询效率低

2. 数据库自增ID
使用数据库的id自增策略,如 MySQL 的 auto_increment。并且可以使用两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。

优点:数据库生成的ID绝对有序,高可用实现方式简单
缺点:需要独立部署数据库实例,成本高,有性能瓶颈

3. 批量生成ID
一次按需批量生成多个ID,每次生成都需要访问数据库,将数据库修改为最大的ID值,并在内存中记录当前值及最大值。

优点:避免了每次生成ID都要访问数据库并带来压力,提高性能
缺点:属于本地生成策略,存在单点故障,服务重启造成ID不连续

4. Redis生成ID
Redis的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保证生成的 ID 肯定是唯一有序的。

优点:不依赖于数据库,灵活方便,且性能优于数据库;数字ID天然排序,对分页或者需要排序的结果很有帮助。

缺点:如果系统中没有Redis,还需要引入新的组件,增加系统复杂度;需要编码和配置的工作量比较大。

考虑到单节点的性能瓶颈,可以使用 Redis 集群来获取更高的吞吐量。假如一个集群中有5台 Redis。可以初始化每台 Redis 的值分别是1, 2, 3, 4, 5,然后步长都是 5。各个 Redis 生成的 ID 为:

A:1, 6, 11, 16, 21
B:2, 7, 12, 17, 22
C:3, 8, 13, 18, 23
D:4, 9, 14, 19, 24
E:5, 10, 15, 20, 25

随便负载到哪个机确定好,未来很难做修改。步长和初始值一定需要事先确定。使用 Redis 集群也可以方式单点故障的问题。
另外,比较适合使用 Redis 来生成每天从0开始的流水号。比如订单号 = 日期 + 当日自增长号。可以每天在 Redis 中生成一个 Key ,使用 INCR 进行累加。
5. Twitter的snowflake算法
Twitter 利用 zookeeper 实现了一个全局ID生成的服务 Snowflake:github.com/twitter/sno…
https://user-gold-cdn.xitu.io/2018/7/2/1645b1a7a9beb2b6?imageslim
如上图的所示,Twitter 的 Snowflake 算法由下面几部分组成:

1位符号位:

由于 long 类型在 java 中带符号的,最高位为符号位,正数为 0,负数为 1,且实际系统中所使用的ID一般都是正数,所以最高位为 0。

41位时间戳(毫秒级):

需要注意的是此处的 41 位时间戳并非存储当前时间的时间戳,而是存储时间戳的差值(当前时间戳 – 起始时间戳),这里的起始时间戳一般是ID生成器开始使用的时间戳,由程序来指定,所以41位毫秒时间戳最多可以使用 (1 << 41) / (1000x60x60x24x365) = 69年。

10位数据机器位:

包括5位数据标识位和5位机器标识位,这10位决定了分布式系统中最多可以部署 1 << 10 = 1024 s个节点。超过这个数量,生成的ID就有可能会冲突。

12位毫秒内的序列:

这 12 位计数支持每个节点每毫秒(同一台机器,同一时刻)最多生成 1 << 12 = 4096个ID
加起来刚好64位,为一个Long型。

优点:高性能,低延迟,按时间有序,一般不会造成ID碰撞
缺点:需要独立的开发和部署,依赖于机器的时钟

简单实现

public class IdWorker {
    /**
     * 起始时间戳 2017-04-01
     */
    private final long epoch = 1491004800000L;
    /**
     * 机器ID所占的位数
     */
    private final long workerIdBits = 5L;
    /**
     * 数据标识ID所占的位数
     */
    private final long dataCenterIdBits = 5L;
    /**
     * 支持的最大机器ID,结果是31
     */
    private final long maxWorkerId = ~(-1L << workerIdBits);
    /**
     * 支持的最大数据标识ID,结果是31
     */
    private final long maxDataCenterId = ~(-1 << dataCenterIdBits);
    /**
     * 毫秒内序列在id中所占的位数
     */
    private final long sequenceBits = 12L;
    /**
     * 机器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;
    /**
     * 数据标识ID向左移17(12+5)位
     */
    private final long dataCenterIdShift = sequenceBits + workerIdBits;
    /**
     * 时间戳向左移22(12+5+5)位
     */
    private final long timestampShift = sequenceBits + workerIdBits + dataCenterIdBits;
    /**
     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = ~(-1L << sequenceBits);
    /**
     * 数据标识ID(0~31)
     */
    private long dataCenterId;
    /**
     * 机器ID(0~31)
     */
    private long workerId;
    /**
     * 毫秒内序列(0~4095)
     */
    private long sequence;
    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;

    public IdWorker(long dataCenterId, long workerId) {
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));
        }
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        this.dataCenterId = dataCenterId;
        this.workerId = workerId;
    }

    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return snowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        //如果是同一时间生成的,则进行毫秒内序列
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = nextMillis(lastTimestamp);
            }
        } else {//时间戳改变,毫秒内序列重置
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        //移位并通过按位或运算拼到一起组成64位的ID
        return ((timestamp - epoch) << timestampShift) |
                (dataCenterId << dataCenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long nextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = lastTimestamp;
        }
        return timestamp;
    } 
}

6. 百度UidGenerator
UidGenerator是百度开源的分布式ID生成器,基于于snowflake算法的实现,看起来感觉还行。不过,国内开源的项目维护性真是担忧。
具体可以参考官网说明:github.com/baidu/uid-g…
7. 美团Leaf
Leaf 是美团开源的分布式ID生成器,能保证全局唯一性、趋势递增、单调递增、信息安全,里面也提到了几种分布式方案的对比,但也需要依赖关系数据库、Zookeeper等中间件。
具体可以参考官网说明:tech.meituan.com/MT_Leaf.htm…

小结

这篇文章和大家分享了全局id生成服务的几种常用方案,同时对比了各自的优缺点和适用场景。在实际工作中,大家可以结合自身业务和系统架构体系进行合理选型。
欢迎大家加Q群:230419550 学习交流讨论架构师进阶知识