移動式水質感測器,今後將可助環保稽查人員沿河流溯源污水工廠

摘錄自2019年12月2日台灣醒報報導

移動式水質感測器,今後將可助環保稽查人員沿河流溯源污水工廠!環保署2日介紹署內「環境及資訊處」今年新投入應用的四項「環境物聯網感測器」,包括與氣象局合作開發的「大氣剖面監測無人機」等。環保署表示,今年物聯網感測器在「空氣、水體、噪音領域」有大突破,將持續與工研院合作應用物聯網技術,嚴密把關污染。

環境監測及資訊處長張順欽處介紹四項新投入環境監測的環境物聯網感測器:

  1. 以「無人機」結合「感測器」的大氣剖面監測無人機,可探測「高空污染物分佈」,並解析其對地面的影響。
  2. 搭載在汽車上的「移動空汙偵測器」,可利用影像辨識技術,分析行車紀錄器影像中的PM2.5空品數據,掌握都市中的空污時空變化。
  3. 用於測工廠水體污染的「水質感測器」分為移動式與浮動式,移動用於監測具有多匯流口的高污染潛勢河川(如桃園市老街溪),它流經廢水排放口後,會出現明顯的數據擾動,方便環保稽查人員進行溯源。全台已佈建150台的浮動式水質感測器,則針對4縣市的工業區、農畜業與砂石場,進行22個水域全時監控。
  4. 「空噪感測器」則像攝影機一樣,架設在都市馬路上,以「噪音陣列麥克風」準確定位噪音源,並結合車牌辨識系統,準確抓到噪音車。

「今年環保署利用新型感測器,充分發揮小兵立大功,讓許多違規工廠無所遁形!」環保署表示,中央與地方環保機關目前在全台已佈建6000個「空污感測器」,涵蓋55個主要工業區、169個重大交通幹道、監控工廠超過三萬家,至今已裁處罰鍰共計8646萬元,「空污費追繳」則達四億元以上。

張順欽說:「這些新設備已陸續有各縣市環保局提出申請,預估明年會再上路一批。」

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

【其他文章推薦】

※使用真空封口機常見問題?

※高價位跟低價位的示波器又有何差異?

※各大百貨每波促銷贈品活動,限量知名LOGOL型資料夾,獨家販售中!!

※哪裡有合版印刷優惠,尋找L夾客製化印刷廠商?

※如何正確使用開飲機?

洗滌塔運作原理介紹

高市府同意將垃圾代燒量由每天50噸提高至80噸,舒緩澎湖紅羅轉運站的壓力

摘錄自2019年12月3日中央通訊社報導

澎湖垃圾委由高雄市政府協助代燒,經澎湖縣政府多次協商,高市府同意將垃圾代燒量由每天50噸提高至80噸,舒緩澎湖紅羅轉運站的壓力。

澎湖縣長賴峰偉今天(3日)率環保局長陳高樑等主管,陪同陳振中、許育愷等議員考察湖西鄉地方建設。縣府將通盤考量多項建議,與議員共同合作,促進地方發展。

陳高樑表示,目前紅羅轉運站累積垃圾量已達5350公噸,高雄市之前每天協助代燒30至50噸垃圾,經多次向高市府協調反映,同意11月份起提高至每天80公噸代燒量,可舒緩紅羅轉運站壓力。

另外,紅羅轉運站堆置大量垃圾,導致垃圾被東北季風吹得滿天飛,環保局也在轉運站南面擋土牆架設3米高防風網擋土牆工程,防止垃圾飛散入海。

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

【其他文章推薦】

※一般小吃店常見使用的nbr耐油手套,是否有含耐酸鹼作用?

迴轉式空壓機性能介紹 !

海線倉儲類人員薪水待遇最新情報

※(全省)堆高機租賃保養一覽表

※高效率洗滌塔活性碳設備,能去除多少有機溶劑?

射出成型技師工作甘苦談

etcd-operator快速入門完全教程

Operator是指一類基於Kubernetes自定義資源對象(CRD)和控制器(Controller)的雲原生拓展服務,其中CRD定義了每個operator所創建和管理的自定義資源對象,Controller則包含了管理這些對象所相關的運維邏輯代碼。

對於普通用戶來說,如果要在k8s集群中部署一個高可用的etcd集群,那麼不僅要了解其相關的配置,同時又需要特定的etcd專業知識才能完成維護仲裁,重新配置集群成員,創建備份,處理災難恢復等等繁瑣的事件。

而在operator這一類拓展服務的協助下,我們就可以使用簡單易懂的YAML文件(同理參考Deployment)來聲明式的配置,創建和管理我們的etcd集群,下面我們就來一同了解下etcd-operator這個服務的架構以及它所包含的一些功能。

目 標

  1. 了解etcd-operator的架構與CRD資源對象

  2. 部署etcd-operator

  3. 使用etcd-operator創建etcd cluster

  4. 基於etcd-operator備份和恢復etcd cluster

服務架構

etcd-operator的設計是基於k8s的API Extension機制來進行拓展的,它為用戶設計了一個類似於Deployment的Controller,只不過這個Controller是用來專門管理etcd這一服務的。

用戶默認還是通過kubectl或UI來與k8s的API進行交互,只不過在這個k8s集群中多了一個用戶自定義的控制器(custom controller),operator controller的服務是以Pod的方式運行在k8s集群中的,同時這個服務也需要配置所需的RBAC權限(比如對Pod,Deployment,Volume等使用到的資源進行增刪改查的操作),下面我們用一個簡單的架構圖來進行闡述:

etcd-operator的自定義資源對象(CRD)

在k8s中,所有自定義的Controller和其自定義的資源對象(CRD)都必須滿足k8s API的規範(參考下圖):

  • apiVersion描述了當前自定義資源對象的版本號

  • Kind表示自定義資源對象的名稱,用戶可通過執行kubectl get $KIND_NAME來獲取所創建的CRD對象

  • Metadata繼承了原生k8s的metadata,用於添加標籤,Annotations等元數據

  • Spec是用戶可自定義設計的服務配置參數,如鏡像版本號,節點數量,資源配置等等..

  • Status包含了當前資源的的相關狀態,每個operator controller可自定義status所包含的信息,一般會選擇添加如conditions,updateTime和message等一類的信息。

下面先我們來了解一下etcd-operator所包含的幾個自定義資源對象(CRDs):

1、EtcdCluster: etcdcluster用來描述用戶自定義的etcd集群,可一鍵式部署和配置一個相關的etcd集群。

apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdCluster
metadata:
  name: etcd-cluster
spec:
  size: 3
  version: 3.2.25

2、EtcdBackup: etcdbackup用來描述和管理一個etcd集群的備份,當前支持定期備份到雲端存儲,如AWS s3, Aliyun oss(oss當前需使用quay.io/coreos/etcd-operator:dev鏡像)。


apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdBackup
metadata:
  name: etcd-backup
spec:
  etcdEndpoints: [<etcd-cluster-endpoints>]
  storageType: OSS #options are S3/ABS/GCS/OSS
  backupPolicy:
    backupIntervalInSecond: 125
    maxBackups: 4
  oss:
    #"<oss-bucket-name>/<path-to-backup-file>"
    path: <full-oss-path>
    ossSecret: <oss-secret>
    # Details about regions and endpoints, see https://www.alibabacloud.com/help/doc-detail/31837.htm
    endpoint: <endpoint> 

3、EtcdRestore:etcdrestore用來幫助將etcdbackup服務所創建的備份恢復到一個指定的etcd的集群。

apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdRestore
metadata:
  # name must be same to the spec.etcdCluster.name
  name: example-etcd-cluster
spec:
  etcdCluster:
    name: example-etcd-cluster
  backupStorageType: OSS
  oss:
    path: <full-oss-path> 
    ossSecret: <oss-secret>
    endpoint: <endpoint>

如何部署和使用etcd-operator

1、部署etcd-operator

在Rancher最新的stable v2.3.2 的版本中,用戶可通過應用商店(Catalog)來一鍵式部署 etcd-operator v0.9.0版本,同時原生k8s也可下載rancher/charts到本地后通過helm install的方式進行部署。

1)(可選)部署etcd-operator時可選擇同時創建一個etcd集群(此集群在etcd-operator被刪除時會被一同移除),當然用戶也可待etcd-operator部署完成通過kubectl apply -f myetcd.yaml來創建一個新的etcd集群。

2)部署時,如果用戶選擇啟動Enable Clusterwide of etcd Operator這個選項,那麼這個etcd-operator將作為集群層級對象來使用(否則為namespaced隔離),如果enable這個選項,那麼在創建etcd集群時需添加以下註釋才能創建創建:

kind: EtcdCluster
metadata:
  name: etcd-cluster
  # add this annotation when the clusterWide is enabled
  annotations:
    etcd.database.coreos.com/scope: clusterwide

2、創建etcd集群

接下來我們就可以使用上述的CRD自定義資源對象對來創建和管理我們的etcd集群了。

2.1 手動創建etcd集群

cat <<EOF | kubectl apply -f -
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdCluster
metadata:
  name: "etcd-cluster"
spec:
  size: 3 # 默認etcd節點數
  version: "3.2.25" # etcd版本號
EOF

2.2 部署后可通過CRD對象來查看我們創建的etcd集群和pod狀態

$ kubectl get etcdcluster
NAME            AGE
etcd-cluster    2m

$ kubectl get pod
NAME                     READY   STATUS  RESTARTS AGE
etcd-cluster-g28f552vvx  1/1   Running    0      2m
etcd-cluster-lpftgqngl8  1/1   Running    0      2m
etcd-cluster-sdpcfrtv99  1/1   Running    0      2m

2.3 可以往etcd集群任意的寫入幾條數據驗證etcd集群是正常工作的(後續也可用來驗證集群的備份和恢復功能)

