奧迪拚轉型電動車 六年內裁員9500人

摘錄自2019年11月26日聯合新聞網報導

德國車廠奧迪(Audi)今(26日)宣布,計劃在2025年底前裁減9500個職位。這項行動包括在奧迪的大規模企業重組計畫內,目標在2029年底前省下數十億歐元成本。

屬於德國福斯汽車集團(Volkswagen Group)的奧迪,正在進行昂貴且痛苦的企業轉型,由發展內燃機引擎動力車轉向發展電動車。

奧迪表示,將透過優退方案和自然人力更替達成裁員目標,但同時也會在電動車和數位化領域繼續雇用新人。減少人力的行動將讓奧迪「在德國的兩座工廠產能最佳化」。

奧迪還說:「因此產生的60億歐元(約新台幣2018億元)經費,將確保達成公司策略鎖定的9%-11%報酬率區間,並投入電動化和數位化等未來計畫。」

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

【其他文章推薦】

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

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

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

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

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

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

人類首次測到藍鯨心跳! 「最高每分鐘34下」解開世界最大體積動物之謎

摘錄自2019年11月27日ETtoday新聞雲報導

藍鯨是目前世界上體型最大的動物,也是地球史上已知動物中體型最大的,由於體型過大,使得科學家一直很難完整得知牠的生理特徵,近來卻有科學家首次測到藍鯨的心跳,也因此解開了更多關於藍鯨的祕密。

這項研究近來發表在《美國國家科學院院刊》(Proceedings of the National Academy of Sciences of the United States of America)上,一組海洋生物學家透過在藍鯨背部安裝吸盤的方式,成功在加州外海測到了心跳。研究人員長達九個小時持續觀察一條藍鯨發現,牠浮出水面時心跳最高可達每分鐘34下,沉入水面時心跳最低卻可降到每分鐘兩下。

科學家解釋,這是因為藍鯨潛入水中時,身體會重新分配氧氣,其心臟和大腦會需要比較多氧氣,肌肉、皮膚和其他器官所吸收的氧氣量較少,因此每呼吸一次便能更長時間停留在水中。

一條成年藍鯨身長可超過30公尺,讓科學家們一直很好奇,要有多大的力量才能為世界上體型最大的動物提供動力,2015年他們從藍鯨的解剖標本發現,其心臟竟然重達180公斤,看起來和一台高爾夫球車差不多大。

加利福尼亞大學助理教授戈德博根(Jeremy Goldbogen)表示,測得藍鯨的心跳能幫助他們了解,藍鯨為什麼沒有長得比現在更大,因為藍鯨身體所需要的動能要求,已經到達心臟所能承受的最大值,光是張開大口這個動作,就會讓其心臟運作達到極限。

若是地球上有體型比藍鯨更大的動物存在,其心臟的跳動速度會需要更快,但科學家認為從目前數據看起來這是不可能的事,也因此解釋了為何目前地球上沒有發現比藍鯨體積更大的動物存在之謎。

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

【其他文章推薦】

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

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

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

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

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

【String註解驅動開發】困擾了我很久的AOP嵌套調用終於解決了!

寫在前面

最近在分析Spring源碼時,在同一個類中寫了嵌套的AOP方法,測試時出現:Spring AOP在同一個類里自身方法相互調用時無法攔截。哎,怎麼辦?還能怎麼辦呢?繼續分析Spring源碼,解決問題唄。於是乎,有了本文。

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

問題闡述

Spring AOP在同一個類里自身方法相互調用時無法攔截。比如下面的代碼:

public class SomeServiceImpl implements SomeService  {  
    public void someMethod()  {  
        someInnerMethod();  
    }  
    public void someInnerMethod(){  
    }  
} 

兩個方法經過AOP代理,執行時都實現系統日誌記錄。單獨使用someInnerMethod時,沒有任何問題。但someMethod就有問題了。someMethod里調用的someInnerMethod方法是原始的,未經過AOP增強的。我們期望調用一次someMethod會記錄下兩條系統日誌,分別是someInnerMethod和someMethod的,但實際上只能記錄下someMethod的日誌,也就是只有一條。在配置事務時也可能會出現問題,比如someMethod方法是REQUIRED,someInnerMethod方法是REQUIRES_NEW,someInnerMethod的配置將不起作用,與someMethod方法會使用同一個事務,不會按照所配置的打開新事務。

問題分析

由於java這個靜態類型語言限制,最後想到個曲線救國的辦法,出現這種特殊情況時,不要直接調用自身方法,而通過AOP代理后的對象。在實現里保留一個AOP代理對象的引用,調用時通過這個代理即可。例如下面的代碼。

//從beanFactory取得AOP代理后的對象  
SomeService someServiceProxy = (SomeService)beanFactory.getBean("someService");   

//把AOP代理后的對象設置進去  
someServiceProxy.setSelf(someServiceProxy);   

//在someMethod裏面調用self的someInnerMethod,這樣就正確了  
someServiceProxy.someMethod();  

但這個代理對象還要我們手動set進來。有沒有更好的方式解決呢?

問題解決

