<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2023-06-21T09:42:26+00:00</updated><id>/feed.xml</id><title type="html">笔记仓库</title><subtitle>八股文备忘录</subtitle><entry><title type="html">Kubernetes</title><link href="/2022/03/15/kubernetes" rel="alternate" type="text/html" title="Kubernetes" /><published>2022-03-15T00:00:00+00:00</published><updated>2022-05-01T00:00:00+00:00</updated><id>/2022/03/15/kubernetes</id><content type="html" xml:base="/2022/03/15/kubernetes">&lt;h2 id=&quot;k8s基本概念&quot;&gt;K8s基本概念&lt;/h2&gt;
&lt;h3 id=&quot;node&quot;&gt;Node&lt;/h3&gt;
&lt;p&gt;一个物理节点或虚拟机&lt;/p&gt;

&lt;h3 id=&quot;pod&quot;&gt;Pod&lt;/h3&gt;
&lt;p&gt;k8s的基本组件和最小单元，是container的抽象。用来运行容器，一个pod中可以运行多个容器，但通常只运行一个&lt;/p&gt;

&lt;h3 id=&quot;serivce&quot;&gt;Serivce&lt;/h3&gt;
&lt;p&gt;可以被附加在Pod上的静态（永久）IP，Pod和Service的生命周期是不同的，Pod停止后，Service依然存在。Service可以绑定多个Pod，起到负载均衡的作用。&lt;/p&gt;

&lt;h3 id=&quot;ingress&quot;&gt;Ingress&lt;/h3&gt;
&lt;p&gt;用于实现用域名的方式访问k8s内部应用&lt;/p&gt;

&lt;h3 id=&quot;configmap&quot;&gt;ConfigMap&lt;/h3&gt;
&lt;p&gt;来保存key-value pair配置数据&lt;/p&gt;

&lt;h3 id=&quot;secret&quot;&gt;Secret&lt;/h3&gt;
&lt;p&gt;存储加密的配置数据&lt;/p&gt;

&lt;h3 id=&quot;volume&quot;&gt;Volume&lt;/h3&gt;
&lt;p&gt;挂载一个存储到Pod中，可以是Pod运行的服务器（即Node）的存储，或是远程存储（不是K8s的一部分）。K8s不管理存储。&lt;/p&gt;

&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;/h3&gt;
&lt;p&gt;Pod的抽象，创建Pod的蓝图，可以指定由多少个Pod副本&lt;/p&gt;

&lt;h3 id=&quot;statefulset&quot;&gt;StatefulSet&lt;/h3&gt;
&lt;p&gt;数据库等有状态的Pod，通过StatefulSet来启动，而不是Deployment&lt;/p&gt;

&lt;h2 id=&quot;k8s架构&quot;&gt;K8s架构&lt;/h2&gt;
&lt;p&gt;K8s集群的Node分为master Node和worker Node，Wokrder Node中可以存在多个Pod。&lt;/p&gt;

&lt;p&gt;每个Worker Node要安装三样东西：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Contaier Runtime，如Dokcer&lt;/li&gt;
  &lt;li&gt;Kublete，接受配置，并在Node中自动Pod，分配Node的资源（CPU，RAM）给Pod&lt;/li&gt;
  &lt;li&gt;Kube Proxy，在请求转发到Pod的组件，起到负载均衡的作用&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;master-node&quot;&gt;Master Node&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Api Server，与K8s集群交互的唯一通道&lt;/li&gt;
  &lt;li&gt;Scheduler，调度器，寻找一个用来部署Pod的Node，然后由Kublete启动Pod&lt;/li&gt;
  &lt;li&gt;Controller Manager，用来检测Pod的状态，如果Pod crash后，Controller会通知Scheduler重新调度该Pod&lt;/li&gt;
  &lt;li&gt;etdc，用来表示集群的KV对，集群所有的状态改变都存储在其中。Controller的行为依据etdc来运行
Master Node一般部署多个，所以Api Server是负载均衡的，etcd是分布式存储。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;kubectl&quot;&gt;Kubectl&lt;/h2&gt;
&lt;p&gt;K8s集群的命令行工具&lt;/p&gt;

&lt;h2 id=&quot;kubectl基本命令&quot;&gt;Kubectl基本命令&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;kebuectl get node.获取所有Node&lt;/li&gt;
  &lt;li&gt;kubectl get pod.获取所有pod
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl get pod
NAME                          READY   STATUS    RESTARTS   AGE
nginx-depl-5ddc44dd46-ss96b   1/1     Running   0          3h34m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;[-o] output, -o wide 获取更多信息，
-o yaml获得详细信息，并以yaml的形式展示&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;kubectl get service&lt;/li&gt;
  &lt;li&gt;kubectl edit [deploy-name]，获得deployment的配置文件。修改配置文件后，pod在列表中替换直到消失，但是replicaset中还存在，只是数量都为0&lt;/li&gt;
  &lt;li&gt;kubectl logs [pod-name]，获取Pod中应用的日志&lt;/li&gt;
  &lt;li&gt;kubectl exec -it [pod-name] – /bin/bash，在容器中打开交互式命令行&lt;/li&gt;
  &lt;li&gt;kubectl delete [deployment]，删除deployment，会连带删除deplicaset和pod&lt;/li&gt;
  &lt;li&gt;kubectl describe pod [pod-name]，获取pod的信息，用于调试&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;创建deployment&quot;&gt;创建Deployment&lt;/h3&gt;
&lt;p&gt;kubectl create depolyment [name] –image=[image-name]，创建一个最基本的deployment，只有名称和镜像名&lt;/p&gt;

&lt;h3 id=&quot;replicaset&quot;&gt;Replicaset&lt;/h3&gt;
&lt;p&gt;Replicaset用于管理Pod的副本，实践中，不能对这个对象作修改，&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl get replicaset
NAME                    DESIRED   CURRENT   READY   AGE
nginx-depl-5ddc44dd46   1         1         1       3h34m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;k8s配置文件&quot;&gt;K8s配置文件&lt;/h3&gt;

&lt;h4 id=&quot;deployment-configuration&quot;&gt;Deployment Configuration&lt;/h4&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx-deployment&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# for deployment&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 部署多少个副本&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 在一个配置文件中再套一层&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# label用于识别一组资源，是一组键值对&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# for a pod，Pod的blueprint，&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 在Pod部署一个container&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx:1.16&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;service-configuration&quot;&gt;Service Configuration&lt;/h4&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx-service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 在deployment和pod之间建立连接&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TCP&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 请求被发往80端口&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# pod在监听那个端口，service将请求转发到对应的pod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;secret-configuration&quot;&gt;Secret Configuration&lt;/h4&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Secret&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql-secret&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Opaque&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;YWRtaW4xMjM=&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# base64编码&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;configmap-1&quot;&gt;ConfigMap&lt;/h4&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql-configmap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;database_url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql-service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;配置文件可以定义Department、Service等。配置文件由三部分组成：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;metadata。name&lt;/li&gt;
  &lt;li&gt;specification&lt;/li&gt;
  &lt;li&gt;status。自动生成，K8s用来比较期望的状态和当前状态（存储在etcd中）来做出改变&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;kubectl apply -f [file-name]。部署一个配置文件，一个配置文件可以被反复部署，多次部署后K8s会识别到是一次更新，如下：&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; nginx-deployment.yaml
deployment.apps/nginx-deployment created
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; nginx-deployment.yaml
deployment.apps/nginx-deployment configured
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;外网可访问的service&quot;&gt;外网可访问的Service&lt;/h4&gt;

&lt;p&gt;默认创建的service分配的IP是k8s集群内部IP，所以叫internal service，通过指定type为LoadBalancer和nodePort，将其变为external service。默认的service type是ClusterIP。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql-express-service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql-express&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 转发到哪个Pod&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LoadBalancer&lt;/span&gt;  
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TCP&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3333&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3333&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;nodePort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30000&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 对外暴露的端口&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;通过describe命令查看service的部署&quot;&gt;通过describe命令查看service的部署&lt;/h4&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl describe service nginx-service
...
Port:              &amp;lt;&lt;span class=&quot;nb&quot;&gt;unset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;  80/TCP
TargetPort:        8080/TCP
Endpoints:         172.17.0.3:8080,172.17.0.4:8080 // 请求转发的目的
Session Affinity:  None
Events:            &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;namespace&quot;&gt;Namespace&lt;/h2&gt;
&lt;p&gt;可以认为是K8s集群中的虚拟集群。多个团队如果再同一个命名空间部署同一个name的组件，会覆盖掉另一个。不同的namespace有隔离作用。通过kubectl获取的组件都是在默认default namespace。部署component时，通过选项-n(–namespace)指定要部署到哪个namespace。或者在配置文件的metadata中指定。
Servcie可以跨命名空间共享，指定service name时，在后面加上所在的namespace即可。&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql-configmap&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;other-namespace&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;database_url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql-service.some-namespace&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 通过后缀指定&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;ingress-1&quot;&gt;Ingress&lt;/h2&gt;

&lt;p&gt;通过暴露同一的外部访问方式，将请求转发给service。每个external service会占用一个端口，ingress可以通过域名的形式访问。&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Ingress&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app.com&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 访问的host&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 通过url定义转发规则&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;serviceName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-service&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 转发给对应的internal service&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;servicePort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# service的port&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;controller&quot;&gt;Controller&lt;/h2&gt;

&lt;p&gt;Ingress只是K8s的一层抽象，如果要让IIngress工作，必须选取一个实现，这个实现被称为Controller。Controller也是运行在K8s商上的Pod。Controller是集群中域名的entry point。&lt;/p&gt;

&lt;h3 id=&quot;安装controller&quot;&gt;安装controller&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl addons &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;ingress
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个命令会安装默认的ingress实现，即nginx-ingress-controller，并运行一个pod。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Ingress&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dashboard-ingress&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubernetes-dashboard&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dashboard.com&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;serviceName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubernetes-dashboard&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;servicePort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;helm&quot;&gt;Helm&lt;/h2&gt;
&lt;p&gt;K8s的包管理器，打包yaml文件，并将其分发到仓库上，能被其他用户共享。yaml文件的集合也被称为Helm Charts。&lt;/p&gt;

