标签 容器 下的文章

minikube v1.20.0版本的一个bug

img{512x368}

本文永久链接 – https://tonybai.com/2021/05/14/a-bug-of-minikube-1-20

近期在研究dapr(分布式应用运行时),这是一个很朴素却很棒的想法,目前大厂,如阿里鹅厂都有大牛在研究该项目,甚至是利用dapr落地了部分应用。关于dapr,后续我也会用单独的文章详细说说。

dapr不仅支持k8s部署,还支持本地部署,并可以对接多个世界知名的公有云厂商的服务,比如:aws、azure、阿里云等。为了体验dapr对云原生应用的支持,我选择了将其部署于k8s中,同时我选择使用minikube来构建本地k8s开发环境。而本文要说的就是将dapr安装到minikube时遇到的问题。

1. 安装minikube

Kubernetes在4月份发布了最新的1.21版本,但目前minikube的最新版依然为1.20版本

minikube是k8s项目自己维护的一个k8s本地开发环境项目,它与k8s的api接口兼容,我们可以快速搭建一个minikube来进行k8s学习和实践。minikube官网上有关于它的安装、使用和维护的详尽资料。

我这里在一个ubuntu 18.04的腾讯云主机上(1 vcpu, 2g mem)上安装minikube v1.20,minikube是一个单体二进制文件,我们先将这个文件下载到本地:

# curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 60.9M  100 60.9M    0     0  7764k      0  0:00:08  0:00:08 --:--:-- 11.5M
# install minikube-linux-amd64 /usr/local/bin/minikube

验证是否下载ok:

# minikube version
minikube version: v1.20.0
commit: c61663e942ec43b20e8e70839dcca52e44cd85ae

接下来我们就利用minikube启动一个k8s cluster用作本地开发环境。由于minikube默认的最低安装要求为2核cpu,而我的虚机仅为1核,我们需要为minikube传递一些命令行参数以让其在单核CPU上也能顺利地启动一个k8s cluster。另外minikube会从gcr.io这个国内被限制访问的站点下载一些控制平面的容器镜像,为了能让此过程顺利进行下去,我们还需要告诉minikube从哪个gcr.io的mirror站点下载容器镜像:

# minikube start --extra-config=kubeadm.ignore-preflight-errors=NumCPU --force --cpus 1 --memory=1024mb --image-mirror-country='cn'
  minikube v1.20.0 on Ubuntu 18.04 (amd64)
  minikube skips various validations when --force is supplied; this may lead to unexpected behavior
  Automatically selected the docker driver. Other choices: ssh, none
  Requested cpu count 1 is less than the minimum allowed of 2
   has less than 2 CPUs available, but Kubernetes requires at least 2 to be available

  Your cgroup does not allow setting memory.
    ▪ More information: https://docs.docker.com/engine/install/linux-postinstall/#your-kernel-does-not-support-cgroup-swap-limit-capabilities

  Requested memory allocation 1024MiB is less than the usable minimum of 1800MB
  Requested memory allocation (1024MB) is less than the recommended minimum 1900MB. Deployments may fail.

  The requested memory allocation of 1024MiB does not leave room for system overhead (total system memory: 1833MiB). You may face stability issues.
  Suggestion: Start minikube with less memory allocated: 'minikube start --memory=1833mb'

  The "docker" driver should not be used with root privileges.
  If you are running minikube within a VM, consider using --driver=none:

https://minikube.sigs.k8s.io/docs/reference/drivers/none/

  Using image repository registry.cn-hangzhou.aliyuncs.com/google_containers
  Starting control plane node minikube in cluster minikube
  Pulling base image ...
    > registry.cn-hangzhou.aliyun...: 20.48 MiB / 358.10 MiB  5.72% 2.89 MiB p/
