1. 程式人生 > >關於Yarn原始碼那些事-前傳之ResourceManager篇(一)初始化

關於Yarn原始碼那些事-前傳之ResourceManager篇(一)初始化

在關於Yarn那些事的部落格裡,介紹的主要是針對任務提交的一個動態流程說明,而其中牽涉到的一些細節問題,必須通過Resourcemanager的啟動和NodeManager的啟動,來更好的說明。

而本系列,就詳細說說ResourceManager啟動過程中,都發生了什麼。

我們都知道,Yarn的啟動指令碼是start-yan.sh,我們就從這個指令碼開始,琢磨琢磨。

"$bin"/yarn-daemon.sh --config $YARN_CONF_DIR  start resourcemanager

腳本里這句話,指向了本目錄下的yarn-daemon.sh指令碼,命令引數指定了resourcemanager,接著看yarn-daemon.sh指令碼:

nohup nice -n $YARN_NICENESS "$HADOOP_YARN_HOME"/bin/yarn --config $YARN_CONF_DIR $command "[email protected]" > "$log" 2>&1 < /dev/null &

這句話很關鍵,交代了我們實際的啟動指令碼是bin目錄下的yarn,我們看下:

elif [ "$COMMAND" = "resourcemanager" ] ; then
  CLASSPATH=${CLASSPATH}:$YARN_CONF_DIR/rm-config/log4j.properties
  CLASSPATH=${CLASSPATH}:"$HADOOP_YARN_HOME/$YARN_DIR/timelineservice/*"
  CLASSPATH=${CLASSPATH}:"$HADOOP_YARN_HOME/$YARN_DIR/timelineservice/lib/*"
  CLASS='org.apache.hadoop.yarn.server.resourcemanager.ResourceManager'
  YARN_OPTS="$YARN_OPTS $YARN_RESOURCEMANAGER_OPTS"
  if [ "$YARN_RESOURCEMANAGER_HEAPSIZE" != "" ]; then
    JAVA_HEAP_MAX="-Xmx""$YARN_RESOURCEMANAGER_HEAPSIZE""m"
  fi

終於順利找到了根基,原來根據我們指定的指令碼,找到的是ResourceManager這個類來啟動的,下面就看看這個類。

先來看下注釋:

/**
 * The ResourceManager is the main class that is a set of components. "I am the
 * ResourceManager. All your resources belong to us..."
 *
 */
@SuppressWarnings("unchecked")
public class ResourceManager extends CompositeService implements Recoverable

格外霸氣,管理整個叢集內所有的資源,並且繼承了CompositeService類,這是一個服務類,不多介紹了,主要提供了一些服務初始化和啟動的方法,供子類使用。

從ResourceManager的成員變數開始看起:

protected ClientToAMTokenSecretManagerInRM clientToAMSecretManager = new ClientToAMTokenSecretManagerInRM();
	protected RMContainerTokenSecretManager containerTokenSecretManager;
	protected NMTokenSecretManagerInRM nmTokenSecretManager;

	protected AMRMTokenSecretManager amRmTokenSecretManager;

	private Dispatcher rmDispatcher;

	protected ResourceScheduler scheduler;
	private ClientRMService clientRM;
	protected ApplicationMasterService masterService;
	private ApplicationMasterLauncher applicationMasterLauncher;
	private AdminService adminService;
	private ContainerAllocationExpirer containerAllocationExpirer;
	protected NMLivelinessMonitor nmLivelinessMonitor;
	protected NodesListManager nodesListManager;
	private EventHandler<SchedulerEvent> schedulerDispatcher;
	protected RMAppManager rmAppManager;
	protected ApplicationACLsManager applicationACLsManager;
	protected QueueACLsManager queueACLsManager;
	protected RMDelegationTokenSecretManager rmDTSecretManager;
	private DelegationTokenRenewer delegationTokenRenewer;
	private WebApp webApp;
	protected RMContext rmContext;
	protected ResourceTrackerService resourceTracker;
	private boolean recoveryEnabled;

