「一帶一路」再受挫 泰國喊停中國湄公河開發計畫

摘錄自2020年2月6日聯合新聞網報導

泰國宣布停止由中國主導的湄公河開發計畫,理由是當地民眾反對,且北京不願提高經費在要開發的地區進行進一步調查。泰國政府副發言人特薩拉薩納庫爾(Trisulee Trisaranakul)表示,泰國政府內閣會議4日決定放棄這個項目。

路透報導,中國在2001年提出計畫,對湄公河進行疏浚,以方便大型貨船從中國的雲南省沿湄公河把商品運往泰國、老撾的港口和東南亞其他地區。

但這個計畫遭到湄公河沿岸的泰國社區民眾和環保人士的反對。他們擔心疏浚計畫會影響環境,而且只能讓中國獲利。一份泰國政府內閣的文件顯示,中國去年通知湄公河流沿岸的國家說,中國不再計畫繼續推動這個項目,可老撾和緬甸境內的疏浚工作依然繼續進行。

土地水文
生物多樣性
土地利用
國際新聞
泰國
湄公河
一帶一路
商業開發
水文

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

【其他文章推薦】

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

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

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

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

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

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

特斯拉達成「第100萬輛」電動車里程碑 單季產能也意外曝光

摘錄自2020年3月11日ETtoday報導

自2003年創立的美國電動車廠特斯拉(Tesla),近日在一輛紅色Model Y從產線離開後,正式達成量產第100萬台電動車的里程碑。儘管目前Model Y電動休旅車尚未進入交車階段,但執行長Elon Musk也提前在推特上特別慶祝一番。

實際來看,特斯拉達成量產100萬輛的里程碑並不讓人意外,因為除了美國Fremont工廠的每年50萬輛的產能外,特斯拉上海工廠也在去年正式完工, 並預計在今年生產15萬輛新車,若非新冠病毒(COVID-19)疫情影響,這項紀錄將會更早達成。

從特斯拉Fremont工廠、上海工廠的年產量來看,預計很快就會迎來第200萬輛的紀錄。

生活環境
能源轉型
國際新聞
美國
特斯拉
電動車
交通運輸

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

【其他文章推薦】

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

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

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

※回頭車貨運收費標準

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

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

防疫比環保重要?新罕布什爾州暫時重推塑膠袋

摘錄自2020年03月22日中央通訊社美國報導

美國新罕布什爾州長蘇努努今天(22日)敦促州內民眾,將可重複使用的環保袋留在家中暫時不用。他說,為對抗武漢肺炎,在此過渡期,購物宜用店家提供的新塑膠袋或紙袋。

此舉主要是愈發擔心賣場員工面對店內人潮擁擠,加上新型冠狀病毒疾病(COVID-19,武漢肺炎)具高度傳染性,且可能附著於各種物件表面,增加感染風險。州長蘇努努(Christopher Sununu)今天推文說:「由於確認(武漢肺炎病毒)社區傳染,顧及賣場裝袋人員、雜貨商和顧客潛在風險,購物者將環保袋暫留家中,這很重要。」

本週在新英格蘭醫學期刊(New England Journal of Medicine)發表的研究顯示,武漢肺炎病毒可在空氣中存活數小時,而在不同的物件表面甚至可存活數天之久。

公害污染
污染治理
國際新聞
美國
環保袋
武漢肺炎
疫情下的食衣住行
廢棄物

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

※回頭車貨運收費標準

荷蘭研究:城市污水測得病毒 或可扮演疫情預警

摘錄自2020年3月31日中央社報導

荷蘭科學家可以在一個城市的2019冠狀病毒疾病(COVID-19,武漢肺炎)病例通報前,在都市污水中發現引發這種疾病的冠狀病毒,顯示這或許有可能成為新疫情早期預警系統。

所謂的SARS-CoV-2冠狀病毒通常會從感染者的糞便中排出。位於荷蘭中部城市尼沃海恩(Nieuwegein)的KWR水資源研究所(KWR Water Research Institute)的首席微生物學家麥德瑪(Gertjan Medema)和他的同僚今(30日)表示,儘管污水不太可能成為重要的傳播途徑。但病原體在社區裡不斷增加的循環會增加它流入下水道系統的數量。

他們3月5日在阿默斯福特(Amersfoort)一座污水處理廠發現武漢肺炎的遺傳物質,當時這個位於阿姆斯特丹東南方約50公里處的城市還沒傳出任何病例。荷蘭2月27日出現境內首起2019冠狀病毒疾病病例,幾天後南部的醫療人員感染生病,顯示疫情已在社區蔓延。

他們表示,即使在武漢肺炎盛行率還很低時,就可以在污水中偵測到引發這種疾病的冠狀病毒,顯示這可以當成監控這種病毒在人口中循環的高敏感度工具。

公害污染
污染治理
國際新聞
荷蘭
疫情
水污染

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

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

Java 多線程基礎(一)基本概念

Java 多線程基礎(一)基本概念

一、併發與并行

1、併發:指兩個或多個事件在同一個時間段發生。

2、并行:指兩個或多個事件在同一時刻發生(同時發生)。

在操作系統中,安裝了多個程序,併發指的是在一段時間內宏觀上有多個程序同時運行,這在單 CPU 系統中,每一時刻只能有一道程序執行,即微觀上這些程序是分時的交替運行,只不過是給人的感覺是同時運行,那是因為分時交替運行的時間是非常短的。

而在多個 CPU 系統中,則這些可以併發執行的程序便可以分配到多個處理器上(CPU),實現多任務并行執行,即利用每個處理器來處理一個可以併發執行的程序,這樣多個程序便可以同時執行。目前電腦市場上說的多核 CPU,便是多核處理器,核 越多,并行處理的程序越多,能大大的提高電腦運行的效率。

3、注意點

單核處理器的計算機肯定是不能并行的處理多個任務的,只能是多個任務在單個CPU上併發運行。同理,線程也是一樣的,從宏觀角度上理解線程是并行運行的,但是從微觀角度上分析卻是串行運行的,即一個線程一個線程的去運行,當系統只有一個CPU時,線程會以某種順序執行多個線程,我們把這種情況稱之為線程調度。

二、線程與進程

1、進程:是指一個內存中運行的應用程序,每個進程都有一個獨立的內存空間,一個應用程序可以同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序即是一個進程從創建、運行到消亡的過程。

2、線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是可以有多個線程的,這個應用程序也可以稱之為多線程程序。

操作系統調度的最小任務單位是線程。常用的Windows、Linux等操作系統都採用搶佔式多任務,如何調度線程完全由操作系統決定,程序自己不能決定什麼時候執行,以及執行多長時間。

(一)、線程的產生

每個進程都有自己的地址空間,即進程空間,在網絡或多用戶換機下,一個服務器通常需要接收大量不確定數量用戶的併發請求,為每一個請求都創建一個進程顯然行不通(系統開銷大響應用戶請求效率低),因此操作系統中線程概念被引進。線程的改變只代表CPU的執行過程的改變,而沒有發生進程所擁有的資源的變化。

  • 線程的執行過程是線性的,儘管中間會發生中斷或者暫停,但是進程所擁有的資源只為改線狀執行過程服務,一旦發生線程切換,這些資源需要被保護起來。
  • 進程分為單線程進程和多線程進程,單線程進程宏觀來看也是線性執行過程,微觀上只有單一的執行過程。多線程進程宏觀是線性的,微觀上多個執行操作。

(二)、進程與線程的區別

  • 地址空間。同一線程共享該進程的地址空間;進程之間是獨立的地址空間,
  • 用於資源。同一進程內的線程共享本進程的資源如內存、I/O、cpu等,但是進程之間的資源是獨立的。
  • 執行過程。每個獨立的進程程有一個程序運行的入口、順序執行序列和程序入口。但是線程不能獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。

(三)、優缺點

線程執行開銷小,但是不利於資源的管理和保護。線程適合在SMP機器(雙CPU系統)上運行。進程執行開銷大,但是能夠很好的進行資源管理和保護。進程可以跨機器前移。

(四)、使用場景

對資源的管理和保護要求高,不限制開銷和效率時,使用多進程。

要求效率高,頻繁切換時,資源的保護管理要求不是很高時,使用多線程。

三、線程的狀態

線程共包括以下5種狀態,也叫生命周期。
1. 新建狀態(New)         :線程對象被創建后,就進入了新建狀態。例如,Thread thread = new Thread()。
2. 就緒狀態(Runnable):也被稱為“可執行狀態”。線程對象被創建后,其它線程調用了該對象的start()方法,從而來啟動該線程。例如,thread.start()。處於就緒狀態的線程,隨時可能被CPU調度執行。
3. 運行狀態(Running)   :線程獲取CPU權限進行執行。需要注意的是,線程只能從就緒狀態進入到運行狀態。
4. 阻塞狀態(Blocked)    :阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
    ① 等待阻塞 — 通過調用線程的wait()方法,讓線程等待某工作的完成。
    ② 同步阻塞 — 線程在獲取 synchronized 同步鎖失敗(因為鎖被其它線程所佔用),它會進入同步阻塞狀態。
    ③ 其他阻塞 — 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5. 死亡狀態(Dead)         :線程執行完了或者因異常退出了run()方法,該線程結束生命周期。

四、多線程的原理

五、進程、線程實現多任務模式

(一)、多進程模式(一個進程只有一個線程)

(二)、多線程模式(一個進程有多個線程)

(三)、多進程 + 多線程模式(複雜度最高)

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

容器技術之Dockerfile(三)

  前面我們聊到了dockerfile的 FROM、COPY 、ADD、LABEL、MAINTAINER、ENV、ARG、WORKDIR、VOLUME、EXPOSE、RUN、CMD、ENTRYPOINT指令的使用和說明,回顧請參考https://www.cnblogs.com/qiuhom-1874/tag/Dockerfile/;今天我們來聊聊剩下的dockerfile指令的使用和說明;

  1、USER:該指令用於指定運行image時的或運行dockerfile中任何RUN、CMD或ENTRYPOINT指令指定的程序時的用戶名或UID;默認情況下,container的運行身份為root用戶;語法格式 USER <UID>|<UserName>; 需要注意的是,<UID>可以為任意数字,但實踐中其必須為/etc/passwd中某用戶的有效UID,否則,docker run命令將運行失敗;

  示例: 

[root@node1 test]# cat Dockerfile 
FROM centos:7

LABEL maintainer="qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

RUN useradd nginx

USER nginx

CMD ["sleep","3000"]

[root@node1 test]# 

  提示:以上dockerfile表示在鏡像運行成容器時,以nginx用戶運行 sleep 3000

  驗證:編譯成鏡像,啟動為容器,然後進入到容器里看看sleep 3000 是否是nginx用戶在運行?

[root@node1 test]# docker build . -t test:v1
Sending build context to Docker daemon  1.051MB
Step 1/7 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/7 : LABEL maintainer="qiuhom <qiuhom@linux-1874.com>"
 ---> Running in 0f503dae4448
Removing intermediate container 0f503dae4448
 ---> d31363b96f38
Step 3/7 : LABEL version="1.0"
 ---> Running in 8dad05999903
Removing intermediate container 8dad05999903
 ---> 2281f36d7c3c
Step 4/7 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Running in d2be9ed44aee
Removing intermediate container d2be9ed44aee
 ---> 8de872e222fb
Step 5/7 : RUN useradd nginx
 ---> Running in 37bda6ba6b60
Removing intermediate container 37bda6ba6b60
 ---> dc681f95f5ca
Step 6/7 : USER nginx
 ---> Running in 97d2357826f9
Removing intermediate container 97d2357826f9
 ---> ed277ac0c482
Step 7/7 : CMD ["sleep","3000"]
 ---> Running in 0ea578fa10bc
Removing intermediate container 0ea578fa10bc
 ---> 461f6ceabc88
Successfully built 461f6ceabc88
Successfully tagged test:v1
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
test                v1                  461f6ceabc88        3 seconds ago       204MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker run --name t1 --rm -d test:v1
37e46346d6ca0ab05b67f5350d4c2a7b6b86b8d34c8d1622d78ef70b7d3dff86
[root@node1 test]# docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
37e46346d6ca        test:v1             "sleep 3000"        3 seconds ago       Up 2 seconds                            t1
[root@node1 test]# docker exec -it t1 /bin/bash
[nginx@37e46346d6ca /]$ ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
nginx         1  0.1  0.0   4364   352 ?        Ss   10:02   0:00 sleep 3000
nginx         6  0.4  0.0  11828  1808 pts/0    Ss   10:02   0:00 /bin/bash
nginx        23  0.0  0.0  51756  1708 pts/0    R+   10:02   0:00 ps aux
[nginx@37e46346d6ca /]$ exit
exit
[root@node1 test]#

  提示:可以看到基於上面的dockerfile構建的鏡像運行為容器,裏面默認跑的進程就是我們在dockerfile中指定用戶運行的進程;使用USER指定用戶運行容器里的進程,需要注意該用戶要對運行進程所需資源的所有權限;否則容器運行不起來;

  2、HEALTHCHECK:該指令用於定義如何對容器做健康狀態檢測;運行為容器后,容器里的進程不掛掉,當然容器也就不會掛掉,但是存在一種情況,容器沒有掛掉,容器里的進程無法正常提供服務了,這個時候我們就需要通過一定的手段,第一時間知道容器里的進程是否健康(是否能夠正常提供服務);healthcheck指令就是用來定義如果去檢測容器內部進程是否健康;語法格式HEALTHCHECK [OPTIONS] CMD command;其中CMD是固定格式,而後面的command是對容器里的進程做健康狀態檢查的命令;而options是用來指定對容器做健康狀態檢查的周期時間相關信息;–interval=DURATION (default: 30s),該選項用於指定對容器做健康狀態檢查的頻率,默認是30s一次;–timeout=DURATION (default: 30s),該選項用於指定對容器內部的進程做健康狀態檢查的超時時長,默認是30秒;–start-period=DURATION (default: 0s)指定對容器中的進程做健康狀態檢查延遲時間,默認0表示不延遲;這裏補充一點,之所以要延遲多少秒做健康狀態檢查是因為,docker運行為容器以後,會立刻把該容器的狀態標記為running狀態,而對於有些初始化比較慢的容器,如果馬上對它做健康狀態檢查,可能是不健康的狀態,這樣一來我們對了解容器是否健康就不是很準確了;如果配合某些工具,很可能存在檢測到容器不健康就把該容器刪除,然後重新創建,以此重複;這樣就會導致我們的容器啟動不起來; –retries=N (default: 3)表示指定對容器做健康狀態檢查的重試次數,默認是3次;也就是說檢查到容器不健康的前提或健康的前提,它都會檢查3次,如果3次檢查都是失敗狀態那麼就標記該容器不健康;而對於我們指定的命令來講,命令的返回值就決定了容器是否健康,通常命令返回值為0表示我們執行的命令正常退出,也就意味着容器是健康狀態;命令返回值為1表示容器不健康;返回值為2我們通常都是保留不使用;HEALTHCHECK NONE就表示不對容器做健康狀態檢查;

  示例:

[root@node1 test]# cat Dockerfile 
FROM centos:7

LABEL maintainer="qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

RUN yum install -y httpd 

ADD ok.html /var/www/html/

CMD ["/usr/sbin/httpd","-DFOREGROUND"]

HEALTHCHECK --interval=5s --timeout=5s --start-period=5s --retries=2 \
        CMD curl -f http://localhost/ok.html || exit 1

[root@node1 test]# 

  提示:以上HEALTHCHECK指令表示每5秒檢查一次,超時時長為5秒,延遲5秒開始檢查,重試2次;如果curl -f http://localhost/ok.html這條命令正常返回0,那麼就表示容器健康,否則就返回1,表示容器不健康;

  驗證:把以上dockerfile構建成鏡像啟動為容器,我們把ok.html刪除或移動到別的目錄,看看容器是否標記為不健康?

[root@node1 test]# docker build . -t test:v1.1
Sending build context to Docker daemon  1.052MB
Step 1/8 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/8 : LABEL maintainer="qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> d31363b96f38
Step 3/8 : LABEL version="1.0"
 ---> Using cache
 ---> 2281f36d7c3c
Step 4/8 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 8de872e222fb
Step 5/8 : RUN yum install -y httpd
 ---> Running in 9964718a2c3e
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
 * base: mirrors.bfsu.edu.cn
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-93.el7.centos will be installed
--> Processing Dependency: httpd-tools = 2.4.6-93.el7.centos for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: system-logos >= 7.92.1-1 for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.6-93.el7.centos.x86_64
--> Running transaction check
---> Package apr.x86_64 0:1.4.8-5.el7 will be installed
---> Package apr-util.x86_64 0:1.5.2-6.el7 will be installed
---> Package centos-logos.noarch 0:70.0.6-3.el7.centos will be installed
---> Package httpd-tools.x86_64 0:2.4.6-93.el7.centos will be installed
---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch          Version                    Repository   Size
================================================================================
Installing:
 httpd               x86_64        2.4.6-93.el7.centos        base        2.7 M
Installing for dependencies:
 apr                 x86_64        1.4.8-5.el7                base        103 k
 apr-util            x86_64        1.5.2-6.el7                base         92 k
 centos-logos        noarch        70.0.6-3.el7.centos        base         21 M
 httpd-tools         x86_64        2.4.6-93.el7.centos        base         92 k
 mailcap             noarch        2.1.41-2.el7               base         31 k

Transaction Summary
================================================================================
Install  1 Package (+5 Dependent packages)

Total download size: 24 M
Installed size: 32 M
Downloading packages:
warning: /var/cache/yum/x86_64/7/base/packages/apr-1.4.8-5.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
Public key for apr-1.4.8-5.el7.x86_64.rpm is not installed
--------------------------------------------------------------------------------
Total                                              2.0 MB/s |  24 MB  00:12     
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Importing GPG key 0xF4A80EB5:
 Userid     : "CentOS-7 Key (CentOS 7 Official Signing Key) <security@centos.org>"
 Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
 Package    : centos-release-7-8.2003.0.el7.centos.x86_64 (@CentOS)
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : apr-1.4.8-5.el7.x86_64                                       1/6 
  Installing : apr-util-1.5.2-6.el7.x86_64                                  2/6 
  Installing : httpd-tools-2.4.6-93.el7.centos.x86_64                       3/6 
  Installing : centos-logos-70.0.6-3.el7.centos.noarch                      4/6 
  Installing : mailcap-2.1.41-2.el7.noarch                                  5/6 
  Installing : httpd-2.4.6-93.el7.centos.x86_64                             6/6 
  Verifying  : mailcap-2.1.41-2.el7.noarch                                  1/6 
  Verifying  : apr-util-1.5.2-6.el7.x86_64                                  2/6 
  Verifying  : httpd-2.4.6-93.el7.centos.x86_64                             3/6 
  Verifying  : apr-1.4.8-5.el7.x86_64                                       4/6 
  Verifying  : httpd-tools-2.4.6-93.el7.centos.x86_64                       5/6 
  Verifying  : centos-logos-70.0.6-3.el7.centos.noarch                      6/6 

Installed:
  httpd.x86_64 0:2.4.6-93.el7.centos                                            

Dependency Installed:
  apr.x86_64 0:1.4.8-5.el7                                                      
  apr-util.x86_64 0:1.5.2-6.el7                                                 
  centos-logos.noarch 0:70.0.6-3.el7.centos                                     
  httpd-tools.x86_64 0:2.4.6-93.el7.centos                                      
  mailcap.noarch 0:2.1.41-2.el7                                                 

Complete!
Removing intermediate container 9964718a2c3e
 ---> a931e93eea06
Step 6/8 : ADD ok.html /var/www/html/
 ---> 97e61f41911d
Step 7/8 : CMD ["/usr/sbin/httpd","-DFOREGROUND"]
 ---> Running in e91ccdef90c2
Removing intermediate container e91ccdef90c2
 ---> 7c8af9bb7eb3
Step 8/8 : HEALTHCHECK --interval=5s --timeout=5s --start-period=5s --retries=2         CMD curl -f http://localhost/ok.html || exit 1
 ---> Running in 80682ab087d3
Removing intermediate container 80682ab087d3
 ---> aa53cba15046
