手持10萬元 到底現在購車還是過年前購車優惠多?_網頁設計

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

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

99萬)錢,再怎麼說跟4s優惠硬是差了1萬塊,也特別不想分期供車覺得錢被銀行賺了,所以想着年底把年終獎拿到了手后再買,但很怕過年時購車大軍會把優惠壓沒了,所以現在特別糾結。觀點分析:金九銀十除了描述房價之外,對於汽車同樣適用。

前言

2016年過去大半,還有兩個月,就要迎來全新的2017年,很多還沒有買車的朋友,肯定會在這段時間糾結一件事:究竟是趁着現在優惠更多把車買了練手幾個月等過年呢,亦或是等着11月廣州車展上市有更多可選新車?

觀點一:據說11月新上市車更多

黃小姐近期一直很想買一台代步小車,年頭的時候其實已經相中了豐田致炫,覺得空間夠用也容易好開,當時因為忙所以剛好沒空去跑4s。今年7月份以來居然收到了致炫出了新車的消息。跟她年頭一起買車的朋友都在後悔不已:早知道新致炫帶了CVT就等等再買啦,現在剛買的車就變老車了好失望,因為這點,黃小姐現在很糾結,想再多等等持幣觀望。

觀點分析:其實一般車企對新車更新速度都保持在1-2年左右,所以不必太過擔心“剛買的車又變舊了”。加之目前很多車企都用家族化前臉設計,所以即使半年度/年度小改款,車子外觀差異也並不明顯。像黃小姐的致炫改款情況,一般很少發生。

致炫改款前後外觀差距不大,單就一項變速箱從4AT變成CVT已經讓很多已購買老款的車主後悔不已。

觀點二:想等年終獎預算更多買好車

小李是公司業務員,每天都要來回跑不少地方,所以想買一台省油耐用有面子的車,於是想買新邁騰,想買的330tsi豪華型的配置(官方報價23.49萬)但僅有330tsi舒適型(官方報價20.99萬)錢,再怎麼說跟4s優惠硬是差了1萬塊,也特別不想分期供車覺得錢被銀行賺了,所以想着年底把年終獎拿到了手后再買,

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

但很怕過年時購車大軍會把優惠壓沒了,所以現在特別糾結。

觀點分析:金九銀十除了描述房價之外,對於汽車同樣適用。目前10月份正是優惠的大好時機,如果有像小李這樣的購車朋友,建議選用分期或是問朋友借些錢購車,畢竟很多4s店會聯手銀行減免分期利息,最多交些手續費就能把車開走,早用早享受不好嗎?還有消息指出,購置稅減半將在2016年12月31日結束,如果你意向車型是1.6升及以下排量車型,這些優惠就沒了哦。

2017款邁騰外觀好看,其實舒適型和豪華型價差2.5萬配置還是差距甚大的,這從內飾上就能看出端倪。

觀點三:手上有錢近期優惠大

陳先生存了大半年,加上前幾年存下來的錢,手上已經積攢了快10萬元,近期一直在看各種車型優惠,曾經對比過5月初的優惠,已經很值得入手了,可是一轉念想着金九銀十是熱銷月份,車企會不會在冷門月份例如12月份、1月份做優惠呢?所以陳先生現在特別糾結。

觀點分析:汽車銷售其實熱門促銷時比冷門促銷時車型更多,優惠也更大,大家都知道旺季是那個時候,所以那幾個月的銷量任務也會更重,甚至有4s會為了旺季臨時加緊和二手車商合作推出快速置換車計劃,也會在熱銷幾個月多招臨時促銷員,熱銷期的店內活動也會更多更火爆,所有的一切,都證明旺季有更豐富的資源,所以你能獲得的也會更多。

買車總之一句話,別糾結別猶豫,看中了就趕緊下手,早買早享受!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

2020年全國鄉村春晚聯動“百縣萬村”_台中搬家公司

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

2019年12月26日,2020年全國鄉村春晚集中展示活動暨麗水鄉村春晚建設成果新聞發布會在北京召開。活動由文化和旅遊部公共服務司、全國公共文化發展中心、浙江省文化和旅遊廳和浙江省麗水市政府主辦。2020年全國鄉村春晚集中展示活動啟動儀式將於2020年1月8日在浙江麗水舉辦。屆時,以麗水為主會場,安徽省、四川省、廣東省、河南省等8省區同步啟動鄉村春晚活動。

此次活動將通過線下鄉村春晚年俗展示、線上互聯網科技互動形式,拉開2020年全國鄉村春晚“百縣萬村”區域聯動大幕。此外,全國鄉村“鬧”春晚展演展示活動也將在全國篩選最具區域特色的節目,到麗水主會場以斗藝和吆喝的形式,集中展示全國鄉村藝術普及成果。

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

在麗水,活動主要以鄉村春晚品牌建設為主導,同步舉辦“麗水味道”年味集市、“麗水山耕”年貨集市、“僑鄉中國年”等十大特色活動,形成文、農、旅大融合,通過網絡直播、主會場與分會場聯動、線上線下互動,全面展現社會發展成就。

據介紹,鄉村春晚起源於1981年由麗水市慶元縣舉水鄉月山村农民自編、自導、自演的“月山春晚”,比央視春晚還早兩年,至今已有30多年的歷史。經過幾十年的發展,麗水鄉村春晚點面結合、遍地開花,成為當地重要的公共文化品牌。(記者 杜潔芳)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

讀懂時代,從讀懂語言開始_網頁設計公司

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

時節如流,歲月不居。年終歲末,不同機構相繼發布年度漢字、漢語盤點,國家語言資源監測與研究中心發布了中國媒體十大流行語:我和我的祖國、金色十年、學習強國、中美經貿磋商、最美奮鬥者、硬核、垃圾分類、先行示範區、基層減負年、我太難了。《咬文嚼字》編輯部、《語言文字周報》等機構公布的年度流行語與之有重合也有不同。

年度流行語盤點,既是語言文字研究領域的一件盛事,也是整個文化界的一樁“雅事”,而且被大眾看成是觀察中國社會、洞悉世道人心的一扇窗口。因此,年復一年,如期而至,廣受關注。今年的年度流行語形態各異,既有“我和我的祖國”“最美奮鬥者”“硬核”等與時代大局大勢同頻共振的“鐘鼓之音”,也有諸如“我太難了”“好嗨呦”之類,反映社會風尚、大眾心態、百姓心聲的“網言網語”。“大珠小珠落玉盤”,盡顯漢語言文字的豐富性。

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

過去的2019年,我們為共和國70年的輝煌成就喝彩,愛國主義情感讓我們熱淚盈眶,“我和我的祖國”在大江南北唱響。我們感動於千千萬萬奮鬥者“擼起袖子加油干”的蓬勃朝氣,也為基層幹部切實減輕了負擔鬆了一口氣;我們為偉大祖國的欣欣向榮而驕傲,也每每因工作的壓力、生活的不易感到焦慮……不論社會如何變化,總有一些與每個人息息相關的國家大事、社會大潮引領着我們的注意力,總有一些標誌性的人和事讓我們產生情感的交集;總有一些共同的情感把我們的心凝聚在一起。也因此,在人口基數如此龐大的中國,每年總有一些被普遍認同的年度流行語。