幸好SpringBeanFactory有BeanPostProcessor擴展,在bean初始化前後會統一傳遞給BeanPostProcess處理,繁瑣的事情就可以交給程序了,代碼如下,首先定義一個BeanSelfAware接口,實現了此接口的程序表明需要注入代理后的對象到自身。

public class SomeServiceImpl implements SomeService,BeanSelfAware{  
	//AOP增強后的代理對象  
    private SomeService self;
    //實現BeanSelfAware接口  
    public void setSelf(Object proxyBean){  
        this.self = (SomeService)proxyBean  
    }  
    public void someMethod(){  
        //注意這句,通過self這個對象,而不是直接調用的  
        someInnerMethod();
    }  
    public void someInnerMethod(){  
    }  
}  

再定義一個BeanPostProcessor,beanFactory中的每個Bean初始化完畢后,調用所有BeanSelfAware的setSelf方法,把自身的代理對象注入自身。

public class InjectBeanSelfProcessor implements BeanPostProcessor  {     
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{ 
        if(bean instanceof BeanSelfAware){  
            System.out.println("inject proxy:" + bean.getClass());  
            BeanSelfAware myBean = (BeanSelfAware)bean;  
            myBean.setSelf(bean);  
            return myBean;  
        }  
        return bean;  
    }  
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{  
        return bean;  
    }  
}

最後,在BeanFactory配置中組合起來,只需要把BeanPostProcesser加進去就可以了,比平常多一行配置而已。

<!-- 注入代理后的bean到bean自身的BeanPostProcessor... -->  
<bean class=" org.mypackage.InjectBeanSelfProcessor"></bean>  

<bean id="someServiceTarget" class="org.mypackage.SomeServiceImpl" />   

<bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean">  
    <property name="target">  
        <ref local="someServiceTarget" />  
    </property>  
    <property name="interceptorNames">  
        <list>  
            <value>someAdvisor</value>  
        </list>  
    </property>  
</bean>  
<!-- 調用spring的DebugInterceptor記錄日誌,以確定方法是否被AOP增強 -->  
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor" />  

<bean id="someAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
    <property name="advice">  
        <ref local="debugInterceptor" />  
    </property>  
    <property name="patterns">  
        <list>  
            <value>.*someMethod</value>  
            <value>.*someInnerMethod</value>  
        </list>  
    </property>  
</bean>  

這裏的someService#someInnerMethod就表現出預期的行為了,無論怎樣,它都是經過AOP代理的,執行時都會輸出日誌信息。

注意事項

用XmlBeanFactory進行測試需要注意,所有的BeanPostProcessor並不會自動生效,需要執行以下代碼:

XmlBeanFactory factory = new XmlBeanFactory(...);  
InjectBeanSelfProcessor postProcessor = new InjectBeanSelfProcessor();  
factory.addBeanPostProcessor(postProcessor);  

好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

參考:iteye.com/blog/fyting-109236

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

【其他文章推薦】

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

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

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

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

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

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

socketserver模塊使用與源碼分析

socketserver模塊使用與源碼分析

前言

  在前面的學習中我們其實已經可以通過socket模塊來建立我們的服務端,並且還介紹了關於TCP協議的粘包問題。但是還有一個非常大的問題就是我們所編寫的Server端是不支持併發性服務的,在我們之前的代碼中只能加入一個通信循環來進行排隊式的單窗口一對一服務。那麼這一篇文章將主要介紹如何使用socketserver模塊來建立具有併發性的Server端。

 

基於TCP協議的socketserver服務端

  我們先看它的一段代碼,對照代碼來看功能。

#!/usr/bin/env python3
# _*_ coding:utf-8 _*_

# ==== 使用socketserver創建支持多併發性的服務器 TCP協議 ====

import socketserver

class MyServer(socketserver.BaseRequestHandler):
    """自定義類"""

    def handle(self):
        """handle處理請求"""
        print("雙向鏈接通道建立完成:", self.request)  # 對於TCP協議來說,self.request相當於雙向鏈接通道conn,即accept()的第一部分
        print("客戶端的信息是:", self.client_address)  # 對於TCP協議來說,相當於accept()的第二部分,即客戶端的ip+port

        while 1:  # 開始內層通信循環
            try:  # # bug修復:針對windows環境
                data = self.request.recv(1024)

                if not data:
                    break  # bug修復:針對類UNIX環境

                print("收到客戶機[{0}]的消息:[{1}]".format(self.client_address, data))
                self.request.sendall(data.upper())  # #sendall是重複調用send.

            except Exception as e:
                break

        self.request.close()  # 當出現異常情況下一定要關閉鏈接


