简介

Kubernetes是一个强大的编排工具,可以用来很方便的管理许多台机器,为了使机器的资源利用率提高,同时也尽可能的把压力分摊到各个机器上,这个职责就是由scheduler来完成的。

Kubernetes scheduler是一个策略丰富、拓扑感知、工作负载特定的功能,显著影响可用性、性能和容量。

为了能更好的使用它,所以从源码的角度,对它进行一个全方位的分析与学习。

scheduler的功能不多,但逻辑比较复杂,里面有很多考虑的因素,总结下来大致有如下几点:

  • Leader选主,确保集群中只有一个scheduler在工作,其它只是高可用备份实例。通过endpoint:kube-scheduler作为仲裁资源。

  • Node筛选,根据设置的条件、资源要求等,匹配出所有满足分配的Node结点。

  • 最优Node选择。在所有满足条件的Node中,根据定义好的规则来打分,取分数最高的。如果有相同分数的,则采用轮询方式。

  • 为了响应高优先级的资源分配,增加了抢占功能。scheduler有权删除一些低优先级的Pod,以释放资源给高优先级的Pod来使用。

功能说明

代码看下来比较困难,下面将分几个场景来描述scheduler工作的过程:

1、环境说明(假设3台机器,分别是8C16G)

场景一: 资源分配——最基本的功能

2、先分配一个请求2C4G的Pod: A

场景二:机器负载均衡——评分机制

3、再分配一个请求2C4G的Pod:B(尽管node1上还有空闲资源可分配B,但node2和node3空闲资源更多,打分更高,所以分配到了node2<选择node2还是node3,是由schedule轮询选择的>。)

4、同理,如果再分配一个C,scheduler会优先分配到node3上

场景三:资源抢占——特权机制

5、现在3个Node上都分配了2C4G,就是都剩余6C12G,如果我这个时候分配一个8C12G的Pod:D,在同优先级的情况下,D将不会分配,处于Pending状态,因为三台机器都资源不足。

6、如果这个时候,我给D设置一个高的优先级,schedule会删除一台机器上的Pod,比如A,然后资源足够了,将D分配到node1上,再将A分配到node2或node3上。(这里分配是一个类似,因为三台都是一样的)

7、下面实战一把,详细试验下scheduler的抢占过程:

我有一个Deployment,有3个复本,分别分配到两台机器上。(为什么用这个例子,是为了说明,抢占一定会发生在10-10-40-89上,因为要删除的Pod最少)

这个时候,我创建一个高优先级的Deployment:

快速查询,能看到下面的阶段:

第一步,将要分配的testpc-745cc7867-fqbp2设置为“提名Pod”,这个名字后面会再出现,同时删除原10-10-40-89上的testpod,由于截的比较慢,下图中新的testpod已经在10-10-88-99上创建了。

第二步,提名Pod将会分配到对应的结点上(等待Terminating状态的Pod释放完资源后)。

第三步,资源足够,Pod正常Running。

最后展示下watch情况下的事件:

测试我共有两个yaml文件,如下:

testpod.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  labels:
    k8s-app: testpod
  name: testpod
spec:
  progressDeadlineSeconds: 600
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: testpod
  template:
    metadata:
      labels:
        k8s-app: testpod
    spec:
      containers:
      - image: nginx:1.17
        imagePullPolicy: IfNotPresent
        name: nginx
        ports:
        - containerPort: 80
          name: nginx
          protocol: TCP
        resources:
          requests:
            cpu: 1
            memory: 2Gi

testpc.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000000
globalDefault: false
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  labels:
    k8s-app: testpc
  name: testpc
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: testpc
  template:
    metadata:
      labels:
        k8s-app: testpc
    spec:
      containers:
      - image: nginx:1.17
        imagePullPolicy: IfNotPresent
        name: nginx
        ports:
        - containerPort: 80
          name: nginx
          protocol: TCP
        resources:
          requests:
            cpu: 6
            memory: 2Gi
      priorityClassName: high-priority

场景四:关系户——亲和与反亲和

scheduler在分配Pod时,考虑的要素很多,亲和性和反亲和,是一个比较常用的,在这里做一个典型来讲讲。

比如在上图中,我新的Pod:D,要求不能和A在一台机器上,和B的互斥打分是100,和C的互斥打分是10。表示说,D一定不能和A在一台机器,尽可能不和B、C在同一台机器,实在没办法时(资源不足),D更倾向于和C在一起。