> registry.cn-hangzhou.aliyun...: 358.10 MiB / 358.10 MiB  100.00% 3.50 MiB
    > registry.cn-hangzhou.aliyun...: 358.10 MiB / 358.10 MiB  100.00% 3.50 MiB
    > registry.cn-hangzhou.aliyun...: 358.10 MiB / 358.10 MiB  100.00% 3.50 MiB
    > registry.cn-hangzhou.aliyun...: 358.10 MiB / 358.10 MiB  100.00% 6.83 MiB
  Creating docker container (CPUs=1, Memory=1024MB) ...
  Preparing Kubernetes v1.20.2 on Docker 20.10.6 ...
    ▪ kubeadm.ignore-preflight-errors=NumCPU
    ▪ Generating certificates and keys ...
    ▪ Booting up control plane ...
    ▪ Configuring RBAC rules ...
  Verifying Kubernetes components...
    ▪ Using image registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5 (global image repository)
  Enabled addons: default-storageclass, storage-provisioner

  /usr/local/bin/kubectl is version 1.17.9, which may have incompatibilites with Kubernetes 1.20.2.
    ▪ Want kubectl v1.20.2? Try 'minikube kubectl -- get pods -A'
  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

查看启动的k8s集群状态:

# minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

我们看到minikube似乎成功启动了一个k8s cluster。

2. pod storage-provisioner处于ErrImagePull状态

在后续使用helm安装redis作为state store组件(components)时,发现安装后的redis处于下面的状态:

# kubectl get pod
NAME               READY   STATUS    RESTARTS   AGE
redis-master-0     0/1     Pending   0          7m48s
redis-replicas-0   0/1     Pending   0          7m48s

通过kubectl describe命令详细查看redis-master-0这个pod:

# kubectl describe pod redis-master-0
Name:           redis-master-0
Namespace:      default
Priority:       0
Node:           <none>
Labels:         app.kubernetes.io/component=master
                app.kubernetes.io/instance=redis
                app.kubernetes.io/managed-by=Helm
                app.kubernetes.io/name=redis
                controller-revision-hash=redis-master-694655df77
                helm.sh/chart=redis-14.1.1
                statefulset.kubernetes.io/pod-name=redis-master-0
Annotations:    checksum/configmap: 0898a3adcb5d0cdd6cc60108d941d105cc240250ba6c7f84ed8b5337f1edd470
                checksum/health: 1b44d34c6c39698be89b2127b9fcec4395a221cff84aeab4fbd93ff4a636c210
                checksum/scripts: 465f195e1bffa9700282b017abc50056099e107d7ce8927fb2b97eb348907484
                checksum/secret: cd7ff82a84f998f50b11463c299c1200585036defc7cbbd9c141cc992ad80963
