004.OpenShift命令及故障排查

一 CLI訪問OpenShift資源

1.1 資源操作


OCP將OpenShift集群中的為由主節點管理的對象統稱為資源,如:node、service、pod、project、deployment、user。

即使針對的是不同的資源,OpenShift命令行工具也提供了一種統一的、一致的方法來更新、修改、刪除和查詢這些資源。

oc命令行工具提供了在軟件開發項目的整個交付生命周期中修改和管理資源的常見操作。

1.2 安裝oc工具


在OpenShift安裝過程中,oc命令行工具安裝在所有master和node節點上,還可以在不屬於OpenShift集群的機器。

安裝后,可以使用用戶名和密碼對任何主節點通過身份驗證后執行相關命令。

根據使用的平台,安裝oc命令行工具有以下幾種方式:

yum安裝:在RHEL平台上,可通過以下命令安裝oc客戶端命令。

[user@host ~]$ sudo yum install atomic-openshift-clients

其它 Linux 發行版本和操作系統,需在擁有 OpenShift 訂閱后,在 Red Hat Customer Portal 中下載。

提示:oc安裝完成后自動補全需要退出一次才可生效,或者source /etc/bash_completion.d/oc。

1.3 oc主要查詢命令


[student@workstation ~]$ oc –help #显示幫助信息

[student@workstation ~]$ oc login -u developer -p redhat https://master.lab.example.com #登錄到OpenShift集群

提示:從client成功通過身份驗證之後,OpenShift將授權令牌保存在用戶的主文件夾中。此令牌用於後續請求,從而無需重新輸入憑據或完整的主URL。

  1 [root@master ~]# oc whoami
  2 system:admin					#master的root用戶為集群的最高權限的用戶
  3 [student@workstation ~]$ oc whoami		        #查看當前用戶
  4 developer
  5 [student@workstation ~]$ oc new-project working	#創建project
  6 [student@workstation ~]$ oc status		        #查看項目狀態
  7 In project working on server https://master.lab.example.com:443
  8 You have no services, deployment configs, or build configs.
  9 Run 'oc new-app' to create an application.
 10 [student@workstation ~]$ oc delete project working	#刪除project
 11 [student@workstation ~]$ oc logout		        #退出該集群。
 12 [student@workstation ~]$ oc get pods		#查看pod
 13 NAME                      READY     STATUS    RESTARTS   AGE
 14 hello-openshift-1-6ls8z   1/1       Running   0          4h
 15 [student@workstation ~]$ oc get all		        #查看所有主要組件信息
 16 [student@workstation ~]$ oc get pods -w		#-w表示以監視模式運行


1.4 oc 其他命令


oc describe:如果oc get提供的摘要不夠,可以使用oc describe命令檢索關於資源的更詳細信息。

[student@workstation ~]$ oc describe pod hello-openshift-1-6ls8z

oc export:使用oc export命令導出資源的定義。典型的用例包括創建備份,或者用於修改定義。默認情況下,export命令以YAML格式輸出對象表示,但是可以通過提供-o選項來更改。

oc create:使用oc create命令從資源定義創建資源。通常,這與用於編輯定義的oc export命令相匹配。

oc delete RESOURCE_TYPE name:使用oc delete命令從OpenShift集群中刪除資源。

注意:部分資源直接刪除後會重新創建,如基於rc的pod,需要對OpenShift體系資源展示形式有一個基本的了解。

oc exec:使用oc exec命令在容器中執行命令,可以使用此命令作為腳本的一部分運行交互式和非交互式批處理命令。

oc rsh POD:oc rsh pod命令打開到容器的遠程shell會話,要遠程登錄到容器shell並執行命令,請運行以下命令。

[student@workstation ~]$ oc rsh <pod>

注意:oc rsh需要pod中存在相應的shell,如bash。

二 OpenShift資源類型

2.1 常見資源


OpenShift容器平台中的應用程序由不同類型的資源組成,主要常見的類型有:

  • Container:如何在可移植Linux環境中運行一個或多個進程的定義。容器從一個映像啟動,並且通常與同一機器上的其他容器隔離。
  • Image:一個分層的Linux文件系統,包含應用程序代碼、依賴關係和函數庫等。image由一個名稱標識,該名稱可以是當前集群的本地名稱,也可以指向遠程Docker倉庫。
  • Pod:部署在節點上並共享唯一IP地址和卷(持久存儲)的一個或多個容器,Pods還為每個容器定義安全性和運行時策略。
  • Label:標籤是鍵值對,可以分配給系統中的任何資源進行分組和選擇。通常資源使用標籤來標識其他資源集。
  • Volume:默認情況下容器不是持久性的,即容器的內容在重新啟動時被清除。volume是掛載在pod及其容器上的文件系統,它們可能由許多本地或網絡的存儲提供。最簡單的卷類型是EmptyDir,它是一台機器上的臨時目錄。
  • Node:node是集群中用來運行容器的節點,node通常由管理員管理,而不是由最終用戶管理。
  • Service:service是表示一組pod的邏輯名稱,service被分配一個IP地址和一個DNS名稱,可以通過端口或route向集群外部公開。名為SERVICE_HOST的環境變量會自動注入到其他pod中。
  • Route:route是一個DNS條目,創建它是為了指向一個service,以便可以從集群外部訪問它。可以配置一個或多個路由器來處理這些route,通常通過HAProxy負載均衡器。
  • Replication Controller:Replication Controller基於匹配一組label的Templates維護特定數量的pod。如果刪除了pod,控制器將創建該pod的新副本。Replication Controller最常用來表示基於image的應用程序部分的單個部署。
  • Deployment Configuration:deployment configuration定義pod的模板,並在屬性更改時管理部署新映像或配置更改。單個deployment configuration通常類似於單個微服務。deployment configuration可以支持許多不同的部署模式,包括完全重啟、可定製的滾動更新以及生命周期前後的順序。每個deployment都表示為一個replication controller。
  • Build Configuration:build configuration包含如何將源代碼和基本image構建為新image的描述。Build可以是基於源代碼的,可以為常見語言(如Java、PHP、Ruby或Python)或基於docker的(從Dockerfile創建構建)使用構建器映像。每個build configuration都有webhook,可以通過對其基本映像的更改自動觸發。
  • Build:構建從源代碼、其他圖像、Dockerfiles或二進制輸入創建新image。Build在容器中運行,具有與普通pod相同的限制。Build通常會導致將image推入Docker倉庫中,但也可以選擇運行post-build測試而不push到image倉庫。
  • Image Streams and Image Stream Tags:IS使用標記名稱對相關is進行分組。它類似於源代碼倉庫中的分支。每個is可以有一個或多個標記(默認標記稱為“latest”),這些標記可能指向外部Docker倉庫、同一is中的其他標記,或者被控製為直接指向已知image。此外,可以通過集成的Docker倉庫直接將image push到docker倉庫。
  • Secret:secret資源可以保存文本或二進制secrets,以便注入至pod。默認情況下,在/var/run/secrets/kubernetes.io/serviceaccount上,每個容器都有一個secret,其中包含訪問API有限特權的令牌。可以創建新的secret並將它們掛載到自己的pod中,也可以引用構建中的secret(用於連接遠程服務器),或者使用它們將遠程image導入到is中。
  • Project:所有上述資源(node除外)都存在於項目中。項目具有成員列表及其role(如view、edit或admin),以及運行的pod上的一組安全控制,並限制項目可以使用多少資源,資源名稱在項目中是惟一的。


