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

劉之西東發表於2021-05-06

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 開發單位只有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("http://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:
        - "http://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>

相關文章