深入理解Ribbon之原始碼解析
什麼是Ribbon
Ribbon是Netflix公司開源的一個負載均衡的專案,它屬於上述的第二種,是一個客戶端負載均衡器,執行在客戶端上。它是一個經過了雲端測試的IPC庫,可以很好地控制HTTP和TCP客戶端的一些行為。 Feign已經預設使用了Ribbon。
- 負載均衡
- 容錯
- 多協議(HTTP,TCP,UDP)支援非同步和反應模型
- 快取和批處理
RestTemplate和Ribbon相結合
Ribbon在Netflix元件是非常重要的一個元件,在Zuul中使用Ribbon做負載均衡,以及Feign元件的結合等。在Spring Cloud 中,作為開發中,做的最多的可能是將RestTemplate和Ribbon相結合,你可能會這樣寫:
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
消費另外一個的服務的介面,差不多是這樣的:
@Service public class RibbonService { @Autowired RestTemplate restTemplate; public String hi(String name) { return restTemplate.getForObject("http://eureka-client/hi?name="+name,String.class); } }
深入理解Ribbon
LoadBalancerClient
在Riibon中一個非常重要的元件為LoadBalancerClient,它作為負載均衡的一個客戶端。它在spring-cloud-commons包下:
的LoadBalancerClient是一個介面,它繼承ServiceInstanceChooser,它的實現類是RibbonLoadBalancerClient,這三者之間的關係如下圖:
其中LoadBalancerClient介面,有如下三個方法,其中excute()為執行請求,reconstructURI()用來重構url:
public interface LoadBalancerClient extends ServiceInstanceChooser { <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; URI reconstructURI(ServiceInstance instance, URI original); }
ServiceInstanceChooser介面,主要有一個方法,用來根據serviceId來獲取ServiceInstance,程式碼如下:
public interface ServiceInstanceChooser {
ServiceInstance choose(String serviceId);
}
LoadBalancerClient的實現類為RibbonLoadBalancerClient,這個類是非常重要的一個類,最終的負載均衡的請求處理,由它來執行。它的部分原始碼如下:
public class RibbonLoadBalancerClient implements LoadBalancerClient {
...//省略程式碼
@Override
public ServiceInstance choose(String serviceId) {
Server server = getServer(serviceId);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
protected Server getServer(String serviceId) {
return getServer(getLoadBalancer(serviceId));
}
protected Server getServer(ILoadBalancer loadBalancer) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer("default"); // TODO: better handling of key
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
...//省略程式碼
在RibbonLoadBalancerClient的原始碼中,其中choose()方法是選擇具體服務例項的一個方法。該方法通過getServer()方法去獲取例項,經過原始碼跟蹤,最終交給了ILoadBalancer類去選擇服務例項。
ILoadBalancer在ribbon-loadbalancer的jar包下,它是定義了實現軟體負載均衡的一個介面,它需要一組可供選擇的服務註冊列表資訊,以及根據特定方法去選擇服務,它的原始碼如下 :
public interface ILoadBalancer {
public void addServers(List<Server> newServers);
public Server chooseServer(Object key);
public void markServerDown(Server server);
public List<Server> getReachableServers();
public List<Server> getAllServers();
}
其中,addServers()方法是新增一個Server集合;chooseServer()方法是根據key去獲取Server;markServerDown()方法用來標記某個服務下線;getReachableServers()獲取可用的Server集合;getAllServers()獲取所有的Server集合。
DynamicServerListLoadBalancer
它的繼承類為BaseLoadBalancer,它的實現類為DynamicServerListLoadBalancer,這三者之間的關係如下:
檢視上述三個類的原始碼,可用發現,配置以下資訊,IClientConfig、IRule、IPing、ServerList、ServerListFilter和ILoadBalancer,檢視BaseLoadBalancer類,它預設的情況下,實現了以下配置:
- IClientConfig ribbonClientConfig: DefaultClientConfigImpl配置
- IRule ribbonRule: RoundRobinRule 路由策略
- IPing ribbonPing: DummyPing
- ServerList ribbonServerList: ConfigurationBasedServerList
- ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter
- ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
IClientConfig 用於對客戶端或者負載均衡的配置,它的預設實現類為DefaultClientConfigImpl。
IRule用於複雜均衡的策略,它有三個方法,其中choose()是根據key 來獲取server,setLoadBalancer()和getLoadBalancer()是用來設定和獲取ILoadBalancer的,它的原始碼如下:
public interface IRule{
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
IRule有很多預設的實現類,這些實現類根據不同的演算法和邏輯來處理負載均衡。Ribbon實現的IRule有一下。在大多數情況下,這些預設的實現類是可以滿足需求的,如果有特性的需求,可以自己實現。
-
BestAvailableRule 選擇最小請求數
-
ClientConfigEnabledRoundRobinRule 輪詢
-
RandomRule 隨機選擇一個server
-
RoundRobinRule 輪詢選擇server
-
RetryRule 根據輪詢的方式重試
-
WeightedResponseTimeRule 根據響應時間去分配一個weight ,weight越低,被選擇的可能性就越低
-
ZoneAvoidanceRule 根據server的zone區域和可用性來輪詢選擇
IPing是用來想server發生"ping",來判斷該server是否有響應,從而判斷該server是否可用。它有一個isAlive()方法,它的原始碼如下:
public interface IPing {
public boolean isAlive(Server server);
}
IPing的實現類有PingUrl、PingConstant、NoOpPing、DummyPing和NIWSDiscoveryPing。它門之間的關係如下:
- PingUrl 真實的去ping 某個url,判斷其是否alive
- PingConstant 固定返回某服務是否可用,預設返回true,即可用
- NoOpPing 不去ping,直接返回true,即可用。
- DummyPing 直接返回true,並實現了initWithNiwsConfig方法。
- NIWSDiscoveryPing,根據DiscoveryEnabledServer的InstanceInfo的InstanceStatus去判斷,如果為InstanceStatus.UP,則為可用,否則不可用。
ServerList是定義獲取所有的server的註冊列表資訊的介面,它的程式碼如下:
public interface ServerList<T extends Server> {
public List<T> getInitialListOfServers();
public List<T> getUpdatedListOfServers();
}
ServerListFilter介面,定於了可根據配置去過濾或者根據特性動態獲取符合條件的server列表的方法,程式碼如下:
public interface ServerListFilter<T extends Server> {
public List<T> getFilteredListOfServers(List<T> servers);
}
閱讀DynamicServerListLoadBalancer的原始碼,DynamicServerListLoadBalancer的建構函式中有個initWithNiwsConfig()方法。在改方法中,經過一系列的初始化配置,最終執行了restOfInit()方法。其程式碼如下:
public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
initWithNiwsConfig(clientConfig);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
try {
super.initWithNiwsConfig(clientConfig);
String niwsServerListClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.NIWSServerListClassName,
DefaultClientConfigImpl.DEFAULT_SEVER_LIST_CLASS);
ServerList<T> niwsServerListImpl = (ServerList<T>) ClientFactory
.instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
this.serverListImpl = niwsServerListImpl;
if (niwsServerListImpl instanceof AbstractServerList) {
AbstractServerListFilter<T> niwsFilter = ((AbstractServerList) niwsServerListImpl)
.getFilterImpl(clientConfig);
niwsFilter.setLoadBalancerStats(getLoadBalancerStats());
this.filter = niwsFilter;
}
String serverListUpdaterClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.ServerListUpdaterClassName,
DefaultClientConfigImpl.DEFAULT_SERVER_LIST_UPDATER_CLASS
);
this.serverListUpdater = (ServerListUpdater) ClientFactory
.instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);
restOfInit(clientConfig);
} catch (Exception e) {
throw new RuntimeException(
"Exception while initializing NIWSDiscoveryLoadBalancer:"
+ clientConfig.getClientName()
+ ", niwsClientConfig:" + clientConfig, e);
}
}
在restOfInit()方法上,有一個 updateListOfServers()的方法,該方法是用來獲取所有的ServerList的。
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
enableAndInitLearnNewServersFeature();
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
進一步跟蹤updateListOfServers()方法的原始碼,最終由serverListImpl.getUpdatedListOfServers()獲取所有的服務列表的,程式碼如下:
@VisibleForTesting
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
if (filter != null) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
}
}
updateAllServerList(servers);
}
而serverListImpl是ServerList介面的具體實現類。跟蹤程式碼,ServerList的實現類為DiscoveryEnabledNIWSServerList,在ribbon-eureka.jar的com.netflix.niws.loadbalancer下。其中DiscoveryEnabledNIWSServerList有 getInitialListOfServers()和getUpdatedListOfServers()方法,具體程式碼如下:
@Override
public List<DiscoveryEnabledServer> getInitialListOfServers(){
return obtainServersViaDiscovery();
}
@Override
public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
return obtainServersViaDiscovery();
}
繼續跟蹤原始碼,obtainServersViaDiscovery(),是根據eurekaClientProvider.get()來回去EurekaClient,再根據EurekaClient來獲取註冊列表資訊,程式碼如下:
private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();
if (eurekaClientProvider == null || eurekaClientProvider.get() == null) {
logger.warn("EurekaClient has not been initialized yet, returning an empty list");
return new ArrayList<DiscoveryEnabledServer>();
}
EurekaClient eurekaClient = eurekaClientProvider.get();
if (vipAddresses!=null){
for (String vipAddress : vipAddresses.split(",")) {
// if targetRegion is null, it will be interpreted as the same region of client
List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
for (InstanceInfo ii : listOfInstanceInfo) {
if (ii.getStatus().equals(InstanceStatus.UP)) {
if(shouldUseOverridePort){
if(logger.isDebugEnabled()){
logger.debug("Overriding port on client name: " + clientName + " to " + overridePort);
}
// copy is necessary since the InstanceInfo builder just uses the original reference,
// and we don't want to corrupt the global eureka copy of the object which may be
// used by other clients in our system
InstanceInfo copy = new InstanceInfo(ii);
if(isSecure){
ii = new InstanceInfo.Builder(copy).setSecurePort(overridePort).build();
}else{
ii = new InstanceInfo.Builder(copy).setPort(overridePort).build();
}
}
DiscoveryEnabledServer des = new DiscoveryEnabledServer(ii, isSecure, shouldUseIpAddr);
des.setZone(DiscoveryClient.getZone(ii));
serverList.add(des);
}
}
if (serverList.size()>0 && prioritizeVipAddressBasedServers){
break; // if the current vipAddress has servers, we dont use subsequent vipAddress based servers
}
}
}
return serverList;
}
其中eurekaClientProvider的實現類是LegacyEurekaClientProvider,它是一個獲取eurekaClient類,通過靜態的方法去獲取eurekaClient,其程式碼如下:
class LegacyEurekaClientProvider implements Provider<EurekaClient> {
private volatile EurekaClient eurekaClient;
@Override
public synchronized EurekaClient get() {
if (eurekaClient == null) {
eurekaClient = DiscoveryManager.getInstance().getDiscoveryClient();
}
return eurekaClient;
}
}
EurekaClient的實現類為DiscoveryClient,在之前已經分析了它具有服務註冊、獲取服務註冊列表等的全部功能。
由此可見,負載均衡器是從EurekaClient獲取服務資訊,並根據IRule去路由,並且根據IPing去判斷服務的可用性。
那麼現在還有個問題,負載均衡器多久一次去獲取一次從Eureka Client獲取註冊資訊呢。
在BaseLoadBalancer類下,BaseLoadBalancer的建構函式,該建構函式開啟了一個PingTask任務,程式碼如下:
public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats,
IPing ping, IPingStrategy pingStrategy) {
...//程式碼省略
setupPingTask();
...//程式碼省略
}
setupPingTask()的具體程式碼邏輯,它開啟了ShutdownEnabledTimer執行PingTask任務,在預設情況下pingIntervalSeconds為10,即每10秒鐘,想EurekaClient傳送一次"ping"。
void setupPingTask() {
if (canSkipPing()) {
return;
}
if (lbTimer != null) {
lbTimer.cancel();
}
lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
true);
lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
forceQuickPing();
}
PingTask原始碼,即new一個Pinger物件,並執行runPinger()方法。
class PingTask extends TimerTask {
public void run() {
try {
new Pinger(pingStrategy).runPinger();
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error pinging", name, e);
}
}
}
檢視Pinger的runPinger()方法,最終根據 pingerStrategy.pingServers(ping, allServers)來獲取服務的可用性,如果該返回結果,如之前相同,則不去向EurekaClient獲取註冊列表,如果不同則通知ServerStatusChangeListener或者changeListeners發生了改變,進行更新或者重新拉取。
public void runPinger() throws Exception {
if (!pingInProgress.compareAndSet(false, true)) {
return; // Ping in progress - nothing to do
}
// we are "in" - we get to Ping
Server[] allServers = null;
boolean[] results = null;
Lock allLock = null;
Lock upLock = null;
try {
/*
* The readLock should be free unless an addServer operation is
* going on...
*/
allLock = allServerLock.readLock();
allLock.lock();
allServers = allServerList.toArray(new Server[allServerList.size()]);
allLock.unlock();
int numCandidates = allServers.length;
results = pingerStrategy.pingServers(ping, allServers);
final List<Server> newUpList = new ArrayList<Server>();
final List<Server> changedServers = new ArrayList<Server>();
for (int i = 0; i < numCandidates; i++) {
boolean isAlive = results[i];
Server svr = allServers[i];
boolean oldIsAlive = svr.isAlive();
svr.setAlive(isAlive);
if (oldIsAlive != isAlive) {
changedServers.add(svr);
logger.debug("LoadBalancer [{}]: Server [{}] status changed to {}",
name, svr.getId(), (isAlive ? "ALIVE" : "DEAD"));
}
if (isAlive) {
newUpList.add(svr);
}
}
upLock = upServerLock.writeLock();
upLock.lock();
upServerList = newUpList;
upLock.unlock();
notifyServerStatusChangeListener(changedServers);
} finally {
pingInProgress.set(false);
}
}
由此可見,LoadBalancerClient是在初始化的時候,會向Eureka回去服務註冊列表,並且向通過10s一次向EurekaClient傳送“ping”,來判斷服務的可用性,如果服務的可用性發生了改變或者服務數量和之前的不一致,則更新或者重新拉取。LoadBalancerClient有了這些服務註冊列表,就可以根據具體的IRule來進行負載均衡。
RestTemplate是如何和Ribbon結合的
最後,回答問題的本質,為什麼在RestTemplate加一個@LoadBalance註解就可可以開啟負載均衡呢?
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
全域性搜尋ctr+shift+f @LoadBalanced有哪些類用到了LoadBalanced有哪些類用到了, 發現LoadBalancerAutoConfiguration類,即LoadBalancer自動配置類。
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
}
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
final List<RestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
}
};
}
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
}
在該類中,首先維護了一個被@LoadBalanced修飾的RestTemplate物件的List,在初始化的過程中,通過呼叫customizer.customize(restTemplate)方法來給RestTemplate增加攔截器LoadBalancerInterceptor。
而LoadBalancerInterceptor,用於實時攔截,在LoadBalancerInterceptor這裡實現來負載均衡。LoadBalancerInterceptor的攔截方法如下:
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
總結
綜上所述,Ribbon的負載均衡,主要通過LoadBalancerClient來實現的,而LoadBalancerClient具體交給了ILoadBalancer來處理,ILoadBalancer通過配置IRule、IPing等資訊,並向EurekaClient獲取註冊列表的資訊,並預設10秒一次向EurekaClient傳送“ping”,進而檢查是否更新服務列表,最後,得到註冊列表後,ILoadBalancer根據IRule的策略進行負載均衡。
而RestTemplate 被@LoadBalance註解後,能過用負載均衡,主要是維護了一個被@LoadBalance註解的RestTemplate列表,並給列表中的RestTemplate新增攔截器,進而交給負載均衡器去處理。
關注我的公眾號
精彩內容不能錯過!
掃碼關注公眾號有驚喜
(轉載本站文章請註明作者和出處 方誌朋的部落格)
相關推薦
深入理解Ribbon之原始碼解析
什麼是Ribbon Ribbon是Netflix公司開源的一個負載均衡的專案,它屬於上述的第二種,是一個客戶端負載均衡器,執行在客戶端上。它是一個經過了雲端測試的IPC庫,可以很好地控制HTTP和TCP客戶端的一些行為。 Feign已經預設使用了Ribbon。
深入理解Feign之原始碼解析
什麼是Feign Feign是受到Retrofit,JAXRS-2.0和WebSocket的影響,它是一個jav的到http客戶端繫結的開源專案。 Feign的主要目標是將Java Http 客戶端變得簡單。Feign的原始碼地址:https://github.com/Op
深入理解Zuul之原始碼解析
Zuul 架構圖 在zuul中, 整個請求的過程是這樣的,首先將請求給zuulservlet處理,zuulservlet中有一個zuulRunner物件,該物件中初始化了RequestContext:作為儲存整個請求的一些資料,並被所有的zuulfilter共享。zuu
深入理解Zuul之原始碼解析(轉載)
轉載請標明出處: http://blog.csdn.net/forezp/article/details/76211680 本文出自方誌朋的部落格 Zuul 架構圖 在zuul中, 整個請求的過程是這樣的,首先將請求給zuulservlet處理
客戶端負載均衡Ribbon之原始碼解析
什麼是負載均衡器? 假設有一個分散式系統,該系統由在不同計算機上執行的許多服務組成。但是,當用戶數量很大時,通常會為服務建立多個副本。每個副本都在另一臺計算機上執行。此時,出現 “Load Balancer(負載均衡器)”。它有助於在伺服器之間平均分配傳入流量。 伺服器端負載均衡器 傳統上,Load Bala
Java併發-深入理解Semaphore(訊號量)之原始碼解析
深入理解Semaphore(訊號量) Semaphore藉助AQS Sync 繼承 AbstractQueuedSynchronizer(AQS同步器) NonfairSync Sync的非公平實現 FairSync Sync的公平實現 為什麼沒有實
深入理解JavaScript之this全面解析
在之前的章節裡我們知道,this 是在函式執行時繫結的,它只與函式在哪被呼叫有關係 1.1 呼叫位置 在理解 this 的繫結之前,我們先理解 this
jQuery深入之原始碼解析(一)
總體架構 可以看出來jQuery主要有三個模組: 入口模組、功能模組、底層支援模組。 - 入口模組 在構造jQuery物件模組中,如果在呼叫建構函式建立jQuery物件時,會呼叫選擇器
delayQueue原理理解之原始碼解析
http://www.jianshu.com/p/e0bcc9eae0ae 內部結構 可重入鎖 用於根據delay時間排序的優先順序佇列 用於優化阻塞通知的執行緒元素leader 用於實現阻塞和通知的Condition物件 delayed和PriorityQueue 在
深入理解"指標"之面試題解析
char str1[] = "abc"; char str2[] = "abc"; const char str3[] = "abc"; const char str4[] = "abc"; const char *str5 = "abc"; const char *str6
深入理解Spring cloud原始碼篇之Eureka原始碼
1.eureka功能分析 首先,eureka在springcloud中充當服務註冊功能,相當於dubbo+zk裡面得zk,但是比zk要簡單得多,zk可以做得東西太多了,包括分散式鎖,分散式佇列都是基於zk裡面得四種節點加watch機制通過長連線來
深入理解python 命令列解析模組optparse(optparse原始碼解讀)
optparse是python用來解析命令列引數的,最早是getopt,option比getopt更強大和靈活。最新的命令列解析使用argparse,因此optparse今後將不會再被開發,optparse在python的原始碼位置是Lib/optparse
帶你深入理解STL之空間配置器(思維導圖+原始碼)
前不久把STL細看了一遍,由於看得太“認真”,忘了做筆記,歸納和總結這步漏掉了。於是為了加深印象,打算重看一遍,並記錄下來裡面的一些實現細節。方便以後能較好的複習它。 以前在專案中運用STL一般都不會涉及到空間配置器,可是,在STL的實現中,空間配置器是重中之
深入理解javascript之設計模式
rip 是我 解決問題 不想 接受 button move center 常識 設計模式 設計模式是命名、抽象和識別對可重用的面向對象設計實用的的通用設計結構。設計模式確定類和他們的實體、他們的角色和協作、還有他們的責任分配。 每個設計模式都聚焦於一個面向對象的設計難題
深入理解javascript之原型
undefine tor ace 對象實例 高級 code turn 三方 true 理解原型 原型是一個對象。其它對象能夠通過它實現屬性繼承。不論什麽一個對象都能夠成為繼承,全部對象在默認的情況下都有一個原型。由於原型本身也是對象,所以每一個原型自身又有一個原型
深入理解Java之線程池
我們 先進先出 tor cor null 討論 等於 影響 log 重要連接:http://www.cnblogs.com/dolphin0520/p/3958019.html 出處:http://www.cnblogs.com/dolphin0520/ 本文歸作者
深入理解JVM之JVM內存區域與內存分配
錯誤 銷毀 構造方法 初學 不存在 data 空閑 table fin 深入理解JVM之JVM內存區域與內存分配 在學習jvm的內存分配的時候,看到的這篇博客,該博客對jvm的內存分配總結的很好,同時也利用jvm的內存模型解釋了java程序中有關參數傳遞的問題。
深入理解SpringCloud之Eureka註冊過程分析
.net then media inject seq tar view inf cas eureka是一種去中心化的服務治理應用,其顯著特點是既可以作為服務端又可以作為服務向自己配置的地址進行註冊。那麽這篇文章就來探討一下eureka的註冊流程。 一、Eureka的服
深入理解系列之 float
子元素 out oat 理解 oom 20px 布局 空格 ble float的設計初衷: 僅僅是為了實現文字環繞效果 float的感性認知: 包裹性: 收縮:元素應用了float後,寬度收縮,緊緊地包裹住內容(即元素的寬度收縮到元素內的內容的寬度大小 堅挺:原來沒有高
深入理解python之二——python列表和元組
n) 數據 兩種 性能 執行 效率 動態 單元 這一 從一開始學習python的時候,很多人就聽到的是元組和列表差不多,區別就是元組不可以改變,列表可以改變。 從數據結構來說,這兩者都應當屬於數組,元組屬於靜態的數組,而列表屬於動態數組。稍後再內存的分配上也會體現這一點。對