$ kubectl get svc
NAME                  TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
etcd-cluster          ClusterIP   None           <none>        2379/TCP,2380/TCP   17h
etcd-cluster-client   ClusterIP   10.43.130.71   <none>        2379/TCP            17h
## write data
$ kubectl exec -it any-etcd-pod -- env "ETCDCTL_API=3" etcdctl --endpoints http://etcd-cluster-client:2379 put foo "Hello World"
## get data
$ kubectl exec -it any-etcd-pod -- env "ETCDCTL_API=3" etcdctl --endpoints http://etcd-cluster-client:2379 get foo
foo
Hello World

3、基於operator備份etcd cluster

3.1 確認了etcd集群正常運行后,作為devops後面要考慮的就是如何創建etcd集群的自動化備份,下面以阿里雲的OSS舉例:

cat <<EOF | kubectl apply -f -
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdBackup
metadata:
  name: example-etcd-cluster-periodic-backup
spec:
  etcdEndpoints: [http://etcd-cluster-client:2379] #內網可使用svc地址,外網可用NodePort或LB代理地址
  storageType: OSS
  backupPolicy:
    backupIntervalInSecond: 120 #備份時間間隔
    maxBackups: 4 #最大備份數
  oss:
    path: my-bucket/etcd.backup
    ossSecret: oss-secret #需預先創建oss secret
    endpoint: oss-cn-hangzhou.aliyuncs.com
EOF

3.2 若OSS Secret不存在,用戶可先手動創建,具體配置可參考如下:

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: oss-secret
type: Opaque
stringData:
  accessKeyID: myAccessKey
  accessKeySecret: mySecret
EOF

3.3 待etcdbackup創建成功后,用戶可以通過kubectl describe etcdbackup或查看etcd-backup controller日誌來查看備份狀態,如狀態显示為Succeeded: true,可以前往oss查看具體的備份內容。

4、基於operator恢復etcd cluster

最後,假設我們要將etcd集群A的備份數據恢復到另一個新的etcd集群B,那麼我們先手動創建一個名為etcd-cluster2的新集群(oss備份/恢復當前需使用quay.io/coreos/etcd-operator:dev鏡像)。

cat <<EOF | kubectl apply -f -
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdCluster
metadata:
  name: "etcd-cluster2"
spec:
  size: 3
  version: "3.2.25"
EOF

然後通過創建etcdresotre將備份數據恢復到etcd-cluster2集群


cat <<EOF | kubectl apply -f -
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdRestore
metadata:
  # name必須與下面的spec.etcdCluster.name保持一致
  name: etcd-cluster2
spec:
  etcdCluster:
    name: etcd-cluster2
  backupStorageType: OSS
  oss:
    path: my-bucket/etcd.backup_v1_2019-08-07-06:44:17
    ossSecret: oss-secret
    endpoint: oss-cn-hangzhou.aliyuncs.com
EOF

待etcdresotre對象創建成功后,可以查看etcd-operator-restore的日誌,大致內容如下:

$ kubectl logs -f etcd-operator-restore
...
time="2019-08-07T06:50:26Z" level=info msg="listening on 0.0.0.0:19999"
time="2019-08-07T06:50:26Z" level=info msg="starting restore controller" pkg=controller
time="2019-08-07T06:56:25Z" level=info msg="serving backup for restore CR etcd-cluster2"

通過kubectl查看pod我們可以看到etcd-cluster2集群的etcd節點被刪除重建:

NAME                       READY   STATUS    RESTARTS   AGE
etcd-cluster2-5tq2d5bvpf    0/1     Terminating   0      93s
etcd-cluster2-kfgvc692pp    1/1     Terminating   0      101s
etcd-cluster2-xqkgz8chb8    0/1     Init:1/3      0      6s
etcd-cluster2-pf2qxgtg9d    1/1     Running       0      48s
etcd-cluster2-x92l9vpx97    1/1     Running       0      40s

最後可通過etcdctl來驗證之前的數據是否存在(需設置ETCDCTL_API=3):

$ kubectl exec -it etcd-pod -- env "ETCDCTL_API=3" etcdctl --endpoints http://etcd-cluster2-client:2379 get foo
foo
Hello World

小 結

Etcd作為當前非常流行的key-value分佈式文件存儲,它本身的強一致性和較優的性能可以為許多分佈式計算解決分佈式存儲的需求,如果你的微服務和應用需要用到此類的數據庫,不妨來試試Rancher Catalog應用中的etcd-operator吧,Just do it!

相關資料:

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

思源:秒級體驗百億級數據量監控鑽取

編者薦語:

當業務量快速增長的時候,業務保障平台就要應運而生,預判問題發出告警,越快越好,從宏觀到微觀一路下鑽響應越快越好,尤其是交易量暴漲的高峰時段。怎麼做到?看思源的現身說法:

以下文章來源於雲縱達摩院 ,作者劉勤紅

 

——業務保障平台性能提升走過的那些路
禧雲信息/研發中心/劉勤紅(思源) 2019年11月
 
業務保障平台需從多維度去監控業務的可靠性,快速定位問題並自動解決或推薦出解決方案,所以時效性顯得非常重要。接下來我將從以下三個維度展開如何將百億級數據量監控鑽取從十幾秒提升到秒級體驗:
· 工具升級:OpenTSDB–>Druid
· 調用鏈收集的優化
· 聚合查詢的優化
 

一、背景回顧

· 禧雲的團餐業務發展非常迅速,短短几個月的時間,日交易量就從數百萬拉高到千萬級,隨着調用鏈追蹤得愈發細緻,業務數據量也從億級上升到百億級別。
· 團餐的高峰交易量比外賣更集中,外賣可以提前預訂,而團餐的早中晚就餐非常集中,全國的學生幾乎都是一個點兒下課,從下單到吃完也就集中在20-30分鐘內,下圖0為交易量的曲線圖,坡度幾乎是直線上升,真是爭分奪秒!

圖0 團餐高峰期爭分奪秒不容有失
在這種高強度業務場景下,業務保障平台應運而生,它主要是多場景多維度實時監控大盤,從設備終端到服務端全鏈路監控,讓技術團隊從事後追查和整改,轉變為事前預警和快速判定根因。它主要涉及交易、業務調用鏈路、網絡監控、設備運行指標、業務日誌等維度數據。架構如下圖1所示。
圖1 基於OpenTSDB版的大致架構
· 千萬級日交易數據:從各業務系統或支付網關迴流的交易數據—>交易迴流服務—>多維度聚合入庫OpenTSDB和ElasticSearch集群;
· 十億級業務調用鏈數據:基於Jaeger Trace植入–>Nginx多路分發–>經Jaeger-collector收集到Kafka–>被多組消費者處理(Flink實時寫入OpenTSDB,批量寫入ES);
· 百億級網絡和設備指標數據:主要是智能設備上報的各種監控指標:基於HTTP/HTTPS層監視–>Nginx–>太空橋(我司IoT平台)設備監控–>批量入庫ES。
技術棧為:
· 鏈路採集:Uber開源的Jaeger
· 基礎數據存儲:ES
· 指標數據存儲:OpenTSDB
· 削峰填谷的消息隊列:Kafka
· 實時計算:Flink  

二、工具升級:OpenTSDB升級到Druid

在禧雲業務保障平台上,OpenTSDB確實沒有太好的性能表現,查詢數據量在百萬級的時候,速度還是可以的,在暑假交易額低谷期並未暴露出OpenTSDB的性能問題。但是進入9月開學季之後,數據延時和查詢緩慢的問題立馬就暴露出來了,中途也寄希望於升級OpenTSDB版本,但依然效果不佳。
 

A. OpenTSDB性能表現不佳的原因

第一個原因,Rowkey設計過長的問題。Rowkey第一大設計原則是保證唯一性,否則原先的數據會被覆蓋掉,第二大設計原則是長度原則,Rowkey是一個二進制,它的長度建議設計在10~100個字節,越短越好。Rowkey如果過長,對性能有以下影響:
1)HBase的持久化文件HFile是按照KeyValue存儲的,如果Rowkey過長,比如500個字節,1000萬列數據,光Rowkey就要佔用500*1000萬=50億個字節,將近1G數據,這會極大影響HFile的存儲效率。
2)MemStore緩存部分數據到內存,如果Rowkey字段過長,內存的有效利用率會降低,系統無法緩存更多的數據,這會降低檢索效率。
需要指出的是不僅Rowkey的長度越短越好,列族名、列名等也盡量使用短名字,因為這些名字都是會寫入HFile中的,過長的Rowkey、列族名、列名都會導致整體的存儲量成倍增加。
 
第二個原因,業務監控業務場景非常多,涉及到不同業務線的多個維度,比如多機房、多支付渠道、商戶/門店/設備/碼等多維度聚合,很難設計出一個滿足全場景的Rowkey方案。在任意維度的組合查詢下,OpenTSDB 查詢效率會明顯降低。
比如對於組合條件查詢使用的就是scan方式,在使用時有以下幾點值得注意:
1)通過setCaching、setBatch方法提高速度,以空間換時間;
2)通過setStartRow與setEndRow來限定範圍,範圍越小,性能越高;
3)通過setFilter方法添加過濾器,這也是分頁、多條件查詢的基礎,比如使用SingleColumnValueFilter。
以上優化當遇到真正海量數據時,會消耗很大的資源,每次都需要花較長的時間處理。
 

B. 升級為Druid

我司其他技術團隊已經試用了Druid, 其核心是通過數據預先聚合提高查詢性能,針對預先定義好的Schema,因此適合實時分析的場景,結果返回時間在亞秒級。
我們切換到Druid之後,響應速度上確實有一個數量級的提升,查詢千萬級的數據範圍基本秒級響應。
 

