LG Chem看中大陸電動車市場 將設第三座海外電池廠

中國大陸對抗空汙,大力推動環保車,LG Chem看好當地前景,15日宣佈將在中國設立合資電池工廠,目前已進入挑選規則合作對象的最後階段。

LG Chem表示,今年下半會公佈合資電池廠的設廠地點和其他細節。發言人Song Choong-sup強調,這將是LG Chem第三座海外電池工廠,該公司在美國密西根州和中國南京設有工廠。IHS預估,2020年中國電動車的電池市場將成長20倍。

中國第一汽車集團和長安汽車之前就已和LG Chem敲定協議,而最近,LG Chem和中國最大車廠上海汽車集團以及觀致汽車簽訂電池供應合約。Song證實,上汽的插電式油電混合車和觀致的次世代電動車將使用LG電池。

據稱,LG高層一致認為全球科技的未來主軸在聯網汽車和無人駕駛車,LG Display計畫明年成為全球車用面板的頂級供應商,內部主管表示,正與Mercedez、Audi等車廠攜手研發高階顯示面板,他們打算用LG Chem電池廠的客戶,將LG打造為汽車解決方案的供應商,並強調若不隨著時勢改變,將無法生存。

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

[源碼解析] 從TimeoutException看Flink的心跳機制

[源碼解析] 從TimeoutException看Flink的心跳機制

目錄

  • [源碼解析] 從TimeoutException看Flink的心跳機制
    • 0x00 摘要
    • 0x01 緣由
    • 0x02 背景概念
      • 2.1 四大模塊
      • 2.2 Akka
      • 2.3 RPC機制
        • 2.3.1 RpcEndpoint:RPC的基類
        • RpcService:RPC服務提供者
        • RpcGateway:RPC調用的網關
      • 2.4 常見心跳機制
    • 0x03 Flink心跳機制
      • 3.1 代碼和機制
      • 3.2 靜態架構
        • 3.2.1 HeartbeatTarget :監控目標抽象
        • 3.2.2 HeartbeatMonitor : 管理heartbeat target的心跳狀態
        • 3.2.3 HeartbeatManager :心跳管理者
        • 3.2.4 HearbeatListener 處理心跳結果
      • 3.3 動態運行機制
        • 3.3.1 HearbeatManagerImpl : Receiver
        • 3.3.2 HeartbeatManagerSenderImpl : Sender
        • 3.3.3 HeartbeatMonitorImpl
        • 3.3.3 HeartbeatServices
    • 0x04 初始化
      • 4.1 心跳服務創建
    • 0x05 Flink中具體應用
      • 5.1 總述
        • 5.1.1 RM, JM, TM之間關係
        • 5.1.2 三者間心跳機制
      • 5.2 初始化過程
        • 5.2.1 TaskExecutor初始化
        • 5.2.2 JobMaster的初始化
        • 5.2.3 ResourceManager初始化
      • 5.3 註冊過程
        • 5.3.1 TM註冊到RM中
          • 5.3.1.1 TM的操作
          • 5.3.1.2 RM的操作
          • 5.3.1.3 返回到TM
        • 5.3.2 TM註冊到 JM
      • 5.4 心跳過程
        • 5.4.1 ResourceManager主動發起
          • 5.4.1.1 Sender遍歷所有監控的Monitor(Target)
          • 5.4.1.2 Target進行具體操作
          • 5.4.1.3 RPC調用
        • 5.4.2 RM通過RPC調用TM
        • 5.4.3 TM 通過RPC回到 RM
      • 5.5 超時處理
        • 5.5.1 TaskManager
        • 5.5.2 ResourceManager
    • 0x06 解決問題
    • 0x07 參考

0x00 摘要

本文從一個調試時候常見的異常 “TimeoutException: Heartbeat of TaskManager timed out”切入,為大家剖析Flink的心跳機制。文中代碼基於Flink 1.10。

0x01 緣由

大家如果經常調試Flink,當進入斷點看到了堆棧和變量內容之後,你容易陷入了沉思。當你發現了問題可能所在,高興的讓程序Resume的時候,你發現程序無法運行,有如下提示:

Caused by: java.util.concurrent.TimeoutException: Heartbeat of TaskManager with id 93aa1740-cd2c-4032-b74a-5f256edb3217 timed out.

這實在是很鬱悶的事情。作為程序猿不能忍啊,既然異常提示中有 Heartbeat 字樣,於是我們就來一起看看Flink的心跳機制,看看有沒有可以修改的途徑。

0x02 背景概念

2.1 四大模塊

Flink有核心四大組件:Dispatcher,JobMaster,ResourceManager,TaskExecutor。

  • Dispatcher(Application Master)用於接收client提交的任務和啟動相應的JobManager。其提供REST接口來接收client的application提交,負責啟動JM和提交application,同時運行Web UI。
  • ResourceManager:主要用於資源的申請和分配。當TM有空閑的slot就會告訴JM,沒有足夠的slot也會啟動新的TM。kill掉長時間空閑的TM。
  • JobMaster :功能主要包括(舊版本中JobManager的功能在新版本中以JobMaster形式出現,可能本文中會混淆這兩個詞,請大家諒解):
    • 將JobGraph轉化為ExecutionGraph(physical dataflow graph,并行化)。
    • 向RM申請資源、schedule tasks、保存作業的元數據。
  • TaskManager:類似Spark的executor,會跑多個線程的task、數據緩存與交換。Flink 架構遵循 Master – Slave 架構設計原則,JobMaster 為 Master 節點,TaskManager 為Slave節點。

這四大組件彼此之間的通信需要依賴RPC實現。

2.2 Akka

Flink底層RPC基於Akka實現。Akka是一個開發併發、容錯和可伸縮應用的框架。它是Actor Model的一個實現,和Erlang的併發模型很像。在Actor模型中,所有的實體被認為是獨立的actors。actors和其他actors通過發送異步消息通信。

Actor模型的強大來自於異步。它也可以顯式等待響應,這使得可以執行同步操作。但是強烈不建議同步消息,因為它們限制了系統的伸縮性。

2.3 RPC機制

RPC作用是:讓異步調用看起來像同步調用。

Flink基於Akka構建了其底層通信系統,引入了RPC調用,各節點通過GateWay方式回調,隱藏通信組件的細節,實現解耦。Flink整個通信框架的組件主要由RpcEndpoint、RpcService、RpcServer、AkkaInvocationHandler、AkkaRpcActor等構成。

RPC相關的主要接口如下:

  • RpcEndpoint
  • RpcService
  • RpcGateway

2.3.1 RpcEndpoint:RPC的基類

RpcEndpoint是Flink RPC終端的基類,所有提供遠程過程調用的分佈式組件必須擴展RpcEndpoint,其功能由RpcService支持。

RpcEndpoint的子類只有四類組件:Dispatcher,JobMaster,ResourceManager,TaskExecutor,即Flink中只有這四個組件有RPC的能力,換句話說只有這四個組件有RPC的這個需求。

每個RpcEndpoint對應了一個路徑(endpointId和actorSystem共同確定),每個路徑對應一個Actor,其實現了RpcGateway接口,

RpcService:RPC服務提供者

RpcServer是RpcEndpoint的成員變量,為RpcService提供RPC服務/連接遠程Server,其只有一個子類實現:AkkaRpcService(可見目前Flink的通信方式依然是Akka)。

RpcServer用於啟動和連接到RpcEndpoint, 連接到rpc服務器將返回一個RpcGateway,可用於調用遠程過程。

Flink四大組件Dispatcher,JobMaster,ResourceManager,TaskExecutor,都是RpcEndpoint的實現,所以構建四大組件時,同步需要初始化RpcServer。如JobManager的構造方式,第一個參數就是需要知道RpcService。

RpcGateway:RPC調用的網關

Flink的RPC協議通過RpcGateway來定義;由前面可知,若想與遠端Actor通信,則必須提供地址(ip和port),如在Flink-on-Yarn模式下,JobMaster會先啟動ActorSystem,此時TaskExecutor的Container還未分配,後面與TaskExecutor通信時,必須讓其提供對應地址。

Dispatcher,JobMaster,ResourceManager,TaskExecutor 這四大組件通過各種方式實現了Gateway。以JobMaster為例,JobMaster實現JobMasterGateway接口。各組件類的成員變量都有需要通信的其他組件的GateWay實現類,這樣可通過各自的Gateway實現RPC調用。

2.4 常見心跳機制

常見的心跳檢測有兩種:

  • socket 套接字SO_KEEPALIVE本身帶有的心跳機制,定期向對方發送心跳包,對方收到心跳包後會自動回復;
  • 應用自身實現心跳機制,同樣也是使用定期發送請求的方式;

Flink實現的是第二種方案。

0x03 Flink心跳機制

3.1 代碼和機制

Flink的心跳機制代碼在:

Flink-master/flink-runtime/src/main/java/org/apache/flink/runtime/heartbeat

四個接口:

HeartbeatListener.java          HeartbeatManager.java      HeartbeatTarget.java  HeartbeatMonitor.java

以及如下幾個類:

HeartbeatManagerImpl.java   HeartbeatManagerSenderImpl.java   HeartbeatMonitorImpl.java
HeartbeatServices.java    NoOpHeartbeatManager.java

Flink集群有多種業務流程,比如Resource Manager, Task Manager, Job Manager。每種業務流程都有自己的心跳機制。Flink的心跳機制只是提供接口和基本功能,具體業務功能由各業務流程自己實現。

我們首先設定 心跳系統中有兩種節點:sender和receiver。心跳機制是sender和receivers彼此相互檢測。但是檢測動作是Sender主動發起,即Sender主動發送請求探測receiver是否存活,因為Sender已經發送過來了探測心跳請求,所以這樣receiver同時也知道Sender是存活的,然後Reciver給Sender回應一個心跳錶示自己也是活着的。

因為Flink的幾個名詞和我們常見概念有所差別,所以流程上需要大家仔細甄別,即:

  • Flink Sender 主動發送Request請求給Receiver,要求Receiver回應一個心跳;
  • Flink Receiver 收到Request之後,通過Receive函數回應一個心跳請求給Sender;

3.2 靜態架構

3.2.1 HeartbeatTarget :監控目標抽象

HeartbeatTarget是對監控目標的抽象。心跳機制在行為上而言有兩種動作:

  • 向某個節點發送請求。
  • 處理某個節點發來的請求。

HeartbeatTarget的函數就是這兩個動作:

  • receiveHeartbeat :向某個節點(Sender)發送心跳回應,其參數heartbeatOrigin 就是 Receiver。
  • requestHeartbeat :向某個節點(Receiver)要求其回應一個心跳,其參數requestOrigin 就是 Sender。requestHeartbeat這個函數是Sender的函數,其中Sender通過RPC直接調用到Receiver。

這兩個函數的參數也很簡單:分別是請求的發送放和接收方,還有Payload載荷。對於一個確定節點而言,接收的和發送的載荷是同一類型的。

public interface HeartbeatTarget<I> {
	/**
	 * Sends a heartbeat response to the target.
	 * @param heartbeatOrigin Resource ID identifying the machine for which a heartbeat shall be reported.
	 */
  // heartbeatOrigin 就是 Receiver
	void receiveHeartbeat(ResourceID heartbeatOrigin, I heartbeatPayload);

	/**
	 * Requests a heartbeat from the target. 
	 * @param requestOrigin Resource ID identifying the machine issuing the heartbeat request.
	 */
  // requestOrigin 就是 Sender
	void requestHeartbeat(ResourceID requestOrigin, I heartbeatPayload);
}

3.2.2 HeartbeatMonitor : 管理heartbeat target的心跳狀態

對HeartbeatTarget的封裝,這樣Manager對Target的操作是通過對Monitor完成,後續會在其繼承類中詳細說明。

public interface HeartbeatMonitor<O> {
  // Gets heartbeat target.
	HeartbeatTarget<O> getHeartbeatTarget();
	// Gets heartbeat target id.
	ResourceID getHeartbeatTargetId();
	// Report heartbeat from the monitored target.
	void reportHeartbeat();
	//Cancel this monitor.
	void cancel();
  //Gets the last heartbeat.
	long getLastHeartbeat();
}

3.2.3 HeartbeatManager :心跳管理者

HeartbeatManager負責管理心跳機制,比如啟動/停止/報告一個HeartbeatTarget。此接口繼承HeartbeatTarget。

除了HeartbeatTarget的函數之外,這接口有4個函數:

  • monitorTarget,把和某資源對應的節點加入到心跳監控列表;
  • unmonitorTarget,從心跳監控列表刪除某資源對應的節點;
  • stop,停止心跳管理服務,釋放資源;
  • getLastHeartbeatFrom,獲取某節點的最後一次心跳數據。
public interface HeartbeatManager<I, O> extends HeartbeatTarget<I> {
	void monitorTarget(ResourceID resourceID, HeartbeatTarget<O> heartbeatTarget);
	void unmonitorTarget(ResourceID resourceID);
	void stop();
	long getLastHeartbeatFrom(ResourceID resourceId);
}

3.2.4 HearbeatListener 處理心跳結果

用戶業務邏輯需要繼承這個接口以處理心跳結果。其可以看做服務的輸出,實現了三個回調函數。

  • notifyHeartbeatTimeout,處理節點心跳超時
  • reportPayload,處理節點發來的Payload載荷
  • retrievePayLoad。獲取對某節點發下一次心跳請求的Payload載荷
