今晚与师兄两人回校,聊起互联网分工时,师兄不免又多说了许多。说着说着,话题又过渡到人生道路上。因觉得颇有共鸣,这里依旧流水账般,记在下面。

关于互联网分工

产品

个人认为,互联网分工的必要组成部分是产品,产品分PM和PD两种,PM和PD上有产品总监。PD即Product Designer,产品经理。负责具象化用户需求,交给技术去实现。话说起来简单,但实际上,具象化需求不是那么好做的,产品总监提出大体构想后,PM多半会召集身边各部门开会,并去整理会议上各方杂乱的美好设想。首先,总监的构想有时会很模糊,甚至会有方向上的错误;其次,会议上各方的建议并不能覆盖所有细节,尤其是枯燥的细节。毕竟产品做不好是PD背锅。接下来,整合好的具体需求是不能直接拿给技术看的,否则很容易被砍死。PD需要融合、简化需求,减轻技术团队的压力。产品,做出来才是真的。接下来,把任务交给PM。除此以外,PD需要尽量为自己所带领的技术团队争取资源,作为挡箭牌处理和营销以及运维的关系甚至办公室政治,这也是让产品成功的重要一点。综上,PD是个多方受压的角色,一旦失误很容易背锅,同时它也是产品流程的重要一环,把控好产品方向,安抚好烦躁的程序员,着实压力巨大。所以,有句古话说得好,PD常有,好的PD不常有。

PM即Project Manager,项目经理。和PD在互联网产品开发中是多对一(多PD)或一对一的关系。PM需要将需求进一步细化到技术上,拆分成各个部分,交给手下的技术团队完成。同时,PM负责把握工程的时间进度。时间过了deadline是没法交差的。PM不一定得很懂技术,但一定得能让技术团队信服(虽然大多数的PM都很懂技术)。否则,手下心不齐,三天两头添乱,PM是很不好受的。毕竟产品开发不完,是它背锅。相比PD,PM距离技术更近,也会经常参与技术团队的开发。

营销

产品从设计到原型结束后,交给营销团队推广拉客。营销部门只负责拉客,并不负责接客。因为它的kpi是按照入驻用户数来计算的。营销又称销售,它做的工作和安利很像,负责为产品招揽足够数量的客户。为了保证每月或每天kpi的硬性要求,营销会将产品好的一面,甚至没有的一面展示出来。这里遇到了两个矛盾:第一,为了产品能卖得更好,营销部门会向PD施压,改变产品需求,使得更种各样的用户都能入驻。万一这时PD不够“硬”,技术团队就要砍人了。第二,营销的kpi不包括客户流失率,毕竟不是它负责接客,不负责任的拉客和承诺很容易招揽垃圾用户和愤怒的用户,这就要考验运营部门的智慧了,它要努力把人留下来,并用甜美的笑容迎接客户的怒骂。可见,运营部门也是个很考验心理素质的地方。

运营与技术

营销说完了,下面是运营。运营的KPI包括客户流失率。上面说过,用户反馈的所以负面意见90%都由运营部门的客服来扛着。产品当然不会做出的第一版就很完美,因为用户是不会接触到产品原型开发的。运营吃的营销的亏,会扔给产品和技术。埋怨产品是怎么设计的,并向PD施压,要求改变产品需求(这话听得怎么那么耳熟),因为用户反馈blablabla。这时,重复修改是无法避免的,PD再调整,PM下的程序员免不了做重复工作。如果调整得不好,技术砍死产品经理就会发生

技术分两种。第一种是学术型技术,多半不会和产品、营销、运维三个部门一起提,即真正的技术。它是埋头做的,不一定短时间内就会生效的。大公司里会分成专门的部门。第二种是工程型技术,和产品在一起提到,很多互联网公司会有“技术池”这个概念,即程序员们的工作并不固定,自己自由选择或分从分配到某个产品线,和某个PD合作。

综上,我们看到,产品、运营、销售是三个相互坑与被坑的角色,矛盾统一的是它们的目标一致——这个产品的前景。换句话说,它们是一条绳上的蚂蚱,相互间痛并快乐着。高工资不是白拿的,它们的压力透过文字就可以辨别出。

关于悲惨的程序员

程序员的悲惨,其实是不需要这段文字就能看出来的。对需求的重复修改是程序员即普通技术的噩梦。技术(以下均指普通技术)的KPI是按照需求数量来计算的。PM把需求分解,按照难度打分后,交给技术完成,很像完成成就。如果PD或PM的功底不够,产品细化的不够狠,技术的KPI就会比较难看。这还不算悲惨。如果PD不够“硬”,产品原型还没出来,就被销售改了几个需求,技术的开发工作就会重复进行,而新的实现方法是不计入KPI的。不计入KPI倒可以忍,改得晚,deadline就在眼前才是最要命的。实际上,程序员加班并不是工作量大,而是重复工作太多了。而PD里有许多应届,出色的PD少,这也就成了业界程序员的常态。

产品推出后,运营也加入了挤压产品、技术的队伍,技术又会针对客户的反馈进行修改。大家也猜到了,这个修改也是不计入KPI的。如此多的工作,却对KPI毫无贡献,悲惨的程序员该怎么想呢?呆不下去就走呗!这也是互联网离职率高的重要原因。

以上讲了技术跟错了产品和产品经理的悲剧。好的产品新修改多,重复修改少,KPI高,年终奖拿到手软,不好的产品线注定会多处很多重复修改,却,见不到用户量的大提升。如果我们隐忍的程序员还没有放弃,产品线终于崩溃了。这时,技术面临两种命运,一,等着被随机分配到另一个产品线上,用鸟枪法找一个靠谱的产品和产品经理;二,原来工作努力上进,口碑好,被好的产品线挑走,从此人生改变。可见,对于技术来讲,找一个信得过的产品是多么重要;幸运的是,对于产品经理来说,找一个信得过的技术也是很重要的(产品实现不了是它背锅)。说到底,这里做事还是看人。

关于技术的前途

工科学校出身,有技术功底,出去最有可能就是技术了。那该选择什么道路,能避免上面的悲惨命运呢?上面也提到了,说大了,技术无非两种,研发型和工程型。前者强调在深度,后者强调在广度。因此,对于前者,在知识上的精深,对高深技术的掌握很重要,Google、微软中国这样的公司将是理想的归宿,尤其是对于技术痴迷者。若出世没那么厉害,互联网等公司的研发岗也是不错的选择。这一切的前提是,得好好学习。

工程岗,又称技术民工。在知识上的灵活,对多领域的技术掌握、有工程技术实践经验很重要。因为,技术毕竟不能干一辈子。长期来看,管理岗位是理想选择;短期来看,PM是最好的跳板,如果因为产品线大限已到,或是因为其他原因调岗后,能因为之前的经验升任PM。那恭喜,你终于能翻身农奴把歌唱,由被坑的角色,转成坑人的角色了。不过,这个几率在你跟对产品线才会比较大。

看来,说到底,还是得跟对人。

近日,和做产品经理的师兄一同返校。聊了许多,顿觉收获颇大,在此以故事的形式一并记之,想到哪儿写到哪儿,以励后进。

师兄本科写了四年的代码,保研去了经管院,实习先后去过咨询公司,外企,互联网三巨头BAT,也算是对产品和商业模式有了些自己的认识与理解。最后借现在这家创业公司的环境,实践以前之所学。

关于创业

创业分五个阶段,第一个阶段,产品由无到有,几个核心成员借由最初的idea,用技术完成一个雏形。这个阶段用技术去证实这个产品是可行的,因此技术占了这个阶段绝大多数工作。产品雏形是它的终点;第二阶段,产品雏形出现以后,团队开始扩张,初创成员开始出现分歧,技术以外的部门诸如融资、营销等会加入团队,这是个不稳定期,需要有“领导”角色的产生和团队内的磨合。一个较为成熟的商业模式在这个阶段形成(注:商业模式的规划是一开始就要做的)。一般,A轮融资发生在这之间,股权的划分也是对团队的考验。成熟的商业产品和商业模型是它的终点;第三阶段,有了融资和统一的产品规划后,将这个商业模式做大,通过烧钱推广产品。这个阶段里,我们会用数据去衡量产品的发展前景,运维、行政等部门又会加入。当用户数出现质的提升时,证明商业模式在社会上可行。这也标志着第三阶段的结束。在这之后,公司一般会迎来B轮融资,以千万起。而大多数公司都倒在第二、三阶段。

第四阶段,商业模式被证明可行后,在各个维度上推广复制,开辟到不同城市、不同周边领域。利用融资继续扩大规模,一般到此,创业者会获得真正的财政自由;第五阶段,公司继续扩张,迎接IPO。在这两个阶段间遇到的困难会大大小于前三个阶段。

综上来看,难倒大多数创业者的门槛反而成了团队内的团结统一。的确,扪心自问真正信得过的合作伙伴太少了,剩下的就好好珍惜吧。现在,我就在一家创业公司的第二阶段,面临着产品从雏形到成熟过程,尽管技术团队人很少,暂时也没有一套成熟的工作模式。不过这种创业的氛围恐怕也能让我学到不少。包括和PM的沟通,技术和需求间的取舍等。如果这条路能跟着走下去的话,学到的将是BAT教不了我的。

关于开源软件与商业模式

正巧这阵在做一个开源小工具,和同寝的技术一起合作开发。就顺便问起了小团队开源开发需要注意的地方。师兄提到,开源软件并非只有免费公益这么简单,从商业模式角度去考虑,这是推广市场或积累口碑的最好选择。以Symbian和Android为例,后起的Android为何为打败Symbian,开源占了很大一部分原因。可是开源的同时,Google难道就没有收益么?正是因为Google核心的商业模式在其搜索引擎上,Android是自带Google搜索的,这才是它关心的;而且Android的知识产权是Google的,这将给Google隐形的收益。以CSDN为例,里面有许多写技术博客的开发者,在开源自己产品的同时,获得了转发,评论等社会影响力,这笔关注在求职或是发表言论将会发挥到它的作用。

所以,有时问题换个层面去思考,会得到很有意思的结果。在开发开源产品的时候,从商业模式上去考量,会得到以前从不会想到的一些问题。合理的商业模式将使产品被用户所接受,甚至会给自己带来收益。

关于眼界与技术

自然科学,社会科学,人文科学这对于一个大学生来说都是需要的。不同的方面的了解将会使你对问题有不同的认知方式。这也正是综合性大学在学生培养上的天生优势。北邮是所很靠谱的理工科大学,所以培养出的学生严谨有余,而灵活、情怀不足。社科赋予人对社会和人际的了解,人文赋予人对艺术和设计上的了解。在创业型公司里,工作中出现的问题种类会比大公司多,多方面的掌握有时会给我们很大的启示。

在说到创业时,师兄突然感慨似乎我们都陷入了完全做好准备再出发的怪圈,许多半路出家边走边学的人却提前取得了成功。难道是研究生严谨学术的氛围使得毕业生们都不肯轻易出手么?研究生的两年半里,不要把眼光限得太死,不要抱着技术不放手,要勇敢接受新事物,展示自己的学习能力,接触更多方面的人,这可能是现阶段对我最好的启示。

而那个开源小项目,可能也得重新思考了。

1月1日,一个值得纪念的日子。校园网告别免费时代。学校美其名曰合理化用网,提升学生们的用网体验。实际上,之前流量用不完的现在依旧用不完,之前“流量大户”的现在忍痛充值也会继续用。10G流量的悬顶之剑,实际上没吓退多少人,只是增加了部分人的刚需消费而已。

不是我说“屁股决定脑袋”,要想真正达到预设的目的,首先就不该一棒子打死所有人。固然,占用大多数流量的人,“是时候让他们流点血了”,但这决不是简简单单一个10G就解决的。没有调查的行动,当然会引起大部分群众的反感。在1个月内的试运营的时间里,也没看到“相关部门”做出的什么调查报告。在我看,多档流量的区分会更好。也没见北京地铁一口价吧。

其次,尽管不能完全“疏”,这种硬“堵”的方式很容易让人误解学校的做法。因此,我不同意学校做出的不能使用运营商网络的决定。正是此决定让大家忍痛也会尽量保持之前的需求。这里很像广电的做法,一刀切,不过审的就是不许上,没有个其他的途径。而硬性需求就像水,有缝总会找地方流走的。不能像电影分级一样么,给观众另外的渠道,起到引流的效果。

说完学校政策方面,我们再来从另一个角度聊聊。窘迫了这么多天,包括我在内的一些人都会感叹,流量总是不够用,要是能有包月服务,一定会大受欢迎。但是,我认为,如果以后真将推出,那包月服务会是个糟糕的决定。设想,学生们交了钱,继续往日的用网习惯,该卡的时段还是卡。学生的用网习惯并未改变,只是多交了些钱罢了。这样,学校本就颇受诟病的目标将更加变味了。毕竟,理想地,网费是次要的,提升网络质量才是主旨。可是,谁又知道学校下一步会怎么改变甚至会不会改变呢,就像半年前谁知道延续数十年的免费午饭在我们这儿就到此为止了。

最后,可以说说群众的不同反应。收费后的群象和乱象不仅令我回想起小时候的场景。那时,父母所在的厂公转私,这是个大事件。有很多员工面临着买断工龄下岗的危机。整个镇都有些人心惶惶,有人骂骂咧咧拿钱走人,有人精明地提前在市区里找了新工作,有人忿忿不平欲和厂领导说理,大部分的人和我的父母一样,没有多提什么只是默默接受着,为留下来做些自己的努力。终于,一个夜晚,几个工人代表带着大批群众聚集在领导办公场所旁,要求立即解决问题。那晚,事情变得有些难以收场。最后,代表们被劝退,事情不了了之。一个多月后,父母换了工作地点,但并未下岗,事情渐渐平息。

说到这里,你联想到什么了呢。哪些人骂骂咧咧拿钱走人?哪些人提前找了新工作?哪些人聚众说理却不了了之?又是哪些人默默接受着?同样的,可以想见,在新政策下来后,无论是合理也好,畸形也好,事情都将会平息,群众总有它独特的适应方式的。至于盗用流量等等的恶性事件,只是从大的校园环境放到了时下的校园网收费环境而已。

2015新年伊始,校园网开始收费。明光村职业学院免费上网的岁月一去不返。我等广大群众感慨之际,突然发现流量用得如此之快。身边的朋友也开始渐渐对看视频心有余悸,对实时流量(此处仅指收费的v4校外流量)的关注也日渐升级。校园网流量计的想法便这样出现了。

p.s. 鉴于开发过程中,有队友的加入,所以下文按照时间顺序而非严格的逻辑顺序进行,望读者谅解。

背景话毕,如何分解我们的需求呢?要想让流量数据可视化,当然又必不可少的两点:a)数据 b)可视化。具体说来便是:

  • 怎么拿到流量数据
  • 怎么更友好的用图表的形式展示

这么一想,解决方案就出来了。

最初设计

考虑到用户对流量实时性了解的需要,决定用动态的折线图呈现最终效果。折线图能显示最近半小时内校园网流量的变化,每约30s采集一次数据。

爬虫程序

既然要收费,学校必定会给学生查看流量的渠道。netaccount.bupt.edu.cn需要离线后才能查看,上网注销窗是最好的选择,每次刷新即可显示当前上线时间,使用流量,剩余网费,很直观;最重要的是每次刷新都能得到实时数据。就爬它了!

写爬虫程序的语言可以有很多,从Java到python到php到js甚至windows shell都可以轻松完成这个简易的爬虫任务:爬取html页面,匹配流量字段,存储。

在爬之前,我们需要知道流量值藏在html页面代码的什么地方。打开开发者工具,代码一览无余。

从flow的大小来看,存储的单位是KB,没关系/1024/1024就行了。准备工作做完后,可以开始码代码了。python比较好上手,我们暂时选择python写爬虫。不说废话,直接上干货。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# -*- coding: utf-8 -*-
#---------------------------------------
#   程序:网关爬虫
#   版本:0.2
#   作者:xym
#   日期:2014-01-03
#   语言:Python 2.7
#   功能:输出当前已用流量
#---------------------------------------
#import urllib
import urllib2
import cookielib
import re
import time
import json
class GATE_Spider
    # 申明相关的属性 
    def __init__(self):   
        self.loginUrl = 'http://10.3.8.211'           # 网关url
        self.cookieJar = cookielib.CookieJar()        # 初始化一个CookieJar来处理Cookie的信息
        self.flow = []      #存储流量
        self.flow_json = []
        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar))
    def flow_init(self):
        for x in range(3):
            # 初始化链接并且获取cookie
            myRequest = urllib2.Request(url = self.loginUrl)# 发送查看请求
            result = self.opener.open(myRequest)            # 得到上网注销窗
            # 打印返回的内容 调试用
            # print result.read()
            self.deal_data(result.read().decode('gbk'))
            self.flow_json = json.dumps(self.flow,indent=2,separators=(',',':'))
            self.cal_data(self.flow);
            time.sleep(5)
    # 抠出流量并换算
    def deal_data(self,myPage): 
        myFlow = re.search(r';flow=\'\d+',myPage)
        myFlow = re.search(r'\d+',myFlow.group(0))     
        self.flow.append({'flow':float(myFlow.group(0).encode('gbk'))/1024,'time':time.time()})

    # 储存流量
    def cal_data(self,items):
        with open('./new.dat','w') as f:
            f.write(self.flow_json)

#调用 
mySpider = GATE_Spider() 
mySpider.flow_init()

思路很清晰,因为登录工作在程序运行前已经完成,cookie已经存在本地,每次刷新只是简单地GET请求而已。所以在初始化后,只需模拟GET请求,拿到回复后,匹配第一次出现的flow和fee即可,以标准的json格式存储在.dat文件里,每过30s循环一次。

p.s.有意思的是,上网注销窗那里的MB竟然是字符串拼接拼出来的,直接/1024就可以得到的结果却都两个大圈子才拼出来,不知目的在哪里。。。

好了,测试之后,文件里已经把流量,网费,时间都按顺序储存好了。怎么和前端数据可视化互动呢?最好的方式当然是选择js,用网页的形式呈现。不过,为了保证实时性,js需要不间断地向后台发起请求。整个流程应该是由js端驱动的。而我对python作网站后台并不熟悉,无奈之下,只能暂时放弃python爬虫这个主意,选择php爬虫。不过,思路上大同小异,没过多久,php的爬虫后台端就搞定了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
header("Content-type: text/json");
class Spider{

public $flow;
public $fee;
public $time

function __construct(){
$this->flow = 0;
$this->fee = 0;
$this->time = 0;
}
/*
get content from a certain URL
@param $url string
@return string
*/
function getURLcontent($url){
// 初始化
$data = "";
$curl = curl_init();
// 设置cURL 参数
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

// 运行cURL,请求网页
$data = curl_exec($curl);
$this->time = time() * 1000+3600*8000; //to fit UTC time
curl_close($curl);

// 显示获得的数据
//var_dump($data);
return $data;
}
/*
get flow and fee from certain message
@param $content string
@return boolean
*/
function simplifyURLcontent($content){
$flow = 0; $fee = 0;
preg_match('/flow=\'([\S\s]*)\';fsele=\d+;fee=([\S\s]*)\';xsele/', $content, $matches);

if(!$matches)
return false;
else{
//查找流量和费用字符串
$this->flow = intval($matches[1])/1024/1024;
$this->fee  = intval($matches[2]);
return true;
}
}
/*
return json to js
@return mixed
*/
function deliverFlow(){
$ret = array($this->time, $this->fee, $this->flow);
echo json_encode($ret);
}
/*
continous work flow
@return false
*/
function workFlow(){
$ret = $this->getURLcontent("http://10.3.8.211");
$isGet = $this->simplifyURLcontent($ret);
if($isGet) $this->deliverFlow();
return false;
}
}
$gate = new Spider();
$gate->workFlow();
?>

这样,js每次发送.ajax请求时,php就可以返回前端需要的数据了。

这么来看,后台爬虫端应该就这么多了。

数据可视化

好的,现在我们已经可以拿到所需的数据了。关键是怎么去呈现它。最好能有现成的工具来好好利用收集到的数据。前人怎么会没考虑到这点呢,js charts用SVG等方法渲染出来的图表足够用了。这里,我选择了Highcharts。Highcharts的文档很丰富,API文档也很充分,所以有一定web基础的人很容易上手。

Highcharts提供了很多种图表类型,考虑到实时性的需要,我们选择最基础的line类型。这里不再介绍Highcharts的基本信息,感兴趣的可以去它的官网查看。既然后台php已经把json标准化的数据拿过来了,就看Highcharts这边怎么用了。

由于这里各点数据是动态更新的。这里Highcharts的Chart对象里,series需要动态更新。这里写个简单的ajax,向后台index.php请求数据,根据上面的php程序,将得到一个[time,fee,flow]的json类型数据point,第一个自然是x轴数据,第二个是第二条y轴数据,第三个是第一条y轴数据。这里我们用series的addPoint方法把数据新增进去即可。由于图只显示近半小时数据,我们姑且视作50个点,因此这里还要判断点数是否超过50,然,则平移。因此addPoint的第二个第三个参数分别是TRUE和判断是否平移的Boolean值。在得到数据后,setTimeout来实现每约30s请求一次数据。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$.ajax({
url: 'index.php',
success: function(point) {
var series = chart.series[0],
series_f = chart.series[1],
shift = series.data.length > 50,
shift_f = series_f.data.length > 50; // shift if the series is longer than 50
// add the point
chart.series[0].addPoint([point[0],point[2]], true, shift);
chart.series[1].addPoint([point[0],point[1]], true, shift_f);
// call it again after one second
setTimeout(requestData, 20000);
},
cache: false
});

之后的就比较简单了。设置好前端表格的格式就可以看到炫酷的效果了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
defaultSeriesType: 'spline',
events: {
load: requestData
}
},
title: {
text: 'Campus Network Live Flow Data(ipv4 only)'
},
credits:{
enabled:false
},
xAxis: {
type: 'datetime',
tickPixelInterval: 40,
maxZoom: 50 * 1000
},
yAxis: [{
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Flow (GB)',
margin: 30
}
},{
opposite:true,
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Balance(yuan)',
margin: 30
}
}],
series: [{
name: 'FLow data',
data: []
},{
yAxis: 1,
name: 'Balance data',
` data: []
}]
});
});

嗯~ 看上去还不错。

p.s. 经测试,在后台取的时间不经处理会早8个小时,这是由于UTC和时区的原因,在后台额外加上整8个小时即可。

历史数据

在实时数据做完后,有朋友参与了进来。经过讨论,我们认为做一个历史性数据展示窗口也是必要的。毕竟,不是所有人都愿意开着网页等着数据更新。后台悄悄地采集数据会更友好。于是,下一步开始了。

有了前面的基础后,后面的开发要快许多。不过我们遇到了一个现实的问题。既然我们的小工具是面向不一定懂编程的用户的,那么php做后台就变得有些不现实。所以这里我们决定用windows的shell来写,并推广到不同操作系统。由于篇幅限制,我们将在下篇日志里介绍这种方法。这里仅对前段部分进行讲解。

思路还是类似的,后台采到的数据用csv格式存在.dat或诸如此类的文件里。Highcharts需要读入其中所有的数据,并存入到series的data域里,一次请求,一次性完成,这是不同于上个功能的。

依旧,在Highcharts的chart对象实体化前,得对变量赋值。重点当然在对csv格式的文件读取中。这里用jQuery的get方法,虽然js跨域访问陌生网站很困难,访问本地资源还是没问题的。读入数据后,以’\n’为分隔得到每个点,这里要初始化一个series对象,预存两条数据线的name,yAxis属性。然后.each函数里,split数据by ‘ , ‘, push到series的data域里即可(注意,这里最好对值parseFLoat处理一下较好)。最后将两组series push到chart的options里(注意,需要push两次,因为是两组数据)。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$.get('data.csv', function(data) {
// Split the lines
var lines = data.split('\n');
var series = [{
name: 'Flow Data',
data: []
},{
name: 'Balance Data',
yAxis: 1,
data: []
}];
// Iterate over the lines and add categories or series
$.each(lines, function(lineNo,line) {
var items = line.split(',');
series[0].data.push([parseFloat(items[0]),parseFloat(items[2])]);
series[1].data.push([parseFloat(items[0]),parseFloat(items[1])]);
});
options.series.push(series[0]);
options.series.push(series[1]);
// Create the chart
var chart = new Highcharts.Chart(options); 
});

配合上预先设定好的Highcharts其他属性,综合成一个options初始化一个Highcharts.chart对象即可完成任务。再稍微处理一下前端,将两个功能组合在一起,加上css样式和一个简单的收起js,得到了半成品.

整体代码js如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
function requestData() {
$.ajax({
url: 'index.php',
success: function(point) {
var series = chart.series[0],
series_f = chart.series[1],
shift = series.data.length > 50,
shift_f = series_f.data.length > 50; // shift if the series is longer than 50
// add the point
chart.series[0].addPoint([point[0],point[2]], true, shift);
chart.series[1].addPoint([point[0],point[1]], true, shift_f);
// call it again after one second
setTimeout(requestData, 20000);
},
cache: false
});
}
var options = {
chart: {
renderTo: 'container_h',
defaultSeriesType: 'spline',
zoomType: 'x'
},
title: {
text: 'Campus Network History Flow Data(ipv4 only)'
},
credits:{
enabled:false
},
xAxis: {
type:'datetime',
minRange: 150000 // 150 seconds
},
yAxis: [{
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Flow (GB)',
margin: 30
}
},{
opposite:true,
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Balance (yuan)',
margin: 30
}
}],
series: []
};
$.get('data.csv', function(data) {
// Split the lines
var lines = data.split('\n');
var series = [{
name: 'Flow Data',
data: []
},{
name: 'Balance Data',
yAxis: 1,
data: []
}];
// Iterate over the lines and add categories or series
$.each(lines, function(lineNo,line) {
var items = line.split(',');
series[0].data.push([parseFloat(items[0]),parseFloat(items[2])]);
series[1].data.push([parseFloat(items[0]),parseFloat(items[1])]);
});
options.series.push(series[0]);
options.series.push(series[1]);
// Create the chart
var chart = new Highcharts.Chart(options);
});

$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
defaultSeriesType: 'spline',
events: {
load: requestData
}
},
title: {
text: 'Campus Network Live Flow Data(ipv4 only)'
},
credits:{
enabled:false
},
xAxis: {
type: 'datetime',
tickPixelInterval: 40,
maxZoom: 50 * 1000
},
yAxis: [{
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Flow (GB)',
margin: 30
}
},{
opposite:true,
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Balance(yuan)',
margin: 30
}
}],
series: [{
name: 'FLow data',
data: []
},{
yAxis: 1,
name: 'Balance data',
data: []
}]
});
});

剩下的部分,我们在下篇文章里继续。下期见 ~

因个人兴趣,从暑期开始做起web后台开发工作。这数月过来,无数次提交,更改,撤销更改等动作,让我深感学习版本控制的必要性。在学长的推荐下,以Git-Book为基础开始学习git。Git-Book是本很好的教材,深入浅出,条理清晰。对比而言,很适合初学者使用。

git为版本控制而生,是DVCS的一种。早期的数据库形式被用户机——CVCS服务器形式所取代。这种模式下,中央宕机会造成成很大损失。考虑到这点,分布式版本控制系统(DVCS)出现,在DVCS下,各用户机亦保存有文件的各版本,即使服务器宕机,也可以通过各用户机恢复。git是DVCS的一种。

git起源于Linux的维护工作。诞生于2005年。建立在Linux经验的基础上,git有几个很明显的特征:追求速度,简单设计,非线性开发需求,完全分布式。因此,它能在简化操作的同时,简化开发者的工作量。

学习git之前,要先建立对git几个特点的认识。这将将极大程度上有利于之后的进展。

  • git记录文件的整体快照而非文件的局部改动。若文件改动,git将记载新文件;反之,git将只储存指向旧文件的指针。
  • git继承了DVCS的特点,操作的本地性极强。不需要联网也可以使用git来保存甚至提交自己所做的改动
  • git十分强调文件数据的完整性,用SHA-1算法得到文件校验得到一个Hash值表示文件。任意文件都有唯一的40bits十六进制字符串对应。
  • git的各种操作绝大多数都是向数据库中添加数据,这意味着几乎所有操作都可以恢复。不用担心覆盖掉正确版本。
  • git管理下的文件只有三种状态——修改,暂存,提交。这点很重要。git只对提交态的文件版本负责。
    在了解了git的基础知识后,就可以在你的主机上安装git了。由于Linux环境下对git的使用较频繁,且我自己也在Linux环境下使用git,下面对Linux做重点介绍。

Linux

源代码安装

先安装依赖包

1
2
3
4
//使用yum的系统
$ yum install curl-devel expat-devel gettext-devel \ openssl-devel zlib-devel
//使用apt-get的系统
$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \ libz-dev libssl-dev

之后下载最新源代码并编译安装

1
2
3
4
5
6
$ tar -zxf git-1.7.2.2.tar.gz 
$ cd git-1.7.2.2
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install
//将git仓库克隆到本地,方便日后更新
$ git clone git://git.kernel.org/pub/scm/git/git.git

傻瓜安装

1
2
3
//区别同上
$ yum install git-core
$ apt-get install git

Mac与Windows

Mac可以通过MacPorts或者下载git图形化工具完成git安装。Windows可以通过安装msysgit使用git命令。

在使用git前,可以通过git config更改个人用户名及电子邮箱地址。这样每次的提交会出现你个人的标识。

0%