♾好好与这个世界对话:gMIS/吉密斯更新+扩展操作行为

距离上次gMIS/吉密斯的更新差不都有半年过去了,这期间gMIS部署和使用的场景进一步扩大。最近又有新的部署并使用,在更新维护的同时,也有增加新功能,比如这次比较重要的一点是进一步地深化和加强了扩展行为操作的使用。情况较复杂,兹详述如下,以备忘。

gMIS/吉密斯 是“通用管理信息系统“软件,当我们有底气说“通用”的时候,意味着这个系统可以管理任何基于关系数据库所管理的数据,为这些数据提供面向非技术人员的人性化的操作和使用数据的途径。关系数据库,技术人员能耳熟能详地列出 Oracle、MySQL、SQLServer、SyBase等,其也是当今信息化的基础设施之一。通用的面向技术人员的,各种数据库的客户端,无论是基于命令行的还是基于GUI的(包括App和Web, 如 Navicat、PHPMyAdmin等),都有不少选择。而能够基于原始数据,直接产生能够面向非技术人员的,类似产品并不多,我们说“通用”,是我们创建了一套方法,可以针对任意指定的“字段”,无论其什么数据类型,我们都能够在gMIS/吉密斯中配置出契合其特点的输出/输入接口/界面,也即我们的 extra目录,插件式的工作,通过指定配置信息,无论这个字段是读写数据、字符串、选择项、文件、层级目录、编辑器等,均能应付自如。当一种新的数据类型、接口、界面被需要时,我们可以再通过extra方式制作并嵌入进去,这就是当我们说“通用”的底气。

一直以来,gMIS/吉密斯秉持这种“通用性”工作良好。

然而,我们深知,多样性、丰富性和复杂性才是世界的本原,我们以“通用”立命题就有某种抗自然规律的冲动,这种带有某种邪乎劲的要“包治百病”式的做法,会让人担忧,也令人不解。毕竟,这世界上没有包治百病的良药。


Fig.1 数据库数据表示层级

深度地解析,上述“通用性”是建立在对“字段”这一级别的操作上,也即,可以应对所有数据类型的字段进行操作,按照通常的数据组织结构划分 “数据库 — 数据表 — 记录 — 字段”(Fig.1),我们实现了在“字段”级的“通用性”操作。

如果多样性和丰富性的需要是针对“记录(Row)”这一级别呢?

目前针对“记录”的操作,我们有规定的动作“add、modify、view、print、delete、search(insite, pickup)、pivot、export、copy”等,如果要增加一种或多种针对“记录”的操作,该如何操作?这种需求合理吗?常见吗? 需要被满足吗? 能被满足吗? 如果能,该如何实现在“记录”层级的“通用性”?

最早我们设想,几乎所有共用的操作,针对一条数据(Row)的操作也就这么多,除了增删改查这四项基本的,我们已经很丰富地增强提供了其他多项。然而,诚如前所言,多样、丰富和复杂的客观世界,可能会有更多种针对一条数据的操作需求。这种需求是合理的,也是客观的描绘世界的必需项。这样的设计应该被满足。

初次遇到这样的需求,并令我们面对和思考这样的问题是在进行 工作流 的设计和制作上。工作流的本质也是对Row为单位的数据进行操作,但其动作已经超过了对Row本身的操作,而是Row之间发生了关系,也即一条Row可能从用户A流转到用户B,然后用户B将该Row流转到用户C等等,依此类推,而且还可能针对Row产生不同的修改。

为了满足这种需求,我们设计并实现了第一版的 ActOption 标记,这一个版本的 ActOption 在数据表的 table节点配置,并输出绑定到 act/view 界面上. 详细记录参考:
[2016] -gMIS 更新多库连接及工作流workflow
[
2018] –gMIS吉密斯更新Workflow工作流、FileMgr文件柜及GTAjax等模块

这些实践,为我们最终打开 gMIS 好好与这个世界的大门,既然 ActOption 可以一种配置文件的形式嵌入到 act/view 中,那距离出现在 list 主页面的 弹出式菜单中也只有一步之遥。如果实现了某种针对 Row级别的操作,既能出现在 act/view 的窗口,也能够出现在 list主页面的弹出式菜单中,与 常规的 add\modify\view\print等相并列,则gMIS/吉密斯就具有了好好与这个世界对话的强大话语表达能力。

于是沿着这个思路,近期我们突破了自我局限,将 ActOption 的配置通过 ido, jdo, comm/ido.js 等修改实现了自动添加到 list 主页面的 弹出式菜单,与常规操作 view/modify/print 等并列。其实现方式亦颇为曲折,大致流程可以描述为:

基于 table 的xml配置信息,配置某个 <table> 的 <actoption>,
–> gMIS 解析引擎读取 <actoption>配置,
–> act=list 时, ido 负责将 actoption 输出到 JavaScript 
–> 当 comm/ido.js: showActList 被激活时,读取 JavaScript 中的 actoption ,拼入 showActList,呈现给用户. 
–> act=view 时, act/view 负责将 actoption 读取并输出.

这既像是 gMIS/吉密斯 自身打开任督二脉,实现圆通;也像是其突破了狭隘的“通用”桎梏,如虎添翼,实现了升级,将“通用”二字的外延进一步扩大。

在此之前,我们说“通用”是可以满足通常情况下的字段级的任意方式方法的输出与输入,已经有的,随配随用,需要创新的没有的输入输出方式,我们可以创制后无缝嵌入。
今天起,我们说“通用”不但可以满足字段级的任意方式方法的输入与输出,而且也可以满足任意方式方法的记录级的操作,已经有的,随配随用,需要创新的没有的操作方式方法,我们可以创制后无缝嵌入。

所谓“通用”,不是寻求某种包治百病的神药,而是普通常见的已经就绪,随去随用;而没有的,也能够见机行事,按需定制,增强适配。如此以来,“通用”则更有底气,无论是理论基础,还是实践经验,我们都信心十足,满怀干劲;既讲求事物的普遍性,也照顾到事物的特殊性;既有由个例到共性的总结归纳,也有由抽象到具象的推导演绎(Fig.2)。也许,“通用”的意思在这里,就像有“科学的科学”——哲学一样。


Fig.2 特殊到普遍,具象到抽象

自2011年创制以来,这将是 gMIS/吉密斯 发展史上的一个标志性的更新,Latest commit e8aaaf0 . 近十年来,gMIS/吉密斯 不断茁壮成长,目前已经具有如下一些鲜明的特色功能点。

创新性的数据表字段级可配置输出输入接口;
先进的无Session用户鉴权体系
创新性强大的站内搜索功能
媲美前端的点选搜索功能
模拟Office Excel的数据透视表功能
商用级别工作流支持;
层级目录式的文件管理系统;
自动跟踪记账系统;
多层级缓存局部更新等优化高速秒开页面
….
创新的数据表记录级的可配置输入输出接口;
….


-gMIS (general Management Information System) 是一种基于 -GWA2 (General Web Application Architecture) 的通用管理信息系统应用软件,具有可配置的输入和输出接口、开箱即用等特征。
可以在其上构建各种信息管理应用软件系统,如
内容管理系统(CMS), 客户资源管理(CRM), 企业资源计划管理(ERP),
办公自动化系统(OA)等, 以及各种行业应用管理系统软件,如
人力资源管理系统(HR),学生管理,档案管理,旅游管理,图书管理,
商品管理及业务运营支撑系统(BOSS)等等。
实现零代码开发、快速搭建各种管理信息系统(MIS, Management Information System).

-gMIS is a -GWA2 based Management Information System (MIS) software with characteristics like configurable input and output interfaces, open-box-to-use.
Various management application software systems can be built on it, such as
Content Management System (CMS), Customer Resource Management (CRM), Enterprise Resource Planning Management (ERP),
Office automation systems (OA), as well as different industry application management system softwares, such as
Human Resource Management System (HR), Student Management, Archive Management, Tourism Management, Book Management,
Commodity management and business operations support systems (BOSS), etc.
With zero code development, -gMIS can build a set of management information systems (MIS) software in a few minutes.

Lower Costs, 
Better Productivity.
降低成本,
提高效率.

 

Posted in -gMIS, 编程技术, 计算机技术 | Tagged , , , , , , | Leave a comment

一例走近科学式地😥网速慢的排查

最近感觉家里的网速逐渐慢下来了,偶尔出现的timeout变成经常性的行为。尽管秉性脾气好忍受度极高也觉得有必要查一下原因,毕竟速度配不上光纤接入速度。

由于计算机网络科班出身,且懂服务器端优化,-R/P2SN ,我想从宏观和微观两个层面去排查。

宏观上分析。

由于访问的目标网站和服务器在国外,起初怀疑是太平洋底的通信光缆受最近地震影响或断了或部分的断了而导致路由表调整而降速。但这一点无法证实或证伪。

其次要考虑的是国外网站及主机受国家防火墙的限制而降速,毕竟黑白名单传了很久若有一天海外未名主机完全无法访问也是有可能的事,只是这一点也无法证实或证伪。

再次,国内路由跨电信运营商的网络之间限速也是有耳闻目睹的事,会不会我要访问的海外主机走的是电信的国际出口而我客户端在联通的网络中。有可能,只是也不容易获得确切证实或证伪的数据。而且,如果网速慢是上述一个或多个交织原因导致的结果,就让排查工作难度提升了不少。

北京城市副中心热火朝天地建设不会影响了这区县的电信网络通信吧?所住小区最近一直在人防工程保养施工,弄断了几根线缆影响了入户线路?

都有可能发生,都不容易确认。

微观上分析。

在网络链路上,网络慢排查需要进一步细化。一个常规检查网络请求处理的时间分为两大块:网路上的时间和服务器端处理请求的时间。由于是自用的服务器,我们可以清晰地看到请求处理时间都在ms毫秒级别。慢不在服务器处理时间上,而是在网络链路上.

于是上各种网络工具,数据显示时好时坏,好的时候各节点一路畅通,坏的时候第一步就走不通. 也能够观察到中间节点超时的情况。相当于链路各节点都有好坏的时候,并不有助于问题的排查。前提的确是,正常情况下ping到服务器端需要 200ms左右,好的时候 100ms,差点就直接超时了。

排查工作就觉得没思路了。于是在浏览器打开本地路由器/网管的控制台看看,居然发现打开本地路由器的控制台页面也有超时的情况。这算是异常到极致了。台式机到路由器只有一条网线,难道是浏览器坏了或者路由器本身有问题?

继续这个思路,使用另外一台mac笔记本无线连接到路由器的控制台,浏览和操控很流畅。进一步地排除了本地路由器的问题。会是台式机浏览器的问题吗?于是从新审视了最新版的Chrome浏览器并打开网络流量监控软件进行实时监测。

在这个过程中,我们又分别重启了路由器和插拔了网线接口,不想问题变得更糟糕了,台式机的网络彻底被断开了,路由器和台式机上都显示网络异常。网线有问题?!

排查路由器到台式机的网线!这跟网线穿墙里走了两个房间进入台式机,起初安装使用时,电信公司工程师拨开了外皮拆了蓝白两条用作了语音电话☎️。另外三组网线用作数据通信,而在拆开网线的部分只是简单包扎,可能来来回回的挪移路由器和牵扯网线导致接口接触不良,信号就传输异常,丢包严重。网路慢和失败就成必然结果。而最后一次的插拔和挪移将问题摆上了台面。

还没有现成可替换的网线,于是重新包扎/包装拨开的网线部分,并重新换了网口,网路恢复,网速回到可以预期的范围。实测本地台式机到本地路由器时延1-3ms,丢包率 0.001,可接受范围。

恐怖片开始、喜剧片结束了这让人哭笑不得😥的网路慢排查工作。

Posted in 服务器运维, 计算机技术 | Tagged , | Leave a comment

🍃GWA2Java新增Cmdline命令行和JavaBean运行时模式

根据业务发展需求,我们按预期新增了GWA2 Java版本的命令行运行模式和JavaBean运行模式。这是距离 GWA2 两年前发布 Perl 版本之后的另外一个主要升级版本。


(GWA2的发展路线图)

# Use GWA2Java in cmdline and/or JavaBean

## GWA2Java cmdline

### compose GWA2 in .java files

### compile them into .class files

shell> cd ./WEB-INF/classes

shell> java com.ufqi.gwa2.Index “?mod=user”

## GWA2Java in .jsp

### compose GWA2 in .java files

### compile them into .class files

### import and use in .jsp:

#### as of a single class
<@page import=”com.ufqi.gwa2.mod.User”%><%
com.ufqi.gwa2.mod.User modUser = new com.ufqi.gwa2.mod.User();
modUser.getId();
//….
%>

#### as of a JavaBean
<jsp:useBean id=”modUser” class=”com.ufqi.gwa2.mod.User” scope=”application”>
<jsp:setProperty name=”modUser” property=”param1″ value=”value1″ />
</jsp:useBean>
<%
modUser.getId();
%>

## Why Tomcat cannot compile automatically .java files under ./WEB-INF?

## as of Caucho Resin?

在此前的 GWA2Java 架构设计下,一个没有明确答案的思考时,如果使用在 .jsp 文件中定义了太多的 classes 并且每个 classes 定义太多的 methods,会不会超过 Java 的规范定义? 即便没有超过,会不会有性能上的不足?

关于第一个问题,我们能够很容易从 Java 文档中得知,一个通常的 Java class可以允许有 65535 个 inner classes,而一个 class也允许有 65535个 methods,所以即便include再多的 class在 .jsp 文件中,也很难到达这个限制。单个 Method有 64K的限制,我们在此前的研发中也的确遇到过,分拆和提炼相应的代码即可回避。

至于第二个问题,这样的class contains inner classes 会不会有性能上的不足,暂时并没有数据支持或者不支持这一说法。

即便如此,根据我们的考虑,仍应该为开发者预留比较传统的Java Servlet的调用方法,也即将单独的class单独封装起来,使用外部类,或者useBean的方式引入 GWA2 的运行时环境,进而与普通的 inner classes一样使用。

基于这样的考虑,我们也一并研制了 GWA2Java 的cmdline命令行运行时模式,这些为GWA2Java的全功能运行时模式提供了思路,继续完善、强化还需要开发者参与并举共襄。


GWA2 cmdline 运行时举例。

GWA2PHP:

shell> cd /path/to/gwa2php
shell> php index.php “?mod=user&act=list”

GWA2Perl:

shell> cd /path/to/gwa2perl
shell> perl index.pl “?mod=user&act=list”

GWA2Java:

shell> cd /path/to/gwa2java
shell> java com.ufqi.gwa2.Index “?mod=user&act=list”


截至目前,基于 GWA2 开发的各种语言版本的软件可以如下几种方式运行:

GWA2PHP
软件以GWA2PHP开发完成后,可以寄宿在Web Server中运行,也可以cmdline命令行运行。

GWA2Perl
GWA2Perl 的软件目前支持以cmdline命令行方式运行。

GWA2Java
GWA2Java 的软件开发完成后,可以寄宿到Web Server中运行,也可以cmdline命令行模式运行。


-GWA2 是”通用网络应用架构( General Web Application Architeture )”,基于 -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, 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 NativeApp, 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
轻松启动, 快速产出.

 

Posted in -GWA2, 编程技术, 计算机技术 | Tagged , , , , , | Leave a comment

Base62x算法改进并增加Base62x in Python

距离上次 “-Base62x 新增 -Perl 版本技术实现 Base62x.pm (-R/J2SL )”, Base62x 在时隔 6 个月后又进行了一些更新,记录一下,也再次印证,最好的版本永远是下一个版本。这次的更新包括:1)对解码算法的优化改进;2)增加Python版本的Base62x的工程实现。

1. 增加 Base62x in Python

春节一过即开工,因着技术项目需求,最近加紧编程开发了 Base62x in Python 的版本(Base62x.py)。 Python.py 版本的 Base62x 调用方法大致如下:

# import Base62x.py
from Base62x import Base62x

# initialize
base62x = Base62x();

rawstr = “abcd1234x’efg89;01”;
encstr = base62x.encode(rawstr);
decstr = base62x.decode(encstr);

Python 对面向对象的支持较好,所以直接以 OOP 方式编制了 Base62x.py , 并没有像 Base62x in Perl版本那样提供了 OOP 和 functional 两种方式。

截至目前Base62x 已经开源的编程语言版本如下: C, Java, JavaScript, PHP, Perl 和 Python. 其中 PHP的提供了三个版本:1)以PHP扩展模块形式的 ext/base62x C语言代码; 2) PHP 5版本的 Base62x.class.php; 3)PHP 7 版本的 Base62x.class.php 。
其中JavaScript 还有两个实现, Base62x.class.js 和 npm base62x 。

在实施 Base62x in Python 的编码时,我们发现 Python 语言中没有提供对自增+1的操作符 “i++” 这样的操作,而在此前所有版本的 Base62x 的算法中,均大量使用了 “i++” 或者 “++i” 的操作。

面对这样的编程语言的特性,我们不得不寻求另外的实现方法,这既是一种挑战,似乎也是一种机遇,我们在下面的一节讨论由此引发的对 Base62x 解码(decode)的优化改进。 

2. Base62x 解码算法的优化改进

2.1. 改进 if-else 顺序
将最大可能的 if 放最前面,减少每次 loop 时的运算对比次数。
改进后的代码大致如下:

if( a > 2){
     # most case
}
else if(a == 2){
     # secondary most case
}
else{
     # least case
}

改进后,有望减少计算判断,提升程序运行速度。也即如果按照通常的逻辑,如果代码写成  if(a==1){ … }else if(a==2){ … }else if(a>2){ … }, 则在每一个循环中,程序都要拿 a 跟 1, 2, 3等各个分支比对一次,然后再进入所对应的分支块。这是本次改进的主要认识之一。

2.2. 改进对自增操作符 i++ 等的调用
使用内部子循环来优化解码(decode)操作, 减掉大量循环体内的 if-else 操作,减掉 ++i 和 ++m 的操作.

如前所述,我们在编码 Base62x in Python 时,发现 Python 无法提供类似 “i++” 或者 “++i”  自增操作符,而在 Base62x 之前的很多版本的解码操作中,均有大量使用自增操作符。原算法的代码大致如下 (C语言):

switch(remaini){
case 1:
printf(“Base62x.decode: found illegal base62x input:[%s]! 1612121816.\n”, input);
break;

case 2:
if(input[i]==xtag){ tmpin[0] = bpos+bint[input[++i]]; }
else{ tmpin[0]=rb62x[input[i]]; }
if(i == maxidx){ //- may be wrapped into a func decode_by_length
c0 = (tmpin[0] << 2);
output[m]=c0;
}
else{
if(input[++i]==xtag){ tmpin[1] = bpos+bint[input[++i]]; }
else{ tmpin[1]=rb62x[input[i]]; }
c0 = tmpin[0] << 2 | tmpin[1];
output[m]=c0;
}
break;

case 3:
if(input[i]==xtag){ tmpin[0] = bpos+bint[input[++i]]; }
else{ tmpin[0]=rb62x[input[i]]; }
if(input[++i]==xtag){ tmpin[1] = bpos+bint[input[++i]]; }
else{ tmpin[1]=rb62x[input[i]]; }
if(i == maxidx){
c0 = tmpin[0] << 2 | tmpin[1];
output[m]=c0;
}
else{
if(input[++i]==xtag){ tmpin[2] = bpos+bint[input[++i]]; }
else{ tmpin[2]=rb62x[input[i]]; }
c0 = tmpin[0] << 2 | tmpin[1] >> 4;
c1 = ( ( tmpin[1] << 4) & 0xf0) | tmpin[2];
output[m]=c0;
output[++m]=c1;
}
break;

default:
if(i < last8){
if( input[i]==xtag){ tmpin[0] = bpos+bint[input[++i]]; }
else{ tmpin[0]=rb62x[input[i]]; }
if( input[++i]==xtag){ tmpin[1] = bpos+bint[input[++i]]; }
else{ tmpin[1]=rb62x[input[i]]; }
if( input[++i]==xtag){ tmpin[2] = bpos+bint[input[++i]]; }
else{ tmpin[2]=rb62x[input[i]]; }
if( input[++i]==xtag){ tmpin[3] = bpos+bint[input[++i]]; }
else{ tmpin[3]=rb62x[input[i]]; }
c0 = tmpin[0] << 2 | tmpin[1] >> 4;
c1 = ( ( tmpin[1] << 4) & 0xf0) | ( tmpin[2] >> 2 );
c2 = ( ( tmpin[2] << 6) & 0xff) | tmpin[3];
output[m]=c0;
output[++m]=c1;
output[++m]=c2;
}
else{
if(input[i]==xtag){ tmpin[0] = bpos+bint[input[++i]]; }
else{ tmpin[0]=rb62x[input[i]]; }
if(input[++i]==xtag){ tmpin[1] = bpos+bint[input[++i]]; }
else{ tmpin[1]=rb62x[input[i]]; }
if(i == maxidx){
c0 = tmpin[0] << 2 | tmpin[1];
output[m]=c0;
}
else{
if(input[++i]==xtag){ tmpin[2] = bpos+bint[input[++i]]; }
else{ tmpin[2]=rb62x[input[i]]; }
if(i == maxidx){
c0 = tmpin[0] << 2 | tmpin[1] >> 4;
c1 = ( ( tmpin[1] << 4) & 0xf0) | tmpin[2];
output[m]=c0;
output[++m]=c1;
}
else{
if(input[++i]==xtag){ tmpin[3] = bpos+bint[input[++i]]; }
else{ tmpin[3]=rb62x[input[i]]; }
c0 = tmpin[0] << 2 | tmpin[1] >> 4;
c1 = ( ( tmpin[1] << 4) & 0xf0) | ( tmpin[2] >> 2 );
c2 = ( ( tmpin[2] << 6) & 0xff) | tmpin[3];
output[m]=c0;
output[++m]=c1;
output[++m]=c2;
}
}
}
}

变量 remaini 是当前待处理的字符流的余量,算法的逻辑是对余量按每四个字符进行解码并按位操作。按余量不同,分别按4,3,2,1的长度情况使用 switch分别处理。这里首先要按 2.1. 的思路,将 余量为4 (最大可能)放第一位 if,然后再处理 i++ 和 ++m 的情况。

根据测试实验,使用一个内部的子循环可以取代 i++ (++i)和 m++ (++m) 的情况,针对C语言版本,还实现了此前处于@todo 的 decode_by_length . 该函数是用于处理 C、Java等可能出现的数据越界的安全检查等。改进后的代码大幅减少,(C语言)源代码大致如下:

if(remaini > 1){
j = 0;
do{
if(input[i] == xtag){
i+=1;
tmpin[j] = bpos+bint[input[i]];
}
else{
tmpin[j]=rb62x[input[i]];
}
i+=1; j+=1;
}
while(j < 4 && i < inputlen);
m = decode_by_length(tmpin, output, m);
}
else{ //- == 1
printf(“Base62x.decode: found illegal base62x input:[%s]! 1612121816.\n”, input);
continue;
}

从代码量上看,改进后的代码缩减至 10几行,而优化改进之前的代码足足有几十行至上百行,因此无论从体量还是从逻辑上看,改进后的代码更高效、简介。改进优化前: do{ if–else if– else if — else if — else; }while(…), 改进优化后: do{ do{ if — else; }while(…); }while(…); .

2.3. 针对所有已知编程语言版本进行升级改进
将上述在 C语言版本  base62x.c 中的算法,近测试无误后将相应地算法同样地在 Java, JavaScript, PHP, Perl 等版本中一一重新实现,并使用所对应的测试脚本对按 2.1 和 2.2 进行优化改进的算法进行测试。

测试结果显示,修改符合预期,优化改进成功。

另外还完成了 C语言、Java语言版本中的 decodeByLength 内部函数/方法的实现。

3. 持续向前兼容所有 Base62x 版本
上述 2.1. 和 2.2. 的优化改进,是 Base62x 在应用范围和算法性能上的扩展和改进。这些改进不会对现有的 Base62x 在格式、定于及语法上引起偏误,改进后的代码持续向前兼容所有 Base62x 的历史版本。

也即,此前版本Base62x 编码出来的 String,能够使用改进后的的算法准确地进行解码;使用改进版本的Base62x编码出来的String,也能够使用原来的Base62x算法原本无误地进行解码。

 

Base62x is an alternative approach to Base64 for only alphanumeric characters in output.
Base62x can be used safely in computer file systems, programming languages for data exchange, internet communication systems, and is an ideal substitute and successor of many variants of Base64 encoding scheme.
Base62x 是一种无符号的Base64编码方案。
Base62x 可以在计算机文件系统、编程语言数据交换、互联网络通信系统中安全地使用,同时是各种变种Base64编码方案的理想替代品、继任者。

 

Posted in Base62x, 编程技术, 计算机技术 | Tagged , , , , | Leave a comment

DevOps: 一例高负载多并发服务器连接池满的异常排解过程

今天是三八国际妇女节, 也是中国农历二月二/龙抬头的节日,恭祝女同胞节日快乐!恭祝中国善良的人们都抬头见喜!

好一阵子没更新Blog,今天也借这喜气冲刷一下往日的阴霾,经过多日的观察,终于找到这阵子“有福新闻”高负载多并发服务器的异常报错背后的原因之一。此前在Blog中频繁观测到 GWA2 PHP Memcached的报错,并且在代码层面进行了多次调优,问题暂时得到缓解,深层并未深究出个所以然。

“GWA2 PHP Memcached自动追加服务器连接?一例缓存服务被击穿的异常分析”, -R/52SP  。

在进行日常服务器状态巡检时,先是发现 Apache 在数日的持续运行中,会有 segmentation fault 导致的死掉,于是对 Apache 2.4.x 进行升级到最新版的操作,然而加了保护措施。尽管偶尔有 Apache child 有 segmentation fault退出,但整个服务顽强的活着.

后续几日的连续观测中,Apache暂无异常死掉,而 GWA2 PHP Memcached再次开始报错,报错信息为唯一的 127.0.0.1/11211 服务被标记为 DEAD (error code: 35). 然而在终端用 telnet 进行手工模拟时,memcached的服务又是正常的,这是为何?时好时坏,随机出错吗?

近日继续探测,在最近的一次服务器上尝试使用 ping ufqi.com 时,命令行开始报错说 sendmeg not permitted… 于是一例服务器操作系统层面的问题被曝露出来。

ping命令的无法执行,可能是网络配置或操作系统异常,循着这个思路,我们使用 journalctl -f 命令,很快看到 kernel层的报错信息:

Mar 08 04:43:06 srv21 kernel: nf_conntrack: nf_conntrack: table full, dropping packet
Mar 08 04:43:06 srv21 kernel: nf_conntrack: nf_conntrack: table full, dropping packet
Mar 08 04:43:06 srv21 kernel: nf_conntrack: nf_conntrack: table full, dropping packet

原来是系统的网络连接池出现了问题!关于 “nf_conntrack: table full, dropping packet” 这里 -R/u2SR 有详细的解释。 简单地说,操作系统集成了 Netfilter 防火墙服务,基于 iptables 的操作系统的连接管理器中,有四个 table(表) 和五个 chain(链)。


Fig1. Iptables 4 tables and 5 chains

Tables: categorized by different operations to data packets.

raw: highest priority, only appied to PREROUTING and OUTPUT chain. When we don’t need to do NAT, we can use RAW table to increase performance.

mangle: modify certain data packet

nat: NAT, port mapping, address mapping

filter: filter

Chains: categorized by different hooks.

PREROUTING: packet before going to route table

INPUT: after packet passing route table, destination is current machine

FORWARDING: after packet passing route table, destination is not current machine

OUTPUT: packet comes from current machine and to outside

POSTROUTIONG: packet before going to network interface

当操作系统处理 TCP/IP 连接相关模块出错时,所有基于连接的服务都会报出异常。这是此前在代码层无论如何优化都没办法解决OSI分层中更底层的问题。

操作系统默认的并发连接数为 65535, 通过命令:cat /proc/sys/net/netfilter/nf_conntrack_max  可以查看到。 如果通过命令 cat /proc/sys/net/netfilter/nf_conntrack_count 获得到的数字等于 max 的最大值时,此时再有新创建连接的需求就会报错,当创建连接失败时,基于其上的所有应用都将失败,而这些在代码层并不容易解决。推荐的 /proc/sys/net/netfilter/nf_conntrack_max 值的计算公式: CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (ARCH / 32) , 如 8GB RAM in x86_64 OS,  max = 8*1024^3/16384/2=262144 。

当一台繁忙的服务器的连接数超过默认值时,需要通过修改 /proc/sys/net/netfilter/nf_conntrack_max  来使得系统能够接收更多连接进来。

临时修改:
echo 524288 > /proc/sys/net/netfilter/nf_conntrack_max
or
sysctl -w net.netfilter.nf_conntrack_max=262144
永久修改:
在 /etc/rc.d/after.local  或者 /etc/rc.d/rc.local 中增加上述命令行语句。

除了增加 /proc/sys/net/netfilter/nf_conntrack_max 参数设定外,还可以通过调整 iptables 的表或者链上相关规则来进行调优,更多参考: -R/u2SR 。

“有福新闻”, “全国招投标信息中心” 等服务又可以畅快地运行并有信心地迎接下一次洪峰的到来。

小结:
1. 在代码层很难发现操作系统层的问题,可行的路线是分析应用层源码;
2. 系统层的异常,通过具有随机性,比如连接数,是一个动态变化数据,可能下一秒异常就没有了,无法确定重现的问题为 troubleshooting 带来了一定的障碍。
但是,已有的问题,在没解决之前,会再次发生,这是确定的。

Posted in -GWA2, 服务器运维, 计算机技术 | Tagged , , , , | 1 Comment

🛠️Hanjst/汉吉斯特更新加JavaScript运行时优化等

这是 Hanjst/汉吉斯特 发布以来的首个主要升级更新版本。这次的主要升级更新的内容包括移除HTML Comments注释行, 优化在 Hanjst include模板文件时的JavaScript运行时环境。

Hanjst 在设计和工程实现时,预留了模板文件嵌套功能,由于在过往的Web软件研发过程中,对模板文件的套用场景非常熟悉,所以对这一需求也感触较多,于是在最早的 Hanjst 初始发布中就预制了模板嵌套功能。

Hanst的模板嵌套采用了与 -Smarty 等模板语言类似的语法表达:

{include file=”$anEmbedTplFile”}

其中 “anEmbedTplFile” 经由服务器端读取并生成想要的表达式语言字符串。然后经由 Hanjst JSON Data 传递给 Hanjst 模板解析引擎。

Hanjst 模板引擎在解析 include 指令时,将对应的模板表达式汇入当前的页面的当前位置,从而实现对模板的嵌套。这个过程很流畅,逻辑也与服务器端模板语言一致。所不同于服务器端模板语言的是,当我们在 anEbedTplFile 里声明 JavaScript 变量及对象时,在父模板的 JavaScript运行时环境中可能找不到该变量或对象。设若, anEmbedTplFile 中包括如下内容:

<p>This is in an Embed Tpl File</p>
<script>
function embedJSFunc(varA){ console.log((new Date())+” :  call embedJSFunc with varA:”+varA); }
embedJSFunc(“I’m in embed tpl file!”);
</script>
<p>EOF.</p>

当我们将该模板以嵌套的形式进行嵌入一个 index.html 中时:

<p>This is in an Index Tpl File.</p>
{include file=”anEmbedTplFIle”}
<p>EOF.</p>

Hanjst 在解析 index 时,会将 anEmbedTplFile 的内容合并到 index 中,同时通过 _appendScript 私有方法将 embedJSFunc 注册到 index 中,同时在 index 的运行时环境执行 embedJSFunc 函数。

这次的主要升级改进就是这一流程中的细节进一步的优化提升。

1)资源冲突问题,当 index 中也有 embedJSFunc 同名函数时,会报错。
该如何处理报错信息?
可行方法是,避免在 index 和 embed 文件中同时命名相同的函数或变量、对象,以免引起冲突导致出错。
即便有开发者意外地在 index 和 embed 中同样地声明了相同的函数后者变量,Hanjst 也做了异常处理,以便整体流程能够顺利执行并完成页面渲染。

2)如果在 embed 中有 <script src=””></script>, 需要考虑引入其脚步资源。
该怎样引入较好?
路径中的变量如何处理?
Hanjst 此次改进增加了对 embed 的引入资源的支持,对资源路径中的变量进行解析,如在 anEmbedTplFile 中:

<script async src=”{$tplDir}/js/adsbygoogle.js”></script>

则 Hanjst 在将 该js引入到 index时,同时解析出 {$tplDir} 所指向的真实路径。

然而,如果是在 index 中模板中的路径,则需要在服务器端进行相应的替换,因为此时Hanjst 尚未接管对页面的渲染,尤其是不能使用带有变量的路径来引入 Hanjst.js 文件。

在服务器端对资源路径进行替换的通常做法大致过程是:A)模板开发人员,前端设计制作人员通常会使用相对资源引用路径在交付设计稿时,B)工程技术人员拿到带有相对资源引用路径时,会分割出共用头文件和尾文件,其他嵌套文件块,然后藉由模板引擎的{include}指令,重新分割、组装模板,C)工程技术人员将其中的相对引用路径替换为带有模板绝对路径的输出内容文本。相应的替换逻辑可以在 -GWA2 Java版中见到。

//- replaces in GWA2Java
String[] repTags = new String[]{“images”, “css”, “js”, “pics”};
for(int ti=0; ti<repTags.length; ti++){
    outx.append(“ti:[“+ti+”] reptag:[“+repTags[ti]+”]”);
    tplcont = tplcont.replaceAll(“\””+repTags[ti]+”/”, “\””+viewdir +”/”+repTags[ti]+”/”);
}

3)执行时序问题,如果是执行语句,绑定了相应的DOM对象,而该对象在Hanjst当前环境中还未就绪,会报错.
该如何处理?
onComplete?  window.setTimeout?

当 Hanjst 将 embed的模板内容中的脚本注入到 index 的运行时环境时,在解决了资源冲突问题之后,需要考虑执行时序,尤其是当待执行的代码需要依靠 Hanjst 后续输出的DOM 对象时,需要延后执行。

延后执行有两种思路,依靠 JavaScript的事件驱动机制event-driven, 将待执行的动作绑定到 Hanjst 的渲染方法 _renderTemplate 之后,也即 Hanjst._renderTemplate.onComplete。 第二种思路是,依靠 JavaScript的匿名函数Anonymous Function机制,既然是延后执行,则可以将待执行的命令封装到一个匿名函数,然后设定一个 window.setTimeout 过一段时间(预计 Hanjst 渲染完成 DOM)之后再触发该动作。

目前,Hanjst 采用的是后一种解决执行时序问题,也即当 embed的动作要执行时,先以一个匿名函数封装并生成一个 window.setTimeout。

执行时序问题,不单是 embed有,有时候index内的执行函数也有问题,一个常见的场景是,如果某些JavaScript调用是发生在 Hanjst引用之前,则会被调用两次(重复!)。解决这个问题的思路就是将 待执行的命令放在 Hanjst 引用之后,或者在命令设计时,允许重复执行而不影响预计效果。

这个设计及实现可以在 Hanjst的Demo页面看到。

Web模板语言及其解析引擎深入下去才发现,复杂而庞大,希望这些细节的探究及描述可以帮助更多的开发人员理解其内幕,仅一个{include}指令的编译就涉及那么多方面,一些以玩票心态用几十行代码实现一个“轮子”的想法可以考虑暂停下来了。

Hanjst 解析引擎目前已经超过600行代码,包括很多类似这样的算一行的统计:

var asyncScriptArr = []; var isAsync = false; var srcPos = -1; var endTagPos = -1; //- four lines
matchStr = match[0]; exprStr = match[1]; //- two lines
if(exprStr.indexOf(‘=’) > -1){ containsEqual = true; } //- three lines

毫无疑问,随着 Hanjst 部署的实例场景越多、兼容的异常越多、引入的模块越多和功能日趋强大,其代码会越来越长。

 


 

Hanjst/汉吉斯特 是一种基于JavaScript的模板语言及解析引擎,她运行在客户端/服务器端。Hanjst能够表述逻辑控制,能够实现与服务器端模块语言相同的功能。

Hanjst/汉吉斯特的主要特征/功能包括:

  • Hanjst当完全在客户端解析时,节省服务器端计算资源;

  • Hanjst模板语言独立,不与服务器端资源做任何绑定;

  • 纯粹的MVC,层间数据用JSON格式传递;

  • 常见模板语言功能全支持,附带复杂而强大的JavaScript编程能力;

  • 无学习成本,直接使用JavaScript书写模板语言;

  • ….

Hanjst 新增部署实例参考:
-Ufqi-Ztb ,
https://ztb.ufqi.com/?mod=ztb , 
全国招投标信息中心 .

Posted in -GWA2, -Hanjst/-汉吉斯特, 编程技术, 计算机技术 | Tagged , , , , , | Leave a comment

GWA2 PHP Memcached自动追加服务器连接?一例缓存服务被击穿的异常分析。

GWA2 PHP Memcached自动追加服务器连接? 一例缓存服务被击穿的异常分析。

最近在一次网络应用服务器巡检时,发现负载特别高,与当时的流量不成比例,于是就进一步地核查看看为何负载高。

首先发现负载高是由于数据库(-MySQL)的查询读取查询异常的多,并发连接有上百个,多数是简单的查询,可见不是数据库结构问题,而是自然流量触发的数据请求。

缓存被击穿了吗?为何这些显而易见的查询会出现那么多并发?稍微留意发现,有些查询SQL是相同的,于是可以大致推断,缓存层出问题。确认缓存被击穿,对一个线上应用来说是一件很恐怖的事情。

@memcached
进一步的诊断缓存服务(-Memcached),使用telnet 工具连接本地缓存服务,通过 stats命令查询,缓存服务正常,有正确的get/set数据,有get_hits/misses统计,缓存服务没有问题。

下一个疑点就是在代码层,PHP连接模块Memcached的地方有问题吗? 调用缓存服务的存取操作有异常吗?

打开 GWA2 PHP 的 inc/config.class 核对连接缓存服务,没有问题,基于IP地址和端口的连接配置;
打开 inc/webapp.class 显示读取缓存的成功与失败日志记录,发现有成功,有失败。
进一步地打开 inc/webapp.class 的 readObject/writeObject的日志记录;打开 inc/cachea.class, inc/memcached.class 中的 set方法的日志记录。

日志记录显示,有成功的,结果是预期的;当失败时,memcached发回的错误代码和错误消息是,35 / Server Marked Dead。

这听起来不可思议,当下的memcached服务,只有一个 127.0.0.1 基于IP的连接配置,怎么会有成功和失败的状况同时存在呢?

更进一步地输出当前 sever list发现,的确有两个缓存服务的配置:

memcached servers:a:2:{i:0;a:3:{s:4:”host”;s:9:”127.0.0.1″;s:4:”port”;i:11211;s:4:”type”;s:3:”TCP”;}i:1;a:3:{s:4:”host”;s:33:”/bin/memcached/memcached.sock”;s:4:”port”;i:11211;s:4:”type”;s:6:”SOCKET”;}}

明明在 inc/config.class 中只配置了 127.0.0.1 的一个缓存服务,而第二个 /bin/memcached/memcached.sock 从哪里来的呢?显然这个 .sock 的连接已经被标注了 DEAD 但并没有从 server list 里移除,这也让人费解,毕竟在 Memcached的连接初始化时声明了自动移除DEAD Server。

$this->mcache = new Memcached(self::persist_ConnId);
$this->mcache->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true);
$this->mcache->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);

一种合理的解释大致是,/bin/memcached/memcached.sock 文件存在,然后Memcached 服务会自动探测,然后追加到当前的 server list里。会这样智能吗? 可能要分析PHP Memcached的扩展模块源码 和 Memcached的服务源码相应的模块才能确认。

导致存在 /bin/memcached/memcached.sock 文件并且使用 127.0.0.1 的原因是,第一次启动 Memcached服务时,指定了 -s /bin/memcached/memcached.sock 然后kill关闭,再次启动时不带 -s 而是指定了 IP/Port,这样 /bin/memcached/memcached.sock 就被遗留下来而没有用。

之所以不再使用 -s /bin/memcached/memcached.sock 模式,是由于该 Memcached服务需要在 PHP和 Java之间共享,而Java读取操作 .sock 文件相对复杂,暂时没有集成到 GWA2 Java中去。而 -s /bin/memcached/memcached.sock 与 IP/Port 在Memached服务看来又不能共存。

由于 /bin/memcached/memcached.sock 是上次启动后遗留下来的,所以其状态可能并不是 DEAD, 所以 OPT_REMOVE_FAILED_SERVERS 也未能生效?

针对这个问题,手工移除 /bin/memcached/memcached.sock , 然后重启 HTTPD ,  GWA2 PHP 再次正常运行,缓存服务不再报错,也没有缓存服务被击穿,导致数据库查询并发大,进而系统负载高的问题出现。


-GWA2 是”通用网络应用架构( General Web Application Architeture )”,基于 -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, 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 NativeApp, 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
轻松启动, 快速产出.

Posted in -GWA2, 编程技术, 计算机技术 | Tagged , , | 1 Comment

㊗️ GWA2/吉娃兔八周年啦!GWA2内置模板引擎成功由Smarty替换为Hanjst

2011.01 — 2019.01 🎉

GWA2/吉娃兔 八周年啦!GWA2内置模板引擎成功由Smarty替换为Hanjst!
欢迎试用体验“轻松启动,高效产出 (EASY模式, Easy Along, Swift Yield)”的“通用网络应用开发框架”。

 314 commits / 8 years , 
3.27 commits / month.
几乎每周 GWA2 有更新,在成长.

GWA2/通用Web开发框架, 2011.01 — 2019.01 ,已经走完八个年头。这期间,GWA2 的设计思想始于Java开发语言,第一个完成的版本却是PHP开发语言的 GWA2PHP。

八年来,GWA2 从单一的PHP版本,后续又逐渐完成了 Java生产就绪版本和 Perl的生产就绪版本,规划中的 Aspx 和 Python版本预计在未来2-3年完成生产就绪的初始版本。

 八年来,GWA2成功部署无数大大小小的商业项目,并成功孕育出通用管理信息系统:-gMIS / -吉米斯

1. GWA2的价值回顾

GWA2的设计与研发思想是面向对象,面向物理世界的,在将这种思想进行工程化实现时,也经历过很多曲折。

我们团队努力要探索和实现的是一种前无古人的问题:

到底能不能实现软件研发框架的跨开发语言?

如果软件开发框架能够跨开发语言,该如何进行?需要遵循哪些规则?有无最佳实践(Best practice)?

计算机科学与技术仍然年轻,信息产业也只是经历短暂的几十年的发展,编程语言及软件工程领域,新思想,新观念,新技术,新尝试仍层出不穷、日新月异。我们庆幸在这波时代巨浪中以 GWA2 的研发探索参与其中,试图回答“软件研发框架是否能够实现跨语言”这样的问题。