public interface HeartbeatListener<I, O> {
	void notifyHeartbeatTimeout(ResourceID resourceID);
	void reportPayload(ResourceID resourceID, I payload);
	O retrievePayload(ResourceID resourceID);
}

3.3 動態運行機制

之前提到Sender和Receiver,下面兩個類就對應上述概念。

  • HeartbeatManagerImpl :Receiver,存在於JobMaster與TaskExecutor中;
  • HeartbeatManagerSenderImpl :Sender,繼承 HeartbeatManagerImpl類,用於周期發送心跳要求,存在於JobMaster、ResourceManager中。

幾個關鍵問題:

  1. 如何判定心跳超時?

    心跳服務啟動后,Flink在Monitor中通過 ScheduledFuture 會啟動一個線程來處理心跳超時事件。在設定的心跳超時時間到達后才執行線程。

    如果在設定的心跳超時時間內接收到組件的心跳消息,會先將該線程取消而後重新開啟,重置心跳超時事件的觸發。

    如果在設定的心跳超時時間內沒有收到組件的心跳,則會通知組件:你超時了。

  2. 何時”調用雙方”發起心跳檢查?

    心跳檢查是雙向的,一方(Sender)會主動發起心跳請求,而另一方(Receiver)則是對心跳做出響應,兩者通過RPC相互調用,重置對方的 Monitor 超時線程。

    以JobMaster和TaskManager為例,JM在啟動時會開啟周期調度,向已經註冊到JM中的TM發起心跳檢查,通過RPC調用TM的requestHeartbeat方法,重置TM中對JM超時線程的調用,表示當前JM狀態正常。在TM的requestHeartbeat方法被調用后,通過RPC調用JM的receiveHeartbeat,重置 JM 中對TM超時線程的調用,表示TM狀態正常。

  3. 如何處理心跳超時?

    心跳服務依賴 HeartbeatListener,當在timeout時間範圍內未接收到心跳響應,則會觸發超時處理線程,該線程通過調用HeartbeatListener.notifyHeartbeatTimeout方法做後續重連操作或者直接斷開。

下面是一個概要(以RM & TM為例):

  • RM : 實現了ResourceManagerGateway (可以直接被RPC調用)

  • TM : 實現了TaskExecutorGateway (可以直接被RPC調用)

  • RM :有一個Sender HM : taskManagerHeartbeatManager,Sender HM 擁有用戶定義的 TaskManagerHeartbeatListener

  • TM :有一個Receiver HM :resourceManagerHeartbeatManager,Receiver HM 擁有用戶定義的ResourceManagerHeartbeatListener。

  • HeartbeatManager 有一個ConcurrentHashMap<ResourceID, HeartbeatMonitor > heartbeatTargets,這個Map是它監控的所有Target。

  • 對於RM的每一個需要監控的TM, 其生成一個HeartbeatTarget,進而被構造成一個HeartbeatMonitor,放置到ResourceManager.taskManagerHeartbeatManager中。

  • 每一個Target對應的Monitor中,有自己的異步任務ScheduledFuture,這個ScheduledFuture不停的被取消/重新生成。如果在某個期間內沒有被取消,則通知用戶定義的listener出現了timeout。

3.3.1 HearbeatManagerImpl : Receiver

HearbeatManagerImpl是receiver的具體實現。它由 心跳 被發起方(就是Receiver,例如TM) 創建,接收 發起方(就是Sender,例如 JM)的心跳發送請求。心跳超時 會觸發 heartbeatListener.notifyHeartbeatTimeout方法。

注意:被發起方監控線程(Monitor)的開啟是在接收到請求心跳(requestHeartbeat被調用后)以後才觸發的,屬於被動觸發。

HearbeatManagerImpl主要維護了

  • 一個心跳監控列表 map : <ResourceID, HeartbeatMonitor<O>> heartbeatTargets;。這是一個KV關聯。

    key代表要發送心跳組件(例如:TM)的ID,value則是為當前組件創建的觸發心跳超時的線程HeartbeatMonitor,兩者一一對應。

    當一個從所聯繫的machine發過來的心跳被收到時候,對應的monitor的狀態會被更新(重啟一個新ScheduledFuture)。當一個monitor發現了一個 heartbeat timed out,它會通知自己的HeartbeatListener。

  • 一個 ScheduledExecutor mainThreadExecutor 負責heartbeat timeout notifications。

  • heartbeatListener :處理心跳結果。

HearbeatManagerImpl 數據結構如下:

@ThreadSafe
public class HeartbeatManagerImpl<I, O> implements HeartbeatManager<I, O> {

	/** Heartbeat timeout interval in milli seconds. */
	private final long heartbeatTimeoutIntervalMs;

	/** Resource ID which is used to mark one own's heartbeat signals. */
	private final ResourceID ownResourceID;

	/** Heartbeat listener with which the heartbeat manager has been associated. */
	private final HeartbeatListener<I, O> heartbeatListener;

	/** Executor service used to run heartbeat timeout notifications. */
	private final ScheduledExecutor mainThreadExecutor;

	/** Map containing the heartbeat monitors associated with the respective resource ID. */
	private final ConcurrentHashMap<ResourceID, HeartbeatMonitor<O>> heartbeatTargets;

	/** Running state of the heartbeat manager. */
	protected volatile boolean stopped;  
}

HearbeatManagerImpl實現的主要函數有:

  • monitorTarget :把一個節點加入到心跳監控列表。
    • 傳入參數有:ResourceId和HearbeatTarget,monitorTarget根據這兩個參數,生成一個HeartbeatMonitor對象,然後把這個對象跟ResrouceId做kv關聯,存入到heartbeatTargets。 一個節點可能參与多個業務流程,因此一個節點參与多個心跳流程,一個節點上運行多個不同類型的HearbeatTarget。所以一個ResourceID可能會跟不同類型的HearbeatTarget對象關聯,分別加入到多個HeartbeatManager,進行不同類型的心跳監控。也因此這個函數入參是兩個參數。
  • requestHeartbeat :Sender通過RPC異步調用到Receiver的這個函數 以要求receiver向requestOrigin節點(就是Sender)發起一次心跳響應,載荷是heartbeatPayLoad。其內部流程如下:
    • 首先會調用reportHeartbeat函數,作用是 通過Monitor 記錄發起請求的這個時間點,然後創建一個ScheduleFuture。如果到期后,requestOrigin沒有作出響應,那麼就將requestOrigin節點對應的HeartbeatMonitor的state設置成TIMEOUT狀態,如果到期內requestOrigin響應了,ScheduleFuture會被取消,HeartbeatMonitor的state仍然是RUNNING。
    • 其次調用reportPayload函數,把requestOrigin節點的最新的heartbeatPayload通知給heartbeatListener。heartbeatListener是外部傳入的,它根據所有節點的心跳記錄做監聽管理。
    • 最後調用receiveHearbeat函數,響應一個心跳給Sender。

3.3.2 HeartbeatManagerSenderImpl : Sender

繼承HearbeatManagerImpl,由心跳管理的一方(例如JM)創建,實現了run函數(即它可以作為一個單獨線程運行),創建后立即開啟周期調度線程,每次遍歷自己管理的heartbeatTarget,觸發heartbeatTarget.requestHeartbeat,要求 Target 返回一個心跳響應。屬於主動觸發心跳請求。

public class HeartbeatManagerSenderImpl<I, O> extends HeartbeatManagerImpl<I, O> implements Runnable {
	public void run() {
		if (!stopped) {
			for (HeartbeatMonitor<O> heartbeatMonitor : getHeartbeatTargets().values()) {
				requestHeartbeat(heartbeatMonitor);
			}
      // 周期調度
			getMainThreadExecutor().schedule(this, heartbeatPeriod, TimeUnit.MILLISECONDS);
		}
	}

  //  主動發起心跳檢查
	private void requestHeartbeat(HeartbeatMonitor<O> heartbeatMonitor) {
		O payload = getHeartbeatListener().retrievePayload(heartbeatMonitor.getHeartbeatTargetId());
		final HeartbeatTarget<O> heartbeatTarget = heartbeatMonitor.getHeartbeatTarget();
    // 調用 Target 的 requestHeartbeat 函數
		heartbeatTarget.requestHeartbeat(getOwnResourceID(), payload);
	}  
}

3.3.3 HeartbeatMonitorImpl

Heartbeat monitor管理心跳目標,它啟動一個ScheduledExecutor。

  • 如果在timeout時間內沒有接收到心跳信號,則判定心跳超時,通知給HeartbeatListener。
  • 如果在timeout時間內接收到心跳信號,則重置當前ScheduledExecutor。
public class HeartbeatMonitorImpl<O> implements HeartbeatMonitor<O>, Runnable {

	/** Resource ID of the monitored heartbeat target. */
	private final ResourceID resourceID;   // 被監控的resource ID

	/** Associated heartbeat target. */
	private final HeartbeatTarget<O> heartbeatTarget; //心跳目標

	private final ScheduledExecutor scheduledExecutor;

	/** Listener which is notified about heartbeat timeouts. */
	private final HeartbeatListener<?, ?> heartbeatListener; // 心跳監聽器

	/** Maximum heartbeat timeout interval. */
	private final long heartbeatTimeoutIntervalMs;

	private volatile ScheduledFuture<?> futureTimeout;
  //  AtomicReference  使用 
	private final AtomicReference<State> state = new AtomicReference<>(State.RUNNING);
   //  最近一次接收到心跳的時間
  private volatile long lastHeartbeat;
  
	// 報告心跳
	public void reportHeartbeat() {
    // 保留最近一次接收心跳時間
		lastHeartbeat = System.currentTimeMillis();
    // 接收心跳后,重置timeout線程
		resetHeartbeatTimeout(heartbeatTimeoutIntervalMs);
	}

  // 心跳超時,觸發lister的notifyHeartbeatTimeout
	public void run() {
		// The heartbeat has timed out if we're in state running
		if (state.compareAndSet(State.RUNNING, State.TIMEOUT)) {
			heartbeatListener.notifyHeartbeatTimeout(resourceID);
		}
	}

  //  重置TIMEOUT
	void resetHeartbeatTimeout(long heartbeatTimeout) {
		if (state.get() == State.RUNNING) {
      //先取消線程,在重新開啟
			cancelTimeout();
      // 啟動超時線程
			futureTimeout = scheduledExecutor.schedule(this, heartbeatTimeout, TimeUnit.MILLISECONDS);

			// Double check for concurrent accesses (e.g. a firing of the scheduled future)
			if (state.get() != State.RUNNING) {
				cancelTimeout();
			}
		}
	}

3.3.3 HeartbeatServices

建立heartbeat receivers and heartbeat senders,主要是對外提供服務。這裏我們可以看到:

  • HeartbeatManagerImpl就是receivers。
  • HeartbeatManagerSenderImpl就是senders。
public class HeartbeatServices {
	// Creates a heartbeat manager which does not actively send heartbeats.
	public <I, O> HeartbeatManager<I, O> createHeartbeatManager(...) {
		return new HeartbeatManagerImpl<>(...);
	}
	// Creates a heartbeat manager which actively sends heartbeats to monitoring targets.
	public <I, O> HeartbeatManager<I, O> createHeartbeatManagerSender(...) {
		return new HeartbeatManagerSenderImpl<>(...);
	}
}

0x04 初始化

4.1 心跳服務創建

心跳管理服務在Cluster入口創建。因為我們是調試,所以在MiniCluster.start調用。

public void start() throws Exception {
				......
				heartbeatServices = HeartbeatServices.fromConfiguration(configuration);
				......
}

HeartbeatServices.fromConfiguration會從Configuration中獲取配置信息:

  • 心跳間隔 heartbeat.interval
  • 心跳超時時間 heartbeat.timeout

這個就是我們解決最開始問題的思路:從配置信息入手,擴大心跳間隔。

public HeartbeatServices(long heartbeatInterval, long heartbeatTimeout) {
		this.heartbeatInterval = heartbeatInterval;
		this.heartbeatTimeout = heartbeatTimeout;
}

public static HeartbeatServices fromConfiguration(Configuration configuration) {
		long heartbeatInterval = configuration.getLong(HeartbeatManagerOptions.HEARTBEAT_INTERVAL);
		long heartbeatTimeout = configuration.getLong(HeartbeatManagerOptions.HEARTBEAT_TIMEOUT);

		return new HeartbeatServices(heartbeatInterval, heartbeatTimeout);
}

0x05 Flink中具體應用

5.1 總述

5.1.1 RM, JM, TM之間關係

系統中有幾個ResourceManager?整個 Flink 集群中只有一個 ResourceManager。

系統中有幾個JobManager?JobManager 負責管理作業的執行。默認情況下,每個 Flink 集群只有一個 JobManager 實例。JobManager 相當於整個集群的 Master 節點,負責整個集群的任務管理和資源管理。

系統中有幾個TaskManager?這個由具體啟動方式決定。比如Flink on Yarn,Session模式能夠指定拉起多少個TaskManager。 Per job模式中TaskManager數量是在提交作業時根據併發度動態計算,即Number of TM = Parallelism/numberOfTaskSlots。比如:有一個作業,Parallelism為10,numberOfTaskSlots為1,則TaskManager為10。

5.1.2 三者間心跳機制

Flink中ResourceManager、JobMaster、TaskExecutor三者之間存在相互檢測的心跳機制:

  • ResourceManager會主動發送請求探測JobMaster、TaskExecutor是否存活。
  • JobMaster也會主動發送請求探測TaskExecutor是否存活,以便進行任務重啟或者失敗處理。

我們之前講過,HeartbeatManagerSenderImpl屬於Sender,HeartbeatManagerImpl屬於Receiver。

  1. HeartbeatManagerImpl所處位置可以理解為client,存在於JobMaster與TaskExecutor中;
  2. HeartbeatManagerSenderImpl類,繼承 HeartbeatManagerImpl類,用於周期發送心跳請求,所處位置可以理解為server, 存在於JobMaster、ResourceManager中。

ResourceManager 級別最高,所以兩個HM都是Sender,監控taskManager和jobManager

public abstract class ResourceManager<WorkerType extends ResourceIDRetrievable>
		extends FencedRpcEndpoint<ResourceManagerId>
		implements ResourceManagerGateway, LeaderContender {
	taskManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender
	jobManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender
}

JobMaster級別中等,一個Sender, 一個Receiver,受到ResourceManager的監控,監控taskManager。

public class JobMaster extends FencedRpcEndpoint<JobMasterId> implements JobMasterGateway, JobMasterService {
	taskManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender
	resourceManagerHeartbeatManager = heartbeatServices.createHeartbeatManager
}

TaskExecutor級別最低,兩個Receiver,分別被JM和RM疾控。

public class TaskExecutor extends RpcEndpoint implements TaskExecutorGateway {
	this.jobManagerHeartbeatManager = return heartbeatServices.createHeartbeatManager
	this.resourceManagerHeartbeatManager = return heartbeatServices.createHeartbeatManager
}

以JobManager和TaskManager為例。JM在啟動時會開啟周期調度,向已經註冊到JM中的TM發起心跳檢查,通過RPC調用TM的requestHeartbeat方法,重置對JM超時線程的調用,表示當前JM狀態正常。在TM的requestHeartbeat方法被調用后,通過RPC調用JM的receiveHeartbeat,重置對TM超時線程的調用,表示TM狀態正常。

5.2 初始化過程

5.2.1 TaskExecutor初始化

TM初始化生成了兩個Receiver HM。

public class TaskExecutor extends RpcEndpoint implements TaskExecutorGateway {
	/** The heartbeat manager for job manager in the task manager. */
	private final HeartbeatManager<AllocatedSlotReport, AccumulatorReport> jobManagerHeartbeatManager;

	/** The heartbeat manager for resource manager in the task manager. */
	private final HeartbeatManager<Void, TaskExecutorHeartbeatPayload> resourceManagerHeartbeatManager;
  
  //初始化函數
	this.jobManagerHeartbeatManager = createJobManagerHeartbeatManager(heartbeatServices, resourceId);
	this.resourceManagerHeartbeatManager = createResourceManagerHeartbeatManager(heartbeatServices, resourceId);
}

生成HeartbeatManager時,就註冊了ResourceManagerHeartbeatListener和JobManagerHeartbeatListener。

此時,兩個HeartbeatManagerImpl中已經創建好對應monitor線程,只有在JM或者RM執行requestHeartbeat后,才會觸發該線程的執行。

5.2.2 JobMaster的初始化

JM生成了一個Sender HM,一個Receiver HM。這裡會註冊 TaskManagerHeartbeatListener 和 ResourceManagerHeartbeatListener

public class JobMaster extends FencedRpcEndpoint<JobMasterId> implements JobMasterGateway, JobMasterService {
  private HeartbeatManager<AccumulatorReport, AllocatedSlotReport> taskManagerHeartbeatManager;
  private HeartbeatManager<Void, Void> resourceManagerHeartbeatManager;

  private void startHeartbeatServices() {
      taskManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender(
        resourceId,
        new TaskManagerHeartbeatListener(),
        getMainThreadExecutor(),
        log);

      resourceManagerHeartbeatManager = heartbeatServices.createHeartbeatManager(
        resourceId,
        new ResourceManagerHeartbeatListener(),
        getMainThreadExecutor(),
        log);
  }
}

5.2.3 ResourceManager初始化

JobMaster在啟動時候,會在startHeartbeatServices函數中生成兩個Sender HeartbeatManager。

taskManagerHeartbeatManager :HeartbeatManagerSenderImpl對象,會反覆啟動一個定時器,定時掃描需要探測的對象並且發送心跳請求。

jobManagerHeartbeatManager :HeartbeatManagerSenderImpl,會反覆啟動一個定時器,定時掃描需要探測的對象並且發送心跳請求。

taskManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender(
			resourceId,
			new TaskManagerHeartbeatListener(),
			getMainThreadExecutor(),
			log);

jobManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender(
			resourceId,
			new JobManagerHeartbeatListener(),
			getMainThreadExecutor(),
			log);  

5.3 註冊過程

我們以TM與RM交互為例。TaskExecutor啟動之後,需要註冊到RM和JM中。

流程圖如下:

 * 1. Run in Task Manager
 *
 *    TaskExecutor.onStart //Life cycle
 *        |
 *        +----> startTaskExecutorServices@TaskExecutor
 *        |     //開始TM服務
 *        |     
 *        +----> resourceManagerLeaderRetriever.start(new ResourceManagerLeaderListener());
 *        |     // 開始連接到RM
 *        |     // start by connecting to the ResourceManager
 *        |    
 *        +----> notifyLeaderAddress@ResourceManagerLeaderListener  
 *        |     // 當RM狀態變化之後,將回調到這裏    
 *        |     // The listener for leader changes of the resource manager.
 *        |        
 *        +----> reconnectToResourceManager@TaskExecutor   
 *        |     // 以下三步調用是漸進的,就是與RM聯繫。
 *        |    
 *        +----> tryConnectToResourceManager@TaskExecutor 
 *        |     
 *        +----> connectToResourceManager()@TaskExecutor
 *        |     // 主要作用是生成了 TaskExecutorToResourceManagerConnection    
 *        |  
 *        +----> start@TaskExecutorToResourceManagerConnection
 *        |     // 開始RPC調用,將會調用到其基類RegisteredRpcConnection的start   
 *        |   
 *        +----> start@RegisteredRpcConnection
 *        |     // RegisteredRpcConnection實現了組件之間註冊聯繫的基本RPC
 *        |      
   
   
 * ~~~~~~~~ 這裡是 Akka RPC
   
 * 2. Run in Resource Manager   
 * 現在程序執行序列到達了RM, 主要是添加一個Target到RM 的 Sender HM;
 *
 *    registerTaskExecutor@ResourceManager
 *        |
 *        +----> taskExecutorGatewayFuture.handleAsync
 *        |     // 異步調用到這裏
 *        |     
 *        +----> registerTaskExecutorInternal@ResourceManager
 *        |     // RM的內部實現,將把TM註冊到RM自己這裏
 *        |      
 *        +----> taskManagerHeartbeatManager.monitorTarget
 *        |     // 生成HeartbeatMonitor,
 *        |      
 *        +---->  heartbeatTargets.put(resourceID,heartbeatMonitor);
 *        |     // 把Monitor放到 HM in TM之中,就是說TM開始監控了RM
 *        |        

 * ~~~~~~~~ 這裡是 Akka RPC
  
 * 3. Run in Task Manager
 * 現在程序回到了TM, 主要是添加一個Target到 TM 的 Receiver HM;
 *
 *    onRegistrationSuccess@TaskExecutorToResourceManagerConnection
 *        |   
 *        |   
 *        +---->  onRegistrationSuccess@ResourceManagerRegistrationListener 
 *        |       // 回調函數
 *        |  
 *        +---->  runAsync(establishResourceManagerConnection) 
 *        |       // 異步執行
 *        |  
 *        +---->  establishResourceManagerConnection@TaskExecutor 
 *        |       // 說明已經和RM建立了聯繫,所以可以開始監控RM了
 *        |  
 *        +---->  resourceManagerHeartbeatManager.monitorTarget 
 *        |     // 生成HeartbeatMonitor,
 *        |      
 *        +---->  heartbeatTargets.put(resourceID,heartbeatMonitor);  
 *        |       // 把 RM 也註冊到 TM了
 *        |       // monitor the resource manager as heartbeat target 

下面是具體文字描述。

5.3.1 TM註冊到RM中

5.3.1.1 TM的操作
  • TaskExecutor啟動之後,調用onStart,開始其生命周期。
  • onStart直接調用startTaskExecutorServices。
  • 啟動服務的第一步就是與ResourceManager取得聯繫,這裏註冊了一個ResourceManagerLeaderListener(),用來監聽RM Leader的變化。
private final LeaderRetrievalService resourceManagerLeaderRetriever;
// resourceManagerLeaderRetriever其實是EmbeddedLeaderService的實現,A simple leader election service, which selects a leader among contenders and notifies listeners.

resourceManagerLeaderRetriever.start(new ResourceManagerLeaderListener());
  • 當得到RM Leader的地址之後,會調用到回調函數notifyLeaderAddress@ResourceManagerLeaderListener,然後調用notifyOfNewResourceManagerLeader。
  • notifyOfNewResourceManagerLeader中獲取到RM地址后,就通過reconnectToResourceManager與RM聯繫。
  • reconnectToResourceManager中間接調用到TaskExecutorToResourceManagerConnection。其作用是建立TaskExecutor 和 ResourceManager之間的聯繫。因為知道 ResourceManagerGateway所以才能進行RPC操作。
  • 然後在 TaskExecutorToResourceManagerConnection中,就通過RPC與RM聯繫。
5.3.1.2 RM的操作
  • RPC調用后,程序就來到了RM中,RM做如下操作:
  • 會註冊一個新的TaskExecutor到自己的taskManagerHeartbeatManager中。
  • registerTaskExecutor@ResourceManager會通過異步調用到registerTaskExecutorInternal。
  • registerTaskExecutorInternal中首先看看是否這個TaskExecutor的ResourceID之前註冊過,如果註冊過就移除再添加一個新的TaskExecutor。
  • 通過 taskManagerHeartbeatManager.monitorTarget 開始進行心跳機制的註冊。
taskManagerHeartbeatManager.monitorTarget(taskExecutorResourceId, new HeartbeatTarget<Void>() {
				public void receiveHeartbeat(ResourceID resourceID, Void payload) {
					// the ResourceManager will always send heartbeat requests to the
					// TaskManager
				}
				public void requestHeartbeat(ResourceID resourceID, Void payload) {
					taskExecutorGateway.heartbeatFromResourceManager(resourceID);
				}
});

當註冊完成后,RM中的Sender HM內部結構如下,能看出來多了一個Target:

taskManagerHeartbeatManager = {HeartbeatManagerSenderImpl@8866} 
 heartbeatPeriod = 10000
 heartbeatTimeoutIntervalMs = 50000
 ownResourceID = {ResourceID@8871} "040709f36ebf38f309fed518a88946af"
 heartbeatListener = {ResourceManager$TaskManagerHeartbeatListener@8872} 
 mainThreadExecutor = {RpcEndpoint$MainThreadExecutor@8873} 
 heartbeatTargets = {ConcurrentHashMap@8875}  size = 1
  {ResourceID@8867} "630c15c9-4861-4b41-9c95-92504f458b71" -> {HeartbeatMonitorImpl@9448} 
   key = {ResourceID@8867} "630c15c9-4861-4b41-9c95-92504f458b71"
   value = {HeartbeatMonitorImpl@9448} 
    resourceID = {ResourceID@8867} "630c15c9-4861-4b41-9c95-92504f458b71"
    heartbeatTarget = {ResourceManager$2@8868} 
    scheduledExecutor = {RpcEndpoint$MainThreadExecutor@8873} 
    heartbeatListener = {ResourceManager$TaskManagerHeartbeatListener@8872} 
    heartbeatTimeoutIntervalMs = 50000
    futureTimeout = {ScheduledFutureAdapter@10140} 
    state = {AtomicReference@9786} "RUNNING"
    lastHeartbeat = 0
5.3.1.3 返回到TM

RM會通過RPC再次回到TaskExecutor,其新執行序列如下:

  • 首先RPC調用到了 onRegistrationSuccess@TaskExecutorToResourceManagerConnection。
  • 然後onRegistrationSuccess@ResourceManagerRegistrationListener中通過異步執行調用到了establishResourceManagerConnection。這說明TM已經和RM建立了聯繫,所以可以開始監控RM了。
  • 然後和RM操作類似,通過resourceManagerHeartbeatManager.monitorTarget 來把RM註冊到自己這裏。
HeartbeatMonitor<O> heartbeatMonitor = heartbeatMonitorFactory.createHeartbeatMonitor 
heartbeatTargets.put(resourceID, heartbeatMonitor);  

當註冊完成后,其Receiver HM結構如下:

resourceManagerHeartbeatManager = {HeartbeatManagerImpl@10163} 
 heartbeatTimeoutIntervalMs = 50000
 ownResourceID = {ResourceID@8882} "96a9b80c-dd97-4b63-9049-afb6662ea3e2"
 heartbeatListener = {TaskExecutor$ResourceManagerHeartbeatListener@10425} 
 mainThreadExecutor = {RpcEndpoint$MainThreadExecutor@10426} 
 heartbeatTargets = {ConcurrentHashMap@10427}  size = 1
  {ResourceID@8886} "122fa66685133b11ea26ee1b1a6cef75" -> {HeartbeatMonitorImpl@10666} 
   key = {ResourceID@8886} "122fa66685133b11ea26ee1b1a6cef75"
   value = {HeartbeatMonitorImpl@10666} 
    resourceID = {ResourceID@8886} "122fa66685133b11ea26ee1b1a6cef75"
    heartbeatTarget = {TaskExecutor$1@10668} 
    scheduledExecutor = {RpcEndpoint$MainThreadExecutor@10426} 
    heartbeatListener = {TaskExecutor$ResourceManagerHeartbeatListener@10425} 
    heartbeatTimeoutIntervalMs = 50000
    futureTimeout = {ScheduledFutureAdapter@10992} 
    state = {AtomicReference@10667} "RUNNING"
    lastHeartbeat = 0

5.3.2 TM註冊到 JM

其調用基本思路與之前相同,就是TM和JM之間互相註冊一個代表對方的monitor:

JobLeaderListenerImpl ----> establishJobManagerConnection

消息到了JM中,做如下操作。

registerTaskManager ----> taskManagerHeartbeatManager.monitorTarget
  // monitor the task manager as heartbeat target

5.4 心跳過程

在任務提交之後,我們就進入了正常的心跳監控流程。我們依然用 TM 和 RM進行演示。

我們先給出一個流程圖。

 * 1. Run in Resouce Manager
 *
 *    HeartbeatManagerSender in RM
 *        |
 *        +----> run@HeartbeatManagerSenderImpl
 *        |     //遍歷所有監控的Monitor(Target),逐一在Target上調用requestHeartbeat
 *        |     
 *        +----> requestHeartbeat@HeartbeatManagerSenderImpl 
 *        |     // 將調用具體監控對象的自定義函數
 *        |     // heartbeatTarget.requestHeartbeat(getOwnResourceID(), payload);
 *        |      
 *        +----> getHeartbeatListener().retrievePayload   
 *        |     // 調用到TaskManagerHeartbeatListener@ResourceManager    
 *        |     // 這裡是return null;,因為RM不會是任何人的Receiver
 *        |        
 *        +----> requestHeartbeat@HeartbeatTarget   
 *        |     // 調用到Target這裏,代碼在ResourceManager這裏,就是生成Target時候賦值的
 *        |    
 *        +----> taskExecutorGateway.heartbeatFromResourceManager
 *        |     // 會通過gateway RPC 調用到TM,這就是主動對TM發起了心跳請求
 *        |      

 * ~~~~~~~~ 這裡是 Akka RPC
   
 * 2. Run in Task Manager   
 * 現在程序執行序列到達了TM, 主要是 1. 重置TM的Monitor線程; 2.返回一些負載信息;
 *
 *    heartbeatFromResourceManager@TaskExecutor
 *        |
 *        +----> resourceManagerHeartbeatManager.requestHeartbeat(resourceID, null);
 *        |     //開始要調用到 Receiver HM in Task Manager
 *        |     
 *        +----> requestHeartbeat@HeartbeatManager in TM 
 *        |     // 在Receiver HM in Task Manager 這裏運行
 *        |      
 *        +----> reportHeartbeat@HeartbeatMonitor   
 *        |     //reportHeartbeat : 記錄發起請求的這個時間點,然後resetHeartbeatTimeout
 *        |      
 *        +----> resetHeartbeatTimeout@HeartbeatMonitor
 *        |     // 如果Monitor狀態依然是RUNNING,則取消之前設置的ScheduledFuture。
 *        |     // 重新創建一個ScheduleFuture。因為如果不取消,則之前那個ScheduleFuture運行時
 *        |     // 會調用HeartbeatMonitorImpl.run函數,run直接compareAndSet后,通知目標函數
 *        |     // 目前已經超時,即調用heartbeatListener.notifyHeartbeatTimeout。
 *        |     // 這裏代表 JM 狀態正常。   
 *        |      
 *        +---->  heartbeatListener.reportPayload
 *        |     // 把Target節點的最新的heartbeatPayload通知給heartbeatListener。 
 *        |     // heartbeatListerner是外部傳入的,它根據所擁有的節點的心跳記錄做監聽管理。
 *        |        
 *        +---->  heartbeatTarget.receiveHeartbeat(getOwnResourceID(), heartbeatListener.retrievePayload(requestOrigin));
 *        |      
 *        |     
 *        +---->  retrievePayload@ResourceManagerHeartbeatListener in TM   
 *        |       // heartbeatTarget.receiveHeartbeat參數調用的
 *        |     
 *        +---->  return new TaskExecutorHeartbeatPayload
 *        |   
 *        |     
 *        +---->  receiveHeartbeat in TM 
 *        |       // 回到 heartbeatTarget.receiveHeartbeat,這就是TM生成Target的時候的自定義函數
 *        |       // 就是響應一個心跳消息回給RM
 *        |     
 *        +----> resourceManagerGateway.heartbeatFromTaskManager  
 *        |     // 會通過gateway RPC 調用到 ResourcManager
 *        |      

 * ~~~~~~~~ 這裡是 Akka RPC
  
 * 3. Run in Resouce Manager
 * 現在程序回到了RM, 主要是 1.重置RM的Monitor線程;2. 上報收到TaskExecutor的負載信息  
 *
 *    heartbeatFromTaskManager in RM
 *        |   
 *        |   
 *        +---->  taskManagerHeartbeatManager.receiveHeartbeat 
 *        |       // 這是個Sender HM
 *        |  
 *        +---->  HeartbeatManagerImpl.receiveHeartbeat  
 *        |  
 *        |     
 *        +---->  HeartbeatManagerImpl.reportHeartbeat(heartbeatOrigin);
 *        | 
 *        |    
 *        +---->  heartbeatMonitor.reportHeartbeat(); 
 *        |      // 這裏就是重置RM 這裏對應的Monitor。在reportHeartbeat重置 JM monitor線程的觸發,即cancelTimeout取消註冊時候的超時定時任務,並且註冊下一個超時檢測futureTimeout;這代表TM正常執行。
 *        |     
 *        +----> heartbeatListener.reportPayload    
 *        |      //把Target節點的最新的heartbeatPayload通知給 TaskManagerHeartbeatListener。heartbeatListerner是外部傳入的,它根據所擁有的節點的心跳記錄做監聽管理。 
 *        |      
 *        +----> slotManager.reportSlotStatus(instanceId, payload.getSlotReport());
 *        |     // TaskManagerHeartbeatListener中調用,上報收到TaskExecutor的負載信息
 *        |        

下面是具體文字描述。

5.4.1 ResourceManager主動發起

5.4.1.1 Sender遍歷所有監控的Monitor(Target)

心跳機制是由Sender主動發起的。這裏就是 ResourceManager 的HeartbeatManagerSenderImpl中定時schedual調用,這裡會遍歷所有監控的Monitor(Target),逐一在Target上調用requestHeartbeat。

// HeartbeatManagerSenderImpl中的代碼

	@Override
	public void run() {
		if (!stopped) {
			for (HeartbeatMonitor<O> heartbeatMonitor : getHeartbeatTargets().values()) {
        // 這裏向被監控對象節點發起一次心跳請求,載荷是heartbeatPayLoad,要求被監控對象回應心跳
				requestHeartbeat(heartbeatMonitor);
			}
			getMainThreadExecutor().schedule(this, heartbeatPeriod, TimeUnit.MILLISECONDS);
		}
	}
}

// 運行時候的變量
this = {HeartbeatManagerSenderImpl@9037} 
 heartbeatPeriod = 10000
 heartbeatTimeoutIntervalMs = 50000
 ownResourceID = {ResourceID@8788} "d349506cae32cadbe99b9f9c49a01c95"
 heartbeatListener = {ResourceManager$TaskManagerHeartbeatListener@8789} 
 mainThreadExecutor = {RpcEndpoint$MainThreadExecutor@8790} 

// 調用棧如下
requestHeartbeat:711, ResourceManager$2 (org.apache.flink.runtime.resourcemanager)
requestHeartbeat:702, ResourceManager$2 (org.apache.flink.runtime.resourcemanager)
requestHeartbeat:92, HeartbeatManagerSenderImpl (org.apache.flink.runtime.heartbeat)
run:81, HeartbeatManagerSenderImpl (org.apache.flink.runtime.heartbeat)
call:511, Executors$RunnableAdapter (java.util.concurrent)
run$$$capture:266, FutureTask (java.util.concurrent)
run:-1, FutureTask (java.util.concurrent)
5.4.1.2 Target進行具體操作

具體監控對象 Target 會調用自定義的requestHeartbeat。

HeartbeatManagerSenderImpl

	private void requestHeartbeat(HeartbeatMonitor<O> heartbeatMonitor) {
		O payload = getHeartbeatListener().retrievePayload(heartbeatMonitor.getHeartbeatTargetId());
		final HeartbeatTarget<O> heartbeatTarget = heartbeatMonitor.getHeartbeatTarget();

    // 這裏就是具體監控對象
		heartbeatTarget.requestHeartbeat(getOwnResourceID(), payload);
	}

heartbeatTarget = {ResourceManager$2@10688} 
 taskExecutorGateway = {$Proxy42@9459} "org.apache.flink.runtime.rpc.akka.AkkaInvocationHandler@6d0c8334"
 this$0 = {StandaloneResourceManager@9458} 

請注意,每一個Target都是由ResourceManager生成的。ResourceManager之前註冊成為Monitor時候就註冊了這個HeartbeatTarget。

這個HeartbeatTarget的定義如下,兩個函數是:

  • receiveHeartbeat :這個是空,因為RM沒有自己的Sender。

  • requestHeartbeat :這個針對TM,就是調用TM的heartbeatFromResourceManager,當然是通過RPC調用。

5.4.1.3 RPC調用

會調用到ResourceManager定義的函數requestHeartbeat,而requestHeartbeat會通過gateway調用到TM,這就是主動對TM發起了心跳請求。

taskManagerHeartbeatManager.monitorTarget(taskExecutorResourceId, new HeartbeatTarget<Void>() {
   @Override
   public void receiveHeartbeat(ResourceID resourceID, Void payload) {
      // the ResourceManager will always send heartbeat requests to the TaskManager
   }

   @Override
   public void requestHeartbeat(ResourceID resourceID, Void payload) {
      //就是調用到這裏
      taskExecutorGateway.heartbeatFromResourceManager(resourceID); 
   }
});

5.4.2 RM通過RPC調用TM

通過taskExecutorGateway。心跳程序執行就通過RPC從RM跳躍到了TM。

taskExecutorGateway.heartbeatFromResourceManager 的意義就是:通過RPC調用回到TaskExecutor。這個是在TaskExecutorGateway就定義好的。

// TaskExecutor RPC gateway interface.
public interface TaskExecutorGateway extends RpcGateway

TaskExecutor實現了TaskExecutorGateway,所以具體在TaskExecutor內部實現了接口函數。

@Override
public void heartbeatFromResourceManager(ResourceID resourceID) {
    //調用到了這裏 ...........
		resourceManagerHeartbeatManager.requestHeartbeat(resourceID, null);
}

TM中,resourceManagerHeartbeatManager 定義如下。

/** The heartbeat manager for resource manager in the task manager. */
private final HeartbeatManager<Void, TaskExecutorHeartbeatPayload> resourceManagerHeartbeatManager;

所以下面就是執行TM中的Receiver HM。在這個過程中有兩個處理步驟:

  1. 調用對應HeartbeatMonitor的reportHeartbeat方法,cancelTimeout取消註冊時候的超時定時任務,並且註冊下一個超時檢測futureTimeout;
  2. 調用monitorTarget的receiveHeartbeat方法,也就是會通過rpc調用JobMaster的heartbeatFromTaskManager方法返回一些負載信息;

具體是調用 requestHeartbeat@HeartbeatManager。在其中會

  • 調用reportHeartbeat@HeartbeatMonitor,記錄發起請求的這個時間點,然後resetHeartbeatTimeout。
  • 在resetHeartbeatTimeout@HeartbeatMonitor之中,如果Monitor狀態依然是RUNNING,則取消之前設置的ScheduledFuture。重新創建一個ScheduleFuture。因為如果不取消,則之前那個ScheduleFuture運行時會調用HeartbeatMonitorImpl.run函數,run直接compareAndSet后,通知目標函數目前已經超時,即調用heartbeatListener.notifyHeartbeatTimeout。
  • 調用 heartbeatListener.reportPayload,把Target節點的最新的heartbeatPayload通知給heartbeatListener。
  • 調用 heartbeatTarget.receiveHeartbeat(getOwnResourceID(), heartbeatListener.retrievePayload(requestOrigin)); 就是響應一個心跳消息回給RM。
	@Override
	public void requestHeartbeat(final ResourceID requestOrigin, I heartbeatPayload) {
		if (!stopped) {
			log.debug("Received heartbeat request from {}.", requestOrigin);

			final HeartbeatTarget<O> heartbeatTarget = reportHeartbeat(requestOrigin);

			if (heartbeatTarget != null) {
				if (heartbeatPayload != null) {
					heartbeatListener.reportPayload(requestOrigin, heartbeatPayload);
				}

				heartbeatTarget.receiveHeartbeat(getOwnResourceID(), heartbeatListener.retrievePayload(requestOrigin));
			}
		}
	}

最後會通過resourceManagerGateway.heartbeatFromTaskManager 調用到 ResourcManager。