if __name__ == '__main__':
    s1 = socketserver.ThreadingTCPServer(("0.0.0.0", 6666), MyServer) # 公網服務器綁定 0.0.0.0 私網測試為 127.0.0.1
    s1.serve_forever()  # 啟動服務

 

  1.導入socketserver模塊

  2.創建一個新的類,並繼承socketserver.BaseRequestHandler

  3.覆寫handle方法,對於TCP協議來說,self.request 相當於雙向鏈接通道connself.client_address相當於被服務方的ip和port信息,也就是addr,而整個handle方法相當於鏈接循環。

  4.寫入收發邏輯規則

  5.防止客戶端發送空的消息已致雙方卡死

  6.防止客戶端突然斷開已致服務端崩潰

  7.粘包優化(可選)

  8.實例化 socketserver.ThreadingTCPServer類,並傳入IP+port,以及剛寫好的類名

  9.使用socketserver.ThreadingTCPServer實例化對象中的server_forever( )方法啟動服務

 

  它其實是這樣的:

    我們不用管鏈接循環,因為在執行handle方法之前內部已經幫我們做好了。當我們使用serve_forever()方法的時候便開始監聽鏈接描述符對象,一旦有鏈接請求就創建一個子線程來處理該鏈接。

 

 

基於UDP協議的socketserver服務端

  基於UDP協議的socketserver服務端與基於TCP協議的socketserver服務端大相徑庭,但是還是有幾點不太一樣的地方。

 

  對TCP來說:

    self.request = 雙向鏈接通道(conn)

  對UDP來說:

    self.request = (client_data_byte,udp的套接字對象)

 

#!/usr/bin/env python3
# _*_ coding:utf-8 _*_

# ==== 使用socketserver創建支持多併發性的服務器 UDP協議 ====

import socketserver

class MyServer(socketserver.BaseRequestHandler):
    """自定義類"""

    def handle(self):
        """handle處理請求"""

        # 由於UDP是基於消息的協議,故根本不用通信循環

        data = self.request[0]  # 對於UDP協議來說,self.request其實是個元組。第一個元素是消息內容主題(Bytes類型),相當於recvfrom()的第一部分
        server = self.request[1]    # 第二個元素是服務端本身,即自己

        print("客戶端的信息是:", self.client_address)   # 對於UDP協議來說,相當於recvfrom()的第二部分,即客戶端的ip+port

        print("收到客戶機[{0}]的消息:[{1}]".format(self.client_address, data))
        server.sendto(data.upper(),self.client_address)


if __name__ == '__main__':
    s1 = socketserver.ThreadingUDPServer(("0.0.0.0", 6666), MyServer)  # 公網服務器綁定 0.0.0.0 私網測試為 127.0.0.1
    s1.serve_forever()  # 啟動服務

 

擴展:socketserver源碼分析

探索socketserver中的繼承關係

  好了,接下來我們開始剖析socketserver模塊中的源碼部分。在Pycharm下使用CTRL+鼠標左鍵,可以進入源碼進行查看。

  我們在查看源碼前一定要首先要明白兩點:

 

  socketserver類分為兩部分,其一是server類主要是負責處理鏈接方面,另一類是request類主要負責處理通信方面。

 

  好了,請在腦子里記住這個概念。我們來看一些socketserver模塊的實現用了哪些其他的基礎模塊。

 

  注意,接下來的源碼註釋部分我並沒有在源代碼中修改,也請讀者不要修改源代碼的任何內容。

import socket  # 這模塊挺熟悉吧
import selectors  # 這個是一個多線程模塊,主要支持I/O多路復用。
import os # 老朋友了
import sys  # 老朋友
import threading  # 多線程模塊
from io import BufferedIOBase  # 讀寫相關的模塊
from time import monotonic as time  # 老朋友time模塊

socketserver中用到的基礎模塊

 

  好了,讓我們接着往下走。可以看到一個變量__all__,是不是覺得很熟悉?就是我們使用 from xxx import xxx 能導入進的東西全是被__all__控制的,我們看一下它包含了哪些內容。

__all__ = ["BaseServer", "TCPServer", "UDPServer",
           "ThreadingUDPServer", "ThreadingTCPServer",
           "BaseRequestHandler", "StreamRequestHandler",
           "DatagramRequestHandler", "ThreadingMixIn"]
           
# 這個是我們原本的 __all__ 中的值。
if hasattr(os, "fork"):
    __all__.extend(["ForkingUDPServer","ForkingTCPServer", "ForkingMixIn"])
if hasattr(socket, "AF_UNIX"):
    __all__.extend(["UnixStreamServer","UnixDatagramServer",
                    "ThreadingUnixStreamServer",
                    "ThreadingUnixDatagramServer"])
                    
# 上面兩個if判斷是給__all__添加內容的,os.fork()這個方法是創建一個新的進程,並且只在類UNIX平台下才有效,Windows平台下是無效的,所以這裏對於Windows平台來說就from socketserver import xxx 肯定少了三個類,這三個類的作用我們接下來會聊到。而關於socket中的AF_UNIX來說我們其實已經學習過了,是基於文件的socket家族。這在Windows上也是不支持的,只有在類UNIX平台下才有效。所以Windows平台下的導入又少了4個類。
​
​
# poll/select have the advantage of not requiring any extra file descriptor,
# contrarily to epoll/kqueue (also, they require a single syscall).
if hasattr(selectors, 'PollSelector'):
    _ServerSelector = selectors.PollSelector
else:
    _ServerSelector = selectors.SelectSelector
    
