Spring Native 0.11发布,带来新的AOT引擎和性能优化

Spring Native 0.11已于2021年12月9日发布。

这个宏大的版本是Spring团队五个月辛勤工作的结果,他们一直在研究一个全新的架构,将让Spring使用GraalVM创建原生可执行文件的方式提升到一个新的水平。你目前已经可以已经在start.spring.io上试用了!

想了解有关Spring Native 0.11的更多信息,可以查看来自Spring布道师的新一期的Spring Tips视频(在YouTube上)。

新的AOT引擎

这个版本最大的变化无疑是引入了新的AOT引擎,该引擎在构建时对Spring程序进行深入的转化和分析,并生成所需的GraalVM Native配置。这些转换由Maven和Gradle Spring AOT插件执行。

spring boot native

更深入地说,AOT引擎在构建时评估构建环境,以便生成专门为您的应用程序优化后的 application context 和 Spring factories(Spring Boot背后的插件系统)。在实践中,这意味着:

  • 在运行时执行的 Spring 基础结构更少
  • 在运行时要判断的条件更少
  • 减少反射,因为使用的是编程式bean注册

AOT 引擎根据标记为活动的 Bean、Spring 编程模型的知识以及与 Spring Native 捆绑在一起或由应用程序本身提供的native hint,来推断出将应用程序编译为本机可执行文件所需的native configuration。

aot architecture

阅读更多

MySQL解决ONLY_FULL_GROUP_BY的几个方法

问题

employee 示例数据库为例,测试环境用了一条语句:

SELECT * FROM employees GROUP BY gender;

在测试环境运行正常,但是在线上就会有问题,报错如下:

1
2
3
4
5
6
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'mse.msc_k8s_cluster.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)

原因

MySQL 在ONLY_FULL_GROUP_BY模式下,有如下约束:

执行了带 GROUP BY 和 ORDER BY的SELECT语句,就需要保证你 SELECT 的列都在 GROUP BY 和 ORDER BY 中。

打个比方,如果有数据如下:

emp_no gender
1 M
2 M
阅读更多

在Java的类型系统中,数组有什么缺陷吗?

2020年2月,王垠吐槽了下Java的类型系统,说:

关于程序员对 Java 类型系统的理解,比较高级的一个面试问题是这样:

王垠原版的代码
1
2
3
4
5
6
public static void f() {
String[] a = new String[2];
Object[] b = a;
a[0] = "hi";
b[1] = Integer.valueOf(42);
}

这段代码里面到底哪一行错了?为什么?如果某个 Java 版本能顺利运行这段代码,那么如何让这个错误暴露得更致命一些?
注意这里所谓的「错了」是本质上,原理上的。

那么这儿的“错误”是指什么呢?

TL;DR

如果只能用一句话回答这个问题的话,那么就是:

Java数组不支持泛型,破坏了Java的类型安全性

类型系统的一些前提

一个好的类型系统,能够尽可能早的检测出错误,比如你将一个String赋值给int变量的时候,编译器就会报错,而不是等程序跑起来再报错。

阅读更多

尝鲜Go 1.18中范型版本的map和slice

大家最近都关注到了Go 1.18会支持范型的消息了吧。

作为Golang的内置类型,大家都期待map和slice支持范型后,可以简化很多的判断逻辑,比如Equal逻辑等等。

几天前,Go范型的标准库已经提交了,且可以试用了:

大家也可以读一下对应的代码:https://cs.opensource.google/go/x/exp/+/master:maps/maps.go

废话不多说,我们看下如何尝试范型版本的map和slice吧!

如何使用Go 1.18?

Golang 官网链接只有1.17版本的下载,那么我们如何才能使用1.18版本的Golang呢?

网上翻了翻,有人提供了Golang 1.18版本的Docker镜像 seongwoohong/golang-nightly:1.18,而且保证维护到1.18版本正式发布:

阅读更多

如何通过Kubernetes事件来报告错误

组内有维护一个Kubernetes Webhook,可以拦截pod的创建请求,并做一些修改(比如添加环境变量、添加init-container等)。

业务逻辑本身很简单,但是如果过程中产生错误,就很难处理。要不直接阻止pod创建,那么就有可能导致应用无法启动。要么忽略业务逻辑,那么就会导致静默失败,谁也不知道这儿出现了一个错误。

于是,朴素的想法就是接入告警系统,但这会导致当前组件和具体的告警系统耦合起来。

在Kubernetes中,有Event机制,可以做到把一些事件,比如警告、错误等信息记录下来,就比较适合这个场景。

什么是Kubernetes中的事件/Event?

事件(Event)是 Kubernetes 中众多资源对象中的一员,通常用来记录集群内发生的状态变更,大到集群节点异常,小到 Pod 启动、调度成功等等。

比如我们Describe一个pod,就能看到这个pod对应的事件:

kubectl describe pod sc-b-68867c5dcb-sf9hn

可以看到,从调度、到启动、再到这个pod最终拉取镜像失败,都会通过event的方式记录下来。

阅读更多

基于内存通信的gRPC调用

Apache Dubbo 有injvm方式的通信,能够避免网络带来的延迟,同时也不占用本地端口,对测试、本地验证而言,是一种比较方便的RPC通信方式。

最近看到 containerd 的代码,发现它也有类似的需求。
但使用ip端口通信,有可能会有端口冲突;使用unix socket,可能会有路径冲突。
考察了下gRPC有没有和injvm类似的,基于内存的通信方式。后来发现pipe非常好用,所以记录了下。

Golang/gRPC对网络的抽象

