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

如何手搓一個自定義的RPC(遠程過程調用框架)

開發 架構
本文首先簡單介紹了一下RPC的概念、應用場景及常用的RPC框架,然后講述了一下如何自己手動實現一個RPC框架的基本功能。

1.RPC(遠程過程調用概述)  

遠程過程調用(RPC, Remote Procedure Call)是一種通過網絡從遠程計算機程序上請求服務,而無需了解網絡細節的通信技術。在分布式系統中,RPC是一種常用的技術,能夠簡化客戶端與服務器之間的交互。本文將介紹如何基于Netty(網絡編程框架)實現一個自定義的簡單的RPC框架。

首先簡單介紹一下RPC 主要特點:

1.1 RPC遠程過程調用的主要特點

  • 透明性:調用方(客戶端)調用遠程服務就像調用本地API函數一樣,而無需關心執行過程中的底層的網絡通信細節。
  • 客戶端-服務器模型:RPC通常基于客戶端-服務器模型,客戶端發送請求到服務器,服務器處理請求并返回結果。
  • 序列化及反序列化:RPC需要將請求參數序列化成字節流(即數據轉換成網絡可傳輸的格式)并通過網絡傳輸到服務器端,服務器端接收到字節流后,需按照約定的協議將數據進行反序列化(即恢復成三原始格式)
  • 同步及異步調用:RPC支持同步、異步調用。同步調用會阻塞直到服務器返回結果,或超時、異常等。而異步調用則可以立即返回,通過注冊一個回調函數,在有結果返回的時候再進行處理。從而讓客戶端可以繼續執行其它操作。
  • 錯誤處理:PRC由于涉及網絡通信,因此需要處理各種可能的網絡異常,如網絡故障,服務宕機,請求超時,服務重啟、或上下線、擴縮容等,這些對調用方來說需要保持透明。
  • 協議及傳輸居:RPC可以基于多種協議和傳輸層實現,如HTTP、TCP等,本文采用的是基于TCP的自定義協議。

1.2 RPC的應用場景

  • 分布式系統:多個服務之間進行通信,如微服務框架。
  • 客戶端-服務器架構:如移動應用與后臺服務器的交互。
  • 跨平臺調用:不同技術棧之間的服務調用。
  • API服務:通過公開API對外提供功能,使用客戶端能方便使用服務提供的功能,如支付網關,身份驗證服務等。
  • 大數據處理:在大數據處理框架中,不同節點之間需要頻繁通信來協調任務和交接數據,RPC可以提供高效的節點通信機制,如Hadoop 和Spark等大數據框架中節點間的通信。
  • 云計算:在云計算環境中,服務通常分布在多個虛擬機或容器中,通過RPC實現實現服務間的通信和管理。
  • 跨網絡服務調用:當應用需要調用部署在不同網絡中的服務時,RPC提供了一種簡單而建議目前的調用方式,如。跨數據中心或嘴唇地域的服務調用。

1.3 常見的RPC框架

  • JSF:京東開源的分布式服務框架,提供高性能、可擴展、穩定的服務治理能力,支持服務注冊及發現,負載均衡、容錯機制、服務監控、多種協議支持等。
  • gRPC:基于HTTP/2和Protocol Buffers的高性能RPC框架,由Google開發。
  • Dubbo:一個高性能、輕量級的Java RPC框架,用于提供基于接口的遠程服務調用,支持負載均衡、服務自動注冊及服務、容錯等。
  • JSON-RPC:使用JSON格式編碼調用和結果的RPC協議。
  • Apache Thrift:由Facebook開發,支持多種編程語言和協議

2.實現自定義的RPC

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板后進行模板加載,加載階段會將產物轉換為視圖樹的結構,轉換完成后將通過表達式引擎解析表達式并取得正確的值,通過事件解析引擎解析用戶自定義事件并完成事件的綁定,完成解析賦值以及事件綁定后進行視圖的渲染,最終將

要實現一個自定義的RPC框架需解決以下幾個主要問題:

1.客戶端調用:客戶端調用本地的代理函數(stub代碼,這個函數負責將調用轉換為RPC請求)。這其實就是一個接口描述文件,它可以有多種形式如JSON、XML、甚至是一份word文檔或是口頭約定均可,只要客戶端及服務端都是遵守這份接口描述文件契約即可。在我們的實際開發中一種常見的方式是服務提供者發布一個包含服務接口類的jar包到maven 中央倉庫,調用方通過pom文件將之依賴到本地。

2.參數序列化:代理函數將調用參數進行序列化,并將請求發送到服務器。

3.服務端數據接收:服務器端接收到請求,并將其反序列化,恢復成原始參數。

4.執行遠程過程:服務端調用實際的服務過程(函數)并獲取結果。

5.返回結果:服務端將調用結果進行序列化,并通過網絡傳給客戶端。

6.客戶端接收調用結果:客戶到接收到服務端傳輸的字節流,進行反序列化,轉換為實際的結果數據格式,并返回到原始調用方。

下面需我們通過代碼一一展示上述各功能是如何實現的。

2.1 自定義通信協議

本文的目的是要實現一個自定義通信協議的遠程調用框架,所以首先要定義一個通信協議數據格式。

整個自定義協議總體上分為Header 及 Body Content兩部分;Header 占16個字節,又分為4個部分。

前2位為魔法值用于Netty編解碼組件,解決網絡通信中的粘包、半包等問題,此處不展開細講。

msgtype用于表示消息的類型,如request(請求)、respone(響應)、heartbeat(心跳)等。

code 占1位,表示請求的響應狀態,成功還是失敗。

request id占8位,表示請求的序列號,用于后續調用結果的匹配,保證線程內唯一。

body size 占4位,表示實現請求內容的長度,在反序化時讀取此長度的內容字節,解析出正確的數據。

客戶端、服務端在通信過程中都要按照上述約定的通信協議進行數據的編、解碼工作。

2.2 客戶端調用

2.2.1 客戶端的使用

客戶端一般通過接口代理工廠通過動態代理技術來生成一個代理實例,所有的遠程調用中的細節,如參數序列化,網絡傳輸,異常處理等都隱藏在代理實例中實現,對調用方來說調用過程是透明的,就像調用本地方法一樣。

首先看一下客戶端的使用方式,本文假設一個IShoppingCartService (購物車)的接口類,基中有一個方法根據傳入的用戶pin,返回購物車詳情。

//接口方法
ShoppingCart shopping(String pin);
//客戶端通過代理工廠實現接口的一個代理實例
IShoppingCartService serviceProxy = ProxyFactory.factory(IShoppingCartService.class)                
                .setSerializerType(SerializerType.JDK) //客戶端設置所使用的序列化工具,此處為JDK原生
                .newProxyInstance(); //返回代理 實現
//像調用本地方法一樣,調用此代理實例的shopping 方法
ShoppingCart result = serviceProxy.shopping("userPin");
log.info("result={}", JSONObject.toJSONString(result));
2.2.2 客戶端代理工廠的核心功能
public class ProxyFactory<I> {
    //……省略
    /**
     * 代理對象
     *
     * @return
     */
    public I newProxyInstance() {     
        //服務的元數據信息
        ServiceData serviceData = new ServiceData(
                group, //分組
                providerName, //服務名稱,一般為接口的class的全限定名稱
                StringUtils.isNotBlank(version) ? version : "1.0.0" //版本號
        );


        //調用器
        Calller caller = newCaller().timeoutMillis(timeoutMillis);
        //集群策略,用于實現快速失敗或失敗轉等功能
        Strategy strategy = StrategyConfigContext.of(strategy, retries);
        Object handler = null;
        switch (invokeType) {
            case "syncCall":
                //同步調用handler
                handler = new SyncCaller(serviceData, caller);
                break;
            case "asyncCall":
                //異步調用handler
                handler = new AsyncCaller(client.appName(), serviceData, caller, strategy);
                break;
            default:
                throw new RuntimeException("未知類型: " + invokeType);
        }


        //返回代理實例
        return ProxyEnum.getDefault().newProxy(interfaceClass, handler);
    }
    //……省略
}

代碼 ProxyEnum.getDefault().newProxy(interfaceClass, handler) 返回一個具體的代理實例,此方法要求傳入兩個參數,interfaceClass  被代理的接口類class,即服務方所發布的服務接口類。

handler 為動態代理所需要代碼增強邏輯,即所有的調用細節都由此增強類完成。按照動態代理的實現方式的不同,本文支持兩種動態代理方式:

1.JDK動態代碼,如采用此方式,handler 需要實現接口 InvocationHandler

2.ByteBuddy,它是一個用于在運行時生成、修改和操作Java類的庫,允許開發者通過簡單的API生成新的類或修改已有的類,而無需手動編寫字節碼,它廣泛應用于框架開發、動態代理、字節碼操作和類加載等領域。

本文默認采用第二種方式,通過代碼簡單展示一下代理實例的的生成方式。