使用oc types命令快速查看可用的概念和類型。

2.2 創建應用


簡單的應用程序、複雜的多層應用程序和微服務應用程序都可以使用資源定義文件來描述。

這個文件包含許多pod定義、連接這些pod的服務定義、用於水平伸縮應用程序pod的rc或dc、用於持久存儲應用程序數據的持久卷,以及OpenShift可以管理的任何其他需要的內容。

oc new-app命令可以使用-o json或-o yaml選項分別創建以json或yaml格式的定義文件的資源。可以使用oc create -f <filename>命令調用定義文件,並將其用於創建應用程序,或者與其他資源定義文件合併以創建複合應用程序。

oc new-app命令可以以許多不同的方式創建在OpenShift上運行的pod應用程序。它可以使用source-to-image (S2I)流程從現有docker映像、Dockerfiles或原始源代碼創建pod。

運行oc new-app -h命令,了解在OpenShift上創建新應用程序的所有不同選項。最常見的選項如下:

運行以下命令創建應用程序。OpenShift根據Docker配置文件的ADD_REGISTRY選項定義的倉庫 pull image。

$ oc new-app mysql MYSQL_USER=user MYSQL_PASSWORD=pass MYSQL_DATABASE=testdb -l

db=mysql

根據私有倉庫中的image創建應用程序。

$ oc new-app –docker-image=myregistry.com/mycompany/myapp –name=myapp

根據存儲在Git庫中的源代碼創建應用程序。

$ oc new-app https://github.com/openshift/ruby-hello-world –name=ruby-hello

創建基於存儲在Git庫中的源代碼並引用IS的應用程序。

$ oc new-app https://mygitrepo/php-hello -i php:7.0 –name=php-hello

從Docker配置文件的ADD_REGISTRY指令定義的可用倉庫之一創建一個基於mysql映像的應用程序。l db=mysql選項定義了一個值為mysql的db標籤。

$ oc new-app mysql MYSQL_USER=user \

MYSQL_PASSWORD=pass \

MYSQL_DATABASE=testdb \

-l db=mysql

下圖显示了oc new-app命令在參數為容器image時創建的Kubernetes和OpenShift資源。該命令創建dc、is和svc,可以通過端口或route從外部訪問。



提示:通過使用帶有源代碼的oc new-app,將創建一個build configuration,而bc又從源代碼創建一個新的應用程序。但是,如果命令中沒有使用源代碼,則不會創建gc。該命令始終為應用程序創建dc和svc。

三 oc使用練習

3.1 前置準備


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

3.2 本練習準備


[student@workstation ~]$ lab manage-oc setup

3.3 驗證OpenShift

  1 [student@workstation ~]$ oc login -u admin -p redhat https://master.lab.example.com
  2 [student@workstation ~]$ oc project default
  3 Already on project "default" on server "https://master.lab.example.com:443".
  4 [student@workstation ~]$ oc project default
  5 Already on project "default" on server "https://master.lab.example.com:443".
  6 [student@workstation ~]$ oc get nodes
  7 NAME                     STATUS    ROLES     AGE       VERSION
  8 master.lab.example.com   Ready     master    23h       v1.9.1+a0ce1bc657
  9 node1.lab.example.com    Ready     compute   23h       v1.9.1+a0ce1bc657
 10 node2.lab.example.com    Ready     compute   23h       v1.9.1+a0ce1bc657
 11 [student@workstation ~]$ oc describe node master.lab.example.com		#查看master節點詳情
 12 [student@workstation ~]$ oc describe node node1.lab.example.com
 13 [student@workstation ~]$ oc describe node node2.lab.example.com
 14 [student@workstation ~]$ oc get pods -o wide
 15 NAME                       READY     STATUS    RESTARTS   AGE       IP              NODE
 16 docker-registry-1-8v7sh    1/1       Running   4          23h       10.129.0.30     node2.lab.example.com
 17 docker-registry-1-rrmhm    1/1       Running   2          23h       10.128.0.12     node1.lab.example.com
 18 registry-console-1-xzxxp   1/1       Running   4          23h       10.129.0.31     node2.lab.example.com
 19 router-1-fwttd             1/1       Running   4          23h       172.25.250.12   node2.lab.example.com
 20 router-1-xdw84             1/1       Running   2          23h       172.25.250.11   node1.lab.example.com
 21 [student@workstation ~]$ oc  describe pod docker-registry-1-8v7sh		#查看pod詳情


3.4 pod操作


[student@workstation ~]$ oc exec docker-registry-1-8v7sh hostname #執行pod中命令

docker-registry-1-8v7sh

[student@workstation ~]$ oc exec router-1-fwttd ls /

[student@workstation ~]$ oc exec docker-registry-1-8v7sh cat /etc/resolv.conf

提示:只要pod中存在的命令,都可以通過oc exec直接執行。

[student@workstation ~]$ oc rsh docker-registry-1-8v7sh #進入pod的shell

sh-4.2$ ls /

3.5 oc其他操作


[student@workstation ~]$ oc status -v #現實詳細的狀態



[student@workstation ~]$ oc get events #查看集群生命周期事件

[student@workstation ~]$ oc get all #獲取所有資源信息

3.6 導出資源


[student@workstation ~]$ oc export pod docker-registry-1-8v7sh

提示:oc export命令通常用於導出現有資源,並將它們轉換為配置文件(YAML或JSON),以便備份或在集群的其他地方重新創建資源。

[student@workstation ~]$ oc export svc,dc docker-registry –as-template=docker-registry

#通過將–as-template選項傳遞給oc export命令,將多個資源作為OpenShift模板同時導出。

[student@workstation ~]$ oc export svc,dc docker-registry > docker-registry.yaml #也可以使用重定嚮導出

[student@workstation ~]$ oc export –help #查看幫助

四 oc常見故障排除

4.1 常見環境信息


使用RPM安裝的OCP,那麼master和node的ocp相關服務將作為Red Hat Enterprise Linux服務運行。從master和node使用標準的sosreport實用程序,收集關於環境的信息,以及docker和openshift相關的信息。

[root@master ~]# sosreport -k docker.all=on -k docker.logs=on

sosreport命令創建一個包含所有相關信息的壓縮歸檔文件,並將其保存在/var/tmp目錄中。

另一個有用的診斷工具是oc adm diagnostics命令,能夠在OpenShift集群上運行多個診斷檢查,包括network、日誌、內部倉庫、master節點和node節點的服務檢查等等。oc adm diagnostics –help命令,獲取幫助。

4.2 常見診斷命令


oc客戶端命令是用來檢測和排除OpenShift集群中的問題的主要工具。它有許多選項,能夠檢測、診斷和修復由集群管理的主機和節點、服務和資源的問題。若已授權所需的權限,可以直接編輯集群中大多數託管資源的配置。

  • oc get events