&lt;p&gt;Helm可以用于构建蓝图；或者是模板，动态将文件中的placeholder进行替换。Template file存放这yaml模板，placeholder的值在value.yml中寻找。&lt;/p&gt;

&lt;h3 id=&quot;结构&quot;&gt;结构&lt;/h3&gt;
&lt;p&gt;Helm的顶层是个文件夹，文件夹的名称是charts的名称。Chart.yaml包含chart的元数据，如名称、依赖、版本。Values.yaml包含template中的	value，可以有默认值并重载它。charts文件夹有chart的依赖，有其他chart文件。template文件夹包含模板文件。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mychart/
	Chart.yaml
	cahrts/
	templates/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当执行 helm install &lt;chartname&gt; 命令时，template会从Values中填充，生产K8s能够识别的文件。&lt;/chartname&gt;&lt;/p&gt;

&lt;h3 id=&quot;替换默认值&quot;&gt;替换默认值&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;my-value.yml &amp;lt;cahrtname&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;这个命令会将my-value.yml中的值合并到Value.yml中。&lt;/p&gt;

&lt;p&gt;或：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;xxx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;release-managerment&quot;&gt;Release Managerment&lt;/h3&gt;

&lt;h3 id=&quot;volumes&quot;&gt;Volumes&lt;/h3&gt;

&lt;p&gt;K8s有三种持久化数据的组件，并相互配合&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Persistent Volume, pv&lt;/li&gt;
  &lt;li&gt;Persistent Volume Claim, pvc&lt;/li&gt;
  &lt;li&gt;Storage Class, sc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;K8s没有开箱即用的持久化方案，需要自己配置在pod重启时保存数据。因为不知道pod何时重启，所以storage要能被所有pod访问到&lt;/p&gt;

&lt;h4 id=&quot;pv&quot;&gt;pv&lt;/h4&gt;
&lt;p&gt;pv是集群中的资源，像cpu和ram一样。用来存储数据。pv由k8s配置文件创建。&lt;/p&gt;

&lt;p&gt;pv是一个抽象概念，它必须由实际的物理存储提供实现，可以选择node的物理存储，或者aws之类的云存储。pv没有命名空间。&lt;/p&gt;

&lt;p&gt;storageClass:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;hostpath 类似docker，将node的路径挂载到pod中。但是在集群中pod可能会被调度到其他node上
```yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-volume
labels:
  type: local
spec:
#we use local node storage here!
#kubectl get storageclass
    &lt;h1 id=&quot;通过kubectl-get-storageclass获取k8s存储类型接口k8s需要知道使用的何种类型的存储&quot;&gt;通过kubectl get storageclass获取。k8s存储类型接口，k8s需要知道使用的何种类型的存储&lt;/h1&gt;
    &lt;p&gt;storageClassName: hostpath # docker提供的
capacity: # 容量
  storage: 1Gi
accessModes: # 权限&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;ReadWriteOnce
hostPath:
  path: “/mnt/data” # 在host上的哪个路径
```&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;pvc&quot;&gt;pvc&lt;/h4&gt;
&lt;p&gt;pv由管理创建，开发者在应用声明他们想用到的pv，这在pvc中配置。配置会会匹配到一个具体pv，然后应用。向pv申请存储。有namespace&lt;/p&gt;
&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;example-claim&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;storageClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hostpath&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;accessModes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ReadWriteOnce&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;50Mi&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 从pv中申请50M&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在pod应用：&lt;/p&gt;
&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres-config&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;POSTGRES_DB&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgresdb&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin123&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;StatefulSet&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serviceName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres:10.4&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;imagePullPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;IfNotPresent&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5432&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;envFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;configMapRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres-config&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 挂载到pod哪个目录下&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 指向volume&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/var/lib/postgresql/data&lt;/span&gt; 
      &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 指向一个具体的pvc&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;claimName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;example-claim&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TCP&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5432&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5432&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;应用发起存储请求时，被发送给pvc，然后pvc将请求发送给匹配的pv。找到匹配的pv后，volume会被挂载到pod上&lt;/p&gt;

&lt;h4 id=&quot;sc&quot;&gt;sc&lt;/h4&gt;
&lt;p&gt;当pvc想要pv时，sc可以用来动态创建pv。&lt;/p&gt;

&lt;h3 id=&quot;statefulset-1&quot;&gt;StatefulSet&lt;/h3&gt;

&lt;p&gt;StatefulSet和Deployment相似，可以有replica和storage。Deployment创建时会有个随机的名称，创建和删除pod是随机的。StatefulSet创建时要在yml指定名称，因为要有一种机制来决定那个节点是主节点，其他节点同步主节点数据，所以存储（pv）也是分开的。pod会共享volume，statefulset有自己的pv。pod没有DNS。&lt;/p&gt;

&lt;p&gt;statefulset在启动是不会同时启动，而是会一个个启动。stateful命名规则为，[podname]-[ordinal]，ordinal会从0累加。&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;update-node.sh&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;#!/bin/sh&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;REDIS_NODES=&quot;/data/nodes.conf&quot;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;sed -i -e &quot;/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/&quot; ${REDIS_NODES}&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;exec &quot;$@&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;redis.conf&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|+&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;cluster-enabled yes&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;cluster-require-full-coverage no&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;cluster-node-timeout 15000&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;cluster-config-file /data/nodes.conf&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;cluster-migration-barrier 1&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;appendonly yes&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;protected-mode no&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;StatefulSet&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serviceName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 暴露pod&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis:5.0.1-alpine&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6379&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;client&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16379&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gossip&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/conf/update-node.sh&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;redis-server&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/conf/redis.conf&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POD_IP&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;fieldRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;fieldPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;status.podIP&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;conf&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/conf&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# redis写入数据的路径&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/data&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;conf&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;configMap&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;defaultMode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0755&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;volumeClaimTemplates&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 通过pvc来持久化数据，为每个pod动态提供volume&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;accessModes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ReadWriteOnce&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;storageClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hostpath&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;50Mi&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 在data这个volume里面，声明50M的存储&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;clusterIP&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;None&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6379&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6379&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;client&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16379&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16379&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gossip&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis-cluster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><category term="Kubernetes" /><summary type="html">K8s基本概念 Node 一个物理节点或虚拟机</summary></entry><entry><title type="html">Docker速成</title><link href="/2022/03/12/Docker" rel="alternate" type="text/html" title="Docker速成" /><published>2022-03-12T00:00:00+00:00</published><updated>2022-03-12T00:00:00+00:00</updated><id>/2022/03/12/Docker</id><content type="html" xml:base="/2022/03/12/Docker">&lt;h2 id=&quot;拉取镜像&quot;&gt;拉取镜像&lt;/h2&gt;
&lt;p&gt;docker pull REPOSITORY:tag。tag为版本号，默认为latest表示最新版。&lt;/p&gt;

&lt;h2 id=&quot;运行容器&quot;&gt;运行容器&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;docker run REPOSITIRY&lt;/strong&gt;。不指定参数下运行在attach mode下，terminal被阻塞。
[-d,–detach] 容器在terminal的后台运行，此时会打印容器id
[-p,–publish] -p 80:8080，在主机和dokcer容器中创建桥接网络
[–name] 只指定容器name&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docker stop containerId&lt;/strong&gt;，停止一个容器。停止的容器可以使用相同的id再次启动。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docker start containerId&lt;/strong&gt; 重启容器，重启的容器拥有docker run时所有的属性&lt;/p&gt;

&lt;h2 id=&quot;docker命令&quot;&gt;Docker命令&lt;/h2&gt;

&lt;h3 id=&quot;docker-ps&quot;&gt;docker ps&lt;/h3&gt;
&lt;p&gt;查看所有运行的容器
[-a] 显示所有容器，包括运行和停止的&lt;/p&gt;

&lt;h3 id=&quot;docker-log-idname&quot;&gt;docker log [id|name]&lt;/h3&gt;
&lt;p&gt;查看容器日志&lt;/p&gt;

&lt;h3 id=&quot;docker-exec-options-container-command-arg&quot;&gt;docker exec [OPTIONS] CONTAINER COMMAND [ARG…]&lt;/h3&gt;
&lt;p&gt;在运行的容器中运行一个命令
&lt;em&gt;docker exec -it 5af5c338dc81 /bin/bash&lt;/em&gt; 进入容器命令行。exit可以退出
[-it] 以交互式命令运行（interactive terminal）&lt;/p&gt;

&lt;h2 id=&quot;docker-network&quot;&gt;Docker network&lt;/h2&gt;

&lt;p&gt;docker network ls&lt;/p&gt;

&lt;p&gt;docker network create [name] 创建一个网络，让容器之间可以仅通过容器名称来通讯.在需要ip和端口的地方可直接用servicename替代&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;docker run &lt;br /&gt;
-p 27017:27017 &lt;br /&gt;
-d &lt;br /&gt;
-e MONGO_INITDB_ROOT_USERNAME=admin &lt;br /&gt;
-e MONGO_INITDB_ROOT_PASSWORD=admin123 &lt;br /&gt;
–name mongodb &lt;br /&gt;
–net mongo-network &lt;br /&gt;
mongo&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;docker run -d -p 8081:8081 &lt;br /&gt;
-e ME_CONFIG_MONGODB_AUTH_USERNAME=admin &lt;br /&gt;
-e ME_CONFIG_MONGODB_AUTH_PASSWORD=admin123 &lt;br /&gt;
–net mongo-network &lt;br /&gt;
–name mongo-express &lt;br /&gt;
-e ME_CONFIG_MONGO_SERVER=mongodb &lt;br /&gt;
-e ME_CONFIG_MONGODB_URL=”mongodb://mongodb:27017”
mongo-express&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;docker-compose&quot;&gt;Docker Compose&lt;/h2&gt;
&lt;p&gt;运行多容器应用的工具。配置文件中不用配置网络，Docker会自己处理&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3.9&quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# optional since v1.27.0&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;mongodb&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# name of the container&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mongo&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# image&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# -p&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;27017:27017&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# HOST:CONTAINER&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin123&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;mongo-express&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mongo-express&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8081:8081&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ME_CONFIG_MONGODB_ADMINUSERNAME&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ME_CONFIG_MONGODB_ADMINPASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin123&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mongodb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;blockquote&gt;
  &lt;p&gt;docker-compose -f FILE COMMAND
[-f] 
[COMMAND] up运行,down移除容器和network&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;dockerfile&quot;&gt;Dockerfile&lt;/h2&gt;
&lt;p&gt;构建docker镜像的文件&lt;/p&gt;

&lt;h3 id=&quot;from&quot;&gt;FROM&lt;/h3&gt;
&lt;p&gt;所有dockerfile的第一个语句，基于其他镜像&lt;/p&gt;

&lt;h3 id=&quot;env-propertyvalue&quot;&gt;ENV PROPERTY=VALUE&lt;/h3&gt;
&lt;p&gt;设置环境变量&lt;/p&gt;

&lt;h3 id=&quot;run&quot;&gt;RUN&lt;/h3&gt;
&lt;p&gt;执行Linux命令&lt;/p&gt;

&lt;h3 id=&quot;copy&quot;&gt;COPY&lt;/h3&gt;
&lt;p&gt;将本机文件复制到镜像中&lt;/p&gt;

&lt;h3 id=&quot;cmd&quot;&gt;CMD&lt;/h3&gt;
&lt;p&gt;一个dockerfile只能有一个CMD命令，可以表示程序的entry point&lt;/p&gt;

&lt;h3 id=&quot;镜像构建&quot;&gt;镜像构建&lt;/h3&gt;
&lt;p&gt;在Dockerfile所在文件打开terminal&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;docker build -t my-app:1.0 .
[t] tag&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;dokcer-repository&quot;&gt;Dokcer Repository&lt;/h2&gt;

&lt;h3 id=&quot;登录&quot;&gt;登录&lt;/h3&gt;
&lt;p&gt;docker login –username={username} {url}&lt;/p&gt;

&lt;h3 id=&quot;重命名镜像&quot;&gt;重命名镜像&lt;/h3&gt;
&lt;p&gt;docker tag [ImageId] domain:host/url/my-app:[镜像版本号]&lt;/p&gt;

&lt;h3 id=&quot;推送镜像&quot;&gt;推送镜像&lt;/h3&gt;
&lt;p&gt;docker push fullImageName&lt;/p&gt;

&lt;h2 id=&quot;volumes&quot;&gt;Volumes&lt;/h2&gt;
&lt;p&gt;将物理路径挂载到docker镜像&lt;/p&gt;

&lt;h3 id=&quot;run挂载&quot;&gt;Run挂载&lt;/h3&gt;
&lt;p&gt;docker run -v [host-directory]:[container-directory] [image]
[-v] 将物理路径挂载到镜像中
这种方式称为Host Volume&lt;/p&gt;

&lt;h3 id=&quot;anonymous-volumes&quot;&gt;Anonymous Volumes&lt;/h3&gt;
&lt;p&gt;docker run -v [container-directory] [image]
由docker自动挂载到host目录中，自动创建名为/var/lib/docker/volumes/random-hash/_data&lt;/p&gt;

&lt;h3 id=&quot;name-volumes&quot;&gt;Name Volumes&lt;/h3&gt;
&lt;p&gt;docker run -v [name]:[container-directory] [image]
生产环境中最常用的&lt;/p&gt;

&lt;h3 id=&quot;volumes路径&quot;&gt;Volumes路径&lt;/h3&gt;
&lt;p&gt;/var/lib/docker/volumes&lt;/p&gt;</content><author><name></name></author><category term="Docker" /><summary type="html">拉取镜像 docker pull REPOSITORY:tag。tag为版本号，默认为latest表示最新版。</summary></entry><entry><title type="html">Kafka使用及原理</title><link href="/2022/02/20/kafka" rel="alternate" type="text/html" title="Kafka使用及原理" /><published>2022-02-20T00:00:00+00:00</published><updated>2022-05-29T00:00:00+00:00</updated><id>/2022/02/20/kafka</id><content type="html" xml:base="/2022/02/20/kafka">&lt;h2 id=&quot;常见命令&quot;&gt;常见命令&lt;/h2&gt;

&lt;h3 id=&quot;topic&quot;&gt;Topic&lt;/h3&gt;
&lt;h4 id=&quot;创建topic&quot;&gt;创建Topic&lt;/h4&gt;

&lt;h4 id=&quot;查看topic&quot;&gt;查看topic&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./kafka-topics.sh &lt;span class=&quot;nt&quot;&gt;--zookeeper&lt;/span&gt; localhost: 2181/kafka &lt;span class=&quot;nt&quot;&gt;--describe&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--topic&lt;/span&gt; topic-demo
Topic: &lt;span class=&quot;nb&quot;&gt;test     &lt;/span&gt;TopicId: Zi5yXIXcTZuLE_MkTyC-Wg PartitionCount: 4       ReplicationFactor: 3    Configs:
        Topic: &lt;span class=&quot;nb&quot;&gt;test     &lt;/span&gt;Partition: 0    Leader: 2       Replicas: 2,0,1 Isr: 2,0,1
        Topic: &lt;span class=&quot;nb&quot;&gt;test     &lt;/span&gt;Partition: 1    Leader: 0       Replicas: 0,1,2 Isr: 0,1,2
        Topic: &lt;span class=&quot;nb&quot;&gt;test     &lt;/span&gt;Partition: 2    Leader: 1       Replicas: 1,2,0 Isr: 1,2,0
        Topic: &lt;span class=&quot;nb&quot;&gt;test     &lt;/span&gt;Partition: 3    Leader: 2       Replicas: 2,1,0 Isr: 2,1,0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;[–describe] 展示主题的更多具体信息&lt;/p&gt;

&lt;h4 id=&quot;查看所有topic&quot;&gt;查看所有topic&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./kafka-topics.sh &lt;span class=&quot;nt&quot;&gt;--list&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--zookeeper&lt;/span&gt; localhost:2181
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;producer&quot;&gt;Producer&lt;/h3&gt;
&lt;h4 id=&quot;控制台生产者&quot;&gt;控制台生产者&lt;/h4&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./kafka-console-producer.sh &lt;span class=&quot;nt&quot;&gt;--broker-list&lt;/span&gt; localhost:9092 &lt;span class=&quot;nt&quot;&gt;--topic&lt;/span&gt; test-topic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;consumer&quot;&gt;Consumer&lt;/h3&gt;

&lt;p&gt;从最后一条消息的偏移量+1开始消费&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./kafka-console-consumer.sh &lt;span class=&quot;nt&quot;&gt;--bootstrap-server&lt;/span&gt; localhost:9092 &lt;span class=&quot;nt&quot;&gt;--topic&lt;/span&gt; test-topic 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;[–from-beginning] 从头开始消费
[–consumer-property] group.id={id} 设置消费者组。同一个消费者组中的消费者，如果订阅了相同的topic，只能有一个消费者收到消息（单播消息）。不同消费者组的消费者只能有一个消费者收到消息（多播）&lt;/p&gt;

&lt;p&gt;查看消费者组
./kafka-consumer-groups.sh –bootstrap-server localhost:9092 –list
查看kafka中的消费者组&lt;/p&gt;

&lt;p&gt;./kafka-consumer-groups.sh –bootstrap-server localhost:9092 –describe –group test-group
查看组的详细信息:
GROUP           TOPIC           PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             CONSUMER-ID                                                HOST            CLIENT-ID
test-group      test-topic      0          29              29              0               consumer-test-group-1-ed58cfd7-73ac-48c6-8f36-cbb58c833c96 /127.0.0.1      consumer-test-group-1&lt;/p&gt;

&lt;p&gt;./kafka-topics.sh –describe –zookeeper localhost:2181 –topic my-topic
产看消息状态&lt;/p&gt;

&lt;p&gt;Topic: my-topic TopicId: nlRdagkNQ3-2Rfr6FEP9sA PartitionCount: 2       ReplicationFactor: 3    Configs:
        Topic: my-topic Partition: 0    Leader: 0       Replicas: 2,1,0 Isr: 0,1,2
        Topic: my-topic Partition: 1    Leader: 0       Replicas: 0,2,1 Isr: 0,1,2&lt;/p&gt;

&lt;p&gt;同一topic 的不同分区Leader可能不同&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;ps -aux&lt;/td&gt;
      &lt;td&gt;grep server1.properties&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;./kafka-consumer-groups.sh –bootstrap-server iot-kafka-service:9092 –describe –all-groups
查看所有消息的消费状态&lt;/p&gt;

&lt;h2 id=&quot;同步与异步&quot;&gt;同步与异步&lt;/h2&gt;
&lt;p&gt;同步发送消息是，生产者会阻塞，直到broker发送ack消息，超时时间为3秒，重试次数为3次。ack是可以设置的，&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;0，发送给cluster后直接返回ack，不管broker有没有收到&lt;/li&gt;
  &lt;li&gt;1，Leader收到消息，并写入log文件&lt;/li&gt;
  &lt;li&gt;-1/all，leader写入log文件，并且同步给其它follower，可以通过min.insync.replicas指定最少由多少个follower收到消息，默认为1，推荐为大于等于2，方便重新选举leader&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每个生产者默认有个32m的缓冲区，消息首先被塞入缓冲区，生产者本地线程每次拉取16k的消息。如果缓冲区没有16k，10ms后自动拉取。&lt;/p&gt;

&lt;h2 id=&quot;polll&quot;&gt;Polll&lt;/h2&gt;
&lt;p&gt;两次poll时间超过30s，Kafka会认为消费者消费能力不足，改消费者会被踢出消费组&lt;/p&gt;

&lt;h2 id=&quot;消费方式&quot;&gt;消费方式&lt;/h2&gt;
&lt;p&gt;指定分区消费 从头消费 指定offset消费 指定时间消费
新的消费组启动后，默认从消费者启动后开始消费，也可指定从头开始消费，以后启动时从记录到的offset处开始消费。&lt;/p&gt;

&lt;h2 id=&quot;controller&quot;&gt;Controller&lt;/h2&gt;
&lt;p&gt;当分区的Leader副本出现故障时，由controller负责选举新的分区副本。。broker在创建的时候会向zookeeper注册创建临时序号节点，谁最小谁就是controller。Topic中有个isr属性会对broker集群的状态做排序，选取排在Leader后面的broker作为新的Leader。&lt;/p&gt;

&lt;p&gt;当isr发生变化、topic分区数增加时，由controller负责通知其他broker&lt;/p&gt;

&lt;p&gt;When a new event is published to a topic, it is actually appended to one of the topic’s partitions.A common production setting is a replication factor of 3, i.e., there will always be three copies of your data. This replication is performed at the level of topic-partitions.&lt;/p&gt;

&lt;p&gt;To get hands-on experience with Kafka https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying&lt;/p&gt;

&lt;h2 id=&quot;kafka客户端&quot;&gt;Kafka客户端&lt;/h2&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaProducerAnalysis&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brokerList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;localhost:9092&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;topic-demo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bootstrap.servers&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brokerList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;key.serializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;org.apache.kafka.common.serialization.StringSerializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value.serializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;org.apache.kafka.common.serialization.StringSerializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;client.id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;producer.client.id.demo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;KafkaProducer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaProducer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, Kafka!&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ProducerRecord中含有多种属性&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProducerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//主题&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//分区号&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Headers&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//消息头部&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;K&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//键&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//值&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//消息的时间戳&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//省略其他成员方法和构造方法&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;消息的键是消息的附加信息，同时也用来计算分区号。&lt;/p&gt;

&lt;p&gt;消息发送有三种模式：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;发后即忘（fire-and-forget）。只管发，其他都不管&lt;/li&gt;
  &lt;li&gt;同步（sync）&lt;/li&gt;
  &lt;li&gt;异步（async）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要实现同步的发送方式，可以利用返回的 Future 对象实现。&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ExecutionException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 或&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RecordMetadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;future&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;RecordMetadata&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ExecutionException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;异常分为可重试异常和不可重试异常。常见的可重试异常有：NetworkException（网络异常）、LeaderNotAvailableException（eader 副本下线而新的 leader 副本选举完成之前）、UnknownTopicOrPartitionException、NotEnoughReplicasException、NotCoordinatorException 等。可重试异常可以配置retries 属性进行重试，如果重试成功则不抛异常。&lt;/p&gt;

&lt;p&gt;异步发送可通过指定Callback的形式进行：&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onCompletion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RecordMetadata&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 消息发送成功时，metadata 不为 null 而 exception 为 null；消息发送异常时，metadata 为 null 而 exception 不为 null。&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;回调函数的调用在一个分区中也会保持和发送顺序相同的顺序调用。&lt;/p&gt;

&lt;h3 id=&quot;消费者组&quot;&gt;消费者组&lt;/h3&gt;

&lt;p&gt;Kaka消费者&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 配置消费者客户端参数及创建相应的消费者实例。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 订阅主题。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 拉取消息并消费。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 提交消费位移。&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 关闭消费者实例。&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaConsumerAnalysis&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brokerList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;localhost:9092&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;topic-demo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;group.demo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AtomicBoolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isRunning&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AtomicBoolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;key.deserializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;org.apache.kafka.common.serialization.StringDeserializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value.deserializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;org.apache.kafka.common.serialization.StringDeserializer&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bootstrap.servers&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brokerList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;group.id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;client.id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;consumer.client.id.demo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;KafkaConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;KafkaConsumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Arrays&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;asList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 多次调用以最后一次subcribe，以最后一次调用为准&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isRunning&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;ConsumerRecords&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 
                    &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ofMillis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;topic = &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; 
                            &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, partition = &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; 
                            &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, offset = &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;key = &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                            &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, value = &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;//do something to process record.&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;occur exception &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;rebalance&quot;&gt;rebalance&lt;/h2&gt;
&lt;p&gt;分区所有权从一个消费者转移到另一个消费者,为消费者组具备高可用和可伸缩性提供保障，可以为我们方便的向消费者组安全地删除和添加消费者，rebalance期间，消费者组内的消费者无法消费数据（消费者组不可用）。&lt;/p&gt;

&lt;p&gt;当分区分配给另一个消费者，消费者当前状态会丢失。比如消费者拉取消息后还没来的及提交，就发生了rebalance，这个分区被分配给另一个消费者，原来的消息会被重新消费一遍，也就是发生了重复消费。一般情况下，应尽量避免不必要的rebalance的发生。&lt;/p&gt;

&lt;p&gt;在subscribe方法中：&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topics&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsumerRebalanceListener&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Pattern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsumerRebalanceListener&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;有ConsumerRebalanceListener参数，用来监听rebalance前后的准备和收尾工作。ConsumerRebalanceListener接口中有两个方法：&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsumerRebalanceListener&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 会在rebalance之前和消费者停止消费之后调用，&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 可用来处理位移提交，防止重复消费&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onPartitionsRevoked&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TopicPartition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partitions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 会在rebalance之后和消费者开始消费之前调用。&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 参数标识rebalance后的分区&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onPartitionsAssigned&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TopicPartition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partitions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TopicPartition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OffsetAndMetadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentOffsets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Arrays&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;asList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConsumerRebalanceListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onPartitionsRevoked&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TopicPartition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partitions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;commitSync&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentOffsets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
	        &lt;span class=&quot;n&quot;&gt;currentOffsets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onPartitionsAssigned&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TopicPartition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partitions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//do nothing.&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isRunning&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ConsumerRecords&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ofMillis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConsumerRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//process the record.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;currentOffsets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TopicPartition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OffsetAndMetadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;commitAsync&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentOffsets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;也已通过在onPartitionsRevoked中将offset存储到数据库，然后onPartitionsAssigned时将数据取出并调用seek方法。&lt;/p&gt;

&lt;p&gt;触发Rebalance机制的前提是消费者没有指定分区消费。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果消费者心跳回复超过10s、或两次poll之间超过30s，Kafka会认为该消费者消费能力太弱，会将其踢出消费者组，topic的风区在消费者组中可能没有消费者消费，Kafka会将分区交给其他消费者，触发Rebalance机制。&lt;/li&gt;
  &lt;li&gt;新加消费者也会触发Rebalance机制&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><category term="kafka" /><summary type="html">常见命令</summary></entry><entry><title type="html">网络安全概述</title><link href="/2020/11/16/network-security-overview" rel="alternate" type="text/html" title="网络安全概述" /><published>2020-11-16T20:24:25+00:00</published><updated>2020-11-16T20:24:25+00:00</updated><id>/2020/11/16/network-security-overview</id><content type="html" xml:base="/2020/11/16/network-security-overview">&lt;h1 id=&quot;非对称加密&quot;&gt;非对称加密&lt;/h1&gt;

&lt;p&gt;引入Alice作为发送方，Bob作为接收方。
非对称加密技术要求双方公开一个用于加密报文的密钥，称为公钥（K+）。然后使用不公开的、仅双方知道的密钥（K-）进行解密。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/computer-networking/public-key-cryptography.png&quot; alt=&quot;非对称加密技术&quot; /&gt;&lt;/p&gt;

&lt;p&gt;RSA是非对称加密技术的代名词，采用模n运算，所以RSA会将报文作为一个数字来看待。RSA算法相当耗时，如果要发送大量数据，发送数据前，先用RSA再加密一个密钥（session key，Ks），然后使用对称加密技术发送数据。典型的RSA公钥长度为2048bit。&lt;/p&gt;

&lt;h1 id=&quot;对称加密&quot;&gt;对称加密&lt;/h1&gt;

&lt;p&gt;Alice和Bob共用一个密钥加密。此处涉及如何安全地共享密钥。常见的对称加密算法有DES，AES，3DES等。&lt;/p&gt;

&lt;h1 id=&quot;完整性与数字签名&quot;&gt;完整性与数字签名&lt;/h1&gt;

&lt;p&gt;为了防止报文中途被人篡改，通常需要通过一个Hash函数（摘要算法）对报文计算一个摘要或散列值（Hash）。即：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;找到任意两个报文x、y使得H(x) = H(y)，在计算上是不可能的&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;常用的Hash函数有MD5（生成128比特的散列值）、SHA-1（生成160比特散列值）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/computer-networking/public-key-cryptography.png&quot; alt=&quot;非对称加密技术&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Alice发送报文时，将生成的摘要附加到原始报文中，形成扩展报文 &lt;strong&gt;(m, H(m))&lt;/strong&gt;，Bob收到后计算 &lt;strong&gt;H(m)&lt;/strong&gt;*，并于摘要对比。&lt;/p&gt;

&lt;h2 id=&quot;mac&quot;&gt;MAC&lt;/h2&gt;

&lt;p&gt;用密钥s级联原文m形成的散列值H(m+s)称为报文鉴别码（MAC）。鉴别报文完整性和防篡改。&lt;/p&gt;

&lt;h2 id=&quot;数字签名&quot;&gt;数字签名&lt;/h2&gt;

&lt;p&gt;为了证明报文确实是Bob发送的，需要在原报文上做特殊处理，对报文进行数字签名达到这个目的。&lt;/p&gt;

&lt;p&gt;Bob发送明文m前，先对m生成一个摘要，并使用他的私钥对H(m)，进行加密，得到 &lt;strong&gt;K-(H(m))&lt;/strong&gt;，对原文直接加密计算代价太昂贵。Alice收到报文后，对明文m进行摘要计算，得到散列结果&lt;strong&gt;H(m)&lt;/strong&gt;，然后用Bob的公钥K+解密获得原摘要，然后对两个摘要做对比。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/computer-networking/verifying-signed-message.png&quot; alt=&quot;数字签名&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ca&quot;&gt;CA&lt;/h2&gt;

&lt;p&gt;这种签名方式有个缺点，就是可以在Alice的电脑上伪造Bob的公钥，所以要将公钥交给认证中心（CA）保管，由CA负责将公钥与实体绑定，并生成一个证书。证书包含公钥和公钥所有者全局唯一身份信息（IP地址、名字）。CA使用自己的私钥对Bob的公钥和和相关信息进行加密生成证书。&lt;/p&gt;

&lt;p&gt;Bob拿到证书后，每次向Alice发送信息时，都要附上该证书，Alice收到信息后，用CA的公钥解开数字证书，拿到Bob的真实公钥。&lt;/p&gt;

&lt;h1 id=&quot;ssl&quot;&gt;SSL&lt;/h1&gt;

&lt;p&gt;TCP的安全增强版被称为安全套接字（SSL），SSL的版本3被称为运输层安全性（TSL），统称为SSL/TSL。&lt;/p&gt;

&lt;p&gt;SSL需要双方通讯前交换密钥，协商对称加密算法、公钥算法、MAC。&lt;/p&gt;

&lt;p&gt;SSL双方要生成四种密钥：用于加密会话的加密密钥&lt;strong&gt;E&lt;/strong&gt;，用于加密MAC的MAC密钥M。加密的MAC实际上为：(数据, MAC密钥M, 不重复的序号)。不重复数可以防止被攻击。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$E_B$ 从Bob到Alice的会话密钥&lt;/li&gt;
  &lt;li&gt;$M_B$ 从Bob到Alice的MAC密钥&lt;/li&gt;
  &lt;li&gt;$E_A$ 从Alice到Bob的会话密钥&lt;/li&gt;
  &lt;li&gt;$M_A$ 从Alice到Bob的MAC密钥&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/computer-networking/ssl-record.png&quot; alt=&quot;SSL记录格式&quot; /&gt;&lt;/p&gt;

&lt;p&gt;TSL握手过程：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;B: 客户发送它所支持的算法列表，和一个不重复数&lt;/li&gt;
  &lt;li&gt;A: &lt;em&gt;服务器选择一种对称算法（如AES）、公钥算法（如RSA）、MAC算法。把这些选择连同CA证书和服务器不重复数发送给客户端&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;B: 客户端验证证书、提取公钥，生成前主密钥（PMS），用公钥加密PMS发送给服务器&lt;/li&gt;
  &lt;li&gt;A&amp;amp;B: 使用相同的密钥导出函数（SSL标准定义），客户和服务器独立从PMS和不重复数中计算出主密钥（MS）。然后该MS被切分生成两个密码和MAC密钥&lt;/li&gt;
  &lt;li&gt;B: 客户发送所有握手报文的MAC&lt;/li&gt;
  &lt;li&gt;A: 服务发送所有握手报文的MAC&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;最后两个步骤可以防止握手被篡改。因为握手报文是明文发送的，攻击者可以从算法列表中删除一些算法强的。如果最后的MAC不一致，服务可以选择断开SSL。&lt;/p&gt;

&lt;p&gt;发送不重复数也可以使每次使用的密钥不同。&lt;/p&gt;

&lt;h2 id=&quot;ssh&quot;&gt;SSH&lt;/h2&gt;

&lt;p&gt;SSH是目前较可靠，专为远程登录会话和其他网络服务提供安全性的协议。SSH指纹是服务端公钥的摘要，&lt;/p&gt;

&lt;p&gt;SSH握手过程：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;版本号协商，选择使用SSH1还是SSH2&lt;/li&gt;
  &lt;li&gt;密钥算法协商，选择公钥算法、加密算法、MAC算法&lt;/li&gt;
  &lt;li&gt;利用DH交换算法，计算出密钥
    &lt;ol&gt;
      &lt;li&gt;Bob生成公钥和密钥，并向Alice发送公钥&lt;/li&gt;
      &lt;li&gt;Alice利用Bob的公钥构造密钥对，将生成的公钥发送给Bob&lt;/li&gt;
      &lt;li&gt;Alice和Bob都用对方的公钥和自己的私钥利用算法计算出相同的密钥&lt;/li&gt;
      &lt;li&gt;利用这个密钥进（session-key）行对称加密&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;利用密钥进行身份验证&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;处理密码验证的形式，还可以通过证书验证。客户端通过ssh-keygen生成密钥对，并将公钥保存在server的authorized_key文件中。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;客户端发送认证请求，发送public key发送给服务端&lt;/li&gt;
  &lt;li&gt;服务端确认公钥存在，利用公钥对一个随机的256位字符串进行MD5加密，并发送给客户端&lt;/li&gt;
  &lt;li&gt;客户端用私钥进行解密，并将它和一个会话ID生成MD5值发送给服务器&lt;/li&gt;
  &lt;li&gt;服务端同样生成原文和会话ID的MD5摘要，并与客户端的进行比较&lt;/li&gt;
&lt;/ol&gt;</content><author><name></name></author><category term="ComputerNetworking" /></entry><entry><title type="html">Java线程间协作</title><link href="/2020/01/01/java-thread-coordinate" rel="alternate" type="text/html" title="Java线程间协作" /><published>2020-01-01T00:00:00+00:00</published><updated>2020-01-10T00:00:00+00:00</updated><id>/2020/01/01/java-thread-coordinate</id><content type="html" xml:base="/2020/01/01/java-thread-coordinate">&lt;h2 id=&quot;waitnotify&quot;&gt;wait/notify&lt;/h2&gt;

&lt;p&gt;Object.wait()和Object.wait(long)，以及Object.notify()和Object.notifyAll()可用于等待和通知。wait用于暂停一个线程，生命周期状态变为WAITING；notify用于唤醒一个暂停的线程。执行Object.wait的称为等待线程。Java中的任何对象都能实现等待和通知。&lt;/p&gt;

&lt;p&gt;实现Object.wait的通用模板：&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 等待通知线程更新&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;someObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;doActioin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;someObject.wait会以原子的形式使执行线程暂停，并释放其持有的someObject内部锁，此时wait调用并未返回。notify方法可以唤醒任意一个线程，被唤醒的线程需再次申请someObject对应的内部锁。被唤醒线程再次持有someObject对应的内部锁的情况下继续执行wait剩余的指令，直到wait返回。&lt;/p&gt;

&lt;p&gt;通知线程模板&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;someObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;等待线程被唤醒后继续占有处理器，该线程可能会被其它线程占用内部锁导致上下文切换。notify方法要尽量靠近临界区结束的地方。&lt;/p&gt;

&lt;p&gt;JVM会为每个对象维护一个入口集（EntrySet）用于存储申请该对象内部锁的线程。此外还会维护一个等待集（WaitSet）的队列，用于存储对象上的等待线程。Object.wait将当前线程暂停并释放相应内部锁的同时将调用线程存入等待集。对象 的notify方法调用后，任意一个线程会被唤醒，被唤醒线程仍在等待集中，直到相应线程持有内部锁，wait会将线程从等待集中移除，接着wait方法返回。&lt;/p&gt;

&lt;h2 id=&quot;waitnotify问题&quot;&gt;wait/notify问题&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;过早唤醒。通知线程唤醒了等待在某个对象的线程，但此时等等待线程所需的条件并没有得到满足。可以利用Conditional解决。&lt;/li&gt;
  &lt;li&gt;信号丢失。等待线程没有结果条件判断直接调用wait，那么会出现一种情形：通知线程在等待线程进入临界区之前更新了共享变量，并调用了notify，然而等待线程直接调用wait，此时就没有其它线程进行通知。在需要调用notifyAll的地方调用了notify也会导致这种现象，如随机唤醒的线程使用了其它的保护条件。&lt;/li&gt;
  &lt;li&gt;欺骗性唤醒。在没有任何线程调用notify的情况下被唤醒&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thread.join(long)方法可以等待一个线程结束后继续执行,它的底层采用了wait/notify来实现。调用时，如果检测到目标线程未结束，就调用wait来暂停当前线程。JVM会在目标线程run方法结束后调用notifyAll通知所有等待线程。目标线程充当了同步对象（Object.wait），&lt;/p&gt;

&lt;h2 id=&quot;条件变量&quot;&gt;条件变量&lt;/h2&gt;

&lt;p&gt;wait/notify过于底层，且存在过早唤醒的问题。Object.wait(long)也无法区分其返回是由于超时还是被通知线程唤醒等问题。&lt;/p&gt;

&lt;p&gt;Conditional可以作为wait/notify的替代品来实现通知，解决了过早唤醒、区分超时等待的问题。Conditional接口的await/signal/signalAll相当于Object的wait/notify/notifyAll。&lt;/p&gt;

&lt;p&gt;Conditional模板与wait/notify一样。&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConditionalUsage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReentrantLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Condition&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;con&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;doAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;updateState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;await/signal执行时，执行线程需持有条件变量的 &lt;strong&gt;显示锁&lt;/strong&gt; 。循环语句与目标动作在同一个显示锁引导的临界区内，防止欺骗性唤醒和信号丢失。await和wait类似，暂停当前线程的同时也使当前线程释放持有的锁，这是调用还没有返回。被唤醒的等待线程继续运行后，也需要申请相应的显示锁，被唤醒线程申请到锁后，await调用才返回。&lt;/p&gt;

&lt;p&gt;通过在应用代码层为每个保护条件建立一个条件变量来解决过早唤醒的问题，让不同的线程在不同的条件变量上等待；并让通知线程尽唤醒相应保护条件的线程。Condition在唤醒时，能够分辨是超时还是正常的signal唤醒。Condition.awaitUntil(Date)返回true，则说明是正常唤醒，可以通过返回值做进一步的动作。&lt;/p&gt;

&lt;h2 id=&quot;countdownlatch&quot;&gt;CountDownLatch&lt;/h2&gt;

&lt;p&gt;CountDownLatch可以实现一个或多个线程等待其它线程完成一组特定的操作（先决操作）之后才继续运行。&lt;/p&gt;

&lt;p&gt;CountDownLatch内部维护一个 &lt;strong&gt;未完成先决操作&lt;/strong&gt; 数量的计数器。CountDownLatch.countDown()每调用一次，计数器减1。CountDownLatch.await()方法相当于一个保护方法，保护条件为“计数器值为0”。当计数器不为0时，调用await的线程被暂停，countDown()相当于通知方法，当计数器值为0时，唤醒该实例上所有等待线程。&lt;/p&gt;

&lt;p&gt;一个CountDownLatch实例只能实现一次等待和唤醒，当计数器达到0后，继续调用countDown不会有任何作用，也不会唤醒线程；同时继续调用await不会暂停线程。&lt;/p&gt;

&lt;p&gt;调用await和countDown时无需加锁。CountDownLatch.await(long, TimeUnit)允许设置超时时间，返回值为true说明为正常唤醒。&lt;/p&gt;

&lt;h2 id=&quot;cyclicbarrier&quot;&gt;CyclicBarrier&lt;/h2&gt;

&lt;p&gt;CyclicBarrier用于多个线程互相等到代码执行到某个地方，这是这些线程才能继续执行。Cyclic意思是该类可以重复使用。使用CyclicBarrier的线程称为参与方（party），参与方执行执行CyclicBarrier.await()就可以实现等待。CyclicBarrier内部维护了显示锁，可以区分最后一个调用await的参与方。除了最后一个线程外的任何参与方调用await都会被暂停（WATING）。最后一个线程执行await的时候，会使得所有参与方都被唤醒，但是最后一个线程本身不会被唤醒。&lt;/p&gt;

&lt;p&gt;CyclicBarrier(int, Runnable)允许指定一个任务，在最后一个线程执行await时、其它线程唤醒前执行该任务。&lt;/p&gt;

&lt;h2 id=&quot;阻塞队列&quot;&gt;阻塞队列&lt;/h2&gt;

&lt;p&gt;JDK1.5引入了juc.BlockingQueue接口，它定义了一种线程安全队列——阻塞队列。BlockingQueue的常见实现类有ArrayBlockingQueue、LinkedBlockingQueue和SynchronousQueue。阻塞队列可按容量是否受限划分为有界队列和无界队列。有界队列由应用程序指定，无界队列最大存储容量为Integer.MAX_VALUE。使用有界队列的好处是“反压”——当消费者能力跟不上时，队列积满，生产者会被暂停。&lt;/p&gt;

&lt;p&gt;put操作将数据入队，生产者执行的put操作对后续消费者的take操作是可见的、有序的。&lt;/p&gt;

&lt;p&gt;ArrayBlockingQueue在内部实现时，put和take操作用的是同一把锁，可能导致锁的高争用，造成过多的上下文切换。&lt;/p&gt;

&lt;p&gt;LinkedBlockingQueue既能做有界队列，又能做无界队列。它的优点是在内部实现时，put、take操作时用的是两把显示锁，这降低了锁争用的可能性。&lt;/p&gt;

&lt;p&gt;SynchronousQueue是空间为1的有界队列，它的内部不维护任何存储空间。生产者执行了SynchronousQueue.put时，该生产者被暂停，知道有消费线程执行take。类似，如果消费者执行了take，它会被暂停，知道生成者执行了put。&lt;/p&gt;

&lt;p&gt;SynchronousQueue和ArrayBlockingQueue既支持非公平调度也支持公平调度。LinkedBlockingQueue只支持非公平调度。&lt;/p&gt;

&lt;p&gt;阻塞队列支持非阻塞调用，offer(E)和poll。offer通过返回false表示入队失败，poll通过返回null表示队列为空。&lt;/p&gt;

&lt;h2 id=&quot;信号量&quot;&gt;信号量&lt;/h2&gt;

&lt;p&gt;信号量Semaphore用来控制对一个资源的访问次数，Semaphore(int)用来指定一个资源能同时被访问几次。Semaphore.acquire()用来申请配额，在调用成功后会使Semaphore内部的计数器减1，当配额不足时，会使当前线程睡眠。Semaphore。release会使计数器加1，并唤醒等待队列中的一个线程。&lt;/p&gt;

&lt;p&gt;不像锁那样，acquire/release不必配合使用。&lt;/p&gt;

&lt;h2 id=&quot;管道&quot;&gt;管道&lt;/h2&gt;

&lt;h2 id=&quot;双缓冲&quot;&gt;双缓冲&lt;/h2&gt;

&lt;p&gt;用两个缓冲区来实现数据的并发交换。生产者将一个缓冲区填满后，可以被消费者消费。而另一个空的、使用过的缓冲区用力啊填充新数据。即消费者消费一个填满的缓冲区，生产者填充一个缓冲区。&lt;/p&gt;

&lt;p&gt;Java的Exchanger类用来实现消息的交换，它相当于只有两个参与者的CyclicBarrier。只有生产者和消费者都调用Exchaner.exchange(V)后，该方法调用才能返回。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exchange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;参数x相当于缓冲区，生产者的返回值为消费者的参数（即已消费的缓冲区），消费者的返回值为生产者的参数（即已填满的缓冲区）。非常像一手交钱，一手交货。&lt;/p&gt;

&lt;h2 id=&quot;线程中断&quot;&gt;线程中断&lt;/h2&gt;

&lt;p&gt;一个线程请（发起线程）求另一个线程（目标线程）停止其正在执行的操作。中断是发起线程发送给目标线程的一种指示，指示目标线程停止其正在执行的操作。Java为每个线程维护一个中断标记，用于表示响应的线程是否收到中断。目标线程可以通过Thread.currentThread.isInterrupted()来获取该线程的中断标记，也可以通过Thread.interrupted()来获取并重置标记。&lt;/p&gt;

&lt;p&gt;目标现场检查中断标记后所执行的操作，被称为中断响应。目标线程对中断的响应包括：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;无影响。发起线程调用目标线程的interru()方法不会对目标线程产生任何影响。这种操作包括target正在调用InputStream.read()、ReentrantLock.lock()、申请内部锁等阻塞操作&lt;/li&gt;
  &lt;li&gt;取消任务运行，中断发生时的任务被取消，但是不会影响继续运行其它任务&lt;/li&gt;
  &lt;li&gt;工作者线程停止，生命周期变更为TERMINATED&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;interruptedexception&quot;&gt;InterruptedException&lt;/h3&gt;

&lt;p&gt;Java标准库对许多中断的处理方法都是抛出InterruptedException等异常。Java标准库中有些阻塞方法也无法响应中断，如InputStream.read()、Lock.lock()以及内部锁申请。&lt;/p&gt;

&lt;p&gt;能够对中断做出相应的标准库：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Object.wait()/Object.wait(long)/wait(long, int)&lt;/li&gt;
  &lt;li&gt;Thread.sleep(long)/sleep(long, int)&lt;/li&gt;
  &lt;li&gt;Thread.join()/join(long)/join(long, int)&lt;/li&gt;
  &lt;li&gt;BlockingQueue.take()/put(E)&lt;/li&gt;
  &lt;li&gt;Lock.lockInterruptibly()&lt;/li&gt;
  &lt;li&gt;CountDownLatch.await()、CyclicBarrier.await()、Exchanger.exchang(V)&lt;/li&gt;
  &lt;li&gt;channels.InterruptibleChannel。抛出ClosedByInterruptException&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;能够响应中断的方法通常在调用阻塞操作前判断中断标志位，若中断标志位为true，则抛出InterruptedException。如ReentrantLock.lockInterruptibly与ReentrantLock.lock()效果相似，但是前者能够对中断进行相应。凡是抛出InterruptedException的方法，通常会在其抛出异常前，会将当前标志位重置为false。&lt;/p&gt;

&lt;p&gt;如果中断发生的那一刻，目标线程以及调用了阻塞方法而被暂停，那么JVM会设置目标线程会将相应的线程唤醒，并使其抛出InterruptedException。所以Jaba应用层代码可以通过对InterruptedException等异常的处理来响应中断。对InterruptedException的处理包括以下方式：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;不捕获，可以在方法声明处加throw，抛给上层处理&lt;/li&gt;
  &lt;li&gt;捕获后重新抛出&lt;/li&gt;
  &lt;li&gt;捕获后中断当前线程。这种策略实际上在捕获异常后又恢复中断标志位。相当于告诉其它代码，当前保留了中断标志位，但不知如何处理&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;捕获异常又恢复中断标志位：&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;InterruptedException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;interrupt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 保留中断标记&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;线程停止&quot;&gt;线程停止&lt;/h2&gt;</content><author><name></name></author><category term="java" /><summary type="html">wait/notify</summary></entry><entry><title type="html">Java线程安全</title><link href="/2020/01/01/java-thread-safe" rel="alternate" type="text/html" title="Java线程安全" /><published>2020-01-01T00:00:00+00:00</published><updated>2020-01-10T00:00:00+00:00</updated><id>/2020/01/01/java-thread-safe</id><content type="html" xml:base="/2020/01/01/java-thread-safe">&lt;h2 id=&quot;装饰器-decorator&quot;&gt;装饰器 Decorator&lt;/h2&gt;

&lt;p&gt;装饰器模式可以用来实现线程安全，其基本思想是为非线程安全对象创建一个相应的线程安全的包装类对象。包装对象与相应的非线程安全对象具有相同的接口，包装对象通常会借助锁。&lt;/p&gt;

&lt;p&gt;java.util.Collections.synchronizedX，可以反会线程安全的集合，X 可以为 Map、Set、List。这些对象称为同步集合。同步对象的 Iterator 不是线程安全的，为了保障对同步集合遍历操作的线程安全性，&lt;/p&gt;

&lt;h2 id=&quot;并发集合&quot;&gt;并发集合&lt;/h2&gt;

