分布式服務框架XXL-RPC


《分布式服務框架XXL-RPC》

Actions Status Maven Central GitHub release GitHub stars Docker Status License donate

[TOCM]

[TOC]

一、簡介

1.1 概述

XXL-RPC 是一個分布式服務框架,提供穩定高性能的RPC遠程服務調用功能。擁有"高性能、分布式、注冊中心、負載均衡、服務治理"等特性。現已開放源代碼,開箱即用。

1.2 特性

  • 1、快速接入:接入步驟非常簡潔,兩分鍾即可上手;
  • 2、服務透明:系統完整的封裝了底層通信細節,開發時調用遠程服務就像調用本地服務,在提供遠程調用能力時不損失本地調用的語義簡潔性;
  • 3、多調用方案:支持 SYNC、ONEWAY、FUTURE、CALLBACK 等方案;
  • 4、多通訊方案:支持 TCP 和 HTTP 兩種通訊方式進行服務調用;
  • 5、多序列化方案:支持 HESSIAN、HESSIAN1 等方案;
  • 6、負載均衡/軟負載:提供豐富的負載均衡策略,包括:輪詢、隨機、LRU、LFU、一致性HASH等;
  • 7、注冊中心:可選組件,支持服務注冊並動態發現(內置“輕量級注冊中心 / 服務管心”(推薦)、“Local注冊中心”等);可選擇不啟用,直接指定服務提供方機器地址通訊;
  • 8、服務治理:提供服務治理中心,可在線管理注冊的服務信息,如服務鎖定、禁用等;
  • 9、服務監控:可在線監控服務調用統計信息以及服務健康狀況等(計划中);
  • 10、容錯:服務提供方集群注冊時,某個服務節點不可用時將會自動摘除,同時消費方將會移除失效節點將流量分發到其余節點,提高系統容錯能力。
  • 11、解決1+1問題:傳統分布式通訊一般通過nginx或f5做集群服務的流量負載均衡,每次請求在到達目標服務機器之前都需要經過負載均衡機器,即1+1,這將會把流量放大一倍。而XXL-RPC將會從消費方直達服務提供方,每次請求直達目標機器,從而可以避免上述問題;
  • 12、高兼容性:得益於優良的兼容性與模塊化設計,不限制外部框架;除 spring/springboot 環境之外,理論上支持運行在任何Java代碼中,甚至main方法直接啟動運行;
  • 13、泛化調用:服務調用方不依賴服務方提供的API;

1.3 背景

RPC(Remote Procedure Call Protocol,遠程過程調用),調用遠程服務就像調用本地服務,在提供遠程調用能力時不損失本地調用的語義簡潔性;

一般公司,尤其是大型互聯網公司內部系統由上千上萬個服務組成,不同的服務部署在不同機器,跑在不同的JVM上,此時需要解決兩個問題:

  • 1、如果我需要依賴別人的服務,但是別人的服務在遠程機器上,我該如何調用?
  • 2、如果其他團隊需要使用我的服務,我該怎樣發布自己的服務供他人調用?

“XXL-RPC”可以高效的解決這個問題:

  • 1、如何調用:只需要知曉遠程服務的stub和地址,即可方便的調用遠程服務,同時調用透明化,就像調用本地服務一樣簡單;
  • 2、如何發布:只需要提供自己服務的stub和地址,別人即可方便的調用我的服務,在開啟注冊中心的情況下服務動態發現,只需要提供服務的stub即可;

1.4 下載

文檔地址

源碼倉庫地址

源碼倉庫地址 Release Download
https://github.com/xuxueli/xxl-rpc Download
https://gitee.com/xuxueli0323/xxl-rpc Download

技術交流

1.5 環境

  • Maven3+
  • Jdk1.7+
  • Tomcat7+

二、快速入門( springboot版本 + 輕量級注冊中心 )

2.1 准備工作

解壓源碼,按照maven格式將源碼導入IDE, 使用maven進行編譯即可,源碼結構如下:

源碼目錄介紹:
- /doc - /xxl-rpc-admin :輕量級服務(注冊)中心,可選模塊; - /xxl-rpc-core :核心依賴; - /xxl-rpc-samples :示例項目;  - /xxl-rpc-sample-frameless :無框架版本示例;  - /xxl-rpc-sample-springboot :springboot版本示例;  - /xxl-rpc-sample-springboot-api :公共API接口  - /xxl-rpc-sample-springboot-client :服務消費方 invoker 調用示例;  - /xxl-rpc-sample-springboot-server :服務提供方 provider 示例;

2.2 搭建部署 "服務(注冊)中心"

推薦使用 "xxl-rpc-admin" 作為輕量級服務(注冊)中心。非常輕量級,一分鍾可完成部署工作。 注冊中心為可選模塊,可以不使用注冊中心,也可以選型其他注冊中心。

2.2.1 初始化“服務(注冊)中心數據庫”

請下載項目源碼並解壓,獲取 "調度數據庫初始化SQL腳本" 並執行即可。數據庫初始化SQL腳本"位置為:

/xxl-rpc/doc/db/tables_xxl_rpc.sql

服務(注冊)中心支持集群部署,集群情況下各節點務必連接同一個mysql實例;如果mysql做主從,集群節點務必強制走主庫;

2.2.2 配置部署“服務(注冊)中心”

服務(注冊)中心項目:xxl-rpc-admin
作用:一個輕量級分布式服務(注冊)中心,擁有"輕量級、秒級注冊上線、多環境、跨語言、跨機房"等特性。現已開放源代碼,開箱即用。
步驟一:配置項目:

配置文件地址:

/xxl-rpc/xxl-rpc-admin/src/main/resources/application.properties

配置內容說明:

### 數據庫配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_rpc?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai  ### 服務注冊數據磁盤同步目錄 xxl.rpc.registry.data.filepath=/data/applogs/xxl-rpc/registrydata ### xxl-rpc, access token xxl.rpc.registry.accessToken=  ### 登陸信息配置 xxl.rpc.registry.login.username=admin xxl.rpc.registry.login.password=123456
步驟二:部署項目:

如果已經正確進行上述配置,可將項目編譯打包部署。 訪問地址:http://localhost:8080/xxl-rpc-admin (該地址接入方項目將會使用到,作為注冊地址),登錄后運行界面如下圖所示

輸入圖片說明

至此“服務注冊中心”項目已經部署成功。