Status:         Pending
IP:
IPs:            <none>
Controlled By:  StatefulSet/redis-master
Containers:
  redis:
    Image:      docker.io/bitnami/redis:6.2.3-debian-10-r0
    Port:       6379/TCP
    Host Port:  0/TCP
    Command:
      /bin/bash
    Args:
      -c
      /opt/bitnami/scripts/start-scripts/start-master.sh
    Liveness:   exec [sh -c /health/ping_liveness_local.sh 5] delay=5s timeout=6s period=5s #success=1 #failure=5
    Readiness:  exec [sh -c /health/ping_readiness_local.sh 1] delay=5s timeout=2s period=5s #success=1 #failure=5
    Environment:
      BITNAMI_DEBUG:           false
      REDIS_REPLICATION_MODE:  master
      ALLOW_EMPTY_PASSWORD:    no
      REDIS_PASSWORD:          <set to the key 'redis-password' in secret 'redis'>  Optional: false
      REDIS_TLS_ENABLED:       no
      REDIS_PORT:              6379
    Mounts:
      /data from redis-data (rw)
      /health from health (rw)
      /opt/bitnami/redis/etc/ from redis-tmp-conf (rw)
      /opt/bitnami/redis/mounted-etc from config (rw)
      /opt/bitnami/scripts/start-scripts from start-scripts (rw)
      /tmp from tmp (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from redis-token-rtxk2 (ro)
Conditions:
  Type           Status
  PodScheduled   False
Volumes:
  redis-data:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  redis-data-redis-master-0
    ReadOnly:   false
  start-scripts:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      redis-scripts
    Optional:  false
  health:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      redis-health
    Optional:  false
  config:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      redis-configuration
    Optional:  false
  redis-tmp-conf:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
  tmp:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
  redis-token-rtxk2:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  redis-token-rtxk2
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  18s (x6 over 5m7s)  default-scheduler  0/1 nodes are available: 1 pod has unbound immediate PersistentVolumeClaims.

我们发现是该pod的PersistentVolumeClaims没有得到满足,没有绑定到适当PV(persistent volume)上。查看pvc的状态,也都是pending:

# kubectl get pvc
NAME                          STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
redis-data-redis-master-0     Pending                                      standard       35m
redis-data-redis-replicas-0   Pending                                      standard       35m

详细查看其中一个pvc的状态:

# kubectl describe  pvc redis-data-redis-master-0
Name:          redis-data-redis-master-0
Namespace:     default
StorageClass:  standard
Status:        Pending
Volume:
Labels:        app.kubernetes.io/component=master
               app.kubernetes.io/instance=redis
               app.kubernetes.io/name=redis
Annotations:   volume.beta.kubernetes.io/storage-provisioner: k8s.io/minikube-hostpath
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
VolumeMode:    Filesystem
Mounted By:    redis-master-0
Events:
  Type    Reason                Age                  From                         Message
  ----    ------                ----                 ----                         -------
  Normal  ExternalProvisioning  55s (x143 over 35m)  persistentvolume-controller  waiting for a volume to be created, either by external provisioner "k8s.io/minikube-hostpath" or manually created by system administrator

我们看到该pvc在等待绑定一个volume,而k8s cluster当前在default命名空间中没有任何pv资源。问题究竟出在哪里?

我们回到minikube自身上来,在minikube文档中,负责自动创建HostPath类型pv的是storage-provisioner插件:

img{512x368}

图:minikube插件使能情况

我们看到storage-provisioner插件的状态为enabled,那么为什么该插件没能为redis提供需要的pv资源呢?我顺便查看了一下当前k8s cluster的控制平面组件的运行情况:

# kubectl get po -n kube-system
NAMESPACE     NAME                                    READY   STATUS             RESTARTS   AGE
kube-system   coredns-54d67798b7-n6vw4                1/1     Running            0          20h
kube-system   etcd-minikube                           1/1     Running            0          20h
kube-system   kube-apiserver-minikube                 1/1     Running            0          20h
kube-system   kube-controller-manager-minikube        1/1     Running            0          20h
kube-system   kube-proxy-rtvvj                        1/1     Running            0          20h
kube-system   kube-scheduler-minikube                 1/1     Running            0          20h
kube-system   storage-provisioner                     0/1     ImagePullBackOff   0          20h

我们惊奇的发现:storage-provisioner这个pod居然处于ImagePullBackOff状态,即下载镜像有误!

3. 发现真相

还记得在minikube start命令的输出信息的末尾,我们看到这样一行内容:

Using image registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5 (global image repository)

也就是说我们从registry.cn-hangzhou.aliyuncs.com下载storage-provisioner:v5有错误!我手动在本地执行了一下下面命令:

# docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5

Error response from daemon: pull access denied for registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

居然真的无法下载成功!

究竟是什么地方出现问题了呢?从提示来看,要么是该镜像不存在,要么是docker login被拒绝,由于registry.cn-hangzhou.aliyuncs.com是公共仓库,因此不存在docker login的问题,那么就剩下一个原因了:镜像不存在!

于是我在minikube官方的issue试着搜索了一下有关registry.cn-hangzhou.aliyuncs.com作为mirror的问题,还真让我捕捉到了蛛丝马迹。

在https://github.com/kubernetes/minikube/pull/10770这PR中,有人提及当–image-mirror-country使用cn时,minikube使用了错误的storage-provisioner镜像,镜像的地址不应该是registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5,而应该是registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner:v5。

我在本地试了一下registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5,的确可以下载成功:

# docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner:v5
v5: Pulling from google_containers/storage-provisioner
Digest: sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944
Status: Image is up to date for registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner:v5
registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner:v5

4. 解决问题

发现问题真相:当–image-mirror-country使用cn时,minikube使用了错误的storage-provisioner镜像。那我们如何修正这个问题呢?

我们查看一下storage-provisioner pod的imagePullPolicy:

# kubectl get pod storage-provisioner  -n kube-system -o yaml
... ...
spec:
  containers:
  - command:
    - /storage-provisioner
    image: registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5
    imagePullPolicy: IfNotPresent
    name: storage-provisioner

我们发现storage-provisioner的imagePullPolicy为ifNotPresent,这意味着如果本地有storage-provisioner:v5这个镜像的话,minikube不会再去远端下载该image。这样我们可以先将storage-provisioner:v5下载到本地并重新tag为registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5。

下面我们就来操作一下:

# docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/storage-provisioner:v5 registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5

一旦有了image,通过minikube addons子命令重新enable对应pod,可以重启storage-provisioner pod,让其进入正常状态:

# minikube addons enable storage-provisioner

    ▪ Using image registry.cn-hangzhou.aliyuncs.com/google_containers/k8s-minikube/storage-provisioner:v5 (global image repository)
  The 'storage-provisioner' addon is enabled

# kubectl get po -n kube-system
NAME                               READY   STATUS    RESTARTS   AGE
coredns-54d67798b7-n6vw4           1/1     Running   0          25h
etcd-minikube                      1/1     Running   0          25h
kube-apiserver-minikube            1/1     Running   0          25h
kube-controller-manager-minikube   1/1     Running   0          25h
kube-proxy-rtvvj                   1/1     Running   0          25h
kube-scheduler-minikube            1/1     Running   0          25h
storage-provisioner                1/1     Running   0          69m

当storgae-provisioner恢复正常后,之前安装的dapr state component组件redis也自动恢复正常了:

# kubectl get pod
NAME               READY   STATUS    RESTARTS   AGE
redis-master-0     1/1     Running   0          18h
redis-replicas-0   1/1     Running   1          18h
redis-replicas-1   1/1     Running   0          16h
redis-replicas-2   1/1     Running   0          16h

“Gopher部落”知识星球正式转正(从试运营星球变成了正式星球)!“gopher部落”旨在打造一个精品Go学习和进阶社群!高品质首发Go技术文章,“三天”首发阅读权,每年两期Go语言发展现状分析,每天提前1小时阅读到新鲜的Gopher日报,网课、技术专栏、图书内容前瞻,六小时内必答保证等满足你关于Go语言生态的所有需求!部落目前虽小,但持续力很强。在2021年上半年,部落将策划两个专题系列分享,并且是部落独享哦:

  • Go技术书籍的书摘和读书体会系列
  • Go与eBPF系列

欢迎大家加入!

Go技术专栏“改善Go语⾔编程质量的50个有效实践”正在慕课网火热热销中!本专栏主要满足广大gopher关于Go语言进阶的需求,围绕如何写出地道且高质量Go代码给出50条有效实践建议,上线后收到一致好评!欢迎大家订
阅!

img{512x368}

我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网热卖中,欢迎小伙伴们订阅学习!

img{512x368}

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。2020年4月8日,中国三大电信运营商联合发布《5G消息白皮书》,51短信平台也会全新升级到“51商用消息平台”,全面支持5G RCS消息。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博:https://weibo.com/bigwhite20xx
  • 微信公众号:iamtonybai
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • “Gopher部落”知识星球:https://public.zsxq.com/groups/51284458844544

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

一文告诉你如何抢先体验Go泛型

img{512x368}

本文首发于我主持的“Gopher部落”知识星球,欢迎大家加入星球,一起学习Go语言!年底前8.8折优惠,不要错过哦!

2020年11月22日,Go核心开发团队技术负责人Russ Coxgolang-dev论坛上确认了Go泛型将在Go 1.18落地(2022.2):

img{512x368}

这对于那些迫切期盼go加入泛型的gopher来说无疑是一个重大利好消息!不过,泛型是把双刃剑!泛型的加入势必会让Go语言的复杂性大幅提升。我很是担心Go加入泛型后会像C++模板那样被“滥用”而形成很多奇技淫巧,这显然不是Go项目组想看到的。因此他们现在在宣传泛型时都是比较谨慎的。Robert GriesemerGopherCon 2020大会上演讲“Typing [Generic] Go”中明确给出了Go泛型的使用时机:

  • 可增强静态类型安全性的时候
  • 可以更高效的使用内存的时候
  • 可以(显著的)提升性能的时候

虽然这不能完全避免滥用,但至少表明了Go团队对泛型使用的态度。“能力越大,责任越大”,大家在使用泛型时务必三思而后行

现在,Go泛型已经处于“箭在弦上不得不发”的状态,作为Gopher,我们能做的就是拥抱它!

离Go 1.18发布还有一年多的时间,对于极其渴望支持泛型的gopher来说,这个时间有点长!好在Go项目组已经提供了一些抢先体验Go泛型语法的方法,这里我们就来全面介绍一下,小伙伴们可以根据自己的情况任选一种抢先体验Go泛型!

1. Go泛型在线playground

2020.6月末,Ian Lance Taylor和Robert Griesemer在Go官方博客发表了文章《The Next Step for Generics》,介绍了Go泛型工作的最新进展。同时,Go团队还推出了可以在线试验Go泛型语法的playground:https://go2goplay.golang.org:

img{512x368}

通过该在线playground,我们可以体验最新的Go泛型语法并查看编译和运行结果。

在线playground的好处就在于可以随时随地访问和体验,体验设备也不局限于计算机,甚至可以使用手机/平板终端。不过该playground在国内访问不畅,并且体验仅局限于单文件的形式,对于复杂一些的项目无法支持。

2. 基于源码编译出go2go工具

Go项目在dev.go2go分支上加入了Go泛型语法的实现,我们可以在本地基于Go项目源码构建出可以用于体验Go泛型的go2go工具。

要想构建go2go工具,我们首先就需要下载Go项目源码。但截至目前,Go项目仓库github.com/golang/go有45000多次提交,在国内以20k/s的速度clone这个仓库那是相当耗时费力,还不一定有好结果(经常断连,一断连,就要重新来过)。当然如果你有高速vpn则另当别论了。这里介绍一个下载github上Go项目源码的过渡方法:利用gitee(码云)建立Go仓库镜像库,然后从码云以2M/s速度下载。步骤如下:

  • 在gitee上建立一个公共仓库,比如:gitee.com/bigwhite/go,在建立仓库时选择“导入现有库”,填入现有库的地址:https:///github.com/golang.go.git,之后,强大的“码云”就会帮助我们快速同步了。

  • 之后我们就可以从码云clone这个仓库:gitee.com/bigwhite/go,2M/s的速度,一分钟内就完成clone。并且码云支持强制从源仓库github.com/golang/go同步最新更新到镜像仓库,十分方便。

$git clone https://gitee.com/bigwhite/go.git

既然我已经在码云建立的go仓库的镜像,各位小伙伴儿们就可以直接clone我的公共库(https://gitee.com/bigwhite/go)来获取go仓库源码了。

接下来,我们来构建go2go工具,主要步骤如下(当前环境为ubuntu,并已安装的go的版本为go 1.15.4 linux/amd64):

  • 切换到dev.go2go分支
// 进入下载后的go仓库源码目录(我这里为~/.bin/go)
$git checkout dev.go2go
Branch 'dev.go2go' set up to track remote branch 'dev.go2go' from 'origin'.
Switched to a new branch 'dev.go2go'

注:ubuntu需安装build-essential(apt-get install build-essential),否则在go源码编译过程可能会出现“fatal error: stdlib.h: No such file or directory”的错误。

  • 编译dev.go2go分支源码

Go源码编译是“一键式”的,并且速度非常快!进入到Go项目源码下的src目录(cd ~/.bin/go/src),执行下面命令:

$./all.bash 

Building Go cmd/dist using /root/.bin/go1.15.4. (go1.15.4 linux/amd64)
Building Go toolchain1 using /root/.bin/go1.15.4.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for linux/amd64.
... ...
ALL TESTS PASSED
---
Installed Go for linux/amd64 in /root/.bin/go
Installed commands in /root/.bin/go/bin
*** You need to add /root/.bin/go/bin to your PATH.

构建后的可执行文件go与gofmt被放在了bin目录下(~/go/bin),为方便使用,我们最好将其所在路径配置到PATH环境变量中。。

  • 验证构建结果
$go version
go version devel +440f144a10 Tue Nov 24 01:29:01 2020 +0000 linux/amd64

如果看到上面结果,说明构建是ok的。

接下来,我们就来使用构建出的go工具体验一下编译运行一个使用泛型语法编写的源文件sort.go2

// sort.go2

package main

import (
    "fmt"
    "sort"
)

type Lang struct {
    Name string
    Rank int
}

type sliceFn[T any] struct {
    s   []T
    cmp func(T, T) bool
}

func (s sliceFn[T]) Len() int           { return len(s.s) }
func (s sliceFn[T]) Less(i, j int) bool { return s.cmp(s.s[i], s.s[j]) }
func (s sliceFn[T]) Swap(i, j int)      { s.s[i], s.s[j] = s.s[j], s.s[i] }

func SliceFn[T any](s []T, cmp func(T, T) bool) {
    sort.Sort(sliceFn[T]{s, cmp})
}

func main() {
    langs := []Lang{
        {"rust", 2},
        {"go", 1},
        {"swift", 3},
    }

    SliceFn(langs, func(p1, p2 Lang) bool { return p1.Rank < p2.Rank })
    fmt.Println(langs) // [{go 1} {rust 2} {swift 3}]
}

go2go是以go tool的一个子命令形式存在的,它支持编译和运行以.go2为后缀的Go源文件,如果让它编译和运行.go文件,它会报如下错误:

$go tool go2go run sort.go
Go file sort.go was not created by go2go

编译运行上面的sort.go2的命令和结果如下:

$go tool go2go run sort.go2
[{go 1} {rust 2} {swift 3}]

有小伙伴可能会说,这个例子也是单一源文件,太简单!那我们接下来就整一个稍复杂些的。go2go这个子命令自带了一些复杂的Go泛型包,这些包的源码被放在了Go仓库源码的src/cmd/go2go/testdata下面:

$tree -LF 2 go2path
go2path
└── src/
    ├── alg/
    ├── chans/
    ├── constraints/
    ├── graph/
    ├── gsort/
    ├── list/
    ├── maps/
    ├── metrics/
    ├── orderedmap/
    ├── sets/
    └── slices/

go2go目前仅支持gopath mode,还不支持module-ware mode。go2go支持专用的GO2PATH环境变量用于指示GOPATH路径,也可以用传统的GOPATH环境变量。为了使用go2go自带的那些样例源码包,我们需要将GOPATH或GO2PATH设置为\$GOROOT/src/cmd/go2go/testdata/go2path。我们在go2path路径下建立我们的样例repo:

$tree -LF 5 go2path
go2path
└── src/
    │   ... ...
    ├── github.com/
    │   └── bigwhite/
    │       └── gsort-demo/
    │           └── demo.go2
    │   ... ...
    └── slices/
        ├── slices.go2
        └── slices_test.go2

// demo.go2
package main

import (
    "fmt"
    "gsort"
)

type Lang struct {
    Name string
    Rank int
}

func main() {
    langs := []Lang{
        {"rust", 2},
        {"go", 1},
        {"swift", 3},
    }

    gsort.SliceFn(langs, func(p1, p2 Lang) bool { return p1.Rank < p2.Rank })
    fmt.Println(langs)
}

我们可以用两种方法运行demo.go2:

// 设置GO2PATH:

~/.bin/go/src/cmd/go2go/testdata/go2path/src/github.com/bigwhite/gsort-demo$ GO2PATH=$GOROOT/src/cmd/go2go/testdata/go2path go tool go2go run demo.go2
[{go 1} {rust 2} {swift 3}]

或

// 设置GOPATH和关闭GO111MODULE:

~/.bin/go/src/cmd/go2go/testdata/go2path/src/github.com/bigwhite/gsort-demo$ GOPATH=$GOROOT/src/cmd/go2go/testdata/go2path GO111MODULE=off go tool go2go run demo.go2
[{go 1} {rust 2} {swift 3}]

通过源码构建go2go工具的方法是体验Go泛型最基本的方法,我们还可以定期更新Go项目源码以体验泛型草案的最新变化。我们还可以通过go doc cmd/go2go来查看go2go命令的文档。

3. 使用go2go的docker容器

如果觉得使用源码构建本地可用的go2go工具依然“门槛高”或者繁琐,那么可以利用一些gopher已经上传的现成的docker容器来构建使用了泛型语法的*.go2文件。这里使用的是levonet/golang:go2go

$docker pull levonet/golang:go2go
  • 使用go2go容器编译运行单个*.go2文件

我们还以上面那个sort.go2为例,该文件可以放在任意目录下,然后我们在该文件所在目录下执行下面命令即可编译运行它:

$ docker run --rm -v "$PWD":/go/src/myapp -w /go/src/myapp levonet/golang:go2go go tool go2go run sort.go2
[{go 1} {rust 2} {swift 3}]

这句docker run命令的含义是:将宿主机当前工作目录(即sort.go2所在目录)挂载到容器中的/go/src/myapp下面,并将/go/src/myapp作为当前工作目录,执行go tool go2go run sort.go2

对于复杂的如上面的github.com/bigwhite/gsort-demo的例子,通过docker容器一样可以编译,只不过命令复杂一些:

~/temp/github.com $docker run --rm -v "$PWD":/usr/local/lib/go/src/cmd/go2go/testdata/go2path/src/github.com -w /usr/local/lib/go/src/cmd/go2go/testdata/go2path/src/github.com/bigwhite/gsort-demo -e GO2PATH="/usr/local/lib/go/src/cmd/go2go/testdata/go2path" levonet/golang:go2go go tool go2go run demo.go2
[{go 1} {rust 2} {swift 3}]

我们将github.com目录放在任意目录下,比如:~/temp,然后将当前目录挂载到容器的/usr/local/lib/go/src/cmd/go2go/testdata/go2path/src/github.com目录下,设定工作目录为/usr/local/lib/go/src/cmd/go2go/testdata/go2path/src/github.com/bigwhite/gsort-demo,然后为容器新增以环境变量GO2PATH,这样我们就可以编译运行demo.go2了。

注1:容器中的GOROOT为/usr/local/lib/go

4. 使用Goland体验Go泛型

著名Go语言IDE产品goland也宣布支持体验最新的Go泛型语法,由于笔者很少使用图形化的IDE,因此各位小伙伴可自行通过这篇博客https://blog.jetbrains.com/go/2020/11/24/experimenting-with-go-type-parameters-generics-in-goland/来了解具体情况。

5. 参考资料

  • levonet/golang – https://hub.docker.com/r/levonet/golang
  • dev.go2go branch – https://go.googlesource.com/go/+/refs/heads/dev.go2go/README.go2go.md

“Gopher部落”知识星球开球了!高品质首发Go技术文章,“三天”首发阅读权,每年两期Go语言发展现状分析,每天提前1小时阅读到新鲜的Gopher日报,网课、技术专栏、图书内容前瞻,六小时内必答保证等满足你关于Go语言生态的所有需求!星球首开,福利自然是少不了的!2020年年底之前,8.8折(很吉利吧^_^)加入星球,下方图片扫起来吧!

我的Go技术专栏:“改善Go语⾔编程质量的50个有效实践”上线了,欢迎大家订阅学习!

img{512x368}

我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网热卖中,感谢小伙伴们学习支持!

img{512x368}

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

2020年4月8日,中国三大电信运营商联合发布《5G消息白皮书》,51短信平台也会全新升级到“51商用消息平台”,全面支持5G RCS消息。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博:https://weibo.com/bigwhite20xx
  • 微信公众号:iamtonybai
  • 博客:tonybai.com
  • github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats