精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

高并發(fā)的“大殺器”:異步化、并行化

開(kāi)發(fā) 架構(gòu) 開(kāi)發(fā)工具
隨著互聯(lián)網(wǎng)的快速發(fā)展,高并發(fā)成為了大家架構(gòu)設(shè)計(jì)中遇到的一個(gè)難題。本文從異步化和并行化兩個(gè)方案中給大家介紹如何處理這個(gè)問(wèn)題。

高并發(fā)的大殺器:異步化

同步和異步,阻塞和非阻塞

同步和異步,阻塞和非阻塞,這幾個(gè)詞已經(jīng)是老生常談,但是還是有很多同學(xué)分不清楚,以為同步肯定就是阻塞,異步肯定就是非阻塞,其實(shí)他們并不是一回事。

同步和異步關(guān)注的是結(jié)果消息的通信機(jī)制:

  • 同步:調(diào)用方需要主動(dòng)等待結(jié)果的返回。
  • 異步:不需要主動(dòng)等待結(jié)果的返回,而是通過(guò)其他手段,比如狀態(tài)通知,回調(diào)函數(shù)等。

阻塞和非阻塞主要關(guān)注的是等待結(jié)果返回調(diào)用方的狀態(tài):

  • 阻塞:是指結(jié)果返回之前,當(dāng)前線(xiàn)程被掛起,不做任何事。
  • 非阻塞:是指結(jié)果在返回之前,線(xiàn)程可以做一些其他事,不會(huì)被掛起。

可以看見(jiàn)同步和異步,阻塞和非阻塞主要關(guān)注的點(diǎn)不同,有人會(huì)問(wèn)同步還能非阻塞,異步還能阻塞?

當(dāng)然是可以的,下面為了更好的說(shuō)明它們的組合之間的意思,用幾個(gè)簡(jiǎn)單的例子說(shuō)明:

同步阻塞:同步阻塞基本也是編程中最常見(jiàn)的模型,打個(gè)比方你去商店買(mǎi)衣服,你去了之后發(fā)現(xiàn)衣服賣(mài)完了,那你就在店里面一直等,期間不做任何事(包括看手機(jī)),等著商家進(jìn)貨,直到有貨為止,這個(gè)效率很低。

  • 同步非阻塞:同步非阻塞在編程中可以抽象為一個(gè)輪詢(xún)模式,你去了商店之后,發(fā)現(xiàn)衣服賣(mài)完了。

這個(gè)時(shí)候不需要傻傻的等著,你可以去其他地方比如奶茶店,買(mǎi)杯水,但是你還是需要時(shí)不時(shí)的去商店問(wèn)老板新衣服到了嗎。

  • 異步阻塞:異步阻塞這個(gè)編程里面用的較少,有點(diǎn)類(lèi)似你寫(xiě)了個(gè)線(xiàn)程池,submit 然后馬上 future.get(),這樣線(xiàn)程其實(shí)還是掛起的。

有點(diǎn)像你去商店買(mǎi)衣服,這個(gè)時(shí)候發(fā)現(xiàn)衣服沒(méi)有了,這個(gè)時(shí)候你就給老板留個(gè)電話(huà),說(shuō)衣服到了就給我打電話(huà),然后你就守著這個(gè)電話(huà),一直等著它響什么事也不做。這樣感覺(jué)的確有點(diǎn)傻,所以這個(gè)模式用得比較少。

  • 異步非阻塞:這也是現(xiàn)在高并發(fā)編程的一個(gè)核心,也是今天主要講的一個(gè)核心。

好比你去商店買(mǎi)衣服,衣服沒(méi)了,你只需要給老板說(shuō)這是我的電話(huà),衣服到了就打。然后你就隨心所欲的去玩,也不用操心衣服什么時(shí)候到,衣服一到,電話(huà)一響就可以去買(mǎi)衣服了。

同步阻塞 PK 異步非阻塞

上面已經(jīng)看到了同步阻塞的效率是多么的低,如果使用同步阻塞的方式去買(mǎi)衣服,你有可能一天只能買(mǎi)一件衣服,其他什么事都不能干;如果用異步非阻塞的方式去買(mǎi),買(mǎi)衣服只是你一天中進(jìn)行的一個(gè)小事。

我們把這個(gè)映射到我們代碼中,當(dāng)我們的線(xiàn)程發(fā)生一次 RPC 調(diào)用或者 HTTP 調(diào)用,又或者其他的一些耗時(shí)的 IO 調(diào)用。

發(fā)起之后,如果是同步阻塞,我們的這個(gè)線(xiàn)程就會(huì)被阻塞掛起,直到結(jié)果返回,試想一下,如果 IO 調(diào)用很頻繁那我們的 CPU 使用率會(huì)很低很低。

正所謂是物盡其用,既然 CPU 的使用率被 IO 調(diào)用搞得很低,那我們就可以使用異步非阻塞。

當(dāng)發(fā)生 IO 調(diào)用時(shí)我并不馬上關(guān)心結(jié)果,我只需要把回調(diào)函數(shù)寫(xiě)入這次 IO 調(diào)用,這個(gè)時(shí)候線(xiàn)程可以繼續(xù)處理新的請(qǐng)求,當(dāng) IO 調(diào)用結(jié)束時(shí),會(huì)調(diào)用回調(diào)函數(shù)。

而我們的線(xiàn)程始終處于忙碌之中,這樣就能做更多的有意義的事了。這里首先要說(shuō)明的是,異步化不是萬(wàn)能,異步化并不能縮短你整個(gè)鏈路調(diào)用時(shí)間長(zhǎng)的問(wèn)題,但是它能極大的提升你的最大 QPS。

一般我們的業(yè)務(wù)中有兩處比較耗時(shí):

  • CPU:CPU 耗時(shí)指的是我們的一般的業(yè)務(wù)處理邏輯,比如一些數(shù)據(jù)的運(yùn)算,對(duì)象的序列化。這些異步化是不能解決的,得需要靠一些算法的優(yōu)化,或者一些高性能框架。
  • IO Wait:IO 耗時(shí)就像我們上面說(shuō)的,一般發(fā)生在網(wǎng)絡(luò)調(diào)用,文件傳輸中等等,這個(gè)時(shí)候線(xiàn)程一般會(huì)掛起阻塞。而我們的異步化通常用于解決這部分的問(wèn)題。

哪些可以異步化

上面說(shuō)了異步化是用于解決 IO 阻塞的問(wèn)題,而我們一般項(xiàng)目中可以使用異步化的情況如下:

  • Servlet 異步化
  • Spring MVC 異步化
  • RPC 調(diào)用如(Dubbo,Thrift),HTTP 調(diào)用異步化
  • 數(shù)據(jù)庫(kù)調(diào)用,緩存調(diào)用異步化

下面我會(huì)從上面幾個(gè)方面進(jìn)行異步化的介紹。

Servlet 異步化

對(duì)于 Java 開(kāi)發(fā)程序員來(lái)說(shuō) Servlet 并不陌生,在項(xiàng)目中不論你使用 Struts2,還是使用的 Spring MVC,本質(zhì)上都是封裝的 Servlet。