事件允許OpenShift記錄集群中生命周期事件的信息,以統一的方式查看關於OpenShift組件的信息。oc get events命令提供OpenShift namespace的事件信息,可實現以下事件的捕獲:

    • Pod創建和刪除
    • pod調度的節點
    • master和node節點的狀態


事件通常用於故障排除,從而獲得關於集群中的故障和問題的高級信息,然後使用日誌文件和其他oc子命令進一步定位。

示例:使用以下命令獲得特定項目中的事件列表。

[student@workstation ~]$ oc get events -n <project>

也可以通過Web控制台進行事件的查看events。

  • oc log


oc logs命令查看build、deployment或pod的日誌輸出,。

示例1:使用oc命令查看pod的日誌。

[student@workstation ~]$ oc logs pod

示例2:使用oc命令查看build的日誌。

[student@workstation ~]$ oc logs bc/build-name

使用oc logs命令和-f選項實時跟蹤日誌輸出。例如,這對於連續監視build的進度和檢查錯誤非常有用。

也可以通過Web控制台進行事件的查看log。

  • oc rsync


oc rsync命令將內容複製到正在運行的pod中的目錄或從目錄複製內容。如果一個pod有多個容器,可以使用-c選項指定容器ID。否則,它默認為pod中的第一個容器。通常用於從容器傳輸日誌文件和配置文件。

示例1:將pod目錄中的內容複製到本地目錄。

[student@workstation ~]$ oc rsync <pod>:<pod_dir> <local_dir> -c <container>

示例2:將內容從本地目錄複製到pod的目錄中。

[student@workstation ~]$ oc rsync <local_dir> <pod>:<pod_dir> -c <container>

  • oc port-forward


使用oc port-forward命令將一個或多個本地端口轉發到pod。這允許在本地監聽特定或隨機端口,並將數據轉發到pod中的特定端口。

示例1:本地監聽3306並轉發到pod的3306.

[student@workstation ~]$ oc port-forward <pod> 3306:3306

五 TS常見故障

5.1 資源限制和配額問題


對於設置了資源限制和配額的項目,不適當的資源配置將導致部署失敗。使用oc get events和oc describe命令來排查失敗的原因。

例如試圖創建超過項目中pod數量配額限制的pod數量,那麼在運行oc get events命令時會提示:

Warning FailedCreate {hello-1-deploy} Error creating: pods “hello-1” is forbidden:

exceeded quota: project-quota, requested: cpu=250m, used: cpu=750m, limited: cpu=900m

5.2 S2I build失敗


使用oc logs命令查看S2I構建失敗。例如,要查看名為hello的構建配置的日誌:

[student@workstation ~]$ oc logs bc/hello

例如可以通過在build configuration策略中指定BUILD_LOGLEVEL環境變量來調整build日誌的詳細程度。

  1 {
  2 "sourceStrategy": {
  3 ...
  4 "env": [
  5 {
  6 "name": "BUILD_LOGLEVEL",
  7 "value": "5"
  8 }
  9 ]
 10 }
 11 }


5.3 ErrImagePull和imgpullback錯誤


通常是由不正確的deployment configuration造成、部署期間引用的錯誤或缺少image或Docker配置不當造成。

使用oc get events和oc describe命令排查,通過使用oc edit dc/<deploymentconfig>編輯deployment configuration來修復錯誤。

5.4 docker配置異常


master和node上不正確的docker配置可能會在部署期間導致許多錯誤。

通常檢查ADD_REGISTRY、INSECURE_REGISTRY和BLOCK_REGISTRY設置。使用systemctl status, oc logs, oc get events和oc describe命令對問題進行排查。

可以通添加/etc/sysconfig/docker配置文件中的–log-level參數來更改docker服務日誌級別。

示例:將日誌級別設置為debug。

OPTIONS=’–insecure-registry=172.30.0.0/16 –selinux-enabled –log-level=debug’

5.5 master和node節點失敗


運行systemctl status命令,對atomicopenshift-master、atom-openshift-node、etcd和docker服務中的問題進行排查。使用journalctl -u <unit-name>命令查看與前面列出的服務相關的系統日誌。

可以通過在各自的配置文件中編輯–loglevel變量,然後重新啟動關聯的服務,來增加來自atom-openshift-node、atomicopenshift-master-controllers和atom-openshift-master-api服務的詳細日誌記錄。

示例:設置OpenShift主控制器log level為debug級別,修改/etc/sysconfig/atomic-openshift-master-controllers文件。

OPTIONS=–loglevel=4 –listen=https://0.0.0.0:8444

延伸:

Red Hat OpenShift容器平台有五個級別的日誌詳細程度,無論日誌配置如何,日誌中都會出現帶有致命、錯誤、警告和某些信息嚴重程度的消息。

  • 0:只有錯誤和警告
  • 2:正常信息(默認)
  • 4:debug級信息
  • 6:api級debug信息(請求/響應)
  • 8:帶有完整請求體的API debug信息

5.6 調度pod失敗


OpenShift master調度pod在node上運行,通常由於node本身沒有處於就緒狀態,也由於資源限制和配額,pod無法運行。

使用oc get nodes命令驗證節點的狀態。在調度失敗期間,pod將處於掛起狀態,可以使用oc get pods -o wide命令進行檢查,該命令還显示了計劃在哪個節點上運行pod。使用oc get events和oc describe pod命令檢查調度失敗的詳細信息。

示例1:如下所示pod調度失敗,原因是CPU不足。

{default-scheduler } Warning FailedScheduling pod (FIXEDhello-phb4j) failed to

fit in any node

fit failure on node (hello-wx0s): Insufficient cpu

fit failure on node (hello-tgfm): Insufficient cpu

fit failure on node (hello-qwds): Insufficient cpu

示例2:如下所示pod調度失敗,原因是節點沒有處於就緒狀態,可通過oc describe排查。

{default-scheduler } Warning FailedScheduling pod (hello-phb4j): no nodes

available to schedule pods

六 常見問題排查

6.1 前置準備


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

6.2 本練習準備


[student@workstation ~]$ lab common-troubleshoot setup

6.3 創建應用


[student@workstation ~]$ oc new-project common-troubleshoot

[student@workstation ~]$ oc new-app –name=hello -i php:5.4 \ #從源代碼創建應用

> http://services.lab.example.com/php-helloworld


6.4 查看詳情


[student@workstation ~]$ oc describe is php -n openshift





結論:由上可知,倉庫中不存在所需鏡像。

6.5 修正錯誤


[student@workstation ~]$ oc new-app –name=hello -i php:7.0 http://services.lab.example.com/php-helloworld

[student@workstation ~]$ oc get pod -o wide #再次查看發現一隻出於pending

NAME READY STATUS RESTARTS AGE IP NODE

hello-1-build 0/1 Pending 0 40s <none> <none>