样例:

1
2
3
4
5
6
7
8
9
10
11
podAntiAffinity:
  preferredDuringSchedulingIgnoredDuringExecution:
  - weight: 100
    podAffinityTerm:
      labelSelector:
        matchExpressions:
        key: security
          operator: In
          values:
          - S2
      topologyKey: kubernetes.io/hostname

通过对这四个应用场景的分析,对它的功能有了一个初步的了解。要想更全面、深入的了解它的功能,需要从它的源码来着手。下面将从源码层面来做深入分析。

代码分析

scheduler总体结构

scheduler的配置,基本都是采用默认配置,图中列出了它的配置加载流程,基本都是加载它自身的默认配置。

server.Run为它的主体逻辑,之后会详细讲解。

重要配置讲解

图中,单独列出了两个config配置:

1、disablePreemption:

scheduler有个抢占功能。当Pod调度发现无可用资源时,它会将比该Pod优先级低的Pod删除,以释放资源给它来调度。disablePreemption默认为false,表示开启抢占,如果需要关闭,则设置为true。

2、既然说到优先级,所以我还列出来了优先级的设置方法。

Kubernetes中有个单独的优先级的资源,叫:PriorityClass,通过下面这个yaml,能创建一个PriorityClass。

1
2
3
4
5
6
7
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

然后可将这个PriorityClass关联到Pod上:

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
 priorityClassName: high-priority

这样就完成的Pod优先级的设置。如果不设置,Pod默认是同一优先级(为0)。

特别注意:

static Pod比较特殊,需要直接设置priority,因为kubelet是根据priority来判断。

scheduler启动流程

通过深入分析server.Run,可看到如下流程:

server.Run还是有一部分的配置处理流程。

schedulerConfig中,根据默认的参数,加载了两大块内容:predicate、priority函数。

  • predicate函数用于做Pod是否可分配到Node上的检查函数。

  • priority函数,则用于选优。当可分配的Node有多个时,这个时候就会根据priority函数来给node打分,最终调度到分数最高的Node上。

Kubernetes提供了这些默认的判断函数:

predicate:

1、CheckNodeConditionPredicate

we really don’t want to check predicates against unschedulable nodes.

检查Node状态:是否处于可调度状态等。

-----> 遍历nodeInfo中Node的所有状况:

  • 如果Node类型为ready,并且状态不是True,则认为结点为notReady

  • 如果Node类型为OutOfDisk,并且状态不是False,则认为结点OutOfDisk

  • 如果Node类型为NetworkUnavailable,并且状态不是False,则认为结点状态为:NetworkUnavailable

检查Node的spec,如果是UnSchedulable,则认为结点为UnSchedulable。

以上检查都通过,则返回匹配成功。

2、PodFitsHost

we check the pod.spec.nodeName.

检查pod.spec.nodeName是否匹配。

----> 如果Pod未指定NodeName,则返回匹配成功。

检查Node的名字,如果与Pod指定的同名,则匹配成功,否则返回:nodeName不匹配。

3、PodFitsHostPorts

we check ports asked on the spec.

检查服务端口是否被占用。

-----> 如果元数据metadata中有定义需要的podPorts,则直接从元数据中取,否则从Pod的所有容器中获取需要的port。

如果需要的port为空,则返回匹配成功。

从nodeInfo中获取当前已经使用的port,如果有冲突,则返回:端口不匹配,否则返回匹配成功。

4、PodMatchNodeSelector

check node label after narrowing search.

检查label是否匹配。

------> 如果Pod中定义了NodeSelector,则根据选择来匹配Node的labels,如果不匹配,则返回NodeSelectorNotMatch。

如果Pod的Affinity中定义了NodeAffinity,则检查结点亲和关系:

  • 如果未定义requiredDuringSchedulingIgnoredDuringExecution,则直接返回匹配。

  • 如果定义了requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms,则里面有一个匹配,则匹配。否则认为不匹配。

特别的:如果nodeSelectorTerms为nil,则全不匹配;如果nodeSelectorTerms不为nil,但是空的切片,则全不匹配;同样,nodeSelectorTerms中的MatchExpressions,如果为nil或者是空切片,也不匹配。

5、PodFitsResources

this one comes here since it’s not restrictive enough as we do not try to match values but ranges.

