400-888-0545
有创意,就创造,让信息化建设更便捷更高效
O2OA是基于JavaEE分布式架构的全平台,全终端,国产化开源低代码开发平台。平台拥有5个核心系统以及多个内置应用,能够让开发者根据客户需求按需定制、零代码快速实现复杂业务管理,和现有组织架构、基础数据、权限体系形成一体化业务建模。
开发方式及特点
1. 平台全功能源码公开(包括移动端),官方承诺无任何功能和人数限制。
2. 可视化开发,流程、表单编辑所见即所得,界面样式支持灵活定制,可集成Vue,React以及Angular。
3. Nashorn引擎,支持直接使用JavaScript完成服务代码编写并且发布使用。
4. 支持第三方以Restful服务方式驱动和管理流程,支持平台与第三方服务的数据交互。
5. 支持私有化部署、内网部署,支持传输加密,数据更安全。
6. 提供原生的IOS/Android应用, 并且支持WeLink、钉钉和企业微信集成。
How to Start

windows

1.下载o2server. yyyyMMddHHmmss_ windows.zip程序包.
2.解压下载后的压缩包到任意目录
3.确认开通服务器的80、20020、20030端口
4.打开o2server文件夹,选择start_ windows.bat双击打开
5.启动服务,等待相关服务自动完成.
6.自动完成后打开浏览器访问http://127.0.0.1
7.输入用户名xadmin密码o2登陆系统

linux

1.下载o2server. yyyyMMddHHmmss_linux.zip程序包.
2.确认开通服务器的80、20020、20030端口
3."unzip o2server. syyyMMddHHmmss linux.zip" 解压程序包.
4."cd o2server. yyyMMddHHmmss_ linux" 进入解压目录
5."cd o2server"进入程序目录.
6."./start. linux.sh" 回车启动服务器控制台.
7.启动服务,等待相关服务自动完成.
8.自动完成后打开浏览器访问http://127.0.0.1
9.输入用户名xadmin密码o2登陆系统

Configure The Compilation Environment

JVM 下载

安装NodeJS

1. 访问nodejs的官方网站的downdolad,网址:https://nodejs.org/en/download/,获取Linux Binaries (x64)安装包下载链接:

wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-x64.tar.xz

2. 解压安装:

# yum search xz # yum install xz.i386
# xz -d node-v10.15.0-linux-x64.tar.xz
# tar -xf node-v10.15.0-linux-x64.tar
# mv node-v10.15.0-linux-x64 node-v10.15.0

3. 配置nodejs(略)

安装 Java8 及配置Java环境

安装 apache-maven-3.6.0 及配置maven环境

wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo

Source Code Compilation

1、打开命令提示工具,cd到o2oa/o2server目录cd /usr/o2oa/o2server
2、执行命令进行编译: mvn install,开始进行源码编译mvn install

关于编译错误

o2oa/o2server/target目录下会有打包好的zip包,将此zip包Copy到其他目录解压(避免目录层级太深造成启动异常),
服务器部署和后动相关的教程文档, 请移步系列教程: https://my.oschina.net/u/3931542/blog/2209110

程序人生
5/10 第1
各种主流软件开源协议解释说明
世界上的开源许可证,大概有上百种。反正笔者是搞不懂他们之间的区别。目前最常见、最流行的六种开源协议:GPL、BSD、MIT、Mozilla、Apache和LGPL,看起来还是很复杂。找到中文说明:http://www.gnu.org/licenses/license-list.html原来看懂开源协议也挺难的,开始怀疑自己的理解能力。图片引用阮一峰的博客:  https://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html据说此图出自乌克兰程序员Paul Bagwell不花钱,还能拿来赚钱,拿来基本随便用,能闭源-区别在于广告和版权说明一:Apache Licence-随便改,广告也无所谓,版权上有我名字Apache Licence :Apache License, Version 2.0、Apache License, Version 1.1、Apache License, Version 1.0)Apache Licence是著名的非盈利开源组织Apache采用的协议。鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)二:MIT(MIT)-随便改,不能写我的名字打广告,版权无所谓MIT是和BSD一样宽范的许可协议,作者只想保留版权,而无任何其他了限制.也就是说,你必须在你的发行版里包含原许可协议的声明,无论你是以二进制发布的还是以源代码发布的。三:BSD开源协议(original BSD license、FreeBSD license、Original BSD license)-随便改,什么都无所谓BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。不能“免费”拿来赚钱四:MPL(Mozilla Public License)-您的修改归开源软件的发起者,这是奉献精神MPL协议允许免费重发布、免费修改,但要求修改后的代码版权归软件的发起者 。这种授权维护了商业软件的利益,它要求基于这种软件的修改无偿贡献版权给该软件。五:GPL(GNU General Public License)-我开源,你就得开源我们很熟悉的Linux 就是采用了GPL 。GPL 协议和BSD, Apache Licence 等鼓励代码重用的许可很不一样。GPL不允许修改后和衍生的代码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux ,包括商业公司的linux 和linux 上各种各样的由个人,组织,以及商业软件公司开发的免费软件了。GPL 协议的主要内容是只要在一个软件中使用(” 使用” 指类库引用,修改后的代码或者衍生代码)GPL 协议的产品,则该软件产品必须也采用GPL 协议,既必须也是开源和免费。这就是所谓的” 传染性”。GPL 协议的产品作为一个单独的产品使用没有任何问题,还可以享受免费的优势。由于GPL 严格要求使用了GPL 类库的软件产品必须使用GPL 协议,对于使用GPL 协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/ 采用作为类库和二次开发的基础。六:LGPL(GNU Lesser General Public License)-可以有部分代码不开源,有点赚头LGPL 协议的开源 代码很适合作为第三方类库被商业软件引用LGPL 是GPL 的一个为主要为类库使用设计的开源协议。和GPL 要求任何使用/ 修改/ 衍生之GPL 类库的的软件必须采用GPL 协议不同。LGPL 允许商业软件通过类库引用(link) 方式使用LGPL 类库而不需要开源商业软件的代码。这使得采用LGPL 协议的开源代码可以被商业软件作为类库引用并 发布和销售。但是如果修改LGPL 协议的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生的代码都必须采用LGPL 协议。因此LGPL 协议的开源 代码很适合作为第三方类库被商业软件引用,但不适合希望以LGPL 协议代码为基础,通过修改和衍生的方式做二次开发的商业软件采用。O2OA 使用的开源协议 AGPL(Affero General Public License)协议说明https://www.gnu.org/licenses/why-affero-gpl.html说人话:1.APGL协议,您可以随意修改、自用,但是不能用来商用。2.APGL协议,您可以自用,但是不能再互联网上搭建服务,提供给网友使用。例如:你不能用o2oa搭建一个多租户平台在互联网上进行用户注册售卖,这样会有法律风险。引用自开源中国购买o2oa商用许可授权后,您相当于得到了一套商业软件的源码和使用权,不受开源协议的限制。
23
2020-09
导致MySQL索引失效的几种常见写法
最近一直忙着处理原来老项目遗留的一些SQL优化问题,由于当初表的设计以及字段设计的问题,随着业务的增长,出现了大量的慢SQL,导致MySQL的CPU资源飙升,基于此,给大家简单分享下这些比较使用的易于学习和使用的经验。这次的话简单说下如何防止你的索引失效。再说之前我先根据我最近的经验说下我对索引的看法,我觉得并不是所以的表都需要去建立索引,对于一些业务数据,可能量比较大了,查询数据已经有了一点压力,那么最简单、快速的办法就是建立合适的索引,但是有些业务可能表里就没多少数据,或者表的使用频率非常不高的情况下是没必要必须要去做索引的。就像我们有些表,2年了可能就10来条数据,有索引和没索引性能方面差不多多少。索引只是我们优化业务的一种方式,千万为了为了建索引而去建索引。下面是我此次测试使用的一张表结构以及一些测试数据CREATE TABLE `user` ( `id` int(5) unsigned NOT NULL AUTO_INCREMENT, `create_time` datetime NOT NULL, `name` varchar(5) NOT NULL, `age` tinyint(2) unsigned zerofill NOT NULL, `sex` char(1) NOT NULL, `mobile` char(12) NOT NULL DEFAULT '', `address` char(120) DEFAULT NULL, `height` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_createtime` (`create_time`) USING BTREE, KEY `idx_name_age_sex` (`name`,`sex`,`age`) USING BTREE, KEY `idx_ height` (`height`) USING BTREE, KEY `idx_address` (`address`) USING BTREE, KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=261 DEFAULT CHARSET=utf8;INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (1, '2019-09-02 10:17:47', '冰峰', 22, '男', '1', '陕西省咸阳市彬县', '175'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (2, '2020-09-02 10:17:47', '松子', 13, '女', '1', NULL, '180'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (3, '2020-09-02 10:17:48', '蚕豆', 20, '女', '1', NULL, '180'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (4, '2020-09-02 10:17:47', '冰峰', 20, '男', '17765010977', '陕西省西安市', '155'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (255, '2020-09-02 10:17:47', '竹笋', 22, '男', '我测试下可以储存几个中文', NULL, '180'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (256, '2020-09-03 10:17:47', '冰峰', 21, '女', '', NULL, '167'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (257, '2020-09-02 10:17:47', '小红', 20, '', '', NULL, '180'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (258, '2020-09-02 10:17:47', '小鹏', 20, '', '', NULL, '188'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (259, '2020-09-02 10:17:47', '张三', 20, '', '', NULL, '180'); INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (260, '2020-09-02 10:17:47', '李四', 22, '', '', NULL, '165');单个索引1、使用!= 或者 <> 导致索引失效SELECT * FROM `user` WHERE `name` != '冰峰';我们给name字段建立了索引,但是如果!= 或者 <> 这种都会导致索引失效,进行全表扫描,所以如果数据量大的话,谨慎使用可以通过分析SQL看到,type类型是ALL,扫描了10行数据,进行了全表扫描。<>也是同样的结果。2、类型不一致导致的索引失效在说这个之前,一定要说一下设计表字段的时候,千万、一定、必须要保持字段类型的一致性,啥意思?比如user表的id是int自增,到了用户的账户表user_id这个字段,一定、必须也是int类型,千万不要写成varchar、char什么的骚操作。SELECT * FROM `user` WHERE height= 175;这个SQL诸位一定要看清楚,height表字段类型是varchar,但是我查询的时候使用了数字类型,因为这个中间存在一个隐式的类型转换,所以就会导致索引失效,进行全表扫描。现在明白我为啥说设计字段的时候一定要保持类型的一致性了不,如果你不保证一致性,一个int一个varchar,在进行多表联合查询(eg: 1 = '1')必然走不了索引。遇到这样的表,里面有几千万数据,改又不能改,那种痛可能你们暂时还体会。少年们,切记,切记。3、函数导致的索引失效SELECT * FROM `user` WHERE DATE(create_time) = '2020-09-03';如果你的索引字段使用了索引,对不起,他是真的不走索引的。4、运算符导致的索引失效SELECT * FROM `user` WHERE age - 1 = 20;如果你对列进行了(+,-,*,/,!), 那么都将不会走索引。5、OR引起的索引失效SELECT * FROM `user` WHERE `name` = '张三' OR height = '175';OR导致索引是在特定情况下的,并不是所有的OR都是使索引失效,如果OR连接的是同一个字段,那么索引不会失效,反之索引失效。6、模糊搜索导致的索引失效SELECT * FROM `user` WHERE `name` LIKE '%冰';这个我相信大家都明白,模糊搜索如果你前缀也进行模糊搜索,那么不会走索引。7、NOT IN、NOT EXISTS导致索引失效SELECT s.* FROM `user` s WHERE NOT EXISTS (SELECT * FROM `user` u WHERE u.name = s.`name` AND u.`name` = '冰峰')SELECT * FROM `user` WHERE `name` NOT IN ('冰峰');这两种用法,也将使索引失效。但是NOT IN 还是走索引的,千万不要误解为 IN 全部是不走索引的。我之前就有误解(丢人了...)。8、IS NULL不走索引,IS NOT NULL走索引SELECT * FROM `user` WHERE address IS NULL不走索引。SELECT * FROM `user` WHERE address IS NOT NULL;走索引。根据这个情况,建议大家这设计字段的时候,如果没有必要的要求必须为NULL,那么最好给个默认值空字符串,这可以解决很多后续的麻烦(有深刻的体验<体验=教训>)。复合索引1、最左匹配原则EXPLAIN SELECT * FROM `user` WHERE sex = '男';EXPLAIN SELECT * FROM `user` WHERE name = '冰峰' AND sex = '男';测试之前,删除其他的单列索引。啥叫最左匹配原则,就是对于符合索引来说,它的一个索引的顺序是从左往右依次进行比较的,像第二个查询语句,name走索引,接下来回去找age,结果条件中没有age那么后面的sex也将不走索引。注意:SELECT * FROM `user` WHERE sex = '男' AND age = 22 AND `name` = '冰峰';可能有些搬砖工可能跟我最开始有个误解,我们的索引顺序明明是name、sex、age,你现在的查询顺序是sex、age、name,这肯定不走索引啊,你要是自己没测试过,也有这种不成熟的想法,那跟我一样还是太年轻了,它其实跟顺序是没有任何关系的,因为mysql的底层会帮我们做一个优化,它会把你的SQL优化为它认为一个效率最高的样子进行执行。所以千万不要有这种误解。2、如果使用了!=会导致后面的索引全部失效SELECT * FROM `user` WHERE sex = '男' AND `name` != '冰峰' AND age = 22;我们在name字段使用了 != ,由于name字段是最左边的一个字段,根据最左匹配原则,如果name不走索引,后面的字段也将不走索引。关于符合索引导致索引失效的情况能说的目前就这两种,其实我觉得对于符合索引来说,重要的是如何建立高效的索引,千万不能说我用到那个字段我就去建立一个单独的索引,不是就可以全局用了嘛。这样是可以,但是这样并没有符合索引高效,所以为了成为高级的搬砖工,我们还是要继续学习,如何创建高效的索引。
23
2020-09
10 月 1 日起,GitHub 中的 master 将默认更改为 main
GitHub 官方表示,从今年 10 月 1 日起,在该平台上创建的所有新的源代码仓库将默认被命名为 "main",而不是原先的"master"。值得注意的是,现有的存储库不会受到此更改影响。早在今年 6 月份,受美国大规模的 “Black Lives Matter”运动影响,为了安抚愈演愈烈的民众情绪,GitHub 就宣布将替换掉 master 等术语,以避免联想奴隶制。现如今,在外界一些声音的催促下,这一举措则终于要正式落地了。除 GitHub 外,为了避免带有所谓的“种族歧视色彩”,许多科技巨头或知名软件也都调整了自己的业务和产品,以平息社会舆论。包括有:MySQL 宣布删除 master、黑名单白名单等术语;Linus Torvalds 通过了 Linux 中避免 master/slave 等术语的提案;还有 Twitter 、GitHub、微软、LinkedIn、Ansible、Splunk、OpenZFS、OpenSSL、JP Morgan、 Android 移动操作系统、Go 编程语言、PHPUnit 和 Curl 等宣布要对此类术语进行删除或更改。同时,IBM、亚马逊、微软也都接连调整面部识别平台业务,以防加深歧视或遭受指责。且最初在 Git 中写下“master”一词的开发者 Petr Baudis 也于 6 月份在社交网站上表明立场称,自己当年不该使用“master”这个可能给别人造成伤害的词语。并表示,他曾多次希望可以将“master”改成“main”(和“upstream”)。不过直到现在,才由 GitHub 开始主导替换工作。而对于为何选择“main”而不是其他替换词汇,Github 方面给出的解释为,main 是他们在平台上看到的最受欢迎的 master 替代品。并且 main 这个词汇很短,可以帮助用户形成良好的肌肉记忆;在很多种语言中翻译起来也都很容易。目前,该平台已将 main 用于新创建的仓库和正在迁移的仓库,例如 dependabot-core。不过,Github 也指出,对于现有的仓库来说,重命名默认分支的举措无可避免地会造成一些麻烦,譬如必须编辑拉请求的设置和修改安全策略等。此外,Github 还透露,截至今年年底,他们将使现有存储库无缝重命名其默认分支。当用户重命名分支机构时,他们将重新定位打开的 PR 和草稿版本、移动分支机构保护策略等,且所有的这些都将自动完成。事实上,计算机术语政治正确性早已不是新鲜话题。2004 年,“master/slave”曾被全球语言检测机构评为年度最不政治正确的十大词汇之一,时任主席称这是政治渗透到计算机技术控制中的表现。2008 年,开源软件 Drupal 在社区发布消息,高调站队,将“master/slave”重命名为“client/server”。2018年,IETF 也在草案当中指出,要求开源软件更改“master/slave”和“blacklist/whitelist”两项表述。然而在一些开发者呼吁一些开源软件厂商修改源码,清除此类词汇的同时,也有很多人持反对意见。其中最突出的两个理由则是:计算机源码中的“master、blacklist”等词语并不包含歧视情绪;和更改的成本不低。 可以理解的是,倡议者们想表达的是,并不是不能使用“黑”这个词,而是希望不要把“黑”作为“白”的对立面,表达“不好”、“坏”、“需要被限制”等负面意思。同理,当“master、slave”出现在源码中,并且表达的“主-从”关系,这会让一些人联想到奴隶制。但是值得思考的是,在计算机源码领域中,“master/slave”和“blacklist/whitelist”之类的技术用语有错吗?一味的“一刀切”的话,会不会导致所谓的矫枉过正呢?转载自:https://www.oschina.net/news/118751/github-to-replace-master-with-main
23
2020-09
Oracle JDK 15安装及新特性介绍
JDK 15已经于2020年9月15日如期发布。本文介绍JDK 15新特性。发布版本说明根据发布的规划,这次发布的 JDK 15 将是一个短期的过度版,只会被 Oracle 支持(维护)6 个月,直到明年 3 月的 JDK 16 发布此版本将停止维护。而 Oracle 下一个长期支持版(LTS 版)会在明年的 9 月份候发布(Java 17),LTS 版每 3 年发布一个,上一次长期支持版是 18 年 9 月发布的 JDK 11。 下图展示了各个版本的发布历史。安装包下载主要分为OpenJDK版本和Oracle版本,下载地址如下:OpenJDK版本:https://jdk.java.net/15/Oracle版本:http://www.oracle.com/technetwork/java/javase/downloads/index.html上述版本,如果是个人学习用途,则差异不大。但如果是用于商业用途,则需要仔细看好相关的授权。Oracle JDK根据二进制代码许可协议获得许可,而OpenJDK根据GPL v2许可获得许可。安装、验证本例子以OpenJDK版本为例。解压安装包openjdk-15_windows-x64_bin.zip到任意位置。设置系统环境变量“JAVA_HOME”,如下图所示。在用户变量“Path”中,增加“%JAVA_HOME%\bin”。安装完成后,执行下面命令进行验证:>java -version openjdk version "15" 2020-09-15 OpenJDK Runtime Environment (build 15+36-1562) OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)更多有关Java的基本知识,可以参阅《Java核心编程》这本书,描述的非常详细。JDK 15 新特性说明JDK 15 为用户提供了14项主要的增强/更改,包括一个孵化器模块,三个预览功能,两个不推荐使用的功能以及两个删除功能。1. EdDSA 数字签名算法新加入 Edwards-Curve 数字签名算法(EdDSA)实现加密签名。在许多其它加密库(如 OpenSSL 和 BoringSSL)中得到支持。与 JDK 中的现有签名方案相比,EdDSA 具有更高的安全性和性能。这是一个新的功能。使用示例如下:// example: generate a key pair and sign KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519"); KeyPair kp = kpg.generateKeyPair(); // algorithm is pure Ed25519 Signature sig = Signature.getInstance("Ed25519"); sig.initSign(kp.getPrivate()); sig.update(msg); byte[] s = sig.sign(); // example: use KeyFactory to contruct a public key KeyFactory kf = KeyFactory.getInstance("EdDSA"); boolean xOdd = ... BigInteger y = ... NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519"); EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec, new EdPoint(xOdd, y)); PublicKey pubKey = kf.generatePublic(pubSpec);有关EdDSA 数字签名算法的详细内容见RFC 8032规范。2. 封闭类(预览特性)可以是封闭类和或者封闭接口,用来增强 Java 编程语言,防止其他类或接口扩展或实现它们。有了这个特性,意味着以后不是你想继承就继承,想实现就实现了,你得经过允许才行。示例如下:public abstract sealed class Student permits ZhangSan, LiSi, ZhaoLiu { ... }类 Student 被 sealed 修饰,说明它是一个封闭类,并且只允许指定的 3 个子类继承。3. 隐藏类此功能可帮助需要在运行时生成类的框架。框架生成类需要动态扩展其行为,但是又希望限制对这些类的访问。隐藏类很有用,因为它们只能通过反射访问,而不能从普通字节码访问。此外,隐藏类可以独立于其他类加载,这可以减少框架的内存占用。这是一个新的功能。4. 移除了 Nashorn JavaScript 脚本引擎移除了 Nashorn JavaScript 脚本引擎、APIs,以及 jjs 工具。这些早在 JDK 11 中就已经被标记为 deprecated 了,JDK 15 被移除就很正常了。Nashorn 是 JDK 1.8 引入的一个 JavaScript 脚本引擎,用来取代 Rhino 脚本引擎。Nashorn 是 ECMAScript-262 5.1 的完整实现,增强了 Java 和 JavaScript 的兼容性,并且大大提升了性能。那么为什么要移除?官方的解释是主要的:随着 ECMAScript 脚本语言的结构、API 的改编速度越来越快,维护 Nashorn 太有挑战性了,所以……。5. 重新实现 DatagramSocket API重新实现旧版 DatagramSocket API,更简单、更现代的实现来代替java.net.DatagramSocket和java.net.MulticastSocketAPI 的基础实现,提高了 JDK 的可维护性和稳定性。新的底层实现将很容易使用虚拟线程,目前正在 Loom 项目中进行探索。这也是 JEP 353 的后续更新版本,JEP 353 已经重新实现了 Socket API。6. 准备禁用和废除偏向锁在 JDK 15 中,默认情况下禁用偏向锁(Biased Locking),并弃用所有相关的命令行选项。后面再确定是否需要继续支持偏向锁,国为维护这种锁同步优化的成本太高了。7. 模式匹配(第二次预览)第一次预览是 JDK 14 中提出来的,点击这里查看我之前写的详细教程。Java 14 之前用法:if (obj instanceof String) { String s = (String) obj; // 使用s }Java 14之后的用法:if (obj instanceof String s) { // 使用s }Java 15 并没有对此特性进行调整,继续预览特性,只是为了收集更多的用户反馈,可能还不成熟吧。8. ZGC 功能转正ZGC是一个可伸缩、低延迟的垃圾回收器。ZGC 已由JEP 333集成到JDK 11 中,其目标是通过减少 GC 停顿时间来提高性能。借助 JEP 377,JDK 15 将 ZGC 垃圾收集器从预览特性变更为正式特性而已,没错,转正了。这个 JEP 不会更改默认的 GC,默认仍然是 G1。9. 文本块功能转正文本块,是一个多行字符串,它可以避免使用大多数转义符号,自动以可预测的方式格式化字符串,并让开发人员在需要时可以控制格式。文本块最早准备在 JDK 12 添加的,但最终撤消了,然后在 JDK 13 中作为预览特性进行了添加,然后又在 JDK 14 中再次预览,在 JDK 15 中,文本块终于转正,暂不再做进一步的更改。Java 13 之前用法,使用one-dimensional的字符串语法:String html = "<html>\n" + " <body>\n" + " <p>Hello, world</p>\n" + " </body>\n" + "</html>\n";Java 13 之后用法,使用two-dimensional文本块语法:String html = """ <html> <body> <p>Hello, world</p> </body> </html> """;10. Shenandoah 垃圾回收算法转正Shenandoah 垃圾回收从实验特性变为产品特性。这是一个从 JDK 12 引入的回收算法,该算法通过与正在运行的 Java 线程同时进行疏散工作来减少 GC 暂停时间。Shenandoah 的暂停时间与堆大小无关,无论堆栈是 200 MB 还是 200 GB,都具有相同的一致暂停时间。JDK 15 Shenandoah垃圾收集器从预览特性变更为正式特性而已,没错,又是转正了。11. 移除了 Solaris 和 SPARC 端口。移除了 Solaris/SPARC、Solaris/x64 和 Linux/SPARC 端口的源代码及构建支持。这些端口在 JDK 14 中就已经被标记为 deprecated 了,JDK 15 被移除也不奇怪。12. 外部存储器访问 API(二次孵化)这个最早在 JDK 14 中成为孵化特性,JDK 15 继续二次孵化并对其 API 有了一些更新。目的是引入一个 API,以允许 Java 程序安全有效地访问 Java 堆之外的外部内存。这同样是 Java 14 的一个预览特性。13. Records Class(二次预览)Records Class 也是第二次出现的预览功能,它在 JDK 14 中也出现过一次了,使用 Record 可以更方便的创建一个常量类,使用的前后代码对比如下。旧写法:class Point { private final int x; private final int y; Point(int x, int y) { this.x = x; this.y = y; } int x() { return x; } int y() { return y; } public boolean equals(Object o) { if (!(o instanceof Point)) return false; Point other = (Point) o; return other.x == x && other.y = y; } public int hashCode() { return Objects.hash(x, y); } public String toString() { return String.format("Point[x=%d, y=%d]", x, y); } }新写法:record Point(int x, int y) { }也就是说在使用了 record 之后,就可以用一行代码编写出一个常量类,并且这个常量类还包含了构造方法、toString()、equals() 和 hashCode() 等方法。14. 废除 RMI 激活废除 RMI 激活,以便在将来进行删除。需要说明的是,RMI 激活是 RMI 中一个过时的组件,自 Java 8 以来一直是可选的。参考引用本文同步至: https://waylau.com/jdk-15-released/https://jdk.java.net/15/release-noteshttps://openjdk.java.net/projects/jdk/15/https://openjdk.java.net/projects/jdk/15/spec/《Java核心编程》https://github.com/waylau/modern-java-demos转载自:https://my.oschina.net/waylau/blog/4633203
23
2020-09
Redis 发布订阅,小功能大用处,真没那么废材!
今天小黑哥来跟大家介绍一下 Redis 发布/订阅功能。也许有的小伙伴对这个功能比较陌生,不太清楚这个功能是干什么的,没关系小黑哥先来举个例子。假设我们有这么一个业务场景,在网站下单支付以后,需要通知库存服务进行发货处理。上面业务实现不难,我们只要让库存服务提供给相关的给口,下单支付之后只要调用库存服务即可。后面如果又有新的业务,比如说积分服务,他需要获取下单支付的结果,然后增加用户的积分。这个实现也不难,让积分服务同样提供一个接口,下单支付之后只要调用库存服务即可。如果就两个业务需要获取下单支付的结果,那也还好,程序改造也快。可是随着业务不断的发展,越来越多的新业务说是要下单支付的结果。这时我们会发现上面这样的系统架构存在很多问题:第一,下单支付业务与其他业务重度耦合,每当有个新业务需要支付结果,就需要改动下单支付的业务。第二,如果调用业务过多,会导致下单支付接口响应时间变长。另外,如果有任一下游接口响应变慢,就会同步导致下单支付接口响应也变长。第三,如果任一下游接口失败,可能导致数据不一致的情况。比如说下图,先调用 A,成功之后再调用 B,最后再调用 C。如果在调用 B 接口的发生异常,此时可能就导致下单支付接口返回失败,但是此时 A 接口其实已经调用成功,这就代表它内部已经处理下单支付成功的结果。这样就会导致 A,B,C 三个下游接口,A 获取成功获取支付结果,但是 B,C 没有拿到,导致三者系统数据不一致的情况。其实我们仔细想一下,对于下单支付业务来讲,它其实不需要关心下游调用结果,只要有某种机制通知能通知到他们就可以了。讲到这里,这就需要引入今天需要介绍发布订阅机制。Redis 发布与订阅Redis 提供了基于「发布/订阅」模式的消息机制,在这种模式下,消息发布者与订阅者不需要进行直接通信。如上图所示,消息发布者只需要想指定的频道发布消息,订阅该频道的每个客户端都可以接受到到这个消息。使用 Redis 发布订阅这种机制,对于上面业务,下单支付业务只需要向支付结果这个频道发送消息,其他下游业务订阅支付结果这个频道,就能收相应消息,然后做出业务处理即可。这样就可以解耦系统上下游之间调用关系。接下来我们来看下,我们来看下如何使用 Redis 发布订阅功能。Redis 中提供了一组命令,可以用于发布消息,订阅频道,取消订阅以及按照模式订阅。首先我们来看下如何发布一条消息,其实很简单只要使用 publish 指令:publish channel message上图中,我们使用 publish 指令向 pay_result 这个频道发送了一条消息。我们可以看到 redis 向我们返回 0 ,这其实代表当前订阅者个数,由于此时没有订阅,所以返回结果为 0 。接下来我们使用 subscribe 订阅一个或多个频道subscribe channel [channel ...]如上图所示,我们订阅 pay_result 这个频道,当有其他客户端往这个频道发送消息,当前订阅者就会收到消息。我们子在使用订阅命令,需要主要几点:第一,客户端执行订阅指令之后,就会进入订阅状态,之后就只能接收 subscribe、psubscribe、unsubscribe、punsubscribe 这四个命令。第二,新订阅的客户端,是无法收到这个频道之前的消息,这是因为 Redis 并不会对发布的消息持久化的。相比于很多专业 MQ,比如 kafka、rocketmq 来说, redis 发布订阅功能就显得有点简陋了。不过 redis 发布订阅功能胜在简单,如果当前场景可以容忍这些缺点,还是可以选择使用的。除了上面的功能以外的,Redis 还支持模式匹配的订阅方式。简单来说,客户端可以订阅一个带 * 号的模式,如果某些频道的名字与这个模式匹配,那么当其他客户端发送给消息给这些频道时,订阅这个模式的客户端也将会到收到消息。使用 Redis 订阅模式,我们需要使用一个新的指令 psubscribe。我们执行下面这个指令:psubscribe pay.*那么一旦有其他客户端往 pay 开头的频道,比如 pay_result、pay_xxx,我们都可以收到消息。如果需要取消订阅模式,我们需要使用相应punsubscribe 指令,比如取消上面订阅的模式:punsubscribe pay.*Redis 客户端发布订阅使用方式基于 Jedis 开发发布/订阅聊完 Redis 发布订阅指令,我们来看下 Java Redis 客户端如何使用发布订阅。下面的例子主要基于 Jedis,maven 版本为:<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.1.0</version> </dependency>其他 Redis 客户端大同小异。jedis 发布代码比较简单,只需要调用 Jedis 类的 publish 方法。// 生产环境千万不要这么使用哦,推荐使用 JedisPool 线程池的方式 Jedis jedis = new Jedis("localhost", 6379); jedis.auth("xxxxx"); jedis.publish("pay_result", "hello world");订阅的代码就相对复杂了,我们需要继承 JedisPubSub 实现里面的相关方法,一旦有其他客户端往订阅的频道上发送消息,将会调用 JedisPubSub 相应的方法。private static class MyListener extends JedisPubSub { @Override public void onMessage(String channel, String message) { System.out.println("收到订阅频道:" + channel + " 消息:" + message); } @Override public void onPMessage(String pattern, String channel, String message) { System.out.println("收到具体订阅频道:" + channel + "订阅模式:" + pattern + " 消息:" + message); } }其次我们需要调用 Jedis 类的 subscribe 方法:Jedis jedis = new Jedis("localhost", 6379); jedis.auth("xxx"); jedis.subscribe(new MyListener(), "pay_result");当有其他客户端往 pay_result频道发送消息时,订阅将会收到消息。不过需要注意的是,jedis#subscribe 是一个阻塞方法,调用之后将会阻塞主线程的,所以如果需要在正式项目使用需要使用异步线程运行,这里就不演示具体的代码了。基于 Spring-Data-Redis 开发发布订阅原生 jedis 发布订阅操作,相对来说还是有点复杂。现在我们很多应用已经基于 SpringBoot 开发,使用 spring-boot-starter-data-redis ,可以简化发布订阅开发。首先我们需要引入相应的 startter 依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <artifactId>lettuce-core</artifactId> <groupId>io.lettuce</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>这里我们使用 Jedis 当做底层连接客户端,所以需要排除 lettuce,然后引入 Jedis 依赖。然后我们需要创建一个消息接收类,里面需要有方法消费消息:@Slf4j public class Receiver { private AtomicInteger counter = new AtomicInteger(); public void receiveMessage(String message) { log.info("Received <" + message + ">"); counter.incrementAndGet(); } public int getCount() { return counter.get(); } }接着我们只需要注入 Spring- Redis 相关 Bean,比如:StringRedisTemplate,用来操作 Redis 命令MessageListenerAdapter ,消息监听器,可以在这个类注入我们上面创建消息接受类 ReceiverRedisConnectionFactory, 创建 Redis 底层连接@Configuration public class MessageConfiguration { @Bean RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); // 订阅指定频道使用 ChannelTopic // 订阅模式使用 PatternTopic container.addMessageListener(listenerAdapter, new ChannelTopic("pay_result")); return container; } @Bean MessageListenerAdapter listenerAdapter(Receiver receiver) { // 注入 Receiver,指定类中的接受方法 return new MessageListenerAdapter(receiver, "receiveMessage"); } @Bean Receiver receiver() { return new Receiver(); } @Bean StringRedisTemplate template(RedisConnectionFactory connectionFactory) { return new StringRedisTemplate(connectionFactory); } }最后我们使用 StringRedisTemplate#convertAndSend 发送消息,同时 Receiver 将会收到一条消息。@SpringBootApplication public class MessagingRedisApplication { public static void main(String[] args) throws InterruptedException { ApplicationContext ctx = SpringApplication.run(MessagingRedisApplication.class, args); StringRedisTemplate template = ctx.getBean(StringRedisTemplate.class); Receiver receiver = ctx.getBean(Receiver.class); while (receiver.getCount() == 0) { template.convertAndSend("pay_result", "Hello from Redis!"); Thread.sleep(500L); } System.exit(0); } }Redis 发布订阅实际应用Redis Sentinel 节点发现Redis Sentinel 是 Redis 一套高可用方案,可以在主节点故障的时候,自动将从节点提升为主节点,从而转移故障。今天这里我们不详细解释 Redis Sentinel 详细原理,主要来看下 Redis Sentinel 如何使用发布订阅机制。Redis Sentinel 节点主要使用发布订阅机制,实现新节点的发现,以及交换主节点的之间的状态。如下所示,每一个 Sentinel 节点将会定时向 _sentinel_:hello 频道发送消息,并且每个 Sentinel 都会订阅这个节点。这样一旦有节点往这个频道发送消息,其他节点就可以立刻收到消息。这样一旦有的新节点加入,它往这个频道发送消息,其他节点收到之后,判断本地列表并没有这个节点,于是就可以当做新的节点加入本地节点列表。除此之外,每次往这个频道发送消息内容可以包含节点的状态信息,这样可以作为后面 Sentinel 领导者选举的依据。以上都是对于 Redis 服务端来讲,对于客户端来讲,我们也可以用到发布订阅机制。当 Redis Sentinel 进行主节点故障转移,这个过程各个阶段会通过发布订阅对外提供。对于我们客户端来讲,比较关心切换之后的主节点,这样我们及时切换主节点的连接(旧节点此时已故障,不能再接受操作指令),客户端可以订阅 +switch-master频道,一旦 Redis Sentinel 结束了对主节点的故障转移就会发布主节点的的消息。redission 分布式锁redission 开源框架提供一些便捷操作 Redis 的方法,其中比较出名的 redission 基于 Redis 的实现分布式锁。今天我们来看下 Redis 的实现分布式锁中如何使用 Redis 发布订阅机制,提高加锁的性能。PS:redission 分布式锁实现原理,可以参考之前写过的文章:可重入分布式锁的实现方式Redis 分布式锁,看似简单,其实真不简单首先我们来看下 redission 加锁的方法:Redisson redisson = .... RLock redissonLock = redisson.getLock("xxxx"); redissonLock.lock();RLock 继承自 Java 标准的 Lock 接口,调用 lock 方法,如果当前锁已被其他客户端获取,那么当前加锁的线程将会被阻塞,直到其他客户端释放这把锁。这里其实有个问题,当前阻塞的线程如何感知分布式锁已被释放呢?这里其实有两种实现方法:第一钟,定时查询分布时锁的状态,一旦查到锁已被释放(Redis 中不存在这个键值),那么就去加锁。实现伪码如下:while (true) { boolean result=lock(); if (!result) { Thread.sleep(N); } }这种方式实现起来起来简单,不过缺点也比较多。如果定时任务时间过短,将会导致查询次数过多,其实这些都是无效查询。如果定时任务休眠时间过长,那又会导致加锁时间过长,导致加锁性能不好。那么第二种实现方案,就是采用服务通知的机制,当分布式锁被释放之后,客户端可以收到锁释放的消息,然后第一时间再去加锁。这个服务通知的机制我们可以使用 Redis 发布订阅模式。当线程加锁失败之后,线程将会订阅 redisson_lock__channel_xxx(xx 代表锁的名称) 频道,使用异步线程监听消息,然后利用 Java 中 Semaphore 使当前线程进入阻塞。一旦其他客户端进行解锁,redission 就会往这个redisson_lock__channel_xxx 发送解锁消息。等异步线程收到消息,将会调用 Semaphore 释放信号量,从而让当前被阻塞的线程唤醒去加锁。ps:这里只是简单描述了 redission 加锁部分原理,出于篇幅,这里就不再消息解析源码。感兴趣的小伙伴可以自己看下 redission 加锁的源码。通过发布订阅机制,被阻塞的线程可以及时被唤醒,减少无效的空转的查询,有效的提高的加锁的效率。ps: 这种方式,性能确实提高,但是实现起来的复杂度也很高,这部分源码有点东西,快看晕了。总结今天我们主要介绍 Redis 发布订阅功能,主要对应的 Redis 命令为:subscribe channel [channel ...] 订阅一个或多个频道unsubscribe channel 退订指定频道publish channel message 发送消息psubscribe pattern 订阅指定模式punsubscribe pattern 退订指定模式我们可以利用 Redis 发布订阅功能,实现的简单 MQ 功能,实现上下游的解耦。不过需要注意了,由于 Redis 发布的消息不会被持久化,这就会导致新订阅的客户端将不会收到历史消息。所以,如果当前的业务场景不能容忍这些缺点,那还是用专业 MQ 吧。最后介绍了两个使用 Redis 发布订阅功能使用场景供大家参考。转载自:https://my.oschina.net/u/2526533/blog/4633262
23
2020-09
视频学习
全部|产品宣传|功能介绍|开发教程
论坛热帖
1
2
开发者社区