# 這兩個if還是做I/O多路復用使用的,Windows平台下的結果是False,而類Unix平台下的該if結果為True,這關乎I/O多路復用的性能選擇。到底是select還是poll或者epoll。

socketserver模塊對於from xxx import * 導入的處理

 

  我們接着向下看源碼,會看到許許多多的類。先關掉它來假設自己是解釋器一行一行往下走會去執行那個部分。首先是一條if判斷

if hasattr(os, "fork"):
    class ForkingMixIn:
        pass # 這裏我自己省略了
 
 # 我們可以看見這條代碼是接下來執行的,它意思還是如果在類Unix環境下,則會去創建該類。如果在Windows平台下則不會創建該類

處理點一

 

  繼續走,其實這種if判斷再創建類的地方還有兩處。我這裏全部列出來:

if hasattr(os, "fork"):
    class ForkingUDPServer(ForkingMixIn, UDPServer): pass
    class ForkingTCPServer(ForkingMixIn, TCPServer): pass
 

if hasattr(socket, 'AF_UNIX'):
​
    class UnixStreamServer(TCPServer):
        address_family = socket.AF_UNIX
​
    class UnixDatagramServer(UDPServer):
        address_family = socket.AF_UNIX
​
    class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): passclass ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass

處理點二 and 三

 

  好了,說完了大體粗略的一個流程,我們該來研究這裏面的類都有什麼作用,這裏可以查看每個類的文檔信息。大致如下:

 

  前面已經說過,socketserver模塊中主要分為兩大類,我們就依照這個來進行劃分。

 

socketserver模塊源碼內部class功能一覽 
處理鏈接相關  
BaseServer 基礎鏈接類
TCPServer TCP協議類
UDPServer UDP協議類
UnixStreamServer 文件形式字節流類
UnixDatagramServer 文件形式數據報類
處理通信相關  
BaseRequestHandler 基礎請求處理類
StreamRequestHandler 字節流請求處理類
DatagramRequestHandler 數據報請求處理類
多線程相關  
ThreadingMixIn 線程方式
ThreadingUDPServer 多線程UDP協議服務類
ThreadingTCPServer 多線程TCP協議服務類
多進程相關  
ForkingMixIn 進程方式
ForkingUDPServer 多進程UDP協議服務類
ForkingTCPServer 多進程TCP協議服務類

 

  他們的繼承關係如下:

ForkingUDPServer(ForkingMixIn, UDPServer)
​
ForkingTCPServer(ForkingMixIn, TCPServer)
​
ThreadingUDPServer(ThreadingMixIn, UDPServer)
​
ThreadingTCPServer(ThreadingMixIn, TCPServer)
​
StreamRequestHandler(BaseRequestHandler)
​
DatagramRequestHandler(BaseRequestHandler)

 

  處理鏈接相關

處理通信相關

多線程相關

 

總繼承關係(處理通信相關的不在其中,並且不包含多進程)

 

  最後補上一個多進程的繼承關係,就不放在總繼承關係中了,容易圖形造成混亂。

 

多進程相關

 

實例化過程分析

  有了繼承關係我們可以來模擬實例化的過程,我們以TCP協議為準:

 

socketserver.ThreadingTCPServer(("0.0.0.0", 6666), MyServer)

 

  我們點進(選中上面代碼的ThradingTCPServer部分,CTRL+鼠標左鍵)源碼部分,查找其 __init__ 方法:

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

 

  看來沒有,那麼就找第一父類有沒有,我們點進去可以看到第一父類ThreadingMixIn也沒有__init__方法,看上面的繼承關係圖可以看出是普通多繼承,那麼就是廣度優先的查找順序。我們來看第二父類TCPServer中有沒有,看來第二父類中是有__init__方法的,我們詳細來看。

class TCPServer(BaseServer):

   """註釋全被我刪了,影響視線"""

    address_family = socket.AF_INET  #  基於網絡的套接字家族

    socket_type = socket.SOCK_STREAM  # TCP(字節流)協議

    request_queue_size = 5  # 消息隊列最大為5,可以理解為backlog,即半鏈接池的大小

    allow_reuse_address = False  # 端口重用默認關閉

    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        BaseServer.__init__(self, server_address, RequestHandlerClass)
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
                                    
       # 可以看見,上面先是調用了父類的__init__方法,然後又實例化出了一個socket對象!所以我們先不着急往下看,先看其父類中的__init__方法。
       
        if bind_and_activate:
            try:
                self.server_bind()
                self.server_activate()
            except:
                self.server_close()
                raise

TCPServer中的__init__()

 

  來看一下,BaseServer類中的__init__方法。

class BaseServer:

    """註釋依舊全被我刪了"""

    timeout = None  # 這個變量可以理解為超時時間,先不着急說他。先看 __init__ 方法

    def __init__(self, server_address, RequestHandlerClass):
        """Constructor.  May be extended, do not override."""
        self.server_address = server_address  # 即我們傳入的 ip+port ("0.0.0.0", 6666)
        self.RequestHandlerClass = RequestHandlerClass  # 即我們傳入的自定義類 MyServer
        self.__is_shut_down = threading.Event()  # 這裏可以看到執行了該方法,這裏先不詳解,因為它是一個事件鎖,所以不用管
        self.__shutdown_request = False 