-----> 检查Node的allowedPodNumber是否超过,如果超过,增加超限错误(此处未直接返回,会把所有错误检查完一次性返回)。

检查元数据中是否有定义podRequest、ignoredExtendedResources,如果定义了,则从元数据中取。否则从Pod中每个容器中取:先检查所有container中所有需要的资源总合,再检查initContainer中,如果有资源比总合还大,则取较大的为所需要的资源。

如果需要的资源都为0,则返回检查结果。

获取Node的可用资源,检查需要新申请的资源+已申请的资源是否超过可用资源,如果超过,则记录资源不足。

检查所有Pod扩展资源,并判断扩展资源是否需要检查(ignoredExtendedResources),如果需要检查,则判断资源是否足够,不足够则记录失败。

返回检查结果(如果无失败,是检查成功)。

6、NoDiskConflict

Following the resource predicate, we check disk.

----> 遍历Pod所有存储、Node下的所有Pod,检查是否有存储冲突:

如果Pod无存储(无GCE、AWS、RBD、ISCSI),则检查通过。

7、PodToleratesNodeTaints

check toleration here, as node might have toleration.

-----> 检查结点是否容忍taint环境:

参数:Pod中定义的容忍规则:tolerations,Node中的环境状态:taints,筛选规则:取effect为NoSchedule、NoExecute的。

如果Node无taints,返回匹配成功。

遍历所有taints,如果taint不满足筛选规则,则跳过检查。

遍历所有的容忍规则,检查是否有规则是允许结点的taint状态。检查步骤:

  1. 如果effect为空,则检查通过,否则要相同。

  2. 如果key为空,则检查通过,否则要相同。

  3. 如果operator为Exists,则检查通过,如果为空或者是Equal,则要相同,否则不通过。

8、PodToleratesNodeNoExecuteTaints

check toleration here, as node might have toleration.

-----> 检查规则同上相似,只是筛选规则变了:取effect为NoExecute的。

9、CheckNodeLabelPresence

labels are easy to check, so this one goes before.

------> 检查label是否存在,不关心值。可设置label存在与不存在。

只有在scheduler.CreateFromConfig(policy)才会初始化该检查,在RegisterCustomFitPredicate中注册,默认无该检查。

10、checkServiceAffinity

-----> 检查服务类同关系。

如果一个Pod的服务调度到有label:"region=foo"的Node,之后有相同服务的Pod都会调度到该Node。

11、MaxPDVolumeCountPredicate

-----> 检查挂载的卷个数是不是超标,只支持:ESB:39,GCE:16,AzureDisk:16。

12、VolumeNodePredicate

-----> 无

13、VolumeZonePredicate

-----> 检查存储区域划分:

检查Node中是否有label:failure-domain.beta.kubernetes.io/zone或者failure-domain.beta.kubernetes.io/region,如果有,则检查Pod存储情况。

遍历Pod需要的存储信息:

根据PVC名字获取PVC信息,取出PVC对应的PV名字,如果没有名字(表示还未绑定PV),获取PVC的StorageClassName,如果处理正在绑定中,则跳过不检查,否则返回匹配失败(因为PVC绑定失败)。

绑定成功的,根据pvName获取对应的PV信息,检查PV的标签,如果PV有上面两个标签(zone、region),检查PV的值中(值可能有多个,用__分隔),是否包含Node对应标签的值,如果没有包含,则返回匹配失败。

14、CheckNodeMemoryPressurePredicate

doesn’t happen often.

-----> 检查Node内存压力。

15、CheckNodeDiskPressurePredicate

doesn’t happen often.

16、InterPodAffinityMatches

Most expensive predicate to compute.

默认有这些打分函数(priority):

SelectorSpreadPriority:根据相同的RC和服务拆分,使每个Node具有相同服务或RC的Pod尽量少,spreads pods by minimizing the number of pods (belonging to the same service or replication controller) on the same node.

InterPodAffinityPriority:根据Pod共性来分配,pods should be placed in the same topological domain (e.g. same node, same rack, same zone, same power domain, etc.).

LeastRequestedPriority:选择比较闲的node,Prioritize nodes by least requested utilization.

BalancedResourceAllocation:从资源分配平衡性来考虑分配,Prioritizes nodes to help achieve balanced resource usage.

