久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

設(shè)計(jì)RPC接口時(shí),,你有考慮過這些嗎?

 airen89 2019-01-19

RPC 框架的討論一直是各個(gè)技術(shù)交流群中的熱點(diǎn)話題,,阿里的 dubbo,,新浪微博的 motan,谷歌的 grpc,,以及不久前螞蟻金服開源的 sofa,,都是比較出名的 RPC 框架。RPC 框架,,或者一部分人習(xí)慣稱之為服務(wù)治理框架,,更多的討論是存在于其技術(shù)架構(gòu),比如 RPC 的實(shí)現(xiàn)原理,,RPC 各個(gè)分層的意義,,具體 RPC 框架的源碼分析…但卻并沒有太多話題和“如何設(shè)計(jì) RPC 接口”這樣的業(yè)務(wù)架構(gòu)相關(guān)。

段子

可能很多小公司程序員還是比較關(guān)心這個(gè)問題的,,這篇文章主要分享下一些個(gè)人眼中 RPC 接口設(shè)計(jì)的最佳實(shí)踐,。

初識(shí) RPC 接口設(shè)計(jì)

由于 RPC 中的術(shù)語每個(gè)程序員的理解可能不同,所以文章開始,,先統(tǒng)一下 RPC 術(shù)語,,方便后續(xù)闡述。

大家都知道共享接口是 RPC 最典型的一個(gè)特點(diǎn),,每個(gè)服務(wù)對(duì)外暴露自己的接口,,該模塊一般稱之為 api,;外部模塊想要實(shí)現(xiàn)對(duì)該模塊的遠(yuǎn)程調(diào)用,則需要依賴其 api,;每個(gè)服務(wù)都需要有一個(gè)應(yīng)用來負(fù)責(zé)實(shí)現(xiàn)自己的 api,,一般體現(xiàn)為一個(gè)獨(dú)立的進(jìn)程,該模塊一般稱之為 app,。

api 和 app 是構(gòu)建微服務(wù)項(xiàng)目的最簡單組成部分,,如果使用 maven 的多 module 組織代碼,則體現(xiàn)為如下的形式,。

serviceA 服務(wù)

serviceA/pom.xml 定義父 pom 文件

<modules>
    <module>serviceA-api</module>
    <module>serviceA-app</module>
</modules>

<packaging>pom</packaging>
<groupId>moe.cnkirito</groupId>
<artifactId>serviceA</artifactId>
<version>1.0.0-SNAPSHOT</version>

serviceA/serviceA-api/pom.xml 定義對(duì)外暴露的接口,,最終會(huì)被打成 jar 包供外部服務(wù)依賴

<parent>
    <artifactId>serviceA</artifactId>
    <groupId>moe.cnkirito</groupId>
    <version>1.0.0-SNAPSHOT</version>
</parent>

<packaging>jar</packaging>
<artifactId>serviceA-api</artifactId>

serviceA/serviceA-app/pom.xml 定義了服務(wù)的實(shí)現(xiàn),一般是 springboot 應(yīng)用,,所以下面的配置文件中,,我配置了 springboot 應(yīng)用打包的插件,最終會(huì)被打成 jar 包,,作為獨(dú)立的進(jìn)程運(yùn)行,。

<parent>
    <artifactId>serviceA</artifactId>
    <groupId>moe.cnkirito</groupId>
    <version>1.0.0-SNAPSHOT</version>
</parent>

<packaging>jar</packaging>
<artifactId>serviceA-app</artifactId>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

麻雀雖小,五臟俱全,,這樣一個(gè)微服務(wù)模塊就實(shí)現(xiàn)了,。

舊 RPC 接口的痛點(diǎn)