首先,我们先看一下gRPC一次调用的架构图。当然,这个架构图目前只关注了网络抽象分布。

我们重点关注网络部分。

操作系统系统抽象

首先,在网络包之上,系统抽象出来了socket,代表一条虚拟连接,对于UDP,这个虚拟连接是不可靠的,对于TCP,这个链接是尽力可靠的。

对于网络编程而言,仅仅有连接是不够的,还需要告诉开发者如何创建、关闭连接。
对于服务端,系统提供了accept方法,用来接收连接。
对于客户端,系统提供了connect方法,用于和服务端建立连接。

Golang抽象

阅读更多

如何通过抓包来查看Kubernetes API流量

当我们通过kubectl来查看、修改Kubernetes资源时,有没有想过后面的接口到底是怎样的?有没有办法探查这些交互数据呢?

Kuberenetes客户端和服务端交互的接口,是基于http协议的。所以只需要能够捕捉并解析https流量,我们就能看到kubernetes的API流量。

但是由于kubenetes使用了客户端私钥来实现对客户端的认证,所以抓包配置要复杂一点。具体是如下的结构:

如果想了解更多Kubernetes证书的知识,可以看下这篇Kubernetes证书解析的文章

从kubeconfig中提取出客户端证书和私钥

kubeconfig中包含了客户端的证书和私钥,我们首先要把它们提取出来:

1
2
3
4
5
6
7
8
9
10
11
12
# 提取出客户端证书
grep client-certificate-data ~/.kube/config | \
awk '{ print $2 }' | \
base64 --decode > client-cert.pem
# 提取出客户端私钥
grep client-key-data ~/.kube/config | \
awk '{ print $2 }' | \
base64 --decode > client-key.pem
# 提取出服务端CA证书
grep certificate-authority-data ~/.kube/config | \
awk '{ print $2 }' | \
base64 --decode > cluster-ca-cert.pem

参考自Reddit

配置Charles代理软件

阅读更多

Dockershim即将被删除,你准备好了吗?

重点:1.24版本的k8s将不会自带dockershim,即默认不支持docker作为容器运行时了。现在可以给Kubernetes反馈相关信息了:https://forms.gle/svCJmhvTv78jGdSx8。

Mirantis单独维护了dockershim组件,仍然可以使用:https://github.com/Mirantis/cri-dockerd


正文:

去年我们宣布Dockershim已被弃用:Dockershim弃用FAQ。我们目前的计划是尽快将dockershim从Kubernetes代码库中移除。我们正在寻求您的反馈,您是否已经准备好移除dockershim,并确保您已经准备好了。请填写这个调查问卷:https://forms.gle/svCJmhvTv78jGdSx8

Docker作为Kubernetes容器运行时的dockershim组件已被弃用,取而代之的是直接使用容器运行时接口(CRI)的运行时。许多Kubernetes用户已经顺利地迁移到其他容器运行时。然而,我们看到dockershim仍然很受欢迎。您可能会在DataDog最近的容器报告中看到一些公开统计数据。一些Kubernetes供应商最近才启用了其他运行时支持(特别是Windows节点)。我们知道许多第三方工具供应商还没有准备好:迁移遥测和安全代理

在这一点上,我们相信Docker和其他运行时之间的特性是等价的。许多终端用户已经使用了我们的迁移指南,并且正在使用这些不同的运行时运行生产工作负载。现在的计划是,dockershim将在明年4月发布的1.24版本中被移除。对于那些Kubernetes开发者或运行alpha和beta版本的人,dockershim将在12月开始的1.24发布周期被移除。

只有一个月的时间给我们反馈。我们想让你告诉我们你准备好了多少。

为了更好地了解dockershim移除的准备工作,我们的调查问卷会询问您当前使用的Kubernetes版本,以及您认为采用Kubernetes 1.24的估计时间。所有关于dockershim移除的汇总信息最终将被公开。一些评论将由SIG Node来审查过滤。如果您想讨论从dockershim迁移的任何细节、报告bug或阻碍点,您可以在在任何时候联系SIG Node:https://github.com/kubernetes/community/tree/master/sig-node#contact

Kubernetes是一个成熟的项目。dockershim的移除是为了摆脱“永久的beta特性”并提供更强的稳定性和兼容性保证。通过dockershim的迁移,你将获得更多的灵活性和容器运行时的选项,减少了对特定底层技术的依赖。请花时间查看dockershim迁移文档,并咨询Kubernetes供应商有哪些容器运行时选项可供您选择。阅读有关如何使用容器和CRI-O的容器运行时文档,以帮助您在升级到1.24时做好准备。

阅读更多

sockfwd 一个数据转发的小工具

最近在看containerd的代码,上手试的时候才发现它监听的是unix socket,没法从外部访问containerd。
而我要验证的是从远端能不能访问containerd、管理containerd的容器,所以需要一个从远端访问unix socket的工具。

网上搜了一圈,没有现成的实现,就自己写了 sockfwd

用法

1
2
3
4
5
6
7
Usage:
sockfwd [flags]

Flags:
-d, --destination string 目的地址,即要转发到的地址
-s, --source string 源地址,即接收请求的地址
-q, --quiet 静默模式

例子

将本地的containerd实例暴露到网络上:

./sockfwd -s tcp://127.0.0.1:8090 -d unix:///var/run/containerd.sock

将本地的127.0.0.1:8080端口暴露到0.0.0.0:8090端口上:

./sockfwd -s tcp://127.0.0.1:8090 -d unix://127.0.0.1:8090

将本地的服务暴露到网络上,需要格外注意是否有安全隐患!

阅读更多