最近开发一直用的是thinkphp5.0 rc4,个人感觉相较于tp3.x, tp5.0的进步是巨大的。
开发使用框架时,有时候需要根据自己的业务逻辑来框架源码做一些小改动,本文记录我在开发过程中对tp5.0 rc4做出修改的地方,如果更新框架,就需要对这些地方重新修改,特此MARK!

1.Db类中使用分页输出数据的page()方法。
改动缘由:跟前台程序对接的时候,需要根据前台传入的$page变量来选择输出.例如page($page,10),代表按10条数据分页,取第$page页。
在开发的时候我发现$page传入0或者1,生成的sql都是一样的,取到的数据当然也一样。可能tp作者觉得默认开始的第一页就应该以1来表示,没有第0页。但跟前台约定的是:$page默认是0,所以$page=0应该是第一页,$pgae=1代表第二页。如果不做修改的话,$page第一次传0,下一次传1会返回相同的数据。
改动位置/thinkphp/library/think/db/Query.php(2122-2129):

if (isset($options['page'])) {
     // 根据页数计算limit
     list($page, $listRows) = $options['page'];
     $page                  = $page > 0 ? $page : 1;//对于传入的0它会自动变为1,需要改动
     $listRows              = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20);
     $offset                = $listRows * ($page - 1);
     $options['limit']      = $offset . ',' . $listRows;
}

更改后:

if (isset($options['page'])) {
     // 根据页数计算limit
     list($page, $listRows) = $options['page'];
     $page                  = $page >= 0 ? $page : 0;//传入的0会保持原来的0,并且其他非自然数类型也要自动变为0
     $listRows              = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20);
     $offset                = $listRows * $page;//$page-1改为$page
     $options['limit']      = $offset . ',' . $listRows;
}

2.tp5在应用根目录下composer安装极光推送后,服务器一直500。经察是由于极光推送的日志文件jpush.log由于权限不够无法创建导致。手动建立该文件,并赋予766权限后还是报同样的错误。后来发现是由于极光推送的默认日志记录文件为【JPush.php中第16行】:

const DEFAULT_LOG_FILE = "./jpush.log";

这个目录应该建立在tp5的入口文件的同级目录下。出于安全起见,我将tp5的入口文件部署在web目录下,而tp5核心框架及代码部署在非web目录下,所以我刚开始建立的jpush.log的位置错了。
可以手动指定绝对路径:

const DEFAULT_LOG_FILE = "absolute_path";

有时候我们进行贝叶斯分类时,由于数据量太大导致内存溢出或者对模型的训练有着特殊要求(比如用第一个月的数据预测第二个月,再将第二个月的数据加入已经训练好的模型,去预测第三个月…),这时普通的贝叶斯分类不行了。我们需要使用贝叶斯来进行增量学习(incremental learning), weka的贝叶斯分类器即存在增量式的贝叶斯分类器。下文以多项式朴素贝叶斯增量学习为例,weka中的包为:

weka.classifiers.bayes.NaiveBayesMultinomialUpdateable

下文是核心代码:

        //获取第一个月的数据用于建立模型
        ArffLoader loader = new ArffLoader();
        loader.setFile(new File("month1.arff"));
        loader.getStructure();//这句话必须有,不然会报错

        //定义分类器 并用第一个月的数据做训练
        Instances originDataSet = loader.getDataSet();//获取第一个月的实例
        originDataSet.setClassIndex(originDataSet.numAttributes()-1);//定义最后一个属性作为分类目标
        NaiveBayesMultinomialUpdateable nb = new NaiveBayesMultinomialUpdateable();//定义模型
        nb.buildClassifier(originDataSet);//建立模型

        //对从第二个月起的每个月进行预测和训练,一直到第100个月,(2,100只是用于示例)
        for (int month = 2; month <= 100; month++) {
            //读取测试集
            ArffLoader testLoader = new ArffLoader();
            testLoader.setFile(new File("month"+i+".arff"));
            testLoader.getStructure();

            Instances testDataSet = testLoader.getDataSet();//获取测试集实例
            //进行分类,并且输出
            for (int i = 0; i< testDataSet.numInstances(); i++) {
                FileWriter fileWritter = new FileWriter("output.txt", true);
                BufferedWriter bufferWritter = new BufferedWriter(fileWritter);
                bufferWritter.write(nb.distributionForInstance(testDataSet.instance(i))[1]+'\n');//这一行输出的是分类中分到第二类(下标为1)的概率
                bufferWritter.flush();
            }

            // 再把这次的测试集当做训练集进行训练
            for (int i = 0; i< testDataSet.size(); i++) {
                testDataSet.setClassIndex(testDataSet.numAttributes()-1);
                nb.updateClassifier(testDataSet.get(i));
            }

一款产品发布,其初始用户量很小,这时就需要伪造一批用户来增加平台的用户量,搭起初步的用户生态系统。那么伪造用户时,我们必然需要网名以及头像(也有可能需要签名)等等信息。所以前些日子写了个小爬虫来爬取一批(30000+)网名及头像。

对于头像的爬取,我选择的站点是“我要个性网”(http://www.woyaogexing.com),它的图片都没有做防盗链,因此我只需要爬它图片的url就行。对于昵称,我选择的网站是“QQ网名”(http://www.oicq88.com)。选择好爬取目标,就需要咱的爬虫登场了。以前爬虫经常用scrapy,但对于我这次的量显然是杀鸡用牛刀。这个爬虫是利用python自带的urllib2,解析网页用的是BeautifulSoup(可用pip安装),与xpath原理类似。

下面是头像爬虫源码,代码很简单:

# coding:utf-8
import time
import urllib2
import random
from bs4 import BeautifulSoup

def filter(tag):
    if cmp(tag.name, 'img') == 0:
        if tag.has_attr('class'):
            if cmp(tag['class'][0], 'lazy' == 0):
                return True

outfile = open("./20160717/avatar.txt", "a")

for i in range(135, 1500):
    print i
    url = 'http://www.woyaogexing.com/touxiang/index_'+str(i)+'.html'
    response = urllib2.urlopen(url)
    data = response.read()
    soup = BeautifulSoup(data, "lxml")
    imgs = soup.find_all(filter)

    for img in imgs:
        outfile.write(img['src'] + ',' + str(random.randint(0, 1))+ '\n')
    time.sleep(0.5)

#最后输出的时候在每个头像url后面接了个随机的0或者1,这是用随机数来标志这个人是男生还是女生。

下面是爬取昵称的爬虫:

# coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

import urllib2
from bs4 import BeautifulSoup

def filter(tag):#解析包含网名的标签
    if cmp(tag.name, "ul") == 0:
        if tag.has_attr("class"):
            if cmp(tag['class'][0], 'list') == 0:
                return True

outfile = open("./name.txt", "a")#输出文件

for i in range(55, 145):
    print i
    url = 'http://www.oicq88.com/nvsheng/'+str(i)+'.htm'
    response = urllib2.urlopen(url)#/nvsheng/可以替换为其他的
    data = response.read()
    soup = BeautifulSoup(data, "lxml")
    ul = soup.find_all(filter)

    ulsoup = BeautifulSoup(str(ul[0]), "lxml")
    lis = ulsoup.find_all("li")


    for li in lis:
        outfile.write(str(li.p.text)+'\n')</pre>

可以直接复制源码进行爬取,前提是“我要个性网”以及“QQ网名”的前端页面没变,为了防止这种情况发生,文末我会附上爬取的源文件链接,大家可以自行下载。

文件下载链接:https://ayonel.malash.net/files/name_avatar_over.zip

今天打算好好测一下我们的哒哒相机APP (友情广告一波,下载链接)的后台性能。目前哒哒相机APP只用了一台阿里云ECS(1核CPU、2G内存~~). 为了开发测试, 又买了一台用作测试服务器(1核CPU、1G内存). 后台环境为ubuntu14.04、nginx 1.4、mysql 5.6 以及php5.6.

用ab分别在正式服务器和测试服务器对同一个php文件进行读取,发现在同等压力下测试服务器的速度就是要比正式服务器高。这可就郁闷死我了,你说一样的配置,正式服务器还比测试服务器多了一倍内存,为毛并发还比不上测试服务器?这一整天就耗在这个问题上了。

刚开始我以为是PHP配置的问题,对比了两台服务器的PHP配置项,关于cache以及memory的都一样啊,为了排除是否是PHP的原因,我将测试PHP文件换为了一张4KB的图片,继续AB:

这是正式服务器100并发结果,如下:

正式服务器AB

这是测试服务器100并发结果,如下:

abtest

可以看到测试服务器响应速度还是要明显高于正式服务器(多次换文件测试,速度大概高1/3),这就排除了是PHP的问题;那就只有nginx了。又反复查看了两台nginx的配置文件,也是一样,期间还做了很多乱七八糟的测试,还是没能找出原因。

这时我突然意识到会不会是硬件的问题,两台服务器都是阿里云,但是买的时候先买了正式服务器,最近才买的这个测试服务器。虽然买的时候都选的同样的配置,但是阿里云屏蔽了很多硬件细节的东西,只告诉你CPU要几核,内存要多大。。。

  1. 先测两者的硬盘读写速率,我用的是hdpram,需要先安装.
    正式服务器为:
    >hdparm -tT /dev/xvda
    /dev/xvda:
    Timing cached reads: 15612 MB in 1.99 seconds = 7845.40 MB/sec
    Timing buffered disk reads: 238 MB in 3.02 seconds = 78.92 MB/sec
    测试服务器为:
    /dev/xvda:
    Timing cached reads: 17848 MB in 2.00 seconds = 8932.35 MB/sec
    Timing buffered disk reads: 240 MB in 3.00 seconds = 79.90 MB/sec
    以上结果测试了多遍,均显示测试服务器磁盘的读速率要比正式服务器磁盘高出14%,两者的写差不多。到这我还是不太相信两台服务器并发量的差别完全是由磁盘导致的。
  2. 我又查看了两者的CPU 信息,命令: cat /proc/cpuinfo
    正式服务器结果为:
    processor : 0
    vendor_id : GenuineIntel
    cpu family : 6
    model : 45
    model name : Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz(型号)
    stepping : 7
    microcode : 0x70d
    cpu MHz : 2300.102(频率)
    cache size : 15360 KB(缓存大小)
    physical id : 0
    siblings : 1
    core id : 0
    cpu cores : 1
    apicid : 0
    initial apicid : 0
    fpu : yes
    fpu_exception : yes
    cpuid level : 13
    wp : yes
    flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl pni ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm
    bogomips : 4600.20
    clflush size : 64
    cache_alignment : 64
    address sizes : 46 bits physical, 48 bits virtual
    power management:
    测试服务器结果为:
    processor : 0
    vendor_id : GenuineIntel
    cpu family : 6
    model : 62
    model name : Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz(CPU型号)
    stepping : 4
    microcode : 0x428
    cpu MHz : 2593.838(频率)
    cache size : 20480 KB(缓存大小)
    physical id : 0
    siblings : 1
    core id : 0
    cpu cores : 1
    apicid : 0
    initial apicid : 0
    fpu : yes
    fpu_exception : yes
    cpuid level : 13
    wp : yes
    flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl pni ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm
    bogomips : 5187.67
    clflush size : 64
    cache_alignment : 64
    address sizes : 46 bits physical, 48 bits virtual
    power management:

    可以看出测试服务器CPU为2.6GHz,要比正式服务的2.3GHz要高不少,另外CPU缓存也高出了1/3.到这,我基本确定了,不是PHP的问题,不是nginx的问题,是阿里云不同时间段买的ECS底层配置根本不一样,虽然用户购买的时候都选的同样的核心数,多样大的磁盘,但是它的硬件已经升级了(自来水一波:阿里云还是相当不错的)。
    一天的时间就耗费在这儿了,不过挺开森~~~特此MARK

前些日子,AWS EC2做活动,注册即可免费试用一年,这等好事怎能错过,遂赶紧领了一台,可以上一些和谐的网站,顺便当做我个人的博客。因此这算是我正式写的第一篇博客吧。(Mark)

——————————————-分割线————————————————-

大概在三个月前过年回家的时候,偶然看到了前阿里大牛李智慧的《大型网站技术架构-核心原理与案例分析》,作为一个技术noob,内心除了翻滚的敬仰之情,更多的带给我的是向往,淘宝这种日均亿级别PV的网站是如何实现的。

我也写了半年后台,用的是LNMP,但对系统性能调优方面一无所知,服务器正常运作的并发量也就在几百左右,现在才深知与这个行业最牛X的那群工程师们的差距。但我认为,永远不要畏惧新的事物,这是一个程序猿最重要的品质,如果你惧怕它,觉得接触新的东西可能要花一段时间去适应它,但问题就是计算机这个日新月异的行业,你只有永远站在浪尖上,哪怕是追随,才能保证自己不被淘汰。所以我认为以android studio不好用,不习惯,教程少等为借口而继续用eclipse开发android的人,不具备一个程序员的基本素质。

扯远了,回到正题,前些阵子由于各种杂事,好一阵忙。看《大型网站技术架构》时的一腔激情也正在慢慢消磨,我意识到一定要尽快开始本小白的架构之路了,但我意识到以我一个人的精力,能力,要独立搭一个像模像样的网站架构,可能有些虚。果断邀请了三个同学:海哥、Master韩,小聪来一起做,出乎意料的是我找他们谈的时候,他们都展示出了极大的兴趣。

Let‘s Begin!

做这东西,需要服务器啊怎么办,本来为这事正发愁的时候,到实验室一看,OMG,墙角居然有一堆退槽的主机。真是天助我也,话多不说,当晚就开始。可恨的是这些主机大部分都不能直接用,不是硬盘坏了就是其他问题,没办法,只能东凑凑,西拼拼,鼓鼓捣捣一直到第二天凌晨3点半,居然凑出了6台能用的主机,都给“他”们清一色地上了ubuntu-server-14.04.4。(注意用词,没用它,因为他们都有一个响亮的名字,大娃、二娃、三娃、四娃、五娃,六娃,可惜的是七娃的系统就是装不上去,难道是跟ubuntu一个色,冲突了?哈哈!)

来一张图:

韩大师有特殊的攒机技能

韩大师有特殊的攒机技能

这也算正式开始了我们几个小白的架构之路!能够与一帮志同道合的同学,做一件自己喜欢的,有挑战性的事,这应该算是一种快乐!

PS:manual rainbow