• ·编程说·
  • ·文字·
  • ·技术·
  • About Me
Coding & Living & Enjoying
生活总是这样,现在措手不及的正是当初游刃有余的你
未分类

此去经年 - 永兴未来城[2033年]

本系列纯属虚构,所引用事迹皆为虚拟与臆想,如有雷同,纯属命运捉弄... 今天,我宣布:永兴未来科技城正式营业。在五年前的今天,晖旭集团成功竞得此项目,而且更让我们想象不到的是竟然比预期计划提前了整整两年,接下来有请晖旭集团董事长李晖先生发言。 台上时任永兴县国资委主任的马主任慷慨有力地宣布着,说罢主持人把一个中年男人迎上了舞台。这个中年男人谦虚地从主持人手里双手接过话筒,风采飞扬的说道: 是的,我们不仅仅提前了两年完成了所有的既定目标,还新增了诸多项目。这里离不开诸位像马主任这样的政府工作人员的鼎立配合,把办事处直接开在了工地,原本应该在办公室里悠哉游哉的工作人员、干部们和所有的工人们一起风里雨里的拼命工作。让我们给他们最热烈的掌声。 话说回来,除了所有的工作人员的热情,祖国科技力量、基建能力的急速进步也是功不可没。我们引入了最新的作业规划系统、巧妙地利用了人工智能进行作业规划与进度管理,在管理上实现了最优解,另一方面我们也大胆引入了新型建筑材料,无论是刚度、柔度都是最先进的。 ... 电视机里李晖还在继续着他的热情演讲,而我的思绪飞回到了十年前的那个下午。 2023年,秋。 李晖组织着同学们进行了一次小范围的聚会。会议主题也十分鲜明,就是借钱。 “我现在就只需要一百万,让我先把员工的工资发了,让他们先过个年”。 李晖近乎央求的语气说着,猛吞了一口白酒。 “你就是作的,你一个小小的私企竟然想去研发材料,你知道材料科学需要费多大的劲吗?需要烧多少资金吗?” 我气不打一处来地揶揄着。 大家伙也都七嘴八舌的劝他放弃。 “大家说的我都知道!可是你看看三年前我们被美国科技压制地差点整个行业崩溃,我们不能只是买买买,再卖卖卖。归根到底还必须得东西是自己的才有话语权。我们现在已经在实验室里面完成了技术方案的验证,只差一点资金进行小规模投产”。 李晖红涨着脸辩解着。 “好。我把我那个小破厂关了,全力支持你”。 一直在旁边不说话的刘旭辉笃定的说,说话的声音不大,但却异常清晰。 这次聚会之后大家都奔波于生计,没有更多的后话。再一次关注就是2028年的竞标会。 我不是科技出身,对细节不是很明白,但是我们知道竞标成功意味着他们成功了!

2020年08月23日 0Comments 31Browse 0Like Read more
技术

编程说【第二期】 - 聊聊系统集成与中间件开发

编程说【第二期】 - 聊聊系统集成与中间件开发 hi,大家好,这是编程说的第二期。今天想和大家聊一聊软件里的系统集成和中间件的开发。 背景 软件世界早已经从个人英雄主义进化到了高度产业化的时代,开发运营越来越呈现了模块化的趋向,一个公司可能只负责一个独立的功能。比如专门做CRM的,ERP的,营销工具的。在一个大型软件项目中,数十个软件公司共同参与也是很平常的事情。 当年求伯君一人之力硬刚wps的故事未来会难以重演。 如果一家公司需要上述的这些功能,那么他就需要采买不同公司的软件,而为了方便使用,它们之间有部分数据需要进行交换。由于这些软件分属于不同的软件公司,没有办法提前为其他软件提供完全的支持,因此,需要另外的软件来将这些功能串联起来,这样的软件就是系统集成软件,也叫做中间件。 在系统集成方面,硬件的研发与设计相对于软件而言走得更远,比如我们的手机里面,摄像头是sony的,屏幕是三星的,内存又会是另外一个供应商的。最后它们在一个流水线中 被组装起来。流水线就是系统集成的角色。 这个时候我们来看系统集成的特点就是自己不生产功能,只是把不同软件的功能进行对接,使它们可以联动操作。 小明的服装工厂 假如小明开了一个服装工厂,他同时入驻了京东和淘宝商城,另一边,自己的内部仓存管理又是从oracle买的erp软件,那么,我们如何将产品信息(价格,库存数量)进行三方同步? 京东和淘宝老死不相往来,它们怎么会互相对接嘞? 而小明本身的仓库软件又怎么会知道你要对接这两个平台? 这个时候小明就需要另外的一个软件将他们串联起来,这个软件的功能非常的纯粹。它只需要接收来自erp软件的信息,然后经过协议转换分别发送给京东和淘宝,另一个方面接收来自京东和淘宝的信息再一次的经过协议转换反馈到erp系统。 这样就完成了三方打通。 中间件的意义 我们知道了中间件的目的,那么在实际开发一个中间件的时候要注意什么? 中间件只做集成,不做业务功能。 尤其是当其中一方是自研软件的时候,非常容易把一些原本属于软件的功能做进了中间件。因为我们有整个系统的软件,数据库的软件。 怎么考量有没有做业务功能?就看代码里面在与需要对接的软件的沟通过程都是通过公开的接口,SDK. 正确地处理映射。映射表现在不同的三个方面 字段之间的映射 行为之间的映射 状态之间的映射 字段的映射也可以概括为协议翻译器,其目的就是把一个系统的消息格式转换到另一个系统的消息格式。 行为的映射也就是指这个中间件关注的是哪些功能,我们如何把这些功能抽象到不同的平台的实现中。 有点类似于接口与其实现的类。 状态映射指的如何及时地同步那些在不同软件中会实时发生变化的数据(比如库存数量,是否下架),这也是中间件软件的最大的目的。具体的实施无外乎以下几种方式: 建立长链接,实时推送。 轮询查询接口。 webhook。 导入与导出。 无论采用哪种通讯方式,最最最重要的就是保持一点,一定是消费者确定模式的实施。只有对方明确告诉你成功接收了才算接收到。由于技术实施上的不合理造成的消息丢失是中间件软件最头疼的问题。 其他中间件概念 除了这种不同业务系统的中间件之外,在软件里面还有一个重要的中间件领域就是大数据开发中的ETL. ETL 是用于不同数据库之间的同步过程,整个过程分成了提取,翻译,加载。和我们上述提到了软件中间件的过程也极度类似。 所以,中间件就是用于不同软件之从学校间互相通讯的软件,这个过程也就是系统集成。

2020年08月17日 0Comments 5Browse 1Like Read more
文字

请别拿一两个胸部和游戏来说事!爷,不堕落!

请别拿一两个胸部和游戏来说事!爷,不堕落! 2009-12-09 有关胸部 韩寒:我没错! 韩寒链接松岛枫的博客从本质上看是一点的都不具备新闻价值的,这样的行为在各大博客公司每天的流出量是惊人的。可就是有那么一群上了年纪或者稍微有点年纪的假道学们就开始捶胸顿足,大叹世风日下。理由也不过是松岛枫曾是日本AV女优。这博客被很多网友证实比我国的任何娱乐网站的内容还健康无比,但人家就抓住松岛枫博客那个露胸部的博客来说事。我就纳闷了,药和人在一起呆久了会沾染人的习气变得圆滑无比,是不是博客和AV明星呆久了也沾染了色情的习气不成? 的确,松岛枫的博客存在着那么两个胸部,可胸部真有那么大的威力?竟能让我泱泱大国(假道学不老是自以为是的说泱泱大国吗?)的一代人败坏? 如果胸部真的那么顶用的话,那我泱泱大国也绝对是泱泱“胸”国,无论你是来自北美的还是东亚的,我们就露一两个胸部去毒害你们的下一代。如果不够,一两个亿的胸部我们也是拿得出的。可事实上是“胸”国依旧凶不了。所以胸部的诱惑也就那么回事,除了刚出生的小爷们没那么多人把它当回事。 所以,韩寒链接松岛枫的博客是完全合理的私人行为,没有错! AV啊AV AV,这东西其实是好东西,它在中国冒着骂名无私的完成了70-90后以及00后最初的性教育。所以,AV和学校的教师一样,都是蜡烛。而且比学校的蜡烛燃烧的更快。她们不只是要动嘴,还要动身,还要得梅毒而早早的死去。如果算起来,一个普通人一辈子的性生活绝对不比人家少。 不看AV的男人不是男人,我自觉我们的主席同志也会目睹一二,大赞尤物啊,真是尤物。 所以,AV一直存在着,我们的祖国一直发展着。 AV还有继续存在的价值。 没看过AV的请举手! 有关游戏 久游:我很冤! 《劲舞团》这样一部很娱乐的游戏却无情的被一大群人指责成“全国最大的一夜情基地”“最大的性交易平台” 我依旧纳闷,久游公司从未发表声明鼓励一夜情,纵观官网,也是健康无比。 这游戏我玩过,游戏公司基本没问题,他们的立场除了游戏只是游戏。与性无关!至于有谁在里面发生点什么,游戏公司就很难保证了! 当然,有站街女站在马路上招揽生意我们能够说是马路的错吗?有人在公园强奸能够说是公园建在这么偏僻的地方就是鼓励强奸? 是不是要岳飞老爷来说教说教!何患无词! 而且,CCTV的节目经常在报道负面消息的时候公开这些游戏公司的名字或者游戏名字,甚至发表主观立场。我认为这样对游戏公司是很大的不公平。完全可以说是被侮辱名誉,可以要求赔偿的。 其他的游戏公司也遭遇到如此侵犯权利的境况。真不公平,GVM收了人家的税,却不能保证最基本的权利。杯具,真实杯具! 有关教育 学校的问题 教育机构是最能教育人的!现在的学校除了考试没有教育。就笔者曾经说在的中学来说,老师的口头除了高考无其他的,甚至公开教授我们高考的“非智力技巧”,一个骗子教另一个人骗人是不对的,这样的教育有多大的作用和说服力。教育,何幸之有!(外:我所在的县一中前校长应赌博和强奸学生被拘。我敢保证这校长绝对没有玩过劲舞。此学生午夜在学校公寓跳楼身亡。)