過去的2019年,我們的社交方式、信息獲取方式乃至生活方式,都在發生更深刻改變。人們在為奮鬥者喝彩的同時,也對所謂的“996工作制”產生質疑;既熱衷於以“X千萬條,Y第一條”造句,也在類似“我不要你覺得,我要我覺得”的表達中展現着自我意識的增強、個體個性的鮮明;盤他、雨女無瓜、檸檬精、斷舍離等等流行語,讓很多人感到摸不着頭腦,一些人卻高度認同、非常默契,這提示我們,分眾化的社交趨勢更加明顯,不同社交圈的人往往有着全然不同的文化生態和話語體系。如果把這種巨大的差異性放在70年歲月變遷的歷史長河中去觀察,我們就會看到,這種千差萬別、百花齊放、形態各異,展現的正是生活選擇的自由、社會文化的多元、人的個性的舒展。“和而不同,各美其美”是社會繁榮進步的體現。

語言是時代的風向標,讀懂時代,當從讀懂語言開始。十多年來,每至歲末,一串串閃現在語言文字大潮中的流行語被“打撈”出來。這種“打撈”,實際上是對國家大勢、世界風雲、民生實事、社會熱點的“打撈”,是對時代變化、社會變遷的“打撈”。一個個時代流行語鋪展在面前,嘴角輕揚,看似波瀾不驚地咂摸間,往往蘊含着人們對自身生活、社會變革與人類發展的深長思考,往往寄予着擁抱更美好生活、更美好時代的蓬勃進取心。“只爭朝夕,不負韶華”,新年已至,讓我們整裝出發。(作者:李思輝,系華中科技大學新聞評論研究中心特聘研究員)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

恢復古城風貌 傳承傳統文化_租車

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

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

本報石家莊1月8日電  (記者張志鋒)“登得上城樓,望得見古塔,記得住鄉愁。”新年伊始,河北正定東城門、府文廟、府城隍廟和正定博物館正式開放。歷時三年,正定縣24項古城風貌恢復提升工程全部完成。據了解,2019年正定縣旅遊接待預計突破1471萬人次,同比增長逾16.7%。

正定是國家歷史文化名城,有1600多年的建城史,縣城有“九樓四塔八大寺,二十四座金牌坊”,全縣有全國重點文物保護單位9處、河北省文物保護單位6處,各類館藏文物7672件。2017年1月,當地啟動24項古城風貌恢復提升工程。

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

提升工程主要包括:修復加固南城門系統和南部城牆5800米;在北城門建成遺址公園,修建東城門主城及瓮城,恢復護城河;原址復建“鎮府巨觀”陽和樓,展示正定傳統文化等。

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

曹工說mini-dubbo(2)–分析eureka client源碼,想辦法把我們的服務提供者註冊到eureka server(上)_網頁設計

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

前言

eureka是spring cloud Netflix技術體系中的重要組件,主要完成服務註冊和發現的功能;那現在有個問題,我們自己寫的rpc服務,如果為了保證足夠的開放性和功能完善性,那肯定要支持各種註冊中心。目前我們只支持redis註冊中心,即服務提供者,在啟動的時候,將自身的ip+端口信息寫入到redis,那,我們是否註冊到 eureka中呢?

這個想法可行嗎?可行。eureka client 和eureka server間,無非是網絡通信,既然是網絡通信,那就有網絡協議,那我們的應用,只要遵照eureka server的協議來,就可以接入。

另外,eureka server沒有採用spring mvc來實現,而是採用了jersey框架,這個框架啥意思呢,可以理解為對Restful的實現。我從網上找了一段(https://www.jianshu.com/p/88f97b90963c):

SpringMVC在開發REST應用時,是不支持JSR311/JSR339標準的。如果想要按照標準行事,最常用的實現了這兩個標準的框架就是Jersey和CxF了。但是,因為Jersey是最早的實現,也是JSR311參考的主要對象,所以,可以說Jersey就是事實上的標準(類似Hibernate是JPA的事實上的標準),也是現在使用最為廣泛的REST開發框架之一。

因為eureka server採用了jersey,所以eureka client最終也是使用了配套的jersey client來和服務端通信。

所以,eureka client,裏面其實依賴了一堆jersey的包:

注意,上面的jersey-client、jersey-core等包,其group id都是這樣的:

    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-client</artifactId>
      <version>1.19.1</version>
      <scope>runtime</scope>
    </dependency>

但是,不知道為啥,eureka client中,最終並沒有完全使用jersey-client,而是使用了

    <dependency>
      <groupId>com.sun.jersey.contribs</groupId>
      <artifactId>jersey-apache-client4</artifactId>
      <version>1.19.1</version>
      <scope>runtime</scope>
    </dependency>

這個包,內部引入了:

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.1.1</version>
        </dependency>

這個包,你可以簡單理解為,jersey-client變成了一個接口,jersey-apache-client4是它的一個實現,實現里,用了httpClient去實現。

httpClient,沒有幾個java同學不知道吧?這麼做是可行的,因為你最終通信,還是http,不管你服務端框架,是jersey、還是spring mvc、甚至以前的struts,這都不重要。

所以,大家在下面的源碼中看到jersey的時候,腦海里可以有這麼一張圖。從上層到底層的接口,分別是:

CloudEurekaClient
       ...
DiscoveryClient  
	...
EurekaClient
	...
JerseyClient
	...
HttpClient	

在此之前,我們還是先分析下eureka client 註冊到eureka server的源碼。

源碼環境

minidubbo代碼和相關博文在:
曹工說mini-dubbo(1)–為了實踐動態代理,我寫了個簡單的rpc框架
https://gitee.com/ckl111/mini-dubbo

代碼很簡單,不過還是給個代碼鏈接吧:

https://gitee.com/ckl111/all-simple-demo-in-work-1/tree/master/eureka-client

主要就是在pom.xml中,引入:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

然後啟動類:

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

源碼分析

spring.factory支持自動配置

因為前面的pom,引入了如下jar包:

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-eureka-client</artifactId>
		</dependency>

該jar包的META-INF\spring.factories中,有如下幾行:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

我們看到,key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,value是逗號分割的列表,這裏面都是需要被自動裝配的配置類,其中,我們看第三行的:

org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration

這個類,是自動裝配的配置類,我們可以簡單一覽:

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
		CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
public class EurekaClientAutoConfiguration {

裏面一堆@ConditionalOn***,主要是看該配置類是否生效。

我們不管,這裏條件是滿足的,所以,看具體java文件里有什麼要裝配的內容,裏面內容較多,我們關注我們需要關注的:

		@Bean(destroyMethod = "shutdown")
		@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
		@Lazy
		public EurekaClient eurekaClient(ApplicationInfoManager manager,
				EurekaClientConfig config, EurekaInstanceConfig instance,
				@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
			ApplicationInfoManager appManager;
			if (AopUtils.isAopProxy(manager)) {
				appManager = ProxyUtils.getTargetObject(manager);
			}
			else {
				appManager = manager;
			}
            // 1
			CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,
					config, this.optionalArgs, this.context);
			cloudEurekaClient.registerHealthCheck(healthCheckHandler);
			return cloudEurekaClient;
		}

這裡會自動裝配一個EurekaClient類型的bean,(從返回值可以看出來),而具體的類型呢,從上面的1處,可以看出,具體類型是CloudEurekaClient。

所以,我們開始看1處,這個CloudEurekaClient是怎麼new出來的。

CloudEurekaClient的創建

先看看其繼承結構:

我們這個CloudEurekaClient,位於spring-cloud-netflix-eureka-client-2.1.5.RELEASE包。

而其父類DiscoveryClient和接口EurekaClient,位於eureka-client-1.9.13

大致能分析出,CloudEurekaClient的底層實現是eureka,其本身,是一個膠水,集成 spring 和 Netflix。

CloudEurekaClient的構造函數

	public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
			EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs<?> args,
			ApplicationEventPublisher publisher) {
        // 1
		super(applicationInfoManager, config, args);
        // 2
		this.applicationInfoManager = applicationInfoManager;
		this.publisher = publisher;
		this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class,
				"eurekaTransport");
		ReflectionUtils.makeAccessible(this.eurekaTransportField);
	}

我們看1處,調用了父類的構造函數;2處下面的幾行,主要是對本類中的幾個field進行賦值,這幾個字段,我們不關心,所以,直接看父類的構造函數吧。

DiscoveryClient的構造函數

    public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) {
        this(applicationInfoManager, config, args, ResolverUtils::randomize);
    }

    public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, EndpointRandomizer randomizer) {
        // 1
        this(applicationInfoManager, config, args, null, randomizer);
    }

上面兩個,都是重載。1處調用的,我們接下來會重點分析。

步驟1:一堆field賦值

DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
                    Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
    	// 0	
        if (args != null) {
            this.healthCheckHandlerProvider = args.healthCheckHandlerProvider;
            this.healthCheckCallbackProvider = args.healthCheckCallbackProvider;
            this.eventListeners.addAll(args.getEventListeners());
            this.preRegistrationHandler = args.preRegistrationHandler;
        } else {
            this.healthCheckCallbackProvider = null;
            this.healthCheckHandlerProvider = null;
            this.preRegistrationHandler = null;
        }
        
        this.applicationInfoManager = applicationInfoManager;
        InstanceInfo myInfo = applicationInfoManager.getInfo();
		// 1
        clientConfig = config;
        staticClientConfig = clientConfig;
        transportConfig = config.getTransportConfig();
        instanceInfo = myInfo;
        if (myInfo != null) {
            appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();
        } else {
            logger.warn("Setting instanceInfo to a passed in null value");
        }
		...

這一堆都是根據入數,來對類中的field進行賦值。比如

0處,主要是一些健康檢查的東西;1處,config類型為 com.netflix.discovery.EurekaClientConfig,這裏主要是eureka client的一些配置,比如我們在yml中配置了eureka.client.*之類的,就會到這裏。

步驟2:判斷是否要獲取eureka server中的服務提供者信息

	// 1
	if (config.shouldFetchRegistry()) {
            this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

1處,可以看出來,是根據config中的shouldFetchRegistry進行判斷,是否要去獲取eureka server。

然後進行了一些監控指標的初始化。

步驟3:判斷是否要註冊到eureka server

    	// 1    
		if (config.shouldRegisterWithEureka()) {
            this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

同上。

步驟4:如果既不註冊,也不獲取,則處理基本結束

		// 1
		if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
            logger.info("Client configured to neither register nor query for data.");
            scheduler = null;
            heartbeatExecutor = null;
            cacheRefreshExecutor = null;
            eurekaTransport = null;
            instanceRegionChecker = new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config), clientConfig.getRegion());

            DiscoveryManager.getInstance().setDiscoveryClient(this);
            DiscoveryManager.getInstance().setEurekaClientConfig(config);
			// 2
            return;  // no need to setup up an network tasks and we are done
        }
  • 1處,既不註冊,也不從eureka server獲取
  • 2處,直接結束

步驟5:定義三個線程池

            //1 default size of 2 - 1 each for heartbeat and cacheRefresh
            scheduler = Executors.newScheduledThreadPool(2,
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-%d")
                            .setDaemon(true)
                            .build());
			// 2
            heartbeatExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff
			// 3 
			cacheRefreshExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff
  • 1處,定義一個用於服務提供者信息的緩存刷新的定時線程池
  • 2處,定義一個心跳線程池
  • 3處,這個看起來也是用於緩存刷新的

步驟6:創建eurekaTransport對象

com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
// 1
eurekaTransport = new EurekaTransport();
// 2
scheduleServerEndpointTask(eurekaTransport, args);
  • 1處,eurekaTransport是一個field,該類主要封裝了幾個後續通信要使用的底層client。

    
        private static final class EurekaTransport {
            private ClosableResolver bootstrapResolver;
            private TransportClientFactory transportClientFactory;
    		// 1.1
            private EurekaHttpClient registrationClient;
            private EurekaHttpClientFactory registrationClientFactory;
    		// 1.2
            private EurekaHttpClient queryClient;
            private EurekaHttpClientFactory queryClientFactory;
    

    1.1處,這個應該是註冊用的,也是我們需要的;

    1.2處,應該是查詢信息用的。

  • 調用了當前類的方法scheduleServerEndpointTask,且把eurekaTransport傳入了

步驟7:schedule周期任務

創建抽象工廠

因我們只是new了eurekaTransport,沒有對其field進行任何賦值,所以,這個scheduleServerEndpointTask總,有個地方對其field進行賦值。

com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
// 1
TransportClientFactories transportClientFactories =new Jersey1TransportClientFactories();

// 2
eurekaTransport.transportClientFactory = transportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo(), sslContext, hostnameVerifier)

  • 1處,就是new了一個抽象工廠,抽象工廠,我個人理解是工廠的工廠,其產出的東西,不是直接的最終對象,而是另一種工廠。

    TransportClientFactories 是一個接口,主要包含了如下方法:

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

    擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

    	public TransportClientFactory newTransportClientFactory(
            	final EurekaClientConfig clientConfig,
            	final Collection<F> additionalFilters,
                final InstanceInfo myInstanceInfo,
                final Optional<SSLContext> sslContext,
                final Optional<HostnameVerifier> hostnameVerifier);
    

    主要5個參數,排除掉最後的倒數2個,可選參數,剩3個。分別是:eurekaClient的配置bean,額外的filter集合,當前實例信息。

具體工廠的職責

  • 2處,就是利用1處創建的抽象工廠,來生成我們需要的工廠。

    這裏,我們可以先看看,最終我們需要的工廠,是什麼樣的。

    /**
     * A low level client factory interface. Not advised to be used by top level consumers.
     *
     * @author David Liu
     */
    public interface TransportClientFactory {
    
        EurekaHttpClient newClient(EurekaEndpoint serviceUrl);
    
        void shutdown();
    
    }
    

    newClient這個方法,聽名字,就是一個創建客戶端的,創建客戶端,需要什麼參數呢?總得知道要連接到哪個eureka server服務器吧,服務器地址是啥吧?沒錯,參數EurekaEndpoint serviceUrl可以給我們提供需要的這些:

    package com.netflix.discovery.shared.resolver;
    
    public interface EurekaEndpoint extends Comparable<Object> {
    	// 1
        String getServiceUrl();
    	// 2
        String getNetworkAddress();
    	// 3
        int getPort();
    
        boolean isSecure();
    
        String getRelativeUri();
    
    }
    
    
    • 1處,獲取url
    • 2處,獲取網絡地址
    • 3處,獲取端口

    基本對於我們一個客戶端來說,需要的參數就這些。

    說完了newClient的參數,再來看看響應:

    
    /**
     * Low level Eureka HTTP client API.
     *
     * @author Tomasz Bak
     */
    public interface EurekaHttpClient {
    
        EurekaHttpResponse<Void> register(InstanceInfo info);
    
        EurekaHttpResponse<Void> cancel(String appName, String id);
    
        EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus);
    
        EurekaHttpResponse<Void> statusUpdate(String appName, String id, InstanceStatus newStatus, InstanceInfo info);
    
        EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id, InstanceInfo info);
    
        EurekaHttpResponse<Applications> getApplications(String... regions);
    
        EurekaHttpResponse<Applications> getDelta(String... regions);
    
        EurekaHttpResponse<Applications> getVip(String vipAddress, String... regions);
    
        EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id);
    
        EurekaHttpResponse<InstanceInfo> getInstance(String id);
    
        void shutdown();
    }
    

    看到了嗎,各種註冊、取消、發送心跳、狀態更新啥的,這幾本涵蓋了eureka client的所有操作了,沒錯,我們就是需要這麼個東西。

創建具體工廠

看完了我們需要的工廠的功能,我們馬上來看看這麼厲害的工廠怎麼創建出來?

com.netflix.discovery.shared.transport.jersey.Jersey1TransportClientFactories#newTransportClientFactory(...)
        
	@Override
    public TransportClientFactory newTransportClientFactory(
        	EurekaClientConfig clientConfig,
            Collection<ClientFilter> additionalFilters,
        	InstanceInfo myInstanceInfo,
        	Optional<SSLContext> sslContext,
        	Optional<HostnameVerifier> hostnameVerifier) {
    	// 2.1
        final TransportClientFactory jerseyFactory = JerseyEurekaHttpClientFactory.create(
                clientConfig,
                additionalFilters,
                myInstanceInfo,
                new EurekaClientIdentity(myInstanceInfo.getIPAddr()),
                sslContext,
                hostnameVerifier
        );
        // 2.2
        final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);
		// 2.3
        return new TransportClientFactory() {
            @Override
            public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
                return metricsFactory.newClient(serviceUrl);
            }

            @Override
            public void shutdown() {
                metricsFactory.shutdown();
                jerseyFactory.shutdown();
            }
        };
    }
  • 2.1處,調用JerseyEurekaHttpClientFactory的create 靜態方法,生成了一個工廠
  • 2.2處,對生成的工廠,進行了包裝,看名稱,應該是包裝了統計相關信息。
  • 2.3處,對2.2處生成的工廠,用匿名內部類進行了包裝,調用匿名內部類的newClient時,直接代理給了metricsFactory;而shutdown方法,則主要是關閉 metricsFactory 和 jerseyFactory 工廠。

所以,我們現在要看看,2.1處,是怎麼創建工廠的。

com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory#create
    
    public static JerseyEurekaHttpClientFactory create(
    	EurekaClientConfig clientConfig,
        Collection<ClientFilter> additionalFilters,
    	InstanceInfo myInstanceInfo,                                                       		 AbstractEurekaIdentity clientIdentity) {
        // 1
    	boolean useExperimental = "true".equals(clientConfig.getExperimental("JerseyEurekaHttpClientFactory.useNewBuilder"));
		// 2
        JerseyEurekaHttpClientFactoryBuilder clientBuilder = (useExperimental ? experimentalBuilder() : newBuilder())
                .withAdditionalFilters(additionalFilters)
                .withMyInstanceInfo(myInstanceInfo)
                .withUserAgent("Java-EurekaClient")
                .withClientConfig(clientConfig)
                .withClientIdentity(clientIdentity);
    	// 3
        clientBuilder.withClientName("DiscoveryClient-HTTPClient");
		// 4
        return clientBuilder.build();
    }
  • 1處,砍斷是否要使用實驗性的builder
  • 2處,創建對應的builder,並把我們的參數,通過with*方法,設置進去
  • 3處,設置客戶端名稱
  • 4處,生成客戶端工廠
com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory.JerseyEurekaHttpClientFactoryBuilder#build

    
        @Override
        public JerseyEurekaHttpClientFactory build() {
    		// 1
            Map<String, String> additionalHeaders = new HashMap<>();
    		// 2
            if (allowRedirect) {
                additionalHeaders.put(HTTP_X_DISCOVERY_ALLOW_REDIRECT, "true");
            }
            if (EurekaAccept.compact == eurekaAccept) {
                additionalHeaders.put(EurekaAccept.HTTP_X_EUREKA_ACCEPT, eurekaAccept.name());
            }
			
            // 3
            return buildLegacy(additionalHeaders, systemSSL);
        }

這裏就是弄了個hashmap,設置了幾個header進去,然後3處,調用buildLegacy。

com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory.JerseyEurekaHttpClientFactoryBuilder#buildLegacy
    
        private JerseyEurekaHttpClientFactory buildLegacy(Map<String, String> additionalHeaders, boolean systemSSL) {
    		// 1
            EurekaJerseyClientBuilder clientBuilder = new EurekaJerseyClientBuilder()
                    .withClientName(clientName)
                    .withUserAgent("Java-EurekaClient")
                    .withConnectionTimeout(connectionTimeout)
                    .withReadTimeout(readTimeout)
                    .withMaxConnectionsPerHost(maxConnectionsPerHost)
                    .withMaxTotalConnections(maxTotalConnections)
                    .withConnectionIdleTimeout((int) connectionIdleTimeout)
                    .withEncoderWrapper(encoderWrapper)
                    .withDecoderWrapper(decoderWrapper);
			...
            
			// 2
            EurekaJerseyClient jerseyClient = clientBuilder.build();
    		// 3
            ApacheHttpClient4 discoveryApacheClient = jerseyClient.getClient();
            addFilters(discoveryApacheClient);
			// 4
            return new JerseyEurekaHttpClientFactory(jerseyClient, additionalHeaders);
        }
  • 1處,通過我們傳入的一些參數,以及該類自身的一些field,比如connectionTimeout、readTimeout、maxTotalConnections、maxConnectionsPerHost這些,構造一個builder。

    這些參數,已經看出來,是網絡通信所需要的東西了

  • 2處,通過1處的builder,調用build,拿到了EurekaJerseyClient類型的對象,可以說,這裏其實是已經把客戶端構造好了。也就是說,在構造這個工廠的過程中,其實已經在生成對應的產品了

  • 3處,對2處拿到的客戶端,做一些處理

  • 4處,將2處拿到的客戶端,封裝到了工廠的一些field中,後續調用工廠生產產品的時候,直接從field中取就行了。

        public JerseyEurekaHttpClientFactory(EurekaJerseyClient jerseyClient, Map<String, String> additionalHeaders) {
            this(jerseyClient, null, -1, additionalHeaders);
        }
    	private JerseyEurekaHttpClientFactory(EurekaJerseyClient jerseyClient,
                                              ApacheHttpClient4 apacheClient,
                                              long connectionIdleTimeout,
                                              Map<String, String> additionalHeaders) {
            this.jerseyClient = jerseyClient;
            this.apacheClient = jerseyClient != null ? jerseyClient.getClient() : apacheClient;
            this.additionalHeaders = additionalHeaders;
        }
    

所以,我們的重點,要放在2處的build身上。

	com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl.EurekaJerseyClientBuilder#build
	public EurekaJerseyClient build() {
            MyDefaultApacheHttpClient4Config config = new MyDefaultApacheHttpClient4Config();
            try {
                // 1
                return new EurekaJerseyClientImpl(connectionTimeout, readTimeout, connectionIdleTimeout, config);
            } catch (Throwable e) {
                throw new RuntimeException("Cannot create Jersey client ", e);
            }
        }

接下來看1處:

public EurekaJerseyClientImpl(int connectionTimeout, int readTimeout, final int connectionIdleTimeout,ClientConfig clientConfig) {
        try {
            jerseyClientConfig = clientConfig;
            // 1
            apacheHttpClient = ApacheHttpClient4.create(jerseyClientConfig);
            // 2
            HttpParams params = apacheHttpClient.getClientHandler().getHttpClient().getParams();

            HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
            HttpConnectionParams.setSoTimeout(params, readTimeout);
			
        } catch (Throwable e) {
            throw new RuntimeException("Cannot create Jersey client", e);
        }
    }
  • 1處,創建com.sun.jersey.client.apache4.ApacheHttpClient4類型的對象

    該類型,就位於:

        <dependency>
          <groupId>com.sun.jersey.contribs</groupId>
          <artifactId>jersey-apache-client4</artifactId>
          <version>1.19.1</version>
          <scope>runtime</scope>
        </dependency>
    
    
        public static ApacheHttpClient4 create(final ClientConfig cc) {
            return new ApacheHttpClient4(createDefaultClientHandler(cc), cc);
        }
    

    這裏的createDefaultClientHandler(cc),裏面會去創建HttpClient。

    private static ApacheHttpClient4Handler createDefaultClientHandler(final ClientConfig cc) {
    		...
    
    		// 1
            final DefaultHttpClient client = new DefaultHttpClient(
                    (ClientConnectionManager)connectionManager,
                    (HttpParams)httpParams
            );
    
            ...
    		
            return new ApacheHttpClient4Handler(client, cookieStore, preemptiveBasicAuth);
        }
    

    這裏面細節省略了部分,主要就是1處,創建了HttpClient,這個就是平時我們用來發http請求的那個。

  • 2處,設置一些參數,這裏的HttpParams,從哪兒取出來的?apacheHttpClient.getClientHandler().getHttpClient()。這裏取到的,已經是HttpClient了。

    到此為止,我們可以看看httpParams中有哪些header:

在具體工廠基礎上,對註冊用的工廠進行封裝

        com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
        // 1    
		if (clientConfig.shouldRegisterWithEureka()) {
            EurekaHttpClientFactory newRegistrationClientFactory = null;
            EurekaHttpClient newRegistrationClient = null;
            // 2
            newRegistrationClientFactory = EurekaHttpClients.registrationClientFactory(
                eurekaTransport.bootstrapResolver,
                eurekaTransport.transportClientFactory,
                transportConfig
            );
            // 3
            newRegistrationClient = newRegistrationClientFactory.newClient();
            // 4
            eurekaTransport.registrationClientFactory = newRegistrationClientFactory;
            eurekaTransport.registrationClient = newRegistrationClient;
        }

我們前面的n步,已經把通信用的客戶端,及對應的工廠,都已經創建出來了,為啥這裏又要創建什麼工廠。

簡單來說,前面的工廠,造出來的客戶端,通信是沒問題了;但是,你通信失敗了,要重試嗎,重試的話,換哪一台呢?你每次通信是成功,還是失敗,還是超時,需要統計嗎?一個生產級的框架,是要有這些功能的。

所以,這裏主要是進行一些上層的封裝。

ok,繼續分析上面的代碼。

  • 1處,判斷是否要註冊到eureka
  • 2處,生成一個工廠,該工廠負責生產:註冊用的客戶端
  • 3處,使用2處拿到的工廠,創建註冊用的客戶端
  • 4處,把3處拿到的客戶端,存儲到eurekaTransport的field中。

繼續深入2處。

    com.netflix.discovery.shared.transport.EurekaHttpClients#canonicalClientFactory
	static EurekaHttpClientFactory canonicalClientFactory(
        final String name,
        final EurekaTransportConfig transportConfig,
        final ClusterResolver<EurekaEndpoint> clusterResolver,
        final TransportClientFactory transportClientFactory) {
		// 1
        return new EurekaHttpClientFactory() {
            // 2
            @Override
            public EurekaHttpClient newClient() {
                // 3
                return new SessionedEurekaHttpClient(
                        name,
                        RetryableEurekaHttpClient.createFactory(...),
                        transportConfig.getSessionedClientReconnectIntervalSeconds() * 1000
                );
            }

            @Override
            public void shutdown() {
                wrapClosable(clusterResolver).shutdown();
            }
        };
    }
  • 1處,返回了一個工廠對象
  • 2處,工廠里重寫了newClient
  • 3處,返回了一個包裝過的EurekaClient。

可以看下這裏返回的SessionedEurekaHttpClient類。

這裏就是裝飾器模式,對enreka進行了層層封裝,和 java 的 io 流那樣理解就對了。

在具體工廠基礎上,對查詢用的工廠進行封裝

		// 1
		if (clientConfig.shouldFetchRegistry()) {
            EurekaHttpClientFactory newQueryClientFactory = null;
            EurekaHttpClient newQueryClient = null;
            // 2
            newQueryClientFactory = EurekaHttpClients.queryClientFactory(
                eurekaTransport.bootstrapResolver,
                eurekaTransport.transportClientFactory,
                clientConfig,
                transportConfig,
                applicationInfoManager.getInfo(),
                applicationsSource,
                endpointRandomizer
            );
            // 3
            newQueryClient = newQueryClientFactory.newClient();
            eurekaTransport.queryClientFactory = newQueryClientFactory;
            eurekaTransport.queryClient = newQueryClient;
        }

這裏的代碼,和上面基本相似。只不過,這裡是給查詢用的,所謂查詢,就是去eureka server獲取信息,比如服務提供者列表啥的。

  • 1處,判斷是否要去eureka server獲取
  • 2處,創建查詢用的工廠
  • 3處,利用2處拿到的工廠,創建查詢客戶端

步驟8:去eureka server獲取服務提供者信息

我們終於把步驟7講完了,實在有點長。

com.netflix.discovery.DiscoveryClient#DiscoveryClient(...)
    
// 1    
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
    // 2
    fetchRegistryFromBackup();
}

這裏1處,就是判斷要不要去獲取,如果要的話,就調用fetchRegistry(false)

2處,如果1處沒取到,則要從backup地方去取。這塊可以自己定製backup策略。

註冊到eureka server

        if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
            // 1
            if (!register() ) {
                throw new IllegalStateException("Registration error at startup. Invalid server response.");
            }
        }

這裡會判斷是否要註冊,是否要在初始化的時候註冊,如果要的話,進入1處,進行註冊。

初始化周期執行的任務

        // finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
        initScheduledTasks();

看這裏註釋,初始化的任務包括:集群解析、心跳、實例信息註冊、周期從eureka server獲取信息等。

周期任務:獲取服務提供者信息

if (clientConfig.shouldFetchRegistry()) {
            // registry cache refresh timer
            int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
            int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "cacheRefresh",
                            scheduler,
                            cacheRefreshExecutor,
                            registryFetchIntervalSeconds,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new CacheRefreshThread()
                    ),
                    registryFetchIntervalSeconds, TimeUnit.SECONDS);
        }

默認30s一次。

周期任務:定時發心跳,向eureka server進行renew

            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);

            // Heartbeat timer
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);

這個也是30s。

心跳包,基本就是個put請求,裏面攜帶了2個參數。