5.4.3 TM 通過RPC回到 RM

JobMaster在接收到rpc請求后調用其heartbeatFromTaskManager方法,會調用taskManagerHeartbeatManager的receiveHeartbeat方法,在這個過程中同樣有兩個處理步驟:

  1. 調用對應HeartbeatMonitor的reportHeartbeat方法,cancelTimeout取消註冊時候的超時定時任務,並且註冊下一個超時檢測futureTimeout;
  2. 調用TaskManagerHeartbeatListener的reportPayload方法,上報收到TaskExecutor的負載信息

至此一次完成心跳過程已經完成,會根據heartbeatInterval執行下一次心跳。

5.5 超時處理

5.5.1 TaskManager

首先,在HeartbeatMonitorImpl中,如果超時,會調用Listener。

public void run() {
   // The heartbeat has timed out if we're in state running
   if (state.compareAndSet(State.RUNNING, State.TIMEOUT)) {
      heartbeatListener.notifyHeartbeatTimeout(resourceID);
   }
}

這就來到了ResourceManagerHeartbeatListener,會嘗試再次連接RM。

private class ResourceManagerHeartbeatListener implements HeartbeatListener<Void, TaskExecutorHeartbeatPayload> {

   @Override
   public void notifyHeartbeatTimeout(final ResourceID resourceId) {
      validateRunsInMainThread();
      // first check whether the timeout is still valid
      if (establishedResourceManagerConnection != null && establishedResourceManagerConnection.getResourceManagerResourceId().equals(resourceId)) {
         reconnectToResourceManager(new TaskManagerException(
            String.format("The heartbeat of ResourceManager with id %s timed out.", resourceId)));
      } else {
         .....
      }
   }

5.5.2 ResourceManager

RM就直接簡單粗暴,關閉連接。

private class TaskManagerHeartbeatListener implements HeartbeatListener<TaskExecutorHeartbeatPayload, Void> {

   @Override
   public void notifyHeartbeatTimeout(final ResourceID resourceID) {
      validateRunsInMainThread();
      closeTaskManagerConnection(
         resourceID,
         new TimeoutException("The heartbeat of TaskManager with id " + resourceID + "  timed out."));
   }
}  

0x06 解決問題

心跳機制我們講解完了,但是我們最初提到的異常應該如何解決呢?在程序最開始生成環境變量時候,通過設置環境變量的配置即可搞定:

Configuration conf = new Configuration();
conf.setString("heartbeat.timeout", "18000000");
final LocalEnvironment env = ExecutionEnvironment.createLocalEnvironment(conf);

0x07 參考

[flink-001]flink的心跳機制

Flink中心跳機制

flink1.8 心跳服務

你有必要了解一下Flink底層RPC使用的框架和原理

flink RPC(akka)

弄清Flink1.8的遠程過程調用(RPC)

Apache Flink源碼解析 (七)Flink RPC的底層實現

flink源碼閱讀第一篇—入口

flink-on-yarn 基礎架構和啟動流程

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

【其他文章推薦】

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

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

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

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

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

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

最詳細教學–win10 + frp + rdpwrap + 阿里雲服務器 –實現win10 多用戶同時遠程登錄內網機

概述:

  使用win10 專業版 + frp + RDPwrap + 阿里雲服務器 的組合實現win10 多用戶同時遠程登錄內網機。使用frp 做內網穿透,將內網機的指定端口暴露在外網,通過ip+port 來實現遠程登錄。再使用rdpwrap 來破解win10 不能同時多用戶登錄的問題。

 

 

設想一下場景

  我是一個建築工程師。經常出差,需要經常畫3D圖和展示建築圖紙,所以買了一台性能非常強的筆記本工作站。筆記本重量大概3.9kg,充電器0.5kg,一個本子,一個書包,全部加起來接近10斤的重量。每天背着10斤重的東西跑來跑去出差,想想都累!!!

  筆記本工作站不僅重,價格也很貴,非常不方便用於出差,那簡直是折磨。。。。。。

   被折磨幾個月後,他開始向他的一個朋友訴苦,這真的是太t*d痛苦了,我能不能背着一台輕薄本筆記本出差啊,可是性能又要很好才行,怎麼辦???

然後就有了這篇文章。

 

以↑純屬扯淡……

—————————————————我是完美分隔符—————————————————

一、先實現單用戶遠程登錄內網機

 1.為什麼要實現內網穿透

    繼續對話:

    你怎麼在互聯網裡面找到你家裡面的電腦,是不是要把你的電腦與互聯網對接上。

    是啊,對接上啦,我的電腦不是連着網線嗎……

    (我語文水平有問題……。)兩個條件:一是你的電腦與互聯網對接上,二是讓互聯網知道你家在哪裡,不,你的電腦在哪裡。

    明白,好的。那什麼是內網穿透啊?

    額,(心想:md還要給你解釋內網外網……還要幫你弄,還免費的,我還有一堆事要忙啊,si建築的)。

    enenen……我幫你弄好就行了,你看百度吧,給個鏈接你:https://baike.baidu.com/item/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F

    (這不是本文的重點)

 2.要準備什麼呢

電腦若干台……
雲服務器(比如阿里雲服務器)
frp 反向代理工具(免費簡單高效)

 資源下載路徑>>>

   window端frp下載:https://github.com/fatedier/frp/releases/download/v0.30.0/frp_0.30.0_windows_amd64.zip

   linux端frp 下載:https://github.com/fatedier/frp/releases/download/v0.30.0/frp_0.30.0_linux_amd64.tar.gz

   資源解壓后的模樣>>>

      windows 解壓后                                                               linux  解壓后

                               

  

先上草圖過過目: 

 

 3.配置過程

      單用戶遠程登錄內網機需要配置的東東:

  1. windows下的frp客戶端:frpc
  2. linux的frp服務端:frps;
  3. 阿里雲服務器端口開放;
  4. windows的遠程登錄配置;
  5. 啟動window中的frpc 客戶端的命令:客戶端連接服務;
  6. 開啟另外一台windows電腦,準備遠程連接測試

 

   1) win 配置frpc.ini 文件(是 frpc ,是客戶端,別配錯了)

[common]
server_addr = 11.11.11.11
server_port = 7000

# trace, debug, info, warn, error
log_level = trace

#遠程桌面
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 3389
remote_port = 6000

 

  2)Linux 配置frps.ini 文件 (服務端)

[common]
bind_port = 7000
vhost_http_port=8080

  啟動linux中的frps 服務的命令:啟動服務

當前窗口啟動,關閉窗口失效: ./frps -c frps.ini
後台啟動,關閉窗口依然有效:nohup ./frps -c frps.ini &

  啟動成功的效果分別是>>>

   

   

          第二張圖片里的 [1] 2374 是什麼???   

           輸入命令:kill 2374   就知道了……

 

  3)打開阿里雲服務器端口

    進入:安全組配置

 

   進入:安全組列表>配置規則

 

    開放端口:6000和7000

 

   阿里雲實例:重啟

   

  4)配置windows 遠程登錄用戶

     右鍵“我的電腦”進入這個界面    選擇“遠程設置”

     

 

      允許遠程連接

      

   

      點“添加”進入, >>>  “高級”  >>> “立即查找”  >>> 選擇用戶,用於遠程登錄

     

 

    PS: 對於win10 家庭版的用戶,遠程設置的界面是這樣子的

    

     對於win家庭版的用戶,將破解多用戶遠程登錄提前先做: 跳轉

 

  5)啟動window中的frpc 客戶端的命令:客戶端連接服務

打開cmd窗口:
先cd 到:frpc.exe 執行程序的目錄
再執行: frpc.exe -c frpc.ini

  啟動成功的效果圖>>> 簡單的描述一下

       

 

   6)開啟另外一台windows電腦,準備遠程連接測試

     快速打開遠程連接窗口:win + R   >>>  輸入:mstsc      再確定

     

 

     最激動人心的時刻到了

      

      遠程登錄輸入用戶密碼

      

 

     有這個界面說明成功了       點進去

      

 

 

     到目前為止單用戶遠程登錄已完成!!!

      

比如一台高性能的電腦只能同時給一個人用,那太浪費了;

又比如另一個人要用你賬號登錄時還要問你:親,你在在用XXX電腦嗎;

再比如多個人同時用一個賬號遠程登錄時:哪個親在用,不用的人不出聲回復你,正在用的人可能沒聽到,你就尷尬吧/(ㄒoㄒ)/~~

最後比如有個人正在使用,你一聲不吭登錄了,咔嚓,m蛋那個gou兒子登錄不說…………………………

繼續學習,解決問題>>> 

二、多用戶同時遠程登錄內網機

       多用戶同時遠程登錄內網機需要做的那些事:

  1. 單用戶遠程登錄成功;
  2. 新建一個windows登錄用戶;
  3. 配置windows遠程登錄用戶;
  4. 編輯本地組策略:配置關於遠程登錄的東東;
  5. 解決windows多用戶同時登錄的問題;
  6. 測試遠程登錄。

   

1)單用戶遠程登錄成功

      前提:在你 單用戶遠程登錄成功后再做多用戶同時遠程登錄。

 

2)新建一個windows登錄用戶

    直接搜索:“用戶”,進入“創建標準用戶賬戶”

       

 

 

 

 輸入新用戶信息:ccccc  隨便輸入你喜歡的

 

 

   ps:當你點擊創建時,輸入框數據會被清空,其實已經創建好了,只是win10 沒有自動幫你關窗口,也許是方便創建多個用戶吧,個人覺得體驗感很差。

   雙擊打開:用戶  >>> 可以看到有啦

  

 

  

3)配置windows遠程登錄用戶

    將剛才新建的用戶添加到遠程登錄(上面已經講過了)

   

    添加成功:

   

 

4)編輯本地組策略

     win + R  >>>  輸入 ”gpedit.msc“ 

     

       打開本地組策略

      

 

      進入到遠程登錄配置

     

 

       配置連接數和同時遠程登錄的信息  

     

        PS: windows 雖然允許設置多用戶同時遠程登錄,但不允許你這麼遠程連接……

        配置完這個后的效果:先遠程登錄一個用戶,再遠程登錄另外一個用戶時會提示等待前一個用戶退出。

         可自行驗證,在這裏就不演示了。

        

 

 5)解決windows多用戶同時登錄的問題

     先下載:RDPWrap-v1.6   https://github.com/stascorp/rdpwrap/releases/tag/v1.6.2

   

 

      解壓后:

     

    1. 先再cmd 下 執行“install.bat” 安裝RDPWrap ;安裝成功后,在“C:\Program Files\RDP Wrapper” 目錄下有

     

 

    2. 再嘗試執行下update.bat 。更新不了配置信息,需要手動來配置

      在配置rdpwrap.ini 之前先看下電腦版本:win + r   ,接着輸入:ver (細心的小夥伴會看到,打開cmd其實就已經看到了版本:10.0.18362.53 )

     

 

     接着打開 C:\Program Files\RDP Wrapper\rdpwrap.ini ,在文本末尾添加面的配置信息(不同版本的配置不一樣)     配置之間有空行,最後的空行也不要漏了

[10.0.18362.53]
LocalOnlyPatch.x86=1
LocalOnlyOffset.x86=B7D06
LocalOnlyCode.x86=jmpshort
LocalOnlyPatch.x64=1
LocalOnlyOffset.x64=82FB5
LocalOnlyCode.x64=jmpshort
SingleUserPatch.x86=1
SingleUserOffset.x86=50535
SingleUserCode.x86=nop
SingleUserPatch.x64=1
SingleUserOffset.x64=DBFC
SingleUserCode.x64=Zero
DefPolicyPatch.x86=1
DefPolicyOffset.x86=50269
DefPolicyCode.x86=CDefPolicy_Query_eax_ecx
DefPolicyPatch.x64=1
DefPolicyOffset.x64=1FE15
DefPolicyCode.x64=CDefPolicy_Query_eax_rcx
SLInitHook.x86=1
SLInitOffset.x86=5A77A
SLInitFunc.x86=New_CSLQuery_Initialize
SLInitHook.x64=1
SLInitOffset.x64=22DDC
SLInitFunc.x64=New_CSLQuery_Initialize

[10.0.18362.53-SLInit]
bInitialized.x86      =D577C
bServerSku.x86        =D5780
lMaxUserSessions.x86  =D5784
bAppServerAllowed.x86 =D578C
bRemoteConnAllowed.x86=D5790
bMultimonAllowed.x86  =D5794
ulMaxDebugSessions.x86=D5798
bFUSEnabled.x86       =D579C

bInitialized.x64      =F6A8C
bServerSku.x64        =F6A90
lMaxUserSessions.x64  =F6A94
bAppServerAllowed.x64 =F6A9C
bRemoteConnAllowed.x64=F6AA0
bMultimonAllowed.x64  =F6AA4
ulMaxDebugSessions.x64=F6AA8
bFUSEnabled.x64       =F6AAC

 

    3. 管理員啟動RDPWrap.exe

    

   ps:不配置第2步,或者版本不對的效果

   

 

  4. 執行:RDPCheck.exe 檢測是否破解成功

   

  對於已經全部綠色了,但提示“訪問拒絕”的,應該是用了當前的用戶(相同用戶)登錄了。

 

    PS:啟動RDPConf.exe 前要 重啟 “遠程桌面服務”:Remote Desktop Services 如圖

    

 