Successfully built aa53cba15046
Successfully tagged test:v1.1
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
test                v1.1                aa53cba15046        8 seconds ago       312MB
test                v1                  461f6ceabc88        57 minutes ago      204MB
centos              7                   b5b4d78bc90c        4 weeks ago         203MB
[root@node1 test]# docker run --name t1 --rm -d test:v1.1
332590e683fcb29f60a28703548fce7aa83df715cbb840e1283472834867d6a1
[root@node1 test]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES
332590e683fc        test:v1.1           "/usr/sbin/httpd -DF…"   3 seconds ago       Up 2 seconds (health: starting)                       t1
[root@node1 test]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS               NAMES
332590e683fc        test:v1.1           "/usr/sbin/httpd -DF…"   7 seconds ago       Up 6 seconds (healthy)                       t1
[root@node1 test]# 

  提示:可以看到基於我們寫的dockerfile構建的鏡像已經成功運行為容器,並且標記為healthy;接下來我們進入容器把ok.html幹掉,然後在看看容器是否標記為不健康狀態?

  提示:從上面的信息可以看到我們把ok.html移除后,容器狀態就變成不健康狀態了;我們再把ok.html還原到原有位置,看看容器是否會從不健康轉換為健康呢?

  提示:可以看到把ok.html還原到/var/www/html/目錄后,容器從不健康狀態變為了健康狀態;

  3、SHELL:該指令用於指定默認shell,該指令開始到下一個SHELL中間的命令都是SHELL指定的shell 運行,所以SHELL指令在dockerfile中可出現多次,後面的SHELL指令指定的shell會覆蓋前面所有SHELL指令指定的shell;默認在Linux上是[“/bin/sh”,”-c”]在Windows上述[“cmd”,”/s”,”/c”];SHELL指令必須是以json數組的格式定義;語法SHELL [“executable”, “parameters”];

  4、STOPSIGNAL:該指令用於定義停止容器的信號;默認停止容器是15號信號 SIGTERM;語法STOPSIGNAL signal

  5、ONBUILD:該指令用於在Dockerfile中定義一個觸發器;Dockerfile用於build映像文件,此映像文件亦可作為base image被另一個Dockerfile用作FROM指令的參數,並以之構建新的映像文件;在後面的這個Dockerfile中的FROM指令在build過程中被執行時,將會“觸發”創建其base image的Dockerfile文件中的ONBUILD指令定義的觸發器;用法格式ONBUILD <INSTRUCTION>;儘管任何指令都可註冊成為觸發器指令,但ONBUILD不能自我嵌套,且不會觸發FROM和MAINTAINER指令;使用包含ONBUILD指令的Dockerfile構建的鏡像應該使用特殊的標籤,例如ruby:2.0-onbuild;在ONBUILD指令中使用ADD或COPY指令應該格外小心,因為新構建過程的上下文在缺少指定的源文件時會失敗;

  示例:

[root@node1 test]# cat Dockerfile
FROM centos:7

LABEL maintainer="qiuhom <qiuhom@linux-1874.com>"

ONBUILD RUN yum install -y httpd




[root@node1 test]# 

  提示:以上dockerfile表示在本次構建鏡像中不運行yum install -y httpd這條命令,而是在後面的dockerfile中以本dockerfile製作的進行作為基礎繼續時,yum install -y httpd這條命令就會被觸發執行;簡單講onbuild就是指定dockerfile指令延遲執行;這裏一定要記住一點onbuild指令後面一定是跟的是dockerfile指令;

  驗證:將上面的dockerfile編譯鏡像,看看yum install -y httpd 是否執行了?

[root@node1 test]# docker build . -t test:v1.5
Sending build context to Docker daemon  1.052MB
Step 1/3 : FROM centos:7
 ---> b5b4d78bc90c
Step 2/3 : LABEL maintainer="qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> d31363b96f38
Step 3/3 : ONBUILD RUN yum install -y httpd
 ---> Running in d3601fa1c3b7
Removing intermediate container d3601fa1c3b7
 ---> 370e3a843c3c
Successfully built 370e3a843c3c
Successfully tagged test:v1.5
[root@node1 test]# 

  提示:可以看到yum install -y httpd 這條命令並沒有執行;

  驗證:將我們上面製作好的鏡像作為基礎鏡像,再來製作其他鏡像,看看yum install -y httpd 被執行?

[root@node1 aaa]# pwd
/root/test/aaa
[root@node1 aaa]# ls
Dockerfile
[root@node1 aaa]# cat Dockerfile 
FROM test:v1.5

LABEL maintainer="qiuhom <admin@admin.com>"
[root@node1 aaa]# docker build . -t myweb:v1
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM test:v1.5
# Executing 1 build trigger
 ---> Running in cf93e9f03e89
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
 * base: mirrors.huaweicloud.com
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-93.el7.centos will be installed
--> Processing Dependency: httpd-tools = 2.4.6-93.el7.centos for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: system-logos >= 7.92.1-1 for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.6-93.el7.centos.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.6-93.el7.centos.x86_64
--> Running transaction check
---> Package apr.x86_64 0:1.4.8-5.el7 will be installed
---> Package apr-util.x86_64 0:1.5.2-6.el7 will be installed
---> Package centos-logos.noarch 0:70.0.6-3.el7.centos will be installed
---> Package httpd-tools.x86_64 0:2.4.6-93.el7.centos will be installed
---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch          Version                    Repository   Size
================================================================================
Installing:
 httpd               x86_64        2.4.6-93.el7.centos        base        2.7 M
Installing for dependencies:
 apr                 x86_64        1.4.8-5.el7                base        103 k
 apr-util            x86_64        1.5.2-6.el7                base         92 k
 centos-logos        noarch        70.0.6-3.el7.centos        base         21 M
 httpd-tools         x86_64        2.4.6-93.el7.centos        base         92 k
 mailcap             noarch        2.1.41-2.el7               base         31 k

Transaction Summary
================================================================================
Install  1 Package (+5 Dependent packages)

Total download size: 24 M
Installed size: 32 M
Downloading packages:
warning: /var/cache/yum/x86_64/7/base/packages/apr-1.4.8-5.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
Public key for apr-1.4.8-5.el7.x86_64.rpm is not installed
--------------------------------------------------------------------------------
Total                                              7.2 MB/s |  24 MB  00:03     
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Importing GPG key 0xF4A80EB5:
 Userid     : "CentOS-7 Key (CentOS 7 Official Signing Key) <security@centos.org>"
 Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
 Package    : centos-release-7-8.2003.0.el7.centos.x86_64 (@CentOS)
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : apr-1.4.8-5.el7.x86_64                                       1/6 
  Installing : apr-util-1.5.2-6.el7.x86_64                                  2/6 
  Installing : httpd-tools-2.4.6-93.el7.centos.x86_64                       3/6 
  Installing : centos-logos-70.0.6-3.el7.centos.noarch                      4/6 
  Installing : mailcap-2.1.41-2.el7.noarch                                  5/6 
  Installing : httpd-2.4.6-93.el7.centos.x86_64                             6/6 
  Verifying  : mailcap-2.1.41-2.el7.noarch                                  1/6 
  Verifying  : apr-util-1.5.2-6.el7.x86_64                                  2/6 
  Verifying  : httpd-2.4.6-93.el7.centos.x86_64                             3/6 
  Verifying  : apr-1.4.8-5.el7.x86_64                                       4/6 
  Verifying  : httpd-tools-2.4.6-93.el7.centos.x86_64                       5/6 
  Verifying  : centos-logos-70.0.6-3.el7.centos.noarch                      6/6 

Installed:
  httpd.x86_64 0:2.4.6-93.el7.centos                                            

Dependency Installed:
  apr.x86_64 0:1.4.8-5.el7                                                      
  apr-util.x86_64 0:1.5.2-6.el7                                                 
  centos-logos.noarch 0:70.0.6-3.el7.centos                                     
  httpd-tools.x86_64 0:2.4.6-93.el7.centos                                      
  mailcap.noarch 0:2.1.41-2.el7                                                 

Complete!
Removing intermediate container cf93e9f03e89
 ---> a89914bda4b5
Step 2/2 : LABEL maintainer="qiuhom <admin@admin.com>"
 ---> Running in e175e0542b5e
Removing intermediate container e175e0542b5e
 ---> 4f406abeaab7
Successfully built 4f406abeaab7
Successfully tagged myweb:v1
[root@node1 aaa]#

  提示:可以看到在我們的dockerfile中並沒有寫 RUN  yum install -y httpd  ,但build時卻執行了 yum install -y httpd ;這是因為onbuild指令被觸發了;我們可以理解為如果我們製作的鏡像有onbuild指令指定的命令,那麼該鏡像被其他dockerfile 作為基礎鏡像時(或者被其他docker FROM指令引用時)onbuild指定就會被激活,被執行;

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

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