統(tǒng)一好術(shù)語,這一節(jié)來描述下我曾經(jīng)遭遇過的 RPC 接口設(shè)計(jì)的痛點(diǎn),,相信不少人有過相同的遭遇,。

  • 查詢接口過多。各種 findBy 方法,,加上各自的重載,,幾乎占據(jù)了一個(gè)接口 80% 的代碼量。這也符合一般人的開發(fā)習(xí)慣,,因?yàn)轫撁嫘枰魇礁鳂拥臄?shù)據(jù)格式,,加上查詢條件差異很大,便造成了:一個(gè)查詢條件,,一個(gè)方法的尷尬場景,。這樣會(huì)導(dǎo)致另外一個(gè)問題,需要使用某個(gè)查詢方法時(shí),,直接新增了方法,,但實(shí)際上可能這個(gè)方法已經(jīng)出現(xiàn)過了,,隱藏在了令人眼花繚亂的方法中,。
  • 難以擴(kuò)展。接口的任何改動(dòng),,比如新增一個(gè)入?yún)?,都?huì)導(dǎo)致調(diào)用者被迫升級(jí),,這也通常是 RPC 設(shè)計(jì)被詬病的一點(diǎn),不合理的 RPC 接口設(shè)計(jì)會(huì)放大這個(gè)缺點(diǎn),。
  • 升級(jí)困難,。 在之前的 “初識(shí) RPC 接口設(shè)計(jì)”一節(jié)中,版本管理的粒度是 project,,而不是 module,,這意味著:api 即使沒有發(fā)生變化,app 版本演進(jìn),,也會(huì)造成 api 的被迫升級(jí),,因?yàn)?project 是一個(gè)整體。問題又和上一條一樣了,,api 一旦發(fā)生變化,,調(diào)用者也得被迫升級(jí),牽一發(fā)而動(dòng)全身,。
  • 難以測試,。接口一多,職責(zé)隨之變得繁雜,,業(yè)務(wù)場景各異,,測試用例難以維護(hù)。特別是對(duì)于那些有良好習(xí)慣編寫單元測試的程序員而言,,簡直是噩夢,,用例也得跟著改。
  • 異常設(shè)計(jì)不合理,。在既往的工作經(jīng)歷中曾經(jīng)有一次會(huì)議,,就 RPC 調(diào)用中的異常設(shè)計(jì)引發(fā)了爭議,一派人覺得需要有一個(gè)業(yè)務(wù) CommonResponse,,封裝異常,,每次調(diào)用后,優(yōu)先判斷調(diào)用結(jié)果是否 success,,在進(jìn)行業(yè)務(wù)邏輯處理,;另一派人覺得這比較麻煩,由于 RPC 框架是可以封裝異常調(diào)用的,,所以應(yīng)當(dāng)直接 try catch 異常,,不需要進(jìn)行業(yè)務(wù)包裹。在沒有明確規(guī)范時(shí),,這兩種風(fēng)格的代碼同時(shí)存在于項(xiàng)目中,,十分難看!

在千米網(wǎng)的三個(gè)月中,,看了不少最佳實(shí)踐,。加上一次公司內(nèi)部易永健老師的分享,,涉及到了相同的話題,耳濡目染,,這些曾經(jīng)我發(fā)覺的痛點(diǎn)也逐漸有了解決之道,。

單參數(shù)接口

如果你使用過 springcloud ,可能會(huì)不適應(yīng) http 通信的限制,,因?yàn)?@RequestBody 只能使用單一的參數(shù),,也就意味著,springcloud 構(gòu)建的微服務(wù)架構(gòu)下,,接口天然是單參數(shù)的,。而 RPC 方法入?yún)⒌膫€(gè)數(shù)在語法層面是不會(huì)受到限制的,但如果強(qiáng)制要求入?yún)閱螀?shù),,會(huì)解決一部分的痛點(diǎn),。

使用 Specification 模式解決查詢接口過多的問題

public interface StudentApi{
    Student findByName(String name);
    List<Student> findAllByName(String name);
    Student findByNameAndNo(String name,String no);
    Student findByIdcard(String Idcard);
}

如上的多個(gè)查詢方法目的都是同一個(gè):根據(jù)條件查詢出 Student,只不過查詢條件有所差異,。試想一下,,Student 對(duì)象假設(shè)有 10 個(gè)屬性,最壞的情況下它們的排列組合都可能作為查詢條件,,這便是查詢接口過多的根源,。

public interface StudentApi{
    Student findBySpec(StudentSpec spec);
    List<Student> findListBySpec(StudentListSpec spec);
    Page<Student> findPageBySpec(StudentPageSpec spec);
}

