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 的两个特性说明:
- 自守护模式,即失败后不断延时重发确保最终成功。此特性可支持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限制
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)使用网关,为同一套接口提供不同的输出
网关的技术本质,是一个定制了的 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.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>