標籤: 銷售文案

  • Python異常處理

    Python異常處理

    Python中的異常處理

    異常分類

      程序中難免出現錯誤,總共可分為兩種。

     

      1.邏輯錯誤

      2.語法錯誤

     

      對於剛接觸編程的人來說,這兩個錯誤都會經常去犯,但是隨着經驗慢慢的積累,語法錯誤的情況會越來越少反而邏輯錯誤的情況會越來越多(因為工程量巨大)。不論多麼老道的程序員都不可避免出現這兩種錯誤。

     

    異常的三大信息

      異常其實就是程序運行時發生錯誤的信號,我們寫代碼的過程中不可避免也最害怕的就是出現異常,然而當程序拋出異常時實際上會分為三部分,即三大信息。

     

     

    常用的異常類

      在Python中一切皆對象,異常本身也是由一個類生成的,NameError其實本身就是一個異常類,其他諸如此類的異常類還有很多。

    Python中常見的異常類 
    AttributeError 試圖訪問一個對象沒有的屬性,比如foo.x,但是foo並沒有屬性x
    IOError 輸入/輸出異常;基本上是無法打開文件
    ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
    IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
    IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
    KeyError 試圖訪問字典里不存在的鍵
    KeyboardInterrupt Ctrl+C被按下
    NameError 試圖使用一個還未被賦予對象的變量
    SyntaxError Python代碼非法,代碼不能編譯(其實就是語法錯誤,寫錯了)
    TypeError 傳入對象類型與要求的不符合
    UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的 全局變量,導致你以為正在訪問它
    ValueError 傳入一個調用者不期望的值,即使值的類型是正確的

     

    異常處理

      我們可以來用某些方法進行異常捕捉,當出現異常時我們希望代碼以另一種邏輯運行,使得我們的程序更加健壯,這個就叫做異常處理。異常處理是非常重要的,本身也並不複雜,千萬不可馬虎大意。

      但是切記不可濫用異常處理,這會使得你的代碼可讀性變差。

     

    if else處理異常

      ifelse本身就具有處理異常的功能,他們更多的是在我們能預測到可能出現的範圍內進行規避異常,對於我們不能預測的異常來說就顯得不是那麼的好用。如下:

    # ==== if else 處理異常 ====
    
    while 1:
        select = input("請輸入数字0進行關機:").strip()
        if select.isdigit():  # 我們可以防止用戶輸入非数字的字符
            if select == "0":
                print("正在關機...")
                break
        print("輸入有誤,請重新輸入")
    
    # ==== 執行結果 ====
    
    """
    請輸入数字0進行關機:關機
    輸入有誤,請重新輸入
    請輸入数字0進行關機:關機啊
    輸入有誤,請重新輸入
    請輸入数字0進行關機:0
    正在關機...
    """

      這種異常處理機制雖然非常簡單,但是並不靈活,我們可以使用更加簡單的方式來處理他們。

     

    try except處理異常

     

      try:代表要檢測可能出現異常的代碼塊

      except:當異常出現后的處理情況

     

      執行流程:

        try中檢測的代碼塊 —> 如果有異常 —> 執行except代碼塊 —> 執行正常邏輯代碼 —> 程序結束

        try中檢測的代碼塊 —> 如果沒有異常 —> 執行完畢try中的代碼塊 —> 執行正常邏輯代碼 —> 程序結束

     

    # ====  try except 執行流程 有異常的情況 ====
    
    li = [1,2,3]
    
    try:
        print("開始執行我try了...")
        print(li[10])  # 出錯點...
        print("繼續執行我try...")
    except IndexError as e:
        print("有異常執行我except...")
    
    print("正常邏輯代碼...")
    
    # ==== 執行結果 ====
    
    """
    開始執行我try了...
    有異常執行我except...
    正常邏輯代碼...
    """ 
    # ====  try except 執行流程 無異常的情況 ====
    
    li = [1,2,3]
    
    try:
        print("開始執行我try了...")
        print(li[2])
        print("繼續執行我try...")
    except IndexError as e:
        print("有異常執行我except...")
    
    print("正常邏輯代碼...")
    
    # ==== 執行結果 ====
    
    """
    開始執行我try了...
    3
    繼續執行我try...
    正常邏輯代碼...
    """ 
    # ==== try except 處理異常 ====
    
    while 1:
        try:  # try檢測可能出錯的語句,一旦出錯立馬跳轉到except語句塊執行代碼。
            select = int(input("請輸入数字0進行關機:").strip())
            if select == 0:
                print("正在關機...")
                break
            print("輸入有誤,請重新輸入...")
        except ValueError as e:  # 當執行完except的代碼塊后,程序運行結束,其中e代表的是異常信息。
            print("錯誤信息是:",e)
            print("輸入有誤,請重新輸入")
    
    # ==== 執行結果 ====
    
    """
    請輸入数字0進行關機:1
    輸入有誤,請重新輸入...
    請輸入数字0進行關機:tt
    錯誤信息是: invalid literal for int() with base 10: 'tt'
    輸入有誤,請重新輸入
    請輸入数字0進行關機:0
    正在關機...
    """

     

    多段except捕捉多異常

      我們可以使用try和多段except的語法來檢測某一代碼塊,可以更加方便的應對更多類型的錯誤,Ps不常用:

    # ==== 多段 except 捕捉多異常 ====
    
    while 1:
    
        li = [1,2,3,4]
        dic = {"name":"Yunya","age":18}
    
        li_index = input("請輸入索引:")
        dic_key = input("請輸入鍵的名稱:")
    
        if li_index.isdigit():
            li_index = int(li_index)
    
        try:
            print(li[li_index])
            print(dic[dic_key])
        except IndexError as e1:  # 注意,先拋出的錯誤會直接跳到其處理的except代碼塊,而try下面的語句將不會被執行。
            print("索引出錯啦!")
        except KeyError as e2:
            print("鍵出錯啦!")
    
    # ==== 執行結果 ====
    
    """
    請輸入索引:10
    請輸入鍵的名稱:gender
    索引出錯啦!
    請輸入索引:2
    請輸入鍵的名稱:gender
    3
    鍵出錯啦!
    """

     

    元組捕捉多異常

      使用多段except捕捉多異常會顯得特別麻煩,這個時候我們可以使用(異常類1,異常類2)來捕捉多異常,但是需要注意的是,對比多段except捕捉多異常來說,這種方式的處理邏輯會顯得較為複雜(因為只有一段處理邏輯),如下:

    # ====  元組捕捉多異常 ====
    
    while 1:
    
        li = [1,2,3,4]
        dic = {"name":"Yunya","age":18}
    
        li_index = input("請輸入索引:")
        dic_key = input("請輸入鍵的名稱:")
    
        if li_index.isdigit():
            li_index = int(li_index)
    
        try:
            print(li[li_index])
            print(dic[dic_key])
        except (IndexError,KeyError) as e:  # 使用()的方式可以同時捕捉很多異常。
            print("出錯啦!")
    
    # ==== 執行結果 ====
    
    """
    請輸入索引:10
    請輸入鍵的名稱:gender
    出錯啦!
    請輸入索引:2
    請輸入鍵的名稱:gender
    3
    出錯啦!
    """

      可以看到,不管是那種錯誤都只有一種應對策略,如果我們想要多種應對策略就只能寫if判斷來判斷異常類型再做處理。所以就會顯得很麻煩,如下:

    # ====  元組捕捉多異常 ====
    
    while 1:
    
        li = [1,2,3,4]
        dic = {"name":"Yunya","age":18}
    
        li_index = input("請輸入索引:")
        dic_key = input("請輸入鍵的名稱:")
    
        if li_index.isdigit():
            li_index = int(li_index)
    
        try:
            print(li[li_index])
            print(dic[dic_key])
        except (IndexError,KeyError) as e:
            # 判斷異常類型再做出相應的對應策略
            if isinstance(e,IndexError):
                print("索引出錯啦!")
            elif isinstance(e,KeyError):
                print("鍵出錯啦!")
    
    # ==== 執行結果 ====
    
    """
    請輸入索引:10
    請輸入鍵的名稱:gender
    索引出錯啦!
    請輸入索引:2
    請輸入鍵的名稱:gender
    3
    鍵出錯啦!
    """

     

    萬能異常Exception

      我們可以捕捉Exception類引發的異常,它是所有異常類的基類。(Exception類的父類則是BaseException類,而BaseException的父類則是object類)

    # ====  萬能異常Exception ====
    
    while 1:
    
        li = [1,2,3,4]
        dic = {"name":"Yunya","age":18}
    
        li_index = input("請輸入索引:")
        dic_key = input("請輸入鍵的名稱:")
    
        if li_index.isdigit():
            li_index = int(li_index)
    
        try:
            print(li[li_index])
            print(dic[dic_key])
        except Exception as e:  #使用 Exception來捕捉所有異常。
            # 判斷異常類型再做出相應的對應策略
            if isinstance(e,IndexError):
                print("索引出錯啦!")
            elif isinstance(e,KeyError):
                print("鍵出錯啦!")
    
    # ==== 執行結果 ====
    
    """
    請輸入索引:10
    請輸入鍵的名稱:gender
    索引出錯啦!
    請輸入索引:2
    請輸入鍵的名稱:gender
    3
    鍵出錯啦!
    """

     

     

    try except else聯用

      這種玩法比較少,else代表沒有異常發生的情況下執行的代碼,執行順序如下:

     

      try中檢測的代碼塊 —> 如果有異常 —> 終止try中的代碼塊繼續執行 —> 執行except代碼塊 —> 執行正常邏輯代碼 —> 程序結束

      try中檢測的代碼塊 —> 如果沒有異常 —> 執行完畢try中的代碼塊 —> 執行else代碼塊 —> 執行正常邏輯代碼 —> 程序結束

     

    # ====  try except else聯用 有異常的情況====
    
    li = [1,2,3]
    
    try:
        print("開始執行我try了...")
        print(li[10])  # 出錯點...
        print("繼續執行我try...")
    except IndexError as e:
        print("有異常執行我except...")
    else:
        print("沒有異常執行我else...")
    print("正常邏輯代碼...")
    
    # ==== 執行結果 ====
    
    """
    開始執行我try了...
    有異常執行我except...
    正常邏輯代碼...
    """
    # ====  try except else聯用 無異常的情況====
    
    li = [1,2,3]
    
    try:
        print("開始執行我try了...")
        print(li[2])
        print("繼續執行我try...")
    except IndexError as e:
        print("有異常執行我except...")
    else:
        print("沒有異常執行我else...")
    print("正常邏輯代碼...")
    
    # ==== 執行結果 ====
    
    """
    開始執行我try了...
    3
    繼續執行我try...
    沒有異常執行我else...
    正常邏輯代碼...
    """

     

    try except finally聯用

      finally代表不論拋異常與否都會執行,因此常被用作關閉系統資源的操作,關於try,except,else,finally他們的優先級如下:

     

      有異常的情況下:

        try代碼塊

        終止try代碼塊繼續執行

        except代碼塊

        finally代碼塊

        正常邏輯代碼

     

      無異常的情況下:

        try代碼塊

        else代碼塊

        finally代碼塊

        正常邏輯代碼

     

    # ====  try except else finally 執行流程 有異常的情況 ====
    
    li = [1,2,3]
    
    try:
        print("開始執行我try了...")
        print(li[10])  # 出錯點...
        print("繼續執行我try...")
    except IndexError as e:
        print("有異常執行我except...")
    else:
        print("沒有異常執行我else...")
    finally:
        print("不管有沒有異常都執行我finally...")
    
    print("正常邏輯代碼...")
    
    # ==== 執行結果 ====
    
    """
    開始執行我try了...
    有異常執行我except...
    不管有沒有異常都執行我finally...
    正常邏輯代碼...
    """
    # ====  try except else finally 執行流程 無異常的情況 ====
    
    li = [1,2,3]
    
    try:
        print("開始執行我try了...")
        print(li[2])
        print("繼續執行我try...")
    except IndexError as e:
        print("有異常執行我except...")
    else:
        print("沒有異常執行我else...")
    finally:
        print("不管有沒有異常都執行我finally...")
    
    print("正常邏輯代碼...")
    
    # ==== 執行結果 ====
    
    """
    開始執行我try了...
    3
    繼續執行我try...
    沒有異常執行我else...
    不管有沒有異常都執行我finally...
    正常邏輯代碼...
    """

     

    自定義異常

    raise主動拋出異常

      在某些時候我們可能需要主動的去阻止程序的運行,主動的拋出一個異常。可以使用raise來進行操作。這個是一種非常常用的手段。

    # ====  raise使用方法  ====
    
    print("----1----")
    print("----2----")
    print("----3----")
    raise Exception("我也不知道是什麼類型的異常...")
    print("----4----")
    print("----5----")
    print("----6----")
    
    # ==== 執行結果 ====
    
    """
    ----1----
    ----2----
    ----3----
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/learn/元類編程.py", line 6, in <module>
        raise Exception("我也不知道是什麼類型的異常...")
    Exception: 我也不知道是什麼類型的異常...
    
    Process finished with exit code 1
    """

     

    自定義異常類

      前面已經說過一切皆對象,異常也來自一個對象。因此我們也可以自己來定製一個對象。注意,自定義異常類必須繼承BaseException類。

    # ====  自定義異常類  ====
    
    class MyError(BaseException):
        pass
    
    raise MyError("我的異常")
    
    
    
    # ==== 執行結果 ====
    
    """
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/learn/元類編程.py", line 6, in <module>
        raise MyError("我的異常")
    __main__.MyError: 我的異常
    """

     

    擴展:斷言assert

      斷言是一個十分裝逼的使用,假設多個函數進行計算,我們已經有了預期的結果只是在做一個算法的設計。如果函數的最後的結果不是我們本來預期的結果那麼寧願讓他停止運行也不要讓錯誤繼續擴大,在這種情況下就可以使用斷言操作,使用斷言會拋出一個AssertionError類的異常。

    # ====  斷言assert  ====
    
    def calculate():
        """假設在做非常複雜的運算"""
        return 3+2*5
    
    res = calculate()
    
    assert res == 25  # AssertionError
    
    print("算法測試通過!你真的太厲害了")
    
    # ==== 執行結果 ====
    
    """
    Traceback (most recent call last):
      File "C:/Users/Administrator/PycharmProjects/learn/元類編程.py", line 8, in <module>
        assert res == 25
    AssertionError
    """

     

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

    【其他文章推薦】

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

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

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

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

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

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

  • 都在講DevOps,但你知道它的發展趨勢嗎?

    都在講DevOps,但你知道它的發展趨勢嗎?

    根據最近的一項集體研究,DevOps的市場在2017年創造了約29億美元的產值,預計到2022年,這個数字將達到約66億美元。人工智能的融入和安全性的融入,加上向自動化的巨大轉變,可合理預測,在2020年,DevOps將成為軟件工程的主流模式。

    DevOps具有以下優勢:

    ●對需求變更的迅速響應

    ●超快的交付速度及靈活的安全部署

    ●建立完善的協作溝通渠道

    ●快速識別代碼中的錯誤或漏洞

    ●讓團隊將注意力集中在其他關鍵的事情上,而不是集中在安全特性上

     

    越來越多的企業正採用DevOps的產品交付模式:根據Statista的統計數據,全面採用DevOps的企業數量從2017年的約10%增長到了2018年的17%。

    而devops也將在2020年迎來新趨勢。

     

    自動化成為焦點

    實施DevOps產品交付模式的組織已經見證了極高的效率和超快速的部署速度。在提到DevOps時,我們主要討論的是DevOps自動化,零接觸自動化是未來的發展方向。在DevOps生命周期的7C(持續發展、持續集成、持續測試、持續反饋、持續監測、持續部署、持續運維)中,應用自動化是未來的關鍵,因為預計這將是2020年的主要目標之一。

    注意力從CI管道轉移到DevOps的裝配線

    DevOps的重要目標是改進交付過程的計劃階段和自動化階段之間的協作。這不僅僅關乎CI(持續集成),更重要的是關乎CD(持續交付)。許多組織正在投入額外的精力和時間來使公司軟件開發的整個過程自動化。因此,對於這些組織來說,現在是聯繫DevOps諮詢服務提供商的時候了。預計到2020年,注意力將從CI管道轉移到DevOps的裝配線。裝配線的一些共同優點如下:

    ●原生集成

    ●堅固的嵌套可見性

    ●適當互用性的完美持續交付

    ●基於團隊的分析以及商業智能

    ●快速實現和擴展“一切皆代碼”理念

     

    對無服務器架構的使用增加

    使用無服務器架構可以將DevOps提升到更高的水平,這並不意味着沒有服務器,而是使用雲服務的整體架構。FaaS(Function as a Service,功能即服務)和BaaS(Backend as a Service,後端即服務)是無服務器架構的兩個關鍵方面。通過採用這種無服務器體繫結構,企業可以節省時間、降低成本,並擁有具有彈性的、靈活的工作流。

    “一切皆代碼”的概念

    程序編碼是IT部門及其服務系統的骨幹。對DevOps自動化工具和腳本的充分理解將支配整個2020年。這個特定IT領域的前景與產品的未來取決於開發人員、測試人員及運維人員的技術能力。現在,隨着交付周期的縮短,需要引入代碼來提高軟件生產周期的效率。“一切皆代碼”的概念是在DevOps內部完成代碼的SDLC的實踐。如果軟件測試人員還不開始學習編程和編寫測試腳本,工作很可能會受到阻礙。

    更好的嵌入式安全性

    隨着安全漏洞的出現,越來越多的大小企業意識到網絡安全的重要性。2020年,DevOps預計將迅速將安全問題納入流程。DevSecOps首先在應用程序的開發生命周期中注入安全性,這有助於減少各種缺陷和漏洞,增加業務信譽。公司轉向DevSecOps促使項目中每個人都擔負安全方面的責任,這將在軟件開發過程中帶來很棒的協作,因為它確保了軟件開發過程始終保持完美、高效和可操作。

    人工智能的興起和數據科學的飛速發展

    隨着人工智能驅動的應用程序大量增加,數據科學正在推動越來越多的公司在其工作流程中採用DevOps理念。隨着數據科學和開發團隊在軟件開發、部署以及人工智能驅動的應用程序管理方面的效率越來越高,這將會進一步推動數據科學的發展。

    2020年的主要目標是實現零接觸自動化。 持續不斷的人工智能和數據科學熱潮改變着遊戲規則。 許多應用程序都引入了人工智能,這已經促使多個DevOps團隊通過人工智能和數據科學實現自動化,數據科學團隊和開發團隊相輔相成地提高彼此的技能與交付水平。

    對無服務器架構的使用增加

    使用無服務器架構可以將DevOps提升到更高的水平,這並不意味着沒有服務器,而是使用雲服務的整體架構。FaaS(Function as a Service,功能即服務)和BaaS(Backend as a Service,後端即服務)是無服務器架構的兩個關鍵方面。通過採用這種無服務器體繫結構,企業可以節省時間、降低成本,並擁有具有彈性的、靈活的工作流。

    Kubernetes長足發展

    Kubernetes提供了基於容器技術的分佈式架構領先方案產品,因自身性能及易用性,已經成為應用廣泛的容器技術。伴隨着各類企業進一步通過深度採用容器技術來運行它們的雲原生應用,K8s將會迎來更廣的普及、更大的發展。

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

    【其他文章推薦】

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

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

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

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

    ※超省錢租車方案

  • 使用 Masstransit中的 Request/Response 與 Courier 功能實現最終一致性

    簡介

      目前的.net 生態中,最終一致性組件的選擇一直是一個問題。本地事務表(cap)需要在每個服務的數據庫中插入消息表,而且做不了此類事務 比如:創建訂單需要 餘額滿足+庫存滿足,庫存和餘額處於兩個服務中。masstransit 是我目前主要用的方案。以往一般都用 masstransit 中的 sagas 來實現 最終一致性,但是隨着併發的增加必定會對sagas 持久化的數據庫造成很大的壓力,根據stackoverflow 中的一個回答 我發現了 一個用  Request/Response 與 Courier 功能 實現最終一致性的方案 Demo地址。

    Masstransit 中 Resquest/Response 功能 

     消息DTO

        public class SampleMessageCommand
        {
        }
    

     消費者

        public class SampleMessageCommandHandler : IConsumer<SampleMessageCommand>
        {
            public async Task Consume(ConsumeContext<SampleMessageCommand> context)
            {
                await context.RespondAsync(new SampleMessageCommandResult() { Data = "Sample" });
            }
        }
    

     返回結果DTO

     

        public class SampleMessageCommandResult
        {
            public string Data { get; set; }
        }
    

     調用方式與註冊方式略過,詳情請看 官方文檔。

      

      本質上使用消息隊列實現 Resquest/Response,客戶端(生產者)將請求消息發送至指定消息隊列並賦予RequestId和ResponseAddress(臨時隊列 rabbitmq),服務端(消費者)消費消息並把 需要返回的消息放入指定ResponseAddress,客戶端收到 Response message  通過匹配 RequestId 找到 指定Request,最後返回信息。

    Masstransit 中 Courier  功能

      通過有序組合一系列的Activity,得到一個routing slip。每個 activity(忽略 Execute Activities) 都有 Execute 和 Compensate 兩個方法。Compensate 用來執撤銷 Execute 方法產生的影響(就是回退 Execute 方法)。每個 Activity Execute 最後都會 調用 Completed 方法把 回退所需要的的信息記錄在message中,最後持久化到消息隊列的某一個消息中。

     餘額扣減的Activity ,這裏的 DeductBalanceModel 是請求扣減的數據模型,DeductBalanceLog 是回退時需要用到的信息。

    public class DeductBalanceActivity : IActivity<DeductBalanceModel, DeductBalanceLog>
        {
            private readonly ILogger<DeductBalanceActivity> logger;
            public DeductBalanceActivity(ILogger<DeductBalanceActivity> logger)
            {
                this.logger = logger;
            }
            public async Task<CompensationResult> Compensate(CompensateContext<DeductBalanceLog> context)
            {
                logger.LogInformation("還原餘額");
                var log = context.Log; //可以獲取 所有execute 完成時保存的信息
                //throw new ArgumentException("some things were wrong");
                return context.Compensated();
            }
    
            public async Task<ExecutionResult> Execute(ExecuteContext<DeductBalanceModel> context)
            {
    
                logger.LogInformation("扣減餘額");
                await Task.Delay(100);
                return context.Completed(new DeductBalanceLog() { Price = 100 });
            }
        }

     

          扣減庫存 Activity

        public class DeductStockActivity : IActivity<DeductStockModel, DeductStockLog>
        {
            private readonly ILogger<DeductStockActivity> logger;
            public DeductStockActivity(ILogger<DeductStockActivity> logger)
            {
                this.logger = logger;
            }
            public async Task<CompensationResult> Compensate(CompensateContext<DeductStockLog> context)
            {
                var log = context.Log;
                logger.LogInformation("還原庫存");
                return context.Compensated();
            }
    
            public async Task<ExecutionResult> Execute(ExecuteContext<DeductStockModel> context)
            {
                var argument = context.Arguments;
                logger.LogInformation("扣減庫存");
                await Task.Delay(100);
                return context.Completed(new DeductStockLog() { ProductId = argument.ProductId, Amount = 1 });
            }
        }
    

           生成訂單 Execute Activity

        public class CreateOrderActivity : IExecuteActivity<CreateOrderModel>
        {
            private readonly ILogger<CreateOrderActivity> logger;
            public CreateOrderActivity(ILogger<CreateOrderActivity> logger)
            {
                this.logger = logger;
            }
            public async Task<ExecutionResult> Execute(ExecuteContext<CreateOrderModel> context)
            {
                logger.LogInformation("創建訂單");
                await Task.Delay(100);
                //throw new CommonActivityExecuteFaildException("當日訂單已達到上限");
                return context.CompletedWithVariables(new CreateOrderResult { OrderId="111122",Message="創建訂單成功" });
            }
        }
    

      組裝 以上 Activity 生成一個 Routing Slip,這是一個有序的組合,扣減庫存=》扣減餘額=》生成訂單

                var builder = new RoutingSlipBuilder(NewId.NextGuid());
    builder.AddActivity("DeductStock", new Uri($"{configuration["RabbitmqConfig:HostUri"]}/DeductStock_execute"), new DeductStockModel { ProductId = request.Message.ProductId }); builder.AddActivity("DeductBalance", new Uri($"{configuration["RabbitmqConfig:HostUri"]}/DeductBalance_execute"), new DeductBalanceModel { CustomerId = request.Message.CustomerId, Price = request.Message.Price }); builder.AddActivity("CreateOrder", new Uri($"{configuration["RabbitmqConfig:HostUri"]}/CreateOrder_execute"), new CreateOrderModel { Price = request.Message.Price, CustomerId = request.Message.CustomerId, ProductId = request.Message.ProductId });
    var routingSlip = builder.Build();

      執行 Routing Slip

    await bus.Execute(routingSlip);
    

      

          這裡是沒有任何返回值的,所有activity都是 異步執行,雖然所有的activity可以執行完成或者由於某個Activity執行出錯而全部回退。(其實這裡有一種更壞的情況就是 Compensate 出錯,默認情況下 Masstransit 只會發送一個回退錯誤的消息,後面講到創建訂單的時候我會把它塞到錯誤隊列里,這樣我們可以通過修改 Compensate bug后重新導入到正常隊列來修正數據),這個功能完全滿足不了 創建訂單這個需求,執行 await bus.Execute(routingSlip) 后我們完全不知道訂單到底創建成功,還是由於庫存或餘額不足而失敗了(異步)。

         還好 routing slip 在執行過程中產生很多消息,比如 RoutingSlipCompleted ,RoutingSlipCompensationFailed ,RoutingSlipActivityCompleted,RoutingSlipActivityFaulted 等,具體文檔,我們可以訂閱這些事件,再結合Request/Response 實現 創建訂單的功能。

    實現創建訂單(庫存滿足+餘額滿足)長流程

    創建訂單 command 

        /// <summary>
        /// 長流程 分佈式事務
        /// </summary>
        public class CreateOrderCommand
        {
            public string ProductId { get; set; }
            public string CustomerId { get; set; }
            public int Price { get; set; }
        }

      事務第一步,扣減庫存相關 代碼

      public class DeductStockActivity : IActivity<DeductStockModel, DeductStockLog>
        {
            private readonly ILogger<DeductStockActivity> logger;
            public DeductStockActivity(ILogger<DeductStockActivity> logger)
            {
                this.logger = logger;
            }
            public async Task<CompensationResult> Compensate(CompensateContext<DeductStockLog> context)
            {
                var log = context.Log;
                logger.LogInformation("還原庫存");
                return context.Compensated();
            }
    
            public async Task<ExecutionResult> Execute(ExecuteContext<DeductStockModel> context)
            {
                var argument = context.Arguments;
                logger.LogInformation("扣減庫存");
                await Task.Delay(100);
                return context.Completed(new DeductStockLog() { ProductId = argument.ProductId, Amount = 1 });
            }
        }
        public class DeductStockModel
        {
            public string ProductId { get; set; }
        }
        public class DeductStockLog
        {
            public string ProductId { get; set; }
            public int Amount { get; set; }
        }

     事務第二步,扣減餘額相關代碼

    public class DeductBalanceActivity : IActivity<DeductBalanceModel, DeductBalanceLog>
        {
            private readonly ILogger<DeductBalanceActivity> logger;
            public DeductBalanceActivity(ILogger<DeductBalanceActivity> logger)
            {
                this.logger = logger;
            }
            public async Task<CompensationResult> Compensate(CompensateContext<DeductBalanceLog> context)
            {
                logger.LogInformation("還原餘額");
                var log = context.Log;
                //throw new ArgumentException("some things were wrong");
                return context.Compensated();
            }
    
            public async Task<ExecutionResult> Execute(ExecuteContext<DeductBalanceModel> context)
            {
    
                logger.LogInformation("扣減餘額");
                await Task.Delay(100);
                return context.Completed(new DeductBalanceLog() { Price = 100 });
            }
        }
        public class DeductBalanceModel
        {
            public string CustomerId { get; set; }
            public int Price { get; set; }
        }
        public class DeductBalanceLog
        {
            public int Price { get; set; }
        }

     事務第三步,創建訂單相關代碼

     public class CreateOrderActivity : IExecuteActivity<CreateOrderModel>
        {
            private readonly ILogger<CreateOrderActivity> logger;
            public CreateOrderActivity(ILogger<CreateOrderActivity> logger)
            {
                this.logger = logger;
            }
            public async Task<ExecutionResult> Execute(ExecuteContext<CreateOrderModel> context)
            {
                logger.LogInformation("創建訂單");
                await Task.Delay(100);
                //throw new CommonActivityExecuteFaildException("當日訂單已達到上限");
                return context.CompletedWithVariables(new CreateOrderResult { OrderId="111122",Message="創建訂單成功" });
            }
        }
        public class CreateOrderModel
        {
            public string ProductId { get; set; }
            public string CustomerId { get; set; }
            public int Price { get; set; }
        }
        public class CreateOrderResult
        {
            public string OrderId { get; set; }
            public string Message { get; set; }
        }

       我通過 消費 創建訂單 request,獲取 request 的 response 地址與 RequestId,這兩個值 返回 response 時需要用到,我把這些信息存到 RoutingSlip中,並且訂閱 RoutingSlipEvents.Completed | RoutingSlipEvents.Faulted | RoutingSlipEvents.CompensationFailed 三種事件,當這三種消息出現時 我會根據 事件類別 和RoutingSlip中 之前加入的 (response 地址與 RequestId)生成 Response ,整個過程大概就是這麼個意思,沒理解可以看demo。這裏由於每一個事物所需要用到的 RoutingSlip + Request/Response 步驟都類似 可以抽象一下(模板方法),把Activity 的組裝 延遲到派生類去解決,這個代理類Masstransit有 ,但是官方沒有顧及到 CompensationFailed 的情況,所以我乾脆自己再寫一個。

        public abstract class RoutingSlipDefaultRequestProxy<TRequest> :
            IConsumer<TRequest>
            where TRequest : class
        {
            public async Task Consume(ConsumeContext<TRequest> context)
            {
                var builder = new RoutingSlipBuilder(NewId.NextGuid());
    
                builder.AddSubscription(context.ReceiveContext.InputAddress, RoutingSlipEvents.Completed | RoutingSlipEvents.Faulted | RoutingSlipEvents.CompensationFailed);
                
                builder.AddVariable("RequestId", context.RequestId);
                builder.AddVariable("ResponseAddress", context.ResponseAddress);
                builder.AddVariable("FaultAddress", context.FaultAddress);
                builder.AddVariable("Request", context.Message);
    
                await BuildRoutingSlip(builder, context);
    
                var routingSlip = builder.Build();
    
                await context.Execute(routingSlip).ConfigureAwait(false);
            }
    
            protected abstract Task BuildRoutingSlip(RoutingSlipBuilder builder, ConsumeContext<TRequest> request);
        }


     這個 是派生類 Routing slip 的拼裝過程 

        public class CreateOrderRequestProxy : RoutingSlipDefaultRequestProxy<CreateOrderCommand>
    
        {
            private readonly IConfiguration configuration;
            public CreateOrderRequestProxy(IConfiguration configuration)
            {
                this.configuration = configuration;
            }
            protected override Task BuildRoutingSlip(RoutingSlipBuilder builder, ConsumeContext<CreateOrderCommand> request)
            {
                builder.AddActivity("DeductStock", new Uri($"{configuration["RabbitmqConfig:HostUri"]}/DeductStock_execute"), new DeductStockModel { ProductId = request.Message.ProductId });
    
                builder.AddActivity("DeductBalance", new Uri($"{configuration["RabbitmqConfig:HostUri"]}/DeductBalance_execute"), new DeductBalanceModel { CustomerId = request.Message.CustomerId, Price = request.Message.Price });
    
                builder.AddActivity("CreateOrder", new Uri($"{configuration["RabbitmqConfig:HostUri"]}/CreateOrder_execute"), new CreateOrderModel { Price = request.Message.Price, CustomerId = request.Message.CustomerId, ProductId = request.Message.ProductId });
    
                return Task.CompletedTask;
            }
        }

      構造response 基類,主要是對三種情況做處理。

     

        public abstract class RoutingSlipDefaultResponseProxy<TRequest, TResponse, TFaultResponse> : IConsumer<RoutingSlipCompensationFailed>, IConsumer<RoutingSlipCompleted>,
            IConsumer<RoutingSlipFaulted>
            where TRequest : class
            where TResponse : class
            where TFaultResponse : class
        {
            public async Task Consume(ConsumeContext<RoutingSlipCompleted> context)
            {
                var request = context.Message.GetVariable<TRequest>("Request");
                var requestId = context.Message.GetVariable<Guid>("RequestId");
    
                Uri responseAddress = null;
                if (context.Message.Variables.ContainsKey("ResponseAddress"))
                    responseAddress = context.Message.GetVariable<Uri>("ResponseAddress");
    
                if (responseAddress == null)
                    throw new ArgumentException($"The response address could not be found for the faulted routing slip: {context.Message.TrackingNumber}");
    
                var endpoint = await context.GetResponseEndpoint<TResponse>(responseAddress, requestId).ConfigureAwait(false);
    
                var response = await CreateResponseMessage(context, request);
    
                await endpoint.Send(response).ConfigureAwait(false);
            }
    
            public async Task Consume(ConsumeContext<RoutingSlipFaulted> context)
            {
                var request = context.Message.GetVariable<TRequest>("Request");
                var requestId = context.Message.GetVariable<Guid>("RequestId");
    
                Uri faultAddress = null;
                if (context.Message.Variables.ContainsKey("FaultAddress"))
                    faultAddress = context.Message.GetVariable<Uri>("FaultAddress");
                if (faultAddress == null && context.Message.Variables.ContainsKey("ResponseAddress"))
                    faultAddress = context.Message.GetVariable<Uri>("ResponseAddress");
    
                if (faultAddress == null)
                    throw new ArgumentException($"The fault/response address could not be found for the faulted routing slip: {context.Message.TrackingNumber}");
    
                var endpoint = await context.GetFaultEndpoint<TResponse>(faultAddress, requestId).ConfigureAwait(false);
    
                var response = await CreateFaultedResponseMessage(context, request, requestId);
    
                await endpoint.Send(response).ConfigureAwait(false);
            }
            public async Task Consume(ConsumeContext<RoutingSlipCompensationFailed> context)
            {
                var request = context.Message.GetVariable<TRequest>("Request");
                var requestId = context.Message.GetVariable<Guid>("RequestId");
    
                Uri faultAddress = null;
                if (context.Message.Variables.ContainsKey("FaultAddress"))
                    faultAddress = context.Message.GetVariable<Uri>("FaultAddress");
                if (faultAddress == null && context.Message.Variables.ContainsKey("ResponseAddress"))
                    faultAddress = context.Message.GetVariable<Uri>("ResponseAddress");
    
                if (faultAddress == null)
                    throw new ArgumentException($"The fault/response address could not be found for the faulted routing slip: {context.Message.TrackingNumber}");
    
                var endpoint = await context.GetFaultEndpoint<TResponse>(faultAddress, requestId).ConfigureAwait(false);
    
                var response = await CreateCompensationFaultedResponseMessage(context, request, requestId);
    
                await endpoint.Send(response).ConfigureAwait(false);
            }
            protected abstract Task<TResponse> CreateResponseMessage(ConsumeContext<RoutingSlipCompleted> context, TRequest request);
    
            protected abstract Task<TFaultResponse> CreateFaultedResponseMessage(ConsumeContext<RoutingSlipFaulted> context, TRequest request, Guid requestId);
            protected abstract Task<TFaultResponse> CreateCompensationFaultedResponseMessage(ConsumeContext<RoutingSlipCompensationFailed> context, TRequest request, Guid requestId);
        }

     Response 派生類 ,這裏邏輯可以隨自己定義,我也是隨便寫了個 CommonResponse和一個業務錯誤拋錯(犧牲了一點性能)。

        public class CreateOrderResponseProxy :
                RoutingSlipDefaultResponseProxy<CreateOrderCommand, CommonCommandResponse<CreateOrderResult>, CommonCommandResponse<CreateOrderResult>>
        {
    
            protected override Task<CommonCommandResponse<CreateOrderResult>> CreateResponseMessage(ConsumeContext<RoutingSlipCompleted> context, CreateOrderCommand request)
            {
    
                return Task.FromResult(new CommonCommandResponse<CreateOrderResult>
                {
                    Status = 1,
                    Result = new CreateOrderResult
                    {
                        Message = context.Message.Variables.TryGetAndReturn(nameof(CreateOrderResult.Message))?.ToString(),
                        OrderId = context.Message.Variables.TryGetAndReturn(nameof(CreateOrderResult.OrderId))?.ToString(),
                    }
                });
            }
            protected override Task<CommonCommandResponse<CreateOrderResult>> CreateFaultedResponseMessage(ConsumeContext<RoutingSlipFaulted> context, CreateOrderCommand request, Guid requestId)
            {
                var commonActivityExecuteFaildException = context.Message.ActivityExceptions.FirstOrDefault(m => m.ExceptionInfo.ExceptionType == typeof(CommonActivityExecuteFaildException).FullName);
                if (commonActivityExecuteFaildException != null)
                {
                    return Task.FromResult(new CommonCommandResponse<CreateOrderResult>
                    {
                        Status = 2,
                        Message = commonActivityExecuteFaildException.ExceptionInfo.Message
                    });
                }
                // system error  log here
                return Task.FromResult(new CommonCommandResponse<CreateOrderResult>
                {
                    Status = 3,
                    Message = "System error"
                });
            }
    
            protected override Task<CommonCommandResponse<CreateOrderResult>> CreateCompensationFaultedResponseMessage(ConsumeContext<RoutingSlipCompensationFailed> context, CreateOrderCommand request, Guid requestId)
            {
                var exception = context.Message.ExceptionInfo;
                // lg here context.Message.ExceptionInfo
                return Task.FromResult(new CommonCommandResponse<CreateOrderResult>
                {
                    Status = 3,
                    Message = "System error"
                });           
            }
        }

    對於  CompensationFailed 的處理 通過 ActivityCompensateErrorTransportFilter 實現 發送到錯誤消息隊列,後續通過prometheus + rabbitmq-exporter + alertmanager 觸發告警 通知相關人員處理。

      public class ActivityCompensateErrorTransportFilter<TActivity, TLog> : IFilter<CompensateActivityContext<TActivity, TLog>>
            where TActivity : class, ICompensateActivity<TLog>
            where TLog : class
        {
            public void Probe(ProbeContext context)
            {
                context.CreateFilterScope("moveFault");
            }
    
            public async Task Send(CompensateActivityContext<TActivity, TLog> context, IPipe<CompensateActivityContext<TActivity, TLog>> next)
            {
                try
                {
                    await next.Send(context).ConfigureAwait(false);
                }
                catch(Exception ex)
                {
                    if (!context.TryGetPayload(out IErrorTransport transport))
                        throw new TransportException(context.ReceiveContext.InputAddress, $"The {nameof(IErrorTransport)} was not available on the {nameof(ReceiveContext)}.");
                    var exceptionReceiveContext = new RescueExceptionReceiveContext(context.ReceiveContext, ex);
                    await transport.Send(exceptionReceiveContext);
                }
            }
        }

    註冊 filter 

        public class RoutingSlipCompensateErrorSpecification<TActivity, TLog> : IPipeSpecification<CompensateActivityContext<TActivity, TLog>>
            where TActivity : class, ICompensateActivity<TLog>
            where TLog : class
        {
            public void Apply(IPipeBuilder<CompensateActivityContext<TActivity, TLog>> builder)
            {
                builder.AddFilter(new ActivityCompensateErrorTransportFilter<TActivity, TLog>());
            }
    
            public IEnumerable<ValidationResult> Validate()
            {
               yield return this.Success("success");
            }
        }
    
    
                cfg.ReceiveEndpoint("DeductStock_compensate", ep =>
                {
                    ep.PrefetchCount = 100;
                    ep.CompensateActivityHost<DeductStockActivity, DeductStockLog>(context.Container, conf =>
                     {
                         conf.AddPipeSpecification(new RoutingSlipCompensateErrorSpecification<DeductStockActivity, DeductStockLog>());
                     });
    
                });

     

    實現創建產品(創建完成+添加庫存)

    實現了 創建訂單的功能,整個流程其實是同步的,我在想能不能實現最為簡單的最終一致性 比如 創建一個產品 ,然後異步生成它的庫存 ,我發現是可以的,因為我們可以監聽到每一個Execute Activity 的完成事件,並且把出錯時的信息通過 filter 塞到 錯誤隊列中。

    這裏的代碼就不貼了,詳情請看 demo

     

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

  • 一條update SQL語句是如何執行的

    一條update SQL語句是如何執行的

      一條更新語句的執行過程和查詢語句類似,更新的流程涉及兩個日誌:redo log(重做日誌)和binlog(歸檔日誌)。比如我們要將ID(主鍵)=2這一行的值加(c:字段)1,SQL語句如下:

    update T set c=c+1 where ID=2;
    • redo log

      重做日誌是InnoDB引擎特有的,是物理日誌,記錄在“某個數據頁上做了什麼修改“。大小是固定,可以進行配置大小。假如我們配置一組4個文件,圖中write pos是當前記錄的位置,往後推移並且循環;checkpoint是當前要擦除的位置,移動規律和前者一樣。兩者之間的位置可以記錄新的操作

      

      如果write pos 追上checkpoint,就移動checkpoint擦除一些記錄。所以即使數據可以發生異常重啟,InnoDB也可以保證之前提交的記錄不會丟,這就是MySQL的crash_safe能力。

    • binlog

       歸檔日誌是MySQL的server層的實現的,所有引擎都可以使用。binlog記錄的是sql語句的原始邏輯,比如根劇’id’字段查詢所有的信息;相比redo log的循環寫入,binlog是追加寫的,binlog文件寫到一定大小後會切換到下一個,不會覆蓋以前的日誌。

      Binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新后都有。

     文章開頭的更新語句在InnoDB中的執行流程如下:深色代表在執行器中執行的,淺色是在存儲引擎中。

      最後寫入redolog分為了prepare和commit兩步,用來保證兩個日誌寫入的一致性,這就是“兩階段提交”。比如我們執行“update T set status = 1“時:

    • 如果寫入redolog成功,但寫binlog失敗,重啟恢復時,數據庫發現沒有commit,那麼事務本身回滾;備份恢復時沒有binlog,數據庫里的status值不變。
    • 如果在commit失敗,重啟恢復時redolog和binlog一致,重新commit;備份恢復時有binlog,直接恢復。

      總的來說binlog記錄了對數據庫所有的邏輯操作,可以通過binlog來備份出一份完全相同的庫;因為redolog是InnoDB引擎特有的,如果使用其他引擎,那麼就要依賴binlog來記錄操作。

    Q定期全量備份的周期“取決於系統重要性,有的是一天一備,有的是一周一備”。那麼在什麼場景下,一天一備會比一周一備更有優勢呢?或者說,它影響了這個數據庫系統的哪個指標?

    A一天一備,那麼如果需要恢複數據的話,只要保證當天的binlog完整即可;一周一備的話就要保證一周的binlog完整;同時頻繁全量備份需要更多存儲空間,如何選擇取決於業務的重要性,對應的指標是RTO(目標恢復時間)。

     — 《MySQL實戰45講》筆記二

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

  • 畢業了,我的四年大學:平凡但不平庸(寫給每一位想要認真學習的小夥伴)

    畢業了,我的四年大學:平凡但不平庸(寫給每一位想要認真學習的小夥伴)

    去年十月份的時候,我分享了一篇關於我三年大學的文章:普普通通,我的三年大學,說實話,這篇文章還是激勵了不少小夥伴,給很多人帶來了動力。寫這篇那會,我剛結束了自己的秋招,結束秋招的那一刻,可以說是 2019 年心情最放鬆的時刻了,對於沒有讀研的學生來說,秋招那一戰,可以算是整個大學最重要的一戰了,在這裏,也希望各位小夥伴,也要早點做好準備。

    時間是過的真快,一不小心就在公司實習了差不多三個月了,大學最後的作業 —- 答辯和論文,也都處理完畢了,可以說我的大學,即將要畫上一個句號了。

    我的四年大學,可以說是普普通通,在校期間也沒啥輝煌的戰績,更準確的說是,0 戰績:沒有參加過什麼大賽,甚至沒有參加過比賽;在學校也沒拿過獎學金,連個三好學生都不給;也沒去用心融入一個社團,成為裏面的一員;更沒有去做過兼職,發過海報。

    但正是因為普普通通,我才要分享自己的四年大學。一個簡單的原因就是,我的公眾號里有 65% 是在校生,並且我相信大部分人和我一樣,都是普普通通,所以我希望我在大學的做法,或許可以給你們一個參考。不過說實話,雖然我的大學沒有什麼輝煌的戰績,但我還算滿意,至少是按照我自己的計劃走過來的,並且自己的目標也都實現了。

    大一

    大一第一學期這部分的事情最多了,所以會寫的多一些,因為我覺得,在大一,我的思維發生了很大的改變,大一過後,我的想法、計劃就基本有着落了,後面基本事按照自己的計劃,去做自己喜歡的事了。

    大一第一學期的第一要務:轉專業

    看過我之前文章的讀者可能都知道,我大一第一學期不是計算機專業的,讀的專業是木材科學與工程。當然,這不是我自己選擇的專業,而是高考分數不夠,被調劑的專業,我自己熱愛的專業是計算機類專業,還沒有進入大學之前,我就說我一定要讀編程相關的專業了。

    說實話,我很慶幸自己最後轉到了計算機類的專業,一個很現實的原因就是,學編程,真的能夠掙錢,而且你願意努力學的話,你一畢業就能夠拿到很高的工資,本科畢業,有好些可以拿到年薪 50 萬,你敢信?就算工資很低,也都能年薪十幾萬,當然,我不能暴露自己的工資,暴露了就要被公司勸退了。

    是的,我讀計算機專業,有兩個重要的原因,一個原因就是熱愛,一個是家裡窮,我想掙錢,改變家裡的現狀。所以在我大一第一學期那一會,我的目標就很明確,這學期的任務就是轉專業,所以我了解了轉專業的各種規則,了解到轉專業的一個最基本的要求就是數學和英語需要 85 分以上才能參与考試。

    85 分很難嗎?不難;但是如果需要你保證 85 分難嗎?說實話,有一些,因為萬一考不好呢?考不好就得大二轉了,所以我第一學期,英語的平時分,我給刷到了 100 分,也就是滿分。數學是我的強項,但我依然不敢掉以輕心,在考前把 2006-2015 年的試卷都刷了一兩遍。

    有這樣的準備,數學和英語的分數那必須杠杠,實不相瞞,大一學期的績點是我整個大學的巔峰,然而,三等獎依然沒有拿到!!!因為我只學了這兩門,其他隨意,不掛科即可。

    當然,85 分只是一個門檻,轉專業的競爭還是挺激烈。我選了軟件工程,參与人數大概是差不多 60 人,不過學院只接 15 個人(後來好像是錄取了 20 人),所以為了穩一些,我在第一學期就把 c 語言自學了一遍,把學校的 OJ 題庫,第一學期相關的編程題,給刷了一兩遍。

    說實話,有了這樣的準備,轉專業想不成功都難,考試 2 小時,我不到半個小時就離開考場了,然而我居然是第三個離開考場的,而不是第一個。第一個和第二個離開考場的,單挑嗎?

    有必要加入社團嗎?

    對於剛步入大學的同學來說,社團是一個比較有趣的玩意,絕大部分人都會去加入自己喜歡的社團吧。

    我進入大學之前,很多人說,在大學,要多交點人脈,多認識一些朋友,因為這些人會成為你後面很重要的資源,所以我去加入一些社團,其實是想多認識一些人,然而,我去面試了兩個社團,都在二面被刷了,說實話,對於面試,我還是有點恐懼的,反正就是挺緊張,後來我就乾脆不去面試了,不加入社團了。

    後來我就加入了他們的會員,之前面試那個,是成為他們的幹部。成為會員則不需要面試,不過需要交會員費用,所以我就順便成為了幾個社團的會員,例如羽毛球,愛心社團啥的。

    不過,我去參与了一兩次社團的活動之後,就沒在去了,一個簡單的原因就是,我不大喜歡,我還是喜歡去做自己喜歡的事。我是一個懶散,喜歡自由,不喜歡被束縛的人,所以我覺得,我還是不去參加這些活動了。

    回答剛才的問題,有必要加入社團嗎?,可能很多人會覺得,必須要加入社團,不然大學就不完整了,我的想法則是,加入社團不是一個必選項,加與不加,我覺得都沒事,看你自己的喜歡。加入社團能學到很多東西嗎?能學到一些,但這些沒啥的,對於以後找工作,我覺得屬於可有可無。如果你自己願意學習,學習能力比較強,在哪裡都可以學到很多東西。

    擺脫的社團之後,我基本就什麼組織也沒參加了,然後我宿舍也有一個和我一樣比較逗比+沙雕的,我倆就經常去外面溜達,看到有趣的活動,就去參与一波,反正完全看心情,報名了活動,交了錢,心情不好就不去參加了,美滋滋(感覺要被噴,哈哈)

    有必要多認識些人嗎?

    我剛才說了,我希望自己在大學多認識些人,多泛交些朋友,後來經過第一學期的感悟,我發現,這沒必要,在大學,大家基本都在忙各自的事情,我本來還想在大學找幾個摯友,以後一起干大事,但經過一學期的觀察,發現這很難,當然,很難不代表不存在。

    總之,對於現在還在大一大二的學生,如果你有這方面的疑問,那麼我給的建議是,沒必要刻意去交朋友,其實後面大家都各自去做自己的事情了,畢竟找到一些經的起時間考驗的志同道合的朋友,很不容易。

    第二學期:落差之后的折騰

    大一學期其實可以寫的還有非常非常多,因為第一學期,我的任務就兩門課程,還是非常閑,期間也發生了非常多改變我想法多事情,不過一不小心就兩千多字了,還是不繼續寫了,後面的時光可能就沒有第一學期那麼豐富了,相對比較枯燥了。

    經過了第一學期,成功進入了軟件工程,自己也沒參与什麼社團,並且大學想要干點事,例如創業之類的,因為沒讀大學之前,經常聽說創業這事……總之,進入大學的時候,感覺前途一片光明,我覺得我要干非常非常多的事,但進入大學之後,發現並沒有啥戰績。

    慶幸的事,我完成了一個非常堅定的目標,那便是轉專業。轉過來軟工之後,我也想干點大事,例如 acm 拿個牛逼的獎牌,或者寫個牛逼的軟件出來,因為經常聽到某某人開發了一個 xx,然後就成名了。

    然而,學了 c 語言,發現啥也寫不出來,學了算法,發現 acm 那些題也太難了,一道題做一天,還是沒做出來,答案也看不懂,發現自己並不是大神,腦子也並沒有大佬轉的快。

    後來,我就不打算參加 acm 了,感覺如果自己要拿到名次,肯定會花很多時間,並且不一定拿的到,加上我看到班裡也有人退出 acm 集訓隊了,這更加堅定了我的想法。

    聽說數據結構與算法很重要,所以我早早就把數據結構與算法這門課學完了,我第二學期學的最多的就是數據結構 + 算法這兩門課,雖然不參加 ACM,但算法還是得學,會點算法聽說會顯的牛逼一些。

    暑假的折騰

    大一暑假那會,雖然數據結構與算法學的還不錯,不過發現啥東西的做不出來,C 語言寫的程序都是黑乎乎的界面,然後我就學了 windows 程序編程,這樣我就可以寫個程序給身邊的人玩了,畢竟我是學編程的,至少得寫個作品出來給別人玩啊。

    在暑假花了十幾天,把那本 900 多頁的windows程序編程刷了 700 多頁,寫了個計算器,後來發現身邊的人還是玩不了,因為很多人沒電腦,於是我就對 windows 編程沒興趣了,想着寫個程序能夠在手機運行就好了,於是花了十幾天學了 android 編程,刷完了《第一行代碼》這本書,順便入門了 Java,跟着書寫了個天氣預報,還是挺開心。

    不過我又改變主意了,想着要是能掙到錢就更好了,於是我發現把 app 上傳到商店,然後植入廣告,就可以掙錢了,於是我買了一些實戰類 android 項目的書籍,寫了幾個 app,自己改版之後傳到了應用商店。

    然而,沒啥人下載,於是我又放棄搞安卓了,後來想寫個網站,然後放一些資源,讓別人來下載好像也有機會掙錢,於是我學習了 HTML,CSS,JavaScript,然後又不了了之……

    這兩個月的暑假,我感覺自己搞了好多東西,好多都是半途放棄,實不相瞞,驅動我去折騰這些事有兩個原因:掙錢 + 裝逼。說實話,我做什麼事情,都需要驅動力,我覺得驅動力對我來說太重要了,這個驅動力可以很虛,但必須得有,而我又是一個俗人,能夠掙錢,是我最大的驅動力。當初我玩斗地主,驅動力是掙 Q 幣,後來我發現這些 Q 幣好像不能充 QQ會 員還是怎麼的,我就放棄不玩了。

    這段折騰,我覺得讓我慢慢摸清了自己的方向,所以在這裏,對於大一或者大二的同學,如果你們有自己感興趣的,或許可以去嘗試一波。別人可能會說,這搞一下,那搞一下,會導致樣樣都會,但樣樣不精。而我的想法是:完全可以去嘗試,大學的學習,不存在精通這一說法,大一大二多嘗試,大三確定自己的方向來學習完全來得及。

    大二

    經過大一的洗禮,我覺得我的目標相對比較明確了,該玩的玩了,該折騰的折騰了,現在得好好規劃下自己的未來了。我了解到校招時大廠非常看重基礎,於是我大二就一直在學習基礎,例如計算機網絡,算法,操作系統這些,怎麼學?

    得有動力啊,於是我報名了中級軟考,這算是我一個動力,這門考試會考整個大學涉及到的知識,於是為了搞定這個軟考,我大二把很多課程都學了,後面軟考也順利通過了。

    中級軟考有必要參加嗎?答是隨便,這個證沒啥含金量的,我的目的是讓他督促我學習基礎知識,適合用來複習知識吧。

    總之,大一,我學了很多數據結構和算法相關知識,大二,我學了很多基礎知識 + Java 的知識,並且大二比較專心,啥比賽,啥活動也沒參加,我說了,我喜歡做自己喜歡做的事,喜歡跟着自己的步伐走,別人的建議,我可能會參考下,但我無論做什麼事,都有自己的想法和思考。

    有人也有問,有必要加入實驗室嗎?,我沒加過實驗室,但我想說的是,加與不加,都沒關係,重點是你想學習什麼,想成為什麼樣的人,實驗室,更多的是一種氣氛,但不一定適合你。要是我加入實驗室,我可能會把實驗室當作一個學習的場地,進而去學習自己喜歡的東西。

    也有人問,那些基礎知識很枯燥,有沒有什麼辦法?,答是沒有,有些本來就枯燥,但枯燥的東西,往往是決定你我之間的區別,如果都很有趣,那大家肯定也都學,正是因為困難,所以才有了人與人之間的區別。

    大三

    其實我大三基本就處於複習 + 寫作 + 運營公眾號了,關於寫作和運營公眾號這個事,我不想說太多,因為我覺得我可以再寫兩篇文章來說這些事了。我只能說,運營一個公眾號,很不容易,我希望你,好好積累,好好準備秋招或者考研。我之所以能夠在大三寫出那麼多原創文章,一個很重要的原因就是,我大一大二積累了很多,所以大三就輸入的很少,基本處於輸出和折騰公眾號的過程。

    大三的第二學期就是春招了,也就是找暑假實習,不過在大三,一般都面臨兩個選擇:讀研 還是 找工作?,實不相瞞,我從來沒想過讀研,讀研從來都沒在我的字典里。

    為什麼?

    之前也有挺多小夥伴問我為什麼沒讀研的,不過這個話題,我覺得我可以寫一篇關於我自己為何沒有讀研的原因了,如果你們感興趣,我後面寫一篇吧。

    總的來說就是,大三處於複習的過程,之前我也曬過自己的思維導圖:當初為了有機會進大廠,帥地狠心複習了這9門核心知識,熬夜整理成思維導圖送給大家

    不過說實話,其實我大三花在寫文章 + 弄公眾號的時間,佔比非常非常多,公眾號給我的學習狀態,帶來了很多負面影響,但幸運的是,我的公眾號做的不錯,給我帶來了不少收入,同時也幫忙了不少人,很多人都來感謝過我,這讓我很開心。

    大三,能說的太多,但更多的都是非技術學習,我這裏就不說了,這篇文章字數也挺多了,有機會後面再說。

    大四

    大三暑假,也就是 2019 年 9月份,我就結束了自己的秋招了,很幸運,找到了自己喜歡的公司與城市,2019 年這一年,真的可以說是非常幸運,找到了工作 + 有了自己的公眾號,所以大四,過的很輕鬆,畢竟沒有找工作的壓力,所以大四上學期,都是在 玩 + 為工作準備 + 運營公眾號

    到了大四第二學期,也就是 2020 年的 2 月份底,我就來公司實習了,一直實習到至今,關於實習到感悟,有機會再寫篇文章吧。

    總結

    說實話,我的大學,0 比賽 0 獎學金 0 證書,算是平凡但不平庸,但我始終都有一個明確目標支撐我去學習與探索,總的來說就是,我的大學做對了三件事,一是選擇了編程,二是學習正確的技能,三是入坑了寫作。

    說實話,如果你們願意學習,你們也是可以做到的,至少,你們畢業后的薪資會對的起你們平時的學習。

    我是帥地,一個即將畢業,步入社會大學的學生,希望在未來,我們共同成長,也歡迎大家見證我的成長!

    最後,獻上我備戰校招的思維導圖 + 提升內功的 PDF 吧

    九大思維導圖助你拿到心儀的 offer

    打開計算機網絡的思維導圖長這樣

    由於鏈接容易失效,不方便更新,大家可以在我的微信公眾號帥地玩編程回復思維導圖,即可獲取九大思維導圖,相信一定可以在面試時助你一臂之力。

    作者簡潔

    作者:大家好,我是帥地,從大學、自學一路走來,深知算法計算機基礎知識的重要性,所以申請了一個微星公眾號『帥地玩編程』,專業於寫這些底層知識,提升我們的內功,帥地期待你的關注,和我一起學習。 轉載說明:未獲得授權,禁止轉載

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

  • BMW i3 在美銷量 首度超越 Tesla Model S

    BMW i3 在美銷量 首度超越 Tesla Model S

     

    BMW i3 純電動汽車今年 8 月在美銷量為 1,025 輛,首次超過特斯拉 Model S,後者 8 月銷量僅為 600 輛。

    寶馬 i3 純電動車型在美國市場已上市 3 個月,之前由於寶馬內部的運轉效率問題,其銷量一直低於特斯拉 Model S。但該問題解決後,寶馬 i3 的銷量也迎頭趕上。特斯拉 8 月整體需求較去年同期下降 54%,7 月更是下降了 72%。與 2013 年同期相比,特斯拉今年上半年銷量下降了 26%。

    專家稱,特斯拉目前正處發展壯大的階段,而寶馬在歐洲和亞洲市場則以紮穩腳跟。在產品創新度、更新速度及售價方面,特斯拉 Model S 的優勢愈來愈不明顯,而隨著寶馬 i3 的銷量逐步上升,預計歐洲將會成為其最大銷售市場。

     

    (圖片來源:)

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

    【其他文章推薦】

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

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

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

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

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

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

  • 日產瞄準中國電動車市場 目標拿下 20% 市占

    日產瞄準中國電動車市場 目標拿下 20% 市占

      日本汽車大廠日產汽車(Nissan)10 日宣佈,旗下中國大陸合資公司東風汽車有限公司的乘用車部門「東風日產乘用車公司(以下稱東風日產)」自 10 日起將在大陸開賣東風日產自有品牌電動車「Venucia e30」,售價為 26 萬 7,800 元人民幣,目標為在 2018 年於中國電動車市場拿下 20% 市佔率。   Venucia e30 是以日產於 2010 年在日本開賣的電動車「Leaf」的車台、技術為根基,由日產與東風日產所攜手研發的車款,而日產也將成為第一家進軍中國電動車市場的日系車廠。   Venucia e30 初期將在北京、上海、廣州、深圳、大連、武漢、天津、鄭州和杭洲等 9 個都市販售,並計劃在 2015 年將販售區域擴及至中國全國。   據日本媒體共同通信指出,Venucia e30 約 4 小時可充飽電、充飽電狀態下的行駛距離為 175km;Venucia e30 將在廣州生產、2018 年銷售目標為 5 萬台。     (圖片來源:)

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • Jmeter系列(31)- 獲取並使用 JDBC Request 返回的數據

    Jmeter系列(31)- 獲取並使用 JDBC Request 返回的數據

    如果你想從頭學習Jmeter,可以看看這個系列的文章哦

    https://www.cnblogs.com/poloyy/category/1746599.html

     

    前言

    • Jmeter 使用 JDBC Request 獲取數據庫中數據,很多人都會用,因為測試中,有時候需要大量的用戶進行登錄,然後獲取數據庫中真實的數據用於測試
    • 前面也詳細講到 JDBC Request 的具體使用,一般是通過 Variable names 和 Result variable name 來獲取返回的數據
    • 這篇文章主要講的就是把 Variable names 和 Result variable name 獲取到的數據提取出來,給到 HTTP 請求使用

     

    Variable names + Foreach控制器

    線程組結構樹

     

    JDBC Request

     

    調試取樣器運行結果

    有 100 條記錄

     

    ForEach控制器

     

    循環運行的結果( mobile:${mobile} )

     

    Variable names + 循環控制器

    和上面的栗子只是換了個控制器而已,沒太大變化

    線程組結構樹

     

    循環控制器

    填寫 100,是代表循環100次

     

    計數器

    從 1 開始,遞增加到 100為止,每次遞增 1

    • 初始值=1
    • 每次增加 1
    • 最大的值=100(包含)
    • 新變量 num

     

    循環控制器內的 Debug Sampler

     ${__V()} 是關聯函數,後面講到

     

    循環運行的結果( mobile:${mobile} )

     

    Result variable name + Foreach控制器

    線程組結構樹

     

    JDBC Request

     

    正則提取器

     

    重點

    Applu to 選中 Jmeter Variable Name to use,因為要從 Jmeter Variables 中拿到 result_mobile 變量進行提取

     

    調試取樣器運行結果

    正則提取后的值是不是跟上面 Variable names 獲取的值列表很像,是的!然後再結合 ForEach控制器就好啦

     

    ForEach控制器

    變量前綴是正則提取器里的引用名稱

     

    循環運行的結果( mobile:${mobile} )

     

    Result variable name + 循環控制器

    和上面的栗子只是換了個控制器而已,沒太大變化

    線程組結構樹

     

    循環控制器

    填寫 100,是代表循環100次

     

    計數器

     

    用戶參數

    重點一

    •  ${__BeanShell(vars.getObject(“result_mobile”).get(${num}).get(“mobile”))} 
    •  ${__BeanShell()} :執行BeanShell腳本,一般比較短的腳本可以用此方法來寫,後面會再詳細講解這個函數

    重點二

    •  vars.getObject(“result_mobile”).get(${num}).get(“mobile”) 
    • result_mobile:是一個數組,即 JDBC Request 里的 Result variable name,每個元素的格式都是 {mobile=158000480001} 
    • ${num}:上面計數器的值,每次遞增 1,這裡是數組下標的意思
    • 總結:獲取 result_mobile 數組,每次取數組中第 num 個元素,從元素中取 mobile 鍵的值【這是固定寫法,只改Object 名、鍵名就行了】

     

    循環運行的結果( mobile:${user_mobile} )

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

    【其他文章推薦】

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

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

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

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

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

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

  • 深入理解JVM(③)虛擬機的類加載時機

    深入理解JVM(③)虛擬機的類加載時機

    前言

    Java虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這個過程被稱為虛擬機的類加載機制。

    類加載的時機

    一個類型從被加載到虛擬機內存中開始,到卸載除內存為止,它的生命周期將會經歷加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和 卸載(Unloading)、七個階段,其中驗證、準備、解析三個部分統稱為連接(Linking)。
    類的生命周期如下圖:

    其實加載、驗證、準備、初始化和卸載這五個階段的順序是確定的,類型的加載過程必須按照這種順序按部就班地開始,而解析階段則不一定:它在某些情況下可以在初始化階段之後再開始,這是為了支持Java語音的運行時綁定特性(也稱為動態綁定或晚期綁定)。
    在什麼情況下需要開始類加載過程的第一個階段“加載”,《Java虛擬機規則》中並沒有進行強制約束,但是對於初始化階段《Java虛擬機規範》則是嚴格規定了有且只有以下六種情況必須立即對類進行“初始化”。

    1. 遇到newgetstaticputstaticinvokestatic這四條字節碼指令時,如果類型沒有進行過初始化,則需要先觸發其初始化階段。
      涉及到這四條指令的典型場景有:
    • 使用new關鍵字實例化對的時候。
    • 讀取或設置一個類型的靜態字段(被final修飾、已在編譯期把結果放入常量池的靜態字段除外)的時候。
    • 調用一個類型的靜態方法的時候。
    1. 使用 java.lang.reflect 包的方法對類型進行反射調用的時候,如果類型沒有進行過初始化,則需要先觸發其初始化。
    2. 當初始化類型的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
    3. 當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
    4. 當使用JDK7新加入的動態語言支持時,如果一個java.lang.invoke.MethodHandle實例最後的解析結果為REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四種類型的方法句柄,並且這個方法句柄對應的類沒有進行過初始化,則需要先觸發其初始化。
    5. 當一個接口中定義了JDK8新加入的默認方法(被default關鍵字修飾的接口方法)時,如果這個接口的實現類發生了初始化,那該接口要在其之前被初始化。
      除了以上的這個六種場景外,所有引用類型的方式都不會觸發初始化,稱為被動引用。
      下面來看一下哪些是被動引用:

    例子1:

    父類

    package com.jimoer.classloading;
    
    /**
     * @author jimoer
     * @date Create in 2020/06/24 16:08
     * @description 通過子類引用父類的靜態字段,不會導致子類初始化。
     */
    public class FatherClass {
    
        static {
            System.out.println("FatherClass init!!!!!");
        }
    
        public static int value = 666;
    
    }
    

    子類

    package com.jimoer.classloading;
    
    public class SonClass extends FatherClass{
    
        static {
            System.out.println("SonClass init!!!");
        }
    
    }
    

    測試類

    @Test
    public void testInitClass(){
        System.out.println(SonClass.value);
    }
    

    運行結果:

    FatherClass init!!!!!
    666
    

    通過運行結果我們看到,只輸出了“FatherClass init!!!!!”,並沒有輸出“SubClass init!!!”,這是因為對於使用靜態字段,只有直接定義這個字段的類才會被初始化,因此通過子類來引用父類中定義的靜態字段,並不會初始化子類。

    例子2:

    /**
     * 通過數組定義來引用類,不會觸發此類的初始化
     */
    @Test
    public void testInitClass2(){
        FatherClass[] fathers = new FatherClass[5];
    }
    

    運行結果:未打印任何信息。
    通過運行結果我們發現,並沒有打印出 FatherClass init!!!!! ,這說明並沒有觸發Father類的初始化階段。但是這段代碼裏面觸發了另一個名為“[Lcom.jimoer.classloading.FatherClass”的類的初始化階段,它是一個由虛擬機自動生成的、直接繼承與java.lang.Object的子類,創建動作由字節碼newarray觸發。這個類代表了一個元素類型為FatherClass的一維數組,數組中應用的屬性和方法(length屬性和clone()方法)都實現在這個類里。

    例子3:

    /**
     * @author jimoer
     * 常量在編譯階段會存入調用類的常量池中,
     * 本質上沒有直接引用到定義常量的類,
     * 因此不會觸發定義常量的類的初始化。
     */
    public class ConstantClass {
        
        static {
            System.out.println("ConstantClass init !!!");
        }
        
        public static final String CLASS_LOAD = "class load test !!!";
        
    }
    

    使用

    /**
     * 使用常量
     */
    @Test
    public void testInitClass3(){
        System.out.println(ConstantClass.CLASS_LOAD);
    }
    

    運行結果:

    class load test !!!
    

    通過運行結果,我們看到當在使用一個類的常量時,並不會初始化定義了常量的類。這是因為雖然在Java源碼中確實引用了ConstatClass的類的常量CLASS_LOAD,但其實在編譯階段通過常量傳播優化,已經將此常量的值“class load test !!!”直接存儲在使用常量的類中的常量池中了,所以在使用ConstantClass.CLASS_LOAD時候,實際上都被轉化為在使用類自身的常量池的引用了。

    接口也是有初始化過程的,上面的代碼都是用靜態語句塊“static {}”來輸出初始化信息的,而接口中不能使用static{}語句塊,但編譯器仍然會為接口生成“ ()”類構造器,用於初始化接口中所定義的成員變量。
    還有一點接口與類不同,當一個類在初始化時,要求其父類全部都已經初始化過了,但是在一個接口初始化時,並不要求其父接口全部都完成了初始化,只有在真正使用到父接口的時候(例如引用接口中的常量)才會初始化。

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

    【其他文章推薦】

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

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

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

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

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

  • 厄瓜多總統與原住民領袖達協議 化解反撙節示威

    摘錄自2019年10月14日中央社報導

    莫雷諾為向國際貨幣基金貸款42億美元而取消燃油補貼後,國內物價迅速飆升,引發長達12天的示威,造成7人喪命。暴力衝突迫使莫雷諾將政府遷移至厄瓜多第2大城瓜亞基爾(Guayaquil),並嚴重影響石油業,能源部暫停超過2/3原油的配送。抗議人士甚至佔領亞馬遜雨林地區的3處石油設施。

    厄瓜多總統莫雷諾及原住民領袖瓦爾加斯13日達成協議,終結近2週來反對撙節措施的暴力抗議。政府是為為獲得國際貨幣基金(IMF)數十億美元貸款,而採取這些緊縮措施。

    法新社報導,聯合國官員代表宣讀的聯合聲明說,「根據這項協議,厄瓜多各地的群眾動員劃下句點,我們承諾會恢復國內和平」。聲明並表示,政府已撤回取消燃油補貼的命令。臉上塗著油彩、頭上頂著羽毛頭飾的瓦爾加斯證實:「這些適用於我們全國各地的措施已經取消了。」

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

    【其他文章推薦】

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

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

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

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

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

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