简单的业务更考验技术--化腐朽为神奇

  金庸经典《射雕英雄传》里,黄蓉为了让洪七公交自己和靖哥哥武功,天天对师傅美食相待,在做了“玉笛谁家听落梅”这样一些世间珍品之后,告诉师傅说今天要做的是"炒白菜"。洪七公露出非常欣赏的眼光,说:“好,我倒要看看你怎样化腐朽为神奇。”上周五听了一个我们内部的深度学习讲座,基本这方面处于初始探索阶段。上周六去3w咖啡听了百度的人工智能讲座,他们的深度学习也只限于对代码的训练。想一想代码这个东西分支相对来说还是有限的,所以现在的各种集成开发软件已经很简化程序员的工作了,所以看百度做的基于AI的效果还是有点杀鸡用牛刀。我们部门不涉及大数据,云计算,人工智能,深度学习这些听起来高大上的业务和技术,但是扎扎实实做好自己要比用这些包装好的TensorFlow啥的对底层的理解要深入的多。特别是在长期做一个业务的过程中,系统一些潜在的问题会在大脑中不断的旋转,一些新知识的摄取很快就能够产生对现有业务的改造想法。好了:Talking is cheap, show me the code.

  我现在主要在做两件事,一件事是媒资接口的并发量上不去,我已经跟领导说好了:给我时间,我会搞定的。我脑子里的方案有A,B,C,D,E。但是这个业务相当重要,实际上做的时候虽然觉得这个架构太老太不合理,也只能先从JVM调优,dubbo参数调优,缓存参数调优这些做起,看看不改动架构的前提下能改善多少。然后再从一些局部的性能消耗点入手,从局部到整体一点点来。上周因为并发量上去之后,CPU使用率过高,jstat看到minor gc相当频繁,并发量上去之后竟然达到每秒4,5次。就先增加了新生代的容量,效果有,但是很小。用jmap看到频繁的对象是系统的本地缓存,这个是定时任务每次新数据覆盖的,会有相当多的垃圾回收。而且每次服务层启动,先运行大量本地缓存,很耗时,服务已经暴露,但是实际还不具备处理能力,结果经常会启动时出现dubbo线程池满,一段时间后自行恢复。为了解决本地缓存的问题,我想采用缓存数据存于redis,用canel订阅mysql的更新,启动时只是取一下redis的值,采用redis的哈希结构,可以直接反序列化成java的hashmap,很快。然后监听redis更新,不用定时跑。这样就涉及到一个问题:线上没有此业务的redis集群,本地缓存的字典值很多,究竟性能怎样,需要测试对比。

  好在我还有另外一件事情要做:离线服务,之前做的时候也比较仓促,虽然细节处我做了很多的优化,处处体现java功底,但是跟人家讲,我讲不出来到底这个有什么闪光点,太零碎了,人家听了就一个反应:不就是一个后台服务嘛。确实从大的架构层做的就不像一个架构师,仅仅增量上做了一个负载分摊,全量只是简单的主备。像全量这种既消耗时间又消耗资源的,怎么能从一开始不做分布式计算呢。于是最近做了一版改造,解决分布式计算,横向扩展问题。采用redis作为中间通信工具和字典存储工具,正好和媒资接口的字典数据是一样的,这样就可以用这个项目来进行线上本地缓存性能的测试,而不影响最重要的媒资接口服务。本来也想用搜索中间件来存储数据,解耦数据库,因为我最终肯定是要做自己的搜索中间件的。但是确实,对于项目来说是可用可不用的东西,还增加维护成本,那就不应该用。想做自己找时间做去。

  

  上面是整个系统的架构图。其中包括了对接搜索部门直到面向用户终端的整个流程,里面用到了自己做的离线数据框架epiphany。放在我的github:https://github.com/xiexiaojing/epiphany。可以通过maven管理下载,pom配置如下(如需引用请注意版本更新):

<dependency>
  <groupId>com.brmayi</groupId>
  <artifactId>epiphany</artifactId>
  <version>0.7</version>
</dependency>