很多,具體可以參照每個類的用法,在此不多說,其中牽涉到Application較多的,比如RMAppManager,RMContext等,需要細看,這是廢話,每個都值得研究。

看Main方法:

			Configuration conf = new YarnConfiguration();
			ResourceManager resourceManager = new ResourceManager();
			ShutdownHookManager.get().addShutdownHook(new CompositeServiceShutdownHook(resourceManager),
					SHUTDOWN_HOOK_PRIORITY);
			setHttpPolicy(conf);
			resourceManager.init(conf);
			resourceManager.start();
		

直接把目光聚焦在這裡,我們重點研究下服務的初始化和啟動。

this.rmDispatcher = createDispatcher();
		addIfService(this.rmDispatcher);

初始化的第一個關鍵點,建立排程器,這是ResourceManager非同步排程的關鍵,看看這個方法:很簡單:

protected Dispatcher createDispatcher() {
		return new AsyncDispatcher();
	}

很明顯,這是個非同步排程器,看看這個類的註釋和初始化步驟:

/**
 * Dispatches {@link Event}s in a separate thread. Currently only single thread
 * does that. Potentially there could be multiple channels for each event type
 * class and a thread pool can be used to dispatch the events.
 */
@SuppressWarnings("rawtypes")
@Public
@Evolving
public class AsyncDispatcher extends AbstractService implements Dispatcher 

這也是一個需要啟動的服務,用於事件的排程:

public AsyncDispatcher() {
		this(new LinkedBlockingQueue<Event>());
	}

	public AsyncDispatcher(BlockingQueue<Event> eventQueue) {
		super("Dispatcher");
		this.eventQueue = eventQueue;
		this.eventDispatchers = new HashMap<Class<? extends Enum>, EventHandler>();
	}

注意,Dispatcher內部封裝了一個阻塞佇列,執行過程中會把事件都放在這個池子裡,並進行排程處理,同時定義了一個eventDispatchers,後續程式碼更容易看懂這個map的作用:

我們仔細看看AsyncDispatcher的服務初始化程式碼:

@Override
	protected void serviceInit(Configuration conf) throws Exception {
		this.exitOnDispatchException = conf.getBoolean(Dispatcher.DISPATCHER_EXIT_ON_ERROR_KEY,
				Dispatcher.DEFAULT_DISPATCHER_EXIT_ON_ERROR);
		super.serviceInit(conf);
	}

目前來說,AsyncDispatcher的初始化程式碼先到這兒,我們繼續看ResourceManager的服務初始化程式碼:

this.amRmTokenSecretManager = createAMRMTokenSecretManager(conf);

我們看到內部有個這個成員變數:

/**
 * AMRM-tokens are per ApplicationAttempt. If users redistribute their
 * tokens, it is their headache, god save them. I mean you are not supposed to
 * distribute keys to your vault, right? Anyways, ResourceManager saves each
 * token locally in memory till application finishes and to a store for restart,
 * so no need to remember master-keys even after rolling them.
 */
public class AMRMTokenSecretManager extends
    SecretManager<AMRMTokenIdentifier> 

看註釋,清晰明瞭,每次提交一個ApplicationAttempt時候,都不用再遞交自己的token了,實際上還是一個身份驗證工具(自己的理解)。

this.containerAllocationExpirer = new ContainerAllocationExpirer(this.rmDispatcher);
		addService(this.containerAllocationExpirer);

看下這兩句話ContainerAllocationExpirer,並且加到了serviceList中,用於最後的初始化,我們看看這個是什麼作用,註釋非常簡單,還是留在動態提交ApplicationMaster的時候分析吧,其實主要是用來判斷分配的container是否在規定時間內得到啟動的:

AMLivelinessMonitor amLivelinessMonitor = createAMLivelinessMonitor();
		addService(amLivelinessMonitor);

看這個AMLiveLinessMonitor,顧名思義,是用來檢查ApplicationLivenessMonitor是否存活的,其繼承了這個類:

/**
 * A simple liveliness monitor with which clients can register, trust the
 * component to monitor liveliness, get a call-back on expiry and then finally
 * unregister.
 */
@Public
@Evolving
public abstract class AbstractLivelinessMonitor<O> extends AbstractService 

同時,ContainerAllocationMonitor也繼承了這個類,就是用於讓客戶端監控的:

AMLivelinessMonitor amFinishingMonitor = createAMLivelinessMonitor();
		addService(amFinishingMonitor);

下面初始化了一個一樣的AMLiveLinessMonitor,但是變數名不同,同樣是起監控作用的:

接下來看RMStateStore的初始化:

boolean isRecoveryEnabled = conf.getBoolean(YarnConfiguration.RECOVERY_ENABLED,
				YarnConfiguration.DEFAULT_RM_RECOVERY_ENABLED);

		RMStateStore rmStore = null;
		if (isRecoveryEnabled) {
			recoveryEnabled = true;
			rmStore = RMStateStoreFactory.getStore(conf);
		} else {
			recoveryEnabled = false;
			rmStore = new NullRMStateStore();
		}

對於大部分成員變數的初始化不予多說,先看下RMStateStore的初始化,在我們預設配置下:isRecoveryEnabled為false,所以建立了一個空的RMStateStore,即NullRMStateStore,對於ResourceManager的狀態進行儲存:

			rmStore.init(conf);
			rmStore.setRMDispatcher(rmDispatcher);
		

這裡面注意下,rmStore內部的dispatcher與RM的dispatcher不是同一個,程式碼如下:

private Dispatcher rmDispatcher;
AsyncDispatcher dispatcher;

RMStateStore內部有兩個排程器,rmDispatcher是RM的排程器,而dispatcher則是其內部用來排程事件的排程器,對於RMStateStore的init程式碼有些繞,仔細看下:

@Override
	public void init(Configuration conf) {
		if (conf == null) {
			throw new ServiceStateException("Cannot initialize service " + getName() + ": null configuration");
		}
		if (isInState(STATE.INITED)) {
			return;
		}
		synchronized (stateChangeLock) {
			if (enterState(STATE.INITED) != STATE.INITED) {
				setConfig(conf);
				try {
					serviceInit(config);
					if (isInState(STATE.INITED)) {
						// if the service ended up here during init,
						// notify the listeners
						notifyListeners();
					}
				} catch (Exception e) {
					noteFailure(e);
					ServiceOperations.stopQuietly(LOG, this);
					throw ServiceStateException.convert(e);
				}
			}
		}
	}

其實際呼叫的是AbstractService的init方法,其中呼叫到了serviceInit方法,而這個方法,則是RMStateStore的方法:

public synchronized void serviceInit(Configuration conf) throws Exception {
		// create async handler
		dispatcher = new AsyncDispatcher();
		dispatcher.init(conf);
		dispatcher.register(RMStateStoreEventType.class, new ForwardingEventHandler());
		initInternal(conf);
	}

很清楚看到了內部封裝了一個自己的dispatcher,用於排程RMStateStoreEventType型別的事件:

下面接著看:

this.rmContext = new RMContextImpl(this.rmDispatcher, rmStore, this.containerAllocationExpirer,
				amLivelinessMonitor, amFinishingMonitor, delegationTokenRenewer, this.amRmTokenSecretManager,
				this.containerTokenSecretManager, this.nmTokenSecretManager, this.clientToAMSecretManager);

這是重頭戲,我們必須看看這個擁有如此多成員變數的RMContextImpl到底是什麼:

/**
 * Context of the ResourceManager.
 */
public interface RMContext {

這是RMContextImpl父類的註釋,是ResourceManager的上下文,就相當於管家了,基本大權在握,是ResourceManager的心腹。

接下來是這兒:

		this.nodesListManager = new NodesListManager(this.rmContext);

其實就相當於告訴了RM,這裡到底有多少個子節點可供使用,而且給新建的NodesListManager內部也安插了RM的心腹,即RMContextImpl。

		this.rmDispatcher.register(NodesListManagerEventType.class, this.nodesListManager);

看到這兒,我們又得回去看AsyncDispatcher中的一個register方法:

@SuppressWarnings("unchecked")
	@Override
	public void register(Class<? extends Enum> eventType, EventHandler handler) {
		/* check to see if we have a listener registered */
		EventHandler<Event> registeredHandler = (EventHandler<Event>) eventDispatchers.get(eventType);
		LOG.info("Registering " + eventType + " for " + handler.getClass());
		if (registeredHandler == null) {
			eventDispatchers.put(eventType, handler);
		} else if (!(registeredHandler instanceof MultiListenerHandler)) {
			/* for multiple listeners of an event add the multiple listener handler */
			MultiListenerHandler multiHandler = new MultiListenerHandler();
			multiHandler.addHandler(registeredHandler);
			multiHandler.addHandler(handler);
			eventDispatchers.put(eventType, multiHandler);
		} else {
			/* already a multilistener, just add to it */
			MultiListenerHandler multiHandler = (MultiListenerHandler) registeredHandler;
			multiHandler.addHandler(handler);
		}
	}

仔細看來,其實就相當於eventDispatcher的充實,把各類事件即相應的處理,都送給eventdispatcher,方便後續出現類似事件,eventdispatcher能夠迅速找到對應的物件來進行處理。

這裡,就相當於把對應於NodeManager出現的事情,都交給了NodeListManager,這就是你的工作了。

public enum NodesListManagerEventType {
	NODE_USABLE, NODE_UNUSABLE
}

如果出現了節點可用和不可用的事情,你就得迅速予以處理了。

// Initialize the scheduler
		this.scheduler = createScheduler();
		this.schedulerDispatcher = createSchedulerEventDispatcher();
		addIfService(this.schedulerDispatcher);
		this.rmDispatcher.register(SchedulerEventType.class, this.schedulerDispatcher);

接下來,建立了一個排程器,這一段得仔細看看了,因為yarn中的排程器非常重要,我們作業的初始化,都離不開它:

protected ResourceScheduler createScheduler() {
		String schedulerClassName = conf.get(YarnConfiguration.RM_SCHEDULER, YarnConfiguration.DEFAULT_RM_SCHEDULER);
		LOG.info("Using Scheduler: " + schedulerClassName);
		try {
			Class<?> schedulerClazz = Class.forName(schedulerClassName);
			if (ResourceScheduler.class.isAssignableFrom(schedulerClazz)) {
				return (ResourceScheduler) ReflectionUtils.newInstance(schedulerClazz, this.conf);
			} else {
				throw new YarnRuntimeException("Class: " + schedulerClassName + " not instance of "
						+ ResourceScheduler.class.getCanonicalName());
			}
		} catch (ClassNotFoundException e) {
			throw new YarnRuntimeException("Could not instantiate Scheduler: " + schedulerClassName, e);
		}
	}

我這裡的程式碼是hadoop 2.2.0,在預設配置下,配置的排程器是:

org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler

而接著,我們給排程器自己定義了一個排程器,換句話說,對於排程器接受的事件型別,其會繼續排程給其他的handler去處理

最後,把這個排程器也註冊給了RM內部的排程器:

protected EventHandler<SchedulerEvent> createSchedulerEventDispatcher() {
		return new SchedulerEventDispatcher(this.scheduler);
	}
		this.rmDispatcher.register(SchedulerEventType.class, this.schedulerDispatcher);

接著,我們的非同步排程器又加入了三個成分,負責對Application相關事件,ApplicationAttempt事件,RMNode事件進行排程:

// Register event handler for RmAppEvents
		this.rmDispatcher.register(RMAppEventType.class, new ApplicationEventDispatcher(this.rmContext));

		// Register event handler for RmAppAttemptEvents
		this.rmDispatcher.register(RMAppAttemptEventType.class, new ApplicationAttemptEventDispatcher(this.rmContext));

		// Register event handler for RmNodes
		this.rmDispatcher.register(RMNodeEventType.class, new NodeEventDispatcher(this.rmContext));

接著,我們看下這個:

this.resourceTracker = createResourceTrackerService();
		addService(resourceTracker);

從官方文件中,我們知道RM實現了對於系統全部資源的管控,而這個管控是通過RPC來實現的,NodeManager呼叫ResourceTracker內的方法來提交自己的資源,而RM端有相應的處理,返回命令,讓NM予以執行,而ResourceTrackerSerive就是在此處初始化的:

@Override
	protected void serviceInit(Configuration conf) throws Exception {
		resourceTrackerAddress = conf.getSocketAddr(YarnConfiguration.RM_RESOURCE_TRACKER_ADDRESS,
				YarnConfiguration.DEFAULT_RM_RESOURCE_TRACKER_ADDRESS,
				YarnConfiguration.DEFAULT_RM_RESOURCE_TRACKER_PORT);

		RackResolver.init(conf);
		nextHeartBeatInterval = conf.getLong(YarnConfiguration.RM_NM_HEARTBEAT_INTERVAL_MS,
				YarnConfiguration.DEFAULT_RM_NM_HEARTBEAT_INTERVAL_MS);
		if (nextHeartBeatInterval <= 0) {
			throw new YarnRuntimeException("Invalid Configuration. " + YarnConfiguration.RM_NM_HEARTBEAT_INTERVAL_MS
					+ " should be larger than 0.");
		}

		minAllocMb = conf.getInt(YarnConfiguration.RM_SCHEDULER_MINIMUM_ALLOCATION_MB,
				YarnConfiguration.DEFAULT_RM_SCHEDULER_MINIMUM_ALLOCATION_MB);
		minAllocVcores = conf.getInt(YarnConfiguration.RM_SCHEDULER_MINIMUM_ALLOCATION_VCORES,
				YarnConfiguration.DEFAULT_RM_SCHEDULER_MINIMUM_ALLOCATION_VCORES);

		super.serviceInit(conf);
	}

可以看到,這裡牽涉到了很多的預設配置的地址,所以,看程式碼是必要的,我們想要把配置檔案全部搞通,其實認真搞通程式碼,就輕而易舉了。

masterService = createApplicationMasterService();
		addService(masterService);

看看這一段,新建了一個ApplicationMasterService,用於對所有提交的ApplicationMaster進行管理:其中的serviceInit方法不復雜,重點在其serviceStart方法中:

下面,我們注意看下這段程式碼:

this.rmAppManager = createRMAppManager();
		// Register event handler for RMAppManagerEvents
		this.rmDispatcher.register(RMAppManagerEventType.class, this.rmAppManager);
		this.rmDTSecretManager = createRMDelegationTokenSecretManager(this.rmContext);
		rmContext.setRMDelegationTokenSecretManager(this.rmDTSecretManager);
		clientRM = createClientRMService();
		rmContext.setClientRMService(clientRM);
		addService(clientRM);

		adminService = createAdminService(clientRM, masterService, resourceTracker);
		addService(adminService);