从本质价值上说,之所以用软件开发框架,也是前人探索和总结的经验,“为解决一个开放性问题而设计的具有一定约束性的支撑结构”,能够“更迅速和方便地构建完整的解决问题的方案”。GWA2作为单一开发语言的开发框架,承载了这一使命,并严格遵循了面向对象,面向业务等开发规范,在扩展性、易用性方面取得可喜的进步。

在单一开发语言等软件框架的横向比较中,GWA2 也有出色的表现,如GWA2的PHP版本,“-gwa2 vs -thinkphp”, -R/L2SP 。

为何要有跨编程语言的开发框架?

我们预设一名软件工程师在其一生的职业生涯中,绝大多数人不会也不能只懂一门开发语言,在不同的职业阶段,在不同的应用项目上,甚至在不同的历史时期,会或多或少地使用两种以上的开发语言进行工作。如此以来,软件工程师就面临要学习两种以上的开发语言,然后每种开发语言再学习一种软件开发框架。

GWA2 的初衷就是解决掉后面这个问题,软件工程师在A语言掌握和习得的软件开发框架知识,可以无缝地在B语言上继续使用。无需再学习,也不必再学习另外一种额外的软件开发框架。此其核心价值之一。

其二,籍由跨开发语言的“上帝视角”,我们可以审慎地对待每一种开发语言,汲取该开发语言的优点而实现和部署在其他开发语言的相应GWA2的版本中,抛弃其缺点与不足;积极参与各个开发语言社区,以促进各个开发语言的竞向发展。从而实现在不断完善和增强改进GWA2的基础上,推动人类在软件领域的集体进步。

2. GWA2内置模版引擎由从Smarty 到Hanjst

GWA2最近较大幅度的改进是其内置模版引擎由原来的 Smarty 改为 Hanjst。

这首先得益于 Hanjst的生产环境就绪版本的发布(-Hanjst, -汉吉斯特, -R/U2SK )。谈及改动的动机,大致由如下几点。

1) Smarty 本身已经足够好,足够强大到满足生产环境需求,这是 Smarty 在之前我们的遴选中能够胜出的原因之一。Smarty 简洁明了的表达语言很接近自然语言,令其学习门槛低,容易上手,而进阶应用也保持了这种简洁,所以学习曲线并不陡峭。

我们在 GWA2PHP 中集成了 Smarty ,他工作地很好。当我们研发 GWA2Java时,发现 网上 Smarty爱好者已经有将 Smarty 移至成 Java 版本的, Smarty4J ,于是我们也顺理成章地集成了 Smarty4J。

遗憾的是, Smarty4J 是大约7-8年其版本,且其后没有更新,只兼容 Smarty 2的语法,这极大地限制 GWA2Java 进一步的发展。

2)寻求跨开发语言的 模版引擎的想法一直在我们的 Todo List上,最早在 2016年我们就尝试一种 Jstpl的模版引擎创制。

当初的想法一是对跨开发语言的模版引擎的认识,逐渐归集到 JavaScript身上,二是网上现存的JavaScript的模版引擎虽各有千秋,各具特色,但拿出来跟 Smarty相提并论但并没有,多数JavaScript模版引擎均属画地为牢自我限制,然后孤悬一偶。

同时,作为 Smarty的重度用户,在深度使用 Smarty时,也发现一些不足。

3)带着这些想法,我们希望创制一种能够在语法、功能上与Smarty媲美,基于JavaScript的能够跨开发语言的模版引擎。

令人惊奇的是,Smarty 爱好者如此广泛,让 Smarty JavaScript也被研制出来了: JSmart (-R/s2SR ) 。这个开源的模版引擎也在我们的考察范围之内,令人稍微不满意的是,我们无法接受下面这样的模版表达式:

<script id="test_tpl" type="text/x-jsmart-tmpl">
    Hello {$name}
  </script>

我们认为, 第一行和第三行应该去掉。参考: -R/U2SK 。

4)加上其他要改进的 Smarty的语法,我们最终还是决定新造一个我们认为可以做得更好的轮子, Hanjst / 汉吉斯特。

从2016年开始,历时3年的探索与研制,2019年元旦开花结果,-Hanjst, Han JavaScript Template Language/Engine 终于发布一个生产就绪版本,中文名 -汉吉斯特。详见:-R/U2SK 。

3. GWA2的下一个八年

2019.01 — 2027.01

我们会将 GWA2 持续研发下去,继续完善和增强,不断增加和扩张公用类库,拓展增加更多的开发语言版本,如 GWA2Python版本, GWA2Apsx版本等。

我们还将继续完善作为GWA2的集成部分,也可以独立发展的 Hanjst模版,以减少和降低研发人员在使用模版方面的困扰。

最主要的,GWA2 的各个版本都将被部署和应用到更多商业项目中,在其中被检视、检验,并发现问题、修正问题、改进性能等,这些实践是GWA2 不断进步的前提和基石。

我们相信,信息产业、软件行业在未来信息时代的人工智能普遍应用阶段更逐渐成功社会发展和人类进步的基础技术领域和基本设施范畴。

🎉期待GWA2 的下一个八年,祝愿我们共同的下一个八年更美好!㊗️

 


 

-GWA2 是”通用网络应用架构( General Web Application Architeture )”,基于 -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, 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 NativeApp, 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
轻松启动, 快速产出.

 

Posted in -GWA2, -Hanjst/-汉吉斯特, 服务器运维, 编程技术, 计算机技术 | Tagged , , , | Leave a comment

Apache Tomcat JDBC连接池接入MySQL数据库的NamingContext异常排解

在最近新部署的 -GWA2 in Java 的实例中,一切进展顺利,唯独在启用连接池访问 Tomcat 接管的 MySQL 数据库连接池时报错出异常。问题排解过程曲折,值得记录如下。服务器端日志信息如下:

java.lang.ClassCastException: class org.apache.naming.NamingContext cannot be cast to class javax.sql.DataSource (org.apache.naming.NamingContext is in unnamed module of loader java.net.URLClassLoader @5383967b; javax.sql.DataSource is in module java.sql of loader ‘platform’)

根据报错信息,找出报出问题的代码段如下:

if(this.hasSocketPool){
try{
    this.ctx = new InitialContext();
    this.ds = (DataSource)ctx.lookup(“java:comp/env/jdbc/”+this.myDb); //- error!
}
catch(Exception ex){
ex.printStackTrace();
}
}

就是在 ctx.lookup 返回对象进行强制类型(DataSource)转换时发送错误,抛出异常。

让人费解的是,这些代码在另外的运行时环境时,运行良好,难道跟新部署的环境有关? Tomcat 9.0+ / MySql 8.0+ / Java 11.0+ ?

报错信息的字面意思是 “A类不能强制转为B类(A类在未命名的加载器模块C中,B类在platform加载器的java.sql模块中)”。根据这些理解,大致是加载的类出现错乱了,于是沿着这个思路搜索一下网上其他同学的过往经历。

网上的资讯大致可以分为两种,一种是的确加载错了 org.apache.naming.* 相关的类,另外一种更大的可能是没有正确地在 Tomcat中配置 conf/server.xml 和 conf/web.xml ,这个参照 Tomcat的手册可以很快排除。

而第一种,加载错乱的情况,在GWA2 Java中,理论上不应该发生,因为我们在 inc/MySql.class 中明确地声明了加载如下类:

<%@page import=”javax.sql.DataSource,
java.sql.DriverManager,
java.sql.ResultSet,
java.sql.PreparedStatement,
javax.naming.Context,
javax.naming.InitialContext”%>

显然,其中并没有显示地加载任何 org.apache.naming 相关的类。尽管如此,我们还是在代码层面将类的调用再一次的明确下来,使用类的绝对路径以避免可能引起的混淆。改进后的代码为:

javax.naming.Context ctx = new javax.naming.InitialContext();
javax.naming.Context envCtx = (javax.naming.Context)ctx.lookup(“java:comp/env”);
envCtx = (javax.naming.Context)envCtx;
Object dsObj = envCtx.lookup(“jdbc/”+this.myDb);

很遗憾,带上绝对路径的类的声明与应用于事无补。

迫不得已,我们决定将部署归零,清空所有配置,重新下载最新版的 Tomcat, GWA2 Java等,然后重新安装,再次运行时,很遗憾,问题依旧!

几乎要放弃了,这种可能由于软件不匹配导致的问题,已经超出应用层和代码层的解决能力范畴。不能放弃,还有最后一招——分析源代码——还没使用。于是耐下心来,翻看 Tomcat 的API文档和 Java 的API 文档,逐个对象查询、分析,并在程序中逐个步骤进行断点测试。

javax.naming.NamingEnumeration dsList = envCtx.list(“jdbc/”);
debug(“dsObj:”+dsObj+” / “+dsObj.toString()+” ctx:”+ctx+” envctx:”+envCtx+” envctx.getEnv:”+envCtx.getEnvironment());
debug(“dsList:”+dsList+” mydb:”+this.myDb);
while(dsList.hasMore()){
    debug(“dsList: next:”+dsList.next());
}

-R/B2SR 
https://tomcat.apache.org/tomcat-9.0-doc/api/index.html

-R/72SS 
https://docs.oracle.com/en/java/javase/11/docs/api/java.naming/javax/naming/Context.html

进一步地日志输出中,我们看到了蛛丝马迹,在测试代码的第二步,原本应该返回 javax.naming.Context 对象的,却被 Tomcat 替换为 org.apache.naming.NamingContext. 所以后就几乎全都跑偏了。

Thu Jan 03 05:56:32 UTC 2019 dsObj:org.apache.naming.NamingContext@368631b7 / org.apache.naming.NamingContext@368631b7 ctx:javax.naming.InitialContext@4f52e2fc envctx:org.apache.naming.NamingContext@5b51d294 envctx.getEnv:{}
Thu Jan 03 05:56:32 UTC 2019 dsList:org.apache.naming.NamingContextEnumeration@152fa16f mydb:
Thu Jan 03 05:56:32 UTC 2019 dsList: next:userdb: org.apache.tomcat.dbcp.dbcp2.BasicDataSource
Thu Jan 03 05:56:32 UTC 2019 dsList: next:contentdb: org.apache.tomcat.dbcp.dbcp2.BasicDataSource
Thu Jan 03 05:56:32 UTC 2019 dsList: next:newsdb: org.apache.tomcat.dbcp.dbcp2.BasicDataSource

当我们调用 org.apache.naming.NamingContext 对象的方法 .getEnvironment 和 .list 进一步地输出时发现,配置信息 userdb, contentdb 和 newsdb 都在。 为何返回是一个包含列表的对象NamingContext 而不是一个 DataSource类型的数据?

谜底揭开!在当前运行时环境中关键变量 this.myDb 为空,当传入一个空值的 资源标识符(数据库资源)时,ctx.lookup 不是报错未找到,而是返回了DataSource的父类 NamingContext!

(DataSource)ctx.lookup(“java:comp/env/jdbc/”+this.myDb); //- error if empty myDb

修正方式:
1)在 GWA2 Java中对 this.myDb 做兼容,如果为空,则不启用默认数据库连接池的连接;
2)尝试递交 bug给 Apache Tomcat 开发组,修改报错信息或改进返回值类型,减少对用户可能产生误导的信息。
3) 尝试回复stackoverflow上类似悬而未决的一问题。

翻阅源代码,仔细查看API文档依然是解决问题的终极大杀器之一。

-GWA2 是”通用网络应用架构( General Web Application Architeture )”,基于 -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, 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 NativeApp, 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
轻松启动, 快速产出.

Posted in -GWA2, 编程技术, 计算机技术 | Tagged , , , , | Leave a comment

Hello 2019! Hanjst/汉吉斯特 模板语言及引擎创新发布

Hello 2019!  Hanjst/汉吉斯特 模板语言及引擎创新发布。

值此一元复始之际,恭祝 网友们新年万事如意!
寄望 Hanjst/汉吉斯特能帮助大家从各种模板中解脱出来,为人类做出更大的贡献。

1. 背景

网页模板语言/引擎缘起于 MVC 思想的引入。当软件项目膨胀复杂到足够大时,软件研发人员与UI/UE设计人员就需要分别独立出来,其中设计制作人员工作的V(View)部分需要用模板语言与引擎。

模板语言及引擎的设计与研发工作是一项既简单又复杂的工作。简单地说,模板就是一种变量替换的工作,在模板中预留相应的占位符和变量标记,模板引擎在工作时在相应的占位符将对应的变量替换为实际对应的数值即可。

复杂地说,模板语言是一种全新表达语言的设计,设计一门语言,其难度是可想而知的,这门语言要能够满足日常沟通之需要,首先或入门的是语言设计者要考虑的,是该模板语言能够普遍接受和广泛使用,这样语言才有生命力。无疑,简洁而表意丰富是重要而优先的考虑项。

先行者已经在模板领域创制出大量符合各种需求的模板语言与模板引擎,这里是一个简单的归集  -R/U2SJ ,  为了更好的梳理其发展类别,我们绘制了一个 Mind map:

Fig1. 网页模板语言分类概括

2. 问题讨论

如在Fig1中所展示的,模板语言及引擎有很多种类,每个分类下面又有不同的应用实例,可谓多姿多彩,琳琅满目。实际上,在网络上搜索一下可能会发现,网页模板语言和引擎多到不胜枚举,连JSP、PHP这样的开发语言都可以归类为某种模板语言的范围。

通过脑图的分析,我们发现在这一领域还有两个问题没有得到解决,或者没有得到很好的解决:1)服务器端,有没有一种模板语言与引擎可以实现跨开发语言的? 2)客户端/浏览器终端,有没有一种模板语言与引擎能够不需要Script tags的?

针对第一个问题,我们搜索了多次,发现的确有针对不同开发语言而设计的模板语言与引擎,多数只是针对某几种主流语言开发了模板编译引擎,只是做到“准跨开发语言”,另外就是,这些看似高级的模板语言与引擎,大多数都是私有软件,非开源,需要购买获取授权才能使用。

至于第二个问题,Script tags,一般的定义是这样的,如果我们在客户端使用JavaScript来写一个模板,通常都需要先声明一段区块头部,然后完事之后再声明一下区块尾部,这个区块的头尾部使用Script tags来实现的,如下:

<script id="template" type="x-tmpl-mustache">
Hello {{ name }}!
</script>

这是令人不悦的,为何要表述一句 “Hello {{name}}”, 无辜地多写了第一行和第三行,不能省掉吗?

令人无可忍受地还有其他的,
3)logicless 。如果一种模板语言无法表达逻辑,这是什么逻辑?
4){{name}}。为何必需用两个“{{” ,而不是一个“{”?
5)<#list>. 为何要用 “<#” , 能再简洁易懂些吗?


Fig2 腾讯理财通客户端等JavaScript模版

从 Fig2 中我们可以看到这些令人不满意等地方,函数等调用 | f2y | n2t 借用了命令行管道等意思,如果有参数呢?能否写成:

f2y(n2t(IMonthProfit)) 
or
IMonthProfit.n2t().f2y()

通过Fig2 我们还发现 模版语言,尤其是 JavaScript 模版语言在App上应用等依然存在,模版等使用并没有随着Web向App转换而减少。

于是,怀着这些不满意,我们设想满足以下需要来设计一套新的模板语言与引擎:

A)跨开发语言,与服务器开发语言不做绑定,同时开源,免费使用;
B)去掉 Scripts tags;
C)基于JavaScript提供,同时与服务器端一致强大而复杂的表达、表示能力。

如能满足以上,我们在继续研制 -GWA2  / -吉娃兔 的道路上,有望一通各个开发语言的模板引擎,而不是在开发 Java 版本的 GWA2时 选择 Velocity, 在 开发 PHP 版本的 GWA2 时选择 Smarty 等问题。

以语言学家的视角来设计这套模板语言与引擎,
以工程师的思维来实现语法、语义的程序化表达,
这就是 Hanjst 模板语言及引擎。

3. Hanjst, 汉吉斯特 模板语言及引擎

Hanjst是一种基于JavaScript的模板语言及解析引擎,她可以运行在客户端,也可以运行在服务器端。

Hanjst能够表述逻辑控制,能够实现与服务器端模块语言相同的功能。

特征/功能

  • Hanjst运行在客户端时完全客户端解析,节省服务器端计算资源;

  • Hanjst模板语言独立,不与服务器端开发语言做任何绑定;

  • 纯粹的MVC,层间数据用JSON格式传递;

  • 常见模板语言功能全支持,附带复杂而强大的JavaScript编程能力;

  • 无学习成本,直接使用JavaScript书写模板语言;

  • 开源的,免费使用;
  • ….

Han 是我妻子的姓(韩), 也是出现我女儿和儿子名字中的音节。Han 也是中文“汉族”的意思。

Hanjst 模板语言及引擎设计用来终止在HTML模板语言领域不断地“再造轮子”的活动,尽管这听起来有些怪异。

Hanjst 的语法与基于 PHP 的Smarty语言有相似的地方,原因是我们借鉴了 Smarty的一些设计,之所以如此,是我们赞同 Smarty 在语言精炼方面精益求精的探索( -R/x12SU  )。

Note that the PHP syntax uses 5 punctuation characters to display a simple variable: <?=?>, whereas Smarty uses 2: {}

Hanjst 依托JavaScript内部对象及函数的功能,赋予了在模板中直接调用这些功能的能力,实现了媲美服务端模板语言一致的表示、表达能力。如表达截短一个字符串:

{$myString.substring(0, 10)} 

 

4. 发展规划及设想

Hanjst 模板语言及引擎已发布在 -GitHub 上,地址为: https://github.com/wadelau/Hanjst

Hanjst 模板语言及引擎的样例展示地址, -Hanjst , -R/j2SP 。 现在就可以点开尝鲜。

Hanjst 模板语言及引擎的参考手册在编辑中,不日将在线发布。

Table of Contents

I. What is Hanjst?
1.
Hanjst Installation
2.
Basic Settings
II. Hanjst for Template Designers
3.
Syntax and Semantic
4.
Variables
5.
Modifiers on Variables
6.
Built-in Functions
7.
Warnings and Errors
III. Hanjst Template for Programmers
8.
JSON Data
9.
Includes
10.
Compile and Cache
IV. Search Engine Optimization
11.
HTML Head Element
12.
Plain Content Div Element
13.
Robot-oriented Links
V. Advanced Applications for Hanjst
14.
Embedded in HTML Elements

 

5. 不足及改进

目前已知的不足之一是在客户端运行编译模板文件时,对搜索引擎不够友好。

针对这一问题,我们提供了优化HTML head, 曝露 Hanjstjsondata 等方式进行补充。

其他的,请大家试用并反馈。

2019年元旦,Hanjst/汉吉斯特 正式对外公布。

Posted in -Hanjst/-汉吉斯特, 编程技术, 计算机技术 | Tagged , , , , , | Leave a comment

gMIS吉密斯更新Workflow工作流、FileMgr文件柜及GTAjax等模块

gMIS吉密斯 近期更新了Workflow工作流、FileMgr文件柜及GTAjax等模块,记录于此,备忘备查。北京雾霾持续严重级别,雾霾指数持续300多。翻阅两年前的2016Nov26, 雾霾情况居然相同的严重。

  1. WorkFlow工作流改进更新
    因工作需要,很早的时候就在 gMIS 中增加了一个简易的工作流功能。这次在考虑了多用户协作方面对该功能进行了拓展。
    扩增 Act Options功能,进一步释放操作潜能,在被管理的任意一条数据上,除了常规的“编辑/打印/删除”等,可以允许开发者有针对性的增加任意多的操作动作,这些动作可以通过 xml 绑定到指定的数据表上。
    <actoption>actHrefOne | actHrefTwo | …</actoption>

    Workflow 在 gMIS吉密斯中简称为“todolist/任务管理”, 之前是类似与便签的工作笔记,现在通过 ActOption 增加了 回复 / 转发 等功能,可以为工作流转提供更多便利性。如:

    <actoption>jdo.php::tbl=THIS_TBL,pnskpid=THIS_ID,id=THIS_ID,pnsktriggerbyparent=THIS_triggerbyparent,pnsktriggerbyparentid=THIS_triggerbyparentid,act=’addbycopy’::回复::confirm=0,blank=0 |jdo.php::tbl=THIS_TABLE,pnskpid=THIS_ID,id=THIS_ID,act=’addbycopy’::转交::confirm=0,blank=0</actoption>

    actoption 配置功能标签的增加,将为被管理对象带来更多操作便利,为增加个性化操作提供了一条通道。

  2. 新增文件管理功能FileMgr

    最近在 gMIS 实现了一个简易的Web网盘,可以考虑下载试用。如下是一些操作参考内容。网盘逐渐成为刚需,而且目前市面上有各种各样的网盘服务,限于各种原由,其通用性均差强人意,比如“随时随地跨设备”这样的要求。

    我作为技术“顾问”也多次被寻求类似的简易的网盘解决方案,在 gMIS的过往部署中,也屡次被提及此功能,都是由于“懒”,没有上手弄。这次“出手”成下面这样的gMIS版本的网盘。
    数据表设计为冗余字段 parentid, parentname, pparentname, 其中parentid用于记录上一级目录的名称,适合机器读取操作,parentname用于记录上一级目录的名称,pparentname用于记录当前目录的祖父目录。
    其中, parentname和pparentname都采用了fullpath/全目录的结构设计,适合人读取、理解、使用和接收。

    1)访问路径: Π 首页 → 桌面 & 系统配置 → 文件柜 | 文件柜
    2)上传文件或新建目录
    3)子目录导航、浏览
    4)文件下载/更新,子目录的修改
    当目录名称被修改时,按逻辑同步修改其下所有资源和子目录的目录名称;
    当目录被删除时,按逻辑检查目录下是否非空,当目录下还有内容时,删除目录动作将被拒绝;
    5)其上叠加了很多 gMIS 的各种增强的辅助功能。

    FileMgr 文件柜可以为用户提供了全功能的在线文件管理,轻松实现文件的集中管理、分享、分发、备份等目的。

  3. 升级 GTAjax至最新 5.6 版本
    gMIS 依赖 GTAjax 实现页面局部刷新或者后台通信。这次更新gMIS要解决目标数据表的字段名称为“name”的表单的新增、修改异常问题。

    例如当某个数据表包含有某个字段名称为“name”时,在生成新增HTML表单时,会自动创建如下HTML表单,
    <form name=”gmisForm” id=”gmisForm”>
        <input name=”name” id=”name”/>
        ….
    </form>

    针对上述表单,gMIS 会依照一贯的方法呼叫 GTAjax 进行表单数据递交。

    此时,如果通过 <button name=”submitbtn” type=”submit” onclick=”javascript:doAction(this.form.name);”></button> 对表单的递交进行处理的话, this.form.name 预期返回的是 “gmisForm”, 而如果有一个input元素的名称是“name”时,HMTL form的 this.form.name 会返回一个 input对象。

    GTAjax 是一个单独项目,主要用来处理在页面无刷新的情况下,对页面元素进行局部更新,从而实现更高效地HTTP通信。
    GTAjax 项目开源地址在 -GTAjax

  4. 其他更新与修正
    1)修正了导出数据到MS Office Excel时的UTF BOM指定,增加打开目标数据文件的模式为“wb”;
    2) 更新了文件上传模块,增加更多的上传错误明细显示;
    3) 更新了核心类 MySQLix.class, 增加异常日志打印;
    4)更新了登录模块验证码功能,增加实时校验验证码功能;
    5)改进act/trigger参数传递;
    6)改进 pickup 区域同步刷新机制;
    7)改进 class/pagenavi ,增加 notcontainslist等操作符;
    8)多实例对象共享多数据库配置。在最早的设计中,gMIS 只依赖  gtbl.class 一个全局对象完成对数据的操作,因此在实现对多个数据库进行操作时,只需在 gtbl.class 中进行多数据库的连接配置即可。
    随着 gMIS 的功能的增强,我们增加了越来越多的实例对象,如 IntSiteSearch, Pivot, PickUp 等,如何在这些对象里共享 gtbl.class 的多数据库信息成为需要解决的问题。
    本次引入了全局配置参数 args_to_parent 将 gtbl.class 的共享配置信息传递给 InSiteSearch, Pivot 和 PickUp 等。

-gMIS (general Management Information System) 是一种基于 -GWA2 (General Web Application Architecture) 的通用管理信息系统应用软件,具有可配置的输入和输出接口、开箱即用等特征。
可以在其上构建各种信息管理应用软件系统,如
内容管理系统(CMS), 客户资源管理(CRM), 企业资源计划管理(ERP),
办公自动化系统(OA)等, 以及各种行业应用管理系统软件,如
人力资源管理系统(HR),学生管理,档案管理,旅游管理,图书管理,
商品管理及业务运营支撑系统(BOSS)等等。
实现零代码开发、快速搭建各种管理信息系统(MIS, Management Information System).

-gMIS is a -GWA2 based Management Information System (MIS) software with characteristics like configurable input and output interfaces, open-box-to-use.
Various management application software systems can be built on it, such as
Content Management System (CMS), Customer Resource Management (CRM), Enterprise Resource Planning Management (ERP),
Office automation systems (OA), as well as different industry application management system softwares, such as
Human Resource Management System (HR), Student Management, Archive Management, Tourism Management, Book Management,
Commodity management and business operations support systems (BOSS), etc.
With zero code development, -gMIS can build a set of management information systems (MIS) software in a few minutes.

Lower Costs, 
Better Productivity.
降低成本,
提高效率.

Posted in -gMIS, -GTAjax, 编程技术, 计算机技术 | Tagged , , , , , , | 1 Comment

GWA2Perl研发:try-catch无法退出循环、方法传参引用传值及多数据库链接

在使用 GWA2Perl 开发时遇到一些破费周折才发现的 Bug或改进的功能,分析记录如下,备忘。 GWA2 是一套跨语言的网络应用软件开发框架,目前有PHP、Java和Perl版本可供下载选用,也即 GWA2PHP, GWA2Java 和 GWA2Perl。

1. 应用 Tiny::Try 的Catch模块无法执行退出循环的问题


use Tiny::Try
….
foreach my $i(keys %alist){
try{
    # something
}
catch{
    if(aCondition==1){
        next; # cannot?
    }
}
}

此处的 next 无法按预期执行. 改进的措施是,预先顶一个 $tryresult = 0; 然后在成功的时候,将 $tryresult =1; 在 try::catch 执行完毕之后再对 $tryresult 进行判断,以决定是否退出循环. 修改后的代码大致如下。


use Tiny::Try
….
foreach my $i(keys %alist){
my $tryresult = 1;
try{
    # something
}
catch{
    if(aCondition==1){
        next; # cannot?
    }
    $tryresult = 0;
}
if($tryresult == 0){
    next;
}
}

2. Perl方法传递参数应用传值的问题

my %alist = (‘a’=>1, ‘b’=>2);
&functionA(\%alist);
&functionA(%alist); # wrong!
&functionA($alist) # wrong!

my $blist = \%alist;
&functionA($blist);
&functionA(\%blist); # wrong!

Perl的方法参数传递问题,在此前的贴文中进行过相应的讨论,如 -R/H2SP 和 -R/32SJ , 这次重新再三的提及,主要还是由于在Perl的参数传递过程中,极易发生误解,对Perl依靠形式来获取数据类型的做法认识的不够深刻。如给定同一个变量名称,可能表述不同的意思。

$varA = undef; –> 一个普通的未知类型的变量 varA;
$varA[0] –>  一个数组变量 varA ;
$varA{‘a’} –> 一个哈希变量 varA ;
$varA = []; –> 一个hashref变量 varA;

正是这种随意性使得Perl语言的变量掌握起来让人容易出错,再结合传值或者传引用,迷惑性更大。

3. Perloop/Perlobj 面向对象的Perl进行多数据库同时连接的问题

3.1.问题描述
在 GWA2Perl的实现中,对象连接数据库通过 WebApp –> Dba –> Conn –> Driver 的过程( -R/12SL  )。 设若有对象ObjectA,需要连接数据库DatabaseA; 有对象ObjectB,需要连接数据库DatabaseB。
在此设置下,若单独运行实例化的 ObjectA,或者ObjectB均没有问题,或者同时运行ObjectA/ObjectB,同时连接到数据库DatabaseA,也没有问题。 当我们希望同时运行实例化的ObjectA/ObjectB,并分别连接数据库DatabaseA/DatabaseB时,问题就发生了,Perl的运行时环境中,总是留存最后一个活跃的数据库连接信息。
类似的问题并不会在非PerlOOP的环境中产生,如果在通过的函数式编程中,可以通过声明不同的 $dbi 实例来映射到不同的数据库连接上,并保持到一个session执行结束或者手工关闭。比如 $dbiDatabaseA 连接到DatabaseA上,$dbiDatabaseB连接到DatabaseB上。

3.2. 通过对象实例的UniqueId来区别实例
根据对问题的描述,在 PerlOOP中,实例化后的对象共享数据库连接,如果其中以参数的方式带入不同的数据库连接信息,实例化后对象的无法保持数据库信息与具体实例的对应关系。
解决的思路是在 WebApp一层增加如下设施:
1) + getUniqueId
2) + _checkDbConn
其中 1) 是在对象实例化后根据对象 Scalar::Util 的方法 refaddr 来获取对象的唯一 Id号,然后在 WebApp 中,实现对象与数据库信息做绑定;
2) 在 WebApp 中每当进行数据库读写操作时,获取当前实例化对象的 唯一Id,然后从环境变量 %hmf 中获得其绑定的 数据库信息,从数据库对象 $dba 中获得当前活跃的数据库信息,然后对两者做比较,当两者相符时,继续前行;当两者不一致时,重置/切换数据库连接至实例化对象所绑定的数据库上。
也许这是临时解决方案,在后续的PerlOOP中能得到解决,或者在 Perl 6中能够将对象封装的更严密些。

3.3 实例化后的对象根据唯一Id扩展
实现了可以同时操作多个数据库的PerlOOP,GWA2Perl的能力将进一步扩增,其他类似的资源读取均可以实现多源读取,如Session,Cache,File等其他网络资源。
这一问题是第二次碰到,起初遇到时,想到的是在程序中临时切换数据库连接,当其时并没有更好的解决方案,今次深入分析,获得一个可以自动化的持续解决方法,可以实现:
1) 同一个对象在一个时期内,总是持续一致地维持对资源的连接;
2) 不同的对象,拥有不同的资源连接,且可以同时并行。

-GWA2 是”通用网络应用架构( General Web Application Architeture )”,基于 -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, 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 NativeApp, 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
轻松启动, 快速产出.

Posted in -GWA2, 编程技术, 计算机技术 | Tagged , , , , | Leave a comment

[转]我所经历的三次工业革命

真正的经济增长,主要不是表现在GDP(国内生产总值)统计数字上,而是表现在新产品、新技术、新产业的不断出现,表现在人们的生产方式和消费方式的不断改善。

(原作者 张维迎,北京大学国家发展研究院教授,联合创始人)

S1.
纪念改革开放40周年

人类的历史有250万年,但人类的经济增长只有250年的历史。经济增长在今天被当作常态,但250年前,经济不增长是常态。

真正的经济增长,主要不是表现在GDP(国内生产总值)统计数字上,而是表现在新产品、新技术、新产业的不断出现,表现在人们的生产方式和消费方式的不断改善。250年前,人类生产和消费的产品种类大概只有100种到1000种,今天则是10亿到100亿种。根据2017年10月的统计,亚马逊网站销售的商品就有5.98亿种。

人类过去250年的经济增长,是三次工业革命的结果。第一次工业革命大约从1760年代开始持续到1840年,其标志是蒸汽动力的发明、纺织业的机械化和冶金工业的变革;第二次工业革命大约从1860年代开始持续至第二次世界大战之前,其标志是电力和内燃机的发明和应用,还有石油化学工业、家用电器等新产业的出现;第三次工业革命大约从上世纪50年代开始直到现在,其标志是计算机的发明、信息化和通信产业的变革。

但三次工业革命并不是在所有国家同时发生的。英国引领了第一次工业革命,美国和德国引领了第二次工业革命,美国接着又引领了第三次工业革命。有些国家虽然不是引领者,但在每次工业革命发生后,能很快追赶上,而另一些国家则被远远甩在后面,其中有些国家至今还没有完成第一次工业革命。这就是富国与穷国差距的原因。

西方发达国家像我这样年龄的人,当他们出生的时候,前两次工业革命早已完成,只能经历第三次工业革命,但作为中国人,我有缘享受“后发优势”,用短短的40年经历了三次工业革命,走过了西方世界十代人走过的路!

S2.
我的第一次工业革命

1959年秋,我出生在陕北黄土高原一个偏远的小山村。在我出生的时候,除了共产党的领导和人民公社是第一次工业革命的产物,当地人的生活方式和生产方式几乎没有受到第一次和第二次工业革命的影响。我出生的窑洞是什么时候修建的,我父亲不知道,他的父亲也不知道。

在人类漫长的历史中,生活就是衣食住行、柴米油盐,生产就是春种秋收、男耕女织。在我年幼的时候,我穿的衣服和鞋都是母亲手工纺线、手工织布、手工缝制完成的。我至今仍然能回想起,我睡梦中听到的纺车发出的嗡嗡声和织布机发出的吱咔声。

纺织业是人类最早的工业。手摇纺车在汉代就普遍使用,母亲使用的纺车看上去与汉画像石上的纺车没有什么区别。母亲用的木制脚踏织布机是印度人在公元500年至1000年间发明的,大约在公元11世纪传入中国(也有专家认为是中国人发明的)。英国人约翰·凯伊于1733年发明了飞梭,在接近1760年的时候,飞梭在英国已经普及开来,但200年之后,母亲仍然不知道有飞梭,所以不仅织布速度慢,而且只能织出窄幅匹的布,一条被子需要好几块布料拼接而成。

根据英国科技史学家李约瑟的考证,中国在公元1313年就有了三锭甚至五锭纺车,但不知为什么直到我小时候,母亲用的仍然是单锭纺车。英国工业革命期间,詹姆斯·哈尔格里夫斯于1765年发明了多轴纺纱机(珍妮机),使得一个人同时能纺出几根线。哈尔格里夫斯最初的模型仅有八个锭子,但在他还活着的时候,人们已经能制造80个甚至更多锭子的多轴纺纱机了。如果母亲当年能用上多轴纺纱机,她就不会那么辛苦了。理查德·阿克赖特于1768年发明了水力纺纱机,埃德蒙德·卡特赖特于1785年发明了机械织布机了,这些都没有影响母亲的生活。

母亲缝制的衣服都是老式的,所以我小时候穿的裤子前面没有开口拉链。偶然会发生尴尬的事情,就是尿急时裤带打成了死结解不开,就只能尿在裤子里了。每每想起此事,总会让我觉得美国人威特康·L·朱迪森和瑞典人吉迪昂·森贝克在100多年前发明的拉链,真是了不起。

美国人艾萨克·辛格早在1851年就发明了缝纫机并很快投入商业化生产,但我小的时候,缝纫机在我们那里仍然非常罕见。在我10来岁时,村里的一位复员军人带回一位山东媳妇,按母亲一方的亲戚关系,我叫她嫂子。这位嫂子心灵手巧,会用缝纫机做衣服,我穿的第一件“制服”就是她做的。

上大学之后,我就不再穿母亲用土布缝制的衣服了。后来,家里的纺车和脚踏织布机也被当作柴火烧了。

纺和织是棉纺织业的两道主要工序,但在原棉变成能纺纱的原料之前,还需要一些其他工序,其中一项是梳棉。梳棉就是通过疏松、清理和混合,将棉花纤维变得连续可纺的工艺。母亲纺纱用的棉卷是父亲用梳棉弓梳理的。根据李约瑟的考证,梳棉弓(carding bow)是印度人在公元2世纪发明的。梳棉弓在我们当地被称为“弹花弓”,弹花算是一门小小的手艺,能赚点小钱,父亲是从他的四舅那里学到这门手艺的。

“文革”初期,父亲和他四舅及另一个人合伙买了一台梳棉机,存放在离我们村25华里的镇上,逢集的时候就提前一天去镇上弹棉花。梳棉机比梳棉弓的效率要高好多,每次干两天活,每人可以赚到三四块钱,这在当时算一笔不小的收入。可惜好景不长,后来政府搞“割资本主义尾巴”运动,他们的生意就做不成了。

1979年,村里搞起了“包产到户”。父亲把那台梳棉机从镇上搬回家,以为又可以弹棉花赚钱了。但父亲的预测完全错了。没过多久,村里人都开始买机织布了,连棉花也没有人种了,他的那点小手艺也就废了。根据我脑子里的印象,父亲他们的那台梳棉机,就是1748年刘易斯·保尔发明、1775年理查德·阿克赖特改进过的那种梳棉机!

改革开放后,父亲的另一项手艺也废了。我小时候冬天穿的袜子,都是父亲自己捻毛线、自己编织而成。父亲捻毛线用的捻锤,是新石器时代的发明。我上大学后,就不再穿父亲织的袜子了,他也就不再编织了。其实早在1598年,英国剑桥大学的毕业生威廉·李就发明了织袜子机。

第一次工业革命的另一项重要进步发生在冶金工业。冶金工业也是一个非常古老的产业,人类掌握冶炼技术已有5000年,炼铁业也有3000多年的历史。但即使进入“铁器时代”,铁仍然是一种稀有的贵金属,中国宋代曾用铁做过货币。

但铁的稀缺性被第一次工业革命改变了。1710年,英国企业家亚伯拉罕·达比发明了焦炭炼铁工艺,使得大规模廉价铁的生产成为可能。1870年代,英国海军采购代理人亨利·科特发明了搅拌炼铁法。不久,搅拌炼铁法便在全大不列颠境内成为生产熟铁的通用方法,千百万吨铁就这样制造出来,人类真正进入铁的时代。1856年和1861年又相继出现了贝塞麦转炉炼钢法和西门子平炉炼钢法,钢的生产成本大幅度下降,从此,钢逐渐替代铁和木材,成为机器设备和车船的主要制造材料。钢不仅架起了跨江大桥,而且托起了摩天大楼。1889年巴黎埃菲尔铁塔的建成,标志着铁时代的结束和钢时代的开始。

进入钢铁时代,也是新中国领导人的梦想。在我出生的前一年,中国搞起了全民大炼钢铁运动。但遍地土高炉圆不了举国钢铁梦。我在农村时,钢还只能用在刀刃上,全村没有一把全钢制的斧头、镰刀、菜刀。不要说钢,铁也很稀缺,最值钱的就是做饭用的锅,所以“砸锅卖铁”就成为人们陷入绝境的隐喻。锅是生铁铸造的,空锅烧热时一沾凉水,就会裂缝,我们家的锅不知补过多少次了。当时农用工具基本都是木制的,门窗上唯一的金属是锁环。由于这个原因,尽管几乎每个村都有一两个木匠,周围数十里才有一个铁匠。

但改革开放后,随着现代化冶炼技术的引进,中国终于进入钢的时代。1996年,中国取代日本成为世界第一大钢铁生产国。现在再回到农村,发现犁、耙子、扇车都已经变成钢制的了,木制工具已成为古董。

煤炭在工业革命中发挥了重要作用,不仅炼铁需要大量的煤,蒸汽机也要烧大量的煤。中国和英国都是煤炭资源丰富的国家,但英国的煤炭助燃了工业革命,中国的煤炭则长期躲藏在人们看不见的地方。经济史学家彭慕兰用煤炭资源的丰富性解释英国工业革命的起源,看来说服力不是很大。我的老家榆林市现在已成为中国的煤都,其产量占到全国的十分之一。但在我小的时候,村民做饭、取暖用的燃料主要是柴草、树梢和秸秆,大部分庄户人家用不起煤,尽管那时候每百斤煤的价格只有4毛钱(现在的价格是20元左右)。今天政府已经开始禁止老百姓烧煤取暖了,但那个时候是烧不起煤。

在漫长的历史中,人类生产和生活需要的动力主要是人自身和大型动物的肌肉,这一点直到蒸汽机出现之后才得到根本性改变。但蒸汽机发明200年之后,我在农村的时候,动力仍然是人力和畜力。农村人看一个人是不是好劳力,主要看他肩能扛多重,背上能揹多少斤。我们村没有马,因为马太贵,饲养起来也麻烦,仅有的几头驴,是生产队最珍贵的生产工具,耕地、驮碳、拉磨、娶亲,都靠它们。如果一头驴死了,就是生产队最大的损失。

我小的时候不爱干家务活。当时农村磨面用的是石磨,碾米和脱壳用的是石碾。据说,石磨在公元前二世纪的汉代中国就有了,而古罗马在公元前160年也已广泛使用;石碾也是从汉代开始就被人们用来碾米和脱粒了。逢年过节或有红白喜事的时候,由于需要碾磨的量大,通常使用畜力驱动石碾和石磨,但平时小量的碾磨,只能使用人力。母亲要我帮她碾米推磨时,我总有些不情愿,围着碾盘或磨盘转圈圈让人觉得枯燥无味。

蒸汽机最初只用于矿井排水。在瓦特把蒸汽机转变为旋转动力之后,蒸汽机就逐步替代人力和马力,成为石磨旋转的动力。1786年,瓦特和博尔顿在伦敦建立了大不列颠面粉厂,两台蒸汽机推动50对磨石,每周生产435吨的面粉。这个面粉厂的开设轰动了整个伦敦,来这里参观成为一种风气,搞得瓦特很不耐烦。

我老家的石磨和石碾从来没有被蒸汽机推动过,但在我离开家乡三十年后,石磨和石碾基本上都被废弃了。村民们跨越了蒸汽机,直接进入内燃机和电动机时代,这或许就是人们说的“弯道超车”吧!

S3.
我的第二次工业革命

第一次工业革命主要发生在纺织和冶金这两个传统部门,第二次工业革命则创造了许多新的产业。第一次工业革命用蒸汽机动力代替了人力和畜力,第二次工业革命则用内燃机和电动机代替了蒸汽机。内燃机是德国人奥古斯塔·奥托(August Otto)于1879年发明的,电动机是移民美国的塞尔维亚人尼古拉·特斯拉(Nikola Tesla)于1888年发明的。但直到我上初中之前,我们村里还没有内燃机,更没有电动机。

在黄土高原,能种庄稼的地都是些沟沟峁峁的山地,祖祖辈辈都是靠天吃饭。但不知从什么时候起,村民们还是用石头在沟里垒起了一些水地。

水地在当地被称为“园子”,只有少数园子可以引水灌溉,大部分只能靠人工浇灌。零散的小块园子靠挑水浇灌,稍大块的园子则使用一种叫“橘槔”的装置提水浇灌。橘槔是这样一个装置:在一个架空的横木中间垂直钩一个长木杠,长木杠的一端固定一块很重的石头,另一端用一个活动连杆挂着一个柳编水桶。提水的时候,操作者站在石墙半空突出来的台阶上,用力将连杠向下拉,等水桶到达下面的水池灌满水后,再将手松开,靠着长木杠另一端石头的重力,水桶被提到适当的高度时,操作者将桶里的水倒入引水沟。如此往复不断,就可以灌溉大片的园子。

橘槔工作的时候,从远处看起来,酷似托马斯·纽科门于1712年发明的蒸汽机水泵,只是它的原动力来自人力,而非蒸汽。橘槔的英文名字叫shaduf,早在公元前1500年前,埃及人就用它提水了。至于橘槔何时引入中国,不得而知。但从古埃及人最初发明到我们村的人弃之不用,有3500年之久,真是不可思议!

橘槔之所以被弃用,是因为柴油机的引进。

柴油机是内燃机的一种,它是由德国人鲁道夫·狄塞尔(Rudolf Diesel)于1893年发明的,被认为是自瓦特分离式冷凝器之后动力生产方面最重要的发明。狄塞尔死后,柴油机经过一系列改进,在许多应用领域(包括火车、轮船、农业机械等)代替了蒸汽机,至今仍然是移动机械的重要动力。

大约在我上初中的时候,村里有了一台6马力的柴油机。柴油机配上一个水泵,就可以把沟里的水扬程到园子地里,轰动了全村人。只是这台柴油机老出问题,并没有立马替代橘槔。

后来公社又给我们村奖励了一台12马力的手扶拖拉机。这个英国人赫伯特·阿克伊德·斯图尔特于1896年发明的东西,八十年后,终于出现在我们这个偏僻小村。手扶拖拉机马力不大,但又好像无所不能,农忙时耕地、脱粒、抽水,农闲时带动磨面机磨面,或者跑运输。

包产到户后,拖拉机被拆成部件分了,我以为农业机械化没希望了。但没过多久,村里好几户人家自己买了拖拉机,其中还有人买了面粉机和脱粒机,开始商业化运营。慢慢地,到上世纪90年代后期,石磨和石碾被淘汰了,橘槔也被弃之不用,牛驴也没有人养了。

内燃机的最大影响发生在交通运输业。1886年,德国人卡尔·本茨和戈特利布·戴姆勒同时发明了内燃机驱动的汽车;22年后,美国人亨利·福特用自动组装线生产出了廉价的T型车,使得普通工薪阶层也能够买得起。到1930年,汽车已进入60%的美国家庭,美国由此成为“骑在轮子上的国家”。

但我小的时候,方圆几十里内见过汽车的人还屈指可数,全村没有一辆自行车,人们出行的方式仍然是步行。我既兴奋、又恐惧的是每年正月初二跟随父亲去探望改嫁远村的奶奶,虽然路程不过五十华里,但好像有翻不完的山峁、走不完的沟壑,早晨出发傍晚才能到达。

1973年公路修到我们村,起因是五里外的邻村变成了全国农业学大寨的先进大队,省委第一书记要去视察,必须从我们村路过。当26辆吉普车队尘土飞扬经过时,全村男女老少都站在硷畔上观看,真是大开眼界!

我到北京工作之后,每次回家探亲,县政府总会派车把我送到村里,走时又派车把我接到县城。据说这是对在外地工作的县团级官员的待遇,我虽然不是县团级干部,但他们觉得我有点名气,又在中央机关工作,所以就视同县团级对待。我自己也欣然接受这种安排,因为,从县城到我们村八十华里路程,没有班车,找顺风车也不方便。

在牛津读博士期间,我花了一千英镑买了一辆福特二手车,从此有了自己的小轿车。回国后,我又用免税指标买了一辆大众捷达车。记得直到1999年,光华管理学院大楼前平时还只孤零零停着我的一辆车,没想到几年之后,大楼前已是车满为患了。

更让我们没有想到的是,现在每次回老家,村里总停着几辆车,汽车在农村也已不再是稀罕物了,一个远房的堂弟还买了辆中巴跑班车,仍然住在村里的年轻人大多有摩托车。

据统计数据,中国城市人口中每百户拥有的家用汽车在1999年只有0.34辆,2015年则达到30辆。虽然普及率还不及美国1930年全国水平的一半,但在汽车发明130年后,大部分中国城市居民总算享受到了这个第二次工业革命的重要创新!

电力,是第二次工业革命的另一项重要创新。1882年,美国人托马斯·爱迪生在纽约曼哈顿建成了人类历史上第一个集中供电的照明系统,为电气化时代打开了大门。到1930年,美国近70%的人口都用上了电,1960年这一比例已达100%。列宁曾说过,共产主义就是苏维埃+电气化,但在我的家乡,虽然苏维埃很早就捷足先登,电气化却是姗姗来迟。

从出生到去县城上高中之前,我没有见过电灯,村里人照明用的都是煤油灯或麻油灯,有些家道贫困的人家连煤油灯也用不起,一到晚上就黑灯瞎火。有个流传的笑话说,一位客人在主人家吃晚饭,主人舍不得点灯,客人不高兴,就在主人家小孩的屁股上狠狠拧了一下,小孩顿时嚎啕大哭,客人说,快把灯点着,孩子看不见,把饭吃到鼻子里了。

父母鼓励我读书,说愿意为我多费二斤油钱。确实,村里好多人家就是因为怕花油钱,不让孩子晚上看书。为了省油,煤油灯的灯芯都很小,晚上在灯下看书的时候,头必须尽量靠近灯光,有时候打瞌睡,第二天上学的时候,头上就顶着一缕烧焦的头发,被同学们取笑。当时全村最亮的灯在生产大队的公用窑,是带玻璃罩的罩子灯,比小煤油灯费油好几倍。

到县城上高中时,我第一次见到了电灯,不仅宿舍里有白炽灯,教室里还有日光灯。但电压总是不稳,时明时暗,还经常断电,罩子灯仍然是宿舍的必备。

1993年我在牛津读书期间,暑期回老家看望父母,听说两公里外的村子已经拉上电了,我们村因为县上没人说话就没有拉上。知道我认识县委书记,村民们专门到我家,希望我给县委书记说说,给我们村也拉电。我说了,但没有管用。想到村里人对我的期待,这事成了我的一块心病。几个朋友愿意帮忙,一共筹集了四万多块钱,1995年,我们村终于通电了!

通了电,村民的生活就完全不一样了。电不仅能照明,而且能带动家用电器和其他机械。从本世纪第一个十年开始,不少人家相继买了电视机。电冰箱、洗衣机、电风扇、电熨斗、空调等家用电器,这些第二次工业革命时期的重要发明,虽然在那里的农村没有很大的实用价值,但还是有个别人家买了。村里也有了由电动机驱动的磨面机、碾米机、脱粒机、电锯。更重要的是,有了电动机,家家户户都可以用上自制的自来水系统,就是在比窑洞高的地方修一个封闭的蓄水池,把井水抽到蓄水池,水管连接到屋里,水龙头一打开,水就自动流出来了。我在农村的时候,每天早晚去井里挑水是一件很愁人的事,现在再没有人为挑水发愁了。

S4.
我的第三次工业革命

1978年4月,我离开老家去西安上大学。我从县城搭长途汽车到山西介休,再乘火车到西安。这是我第一次坐火车,也是第一次见到火车。火车是英国企业家斯蒂文森父子1825年发明的。至1910年,美国已修建了近40万公里的铁路,而到1978年,国土面积相当的中国只有5万公里铁路。

此时距离第一台大型数字计算机的发明已有33年,微型计算机产业正处于顶峰,比尔·盖茨和保罗·艾伦的微软公司已经成立4年,斯蒂芬·乔布斯和斯蒂芬·沃茨尼亚克的苹果II个人计算机也已经上市两年了,但直到进入大学后,我才第一次听说计算机这个名词。一开始,我以为计算机就是用于加减乘除运算的,可以替代我当生产队会计时使用的算盘。算盘是中国人和埃及人在公元前400年前就使用的东西。但后来我就知道自己错了,计算机将替代的远不止算盘。

经济系一年级的课程有一门“计算机原理”,记得第一次上课的时候,看到硕大无比的计算机感到很新奇。后来知道,1945年宾州大学研发的第一台计算机ENIAC重量接近30吨,长100英尺,高8英尺,占地面积相当于一间大教室。我们还学过二进位制、打孔卡原理和BASIC语言。但除了拿到考试成绩,整个本科四年和研究生三年期间,计算机对我的学习和生活没有发生任何影响。

1985年,我开始在北京国家机关工作。我所在的研究所买了两台电脑,但放在机房,神神秘秘,由专人看管,只有搞经济预测的人可以使用。单位还有一台四通电子打字机,由打字员操作。与手写复写纸、蜡纸刻字印刷以及传统打字机相比,电子打字机最大的好处是可以储存文本,反复修改。复写纸是在19世纪初英国人雷夫·韦奇伍德发明的,蜡纸刻字印刷是爱迪生于1886年发明的,我在高中时和高中毕业返乡务农时都用过。英文打字机是克里斯托弗·肖尔斯等几个美国人于1868年发明的,中文打字机是山东留美学生祁暄于1915年发明的,我上高中时我们学校有一台。

我第一次使用计算机是1988年在牛津读书的时候。我把自己手写的两篇英文文章拿到学院计算机房输入计算机,然后用激光打印机在A4纸上打印出来。激光打印出来的字体真是漂亮,像印刷出版的书一样,让人无比兴奋。

激光真是一个神奇的东西。据说1960年刚发明时,贝尔实验室的专利律师甚至不主张申请专利,因为它“没有什么实用价值”,但自与康宁公司1970年发明的光纤玻璃结合后,它就彻底改变了通讯产业,并且变得无处不在。我第一次享受激光技术是1981年,医生用激光切除了我脸上的一个痣。现在讲课时,我手里拿的是激光笔,不是粉笔。

1990年9月,我回到牛津攻读博士学位时,买了一台286个人电脑,从此就告别了手写论文的时代。1994年回国时,我还把这台电脑托运回北京。但个人电脑技术的发展是如此之快,很快出现了486电脑,这台旧电脑的托运费也白交了。后来又有了桌面激光打印机,这样我就有了自己的桌面出版系统。之后还换过多少台电脑(包括笔记本电脑),自己也记不清楚了。

计算机从公共教室那么大,变得办公桌上放得下(个人电脑)、书包里装得下(笔记本电脑)、甚至口袋里揣得下(智能手机),从而使得像我这样的普通人也能买得起,全仰仗于因特尔公司于1971年发明的微处理器。有了微处理器,个人电脑才成为可能。而微处理器建立在诺伊斯和基尔比于1969年发明的微芯片(集成电路)的基础上,微芯片又以晶体管为基础。所以有人说,晶体管对数字时代的意义,相当于第一次工业革命时期的蒸汽机。

晶体管是贝尔实验室的三位科学家于1947年发明的,不仅比真空管体积小、成本低、能耗少,而且不易损坏,其在消费设备上的第一个应用是德州仪器公司于1954年生产的袖珍收音机。在牛津读书期间,一位台湾来的同学送了我一个台湾产的袖珍收音机,像香烟盒大小,但音质非常好,让我爱不释手。回想起我在农村时滋滋啦啦的有线广播,真是天壤之别。

对大部分人而言,一台孤立的电脑不过是一个文字处理机,我当初买个人电脑的目的就是为了写论文方便。但多台计算机连接成一个网络,用处就大了。1969年,第一代互联网——阿帕网诞生了。1972年,阿帕网的第一个热门应用——电子邮件诞生了。1992年后,我自己也开始用电子邮件了,但当时国内的人还无法使用电子邮件。1993年在筹办中国经济研究中心时,我们向北京大学校领导提的一个要求就是,给我们通电子邮箱。这个愿望被满足了。但没过多久,北大所有的教员都可以使用电子邮箱了。几年之后,中国就进入互联网时代了。

记得1993年12月我儿子在牛津出生的消息,我还是先通过国际长途电话告诉国内亲戚,然后再由这位亲戚发电报告诉老家的父母。电报是美国人戈登·摩斯于1844年发明的,最初一条电报线只能发送一个频率,亚历山大·贝尔想让一条线路同时发送多个频率,结果于1876年发明了电话。到1930年,美国家庭电话的普及率已达到40%,但至1978年的时候,除了少数政府高级官员家里装有公费电话外,中国普通老百姓家庭的电话普及率几乎为0。我在农村的时候,生产大队的公窑里有一部手摇电话,一根电话线串着好几个村,通话时必须大喊大叫才行;往不同线路的电话需要人工交换机转接,全公社只有一个交换机,接线员是很让人羡慕的工作。

转盘拨号电话是西门子公司于1908年发明的,按键拨号电话是贝尔公司于1963年发明的(必须有晶体管电子元件)。上大学之前,我没有见过转盘拨号电话,更没有见过按键拨号电话,因为连县长办公室的电话都是手摇的。我第一次使用转盘拨号电话是1982年上研究生期间,在校门口的一个公用电话上,还是过路的一位老师教我怎么拨号的。在牛津读书期间,偶尔给国内家人打一次长途电话,心跳的比电话上显示的英镑数字蹦得还快。当时国际长途电话很贵,从牛津到北京,每分钟的费用在3英镑以上。

我第一次安装家用电话是留学回国的1994年,也就是贝尔发明电话118年后。当时安装电话要先申请,缴纳5000元的初装费后,再排队等候。后来初装费取消了,但我早已缴过了。1999年,我开始使用移动电话,家里的固定电话就很少用了。

但很长时间,我还是没有办法和老家的父母通电话,直到老家农村也可以安装电话为止。我最后一次收到姐姐写的家信是2000年。

2006年之后,老家农村也有移动电话信号了。我给父母买了一部手机,母亲高兴得不得了, 可惜她的信息时代来得太迟了。2008年母亲下葬的时候,我把她心爱的手机放在她身边,希望她在九泉之下也能听到儿子的声音。

自从用上iPhone智能手机,短期出差我不再带笔记本电脑,也不带相机了。有了智能手机,我与父亲不仅可以通话,还可以用微信视频。父亲现在住在榆林城里,春节时能与村里的乡亲们手机拜年,他很开心。

2017年8月,我带几位朋友去了一趟我们村。朋友们有心,给村里每户人家带了一条烟、一瓶酒。我正发愁如何通知大家来领,村长告诉我,他可以在微信群里通知一下。傍晚时分,乡亲们果真都来了,烟和酒一件不剩领走了。回想起我在农村时,村支书需要用铁皮卷成的喇叭筒大喊大叫很久,才能把全村人召集在一起,真是今非昔比。

S5.
结束语 

我祖父于1943年去世,当时只有三十岁,父亲刚刚12岁。祖父出生的时候(1913年),第二次工业革命的绝大部分新技术和新产品都已发明出来并投入商业化使用,他去世的时候,西方发达国家已经进入第二次工业革命的尾声,但他连第一次工业革命也没有经历。他短暂的一生中吃的、穿的、用的与他的祖父时代没有什么区别。

父亲比祖父幸运,他和我一起经历了三次工业革命。他下半辈子吃的、穿的、用的与祖父在世时大不相同,也与他自己的前半辈子有很大不同。他坐过火车、飞机、汽车,在我写这篇文章时,也许正在看着电视、用着手机。

我比父亲更幸运,因为每次工业革命我都比他早几年经历。我坐火车比他早,坐飞机比他早,坐汽车比他早,看电视比他早,用手机比他早。我还会上网购物,他不会。

我的幸运是托中国市场化改革开放的福。正是改革开放,使得像我这样的普通中国人有机会享受到人类过去三百年的发明和创造,即便我自己并没有对这些发明和创造做出任何贡献。这或许就是经济学家讲的创新的“外溢效应”吧!生活在世界经济共同体,真是一件好事。

据说第四次工业革命已经在美国的引领下开始了。如果中国晚四十年改革开放,我就得从后半生开始,和我儿子一起同时经历四次工业革命。如果那样,我敢肯定,未来40年中国经济增长率会比过去40年的实际增长率还要高,更让世界瞩目。但我还是庆幸,历史没有这样进行。

作为经济学家,在享受三次工业革命成果的同时,我还是期待着我们的国家,能在未来第四次工业革命中做出原创性的技术贡献,而不再只是一个搭便车者。我知道,九泉之下的杨小凯先生会立马警告说,这要看中国能否走出“后发劣势”陷阱。

-R/l2SR

 

Posted in 社会生活 | Tagged , , , | Leave a comment

mysqld运行时参数配置文件与命令行有何异同?

1. 在 my.cnf 中配置:
interactive_timeout=300 
wait_timeout=300
重启 mysqld, 通过 show vairables
mysql> show variables like ‘%timeout%’;
+—————————–+———-+ | Variable_name | Value | +—————————–+———-+
| connect_timeout | 10 |
| delayed_insert_timeout | 300 |
| have_statement_timeout | YES |
| innodb_flush_log_at_timeout | 1 |
| innodb_lock_wait_timeout | 50 |
| innodb_rollback_on_timeout | OFF |
| interactive_timeout | 300 |
| lock_wait_timeout | 31536000 |
| net_read_timeout | 30 |
| net_write_timeout | 60 |
| rpl_stop_slave_timeout | 31536000 |
| slave_net_timeout | 60 |
| wait_timeout | 300 |
+—————————–+———-+
13 rows in set (0.00 sec)
能看到已经生效,但 mysqld 并不会在 300时杀掉 sleep 超过 300的连接。

2. 在 mysqld 的其中脚本 mysql.server 中增加:
other_args=”$other_args –explicit_defaults_for_timestamp –wait_timeout=300 –interactive_timeout=300″;
重启 mysqld 后, 通过 show variables 看, 参数无变化, 但当某个查询连接超过 300 时会被强行关闭。

为何? 配置文件中的指令不生效而命令行参数的指令则生效?

 

Posted in 服务器运维 | Leave a comment

超越前端Beyond Frontend — 吉密斯/gMIS 增加点选/PickUp概览功能

“….总是拿着微不足道的成就来骗自己….,
总是靠一点酒精的麻醉才能够睡去….”
— 台湾歌手郑智化《水手》歌词.

超越前端Beyond Frontend — 吉密斯/gMIS 增加点击勾选(点选)概览等功能侧记。
一直以来,gMIS吉密斯定位于管理操作后台、生产支撑、运营管控系统,主要面向于生产运营、管理操作人员,鲜有面向终端用户的机会,所以,设计考虑和实现权衡时,多是可用性,少有易用性。虽然多有追求美的勇气,终是在这方面难有建树。

直到写下这些文字,完成了gMIS具有标志意义的模块:点选/点击勾选/Pick Up/Click2Select. 从这一功能其,gMIS吉密斯已经具有超越一些前端设计交互能力的模块,强大且便捷。

S1.
基于数据库的管理系统主要功能是增删改查,其中的“查”字用的是最多的,可能1:9或者2:8于其他功能模块,也即1分量的增删改,对应着8~9分量的查,所以 gMIS吉密斯在“查”字上也下足功夫,目前主要的查询功能包括:

1. List页面试图顶部的快捷检索输入框;
(见下图)
2. “深度搜索查询”模块;

(gmis-deepsearch-with-quicksearch)
3. 站内搜索模块;
参考“-gMIS持续优化更新, +InSiteSearch站内搜索”,-R/52SN 。

这些模块应对的功能需求各部相同,前两个是基于单表查询,后一个是站内所有数据表“合适”字段的扫描。“点击勾选”已是“查”字上的第四个“搜索/查询”模块。

S2.
“点击勾选概览”在打开某一数据表的主体数据的同时,快速扫描主要字段,并对每一项字段按一定的规则进行汇聚,如统计去重数,计算区间等,然后生成一个针对该数据表的概览数据,并分别置为可供进一步筛选的“选项”,当用户点击某个“选项”时,下发列表数据自动做相应的更新,当有多个不同维度的“选项”被选择时,维度之间按“并且”求“交集”;当同一维度的多个选项被选中时,维度内选项按“或者”求“并集”——也即,当用户点击同一维度的两个选项时,我们理解为用户想要“A”或者“B”。

这里是对目前点击勾选实现的一次升级和超越——合并了多选和单选,集成到一个视图、同步完成,此其一;其二,在可供统计的维度,提供了聚类统计数据,对要浏览的数据能够一目了然,所谓“概览/概要浏览”。

(gmis-pickup-full-mode)

当我们被驱动要实现点击勾选时,我们对目前已经存在的实现做了一番考察。
1. 最先想到的是,几年前,我们用 GWA2 实现一个周边度假村数据检索的站点,其终端/前端页面对度假村的一些维度,实现了点选。用起来顺手,得意。@Shujuan
2. 点击勾选进入大众视野,并被广泛接受应该是电商平台的筛选功能,比如 -京东 , -天猫 等在商品列表页,均设计了点击勾选,按不同商品的不同维度,提供了各种筛选组合,其中将多选和单选分开进行。

(pickup-jingdong, 京东商品页面点选)

(pickup-tianmao, 天猫商品页面点选)

本质上,点击勾选/点选是为了减少用户输入的麻烦。这种繁琐来自两个方面:1)需要使用键盘操作,需要录入相应的关键词;2)有些情况下不知道目标关键词或者拼写错误等。

这次新增的“点击勾选概览”解决了这个麻烦,用户无需再手工录入相应的文字,依据“概览”提供的相应的线索,可以实现“按图索骥”一般,逐步筛选出预期的结果集。

S3.
与此前惯常见到的点选不同,gMIS 吉密斯的点选实现,还融合了多选与单选,不再显示地要求用户进行多选还是单选,当用户在同一筛选条件上选择一个“选项”时,我们理解为是单选,当用户选择两个以上的“选项”时,我们理解为用户是多选。如此简单、直白、有力,gMIS 吉密斯这一次超越了前端繁琐的多选、单选切换,融合单选、多选模式为一种操作模式。

(gmis-pickup-multiple-select)

单独做某一个/某一类的数据的拣选条件相对容易,正如电商平台里所列勾选条件一下,gMIS吉密斯需要一定的通用性,如何面对一个未知的数据集,能够在没有配置,第一次遇到时,就能够自动获得各种相应的具有统计意义的“概览”数据?

为实现这一点,我们将“数据”抽象为两大类——数值型和字符型。对于数值型的字段数据,我们设计了取值区段,并将可能的区段列出来供点选使用;对于字符型字段数据,我们使用枚举,并将尽可能多的记录的选项列在前排。有了这样的算法,对于任何给定的数据集,我们能够快速的排列出可供勾选的筛选条件列表。

这种做法,大多时是可靠的,尤其是对于数值型字段数据,在获得最小值Min和最大值Max之后,再辅以求值一个步长,很容易能够实现列出可供勾选的区段列表。对于字符型,也能够求得各个字段值的相应记录条数,尤其是一些枚举类型的字段,比如表现在管理后台的 select/选择项,或者 enum类型。

S4.
然而,我们不得不在工程实现时考虑异常,由于gMIS吉密斯的实现是通过页面 组装通信地址,然后通过GTAjax 递交给处理程序到服务器,然后将返回数据写入到指定区域,这种依赖JavaScript的功能实现,要考虑如何规避 “+”和空格,如何规避“'”, ‘”‘ 等敏感符号等,由于不能预先获知被管理数据类型(通用性要求),这些需要预先做兼容处理并Cover这样的场景。

于是,应对计算机符号问题的专家—— -Base62x 再次被排上用场。在生成候选选项列表时,对字符型的值进行 Base62x 编码(Server端PHP版本),前端点击触发相应 JavaScript 操作时,也使用同样的 Base62x编码(Client端JavaScript版本),当数据被递交回服务器端进行查询或显现时,又需要做解码操作,这一部分在 comm/header.inc 中完成. 有了这样的闭环操作,在管理页面上显示为明文的候选项,在通信和JavaScript代码层,则是Base62x编码后的无符号字符串,很好的规避了符号问题,类似的解决方案可参考:“-gMIS 吉密斯优化更新+分组项区段AddGroupBySeg/+复制AddByCopy等(-R/G2SW )” 提到的“注册动作registerAct: 改进增加 Base62x.class.js”。

S5.
额外地,点选与现有查询等功能的交互:1)在List顶部的导航快速搜索模块,当有选项被点选时,需要将对应的选项列入相应的输入框中,当List页面的右上角的“并搜”,“或搜”时,查询能够继续下去。2)当所点选的条件组合没有查询到相应结果时,gMIS吉密斯默认会给出当前查询条件组合并在每一个筛选条件上提供进一步地操作——去掉相应的条件做再一次的搜索尝试。这里就设计到快捷搜索和点选的交互。在此前的设计中,快捷搜索,当去掉一个搜索条件时,刷新List列表即可,而如果与点选结合,则需要进一步地更新点选的相关选项——选中或者未选中。

关于“选中/Selected”与“未选中/Unselected”,我们在设计时参考了在实现 -UFQI-News 的做法——当某个选项未被选中时,我们以常规的超级链接显示,并冠以加法符号“+”, 表述为:点击当前以“+”开头的链接表示在此前的语境上进一步的加上即将选择的内容;当某个选项已经被选中时,我们以背景色、前景色互换的方式将当前已经被选中的选项标识出来,并冠以“-”减法符号,表述为:点击当前以“-”开头的链接表示在当前语境中进一步地减去即将点选的内容。

(-UFQI-News)

-UFQI-News 的点选设计正符合这样的场景,于是就借用得来。当某个待点选的条件尚未被选择时,冠以加法符号,“+”,意为点击该选项,将在当前语境增加此条件;当某个已被选中的条件呈现时,冠以减法符号,“-”,意为点击该选项,将在当前语境减去此条件。大约符号语言或者“言简意赅”即是如此。

(gmis-selected-and-unselected)

S6.
通常情况下,可供使用的筛选条件维度,往往会有多个,默认为List页面所有显示的字段均提供了点击概览,通常这会慢慢地显示为一整屏幕,从而遮盖了List内容主体。为此,我们参考电商平台页面的筛选条件折叠功能,默认提供减缩模式,只列每个List页面的关键字段,也既List页面左侧的3-5个字段作为候选项列出来。
当遇有不适合作为候选字段的,则沿着List列表头部的字段往后顺延,凑齐了3-5个为算。

(gmis-pickup-full-mode)
当用户在默认减缩模式下无法满足检索需求时,可以点击右上角的“+更多”来显示全部可供使用的筛选条件。对应的,当用户在full-mode下点击右上角的“-更多”这可以收起富于的候选项,进入减缩模式。

S7.
gMIS吉密斯 点选PickUp功能带来变更模块摘要: 
1)comm/header.inc : + $base62xTag 及相应解码;
2)comm/ido.js : + fillPickUpReqt; 
3) + act/pickup.php 
4) + class/pickup.class.php 
5) ido.php: + doActionEx with act=pickup 
6) jdo.php: +doActionEx with act=pickup 
7) class/pagenavi: +containslist, inrangelist

一些继续的思考和改进:
1)针对一些字符型数值,如果按其值进行聚类统计,字符串截取太长则可能无意义,太短则又太笼统,根据小范围测试,目前默认值被设置为截取字符型数值的 240 字节;
2)针对数值型字段,其区间的划分,默认被划分为 12个区间;
3)字符型或选择型字段,如果候选项太多时,默认只显示前 12个选项。
这些默认值都有待进一步商榷。

 

gmis-logo-201606

-gMIS 是一种基于 -GWA2 的通用管理信息系统(Management Information System)应用软件,具有可配置的输入和输出接口、开箱即用等特征。
可以在其上构建各种管理应用软件系统,如
内容管理系统(CMS), 客户资源管理(CRM), 企业资源计划管理(ERP),
办公自动化系统(OA)等, 以及各种行业应用管理系统软件,如
人力资源管理系统(HR),学生管理,档案管理,旅游管理,图书管理,
商品管理及业务运营支撑系统(BOSS)等等。
实现零代码开发、快速搭建各种管理信息系统(MIS).

-gMIS is a -GWA2 based Management Information System (MIS) software with characteristics like configurable input and output interfaces, open-box-to-use.
Various management application software systems can be built on it, such as
Content Management System (CMS), Customer Resource Management (CRM), Enterprise Resource Planning Management (ERP),
Office automation systems (OA), as well as different industry application management system softwares, such as
Human Resource Management System (HR), Student Management, Archive Management, Tourism Management, Book Management,
Commodity management and business operations support systems (BOSS), etc.
With zero code development, -gMIS can build a set of management information systems (MIS) software in a few minutes.

Lower Costs, 
Better Productivity.
降低成本,
提高效率.

“…看看可爱的天,摸摸真实的脸,
…想想长长的路,擦擦脚下的鞋…”
——电视剧《外来妹》主题曲

 

Posted in -gMIS, Base62x, 计算机技术 | Tagged , , , , , , | Leave a comment