BaseServer中的__init__()

 

  BaseServer中執行了thrading模塊下的Event()方法。我這裏還是提一嘴這個方法是幹嘛用的,它會去控制線程的啟動順序,這裏實例化出的self.__is_shut_down其實就是一把鎖,沒什麼深究的,接下來的文章中我也會寫到。我們繼續往下看,現在是該回到TCPServer__init__方法中來了。

class TCPServer(BaseServer):

   """註釋全被我刪了,影響視線"""
   
    address_family = socket.AF_INET  #  基於網絡的套接字家族

    socket_type = socket.SOCK_STREAM  # TCP(字節流)協議

    request_queue_size = 5  # 消息隊列最大為5,可以理解為backlog,即半鏈接池的大小
    
    allow_reuse_address = False  # 端口重用默認關閉

    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): # 看這裏!!!!
    """Constructor.  May be extended, do not override."""
        BaseServer.__init__(self, server_address, RequestHandlerClass)
        self.socket = socket.socket(self.address_family,
                                self.socket_type)                        
   
        if bind_and_activate:  # 在創建完socket對象后就會進行該判斷。默認參數bind_and_activate就是為True
            try:
                self.server_bind() # 現在進入該方法查看細節
                self.server_activate()
            except:
                self.server_close()
                raise

TCPServer中的__init__()


  好了,需要找這個self.bind()方法,還是從頭開始找。實例本身沒有,第一父類ThreadingMixIn也沒有,所以現在我們看的是TCPServerserver_bind()方法:

def server_bind(self):
    """Called by constructor to bind the socket.

    May be overridden.

    """
    if self.allow_reuse_address:  # 這裏的變量對應 TCPServer.__init__ 上面定義的類方法,端口重用這個。由於是False,所以我們直接往下執行。
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    self.socket.bind(self.server_address)  # 綁定 ip+port 即 ("0.0.0.0", 6666)
    self.server_address = self.socket.getsockname() # 獲取socket的名字 其實還是 ("0.0.0.0", 6666)

TCPServer中的server_bind()

 

  現在我們該看TCPServer下的server_activate()方法了。

def server_activate(self):
    """Called by constructor to activate the server.

    May be overridden.

    """
    self.socket.listen(self.request_queue_size)  # 其實就是監聽半鏈接池,backlog為5

TCPServer中的server_activate()

 

  這個時候沒有任何異常會拋出的,所以我們已經跑完了整個實例化的流程。並將其賦值給s1

  現在我們看一下s1__dict__字典,再接着進行源碼分析。

{'server_address': ('0.0.0.0', 6666), 'RequestHandlerClass': <class '__main__.MyServer'>, '_BaseServer__is_shut_down': <threading.Event object at 0x000002A96A0208E0>, '_BaseServer__shutdown_request': False, 'socket': <socket.socket fd=716, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 6666)>}

s1的__dict__

 

server_forever()啟動服務分析

  我們接着來看下一條代碼。

s1.serve_forever()

 

  還是老規矩,由於s1ThreadingTCPServer類的實例對象,所以我們去一層層的找serve_forever(),最後在BaseServer類中找到了。

def serve_forever(self, poll_interval=0.5):
    """註釋被我刪了"""
    self.__is_shut_down.clear()  # 上面說過了那個Event鎖,控制子線程的啟動順序。這裏的clear()代表清除,這個不是重點,往下看。
    try:
        # XXX: Consider using another file descriptor or connecting to the
        # socket to wake this up instead of polling. Polling reduces our
        # responsiveness to a shutdown request and wastes cpu at all other
        # times.
        with _ServerSelector() as selector:  
            selector.register(self, selectors.EVENT_READ)# 這裡是設置了一個監聽類型為讀取事件。也就是說當有請求來的時候當前socket對象就會發生反應。

            while not self.__shutdown_request: # 為False,會執行,注意!下面都是死循環了!!!
                ready = selector.select(poll_interval)  # 設置最大監聽時間為0.5s
                # bpo-35017: shutdown() called during select(), exit immediately.
                if self.__shutdown_request: # BaseServer類中的類方法,為False,所以不執行這個。
                    break
                if ready: # 代表有鏈接請求會執行下面的方法
                    self._handle_request_noblock()  # 這兒是比較重要的一個點。我們先來看。

                self.service_actions() 
    finally:
        self.__shutdown_request = False
        self.__is_shut_down.set() # 這裡是一個釋放鎖的行為

BaseServer中的serve_forever()

 

  如果有鏈接請求,則會執行self._handle_request_noblock()方法,它在哪裡呢?剛好這個方法就在BaseServerserve_forever()方法的正下方第4個方法的位置。

