當前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]RPC 框架的討論一直是各個技術(shù)交流群中的熱點話題,阿里的 dubbo,新浪微博的 motan,谷歌的 grpc,以及不久前螞蟻金服開源的 sofa,都是比較出名的 RPC 框架。

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

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

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

初識 RPC 接口設(shè)計

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

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

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

serviceA 服務(wù)

serviceA/pom.xml 定義父 pom 文件

<modules> <module>serviceA-apimodule> <module>serviceA-appmodule> modules> <packaging>pompackaging> <groupId>moe.cnkiritogroupId> <artifactId>serviceAartifactId> <version>1.0.0-SNAPSHOTversion> 

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

<parent> <artifactId>serviceAartifactId> <groupId>moe.cnkiritogroupId> <version>1.0.0-SNAPSHOTversion> parent> <packaging>jarpackaging> <artifactId>serviceA-apiartifactId> 

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

<parent> <artifactId>serviceAartifactId> <groupId>moe.cnkiritogroupId> <version>1.0.0-SNAPSHOTversion> parent> <packaging>jarpackaging> <artifactId>serviceA-appartifactId> <build> <plugins> <plugin> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-maven-pluginartifactId> plugin> plugins> build> 

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

舊 RPC 接口的痛點

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

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

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

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

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

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

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

單參數(shù)接口

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

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

public interface StudentApi{ Student findByName(String name); ListfindAllByName(String name); Student findByNameAndNo(String name,String no); Student findByIdcard(String Idcard);
}

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

public interface StudentApi{ Student findBySpec(StudentSpec spec); ListfindListBySpec(StudentListSpec spec); PagefindPageBySpec(StudentPageSpec spec);
}

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

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

public interface SomeProvider { void opA(ARequest request); void opB(BRequest request); CommonResponseopC(CRequest request);
}

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

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

  • 請求入?yún)⒔y(tǒng)一校驗(request.checkParam(); param.checkParam();)

  • 實體變更統(tǒng)一加鎖,降低鎖粒度

  • 請求分類統(tǒng)一處理(if (request instanceof XxxRequest))

  • 請求報文統(tǒng)一記日志(log.setRequest(JsonUtil.getJsonString(request)))

  • 操作成功統(tǒng)一發(fā)消息

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

單參數(shù)入?yún)⒓嫒菪詮?/span>

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

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

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

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

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

  • 有利于 TestNg 集成測試

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

設(shè)計RPC接口時,你有考慮過這些嗎? TestNg 自動化測試

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

接口異常設(shè)計

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

初始方案

public interface ModuleAProvider { void opA(ARequest request); void opB(BRequest request); CommonResponseopC(CRequest request);
}

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

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

public interface ModuleAProvider { void opA(ARequest request) throws ModuleAException; void opB(BRequest request) throws ModuleAException; CommonResponseopC(CRequest request) throws ModuleAException;
}

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

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

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

異常與熔斷

作為系統(tǒng)設(shè)計者,我們應(yīng)該認識到一點:RPC 調(diào)用,失敗是常態(tài)。通常我們需要對 RPC 接口做熔斷處理,比如千米內(nèi)部便集成了 Netflix 提供的熔斷組件 Hystrix。Hystrix 需要知道什么樣的異常需要進行熔斷,什么樣的異常不能夠進行熔斷。在沒有上述的異常設(shè)計之前,回答這個問題可能還有些難度,但有了 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 CommonResponseopC(CRequest request) throws ModuleAException { return moduleAProvider.opC(request);
    }

}

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

API 版本單獨演進

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

A:我下載了你們的代碼庫怎么編譯不通過啊,依賴中 xxx-api-1.1.3 版本的 jar 包找不到了,那可都是 RELEASE 版本啊。

B:你不知道我們 nexus 容量有限,只能保存最新的 20 個 RELEASE 版本嗎?那個 API 現(xiàn)在最新的版本是 1.1.31 啦。

A:啊,這才幾個月就幾十個 RELEASE 版本啦?這接口太不穩(wěn)定啦。

B:其實接口一行代碼沒改,我們業(yè)務(wù)分析是很牛逼的,一直很穩(wěn)定。但是這個 API 是和我們項目一起打包的,我們需求更新一次,就發(fā)布一次,API 就被迫一起升級版本。發(fā)生這種事,大家都不想的。

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

問題回歸

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

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

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

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

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

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

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

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

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

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


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