但是我們一般的開(kāi)發(fā)都是使用的同步阻塞,模式如下:

 

上面的模式優(yōu)點(diǎn)在于編碼簡(jiǎn)單,適合在項(xiàng)目啟動(dòng)初期,訪(fǎng)問(wèn)量較少,或者是 CPU 運(yùn)算較多的項(xiàng)目。

缺點(diǎn)在于,業(yè)務(wù)邏輯線(xiàn)程和 Servlet 容器線(xiàn)程是同一個(gè),一般的業(yè)務(wù)邏輯總得發(fā)生點(diǎn) IO,比如查詢(xún)數(shù)據(jù)庫(kù),比如產(chǎn)生 RPC 調(diào)用,這個(gè)時(shí)候就會(huì)發(fā)生阻塞。

而我們的 Servlet 容器線(xiàn)程肯定是有限的,當(dāng) Servlet 容器線(xiàn)程都被阻塞的時(shí)候我們的服務(wù)這個(gè)時(shí)候就會(huì)發(fā)生拒絕訪(fǎng)問(wèn),線(xiàn)程不夠我當(dāng)然可以通過(guò)增加機(jī)器的一系列手段來(lái)解決這個(gè)問(wèn)題。

但是俗話(huà)說(shuō)得好靠人不如靠自己,靠別人替我分擔(dān)請(qǐng)求,還不如我自己搞定。

所以在 Servlet 3.0 之后支持了異步化,我們采用異步化之后,模式變成如下:

 

在這里我們采用新的線(xiàn)程處理業(yè)務(wù)邏輯,IO 調(diào)用的阻塞就不會(huì)影響我們的 Serlvet 了,實(shí)現(xiàn)異步 Serlvet 的代碼也比較簡(jiǎn)單,如下:

  1. @WebServlet(name = "WorkServlet",urlPatterns = "/work",asyncSupported =true
  2. public class WorkServlet extends HttpServlet{ 
  3.    private static final long serialVersionUID = 1L; 
  4.    @Override 
  5.    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  6.        this.doPost(req, resp); 
  7.    } 
  8.  
  9.    @Override 
  10.    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  11.        //設(shè)置ContentType,關(guān)閉緩存 
  12.        resp.setContentType("text/plain;charset=UTF-8"); 
  13.        resp.setHeader("Cache-Control","private"); 
  14.        resp.setHeader("Pragma","no-cache"); 
  15.        final PrintWriter writer= resp.getWriter(); 
  16.        writer.println("老師檢查作業(yè)了"); 
  17.        writer.flush(); 
  18.        List<String> zuoyes=new ArrayList<String>(); 
  19.        for (int i = 0; i < 10; i++) { 
  20.            zuoyes.add("zuoye"+i);; 
  21.        } 
  22.        //開(kāi)啟異步請(qǐng)求 
  23.        final AsyncContext ac=req.startAsync(); 
  24.        doZuoye(ac, zuoyes); 
  25.        writer.println("老師布置作業(yè)"); 
  26.        writer.flush(); 
  27.    } 
  28.  
  29.    private void doZuoye(final AsyncContext ac, final List<String> zuoyes) { 
  30.        ac.setTimeout(1*60*60*1000L); 
  31.        ac.start(new Runnable() { 
  32.            @Override 
  33.            public void run() { 
  34.                //通過(guò)response獲得字符輸出流 
  35.                try { 
  36.                    PrintWriter writer=ac.getResponse().getWriter(); 
  37.                    for (String zuoye:zuoyes) { 
  38.                        writer.println("\""+zuoye+"\"請(qǐng)求處理中"); 
  39.                        Thread.sleep(1*1000L); 
  40.                        writer.flush(); 
  41.                    } 
  42.                    ac.complete(); 
  43.                } catch (Exception e) { 
  44.                    e.printStackTrace(); 
  45.                } 
  46.            } 
  47.        }); 
  48.    } 

實(shí)現(xiàn) Serlvet 的關(guān)鍵在于 HTTP 采取了長(zhǎng)連接,也就是當(dāng)請(qǐng)求打過(guò)來(lái)的時(shí)候就算有返回也不會(huì)關(guān)閉,因?yàn)榭赡苓€會(huì)有數(shù)據(jù),直到返回關(guān)閉指令。

AsyncContext ac=req.startAsync();用于獲取異步上下文,后續(xù)我們通過(guò)這個(gè)異步上下文進(jìn)行回調(diào)返回?cái)?shù)據(jù),有點(diǎn)像我們買(mǎi)衣服的時(shí)候,留給老板一個(gè)電話(huà)。

而這個(gè)上下文也是一個(gè)電話(huà),當(dāng)有衣服到的時(shí)候,也就是當(dāng)有數(shù)據(jù)準(zhǔn)備好的時(shí)候就可以打電話(huà)發(fā)送數(shù)據(jù)了。ac.complete();用來(lái)進(jìn)行長(zhǎng)鏈接的關(guān)閉。

Spring MVC 異步化

現(xiàn)在其實(shí)很少人來(lái)進(jìn)行 Serlvet 編程,都是直接采用現(xiàn)成的一些框架,比如 Struts2,Spring MVC。下面介紹下使用 Spring MVC 如何進(jìn)行異步化:

首先確認(rèn)你的項(xiàng)目中的 Servlet 是 3.0 以上,其次 Spring MVC 4.0+:

  1. <dependency> 
  2.      <groupId>javax.servlet</groupId> 
  3.      <artifactId>javax.servlet-api</artifactId> 
  4.      <version>3.1.0</version> 
  5.      <scope>provided</scope> 
  6.    </dependency> 
  7.    <dependency> 
  8.      <groupId>org.springframework</groupId> 
  9.      <artifactId>spring-webmvc</artifactId> 
  10.      <version>4.2.3.RELEASE</version> 
  11.    </dependency> 

web.xml 頭部聲明,必須要 3.0,F(xiàn)ilter 和 Serverlet 設(shè)置為異步:

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 
  3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  5.    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 
  6.    <filter> 
  7.        <filter-name>testFilter</filter-name
  8.        <filter-class>com.TestFilter</filter-class> 
  9.        <async-supported>true</async-supported> 
  10.    </filter> 
  11.  
  12.    <servlet> 
  13.        <servlet-name>mvc-dispatcher</servlet-name
  14.        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
  15.        ......... 
  16.        <async-supported>true</async-supported> 
  17.    </servlet> 

使用 Spring MVC 封裝了 Servlet 的 AsyncContext,使用起來(lái)比較簡(jiǎn)單。以前我們同步的模式的 Controller 是返回 ModelAndView。

而異步模式直接生成一個(gè) DeferredResult(支持我們超時(shí)擴(kuò)展)即可保存上下文,下面給出如何和我們 HttpClient 搭配的簡(jiǎn)單 demo:

  1. @RequestMapping(value="/asynctask", method = RequestMethod.GET) 
  2.    public DeferredResult<String> asyncTask() throws IOReactorException { 
  3.        IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setIoThreadCount(1).build(); 
  4.        ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig); 
  5.        PoolingNHttpClientConnectionManager conManager = new PoolingNHttpClientConnectionManager(ioReactor); 
  6.        conManager.setMaxTotal(100); 
  7.        conManager.setDefaultMaxPerRoute(100); 
  8.        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setConnectionManager(conManager).build(); 
  9.        // Start the client 
  10.        httpclient.start(); 
  11.        //設(shè)置超時(shí)時(shí)間200ms 
  12.        final DeferredResult<String> deferredResult = new DeferredResult<String>(200L); 
  13.        deferredResult.onTimeout(new Runnable() { 
  14.            @Override 
  15.            public void run() { 
  16.                System.out.println("異步調(diào)用執(zhí)行超時(shí)!thread id is : " + Thread.currentThread().getId()); 
  17.                deferredResult.setResult("超時(shí)了"); 
  18.            } 
  19.        }); 
  20.        System.out.println("/asynctask 調(diào)用!thread id is : " + Thread.currentThread().getId()); 
  21.        final HttpGet request2 = new HttpGet("http://www.apache.org/"); 
  22.        httpclient.execute(request2, new FutureCallback<HttpResponse>() { 
  23.  
  24.            public void completed(final HttpResponse response2) { 
  25.                System.out.println(request2.getRequestLine() + "->" + response2.getStatusLine()); 
  26.                deferredResult.setResult(request2.getRequestLine() + "->" + response2.getStatusLine()); 
  27.            } 
  28.  
  29.            public void failed(final Exception ex) { 
  30.                System.out.println(request2.getRequestLine() + "->" + ex); 
  31.            } 
  32.  
  33.            public void cancelled() { 
  34.                System.out.println(request2.getRequestLine() + " cancelled"); 
  35.            } 
  36.  
  37.        }); 
  38.        return deferredResult; 
  39.    } 

注意:在 Serlvet 異步化中有個(gè)問(wèn)題是 Filter 的后置結(jié)果處理,沒(méi)法使用,對(duì)于我們一些打點(diǎn),結(jié)果統(tǒng)計(jì)直接使用 Serlvet 異步是沒(méi)法用的。

在 Spring MVC 中就很好的解決了這個(gè)問(wèn)題,Spring MVC 采用了一個(gè)比較取巧的方式通過(guò)請(qǐng)求轉(zhuǎn)發(fā),能讓請(qǐng)求再次通過(guò)過(guò)濾器。

但是又引入了新的一個(gè)問(wèn)題那就是過(guò)濾器會(huì)處理兩次,這里可以通過(guò) Spring MVC 源碼中自身判斷的方法。

我們可以在 Filter 中使用下面這句話(huà)來(lái)進(jìn)行判斷是不是屬于 Spring MVC 轉(zhuǎn)發(fā)過(guò)來(lái)的請(qǐng)求,從而不處理 Filter 的前置事件,只處理后置事件:

  1. Object asyncManagerAttr = servletRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE); 
  2. return asyncManagerAttr instanceof WebAsyncManager ; 

全鏈路異步化

上面我們介紹了 Serlvet 的異步化,相信細(xì)心的同學(xué)都看出來(lái)似乎并沒(méi)有解決根本的問(wèn)題,我的 IO 阻塞依然存在,只是換了個(gè)位置而已。

當(dāng) IO 調(diào)用頻繁同樣會(huì)讓業(yè)務(wù)線(xiàn)程池快速變滿(mǎn),雖然 Serlvet 容器線(xiàn)程不被阻塞,但是這個(gè)業(yè)務(wù)依然會(huì)變得不可用。

 

那么怎么才能解決上面的問(wèn)題呢?答案就是全鏈路異步化,全鏈路異步追求的是沒(méi)有阻塞,打滿(mǎn)你的 CPU,把機(jī)器的性能壓榨到極致。模型圖如下:

 

具體的 NIO Client 到底做了什么事呢,具體如下面模型:

 

上面就是我們?nèi)溌樊惒降膱D了(部分線(xiàn)程池可以?xún)?yōu)化)。全鏈路的核心在于只要我們遇到 IO 調(diào)用的時(shí)候,我們就可以使用 NIO,從而避免阻塞,也就解決了之前說(shuō)的業(yè)務(wù)線(xiàn)程池被打滿(mǎn)的尷尬場(chǎng)景。

遠(yuǎn)程調(diào)用異步化

我們一般遠(yuǎn)程調(diào)用使用 RPC 或者 HTTP:

  • 對(duì)于 RPC 來(lái)說(shuō),一般 Thrift,HTTP,Motan 等支持都異步調(diào)用,其內(nèi)部原理也都是采用事件驅(qū)動(dòng)的 NIO 模型。
  • 對(duì)于 HTTP 來(lái)說(shuō),一般的 Apache HTTP Client 和 Okhttp 也都提供了異步調(diào)用。

下面簡(jiǎn)單介紹下 HTTP 異步化調(diào)用是怎么做的。首先來(lái)看一個(gè)例子:

  1. public class HTTPAsyncClientDemo { 
  2.    public static void main(String[] args) throws ExecutionException, InterruptedException, IOReactorException { 
  3.      //具體參數(shù)含義下文會(huì)講 
  4.       //apache提供了ioReactor的參數(shù)配置,這里我們配置IO 線(xiàn)程為1 
  5.        IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setIoThreadCount(1).build(); 
  6.      //根據(jù)這個(gè)配置創(chuàng)建一個(gè)ioReactor 
  7.        ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig); 
  8.      //asyncHttpClient使用PoolingNHttpClientConnectionManager管理我們客戶(hù)端連接 
  9.        PoolingNHttpClientConnectionManager conManager = new PoolingNHttpClientConnectionManager(ioReactor); 
  10.      //設(shè)置總共的連接的最大數(shù)量 
  11.        conManager.setMaxTotal(100); 
  12.      //設(shè)置每個(gè)路由的連接的最大數(shù)量 
  13.        conManager.setDefaultMaxPerRoute(100); 
  14.      //創(chuàng)建一個(gè)Client 
  15.        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setConnectionManager(conManager).build(); 
  16.        // Start the client 
  17.        httpclient.start(); 
  18.  
  19.        // Execute request 
  20.        final HttpGet request1 = new HttpGet("http://www.apache.org/"); 
  21.        Future<HttpResponse> future = httpclient.execute(request1, null); 
  22.        // and wait until a response is received 
  23.        HttpResponse response1 = future.get(); 
  24.        System.out.println(request1.getRequestLine() + "->" + response1.getStatusLine()); 
  25.  
  26.        // One most likely would want to use a callback for operation result 
  27.        final HttpGet request2 = new HttpGet("http://www.apache.org/"); 
  28.        httpclient.execute(request2, new FutureCallback<HttpResponse>() { 
  29.                        //Complete成功后會(huì)回調(diào)這個(gè)方法 
  30.            public void completed(final HttpResponse response2) { 
  31.                System.out.println(request2.getRequestLine() + "->" + response2.getStatusLine()); 
  32.            } 
  33.  
  34.            public void failed(final Exception ex) { 
  35.                System.out.println(request2.getRequestLine() + "->" + ex); 
  36.            } 
  37.  
  38.            public void cancelled() { 
  39.                System.out.println(request2.getRequestLine() + " cancelled"); 
  40.            } 
  41.  
  42.        }); 
  43.    } 

