这么设计参数,新人不掉坑里才怪

最近遇到2个非常真实的案例,一个参数控制2种不同的业务,虽然业务有所关联,但是严格的来讲,又属于两种不同的业务,导致花费了不少时间去梳理与配置。这让我不得不思考一下如何设计参数防止新人踩坑。真实的案例,真实的感受。

案例一 开始时间 结束时间

2个业务A、B都有开始时间与结束时间,业务场景一【开始时间X,结束时间Y】使用业务A,其他时间使用业务B,业务场景二【开始时间X,结束时间Y】使用业务B,其他时间使用业务A。

如果设计参数的时候,仅设计2个参数开始时间与结束时间,还允许出现参数配置中出现开始时间大小结束时间的配置方式,的确,2个参数满足了业务配置。这块参数配置就像是人为挖了一个坑,一个简单的配置,变成了一个烧脑的配置。

做为新人来讲,做梦也难以猜到一个配置影响2个业务,还能配置成开始时间大于结束时间,这都不符合常理。这直接导致花费了许久的时间与读代码,还耗费了技术经理的时间,这对于项目的可维护性来讲,就是毁灭性的打击。

案例二 一个执行时间2个业务用

2个业务A、B,一个参数配置,业务A取执行时间的小时部分,每小时执行,业务B取小时与分钟,每天执行一次。比如执行时间18:10,那业务A则每个小时的第10分钟执行,业务B则在每天的18:10分执行。

看起来好的设计与实现,周末都在为业务排查出了什么问题。不看代码,难以想像一个参数控制了2个业务,还有这么精致的规则在里面。不踩坑都难。如果我们简单的用2个参数分别来控制业务A、B,何坑之有?

小结

我们并不是在一个电脑内存只有128k的时代,我们关心的不是参数能不能复用,我们关心项目的可维护性。感觉每个人写代码的思维方式真的是完全不一样,条条大路通罗马。站在可维护性的角度,坚决抵制一参多业务的编程习惯。

发表在 未分类 | 留下评论

存储过程也能玩可选参数 Stored Procedure可选参数实现方案

create proc yucbtest  @i int=nullas beginif @i is not nullbegin print @iendelse print 'aaaa'endgoexec yucbtest 1

今天要改造一个现有的存储过程,但是不想在刷存储过程与发布程序的时间窗口里出现崩溃事件,由是找DBA要一个可选存储参数的可选参数玩法。果真有效~也就毫无保留的分享给粉丝朋友们!使用可选参数,部署程序更加平稳,不容易出现失败异常。在这里也是5星级推荐!

部署的时候一定要记住先更新存储过程,让存储过程支持可选参数,再去部署程序。如果颠倒过来,则会出现程序里给多了参数,会报错。

Too many parameters were provided

如果不使用可选参数的存储过程,不管怎么部署,都会出问题。先刷了存储过程,会报有参数未提供。先部署了程序,又会报参数提供过多。怎么都绕不开崩溃。所以优选可选参数,按照文章开头的方式就能实现自己想要的可选参数!

学到了不? 记得给点个赞~不会的请留言,包教包会~

发表在 未分类 | 留下评论

快速将大脚本分拆成多份并发执行脚本 巧用命令 更快更爽

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!

关注公众号,进入学海无涯,高人带路模式!!​split​再难,有人带路,轻松搞定

命令比较晦涩

split -l 2 1.txt -d -a 4 data_&&ls|grep data_|xargs -n1 -i{} mv {} {}.sh

$ split --helpUsage: split [OPTION]... [FILE [PREFIX]]Output pieces of FILE to PREFIXaa, PREFIXab, ...;default size is 1000 lines, and default PREFIX is 'x'.With no FILE, or when FILE is -, read standard input.Mandatory arguments to long options are mandatory for short options too.  -a, --suffix-length=N   generate suffixes of length N (default 2)      --additional-suffix=SUFFIX  append an additional SUFFIX to file names  -b, --bytes=SIZE        put SIZE bytes per output file  -C, --line-bytes=SIZE   put at most SIZE bytes of records per output file  -d                      use numeric suffixes starting at 0, not alphabetic      --numeric-suffixes[=FROM]  same as -d, but allow setting the start value  -e, --elide-empty-files  do not generate empty output files with '-n'      --filter=COMMAND    write to shell COMMAND; file name is $FILE  -l, --lines=NUMBER      put NUMBER lines/records per output file  -n, --number=CHUNKS     generate CHUNKS output files; see explanation below  -t, --separator=SEP     use SEP instead of newline as the record separator;                            '\0' (zero) specifies the NUL character  -u, --unbuffered        immediately copy input to output with '-n r/...'      --verbose           print a diagnostic just before each                            output file is opened      --help     display this help and exit      --version  output version information and exitThe SIZE argument is an integer and optional unit (example: 10K is 10*1024).Units are K,M,G,T,P,E,Z,Y (powers of 1024) or KB,MB,... (powers of 1000).CHUNKS may be:  N       split into N files based on size of input  K/N     output Kth of N to stdout  l/N     split into N files without splitting lines/records  l/K/N   output Kth of N to stdout without splitting lines/records  r/N     like 'l' but use round robin distribution  r/K/N   likewise but only output Kth of N to stdoutGNU coreutils online help: <http://www.gnu.org/software/coreutils/>Report split translation bugs to <http://translationproject.org/team/>Full documentation at: <http://www.gnu.org/software/coreutils/split>or available locally via: info '(coreutils) split invocation'
不要造低级的轮子,这个轮子一点都不低级,如果自己去写代码的话,也好实现,不过,有现成的工具就用上吧,而且直接使用git bash就能完成脚本拆分。
这已经实现了昨天所期望的自动拆分脚本,而脚本生成,执行进度监控并没有实现,不过,感觉已经比较爽了。一个适度的平衡点吧!split -l 2 1.txt -d -a 4 data_&&ls|grep data_|xargs -n1 -i{} mv {} {}.sh 这轮子顶很多很多行代码了,如果写个程序进行大文本拆成多份文本,想想得多少行,这么一行命令就按定

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

巧用正则+Chrome彩蛋功能=快速生成cUrl脚本 fetch、cUrl cmd、cUrl bash、PowerShell

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!学会一招神技能​

今天遇到一个需要批量调接口的场景,从数据库中查出上万条数据,再按接口规则生成带get参数的url请求,还要带登陆状态。

一、从数据库中捞出数据

将查出来的数据贴到notepad++中,大概是这样子的数据,示范一下

1  a  112  b  223  c  334  d  445  e  556  f  667  g  778  h  889  i  99

将空格换成%20,这个直接把空格换掉就是,示例数据没有演示这个场景,自行脑补一下。

二、使用chrome生成一个带cookie的请求

操作截图如上图所示,是不是很方便,cookies都带上了,有登陆状态,调接口相当方面。

curl "http://yue.ma/?i=9&m=6&p=11" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecure

三、使用正则替换,生成批量脚本

制作一个脚本正则模板

curl "http://yue.ma/?i=$1&m=$2&p=$3" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecure

使用notepad++替换功能

查找目标填写:

^([^\t]+)\t([^\t]+)\t([^\t]+)$

替换为填写:

curl "http://yue.ma/?i=$1&m=$2&p=$3" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecure

生成脚本

curl "http://yue.ma/?i=1&m=a&p=11" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=2&m=b&p=22" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=3&m=c&p=33" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=4&m=d&p=44" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=5&m=e&p=55" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=6&m=f&p=66" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=7&m=g&p=77" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=8&m=h&p=88" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecurecurl "http://yue.ma/?i=9&m=i&p=99" -H "Connection: keep-alive" -H "Cache-Control: max-age=0" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: Mozilla/5.0 Windows NT 10.0; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/78.0.3904.108 Safari/537.36" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: zh-CN,zh;q=0.9" -H "Cookie: __51cke__=; __tins__18251311=^%^7B^%^22sid^%^22^%^3A^%^201575548726427^%^2C^%^20^%^22vd^%^22^%^3A^%^203^%^2C^%^20^%^22expires^%^22^%^3A^%^201575551216664^%^7D; __51laig__=3" --compressed --insecure
执行脚本,我使用git bash执行的,直接新建一个bash.sh,把生成的代码放进去,执行脚本
./bash.sh
等着几万条调用执行吧!no no no,数据过多,可以拆成多份bash脚本执行,可以加快速度。这种执行方式有一个好处就是,不需要额外去写程序,直接一顿replace生成脚本跑起来。不好的就是命令太多的话,执行并没有进度显示。
可以写个辅助的脚本执行工具,自动拆份脚本并行执行,显示显示进度什么的。嗯嗯,学会一下^([^\t]+)\t([^\t]+)\t([^\t]+)$ 与chrome的脚本生成功能,非常不错!

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

FileBeat CPU占用率过高Case处理 | max_procs: 1

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!Filebeat​再难,有人带路,轻松搞定

Case: Filebeat内存占用率过高
处理​方案
max_procs: 1
问题渐进明细,遇到这种别人的东西CPU占用率过高,是不太好下手的​。解决这个问题的思路有2个,1是网上找踩过坑的资料,2是再现一下问题,寻找​思路。
一、找资料
FileBeat出现cpu占用过高,有说是FileBeat版本与kafka版本不一致,也有说监控文件过多,没有及时inactive的,还有就是限制c​pu使用数量的。最开始找资料的时候,并没有太看到限制cpu核数的​。所以​踩坑还是一踩到底。
二、再现问题
2.1 LogAuto模拟大量日志
为了重现问题,在本地启动一个Filebeat,输出为控制台,采集目录有多个,写了一个日志生成小工具。
using System;using System.Collections.Generic;using System.Configuration;using System.IO;using System.Linq;using System.Text;using System.Threading;namespace LogAuto{    class Program    {        static void Main(string[] args)        {            String siteName = ConfigurationManager.AppSettings["siteName"]??"default Site";            // 模拟 100个站点            List<LogThread> logThreadPool = new List<LogThread>();            for(int i=0;i<100;i++){                LogThread logThread = new LogThread();                logThread.logTag = String.Format("{0}", i);                logThread.siteName = siteName;                logThreadPool.Add(logThread);                new Thread(new ThreadStart(logThread.WriteLog)).Start();            }        }    }}
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;namespace LogAuto{    class LogThread    {        public String logTag { get;set;}        public String siteName { get;set;}        public void WriteLog() {            while (true)            {                File.AppendAllText(String.Format("{0}.log.log", logTag), String.Format("产生一条日志 {0} {1}\r\n", siteName, DateTime.Now));                Console.WriteLine(String.Format("{2}输出一条日志:{0} {1}", siteName, DateTime.Now, logTag));                //Thread.Sleep(1000);            }        }    }}

​不管启多少个LogAuto,生成日志,filebeat采集日志,完全看不出Filebeat有什么不爽,于是,​这个模拟算是失败了。连多行日志都没有模拟出来,也没有让Filebeat​有任何压力。

​2.2 模拟大量线上日志

using System;using System.Collections.Generic;using System.Configuration;using System.Linq;using System.Net;using System.Text;using System.Threading;namespace LoadTest{    class Program    {        static void Main(string[] args)        {            int threadCount = int.Parse(ConfigurationManager.AppSettings["ThreadCount"] ?? "1000");            int threadSleep = int.Parse(ConfigurationManager.AppSettings["ThreadSleep"] ?? "1000");            String targetUrl = ConfigurationManager.AppSettings["TargetUrl"] ?? "http://yue.ma/?fbtest";            List<Thread> logThreadPool = new List<Thread>();            for (int i = 0; i < threadCount; i++)            {                Thread tread = new Thread((m) =>                {                    while (true) {                        try                        {                            WebClient wc = new WebClient();                            String data = wc.DownloadString(String.Format("{0}?r={1}", targetUrl, DateTime.Now));                            Console.WriteLine(String.Format("线程 {0} 返回结果 {1} 时间 {2}", m, data, DateTime.Now));                            Thread.Sleep(threadSleep);                        }                        catch (Exception ex) {                            Console.WriteLine(ex.Message);                        }                    }                });                logThreadPool.Add(tread);                tread.Start(i);            }            Console.WriteLine("输入任意字符退出");            Console.ReadKey();        }    }}

​启动了多个LoadTest实例,线程数都1000,压力是上去了​。发现FileBeat没有任何压力,完全不对​劲。查看FileBeat日志,filebeat压根就没有工作,kafka\logstash\elasticsearch统统没有工作。

​ 2.3 完整的日志收集环境+模拟大量线上日志

​这里要推荐linux下实用的history命令,可以查看前辈们使用的命令​。

​history

      从history命令历史中去揣摩命令,其实是一种乐趣,但是效率不高。再次分享一下前辈总结出来的命令清单

    ​    ​玩日志系统必备的17条操作命令|启动zookeeper kafka logstash storm 查看日志 我手上最珍贵的命令集

    ​测试结果压力一上去,w2wp.exe占用的cpu自己都能跑满100%,有没有filebeat,这cpu都能到100%,开着filebeat,filebeat能抢走40%到80%的cpu,随着日志压力大小会有所变化。

    ​2.4 完整的日志收集环境+模拟大量线上日志+Filebeat限制cpu

    ​当业务忙的时候,日志也多,这个时候,日志收集​就不要抢戏了。做为测试环境,配置​低,就限制1个cpu内核。开启压测后,显示Filebeat的cpu被限制在20%以下,不会出现Filebeat抢占过高的业务​资源。

​​小结:本次Filebeat占用CPU过高的case处理,最终的结论是max_procs: 1,为了这13个字符,设计了多种压测环境,回过头来想想,其实一开始就想限制cpu使用,万万没有想到是max_procs​。资源是有限的, cpu、内存,都是这样子​。嗯,cpu就这么控制一下​!​

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

已知公众号原始id反查公众号信息|这种神操作也可以 再也不用怕忘记公众号了 用公众号原始id直接生成公众号二维码 谁才会忘公众号

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!公众号二维码再难,有人带路,轻松搞定

问题:已知公众号原始Id:gh_e289dcf40338,求公众号信息

解法:

格式:

https://open.weixin.qq.com/qr/code?username=公众号原始id

拼出来

https://open.weixin.qq.com/qr/code?username=gh_e289dcf40338

效果图

怎么产生的问题?

不知道谁会忘记公众号信息,这也是很奇怪的!能记住公众号的原始id却不记得公众号基本信息?哈哈!

二维码连接好处

通过这种方式生成公众号的二维码有一个好处,不用再存储图片了。图片的大小远大小于这行链接。样式也是微信官方的样式!

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

银企直连|平安银行与招商银行的银企直连有种神似 还是熟悉的玩法 前置机、报文、加密解密、古老的报文 高门槛高服务费 对接传统银行

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!银企直连再难,有人带路,轻松搞定

招商银行银企直连怎么收费

您好,银企直联主要是可以将企业网银与企业财务软件连接的方式,一般企业申请银企“直联”会收取服务费:开通10000元,每月基本服务费200元,或按协议价格收取。通过银企直联转账手续费同企业网银支付功能收费。

这个收费标准是否已经让一波小伙伴放弃了银企直连,如果每家银行都这个标准来算的话,全部接下来,成本也不少。这就是第三方支付的生存意义了。结合之前写的平安银行的银企直连,再来看招商银行的,会发现,基本是一相通的,莫非出自一个外包公司之手?

温故而知新

平安银行对接|银企对接扫雷排坑实战经验分享 感受一下财大气粗的银行对接 感受一下等长报文的痛苦 未曾经历无以感受他人在坑中崩溃状

熟悉的前置机

对比平安银行的对接玩法,再来看招商银行的银企直连,又看到了这么一个熟悉的前置机”神兽”,这跟支付宝对接、微信对接的玩法,出入相当的大。

demo

java版demo就4个文件HttpRequest.java、SaxHandler.java、SocketRequest.java、XmlPacket.java

import java.io.*;import java.net.*;import java.util.Map;import java.util.Properties;/** *  * HTTP通讯范例: 直接支付 *  */public class HttpRequest {  /**   * 生成请求报文   *    * @return   */  private String getRequestStr() {    // 构造支付的请求报文    XmlPacket xmlPkt = new XmlPacket("Payment", "USRA01");    Map mpPodInfo = new Properties();    mpPodInfo.put("BUSCOD", "N02031");    xmlPkt.putProperty("SDKPAYRQX", mpPodInfo);    Map mpPayInfo = new Properties();    mpPayInfo.put("YURREF", "201009270001");    mpPayInfo.put("DBTACC", "571905400910411");    mpPayInfo.put("DBTBBK", "57");    mpPayInfo.put("DBTBNK", "招商银行杭州分行营业部");    mpPayInfo.put("DBTNAM", "NEXT TEST");    mpPayInfo.put("DBTREL", "0000007715");    mpPayInfo.put("TRSAMT", "1.01");    mpPayInfo.put("CCYNBR", "10");    mpPayInfo.put("STLCHN", "N");    mpPayInfo.put("NUSAGE", "费用报销款");    mpPayInfo.put("CRTACC", "571905400810812");    mpPayInfo.put("CRTNAM", "测试收款户");    mpPayInfo.put("CRTBNK", "招商银行");    mpPayInfo.put("CTYCOD", "ZJHZ");    mpPayInfo.put("CRTSQN", "摘要信息:[1.01]");    xmlPkt.putProperty("SDKPAYDTX", mpPayInfo);    return xmlPkt.toXmlString();  }  /**   * 连接前置机,发送请求报文,获得返回报文   *    * @param data   * @return   * @throws MalformedURLException   */  private String sendRequest(String data) {    String result = "";    try {      URL url;      url = new URL("http://localhost:8080");      HttpURLConnection conn;      conn = (HttpURLConnection) url.openConnection();      conn.setRequestMethod("POST");      conn.setDoInput(true);      conn.setDoOutput(true);      OutputStream os;      os = conn.getOutputStream();      os.write(data.toString().getBytes("gbk"));      os.close();      BufferedReader br = new BufferedReader(new InputStreamReader(conn          .getInputStream()));      String line;      while ((line = br.readLine()) != null) {        result += line;      }      System.out.println(result);      br.close();    } catch (MalformedURLException e) {      e.printStackTrace();    } catch (UnsupportedEncodingException e) {      e.printStackTrace();    } catch (ProtocolException e) {      e.printStackTrace();    } catch (IOException e) {      e.printStackTrace();    }    return result;  }  /**   * 处理返回的结果   *    * @param result   */  private void processResult(String result) {    if (result != null && result.length() > 0) {      XmlPacket pktRsp = XmlPacket.valueOf(result);      if (pktRsp != null) {        String sRetCod = pktRsp.getRETCOD();        if (sRetCod.equals("0")) {          Map propPayResult = pktRsp.getProperty("NTQPAYRQZ", 0);          String sREQSTS = (String) propPayResult.get("REQSTS");          String sRTNFLG = (String) propPayResult.get("RTNFLG");          if (sREQSTS.equals("FIN") && sRTNFLG.equals("F")) {            System.out.println("支付失败:"                + propPayResult.get("ERRTXT"));          } else {            System.out.println("支付已被银行受理(支付状态:" + sREQSTS + ")");          }        } else if (sRetCod.equals("-9")) {          System.out.println("支付未知异常,请查询支付结果确认支付状态,错误信息:"              + pktRsp.getERRMSG());        } else {          System.out.println("支付失败:" + pktRsp.getERRMSG());        }      } else {        System.out.println("响应报文解析失败");      }    }  }  public static void main(String[] args) {    try {      HttpRequest request = new HttpRequest();      // 生成请求报文      String data = request.getRequestStr();      // 连接前置机,发送请求报文,获得返回报文      String result = request.sendRequest(data);      // 处理返回的结果      request.processResult(result);    } catch (Exception e) {      System.out.println(e.getMessage());    }  }}
import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;import java.util.*;/** *  * 招行XML报文解析类 * */public class SaxHandler extends DefaultHandler {  int layer=0;  String curSectionName;  String curKey;  String curValue;  XmlPacket pktData;  Map mpRecord;    public SaxHandler(XmlPacket data){    curSectionName = "";    curKey = "";    curValue = "";    pktData = data;    mpRecord = new Properties();  }  public void startElement(String uri, String localName, String qName,      Attributes attributes) throws SAXException {    layer++;    if(layer==2){      curSectionName = qName;    }else if(layer==3){      curKey = qName;    }  }  public void endElement(String uri, String localName, String qName)      throws SAXException {    if(layer==2){      pktData.putProperty(curSectionName, mpRecord);      mpRecord = new Properties();    }else if(layer==3){      mpRecord.put(curKey, curValue);      if(curSectionName.equals("INFO")){        if(curKey.equals("FUNNAM")){          pktData.setFUNNAM(curValue);        }else if(curKey.equals("LGNNAM")){          pktData.setLGNNAM(curValue);        }else if(curKey.equals("RETCOD")){          pktData.setRETCOD(curValue);        }else if(curKey.equals("ERRMSG")){          pktData.setERRMSG(curValue);        }      }    }    curValue = "";    layer--;  }  public void characters(char[] ch, int start, int length)      throws SAXException {    if(layer==3){      String value = new String(ch, start, length);      if(ch.equals("\n")){        curValue += "\r\n";      }else{        curValue += value;      }    }  }}
import java.io.*;import java.net.*;import java.util.*;/** * SOCKET通讯范例:查询账户信息 */public class SocketRequest {  /**   * 生成请求报文   *    * @return   */  private String getRequestStr() {    // 构造查询余额的请求报文    XmlPacket xmlPkt = new XmlPacket("GetAccInfo", "USRA01");    Map mpAccInfo = new Properties();    mpAccInfo.put("BBKNBR", "57");    mpAccInfo.put("ACCNBR", "571905400610301");    xmlPkt.putProperty("SDKACINFX", mpAccInfo);    return xmlPkt.toXmlString();  }  /**   * 连接前置机,发送请求报文,获得返回报文   *    * @param data   * @return   */  private String sendRequest(String data) {    // 连接前置机:Ip + port    String hostname = "localhost";    int port = 8080;    String result = null;    try {      InetAddress addr = InetAddress.getByName(hostname);      Socket socket = new Socket(addr, port);      // 设置2分钟通讯超时时间      socket.setSoTimeout(120 * 1000);      DataOutputStream wr = new DataOutputStream(socket.getOutputStream());      // 通讯头为8位长度,右补空格:先补充8位空格,再取前8位作为报文头      String strLen = String.valueOf(data.getBytes().length) + "        ";      wr.write((strLen.substring(0, 8) + data).getBytes());      wr.flush();      DataInputStream rd = new DataInputStream(socket.getInputStream());      // 接收返回报文的长度      byte rcvLen[] = new byte[8];      rd.read(rcvLen);      String sLen = new String(rcvLen);      int iSum = 0;      try {        iSum = Integer.valueOf(sLen.trim());      } catch (NumberFormatException e) {        System.out.println("报文头格式错误:" + sLen);      }      if (iSum > 0) {        System.out.println("响应报文长度:" + iSum);        // 接收返回报文的内容          int nRecv = 0, nOffset = 0;        byte[] rcvData = new byte[iSum];// data        while (iSum > 0) {          nRecv = rd.read(rcvData, nOffset, iSum);          if (nRecv < 0)            break;          nOffset += nRecv;          iSum -= nRecv;        }        result = new String(rcvData);        System.out.println("响应报文内容:" + result);      }      wr.close();      rd.close();    } catch (java.net.SocketTimeoutException e) {      System.out.println("通讯超时:" + e.getMessage());    } catch (IOException e) {      System.out.println(e.getMessage());    }    return result;  }  /**   * 处理返回的结果   *    * @param result   */  private void processResult(String result) {    if (result != null && result.length() > 0) {      XmlPacket pktRsp = XmlPacket.valueOf(result);      if (pktRsp != null) {        if (pktRsp.isError()) {          System.out.println("取账户信息失败:" + pktRsp.getERRMSG());        } else {          Map propAcc = pktRsp.getProperty("NTQACINFZ", 0);          System.out.println("账户" + propAcc.get("ACCNBR") + "的联机余额:"              + propAcc.get("ONLBLV"));        }      } else {        System.out.println("响应报文解析失败");      }    }  }  public static void main(String[] args) {    try {      SocketRequest request = new SocketRequest();      // 生成请求报文      String data = request.getRequestStr();      // 连接前置机,发送请求报文,获得返回报文      String result = request.sendRequest(data);      // 处理返回的结果      request.processResult(result);    } catch (Exception e) {      System.out.println(e.getMessage());    }  }}
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.util.*;import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import java.io.ByteArrayInputStream;import org.xml.sax.SAXException;/** *  * 招行XML通讯报文类 * */public class XmlPacket{  protected String FUNNAM;  protected final String DATTYP="2";//报文类型固定为2  protected String LGNNAM;  protected String RETCOD;  protected String ERRMSG;  protected Map data; //<String,Vector>    public XmlPacket(){    data = new Properties();  }    public XmlPacket(String sFUNNAM){    FUNNAM = sFUNNAM;    data = new Properties();  }    public XmlPacket(String sFUNNAM, String sLGNNAM){    FUNNAM = sFUNNAM;    LGNNAM = sLGNNAM;    data = new Properties();  }    public String getFUNNAM() {    return FUNNAM;  }  public void setFUNNAM(String fUNNAM) {    FUNNAM = fUNNAM;  }  public String getLGNNAM() {    return LGNNAM;  }  public void setLGNNAM(String lGNNAM) {    LGNNAM = lGNNAM;  }  public String getRETCOD() {    return RETCOD;  }  public void setRETCOD(String rETCOD) {    RETCOD = rETCOD;  }  public String getERRMSG() {    return ERRMSG;  }  public void setERRMSG(String eRRMSG) {    ERRMSG = eRRMSG;  }    /**   * XML报文返回头中内容是否表示成功   * @return   */  public boolean isError(){    if(RETCOD.equals("0")){      return false;    }else{      return true;    }  }    /**   * 插入数据记录   * @param sSectionName   * @param mpData <String, String>   */  public void putProperty(String sSectionName, Map mpData){    if(data.containsKey(sSectionName)){      Vector vt = (Vector)data.get(sSectionName);      vt.add(mpData);    }else{      Vector vt = new Vector();      vt.add(mpData);      data.put(sSectionName, vt);      }      }    /**   * 取得指定接口的数据记录   * @param sSectionName   * @param index 索引,从0开始   * @return Map<String,String>   */  public Map getProperty(String sSectionName, int index){    if(data.containsKey(sSectionName)){      return (Map)((Vector)data.get(sSectionName)).get(index);    }else{      return null;    }  }    /**   * 取得制定接口数据记录数   * @param sSectionName   * @return   */  public int getSectionSize(String sSectionName){    if(data.containsKey(sSectionName)){      Vector sec = (Vector)data.get(sSectionName);      return sec.size();    }    return 0;  }    /**   * 把报文转换成XML字符串   * @return   */  public String toXmlString(){    StringBuffer sfData = new StringBuffer(        "<?xml version='1.0' encoding = 'GBK'?>");    sfData.append("<CMBSDKPGK>");    sfData        .append("<INFO><FUNNAM>"+FUNNAM+"</FUNNAM><DATTYP>"+DATTYP+"</DATTYP><LGNNAM>"+LGNNAM+"</LGNNAM></INFO>");    int secSize = data.size();    Iterator itr = data.keySet().iterator();    while(itr.hasNext()){      String secName = (String)itr.next();      Vector vt = (Vector)data.get(secName);      for(int i=0; i<vt.size(); i++){        Map record = (Map)vt.get(i);        Iterator itr2 = record.keySet().iterator();        sfData.append("<"+secName+">");        while(itr2.hasNext()){          String datakey = (String)itr2.next();          String dataValue = (String)record.get(datakey);          sfData.append("<"+datakey+">");          sfData.append(dataValue);          sfData.append("</"+datakey+">");        }        sfData.append("</"+secName+">");      }    }    sfData.append("</CMBSDKPGK>");        return sfData.toString();  }    /**   * 解析xml字符串,并转换为报文对象   * @param message   */  public static XmlPacket valueOf(String message) {    SAXParserFactory saxfac = SAXParserFactory.newInstance();    try {      SAXParser saxparser = saxfac.newSAXParser();      ByteArrayInputStream is = new ByteArrayInputStream(message.getBytes());      XmlPacket xmlPkt= new XmlPacket();      saxparser.parse(is, new SaxHandler(xmlPkt));      is.close();      return xmlPkt;    } catch (ParserConfigurationException e) {      e.printStackTrace();    } catch (SAXException e) {      e.printStackTrace();    } catch (IOException e) {      e.printStackTrace();    }      return null;  }}

C#版demo

相对java版,C#版的demo好歹是一个工程,用vs可以直接打开。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net;using System.Net.Sockets;using System.IO;namespace CSharpTest{    class Program    {        static void Main()        {            //报文内容,用户自己根据接口组装            string strSendData = "<?xml version=\"1.0\" encoding = \"GBK\"?><CMBSDKPGK><INFO><FUNNAM>SDKCSFDFBRTIMG</FUNNAM><DATTYP>2</DATTYP><LGNNAM>QGZ01</LGNNAM></INFO><CSRRCFDFY0><EACNBR>216082647110001</EACNBR><BEGDAT>20170426</BEGDAT><ENDDAT>20170526</ENDDAT><RRCFLG>1</RRCFLG><RRCCOD>HD002016</RRCCOD></CSRRCFDFY0></CMBSDKPGK>";            //前置机部署的IP地址            string strIP = "127.0.0.1";            //Socket传输方式前置机的监听端口            int iSocketPort = 1080;            //Http传输方式前置机的监听端口            int iHttpPort = 8080;            string strReciveData = string.Empty;            Console.WriteLine("请选择通讯方式:1.Socket     2.HTTP");            string strType = Console.ReadLine();            if (strType.Equals("1"))            {                strReciveData = Send_Socket(strSendData, strIP, iSocketPort);            }            else if (strType.Equals("2"))            {                strReciveData = Send_Http(strSendData, strIP, iHttpPort);            }            else            {                Console.WriteLine("输入有误!");            }            Console.WriteLine(strReciveData);            Console.WriteLine("按Enter键退出!");            Console.ReadLine();        }        static private string Send_Socket(string strSendData, string strIP, int iPort)        {            string strDataLen = Convert.ToString(Encoding.Default.GetBytes(strSendData).Length).PadLeft(8, '0');    //长度8字节左补0            string sReturn = string.Empty;            IPAddress ip = IPAddress.Parse(strIP);    //前置机IP地址            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 30 * 1000);            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 30 * 1000);            try            {                socket.Connect(new IPEndPoint(ip, iPort));                byte[] dataToBeSend = Encoding.Default.GetBytes(strDataLen + strSendData);                socket.Send(dataToBeSend);                byte[] receivedData = new byte[3000];                int recvLen = socket.Receive(receivedData);                if (recvLen == 0)                {                    throw new Exception("返回数据为空");                }                sReturn = Encoding.Default.GetString(receivedData, 0, recvLen);            }            catch (Exception ex)            {                sReturn = ex.Message;            }            if (socket != null)            {                socket.Close();            }            return sReturn;        }        static private string Send_Http(string strSendData, string strIP, int iPort)        {            string strUrl = "http://" + strIP + ":" + iPort.ToString();    //前置机的地址和监听端口            string sReturn = string.Empty;            try            {                byte[] byteArray = Encoding.Default.GetBytes(strSendData);                HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(new Uri(strUrl));                webReq.Method = "POST";                webReq.ContentType = "application/x-www-form-urlencoded";                webReq.Timeout = 30 * 1000;                webReq.ContentLength = byteArray.Length;                Stream newStream = webReq.GetRequestStream();                newStream.Write(byteArray, 0, byteArray.Length);                newStream.Close();                HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.Default);                sReturn = sr.ReadToEnd();                sr.Close();                response.Close();                newStream.Close();            }            catch (Exception ex)            {                sReturn = ex.Message;            }            return sReturn;        }    }}

 

招商银行的网银直连文档可以直接在官方查到
https://u.ebank.cmbchina.com/CmbBank_GenShell/UI/Help/DCBank2/Main.aspx
 对比招商银行的直连文档、再看看平安银行的直连文档,再对比第三方支付提供的sdk,对接体验,嗯嗯,还是第三方好!

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

一点都不打脸的帮同事搜索|自己不会,但是可以搜索啊 搜索也是需要技巧的,有时候也需要运气!千万不要鄙视那些搜索出结果的学习小伙伴

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!问题再难,有人问可以搜索,轻松搞定

一点都不打脸

的帮同事搜索|自己不会,但是可以搜索啊 搜索也是需要技巧的,有时候也需要运气!千万不要鄙视那些搜索出结果的学习小伙伴!

搜索一下,没有主角光坏也不行,没有运气也不行!今天搜索nginx为什么抢不到80端口就是纯靠运气!

今天算是一个运气比较好的,又陷入了趣事回忆中。

N前年,满怀信心的帮人配置Lync机器人,拿出自己总结的小经验,准备秀一把,预想是分分钟钟就能搞定的事情!
到现场,凉菜了
域环境有问题
机器人配置不成功
于是开始了一场搜索行动
立即将服务器机器人改成客户端机器人
凌晨2点才搞定
 
 
真的很惊险
今天的往事回忆篇完美收工!

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

nginx启动失败的case处理经验|前端竟然安装了SqlServer Reporting service 有点怪怪的抢后端故事

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!Nginx再难,有人带路,轻松搞定

Nginx启动报错:

nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions)

查端口占用

D:\nginx-1.12.2>netstat -aon|findstr “80”
TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       4

C:\>netstat -aon|findstr “80”

通常情况,直接把IIS关掉,然后再启动nginx即可以成功启动。然而关掉之后,却不行不行…

于是我就帮前端搜搜搜,搜到了一个

sqlserver reporting services

怎么也没有想到,前端会安装报表服务

这个小故事真诠释出了坑不再多,够神奇就好。想不想不到报表服务器会抢80端口,而且显示是由系统占用了。不过,处理完这个之后,还是感觉豁然开朗。为了让前端正常调试,我一度将自己的电脑配置成了nginx服务器对外服务。

如果你恰好路过,看到了,一定记得nginx端口占用可能是由iis,也可能是由sqlserver reporting servicesh占用导致。这也很好的解释了当初为什么老是被抢端口,而且抢得还那么那么快。

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

二维码生成压测实战|二维码图片合成内存爆表 Connection_Dropped_List_Full JS渲染二维码兼容PK赛

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!压测再难,有人带路,轻松搞定

这是当面付的续集,想不到继集这么多。这回是压测的故事了。收银台的二维码是自己生成的,本身的逻辑比之前调用支付宝的更加“轻量级”,无需进行外网的请求,只涉及到自身的缓存操作。所以这个环节的压测数据不会是一个问题,于少会优于当前的。最大的问题是,生成二维码图片了。

一、服务器端合成图片

压测目标定得高的话,会导致大量的 503请求。

通过查看如下IIS日志

%Systemroot%\System32\LogFiles\HTTPERR\httperr1.log

可以查看到onnection_Dropped_List_Full、QueueFull的状态。加大IIS队列之后,压测数值更漂亮掉了。

合成图片,优化了一下LOGO图片,因为都是一样的,所以搞成了一个单例,避免重复读取文件。测试退化成了压测ThoughtWorks.QRCode.Codec与IIS队列了。

二、巧妙的给自己找台阶下

最后的压测结果,划一道线的话,会比较尴尬。结果实际峰值估算出一个合理的预期峰值来当压测标准的话,就很好的过了。这里有个不太好解决的事情是,优化图片合成有点像个死胡同,时间与技能都确实。这个台阶找得好,华丽的结合业务峰值,即不打脸,也不耽误进度,还有历史数据说话。

三、被抛弃的客户端JS渲染二维码图片方案

服务器端合合成二维图片,占内存,未调整IIS队列之前,未优化之前,压测数据惨不忍睹。我是很想使用JS来处理二维码图片,不让服务器端生成图片,这样子可以完美的绕过压测性能问题。但是这个方案不给过,考虑到js兼容性问题,也没法测试兼容性,就罢了。

四、内存暴表

有的时候,我们需要改变一下,适当的结合业务峰值,嗯嗯嗯,就是这样子。今天的压测算是尝试优化,不断被压测数据打脸,在优化到“极致”的时候,再用历史峰值找了个台阶下,避免了掉进入图片合成优化的深坑中。
故事,就是这样子,没有无限的资源可以投入进去,只能取舍一下。当面付这回,算是收尾了吧。不然,又得写续集了。敬请期待下一个回合。

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论