6.6 查看詳情

  1 [student@workstation ~]$ oc log hello-1-build		#查看log
  2 W0720 20:22:16.455008   18942 cmd.go:358] log is DEPRECATED and will be removed in a future version. Use logs instead.
  3 [student@workstation ~]$ oc get events			#查看事件
  4 LAST SEEN   FIRST SEEN   COUNT     NAME                             KIND      SUBOBJECT   TYPE      REASON             SOURCE              MESSAGE
  5 56s         4m           15        hello-1-build.15b31cbd8da8ff1e   Pod                   Warning   FailedScheduling   default-scheduler   0/3 nodes are available: 1 MatchNodeSelector, 2 NodeNotReady.
  6 [student@workstation ~]$ oc describe pod hello-1-build	#查看詳情
  7 ……
  8 Warning  FailedScheduling  31s (x22 over 5m)  default-scheduler  0/3 nodes are available: 1 MatchNodeSelector, 2 NodeNotReady.
  9 結論:由上可知,沒有node可供調度此pod。
 10 [root@master ~]# oc get nodes				#在master節點進一步排查node情況
 11 NAME                     STATUS     ROLES     AGE       VERSION
 12 master.lab.example.com   Ready      master    1d        v1.9.1+a0ce1bc657
 13 node1.lab.example.com    NotReady   compute   1d        v1.9.1+a0ce1bc657
 14 node2.lab.example.com    NotReady   compute   1d        v1.9.1+a0ce1bc657
 15 結論:由上可知,node狀態異常,都未出於ready狀態。


6.7 檢查服務


[root@node1 ~]# systemctl status atomic-openshift-node.service

[root@node2 ~]# systemctl status atomic-openshift-node.service

[root@node1 ~]# systemctl status docker

[root@node2 ~]# systemctl status docker




結論:由上可知,node節點的docker異常。

6.8 啟動服務


[root@node1 ~]# systemctl start docker

[root@node2 ~]# systemctl start docker

6.9 確認驗證


[root@master ~]# oc get nodes #再次查看node狀態

NAME STATUS ROLES AGE VERSION

master.lab.example.com Ready master 1d v1.9.1+a0ce1bc657

node1.lab.example.com Ready compute 1d v1.9.1+a0ce1bc657

node2.lab.example.com Ready compute 1d v1.9.1+a0ce1bc657

[student@workstation ~]$ oc get pods #確認pod是否正常調度至node

NAME READY STATUS RESTARTS AGE

hello-1-build 1/1 Running 0 22m

[student@workstation ~]$ oc describe is #查看is詳情




結論:由上可知,IS也將image推送至內部倉庫。

七 oc命令綜合實驗

7.1 前置準備


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

7.2 本練習準備


[student@workstation ~]$ lab execute-review setup

7.3 git項目至本地


[student@workstation ~]$ cd /home/student/DO280/labs/execute-review/

[student@workstation execute-review]$ git clone http://services.lab.example.com/node-hello

7.4 docker構建image


[student@workstation execute-review]$ cd node-hello/

[student@workstation node-hello]$ docker build -t node-hello:latest .

[student@workstation node-hello]$ docker images #查看image

REPOSITORY TAG IMAGE ID CREATED SIZE

node-hello latest ff48daa00d8e 12 seconds ago 495 MB

registry.lab.example.com/rhscl/nodejs-6-rhel7 latest fba56b5381b7 22 months ago 489 MB

7.5 修改docker tag


[student@workstation node-hello]$ docker tag ff48daa00d8e \

> registry.lab.example.com/node-hello:latest

[student@workstation node-hello]$ docker images

REPOSITORY TAG IMAGE ID CREATED SIZE

node-hello latest ff48daa00d8e About a minute ago 495 MB

registry.lab.example.com/node-hello latest ff48daa00d8e About a minute ago 495 MB

registry.lab.example.com/rhscl/nodejs-6-rhel7 latest fba56b5381b7 22 months ago 489 MB

7.6 push image

[student@workstation node-hello]$ docker push registry.lab.example.com/node-hello:latest

7.7 創建project


[student@workstation ~]$ oc login -u developer -p redhat \

> https://master.lab.example.com

[student@workstation ~]$ oc projects

[student@workstation ~]$ oc project execute-review

[student@workstation ~]$ oc new-app registry.lab.example.com/node-hello –name hello

[student@workstation ~]$ oc get all #查看全部資源


7.8 排查ImagePullBackOff


[student@workstation ~]$ oc logs hello-1-2jkkj #查看日誌

Error from server (BadRequest): container “hello” in pod “hello-1-2jkkj” is waiting to start: trying and failing to pull image

[student@workstation ~]$ oc describe pod hello-1-2jkkj #查看詳情

[student@workstation ~]$ oc get events –sort-by=’.metadata.creationTimestamp’ #查看事件

結論:由上可知,為image pull失敗。

7.9 手動pull鏡像


[student@workstation ~]$ oc get pod -o wide

NAME READY STATUS RESTARTS AGE IP NODE

hello-1-2jkkj 0/1 ImagePullBackOff 0 8m 10.128.0.45 node1.lab.example.com

hello-1-deploy 1/1 Running 0 8m 10.129.0.72 node2.lab.example.com

[root@node1 ~]# docker pull registry.lab.example.com/node-hello #手動拉去也失敗

Using default tag: latest

Trying to pull repository registry.lab.example.com/node-hello …

All endpoints blocked.

結論:由上可知,所有endpoint都被阻塞了。這種類型的錯誤通常發生在OpenShift中,原因是不正確的部署配置或無效docker配置。

7.10 修正docker配置


[root@node1 ~]# vi /etc/sysconfig/docker

將BLOCK_REGISTRY=’–block-registry registry.access.redhat.com –block-registry docker.io –block-registry registry.

lab.example.com’

修改為

BLOCK_REGISTRY=’–block-registry registry.access.redhat.com –block-registry docker.io’

[root@node1 ~]# systemctl restart docker

提示:node2也需要如上操作。

7.11 更新pod


[student@workstation ~]$ oc rollout latest hello

[student@workstation ~]$ oc get pods #確認

NAME READY STATUS RESTARTS AGE

hello-1-deploy 0/1 Error 0 22m

hello-2-75x9t 1/1 Running 0 47s

7.12 確認驗證


[student@workstation ~]$ oc logs hello-2-75x9t #查看log

nodejs server running on http://0.0.0.0:3000

7.13 暴露服務


[student@workstation ~]$ oc expose svc hello –hostname=hello.apps.lab.example.com

route “hello” exposed

7.14 測試服務


[student@workstation ~]$ curl http://hello.apps.lab.example.com

Hi! I am running on host -> hello-2-75x9t

[student@workstation ~]$ lab execute-review grade #腳本驗證試驗

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

綠色運輸衝一波 城市的規劃與挑戰

文:詹詒絜(台達電子文教基金會高級專員)

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

【其他文章推薦】

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

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

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

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

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

聯合國專家:防疫措施恐釀全球缺糧

摘錄自2020年03月26日自由時報報導

武漢肺炎讓全球各地都有民眾因恐慌而瘋狂囤積生活用品、糧食,聯合國糧農組織(FAO)首席經濟學家托雷羅(Maximo Torero)警告,各國實施的防疫措施,恐怕會導致全球糧食短缺。

綜合外電報導,各國為確保物資充足,紛紛祭出提高關稅、限制出口、禁止人口移動等措施。托雷羅表示,從2007年的糧食危機就有觀察到,貿易壁壘會造成價格波動,進而使狀況惡化。雖然目前全球的糧食狀況看似很好,但幾週內糧食問題就會逐漸浮現,並隨著蔬果進入收成季節,於兩個月內惡化。

限制人口流動雖然能有效防止疫情傳播,但邁入收成季的蔬果園也將難以招募到熟練的臨時工,許多作物可能來不及在腐壞前收成。托雷羅也建議消費者,可以透過避免恐慌搶購、囤貨、浪費物資,緩解防疫措施導致的糧食問題。

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

報告指亞馬遜雨林去年火災次數飆升三成

摘錄自2020年1月9日星島日報報導

巴西國家太空研究院(INPE)周三發表一份報告顯示,2019年全年在南美洲亞馬遜雨林內發生的火災,比對上一年大幅上升30%。

根據 INPE 提供的數據,在2019年,在亞馬遜地區偵測到的火災多達8萬9178宗,比2018年的6萬8345宗大幅增加。不過,這個數字仍然低於10萬9630宗的歷史性平均紀錄。

亞馬遜森林去年發生的特大山火,引起國際社會廣泛關注。巴西總統博爾索納羅的政策和立場受到各方抨擊,指他上台後只顧經濟利益,把環保及全球氣候暖化的問題置之不理。巴西在2019年斬伐林木及開墾土地的數字,創下10年來新高。如何保護亞馬遜森林成為一個緊急議題。

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

【其他文章推薦】

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

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

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

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

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

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

英國新創開發藻類塗層 打造會行光合作用的衣服

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

【其他文章推薦】

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

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

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

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

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

Quartz.Net系列(七):Trigger之SimpleScheduleBuilder詳解

所有方法圖

 

 SimpleScheduleBuilder方法

RepeatForever:指定觸發器將無限期重複。

WithRepeatCount:指定重複次數

var trigger = TriggerBuilder.Create().WithSimpleSchedule(s=>s.WithIntervalInSeconds(1).RepeatForever()).Build();

 

            var trigger = TriggerBuilder.Create().WithSimpleSchedule(s=>s.WithIntervalInSeconds(1)
                                                                         .WithRepeatCount(10)).Build();

 

注:底層實現是repeatCount+1,也就是總共執行repeatCount+1次

        /// <summary>
        /// Specify a the number of time the trigger will repeat - total number of
        /// firings will be this number + 1.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="repeatCount">the number of seconds at which the trigger should repeat.</param>
        /// <returns>the updated SimpleScheduleBuilder</returns>
        /// <seealso cref="ISimpleTrigger.RepeatCount" />
        /// <seealso cref="RepeatForever" />
        public SimpleScheduleBuilder WithRepeatCount(int repeatCount)
        {
            this.repeatCount = repeatCount;
            return this;
        }

 

 

WithInterval:以毫秒為單位指定重複間隔,由於是TimeSpan也可以指定時分秒

WithIntervalInHours:以小時為單位指定重複間隔

WithIntervalInMinutes:以分鐘單位指定重複間隔

WithIntervalInSeconds:以秒為單位指定重複間隔

            var trigger = TriggerBuilder.Create().WithSimpleSchedule(s=>s .WithIntervalInSeconds(1)
                                                                          .WithInterval(TimeSpan.FromDays(1))
                                                                          .WithIntervalInMinutes(1)
                                                                          .WithIntervalInHours(1)
                                                                          .WithRepeatCount(5))
                                                 .Build();

注:底層都是通過WithInterval實現的

        /// <summary>
        /// Specify a repeat interval in milliseconds.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="timeSpan">the time span at which the trigger should repeat.</param>
        /// <returns>the updated SimpleScheduleBuilder</returns>
        /// <seealso cref="ISimpleTrigger.RepeatInterval" />
        /// <seealso cref="WithRepeatCount(int)" />
        public SimpleScheduleBuilder WithInterval(TimeSpan timeSpan)
        {
            interval = timeSpan;
            return this;
        }

        /// <summary>
        /// Specify a repeat interval in seconds.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="seconds">the time span at which the trigger should repeat.</param>
        /// <returns>the updated SimpleScheduleBuilder</returns>
        /// <seealso cref="ISimpleTrigger.RepeatInterval" />
        /// <seealso cref="WithRepeatCount(int)" />
        public SimpleScheduleBuilder WithIntervalInSeconds(int seconds)
        {
            return WithInterval(TimeSpan.FromSeconds(seconds));
        }

靜態方法:

RepeatMinutelyForever

RepeatMinutelyForTotalCount

RepeatSecondlyForever

RepeatSecondlyForTotalCount

RepeatHourlyForever

RepeatHourlyForTotalCount

var trigger = TriggerBuilder.Create().WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForTotalCount(2)).Build();

 

 /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with a 1 minute interval.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForever()
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(1))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with an interval
        /// of the given number of minutes.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForever(int minutes)
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(minutes))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with a 1 second interval.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForever()
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(1))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with an interval
        /// of the given number of seconds.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForever(int seconds)
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(seconds))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with a 1 hour interval.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForever()
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(1))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with an interval
        /// of the given number of hours.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForever(int hours)
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(hours))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with a 1 minute interval.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForTotalCount(int count)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(1))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with an interval of the given number of minutes.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForTotalCount(int count, int minutes)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(minutes))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with a 1 second interval.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForTotalCount(int count)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(1))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with an interval of the given number of seconds.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForTotalCount(int count, int seconds)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(seconds))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with a 1 hour interval.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForTotalCount(int count)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(1))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with an interval of the given number of hours.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForTotalCount(int count, int hours)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(hours))
                .WithRepeatCount(count - 1);

            return sb;
        }

 

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

【其他文章推薦】

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

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

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

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

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

Day8-微信小程序實戰-交友小程序-首頁用戶列表渲染及多賬號調試及其點贊功能的實現

在這之前已經把編輯個人的所有信息的功能已經完成了

之後先對首頁的列表搞動態的,之前都是寫死的靜態

1、之前都是把好友寫死的,現在就在js裏面定義一個數組,用循環來動態的綁定

在onReady中定義,取真實的數據給定義的列表數組list  

通過調用  db.collection(‘users’).get()  這裏沒有加其他的限制,得到的就是所有的數據了,拿到全部的數據之後就會觸發then方法了

用then返回的res中有一個data的列表集合,有一個注意的點就是,這樣子讀取是吧數據庫中的數據的全部字段,但是我們需要用的只是

用戶的頭像和點贊數、用戶昵稱,其他的數據其實是不需要的

可以加一個field方法,可以要求返回的字段是哪些的

 

可以看到是只有一個用戶,我們為了模擬的話,就可以多賬號進行調試,

 

 1、創立多賬號

    ①首先這個多賬號的一定要是測試號,所以先進入微信的管理後台 https://mp.weixin.qq.com/

    ②進入 【成員管理】添加項目成員

    ③回到微信開發者工具中-》工具-》多賬號調試-》可以通過添加虛擬測試號來進行測試的,不用真實的微信號都可以

 