步驟三:服務注冊中心集群(可選):

服務注冊中心支持集群部署,提升消息系統容災和可用性。

集群部署時,幾點要求和建議:

  • DB配置保持一致;
  • 登陸賬號配置保持一致;
  • 建議:推薦通過nginx為集群做負載均衡,分配域名。訪問、客戶端使用等操作均通過該域名進行。
其他:Docker 鏡像方式搭建消息中心:
  • 下載鏡像
// Docker地址:https://hub.docker.com/r/xuxueli/xxl-rpc-admin/
docker pull xuxueli/xxl-rpc-admin
  • 創建容器並運行
docker run -p 8080:8080 -v /tmp:/data/applogs --name xxl-rpc-admin  -d xuxueli/xxl-rpc-admin
 /** * 如需自定義 mysql 等配置,可通過 "PARAMS" 指定,參數格式 RAMS="--key=value --key2=value2" ; * 配置項參考文件:/xxl-rpc/xxl-rpc-admin/src/main/resources/application.properties */ docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_rpc?Unicode=true&characterEncoding=UTF-8" -p 8080:8080 -v /tmp:/data/applogs --name xxl-rpc-admin -d xuxueli/xxl-rpc-admin

2.3 項目中使用XXL-RPC

以示例項目 “xxl-rpc-sample-springboot” 為例講解;

2.3.1 開發“服務API”

開發RPC服務的 “接口 / interface” 和 “數據模型 / DTO”;

可參考如下代碼:
com.xxl.rpc.sample.api.DemoService com.xxl.rpc.sample.api.dto.UserDTO

2.3.2 配置開發“服務提供方”

  • 1、配置 “maven依賴”:

需引入:XXL-RPC核心依賴 + 公共API接口依賴

<dependency>
 <groupId>com.xuxueli</groupId>  <artifactId>xxl-rpc-core</artifactId>  <version>${parent.version}</version> </dependency>
  • 2、配置“服務提供方 ProviderFactory”
// 參考代碼位置:com.xxl.rpc.sample.server.conf.XxlRpcProviderConfig
 @Bean public XxlRpcSpringProviderFactory xxlRpcSpringProviderFactory() {   XxlRpcSpringProviderFactory providerFactory = new XxlRpcSpringProviderFactory();  providerFactory.setServer(NettyServer.class);  providerFactory.setSerializer(HessianSerializer.class);  providerFactory.setCorePoolSize(-1);  providerFactory.setMaxPoolSize(-1);  providerFactory.setIp(null);  providerFactory.setPort(port);  providerFactory.setAccessToken(null);  providerFactory.setServiceRegistry(XxlRegistryServiceRegistry.class);  providerFactory.setServiceRegistryParam(new HashMap<String, String>() {{  put(XxlRegistryServiceRegistry.XXL_REGISTRY_ADDRESS, address);  put(XxlRegistryServiceRegistry.ENV, env);  }});   logger.info(">>>>>>>>>>> xxl-rpc provider config init finish.");  return providerFactory; }
ProviderFactory 參數 說明
setServer 服務通訊方案,可選范圍:NettyServer(默認)、NettyHttpServer ;
setSerializer 序列化方案,可選范圍: HessianSerializer(默認)、Hessian1Serializer ;
setCorePoolSize 業務線程池core大小
setMaxPoolSize 業務線程是max大小
ip 服務方IP,為空自動獲取機器IP,支持手動指定
port 服務方端口,默認 7080
accessToken 服務鑒權Token,非空時生效;
setServiceRegistry 服務注冊中心,可選范圍:XxlRegistryServiceRegistry.class、LocalServiceRegistry.class;支持靈活自由擴展;
setServiceRegistryParam 服務注冊中心啟動參數,參數說明可參考各注冊中心實現的 start() 的方法注釋;
  • 3、開發“服務實現類”

實現 “服務API” 的接口,開發業務邏輯代碼;

可參考如下代碼:
com.xxl.rpc.sample.api.DemoService  注意: 1、添加 “@Service” 注解:被Spring容器掃描識別為SpringBean; 2、添加 “@XxlRpcService” 注解:被 “XXL-RPC” 的 ProviderFactory 掃描識別,進行Provider服務注冊,如果開啟注冊中心同時也會進行注冊中心服務注冊; 
XxlRpcService 注解參數 說明
version 服務版本,默認空;可據此區分同一個“服務API” 的不同版本;

2.3.3 配置開發“服務消費方”

  • 1、配置 “maven依賴”:

需引入:XXL-RPC核心依賴 + 公共API接口依賴

<dependency>
 <groupId>com.xuxueli</groupId>  <artifactId>xxl-rpc-core</artifactId>  <version>${parent.version}</version> </dependency>
  • 2、配置“服務消費方 InvokerFactory”
// 參考代碼位置:com.xxl.rpc.sample.client.conf.XxlRpcInvokerConfig
 @Bean public XxlRpcSpringInvokerFactory xxlJobExecutor() {   XxlRpcSpringInvokerFactory invokerFactory = new XxlRpcSpringInvokerFactory();  invokerFactory.setServiceRegistryClass(XxlRegistryServiceRegistry.class);  invokerFactory.setServiceRegistryParam(new HashMap<String, String>(){{  put(XxlRegistryServiceRegistry.XXL_REGISTRY_ADDRESS, address);  put(XxlRegistryServiceRegistry.ENV, env);  }});   logger.info(">>>>>>>>>>> xxl-rpc invoker config init finish.");  return invokerFactory; }
InvokerFactory 參數 說明
serviceRegistryClass 服務注冊中心,可選范圍:XxlRegistryServiceRegistry.class、LocalServiceRegistry.class;支持靈活自由擴展;
serviceRegistryParam 服務注冊中心啟動參數,參數說明可參考各注冊中心實現的 start() 的方法注釋;
  • 3、注入並實用遠程服務
// 參考代碼位置:com.xxl.rpc.sample.client.controller.IndexController
 @XxlRpcReference private DemoService demoService;  …… UserDTO user = demoService.sayHi(name); …… 
“@XxlRpcReference” 注解參數 說明
client 服務通訊方案,可選范圍:NettyClient(默認)、NettyHttpClient ;
serializer 序列化方案,可選范圍: HESSIAN(默認)、HESSIAN1;
callType 請求類型,可選范圍:SYNC(默認)、ONEWAY、FUTURE、CALLBACK;
loadBalance 負載均衡類型,可選范圍:ROUND(默認)、RANDOM、LRU、LFU、CONSISTENT_HASH;
version 服務版本,默認空;可據此區分同一個“服務API” 的不同版本;
timeout 服務超時時間,單位毫秒;
address 服務遠程地址,ip:port 格式;選填;非空時將會優先實用該服務地址,為空時會從注冊中心服務地址發現;
accessToken 服務鑒權Token,非空時生效;

2.3.4 測試

// 參考代碼位置:com.xxl.rpc.sample.client.controller.IndexController

代碼中將上面配置的消費方 invoker 遠程服務注入到測試 Controller 中使用,調用該服務,查看看是否正常。 如果正常,說明該接口項目通過XXL-RPC從 client 項目調用了 server 項目中的服務,誇JVM進行了一次RPC通訊。

訪問該Controller地址即可進行測試:http://127.0.0.1:8081/?name=jack

三、快速入門( frameless 無框架版本 )

得益於優良的兼容性與模塊化設計,不限制外部框架;除 spring/springboot 環境之外,理論上支持運行在任何Java代碼中,甚至main方法直接啟動運行;

以示例項目 “xxl-rpc-sample-frameless” 為例講解;該示例項目以直連IP方式進行演示,也可以選擇接入注冊中心方式使用。

3.1 API方式創建“服務提供者”:

// 參考代碼位置:com.xxl.rpc.sample.server.XxlRpcServerApplication
 // init XxlRpcProviderFactory providerFactory = new XxlRpcProviderFactory(); providerFactory.setServer(NettyServer.class); providerFactory.setSerializer(HessianSerializer.class); providerFactory.setCorePoolSize(-1); providerFactory.setMaxPoolSize(-1); providerFactory.setIp(null); providerFactory.setPort(7080); providerFactory.setAccessToken(null); providerFactory.setServiceRegistry(null); providerFactory.setServiceRegistryParam(null);  // add services providerFactory.addService(DemoService.class.getName(), null, new DemoServiceImpl());  // start providerFactory.start();  while (!Thread.currentThread().isInterrupted()) {  TimeUnit.HOURS.sleep(1); }  // stop providerFactory.stop();

3.2 API方式創建“服務消費者”:

// 參考代碼位置:com.xxl.rpc.sample.client.XxlRpcClientAplication
 // init client XxlRpcReferenceBean referenceBean = new XxlRpcReferenceBean(); referenceBean.setClient(NettyClient.class); referenceBean.setSerializer(HessianSerializer.class); referenceBean.setCallType(CallType.SYNC); referenceBean.setLoadBalance(LoadBalance.ROUND); referenceBean.setIface(DemoService.class); referenceBean.setVersion(null); referenceBean.setTimeout(500); referenceBean.setAddress("127.0.0.1:7080"); referenceBean.setAccessToken(null); referenceBean.setInvokeCallback(null); referenceBean.setInvokerFactory(null);  DemoService demoService = (DemoService) referenceBean.getObject();  // test UserDTO userDTO = demoService.sayHi("[SYNC]jack"); System.out.println(userDTO);

四、系統設計

4.1 系統架構圖

輸入圖片說明

4.2 核心思想

提供穩定高性能的RPC遠程服務調用功能,簡化分布式服務通訊開發。

4.3 角色構成

  • 1、provider:服務提供方;
  • 2、invoker:服務消費方;
  • 3、serializer: 序列化模塊;
  • 4、remoting:服務通訊模塊;
  • 5、registry:服務注冊中心;
  • 6、admin:服務治理、監控中心:管理服務節點信息,統計服務調用次數、QPS和健康情況;(非必選,暫未整理發布...)

4.4 RPC工作原理剖析

輸入圖片說明

概念:

  • 1、serialization:序列化,通訊數據需要經過序列化,從而支持在網絡中傳輸;
  • 2、deserialization:反序列化,服務接受到序列化的請求數據,需要序列化為底層原始數據;
  • 3、stub:體現在XXL-RPC為服務的api接口;
  • 4、skeleton:體現在XXL-RPC為服務的實現api接口的具體服務;
  • 5、proxy:根據遠程服務的stub生成的代理服務,對開發人員透明;
  • 6、provider:遠程服務的提供方;
  • 7、consumer:遠程服務的消費方;

RPC通訊,可大致划分為四個步驟,可參考上圖進行理解:(XXL-RPC提供了多種調用方案,此處以 “SYNC” 方案為例講解;)

  • 1、consumer發起請求:consumer會根據遠程服務的stub實例化遠程服務的代理服務,在發起請求時,代理服務會封裝本次請求相關底層數據,如服務iface、methos、params等等,然后將數據經過serialization之后發送給provider;
  • 2、provider接收請求:provider接收到請求數據,首先會deserialization獲取原始請求數據,然后根據stub匹配目標服務並調用;
  • 3、provider響應請求:provider在調用目標服務后,封裝服務返回數據並進行serialization,然后把數據傳輸給consumer;
  • 4、consumer接收響應:consumer接受到相應數據后,首先會deserialization獲取原始數據,然后根據stub生成調用返回結果,返回給請求調用處。結束。

4.5 TCP通訊模型

輸入圖片說明

consumer和provider采用NIO方式通訊,其中TCP通訊方案可選NETTY具體選型,高吞吐高並發;但是僅僅依靠單個TCP連接進行數據傳輸存在瓶頸和風險,因此XXL-RPC在consumer端自身實現了內部連接池,consumer和provider之間為了一個連接池,當盡情底層通訊是會取出一條TCP連接進行通訊(可參考上圖)。

4.6 sync-over-async

輸入圖片說明

XXL-RPC采用NIO進行底層通訊,但是NIO是異步通訊模型,調用線程並不會阻塞獲取調用結果,因此,XXL-RPC實現了在異步通訊模型上的同步調用,即“sync-over-async”,實現原理如下,可參考上圖進行理解:

  • 1、每次請求會生成一個唯一的RequestId和一個RpcResponse,托管到請求池中。
  • 2、調度線程,執行RpcResponse的get方法阻塞獲取本次請求結果;
  • 3、然后,底層通過NIO方式發起調用,provider異步響應請求結果,然后根據RequestId尋找到本次調用的RpcResponse,設置響應結果后喚醒調度線程。
  • 4、調度線程被喚醒,返回異步響應的請求數據。

4.7 注冊中心

XXL-RPC的注冊中心,是可選組件,支持服務注冊並動態發現;

可選擇不啟用,直接指定服務提供方機器地址通訊;

選擇啟用時,內置可選方案:“XXL-RPC-ADMIN 輕量級服務注冊中心”(推薦)、“ZK注冊中心”、“Local注冊中心”等;

** a、XXL-RPC-ADMIN 輕量級服務注冊中心(推薦) **

推薦使用內置的 "XXL-RPC-ADMIN" 作為注冊中心。非常輕量級,一分鍾可完成部署工作。

更易於集群部署、橫向擴展,搭建與學習成本更低,推薦采用該方式;

** b、ZK注冊中心 ** 內置“ZK注冊中心”,可選組件,結構圖如下:

輸入圖片說明

原理:
XXL-RPC中每個服務在zookeeper中對應一個節點,如圖"iface name"節點,該服務的每一個provider機器對應"iface name"節點下的一個子節點,如圖中"192.168.0.1:9999"、"192.168.0.2:9999"和"192.168.0.3:9999",子節點類型為zookeeper的EPHMERAL類型,該類型節點有個特點,當機器和zookeeper集群斷掉連接后節點將會被移除。consumer底層可以從zookeeper獲取到可提供服務的provider集群地址列表,從而可以向其中一個機器發起RPC調用。

4.8 在線服務目錄

服務提供方新增 "/services" 服務目錄功能,可查看在線服務列表;暫時僅針對NETTY_HTTP通訊方案,瀏覽器訪問地址 "{端口地址}/services" 即可。

4.9 如何切換“通訊方案”選型

XXL-RPC提供多中通訊方案:支持 TCP 和 HTTP 兩種通訊方式進行服務調用;其中 TCP 提供可選方案 NETTY ,HTTP 提供可選方案 NETTY_HTTP (新版本移除了Mina和Jetty通訊方案,主推Netty;如果有需要可以參考舊版本;);

如果需要切換XXL-RPC“通訊方案”,只需要執行以下兩個步驟即可:

  • a、引入通訊依賴包,排除掉其他方案依賴,各方案依賴如下:
    • NETTY:依賴 netty-all ;
    • NETTY_HTTP:依賴 netty-all ;
  • b、修改通訊枚舉,需要同時在“服務方”與“消費方”兩端一同修改,通訊枚舉屬性代碼位置如下:
    • 服務工廠 "XxlRpcSpringProviderFactory.netType" :可參考springboot示例組件初始化代碼;
    • 服務引用注解 "XxlRpcReference.netType" | 服務Bean對象 "XxlRpcReferenceBean.netType" :可參考springboot示例組件初始化代碼;

4.10 如何切換“注冊中心”選型

XXL-RPC的注冊中心,是一個可選組件,不強制依賴;支持服務注冊並動態發現;
可選擇不啟用,直接指定服務提供方機器地址通訊;
選擇啟用時,原生提供多種開箱即用的注冊中心可選方案,包括:“XXL-RPC原生輕量級注冊中心”、“ZK注冊中心”、“Local注冊中心”等;

如果需要切換XXL-RPC“注冊中心”,只需要執行以下兩個步驟即可:

  • a、引入注冊注冊中心依賴包,排除掉其他方案依賴,各方案依賴如下:
    • XXL-RPC原生輕量級注冊中心:輕量級、無第三方依賴;
    • ZK注冊中心:依賴 zookeeper
    • Local注冊中心:輕量級、零依賴;
  • b、修改注冊中心配置,需要同時在“服務方”與“消費方”兩端一同修改,代碼位置如下:
    • XxlRpcSpringProviderFactory.serviceRegistryClass:注冊中心實現類,可選:XxlRegistryServiceRegistry.class、LocalServiceRegistry.class、ZkServiceRegistry.class
    • XxlRpcSpringProviderFactory.serviceRegistryParam:注冊中心啟動參數,各種注冊中心啟動參數不同,可參考其 start 方案了解;

4.11 泛化調用

XXL-RPC 提供 "泛化調用" 支持,服務調用方不依賴服務方提供的API;泛化調用通常用於框架集成,比如 "網關平台、跨語言調用、測試平台" 等; 開啟 "泛化調用" 時服務方不需要做任何調整,僅需要調用方初始化一個泛化調用服務Reference ("XxlRpcGenericService") 即可。

“XxlRpcGenericService#invoke” 請求參數 說明
String iface 服務接口類名
String version 服務版本
String method 服務方法
String[] parameterTypes 服務方法形參-類型,如 "int、java.lang.Integer、java.util.List、java.util.Map ..."
Object[] args 服務方法形參-數據
// 服務Reference初始化-注解方式示例
@XxlRpcReference private XxlRpcGenericService genericService;  // 服務Reference初始化-API方式示例 XxlRpcGenericService genericService = (XxlRpcGenericService) new XxlRpcReferenceBean(……).getObject();  // 調用方示例 Object result = genericService.invoke(  "com.xxl.rpc.sample.server.service.Demo2Service",  null,  "sum",  new String[]{"int", "int"},  new Object[]{1, 2}  );   // 服務方示例 public class Demo2ServiceImpl implements Demo2Service {   @Override  public int sum(int a, int b) {  return a + b;  }  }

五、分布式服務(注冊)中心詳解

5.1 概述

XXL-RPC-ADMIN(原XXL-REGISTRY) 是一個輕量級分布式服務注冊中心,擁有"輕量級、秒級注冊上線、多環境、跨語言、跨機房"等特性。現已開放源代碼,開箱即用。

5.2 特性

  • 1、輕量級:基於DB與磁盤文件,只需要提供一個DB實例即可,無第三方依賴;
  • 2、實時性:借助內部廣播機制,新服務上線、下線,可以在1s內推送給客戶端;
  • 3、數據同步:注冊中心會定期全量同步數據至磁盤文件,清理無效服務,確保服務數據實時可用;
  • 4、性能:服務發現時僅讀磁盤文件,性能非常高;服務注冊、摘除時通過磁盤文件校驗,防止重復注冊操作;
  • 5、擴展性:可方便、快速的橫向擴展,只需保證服務注冊中心配置一致即可,可借助負載均衡組件如Nginx快速集群部署;
  • 6、多狀態:服務內置三種狀態:
    • 正常狀態=支持動態注冊、發現,服務注冊信息實時更新;
    • 鎖定狀態=人工維護注冊信息,服務注冊信息固定不變;
    • 禁用狀態=禁止使用,服務注冊信息固定為空;
  • 7、跨語言:注冊中心提供HTTP接口(RESTFUL 格式)供客戶端實用,語言無關,通用性更強;
  • 8、兼容性:項目立項之初是為XXL-RPC量身設計,但是不限於XXL-RPC使用。兼容支持任何服務框架服務注冊實用,如dubbo、springboot等;
  • 9、跨機房:得益於服務注冊中心集群關系對等特性,集群各節點提供冪等的配置服務;因此,異地跨機房部署時,只需要請求本機房服務注冊中心即可,實現異地多活;
  • 10、容器化:提供官方docker鏡像,並實時更新推送dockerhub,進一步實現 "服務注冊中心" 產品開箱即用;
  • 11、訪問令牌(accessToken):為提升系統安全性,注冊中心和客戶端進行安全性校驗,雙方AccessToken匹配才允許通訊;

5.3 “服務(注冊)中心 xxl-rpc-admin” 快速入門

可參考章節:2.2 搭建部署 "服務(注冊)中心"

5.4 接入 "服務注冊中心" 示例

a、Java語言項目接入示例

XXL-RPC默認將 "XXL-RPC-ADMIN" 作為原生注冊中心。其他Java服務框架。

其他Java服務框架,如dubbo、springboot等,建議參考 XXL-RPC 提供的實例項目;

b、非Java語言項目接入;

非Java語言項目,可以借助提供的 RESTFUL 格式API接口實現服務注冊與發現功能。

5.5 注冊中心 RESTful API

服務注冊中心為支持服務注冊與發現功能,提供的 RESTful 格式API接口如下:

5.5.1、服務注冊 & 續約 API

說明:新服務注冊上線1s內廣播通知接入方;需要接入方循環續約,否則服務將會過期(三倍於注冊中心心跳時間)下線;

地址格式:{服務注冊中心跟地址}/registry
 請求參數說明:  1、accessToken:請求令牌;  2、biz:業務標識  2、env:環境標識  3、registryDataList:服務注冊信息  請求數據格式如下,放置在 RequestBody 中,JSON格式:   {  "accessToken" : "xx",  "biz" : "xx",  "env" : "xx",  "registryDataList" : [{  "key" : "service01",  "value" : "address01"  }]  }  

5.5.2、服務摘除 API

說明:新服務摘除下線1s內廣播通知接入方;

地址格式:{服務注冊中心跟地址}/remove
 請求參數說明:  1、accessToken:請求令牌;  2、biz:業務標識  2、env:環境標識  3、registryDataList:服務注冊信息  請求數據格式如下,放置在 RequestBody 中,JSON格式:   {  "accessToken" : "xx",  "biz" : "xx",  "env" : "xx",  "registryDataList" : [{  "key" : "service01",  "value" : "address01"  }]  } 

5.5.3、服務發現 API

說明:查詢在線服務地址列表;

地址格式:{服務注冊中心跟地址}/discovery
 請求參數說明:  1、accessToken:請求令牌;  2、biz:業務標識  2、env:環境標識  3、keys:服務注冊Key列表  請求數據格式如下,放置在 RequestBody 中,JSON格式:   {  "accessToken" : "xx",  "biz" : "xx",  "env" : "xx",  "keys" : [  "service01",  "service02"  ]  } 

5.5.4、服務監控 API

說明:long-polling 接口,主動阻塞一段時間(三倍於注冊中心心跳時間);直至阻塞超時或服務注冊信息變動時響應;

地址格式:{服務注冊中心跟地址}/monitor
 請求參數說明:  1、accessToken:請求令牌;  2、biz:業務標識  2、env:環境標識  3、keys:服務注冊Key列表  請求數據格式如下,放置在 RequestBody 中,JSON格式:   {  "accessToken" : "xx",  "biz" : "xx",  "env" : "xx",  "keys" : [  "service01",  "service02"  ]  }  

5.6 服務中心系統設計

5.6.1 系統架構圖

輸入圖片說明

5.6.2 原理解析

內部通過廣播機制,集群節點實時同步服務注冊信息,確保一致。客戶端借助 long pollong 實時感知服務注冊信息,簡潔、高效;

5.6.3 跨機房(異地多活)

得益於服務注冊中心集群關系對等特性,集群各節點提供冪等的服務注冊服務;因此,異地跨機房部署時,只需要請求本機房服務注冊中心即可,實現異地多活;

舉個例子:比如機房A、B 內分別部署服務注冊中心集群節點。即機房A部署 a1、a2 兩個服務注冊中心服務節點,機房B部署 b1、b2 兩個服務注冊中心服務節點;

那么各機房內應用只需要請求本機房內部署的服務注冊中心節點即可,不需要跨機房調用。即機房A內業務應用請求 a1、a2 獲取配置、機房B內業務應用 b1、b2 獲取配置。

這種跨機房部署方式實現了配置服務的 "異地多活",擁有以下幾點好處:

  • 1、注冊服務響應更快:注冊請求本機房內搞定;
  • 2、注冊服務更穩定:注冊請求不需要跨機房,不需要考慮復雜的網絡情況,更加穩定;
  • 2、容災性:即使一個機房內服務注冊中心全部宕機,僅會影響到本機房內應用加載服務,其他機房不會受到影響。

5.6.4 一致性

類似 Raft 方案,更輕量級、穩定;

  • Raft:Leader統一處理變更操作請求,一致性協議的作用具化為保證節點間操作日志副本(log replication)一致,以term作為邏輯時鍾(logical clock)保證時序,節點運行相同狀態機(state machine)得到一致結果。
  • 本項目:
    • Leader(統一處理分發變更請求):DB消息表(僅變更時產生消息,消息量較小,而且消息輪訓存在間隔,因此消息表壓力不會太大;);
    • state machine(順序操作日志副本並保證結果一直):順序消費消息,保證本地數據一致,並通過周期全量同步進一步保證一致性;

5.7 版本

5.7.1 版本 v1.0.0 Release Notes[2018-12-01]

  • 1、輕量級:基於DB與磁盤文件,只需要提供一個DB實例即可,無第三方依賴;
  • 2、實時性:借助內部廣播機制,新服務上線、下線,可以在1s內推送給客戶端;
  • 3、數據同步:注冊中心內部10s會全量同步一次磁盤數據,清理無效服務,確保服務數據實時可用;
  • 4、性能:服務發現時僅讀磁盤文件,性能非常高;服務注冊、摘除時通過磁盤文件校驗,防止重復注冊操作;
  • 5、擴展性:可方便、快速的橫向擴展,只需保證服務注冊中心配置一致即可,可借助負載均衡組件如Nginx快速集群部署;
  • 6、多狀態:服務內置三種狀態:
    • 正常狀態=支持動態注冊、發現,服務注冊信息實時更新;
    • 鎖定狀態=人工維護注冊信息,服務注冊信息固定不變;
    • 禁用狀態=禁止使用,服務注冊信息固定為空;
  • 7、跨語言:注冊中心提供HTTP接口(RESTFUL 格式)供客戶端實用,語言無關,通用性更強;
  • 8、兼容性:項目立項之初是為XXL-RPC量身設計,但是不限於XXL-RPC使用。兼容支持任何服務框架服務注冊實用,如dubbo、springboot等;
  • 9、跨機房:得益於服務注冊中心集群關系對等特性,集群各節點提供冪等的配置服務;因此,異地跨機房部署時,只需要請求本機房服務注冊中心即可,實現異地多活;
  • 10、容器化:提供官方docker鏡像,並實時更新推送dockerhub,進一步實現 "服務注冊中心" 產品開箱即用;
  • 11、long polling 超時時間優化;服務端默認 30s 超時限制;客戶端默認 60s 阻塞登台;二者以較小者為准,建議客戶端大於服務端。

5.7.2 版本 v1.0.1 Release Notes[2018-12-20]

  • 1、訪問令牌(accessToken):為提升系統安全性,注冊中心和客戶端進行安全性校驗,雙方AccessToken匹配才允許通訊;
  • 2、底層通訊參數統一:請求參數統一由 postbody 發送接收,數據格式見公共消息體 "XxlRegistryParamVO",內部包含 accessToken、biz、env 等屬性;
  • 3、環境屬性 "env" 長度限制調整為 "2~255" ,兼容 "qa"、"dev" 等短環境標識;
  • 4、升級 pom 依賴至較新版本;

5.7.3 版本 v1.0.2 Release Notes[2018-02-21]

  • 1、服務端空值也支持響應,客戶端注冊信息發現null值緩存,避免緩存穿透;
  • 2、客戶端配置監控邏輯優化,避免異常情況下重試請求太頻繁;
  • 3、客戶端日志優化:僅變更日志保留為info級別,非核心日志調整為debug級別;
  • 4、內部JSON組件優化,支持多級父類屬性序列化;
  • 5、移除冗余屬性,如version等;
  • 6、服務注冊中心全量同步線程優化,對齊起始時間,避免集群節點數據不一致;

5.7.4 版本 v1.1.0 Release Notes[2019-11-16]

  • 1、注冊日志文件加載方式優化,修復文件名亂碼問題;
  • 2、修復服務注冊version不匹配問題;
  • 3、升級依賴版本,如slf4j-api/spring-boot/mybatis/mysql等;
  • 4、小概率情況下底層通訊亂碼問題修復;

六、版本更新日志

6.1 版本 v1.1 新特性

  • 1、快速接入:接入步驟非常簡潔,兩分鍾即可上手;
  • 2、服務透明:系統完整的封裝了底層通信細節,開發時調用遠程服務就像調用本地服務,在提供遠程調用能力時不損失本地調用的語義簡潔性;
  • 3、注冊中心(可選):支持使用zookeeper作為服務注冊中心,服務注冊並動態發現。同時,也可以不使用注冊中心,直接指定服務提供方機器地址進行RPC通訊;
  • 4、軟負載均衡及容錯:服務提供方集群注冊時,在使用軟負載算法進行流量分發;
  • 5、容錯:服務提供方集群注冊時,某個服務節點不可用時將會自動摘除,同時消費方將會移除失效節點將流量分發到其余節點,提高系統容錯能力。
  • 6、TCP/HTTP通訊:支持TCP和HTTP兩種通訊方式進行服務調用,其中TCP通訊可以執行NETTY或MINA作為可選通訊方案,以提供高效的服務通訊支持;
  • 7、序列化:支持hessian、protobuf和jackson等多種序列化方案;
  • 8、服務治理:提供服務治理中心,可在線管理注冊的服務信息,如管理服務節點、節點權重等;(部分實現)
  • 9、服務監控:可在線監控服務調用統計信息以及服務健康狀況等(計划中);
  • 10、解決1+1問題:傳統分布式通訊一般通過nginx或f5做集群服務的流量負載均衡,如hessian,每次請求在到達目標服務機器之前都需要經過負載均衡機器,即1+1,這將會把流量放大一倍。而XXL-RPC將會從消費方至服務提供方建立TCP長連接,每次請求直達目標機器,從而可以避免上述問題;

6.2 版本 v1.2.0 [2018-10-26]

  • 1、核心模塊重度重構:模塊化划分、包名重構;
  • 2、輕量級/模塊化改造:移除對具體組件的依賴,如ZK、Netty、Mina等,改為可選擴展方式;
  • 3、支持多種請求方式,如:SYNC、ONEWAY、FUTURE、CALLBACK 等;
  • 4、各模塊擴展改為非強制依賴:擴展依賴需要單獨進行 maven 引入(provided類型);提供強制依賴最小精簡選型組合 "jetty + hessian + 無注冊中心";
  • 5、服務AccessToken鑒權;
  • 6、支持HTTP異步請求,線程優化,統一通訊流程;
  • 7、可選ZK注冊中心重構,不依賴配置文件,通過代碼初始化;
  • 8、可選ZK注冊中心初始化邏輯優化,避免並發初始化,阻塞至TCP連接創建成功才允許后續操作;
  • 9、推送core到maven中央倉庫;
  • 10、服務注冊邏輯優化,舊方案以 "iface" 接口包名進行服務注冊, 改為結合 "iface + version" 作為 serviceKey 進行注冊,便於接口多服務復用;

6.3 版本 v1.2.1 Release Notes[2018-11-09]

  • 1、內置注冊中心選擇ZK時邏輯優化,ZK初始化時unlock邏輯調整,優化斷線重連特性;
  • 2、除了springboot類型示例;新增無框架示例項目 "xxl-rpc-sample-frameless"。不依賴第三方框架,只需main方法即可啟動運行;
  • 3、選型http通訊方式時,校驗為IP端口格式地址則主動添加地址前綴;
  • 4、RPC異步請求邏輯優化,請求異常時主動通知Client端,避免無效等待時間;
  • 5、http通訊方式選型jetty時,線程池升級為QueuedThreadPool,修復jetty9.4版本server自動銷毀問題;
  • 6、Server新增 "/services" 目錄功能,可查看在線服務列表;

6.4 版本 v1.2.2 Release Notes[2018-11-26]

  • 1、默認通訊方案切換為 Netty,可選方案依賴均調整為 provided 類型;提供強制依賴最小精簡選型組合 "netty + hessian + 無注冊中心(推薦采用:XXL-RPC原生注冊中心)";
  • 2、XXL-RPC原生注冊中心:底層抽象注冊中心模塊,並原生提供自研基於DB的注冊中心,真正實現開箱即用,更輕量級、降低第三方依賴;至今XXL-RPC以提供三種注冊中心具體實現:"XXL-RPC原生注冊中心方案","ZK方案","Local方案";其中"XXL-RPC原生注冊中心方案"特性如下:
    • 輕量級:基於DB與磁盤文件,只需要提供一個DB實例即可,無第三方依賴;
    • 實時性:借助內部廣播機制,新服務上線、下線,可以在1s內推送給客戶端;
    • 數據同步:注冊中心內部10s會全量同步一次磁盤數據,清理無效服務,確保服務數據實時可用;
    • 性能:服務發現時僅讀磁盤文件,性能非常高;服務注冊、摘除時通過磁盤文件校驗,防止重復注冊操作;
    • 擴展性:可方便、快速的橫向擴展,只需保證 "注冊中心" 配置一致即可,可借助負載均衡組件如Nginx快速集群部署;
    • 多狀態:服務內置三種狀態:正常狀態=支持動態注冊、發現,服務注冊信息實時更新;鎖定狀態=人工維護注冊信息,服務注冊信息固定不變;禁用狀態=禁止使用,服務注冊信息固定為空;
    • 跨語言:注冊中心提供HTTP接口供客戶端實用,語言無關,通用性更強;
    • 兼容性:“XXL-RPC原生輕量級注冊中心”雖然為XXL-RPC設計,但是不限於XXL-RPC使用。兼容支持任何服務框架服務注冊實用,如dubbo、springboot等;
    • 容器化:提供官方docker鏡像,並實時更新推送dockerhub,進一步實現"XXL-RPC原生注冊中心方案"產品開箱即用;
  • 3、XXL-RPC客戶端適配"XXL-RPC原生注冊中心",可快速接入,只需要切換注冊中心實現為 "NativeServiceRegistry" 即可,文檔由專門章節介紹;
  • 4、注冊中心啟動參數位置調整,與注冊中心實現關聯;
  • 5、服務提供者參數優化,IP為空時原生動態獲取,核心參數啟動時增強校驗;
  • 6、注冊模塊API優化,改為批量模式進一步提升性能;
  • 7、文檔增強,注冊中心配置切換、通訊方案配置切換說明;
  • 8、IP工具類優化,兼容 Inet6Address 格式地址;
  • 9、Netty銷毀邏輯優化;
  • 10、擴展第三方注冊中心ZK底層邏輯優化,避免舊注冊信息無法清理的問題;

6.5 版本 v1.3.0 Release Notes[2018-12-02]

  • 1、原生注冊中心拆分為獨立項目 "xxl-registry"(https://github.com/xuxueli/xxl-registry ),提供服務注冊restful服務,並提送響應client端依賴用於簡化接入難度;
  • 2、NativeServiceRegistry 更名為 XxlRegistryServiceRegistry;
  • 3、POM依賴升級,冗余POM清理;
  • 4、代碼優化:XxlRpcInvokerFactory 移除 static 代碼塊及相關組件,進一步實現組件無狀態;
  • 5、服務注冊邏輯優化,避免地址重復生成;

6.6 版本 v1.3.1 Release Notes[2018-12-21]

  • 1、負載均衡/軟負載:提供豐富的負載均衡策略,包括:輪詢、隨機、LRU、LFU、一致性HASH等;
  • 2、服務發現注冊邏輯優化:支持批量注冊、摘除,升級 xxl-registry 至 v1.0.1;
  • 3、新增jfinal類型示例項目 "xxl-rpc-sample-jfinal" 支持jfinal項目快速接入分布式RPC服務功能;高兼容性,原則上支持任務框架,甚至main方法直接運行;
  • 4、TCP通訊方案Server端Channel線程優化(線程參數=60/300/1000),避免IO線程阻塞於業務;
  • 5、TCP通訊方案Client端Channel線程優化(線程參數=10/100/1000),避免IO線程阻塞於callback業務;
  • 6、TCP通訊方案Client初始化邏輯優化;
  • 7、TCP長連銷毀邏輯優化;
  • 8、底層Log整理,RPC報錯時打印完整Log,包括請求地址,請求參數等;
  • 9、Server端銷毀邏輯優化;
  • 10、static代碼塊優化,進行組件無狀態優化:response factory等;遷移到invoke factory上來;
  • 11、升級多項pom依賴至較新穩定版本;

6.7 版本 v1.3.2 Release Notes[2019-02-21]

  • 1、泛化調用:服務調用方不依賴服務方提供的API;
  • 2、新增通訊方案 "NETTY_HTTP";
  • 3、新增序列化方案 "KRYO";
  • 4、通訊效率優化:TCP連接池取消,改為單一長連接,移除commons-pool2依賴;
  • 5、RPC請求路由時空地址處理優化;
  • 6、通訊連接池address參數優化,出IP:PORT格式外兼容支持常規URL格式地址;
  • 7、線程名稱優化,便於適配監控快速進行線程定位;

6.8 版本 v1.4.0 Release Notes[2019-04-20]

  • 1、LRU路由更新不及時問題修復;
  • 2、JettyClient Buffer 默認長度調整為5M;
  • 3、Netty Http客戶端配置優化;
  • 4、升級依賴版本,如netty/mina/spring等

6.9 版本 v1.4.1 Release Notes[2019-05-23]

  • 1、客戶端長連優化,修復初始化時服務不可用導致長連冗余創建的問題;
  • 2、升級依賴版本,如netty/mina/jetty/jackson/spring/spring-boot等;
  • 3、空閑鏈接自動回收:服務端與客戶端主動檢測空閑鏈接並回收,及時釋放相關資源(netty、mina);空閑超10min自動釋放;

6.10 版本 v1.4.2 Release Notes[2019-11-18]

  • 1、長連心跳保活:客戶端周期性發送心跳請求給服務端保活;服務端連續三次未收到心跳時,銷毀連接;
  • 2、服務線程優化,支持自定義線程參數;
  • 3、API重構:初始化枚舉改為接口實例,方便擴展;
  • 4、代碼優化,ConcurrentHashMap變量類型改為ConcurrentMap,避免因不同版本實現不同導致的兼容性問題;
  • 5、Netty Http客戶端優化,識別並過濾非法響應數據;
  • 6、通訊方案收斂:主推Netty和Netty_Http,移除Mina和Jetty內置擴展,如有需求自行擴展維護;
  • 7、序列化方案收斂:主推HESSIAN和HESSIAN1,移除protostuff、KRYO、JACKSON內置擴展,如有需求自行擴展維護;
  • 8、升級依賴版本,如netty/mina/hessian/jackson/zookeeper等;

6.11 版本 v1.5.0 Release Notes[2019-11-22]

  • 1、IpUtil優化:增加連通性校,過濾明確非法的網卡;

6.12 版本 v1.6.0 Release Notes[2020-04-15]

  • 1、為方便維護,合並xxl-registry至xxl-rpc,模塊名為xxl-rpc-admin;
  • 2、一致性哈希路由策略優化:默認虛擬節點數量調整為100,提高路由的均衡性;
  • 3、RPC Client端,復用單例EventLoopGroup線程池,降低資源開銷;
  • 4、RPC Server端,新增屬性 ”注冊地址/registryAddress“,優先使用該屬性作為注冊地址,為空時使用服務 ”IP:PORT“ 作為注冊地址。從而更靈活的支持容器類型執行器動態IP和動態映射端口問題。

6.13 版本 v1.6.1 Release Notes[迭代中]

  • 1、[迭代中]數據庫編碼調整為utf8mb4;
  • 2、[迭代中]業務標識和環境標識字段長度上限調整為50;

TODO

  • 提高系統可用性,以部分功能暫時不可達為代價,防止服務整體緩慢或雪崩
    • 限流=防止負載過高,導致服務雪崩;client、server,雙向限流;方法級,QPS限流;在途請求數,流控依據;
    • 降級=10s內超閾值(異常、超時);拒絕服務、默認值;
      • 超過(熔斷模式):99.9% 返回默認值,0.1%真實請求;
      • 未超過:熔斷模式下,每 10s 增加 10% 的流量,直至恢復;
    • 服務隔離:超時較多的請求,自動路由到 “慢線程池” ,避免占用公共線程池;
    • 預熱控制,剛啟動的節點,只會分配比較少的請求;逐步增大,直至平均。幫助新節點啟動;
  • 支持HTTP異步響應,至此底層remoting層通訊全異步化;
  • zk注冊中心初始化時取消對集群狀態強依賴,底層異常時循環檢測;
  • Server啟動失敗時,ZK銷毀中斷問題修復,偶發;
  • 服務提供者iface獲取方式優化,兼容代理方式獲取接口 “getProxiedInterfaces”;
  • 演進計划:
    • 通訊:remoting模塊;TCP、HTTP、HTTP2可選方案;
    • 限流:ratelimit模塊;滑動窗口方式,單機限流,請求/響應方雙向限流;[ING]
    • 網關:servlet3 + 泛化調用模塊;計划:基於DB輕量級注冊中心,服務動態發現,自動轉發;
  • admin-服務監控(《xxl-trace》):
    • tps,99線;
    • 成功率;
    • 調用鏈:
  • rpc filter:方便埋點、監控等;
  • 服務治理實現,服務調用量,成功率,1min上報一次;
  • static代碼塊移除,進行組件無狀態優化,jetty/pool/等;
  • 接入方配置方式優化,provider與invoker配置合並至新組建;
  • 新增 appname 屬性,為后續服務 trace 做准備;
  • 新增 nutz 類型示例項目;
  • Server/Client失敗盡量響應,避免等到到timeout;
  • 線程隔離:通訊線程池拆分為Fast/Slow兩個,針對響應較慢服務方法請求,降級使用Slow線程池;考慮是否可以方法級隔離線程池,避免線程阻塞;
  • rpc時鍾參數僅記錄,取消時鍾校驗邏輯;
  • 調用鏈追蹤,監控;結合 xxl-apm 與 xxl-rpc filter共同演進;
  • 限流-熔斷-降級,結合xxl-registry與xxl-rpc filter共同演進;
  • [ING]"ConnectClient#clientLock" 優化,復用連接對象;
  • 長連心跳、斷線重連、空閑連接回收;
  • 服務注冊中心:
    • 服務端:注冊IP黑名單、白名單;
    • 客戶端:瞬間下線拒絕,比如超過 90% 服務批量下線,本次下線標記失敗,連續三次才生效;防止注冊中心故障,導致整體服務不可用;
    • 注冊方式附屬信息;
      • 服務注冊,支持節點權重配置;
      • 注冊服務支持Tag屬性;如機房TAG,客戶端優先使用本機房,即機房TAG一致的服務;
    • springboot、dubbo 示例;
    • 單機版本:H2數據庫;
    • 異地多活:注冊中心無中心服務;
    • 同機房讀:服務支持Region屬性,優先使用本Region服務;

七、其他

7.1 項目貢獻

歡迎參與項目貢獻!比如提交PR修復一個bug,或者新建 Issue 討論新特性或者變更。

7.2 用戶接入登記

更多接入的公司,歡迎在 登記地址 登記,登記僅僅為了產品推廣。

7.3 開源協議和版權

產品開源免費,並且將持續提供免費的社區技術支持。個人或企業內部可自由的接入和使用。

  • Licensed under the GNU General Public License (GPL) v3.
  • Copyright (c) 2015-present, xuxueli.

捐贈

無論金額多少都足夠表達您這份心意,非常感謝 :) 前往捐贈


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM