使用go实现简单的文章爬虫功能

需求:

  1. 使用go语言爬取CSDN上某位博主的所有博客

分析:

1、 对于一般的博客文章,主要采集文章标题、发表时间、文章标签、文章链接、文章内容

2、一般都会分页展示博客文章

如果是采用单线程的方式,其步骤是:依次获取每一页的文章列表,将获取的列表存入待爬取的队列里,效率低。

如果是采用并发的方式,其步骤是:并发获取每一页的文章列表,将这些列表存入待爬取的文章通道里,加一个goroutine不断的从通道里取文章基本信息并去爬文章内容。

对于简单的单线程,这里不贴代码了,主要着重并发获取。

第一步:根据每一页获取文章列表信息

  1. package main
  2.  
  3. import (
  4.    “github.com/PuerkitoBio/goquery”
  5.    “fmt”
  6. )
  7.  
  8.  
  9. //定向爬某个博客
  10. type Bloginfo struct {
  11.    HomeUrl string
  12.    BaseUrl string
  13.    PageUrl string
  14.  
  15. }
  16.  
  17. type BlogItem struct {
  18.    Title string
  19.    Link string
  20.    GmtPublish string
  21.    Tag string
  22.    Content string
  23. }
  24.  
  25.  
  26. func main() {
  27.  
  28.    blogInfo := Bloginfo{
  29.       “http://blog.csdn.net/testcs_dn”,
  30.       “http://blog.csdn.net”,
  31.       “http://blog.csdn.net/testcs_dn/article/list/%d”}
  32.  
  33.    d := get_page_list(blogInfo, 1)
  34.    fmt.Println(d)
  35.  
  36. }
  37.  
  38. //获取每一页的文章列表
  39. func get_page_list(info Bloginfo, page int) []BlogItem{
  40.    rst := make([]BlogItem,0);
  41.    doc,:= goquery.NewDocument(fmt.Sprintf(info.PageUrl, page))
  42.    if e!= nil {
  43.       panic(e)
  44.       return rst
  45.    }
  46.    doc.Find(“.article_item”).Each(func(int, e *goquery.Selection){
  47.       item := BlogItem{}
  48.       item.Title = e.Find(“.link_title”).Text()
  49.       item.Link,= e.Find(“.link_title a”).Attr(“href”)
  50.       item.Link = info.BaseUrl + item.Link
  51.       item.GmtPublish = e.Find(“.link_postdate”).Text()
  52.       rst = append(rst, item)
  53.    });
  54.    return rst
  55. }

亲测有效

  1. [{
  2.         [置顶]
  3.         作为一个程序员我为什么要写博客?            
  4.          /testcs_dn/article/details/51231922 20160424 14:08 } {
  5.         [置顶]
  6.         有学历的程序员永远不懂没学历的痛,就像白天不懂夜的黑            
  7.          /testcs_dn/article/details/51170327 20160417 21:06 } {
  8.         [置顶]
  9.         【读评】为什么你有10年经验,但成不了专家?            
  10.          /testcs_dn/article/details/51158562 20160416 14:17 } {
  11.         [置顶]
  12.         After 500:写500篇博客其实和写一篇是一样的            
  13.          /testcs_dn/article/details/50791702 20160304 09:07 } {
  14.         [置顶]
  15.         C#软件开发实例.私人订制自己的屏幕截图工具(一)功能概览            
  16.          /testcs_dn/article/details/23169549 20140408 11:50 } {
  17.         [置顶]
  18.         MySQL学习(二)图形界面管理工具Navicat for MySQL安装和使用            
  19.          /testcs_dn/article/details/21122035 20140312 23:17 } {
  20.         [置顶]
  21.         ThinkPHP学习(一) WindowsNginx+PHP5+ThinkPHP_3.2.1的安装与配置            
  22.          /testcs_dn/article/details/21036345 20140311 21:20 } {
  23.         TFS部署:create_block allocate space error. ret: 1, error: 28, error desc: No space left on device            
  24.          /testcs_dn/article/details/51955536 20160719 15:06 } {
  25.         TFS部署:ERROR create_fs_dir (blockfile_manager.cpp:1191) make extend dir error. ret: 1, error: 17            
  26.          /testcs_dn/article/details/51954873 20160719 13:14 } {
  27.         2016年中总结、半年总结            
  28.          /testcs_dn/article/details/51841165 20160717 22:12 } {
  29.         TFS安装:base_packet.cpp:246: 错误:从类型‘const char*’到类型‘pthread_t’的转换无效            
  30.          /testcs_dn/article/details/51911785 20160714 18:37 } {
  31.         CentOS 6.5 安装 Redis 执行 make #error “Newer version of jemalloc required”            
  32.          /testcs_dn/article/details/51867879 20160709 18:27 } {
  33.         service mysqld start MySQL Daemon failed to start.            
  34.          /testcs_dn/article/details/51811900 20160704 10:26 } {
  35.         我如何添加一个空目录到Git仓库?            
  36.          /testcs_dn/article/details/51811974 20160704 10:24 } {
  37.         IntelliJ IDEA 简单的项目配置            
  38.          /testcs_dn/article/details/51815950 20160703 17:18 } {
  39.         IntelliJ IDEA 的智能编码功能            
  40.          /testcs_dn/article/details/51815933 20160703 17:12 } {
  41.         IntelliJ IDEA 快速入门指南            
  42.          /testcs_dn/article/details/51759461 20160703 17:05 } {
  43.         熟悉 IntelliJ IDEA 的主界面            
  44.          /testcs_dn/article/details/51814897 20160703 13:35 } {
  45.         centos 删除文件夹 permission denied, xxx is not in the sudoers file.            
  46.          /testcs_dn/article/details/51814585 20160703 11:56 } {
  47.         IntelliJ IDEA 运行你的第一个Java应用程序            
  48.          /testcs_dn/article/details/51793511 20160630 22:03 } {
  49.         js跨域交互(jQuery+php)之jsonp使用心得            
  50.          /testcs_dn/article/details/51785002 20160630 13:14 } {
  51.         Linux 下安装IntelliJ IDEA Community Edition            
  52.          /testcs_dn/article/details/51776058 20160628 18:45 } {
  53.         Mac OS 下安装IntelliJ IDEA Community Edition            
  54.          /testcs_dn/article/details/51771422 20160627 21:48 } {
  55.         IntelliJ IDEA的安装和启动            
  56.          /testcs_dn/article/details/51755616 20160624 21:48 } {
  57.         WindowsXP下安装IntelliJ IDEA Ultimate Edition            
  58.          /testcs_dn/article/details/51754979 20160624 18:32 } {
  59.         IntelliJ IDEA的安装环境要求            
  60.          /testcs_dn/article/details/51741298 20160623 15:11 } {
  61.         Windows7下安装IntelliJ IDEA Community Edition 2016.1.3(64)            
  62.          /testcs_dn/article/details/51742613 20160623 14:57 }]

第二步、并发获取

  1. package main
  2.  
  3. import (
  4.    “github.com/PuerkitoBio/goquery”
  5.    “fmt”
  6.    “sync”
  7. )
  8.  
  9.  
  10. //定向爬某个博客
  11. type Bloginfo struct {
  12.    HomeUrl string
  13.    BaseUrl string
  14.    PageUrl string
  15.  
  16. }
  17.  
  18. type BlogItem struct {
  19.    Title string
  20.    Link string
  21.    GmtPublish string
  22.    Tag string
  23.    Content string
  24.  
  25. }
  26.  
  27.  
  28. func main() {
  29.  
  30.    blogInfo := Bloginfo{
  31.       “http://blog.csdn.net/testcs_dn”,
  32.       “http://blog.csdn.net”,
  33.       “http://blog.csdn.net/testcs_dn/article/list/%d”}
  34.  
  35.    pages := 20
  36.  
  37.    result := make([]BlogItem,0)
  38.  
  39.    articleChannel := make(chan []BlogItem,10)
  40.  
  41.  
  42.    wg := sync.WaitGroup{}
  43.    for i:=0;i<pages;i++ {
  44.       wg.Add(1)
  45.       go func(page int){
  46.          fmt.Println(“down list”)
  47.          articleChannel <- get_page_list(blogInfo, page)
  48.          wg.Done()
  49.       }(i)
  50.    }
  51.  
  52.    go func(){
  53.       wg.Wait()
  54.       fmt.Println(“closed”)
  55.       close(articleChannel)
  56.    }()
  57.  
  58.    for articleLists := range articleChannel {
  59.       //爬取内容
  60.       for _,item := range articleLists {
  61.          fmt.Println(“下载…” + item.Link)
  62.          result = append(result, get_article(item))
  63.       }
  64.    }
  65.    fmt.Println(result)
  66. }
  67.  
  68. //获取每一页的文章列表
  69. func get_page_list(info Bloginfo, page int) []BlogItem{
  70.    rst := make([]BlogItem,0);
  71.    doc,:= goquery.NewDocument(fmt.Sprintf(info.PageUrl, page))
  72.    if e!= nil {
  73.       panic(e)
  74.       return rst
  75.    }
  76.    doc.Find(“.article_item”).Each(func(int, e *goquery.Selection){
  77.       item := BlogItem{}
  78.       item.Title = e.Find(“.link_title”).Text()
  79.       item.Link,= e.Find(“.link_title a”).Attr(“href”)
  80.       item.Link = info.BaseUrl + item.Link
  81.       item.GmtPublish = e.Find(“.link_postdate”).Text()
  82.       rst = append(rst, item)
  83.    });
  84.    return rst
  85. }
  86.  
  87. //获取文章内容
  88. func get_article(item BlogItem) BlogItem {
  89.    doc, e := goquery.NewDocument(item.Link)
  90.  
  91.    if e!= nil {
  92.       panic(e)
  93.    }
  94.  
  95.    item.Content,= doc.Find(“#article_details”).Html()
  96.    item.Tag = doc.Find(“.link_categories”).Text()
  97.    return item
  98. }

Golang实现的一个并发爬虫框架

 

利用golan能简单编写并发程序和擅长网络编程的特性实现了一个并发爬虫框架。

一些特点

  • 轻量,易使用
  • 自定义解析库(方法),自定义数据处理方式
  • 组件独立模块化,易扩展,并发实现
  • 多规则,参数支持(爬行深度,http制定等)

基本结构

包含四个组件和一个控制中心,参考了Scarpy的架构,如图

01

Controller控制器

控制器负责调度整个爬虫的运行流程和协调各组件间的工作,包括组件的初始化,数据传递,停止等,使各组件的工作专注于自己的职责

Downloader下载器

下载器是爬虫与互联网交互的部分,把请求通过http与目标交互,获得响应后封装输送至分析器进行解析。这里会提供到对http请求的各种包装(Header, Cookie)接口。

Analyzer分析器

分析器对http响应进行解析,这里会让框架使用者自定义解析方法,提取所需要抓取的数据和下一步请求的链接,把这些数据送至处理器助理,还有对http响应的一些过滤等。

Porcessor处理器

处理器对分析器输送出来的数据和链接进行处理。数据会由使用者提供的方法来进行持久化储存,可选择文件,数据库等多种形式,这里把网络处理与数据储存细节分离开。而链接会进行筛选(去重, host控制),封装为请求以后发送至下载器到下一个流程。处理器还对通道传输有一定的缓存作用。

Monitor监视器

监视器对爬虫整个运行状态进行监控,包括一些实时的抓取数据,组件间数据传输状态,根据情况再优化爬取流程。

四个组件中除了监视器,其他都能由控制器调度拥有多个实例同时工作,得益于golang的goroutine和channel能够简洁的实现。各组件的职责专一降低了框架的耦合性。数据通过channel传输,

部分实现

分析器接口

type Parser func(httpRes *http.Response) ([]string, []basic.Item)

type Analyzer struct {
    linklist []string
    itemlist []basic.Item
}

type GenAnalyzer interface {
    Analyze(httpRes *http.Response, parser Parser) ([]string, []basic.Item)
}

分析器的接口,数据结构,和自定义的解析方法,Parser解析函数由使用者定义并传入分析器,接受一个response, 返回linklist, itemlist,这里使其与框架分离,是使用者按照自己的习惯和爱好选择解析方法(有类似于jquery解析方法的github.com/PuerkitoBio/goquery包,xpath语言的launchpad.net/xmlpath包等等)。对item的储存方法的接口一样的处理

爬虫停止流程

由于涉及多个channel,关闭通道的顺序尤为重要,因为本身也是靠循环通道获取值来流通整个爬虫流程的数据交流。在非意外情况下,当爬行深度大于指定值是发送停止信号,这是在Processor中出现,这时候应该关闭请求通道,按照channel的特点,去除通道的所有值时才会退出循环,退出循环时关闭响应通道。同理在响应通道数据接收完毕后跳出循环关闭link和item通道。

ReqChannel -> ResChannel -> LinkChannel && ItemChannel

工作池

没有去实现类似线程池的goroutine池,一个工作池已经能够完成任务。工作池接收一个work函数,以num个goroutine去运行,在work通过channel来控制阻塞和流通。这里也可以很容易扩展work为接口。

func (self *WorkPool) Pool(num int, work func()) {
    for w := 0; w < num; w++ {
        go work()
    }
}

全局配置

一些可以默认又提供自定义的全局参数配置,以下是一部分:

type config struct {
    Name             string
    StartUrl         string
    RequestMethod    string
    HttpHeader       map[string]string
    DownloaderNumber int
    AnalyzerNumber   int
    ProcessorNumber  int
    ReqChanLength    int
    ResChanLength    int
    LinkChanLength   int
    ItemChanLength   int
}

爬虫启动后会执行初始化,如果没有在main函数里配置相关选项将会使用一个默认的参数。使用者可以根据监视器反映的情况来调整这些参数

快速使用

下面是一个简单的例子

func main() {
    //创建一个控制器,这里有4个必须给予的参数:
    //爬取的初始url,爬取深度,解析函数,储存函数
    controller := controller.NewController("http://www.ccse.uestc.edu.cn/", 1, Parser, Store)
    //启动爬虫
    controller.Go()
}

func Parser(httpRes *http.Response) ([]string, []basic.Item) {
    //两个需要返回的列表
    linklist := make([]string, 0) //下一步需要请求的链接
    itemlist := make([]basic.Item, 0)//保存的数据类型为 map[string]interface{}

    //自定义部分
    //抓取所有链接
    doc, _ := goquery.NewDocumentFromResponse(httpRes)
    doc.Find("a").Each(func(i int, s *goquery.Selection) {
        link, exits := s.Attr("href")
        if exits {
            link = basic.CheckLink(link)
            if link != "" {
                linklist = append(linklist, link)
            }
        }
    })
    //保存每个页面的标题
    title := strings.TrimSpace(doc.Find("head title").Text())
    if title != "" {
        item := make(map[string]interface{})
        item["标题"] = title
        itemlist = append(itemlist, item)
    }

    return linklist, itemlist
}

//储存函数定义
func Store(item basic.Item) {
    //这里只打印抓取的数据
    fmt.Println(item)
}

实现了两个函数用于页面解析和数据处理(函数名任意),然后在main函数里创建一个控制器,再执行Go()函数就能开启爬虫,

还需要做的事

  • 程序不少细节待完善,包括错误处理,日志记录,监控细节,本身的Bug等等
  • 对可自定义的部分进一步完善接口
  • 考虑实现一个更通用,简洁易用的解析方法(按给定的方法去配置,类似Config),真正的解析交给分析器做
  • 在平时的使用中来完善,不断更新迭代

附上源码: Github

~~

  • 越来越喜欢Go的简洁易用,和Python配合基本能满足我的需求了
  • 再次感受到自己更乐意以解决问题的目的编程而不是创造东西的目的,只有面对问题才会激起我的欲望~~
  • Enjoy Coding

如何做一个小型公司的技术总监

资深程序员是团队中最强大的生产力,但往往被不合理的工作安排浪费掉。因此作为一个团队的技术的“头”,必须要有明确清晰的认识,把主要的事务性工作剥离出来。并且放弃大量的管理“权力”,以提高团队开发质量和效率为最主要的目标去安排自己的工作。一般来说技术总监其实会被要求做事实上是2个职位的工作:主程、项目经理(技术化)

因此必须明确此两个职位的工作任务分割。然后把项目经理的工作,安排给另外一个人做,当然其职称可能同样也得叫“技术总监”或“主程”,总之听起来越牛X越好。

而真正的主程(技术总监)则应该投身于尽量多的技术工作中。而最重要的工作则是开发——生产代码和文档。

主程的工作:

一、开发


从来没有一个资深的外科医生会放下手术刀,而转到手术室外面指手画脚。一个资深的程序员也不应该离开代码和文档的编写,而只是做做架构图。作为对一个复杂系统的负责人,必须亲手领导和参与建造,才能有足够的能力去负担起这个责任。因此需要至少使用60%的时间来参与开发的工作,并且建议从一开始上班就开始,虽然早上的效率很低,但是跟任何艰巨工作都一样:万事开头难。在你好不容易等待电脑慢吞吞的打开了所有的IDE、需求文档、参考资料、工作计划这堆要命的东西之后,你就迈出了最重要的一步,你会发现你不在需要在网上看微博和聊QQ来提振开始工作的激情,而会被某一个优化代码的灵感而激励,或者被一个复杂而有趣的问题所吸引,从而更快的能投入到开发中。坚持打开电脑做的第一件事是打开IDE软件,是这一切最重要的一步。

开发的工作内容包括有:

1.     提出非功能性需求

一般来说功能需求总是让开发人员焦头烂额的主要原因。但是实际上很多项目死在发布之后,却是因为性能、产品质量、扩展性、二次开发效率等非功能性需求没认真去解决而导致的。主程作为经验最丰富的成员,必须要利用自己曾经的经验和教训(在这里教训往往比经验重要),提出那些自己折腾自己的“非功能性需求”,来保障整个项目在发布后不会轰然倒塌。这是个吃力不讨好的工作,因为老板和客户往往只会抱怨技术人员在玩弄把戏,骗取更多的资源或者杞人忧天。如何说服这些家伙也许不是主程的工作,但是主程必须要以高度的责任心把问题放到台面上来。沟通的工作也许让项目经理去做会更好,他们有一整套如何威逼利诱老板和客户的戏法。

2.     设计和修正软件架构

软件架构设计至关重要,而且工作繁重。不画图纸就敢开工的技术人员要么是天才要么是笨蛋。对于团队来说,架构在分工合作、避免风险、提高质量等多个方面有无可替代的作用。架构要避免成为空洞的文档,最重要的一步是有人来掌控和实施。而主程主持设计和修正的架构,并且亲手实施,让团队中的腹诽之徒完全无法避开,否则代码将无法运行!所谓设计和修正架构,并不意味所有的文档应该一个人写,而是指这个架构的每个环节,都是经过主程决策同意的。当然最好这些文档能尽量由他撰写,对于“菜鸟”团队来说,输出这种文档本身就意味着“权势”,有助于主程建立个人威信——这种看起来有点肮脏的“政治”东西,在避免团队内无止境的扯皮,以及稳定那些随时准备跳槽的成员来说,都是相当实用的。

3.     难点代码(关键需求)的开发

主程必须写代码,写那些大家都认为风险大的代码。有的系统对于性能要求很高,他就必须去完成容易出性能问题的部分,比如IO操作或者设计数据库索引。有些系统的需求非常飘忽,他就要去想办法完成框架代码或者脚本引擎,以便众多小弟可以跟着产品人员疲于奔命。这种工作内容会让主程不必完全的读过所有代码,而能牢牢的“掌握”代码,以免团队成员甩耙子的时候能充当备胎。因为融入团队的代码开发,也是一个让架构设计从日常工作中真正控制系统的工作。而且主程代码通常会被别人接触,能直接教育其他团队成员,同时也能建立——威信。

4.     救火和杀虫

这个工作其实和代码开发是一致的,如果没有平日的开发,通常紧急问题的解决也是比较难处理的。但是这个也有一个调试技巧的要求,比如要求会使用各种诊断工具。这些工具一般的开发人员可能会比较少使用。找问题的过程本身也可以提高团队其他人的技术水平。

二、培训


培训的工作应该占用30%左右的工作时间。培训是稳定团队人员最重要的手段。也是提高团队开发效率最有效的手段。工具、过程、制度、奖惩,这些都代替不了程序员一行行的去写代码,最直接的方法是让他们做的更快更好,这些需要经验和知识的积累。

1.     代码审查

关于代码审查,有太多的论述。但是代码审查还是一种“强迫”推行某种风格或者技巧的手段,这是最真实的“控制”系统的手段。也是推广知识和经验最直接的手段。一个人写的代码通常应对的问题不会特别“广泛”,因此只要审查其中一部分代码,就能给大部分别的代码带来好处。

2.     技术方案评审

什么事情应该写一个技术方案,然后进行评审,这是一个关键的问题。一般认为开发时间在2周以上的单项工作应该先做个方案。往往技术方案是系统架构的完善和补充,或者是挑战。所以主程的参与是非常必要的。但是要注意不需要去做的太琐碎,而是要提炼出“关键”的需求和“关键”的解决方案进行评审,而这些“关键”往往不是功能,而是质量上的需求,如这个系统的扩展性,是否能方便后续开发等等。也有可能在这些会议上会发生争吵,但是决策人是主程的地位是不容动摇的。君子和而不同,每个程序员都可以拥有自己的看法,但是代码必须能按方案运行起来,主程必须经常申明这点。

3.     学习与讲座

如果团队碰到问题,没有新的方法和技术去解决,是不会提高开发效率的。就好像你用牛来耕地,不管用什么管理方法,都不会赶上机械化的速度。而主程承担着不断突破自己的技术上限,介绍和推动团队使用更新的技术来解决问题的责任。抱残守缺,思想僵化,最后会被团队成员所抛弃,而且也会让团队的效能落后于业界,最后直接影响产品的生死。每年学一门新语言,这个说法可能有点激进,但是这也是作为程序员应该有的激情。

三、管理


管理等于权势?管理等于沟通?管理等于文山会海?多年专业训练出来的技术人员如何去做管理?

管理的目标是提高绩效,如果和这个目标无关,而只是和“管理者”这个头衔有关的事情,最好丢给别人去做,包括那个头衔。管理主要手段是创新:想出新的方法去解决问题,而不是繁杂的事务性工作!——一个专业秘书能比主程做的好一百倍。技术工作的创新,最主要还是在技术工作里面,而不是跳出来说:做这个,做那个。

管理的事情如果超过10%的工作时间,等于说你更像一个项目经理而非主程。

1.     绩效评定

以专业的意见来衡量别人的工作,这个负担是无人能够承担的。这个工作往往是利益分配的一种手段。类似奖惩手段。这种管理方法已经不是新事物了。但是实际上技术人员对于绩效往往持一定保留和暧昧的态度,因为这种事情难以很清晰的界定出来。需要判断而非量度,才是绩效的真正手段。如果一定要打分,一共两项足够了:进度、质量,5分制即可。更重要的事情是,告诉每个人主程的看法,告诉别人,怎样做才是更好。或者告诉团队,怎样做才更有利于我们成功(发财、上市、赢得老板和客户……)——把目标清晰告诉团队,发挥他们的主动性,是绩效评定最重要的目标。

2.     需求评定

最让技术人员头疼的可能就是和客户谈判。这个事情实际上不应该让技术人员来伤心,有项目经理就可以了。而需求评定更多的是可行性的讨论。主程如果参加每个需求评定,他要三头六臂也搞不定,正确的做法应该是具体开发的团队人员参加,而主程在开会前给与自己的意见,或者会后听取参与者的总结。——这是了解别人做什么事的一个重要手段,但无需陷入太深,因为还有代码评审和项目经理的帮忙。

3.     跨部门沟通

实在没必要参加,能躲就躲,这是扯皮的天堂。让项目经理去吧,他们的专业技巧能让这些事情更加有效。只要回来后让项目经理告诉你发生了什么事情就可以了。

4.     进度审核和任务分派

又是一个很有“权势”的工作,实际上团队成员的情况大家都知道,决定谁应该做什么事情并非需要很多时间去想的事情。所以大可以把方向性的意见告诉项目经理,让他去做。很多优秀的开发者玩EXCELPROJECT之类的水平还不如只有一年工作经验的秘书,别折腾自己了。

5.     面试

如果真想帮忙,准备一份有区分度的笔试题目吧。不靠谱的人太多,老板可不是花钱请你和他们聊天的。让项目经理去聊,不用担心他们技术不强,再不够,也会比大多数面试者要牛X。他们搞不定的人,就是应该雇佣的家伙。毕业生招聘怎么办?只要看看他们课外活动是不是有搞些专业的事情就可以了,上进心比别的东西都重要,HR会比主程看的更准,相信我。

6.     各种会议

饭无好饭,会无好会,超过6个人的会议应该坚决抵制。如果你有一个程序等着你去写,你一定无比痛恨这些会议,顺应你的内心吧!上帝保佑你。

最后说说项目经理的工作:

项目经理就像下水道的清洁工,所有那些主程不愿意去做的事情,他们都弯下腰去认真的把玩,实在是太伟大了。既然如此,为何不让他们拥有更好一点的头衔呢?如果没有他们去处理这些工作,任何一个主程都会被逼疯掉,或者他们自己变成了项目经理,让团队损失了最强力的一台代码发动机。

一、进度

1.     指定工作计划

2.     进度检查和告警

3.     工作总结和统计

二、资源

1.     整合提供各种资源,如找DBA,IT,运维人员,硬件,SVN权限,测试环境,福利,周末的活动……

2.     面试:人员是最重要的资源,不是吗?

3.     资源谈判:往往是和老板谈判,让别人明白现在的真实情况。又一个吃力不讨好的差事,但是总需要人做。

三、沟通

1.     需求评审:和需求方讨价还价,项目经理真是命苦啊……

2.     组织会议或者用其他方式通知信息给所有人:小喇叭、大喇叭、全服广播、世界频道……

对于一个小型公司,职权,头衔,收益,往往会更加敏感。但是这些都不是让项目失败的理由。一颗叫程序员的种子说:长大了我就是叫管理者的树。这个错误的观念只会让这个种子永远无法发芽。软件开发是类似外科医生的行业,而不是血汗工厂,所以不需要手持皮鞭的经理,而需要仁心仁术的神医。

使用Hyper-V/Vmware安装CentOS 6.3

工欲善其事,必先利其器。

1、首先我们得有一个CentOS 的光盘镜像。

这里为什么选择CentOS 版本呢?因为现在的主流云主机系统,以CentOS 6系列版本居多,是目前为止使用最多、最稳定的版本,并且它有一个后台很硬的老爸—–Red Hat。

我手上只有现成的CentOS 6.3光盘镜像,所以本次使用CentOS 6.3来安装体验。当然你也可以选择6.4、或6.5,甚至最新的版7.0。安装的原理都是差不多的。

2、CentOS 光盘镜像有了,你得还需要一台电脑。

使用Hyper-V的话,你的电脑必须支持硬件虚拟化,并且需要在Bios中开启硬件虚拟化才能使用Hyper-V,你的主机操作系统也必须在Windows 2008 x64位以上才能安装Hyper-V功能。目前的新硬件一般都没问题。

而Vmware的要求就不是很高了,一般都能很好的支持。我下面演示的是Vmware WorkStation 8.0版本,如果你用更新版本也可以。

安装Hyper-V

Windows server 2008/2012 系统中安装Hyper-V功能很简单,打开“服务器管理器”,添加“角色”,选择“Hyper-V”,等待安装结束即可。

在windwos 8/8.1中安装Hyper-V功能也很简单,只需要在“启用或关闭Windows 功能”中选择“Hyper-V”安装即可。如果你的CPU、主板支持硬件虚拟化功能的话,其中的“Hyper-V 平台”选项就会变为可选项,如果不支持硬件虚拟化,则只能安装“Hyper-V 管理工具”。如下图:

在Hyper-V中创建一个CentOS 6.3虚拟机

打开Hyper-V管理器,新建虚拟机,会弹出新建虚拟机向导,点“下一步”,

为虚拟机起个名字,并且指定虚拟机存放目录,

虎拟机代数选择第一代即可,如果安装windows 2012/8,则可以选择第二代,提供更多高级功能,

CentOS最小需要628M内存才能启用图形安装模式,所以我这里指定了1024M内存,

网络连接先默认,在后面的设置里详细设置,

创建虚拟硬盘,不需要做更改,默认即可。

我先选择“以后安装系统”,后面的设置里面再指定光盘镜像,如果这里指定了光盘镜像,则虚拟机创建完成后会自动启动虚拟机开始安装系统。

点击“完成”,完成虚拟机的创建 。

返回Hyper-V界面,可以看到我们新建的虚拟机CentOS 6.3.

选中虚拟机“CentOS 6.3”,点击右侧的“设置”,定位到”DVD驱动器”,右侧选择映像文件。

定位到“网络适配器”,为我们的虚拟机添加一张网卡,选择右侧“虚拟交换机”下拉列表发现没有可以使用的物理网卡,只有“未连接”和”Windows Phone Emulator Internal Switch”二项,“未连接”就是不添加网卡,那我们的虚拟机将不能连网,第二项“Windows Phone Emulator Internal Switch”是给Windows Phone手机模拟器用的,这一项一般没有的,因为我的电脑安装了微软的开发工具VS2013和Windows Phone SDK 8.1,所以多了这一项。

没有网卡肯定不行,我们必须想办法添加一个。接着往下看,

打开服务器节点下的“虚拟交换机管理器”,

选中“外部”,点击“创建虚拟交换机”

为我们的虚拟交换机起一个名称,方便记忆,并选择一张一地网卡,点击“应用”,会提示会断开网络,选择“是”,这样我们的虚拟交换机就建好了。

再返回到虚拟机的设置界面,发现我们刚刚添加的虚拟交换机已经在下拉列表里了,选择它,

,如果你还要设置其它如内存、CPU核数等都可以根据自己主机来调整。我就不演示了。

好了,基本的设置已经可以了。让我们启动虚拟机开始安装系统吧。

注意:在我们想关闭Hyper-V管理器的时候可能会弹出“关闭hyperv管理器前你必须关闭所有会话框”的对话框,

这时你只需要切换下输入法就可以正常关闭了。

在Vmware Workstation 8 中创建CentOS 6.3虚拟机

Vmware的版本很多,功能很强大,现在有很多云技术就是基于Vmware公司的云技术搭建的。当然我们今天使用的是Vmware Workstation,是一款桌面虚拟计算机软件,为我们的学习、开发、测试、部署提供解决方案。你只需要一台主机就可以安装多个操作系统来供我们使用,有次节省硬件的采购成本。

Vmware的安装就不说了,标准的Windows软件安装方式,只要下一步下一少就可以了。

打开Vmware Workstation,新建虚拟机。

推荐使用“标准”设置,比较简单一些,

第一项是指从主机的光驱安装系统,适合哪些已经将镜像刻录成光盘的用户,第二项是指定镜像位置,并立即开始安装系统,并且安装过程不需要我们干预,这种方式不适合我们学习,所以我们选择“我以后再安装操作系统 ”,

客户操作系统选择”Linux”,版本选择‘CentOS’,这个根据你的Vmare版本选择,每个版本都有小区别,

给虚拟机起一个名字,并指定虚拟机的保存位置,

虚拟机的磁盘空间,默认“20G”就可以了,这里的“20G”不是你指定了20G就直接从你的硬盘划走20G,会随着你的使用慢慢占满20G,最大只能占到20G,

完成,虚拟机就建好了。

右击我们建的虚拟机,“设置”,

这里可以设置修改内存大小、CPU核数,还可以添加新硬件。给CD/DVD指定ISO镜像文件路径,

同样我这里给虚拟机指定了1G内存,最小是512M,只能启用字符安装界面,如果要启用图形安装界面,则最小需要628M内存。

设置虚拟机的网络,建议选择“桥接”,

桥接:使用和你的物理主机一样的网络,就是和你的物理主机同网段。这种使用最方便,设置最简单。

NAT:使用Vmnet8 这块虚拟网卡连接网络,它可以共享主机的外网,可以和主机通信但不能和主机同网段里的其它机器通器。

Host-only:使用Vmnet1这块虚拟网卡和主机通信,它只能和主机通信。

至些,虚拟机都创建好了,可以启动安装系统了。

安装CentOS 6.3

虚拟机启动的第一界面,如下:

菜单说明:

Install or upgrade an existing system—安装或升级一个现有的系统

Install system with basic video driver—安装系统并且使用基本的图形驱动

rescue installed system—进入系统修复模式

Boot from local drive—-从硬盘启动

Memory test—内存测试

我们在这里选择使用“Install or upgrade an existing system”这项来安装系统。

选择“Install or upgrade an existing system”后,会跳出如下界面,意思是已经发现了安装光盘,问你是否需要检测光盘镜像的完整性,那我们是下载的光盘,就不检测了,因为检测会比较耗时,所以选择“Skip”跳过检测,

进入以下界面,没有过多的选项,直接“下一步”,

进入安装语言选择界面,我们选择简体中文,这里的中文,指的是我们安装界面的语言,不是系统的语言,“下一步”,

进入键盘选择界面,我们一般都选择“美国式英语”,“下一步”,

进入存储设备选项,选择“基本储存设备”,“下一步”,

弹出一个存储设备的警告框,说是在这个磁盘里没有发现分区信息,而我们要安装Linux系统必须要进行分区,分区的目的是写入文件系统,可以让Linux识别到我们的硬盘,所以我们这里选择“是,忽略所有数据(Y)”,

进入设置计算机名界面,不建议修改,因为Linux对主机名不敏感,所以这里默认即可,“下一步”,

进入时区选择界面,选择“亚洲/上海”,同时勾选“系统时钟使用UTC时间”,“下一步”,

进入设置管理员密码界面,Linux系统默认的管理员是root,它拥有服务器的所有权限,所以我们应该设置一个高强度的密码。

关于密码强度需要做到如下几点:

复杂性:长度最少需要8位以上,并且需要包含大小写字母、数字、特殊符号中的任意3种字符,不要直接使用英文单词,不要直接和用户的个人信息相关,如身份证号、手机号等。

易记性:这个看起来和上面冲突了,又要复杂又要易记,是不是有点难。但是我们可以经过变形得到密码的复杂性。举个例子:Flzx@3QC,你能猜出这个密码的含义吗?这个密码的意思来自一句诗词:飞流直下三千尺。这样就达到了易记性,同时又符合复杂性。

时效性:建议3个月修改一次密码

最近几年,互联网爆出很多密码泄漏时件,为了安全起见,最好经常修改密码。

我输入的是密码123456,会弹出下面这个界面,说密码太过简单,因为是在虚拟机中做演示而用,我可以这样设置,直接选择”无论如何都使用“。在生产服务器上密码必须符合上面密码原则

进入安装类型选择界面,我们是为了学习,第一次安装Linux,所以选择”创建自定义布局“,这样系统到底为我样做了那些工作,我们可以有一个更直观的认识,”下一步“,

进入创建分区界面,这里我们可以看到有一块20G的磁盘,还没有经过分区。

关于Linux分区:Linux中有二个分区必须要有,一个是根分区,另一个是交换分区,而我们实际在分区中可能分不止这二个,会加上boot分区,home分区,usr分区等

Linux分区的顺序:先分boot分区,交换分区swap,home分区,usr分区,最后分根分区。

点击”创建“时会弹出一个存储类型界面,我们这里只有一块硬盘,所以选择”标准分区“,再次点击”创建“,

进入真正创建各个分区的界面,先分boot分区,选择挂载点”/boot“,文件系统类型选择”ext4“,大小”200MB“足够了,

创建交换分区swap,直接选择文件系统类型为”swap“,交换分区的大小一般为内存的2位,但是最大不超过2G,这个分区不是供我们使用,是由操作系统内核直接调用,

创建一个home分区,挂载点选择”/home”,文件系统类型也为”ext4“,用户上传的一些文件一般放在home分区下,所以可以相应的大一点,我这里设了5000MB,

最后分根区,挂载点选择”/“,文件系统类型”ext4“,大小选择”使用全部可用空间“

分好区,点击”下一步“,弹出一个警告框,提示将格式化磁盘分区,选择”格式化“,

再一次提示格式化分将文件系统写入磁盘,原先磁盘中的数据将丢失,选择”将修改写入磁盘“,

安装引导程序,默认即可,

进入软件包安装界面,

菜单说明:

Desktop:桌面

Minimal Desktop:最小桌面

Minimal:最小化安装

Basic Server:基本的服务

Database Server:数据库服务器

Web Server:web服务器

Virtual Host:虚拟主机

Software Development Workstation:软件开发工作站

这里说的一个意思就是你安装这台服务器用来干什么,不同的选项会安装不同的软件。而作为一台生产服务器,我们建议最小化安装,需要用到哪些软件可以自定义来安装,安装的东西越少,相对性能也越好,安全性也越高。

也可以选择”现在自定义“选项进入软件选择界面,需要哪些软件,只要在这里勾选即可。

而我们这里安装的目的是为了更好的学习Linux,所以我最终选择的是”Basic Server“选项,一些其它软件我们以后自己手动安装,所以选择”以后自定义“,

”下一步“,就正式开始安装了,根据机器的性能不同和安装软件包的多少,大概需要半个小时左右的时间,直至安装结束。

点击”重新引导“,重启虚拟机。

重新启动后,输入管理员用户root,密码123456就可以登录了。登录后会定位到root的家目录。这个界面看起是不是高大上,

输入命令:ls,会列出当前目录下的文件,

anaconda-ks.cfg:以Kickstart配置文件的格式记录安装过程中设置的选项信息,用于多台机子的批量安装。

install.log:存储了安装在系统中的软件包及版信息

install.log.syslog:存储了安装过程中留下的事件记录

本章CentOS 6.3系统的安装到此结束!

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。

http://blog.postcha.com/read/14 使用Hyper-V/Vmware安装CentOS 6.3

像花椒,映客,来疯这种直播app,技术实现难度在哪?需要什么样技术人才,还有就是服务器带宽要求及成本?

作者:宋少东
链接:https://www.zhihu.com/question/41868659/answer/95092032
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

技术层面:
技术相对都比较成熟,设备也都支持硬编码。IOS还提供现成的 Video ToolBox框架,可以对摄像头和流媒体数据结构进行处理,但Video ToolBox框架只兼容8.0以上版本,8.0以下就需要用x264的库软编了。Andriod 可以考虑用 ffmpeg 软编。

github上有现成的开源实现,推流、美颜、水印、弹幕、点赞动画、滤镜、播放都有,技术其实不是很难,而且现在很多云厂商都提供SDK,招几个懂行经验丰富的就可以,并不必须是流媒体人才。

链接:
如何搭建一个完整的视频直播系统? – 网络主播

映客、Periscope、花椒等直播APP点赞动画:
GitHub – singer1026/DMHeartFlyAnimation: 直播点赞动画

Android开源弹幕:
GitHub – Bilibili/DanmakuFlameMaster: Android开源弹幕引擎·烈焰弹幕使 ~ JNI source:Bilibili/NativeBitmapFactory

IOS开源弹幕:
GitHub – panghaijiao/HJDanmakuDemo: A high performance danmaku engine for iOS

七牛云移动端推流开源SDK:
GitHub – pili-engineering/PLCameraStreamingKit: Pili RTMP Streaming SDK for iOS, H.264 and AAC hardware encoding supported. Camera and Microphone as input source.

基于Android手机推流:
GitHub – begeekmyfriend/yasea: RTMP streaming client in pure Java for Android for those who hate JNI.

基于IOS的图像处理:
GitHub – BradLarson/GPUImage: An open source iOS framework for GPU-based image and video processing

完整的基于IOS手机直播:
GitHub – songsmith/LiveVideoCoreSDK

IOS-推流端的 RTMP库:
GitHub – songsmith/ios-librtmp: For PushSDK

OBS-PC端主播推流工具,斗鱼等游戏直播都在用
GitHub – jp9000/obs-studio: OBS

RTMP直播可以用nginx-rtmp
GitHub – arut/nginx-rtmp-module: NGINX-based Media Streaming Server

开源播放器也很多,ffplay、jwplayer、ijkplayer等等,我就不一一给你发了,哈哈
github.com/Bilibili/ijk

其实最难的难点是提高首播时间、服务质量即Qos,如何在丢包率20%的情况下还能保障稳定、流畅的直播体验,需要考虑以下方案:

1. 为加快首播时间,收流服务器主动推送 GOP 至边缘节点,边缘节点缓存 GOP,播放端则可以快速加载,减少回源延迟

2. gop丢帧,为解决延时,为什么会有延时,网络抖动、网络拥塞导致的数据发送不出去,丢完之后所有的时间戳都要修改,切记,要不客户端就会卡一个 gop的时间,是由于 dts 和 pts 的原因,或者播放器修正 dts 和 pts 也行(推流端丢gop更复杂,丢 p 帧之前的 p 帧会花屏)

3. 纯音频丢帧,要解决音视频不同步的问题,要让视频的 delta增量到你丢掉音频的delta之后,再发音频,要不就会音视频不同步

4. 源站主备切换和断线重连

5. 根据TCP拥塞窗口做智能调度,当拥塞窗口过小说明丢包率过高,需要切换节点和故障排查

6. 增加上行、下行带宽探测接口,当带宽不满足时降低视频质量,即降低码率

7. 定时获取最优的推流、拉流链路IP,尽可能保证提供最好的服务

8. 监控必须要,监控各个节点的Qos状态,来做整个平台的资源配置优化和调度

9. 如果你家产品从推流端、CDN、播放器都是自家的,保障 Qos 优势非常大

10. 当直播量非常大时,要加入集群管理和调度,保障 Qos

11. 播放端通过增加延时来减少网络抖动,通过快播来减少延时

12. 引入TCP/IP 优化,针对直播业务做下行TCP/IP优化,在某一个节点丢包率50%情况下,800kb码率可流畅播放

2016-05-27 补充:
我先后调研了七牛云、金山云、乐视云、腾讯云、百度云、斗鱼直播伴侣 推流端,功能几乎都是一样的,没啥亮点,不同的是整个直播平台服务差异和接入的简易性。后端现在 RTMP/HTTP-FLV 清一色,APP挂个源站直接接入云厂商或CDN就OK。直播要做好真心不易,楼主也做的不是很好,只是在不断的摸索、改进和优化,关于如何选择接入的云厂商或CDN,哪个稳定选哪个!

带宽成本:

粗略计算,斗鱼 TV 的在线人数超过 1000 万 +,战旗 TV 在在线人数约 500 万 +,龙珠在线人数约 400 万 +,虎牙在线人数约 100 万 +(统计于 2016 年 2 月 18 日,以各大节目在线人数人工相加得出,并未统计全部节目)。

直播平台的带宽成本费用通常取带宽峰值月结,当月 100 万最高同时在线人数,对应消耗带宽 1.5T,月结费用 3000 万人民币,20块钱1M的样子,根据直播方需求不同,价格也不一样,质量越好的就越贵。
PS:此计算方式来源于网络

可以算出各家在带宽方面每年需要付出的成本,斗鱼 TV 为 3 亿人民币,战旗 TV 为 1.5 亿人民币,龙珠为 1.2 亿人民币,虎牙为 3000 万 + 人民币。
运营层面:
正是因为现在做这个的太多了,所以你要运营和推广,这个就比较烧钱了,反正我现在知道的一些做移动直播、游戏直播、秀场直播的A轮至少得上千万。

用户体验:
离不开CDN和云厂商,他们会帮你解决大部分的技术问题,不流畅、卡顿、花屏、带宽不够、攻击、用户体验不好等一系列问题,还给你提供免费技术支持,上面提到的那些技术方案就是云厂商和CDN来帮你解决的。

支付系统设计

支付账户和登录账号

账户体系设计首先要区分两个概念,支付账户和登录账号。 这是两个不同业务领域的概念。 支付账户 指用户在支付系统中用于交易的资金所有者权益的凭证。 登录账号 指用户在系统中的登录的凭证和个人信息。 一个用户可以有多个登录账户,一个登录账户可以有多个支付账户,比如零钱账户,储值卡账户等。 一般来说,支付账户不会在多个登录账户之间共用。如果没有特殊说明,下文中的账户,都默认指支付账户。

账户的设计需求

在支付系统中,账户的设置,主要是从如下几个方面来考虑:

  • 交易的需求,比如检查账户是否被锁定、余额是否足够、是否有效等。
  • 记账的需求,按照公司会计需求记录账户上的所有行为,包括支出、充值、转账等。
  • 对账的需求,包括和支付渠道、商户、个人的对账需求,核对交易和账户余额是否正确。
  • 风控的需求,如反洗钱、反欺诈等,都需要依赖于账户体系来提供核心数据。本文暂不分析这个内容,将在《支付风控》、《支付反洗钱》这两篇文章中详细分析
  • 信用的需求,对用户、资产、商户等主体进行信用评估时,也需要依赖账户体系来提供的核心数据。本文也暂不分析这内容,将在《信用与支付》一文中分析。

这五个需求,按照其设计的优先级,也是从支付、记账、对账、风控来进行。 支付系统根据其发展所处的阶段,逐步将新增需求纳入设计中。

交易与账户

账户设置,一般是从交易开始的。 交易的实现必须有账户的支持,账户是交易的基本构成元素。 从支付系统的角度,交易中涉及到的资金流是资金从一个账户流向另一个账户。 发起交易的一方,被称之为交易主体,他可以是个人,也可以是一个机构。 资金从该主体所拥有的账户中流出。 而接收交易的一方,被称为交易对手,他也可以是个人,或者机构。 和第三方支付或者金融机构的交易不同,电商系统中,交易还会涉及到渠道。 由于电商系统本身并无清结算的资质,所有资金从交易主体到交易对手的账户的流动,在大部分情况下,并没有经过电商系统,而是由电商系统调用支付渠道提供的接口,由它来完成真正的支付过程。 当然,渠道也不是活雷锋,在这过程中,渠道要收取费用。所以,在电商系统中,一次交易会涉及到三个账户: 交易主体账户、交易对手账户以及支付渠道账户。 如何在这三个账户中完成一次交易,我们将在后续的《交易和记账》一文中详细分析。

记账与账户

公司的会计需要对每一笔交易都要做详细的记录,即记账。 公司每天都产生大量的交易行为,为了便于管理和统计,一个简单的方法是对交易进行分类,比如食品、带宽、办公用品等等。 这个分类,按照公司的规模和业务复杂度,可以有一级,二级,三级或者更多级的结构,这被称之为会计科目。 记账时,除了交易明细,还需要在每个级别上对交易额进行汇总。 一般来说,一级科目上汇总称为总帐科目,而详细记录称为明细科目。 在电商系统中,由于涉及到的参与方较多,记账也相对复杂,但基本方法也是类似的。 电商的参与者可以分为商户、买家和渠道,对这三类参与者,都需要分别建立总帐账户和明细账户。

内部账户和外部账户

当用户使用银行卡来支付时,电商支付系统需要和银行对接,从用户银行卡所代表的账户上扣除资金。对接了银行,第三方支付等机构的电商支付系统,它需要连接到用户在这些机构的账户来执行扣款或者充值操作,这些账户或称为外部账户。对外部账户,支付系统只能记录账户在本系统的明细以及累计消费额,无法得知账户真正余额。 不少电商在玩零钱的概念,也就是让用户充值到零钱,使用的时候就直接从零钱中扣除。这就需要零钱账号。这是电商系统中自己设立的账号,所以也叫内部账号,可以知道账号的全部消费明细和余额。 当然,除了零钱账号,也可以有储值卡账号,信用账号等。那问题来了,什么时候需要建立账户,比如优惠券,需要账户吗? 一次消费的储值卡和可以充值的储值卡,需要建立账户吗?这里先埋个雷,后续介绍支付和记账时,给出答案。

收款账户和收单账户

当电商要对接银行时,往往都会被要求开设一个收款账户。用户通过这个银行来支付时,钱就被转到这个账户上。 对第三方支付也是一样。收款账户是开设在银行或者第三方支付这边的, 即渠道侧。 一般来说,渠道每天都可以提供这个账户的交易流水供电商对账用。 这样在电商这边,渠道就成为一个收单机构。 所以在电商这边,建立这个收款账户对应的对账用的收单账号,用来记录通过这个渠道进行的各项交易流水。

账户建模

说了这么多,目的是为了对账户建模。 账户模型是和公司业务密切相关的,公司不同规模,发展的不同阶段需要不同的模型。 账户建模本身包括三大核心模型:实体模型、账户模型和交易模型。 从交易模型中可以衍生出针对各个角色的账户流水,即明细模型,用于支持对账。

实体模型

实体模型和用户、商户模型有重叠的地方,这里专门针对支付而设置的各个实体属性。 一般来说,支付相关的实体模型需要包括如下的属性:

  • 用户ID,一般直接映射到登录账户的ID;。
  • 是否允许执行支付;
  • 支付密码;
  • 用于设置或者重置支付密码的手机号;
  • 用户设置或者重置支付密码的邮箱;
  • 用户的安全等级,根据业务需要来设置。

账户模型

根据业务需要,可以设置多种账户,如支付账户、预付卡账户、代扣账户、零钱账户、结算账户等。 从类别上来说,这里的账户,一般指总账账户。一般来说电商系统中涉及的账户类型有:

  • 虚拟币账号:用户和使用奇点奇豆的商户都需要建立虚拟币账户。
  • 代扣账号: 用来支持订阅类型的定期代扣;
  • 零钱账号:即电商的内部账号,用户、商户、清算单位需要建立零钱账户
  • 第三方支付账号:用户在第三方支付机构建立的账户。
  • 银行卡账号:用户的银行卡信息,每个卡对应一个账户。
  • 结算账号:用来支持和第三方支付公司、银行进行结算用。 第三方支付需要为每个商户号建立结算账号;银行需要为借记卡、贷记卡分别建立结算账号(有必要吗?银行卡直连时使用)。
  • 代扣代缴账户:用来支持代扣税款业务。

对这些账户,需要设置如下属性: 基本属性,包括:

  • 账户号,或称为账户ID,一般是系统自动生成。特别注意的是,要事先约定好账户ID的规则。比如头三位用来表示账户类型,后几位用来表示账户编号等。务必保证根据账号号能够快速确定账户类型,并且保证账户号是不重复的。
  • 账户名称,一般是由用户自己设置的,显示用。
  • 账户使用的货币类型,注意虽然一张银行卡可以支持多个币种,实际在内部,还是针对每个币种建立独立的子账户。 涉及到多币种的账户,也可以采用类似的建模方案。
  • 会计科目代码,一般是一级会计科目的代码。

账户控制相关:

  • 是否允许充值;
  • 是否允许提现;
  • 是否允许透支;
  • 是否允许支付;
  • 是否允许转账进入;
  • 是否允许转账转出;
  • 是否有安全保障;
  • 是否激活;
  • 是否冻结;

资金相关:

  • 当前账户余额:等于可用余额+冻结余额;
  • 当前账户可用余额;
  • 当前账户冻结的余额。冻结余额指在账户上暂不能使用的额度。在支付的时候,往往是先冻结,商品出库后, 再实际执行扣款。

银行卡、第三方支付信息:

  • 第三方实体的ID;
  • 第三方账号,如银行卡号或者在第三方支付的open_id等;
  • 第三方的app_id;
  • 账号的失效日期,该账号什么时候失效。

注意,有些第三方信息是不能保存的,如用户的账号密码、信用卡的CV号等。 为了避免账户信息被爬库或者数据库信息意外泄露,一般还需要对敏感字段,如密码等,进行加密保存,甚至保存到另外的表中。 更进一步,为了避免账户信息被意外修改,还可以增加一个校验字段,在写入数据时设置该字段,在读取数据时做校验,一旦发现数据有问题,则关闭该账号。

交易模型

交易记录,交易流水,账户流水,交易台账,这三个容易混淆的概念,从数据上来说,却并不复杂,它们的核心是交易流水,账户流水是从账户视角的交易流水。那对一笔交易,涉及到的方方面面内容很多,有哪些需要记录的呢?考虑到交易记录将被用于风控和信用分析,能收集到的信息是越全面越好。

  • 流水号:每一笔交易的流水号都不一样。需要根据业务情况详细设计流水号。这个号往往也是对交易表做分表分库的依据。
  • 交易记录创建时间;
  • 交易记录最后修改时间;
  • 会计科目代码
  • 关联的订单号,由商户提供;
  • 订单名称、描述、关联的地址等信息;
  • 费用信息,包括: 结算货币类型、原始费用、实际费用等;
  • 交易主体信息,记录主体ID、类型、名字、账号、账号类型、使用的IP地址、手机号、平台、通知邮箱、当前位置等。 这些信息虽然可以从主体表中获取,但考虑主体表信息随时会被修改,所以这里需要记录详细的各原始信息。
  • 交易对手信息,记录对手主体的ID,类型,名字,账号,账号类型,手机号,平台,通知邮箱等。
  • 交易渠道信息,记录所使用的交易渠道的实体id,渠道账户,渠道执行支付的时间、渠道侧返回的订单号等。如果有错误发生,还需要记录从渠道接收到的错误信息和错误码。

总结

如上内容,不管是账户还是交易,模型都很复杂。是否有必要记录这么多信息,如何在交易中使用这些模型,请关注后续文章。

头条的同学们,记得帮忙点赞啊。

感谢您对本文的关注,如需要及时收到凤凰牌老熊的最新作品,请扫码关注“凤凰牌老熊”的微信公众号,谢谢。

7 天打造前端性能监控系统

引言

前阵子在w3ctech走进名企 – 百度前端 FEX 专场上曾“夸下海口”说听完讲座后七天就可以打造自己的前端性能监控系统,既然说出去了也不能食言。从前一篇文章前端数据之美相信大家对前端数据有了一定的了解,下面就针对其中的性能数据及其监控进行详细阐述。

开始行动

本文中的性能主要指 web 页面加载性能,对性能还不了解?不用担心,接下来的“每一天”跟我一起进入前端性能的世界。

Day 1 为什么要监控性能?

“If you cannot measure it, you cannot improve it” ———— William Thomson

这是一个最基本的问题,为什么要关注和监控前端性能?对于公司来说,性能在一定程度上与利益直接相关。国外有很多这方面的调研数据:

性能 收益
Google 延迟 400ms 搜索量下降 0.59%
Bing 延迟 2s 收入下降 4.3%
Yahoo 延迟 400ms 流量下降 5-9%
Mozilla 页面打开减少 2.2s 下载量提升 15.4%
Netflix 开启 Gzip 性能提升 13.25% 带宽减少50%

数据来源:http://www.slideshare.net/bitcurrent/impact-of-web-latency-on-conversion-rates http://stevesouders.com/docs/jsdayit-20110511.pptx

为什么性能会影响公司的收益呢?根本原因还是在于性能影响了用户体验。加载的延迟、操作的卡顿等都会影响用户的使用体验。尤其是移动端,用户对页面响应延迟和连接中断的容忍度很低。想象一下你拿着手机打开一个网页想看到某个信息却加载半天的心情,你很可能选择直接离开换一个网页。谷歌也将页面加载速度作为 SEO 的一个权重,页面加载速度对用户体验和 SEO 的影响的调研有很多

尽管性能很重要,开发迭代过程中难免会有所忽视,性能会伴随产品的迭代而有所衰减。特别在移动端,网络一直是一个很大的瓶颈,而页面却越来越大,功能越来越复杂。并没有简单的几条黄金规则就可以搞定性能优化工作,我们需要一套性能监控系统持续监控、评估、预警页面性能状况、发现瓶颈,指导优化工作的进行。

Day 2 有什么可用的工具?

工欲善其事必先利其器

页面性能的评估与监控有很多成熟优秀的工具,合理利用已有工具能达到事半功倍的效果。下面简单介绍几个常用的工具:

Page Speed

Page Speed 是谷歌开发的分析和优化网页的工具,可以作为浏览器插件使用。工具基于一系列优化规则对网站进行检测,对于未通过的规则会给出详细的建议。与此类似的工具还有 Yslow 等,推荐使用gtmetrix网站同时查看多个分析工具的结果,如下图所示:

gtmetrix

WebPagetest

WebPageTest 是一款非常优秀的网页前端性能测试工具,已开源。可以使用在线版,也可以自己搭建。国内也有利用 WebPagetest 搭建的性能测试平台,推荐使用阿里测 (以下示例使用阿里测进行测试)。

使用 WebPagetest,你可以详细掌握网站加载过程中的瀑布流、性能得分、元素分布、视图分析等数据。其中比较直观的视图分析功能可以直接看到页面加载各个阶段的截屏:

webpagetest

: 整个测试结果请点击此处

上图直观地展现了浏览类网站两个重要的时间点:白屏时间和首屏时间,即用户多久能在页面中看到内容,以及多久首屏渲染完成(包含图片等元素加载完成)。这两个时间点直接决定了用户需要等待多久才能看到自己想看到的信息。谷歌优化建议中也提到减少非首屏使用的 css 及 JS,尽快让首屏呈现。

PhantomJS

PhantomJS轻松地将监控带入了自动化的行列。Phantom JS 是一个服务器端的 JavaScript API 的 WebKit,基于它可以轻松实现 web 自动化测试。PhantomJS 需要一定编程工作,但也更灵活。官方文档中已经有一个完整的获取网页加载 har 文件的示例,具体说明可以查看此文档,国内也有不少关于此工具的介绍。另外新浪@貘吃馍香开发的类似工具berserkJS也挺不错,还贴心的提供了首屏统计的功能,具体文章可以查看此处

Day 3 开始线上真实用户性能监控

取其所长,避其所短

到此肯定有同学问,既然有这么多优秀的工具,为什么要监控线上用户真实访问性能呢?

我们发现,工具模拟测试会在一定程度上与真实情况偏离,有时无法反映性能的波动情况。另外除了白屏首屏之类的基础指标,产品线同样关注产品相关的指标,例如广告可见、搜索可用、签到可用等,这些功能直接与页面 JS 加载相关,通过工具较难模拟。

为了持续监控不同网络环境下用户访问情况与页面各功能可用状况,我们选择在页面中植入 JS 来监控线上真实用户访问性能,同时利用已有的分析工具作为辅助,形成一套完整多元的数据监控体系,为产品线的评估与优化提供可靠的数据。

关于不同监控方式的简单对比可以查看下表:

类型 优点 缺点 示例
非侵入式 指标齐全、客户端主动监测、竞品监控 无法知道性能影响用户数、采样少容易失真、无法监控复杂应用与细分功能 Pagespeed、PhantomJS、UAQ
侵入式 真实海量用户数据、能监控复杂应用与业务功能、用户点击与区域渲染 需插入脚本统计、网络指标不全、无法监控竞品 DP 、Google 统计

Day 4 如何采集性能数据?

监控用户的痛点

线上监控哪些指标呢?如何更好地反映用户感知?

对于用户来说他感觉到的为什么页面打不开、为什么按钮点击不了、为什么图片显示这么慢。而对于工程师来说,可能关注的是 DNS 查询、TCP 连接、服务响应等浏览器加载过程指标。我们根据用户的痛点,将浏览器加载过程抽取出四个关键指标,即白屏时间、首屏时间、用户可操作、总下载时间(定义可见上篇文章)。这些指标是如何统计的呢?

确定统计起点

我们需要在用户输入 URL 或者点击链接的时候就开始统计,因为这样才能衡量用户的等待时间。如果你的用户高端浏览器占比很高,那么可以直接使用Navigation Timing接口来获取统计起点以及加载过程中的各个阶段耗时。另外也可以通过 cookie 记录时间戳的方式来统计,需要注意的是 Cookie 方式只能统计到站内跳转的数据。

统计白屏时间

白屏时间是用户首次看到内容的时间,也叫做首次渲染时间,chrome 高版本有 firstPaintTime 接口来获取这个耗时,但大部分浏览器并不支持,必须想其他办法来监测。仔细观察 WebPagetest 视图分析发现,白屏时间出现在头部外链资源加载完附近,因为浏览器只有加载并解析完头部资源才会真正渲染页面。基于此我们可以通过获取头部资源加载完的时刻来近似统计白屏时间。尽管并不精确,但却考虑了影响白屏的主要因素:首字节时间和头部资源加载时间。

如何统计头部资源加载呢?我们发现头部内嵌的 JS 通常需等待前面的 JS\CSS 加载完才会执行,是不是可以在浏览器 head 内底部加一句 JS 统计头部资源加载结束点呢?可以通过一个简单的示例进行测试:

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="UTF-8"/>
    <script>
      var start_time = +new Date; //测试时间起点,实际统计起点为 DNS 查询
    </script>
    <!-- 3s 后这个 js 才会返回 -->
    <script src="script.php"></script>  
    <script>
      var end_time = +new Date; //时间终点
      var headtime = end_time - start_time; //头部资源加载时间    
      console.log(headtime);
    </script>
    </head> 
    <body>     
    <p>在头部资源加载完之前页面将是白屏</p>
    <p>script.php 被模拟设置 3s 后返回,head 底部内嵌 JS 等待前面 js 返回后才执行</p>
    <p>script.php 替换成一个执行长时间循环的 js 效果也一样</p>  
    </body>
</html>

经测试发现,统计的头部加载时间正好跟头部资源下载时间相近,而且换成一个执行时间很长的 JS 也会等到 JS 执行完才统计。说明此方法是可行的(具体原因可查看浏览器渲染原理及 JS 单线程相关介绍)。

统计首屏时间

首屏时间的统计比较复杂,因为涉及图片等多种元素及异步渲染等方式。观察加载视图可发现,影响首屏的主要因素的图片的加载。通过统计首屏内图片的加载时间便可以获取首屏渲染完成的时间。统计流程如下:

首屏位置调用 API 开始统计 -> 绑定首屏内所有图片的 load 事件 -> 页面加载完后判断图片是否在首屏内,找出加载最慢的一张 -> 首屏时间

这是同步加载情况下的简单统计逻辑,另外需要注意的几点:

  • 页面存在 iframe 的情况下也需要判断加载时间
  • gif 图片在 IE 上可能重复触发 load 事件需排除
  • 异步渲染的情况下应在异步获取数据插入之后再计算首屏
  • css 重要背景图片可以通过 JS 请求图片 url 来统计(浏览器不会重复加载)
  • 没有图片则以统计 JS 执行时间为首屏,即认为文字出现时间

统计用户可操作和总下载

用户可操作默认可以统计domready时间,因为通常会在这时候绑定事件操作。对于使用了模块化异步加载的 JS 可以在代码中去主动标记重要 JS 的加载时间,这也是产品指标的统计方式。

总下载时间默认可以统计onload时间,这样可以统计同步加载的资源全部加载完的耗时。如果页面中存在很多异步渲染,可以将异步渲染全部完成的时间作为总下载时间。

网络指标

网络类型判断

对于移动端来说,网络是页面加载速度最大的影响因素,需要根据不同的网络来采取相应的优化措施,例如对于 2G 用户采用简版等。但 web 上没有接口获取用户的网络类型。为了获取用户网络类型,可以通过测速的方式来判断不同 IP 段对应的网络。测速例如比较经典的有 facebook 的方案。经过测速后的分析,用户的加载速率有明显的分布区间,如下图所示:

gtmetrix

各个分布区间正好对应不同的网络类型,经过与客户端的辅助测试,成功率可以在 95%以上。有了这个 IP 库对应的速率数据,就可以在分析用户数据时根据 IP 来判断用户网络类型。

网络耗时统计

网络耗时数据可以借助前面提到 Navigation Timing 接口获取,与之类似的还有Resource Timing,可以获取页面所有静态资源的加载耗时。通过此接口可以轻松获取 DNS、TCP、首字节、html 传输等耗时,Navigation Timing 的接口示意图如下所示:

gtmetrix

以上重点介绍了数据采集部分,这也是系统中最关键的一部分,只有保证数据能真实反映用户感知,才能对症下药提升用户体验。数据采集完之后我们可以在页面加载完之后统一上报,如示例:

http://xxx.baidu.com/tj.gif?dns=100&ct=210&st=300&tt=703&c_dnslookup=0&c_connecting=0&c_waiting=296&c_receiving=403&c_fetch_dns=0&c_nav_dns=75&c_nav_fetch=75&drt=1423&drt_end=1436&lt=3410&c_nfpt=619&nav_type=0&redirect_count=0&_screen=1366*768|1366*728&product_id=10&page_id=200&_t=1399822334414

### Day 5 如何分析性能数据?

让数据会说话

而数据分析过程,如前一篇文章所述,可以从多个维度去分析数据。大数据处理需要借助 hadoop、Hive 等方式,而对于普通站点则任意一种后端语言处理即可。

均值与分布

均值与分布是数据处理中最常见的两种方式。因为它能直观的表示指标的趋势与分布状况,方便进行评估、瓶颈发现与告警。处理过程中应去除异常值,例如明显超过阈值的脏数据等。

耗时的评估中,有很多这方面的研究数据。例如有人提出三个基本的时间范围:

  • 0.1秒 : 0.1 秒是用户感知的最小粒度,在这个时间范围内完成的操作被认为是流畅没有延迟的
  • 1.0秒 : 1.0 秒内完成的响应认为不会干扰用户的思维流。尽管用户能感觉到延迟,但 0.1 秒 -1.0 秒内完成的操作并不需要给出明显 loading 提示
  • 10秒 : 达到 10 秒用户将无法保持注意力,很可能选择离开做其他事情

我们根据业界的一些调研,结合不同指标的特点,制定了指标的分布评估区间。如下图所示:

gtmetrix

评估区间的制定方便我们了解当前性能状况,同时对性能趋势波动做出反应。

多维分析

为了方便挖掘性能可能的瓶颈,需要从多维的角度对数据进行分析。例如移动端最重要的维度就是网络,数据处理上除了总体数据,还需要根据网络类型对数据进行分析。常见的维度还有系统、浏览器、地域运营商等。我们还可以根据自身产品的特点来确定一些维度,例如页面长度分布、简版炫版等。

需要注意的是维度并不是越多越好,需要根据产品的特点及终端来确定。维度是为了方便查找性能瓶颈

小插曲 :之前从微博中看到有人评价说想做监控但是公司没有日志服务器。并不需要单独的日志服务器,只要能把统计的这个请求访问日志保存下来即可。如果网站自己的独立服务器都没有还有解决办法,在百度开发者中心新建一个应用,写一个简单的 Web 服务将接收到的统计数据解析存到百度云免费的数据库中,然后每天再用 Mysql 处理下当天的数据即可,对于普通站点的抽样性能数据应该没问题。请叫我雷锋。

Day 6 如何利用监控数据解决问题?

发现瓶颈,对症下药

对于图表制作,比较出名的有Highcharts,百度开发的Echarts也很不错。不管使用什么工具,最关键的一点就是让报表能突出重点,直观明了

制作报表前多问几个如何让人直观看到目前状况和可能存在的问题,哪些地方可以加强,哪些可以去掉,使用是否习惯等。

gtmetrix

有了能反映用户感知的真实世界、并且细分到各个业务功能,有详细的网络等辅助数据,我们在解决前端性能上便更加得心应手。监控系统已经对线上访问状况有了连续的反馈,根据现有评估与瓶颈选择对应方案进行优化,最后根据反馈进行调整,相信性能优化不再是个难题。

如何选择优化方案呢?这又是一个比较大的话题了,好在已经有很多经验可以借鉴。附录中就整理了部分性能的学习资料,可以根据需要阅读学习。

gtmetrix

Day 7 总结

通过以上“几天”的努力,我们可以搭建一个小而美的前端性能监控系统。但这仅仅是开始,前端数据有很多挖掘的价值。性能优化也是一门需要认真学习的课程,为了打造流畅的使用体验,为了让用户更加满意,赶紧搭建起自己的前端数据平台吧!

该文写在w3ctech走进名企 – 百度前端 FEX 专场之后,分享时的 PPT 在这里,视频在这里

华丽非分割线 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

福利——前端性能学习资料整理

性能准则 ★★★★★

分析工具 ★★★

入门

  • pageSpeed 基于谷歌性能准则的检测,可浏览器安装插件运行
  • Yslow 基于雅虎性能准则的检测工具,可浏览器安装插件运行
  • pageCheck 百度内部开发,指标齐全,支持自动运行

进阶

  • webPageTest 查看页面加载瀑布流等数据,进阶必备工具
  • Chrome 开发者工具 功能强大,值得学习
  • PhantomJS 功能强大的分析工具,高手必备瑞士军刀
  • JsPerf JS 执行性能分析网站,谁用谁知道

浏览器与 Html 标准 ★★

入门

进阶

开发实战 ★★★★

通用

动画与渲染

移动端开发

性能监控 ★★★★

相关会议 ★★★

推荐博客 ` ★★★`

Building Grafana from source

This guide will help you create packages from source and get grafana up and running in dev environment. Grafana ships with its own required backend server; also completely open-source. It’s written in Go and has a full HTTP API.

Dependencies

Get Code

Create a directory for the project and set your path accordingly. Then download and install Grafana into your $GOPATH directory

export GOPATH=`pwd`
go get github.com/grafana/grafana

Building the backend

cd $GOPATH/src/github.com/grafana/grafana
go run build.go setup              # (only needed once to install godep)
$GOPATH/bin/godep restore          # (will pull down all golang lib dependencies in your current GOPATH)
go run build.go build              # (or 'go build .')

Building on Windows

The Grafana backend includes Sqlite3 which requires GCC to compile. So in order to compile Grafana on windows you need to install GCC. We recommend TDM-GCC.

Build the Front-end Assets

To build less to css for the frontend you will need a recent version of node (v0.12.0), npm (v2.5.0) and grunt (v0.4.5). Run the following:

npm install
npm install -g grunt-cli
grunt

Recompile backend on source change

To rebuild on source change (requires that you executed godep restore)

go get github.com/Unknwon/bra
bra run

Running Grafana Locally

You can run a local instance of Grafana by running:

./bin/grafana-server

If you built the binary with go run build.go build, run ./bin/grafana-server

If you built it with go build ., run ./grafana

Open grafana in your browser (default http://localhost:3000) and login with admin user (default user/pass = admin/admin).

Developing for Grafana

To add features, customize your config, etc, you’ll need to rebuild on source change (requires that you executed godep restore, as outlined above).

go get github.com/Unknwon/bra
bra run

You’ll also need to run grunt watch to watch for changes to the front-end.

Creating optimized release packages

This step builds linux packages and requires that fpm is installed. Install fpm via gem install fpm.

go run build.go build package

Dev config

Create a custom.ini in the conf directory to override default configuration options. You only need to add the options you want to override. Config files are applied in the order of:

  1. grafana.ini
  2. custom.ini

Learn more about Grafana config options in the Configuration section

Create a pull requests

Please contribute to the Grafana project and submit a pull request! Build new features, write or update documentation, fix bugs and generally make Grafana even more awesome.

Before or after you create a pull request, sign the contributor license agreement. Together we can build amazing software faster.