♻️ refactor(playbook): streamline agents and refresh tsl docs

This commit is contained in:
csh
2026-01-10 16:55:44 +08:00
parent b529012c59
commit 5822a8736d
88 changed files with 103561 additions and 130104 deletions
+127 -304
View File
@@ -5,47 +5,33 @@
## 目录
- [#网格计算操作符](#网格计算操作符)
- [内容](#内容)
- [#网格计算操作符简介](#网格计算操作符简介)
- [网格计算案例](#网格计算案例)
- [网格计算代入系统参数的案例](#网格计算代入系统参数的案例)
- [网格计算设置任务超时时间](#网格计算设置任务超时时间)
- [TSL 全局缓存的应用说明](#tsl全局缓存的应用说明)
- [内容](#内容-1)
- [TSL 全局缓存的应用说明](#tsl-全局缓存的应用说明)
- [全局缓存管理](#全局缓存管理)
- [内容](#内容-2)
- [全局缓存管理的函数](#全局缓存管理的函数)
- [全局缓存的使用](#全局缓存的使用)
- [全局缓存的过期与回收策略](#全局缓存的过期与回收策略)
- [全局缓存管理的初始化和监控](#全局缓存管理的初始化和监控)
- [未升级的系统对新代码的使用](#未升级的系统对新代码的使用)
- [初始化 TSL 和监控管理的 TSL](#初始化tsl和监控管理的tsl)
- [内容](#内容-3)
- [初始化 TSL 和监控管理的 TSL](#初始化-tsl-和监控管理的-tsl)
- [功能设计](#功能设计)
## #网格计算操作符
### 内容
- #网格计算操作符简介
- 网格计算案例
- 网格计算代入系统参数的案例
- 网格计算设置任务超时时间
### #网格计算操作符简介
什么是网格计算?
随着计算技术的发展,计算机已经得到了大规模普及,计算资源随处可见。另一方面,随着 CPU 制程技术提高的门槛越来越高,制造工艺成为了单一 CPU 内核计算性能提升的瓶颈。CPU 的主流发展从主频的提高逐步向多核迈进,而在应用层面上,以往单服务器计算逐渐被集群式计算所代替。在处理数据规模越来越大,计算模型越来越复杂的趋势下,传统单任务串行计算越来越成为了技术瓶颈,对于多 CPU 多内核以及多服务器群而言,成为了计算资源的巨大浪费,这个时候,对并行计算的研究成为了未来发展的主流,而网格计算则成为了并行计算研究中的热点
并行计算在超算中心的大型主机应用中被广泛应用,依靠硬件技术,成千上万台主机被联接成为一个大型主机,这样使得在其上的开发对于用户而言,后台成千上万的主机只是一台拥有成千上万个计算核心的计算,用户不用关注数据的同步,用户也不用关注计算将派发到哪里。但是超算中心的建设和使用成本都非常巨大,而且还受到超算中心的数据管理麻烦的制约。
普通 PC 服务器的 CPU 核心数都非常有限,单机并行计算无法充分利用起计算机资源,这个时候,网格计算就孕育而生了。
网格计算用于并行执行函数调用,把任务分发到可用计算资源并异步返回句柄。适用于大量独立任务的批量计算;对强顺序依赖的流程不适用
网格使用格式:
R[i]:=#函数名(参数…) with array(系统参数列表…) timeout N;
```tsl
R[i] := #函数名(参数…) with array(系统参数列表…) timeout N;
```
其中,with 语句可指定网格运算子程中的系统参数,此输入可省
timeout
@@ -59,45 +45,26 @@ N 为指定网格运算子程序运行的超时时间,若运行超过设定时
例如:
```text
A:=Array();
B:=Array('SZ000001','SZ000002','SH600000');
for i:=0 to length(B)-1 do
begin
A[I]:=
###
CalcStock(B[I]);
end;
//--对结果进行访问,用来等待所有网格运行完成,并得到运行结果
r:=array();
for j:=0 to length(A)-1 do
r[j]:=dupvalue(A[j]);//复制网格子程序结果-》即等待网格运行完成
Return r;
```tsl
A := array();
B := array('SZ000001', 'SZ000002', 'SH600000');
for i := 0 to length(B) - 1 do
begin
A[i] := #CalcStock(B[i]);
end;
// 对结果进行访问,用来等待所有网格运行完成并取得结果
r := array();
for j := 0 to length(A) - 1 do r[j] := dupvalue(A[j]); // 复制网格子程序结果
return r;
```
//---需要被网格调用的函数 CalcStock
需要被网格调用的函数 `CalcStock`
```text
Function CalcStock(StockId);
Begin
//…………………
End;
```tsl
function CalcStock(StockId);
begin
// ...
end;
```
### 网格计算代入系统参数的案例
@@ -106,12 +73,11 @@ CalcStock(B[I]);
例如:
```text
A[i]:=#Close()
with Array(pn_Stock():StockId,pn_Date():ToDay()-1)
```tsl
A[i] := #Close() with array(pn_stock():StockId, pn_date():Today() - 1);
```
就可以获得指定股票昨的收盘价\*实际中我们不会用网格计算调用收盘价函数,因为网格存在开销那样的效率会更低,这只是一个例子
就可以获得指定股票昨的收盘价。实际使用时不建议用网格计算调用收盘价函数,这里仅作示例
### 网格计算设置任务超时时间
@@ -120,47 +86,34 @@ N 对该子进程进行设置超时时间,若网格运行的程序运行时间
如有网格运行目标程序:
```text
Function testdo();
```tsl
function TestDo();
begin
sleep(10*1000);
return getsysparam(pn_stock());
sleep(10 * 1000);
return getsysparam(pn_stock());
end;
```
在网格中设置超时间为 3 秒,调用如下:
```text
r:=# testDo() timeout 3000;
t:=dupvalue(r);
return t;
```tsl
r := #TestDo() timeout 3000;
t := dupvalue(r);
return t;
```
在网格中通过 with 传入系统参数的同时设置超时间为 3 秒,调用如下:
```text
r:=# testDo() with array(pn_stock():"SZ000002") timeout 3000;
t:=dupvalue(r);
return t;
```tsl
r := #TestDo() with array(pn_stock():"SZ000002") timeout 3000;
t := dupvalue(r);
return t;
```
超时报错 Grid timeout,示例如下:
## TSL 全局缓存的应用说明
### 内容
- 全局缓存管理
- 初始化 TSL 和监控管理的 TSL
### 全局缓存管理
TSL 设计的全局缓存是一套极为高效的内存缓存机制,其以 COPYONWRITE 的模式实现了设置和写入完全分离,允许存在多份使用中的版本,使得更新数据和读数据无冲突。
@@ -175,48 +128,25 @@ TSL 的全局缓存主要应用于数组和矩阵两种类型,也支持其他
由于我们对 TSL 全局缓存的透明处理,因为普通函数无法分辨全局缓存还是标准数据类型,除非采用特殊的函数。
#### 内容
- 全局缓存管理的函数
- 全局缓存的使用
- 全局缓存的过期与回收策略
- 全局缓存管理的初始化和监控
- 未升级的系统对新代码的使用
#### 全局缓存管理的函数
##### 内容
- SetGlobalCache
- GetGlobalCache
- CheckGlobalCacheExpired
- GetGlobalCacheInfo
- ListGlobalCache
- ListGlobalCacheRemoved
- IfCache
##### SetGlobalCache
范例
```text
a:=rand(1000,100);
return SetGlobalCache("LLL",a,now()+1); //设置一个名为”LLL”的全局缓存,生存周期为1天
```tsl
a := rand(1000, 100);
return SetGlobalCache("LLL", a, now() + 1); // 设置一个名为”LLL”的全局缓存,生存周期为1天
```
##### GetGlobalCache
范例
```text
```tsl
mtic;
for i:=0 to 999999 do
getglobalCache("LLL",V);
return array(V,mtoc);
for i := 0 to 999999 do getglobalCache("LLL", V);
return array(V, mtoc);
```
这个例子告诉我们,对于全局缓存数据,无论全局缓存数据本身多大,例如这个是 1000\*1000 的矩阵,获取 100 万次花费的时间也是微乎其微的。这样我们的数据准备工作所耗费的时间几乎就不存在了。
@@ -225,36 +155,28 @@ mtic;
范例
```text
Setglobalcache("VVV",rand(1000,100));
Getglobalcache("VVV",V);
expired1:=CheckGlobalCacheExpired(V);
setglobalcache("VVV",rand(1000,100));//重置,V过期
return array("重置前":expired1,"重置后":CheckGlobalCacheExpired(V));
```tsl
Setglobalcache("VVV", rand(1000, 100));
Getglobalcache("VVV", V);
expired1 := CheckGlobalCacheExpired(V);
setglobalcache("VVV", rand(1000, 100)); // 重置,V过期
return array("重置前":expired1, "重置后":CheckGlobalCacheExpired(V));
```
##### GetGlobalCacheInfo
范例
```text
if GetGlobalCache("LLL",V) then
return GetGlobalCacheInfo(V);
```tsl
if GetGlobalCache("LLL", V) then return GetGlobalCacheInfo(V);
```
##### ListGlobalCache
范例
```text
getglobalcache("LLL",v);
```tsl
getglobalcache("LLL", v);
return listglobalcache();
```
@@ -266,32 +188,24 @@ Owners 列里是缓存使用中的用户列表
范例 1
```text
SetGlobalCache("CCC",array(1,2,3));
Getglobalcache("CCC",V); //v指向全局缓存
setglobalcache("CCC",array(1,2,3,4));//v的版本已经过期
return listglobalcacheremoved();
```tsl
SetGlobalCache("CCC", array(1, 2, 3));
Getglobalcache("CCC", V); // v指向全局缓存
setglobalcache("CCC", array(1, 2, 3, 4)); // v的版本已经过期
return listglobalcacheremoved();
```
存在一份 CCC 的过期版本。
范例 2
```text
SetGlobalCache("CCC",array(1,2,3));
Getglobalcache("CCC",V); //v指向全局缓存
setglobalcache("CCC",array(1,2,3,4));//V已经过期
//return listglobalcacheremoved();
V:=nil;//V释放了没有过期版本
return listglobalcacheremoved();
```tsl
SetGlobalCache("CCC", array(1, 2, 3));
Getglobalcache("CCC", V); // v指向全局缓存
setglobalcache("CCC", array(1, 2, 3, 4)); // V已经过期
// return listglobalcacheremoved();
V := nil; // V释放了没有过期版本
return listglobalcacheremoved();
```
返回结果:空数组。
@@ -300,117 +214,71 @@ SetGlobalCache("CCC",array(1,2,3));
#### 全局缓存的使用
##### 内容
- 全局缓存的基础函数和子矩阵的支持
- 全局缓存的算符支持
- 支持 SELECT
- 写入实例化
##### 全局缓存的基础函数和子矩阵的支持
绝大多数的函数已经可以完全支持全局缓存,当成和原始数据类型对待,而子矩阵这类的操作也毫无问题。
```text
a:=array();
for i:=1 to 9 do
for j:=1 to 9 do
a[i-1,j-1]:=i*j;
setglobalcache("99MT",a);
Getglobalcache("99MT",V);
return array(sum(sum(V)),length(V),mcols(V),ifarray(V),V[8,8],V[0:3,0:3]);
```tsl
a := array();
for i := 1 to 9 do for j := 1 to 9 do
a[i - 1, j - 1] := i * j;
setglobalcache("99MT", a);
Getglobalcache("99MT", V);
return array(sum(sum(V)), length(V), mcols(V), ifarray(V), V[8, 8], V[0:3, 0:3]);
```
##### 全局缓存的算符支持
对于四则运算等算符,以及矩阵运算符,还有集合运算符号等等,全局缓存和原始类型一致。
```text
a:=array();
for i:=1 to 9 do
for j:=1 to 9 do
a[i-1,j-1]:=i*j;
setglobalcache("99MT",a);
Getglobalcache("99MT",V);
return V+100;
```tsl
a := array();
for i := 1 to 9 do for j := 1 to 9 do
a[i - 1, j - 1] := i * j;
setglobalcache("99MT", a);
Getglobalcache("99MT", V);
return V + 100;
```
##### 支持 SELECT
对于 SELECT 而言,全局缓存的表现和其原始数据没有任何差异。
```text
a:=array();
for i:=1 to 9 do
for j:=1 to 9 do
a[i-1,j-1]:=i*j;
setglobalcache("99MT",a);
Getglobalcache("99MT",V);
return select * from V order by [0] desc end;
```tsl
a := array();
for i := 1 to 9 do for j := 1 to 9 do
a[i - 1, j - 1] := i * j;
setglobalcache("99MT", a);
Getglobalcache("99MT", V);
return select * from V order by [0] desc end;
```
##### 写入实例化
当对全局缓存进行各式写入操作时,无论是数据设置,还是 UPDATE,INSERT 等 SQL 操作,我们均会将全局缓存实例化,在用户使用的时候和传统数据复制后进行写入操作毫无差异,这样最大化地保证了易用性。
```text
a:=array();
for i:=1 to 9 do
for j:=1 to 9 do
a[i-1,j-1]:=i*j;
setglobalcache("99MT",a);
Getglobalcache("99MT",V);
b:=ifcache(V);//是缓存 ,b为真
V[0,0]:=100; //设置完成后,ifcache就为假了
return array(b,ifcache(V),V);
```tsl
a := array();
for i := 1 to 9 do for j := 1 to 9 do
a[i - 1, j - 1] := i * j;
setglobalcache("99MT", a);
Getglobalcache("99MT", V);
b := ifcache(V); // 是缓存 ,b为真
V[0, 0] := 100; // 设置完成后,ifcache就为假了
return array(b, ifcache(V), V);
```
使用 SQL 的 Update 更新全局缓存也引发数据的实例化
```text
a:=array();
for i:=1 to 9 do
for j:=1 to 9 do
a[i-1,j-1]:=i*j;
setglobalcache("99MT",a);
Getglobalcache("99MT",V);
b:=ifcache(V);//是缓存 ,b为真
update v set [0]=1 end; //update后V也不再是globalcache
return array(b,ifcache(V),V);
```tsl
a := array();
for i := 1 to 9 do for j := 1 to 9 do
a[i - 1, j - 1] := i * j;
setglobalcache("99MT", a);
Getglobalcache("99MT", V);
b := ifcache(V); // 是缓存 ,b为真
update v set [0] = 1 end; // update后V也不再是globalcache
return array(b, ifcache(V), V);
```
#### 全局缓存的过期与回收策略
@@ -423,10 +291,6 @@ TSL 为全局缓存建立了一套回收规则,结合了系统的剩余内存
一旦遇到这类的问题,用户应该检查模型使用这些全局缓存是否存在问题。由于全局缓存的获取效率以及使用效率均极高,用户不应该将全局缓存放在系统变量,TSL 的 GLOBAL 存贮等等中,用户应尽量直接使用缓存,并审慎长期占用缓存的模式。
##### 内容
- 配置
##### 配置
配置在 plugin\FileMgr.ini 中
@@ -445,14 +309,6 @@ ExpiredLoadLimit=5 //允许过期的全局缓存占用的物理内存百分比
ExpiredAvailLimit=16777216 //允许过期的全局缓存占用的物理内存大小,单位 KB
###### 内容
- MemoryLoadLimit
- MemoryAvailLimit
- ExpiredSecondsCheck
- ExpiredLoadLimit
- ExpiredAvailLimit
###### MemoryLoadLimit
单位:百分数
@@ -485,28 +341,18 @@ ExpiredAvailLimit=16777216 //允许过期的全局缓存占用的物理内存大
#### 全局缓存管理的初始化和监控
##### 内容
- 有瑕疵的全局缓存管理方式
- 期望的方式
- 不推荐的模式
- 全局缓存的初始化
- 全局缓存的更新监控
##### 有瑕疵的全局缓存管理方式
全局缓存的生成,一种模式是在由应用模型内来设置:
例如: if not GetGlobalCache(CacheName,V) then
Begin
V:=CalcDataCall();
SetGlobalCache(Cache,V);
End;
```tsl
begin
V := CalcDataCall();
SetGlobalCache(Cache, V);
end;
```
但这样存在几个问题:
一是全局缓存的设置是需要权限的,一旦采用这样的模式,代码只能运行在高权限下。
@@ -515,32 +361,29 @@ ExpiredAvailLimit=16777216 //允许过期的全局缓存占用的物理内存大
三是我们很难知道何时适合于进行缓存的准备工作以及缓存的更新工作,因为缓存总有失效的时候,如果要解决失效问题,我们还得将代码变成如下:
if not GetGlobalCache(CacheName,V) or IsDataNeedReCalc(V) then
Begin
V:=CalcDataCall();
SetGlobalCache(Cache,V);
End
```tsl
if not GetGlobalCache(CacheName, V) or IsDataNeedReCalc(V) then
begin
V := CalcDataCall();
SetGlobalCache(Cache, V);
end;
```
我们需要在 IsDataNeedReCalc 里来检查诸如外部数据的版本是否发生了变更等工作,往往这种检查对事件的耗费远远大于全局缓存的获取,这样又会影响到用户模型的效率。
##### 期望的方式
如果全局缓存的生成和更新,交由系统,那么我们期望的应用开发是如下模式:
if not GetGlobalCache(CacheName,V) then
V:=CalcDataCall();
```tsl
if not GetGlobalCache(CacheName, V) then V := CalcDataCall();
```
由于全局缓存系统管了生成,所以我们只需要取即可,如果系统未生成,我们直接进入计算模式。
##### 不推荐的模式
GetGlobalCache(CacheName,V);
```tsl
GetGlobalCache(CacheName, V);
```
有的开发者会在升级应用后,将取数程序变成了最简单的模式,假设缓存的获得会成功,这样在项目实施中并无不可,假设数据的初始化和数据变更确定性由系统其他部分完成了,这样也带来了数据底层来源和上层应用分离的优势。
但在产品开发中,我们并不推荐如此模式,因为这样会带来了新的问题。
@@ -583,17 +426,8 @@ GetGlobalCache(CacheName,V);
注:初始化 TSL 只运行一次,而监控的线程不会退出,当 TSL 运行完毕后会重新调用运行。
#### 内容
- 功能设计
#### 功能设计
##### 内容
- WEB 模块的初始化和监控
- 平台的初始化和监控
##### WEB 模块的初始化和监控
Apache 的模块,设置在 TSL.INI 中。
@@ -604,11 +438,6 @@ automonthreads=1 //监控线程的个数
initrun=1 //是否运行初始化 TSL
###### 内容
- 初始化
- 监控及管理线程
###### 初始化
初始化 InitRun.TSL 位于进程或者模块所在目录,进程所在目录优先。
@@ -636,13 +465,6 @@ Automonthreads 设置的是启动的监控管理线程的数量。
监控进程目前并未支持调用网格计算。
###### 内容
- 初始化
- 监控及管理线程
- 平台的初始化和监控 TSL 的管理
- 平台的初始化和监控的权限管控
###### 初始化
初始化的启动是通过参数-i 来设定的,例如 Exec64.exe -i 则表明需要启动初始化。
@@ -672,6 +494,7 @@ Automonthreads 设置的是启动的监控管理线程的数量。
###### 平台的初始化和监控的权限管控
如果初始化和监控 TSL 需要调用内外部的 TSL 函数来进行数据准备,设计者推荐用户采用 data:=sudo("modeluser",getcalcdata())的模式来进行数据的一些准备工作,因为 GetCalcData()这类的函数往往不需要任何特殊权限,这样可以最大限度地防止非授权代码的运行。
```tsl
// 如果初始化和监控 TSL 需要调用内外部的 TSL 函数来进行数据准备,设计者推荐用户采用 data:=sudo("modeluser",getcalcdata())的模式来进行数据的一些准备工作,因为 GetCalcData()这类的函数往往不需要任何特殊权限,这样可以最大限度地防止非授权代码的运行。
```
如果我们仅仅只是利用初始化和监控进行一些系统性操作,设计者强烈建议不需要使用一些中间函数,将除了二进制函数外的实现直接在.TSL 里完成,这样做可以让这些代码可以独立运行。如果无法保障这一点,强烈建议将无需权限运行的内容以 sudo 模式来运行。