三、調用鏈收集的優化

調用鏈收集的數據流為:jaeger-collector–>Kafka–>jaeger-ingester 消費–>入庫ES。下面講一下如何優化。
A. 第一階段:Kafka消息堆積高峰期由千萬級降到百萬級
強調一點,合理的分區設置很重要。
剛開始我們嘗試調整每次拉取的消息條數,將ingester.parallelism由1000調整為6000,消息堆積似乎好了一點,但效果不明顯。
考慮到可能是因為併發數不夠,所以通過擴充Kafka的分區數去提高併發。先將分區數由5個擴到8個,消息堆積由千萬級降到百萬級。但繼續將分區擴到10個,就幾乎沒有什麼效果了。  
B. 第二階段:Kafka的版本選擇不可忽視
莫名其妙的是jaeger-ingester消費非常不穩定,頻繁與kafka斷開重連,嘗試去消費其他分區消息,導致消費速率上不去。
後來發現,當Kafka版本為v2.3時,多個jaeger-ingester節點會反覆觸發kafka的消費再平衡機制,結果導致jaeger-ingester只能單點消費。
所以我們又將Kafka版本回退到了v2.2,調整jaeger-ingester實例個數和Kafka分區數為1:1,可橫向擴容支持高併發。
 
C. 第三階段:消息堆積高峰期由百萬降到萬級,延時秒級已可接受
我們觀察到ES的負載已經很高了,單節點高峰期CPU負載達到16。之前為了方便定位問題,給網絡請求實時加上了traceId標記,調用的是jaeger原生的trace鏈路計算。現在分析發現查詢QPS太高,所以嘗試優化查詢邏輯,一方面改為自定義的邏輯直接查詢ES,一方面調整好批量閾值查詢(指的是1次查多少,目前是按時間10ms和數據條數100條一個批次去查ES)。
優化完成后,消息堆積又下降一個數量級。目前高峰期堆積非常少,秒級消費已可接受。
 

四、聚合查詢的優化

A. 散點圖 vs 柱狀圖

對於調用鏈宏觀展示來說,慣常使用散點圖,它可以表達出一段時間內請求的耗時分佈情況,如下圖2所示。

圖2 調用鏈散點圖
但是存在一個問題,當時間區間選得比較大的時候,服務端查詢的數據過多而響應變慢,客戶端要渲染的點太多也會非常卡。所以點要抽樣。抽樣方式能想得到的有:
a.過濾出耗時長的數據;
b.取最近一段時間的數據;
c.只取指定數量的數據;
d.對相同耗時進行聚合展示等等。
這些調整可謂犧牲了監控者的真實需求,因為有些數據監控者看不了,或者很難看全。
我們對比了阿里雲日誌服務的設計,他山之石可以攻玉,借鑒了以下兩點:
第一點,人們看問題的方式總是從宏觀一路下鑽到微觀,所以我們加了柱狀圖做為時間段聚合,方便從宏觀上看到請求量的規模分佈。下圖3是某區域的訂單趨勢柱狀圖。

圖3 訂單趨勢柱狀圖
第二點,如果數據量非常大,可繼續點擊柱圖,展開當前時間條件下的子柱狀圖。系統根據數據量規模,自動展示出散點圖,方便用戶瀏覽耗時分佈。這樣一層層穿刺下鑽,既滿足了人的操作習慣,又提高了處理速度,做到了秒級響應。

圖4柱狀圖下鑽
 

B. 從宏觀到微觀,化繁為簡

就宏觀到微觀的監控下鑽,下面講三個案例。
案例一,單一維度下鑽
其實多數時候人們只想關注某一維度的分佈情況,比如按應用、按工程、按服務IP、按請求URL、按商戶、按門店、按設備看分佈。對於ES來說,單一條件聚合速度很快,秒級響應。
案例二,網絡質量監察
我司在全國大江南北分佈着大量餐飲中心(即食堂),如何快速定位出哪些食堂網絡環境糟糕呢?不能等客戶告訴我們。
我們從請求量級篩選(系統推薦和自定義)、dns/http/ssl/tcp/mqtt耗時情況、趨勢發展等諸多因子中分析出餐飲中心網絡情況,哪怕是學校食堂的一次網絡抖動,都可以被我們偵查到。不僅能分析出網絡問題,而且還能下鑽到請求鏈路上的任何階段,比如是DNS、TCP、SSL、首包等,並分析出受影響的終端設備。依然是秒級響應,如下圖5所示。

圖5 設備耗時下鑽
 
案例三,找出掉單
當用戶已支付而商戶未收款時,如何從千萬級訂單中快速找到丟失的那一筆訂單呢?內部稱sos訂單:

  • 秒級偵查出來;
  • 快速定位在哪個環節出了問題;
  • 系統自我修復能力

具體做法為:
第一步,將內部可能發生的業務場景圈出來,通過Flink實時計算形成閉環,當其中一個鏈條斷了,就會立馬把待排查的sos訂單壓入隊列任務,然後不斷主動查詢第三方支付渠道確認支付狀態。
第二步,對於一些疑難問題如短時間內系統無法解決時,比如第三方支付渠道出現了故障,就會發出告警消息給客服,客服預先跟進,減少用戶投訴處理時間。
 
總結一下:
OpenTSDB切換為Druid,明顯提升了從宏觀下鑽到微觀的響應時間,基本能做到百億級數據量秒級響應。高峰時段Kafka消息堆積也大幅降低。下鑽方式做了優化,更符合工程師探查習慣。
 
-完-

歡迎關注公眾號:老兵筆記

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

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

019.Kubernetes二進制部署插件dashboard

一 修改配置文件

1.1 下載解壓

  1 [root@k8smaster01 ~]# cd /opt/k8s/work/kubernetes/
  2 [root@k8smaster01 kubernetes]# tar -xzvf kubernetes-src.tar.gz



提示:k8smaster01節點已解壓完畢,可直接修改配置。

1.2 修改配置

  1 [root@k8smaster01 ~]# cd /opt/k8s/work/kubernetes/cluster/addons/dashboard
  2 [root@k8smaster01 dashboard]# vi dashboard-service.yaml
  3 ……
  4   type: NodePort			#增加此行,使用node形式訪問
  5 ……
  6 #使用node方式訪問dashboard

1.3 修改為國內源


  1 [root@k8smaster01 dashboard]# vi dashboard-controller.yaml
  2 ……
  3         image: mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.10.1
  4 ……



提示:將yaml文件中的image字段修改為mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.10.1。

二 創建 dashboard

2.1 創建dashboard並檢查

  1 [root@k8smaster01 ~]# cd /opt/k8s/work/kubernetes/cluster/addons/dashboard
  2 [root@k8smaster01 dashboard]# kubectl apply -f .


2.2 查看分配的NodePort

  1 [root@k8smaster01 ~]# kubectl get deployment kubernetes-dashboard -n kube-system
  2 NAME                  READY    UP-TO-DATE    AVAILABLE    AGE
  3 kubernetes-dashboard  1/1      1             1            84s
  4 [root@k8smaster01 ~]# kubectl --namespace kube-system get pods -o wide
  5 [root@k8smaster01 ~]# kubectl get services kubernetes-dashboard -n kube-system





提示:k8smaster02 NodePort 31181 映射到 dashboard pod 443 端口。

2.3 查看dashboard參數

  1 [root@k8smaster01 ~]# kubectl exec --namespace kube-system -it kubernetes-dashboard-7848d45466-bgz94  -- /dashboard --help


提示:dashboard 的 –authentication-mode 支持 token、basic,默認為 token。如果使用 basic,則 kube-apiserver 必須配置 –authorization-mode=ABAC 和 –basic-auth-file 參數。

三 dashboard驗證方式


由於Kubernetes默認證書可能過期導致無法訪問dashboard,本實驗在已成功部署Kubernetes後手動重新創建證書。

3.1 創建證書

  1 [root@k8smaster01 ~]# cd /opt/k8s/work/
  2 [root@k8smaster01 work]# openssl genrsa -out dashboard.key 2048
  3 [root@k8smaster01 work]# openssl rsa -passin pass:x -in dashboard.key -out dashboard.key
  4 [root@k8smaster01 work]# openssl req -new -key dashboard.key -out dashboard.csr
  5 -----
  6 Country Name (2 letter code) [XX]:CN
  7 State or Province Name (full name) []:Shanghai
  8 Locality Name (eg, city) [Default City]:Shanghai
  9 Organization Name (eg, company) [Default Company Ltd]:k8s
 10 Organizational Unit Name (eg, section) []:System
 11 [root@k8smaster01 work]# openssl x509 -req -sha256 -days 365 -in dashboard.csr -signkey dashboard.key -out dashboard.crt
 12 [root@k8smaster01 work]# openssl x509  -noout -text -in ./dashboard.crt		#查看證書


3.2 分發證書

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for all_ip in ${ALL_IPS[@]}
  4   do
  5     echo ">>> ${all_ip}"
  6     scp dashboard.* root@${all_ip}:/etc/kubernetes/cert
  7   done


3.3 修改默認證書配置

  1 [root@k8smaster01 work]# cd /opt/k8s/work/kubernetes/cluster/addons/dashboard
  2 [root@k8smaster01 dashboard]# kubectl delete -f .		#刪除使用默認證書所創建的dashboard
  3 [root@k8smaster01 dashboard]# ll /etc/kubernetes/cert/dashboard.*
  4 -rw-r--r-- 1 root root 1.2K Jun 28 18:06 /etc/kubernetes/cert/dashboard.crt
  5 -rw-r--r-- 1 root root  976 Jun 28 18:06 /etc/kubernetes/cert/dashboard.csr
  6 -rw-r--r-- 1 root root 1.7K Jun 28 18:06 /etc/kubernetes/cert/dashboard.key
  7 
  8 [root@master dashboard]# kubectl create secret generic kubernetes-dashboard-certs --from-file="/etc/kubernetes/cert/dashboard.crt,/etc/kubernetes/cert/dashboard.key" -n kube-system	#掛載新證書到dashboard
  9 [root@master dashboard]# kubectl get secret kubernetes-dashboard-certs -n kube-system -o yaml	#查看新證書


3.4 重新部署dashboard

  1 [root@k8smaster01 work]# cd /opt/k8s/work/kubernetes/cluster/addons/dashboard
  2 [root@master dashboard]# kubectl apply -f .
  3 [root@master dashboard]# kubectl get pods --namespace=kube-system | grep dashboard		#確認驗證


3.5 確認驗證

  1 [root@k8smaster01 ~]# kubectl get deployment kubernetes-dashboard -n kube-system
  2 [root@k8smaster01 ~]# kubectl --namespace kube-system get pods -o wide
  3 [root@k8smaster01 ~]# kubectl get services kubernetes-dashboard -n kube-system





提示:k8smaster03 NodePort 30938 映射到 dashboard pod 443 端口。

四 訪問dashboard

3.1 導入證書


將dashboard.crt導入IE瀏覽器,並設置為信任,導入操作略。

3.2 訪問方式


本實驗採用nodeip:nodepord方式訪問。

瀏覽器訪問:https://172.24.8.73:30938



提示:

更多dashboard訪問方式及認證可參考《附004.Kubernetes Dashboard簡介及使用》。

dashboard登錄整個流程可參考:https://www.cnadn.net/post/2613.htm

apiserver方式見3.4,Kubeconfig驗證方式見《附006.Kubernetes身份認證》中的3.5。

五 驗證方式

5.1 創建token

  1 [root@k8smaster01 ~]# kubectl create sa dashboard-admin -n kube-system
  2 [root@k8smaster01 ~]# kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin
  3 [root@k8smaster01 ~]# ADMIN_SECRET=$(kubectl get secrets -n kube-system | grep dashboard-admin | awk '{print $1}')
  4 [root@k8smaster01 ~]# DASHBOARD_LOGIN_TOKEN=$(kubectl describe secret -n kube-system ${ADMIN_SECRET} | grep -E '^token' | awk '{print $2}')
  5 [root@k8smaster01 ~]# echo ${DASHBOARD_LOGIN_TOKEN}	#輸入登錄的token
  6 eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4tdmc5bWgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiZTlkNGRjNGUtOTk3OC0xMWU5LTkzNTItMDAwYzI5ZmE3YTc5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.X1NJsPNaAgV2TzJo0NlqOWFofDYOsSdkeiYHFGQFk5nNy0nbbnfnnoH0yumj_Ld0nGPakIjEpsUq9dqgCazeCpgk5EsygD6UlSg5sYA2sTLswbDoZdS3QzrOjY5MXWD3VDc_OQofD94MZqHMMw7IABVlfVsZ0vMEvHe-Qtyt6EQlFlHq5QjwDX8dCQDKRbwuiCr-Iy_dCWHHIhaT25BREf2viei8sZ497D8h4TXgO_u2CGf3qXRGNXj26VSdD8bT-BFGiDdyuXPbDHPU5LalvxF4WThChRfjO4zHLI2fOXq8BBF6DjbjhtG4X8fLuvJaxF4YWAmVS_78eJHhA3nvRg


3.4 創建kubeconfig文件


使用token相對複雜,可將token添加至kubeconfig文件中,使用KubeConfig 文件訪問dashboard。

  1 [root@k8smaster01 ~]# cd /opt/k8s/work/
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# kubectl config set-cluster kubernetes \
  4   --certificate-authority=/etc/kubernetes/cert/ca.pem \
  5   --embed-certs=true \
  6   --server=${KUBE_APISERVER} \
  7   --kubeconfig=dashboard.kubeconfig		# 設置集群參數
  8 [root@k8smaster01 work]# kubectl config set-credentials dashboard_user \
  9   --token=${DASHBOARD_LOGIN_TOKEN} \
 10   --kubeconfig=dashboard.kubeconfig		# 設置客戶端認證參數,使用上面創建的 Token
 11 [root@k8smaster01 work]# kubectl config set-context default \
 12   --cluster=kubernetes \
 13   --user=dashboard_user \
 14   --kubeconfig=dashboard.kubeconfig		# 設置上下文參數
 15 [root@k8smaster01 work]# kubectl config use-context default --kubeconfig=dashboard.kubeconfig			# 設置默認上下文,將dashboard.kubeconfig文件導入,以便於瀏覽器使用該文件登錄。


六 正式登錄

6.1 kubeconfig訪問


瀏覽器訪問:https://172.24.8.73:30938








提示:由於缺少 Heapster 插件,當前 dashboard 不能展示 Pod、Nodes 的 CPU、內存等統計數據和圖表。本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

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

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務

架構設計:”4+1″視圖

概念

“4+1”視圖,是指從5個不同視角來描述軟件體繫結構。
“4+1”分別指:

  1. 邏輯視圖
  2. 過程視圖
  3. 物理視圖
  4. 開發視圖
  5. 場景/用例 視圖

邏輯架構的描述可以圍繞前四個視圖進行組織,然後結合用例或場景進行說明,形成第五個視圖。

每個視圖只關心繫統的一個側面,5個視圖結合起來,才能反映系統的全部內容。

關於視圖

軟件設計可以從不同的概念角度進行描述和記錄,這些角度通常被稱為視圖。

“視圖表示軟件體繫結構的一部分,它显示軟件系統的特定屬性”

不同的視圖涉及與軟件相關的不同問題。

總之,軟件設計是由設計過程產生的多方面的產物,通常由相對獨立的正交視圖組成,可以結合建築視圖理解。

邏輯視圖

當使用面向對象的設計方法時,邏輯視圖對應設計的對象模型,常用描述方法有UML類圖、E-R圖。

邏輯架構主要支持功能需求,即系統應該為用戶提供什麼樣的服務。
系統被分解成一組關鍵抽象,以對象或對象類的形式從問題中表述。

類的設計遵循抽象、封裝和繼承的原則,這種分解不僅是為了進行功能分析,也是為了理清系統各個部分的通用機制和設計元素。

過程視圖

過程架構關注設計的併發和同步方面,考慮了一些非功能性需求,比如性能和可用性。
過程視圖可以在幾個抽象層次上進行描述,每個抽象層次處理不同的關注點:

  • 在最高層次上關注進程,進程分佈在由LAN或WAN連接的一組硬件資源上,作為一組獨立執行的通信程序邏輯網絡。
  • 多個邏輯網絡可以同時存在,共享相同的物理資源。

主要任務是通過一組定義良好的任務間通信機制進行通信:基於同步和異步消息的通信服務、遠程過程調用、事件廣播等。

次要任務是可以通過集合或共享內存進行通信,避免重大任務在同一過程或處理節點上進行配置假設。

物理視圖

物理視圖描述軟件到硬件的映射,主要反映在分佈式方面。

物理架構主要考慮系統的非功能性需求,如可用性、可靠性(容錯性)、性能(吞吐量)和可擴展性。

常見物理配置:

  • 測試
  • 為不同站點或不同客戶部署系統

開發視圖

開發視圖描述軟件在其開發環境中的靜態組織。

開發架構的重點:

  • 對軟件開發環境中實際軟件模塊進行組織
  • 將軟件打包成小的程序庫,或者打包成可以由一個或少量開發人員開發的子系統

系統的開發架構由模塊和子系統圖表示,表示成“導出”和“導入”關係。只有當軟件的所有元素都被識別之後,才能描述完整的開發架構。

在很大程度上,開發架構考慮發展的便利性、軟件管理、重用或通用性,以及工具集或編程語言施加的約束。

開發視圖是需求分配的基礎,便於開發團隊分配工作,有助於成本評估和提前計劃、監控項目進度、軟件重用、可移植性和安全性的推理。通過開發視圖,容易得出項目開發人員的分工配置。

實際應用中,開發視圖會在邏輯視圖的基礎上增加大量內容,比如大量接口、輔助類等。

場景/用例 視圖

架構的描述決策可以圍繞前四個視圖進行組織,然後由一些選定的用例或場景(成為第五個視圖)進行說明。

其他四個視圖中的元素,可以通過一些重要的場景或用例進行更好的展示,比如:

  • 構造更符合用例的實例
  • 描述一些關聯腳本,如對象之間或進程之間的交互

總結

並非所有的軟件架構都需要完整的“4+1”視圖。

無用的視圖可以從架構描述中省略,例如:

  • 如果只有一個處理器,則不需要物理視圖
  • 如果只有一個進程或程序,則不需要進程視圖
  • 對於非常小的系統,有可能邏輯視圖和開發視圖非常相似,不需要單獨描述

場景視圖在任何情況下都有用。

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

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”

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

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

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

直接引用MrAdvice.dll文件不能實現AOP攔截,教你1分鐘解決這個問題

直接引用MrAdvice.dll文件不能實現AOP攔截,教你1分鐘解決這個問題。近日工作中,要實現一個功能,那就是業務層方法裏面實現自動緩存。編寫業務的C#開發人員只關注如何將業務代碼編寫正確就可以了,而緩存的代碼,大多類似,無非就是判斷是否有緩存,有就取出返回,沒有就調用數據庫代碼獲取數據再緩存起來而已,於是這部分代碼通過使用AOP的方式自動接管掉這種重複性代碼。

MrAdvice開源項目github地址:

直接引用MrAdvice.dll文件不能實現AOP攔截功能

1月份的時候寫過一篇的文章,在測試程序中使用的是MrAdvice這個開源組件,對它熟悉,就又使用它了。只不過這次使用有點特殊,以前開發是可以聯網的,可以很方便的使用nuget將其安裝到本地,而這次是因項目原因內外網隔離,且是斷網開發的,就只能在外網寫個測試程序,然後將MrAdvice.dll文件複製到內網電腦,內網電腦通過引用dll的方式來使用該組件,結果是不會進入到攔截方法的。

直接引用MrAdvice.dll  

通過下圖可以看到,成功解決后,可以實現自動緩存了。

實現AOP攔截 

下面是全部的演示程序源碼。

演示程序解決方案目錄一覽

該項目是一個控制台項目,解決方案如下圖所示:

演示程序解決.

MrAdvice.dll是直接引用的,不是通過nuget安裝的,至於這個dll文件的獲取,你可以通過nuget獲取了找到它即可。

演示程序的源碼

控制台入口的代碼比較簡單,單純的調用接口。

程序入口代碼

class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "jhrs.com AOP演示程序,通過直接引用MrAdvice.dll編寫的代碼!";
            DateTime dtNow = DateTime.Now;
            IJhrscom api = new Jhrscom();
            var result = api.GetResult("這是a參數", dtNow, 12342);
            Console.WriteLine();
            Console.WriteLine($"第1次調用時返回結果是:"+result.ToJson());
            Console.WriteLine();
            result = api.GetResult("這是a參數", dtNow, 12342);
            Console.WriteLine();
            Console.WriteLine($"第2次調用時返回結果是來自第1次緩存數據,只不過被改了下:" + result.ToJson());
            Console.WriteLine();
            //api.GetPatient(Guid.NewGuid(), result);
        }
    }

  

程序接口代碼

程序接口代碼主要是模擬業務方法裏面的一些類,定義了一個接口,一個實現類,另外實現類上面是標註了一個自動緩存的特性(AutoCache),該特性的實現代碼即為下面所述的核心的AOP攔截代碼,具體下面會給出的;另外還有一個輸出結果(響應消息)的類。整個源碼是放到一個文件裏面的,如下所示:

public interface IJhrscom
    {
        ResponseResult GetResult(string a, DateTime dateTime, int id);

        ResponseResult GetPatient(Guid id, ResponseResult t);
    }

    public class Jhrscom : IJhrscom
    {
        [AutoCache(10)]
        public ResponseResult GetPatient(Guid id, ResponseResult t)
        {
            string key = GetKey(new object[] { id, t });
            ResponseResult result = new ResponseResult() { Code = 4444, Message = "第2個方法" };
            return result;
        }

        [AutoCache(cacheMinutes: 12, enableSliding: true)]
        public ResponseResult GetResult(string a, DateTime dateTime, int id)
        {
            ResponseResult result = new ResponseResult() { Code = 1122, Message = "緩存測試消息" };
            string key = GetKey(new object[] { a, dateTime, id });
            return result;
        }

        /// <summary>
        /// 緩存key
        /// </summary>
        /// <param name="pars"></param>
        /// <returns></returns>
        private string GetKey(params object[] pars)
        {
            var method = new StackFrame(1).GetMethod();
            var array = method.GetParameters();
            var key = array.Select(x => { return pars[x.Position].ToJson(); }).ToArray();

            var cacheKey = $"{method.DeclaringType.ToString()}|{method.Name.Replace("′", "")}|{string.Join("_", array.Select(x => x.Name))}|{string.Join("_", key)}".GetMd5();
            Console.WriteLine($"【{method.Name.Replace("′", "")}】實現類裏面的緩存Key:" + cacheKey);
            return cacheKey;
        }
    }

    /// <summary>
    /// 輸出結果
    /// </summary>
    public class ResponseResult
    {
        public int Code { get; set; }
        public string Message { get; set; }

        //.....其它屬性
    }

  

核心的AOP攔截代碼

該代碼是用於實現自動緩存功能,思路就是在調用業務方法前,根據緩存key,緩存key按一定規則生成,保證唯一就可以了,具體源碼中有說明,從緩存裏面取出數據,如果存在緩存就直接返回給調用者即可,並終止業務方法的執行(體現在不調用context.Proceed()方法上);如果不存在緩存數據或者緩存過期了,則調用業務方法獲取數據后並緩存就可以了。

/// <summary>
    /// 用AOP來實現自動緩存
    /// </summary>
    public class AutoCacheAttribute : Attribute, IMethodAdvice
    {
        /// <summary>
        /// 滑動過期
        /// </summary>
        public bool EnableSliding { get; set; }

        /// <summary>
        /// 緩存時間,分鐘
        /// </summary>
        public int CacheMinutes { get; set; }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="cacheMinutes">緩存時間,分鐘,默認5分鐘,小於等於0永久緩存</param>
        /// <param name="enableSliding">使用滑動過期緩存控制策略</param>
        public AutoCacheAttribute(int cacheMinutes = 5, bool enableSliding = false)
        {
            EnableSliding = enableSliding;
            CacheMinutes = cacheMinutes;
        }

        /// <summary>
        /// AOP組件攔截方法,用於實現自動緩存,有緩存時直接返回;
        /// 沒有緩存時,調用被攔截方法后,有返回值則將數據自動緩存起來
        /// </summary>
        /// <param name="context"></param>
        public void Advise(MethodAdviceContext context)
        {
            var key = GetKey(context);
            if (context.HasReturnValue && key.TryGetCache(out object m))
            {
                var r = m as ResponseResult;
                r.Message = "在攔截方法裏面改了緩存裏面取出來的數據!";

                context.ReturnValue = r;
                //context.ReturnValue = m;  

                //context.Proceed();  //直接取出緩存返回,不用執行原來取數據方法。
            }
            else
            {
                context.Proceed();//執行被攔截的方法
                if (context.HasReturnValue && context.ReturnValue != null)
                {
                    //被攔截方法有返回值,並且返回值不為null
                    if (EnableSliding && CacheMinutes > 0)
                        context.ReturnValue.SetCache(key, TimeSpan.FromMinutes(CacheMinutes));
                    else if (CacheMinutes > 0)
                        context.ReturnValue.SetCache(key, DateTime.Now.AddMinutes(CacheMinutes));
                    else
                        context.ReturnValue.SetCache(key);
                }
            }
        }

        /// <summary>
        /// 獲取緩存key,key的規則為: md5(類全名|方法名|參數列表拆分數組|參數值的json數組),這樣可以保證唯一
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private string GetKey(MethodAdviceContext context)
        {
            var array = context.TargetMethod.GetParameters();
            var key = array.Select(x => { return context.Arguments[x.Position].ToJson(); }).ToArray();

            var cacheKey = $"{context.Target.ToString()}|{context.TargetName}|{string.Join("_", array.Select(x => x.Name))}|{string.Join("_", key)}".GetMd5();
            return cacheKey;
        }
    }

    /// <summary>
    /// 緩存擴展方法,可使用其它緩存替代
    /// </summary>
    public static class CacheExtensions
    {
        private static MemoryCache cache = new MemoryCache("https://jhrs.com");

        /// <summary>
        /// 設置緩存,一直不過期
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="key"></param>
        public static void SetCache<T>(this T value, string key)
        {
            if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"緩存鍵參數{nameof(key)}不能為null或空");
            if (value == null) throw new ArgumentException($"緩存值參數{nameof(value)}不能為null");
            CacheItemPolicy policy = new CacheItemPolicy();
            cache.Set(key, value, policy);
        }

        /// <summary>
        /// 設置緩存,固定過期時間
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="key"></param>
        /// <param name="absoluteExpiration"></param>
        public static void SetCache<T>(this T value, string key, DateTimeOffset? absoluteExpiration)
        {
            if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"緩存鍵參數{nameof(key)}不能為null或空");
            if (value == null) throw new ArgumentException($"緩存值參數{nameof(value)}不能為null");
            CacheItemPolicy policy = new CacheItemPolicy() { AbsoluteExpiration = (DateTimeOffset)absoluteExpiration };
            cache.Set(key, value, policy);
        }

        /// <summary>
        /// 設置緩存,滑動過期
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="key"></param>
        /// <param name="slidingExpiration"></param>
        public static void SetCache<T>(this T value, string key, TimeSpan? slidingExpiration)
        {
            if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"緩存鍵參數{nameof(key)}不能為null或空");
            if (value == null) throw new ArgumentException($"緩存值參數{nameof(value)}不能為null");
            CacheItemPolicy policy = new CacheItemPolicy() { SlidingExpiration = (TimeSpan)slidingExpiration };
            cache.Set(key, value, policy);
        }

        /// <summary>
        /// 獲取緩存數據
        /// </summary>
        /// <typeparam name="T">對象類型</typeparam>
        /// <param name="key"><緩存key/param>
        /// <param name="value">返回的緩存數據對名</param>
        /// <returns></returns>
        public static bool TryGetCache<T>(this string key, out T value)
        {
            value = default(T);
            if (cache.Contains(key))
            {
                value = (T)cache.Get(key);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 獲取字符串MD5值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetMd5(this string value)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(value);

            StringBuilder sb = new StringBuilder();
            MD5 hash = new MD5CryptoServiceProvider();
            bytes = hash.ComputeHash(bytes);
            foreach (byte b in bytes)
            {
                sb.AppendFormat("{0:x2}", b);
            }
            return sb.ToString();
        }
    }

  

附加的JSON擴展類

該擴展類只是方便將對象轉為JSON而已,代碼不復如,如下所示:

 public static class JsonExtensions
    {
        /// <summary>
        /// 將對象轉換為JSON字符串
        /// </summary>
        /// <param name="obj">要轉換的對象</param>
        /// <param name="camelCase">是否小寫名稱</param>
        /// <param name="indented"></param>
        /// <returns></returns>
        public static string ToJson(this object obj, bool camelCase = false, bool indented = false)
        {
            JsonSerializerSettings settings = new JsonSerializerSettings();
            if (camelCase)
            {
                settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            }
            if (indented)
            {
                settings.Formatting = Formatting.Indented;
            }
            return JsonConvert.SerializeObject(obj, settings);
        }

        /// <summary>
        /// 把Json字符串轉換為強類型對象
        /// </summary>
        public static T FromJson<T>(string json)
        {
            if (string.IsNullOrWhiteSpace(json)) return default(T);
            json = JsonDateTimeFormat(json);
            return JsonConvert.DeserializeObject<T>(json);
        }

        /// <summary>
        /// 處理Json的時間格式為正常格式
        /// </summary>
        private static string JsonDateTimeFormat(string json)
        {
            json = Regex.Replace(json,
                @"\\/Date\((\d+)\)\\/",
                match =>
                {
                    DateTime dt = new DateTime(1970, 1, 1);
                    dt = dt.AddMilliseconds(long.Parse(match.Groups[1].Value));
                    dt = dt.ToLocalTime();
                    return dt.ToString("yyyy-MM-dd HH:mm:ss.fff");
                });
            return json;
        }
    }

  

解決直接引用MrAdvice.dll不能攔截的問題

出現這個問題的根源是,MrAdvice這個組件是在編譯時會給你的項目源碼編織一些AOP攔截代碼,熟悉PostSharp的應該對此了解,這也是在MrAdvice項目地址的issues處得到解答,地址是:

所以我們需要在項目文件csproj裏面添加一些配置,並且把MrAdvice的目錄複製到斷網開發項目的packages目錄。通過完成這兩個步驟就可以解決了。

You’ve missed the point: Mr Advice is a post-build weaver, which changes the assembly at build-time after the csc compiler has generated it. To achieve this, is inserts a task in the csproj. So if you want to do the same manually, you need to also add the build task in your csproj. If you have a VS2017 solution with a project working, you’ll only need to copy the lines that were added to the csproj into your own project.

解決步驟

  • 聯網新建一個項目,通過nuget安裝MrAdvice,然後在解決方案的packages目錄裏面將nuget下載的MrAdvice目錄包,複製到你斷網環境的解決方案的packages目錄,如下圖所示:

MrAdvice 目錄

  • 修改項目文件,即修改csproj文件,csproj文件可以使用記事本或者其它軟件打開,增加以下節點,如下圖所示:

csproj文件

配置節點為如下:

<Import Project="..\packages\MrAdvice.2.8.8\build\MrAdvice.targets" Condition="Exists('..\packages\MrAdvice.2.8.8\build\MrAdvice.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>這台計算機上缺少此項目引用的 NuGet 程序包。使用“NuGet 程序包還原”可下載這些程序包。有關更多信息,請參見 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\packages\MrAdvice.2.8.8\build\MrAdvice.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MrAdvice.2.8.8\build\MrAdvice.targets'))" />
  </Target>

  

好了,通過以上步驟就可以在斷網環境裏面愉快的使用MrAdvice這個AOP攔截組件來省點體力勞動了。

源碼可以在首發地址下載,本文首發於:

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

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

【算法】leetcode算法筆記:二叉樹,動態規劃和回溯法

前言

寫的比較匆忙,測試用例是能全部跑通的,不過考慮內存和效率的話,還有許多需要改進的地方,所以請多指教

在二叉樹中增加一行

題目描述

給定一個二叉樹,根節點為第1層,深度為 1。在其第 d 層追加一行值為 v 的節點。

添加規則:給定一個深度值 d (正整數),針對深度為 d-1 層的每一非空節點 N,為 N 創建兩個值為 v 的左子樹和右子樹。

將 N 原先的左子樹,連接為新節點 v 的左子樹;

將 N 原先的右子樹,連接為新節點 v 的右子樹。

如果 d 的值為 1,深度 d – 1 不存在,則創建一個新的根節點 v,原先的整棵樹將作為 v 的左子樹。

Example

Input: 
A binary tree as following:
       4
     /   \
    2     6
   / \   / 
  3   1 5   

v = 1

d = 2

Output: 
       4
      / \
     1   1
    /     \
   2       6
  / \     / 
 3   1   5  

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/add-one-row-to-tree

 
基本思想
二叉樹的先序遍歷   
代碼的基本結構
不是最終結構,而是大體的結構

/**
 * @param {number} cd:current depth,遞歸當前深度
 * @param {number} td:target depth, 目標深度
 */
var traversal = function (node, v, cd, td) {
    // 遞歸到目標深度,創建新節點並返回
  if (cd === td) {
    // return 新節點
  }
  // 向左子樹遞歸
  if (node.left) {
    node.left = traversal (node.left, v, cd + 1, td);
  }
  // 向右子樹遞歸
  if (node.right) {
    node.right = traversal (node.right, v, cd + 1, td);
  }
  // 返回舊節點
  return node;
};
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} v
 * @param {number} d
 * @return {TreeNode}
 */
var addOneRow = function (root, v, td) {
    // 從根節點開始遞歸
  traversal (root, v, 1, td);
  return root;
};

 

 

具體分析
我們可以分類討論,分三種情況處理  
第1種情況:目標深度<=當前遞歸路徑的最大深度 
處理方法:val節點替換該目標深度對應的節點,並且

  • 如果目標節點原來是左子樹,那麼重置后目標節點是val節點的左子樹

  • 如果目標節點原來是右子樹,那麼重置后目標節點是val節點的右子樹

 

第2種情況:目標深度>當前遞歸路徑的最大深度
閱讀題目發現,有這麼一個描述:“輸入的深度值 d 的範圍是:[1,二叉樹最大深度 + 1]”
所以呢,當目標深度恰好比當前路徑的樹的深度再深一層時,處理方式是:
在最底下那一層節點的左右分支新增val節點

 

第3種情況:目標深度為1

我們再分析題意,題目里說:“如果 d 的值為 1,深度 d – 1 不存在,則創建一個新的根節點 v,原先的整棵樹將作為 v 的左子樹。”

這說明當:目標深度為1時,我們的處理方式是這樣的 

 

全部代碼 

/**
 * @param {v} val,插入節點攜帶的值
 * @param {cd} current depth,遞歸當前深度
 * @param {td} target depth, 目標深度
 * @param {isLeft}  判斷原目標深度的節點是在左子樹還是右子樹
 */
var traversal = function (node, v, cd, td, isLeft) {
  debugger;
  if (cd === td) {
    const newNode = new TreeNode (v);
    // 如果原來是左子樹,重置后目標節點還是在左子樹上,否則相反
    if (isLeft) {
      newNode.left = node;
    } else {
      newNode.right = node;
    }
    return newNode;
  }
  // 處理上述的第1和第2種情況
  if (node.left || (node.left === null && cd + 1 === td)) {
    node.left = traversal (node.left, v, cd + 1, td, true);
  }
  if (node.right || (node.right === null && cd + 1 === td)) {
    node.right = traversal (node.right, v, cd + 1, td, false);
  }
  return node;
};
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} v
 * @param {number} d
 * @return {TreeNode}
 */
var addOneRow = function (root, v, td) {
  // 處理目標深度為1的情況,也就是上述的第3種情況
  if (td === 1) {
    const n = new TreeNode (v);
    n.left = root;
    return n;
  }
  traversal (root, v, 1, td);
  return root;
};

 

單詞拆分 

題目描述 

給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict,判定 s 是否可以被空格拆分為一個或多個在字典中出現的單詞。

說明:

1.拆分時可以重複使用字典中的單詞。

2.你可以假設字典中沒有重複的單詞。

 

Example 

example1
輸入: s = "applepenapple", wordDict = ["apple", "pen"]
輸出: true
解釋: 返回 true 因為 "applepenapple" 可以被拆分成 "apple pen apple"。
注意: 你可以重複使用字典中的單詞。

example2
輸入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
輸出: false

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/word-break

 

基本思想 

動態規劃

具體分析
動態規劃的關鍵點是:尋找狀態轉移方程
有了這個狀態轉移方程,我們就可以根據上一階段狀態和決策結果,去求出本階段的狀態和結果
然後,就可以從初始值,不斷遞推求出最終結果。
在這個問題里,我們使用一個一維數組來存放動態規劃過程的遞推數據
假設這個數組為dp,數組元素都為true或者false,
dp[N] 存放的是字符串s中從0到N截取的子串是否是“可拆分”的布爾值
讓我們從一個具體的中間場景出發來思考計算過程
假設我們有

wordDict = ['ab','cd','ef']
s ='abcdef'

並且假設目前我們已經得出了N=1到N=5的情況,而現在需要計算N=6的情況

或者說,我們已經求出了dp[1] 到dp[5]的布爾值,現在需要計算dp[6] = ?  
該怎麼計算呢?
現在新的字符f被加入到序列“abcde”的後面,如此以來,就新增了以下幾種6種需要計算的情況

A序列 + B序列
1.abcdef + ""
2.abcde + f
3.abcd + ef
4.abc + def
5.ab + cdef
6.a + bcdef
注意:當A可拆且B可拆時,則A+B也是可拆分的

 

從中我們不難發現兩點

  1. 當A可拆且B可拆時,則A+B也是可拆分的

  2. 這6種情況只要有一種組合序列是可拆分的,abcdef就一定是可拆的,也就得出dp[6] = true了

下面是根據根據已有的dp[1] 到dp[5]的布爾值,動態計算dp[6] 的過程

(注意只要計算到可拆,就可以break循環了)  
具體代碼

var initDp = function (len) {
  let dp = new Array (len + 1).fill (false);
  return dp;
};
/**
 * @param {string} s
 * @param {string[]} wordDict
 * @return {boolean}
 */
var wordBreak = function (s, wordDict) {
  // 處理空字符串
  if (s === '' && wordDict.indexOf ('') === -1) {
    return false;
  }
  const len = s.length;
  // 默認初始值全部為false
  const dp = initDp (len);
  const a = s.charAt (0);
  // 初始化動態規劃的初始值
  dp[0] = wordDict.indexOf (a) === -1 ? false : true;
  dp[1] = wordDict.indexOf (a) === -1 ? false : true;
  // i:end
  // j:start
  for (let i = 1; i < len; i++) {
    for (let j = 0; j <= i; j++) {
      // 序列[0,i] = 序列[0,j] + 序列[j,i]
      // preCanBreak表示序列[0,j]是否是可拆分的
      const preCanBreak = dp[j];
      // 截取序列[j,i]
      const str = s.slice (j, i + 1);
      // curCanBreak表示序列[j,i]是否是可拆分的
      const curCanBreak = wordDict.indexOf (str) !== -1;
      // 情況1: 序列[0,j]和序列[j,i]都可拆分,那麼序列[0,i]肯定也是可拆分的
      const flag1 = preCanBreak && curCanBreak;
      // 情況2: 序列[0,i]本身就存在於字典中,所以是可拆分的
      const flag2 = curCanBreak && j === 0;
      if (flag1 || flag2) {
        // 設置bool值,本輪計算結束
        dp[i + 1] = true;
        break;
      }
    }
  }
  // 返回最後結果
  return dp[len];
};

全排列 

題目描述

給定一個沒有重複数字的序列,返回其所有可能的全排列。

Example

輸入: [1,2,3]
輸出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/permutations

 

基本思想

回溯法

 

具體分析

  1. 深度優先搜索搞一波,index在遞歸中向前推進

  2. 當index等於數組長度的時候,結束遞歸,收集到results中(數組記得要深拷貝哦)

  3. 兩次数字交換的運用,計算出兩種情況

總結

想不通沒關係,套路一波就完事了 

具體代碼

var swap = function (nums, i, j) {
  const temp = nums[i];
  nums[i] = nums[j];
  nums[j] = temp;
};

var recursion = function (nums, results, index) {
  // 剪枝
  if (index >= nums.length) {
    results.push (nums.concat ());
    return;
  }
  // 初始化i為index
  for (let i = index; i < nums.length; i++) {
    // index 和 i交換??
    // 統計交換和沒交換的兩種情況
    swap (nums, index, i);
    recursion (nums, results, index + 1);
    swap (nums, index, i);
  }
};
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function (nums) {
  const results = [];
  recursion (nums, results, 0);
  return results;
};

 

 

 

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

MySQL InnoDB MVCC

MySQL 原理篇

MVCC

MVCC 的定義

MVCC(Multiversion concurrency control):多版本併發控制,併發訪問(讀或寫)數據庫時,對正在事務內處理的數據做多版本的管理。以達到用來避免寫操作的堵塞,從而引發讀操作的併發問題。

MVCC 邏輯流程

插入

MySQL 在每一行數據中都會默認添加一些隱藏列 DB_TRX_IDDB_ROLL_PT。

上面圖中的執行步驟如下:

  1. 手動開啟事務,從 InnoDB 引擎中獲取一個全局事務ID(1)
  2. 然後往 teacher 表中插入兩條數據,同時設置數據行的版本號為當前事務ID,刪除版本號為 NULL

思考:如果事務是自動提交的(SET AUTOCOMMIT = NO),且未手動開啟事務,執行如下兩條 SQL,插入的數據會是什麼樣子的?

INSERT INTO teacher (NAME, age) VALUE ('seven', 18) ;

INSERT INTO teacher (NAME, age) VALUE ('qingshan', 19) ;

因為事務是自動提交的,所以兩條插入語句會分別獲取事務ID,所以這裏插入的數據行的版本號是1和2。

刪除

上面圖中的執行步驟如下:

  1. 手動開啟事務,從 InnoDB 引擎中獲取一個全局事務ID(22)
  2. 然後執行一條刪除語句,InnoDB 會找到這條記錄,把它的刪除版本號設置為當前事務ID

修改

上面圖中的執行步驟如下:

  1. 手動開啟事務,從 InnoDB 引擎中獲取一個全局事務ID(33)
  2. 然後執行一條修改語句,InnoDB 會找到這條記錄,copy 一份原數據插入到表中,將新行數據的數據行的版本號的值設置為當前事務ID,將原行數據的刪除版本號的值設置為當前事務ID

查詢

上面圖中的執行步驟如下:

  1. 手動開啟事務,從 InnoDB 引擎中獲取一個全局事務ID(44)
  2. 根據數據查詢規則的描述
    1. 查找數據行版本早於當前事務版本的數據行,發現表中三行數據都滿足條件
    2. 查找刪除版本號要麼為 NULL,要麼大於當前事務版本號的記錄,發現只有最後一條數據滿足條件(1, seven, 19)

案例分析

數據準備:

CREATE TABLE `teacher` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

INSERT  INTO teacher(id,NAME,age) VALUES (1,'seven',18);
INSERT  INTO teacher(id,NAME,age) VALUES (2,'qingshan',20);

案例一

-- 事務A執行
BEGIN;                                     -- 1
SELECT * FROM teacher;                       -- 2
COMMIT;

--事務B執行
BEGIN;                                     -- 3
UPDATE teacher SET age =28 WHERE id=1;     -- 4
COMMIT;

案例一的執行步驟是:1,2,3,4,2,執行效果如下圖所示:

雖然在執行 3,4 步驟的時候更新 id=1 的數據,但是根據 MVCC 的查詢邏輯流程,再次執行2,獲取到的數據依然和第一次一樣。

案例二

-- 事務A執行
BEGIN;                                     -- 1
SELECT * FROM teacher;                       -- 2
COMMIT;

--事務B執行
BEGIN;                                     -- 3
UPDATE teacher SET age =28 WHERE id=1;     -- 4
COMMIT;

案例二的執行步驟是:3,4,1,2,執行效果如下圖所示:

根據 MVCC 的查詢邏輯流程,執行1,2,獲取到的數據是事務B未提交的數據,這個是有問題的。

分析了案例一和案例二,發現 MVCC 不能解決案例二的問題,InnoDB 會使用 Undo log 解決案例二的問題。

Undo Log

Undo Log 的定義

Undo:意為取消,以撤銷操作為目的,返回指定某個狀態的操作。

Undo Log:數據庫事務提交之前,會將事務修改數據的鏡像(即修改前的舊版本)存放到 undo 日誌里,當事務回滾時,或者數據庫奔潰時,可以利用 undo 日誌,即舊版本數據,撤銷未提交事務對數據庫產生的影響。。

  • 對於 insert 操作,undo 日誌記錄新數據的 PK(ROW_ID),回滾時直接刪除;
  • 對於 delete/update 操作,undo 日誌記錄舊數據 row,回滾時直接恢復;
  • 他們分別存放在不同的buffer里。

Undo Log 是為了實現事務的原子性而出現的產物。

 

Undo Log 實現事務原子性:事務處理過程中,如果出現了錯誤或者用戶執行了 ROLLBACK 語句,MySQL 可以利用 Undo Log 中的備份將數據恢復到事務開始之前的狀態。

InnoDB 發現可以基於 Undo Log 來實現多版本併發控制。

Undo Log 在 MySQL InnoDB 存儲引擎中用來實現多版本併發控制。

 

Undo Log 實現多版本併發控制:事務未提交之前,Undo Log 保存了未提交之前的版本數據,Undo Log 中的數據可作為數據舊版本快照供其他併發事務進行快照讀。

分析下圖中 SQL 的執行過程。

  • 事務A手動開啟事務,執行更新操作,首先會把更新命中的數據拷貝到 Undo Buffer 中
  • 事務B手動開啟事務,執行查詢操作,會讀取 Undo Log 中數據返回,進行快照度

當前讀和快照讀

快照讀

SQL 讀取的數據是快照版本,也就是歷史版本,普通的 SELECT 就是快照讀。

InnoDB 快照讀,數據的讀取將由 cache(原本數據)+ Undo Log(事務修改過的數據)兩部分組成。

當前讀

SQL 讀取的數據是最新版本,通過鎖機制來保證讀取的數據無法通過其他事務進行修改。

UPDATE 、DELETE 、INSERT 、SELECT … LOCK IN SHARE MODE 、SELECT … FOR UPDATE 都是當前讀,這些操作在《MySQL InnoDB 鎖》這篇文章中有過演示,事務A執行這些 SQL,會阻塞事務B的 SQL 執行。

在 InnoDB 引擎裏面,快照讀通過 MVCC 解決幻讀的問題,當前讀通過 Next-Key Locks 解決幻讀的問題。

Redo Log

Redo Log 的定義

Redo:顧名思義就是重做。以恢復操作為目的,重現操作。

Redo Log:指事務中操作的任何數據,將最新的數據備份到一個地方(Redo Log)。

Redo Log 的持久化:不是隨着事務的提交才寫入的,而是在事務的執行過程中,便開始寫入 Redo Log 中,具體的落盤策略可以進行配置。

Redo Log 是為了實現事務的持久性而出現的產物。

Redo Log 實現事務持久性:防止在發生故障的時間點,尚有臟頁未寫入表的 IBD 文件中,在重啟 MySQL 服務的時候,根據 Redo Log 進行重做,從而達到事務的未入磁盤數據進行持久化這一特性。

根據下圖分析 Redo Log 的執行流程

InnoDB 不是每一次提交事務都把數據從緩存區持久化到硬盤的,因為每次提交事務都把數據持久化到硬盤,效率很低,每一次持久化都需要執行 IO 操作。

InnoDB 會把每次數據變化會先進入 Redo Buffer 中,事務提交了,會根據策略把新的數據寫入 Redo Log 中,InnoDB 就會認為這次事務提交成功了,數據並不一定馬上就進入表的 IBD 文件中。

疑問:持久化到 Redo Log 中和持久化到表的 IBD 文件一樣都是 IO 操作,為什麼要設計 Redo Log 呢?

其實是因為持久化到 Redo Log 中是順序 IO 的操作,而持久化到表的 IBD 文件中是一個隨機 IO 的操作,比如我們需要更新 id=1 和 id=8 的數據,如果是 Redo Log,就只需要把更新的數據順序存入 Redo Log 中;但如果是表的 IBD 文件,就需要先找到 id=1 和 id=8 的兩個不連續的磁盤文件地址,再做持久化操作,影響數據庫服務的併發性能。

Redo Log 的持久化配置

指定 Redo Log 記錄在 {datadir}/ib_logfile1 和 ib_logfile2 兩個文件中,可以通過 innodb_log_group_home_dir配置指定目錄存儲。

一旦事務成功提交且數據持久化到表的 IBD 文件中之後,此時 Redo Log 中的對應事務數據記錄就失去了意義,所 以 Redo Log 的寫入是日誌文件循環寫入的過程,也就是覆蓋寫的過程。

  • 指定 Redo Log 日誌文件組中的數量 innodb_log_files_in_group 默認為2
  • 指定 Redo Log 每一個日誌文件最大存儲量 innodb_log_file_size 默認48M
  • 指定 Redo Log 在 cache/buffer 中的 buffer 池大小 innodb_log_buffer_size 默認16M

Redo Buffer 持久化到 Redo Log 的策略,通過設置 Innodb_flush_log_at_trx_commit 的值:

  • 取值0:每秒提交 Redo buffer -> Redo Log OS cache -> flush cache to disk,可能丟失一秒內的事務數據。
  • 取值1(默認值):每次事務提交執行 Redo Buffer -> Redo Log OS cache -> flush cache to disk,最安全,性能最差的方式
  • 取值2:每次事務提交執行 Redo Buffer -> Redo log OS cache 再每一秒執行 -> flush cache to disk 操作

一般建議選擇取值2,因為 MySQL 掛了最多損失一次事務提交的數據,整個服務期掛了才會損失一秒的事務提交數據。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

Vue項目性能優化整理

 以下方式基於 @vue/cli 快速搭建的交互式項目腳手架

1. 路由懶加載

當打包構建應用時,JavaScript 包會變得非常大,影響頁面加載。如果我們能把不同路由對應的組件分割成不同的代碼塊,然後當路由被訪問的時候才加載對應組件,這樣就更加高效了。

結合 Vue 的和 Webpack 的,輕鬆實現路由組件的懶加載。

 1 import Vue from 'vue'
 2 import Router from 'vue-router'
 3 import store from './store'
 4 import Home from './views/Home.vue'
 5 
 6 Vue.use(Router)
 7 
 8 const router = new Router({
 9   routes: [
10     {
11       path: '/',
12       name: 'home',
13       component: Home,
14     },
15     {
16       path: '/make',
17       name: 'make',
18       component: () => import(/* webpackChunkName: "make" */ './views/Make.vue'),
19     }
20   ],
21 })

 

2. webpack動態導入

將statically import(靜態導入) 改為 dynamic import(動態導入)進行代碼拆分

 1 import(/* webpackChunkName: "html2canvas" */ 'html2canvas').then(
 2   ({ default: html2canvas }) => {
 3     html2canvas(document.querySelector('.container'), {
 4       scale: window.devicePixelRatio,
 5       allowTaint: false,
 6       useCORS: true,
 7     }).then(canvas => {
 8       console.log(canvas.toDataURL('image/jpeg', 0.9))
 9     })
10   }
11 )

 

3. 預取/預加載模塊(prefetch/preload module)

在聲明 import 時,使用下面這些內置指令,可以讓 webpack 輸出 “resource hint(資源提示)”,來告知瀏覽器:

  prefetch(預取):將來某些導航下可能需要的資源
  preload(預加載):當前導航下可能需要資源

import(/* webpackPrefetch: true */ 'LoginModal');

import(/* webpackPreload: true */ 'ChartingLibrary');

這會生成 <link rel="prefetch" href="login-modal-chunk.js"> 並追加到頁面頭部,指示着瀏覽器在閑置時間預取 login-modal-chunk.js 文件。

只要父 chunk 完成加載,webpack 就會添加 prefetch hint(預取提示)。

與 prefetch 指令相比,preload 指令有許多不同之處:

  • preload chunk 會在父 chunk 加載時,以并行方式開始加載。prefetch chunk 會在父 chunk 加載結束后開始加載。
  • preload chunk 具有中等優先級,並立即下載。prefetch chunk 在瀏覽器閑置時下載。
  • preload chunk 會在父 chunk 中立即請求,用於當下時刻。prefetch chunk 會用於未來的某個時刻。
  • 瀏覽器支持程度不同。

vue默認開啟,可在vue.config.js中全局禁用prefetch,再針對指定模塊開啟。

chainWebpack: config => {
  config.plugins.delete('prefetch')
},

 

4. 添加Gzip打包配置()

  yarn add compression-webpack-plugin -D

configureWebpack: config => {
  const CompressionPlugin = require('compression-webpack-plugin')
  config.plugins.push(new CompressionPlugin())
}

 

5. 添加頁面預渲染()

在單頁應用程序中預呈現靜態HTML,可以極大的提高網頁訪問速度,而且配合一些meat插件,基本可以滿足SEO需求。

預渲染基本上是在啟動無頭瀏覽器,加載應用程序的路由並將結果保存到靜態HTML文件中。然後將其與以前使用的任何靜態文件服務解決方案一起使用,是無需更改代碼或添加服務器端渲染的解決方法。

不過,它僅適用於HTML5 history,因為每個預渲染的路由都會生成一個對應的HTML,在hash模式下使用只會有一個HTML。

  yarn add prerender-spa-plugin -D

 1 configureWebpack: config => {
 2   const path = require('path')
 3   const PrerenderSPAPlugin = require('prerender-spa-plugin')
 4   config.plugins.push(
 5     new PrerenderSPAPlugin({
 6       staticDir: path.join(__dirname, 'dist'),
 7       routes: ['/'],
 8       minify: {
 9         collapseBooleanAttributes: true,
10         collapseWhitespace: true,
11         keepClosingSlash: true,
12         decodeEntities: true,
13         sortAttributes: true,
14       },
15       renderer: new PrerenderSPAPlugin.PuppeteerRenderer({
16         renderAfterDocumentEvent: 'render-event',
17         renderAfterTime: 5000,
18         // headless: false,
19       }),
20     })
21   )
22 }
23 
24 new Vue({
25   router,
26   store,
27   render: h => h(App),
28   mounted() {
29     // 預渲染 renderAfterDocumentEvent.
30     document.dispatchEvent(new Event('render-event'))
31   },
32 }).$mount('#app')

prerender-spa-plugin 利用了 Puppeteer 的爬取頁面的功能。 Puppeteer 是一個 Chrome官方出品的 headlessChromenode 庫。它提供了一系列的 API, 可以在無 UI 的情況下調用 Chrome 的功能, 適用於爬蟲、自動化處理等各種場景。它很強大,所以很簡單就能將運行時的 HTML 打包到文件中。原理是在 Webpack 構建階段的最後,在本地啟動一個 Puppeteer 的服務,訪問配置了預渲染的路由,然後將 Puppeteer 中渲染的頁面輸出到 HTML 文件中,並建立路由對應的目錄。

 

6. 壓縮js,刪除console()

  yarn add terser-webpack-plugin -D

1 configureWebpack: config => {
2   const TerserPlugin = require('terser-webpack-plugin')
3   config.optimization.minimizer.push(
4     new TerserPlugin({
5       extractComments: false,
6       terserOptions: { compress: { drop_console: true } },
7     })
8   )
9 }

 

7. bundle 分析()

將 bundle 內容展示為便捷的、交互式、可縮放的樹狀圖形式。

  yarn add -D webpack-bundle-analyzer

1 configureWebpack: config => {
2   const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
3     .BundleAnalyzerPlugin
4   config.plugins.push(new BundleAnalyzerPlugin())
5 }

 

8. WebP

WebP(發音 weppy),是一種支持有損壓縮和無損壓縮的圖片文件格式,派生自圖像編碼格式 VP8。根據 Google 的測試,無損壓縮后的 WebP 比 PNG 文件少了 45% 的文件大小,即使這些 PNG 文件經過其他壓縮工具壓縮之後,WebP 還是可以減少 28% 的文件大小。

不過WebP目前在IOS上兼容性不好,可以使用JavaScript進行檢測,對支持 WebP 的用戶輸出 WebP 圖片。

 1 created() {
 2   const htmlClass = document.documentElement.classList
 3   this.checkWebpSupport() ? htmlClass.add('webps') : htmlClass.remove('webps')
 4 }
 5 
 6 checkWebpSupport() {
 7   try {
 8     return (
 9       document
10         .createElement('canvas')
11         .toDataURL('image/webp')
12         .indexOf('data:image/webp') === 0
13     )
14   } catch (err) {
15     return false
16   }
17 }

記一次BUG:

  在默認情況下,頁面加載完成執行 this.checkWebpSupport() && document.documentElement.classList.add(‘webps’),沒有問題。

  但使用了prerender-spa-plugin進行預渲染后,因為執行預渲染的瀏覽器是支持WebP的,所有會直接在頁面中加上’webps’類,所以即使瀏覽器不支持WebP,不執行此方法也會有該類名。

 

9. 網頁性能優化測試()

進行網頁測試,根據優化建議針對性的修改,提高網頁加載速度。

  https://www.googlespeed.cn/

 

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

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

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

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