“造輪運動”之 ORM框架系列(三)~ 乾貨呈上

   這一趴裏面,我就來正式介紹一下CoffeeSQL的乾貨。

    首先要給CoffeeSQL來個定位:最開始就是由於本人想要了解ORM框架內部的原理,所以就四處搜尋有關的博客與學習資料,就是在那個夏天,在博客園上看到了一位7tiny老哥的博客(https://www.cnblogs.com/7tiny/p/9575230.html),裏面基本上包含了我所想要了解的全套內容。幸得7tiny老哥的博客和代碼都寫的非常清晰,所以沒花多久時間就看完了源碼並洞悉其中奧妙,於是自己就有個想法:在7tiny的開源代碼的基礎上歸納自己的ORM框架。於是出於學習與自我使用的目的就開始了擴展功能的道路,到現在為止,自己已經在公司的一個項目中用上了,效果還不錯。在這裏也感謝7tiny老哥對我提出的一些問題及時的回復和指導,真心感謝。

一、框架模塊介紹

  根據CoffeeSQL的功能模塊組成來劃分,可以分為:數據庫連接管理、SQL命令執行入口、SQL命令生成器、SQL查詢引擎、ORM緩存機制、實體數據驗證 這六個部分,CoffeeSQL的操作入口與其他的ORM框架一樣,都是以數據庫上下文(DBContext)的方式進行操作。整體結構圖如下:

 

下面就大致地介紹一下每一個模塊的具體功能與實現的思路:

1、數據庫連接管理(DBConnectionManagement)

   數據庫連接的管理實際上就是對數據庫連接字符串與其對應的數據庫連接對象的管理機制,它可以保證在進行一主多從的數據庫部署時ORM幫助我們自動地切換連接的數據庫,而且還支持 <最小使用>與 <輪詢>兩種數據庫連接切換策略。

 

2、SQL命令執行入口(QueryExecute)

   QueryExecute是CoffeeSQL生成的所有sql語句執行的入口,執行sql語句並返回結果,貫穿整個CoffeeSQL最核心的功能就是映射sql查詢結果到實體,這裏採用的是構建表達式樹的技術,性能大大優於反射獲取實體的方式,具體的兩者速度對比的實驗在7tiny的博客中有詳細介紹,大家可以移步觀看(https://www.cnblogs.com/7tiny/p/9861166.html),在我的博客(https://www.cnblogs.com/MaMaNongNong/p/12173620.html)中我使用表達式樹的技術造了個簡練版的OOM框架。

   這裏貼出核心代碼,方便查看:

   

  1     /// <summary>
  2     /// Auto Fill Adapter
  3     /// => Fill DataRow to Entity
  4     /// </summary>
  5     public class EntityFillAdapter<Entity>
  6     {
  7         private static readonly Func<DataRow, Entity> funcCache = GetFactory();
  8 
  9         public static Entity AutoFill(DataRow row)
 10         {
 11             return funcCache(row);
 12         }
 13 
 14         private static Func<DataRow, Entity> GetFactory()
 15         {
 16             #region get Info through Reflection
 17             var entityType = typeof(Entity);
 18             var rowType = typeof(DataRow);
 19             var convertType = typeof(Convert);
 20             var typeType = typeof(Type);
 21             var columnCollectionType = typeof(DataColumnCollection);
 22             var getTypeMethod = typeType.GetMethod("GetType", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(string) }, null);
 23             var changeTypeMethod = convertType.GetMethod("ChangeType", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(object), typeof(Type) }, null);
 24             var containsMethod = columnCollectionType.GetMethod("Contains");
 25             var rowIndexerGetMethod = rowType.GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, new[] { new ParameterModifier(1) });
 26             var columnCollectionIndexerGetMethod = columnCollectionType.GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(int) }, new[] { new ParameterModifier(1) });
 27             var entityIndexerSetMethod = entityType.GetMethod("set_Item", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(string), typeof(object) }, null);
 28             var properties = entityType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
 29             #endregion
 30 
 31             #region some Expression class that can be repeat used
 32             //DataRow row
 33             var rowDeclare = Expression.Parameter(rowType, "row");
 34             //Student entity
 35             var entityDeclare = Expression.Parameter(entityType, "entity");
 36             //Type propertyType
 37             var propertyTypeDeclare = Expression.Parameter(typeof(Type), "propertyType");
 38             //new Student()
 39             var newEntityExpression = Expression.New(entityType);
 40             //row == null
 41             var rowEqualnullExpression = Expression.Equal(rowDeclare, Expression.Constant(null));
 42             //row.Table.Columns
 43             var rowTableColumns = Expression.Property(Expression.Property(rowDeclare, "Table"), "Columns");
 44             //int loopIndex
 45             var loopIndexDeclare = Expression.Parameter(typeof(int), "loopIndex");
 46             //row.Table.Columns[loopIndex].ColumnName
 47             var columnNameExpression = Expression.Property(Expression.Call(rowTableColumns, columnCollectionIndexerGetMethod, loopIndexDeclare), "ColumnName");
 48             //break;
 49             LabelTarget labelBreak = Expression.Label();
 50             //default(Student)
 51             var defaultEntityValue = Expression.Default(entityType);
 52             #endregion
 53 
 54             var setRowNotNullBlockExpressions = new List<Expression>();
 55                         
 56             #region entity = new Student();loopIndex = 0;
 57             setRowNotNullBlockExpressions.Add(Expression.Assign(entityDeclare, newEntityExpression));
 58             setRowNotNullBlockExpressions.Add(Expression.Assign(loopIndexDeclare, Expression.Constant(0)));
 59 
 60             #endregion
 61 
 62             #region loop Fill DataRow's field to Entity Indexer
 63             /*
 64              * while (true)
 65              * {
 66              *     if (loopIndex < row.Table.Columns.Count)
 67              *     {
 68              *         entity[row.Table.Columns[loopIndex].ColumnName] = row[row.Table.Columns[loopIndex].ColumnName];
 69              *         loopIndex++;
 70              *     }
 71              *     else break;
 72              * } 
 73              */
 74 
 75             setRowNotNullBlockExpressions.Add(
 76 
 77                 Expression.Loop(
 78                     Expression.IfThenElse(
 79                         Expression.LessThan(loopIndexDeclare, Expression.Property(rowTableColumns, "Count")),
 80                         Expression.Block(
 81                             Expression.Call(entityDeclare, entityIndexerSetMethod, columnNameExpression, Expression.Call(rowDeclare, rowIndexerGetMethod, columnNameExpression)),
 82                             Expression.PostIncrementAssign(loopIndexDeclare)
 83                         ),
 84                         Expression.Break(labelBreak)
 85                     ),
 86                     labelBreak
 87                 )
 88             );
 89             #endregion
 90 
 91             #region assign for Entity property
 92             foreach (var propertyInfo in properties)
 93             {
 94                 var columnAttr = propertyInfo.GetCustomAttribute(typeof(ColumnAttribute), true) as ColumnAttribute;
 95 
 96                 // no column , no translation
 97                 if (null == columnAttr) continue;
 98 
 99                 if (propertyInfo.CanWrite)
100                 {
101                     var columnName = Expression.Constant(columnAttr.GetName(propertyInfo.Name), typeof(string));
102 
103                     //entity.Id
104                     var propertyExpression = Expression.Property(entityDeclare, propertyInfo);
105                     //row["Id"]
106                     var value = Expression.Call(rowDeclare, rowIndexerGetMethod, columnName);
107                     //default(string)
108                     var defaultValue = Expression.Default(propertyInfo.PropertyType);
109                     //row.Table.Columns.Contains("Id")
110                     var checkIfContainsColumn = Expression.Call(rowTableColumns, containsMethod, columnName);
111                     //!row["Id"].Equals(DBNull.Value)
112                     var checkDBNull = Expression.NotEqual(value, Expression.Constant(System.DBNull.Value));
113                     
114                     var propertyTypeName = Expression.Constant(propertyInfo.PropertyType.ToString(), typeof(string));
115 
116                     /*
117                      * if (row.Table.Columns.Contains("Id") && !row["Id"].Equals(DBNull.Value))
118                      * {
119                      *     propertyType = Type.GetType("System.String");
120                      *     entity.Id = (string)Convert.ChangeType(row["Id"], propertyType);
121                      * }
122                      * else
123                      *     entity.Id = default(string);
124                      */
125                     setRowNotNullBlockExpressions.Add(
126 
127                         Expression.IfThenElse(
128                             Expression.AndAlso(checkIfContainsColumn, checkDBNull),
129                             Expression.Block(
130                                 Expression.Assign(propertyTypeDeclare, Expression.Call(getTypeMethod, propertyTypeName)),
131                                 Expression.Assign(propertyExpression, Expression.Convert(Expression.Call(changeTypeMethod, value, propertyTypeDeclare), propertyInfo.PropertyType))
132                             ),
133                             Expression.Assign(propertyExpression, defaultValue)
134                         )
135                     );
136                 }
137             }
138 
139             #endregion
140 
141             var checkIfRowIsNull = Expression.IfThenElse(
142                 rowEqualnullExpression,
143                 Expression.Assign(entityDeclare, defaultEntityValue),
144                 Expression.Block(setRowNotNullBlockExpressions)
145             );
146 
147             var body = Expression.Block(
148 
149                 new[] { entityDeclare, loopIndexDeclare, propertyTypeDeclare },
150                 checkIfRowIsNull,
151                 entityDeclare   //return Student;
152             );
153 
154             return Expression.Lambda<Func<DataRow, Entity>>(body, rowDeclare).Compile();
155         }
156     }
157 
158     #region
159     //public class Student : EntityDesign.EntityBase
160     //{
161     //    [Column]
162     //    public string Id { get; set; }
163 
164     //    [Column("StudentName")]
165     //    public string Name { get; set; }
166     //}
167     ////this is the template of "GetFactory()" created.
168     //public static Student StudentFillAdapter(DataRow row)
169     //{
170     //    Student entity;
171     //    int loopIndex;
172     //    Type propertyType;
173 
174     //    if (row == null)
175     //        entity = default(Student);
176     //    else
177     //    {
178     //        entity = new Student();
179     //        loopIndex = 0;
180 
181     //        while (true)
182     //        {
183     //            if (loopIndex < row.Table.Columns.Count)
184     //            {
185     //                entity[row.Table.Columns[loopIndex].ColumnName] = row[row.Table.Columns[loopIndex].ColumnName];
186     //                loopIndex++;
187     //            }
188     //            else break;
189     //        }
190 
191     //        if (row.Table.Columns.Contains("Id") && !row["Id"].Equals(DBNull.Value))
192     //        {
193     //            propertyType = Type.GetType("System.String");
194     //            entity.Id = (string)Convert.ChangeType(row["Id"], propertyType);
195     //        }
196     //        else
197     //            entity.Id = default(string);
198 
199     //        if (row.Table.Columns.Contains("StudentName") && !row["StudentName"].Equals(DBNull.Value))
200     //        {
201     //            propertyType = Type.GetType("System.String");
202     //            entity.Name = (string)Convert.ChangeType(row["StudentName"], propertyType);
203     //        }
204     //        else
205     //            entity.Name = default(string);
206     //    }
207 
208     //    return entity;
209     //}
210     #endregion

EntityFillAdapter(表達式樹技術)

 

3、SQL查詢引擎(QueryEngine)

  SQL查詢引擎的功能主要就是以函數的形式來構建查詢SQL的結構。將sql語句使用高級語言的函數來進行構建能大大減輕程序員必須一絲不苟編寫sql語句的壓力。特別是在使用強類型查詢引擎時以Lambda表達式的方式編寫程序,相當舒適的體驗;對於稍微複雜的sql,建議使用弱類型查詢引擎來構建sql查詢語句,同時也提供方便的分頁功能,用法與Dapper類似;再複雜一點的數據庫查詢邏輯可能你就要考慮使用存儲過程查詢引擎了,總之,有了這三個查詢引擎,所有的查詢需求都能滿足了。最後一個是update的執行引擎,它被用來構建update的語句。

 

4、實體數據驗證(EntityValidation)

  實體數據驗證是完全獨立的一部分,主要用來檢驗實體類中字段值的合法性,相當於在高級語言層面對即將持久化到數據庫表中的數據進行預先的字段合法性校驗,避免在持久化過程中發生不必要的字段格式不合法的錯誤。

 

5、ORM緩存機制(ORMCache)

  這裏的ORM緩存主要分為兩級緩存,一級緩存為以sql語句為緩存鍵的緩存,緩存的內容就是當前執行的sql語句的執行結果;而二級緩存則是以表名為緩存鍵的表緩存,就是會把一整個表的數據全部存入緩存中,所以表緩存最適合那些數據量不大且查詢頻繁的表

 

6、SQL命令生成器【強類型】(CommandTextGenerator)

  在使用諸如強類型查詢引擎、Update執行引擎等進行了強類型的SQL語句構造后,相應的sql構造信息都要通過SQL命令生成器來生成最終可由數據庫執行的sql語句。SQL命令生成器扮演的就是類似於翻譯官的角色,將高級語言中的語句轉化為數據庫中的sql語句。在實際的應用場景中還可以根據不同的數據庫類型將SQL命令生成器擴展成諸如Mysql-SQL命令生成器或者Oracle-SQL命令生成器以符合不同類型數據庫的不同sql語法。

 

7、數據庫上下文(DBContext)

  作為整個CoffeeSQL的操作入口,DBContext類涵蓋了各種配置參数字段與增刪改查的API調用函數。其中在事務處理中,由於寫操作都是通過對主庫的操作,所以在事務處理中是以主庫作為事務處理的對象。

二、使用方式

  下載CoffeeSql源碼進行編譯,你會得到 CoffeeSql.Core.dll、CoffeeSql.Oracle.dll、CoffeeSql.Mysql.dll 三個dll文件,其中CoffeeSql.Core.dll為必選,然後根據你的數據庫類型選擇是CoffeeSql.Oracle.dll或者CoffeeSql.Mysql.dll,目前還只支持這兩種數據庫,後續會支持更多數據庫。

 

 

三、展望

  路漫漫其修遠兮,吾將上下而求索,對比市面上火熱的ORM框架,CoffeeSQL還是缺少了一些實用的功能,對這個ORM框架的展望中我會考慮以下一些功能:

    1、CodeFirst、DbFirst功能的支持,可以快捷方便地進行實體類與數據庫建表sql的生成;

    2、批量插入操作的實現,可以提高批量插入數據的性能;

    3、對多表聯合查詢的lambda語法支持;

  

  介紹的再多都不如讀一遍源碼來的實在,有想深入了解orm原理的小夥伴可以閱讀一下源碼,真的SO EASY!

   源碼地址:https://gitee.com/xiaosen123/CoffeeSqlORM

   本文為作者原創,轉載請註明出處:https://www.cnblogs.com/MaMaNongNong/p/12896787.html

 

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

Alink漫談(六) : TF-IDF算法的實現

Alink漫談(六) : TF-IDF算法的實現

目錄

  • Alink漫談(六) : TF-IDF算法的實現
    • 0x00 摘要
    • 0x01 TF-IDF
      • 1.1 原理
      • 1.2 計算方法
    • 0x02 Alink示例代碼
      • 2.1 示例代碼
      • 2.2 TF-IDF模型
      • 2.3 TF-IDF預測
    • 0x03 分詞 Segment
      • 3.1 結巴分詞
      • 3.2 分詞過程
    • 0x04 訓練
      • 4.1 計算IDF
      • 4.2 排序
        • 4.2.1 SortUtils.pSort
          • 採樣SampleSplitPoint
          • 歸併 SplitPointReducer
          • SplitData把真實數據IDF插入
          • reduceGroup計算同類型單詞數目
        • 4.2.2 localSort
      • 4.3 過濾
    • 0x05 生成模型
      • 5.1 DocCountVectorizerModelData
      • 5.2 BuildDocCountModel
    • 0x06 預測
    • 0x07 參考

0x00 摘要

Alink 是阿里巴巴基於實時計算引擎 Flink 研發的新一代機器學習算法平台,是業界首個同時支持批式算法、流式算法的機器學習平台。TF-IDF(term frequency–inverse document frequency)是一種用於信息檢索與數據挖掘的常用加權技術。本文將為大家展現Alink如何實現TF-IDF。

0x01 TF-IDF

TF-IDF(term frequency–inverse document frequency)是一種統計方法,一種用於信息檢索與數據挖掘的常用加權技術。

TF是詞頻(Term Frequency),IDF是逆文本頻率指數(Inverse Document Frequency)。

為什麼要用TF-IDF?因為計算機只能識別数字,對於一個一個的單詞,計算機是看不懂的,更別說是一句話,或是一篇文章。而TF-IDF就是用來將文本轉換成計算機看得懂的語言,或者說是機器學習或深度學習模型能夠進行學習訓練的數據集

1.1 原理

TF-IDF用以評估一個詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨着它在文件中出現的次數成正比增加,但同時會隨着它在語料庫中出現的頻率成反比下降。

TF-IDF的主要思想是:如果某個詞或短語在一篇文章中出現的頻率TF高,並且在其他文章中很少出現,則認為此詞或者短語具有很好的類別區分能力,適合用來分類。

TF-IDF實際上是:TF * IDF,TF詞頻(Term Frequency),IDF逆向文件頻率(Inverse Document Frequency)。

詞頻(term frequency,TF)指的是某一個給定的詞語在該文件中出現的頻率。這個数字是對詞數(term count)的歸一化,以防止它偏向長的文件(同一個詞語在長文件里可能會比短文件有更高的詞數,而不管該詞語重要與否)。

IDF逆向文件頻率 (inverse document frequency, IDF)反應了一個詞在所有文本(整個文檔)中出現的頻率,如果一個詞在很多的文本中出現,那麼它的IDF值應該低。而反過來如果一個詞在比較少的文本中出現,那麼它的IDF值應該高。比如一些專業的名詞如“Machine Learning”。這樣的詞IDF值應該高。一個極端的情況,如果一個詞在所有的文本中都出現,那麼它的IDF值應該為0。

如果單單以TF或者IDF來計算一個詞的重要程度都是片面的,因此TF-IDF綜合了TF和IDF兩者的優點,用以評估一字詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨着它在文件中出現的次數成正比增加,但同時會隨着它在語料庫中出現的頻率成反比下降。上述引用總結就是:一個詞語在一篇文章中出現次數越多, 同時在所有文檔中出現次數越少, 越能夠代表該文章,越能與其它文章區分開來。

1.2 計算方法

TF的計算公式如下:

\[TF_w = \frac {N_w}{N} \]

其中 N_w 是在某一文本中詞條w出現的次數,N 是該文本總詞條數。

IDF的計算公式如下:

\[IDF_w = log(\frac {Y}{Y_w + 1}) \]

其中 Y 是語料庫的文檔總數,Y_w 是包含詞條w的文檔數,分母加一是為了避免w 未出現在任何文檔中從而導致分母為0 的情況。

TF-IDF 就是將TF和IDF相乘 :

\[TF-IDF_w = TF_w * IDF_w \]

從以上計算公式便可以看出,某一特定文件內的高詞語頻率,以及該詞語在整個文件集合中的低文件頻率,可以產生出高權重的TF-IDF。因此,TF-IDF傾向於過濾掉常見的詞語,保留重要的詞語。

0x02 Alink示例代碼

2.1 示例代碼

首先我們給出示例代碼,下文是通過一些語料來訓練出一個模型,然後用這個模型來做預測:

public class DocCountVectorizerExample {

    AlgoOperator getData(boolean isBatch) {
        Row[] rows = new Row[]{
                Row.of(0, "二手舊書:醫學電磁成像"),
                Row.of(1, "二手美國文學選讀( 下冊 )李宜燮南開大學出版社 9787310003969"),
                Row.of(2, "二手正版圖解象棋入門/謝恩思主編/華齡出版社"),
                Row.of(3, "二手中國糖尿病文獻索引"),
                Row.of(4, "二手郁達夫文集( 國內版 )全十二冊館藏書")
        };

        String[] schema = new String[]{"id", "text"};

        if (isBatch) {
            return new MemSourceBatchOp(rows, schema);
        } else {
            return new MemSourceStreamOp(rows, schema);
        }
    }

    public static void main(String[] args) throws Exception {
        DocCountVectorizerExample test = new DocCountVectorizerExample();
        BatchOperator batchData = (BatchOperator) test.getData(true);

         // 分詞
        SegmentBatchOp segment = new SegmentBatchOp() 
                                                .setSelectedCol("text")
                                                .linkFrom(batchData);
        // TF-IDF訓練
        DocCountVectorizerTrainBatchOp model = new DocCountVectorizerTrainBatchOp()
                                                .setSelectedCol("text")
                                                .linkFrom(segment);
        // TF-IDF預測
        DocCountVectorizerPredictBatchOp predictBatch = new 
            																		DocCountVectorizerPredictBatchOp()
                                                .setSelectedCol("text")
                                                .linkFrom(model, segment);
        model.print();
        predictBatch.print();
    }
}

2.2 TF-IDF模型

TF-IDF模型打印出來如下:

model_id|model_info
--------|----------
0|{"minTF":"1.0","featureType":"\"WORD_COUNT\""}
1048576|{"f0":"二手","f1":0.0,"f2":0}
2097152|{"f0":"/","f1":1.0986122886681098,"f2":1}
3145728|{"f0":"出版社","f1":0.6931471805599453,"f2":2}
4194304|{"f0":")","f1":0.6931471805599453,"f2":3}
5242880|{"f0":"(","f1":0.6931471805599453,"f2":4}
6291456|{"f0":"入門","f1":1.0986122886681098,"f2":5}
......
36700160|{"f0":"美國","f1":1.0986122886681098,"f2":34}
37748736|{"f0":"謝恩","f1":1.0986122886681098,"f2":35}
38797312|{"f0":"象棋","f1":1.0986122886681098,"f2":36}

2.3 TF-IDF預測

TF-IDF預測結果如下:

id|text
--|----
0|$37$0:1.0 6:1.0 10:1.0 25:1.0 26:1.0 28:1.0
1|$37$0:1.0 1:1.0 2:1.0 4:1.0 11:1.0 15:1.0 16:1.0 19:1.0 20:1.0 32:1.0 34:1.0
2|$37$0:1.0 3:2.0 4:1.0 5:1.0 8:1.0 22:1.0 23:1.0 24:1.0 29:1.0 35:1.0 36:1.0
3|$37$0:1.0 12:1.0 27:1.0 31:1.0 33:1.0
4|$37$0:1.0 1:1.0 2:1.0 7:1.0 9:1.0 13:1.0 14:1.0 17:1.0 18:1.0 21:1.0 30:1.0

0x03 分詞 Segment

中文分詞(Chinese Word Segmentation) 指的是將一個漢字序列切分成一個一個單獨的詞。分詞就是將連續的字序列按照一定的規範重新組合成詞序列的過程。

示例代碼中,分詞部分如下:

    SegmentBatchOp segment = new SegmentBatchOp() 
                                            .setSelectedCol("text")
                                            .linkFrom(batchData);

分詞主要是如下兩個類,其作用就是把中文文檔分割成單詞。

public final class SegmentBatchOp extends MapBatchOp <SegmentBatchOp>
	implements SegmentParams <SegmentBatchOp> {

	public SegmentBatchOp(Params params) {
		super(SegmentMapper::new, params);
	}
}

public class SegmentMapper extends SISOMapper {
	private JiebaSegmenter segmentor;
}

3.1 結巴分詞

有經驗的同學看到這裏就會露出微笑:結巴分詞。

jieba分詞是國內使用人數最多的中文分詞工具https://github.com/fxsjy/jieba。jieba分詞支持四種分詞模式:

  • 精確模式,試圖將句子最精確地切開,適合文本分析;
  • 全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;
  • 搜索引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜索引擎分詞。
  • paddle模式,利用PaddlePaddle深度學習框架,訓練序列標註(雙向GRU)網絡模型實現分詞。

Alink使用了com.alibaba.alink.operator.common.nlp.jiebasegment.viterbi.FinalSeg;來 完成分詞。具體是在https://github.com/huaban/jieba-analysis的基礎上稍微做了調整。

public class JiebaSegmenter implements Serializable {
    private static FinalSeg finalSeg = FinalSeg.getInstance();
    private WordDictionary wordDict;
    ......
    private Map<Integer, List<Integer>> createDAG(String sentence) 
}

從Alink代碼中看,實現了索引分詞和查詢分詞兩種模式,應該是有分詞粒度粗細之分。

createDAG函數的作用是 :在處理句子過程中,基於前綴詞典實現高效的詞圖掃描,生成句子中漢字所有可能成詞情況所構成的有向無環圖 (DAG)。

結巴分詞對於未登錄詞,採用了基於漢字成詞能力的 HMM 模型,使用了 Viterbi 算法。

3.2 分詞過程

分詞過程主要是在SegmentMapper.mapColumn函數中完成的,當輸入是 “二手舊書:醫學電磁成像”,結巴分詞將這個句子分成了六個單詞。具體參見如下:

input = "二手舊書:醫學電磁成像"
tokens = {ArrayList@9619}  size = 6
 0 = {SegToken@9630} "[二手, 0, 2]"
 1 = {SegToken@9631} "[舊書, 2, 4]"
 2 = {SegToken@9632} "[:, 4, 5]"
 3 = {SegToken@9633} "[醫學, 5, 7]"
 4 = {SegToken@9634} "[電磁, 7, 9]"
 5 = {SegToken@9635} "[成像, 9, 11]"
 
mapColumn:44, SegmentMapper (com.alibaba.alink.operator.common.nlp)
apply:-1, 35206803 (com.alibaba.alink.common.mapper.SISOMapper$$Lambda$646)
handleMap:75, SISOColsHelper (com.alibaba.alink.common.mapper)
map:52, SISOMapper (com.alibaba.alink.common.mapper)
map:21, MapperAdapter (com.alibaba.alink.common.mapper)
map:11, MapperAdapter (com.alibaba.alink.common.mapper)
collect:79, ChainedMapDriver (org.apache.flink.runtime.operators.chaining)
collect:35, CountingCollector (org.apache.flink.runtime.operators.util.metrics)
invoke:196, DataSourceTask (org.apache.flink.runtime.operators)

0x04 訓練

訓練是在DocCountVectorizerTrainBatchOp類完成的,其通過linkFrom完成了模型的構建。其實計算TF IDF相對 簡單,複雜之處在於之後的大規模排序。

public DocCountVectorizerTrainBatchOp linkFrom(BatchOperator<?>... inputs) {
        BatchOperator<?> in = checkAndGetFirst(inputs);
  
        DataSet<DocCountVectorizerModelData> resDocCountModel = generateDocCountModel(getParams(), in);

        DataSet<Row> res = resDocCountModel.mapPartition(new MapPartitionFunction<DocCountVectorizerModelData, Row>() {
            @Override
            public void mapPartition(Iterable<DocCountVectorizerModelData> modelDataList, Collector<Row> collector) {
                new DocCountVectorizerModelDataConverter().save(modelDataList.iterator().next(), collector);
            }
        });
        this.setOutput(res, new DocCountVectorizerModelDataConverter().getModelSchema());
        return this;
}

4.1 計算IDF

計算 IDF 的工作是在generateDocCountModel完成的,具體步驟如下:

第一步 通過DocWordSplitCount和UDTF的混合使用得到了文檔中的單詞數目docWordCnt

BatchOperator<?> docWordCnt = in.udtf(
        params.get(SELECTED_COL),
        new String[] {WORD_COL_NAME, DOC_WORD_COUNT_COL_NAME},
        new DocWordSplitCount(NLPConstant.WORD_DELIMITER),
        new String[] {});

DocWordSplitCount.eval的輸入是已經分詞的句子,然後按照空格分詞,按照單詞計數。其結果是:

map = {HashMap@9816}  size = 6
 "醫學" -> {Long@9833} 1
 "電磁" -> {Long@9833} 1
 ":" -> {Long@9833} 1
 "成像" -> {Long@9833} 1
 "舊書" -> {Long@9833} 1
 "二手" -> {Long@9833} 1

第二步 得到了文檔數目docCnt

BatchOperator docCnt = in.select("COUNT(1) AS " + DOC_COUNT_COL_NAME);

這個數目會廣播出去 .withBroadcastSet(docCnt.getDataSet(), "docCnt");,後面的CalcIdf會繼續使用,進行 行數統計。

第三步 會通過CalcIdf計算出每一個單詞的DF和IDF

open時候會獲取docCnt。然後reduce會計算IDF,具體計算如下:

double idf = Math.log((1.0 + docCnt) / (1.0 + df));
collector.collect(Row.of(featureName, -wordCount, idf));

具體得到如下

df = 1.0
wordCount = 1.0
featureName = "中國"
idf = 1.0986122886681098
docCnt = 5

這裏一個重點是:返回值中,是 -wordCount,因為單詞越多權重越小,為了比較所以取負

4.2 排序

得到所有單詞的IDF之後,就得到了一個IDF字典,這時候需要對字典按照權重進行排序。排序具體分為兩步。

4.2.1 SortUtils.pSort

第一步是SortUtils.pSort,大規模并行抽樣排序。

Tuple2<DataSet<Tuple2<Integer, Row>>, DataSet<Tuple2<Integer, Long>>> partitioned = SortUtils.pSort(sortInput, 1);

這步非常複雜,Alink參考了論文,如果有興趣的兄弟可以深入了解下。

* reference: Yang, X. (2014). Chong gou da shu ju tong ji (1st ed., pp. 25-29).
* Note: This algorithm is improved on the base of the parallel sorting by regular sampling(PSRS).

pSort返回值是:

* @return f0: dataset which is indexed by partition id, f1: dataset which has partition id and count.

pSort中又分如下幾步

採樣SampleSplitPoint

SortUtils.SampleSplitPoint.mapPartition這裏完成了採樣。

DataSet <Tuple2 <Object, Integer>> splitPoints = input
   .mapPartition(new SampleSplitPoint(index))
   .reduceGroup(new SplitPointReducer());

這裏的輸入row就是上文IDF的返回數值。

用allValues記錄了本task目前處理的句子有多少個單詞。

用splitPoints做了採樣。如何選擇呢,通過genSampleIndex函數。

public static Long genSampleIndex(Long splitPointIdx, Long count, Long splitPointSize) {
   splitPointIdx++;
   splitPointSize++;

   Long div = count / splitPointSize;
   Long mod = count % splitPointSize;

   return div * splitPointIdx + ((mod > splitPointIdx) ? splitPointIdx : mod) - 1;
}

後續操作也使用同樣的genSampleIndex函數來做選擇,這樣保證在操作所有序列上可以選取同樣的採樣點。

allValues = {ArrayList@10264}  size = 8  //本task有多少單詞
 0 = {Double@10266} -2.0
 1 = {Double@10271} -1.0
 2 = {Double@10272} -1.0
 3 = {Double@10273} -1.0
 4 = {Double@10274} -1.0
 5 = {Double@10275} -1.0
 6 = {Double@10276} -1.0
 7 = {Double@10277} -1.0
 
splitPoints = {ArrayList@10265}  size = 7 //採樣了7個
 0 = {Double@10266} -2.0
 1 = {Double@10271} -1.0
 2 = {Double@10272} -1.0
 3 = {Double@10273} -1.0
 4 = {Double@10274} -1.0
 5 = {Double@10275} -1.0
 6 = {Double@10276} -1.0

最後返回採樣數據,返回時候附帶當前taskIDnew Tuple2 <Object, Integer>(obj,taskId)

這裡有一個trick點

  for (Object obj : splitPoints) {
     Tuple2 <Object, Integer> cur
        = new Tuple2 <Object, Integer>(
        obj,
        taskId); //這裏返回的是類似 (-5.0,2) :其中2就是task id,-5.0是-wordcount。
     out.collect(cur);
  }

  out.collect(new Tuple2(
     getRuntimeContext().getNumberOfParallelSubtasks(),
     -taskId - 1));//這裏返回的是一個特殊元素,類似(4,-2) :其中4是本應用中并行task數目,-2是當前-taskId - 1。這個task數目後續就會用到。

具體數據參見如下:

row = {Row@10211} "中國,-1.0,1.0986122886681098"
 fields = {Object[3]@10214} 
 
cur = {Tuple2@10286} "(-5.0,2)" // 返回採樣數據,返回時候附帶當前taskID
 f0 = {Double@10285} -5.0 // -wordcount。
 f1 = {Integer@10300} 2 // 當前taskID
歸併 SplitPointReducer

歸併所有task生成的sample。然後再次sample,把sample數據組成一個數據塊,這個數據塊選擇的原則是:每個task都盡量選擇若干sample

這裏其實是有一個轉換,就是從正常單詞的抽樣 轉換到 某一類單詞的抽樣,這某一類的意思舉例是:出現次數為一,或者出現次數為五 這種單詞

這裏all是所有採樣數據,其中一個元素內容舉例 (-5.0,2) :其中2就是task id,-5.0是-wordcount。

這裏用 Collections.sort(all, new PairComparator()); 來對所有採樣數據做排序。排序基準是首先對 -wordcount,然後對task ID。

SplitPointReducer的返回採樣數值就作為廣播變量存儲起來:.withBroadcastSet(splitPoints, "splitPoints");

這裏的trick點是:

for (Tuple2 <Object, Integer> value : values) {
   if (value.f1 < 0) { 
      instanceCount = (int) value.f0;  // 特殊數據,類似(4,-2) :其中4是本應用中task數目,這個就是後續選擇哪些taskid的基準
      continue;
   }
   all.add(new Tuple2 <>(value.f0, value.f1)); // (-5.0,2) 正常數據
}

選擇sample index splitPoints.add(allValues.get(index));也使用了同樣的genSampleIndex。

計算中具體數據如下:

for (int i = 0; i < splitPointSize; ++i) {
		int index = genSampleIndex(
					Long.valueOf(i),
					Long.valueOf(count),
					Long.valueOf(splitPointSize))
					.intValue();
		spliters.add(all.get(index));
}
for (Tuple2 <Object, Integer> spliter : spliters) {
		out.collect(spliter);
}

count = 33
all = {ArrayList@10245}  size = 33 // 所有採樣數據,
0 = {Tuple2@10256} "(-5.0,2)"// 2就是task id,-5.0是-wordcount。
1 = {Tuple2@10285} "(-2.0,0)"
......
6 = {Tuple2@10239} "(-1.0,0)"
7 = {Tuple2@10240} "(-1.0,0)"
8 = {Tuple2@10241} "(-1.0,0)"
9 = {Tuple2@10242} "(-1.0,0)"
10 = {Tuple2@10243} "(-1.0,0)"
11 = {Tuple2@10244} "(-1.0,1)"
......
16 = {Tuple2@10278} "(-1.0,1)"
......
24 = {Tuple2@10279} "(-1.0,2)"
......
32 = {Tuple2@10313} "(-1.0,3)"
  
// spliters是返回結果,這裏分別選取了all中index為8,16,24這個三個record。每個task都選擇了一個元素。
spliters = {HashSet@10246}  size = 3
 0 = {Tuple2@10249} "(-1.0,0)" // task 0 被選擇。就是說,這裏從task 0中選擇了一個count是1的元素,具體選擇哪個單詞其實不重要,就是為了選擇count是1的這種即可。
 1 = {Tuple2@10250} "(-1.0,1)" // task 1 被選擇。具體同上。
 2 = {Tuple2@10251} "(-1.0,2)" // task 2 被選擇。具體同上。
SplitData把真實數據IDF插入

use binary search to partition data into sorted subsets。前面函數給出的是詞的count,但是沒有IDF。這裏將用二分法查找 找到IDF,然後把IDF插入到partition data中。

首先要注意一點:splitData的輸入就是原始輸入input, 和splitPoints的輸入是一樣 的

DataSet <Tuple2 <Integer, Row>> splitData = input
   .mapPartition(new SplitData(index))
   .withBroadcastSet(splitPoints, "splitPoints");

open函數中會取出廣播變量 splitPoints。

splitPoints = {ArrayList@10248}  size = 3
 0 = {Tuple2@10257} "(-1.0,0)"
 1 = {Tuple2@10258} "(-1.0,1)"
 2 = {Tuple2@10259} "(-1.0,2)"

本函數的輸入舉例

row = {Row@10232} "入門,-1.0,1.0986122886681098"

會在splitPoints中二分法查找,得到splits中每一個 sample 對應的真實IDF。然後發送出去。

這裏需要特殊說明下,這個二分法查找查找的是IDF數值,比如count為1的這種單詞對應的IDF數值,可能很多單詞都是count為1,所以找到一個這樣單詞的IDF即可

splitPoints = {ArrayList@10223}  size = 3
 0 = {Tuple2@10229} "(-1.0,0)"
 1 = {Tuple2@10230} "(-1.0,1)"
 2 = {Tuple2@10231} "(-1.0,2)"
curTuple.f0 = {Double@10224} -1.0
  
int bsIndex = Collections.binarySearch(splitPoints, curTuple, new PairComparator());

		int curIndex;
		if (bsIndex >= 0) {
			curIndex = bsIndex;
		} else {
			curIndex = -bsIndex - 1;
		}

// 假設單詞是 "入門",則發送的是 "入門" 這類單詞在本partition的index,和 "入門" 的單詞本身
// 其實,從調試過程看,是否發送單詞信息本身並不重要,因為接下來的那一步操作中,並沒有用到單詞本身信息
out.collect(new Tuple2 <>(curIndex, row)); 
reduceGroup計算同類型單詞數目

這裡是計算在某一partition中,某一種類單詞的數目。比如count為1的單詞,這種單詞總共有多少個

後續會把new Tuple2 <>(id, count)作為partitionCnt廣播變量存起來。

id就是這類單詞在這partition中間的index,我們暫時稱之為partition index。count就是這類單詞在本partition的數目。

// 輸入舉例
value = {Tuple2@10312} "(0,入門,-1.0,1.0986122886681098)"
 f0 = {Integer@10313} 0
 
// 計算數目
for (Tuple2 <Integer, Row> value : values) {
		id = value.f0;
		count++;
}

out.collect(new Tuple2 <>(id, count));  
  
// 輸出舉例,假如是序號為0的這類單詞,其總體數目是12。這個序號0就是這類單詞在某一partition中的序號。就是上面的 curIndex。
id = {Integer@10313} 0
count = {Long@10338} 12

4.2.2 localSort

第二步是localSort。Sort a partitioned dataset. 最終排序並且會返回最終數值,比如 (29, “主編,-1.0,1.0986122886681098″), 29就是”主編” 這個單詞在 IDF字典中的序號。

DataSet<Tuple2<Long, Row>> ordered = localSort(partitioned.f0, partitioned.f1, 1);

open函數中會獲取partitionCnt。然後計算出某一種類單詞,其在本partition之前所有partition中,這類單詞數目。

public void open(Configuration parameters) throws Exception {
		List <Tuple2 <Integer, Long>> bc = getRuntimeContext().getBroadcastVariable("partitionCnt");
		startIdx = 0L;
		int taskId = getRuntimeContext().getIndexOfThisSubtask();
		for (Tuple2 <Integer, Long> pcnt : bc) {
			if (pcnt.f0 < taskId) {
					startIdx += pcnt.f1;
			}
		}
}

bc = {ArrayList@10303}  size = 4
 0 = {Tuple2@10309} "(0,12)"  // 就是task0裏面,這種單詞有12個
 1 = {Tuple2@10310} "(2,9)"// 就是task1裏面,這種單詞有2個
 2 = {Tuple2@10311} "(1,7)"// 就是task2裏面,這種單詞有1個
 3 = {Tuple2@10312} "(3,9)"// 就是task3裏面,這種單詞有3個
// 如果本task id是4,則其startIdx為30。就是所有partition之中,它前面index所有單詞的和。  

然後進行排序。Collections.sort(valuesList, new RowComparator(field));

valuesList = {ArrayList@10405}  size = 9
 0 = {Row@10421} ":,-1.0,1.0986122886681098"
 1 = {Row@10422} "主編,-1.0,1.0986122886681098"
 2 = {Row@10423} "國內,-1.0,1.0986122886681098"
 3 = {Row@10424} "文獻,-1.0,1.0986122886681098"
 4 = {Row@10425} "李宜燮,-1.0,1.0986122886681098"
 5 = {Row@10426} "糖尿病,-1.0,1.0986122886681098"
 6 = {Row@10427} "美國,-1.0,1.0986122886681098"
 7 = {Row@10428} "謝恩,-1.0,1.0986122886681098"
 8 = {Row@10429} "象棋,-1.0,1.0986122886681098"
  
  
// 最後返回時候,就是  (29, "主編,-1.0,1.0986122886681098"),29就是“主編”這個單詞在最終字典中的序號。
// 這個序號是startIdx + cnt,startIdx是某一種類單詞,其在本partition之前所有partition中,這類單詞數目。比如在本partition之前,這類單詞有28個,則本partition中,從29開始計數。就是最終序列號
	for (Row row : valuesList) {
		out.collect(Tuple2.of(startIdx + cnt, row));
		cnt++; // 這裏就是在某一類單詞中,單調遞增,然後賦值一個字典序列而已
	}  
cnt = 1
row = {Row@10336} "主編,-1.0,1.0986122886681098"
 fields = {Object[3]@10339} 
startIdx = 28

4.3 過濾

最後還要進行過濾,如果文字個數超出了字典大小,就拋棄多餘文字。

ordered.filter(new FilterFunction<Tuple2<Long, Row>>() {
    @Override
    public boolean filter(Tuple2<Long, Row> value) {
        return value.f0 < vocabSize;
    }
})

0x05 生成模型

具體生成模型代碼如下。

DataSet<DocCountVectorizerModelData> resDocCountModel = ordered.filter(new FilterFunction<Tuple2<Long, Row>>() {
    @Override
    public boolean filter(Tuple2<Long, Row> value) {
        return value.f0 < vocabSize;
    }
}).mapPartition(new BuildDocCountModel(params)).setParallelism(1);
return resDocCountModel;

其中關鍵類是 DocCountVectorizerModelData 和 BuildDocCountModel。

5.1 DocCountVectorizerModelData

這是向量信息。

/**
 * Save the data for DocHashIDFVectorizer.
 *
 * Save a HashMap: index(MurMurHash3 value of the word), value(Inverse document frequency of the word).
 */
public class DocCountVectorizerModelData {
    public List<String> list;
    public String featureType;
    public double minTF;
}

5.2 BuildDocCountModel

最終生成的模型信息如下,這個也就是之前樣例代碼給出的輸出。

modelData = {DocCountVectorizerModelData@10411} 
 list = {ArrayList@10409}  size = 37
  0 = "{"f0":"9787310003969","f1":1.0986122886681098,"f2":19}"
  1 = "{"f0":"下冊","f1":1.0986122886681098,"f2":20}"
  2 = "{"f0":"全","f1":1.0986122886681098,"f2":21}"
  3 = "{"f0":"華齡","f1":1.0986122886681098,"f2":22}"
  4 = "{"f0":"圖解","f1":1.0986122886681098,"f2":23}"
  5 = "{"f0":"思","f1":1.0986122886681098,"f2":24}"
  6 = "{"f0":"成像","f1":1.0986122886681098,"f2":25}"
  7 = "{"f0":"舊書","f1":1.0986122886681098,"f2":26}"
  8 = "{"f0":"索引","f1":1.0986122886681098,"f2":27}"
  9 = "{"f0":":","f1":1.0986122886681098,"f2":28}"
  10 = "{"f0":"主編","f1":1.0986122886681098,"f2":29}"
  11 = "{"f0":"國內","f1":1.0986122886681098,"f2":30}"
  12 = "{"f0":"文獻","f1":1.0986122886681098,"f2":31}"
  13 = "{"f0":"李宜燮","f1":1.0986122886681098,"f2":32}"
  14 = "{"f0":"糖尿病","f1":1.0986122886681098,"f2":33}"
  15 = "{"f0":"美國","f1":1.0986122886681098,"f2":34}"
  16 = "{"f0":"謝恩","f1":1.0986122886681098,"f2":35}"
  17 = "{"f0":"象棋","f1":1.0986122886681098,"f2":36}"
  18 = "{"f0":"二手","f1":0.0,"f2":0}"
  19 = "{"f0":")","f1":0.6931471805599453,"f2":1}"
  20 = "{"f0":"/","f1":1.0986122886681098,"f2":2}"
  21 = "{"f0":"出版社","f1":0.6931471805599453,"f2":3}"
  22 = "{"f0":"(","f1":0.6931471805599453,"f2":4}"
  23 = "{"f0":"入門","f1":1.0986122886681098,"f2":5}"
  24 = "{"f0":"醫學","f1":1.0986122886681098,"f2":6}"
  25 = "{"f0":"文集","f1":1.0986122886681098,"f2":7}"
  26 = "{"f0":"正版","f1":1.0986122886681098,"f2":8}"
  27 = "{"f0":"版","f1":1.0986122886681098,"f2":9}"
  28 = "{"f0":"電磁","f1":1.0986122886681098,"f2":10}"
  29 = "{"f0":"選讀","f1":1.0986122886681098,"f2":11}"
  30 = "{"f0":"中國","f1":1.0986122886681098,"f2":12}"
  31 = "{"f0":"書","f1":1.0986122886681098,"f2":13}"
  32 = "{"f0":"十二冊","f1":1.0986122886681098,"f2":14}"
  33 = "{"f0":"南開大學","f1":1.0986122886681098,"f2":15}"
  34 = "{"f0":"文學","f1":1.0986122886681098,"f2":16}"
  35 = "{"f0":"郁達夫","f1":1.0986122886681098,"f2":17}"
  36 = "{"f0":"館藏","f1":1.0986122886681098,"f2":18}"
 featureType = "WORD_COUNT"
 minTF = 1.0

0x06 預測

預測業務邏輯是DocCountVectorizerModelMapper

首先我們可以看到 FeatureType,這個可以用來配置輸出哪種信息。比如可以輸出以下若干種:

public enum FeatureType implements Serializable {
    /**
     * IDF type, the output value is inverse document frequency.
     */
    IDF(
        (idf, termFrequency, tokenRatio) -> idf
    ),
    /**
     * WORD_COUNT type, the output value is the word count.
     */
    WORD_COUNT(
        (idf, termFrequency, tokenRatio) -> termFrequency
    ),
    /**
     * TF_IDF type, the output value is term frequency * inverse document frequency.
     */
    TF_IDF(
        (idf, termFrequency, tokenRatio) -> idf * termFrequency * tokenRatio
    ),
    /**
     * BINARY type, the output value is 1.0.
     */
    BINARY(
        (idf, termFrequency, tokenRatio) -> 1.0
    ),
    /**
     * TF type, the output value is term frequency.
     */
    TF(
        (idf, termFrequency, tokenRatio) -> termFrequency * tokenRatio
    );
}

其次,在open函數中,會加載模型,比如:

wordIdWeight = {HashMap@10838}  size = 37
 "醫學" -> {Tuple2@10954} "(6,1.0986122886681098)"
 "選讀" -> {Tuple2@10956} "(11,1.0986122886681098)"
 "十二冊" -> {Tuple2@10958} "(14,1.0986122886681098)"
...
 "華齡" -> {Tuple2@11022} "(22,1.0986122886681098)"
 "索引" -> {Tuple2@11024} "(27,1.0986122886681098)"
featureType = {DocCountVectorizerModelMapper$FeatureType@10834} "WORD_COUNT"

最後,預測時候調用predictSparseVector函數,會針對輸入 二手 舊書 : 醫學 電磁 成像來進行匹配。生成稀疏向量SparseVector。

0|$37$0:1.0 6:1.0 10:1.0 25:1.0 26:1.0 28:1.0

以上表示那幾個單詞 分別對應0 6 10 25 26 28 這幾個字典中對應序號的單詞,其在本句對應的出現數目都是一個。

0x07 參考

Tf-Idf詳解及應用

https://github.com/fxsjy/jieba

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

kubernetes pod內抓包,telnet檢查網絡連接的幾種方式

背景

在日常kubernetes的運維中,經常遇到pod的網絡問題,如pod間網絡不通,或者端口不通,更複雜的,需要在容器裏面抓包分析才能定位。而kubertnets的場景,pod使用的鏡像一般都是盡量精簡,很多都是基於alpine基礎鏡像製作的,因而pod內沒有ping,telnet,nc,curl命令,更別說tcpdump這種複雜的工具了。除了在容器或者鏡像內直接安裝這些工具這種最原始的法子,我們探討下其他法子。

實現

kubectl debug插件方式

項目地址 kubect debug,https://github.com/aylei/kubectl-debug

kubectl-debug 是一個簡單的 kubectl 插件,能夠幫助你便捷地進行 Kubernetes 上的 Pod 排障診斷。背後做的事情很簡單: 在運行中的 Pod 上額外起一個新容器,並將新容器加入到目標容器的 pid, network, user 以及 ipc namespace 中,這時我們就可以在新容器中直接用 netstat, tcpdump 這些熟悉的工具來解決問題了, 而舊容器可以保持最小化,不需要預裝任何額外的排障工具。操作流程可以參見官方項目地址文檔。

一條 kubectl debug命令背後是這樣的

步驟分別是:

  1. 插件查詢 ApiServer:demo-pod 是否存在,所在節點是什麼
  2. ApiServer 返回 demo-pod 所在所在節點
  3. 插件請求在目標節點上創建 Debug Agent Pod
  4. Kubelet 創建 Debug Agent Pod
  5. 插件發現 Debug Agent 已經 Ready,發起 debug 請求(長連接)
  6. Debug Agent 收到 debug 請求,創建 Debug 容器並加入目標容器的各個 Namespace 中,創建完成后,與 Debug 容器的 tty 建立連接

接下來,客戶端就可以開始通過 5,6 這兩個連接開始 debug 操作。操作結束后,Debug Agent 清理 Debug 容器,插件清理 Debug Agent,一次 Debug 完成。

直接進入容器net ns方式

有2種進入pod 所在net ns的方式,前提都是需要登錄到pod所在宿主機,且需要找出pod對應的容器ID或者名字。

ip netns方式

  • 獲取pod對應容器的ID或者name

    pid="$(docker inspect -f '{{.State.Pid}}' <container_name | uuid>)" #替換為環境實際的容器名字或者uuid
    
  • 創建容器對應netns

    ip netns會到/var/run/netns目錄下尋找network namespace,把容器進程中netns連接到這個目錄中后,ip netns才會感知到

    $ sudo mkdir -p /var/run/netns
    
    #docker默認不會創建這個鏈接,需要手動創建,這時候執行ip netns,就應當看到鏈接過來的network namespace
    $ sudo ln -sf /proc/$pid/ns/net "/var/run/netns/<container_name|uuid>" 
    
  • 執行ip netns <<container_name|uuid > bash,進入容器ns

    ip netns exec <container_name|uuid>  bash
    
  • 執行telnet,tcpdump等命令,此時執行ip a或者ifconfig,只能看到容器本身的IP

如下圖,執行ifconfig,只看到容器本身的IP,此時執行telnet,tcpdump等於直接在容器內操作

nsenter方式

nsenter為util-linux裏面的一個工具,除了進入容器net ns,還支持其他很多操作,可以查看官方文檔。

pid="$(docker inspect -f '{{.State.Pid}}' <container_name | uuid>)"
nsenter -t $pid -n /bin/bash
tcpdump -i eth0 -nn  #此時利用宿主機的tcpdump執行抓包操作,等於在容器內抓包

總結

  1. kubectl debug方式功能更強大,缺點是需要附加鏡像,要在目標pod創建debug agent的容器,比較笨重,但是優點是能使用的工具更多,不需要ssh到pod所在節點,除了netstat,tcpdump工具,還能使用htop,iostat等其他高級工具,不僅能對網絡進行debug,還能對IO等其他場景進行診斷,適用更複雜的debug場景。
  2. 直接進入容器net ns方式相對比較輕量,復用pod所在宿主機工具,但魚和熊掌不可兼得,缺點是只能進行網絡方面的debug,且需要ssh登錄到pod所在節點操作。

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

【其他文章推薦】

※回頭車貨運收費標準

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

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

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

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

北京伽途im系列亮相廣州車展 引領MPV市場升級

外觀方面,該車相比先期上市的北京伽途ix系列更具時尚氣息。中網格柵由大面積鍍鉻材質組成,層次感較為鮮明。尺寸方面,伽途im6長/寬/高依次為4700/1780/1820mm。而伽途im8的長/寬/高依次為4730×1810×1830mm,兩車軸距同為2760mm。內飾方面,中控台液晶屏尺寸較大,面板採用了碳纖維材質和仿桃木水轉印材質進行裝飾。

11月18日,福田汽車乘用車品牌在廣州車展重磅發布北京伽途im系列新品,引領MpV市場全面升級。該系列車型包含北京伽途im6和北京伽途im8兩款產品,前置前驅的配置,更大的空間,在保障產品安全性的同時,在舒適性、便利性和人性化方面更進一步,尤其是智能車聯網系統,通過大數據共享,將人與車通過網絡實時互聯,實現人與車有效互動,憑藉全面智能化升級,滿足用戶多元化的需求。

為大家庭時代品質出行而來

二胎政策出台意味着家庭結構將發生明顯的變化,大家庭的出行對車輛的空間、駕乘舒適性和安全性等均提出更高的要求,能夠滿足大家庭出行用車的MpV車型呈現出快速增長趨勢,同時大家庭的出現也對家庭第二輛車有較大的潛在需求。

定位“智能家庭車”的北京伽途,以“引領新汽車生活潮流、提升大家庭出行品質”為願景,致力於成就汽車消費者的“精智之道、愛家之選”。北京伽途中“伽”與中國濃厚的“家”文化傳統一脈相承,“伽途”寓意“家庭的美好前途”。

北京伽途作為融合科技、智能、時尚的未來智能汽車的代表,目標市場精準鎖定為中國乘用車需求最旺盛的大家庭用車市場。在福田汽車“互聯網汽車生態系統”基礎上,其以滿足用戶個性化需求為導向,基於網絡信息系統、OTD訂單系統和智能製造系統的定製化大規模生產方式,實現研發力、製造力、營銷力和品牌力的不斷提升,最大程度滿足大家庭消費者需求。

隨着消費者生活水平的提高,對MpV車型的需求也在向中高端發展。針對目前增長較快的前驅市場,北京伽途迅速抓住有利時機,推出了中高端重磅新品——im系列。該系列以其獨到的智能、舒適、時尚、安全可靠等多重優勢成為福田汽車集團鋪路乘用車領域的扛鼎之作。

大氣靈動智慧升級

北京伽途im系列車型延續了福田汽車工業4.0體系的“智造”水準,兼具大氣靈動的外觀與科幻感的座艙設計,以比肩世界的技術和品質,代表着福田汽車對中國汽車工業的誠意探索。

外觀方面,該車相比先期上市的北京伽途ix系列更具時尚氣息。中網格柵由大面積鍍鉻材質組成,層次感較為鮮明。尺寸方面,伽途im6長/寬/高依次為4700/1780/1820mm;而伽途im8的長/寬/高依次為4730×1810×1830mm,兩車軸距同為2760mm。

內飾方面,中控台液晶屏尺寸較大,面板採用了碳纖維材質和仿桃木水轉印材質進行裝飾。方向盤採用了三幅式設計,優質仿皮材質輪緣,握感舒適。整體造型較為硬朗,比較符合時下年輕人的審美需求。

動力方面,北京伽途im系列兩款產品採用了兩款不同的動力系統。北京伽途im6配備福田1.5L 4A15M發動機,最大功率81kW;北京伽途im8則採用東安1.5L DAM15D機型,最大功率85kW。

據悉,廣州車展是北京伽途im系列繼全國發布之後區域亮相的第一站,該系列產品將於2017年2月正式登陸全國市場,讓我們拭目以待。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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