通过 k8s 源码运行一个集群的操作在 github 上有对应的文档,我这里主要记录一下过程中遇到的坑。
运行环境只能是 linux, 之前我看 Docker Desktop 里面可以在 mac 直接启动 k8s 集群,还以为官方提供的脚本也可以,配置好了环境后以为好了,运行一个 demo:
./cluster/kubectl.sh run my-nginx --image=nginx --port=80
查看 pod 详情
./cluster/kubectl.sh describe pod my-nginx
失败了
报错:no nodes available to schedule pods
查看一下 node
./cluster/kubectl.sh get nodesNo resources found
仔细观察 /hack/local-up-cluster.sh 脚本的输出
kubelet is not currently supported in darwin, kubelet aborted.kubelet is not currently supported in darwin, kube-proxy aborted.
查了一下,https://github.com/kubernetes/kubernetes/issues/62940 这个脚本确实不能在 mac 上运行 kubelet 和 kube-proxy。
那我只能去 找一个 linux 环境了。
这个报错是在准备好 debian 的 k8s 开发环境后,运行 make WHAT=cmd/kubectl
构建时遇到的
这个报错是在 debian 系统报错的,报错原因看着是 shell 脚本传递的参数太多了,相同的源码,在 mac 上没报错。运行 git status
显示本地文件没有更改。
运行 getconf ARG_MAX
命令,查看参数长度限制
Mac 是 1048576 ,debian 是 2097152,dibian 比 mac 长,但是mac 构建成功了,debian 却失败了?
我决定查一下是哪个命令报错的,给 make 命令加上 -d 选项,并且设置 DBG_MAKEFILE 变量,打印处理详细的执行信息
make -d DBG_MAKEFILE=1
输出
// 忽略无关内容Reading makefile '.make/go-pkgdeps.mk' (search path) (no ~ expansion)...Makefile.generated_files:109: ***** finding all *.go dirsmake[1]: execvp: /usr/bin/env: Argument list too long
发现第一次报错是在 Makefile.generated_files:109 后面,看了一下上下文脚本,这里是把项目里面所所有 go 文件的文件名保存到了.make/all_go_dirs.mk
,然后下面的命令还出现了把文件内容作为参数传入命令行的操作,初步怀疑是这里超长了。
随便看了一下,发现有文件名重复了,有很多文件出现了两次,有一次多了个前缀 kubernetes, 一看根目录,确实有一个 kubernates 目录,里面是另一份源码。删除源码后,再次 make就成功了。
这个问题是多个因素导致的,刚开始,我没有把 k8s 源码放在 $GOPATH/src/k8s.io
里面,操作完回想了一下,老版本的 k8s 源码基于 GOPATH, 位置不对现在的版本代码运行正常,但是切换到老版本就运行不了了。
于是我直接运行 mv kubernetes $GOPATH/src/k8s.io/kubernetes
,把clone 好的文件夹移动过去了,然后到了新文件夹下 git status
后,没有更改,就以为移动成功了。
我运行的 mv 命令,如果 $GOPATH/src/k8s.io 目录下不存在 kubernetes 文件,那么就会把文件夹移动到 $GOPATH/src/k8s.io 下,如果已经有一个 $GOPATH/src/k8s.io/kubernetes 目录了,就会放在这个目录下面。所以我 mv 的代码实际去到了 $GOPATH/src/k8s.io/kubernetes/kubernetes 目录下。
那为什么 git status
看不出来文件夹里面多了一个文件夹呢?看了一下 .gitignore, 里面确实把根目录下的 kubernetes 文件夹加进去了。
cat .gitignore| grep kubernetes/kubernetes/# Downloaded kubernetes binary release tar ballkubernetes.tar.gz
到这里这个问题就清楚了,前几天我已经放了一份源码到这个 debian 电脑了,但是忘记了,重新 clone 一份后 mv , 因为已经有一个 kubernetes 文件夹了,所以新源码被放到了根目录。然后刚好 k8s 的 .gitignore 把 kubernetes 文件夹加进去了,git 命令看着没有任何文件。构建的时候,脚本收集了项目的所有文件名,并且作为参数传给其他 shell 命令,两份项目的文件名超过了系统参数长度限制。
最新版 k8s 要求 18.2 , 我的版本不够,升级一下。
wget https://go.dev/dl/go1.18.4.linux-amd64.tar.gztar -zxvf go1.18.4.linux-amd64.tar.gz# 可能你的go位置和我的不一样,使用 which go 查看sudo mv /usr/local/lib/go /usr/local/lib/go.baksudo mv go /usr/local/lib/
脚本需要用 root 权限运行
每次启动构建没必要,可以先 make all
,然后启动集群的时候使用 sudo ./hack/local-up-cluster.sh -O
sudo ./hack/local-up-cluster.sh -O
首先我已经按照文档安装了etcd, https://github.com/kubernetes/community/blob/master/contributors/devel/development.md#etcd
运行命令如下
sudo ./hack/local-up-cluster.sh -O
然后确认 bash zsh 打开新窗口都能找到 etcd 了,但是运行的时候就是报 etcd must be in your PATH
搜索内容,发现是 hack/lib/etcd.sh:31 这个地方报出来的,启动脚本 这个地方调用了验证,hack/local-up-cluster.sh:1137 。
在调用前打印一下环境变量
echo `env`
发现 path 没了,又在脚本开头确认了一下,还是没有 path
检查了一下
sudo visudo
发现内容里面有Defaults env_reset
,这里设置成了 sudo 的时候不继承环境变量了。
可以改成Defaults !env_reset
,或者在暂时在 sudo 后加 env PATH=$PATH
新的运行命令
sudo env PATH=$PATH hack/local-up-cluster.sh -O
这次可以找到了
集群终于起来了, node 也 ok 了
./cluster/kubectl.sh get nodesNAME STATUS ROLES AGE VERSION127.0.0.1 Ready <none> 3h18m v1.25.0-alpha.3.129+12b22ede416177-dirty
运行了一个 pod, ./cluster/kubectl.sh run my-nginx --image=nginx --port=80
查看情况,./cluster/kubectl.sh describe pod my-nginx,发现报错了 unknown service runtime.v1alpha2.RuntimeService
找到了这个 issue https://github.com/containerd/containerd/issues/4581 ,跟着一通配置也不行,后面看https://github.com/kubernetes/kubernetes/issues/73189,需要重新安装一下containerd cri,https://github.com/containerd/containerd,
wget https://github.com/containerd/containerd/releases/download/v1.5.11/containerd-1.5.11-linux-amd64.tar.gzsudo tar -C / -xzf cri-containerd-cni-1.5.11-linux-amd64.tar.gz containerd config default > /etc/containerd/config.tomlsystemctl enable containerd --nowsystemctl restart containerd
我是在这里下载的containerd https://github.com/containerd/containerd/releases/tag/v1.5.11
保险起见,重启 docker
sudo service docker restart# 发现启动失败了systemctl status docker.service# Failed to start docker.service: Unit docker.service is masked.
解决
systemctl unmask docker.servicesystemctl unmask docker.socketsystemctl start docker.service
然后再尝试,报错变了
sudo dockerd --debug
这个就是镜像被墙了,需要配置代理或者镜像,这里需要注意的是,我给 docker 配置好镜像源后, docker pull k8s.gcr.io/pause:3.5
,没问题了,k8s 启动还是会报错,这里网上的资料基本是给 docker 配置后就可以了,最新版还不行。
后面才知道 还要给 containerd 配置一下,配置后还是报错,failed to do request: Head "https://k8s.gcr.io/v2/pause/manifests/3.5": dial tcp 142.250.157.82:443: connect: connection timed out
直接给 containerd 配置代理,问题解决。
参考:https://segmentfault.com/a/1190000020363043
再次尝试 启动 pod , 发现 报错。
unable to retrieve OCI runtime error xxx/log.json: no such file or directory
看了Shashank V的回答https://stackoverflow.com/questions/59544107/docker-error-response-from-daemon-oci-runtime-create-failed-unable-to-retriev,试了一下 docker run hello-world
,也在报错,那应该是 docker 的问题。
再找了一下找到这个 issue https://github.com/moby/moby/issues/35906
是缺少 libseccomp 库
下载地址: https://packages.debian.org/zh-cn/sid/amd64/libseccomp2/download
wget http://ftp.us.debian.org/debian/pool/main/libs/libseccomp/libseccomp2_2.5.4-1+b1_s390x.debsudo dpkg -i libseccomp2_2.5.4-1+b1_s390x.deb
安装后 docker run hello-world
,正常了。
下载某些文件的时候再终端设置了 http 代理,运行 apt 会报错,把代理 unset 掉后不报错了。
最终,通过源码构建一个集群并且运行一个 pod 成功
https://github.com/kubernetes/community/blob/master/contributors/devel/development.md
https://github.com/kubernetes/community/blob/master/contributors/devel/running-locally.md
https://blog.csdn.net/yj1499945/article/details/80944203
https://mdnice.com/writing/3e3ec25bfa464049ae173c31a6d98cf8
https://blog.csdn.net/u011403655/article/details/50524071
https://blog.csdn.net/warrior_0319/article/details/79713155
https://segmentfault.com/a/1190000020363043
https://github.com/moby/moby/issues/35906
https://github.com/containerd/containerd/issues/4581
]]>随着 Kubernetes 的日趋成熟,其已经基本成为了容器编排调度框架的标准。各大厂基本都基于 Kubernetes 构建自己的容器编排平台。
Kubernetes 首当其冲成为一项必备技能,面试时用于吹牛提升自身竞争力,工作时能将开发与运维双剑合璧,减少开发与运维之间相互扯皮,避免被甩锅。当然了,Kubernetes 本身就是自带马甲的强 buff,后端工程师要想升级打怪,Kubernetes 简直就是助攻神器。
本文将整体概述 Kubernetes 的实现原理和架构,让大家对 Kubernetes 的原理和架构有一个具体的认识。
此文的最后为大家提供了一个 Kubernetes 在线体验地址,大家可以参考本文提供的技术教学步骤,体验一下 Kubernetes 具体实践。
Kubernetes,又称为 k8s(首字母为 k、首字母与尾字母之间有 8 个字符、尾字母为 s,所以简称 k8s)或者简称为 "kube" ,是一种可自动实施 Linux 容器操作的开源平台。
是一个生产级别的容器编排系统。
在物理服务器上运行应用程序。
缺点:
容器类似于 VM,但是容器之间可以共享操作系统(OS)。容器比 VM 更轻量级。
容器的一些优势:
容器是打包和运行应用程序的好方式。在生产环境中, 你需要管理运行着应用程序的容器,并确保服务不会下线。例如,如果一个容器发生故障,则你需要启动另一个容器。如果此行为交由给系统处理,是不是会更容易一些?
这就是 Kubernetes 要来做的事情!Kubernetes 为你提供了一个可弹性运行分布式系统的框架。Kubernetes 会满足你的扩展要求、故障转移、部署模式等。例如,Kubernetes 可以轻松管理系统的 Canary 部署。
Kubernetes 为你提供:
Kubernetes 可以使用 DNS 名称或自己的 IP 地址来曝露容器。如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。
Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。
你可以使用 Kubernetes 描述已部署容器的所需状态, 它可以以受控的速率将实际状态更改为期望状态。例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。
Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM)。当容器指定了资源请求时,Kubernetes 可以做出更好的决策来为容器分配资源。
Kubernetes 将重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器, 并且在准备好服务之前不将其通告给客户端。
Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。
kubectl 是 Kubernetes 官方提供的命令行工具,用户可以 通过kubectl以命令行交互的方式对 Kubernetes 进行操作。
所有服务访问的唯一入口,提供认证、授权、访问控制、API 注册和发现等机制。
Kube-apiserver 负责将 Kubernetes 的资源以 RESTful 风格的形式对外暴露并提供服务。Kubernetes 集群中的所有组件都通过 kube-apiserver 组件操作资源对象。kube-apiserver组件也是集群中唯一与 Etcd 集群 进行交互的核心组件。拥有丰富的集群安全访问机制,以及认证、授权及准入控制器。
负责确保 Kubernetes 系统的实际状态收敛到 所需状态。
kube-controller-manager 组 件 负责管理 Kubernetes 集群中的节点 (Node)、Pod副本、服务、端点(Endpoint)、命名空间 (Namespace)、服务账户(ServiceAccount)、资源定额 (ResourceQuota)等。例如,当某个节点意外宕机时,Controller Manager会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。
负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上。
kube-scheduler 组件监控整个集群的 Pod 资源对象和Node资源对 象,当监控到新的 Pod资源对象时,会通过调度算法为其选择最优节点。调度算法分为两种,分别为预选调度算法和优选调度算法。
负责维护容器的生命周期,同时也负责 Volume 和网络的管理。
kubelet组件用来接收、处理、上报kube-apiserver组件下发的任务。kubelet进程启动时会向kube-apiserver注册节点自身信息。它主要负 责所在节点(Node)上的Pod资源对象的管理,例如Pod资源对象的创 建、修改、监控、删除、驱逐及Pod生命周期管理等。
负责为 Service 提供 cluster 内部的服务发现和负载均衡。
是一个简单的网络访问代理,同时也是一个 Load Balancer。它负责将访问到某个服务的请求具体分配给工作节点上同一类标签的 Pod。kube-proxy 实质就是通过操作防火墙规则(iptables或者ipvs)来实现 Pod 的映射。
kube-proxy 组 件 是 参 与 管 理 Pod-to-Service 和 External-to- Service网络的最重要的节点组件之一。
键值对数据库,保存了整个集群的状态。
是一种高一致的分布式键值存储,它提供了一种可靠的方式来存储需要由分布式系统或机器集群访问的数据。它在网络内优雅地处理领导者选举,并且可以容忍机器故障,就算是领导者故障也不会影响高一致性。
Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。
Pod : 豌豆荚
Pod: 是一组(一个或多个) 容器。这些容器共享存储、网络、以及怎样运行这些容器的声明。
运行一个 nginx
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
要创建上面显示的 Pod,请运行以下命令:
kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml
使用 kubectl describe pod nginx 查看 Pod 详细信息
可以看到 Pod 的 ip 是 172.18.0.6, 访问这个地址, curl 172.18.0.6
Kubectl 读取 yaml 文件,向 APIServer 发起请求。
服务一般是多节点的,我们会通过负载来扩容缩容。
使用 ReplicaSet 来扩缩容示例:
apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: guestbook tier: frontend spec: *# 按你的实际情况修改副本数 * replicas: 3 selector: matchLabels: tier: frontend template: metadata: labels: tier: frontend spec: containers: - name: php-redis image: gcr.io/google_samples/gb-frontend:v3
kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml
过一会运行
kubectl get pods
就可以看到部署了3个 以 frontend 开头的 pod。
然后再查看 pod 情况
发现原先的 h476q 已经没了,又出来一个新的 hb8x8。
YAML kubectl label pod frontend-hb8x8 tier=backend --overwrite
然后查看 pod 情况
发现 Pod 数量从原来的 3 个变成了 4 个。
所有 ReplicaSet 对象的增删改查都是由 ReplicaSetController 控制器完成的,该控制器会监听 ReplicaSet 的变更。
Replication Controller 的核心作用是确保在任何时候集群中一个RC所关联的Pod 副本数量
保持预设值。如果发现 Pod 副本数量超过预期值,则 Replication Controller 会销毁一些Pod 副本;
Controller Manager 里面的 Controller 原理类似, 它们通过API Server 提供的接口实时监控整个集群里的每个资源对象的当前状态,当发生各种故障导致系统状态发生变化时,会尝试着将系统状态从“现有状态”修正到“期望状态”。
第一个示例中,创建 ReplicaSet 之前,满足带有 tier: frontend label 条件的 pod 数量是 0 个,Controller Manager 发现期望状态是 3 个,就会按模板创建出 3 个Pod。
第二个示例中,我们删除了一个 Pod, Controller Manager 发现期望状态是 3 个,但是实际值是 2 个,就会再根据模板创建一个新的 Pod。
第三个示例中,我们修改了一个 Pod 的 label, 导致它不满足 ReplicaSet 的 selector 条件:tier: frontend。Controller Manager 根据 selector 规则来统计数量时发现只有 2 个(期望 3 个),就会根据模板再创建一个 Pod,总共就有 4 个 Pod 了。
在线实践 Kubernetes
https://kubernetes.io/zh-cn/docs/tutorials/kubernetes-basics/deploy-app/deploy-interactive/
https://labs.play-with-k8s.com/
《Kubernetes权威指南:从Docker到Kubernetes实践全接触(第2版)》
《Kubernetes源码剖析》
https://kubernetes.io/zh-cn/docs/concepts/overview/what-is-kubernetes/
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/
https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/replicaset/
https://www.cnblogs.com/yanjieli/p/11792737.html
]]>go test -c
会生成xx.test
的二进制文件。
将文件上传到线上测试环境后,运行这个test文件即可进行单元测试。但是大多数时候我们只想运行其中一个测试用例,可以使用
./xx.test -test.run "TestUnitReport$" -test.v
运行指定测试用例。
]]>Kefa decided to make some money doing business on the Internet for exactly n days. He knows that on the i-th day (1 ≤ i ≤ n) he makes a**i money. Kefa loves progress, that's why he wants to know the length of the maximum non-decreasing subsegment in sequence a**i. Let us remind you that the subsegment of the sequence is its continuous fragment. A subsegment of numbers is called non-decreasing if all numbers in it follow in the non-decreasing order.
Help Kefa cope with this task!
Input
Output
Print a single integer — the length of the maximum non-decreasing subsegment of sequence a.
Examples
Input
62 2 1 3 4 1
Output
3
Input
32 2 9
Output
3
Note
In the first test the maximum non-decreasing subsegment is the numbers from the third to the fifth one.
In the second test the maximum non-decreasing subsegment is the numbers from the first to the third one.
简单题,直接输入的时候判断是不是比上一个小,如果是,重新计数;如果否,则计数。
#include <cstdio>#include <algorithm>using namespace std;int main() { int n; scanf("%d", &n); int last,in,curLen =1,result=1; scanf("%d", &last); for (int i = 1; i < n; ++i) { scanf("%d", &in); if (in >= last){ curLen++; } else{ curLen = 1; } result = max(result,curLen); last = in; } printf("%d\n", result); return 0;}
]]>
将资源暴露为新的Kubernetes Service。
指定deployment、service、replica set、replication controller或pod ,并使用该资源的选择器作为指定端口上新服务的选择器。deployment 或 replica set只有当其选择器可转换为service支持的选择器时,即当选择器仅包含matchLabels组件时才会作为暴露新的Service。
$ expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]
比如,可以为前面的 nginx-app 创建一个service:
$ kubectl expose deployment nginx-app --port=80 --target-port=80 --type=NodePortservice "nginx-app" exposed
查看service信息。
$ kubectl describe service nginx-appName:nginx-appNamespace:defaultLabels:run=nginx-appSelector:run=nginx-appType:NodePortIP:10.254.30.106Port:<unset>80/TCPNodePort:<unset>31069/TCPEndpoints:172.17.0.2:80Session Affinity:NoneNo events.
然后就可以使用访问nginx.
扩容或缩容 Deployment、ReplicaSet、Replication Controller或 Job 中Pod数量。
scale也可以指定多个前提条件,如:当前副本数量或 --resource-version ,进行伸缩比例设置前,系统会先验证前提条件是否成立。
通过修改 Deployment 中副本的数量(replicas),可以动态扩展或收缩应用。这些自动扩展的容器会自动加入到 service 中,而收缩回收的容器也会自动从 service 中删除。
$ kubectl scale --replicas=3 deployment/nginx-appdeployment "nginx-app" scaled$ kubectl get deployNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGEnginx-app 3 3 3 3 3d$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 3dnginx-app-2007220645-24w6n 1/1 Running 0 10hnginx-app-2007220645-6f66z 1/1 Running 0 4dnginx-app-2007220645-8mjdk 1/1 Running 0 10h
执行指定ReplicationController的滚动更新。
该命令创建了一个新的RC, 然后一次更新一个pod方式逐步使用新的PodTemplate,最终实现Pod滚动更新。
$ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2
在滚动升级的过程中,如果发现了失败或者配置错误,还可以随时回滚:
$ kubectl rolling-update frontend-v1 frontend-v2 --rollback
需要注意的是,kubectl rolling-update
只针对 ReplicationController。对于更新策略是 RollingUpdate 的 Deployment(Deployment 可以在 spec 中设置更新策略为 RollingUpdate,默认就是 RollingUpdate),更新应用后会自动滚动升级:
spec: replicas: 3 selector: matchLabels: run: nginx-app strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate
配置应用资源。
使用这些命令能帮你更改现有应用资源一些信息。
而更新应用的话,就可以直接用 kubectl set
命令:
$ kubectl set image deployment/nginx-app nginx-app=nginx:1.9.1deployment "nginx-app" image updated
滚动升级的过程可以用 rollout
命令查看:
$ kubectl rollout status deployment/nginx-appWaiting for rollout to finish: 2 out of 3 new replicas have been updated...
这里我的卡住了
$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 3dnginx-app-2007220645-24w6n 1/1 Running 0 10hnginx-app-2007220645-6f66z 1/1 Running 0 4dnginx-app-616742947-97f9h 0/1 ImagePullBackOff 0 19mnginx-app-616742947-j62g8 0/1 ErrImagePull 0 19m
pull镜像的时候失败
$ kubectl describe pod...... 18m3m5{kubelet 127.0.0.1}spec.containers{nginx-app}WarningFailedFailed to pull image "nginx:1.9.1": net/http: request canceled 18m3m5{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "nginx-app" with ErrImagePull: "net/http: request canceled" 18m1m23{kubelet 127.0.0.1}spec.containers{nginx-app}NormalBackOffBack-off pulling image "nginx:1.9.1" 18m1m23{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "nginx-app" with ImagePullBackOff: "Back-off pulling image \\"nginx:1.9.1\\"" 19m49s6{kubelet 127.0.0.1}spec.containers{nginx-app}NormalPullingpulling image "nginx:1.9.1"
最后设置了代理,拉取成功。
$ kubectl rollout status deployment/nginx-appdeployment "nginx-app" successfully rolled out
$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 3dnginx-app-2007220645-24w6n 1/1 Running 0 10hnginx-app-2007220645-6f66z 1/1 Running 0 4dnginx-app-2007220645-8mjdk 1/1 Running 0 10h
]]>
可以方便的创建一个容器(实际上创建的是一个由 deployment 来管理的 Pod)。
$ kubectl run --image=nginx:alpine nginx-app --port=80deployment "nginx-app" created
查询资源列表
用下面的命令查询刚刚创建的pod状态。
$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx-app-2007220645-6f66z 0/1 ContainerCreating 0 15s
这里一般情况下过一会READY
状态会变成1/1,STATUS会变成RUNNING。不过我当时是长时间没变。然后我去干其他事情,回来后状态变成了ImagePullBackOff。
NAME READY STATUS RESTARTS AGEnginx-app-2007220645-6f66z 0/1 ImagePullBackOff 0 33m
输出指定的一个/多个资源的详细信息。
$ kubectl describe podName:nginx-app-2007220645-6f66zNamespace:defaultNode:127.0.0.1/127.0.0.1Start Time:Wed, 08 Apr 2020 00:05:03 +0800Labels:pod-template-hash=2007220645run=nginx-appStatus:PendingIP:172.17.0.2Controllers:ReplicaSet/nginx-app-2007220645Containers: nginx-app: Container ID: Image:nginx:alpine Image ID: Port:80/TCP State:Waiting Reason:ImagePullBackOff Ready:False Restart Count:0 Volume Mounts:<none> Environment Variables:<none>Conditions: TypeStatus Initialized True Ready False PodScheduled True No volumes.QoS Class:BestEffortTolerations:<none>Events: FirstSeenLastSeenCountFromSubObjectPathTypeReasonMessage ------------------------------------------------------------ 33m33m1{default-scheduler }NormalScheduledSuccessfully assigned nginx-app-2007220645-6f66z to 127.0.0.1 23m23m1{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "POD" with ErrImagePull: "image pull failed for registry.access.redhat.com/rhel7/pod-infrastructure:latest, this may be because there are no credentials on this request. details: (net/http: request canceled)" 21m21m1{kubelet 127.0.0.1}WarningMissingClusterDNSkubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy. 17m17m1{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "nginx-app" with ErrImagePull: "Get https://registry-1.docker.io/v2/library/nginx/manifests/sha256:ef2b6cd6fdfc6d0502b77710b27f7928a5e29ab5cfae398824e5dcfbbb7a75e2: net/http: TLS handshake timeout" 17m17m1{kubelet 127.0.0.1}spec.containers{nginx-app}WarningFailedFailed to pull image "nginx:alpine": Get https://registry-1.docker.io/v2/library/nginx/manifests/sha256:ef2b6cd6fdfc6d0502b77710b27f7928a5e29ab5cfae398824e5dcfbbb7a75e2: net/http: TLS handshake timeout 15m15m1{kubelet 127.0.0.1}spec.containers{nginx-app}WarningFailedFailed to pull image "nginx:alpine": Get https://registry-1.docker.io/v2/library/nginx/manifests/alpine: net/http: TLS handshake timeout 15m15m1{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "nginx-app" with ErrImagePull: "Get https://registry-1.docker.io/v2/library/nginx/manifests/alpine: net/http: TLS handshake timeout" 18m9m3{kubelet 127.0.0.1}spec.containers{nginx-app}WarningFailedFailed to pull image "nginx:alpine": net/http: request canceled 18m9m3{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "nginx-app" with ErrImagePull: "net/http: request canceled" 6m6m1{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "nginx-app" with ErrImagePull: "Get https://registry-1.docker.io/v2/: net/http: TLS handshake timeout" 6m6m1{kubelet 127.0.0.1}spec.containers{nginx-app}WarningFailedFailed to pull image "nginx:alpine": Get https://registry-1.docker.io/v2/: net/http: TLS handshake timeout 18m1m45{kubelet 127.0.0.1}spec.containers{nginx-app}NormalBackOffBack-off pulling image "nginx:alpine" 18m1m45{kubelet 127.0.0.1}WarningFailedSyncError syncing pod, skipping: failed to "StartContainer" for "nginx-app" with ImagePullBackOff: "Back-off pulling image \\"nginx:alpine\\"" 21m53s7{kubelet 127.0.0.1}spec.containers{nginx-app}NormalPullingpulling image "nginx:alpine"
百度搜索k8s Get https://registry-1.docker.io/v2: net/http: TLS handshake timeout"
,按上面的方法dig @114.114.114.114 registry-1.docker.io
然后把获取到的ip填入hosts文件。
sudo gedit /etc/hosts
然后实验了一下sudo docker pull nginx:alpine
是没问题的。
然后看了一下pod状态,Running了。
$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx-app-2007220645-6f66z 1/1 Running 0 19h
执行 容器命令
查看容器内进程
$ kubectl exec nginx-app-2007220645-6f66z ps auxPID USER TIME COMMAND 1 root 0:00 nginx: master process nginx -g daemon off; 7 nginx 0:00 nginx: worker process 8 nginx 0:00 nginx: worker process 9 nginx 0:00 nginx: worker process 10 nginx 0:00 nginx: worker process 11 root 0:00 ps aux
可以看到nginx已经起来了。
查看容器详细信息。
$ kubectl describe pod nginx-app-2007220645-6f66zName:nginx-app-2007220645-6f66zNamespace:defaultNode:127.0.0.1/127.0.0.1Start Time:Wed, 08 Apr 2020 00:05:03 +0800Labels:pod-template-hash=2007220645run=nginx-appStatus:RunningIP:172.17.0.2Controllers:ReplicaSet/nginx-app-2007220645Containers: nginx-app: Container ID:docker://9b3245d69aeb71052bfd95cbd3fbb62942d9e2dd868c6b7a0dee9b7e84831766 Image:nginx:alpine Image ID:docker-pullable://docker.io/nginx@sha256:abe5ce652eb78d9c793df34453fddde12bb4d93d9fbf2c363d0992726e4d2cad Port:80/TCP State:Running Started:Wed, 08 Apr 2020 19:49:33 +0800 Ready:True Restart Count:0 Volume Mounts:<none> Environment Variables:<none>Conditions: TypeStatus Initialized True Ready True PodScheduled True No volumes.QoS Class:BestEffortTolerations:<none>Events: FirstSeenLastSeenCountFromSubObjectPathTypeReasonMessage ------------------------------------------------------------ 19h14m8{kubelet 127.0.0.1}spec.containers{nginx-app}NormalPullingpulling image "nginx:alpine" 19h13m2{kubelet 127.0.0.1}WarningMissingClusterDNSkubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy. 13m13m1{kubelet 127.0.0.1}spec.containers{nginx-app}NormalPulledSuccessfully pulled image "nginx:alpine" 13m13m1{kubelet 127.0.0.1}spec.containers{nginx-app}NormalCreatedCreated container with docker id 9b3245d69aeb; Security:[seccomp=unconfined] 13m13m1{kubelet 127.0.0.1}spec.containers{nginx-app}NormalStartedStarted container with docker id 9b3245d69aeb
可以看到pod的IP为172.17.0.2
使用curl
或者浏览器访问
成功出来nginx欢迎页。
输出pod中一个容器的日志。
$ kubectl logs nginx-app-2007220645-6f66z172.17.0.1 - - [08/Apr/2020:12:03:44 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" "-"172.17.0.1 - - [08/Apr/2020:12:03:44 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" "-"2020/04/08 12:03:44 [error] 8#8: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "172.17.0.2"
先创建一个名为nginx.yaml
的文件。
apiVersion: v1kind: Podmetadata: name: nginx labels: app: nginxspec: containers: - name: nginx image: nginx ports: - containerPort: 80
然后使用下面的命令创建pod
$ kubectl create -f nginx.yamlpod "nginx" created$ kubectl get podsNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 3mnginx-app-2007220645-6f66z 1/1 Running 0 20h
第二个pod也创建好了。
$ kubectl describe pod nginx...IP:172.17.0.3...
可以看到,这个也是ok的。
]]>访问https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
参考里面的教程操作,win10安装Docker for Windows配置比较简单。Docker Toolbox遇到的问题比较多。
]]>Error creating machine: Error in driver during machine creation: Unable to start the VM: C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe startvm default --type headless failed:VBoxManage.exe: error: The virtual machine 'default' has terminated unexpectedly during startup with exit code 1 (0x1). More details may be available in 'C:\\Users\\hjw\\.docker\\machine\\machines\\default\\default\\Logs\\VBoxHardening.log'VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component MachineWrap, interface IMachineLooks like something went wrong in step ´Checking if machine default exists´... Press any key to continue...
试了各种方法后发现是 VirtualBox 版本太低。
直接进入 VirtualBox 检查更新,会直接给一个链接。
这是我当时的最新版本
下载安装后正常启动。
]]>报错:
Error with pre-create check: "read tcp 192.168.43.151:49696->52.216.176.107:443: wsarecv: A socket operation was attempted to an unreachable network."Looks like something went wrong in step ´Checking if machine default exists´... Press any key to continue...
这里我下载好备份一下,以防以后要用。
下载好后放到C:\\Users\\hjw\\.docker\\machine\\cache
文件夹里面就行了。
然后在配置CDN的时候需要修改www和@的CNAME记录,提示“CNAME记录与MX记录冲突”。
了解了一下,CNAME优先级最高,所以在解析请求过程中,会优先返回CNAME解析记录结果,这样设置的结果导致用户无法请求到MX记录,直接对邮箱业务造成使用影响。
可通过 配置主机记录为www的CNAME记录指向CDN,再配置主机记录为@的URL转发指向主机记录为www的域名 即可解决主机记录为@的CNAME和MX记录冲突问题。
如下:
]]>Header
,Body
和Footer
<type>(<scope>): <subject>// 空一行<body>// 空一行<footer>
其中, Header 是必需的, Body 和 Footer 可以省略不管是哪一个部分, 任何一行都不得超过72个字符(或100个字符), 这是为了避免自动换行影响美观
Header部分只有一行,包括三个字段:type
(必需)、scope
(可选)和subject
(必需)
(1)type
type用于说明 commit 的类别,只允许使用下面7个标识
feat:新功能(feature)
fix:修补bug
docs:文档(documentation)
style: 格式(不影响代码运行的变动)
refactor:重构(即不是新增功能,也不是修改bug的代码变动)
test:增加测试
chore:构建过程或辅助工具的变动
如果type
为feat
和fix
,则该 commit
将肯定出现在 Change log
之中. 其他情况(docs、chore、style、refactor、test)由你决定,要不要放入 Change log,建议是不要。
(2)scope
scope用于说明commit
影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
(3)subject
subject 是 commit 目的的简短描述,不超过50个字符以动词开头,使用第一人称现在时,比如change , 而不是 changed 或 changes 第一个字母小写, 结尾不加句号(.)
##Body
Body 部分是对本次 commit 的详细描述, 可以分成多行. 下面是一个范例
More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. Further paragraphs come after blank lines.- Bullet points are okay, too- Use a hanging indent
有两个注意点:
(1)使用第一人称现在时, 比如使用change, 而不是changed或changes
(2)应该说明代码变动的动机. 以及与以前行为的对比
Footer 部分只用于两种情况。
(1)不兼容变动
如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE
开头,后面是对变动的描述、以及变动理由和迁移方法。
BREAKING CHANGE: isolate scope bindings definition has changed. To migrate the code follow the example below: Before: scope: { myAttr: 'attribute', } After: scope: { myAttr: '@', } The removed
inject
wasn't generaly useful for directives so there should be no code using it.
(2)关闭 Issue
如果当前 commit 针对某个issue, 那么可以在 Footer 部分关闭这个 issue
Closes #234
也可以一次关闭多个 issue
Closes #123, #245, #992
还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。
revert: feat(pencil): add 'graphiteWidth' optionThis reverts commit 667ecc1654a317a13331b17617d973392f415f02.
Body部分的格式是固定的,必须写成This reverts commit
如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前commit,会出现在 Change log 的Reverts 小标题下面。
参考资料:
]]>java.util.Map
是一个泛型接口。
public interface Map<K,V> {}
Map 接口可以将键映射到值,不能包含重复的键,每个键最多可以映射到一个值。
与之相似的是Dictionary
抽象类,不过接口使用起来更灵活一些。每天被面试官问的HashMap
实现了Map
接口,而HashTable
也实现了Map
接口,并且继承了Dictionary
抽象类。
Map
接口是用来代替Dictionary
抽象类的,接口更适合定义行为规范。
Map
接口有一个内部接口Interface Map.Entry<K,V>
。
此接口主要定义了对key和value的读写操作。
Map
提供了3种集合视图(键集,值集,键值对集),可以通过下面的方法获取。Map接口对于集合顺序没有要求,不过像TreeMap
的集合是有序的,而HashMap
的集合则无序。
Set<K> keySet();// 不可重复Collection<V> values();// 可重复Set<Map.Entry<K, V>> entrySet();// 不可重复
下面用HashMap
举一个例子:
HashMap<String, String> hashMap = new HashMap<String, String>() {{ put("blog", "hjwblog.com"); put("name", "hjw"); put("time", "2019/09/11"); }}; System.out.println("键集:"); Set<String> keySet = hashMap.keySet(); for (String key : keySet) { System.out.println(key); } System.out.println("键集:"); Collection<String> values = hashMap.values(); for (String value : values) { System.out.println(value); } System.out.println("键值对集"); Set<Map.Entry<String, String>> entrySet = hashMap.entrySet(); for (Map.Entry<String, String> entry : entrySet) { System.out.println(entry.getKey() + ":" + entry.getValue()); }
输出:
键集:nametimeblog键集:hjw2019/09/11hjwblog.com键值对集name:hjwtime:2019/09/11blog:hjwblog.com
int size();
此方法返回Map
中键值对的数量,如果数量超过Integer.MAX_VALUE
,返回Integer.MAX_VALUE
。
boolean isEmpty();
此方法返回Map
里面是否有键值对。
boolean containsKey(Object key);
判断是否包含对应的key。
此接口是可以把null当作键的,如果实现上不允许键为null,可以抛出NullPointerException
。
当key的类型是map实现类不支持的时候,则抛出ClassCastException
异常。
boolean containsValue(Object value);
判断是否包含对应的value值。
这个操作复杂度应该是O(n)的。
V get(Object key);
此方法返回指定key映射到的 value,如果该 Map 不包含该 key 的映射,则返回null。
如果 value 可以为 null ,此方法不能区分是否包含该键,都可能返回 null 。需要用containsKey
方法区分。
V put(K key, V value);
插入一个键值对。
如果存在此键了,会替换该键对应的值,顺便看了一下HashMap
的实现中,是会返回旧值的。
此处对于熟悉 C++ STL 的要注意了,C++ STL里面的 map 插入键值对如果键值对已存在,是不会更改旧值的,要判断返回值才知道是否插入成功,之前我在这里遇到了坑。
V remove(Object key);
移除 key 对应的键值对。
如果移除成功返回值,没有找到返回 null 。
void putAll(Map<? extends K, ? extends V> m);
此方法可以将一个 Map 的全部键值对添加到本 Map 中。
作用相当于遍历 m ,对每一个键值对调用一次 put 方法。
这里? extends K
指得是 K 或者 K 的子类。
相对的? super K
指得是 K 或者 K 的父类。
void clear();
清空Map
。
Set<K> keySet();
返回此Map
中包含的键的集合视图。
如果此时Map
被改变,则此Set
也会变,反之亦然,不过只支持移除操作,不能执行插入操作。
Set<String> keySet = hashMap.keySet();keySet.remove("name");
如上,对Set
执行remove方法,会导致Map
里面的键值对被删除。
这里有个注意事项,在遍历过程中对Map执行remove是会报错的。(虽然没人会这样写,但是这对ArrayList之类的也一样)
Iterator<String> it = keySet.iterator(); while (it.hasNext()){ String key = it.next(); if ("name".equals(key)){ keySet.remove(key); } }
上面的代码会抛出java.util.ConcurrentModificationException
异常。
正确写法:
Iterator<String> it = keySet.iterator(); while (it.hasNext()){ String key = it.next(); if ("name".equals(key)){ it.remove(); } }
虽然对于Map
删除元素只要调用remove方法,但是这里写这个例子主要是说明迭代过程中修改Map的正确方法。
Collection<V> values();
返回此Map
中包含的值的集合视图。
类似keySet
,如果此时Map
被改变,Set
也会变,反之亦然,不过只支持移除操作,不能执行插入操作。
其他方面和keySet
方法差不多。
Set<Map.Entry<K, V>> entrySet();
返回此Map
中包含的映射的集合视图。
其他方面和keySet
方法差不多。
boolean equals(Object o);
就是比较两个Map
是否相等的。
Map
相等的定义是m1.entrySet().equals(m2.entrySet())
。
int hashCode();
返回Map
的hash码。
Map
的哈希码定义为``entrySet()的所有entry的哈希码之和。
保证如果m1.equals(m2)
则m1.hashCode()==m2.hashCode()
。
default V getOrDefault(Object key, V defaultValue) { V v; return (((v = get(key)) != null) || containsKey(key)) ? v : defaultValue; }
这里使用了jdk8的新特性——default。
可以在接口实现一个方法。
此方法可以通过key
获取value
,如果key
不存在,则返回指定的默认值。
在某些特定时刻,此方法可以精简代码。
default void forEach(BiConsumer<? super K, ? super V> action) { Objects.requireNonNull(action); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } action.accept(k, v); } }
为Map
中的每个entry
执行给定的操作,直到处理完所有entry
或操作引发异常为止。
相当于:
for (Map.Entry<K, V> entry : map.entrySet()) action.accept(entry.getKey(), entry.getValue());Objects.requireNonNull(action);
Objects 类是jdk7出现的,提供了一些对对象的常用操作静态方法。
requireNonNull
如果判断参数是null
,则会抛出NullPointerException
。
hashMap.forEach(new BiConsumer<String, String>() { @Override public void accept(String key, String value) { System.out.println(key + ":" + value); } });
同样的,不要尝试在遍历时修改Map。
hashMap.forEach(new BiConsumer<String, String>() { @Override public void accept(String key, String value) { hashMap.remove(key); } });
这样会抛出ConcurrentModificationException
。
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { Objects.requireNonNull(function); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } // ise thrown from function is not a cme. v = function.apply(k, v); try { entry.setValue(v); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } } }
这个方法会对所有元素调用apply
方法,并将key
和value
作为参数,将返回值作为新value。
等价于:
for (Map.Entry<K, V> entry : map.entrySet()) entry.setValue(function.apply(entry.getKey(), entry.getValue()));
例子:
hashMap.replaceAll(new BiFunction<String, String, String>() { @Override public String apply(String key, String value) { if ("hjw".equals(value)){ return "hjw1"; } return value; } });
BiFunction
泛型接口第一个参数是key
的类型,第二个参数是value
的类型,第三个参数是新value
的类型,即为apply
方法的返回值。
default V putIfAbsent(K key, V value) { V v = get(key); if (v == null) { v = put(key, value); } return v; }
如果此key
对应的value为null
,则插入value
,否则不变。
default boolean remove(Object key, Object value) { Object curValue = get(key); if (!Objects.equals(curValue, value) || (curValue == null && !containsKey(key))) { return false; } remove(key); return true; }
这个方法只有key
和value
都匹配时才会移除键值对。
Ctrl+Shift+p
输入
configure display language
install additional languages
,安装中文.
再次重复上面的操作,选择zh-cn
重启后就是中午了。
由于 github 不能通过 TOC 生成目录,但是可以通过工具生成链接目录。
打开vscode应用市场。
搜索安装Auto Markdown TOC
。
然后就能生成目录了。
删除<!-- /TOC -->
注释。
查看效果。
]]>首先,我们找到 Spring 的 github 仓库https://github.com/spring-projects/spring-framework
。
复制链接。
打开 IDEA , 如下图操作。
然后粘贴刚刚复制的仓库地址,选择一个位置
等待 clone 代码。
然后导入。
等待导入依赖。
我看的是5.0.x,checkout一下。
这时候 gradle import 可能报下面的错误。
这是因为 Gradle 版本不兼容。
打开 gradle/wrapper/gradle-wrapper.properties
文件,这里显示 gradle 版本是4.4.1。
将设置里面 use Gradle from 改成 gradle-wrapper.properties
,IDEA 会自动下载需要的 Gradle。
导入包的过程很慢,可以配一下国内仓库来加快下载速度。打开build.gradle
文件。在repositories
标签里面加入maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
。
耐心等待 build。
然后根目录有一个import-into-idea.md
文件,需要运行一个任务。我们运行一下。
然后Reimport
一下。
最后是这个样子的。
sudo netstat -ntulp
-t
显示TCP端口
-u
显示UDP端口
-l
仅显示监听套接字
-p
显示进程标识符和程序名称
-n
不进行DNS轮询,显示IP
有时候公司 IDC 环境打开的端口数特别多,可以配合 grep
命令筛选。
下面的函数可以获取出错位置。
func fileLine() string {_, file, line, _ := runtime.Caller(1)return fmt.Sprintf("file:%s, line:%d \\n", file, line)}
效果如下
]]>环境是Ubuntu 16.04。
cvt 1920 1080xrandr --newmode "1920X1080_60.00" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsyncxrandr --addmode Virtual1 "1920X1080_60.00"xrandr --output Virtual1 --mode "1920X1080_60.00"
运行上面的命令就可以加一个1920X1080的分辨率。
重新启动可能会出现分辨率又没了,可以在/etc/profile
文件最后加入上面的命令。
pip3 install numpy
速度只有10k。这里下载一下python3 numpy安装文件备份一下,以后安装方便使用。numpy whl文件:numpy-1.16.4-cp35-cp35m-manylinux1_x86_64.whl
下载后运行命令
pip install numpy-1.16.4-cp35-cp35m-manylinux1_x86_64.whl
]]>
package com.hjwblog;import java.util.BitSet;public class BitSetTest {public static void main(String[] args) {// 创建一个10亿个元素的BitSetBitSet bitSet = new BitSet(1000000000);// 将偶数位设置为truefor (int i = 0; i < 1000000000; i++) {if ((i & 1) == 0) {bitSet.set(i);}}// 大小System.out.println("大小::" + bitSet.size() + "bit"); // 大小::1000000000bitSystem.out.println("大小:" + bitSet.size() / 8 / 1024 / 1024 + "m"); // 大小:119m// 返回此 BitSet中设置为 true的位数System.out.println(bitSet.cardinality()); // 500000000// 返回一个新的 BitSet,它由此 BitSet 中从 fromIndex(包括)到 toIndex(不包括)范围内的位组成BitSet bitSet2 = bitSet.get(0, 100);System.out.println(bitSet2);// 将此 BitSet 中的所有位设置为 falsebitSet.clear();System.out.println(bitSet.cardinality()); // 0}}
运行结果:
大小::1000000000bit大小:119m500000000{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98}0
]]>
package com.hjwblog;public class TaskRunnable implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().toString());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class SingleThreadExecutorTest {public static void main(String[] args) {ExecutorService singleTreadExecutor = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {singleTreadExecutor.execute(new TaskRunnable());}singleTreadExecutor.shutdown();System.out.println("shutdown");while (!singleTreadExecutor.isTerminated()) {}System.out.println("Finished all task");}}
运行结果:
shutdownThread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Finished all task
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class FixedThreadPoolTest {public static void main(String[] args) {ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {fixedThreadPool.execute(new TaskRunnable());}fixedThreadPool.shutdown();System.out.println("shutdown");// shutdown后再添加任务,会抛出RejectedExecutionException异常// fixedThreadPool.execute(new TaskRunnable());while (!fixedThreadPool.isTerminated()) {}System.out.println("Finished all tasks");}}
运行结果:
shutdownThread[pool-1-thread-3,5,main]Thread[pool-1-thread-2,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-4,5,main]Thread[pool-1-thread-5,5,main]Thread[pool-1-thread-3,5,main]Thread[pool-1-thread-2,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-5,5,main]Thread[pool-1-thread-4,5,main]Finished all tasks
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ChachedTreadPoolTest {public static void main(String[] args) {ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {cachedThreadPool.execute(new TaskRunnable());}cachedThreadPool.shutdown();System.out.println("shutdown");// shutdown后再添加任务,会抛出RejectedExecutionException异常// cachedThreadPool.execute(new TaskRunnable());while (!cachedThreadPool.isTerminated()) {}System.out.println("Finished all tasks");}}
运行结果:
Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-2,5,main]Thread[pool-1-thread-4,5,main]shutdownThread[pool-1-thread-5,5,main]Thread[pool-1-thread-6,5,main]Thread[pool-1-thread-3,5,main]Thread[pool-1-thread-7,5,main]Thread[pool-1-thread-8,5,main]Thread[pool-1-thread-9,5,main]Thread[pool-1-thread-10,5,main]Finished all tasks
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledFuture;import java.util.concurrent.TimeUnit;public class ScheduledTreadPoolTest {public static void main(String[] args) {ScheduledExecutorService scheduledTreadPool = Executors.newScheduledThreadPool(2);// 延迟3s后,每2s执行一次ScheduledFuture<?> scheduledFuture = scheduledTreadPool.scheduleAtFixedRate(new TaskRunnable(), 3, 2,TimeUnit.SECONDS);try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}scheduledFuture.cancel(true);System.out.println("cancel");}}
运行结果:
Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-2,5,main]Thread[pool-1-thread-2,5,main]cancel
这里要注意,取消任务时如果任务在sleep,会抛出异常,可以将延迟时间改为1试试。
Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]Thread[pool-1-thread-1,5,main]canceljava.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at com.hjwblog.TaskRunnable.run(TaskRunnable.java:9)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)
]]>