//方法newProxy 的具體實現
public <T> T newProxy(Class<T> interfaceType, Object handler) {
            Class<? extends T> cls = new ByteBuddy()
                     //生成接口的子類
                    .subclass(interfaceType) 
                     //默認代理接口中所有聲明的方法
                    .method(ElementMatchers.isDeclaredBy(interfaceType))
                     //代碼增強,即接口中所有被代理的方法都
                     //委托給用戶自定義的handler處理,這也是動態代理的意義所在
                   .intercept(MethodDelegation.to(handler, "handlerInstance"))
                    .make()
                     //通過類加載器加載
                   .load(interfaceType.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getLoaded();


            try {
                //通過newInstance構建一個代理實例并返回    
               return cls.newInstance();
            } catch (Throwable t) {
                ……
            }
        }

本文以同步調用為例,現在展示一下 SyncInvoker 的具體實現邏輯。

public class SyncCaller extends AbstractCaller {
    //……省略   
    /**
     * @RuntimeType 的作用提示ByteBuddy根據被攔截方法的實際類型,對此攔截器的返回值進行類型轉換
     */
    @RuntimeType
    public Object syncCall(@Origin Method method, @AllArguments @RuntimeType Object[] args) throws Throwable {
        //封裝請求的接口中的方法名及方法參數,組成一個request請求對象
        StarGateRequest request = createRequest(methodName, args);
        //集群容錯策略調度器接口
        //提供快速失敗,失敗轉移等策略供調用方選擇,此處默認采用了快速失敗的策略
        Invoker invoker = new FastFailInvoker();
        //returnType 的類型決定了泛型方法的實際結果類型,用于后續調用結果的類型轉換
        Future<?> future = invoker.invoke(request, method.getReturnType());
        if (sync) {
            //同步調用,線程會阻塞在get方法,直到超時或結果可用
            Object result = future.getResult();
            return result;
        } else {
            return future;
        }
    }
}


//同步,異步調用的關鍵點就在于InvokeFuture,它繼承了Java的CompletionStage類,用于異步編程

通過以上核心代碼,客戶端就完成了服務調用環節,下一步RPC框架需要將客戶端請求的接口方法及方法參數進行序列化并通過網絡進行傳輸。下面通過代碼片段展示一下序列化的實現方式。

2.2.3 請求參數序列化

我們將請求參數序列化的目的就是將具體的請求參數轉換成字節組,填充進入上述自定義協議的 body content 部分。下面通過代碼演示一下如何進行反序列化。

本文默認采用JDK原生的對象序列化及反序列化框架,也可通過SPI技術擴展支持Protocol Buffers等。

//上述代碼行Future<?> future = invoker.invoke(request, method.getReturnType());
//具體實現


public <T> Future<T> invoke(StarGateRequest request, Class<T> returnType) throws Exception {
        //對象序列化器,默認為JDK
        final Serializer _serializer = serializer();
        //message對象包含此次請求的接口名,方法名及實際參數列表
        final Message message = request.message();
        //通過軟負載均衡選擇一個 Netty channel
        Channel channel = selectChannel(message.getMetadata());
        byte code = _serializer.code();
        //將message對象序列成字節數組
        byte[] bytes = _serializer.writeObject(message);
        request.bytes(code, bytes);


        //數據寫入 channel 并返回 future 約定,用于同步或異步獲得調用結果
        return write(channel, request, returnType);
    }
//對象的序列化,JDK 原生方式
public <T> byte[] writeObject(T obj) {
        ByteArrayOutputStream buf = OutputStreams.getByteArrayOutputStream();
        try (ObjectOutputStream output = new ObjectOutputStream(buf)) {
            output.writeObject(obj);
            output.flush();
            return buf.toByteArray();
        } catch (IOException e) {
            ThrowUtil.throwException(e);
        } finally {
            OutputStreams.resetBuf(buf);
        }
        return null; 
    }
2.2.4 請求參數通過網絡發送
//上述代碼  write(channel, request, returnType);
//具體實現
protected <T> DefaultFuture<T> write(final Channel channel,
                                               final StarGateRequest request,
                                               final Class<T> returnType) {
        //……省略


        //調用結果占位 future對象,這也是promise編程模式
        final Future<T> future = DefaultFuture.newFuture(request.invokeId(), channel, timeoutMillis, returnType);


        //將請求負載對象寫入Netty channel通道,并綁定監聽器處理寫入結果
        channel.writeAndFlush(request).addListener((ChannelFutureListener) listener -> {
            if (listener.isSuccess()) {
                //網絡寫入成功
                ……
            } else {
                //異常時,構造造調用結果,供調用方進行處理
                DefaultFuture.errorFuture(channel, response, dispatchType);
            }
        });


        //因為Netty 是非阻塞的,所以寫入后可立刻返回
        return future;
    }

2.2.4.1 Netty 消息編碼器

消息寫入Netty channel 后,會依次經過 channel pipline 上所安裝的各種handler處理,然后再通過物理網絡將數據發送出去,這里展示了客戶端及服務端所使用的自定義編、解解器。

//自定義的編碼器 繼承自Netty 的 MessageToByteEncoder
public class StarGateEncoder extends MessageToByteEncoder<Payload> {


    //……省略


    private void doEncodeRequest(RequestPayload request, ByteBuf out) {
        byte sign = StarGateProtocolHeader.toSign(request.serializerCode(), StarGateProtocolHeader.REQUEST);
        long invokeId = request.invokeId();
        byte[] bytes = request.bytes();
        int length = bytes.length;


        out.writeShort(StarGateProtocolHeader.Head)  //寫入兩個字節
                .writeByte(sign)              //寫入1個字節
                .writeByte(0x00)            //寫入1個字節
                .writeLong(invokeId)          //寫入8個節節
                .writeInt(length)             //寫入4個字節
                .writeBytes(bytes);
    }


}

至此,通過上述核心代碼,客戶的請求已經按照自定義的協議格式進行了序列化,并把數據寫入到Netty channel中,最后通過物理網絡傳輸到服務器端。

2.3 服務端接收數據

2.3.1 消息解碼器

服務器端接收到客戶端的發送的數據后,需要進行正確的消息解碼,下面是解碼器的實現。

//消息解碼器,繼承自Netty 的ReplayingDecoder,將客戶端請求解碼為 RequestPayload 對象,供業務處理handler使用
public class StarGateDecoder extends ReplayingDecoder<StarGateDecoder.State> {


    //……省略


    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        switch (state()) {
            case HEAD:
                checkMagic(in.readShort());         // HEAD
                checkpoint(State.HEAD);
            case SIGN:
                header.sign(in.readByte());         // 消息標志位
                checkpoint(State.STATUS);
            case STATUS:
                header.status(in.readByte());       // 狀態位
                checkpoint(State.ID);
            case ID:
                header.id(in.readLong());           // 消息id
                checkpoint(State.BODY_SIZE);
            case BODY_SIZE:
                header.bodySize(in.readInt());      // 消息體長度
                checkpoint(State.BODY);
            case BODY:
                switch (header.messageCode()) {
                    //……省略
                    case StarGateProtocolHeader.REQUEST: {
                        //消息體長度信息
                        int length = checkBodySize(header.bodySize());
                        byte[] bytes = new byte[length];
                        //讀取指定長度字節
                        in.readBytes(bytes);
                        //調用請求
                        RequestPayload request = new RequestPayload(header.id());
                        //設置序列化器編碼,有效載荷
                        request.bytes(header.serializerCode(), bytes);
                        out.add(request);
                        break;
                    }


                    default:
                        throw new Exception("錯誤標志位");
                }
                checkpoint(State.HEAD);
        }
    }


   //……省略
}
2.3.2 請求參數反序列化
//服務端 Netty channel pipline 上所安裝的業務處理 handler
//業務處理handler 對RequestPayload 所攜帶的字節數組進行反序列化,解析出客戶端所傳遞的實際參數
public class ServiceHandler extends ChannelInboundHandlerAdapter {