NodePreferAvoidPodsPriority:用于用户自定义分配,权重10000起,方便用户来指定。0的时候不起作用。用户通过这个来指定:scheduler.alpha.kubernetes.io/preferAvoidPods Set this weight large enough to override all other priority functions.

NodeAffinityPriority:根据结点关系来分配,Prioritizes nodes that have labels matching NodeAffinity.

TaintTolerationPriority:根据pod设置的容忍项来分配,Prioritizes nodes that marked with taint which pod can tolerate.

最终,死循环进入:scheduleOne,真正开始schedule的调度流程。

Schedule调度流程

先讲主流程:

主流程分为以下8步:

  1. 从Pod队列中取出一个需要调度的Pod。

  2. 尝试调度该Pod。

  3. 调度失败,则尝试抢占Pod。

  4. 调度成功后,尝试做volumes绑定。

  5. 由于reserve插件暂时未启用,暂未分析。

  6. 尝试将Pod分配到Node上。

  7. 真正实现绑定。第4步和第6步中,都只是对schedule的cache的操作,先确保对cache的操作能完成,最终在第7步,异常实现将cache中的修改应用到apiserver中。如果应用失败,会将pod的分配信息从cache中清除,重新进行scheduler。

  8. 最复杂也最核心的,就是第2步和第3步,下面分别进行分析。

调度Pod流程

调度Pod,就是尝试将Pod分配到Node上,流程如下:

共有7点,将逐步分析:

  1. Pod基本检查,检查Pod是否有了对应的PVC,这里只是检查PVC是否存在,不关心绑定过程。

  2. 取出所有Node列表。

  3. 将nodeInfo应用到缓存中。全局nodeInfo中保存了当前Node的真实数据信息,而cache中会有调度过程的假设分析的信息。

  4. 检查Pod是否可调度到Node上,返回可调度的Node列表。

    a) 这里的检查,是针对前面初始化时,注册的predicate函数,如果有不符合,则认为不可调度。

    b) 这里会尝试两次,之所以两次,是因为有“提名Pod”的存在。暂时先不管“提名Pod”哪来的,后面会讲到。提名Pod,就是说,这个Pod已经分配到Node上,但它还未应用到Kubernetes环境,目前只是占着这个坑位,要调度的Pod,在调度的过程中,也需要考虑它所占的资源。

    c) 第一次时,会先把优先级大于当前Pod的提名Pod分配到Node中(添加到一个临时的nodeInfo中),然后检查所有的predicat函数是否通过。

    d) 第二次时,不添加提名Pod,再检查所有的predicate函数。之所以有第二次,是因为提名Pod实际还并不存在,有些Pod亲和性可能会判断有误。

    e) 当然,如果没有提名Pod,则不需要第二次判断。

  5. 如果找不到,则返回失败。如果只找到一个,则返回该Node。

  6. 当找到多个Node时,会去给Node打分,打分规则如下:

    a) 如果没有定义打分规则,则返回所有分数都为1。schedule默认是有打分函数的,前面初始化中有讲。

    b) 运行早期老版本的打分函数。早期就是单纯的一个function,运行后得到打分结果。

    c) 新版本,将打分函数拆分成两步,map和reduce,先按16个并发运行map,之后运行reduce统计执行结果。

    d) 这里还预留了扩展支持。

    e) 最终返回打分结果。

  7. 根据打分结果,选择Node。

    a) 先取出得分最高的Node列表。

    b) 然后按round-robin的方式选择Node。

由于相同最高分的Node可能有多个,genericScheduler采用round-robin的方式:它自己记录一个全局的lastNodeIndex,如何num为当前有相同最高分的节点数,则用lastNodeIndex % num来选取本次节点的下标,之后lastNodeIndex加1,实现轮询调度。

到此,Pod的调度流程分析完成。当中有个特别的东西:提名Pod(NominatedPod),它的出现和下面讲的抢占流程有关。

Pod抢占流程

抢占的流程,比调度复杂一些,主要分两大步:抢占分析和抢占。第一步是检查是不是能完成抢占,第二步是执行抢占(删除Pod)。

