Web应用开发中的本地存储限额localStroage Quota

近几年互联网络的发展似乎进入一个瓶颈期。不独一时一地,全球趋势大致如此,大概处于传统互联网络已经日至成熟,而创新技术在萌芽期尚未获得突破之际。所以传统互联网络技术只好在细节上打磨、内卷(involution)和进化(evolution),以此来消磨革命性(revolution)和创新性(innovation)技术来临前的时光。Web应用(网页应用)的开发者中新推进的本地化静态存储locaStorage就是这方面的一个代表。

作为矢志不渝的目标之一,Web应用要在性能和体验上追赶并且超越本地应用,除了客户端机器本身性能和网络带宽的飞速提升,Web应用本身对数据的处理也是重要的革新和优化方向之一。将Web应用中的常用数据在客户端本地进行持久化静态存储是个不错的主意,目前多数浏览器已经支持,而且一经推出即获得好评和广泛应用部署。比如在我们最近的老项目 有福新闻(UfqiNews, https://ufqi.com/news ) 和新项目 有福金融(UfqiFina, https://ufqi.com/finance )中,都有用到网页对象 window.localStorage对象对用户常用的交互数据进行本地化储存。

处于谨慎安全与性能考虑,浏览器的本地存储localStore对象给与每个域名的空间是有限的,这个限制是理性的和平衡的。有了这个限制,在调用localStorage 进行本地存储操作时,就可能会触及限额超额的问题。目前多数浏览器默认的window.localStorage的限额为每个域名5MB的空间. 基本上节省点用,一些基本需求还是可以得到满足的。

这个节省点用,是个模糊的描述。最近我们在Web开发中就遇到了localStorage报错说超额失败了。这个错误信息大致描述为:

DOMException: Failed to execute ‘setItem’ on ‘Storage’: Setting the value of ‘A_USER_KET_NAME’ exceeded the quota.

至少有两个公开的Web应用中存在很容易触发类似问题进而影响正常功能使用的现象, 比如第三方共享/分享插件 ShareThis 和博客程序 WordPress 。引起window.localStorage的问题可能很多,ShareThis 和 WordPress报错也不一定是他们本身存储了太多的东西,但目前发现,他们由于localStorage问题影响了正常功能使用。

ShareThis 的Web页面的JavaScript程序对访问过的每一个页面无差别地将其写入本地存储localStorage,写入时Key为 st_shares_URL_WITH_PARAMETERS , 这很恐怖的导致 localStorage 存储尺寸飞速上升,如果访问页面地址很长,而用户又在一个domain站点里访问了很多页面,我们观测到当我们在 UfqiNews 访问2000+页面时,localStorage 存储ShareThis的数据已经开始报错超额了!😢

WordPress 的新版本在启用编辑器时,会调用localStoage,当操作localStorage读写失败时,编辑器就无法使用了。😰

上述读写本地存储localStorage问题表现在:
1) 同一个页面功能不该创建过多的Keys,比如ShareThis, 应该将所需本地化的数据统一放入一个 ShareThis_LocalStoage 主键Key下面,然后再是各个key/value 对,更加明确;
2)既然 localStorage有限额quota,那么在设计数据结构时和流转机制时,就应该以某种先进后出或者先进先出的形式,有定期更新和删除操作,不然再大的空间也有用完耗尽的一天;
3) 既然 localStorage有限额quota ,那么在设计读写操作程序时,就应该有错误处理机制,当遇到无法返回预期数据处理结果时,应该平滑地进行下面的操作,或读取远程服务器数据或操作磁盘文件系统,而不应该让程序因此而卡壳掉。

我们在 有福新闻UfqiNews和有福金融的开发中就遇到类似需求。当我们需要存储用户个性化的一些动态信息时,比如用户最近阅读的主题词或者用户查询的基金代码等。在没有window.localStorage对象之前,只能在服务器端存储,在服务器端上维持个庞大的用户浏览历史记录表,而且需要实时高并发的写入更新。这存储还可以,高并发的写入更新实在勉为其难。window.localStorage生逢其时,可以很好的满足这个需求,我们可以将用户的浏览历史数据,写入本地localStorage, 既是个性化、分布式的,也避免了实时高并发地写入更新服务器数据。

fig.1. 有福新闻UfqiNews全局热点和用户自选主题词列表
fig.2. 有福金融UfqiFina用户自选基金行情K线图查询历史列表

有鉴于在 本地存储中 ShareThis 和 WordPress上述问题的经验教训,我们在设计本地存储时,
1)遵循了一个功能点,设计为一个主键Key的方式,然后将相应的数据放入这个主键Key下面,以JSON等方式进行读写。
2)同时,考虑到本地读写的便捷性,我们还对历史数据进行排序和统计,让更高频地使用的主题词或条目排列在最前面,方便用户更快捷地命中信息。
3)基于这些统计数据,我们对所要存储的数据有了把握,可以控制要存储的数据条目数和所占用空间的尺寸。定期或者按需清除不需要的数据,从而节省有限的 window.localSroage空间,以图长久。
4)考虑到在服务器端无法感知的本地存储限额异常错误的发生,在运行时,如果读写本地存储异常时则跳过功能点继续其他页面流程,而不是卡壳程序。