上述接口便是最通用的單參接口,三個(gè)方法幾乎囊括了 99% 的查詢條件,。所有的查詢條件都被封裝在了 StudentSpec,StudentListSpec,StudentPageSpec 之中,,分別滿足了單對(duì)象查詢,批量查詢,,分頁查詢的需求,。如果你了解領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),會(huì)發(fā)現(xiàn)這里借鑒了其中 Specification 模式的思想,。

單參數(shù)易于做統(tǒng)一管理

public interface SomeProvider {
    void opA(ARequest request);
    void opB(BRequest request);
    CommonResponse<C> opC(CRequest request);
}

入?yún)⒅械娜雲(yún)㈦m然形態(tài)各異,,但由于是單個(gè)入?yún)ⅲ钥梢越y(tǒng)一繼承 AbstractBaseRequest,,即上述的 ARequest,,BRequest,CRequest 都是 AbstractBaseRequest 的子類,。在千米內(nèi)部項(xiàng)目中,,AbstractBaseRequest 定義了 traceId、clientIp,、clientType,、operationType 等公共入?yún)ⅲ瑴p少了重復(fù)命名,,我們一致認(rèn)為,,這更加的 OO,。

有了 AbstractBaseRequest,,我們可以更加輕松地在其之上做 AOP,,千米的實(shí)踐中,大概做了如下的操作:

  • 請求入?yún)⒔y(tǒng)一校驗(yàn)(request.checkParam(); param.checkParam();)
  • 實(shí)體變更統(tǒng)一加鎖,,降低鎖粒度
  • 請求分類統(tǒng)一處理(if (request instanceof XxxRequest))
  • 請求報(bào)文統(tǒng)一記日志(log.setRequest(JsonUtil.getJsonString(request)))
  • 操作成功統(tǒng)一發(fā)消息

如果不遵守單參數(shù)的約定,,上述這些功能也并不是無法實(shí)現(xiàn),但所需花費(fèi)的精力遠(yuǎn)大于單參數(shù),,一個(gè)簡單的約定帶來的優(yōu)勢,,我們認(rèn)為是值得的。

單參數(shù)入?yún)⒓嫒菪詮?qiáng)

還記得前面的小節(jié)中,,我提到了 SpringCloud,,在 SpringCloud Feign 中,接口的入?yún)⑼ǔ?huì)被 @RequestBody 修飾,,強(qiáng)制做單參數(shù)的限制,。千米內(nèi)部使用了 Dubbo 作為 Rpc 框架,一般而言,,為 Dubbo 服務(wù)設(shè)計(jì)的接口是不能直接用作 Feign 接口的(主要是因?yàn)?@RequestBody 的限制),,但有了單參數(shù)的限制,便使之成為了可能,。為什么我好端端的 Dubbo 接口需要兼容 Feign 接口,?可能會(huì)有人發(fā)出這樣的疑問,莫急,,這樣做的初衷當(dāng)然不是為了單純做接口兼容,,而是想充分利用 HTTP 豐富的技術(shù)棧以及一些自動(dòng)化工具。

  • 自動(dòng)生成 HTTP 接口實(shí)現(xiàn)(讓服務(wù)端同時(shí)支持 Dubbo 和 HTTP 兩種服務(wù)接口)

看過我之前文章的朋友應(yīng)該了解過一個(gè)設(shè)計(jì):千米內(nèi)部支持的是 Dubbo 協(xié)議和 HTTP 協(xié)議族(如 JSON RPC 協(xié)議,,Restful 協(xié)議),,這并不意味著程序員需要寫兩份代碼,我們可以通過 Dubbo 接口自動(dòng)生成 HTTP 接口,,體現(xiàn)了單參數(shù)設(shè)計(jì)的兼容性之強(qiáng),。

  • 通過 Swagger UI 實(shí)現(xiàn)對(duì) Dubbo 接口的可視化便捷測試

又是一個(gè)兼容 HTTP 技術(shù)棧帶來的便利,在 Restful 接口的測試中,,Swagger 一直是備受青睞的一個(gè)工具,,但可惜的是其無法對(duì) Dubbo 接口進(jìn)行測試。兼容 HTTP 后,,我們只需要做一些微小的工作,,便可以實(shí)現(xiàn) Swagger 對(duì) Dubbo 接口的可視化測試。

  • 有利于 TestNg 集成測試

自動(dòng)生成 TestNG 集成測試代碼和缺省測試用例,,這使得服務(wù)端接口集成測試變得異常簡單,,程序員更能集中精力設(shè)計(jì)業(yè)務(wù)用例,,結(jié)合缺省用例、JPA 自動(dòng)建表和 PowerMock 模擬外部依賴接口實(shí)現(xiàn)本機(jī)環(huán)境,。

TestNg 自動(dòng)化測試

這塊涉及到了公司內(nèi)部的代碼,,只做下簡單介紹,我們一般通過內(nèi)部項(xiàng)目 com.qianmi.codegenerator:api-dubbo-2-restful ,,com.qianmi.codegenerator:api-request-json 生成自動(dòng)化的測試用例,,方便測試。而這些自動(dòng)化工具中大量使用了反射,,而由于單參數(shù)的設(shè)計(jì),,反射用起來比較方便。

接口異常設(shè)計(jì)

首先肯定一點(diǎn),,RPC 框架是可以封裝異常的,,Exception 也是返回值的一部分。在 go 語言中可能更習(xí)慣于返回 err,res 的組合,,但 JAVA 中我個(gè)人更偏向于 try catch 的方法捕獲異常,。RPC 接口設(shè)計(jì)中的異常設(shè)計(jì)也是一個(gè)注意點(diǎn)。

初始方案

public interface ModuleAProvider {
    void opA(ARequest request);
    void opB(BRequest request);
    CommonResponse<C> opC(CRequest request);
}

我們假設(shè)模塊 A 存在上述的 ModuleAProvider 接口,,ModuleAProvider 的實(shí)現(xiàn)中或多或少都會(huì)出現(xiàn)異常,,例如可能存在的異常 ModuleAException,調(diào)用者實(shí)際上并不知道 ModuleAException 的存在,,只有當(dāng)出現(xiàn)異常時(shí),,才會(huì)知曉。對(duì)于 ModuleAException 這種業(yè)務(wù)異常,,我們更希望調(diào)用方能夠顯示的處理,,所以 ModuleAException 應(yīng)該被設(shè)計(jì)成 Checked Excepition。

正確的異常設(shè)計(jì)姿勢

public interface ModuleAProvider {
    void opA(ARequest request) throws ModuleAException;
    void opB(BRequest request) throws ModuleAException;
    CommonResponse<C> opC(CRequest request) throws ModuleAException;
}

上述接口中定義的異常實(shí)際上也是一種契約,,契約的好處便是不需要敘述,,調(diào)用方自然會(huì)想到要去處理 Checked Exception,否則連編譯都過不了,。

調(diào)用方的處理方式

在 ModuleB 中,,應(yīng)當(dāng)如下處理異常:

public class ModuleBService implements ModuleBProvider {
    @Reference
    ModuleAProvider moduleAProvider;

    @Override
    public void someOp() throws ModuleBexception{
        try{
            moduleAProvider.opA(...);
        }catch(ModuleAException e){
            throw new ModuleBException(e.getMessage());
        }
    }

    @Override
    public void anotherOp(){
        try{
            moduleAProvider.opB(...);
        }catch(ModuleAException e){
            // 業(yè)務(wù)邏輯處理
        }
    }
}

someOp 演示了一個(gè)異常流的傳遞,ModuleB 暴露出去的異常應(yīng)當(dāng)是 ModuleB 的 api 模塊中異常類,,雖然其依賴了 ModuleA ,,但需要將異常進(jìn)行轉(zhuǎn)換,或者對(duì)于那些意料之中的業(yè)務(wù)異??梢韵?anotherOp() 一樣進(jìn)行處理,,不再傳遞。這時(shí)如果新增 ModuleC 依賴 ModuleB,那么 ModuleC 完全不需要關(guān)心 ModuleA 的異常,。

異常與熔斷

作為系統(tǒng)設(shè)計(jì)者,,我們應(yīng)該認(rèn)識(shí)到一點(diǎn): RPC 調(diào)用,失敗是常態(tài),。通常我們需要對(duì) RPC 接口做熔斷處理,,比如千米內(nèi)部便集成了 Netflix 提供的熔斷組件 Hystrix。Hystrix 需要知道什么樣的異常需要進(jìn)行熔斷,,什么樣的異常不能夠進(jìn)行熔斷,。在沒有上述的異常設(shè)計(jì)之前,,回答這個(gè)問題可能還有些難度,,但有了 Checked Exception 的契約,一切都變得明了清晰了,。

public class ModuleAProviderProxy {

    @Reference
    private ModuleAProvider moduleAProvider;

    @HystrixCommand(ignoreExceptions = {ModuleAException.class})
    public void opA(ARequest request) throws ModuleAException {
        moduleAProvider.opA(request);
    }

    @HystrixCommand(ignoreExceptions = {ModuleAException.class})
    public void opB(BRequest request) throws ModuleAException {
        moduleAProvider.oBB(request);
    }

    @HystrixCommand(ignoreExceptions = {ModuleAException.class})
    public CommonResponse<C> opC(CRequest request) throws ModuleAException {
        return moduleAProvider.opC(request);
    }

}

如服務(wù)不可用等原因引發(fā)的多次接口調(diào)用超時(shí)異常,,會(huì)觸發(fā) Hystrix 的熔斷;而對(duì)于業(yè)務(wù)異常,,我們則認(rèn)為不需要進(jìn)行熔斷,,因?yàn)閷?duì)于接口 throws 出的業(yè)務(wù)異常,我們也認(rèn)為是正常響應(yīng)的一部分,,只不過借助于 JAVA 的異常機(jī)制來表達(dá),。實(shí)際上,和生成自動(dòng)化測試類的工具一樣,,我們使用了另一套自動(dòng)化的工具,,可以由 Dubbo 接口自動(dòng)生成對(duì)應(yīng)的 Hystrix Proxy。我們堅(jiān)定的認(rèn)為開發(fā)體驗(yàn)和用戶體驗(yàn)一樣重要,,所以公司內(nèi)部會(huì)有非常多的自動(dòng)化工具,。

API 版本單獨(dú)演進(jìn)

引用一段公司內(nèi)部的真實(shí)對(duì)話:

A:我下載了你們的代碼庫怎么編譯不通過啊,依賴中 xxx-api-1.1.3 版本的 jar 包找不到了,,那可都是 RELEASE 版本啊,。 B:你不知道我們 nexus 容量有限,只能保存最新的 20 個(gè) RELEASE 版本嗎,?那個(gè) API 現(xiàn)在最新的版本是 1.1.31 啦,。 A:啊,這才幾個(gè)月就幾十個(gè) RELEASE 版本啦,?這接口太不穩(wěn)定啦,。 B: 其實(shí)接口一行代碼沒改,我們業(yè)務(wù)分析是很牛逼的,,一直很穩(wěn)定,。但是這個(gè) API 是和我們項(xiàng)目一起打包的,我們需求更新一次,就發(fā)布一次,,API 就被迫一起升級(jí)版本,。發(fā)生這種事,大家都不想的,。

在單體式架構(gòu)中,,版本演進(jìn)的單位是整個(gè)項(xiàng)目。微服務(wù)解決的一個(gè)關(guān)鍵的痛點(diǎn)便是其做到了每個(gè)服務(wù)的單獨(dú)演進(jìn),,這大大降低了服務(wù)間的耦合,。正如我文章開始時(shí)舉得那個(gè)例子一樣:serviceA 是一個(gè)演進(jìn)的單位,serviceA-api 和 serviceA-app 這兩個(gè) Module 從屬于 serviceA,,這意味著 app 的一次升級(jí),,將會(huì)引發(fā) api 的升級(jí),因?yàn)樗麄兪枪采?!而從微服?wù)的使用角度來看,,調(diào)用者關(guān)心的是 api 的結(jié)構(gòu),而對(duì)其實(shí)現(xiàn)壓根不在乎,。所以對(duì)于 api 定義未發(fā)生變化,,其 app 發(fā)生變化的那些升級(jí),其實(shí)可以做到對(duì)調(diào)用者無感知,。在實(shí)踐中也是如此 api 版本的演進(jìn)應(yīng)該是緩慢的,,而 app 版本的演進(jìn)應(yīng)該是頻繁的。 所以,,對(duì)于這兩個(gè)演進(jìn)速度不一致的模塊,,我們應(yīng)該單獨(dú)做版本管理,他們有自己的版本號(hào),。

問題回歸

  • 查詢接口過多,。各種 findBy 方法,加上各自的重載,,幾乎占據(jù)了一個(gè)接口 80% 的代碼量,。這也符合一般人的開發(fā)習(xí)慣,因?yàn)轫撁嫘枰魇礁鳂拥臄?shù)據(jù)格式,,加上查詢條件差異很大,,便造成了:一個(gè)查詢條件,一個(gè)方法的尷尬場景,。這樣會(huì)導(dǎo)致另外一個(gè)問題,,需要使用某個(gè)查詢方法時(shí),直接新增了方法,,但實(shí)際上可能這個(gè)方法已經(jīng)出現(xiàn)過了,,隱藏在了令人眼花繚亂的方法中。

解決方案:使用單參+Specification 模式,降低重復(fù)的查詢方法,,大大降低接口中的方法數(shù)量,。

  • 難以擴(kuò)展。接口的任何改動(dòng),,比如新增一個(gè)入?yún)?,都?huì)導(dǎo)致調(diào)用者被迫升級(jí),這也通常是 RPC 設(shè)計(jì)被詬病的一點(diǎn),,不合理的 RPC 接口設(shè)計(jì)會(huì)放大這個(gè)缺點(diǎn),。

解決方案:單參設(shè)計(jì)其實(shí)無形中包含了所有的查詢條件的排列組合,可以直接在 app 實(shí)現(xiàn)邏輯的新增,,而不需要對(duì) api 進(jìn)行改動(dòng)(如果是參數(shù)的新增則必須進(jìn)行 api 的升級(jí),,參數(shù)的廢棄可以用 @Deprecated 標(biāo)準(zhǔn))。

  • 升級(jí)困難,。 在之前的 “初識(shí) RPC 接口設(shè)計(jì)”一節(jié)中,,版本管理的粒度是 project,,而不是 module,,這意味著:api 即使沒有發(fā)生變化,app 版本演進(jìn),,也會(huì)造成 api 的被迫升級(jí),,因?yàn)?project 是一個(gè)整體。問題又和上一條一樣了,,api 一旦發(fā)生變化,,調(diào)用者也得被迫升級(jí),牽一發(fā)而動(dòng)全身,。

解決方案:以 module 為版本演進(jìn)的粒度,。api 和 app 單獨(dú)演進(jìn),減少調(diào)用者的不必要升級(jí)次數(shù),。

  • 難以測試,。接口一多,職責(zé)隨之變得繁雜,,業(yè)務(wù)場景各異,,測試用例難以維護(hù)。特別是對(duì)于那些有良好習(xí)慣編寫單元測試的程序員而言,,簡直是噩夢,,用例也得跟著改。

解決方案:單參數(shù)設(shè)計(jì)+自動(dòng)化測試工具,,打造良好的開發(fā)體驗(yàn),。

  • 異常設(shè)計(jì)不合理。在既往的工作經(jīng)歷中曾經(jīng)有一次會(huì)議,就 RPC 調(diào)用中的異常設(shè)計(jì)引發(fā)了爭議,,一派人覺得需要有一個(gè)業(yè)務(wù) CommonResponse,,封裝異常,每次調(diào)用后,,優(yōu)先判斷調(diào)用結(jié)果是否 success,,在進(jìn)行業(yè)務(wù)邏輯處理;另一派人覺得這比較麻煩,,由于 RPC 框架是可以封裝異常調(diào)用的,,所以應(yīng)當(dāng)直接 try catch 異常,不需要進(jìn)行業(yè)務(wù)包裹,。在沒有明確規(guī)范時(shí),,這兩種風(fēng)格的代碼同時(shí)存在于項(xiàng)目中,十分難看,!

解決方案:Checked Exception+正確異常處理姿勢,,使得代碼更加優(yōu)雅,降低了調(diào)用方不處理異常帶來的風(fēng)險(xiǎn),。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多