標籤: 銷售文案

  • Zookeeper分佈式過程協同技術 – 部署及設置

    Zookeeper分佈式過程協同技術 – 部署及設置

    Zookeeper分佈式過程協同技術 –  部署及設置

    Zookeeper支持單機模式、偽集群模式、集群模式三種部署方式。演示部署環境為CentOS、jdk版本為1.8、Zookeeper版本為3.4.9。

    單機模式

    單機模式適合入門學習使用,只需要一台機器就可以輕鬆搭建Zookeeper服務用於學習和測試。

    1. 進入官網下載Zookeeper的JAR包,下載地址:https://zookeeper.apache.org/releases.html。

    2. 解壓壓縮包,命令:tar -zxvf zookeeper-3.4.9.tar.gz,解壓后目錄格式如下。

    3. 進入conf目錄,複製zoo_sample.cfg文件命名為zoo.cfg。這裏可以直接使用默認的參數,也可以根據自己的需要修改。

    命令:cp zoo_sample.cfg zoo.cfg

    主要配置參數說明

    • clientPort

    客戶端連接的服務器所監聽的TCP端口,默認情況下,服務器會監聽所有的網絡連接的這個端口,除非設置了clientPortAddress參數。客戶端口可以設置為任何值,不同的服務器可以設置不同的端口,默認端口號為2181。

    • tickTime

    tickTime的時長單位為毫秒,Zookeeper集群中使用的超時時間單位通過tickTime指定。tickTime設置了超時時間的下限值,因為最小的超時時間為一個tick時間,客戶端最小會話超時時間為2個tick時間。

    tickTime的默認值是3000毫秒,更低的tickTime值可以更快地發現超時問題,但也導致更高的網絡流量和更高的CPU使用率。

    • dataDir

    dataDir用於配置內存數據庫保存的模糊快照目錄,如果某個服務器為集群中的一台,id文件也保存在該目錄下。

    • dataLogDir

    用於配置事務日誌的保存目錄。服務端在確認一個事務前必須將數據同步到存儲中,如果寫入磁盤過於忙碌會影響到寫入的吞吐能力。因此,比較好的方案是使用專用的日誌存儲設備,將dataLogDir目錄配置指向該設備。

    • maxClientCnxns

    允許每個IP地址發起socket連接的最大數量。Zookeeper通過流量控制和限制值來避免過載情況的發生。當某個IP地址的客戶端建立的連接數大於此值時,服務器會拒絕該IP地址新的連接。

    • initLimit

    對於追隨者最初連接到群首時的超時時間,單位為tick(tickTime)值的倍數。

    當某個追隨者最初與群首建立連接時,它們之間會傳輸相當多的數據,尤其是追隨者落後整體很多時。配置initLimit參數值取決於群首與追隨者之間的網絡傳輸速度以及傳輸數據量的大小。

    但是如果設置值過高,在首次連接到故障的服務器就會消耗更多的時間,同時還會消耗更多的恢復時間。因此在實際部署時,最好進行集群間的網絡基準測試來測試出你所期望的時間。

    • syncLimit

    對於追隨者與群首進行sync操作時的超時值,單位為tick(tickTime)值的倍數。

    追隨者總是會稍落後於群首,如果群首與追隨者無法進行sync操作,而且超過了syncLimit的tick時間,就會放棄該追隨者。

    • leaderServes

    配置值為“yes”或“no”標誌,指示群首服務器是否為客戶端提供服務。擔任群首的服務器需要做很多工作,它需要與所有的追隨者進行通信並會執行所有的變更操作,這意味着群首的負載會比追隨者高很多,如果群首過載,整個系統都有可能受到影響。

    4. 進入bin目錄,可以看到很多sh腳本文件,通過zkServer.sh來啟動zookeeper。

    操作命令:

    啟動命令:.zkServer.sh start

    停止命令:.zkServer.sh stop

    重啟命令:.zkServer.sh restart

    狀態查看命令:.zkServer.sh status

    啟動zookeeper服務后,通過status命令可以看到當前服務狀態、使用的配置文件、運行模式。

     這樣Zookeeper的單機部署模式就已經初步完成了。如果需要修改JVM配置,可以修改zkServver.sh文件中的配置,在如下位置加入你需要的參數。

    偽集群部署

     偽集群部署指在一台機器上部署多個Zookeeper服務。

    1. 首先將之前配置好的Zookeeper服務目錄另外複製兩份,命名為zookeeper-2和zookeeper-3。

    2. 然後分別修改三份的zoo.cfg配置項,添加配置 server.x=[hostname]:n:n[:observer],示例如下。

    server.1=localhost:2887:3887

    server.2=localhost:2888:3888

    server.3=localhost:2889:3889

    Zookeeper服務器需要知道它們如何通信,配置文件中該形式的配置項就指定了服務器x的配置信息,其實x為服務器的ID值(一個整數)。當一個服務器啟動后,就會讀取data目錄下myid文件中的值,之後服務器就會使用這個值作為查找server.x項,通過該項中的數據配置服務器自己。如果需要連接到另外一個服務器y,就會使用server.y項配置信息來與這個服務器進行通信。

    hostname為服務器在網絡中的名稱(ip或者主機名),同時後面跟着兩個端口號,第一個端口號用於事務的發送,第二個端口號用於群首選舉。如果最後一個字段標記了observer屬性(選填),服務器就會進入觀察者模式。

    三份配置文件的myid和兩個配置端口號以及clientPort不可以重複,因為是在一台機器上部署,生產環境集群部署時可以忽略。最終的配置清單如下:

    3. 分別添加myid文件,在data目錄下添加myid文件。

    echo ‘1’ > data/myid

    4. 分別啟動三個Zookeeper服務,啟動成功后通過status命令可以查看服務的狀態。可以看到目前集群里有一台leader和兩台follower。

    集群部署

    生產環境的集群部署步驟和偽集群部署並無太多差異,同樣是修改zoo.cfg配置文件並且添加Zookeeper集群信息,集群部署時,客戶端端口號、事務端口號、選舉端口號都可以保持一致。另外集群部署時需要充分考慮集群機器間的網絡情況來制定合理的超時時間設置。

     

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 擴張的撒哈拉沙漠 破碎的家庭和暴力

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

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

    【其他文章推薦】

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

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

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

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

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

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

  • 菲律賓民答那峨島發生規模6.6強震

    摘錄自2019年10月16日上報、29日自由時報報導

    根據美國地質調查所(USGS)觀測顯示,菲律賓民答那峨島當地時間29日早9時許傳出規模6.6強震。震央位於民答那峨島的達沃市(Davao City)東南方,地震深度僅15.3公里。

    截至下午4:00為止,麥格塞塞(Magsaysay)鎮發言人告訴法新社,一名十多歲的男學生逃離校舍時,遭倒下的牆壓死。另有學生在逃出教室時因「踩踏」受傷,所幸保住性命。 科羅納達爾(Koronadal)鎮當局也表示,當地一名66歲男性被倒下的牆壓死。 截至目前至少有50人遭掉落的瓦礫砸傷,包括一所小學的7名師生在逃離校舍時受傷。

    本月16日,一場規模6.4的淺層強震才襲擊菲律賓南部,至少1人受傷。根據美國地質調查所,震源深度14公里,震央距離民答那峨島哥倫比歐市(Columbio)約7.7公里。地震發生於當地時間晚間7時37分。其中,基達帕灣市(Kidapawan City)達7級震度,預計仍會有餘震,該市市長宣布17日全面停課。

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 巴黎共享滑板車亂象多 河中打撈竟成新興工作

    摘錄自2019年10月26日民視新聞報導

    巴黎目前有十多家業者、提供了大概2萬輛電動滑板車,可以自由租借,帶來方便的同時,也帶來了混亂,街頭隨處可見被人亂停、亂丟的電動滑板車,還有不少直接被扔進塞納河。負責清理、回收這些電動滑板車的「巡邏隊」,成了新興工作。

    塞納河畔有人用力拉著繩索,他們可不是在釣魚,而是在「釣電動滑板車」,這家電動滑板車公司的員工,3個鐘頭從河裡打撈了足足15輛。電動滑板車公司員工表示:「就像那輛的手把卡在岸邊,我們不可能直接把它拉起來,你必須下到水裡,把手把轉向才能將它弄起來。」

     

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

    【其他文章推薦】

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

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

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

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

    ※超省錢租車方案

  • 三菱插電式混動新技術 可為家庭供電

    日前,日本三菱汽車公司宣佈,其歐藍德插電式混合動力汽車(PHEV)將配置V2H電動汽車家庭供電系統,其電池儲存的電量可以支援家庭供電的不時之需。此前V2H供電系統僅用於類似i-MIEV之類的純電動汽車。據悉,歐藍德PHEV搭載2.0升汽油發動機與一台電動馬達,可以以60英里每小時的速度行駛30英里,總續航里程可達540英里。

    三菱的V2H系統在自然災害或停電的時候,可以作為100伏特(日本規格)的家用供電電源使用,運用1500瓦的交流電源,滿足家用需求。僅電池電量可以滿足普通家庭一天的用電需求。如果加滿汽油,其總電量或將滿足一個普通家庭將近10天的用電需求。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

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

  • 不畏油價跌 美電動車 12 月創最高單月銷售紀錄

    儘管美國汽油零售價格跟隨國際原油同步下挫到每加侖 2 美元的低水準,2014 年美國電動車銷售強勁增加 23%,12 月並創下單月最佳紀錄;顯見電動車已蔚為一股持久的趨勢,不受油價影響。   根據電動車專業網站 InsideEVs 統計,2014 年 12 月美國賣出 12,874 輛電動車,為統計以來最高單月銷售紀錄。至於 2014 年全年,電動車銷售勁揚 23%,達到 119,710 輛。   就個別品牌來看,全美電動車銷售冠軍是日產 Leaf,2014 年總計售出 30,200 輛,遙遙領先亞軍通用汽車雪佛蘭 Volt 的 18,805 輛。起價介於 3.5 萬到 4 萬美元的特斯拉 Model S 排名第 3,全年銷量為 17,300 輛,市占不及 15%。不過,在 12 月 Model S 首度擊敗售價較低的日產 Leaf(約 3 萬美元)。   2014 年美國電動車市場唯一新上市車款是 BMW,分別在 5 月及 8 月推出 i3 與 i8 ,加總的銷量僅 6,647 輛。展望 2015 年,將有數款新型電動車上路,尤其是在休旅車和越野車市場。而特斯拉的 Model X 則預計 2015 年下半年登場,是今年最受期待的電動車。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 產能尚未滿載 特斯拉要到 2020 年才可能獲利

    特斯拉執行長 Elon Musk 在世界汽車業大會 (Automotive News World Congress) 中表示,以目前狀況看來,特斯拉的電動車在 2020 年前都無法開始獲利。   根據華爾街日報報導,特斯拉的 Model 3 電動車在現有會計原則下,至少要等到 2020 年產能滿載時才能正式轉盈。Model 3 是特斯拉功能性較少的電動車,一台可能售價約介於 3 萬到 4 萬美元之間,比起其他 Model S 系列一台動輒超過 10 萬美元來說相對便宜,Model 3 預計在 2017 年推出。特斯拉的股價盤後下跌 7%,來到 189.95 美元。   對於電動車的發展,Musk 也要求其他汽車公司盡速推出自家電動車。特斯拉目前電動車銷售量約為 35,000 台,預計到 2025 年可賣出數百萬台。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

    ※超省錢租車方案

  • python高級-閉包-裝飾器

    閉包內容:

    1. 匿名函數:能夠完成簡單的功能,傳遞這個函數的引用,只有功能

    2. 普通函數:能夠完成複雜的功能,傳遞這個函數的引用,只有功能

    3. 閉包:能夠完成較為複雜的功能,傳遞這個閉包中的函數以及數據,因此傳遞是功能+數據

    4. 對象:能夠完成最複雜的功能,傳遞很多數據+很多功能,因此傳遞的是數據+功能

    ———————————————————

    1. 對全局函數進行修改:在函數當中加global,在閉包中外邊中的變量加nonlocal

    2. 閉包定義:有兩個函數嵌套使用,裏面的函數可以使用外面函數所傳輸的參數,最後可傳遞的是裏面函數的結構與數據(個人理解)。

    3. 最後閉包可以在python中引申出裝飾器 ———————————————————

     1 def closure():
     2     # 在函數內部再定義一個函數,
     3     # 並且這個函數用到了外邊函數的變量,那麼將這個函數以及用到的一些變量稱之為閉包
     4     def closure_in(x):
     5         print('---------我是打不死的%s--------' %x)
     6     return closure_in
     7  8 x = closure()
     9 x('小強')
    10 11 print('*'*20)
    12 # -----加餐---------
    13 def closure_1(a,b,c):
    14     def closure_on(x):
    15         print('-----%s加餐-------' %b)
    16         print(a*x + c)
    17     return closure_on
    18 19 demo = closure_1(2,'小強',3) #傳closure_1函數
    20 demo(4) #傳clsure_on函數
    21 22 #注:函數不加括號,調用的是函數本身【function】;函數加括號,調用的是函數的return結果。

     

    裝飾器內容:

    代碼要遵守‘開放封閉’原則;對已經寫好的函數遵守封閉,對功能擴展遵守開放;

     1 # 裝飾器的作用:為了對原來的代碼上進行擴展
     2 def decoration(func):
     3     def call_func():
     4         print('-------正在裝飾 -------' )
     5         func()
     6     return call_func
     7  8 #@decoration     #--->demo_new = decoration(demo)
     9 def demo():
    10    print('demo----')
    11 12 demo_new = decoration(demo)
    13 demo_new()

     

    使用裝飾器來測試一個函數的運行時:

     1 import time
     2 def set_func(func):
     3     def call_func():
     4         start_time = time.time()
     5         func()
     6         stop_func = time.time()
     7         print(‘alltimes is %f’ %(stop_func-start_fun))
     8     return call_func
     9 @set_func
    10 def test1():
    11     print(‘——-test1———’)    
    12 test1()
    13 14 #等價於:
    15 @set_func==test1 = set_func(test1)
    16

     

    1. 沒有參數,沒有返回值的函數進行裝飾:
     1 def set_func(func):
     2     def call_func():
     3         print(‘———test2——-’)
     4         print(‘———-test3——’)
     5         func()
     6     return call_func
     7     
     8 @set_func
     9 def test1():
    10     print(‘——test1——-   ’)

     

    2. 對有參數無返回值的函數進行裝飾:
     1 def set_func(func):
     2     def call_func(a):  #
     3         print(‘———test2——-’)
     4         print(‘———-test3——’)
     5         func(a) #
     6     return call_func
     7     
     8 @set_func
     9 def test1(num):
    10     print(‘——test1——- %d    ’ %num)
    11 12 test1(100) —->call_func(100)
    13 test1(200)——>call_func(200)

     

    復現裝飾器原理:

    ————————————————————————-

    只要遇到@函數 裝飾器(這句話),在程序中就已經執行了!!
    3. 不定長參數的函數裝飾:
     1 def set_func(func):
     2     def call_func(*args,**kwargs):  #
     3         print(‘———test2——-’)
     4         print(‘———-test3——’)
     5         func(*args,**kwargs) #(拆包)將元祖拆開,每個進行傳輸;
     6         #func(args,kwargs)—>不行,相當於傳遞了兩個參數:一個元祖,一個字典。
     7     return call_func
     8     
     9 @set_func
    10 def test1(num,*args,**kwargs):
    11     print(‘——test1——- %d    ’ %num)
    12     print(‘——test1——-   ’ , args)
    13     print(‘——test1——- ’ ,kwargs )
    14     
    15 test1(100)  
    16 test1(100,200)
    17 test1(100,200,300,mm=100)

    注意:*args保存不定長參數,以元祖保存,**kwargs保存字典形式(mm=…)

    4.對應的返回值參數進行裝飾、通用裝飾器:
     1 #通用裝飾器
     2 def set_func(func):
     3     print(“開始進行裝飾———-”)
     4     def call_func(*args,**kwargs):  #
     5         print(‘———test2——-’)
     6         print(‘———-test3——’)
     7         return func(*args,**kwargs) #(拆包)將元祖拆開,每個進行傳輸;如果沒有return ret返回none。
     8         #func(args,kwargs)—>不行,相當於傳遞了兩個參數:一個元祖,一個字典。
     9     return call_func
    10     
    11 @set_func
    12 def test1(num,*args,**kwargs):
    13     print(‘——test1——- %d    ’ %num)
    14     print(‘——test1——-   ’ , args)
    15     print(‘——test1——- ’ ,kwargs )
    16     return ‘ok’    #—-返回給上面的func(),然後return func—ret
    17     
    18 ret = test1(100)
    19

     

    5. 多個裝飾器對同一個函數進行裝飾:
     1 def add_qx(func):
     2     print(“——開始進行裝飾權限1———-”)
     3     def call_func(*args,**kwargs):  #
     4         print(‘這是權限驗證1’)
     5         return func(*args,**kwargs)
     6     return call_func
     7     
     8  9 def add_xx(func):
    10     print(“——開始進行裝飾xx功能———-”)
    11     def call_func(*args,**kwargs):  #
    12         print(‘這是xx權限驗證’)
    13         return func(*args,**kwargs)
    14     return call_func
    15     
    16 @add_qx
    17 @add_xx
    18 def test1():
    19     print(‘——test1——-’) 
    20 21 test1()
    22

     

    首先執行第一個,但是第一個裝飾器下面不是函數(裝飾器原則:下面必須是函數,否則不執行),所以第一個函數先等待,等第二個裝飾器執行后形成函數在交給第一個裝飾器;所以運行結果是:

    1. 開始進行裝飾xx的功能,

    2. 開始進行裝飾權限1,

    3. 這是權限驗證1,

    4. 這是xx權限驗證,

    5. ——-test1——-,

    ——————裝飾器練習—————- 輸出格式:<td><h1>haha</h1></td>

     1 def set_func_1(func):
     2     def call_func():
     3         return ‘<h1>’ + func() + ’</h1> 4     return call_func
     5     
     6  7 def set_func_2(func):
     8     def call_func():
     9         return ‘<td>’ + func() + ’</td>10     return call_func
    11     
    12 @set_func_1()
    13 @set_func_2()
    14 def get_str():
    15     return ‘haha’
    16     
    17 print(get_str()) 

    18 最後執行的效果: <h1><td>haha</td></h1>
    6. 用類對函數進行裝飾(了解):
     1 class Test(object):
     2     def __init__(self,func):
     3         self.func = fun
     4         
     5     def __call__(self):
     6         print(‘這裡是裝飾器的功能。。。。’)
     7         return self.func()
     8         
     9 @Test
    10 def get_str():
    11     return ‘haha’
    12     
    13 print(get_str())

     

    以上就是裝飾器與閉包的全部內容,希望有所收穫,如果有錯誤,希望指出,感謝!!

     

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

    【其他文章推薦】

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

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

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

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

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

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

  • 實時web應用方案——SignalR(.net core)

    實時web應用方案——SignalR(.net core)

    何為實時

    先從理論上解釋一下兩者的區別。

    大多數傳統的web應用是這樣的:客戶端發起http請求到服務端,服務端返回對應的結果。像這樣:

     

    也就是說,傳統的web應用都是客戶端主動發起請求到服務端。

    那麼實時web應用呢?它不需要主動發起請求,服務端可以主動推送信息到客戶端。

    舉栗子的話,實時聊天工具、web遊戲等都可以算是實時應用。

    什麼是SignalR

    如果想做一個實時應用,最好用web socket。很早以前我也寫過web socket的實現方式,但不夠全面,這裏再補上一篇。

    來說說signalR,它是一款開源的實時框架,可以使用三種方式實現通信(long polling、server sent events、web socket)。它很好的整合了底層技術,讓我們可以不用關注底層技術實現而把精力聚焦在業務實現上。一個完整的signalR包括客戶端和服務端,服務端支持net core/net framework,還支持大部分客戶端,比如瀏覽器和桌面應用。

    回落機制

    為了兼容不同瀏覽器(客戶端)和服務端,signalR採用了回落機制,使得它可以根據情況協商使用不同的底層傳輸方式。假如瀏覽器不支持web socket,就自動降級使用sse,再不行就long polling。當然,也可以禁用這種機制,指定其中一種。

    三種通信方式

    long polling(長輪詢)

    長輪詢是客戶端發起請求到服務端,服務器有數據就會直接返回。如果沒有數據就保持連接並且等待,一直到有新的數據返回。如果請求保持到一段時間仍然沒有返回,這時候就會超時,然後客戶端再次發起請求。

    這種方式優點就是簡單,缺點就是資源消耗太多,基本是不考慮的。

    server sent events(sse)

    如果使用了sse,服務器就擁有了向客戶端推送的能力,這些信息和流信息差不多,期間會保持連接。

    這種方式優點還是簡單,也支持自動重連,綜合來講比long polling好用。缺點也很明顯,不支持舊的瀏覽器不說,還只能發送本文信息,而且瀏覽器對sse還有連接數量的限制(6個)。

    web socket

    web socket允許客戶端和服務端同時向對方發送消息(也就是雙工通信),而且不限制信息類型。雖然瀏覽器同樣有連接數量限制(可能是50個),但比sse強得多。理論上最優先使用。

    進入正題

    開始之前,還需要了解RPC和Hub的概念。

    RPC:全程Remote Procedure Call,字面意思遠程服務調用,可以像調用本地方法一樣調用遠程服務。前端可以調用後端方法,後端也可以調用前端方法。

    Hub:基於RPC,接受從客戶端發過來的消息,也同時負責把服務端的消息發送給客戶端。客戶端可以調用Hub裏面的方法,服務端可以通過Hub調用客戶端裏面的方法。

    好了,概念已經理解清楚了,接下來上代碼。

    在項目里新增Hub類:

    using Microsoft.AspNetCore.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRDemo.Server
    {
        public class SignalRHub : Hub
        {
            /// <summary>
            /// 客戶連接成功時觸發
            /// </summary>
            /// <returns></returns>
            public override async Task OnConnectedAsync()
            {
                var cid = Context.ConnectionId;
    
                //根據id獲取指定客戶端
                var client = Clients.Client(cid);
    
                //向指定用戶發送消息
                await client.SendAsync("Self", cid);
    
                //像所有用戶發送消息
                await Clients.All.SendAsync("AddMsg", $"{cid}加入了聊天室");
            }
        }
    }

    為了讓外部可以訪問,我們還需要一個控制器。在控制器里聲明隨便建一個:

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.SignalR;
    using SignalRDemo.Server;
    using System.Threading.Tasks;
    
    namespace SignalRDemo.Controllers
    {
        public class HomeController : Controller
        {
            private readonly IHubContext<SignalRHub> _countHub;
    
            public HomeController(IHubContext<SignalRHub> countHub)
            {
                _countHub = countHub;
            }
    
            /// <summary>
            /// 發送信息
            /// </summary>
            /// <param name="msg"></param>
            /// <param name="id"></param>
            /// <returns></returns>
            public async Task Send(string msg, string id)
            {
                await _countHub.Clients.All.SendAsync("AddMsg", $"{id}:{msg}");
            }
        }
    }

    再然後進入StartUp設置端點:

    endpoints.MapHub<SignalRHub>("/hub");

    完成以後,配置signalr客戶端:

    setupConn = () => {
        conn = new signalR.HubConnectionBuilder()
            .withUrl("/hub")
            .build();
    
        conn.on("AddMsg", (obj) => {
            $('#msgPanel').append(`<p>${obj}</p>`);
        });
    
        conn.on("Finished", () => {
            conn.stop();
            $('#msgPanel').text('log out!');
        });
    
        conn.on("Self", (obj) => {
            $('#userId').text(obj);
        });
    
        conn.start()
            .catch(err => console.log(err));
    }

    要注意withUrl裏面的路徑就是之前設置好的端點。

    運行效果:

     

     

     Hub還支持組操作,比如:

    //將用戶添加到A組
    await
    Groups.AddToGroupAsync(Context.ConnectionId, "GroupA");
    //將用戶踢出A組
    await Groups.RemoveFromGroupAsync(Context.ConnectionId, "GroupA");
    //向A組所有成員廣播消息
    await Clients.Group("GroupA").SendAsync("AddMsg", "群組消息");

    更多操作請參考官方文檔。

    本文演示demo的源碼見git,地址:https://gitee.com/muchengqingxin/SignalRDemo.git

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 十、深度優先 && 廣度優先

    十、深度優先 && 廣度優先

    原文地址

    一、什麼是“搜索”算法?

    • 算法是作用於具體數據結構之上的,深度優先搜索算法和廣度優先搜索算法都是基於“圖”這種數據結構的。
    • 因為圖這種數據結構的表達能力很強,大部分涉及搜索的場景都可以抽象成“圖”。
    • 圖上的搜索算法,最直接的理解就是,在圖中找出從一個頂點出發,到另一個頂點的路徑。
    • 具體方法有很多,兩種最簡單、最“暴力”的方法為深度優先、廣度優先搜索,還有A、 IDA等啟髮式搜索算法。
    • 圖有兩種主要存儲方法,鄰接表和鄰接矩陣。
    • 以無向圖,採用鄰接表存儲為例:
    public class Graph {
        // 頂點的個數
        private int v;
        // 每個頂點後面有個鏈表
        private LinkedList<Integer>[] adj;
    
        public Graph(int v) {
            this.v = v;
            adj = new LinkedList[v];
            for (int i = 0; i < v; i++) {
                adj[i] = new LinkedList<>();
            }
        }
    
        /**
         * 添加邊
         * @param s 頂點
         * @param t 頂點
         */
        public void addEdge(int s,int t){
            // 無向圖一條邊存兩次(聯想微信好友)
            adj[s].add(t);
            adj[t].add(s);
        }
    }
    

    二、廣度優先搜索(BFS)

    • 廣度優先搜索(Breadth-First-Search),簡稱為 BFS。
    • 它是一種“地毯式”層層推進的搜索策略,即先查找離起始頂點最近的,然後是次近的,依次往外搜索

    2.1、實現過程

    /**
     * 圖的廣度優先搜索,搜索一條從 s 到 t 的路徑。
     * 這樣求得的路徑就是從 s 到 t 的最短路徑。
     *
     * @param s 起始頂點
     * @param t 終止頂點
     */
    public void bfs(int s, int t) {
        if (s == t) {
            return;
        }
        // visited 記錄已經被訪問的頂點,避免頂點被重複訪問。如果頂點 q 被訪問,那相應的visited[q]會被設置為true。
        boolean[] visited = new boolean[v];
        visited[s] = true;
        // queue 是一個隊列,用來存儲已經被訪問、但相連的頂點還沒有被訪問的頂點。因為廣度優先搜索是逐層訪問的,只有把第k層的頂點都訪問完成之後,才能訪問第k+1層的頂點。
        // 當訪問到第k層的頂點的時候,需要把第k層的頂點記錄下來,稍後才能通過第k層的頂點來找第k+1層的頂點。
        // 所以,用這個隊列來實現記錄的功能。
        Queue<Integer> queue = new LinkedList<>();
        queue.add(s);
        // prev 用來記錄搜索路徑。當從頂點s開始,廣度優先搜索到頂點t后,prev數組中存儲的就是搜索的路徑。
        // 不過,這個路徑是反向存儲的。prev[w]存儲的是,頂點w是從哪個前驅頂點遍歷過來的。
        // 比如,通過頂點2的鄰接表訪問到頂點3,那prev[3]就等於2。為了正向打印出路徑,需要遞歸地來打印,就是print()函數的實現方式。
        int[] prev = Arrays.stream(new int[v]).map(f -> -1).toArray();
    
        while (queue.size() != 0) {
            int w = queue.poll();
            LinkedList<Integer> wLinked = adj[w]; // 表示:鄰接表存儲時頂點為w,所對應的鏈表
            for (int i = 0; i < wLinked.size(); ++i) {
                int q = wLinked.get(i);
                // 判斷頂點 q 是否被訪問
                if (!visited[q]) {
                    // 未被訪問
                    prev[q] = w;
                    if (q == t) {
                        print(prev, s, t);
                        return;
                    }
                    visited[q] = true;
                    queue.add(q);
                }
            }
        }
    }
    
    // 遞歸打印s->t的路徑
    private void print(int[] prev, int s, int t) {
        if (prev[t] != -1 && t != s) {
            print(prev, s, prev[t]);
        }
        System.out.print(t + " ");
    }
    

    原理如下:

    2.2、複雜度分析

    • 最壞情況下,終止頂點 t 離起始頂點 s 很遠,需要遍歷完整個圖才能找到。
    • 這個時候,每個頂點都要進出一遍隊列,每個邊也都會被訪問一次,所以,廣度優先搜索的時間複雜度是 O(V+E)
    • 其中,V 表示頂點的個數,E 表示邊的個數。
    • 對於一個連通圖來說,也就是說一個圖中的所有頂點都是連通的,E肯定要大於等於 V-1,所以,廣度優先搜索的時間複雜度也可以簡寫為 O(E)。
    • 廣度優先搜索的空間消耗主要在幾個輔助變量 visited 數組、queue 隊列、prev 數組上。
    • 這三個存儲空間的大小都不會超過頂點的個數,所以空間複雜度是 O(V)

    三、深度優先搜索(DFS)

    • 深度優先搜索(Depth-First-Search),簡稱DFS。
    • 最直觀的例子就是“走迷宮,假設站在迷宮的某個岔路口,然後想找到出口。
    • 隨意選擇一個岔路口來走,走着走着發現走不通的時候,就回退到上一個岔路口,重新選擇一條路繼續走,直到最終找到出口。這種走法就是一種深度優先搜索策略。
    • 如下圖所示,在圖中應用深度優先搜索,來找某個頂點到另一個頂點的路徑。
    • 搜索的起始頂點是 s,終止頂點是 t,在圖中尋找一條從頂點 s 到頂點 t 的路徑。
    • 用深度遞歸算法,把整個搜索的路徑標記出來了。實線箭頭表示遍歷,虛線箭頭表示回退。
    • 從圖中可以看出,深度優先搜索找出來的路徑,並不是頂點 s 到頂點 t 的最短路徑。

    3.1、實現過程

    // 全局變量或者類成員變量,標記是否找到終點 t
    boolean found = false;
    
    /**
     * 深度優先搜索
     *
     * @param s 起始頂點
     * @param t 終止頂點
     */
    public void dfs(int s, int t) {
        found = false;
        // 標記頂點是否被訪問
        boolean[] visited = new boolean[v];
        // prev 用來記錄搜索路徑,prev[w] = a 表示 w 頂點的上一級節點為 a
        int[] prev = Arrays.stream(new int[v])
                .map(f -> -1).toArray();
    
        recurDfs(s, t, visited, prev);
        print(prev, s, t);
    }
    
    private void recurDfs(int w, int t, boolean[] visited, int[] prev) {
        if (found == true) {
            return;
        }
        visited[w] = true;
        if (w == t) {
            found = true;
            return;
        }
        LinkedList<Integer> wLinked = adj[w];
        for (int i = 0; i < wLinked.size(); ++i) {
            int q = wLinked.get(i);
            if (!visited[q]) {
                prev[q] = w;
                recurDfs(q, t, visited, prev);
            }
        }
    }
    

    3.2、複雜度分析

    • 深度搜索中每條邊最多會被訪問兩次,一次是遍歷,一次是回退。
    • 所以,深度優先搜索算法的時間複雜度是 O(E), E 表示邊的個數。
    • 深度優先搜索算法的消耗內存主要是 visited、 prev 數組和遞歸調用棧。
    • visited、 prev 數組的大小跟頂點的個數V成正比,遞歸調用棧的最大深度不會超過頂點的個數,所以總的空間複雜度就是 O(V)

    四,兩者對比

    • 廣度優先搜索和深度優先搜索是圖上的兩種最常用、最基本的搜索算法,比起其他高級的搜索算法,比如A、 IDA等,要簡單粗暴,沒有什麼優化,所以,也被
      叫作暴力搜索算法。
    • 所以,這兩種搜索算法僅適用於狀態空間不大,也就是說圖不大的搜索。
    • 廣度優先搜索,通俗的理解就是,地毯式層層推進,從起始頂點開始,依次往外遍歷。
    • 廣度優先搜索需要藉助隊列來實現,遍歷得到的路徑就是,起始頂點到終止頂點的最短路徑。
    • 深度優先搜索用的是回溯思想,非常適合用遞歸實現。換種說法,深度優先搜索是藉助棧來實現的。
    • 在執行效率方面,深度優先和廣度優先搜索的時間複雜度都是 O(E),空間複雜度是 O(V)。

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

    【其他文章推薦】

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

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

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

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

    ※超省錢租車方案