6)測試遠程登錄

    快速打開遠程登錄窗口:win+r  >>> 輸入:mstsc   

    多用戶遠程同時登錄內網機測試效果:  

    

  最後附上不同版本的rdpwrap.ini的配置信息(先看裏面有沒有合適的版本,沒有就沒必要下載了):https://gitee.com/RDNGL/rdpwrap

 

後面純屬扯淡,可以不看。

三、拓展學習

  到目前為止,多用戶同時遠程登錄內網機已完成。

       擴展其應用:內網穿透后,外網可以訪問內網,內網的服務可以被互聯網訪問,也即是可以在內網發布web服務,ftp服務等等–內網穿透的應用。

    外網可以遠程連接內網機器,多個人可以同時使用一台電腦。

    拿着一台可以聯網的筆記本便可以擁有巨大的計算資源。

       延伸為雲計算:

    趨勢:現在很多東西都開始“雲”化,這也是未來的發展方向。未來的趨勢:不需要再買固定配置的電腦,升級不僅麻煩,而且還不方便攜帶。未來只需要購買連接器設備,再開個雲計算機服務,隨時隨地升級配置,便可擁有巨大的計算資源。

         “雲”趨勢帶來的影響:硬件配置將面臨企業集中式採購,而零售數量下降。好像扯遠了……

 

 

    到目前為止全部講完了,歡迎來評論區打唾沫戰。

 

學習會讓人視野開闊,站在頂端才能仰望未來。

轉載請指明出處:https://www.cnblogs.com/dennyLee2025/p/13168408.html

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

現代化編程模式(1)-快

感謝大家購買我的書

首先十分感謝大家購買我的書,在Tommy和Julisa購買我的書的時候,我承諾會開Sharing給大家。我開Sharing的目標就是讓像Tommy和Julisa這樣的基礎比較低的人都能夠學會編程。所以我就以Tommy和Julisa為目標制定sharing課程,不再按照書籍章節順序一章一章的講。

現代化編程模式核心 – 快

我這本書最值得學習的地方不是.NET,所以這節sharing我故意不用.NET來演示,而是採用JavaScript來演示。

我這本書最值得學習的地方也不是併發。

我這本書最值得學習的地方是“現代化的編程模式”。也就是如下標黃處

 

 

 

現代化編程模式核心只有一個字,就是 – 快!

為了显示出快,我故意挑選了最慢的JavaScript來做這次sharing的演示語言!

現代化編程模式適用於所有現代化的編程語言,包括.NET/(C#和F#),Java,Python,JavaScript,Scala,Clojure,Swift。所以無論是我書中的C#和F#,還是我這次sharing用的JavaScript,都是通用的。最典型的就是本書第6章所講的Rx,它就有多個編程語言的版本:Java,JavaScript,.NET,Scala,Clojure,Swift。以下截圖取自Rx的官網 http://reactivex.io/

 

 

 

快主要來自兩個方面:

  • 程序運行速度快。
  • 編程速度快。

程序運行速度快

從大家的反饋來看,大家不太關注這點,所以我這次sharing先不講,以後再講。

編程速度快

所有技術的進步趨勢都是越來越容易,門檻越來越低,編程也同樣如此!

現在我回首十九年前(2001年)我用C語言寫出第一個程序的時候,當時的編程門檻是相當的高。當時的Turbo C 2.0還是跑在Dos上的,與今天相比真是天壤之別。編程門檻越來越低,編程工具環境越來越完善。這些基礎設施的完善決定了我們編程速度會越來越快。所以大家不需要再對編程帶有恐懼感,覺得編程難!現在編程的難度已經是你讀書時代的幾十分之一了!如果你掌握的是現代化的編程模式而不再是傳統古老的編程模式的話。

在這次sharing我只講大家最容易理解的幾點:

  • 調試診斷快
  • 不用動腦跟着感覺走導致行動快
  • 不用等待他人協作導致自身行動快

調試診斷快

在傳統的編程模式中,我們是這樣編程的:

  1. 啪啪啪敲一堆鍵盤之後,程序寫完了。
  2. 跑起來試一下,oh! No! 出錯了!(耗時幾分到十幾分鐘)
  3. 這時候就加斷點調試,先進一點的就不加斷點,而是看log來診斷。(至少耗時一分鐘起)

大家可以看到,第二步和第三步耗時都是以分鐘為單位的。如果unlucky,第三步可能要按小時甚至天為單位。為了加深大家的印象,sharing的時候我會視時間多少而現場演示一遍這個痛苦的傳統編程模式。

但是現代化的編程模式是以秒為單位的,比以分為單位的傳統編程模式快了一個數量級!!!

Sharing的時候我以JavaScript為編程語言,Visual Studio Code為編程工具,框架選karma + jasmine + AngularJS(我故意沒選最近的Angular而是選AngularJS,也是為了證明現代化編程模式是通用的)來演示現代化編程模式是如何以秒為單位進行調試診斷的。

現代化編程模式以秒為單位進行調試診斷的要點在於:

  1. 每Save一次就會把所有BDD Test cases在一兩秒內跑完!
  2. 基於上一點,也就是說,與傳統的編程模式相比,我馬上就能知道結果,而不是要等幾分鐘。
  3. 基於上一點,就可以每改一點代碼,就馬上Save,同時就馬上知道結果。如果此時發現BDD Test Case變紅了,馬上就知道剛才改的哪一處代碼有錯誤。馬上就能知道原因,馬上就能夠Fix!這裏的每改一點代碼可以理解成:每改一行代碼,每改一個字符,等等。
  4. 因為每次修改代碼的粒度是如此之小,小到每一行和每一個字符,可謂:一步一個腳印。並且每次改動都能保證程序是能BDD Test Case跑通過的,自然也就不需要加斷點調試和看log來診斷了。
  5. 然而人總是會犯錯的,程序出錯時,現代化編程模式不需要通過加斷點調試和看log診斷這種這麼古老的方式,而是通過不斷的加BDD Test Case和縮小Scope來定位問題發生的地方以及去解決。

這部分實操內容也是這次Sharing的大頭。

不用動腦跟着感覺走導致行動快

傳統的編程模式需要程序員去動腦,去思考程序代碼的流程,所以需要寫if/else/switch/for/foreach等語句。然而動腦是很容易累的,同時又是很耗時間的,所以傳統的編程模式自然是快不起來的!

“動腦是不可能動腦的啦,做生意又不會,只能跟着感覺走寫寫代碼才能生活這樣子”

竊-格瓦拉

現代化編程模式改變了思路,不再去思考程序代碼的流程:

 

傳統的編程模式

現代化編程模式

程序員所要做的

需要動腦思考如何做,需要明確的指出每一步該怎麼做。

只需要告訴計算機你的目標即可!

相關編程語句和庫

if/else/switch/for/foreach

LINQ,Reactive Extensions, Promises (不應該再出現if/else/switch/for/foreach)

專業術語

命令式編程

聲明式編程

 

 

 

補充多一句:聲明式編程語言通常用作解決人工智能和約束滿足問題。哈哈,看到這裏大家都懂的啦。

從上面的表格對比中可以看出,“只需要告訴計算機你的目標”的現代化編程模式明顯就比“還需要明確的指出每一步該怎麼做。”的傳統編程模式節省腦子很多!!!

大家可以看看 https://baike.baidu.com/item/%E5%A3%B0%E6%98%8E%E5%BC%8F%E7%BC%96%E7%A8%8B/9939512 來預習一下

 

 

 

不用等待他人協作導致自身行動快

自從進入移動互聯網時代之後,因為客戶端設備的多樣化,前後端分離已經成了大趨勢。同時也帶來了一個大問題:前後端程序員協作的問題!

接着前面“調試診斷快”一節中所share的內容,傳統的編程模式對前後端程序員聯調的依賴性比較高。而現代化編程模式因為可以通過mock的方式來模擬絕大部分後端的響應,前端程序員不再需要等待後端程序員的工作了,從而節約了這部分時間,導致自身在項目進度中更快更有保障。

在這次sharing中我所使用的Karma(BDD框架)和Jasmine(包含了httpbackend mock庫的unit testing框架)在其他編程語言中都有對應的框架和庫,再次體現了現代化編程模式是在各種編程語言中是通用的!!!

總結和動手口訣

在這次sharing之前,我請了兩位老夥計参觀了一下,都得到了好評,希望參加的有經驗的程序員也同樣會一聲驚嘆!

當然,非程序員還是不可能只通過這一次sharing就馬上學會編程的,所以對於你們來說,這次sharing的目的應該是:你能夠識別和分辨出哪些才是編程里正確的道路,從而找到先進的編程資料和避免誤讀落後的編程資料。關於這點,我總結了口訣如下:

  1. 快是王道!
    凡是要一分鐘以上才能看到編程運行結果的都是落後的編程模式,現代化模式是一兩秒就能看到運行結果的。凡是要通過debug和看log這麼耗時的行為,我們都要思考是否通過BDD來減少時間。
  2. 節省腦力是王道!
    現代化編程模式已經可以實現了只需要告訴計算機你的目標即可!如果還需要費腦子去想if/else/switch/for/foreach。那你就落後了。
  3. 能夠獨立完成任務,不依賴別人是王道!
    Mock+BDD已經可以讓你不需要過多等待和依賴其他程序員的工作了。

大家下周sharing見!

風險提示:

我的blog文章和我所翻譯和所寫的書籍不一樣:

    • 沒有像書籍一樣經過三審三校。所以不像書籍一樣嚴謹和全面。
    • 都有當時特定的閱讀對象,然而每篇blog的閱讀對象都不一樣,這點和書籍十分不一樣。所以如果你讀起來覺得怪怪的,那很有可能是你與該篇blog閱讀對象差異很大。
    • 所有文章全部不構成任何投資建議。如因採納這些文章而進行投資所造成的虧損,我不負任何責任。

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

【其他文章推薦】

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

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

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

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

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

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

【String註解驅動開發】你了解@PostConstruct註解和@PreDestroy註解嗎?

寫在前面

在之前的文章中,我們介紹了如何使用@Bean註解指定初始化和銷毀的方法,小夥伴們可以參見《【Spring註解驅動開發】如何使用@Bean註解指定初始化和銷毀的方法?看這一篇就夠了!!》,也介紹了使用InitializingBean和DisposableBean來處理bean的初始化和銷毀,小夥伴們可以參見《【Spring註解驅動開發】Spring中的InitializingBean和DisposableBean,你真的了解嗎?》。除此之外,在JDK中也提供了兩個註解能夠在bean加載到Spring容器之後執行和在bean銷毀之前執行,今天,我們就一起來看看這兩個註解的用法。

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

@PostConstruct註解

@PostConstruct註解好多人以為是Spring提供的。其實是Java自己的註解。我們來看下@PostConstruct註解的源碼,如下所示。

package javax.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}

從源碼可以看出,@PostConstruct註解是Java中的註解,並不是Spring提供的註解。

@PostConstruct註解被用來修飾一個非靜態的void()方法。被@PostConstruct修飾的方法會在服務器加載Servlet的時候運行,並且只會被服務器執行一次。PostConstruct在構造函數之後執行,init()方法之前執行。

通常我們會是在Spring框架中使用到@PostConstruct註解,該註解的方法在整個Bean初始化中的執行順序:

Constructor(構造方法) -> @Autowired(依賴注入) -> @PostConstruct(註釋的方法)。

@PreDestroy註解

@PreDestroy註解同樣是Java提供的,看下源碼,如下所示。

package javax.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}

被@PreDestroy修飾的方法會在服務器卸載Servlet的時候運行,並且只會被服務器調用一次,類似於Servlet的destroy()方法。被@PreDestroy修飾的方法會在destroy()方法之後運行,在Servlet被徹底卸載之前。執行順序如下所示。

調用destroy()方法->@PreDestroy->destroy()方法->bean銷毀。

總結:@PostConstruct,@PreDestroy是Java規範JSR-250引入的註解,定義了對象的創建和銷毀工作,同一期規範中還有註解@Resource,Spring也支持了這些註解。

案例程序

對@PostConstruct註解和@PreDestroy註解有了簡單的了解之後,接下來,我們就寫一個簡單的程序來加深對這兩個註解的理解。

我們創建一個Cat類,如下所示。

package io.mykit.spring.plugins.register.bean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試@PostConstruct註解和@PreDestroy註解
 */
public class Cat {

    public Cat(){
        System.out.println("Cat類的構造方法...");
    }

    public void init(){
        System.out.println("Cat的init()方法...");
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("Cat的postConstruct()方法...");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("Cat的preDestroy()方法...");
    }

    public void destroy(){
        System.out.println("Cat的destroy()方法...");
    }
}

可以看到,在Cat類中,我們提供了構造方法,init()方法、destroy()方法,使用 @PostConstruct註解標註的postConstruct()方法和只用@PreDestroy註解標註的preDestroy()方法。接下來,我們在AnimalConfig類中使用@Bean註解將Cat類註冊到Spring容器中,如下所示。

@Bean(initMethod = "init", destroyMethod = "destroy")
public Cat cat(){
    return new Cat();
}

接下來,在BeanLifeCircleTest類中新建testBeanLifeCircle04()方法進行測試,如下所示。

@Test
public void testBeanLifeCircle04(){
    //創建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
    //關閉IOC容器
    context.close();
}

運行BeanLifeCircleTest類中的testBeanLifeCircle04()方法,輸出的結果信息如下所示。

Cat類的構造方法...
Cat的postConstruct()方法...
Cat的init()方法...
Cat的preDestroy()方法...
Cat的destroy()方法...

從輸出的結果信息中,可以看出執行的順序是: 構造方法 -> @PostConstruct -> init()方法 -> @PreDestroy -> destroy()方法。

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

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

寫在最後

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

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

【其他文章推薦】

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

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

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

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

※超省錢租車方案

Redis SDS 深入一點,看到更多!

1、什麼是SDS?

Redis 自定的字符串存儲結構,關於redis,你需要了解的幾點!中我們對此有過簡要說明。

Redis 底層是用C語言編寫的,可是在字符存儲上,並未使用C原生的String類型,而是定義了自己的字符串結構 Simple Dynamic Stirng,簡稱SDS。

SDS基本結構如下:

struct sdshdr {
int len; // 記錄buf數組中已使用字節的數量,等於SDS所保存字符串的長度
int free; // 記錄buf數組中未使用字節的數量
char buf[];// 字節數組,用於保存字符串
};

例如,字符串“Redis”存儲示意圖為:

圖1

當前存儲字符串長度為5,未使用長度為0,字節數組存儲的字符為“Redis\0”。

這裏需要注意的是:內部數據數組存儲字符串形式符合C語言要求,以‘\0’結尾。且len字符串長度不包含結尾標識符‘\0’。

buf[]的這種遵循C語言形式的存儲,使得Redis可以直接使用C語言的相關字符串函數進行SDS對象的操作。

二、SDS的優勢

1、O(1)時間複雜度獲取字符串長度

SDS內部維護着一個字符串長度的len變量,可以直接讀取,時間複雜度為O(1)。

對於傳統的C字符串:字符+“\0”,想要獲取字符長度,則需要遍歷整個字符串,直到遇到結束字符,時間複雜度為O(n)。

2、緩衝區溢出規避

所謂緩衝區溢出即所需要的內存超出了實際的內存。因此對於C字符串來說,要特別注意內存分配,回收使用問題。

比如,向一個現有字符串內添加特定字符時,需要保證當前已經分配了這足夠的內存。 

圖2

與C不同的是,SDS的空間預分配策略可以避免緩衝區溢出發生,

當需要對SDS進行操作時,首先會檢查當前空間是否滿足需求,不足則擴展當前分配空間。內存檢查相對於C變成了內部預置操作。

3、減少內存重分配次數

上面我們講到過,C字符操作前都需要進行內存的分配操作,同時,操作完成后,也需要進行相應的內存回收操作。一次操作至少涉及一次內存分配操作。

大家都知道內存的重分配是一個比較複雜且需精細控制的過程,耗時耗資源。針對此弊端,Redis 在SDS內存配置策略上採用了空間預分配+惰性刪除相結合的策略。

a)空間預分配:

空間預分配用於優化SDS字符擴展操作。

所謂預分配,也即是說在一次擴展操作中,擴展的空間大小會大於實際需要的空間大小。
如下,圖1執行圖2操作后SDS變更為:

圖3 

預分配空間的大小基於以下規則計算:

SDS len<1M:分配len長度空間作為預分配空間;

SDS len>=1M:分配1M空間作為預分配空間;

這樣,在下次進行字符操作的時候,如果所需要的空間小於當前SDS free空間,則可以直接行操作,而不需要再執行內存擴展,重分配操作。

SDS的預分配機制使得一次擴展操作所需的內存重分配次數變為<=1。

b)惰性刪除機制

所謂惰性刪除,即調整刪除SDS中部分數據時,不會立刻執行內存重分配,而是會保留空出來內存,並更新內部free屬性。以備將來有字符擴展需求,可以直接使用。

當然,Redis也提供了主動釋放未使用內存的方法。

如下,刪除“ent”之後的SDS結構:

 圖4

SDS的內存分配機制,尤其對於以寫為主的應用場景,能夠提供更加優異的性能表現。

3、二進制安全

C字符串由於特殊的編碼要求只能保存文本數據。

SDS相關的功能方法會以二進制的形式來操作SDS存儲的數據,沒有任何中間操作,存儲最原始的數據,因此不會有字符層面的因素影響。

SDS可以保存任何源的二進制數據,字符、圖片、文件或者序列化的對象等等。

 

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

正則匹配中的非貪婪匹配不是最短匹配

最近在工作中遇到一個需求,就是找出html中所有錨文字包含 聯繫方式 的超鏈接。剛開始我寫了一個很簡單的正則來解決這個問題<a.*?聯繫方式.*?</a。但是在測試的時候卻發現這個正則表達式並不像我想象的那樣工作。

圖中給出了一個正則表達式匹配的例子,可以看出在這段文字中有兩個匹配,但是第一個匹配所包含的結果已經超出了實際需要的範圍,包含了太多的超鏈接標籤,而我需要的是最短的匹配也就是圖中橫線畫出的範圍,這是怎麼回事?

正則匹配的原理

這要從正則匹配的原理說起,簡單的來說正則匹配是一種貪心的算法。它總是先找到第一個匹配的位置,然後向後繼續匹配其他的表達式符號。對於本文給出的正則表達式,會現在html中找到一個<a標籤,然後之後是.*?,直到找到一個聯繫方式,在這個過程中如果找到了另一個<a,會被當作.*?匹配的部分,而忘記了要匹配表達式的開頭就是<a。也就是說,除非發生失配,正則表達式不會主動地回溯。儘管使用了?來表達非貪婪匹配,也只能限制向後匹配時盡可能地短,而不能縮短已匹配部分的長度。也就是說,非貪婪匹配向後是最短匹配,但是向前不是最短匹配。

對於這個任務,我後來使用了其他效率更高的方法實現了,但是有沒有可能使用正則表達式來完成這個任務呢?

零寬斷言

零寬斷言是一種零寬度的匹配,它匹配到的內容不會保存到匹配結果中去,最終匹配結果只是一個位置而已。
作用是給指定位置添加一個限定條件,用來規定此位置之前或者之後的字符必須滿足限定條件才能使正則中的字表達式匹配成功。

零寬斷言總共有四種

對於這個需求,實際上應該找到離聯繫方式最近的一個<a,也就是說,在<a到聯繫方式之前不能再有其他的<a了。而最開始的正則匹配表達式<a.*?聯繫方式.*?</a中的.*?可以通過.來匹配任意一個字符,在這裏可以使用零寬度負先行斷言來限制.匹配的任何一個字符的右側不能夠再有<a。也就是.(?!<a),再將這個整體重複多次(.(?!<a))*?。這裏引入了一個額外的括號,為了不產生多餘的匹配,可以使用非捕獲組來去除不需要的匹配,最終可以將整個表達式寫成<a(:?.(?!<a))*?聯繫方式.*?</a。

可以看到匹配的範圍已經縮小到最後一個出現的超鏈接。

總結

因為正則表達式實現原理的限制,儘管選擇非貪婪匹配,匹配到的結果也不一定是最短的匹配。
通常正則表達式總是表明了“要匹配什麼”,而通過零寬度負斷言,則可以表明“不匹配什麼”,這比字符集中使用^來取反更加強大。

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

用Python進行實時計算——PyFlink快速入門

Flink 1.9.0及更高版本支持Python,也就是PyFlink。

在最新版本的Flink 1.10中,PyFlink支持Python用戶定義的函數,使您能夠在Table API和SQL中註冊和使用這些函數。但是,聽完所有這些后,您可能仍然想知道PyFlink的架構到底是什麼?作為PyFlink的快速指南,本文將回答這些問題。

為什麼需要PyFlink?

Python上的Flink和Flink上的Python

那麼,PyFlink到底是什麼?顧名思義,PyFlink就是Apache Flink與Python的組合,或者說是Python上的Flink。但是Flink on Python是什麼意思?首先,兩者的結合意味着您可以在Python中使用Flink的所有功能。而且,更重要的是,PyFlink還允許您在Flink上使用Python廣泛的生態系統的計算功能,從而可以進一步促進其生態系統的開發。換句話說,這對雙方都是雙贏。如果您更深入地研究這個主題,您會發現Flink框架和Python語言的集成絕不是巧合。

Python和大數據生態系統

python語言與大數據緊密相連。為了理解這一點,我們可以看一下人們正在使用Python解決的一些實際問題。一項用戶調查显示,大多數人都在使用Python進行數據分析和機器學習應用程序。對於此類情況,大數據空間中還解決了一些理想的解決方案。除了擴大大數據產品的受眾範圍之外,Python和大數據的集成還通過將其獨立體繫結構擴展到分佈式體繫結構,極大地增強了Python生態系統的功能。這也解釋了在分析大量數據時對Python的強烈需求。

為什麼選擇Flink和Python?

Python和大數據的集成與其他最近的趨勢一致。但是,再次說明一下,為什麼Flink現在支持Python,而不是Go或R或另一種語言?而且,為什麼大多數用戶選擇PyFlink而不是PySpark和PyHive?

為了理解原因,讓我們首先考慮使用Flink框架的一些優勢:

  • 有利的體繫結構: Flink是具有統一流和批處理功能的純流計算引擎。
  • 新的活力:根據ASF的客觀統計,Flink是2019年最活躍的開源項目。
  • 高可靠性:作為一個開源項目,Flink經過長期測試,並廣泛應用於大數據公司的生產環境中。

接下來,讓我們看看為什麼Flink支持Python而不是其他語言。統計數據显示,Python是繼Java和C之後最受歡迎的語言,並且自2018年以來一直在快速發展。Java和Scala是Flink的默認語言,但是Flink支持Python似乎是合理的。

PyFlink是相關技術發展的必然產物。但是,僅僅了解PyFlink的重要性是不夠的,因為我們的最終目標是使Flink和Python用戶受益並解決實際問題。因此,我們需要進一步探索如何實現PyFlink。

PyFlink架構

要實現PyFlink,我們需要知道要實現的關鍵目標和要解決的核心問題。PyFlink的主要目標是什麼?簡而言之,PyFlink的主要目標如下:

  1. 使所有Flink功能對Python用戶可用。
  2. 在Flink上運行Python的分析和計算功能,以提高Python解決大數據問題的能力。

在此基礎上,讓我們分析實現這些目標需要解決的關鍵問題。

使Flink功能可供Python用戶使用

要實現PyFlink,是否需要像現有Java引擎一樣在Flink上開發Python引擎?答案是NO。嘗試在Flink 1.8版或更早版本中進行,但效果不佳。基本設計原則是以最小的成本實現給定的目標。最簡單但最好的方法是提供一層Python API,並重用現有的計算引擎。

那麼,我們應該為Flink提供哪些Python API?他們對我們很熟悉:高級表API和SQL,以及有狀態的DataStream API。現在,我們越來越接近Flink的內部邏輯,下一步是提供適用於Python的Table API和DataStream API。但是,剩下要解決的關鍵問題到底是什麼呢?

關鍵問題

顯然,關鍵問題在於在Python虛擬機(PyVM)和Java虛擬機(JVM)之間建立握手,這對於Flink支持多種語言至關重要。要解決此問題,我們必須選擇適當的通信技術。

選擇虛擬機通信技術

當前,有兩種解決方案可用於實現PyVM和JVM之間的通信,它們是Beam和Py4J。前者是一個著名的項目,具有多語言和多引擎支持,而後者是用於PyVM和JVM之間通信的專用解決方案。我們可以從幾個不同的角度比較和對比Apache Beam和Py4J,以了解它們之間的區別。首先,考慮一個比喻:要越過一堵牆,Py4J會像痣一樣在其中挖一個洞,而Apache Beam會像大熊一樣把整堵牆推倒。從這個角度來看,使用Apache Beam來實現VM通信有點複雜。簡而言之,這是因為Apache Beam專註於通用性,在極端情況下缺乏靈活性。

除此之外,Flink還需要交互式編程。此外,為了使Flink正常工作,我們還需要確保其API設計中的語義一致性,尤其是在其多語言支持方面。Apache Beam的現有體繫結構無法滿足這些要求,因此答案很明顯,Py4J是支持PyVM和JVM之間通信的最佳選擇。

技術架構

在PyVM和JVM之間建立通信之後,我們已經實現了向Python用戶提供Flink功能的第一個目標。我們已經在Flink 1.9版中實現了這一點。現在,讓我們看一下Flink 1.9版中PyFlink API的體繫結構:

Flink 1.9版使用Py4J來實現虛擬機通信。我們為PyVM啟用了網關,為JVM啟用了網關服務器以接收Python請求。此外,我們還提供了Python API中的TableENV和Table之類的對象,這些對象與Java API中提供的對象相同。因此,編寫Python API的本質是關於如何調用Java API。Flink 1.9版還解決了作業部署問題。它使您可以通過各種方式提交作業,例如運行Python命令以及使用Python Shell和CLI。

但是,此體繫結構提供了哪些優勢?首先,該體繫結構很簡單,並且可以確保Python API和Java API之間的語義一致性。其次,它還提供了與Java作業相當的出色Python作業處理性能。

在Flink上運行Python的分析和計算功能

上一節介紹了如何使Flink功能可供Python用戶使用。本節說明如何在Flink上運行Python函數。通常,我們可以通過以下兩種方式之一在Flink上運行Python函數:

  1. 選擇一個典型的Python類庫,並將其API添加到PyFlink。該方法花費很長時間,因為Python包含太多的類庫。在合併任何API之前,我們需要簡化Python執行。
  2. 基於現有的Flink Table API和Python類庫的特徵,我們可以將所有現有的Python類庫函數視為用戶定義的函數,並將其集成到Flink中。Flink 1.10及更高版本中支持此功能。功能集成的關鍵問題是什麼?同樣,它取決於Python用戶定義函數的執行。

接下來,讓我們為這個關鍵問題選擇一種技術。

選擇執行用戶定義功能的技術

實際上,執行Python用戶定義的函數非常複雜。它不僅涉及虛擬機之間的通信,還涉及以下所有方面:管理Python執行環境,解析Java和Python之間交換的業務數據,將Flink中的狀態後端傳遞給Python以及監視執行狀態。鑒於所有這些複雜性,現在是Apache Beam發揮作用的時候了。作為支持多種引擎和多種語言的大熊,Apache Beam可以在解決這種情況方面做很多工作,所以讓我們看看Apache Beam如何處理執行Python用戶定義的函數。

下面显示了可移植性框架,該框架是Apache Beam的高度抽象的體繫結構,旨在支持多種語言和引擎。當前,Apache Beam支持幾種不同的語言,包括Java,Go和Python。

用戶定義的功能架構

UDF體繫結構不僅需要實現PyVM與JVM之間的通信,還需要在編譯和運行階段滿足不同的要求。在下面的PyLink用戶定義功能架構圖中,JVM中的行為以綠色表示,而PyVM中的行為以藍色表示。讓我們看看編譯期間的局部設計。本地設計依賴於純API映射調用。Py4J用於VM通信。

現在,讓我們看看Python API和Java API在此架構中的工作方式。在Java方面,JobMaster將作業分配給TaskManager,就像處理普通Java作業一樣,並且TaskManager執行任務,這涉及到操作員在JVM和PyVM中的執行。在Python用戶定義的函數運算符中,我們將設計各種gRPC服務,用於JVM和PyVM之間的通信。例如,用於業務數據通信的DataService和用於Python UDF的StateService來調用Java State後端。還將提供許多其他服務,例如日誌記錄和指標。

我們如何使用PyFlink?

了解了PyFlink的體繫結構及其背後的思想之後,我們來看一下PyFlink的特定應用場景,以更好地了解其背後的方式和原因。

PyFlink的應用場景

PyFlink支持哪些業務方案?我們可以從兩個角度分析其應用場景:Python和Java。請記住,PyFlink也適用於Java可以應用的所有情況。

  1. 事件驅動的方案,例如實時數據監控。
  2. 數據分析,例如庫存管理和數據可視化。
  3. 數據管道,也稱為ETL方案,例如日誌解析。
  4. 機器學習,例如有針對性的建議。

您可以在所有這些情況下使用PyFlink。PyFlink也適用於特定於Python的方案,例如科學計算。在如此眾多的應用場景中,您可能想知道現在可以使用哪些特定的PyFlink API。因此,現在我們也來研究這個問題。

PyFlink安裝

在使用任何API之前,您需要安裝PyFlink。當前,要安裝PyFlink,請運行命令:pip install apache-Flink

PyFlink API

PyFlink API與Java Table API完全一致,以支持各種關係和窗口操作。某些易於使用的PyFlink API比SQL API更為強大,例如特定於列操作的API。除了API,PyFlink還提供了多種定義Python UDF的方法。

PyFlink中用戶定義的函數定義

可以擴展ScalarFunction(例如,通過添加指標)以提供更多輔助功能。另外,PyFlink用戶功能函數支持Python支持的所有方法定義,例如lambda,命名函數和可調用函數。

定義完這些方法后,我們可以使用PyFlink Decorators進行標記,並描述輸入和輸出數據類型。我們還可以基於Python的類型提示功能進一步簡化更高版本,以進行類型派生。以下示例將幫助您更好地了解如何定義用戶定義的函數。

定義Python用戶定義函數的一種情況

在本例中,我們將兩個数字相加。首先,為此,導入必要的類,然後定義前面提到的函數。這非常簡單,因此讓我們進行一個實際案例。

PyFlink的未來前景如何?

通常,使用PyFlink進行業務開發很簡單。您可以通過SQL或Table API輕鬆描述業務邏輯,而無需了解基礎實現。讓我們看一下PyFlink的整體前景。

目標驅動路線圖

PyFlink的開發始終受到目標的推動,這些目標是使Flink功能可供Python用戶使用並將Python函數集成到Flink中。根據下面显示的PyFlink路線圖,我們首先在PyVM和JVM之間建立了通信。然後,在Flink 1.9中,我們提供了Python Table API,向Python用戶開放了現有的Flink Table API功能。在Flink 1.10中,我們準備通過以下操作將Python函數集成到Flink:集成Apache Beam,設置Python用戶定義的函數執行環境,管理Python對其他類庫的依賴關係以及為用戶定義用戶定義的函數API,以便支持Python用戶定義函數。

為了擴展分佈式Python的功能,PyFlink提供了對Pandas Series和DataFrame支持,以便用戶可以在PyFlink中直接使用Pandas用戶定義的函數。此外,將來會在SQL客戶端上啟用Python用戶定義函數,以使PyFlink易於使用。PyFlink還將提供Python ML管道API,以使Python用戶能夠在機器學習中使用PyFlink。監視Python用戶定義的函數執行對實際生產和業務至關重要。因此,PyFlink將進一步為Python用戶定義函數提供度量管理。這些功能將包含在Flink 1.11中。

但是,這些只是PyFlink未來發展計劃的一部分。還有更多工作要做,例如優化PyFlink的性能,提供圖形計算API以及為Flink上的Pandas支持Pandas的本機API。我們將繼續向Python用戶提供Flink的現有功能,並將Python的強大功能集成到Flink中,以實現擴展Python生態系統的最初目標。

PyFlink的前景如何?您可能知道,PyFlink是Apache Flink的一部分,它涉及運行時和API層。

PyFlink在這兩層將如何發展?在運行時方面,PyFlink將構建用於JVM和PyVM之間通信的gRPC常規服務(例如控件,數據和狀態)。在此框架中,將抽象化Java Python用戶定義函數運算符,並構建Python執行容器以支持Python的多種執行方式。例如,PyFlink可以在Docker容器中甚至在外部服務集群中作為進程運行。特別是在外部服務群集中運行時,將以套接字的形式啟用無限擴展功能。這一切在後續的Python集成中都起着至關重要的作用。

在API方面,我們將在Flink中啟用基於Python的API,以實現我們的使命。這也依賴於Py4J VM通信框架。PyFlink將逐漸支持更多的API,包括Flink中的Java API(例如Python Table API,UDX,ML Pipeline,DataStream,CEP,Gelly和State API)以及在Python用戶中最受歡迎的Pandas API。基於這些API,PyFlink將繼續與其他生態系統集成以便於開發;例如Notebook,Zeppelin,Jupyter和Alink,這是阿里巴巴的Flink開源版本。到目前為止,PyAlink已完全整合了PyFlink的功能。PyFlink也將與現有的AI系統平台集成,例如著名的TensorFlow。

為此,PyFlink將一直保持活力。同樣,PyFlink的任務是使Flink功能可供Python用戶使用,並在Flink上運行Python分析和計算功能。

更多實時數據分析相關博文與科技資訊,歡迎關注 “實時流式計算”
關注 “實時流式計算” 回復 “电子書” 獲取Flink 300頁實戰电子書

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

2014第三屆混合動力技術國際峰會 會後報告

6月26日,以“技術驅動混合動力市場化”為主題的2014第三屆混合動力技術國際峰會開幕。本屆峰會由Merisis Consulting舉辦,超過150位來自政府部門,科研機構,整車,一級和二級的核心零部件供應商的高層代表們齊聚一堂,共議混合動力汽車的技術路線和發展。

乘用車市場方面,目前的市場熱度雖然中等,可以預見在不久以後將會迎來良好的發展勢頭。四部委聯合發佈的《關於繼續開展新能源汽車推廣應用工作的通知》中明確了PHEV的補貼政策,上海廣州北京各地政府也相繼推出了對於PHEV和燃料電池汽車的大力度補貼條例,這將在未來1~2年內對HEV的銷量和市場接受度產生一定的推動力度。而技術相較成熟的商用車市場,大力度的政策補貼,也驅使整車廠們在不斷追求規模的基礎上追求更低成本,最優化的技術。

2014第三屆混合動力技術國際峰會為汽車業界關注混合動力和新能源汽車發展的同仁們提供了一手資訊,説明他們更好的判斷行業未來走向,學習借鑒前沿的經驗與技術,並更好的推動混合動力汽車的研發和市場化進程。

在兩天全程的演講中,嘉賓們從各自領域為在場觀眾分享了行業內前瞻資訊和專業知識。以上總結部分讓筆者印象深刻並值得行業人士深入思考的觀點。

國家發改委耿志成老師在報告中明確國家主要扶持的發展方向有三方面:電動車(包括PHEV),燃料電池汽車,HEV電池控制系統。那麼汽車工業尤其是新能源汽車的未來是靠市場還是靠政策,這一圍繞人們心中許久的問題也許仍然有待時日來解答。

真鋰研究聶昕老師未對目前中國電池行業發展做過於資料性的分析,而是提出未來電池技術的瓶頸將不再是問題。從今年正極材料已經發展到五氟級,以及液體電解質發展到固態電解質,性能大幅度提升等方面可以預見未來無限可能性。同時,也認為目前電池領域存在的主要將圍繞BMS以及一致性問題展開。

第一天上午的小組討論環節中上汽集團商用車技術中心蘭志波總工分享了他個人的獨到觀點,他認為目前HEV存在的成本問題還是基於用戶需求挖掘不夠,用戶並非一味的追求低價格,真正有特點的產品仍然能夠打動消費者。汽車不論技術如何發展,消費者還是講駕駛體驗。

來自吉孚動力的Sven Steinwascher先生分享了混合動力變速箱方面的最新技術挑戰,從客戶接受度到換擋品質,以及變速箱的整合可能性。在演講中他提出了變速箱混合的概念並對其動力總成的佈局進行了詳盡分析。從而最終達到了良好的駕駛性能,經濟型和低成本。

法國Energies Nouvelles的Sébastien MAGAND先生為我們分享了混合動力傳動技術方面的海外領先經驗。IFPEN的傳動系統有11個模式,低於50kg的輕量化設計,有低於300mm長度的無離合器版本,以及模組化的電動汽車整合及比率。優勢在於大量節油,設計輕巧,卓越駕駛性能以及成本最優化。

第二天下午商用車專場中來自五洲龍的全頌華先生細緻深入的為大家講解了公司技術的發展,關鍵零部件的發展以及車配套設施的發展。公司緊跟國家政策,參與了2009年商業示範運行項目並利用2011年世界大學生運動會實現了真正的市場化和批量化。

一位演講者曾經這麼說到,在混合動力越走越看不到前途的過程中,松正的技術突然讓我感覺又有了希望。天津松正的李喬博士在本次論壇上圍繞第IV代插電式深混公車動力系統關鍵技術及應用展開的詳細的解析。觀點很鮮明:技術應以使用者為導向,使用者關心價格又希望避免插電的繁瑣過程。松正的技術可以同時滿足客戶的這兩點需求。

宇通客車主管新能源產品的胡俊傑經理提出混合動力才是未來公交的發展方向。配置可更改、功能可定制的動力系統技術平臺,大力自主研發電機電控電源技術、整車和發動機節能技術和輕量化技術。作為新能源客車推廣應用的領頭企業,他認為制約推廣的瓶頸在於電源和充電技術兩方面,電池的成本和壽命,充電標準、設施、模式包括技術開發。

在聆聽了插電式、混合動力之後,東風揚子江客車雷洪鈞開始了他純電動客車的精彩報告。他認為電動車,混合動力,插電式三者存在都有它的意義,細分市場和技術方向。混合動力中“混合”二字的定義有待深究,混合的普遍解釋是2種以上能源作為動力並存。混合僅僅是油電混合,氣電混合或者也應將燃料汽車加入其中。如果新能源汽車的最終目標是節能減排,那麼他的觀點是減排肯定排第一位,然後才是節能。而我們所談論的重混,中混&微混根本是同一條技術路線,只是發展程度和時間問題。

衷心感謝中國公交資訊網、電動汽車網、高工鋰電網、新華信汽車網等十餘家媒體合作夥伴對論壇進行的全程的報導。我們將在來年呈現更精彩豐富的會議內容,也期待行業內合作夥伴的參與和支持。

更多混合動力行業最新動態、趨勢、資料、人脈資訊,可關注搜索微信服務號“merisis-asia”,掃描以下二維碼關注我們的微信互動平臺

網站:

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

【其他文章推薦】

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

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

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

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

※超省錢租車方案

砸數十億歐元!BMW加碼購三星SDI電池 衝電動車市場

BMW15日表示,計劃為i3電動都市車、插電式混合動力的i8跑車與未來數年新推出的混合動力車款擴大採購SDI電池。三星SDI發表聲明說,與BMW的供應協議價值「數十億歐元」,但未提確切金額。   BMW正推出i8與i3車款來對抗電動車大廠Tesla搶攻豪華車領域,上述交易讓BMW能確保汽車電池的供應來源。對三星SDI來說,BMW的訂單有助擴大汽車電池業務。

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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