♾好好与这个世界对话: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.
降低成本,
提高效率.

 

发表在 -gMIS, 编程技术, 计算机技术 | 标签为 , , , , , , | 留下评论

老板选址BossXuanzhiUfqiFina有福金融UfqiWork有福工坊UfqiNews有福常在

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

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

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

宏观上分析。

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

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

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

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

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

微观上分析。

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

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

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

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

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

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

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

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

发表在 服务器运维, 计算机技术 | 标签为 , | 留下评论

UfqiNews有福常在Google Books亮月亮科技Apache Softwares

🍃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
轻松启动, 快速产出.

 

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

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

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编码方案的理想替代品、继任者。

 

发表在 Base62x, 编程技术, 计算机技术 | 标签为 , , , , | 2条评论

老板选址BossXuanzhiUfqiFina有福金融UfqiWork有福工坊UfqiNews有福常在

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 带来了一定的障碍。
但是,已有的问题,在没解决之前,会再次发生,这是确定的。

发表在 -GWA2, 服务器运维, 计算机技术 | 标签为 , , , , | 一条评论

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

🛠️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 , 
全国招投标信息中心 .

发表在 -GWA2, -Hanjst/-汉吉斯特, 编程技术, 计算机技术 | 标签为 , , , , , | 2条评论

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