框架核心思想:

  将离线数据服务划分为全量服务,增量服务和手动处理服务三部分。全量和增量采用redis作为作业调度和管理机制。在redis宕机时各个服务独立运行,产生相同的输出,结果集是在正常情况下的n倍,n为服务器单元。其中全量服务因为原型是在我们项目的离线服务基础上进行开发,数据量大,文件压缩后是几十G的数据量级,所以数据存于磁盘。每个服务通过redis获取将处理数据的区间,各自处理。服务器的磁盘采用async同步处理结果。为了高可用,采用的是分步计算,结果冗余。获取方可以将其中一个磁盘作为主磁盘作为hadoop的节点或者采用linux的async同步,或者ftp,nfs等手段拉取数据。增量服务可以采用消息队列等手段进行数据传递,如果消息多,消息体大,可以用消息传递更新的id,内容可存于磁盘,中间数据库,缓存等,让调用方来进行拉取。手动处理服务直接采用netty处理客户端的http请求。整个框架运行不需任何外部容器。直接用jvm运行main方法。

框架使用方法:

  整个架构体系已经在框架内部处理,业务方只需实现DataService接口,将数据传入框架,然后按照自己的需求启动服务即可。DataService接口定义如下:

package com.brmayi.epiphany.service;

import java.util.List;

import com.brmayi.epiphany.exception.EpiphanyException;
/**
 *
 * 	通用文件处理类:这是业务代码的核心类
 *
 *            .==.       .==.
 *           //‘^\\     //^‘\ *          // ^^\(\__/)/^ ^^\ *         //^ ^^ ^/6  6\ ^^^ \ *        //^ ^^ ^/( .. )\^ ^^ \ *       // ^^  ^/\|v""v|/\^^ ^ \ *      // ^^/\/  / ‘~~‘ \ \/\^ ^\ *      ----------------------------------------
 *      HERE BE DRAGONS WHICH CAN CREATE MIRACLE
 *
 *      @author 静儿([email protected])
 *
 */
public interface DataService {
	/**
	 * 根据ID进行业务数据处理
	 * @param dealIds 处理ID
	 * @param path 要保存到的磁盘路径,不需要保存磁盘,可以为null
	 * @throws EpiphanyException 抛出通用异常
	 */
	public void dealDataByIds(List<Long> dealIds, String path) throws EpiphanyException;

	/**
	 * 根据时间区间获取id列表
	 * @param beginTime 开始时间
	 * @param endTime 结束时间
	 * @return id列表
	 * @throws EpiphanyException 抛出通用异常
	 */
	public List<Long> getIds(String beginTime, String endTime) throws EpiphanyException;

	/**
	 * 根据开始结束ID处理数据
	 * @param beginId 开始ID
	 * @param endId 结束ID
	 * @param path 要保存到的磁盘路径,不需要保存磁盘,可以为null
	 * @throws EpiphanyException 抛出通用异常
	 */
	public void dealDataByBeginEnd(long beginId, long endId, String path) throws EpiphanyException;

	/**
	 * 取得最大ID
	 * @return 最大ID
	 * @throws EpiphanyException 抛出通用异常
	 */
	public long getMaxId() throws EpiphanyException;

	/**
	 * 取得最小ID
	 * @return 最小ID
	 * @throws EpiphanyException 抛出通用异常
	 */
	public long getMinId() throws EpiphanyException;
}

深入技术细节:

  ☆ 关于压缩:压缩是递归操作,如果java栈设置很大,压缩操作会非常消耗CPU。所以框架设计时,业务方可设置全量的线程数,但是压缩是异步用另外的线程池来管理,这个线程池的容量是全量线程数的一半。比如我们线上用的是24核高配物理机,现在上面有多个服务进行复用。我的离线服务是视频和专辑两个部分,有数据通用的逻辑,但是是独立的业务,所以我用一个工程来进行项目管理,但是用的是两个独立进程,采用两个脚本分开部署。千万级数据,每个业务全量都使用10个线程。在改造前的那一版采用的是专辑400个线程,视频660个线程,用50个线程的线程池来跑。测试发现改造后的10个线程速度并不比改造前差多少。原因是追加操作和文件大小关系不是很大,开销要小于新建文件的开销。线程少减少了资源开销和上下文切换。还有就是压缩操作,大文件的压缩效率要高很多。因为用的是哈夫曼系的gz压缩,减少了头文件的字符映射。

  ☆ Redis的哈希结构:这个结构看起来是对java的hashmap的很好的对应。但是实际使用的时候,如果map的key(对应于redis哈希中的field)大于1000,插入效率急剧下降。因为redis是单线程的IO,而一个map对应的redis的key是一个,所有这些写操作会被映射到一个redis节点,效率很低。我试图将一个3w7k的字典map放入redis。结果运行了近一个小时,插入了20402条后再也插不进去了,连接超时,运行几次都没能插入更多。

  ☆ 巧用对象池:我在框架中封装了有限制的对象和无限制的对象池来作为线程池进行一些异步调用。无限制的对象池是因为对象的总数在其他地方有限制。而有限制的对象池是为了防止对象在异常时过多资源占用。而异步有点地方是为了提高效率,有些地方又是必须的。比如我在程序中一个方法调用mysql取数据,而这个方法处理完数据后还要给MQ发消息,消息体特别大,发送时间特别长。长时间mysql不断开,就会连接超时异常。

 一点感悟:

  一个人的智商决定了学习的速度和领悟能力。而对情商决定了在一条路上能走多远。对一个项目的热爱可以深入到对用到的每条sql都对其性能做深入的研究。而对于整个项目的架构更可以深入到linux的内核方面。所以足够用心就会掌握更多的技术。而写一个自己的框架会对国内的框架有一个更好的理解和容忍度。比如我在写框架的时候用到的默认值和建议值都是基于我自己的项目。因为这个框架在我们内部很多的离线项目都可以用,我在考虑他们的具体环境怎样设置更加合适。但是再远一点,别人用的时候,怎么设置合理,性能曲线我还在研究中。像dubbo这种开源框架也没能在这方面给出一个特别好的文档。

周末轻松一下:

  周末在家开电脑,儿子在旁边千万不要打开数据库。否则他的小手在键盘上划一下,你就会见识到什么叫真正的噩梦。

  儿子特别黏我,我总想找借口把儿子推给他爸。昨晚他有粘着我的时候,我说:跟你爸下象棋去。儿子找了半天,蓝棋子少两个,所以他想在手机上玩。我说:将红棋子那两个也拿走就可以公平的玩了嘛。他爹平静的说:恩,没有車和將随便下。 当场笑的肚子疼。

  我要写文,他爹带着儿子去外面玩。临走很温柔的说:你手机快没电了,记得充。我一下子就感动了,好细心,暖男。再一想,前面他还说过让我在家订好烤串,5点半送到他好回来吃!

时间: 2024-10-23 22:32:36

简单的业务更考验技术--化腐朽为神奇的相关文章

互联网技术发展之路(2)- 业务如何驱动技术发展

互联网技术发展之路(2)- 业务如何驱动技术发展 在<互联网技术发展之路(1) - 技术发展的驱动力>一文中,我们详细阐述了对于服务类的业务来说,业务发展是技术发展的驱动力.那接下来我们就看看业务究竟是如何驱动技术发展的. 互联网业务千差万别,但由于他们具有"规模决定一切"的相同点,其发展路径也基本上是一致的.互联网业务发展一般分为几个时期:初创期.快速发展期.竞争期.成熟期. 不同时期的差别主要体现在两个方面:复杂性.用户规模. 复杂性 业务的发展第一个主要方向就是&qu

是业务成就了技术,是事业成就了人

这个世界没有那个网站从诞生起就是大型网站:也没有那个网站第一次发布就拥有庞大的用户,高并发的访问,海量的数据:大型网站都是从小型网站发展而来.网站价值在于他能为用户提供什么价值,在于网站能做什么,而不在于它怎么做,所以在网站还很小的时候就去追求网站的架构师舍本逐末,得不偿失.小型网站要做的就是为用户提供好的服务来创造价值,得到用户的认可,活下去,野蛮生长. 大型网站架构技术核心价值不是从无到有搭建一个大型网站,而是能够伴随小型网站业务的逐步发展,慢慢演化成一个大型的网站.这是一个漫长的技术演化和

云计算这样可以使企业业务更安全

随着云计算技术和互联网的不断发展,云计算技术也随着互联网的发展而不断发展.现在,越来越多的企业加入了云计算的发展行列.就国内云计算产业而言,近年来在云计算的学术领域,技术应用领域的许多方面都进行了大量的探索和尝试,现在我国的云计算技术已经发展到了一定的阶段. 云计算这样可以使企业业务更安全?那么云计算如何使业务更安全?对此,擅自占地者表示企业的数据安全是一个非常重要的方面.只有企业数据安全得到保障,企业才能确保业务的顺利发展.企业内的数据访问是复杂的,高浓度的用户帐户和信息资源可能导致数据丢失的

扒一扒那些说起来简单做着难的技术需求

原文链接 总有一些产品经理在跟技术沟通的时候,说这个不就是调用一个什么方法,那个按钮不就是添加个监听事件吗,有什么难的.但实际开发中要比产品经理想的困难.麻烦得多, 老夫来分享一些人僧惊艳. 对于产品和开发,两支天赋我都基本加通了.产品(游戏策划)方面的天赋点,是因曾经被坑了太多,失去了找到高契合度产品合作者的信心,于是自行转职修炼而成.作为一个二转角色,这题还是可以答一下的. 在分辨需求方靠谱度这方面,公司豢养的程序员是远不如宅家接外包的soho狗们的.被不靠谱的发包方坑乃是soho狗成长路上

简单搜索--Paddle Mobile的技术实现和业务落地

Paddle Mobile是PaddlePaddle组织下的致力于嵌入式平台的深度学习框架,集成了百度移动端预测的实践经验,提供多平台支持,在工程实现支持及底层算法模型压缩的基础上,通过CPU.mall GPU等硬件加速,作用于DuerOS.百度APP.百度网盘APP等移动端场景.目前,Paddle Mobile在PaddlePaddle 0.14版本下已支持CPU.Mali GPU等平台,以及图像.人脸.OCR等模型.值得一提的是,它的体积极小,最小仅300K.今天这篇文章是由百度大搜索高级研

文武双全!为什么数据分析师需要既懂业务又懂技术

作者:接地气的陈老师 ================================================================================= 在企业中,数据分析师们往往分为业务和技术两大类.两类能力和工作内容有较大区别,但经常企业在招人的时候都叫:数据分析师.这常使想进门的新人感到困惑.今天,我们就来科普一下业务与技术的那些事. 业务 or 技术 业务类分析师,往往在战略发展部,市场部,会员中心,销售部,运营部.根据服务的业务部门的不同,他们也可能叫数

5连进总决詹皇化腐朽为神奇 他无愧超引力场

东部决赛第四战,克利夫兰骑士展现超强统治力,以118-88狂胜亚特兰大老鹰,完成横扫.由于凯里-欧文的复出分担掉不少压力,勒布朗-詹姆斯今天打得闲庭信步,在短短29分10秒时间里,轻松收下23分9个篮板7次助攻2次抢断以及1记封盖,仅有1次失误,个人连续第五年挺进总决赛.http://www.ximalaya.com/zhubo/27969127/ http://www.ximalaya.com/zhubo/27969157/ http://www.ximalaya.com/zhubo/2796

简单理解预加载技术

预加载原理就是在浏览器加载页面的时候先创建一个对象,然后填充数据,从而达到预先加载的效果.(即按照文档流顺序,先利用js加载函数去加载图片,然后在渲染dom元素) 代码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>js预加载</title> </head> <script> //针对firefox window

java学习笔记(12) —— Struts2 通过 xml /json 实现简单的业务处理

XML 1.引入dom4j-2.0.0.jar 2.引入jquery-1.8.2.js 3.新建common.js getInfo = function(){ $.post("getXmlAction.action",{name:$('#name').val()},function(ret,status){ if("success" == status) var id = $(ret).find("id").text(); var age = $