def _handle_request_noblock(self):
    """註釋被我刪了"""
    try:
        request, client_address = self.get_request()  # 這裏的這個方法在TCPServer中,它的return值是 self.socket.accept(),就是就是返回了元組然後被解壓賦值了。其實到這一步三次握手監聽已經開啟了。
    except OSError:
        return
    if self.verify_request(request, client_address): # 這個是驗證ip和port,返回的始終是True
        try:
            self.process_request(request, client_address) # request 雙向鏈接通道,client_address客戶端ip+port。現在我們來找這個方法。
        except Exception:
            self.handle_error(request, client_address)
            self.shutdown_request(request)
        except:
            self.shutdown_request(request)
            raise
    else:
        self.shutdown_request(request)

BaseServer中的_handle_request_noblock()

 

  現在開始查找self.process_request(request, client_address)該方法,還是先從實例對象本身找,找不到去第一父類找。他位於第一父類ThreadingMixIn中。

def process_request(self, request, client_address):
    """Start a new thread to process the request."""
    t = threading.Thread(target = self.process_request_thread,
                         args = (request, client_address))  # 創建子線程!!看這裏!
    t.daemon = self.daemon_threads # ThreadingMixIn的類屬性,為False
    if not t.daemon and self.block_on_close:  # 第一個值為False,第二個值為True。他們都是ThreadingMixIn的類屬性
        if self._threads is None:  # 會執行
            self._threads = []  # 創建了空列表
        self._threads.append(t) # 將當前的子線程添加至空列表中
    t.start()  # 開始當前子線程的運行,即運行self.process_request_thread方法

ThreadingMixIn中的process_request()

 

  我們可以看到,這裏的target參數中指定了一個方法self.process_request_thread,其實意思就是說當這個線程tstart的時候會去執行該方法。我們看一下它都做了什麼,這個方法還是在ThreadingMixIn類中。

def process_request_thread(self, request, client_address):
    """Same as in BaseServer but as a thread.

    In addition, exception handling is done here.

    """
    try:
        self.finish_request(request, client_address) # 可以看到又執行該方法了,這裏我再標註一下,別弄頭暈了。request 雙向鏈接通道,client_address客戶端ip+port。
    except Exception:
        self.handle_error(request, client_address)
    finally:
        self.shutdown_request(request)  # 它不會關閉這個線程,而是將其設置為wait()狀態。

ThreadingMixIn中的 process_request_thread()

 

  self.finish_request()方法,它在BaseServer類中

def finish_request(self, request, client_address):
    """Finish one request by instantiating RequestHandlerClass."""
    self.RequestHandlerClass(request, client_address, self)  # 這裡是幹嘛?其實就是在進行實例化!

BaseServer中的finish_request


  self.RequestHandlerClass(request, client_address, self),我們找到self__dict__字典,看看這個到底是什麼東西

{'server_address': ('0.0.0.0', 6666), 'RequestHandlerClass': <class '__main__.MyServer'>, '_BaseServer__is_shut_down': <threading.Event object at 0x000002A96A0208E0>, '_BaseServer__shutdown_request': False, 'socket': <socket.socket fd=716, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 6666)>}

s1的__dict__

 

  可以看到,它就是我們傳入的那個類,即自定義的MyServer類。我們把request,client_address,以及整個是實例self傳給了MyServer的__init__方法。但是我們的MyServer類沒有__init__,怎麼辦呢?去它父類BaseRequestHandler裏面找唄。

class BaseRequestHandler:

    """註釋被我刪了"""

    def __init__(self, request, client_address, server):
        self.request = request  # request 雙向鏈接通道
        self.client_address = client_address  # 客戶端ip+port
        self.server = server # 即 實例對象本身。上面的__dict__就是它的__dict__
        self.setup() # 鈎子函數,我們可以自己寫一個類然後繼承`BaseRequestHandler`並覆寫其setup方法即可。
        try:
            self.handle()  # 看,自動執行handle
        finally:
            self.finish()  # 鈎子函數

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass

BaseRequestHandler中的__init__

 

 

  現在我們知道了,為什麼一定要覆寫handle方法了吧。

 

socketserver內部調用順序流程圖(基於TCP協議)

 

實例化過程圖解

 

server_forever()啟動服務圖解

 

擴展:驗證鏈接合法性

  在很多時候,我們的TCP服務端為了防止網絡泛洪可以設置一個三次握手驗證機制。那麼這個驗證機制的實現其實也是非常簡單的,我們的思路在於進入通信循環之前,客戶端和服務端先走一次鏈接認證,只有通過認證的客戶端才能夠繼續和服務端進行鏈接。

  下面就來看一下具體的實現步驟。

 

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn):
    '''
    認證客戶端鏈接
    :param conn:
    :return:
    '''
    print('開始驗證新鏈接的合法性')
    msg=os.urandom(32)  # 新方法,生成32位隨機Bytes類型的值
    conn.sendall(msg)
    h=hmac.new(secret_key,msg)
    digest=h.digest()
    respone=conn.recv(len(digest))
    return hmac.compare_digest(respone,digest) # 對比結果為True或者為False

def data_handler(conn,bufsize=1024):
    if not conn_auth(conn):
        print('該鏈接不合法,關閉')
        conn.close()
        return
    print('鏈接合法,開始通信')
    while True:
        data=conn.recv(bufsize)
        if not data:break
        conn.sendall(data.upper())