之後就是對點贊的功能進行設計了(就可以在index.wxml中給點贊的小心上加一個點擊事件即可了

(小細節在小程序中規定,在客戶端中讀取用戶列表的時候,一般不會把整個數據庫的用戶都讀取出來的,是有一個限制的

一般都是把前二十條數據給讀取出來的,如果數據一多的話,一般都是進行分頁處理了–一般都是用數據庫中的collection.skip和collection.limit,這兩個東西一起配合的話就可以做下拉加載的功能了)

由於這個點贊是要對找到用戶的id地址的,所以在wxml中給點贊這個圖標加上一個id自定義屬性的掛載

 

data-id="{{ item._id}}"

這個東西的用處就是,在點擊這個心心的時候就可以拿到這個自定義的屬性id了  

通過把handleLinks(ev)函數中把ev打印出來發現,這個自定義屬性id 的位置在

 ev.target.dataset.id

console.log就是用來測試的,如果沒效果的話,一般都是直接在點擊事件後面通過promise 的then把res打印出來看看情況是怎麼樣的

***有時候這些點擊事件無法觸發的話,就可以在檢查一下樣式,可能這個區域在前端显示是在這裏,但是實際上是在其他的地方,

 

 

 也就是布局引起的問題了

 

 

 

 可以看到這裏就是碰到的是點贊的這個圖標,但是這個樣式是在左上角進行了渲染的

這個時候為了演示把,就把自定義屬性,和點擊事件放在點贊圖標還有點贊數包含的這個text裏面了

<text bindtap="handleLinks" data-id="{{ item._id}}" >
          <!-- 點贊圖標 -->
              <text class="iconfont icondianzan"
              ></text>
          <!-- 點贊數 -->
              <text>{{ item.links }}</text>
          </text>

改完之後,再點擊心心或者是數量,就可以正常的進行點贊了

  但是發現只有給自己點贊的時候才可以改變點贊的數量,而改其他人點贊的時候就改不了

比如:

 

 

 這就是一個權限的問題了也就是只能改自己的數據(點贊數)改不了別人的數據l

 

 

因為在小程序端中,由於用戶可以直接對數據庫進行操作,所以會有一定的風險,所以就通過這個訪問權限來進行了限制

那?

怎麼修改別人的數據呢?—這個操作就要在服務端來進行操作了!  也就是在雲函數中去完成一個雲函數的操作了

 

下面就是講解 如何在 服務端來對数字字段來進行更改!

 

二、點贊功能實現與update雲函數

 由於要在服務端來做的話,就可以把這一塊的部分代碼刪掉了

  handleLinks(ev){
    let id = ev.target.dataset.id;
    db.collection('users').doc(id).update({
      data : {
        links : 5
      }
    }).then((res)=>{
      console.log(res)
    }); 
  }

需要新創一個雲函數(新建一個node.js雲函數

 

這些默認的結構都是可以刪掉的了

 

 之後就是參考 微信開放文檔 雲開發-》SDK文檔->數據庫-》collection->update-》示例代碼demo

其中:這個就是指定了數據庫的環境

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

然後就是在服務端拿到數據庫db

const db = cloud.database()

然後可以在示例代碼中看到async這個異步的操作

直接複製 try catch

try {
    return await db.collection('todos').where({
      done: false
    })
    .update({
      data: {
        progress: _.inc(10)
      },
    })
  } catch(e) {
    console.error(e)
  }

return就是返回一個異步的數據,而catch就是返回錯誤信息

主要修改的就是 db.collecion()中要反問那個數據庫表單,這裏不能寫死為users,因為可能其他的地方也是要用到更新的這個功能的

所以最好就是把update這個雲函數寫成一個通用的方式

其中雲函數入口函數 

exports.main = async (event, context) => {  這個裡面的event就是前端傳參過來的對象了 就訪問 event.colletion,然後不用where而是用doc 再把event的doc也傳進來,這 ***小知識點:ES6的擴展運算符 。。。 (三個點)

更新成功了之後,就可以返回結果到前台了

這個就是update雲函數中js文件的代碼(傳進去的doc實際上是用戶的_id

 // 雲函數入口文件
const cloud = require('wx-server-sdk')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
// 雲函數入口函數
exports.main = async (event, context) => {
  try {
    return await db.collection(event.collection).doc(event.doc)
      .update({
        data: {
         ...event.data 
        },
      })
  } catch (e) {
    console.error(e)
  }
}

View Code

把雲函數寫好之後,這個時候雲函數還是在本地,要把這個雲函數傳到雲開發平台上

 

上傳了之後一定要去雲平台-》雲函數中去檢查一下

 

之後就可以調用這個雲函數了

再回到index.js 點贊的方法 handleLinkes方法中進行設置即可;

 

 如果雲函數沒問題,可能是雲函數裏面定義的env出了問題,就可以寫死了,傳入自己的那個環境,就不用默認的那個環境了

【注意】修改了雲函數記得要重新上傳到雲平台才行

 

 

 

 (因為在服務端是不會受到數據庫權限的限制的)

後面要優化的就是(上面的點贊是寫死給多少links的,並且不能點完之後立馬更新

可以把數據庫的links讀出來,+1之後再寫入,但是這樣的話就多了一個數據庫的操縱了  ,但是數據庫本身就提供了累加或者累減等運算的操作的

(這樣的話就只需要一次的數據庫讀取即可了)

在開放文檔 db.command裏面就有很多的方法

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/command/Command.inc.html

示例代碼
將一個 todo 的進度自增 10:

const _ = db.command
db.collection('todos').doc('todo-id').update({
  data: {
    progress: _.inc(10)
  }
})

為了在服務端不把運算給寫死了,一般都是把運算直接通過前端來傳入的

由於前端不認識下劃線這種操作,因為前端要先解析,之後再把東西傳到服務端的,所以就可以在前端給服務端傳一個字符串的話

然後再在服務端解析即可

 data : "{links : _.inc(1)}"

在前端把這個字符串傳入到服務端中,之後就可以在服務端那邊對這個字符串進行解析了

所以在update雲函數中 就要對event.data這個傳過來的數據進行判斷,判斷它的類型,是普通的還是字符串類型的,如果是字符串類型的話就要進行解析

用js裏面的eval方法,它是把字符串轉成 js 語句的

 if(typeof event.data == 'string'){
      event.data =  eval('(' + event.data + ')')
    }

即可了(點一下心心就可以讓點贊數+1)

後面有空的話,可以繼續進行優化,也就是對一次的點贊數進行限制,或者是點一下加+1,再點一下就是取消了就-1了

後面就是要把點贊了之後,實時的把點贊數進行更新

 

可以看到給服務端那邊上傳之後,對數據庫進行了更新之後,then返回的結果res,中有一個是updated==1,就可以進行if判斷了

再用for循環對列表中的每一個元素判斷,是不是現在被點擊點贊的這個id

let updated = res.result.stats.updated;
    if(updated){
      // 先用擴展運算符 克隆一份數組
      let cloneListData = [...this.data.listData];
      for(let i = 0;i < cloneListData.length ; i++){
        if( cloneListData[i]._id == id){ 
          cloneListData[i].links++;
        }
      }
      this.setData({
        listData : cloneListData
      });
    }

點贊數增加 就是通過_inc 但是在服務端中的update函數中是用全局的,不能寫死,所以運算的規則就通過前端傳過去

為了以後其他的頁面也有更新功能的話做準備了

 

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

一起玩轉微服務(7)——單一職責

 

單一職責

單一職責原則(Single Responsibility Principle, SRP):一個類只負責一個功能領域中的相應職責,或者可以定義為:就一個類而言,應該只有一個引起它變化的原因。

單一職責原則是實現高內聚、低耦合的指導方針,它是最簡單但又最難運用的原則

單一職責原則是最簡單的面向對象設計原則,它用於控制類的粒度大小

設計原則很重要的一點就是簡單,單一職責,也就是我們經常所說的專人干專事。

一個單元(一個類、函數或者微服務)應該有且只有一個職責。無論如何,一個微服務不應該包含多於一個的職責。職責單一的後果之一就是職責單位(微服務,類,接口,函數)的數量劇增。據說Amazon,Netflix這些採用微服務架構的網站一個小功能就會調用幾十上百個微服務。但是相較於每個函數都是多個業務邏輯或職責功能的混合體的情況,維護成本還是低很多的。 SRP中的“單一職責”是個比較模糊的概念。對於函數,它可能指單一的功能,不涉及複雜邏輯;但對於類或者接口,它可能是指對單一對象的操作,也可能是指對該對象單一屬性的操作。總而言之,單一職責原則就是為了讓代碼邏輯更加清晰,可維護性更好,定位問題更快的一種設計原則。

什麼是高內聚低耦合?

這犀利的措辭一看就是來自開發界的術語。高內聚是說一個功能模塊最好僅完成一個獨立的子功能並且完成的很好。低耦合是指模塊與模塊之間盡量獨立/聯繫少/接口簡單。

這個原則出現的背景是為了讓程序“可復用/可擴展/夠靈活/可維護”。干過一陣子產品的人對這幾個詞應該都不陌生。對於程序設計者來說,這幾個詞是十分重要的,不亞於產品經理口中的“用戶體驗”(原則or擋箭牌)。

優點

單一職責的優點如下:

•類的複雜性降低,實現什麼職責都有清晰明確的定義。•可讀性提高,複雜性降低。•可維護性提高,可讀性提高。•變更引起的風險降低,變更是必不可少的,如果接口的單一職責做得好,一個接口修改只對相應的實現類有影響,對其他的接口無影響,這對系統的擴展性、維護性都有非常大的幫助。

記得在三字經裡邊有這樣一段 教之道,貴以專(出自三字經) 說的就是無論學習還是構建團隊,最重要的是專才,而不是全才。就好比一個足球隊,如果都是前鋒或者都是後衛,那麼這樣的球隊一定不能出成績,反而是將各個位置上的人進行統一協調,根據分工不同,共同協作,形成1+1>2的效果,那麼這樣的團隊就非常容易出成績。

有很多公司為了趕進度,經常會招聘一些所謂的全能型人才,但是這種人往往專業的程度不夠,當遇到某些棘手的問題的時候,往往不能夠非常快速的解決問題。從而導致最終交付的質量較差。

單一職責的目的

實施單一職責的目的如下:

•以類來隔離需求功能點,這樣當一個點的需求發生變化的時候,不會影響別的類的邏輯,這個和設計模式中的開閉原則類似,對於擴展持開放態度,對於修改持關閉態度。•是一個原子模塊級的粒度,至於原子的粒度到底是什麼樣的,應該因業務而異,設計的過程中同時考慮業務的擴展,所以這就要求在設計的過程中,必須有業務專家共同參与,共同規避風險。•粒度小,靈活,復用性強,方便更高一級別的抽象。

每個微服務單獨運行在獨立的進程中,能夠實現松耦合,並且獨立部署。

如何做

分3步:

1.把一個具體的問題抽象成一類問題;

2.根據用戶體驗流程劃分功能模塊;

3.針對每個功能設計封閉的解決方案。

最佳實踐

在實際工作中,有一個經常會用到的設計模式,DAO模式,又叫數據訪問對象,裏面定義了數據庫中表的增、刪、改、查操作,按照單一職責原則,為什麼不把增、刪、改、查分別定義成四種接口?這是因為數據庫的表操作,基本上就是這四種類型,不可能變化,所以沒有必要分開定義,反而經常變化的是數據庫的表結構,表結構一變,這四種操作都要跟着變。所以通常我們會針對一張表實現一個DAO,一張表就代表一種類型的職責。

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

【其他文章推薦】

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

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

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

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

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

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

鍵盤俠Linux教程(五)| 基本權限管理

基本權限管理

權限的介紹

權限位的含義

前面講解ls命令時,我們已經知道長格式显示的第一列就是文件的權限,例如:

[root@es ~]# ls -l anaconda-ks.cfg
-rw-------. 1 root root 1573 May 18 23:28 anaconda-ks.cfg

第一位為文件類型

文件類型標識 文件類型
普通文件
d 目錄文件
l 軟鏈接文件
s(偽文件) 套接字文件
b(偽文件) 塊設備文件
c(偽文件) 字符設備文件
p(偽文件) 管道符文件

第一列的權限位如果不計算最後的 “.” (這個點的含義我們在後面解釋),則共有10位,這10位權限位的含義如圖所示。

基本權限介紹

修改權限的命令 chmod ,基本信息如下

命令格式

[root@es ~]#chmod [選項] 權限模式 文件名

選項:

-R : 遞歸設置權限,也就是給子目錄中的所有文件設定權限

權限模式

chmod 命令的權限模式的格式時”[ugoa][[+-=][perms]]”,也就是“[用戶身份][[賦予方式][權限]]”的格式

  • 用戶身份:

    • u:代表所有者(user)

    • g:代表所屬組(group)

    • o:代表其他人(other)

    • a:代表全部身份(all)

  • 賦予方式:

    • +:加入權限

    • -:減去權限

    • =:設置權限

  • 權限:

    • -r:讀取權限(read)

    • -w:寫權限(write)

    • -x:執行權限(execute)

数字權限

数字權限的賦予方式是最簡單的,但是不如之前的字母權限好記,直觀。我們來看看這些数字權限的含義。

  • 4:代表”r”權限

  • 2:代表”w”權限

  • 1:代表”x”權限

常用權限

数字權限的賦予方式更加簡單,但是需要用戶對這幾個数字更加熟悉。其實常用權限也並不多,只有如下幾個。

  • 644:這是文件的基本權限,代表所有者擁有讀,寫權限,而所屬組和其他人擁有隻讀權限。

  • 755:這是文件的執行權限和目錄的基本權限,代表所有者擁有讀,寫和執行權限,而所屬組和其他人擁有讀和執行權限。

  • 777:這時最大權限。在實際的生產服務器中,要儘力避免給文件或目錄賦予這樣的權限,這會造成一定的安全隱患。

基本權限的作用

權限含義的解釋

首先,讀,寫,執行權限對文件和目錄的作用是不同的。

  • 權限對文件的作用

    • 讀(r):對文件有讀(r)權限,代表可以讀取文件中的數據。如果把權限對應到命令上,那麼一旦對文件有讀(r)權限,就可以對文件執行cat,more,less,head,tail等文件查看命令。

    • 寫(w):對文件有寫(w)權限,代表可以修改文件中的數據。如果把權限對應到命令上,那麼一旦對文件有寫(w)權限,就可以對文件執行vim,echo等修改文件數據的命令。注意:對文件有寫權限,是不能刪除文件本身的,只能修改文件中的數據。如果要想刪除文件,則需要對文件的上級目錄擁有寫權限。

    • 執行(x):對文件有執行(x)權限,代表文件擁有了執行權限,可以運行。在Linux中,只要文件有執行(x)權限,這個文件就是執行文件了。只是這個文件到底能不能正確執行,不僅需要執行(x)權限,還要看文件中代碼是不是正確的語言代碼。文件夾來說,執行(x)權限是最高權限。

  • 權限對目錄的作用

    • 讀(r):對目錄有讀(r)權限,代表可以查看目錄下的內容,也就是可以查看目錄下有哪些子文件和子目錄。如果把權限對應到命令上,那麼一旦對目錄擁有了讀(r)權限,就可以在目錄下執行ls命令,查看目錄下的內容

    • 寫(w):對目錄有寫(r)權限,代表可以修改目錄下的數據,也就是可以在目錄中新建,刪除,複製,剪切子文件或子目錄。如果把權限對應到命令上,那麼一旦目錄擁有了寫(w)權限,就可以在目錄下執行touch,rm,cp,mv命令。對目錄來說寫(w)權限是最高權限。

    • 執行(x):目錄是不能運行的,那麼對目錄擁有執行(x)權限,代表可以進入到目錄。如果把權限對應到命令上,那麼一旦對目錄擁有了執行(x)權限,就可以對目錄執行cd命令,進入目錄。

目錄的可用權限

目錄的可用權限其實有以下幾個

  • 0:任何權限都不賦予

  • 5:基本的目錄瀏覽和進入權限

  • 7:完全權限

所有者和所屬組命令

chown命令

修改權限的命令 chown ,基本信息如下

[root@es ~]#chown [選項] 所有者:所屬組 文件或目錄

選項:

-R : 遞歸設置權限,也就是給子目錄中的所有文件設定權限

普通用戶不能修改文件的所有者,哪怕自己是這個文件的所有者也不行,只有超級用戶才能修改
普通用戶可以修改所有者是自己的文件的權限=

umask 默認權限

查看系統的umask權限

#用八進制數值显示umask權限
[root@centos7 ~]# umask
0022

#用字母表示文件和目錄的初始權限
[root@centos7 ~]# umask -S
u=rwx,g=rx,o=rx

umask權限的計算方法

我們需要先了解一下新建文件和目錄的默認最大權限

  • 對文件來講,新建文件的默認最大權限是666,沒有執行(x)權限。這時因為執行權限對文件來講比較危險,不能在新建文件的時候默認賦予,而必須通過用戶手工賦予。

  • 文件的默認權限最大隻能是666,而umask的值是022
    “-rw-rw-rw-“減去”—–w–w-” 等於”-rw-r–r–“

  • 對目錄來講,新建文件的默認最大權限是777。這時因為對目錄而言,執行(x)權限僅僅代表進入目錄,所以即使建立新文件時直接默認賦予,也沒有什麼危險。

  • 目錄的權人權限最大隻能是777,而umask的值是022

“drwxrwxrwx” 減去 “d—-w–w-” 等於 “drwx-r-xr-x”

寫在最後

如果文檔對你有幫助的話,留個贊再走吧 ,你的點擊是我的最大動力。

我是鍵盤俠,現實中我唯唯諾諾,網絡上我重拳出擊,關注我,持續更新Linux乾貨教程。

更多鍵盤俠Linux系列教程:鏈接地址

更多Linux乾貨教程請掃:

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

【其他文章推薦】

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

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

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

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

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

教你一招另闢蹊徑抓取美團火鍋數據

最近有個小夥伴在群里問美團數據怎麼獲取,而且她只要火鍋數據,她在上海,只要求抓上海美團火鍋的數據,而且要求也不高,只要100條,想做個簡單的分析,相關的字段如下圖所示。

乍一看,這個問題還真的是蠻難的,畢竟美團也不是那麼好抓,什麼驗證碼,模擬登陸等一大堆拂面而來,嚇得小夥伴都倒地了。

通過F12查看,抓包,分析URL,找規律,等等操作。

不過白慌,今天小編給大家介紹一個小技巧,另闢蹊徑去搞定美團的數據,這裏需要用到抓包工具Fiddler。講道理,之前我開始接觸網絡爬蟲的時候也沒有聽過這個東東,後來就慢慢知道了,而且它真的蠻實用的,建議大家都能學會用它。這個工具專門用於抓包,而且其安裝包也非常小,如下圖所示。

接下來,我們開始進行抓取信息。

1、在Fiddler的左側找到meituan網站的鏈接,如下圖所示。鏈接的左邊返回的response(響應)的文件類型,可以看到是JSON文件,爾後雙擊這一行鏈接。

2、此時在右側會显示下圖的界面,點擊黃色區域內的那串英文“Responsebody is encoded. Click to decode.”意思是response是加密的,點擊此處進行解碼,對返回的網頁進行解碼。

3、此時會彈出下圖所示的界面,在WebView中可以看到返回的數據,與網頁中的內容對應一致。

4、不過美團網限制一頁最多显示32條火鍋信息,如下圖所示。

5、如果我想獲取100條信息的話,那得前後找4頁,才能夠滿足要求。有沒有辦法讓其一次性多显示一些數據呢?答案是可以的,操作方法如下。

在左側找到對應的美團網鏈接,然後點擊右鍵一次選擇CopyàJustUrl,如下圖所示。

7、將得到的URL放到瀏覽器中去進行訪問,如下圖所示。可以看到limit=32,即代表可以獲取到32條相關的火鍋信息,並且返回的內容和Fiddler抓包工具返回的信息是一致的。

8、此時,我們直接在瀏覽器中將limit=32這個參數改為limit=100,也就是說將32更改為100,讓其一次性返回100條火鍋數據,天助我也,竟然可以一次性訪問到,如下圖所示。就這樣,輕輕鬆松的拿到了一百條數據。

9、接下來,可以將瀏覽器返回的數據進行Ctrl+A全部選中,放到一個本地文件中去,存為txt格式,在sublime中打開,如下圖所示。

10、其實乍一看覺得很亂,其實它就是一個JSON文件,剩下的工作就是對這個JSON文件做字符串的提取,寫個代碼,提取我們的目標信息,包括店門、星級、評論數、關鍵詞、地址、人均消費等,如下圖所示。

11、運行程序之後,我們會得到一個txt文件,列與列之間以製表符分開,如下圖所示。

12、在txt文件中看上去很是費勁,將其導入到Excel文件中去,就清晰多了,如下圖所示。接下來就可以很方便的對數據做分析什麼的了。

13、至此,抓取美團火鍋數據的簡易方法就介紹到這裏了,希望小夥伴們都可以學會,以後抓取類似的數據就不用找他人幫你寫程序啦~~

14、關於本文涉及的部分代碼,小編已經上傳到github了,後台回復【美團火鍋】四個字即可獲取。

看完本文有收穫?請轉發分享給更多的人

IT共享之家

入群請在微信後台回復【入群】

想學習更多Python網絡爬蟲與數據挖掘知識,可前往專業網站:http://pdcfighting.com/

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

【其他文章推薦】

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

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

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

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

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