    //……省略


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel ch = ctx.channel();
        if (msg instanceof RequestPayload) {       
            StarGateRequest request = new StarGateRequest((RequestPayload) msg);
            //約定的反序列化器, 由客戶端設置
            byte code = request.serializerCode();
            Serializer serializer = SerializerFactory.getSerializer(code);
            //實際請求參數字組數組
            byte[] bytes = payload.bytes();
            //對象反序列化
            Message message = serializer.readObject(bytes, Message.class);
            log.info("message={}", JSONObject.toJSONString(message));


            request.message(message);


            //業務處理
            process(message);
        } else {
            //引用釋放
            ReferenceCountUtil.release(msg);
        }
    }


    //……省略
}
2.3.3 處理客戶端請求

經過反序列化后,服務端可以知道用戶所請求的是哪個接口、方法、以及實際的參數值,下一步就可進行真實的方法調用。

//處理調用
public void process(Message message) {         
   try {
       ServiceMetadata metadata = msg.getMetadata(); //客戶端請求的元數據
       String providerName = metadata.getProviderName(); //服務名,即接口類名


       //根據接口類名,查找服務端實現此接口的類的全限定類名
       providerName = findServiceImpl(providerName);
       String methodName = msg.getMethodName();  //方法名
       Object[] args = msg.getArgs();    //客戶設置的實際參數


       //線程上下文類加載器
       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
       //加載具體實現類
       Class<?> clazz = classLoader.loadClass(providerName);
       //創建接口類實例
       Object instance = clazz.getDeclaredConstructor().newInstance();


       Method method = null;
       Class<?>[] parameterTypes = new Class[args.length];
       for (int i = 0; i < args.length; i++) {
           parameterTypes[i] = args[i].getClass();
       }
       method = clazz.getMethod(methodName, parameterTypes);


       //反射調用 
       Object invokeResult = method.invoke(instance, args);
       } catch (Exception e) {
            log.error("調用異常:", e);
           throw new RuntimeException(e);
       }


       //處理同步調用結果
      doProcess(invokeResult);


}
2.3.4 返回調用結果

通過反射調用接口實現類,獲取調用結果,然后對結果進行序列化并包裝成response響應消息,將消息寫入到channel, 經過channel pipline 上所安裝的編碼器對消息對象進行編碼,最后發送給調用客戶端。

//處理同步調用結果,并將結果寫回到 Netty channel
private void doProcess(Object realResult) {
        ResultWrapper result = new ResultWrapper();
        result.setResult(realResult);
        byte code = request.serializerCode();
        Serializer serializer = SerializerFactory.getSerializer(code);
        //new response 響應消息對象
        Response response = new Response(request.invokeId());
        //調用結果序列成字節數組
        byte[] bytes = serializer.writeObject(result);
        response.bytes(code, bytes);
        response.status(Status.OK.value());


        //響應消息對象 response 寫入 Netty channel
        channel.writeAndFlush(response).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if (channelFuture.isSuccess()) {
                    log.info("響應成功");
                } else {
                    //記錄調用失敗日志
                    log.error("響應失敗, channel: {}, cause: {}.", channel, channelFuture.cause());
                }
            }
        });
    }

同樣的,消息寫入channel 后,先依次經過pipline 上所安裝的 消息編碼器,再發送給客戶端。具體編碼方式同客戶端編碼器類似,此處不再贅述。

2.4 客戶端接收調用結果

客戶端收到服務端寫入響應消息后,同樣經過Netty channel pipline 上所安裝的解碼器,進行正確的解碼。然后再對解碼后的對象進行正確的反序列化,最終獲得調用結果 。具體的解碼,反序列化過程不再贅述,流程基本同上面服務端的解碼及反序列化類似。

public class consumerHandler extends ChannelInboundHandlerAdapter {


    //……省略


    //客戶端處理所接收到的消息
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel ch = ctx.channel();
        if (msg instanceof ResponseMessage) {
            try {
                //類型轉換
                ResponseMessage responseMessage= (ResponseMessage)msg
                StarGateResponse response = new StarGateResponse(ResponseMessage.getMsg());
                byte code = response.serializerCode();
                Serializer serializer = SerializerFactory.getSerializer(code);
                byte[] bytes = responseMessage.bytes();
                //反序列化成調用結果的包裝類
                Result result = serializer.readObject(bytes, Result.class);
                response.result(result);


                //處理調用結果
                long invokeId = response.id();
                //通過 rnvokeid,從地緩存中拿到客戶端調用的結果點位對象 futrue
                DefaultFuture<?> future = FUTURES_MAP.remove(invokeId);


                //判斷調用是否成功
                byte status = response.status();
                if (status == Status.OK.value()) {
                    //對調用結果進行強制類型轉換,并設置future結果,對阻塞在future.get()的客戶端同步調用來說,調用返回。
                    complete((V) response.getResult());
                } else {
                    //todo 處理異常
                }


            } catch (Throwable t) {
                log.error("調用記錄: {}, on {} #channelRead().", t, ch);
            }
        } else {
            log.warn("消息類型不匹配: {}, channel: {}.", msg.getClass(), ch);
            //計數器減1
            ReferenceCountUtil.release(msg);
        }
    }


}

下面再通過一個簡單的調用時序圖展示一下一次典型的Rpc調用所經歷的步驟。

3.結尾

本文首先簡單介紹了一下RPC的概念、應用場景及常用的RPC框架,然后講述了一下如何自己手動實現一個RPC框架的基本功能。目的是想讓大家對RPC框架的實現有一個大概思路,并對Netty 這一高效網絡編程框架有一個了解,通過對Netty 的編、解碼器的學習,了解如何自定義一個私有的通信協議。限于篇幅本文只簡單講解了RPC的核心的調用邏輯的實現。真正生產可用的RPC框架還需要有更多復雜的功能,如限流、負載均衡、融斷、降級、泛型調用、自動重連、自定義可擴展的攔截器等等。

另外RPC框架中一般有三種角色,服務提供者、服務消費者、注冊中心,本文并沒有介紹注冊中心如何實現。并假定服務提供者已經將服務發布到了注冊中心,服務消費者跟服務提供者之間建立起了TCP 長連接。