def server_handler(ip_port,bufsize,backlog=5):
    '''
    只處理鏈接
    :param ip_port:
    :return:
    '''
    tcp_socket_server=socket(AF_INET,SOCK_STREAM)
    tcp_socket_server.bind(ip_port)
    tcp_socket_server.listen(backlog)
    while True:
        conn,addr=tcp_socket_server.accept()
        print('新連接[%s:%s]' %(addr[0],addr[1]))
        data_handler(conn,bufsize)

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    server_handler(ip_port,bufsize)

Server端

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn):
    '''
    驗證客戶端到服務器的鏈接
    :param conn:
    :return:
    '''
    msg=conn.recv(32) # 拿到隨機位數
    h=hmac.new(secret_key,msg) # 摻鹽
    digest=h.digest()
    conn.sendall(digest)

def client_handler(ip_port,bufsize=1024):
    tcp_socket_client=socket(AF_INET,SOCK_STREAM)
    tcp_socket_client.connect(ip_port)

    conn_auth(tcp_socket_client)

    while True:
        data=input('>>: ').strip()
        if not data:continue
        if data == 'quit':break

        tcp_socket_client.sendall(data.encode('utf-8'))
        respone=tcp_socket_client.recv(bufsize)
        print(respone.decode('utf-8'))
    tcp_socket_client.close()

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    client_handler(ip_port,bufsize)

Client端

 

  到這裏已經很簡單了,服務器將隨機數給客戶機發過去,客戶機收到后也用自家的鹽與隨機數加料,再使用digest()將它轉化為字節,直接發送了回來然後客戶端通過hmac.compare_digest()方法驗證兩個的值是否相等,如果不等就說明鹽不對。客戶機不合法服務端將會關閉與該客戶機的雙向鏈接通道。

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

【其他文章推薦】

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

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

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

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

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

瑞典環保少女啟發 泰年輕人投身氣候變遷運動

摘錄自2019年12月01日中央通訊社泰國報導

受到16歲瑞典環保少女童貝里(Greta Thunberg)啟發,一群泰國年輕人起身組織氣候變遷抗議活動,希望喚起泰國人重視環保議題,目前已有越來越多年輕人加入他們的行列。這場活動是由一群年紀約20歲的泰國年輕人發起的,沒有政黨色彩,沒有強大的組織,主要發起人是年僅22歲、剛從大學畢業的楠緹查(Nanticha Ocharoenchai)。

日前在曼谷市中心的是樂園(Lumpini Park)有一場特別的抗議活動,上百人高舉著「氣候變遷比功課還糟糕」、「如果不停止的話我們都會死」、「這是我們的未來」、「改變正在發生」等標語,並高喊「我們現在就要行動!」等口號,上百人隨後躺在是樂園的地上,代表這塊土地受到氣候變遷的衝擊,正在逐漸衰亡。

根據世界銀行在9月發布的報告,曼谷每年都在下沉,估計到2030年,曼谷有40%的土地會被水淹沒。

這個活動的目標就是希望泰國政府採取行動,也希望喚起更多泰國人關心氣候變遷的議題,因此參與這場抗議活動的除了大人,還有很多曼谷各區的大學生、中學生甚至小學生。被朋友暱稱為琳(Lynn)的楠緹查和她的團隊今年已經第4次在泰國發起這樣的抗議活動,從一開始僅50多人到這次的上百人,越來越多人注意到氣候變遷議題。

琳曾經發信給泰國自然資源和環境部(Ministry of Natural Resources and Environment),呼籲泰國政府承諾減少煤炭使用量,並且在2025年提高使用再生能源佔全部能源的比例達50%,2040年可以達到100%使用再生能源。

琳關心氣候變遷議題已經3、4年時間,但直到今年,她決定發起實際行動,希望帶來更多改變。她說,有越來越多的年輕人願意加入他們,他們也得到政府部門和大企業的正面回應。

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

【其他文章推薦】

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

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

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

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

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

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

人象共用44%國土 斯里蘭卡7隻大象遭毒殺 衝突待解

環境資訊中心外電;范震華 翻譯;賴慧玲 審校;稿源:Mongabay

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

【其他文章推薦】

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

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

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

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

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

蒲亭與習近平視訊連線 啟動首條俄中天然氣管

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

俄羅斯總統蒲亭今天(2日)與中國國家主席習近平透過視訊連線,在電視轉播的儀式中啟動連接俄中兩國的首條天然氣管「西伯利亞力量管道」(Power of Siberia)。

習近平指出,這項計畫是「我們兩國間互利合作的典範」。根據俄國電視的翻譯,他又說:「中俄關係的發展如今是,未來也仍將是我們兩國外交政策的優先要務。」

這條3000公里長的管道從俄羅斯西伯利亞東部偏遠地區,經由邊境城市海蘭泡(Blagoveshchensk)進入中國的中俄東線天然氣管道,中國境內管道的長度超過5000公里。

俄中兩國在經歷十年的艱苦談判後,於2014年簽署了一項為期30年的4000億美元協議,以建設和營運這條管道。這是俄羅斯天然氣工業公司的最大合約。當管道於2025年完全運作時,每年將提供中國380億立方公尺的天然氣。

此外,俄羅斯也正計畫不久後啟動另外兩條天然氣管道,將繞過烏克蘭增加對歐洲的天然氣供應。

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

【其他文章推薦】

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

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

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

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

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

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

取福島核一燃料棒 預計後年展開需10年

摘錄自2019年12月2日公視報導

日本福島核一廠發生核能災變事故,即將屆滿九年。雖然說日本政府已經決定廢爐,但因為技術問題,時間表一改再改。專家會議在今(2日)做出第五次修正,預估從2021年展開,要花10年完成核燃料棒等殘骸的取出工作。至於正式廢爐,預估是2041到2051年間才能完成。

2011年3月11號受到東北大地震與海嘯影響、而發生的福島核電廠災變事故,六個反應爐當中,1號機到3號機發生爐心熔毀的狀況,但事故後連同當時也在運轉中的4號機,都在事故後立即停止運轉,並於隔年2012年的4月19號永久停機。至於事故當時正在檢修的5號與6號機,則是到2014年1月31號永久停機。

將近九年來,除了三不五時傳出放射性汙水外流,以及東京電力公司面對的各種賠償訴訟,日本政府也多次召開專家會議,研商福島核一廠廢爐的時間與程序。日本媒體的報導指出,廢爐過程中最大的困難,就是如何取出熔毀的核燃料殘骸。

福島核一廠廢爐委員會委員長宮野廣說:「廢爐過程中最需要注意的,就是放射性廢棄物飛散的問題。如何找出不發生輻射廢料飛散的工法,是首要解決的技術課題。」

另外,取出核燃料殘骸的前置作業,將由機器人先進入反應爐採集樣本。2號的專家會議決定,為了慎重起見,在派遣機器人之前,要先製作模擬現場的模型,讓操作人員更熟悉機器人在反應爐內的運作,因此,正式的作業將從2021年開始。這也是廢爐相關時間表第五次的修正。

整個核燃料棒移除的工作,預計在2031年告一段落。後續的廢爐相關作業,將視情況於2041年到2051年完成。也就是說,從福島核能災變到核一廠完成廢爐,花費的時間長達30到40年。

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

【其他文章推薦】

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

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

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

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

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

水資源危機默默蔓延 威脅全球糧食供應

摘錄自2019年11月26日中央通訊社報導

美國智庫世界資源研究所今天(26日)表示,一場「默默蔓延的危機」正威脅全球糧食供應,到了2040年,水資源匱乏可能危及多達40%的灌溉作物。

世界資源研究所(World Resources Institute)指出,氣候變遷造成降雨不規律,也威脅到1/3依靠雨季供給水分的作物。科學家認為,水資源的供應受到多重因素影響,包括氣候變遷和管理不善,但農業是一大因素,占用70%淡水。

世界資源研究所今天推出名為Aqueduct Food的線上工具,在地圖上標識出香蕉、咖啡、大豆和棉花等40多種作物的灌溉水資源風險分布。

這個智庫發現,在灌溉作物中,將近67%的小麥、64%的玉米和19%的稻米到了2040年可能會位於極高缺水壓力的區域。

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

【其他文章推薦】

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

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

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

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

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

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

今年較工業化前高1.1度 2010年代將成最熱10年

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

聯合國世界氣象組織(WMO)今天(3日)發表年度評估報告指出,氣候變遷已超出人類適應能力,今年(2019年)以來全球氣溫較工業革命前均溫高出攝氏1.1度,即將成為史上最熱10年。

世界氣象組織說,2019年可能成為歷來最熱的前3年。燃燒化石燃料、建造基礎設施、種植作物及運送產品等造成的人為排放,將讓今年打破大氣中二氧化碳濃度紀錄,地球勢必會進一步升溫。

海洋負責吸收溫室氣體新增的9成熱量,讓全球海水溫度也達歷史新高,如今海水也比150年前酸25%,威脅數以十億計民眾賴以維生的重要海洋生態系統。在格陵蘭過去12個月融冰3290億公噸助長下,全球平均海平面高度於去年10月達到歷來新高。

隨著過去40年的每10年都比前10年還熱,氣候變遷不只是未來世代要面臨的問題,因為人類無節制的慾望與不計代價的消費,已讓數以百萬計民眾身受其害。報告中說,今年上半年有超過1000萬人在自己國家內流離失所,其中700萬人直接受到諸如風暴、洪水及乾旱等極端氣候影響。世界氣象組織說,因極端氣候新增的無家可歸人口,到今年底可能增至2200萬人。

世界氣象組織秘書長塔拉斯(Petteri Taalas)表示:「2019年再度發生天氣及氣候相關風險重創事件。百年一遇的熱浪及水災變得愈來愈常發生。」

在只比工業革命前均溫高出攝氏1.1度的今年,歐洲、澳洲與日本已相繼出現致命熱浪,還有超級風暴重創非洲東南部,加州和澳洲也有失控野火肆虐。

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

【其他文章推薦】

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

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

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

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

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