在 内卷(involution)和进化(evolution)过程中,没有革命性(revolution)和创新性(innovation) ,我们能依靠的只有在细节上更精准地把握,才有可能产生新的突破(disruption),从而完成由量变到质变的飞跃(leap)。
JavaScript担当起Web应用开发的主角,还有很长的路要走,但前途是光明的,恰如数年前我们所预见的那样:JavaScript或成主导的编程语言( https://dl.ccf.org.cn/article/articleDetail.html?id=3738875941521408 )。 如果不去体验几乎难以想象,上面的fig.2.基金行情K线图是完全基于JavaScript的 HighChartStock 所绘制出来的。


ufqinews logo
有福新闻 UfqiNews

这里呈现热点全局, 尺寸间一览所有令人关注的疑点焦点;
这里表达条分缕析, 视野内一睹各个脉络清晰的故事主线.
有福新闻UfqiNews 带来全新的资讯阅览体验, 不信息过载, 亦不信息茧房.
在寻求最大社会共识和满足千人千面之间谋取平衡,
在满足广泛涉猎与追求术业专攻之间谋取平衡.
媒介插上人工智能的翅膀将如虎添翼, 与资讯比翼双飞.
新闻爱好者的良心之选, 匠心之作.

UfqiNews presents the hot spots globally, with all interesting points at a glance.
Information is organized here and there is a clear storyline within every single detail.
UfqiNews brings a brand new reading experience, no information overload and no information Cocoons,
In seeking a balance of the maximum social consensus and meeting thousands of people for each interest,
In achieving a balance between satisfying a wide range of hunting and pursuing specialization in the industry.
That media is being born with wings of the artificial intelligence will be even more powerful and the information will fly swifter than ever.
Better choices of newsreaders and the art of work from them.


有福金融UfqiFina

UfqiFina 是一个旨在促进财富稳步增长的工具平台。
UfqiFina is a platform of tools designed to promote wealth growth steadily.

发表在 UfqiNews有福新闻, 社会生活 | 标签为 , , , , , , , , , , | 一条评论

得力彩色铅笔油性彩铅学生用专业手绘48色水溶性彩铅笔24色画画笔36色小学生安全无毒水溶款绘画素描儿童铅笔医用外科口罩一次性医疗专用口罩防尘透气成人三层防护透气医生床上电脑桌大学生宿舍上铺懒人可折叠小桌子家用寝室简约学习书桌京东制作

山高水长师恩难忘

最近在读《Secret’s for Profiting in Bull and Bear Markets》这本书的原著。由于是每天几页的精读,所以读得速度很慢,留下很多思考和消化的时间。倒不是因为这本书有多么的高深或者艰涩难懂的理论,相反它是一本类似通俗读物的财经畅销书( https://ufqi.com/news/ulongpage.3377.html?tit=股票称傲牛市熊市的秘密 ),甚至读到过半会发现事情就那么些,多数是车轱辘话来回式地强调和反复。又念及这每一段文字下面都是美国华尔街股市无数散户的森森白骨,读得快了怕是阴气太重无法消散。在冬日正午的阳光下散步时去反思去揣摩去论证去质疑,渐次觉得或许这位“华尔街神童”作者(史丹·温斯坦/Stan Weinstein)真是一位好老师。由此及彼,这位老师让我回想起过去四十多年来每逢艰难困苦时,总是有天使般的老师向我施以援手,岁月悠悠这些师恩不曾随时光暗淡,反而逐渐光辉起来了——姜德臣老先生、杨光老师、杨洪波老师和尹耀廷教授

2022元旦,辞旧迎新之际,用追忆来开启新一年。

我成长在普通农村家庭,1980年代后中国改革开放已经蔚然成风,然而我的老家依然是牛耕人织原始版的耕农画面。生活依然主要吃食杂粮,过年过节才有白面馒头或者肉食饺子等大餐。记忆初期就是这样,在这种背景下,求学是很算是很奢侈的了,我记忆中小学1-3年级没怎么上,大概一年级没怎么读,二年级读一阵子,然后读三年级似乎正式起来。一方面是自己可能真显露一些“天分”,二则家人可能也没把学习当回事。

3-5年级持续好成绩,及至初一升读到本乡的一所中学,第一学期结束,依然是各种奖状的大赢家。这时候决定我命运的转折点来了,也就是我的第一位恩师,也是我的大爷——爷爷的哥哥——姜德臣老先生。他是位老师,我记事起他就退休了,门生很多,结交广泛。在我升读本乡初一那一年,他通过熟人介绍将我和他的小女儿(我的小姑姑)一同从本乡普通中学转入本县很有名气的另一所中学。择校这种事,那时候就开始有了。

大爷的确是恩师。记得小时候经常在某个清晨,我们家刚起床,大爷就步入院子先是对我的父母进行诸如“勤俭持家”“人勤春早”“家和万事兴”的教育,然后再对我进行诸如“诗书继世长”“人情练达即文章”“燕雀焉知鸿鹄之志”“大鹏展翅抟扶摇直上九千里”子类的教导。

多年之后回想这次择校,既有父母的极力推崇攀附,也有自己小时候显露出的好学聪慧的一面(夏日午后的晒麦场上讨论可不可以接胡耀邦赵紫阳的班),以至于得到老先生的法眼抬爱。当其时,我曾经有一阵子体验过“一目十行”“过目不忘”的技能,那种体验就是将一页书的内容类似拍照一样形成图片在脑海中,合上书本,若是背诵或者提问,会将脑海中的那本书翻到某一页,然后再查某一行,然后图片就再现了。

多年以后我逐渐完成了初中、高中、大学和研究生教育,老先生始终是我的启蒙恩师,他的教诲也时时闪现。及至后来我返乡时,他交托我一本小册子,是自己平日里读书或思考心得的“增广贤文”,嘱托我整理成册,择机印刊传于子孙后辈小生,敦敦教诲,殷殷期盼。

被转入本县好一点的一所初中后,我们需要离家住校,可是那所学校刚声名鹊起,基础设施里原本没有学生宿舍。好在有办法,对于一些不在学校居住的老师,将其办公室部分征用作为学生宿舍。老师办公和学生宿舍共用。颇具喜感的画面就是白天是老师们的办公室,放学后老师和学生一起在“办公室”里生火做饭(部分回家的老师不用)。

就这样我幸运的遇到了第二位恩师,也是我的室友——杨光老师。杨老师年龄偏长,我在他的办公室居住生活,他偶尔不回家吃饭,也在学校吃住,于是那么两间平房既是我的宿舍,也是杨老师的宿舍和办公室。杨光老师和蔼可亲,我生活和学习上都得到他莫大的帮助。可以想象一个十三四岁左右的男孩子要自己生火做饭。我从他那里学习得了手工赶制面条,一个人的分量,通常鸡蛋大小的面团就足够了。他偶尔做的豆腐特别馋人,我甚至在未经他许可的情况下偷偷地尝几口。
说起生火做饭比较有画面感的是,就是找柴火架起来烧火煮水。那时候稍微先进点的灶具是用煤油炉,赶上市面上没有煤油供应或者没钱买煤油的情况下,就去学校四野找干枯树枝回来烧火。生活总是有办法的,糊锅烧焦和吃食带霉点的馒头是经常的事,毕竟那时候虽然有电灯,但没电冰箱。
生活也是丰富多彩的,我甚至在老师们打麻将桌旁边酣然入睡。

总觉得那时候我的学习全靠另外一个“我”在进行,就是没有自我意思或者事后全回想不起来。懵懂少年?也就类似情况完成了初中学业,高中入学考试考入本县一中。
在一中校园起初是高一(1)班,后来文理科分班后转入6班。记得清楚居然是因为我们创办过《六班日报》。杨洪波老师是我的班主任老师,带语文课。高二下我经历了生命中最严重的伤害,被歹徒偷袭几乎没了性命。所幸大难不死,住院数周康复后无法再在校外居住,杨老师伸出援手。他的办公室后侧有一间可容纳一床的小屋,稍经收拾就留做我的宿舍,从此在校内安居下来,直至高三毕业。

当其时由于头部受伤严重,加上被偷袭的心理阴影,学习上似乎始终无法回归正常聪慧,事后回想起来那些事似乎像“断片”一样无法补全。在杨老师办公室居住的时间里,也结识了杨老师的小儿子向军哥。在他们父子悉心照料和关怀下,我余下的高中生活似乎都是幸福的。而且随着伤病痊愈,后面的事情越发记得清晰。
有一次清早,我在诵读古诗词如琵琶行或者出师表或者岳阳楼记之类的,杨老师听闻步出道,作为理科生为何天天背诵这些诗词?我不记得是由于杨老师带语文课,还是别的什么就读起来的感觉很好。我也不记得是否杨老师训教之后每早转做数理化习题集了。
感念师恩,当写到这一节时,我渐次觉得,如果选一组形容词来,没有比 山高水长 更好的词汇来概括这如父如兄般的温暖。那是雪中送炭,是久旱甘霖。事后推演,如果没有杨老师的帮助,我是否能走出伤病阴影,是否能完成高中,都是未知数。
在那个多事之秋还发生过很多事,在家庭、学习、身心各方困扰下,很多无厘头的事情在当时看来似乎都有注解。我曾经身无分文地从家乡蹭火车去大上海找黑社会,某个高架桥下待了一晚上也没看到电影里的情节,寻找无果再从上海返回家乡。失踪的这些天里,杨老师四处打探寻找我,甚至派人到乡下村上探问回否。

高中毕业离开家乡,起初的岁月里艰难挣扎。待生活稍微好转,重新联络到杨老师时,他已经八十多岁了,偶尔中秋或教师节寄去问候,或年节探望,多是匆匆来忙忙去。所幸某年回访家乡看望恩师,得杨老师手书《朱熹家训》小楷一幅,奉为至宝,装裱于厅堂,以此感念。

朱熹家训,安徽涡阳一中杨洪波老师书法小楷作品

初入京城学习那年我20岁。尹耀廷教授已经退休,之后返聘到学校任教,我们是实打实的亦师亦友的忘年交。入学之后,我似乎已经完全从高二那年的灾难中恢复了,学习也犹如神助,能感觉到是自我在进步。
世事难料,而且总在我还没准备好怎么应对时就发生了。经年连月的读书,我的家庭再也无法支持我越来越昂贵的学业开支了。大一的账目结清,大二就得欠款了,奖学金似乎是杯水车薪。欠款到大三开学,实在无法向当时的班主任交代,甚至一度被老师逼着搬出学生宿舍。当其时似乎不知道哪里来得勇气,我给时任副校长的尹耀廷教授写了一封信。信的内容已经有些模糊,大致是作为学生1-2年来,见闻学校的教与学的各种利弊,希望能改进如此如此。
或许是考虑到我的信,更可能是鉴于我各科考试的优异成绩单,尹教授批复同意我以勤工俭学的方式完成学业。这犹如激流涌浪中的一根救命绳索把我从溃败的边缘拉了回来。也许是尹教授的一丝善念,却挽救了一个青年的一生。如同姜老先生将我从本乡普通初中转入本县知名初中,杨洪波老师收留安置我到其办公室养伤学习,尹教授此举也是我人生的转折点之一。

那之后,我完成了大学学业,继续与尹教授共事。得益于他的青睐,当我提出进修和学习培训时,他总是热心帮助,耐心指导。我从科技英语逐渐转为软件开发工程师职业,兴趣使然,也有尹教授的助攻。及至多年后,我申请英国大学的计算机研究生课程时,咨询尹教授的意见,他欣然同意并愿意写教授推荐信于我。
再次退休后,尹教授热衷于编制音视频内容,我自然应然地担当其技术支持。这或许是有底气地说我们是亦师亦友的一对,如父如子的一双吧。
许是他初见我时的穷学生的模样,在此后的岁月中,虽然我的境况渐次好转,他仍不时地接济我。逢年过节我带孩子们去看望他,他总是以给孩子压岁钱的形式,塞好大的红包。

夏天他偶尔会说,这双凉鞋子我穿着不合适,不如送你吧;秋天他可能会提及,有一大块布料未裁动,不如你拿去做条裤子。
在异乡漂泊的岁月里,这点点滴滴无不都汇聚成涓涓细流,温暖着一个青年、一家人的心房。也许他当真将我视如己出,可能我还未准备好做侍奉。

近年尹教授年事日渐高升,及至前次通电话时,他已入住养老院,又由于疫情防控,探视多有不便,已恍然几年不见。电话里,尹教授知悉我已加入国民党,嘱咐我老房子里有黄埔同学会相关孙中山先生的纪念品。念及此,思忖中夹杂某种交托后事的悲凉,又及自己也年近知天命,止不住潸然泪下…

岁月如梭,时光荏苒,当我看到孩子们一天天长大,自己一天天变老的时候,姜德臣老先生、杨光老师、杨洪波老师和尹耀廷教授。我的这些恩师或已千古或已年事甚高,不理世事。他们的恩情,或欲报无门,惟谨遵教诲,每日三省吾身,于小家,于大国,殚精竭虑死而后已。

师恩如山高,高山仰止,仰之弥高;
师恩如水长,上善若水,下自成蹊。

发表在 社会生活 | 标签为 , , , , , | 2条评论

京东制作护发素发膜正品修复干枯免洗家用护发补水顺滑女士发热焗油膏捐助乌克兰Help Ukraine老板选址BossXuanzhi

两个实例解释清楚Java Annotations注解

Java Annotations注解和Java Comments注释,英文里差别很大,在中文中一字之差让人颇为费解。Java Comments注释是随着Java语言的诞生就有的,意义明确,简单易懂,就是在源代码中的解释信息,通常用在帮助代码编写人员理解代码。Java Annotations出现的较晚,在Java 1.5(Java 5)中才引入,目前(2022年)Java部署的主流是Java 8(Java 1.8),最新发行版是Java 11(Java 1.11).

但Java Annotations注解的意义和作用就显得有些费解,比如官方文档中:

Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools and libraries, which can in turn affect the semantics of the running program. Annotations can be read from source files, class files, or reflectively at run time.

Annotations注解不会直接影响程序的语义,但是他们确实影响工具和库处理程序的方式,进而影响运行中的程序的语义。Annotations注解可以从源文件 ,class文件,或者运行时的反射中读取。

随着“工具和库处理程序”的流行,比如在Java开发中逐渐流行的Spring全家桶等,处处可见的@RequestXXX 等表述,让后生Java Annotations逐渐走入开发者的视野。然则只言片语地要解释清楚Java Annotations注解是什么东西及怎么作用或者工作的,却非易事。

我们所主导的Java 开发框架GWA2 in Java(  https://ufqi.com/dev/gwa2/ )并没有过多地倚重Java Annotations注解,主要是在初期技术架构选型时,我们考察了使用Reflection等相关技术时,发现其性能会下降安全受影响。

Due to 1) issues of performance and security of java.lang.reflection, We do not use it as routing or dynamic module invoking at present.

鉴于Reflection相关技术存在性能和安全相关方面问题,GWA2 in Java 目前没有考虑将其作为路由和动态模块加载技术手段。

然而,Java开发业界似乎形成了一种站在“巨人肩膀”上搭积木式地的堆叠,第一个扣子歪了大家也就一顺溜地继续歪下去,所谓性能和安全问题都是可以克服的,不能解决的部分也是在可接受范围内,皆大欢喜。
近期我们接手了两个Java开发的二期项目,基于Java Spring全家桶,项目技术栈虽然是Java,但其所依赖的各种第三方组件达到令人眼花缭乱的地步(如下图)。

Java Spring 组件套系

既然Java Annotations如此重要,自然要弄清楚,然而当我们试图向一些工程师解释Java Annotations是什么、怎么样的时候,发现并不容易,一方面是Java语言本身的复杂性,另一方面是Annotations是上层应用,是可选项。经过一番搜索、思考和探究,我觉得如下两个实例可以清晰地解释Java Annotations注解的基本原理和应用实践,回答了Java Annotations注解是什么,为什么和怎么样的问题。

实例1. 用Java Annotations注解标记某个对象类、实例、方法和属性具有或不具有某个属性。

这个实例来自官网 ( https://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html )

e.g.1.1. SampleTestTag.java: 自定义一个Java Annotation注解对象,其中的 @Retention 和 @Target 是注解的注解,称之为“元注解”, 注解类的什么是在名称前加 “@” at符号

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SampleTestTag { 

}

e.g.1.2. FooBar.java: 创建一个对象类 FooBar , 使用上面创建的Java注解 SampleTestTag

public class FooBar {
    @SampleTestTag public static void m1() { }
    public static void m2() { }
    @SampleTestTag public static void m3() {
        throw new RuntimeException("Boom");
    }
    public static void m4() { }
    @SampleTestTag public static void m5() { }
    public static void m6() { }
    @SampleTestTag public static void m7() {
        throw new RuntimeException("Crash");
    }
    public static void m8() { }
}

e.g.1.3. FooBarTest.java: 创建一个测试程序,调用对象类 FooBar, 测试 SampleTestTag 的区分作用

import java.lang.reflect.*;

public class FooBarTest {
   public static void main(String[] args) throws Exception {
      int passed = 0, failed = 0;
      for (Method m : Class.forName(args[0]).getMethods()) {
         if (m.isAnnotationPresent(SampleTestTag.class)) {
            try {
               m.invoke(null);
               passed++;
            } catch (Throwable ex) {
               System.out.printf("Test %s failed: %s %n", m, ex.getCause());
               failed++;
            }
         }
      }
      System.out.printf("Passed: %d, Failed %d%n", passed, failed);
   }
}

该测试程序预期运行的结果大致为:

$ java FooBarTest FooBar
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom 
Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash 
Passed: 2, Failed 2

这个实例表示,通过自定义一个注解,可以将对象类的某些方法标记出来,核心方法是isAnnotationPresent。 这只是一个简单的实例,如果这个路子走得通,有这种简单的方法或范式,可以将对象类、实例、方法和属性进行分类,仿佛打开了潘多拉的盒子,赋予开发者无穷尽的分类能力。
当具备这种能力后,可以在运行时根据分类,让A类的运行,让B类的都熄火;在英国区运行A类,在北美运行B类;让VIP的运行A类,让非VIP的运行B类;让小孩看A类,让老人看B类,其他人看C类….

显然这种能力是强大的,但同时其复杂性也显现出来了,开发者看到的代码和最终运行时的代码可能高度的 不–一–致。 尽管代码都摆在哪里,但具体跑那些代码需要看运行时的状态。由此造成了可怕的所见非所得。

实例2. 用Java Annotations注解标记某个对象类、实例、方法和属性具有某个属性的某种赋值。

这是对实例1的进化和升级,如果实例1提供让某个对象具有或不具有,是与非的简单二元分类,则实例2将这种能力无限升级到让某个对象的拥有某个属性,而且这个属性的赋值可以千变万化。
大白话就是注解可以带参数了,这几乎要开一开脑洞才好理解,结合下面这个实例。( https://www.educba.com/java-annotations/ )

e.g.2.1. MagicianAnnotation.java: 自定义一个注解类,该注解带有两个参数,分别给了缺省默认值

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MagicianAnnotation {
    String Wizard() default "Reynolds";
    String House() default "Green";
}

e.g.2.2. MagicianObject.java: 创建一个对象类,使用MagicianAnnotation修饰,该对象类有一个方法 getString, getString也使用注解MagicianAnnotation进行修饰,且带有两个参数

@MagicianAnnotation
public class MagicianObject {
    @MagicianAnnotation(Wizard = "Harry Potter", House = "Red")
    public String getString()  {  return null; }
}

e.g.2.3. MagicianTest.java: 创建一个测试类,运行MagicianObject, 观测被MagicianAnnotation所修饰的情况

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;

public class MagicianObjectTest {
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		new MagicianObject();
		Class<Magician> magic = MagicianObject.class;
		readAnnotationOn(magic);
		Method method = magic.getMethod("getString", new Class[]{});
		readAnnotationOn(method);
	}
	
	static void readAnnotationOn(AnnotatedElement element){
		try{
			System.out.println("\n Find annotations on " + element.getClass().getName());
			Annotation[] annotations = element.getAnnotations();
			for (Annotation annotation : annotations){
				if (annotation instanceof MagicianAnnotation){
					MagicianAnnotation mData = (MagicianAnnotation) annotation;
					System.out.println("Wizard Name :" + mData.Wizard() + " , House Color :" + mData.House());
				}
			}
		} 
		catch (Exception e){
			e.printStackTrace();
		}
	}
}

该测试程序预期运行的结果大致为:

$ java MagicianObjectTest 
Find annotations on java.lang.Class
Wizard Name :Reynolds , House Color :Green

Find annotations on java.lang.reflect.Method
Wizard Name :Harry Potter , House Color :Red

实例2表明,引入了自定义的注解之后,在原代码层面的注解及其属性、赋值等,最终会在运行时分别读取到,依据这些设置、配置的属性及赋值,同样的代码可能会产生千变万化的运行结果。
这种魔法虽然赋予了开发者丰富的技能,却也为程序的追踪设置了陷阱,正如我们在各种Java开发手册( https://ufqi.com/news/ulongpage.1510.html?tit=Java開發手冊-阿里巴巴-嵩山版-3:常量定义 )中,都敦敦教诲不要在程序源代码中留置“魔法”。

【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。

所以,本意上理解,Java Annotation注解是对注释的丰富和扩展,它的边界不应该扩展到影响程序运行逻辑上去,注解的根本仍然是像内置的那几个作用那样(  @Override, @Deprecated, @SuppressWarnings等等 ),是帮助理解代码,而不是修改变更、路由导向程序业务逻辑本身。
从这个意义上来说,Java Spring全家桶等剑走偏锋似乎走得有些远了。

回到起初,希望这两个实例能够清楚的解释了Java Annotations注解的来龙去脉,如同菜刀可以切菜也可以杀人一样,注解可以帮助开发者理解代码,也可以通过Reflect反射技术对程序的运行逻辑进行修改变更、路由导向等等。理解了这些,再去看Java Spring框架中的 @RequestXXX 就会豁然开朗,不过尽管知道了基本原理,要弄清楚每一个自定义的注解的具体含义,还需要进一步地解读源代码或技术手册。

回到上面的技术栈分析图,一个相对简单的Java Web应用CRM系统,如果基于GWA2 Java 进行构建,直接就是Tomcat跑起来一个应用即可。而如果看上面的供应链,就显得复杂无比,各种封装和嵌套多达4-5层,而每一层嵌套又引入更多的第三方组件,由是一个简单的应用变得复杂无比。在其成本上升的同时,其开发门槛也显得高高在上了,几乎没有一个人能够全部掌握所涉及的全部组件。对每一个组件不说去拆解分析源代码,恐怕操作使用说明文档能看完整了也非易事。

供应链越长,质量越难控制。
在立即可用小步快跑快速迭代风行的时期,可能关乎质量、性能、安全等皆居于次要地位。


GWA2

-GWA2 吉娃兔 是”通用网络应用架构( General Web Application Architeture, https://ufqi.com/dev/gwa2/ )”,基于 -GWA2 可以轻便构建各种网络应用程序,
包括复杂的在线购物商城、在线医疗、在线教育、 旅游交易平台、社群或者社交网站和新闻资讯网站等,
也包括各种企事业单位网上门户,在线交互及服务作业系统等.
还可以包括为NativeApp做服务器端支持, 甚至是WebApp的全部.
-GWA2 是为数不多的支持跨开发语言的应用框架,目前支持 -Java, -PHP, -Perl, -Aspx and -Python .

-GWA2 is a “General Web Application Architecture” and based on -GWA2 developers can easily build a variety of network applications,
including complex online shopping malls, online medical services, online teaching, travel trading platforms, community or social networking sites and news information sites, etc.
Also the applications include various online portals of enterprises and institutions, online interaction and service operations systems.
Moreover it contains server-side support for Native App, or even all of the WebApp.
-GWA2 is one of the web frameworks which provide cross-language support for -Java, -PHP, -Perl, -Aspx and -Python at present.

-GWA2 is E.A.S.Y 
Easy Along, Swift Yield
轻松启动, 快速产出.

发表在 -GWA2, 编程技术, 计算机技术 | 标签为 , , , | 一条评论

床上电脑桌大学生宿舍上铺懒人可折叠小桌子家用寝室简约学习书桌京东制作护发素发膜正品修复干枯免洗家用护发补水顺滑女士发热焗油膏捐助乌克兰Help Ukraine

工程技术思维论

可能我们都听过这样的戏言:“我代表人民现在就枪毙你!”
这句戏言如果再展开,大致雷同于下面的对话。

对话1
法官:原告你被判定有罪,处罚有期徒刑1234.5年。
原告:冤枉啊,法官先生,我犯了什么法?
法官:请查阅《中华人民共和国民法典》相关法律条文。
原告:😡🤬

对话2
某官网:网友你的文章已被删除,你已经被禁言1234.5年。
网民:冤枉啊,管理员,我什么地方触犯了你们的什么纪律?
某官网:请参考《某官网社区管理条例》相关内容。
网民:WQNMLGB. 👎 👎 

如果你对上面两个对话感到不适甚至愤怒,恭喜说明你已经具备了工程技术思维,懂基本逻辑,试图通过讲理与这个社会对话。尽管上面的对话1可能不常见,而对话2却是活生生的现实,网友朋友们或经常这么样被粗暴对待。
比如,你的贴子或发言,莫名其妙地被删除,你被禁言,被禁止登录等,尽管这对你来说是社区形象的生死攸关的大事,而管理员一方只是轻描淡写的废话一句,让你查阅社区管理条例。
不仅仅是网友在网络社区被各种无理由的粗暴对待,在社会生活中,这种例子也时常有发生。比如我们前几天购置了一台松不上的破壁机,收货之后,就开始各种安装调试,先是底座底部的螺丝松动,死活无法将上部与下部稳固合体。发挥理工男的优势,几经分析,上下部稳固合体后,接通电源,所有的操作选项,到“启动/确认”之后,都是”滴-滴-”两声的报错声音,再无其他任何参考信息。可以设想,这款破壁机产品经理的脑路是这样的对话.

对话3
破壁机: “滴-滴-” ,发生错误,无法继续执行指令。
用户:啥错误?
破壁机:请参考操作手册。
用户:操作手册上也只说“滴-滴-”是发生错误,啥错误?
破壁机:请参考操作手册。
用户: 😡🤬

还不死心的我们,就接通了这款破壁机的客服电话,客服人员听完我们的描述后,首次启用就“滴-滴-” 报错,居然也一脸茫然,让我们送过去检修。检修是无法接受的,我们选择退货。
后来还是在退货时联系经销商的客服之后,才了解到盖子的扣卡方向反了,尽管也是密室扣合,但方向相反,传感器无法探知是盖上盖子了。

这又是一例明显的设计缺陷或者不具备工程技术思维的案例。其一,在设计上,如果这是检测的关键链条之一,如果反向扣,应该阻止用户扣不上,这样就不会发生扣上了也无法启动机器的情况。其二,在软件编程上,如果探测到是盖子卡扣的方向反了,应该给出具体的错误代码在操作屏幕上,并且在操作手册上列出对应错误代码的解释和解决办法。

这些分析让我想起那些设计优良的产品来,比如IT产业了很多硬件设计,通常不分正反插上都可以用,比如USB Type-C或电源插板插头等,如果区分了正反,插错了不能用的话,就禁止用户将错误的一方插入,迫使用户不能“犯错”,如网线接口,内存条接口,普通USB接口等等。
在软件编程上,这方面做得更好,出错信息大多会具体到某个文件某一行第几个字符上面,让人一目了然。也许真是在这种优良设计的“养尊处优”习惯了,当理工男碰上硬件设计缺陷加上粗暴的软件编程设计时,愤怒到退货、差评是必然的。

为了更好地标记提示、异常和错误信息,在 GWA2吉娃兔 ( https://ufqi.com/dev/gwa2 ) 中我们特意创设了以可读的时间戳作为日志标记的做法。具体行为是,在需要标注的地方,我们针对各种提示、异常和错误信息,分别给予一个特定的时间戳字符串常量,用以全局唯一地记录当前位置、语境。
当运行时环境发生变化,触发了相应提示、异常和错误时,GWA2吉娃兔在抛出相应错误信息的同时,也会同时给出相应的时间戳标记,凭借着这个全局唯一的标记,程序开发人员可以快速地定位到警报位置、语境,从而轻易地排解问题。

这就是工程技术思维的设计实例。受这样的理工思维影响,在处理社会层面时,就会产生误解、冲突和愤怒。在对话1中,良善的做法通常都会给出嫌疑犯所触犯的具体法条的详情,如根据某某法某某条某某款,这样让被告认罪受罚、心服口服。
如此才是法治、善治,是王权、宪法。

相反地,如果无法给出具体的法律条款或者规则等,则是人治、恶法或者欺凌、霸权。语焉不详地处罚,通常都包藏祸心、居心不良。其一,是对被处罚者的不尊重,不履行认真告知的义务,就是觉得不告诉你,不跟你“废话”,你能咋地?
其二,真有难言之隐,无法明确地告知被处罚者。可能这个处罚的事由根本不在任何已经生效的法律法规的任何条款内,可是出于其他不可告人的原因,还是要处罚一下,于是就利用这个大帽子来夹带私货,公器私用,公报私仇。

所以,任何以用户为中心的产品设计、工程实现,都需要具备工程技术思维,不要逼迫用户“造反”。

反例之一:粗暴对待用户

无论是其一、还是其二,这些语焉不详地处罚,都是不可接受的,作为普通用户,除了用脚投票之外,似乎别无选择。
如果有了具体的依据条款,还可以发起抗辩,针对类似“我代表人民就枪毙你”的处罚,你是无法做任何抗辩的。
稍微具备工程技术思维的人都应该明白,我们预期的社会准则是公开、公平和公正。非经法定程序不可以宣布人有罪,法无禁止即可行。套用到互联网络上,内容管理是正当的,经法定程序,公开、公平和公正的执法是必须的,毕竟互联网络不是法外之地。
然则,无法无天的执法在互联网络上是行不通的。如同前些年,谷歌搜索引擎撤离某大国大陆前曾经与管理当局协商过,极力争取留下来。
根据后面披露的相关信息,谷歌搜索( google.com )是同意进行内容审查的。因为谷歌搜索在美国、在全球其他地方都有对所索引的内容进行审查,应相关地方管理当局进行不合适内容的删除。比如儿童色情、暴力恐怖、毒品等反人类的相关内容,放之四海而皆准地对其进行审查和删除。

双方后续争论的焦点是进行审查和删除的操作方式。一方认为,内容审查和删除应该依法进行,根据公开、公平和公正的法定程序对搜索引擎所索引的内容进行审查和删除,审查和删除应该依据书面文书指令进行。
另一方则认为,内容审查和删除,不需要经法定程序,也不需要依书面文书进行。其对内容审查和删除的标准是没有标准,“我不喜欢的内容就是非法的,就得删除”。我想审查和删除相关内容时,不需要任何程序和文书,叫人来面训,或者打个电话要求审查和删除,你们就得执行相关命令,而且执行完了还不准记录下来,更不准公布出来。
这大概就是谷歌搜索不吃屎的故事的由来。

谷歌搜索不吃屎

用脚投票是一件好事,允许在貌合神离的状态下逐渐瓦解那些不讲理的邪恶暗黑势力。
如果这些能够帮助我们理解更多背后的故事,下次遇到语焉不详的处罚时,能够拒绝时则直接拒绝。不能拒绝时,要选择不配合,同时考虑替代方案。

你还要知道,你不孤单,很多人已经具备了工程技术思维。
作为产品设计或者软件设计,尤其需要具备工程技术思维,一不小心就会拒用户于千里之外。人类科技的发展,生产力的极大解放,社会逐渐处于一种供给过剩、有选择的市场之中。


-GWA2 吉娃兔 是”通用网络应用架构( General Web Application Architeture, https://ufqi.com/dev/gwa2/ )”,基于 -GWA2 可以轻便构建各种网络应用程序,
包括复杂的在线购物商城、在线医疗、在线教育、 旅游交易平台、社群或者社交网站和新闻资讯网站等,
也包括各种企事业单位网上门户,在线交互及服务作业系统等.
还可以包括为NativeApp做服务器端支持, 甚至是WebApp的全部.
-GWA2 是为数不多的支持跨开发语言的应用框架,目前支持 -Java, -PHP, -Perl, -Aspx and -Python .

-GWA2 is a “General Web Application Architecture” and based on -GWA2 developers can easily build a variety of network applications,
including complex online shopping malls, online medical services, online teaching, travel trading platforms, community or social networking sites and news information sites, etc.
Also the applications include various online portals of enterprises and institutions, online interaction and service operations systems.
Moreover it contains server-side support for Native App, or even all of the WebApp.
-GWA2 is one of the web frameworks which provide cross-language support for -Java, -PHP, -Perl, -Aspx and -Python at present.

-GWA2 is E.A.S.Y 
Easy Along, Swift Yield
轻松启动, 快速产出.

发表在 -GWA2, 社会生活, 编程技术 | 标签为 , , , | 一条评论

医用外科口罩一次性医疗专用口罩防尘透气成人三层防护透气医生床上电脑桌大学生宿舍上铺懒人可折叠小桌子家用寝室简约学习书桌京东制作护发素发膜正品修复干枯免洗家用护发补水顺滑女士发热焗油膏

国债与信用货币

新冠肺炎疫情逐渐趋缓,打了有效疫苗的群体逐渐恢复了社交,世界似乎距离“接着奏乐接着舞”的时间越来越近了。2019年底新冠肺炎疫情爆发,我们从去年初开始写经济学相关方面的内容,距今这篇,已经是第九篇了,此前的相关八篇请参考页面下面的链接。

这第九篇是对之前一篇的回应和深入,在 治大国若过小家——写写王朝兴衰更替背后的经济账 ( https://ufqi.com/blog/political-reform-country-vs-home/ ) 一篇中,我们总结分析认为,王朝的兴替,其背后都是经济账起作用,每一个朝代政体最后几乎跟一个个具体的小家庭一样,是被穷死的。

细品之后,我们可能会有这样的疑问:
1) 既然打仗或者干别的大事,都需要钱,作为掌握货币发行权的政府/王朝,可劲地印钱不就行了吗?
2) 实在无法自己印钱,或者有所忌惮不想自己印钱,也还有借债(国家债务)这个路子,不至于活活的人让尿被憋死了?

适逢美国股市在新冠肺炎疫情爆发以来,延续十多年的牛市继续一路高歌,而同期美国的国债也一路水涨船高似的,不断突破之前的限制,屡创新高。说股市创新高,那是经济繁荣或是好事,债台高筑,无论是搁哪儿似乎也不能说“预喜”。
最近读了 美国国债200年( https://ufqi.com/news/ulongpage.3494.html?tit=美国国债200年:有借有还,越借越多 ) 和 货币、信贷与债务的关系( https://ufqi.com/news/ulongpage.658.html?tit=从货币、信贷与债务看变化中的世界秩序-3 ), 似有所悟,尝试解读或者回答上面的疑问,王朝/政府在穷途末路之际,为何没多印钱或者通过借债的方式,继续作战直至胜利?

从货币、信贷与债务看变化中的世界秩序-3
货币类型及演化趋势

A. 先来看第一个问题,为何王朝/政府缺钱的时候,不自己可劲地印钱?

回答是,他们也想,但是不行,那么搞,只会以加速师的身份,死得更快。

原因是货币本身“财富”的符号,之所以有“印钱”或者撒钱的说法,是由于货币已经脱离了其本身原来的形象——黄金或之前其他贵金属如白银等。 当用黄金做货币时,就没法“印钱”。不但古代技术无法人造出黄金,人类进入21世纪,在技术高度发达的今天,实验室已经可以人工合成造黄金了。不过,不好的消息是,成本太高,人造1g黄金,可能需要花费2g黄金的成本。

货币由起初的黄金,转为”银票“——银号/银行的票证之后,才有了”印钱”的说法。可靠的做法是有一锭黄金,发行一张面值一锭黄金的银票;不可靠的做法可能就是偷偷多发行几张。只是这作假太危险了,一旦被戳破造假,开弓没有回头箭,几乎只有作死一条路。背后是银票持有人一旦获知这个消息,会迅速整齐一致地要求将银票换回黄金,从而形成“挤兑”。

上述还是“金本位”时代的逻辑。货币的进一步发展,尤其是二战以来,世界承平日久,货币的发行已经不需要黄金了。那靠什么发行货币?回答是现代货币发现靠的信誉,或者信用。
一国的老大说,你看我们靠谱吗? 靠谱,就用我印的纸币当黄金用,你啥时候拿我印的钱回来找我换黄金,我都会给你。
至于这国他有没有黄金,有多少黄金,能不能给你兑换,够不够给你兑换,自行判断。
这就是信用货币。

这种趋势也可能是人类历史发展的必然。黄金作为货币,好是好,只是其出产量(发行量)不一定能满足经济社会发展需求,经济学规律大都强调个货币供应量要与经济发展速度相匹配。发行过多了是通货膨胀、货币贬值,发行少了也会阻碍进一步发展,是通货紧缩、经济衰退。数百年来,黄金作为无法人造的贵金属,它的出产量的确难以与全球、一国、一地区的经济发展速度完美匹配。( 从这里看,总量恒定的去中心化数字货币比特币是不是天生地与经济社会发展不匹配? https://ufqi.com/news/ulongpage.2781.html?tit=投资比特币缺乏令人信服的理由 )

既然有这样的经济学规律制约,黄金不适合作为货币,信用货币的出现就是应运而生了。同样是受制于经济发展,强调货币发行量与经济发展水平想匹配,掌握货币发行权的政府/王朝,不能没钱了就多加印,这是金科玉律般的红线

所以政府/王朝末年陷入战争的泥潭,本就没有了正常和平年份的经济发展,本该是紧缩银根,减少货币发行量,这时候反其道而行之,不是加速作死吗?

B. 借债的疑问,王朝/政府为何不借债打仗?

战争是最耗费金钱/财富的,况且牺牲的每一个生命都是无价的。

然而对于不幸陷入战争的政府和王朝来说,不管是主动还是被动,必须迎战,必须花钱。
先打家底,国库充盈家底厚的可以自己扛几年,但也不一定够用,再厚的家底总会很快打完,又不能印钱,靠什么扛?
有路子!可以借债,国家向人借钱,比较冠冕堂皇,叫做发行国债。

国家债券发出来之后,得有人买才行。有人买了一国的债券,打仗所需的钱粮才有可支付的银两。国家债券的发行对象可以本国的机构、国民或者外国政府、机构和个人等。
在 美国国债200年 文章里,讲述了美国从开始就是靠借钱举债进行的独立革命,国内南北战争,然后进行建国,然后进行社会发展,一次次发行国债,每次都有人买账,真金白银地支持他们拿钱去打仗、去建设、搞开发。

有的王朝/政府,却没有那么幸运,其所发行的国债可能少有人问津,无人愿意雪中送炭地为他们续命。这就会归因到一个核心问题,举债借钱的核心是什么? 凭什么有的人能借到钱,有的人借不到钱?
事情再一次地归根到信用上了。有借有还,再借不难。
诚实守信吗?别搞撒谎欺骗。
有契约精神吗?别翻脸不认账

通常地民间借贷,还有个抵押物,国家债券,似乎少有抵押,多是纯信用借钱,而且一般利息还不高。

很难想象,一个威权政府/王朝一向高高在上的模样,会低眉顺眼地弯腰下来跟金主商量:“大爷能不能借钱江湖救急一下?小爷我摊上事了…”
这么样的“霸王”首先想到是可能是“加税”,后面说。
也很难想象,一个国家内外全是敌人的政府/王朝能借到钱。
相反地,公开公平正义的政府/王朝,即便是很弱小,诚实守信、有锲约精神,也一定能借到钱,甚至是更多的相同价值观的支援,无论是国内民众,还是国际上的友邦。

小结一下,发行国债是战时一国政府/王朝的上好选择,可以团结和凝聚一切有生力量,一旦让对方成为债主,就算是一条线的上蚂蚱了。
同时,战时发行国债,也是一块试金石,如果发行国债成功,说明一切还有希望,如果发行国债失败,束手就擒吧。
少些国成功了,一路有借有还的举债借钱,发展壮大,比如美国。
很多国失败了,所发国债国内外鲜有人问津,从此灰飞烟灭,比如大清国。( 参考 https://ufqi.com/news/ulongpage.3497.html?tit=权力恒久远,制衡永流传-2 )

C. 加税的疑问, 王朝/政府可以靠加税来继续打仗吗?

回答是这比政府/王朝自己印钱死得更快。
打仗本来是靠人头+银两,加税相当于把人头再减少点,同时加大内讧在内部再多招惹一些被迫起义的“敌人”。


总结,基于经济发展规律,旧有的王朝/政体的更迭总是伴随着垂死挣扎与负隅顽抗,和平的权力交接与政权更替是一国的福祉所在,也是全人类文明进步孜孜以求的向往与理想。
归属到本质上,是当权者在位时不作恶,事后不怕清算,自然可以放心交出权力于他人。
相反,统治者一朝大权在握,则无所畏惧,肆意践踏对手,背信弃义,自然会担心下野后必然遭到对手的穷追猛打,如此往复循环,仇恨怨冤相报,永无宁日。这实在是一国地狱模式。
更深层次的是在人的本性上,对自然的认知,对生命的敬畏,对弱者的怜悯。
所幸,经过漫长的成长与进化,一些国家政权已经找到和平权力更替模式,如英联邦,日本国,美利坚等。我巍巍中华,江湖的开万世太平的传说不应该是绝唱。


“货币,就相当于是政府发行的信用债!
朝气勃发勇于进取的帝国精神,才是英镑真正的信用之源,而不是英国皇家国库的那点子库存黄金。”
— 许小年教授( https://ufqi.com/news/ulongpage.105.html?tit=两脚羊殇歌——2017,开不动的印钞机 )

—-

这是温习经济学著作的第九篇习作,之前的各篇附列如下。

  1. 理性经济人在边际上做选择, https://ufqi.com/blog/economic-selection-on-margin/
  2. 写写年度收益率年均收益率和年化收益率-4, https://ufqi.com/blog/income-rate-annuals-with-buffett/
  3. 写写年度收益率年均收益率和年化收益率-3, 
    https://ufqi.com/blog/income-rate-annuals-with-yale/
  4. 写写年度收益率年均收益率和年化收益率-2 , https://ufqi.com/blog/income-rate-annuals-with-cic/
  5. 写写年度收益率年均收益率和年化收益率, https://ufqi.com/blog/income-rate-annuals/
  6. 治大国若过小家——写写王朝兴衰更替背后的经济账, https://ufqi.com/blog/political-reform-country-vs-home/
  7.  写写存款利率贷款利率和负利率, https://ufqi.com/blog/captial-rate-and-minus-rate/
  8. 写写1929年美国经济大萧条与2020年美国股市大跌, https://ufqi.com/blog/us-1929-economic-crisis-2020-stock-shock/

—-

 -R/82SN, https://ufqi.com/blog/treasure-credit-money/

发表在 社会生活 | 标签为 , , , | 2条评论

床上电脑桌大学生宿舍上铺懒人可折叠小桌子家用寝室简约学习书桌京东制作护发素发膜正品修复干枯免洗家用护发补水顺滑女士发热焗油膏捐助乌克兰Help Ukraine

惊魂一夜!有福新闻UfqiNews升级到PHP8

有福新闻UfqiNews的老版本依然运行在 GWA2 PHP ( https://ufqi.com/dev/gwa2/ )上,去年底PHP编程语言本身升级到了PHP8, 获得好评,我们即打算择日升级GWA2 PHP到PHP8版本。

考虑到之前关于升级的经验教训,任何开源软件,能够在生产线部署的,一定是当前稳定版本(latest stable release)的上一个版本,也就是上一个稳定版本的最新小版本。所以我们考虑将 有福新闻UfqiNews的GWA2 PHP升级的话,也不会直接去PHP8,而是先使用 PHP8之前的一个稳定版本PHP7.4.

又由于软件开发和使用,尤其是系统软件的开发和使用,另外一个黄金法则是能够运行就尽量不要去做任何修改(包括升级、优化等)。所以如果没有一定的动力的话,我们是不会去修改(升级、优化)一个运行正常的系统。

不幸的是,时过境迁,物换星移,人们对软件系统的改进期望是无止境的。很快我们就遇到一个需要通过升级来解决的问题:当前的UfqiNews ( https://ufqi.com/news/ ) 系统里在处理图片水印时,无法在WebP格式的图片上打水印。这也不是大问题,要求图片另存为其他格式也可以绕过去,毕竟WebP格式的图片还是新事物。可是看到WebP的发展势头,似乎普及起来也只是时间问题,加上对PHP8的神乎其技的“飞速”传言,我们就依仗“艺高人胆大”的脑门一热的开启了在 UfqiNews上升级PHP8的操作了,从而遇上了惊魂一夜,PHP8有不兼容的问题,尤其是看起来像是走退路的NULL值问题和数据类型错误抛出TypeError/ValueError。

PHP Built-in function count() Changelog ¶

VersionDescription
8.0.0count() will now throw TypeError on invalid countable types passed to the value parameter.
7.2.0count() will now yield a warning on invalid countable types passed to the value parameter.

PHP Built-in function explode() Changelog ¶

VersionDescription
8.0.0explode() will now throw ValueError when separator parameter is given an empty string (""). Previously, explode() returned false instead.

另一个受影响较为严重的PHP内置函数是 implode ,
PHP 7及之前,
$str = implode(“-“, $array);
# 能吃屎, 不管 $array 是啥料,都正常返回,继续程序 …
PHP 8及之后,
$str = implode(“-“, $array);
# 开始硬怼了, $array 类型不对时,直接抛出错误,撂挑子不干了!

PHP之所以雄起的“初心”之一就是有种“能吃屎”的精神,一个变量不管啥类型,都能顺利执行跑通,即便结果是非预期的,断然不至于将程序退出来,直接硬怼调用者说,“傻货,你输入的数据有问题!”

通常只有那些强类型的、预编译的语言的才有那么硬刚硬怼的底气,PHP之前是断然不会的。真是由于这种“有事私下说,有问题日志里打小报告”的平易近人,获得了很多小白初级程序员的喜欢,遇上互联网的东风,逐渐成燎原之势。PHP7及之前的程序代码,只要没有形式上错误,基本上都能顺利运行、跑出结果(或可能非预期)。繁琐复杂的编译、类型检查、内存申请与销毁、垃圾回收、数组越界、空指针、Nnll对象等等一些列问题,语言类库及API本身都做了一系列的努力,使之尽可能不终止程序的运行。

这种平易近人其本质是语言本身的类库及API对程序开发者、对程序使用者提供了异常情况处理工具,是某种包容精神的体现。程序员会搞错类型,终端用户也会手抖,网络也不稳定,操作系统也偶尔异常,输入的数据类型有问题似乎在所难免。

语言开发者这么升级改进长脾气地硬怼程序开发者,如果程序开发者不做异常处理,则就一路硬怼到终端用户那里。这显然是不行的,让终端用户吃瘪,程序员是无法交代的,程序员可以往回找语言开发者吗? 纵有理由千千万,简单易行的PHP,不该忘了初心,将这些繁琐的、基本的检查抛出来(而不是兼容处理消除消化掉问题),把强类型语言的“缺点”学来,作为“长进”。

这些背后可能是复杂和漫长的决策、权衡与博弈,其本身是某种方法论的对比,其本身都没有对与错,而是一些取舍和边际选择。这让我想起了我们在设计 GWA2 的API接口时的一些考虑,同时也在阿里巴巴和Google的一些Java语言规范( https://ufqi.com/news/ulongpage.1507.html?tit=Java開發手冊-阿里巴巴-嵩山版-2:編程規約 )中出现过。

比如在 GWA2 的主体方法与接口中,如果方法的返回值是基本类型( primitive scalar types ),应该是有个默认返回值,以上面 PHP 的 count 函数为例,我们预期的写法:
function int count($list){
int c = 0; # default or on failure
if(!isset($list)){ } # a waring
else if($list == null){ } # another warning
else if(!is_array($list)){ } # 3rd warning
else{
# count based on some rule…
c++;
}
return c;
}

如果是非基本数据类型的,如数组、列表或者Hash表、资源描述符等返回值( non-primitive compound types ),则应该是两个返回字段,一个字段标注是否成功处理,另外一个字段则是处理的实际结果值。以上面 PHP 的 explode 函数为例,我们预期的写法:
function array explode($separator, $string){
$rtnArray = array(); $rtnList = array;
$isSucc = false;
if(!isset($string)){ } # a waring
else if($string == null){ } # another warning
else if(!is_string($string)){ } # 3rd warning
else if($string == “”){ } # 4th warning
else{
# split based on some rule…
$rtnList[$i] = function($string, $i);
$isSucc = true;
}
if($isSucc){ $rtnArray[0] = true; $rtnArray[1] = $rtnList; }
else{ $rtnArray[0] = false; $rtnArray[1] = $rtnList; }
return $rtnArray;
}

GWA2 Manual Book
GWA2 Return Values of Methods

附记1.

为了使得系统能够支持WebP格式的图片文件支持水印功能,我们打算升级PHP语言环境。

起初,我们并不打算升级到 PHP8,而是打算升级到PHP7.4这个版本,在实际编译PHP源代码时,当前系统上报错说缺少 oniguruma类库,即便通过yast/zypper 安装了 oniguruma-devel 之后,仍然报错说无法找到,而相同配置的另外其他服务器则能够顺利找到&通过编译。 Linux还有很长的路要走。
既绕无法安装PHP7.4, 那就试试PHP8, 结果还是类似错误,预计只好针对oniguruma这个类库下点功夫,经过一番折腾,终于通过手工下载 https://github.com/kkos/oniguruma 一份源代码,手工编译&安装之后才通过。

解决了 oniguruma 这个问题之后,在编译PHP8的时候,还遇到 ext/geoip 无法编译通过的情况,后来通过手工下载 https://github.com/rlerdorf/geoip 一份重新放入 ext/geoip 才算通过。

尽管解决了那么多问题,具体PHP8兼容性问题,还没有显现。由于担心会出现异常,我们选择在深夜流量较小的时候找一台服务器进行试装,结果就遇上重启之后大面积报错的情况,举凡遇到 count 和 implode /explode等地方,都需要手工增加相应类型或数据安全检查代码。

顺带的昨晚还未被 Hanjst ( https://ufqi.com/dev/hanjst/ ) 替换掉的PHP Smarty模板引擎,一样随着PHP8的升级,触发一团糟的问题,服务被迫终止了一会。庆幸我们在后续的GWA2中将逐渐解耦模板引擎,使用 Hanjst替换掉Smarty,与服务器端开发语言不再绑定。

再一次的惊魂一夜!再次印证了,不要就近升级到软件的最新的latest stable release,惊魂大于惊喜。
再次印证了,能跑的系统,尽量避免升级,各种问题会导致业务被迫中断。智者千虑,必有一失。

—-

附记2.

这是使用 WordPress 的PHP8兼容版,创作的第一篇Blog,WordPress的PHP8的版本升级比预期的快,体验好!值得学习。

WordPress的新版升级过程大致为:
1)备份原程序和资源文件,备份原数据库;
2)检查备份的可还原性;
3)新创建WordPress子目录,或者将现有目录转移为 old目录;
4)下载并解压WordPress的新版本,解压到相应子目录,通过访问 blog/wp-admin/install.php 启动程序;
5)输入原数据库的访问账号,WordPress自动检查;
6)自动选择升级数据库,此前程序文件已经升级完毕;
7)使用原账号登录到升级后的WordPress后台,一切就绪;
8)还差原WordPress下的主题文件和资源文件,通过 cp -rf blog/wp-content/themes/xxx-theme 复制过来主题文件;
9) 通过 cp -rf blog/wp-content/uploads 复制过来资源文件,上传文件;
10) WordPress 提供了在线升级原主题的服务,顺利实施。
11)升级主题及相关文件之后,原先如果对主题模板文件进行了相应修改的话,需要重新在升级后的模板文件中进行再次有针对性的修改,比如特色链接,插图或者广告链接之类的插件。


-GWA2 吉娃兔 是”通用网络应用架构( General Web Application Architeture, https://ufqi.com/dev/gwa2/ )”,基于 -GWA2 可以轻便构建各种网络应用程序,
包括复杂的在线购物商城、在线医疗、在线教育、 旅游交易平台、社群或者社交网站和新闻资讯网站等,
也包括各种企事业单位网上门户,在线交互及服务作业系统等.
还可以包括为NativeApp做服务器端支持, 甚至是WebApp的全部.
-GWA2 是为数不多的支持跨开发语言的应用框架,目前支持 -Java, -PHP, -Perl, -Aspx and -Python .

-GWA2 is a “General Web Application Architecture” and based on -GWA2 developers can easily build a variety of network applications,
including complex online shopping malls, online medical services, online teaching, travel trading platforms, community or social networking sites and news information sites, etc.
Also the applications include various online portals of enterprises and institutions, online interaction and service operations systems.
Moreover it contains server-side support for Native App, or even all of the WebApp.
-GWA2 is one of the web frameworks which provide cross-language support for -Java, -PHP, -Perl, -Aspx and -Python at present.

-GWA2 is E.A.S.Y 
Easy Along, Swift Yield
轻松启动, 快速产出.


ufqinews logo
有福新闻 UfqiNews

这里呈现热点全局, 尺寸间一览所有令人关注的疑点焦点;
这里表达条分缕析, 视野内一睹各个脉络清晰的故事主线.
有福新闻UfqiNews 带来全新的资讯阅览体验, 不信息过载, 亦不信息茧房.

在寻求最大社会共识和满足千人千面之间谋取平衡,
在满足广泛涉猎与追求术业专攻之间谋取平衡.
媒介插上人工智能的翅膀将如虎添翼, 与资讯比翼双飞.
新闻爱好者的良心之选, 匠心之作.

UfqiNews presents the hot spots globally, with all interesting points at a glance.
Information is organized here and there is a clear storyline within every single detail.
UfqiNews brings a brand new reading experience, no information overload and no information Cocoons,

In seeking a balance of the maximum social consensus and meeting thousands of people for each interest,
In achieving a balance between satisfying a wide range of hunting and pursuing specialization in the industry.
That media is being born with wings of the artificial intelligence will be even more powerful and the information will fly swifter than ever.
Better choices of newsreaders and the art of work from them.

发表在 -GWA2, UfqiNews有福新闻, 社会生活, 编程技术, 计算机技术 | 标签为 , , , , | 一条评论

UfqiFina有福金融UfqiWork有福工坊UfqiNews有福常在Google Books