		this.applicationMasterLauncher = createAMLauncher();
		this.rmDispatcher.register(AMLauncherEventType.class, this.applicationMasterLauncher);

這裡有些東西需要注意,新建了一個ClientRMService,負責客戶端與RM的所有互動,對於客戶端的每個請求,我們都可以在ClientRMService下面找到相應的程式碼。

下面接著看,AMLauncher,這個很重要,負責ApplicationMaster的啟動,必須重點分析下。

this.applicationMasterLauncher = createAMLauncher();
		this.rmDispatcher.register(AMLauncherEventType.class, this.applicationMasterLauncher);

基於RM的管家,建立了一個AMLauncher:

protected ApplicationMasterLauncher createAMLauncher() {
		return new ApplicationMasterLauncher(this.rmContext);
	}

分析下其中的serviceInit方法:

@Override
	protected void serviceStart() throws Exception {
		launcherHandlingThread.start();
		super.serviceStart();
	}

看看其中的launchHandlingThread:

private class LauncherThread extends Thread {
		public LauncherThread() {
			super("ApplicationMaster Launcher");
		}
		@Override
		public void run() {
			while (!this.isInterrupted()) {
				Runnable toLaunch;
				try {
					toLaunch = masterEvents.take();
					launcherPool.execute(toLaunch);
				} catch (InterruptedException e) {
					LOG.warn(this.getClass().getName() + " interrupted. Returning.");
					return;
				}
			}
		}
	}

其內部封裝了一個執行緒池,對於排程給自身的事件不斷進行處理:

在serviceInit方法的最後,呼叫了父類的serviceInit方法,我們看下其父類CompositeService的serviceInit方法:

protected void serviceInit(Configuration conf) throws Exception {
		List<Service> services = getServices();
		if (LOG.isDebugEnabled()) {
			LOG.debug(getName() + ": initing services, size=" + services.size());
		}
		for (Service service : services) {
			service.init(conf);
		}
		super.serviceInit(conf);
	}
public List<Service> getServices() {
		synchronized (serviceList) {
			return Collections.unmodifiableList(serviceList);
		}
	}

在看原始碼的時候,發現RM的serviceInit方法中,所有服務都有一個操作:

protected void addService(Service service) {
		if (LOG.isDebugEnabled()) {
			LOG.debug("Adding service " + service.getName());
		}
		synchronized (serviceList) {
			serviceList.add(service);
		}
	}

呼叫了父類中的addService方法,把所有服務新增到了serviceList中,在這裡統一予以初始化,不得不說設計很精妙,在我們分析原始碼的時候,必須注意看下service相關的類;最頂層的父類是Serivce類,這是個介面,定義了服務的基本操作和生命週期,其唯一的實現類,是個抽象類,為AbstractService,一般來說,並不複雜的服務繼承並實現AbstractService即可,複雜的服務如RM就會繼承Compositeservice(AbstractService的子類)。

在RM服務初始化完畢之後,我們接著看服務的啟動部分。

相關推薦

關於Yarn原始碼那些-ResourceManager初始

在關於Yarn那些事的部落格裡,介紹的主要是針對任務提交的一個動態流程說明,而其中牽涉到的一些細節問題,必須通過Resourcemanager的啟動和NodeManager的啟動,來更好的說明。而本系列,就詳細說說ResourceManager啟動過程中,都發生了什麼。我們都

SpringBoot原始碼事物事物是在哪裡開啟的呢?

最近正在學習使用JPA,JPA是預設開啟事物管理的,在哪裡開啟的呢???突然覺得對spring處理事物的原理一竊不通,才有了這次的原始碼閱讀之行,有不正確的地方歡迎大家指正。 先簡略過一下springboot的執行原理 public void pr

Zabbix3.4安裝

entos agent roo zabb com serve arc centos 7 mysql- 實驗環境:Centos 71.下載zabbix yum 文件 [root@xiaopeng /]# cd /etc/yum.repos.d/ [root@xiaop

Zabbix 3.4 安裝

systemctl 防火墻 sql數據庫 x86_64 mage system 啟動 輸入密碼 shang 實驗環境:Centos 7```1.下載zabbix yum 文件[root@xiaopeng /]# cd /etc/yum.repos.d/[root@x

淺析 rand7生成rand10 方法 思想

同余 one a + b 個數 height 意義 UC ogr edi 【問題描寫敘述】 rand7是一個能生成1-7的隨機數。要求利用rand7生成1-10的隨

構建NetCore應用框架實戰:什麽是框架,如何設計一個框架

net 希望 dmi 清晰 構建 組織 評估 系統開發 概念 一、系列簡述 本篇起,將通過一系列文章,去描述如何構建一個應用開發框架,並以作者開發的框架為例,逐個點展開分析,如何從零開始,構建自己的開發框架。 本系列文章的目的,是帶領有一編程經驗的人,通過動手,初步完成

scala隨筆入門

scala 簡介 Java 方言之一,java的方言除了scala還有kotlin、groovy、clojure等。 執行在JVM之上 scala和kotlin、groovy、clojure一樣是多正規化程式語言,支援函數語言程式設計 scala常用領域是併發程

Elam的caffe筆記配置:CentOS6.5編譯安裝gcc4.8.2

Elam的caffe筆記之配置篇(一):CentOS6.5編譯安裝gcc4.8.2 配置要求: 系統:centos6.5 目標:基於CUDA8.0+Opencv3.1+Cudnnv5.1+python3.6介面的caffe框架 任何對linux處於入門級別的小白都應

運維經典面試題網路

1、寫出12.23.34.0/29的掩碼 11111111.11111111.11111111.11111000 255.255.255.248 2、簡述衝突域與廣播域的區別 衝突域:基於osi的第一層物理層。 一個站點向另一個站

Java多執行緒基礎

一、併發和並行 1.1 概念 1.2 比較 1.3 程序和執行緒 二、基礎概念 2.1 執

Machine LearningPython

Machine Learning之Python篇 概述 教程 《Python機器學習》中文版 東南大學某研究生的github,包含大量ML演算法示例。 上個哥們的DL示例 Python資料分析之武林祕籍。這裡包括了大量ML或DL的python工具包。

CSS3筆記定位relative

知識點1:relative和absolute   relative: 相對自身,並會限制內部absolute元素層疊   absolute: 相對容器,並受到父類容器relative的影響,比如:o

部署Django部落格全記錄Nginx

Nginx的優點 注:具體原理沒搞清,先記著 Nginx更安全;Nginx能更好地處理靜態資源(通過一些http request header)。 Nginx也可以快取一些動態內容;Nginx可以更好地配合CDN。 Nginx可以進行多臺機器的負載均

Spring Boot 入門基礎

一、前言 Spring Boot 是由 Pivotal 團隊提供的全新框架,其設計目的是用來簡化新 Spring 應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。 本系列以快速入門為主,可當作工具小手冊閱讀

Windows驅動開發入門

        本文主要介紹“如何入門Windows驅動開發”和“新手需要掌握哪些知識和動手技能”,大部分是本人探索Windows驅動開發近一月時間的經驗之談。大致包括如下幾個方面: 1,開發工具、除錯工具和除錯手段; 2,Windows驅動開發框架; 3,驅動程式基本知識

FPGA設計硬體

     完成整個硬體電路板的設計後,接下來就是程式的設計了,這裡面靈活性很大,在設計中一定要注意語法的嚴謹性,一個小小的“<="都會帶來無盡的麻煩,在完成程式設計後,一定要進行時序模擬,我一般是先把模組分塊利用工具內部波形模擬來檢視時序,接著採用SIGNAL TAP II進行內部邏輯分析,這個花了太多

小程式入門

      大家好,我是小編,這幾天自己學習了小程式,發現真的太爽了,以下是我寫下的筆記,希望給入門的盆友提供一些幫助吧。 app.js   用於監聽並處理小程式的生命週期函式,宣告全域性變數,呼叫框架,提供豐富的API 同步儲存:wx.setStorageSync()

Linux下安裝Lnmp環境準備

1、準備 1.1、安裝lrzsz yum install lrzsz -y # rz 上傳 sz PATH 1.2、配置防火牆 vi /etc/sysconfig/iptables #編輯防火牆配置檔案 把以下內容貼上進去 #

Spring Boot 學習基礎

該系列並非完全原創,官方文件、作者Spring Boot 是由 Pivotal 團隊提供的全新框架,其設計目的是用來簡化新 Spring 應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。一、環境搭建建立一個Maven專案,

Revit二次開發技能———軸網自動對齊

在做專案的過程中,軸網都是進行翻模或者拾取,而翻模出來的軸網往往都是不堪入目的,為了滿足專案需要,做了軸網對齊的功能,廢話不多說,直接上乾貨。 首先做一些準備工作,先建立一個軸網過濾類,接下來會用到,程式碼如下: class GridSelectionFilter : ISelection