&lt;p&gt;juc 包引入了线程安全的集合对象，通常情况下可以作为同步集合的替代品。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;非线程安全对象&lt;/th&gt;
      &lt;th&gt;线程安全对象&lt;/th&gt;
      &lt;th&gt;共同接口&lt;/th&gt;
      &lt;th&gt;遍历方式&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;ArrayList&lt;/td&gt;
      &lt;td&gt;CopyOnWriteArrayList&lt;/td&gt;
      &lt;td&gt;List&lt;/td&gt;
      &lt;td&gt;快照&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;HashSet&lt;/td&gt;
      &lt;td&gt;CopyOnWriteArraySet&lt;/td&gt;
      &lt;td&gt;Set&lt;/td&gt;
      &lt;td&gt;快照&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;LinkedList&lt;/td&gt;
      &lt;td&gt;ConcurrentLinkedQueue&lt;/td&gt;
      &lt;td&gt;Queue&lt;/td&gt;
      &lt;td&gt;准实时&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;HashMap&lt;/td&gt;
      &lt;td&gt;ConcurrentHashMap&lt;/td&gt;
      &lt;td&gt;Map&lt;/td&gt;
      &lt;td&gt;准实时&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;TreeMap&lt;/td&gt;
      &lt;td&gt;ConcurrentSkipListMap&lt;/td&gt;
      &lt;td&gt;StoredMap&lt;/td&gt;
      &lt;td&gt;准实时&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;TreeSet&lt;/td&gt;
      &lt;td&gt;ConcurrentSkipListSet&lt;/td&gt;
      &lt;td&gt;SortedSet&lt;/td&gt;
      &lt;td&gt;准实时&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;并发集合自身就能对其进行线程安全遍历，无需加锁。并且，对并发集合的遍历操作和对其进行的更新操作是可以由不同的线程并发执行的。并发集合在内部保障线程安全时通常不借助锁，而是用CAS操作，或对锁进行优化（粒度极小的锁）。并发集合的吞吐率要高于同步集合，同步集合会导致锁争用，从而导致上下文切换开销大。&lt;/p&gt;

&lt;p&gt;实现线程安全的遍历通常有两种方式：快照和准实时。快照是在Iterator示例被创建的那一刻，待遍历结构内部生成的只读副本，它反映了集合某一时刻的状态。不同线程对集合进行遍历会得到不同的副本，所以无需加锁就可以实现线程安全。这种遍历返回的iterator是不支持remove的。缺点是集合比较大时，创建副本的开销会比较大。CopyOnWrite就是使用这种方式。&lt;/p&gt;

&lt;p&gt;准实时遍历操作不针对副本，但又不借助锁来保障线程安全，从而使得遍历操作与更新操作并发进行。遍历过程中其它线程对集合进行修改，也能在遍历中反映出来。这种形式返回的Iterator支持remove操作。Concurrent集合采用这种方式。由于Iterator是被设计成一次只能有一个线程使用，所以如果多个线程要进行遍历操作，这些线程间不适宜共享一个Iterator。&lt;/p&gt;

&lt;p&gt;ConcurrentLinkedQueue是Queue接口的线程安全的实现类，相当于LinkedList的线程安全版。其内部不使用锁，而是用CAS，因此是非阻塞的，使用时不会导致线程被暂停。ConcrrentLinkedQueue遍历是准实时的。与BlockingQueue相比，更适合更新操作和遍历操作并发的场景而BlockingQueue更适合多个线程并发更新同一队列的场景，比如消费者生产者模式。&lt;/p&gt;</content><author><name></name></author><category term="java" /><summary type="html">装饰器 Decorator</summary></entry><entry><title type="html">Java同步机制</title><link href="/2019/12/10/java-thread-sync" rel="alternate" type="text/html" title="Java同步机制" /><published>2019-12-10T00:00:00+00:00</published><updated>2019-12-16T00:00:00+00:00</updated><id>/2019/12/10/java-thread-sync</id><content type="html" xml:base="/2019/12/10/java-thread-sync">&lt;p&gt;按照Java虚拟机的实现方式，锁包括synchronize实现的内部锁，和实现Lock接口的显示锁。&lt;/p&gt;

&lt;p&gt;Java锁对线程安全的保障：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;原子性。不管是读还是写，都要获得响应的锁。&lt;/li&gt;
  &lt;li&gt;可见性。 Java平台中，获得锁隐含着刷新处理器缓存的动作，使得读线程在进入临界区之前将对线程共享变量的更新同步到处理器的高速缓存中；而锁的释放隐含这冲刷处理器的动作，使得写线程对共享的更新刷新到处理器的高速缓存中。对于引用变量，还能包装读取到最新的对象字段。数组也能包装读取到最新元素。&lt;/li&gt;
  &lt;li&gt;有序性。锁还能保障有序性，因为读线程没必要知道写线程是以什么顺序更新变量的。临界区只是保证临界区内的操作不会被重排到临界区之外。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可重入锁允许一个线程多次获得一个锁，&lt;/p&gt;

&lt;h2 id=&quot;syncronized&quot;&gt;syncronized&lt;/h2&gt;

&lt;p&gt;java中的任何一个对象都有唯一一个与之关联的锁，称为监视器（monitor）或内部锁（intrinsic）。内部锁是一种排他锁。内部锁通过synchronized关键字实现，可以修饰代码块和方法。被synchronized修饰的方法称为同步方法。synchronized的锁是一个对象，或能够返回对象的表达式。锁可以是this。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 也可以这样写&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一般用final和private修饰锁对象，一旦改变，会使一个同步块使用不同的锁。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;LOCK&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同步静态方法相当于用当前类对象作为锁，类本身也是一个对象。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 以当前类对象作为锁&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 等价&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bar2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;内部锁的获得和释放都是由虚拟机自动完成的，内部锁的使用的不会导致锁泄露，即使代码发生异常。&lt;/p&gt;

&lt;h2 id=&quot;显式锁lock接口&quot;&gt;显式锁：Lock接口&lt;/h2&gt;

&lt;p&gt;Lock的默认实现是ReentrantLock，是可重入锁。ReentrantLock即是公平锁也是非公平锁，可以通过构造函数ReentrantLock(boolean fair)指定。公平锁适合锁持有时间较长，或申请锁平均时间较长的情况，增加了上下文切换作为代价。
相关方法：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Thread.holdsLock(Object)检测当前线程是否持有指定的内部锁&lt;/li&gt;
  &lt;li&gt;ReentrantLock.isLocked检测相应的锁是否被某个线程持有&lt;/li&gt;
  &lt;li&gt;ReentrantLock.getQueueLength&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;读写锁&quot;&gt;读写锁&lt;/h2&gt;

&lt;p&gt;读写锁也被称为共享锁、排他锁，运行多个线程同时读取共享变量，但只允许一个线程对共享变量更新。任何线程读取共享变量的时候，其他线程无法更新改变量。一个线程更新共享变量时，其他线程无法读取、写入该共享变量。读锁对读线程共享，写锁对其他写锁和读锁排他。&lt;/p&gt;

&lt;p&gt;读写锁适合在只读操作比写操作频繁的多、读线程持有锁的时间比较长的场景中使用。ReentrantReadWriteLock支持锁降级，即一个线程持有写锁的情况下可以继续获得相应的读锁。降级的反面是锁升级，即一个线程持有读锁的情况下，申请相应的写锁，ReentrantReadWriteLock不支持锁升级。&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReadWriteLock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;readLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;writeLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;内存屏障&quot;&gt;内存屏障&lt;/h2&gt;

&lt;p&gt;JVM借助内存屏障来实现处理器的刷新和冲刷，内存屏障是对处理器的抽象。内存屏障被插入到两个指令之间，作用是禁止编译器、处理器重排序，从而保障有序性。这些指令也会刷新和冲刷处理器，从而保障可见性。&lt;/p&gt;

&lt;p&gt;内存屏障可以划分为一下几个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;按可见性。加载屏障和存储屏障，它们分别会刷新处理器缓存和冲刷处理器缓存。JVM会在monitorexit（释放锁的JVM指令）对应的机器码指令插入 &lt;strong&gt;存储屏障&lt;/strong&gt; ，保证了写线程在释放锁前在临界区对共享变量的更新对其他线程是可见的。相应的，JVM会在monitorenter对应的机器码指令之后的 &lt;strong&gt;临界区开始之前&lt;/strong&gt; 插入加载屏障，使得读线程对应的处理器能够将写线程从对相应共享变量的更新从其他处理器同步到高处理器的高速缓存中。可见性的保障是通过读线程和写线程使用相应的屏障实现的。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;按有序性，可分为获取屏障（acquire）和释放屏障（release）。获取屏障在一个 &lt;strong&gt;读操作之后&lt;/strong&gt; 插入该内存屏障，作用是禁止该读操作与其后的任何读写操作之间进行重排序（如read-modify-write），相当于在进行后续操作之前要先获得共享数据的所有权。释放屏障在一个 &lt;strong&gt;写操作之前&lt;/strong&gt; 插入该内存屏障，作用是禁止该写操作与其前面的任何读写操作之间进行重排序，相当于对相应的共享变量更新结束后释放所有权。JVM会在monitorenter（包含读操作）对应的机器码之后临界区之前插入获取屏障，并在临界区之后monitorexit对应的机器码之前插入释放屏障&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;两种屏障想三明治一样把临界区的代码包括起来，禁止临界区中任何读写操作被重排序到临界区之前，释放屏障经禁止了读写操作被重排序到临界区之后。这使得临界区的操作具有原子性，同时读线程不用知道临界区的共享变量是以何种顺序被更新的。&lt;/p&gt;

&lt;p&gt;临界区之外的代码可能会被JIT编译进临界区内，但由于内存屏障，处理器不会将其重排序到临界区外（也不会将临界区外的指令重排到临界区内）。x86的Lokc指令前缀能够禁止其前或其后的读写指令的重排序。&lt;/p&gt;

&lt;h2 id=&quot;volatile&quot;&gt;volatile&lt;/h2&gt;

&lt;p&gt;volatile修饰的变量意味着每次读取和更新，都必须从高速缓存或内存中读取。因此，volatile不会被编译器分配到寄存器进行存储。&lt;/p&gt;

&lt;p&gt;volatile是轻量级锁，保证可见性和有序性。但仅能保证写操作的原子性，而不表示赋值有原子性（如value = count + 1），而没有排他性。其次，volatile的使用不会引起上下文的切换。&lt;/p&gt;

&lt;p&gt;对volatile变量进行写操作，会在写操作前后加上释放屏障和存储屏障，以防止对volatile写操作被重排序到前面。并且保证volatile之前的任何读写操作都会先于volatile之前被提交，即读线程看到写线程更新volatile变量时，更新volatile变量之前的内存操作对读线程是可见的，保障了有序性。存储屏障会冲刷处理器缓存，使得volatile变量和之前的任何操作对其他处理器是可见的。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;data2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Read&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// do something&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 当read时，data和data2一定初始化了。&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于volatile的读操作，JVM会在该操作前后插入加载屏障和获取屏障。加载屏障可以刷新处理器，使得读线程所在的处理将其他处理器对共享变量（volatile变量和之前的变量）的更新同步到高速缓存中，获取屏障禁止了volatile读操作之后的任何读写操作与volatile读操作进行重排序。因此对volatile读操作之后的任何操作开始之前，写线程对相关共享变量（包括volatile变量）的更新对当前线程已经可见。&lt;/p&gt;

&lt;p&gt;volatile有序性也可从禁止重排序理解：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;写volatile变量不会往前重排序&lt;/li&gt;
  &lt;li&gt;读volatile变量不会向后重排序&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果volatile修饰数组，那么只会对数组引用本身起作用，而无法对数组元素的操作起作用。可以使用如AtomicIntegerArray来实现类似效果。&lt;/p&gt;

&lt;h2 id=&quot;volatile开销&quot;&gt;volatile开销&lt;/h2&gt;

&lt;p&gt;volatile变量读写不会导致上下文切换，因此volatile的开销比锁小。volatile变量的读取开销可能比普通要高，因为每次都要从高速缓存或内存中同步，而无法在寄存器中暂存。&lt;/p&gt;

&lt;p&gt;应用场景：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;volatile变量作为标志位，避免锁开销&lt;/li&gt;
  &lt;li&gt;保障可见性。多个线程共享一个变量。其他线程在没有加锁的情况下也能看到更新&lt;/li&gt;
  &lt;li&gt;在某些情况下可以代替锁，如多个线程共享一组数据的情况下，通常要用锁来保障这组变量操作的原子性。利用volatile变量写操作的原子性，可以将这组变量包装成对象，对这组变量的更新就可以通过创建新的对象，并将引用赋给变量来实现&lt;/li&gt;
  &lt;li&gt;简易的单变量的读写锁&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;单例模式&quot;&gt;单例模式&lt;/h2&gt;

&lt;h3 id=&quot;双重检测&quot;&gt;双重检测&lt;/h3&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 有问题的代码，没有加volatile&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Singleton&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Singleton&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Singleton&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Singleton&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Singleton&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对象的初始化操作如一下伪代码，对象的初始化操作可以被分为如下3步，如果变量中没加volatile，就会发生将操作2重排序到操作3。其它线程会读取到一个没有初始化的对象。volatile能够禁止该变量写操作与之前的任何读写操作进行重排序，保障instance读取时实例已经初始化完毕。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objRef = allocate(Singleton.class) // 1. 分配空间
involkeConstruct(objRef); // 2. 调用构造器
instance = objRef; // 3. 将对象引用写入共享变量
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;cas&quot;&gt;CAS&lt;/h2&gt;
&lt;p&gt;许多多线程的库最终实现都会借助CAS。CAS能够将read-modift-write和check-and-act之类的操作转为原子操作。CAS是原子的if-then-act操作。CAS保障更新的原子性，但不保障可见性。如果其他线程没有修改过原子变量的值，那么多个线程修改一个原子变量时，下手最快的线程更新成功。&lt;/p&gt;

&lt;p&gt;原子变量类基于CAS实现，一共有12个&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;基础类型，AtomicInteger AtomicLong AtomicBoolean&lt;/li&gt;
  &lt;li&gt;数组型，AtomicIntegerArray AtomicLongArray AtomicReferenceArray&lt;/li&gt;
  &lt;li&gt;字段更新器，AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFiledUpdater&lt;/li&gt;
  &lt;li&gt;引用型，AtomicReference AtomicStampedReference AtomicMarkableReference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AtomicBoolean看起来有些多余，但实际上当执行read-modify-write需要加锁操作。&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AtomicBoolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atomicBool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AtomicBoolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;casInit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atomicBoolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;compareAndSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lockInit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;使用atomic变量能够避免锁的开销，又避免了原子性。AtomicBoolean和AtomicReference从理解上是相似的（有条件更新）。在数组变量上即使加上volatile，也无法使对应元素的读写具有可见性和原子性，Java引入了AtomicIntegerArray等来解决。&lt;/p&gt;

&lt;p&gt;ABA问题：对于共享变量V，一个线程在看到它的值为A的时刻，其他线程将新为B，接着在当前线程将其执行CAS的时刻又被其他线程更新为A，那么可否认为V的值没有变化过？这就是ABA问题，即共享变量的值经历了A -&amp;gt; B -&amp;gt; A的变化。&lt;/p&gt;

&lt;p&gt;要解决ABA问题，可以共享变量进行扩展，引入版本号，每更新一次就在版本号增加1，对应的Java类为AtomicStampedReference。&lt;/p&gt;

&lt;p&gt;AtomicIntegerFieldUpdater（Long、Reference）是字段更新器，更加底层，可理解为对CAS的封装，原子变量的其他几个类都可以用它们来实现。&lt;/p&gt;

&lt;h2 id=&quot;对象发布与逃逸&quot;&gt;对象发布与逃逸&lt;/h2&gt;

&lt;p&gt;对象发布指能够被其作用域之外的线程访问到。常见的对象发布有一下几种方法：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;将对象存储到public变量中&lt;/li&gt;
  &lt;li&gt;在非private方法中返回一个对象&lt;/li&gt;
  &lt;li&gt;创建内部类，使当前对象能够被这个内部类访问，如new Runnerble()中使用”外层类名.this”的语法&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;对象初始化安全&quot;&gt;对象初始化安全&lt;/h3&gt;
&lt;h4 id=&quot;static&quot;&gt;static&lt;/h4&gt;
&lt;p&gt;Java中类的初始化技术是延迟加载的，一个类被虚拟机加载后，该类所有静态变量都为默认值，知道有个线程初次访问该类的任意一个静态变量，才使这个类被初始化（静态初始化块static{}被执行），类的所有变量配赋初值。&lt;/p&gt;

&lt;p&gt;static在多线程环境下有特殊含义，能够保障一个线程在没有其它同步机制下，能够读取到读取到一个类静态变量的初始值。但仅能保障初次读取，不能保障其他线程更新的可见性。对于静态引用变量，static保障一个变量读取该变量初值时，这个引用所指的对象也初始化完毕。&lt;/p&gt;

&lt;h4 id=&quot;final&quot;&gt;final&lt;/h4&gt;
&lt;p&gt;由于重排序的作用，一个线程读取一个对象的引用时，该对象可能尚未初始化完毕，线程可能读取到该变量默认值而不是初始值。多线程环境下final关键字有特殊的作用。&lt;/p&gt;

&lt;p&gt;当一个对象发布到其他线程时，该对象所有final字段都是初始化完毕的，而非final字段则没有这种保证。对于 &lt;strong&gt;引用型final字段&lt;/strong&gt; ，该字段所引用的对象读取时已经初始化完毕，即线程读取到该对象的各个字段都已经初始化完毕，读取时都是初始化值。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clas&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在JIT内联优化下，this.bar = new Bar(x, y)可能会被有编译为以下伪指令：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;objRef = allocate(Foo.class); // 1
objRef.str = &quot;xxxx&quot;; // 2
objBar = allocate(Bar.class); // 3
objBar.x = 1; // 4
objBar.y = 2; // 5
objRef.bar = objBar; // 6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;bar采用final修饰，Java会保证构造器中对该变量的初始化，以及锁引用对象的初始化被限定在操作5之前完成。而url没有采用final修饰，所以任然可能被重排序到操作5之后。&lt;/p&gt;

&lt;p&gt;final只能保障有序性，即保障该对象对外可见的时候，final字段必然是初始化完毕的，但不保证对象本身的可见性。&lt;/p&gt;</content><author><name></name></author><category term="java" /><summary type="html">按照Java虚拟机的实现方式，锁包括synchronize实现的内部锁，和实现Lock接口的显示锁。</summary></entry><entry><title type="html">Welcome to Jekyll Paper!</title><link href="/2018/02/11/welcome-to-jekyll" rel="alternate" type="text/html" title="Welcome to Jekyll Paper!" /><published>2018-02-11T00:00:00+00:00</published><updated>2019-10-16T00:00:00+00:00</updated><id>/2018/02/11/welcome-to-jekyll</id><content type="html" xml:base="/2018/02/11/welcome-to-jekyll">&lt;p&gt;Jekyll Paper is a Jekyll theme, it is designed to helping you to create your own blog by the easiest way. Now, you can learn how to use this blog by this post.&lt;/p&gt;

&lt;p&gt;You’ll find this post in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; directory. Go ahead and edit it and reload the page to see your changes. You can edit this post on Github or your local repository copy.&lt;/p&gt;

&lt;p&gt;To add new posts, simply add a file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; directory that follows the convention &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD-name-of-post.md&lt;/code&gt; and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.&lt;/p&gt;

&lt;p&gt;Jekyll also offers powerful support for code snippets:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; prints 'Hello World!' to STDOUT.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/ghosind/Jekyll-Paper/wiki&quot;&gt;Jekyll Paper docs&lt;/a&gt; or &lt;a href=&quot;https://jekyllrb.com/docs/home&quot;&gt;Jekyll docs&lt;/a&gt; for more info on how to get the most out of Jekyll. If you have questions or suggestions, you can create an issue to asking them on &lt;a href=&quot;https://github.com/ghosind/Jekyll-Paper/issues&quot;&gt;Jekyll Paper Issues&lt;/a&gt; or &lt;a href=&quot;https://talk.jekyllrb.com/&quot;&gt;Jekyll Talk&lt;/a&gt;.&lt;/p&gt;</content><author><name></name></author><category term="Jekyll Paper" /><summary type="html">Jekyll Paper is a Jekyll theme, it is designed to helping you to create your own blog by the easiest way. Now, you can learn how to use this blog by this post.</summary></entry></feed>