网站首页 > java教程 正文
本章涵盖
- 选择具有特定硬件属性的节点
- 使用污点和容忍度来管理具有特殊硬件的节点上的调度行为
- 在独立节点上保持工作负载分离
- 避免单点故障的高可用性部署策略
- 在一个节点上将一些 Pod 分组,同时避免包含特定其他 Pod 的节点
到目前为止,本书将集群中的计算节点——负责实际运行您的容器的机器——视为平等。不同的 Pod 可能请求更多或更少的 CPU,但它们在底层都运行在相同类型的节点上。
云计算的一个基本特性是,即使您正在使用一个抽象平台(如 Kubernetes 平台)来处理大部分低级计算资源的配置,您仍然可能在某种程度上关心实际运行您工作负载的服务器。无服务器是一种不错的概念,但归根结底,工作负载是在计算机上运行的,您无法总是逃避该机器的特性,也并不总是想要逃避。
这就是节点特征选择的作用。在一个托管平台上,包括谷歌 Kubernetes 引擎(GKE),节点有多种不同的硬件和配置选项。节点的 CPU 可以是 x86 架构或 Arm。它可能是 AMD 或 Intel。如果需要,节点可以配备昂贵的硬件,如 GPU,或者可以以低价的 Spot 供应模式运行,节省资金但可能面临中断的风险。您可能并不总是需要关注这些元素,但它们可能很有用,比如通过 Spot 节省资金,或者在您需要 GPU 来运行 AI/ML 工作负载时至关重要。
另一个需要注意的方面是,Kubernetes 在同一节点上运行多个 Pod,这种技术称为装箱(bin-packing)。在同一硬件上运行多个容器可以帮助节省成本,特别是在突发情况下,如果某个 Pod 没有使用其分配的容量,另一个 Pod 可以暂时使用它。装箱的缺点是可能出现单点故障。幸运的是,Kubernetes 提供了一种内置方法,称为 Pod 分布拓扑(pod spread topology),以避免在单个节点上集中相同 Pod 的副本。在本章中,您将学习如何根据特性选择节点,如何将 Pod 组合在一起以及如何将它们分开。
8.1 节点特征选择
并非所有计算节点都是相同的。您可能有需要额外硬件的工作负载,比如更高性能的 CPU 和 GPU,或者像在 Spot 供应模型中运行的属性。一些节点运行 Linux,而另一些则运行 Windows。一些 CPU 使用 x86 架构;其他则使用 Arm 等等。就像过去我们可能将工作负载放置在具有特定功能的机器上一样,我们可以通过节点选择和亲和性在 Kubernetes 中做到这一点。
8.1.1 节点选择器
节点特性在 Kubernetes 中通过节点标签进行区分。您从 Pods 中针对特定节点特性的方式是使用节点选择或节点亲和性。节点选择和亲和性只是表达您的 Pods 所需节点的期望标签(因此也是特性)的方法。
例如,考虑一个需要在基于 Arm 的节点上运行的 Pod。基于 Arm 的节点被标记为众所周知的标签 kubernetes.io/arch: arm64 (众所周知的标签是指在开源中定义的,并且旨在在不同提供者之间保持一致的标签)。我们可以使用节点选择器或节点亲和性来针对该标签,确保我们的 Pod 只在基于 Arm 的节点上运行。在下面的列表中,工作负载选择 arm64 架构,以防止 Pod 被调度到任何其他类型的 CPU 架构上。
清单 8.1 Chapter08/8.1.1_NodeSelection/deploy_nodeselector.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver
spec:
replicas: 1
selector:
matchLabels:
pod: timeserver-pod
template:
metadata:
labels:
pod: timeserver-pod
spec:
nodeSelector: ?
kubernetes.io/arch: arm64 ?
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:5
选择具有 arm64 架构的节点
通过节点亲和性以更详细的方式表达完全相同的需求。
清单 8.2 Chapter08/8.1.1_NodeSelection/deploy_nodeaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver
spec:
replicas: 1
selector:
matchLabels:
pod: timeserver-pod
template:
metadata:
labels:
pod: timeserver-pod
spec:
affinity: ?
nodeAffinity: ?
requiredDuringSchedulingIgnoredDuringExecution: ?
nodeSelectorTerms: ?
- matchExpressions: ?
- key: kubernetes.io/arch ?
operator: In ?
values: ?
- arm64 ?
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:5
? 另一种选择具有 arm64 架构的节点的方法
这两个先前的部署配置将实现完全相同的结果:一个仅放置在基于 Arm 的节点上的 Pod(要验证 Pod 的位置,您可以使用 kubectl get pods -o wide 查询,然后使用 kubectl describe node $NODE_NAME | grep arch 检查节点)。节点亲和性方法的优势,以及您使用它的原因,是它允许更具表现力的逻辑,我将在下一节中详细介绍。
在您的 PodSpecs 中要求这些与功能相关的节点标签是第一步,但您需要一种方法来实际配置具有该功能的节点(即,具有您选择的标签)。与往常一样,节点及其相关功能的配置是在平台级别完成的。如果您使用的是像 GKE 这样的完全托管平台,并且处于自动驾驶模式,只需使用您的功能标签指定节点选择器,就足以获得具有这些能力的节点,前提是该能力由平台提供。在更传统的 Kubernetes 平台上,您需要通过创建具有所需属性的节点池或节点组来额外配置具有这些功能的节点。
要了解支持哪些功能,提供商的文档是最好的。然而,如果您在集群中有一个具有您所需属性的节点,您也可以检查它并查看可供选择的标签。
kubectl describe nodes
以下是运行在 GKE 上的基于 Arm 的节点的一些输出标签:
Labels: cloud.google.com/compute-class=Scale-Out
cloud.google.com/gke-boot-disk=pd-standard
cloud.google.com/gke-container-runtime=containerd
cloud.google.com/gke-image-streaming=true
cloud.google.com/gke-max-pods-per-node=32
cloud.google.com/gke-nodepool=nap-19wjaxds
cloud.google.com/gke-os-distribution=cos
cloud.google.com/machine-family=t2a
kubernetes.io/arch=arm64 ?
kubernetes.io/hostname=gk3-autopilot-cluster-4-nap-19wja
kubernetes.io/os=linux
node.kubernetes.io/instance-type=t2a-standard-4
node.kubernetes.io/masq-agent-ds-ready=true
topology.gke.io/zone=us-central1-f
topology.kubernetes.io/region=us-central1
topology.kubernetes.io/zone=us-central1-f
? 在列表 8.1 和 8.2 中引用的节点标签
8.1.2 节点亲和性和反亲和性
节点亲和性非常灵活,能够做的不仅仅是要求一组标签。例如,使用 In 运算符,您可以指定一个可能值的列表。假设您想选择 x86 或 Arm 作为架构;您可以通过提供一个可能值的列表,使用 In 运算符来实现节点亲和性,如下所示。
清单 8.3 Chapter08/8.1.2_NodeAffinity/deploy_nodeaffinity_multi.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver
spec:
replicas: 6
selector:
matchLabels:
pod: timeserver-pod
template:
metadata:
labels:
pod: timeserver-pod
spec:
affinity: ?
nodeAffinity: ?
requiredDuringSchedulingIgnoredDuringExecution: ?
nodeSelectorTerms: ?
- matchExpressions: ?
- key: kubernetes.io/arch ?
operator: In ?
values: ?
- arm64 ?
- amd64 ?
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:5
resources:
requests:
cpu: 500m
该 Pod 可以在 arm64 (Arm) 或 amd64 (x86) 架构上运行。
虽然在列表 8.1 中使用的 nodeSelector 字段可以基于多个条件进行选择,但所有条件必须都满足才能调度 Pod。这里用于允许在不同值上调度的 In 逻辑是节点亲和性的独特之处。您可以通过在 matchExpressions 下添加额外的表达式来要求满足多个条件。
operator 逻辑可以用来将表达式转变为反亲和性(即,避免具有给定标签的节点),与 NotIn 一起使用,这将确保 Pod 不会落在具有指定标签的节点上(表 8.1)。
表 8.1 操作员逻辑
操作员 | 描述 |
In | 节点标签的值是给定选项之一。 |
NotIn | 该值不在您提供的列表中。 |
Exists | 标签键在节点上存在(具有任何值)。 |
DoesNotExist | 标签键在节点上不存在。 |
Gt | 给定的值大于节点标签中的值。 |
Lt | 给定的值小于节点标签中的值。 |
节点亲和性的另一个好处是您可以创建首选而非必需的规则来表达一组偏好。例如,如果您的容器是多架构的,可以在 x86 或 Arm 上运行,但您更倾向于在可能的情况下使用 Arm(例如,出于成本原因),那么您可以像以下列表中那样表达。
清单 8.4 Chapter08/8.1.2_NodeAffinity/deploy_nodeaffinity_preferred.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver
spec:
replicas: 6
selector:
matchLabels:
pod: timeserver-pod
template:
metadata:
labels:
pod: timeserver-pod
spec:
affinity: ?
nodeAffinity: ?
preferredDuringSchedulingIgnoredDuringExecution: ?
- weight: 100 ?
preference: ?
matchExpressions: ?
- key: kubernetes.io/arch ?
operator: In ?
values: ?
- arm64 ?
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:5
resources:
requests:
cpu: 500m
? 更倾向于在 arm64 上调度,但如果 arm64 不可用,则会在任何节点上调度。
优先亲和力的注意事项
这种 preferredDuringSchedulingIgnoredDuringExecution 逻辑有时可能会产生意想不到的结果。当您在节点上有现有的未分配容量时,偏好排序是有效的,但当没有所需类型的未分配容量且需要新节点时,它与集群自动扩展的交互方式可能与您的偏好相悖。例如,如果您的集群中现有节点上有任何未分配的容量,即使它是非首选类型,Kubernetes 实际上会首先在该处调度 Pod,然后平台才会启动以添加您所需类型的新节点。
原因在于,负责将 Pods 放置在节点上的 Kubernetes 调度器和平台自动扩展器(负责添加新节点的常见平台组件)在某种程度上是独立运行的。在平台层面,典型的节点自动扩展器会寻找待调度的 Pods,如果增加了更多的容量,它们就可以被调度。但是,由于 Kubernetes 调度器首先启动并将 Pod 放置在不优选但可用的容量上,自动扩展器没有机会采取行动。
在使用云服务提供商时,您通常只需要求所需的功能,并依赖于他们具备满足这些需求的能力。
8.1.3 污染节点以防止默认调度
通常,当您有一组具有特殊特征的节点时,您可能希望默认情况下防止 Pods 在这些节点上调度。以 Arm 架构为例:由于并非所有容器镜像都支持它,您可能希望配置您的集群,以便 Arm 架构节点默认情况下不会用于调度,除非工作负载明确表示支持。其他示例包括当您有一个带有特殊硬件(如 GPU)的节点时,您需要仅为将使用该硬件的 Pods 保留,或者当您有可能会突然关闭的 Spot 计算时,并非所有工作负载都能很好地响应。
虽然您可以通过节点反亲和性(即使用 NotIn 操作符的节点亲和性规则)对每个其他 Pod 进行注释以避免此类节点,但这很繁琐。相反,Kubernetes 允许您“标记”一个节点,以防止 Pod 默认在其上调度。其工作原理是,您标记具有特殊特征且不应默认调度的节点。然后,您在仅允许在这些节点上运行的工作负载的 PodSpec 中“容忍”这种标记。
作为示例,我们可以单独标记节点以查看效果。这并不是在生产中通常的做法,但这是一个不错的实验方式。对于这个演示,我们可以使用 Minikube(在第 3 章中介绍)并按如下方式标记其中一个节点:
$ minikube start --nodes 3
Done! kubectl is now configured to use "minikube" cluster
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 77s v1.24.3
minikube-m02 Ready <none> 55s v1.24.3
minikube-m03 NotReady <none> 19s v1.24.3
$ NODE_NAME=minikube-m02
$ kubectl taint nodes $NODE_NAME spot=true:NoSchedule
node/minikube-m02 tainted
提示:稍后,如果您希望去除污点,可以使用 kubectl taint nodes $NODE_NAME spot-
在这个例子中, spot=true 是我们给污点起的名称,稍后在标记能够容忍此污点的 Pod 时会使用。 NoSchedule 关键字表示此污点效果的期望行为(即,没有容忍的 Pod 不应被调度)。有替代 NoSchedule 行为的选项,但我不推荐它们。 PreferNoSchedule 是一个创建软规则的选项,这听起来可能很有用;然而,如果您的主要目标是避免在某些节点类别上调度 Pod,软规则将无法实现这一目标,并可能使调试变得更加困难。有时,拥有一个未调度的 Pod 需要您分配资源,比将其调度到您特殊的污点机器上并导致其他未指定的问题要好。
当您在托管的 Kubernetes 平台上操作时,单独标记节点的可能性不大,就像之前的例子一样。通常,污点适用于具有相同特征的一组节点,并且节点在升级或修复事件期间会定期更换,这意味着任何单个节点的污点都会被撤销。请查找平台提供商的 API,以便您可以标记节点组,这样污点将应用于组中的所有节点,并在升级期间保持有效。
GKE 中的节点污点
在使用 GKE 的 Autopilot 模式时,节点污点处理是完全自动的。当您选择特定的(非默认)功能,如 Spot 计算或 Arm 架构时,所配置的节点会自动被标记为污点。方便的是,Pods 也会被修改以容忍自动污点,因此您只需选择该功能即可。Pods 的这种自动修改是通过一个由平台安装和维护的准入控制器(在第 12 章中介绍)完成的。
在使用 GKE 和节点池时,您可以在创建节点池时对其进行污点。例如,如果您正在创建一个虚拟机的节点池,您可以将所有节点配置为污点,如下所示:
gcloud container node-pools create $NODE_POOL_NAME --cluster $CLUSTER_NAME \
--spot --node-taints spot=true:NoSchedule
如果您的整个集群由临时节点组成,则通常不需要污点,因为没有必要区分节点。
一旦您有了污点节点,如果您调度一个工作负载,您会注意到它不会在这些节点上调度(使用 kubectl get pods -o wide 查看 Pod 落在了哪些节点上)。为了使工作负载能够在您刚刚污点的节点上调度,工作负载需要更新以容忍该污点,如下所示。
清单 8.5 Chapter08/8.1.3_Taints/deploy_tolerate_spot.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver
spec:
replicas: 3
selector:
matchLabels:
pod: timeserver-pod
template:
metadata:
labels:
pod: timeserver-pod
spec:
tolerations: ?
- key: spot ?
value: "true" ?
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:5
? 该工作负载可以容忍带有 spot=true 污点的节点,因此可以在这些节点上调度。
仅有容忍并不会强制 Pod 仅在有污点的节点上调度;它只是允许 Pod 在那里调度。Pod 的调度位置将基于其他几个因素来决定,例如可用容量。因此,具有容忍的 Pod 可以在无污点的节点上以及它们所容忍的有污点的节点上运行,如图 8.1 所示。
通常,您将结合污点和容忍与节点选择器或节点亲和性,以确保特定的一组 Pods,仅该组 Pods,将在相关节点上运行。一个重要的例子是 GPU 工作负载:这些工作负载必须仅在具有 GPU 的节点上运行,而您不希望非 GPU 工作负载占用该宝贵的空间(图 8.2)。
容忍所有污点
某些工作负载——最常见的是作为 DaemonSets 部署的工作负载(在第 12 章中介绍)——需要在每个节点上运行,并且必须设计为处理集群的所有配置。这些工作负载通常可以容忍所有污点,如以下列表所示。
清单 8.6 Chapter08/8.1.3_Taints/daemonset_tolerate_all_taints.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: example-ds
spec:
selector:
matchLabels:
pod: example-pod
template:
metadata:
labels:
pod: example-pod
spec:
tolerations: ?
- effect: NoExecute ?
operator: Exists ?
- effect: NoSchedule ?
operator: Exists ?
containers:
- image: ubuntu
command: ["sleep", "infinity"]
name: ubuntu-container
? 容忍所有污点
请注意,当您这样做时,您的 Pod 实际上需要在集群中现在和将来可能存在的所有节点类型上运行。当添加像基于 Arm 的节点这样的功能时,这可能会成为一个问题,因为这需要为 Arm 架构专门构建容器。如果出现需要在所有节点上调度 Pod 的情况,除了具有特定标签的节点,例如 Arm 架构,您可以通过将容忍度与节点反亲和规则结合来实现,如下一个列表所示。
清单 8.7 Chapter08/8.1.3_Taints/daemonset_tolerate_antiaffinity.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: example-ds
spec:
selector:
matchLabels:
pod: example-pod
template:
metadata:
labels:
pod: example-pod
spec:
tolerations: ?
- effect: NoExecute ?
operator: Exists ?
- effect: NoSchedule ?
operator: Exists ?
affinity: ?
nodeAffinity: ?
requiredDuringSchedulingIgnoredDuringExecution: ?
nodeSelectorTerms: ?
- matchExpressions: ?
- key: kubernetes.io/arch ?
operator: NotIn ?
values: ?
- arm64 ?
containers:
- image: ubuntu
command: ["sleep", "infinity"]
name: ubuntu-container
? 容忍所有污点...
? ...但不要在基于 Arm 的节点上调度
8.1.4 工作负载分离
另一个使用污点、容忍和节点选择器的用途是分离工作负载。到目前为止,我们所涵盖的节点选择用例主要围绕基于特性的选择——需要 Arm 架构、Spot 计算、GPU 节点等。
节点选择不仅限于节点特性,还可以用于将工作负载相互分离。虽然您可以使用 Pod 反亲和性(在 8.2.3 节中介绍)来防止特定的 Pod 被放置在一起,但有时将工作负载保持在各自专用的节点组上会更有帮助。
我听到的一个要求是来自那些运行批量工作负载的人,这些工作负载由调度工作的协调者 Pod 和执行工作的工作 Pod 组成。他们更倾向于将这两种角色的 Pod 分开放在各自的节点上,以便工作 Pod 的节点自动扩展(这些 Pod 往往是来来去去的)不会影响协调者 Pod 的扩展(这些 Pod 通常比较稳定)。另一个例子是噪声邻居问题,其中两个 Pod 可能会在节点上竞争资源,如果分开放置会更有效。
为了实现工作负载分离,我们可以结合到目前为止使用的几种技术,以及一个自定义节点标签。节点获得一个标签和一个污点,工作负载获得该标签的容忍和选择器,这意味着工作负载将单独调度到一组节点上(可能与具有相同选择器和容忍的其他工作负载共享)。
以下列表提供了一个示例部署,具有任意的容忍度和节点选择器,以实现工作负载分离。为了方便,我们将对两个元素使用相同的键值对( "group=1" ),尽管请注意它们在 Kubernetes 中是不同的概念。
清单 8.8 Chapter08/8.1.4_WorkloadSeparation/deploy_group1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver1
spec:
replicas: 5
selector:
matchLabels:
pod: timeserver1-pod
template:
metadata:
labels:
pod: timeserver1-pod
spec:
tolerations: ?
- key: group ?
operator: Equal ?
value: "1" ?
effect: NoSchedule ?
nodeSelector: ?
group: "1" ?
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:5
容忍组=1 污点
选择组=1 标签
为了演示,我们可以在文件 deploy_group2.yaml 中复制这个 Deployment,使用 "group=2" 作为容忍和选择器的键/值:
tolerations:
- key: group ?
operator: Equal ?
value: "2" ?
effect: NoSchedule ?
nodeSelector: ?
group: "2" ?
? 容忍组=2 污点
选择组=2 标签
为了将这些部署的 Pods 部署在离散的节点集上,我们需要对节点进行污点处理,以防止其他 Pods 降落在它们上,并对其进行标记,以便我们的工作负载可以定位到它们。如果您跳过对节点的标记,这些部署将永远不会被调度,因为没有满足节点选择器要求的节点。如果您标记了节点但没有对其进行污点处理,这些工作负载将会被调度,并通过节点选择器相互分离。然而,由于没有污点来阻止它们,其他随机 Pods 也可能会落在这些节点上。
GKE Autopilot 上的工作负载分离
如果您在自动驾驶模式下将之前的工作负载部署到 GKE,具有请求标签和污点的节点将会自动配置!这是因为这个无节点的平台根据您的 Pod 的需求进行操作,并提供匹配的节点,因此您无需做任何其他操作。在传统的 Kubernetes 平台上,您需要自己创建具有这些属性的节点。
在您管理节点的 Kubernetes 环境中,您需要为节点提供正确的污点和标签以实现工作负载分离。以 Minikube 为例,我们可以直接对节点进行污点和标签操作。请注意,在托管平台上,您通常在节点池或组级别操作节点,并会使用平台 API 来提供节点,因此请在该 API 中查找标签和污点参数:
$ minikube start --nodes 3 ?
$ kubectl get nodes ?
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 67s v1.24.3
minikube-m02 Ready <none> 46s v1.24.3
minikube-m03 Ready <none> 24s v1.24.3
$ kubectl taint nodes minikube-m02 group=1:NoSchedule ?
$ kubectl label node minikube-m02 group=1 ?
$ kubectl taint nodes minikube-m03 group=2:NoSchedule ?
$ kubectl label node minikube-m03 group=2 ?
? 创建一个新的 Minikube 集群。
? 查看节点。
? 为组 1 标记和污点 m02 节点。
? 为组 2 标记和标记 m03 节点。
污点和标签都是必需的(与部署中的容忍和节点选择器相匹配),因为它们的目的不同。污点阻止所有不容忍该污点的工作负载落在其上,而标签可以用来确保工作负载不会落在任何其他节点上(例如,没有任何污点的节点)。为了方便,我对污点和标签使用了相同的键值对( "group=1" ),但这并不是必须的。
配置好我们的集群后,我们可以部署工作负载分离的部署并观察结果。特别注意 Pods 落在哪个节点上:
$ kubectl create -f Chapter08/8.1.4_WorkloadSeparation
deployment.apps/timeserver1 created
deployment.apps/timeserver2 created
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE NODE
timeserver1-75b69b5795-9n7ds 1/1 Running 0 2m2s minikube-m02 ?
timeserver1-75b69b5795-kft64 1/1 Running 0 2m2s minikube-m02 ?
timeserver1-75b69b5795-mnc4j 1/1 Running 0 2m2s minikube-m02 ?
timeserver1-75b69b5795-msg9v 1/1 Running 0 2m2s minikube-m02 ?
timeserver1-75b69b5795-r8r9t 1/1 Running 0 2m2s minikube-m02 ?
timeserver2-6cbf875b6b-6wm7w 1/1 Running 0 2m2s minikube-m03 ?
timeserver2-6cbf875b6b-dtnhm 1/1 Running 0 2m2s minikube-m03 ?
timeserver2-6cbf875b6b-fd6vh 1/1 Running 0 2m2s minikube-m03 ?
timeserver2-6cbf875b6b-q6fk8 1/1 Running 0 2m2s minikube-m03 ?
timeserver2-6cbf875b6b-zvk72 1/1 Running 0 2m2s minikube-m03 ?
? timeserver1 的 Pods 正在节点 minikube-m02 上运行
? timeserver2 的 Pods 正在节点 minikube-m03 上运行
一旦您完成了 minikube 集群,您可以删除它的所有痕迹:
minikube delete
猜你喜欢
- 2025-01-20 若一只猫有这8大特征,说明是”百里挑一“的好猫,人人都想要!
- 2025-01-20 TA8钛合金力学性能和热导率分析
- 2025-01-20 事发上海地铁8号线!这一幕太惊险……这种情况冬季多发,一定要注意!
- 2025-01-20 使用 OpenRewrite 升级 JDK 17
- 2025-01-20 JVM内存结构揭秘:深入理解堆内存分代
- 2025-01-20 若一只猫有这8大特征,说明是百里挑一的好猫,人人都想要!
- 2025-01-20 浅谈 Java 列表(List)的初始化方法
- 2025-01-20 用了Stream后,代码反而越写越丑?
- 2025-01-20 我有点想用JDK17了
- 2025-01-20 Your build is currently configured to use incompatible Java 17.0.8
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)