抢占检查

  1. 检查Pod是否可以发起抢占:如果Pod是提名Pod(已经预分配到Node),并且该Node上有处于terminating的Pod p,并且p的优先级小于当前Pod,则不允许发起抢占。

  2. 获取所有Node清单。

  3. 获取可能的Node。检查调度失败原因,如果是nodeNotReady这种原因,则Node不参与抢占。这些都是不参与抢占的:predicates.ErrNodeSelectorNotMatch,predicates.ErrPodAffinityRulesNotMatch,predicates.ErrPodNotMatchHostName,predicates.ErrTaintsTolerationsNotMatch,predicates.ErrNodeLabelPresenceViolated,predicates.ErrNodeNotReady,predicates.ErrNodeNetworkUnavailable,predicates.ErrNodeUnderDiskPressure,predicates.ErrNodeUnderPIDPressure,predicates.ErrNodeUnderMemoryPressure,predicates.ErrNodeUnschedulable,predicates.ErrNodeUnknownCondition,predicates.ErrVolumeZoneConflict,predicates.ErrVolumeNodeConflict,predicates.ErrVolumeBindConflict
  4. 如果可抢占的Node没有,则结束。

  5. 获取pdb列表:pdb is PodDisruptionBudget. 这个是预算的定义,比如statefulset定义了3个复本,而我们定义了,允许其中1个Pod可以挂掉。

  6. 获取通过抢占(删除一些Pod),能完成调度的Node列表。

    a) 将比当前Pod优先级低的Pod全部从nodeInfoCopy中删除,然后尝试去调度。b) 如果调度失败,则表示无法抢占。(因为不能删除比它优先级高的)c) 将要删除的Pod,根据pdb进行拆分:nonViolatingVictim和violatingVictim。说明见图中。d) 然后尝试将violatingVictim中的Pod一个个加进去,尝试能不能调度。numViolatingVictim中记录不通过数。e) 然后尝试将nonViolatingVictim中的Pod一个个加进去,尝试能不能调度。victims记录不通过的Pod信息。f) 返回victims和numViolatingVictim。

  7. extenders扩展保留。
  8. 从可抢占的Node列表中,选择最合适的一个Node。按如下规则进行选择:a) node pdb violations最小。就是上面返回的numViolatingVictimb) 如果只有一个Node满足,则返回该Nodec) 比较Node中victims中优先级的最高值,取最小的那个。最高:取的是单个Node中,优先级的最高值。最小:取的是所有Node中的最小值d) 如果只有一个,则返回该Node。e) 取Node中victims优先级总和最小的。f) 如果只有一个,则返回该Node。g) 取Node中victims的Pod数最小的。h) 返回第一个。
  9. 如果无合适的,则结束。
  10. 获取比当前优先级小的提名Pod。
  11. 返回Node信息,需要删除的Pod列表,优先级小的提名Pod。

到此,抢占检查结束。得到期望调度的Node、要调度到这个Node上,需要删除的Pod列表、以及比当前Pod优先级小的提名Pod。

抢占执行流程(找到了期望的Node才会进入)

  1. 将当前Pod,变更为提名Pod,对应的Node为期望的Node。这里就是提名Pod出现的原因。
  2. 将提名Pod信息更新到apiServer。
  3. 遍历victims(抢占流程返回的需要删除的Pod列表),删除Pod,并记录event。
  4. 遍历nominatedPodsToClear(抢占返回的比当前Pod优先级小的提名Pod),清空提名Pod配置,并更新apiServer。

到此,调度流程分析完成。

参考:https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/