2020年08月13日 0Comments 6Browse 0Like Read more
文字

也谈移动客户端的市场与发展趋势

也谈移动客户端的市场与发展趋势 2011-07-19 一,触摸让生活更美好 大约从2008年初起至今,google成为了全世界热议的话题。这些话题·当然是关于google搜索服务之外的内容。 2007年底,google发布完全开源的android系统。android与周杰伦的相同之处在于他们都是一出道即红遍大江南北。如果说周杰伦的成功是其非主流的音乐风格,那么android的成功就是其卓越的触摸体验。 很早以前,我用过一款motorala的A1268。这款手机中,我只是在不断的感受到触摸所所带来的困扰。那个时候,基于触摸的游戏大多是一些连连看之类的。而在android中,触摸是其核心思想,一个拇指的世界,或许我们还需要用到食指,因为我们需要多点触控。用过android的朋友们都知道,拇指所能完成如何的工作。发短信,浏览网页,打电话,娱乐,以及相互之间的切换。而技术上只是一块分辨率稍微大的屏幕。 另一个成功的触摸产品则是iphone,ipad。出自乔布斯老头之手. 也就是说,未来的移动客户端,触摸是必然的主流。因为触摸让操作更简单。键盘输入则是一种高效率工作的必备品了,也就是说,未来谁还使用键盘,这人要不是个打字员,要不是个程序员。或许还会是个古董收藏家。 二,我们需要的也许只是高速的体验 在PC上,我们可以一边挂着WOW,一边看着电影,然后打开网页,或许还会打开QQ调戏MM,也可能还需要打开 IDE编写代码。这时,我们需要一颗都核心处理器,也就是我们的计算机需要多几个脑袋,因为我的要求有点高,但这种要求很正常,大家或许都在这么干。 但,在移动设备中,我们做不到如此多的工作,因为我们的屏幕不够大,我们的手指也不够细,我们也不希望我们使用移动设备中花费太多的精力。 因此,单核处理器在未来相当长的一段时间里会是主流的。如果一颗1GHz单核的处理器,以及一颗单核700Mhz的双核处理器,我想我会毫不犹豫选择单核处理器的。我仅仅希望我的应用程序能够更快捷! 三,4G网络让生活移动起来 2011年上半年,china moblie的TD网络试点运行,达到了理想的上行下行速度。4G是移动生活的前提,现在的GSM,3G网络都比较鸡肋。没人愿意花一分钟去打开一张图片。 4G的成熟,使得移动互联网市场迅速膨胀起来。首当其次的当然是娱乐市场了! 手机网游市场现在几乎还是处于空白,虽然掌上明珠走在了前列,但其产品却糟糕透顶,我无法再其游戏中体验刺激快乐的感觉。我甚至怀疑其客户端原本只是一个java扩展应用。 我们要做的是更好,谁可以来填补这部分市场嘞。期待网游界的小鸟。 2012年是移动开发的一年,2012年是云计算的一年..当然,2012年也是我们要买诺亚方舟船票的一年!!

2020年08月13日 1Comments 55Browse 4Like Read more
技术

Java编程中影响性能的一些特点

文中将提供一些在Java编程中影响性能的一些特点,为了能够提高Java程序的性能,而经常采用的一些方法和技巧。 1.尽量使用final修饰符。 带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。 2.尽量重用对象。 特别是String对象的使用中,出现字符串连接情况时应使用StringBuffer代替,由于系统不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理。因此生成过多的对象将会给程序的性能带来很大的影响。 3.尽量使用局部变量。 调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量,实例变量等,都在堆(Heap)中创建,速度较慢。

2020年08月13日 0Comments 2Browse 0Like Read more
技术

C++的苦口良药

c++面向对象中的繁琐机制,不得不让你在编程中频频“犯病”,找错误,找bug,困扰,是你的病源所在,本人虽不是什么“良医”, 却也算是总结出以下“良方”。注:难免会有记忆上的遗漏,知识上的不够,遗漏之处还需大家来补充啊! 药方1 ? 必需时刻明白面向对象编程中要扮演的两个角色,一个就是你,类的设计者,一个就是用户,类的使用者,你的设计是针对用户而言的,当然你也可能是身兼两职,同时是设计者又是用户。 药方2 ? 设计一个类时,将声明放于头文件,定义放在cpp文件中,因为类是给用户使用的,将声明放在头文件中,用户便可以方便使用,而定义在cpp文件中主要是为了两点,一提高编译效率,二避免用户包含文件后发生重定义。 药方3 ? 声明完一个类后必须以分号结束,因为声明完一个类后后面还可以加一个对象名,用来声明该类的实例化对象,分号‘;’ 不是表示声明的结束,而是表示声明该类对象的结束,如果单纯一个分号表示没有声明任一对象。 药方4 ? 避免在构造函数里实例化本类,否则会发生递归死循环。 药方5 ? 避免在析构函数里delete本类实例,否则会发生递归死循环。 药方6 ? inline成员函数必须在头文件里定义,因为inline函数本质是内联展开的,跟#define预编译类似。 药方7 ? hpp头文件里的类或函数必须在hpp头文件里定义,因为hpp的本质是让编译器只对hpp文件里的内容编译一次,然后将目标代码附到cpp目标文件中。 药方8 ? hpp头文件里的全局变量必须声明为static 否则会发生重编译,static本质是让编译器只定义一次。 药方9 ? const 数据成员必须要在初始化列表中赋初值(c++11中也可以直接=号赋值),因为它是const变量。 药方10? const成员函数不允许你修改类的数据成员。因为它就是不予许。 药方11? 指向const对象的指针访问成员函数时,只能访问const成员函数,因为指向const对象的指针不予许你修改它所指向的内存的值,而const成员函数不修改类对象数据内存的值,它们俩正好互相吻合。 药方12? static数据成员需要在类外定义,它本质上并非属于包含它的类。 药方13? static成员函数不包含this指针,因为它本质上并非属于本类。 药方14? 如果类中的某一指针数据成员指向分配的内存空间,则一般的做法是定义析构函数,在析构函数里释放它所指的内存空间。因为类的生命周期结束时,如果没有定义析构函数,则会调用编辑器合成的析构函数,而该合成的析构函数不会智能地释放数据成员指针所指向的内存,所以必须在类结束之前释放它指向的内存。还有另一种做法就是将该成员指针定义为智能指针类型。 药方15? 操作符重载不能重载两个内置类型的对象,至少要有一个为类类型或枚举类型。因为重载两个内置类型的对象完全没有必要。 药方16? 赋值操作符重载(operator=),不宜设为虚函数,因为赋值操作符中的指针或引用函数参数有可能是指向基类对象也有可能是指向派生类对象,因此容易发生混淆。 药方17? 在继承层次中,基类应该定义一个虚析构函数,因为非虚析构函数不会被继承,而虚析构函数会在派生类定义析构函数时发生重定义(就是虚表指针指向了派生类的析构函数)。在利用基类指针实现多态时,基类指针指向了新开辟(new)的派生类对象,这时由于基类的析构函数是虚的,所以再利用基类指针释放(delete)掉派生类对象内存时,就可以顺利地执行派生类的析构函数了。 药方18? 模板类或函数,在头文件中声明,并且,定义也要在此头文件中(一般采用hpp头文件),因为模板特化机制实际上是编译时利用模板参数产生可以兼容实例的目标代码附于使用它的cpp文件中(特化),从而实现多态,而不能预先产生目标代码就兼容实例的。 药方19? 声明友元之前必须对该声明友元的对象,进行声明或定义。因为你让我跟他交朋友,可以,但你必需让我知道他的为人吧。^_^ 药方20? 你所知的只是沧海一粟,切莫过于骄傲和自满。

2020年08月13日 0Comments 1Browse 0Like Read more
技术

用汇编的眼光看C++

如果说模板类定义的是一种数据类型,那么模板函数定义的就是一种函数。既然是函数,那么就有输入数据和输出数据。和模板类的概念差不多,模板函数的初衷也是为了在函数操作上抽取共同的特性,屏蔽的是类型的不同和差异。我们可以通过下面一个简单的代码说明问题: int int_compare(int a, int b) { return a > b ? a : b; } double double_compare(double a, double b) { return a > b ? a : b; } 上面的一段代码是取较大值的一段代码。两个函数之间最大的差别就是输入数据类型和输出数据类型之间的差别,那我们有没有一种办法可以屏蔽这种数据类型之间的差别呢?有。那就是函数模板: template <typename type> type compare(type a, type b) { return a > b ? a : b; } 可以看到,模板函数和普通函数没有什么区别,只是在函数的上面把类型抽象成了type,那么模板函数应该怎么使用呢? 246: int i_value = compare(2, 3); 00401458 push 3 0040145A push 2 0040145C call @ILT+10(compare) (0040100f) 00401461 add esp,8 00401464 mov dword ptr [ebp-4],eax 247: double d_value = compare(2.3, 3.1); 00401467 push 4008CCCCh 0040146C push 0CCCCCCCDh 00401471 push 40026666h 00401476 push 66666666h 0040147B call @ILT+5(compare) (0040100a) 00401480 add esp,10h 00401483 fstp qword ptr [ebp-0Ch] 248: } 汇编代码表明,两个compare调用的函数地址并不是一致的。其中整数的compare地址是0x40100f,而double的地址是0x0040100a。这说明编译器在编译的时候帮我们同时生成了两个compare函数。所以说,模板类的本质就是在编译器增加判断处理工作的同时,减少手工的重复劳动。同时和模板类不一样,模板函数不需要显示定义函数的参数类型,这是因为可以从入参或者是返回参数判断出函数的类型。 如果参数类型是 class类型呢? 我们可以试一试。首先定义基本class: class data { int value; public: explicit data(int m): value(m) {} ~data() {} int get_value() { return value;} int operator > (data& d) {return this->get_value() > d.get_value();} 接着,我们调用compare函数: 256: data m(4), n(2); 0040148D push 4 0040148F lea ecx,[ebp-10h] 00401492 call @ILT+40(data::data) (0040102d) 00401497 mov dword ptr [ebp-4],0 0040149E push 2 004014A0 lea ecx,[ebp-14h] 004014A3 call @ILT+40(data::data) (0040102d) 004014A8 mov byte ptr [ebp-4],1 257: data p = compare(m,n); 004014AC mov eax,dword ptr [ebp-14h] 004014AF push eax 004014B0 mov ecx,dword ptr [ebp-10h] 004014B3 push ecx 004014B4 lea edx,[ebp-18h] 004014B7 push edx 004014B8 call @ILT+15(compare) (00401014) 004014BD add esp,0Ch 258: } 256行: data构造了两个基本变量m和n 257行: 我们调用模板函数compare, 函数地址为0x401014,注意dx为p的地址,也就是堆栈临时变量的地址 为了看到算术符>重载,我们跟进compare函数: 241: return a > b ? a : b; 0040212B lea eax,[ebp+10h] 0040212E push eax 0040212F lea ecx,[ebp+0Ch] 00402132 call @ILT+55(data::operator>) (0040103c) 00402137 test eax,eax 00402139 je compare+53h (00402143) 0040213B lea ecx,[ebp+0Ch] 0040213E mov dword ptr [ebp-18h],ecx 00402141 jmp compare+59h (00402149) 00402143 lea edx,[ebp+10h] 00402146…

2020年08月13日 0Comments 1Browse 0Like Read more
技术

Android - 重写BaseAdapter实现自定义ListView

要想实现自定义ListView,首先需要自定义一个listView的单元布局。下面是一个布局实例: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="32dp" android:id="@+id/imageView"/> <TextView android:layout_width="120dp" android:layout_height="35dp" android:textSize="22sp" android:text="2008-12-12" android:gravity="center" android:paddingRight="5dp" android:id="@+id/dateView" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="fill_parent" android:orientation="vertical"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:text="类型:" /> <TextView android:layout_width="40dp" android:layout_height="wrap_content" android:textSize="10sp" android:id="@+id/typeView" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:text="金额:" /> <TextView android:layout_width="40dp" android:layout_height="wrap_content" android:textSize="10sp" android:id="@+id/moneyView" /> </LinearLayout> </LinearLayout> </LinearLayout> 利用该布局实现的效果如下: 我们需要通过一个自定义的Adapter(通常,这个Adapter继承自BaseAdapter)和 List<Map<K,V>>来设置ListView数据。 自定义的Adapter关键代码: public View getView(int position, View convertView, ViewGroup parent) { inflater = LayoutInflater.from(this.context); convertView = inflater.inflate(R.layout.list_items, null); ListView listView = null; if (listView == null) { Log.v("position",Integer.toString(position)); listView = new ListView(); //控件实例化 listView.dateView = (TextView) convertView .findViewById(R.id.dateView); listView.typeView = (TextView) convertView .findViewById(R.id.typeView); listView.moneyView = (TextView) convertView .findViewById(R.id.moneyView); //设置控件数据,数据源来自List<Map<K,V>> listView.dateView.setText((String) dataList.get(position).get( DBtest.DATA_NAME)); listView.typeView.setText((String) dataList.get(position).get( DBtest.TYPE_NAME)); listView.moneyView.setText((String) dataList.get(position).get( DBtest.MONEY_NAME)); Log.v("money",listView.moneyView.getText().toString()); } else { listView = (ListView) convertView.getTag(); } convertView.setTag(listView); //convertView用于在ListView中显示 return convertView; } 数据源部分 Cursor cursor = cursor = DateBaseTestActivity.mDatabase.rawQuery( "SELECT *FROM " + DBtest.TABLE_NAME, null); Log.v("cursor", Integer.toString(cursor.getCount())); cursor.moveToFirst(); do { Map<String, Object> map = new HashMap<String, Object>(); int data_id = cursor.getColumnIndex(DBtest.DATA_NAME); int type_id = cursor.getColumnIndex(DBtest.TYPE_NAME); int money_id = cursor.getColumnIndex(DBtest.MONEY_NAME); String dataString = cursor.getString(data_id); String typeString = cursor.getString(type_id); String moneyString = cursor.getString(money_id); Log.v("isEMpry",moneyString ); map.put(DBtest.DATA_NAME, dataString); map.put(DBtest.TYPE_NAME, typeString); map.put(DBtest.MONEY_NAME, moneyString); Log.v("map",(String)map.get(DBtest.MONEY_NAME)); list.add(map); } while (cursor.moveToNext()); 将数据库中的数据读取到List中。 public ListViewAdapter getAdapter(List<Map<String, Object>> dataList) { Log.v("test", "ok"); ListViewAdapter mAdapter = new ListViewAdapter(this, dataList); Log.v("test", Integer.toString(mAdapter.getCount())); return mAdapter; } 方法getAdapter利用我们获得的这个list初始化一个ListViewAdapter(这是我自定义的adapter)。 备注1:使用Cursor时,需要注意越界检查。 使用完后,请关闭。 备注2:ListView的长度由List<Map<K,V>>的长度决定。

2020年08月13日 0Comments 1Browse 0Like Read more
文字

此去经年 - 我们过去的十六年

此去经年 此去经年是当年闲着无聊以同学们为蓝本进行的系列同人写作。 陈璇篇 2011 - 05 - 18 陈璇结婚的那天我没去参加,除了我之外的人都参加了。可见声势之大。对此,我的官方回应是前天晚上纵欲过度,起床不来。其实我只是依偎在便江边,洒下我的眼睛分泌液体。 那个男人身高超过了175。我想这又何必嘞,像我这样就挺好了,至少接吻无比方便。我还是在自卑,这个175可是个工程师。我,只是中国电视剧大军的一员,跟着一个扯淡的剧组编造一些扯淡的故事。 晚上,曹桥辉找到了我,意味深长地说,还以为你们有戏嘞。 是阿,我也这样觉得。 然后天黑了,我们睡了。永兴今夜无眠。 次日。桥辉走了,阿光走了,我走了。颜鹏留下来了,据说他将建设家乡。­ 颜鹏篇 2011 -05 - 17 陈璇婚礼结束两天内,大家依旧散落在了世界各地。一切照旧,若不是手中还有一张逐渐泛黄的请柬,怕是大多数人都会恍惚自己是否真有回来过。回到这个释放着我们青春的地方。 对于三十而立的我们,立业成了最大的问题。迈过了三年之痒这道坎的争着奔向七年之痒。迈不过去就这样散了。 对于我们这样一批人,最大的奇迹一定是这两人。一个叫颜鹏,一个叫何玲。 从很多年前开始,颜鹏就一直嚷嚷着要去和很多女人上床,立志成为情场高手调情专家。事与愿违的是在我们三贱客中,他却成为了最本分的男人,本分的有点不堪入目。为数不多的几次与我一起混宾馆的机会,当我准备打前台电话时,他脸都白了,豆大的汗珠如趵突泉涌。我也就没了兴致。一起聊天到了天亮。 从师范毕业之后,颜鹏有过一段极其短暂的朝八晚五的生活。突然一天,他们两口子就象从户口本上抹去了一样,消失得无踪无影。我多方打听,了无音讯。我想大多是挂了。我悲伤了很长的一段时间。 再次见到他是两年之后的事了。地点,我家门口。 他驾着一辆凯迪拉瑞直奔我的家门,与其同行的还有夫人何玲,还有一个女儿--颜色。我想他是发了。 不管他发不发,他都还只是我们三贱客的老大。我一拳过去,我歇斯底里的骂道,我操,你他妈怎么没有死啊!死了多好啊! 何玲帮他擦了一下裂开的嘴角,默不作声。 打架不是一个人的事,一个人打架是最无聊的事。我也懒得动手了。 还让她们喝了我研发的柠檬咖啡奶茶。颜色表示以后还要来喝。 现在的颜鹏,宛如一个企业家的形象。尽管他对消失的两年实行“三不”政策(不说话,不生气,不逃避。),但对于家乡来说,银子是衡量一个人的标准。 永兴扩市以后,城区蔓延到 黄泥与碧塘。在龙山路稍远一点的地方,新建了一个新兴产业工业园-----立新工业园。 颜鹏在这里弄了一个搞电路板代工的小厂子。也算是解决了200人的就业问题。 此时我正在四川跟着剧组在山上拍一部《不可触摸的痛》的电视剧,对具体情况并不十分清楚 。 半年之后,厂子正常运营之后,这娃又飞走了…… 曹烁篇 2011-05-18 我认为在任何一拨人之中都会有这么几种人。一种是天天在眼前飘过飘去的,熟悉得快要忘掉;一种是偶尔听闻到消息的,或是发了或是挂了。还有一种就是神秘主义的坚持奉行者。比如曹烁。 这些年来,我不知道有多少人走进过她的内心的柔弱。如果有一天,她死了,我该如何去评价她的这一生。 豪放?陪着陈浩灌黄汤然后一起倒下还骂着,你傻不傻啊!然后还会补充一句,别扶我。 在相当长的一段时间里,赌曹烁的眼泪的赔率已经涨到了1:50。最后身为庄家的黄志攀宣告破产。也就是说他必须得以50块钱渡过剩下的20天。 但,我对钱发誓,我在很多年前我一定看见过她的眼泪。那或许是一个雨后三点半的日子。 可惜庄家已经破产了。 年代的久远,原谅我的忽疏吧! 毕业之后的一段时间她确实是很风平浪静的。嫁给了东北,嫁给了黑土地。平静得快要将她忘记的时候,她又回来了! 黄文涛还没来得及将电话靠近耳朵,一个彪悍的声音响起:傻逼,我在银都421房。给你20分钟,超过一秒,JJ剁掉! 晚上我,黄文涛,曹烁三枚老同志坐在了 一家名为“欲”的休闲会所的一间临街的包间。(“欲”的地点是2011年的永兴一中。2013年的时候,永兴一中和县政府一同搬去了龙山路更远一点的地方。至此,以干劲路为中心的商业区算是搭建了起来。) 其实,我们一直无话。我们不能酸溜溜的说,我想死你了。我又不是那个长着一张肆无忌惮的脸的那个笑星。 我们只是一杯接着一杯的喝酒,喝酒,喝酒。然后我们就都倒了。曹烁还是会说那句,你傻不傻。傻不傻啊。 这一刻,我突然觉得时间的线在飞速的往前面拉,拉到了那个永兴二中的食堂。 我们在一起浑浑噩噩的过了三天。然后她再度消失。 我在剧本的扉页写下了一个题目《我们放肆着摇晃着青春的尾巴》,下面还有一首小诗: 我们 不能够去怨恨时光 因为它让我忘却 我们 不能够去感激时光 因为它让我们忘却 后来,从王兰王店长的口中得知了我不知道的一些事情。曹烁先是离了,然后愤然地对待生活。把所有的时光都抛洒在了商界,南南北北的倒腾东西,小发一笔,但还是很累。 似乎,耳边总有一个声音在低唱:我在这里呀,就在这里呀!如夏花一样绚烂! 王兰篇 2011-05-20 2018年的9月。我应邀参加腾讯科技有限公司11月举行的二十年庆典音乐剧——《企鹅伴随我们飞驰的二十年》 这只看着傻乎乎的企鹅在互联网业内成了最可怕的地狱。QQ游戏大厅完胜联众世界后,贪心的企鹅几乎啃遍了所以的互联网烧饼。2010年输给了新浪微博后,腾讯开始走向开放化与平台化。开放了自己的QQ API 以及第三方平台 Q+。2014年拍拍网以微弱的优势压倒阿里巴巴后,腾讯在中国互联网市场的霸王地位正式的确立。QQ平台化完成。 2015年,由阿里巴巴,新浪,网易,卓越,京东商城等三十余家互联网一线企业联合发表《关于深圳腾讯科技有限公司的垄断若干条例》并一纸状告法庭。2016年,腾讯退出B2C购物,并将腾讯改组为三个分公司。 腾讯时代宣告结束。 这次演出的规模很空前绝后,甚至包括马化腾那爱跑调且嗓门大的外甥女。累死累活的转悠了两个月后,拿到了人民币后我算是被资本家利用完了。剩余的时间就是属于我的了。 在深圳这个让人泪流满面的城市里埋葬了很多的关于现在关于未来的梦想极其幻想。黄文涛在一家不大不小不上不下的软件公司干着编码工作。还有一个一直希望自己是个富婆的王兰,并且是那种自己挣钱自己花的富婆。 王兰从2009年开始涉足B2C业务。在淘宝卖过化妆品,衣服以及各种乱七八糟,灰不溜丢的玩意。这个毫无特色,毫无生气,从没有出现过令人振奋的销量的店子持续到了2013年。 一摸口袋,感觉自己离富婆的梦想还很遥远。王兰撤出了日渐臃肿的淘宝,怀揣着自己的15万块人民币正茫然无措的时候。一次与曹烁的见面使得一切似乎开始改变…… 我当时正好去谈深圳交响乐团。半夜,曹烁直接把电话打到了我的宾馆房间电话,说,我明天上午7点到深圳,我们一起聚一次吧。 深圳的西餐厅都是精致到了散发苍白的境界。记得一个傻逼说过:艺术最终会因为过分精致而死亡! 曹烁把LV的经典款式NEVERFULL随手的扔到了旁边的椅子上,把一直拿在手上的IPhone X1轻轻的放在餐桌上,然后才扶裙入座。俨然一副富婆样。看了看我们,说:“ 我说我的王大班长,早就和你说过,你那破店子早该关闭了。” 说完了,抿了一口柠檬汁。 王兰低着头没有说话。 “我这人说得太直了。我现在有个发财的机会,要不要一起干!干,我们就一起干,不干,我就自己拉团队了!”。曹烁见王兰一直没说话,补充了一句。算是暴露了此行的目的。 “好!”王兰轻声的答应道。 此时我手机响了,深圳交响乐团那边的经纪人要和我谈合同细节了。我只好欠身离去。 后来她们搞了一个很大的二手市场。和淘宝的跳骚街一样的。她们把店铺租出去,就靠吃租子和拉广告过活。 我在富春茶楼深圳店的门口看到了驾着一辆宝马 X1的王兰的时候我知道她做到了。一袭古奇的长裙将富婆本质体现的赤裸裸啊! 那一天,我们聊了一下午。聊到电视剧的时候我会愤愤然的骂道这群傻逼制作人,只知道效率,弄一些粗制滥造的故事去蒙骗观众,卖的不好又得骂我们这些打工的。文明用语他老婆的。然后扯些八卦,陈璇生了个大胖儿子,都已经会叫阿姨了。有个不拿人民币当回事的男人开始追求曹烁很多年了,一直未果。悲剧地怕是要去跳珠江去了。陈婷婷的孩子都高中毕业了…… 我笑着说,你这个齐天大剩是不是准备向着剩者为王前进呀。 王兰一直乐呵呵的说,当然不会。人还是要嫁滴嘛!象我这么天生丽质的女人不嫁人会引发多少交通事故以及令多少荷尔蒙旺盛的男青年喷鼻血呀!本着创建和谐社会的目的人还是要嫁的!不然,我们就给祖国妈妈拖后腿了! 王兰巴拉巴拉的一通,突然幡然醒悟过来,直拍大腿,指着我说,你不也一样,还说我,心里是不是还想着那个陈X啊!” 我故作严肃道:“别胡说了,别人的孩子都可以叫我叔叔了。这种破坏别人家庭的话还是少说为妙。” 我们聊到夜都睡去的时候,算是尽兴了。临分手时,王兰可伶楚楚的说了一句;“你知道我当时是为什么离开淘宝的吗?” “不是生意不好嘛!”我很快速的回答了一句。 王兰摇了摇头,眼睛泛着泪花。我再想去细问的时候,她的座驾已经走远了,彻底的融入了深圳的夜…… 深圳啊,你到底让多少人泪流满面了! 我抱着一把YAMAHA的吉他, 写下了一首歌。《你还想要去远方吗》 后来,这首歌由汪蕊演唱。 曹桥辉篇 2011-06-18 直,那么的直…… 每个259人都有一个疑问--=------曹桥晖的背为什么那么直? 这是为什么嘞?嚎~~~ 为什么啊为什么?答案有木有啊有木有啊 有木有啊 木有啊 有啊 啊 曹桥晖的死让我茫然不知所措,让我们开始思考生命的意义——我们到底在为何而活。据说曹桥晖终结自己的时候特别悲惨。一整瓶的二锅头灌下去,踏着微醉的迷踪步法,在无人的黑夜的楼顶朗诵着诗歌,朗诵着自己: 我 将愿死在地球绚烂的光里 我 将来不及为心爱的河流取一个美丽的名字 …… ———摘自曹桥晖诗歌《青春是一把刺向我的刀》 曹桥晖把所有的郁郁不得志、爱过与错过都刻在诗中、化在酒瓶中。用生命与酒瓶共同验证比萨斜塔实验,可惜他将不再有办法去获知结果,或许对他而言,结果已经没有目的的重要性。 黑夜中,,风依然凄厉不止…… 最初的时候我就知道曹桥晖一定会死于非命,或亲自动手,或假手于别人。因为他不是属于这个年代的人,这个浮躁而又淫荡的年代容不下一个背都不愿意弯的人。但万万没有想到的是这一天会来得这么早,早到我们对生活刚开始抱有一丝希望的时候。 时间对于记忆的冲刷永远是无情的。追悼会上,那一张张渐渐熟悉的脸都变得不是当初的自己,我甚至都无法对号入座了。但就算我把全世界都忘了,至少还有一个人会铭记在心脏的左侧。 陈璇结婚以后我们并没有过多的联系,我不知道她过得是否快乐,他老公是否背着她有另外的女人,然后她将全部的生活用于寻找捉奸的证据;孩子是否将邻居家的小孩的头打破…… 我们相顾无言,她只是哭,从微微抽泣到咬着我的衣服嚎啕大哭。我想紧紧抱着她但却不能。我害怕我会被她丰满的身材所倾倒,这是不合时宜的,而且我也不是一个滥情的人。我只得尴尬的矗在这里,窘了…… 其实,那天我的眼里看不到任何人,我总想起那句话-------我真希望我们可以一起唱歌,歌唱我们肆无忌惮的青春以及堕落又纯洁的未来。 那个带着眼镜弹着吉他,唱歌和我一样难听的家伙就这么死了,我却不愿意说一句悼词,以为他的死是来说明他的永恒的存在的。他比我们任何一个人都伟大。 夜晚时分,陈璇临出门前回头含着眼泪问了我一句:“你为什么不向我求婚?” 依照遗嘱,我接管了他的"心风"琴社……

2020年08月13日 0Comments 45Browse 1Like Read more
技术

编程说 - 如何设计日志格式

介绍 这是第一次视频录制,一个人对着镜头说话还是蛮尴尬的。简单地分享了一下我们在程序中如何正确的记录日志,好的日志格式应该具备怎么样的特点。 一个良好的日志格式,至少应该满足以下条件: 错误等级 -不同的环境配置不同的错误等级监听。 日志分组 - 不同模块之间的日志隔离。 时间准备 - 准确记录日志产生的时间。 人类友好 - 方便人类进行检索与查看。 快速定位 - 通过日志可以快速地定位到问题。

2020年08月12日 0Comments 17Browse 3Like Read more
未分类

[SeqNo] - 一款基于MySQL的分布式自增序列发号器。

Background 通常地,在一些业务中我们需要像MySQL的自增列一样来生成与存储数据。比如餐厅取餐号,排队号码等。 SeqNo 就是基于以上场景而诞生,拥有以下特性: 分布式。 基于分布式锁实现。 自设步长。 定期归零。基于时间格式。 严格自增。 Github Repo: SeqNo

2020年08月12日 0Comments 5Browse 0Like Read more
技术

构建现代化的BI报表系统

Background 对于任何企业服务(SaaS)或者面向大众(To C)的系统,报表是内部运营中最重要的部分。报表内业务通常存在以下几个难题: 变更频繁。尤其是SaaS类企业,报表需求是非常的频繁变更单,因此很难将报表需求进行固化,进行针对化的优化。 SQL复杂。报表通常是直接从源数据库通过SQL的方式加载出来,计算与查询都非常复杂,没办法简单的完全优化。 实时性。实时性是报表业务最具有价值的点。实时性表现在数据更新的实时性以及加载报表的实时性。如果加载一个报表需要半小时那自然是极大的消耗耐心的事情。 那么,我们对于报表业务的架构需求就很清晰了: 满足数据增长的需求。AWS Redshift与Google Cloud Big Query 都是现代的海量存储分析引擎,我们优先选择它们。 尽可能的实时性。 这要求我们在ETL阶段,最好可以使用MySQL Slave的方式进行数据提取。 尽可能的灵活机动性。 这要求不能进行任何的预计算。 业务架构 实施步骤 挑选合适的ETL工具进行从生产环境的transactional database中提取数据。 ETL将数据写入到数据仓库中(全量数据库仓库,半年热数据仓库集)。 BI 应用程序或者自己开发的API连接到数据库仓库进行数据读写。 为什么需要热数据仓库? 因为从redshift/big query等系统查询数据是秒级响应,做不到毫秒级响应。当我们需要最快速的出面向C端的报表业务的时候,延迟是很重要的。而通常,C端报表的数据时长比较短。

2020年08月12日 0Comments 5Browse 0Like Read more
技术

十四小时极限调试挑战 - 流水账

十四小时极限调试挑战 - 流水账 背景 本文以流水线的方式记录了一次难以忘怀的调试经历。 过程 2019-10-29 13:02 分 接到紧急报告,某商业软件在解析特定数据格式时候出现故障,需要立即解决。而无法联系到供应商。 2019-10-29 13:20 分 开始进行问题复现。 2019-10-29 14:26 分 问题复现成功,打开debug级别日志,希望从日志中找出线索。 2019-10-29 14:40 分 日志没有记录到更有效的信息,开始对程序安装目录进行结构分析。 2019-10-29 15:30 分 基本完成对程序的分析,主要使用Java和so进行链接,由于程序是非常模块化的,基本可以定位到某几个Jar包有Bug. 2019-10-29 15:50 分 从Jar包的名称中发现在一个关键位置可能是引入了一个开源库,通过Google 找到该项目源码,希望通过修改这个库以避免这个Bug。 2019-10-29 17:10 分 第一个版本的修改版完成,尝试替换,解决了一定的问题,缩小了问题发生几率,但仍旧没有实际的进展。 2019-10-29 19:00 分 吃了个晚餐。 2019-10-29 19:20 分 经过两个小时的奋战,基本宣告这个方案失败,开始预备尝试对某个私有内部JAR包进行修改的方案。 2019-10-29 21:30 分 通过JBE将JAR包进行反编译,开始尝试直接修改Class文件。 2019-10-29 22:30 分 完成了Class文件修改版,进行替换后无法正常工作,造成了运行时的直接崩溃。 2019-10-29 22:50 分 喝了一杯焦糖加浓美式。 2019-10-29 23:10 分 通过上一个步骤对Jar的反编译已经发现源码没有进行加密与混淆,开始将整个JAR包进行反编译得到源代码,并且直接将该程序的所有JAR包都纳入lib中,开始直接编译。 2019-10-30 01:15 分 项目构成完成,解决了源代码中的一些问题,对代码进行非破坏性的增加日志。上线一个版本。 2019-10-30 01:30 分 拿到更完整的日志,进行日志分析。对代码进行保护性调整,进行了多次Debug。 2019-10-30 02:20 分 基本宣告调试完成,进行完整验证。在验证过程中,发现有部分逻辑遗漏。 2019-10-30 03:20 分 完成了最终版本,调试通过。 2019-10-30 03:30 分 正式投入使用,替代旧版程序。将整个问题Email复现给了供应商。 2019-10-30 04:00 分 记录下来了这次难得可贵的经历。

2020年08月12日 0Comments 4Browse 1Like Read more
技术

Web Audio API 的介绍与应用

什么是 Web Audio API 作为一个大龄程序员来说,对于网页的认知大部分是停留在HTML/CSS/Javascript中,我们会很自豪的认为网页就是用来显示内容和调用服务器API的一个UI组件。 但是历史总是在不停的教育我们,不学习就会倒退,倒退就会不知者无畏。Web Audio API 就足够对我们进行打脸。 在这之前,网页可以对音频进行处理的方式还仅仅是标签,但是其能力十分的有限,对于更高级的音频操作完全的无能为力。为了增强浏览器的音频处理能力,W3C 着手开始了Web Audio API Specification 的工作。 在Web Auido API的设计目标中是结合游戏音频处理系统以及桌面音频应用程序的需求进行设计与开发。也就是说,使用Web Audio API我们几乎可以完成一个专业的音频处理软件(比如Cubase / Logic )的构建,而这一切都是构建在浏览器技术之上。 从API的设计上,可以做到: 实现高精度的音频计算。主要应用于DAW(音序器),软件乐器等场景。 音频混响器。音乐混缩的必要能力。 3D音频。在游戏和音乐制作中都需要。 与 / WebRTC的集成。 Web Audio API 的设计哲学 简单来说,所谓的音频处理其实就是标准的IO函数。从某个地方获取一个音频流(Input),经过实时计算处理后发送去某个地方(Destination),而这个处理也就是Effect(从习惯上来讲,我更愿意称其为Filter函数)。 经过了复杂的Input / Destination 编排之后,就可以得到一个路由表(Routing Graph)。这个路由表定义了音频如何在不同的Filter函数中进行穿梭。这整个过程的定义也就是Audio Context。 AudioContext 类承载了音频流的流转方式。 一个最简单的Audio Context例子可以是: 定义音频流来源于一个audio标签。 将音频流链接到一个音量放大器。 将音量放大器链接到标准音频输出接口(电脑音箱)。 这样我们就得到了一个实时音量增益的音频处理流。这这个基础之上,我们还可以在#2之后,再接入一个混响效果器,加入一点点回声效果,让声音稍微的圆润一些。其链接拓扑结构就成为了: 标签 ---> 音量增益器 ----> 混响器 ----> 电脑音箱。 上面的介绍目的是为了表达我们要用这个可插拔的Filter函数的思路来理解整个Web Audio API的设计将会变得非常的轻松。 在Web Audio API中,一个个不同的Filter函数设计为一个个不同的AudioNode类,所有的AuidoNode 子类都继承自AudioNode类。 而AudioNode有两个核心函数 \connect()和\disconnect()\`。 也就是这两个函数将不同的AudioNode子类进行流程编排而得到了一个处理图(Processing Graph)。 ![](https://pic4.zhimg.com/80/v2-1eb0d3b119898d8f37fb03d1eb4f122b_1440w.jpg) **我们在试图理解整个Web Audio API的设计理念中最重要的就是理解一个音频处理系统本质就是一个编排出来的有向图过程以及音频处理就是从输入转为输出的过程这两点。** **Web Audio API 思维导图** 笔者对常用的Web Audio API 进行了归类,方便初学Web Audio API的同学进行整理。 ![](https://pic2.zhimg.com/80/v2-5042f4c1296e7e2681b8ed45722de819_1440w.jpg) **Web Audio API 的应用** --------------------- [查乐谱 - 学琴新姿势](https://link.zhihu.com/?target=https%3A//www.chayuepu.com)是一个新兴的电子乐谱平台,同时也在尝试使用Web Audio API 进行MIR领域的产品开发。 1. **节拍器应用** [查乐谱 - 小声节拍器​www.chayuepu.com](https://link.zhihu.com/?target=https%3A//www.chayuepu.com/app/beat) 这个应用利用了Web Audio API 实时的创建音频,同时也利用了Analyzer节点进行频谱计算。 使用了Web Audio API的优点: 在传统的网页开发中,对于类似于节拍器这种应用的开发通常会依赖与标签通过变成的方式进行播放与暂停工作。但是这种控制在延时上表现会很糟糕,无法应用在节拍器这种专业领域上。 **2\. 调音器应用** [查乐谱 - 小声调音器​www.chayuepu.com](https://link.zhihu.com/?target=https%3A//www.chayuepu.com/app/tuner) 这款调音器这是一个更为经典的音频处理场景。 1. 从麦克风获取实时音频流。 2. 对音频流进行实时音高识别(Pitch Detection),在处理过程中直接获取到了音频流的FFT频域信息,利用简单的计算规则计算得出。 3. 对实时音频进行频谱绘制(利用Analyzer接口)。

2020年08月12日 0Comments 2Browse 0Like Read more
技术

从一个实际案例来讨论我们应该如何做性能优化

如何优化一个数据解析器? 注:本文所示代码均为伪代码 假设我们要编写一个函数function parse_data(String Raw):Object[] 用来解析一个列表数据到应用程序之中。在二十一世纪的今天,我们会很自然的联想到使用JSON来完成这项需求。因为我们可以脱口而出JSON所有的优点: 兼容性良好。 开源支持力度好。 人类阅读友好。 因此,我们可以很快速的得到一个实现版本。 function parse_data(String raw): Object[] { let lst: Object[]; lst = JSON.parse_into<Object[]>)(raw) return lst } ``` 在相当多的场景里面,以上的实现都堪称完美。假设我们将程序的运行条件限定一下: 1. raw String 可能超过 1GB 2. 物理内存只有256MB 在以上场景的约束下,第一个实现已经无法正常工作了。因为JSON格式必须将其完整的载入内存才可以进行解析,从约束条件来看,raw string 大小已经远远超过了内存限制;同时,将所有数据都解析到内存也是很大的内存开销。从这个分析来看,我们得出了一个结论:第一个版本无法在内存条件苛刻的情况下工作,因此我们需要进行优化。 那么,我们的优化的思考点应该是怎么样的嘞? 我们需要先回过头了看问题,第一个实现的问题是内存占用过大引起的。那么,我们就需要一个方案来减少内存的占用。 减少运行时内存占用 -- 这个就是我们本次优化的哲学指导。 我们已经有了基本的哲学指导思想,那么我们开始进入到具体问题具体分析的阶段去寻找解决方案。我们再深入的思考第一个实现为什么会造成内存占用高: 1. 需要全部载入数据 2. 需要全部将解析结果存到内存 也就是说,我们的新方案只需要解决上面两个问题,也就完成了目标。 经过一番搜索与学习,我们发现可以使用Streaming友好的数据格式(Msgpack,CSV)来作为raw string,同时将数据使用sqlite来存储解析后的结果。 此时此刻,我们就得到了第二个实现版本。 ```javascript function parse_data(String filePath): Object[] { let db_conn = Sqlite.open("./cache.db") CsvReader .open(filePath) .each((line)=>{ db_conn.insert_into(convert_line_to_object(line)) }) } 这个实现通过将raw string 放入磁盘,同时利用csv行间隔离的特性。通过流式的方式将数据迁移到本地db中。对于物理内存的需求基本是趋近于O(1)的。从而解决了我们上面提出的两个问题。 那么,这个版本就完美了吗? 从代码逻辑上来看,这个版本对于数据量的限制从内存转移到了磁盘,在实际过程中可以认为解决了数据量代码无法工作的问题。但是它仍然存在一个问题 -- 磁盘IO过于频繁,磁盘IO表现在两个方面: 读取csv 写入sqlite 那么,我们是否有可能再次优化嘞? function parse_data(String filePath): Object[] { let file_lst = CsvUtil.split_file_by_lines(filePath,1000) // per csv file 1000 line let db_conn = Sqlite.open("./cache.db") foreach file_lst as file: Thread.run(()=>{ let lst: Object[]; CsvReader .open(file) .each((line)=>{ lst.append(convert_line_to_object(line)) }) db_conn.batch_insert(lst); }) end wait_all_thread_done() } 基于我们提出的疑问,我们编写了这个版本。这个版本提出了批处理的概念。也就是将一个任务拆分成数个互相独立的子任务,同时引入了多线程来发挥多核优势。 到此,我们就实现了一个多核加速、数据量无上限(假设磁盘无上限)的数据解析器。当然,同时代码复杂度也指数级上。 总结 本文主要目的是利用了一个实际案例,来讨论我们做性能优化的思辨过程: 在优化之前,发现主要矛盾 根据主要矛盾的来推演我们需要的解决方案 寻找解决方案,并且确定该解决方案可以解决问题 根据需要看是否需要继续优化,如果优化就回到#1。

2020年08月12日 0Comments 2Browse 0Like Read more
技术

TiDB Markdown Plugin - 将数据库运行在你的文章里

TiDB Markdown Plugin Repo: https://github.com/imiskolee/tidb-wasm-markdown 一款可以让TiDB运行在Markdown中的插件。 背景 周三看到朋友圈在疯狂的刷 TiDB - Wasm 这个有意思的东西(让数据库运行在浏览器里?TiDB + WebAssembly 告诉你答案)。 这的确是一件让人兴奋的工作成果,为WASM的应用提供了不错的思路。就这个项目本身而言也是有很大的价值。 我们在编程语言学习的时候,有诸如JSFiddle、Go Playground等很多不同的交互性环境可以使用,并且可以将这些环境内嵌到教学文章之中,形成了交互性的教学资料。但是在 数据库的文章之中,只能是纯粹的纸上谈兵,读者很难进行直观的感受与实操。 相比较而言,纯粹的编程语言的交互环境对于服务端资源消耗是可控的,成本也是可以承担得起的。数据库不一样,一个简单的索引优化的示例数据可能就需要GB级别的内存占用以及更大的CPU消耗,成本上难以承受。而TiDB WASM版本的出现,解决了这一个问题 -- 因为是在客户端构建环境,环境成本由对应的读者承担。基于这个前提,我想到的就是可以把TiDB WASM版本作为数据库的Play Ground环境嵌入到Markdown中进行交互。 实施过程 直接从 http://play.pingcap.com 中把相关资源下载,分析了程序的处理流程(稍后将介绍插件的工作原理)。这个过程有个有意思的发现,在部署的时候,WASM程序被伪装成了main.css这个名字,这个让我花费了几分钟。我觉得这个伪装的目的仅仅是为了复用Nginx的压缩缓存策略。 寻找一款Markdown解析器可以方便的内嵌自定义标签。 最终通过半小时的搜索与学习,定位到了markdown-it以及markdown-it-container这两项工作。 这款解析器必须是前端可以工作的。 这款解析器必须支持插件系统。 花费了1个小时学习了markdown-it的API与markdown-it-container的源码与工作原理。 开始进行Demo验证阶段。 进行了简单的代码重构,进行了简陋的封装。 开发了Example程序 感谢来自社区的朋友JinSong对Example进行了基本重构,稍微好看了一点。 工作原理 使用方法 ::: tisql SELECT * FROM USERS; | 固定tag内容 | sql 内容 | 详情见demo程序。 问题 目前已知在Chrome环境容易崩溃。 WASM程序体积较大,需要进行tiny处理。 目前仅支持一个文档单个数据库实例(基于性能考量) 还需要定义好的Markdown定义方法来描述数据库的交互行为。

2020年08月12日 0Comments 3Browse 0Like Read more
技术

Kubernetes 简介

什么是Docker Docker不是虚拟机。 在很多的网络教案中喜欢将Docker与虚拟机进行类比,这种类比用于理解Docker的优势有着不错的作用,因为Docker与虚拟机有着相同的优势。但是从技术而言,虚拟机技术则是对硬件层的虚拟化,而Docker是一种进程隔离技术。简单的说,我们可以在宿主机(Host Machine)使用 ps aux 看到使用Docker启动的进程。这意味着Docker中的程序实际上是跑在宿主操作系统中。这是我不赞成将Docker与虚拟机进行类比的原因。 要去理解Docker,最避不开的是Namespace 与Cgroups。 Namespace 是Linux中对进程之间进行隔离保护的技术。通过Namespace技术,保证了不同Docker Container中看到的内容不一样,这也是Docker的基础。 cgroups(Control Groups)最初叫Process Container,由Google工程师(Paul Menage和Rohit Seth)于2006年提出,后来因为Container有多重含义容易引起误解,就在2007年更名为Control Groups,并被整合进Linux内核。顾名思义就是把进程放到一个组里面统一加以控制 。本质上,Cgroups 是一种对进程资源控制(比如可访问内存,CPU时间使用)的技术手段。 这里我们可以更加清楚的明白,Docker只是一个基于操作系统提供的虚拟化能力的一个上层应用,其核心功能都是由内核实现。这也是与虚拟机最大的不同点。 什么是Kubernetes Kubernetes 是一个由Google 主导开发的开源容器编排平台,通过数年的发展已经成为了容器编排的事实标准。各大云平台也都提供了完善的Kubernetes功能,我们可以在Kubernetes的源码中看到目前适配的云平台。 在Kubernetes的世界里,我们不在关心具体的服务器细节,我们看到的是一个一个CPU,一条一条内存,一块一块的磁盘摆在眼前,服务器已经被完全的抽象为计算资源。应用服务器的无状态化带来的最大好处是扩容的便利性,我们可以借助云平台,在数分钟内完成百倍的吞吐量扩展。另一方面,对于应用开发者,所有的一切都在kubectl 这一个工具中。 Kubernetes 基础服务 0. Master Master 管理着整个集群的状态与访问。 1. 节点 节点(Node)代表着一台物理实例。所有集群中的容器都运行在节点中。一旦某一个节点发生故障,则运行在该节点的容器会自动的迁移到其他节点(注意,迁移的过程是销毁重建。事实上为了状态的一致性,在Kubernetes中没有容器的停止与重启的概念)。 在集群运行中,我们可以添加/减少节点。在节点新增之后, 2. 网络 在集群(Cluster)内部,Kubernetes使用虚拟网络层进行网络通讯。这使得每一个容器都有一个自己独立的ip。我们可以从任何一个容器通过这个ip访问到另一个容器内部。 对于Web应用,我们经常需要部署多个实例用于负载均衡,在Kubernetes,可以通过Service服务非常快速的创建一个内部负载均衡(ELB)。对于每一个Service,仍然会分配一个ip,使得我们可以从一个容器去访问另外一组容器而无需任何额外的程序(例如Haproxy,Nginx)支持。 在虚拟网络之上,Kubernetes还提供了针对虚拟网络的DNS系统,我们只需要按照特定的规则就可以访问不同的Service,连ip都不需要知晓。 虚拟网络完全的避开了对于物理网络细节(例如Ip 地址,端口号)的依赖,使得我们可以通过配置得到一个完全一致的集群环境。 除了使用虚拟IP进行访问之外,Kubernetes还提供了基于DNS的访问办法。 3. 存储 我们永远不应该在容器中存储任何需要持久化存储的数据(Mysql,Redis),因为容器会崩溃重建,故障迁移等原因造成自动重新部署,这些动作都会造成数据的丢失。如果在容器中需要使用持久化存储,我们需要使用 Persistent Volume 服务。该服务通过将外部的持久化存储系统挂载到容器中进行使用。对于一块磁盘,可以挂载一个允许写操作的卷到一个容器,以及挂载多个只读权限给多个容器。 4. 配置 不建议将容器中应用程序的配置文件在构建镜像(Docker Image)的时候打包进去,这会造成配置文件的变更需要重新构建。在Kubernetes中,提供了ConfigMap服务进行配置文件的管理。 我们通过会通过ConfigMap来构建应用程序的配置管理,然后将其挂载到容器中使用。这可以非常容器的让我们的应用程序跑在不同的环境中。 5. 调度 当我们向集群新增加一个(些)容器的时候,集群会自动将容器部署到合适的节点。集群会根据不同节点(Node)的状态(节点状态,节点资源状态)来进行规划,并且自动部署。在新增容器的时候,我们还可以指定对应的Node Label将不同的服务部署到不同的物理实例中,以实现服务的物理隔离。 Kubernetes 核心概念 1. Pod Pod 是一个或多个Docker容器的组合,它们之间具有共享的存储与网络。Pod也是Kubernetes的最小单位,Pod中的容器会保证永远运行于同一个节点。 比如对于PHP-FPM应用而言,需要PHP-FPM进程与对应的Web Server(Nginx,Apache)。基于Docker最小部署原则,我们会将PHP-FPM与Web Server分为两个镜像。我们可以在一个Pod中同时部署PHP-FPM镜像与Web Server进程,并且它们之间仍然可以通过fastcgi的方式进行通讯。而Kubernetes又保证了这些容器具备相同的生命周期。我们将整体理解为一个应用程序即可。 2. Development 部署(Development)是一个针对如何管理Pod的工具。通过Development我们可以快速地创建多个Pod副本,并且支持滚动热更新,从而实现了应用程序的热更新。我们可以简单的将部署理解为应用的多服务器管理技术手段,在物理机时代,有ansible这样的工具来进行批量部署。 3. Service 我们已经使用部署将我们的应用部署在了多台节点中,尽管Kubernetes已经提供了虚拟IP来让我们进行不同容器之间的通讯,但是这个IP是不稳定的,因为我们的节点会进行故障迁移,宕机,从而可能更新IP。那么我们如何访问这些服务? 答案就是Service。 Service将一些Pod关联在一起,并且设定一些访问策略(比如端口映射),并且允许设定一个固定的ip。当我们尝试去访问这些服务的时候,我们只需要访问Service的ip与端口(映射之后的Service端口)就可以访问这些服务,并且Service本身可以进行负载均衡与健康检查。 这意味着Service是一个内部负载均衡器(ELB)。 4. Ingress Ingress本质上和Service一样,也是一种流量代理的概念,但是与Service不一样的是: a. Service是4层代理,Ingress是7层代理(Http)。 b. Service是服务的集群内部访问方法,Ingress则是通过一组规则来控制从外部系统(internet)到多个服务的访问方法。 我们可以设定不同的域名,不同的Http url path 路由到不同的后端服务(Backend Service)。 因此,Ingress代表着流量入口和负载均衡(LB)的作用。 5. StatefulSet 从概念上,StatefulSet与Development是非常的相似,但是不同点是:Development是对无状态Pod的管理(比如Http Server),而StatefulSet则是对有状态的Pod进行管理,比如(数据库 MySQL),同时会遵循固定的顺序进行启动或销毁容器。 通常地,StatefulSet服务都会搭配持久化存储服务一起工作,我们会将数据库的数据目录指向持久化存储中,这保证了容器在销毁、重建之后数据仍然不会丢失。 6. DaemonSet Daemon Set是一种独特的部署方式,它保证了容器会运行在所有指定的节点中,并且保证在节点生命周期内一直存活。这通常适用于一些节点监控程序或采集程序。比如fluentd 或 logstash 7. Job Job是一种一次性工作程序的部署方式。它会在所有指定的Pod运行完之后结束生命周期。比如我们可以使用Job来运行数据库迁移程序。 8. CronJob 从名称可知,CronJob是计划任务的方式来运行程序,并且使用Cron语法来描述间隔周期,但是CronJob会有一些需要注意的地方: a. CronJob是分布式的,它不会保证运行在某一个确定的节点中,除非在YAML中指定。 b. CronJob可能因为调度的问题,造成运行多次,这需要我们保证CronJob程序的幂等性。 Kubernetes YAML 格式分析 Development YAML Sample apiVersion: apps/v1 #当前服务的API版本,对于Beta中的服务,需要在部署Kubenetes集群中进行开启允许使用beta服务。 kind: Deployment #不同的服务类型使用Kind进行标记,比如Service,Ingress metadata: name: nginx-deployment #name是一个非常有用的属性,它会在DNS系统中会使用 labels: app: nginx #当前服务的标签设定 spec: replicas: 3 #Pod的副本数量 selector: matchLabels: app: nginx template: #Pod的模板 metadata: labels: app: nginx spec: imagePullSecrets: #如果镜像使用的私有镜像仓库,则需要指定docker仓库的key才可以pull docker镜像 - name: DOCKER_PULL_KEY_NAME containers: - name: nginx #容器名称 env: #附加到容器中的环境变了 - name: ENV_NAME value: ENV_VALUE image: nginx:1.7.9 #镜像 ports: - containerPort: 80 #容器内部的需要导出的端口 总结 Kubernetes 凭借着Google多年的大规模服务器管理经验,将后端架构完整的抽象出了一些更高级别的概念,在这些概念中我们完全脱离了服务器本身的限制,不在操心具体的服务器配置。我们可以将更多的精力关注自己的应用。 参考资料 http://man7.org/linux/man-pages/man7/namespaces.7.html https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt

2020年08月12日 0Comments 5Browse 1Like Read more
技术

一种通用递归深度检测技术 - 基于栈帧内容的检测 - Golang语言描述

背景 在递归处理的调用中,在具体的工程实践中一般会引入递归深度检测,防止因为错误的数据造成系统的资源极大的消耗,本方法定义了一种通用简单的递归检查方法。 步骤 实现函数RecursiveDepthChecker func RecursiveDepthChecker(max int) bool { //注意,这里我们跳过了本函数自己的栈,找到父调用的函数名 caller, _, _, _ := runtime.Caller(1) currentFuncName := runtime.FuncForPC(caller).Name() stack := make([]byte, 65535*1) //max 1MB stack traceback //由于Golang Runtime中很多关于栈的函数未导出,无法使用。因此使用最肮脏的字符串检测方法 runtime.Stack(stack, false) start := 0 depth := 0 for { count := strings.Index(string(stack[start:]), currentFuncName) if count >= 0 { start += count + len(currentFuncName) depth++ } else { break } } if depth > max { return false } return true } 在需要进行检测的函数用引入检查即可 func TFunc() { fmt.Println("Start Caller...") if !RecursiveDepthChecker(5) { fmt.Println("Stack Overflow") return } TFunc() }

2020年08月12日 0Comments 4Browse 0Like Read more

Misko lee

男人,就要像贝多芬一样,扼住命运的喉咙 :)

编程说

·编程说·

每周日晚11点更新,节假日不打烊

Archives
  • August 2020
Newest Hotspots Random
Newest Hotspots Random
此去经年 - 永兴未来城[2033年] 编程说【第二期】 - 聊聊系统集成与中间件开发 请别拿一两个胸部和游戏来说事!爷,不堕落! 也谈移动客户端的市场与发展趋势 Java编程中影响性能的一些特点 C++的苦口良药
也谈移动客户端的市场与发展趋势一种通用递归深度检测技术 - 基于栈帧内容的检测 - Golang语言描述Kubernetes 简介TiDB Markdown Plugin - 将数据库运行在你的文章里从一个实际案例来讨论我们应该如何做性能优化Web Audio API 的介绍与应用
TiDB Markdown Plugin - 将数据库运行在你的文章里 也谈移动客户端的市场与发展趋势 请别拿一两个胸部和游戏来说事!爷,不堕落! Kubernetes 简介 一种通用递归深度检测技术 - 基于栈帧内容的检测 - Golang语言描述 编程说 - 如何设计日志格式
Tag aggregation
android 编程说 html5 web audio parser markdown tidb wasm docker k8s kubernetes go
友链
  • Yian
  • 云风
  • 周梦康
  • 橡皮檫檫
  • 风雪之隅

COPYRIGHT © 2020 Misko Lee's Blog. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS