1.1 | kubernetes: 是什么和为什么

WHAT of kubernetes一套基于容器技术的分布式架构方案。基于 Google 的 Borg(内部使用超过10年) 系统实现资源管理的自动化、资源利用的最大化使用 kubernetes 可以减少开发成本(如服务治理、服务监控、故障处理等),将精力投入到业务开发中。开放平台,不限编程语言,模
阅读更多

Golang:半个月从入门到辞退?

这不是标题党,这是一份学习笔记,记录遇到的问题,包括但不仅仅限于 golang 的语法格式、常用用法、神奇操作等。Update: 本以为一篇长文就可以搞定这个入门到辞退,感觉东西好多啊,还是分开多篇吧。关键字func编译报错,返回值要么都有名,要么都没有名称。func funcMui(x, y in
阅读更多

0 | kubernetes:学习路线导览与记录

在之前的后端开发中,多多少少接触过一些 kubernetes 的内容,但是并未深入了解,在接触到 golang 编程以及 CD 发布系统的情形下,知道了 kubernetes 的强大之后,便开始找机会系统了解 kubernetes,因此了解 kubernetes 是一种不可多得的提升自我的方式,不论是从工作上、还是自我提升。

本文路线主要参照此教程给出的建议,记录遇到的问题以及对 kubernetes 的认识。

https://kubernetes.io/

8月第一周

本周目标

  • Kubernetes 的背景
  • 安装 Kubernetes 环境
  • Kubernetes 基本概念和使用方法

为什么会出现 kubernetes

学习任何系统的之前,了解其出现的背景和意义都是必不可少的,为什么会出现 Kubernetes?它解决了什么问题?有没有其他类似的系统?这里推荐阅读才云科技 CEO 张鑫在 2017 年文章《从风口浪尖到十字路口,写在 Kubernetes 两周年之际》。

以简易方式安装 kubernetes

推荐使用 minikubekind 部署一个本地环境,然后开始部署一个”真实”的应用(minikube 安装需要使用科学上网,或使用“国内版” minikube)。如果想一开始就挑战更高难度的安装方式(不推荐),可以使用 kubeadm 或者手动部署所有组件。关于安装,可以参考文档 lab1-installation

kubernetes 资源与概念

推荐熟练使用以下常用资源和概念:Pod、Node、Label、Event、Service、Configmap & Secret、Deployment、Namespace。相关学习可以参考文档 lab2-application-and-service

(可选)仅完成上述内容可能还不足以让我们非常熟悉 Kubernetes 的基本概念,下面列出其他可以参考的资料,大家也可以按照自己的方式去搜索相关的资料:

预期达到效果

  • 反复加深对上面资源的操作熟练度。如果你是第一次接触 Kubernetes,或者仅了解过一点 Kubernetes 的知识,那么基(ken)本(ding)是不明白 Kubernetes 底层到底发生了什么。请不要心急,姑且把它当成一个黑盒工具即可 ?。
  • 你可能会在网上看到更多的概念,如 PVC、Ingress、Priority 等。炼气阶段,请不要尝试学习过多的资源类型。Kubernetes 有非常多的概念和类似的资源,我们这里熟悉最核心的概念即可,否则易走火入魔 ?,切记。当我们打通任督二脉之时,所有的新概念都不过尔尔。

1 | Spinnaker: Agent 缓存云厂商各项数据流程分析

在 clouddriver 中缓存云厂商的各项数据,是由这些 agent 所来完成。本文主要聚焦于何时执行、如何存储、以及存储后如何使用这三点上。Agent 从哪里来创建 agent为每个云账号的每个区域创建10个不同类型的 agent安排启动ProviderUtils.rescheduleAgen
阅读更多

1 | Dubbo:探讨标签路由的实现

项目需要,对 Dubbo 进行了一次功能调研,主要集中在服务治理中的标签路由。这部分的内容不难,但是能够对 Dubbo 的实现有一定的了解。

环境搭建

需要 ZooKeeper、Java Application Based on Dubbo。

ZooKeeper 的搭建

通过 CODING CD,在腾讯云的弹性伸缩组上(有兴趣可到 CODING CD 中详细了解),部署的一个实例,需要先安装 java 依赖,如下:

1
java-1.8.0-openjdk-devel.x86_64

其主要的配置是一个脚本,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.6.1/apache-zookeeper-3.6.1-bin.tar.gz
tar -xzvf apache-zookeeper-3.6.1-bin.tar.gz
mv apache-zookeeper-3.6.1-bin zk
mv zk/conf/zoo_sample.cfg zk/conf/zoo.cfg

echo '''[Unit]
Description=ZooKeeper for Dubbo
After=network.target
[Service]
Type=forking
ExecStart=/bin/sh /root/zk/bin/zkServer.sh start
ExecStop=/bin/sh /root/zk/bin/zkServer.sh stop
User=root
[Install]
WantedBy=multi-user.target ''' > /usr/lib/systemd/system/zk-for-dubbo.service
systemctl enable zk-for-dubbo

在安全组中确保端口 2181 开放。此时:

  • 公网 IP 为 49.233.238.170
  • 内网 IP 为 172.21.0.34

Java Application Based on Dubbo

A 模块在调用 B 模块前,设置了一个 TAG_KEY,表示选择带有 TAG_KEY 的 B 服务:

1
2
3
4
5
6
7
@Reference
private DoSomeThingService doSomeThingService;

public String sayHello() {
RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, "tag1");
return doSomeThingService.sayHello();
}

应用的部署还是采用 CODING CD 部署在腾讯云的弹性伸缩组上。主要的配置如下:

A 模块启动:

1
java -jar -Ddubbo.registry.address=zookeeper://172.21.0.34:2181 /root/a.jar 2>&1 &

B 模块启动:

1
java -jar -Ddubbo.registry.address=zookeeper://172.21.0.34:2181 /root/b.jar 2>&1 &

Dubbo admin 安装与配置

dubbo-admin 使用的 github 中 develop 的最新版,按照相应的提示进行构建即可。

启动 dubbo-admin 的前端,此时的目录为 dubbo-admin/dubbo-admin-ui

1
npm run dev

启动 dubbo-admin 的后端,此时的目录为 dubbo-admin/dubbo-admin-server

修改 dubbo-admin/dubbo-admin-server/src/main/resources/application.properties 文件,改成正确的 zookeeper 地址。

1
2
3
admin.registry.address=zookeeper://49.233.238.170:2181
admin.config-center=zookeeper://49.233.238.170:2181
admin.metadata-report.address=zookeeper://49.233.238.170:2181

编译并启动

1
2
3
mvn clean package -DskipTests=true
cd target
java -jar dubbo-admin-server-0.2.0-SNAPSHOT.jar

打开 dubbo-admin,找到标签路由,配置如下,应用名填写 moduleb:

1
2
3
4
5
6
7
8
9
enabled: true
force: true
runtime: true
tags:
- name: tag1
addresses:
- '172.21.0.40:20880'
- name: tag2
addresses: null

TIPS
如果有遇到在 Dubbo Admin 中修改不生效的情况,考虑一下 Dubbo Admin 与 Dubbo 版本间的差异。在此踩到一个坑如下:

Dubbo Admin 管理页面是用 docker 跑起来的,它里面的源代码比较旧,路由设置到 zk 中的路径与 dubbo 读取 zk 的路径不一样。ISSUE 可见:https://github.com/apache/dubbo-admin/issues/577

效果展示

请求流向:LB -> A -> B

A 中设置的 TAG_KEY 为:

1
2
3
4
public String sayHello() {
RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, "tag1");
return doSomeThingService.sayHello();
}

此时 B 模块服务有两个,即一个新、一个旧。

如果要只返回最新版本,可在 dubbo-admin 控制台的服务治理中,添加标签路由,新的实例在 tag1 下,旧的实例在 tag2 下,如下:

1
2
3
4
5
6
7
8
9
10
enabled: true
force: true
runtime: true
tags:
- name: tag1
addresses:
- '172.21.0.21:20880'
- name: tag2
addresses:
- '172.21.0.40:20880'

此时的返回如下:

如果只使用旧版本实例,可在将上面 tag1 和 tag2 下面的内容调换即可,此时返回如下:

如果要同时能够访问到新旧实例的内容,将上述配置中所有的 IP 全部填写到 tag1 下即可,此时的返回如下:
image

Dubbo 标签路由的实现原理

总共分三个大的方面,分别是配置的存储、配置同步、如何解析配置。

标签路由配置的存储

URL: /api/dev/rules/route/tag/moduleb
Request Method: PUT

在 dubbo-admin-server 中它的实现在文件 TagRoutesController.java 中,最终是将配置保存到 ZooKeeper 中,保存的路径为:

即:/dubbo/config/dubbo/moduleb.tag-router

在 ZooKeeper 中可以在相应的路径下看到相关的配置,如下:

配置变更同步

TagRouter.java 中实现了 ConfigurationListener 接口,当配置变更时,会更新 tagRouterRule

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public synchronized void process(ConfigChangedEvent event) {
if (logger.isDebugEnabled()) {
logger.debug("Notification of tag rule, change type is: " + event.getChangeType() + ", raw rule is:\n " +
event.getContent());
}

try {
if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
this.tagRouterRule = null;
} else {
this.tagRouterRule = TagRuleParser.parse(event.getContent());
}
} catch (Exception e) {
logger.error("Failed to parse the raw tag router rule and it will not take effect, please check if the " +
"rule matches with the template, the raw rule is:\n ", e);
}
}

配置解析

源码位于 dubbo 中的 TagRouter.java

结语

在上面的示例中,我们需要手动在 dubbo-admin 界面中,新增标签路由的配置,但经过对该配置的新增过程的简要分析,发现只是在 ZooKeeper 中新增一条记录。

如何用 random9 求 random10

大意

如果有一个 random9() 函数,可以产生 1-9 的 整数随机数,请利用 random9 实现 random10 函数,返回 1-10 的整数随机数

要点

random9 只能随机产生 1-9 范围内的数,也就是 1-9 出现的概率是一样的;而如果要随机产生 1-10 范围的整数,就需要 1-10 出现的概率是一样的。

所以问题就变成了:如何通过等概率出现的 1-9 产生等概率的 1-10 ?

解决

以一种马后炮的思想尝试解释一下,看能不能说服自己。

random9 显然产生不了 10,所以肯定需要用到加/乘法,那如何产生 10?

  • random9 + n
    如果只用加法,这样便可以随机产生[n+1, n+9]范围的整数,可以达到10,但是肯定不能产生[1, 10]范围内的数,所以这样不行。

  • random9 * n
    如果只用乘法,这样便可以随机产生[n, 2n, 3n, …, 9n],这样产生的数显然也不可能做到随机产生[1, 10],因为中间存在空位,并且无法保证是等概率的。

要想等概率产生[1, 10]范围的数,可以利用等概率产生[1, 20]范围的整数,然后%10 + 1。这里有个重点,怎么样才能等概率?

既然 random9 加/乘一个固定的数,不能达到目的,那么如果加/乘一个不固定但是等概率出现的某一范围的数(也就是 random9)是否可行呢?

  • random9 + random9
    能出现的最大数:9 + 9 = 18;能出现的最小数:1 + 1 = 2
    先考虑能否等概率出现10,先看2-18是否为等概率出现的。
    出现2、18的概率都是1/81,但出现3的概率是2/81,即1+2和2+1,这个就不是等概率出现的了,所以不能做到随机产生。
  • random9 * random9
    最大数:81,最小数:1。
    是否连续?否。
    是否等概率?否。最大、小数的概率是1/81。但是9的概率是3/81。
  • random9 * n + random9
    random9 * n 可等概率产生 1n,2n,3n…9n,如果加上 random9,是否等概率取决于1n和2n之间的间距,如果刚好等于9,那么加上random9就有希望产生连续、并且等概率的整数,范围为:[10, 90]。

如何通过等随机产生的 [10, 90] 来随机产生 [1, 10]?
将随机产生的 [10, 90] 限制为 [10, 89],然后模10,便可以获得随机产生的[0, 9],然后再加1,即可得到随机产生的[1, 10]。

总结为:先通过 random9 * 9 + random9 随机产生[10, 90]范围的整数,然后限制随机产生 [10, 89],再模10,得到[0, 9],再加1即可得到[1, 10]。

所以最终的代码:

1
2
while ((result = 9 * random9() + random9()) >= 90);
return result % 10 + 1;

但是,我看到的答案是这样的,还不太明白这两者之间有什么差别。

1
2
3
4
5
6
7
public int rand10() {
int result;
do {
result = rand9()+(rand9()-1)*9;
} while (result > 80);
return 1 + (result - 1) % 10;
}

更加简单粗暴的方式

但是这个问题是由 random7 求 random10,不过思想是相通的。

感觉这一种方式更加通俗易懂,获取一维数组是随机的,获取第二维数组也是随机,整个过程都是随机的。

1
2
3
4
5
6
7
8
9
10
11
12
public static int random10() {
int[][] seed = { { 1, 2, 3, 4, 5, 6, 7 }, { 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 11 }, { 22, 23, 24, 25, 26, 27, 28 },
{ 29, 30, 31, 32, 33, 34, 35 }, { 36, 37, 38, 39, 40, 41, 42 },
{ 43, 44, 45, 46, 47, 48, 49 } };

int result;
do {
result = seed[random7() - 1][random7() - 1];
} while (result > 40);
return result % 10 + 1;
}

linux中的pushd、popd与dirs

最近在项目中,看到一些模块的启动脚本中,有一些 pushd、popd等操作。之前并没有接触过这类命令,但是目测它是与目录相关的,因为都是操作完了目录之后,才能运行启动命令。dirs展示目录栈。什么是目录栈?使用cd命令进入一个目录后,该目录会存放进以个栈中,当前目录永远位于栈顶。dirs 可以用来查
阅读更多

JUC包下同步工具类及Condition队列

在上篇关于从ReentrantLock看JUC中AQS的这篇文章中,留下了一个非常重要的Condition模块,并未去分析。而这个模块,在实现BlockingQueue的过程中,用到了。因此特地回过头来,去补习一下关于Condition的实现与原理、以及JUC下面其他的同步工具类的使用。Condit
阅读更多

Java中的Queue之概述

对技术还是得有敬畏之心,总觉得Queue好像没啥,其实只是没有仔细去了解过。不过自从上次认真地看了线程池的源代码之后,发现Queue是一个很神奇的集合类。Queue的形式有无界、有界,还有堵塞、非堵塞。初略想想,这个实现可能就不简单。一个问题在线程池中,自定义线程池时,放入什么样的队列可以让线程数达
阅读更多