<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          優(yōu)雅的實(shí)現(xiàn)對(duì)外接口,要注意哪些問(wèn)題?

          共 16237字,需瀏覽 33分鐘

           ·

          2021-06-22 18:36

          點(diǎn)擊下方“IT牧場(chǎng)”,選擇“設(shè)為星標(biāo)”

          作者:xiaolizh

          blog.csdn.net/xiaolizh/article/details/83011031

          博主之前做過(guò)恒豐銀行代收付系統(tǒng)(相當(dāng)于支付接口),包括現(xiàn)在的oltpapi交易接口和虛擬業(yè)務(wù)的對(duì)外提供數(shù)據(jù)接口。

          總之,當(dāng)你做了很多項(xiàng)目寫(xiě)了很多代碼的時(shí)候,就需要回過(guò)頭來(lái),多總結(jié)總結(jié),這樣你會(huì)看到更多之前寫(xiě)代碼的時(shí)候看不到的東西,也能更明白為什么要這樣做。

          做接口需要考慮的問(wèn)題

          什么是接口

          接口無(wú)非就是客戶端請(qǐng)求你的接口地址,并傳入一堆該接口定義好的參數(shù),通過(guò)接口自身的邏輯處理,返回接口約定好的數(shù)據(jù)以及相應(yīng)的數(shù)據(jù)格式。

          接口怎么開(kāi)發(fā)

          接口由于本身的性質(zhì),由于和合作方對(duì)接數(shù)據(jù),所以有以下幾點(diǎn)需要在開(kāi)發(fā)的時(shí)候注意:

          1、定義接口入?yún)ⅲ簩?xiě)好接口文檔

          2、定義接口返回?cái)?shù)據(jù)類型:一般都需要封裝成一定格式,確定返回json還是xml報(bào)文等

          見(jiàn)如下返回?cái)?shù)據(jù)定義格式:

          Result

          package com.caiex.vb.model;
           
          import java.io.Serializable;
          import javax.xml.bind.annotation.XmlAccessType;
          import javax.xml.bind.annotation.XmlAccessorType;
          import javax.xml.bind.annotation.XmlType;
           
          @XmlAccessorType(XmlAccessType.FIELD)
          @XmlType(name = "Result", propOrder = { "resultCode""resultMsg" })
          public class Result implements Serializable {
           private static final long serialVersionUID = 10L;
           protected int resultCode;
           protected String resultMsg;
           
           public int getResultCode() {
            return this.resultCode;
           }
           
           public void setResultCode(int value) {
            this.resultCode = value;
           }
           
           public String getResultMsg() {
            return this.resultMsg;
           }
           
           public void setResultMsg(String value) {
            this.resultMsg = value;
           }
          }

          Response

          package com.caiex.vb.model;
           
          import java.io.Serializable;
           
          public class Response implements Serializable {
           
           private static final long serialVersionUID = 2360867989280235575L;
           
           private Result result;
           
           private Object data;
           
           public Result getResult() {
            if (this.result == null) {
             this.result = new Result();
            }
            return result;
           }
           
           public void setResult(Result result) {
            this.result = result;
           }
           
           public Object getData() {
            return data;
           }
           
           public void setData(Object data) {
            this.data = data;
           }
           
          }

          3、確定訪問(wèn)接口的方式,get or post等等,可以根據(jù)restful接口定義規(guī)則RESTful API:RESTful API 最后,關(guān)注碼猿技術(shù)專欄公眾號(hào),回復(fù)“面試寶典”,送你一份面試題寶典!

          4、定義一套全局統(tǒng)一并通用的返回碼,以幫助排查問(wèn)題;

          reponse code

           public static int NO_AGENT_RATE = 1119;  //未找到兌換率
           
           public static int SCHEME_COMMIT_FAIL = 4000;  //方案提交失敗
           
           public static int SCHEME_CONFIRMATION = 4001;  //方案確認(rèn)中
           
           public static int SCHEME_NOT_EXIST = 4002;  //方案不存在
           
           public static int SCHEME_CANCEL= 4005;  //方案不存在
           
           //。。。。

          5、統(tǒng)一的異常處理:應(yīng)該每個(gè)系統(tǒng)都需要一套統(tǒng)一的異常處理

          package com.caiex.vb.interceptor;
           
          import javax.servlet.http.HttpServletRequest;
           
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.web.bind.annotation.ControllerAdvice;
          import org.springframework.web.bind.annotation.ExceptionHandler;
          import org.springframework.web.bind.annotation.ResponseBody;
           
          import com.caiex.vb.model.Response;
           
          @ControllerAdvice
          @ResponseBody
          public class GlobalExceptionHandler {
           
           private  Logger  logger = LoggerFactory.getLogger(this.getClass()); 
           
              /**
               * 所有異常報(bào)錯(cuò)
               * @param request
               * @param exception
               * @return
               * @throws Exception
               */

              @ExceptionHandler(value=Exception.class)  
              public Response allExceptionHandler(HttpServletRequest request,  
                      Exception exceptionthrows Exception  
              
          {  
               logger.error("攔截到異常:", exception);
                  Response response = new Response();
                  response.setData(null);
                  response.getResult().setResultCode(9999);
                  response.getResult().setResultMsg("系統(tǒng)繁忙");
                  return response;  
              }  
           
          }

          6、攔截器鏈設(shè)置:合作方訪問(wèn)接口的時(shí)候,會(huì)根據(jù)你接口定義好的傳參訪問(wèn)你的接口服務(wù)器,但是會(huì)存在接口參數(shù)類型錯(cuò)誤或者格式不對(duì),必傳參數(shù)沒(méi)傳的問(wèn)題,甚至一些惡意請(qǐng)求,都可以通過(guò)攔截器鏈進(jìn)行前期攔截,避免造成接口服務(wù)的壓力。

          還有很重要的一點(diǎn),加簽驗(yàn)簽也可以在攔截器設(shè)置。繼承WebMvcConfigurerAdapter實(shí)現(xiàn)springboot的攔截器鏈。實(shí)現(xiàn)HandlerInterceptor方法編寫(xiě)業(yè)務(wù)攔截器。

          SignInterceptor

          package com.caiex.vb.interceptor;
           
           
          import javax.annotation.Resource;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
           
          import org.apache.commons.lang3.StringUtils;
          import org.apache.logging.log4j.LogManager;
          import org.apache.logging.log4j.Logger;
          import org.springframework.stereotype.Component;
          import org.springframework.web.servlet.HandlerInterceptor;
          import org.springframework.web.servlet.ModelAndView;
           
          import com.alibaba.fastjson.JSON;
          import com.caiex.redis.service.api.RedisApi;
          import com.caiex.vb.model.Response;
          import com.caiex.vb.utils.CaiexCheckUtils;
           
          @Component
          public class SignInterceptor extends BaseValidator implements HandlerInterceptor{
           
           private Logger logger = LogManager.getLogger(this.getClass());
           
           @Resource
           private RedisApi redisApi;
           
           
           public void afterCompletion(HttpServletRequest arg0,
             HttpServletResponse arg1, Object arg2, Exception arg3)

             throws Exception 
          {
            // TODO Auto-generated method stub
            
           }
           
           public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
             Object arg2, ModelAndView arg3)
           throws Exception 
          {
            // TODO Auto-generated method stub
            
           }
           
           public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
             Object arg2)
           throws Exception 
          {
            if(isTestIpAddr(arg0)){
             return true;
            }
            String securityKey = redisApi.hGet("securityKey", arg0.getParameter("agentid"));
            if(StringUtils.isEmpty(securityKey)){
             Response response = new Response();
             response.setData(null);
             response.getResult().setResultCode(8001);
             response.getResult().setResultMsg("缺少私鑰, 渠道號(hào):" + arg0.getParameter("agentid"));
             logger.error("缺少私鑰, 渠道號(hào):" + arg0.getParameter("agentid"));
             InterceptorResp.printJson(arg1, response);
             return false;
            }
            
            if(StringUtils.isEmpty(arg0.getParameter("sign")) || !arg0.getParameter("sign").equals(CaiexCheckUtils.getSign(arg0.getParameterMap(), securityKey))){
             Response response = new Response();
             response.setData(null);
             response.getResult().setResultCode(3203);
             response.getResult().setResultMsg("參數(shù)簽名認(rèn)證失敗");
             logger.error("參數(shù)簽名認(rèn)證失?。? + JSON.toJSONString(arg0.getParameterMap()) + " securityKey = " + securityKey);
             InterceptorResp.printJson(arg1, response);
             return false;
            }else{
             return true;
            }
           }
          }

          WebAppConfigurer

          package com.caiex.oltp.config;
           
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.ComponentScan;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.web.servlet.config.annotation.EnableWebMvc;
          import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
          import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
           
          import com.caiex.oltp.interceptor.APILimitRateValidator;
          import com.caiex.oltp.interceptor.CommonValidator;
          import com.caiex.oltp.interceptor.DDSAuthValidator;
          import com.caiex.oltp.interceptor.QueryPriceParamsValidator;
          import com.caiex.oltp.interceptor.TradeParamsValidator;
           
           
          @EnableWebMvc
          @Configuration
          @ComponentScan
          public class WebAppConfigurer extends WebMvcConfigurerAdapter {
           
             @Bean
             CommonValidator commonInterceptor() {
                   return new CommonValidator();
               }
           
             @Bean
             DDSAuthValidator ddsAuthInterceptor() {
                   return new DDSAuthValidator();
               }
           
             @Bean
             QueryPriceParamsValidator queryPriceParamsInterceptor() {
                   return new QueryPriceParamsValidator();
               }
           
             @Bean
             TradeParamsValidator tradeParamsInterceptor() {
                   return new TradeParamsValidator();
               }
             
            @Bean
             APILimitRateValidator aPILimitRateInterceptor() {
                   return new APILimitRateValidator();
               }
           
           
               @Override
               public void addInterceptors(InterceptorRegistry registry) {
                
                //訪問(wèn)速率限制
                registry.addInterceptor(aPILimitRateInterceptor())
                .addPathPatterns("/*/*");
                //.addPathPatterns("/price/getPriceParam");
           
                //參數(shù)簽名認(rèn)證
                   registry.addInterceptor(ddsAuthInterceptor())
                   .addPathPatterns("/tradeState/*")
                   .addPathPatterns("/recycle/*")
                   .addPathPatterns("/matchInfo/*")
                   .addPathPatterns("/price/tradeTicketParam");
                   
                   //公共參數(shù)檢查
                   registry.addInterceptor(commonInterceptor())
                   .addPathPatterns("/price/tradeTicketParam")
                   .addPathPatterns("/tradeState/*")
                   .addPathPatterns("/recycle/*");
                   
                   //詢價(jià)參數(shù)校驗(yàn)
                   registry.addInterceptor(queryPriceParamsInterceptor())
                   .addPathPatterns("/price/getPriceParam");
                   
                   //交易參數(shù)檢查
                   registry.addInterceptor(tradeParamsInterceptor())
                   .addPathPatterns("/price/tradeTicketParam");
                   
                   super.addInterceptors(registry);
               }
          }

          7、token令牌和sign數(shù)字簽名實(shí)現(xiàn)數(shù)據(jù)保密性。

          創(chuàng)建令牌(Token)

          為保證請(qǐng)求的合法性,我們提供第三方創(chuàng)建令牌接口,某些接口需要通過(guò)token驗(yàn)證消息的合法性,以免遭受非法攻擊。最后,關(guān)注碼猿技術(shù)專欄公眾號(hào),回復(fù)“面試寶典”,送你一份面試題寶典!

          token過(guò)期時(shí)間目前暫時(shí)定為1天,由于考慮到合作方往往是分布式環(huán)境,多臺(tái)機(jī)器都有可能申請(qǐng)token,為了降低合作方保證token一致性的難度,調(diào)用接口創(chuàng)建token成功以后一分鐘以內(nèi),再次請(qǐng)求token返回的數(shù)據(jù)是一樣的。

          獲取私鑰

          獲取用于數(shù)字簽名的私鑰,第三方獲取的私鑰需妥善保存,并定期更新,私鑰只參與數(shù)字簽名,不作為參數(shù)傳輸。

          數(shù)字簽名方式:

          參數(shù)簽名;簽名方式:所有值不為null的參數(shù)(不包括本參數(shù))均參與數(shù)字簽名,按照“參數(shù)名+參數(shù)值+私鑰”的格式得到一個(gè)字符串,再將這個(gè)字符串MD5一次就是這個(gè)參數(shù)的值。(示例:h15adc39y9ba59abbe56e057e60f883g),所以需要先獲取私鑰。

          驗(yàn)簽方式:

          將用戶的所有非null參數(shù)放入定義好排序規(guī)則的TreeSet中進(jìn)行排序,再用StringBuilder按照按照“參數(shù)名+參數(shù)值+私鑰”的格式得到一個(gè)字符串(私鑰從redis拿),再將這個(gè)字符串MD5一次就是這個(gè)參數(shù)的值。將這個(gè)值與用戶傳來(lái)的sign簽名對(duì)比,相同則通過(guò),否則不通過(guò)。

          private String createToken(){
            String utk = "Msk!D*"+System.currentTimeMillis()+"UBR&FLP";
            logger.info("create token   --- "+Md5Util.md5(utk));
            return Md5Util.md5(utk);
           }

          8、接口限流

          有時(shí)候服務(wù)器壓力真的太大,以防交易接口被擠死,就可以對(duì)一些其他不影響主要業(yè)務(wù)功能并且計(jì)算量大的接口做限流處理。RateLimit--使用guava來(lái)做接口限流,當(dāng)接口超過(guò)指定的流量時(shí),就不處理該接口的請(qǐng)求。詳細(xì)可看RateLimit。也可參考其他限流框架。

          9、協(xié)議加密,http升級(jí)成https;

          為什么要升級(jí)呢,為了保證數(shù)據(jù)的安全性。當(dāng)使用https訪問(wèn)時(shí),數(shù)據(jù)從客戶端到服務(wù)斷,服務(wù)端到客戶端都加密,即使黑客抓包也看不到傳輸內(nèi)容。當(dāng)然還有其他好處,這里不多講。但這也是開(kāi)發(fā)接口項(xiàng)目需要注意的一個(gè)問(wèn)題。


          如何提高接口的高并發(fā)和高可用

          接口開(kāi)發(fā)好了,接下來(lái)就討論接口的可用性問(wèn)題。首先我們要將高并發(fā)和高可用區(qū)分一下,畢竟高可用是在可用的情況,只是很慢或者效率不高。其實(shí)也可以歸為一類問(wèn)題,但是不重要啦,重要的是怎么提高你寫(xiě)的接口的訪問(wèn)速度和性能。


          接口的高并發(fā)解決方案(其實(shí)沒(méi)有唯一答案,業(yè)界針對(duì)不同業(yè)務(wù)也有很多不同的方法)

          當(dāng)訪問(wèn)一個(gè)接口獲取數(shù)據(jù)時(shí),發(fā)現(xiàn)返回很慢,或者總是超時(shí),如果排除網(wǎng)絡(luò)的原因,那就是接口服務(wù)器壓力太大,處理不過(guò)來(lái)了。在世界杯期間,我們查看后臺(tái)日志總是connection by reset和borker pipe和一些超時(shí)問(wèn)題。

          這時(shí)候,你可能遇到了高并發(fā)和高可用問(wèn)題。但是,不管遇到什么問(wèn)題,都不能臆斷和亂改,你得需要找到慢的原因,才能對(duì)癥下藥,亂改可能會(huì)導(dǎo)致其他問(wèn)題的出現(xiàn)。首先,解決高并發(fā)問(wèn)題的三個(gè)方向是負(fù)載均衡,緩存集群。

          負(fù)載均衡

          我們使用的是阿里云服務(wù)器的負(fù)載均衡,后臺(tái)分布式服務(wù)管理,我們運(yùn)維小哥哥搭建了一套k8s,可以自由在k8s上擴(kuò)展服務(wù)節(jié)點(diǎn),各個(gè)服務(wù)結(jié)點(diǎn)也能隨內(nèi)存的使用自動(dòng)漂移,不用多說(shuō),k8s真的很厲害,感興趣的同學(xué)可以詳細(xì)去學(xué)。

          那么問(wèn)題來(lái)了,阿里云的負(fù)載均衡怎么對(duì)應(yīng)到k8s的負(fù)載均衡呢?這個(gè)涉及到了k8s的service暴露的一些特點(diǎn),簡(jiǎn)單說(shuō)就是k8s把所有集群的服務(wù)都通過(guò)指定的內(nèi)部負(fù)載均衡,在指定的服務(wù)器上暴露,然后我們又把這幾個(gè)服務(wù)器接在阿里云負(fù)載均衡下,這個(gè)涉及的細(xì)節(jié)和配置很多。

          當(dāng)然,除nginx外,還有其他負(fù)載均衡解決方案,軟件硬件都有,硬件如f5等。

          • 阿里云的nginx負(fù)載均衡,我們使用的是加權(quán)輪詢策略,其實(shí)輪詢是最低效的方式;

          這就是最基本的負(fù)載均衡實(shí)例,但這不足以滿足實(shí)際需求;目前Nginx服務(wù)器的upstream模塊支持6種方式的分配:

          負(fù)載均衡策略

          集群

          首先,通過(guò)排查問(wèn)題,發(fā)現(xiàn)是oltpapi接口服務(wù)處理請(qǐng)求很慢,大量請(qǐng)求過(guò)來(lái),總是超時(shí)和中斷連接,這時(shí)候,我們想著最簡(jiǎn)單的方法就是加機(jī)器,給oltp接口服務(wù)多加幾臺(tái)機(jī)器。

          嗯,一切都很完美,如預(yù)期進(jìn)行,但是加到一定數(shù)量,你發(fā)現(xiàn),怎么不起效果,異步響應(yīng)還是很慢,或者更直觀的說(shuō),消息隊(duì)列出現(xiàn)了嚴(yán)重的消息堆積。這時(shí)候,你發(fā)現(xiàn)出現(xiàn)了新的問(wèn)題或者瓶頸,這個(gè)問(wèn)題已經(jīng)不是說(shuō)加oltp服務(wù)器能解決了,那么,就需要去重新定位問(wèn)題。

          發(fā)現(xiàn)是消息堆積,消息堆積就是生產(chǎn)者過(guò)快,導(dǎo)致消費(fèi)者消費(fèi)不過(guò)來(lái),這時(shí)候,你就需要增加消費(fèi)者的消費(fèi)數(shù)量。給風(fēng)控系統(tǒng)多加幾臺(tái)機(jī)器,讓消費(fèi)者和生產(chǎn)者達(dá)到一定平衡。

          這里有個(gè)誤區(qū),你可能以為是rocketmq的broker數(shù)量過(guò)少,增加broker數(shù)量,其實(shí)當(dāng)消費(fèi)者和生產(chǎn)者保持一樣的速度時(shí),消息肯定不對(duì)堆積,按照原始的broker數(shù)量就足夠。但是增加broker也會(huì)使得消息得到盡快的處理,提升一定效率。

          緩存

          當(dāng)加機(jī)器不能解決問(wèn)題時(shí),或者說(shuō)沒(méi)那么多服務(wù)器可使用時(shí),那么就要重代碼層面解決高并發(fā)問(wèn)題。Redis 是一個(gè)高性能的key-value數(shù)據(jù)庫(kù),當(dāng)獲取數(shù)據(jù)從數(shù)據(jù)庫(kù)拿很慢時(shí),就可以存儲(chǔ)到redis,從redis取值。

          • 用ConcurrentHashMap緩存對(duì)象,并設(shè)置過(guò)期時(shí)間
          • redis緩存數(shù)據(jù),結(jié)合spring定時(shí)任務(wù)定時(shí)獲取不會(huì)經(jīng)常改動(dòng)的key
          • 提高使用redis的效率:比如使用mGet一次獲取多個(gè)key
          • ....等

          接口高可用問(wèn)題

          高可用問(wèn)題應(yīng)該上升到整個(gè)服務(wù)的架構(gòu)問(wèn)題上,就是說(shuō)在搭建整體系統(tǒng)是就應(yīng)該考慮到。高可用問(wèn)題是以單點(diǎn)故障,訪問(wèn)速度慢的問(wèn)題為主導(dǎo)。

          • redis主從分布式(redis的單點(diǎn)故障和訪問(wèn)速度的提高和主從備份)
          • 分布式dubbo服務(wù)的zookeeper主從集群
          • strom的主從集群
          • ...等

          總結(jié)

          下面對(duì)接口開(kāi)發(fā)服務(wù)做一些總結(jié):

          1.是拉還是推:

          當(dāng)接口作為數(shù)據(jù)源時(shí),還要考慮數(shù)據(jù)是讓合作方主動(dòng)過(guò)來(lái)拉還是數(shù)據(jù)有變化就推送呢,當(dāng)然是推的效果更好,但是如何有效的推數(shù)據(jù),不推重復(fù)數(shù)據(jù)等都是需要根據(jù)實(shí)際業(yè)務(wù)考慮的問(wèn)題。

          2.多臺(tái)分布式服務(wù)器上,怎么保證交易的冪等和訂單的唯一性

          當(dāng)接口服務(wù)和合作方都處于分布式情況下,就很容易出現(xiàn)一個(gè)訂單號(hào)申請(qǐng)多次交易請(qǐng)求,但是根據(jù)冪等性,一張彩票只能交易一次,并且每次不管何時(shí)請(qǐng)求,結(jié)果都應(yīng)該一樣不會(huì)改變。

          這種情況下,我們?cè)趺幢WC唯一性呢,我們需要把該訂單和訂單狀態(tài)存redis,每次請(qǐng)求時(shí)去看是否訂單已存在。但可能這次交易不成功,下次這張票還可以繼續(xù)交易,可以生成新的訂單號(hào)啊。

          redis的setNX是一個(gè)很好的解決方案,意思是當(dāng)存在該key時(shí),返回false,當(dāng)沒(méi)有時(shí),該key和value插入成功。用作檢查訂單是否正在提交,如果是,則阻塞本次請(qǐng)求,避免重復(fù)提交 ,可以設(shè)置過(guò)期時(shí)間3s。提交之前鎖定訂單,防止重復(fù)提交。

          3.處理時(shí)間超過(guò)10s,自動(dòng)返回該訂單交易失敗

          總之,博主發(fā)現(xiàn),在高并發(fā)場(chǎng)景下,導(dǎo)致服務(wù)崩潰的原因還是redis和數(shù)據(jù)庫(kù),可能是redis讀寫(xiě)太慢,或者數(shù)據(jù)庫(kù)的一些sql使用不當(dāng),或者沒(méi)建索引導(dǎo)致讀寫(xiě)很慢。

          總之,這是一條很漫長(zhǎng)的路,我們都需要慢慢積累經(jīng)驗(yàn)和學(xué)習(xí)前人更優(yōu)秀的解決辦法。

          干貨分享

          最近將個(gè)人學(xué)習(xí)筆記整理成冊(cè),使用PDF分享。關(guān)注我,回復(fù)如下代碼,即可獲得百度盤(pán)地址,無(wú)套路領(lǐng)??!

          ?001:《Java并發(fā)與高并發(fā)解決方案》學(xué)習(xí)筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學(xué)習(xí)筆記;?003:《Java面試寶典》?004:《Docker開(kāi)源書(shū)》?005:《Kubernetes開(kāi)源書(shū)》?006:《DDD速成(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)速成)》?007:全部?008:加技術(shù)群討論

          加個(gè)關(guān)注不迷路

          喜歡就點(diǎn)個(gè)"在看"唄^_^

          瀏覽 74
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美人妻在线中文视频 | 久久久久亚洲AV无码网影音先锋 | 日本福利视频一区 | 91成人吃鸡吧 | AA片在线播放 |