下面給出 httpAsync 的整個(gè)類(lèi)圖:

 

對(duì)于我們的 HTTPAysncClient 最后使用的是 InternalHttpAsyncClient,在 InternalHttpAsyncClient 中有個(gè) ConnectionManager,這個(gè)就是我們管理連接的管理器。

而在 httpAsync 中只有一個(gè)實(shí)現(xiàn)那就是 PoolingNHttpClientConnectionManager。

這個(gè)連接管理器中有兩個(gè)我們比較關(guān)心的,一個(gè)是 Reactor,一個(gè)是 Cpool:

  • Reactor:所有的 Reactor 這里都是實(shí)現(xiàn)了 IOReactor 接口。在 PoolingNHttpClientConnectionManager 中會(huì)有擁有一個(gè) Reactor,那就是 DefaultConnectingIOReactor,這個(gè) DefaultConnectingIOReactor,負(fù)責(zé)處理 Acceptor。

在 DefaultConnectingIOReactor 有個(gè) excutor 方法,生成 IOReactor 也就是我們圖中的 BaseIOReactor,進(jìn)行 IO 的操作。這個(gè)模型就是我們上面的 1.2.2 的模型。

  • CPool:在 PoolingNHttpClientConnectionManager 中有個(gè) CPool,主要是負(fù)責(zé)控制我們連接,我們上面所說(shuō)的 maxTotal 和 defaultMaxPerRoute,都是由其進(jìn)行控制。

如果每個(gè)路由有滿(mǎn)了,它會(huì)斷開(kāi)最老的一個(gè)鏈接;如果總共的 total 滿(mǎn)了,它會(huì)放入 leased 隊(duì)列,釋放空間的時(shí)候就會(huì)將其重新連接。

數(shù)據(jù)庫(kù)調(diào)用異步化

對(duì)于數(shù)據(jù)庫(kù)調(diào)用一般的框架并沒(méi)有提供異步化的方法,這里推薦自己封裝或者使用網(wǎng)上開(kāi)源的。

異步化并不是高并發(fā)的銀彈,但是有了異步化的確能提高你機(jī)器的 QPS,吞吐量等等。

上述講的一些模型如果能合理的做一些優(yōu)化,然后進(jìn)行應(yīng)用,相信能對(duì)你的服務(wù)有很大的幫助。

高并發(fā)大殺器:并行化

想必?zé)釔?ài)游戲的同學(xué)小時(shí)候都幻想過(guò)要是自己會(huì)分身之術(shù),就能一邊打游戲一邊上課了。

可惜現(xiàn)實(shí)中并沒(méi)有這個(gè)技術(shù),你要么只有老老實(shí)實(shí)的上課,要么就只有逃課去打游戲了。

雖然在現(xiàn)實(shí)中我們無(wú)法實(shí)現(xiàn)分身這樣的技術(shù),但是我們可以在計(jì)算機(jī)世界中實(shí)現(xiàn)這樣的愿望。

計(jì)算機(jī)中的分身術(shù)

計(jì)算機(jī)中的分身術(shù)不是天生就有了。在 1971 年,英特爾推出的全球第一顆通用型微處理器 4004,由 2300 個(gè)晶體管構(gòu)成。

當(dāng)時(shí),公司的聯(lián)合創(chuàng)始人之一戈登摩爾就提出大名鼎鼎的“摩爾定律”——每過(guò) 18 個(gè)月,芯片上可以集成的晶體管數(shù)目將增加一倍。

最初的主頻 740KHz(每秒運(yùn)行 74 萬(wàn)次),現(xiàn)在過(guò)了快 50 年了,大家去買(mǎi)電腦的時(shí)候會(huì)發(fā)現(xiàn)現(xiàn)在的主頻都能達(dá)到 4.0GHZ了(每秒 40 億次)。

但是主頻越高帶來(lái)的收益卻是越來(lái)越小:

  • 據(jù)測(cè)算,主頻每增加 1G,功耗將上升 25 瓦,而在芯片功耗超過(guò) 150 瓦后,現(xiàn)有的風(fēng)冷散熱系統(tǒng)將無(wú)法滿(mǎn)足散熱的需要。有部分 CPU 都可以用來(lái)煎雞蛋了。
  • 流水線(xiàn)過(guò)長(zhǎng),使得單位頻率效能低下,越大的主頻其實(shí)整體性能反而不如小的主頻。
  • 戈登摩爾認(rèn)為摩爾定律未來(lái) 10-20 年會(huì)失效。

在單核主頻遇到瓶頸的情況下,多核 CPU 應(yīng)運(yùn)而生,不僅提升了性能,并且降低了功耗。

所以多核 CPU 逐漸成為現(xiàn)在市場(chǎng)的主流,這樣讓我們的多線(xiàn)程編程也更加的容易。

說(shuō)到了多核 CPU 就一定要說(shuō) GPU,大家可能對(duì)這個(gè)比較陌生,但是一說(shuō)到顯卡就肯定不陌生,筆者搞過(guò)一段時(shí)間的 CUDA 編程,我才意識(shí)到這個(gè)才是真正的并行計(jì)算。

大家都知道圖片像素點(diǎn)吧,比如 1920*1080 的圖片有 210 萬(wàn)個(gè)像素點(diǎn),如果想要把一張圖片的每個(gè)像素點(diǎn)都進(jìn)行轉(zhuǎn)換一下,那在我們 Java 里面可能就要循環(huán)遍歷 210 萬(wàn)次。

就算我們用多線(xiàn)程 8 核 CPU,那也得循環(huán)幾十萬(wàn)次。但是如果使用 Cuda,最多可以 365535*512 = 100661760(一億)個(gè)線(xiàn)程并行執(zhí)行,就這種級(jí)別的圖片那也是馬上處理完成。

但是 Cuda 一般適合于圖片這種,有大量的像素點(diǎn)需要同時(shí)處理,但是指令集很少所以邏輯不能太復(fù)雜。

應(yīng)用中的并行

一說(shuō)起讓你的服務(wù)高性能的手段,那么異步化,并行化這些肯定會(huì)第一時(shí)間在你腦海中顯現(xiàn)出來(lái),并行化可以用來(lái)配合異步化,也可以用來(lái)單獨(dú)做優(yōu)化。

我們可以想想有這么一個(gè)需求,在你下外賣(mài)訂單的時(shí)候,這筆訂單可能還需要查用戶(hù)信息,折扣信息,商家信息,菜品信息等。

用同步的方式調(diào)用,如下圖所示:

 

設(shè)想一下這 5 個(gè)查詢(xún)服務(wù),平均每次消耗 50ms,那么本次調(diào)用至少是 250ms,我們細(xì)想一下,這五個(gè)服務(wù)其實(shí)并沒(méi)有任何的依賴(lài),誰(shuí)先獲取誰(shuí)后獲取都可以。

那么我們可以想想,是否可以用多重影分身之術(shù),同時(shí)獲取這五個(gè)服務(wù)的信息呢?

優(yōu)化如下:

 

將這五個(gè)查詢(xún)服務(wù)并行查詢(xún),在理想情況下可以?xún)?yōu)化至 50ms。當(dāng)然說(shuō)起來(lái)簡(jiǎn)單,我們真正如何落地呢?

CountDownLatch/Phaser

CountDownLatch 和 Phaser 是 JDK 提供的同步工具類(lèi)。Phaser 是 1.7 版本之后提供的工具類(lèi)。而 CountDownLatch 是 1.5 版本之后提供的工具類(lèi)。

這里簡(jiǎn)單介紹一下 CountDownLatch,可以將其看成是一個(gè)計(jì)數(shù)器,await()方法可以阻塞至超時(shí)或者計(jì)數(shù)器減至 0,其他線(xiàn)程當(dāng)完成自己目標(biāo)的時(shí)候可以減少 1,利用這個(gè)機(jī)制我們可以用來(lái)做并發(fā)。

可以用如下的代碼實(shí)現(xiàn)我們上面的下訂單的需求:

  1. public class CountDownTask { 
  2.    private static final int CORE_POOL_SIZE = 4; 
  3.    private static final int MAX_POOL_SIZE = 12; 
  4.    private static final long KEEP_ALIVE_TIME = 5L; 
  5.    private final static int QUEUE_SIZE = 1600; 
  6.  
  7.    protected final static ExecutorService THREAD_POOL = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, 
  8.            KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<>(QUEUE_SIZE)); 
  9.    public static void main(String[] args) throws InterruptedException { 
  10.        // 新建一個(gè)為5的計(jì)數(shù)器 
  11.        CountDownLatch countDownLatch = new CountDownLatch(5); 
  12.        OrderInfo orderInfo = new OrderInfo(); 
  13.        THREAD_POOL.execute(() -> { 
  14.            System.out.println("當(dāng)前任務(wù)Customer,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  15.            orderInfo.setCustomerInfo(new CustomerInfo()); 
  16.            countDownLatch.countDown(); 
  17.        }); 
  18.        THREAD_POOL.execute(() -> { 
  19.            System.out.println("當(dāng)前任務(wù)Discount,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  20.            orderInfo.setDiscountInfo(new DiscountInfo()); 
  21.            countDownLatch.countDown(); 
  22.        }); 
  23.        THREAD_POOL.execute(() -> { 
  24.            System.out.println("當(dāng)前任務(wù)Food,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  25.            orderInfo.setFoodListInfo(new FoodListInfo()); 
  26.            countDownLatch.countDown(); 
  27.        }); 
  28.        THREAD_POOL.execute(() -> { 
  29.            System.out.println("當(dāng)前任務(wù)Tenant,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  30.            orderInfo.setTenantInfo(new TenantInfo()); 
  31.            countDownLatch.countDown(); 
  32.        }); 
  33.        THREAD_POOL.execute(() -> { 
  34.            System.out.println("當(dāng)前任務(wù)OtherInfo,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  35.            orderInfo.setOtherInfo(new OtherInfo()); 
  36.            countDownLatch.countDown(); 
  37.        }); 
  38.        countDownLatch.await(1, TimeUnit.SECONDS); 
  39.        System.out.println("主線(xiàn)程:"+ Thread.currentThread().getName()); 
  40.    } 

建立一個(gè)線(xiàn)程池(具體配置根據(jù)具體業(yè)務(wù),具體機(jī)器配置),進(jìn)行并發(fā)的執(zhí)行我們的任務(wù)(生成用戶(hù)信息,菜品信息等),最后利用 await 方法阻塞等待結(jié)果成功返回。

CompletableFuture

相信各位同學(xué)已經(jīng)發(fā)現(xiàn),CountDownLatch 雖然能實(shí)現(xiàn)我們需要滿(mǎn)足的功能但是其仍然有個(gè)問(wèn)題是,我們的業(yè)務(wù)代碼需要耦合 CountDownLatch 的代碼。

比如在我們獲取用戶(hù)信息之后,我們會(huì)執(zhí)行 countDownLatch.countDown(),很明顯我們的業(yè)務(wù)代碼顯然不應(yīng)該關(guān)心這一部分邏輯,并且在開(kāi)發(fā)的過(guò)程中萬(wàn)一寫(xiě)漏了,那我們的 await 方法將只會(huì)被各種異常喚醒。

所以在 JDK 1.8 中提供了一個(gè)類(lèi) CompletableFuture,它是一個(gè)多功能的非阻塞的 Future。(什么是 Future:用來(lái)代表異步結(jié)果,并且提供了檢查計(jì)算完成,等待完成,檢索結(jié)果完成等方法。)

我們將每個(gè)任務(wù)的計(jì)算完成的結(jié)果都用 CompletableFuture 來(lái)表示,利用 CompletableFuture.allOf 匯聚成一個(gè)大的 CompletableFuture,那么利用 get()方法就可以阻塞。

  1. public class CompletableFutureParallel { 
  2.    private static final int CORE_POOL_SIZE = 4; 
  3.    private static final int MAX_POOL_SIZE = 12; 
  4.    private static final long KEEP_ALIVE_TIME = 5L; 
  5.    private final static int QUEUE_SIZE = 1600; 
  6.  
  7.    protected final static ExecutorService THREAD_POOL = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, 
  8.            KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<>(QUEUE_SIZE)); 
  9.    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { 
  10.        OrderInfo orderInfo = new OrderInfo(); 
  11.        //CompletableFuture 的List 
  12.        List<CompletableFuture> futures = new ArrayList<>(); 
  13.        futures.add(CompletableFuture.runAsync(() -> { 
  14.            System.out.println("當(dāng)前任務(wù)Customer,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  15.            orderInfo.setCustomerInfo(new CustomerInfo()); 
  16.        }, THREAD_POOL)); 
  17.        futures.add(CompletableFuture.runAsync(() -> { 
  18.            System.out.println("當(dāng)前任務(wù)Discount,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  19.            orderInfo.setDiscountInfo(new DiscountInfo()); 
  20.        }, THREAD_POOL)); 
  21.        futures.add( CompletableFuture.runAsync(() -> { 
  22.            System.out.println("當(dāng)前任務(wù)Food,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  23.            orderInfo.setFoodListInfo(new FoodListInfo()); 
  24.        }, THREAD_POOL)); 
  25.        futures.add(CompletableFuture.runAsync(() -> { 
  26.            System.out.println("當(dāng)前任務(wù)Other,線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  27.            orderInfo.setOtherInfo(new OtherInfo()); 
  28.        }, THREAD_POOL)); 
  29.        CompletableFuture allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); 
  30.        allDoneFuture.get(10, TimeUnit.SECONDS); 
  31.        System.out.println(orderInfo); 
  32.    } 

可以看見(jiàn)我們使用 CompletableFuture 能很快的完成需求,當(dāng)然這還不夠。

Fork/Join

我們上面用 CompletableFuture 完成了對(duì)多組任務(wù)并行執(zhí)行,但是它依然是依賴(lài)我們的線(xiàn)程池。

在我們的線(xiàn)程池中使用的是阻塞隊(duì)列,也就是當(dāng)我們某個(gè)線(xiàn)程執(zhí)行完任務(wù)的時(shí)候需要通過(guò)這個(gè)阻塞隊(duì)列進(jìn)行,那么肯定會(huì)發(fā)生競(jìng)爭(zhēng),所以在 JDK 1.7 中提供了 ForkJoinTask 和 ForkJoinPool。

 

ForkJoinPool 中每個(gè)線(xiàn)程都有自己的工作隊(duì)列,并且采用 Work-Steal 算法防止線(xiàn)程饑餓。

Worker 線(xiàn)程用 LIFO 的方法取出任務(wù),但是會(huì)用 FIFO 的方法去偷取別人隊(duì)列的任務(wù),這樣就減少了鎖的沖突。

 

網(wǎng)上這個(gè)框架的例子很多,我們看看如何使用代碼完成我們上面的下訂單需求:

  1. public class OrderTask extends RecursiveTask<OrderInfo> { 
  2.    @Override 
  3.    protected OrderInfo compute() { 
  4.        System.out.println("執(zhí)行"+ this.getClass().getSimpleName() + "線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  5.        // 定義其他五種并行TasK 
  6.        CustomerTask customerTask = new CustomerTask(); 
  7.        TenantTask tenantTask = new TenantTask(); 
  8.        DiscountTask discountTask = new DiscountTask(); 
  9.        FoodTask foodTask = new FoodTask(); 
  10.        OtherTask otherTask = new OtherTask(); 
  11.        invokeAll(customerTask, tenantTask, discountTask, foodTask, otherTask); 
  12.        OrderInfo orderInfo = new OrderInfo(customerTask.join(), tenantTask.join(), discountTask.join(), foodTask.join(), otherTask.join()); 
  13.        return orderInfo; 
  14.    } 
  15.    public static void main(String[] args) { 
  16.        ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors() -1 ); 
  17.        System.out.println(forkJoinPool.invoke(new OrderTask())); 
  18.    } 
  19. class CustomerTask extends RecursiveTask<CustomerInfo>{ 
  20.  
  21.    @Override 
  22.    protected CustomerInfo compute() { 
  23.        System.out.println("執(zhí)行"+ this.getClass().getSimpleName() + "線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  24.        return new CustomerInfo(); 
  25.    } 
  26. class TenantTask extends RecursiveTask<TenantInfo>{ 
  27.  
  28.    @Override 
  29.    protected TenantInfo compute() { 
  30.        System.out.println("執(zhí)行"+ this.getClass().getSimpleName() + "線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  31.        return new TenantInfo(); 
  32.    } 
  33. class DiscountTask extends RecursiveTask<DiscountInfo>{ 
  34.  
  35.    @Override 
  36.    protected DiscountInfo compute() { 
  37.        System.out.println("執(zhí)行"+ this.getClass().getSimpleName() + "線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  38.        return new DiscountInfo(); 
  39.    } 
  40. class FoodTask extends RecursiveTask<FoodListInfo>{ 
  41.  
  42.    @Override 
  43.    protected FoodListInfo compute() { 
  44.        System.out.println("執(zhí)行"+ this.getClass().getSimpleName() + "線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  45.        return new FoodListInfo(); 
  46.    } 
  47. class OtherTask extends RecursiveTask<OtherInfo>{ 
  48.  
  49.    @Override 
  50.    protected OtherInfo compute() { 
  51.        System.out.println("執(zhí)行"+ this.getClass().getSimpleName() + "線(xiàn)程名字為:" + Thread.currentThread().getName()); 
  52.        return new OtherInfo(); 
  53.    } 

我們定義一個(gè) Order Task 并且定義五個(gè)獲取信息的任務(wù),在 Compute 中分別 Fork 執(zhí)行這五個(gè)任務(wù),最后在將這五個(gè)任務(wù)的結(jié)果通過(guò) Join 獲得,最后完成我們的并行化的需求。

parallelStream

在 JDK 1.8 中提供了并行流的 API,當(dāng)我們使用集合的時(shí)候能很好的進(jìn)行并行處理。

下面舉了一個(gè)簡(jiǎn)單的例子從 1 加到 100:

  1. public class ParallelStream { 
  2.    public static void main(String[] args) { 
  3.        ArrayList<Integer> list = new ArrayList<Integer>(); 
  4.        for (int i = 1; i <= 100; i++) { 
  5.            list.add(i); 
  6.        } 
  7.        LongAdder sum = new LongAdder(); 
  8.        list.parallelStream().forEach(integer -> { 
  9. //            System.out.println("當(dāng)前線(xiàn)程" + Thread.currentThread().getName()); 
  10.            sum.add(integer); 
  11.        }); 
  12.        System.out.println(sum); 
  13.    } 

parallelStream 中底層使用的那一套也是 Fork/Join 的那一套,默認(rèn)的并發(fā)程度是可用 CPU 數(shù) -1。

分片

可以想象有這么一個(gè)需求,每天定時(shí)對(duì) ID 在某個(gè)范圍之間的用戶(hù)發(fā)券,比如這個(gè)范圍之間的用戶(hù)有幾百萬(wàn),如果給一臺(tái)機(jī)器發(fā)的話(huà),可能全部發(fā)完需要很久的時(shí)間。

所以分布式調(diào)度框架比如:elastic-job 都提供了分片的功能,比如你用 50 臺(tái)機(jī)器,那么 id%50 = 0 的在第 0 臺(tái)機(jī)器上;=1 的在第 1 臺(tái)機(jī)器上發(fā)券,那么我們的執(zhí)行時(shí)間其實(shí)就分?jǐn)偟搅瞬煌臋C(jī)器上了。

并行化注意事項(xiàng)

線(xiàn)程安全:在 parallelStream 中我們列舉的代碼中使用的是 LongAdder,并沒(méi)有直接使用我們的 Integer 和 Long,這個(gè)是因?yàn)樵诙嗑€(xiàn)程環(huán)境下 Integer 和 Long 線(xiàn)程不安全。所以線(xiàn)程安全我們需要特別注意。

合理參數(shù)配置:可以看見(jiàn)我們需要配置的參數(shù)比較多,比如我們的線(xiàn)程池的大小,等待隊(duì)列大小,并行度大小以及我們的等待超時(shí)時(shí)間等等。

我們都需要根據(jù)自己的業(yè)務(wù)不斷的調(diào)優(yōu)防止出現(xiàn)隊(duì)列不夠用或者超時(shí)時(shí)間不合理等等。

上面介紹了什么是并行化,并行化的各種歷史,在 Java 中如何實(shí)現(xiàn)并行化,以及并行化的注意事項(xiàng)。希望大家對(duì)并行化有個(gè)比較全面的認(rèn)識(shí)。

最后給大家提個(gè)兩個(gè)小問(wèn)題:

  • 在我們并行化當(dāng)中有某個(gè)任務(wù)如果某個(gè)任務(wù)出現(xiàn)了異常應(yīng)該怎么辦?
  • 在我們并行化當(dāng)中有某個(gè)任務(wù)的信息并不是強(qiáng)依賴(lài),也就是如果出現(xiàn)了問(wèn)題這部分信息我們也可以不需要,當(dāng)并行化的時(shí)候,這種任務(wù)出現(xiàn)了異常應(yīng)該怎么辦?

 

責(zé)任編輯:武曉燕 來(lái)源: 咖啡拿鐵
相關(guān)推薦

2024-09-10 09:31:07

開(kāi)源項(xiàng)目Arthas

2022-03-31 12:08:26

數(shù)據(jù)管理架構(gòu)

2013-04-18 10:01:01

Fiddler前端

2024-05-08 16:54:21

Python編程開(kāi)發(fā)

2023-06-02 18:37:14

Dubbo異步化接口

2019-06-14 08:35:14

華為禁令開(kāi)發(fā)

2021-04-03 23:47:15

人工智能網(wǎng)絡(luò)智能手機(jī)

2012-10-24 10:52:19

服務(wù)器虛擬化服務(wù)器虛擬化

2019-03-18 05:02:30

高并發(fā)京東架構(gòu)

2021-02-14 18:26:25

高并發(fā)大對(duì)象代碼

2016-11-28 09:08:43

java系統(tǒng)異步非阻塞

2021-09-09 10:40:12

服務(wù)器虛擬化數(shù)據(jù)中心

2012-02-28 10:17:17

服務(wù)器虛擬化虛擬機(jī)

2013-03-22 11:31:33

服務(wù)器虛擬化

2024-09-23 17:15:28

Python并發(fā)并行

2016-01-05 16:29:23

服務(wù)器虛擬化

2014-07-07 09:19:32

異步異步編程

2020-06-03 15:13:25

Bug前端調(diào)試應(yīng)用程序

2023-05-08 15:36:50

模型AI

2018-02-27 14:30:17

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美午夜精品久久久久久超碰| 国产成人精品av在线观| 91精品国产一区二区三区香蕉| 日韩区国产区| 国产乱淫片视频| 秋霞午夜一区二区三区视频| 亚洲视频免费观看| 国产精品一区而去| 欧美国产成人精品一区二区三区| 岛国一区二区| 亚洲欧美日韩久久精品| 鲁鲁狠狠狠7777一区二区| 在线免费a视频| 久久av影院| 亚洲国产成人va在线观看天堂| 欧美极品视频一区二区三区| 国产乱码精品一区二区三区精东 | 一本到12不卡视频在线dvd| 国产日韩av一区二区| 成人羞羞国产免费| 久久青青草视频| 久久精品影视| 欧美刺激午夜性久久久久久久| 国产xxxxx视频| 91成人在线观看喷潮蘑菇| 麻豆网站免费观看| 日韩电影毛片| 亚洲综合色区另类av| 精品国产免费一区二区三区| 免费精品一区二区| 国产精品老牛| 在线视频欧美日韩| 日本精品一二三区| 四虎永久精品在线| 日本久久电影网| 超碰97在线资源| 综合久久中文字幕| 伊人久久大香线蕉综合四虎小说 | 午夜激情久久| 欧美日韩在线直播| 日韩a级黄色片| www日韩tube| 成人久久18免费网站麻豆| 成人黄在线观看| 在线免费观看一级片| 久久福利一区| 91国语精品自产拍在线观看性色 | 国产清纯白嫩初高中在线观看性色| 影视一区二区三区| 色丁香久综合在线久综合在线观看| avav在线播放| 国产淫片在线观看| 久久精品视频免费| 国产精品一区二区三区免费| 午夜精品久久久久久久爽| 国产一区二区成人久久免费影院| 中文字幕v亚洲ⅴv天堂| 成都免费高清电影| 黄色漫画在线免费看| 亚洲国产精品高清| 欧美激情视频一区二区三区| 日本1级在线| 91丨九色丨黑人外教| 国产精品夜夜夜一区二区三区尤| 99在线精品视频免费观看软件| 老汉av免费一区二区三区| 日韩av电影手机在线观看| 亚洲图片在线视频| 亚洲一区二区三区四区五区午夜| 2023亚洲男人天堂| 亚洲精品www久久久久久| 欧美a一欧美| 日韩欧美亚洲国产另类| av电影在线播放| av毛片在线看| 日韩美女精品在线| 中文字幕av日韩精品| 超碰免费公开在线| 亚洲一区二区精品久久av| 国产69精品久久久久999小说| 超碰在线99| 色乱码一区二区三区88| 日日干夜夜操s8| 警花av一区二区三区| 精品国产乱码久久| 中文字幕在线1| 91精品成人| 色av中文字幕一区| 五月天婷婷色综合| 日韩mv欧美mv国产网站| 欧美精品一区二区三区四区 | 超碰超碰超碰超碰| 亚洲免费影院| 国产精品久在线观看| 国产区精品在线| 99久久伊人精品| 日本午夜精品一区二区三区| av中文在线| 亚洲国产精品久久人人爱蜜臀| 国产xxxxx在线观看| 精品福利在线| 亚洲国产精彩中文乱码av在线播放| 右手影院亚洲欧美| 国产高清一区二区| 26uuu另类亚洲欧美日本一| 中文字幕欧美色图| 国产酒店精品激情| 日本在线视频一区| 国产偷倩在线播放| 欧美午夜免费电影| 中文字幕在线视频播放| 欧美aaaa视频| 欧美在线观看视频| 精品人妻伦一区二区三区久久| 久久婷婷国产综合国色天香| 欧美与动交zoz0z| 日本免费不卡视频| 国产精品欧美久久久久无广告| 亚洲欧美成人一区| 一个人看的www视频在线免费观看| 日本高清不卡在线观看| 韩国三级hd两男一女| 99久久精品费精品国产| 热久久99这里有精品| 天天干天天舔天天操| 极品裸体白嫩激情啪啪国产精品| 国产精品日韩专区| 日韩a在线观看| 亚洲一区影音先锋| 91看片破解版| 日本电影一区二区| 2019中文字幕在线观看| 亚洲第一页综合| 1000精品久久久久久久久| 精品久久久久久中文字幕2017| 亚洲天堂av资源在线观看| 久久久91精品国产一区不卡| 久久精品视频2| 成人福利视频网站| 日本久久久网站| 毛片在线网站| 欧美日韩一区二区在线观看 | 国内精品久久久久久中文字幕| 艳妇荡乳欲伦69影片| 亚洲欧洲一区二区天堂久久| 91视频网页| h片在线观看网站| 91精品办公室少妇高潮对白| 国产精品九九九九九| 99精品国产福利在线观看免费| 国内精品久久久久影院优| www.国产麻豆| 亚洲自拍偷拍综合| 亚洲免费观看在线| 亚洲大胆av| 国产精选在线观看91| 在线网址91| 精品久久久久99| 精品视频在线观看免费| 成人禁用看黄a在线| 国内精品在线观看视频| 欧美激情影院| 欧美激情精品久久久| 日批视频免费播放| 欧美经典一区二区| 视频一区二区视频| 欧美区一区二区| 欧美精品福利视频| 日本免费一区视频| 色综合久久久久网| 宇都宫紫苑在线播放| 亚洲人metart人体| 成人欧美视频在线| 国产三线在线| 亚洲欧美日韩另类| 看黄色录像一级片| 在线中文字幕亚洲| 国产精品对白刺激久久久| 1234区中文字幕在线观看| 亚洲精品在线观| 日日夜夜操视频| 国产精品久久久久影院老司| 国产农村妇女精品久久| 亚洲国产日本| 亚洲丝袜av一区| 成人h动漫精品一区二区下载 | 国产精品区二区三区日本| 色偷偷色偷偷色偷偷在线视频| 国产一区二区三区直播精品电影| 农村老熟妇乱子伦视频| 国产精品一区二区三区乱码| 97视频在线免费| 亚洲图片久久| 亚洲精品免费av| 日本中文字幕中出在线| 亚洲另类在线一区| 大尺度在线观看| 久久在线91| 肉大捧一出免费观看网站在线播放| 在线观看特色大片免费视频| 自拍视频国产精品| 人妻丰满熟妇av无码区hd| 欧美亚洲高清一区二区三区不卡| 久久久精品人妻一区二区三区四| 久久伊人中文字幕| 下面一进一出好爽视频| 日韩激情中文字幕| 丁香六月激情婷婷| 欧美国产小视频| 久久亚洲一区二区| 91麻豆精品国产综合久久久| 555www成人网| 在线观看av免费| 亚洲人午夜色婷婷| 色综合视频在线| 欧美一激情一区二区三区| 性高潮视频在线观看| 亚洲欧美一区二区三区极速播放 | 国产精品每日更新在线播放网址| 亚洲国产精品第一页| 视频一区免费在线观看| 成人毛片100部免费看| 国产精品片aa在线观看| 国产一区免费视频| 91精品福利观看| 国产精品jizz在线观看麻豆| 欧美男男tv网站在线播放| 欧美精品性视频| 日本在线观看| 国产午夜精品一区理论片飘花| 欧美一级性视频| 精品日韩成人av| 国产精品女同一区二区| 欧美日韩一级二级| av一级在线观看| 欧美性猛交丰臀xxxxx网站| 国产精品第一页在线观看| 一区二区在线观看视频| 91久久久久久久久久久久久久| 国产亚洲短视频| 30一40一50老女人毛片| 99综合电影在线视频| 少妇熟女视频一区二区三区| 国产精品原创巨作av| 中文字幕一区二区三区四| 日韩av不卡在线观看| 热久久精品免费视频| 国产一区日韩欧美| 久草视频这里只有精品| 激情久久五月| 欧美日韩在线一| 在线视频亚洲| 国产女主播自拍| 一区二区国产在线| 午夜在线视频免费观看| 91久久夜色精品国产按摩| 亚洲 欧洲 日韩| 欧美一区免费| 九9re精品视频在线观看re6 | 另类人妖一区二区av| 麻豆一区二区三区视频| 久久99精品国产麻豆婷婷 | 午夜精品成人av| 色偷偷噜噜噜亚洲男人| 三区四区电影在线观看| 精品国产一区二区三区久久狼5月| 日本暖暖在线视频| 久热精品视频在线免费观看 | 精品在线播放| 亚洲bt天天射| 加勒比视频一区| 欧美精品一区在线| 日韩欧美在线中字| 国产乱子伦精品视频| 99在线精品视频在线观看| 欧美三级在线观看视频| 日韩黄色在线观看| 天天干天天干天天干天天干天天干| 蜜臀久久99精品久久久久宅男 | 97se亚洲国产综合在线| 成人网站免费观看| 国产肉丝袜一区二区| 爱爱视频免费在线观看| 性感美女久久精品| 中文字幕av网站| 欧美剧情片在线观看| 亚洲AV无码精品自拍| 亚洲精品一区二区在线| 午夜视频在线观看免费视频| 欧美国产第二页| 日本不卡网站| 成人精品一区二区三区| 国内不卡的一区二区三区中文字幕| 国产精品日韩欧美一区二区三区| 你微笑时很美电视剧整集高清不卡| 日本一级淫片演员| 亚洲综合欧美| 成人在线短视频| 亚洲国产精品t66y| 久久精品国产亚洲av高清色欲| 岛国视频午夜一区免费在线观看| 亚洲无码精品在线播放| 欧美精品一区二区久久婷婷| 午夜在线视频| 日本乱人伦a精品| 日韩在线成人| 亚洲欧美国产不卡| 亚洲一区日韩| 久久久久亚洲av成人网人人软件| 国产精品美女一区二区三区| 久久久久久久久久影院| 欧美一二三四在线| 69久久夜色| 性欧美在线看片a免费观看| 欧美亚洲日本| 九色成人免费视频| 黄色欧美视频| 日韩中文字幕一区二区| 亚洲精品1234| 中文字幕无码毛片免费看| 国产欧美日韩综合| 91在线视频在线观看| 欧美精品一区二区三区视频 | 亚洲 日韩 国产第一| 色综合视频一区二区三区44| 欧美日韩一区二区视频在线观看 | 高清欧美性猛交xxxx黑人猛交| 欧美一级做一级爱a做片性| 欧美激情论坛| 久久精品官网| 国产熟妇搡bbbb搡bbbb| 五月综合激情网| 欧美少妇bbw| 欧美富婆性猛交| 亚洲老司机网| 亚洲乱码国产乱码精品天美传媒| 天堂一区二区在线| 极品白嫩的小少妇| 亚洲免费观看高清完整| 91 中文字幕| www.亚洲成人| 日本高清不卡一区二区三区视频 | 欧美成人三区| 欧美国产日韩精品| 台湾天天综合人成在线| 伊人av成人| 久久精品人人| 丰满少妇一区二区| 狠狠久久亚洲欧美专区| 欧美视频xxx| 17婷婷久久www| 妖精视频一区二区三区| www.日日操| 久久综合国产精品| 国产午夜无码视频在线观看 | 91精品蜜臀在线一区尤物| 在线观看精品一区二区三区| 国产精品jizz在线观看麻豆| 成人在线免费观看91| 奇米精品一区二区三区| 91在线视频免费观看| 久久久蜜桃一区二区| 国产午夜精品一区理论片飘花| 免费视频观看成人| 青青草影院在线观看| 国产精品18久久久久久久久久久久| 麻豆精品一区二区三区视频| 精品精品欲导航| gay欧美网站| 欧美日本亚洲| 精品综合免费视频观看| 亚洲成人生活片| 亚洲精品成人久久电影| 欧美一级二级视频| 国产精品免费一区豆花| 91精品久久久久久久久久不卡| 不卡的一区二区| 欧美色图在线视频| 日本成人在线播放| 91在线观看免费高清| 国产一区日韩一区| 国产一二三四五区| 欧美一区二区三区啪啪| 菠萝蜜视频在线观看www入口| 欧美综合激情| 久久精品99国产精品日本| 爱爱视频免费在线观看| 日韩精品在线视频美女| 久久亚洲精品中文字幕| 丁香花在线影院观看在线播放| 91免费视频大全| 在线观看国产黄| 国内精品久久久久影院 日本资源| 欧美禁忌电影网| 在线观看你懂的视频| 色94色欧美sute亚洲线路一ni| 调教一区二区| 蜜桃网站成人| 国内精品不卡在线| youjizz在线视频|