【转】Kubernetes scheduler学习笔记的更多相关文章

  1. kubernetes CRD学习笔记

    前言 最近在极客时间订阅了kubernetes的专栏,这篇文章是想记录一下自己学习CRD(custom resource definition)的过程,加深一下记忆. 准备工作 首先安装一下我们用的g ...

  2. K8S学习笔记之二进制的方式创建一个Kubernetes集群

    0x00 单节点搭建和简述 minikube Minikube是一个工具,可以在本地快速运行一个单点的Kubernetes,尝试Kubernetes或日常开发的用户使用.不能用于生产环境. 官方地址: ...

  3. Contour 学习笔记(一):使用 Contour 接管 Kubernetes 的南北流量

    原文链接:Contour 学习笔记(一):使用 Contour 接管 Kubernetes 的南北流量 在 Kubernetes 中运行大规模以 Web 为中心的工作负载,最关键的需求之一就是在 L7 ...

  4. Kubernetes 学习笔记(一):基础概念

    个人笔记,仅本人查阅使用,不保证正确. 零.微服务 微服务架构专注于应用解耦合,通过将应用彻底地组件化和服务化,每个微服务只包含一个非常小的功能,比如权限管理.日志收集等等.由这一组微服务组合起来,提 ...

  5. Kubernetes学习笔记(四):服务

    服务介绍 服务是一种为一组相同功能的pod提供单一不变接入点的资源.当服务存在时,他的IP和端口不会改变.客户端通过IP和端口建立连接,这些连接会被路由到任何一个pod上.如此,客户端不需要知道每个单 ...

  6. Kubernetes学习笔记(八):Deployment--声明式的升级应用

    概述 本文核心问题是:如何升级应用. 对于Pod的更新有两种策略: 一是删除全部旧Pod之后再创建新Pod.好处是,同一时间只会有一个版本的应用存在:缺点是,应用有一段时间不可用. 二是先创建新Pod ...

  7. Kubernetes学习笔记之认识Kubernetes组件

    前言:笔记知识点来源于Kubernetes官方文档说明,链接:https://kubernetes.io/docs/concepts/overview/components/ ,本记录仅仅是学习笔记记 ...

  8. Kubernetes权威指南学习笔记(一)

    https://blog.csdn.net/keysilence1/article/details/70239717 概念 Kubernetes是谷歌严格保密十几年的秘密武器——Borg的一个开源版本 ...

  9. Kubernetes全栈架构师(二进制高可用安装k8s集群部署篇)--学习笔记

    目录 二进制高可用基本配置 二进制系统和内核升级 二进制基本组件安装 二进制生成证书详解 二进制高可用及etcd配置 二进制K8s组件配置 二进制使用Bootstrapping自动颁发证书 二进制No ...

随机推荐

  1. leetcode 886. 可能的二分法(DFS,染色,种类并查集)

    题目链接 886. 可能的二分法 题意: 给定一组 N 人(编号为 1, 2, ..., N), 我们想把每个人分进任意大小的两组. 每个人都可能不喜欢其他人,那么他们不应该属于同一组. 形式上,如果 ...

  2. apiAutoTest: 接口自动化测试的数据清洗(备份/恢复)处理方案

    接口自动化测试之数据清洗/隔离/备份/恢复 在得到QQ:1301559180 得代码贡献之后,想到了通过ssh连接上服务器,然后进行数据库备份,数据库恢复, 主要使用了 paramiko库 最终效果 ...

  3. 二本学生拿到腾讯大厂offer的成长记录

    本人迈莫,是在20年以春招实习生的身份进入鹅厂,经过重重波折,最终成为鹅仔一份子.接下来我会以我亲生经历为例,分享一下普通大学的学生也是可以进去大厂,拭目以待!!! 初入大学 惨遭毒打 时间倒回到17 ...

  4. QQ刷屏助手

    本人小学生,求大佬轻点儿 制作原因 原因很简单,还不是想报仇呗! 代码 1 # 原理是先将需要发送的文本放到剪贴板中,然后将剪贴板内容发送到qq窗口 2 # 之后模拟按键发送enter键发送消息 3 ...

  5. 推荐大家去撸60元的阿里云ACA DevOps认证

    要试题的右边扫码支付10元,私聊博客哈,说出你微信号,留下邮箱,发你邮箱Pdf文件,这么便宜拿证!!

  6. Map类型数据导出Excel--poi

    https://blog.csdn.net/KevinChen2019/article/details/101064790 <dependency> <groupId>org. ...

  7. Redis主从、哨兵模式的搭建

    壹.Redis主从分离 准备三个redis配置文件(redis.conf),分别修改为redis6380.conf.redis6381.conf.redis6382.conf 一.配置Master 1 ...

  8. 理解和运用 ClassLoader 该篇文章就够了

    定义 根据<深入理解Java虚拟机>提到"通过一个类的全限定名(packageName.ClassName)来获取描述此类的二进制字节(class文件字节)这个动作的代码模块就叫 ...

  9. CF175C Geometry Horse 题解

    "日拱一卒,功不唐捐" 写在前面 本人因为没开long long而被迫参考楼下思路重构代码,最后发现这个问题加了long long才得以AC 进入正题 -->这是题面 这是百 ...

  10. Integer的十进制转二,八,十六进制

    1,toBinaryString(int i) 将i以二进制形式输出出来 2,toOctalString(int i)将i以八进制形式输出出来 3,toHexString(int i)将i以十六进制形 ...