Redkale 2.8.0 发布,Java 分布式微服务框架
Redkale, 一个 Java 分布式微服务框架,2.2M 的 jar 可以代替传统几十 M 的第三方。包含 TCP/UDP、HTTP、RPC、依赖注入、序列化与反序列化、数据库操作、WebSocket、配置中心、消息队列等功能。 极大的简化业务开发代码,在模块高度整合的同时暴露大量底层,方便二次框架开发。
Java 并不臃肿, 臃肿的是你自己的设计思维!
本次更新主要内容:
1、【新增】FilterNode 增加 Lambda 表达式
2、【新增】Convert 增加字段值转化功能,可用于脱敏
3、【新增】Convert 增加 protobuf 实现
4、【新增】DataSqlSource 增加 DataSqlMapper 和 sql 模板功能
5、【新增】增加 sql 解析器 DataNativeSqlParser 功能
6、【新增】CacheSource 增加订阅消息功能
7、【新增】CacheSource 增加令牌桶算法限流接口
8、【新增】增加方法定时任务功能 @Scheduled
9、【新增】增加方法缓存功能 @Cached
10、【新增】增加方法 MQ 消息功能 @Messaged
11、【新增】增加方法非阻塞模式 @NonBlocking
12、【新增】支持 JDK21 虚拟线程池
13、【新增】增加结果码公共类 RetCodes
14、【新增】支持 yaml 配置格式
15、【优化】重构框架模块化 ModuleEngine
16、【优化】大量包和类名的优化和调整
17、【优化】Convert 兼容 setter 返回值是 void 或类本身对象
18、【优化】HTTP 支持 Expect:100-continue
19、【优化】对象复制类 Copier 代替 Reproduce
20、【优化】javax.annotation 包迁移到 org.redkale.annotation
21、【优化】javax.persistence 包迁移到 org.redkale.persistence
22、【修复】修复 ArrayEncoder 并发下 componentEncoder 偶尔为 null 的 bug
更新详情介绍:
DataSqlMapper 和 SQL 模板:
Redkale 有自己独特设计的 ORM 模块,单表、简单关联表、简单分库分表的增删改查都不需要写 sql,使用 DataSource 可满足大部分需求,同时兼容类似 Mongodb、ElasticSearch 非关系型数据库。 针对复杂的 sql,新增的 DataSqlMapper 支持 sql 模板, 不需要像 MyBatis 那样写 xml,也不需要写很多 if/else 判断语句, 框架会根据参数是否存在动态生成 sql 语句, 查询结果时带下划线的 sql 字段名和类中的驼峰字段名会自动匹配。
SELECT * FROM user_info WHERE user_id IN #{bean.userIds} OR user_name = #{bean.userName}
当 bean.userIds=null,bean.userName='hello' 时,sql 语句转换成:
SELECT * FROM user_info WHERE user_name = 'hello'
当 bean.userIds=[1,2,3],bean.userName=null 时,sql 语句转换成:
SELECT * FROM user_info WHERE user_id IN (1,2,3)
IN 或者 NOT IN 语句当参数不为 null 而是空数组或者空 List 会进行特殊处理,IN 语句会转化成 1=1, NOT IN 语句会转化成 1=2。 当 bean.userIds = 空数组,bean.userName='hello' 时,sql 语句转换成:
SELECT * FROM user_info WHERE 1=2 OR user_name = 'hello'
有些场景要求参数是必需的,就需要使用 ##{} 来校验参数是否必需。
DELETE FROM user_info WHERE user_name = ##{bean.userName}
当 bean=null 或者 bean.userName=null 时,执行 sql 会报错 Missing parameter bean.userName
参数支持默认值 , 用逗号隔开,非 String 参数类型主要指定数据类型,目前支持 (int)、(long)、(short)、(float)、(double), 参数不存在会使用默认值:
DELETE FROM user_info WHERE user_name = #{bean.userName,aaa}
IN 参数也支持默认值:
DELETE FROM user_info WHERE type IN #{types,(1, 2, 3)} DELETE FROM user_info WHERE type IN (1, 2, #{type, (int)3}) DELETE FROM user_info WHERE user_name IN #{name, ('aa','bb','cc')} DELETE FROM user_info WHERE user_name ('aa', 'bb', #{type,cc})
Service 调用原生 SQL 模板示例:
public class ForumInfoService extends AbstractService { //查询单个记录的sql private static final String findSql = "SELECT f.forum_groupid, s.forum_section_color " + "FROM forum_info f, forum_section s " + " WHERE f.forumid = s.forumid AND " + "s.forum_sectionid = #{bean.forumSectionid} AND " + "f.forumid = #{bean.forumid} AND s.forum_section_color = #{bean.forumSectionColor}"; //查询列表记录的sql private static final String querySql = "SELECT f.forum_groupid, s.forum_section_color " + "FROM forum_info f, forum_section s " + " WHERE f.forumid = s.forumid AND " + "s.forum_sectionid = #{bean.forumSectionid} AND " + "f.forumid = #{bean.forumid} AND s.forum_section_color = #{bean.forumSectionColor}"; @Resource private DataSqlSource source; public ForumResult findForumResultOne(ForumBean bean) { return source.nativeQueryOne(ForumResult.class, findSql, Map.of("bean", bean)); } public CompletableFuture> queryForumResultListAsync(ForumBean bean) { return source.nativeQueryListAsync(ForumResult.class, querySql, Map.of("bean", bean)); } //翻页查询 public Sheet queryForumResult(RowBound bound, ForumBean bean){ return source.nativeQuerySheet(ForumResult.class, querySql, round, Map.of("bean", bean)); } }
也可使用 DataSqlMapper:
public interface ForumInfoMapper extends BaseMapper<ForumInfo> { @Sql("SELECT f.forum_groupid, s.forum_section_color " + "FROM forum_info f, forum_section s " + "WHERE f.forumid = s.forumid " + "AND s.forum_sectionid = #{bean.forumSectionid} " + "AND f.forumid = #{bean.forumid} " + "AND s.forum_section_color = #{bean.forumSectionColor}") public ForumResult findForumResultOne(ForumBean bean); @Sql("SELECT f.forum_groupid, s.forum_section_color " + "FROM forum_info f, forum_section s " + "WHERE f.forumid = s.forumid AND " + "s.forum_sectionid = #{bean.forumSectionid} " + "AND f.forumid = #{bean.forumid} " + "AND s.forum_section_color = #{bean.forumSectionColor}") public CompletableFuture findForumResultOneAsync(ForumBean bean); //翻页查询 @Sql("SELECT f.forum_groupid, s.forum_section_color " + "FROM forum_info f, forum_section s " + "WHERE f.forumid = s.forumid AND " + "s.forum_sectionid = #{bean.forumSectionid} " + "AND f.forumid = #{bean.forumid} " + "AND s.forum_section_color = #{bean.forumSectionColor}") public Sheet queryForumResult(RowBound bound, ForumBean bean); @Sql("UPDATE forum_section s " + " SET s.forum_sectionid = '' " + " WHERE s.forum_section_color = ##{bean.forumSectionColor}") public int updateForumResult(@Param("bean") ForumBean bean0); }
支持多数据源:
public class ForumInfoService implements Service { //基于默认数据源创建的DataSqlMapper @Resource private ForumInfoMapper forumInfoMapper; //基于platf数据源创建的DataSqlMapper @Resource(name = "platf") private ForumInfoMapper forumInfoMapper2; }
方法缓存 Cached (只能标记在 Service 非 final/static 的 protoectd 或 public 的方法上):
将结果进行本地缓存 30 秒且远程缓存 60 秒:
@Cached(key = "name", localExpire = "30", remoteExpire = "60") public String getName() { return "haha"; }
以参数 code 为 key 将结果进行本地缓存 (时长由环境变量 env.cache.expire
配置,没配置采用默认值 30 秒):
@Cached(name = "name", key = "#{code}", localExpire = "${env.cache.expire:30}") public CompletableFuture getNameAsync(String code) { return redis.getStringAsync(code); }
以参数 code+map.id 为 key 将结果进行远程缓存 60 毫秒:
@Resource private CachedManager cachedManager; //实时修改远程缓存的key值 public void updateName(String code, Map<String, Long> map) { String key = code + "_" + map.get("id"); cachedManager.remoteSetString("name", key, code + "-" + map, Duration.ofMillis(60)); } @Cached(name = "name", key = "#{code}_#{map.id}", remoteExpire = "60", timeUnit = TimeUnit.MILLISECONDS) public String getName(String code, Map<String, Long> map) { return code + "-" + map; }
动态调整缓存时长
@Resource private CachedManager cachedManager; @Cached(name = "name", key = "#{code}_#{map.id}", remoteExpire = "60", timeUnit = TimeUnit.MILLISECONDS) public String getName(String code, Map<String, Long> map) { return code + "-" + map; } public void updateExpire() { Duration expire = Duration.ofMillis(600); cachedManager.acceptCachedAction("name", action -> { //将缓存时长改成600毫秒,并开启本地缓存 action.setLocalExpire(expire); action.setRemoteExpire(expire); }); }
配置多个多缓存器, 使用 @Resource
注入多个 CachedManager
<cached name="" enabled="true" remote="redis_1" broadcastable="true"/> <cached name="backup" enabled="true" remote="redis_2" broadcastable="true"/>
//第一个缓存器 @Resource private CachedManager cachedManager; //第二个缓存器 @Resource(name = "backup") private CachedManager cachedManager2; //第一个缓存器实时修改远程缓存的key值 public void updateName(String code, Map<String, Long> map) { String key = code + "_" + map.get("id"); cachedManager.remoteSetString("name", key, code + "_" + map, Duration.ofMillis(60)); } //使用第一个缓存器 @Cached(name = "name", key = "#{code}_#{map.id}", remoteExpire = "60", timeUnit = TimeUnit.MILLISECONDS) public String getName(String code, Map<String, Long> map) { return code + "-" + map; } //使用第二个缓存器 @Cached(manager = "backup", name = "name2", key = "#{code}_#{map.id}_2", remoteExpire = "60", timeUnit = TimeUnit.MILLISECONDS) public String getName2(String code, Map<String, Long> map) { return code + "-" + map; }
定时任务 Scheduled:
每秒执行:
@Scheduled(cron = "0/1 * * * * ?") public void task1() { System.out.println(Times.nowMillis() + "执行一次"); }
数值配置 , 系统启动后延迟 10 分钟后每 60 分钟执行一次:
@Scheduled(fixedDelay = "10", fixedRate = "60", timeUnit = TimeUnit.MINUTES) private void task3() { System.out.println(Times.nowMillis() + "执行一次"); }
环境配置 , 定时间隔时间由环境变量 env.scheduled.fixedRate 配置,没配置采用默认值 60 秒):
@Scheduled(fixedRate = "${env.scheduled.fixedRate:60}") public String task2() { System.out.println(Times.nowMillis() + "执行一次"); return ""; }
使用 Xxl-Job
Scheduled 可以采用第三方实现,官方扩展包 redkale-plugins
提供了 xxl-job 实现,且不依赖 xxl-job 包。
pom 依赖:
<dependency> <groupId>org.redkalexgroupId> <artifactId>redkale-pluginsartifactId> <version>2.8.0version> dependency>
配置文件:
"true"> <xxljob addresses="http://localhost:8080/xxl-job-admin" executorName="redkale-examples" ip="127.0.0.1" -- 可选 --> port="5678" accessToken="default_token" /> scheduled>
使用方法:
@Scheduled(name = "testTask") public void runTask(ScheduledEvent event) { System.out.println("xxl-job参数param: " + event.getString("param")); System.out.println("xxl-job参数index: " + event.getInteger("index")); System.out.println("xxl-job参数total: " + event.getInteger("total")); }
MQ 消息生成消费:
MessageAgent 是消息中心抽象接口。
配置参数
<mq name="mymq" type="kafka"> <servers value="127.0.0.1:9092"/> <consumer autoload="true"/> mq>
消息消费
通过 @ResourceConsumer
和 MessageConsumer
接口实现消费
@ResourceConsumer(mq = "mymq", topics = "test_bean_topic") public class TestMessageConsumer implements MessageConsumer<TestBean> { @Override public void init(AnyValue config) { System.out.println("执行 TestMessageConsumer.init"); } @Override public void onMessage(MessageConext context, TestBean message) { System.out.println("消费消息, message: " + message); } @Override public void destroy(AnyValue config) { System.out.println("执行 TestMessageConsumer.destroy"); } }
通过 Service 里标记 @Messaged
的方法实现消费,方法只能是 protected
或 public
, 不能是 final
、static
。
public class TestMessageService extends AbstractService { @Messaged(mq = "mymq", topics = "test_bean_topic") protected void runMessage(TestBean message) { System.out.println("消费消息, message: " + message); } }
通过 @Component
的 Service 里标记 @Messaged
的方法实现消费,方法只能是 public
。
@Component public final class TestMessageService extends AbstractService { @Messaged(mq = "mymq", topics = "test_bean_topic") public int runMessage(TestBean message) { System.out.println("消费消息, message: " + message); return 0; } }
消息生成
通过 @Component
的 Service 里标记 @Messaged
的方法实现消费,方法只能是 public
。
@Component public class TestMessageService extends AbstractService { @ResourceProducer(mq = "mymq") private MessageProducer producer; public void sendMessage() { TestBean bean = new TestBean(12345, "this is a message"); System.out.println("生产消息: " + bean); producer.sendMessage("test_bean_topic", bean); } }
Topic 管理
通过 MessageManager
操作 topic。
public class TestMessageManager extends AbstractService { @Resource(name = "mymq") private MessageManager manager; // 创建topic public void initTopic() { manager.createTopic("topic_1", "topic_2").join(); } // 删除topic public void deleteTopic() { manager.deleteTopic("topic_1", "topic_2").join(); } // 查询topic public void printTopic() { List topics = manager.queryTopic().join(); } }

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
kunlun-admin 2.0.2 发布,SpringCloud+React 多租户多应用 Saas 管理系统
SpringCloud + React 多租户多应用 Saas前后端分离 微服务版 昆仑管理系统 V2.0.2 更新日志 1、多租户多应用业务架构,实现数据的租户隔离和应用隔离; 2、Saas 模式权限管理,包括资源、角色和岗位的授权控制; 3、优化并修复前端及后端若干问题。 系统介绍 昆仑管理系统是一套基于前后端分离微服务架构的后台管理系统,同时采用了多租户多应用的 Saas 设计架构模式。 kunlun-web 是基于 React + Umi (乌米) 的可扩展的企业级前端应用框架构建开发,Umi 是蚂蚁集团的底层前端框架,并使用 Ant Design (蚂蚁集团) 设计可视化界面,提供前端解决方案;kunlun-service 基于 SpringBoot + Spring Cloud 构建开发,提供后端基于微服务架构的解决方案。系统使用 Apache Shiro 与 Jwt 组件,通过 token 进行数据交互认证,可快速开发并独立进行 Docker 容器化部署。 系统业务架构设计采用多租户多应用的 Saas 模式,可以实现数据的租户隔离与应用隔离,同时可对资源、岗位和角...
- 下一篇
🎉 降低 DDD 实践成本 | Wow 3.19.6 发布
领域驱动|事件驱动|测试驱动|声明式设计|响应式编程|命令查询职责分离|事件溯源 官方文档:https://wow.ahoo.me/ 更新内容 重构(core): 移除重复的错误集合。 修复(deps): 更新依赖 org.springframework.boot:spring-boot-dependencies 至 v3.4.1。 新功能(query): 支持 Number 到 Operator.BEFORE_TODAY。 新功能(dashboard): 更新 Angular 至 v19。 重构(query): 添加 QueryFilter API。 任务(deps): 更新依赖 gradle 至 v8.12。 修复(deps): 更新依赖 io.mockk:mockk 至 v1.13.14。 重构(query): 添加 QueryService API。 重构(query): 添加 Operator.AGGREGATE_ID/s。 重构(query): QueryHandler。 新功能(openapi): 支持PagedQueryEventStreamRouteSpec。 新功能...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6