@Override
    public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) {
        String urlPath = "apps/" + appName + '/' + id;
        ClientResponse response = null;
        try {
            WebResource webResource = jerseyClient.resource(serviceUrl)
                    .path(urlPath)
                    .queryParam("status", info.getStatus().toString())
                    .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());

周期任務:InstanceInfoReplicator

這個任務,默認也是30s執行一次。

            instanceInfoReplicator = new InstanceInfoReplicator(
                    this,
                    instanceInfo,
                    clientConfig.getInstanceInfoReplicationIntervalSeconds(),
                    2); // burstSize

這個任務,其實現了runnable,註釋如下:


/**
 * A task for updating and replicating the local instanceinfo to the remote server. Properties of this task are:
 * - 1 configured with a single update thread to guarantee sequential update to the remote server
 * - 2 update tasks can be scheduled on-demand via onDemandUpdate()
 * - 3 task processing is rate limited by burstSize
 * - 4 a new update task is always scheduled automatically after an earlier update task.  However if an on-demand task is started, the scheduled automatic update task is discarded (and a new one will be scheduled after the new
 *   on-demand update).
 *
 *   @author dliu
 */
class InstanceInfoReplicator implements Runnable 
  • 1處,配置了一個單線程,保證向遠程eureka server,順序更新
  • 2處,通過本類的onDemandUpdate,可以強行插入一個任務,而無需通過定時執行
  • 3處,限流相關
  • 4處,執行完一個周期任務后,馬上會給自己安排下一個周期任務

其run方法:

    public void run() {
        try {
            // 1
            discoveryClient.refreshInstanceInfo();

            Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
            if (dirtyTimestamp != null) {
                // 2
                discoveryClient.register();
                instanceInfo.unsetIsDirty(dirtyTimestamp);
            }
        }finally {
            // 3
            Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }
  • 1處,刷新實例信息
  • 2處,如果有需要的話,向eureka server進行註冊
  • 3處,調度下一次任務

初始化結束

基本,這個CloudEurekaClient構造就結束了,後續就依靠其開啟的一堆定時任務去進行工作。

總結

eureka client的初始化就講了這麼多,註冊還沒講,留帶下一講吧。

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

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

都說國產車品質太爛! 到底差在哪裡?_台中搬家公司

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

豐田旗下的每一款車型在耐用性方面表現都是非常的不錯,故障率十分低,並且使用成本也比較低,通過觀察二手車市場你就會發現豐田的車型保值率總是非常的高,這也是由於它耐用性十分突出的原因,反觀自主車型二手車的保值率往往低的嚇人。

我們從來不會否認一台自主汽車的配置水平,它的外觀漂亮與否,現在許許多多的廠家都能夠造出外觀漂亮配置豐富的汽車,比如說眾泰或者陸風。

設計一個好的外觀不難,只需要跑到意大利邀請他們的設計師來參与設計或者主導設計,或者乾脆把外觀設計的項目外包給設計公司,這樣就能夠出來一個非常漂亮的非常國際范的外觀,比如我們非常熟悉的東南DX7就是意大利設計師設計的成果。

而配置則更加無須多言只需要有足夠多的供應商,提供諸多的配置,就能堆砌出一台配置十分豐富的車型,車身穩定系統,名牌音響,氙氣大燈,電動座椅這些通通不成問題。可是除了配置豐富/外觀漂亮/內飾用料高檔之外我們還能想到自主汽車有什麼共性呢?

說到日系車我們會想到節油耐用,說到德國車我們會想到精緻、底盤紮實,可是說到自主車我們只能想到性價比,那麼相比合資產品自主車型到底差在哪裡呢?

机械水平

汽車終究是一個机械產品,它最重要的身份就是交通工具,對於駕駛者來說一輛車它的駕駛體驗是十分重要的,加速性能如何?變速箱的表現怎麼樣?車輛的底盤質感怎麼樣,高速行駛的穩定性是否優秀,壓過減速帶或者是一些溝溝坎坎的時候車內的舒適性如何等等這些方面都是一輛汽車最基本的方面,它不會讓你去刻意關注,但是一旦有什麼短板在體驗中會給你造成極大的困擾。因此,

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

許多人在試駕完自主車型再去試駕合資車型就會發現在机械水平上兩者的差距是比較大的。當然也有小部分的自主車型在机械水平上已經和合資車型打成平手,比如說吉利的博越。

耐用性

自主車型的耐用性也是消費者十分擔心的一個問題,多數自主車型來說沒有強大的用戶基礎,也沒有口碑的積累,說到豐田我們就會想到耐用想到品質感動世界!豐田旗下的每一款車型在耐用性方面表現都是非常的不錯,故障率十分低,並且使用成本也比較低,通過觀察二手車市場你就會發現豐田的車型保值率總是非常的高,這也是由於它耐用性十分突出的原因,反觀自主車型二手車的保值率往往低的嚇人。許多自主車型的買家都會覺得開了三四年之後車子就感覺像散的一樣。而其它的小毛病諸如油耗高之類的問題也是頻頻發生。

配置的完善程度

為什麼說配置的完善程度也是一個考量標準呢?我們知道iphone能夠實現的功能,安卓也全都可以實現,但是。iphone就是比安卓要貴上那麼多,這其中的差別就在於系統的完善程度帶來的體驗差距,我們知道自主車型擁有非常豐富的配置,但是每一個配置是否好用,比如說倒車影像的显示清晰度。掛上倒擋之後倒車影像,显示畫面的速度延遲,倒車影像指示標線會不會隨着方向盤的轉動而轉動,倒車影像畫面在夜晚的時候拍攝的清晰度如何?等等,比如說有的車型有電動摺疊后視鏡,但是在鎖車的時候並不會自動摺疊。這隻是一個程序設置的問題,硬件的成本都花了,為什麼不能在軟件上優化一下呢?

在配置的好用程度/机械水平以及耐用性上,都不如合資產品的自主車自然就會給你比較廉價的感覺,雖然配置很豐富,雖然他的外觀足夠的漂亮,但是作為一個生活用品作為一台交通工具來說,它可能並不是那麼稱職。僅靠豐富的配置打天下的車型可以說是徒有其表,而真正能夠在机械上下功夫的自主車去才是比較有遠見的車企,好在一些一線自主品牌車型已經開始在机械水平上下功夫,比如長安/吉利/奇瑞等。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

不到7萬買馬自達/三菱/日產?這些緊湊型車叫板合資_網頁設計公司

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

但海馬本身對於馬自達的質控學習不過關,在質控上明顯要弱於吉利奇瑞長安這些一線國產廠商。2東南菱悅作為三菱在中國的合作夥伴,東南當然有着一部分三菱的技術儲備。這款V3菱悅就是最好的例子,其的前身實際是大名鼎鼎的Lance EVO,也就是當時叱吒拉力賽的三菱神車,也是現在三菱藍瑟翼神的前身。

緊湊型轎車

隨着我們時代的變遷以及我國汽車產業的發展,汽車的價格不斷下降,車型也越發多了起來。對於我們來說,不僅價格便宜了,還有着更多的選擇。則一些老牌的品牌車型為了提高競爭力甚至不得不降價,這就成為了能用不到7萬的價錢買到以前十萬甚至十萬以上車型的體驗。

海馬的前生其實是海南馬自達,和着馬自達合作的海南馬自達在那時是引進了馬自達323,也就是現在馬自達3昂克賽拉的前身。在脫離馬自達“單幹”以後,海馬繼續生產323.並在這些年吸收其技術,所以研發了這款海馬福美來,並且對於過往的323有着更大的車身和空間表現,在動力方面有着自主研發的1.6L以及1.5T發動機可供選擇,搭配的是6MT或者6AT變速箱,

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

更為優秀的是延續了馬自達在底盤上的造詣,底盤表現出色。但海馬本身對於馬自達的質控學習不過關,在質控上明顯要弱於吉利奇瑞長安這些一線國產廠商。

作為三菱在中國的合作夥伴,東南當然有着一部分三菱的技術儲備。這款V3菱悅就是最好的例子,其的前身實際是大名鼎鼎的Lance EVO,也就是當時叱吒拉力賽的三菱神車,也是現在三菱藍瑟翼神的前身。所以在底盤方面表現非常出色,有着同級別中非常少見的后多連桿式懸架,加上三菱在底盤方面的浸淫,東南菱悅的底盤表現在同價位中可謂是出類拔萃的。但是發動機方面,東南和三菱一樣原地踏步,僅僅只有一個“年長”的1.6L自然發動機勉強擺上台,與對手相比僅僅只有一個保養維修便宜的優點。

對於日產騏達以及頤達相信大家都不會陌生,它們有着保養便宜、油耗低以及同級別中幾近最大的空間的特點,所以在當時非常的暢銷。而啟辰D50就是上一代的日產頤達重新設計外觀而成,有着頤達所有的優點,並且比起當初售價十萬以上的頤達要便宜近一半價錢,搭載的動力總成也繼續延續頤達的動力總成,只是4AT變速箱有些年頭,對油耗不是很友好。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

7-8萬市區行車最實用的合資車型 性價比真不低!_潭子電動車

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

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

pOLO的外觀圓潤飽滿,看起來比較中規中矩,符合大眾的中庸式的設計風格,適合成熟的消費者。內飾也依然很大眾風,簡單實用,缺乏新意,容易審美疲勞。pOLO的動力系統種類繁多,發動機為1。4L 90馬力 L4、1。6L 110馬力 L4和1。

雖然國人都是很喜歡大的車子,但是也不能忽略一個客觀的事實,那就是城市越來越擁堵了,在這環境下開一個大的車子確實很不方便。但是這時候如果有一台靈活的小車,那麼在市區開起來就輕鬆多了。

長安福特-嘉年華

福特嘉年華兩廂車的車身尺寸為3970*1722*1470mm,三廂的為4320*1722*1470mm,軸距都為2495mm,定位小型車。嘉年華的前臉採取了福特家族式設計特徵-馬丁臉的設計,再配合炯炯有神的前大燈組,使得嘉年華看起來動感十足。

嘉年華的動力系統為1.5L 110馬力+5擋手動/6擋雙離合,1.0T 125馬力+6擋雙離合。我在路上基本沒有見過三廂的嘉年華,不過我們也真的不推薦三廂版的車型,看起來真的是好難看。反之兩廂版的看起來則協調很多。

嘉年華的這款1.0T的發動機為三缸發動機,曾經榮獲2015年1.0L以下組的年度國際最佳發動機,擁有缸內直噴、渦輪增壓、可變氣門正時三大技術,燃油經濟性很好,同時平順性和普通的4缸發動機一樣,一般人很難覺察出來這是一款3缸發動機。但是,這麼先進的一款發動機,配合6擋雙離合算是有點失敗了,如果不是特別喜歡,

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

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

盡量不推薦購買雙離合版本的車型。

上汽大眾-pOLO

大眾pOLO的車身尺寸為3970*1682*1462mm、3975*1682*1487mm、3987*1705*1486mm,軸距2470mm。車身尺寸幾乎和嘉年華完全一樣。pOLO的外觀圓潤飽滿,看起來比較中規中矩,符合大眾的中庸式的設計風格,適合成熟的消費者。

內飾也依然很大眾風,簡單實用,缺乏新意,容易審美疲勞。pOLO的動力系統種類繁多,發動機為1.4L 90馬力 L4、1.6L 110馬力 L4和1.4T 150馬力 L4,匹配5擋手動、6擋自動、6擋手自一體和7擋雙離合四款變速箱。雖然大眾曾經被雙離合折磨的夠嗆,但是就現在來看,大眾目前的雙離合和同級別比起來,還是比較好用的。但是然並卵,買pOLO的幾乎都是自吸版本的,1.4T車型幾乎無人問津,畢竟老百姓家用車,還是自吸最省心、省錢。

廣汽豐田-YARiS L 致炫

致炫的車身尺寸為4115*1700*1485mm、4160*1700*1485mm,軸距為2550mm。雖然都是小型兩廂車,但是致炫的長度要大於嘉年華和pOLO,較大的軸距加上日系車的偷空間技術,所以致炫的後排空間是最大的,甚至可以和緊湊型轎車媲美。

致炫的動力系統為1.3L 99馬力/1.5L 107馬力+5擋手動/4擋自動/CVT。其中4AT車型為停產在售狀態,因為前段時間新款致炫上市,用CVT取代了老舊的4AT,同時新款的致炫標配了CVT變速箱,無論在燃油的經濟性和平順性都會有提高。新款致炫空間大,配置不低,皮實耐操,省油省心,也是一款不錯的家庭代步車。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

Microsoft 與 Parallels 合作,要將 Windows 10 帶到 M1 版 Mac 上_網頁設計

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

Apple 在上個月推出了以 ARM 為基礎的 M1 晶片,現在應該全球的開發者都在日以繼夜地努力將自家的應用程導入這個新平台上,對於雙系統用戶來說,還是最關心 Windows 10 到底什麼時候可以支援 M1 晶片。日前 Microsoft 與 Parallels 開展合作,盡量讓大家不用久等就能享用。

Microsoft 與 Parallels 合作,要將 Windows 10 帶到 M1 版 Mac 上

對雙系統用戶來說,Parallels 絕對不陌生,該公司已經開始開發自己的軟體,要將 Windows 10 帶到 Apple Silicon 的世界,在不久前該公司才剛發布了第一個測試預覽版本,讓用戶能夠盡早試用該產品。事實證明,Parallels 並不是唯一一個在這方面使力的人,雖然不清楚兩家公司的合作方式為何,但 Microsoft 旗下 OneDrive 副總裁 Omar Shahine 在 Twitter 上面讚揚了此一合作。

Dang this is amazing! Windows 10 ARM running on MacBook Air M1. Great performance and battery life! Thanks @ParallelsMac and @Microsoft for releasing the ARM build of Windows! pic.twitter.com/BR8vZhJV7V

— Omar Shahine (@OmarShahine) December 23, 2020

有趣的是,在不久前,Apple 的高層主管曾表示將 Windows 引進 Apple Silicon 是僅有 Microsoft 自己能夠做決定,在彼時,Apple 軟體工程高級副總裁 Craig Federighi 曾表示 Apple 擁有 Microsoft 能做到這點的核心技術,可以直接運行 ARM 版本的 Windows,但又能反過來支援 X86 的應用程式,但這必須由 Microsoft 做出決定,將該技術授權給用戶在 M1 版 Mac 設備上運行。

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

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

於此同時,Apple Silicon 世界正在持續壯大,不久前也有傳聞 Microsoft 也正在為新晶片的軟體最佳化投入大量資金,Office、Teams 與其他 Microsoft 應用程式正陸陸續續開放支援 M1 晶片,以求一切都能在新的 Apple 設備上盡可能地平穩運作。

◎資料來源:SoftPedia

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

Cypress系列(3)- Cypress 的初次體驗_台北網頁設計

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

如果想從頭學起Cypress,可以看下面的系列文章哦

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

 

前言

這裏的栗子項目是 Cypress 提供的,在 github 上,所以要 clone 到本地的話需要裝 Git 哦!

 

下載被測應用

進入要安裝該應用的目錄,cmd 敲

git clone git@github.com:cypress-io/cypress-example-recipes.git

進入項目目錄下,安裝項目所需依賴包,敲

npm install

安裝成功后,項目的文件結構如下圖;所有被測應用栗子都在 examples 文件夾中

 

啟動被測應用

啟動測試應用時,可以進入不同子項目文件夾來啟動不同的應用;

假如,我們要測試表單類型的登錄,可以打開以下被測應用

cd examples\logging-in__html-web-forms>

 

啟動本地server

npm start

 

啟動成功后,cmd窗口將显示服務器的地址和端口

 

打開瀏覽器訪問:http://localhost:7077/,即可看到登錄頁面

 

快速測試登錄頁面

首先,設計測試用例步驟

  1. 訪問http://localhost:7077
  2. 輸入用戶名、密碼,點擊登錄
  3. 如果用戶名和密碼正確,則登錄成功,否則登錄失敗

 

接下來,我們來看看實現測試用例的步驟

 

創建測試

在此目錄下 cypress安裝路徑\node_modules\.bin\cypress\integration ,創建一個 js 文件,比如:testLogin.js

integration文件夾

  • Cypress 安裝完畢后自動生成的文件夾
  • 也是 Cypress 默認存放測試用例的根目錄,任何創建在此目錄下的文件都將被當作測試用例

 

編寫測試用例

首先,要在網頁上定位到用戶名、密碼輸入框,此案例中使用標籤+屬性名來定位;最終測試代碼如下

咱們在後面再講解代碼的意思哦

 

運行測試

進入 Cypress 安裝文件夾,cmd執行命令

yarn cypress:open

 

單擊 testLogin.js,Cypress 會啟動 Test  Runner 運行測試,運行成功后,將看到運行結果頁面;運行時長是真的快….

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

 

調試測試用例

前言

  • 測試用例運行時,難免會發生各種情況導致運行失敗;快速定位發生錯誤的位置,了解錯誤信息,一直是自動化測試的痛點
  • 而 Cypress 提供了多種 debug 能力,可以在測試運行錯誤時直達錯誤位置,並支持回放錯誤發生時的上下文信息,可直接看到測試失敗的原因 

 

Cypress Debug 能力介紹

每個命令均有快照且支持回放

像下圖,左側就是測試步驟,右側是測試頁面

  • 鼠標 hover 測試步驟,在右側可以看到執行該命令時的頁面效果
  • 鼠標點擊測試步驟,可以鎖定該步驟,然後查看上下文信息

 

支持查看測試運行時發生的特殊頁面事件

包括:

  • 網絡 XHR 請求
  • URL 哈希更改
  • 頁面加載
  • 表單提交

例如,上面測試用例中,點擊【submit】后產生的就是提交表單的請求,看下圖

可以看到一個 submit 操作,分成了三步走

  1. form sub:提交表單
  2. page load:頁面加載
  3. new url:訪問新的頁面

 

Console 輸出每個命令的詳細信息

瀏覽器F12即可見到熟悉的開發者工具頁面了

以上圖為栗子,一個 submitting form 表單提交的請求,在 Console 中打印了詳細的信息,可以快速了解在運行時的詳細狀態信息

 

暫停測試並逐步運行、恢復執行

在調試測試代碼時,Cypress 提供了兩個命令來暫停測試運行

  1. cy.pause()
  2. cy.debug()

 

cy.pause() 的栗子

在表單提交之前暫停測試,我們來看看運行結果

左上角有兩個按鈕,從左往右分別是

  • Resume:繼續執行測試用例並運行到結束
  • Next:get:測試會變成逐步運行,點一下執行下一個命令

 

cy.debug() 的栗子

運行測試看看下圖結果

測試運行在找到表單的時候,暫停運行並等待用戶操作

頂部的Paused in debugger,右邊兩個按鈕分別是
  • Resume Script Execution(F8):繼續執行測試用例並運行到結束
  • Step Over next function call(F10):跳轉到下一個調用debug()函數的地方 

 

當找到隱藏或多個元素時,可視化結果

更改 username 輸入框的定位器,使他匹配到不止一個元素

運行測試看看下面結果

因為定位表達式匹配到不止一個元素,所以執行 type() 方法時以失敗告終

 

總結

這一節咱們以測試一個登錄界面為需求,寫了一個簡單的測試用例來做栗子,後面將詳細講解 Cypress 的各部分內容哦

 

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

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。