后續會通過其它篇章介紹注冊中心,服務自動注冊,服務發現等功能的實現原理。

責任編輯:武曉燕 來源: 京東技術
相關推薦

2015-06-09 13:31:29

Hadoop RPC遠源碼解析

2022-03-01 11:38:51

RPC框架后端

2023-05-18 08:47:42

2022-06-04 11:12:12

RPCREST協議

2020-11-02 08:19:18

RPC框架Java

2009-06-15 10:00:08

FluorineFx庫Silverlight

2012-11-19 11:07:42

IBMdw

2019-02-21 10:35:44

Windows10遠程過程調用錯誤

2022-06-06 09:28:36

ReactHook

2017-06-20 12:48:55

React Nativ自定義模塊Note.js

2009-04-28 13:25:36

Ajax函數Java

2021-01-06 05:25:56

項目Springboot應用

2020-09-18 10:12:24

KotlinTCP網絡協議

2021-03-09 15:23:45

鴻蒙HarmonyOS應用開發

2024-05-31 08:45:24

2018-09-18 09:38:11

RPC遠程調用網絡通信

2021-01-19 09:19:33

RPC調用過程框架

2010-09-14 16:59:39

SQL自定義函數

2009-07-06 16:20:50

JSP自定義標簽

2018-03-16 08:41:16

Oracle存儲函數
點贊
收藏

51CTO技術棧公眾號

久久电影一区二区| 欧美亚洲动漫另类| 久久99精品久久久久子伦| wwwxxx亚洲| 国产欧美日韩| 9191国产精品| 人妻久久久一区二区三区| 青青草av免费在线观看| 久久亚洲综合| 欧美成人精品一区二区| 97人妻天天摸天天爽天天| 午夜av成人| 亚洲综合色婷婷| 欧美视频1区| 国产白浆在线观看| 丝袜亚洲另类欧美综合| 伦理中文字幕亚洲| av黄色在线免费观看| 日韩亚洲精品在线观看| 狠狠躁夜夜躁人人躁婷婷91 | www国产在线| 乱码第一页成人| 九九精品在线播放| 怡红院一区二区三区| 精品精品精品| 欧美一区二区免费视频| 久久精品视频91| 91九色在线看| 一区二区三区在线观看视频 | 亚洲无中文字幕| 亚洲人a成www在线影院| 娇妻高潮浓精白浆xxⅹ| 日韩三级一区| 欧美中文字幕不卡| 国产女女做受ⅹxx高潮| 国产三线在线| 亚洲激情成人在线| 在线精品亚洲一区二区| 国产经典自拍视频在线观看| 99久久综合国产精品| 亚洲在线第一页| 91午夜交换视频| 日韩精品一级二级| 欧美在线视频观看| 国产无遮挡aaa片爽爽| 亚洲精品va| 久久在线精品视频| 美国黄色片视频| 日韩欧美视频专区| 最新国产成人av网站网址麻豆| 蜜臀av一区二区三区有限公司| 韩国女主播一区二区三区| 日韩精品一区二区三区在线| 不卡的av中文字幕| 农村妇女一区二区| 欧美日韩一级片在线观看| 日av中文字幕| 日韩av超清在线观看| 色狠狠一区二区三区香蕉| 久久久久人妻精品一区三寸| 性欧美18~19sex高清播放| 黄色91在线观看| 成人免费aaa| 电影网一区二区| 欧美性xxxx18| 国产黄色特级片| 欧美成人xxxx| 4438x成人网最大色成网站| 亚洲精品成人在线播放| 成人短视频软件网站大全app| 欧美疯狂性受xxxxx喷水图片| 狠狠操狠狠干视频| 日本在线一区二区三区| 亚洲精品在线网站| 色噜噜在线观看| 国内精品久久久久久久久电影网 | 羞羞电影在线观看www| 亚洲乱码精品一二三四区日韩在线| 男女激烈动态图| 538在线观看| 色偷偷久久一区二区三区| 能看的毛片网站| 成人免费观看49www在线观看| 欧美一级欧美一级在线播放| 丰满岳乱妇一区二区| 中文字幕伦av一区二区邻居| 中文字幕免费精品一区高清| 在线观看黄网址| 一区在线免费观看| 国产suv精品一区二区| 国产精品久久久久久在线| 成人免费毛片a| 欧美18视频| 二区三区在线观看| 亚洲444eee在线观看| 欧美自拍小视频| 韩国一区二区三区视频| 日韩电影中文字幕在线| 69xxx免费| 国产一区欧美| 国产精品久久久久久av福利| 99热这里只有精品1| 99久久99久久免费精品蜜臀| 亚洲精品在线免费看| av伦理在线| 欧美日韩亚洲综合在线| 亚洲图片欧美另类| 成人激情诱惑| 555www成人网| 国产片高清在线观看| 久久影院午夜片一区| 欧美aaa在线观看| 卡通欧美亚洲| 亚洲精品在线观看视频| 日本视频在线免费| 国产精品久久777777毛茸茸| 国产三级精品网站| 美国成人毛片| 亚洲国产精品麻豆| 亚洲高清视频免费| 国产影视一区| 91成人性视频| 欧美特黄一级视频| 日韩一区中文字幕| 中文字幕欧美人妻精品一区| 超碰97久久国产精品牛牛| 久久精品国产视频| 超碰在线观看91| 91在线观看地址| av日韩在线看| 久久九九精品视频| 色偷偷av亚洲男人的天堂| 日本高清不卡码| gogogo免费视频观看亚洲一| 日本三级中文字幕在线观看| 韩国理伦片久久电影网| 一区二区三区四区在线观看视频 | 欧美色爱综合网| 中文字幕av网址| 日韩香蕉视频| 国产九色91| 92久久精品| 精品国产一区二区精华| 69av.com| 国产成人av一区| 女人色极品影院| 亚洲成人偷拍| 欧美国产视频一区二区| h狠狠躁死你h高h| 亚洲三级免费观看| 青娱乐精品在线| 亚洲综合专区| 波多野结衣久草一区| 菠萝蜜视频国产在线播放| 欧美精品在线视频| 视频国产一区二区| 国产一区二区三区综合| 免费观看国产视频在线| 亚洲国产中文在线二区三区免| 欧美成人精品一区| 日韩中文字幕免费观看| 精品久久久久久| 亚洲区免费视频| 日韩av网站免费在线| 亚洲欧美久久久久一区二区三区| 成人在线高清| 久久精品这里热有精品| 国产黄a三级三级三级| 亚洲影视资源网| 国产老熟女伦老熟妇露脸| 久久国产66| 亚洲一区不卡在线| 欧美成人精品午夜一区二区| 久久久久久中文字幕| 肉丝一区二区| 欧美日韩视频在线第一区 | 亚洲va欧美va人人爽| 亚洲男人在线天堂| 日韩av中文字幕一区二区| 一区二区三区三区在线| av不卡一区二区| 青青草一区二区| 麻豆传媒在线完整视频| 亚洲精品一区二区三区福利| 天堂а√在线中文在线新版 | 精品国产乱码久久久久夜深人妻| 亚洲日本免费| 日韩精品在线视频观看| 中国一级片在线观看| 国产一区视频导航| h无码动漫在线观看| 欧亚精品一区| 成人精品视频在线| 日本在线观看高清完整版| 欧美大片在线观看一区二区| 懂色av.com| 国产精品嫩草久久久久| 久久精品亚洲天堂| 亚洲看片免费| 日韩精品久久一区| a看欧美黄色女同性恋| 欧美亚洲另类制服自拍| 77导航福利在线| 日韩女优电影在线观看| 看黄色一级大片| 亚洲美女视频在线| av网站有哪些| 精品亚洲国产成人av制服丝袜| 成人免费aaa| 亚洲乱码精品| 精品不卡在线| 视频91a欧美| 国产ts人妖一区二区三区 | 天堂在线亚洲视频| 日韩精彩视频| 白嫩白嫩国产精品| 国产精品成人av在线| www红色一片_亚洲成a人片在线观看_| 亚洲第一中文字幕在线观看| 羞羞色院91蜜桃| 一区二区三区资源| 懂色av蜜臀av粉嫩av永久| 高清av一区二区| wwwwww.色| 国一区二区在线观看| 尤物一区二区三区| 九九综合九九| 国产精品视频福利| 欧美亚洲福利| 日韩av男人的天堂| heyzo一区| 久热精品视频在线观看| 亚洲s色大片| 亚洲精品有码在线| 亚洲国产精品久久人人爱潘金莲| 欧美中文字幕一区| 伊人成年综合网| 欧美日韩激情小视频| www欧美com| 国产精品精品国产色婷婷| 人人爽人人爽人人片| 99久久精品情趣| 苍井空张开腿实干12次| 韩国精品久久久| 三区视频在线观看| 热久久一区二区| 乱子伦视频在线看| 先锋影音国产一区| 久色视频在线播放| 亚洲激情黄色| 国产1区2区3区中文字幕| 欧美a级一区| 天天综合中文字幕| 91欧美国产| 特色特色大片在线| 亚洲午夜精品一区二区国产 | 五月天婷婷激情网| 精品91自产拍在线观看一区| 天天插天天干天天操| 精品噜噜噜噜久久久久久久久试看| 国产麻豆免费视频| 51精品国自产在线| 人妻无码一区二区三区久久99| 日韩视频一区在线观看| 国产黄a三级三级看三级| 精品国产在天天线2019| 欧美 日韩 国产 精品| 日韩精品一区二区三区中文精品| 国产wwwwwww| 亚洲国产成人久久综合| 国产免费不卡视频| 欧美本精品男人aⅴ天堂| 99久久精品国产一区二区成人| 日韩欧美在线网站| 亚洲免费视频网| 亚洲国产日韩欧美在线动漫| 免费a级毛片在线观看| 亚洲人成网站777色婷婷| 国产视频第一页在线观看| 亚洲精品456在线播放狼人| 韩国福利在线| 色狠狠久久aa北条麻妃 | 久久艳片www.17c.com| 成视频免费观看在线看| 欧美成人sm免费视频| 国产精品蜜臀| 国产精品免费久久久久久| 香蕉久久一区| 国产成人精品福利一区二区三区| 婷婷亚洲成人| 亚洲一区二区在线看| 欧美激情1区2区3区| av影院在线播放| 三级久久三级久久| 91欧美一区二区三区| 成人午夜伦理影院| 日韩av网站在线播放| 亚洲影视在线播放| 久久精品无码av| 日韩女优毛片在线| 牛牛影视精品影视| 欧美另类第一页| xxxx成人| 91久久国产婷婷一区二区| 成人性生交大片免费看中文视频| 欧美激情导航| 国内精品亚洲| 色噜噜狠狠一区二区| 成人av在线影院| 欧美丰满熟妇bbbbbb| 欧美视频免费在线观看| 国产精品玖玖玖| 一本一道久久a久久精品逆3p | 亚洲在线视频播放| 精品国产乱码久久久久久久 | 福利视频一二区| 男女性色大片免费观看一区二区| 中文字幕一二三区| 国产精品久久久久久一区二区三区 | x99av成人免费| 日本不卡网站| 成人久久一区二区| 欧美日韩激情在线一区二区三区| 免费的一级黄色片| 免费高清不卡av| 91精彩刺激对白露脸偷拍| 一区二区三区四区不卡视频| 亚洲精品一区二三区| 亚洲老头同性xxxxx| 色呦呦在线视频| 国产色综合天天综合网| 欧美日韩国产高清电影| 精品中文字幕av| 国产成人一区在线| www.自拍偷拍| 欧美日韩在线视频首页| 国产免费黄色大片| 亚洲男人的天堂网站| 天堂av在线网| 国产精品毛片va一区二区三区| 国产国产精品| 色呦色呦色精品| 亚洲国产成人在线| 手机av免费观看| 在线中文字幕日韩| 午夜精品久久久久久久久久蜜桃| 国产精品毛片va一区二区三区| 极品少妇一区二区三区| 久久久久99人妻一区二区三区| 18欧美乱大交hd1984| 国产精品女人久久久| 中文字幕视频在线免费欧美日韩综合在线看 | 日本理论片午伦夜理片在线观看| 国产免费一区视频观看免费 | 青少年xxxxx性开放hg| 黄网站免费久久| 四虎影视一区二区| 欧美日韩二区三区| 国产精品刘玥久久一区| 成人免费观看a| 中文字幕一区二区三区久久网站| 中文字幕乱码在线人视频| 亚洲欧美日韩在线播放| 国产精品美女一区| 国外成人免费在线播放| 福利在线一区| 欧美三级在线观看视频| 国产午夜精品理论片a级大结局| 国产精品suv一区| 亚洲男人的天堂网站| www.成人| 男人天堂网站在线| 处破女av一区二区| 欧美亚洲另类小说| 在线观看欧美日韩| 伊人亚洲精品| 国产美女主播在线| 99热国产精品| 性高潮视频在线观看| 欧美xxxx做受欧美.88| 国产成人精品福利| 国产亚洲欧美在线视频| 国产精品看片你懂得| 国产福利第一视频| 欧美精品在线视频观看| 日韩极品少妇| 制服丝袜综合网| 亚洲国产欧美日韩另类综合| 四虎精品在线| 国产精品久久一| 亚洲精品麻豆| 国产精品高清无码在线观看| 欧美日韩精品免费观看视频 | 国产精品手机在线播放| 91福利国产成人精品播放| 亚洲精品国产精华液| 香港一级纯黄大片| 91久久精品在线| 奶水喷射视频一区| 国产午夜精品理论片在线|