Solon Cloud 是一系列的接口標准和配置規范。Solon Cloud 為常見的分布式系統模式提供了一種簡單且方便的編程模式,幫助開發人員構建有彈性的、可靠的、協調的應用程序。Solon Cloud 構建於 Solon 之上,可使開發者很容易入手並快速應用於生產中。Solon Cloud 主要由三部份組成:接口定義與配置規范、實現相關接口定義的各種插件,以及通用客戶端。
Solon Cloud 非常容易實現與自研框架進行對接。只要實現相關接口定義,按規范配置的一個插件,即是一個 Solon Cloud 插件。
Solon Cloud 項目源碼:https://gitee.com/noear/solon
Solon Cloud 示例源碼:https://gitee.com/noear/solon_cloud_demo
特點1:身材苗條
最小的 Solon Cloud 開發單位只有5m(含 okhttp、mysql、redis、memcaced、HikariCP 等客戶端或驅動)
特點2:速度更快
本機helloworld測試,Qps可達12萬之多。可參考:《helloworld_wrk_test》
特點3:代碼自由
所有的能力可以用注解,也可以純手寫。按需而定,自由隨心。
建議
讓自己的單體應用,能多實例部署。就是分布式的良好開始......然后改用配置服務,然后增加事件驅動,按需拆分......慢慢來。
一、Solon Cloud 套件內容
(1)接口定義及配置規范清單
接口定義及配置規范,可為不同的框架適配與使用提供了統一的模式
功能名稱 | Solon Cloud | 接口定義 | 配置規范(具體暫略) |
---|---|---|---|
服務注冊與發現 | Solon Cloud Discovery | CloudDiscoveryService | solon.cloud.@@.discovery |
服務間調用方式 | RPC or REST API or Event | - | - |
服務網關 | Solon Gateway | - | - |
斷路器 | Solon Cloud Breaker | CloudBreakerService | solon.cloud.@@.breaker |
分布式配置 | Solon Cloud Config | CloudConfigService | solon.cloud.@@.config |
服務跟蹤 | Solon Cloud Trace | CloudTraceService | solon.cloud.@@.trace |
服務監控 | Solon Cloud Metric | CloudMetricService | solon.cloud.@@.metric |
事件總線 | Solon Cloud Event | CloudEventService | solon.cloud.@@.event |
分布式任務 | Solon Cloud Job | CloudJobService | solon.cloud.@@.job |
分布式ID | Solon Cloud Id | CloudIdService | solon.cloud.@@.id |
分布式文件 | Solon Cloud File | CloudFileService | solon.cloud.@@.file |
分布式名單 | Solon Cloud List | CloudListService | solon.cloud.@@.list |
分布式鎖 | Solon Cloud Lock | CloudLockService | solon.cloud.@@.lock |
分布式日志 | Solon Cloud Logging | CloudLogService | solon.cloud.@@.log |
(2)現有適配插件清單
插件 | 說明 |
---|---|
org.noear:consul-solon-plugin | consul 適配插件(支持Solon cloud 配置服務、注冊與發現服務) |
org.noear:nacos-solon-plugin | nacos 適配插件(支持Solon cloud 配置服務、注冊與發現服務) |
org.noear:zookeeper-solon-plugin | zookeeper 適配插件(支持Solon cloud 配置服務、注冊與發現服務) |
org.noear:water-solon-plugin | water 適配插件(支持Solon cloud 配置服務、注冊與發現服務、事件總線服務、日志服務、跟蹤服務、監控、分布式任務、鎖服務) |
org.noear:rabbitmq-solon-plugin | rabbitmq 適配插件(支持Solon cloud 事件總線服務) |
org.noear:rocketmq-solon-plugin | rocketmq 適配插件(支持Solon cloud 事件總線服務) |
org.noear:mqtt-solon-plugin | mqtt 適配插件(支持Solon cloud 事件總線服務) |
org.noear:kafka-solon-plugin | kafka 適配插件(支持Solon cloud 事件總線服務) |
org.noear:guava-solon-plugin | guava 適配插件(支持Solon cloud 融斷服務) |
org.noear:sentinel-solon-plugin | sentinel 適配插件(支持Solon cloud 融斷服務) |
org.noear:semaphore-solon-plugin | semaphore 適配插件(支持Solon cloud 融斷服務) |
org.noear:aliyun-oss-solon-plugin | aliyun-oss 適配插件(支持Solon cloud 分布式文件服務) |
org.noear:aws-s3-solon-plugin | aws-s3 適配插件(支持Solon cloud 分布式文件服務) |
org.noear:snowflake-id-solon-plugin | snowflake 算法適配插件(支持Solon cloud 分布式ID服務) |
org.noear:xxl-job-solon-plugin | xxl-job 適配插件(支持Solon cloud 分布式任務服務) |
(3)通用客戶端
通用客戶端,提供了所有不同框架的統一使用界面,同時提供了自由手動操控的機制。
//手動獲取配置(不管背后是哪個配置框架,都是如此)
Config val1 = CloudClient.config().pull(Solon.cfg().appGroup(), "demo.ds");
//手動生成ID
long val2 = CloudClient.id().generate();
//手動發布事件(不管背后是哪個消息隊列,都是如此)
CloudClient.event().publish(new Event("demo.user.login","1"));
//等...
二、快速概覽
(1)hello world
一個普通的 rest api,輸出 hello world
public class DemoApp {
public static void main(String[] args) {
Solon.start(DemoApp.class, args, app->{
app.get("/", c -> c.output("Hello world!"));
});
}
}
(2)使用配置服務(目前適配有:water, consul, nacos, zookeeper)
通過本地配置導入需要的分布式配置
solon.cloud.water:
server: water
config:
load: "test.properties" #默認加載一個配置
或者,使用 @CloudConfig 注解生成Bean
@Configuration
public class Config {
@Bean
public DataSource ds(@CloudConfig("demo.ds") HikariDataSource ds){
return ds;
}
}
(3)使用注冊與發現服務實現RPC調用(目前適配有:water, consul, nacos, zookeeper)
服務端
//
// 1.所有 remoting = true 的組件,即為 rpc 服務;
// 2.以 uri 的形式提供資源描述,以同時支持 rest api 和 rpc 兩種模式
//
@Mapping("/rpc/")
@Remoting
public class HelloServiceImpl implements HelloService{
@Override
public String hello(String name) {
return null;
}
}
客戶端
@Controller
public class HelloController {
//注入Rpc服務代理(會自動通過發現服務獲取服務集群)
@NamiClient(name = "hellorpc", path = "/rpc/")
HelloService helloService;
public String hello(String name){
return helloService.hello(name);
}
}
(4)使用Slf4j日志接口,轉發到分布式日志記錄器(目前適配有:water)
Solon Cloud Log 強調語義標簽(或固化的元信息)。通過語議標簽,對日志進行固定索引,進而實現更快的查詢效果。
@Slf4j
public class LogController {
@Mapping("/")
public String hello(String name){
//將元信息固化為 tag0 ... tag4;利於做日志索引
TagsMDC.tag0("user_"+name); //相當於 MDC.put("tag0", "user_"+name);
log.info("有用戶來了");
return name;
}
}
注:也可以改用 logback
或 log4j
做日志服務,只需要排除掉 solon.logging.impl
框架卻可
(5)使用分布式事件進行業務水平擴展(目前適配有:water, rabbitmq, rocketmq, mqtt, kafka)
Solon Cloud Event 的兩個特性說明:
- 自守護模式,即失敗后不斷延時重發確保最終成功。此特性可支持SAGA分布式事務模型。
- 多通道模式,即不同消息隊列並存。此特性可按業務做不同安排,例如:業務消息用 RabbitMQ,IoT消息用 Mqtt。
例,發布事件
public class EventController {
public void onUserRegistered(long user_id) {
//用戶注冊完成后,發布一個事件
//
CloudClient.event().publish(
new Event("user.registered", String.format("{\"user_id\":%d}", user_id)));
}
}
訂閱與消費事件
@CloudEvent("user.registered")
public class EventListen implements CloudEventHandler {
@Override
public boolean handler(Event event) throws Throwable {
//用戶注冊完成后,送個金幣...
//
return true;
}
}
(6)使用分布式名單做IP限制(目前適配有:water)
public class ListController {
public void hello(Context ctx){
String ip = IpUtils.getIP(ctx);
if(CloudClient.list().inListOfIp("safelist", ip) == false){
return;
}
//業務處理...
}
}
(7)使用融斷器進行限流控制(目前適配有:sentinel, guava, semaphore)
添加配置(此配置可通過配置服務,動態更新)
solon.cloud.local:
breaker:
main: 100 #qps = 100
通過注解,添加埋點
//此處的注解埋點,名稱與配置的斷路器名稱須一一對應
@CloudBreaker("main")
public class BreakerController {
@Mapping("/breaker")
public void breaker(){
}
}
(8)使用跟蹤服務獲取並傳播TraceId(目前適配有:water)
通過MDC傳遞給 slf4j MDC
String traceId = CloudClient.trace().getTraceId();
MDC.put(CloudClient.trace().HEADER_TRACE_ID_NAME(), traceId);
通過Http Header 傳給后Http節點
HttpUtils.url("http://x.x.x.x")
.headerAdd(CloudClient.trace().HEADER_TRACE_ID_NAME(), traceId).get();
等......(Solon Cloud Log 默認支持 CloudClient.trace() 接口)
(9)使用簡單監控服務(目前適配有:water)
//監控服務的路徑請求性能(后端實現的時候,可以進一步記錄超5秒、超1秒的次數;以及典線圖)
CloudClient.metric().addMeter("path", path, milliseconds);
//監控服務的路徑請求出錯次數
CloudClient.metric().addCount("path_err", path, 1);
//監控服務的運行時狀態
CloudClient.metric().addGauge("service", "runtime", RuntimeStatus.now());
(10)使用分布式任務(目前適配有:water, xxl-job)
//注解模式 - Hander 風格(也可以用:Bean method 風格)
@CloudJob("JobHandlerDemo1")
public class JobHandlerDemo1 implements Handler {
@Override
public void handle(Context ctx) throws Throwable {
//任務處理
}
}
//手動模式
CloudClient.job().register("JobHandlerDemo3","",c->{
//任務處理
});
(11)使用分布式ID,生成有序不重復ID(目前適配有:snowflake)
long log_id = CloudClient.id().generate();
一般用於無邏輯性的ID生成,如:日志ID、事務ID、自增ID...
(12)使用分布式鎖,對流量或資源進行控制(目前適配有:water, redis)
if(CloudClient.lock().lock("user_"+user_id, 3)){
//對一個用戶嘗試3秒的鎖;3秒內不充行重復提交
}else{
//請求太頻繁了...
}
(13)使用分布式文件服務(目前適配有:aws-s3, aliyun-oss)
//使用分布式文件,存儲用戶擴展信息
CloudClient.file().put("solon/user_"+user_id, new Media("{name:noear}"))
//讀取
Media data = CloudClient.file().get("solon/user_"+user_id);
(14)使用網關,為同一套接口提供不同的輸出(Solon 自帶)
網關的技術本質,是一個定制了的 Solon Handler。如此理解,新切感會好些:)
//網關1
@Mapping("/api/rest/**")
@Component
public class Gateway1 extends Gateway {
@Override
protected void register() {
//設定默認render
before(c -> c.attrSet("@render", "@json"));
//添加服務
add("user", UserServiceImpl.class, true);
}
}
//網關2
@Mapping("/api/rpc/**")
@Component
public class Gateway3 extends Gateway {
@Override
protected void register() {
//設定默認render
before(c -> c.attrSet("@render", "@type_json"));
//添加服務(不帶mapping的函數;需要 remoting = true,才會加載出來)
add("user", UserServiceImpl.class, true);
}
}
//網關3(這個比較復雜,和實戰性)
@Mapping("/api/v2/app/**")
@Component
public class Gateway3 extends UapiGateway {
@Override
protected void register() {
filter(new BreakerFilter()); //融斷過濾器
before(new StartHandler()); //開始計時
before(new ParamsParseHandler()); //參數解析
before(new ParamsSignCheckHandler(new Md5Encoder())); //參數簽名較驗
before(new ParamsRebuildHandler(new AesDecoder())); //參數重構
after(new OutputBuildHandler(new AesEncoder())); //輸出構建
after(new OutputSignHandler(new Md5Encoder())); //輸出簽名
after(new OutputHandler()); //輸出
after(new EndBeforeLogHandler()); //日志
after(new EndHandler("v2.api.app")); //結束計時
addBeans(bw -> "api".equals(bw.tag()));
}
}
三、附:完整的配置
application.yml
solon.app:
group: demo #配置服務使用的默認組
name: helloapp #發現服務使用的應用名
solon.cloud.water:
server: water #water服務地址
config:
load: "test.properties" #默認加載一個配置
discovery:
enable: true #設為 false 時,solon.cloud.local.discovery 會生效(一般用於本地調試)
solon.cloud.local:
discovery:
service:
hellorpc:
- "http://localhost:7112" #本地服務配置
breaker:
main: 100
pom.xml (以下配置打包只有4.7m)
<parent>
<groupId>org.noear</groupId>
<artifactId>solon-parent</artifactId>
<version>1.5.5</version>
</parent>
<dependencies>
<!-- RPC 框架 -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-rpc</artifactId>
</dependency>
<!-- 配置服務、注冊與發現服務、日志服務、鎖服務、名單服務、跟蹤服務... (含 okhttp,redis,memcaced,HikariCP 等...) -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>water-solon-plugin</artifactId>
</dependency>
<!-- 融斷服務 -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>sentinel-solon-plugin</artifactId>
</dependency>
<!-- 文件服務 -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>aliyun-oss-solon-plugin</artifactId>
</dependency>
<!-- ID服務 -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>snowflake-id-solon-plugin</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<finalName>${project.artifactId}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>demo.DemoApp</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>