2025年8月

过去的20-30年,经济发展,物价飞涨,人们生活水平明显提高。手里捂着的哪些东西变值钱了呢,感觉最深的无非房子、股票这些,如果重生到1995年,你会买哪些东西作为长期投资?

首先是房地产,1995年时,北京的房价差不多3、4千一平米,2022年达到了10万一平米,涨幅25倍;如果是四合院,那涨幅能有上百倍。但是投资房地产,需要大量现金,很多人达不到门槛,总不能只投资一个厕所吧。虽然总共只需要50-100万的资金,但是那时的工资都还不到每月1000元。

1995年北京房价

门槛低的投资物品,有古董,珠宝,奢侈品,白酒等等,我们简单看一下他们的涨幅:

古董除非捡漏入手,如果以市场价交易,20多年涨幅约20倍,前提是不要打眼,买到能升值的;珠宝里面拿翡翠钻石为例,只有高端翡翠才有投资价值,极品翡翠20多年的涨幅大概是50-100倍,但是极品翡翠讲究缘分,不是想买就有得买的,而且圈子不大,个人交易或者拍卖都很麻烦;钻石的涨幅只有几倍,可以忽略。

琳琅满目的投资物品

奢侈品和邮票,不是一个长期稳定的投资品,邮票除了个别像81年猴票那样的奇葩,涨幅10万倍,其余大概是几倍到10倍,而且邮票收藏圈子更小,价格并不是一直稳步增长,到一定时间就停止甚至回落,并且很难出手套现。

80庚申年猴票

奢侈品更适合短期投资,而且需要相当的专业知识,根据市场不断更新轮换,像劳力士绿水鬼、迪通拿这些热度很高的涨幅小几倍,也不适合长期捂在手里等升值,最近不就大跌了么。

劳力士绿水鬼

再来说说白酒,1995年飞天茅台260一瓶,一个月工资能买2-3瓶;现在3000一瓶,一个月工资也差不多能买2-3瓶,很友好。

贵州茅台

有没有涨幅特别高的投资品呢?有!1990年海南黄花梨2000元一吨,现在900万一吨,涨价4500倍!这是由于其稀缺性和它的实用价值,加上一部分炒作的成分。回顾下来确实是一个值得投资的东西,但红木中也就唯它独尊,其它品种就差远了。

海南黄花梨海南黄花梨

最后说说股票,20-30年上市到现在暴涨的股票:恒瑞医药涨价200倍以上,贵州茅台200倍,伊利股份300倍以上,山西汾酒超过400倍,云南白药近500倍,泸州老窖超800倍,福耀玻璃1700倍,格力电器2400倍,万科涨了2900倍。股票投资门槛低,资金灵活,以价值投资的角度,选择前景好的公司长期持有,不在意短时的起起落落,或多或少,总有丰厚的回报。

最后讲个笑话,1995年一个人斥巨资屯了一批BP机,结果发现不断降价,他就不断补货降低成本,有人收购也不肯卖,10年后退市了;2005年有人大学填报了BP机维修专业,结果还没毕业就失业了。炒股的朋友手里有一直下跌需要不断补仓的股票,前景不看好的就果断割肉了吧;炒ST股的,如果没有对标的的深度研究和自信判断,还是不要去碰运气了,不然就像别人说的,在连续剧里活不过第2集。

年代久远的BP机


webapi数据处理过程:

1. 首先对接受到的客户端数据包中每个字段值进行解析(解析分为取数,赋值,触发值更新事件,调用表单服务等)

2. 解析完,调用APP层操作服务(其中保存接口还会走mvc层中维护插件的beforeSave和aftereSave事件,其他接口不会走维护插件事件)

3. APP层操作服务,主要对数据进行验证,触发操作插件事件,执行关联的业务流程(比如是否走反写引擎)等。

4. 最后,KSql引擎解析sql,通过DB层把数据保存或更新到数据库。


页面数据处理过程:

1.首先用户点击菜单,mvc层接受到菜单动作,触发相应的维护插件事件,调用菜单对应的业务层操作, 业务层的操作服务根据各种配置执行相关的业务逻辑,比如保存后是否提交,提交是否触发工作流等。

2. 业务层的操作服务同样要调用对应的APP操作服务,App层操作服务跟webapi调用的相同,

3. 调用完了APP层服务可能还有其他业务处理,比如提交后是否审核等。


webapi数据处理过程 VS 页面数据处理过程主要区别

1. webapi需要对字段值进行解析,页面不需要

2. webapi不会走业务逻辑层,页面会走


根据上面区别,我们建议可以从下面几点来做性能优化:

1. 服务端收到消息后首先对接收到的客户端数据包中每个字段值进行解析(解析分为取数,赋值,触发值更新事件,调用表单服务等),所以尽可能精简Josn数据包里面的字段。

2. 尽量使用批量保存代替保存接口,因为保存接口会多走beforeSave和aftereSave事件,并且每次只能处理一条数据, 而批量保存接口不仅可以批量处理还可以开后台线程并发执行。需要注意的是如果开启了多个线程并发调用批量保存接口(一般3到5个即可),那么批量保存接口参数里面BatchCount(服务端开启线程数)建议设置为1或2,否则可能造成服务器端请求阻塞。举个例子,5个线程并发调用,BatchCount设置为5,那么服务器端会产生25个线程并发处理,这种情况很容易造成阻塞。

3.业务层面在WebAPI调用时候,一些取价服务、取折扣服务可以考虑禁用,具体参考销售订单、销售出单相关说明:

https://vip.kingdee.com/article/11179

https://vip.kingdee.com/article/42071

4.避免每次调用增删查改接口之前都调用登录接口(会话默认超时是20分钟,没有必要每次都调用登录接口,登录接口本身也是一个耗时的操作)。一次登录多次使用简单示例

https://vip.kingdee.com/knowledge/650363039845203200?specialId=650386937144032256

5. 也是最重要的一点,星空的请求默认是同步模式,是有状态的。在同一个会话下,前一个请求没有结束,后一个请求即使发起了,也是要等待的。如果是不同的会话,相互间就没有影响。第三方集成调用接口,如果在本地做会话保持,并且用单用户调用,比较容易在客户端出现卡顿。可以考虑使用多端多用户多站点(尽量批量调用接口缓解并发调用压力)的方式来调用星空的接口。例如可以在不同的端发起星空的请求,不同端发起请求可以使用同一个用户;如果在一个端发起请求,则建议对请求进行业务区分,不要用一个用户调用所有请求,需要在集成端使用多个用户来调用星空的接口,缓解同一会话导致请求排队的问题。另外也可以增加多站点的调用机制来解决请求排队的问题。



作者:王文亮

来源:金蝶云社区

原文链接:https://vip.kingdee.com/knowledge/590559077445315584?specialId=650386937144032256&productLineId=1&isKnowledge=2&lang=zh-CN

著作权归作者所有。未经允许禁止转载,如需转载请联系作者获得授权。


【应用场景】


WebAPI登录一次默认会保持20分钟会话,如果20分钟内会调用其他接口,会话时间会重新算;同一用户调用接口,建议只登录一次,后续接口调用后判断返回消息代码,如果是消息代码等于1可以尝试重新登录后继续调用接口;

如果每次调用接口前都登录一次,大量调用后服务器上会有大量的上下文实例得不到及时释放,从而可能导致服务器内存高涨。



【案例演示】

通过webapi保存客户,一次登录后多次调用保存接口。


【实现步骤】

<1>创建控制台 , 引用Kingdee.BOS.WebApi.Client和Newtonsoft.Json。 

<2>代码示例。

using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Net.Http; using System.Text; namespace Zlf.WebApiDemo {     class Program     {         static void Main(string[] args)         {             Console.WriteLine("保存开始..");             string url = "http://localhost/k3cloud/";             var httpInvoke = new WebApiHttpInvoke(url, "67468ad9fb64d4", "demo", "2969888_47aD5+sL6kH8WV8u6eSASbzLTs7aRoLL", "566381bd78d64d83b6a2f9be05ccd01e");             for (int i = 0; i < 10; i++)             {                 string billNo = string.Format("20241129-{0}", i + 1);                 httpInvoke.Save("BD_Customer", billNo, CreateJson(billNo));             }             Console.WriteLine("保存完成..");             Console.ReadLine();         }         private static string CreateJson(string billNo)         {             var data = new             {                 IsVerifyBaseDataField = true,                 Model = new                 {                     FCreateOrgId = new                     {                         FNumber = "100"                     },                     FNumber = billNo,                     FUseOrgId = new                     {                         FNumber = "100"                     },                     FName = billNo,                     FCOUNTRY = new                     {                         FNumber = "China"                     },                     FIsDefPayer = false,                     FIsGroup = false,                     FCustTypeId = new                     {                         FNumber = "KHLB001_SYS"                     },                     FTRADINGCURRID = new                     {                         FNumber = "PRE001"                     },                     FTRANSLEADTIME = 0                 }             };             return JsonConvert.SerializeObject(data);         }     } }

<3>WebApiHttpInvoke.cs代码。

using Kingdee.BOS.WebApi.Client; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Zlf.WebApiDemo {     class WebApiHttpInvoke     {         private readonly string _dbId, _userName, _appId, _appSecret;         private readonly object _obj = new object();         private readonly K3CloudApiClient _client;         private bool _isLogin;         public WebApiHttpInvoke(string url, string dbId, string userName, string appId, string appSecret)         {             this._dbId = dbId;             this._userName = userName;             this._appId = appId;             this._appSecret = appSecret;             _client = new K3CloudApiClient(url, 10 * 60);//超时时间设为10分钟         }         //保存单据         public void Save(string formId, string billNo, string data, bool isRetry = false)         {             string saveResult = _client.Save(formId, data);             var responseStatus = JObject.Parse(saveResult)["Result"]["ResponseStatus"];             var isSuccess = responseStatus["IsSuccess"].Value<bool>();             if (!isSuccess)             {                 var msgCode = responseStatus["MsgCode"].Value<int>();                 if (msgCode == 1)//会话丢失,可能未登录或者过期了                 {                     if (isRetry)                     {                         return;                     }                     _isLogin = false;                     Login(); //重新登录                     Save(formId, billNo, data, true);                 }                 else                 {                     //暂存单据                     Console.WriteLine("保存失败,返回结果:{0}", saveResult);                 }             }             else             {                 Console.WriteLine("保存成功,billNo={0} ", billNo);             }         }         /// <summary>         /// 登录操作         /// </summary>         /// <returns></returns>         private bool Login()         {             lock (_obj)             {                 if (!_isLogin)                 {                     //使用第三方系统登录授权                     var loginResult = _client.LoginByAppSecret(this._dbId, this._userName, this._appId, this._appSecret, 2052);                     if (loginResult.Contains("response_error"))                     {                         //系统异常,包括账套id错误                         throw new Exception(loginResult);                     }                     var resultType = JObject.Parse(loginResult)["LoginResultType"].Value<int>();                     if (resultType == 1)                     {                         _isLogin = true;                         return true;                     }                     throw new Exception(loginResult);                 }             }             return true;         }     } }

【功能验证】

<1>运行代码。

上传图片

<2>启用WebAPI日志 , 可以看到只调用一次登录。

上传图片

上一篇:WebAPI性能优化建议   下一篇:使用多用户多站点调用WebAPI接口简单示例

作者:cyoukon

来源:金蝶云社区

原文链接:https://vip.kingdee.com/knowledge/650363039845203200?specialId=650386937144032256&productLineId=1&isKnowledge=2&lang=zh-CN

著作权归作者所有。未经允许禁止转载,如需转载请联系作者获得授权。


【应用场景】

同一个用户身份使用多线程调用WebAPI接口,默认是排队的,只有前面接口返回了结果,后一个接口才能继续处理 ;这是IIS的机制,相同的 session id的时候超过一定数量的时候会出现排队现象,但是我们可以使用多用户多站点调用来提高调用效率。



【注意事项】

线程不是越多越好,应根据具体应用场景和系统资源来权衡。



【案例演示】

使用4个用户2个站点调用WebAPI接口保存客户。


【实现步骤】

<1>基于帖子https://vip.kingdee.com/article/650363039794871552?productLineId=1&isKnowledge=2&lang=zh-CN的代码做修改。 

<2>代码如下。

using Newtonsoft.Json; using System; using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; namespace Zlf.WebApiDemo {     class Program     {         const string AppId = "296988_47aD5+sL6kH8WV8u6eSASbzLTs7aRoLL";         const string AppSecret = "566381bd78d64d83b6a2f9be05cdd01e";         const string K3cloudUrl = "http://localhost/k3cloud/";//k3cloud站点         const string K3cloudAppUrl = "http://localhost/k3cloudApp/";//k3cloudapp站点         const string DbId = "67468ad9fb6464";         static readonly ConcurrentDictionary<string, WebApiHttpInvoke> DicUser = new ConcurrentDictionary<string, WebApiHttpInvoke>();         static int _currentUserIndex = -1;         static void Main(string[] args)         {         //本示例创建了4个用户实例,其中用了2个站点。这里采用异步的方式调用,确保4个实例是可以并行同步数据。             Console.WriteLine("保存开始..");             DicUser["demo_k3cloud"] = new   WebApiHttpInvoke(K3cloudUrl, DbId, "demo", AppId,AppSecret);             DicUser["demo2_k3cloudapp"] = new WebApiHttpInvoke(K3cloudUrl, DbId, "demo2", AppId,AppSecret);             DicUser["demo3_k3cloud"] = new WebApiHttpInvoke(K3cloudAppUrl, DbId, "demo3", AppId,AppSecret);             DicUser["demo4_k3cloudapp"] = new WebApiHttpInvoke(K3cloudAppUrl, DbId, "demo4", AppId,AppSecret);             Task[] tasks = new Task[10];             for (int i = 0; i < tasks.Length; i++)             {                 int n = i;                 tasks[i] = Task.Run(() => DoSave(string.Format("20241129-{0}", n)));             }             Task.WhenAll(tasks).Wait();             Console.WriteLine("全部保存完成..");             Console.ReadLine();         }         private static void DoSave(string billNo)          {             var instance = GetInstance();             instance.Save("BD_Customer", billNo, CreateJson(billNo));         }         /// <summary>         /// 按轮询方式获取WebApiHttpInvoke实例         /// </summary>         /// <returns></returns>         private static WebApiHttpInvoke GetInstance()         {             _currentUserIndex = (_currentUserIndex + 1) % DicUser.Count;             var kvp = DicUser.ElementAt(_currentUserIndex);             if (kvp.Key == null || kvp.Value == null)             {                 throw new Exception("GetInstance error");             }             return kvp.Value;         }         private static string CreateJson(string billNo)         {             var data = new             {                 IsVerifyBaseDataField = true,                 Model = new                 {                     FCreateOrgId = new                     {                         FNumber = "100"                     },                     FNumber = billNo,                     FUseOrgId = new                     {                         FNumber = "100"                     },                     FName = billNo,                     FCOUNTRY = new                     {                         FNumber = "China"                     },                     FIsDefPayer = false,                     FIsGroup = false,                     FCustTypeId = new                     {                         FNumber = "KHLB001_SYS"                     },                     FTRADINGCURRID = new                     {                         FNumber = "PRE001"                     },                     FTRANSLEADTIME = 0                 }             };             return JsonConvert.SerializeObject(data);         }     } }

<2>WebApiHttpInvoke.cs代码如下。

using Kingdee.BOS.WebApi.Client; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Zlf.WebApiDemo {     class WebApiHttpInvoke     {         private readonly string _dbId, _userName, _appId, _appSecret;         private readonly object _obj = new object();         private readonly K3CloudApiClient _client;         private bool _isLogin;         public WebApiHttpInvoke(string url, string dbId, string userName, string appId, string appSecret)         {             this._dbId = dbId;             this._userName = userName;             this._appId = appId;             this._appSecret = appSecret;             _client = new K3CloudApiClient(url, 10 * 60);//超时时间设为10分钟         }         //保存单据         public void Save(string formId, string billNo, string data, bool isRetry = false)         {             string saveResult = _client.Save(formId, data);             var responseStatus = JObject.Parse(saveResult)["Result"]["ResponseStatus"];             var isSuccess = responseStatus["IsSuccess"].Value<bool>();             if (!isSuccess)             {                 var msgCode = responseStatus["MsgCode"].Value<int>();                 if (msgCode == 1)//会话丢失,可能未登录或者过期了                 {                     if (isRetry)                     {                         return;                     }                     _isLogin = false;                     Login(); //重新登录                     Save(formId, billNo, data, true);                 }                 else                 {                     //暂存单据                     Console.WriteLine("保存失败,返回结果:{0}", saveResult);                 }             }             else             {                 Console.WriteLine("保存成功,billNo={0} ", billNo);             }         }         /// <summary>         /// 登录操作         /// </summary>         /// <returns></returns>         private bool Login()         {             lock (_obj)             {                 if (!_isLogin)                 {                     //使用第三方系统登录授权                     var loginResult = _client.LoginByAppSecret(this._dbId, this._userName, this._appId, this._appSecret, 2052);                     if (loginResult.Contains("response_error"))                     {                         //系统异常,包括账套id错误                         throw new Exception(loginResult);                     }                     var resultType = JObject.Parse(loginResult)["LoginResultType"].Value<int>();                     if (resultType == 1)                     {                         _isLogin = true;                         return true;                     }                     throw new Exception(loginResult);                 }             }             return true;         }     } }

【功能验证】

<1>运行代码。

上传图片


<1>查看WebAPI日志,确认可以多用户并发保存。


上传图片


上一篇:WebAPI一次登录多次调用示例   下一篇:启用webapi日志

作者:cyoukon

来源:金蝶云社区

原文链接:https://vip.kingdee.com/knowledge/650388592971234560?specialId=650386937144032256&productLineId=1&isKnowledge=2&lang=zh-CN

著作权归作者所有。未经允许禁止转载,如需转载请联系作者获得授权。