從linux源碼看socket(tcp)的timeout

從linux源碼看socket(tcp)的timeout

前言

網絡編程中超時時間是一個重要但又容易被忽略的問題,對其的設置需要仔細斟酌。在經歷了數次物理機宕機之後,筆者詳細的考察了在網絡編程(tcp)中的各種超時設置,於是就有了本篇博文。本文大部分討論的是socket設置為block的情況,即setNonblock(false),僅在最後提及了nonblock socket(本文基於linux 2.6.32-431內核)。

connectTimeout

在討論connectTimeout之前,讓我們先看下java和C語言對於socket connect調用的函數簽名:

java:
 // 函數調用中攜帶有超時時間
 public void connect(SocketAddress endpoint, int timeout) ;
C語言:
 // 函數調用中並不攜帶超時時間
 int connect(int sockfd, const struct sockaddr * sockaddr, socklen_t socklent) 	 

操作系統提供的connect系統調用並沒有提供timeout的參數設置而java卻有,我們先考察一下原生系統調用的超時策略。

connect系統調用

我們觀察一下此系統調用的kernel源碼,調用棧如下所示:

connect[用戶態]
	|->SYSCALL_DEFINE3(connect)[內核態]
			|->sock->ops->connect

由於我們考察的是tcp的connect,其socket的內部結構如下圖所示:

最終調用的是tcp_connect,代碼如下所示:

int tcp_connect(struct sock *sk) {
	......
	// 發送SYN
	err = tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
	...
	/* Timer for repeating the SYN until an answer. */
	// 由於是剛建立連接,所以其rto是TCP_TIMEOUT_INIT
	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
				inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
	return 0;	
}

又上面代碼可知,在tcp_connect設置了重傳定時器之後return回了tcp_v4_connect再return到inet_stream_connect。我們繼續考察:

int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
			int addr_len, int flags)
{
	......
	// tcp_v4_connect=>tcp_connect
	err = sk->sk_prot->connect(sk, uaddr, addr_len);
	// 這邊用的是sk->sk_sndtimeo
	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
	......
	inet_wait_for_connect(sk, timeo));
	......
out:
	release_sock(sk);
	return err;

sock_error:
	err = sock_error(sk) ? : -ECONNABORTED;
	sock->state = SS_UNCONNECTED;
	if (sk->sk_prot->disconnect(sk, flags))
		sock->state = SS_DISCONNECTING;
	goto out
}

由上面代碼可見,可以採用設置SO_SNDTIMEO來控制connect系統調用的超時,如下所示:

setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);

不設置SO_SNDTIMEO

如果不設置SO_SNDTIMEO,那麼會由tcp重傳定時器在重傳超過設置的時候后超時,如下圖所示:

這個syn重傳的次數由:

cat /proc/sys/net/ipv4/tcp_syn_retries 筆者機器上是5 

來決定。那麼我們就來看一下這個重傳到底是多長時間:

tcp_connect中:
		// 設置的初始超時時間為icsk_rto=TCP_TIMEOUT_INIT為1s
		inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
				inet_csk(sk)->icsk_rto, TCP_RTO_MAX);

其重傳定時器的回掉函數為tcp_retransmit_timer:

void tcp_retransmit_timer(struct sock *sk)
{
	......
	// 檢測是否超時
	if (tcp_write_timeout(sk))
		goto out;
	......
	// icsk_rto = icsk_rto * 2,由於syn階段,所以isck_rto不會由於網絡傳輸而改變
	// 重傳的時候會以1,2,4,8指數遞增
	icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
	// 重設timer
	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
out:;		
}

而計算tcp_write_timeout的邏輯則是在這篇blog中已經詳細描述過,

https://my.oschina.net/alchemystar/blog/1936433

只不過在connect時刻,重傳的計算以TCP_TIMEOUT_INIT為單位進行計算。而ESTABLISHED(read/write)時刻,重傳以TCP_RTO_MIN進行計算。那麼根據這段重傳邏輯,我們就可以計算出不同tcp_syn_retries最終表現的超時時間。如下圖所示:

那麼整理下錶格,對於系統調用,connect的超時時間為:

tcp_syn_retries timeout
1 min(so_sndtimeo,3s)
2 min(so_sndtimeo,7s)
3 min(so_sndtimeo,15s)
4 min(so_sndtimeo,31s)
5 min(so_sndtimeo,63s)

上述超時時間和筆者的實測一致。

kernel代碼版本細微變化

值得注意的是,linux本身官方發布的2.6.32源碼對於tcp_syn_retries2的解釋和RFC並不一致(至少筆者閱讀的代碼如此,這個細微的變化困擾了筆者好久,筆者下載了和機器對應的內核版本后才發現代碼改了)。而redhat發布的2.6.32-431已經修復了這個問題(不清楚具體哪個小版本修改的),並將初始RTO設置為1s(官方2.6.32為3s)。這也是,不同內核小版本上的實驗會有不同的connect timeout表現的原因(有的抓包到的重傳SYN時間間隔為3,6,12……)。以下為代碼對比:

========================>linux 內核版本2.6.32-431<========================
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ))	/* RFC2988bis initial RTO value	*/

static inline bool retransmits_timed_out(struct sock *sk,
					 unsigned int boundary,
					 unsigned int timeout,
					 bool syn_set)
{
	......
	unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;
	......
	timeout = ((2 << boundary) - 1) * rto_base;
	......

}
========================>linux 內核版本2.6.32.63<========================
#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ))	/* RFC 1122 initial RTO value	*/

static inline bool retransmits_timed_out(struct sock *sk,
					 unsigned int boundary
{
	......
	timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
	......
}

另外,tcp_syn_retries重傳次數可以在單個socket中通過setsockopt設置。

JAVA connect API

現在我們考察下java的connect api,其connect最終調用下面的代碼:

Java_java_net_PlainSocketImpl_socketConnect(...){

    if (timeout <= 0) {
    	 ......
        connect_rv = NET_Connect(fd, (struct sockaddr *)&him, len);
    	 .....
    }else{
    	 // 如果timeout > 0 ,則設置為nonblock模式
        SET_NONBLOCKING(fd);
        /* no need to use NET_Connect as non-blocking */
        connect_rv = connect(fd, (struct sockaddr *)&him, len);
        /*
         * 這邊用系統調用select來模擬阻塞調用超時
         */
        while (1) {
            ......
            struct timeval t;
            t.tv_sec = timeout / 1000;
            t.tv_usec = (timeout % 1000) * 1000;
            connect_rv = NET_Select(fd+1, 0, &wr, &ex, &t);
            ......
        }
        ......
        // 重新設置為阻塞模式
        SET_BLOCKING(fd);
        ......
    }
}

其和connect系統調用的不同點是,在timeout為0的時候,走默認的系統調用不設置超時時間的邏輯。在timeout>0時,將socket設置為非阻塞,然後用select系統調用去模擬超時,而沒有走linux本身的超時邏輯,如下圖所示:

由於沒有java並沒有設置so_sndtimeo的選項,所以在timeout為0的時候,直接就通過重傳次數來控制超時時間。而在調用connect時設置了timeout(不為0)的時候,超時時間如下錶格所示:

tcp_syn_retries timeout
1 min(timeout,3s)
2 min(timeout,7s)
3 min(timeout,15s)
4 min(timeout,31s)
5 min(timeout,63s)

socketTimeout

write系統調用的超時時間

socket的write系統調用最後調用的是tcp_sendmsg,源碼如下所示:

int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
		size_t size){
	......
	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
	......
	while (--iovlen >= 0) {
		......
		// 此種情況是buffer不夠了
		if (copy <= 0) {
	new_segment:
		  ......
		  if (!sk_stream_memory_free(sk))
			  goto wait_for_sndbuf;

		  skb = sk_stream_alloc_skb(sk, select_size(sk),sk->sk_allocation);
		  if (!skb)
			  goto wait_for_memory;
		}
		......
	}
	......
	// 這邊等待write buffer有空間
wait_for_sndbuf:
		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
		if (copied)
			tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
			// 這邊等待timeo長的時間
		if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
			goto do_error;
		......
out:
	// 如果拷貝了數據,則返回
	if (copied)
		tcp_push(sk, flags, mss_now, tp->nonagle);
	TCP_CHECK_TIMER(sk);
	release_sock(sk);
	return copied;		
out_err:
	// error的處理
	err = sk_stream_error(sk, flags, err);
	TCP_CHECK_TIMER(sk);
	release_sock(sk);
	return err;		
}

從上面的內核代碼看出,如果socket的write buffer依舊有空間的時候,會立馬返回,並不會有timeout。但是write buffer不夠的時候,會等待SO_SNDTIMEO的時間(nonblock時候為0)。但是如果SO_SNDTIMEO沒有設置的時候,默認初始化為MAX_SCHEDULE_TIMEOUT,可以認為其超時時間為無限。那麼其超時時間會有另一個條件來決定,我們看下sk_stream_wait_memory的源碼:

int sk_stream_wait_memory(struct sock *sk, long *timeo_p){
		// 等待socket shutdown或者socket出現err
		sk_wait_event(sk, &current_timeo, sk->sk_err ||
						  (sk->sk_shutdown & SEND_SHUTDOWN) ||
						  (sk_stream_memory_free(sk) &&
						  !vm_wait));
}						 

在write等待的時候,如果出現socket被shutdown或者socket出現錯誤的時候,則會跳出wait進而返回錯誤。在不考慮對端shutdown的情況下,出現sk_err的時間其實就是其write的timeout時間,那麼我們看下什麼時候出現sk->sk_err。

SO_SNDTIMEO不設置,write buffer滿之後ack一直不返回的情況(例如,物理機宕機)

物理機宕機后,tcp發送msg的時候,ack不會返回,則會在重傳定時器tcp_retransmit_timer到期后timeout,其重傳到期時間通過tcp_retries2以及TCP_RTO_MIN計算出來。其源碼可見筆者的blog:

https://my.oschina.net/alchemystar/blog/1936433

tcp_retries2的設置位置為:

cat /proc/sys/net/ipv4/tcp_retries2 筆者機器上是5,默認是15

SO_SNDTIMEO不設置,write buffer滿之後對端不消費,導致buffer一直滿的情況

和上面ack超時有些許不一樣的是,一個邏輯是用TCP_RTO_MIN通過tcp_retries2計算出來的時間。另一個是真的通過重傳超過tcp_retries2次數來time_out,兩者的區別和rto的動態計算有關。但是可以大致認為是一致的。

上述邏輯如下圖所示:

write_timeout表格

tcp_retries2 buffer未滿 buffer滿
5 立即返回 min(SO_SNDTIMEO,(25.6s-51.2s)根據動態rto定
15 立即返回 min(SO_SNDTIMEO,(924.6s-1044.6s)根據動態rto定

java的SocketOutputStream的sockWrite0超時時間

java的sockWrite0沒有設置超時時間的地方,同時也沒有設置過SO_SNDTIMEOUT,其直接調用了系統調用,所以其超時時間和write系統調用保持一致。

readTimeout

ReadTimeout可能是最容易導致問題的地方。我們先看下系統調用的源碼:

read系統調用

socket的read系統調用最終調用的是tcp_recvmsg, 其源碼如下:

int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
		size_t len, int nonblock, int flags, int *addr_len)
{
	......
	// 這邊timeo=SO_RCVTIMEO
	timeo = sock_rcvtimeo(sk, nonblock);
	......
	do{
		......
		// 下面這一堆判斷表明,如果出現錯誤,或者已經被CLOSE/SHUTDOWN則跳出循環
		if(copied) {
			if (sk->sk_err ||
			    sk->sk_state == TCP_CLOSE ||
			    (sk->sk_shutdown & RCV_SHUTDOWN) ||
			    !timeo ||
			    signal_pending(current))
				break;
		} else {
			if (sock_flag(sk, SOCK_DONE))
				break;

			if (sk->sk_err) {
				copied = sock_error(sk);
				break;
			}
			// 如果socket shudown跳出
			if (sk->sk_shutdown & RCV_SHUTDOWN)
				break;
			// 如果socket close跳出
			if (sk->sk_state == TCP_CLOSE) {
				if (!sock_flag(sk, SOCK_DONE)) {
					/* This occurs when user tries to read
					 * from never connected socket.
					 */
					copied = -ENOTCONN;
					break;
				}
				break;
			}
			.......
		}
		.......

		if (copied >= target) {
			/* Do not sleep, just process backlog. */
			release_sock(sk);
			lock_sock(sk);
		} else /* 如果沒有讀到target自己數(和水位有關,可以暫認為是1),則等待SO_RCVTIMEO的時間 */
			sk_wait_data(sk, &timeo);	
	} while (len > 0);
	......
}

上面的邏輯如下圖所示:

重傳以及探測定時器timeout事件的觸發時機如下圖所示:

如果內核層面ack正常返回而且對端窗口不為0,僅僅應用層不返回任何數據,那麼就會無限等待,直到對端有數據或者socket close/shutdown為止,如下圖所示:

很多應用就是基於這個無限超時來設計的,例如activemq的消費者邏輯。

java的SocketInputStream的sockRead0超時時間

java的超時時間由SO_TIMOUT決定,而linux的socket並沒有這個選項。其sockRead0和上面的java connect一樣,在SO_TIMEOUT>0的時候依舊是由nonblock socket模擬,在此就不再贅述了。

ReadTimeout超時表格

C系統調用:

tcp_retries2 對端無響應 對端內核響應正常
5 min(SO_RCVTIMEO,(25.6s-51.2s)根據動態rto定 SO_RCVTIMEO==0?無限,SO_RCVTIMEO)
15 min(SO_RCVTIMEO,(924.6s-1044.6s)根據動態rto定 SO_RCVTIMEO==0?無限,SO_RCVTIMEO)

Java系統調用

tcp_retries2 對端無響應 對端內核響應正常
5 min(SO_TIMEOUT,(25.6s-51.2s)根據動態rto定 SO_TIMEOUT==0?無限,SO_RCVTIMEO
15 min(SO_TIMEOUT,(924.6s-1044.6s)根據動態rto定 SO_TIMEOUT==0?無限,SO_RCVTIMEO

對端物理機宕機之後的timeout

對端物理機宕機后還依舊有數據發送

對端物理機宕機時對端內核也gg了(不會發出任何包通知宕機),那麼本端發送任何數據給對端都不會有響應。其超時時間就由上面討論的
min(設置的socket超時[例如SO_TIMEOUT],內核內部的定時器超時來決定)。

對端物理機宕機后沒有數據發送,但在read等待

這時候如果設置了超時時間timeout,則在timeout后返回。但是,如果僅僅是在read等待,由於底層沒有數據交互,那麼其無法知道對端是否宕機,所以會一直等待。但是,內核會在一個socket兩個小時都沒有數據交互情況下(可設置)啟動keepalive定時器來探測對端的socket。如下圖所示:

大概是2小時11分鐘之後會超時返回。keepalive的設置由內核參數指定:

cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 即兩個小時后開始探測
cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 即每次探測間隔為75s
cat /proc/sys/net/ipv4/tcp_keepalve_probes 9 即一共探測9次

可以在setsockops中對單獨的socket指定是否啟用keepalive定時器(java也可以)。

對端物理機宕機后沒有數據發送,也沒有read等待

和上面同理,也是在keepalive定時器超時之後,將連接close。所以我們可以看到一個不活躍的socket在對端物理機突然宕機之後,依舊是ESTABLISHED狀態,過很長一段時間之後才會關閉。

進程宕后的超時

如果僅僅是對端進程宕機的話(進程所在內核會close其所擁有的所有socket),由於fin包的發送,本端內核可以立刻知道當前socket的狀態。如果socket是阻塞的,那麼將會在當前或者下一次write/read系統調用的時候返回給應用層相應的錯誤。如果是nonblock,那麼會在select/epoll中觸發出對應的事件通知應用層去處理。
如果fin包沒發送到對端,那麼在下一次write/read的時候內核會發送reset包作為回應。

nonblock

設置為nonblock=true后,由於read/write都是立刻返回,且通過select/epoll等處理重傳超時/probe超時/keep alive超時/socket close等事件,所以根據應用層代碼決定其超時特性。定時器超時事件發生的時間如上面幾小節所述,和是否nonblock無關。nonblock的編程模式可以讓應用層對這些事件做出響應。

總結

網絡編程中超時時間是個重要但又容易被忽略的問題,這個問題只有在遇到物理機宕機等平時遇不到的現象時候才會凸顯。筆者在經曆數次物理機宕機之後才好好的研究了一番,希望本篇文章可以對讀者在以後遇到類似超時問題時有所幫助。

公眾號

關注筆者公眾號,獲取更多乾貨文章:

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

【String註解驅動開發】如何按照條件向Spring容器中註冊bean?這次我懂了!!

寫在前面

當bean是單實例,並且沒有設置懶加載時,Spring容器啟動時,就會實例化bean,並將bean註冊到IOC容器中,以後每次從IOC容器中獲取bean時,直接返回IOC容器中的bean,不再創建新的bean。

如果bean是單實例,並且使用@Lazy註解設置了懶加載,則Spring容器啟動時,不會實例化bean,也不會將bean註冊到IOC容器中,只有第一次獲取bean的時候,才會實例化bean,並且將bean註冊到IOC容器中。

如果bean是多實例,則Spring容器啟動時,不會實例化bean,也不會將bean註冊到IOC容器中,以後每次從IOC容器中獲取bean時,都會創建一個新的bean返回。

Spring支持按照條件向IOC容器中註冊bean,滿足條件的bean就會被註冊到IOC容器中,不滿足條件的bean就不會被註冊到IOC容器中。接下來,我們就一起來探討Spring中如何實現按照條件向IOC容器中註冊bean。

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

@Conditional註解概述

@Conditional註解可以按照一定的條件進行判斷,滿足條件向容器中註冊bean,不滿足條件就不向容器中註冊bean。

@Conditional註解是由 SpringFramework 提供的一個註解,位於 org.springframework.context.annotation 包內,定義如下。

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
	Class<? extends Condition>[] value();
}

從@Conditional註解的源碼來看,@Conditional註解可以添加到類上,也可以添加到方法上。在@Conditional註解中,存在一個Condition類型或者其子類型的Class對象數組,Condition是個啥?我們點進去看一下。

package org.springframework.context.annotation;

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;
@FunctionalInterface
public interface Condition {
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

可以看到,Condition是一個函數式接口,對於函數式接口不了解的同學可以參見【Java8新特性】中的《【Java8新特性】還沒搞懂函數式接口?趕快過來看看吧!》一文。也可以直接查看《Java8新特性專欄》來系統學習Java8的新特性。

所以,我們使用@Conditional註解時,需要一個類實現Spring提供的Condition接口,它會匹配@Conditional所符合的方法,然後我們可以使用我們在@Conditional註解中定義的類來檢查。

@Conditional註解的使用場景如下所示。

  • 可以作為類級別的註解直接或者間接的與@Component相關聯,包括@Configuration類;
  • 可以作為元註解,用於自動編寫構造性註解;
  • 作為方法級別的註解,作用在任何@Bean方法上。

向Spring容器註冊bean

不帶條件註冊bean

我們在PersonConfig2類中新增person01()方法和person02()方法,併為兩個方法添加@Bean註解,如下所示。

@Bean("binghe001")
public Person person01(){
    return new Person("binghe001", 18);
}

@Bean("binghe002")
public Person person02(){
    return new Person("binghe002", 20);
}

那麼,這兩個bean默認是否會被註冊到Spring容器中呢,我們新建一個測試用例來測試一下。在SpringBeanTest類中新建testAnnotationConfig6()方法,如下所示。

@Test
public void testAnnotationConfig6(){
    ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
    String[] names = context.getBeanNamesForType(Person.class);
    Arrays.stream(names).forEach(System.out::println);
}

我們運行testAnnotationConfig6()方法,輸出的結果信息如下所示。

person
binghe001
binghe002

從輸出結果可以看出,同時輸出了binghe001和binghe002。說明默認情況下,Spring容器會將單實例並且非懶加載的bean註冊到IOC容器中。

接下來,我們再輸出bean的名稱和bean實例對象信息,此時我們在testAnnotationConfig6()方法中添加相應的代碼片段,如下所示。

@Test
public void testAnnotationConfig6(){
    ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
    String[] names = context.getBeanNamesForType(Person.class);
    Arrays.stream(names).forEach(System.out::println);

    Map<String, Person> beans = context.getBeansOfType(Person.class);
    System.out.println(beans);
}

再次運行SpringBeanTest類中的testAnnotationConfig6()方法,輸出結果如下所示。

person
binghe001
binghe002
給容器中添加Person....
{person=Person(name=binghe002, age=18), binghe001=Person(name=binghe001, age=18), binghe002=Person(name=binghe002, age=20)}

可以看到,輸出了註冊到容器的bean。

帶條件註冊bean

現在,我們就要提出新的需求了,比如,如果當前操作系統是Windows操作系統,則向Spring容器中註冊binghe001;如果當前操作系統是Linux操作系統,則向Spring容器中註冊binghe002。此時,我們就需要使用@Conditional註解了。

這裏,有小夥伴可能會問:如何獲取操作系統的類型呢,別急,這個問題很簡單,我們繼續向下看。

使用Spring的ApplicationContext接口就能夠獲取到當前操作系統的類型,如下所示。

ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
System.out.println(osName);

我們將上述代碼整合到SpringBeanTest類中的testAnnotationConfig6()方法中,如下所示。

@Test
public void testAnnotationConfig6(){
    ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
    Environment environment = context.getEnvironment();
    String osName = environment.getProperty("os.name");
    System.out.println(osName);

    String[] names = context.getBeanNamesForType(Person.class);
    Arrays.stream(names).forEach(System.out::println);

    Map<String, Person> beans = context.getBeansOfType(Person.class);
    System.out.println(beans);
}

接下來,我們運行SpringBeanTest類中的testAnnotationConfig6()方法,輸出的結果信息如下所示。

Windows 10
person
binghe001
binghe002
給容器中添加Person....
{person=Person(name=binghe002, age=18), binghe001=Person(name=binghe001, age=18), binghe002=Person(name=binghe002, age=20)}

由於我使用的操作系統是Windows 10操作系統,所以在結果信息中輸出了Windows 10。

到這裏,我們成功獲取到了操作系統的類型,接下來,就可以實現:如果當前操作系統是Windows操作系統,則向Spring容器中註冊binghe001;如果當前操作系統是Linux操作系統,則向Spring容器中註冊binghe002的需求了。此時,我們就需要藉助Spring的@Conditional註解來實現了。

要想使用@Conditional註解,我們需要實現Condition接口來為@Conditional註解設置條件,所以,這裏,我們創建了兩個實現Condition接口的類,分別為WindowsCondition和LinuxCondition,如下所示。

  • WindowsCondition
package io.mykit.spring.plugins.register.condition;


import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @author binghe
 * @version 1.0.0
 * @description Windows條件,判斷操作系統是否是Windows
 */
public class WindowsCondition implements Condition {
    /**
     * ConditionContext:判斷條件使用的上下文環境
     * AnnotatedTypeMetadata:註釋信息
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //判斷是否是Linux系統
        //1.獲取到IOC容器使用的BeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2.獲取類加載器
        ClassLoader classLoader = context.getClassLoader();
        //3.獲取當前的環境信息
        Environment environment = context.getEnvironment();
        //4.獲取bean定義的註冊類,我們可以通過BeanDefinitionRegistry對象查看
        //Spring容器中註冊了哪些bean,也可以通過BeanDefinitionRegistry對象向
        //Spring容器中註冊bean,移除bean,查看bean的定義,查看是否包含某個bean的定義
        BeanDefinitionRegistry registry = context.getRegistry();
        String property = environment.getProperty("os.name");
        return property.contains("Windows");
    }
}
  • LinuxCondition
package io.mykit.spring.plugins.register.condition;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @author binghe
 * @version 1.0.0
 * @description Linux條件,判斷操作系統是否是Linux
 */
public class LinuxCondition implements Condition {
    /**
     * ConditionContext:判斷條件使用的上下文環境
     * AnnotatedTypeMetadata:註釋信息
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //判斷是否是Linux系統
        //1.獲取到IOC容器使用的BeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2.獲取類加載器
        ClassLoader classLoader = context.getClassLoader();
        //3.獲取當前的環境信息
        Environment environment = context.getEnvironment();
        //4.獲取bean定義的註冊類,我們可以通過BeanDefinitionRegistry對象查看
        //Spring容器中註冊了哪些bean,也可以通過BeanDefinitionRegistry對象向
        //Spring容器中註冊bean,移除bean,查看bean的定義,查看是否包含某個bean的定義
        BeanDefinitionRegistry registry = context.getRegistry();
        String property = environment.getProperty("os.name");
        return property.contains("linux");
    }
}

接下來,我們就需要在PersonConfig2類中使用@Conditional註解添加條件了。添加註解后的方法如下所示。

@Conditional({WindowsCondition.class})
@Bean("binghe001")
public Person person01(){
    return new Person("binghe001", 18);
}

@Conditional({LinuxCondition.class})
@Bean("binghe002")
public Person person02(){
    return new Person("binghe002", 20);
}

此時,我們再次運行SpringBeanTest類中的testAnnotationConfig6()方法,輸出的結果信息如下所示。

Windows 10
person
binghe001
給容器中添加Person....
{person=Person(name=binghe002, age=18), binghe001=Person(name=binghe001, age=18)}

可以看到,輸出結果中不再含有名稱為binghe002的bean了,說明程序中檢測到當前操作系統為Windows10,沒有向Spring容器中註冊名稱為binghe002的bean。

@Conditional註解也可以標註在類上,標註在類上含義為:滿足當前條件,這個類中配置的所有bean註冊才能生效,大家可以自行驗證@Conditional註解標註在類上的情況

@Conditional的擴展註解

@ConditionalOnBean:僅僅在當前上下文中存在某個對象時,才會實例化一個Bean。
@ConditionalOnClass:某個class位於類路徑上,才會實例化一個Bean。
@ConditionalOnExpression:當表達式為true的時候,才會實例化一個Bean。
@ConditionalOnMissingBean:僅僅在當前上下文中不存在某個對象時,才會實例化一個Bean。
@ConditionalOnMissingClass:某個class類路徑上不存在的時候,才會實例化一個Bean。
@ConditionalOnNotWebApplication:不是web應用,才會實例化一個Bean。
@ConditionalOnBean:當容器中有指定Bean的條件下進行實例化。
@ConditionalOnMissingBean:當容器里沒有指定Bean的條件下進行實例化。
@ConditionalOnClass:當classpath類路徑下有指定類的條件下進行實例化。
@ConditionalOnMissingClass:當類路徑下沒有指定類的條件下進行實例化。
@ConditionalOnWebApplication:當項目是一個Web項目時進行實例化。
@ConditionalOnNotWebApplication:當項目不是一個Web項目時進行實例化。
@ConditionalOnProperty:當指定的屬性有指定的值時進行實例化。
@ConditionalOnExpression:基於SpEL表達式的條件判斷。
@ConditionalOnJava:當JVM版本為指定的版本範圍時觸發實例化。
@ConditionalOnResource:當類路徑下有指定的資源時觸發實例化。
@ConditionalOnJndi:在JNDI存在的條件下觸發實例化。
@ConditionalOnSingleCandidate:當指定的Bean在容器中只有一個,或者有多個但是指定了首選的Bean時觸發實例化。

@Conditional 與@Profile 的對比

Spring3.0 也有一些和@Conditional 相似的註解,它們是Spring SPEL 表達式和Spring Profiles 註解 Spring4.0的@Conditional 註解要比@Profile 註解更加高級。@Profile 註解用來加載應用程序的環境。@Profile註解僅限於根據預定義屬性編寫條件檢查。 @Conditional註釋則沒有此限制。

Spring中的@Profile 和 @Conditional 註解用來檢查”If…then…else”的語義。然而,Spring4 @Conditional是@Profile 註解的更通用法。

  • Spring 3中的 @Profile僅用於編寫基於Environment變量的條件檢查。 配置文件可用於基於環境加載應用程序配置。
  • Spring 4 @Conditional註解允許開發人員為條件檢查定義用戶定義的策略。 @Conditional可用於條件bean註冊。

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

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

寫在最後

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

參考:
https://www.cnblogs.com/cxuanBlog/p/10960575.html

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

數據誤操作,教你使用ApexSQLLog工具從 SQLServer日誌恢複數據!

前幾天同事不小心誤操作,將SQLServer庫的一張表的一個狀態字段給刷成了一個統一狀態,由於是update執行所以原來的相關狀態無法確定。發生這種事情的時候我的小夥伴背後 一涼。

由於是在開發試運行中的項目,還沒來得及進行備份處理,所以從備份恢復宣告失敗。就算有備份那麼恢復的也是備份時間節點的數據,意味着使用平台做的數據需要從備份時間重新做過,而且有可能有遺漏。

小夥伴問我這咋辦,首先沒有備份,那麼只有從數據庫日誌查找,然後看能不能通過日誌找回之前的數據,再還原到刷狀態之前的數據。然後就找到了ApexSQLLog工具,接下來我介紹下這款工具的使用和如何恢複數據。ApexSQLLog有幾個版本,我是用的是ApexSQLLog2014支持SqlServer更高的版本,數據庫使用的是SqlSerVer2014。

ApexSQLLog2014
提取碼: np4f

  • 首先建一個測試庫,和一張測試表。
    測試庫ApexSQLLogTest和測試用的表TestUser,然後我手動編輯了三條數據進去,保存編輯的數據。

  • 用ApexSQLLog打開測試庫日誌
    選擇要連接的數據庫,也可以從最近的session中打開,打開篩選過的記錄可以保存未session。
    然後選中要篩選的日誌文件,如果有備份數據庫文件也會自動查找到並在這裏羅列出來,自己按情況選擇。

  • 條件篩選
    我們選擇日誌文件后就進入到篩選條件選擇,可以在篩選條件裏面自由組合。
    可以選擇時間段(Time range)、操作(operations)、表(tables)。

    高級選項(advanced options)裏面還有用戶、字段條件等可以選擇。

  • 查看日誌數據
    當我們組合完篩選條件后,就進入到日誌分析界面,可以看到我們之前手動插入的三條數據實際已經在日誌裏面了,分成了三條insert語句。在選中其中一條日誌的時候在下面可以看到執行的各字段值的修改情況。

    可以點擊 下面的Row history查看記錄,Redo script可以生成執行的操作, Undo script可以還原到之前的數據。我們恢複數據就是使用Undo script。

  • 恢複數據測試。
    我們使用update語句將Status狀態全都重置為3。

update TestUser set Status=3 

然後刷新下日誌,會看到多出了三條Update日誌記錄,點擊第一條看到下面的Status字段從0變為了3。

我們選中這三條記錄右鍵或者上面的菜單欄功能,用create undo script 生成恢復sql。

--	This UNDO script was generated with ApexSQL Log 2014.04.1133 on 2020-06-10 11:18:47.601
--	NOTE: Operations in UNDO scripts are always output in descending order.
--	SERVER VIP-966\SQLEXPRESS
--	DATABASE ApexSQLLogTest

--	UPDATE (00000024:000000A0:0004) done at 2020-06-10 11:09:36.293 by VIP-966\Administrator in transaction 0000:0000034B (Committed)
BEGIN TRANSACTION 
UPDATE [dbo].[TestUser] SET [Status] = 2 WHERE [Id] = 3
IF @@ROWCOUNT <= 1 COMMIT TRANSACTION ELSE BEGIN ROLLBACK TRANSACTION; PRINT 'ERROR: STATEMENT AFFECTED MORE THAN ONE ROW. ALL THE CHANGES WERE ROLLED BACK.' END
--	UPDATE (00000024:000000A0:0003) done at 2020-06-10 11:09:36.293 by VIP-966\Administrator in transaction 0000:0000034B (Committed)
BEGIN TRANSACTION 
UPDATE [dbo].[TestUser] SET [Status] = 1 WHERE [Id] = 2
IF @@ROWCOUNT <= 1 COMMIT TRANSACTION ELSE BEGIN ROLLBACK TRANSACTION; PRINT 'ERROR: STATEMENT AFFECTED MORE THAN ONE ROW. ALL THE CHANGES WERE ROLLED BACK.' END
--	UPDATE (00000024:000000A0:0002) done at 2020-06-10 11:09:36.293 by VIP-966\Administrator in transaction 0000:0000034B (Committed)
BEGIN TRANSACTION 
UPDATE [dbo].[TestUser] SET [Status] = 0 WHERE [Id] = 1
IF @@ROWCOUNT <= 1 COMMIT TRANSACTION ELSE BEGIN ROLLBACK TRANSACTION; PRINT 'ERROR: STATEMENT AFFECTED MORE THAN ONE ROW. ALL THE CHANGES WERE ROLLED BACK.' END
GO

--	FINISHED ON 2020-06-10 11:18:47.697
--	TOTAL OPERATIONS PROCESSED 3
--	END OF FILE

最後我們就可以使用這個腳本去恢複數據了。

注意

我們在使用日誌恢復的時候如果表有主鍵會根據主鍵生成sql,如上圖sql中 where後面的條件。如果表沒有主鍵那麼生成的sql後面的where條件會帶上所有的字段。在我幫小夥伴恢複數據的時候發現他的表沒有設置主鍵,而且字段有20多個,3萬多條數據生成的sql都是100多M,還要拆分執行。
比如我把Id主鍵去了再更新下Status狀態到4,生成的sql如下,會提示沒有主鍵。

以上就是一次數據恢復的分享,如果下次你也遇到這種情況希望能幫到你。

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

微服務中的網關

什麼是網關

  簡單點說網關是一個Api服務器,是系統的唯一入口。為每個客戶端提供一個定製的Restful API。同時它還需要具有一些業務之外的責任:鑒權。靜態響應等處理。

為什麼需要gateway

  我們知道我們要進入一個服務本身,並不是一件容易的事情。服務本身有自己的通訊協議,這種協議往往不能很好的兼容各個客戶端的需求,所以我們只能尋找一種公共協議:http。所以網關的概念就誕生了。如圖:

  

gateway的作用

  所以網關的最主要在作用就是路由的轉發 。但是 在我們平時的使用過程中,直接請求http 協議的 api 會存在很多問題。例如:安全問題,流量問題  等等。所以gateway 還需要做一些額外的 事情來保證我們的流程是安全的、可靠的。

使用網關

  現在已經出現了很多網關的產品:比如 Ocelot ,zuul ,Spring Cloud Gateway,kong 等等

  今天我們要 介紹的網關就是  kong

kong 在docker中的部署

創建一個kong的網絡

  docker network create kong-net

創建數據庫 pgsql  

docker run -d --name kong-database --network=kong-net  -p 5432:5432  -e "POSTGRES_USER=kong" -e "POSTGRES_DB=kong"  -e "POSTGRES_PASSWORD=qwe123QWE"  postgres:9.6

 數據遷移  

docker run --rm \
      --network=kong-net \
      -e "KONG_DATABASE=postgres" \
      -e "KONG_PG_HOST=kong-database" \
      -e "KONG_PG_PASSWORD=qwe123QWE" \
      -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
      kong:latest kong migrations bootstrap

啟動kong  

docker run -d --name kong \
   --network=kong-net \
  -e "KONG_DATABASE=postgres" \
  -e "KONG_PG_HOST=kong-database" \
  -e "KONG_PG_PASSWORD=qwe123QWE" \
  -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
  -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
  -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
  -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
  -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
  -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
  -p 8000:8000 \
  -p 8443:8443 \
  -p 8001:8001 \
  -p 8444:8444 \
  kong

瀏覽器輸入:http:kongip:8001,會出現json字符,表示成功

安裝konga

數據遷移

docker run --network=kong-net --rm pantsel/konga:latest -c prepare -a postgres -u postgresql://kong:qwe123QWE@kong-database:5432/konga

啟動konga

docker run 
-p 1337:1337 \
--network kong-net \
--name konga \
-e "NODE_ENV=production" \
-e "DB_ADAPTER=postgres" \
-e "DB_URI=postgresql://kong:qwe123QWE@kong-database:5432/konga" \
pantsel/konga

創建鏈接的Admin URL地址可以使用kong創建的網絡。例如:http://kong:8001

 如圖:

 然後可以自行編輯自己的 serevices 與service對應的route  訪問:http:kongip:8000/路由  就可以訪問你的  api啦

最後安利一波

ketchup 是一個微服務框架  它集成了網關kong,通過簡單的配置,可以自行註冊到kong的網關中,大大減少了開發的時間。

源碼地址:https://github.com/simple-gr/ketchup

操作文檔  請見:https://github.com/simple-gr/ketchup/wiki/%E7%BD%91%E5%85%B3。

qq 交流群:592407137

 

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

看完這三款再考慮買汽油還是新能源車吧?

雷凌的外觀設計要比卡羅拉更顯年輕動感一些,1。2T的發動機最大馬力116匹,峰值扭矩185牛米。國家工信部對於豐田雷凌1。2T的油耗測試結果显示為5。7L/100km,而根據現任車主的反應,綜合油耗也是在6。5-7。0L/100km,比其他車型更具優勢的是,雷凌所使用的1。

基於以上種種的原因還是考慮合資車型,而一款油耗低,售價相對親民的合資轎車肯定更讓人覺得具有吸引力,但就目前來說,還是有不少消費者不太能接受純電動和混合動力車型;本次就推薦幾款油耗低,售價也相對能夠讓人接受的合資家用轎車。

標緻308 230THp

指導價格:10.97-13.37萬

標緻308的外觀更新讓人欣喜,家族式前臉設計更有精神,車身整體線條也更加和諧流暢,採用1.2T的標緻308最大馬力136匹,峰值扭矩230牛米。

工信部油耗显示1.2T的標緻308綜合耗油5.2L/100km左右,根據已經購入標緻308 230THp車型的車主反映,普遍油耗在6.5-7.5L/100km左右,油耗並不顯得太高,基礎排量小為其奠定了根基。

廣汽豐田雷凌1.2T

指導價格:10.98-13.38萬

豐田集團繼混合動力車型推出以後,在雷凌和卡羅拉車型上繼續細分了1.2T的發動機版本以供消費者選擇。雷凌的外觀設計要比卡羅拉更顯年輕動感一些,1.2T的發動機最大馬力116匹,峰值扭矩185牛米。

國家工信部對於豐田雷凌1.2T的油耗測試結果显示為5.7L/100km,而根據現任車主的反應,綜合油耗也是在6.5-7.0L/100km,比其他車型更具優勢的是,雷凌所使用的1.2T是一款4缸發動機,平順性更出色。

一汽大眾高爾夫230TSI

指導價格:14.09-15.79萬

大眾高爾夫是家用車層面銷量非常高的一款緊湊型轎車,市場上曝光率很高,人們對它也非常熟悉,外觀造型顯得大氣簡潔,接受人群的年齡覆蓋面非常廣,230TSI所搭載的1.4T EA211發動機最大馬力131匹,峰值扭矩225牛米。

工信部對高爾夫230TSI車型的油耗測試綜合油耗显示5.9L/100km,根據現任車主普遍的口碑放映,230TSI的自動車型綜合油耗在7.5L/100km左右,高爾夫的優勢在於更符合大眾審美的汽車造型,以及更出色的操控性。

全文總結:以上三款車型都是各自車系中油耗較低的選擇,而標緻308凸顯的是個性,雷凌體現的是平實家用,高爾夫的優點在於紮實的底盤感受以及優良的操控性,基本上可以符合訴求各自不同的消費者。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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

離夢想又近了一丟丟 保時泰新款SUV也許下探10萬以內

由於SR9的自重過大,所以它並沒有使用T600的1。5T發動機,這款1。8T發動機預計將會和Z700的那款1。8T發動機型號一致。Z700的1。8T發動機最大馬力為177馬力,最大扭矩為245牛·米,變速箱為5擋手動和6擋雙離合。SR9很可能也會搭載此套動力系統。

保時捷卡宴一直是小編的夢想之車,但是百萬的售價讓太多人望而卻步。不過保時捷嘗到了SUV的甜頭,為了繼續擴大市場,推出了售價更低的Macan,Macan一推出就獲得了巨大的成功,訂單也是絡繹不絕。

雖然Macan更便宜了,但是,作為一個普通的搬磚工(不是香港的搬磚工),Macan還是距離我太遙遠,也許我這類搬磚工一輩子也開不上Macan。

但是只要有需求,必定會有企業來開拓市場,眾泰不負眾望研發出SR9,直接將“Macan”的價格做到了10.88萬。一時間坊間的小夥伴們奔走相告,喜極而泣,終於可以買到心愛的“Macan”了。不過即使是這個價格也有不少消費者覺得貴了。

作為豪車“價格殺手”的眾泰豈能善罷甘休?目前眾泰又打算推出了SR9的1.8T車型,也就是說如果你覺得10.88萬元的價格有點貴,那麼接下來這款車就可以滿足你的心愿了。

現款的SR9全係為2.0T 190馬力發動機,匹配5擋手動和6擋雙離合變速箱,售價區間為10.88-16.18萬元。眾泰為了豐富SR9的動力系統,提供更多樣化的選擇,同時也為了更大程度的降低售價,增強SR9的競爭力,果斷推出1.8T車型。使得SR9的價格進一步下探。

雖然眾泰官方沒有明確的公布1.8T發動機的具體參數,但是可以肯定的是價格更便宜的1.8T的SR9將會在2017年第四季度上市。由於SR9的自重過大,所以它並沒有使用T600的1.5T發動機,這款1.8T發動機預計將會和Z700的那款1.8T發動機型號一致。

Z700的1.8T發動機最大馬力為177馬力,最大扭矩為245牛·米,變速箱為5擋手動和6擋雙離合。SR9很可能也會搭載此套動力系統。

如果真的是這套動力系統的話那也就不用擔心動力輸出不夠了,這要動力系統還是可以可以滿足SR9的日常行車了。

SR9自從誕生之後就備受爭議,但是SR9憑藉著炫酷的外觀,極其豐富的配置和非常實惠的終端售價,吸引了一大批粉絲。況且,1.8T車型問世之後,SR9的起售價很有可能做到9萬左右,這樣會使得SR9的競爭力更上一層樓,銷量肯定會進一步增長。但是SR9也不是沒有對手,比如它們,熱度也很大。

陸風X7

指導價:12.98-14.78萬

X7的車身尺寸為4420*1910*1630mm,軸距為2670mm,定位緊湊型SUV。雖然SR9有“某保”護體,但是X7也有“某虎”助陣,同時X7全係為2.0T發動機,變速箱為8AT,這點也是比較厲害的。而且X7在坊間的名氣也是比較大的。

眾泰T600

指導價:7.98-14.98萬

誰說親兄弟就不能“互相傷害”,大家都是在激烈的市場裏面競爭,誰能取得熱銷就看誰的“活更好了”。T600在10月份交出了11508輛的銷量,表現非常好。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※台北網頁設計公司全省服務真心推薦

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

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

都在看爆款,15萬我挑的SUV關注的人不多,但顏值高質量好!

觀致汽車 觀致5售價:13。99-19。49萬元作為一個向高端進發的自主品牌,觀致的路走得一般。其中一大部分的原因是其較高的定價與較低的知名度,使得消費者對其認可比較一般。而且較少的4S店使得覆蓋較低也是一個原因。它的外觀不太像SUV車型,更像一款跨界車。

目前的SUV市場,雖然有着合資品牌不斷地進入市場,但在10-15萬這個區間仍是自主品牌佔據着領先的地位。由於這個價格區間是自主品牌的必爭之地,因此各家都在推出實力出眾的車型都與其他品牌一較高下。今天所推薦的這幾款,雖說不是目前市場最火的那幾款,但它們就真的不行么?且聽細細道來。

東風乘用車 東風風神AX7

售價:9.97-14.17萬元

東風乘用車一直都是多生孩子好打架的套路,風神AX7作為一輛原創度很高的車型自從上市以來就受到不少消費者的追捧。不僅由於它親民的定價,還有較大的車身尺寸。

它的前臉採用簡潔的造型設計,美觀耐看。多邊形進氣格柵採用橫向鍍鉻裝飾,整體質感出眾。碩大的X保險桿氣場很足,配以碩大的車身,至少第一印象就很深刻。微微上揚的腰線向後延伸與尾燈相融,流暢自然。車尾同樣採用簡潔的設計與前臉呼應。

相對於它的外觀讓人眼前一亮,AX7的內飾造型則相對比較傳統一些。規整的中控台最大的亮色就是突起的多媒體系統了;其中配以鍍鉻裝飾點綴,內飾整體比較年輕。而且AX7相對較大的車身尺寸,使得它的車內空間比較寬敞,也是它的優勢所在。

東風風神AX7的動力非常豐富,相比競爭對手而已;消費者有更多的選擇空間。除了1.4T車型之外,2.0L/2.3L都是採用了標緻旗下發動機,技術成熟可靠。1.4T的動力相當出色,遺憾的是目前只有手動車型;便利性差了點。2.0L與2.3L的車型則除了手動車型之外還配以6AT,日常行駛動力出色平順。懸挂的調校也相當有功力,整體感很強的底盤駕駛起來比較有信心。

奇瑞汽車 瑞虎7

售價:9.79-15.39萬元

奇瑞汽車作為一個自主品牌而已,這麼多年在合資林立的汽車市場里生存也不容易。能一直活得這麼滋潤,自然是它的產品比較出眾了。但之前的車型的外觀都是中規中矩,這一次瑞虎7可謂是出了一口惡氣。

作為奇瑞新研發平台T1X的首款SUV車型,瑞虎7是一款非常重要的產品。它的外觀原創度非常高;層次感豐富的前臉造型一下子就抓住了年輕人的眼球。凹凸有致的線條設計也時尚感很強。波浪式造型的車身線條很有設計感,而且轉向燈融入車身的設計很少見。

它的內飾造型相比目前最火的車型可能會稍遜一籌,但這是因為瑞虎7是在5年前就開始研發。以今天的目光來看,仍然非常好看,就是設計感差點。層次感較強的中控採用活潑的配色,黑棕布局亮點十足。而且瑞虎7的空間實用性也非常出色,後排配備了空調出風口。

瑞虎7搭載的了2.0L與1.5T的發動機,2.0L的車型動力輸出一般,搭載CVT更多的是為了日常的平順性需求的。1.5T則有兩種調校,動力的輸出更加直接充沛一些。日常行駛,這套動力總成做得很不錯,2.0L平順性很好,能滿足代步需求。1.5T車型則駕駛起來更輕鬆一些,變速箱涵接聰明。配上韌性十足的底盤調校,屬於同級一款很出眾的車型。

觀致汽車 觀致5

售價:13.99-19.49萬元

作為一個向高端進發的自主品牌,觀致的路走得一般。其中一大部分的原因是其較高的定價與較低的知名度,使得消費者對其認可比較一般。而且較少的4S店使得覆蓋較低也是一個原因。

它的外觀不太像SUV車型,更像一款跨界車。敦實圓潤的車身造型很獨特。它的前臉非常具有辨識度,設計感觸手的格柵布滿了中國風的設計語言,很有特色。碩大的鍍鉻裝飾也不會顯得很多餘。流線型的車身線條配上一個大尺寸的輪轂,很吸人眼球。車尾造型則相對簡單一些。

它的內飾造型則採用了簡潔的設計,簡單的線條勾勒出層次感強烈的中控台。平整的中控台看上去比較清爽,很適合年輕人的胃口。而它的乘坐空間與儲物空間都都相當出色,假如價格再降一些,很值得購買。

觀致5全系只搭載了一台1.6T的發動機,156ps的賬面數據表現還是可以。除了手動車型之後搭載是6擋雙離合;日常行駛動力輸出比較平順,只是雙離合變速箱為了更好的平順性,降擋會稍微有所猶豫。韌性的懸挂配以輕盈的轉向,日常開起來很輕鬆。

上汽集團 銳騰

銳騰與RX5同屬上汽旗下的SUV車型,雖然銳騰上市時間早很多,但熱度總體來說比RX5還是差了些。但這款個性前衛的SUV確實很出眾,如今外觀造型變得更加的美觀;不太那麼個性。

相比老款而言,新款銳騰變化不大。雖然變化不多,但整體呈現出來的效果非常出色。前臉的重心下移之後,變得更加美觀。經過重新調整的上下進氣格柵使得前臉更加協調。細節上,銳騰在霧燈區域設計了鋼爪設計,很有特色。車身的線條則變化較少,車尾同樣降低了重心,鋼爪的設計與前臉來了呼應。

銳騰的內飾造型變化很大,相比老款而言,減少了塑料感增加了檔次。造型規整的中控層次感豐富,上層採用鍍鉻裝飾條,接觸較多的區域都採用了皮革包裹,質感很出眾。乘坐空間整體處在主流水準,儲物空間豐富。遺憾的就是座椅較硬,長途乘坐會有點累。

作為改款車型,銳騰的動力則變化不大。依然還是1.5T/2.0T發動機與手動變速箱與雙離合的搭配。前麥弗遜后多連桿獨立懸挂使得銳騰天生就比競爭對手好不少,而銳騰呈現出就是底盤的支撐性很好,運動感會強一些。1.5T/2.0T發動機動力充沛,日常行駛比較輕鬆,變速箱涵接較好。

風神AX7顏值很帥氣,除了主打性價比之外;它寬敞的空間與成熟的動力總成都是它的亮點,相信只要多點宣傳;未來的銷量會更好。瑞虎7作為承載奇瑞汽車重任的一款SUV,如今已經開始展露自己的實力。要在眾多對手當中崛起,不僅品質出眾,未來的宣傳與口碑也有過關。

觀致5作為一個新品牌的SUV車型,外觀就不用多說了;很出眾。整體的配置與空間都在這個級別的主流水準,恰巧價格高了很多;4S覆蓋偏少也是它弱勢;希望未來能在這方面加強一下;價格再有所降低仍是一款好車。最後的銳騰,新款的外觀造型變得更加協調,不再是年輕人的專屬。整體的配置都相當出色,宣傳得好又是一款銷量高的車型。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

不忽悠直接試!這輛車為什麼讓很多人慾罷不能?

對於在城市裡比較緊張的停車場停車,這就是避免剮蹭的一大保障。不過通過後視攝像頭集成倒車影像在這個價位的車裡不算特別稀奇,新凌派的安全配置才是最大的亮點。全系標配VSA車身穩定控制系統、HSA斜坡起步輔助系統、ESS緊急剎車警示系統及領先搭載LWC盲點显示系統四大安全科技。

又到年底,一年中大家的荷包最豐厚的時節,於是買車成為了不少人的心頭大事。後台關於車型的詢問量也與日俱增,搞得最近黃金右手有點撐不住。正好某天看到有個粉絲問能不能說說凌派,更妙的是恰好咱們手上有輛凌派試駕車,與其用黃金右手碼字說,不如直接上黃金右腳來得直接。於是一不做二不休乾脆勾搭上這位粉絲,帶着他共同試駕新凌派,讓他親自感受一番這車究竟如何!

這一次我們試駕的車型是次頂配,香檳雅金的凌派,這是新凌派的主打車色,路上見到的也多,可見大家對“土豪金”的喜愛。

接着另一位主角登場,正是邀請來試駕的粉絲——阿聰。

外觀——“時尚中不失個性”

阿聰首先感受了一番新凌派的外觀,他直言自己算是個“顏控”。而且相比中規中矩的卡羅拉、雷凌,他更喜歡凌派“時尚中不失個性”的外觀。

覺得,阿聰這個描述很到位。如果把同級車型比成一個班級,凌派就是班級里打扮最前衛,每次出現在教室門口全班同學都會抬頭看的那位。前臉十分特別,“大嘴”前格柵與“大眼睛”一般的大燈組成了獨特的“笑模樣”。側面刀鋒式的雙腰線和切削式的鋁合金輪轂造型充分展現個性,尾部採用運動型保險杠體現動感。可以說新凌派絕對是一輛辨識度非常高的車,恰恰滿足了像阿聰這樣的年輕人不想“泯然眾人已”的心理。

內飾——“簡約而不簡單”

進入車內,空調出風口、中控大屏、各個功能按鍵有序分佈,整體設計是一貫的本田風格,簡潔、實用、時尚。內飾用料也比較精細,手觸摸上去軟硬適中,完全沒有塑料感,相比很多同級車型,新凌派在這個價位做出了質感,這是非常讓人驚喜的地方。

在給阿聰對內飾做了一番講解后,阿聰瞭然的點頭,一句話總結了要表達的意思:“簡約而不簡單。”他想要的也正是這樣內飾布局簡單明了,一坐進去就非常容易上手操作的車。

空間——“夠大夠舒服”

體驗了內飾后,問阿聰對車的空間有些什麼要求。阿聰說主要是平時上下班代步,去運動,周末和朋友短途自駕,所以乘坐空間要夠大夠舒服,後備箱最好能多裝點。聽完就覺得難怪他會偏愛新凌派了。

擁有4664mm的超長車身和2650mm的軸距優勢,加上本田大師水準的空間利用技術,及本田獨有的MM理念的運用,新凌派無論前後排空間都非常寬敞。為了讓阿聰更加深刻的體驗到新凌派的空間,上陣教了他一套“媒體老濕”用的空間測試法。阿聰身高175,坐進新凌派後頭部空間一拳半,腿部空間遠遠超過兩拳,完全能夠舒服的乘坐。

後備箱空間就更能滿足阿聰的需求了。無論深度還是寬度都很夠,放了一摞又一摞物料后,阿聰整個人還能愜意的躺進後備箱,這個展現後備箱寬敞程度的招式,也是服氣。

配置——“亮點滿滿”

當然阿聰更關心的還有凌派的配置。上車之前他就親自體驗了新凌派的無鑰匙進入系統,只要攜帶鑰匙靠近,車門就會自動解鎖。阿聰覺得這個功能非常酷。

而從車內分佈的各個按鍵也能知道配置的豐富程度。尤其一鍵啟停、小綠恭弘=叶 恭弘也深得阿聰這個90后的歡心,既體現高科技,又保證經濟性。

細心的阿聰還發現,新凌派配備的倒車影像具有三模式視角,分別是廣角、常規和高角度。對於在城市裡比較緊張的停車場停車,這就是避免剮蹭的一大保障。

不過通過後視攝像頭集成倒車影像在這個價位的車裡不算特別稀奇,新凌派的安全配置才是最大的亮點。全系標配VSA車身穩定控制系統、HSA斜坡起步輔助系統、ESS緊急剎車警示系統及領先搭載LWC盲點显示系統四大安全科技。而LWC盲點显示系統更是本田的“黑科技”代表之一,在凌派的同級車型中更是絕無僅有。

LWC盲點显示系統使用非常簡單,只要按一下方向盤後方擋把上的啟動按鈕,就能啟動位於右後視鏡上的攝像頭,右後方的影像立即显示在中控大屏上。啟動這個功能后,右後方的視野能從20%擴大到80%,消除肉眼觀察時的盲區,並且帶有輔助線以供駕駛者判斷安全距離。

對這個逆天的功能阿聰表示,開車時右後方經常會有視線盲區,尤其在複雜路況行車時,切線或者轉彎都不免讓很多人的心都提一提,現在只需輕輕看一眼就能掌握右後方路況,開起車來會從容許多。甚至平時還可以來個創意自拍,新凌派的配置不僅豐富,還亮點滿滿。

只想說:果然是年輕人,會玩!

動力——“舒適之餘也談激情”

動力上凌派搭載的是1.8L自然吸氣發動機,擁有本田看家的i-VTEC系統,匹配CVT無級變速器,最大功率100kW,最大扭矩169N·m,參數表現是非常不錯的。而阿聰體驗過後實際是什麼感受呢?

剛把車發動起來,阿聰就感受到凌派強勁的動力了。不同於很多車經常會有起步比較“肉”的感受,本田的可變氣門正時系統讓發動機即使在低扭矩時也能保證足夠的動力輸出。

一天的相處下來,發現阿聰是一個個性又不失內斂的男生,開車風格是“暖男派”,不爭不搶。在市區駕駛時開啟ECON模式是非常適合他的,兼顧舒適性和經濟性。而當他偶爾想要來點激情時,讓他切到S擋,深踩油門感受了一番新凌派的加速,油門與剎車響應都比較靈敏,阿聰開起來完全游刃有餘。

最後還帶着阿聰來了一次劈彎。在比較高速的情況下轉彎,凌派的側傾只會讓你恰到好處的感覺到車身在過彎,並不會有傾斜幅度過大的感覺,車尾的循跡性也不錯。可見新凌派底盤是主打舒適的調校。用阿聰的話來說,新凌派給他的感覺非常“穩”。

最終一天試駕下來的油耗也讓阿聰更“穩”了,他開出了百公里6.8L的超低油耗,看來黃金右腳的稱號要拱手讓人了。

分別前問了阿聰最後一個,也是試駕前阿聰本人帶來的問題:

“今天能不能讓你決定射出這臨門一腳買下凌派呢?”

阿聰回答說:

“本來就覺得新凌派很對胃口,在和一起試駕后,還發現了它獨特的LWC、強勁的動力、超大的空間等超越同級的亮點,可以說由內而外都非常適合我,一天下來我感覺和新凌派幾乎不用磨合,不禁產生一種“這就是我的車”的感覺。”

還是那句話,最適合你的車就是最好的車。像阿聰這樣的年輕人,一輛外觀時尚,配置豐富,大空間,動力足的十萬級別轎車就是最大的助力。而擁有1.5油耗,1.6價格,1.8動力,2.0空間的新凌派,阿聰試完之後已經找不出不買它的理由。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

用路虎給公司女同事一個聖誕驚喜

主角到場,似乎激動得說不出話了,這是要求婚的節奏。顏值擔當:小喬Jacky深情演唱。攝影師:費玉清-如果我是DJ。後期剪輯:子詠運營:苦瓜。帥X主持人:張濤靈魂字幕君:KAM知道你們嫉妒我的顏。我就靜靜的看你們裝逼。祝大家聖誕快樂。

眼看幾年即將結束,為了給公司勞苦功高的女同事一個浪漫的聖誕之夜,TV的小夥伴們精心策劃了一個聖誕party。

有夢想的男人:大宇宙哥

7200干他:Jacky

滿滿一車東西,滿滿的都是驚喜,出發!

我曾經跨過高山大海,現在就經常買買菜。

包裝師:雞爺

悉心裝飾現場。

恩,小喬會不會感動就靠你了。

就等夜晚降臨。

主角到場,似乎激動得說不出話了,這是要求婚的節奏?

顏值擔當:小喬

Jacky深情演唱。

攝影師:費玉清-如果我是DJ。

後期剪輯:子詠

運營:苦瓜。

帥X主持人:張濤

靈魂字幕君:KAM

知道你們嫉妒我的顏。

我就靜靜的看你們裝逼。

祝大家聖誕快樂!

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

【其他文章推薦】

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

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

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

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

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

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

情報局:大排量發動機都狗帶了,霧霾還會繼續嗎?

未來,奧迪Q8的量產版還可能搭載3。0T V6汽油發動機或4。0T V8柴油發動機,並且會配備奧迪最新的48V電氣系統。(奧迪的設計師不是都跑光了。)中興手機也造車日前,中興通訊通過收購珠海廣通客車,獲得了進入汽車市場的資質以及傳統造車技術。

奔馳E180

日前,有媒體拍攝到了北京奔馳E 180 L的無偽裝實車圖,外觀與在售E級加長保持一致,內飾最大的不同就是採用了傳統机械式指針儀錶盤。

動力方面將搭載一台1.6T發動機,最大功率為115kW,峰值扭矩250牛米,與發動機匹配的是7速手自一體變速箱。預計將於2017年春季上市,將會成為國產全新E級的入門車型,售價也將進一步下探。(或將掀起一股新的扣標潮)

紅旗N501

近日,有媒體曝光一組紅旗全新大型車的路試諜照。據悉,新車將基於紅旗L5的平台進行打造,內部代號為N501,外觀方面。。。與勞斯萊斯古斯特有些相似。

動力將會搭載全新的4.0T V8雙渦輪增壓發動機,還說沒有搭載6.0L V12自然吸氣發動機的計劃。

全新寶馬x3

據海外媒體報道,全新寶馬X3將於2017年8月正式發布,並在同年9月開幕的2017法蘭克福車展上面向全球消費者亮相,它將比現款的X3(F25)在車身尺寸上有全面的放大。

在中國還將實現國產加長。新車將搭載四缸和六缸發動機,此外還將推出性能車型X3 M40i、X3 M40d,以及純電動版本車型(也就是更大更粗更強)

奧迪Q8e-tron概念車

而奧迪則正式公布了Q8e-tron概念車的官圖,並且將在2017年1月的北美車展上展出發布。直接與寶馬X6以及奔馳GLE Coupe進行pK。

從名字來看,這款奧迪Q8概念車將會搭載一套純電動或者插電式混動系統。

未來,奧迪Q8的量產版還可能搭載3.0T V6汽油發動機或4.0T V8柴油發動機,並且會配備奧迪最新的48V電氣系統。(奧迪的設計師不是都跑光了?)

中興手機也造車

日前,中興通訊通過收購珠海廣通客車,獲得了進入汽車市場的資質以及傳統造車技術。

此前中興智能汽車副總經理曾表示爭取在五年時間內,商用車市場做到業內前五”。

另有中興通訊的工作人員表示,中興收購廣通客車主要是為了取得資質,目前新能源客車的訂單量對於整個公司的業績還不構成影響,收購廣通就是為了牌照,所以產能有多少,能夠獲得多少,在於國家政策補貼。。。(哦哦,這個我們知道了)

Top gear賽道被賣

最近,前英國BBC媒體報道稱電視汽車節目《TopGear》專用賽道即將轉變成為生活住宅區,準備開發1800至2600棟全新房屋。

在經過多年的鬥爭,最終經過英國Waverly理事會公布10比8的投票結果,決定將《TopGear》使用的測試賽道重新開發規劃成生活、商業住宅區。(是不是李嘉誠做的?)

沃德十佳發動機大排全軍覆沒

不久前,沃德十佳發動機2017年的評選公布了名單,除了純自吸動力全軍覆沒之外,那就是今年沒有一台V8發動機,最大排量只有3.6L,要知道沃德是美國人的機構,V8發動機在他們心中的地位就像中國的乒乓球一樣。

其實這個評選是對美國本土銷售的車型進行了接近日常駕駛狀況的測試后,根據每台發動機的馬力、扭矩、技術含量,以及實際使用中表現出來的工作特性進行全面評估,最終選出十台綜合表現最好的發動機。

最早始於1994年,在每年年末發布。整個評選過程有着非常明確的規則,首先獲得提名的發動機必須是批量生產,而且所裝備的汽車開始銷售的時間也有明確的規定。同時參評的發動機所裝備的汽車售價須低於6萬美元,(有些沒獲獎的發動機可能是車賣得太貴了),此次的獲獎發動機要麼是混合動力,要麼是渦輪增壓,雖然曾有人研究表明不能盲目相信沃德十佳發動機

有諸多“潛規則”,比如豪車賣得便宜(這TM誰都喜歡啊),奇葩發動機容易獲獎:像沃爾沃的机械渦輪雙增壓,眼看着汽車的混合動力越來越多,排量是越來越小,排放越來越好,限號越來越多,要是以後路上全是4缸混動車等霧霾出來的時候,誰會成為下一個污染源呢?

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

【其他文章推薦】

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

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

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

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

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