• 不妨试试更快更小更灵活Java开发框架Solon


    概述

    定义

    Solon 官网地址 https://solon.noear.org/

    Solon GitHub源码地址 https://github.com/noear/solon

    Solon for java,一个更现代感的,轻量级应用开发框架,崇尚克制、简洁、开放、生态设计理念。最新版本为1.10.7

    Solon从项目启动以来,参考过大量前人的作品。尤其是 Spring Boot、jFinal、Javalin 和 Asp.Net,吸取了诸多优点,且避开很多繁重的设计。历时多年,内核始终保持 0.1Mb 的身材,超高的跑分,良好而自由的使用体验。

    目前支持jdk8、jdk11、jdk17、jdk19四个大版本,开发定制方便,可通过组合不同的插件快速开发不同的需求,开发人员几乎可使用与SpringBoot相似的开发方式。其Solon Cloud 为一系列分布式开发的接口标准和配置规范,相当于DDD模式里的防腐层概念,是 Solon 的微服务架构模式开发解决方案。在开发使用上官方也提供其与SpringBoot、SpringCloud、Dubbo的详细区别,使用时查阅官方文档即可。

    性能

    Solon 根据官方提供数据,比传统的Java应用特别是Spring生态开发的应用启动快 5 ~ 10 倍,qps 高 2~ 3 倍,运行时内存节省 1/3 ~ 1/2,打包可以缩到 1/2 ~ 1/10。因此成为更现代感的应用开发框架,实现更快、更小、更少、更自由!

    • 快:Qps 可达10万之多
    • 小:内核 0.1Mb,最小 Web 完整开发单位 1Mb(相比Springboot项目包,小到可以乎略不计了)
    • 自由:代码操控自由,除了注解模式之外,还可以按需手动;框架选择自由:可以用 solon-web 这样的快速开发集成包。也可以按项目需要选择不同的插件组装,比如:为非Solon项目添加solon.boot.jlhttp,0.2Mb 即可让项目实现 http+mvc 支持。

    image-20221019105758727

    架构

    • 缘起统一的处理架构想法(俗称:三源合一):Http、Socket、WebSocket。不同的通讯信号,进行统一架构处理,且小巧。 对于 Socket 和 WebSocket,在原 消息+监听 的模式之外增加了 上下文+处理 模式

    image-20221020133420510

    • 关于应用内在的启动过程(即:应用的生命周期):串行的处理过程(含四个事件扩展点 + 两个函数扩展点)

    image-20221020133549054

    • 请求的处理过程

    image-20221020133609336

    • Ioc & Aop 内部结构

      image-20221020133742051

    • 现有家簇成员图谱

    image-20221020133754608

    实战

    Solon Web示例

    下载官方的helloworld示例 体验下Solon 轻量和快。此外还可以下载官网提供丰富的配套示例:

    项目地址说明
    solon-exampleshttps://gitee.com/noear/solon-examples配套"学习/科目学习"进行演示

    下载完解压后导入Idea中,是个标准的maven项目,pom文件引入solon的父依赖和核心依赖

    <dependency>
        <groupId>org.noear</groupId>
        <artifactId>solon-web</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4

    一个配置文件app.yml,一个启动类DemoApp,是不是和SpringBoot很相似,Solon 是一个容器型的应用开发框,在main方法中使用Solon.start启动。app.yml内容如下:

    server.port: 8080
    solon.app:
      name: demoapp
      group: demo
    
    • 1
    • 2
    • 3
    • 4

    这里简单修改hello方法的返回结果如下,可以直接运行,也可以先通过mvn clean package -DskipTests打包后再使用java -jar demo.jar运行。

    image-20221020141016190

    几小行代码一个http接口就完成,启动速度非常快只用3ms,访问http://localhost:8080/hello?name=itxiaoshen 返回正确的结果

    image-20221020141500974

    Solon Mybatis-Plus示例

    环境准备:创建MySQL数据库test、表appx,并插入测试数据

    CREATE TABLE `appx` (
      `app_id` INT NOT NULL AUTO_INCREMENT COMMENT '应用ID',
      `app_key` VARCHAR(40) DEFAULT NULL COMMENT '应用访问KEY',
      `akey` VARCHAR(40) DEFAULT NULL COMMENT '(用于取代app id 形成的唯一key) //一般用于推广注册之类',
      `ugroup_id` INT DEFAULT '0' COMMENT '加入的用户组ID',
      `agroup_id` INT DEFAULT NULL COMMENT '加入的应用组ID',
      `name` VARCHAR(50) DEFAULT NULL COMMENT '应用名称',
      `note` VARCHAR(50) DEFAULT NULL COMMENT '应用备注',
      `ar_is_setting` INT NOT NULL DEFAULT '0' COMMENT '是否开放设置',
      `ar_is_examine` INT NOT NULL DEFAULT '0' COMMENT '是否审核中(0: 没审核 ;1:审核中)',
      `ar_examine_ver` INT NOT NULL DEFAULT '0' COMMENT '审核 中的版本号',
      `log_fulltime` DATETIME DEFAULT NULL,
      PRIMARY KEY (`app_id`),
      UNIQUE KEY `IX_akey` (`akey`) USING BTREE
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='应用表';
    
    INSERT appx(app_key,akey,ugroup_id,agroup_id,NAME,note,ar_is_setting,ar_is_examine,ar_examine_ver,log_fulltime) 
    VALUES('asdfghjk','aaaaabbbbb',100,1001,'抖音','时尚短视频',0,1,1,NOW());
    INSERT appx(app_key,akey,ugroup_id,agroup_id,NAME,note,ar_is_setting,ar_is_examine,ar_examine_ver,log_fulltime) 
    VALUES('sdfsdf','ccccdddd',102,1002,'招行','储蓄',0,1,1,NOW());
    INSERT appx(app_key,akey,ugroup_id,agroup_id,NAME,note,ar_is_setting,ar_is_examine,ar_examine_ver,log_fulltime) 
    VALUES('34543','eeeegggg',103,1003,'有道词典','翻译',0,1,1,NOW());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    image-20221021112503788

    添加mybatis-plus和mysql相关依赖如下:

            <dependency>
                <groupId>org.noear</groupId>
                <artifactId>mybatis-plus-extension-solon-plugin</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
                <version>4.0.3</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.18</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    app.yml文件增加数据源和mybatis-plus的配置

    test.db1:
        schema: rock
        jdbcUrl: jdbc:mysql://192.168.40.100:3308/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
        driverClassName: com.mysql.cj.jdbc.Driver
        username: root
        password: 123456
    
    ##默认
    mybatis.db1:
        typeAliases:    #支持包名 或 类名(.class 结尾)
            - "demo4031.model"
        mappers:        #支持包名 或 类名(.class 结尾)或 xml(.xml结尾 或 *.xml 结尾)
            - "demo4031.dso.mapper"
    #        - "demo4031/dso/*.xml"
        configuration:
            cacheEnabled: false
            mapUnderscoreToCamelCase: true
            logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
        globalConfig:
            banner: false
            metaObjectHandler: "demo4031.dso.MetaObjectHandlerImpl"
            dbConfig:
                logicDeleteField: "deleted"
                logicDeleteValue: "2"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    mapper接口和mapper.xml文件与Spring整合Mybatis基本相同

    @Mapper
    public interface AppxMapper {
        AppxModel appx_get();
        Page<AppxModel> appx_get_page(Page<AppxModel> page);
        AppxModel appx_get2(int app_id);
        void appx_add();
        Integer appx_add2(int v1);
    
        @Select("SELECT * FROM INFORMATION_SCHEMA.TABLES")
        List<DbTable> listTables();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    再添加业务的Service和实现类,最后添加PlusController控制器实现

    @Mapping("/plus/")
    @Controller
    public class PlusController {
        @Inject
        AppServicePlus appServicePlus;
    
        @Mapping("test")
        public AppxModel test() {
            return appServicePlus.getById(2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    添加mybatis-plus分页的PageController控制器实现

    @Mapping("/page/")
    @Controller
    public class PageController {
        @Db
        AppxMapper appxMapper;
    
        @Mapping("test")
        public Object test() throws Throwable {
            Page<AppxModel> page = new Page<>(2, 2);
            return appxMapper.appx_get_page(page);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    启动程序后日志输出如下

    image-20221021145226373

    访问http://localhost:8080/plus/test,返回正确的结果

    image-20221021105957550

    访问http://localhost:8080/page/test ,返回正确的分页结果

    image-20221021110153007

    Solon WebSocket示例

    引入依赖

            <dependency>
                <groupId>org.noear</groupId>
                <artifactId>nami</artifactId>
            </dependency>
            <dependency>
                <groupId>org.noear</groupId>
                <artifactId>solon.socketd.client.websocket</artifactId>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    简单几行代码就实现WebSocket的服务端编程

    @ServerEndpoint(value = "/ws/demo/{id}")
    public class WebSocket implements Listener {
        @Override
        public void onOpen(Session session) {
            //path var
            String id = session.param("id");
            //query var
            String token = session.param("token");
            /*此处可以做签权;会话的二次组织等...*/
        }
    
        @Override
        public void onMessage(Session session, Message message) throws IOException {
            //message.setHandled(true); //设为true,则不进入mvc路由
            session.send("你发了:" + message.bodyAsString());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    然后通过一个debug.htm通过javascript实现WebSocket收发功能,App启动类开启enableWebSocket

    public class App {
        public static void main(String[] args) {
            //
            // 启动Solon,并开启WebSocket监听;同时添加/路径跳转
            //
            Solon.start(App.class, args, app -> app.enableWebSocket(true)).get("/", c -> {
                c.redirect("/debug.htm");
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    启动App后日志输出如下

    image-20221021144646593

    访问http://localhost:8080/ 输入发送信息后服务端打印收到的输入信息

    image-20221020170902832

    Solon Remoting RPC示例

    RPC的实现分为3个模块,RPC提供者的实现、公共模块、服务消费者,公共模块存放数据模型和接口,可以同时提供给提供者和消费者引用。

    服务提供者添加solon-rpc依赖

            <dependency>
                <groupId>org.noear</groupId>
                <artifactId>solon-rpc</artifactId>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4

    服务提供者通过@Remoting注解实现RPC服务,代码如下

    @Mapping("/user/")
    @Remoting
    public class UserServiceImpl implements UserService {
        @Override
        public UserModel getUser(Integer userId) {
            UserModel model = new UserModel();
            model.setId(userId);
            model.setName("user-" + userId);
            return model;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    服务消费者添加如下依赖

            <dependency>
                <groupId>org.noear</groupId>
                <artifactId>solon-rpc</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.noear</groupId>
                <artifactId>solon.cloud</artifactId>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    app.yml配置文件配置服务发现的地址,也即是服务提供者提供的地址

    server.port: 8080
    
    solon.app:
      name: demoapp
      group: demo
    
    solon.cloud.local:
      discovery:
        service:
          local:
            - "http://localhost:8081"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过@NamiClient注解实现RPC远程方法的调用

    @Controller
    public class UserController {
        //使用负载
        @NamiClient(name = "local",path = "/user/")
        UserService userService;
    
        @Mapping("test")
        public UserModel test() {
            UserModel user = userService.getUser(100);
            System.out.println(user);
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    启动服务提供者和服务消费者

    image-20221021144646593

    访问服务消费者测试Controller的测试接口,http://localhost:8080/test ,返回正确结果

    image-20221021102514729

    Solon Cloud Nacos示例

    引入依赖

            <dependency>
                <groupId>org.noear</groupId>
                <artifactId>nacos-solon-cloud-plugin</artifactId>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4

    先准备好Nacos Server,这里就直接使用前面文章已部署好的Nacos,创建好nacos的test命名空间,为了演示读取nacos的配置,在test下创建一个组为demo的test.properties,并添加db1.url的键值对。

    image-20221021143133768

    然后在服务注册端的本地app.yml配置文件添加相关nacos的配置信息

    server.port: 7112
    solon.app:
      namespace: test
      group: demo
      name: helloapi    #发现服务使用的应用名(在Demo,将被NimaClient引用)
    solon.cloud.nacos:
      server: 192.168.50.95:8848   #nacos服务地址
      username: nacos             #nacos链接账号
      password: nacos             #nacos链接密码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    声明HelloService接口,服务注册方实现接口,服务端的工作就完成了

    @Mapping("/rpc/")@Remotingpublic class HelloServiceRemoteImp implements HelloService {    @Override    public String hello() {        return "remote: hello";    }}
    
    • 1

    作为服务发现的客户端本地app.yml配置文件添加相关nacos的配置信息如下

    solon.app:  namespace: test  group: demo       #配置服务使用的默认组  name: helloapp    #发现服务使用的应用名solon.cloud.nacos:  server: 192.168.50.95:8848   #nacos服务地址  username: nacos             #nacos链接账号  password: nacos             #nacos链接密码  config:    load: "test.properties"
    
    • 1

    测试的客户端中也是通过注解@NamiClient注入HelloService接口,添加一个测试controller控制器演示

    @Controllerpublic class TestController {    //这是远程的    @NamiClient    HelloService helloService;    @Mapping("/test")    public String test() throws Exception {        helloService.hello();        String temp = helloService.hello();        System.out.println("helloService return"+temp);        return temp + "," + Solon.cfg().get("db1.url");    }}
    
    • 1

    已启动服务注册serverApp和服务发现ClientApp

    image-20221021142155320

    查看Nacos服务管理可以看下服务名已经正常注册了

    image-20221021143425294

    访问测试地址http://localhost:8080/test,可以看到成功调用服务注册方的方法,也打印从Nacos配置中心的配置项值,至此,已经实现基于Nacos的配置、服务注册和发现的基本功能。

    image-20221021143533069

    **本人博客网站 **IT小神 www.itxiaoshen.com

  • 相关阅读:
    6.netty线程模型-Reactor
    50mm镜头对20m外标定板成像实验
    Python --- 面向对象
    Seq2Seq - 序列到序列的学习(RNN循环神经网络)
    416. 分割等和子集 1049.最后一块石头的重量II
    Linux命令之usermod命令
    信息熵和热力学定律中的熵有关系吗?
    JVM 对 Java 的 原 生 锁 做 了 哪 些 优 化?
    Win10/Win11下部署Django项目到Apache2.4的方法
    出现了一个全新的编程语言——Mojo
  • 原文地址:https://blog.csdn.net/qq_20949471/article/details/127455504