<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      如何用Netty實現一個輕量級的HTTP代理服務器

        為什么會想通過Netty構建一個HTTP代理服務器?這也是筆者發表這篇文章的目的所在。

        其主要還是源于解決在日常開發測試過程中,一直困擾測試同學很久的一個問題,現在我就來具體闡述一下這個問題。

        在日常開發測試過程中,為了確保上線項目的萬無一失,集成測試通常有部署,測試環境和回歸環境這兩套環境。開發人員根據需求編寫代碼模塊,自測通過之后,由測試的同學更新到測試環境,進行測試。如果測試通過,確定項目具備上線條件,后續會在回歸環境,進行回歸測試。回歸驗證通過的項目,才具備上線條件。

        由于模塊的復雜性和多樣性,我們系統要跟外系統進行一些數據的交互,這通常是通過HTTP協議方式完成的。現在由于某些條件的限制,通常只有測試環境的網絡和端口是和外系統是打通的,回歸環境的這塊網絡鏈路是關閉的。這樣就產生了一個很尷尬的問題:如果一個模塊有跟外系統進行交互,回歸環境是不具備回歸條件的,這樣就要測試的同學,額外把模塊更新到測試環境來驗證,這樣不僅耗時耗力。并且由于測試環境和回歸環境系統數據的差異,往往可能導致項目的潛在風險沒有被及時地發現。

        現在迫切希望有一個HTTP代理服務器,能夠路由回歸環境的請求到測試環境。更進一步地,如果能根據請求報文的某些關鍵字來過濾,決定最終路由的地址,這個當然是最好了。

        基于這些因素,考慮到HTTP代理服務器的主要用途是轉發URL請求,可選的方案有很多種。比如Apache、Nginx等等。但是最終都沒有被采用,主要基于以下幾點考慮:

      1. Apache服務器不能根據某些指定的關鍵字過濾轉發URL請求,只能做簡單的代理轉發。
      2. Nginx相比Aapche服務器單純進行請求轉發而言,通過OpenResty(http://openresty.org/cn/),可以把lua解析器內嵌到Nginx,這樣可以編寫lua腳本的關鍵字過濾規則,但是要測試同學短時間內學會配置不太現實。

        有沒有通過簡單的幾個配置,就可以達到目的的可行方案呢?

        我首先想到了使用Netty這個NIO框架,來實現一個輕量級的HTTP代理轉發服務器,同時只要簡單地配置過濾規則,就可以實現請求的規則路由。

        本文要求你熟悉Netty網絡框架的工作流程,基本原理。有興趣的朋友,可以認真研讀一下《Netty in Action》這本書,對提高Netty的功力有很大幫助。

        言歸正傳,下面是這個HTTP代理轉發服務器的工作流程圖:

        這里我簡單描述一下:

      • 首先是Netty的服務端連接器(Acceptor)線程接收到HTTP請求,然后會把這個請求放入后端Netty專門負責處理I/O操作的線程池中。這個也是Netty經典的主從Reactor多線程模型的應用。
      • I/O處理線程先對HTTP請求,調用HttpRequestDecoder解碼器進行解碼。
      • HttpRequestDecoder把解碼的結果,通知給路由規則計算的核心模塊(GatewayServerHandler),核心模塊根據配置加上請求報文中的關鍵字,計算出要轉發的URL地址。
      • 通過HTTP POST方式把請求,轉發給計算出來的URL地址。
      • 獲取HTTP POST的獲得到的應答結果。
      • 然后通過HttpResponseEncoder編碼器,把應答結果進行HTTP編碼,最后透傳給調用方。

        流程描述很簡單,現在關鍵是,如何設計關鍵字路由規則配置模塊。

        我是通過屬性配置文件(.properties)方式來實現的,主要有兩個配置文件。

      • netty-gateway.properties配置文件,主要是用來描述URL中的路徑、以及其沒有和請求URL路徑匹配成功時,默認轉發的URL地址。

        配置文件的配置參考說明:

      #配置說明參考:
      #netty-gateway.config1.serverPath ==> URL路徑關鍵字。
      #netty-gateway.config1.defaultAddr ==> 請求報文中的關鍵字沒有匹配成功時,默認轉發的URL地址。
      #config的數字后綴順序遞增即可。
      
      netty-gateway.config1.serverPath=fcgi-bin/UIG_SFC_186
      netty-gateway.config1.defaultAddr=http://10.46.158.10:8088/fcgi-bin/UIG_SFC_186
      
      netty-gateway.config2.serverPath=fcgi-bin/BSSP_SFC
      netty-gateway.config2.defaultAddr=http://10.46.158.10:8089/fcgi-bin/BSSP_SFC
      • netty-route.properties配置文件,則是主要配置URL中的路徑、請求報文關鍵字集合、以及請求的URL路徑、請求報文關鍵字和配置的匹配成功時,轉發的URL地址。

        配置文件的配置參考說明:

      #配置說明參考:
      #netty-gateway.config1.serverPath ==> URL路徑關鍵字。
      #netty-gateway.config1.keyWord ==> 請求報文匹配關鍵字。支持1~N個關鍵字,多個關鍵字用逗號分割,關鍵字之間是邏輯與的關系。
      #netty-gateway.config1.matchAddr ==> 請求報文匹配關鍵字匹配成功時,轉發的ULR地址。
      #config的數字后綴順序遞增即可。
      
      netty-gateway.config1.serverPath=fcgi-bin/UIG_SFC_186
      netty-gateway.config1.keyWord=1,2,3
      netty-gateway.config1.matchAddr=http://10.46.158.20:8088/fcgi-bin/UIG_SFC_186
      
      netty-gateway.config2.serverPath=fcgi-bin/UIG_SFC_186
      netty-gateway.config2.keyWord=1,2,3,4
      netty-gateway.config2.matchAddr=http://10.46.158.20:8088/fcgi-bin/UIG_SFC_186
      
      netty-gateway.config3.serverPath=fcgi-bin/BSSP_SFC
      netty-gateway.config3.keyWord=HelloWorldNettyGateway
      netty-gateway.config3.matchAddr=http://10.46.158.20:8089/fcgi-bin/BSSP_SFC

        有了上述兩個基礎的配置信息之后,就可以實現基于Netty的關鍵字HTTP路由轉發服務器了。

        這里主要說明關鍵代碼模塊的設計思路:

        首先是GatewayAttribute類,它主要對應netty-gateway.properties配置文件的數據結構。

      package com.newlandframework.gateway.commons;
      
      /**
       * @author tangjie<https://github.com/tang-jie>
       * @filename:GatewayAttribute.java
       * @description:GatewayAttribute功能模塊
       * @blogs http://www.rzrgm.cn/jietang/
       * @since 2018/4/18
       */
      public class GatewayAttribute {
          private String serverPath;
          private String defaultAddr;
      
          public String getDefaultAddr() {
              return defaultAddr;
          }
      
          public void setDefaultAddr(String defaultAddr) {
              this.defaultAddr = defaultAddr;
          }
      
          public String getServerPath() {
              return serverPath;
          }
      
          public void setServerPath(String serverPath) {
              this.serverPath = serverPath;
          }
      }

        其次是RouteAttribute類,它主要對應netty-route.properties配置文件的數據結構。

      package com.newlandframework.gateway.commons;
      
      /**
       * @author tangjie<https://github.com/tang-jie>
       * @filename:RouteAttribute.java
       * @description:RouteAttribute功能模塊
       * @blogs http://www.rzrgm.cn/jietang/
       * @since 2018/4/18
       */
      public class RouteAttribute {
          private String serverPath;
          private String keyWord;
          private String matchAddr;
      
          public String getMatchAddr() {
              return matchAddr;
          }
      
          public void setMatchAddr(String matchAddr) {
              this.matchAddr = matchAddr;
          }
      
          public String getServerPath() {
              return serverPath;
          }
      
          public void setServerPath(String serverPath) {
              this.serverPath = serverPath;
          }
      
          public String getKeyWord() {
              return keyWord;
          }
      
          public void setKeyWord(String keyWord) {
              this.keyWord = keyWord;
          }
      }

        然后通過實現spring框架的BeanDefinitionRegistryPostProcessor接口,來實現配置文件的自動加載注入。對應代碼如下:

      package com.newlandframework.gateway.commons;
      
      import org.springframework.beans.BeansException;
      import org.springframework.beans.MutablePropertyValues;
      import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
      import org.springframework.beans.factory.support.BeanDefinitionRegistry;
      import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
      import org.springframework.beans.factory.support.GenericBeanDefinition;
      import org.springframework.core.io.ClassPathResource;
      import org.springframework.core.io.Resource;
      
      import java.io.IOException;
      import java.util.*;
      
      import static com.newlandframework.gateway.commons.GatewayOptions.*;
      
      /**
       * @author tangjie<https://github.com/tang-jie>
       * @filename:RoutingLoader.java
       * @description:RoutingLoader功能模塊
       * @blogs http://www.rzrgm.cn/jietang/
       * @since 2018/4/18
       */
      public class RoutingLoader implements BeanDefinitionRegistryPostProcessor {
          public static final List<RouteAttribute> ROUTERS = new ArrayList<RouteAttribute>();
          public static final List<GatewayAttribute> GATEWAYS = new ArrayList<GatewayAttribute>();
      
          private static final List<String> KEY_ROUTERS = new ArrayList<String>();
          private static final List<String> KEY_GATEWAYS = new ArrayList<String>();
      
          @Override
          public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
              initGatewayRule(registry);
              initRouteRule(registry);
          }
      
          @Override
          public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
              GATEWAYS.clear();
              ROUTERS.clear();
      
              for (String beanName : KEY_GATEWAYS) {
                  GATEWAYS.add(beanFactory.getBean(beanName, GatewayAttribute.class));
              }
      
              for (String beanName : KEY_ROUTERS) {
                  ROUTERS.add(beanFactory.getBean(beanName, RouteAttribute.class));
              }
          }
          
          //加載netty-gateway.properties配置文件
          private void initGatewayRule(BeanDefinitionRegistry registry) {
              GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
              Resource resource = new ClassPathResource(GATEWAY_OPTION_GATEWAY_CONFIG_FILE);
              Properties p = new Properties();
              try {
                  p.load(resource.getInputStream());
      
                  String key = null;
                  String keyPrefix = null;
                  String defaultAddr = null;
                  String serverPath = null;
      
                  Map<String, String> valuesMap = null;
                  MutablePropertyValues mpv = null;
      
                  for (Object obj : p.keySet()) {
                      key = obj.toString();
                      if (key.endsWith(GATEWAY_PROPERTIES_PREFIX_SERVER_PATH)) {
                          keyPrefix = key.substring(0, key.indexOf(GATEWAY_PROPERTIES_PREFIX_SERVER_PATH));
                          serverPath = p.getProperty(keyPrefix + GATEWAY_PROPERTIES_PREFIX_SERVER_PATH).trim();
                          defaultAddr = p.getProperty(keyPrefix + GATEWAY_PROPERTIES_PREFIX_DEFAULT_ADDR).trim();
      
                          valuesMap = new LinkedHashMap<String, String>();
                          valuesMap.put(GATEWAY_PROPERTIES_DEFAULT_ADDR, defaultAddr);
                          valuesMap.put(GATEWAY_PROPERTIES_SERVER_PATH, serverPath);
      
                          mpv = new MutablePropertyValues(valuesMap);
                          beanDefinition = new GenericBeanDefinition();
                          beanDefinition.setBeanClass(GatewayAttribute.class);
                          beanDefinition.setPropertyValues(mpv);
                          registry.registerBeanDefinition(serverPath, beanDefinition);
      
                          KEY_GATEWAYS.add(serverPath);
                      }
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      
          //加載netty-route.properties配置文件
          private void initRouteRule(BeanDefinitionRegistry registry) {
              GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
              Resource resource = new ClassPathResource(GATEWAY_OPTION_ROUTE_CONFIG_FILE);
              Properties p = new Properties();
      
              try {
                  p.load(resource.getInputStream());
      
                  String key = null;
                  String keyPrefix = null;
                  String keyWord = null;
                  String matchAddr = null;
                  String serverPath = null;
      
                  Map<String, String> valuesMap = null;
                  MutablePropertyValues mpv = null;
      
                  for (Object obj : p.keySet()) {
                      key = obj.toString();
                      if (key.endsWith(GATEWAY_PROPERTIES_PREFIX_KEY_WORD)) {
                          keyPrefix = key.substring(0, key.indexOf(GATEWAY_PROPERTIES_PREFIX_KEY_WORD));
                          keyWord = p.getProperty(keyPrefix + GATEWAY_PROPERTIES_PREFIX_KEY_WORD).trim();
                          if (keyWord.isEmpty()) continue;
                          matchAddr = p.getProperty(keyPrefix + GATEWAY_PROPERTIES_PREFIX_MATCH_ADDR).trim();
                          serverPath = p.getProperty(keyPrefix + GATEWAY_PROPERTIES_PREFIX_SERVER_PATH).trim();
      
                          valuesMap = new LinkedHashMap<String, String>();
                          valuesMap.put(GATEWAY_PROPERTIES_KEY_WORD, keyWord);
                          valuesMap.put(GATEWAY_PROPERTIES_MATCH_ADDR, matchAddr);
                          valuesMap.put(GATEWAY_PROPERTIES_SERVER_PATH, serverPath);
      
                          mpv = new MutablePropertyValues(valuesMap);
                          beanDefinition = new GenericBeanDefinition();
                          beanDefinition.setBeanClass(RouteAttribute.class);
                          beanDefinition.setPropertyValues(mpv);
                          String beanName = serverPath + GATEWAY_OPTION_SERVER_SPLIT + keyWord;
                          registry.registerBeanDefinition(beanName, beanDefinition);
      
                          KEY_ROUTERS.add(beanName);
                      }
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }

        最后是重點的關鍵字過濾轉發代碼模塊,主要完成路由轉發地址的匹配計算、路由轉發、以及應答轉發結果給請求客戶端的工作。

      import com.newlandframework.gateway.commons.GatewayAttribute;
      import com.newlandframework.gateway.commons.HttpClientUtils;
      import com.newlandframework.gateway.commons.RouteAttribute;
      import com.newlandframework.gateway.commons.RoutingLoader;
      import io.netty.buffer.ByteBuf;
      import io.netty.buffer.Unpooled;
      import io.netty.channel.ChannelFutureListener;
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.channel.SimpleChannelInboundHandler;
      import io.netty.handler.codec.http.*;
      import io.netty.util.Signal;
      import io.netty.util.concurrent.Future;
      import io.netty.util.concurrent.FutureListener;
      import io.netty.util.concurrent.GlobalEventExecutor;
      import org.springframework.util.StringUtils;
      
      import java.util.concurrent.Callable;
      import java.util.concurrent.CountDownLatch;
      import java.util.concurrent.TimeUnit;
      
      import static com.newlandframework.gateway.commons.GatewayOptions.*;
      import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
      import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
      import static io.netty.handler.codec.http.HttpResponseStatus.OK;
      import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
      
      /**
       * @author tangjie<https://github.com/tang-jie>
       * @filename:GatewayServerHandler.java
       * @description:GatewayServerHandler功能模塊
       * @blogs http://www.rzrgm.cn/jietang/
       * @since 2018/4/18
       */
      public class GatewayServerHandler extends SimpleChannelInboundHandler<Object> {
          private HttpRequest request;
          private StringBuilder buffer = new StringBuilder();
          private String url = "";
          private String uri = "";
          private StringBuilder respone;
          private GlobalEventExecutor executor = GlobalEventExecutor.INSTANCE;
          private CountDownLatch latch = new CountDownLatch(1);
      
          @Override
          public void channelReadComplete(ChannelHandlerContext ctx) {
              ctx.flush();
          }
      
          @Override
          protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
              if (msg instanceof HttpRequest) {
                  HttpRequest request = this.request = (HttpRequest) msg;
      
                  //收到客戶端的100-Continue協議請求,說明客戶端要post數據給服務器
                  if (HttpUtil.is100ContinueExpected(request)) {
                      notify100Continue(ctx);
                  }
      
                  buffer.setLength(0);
                  uri = request.uri().substring(1);
              }
      
              if (msg instanceof HttpContent) {
                  HttpContent httpContent = (HttpContent) msg;
                  ByteBuf content = httpContent.content();
                  if (content.isReadable()) {
                      buffer.append(content.toString(GATEWAY_OPTION_CHARSET));
                  }
      
                  //獲取post數據完畢
                  if (msg instanceof LastHttpContent) {
                      LastHttpContent trace = (LastHttpContent) msg;
      
                      System.out.println("[NETTY-GATEWAY] REQUEST : " + buffer.toString());
      
                      //根據netty-gateway.properties、netty-route.properties匹配出最終轉發的URL地址
                      url = matchUrl();
                      System.out.println("[NETTY-GATEWAY] URL : " + url);
      
                      //http請求異步轉發處理,不要阻塞當前的Netty Handler的I/O線程,提高服務器的吞吐量。
                      Future<StringBuilder> future = executor.submit(new Callable<StringBuilder>() {
                          @Override
                          public StringBuilder call() {
                              return HttpClientUtils.post(url, buffer.toString(), GATEWAY_OPTION_HTTP_POST);
                          }
                      });
      
                      future.addListener(new FutureListener<StringBuilder>() {
                          @Override
                          public void operationComplete(Future<StringBuilder> future) throws Exception {
                              if (future.isSuccess()) {
                                  respone = ((StringBuilder) future.get(GATEWAY_OPTION_HTTP_POST, TimeUnit.MILLISECONDS));
                              } else {
                                  respone = new StringBuilder(((Signal) future.cause()).name());
                              }
                              latch.countDown();
                          }
                      });
      
                      try {
                          latch.await();
                          writeResponse(respone, future.isSuccess() ? trace : null, ctx);
                          ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
              cause.printStackTrace();
              ctx.close();
          }
      
          //根據netty-gateway.properties、netty-route.properties匹配出最終轉發的URL地址
          private String matchUrl() {
              for (GatewayAttribute gateway : RoutingLoader.GATEWAYS) {
                  if (gateway.getServerPath().equals(uri)) {
                      for (RouteAttribute route : RoutingLoader.ROUTERS) {
                          if (route.getServerPath().equals(uri)) {
                              String[] keys = StringUtils.delimitedListToStringArray(route.getKeyWord(), GATEWAY_OPTION_KEY_WORD_SPLIT);
                              boolean match = true;
                              for (String key : keys) {
                                  if (key.isEmpty()) continue;
                                  if (buffer.toString().indexOf(key.trim()) == -1) {
                                      match = false;
                                      break;
                                  }
                              }
                              if (match) {
                                  return route.getMatchAddr();
                              }
                          }
                      }
      
                      return gateway.getDefaultAddr();
                  }
              }
              return GATEWAY_OPTION_LOCALHOST;
          }
      
          //把路由轉發的結果應答給http客戶端
          private void writeResponse(StringBuilder respone, HttpObject current, ChannelHandlerContext ctx) {
              if (respone != null) {
                  boolean keepAlive = HttpUtil.isKeepAlive(request);
      
                  FullHttpResponse response = new DefaultFullHttpResponse(
                          HTTP_1_1, current == null ? OK : current.decoderResult().isSuccess() ? OK : BAD_REQUEST,
                          Unpooled.copiedBuffer(respone.toString(), GATEWAY_OPTION_CHARSET));
      
                  response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=GBK");
      
                  if (keepAlive) {
                      response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
                      response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
                  }
      
                  ctx.write(response);
              }
          }
      
          private static void notify100Continue(ChannelHandlerContext ctx) {
              FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE);
              ctx.write(response);
          }
      }

        這樣把整個工程maven打包部署運行,服務器默認啟動端口8999,你可以通過netty-gateway.xml的gatewayPort屬性進行配置調整。

        控制臺打印出如下的信息,則說明服務器啟動成功。

        下面繼續以一個實際的案例來說明一下,如何配置使用這個HTTP服務器。

        NettyGateway代理轉發場景描述

      • NettyGateway部署在10.1.1.76主機,URL中的路徑為:fcgi-bin/BSSP_SFC
      • 如果請求報文中出現HelloWorldNettyGateway關鍵字的時候,轉發到http://10.46.158.20:8089/fcgi-bin/BSSP_SFC
      • 否則轉發到http://10.46.158.10:8089/fcgi-bin/BSSP_SFC

        NettyGateway代理轉發場景配置說明

      • 配置文件netty-gateway.properties新增如下屬性:
      netty-gateway.config2.serverPath=fcgi-bin/BSSP_SFC
      netty-gateway.config2.defaultAddr=http://10.46.158.10:8089/fcgi-bin/BSSP_SFC
      • 配置文件netty-route.properties新增如下屬性:
      netty-gateway.config3.serverPath=fcgi-bin/BSSP_SFC
      netty-gateway.config3.keyWord=HelloWorldNettyGateway
      netty-gateway.config3.matchAddr=http://10.46.158.20:8089/fcgi-bin/BSSP_SFC

        NettyGateway代理轉發測試

      • 發送HelloWorldNettyGateway到NettyGateway,關鍵字匹配成功,路由到http://10.46.158.20:8089/fcgi-bin/BSSP_SFC

      • 發送Tangjie到NettyGateway,關鍵字匹配不成功,路由到默認的http://10.46.158.10:8089/fcgi-bin/BSSP_SFC

       

        到此,整個基于Netty實現的,一個輕量級HTTP代理服務器的主要設計思路已經介紹完了。整個服務器實現代碼非常的少,而且通過簡單地配置,就能很好的滿足實際要求。相比通過“重量級”的服務器:Apache、Nginx,進行HTTP代理轉發而言,提供了另外一種解決問題的思路。在部門的實際部署運行中,這個Netty寫的小而精的服務器,運行良好,很好地幫助測試部門的同學,解決了一個困擾他們很久的問題。

        俗話說得好,黑貓、白貓,抓到老鼠就是好貓。我把這個基于Netty的HTTP代理服務器取名“NettyGateway”,目前把代碼托管在github上面:https://github.com/tang-jie/NettyGateway

        有興趣的朋友,可以關注一下。由于技術能力所限,文中難免有紕漏和不足,大家如果有疑問,歡迎在下方的博客園評論區留言,或者通過github提issue給我。

        最后,感謝您耐心閱讀。如果喜歡本文的話,可以點擊推薦,算是給我一個小小的鼓勵!謝謝大家。

       

        附上本人曾經在博客園發表的,基于Netty框架實際應用的原創文章,有興趣的朋友,可以關聯閱讀。

        基于Netty構建的RPC

        談談如何使用Netty開發實現高性能的RPC服務器

        Netty實現高性能RPC服務器優化篇之消息序列化

        基于Netty打造RPC服務器設計經驗談

        基于Netty構建的簡易消息隊列

        Netty構建分布式消息隊列(AvatarMQ)設計指南之架構篇

        Netty構建分布式消息隊列實現原理淺析

       

      posted @ 2018-04-24 10:13  Newland  閱讀(22637)  評論(18)    收藏  舉報
      主站蜘蛛池模板: 视频一区二区三区高清在线| 亚洲AV日韩AV激情亚洲| 亚洲高清国产拍精品熟女| 免费观看一级欧美大| 灵山县| 中文字幕色偷偷人妻久久| 国产亚洲精品第一综合另类| 一本色道久久加勒比综合| 她也色tayese在线视频 | 人妻少妇精品视频专区| 中文幕无线码中文字夫妻| 久久精品亚洲精品国产区| 四虎精品视频永久免费| 无码人妻丰满熟妇奶水区码| 亚洲激情av一区二区三区| 亚洲色拍拍噜噜噜最新网站| 国产精品自在线拍国产手青青机版| 久久精品国产亚洲成人av| 中文字幕成人精品久久不卡| 亚洲18禁私人影院| 亚在线观看免费视频入口| 久久精品国产清自在天天线| 波多野结衣久久一区二区| 欧美黑人又粗又大又爽免费| 亚洲精品成人福利网站| 亚洲国产精品ⅴa在线观看| 亚洲一区二区无码影院| 中文字幕人妻精品在线| 始兴县| 国产精品人妻熟女男人的天堂| 精品国产AV最大网站| 亚洲一区二区精品动漫| 色综合天天色综合久久网| 欧洲精品色在线观看| 深田えいみ禁欲后被隔壁人妻| 欧美成人精品一级在线观看| 人人妻人人澡人人爽不卡视频| 少妇办公室好紧好爽再浪一点 | 亚洲男女羞羞无遮挡久久丫| 日韩深夜免费在线观看| 国产一区二区三区自拍视频|