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); } }<360>(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: 100pom.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>
发表评论 取消回复