009.OpenShift管理及監控

一 資源限制

1.1 pod資源限制

pod可以包括資源請求和資源限制:

  • 資源請求

用於調度,並控制pod不能在計算資源少於指定數量的情況下運行。調度程序試圖找到一個具有足夠計算資源的節點來滿足pod請求。

  • 資源限制

用於防止pod耗盡節點的所有計算資源,基於pod的節點配置Linux內核cgroups特性,以執行pod的資源限制。

儘管資源請求和資源限制是pod定義的一部分,但通常建議在dc中設置。OpenShift推薦的實踐規定,不應該單獨創建pod,而應該由dc創建。

1.2 應用配額

OCP可以執行跟蹤和限制兩種資源使用的配額:

對象的數量:Kubernetes資源的數量,如pod、service和route。

計算資源:物理或虛擬硬件資源的數量,如CPU、內存和存儲容量。

通過避免master的Etcd數據庫的無限制增長,對Kubernetes資源的數量設置配額有助於OpenShift master服務器的穩定性。對Kubernetes資源設置配額還可以避免耗盡其他有限的軟件資源,比如服務的IP地址。

同樣,對計算資源的數量施加配額可以避免耗盡OpenShift集群中單個節點的計算能力。還避免了一個應用程序使用所有集群容量,從而影響共享集群的其他應用程序。

OpenShift通過使用ResourceQuota對象或簡單的quota來管理對象使用的配額及計算資源。

ResourceQuota對象指定項目的硬資源使用限制。配額的所有屬性都是可選的,這意味着任何不受配額限制的資源都可以無限制地使用。

注意:一個項目可以包含多個ResourceQuota對象,其效果是累積的,但是對於同一個項目,兩個不同的 ResourceQuota 不會試圖限制相同類型的對象或計算資源。

1.3 ResourceQuota限制資源

下錶显示 ResourceQuota 可以限制的主要對象和計算資源:

對象名 描述
pods 總計的pod數量
replicationcontrollers 總計的rc數量
services 總計的service數量
secrets 總計的secret數量
persistentvolumeclaims 總計的pvc數量
cpu 所有容器的CPU使用總量
memory 所有容器的總內存使用
storage 所有容器的磁盤總使用量

Quota屬性可以跟蹤項目中所有pod的資源請求或資源限制。默認情況下,配額屬性跟蹤資源請求。要跟蹤資源限制,可以在計算資源名稱前面加上限制,例如limit.cpu。

示例一:使用YAML語法定義的ResourceQuota資源,它為對象計數和計算資源指定了配額:

  1 $ cat
  2 apiVersion: v1
  3 kind: ResourceQuota
  4 metadata:
  5   name: dev-quota
  6 spec:
  7   hard:
  8     services: "10"
  9     cpu: "1300m"
 10     memory: "1.5Gi"
 11 $ oc create -f dev-quota.yml

示例二:使用oc create quota命令創建:

  1 $ oc create quota dev-quota \
  2 --hard=services=10 \
  3 --hard=cpu=1300m \
  4 --hard=memory=1.5Gi
  5 $ oc get resourcequota				#列出可用的配額
  6 $ oc describe resourcequota NAME		#查看與配額中定義的任何與限制相關的統計信息
  7 $ oc delete resourcequota compute-quota		#按名稱刪除活動配額

提示:若oc describe resourcequota命令不帶參數,只显示項目中所有resourcequota對象的累積限制集,而不显示哪個對象定義了哪個限制。

當在項目中首次創建配額時,項目將限制創建任何可能超出配額約束的新資源的能力,然後重新計算資源使用情況。在創建配額和使用數據統計更新之後,項目接受新內容的創建。當創建新資源時,配額使用量立即增加。當一個資源被刪除時,在下一次對項目的 quota 統計數據進行全面重新計算時,配額使用將減少。

ResourceQuota 應用於整個項目,但許多 OpenShift 過程,例如 build 和 deployment,在項目中創建 pod,可能會失敗,因為啟動它們將超過項目 quota。

如果對項目的修改超過了對象數量的 quota,則服務器將拒絕操作,並向用戶返回錯誤消息。但如果修改超出了計算資源的quota,則操作不會立即失敗。OpenShift 將重試該操作幾次,使管理員有機會增加配額或執行糾正操作,比如上線新節點,擴容節點資源。

注意:如果設置了計算資源的 quota,OpenShift 拒絕創建不指定該計算資源的資源請求或資源限制的pod。

1.3 應用限制範圍

LimitRange資源,也稱為limit,定義了計算資源請求的默認值、最小值和最大值,以及項目中定義的單個pod或單個容器的限制,pod的資源請求或限制是其容器的總和。

要理解limit rang和resource quota之間的區別,limit rang為單個pod定義了有效範圍和默認值,而resource quota僅為項目中所有pod的和定義了最高值。

通常可同時定義項目的限制和配額。

LimitRange資源還可以為image、is或pvc的存儲容量定義默認值、最小值和最大值。如果添加到項目中的資源不提供計算資源請求,那麼它將接受項目的limit ranges提供的默認值。如果新資源提供的計算資源請求或限制小於項目的limit range指定的最小值,則不創建該資源。同樣,如果新資源提供的計算資源請求或限制高於項目的limit range所指定的最大值,則不會創建該資源。

OpenShift 提供的大多數標準 S2I builder image 和 templabe 都沒有指定。要使用受配額限制的 template 和 builder,項目需要包含一個 limit range 對象,該對象為容器資源請求指定默認值。

如下為描述了一些可以由LimitRange對象指定的計算資源。


類型 資源名稱 描述
Container cpu 每個容器允許的最小和最大CPU。
Container memory 每個容器允許的最小和最大內存
Pod cpu 一個pod中所有容器允許的最小和最大CPU
Pod memory 一個pod中所有容器允許的最小和最大內存
Image storage 可以推送到內部倉庫的圖像的最大大小
PVC storage 一個pvc的容量的最小和最大容量

示例一:limit rang的yaml示例:

  1 $ cat dev-limits.yml
  2 apiVersion: "v1"
  3 kind: "LimitRange"
  4 metadata:
  5   name: "dev-limits"
  6 spec:
  7   limits:
  8     - type: "Pod"
  9       max:
 10         cpu: "2"
 11         memory: "1Gi"
 12       min:
 13         cpu: "200m"
 14         memory: "6Mi"
 15     - type: "Container"
 16       default:
 17         cpu: "1"
 18         memory: "512Mi"
 19 $ oc create -f dev-limits.yml
 20 $ oc describe limitranges NAME		#查看項目中強制執行的限制約束
 21 $ oc get limits				#查看項目中強制執行的限制約束
 22 $ oc delete limitranges name		#按名稱刪除活動的限制範圍

提示:OCP 3.9不支持使用oc create命令參數形式創建limit rang。

在項目中創建limit rang之後,將根據項目中的每個limit rang資源評估所有資源創建請求。如果新資源違反由任何limit rang設置的最小或最大約束,則拒絕該資源。如果新資源沒有聲明配置值,且約束支持默認值,則將默認值作為其使用值應用於新資源。

所有資源更新請求也將根據項目中的每個limit rang資源進行評估,如果更新后的資源違反了任何約束,則拒絕更新。

注意:避免將LimitRange設的過高,或ResourceQuota設的過低。違反LimitRange將阻止pod創建,並清晰保存。違反ResourceQuota將阻止pod被調度,狀態轉為pending。

1.4 多項目quota配額

ClusterResourceQuota資源是在集群級別創建的,創建方式類似持久卷,並指定應用於多個項目的資源約束。

可以通過以下兩種方式指定哪些項目受集群資源配額限制:

  • 使用openshift.io/requester標記,定義項目所有者,該所有者的所有項目均應用quota;
  • 使用selector,匹配該selector的項目將應用quota。

示例1:

  1 $ oc create clusterquota user-qa \
  2 --project-annotation-selector openshift.io/requester=qa \
  3 --hard pods=12 \
  4 --hard secrets=20			#為qa用戶擁有的所有項目創建集群資源配額
  5 $ oc create clusterquota env-qa \
  6 --project-label-selector environment=qa \
  7 --hard pods=10 \
  8 --hard services=5			#為所有具有environment=qa標籤的項目創建集群資源配額
  9 $ oc describe QUOTA NAME		#查看應用於當前項目的集群資源配額
 10 $ oc delete clusterquota NAME		#刪除集群資源配額

提示:不建議使用一個集群資源配額來匹配超過100個項目。這是為了避免較大的locking開銷。當創建或更新項目中的資源時,在搜索所有適用的資源配額時鎖定項目需要較大的資源消耗。

二 限制資源使用

2.1 前置準備

準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

2.2 本練習準備

  1 [student@workstation ~]$ lab monitor-limit setup

2.3 查看當前資源

  1 [student@workstation ~]$ oc login -u admin -p redhat https://master.lab.example.com
  2 [student@workstation ~]$ oc describe node node1.lab.example.com | grep -A 4 Allocated
  3 [student@workstation ~]$ oc describe node node2.lab.example.com | grep -A 4 Allocated

2.4 創建應用

  1 [student@workstation ~]$ oc new-project resources
  2 [student@workstation ~]$ oc new-app --name=hello \
  3 --docker-image=registry.lab.example.com/openshift/hello-openshift
  4 [student@workstation ~]$ oc get pod -o wide
  5 NAME            READY     STATUS    RESTARTS   AGE       IP            NODE
  6 hello-1-znk56   1/1       Running   0          24s       10.128.0.16   node1.lab.example.com

2.5 刪除應用

  1 [student@workstation ~]$ oc delete all -l app=hello

2.6 添加資源限制

作為集群管理員,向項目quota和limit range,以便為項目中的pod提供默認資源請求。

  1 [student@workstation ~]$ cd /home/student/DO280/labs/monitor-limit/
  2 [student@workstation monitor-limit]$ cat limits.yml		#創建limit range
  3 apiVersion: "v1"
  4 kind: "LimitRange"
  5 metadata:
  6   name: "project-limits"
  7 spec:
  8   limits:
  9     - type: "Container"
 10       default:
 11         cpu: "250m
 12 [student@workstation monitor-limit]$ oc create -f limits.yml	#創建limit range
 13 [student@workstation monitor-limit]$ oc describe limitrange	#查看limit range

  1 [student@workstation monitor-limit]$ cat quota.yml		#創建配額
  2 apiVersion: v1
  3 kind: ResourceQuota
  4 metadata:
  5   name: project-quota
  6 spec:
  7   hard:
  8     cpu: "900m"
  9 [student@workstation monitor-limit]$ oc create -f quota.yml
 10 [student@workstation monitor-limit]$ oc describe quota		#確保創建了resource限制

2.7 授權項目

  1 [student@workstation monitor-limit]$ oc adm policy add-role-to-user edit developer

2.8 驗證資源限制

  1 [student@workstation ~]$ oc login -u developer -p redhat https://master.lab.example.com
  2 [student@workstation ~]$ oc project resources			#選擇項目
  3 Already on project "resources" on server "https://master.lab.example.com:443".
  4 [student@workstation ~]$ oc get limits				#查看limit
  5 NAME             AGE
  6 project-limits   14m
  7 [student@workstation ~]$ oc delete limits project-limits	#驗證限制是否有效,但developer用戶不能刪除該限制
  8 Error from server (Forbidden): limitranges "project-limits" is forbidden: User "developer" cannot delete limitranges in the namespace "resources": User "developer" cannot delete limitranges in project "resources"
  9 [student@workstation ~]$ oc get quota
 10 NAME            AGE
 11 project-quota   15m

2.9 創建應用

  1 [student@workstation ~]$ oc new-app --name=hello \
  2 --docker-image=registry.lab.example.com/openshift/hello-openshift
  3 [student@workstation ~]$ oc get pod
  4 NAME            READY     STATUS    RESTARTS   AGE
  5 hello-1-t7tfn   1/1       Running   0          35s

2.10 查看quota

  1 [student@workstation ~]$ oc describe quota
  2 Name:       project-quota
  3 Namespace:  resources
  4 Resource    Used  Hard
  5 --------    ----  ----
  6 cpu         250m  900m

2.11 查看節點可用資源

  1 [student@workstation ~]$ oc login -u admin -p redhat \
  2 https://master.lab.example.com
  3 [student@workstation ~]$ oc get pod -o wide -n resources
  4 [student@workstation ~]$ oc describe node node1.lab.example.com | grep -A 4 Allocated
  5 [student@workstation ~]$ oc describe pod hello-1-t7tfn | grep -A2 Requests

2.12 擴容應用

  1 [student@workstation ~]$ oc scale dc hello --replicas=2		#擴容應用
  2 [student@workstation ~]$ oc get pod				#查看擴容后的pod
  3 [student@workstation ~]$ oc describe quota			#查看擴容后的quota情況
  4 [student@workstation ~]$ oc scale dc hello --replicas=4		#繼續擴容至4個
  5 [student@workstation ~]$ oc get pod				#查看擴容的pod
  6 [student@workstation ~]$ oc describe dc hello | grep Replicas	#查看replaces情況

結論:由於超過了配額規定,會提示控制器無法創建第四個pod。

2.13 添加配額請求

  1 [student@workstation ~]$ oc scale dc hello --replicas=1
  2 [student@workstation ~]$ oc get pod
  3 [student@workstation ~]$ oc set resources dc hello --requests=memory=256Mi	#設置資源請求
  4 [student@workstation ~]$ oc get pod
  5 [student@workstation ~]$ oc describe pod hello-2-4jvpw | grep -A 3 Requests
  6 [student@workstation ~]$ oc describe quota					#查看quota

結論:由上可知從項目的配額角度來看,沒有什麼變化。

2.14 增大配額請求

  1 [student@workstation ~]$ oc set resources dc hello --requests=memory=8Gi	#將內存請求增大到超過node最大值
  2 [student@workstation ~]$ oc get pod						#查看pod
  3 [student@workstation ~]$ oc logs hello-3-deploy					#查看log
  4 [student@workstation ~]$ oc status

結論:由於資源請求超過node最大值,最終显示一個警告,說明由於內存不足,無法將pod調度到任何節點。

三 OCP升級

3.1 升級OPENSHIFT

當OCP的新版本發布時,可以升級現有集群,以應用最新的增強功能和bug修復。這包括從以前的次要版本(如從3.7升級到3.9)升級,以及對次要版本(3.7)應用更新。

提示:OCP 3.9包含了Kubernetes 1.8和1.9的特性和補丁的合併。由於主要版本之間的核心架構變化,OpenShift Enterprise 2環境無法升級為OpenShift容器平台3,必須需要重新安裝。

通常,主版本中不同子版本的node是向前和向後兼容的。但是,運行不匹配的版本的時間不應超過升級整個集群所需的時間。此外,不支持使用quick installer將版本3.7升級到3.9。

3.2 升級方式

有兩種方法可以執行OpenShift容器平台集群升級,一種為in-place升級(可以自動升級或手動升級),也可以使用blue-green部署方法進行升級。

in-place升級:使用此方式,集群升級將在單個運行的集群中的所有主機上執行。首先升級master,然後升級node。在node升級開始之前,Pods被遷移到集群中的其他節點。這有助於減少用戶應用程序的停機時間。

注意:對於使用quick和高級安裝方法安裝的集群,可以使用自動in-place方式升級。

當使用高級安裝方法安裝集群時,您可以通過重用它們的庫存文件執行自動化或手動就地升級。

blue-green部署:blue-green部署是一種旨在減少停機時間同時升級環境的方法。在blue-green部署中,相同的環境與一個活動環境一起運行,而另一個環境則被更新。OpenShift升級方法標記了不可調度節點,並將pod調度到可用節點。升級成功后,節點將恢復到可調度狀態。

3.3 執行自動化集群升級

使用高級安裝方法,可以使用Ansible playbook自動化執行OpenShift集群升級過程。用於升級的劇本位於/usr/share/ansible/openshift-ansible/Playbooks/common/openshift-cluster/updates/中。該目錄包含一組用於升級集群的子目錄,例如v3_9。

注意:將集群升級到 OCP 3.9 前,集群必須已經升級到 3.7。集群升級一次不能跨越一個以上的次要版本,因此,如果集群的版本早於3.6,則必須先漸進地升級,例如從3.5升級到3.6,然後從3.6升級到3.7

要執行升級,可以使用ansible-playbook命令運行升級劇本,如使用v3_9 playbook將運行3.7版本的現有OpenShift集群升級到3.9版本。

自動升級主要執行以下任務:

  • 應用最新的配置更改;
  • 保存Etcd數據;
  • 將api從3.7更新到3.8,然後從3.8更新到3.9;
  • 如果存在,將默認路由器從3.7更新到3.9;
  • 如果存在,則將默認倉庫從3.7更新到3.9;
  • 更新默認is和Templates。

注意:在繼續升級之前,確保已經滿足了所有先決條件,否則可能導致升級失敗。

如果使用容器化的GlusterFS,節點將不會從pod中撤離,因為GlusterFS pod作為daemonset的一部分運行。要正確地升級運行容器化GlusterFS的集群,需要:

1:升級master服務器、Etcd和基礎設施服務(route、內部倉庫、日誌記錄和metric)。

2:升級運行應用程序容器的節點。

3:一次升級一個運行GlusterFS的節點。

注意:在升級之前,使用oc adm diagnostics命令驗證集群的健康狀況。這確認節點處於ready狀態,運行預期的啟動版本,並且沒有診斷錯誤或警告。對於離線安裝,使用–network-pod-image=’REGISTRY URL/ IMAGE參數指定要使用的image。

3.4 準備自動升級

下面的過程展示了如何為自動升級準備環境,在執行升級之前,Red Hat建議檢查配置Inventory文件,以確保對Inventory文件進行了手動更新。如果配置沒有修改,則使用默認值覆蓋更改。

  1. 如果這是從OCP 3.7升級到3.9,手動禁用3.7存儲庫,並在每個master節點和node節點上啟用3.8和3.9存儲庫:
  1 [root@demo ~]# subscription-manager repos \
  2 --disable="rhel-7-server-ose-3.7-rpms" \
  3 --enable="rhel-7-server-ose-3.9-rpms" \
  4 --enable="rhel-7-server-ose-3.8-rpms" \
  5 --enable="rhel-7-server-rpms" \
  6 --enable="rhel-7-server-extras-rpms" \
  7 --enable="rhel-7-server-ansible-2.4-rpms" \
  8 --enable="rhel-7-fast-datapath-rpms"
  9 [root@demo ~]# yum clean all

  1. 確保在每個RHEL 7系統上都有最新版本的atom-openshift-utils包,它還更新openshift-ansible-*包。
  1 [root@demo ~]# yum update atomic-openshift-utils
  1. 在OpenShift容器平台的以前版本中,安裝程序默認將master節點標記為不可調度,但是,從OCP 3.9開始,master節點必須標記為可調度的,這是在升級過程中自動完成的。

如果沒有設置默認的節點選擇器(如下配置),它們將在升級過程中添加。則master節點也將被標記為master節點角色。所有其他節點都將標記為compute node角色。

  1 openshift_node_labels="{'region':'infra', 'node-role.kubernetes.io/compute':'true'}
  1. 如果將openshift_disable_swap=false變量添加到的Ansible目錄中,或者在node上手動配置swap,那麼在運行升級之前禁用swap內存。

3.5 升級master節點和node節點

在滿足了先決條件(如準備工作)之後,則可以按照如下步驟進行升級:

  1. 在清單文件中設置openshift_deployment_type=openshift-enterprise變量。
  2. 如果使用自定義Docker倉庫,則必須顯式地將倉庫的地址指定為openshift_web_console_prefix和template_service_broker_prefix變量。這些值由Ansible在升級過程中使用。
  1 openshift_web_console_prefix=registry.demo.example.com/openshift3/ose-
  2 template_service_broker_prefix=registry.demo.example.com/openshift3/ose-

  1. 如果希望重啟service或重啟node,請在Inventory文件中設置openshift_rolling_restart_mode=system選項。如果未設置該選項,則默認值表明升級過程在master節點上執行service重啟,但不重啟系統。
  2. 可以通過運行一個Ansible Playbook (upgrade.yml)來更新環境中的所有節點,也可以通過使用單獨的Playbook分多個階段進行升級。
  3. 重新啟動所有主機,重啟之後,如果沒有部署任何額外的功能,可以驗證升級。

3.6 分階段升級集群

如果決定分多個階段升級環境,根據Ansible Playbook (upgrade_control_plan .yml)確定的第一個階段,升級以下組件:

  1. master節點;
  2. 運行master節點的節點services;
  3. Docker服務位於master節點和任何獨立Etcd主機上。

第二階段由upgrade_nodes.yml playbook,升級了以下組件。在運行此第二階段之前,必須已經升級了master節點。

  1. node節點的服務;
  2. 運行在獨立節點上的Docker服務。

兩個階段的升級過程允許通過指定自定義變量自定義升級的運行方式。例如,要升級總節點的50%,可以運行以下命令:

  1 [root@demo ~]# ansible-playbook \
  2 /usr/share/ansible/openshift-ansible/playbooks/common/openshift-cluster/upgrades/
  3 v3_9/upgrade_nodes.yml \
  4 -e openshift_upgrade_nodes_serial="50%"

若要在HA region一次升級兩個節點,請運行以下命令:

  1 [root@demo ~]# ansible-playbook \
  2 /usr/share/ansible/openshift-ansible/playbooks/common/openshift-cluster/upgrades/
  3 v3_9/upgrade_nodes.yml \
  4 -e openshift_upgrade_nodes_serial="2"
  5 -e openshift_upgrade_nodes_label="region=HA"

要指定每個更新批處理中允許有多少節點失敗,可使用openshift_upgrade_nodes_max_fail_percent選項。當故障百分比超過定義的值時,Ansible將中止升級。

使用openshift_upgrade_nodes_drain_timeout選項指定中止play前等待的時間。

示例:如下所示一次升級10個節點,以及如果20%以上的節點(兩個節點)失敗,以及終止play執行的等待時間。

  1 [root@demo ~]# ansible-playbook \
  2 /usr/share/ansible/openshift-ansible/playbooks/common/openshift-cluster/upgrades/
  3 v3_9/upgrade_nodes.yml \
  4 -e openshift_upgrade_nodes_serial=10 \
  5 -e openshift_upgrade_nodes_max_fail_percentage=20 \
  6 -e openshift_upgrade_nodes_drain_timeout=600

3.7 使用Ansible Hooks

可以通過hook為特定的操作執行定製的任務。hook允許通過定義在升級過程中特定點之前或之後執行的任務來擴展升級過程的默認行為。例如,可以在升級集群時驗證或更新自定義基礎設施組件。

提示:hook沒有任何錯誤處理機制,因此,hook中的任何錯誤都會中斷升級過程。需要修復hook並重新運行升級過程。

使用Inventory文件的[OSEv3:vars]部分來定義hook。每個hook必須指向一個.yaml文件,該文件定義了可能的任務。該文件是作為include語句的一部分集成的,該語句要求定義一組任務,而不是一個劇本。Red Hat建議使用絕對路徑來避免任何歧義。

以下hook可用於定製升級過程:

1. openshift_master_upgrade_pre_hook:hook在更新每個master節點之前運行。

2. openshift_master_upgrade_hook:hook在每個master節點升級之後、主服務或節點重新啟動之前運行。

3.openshift_master_upgrade_post_hook:hook在每個master節點升級並重啟服務或系統之後運行。

示例:在庫存文件中集成一個鈎子。

  1 [OSEv3:vars]
  2 openshift_master_upgrade_pre_hook=/usr/share/custom/pre_master.yml
  3 openshift_master_upgrade_hook=/usr/share/custom/master.yml
  4 openshift_master_upgrade_post_hook=/usr/share/custom/post_master.yml

如上示例,引入了一個pre_master.yml,包括了以下任務:

  1 ---
  2 - name: note the start of a master upgrade
  3 debug:
  4 msg: "Master upgrade of {{ inventory_hostname }} is about to start"
  5 - name: require an operator agree to start an upgrade pause:
  6 prompt: "Hit enter to start the master upgrade"

3.8 驗證升級

升級完成后,應該執行以下步驟以確保升級成功。

  1 [root@demo ~]# oc get nodes		#驗證node處於ready
  2 [root@demo ~]# oc get -n default dc/docker-registry -o json | grep \"image\"
  3 #驗證倉庫版本
  4 [root@demo ~]# oc get -n default dc/router -o json | grep \"image\
  5 #驗證image版本
  6 [root@demo ~]# oc adm diagnostics	#使用診斷工具

3.9 升級步驟匯總

  1. 確保在每個RHEL 7系統上都有atom-openshift-utils包的最新版本。
  2. 如果使用自定義Docker倉庫,可以選擇將倉庫的地址指定為openshift_web_console_prefix和template_service_broker_prefix變量。
  3. 禁用所有節點上的swap。
  4. 重新啟動所有主機,重啟之後,檢查升級。
  5. 可選地:檢查Inventory文件中的節點選擇器。
  6. 禁用3.7存儲庫,並在每個master主機和node節點主機上啟用3.8和3.9存儲庫。
  7. 通過使用合適的Ansible劇本集,使用單個或多個階段策略進行更新。
  8. 在清單文件中設置openshift_deployment_type=openshift-enterprise變量。

四 使用probes監視應用

4.1 OPENSHIFT探針介紹

OpenShift應用程序可能會因為臨時連接丟失、配置錯誤或應用程序錯誤等問題而異常。開發人員可以使用探針來監視他們的應用程序。探針是一種Kubernetes操作,它定期對正在運行的容器執行診斷。可以使用oc客戶端命令或OpenShift web控制台配置探針。

目前,可以使用兩種類型的探測:

  • Liveness探針

Liveness探針確定在容器中運行的應用程序是否處於健康狀態。如果Liveness探針返回檢測到一個不健康的狀態,OpenShift將殺死pod並試圖重新部署它。開發人員可以通過配置template.spec.container.livenessprobe來設置Liveness探針。

  • Readiness探針

Readiness探針確定容器是否準備好為請求服務,如果Readiness探針返回失敗狀態,OpenShift將從所有服務的端點刪除容器的IP地址。開發人員可以使用Readiness探針向OpenShift發出信號,即使容器正在運行,它也不應該從代理接收任何流量。開發人員可以通過配置template.spec.containers.readinessprobe來設置Readiness探針。

OpenShift為探測提供了許多超時選項,有五個選項控制支持如上兩個探針:

initialDelaySeconds:強制性的。確定容器啟動后,在開始探測之前要等待多長時間。

timeoutSeconds:強制性的確定等待探測完成所需的時間。如果超過這個時間,OpenShift容器平台會認為探測失敗。

periodSeconds:可選的,指定檢查的頻率。

successThreshold:可選的,指定探測失敗后被認為成功的最小連續成功數。

failureThreshold:可選的,指定探測器成功后被認為失敗的最小連續故障。

4.2 檢查應用程序健康

Readiness和liveness probes可以通過三種方式檢查應用程序的健康狀況:

HTTP檢查:當使用HTTP檢查時,OpenShift使用一個webhook來確定容器的健康狀況。如果HTTP響應代碼在200到399之間,則認為檢查成功。

示例:演示如何使用HTTP檢查方法實現readiness probe 。

  1 ...
  2 readinessProbe:
  3   httpGet:
  4     path: /health			#檢測的URL
  5     port: 8080				#端口
  6   initialDelaySeconds: 15		#在容器啟動后多久才能檢查其健康狀況
  7   timeoutSeconds: 1			#要等多久探測器才能完成
  8 ...

4.3 容器執行檢查

當使用容器執行檢查時,kubelet agent在容器內執行命令。退出狀態為0的檢查被認為是成功的。

示例:實現容器檢查。

  1 ...
  2 livenessProbe:
  3   exec:
  4     command:
  5     - cat
  6     - /tmp/health
  7   initialDelaySeconds: 15
  8   timeoutSeconds: 1
  9 ...

4.4 TCP Socket檢查

當使用TCP Socket檢查時,kubelet agent嘗試打開容器的socket。如果檢查能夠建立連接,則認為容器是健康的。

示例:使用TCP套接字檢查方法實現活動探測。

  1 ...
  2 livenessProbe:
  3   tcpSocket:
  4     port: 8080
  5   initialDelaySeconds: 15
  6   timeoutSeconds: 1
  7 ...

4.5 使用Web管理probes

開發人員可以使用OpenShift web控制台管理readiness和liveness探針。對於每個部署,探針管理都可以從Actions下拉列表中獲得。

對於每種探針類型,開發人員可以選擇該類型,例如HTTP GET、TCP套接字或命令,併為每種類型指定參數。web控制台還提供了刪除探針的選項。

web控制台還可以用於編輯定義部署配置的YAML文件。在創建探針之後,將一個新條目添加到DC的配置文件中。使用DC編輯器來檢查或編輯探針。實時編輯器允許編輯周期秒、成功閾值和失敗閾值選項。

五 使用探針監視應用程序實驗

5.1 前置準備

準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

5.2 本練習準備

  1 [student@workstation ~]$ lab probes setup

5.3 創建應用

  1 [student@workstation ~]$ oc login -u developer -p redhat \
  2 https://master.lab.example.com
  3 [student@workstation ~]$ oc new-project probes
  4 [student@workstation ~]$ oc new-app --name=probes \
  5 http://services.lab.example.com/node-hello
  6 [student@workstation ~]$ oc status

  1 [student@workstation ~]$ oc get pods -w
  2 NAME             READY     STATUS      RESTARTS   AGE
  3 probes-1-build   0/1       Completed   0          1m
  4 probes-1-nqpwh   1/1       Running     0          12s

5.4 暴露服務

  1 [student@workstation ~]$ oc expose svc probes --hostname=probe.apps.lab.example.com
  2 [student@workstation ~]$ curl http://probe.apps.lab.example.com
  3 Hi! I am running on host -> probes-1-nqpwh

5.5 檢查服務

  1 [student@workstation ~]$ curl http://probe.apps.lab.example.com/health
  2 OK
  3 [student@workstation ~]$ curl http://probe.apps.lab.example.com/ready
  4 READY

5.6 創建readiness探針

使用Web控制台登錄。並創建readiness探針。

Add readiness probe

參考5.5存在的用於檢查健康的鏈接添加probe。

5.7 創建Liveness探針

使用Web控制台登錄。並創建Liveness探針。

參考5.5存在的用於檢查健康,特意使用healtz錯誤的值而不是health創建,從而測試相關報錯。這個錯誤將導致OpenShift認為pod不健康,這將觸發pod的重新部署。

提示:由於探針更新了部署配置,因此更改將觸發一個新的部署。

5.8 確認探測

通過單擊側欄上的Monitoring查看探測的實現。觀察事件面板的實時更新。此時將標記為不健康的條目,這表明liveness探針無法訪問/healtz資源。

view details查看詳情。

[student@workstation ~]$ oc get events –sort-by=’.metadata.creationTimestamp’ | grep ‘probe failed’ #查看probe失敗事件

5.9 修正probe

修正healtz為health。

5.10 再次確認

  1 [student@workstation ~]$ oc get events \
  2 --sort-by='.metadata.creationTimestamp'

#從終端重新運行oc get events命令,此時OpenShift在重新部署DC新版本,以及殺死舊pod。同時將不會有任何關於pod不健康的信息。

六 Web控制台使用

6.1 WEB控制台簡介

OpenShift web控制台是一個可以從web瀏覽器訪問的用戶界面。它是管理和監視應用程序的一種方便的方法。儘管命令行界面可以用於管理應用程序的生命周期,但是web控制台提供了額外的優勢,比如部署、pod、服務和其他資源的狀態,以及

關於系統範圍內事件的信息。

可使用Web查看基礎設施內的重要信息,包括:

  • pod各種狀態;
  • volume的可用性;
  • 通過使用probes獲得應用程序的健康行;

登錄並選擇項目之後,web控制台將提供項目項目的概述。

  1. 項目允許在授權訪問的項目之間切換。
  2. Search Catalog:瀏覽image目錄。
  3. Add to project:向項目添加新的資源和應用程序。可以從文件或現有項目導入資源。
  4. Overview:提供當前項目的高級視圖。它显示service的名稱及其在項目中運行的相關pod。
  5. Applications:提供對部署、pod、服務和路由的訪問。它還提供了對Stateful set的訪問,Kubernetes hat特性為pod提供了一個惟一的標識,用於管理部署的順序。
  6. build:提供對構建和IS的訪問。
  7. Resources:提供對配額管理和各種資源(如角色和端點)的訪問。
  8. Storage:提供對持久卷和存儲請求的訪問。
  9. Monitoring選項卡提供對構建、部署和pod日誌的訪問。它還提供了對項目中各種對象的事件通知的訪問。
  10. Catalog選項卡提供對可用於部署應用程序包的模板的訪問。

6.2 使用HAWKULAR管理指標

Hawkular是一組用於監控環境的開源項目。它由各種組件組成,如Hawkular services、Hawkular Application Performance Management (APM)和Hawkular metrics。Hawkular可以通過Hawkular OpenShift代理在OpenShift集群中收集應用程序指標。通過在OpenShift集群中部署Hawkular,可以訪問各種指標,比如pod使用的內存、cpu數量和網絡使用情況。

在部署了Hawkular代理之後,web控制台可以查看各種pod的圖表了。

6.3 管理Deployments和Pods

·Actions按鈕可用於pod和部署,允許管理各種設置。例如,可以向部署添加存儲或健康檢查(包括準備就緒和活動探測)。該按鈕還允許訪問YAML編輯器,以便通過web控制台實時更新配置。

6.4 管理存儲

web控制台允許訪問存儲管理,可以使用該接口創建卷聲明,以使用向項目公開的卷。注意,該接口不能用於創建持久卷,因為只有管理員才能執行此任務。管理員創建持久性卷之後,可以使用web控制台創建請求。該接口支持使用選擇器和標籤屬性。

定義卷聲明之後,控制台將显示它所使用的持久性卷,這是由管理員定義的。、

七 Web控制台監控指標

7.1 前置準備

準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

同時安裝OpenShift Metrics,參考《008.OpenShift Metric應用》3.1

7.2 本練習準備

  1 [student@workstation ~]$ lab web-console setup

7.3 創建項目

  1 [student@workstation ~]$ oc login -u developer -p redhat \
  2 https://master.lab.example.com
  3 [student@workstation ~]$ oc new-project load
  4 [student@workstation ~]$ oc new-app --name=load http://services.lab.example.com/node-hello

7.4 ·暴露服務

  1 [student@workstation ~]$ oc expose svc load
  2 [student@workstation ~]$ oc get pod
  3 NAME           READY     STATUS    RESTARTS   AGE
  4 load-1-build   1/1       Running   0          48s

7.5 壓力測試

  1 [student@workstation ~]$ sudo yum install httpd-tools
  2 [student@workstation ~]$ ab -n 3000000 -c 20 \
  3 http://load-load.apps.lab.example.com/

7.6 控制台擴容pod

workstation節點上登錄控制填,並擴展應用。

查看概覽頁面,確保有一個pod在運行。單擊部署配置load #1,所显示的第一個圖,它對應於pod使用的內存。並指示pod使用了多少內存,突出显示第二張圖,該圖表示pods使用的cpu數量。突出显示第三個圖,它表示pod的網絡流量。

單擊pod視圖圈旁的向上指向的箭頭,將此應用程序的pod數量增加到兩個。

導航到應用程序→部署以訪問項目的部署

注意右側的Actions按鈕,單擊它並選擇Edit YAML來編輯部署配置。

檢查部署的YAML文件,確保replicas條目的值為2,該值與為該部署運行的pod的數量相匹配。

7.7 查看metric

單擊Metrics選項卡訪問項目的度量,可以看到應用程序的四個圖:使用的內存數量、使用的cpu數量、接收的網絡數據包數量和發送的網絡數據包數量。對於每個圖,有兩個圖,每個圖被分配到一個pod。

7.8 查看web控制監視

在側窗格中,單擊Monitoring以訪問Monitoring頁面。Pods部分下應該有兩個條目,deployment部分下應該有一個條目。

向下滾動以訪問部署,並單擊部署名稱旁邊的箭頭以打開框架。日誌下面應該有三個圖表:一個表示pod使用的內存數量,一個表示pod使用的cpu數量,一個表示pod發送和接收的網絡數據包。

7.9 創建PV

為應用程序創建PVC,此練習環境已經提供了聲明將綁定到的持久卷。

單擊Storage創建持久卷聲明,單擊Create Storage來定義聲明。輸入web-storage作為名稱。選擇Shared Access (RWX)作為訪問模式。輸入1作為大小,並將單元保留為GiB

單擊Create創建持久卷聲明。

7.10 嚮應用程序添加存儲

導航到應用程序——>部署來管理部署,單擊load條目以訪問部署。單擊部署的Actions,然後選擇Add Storage選項。此選項允許將現有的持久卷聲明添加到部署配置的模板中。選擇web-storage作為存儲聲明,輸入/web-storage作為掛載路徑,web-storage作為卷名。

7.11 檢查存儲

從deployment頁面中,單擊由(latest)指示的最新部署。等待兩個副本被標記為活動的。確保卷部分將卷web存儲作為持久卷。從底部的Pods部分中,選擇一個正在運行的Pods。單擊Terminal選項卡打開pod的外殼。

也可在任何一個pod中運行如下命令查看:

八 管理和監控OpenShift

8.1 前置準備

準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

8.2 本練習準備

  1 [student@workstation ~]$ lab review-monitor setup

8.3 創建項目

  1 [student@workstation ~]$ oc login -u developer -p redhat https://master.lab.example.com、
  2 [student@workstation ~]$ oc new-project load-review

8.4 創建limit range

  1 [student@workstation ~]$ oc login -u admin -p redhat
  2 [student@workstation ~]$ oc project load-review
  3 [student@workstation ~]$ cat /home/student/DO280/labs/monitor-review/limits.yml
  4 apiVersion: "v1"
  5 kind: "LimitRange"
  6 metadata:
  7   name: "review-limits"
  8 spec:
  9   limits:
 10     - type: "Container"
 11       max:
 12         memory: "300Mi"
 13       default:
 14         memory: "200Mi"
 15 [student@workstation ~]$ oc create -f /home/student/DO280/labs/monitor-review/limits.yml
 16 [student@workstation ~]$ oc describe limitrange
 17 Name:       review-limits
 18 Namespace:  load-review
 19 Type        Resource  Min  Max    Default Request  Default Limit  Max Limit/Request Ratio
 20 ----        --------  ---  ---    ---------------  -------------  -----------------------
 21 Container   memory    -    300Mi  200Mi            200Mi          -

8.5 創建應用

  1 [student@workstation ~]$ oc login -u developer -p redhat
  2 [student@workstation ~]$ oc new-app --name=load http://services.lab.example.com/node-hello
  3 [student@workstation ~]$ oc get pods
  4 NAME           READY     STATUS      RESTARTS   AGE
  5 load-1-6szhm   1/1       Running     0          6s
  6 load-1-build   0/1       Completed   0          43s
  7 [student@workstation ~]$ oc describe pod load-1-6szhm

8.6 擴大資源請求

  1 [student@workstation ~]$ oc set resources dc load --requests=memory=350M
  2 [student@workstation ~]$ oc get events | grep Warning

結論:請求資源超過limit限制,則會出現如上告警。

  1 [student@workstation ~]$ oc set resources dc load --requests=memory=200Mi

8.7 創建ResourceQuota

  1 [student@workstation ~]$ oc status ; oc get pod

  1 [student@workstation ~]$ oc login -u admin -p redhat
  2 [student@workstation ~]$ cat /home/student/DO280/labs/monitor-review/quotas.yml
  3 apiVersion: "v1"
  4 kind: "LimitRange"
  5 metadata:
  6   name: "review-limits"
  7 spec:
  8   limits:
  9     - type: "Container"
 10       max:
 11         memory: "300Mi"
 12       default:
 13         memory: "200Mi"
 14 [student@workstation ~]$ oc create -f /home/student/DO280/labs/monitor-review/quotas.yml
 15 [student@workstation ~]$ oc describe quota
 16 Name:            review-quotas
 17 Namespace:       load-review
 18 Resource         Used  Hard
 19 --------         ----  ----
 20 requests.memory  200M  600Mi   -

8.8 創建應用

  1 [student@workstation ~]$ oc login -u developer -p redhat
  2 [student@workstation ~]$ oc scale --replicas=4 dc load
  3 [student@workstation ~]$ oc get pods
  4 NAME           READY     STATUS      RESTARTS   AGE
  5 load-1-build   0/1       Completed   0          7m
  6 load-3-577fc   1/1       Running     0          5s
  7 load-3-nnncf   1/1       Running     0          4m
  8 load-3-nps4w   1/1       Running     0          5s
  9 [student@workstation ~]$  oc get events | grep Warning

結論:當前已應用配額規定會阻止創建第四個pod。

  1 [student@workstation ~]$ oc scale --replicas=1 dc load

8.9 暴露服務

  1 [student@workstation ~]$ oc expose svc load --hostname=load-review.apps.lab.example.com

8.10 創建探針

Web控制台創建。

Applications ——> Deployments ——> Actions ——> Edit Health Checks。

9.11 確認驗證

導航到Applications ——> Deployments,選擇應用程序的最新部署。

在Template部分中,找到以下條目:

  1 [student@workstation ~]$ lab review-monitor grade #腳本判斷結果
  2 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

架構設計 | 高併發流量削峰,共享資源加鎖機制

本文源碼:GitHub·點這裏 || GitEE·點這裏

一、高併發簡介

在互聯網的業務架構中,高併發是最難處理的業務之一,常見的使用場景:秒殺,搶購,訂票系統;高併發的流程中需要處理的複雜問題非常多,主要涉及下面幾個方面:

  • 流量管理,逐級承接削峰;
  • 網關控制,路由請求,接口熔斷;
  • 併發控制機制,資源加鎖;
  • 分佈式架構,隔離服務和數據庫;

高併發業務核心還是流量控制,控制流量下沉速度,或者控制承接流量的容器大小,多餘的直接溢出,這是相對複雜的流程。其次就是多線程併發下訪問共享資源,該流程需要加鎖機制,避免數據寫出現錯亂情況。

二、秒殺場景

1、預搶購業務

活動未正式開始,先進行活動預約,先把一部分流量收集和控制起來,在真正秒殺的時間點,很多數據可能都已經預處理好了,可以很大程度上削減系統的壓力。有了一定預約流量還可以提前對庫存系統做好準備,一舉兩得。

場景:活動預約,定金預約,高鐵搶票預購。

2、分批搶購

分批搶購和搶購的場景實現的機制是一致的,只是在流量上緩解了很多壓力,秒殺10W件庫存和秒殺100件庫存系統的抗壓不是一個級別。如果秒殺10W件庫存,系統至少承擔多於10W幾倍的流量衝擊,秒殺100件庫存,體系可能承擔幾百或者上千的流量就結束了。下面流量削峰會詳解這裏的策略機制。

場景:分時段多場次搶購,高鐵票分批放出。

3、實時秒殺

最有難度的場景就是準點實時的秒殺活動,假如10點整準時搶1W件商品,在這個時間點前後會湧入高併發的流量,刷新頁面,或者請求搶購的接口,這樣的場景處理起來是最複雜的。

  • 首先系統要承接住流量的湧入;
  • 頁面的不斷刷新要實時加載;
  • 高併發請求的流量控制加鎖等;
  • 服務隔離和數據庫設計的系統保護;

場景:618準點搶購,雙11準點秒殺,電商促銷秒殺。

三、流量削峰

1、Nginx代理

Nginx是一個高性能的HTTP和反向代理web服務器,經常用在集群服務中做統一代理層和負載均衡策略,也可以作為一層流量控制層,提供兩種限流方式,一是控制速率,二是控制併發連接數。

基於漏桶算法,提供限制請求處理速率能力;限制IP的訪問頻率,流量突然增大時,超出的請求將被拒絕;還可以限制併發連接數。

高併發的秒殺場景下,經過Nginx層的各種限制策略,可以控制流量在一個相對穩定的狀態。

2、CDN節點

CDN靜態文件的代理節點,秒殺場景的服務有這樣一個操作特點,活動倒計時開始之前,大量的用戶會不斷的刷新頁面,這時候靜態頁面可以交給CDN層面代理,分擔數據服務接口的壓力。

CDN層面也可以做一層限流,在頁面內置一層策略,假設有10W用戶點擊搶購,可以只放行1W的流量,其他的直接提示活動結束即可,這也是常用的手段之一。

話外之意:平時參与的搶購活動,可能你的請求根本沒有到達數據接口層面,就極速響應商品已搶完,自行意會吧。

3、網關控制

網關層面處理服務接口路由,一些校驗之外,最主要的是可以集成一些策略進入網關,比如經過上述層層的流量控制之後,請求已經接近核心的數據接口,這時在網關層面內置一些策略控制:如果活動是想激活老用戶,網關層面快速判斷用戶屬性,老用戶會放行請求;如果活動的目的是拉新,則放行更多的新用戶。

經過這些層面的控制,剩下的流量已經不多了,後續才真正開始執行搶購的數據操作。

話外之意:如果有10W人參加搶購活動,真正下沉到底層的搶購流量可能就1W,甚至更少,在分散到集群服務中處理。

4、併發熔斷

在分佈式服務的接口中,還有最精細的一層控制,對於一個接口在單位之間內控制請求處理的數量,這個基於接口的響應時間綜合考慮,響應越快,單位時間內的併發量就越高,這裏邏輯不難理解。

言外之意:流量經過層層控制,數據接口層面分擔的壓力已經不大,這時候就是面對秒殺業務中的加鎖問題了。

四、分佈式加鎖

1、悲觀鎖

機制描述

所有請求的線程必須在獲取鎖之後,才能執行數據庫操作,並且基於序列化的模式,沒有獲取鎖的線程處於等待狀態,並且設定重試機制,在單位時間后再次嘗試獲取鎖,或者直接返回。

過程圖解

Redis基礎命令

SETNX:加鎖的思路是,如果key不存在,將key設置為value如果key已存在,則 SETNX 不做任何動作。並且可以給key設置過期時間,過期后其他線程可以繼續嘗試鎖獲取機制。

藉助Redis的該命令模擬鎖的獲取動作。

代碼實現

這裏基於Redis實現的鎖獲取和釋放機制。

import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import javax.annotation.Resource;
@Component
public class RedisLock {

    @Resource
    private Jedis jedis ;

    /**
     * 獲取鎖
     */
    public boolean getLock (String key,String value,long expire){
        try {
            String result = jedis.set( key, value, "nx", "ex", expire);
            return result != null;
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) jedis.close();
        }
        return false ;
    }

    /**
     * 釋放鎖
     */
    public boolean unLock (String key){
        try {
            Long result = jedis.del(key);
            return result > 0 ;
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) jedis.close();
        }
        return false ;
    }
}

這裏基於Jedis的API實現,這裏提供一份配置文件。

@Configuration
public class RedisConfig {

    @Bean
    public JedisPoolConfig jedisPoolConfig (){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig() ;
        jedisPoolConfig.setMaxIdle(8);
        jedisPoolConfig.setMaxTotal(20);
        return jedisPoolConfig ;
    }

    @Bean
    public JedisPool jedisPool (@Autowired JedisPoolConfig jedisPoolConfig){
        return new JedisPool(jedisPoolConfig,"127.0.0.1",6379) ;
    }

    @Bean
    public Jedis jedis (@Autowired JedisPool jedisPool){
        return jedisPool.getResource() ;
    }
}

問題描述

在實際的系統運行期間可能出現如下情況:線程01獲取鎖之後,進程被掛起,後續該執行的沒有執行,鎖失效后,線程02又獲取鎖,在數據庫更新后,線程01恢復,此時在持有鎖之後的狀態,繼續執行后就會容易導致數據錯亂問題。

這時候就需要引入鎖版本概念的,假設線程01獲取鎖版本1,如果沒有執行,線程02獲取鎖版本2,執行之後,通過鎖版本的比較,線程01的鎖版本過低,數據更新就會失敗。

CREATE TABLE `dl_data_lock` (
	`id` INT (11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
	`inventory` INT (11) DEFAULT '0' COMMENT '庫存量',
	`lock_value` INT (11) NOT NULL DEFAULT '0' COMMENT '鎖版本',
	PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '鎖機製表';

說明:lock_value就是記錄鎖版本,作為控制數據更新的條件。

<update id="updateByLock">
    UPDATE dl_data_lock SET inventory=inventory-1,lock_value=#{lockVersion}
    WHERE id=#{id} AND lock_value &lt;#{lockVersion}
</update>

說明:這裏的更新操作,不但要求線程獲取鎖,還會判斷線程鎖的版本不能低於當前更新記錄中的最新鎖版本。

2、樂觀鎖

機制描述

樂觀鎖大多是基於數據記錄來控制,在更新數據庫的時候,基於前置的查詢條件判斷,如果查詢出來的數據沒有被修改,則更新操作成功,如果前置的查詢結果作為更新的條件不成立,則數據寫失敗。

過程圖解

代碼實現

業務流程,先查詢要更新的記錄,然後把讀取的列,作為更新條件。

@Override
public Boolean updateByInventory(Integer id) {
    DataLockEntity dataLockEntity = dataLockMapper.getById(id);
    if (dataLockEntity != null){
        return dataLockMapper.updateByInventory(id,dataLockEntity.getInventory())>0 ;
    }
    return false ;
}

例如如果要把庫存更新,就把讀取的庫存數據作為更新條件,如果讀取庫存是100,在更新的時候庫存變了,則更新條件自然不能成立。

<update id="updateByInventory">
    UPDATE dl_data_lock SET inventory=inventory-1 WHERE id=#{id} AND inventory=#{inventory}
</update>

五、分佈式服務

1、服務保護

在處理高併發的秒殺場景時,經常出現服務掛掉場景,常見某些APP的營銷頁面,出現活動火爆頁面丟失的提示情況,但是不影響整體應用的運行,這就是服務的隔離和保護機制。

基於分佈式的服務結構可以把高併發的業務服務獨立出來,不會因為秒殺服務掛掉影響整體的服務,導致服務雪崩的場景。

2、數據庫保護

數據庫保護和服務保護是相輔相成的,分佈式服務架構下,服務和數據庫是對應的,理論上秒殺服務對應的就是秒殺數據庫,不會因為秒殺庫掛掉,導致整個數據庫宕機。

六、源代碼地址

GitHub·地址
https://github.com/cicadasmile/data-manage-parent
GitEE·地址
https://gitee.com/cicadasmile/data-manage-parent

推薦閱讀:《架構設計系列》,蘿蔔青菜,各有所需

序號 標題
00 架構設計:單服務.集群.分佈式,基本區別和聯繫
01 架構設計:分佈式業務系統中,全局ID生成策略
02 架構設計:分佈式系統調度,Zookeeper集群化管理
03 架構設計:接口冪等性原則,防重複提交Token管理
04 架構設計:緩存管理模式,監控和內存回收策略
05 架構設計:異步處理流程,多種實現模式詳解

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

深入淺出PyTorch(算子篇)

Tensor

自從張量(Tensor)計算這個概念出現后,神經網絡的算法就可以看作是一系列的張量計算。所謂的張量,它原本是個數學概念,表示各種向量或者數值之間的關係。PyTorch的張量(torch.Tensor)表示的是N維矩陣與一維數組的關係。

torch.Tensor的使用方法和numpy很相似(https://pytorch.org/…tensor-tutorial-py),兩者唯一的區別在於torch.Tensor可以使用GPU來計算,這就比用CPU的numpy要快很多。

張量計算的種類有很多,比如加法、乘法、矩陣相乘、矩陣轉置等,這些計算被稱為算子(Operator),它們是PyTorch的核心組件。

算子的backend一般是C/C++的拓展程序,PyTorch的backend是稱為”ATen”的C/C++庫,ATen是”A Tensor”的縮寫。

Operator

PyTorch所有的Operator都定義在Declarations.cwrap和native_functions.yaml這兩個文件中,前者定義了從Torch那繼承來的legacy operator(aten/src/TH),後者定義的是native operator,是PyTorch的operator。

相比於用C++開發的native code,legacy code是在PyTorch編譯時由gen.py根據Declarations.cwrap的內容動態生成的。因此,如果你想要trace這些code,需要先編譯PyTorch。

legacy code的開發要比native code複雜得多。如果可以的話,建議你盡量避開它們。

MatMul

本文會以矩陣相乘–torch.matmul()為例來分析PyTorch算子的工作流程。

我在深入淺出全連接層(fully connected layer)中有講在GPU層面是如何進行矩陣相乘的。Nvidia、AMD等公司提供了優化好的線性代數計算庫–cuBLAS/rocBLAS/openBLAS,PyTorch只需要調用它們的API即可。

Figure 1是torch.matmul()在ATen中的function flow。可以看到,這個flow可不短,這主要是因為不同類型的tensor(2d or Nd, batched gemm or not,with or without bias,cuda or cpu)的操作也不盡相同。

at::matmul()主要負責將Tensor轉換成cuBLAS需要的格式。前面說過,Tensor可以是N維矩陣,如果tensor A是3d矩陣,tensor B是2d矩陣,就需要先將3d轉成2d;如果它們都是>=3d的矩陣,就要考慮batched matmul的情況;如果bias=True,後續就應該交給at::addmm()來處理;總之,matmul要考慮的事情比想象中要多。

除此之外,不同的dtype、device和layout需要調用不同的操作函數,這部分工作交由c10::dispatcher來完成。

Dispatcher

dispatcher主要用於動態調用dtype、device以及layout等方法函數。用過numpy的都知道,np.array()的數據類型有:float32, float16,int8,int32,…. 如果你了解C++就會知道,這類程序最適合用模板(template)來實現。

很遺憾,由於ATen有一部分operator是用C語言寫的(從Torch繼承過來),不支持模板功能,因此,就需要dispatcher這樣的動態調度器。

類似地,PyTorch的tensor不僅可以運行在GPU上,還可以跑在CPU、mkldnn和xla等設備,Figure 1中的dispatcher4就根據tensor的device調用了mm的GPU實現。

layout是指tensor中元素的排布。一般來說,矩陣的排布都是緊湊型的,也就是strided layout。而那些有着大量0的稀疏矩陣,相應地就是sparse layout。

Figure 2是strided layout的演示實例,這裏創建了一個2行2列的矩陣a,它的數據實際存放在一維數組(a.storage)里,2行2列只是這個數組的視圖。

stride充當了從數組到視圖的橋樑,比如,要打印第2行第2列的元素時,可以通過公式:\(1 * stride(0) + 1 * stride(1)\)來計算該元素在數組中的索引。

除了dtype、device、layout之外,dispatcher還可以用來調用legacy operator。比如說addmm這個operator,它的GPU實現就是通過dispatcher來跳轉到legacy::cuda::_th_addmm。

END

到此,就完成了對PyTorch算子的學習。如果你要學習其他算子,可以先從aten/src/ATen/native目錄的相關函數入手,從native_functions.yaml中找到dispatch目標函數,詳情可以參考Figure 1。

更多精彩文章,歡迎掃碼關注下方的公眾號, 並訪問我的簡書博客:https://www.jianshu.com/u/c0fe8671254e

歡迎轉發至朋友圈,工作號轉載請後台留言申請授權~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

MyBatis入門學習-連接oracle實現CURD基本操作

目錄

  • 前言
  • 導入oracle庫
  • 配置
    • 環境配置
    • 配置引用
    • 配置映射
  • 查詢
    • 單條件查詢
    • 多條件查詢
      • 通過類字段傳遞參數
      • 通過Map接口傳參
      • Param註解
  • 插入
  • 更新
  • 刪除
  • 字段映射
  • 參考文獻

前言

本篇記錄使用mybatis連接oracle數據庫實現基本的CURD操作。

導入oracle庫

由於oracle收費, 因此maven沒有oracle庫包,需要我們自己導入,可以手工導入外部包,也可以將oracle的jar導入到maven庫種。具體導入步驟可以查看Maven添加Oracle的依賴及驅動

導入mybatis庫包,我本地使用的是3.5.5版本。最後的配置如下所示


<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.5</version>
</dependency>

<dependency>
  <groupId>com.oracle.jdbc</groupId>
  <artifactId>ojdbc6</artifactId>
  <version>11.2.0.1.0</version>
</dependency>

配置

準備mybatis的配置,在resources目錄下新建一個mybatis-config.xml文件,配置如下

 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 根標籤 -->
<configuration>
    <properties>
        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
        <property name="username" value="fgmain10001" />
        <property name="password" value="test1" />
    </properties>

    <!-- 環境,可以配置多個,default:指定採用哪個環境 -->
    <environments default="test">
        <!-- id:唯一標識 -->
        <environment id="test">
            <!-- 事務管理器,JDBC類型的事務管理器 -->
            <transactionManager type="JDBC" />
            <!-- 數據源,池類型的數據源 -->
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
                <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
                <property name="username" value="fgmain10001" />
                <property name="password" value="test1" />
            </dataSource>
        </environment>
        <environment id="development">
            <!-- 事務管理器,JDBC類型的事務管理器 -->
            <transactionManager type="JDBC" />
            <!-- 數據源,池類型的數據源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="singleTransMapper.xml" />
    </mappers>
</configuration>

環境配置

 <environment id="test">
      <!-- 事務管理器,JDBC類型的事務管理器 -->
      <transactionManager type="JDBC" />
      <!-- 數據源,池類型的數據源 -->
      <dataSource type="POOLED">
          <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
          <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
          <property name="username" value="fgmain10001" />
          <property name="password" value="test1" />
      </dataSource>
  </environment>

其中environments可以配置多個環境的oracle數據源。

id是環境變量的編號,在<environments>default中可以設置當前的環境值。
dataSource中設置數據源。類型有3種。包括:POOLED池化,UNPOOLED非池化和JNDI從其他配置元加載。
driver配置的類名,oracle填寫oracle.jdbc.driver.OracleDriver
url為配置的數據源,使用239測試庫jdbc:oracle:thin:@10.60.45.239:1521:devdb
username是用戶名。
password是密碼。

配置引用

在value中可以填寫如${變量名}的配置引用,通過在properties/propertie添加對應的實際的配置值。

<configuration>
    <properties>
        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
    </properties>
    
    <environments default="test">
        ...
        <environment id="test">
            ...
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
                ...
            </dataSource>
        </environment>
    </environments>
</configuration>

配置映射

配置好數據源后,需要添加對應的表映射,映射包括CRUD對應的SQL語句以及與類之間的映射關係。


<configuration>
  ...
    <mappers>
        <mapper resource="singleTransMapper.xml" />
    </mappers>
</configuration>

在resources目錄下新建一個singleTransMapper.xml文件,MyBatis會將singleTransMapper.xml映射到對應的類

除了resources以外MyBatis還支持classurlpackage共四種配置

class可以配置具體類名,如com.mybatistest.DAO.SingleTransMapper
url可以配置完整的文件路徑,如file:///var/mappers/PostMapper.xml
package可以配置package名稱,註冊所有接口。

查詢


public class SingletransDTO {

    public String EnterpriseNum;

    public String TransNo;

    public String CommandCode;

    public int State;
}

單條件查詢

增加一個查詢單筆的語句,通過輸入流水號,返回查詢到的單筆信息。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
    <select id="selectSingle" parameterType="String" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{transno}
  </select>
</mapper>

namespace需要對應到java中的類,參數和返回類型也需要一致。

在mapper節點下添加select表示select語句
parameterType為輸入的參數
resultType為返回的類型,返回類型需要對應java中的類


public interface SingleTransMapper {
    SingletransDTO selectSingle(String transNo);
}


String resource = "mybatis-config.xml";
//加載資源
InputStream inputStream = Resources.getResourceAsStream(resource);
//創建session
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
  //獲取單筆映射對象
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    //根據流水號查詢
    SingletransDTO blog = mapper.selectSingle("642EHDCS899XKF8P");

    if(blog != null) {
        System.out.println(blog.ENTERPRISENUM);
        System.out.println(blog.TRANSNO);
        System.out.println(blog.COMMANDCODE);
    }else{
        System.out.println("not found");
    }

}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

多條件查詢

通過類字段傳遞參數

添加一個查詢配置selectSingleByParam

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
   
   ...
    <select id="selectSingleByClass" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{TransNo} and commandCode= #{CommandCode}
  </select>
</mapper>

對應的映射類添加方法對應的方法,MyBatis可以通過反射將類的字段映射到SQL的參數,需要注意的是類的字段名和sql中配置的大小寫需要一致。


public interface SingleTransMapper {
  ...
  SingletransDTO selectSingleByClass(SingleCondition singleCondition);
}
public class SingleCondition {
    /**
     * 流水號
     */
    public String TransNo;

    /**
     * 指令類型
     */
    public String CommandCode;

    public SingleCondition(String transNo, String commandCode)
    {
        TransNo = transNo;
        CommandCode = commandCode;
    }
}

調用構造函數類的多條件查詢方案

...
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
SingletransDTO blog = mapper.selectSingleByClass(new SingleCondition( "642EHDCS899XKF8P","10009"));

通過Map接口傳參

另一種方案可以通過傳入HashMap,MayBatis會根據key自動映射到對應的參數。
下面實現通過流水號和指令類型查詢。
添加一個查詢配置selectSingleByMultCondition

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
    ...
    <select id="selectSingleByMultCondition" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{transNo} and commandCode= #{commandCode}
  </select>
</mapper>

添加對應的方法,傳入參數為HashMap<String,Object> param

public interface SingleTransMapper {
  ...
    SingletransDTO selectSingleByMultCondition(HashMap<String,Object> param);
}

修改調用新的多條件查詢方法

...
//獲取單筆映射對象
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
//根據流水號查詢
HashMap<String,Object> param = new HashMap<String,Object>();
param.put("transNo","642EHDCS899XKF8P");
param.put("commandCode","10009");
SingletransDTO blog = mapper.selectSingle2(param);
...

需要注意的是,由於HashMap的key是不區分大小寫的,因此需要和配置文件sql的參數大小寫一致。

Param註解

通過類參數和Map進行多條件查詢都需要創建額外的對象,另一種比較好的方式可以通過在方法參數上添加Param註解的方式配置方法參數和SQL參數的映射關係。

添加一個查詢配置selectSingleByParam

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
   
   ...
    <select id="selectSingleByParam" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{param1} and commandCode= #{param2}
  </select>
</mapper>

對應的映射類添加方法對應的方法,這樣MyBatis就知道參數映射規則,就會自動映射,需要注意的數參數和sql中配置的大小寫也需要一致。


public interface SingleTransMapper {
    SingletransDTO selectSingle(String transNo);
    SingletransDTO selectSingleByMultCondition(HashMap<String,Object> param);
    SingletransDTO selectSingleByParam(@Param("param1")String transNo, @Param("param2") String commandCode);
}

調用註解傳參方法

...
//獲取單筆映射對象
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
SingletransDTO blog = mapper.selectSingleByParam("642EHDCS899XKF8P","10009");
...

插入

在mapper下添加insert表示插入的sql映射。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
  <insert id="insert" parameterType="DTO.SingletransDTO">
    insert into se_singletrans(ENTERPRISENUM,TRANSNO,COMMANDCODE,STATE) values(#{EnterpriseNum},#{TransNo},#{CommandCode},#{State})
  </insert>
</mapper>

添加類對應的insert方法


public interface SingleTransMapper {

    ...
    int insert(SingletransDTO singletransDTO);
}

SqlSession默認會開啟事務,在insert完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    SingletransDTO singletransDTO = new     SingletransDTO();
    singletransDTO.EnterpriseNum  = "QT330001";
    singletransDTO.TransNo = "MYBATIS.INSERT";
    singletransDTO.CommandCode = "10009";
    int count = mapper.insert(singletransDTO);
    session.commit();
    System.out.println("insert result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

我們也可以調用SqlSession openSession(boolean autoCommit)傳入參數,自動提交。

更新

在mapper下添加update節點表示插入,插入時可以對插入的字段設置條件,達成某條件是該字段才需要更新。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
   <update id="update" parameterType="DTO.SingletransDTO">
    update se_singletrans
    <set>
        <if test="State != null and State!=''"></if>
        STATE=#{State}
    </set>
    where transno = #{TransNo} and commandCode= #{CommandCode}
  </update>
</mapper>

添加類對應的update方法


public interface SingleTransMapper {

    ...
    int update(SingletransDTO singletransDTO);
}

SqlSession默認會開啟事務,和insert一樣,在update完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    SingletransDTO singletransDTO = new SingletransDTO();
    singletransDTO.EnterpriseNum = "QT330001";
    singletransDTO.TransNo = "MYBATIS.INSERT";
    singletransDTO.CommandCode = "10009";
    singletransDTO.State = 2;
    int count = mapper.update(singletransDTO);
    session.commit();
    System.out.println("update result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

刪除

在mapper下添加delete節點表示刪除。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
   
  <delete id="delete">
    delete from se_singletrans where transno = #{TransNo} and commandCode= #{CommandCode}
  </delete>
</mapper>

添加類對應的delete方法,可以通過參數註解的方式指定參數。


public interface SingleTransMapper {

    ...
    int delete(@Param("TransNo")String transNo, @Param("CommandCode") String commandCode);
}

SqlSession默認會開啟事務,在delete完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    int count = mapper.delete("MYBATIS.INSERT","10009");
    session.commit();
    System.out.println("delete result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

字段映射

若字段名和數據庫的字段名不一致,可以通過配置進行映射。添加resultMap節點,配置類字段和數據庫字段的映射關係,若沒有配置的字段,則根據默認MyBatis的映射關係處理,即字段名一樣的自動映射,MyBatis會嘗試進行類型轉換,若轉換異常,則可能拋錯。我們也可以通過typeHandler自定義自己的類型處理器。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">

  <resultMap id="singleResultMap" type="DTO.SingletransDTO">
    <result property="TransNo"  column="transNo" />
    <result property="CommandCode" column="commandCode"/>
    <result property="SpecialProperty" typeHandler="CustomTypeHandle" column="SpecialColumn"/>
  </resultMap>
  <select id="selectSingleToReusltMap" resultMap="singleResultMap">
    select * from se_singletrans where transno = #{param1} and commandCode= #{param2}
  </select>
</mapper>

關於TypeHandle這裏不做具體闡述,有興趣的可以看下MyBatis自定義類型處理器 TypeHandler

參考文獻

  1. Maven添加Oracle的依賴及驅動
  2. MyBatis自定義類型處理器 TypeHandler

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

※教你寫出一流的銷售文案?

【asp.net core 系列】12 數據加密算法

0. 前言

這一篇我們將介紹一下.net core 的加密和解密。在Web應用程序中,用戶的密碼會使用MD5值作為密碼數據存儲起來。而在其他的情況下,也會使用加密和解密的功能。

常見的加密算法分為對稱加密和非對稱加密。所謂的對稱加密是指加密密鑰和解密密鑰是同一個,非對稱加密是值加密密鑰和解密迷藥不同。而我們常應用在保存用戶登錄密碼這個過程中的MD5本質上並不是加密算法,而是一種信息摘要算法。不過MD5盡量保證了每個字符串最後計算出來的值都不一樣,所以在密碼保存中常用MD5做為保密值。

1. 常見對稱加密算法

對稱加密算法,簡單的說就是加密和解密使用相同的密鑰進行運算。對於大多數加密算法,解密和加密是一個互逆的運算。對稱加密算法的安全性取決於密鑰的長度,密鑰越長越安全。當然,不建議使用過長的密鑰。

那麼,我們來看看常見的對稱加密算法有哪些吧,以及C#該如何實現。

1.1 DES 和 DESede 算法

DES算法和DESede算法(又稱三重DES算法) 統稱DES系列算法。DES全稱為Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法。而DESede就是針對同一塊數據做三次DES加密。這裏就不對原理做過多的介紹了,來看看.net core里如何實現DES加/解密吧。

在Utils項目里,創建目錄Security

在Security目錄下,創建DESHelper類:

namespace Utils.Security
{
    public class DesHelper
    {
        
    }
}

加密解密實現:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class DesHelper
    {
        static DesHelper()
        {
            DesHandler =  DES.Create("DES");
            DesHandler.Key = Convert.FromBase64String("L1yzjGB2sI4=");
            DesHandler.IV = Convert.FromBase64String("uEcGI4JSAuY=");
        }

        private static DES DesHandler { get; }

        /// <summary>
        /// 加密字符
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Encrypt(string source)
        {
            try
            {
                using (var memStream = new MemoryStream())
                using (var cryptStream = new CryptoStream(memStream, DesHandler.CreateEncryptor(DesHandler.Key, DesHandler.IV),
                    CryptoStreamMode.Write))
                {
                    var bytes = Encoding.UTF8.GetBytes(source);
                    cryptStream.Write(bytes, 0, bytes.Length);
                    cryptStream.FlushFinalBlock();
                    
                    return Convert.ToBase64String(memStream.ToArray());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Decrypt(string source)
        {
            try
            {
                using (var mStream = new MemoryStream(Convert.FromBase64String(source)))
                using (var cryptoStream =
                    new CryptoStream(mStream, DesHandler.CreateDecryptor(DesHandler.Key, DesHandler.IV), CryptoStreamMode.Read))
                using (var reader = new StreamReader(cryptoStream))
                {
                    return reader.ReadToEnd();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }
    }
}

每次調用DesHandler = DES.Create("DES"); 都會重新獲得一個DES算法實現實例,這樣每次獲取的實例中Key、IV這兩個屬性的值也會發生變化。如果直接使用會出現這次加密的數據下次就沒法解密了,為了減少這種情況,所以代碼處手動賦值了Key、IV這兩個屬性。

1.2 AES 加密算法

AES算法(Advanced Encryption Standard)也就是高級數據加密標準算法,是為了解決DES算法中的存在的漏洞而提出的算法標準。現行的AES算法核心是Rijndael算法。當然了,這個不用太過於關心。我們直接看看是如何實現吧:

同樣,在Security目錄創建一個AesHelper類:

namespace Utils.Security
{
    public static class AesHelper
    {
        
    }
}

具體的加解密實現:

using System;
using System.IO;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class AesHelper
    {
        static AesHelper()
        {
            AesHandler = Aes.Create();
            AesHandler.Key = Convert.FromBase64String("lB2BxrJdI4UUjK3KEZyQ0obuSgavB1SYJuAFq9oVw0Y=");
            AesHandler.IV = Convert.FromBase64String("6lra6ceX26Fazwj1R4PCOg==");
        }

        private static Aes AesHandler { get; }

        public static string Encrypt(string source)
        {
            using (var mem = new MemoryStream())
            using (var stream = new CryptoStream(mem, AesHandler.CreateEncryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Write))
            {
                using (var writer = new StreamWriter(stream))
                {
                    writer.Write(source);
                }   
                return Convert.ToBase64String(mem.ToArray());
            }
            
        }

        public static string Decrypt(string source)
        {
            var data = Convert.FromBase64String(source);
            using (var mem = new MemoryStream(data))
            using (var crypto = new CryptoStream(mem, AesHandler.CreateDecryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Read))
            using (var reader = new StreamReader(crypto))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

2. 常見非對稱加密算法

非對稱加密算法,指的是加密密鑰和解密密鑰並不相同。非對稱加密算法的秘鑰通常成對出現,分為公開密鑰和私有密鑰。公開密鑰可以以公開的形式發給數據交互方,而不會產生泄密的風險。因為非對稱加密算法,無法通過公開密鑰推算私有密鑰,反之亦然。

通常,非對稱加密算法是用公鑰進行加密,使用私鑰進行解密。

2.1 RSA算法

RSA算法是標準的非對稱加密算法,名字來源是三位發明者的姓氏首字母。RSA公開密鑰密碼體制是一種使用不同的加密密鑰與解密密鑰,“由已知加密密鑰推導出解密密鑰在計算上是不可行的”密碼體制 。其安全性取決於密鑰的長度,1024位的密鑰幾乎不可能被破解。

同樣,在Utils.Security下創建RSAHelper類:

namespace Utils.Security
{
    public static class RsaHelper
    {
        
    }
}

具體實現:

using System;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class RsaHelper
    {
        public static RSAParameters PublicKey { get; private set; }
        public static RSAParameters PrivateKey { get; private set; }

        static RsaHelper()
        {
            
        }

        public static void InitWindows()
        {
            var parameters = new CspParameters()
            {
                KeyContainerName = "RSAHELPER" // 默認的RSA保存密鑰的容器名稱
            };
            var handle = new RSACryptoServiceProvider(parameters);
            PublicKey = handle.ExportParameters(false);
            PrivateKey = handle.ExportParameters(true);
        }

        public static void ExportKeyPair(string publicKeyXmlString, string privateKeyXmlString)
        {
            var handle  = new RSACryptoServiceProvider();
            handle.FromXmlString(privateKeyXmlString);
            PrivateKey = handle.ExportParameters(true);
            handle.FromXmlString(publicKeyXmlString);
            PublicKey = handle.ExportParameters(false);
        }
        public static byte[] Encrypt(byte[] dataToEncrypt)
        {
            try
            {
                byte[] encryptedData;
                using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
                {
                    RSA.ImportParameters(PublicKey);
                    encryptedData = RSA.Encrypt(dataToEncrypt, true);
                }

                return encryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        public static byte[] Decrypt(byte[] dataToDecrypt)
        {
            try
            {
                byte[] decryptedData;
                using (var rsa = new RSACryptoServiceProvider())
                {
                    rsa.ImportParameters(PrivateKey);
                    decryptedData = rsa.Decrypt(dataToDecrypt, true);
                }
                return decryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());
                return null;
            }
        }
    }
}

因為RSA的特殊性,需要預先設置好公鑰和私鑰。C# 支持多種方式導入密鑰,這裏就不做過多介紹了。

3. 信息摘要算法

這種算法嚴格意義上並不是加密算法,因為它完全不可逆。也就是說,一旦進行使用該類型算法加密后,無法解密還原出數據。當然了,也正是因為這種特性常常被用來做密碼的保存。因為這樣可以避免某些人拿到數據庫與代碼后,可以簡單反推出用戶的密碼。

3.1 MD5算法

最常用的信息摘要算法就是MD5 加密算法,MD5信息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。

原理不解釋,我們看下如何實現,照例現在Security下創建MD5Helper:

namespace Utils.Security
{
    public static class Md5Helper
    {
        
    }
}

具體實現:

using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class Md5Helper
    {
        private static MD5 Hanlder { get; } = new MD5CryptoServiceProvider();

        public static string GetMd5Str(string source)
        {
            var data = Encoding.UTF8.GetBytes(source);
            var security = Hanlder.ComputeHash(data);
            var sb = new StringBuilder();
            foreach (var b in security)
            {
                sb.Append(b.ToString("X2"));
            }

            return sb.ToString();
        }
    }
}

4 總結

這一篇簡單介紹了四種常用的加密算法的實現,當然最常用的就是 MD5,因為這個是大多數系統用來做密碼保存的加密算法。

更多內容煩請關注我的博客《高先生小屋》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

圖解leetcode5-10 | 和233醬一起刷leetcode系列(2)

本周我們繼續來看5道磨人的小妖精,圖解leetcode6-10~

多說一句,leetcode10 殺死了233醬不少腦細胞…

另:

沉迷算法,無法自拔。快來加入我們吧!

別忘了233醬的一條龍服務:

公眾號文章題解 -> 私信答疑 -> 刷題群答疑 -> 視頻講解

我們的目的是成為套路王~

嘿嘿,廣告完畢 , Let’s go!

leetcode6: Z 字形變換

題目描述:

將一個給定字符串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。

題目示例:

輸入: s = "LEETCODEISHIRING", numRows = 4
輸出: "LDREOEIIECIHNTSG"

解釋:

L     D     R
E   O E   I I
E C   I H   N
T     S     G

解題思路:

相信小夥伴看到這道題目,也和233一樣覺得Z字形排列的字符串冥冥中有些規律。為了方便解釋 ,我們假設輸入:

字符串s=”0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15″
numRows=4
注意: s中的輸入字符依次為:為0-15,中間的空格是我為了展示清楚額外加的。

那麼s的Z字形排列如下:

需要輸出的結果是:“0 6 12 15 7 11 13 2 4 8 10 14 3 9 15”

假設我們將Z字形排列后的字符串每一行i 用一個數組arr[i]存起來,最後按行數i的順序輸出arr[i]中的值,那麼就可以得到最終的輸出結果。

如何知道字符串s中的各個字符在哪個arr數組的哪個索引位置呢?這就是我們用数字字符的字符串來舉例子的好處了,因為数字的值就對應着字符在字符串s中的下標。當我們遍歷字符串s時,是我們可以用pointer表示當前遍歷的字符所對應的行數i,代表這個字符是要放到arr[i]中的。

我們可以發現每當遍歷numRows=4 個字符,pointer就從 0->3 轉化為 3->0。所以我們可以用一個flag記錄pointer的變化量。

思路有了,我們來看一下時間空間複雜度:

  • 時間複雜度:遍歷一遍字符串s: O(n)。
  • 空間複雜度:數組arr的存儲:O(n)。

可以寫出代碼嗎:)

Java版本

class Solution {
    public String convert(String s, int numRows) {
        if(numRows <= 1){
            return s;
        }
        List<StringBuilder> arr = new ArrayList<>();
        for(int i = 0 ;i< numRows;i++){
            arr.add(new StringBuilder());
        }
        int flag = -1;
        int pointer = 0;
        for(int i =0;i<s.length();i++){
           char ch = s.charAt(i);
           arr.get(pointer).append(ch);
           if(pointer == 0 || pointer == numRows -1) flag = - flag;
            pointer += flag;
            
        }
        StringBuilder res = new StringBuilder();
        for(StringBuilder row : arr) res.append(row);
        return res.toString();
    }
}

leetcode7: 整數反轉

題目描述:

給出一個 32 位的有符號整數,你需要將這個整數中每位上的数字進行反轉。

題目示例:

輸入: 123
輸出: 321

輸入: -123
輸出: -321

輸入: 120
輸出: 21

注意:
假設我們的環境只能存儲得下 32 位的有符號整數,則其數值範圍為 [−231,  231 − 1]。請根據這個假設,如果反轉后整數溢出那麼就返回 0。

解題思路:
這道題考的還是 數學運算

Step1:需要分別取出十進制数字的個位,十位,百位..一直到最高位的数字。

阿姨來教你小學數學的除法運算:

所以當我們 取余再取模 就可以得到高位的数字。

Step2:將取出來的個位,十位,百位..一直到最高位的数字 依次放到 最高位,…,百位,十位,個位。

阿姨來教你小學數學的乘法運算:

至於示例中列舉的幾個邊界條件,Java中的整數是帶有符號的。剛好符合我們的乘除運算。

另外,需要判斷乘法計算時正負数字的越界問題。當然如果res用long表示,也就不需要考慮這個問題了。代碼如下:

Java版本

class Solution {
    public int reverse(int x) {
        int res = 0;
        while(x!=0){
            if(x>0 && res > ((Integer.MAX_VALUE-x%10)/10)) return 0;
            if(x<0 && res < ((Integer.MIN_VALUE-x%10)/10)) return 0;
            res = res*10 + x%10;
            x/=10;
        }
        return res;
    }
}

leetcode8: 字符串轉換整數(atoi)

題目描述:

請你來實現一個 atoi 函數,使其能將字符串轉換成整數。

首先,該函數會根據需要丟棄無用的開頭空格字符,直到尋找到第一個非空格的字符為止。接下來的轉化規則如下:

如果第一個非空字符為正或者負號時,則將該符號與之後面盡可能多的連續数字字符組合起來,形成一個有符號整數。
假如第一個非空字符是数字,則直接將其與之後連續的数字字符組合起來,形成一個整數。
該字符串在有效的整數部分之後也可能會存在多餘的字符,那麼這些字符可以被忽略,它們對函數不應該造成影響。
注意:假如該字符串中的第一個非空格字符不是一個有效整数字符、字符串為空或字符串僅包含空白字符時,則你的函數不需要進行轉換,即無法進行有效轉換。

在任何情況下,若函數不能進行有效的轉換時,請返回 0 。

提示:

本題中的空白字符只包括空格字符 ‘ ‘ 。
假設我們的環境只能存儲 32 位大小的有符號整數,那麼其數值範圍為 [−231,  231 − 1]。如果數值超過這個範圍,請返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

題目示例:

示例 1:
輸入: "42"
輸出: 42

示例 2:
輸入: "   -42"
輸出: -42
解釋: 第一個非空白字符為 '-', 它是一個負號。
     我們盡可能將負號與後面所有連續出現的数字組合起來,最後得到 -42 。

示例 3:
輸入: "4193 with words"
輸出: 4193
解釋: 轉換截止於数字 '3' ,因為它的下一個字符不為数字。

示例 4:
輸入: "words and 987"
輸出: 0
解釋: 第一個非空字符是 'w', 但它不是数字或正、負號。
     因此無法執行有效的轉換。

示例 5:
輸入: "-91283472332"
輸出: -2147483648
解釋: 数字 "-91283472332" 超過 32 位有符號整數範圍。 
     因此返回 INT_MIN (−231) 。

解題思路:
放這麼多 題目示例 阿姨並不是為了湊字數,而是這類問題就是屬於考邊界情況的問題,邊界情況拎清了,就不會被磨到了~

假設輸入一個字符串 ” -4193 with words” , 我們可以從左到右遍歷這個字符串,用k 表示當前遍歷到的字符:

另外,我們還需要注意 示例5的情況,當乘法計算時的值超過INT_MAX or INT_MIN時,結束並返回 INT_MAX or INT_MIN.

Java版本

class Solution {
    public int myAtoi(String str) {
        int res = 0;
        int k = 0;

        while(k< str.length() &&  ' ' == str.charAt(k))k++;
        int minus = 1;
        if(str.length() == k) return res;
        if('-' == str.charAt(k)) {
            minus = -1;
            k++;
        }else if('+' == str.charAt(k)){
            k++;
        }

        while(k<str.length() && str.charAt(k) >= '0' && str.charAt(k) <='9'){
            int x = str.charAt(k) - '0';
            if(minus >0 && res> (Integer.MAX_VALUE - x)/ 10){
                return Integer.MAX_VALUE;
            }
            //-res * 10 - str.charAt(k) < Integer.MIN_VALUE
            if(minus <0 && -res < (Integer.MIN_VALUE + x)/10) 
                return Integer.MIN_VALUE;
            //最大的負數是存不下來的
            if((-res * 10 - x) == Integer.MIN_VALUE ) {
                return Integer.MIN_VALUE;
            }
            res = res* 10 + x;
            k++;
        }
        res *= minus;
        return res;

    }
}

leetcode9: 迴文數

題目描述:

判斷一個整數是否是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數。

題目示例:

示例 1:

輸入: 121
輸出: true
示例 2:

輸入: -121
輸出: false
解釋: 從左向右讀, 為 -121 。 從右向左讀, 為 121- 。因此它不是一個迴文數。
示例 3:

輸入: 10
輸出: false
解釋: 從右向左讀, 為 01 。因此它不是一個迴文數。

解題思路:

上篇文章中我們講過最長迴文子串的查找。再來看這道題就很easy了。這道題的解法也很多:
比如我們可以把它變為字符串。然後reverse一下,判斷前後兩個字符串是否相等。

但是我們用一種更簡單的方式,只需要反轉整數,然後判斷兩個整數是否相等,就可以確定是不是迴文整數。又回到leetcode7了,有沒有覺得阿姨的乘除法運算還是有幫助的:)

Java版本

class Solution {
    public boolean isPalindrome(int x) {
        
        if(x<0) return false;
        if(x<=9) return true;
        int oringin = x;
        int res = 0;
        while(x>0){
            //如果越界了說明不對稱
            res = res*10 + x%10;
            x/=10;
        }
        return oringin == res;
    }
}

leetcode10: 正則表達式匹配

題目描述:

給你一個字符串 s 和一個字符規律 p,請你來實現一個支持 ‘.’ 和 ‘*’ 的正則表達式匹配。

‘.’ 匹配任意單個字符
‘*’ 匹配零個或多個前面的那一個元素
所謂匹配,是要涵蓋 整個 字符串 s的,而不是部分字符串。

說明:

  • s 可能為空,且只包含從 a-z 的小寫字母。
  • p 可能為空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。

題目示例:

示例 1:
輸入:
s = "aa"
p = "a*"
輸出: true
解釋: 因為 '*' 代表可以匹配零個或多個前面的那一個元素, 在這裏前面的元素就是 'a'。因此,字符串 "aa" 可被視為 'a' 重複了一次。

示例 2:
輸入:
s = "ab"
p = ".*"
輸出: true
解釋: ".*" 表示可匹配零個或多個('*')任意字符('.')。

神奇的.*來了,Hard模式,大家坐好~

判斷 字符串s 是否與 一個 可能還有“.” or “*” 的字符規律 p 匹配,其實就是從 p 代表的所有的字符串中枚舉出一個 匹配值。 簡單暴力枚舉的時間複雜度是指數級的。我們需要考慮對於求解一個最優解 或 匹配解的類似問題,有哪些可以降低時間複雜度的方案?

好了,不饒彎子了,動態規劃 要來了。

溫馨後記:寫着寫着就列舉了一堆動態規劃的理論,比較了解的朋友可以直接翻過這段看後面這一題的圖解。

解題之前,我們先了解下:動態規劃是什麼?為什麼動態規劃能降低時間複雜度?什麼類型的問題又能用動態規劃去解決?如何構造解題步驟?

動態規劃是什麼

動態規劃與分治方法相似,都是通過組合子問題的解來求解原問題。

分治算法將問題劃分為互不相交的子問題,遞歸地求解子問題,再將他們的解組合起來,求出原問題的解。如歸併排序,劃分的左右排序子問題是對不同的数字序列進行排序的,最後再把他們合併起來。

動態規劃應用於子問題重疊的情況,即不同的子問題具有公共的子子問題。這種情況下分治算法需要對子子問題反覆求解,而動態規劃算法只對子子問題求解一次,將其結果保存到備忘錄中 or 按照 自底向下 的順序 求解每個子問題(也就是保證在求解子問題時,它所依賴的子子問題的解已經求出來了)這兩種方式,避免不必要的計算工作,降低時間複雜度。

舉一個簡單的斐波那契數列的例子:

斐波那契數列指的是這樣一個數列:
1、1、2、3、5、8…

相信小夥伴們都知道,它的遞推規律是:

假設求f(10),則遞推公式展開為:

可以看到其中有大量的重複子問題:f(6),f(5) 等。

動態規劃的兩種做法就是:
1.用 遞歸的代碼求解時,將第一次計算的f(6)保存起來,如f(8)中的f(6). 這樣再求解f(7)中的f(6)就可以直接獲取到結果了
2.按照求f(3), ->(4)->…->f(10)的自底向下的順序求解,這樣再求 f(8)時,只需要保存下來 f(7) 和 f(6)的值,就可以求出了,f(10)同理。這種方式大多是循環的寫法。

動態規劃解決的問題類型

初步明白后,我們再來看下動態規劃解決問題的類型:

極客時間的王爭大佬 概括為: 一個模型,三個特徵

一個模型:多階段決策最優解模型
我們一般是用動態規劃來解決最優問題。而解決問題的過程,需要經歷多個決策階段。每個決策階段都對應着一組狀態。然後我們尋找一組決策序列,經過這組決策序列,能夠產生最終期望求解的最優值。
特徵1:最優子結構

指的是,問題的最優解包含子問題的最優解。反過來說就是,我們可以通過子問題的最優解,推導出問題的最優解。如果我們把最優子結構,對應到我們前面定義的動態規劃問題模型上,那我們也可以理解為,後面階段的狀態可以通過前面階段的狀態推導出來。

特徵2:無後效性

無後效性有兩層含義,第一層含義是,在推導後面階段的狀態的時候,我們只關心前面階段的狀態值,不關心這個狀態是怎麼一步一步推導出來的。第二層含義是,某階段狀態一旦確定,就不受之後階段的決策影響。無後效性是一個非常“寬鬆”的要求。只要滿足前面提到的動態規劃問題模型,其實基本上都會滿足無後效性。

特徵3. 重複子問題
這個就是我們前面提到的,不同的決策序列,到達某個相同的階段時,可能會產生重複的狀態。

動態規劃的解題步驟

Step1.刻畫一個最優解的結構特徵
也就是能夠把問題抽象轉化為一種數學描述,通俗說 就是 狀態的定義。如上述斐波那契數列 中 f(n)就是狀態的定義。

Step2.遞歸地定義最優解的值。
就是問題與子問題之間的遞推表達式是什麼,通俗說 就是 狀態轉移方程的定義。如上述斐波那契數列 中的f(n) = f(n-1) + f(n-2)

Step3.計算最優解的值
就是採用的動態規劃具體計算的做法,包括 遞歸+備忘錄 or 循環+自底向下 求解兩種方式。

Step4.利用計算出的信息構造一個最優解
因為我們步驟一定義的狀態有時並不是我們直接要求的最優解,所以這一步就是利用狀態和狀態轉移方式 表達出我們最終要求的最優解怎麼得到。

我們會根據leetcode10來理解這些理論知識。

解題思路:

Step1.抽象出狀態

這個問題實際求的是字符串s能否從字符規律p代表的所有字符串集合中找出一個匹配值。一般求兩個字符串的匹配問題的狀態用二維的數組來定義,為什麼。。聽大佬說:靠經驗,靠悟。我們定義:
dp[i,j] : 代表 所有 字符串s[0,i-1] (前i個字符) 和 字符規律p[0,j-1] (前j個字符)的匹配方案 集合。
dp[i,j] 的值: 代表是否存在一種方案 使得 字符規律p 匹配 字符串s。這個值就是我們這個問題的解。true:存在。false:不存在。

Step2.遞歸地定義最優解的值。

這一步其實就是求狀態遞推式,找出問題dp[i,j] 和子問題之間的關係。

對於字符串s[i] 和 p[j] 是否匹配,因為p[j] 可能是* or . 。我們需要枚舉出p所代表的所有字符串。我們我們可以從最後的字符 s[i] 和 p[j]來考慮。

可分為p[j] == * or p[j] != * 兩種情況。因為 ‘*’ 代表着0-多個字符,會影響p的枚舉數。’.’ 我們只需要把它當成一個萬能字符就好,’.’ 不會影響p的枚舉數量。

  • p[j] != '*' 時,則 s 與 p 是否匹配 取決於 s[i] 是否等於 p[j] && dp[i][j] 是否為true

  • p[j] == '*' 時,我們需要枚舉* 代表的從0-多個字符的字符序列集合中,s 是否與他們其中之一匹配。

如圖所示,考慮p[j] == '*' 所代表的字符數,我們需要列舉出 組成dp[i+1,j+1] 的所有可能情況,同時我們其實靠yy也能推斷出:
dp[i+1,j+1] 和 它的子問題:dp[i,j+1] 的關係,圖中我也有列舉出公式推導來源。

這裡有一點需要注意: dp[i+1,j+1]才表示s[0,i] 和 p[0,j] 匹配。因為s[0]就代表了第一個字符。而我們也需要表示 s長度為0的dp[0,..]的值。不然會影響到我們遞推公式的求值。

好了,到這裏我們先總結下 這個問題動態規劃解法的狀態和狀態轉移方程:

Step3.計算最優解的值。
這個步驟就是具體計算遞推公式dp[i+1,j+1]的過程了,我們可以採用 循環+ 自底向下的方式來求解,也就是對於二維數組先填第0行的值,再填第0列的值,以此類推。
假設s=”aa”, p=”a*” 。則它的二維填狀態表的順序和結果為:

Step4.利用計算出的信息構造一個最優解

在Step1的時候,我們其實就定義了。 s與p是否匹配 等價於 dp[i+1][j+1] 的值 是否為 true。 所以我們只需要返回 dp[i+1][j+1]的值 就是這道題的結果。

徹底完了,看懂了沒,上代碼吧。

Java版本

class Solution {
    public boolean isMatch(String s, String p) {
        int slen = s.length();
        int plen = p.length();
        //需要分別取出s和p為空的情況,所以dp數組大小+1
        boolean[][] dp = new boolean[slen + 1][plen + 1];
        //初始化dp[0][0]=true,dp[0][1]和dp[1][0]~dp[s.length][0]默認值為false所以不需要顯式初始化
        dp[0][0] = true;
        //填寫第一行dp[0][2]~dp[0][p.length]
        for (int k = 2; k <= plen; k++) {
            //p字符串的第2個字符是否等於'*',此時j元素需要0個,所以s不變p減除兩個字符
            dp[0][k] = p.charAt(k - 1) == '*' && dp[0][k - 2];
        }
        //填寫dp數組剩餘部分
        for (int i = 0; i < slen; i++) {
            for (int j = 0; j < plen; j++) {
                //p第j個字符是否為*
                if (p.charAt(j) == '*') {
                    //兩種情況:1.s不變[i+1],p移除兩個元素[j+1-2]。
                    // 2.比較s的i元素和p的j-1(因為此時j元素為*)元素,相等則移除首元素[i+1-1],p不變。
                    dp[i + 1][j + 1] = dp[i + 1][j - 1] ||
                            (dp[i][j + 1] && headMatched(s, p, i, j - 1));
                } else {
                    //s的i元素和p的j元素是否相等,相等則移除s的i元素[i+1-1]和p的j元素[j+1-1]
                    dp[i + 1][j + 1] = dp[i][j] && headMatched(s, p, i, j);
                }
            }
        }
        return dp[slen][plen];
    }

    //判斷s第i個字符和p第j個字符是否匹配
    public boolean headMatched(String s, String p, int i, int j) {
        return s.charAt(i) == p.charAt(j) || p.charAt(j) == '.';
    }

}

能看到這裏看來是真愛了,233醬都要對你豎起大拇指,要不要也在看,轉發 對233醬豎起大拇指 …… ^ _ ^。不管對文章是否有疑問,都歡迎可愛的你加入我們的刷題群,有疑問233醬會在群里答疑哦~

參考資料:
[1].《算法導論》
[2].https://time.geekbang.org/column/article/75702

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

也來聊聊 HTTPS.

前言: 網上聊 HTTPS 的文章已經數都數不過來了吧,厚着臉皮,整理下讀書筆記,結合平常項目的實踐,也來聊聊 HTTPS。

一、為什麼需要 HTTPS?

眾所周知,HTTP 協議具有無連接、不可靠、盡最大努力的特點,這也為 HTPP 協議帶來信息竊聽或身份偽裝等安全問題。主要體現在幾個方面:

  • 通信使用明文(不加密),內容可能會被竊聽。
  • 不驗證通信方的身份,因此有可能遭遇偽裝。
  • 無法證明報文的完整性,所以有可能已遭篡改。

那要如何做到防止竊聽保護信息呢?最為普及的就是加密技術。

  • 通信的加密:用 SSL(Secure Socket Layer,安全套接層)或 TLS(Transport Layer Security,安全層傳輸協議)建立安全通信線路之後,就可以在這條線路上進行 HTTP 通信了。與 SSL/TLS 組合使用的 HTTP 就是 HTTPS,通常 HTTP 直接和 TCP 通信,當使用 SSL 時,則演變成先與 SSL 通信,再由 SSL 和 TCP 通信了,所以 HTTPS 並不是一種新的協議。
  • 內容的加密: 對 HTTP 協議傳輸的內容本身加密,即把 HTTP 報文里所含的內容進行加密處理。

SSL 協議最初是由瀏覽器開發商網景通信公司率先倡導的,開發過 SSL3.0 之前的版本。IETF 以 SSL3.0 為基準,后又制定了 TLS1.0、TLS1.1 和 TLS1.2。TSL 是以 SSL為原型開發的協議,有時會統一稱該協議為 SSL。當前主流的版本是 SSL3.0 和 TLS1.0。

SSL 不僅提供加密處理,而且還使用了一種被稱為證書的手段,可用於確定通信方。

二、HTTPS 怎麼來保障通信安全的?

HTTPS 具有加密、認證以及完整性保護的功能。

1. 加密

客戶端和服務端想要進行安全的通信,首先想到的就是對通信雙方的內容進行加密處理。客戶端利用“密鑰”加密內容,服務端利用“密鑰”解密內容,反之亦然。這種方式稱為對稱(共享密鑰)加密。

對稱加密客戶端和服務端的“密鑰”是一致的,因此,客戶端和服務端之間的“密鑰”傳輸不可避免,如果“密鑰”在傳輸途中被盜用,那麼加密處理就沒有意義了。

那麼如何保護“密鑰”的傳輸安全呢?實踐的思路是非對稱(公開密鑰)加密,服務端擁有 公鑰(public key)+ 私鑰(private key)的密鑰對,公鑰任何人都可以獲取,私鑰只保存在服務端。以下是 SSL 建立安全通信線路的過程。

  1. 服務端將公鑰傳輸給客戶端。
  2. 客戶端通過公鑰加密“密鑰”(客戶端生成)得到一個加密串並傳輸給服務端。
  3. 服務端根據私鑰解密加密串得到“密鑰”。
  4. 雙方通過“密鑰”加密傳輸。

非對稱加密“密鑰”的方式很好的保障了“密鑰”的安全傳輸,因為即使傳輸過程中加密串被盜用了,由於盜用者沒有私鑰信息,也無法得到加密串中的“密鑰”信息。

HTTPS 採用對稱(共享密鑰)加密和非對稱(公開密鑰)加密兩者並用的混合加密機制。之所以要這麼複雜,是因為非對稱加密的處理速度相較於對稱加密要慢,因此,我們一般在交換“密鑰”環節使用非對稱加密,之後的建立通信交換報文階段則使用對稱加密方式。

2. 認證

遺憾的是,非對稱加密傳輸“密鑰”的方式仍然有缺陷,那就是無法證明服務器公鑰本身就是貨真價實的公鑰。比如,接收到某台服務器的公鑰,如何證明公鑰就是原本預想的那台服務器發行的公鑰呢?或許在公鑰傳輸途中,真正的公鑰已經被攻擊者替換掉了。

計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決。

這裏我們引入的中間層就是数字證書認證機構(CA,Certificate Authority),数字證書認證機構處於客戶端與服務器雙方都可信賴的第三方機構的立場上,以下是数字證書認證機構的業務流程。

  1. 服務器的運營人員向数字證書認證機構提出公開密鑰的申請。
  2. 数字證書認證機構在判明提出申請者的身份之後,會對已申請的公開密鑰做数字簽名,然後分配這個已簽名的公開密鑰,並將該公開密鑰放入公鑰證書。
  3. 服務器將公鑰證書下發給客戶端。
  4. 客戶端使用公鑰證書的公開密鑰,對那張證書上的数字簽名進行驗證,一旦驗證通過,客戶端便可明確兩件事:一,認證服務器的公開密鑰的是真實有效的数字證書認證機構頒發的。二,服務器的公開密鑰是值得信賴的。

HTTPS 中還可以使用客戶端證書,以客戶端證書進行客戶端認證,證明服務器正在通信的對方始終是預料之內的客戶端,其作用跟服務器證書如出一轍。

使用 OpenSSL 這套開源程序,每個人都可以構建一套屬於自己的認證機構,從而自己給自己頒發服務器證書,但該服務器證書在互聯網上不可作為證書使用,因為個人並不是可信任的三方機構。

3. 完整性保護

基於 SSL 進行 HTTP 通信時,應用層發送數據會附加一種叫做 MAC(Message Authentication Code)的報文摘要,MAC 能夠查知報文是否遭到篡改,從而保護報文的完整性。

三、HTTPS 的通信過程

CBC 模式(Cipher Block Chaining)又名密碼分組鏈接模式。在此模式下,將前一個明文塊加密處理后和下一個明文塊做 XOR 運算,使之重疊,然後再對運算結果做加密處理。 對第一個明文塊做加密時,要麼使用前一段密文的最後一塊,要麼利用外部生成的初始向量(initial vector, IV)。

四、HTTPS 的缺點?

處理速度上,由於 HTTPS 還需要做服務器、客戶端雙方加密及解密過程,因此會消耗 CPU 和內存等硬件資源。

通信上,和單純 HTTP 通信相比,SSL 通信會消耗部分網絡資源。

綜上所述,相較於 HTTP 通信來說,HTTPS 通信速度會變慢。針對速度變慢這一問題,並沒有根本性的解決方案,我們會使用 SSL 加速器這種(專用服務器)硬件來改善該問題。 該硬件為 SS通信專用硬件,相對軟件來講,能夠提高數倍 SSL 的計算速度。

另外,SSL 證書的費用開銷也是使用 HTTPS 的考慮因素之一(阿里雲/騰訊雲有免費的 SSL 證書可以申請使用)。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

01 . 容器編排簡介及Kubernetes核心概念

Kubernetes簡介

Kubernetes是谷歌嚴格保密十幾年的秘密武器—Borg的一個開源版本,是Docker分佈式系統解決方案.2014年由Google公司啟動.

Kubernetes提供了面嚮應用的容器集群部署和管理系統。Kubernetes的目標旨在消除編排物理/虛擬計算,網絡和存儲基礎設施的負擔,並使應用程序運營商和開發人員完全將重點放在以容器為中心的原語上進行自助運營。Kubernetes 也提供穩定、兼容的基礎(平台),用於構建定製化的workflows 和更高級的自動化任務。 Kubernetes 具備完善的集群管理能力,包括多層次的安全防護和准入機制、多租戶應用支撐能力、透明的服務註冊和服務發現機制、內建負載均衡器、故障發現和自我修復能力、服務滾動升級和在線擴容、可擴展的資源自動調度機制、多粒度的資源配額管理能力。 Kubernetes 還提供完善的管理工具,涵蓋開發、部署測試、運維監控等各個環節。

Kubernetes作為雲原生應用的基石,相當於一個雲操作系統,其重要性不言而喻。

容器編排

容器編排引擎三足鼎立:

    Mesos
    Docker Swarm+compose
    Kubernetes

早在 2015 年 5 月,Kubernetes 在 Google 上的搜索熱度就已經超過了 Mesos 和 Docker Swarm,從那兒之後更是一路飆升,將對手甩開了十幾條街,容器編排引擎領域的三足鼎立時代結束。

目前,AWS、Azure、Google、阿里雲、騰訊雲等主流公有雲提供的是基於 Kubernetes 的容器服務;Rancher、CoreOS、IBM、Mirantis、Oracle、Red Hat、VMWare 等無數廠商也在大力研發和推廣基於 Kubernetes 的容器 CaaS 或 PaaS 產品。可以說,Kubernetes 是當前容器行業最炙手可熱的明星。

Google 的數據中心裏運行着超過 20 億個容器,而且 Google 十年前就開始使用容器技術。

最初,Google 開發了一個叫 Borg 的系統(現在命名為 Omega)來調度如此龐大數量的容器和工作負載。在積累了這麼多年的經驗后,Google 決定重寫這個容器管理系統,並將其貢獻到開源社區,讓全世界都能受益。這個項目就是 Kubernetes。簡單的講,Kubernetes 是 Google Omega 的開源版本。

跟很多基礎設施領域先有工程實踐、後有方法論的發展路線不同,Kubernetes 項目的理論基礎則要比工程實踐走得靠前得多,這當然要歸功於 Google 公司在 2015 年 4 月發布的 Borg 論文了。

Borg 系統,一直以來都被譽為 Google 公司內部最強大的”秘密武器”。雖然略顯誇張,但這個說法倒不算是吹牛。

因為,相比於 Spanner、BigTable 等相對上層的項目,Borg 要承擔的責任,是承載 Google 公司整個基礎設施的核心依賴。在 Google 公司已經公開發表的基礎設施體系論文中,Borg 項目當仁不讓地位居整個基礎設施技術棧的最底層。

由於這樣的定位,Borg 可以說是 Google 最不可能開源的一個項目。而幸運地是,得益於 Docker 項目和容器技術的風靡,它卻終於得以以另一種方式與開源社區見面,這個方式就是 Kubernetes 項目。

所以,相比於”小打小鬧”的 Docker 公司、”舊瓶裝新酒”的 Mesos 社區,Kubernetes 項目從一開始就比較幸運地站上了一個他人難以企及的高度:在它的成長階段,這個項目每一個核心特性的提出,幾乎都脫胎於 Borg/Omega 系統的設計與經驗。更重要的是,這些特性在開源社區落地的過程中,又在整個社區的合力之下得到了極大的改進,修復了很多當年遺留在 Borg 體系中的缺陷和問題。

所以,儘管在發布之初被批評是”曲高和寡”,但是在逐漸覺察到 Docker 技術棧的”稚嫩”和 Mesos 社區的”老邁”之後,這個社區很快就明白了:k8s 項目在 Borg 體系的指導下,體現出了一種獨有的”先進性”與”完備性”,而這些特質才是一個基礎設施領域開源項目賴以生存的核心價值。

什麼是編排

一個正在運行的 Linux 容器,可以分成兩部分看待

1 . 容器的靜態視圖

一組聯合掛載在 /var/lib/docker/aufs/mnt 上的 rootfs,這一部分稱為”容器鏡像”(Container Image)

2 . 容器的動態視圖

一個由 Namespace+Cgroups 構成的隔離環境,這一部分稱為”容器運行時”(Container Runtime)

作為一名開發者,其實並不關心容器運行時的差異。在整個”開發 – 測試 – 發布”的流程中,真正承載着容器信息進行傳遞的,是容器鏡像,而不是容器運行時。

這正是容器技術圈在 Docker 項目成功后不久,就迅速走向了”容器編排”這個”上層建築”的主要原因:作為一家雲服務商或者基礎設施提供商,我只要能夠將用戶提交的 Docker 鏡像以容器的方式運行起來,就能成為這個非常熱鬧的容器生態圖上的一個承載點,從而將整個容器技術棧上的價值,沉澱在我的這個節點上。

更重要的是,只要從這個承載點向 Docker 鏡像製作者和使用者方向回溯,整條路徑上的各個服務節點,比如 CI/CD、監控、安全、網絡、存儲等等,都有可以發揮和盈利的餘地。這個邏輯,正是所有雲計算提供商如此熱衷於容器技術的重要原因:通過容器鏡像,它們可以和潛在用戶(即,開發者)直接關聯起來。

從一個開發者和單一的容器鏡像,到無數開發者和龐大的容器集群,容器技術實現了從”容器”到”容器雲”的飛躍,標志著它真正得到了市場和生態的認可。

這樣,容器就從一個開發者手裡的小工具,一躍成為了雲計算領域的絕對主角;而能夠定義容器組織和管理規範的”容器編排”技術,則當仁不讓地坐上了容器技術領域的”頭把交椅”。

最具代表性的容器編排工具

# 1. Docker 公司的 Compose+Swarm 組合
# 2. Google 與 RedHat 公司共同主導的 Kubernetes 項目

編排工具

Swarm與CoreOS

Docker 公司發布 Swarm 項目

Docker 公司在 2014 年發布 Swarm 項目. 一個有意思的事實:雖然通過”容器”這個概念完成了對經典 PaaS 項目的”降維打擊”,但是 Docker 項目和 Docker 公司,兜兜轉轉了一年多,卻還是回到了 PaaS 項目原本深耕多年的那個戰場:如何讓開發者把應用部署在我的項目上

Docker 項目從發布之初就全面發力,從技術、社區、商業、市場全方位爭取到的開發者群體,實際上是為此後吸引整個生態到自家”PaaS”上的一個鋪墊。只不過這時,”PaaS”的定義已經全然不是 Cloud Foundry 描述的那個樣子,而是變成了一套以 Docker 容器為技術核心,以 Docker 鏡像為打包標準的、全新的”容器化”思路。

這正是 Docker 項目從一開始悉心運作”容器化”理念和經營整個 Docker 生態的主要目的。

Docker 公司在 Docker 項目已經取得巨大成功后,執意要重新走回 PaaS 之路的原因:

雖然 Docker 項目備受追捧,但用戶們最終要部署的,還是他們的網站、服務、數據庫,甚至是雲計算業務。只有那些能夠為用戶提供平台層能力的工具,才會真正成為開發者們關心和願意付費的產品。而 Docker 項目這樣一個只能用來創建和啟停容器的小工具,最終只能充當這些平台項目的”幕後英雄”。

Docker 公司的老朋友和老對手 CoreOS:

CoreOS 是一個基礎設施領域創業公司。 核心產品是一個定製化的操作系統,用戶可以按照分佈式集群的方式,管理所有安裝了這個操作系統的節點。從而,用戶在集群里部署和管理應用就像使用單機一樣方便了。

Docker 項目發布后,CoreOS 公司很快就認識到可以把”容器”的概念無縫集成到自己的這套方案中,從而為用戶提供更高層次的 PaaS 能力。所以,CoreOS 很早就成了 Docker 項目的貢獻者,並在短時間內成為了 Docker 項目中第二重要的力量。

2014 年底,CoreOS 公司與 Docker 公司停止合作,並推出自己研製的 Rocket(後來叫 rkt)容器。

原因是 Docker 公司對 Docker 項目定位的不滿足。Docker 公司的解決方法是讓 Docker 項目提供更多的平台層能力,即向 PaaS 項目進化。這與 CoreOS 公司的核心產品和戰略發生了嚴重衝突。

Docker 公司在 2014 年就已經定好了平台化的發展方向,並且絕對不會跟 CoreOS 在平台層面開展任何合作。這樣看來,Docker 公司在 2014 年 12 月的 DockerCon 上發布 Swarm 的舉動,也就一點都不突然了。

CoreOS 項目

依託於一系列開源項目(比如 Container Linux 操作系統、Fleet 作業調度工具、systemd 進程管理和 rkt 容器),一層層搭建起來的平台產品

Swarm 項目:

以一個完整的整體來對外提供集群管理功能。Swarm 的最大亮點是它完全使用 Docker 項目原本的容器管理 API 來完成集群管理,比如:

單機 Docker 項目
docker run 我的容器

多機 Docker 項目

“docker run -H ” 我的 Swarm 集群 API 地址 ” ” 我的容器 “`

在部署了 Swarm 的多機環境下,用戶只需使用原先的 Docker 指令創建一個容器,這個請求就會被 Swarm 攔截下來處理,然後通過具體的調度算法找到一個合適的 Docker Daemon 運行起來。

這個操作方式簡潔明了,對於已經了解過 Docker 命令行的開發者們也很容易掌握。所以,這樣一個”原生”的 Docker 容器集群管理項目一經發布,就受到了已有 Docker 用戶群的熱捧。相比之下,CoreOS 的解決方案就顯得非常另類,更不用說用戶還要去接受完全讓人摸不着頭腦、新造的容器項目 rkt 了。

Swarm 項目只是 Docker 公司重新定義”PaaS”的關鍵一環。2014 年到 2015 年這段時間里,Docker 項目的迅速走紅催生出了一個非常繁榮的”Docker 生態”。在這個生態里,圍繞着 Docker 在各個層次進行集成和創新的項目層出不窮.

cncf(Fig/Compose)

Fig 項目

被docker收購后改名為 Compose

Fig 項目基本上只是靠兩個人全職開發和維護的,可它卻是當時 GitHub 上熱度堪比 Docker 項目的明星。

Fig 項目受歡迎的原因

是它在開發者面前第一次提出”容器編排”(Container Orchestration)的概念。

“編排”(Orchestration)在雲計算行業里不算是新詞彙,主要是指用戶如何通過某些工具或者配置來完成一組虛擬機以及關聯資源的定義、配置、創建、刪除等工作,然後由雲計算平台按照這些指定的邏輯來完成的過程。

容器時代,”編排”就是對 Docker 容器的一系列定義、配置和創建動作的管理。而 Fig 的工作實際上非常簡單:假如現在用戶需要部署的是應用容器 A、數據庫容器 B、負載均衡容器 C,那麼 Fig 就允許用戶把 A、B、C 三個容器定義在一個配置文件中,並且可以指定它們之間的關聯關係,比如容器 A 需要訪問數據庫容器 B。

接下來,只需執行一條非常簡單的指令:# fig up

Fig 就會把這些容器的定義和配置交給 Docker API 按照訪問邏輯依次創建,一系列容器就都啟動了;而容器 A 與 B 之間的關聯關係,也會交給 Docker 的 Link 功能通過寫入 hosts 文件的方式進行配置。更重要的是,你還可以在 Fig 的配置文件里定義各種容器的副本個數等編排參數,再加上 Swarm 的集群管理能力,一個活脫脫的 PaaS 呼之欲出。

它成了 Docker 公司到目前為止第二大受歡迎的項目,一直到今也依然被很多人使用。

當時的這個容器生態里,還有很多開源項目或公司。比如:

專門負責處理容器網絡的 SocketPlane 項目(後來被 Docker 公司收購)

專門負責處理容器存儲的 Flocker 項目(後來被 EMC 公司收購)

專門給 Docker 集群做圖形化管理界面和對外提供雲服務的 Tutum 項目(後來被 Docker 公司收購)等等。

Mesosphere與Mesos

老牌集群管理項目 Mesos 和它背後的創業公司 Mesosphere:Mesos 社區獨特的競爭力:

超大規模集群的管理經驗

Mesos 早已通過了萬台節點的驗證,2014 年之後又被廣泛使用在 eBay 等大型互聯網公司的生產環境中。

Mesos 是 Berkeley 主導的大數據套件之一,是大數據火熱時最受歡迎的資源管理項目,也是跟 Yarn 項目殺得難捨難分的實力派选手。

大數據所關注的計算密集型離線業務,其實並不像常規的 Web 服務那樣適合用容器進行託管和擴容,也沒有對應用打包的強烈需求,所以 Hadoop、Spark 等項目到現在也沒在容器技術上投下更大的賭注;

但對於 Mesos 來說,天生的兩層調度機制讓它非常容易從大數據領域抽身,轉而去支持受眾更加廣泛的 PaaS 業務。

在這種思路指導下,Mesosphere 公司發布了一個名為 Marathon 的項目,這個項目很快就成為 Docker Swarm 的一個有力競爭對手。

通過 Marathon 實現了諸如應用託管和負載均衡的 PaaS 功能之後,Mesos+Marathon 的組合實際上進化成了一個高度成熟的 PaaS 項目,同時還能很好地支持大數據業務。

Mesosphere 公司提出”DC/OS”(數據中心操作系統)的口號和產品:

旨在使用戶能夠像管理一台機器那樣管理一個萬級別的物理機集群,並且使用 Docker 容器在這個集群里自由地部署應用。這對很多大型企業來說具有着非同尋常的吸引力。

這時的容器技術生態, CoreOS 的 rkt 容器完全打不開局面,Fleet 集群管理項目更是少有人問津,CoreOS 完全被 Docker 公司壓制了。

RedHat 也是因為對 Docker 公司平台化戰略不滿而憤憤退出。但此時,它竟只剩下 OpenShift 這個跟 Cloud Foundry 同時代的經典 PaaS 一張牌可以打,跟 Docker Swarm 和轉型后的 Mesos 完全不在同一個”競技水平”之上。

google與k8s

2014 年 6 月,基礎設施領域的翹楚 Google 公司突然發力,正宣告了一個名叫 Kubernetes 項目的誕生。這個項目,不僅挽救了當時的 CoreOS 和 RedHat,還如同當年 Docker 項目的橫空出世一樣,再一次改變了整個容器市場的格局。

這段時間,也正是 Docker 生態創業公司們的春天,大量圍繞着 Docker 項目的網絡、存儲、監控、CI/CD,甚至 UI 項目紛紛出台,也湧現出了很多 Rancher、Tutum 這樣在開源與商業上均取得了巨大成功的創業公司。

在 2014~2015 年間,整個容器社區可謂熱鬧非凡。

這令人興奮的繁榮背後,卻浮現出了更多的擔憂。這其中最主要的負面情緒,是對 Docker 公司商業化戰略的種種顧慮。

事實上,很多從業者也都看得明白,Docker 項目此時已經成為 Docker 公司一個商業產品。而開源,只是 Docker 公司吸引開發者群體的一個重要手段。不過這麼多年來,開源社區的商業化其實都是類似的思路,無非是高不高調、心不心急的問題罷了。

而真正令大多數人不滿意的是,Docker 公司在 Docker 開源項目的發展上,始終保持着絕對的權威和發言權,並在多個場合用實際行動挑戰到了其他玩家(比如,CoreOS、RedHat,甚至谷歌和微軟)的切身利益。

那麼,這個時候,大家的不滿也就不再是在 GitHub 上發發牢騷這麼簡單了。

相信很多容器領域的老玩家們都聽說過,Docker 項目剛剛興起時,Google 也開源了一個在內部使用多年、經歷過生產環境驗證的 Linux 容器:lmctfy(Let Me Container That For You)。

然而,面對 Docker 項目的強勢崛起,這個對用戶沒那麼友好的 Google 容器項目根本沒有招架之力。所以,知難而退的 Google 公司,向 Docker 公司表示了合作的願望:關停這個項目,和 Docker 公司共同推進一个中立的容器運行時(container runtime)庫作為 Docker 項目的核心依賴。

不過,Docker 公司並沒有認同這個明顯會削弱自己地位的提議,還在不久后,自己發布了一個容器運行時庫 Libcontainer。這次匆忙的、由一家主導的、並帶有戰略性考量的重構,成了 Libcontainer 被社區長期詬病代碼可讀性差、可維護性不強的一個重要原因。

至此,Docker 公司在容器運行時層面上的強硬態度,以及 Docker 項目在高速迭代中表現出來的不穩定和頻繁變更的問題,開始讓社區叫苦不迭。

這種情緒在 2015 年達到了一個高潮,容器領域的其他幾位玩家開始商議”切割”Docker 項目的話語權。而”切割”的手段也非常經典,那就是成立一个中立的基金會。

於是,2015 年 6 月 22 日,由 Docker 公司牽頭,CoreOS、Google、RedHat 等公司共同宣布,Docker 公司將 Libcontainer 捐出,並改名為 RunC 項目,交由一個完全中立的基金會管理,然後以 RunC 為依據,大家共同制定一套容器和鏡像的標準和規範。

這套標準和規範,就是 OCI( Open Container Initiative )。OCI 的提出,意在將容器運行時和鏡像的實現從 Docker 項目中完全剝離出來。這樣做,一方面可以改善 Docker 公司在容器技術上一家獨大的現狀,另一方面也為其他玩家不依賴於 Docker 項目構建各自的平台層能力提供了可能。

不過,OCI 的成立更多的是這些容器玩家出於自身利益進行干涉的一個妥協結果。儘管 Docker 是 OCI 的發起者和創始成員,它卻很少在 OCI 的技術推進和標準制定等事務上扮演關鍵角色,也沒有動力去积極地推進這些所謂的標準。

這也是迄今為止 OCI 組織效率持續低下的根本原因。

OCI 並沒能改變 Docker 公司在容器領域一家獨大的現狀,Google 和 RedHat 等公司於是把第二把武器擺上了檯面。

Docker 之所以不擔心 OCI 的威脅,原因就在於它的 Docker 項目是容器生態的事實標準,而它所維護的 Docker 社區也足夠龐大。可是,一旦這場鬥爭被轉移到容器之上的平台層,或者說 PaaS 層,Docker 公司的競爭優勢便立刻捉襟見肘了。

在這個領域里,像 Google 和 RedHat 這樣的成熟公司,都擁有着深厚的技術積累;而像 CoreOS 這樣的創業公司,也擁有像 Etcd 這樣被廣泛使用的開源基礎設施項目。

可是 Docker 公司卻只有一個 Swarm。

所以這次,Google、RedHat 等開源基礎設施領域玩家們,共同牽頭髮起了一個名為 CNCF(Cloud Native Computing Foundation)的基金會。這個基金會的目的其實很容易理解:它希望,以 Kubernetes 項目為基礎,建立一個由開源基礎設施領域廠商主導的、按照獨立基金會方式運營的平台級社區,來對抗以 Docker 公司為核心的容器商業生態。

為了打造出一個圍繞 Kubernetes 項目的”護城河”,CNCF 社區就需要至少確保兩件事情:

# 1. Kubernetes 項目必須能夠在容器編排領域取得足夠大的競爭優勢
# 2. CNCF 社區必須以 Kubernetes 項目為核心,覆蓋足夠多的場景

CNCF 社區如何解決 Kubernetes 項目在編排領域的競爭力的問題:

在容器編排領域,Kubernetes 項目需要面對來自 Docker 公司和 Mesos 社區兩個方向的壓力。Swarm 和 Mesos 實際上分別從兩個不同的方向講出了自己最擅長的故事:Swarm 擅長的是跟 Docker 生態的無縫集成,而 Mesos 擅長的則是大規模集群的調度與管理。

這兩個方向,也是大多數人做容器集群管理項目時最容易想到的兩個出發點。也正因為如此,Kubernetes 項目如果繼續在這兩個方向上做文章恐怕就不太明智了。

Kubernetes 選擇的應對方式是:Borg

k8s 項目大多來自於 Borg 和 Omega 系統的內部特性,這些特性落到 k8s 項目上,就是 Pod、Sidecar 等功能和設計模式。

這就解釋了,為什麼 Kubernetes 發布后,很多人”抱怨”其設計思想過於”超前”的原因:Kubernetes 項目的基礎特性,並不是幾個工程師突然”拍腦袋”想出來的東西,而是 Google 公司在容器化基礎設施領域多年來實踐經驗的沉澱與升華。這正是 Kubernetes 項目能夠從一開始就避免同 Swarm 和 Mesos 社區同質化的重要手段。

CNCF 接下來的任務是如何把這些先進的思想通過技術手段在開源社區落地,並培育出一個認同這些理念的生態?

RedHat 發揮了重要作用。當時,Kubernetes 團隊規模很小,能夠投入的工程能力十分緊張,這恰恰是 RedHat 的長處。RedHat 更是世界上為數不多、能真正理解開源社區運作和項目研發真諦的合作夥伴。

RedHat 與 Google 聯盟的成立,不僅保證了 RedHat 在 Kubernetes 項目上的影響力,也正式開啟了容器編排領域”三國鼎立”的局面。

Mesos 社區與容器技術的關係,更像是”借勢”,而不是這個領域真正的參与者和領導者。這個事實,加上它所屬的 Apache 社區固有的封閉性,導致了 Mesos 社區雖然技術最為成熟,卻在容器編排領域鮮有創新。

一開始,Docker 公司就把應對 Kubernetes 項目的競爭擺在首要位置:
一方面,不斷強調”Docker Native”的”重要性”
一方面,與 k8s 項目在多個場合進行了直接的碰撞。

這次競爭的發展態勢,很快就超過了 Docker 公司的預期。

Kubernetes 項目並沒有跟 Swarm 項目展開同質化的競爭
所以 “Docker Native”的說辭並沒有太大的殺傷力
相反 k8s 項目讓人耳目一新的設計理念和號召力,很快就構建出了一個與眾不同的容器編排與管理的生態。

Kubernetes 項目在 GitHub 上的各項指標開始一騎絕塵,將 Swarm 項目遠遠地甩在了身後.

CNCF 社區如何解決第二個問題:

在已經囊括了容器監控事實標準的 Prometheus 項目后,CNCF 社區迅速在成員項目中添加了 Fluentd、OpenTracing、CNI 等一系列容器生態的知名工具和項目。

而在看到了 CNCF 社區對用戶表現出來的巨大吸引力之後,大量的公司和創業團隊也開始專門針對 CNCF 社區而非 Docker 公司制定推廣策略。

2016 年,Docker 公司宣布了一個震驚所有人的計劃:放棄現有的 Swarm 項目,將容器編排和集群管理功能全部內置到 Docker 項目當中。

Docker 公司意識到了 Swarm 項目目前唯一的競爭優勢,就是跟 Docker 項目的無縫集成。那麼,如何讓這種優勢最大化呢?那就是把 Swarm 內置到 Docker 項目當中。

從工程角度來看,這種做法的風險很大。內置容器編排、集群管理和負載均衡能力,固然可以使得 Docker 項目的邊界直接擴大到一個完整的 PaaS 項目的範疇,但這種變更帶來的技術複雜度和維護難度,長遠來看對 Docker 項目是不利的。

不過,在當時的大環境下,Docker 公司的選擇恐怕也帶有一絲孤注一擲的意味。

k8s 的應對策略

是反其道而行之,開始在整個社區推進”民主化”架構,即:從 API 到容器運行時的每一層,Kubernetes 項目都為開發者暴露出了可以擴展的插件機制,鼓勵用戶通過代碼的方式介入到 Kubernetes 項目的每一個階段。

Kubernetes 項目的這個變革的效果立竿見影,很快在整個容器社區中催生出了大量的、基於 Kubernetes API 和擴展接口的二次創新工作,比如:
目前熱度極高的微服務治理項目 Istio;
被廣泛採用的有狀態應用部署框架 Operator;
還有像 Rook 這樣的開源創業項目,它通過 Kubernetes 的可擴展接口,把 Ceph 這樣的重量級產品封裝成了簡單易用的容器存儲插件。

在鼓勵二次創新的整體氛圍當中,k8s 社區在 2016 年後得到了空前的發展。更重要的是,不同於之前局限於”打包、發布”這樣的 PaaS 化路線,這一次容器社區的繁榮,是一次完全以 Kubernetes 項目為核心的”百花爭鳴”。

面對 Kubernetes 社區的崛起和壯大,Docker 公司也不得不面對自己豪賭失敗的現實。但在早前拒絕了微軟的天價收購之後,Docker 公司實際上已經沒有什麼迴旋餘地,只能選擇逐步放棄開源社區而專註於自己的商業化轉型。

所以,從 2017 年開始,Docker 公司先是將 Docker 項目的容器運行時部分 Containerd 捐贈給 CNCF 社區,標志著 Docker 項目已經全面升級成為一個 PaaS 平台;緊接着,Docker 公司宣布將 Docker 項目改名為 Moby,然後交給社區自行維護,而 Docker 公司的商業產品將佔有 Docker 這個註冊商標。

Docker 公司這些舉措背後的含義非常明確:它將全面放棄在開源社區同 Kubernetes 生態的競爭,轉而專註於自己的商業業務,並且通過將 Docker 項目改名為 Moby 的舉動,將原本屬於 Docker 社區的用戶轉化成了自己的客戶。

2017 年 10 月,Docker 公司出人意料地宣布,將在自己的主打產品 Docker 企業版中內置 Kubernetes 項目,這標志著持續了近兩年之久的”編排之爭”至此落下帷幕。

2018 年 1 月 30 日,RedHat 宣布斥資 2.5 億美元收購 CoreOS。

2018 年 3 月 28 日,這一切紛爭的始作俑者,Docker 公司的 CTO Solomon Hykes 宣布辭職,曾經紛紛擾擾的容器技術圈子,到此塵埃落定。

容器技術圈子在短短几年裡發生了很多變數,但很多事情其實也都在情理之中。就像 Docker 這樣一家創業公司,在通過開源社區的運作取得了巨大的成功之後,就不得不面對來自整個雲計算產業的競爭和圍剿。而這個產業的垄斷特性,對於 Docker 這樣的技術型創業公司其實天生就不友好。

在這種局勢下,接受微軟的天價收購,在大多數人看來都是一個非常明智和實際的選擇。可是 Solomon Hykes 卻多少帶有一些理想主義的影子,既然不甘於”寄人籬下”,那他就必須帶領 Docker 公司去對抗來自整個雲計算產業的壓力。

只不過,Docker 公司最後選擇的對抗方式,是將開源項目與商業產品緊密綁定,打造了一個極端封閉的技術生態。而這,其實違背了 Docker 項目與開發者保持親密關係的初衷。相比之下,Kubernetes 社區,正是以一種更加溫和的方式,承接了 Docker 項目的未盡事業,即:以開發者為核心,構建一個相對民主和開放的容器生態。

這也是為何,Kubernetes 項目的成功其實是必然的。

很難想象如果 Docker 公司最初選擇了跟 Kubernetes 社區合作,如今的容器生態又將會是怎樣的一番景象。不過我們可以肯定的是,Docker 公司在過去五年裡的風雲變幻,以及 Solomon Hykes 本人的傳奇經歷,都已經在雲計算的長河中留下了濃墨重彩的一筆。

小結
# 1. 容器技術的興起源於 PaaS 技術的普及;
# 2. Docker 公司發布的 Docker 項目具有里程碑式的意義;
# 3. Docker 項目通過"容器鏡像",解決了應用打包這個根本性難題。

# 容器本身沒有價值,有價值的是"容器編排"。
# 也正因為如此,容器技術生態才爆發了一場關於"容器編排"的"戰爭"。而這次戰爭,最終以 Kubernetes 項目和 CNCF 社區的勝利而告終。

Kubernetes核心概念

什麼是Kubernetes?

Kubernetes是一個完備的分佈式系統支撐平台。

Kubernetes具有完備的集群管理能力,包括多層次的安全防護和准入機制/多租戶應用支撐能力、透明的服務註冊和服務發現機制、內建智能負載均衡器、強大的故障發現和自我修復功能、服務滾動升級和在線擴容能力、可擴展的資源自動調度機制,以及多粒度的資源配額管理能力。同時kubernetes提供了完善的管理工具,這些工具覆蓋了包括開發、測試部署、運維監控在內的各個環節;因此kubernetes是一個全新的基於容器技術的分佈式架構解決方案,並且是一個一站式的完備的分佈式系統開發和支撐平台.

Kubernetes Service介紹

Service是分佈式集群結構的核心,一個Server對象有以下關鍵特徵:

# 1. 擁有一個唯一指定的名字(比如mysql-server)
# 2. 擁有一個虛擬IP(Cluster IP,Service IP或VIP和端口號)
# 3. 能夠提供某種遠程服務能力
# 4. 被映射到了提供這種服務能力的一組容器應用上.

Service的服務進程目前都基於Socker通信方式對外提供服務,比如redis、memcache、MySQL、Web Server,或者是實現了某個具體業務的一個特定的TCP Server進程。雖然一個Service通常由多個相關的服務進程來提供服務,每個服務進程都有一個獨立的Endpoint(IP+Port)訪問點,但Kubernetes 能夠讓我們通過Service虛擬Cluster IP+Service Port連接到指定的Service上。有了Kubernetes內建的透明負載均衡和故障恢復機制,不管後端有多少服務進程,也不管某個服務進程是否會由於發生故障而重新部署到其他機器,都不會影響到我們對服務的正常調用。更重要的是這個Service本身一旦創建就不再變化,這意味着Kubernetes集群中,我們再也不用為了服務的IP地址變來變去的問題而頭疼。

Kubernetes Pod介紹

Pod概念 Pod運行在一個我們稱之為Node的環境中,可以是私有雲也可以是公有雲的虛擬機或者物理機上,通常在一個節點上運行幾百個Pod,每個Pod運行着一個特殊的稱之為Pause的容器,其他容器則為業務容器,這些業務容器共享着Pause容器的網絡棧和Volume掛載卷,因此他們之間的通訊和數據交換更為高效,在設計時我們充分利用這一特徵將一組密切相關的服務進程放入同一個Pod中.

並不是每個Pod和它裏面的容器都映射到一個Service上,只是那些提供服務(無論是內還是對外)的一組Pod才會被映射成一個服務.

Service和Pod如何關聯

容器提供了強大的隔離功能,所以有必要把Service提供服務的這組容器放入到容器中隔離,Kubernetes設計了Pod服務,將每個服務進程包裝成相應的Pod中,使其成為Pod中運行的一個容器Container,為了建立Service和Pod間的關聯關係,Kubernetes首先給每個Pod貼上了一個標籤Label,給運行Mysql的Pod貼上了name=mysql標籤,給運行PHP貼上name=php標籤,然後給相應的Service定義標籤選擇器Label Selector,比如Mysql Service的標籤選擇器選擇條件為name=mysql,意為該Service要作用於所有包含name=mysql Label的Pod上,這樣就巧妙的解決了Service和Pod關聯的問題.

Kubernetes RC介紹

RC介紹在Kubernetes集群中,你只需要為需要擴容的Service關聯的Pod創建一個RC(Replication Controller),則該Service的擴容以至於後來的Service升級等頭疼問題都可以迎刃而解,定義一個RC文件包含以下3個關鍵點.

# 1. 目標Pod的定義
# 2. 目標Pod需要運行的副本數量(Replicas)
# 3. 要監控的目標Pod的標籤(Label)

在創建好RC系統自動創建號Pod后,Kubernetes會通過RC中定義的Label篩選出對應的Pod實例並監控其狀態和數量,如果實例數量少於定義的副本數量Replicas則會用RC中定義的Pod模板來創建一個新的Pod,然後將Pod調度到合適的Node上運行,直到Pod實例的數量達到預定目標,這個過程完全是自動化的,無需人干預,只需要修改RC中的副本數量即可.

Kubernetes Master介紹

Kubernetes 里的Master指的是集群控制節點,每個Kubernetes集群里需要有一個Master節點來負責整個集群的管理和控制,基本上Kubernetes所有的控制命令都發給它,它負責具體的執行過程,我們後面執行的所有命令基本上都是在Master節點上運行的。如果Master宕機或不可用,那麼集群內容器的管理都將失效.

Master節點上運行一下一組關鍵進程:

  1. Kubernetes API Server: 提供了HTTP Rest接口的關鍵服務進程,是Kubernetes里所有資源的增刪改查等操作的唯一入口,也是集群控制的入門進程.
  2. Kubernetes Controller Manager 里所有的資源對象的自動化控制中心.
  3. Kubernetes Scheduler: 負責資源調度(Pod調度)的進程

另外在Master節點還需要啟動一個etcd服務,因為Kubernetes里所有資源對象的數據全部保存在etcd中.

Kubernetes Node介紹

除了Master,集群中其他機器稱為Node節點,每個Node都會被分配一些工作負載Docker容器,當某個Node宕機,其上的工作負載都會被Master自動轉移到其他節點上去.

每個Node節點上都運行着以下一組關鍵進程

# 1. kubelet: 負責Pod對應的創建、停止等服務,同時與Master節點密切協作,實現集群管理的基本功能.
# 2. kube-proxy: 實現Kubernetes Service的通信與負載均衡機制的重要組件.
# 3. Docker Engine: Docker引擎,負責本機的容器創建和管理工作

在集群管理方面,Kubernetes將集群中的機器劃分為一個Master節點和一群工作節點(Node)中,在Master節點上運行着集群管理相關的一組進程kube-apiserver,kube-controller-manager和kube-scheduler,這些進程實現了整個集群的資源管理,Pod調度,彈性伸縮,安全控制,系統監控和糾錯等管理功能,並且都是全自動完成的、Node作為集群中的工作節點,運行真正的應用程序,在Node上Kubernetes最小運行單元是Pod,Node上運行着Kubernetes的Kubelet、kube-proxy服務進程,這些服務進程負責Pod創建、啟動、監控、重啟、銷毀以及軟件模式的負載均衡.

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

門多西諾複合大火續燒 面積相當於洛杉磯市

摘錄自2018年8月8日中央通訊社台北報導

美國加州野火繼續傳出災情,「門多西諾複合大火」延燒面積已相當於整個洛杉磯市,上萬名消防員今天仍不斷努力救災控制火勢,上月爆發的一連串野火已造成11人喪命。

加州北部的門多西諾複合大火(Mendocino Complex Fire)由2個分別名為「河流」(River fire)及「牧場」(Ranch fire)的野火組成,延燒面積已達29萬英畝(11萬7359公頃),大小相當於整個洛杉磯市。

門多西諾複合大火昨天(7日)改寫短短8個月前「湯瑪斯野火」(Thomas Fire)紀錄,成為加州有紀錄以來最大野火。湯瑪斯野火去年12月在南加州肆虐,延燒面積達28萬1893英畝。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

蓄意污染亞馬遜雨林 雪佛龍在厄瓜多憲法法庭敗訴

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

美國雪佛龍(Chevron)公司的世紀污染訴訟案,有了最新進展。厄瓜多憲法法庭稍早駁回了雪佛龍對95億美元污染判決的最終上訴,認定該公司蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林的原民土地上。

長達151頁的判決書中,以8比0一致判定原告原民團體勝訴,並駁回了雪佛龍的所有說法,例如雪佛龍自己也是詐欺受害者,以及厄瓜多法院對此案無管轄權等都遭到駁回。

前油田運營商德士古(後被雪佛龍收購)蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林。圖片來源:Caroline Bennett / Rainforest Action Network

環境集體訴訟案 第四次獲勝

厄瓜多憲法法院僅審理憲法問題,是厄瓜多第三大上訴法院,也是該國第四個維持2011年判決結果的法院,連同厄瓜多最高民事法庭和國家法院一致裁定雪佛龍須做出鉅額賠償。

這個針對雪佛龍公司的集體訴訟案,判決書於7月11日發布,原告是守護亞馬遜陣線(Frente de Defensa de la Amazonia),代表厄瓜多亞馬遜北部地區80個原民和農民社區的草根團體。

該訴訟最初由約3萬名熱帶雨林村民於1993年在紐約聯邦法院提出,被告是前油田運營商德士古(Texaco,已被雪佛龍公司收購)。2001年雪佛龍公司接受厄瓜多具有管轄權後,一名美國聯邦法官將此案移交給厄瓜多法院。

「這個判決是厄瓜多人民20年來為了爭取環境正義,對抗世界級污染者、流氓企業的再一次巨大勝利,」環保金人獎得主、1993年訴訟案起草者楊澤(Luis Yanza)表示。他目前也是守護亞馬遜陣線的主席。

楊澤說:「在雪佛龍開始賠償厄瓜多人民前,任何國家都不應該與雪佛龍做生意。」

致癌油廢料污染 雪佛龍八年拖延纏訟

厄瓜多首席律師薩拉查(Patricio Salazar)表示,「司法已認定雪佛龍非法攻擊代表原住民社群的律師,而不是根據是非來訴訟。雪佛龍現在極有可能必須全額支付賠償金,因為它在法律上和道德上都有義務這樣做。」

厄瓜多初審法院依據105份技術證據報告,在2011年判決該公司致癌油廢料污染了1500平方英里的亞馬遜土地。但是八年來,雪佛龍以拖延戰術因應訴訟。

生活在熱帶雨林中的數千人,包括許多原住民,已經死於癌症,而成千上萬的人必須忍受這場世界級的公衛災難。

代表厄瓜多村民的美國律師唐齊格(Steven Donziger)。圖片來源:Steven Donziger

拒支付賠償金 村民仍沒有醫療

收受雪佛龍公司400口油井特許使用費的厄瓜多政府,沒有給受害者太大幫助。

哈佛大學畢業,代表厄瓜多村民的美國律師唐齊格(Steven Donziger)曾走訪受影響地區數十次,他表示受影響地區並沒有醫療服務,許多人甚至一次醫生都沒看,也沒接受任何治療就死於癌症。

「雪佛龍在厄瓜多引發了一場史無前例的人道主義危機。再不清除污染,未來幾年將有數萬人死亡。全世界都必須關注,雪佛龍的股東和管理層必須立即採取行動,解決這個日益嚴重的問題。」唐齊格說。

雪佛龍多年來一直拒絕支付判決賠償金,目前連本帶利已經來到120億美元(約新台幣1361億元)。

雪佛龍公司人員甚至威脅原民,若繼續堅持訴訟,他們可和原告「終身纏訟」。

卡普蘭法官 採信偽證風波

這份最新判決也是對美國紐約南區地方法院法官卡普蘭(Lewis A. Kaplan)的重大打擊。

2014年,這位具有爭議性的法官僅憑一位承認收賄的雪佛龍證人的假證詞便裁定,厄瓜多最高法院對雪佛龍提出的95億美元賠償判決是透過欺詐和脅迫手段取得。卡普蘭拒絕讓公正的事實調查員陪審團參與,也拒絕考量任何有關雪佛龍污染厄瓜多環境的證據。

但新事證顯示,雪佛龍支付厄瓜多前法官格拉(Alberto Guerra)大筆賄款之後,卡普蘭的判決被推翻。雪佛龍幫助格拉一家搬到美國後,格拉承認在雪佛龍律師的指導下作偽證。

卡普蘭僅憑格拉的偽證做出判決,也是全世界唯一做出有利雪佛龍判決的法官。

「出於省錢的錯誤營運決策」 法庭定調蓄意污染

基於比卡普蘭所能取得更完整的證據,17名厄瓜多法官作出有利厄瓜多村民的判決。12名加拿大法官,包括該國最高法院,也針對多個技術性問題作出有利厄瓜多村民的判決。

厄瓜多憲法法庭強調,雪佛龍污染環境造成嚴重後果不是意外造成,而是處心積慮為公司和股東省錢的運營決策,加上20年來蓄意拖延的額外罪行所導致。

厄瓜多原告也在加拿大法院取得了幾項上訴勝利,他們在加國蒐集雪佛龍資產,以迫使其遵守厄瓜多的判決。

Chevron Defeated in Ecuador’s Constitutional Court QUITO, Ecuador, July 31, 2018 (ENS)

In a benchmark pollution case, Ecuador’s Constitutional Court has rejected Chevron’s final appeal of a $9.5 billion pollution judgment that found the company deliberately dumped billions of gallons of toxic oil waste onto Indigenous lands in the Amazon rainforest.

Photo: Caroline Bennett / Rainforest Action Network

The unanimous 8-0 decision, issued in a 151-page document published July 11, was a total victory for the Indigenous groups that brought the case and a rejection of all of Chevron’s claims.

The Court rejected Chevron’s allegations that it was victimized by fraud, and the court threw out the company’s claim that Ecuadorian courts had no jurisdiction over the matter.

Ecuador’s Constitutional Court, which deals only with Constitutional issues, is the third major appellate court in Ecuador and the fourth court overall in the country to uphold the trial-level decision against Chevron, which was issued in 2011. Ecuador’s highest civil court, the National Court of Justice, has ruled unanimously to affirm the judgment against Chevron.

The class action case against Chevron was spearheaded by the Frente de Defensa de la Amazonia, the Amazon Defense Front, a grassroots group representing 80 Indigenous peoples and farmer communities in Ecuador’s northern Amazon region.

The case was originally filed in 1993 in federal court in New York against the former oil field operator Texaco, now part of Chevron Corporation, on behalf of an estimated 30,000 rainforest villagers. But in 2001 a U.S. federal judge moved it to Ecuador’s courts at Chevron’s request after the company accepted jurisdiction there.

“This decision is another huge victory for the people of Ecuador in their historic two-decade battle for environmental justice against the world’s worst corporate polluter and rogue operator,” said Luis Yanza, a Goldman Prize winner who initiated the lawsuit against Chevron in U.S. federal court in 1993, and serves as president of the Frente de Defensa de la Amazonia, Amazon Defense Front.

“No country should ever do business with Chevron until the company first pays for the harm it caused to the people of Ecuador,” Yanza said.

Patricio Salazar, the lead Ecuadorian lawyer on the case, said, “Justice has prevailed over Chevron’s illegal attempts to engage in constant attacks on lawyers who defend the Indigenous communities rather than litigate in good faith on the merits. It is now highly likely that Chevron will pay every last dollar of the judgment against it, as it is legally and ethically obligated to do.”

After eight years of proceedings slowed by Chevron’s strategy of deliberate delay, Ecuador’s trial court relied on 105 technical evidentiary reports to find in 2011 that the company poisoned a 1,500 square mile area of the Amazon with carcinogenic oil waste.

Thousands who live in the rainforest, including many Indigenous peoples, have died of cancer while tens of thousands must endure what is one of the world’s worst ongoing public health catastrophes.

Photo: Jonathan McIntosh / Rainforest Action Network

Ecuador’s government, which received royalties from Chevron’s operation of 400 well sites, has been of little help to the victims. Medical care in the affected region is non-existent, and many people perish from cancer without even visiting a doctor and after receiving no treatment, said Steven Donziger, the Harvard educated U.S. legal representative of the Ecuadorian communities, who has taken dozens of trips to the affected area.

“Chevron has caused a humanitarian crisis in Ecuador of epic proportions that is ongoing to this day,” he said. “Tens of thousands of people will die in the coming years if nothing is done to clean up the pollution. The world must pay attention and Chevron shareholders and management must act immediately to address this worsening problem.”

Chevron has refused for years to pay the Ecuador judgment, now worth $12 billion with interest. Company officials have threatened the Indigenous groups with a “lifetime of litigation” if they persist.

The latest Ecuadorean court decision is also a major blow to controversial Judge Lewis A. Kaplan, a judge of the U.S. District Court for the Southern District of New York.

In 2014, Judge Kaplan ruled that the $9.5 billion Lago Agrio judgment leveled against Chevron by Ecuador’s highest court, was obtained by way of fraud and coercion.

Kaplan relied on false testimony from an admittedly corrupt Chevron witness to find that the Ecuador judgment was procured by fraud. Kaplan refused to seat a jury of impartial fact finders, and he refused to consider any evidence of Chevron’s environmental contamination in Ecuador.

But Kaplan’s decision was disproven after evidence emerged that Chevron paid large sums to Alberto Guerra, a former Ecuadorian judge booted from the bench after he admitted taking bribes. Guerra was moved with his family by Chevron to the United States and later admitted lying on the stand after being coached for 53 days by Chevron lawyers headed by Randy Mastro at Gibson Dunn.

Kaplan based his core findings largely on Guerra’s false testimony. And Kaplan remains the only judge in the world to have ruled in favor of Chevron.

Seventeen Ecuador judges, who had access to a fuller evidentiary record than Kaplan, ruled in favor of the affected communities. Twelve judges from Canada, including the country’s entire Supreme Court, have also ruled in favor of the Ecuadorians on various technical issues.

The Ecuador decision confronts Chevron on the brutal human consequences of both its original environmental crimes, which the Court emphasizes were not the result of an accident, but rather of deliberate operational decision-making designed to save money and enrich the company’s shareholders and executives, and the additional offense of its two-decade campaign of distraction and delay.

The Ecuadorian plaintiffs also have picked up several appellate victories in Canadian courts as they attempt to collect Chevron assets in that country to force compliance with the Ecuador judgment.

※ 全文及圖片詳見:

作者

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

延伸閱讀

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

※推薦評價好的iphone維修中心