Solon Cloud 分佈式開發套件清單與快速概覽

Solon Cloud 是一系列的接口標準和配置規範。Solon Cloud 為常見的分佈式系統模式提供了一種簡單且方便的編程模式,幫助開發人員構建有彈性的、可靠的、協調的應用程序。Solon Cloud 構建於 Solon 之上,可使開發者很容易入手並快速應用於生產中。Solon Cloud 主要由三部份組成:接口定義與配置規範、實現相關接口定義的各種插件,以及通用客戶端。

Solon Cloud 非常容易實現與自研框架進行對接。只要實現相關接口定義,按規範配置的一個插件,即是一個 Solon Cloud 插件。

Solon Cloud 項目源碼://gitee.com/noear/solon

Solon Cloud 示例源碼://gitee.com/noear/solon_cloud_demo

特點1:身材苗條

最小的 Solon Cloud 開發單位只有4m(含 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 Tracing CloudTraceService solon.cloud.@@.trace
事件總線 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服務)

(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)使用配置服務

通過本地配置導入需要的分佈式配置

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調用

服務端

//
// 1.所有 remoting = true 的組件,即為 rpc 服務;
// 2.以 uri 的形式提供資源描述,以同時支持 rest api 和 rpc 兩種模式
//
@Mapping("/rpc/")
@Component(remoting = true)
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日誌接口,轉發到分佈式日誌記錄器

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 做日誌服務,只需要排除掉 solon.logging.impl 框架卻可

(5)使用分佈式事件進行業務水平擴展

Solon Cloud Event 的兩個特性說明:

  1. 自守護模式,即失敗後不斷延時重發確保最終成功。此特性可支持SAGA分佈式事務模型。
  2. 多通道模式,即不同消息隊列並存。此特性可按業務做不同安排,例如:業務消息用 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限制

public class ListController {
    public void hello(Context ctx){
        String ip = IpUtils.getIP(ctx);
        
        if(CloudClient.list().inListOfIp("safelist", ip) == false){
            return;
        }
        
        //業務處理...
    }
}

(7)使用融斷器進行限流控制

添加配置(此配置可通過配置服務,動態更新)

solon.cloud.local:
  breaker:
    main: 100 #qps = 100

通過註解,添加埋點

//此處的註解埋點,名稱與配置的斷路器名稱須一一對應
@CloudBreaker("main")
public class BreakerController {
    @Mapping("/breaker")
    public void breaker(){
        
    }
}

(8)使用跟蹤服務獲取並傳播TraceId

通過MDC傳遞給 slf4j MDC

String traceId = CloudClient.trace().getTraceId();

MDC.put(CloudClient.trace().HEADER_TRACE_ID_NAME(), traceId);

通過Http Header 傳給後Http節點

HttpUtils.url("//x.x.x.x")
  .headerAdd(CloudClient.trace().HEADER_TRACE_ID_NAME(), traceId).get();

等……(Solon Cloud Log 默認支持 CloudClient.trace() 接口)

(9)使用分佈式ID,生成有序不重複ID

long log_id = CloudClient.id().generate();

(10)使用分佈式鎖,對流量或資源進行控制

if(CloudClient.lock().lock("user_"+user_id, 3)){
    //對一個用戶嘗試3秒的鎖;3秒內不充行重複提交
}else{
    //請求太頻繁了...
}

(11)使用分佈式文件服務

//使用分佈式文件,存儲用戶擴展信息
CloudClient.file().putText("solon/user_"+user_id, "{name:noear}")

(12)使用網關,為同一套接口提供不同的輸出

//網關1
@Mapping("/api/rest/**")
@Component
public class ApiGateway 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 RpcGateway extends Gateway {
    @Override
    protected void register() {
        //設定默認render
        before(c -> c.attrSet("@render", "@type_json"));

        //添加服務(不帶mapping的函數;需要 remoting = true,才會加載出來)
        add("user", UserServiceImpl.class, true);
    }
}

三、附:完整的配置

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:
        - "//localhost:7112"  #本地服務配置
  breaker:
    main: 100

pom.xml (以下配置打包只有4.7m)

<parent>
    <groupId>org.noear</groupId>
    <artifactId>solon-parent</artifactId>
    <version>1.3.32</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>