HTML5实用标签属性用法

整   理:晋 哲

时   间:2017-02-23

说   明:介绍<picture>和<details>HTML5新标签的实用用法

一、响应式显示图像
在<picture>标签里会先识别<source>里的路径,当满足media条件时显示srcset里的图片,当不满足条件则显示<img>里的图片。

<picture>
    <source srcset="images/pic2_1.png, images/pic2_2.png 2x"
            media="(min-width: 200px) and (max-width:320px)">
    <img src="images/pic1_1.png" srcset="images/pic1_2.png 2x, images/pic1_3.png 3x" />
</picture>

1、<source>元素用于创建可替换的图像文件;

2、media属性:用来包含想要展示的特性,比如视口的当前高度(viewport height),宽度(width),方向(orientation)等;

3、srcset属性:与相应的图像文件名相匹配,进行加载。如果需要提供不同的像素密度,可以添加多个的文件名到srcset属性中。

提示:可以放置多个<source>标签,以指定不同的图像文件名,进而根据不同的条件进行加载。
兼容性:
compatibility1

二、显示或隐藏下拉菜单
在<details>标签里会默认先隐藏<summary>标签以外的html内容,点击<summary>标签内容则显示出来,形成下拉效果。

<details>
    <summary>Click me</summary>
    <dl>
        <dd>list1</dd>
        <dd>list2</dd>
        <dd>list3</dd>
    </dl>
</details>

提示:请与<details>标签一起使用。<summary>标题是可见的,当用户点击标题时会显示出详细信息。
注释:"summary"元素应该是"details"元素的第一个子元素。
兼容性:
compatibility2

分析页面ajax请求时间过长问题

标签(空格分隔): 性能


前提:
商城的首页是采用装修做的,页面会有很多的商品块,采用ajax局部加载的方式来绘制到页面上,但是部分ajax的请求速度非常慢,找出问题原因并解决


此处输入图片的描述
上图可以看出:ajax请求加载页面时,尽管会获取的内容很小(几百B)但是网络相应时间却非常长,主要集中在waiting(TTFB)

waiting(TTFB)解释:
从客户端开始和服务端交互到服务端开始向客户端浏览器传输数据的时间(包括DNS、socket连接和请求响应时间),是能够反映服务端响应速度的重要指标,获取在接收到响应的首字节前花费的毫秒数。


本次主要从程序方面来分析:即服务器接受请求开始到返回数据给客户端为止
1、看页面ajax请求的页面是否本身加载速度很慢
2、在页面请求过程中,除了本身请求的商品数据之外,是否还有其他的内容需要加载
3、解决上述两点问题,观察页面的响应时间


解决办法:
1、通过xhprof来进行性能分析,发现商品信息加载只耗费了43ms左右的时间,钩子中的商城数据消耗约417ms的时间,对钩子数据接口进行缓存,将钩子的请求时间降到45ms左右
重新观察页面,部分ajax请求的时间仍然在1-2s左右。

2、试着减少页面的ajax强求,发现响应时间会有变化,怀疑页面ajax请求过多导致,google:AJAX请求使PHP反应时间过长的问题。

PHP自带Session隐患(session文件独占锁引起阻塞):当服务器发送一个Ajax请求时,PHP脚本开启了session_start(),它的调用会锁定PHP的session文件。PHP默认会把session数据存储在服务器上的文件中。因为仅仅只有一个PHP请求能改变同一个session文件,两个同时的PHP请求可能会造成典型的文件锁条件。

大部分PHP框架会首先在主文件中使用session_start()。因此,如果使用会调用session_start()的框架或者函数库,将会造成session文件锁,对于使用同一个浏览器的相同用户,这将延迟同时发送的Ajax请求。

结合了PHP的Session机制,找到了阻塞的原因。由于PHP的Session信息是写入文件的,1个客户端占有1个session文件。因此,当session_start被调用的时候,该文件是被锁住的,而且是以读写模式锁住的(因为程序中可能要修改session的值),这样,第2次调用 session_start的时候就被阻塞了。

    最简解决方法:
PHP的手册session_write_close函数,作用是Write session data and end session。因此,我们可以在用完session之后,调用这个函数关闭session 文件即可解除锁定。

调用session_write_close()之后,当前脚本会继续正常运行,但在调用session_write_close()之后不允许改变任何session变量;在同一个脚本中,其它同时发送给PHP的请求可以锁定session文件并改变session变量。


备注:
session数据通常会在脚本执行结束后被保存,而并不需要调用session_write_close(),但是为保护session在任何时候都只能被一个脚本执行写操作,session的数据会被锁住。当同时使用框架网页和session时你会发现,框架里的网页会因为这个个锁定而逐个载入。可以通过在所有的session数据修改保存结束后马上结束session来加快载入时间。

安装软件包的三种方式

标签(空格分隔): linux


前提:搭建虚拟机配置,需要注意:需要安装哪些软件?如何安装?放在linux哪里个目录下?本文主要讲需要可以使用哪些命令来安装软件

1、copy 解压

1、从官方网站直接下载软件包
2、利用winSCP上传
3、解压相关文件,编译安装

2、wget:命令行下载工具,多数linux发行版本都默认包含此工具,若没有可以自行编译安装

使用方式:

 wget  http://downloads.sourceforge.net/project/pcre/pcre/8.39/pcre-8.39.tar.gz

命令参数使用:http://man.linuxde.net/wget

2.1 支持断点下传功能


2.2 同时支持FTP和HTTP下载方式        

wget [url]

这个命令可以将url的首页下载下来。
使用-x会强制建立服务器上一模一样的目录,如果使用-nd参数,那么服务器上下载的所有内容都会加到本地当前目录首页下载下来。会按照递归的方法,下载服务器上所有的目录和文件,实质就是下载整个网站。这个命令一定要小心使用,因为在下载的时候,被下载网站指向的所有地址。
同样会被下载,因此,如果这个网站引用了其他网站,那么被引用的网站也会被下载下来!基于这个原因,这个参数不常用。可以用-l

number参数来指定下载的层次。例如只下载两层,那么使用-l 2。
要是您想制作镜像站点,那么可以使用-m参数,

例如:wget  -m [url]
这时wget会自动判断合适的参数来制作镜像站点。

2.3 批量下载
如果有多个文件需要下载,那么可以生成一个文件,把每个文件的URL写一行,例如生成文件download.txt,

命令:wget -i download.txt

使用断点续传要求服务器支持断点续传。-t参数表示重试次数,例如需要重试100次,那么就写-t 100,如果设成-t 0,那么表示无穷次重试,直到连接成功。
-T参数表示超时等待时间,例如-T 120,表示等待120秒连接不上就算超时。

选择性的下载可以指定让wget只下载一类文件,或者不下载什么文件。例如:

wget -m –reject=gif -accept=LIST http://target.web.site/subdirectory


安装软件的步骤:

# tar zxvf 安装包
# cd 解压后的安装包
# ./configure编译
# make
# make install

3、yum:

介绍:

Shell前端软件包管理器,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。yum提供了查找、安装、删除某一个、一组甚至全部软件包的命令,而且命令简洁而又好记。

 **参数使用:http://man.linuxde.net/yum**

3.1 yum的一切配置信息都储存在一个叫yum.conf的配置文件中,通常位于/etc目 录下,这是整个yum系统的重中之重
[main]
cachedir=/var/cache/yum/$basearch/$releasever //yum缓存的目录,yum在此存储下载的rpm包和数据库,一般是/var/cache/yum
keepcache=0 //缓存是否保存,1保存,0不保存。
debuglevel=2    //除错级别,0──10,默认是2
logfile=/var/log/yum.log //yum的日志文件
exactarch=1 //有两个选项1和0,代表是否只升级和你安装软件包cpu体系一致的包,如果设为1,则如你安装了一个i386的rpm,则yum不会用686的包来升级
obsoletes=1 //  #update的参数,相当于upgrade,允许更新陈旧的RPM包。
gpgcheck=1 //有1和0两个选择,分别代表是否是否进行gpg校验,默认是检查的。
plugins=1 //是否允许使用插件,默认是0不允许,但是我们一般会用yum-fastestmirror这个插件
installonly_limit=5//允许保留多少个内核包。
bugtracker_url=http://bugs.centos.org/set_project.php?project_id=19&ref=http://bugs.centos.org/bug_report_page.php?category=yum
distroverpkg=centos-release
3.2 用yum查询想安装的软件
yum search//使用YUM查找软件包
yum list//列出所有可安装的软件包
yum list updates//列出所有可更新的软件包
yum list installed//列出所有已安装的软件包
yum list extras//列出所有已安装但不在 Yum Repository 內的软件包
yum list// 列出所指定的软件包
yum info  //使用YUM获取软件包信息
yum info //列出所有软件包的信息
yum info updates//列出所有可更新的软件包信息
yum info installed//列出所有已安裝的软件包信息
yum info extras//列出所有已安裝但不在 Yum Repository 內的软件包信息
yum provides//列出软件包提供哪些文件
3.3.清除YUM缓存
    yum 会把下载的软件包和header存储在cache中,而不会自动删除。如果我们觉得它们占用了磁盘空间,可以使用yum clean指令进行清除,更精确的用法是yum clean headers清除header,yum clean packages清除下载的rpm包,yum clean all 清除所有
yum clean packages//清除缓存目录(/var/cache/yum)下的软件包 
yum clean headers//清除缓存目录(/var/cache/yum)下的 headers 
yum clean oldheaders//清除缓存目录(/var/cache/yum)下旧的 headers 
yum clean, yum clean all (= yum clean packages; yum clean oldheaders)//清除缓存目录(/var/cache/yum)下的软件包及旧的headers

webpack基础入门

modules with dependencies ---webpack---> static assets

使用的DEMO

介绍

模块打包工具(不仅仅只是打包js)

模块

现在的网站都在演变成为Web Apps:

  • 页面上的JavaScript越来越多。
  • 在现代浏览器上用户可以做更多的事情了。
  • 整个页面重新加载的情况更少了,与此同时,页面上的代码量更大了。

结果就是:客户端的代码量变得越来越庞大,庞大的代码量意味着我们需要适当地组织代码,而模块系统则提供了把代码分割成不同模块的功能。

模块化历史进程

  • Script标签形式
  • CommonJS(CMD)
  • AMD和一些变种实现
  • ES6模块
  • 其它

目的

  • 把依赖树按需分割
  • 把初始加载时间控制在较低的水平
  • 每个静态资源都应该能成为一个模块
  • 能把第三方库继承到项目里来成为一个模块
  • 能定制模块打包器的每个部分
  • 能适用于大型项目

简单使用

几个名称
1. node

js的服务端实现,基于CMD

  1. npm

    > node 官方认证的包管理工具,类似于php composer。非常慢(加上代理也是慢)。

  2. cnpm

    > 阿里巴巴出品的包管理工具,兼容大部分npm功能,使用淘宝源,项目结构必npm简单,更新安装新的依赖快。

  3. yarn

    > facebook 出品的包管理工具,和cnpm类似,诞生时间多,但是比cnpm热,中国码农的国际影响力不行啊,有好东西不知道宣传,cnpm比yarn不知道早多久了。yarn 比cnpm突出的一点是:默认使用lock文件,固定版本,有利于大型项目规定依赖版本。

推荐使用yarn(不过cnpm更简单一些,所以可以先用cnpm)

npm install -g cnpm --registry=https://registry.npm.taobao.org

安装Webpack-cli

cnpm install webpack -g

使用命令行

 // 最简单的命令 webpack <entry> <output>
webpack ./app.js bundle.js

额外参数

  1. -p 压缩js并且丑化
  2. --watch 监控文件改动
  3. --progress 显示构建的详细过程

详细参数文档

使用配置文件

对于一般的项目,直接使用cli是不太可能的,所以我们需要配置文件,使用配置文件有两种方式

  1. cli
  2. node api

CLI

创建 webpack.config.js 文件

 module.exports = {
     entry: './src/app.js',
     output: {
         path: './dist',
         filename: 'app.bundle.js'
     }
 };

然后在webpack.config.js文件的目录下面执行命令webpack

webpack 会自动查找目录下面的webpack.config.js,如果需要指定,可以使用--config 指定配置文件。

NODE API

创建webpack.node.js文件

var webpack = require("webpack");

webpack({
    entry: './src/app.js', // 原文件
    output: {
        path: './dist', // 输出路径
        filename: 'app.bundle.js' // 输出文件名
    }
}, function (err, stats) {
});

然后在webpack.config.js文件的目录下面执行命令node webpack.node.js

DEMO2

ES6

一个很好的ES6指南

为什么要ES6?随波逐流是个好选择,不过就自己来说我喜欢ES6的这几个方面

  1. module管理,import和export使用非常爽
  2. let不自动扩展作用域
  3. =>,解决了function this的问题

下面我们使用ES6的module管理来编写一段程序

log.js

var log = function (text) {
    console.log(text);
}

export {log};

app.js

var log = function (text) {
   console.log(text);
};

export log;

不过es6现在不是浏览器的主流支持版本,所以需要转义成es5,这里就需要使用babel

修改package.json,加入babel依赖

{
  "name": "webpack-demo",
  "version": "0.0.1",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^1.14.0",
    "babel-core": "^6.18.2",
    "babel-loader": "^6.2.8",
    "babel-plugin-transform-runtime": "^6.15.0",
    "babel-polyfill": "^6.16.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-stage-2": "^6.18.0"
  }
}

再修改webpack.config.js

module.exports = {
    entry: './src/app.js',
    output: {
        path: './dist',
        filename: 'app.bundle.js'
    },
  module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader?cacheDirectory=true'
            }
        ]
    }
};

这里使用了webpack的loader功能,这是webpack最重要的功能了,加载各个模块,打包到指定的地方。

之后我们还需要指定babel使用的规范,这里有两个选择,一个是写在webpack的config文件中,还有一个是在root dir下面创建.babelrc来指定规范,这里选择第二种(因为这样可以确保别人能一眼看出你代码书写的规范),在当前目录创建.babelrc

{
  "presets": ["es2015", "stage-2"],
  "plugins": ["transform-runtime"],
  "comments": false
}

然后在webpack.config.js文件的目录下面执行命令webpack

DEMO4

关于其他loader使用,一般是**-loader

  • url-loader
  • file-loader
  • style-loader
  • vue-loader

不过每个loader都存在一些使用的差异性,需要去查看各自的文档。

插件

之前可以使用webpack -p 压缩并且丑化js,不过在配置文件可以更详细的配置这些操作

module.exports = {
    entry: './src/app.js',
    output: {
        path: './dist',
        filename: 'app.bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader'
            }
        ]
    },
        plugins: [
            // 压缩代码
            new webpack.optimize.UglifyJsPlugin({
                compress: {
                    warnings: false
                }
            }),
            // 删除重复的依赖
            new webpack.optimize.DedupePlugin()
    ]
};

再使用webpack试试看

加入Vue

使用vue需要vue-loader这个依赖,修改package.json,这些依赖是必须要的must

{
  "name": "webpack-demo",
  "version": "0.0.1",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^1.14.0",
    "babel-core": "^6.18.2",
    "babel-loader": "^6.2.8",
    "babel-plugin-transform-runtime": "^6.15.0",
    "babel-polyfill": "^6.16.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-stage-2": "^6.18.0",
    "css-loader": "^0.25.0",
    "file-loader": "^0.9.0",
    "style-loader": "^0.13.0",
    "url-loader": "^0.5.7",
    "vue-html-loader": "^1.0.0",
    "vue-loader": "^8.0.0",
    "vue-style-loader": "^1.0.0",
    "vue-template-compiler": "^2.1.0"
  },
  "dependencies": {
    "vue": "2.*"
  }
}

webpack.config.js

var webpack = require("webpack");

module.exports = {
    entry: './src/main.js',
    output: {
        path: './dist',
        publicPath: 'dist/',
        filename: 'app.bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader'
            },
                        {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
                loader: 'file-loader?name=font/[hash:8].[ext]'
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?\S*)?$/,
                loader: 'file-loader?name=image/[hash:8].[ext]'
            }
        ]
    },
    vue: {
        loaders: {
            js: 'babel?cacheDirectory=true'
        }

    },
    resolve: {
        alias: {
            vue: 'vue/dist/vue.js'
        }
    },
    plugins: [
        // 压缩代码
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        }),
        // 删除重复的依赖
        new webpack.optimize.DedupePlugin()
    ]
};

执行webpack,为了查看具体效果,创建一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app"></div>

<script src="dist/app.bundle.js"></script>
</body>
</html>

浏览index.html查看具体效果吧

DMOE6


一个悲痛的消息!webpack1(就是上面的)被放弃了,现在官方推行webpack2(有一些配置的不同),据说可以减少打包文件的大小,具体还要看实际效果。

官方文档

更全面的DEMO

linux学习之管道符与管道命令

目录:
1、管道符
2、管道命令
3、管道
4、重定向、多命令顺序执行、tee命令、xargs命令

一、管道符
linux中,“|”就是管道符,也叫管道命令操作符,简称管道符。就是将前面一个命令正确标准输出信息,作为下一个命令的标准输入。
其实就是第一个命令的正确标准输出是第二个命令的标准输入,第二个命令的正确标准输出是第三个命令的标准输入,以此类推。
如下图所示:

二、管道命令
http://www.cnblogs.com/lemonbar/archive/2014/08/24/3933390.html
1、grep 分析一行信息,如果匹配,就将该行取出。
(1)参数
-a : 将binary文件以 text文件的方式搜寻数据
-c : 计算找到搜索条件的次数
-i : 忽略大小查找
-n : 输出行号
-v : 去除包含 -v后面条件的那一行
--color = auto : 将查找的关键字加上颜色
(2)例子
grep 'function' *|grep -v 'private'|grep -v '__construct'|grep -v 'checkAuth'|grep -v 'apiLog'|grep -v 'getVerifField'|awk '{print $4}'|less
以上列子等价于
grep ‘funciton’ *|grep -vE 'private|__construct|checkAuth|apiLog|getVerifField'|awk '{print $4}'|less
-E 启用扩展选项
2、awk 一个强大的文本分析工具
(1)参数
-F:指定分隔符。注意一定要大写。
(2)例子
在api_base.txt 中不区分大小写查找包含‘’coupons‘’ 的行,根据空格分隔,并取第一列
grep -i 'coupons' api_base.txt|awk '{print $1}'|less
在api_base.txt 中不区分大小写查找包含‘’coupons‘’ 的行,根据‘’.php‘’分隔,并取第一列
grep -i 'coupons' api_base.txt|awk -F '.php' '{print $1}'|less
2、cut 选取命令。将一段数据经过分析,取出我们想要的。一般选取信息都是针对行来说的。
(1)参数
-d : 后面接分隔符,与-f 一起使用
-f : 将-d取出的数据截取其中的几段。
-c : 以字符为单位取出固定字符区间
(2)例子
截取3-5字节的数据
grep 'coupons' api_base.txt|cut -b 3-5
以:作为分隔符截取第一段
grep 'coupons' api_base.txt|cut -d ':' -f 1
3、sort 排序。可以根据不同类型排序。
(1)参数
-f : 忽略大小。其实是将小写字母转换成大写字母来比较,相当于忽略大小
-b : 忽略最前面的空格符
-M : 以月份的名字来排序
-n : 数字排序
-r : 反向排序
-u : 去重
-t : 分隔符,默认Tab
-k : 与t相关,用分割的哪一段排序
(2)例子
升序排序
[root@shiyong api]# grep 'coupons' api_base.txt|sort
Mkt_coupons.php: function getByUser()
Mkt_coupons.php: function getCouponList()
Mkt_coupons.php: function subUserCouponsChange()
Mkt_coupons.php: function syncCoupons()

降序排序
[root@shiyong api]# grep 'coupons' api_base.txt|sort -r
Mkt_coupons.php: function syncCoupons()
Mkt_coupons.php: function subUserCouponsChange()
Mkt_coupons.php: function getCouponList()
Mkt_coupons.php: function getByUser()

4、unique 去重。重复行只显示一行。
(1)参数
-i : 忽略大小
-c : 计算次数
(2)例子
去重
[root@shiyong api]# grep 'function' api_users.txt|uniq
Shop_user.php: function updateByUserId()
shop_user.php: function updateByUserId()
Vuser.php: function create()
vuser.php: function create()
去重不区分大小写
[root@shiyong api]# grep 'function' api_users.txt|uniq -i
Shop_user.php: function updateByUserId()
Vuser.php: function create()

去重并且计算次数
[root@shiyong api]# grep 'function' api_users.txt|uniq -c
4 Shop_user.php: function updateByUserId()
1 shop_user.php: function updateByUserId()
4 Vuser.php: function create()
1 vuser.php: function create()

去重不区分大小写并且计算次数
[root@shiyong api]# grep 'function' api_users.txt|uniq -c -i
5 Shop_user.php: function updateByUserId()
5 Vuser.php: function create()
5、wc 计算输出信息的整体数据
(1)参数
-l : 统计行数
-w : 统计字数
-m : 统计字符数
(2)例子
grep 'function' api_users.txt|wc
10 30 360
行数 单词数 总字符数
grep 'function' api_users.txt|wc -l
10
6、tee 双重定向。 既输出到屏幕,又可以输出到文件
(1)参数
-a : 追加
(2)例子
[root@shiyong api]# grep 'coupons' api_base.txt|tee test2.txt
Mkt_coupons.php: function syncCoupons()
Mkt_coupons.php: function subUserCouponsChange()
Mkt_coupons.php: function getCouponList()
Mkt_coupons.php: function getByUser()
7、tr 删除或者替换某一段字符串
(1)参数
-d : 删除
-s : 替换
(2)例子
[root@shiyong api]# grep 'coupons' api_base.txt|tr -d 'getByUser'
Mk_coupon.php: funcion ncCoupon()
Mk_coupon.php: funcion ubCouponChan()
Mk_coupon.php: funcion CouponLi()
Mk_coupon.php: funcion ()
[root@shiyong api]# grep 'coupons' api_base.txt|tr -s 'ct' '1111'
Mk1_1oupons.php: fun1ion syn1Coupons()
Mk1_1oupons.php: fun1ion subUserCouponsChange()
Mk1_1oupons.php: fun1ion ge1CouponLis1()
Mk1_1oupons.php: fun1ion ge1ByUser()

这边命令有很多,就不一一来说明了。除了这些,还可以使用col,join,paste,expand 等等。
三、管道
linux中的管道描述的是进程之间是如何通信的,管道可以分为无名管道和命名管道。我平时用的“|”管道符就是无名管道。

四、延伸知识点
1、重定向
linux重定向 与 文件描述符(FD)有关。shell中FD通常有10个,0~9;
常用的FD有3个:
0:标准输入(stdin);
1:标准输出(stdout);
2:标准错误输出(stderr);
对于3~9,则分给了正在打开的额外文件。
>:输出重定向到文件,并且覆盖。
>>:以追加的方式输入到文件
<:输入重定向到一个程序
2 >& 1 : 标准错误输出重定向到标准输出
重定向与管道符的区别:
1、管道符左右两边都是命令,且左边命令应该有标准输出,右边命令需要可以接收左边命令的标准输出。
2、重定向左边是命令,右边只能是文件或者设备。
2、多命令顺序执行
; :分号。命令1;命令2; 命令之间无联系
&&:逻辑与。命令1&&命令2
|| :逻辑或。命令1 || 命令2
多命令顺序执行与管道符的区别:
1、管道符两边命令是有联系的;
2、多命令执行两边的命令是无联系的;
[root@shiyong api]# grep 'cartDataGet' api_base.txt;ll
Buy.php: function cartDataGet()
总计 140
-rw-r--r-- 1 root root 31995 01-19 13:31 api_base.txt
-rw-r--r-- 1 root root 70781 01-19 13:31 apidoc.txt
-rw-r--r-- 1 root root 2461 01-19 13:31 api_ins.txt
-rw-r--r-- 1 root root 22 01-19 13:31 api_polymerize.txt
-rw-r--r-- 1 root root 778 01-19 13:31 api_trades.txt
-rw-r--r-- 1 root root 366 01-19 15:03 api_users.txt
-rw-r--r-- 1 root root 1096 01-19 13:31 api_weixin.txt
-rw-r--r-- 1 root root 169 01-19 14:33 test1.txt
-rw-r--r-- 1 root root 169 01-19 14:50 test2.txt
-rw-r--r-- 1 root root 169 01-19 14:33 test.txt
[root@shiyong api]# cp test.txt test1.txt && echo 'success'
success

3、tee命令
tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。
就是既会输出到屏幕,也会输出到指定的文件。
4、xargs命令
(1)将标准输入作为命令的参数。
(2)通过管道传递给xargs的输入将会包含换行和空白,不过通过xargs的处理,换行和空白将被空格取代。
(3)一般与find一起使用。
find命令把匹配到的文件传递给xargs命令,而xargs命令每次只获取一部分文件而不是全部,不像-exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。
find . -name "api_base.txt"|xargs grep 'coupons'|less

数据库的长连接和短链接

什么是长连接和短链接?

顾名思义,长连接相对短链接,长时间保持客户端和服务端的链接状态
短连接: 连接-》数据传输-》关闭连接;
长连接:连接-》数据传输-》保持连接-》数据传输-》保持连接-》…………-》关闭连接;
当然由于php没有连接池的概念,长连接也是有个时间限度的,对应的是数据库中的wait_timeout(默认是8小时),在一段时间没有操作数据库后,数据库会断掉这个连接。
PS:如何保持类似的永久连接呢,可以通过类似于socket通信中的心跳机制,不断的发送心跳包,保持和数据的连接。(因为数据库的timeout计时会在每次数据库访问重新开始计时)

分别在什么情况下使用?

1.长连接较短链接并没有什么特殊功能,唯一的是效率
当webserver连接sql(overhead高的原因有很多,比如数据库种类,数据库服务,服务器负载。。)的耗费较高时候,持久连接因为没有重新建立连接所以效率会更高。
2.有利也有弊,由于数据库连接数是有限的,比如数据库连接设置为100个,而apache访问最大数为200个时,当超过100个请求所有的请求都取访问数据库,如果使用长连接,之前的100个没有被释放掉的话,接下来的100个都会无法连接数据库,直到之前的数据库连接被复用或者释放,所以当并发访问量很大的时候就要考虑下是否采用长连接的方式了(前100个由于长连接可能是被占用状态,也可能是和后100个所请求的数据库不一致,导致无法被复用)

php的几个运行模式下的长连接

1.cgi,php用作单独的语言解释器,php 会为向 web 服务器提出的每个 PHP 页面请求生成并结束一个 PHP解释器线程。由于该线程会随每个请求的结束而结束,因此任何在这个线程中利用的任何资源(例如指向SQL 数据库服务器的连接)都会随线程的结束而关闭。在这种情况下,使用持久连接和非持久连接没有任何区别——因为PHP脚本本身的执行不是持久的。
2.通常(我们系统),php用作web服务的一个模块,apache会加载php模块,一个请求过来,通常有父子两个进程协调处理的,子进程通常用作处理web页面的生成,父进程通常用于数据的写入,而数据库连接是建立在父进程里的,所以当子进程关闭的时候,并不会影响长连接的重用
3.与cgi类似,无法使用长连接

具体使用,测试

mysql_connect 短链接
mysql_pconnect 长连接
1.对于一些常驻进程,为什么不能使用this->load->model的方式去访问数据库
第一次连接数据库是可以访问的,但由于这个进程长时间没有访问数据库,超过了数据库设置的wait_timeout,数据库自动断开了此连接,下次常驻进程监听到消息,再去操作数据库的时候,就会报错。

解决方案:
可以通过自己手动关闭数据库连接,从而下次监听消息再次主动连接数据库
采用pconnect方式,并定时向数据库发送数据,保持数据库永久连接

2.测试后,mysql数据库连接的消耗并不是很大,所以绝大部分情况可以不使用长连接,避免产生数据库连接过多,因为当数据库连接快满的时候,产生新的连接的消耗,会明显增加。

bigpipe技术初探

情景模拟:
当网页越来越大,相应的请求数据,css,js等也越来越多,页面的加载速度就变的越来越慢慢,通过研究数据表明,用户能够容忍的大概为2.5s,如果达到3到4s还是白页的话,那么用户基本也就失去耐心。

目前我们的处理方法基本基于ajax来减少加载时间。

页面完整加载流程如下:
1) 用户访问网页,浏览器发送一个HTTP 请求到网络服务器
2) 服务器解析这个请求,然后从存储层去数据,接着生成一个html 文件内容,并在一个HTTP Response 中把它传送给客户端
3) HTTP response 在网络中传输
4) 浏览器解析这个Response ,创建一个DOM 树,然后下载所需的CSS 和JS文件
5) 下载完CSS 文件后,浏览器解析他们并且应用在相应的内容上
6) 下载完JS 后,浏览器解析和执行他们

可以看出,这些都是按照严格的顺序来执行的,当其中有一个操作没有执行结束,后面的操作就不会之执行,即操作之间不能重叠。这样就易造成性能的瓶颈。(这样就造成性能的瓶颈:服务器生成一个页面的内容时,浏览器是空闲的,显示空白内容;而当浏览器加载渲染页面内容时,服务器又是空闲的,时间与性能的浪费由此产生。)

解决方案:

bigpipe 是有Facebook发明的一种页面加载技术,即一种减少http请求的,首屏快速加载的异步加载页面方案;其核心思想是并行,服务器生成的数据和浏览器渲染数据的并行。

php实现原理:利用ob_flush()与flush()将缓冲区的内容提前输出,浏览器可提早加载这部分的内容,无需等待所有输出完成再加载。将页面内容划分为一个个小块,输出一个后在输出下一个,使用户尽早看到页面内容,优化用户体验。

详细内容:
bigPipe提出分块的概念,即根据页面内容位置的不同,将整个页面分成不同的块儿– 称为pagelet。
将众多pagelet加载的不同阶段像流水线一样在浏览器和服务器上执行,这样就做到了浏览器和服务器的并行化,从而达到重叠服务器端运行时间和浏览器端运行时间的目的。使用BigPipe 不仅可以节省时间,使加载的时间缩短,而且可以同过pagelet的分步输出,使一部分的页面内容更快的输出,从而获得更好的用户体验。BigPipe 中,用户提出页面访问请求后,页面的完整加载流程如下:

1) Request parsing:服务器解析和检查http request
2)Datafetching:服务器从存储层获取数据
3) Markup generation:服务器生成html 标记
4) Network transport : 网络传输response
5) CSS downloading:浏览器下载CSS
6) DOM tree construction and CSS styling:浏览器生成DOM 树,并且使用CSS
7) JavaScript downloading: 浏览器下载页面引用的JS 文件
8) JavaScript execution: 浏览器执行页面JS代码

初略看一下也许会好奇这个和刚开始说的那个没什么区别,但这8个流程属于一个pagelet,而多个pagelet 的不同操作阶段就可以像流水线一样进行执行了。

与ajax比较

1) AJAX 的核心是XMLHttpRequest,客户端需要异步的向服务器端发送请求,然后将传送过来的内容动态添加到网页上。如此实现存在一些缺陷,即发送往返请求需要耗费时间,而BigPipe 技术使浏览器并不需要发送XMLHttpRequest 请求,这样就节省时间损耗。

2) 使用AJAX时,浏览器和服务器的工作顺序执行。服务器必须等待浏览器的请求,这样就会造成服务器的空闲。浏览器工作时,服务器在等待,而服务器工作时,浏览器在等待,这也是一种性能的浪费。使用BigPipe,浏览器和服务器可以并行同时工作,服务器不需要等待浏览器的请求,而是一直处于加载页面内容的工作阶段,这就会使效率得到更大的提高。
3) 减少浏览器发送到请求。对一个5亿用户的网站来说,减少了使用AJAX额外带来的请求,会减少服务器的负载,同样会带来很大的性能提升。

缺点
不利于seo,针对爬虫需要通过ua判断,爬虫还是用传统模式,用户就用bigpipe

注意事项:
1.如果使用nginx作为web服务器,那么nginx可能会缓冲php的输出。即便是调用了flush方法,相应内容也会被nginx缓冲,而不会输出到浏览器。
2.某些浏览器也会有缓冲,如在接收的数据小于一定值的时候,不会对代码进行渲染。

补充:
php文件最终在浏览器上显示,走过3个缓冲阶段
即:php buffer=》web server buffer=》浏览器buffer

1)php buffer
php运行的结果先放入缓冲区(buffer),只有当缓冲区满了或者php运行完毕,才将数据输出去。
所以这边通过ob_flush将缓冲区的内容强制输出。

2)web server buffer
目前我们接触的也就apache和nginx
apache buffer
当php的数据给apache服务器时,也会想放入缓冲区(当缓冲区数据满或者执行完毕时,才执行数据)
这边我们可以在php层使用flush()来强制将缓冲区数据输出
(旧版本Apache可能需要关闭GZip。)

nginx buffer
nginx使用fastcgi缓冲区来缓存数据,fastcgi是强制将buffer打开的,无法关闭。
应对方法:通过apache+php实现bigpipe,nginx放在代理服务器的角色上,并使用proxy_buffering关闭代理缓存

3)浏览器buffer
这里研究得不深,不过这里指的不是讲图片,样式,头信息和js等缓存,而是和上面一样,将内容想放入缓存区中,当请求完毕和缓存写满后在输出。
这一层清楚方法未知,不过影响不是很大,一般主流的不超过4k。

实现代码
见附件

直接将txt换成php运行即可、
aaa

这边只是用php简单的实现其大概思想,但这样并不支持多线程,但对于小网站来说这样串行加载已经可以达到加载要求了。

对于大型网站,那么就需要考虑多线程执行这些pagelet。由于php不支持线程,所以可以使用的方式大致有curl,stream_select和php5新增的stream_socket_client函数或者用PHP 的扩展模块paaactnl模块中的pcntl_fork()函数来生成子进程。

——————————————————————————————

相关参考

http://www.searchtb.com/2011/04/an-introduction-to-bigpipe.html(bigpipe详细介绍以及相关bigpipe参考链接)
http://taobaofed.org/blog/2016/03/25/seller-bigpipe-coding/(bigpipe编程实践)
http://blog.sina.com.cn/s/blog_4dd475390102ebe6.html(bigpipe注意事项)
http://www.bo56.com/%E4%BB%8Ephp%E7%9A%84%E7%BC%93%E5%86%B2%E5%8C%BA%E8%AF%B4%E8%B5%B7/(从php缓存去说起)

开发过程的一些问题总结

一、大家都讨厌的deadline

要求:
1、进度安排
xx-xx号 完成xxx
2、结果:可展示的内容
后台是这样的,接口我写好了。

其实是个更简单的想法:
给自己要求2天完成,可能最后2天半才完成。
如果没定计划的话,可能需要3天。

TIP:你可能会觉得有和没有并没有区别,我觉得是给自己一种压力。

二、表达能力问题

暂时没找到解决方法

TIP: 理清自己的思路,就更容易清楚表达。
可以祭出流程图(龙哥带的,是会被影响的)
栗子:上次保险的流程逻辑

也可以在表达之前用纸理下思路。 龙哥,传哥都在用。
(15块钱一本500张)

三、反馈

如果可以,最好告诉别人结果:我处理完了。
而不是因为xxx,还没搞定。

四、看似莫名其妙的问题,怎么办?

1、自己搞不定,可以询问下其他人。
比如上周:js注入微博的一个链接。
到这周一才解决。

2、建议:追求一个结果。
什么结果?就是这个事情解决完了。

3、栗子
很久之前,sphinx,api不能用,因为版本问题。
常见的例子:测试上是好的,正式机上就不行。

4、不想去解决?
解决是一次进步。

五、项目方面

1、保证和需求方想法一致
实现方案有多种,要的是哪种?
有的时候接的需求,并不完成清楚,就去多问。
当然自己也要想清楚,不耽误他们的时间。

2、完成比完美重要
开发时:怎么方便,怎么来。
开发后:

3、开发后期,先完成的模块可以先上。

4、维护阶段
不能一次性解决,每次就优化一点。
比如上次吐槽apidoc不好用,可以想下其他方法。

30分钟,快速在项目中使用git


编   写:袁 亮
时   间:2017-01-17
说   明:30分钟,快速在项目中使用git

一、账号环境
    1、https://gitlab.oneitfarm.com
        使用自己的企业邮箱注册自己的账号
    2、安装windows环境
        https://git-scm.com/downloads/guis
        \\192.168.0.18\运维网络硬盘\y袁亮\software\Git-2.8.1-64-bit.exe
        cmd下运行 git 配置 
            git config --global user.email ""
            git config --global user.name ""
    3、生成sshkey (https,安全证书,一台电脑一个)
        任意目录,右键,打开 Git GUI
        help => show sshkey => Generate Key
    4、在gitlab添加ssk key
        https://gitlab.oneitfarm.com/profile/keys
    
二、加入开发组 
    1、申请加入开发者 https://gitlab.oneitfarm.com/geek
    2、审核通过,并分配相应权限
    
三、导出项目代码
    1、本地无代码,从远程拉取【必须会】
        1.1 注意事项 
            使用https协议的,ssh的服务器没有开启
            所有跟远程代码库交互,都需要输入git账号和密码
            windows下,空项目一直clone不下来
        1.2 linux
            git clone https://gitlab.oneitfarm.com/geek/dealer.git yl
        1.3 windows
            空目录,右键Git Gui
            Clone Existing Respository
            ps:需要输3次账号密码,不知道是什么鬼
    2、本地有代码,关联到远程一个空代码库【了解即可】
        2.1 linux
            a. 创建本地仓库 git init
            b. git remote add 
        2.2 windows
            a. 创建本地仓库 Git Gui => Respository => New
            b. 将本地代码,提交到本地仓库 stage change => commit
            c. 关联远程代码仓库 Remote => Add
                origin
                https://gitlab.oneitfarm.com/geek/dealer.git
            d. 提交到远程代码仓库
                push 
    3、本地有代码,关联到远程一个非空代码库
        略
    
四、常规操作 【必须会】
    1、本地仓库 查看当前git状态
        1.1 git status (svn st)
        1.2 可以通过配置alish,使用git st    
    2、本地仓库 新增文件
        2.1 新增 
            git add application/controllers/Store.php
        2.2 提交到本地仓库
            git commit -m '增加store控制器' application/controllers/Store.php
    3、本地仓库 修改文件
        3.1 查看文件改动
            git diff webroot/index.php
        3.2 不想修改,恢复文件
            git checkout webroot/index.php
        3.3 文件改动存入缓存区
            git add webroot/index.php
        3.4 改错,不想保持,取消修改
            git reset webroot/index.php
            git checkout webroot/index.php
        3.4 提交修改
            git commit -m 'index.php代码' webroot/index.php
    4、本地仓库 删除文件
        4.1 本地仓库删除 
            git rm debug.php
        4.2 发现删了,撤销
            git reset HEAD debug.php
            git checkout debug.php
        4.3 确认删除,提交
            git commit -m '删除调试代码' debug.php
        4.3 提交之后发现删错了
            a. 查看 log
                git log 
            b. 找到删除前的版本,并回滚到那个版本
                git reset --hard {commit_id}
                将被删除的文件备份出来
            c. 查看所有的版本log,并找到要恢复的版本
                git reflog
            d. 恢复到最新版本
                 git reset --hard {commit_id}
            e.将备份出来的代码还原回去
                cp或者mv回来,然后add,commit
    5、查看文件版本记录
        git log webroot/index.php
    6、提交代码到远程仓库
        git push -u origin master
    7、从远程代码库拉取最新代码
        git pull 
    8、常用设置
        8.1 pull push 免去每次输账号密码
            git config --global credential.helper store
        8.2 颜色设置
            git config --global color.ui true
        8.3 常用alias
            git config --global alias.st 'status'
            git config --global alias.ci 'commit'
            git config --global alias.co 'checkout'
            git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
    9、忽略文件(日志,配置等不想要提交的文件)
        在项目文件下新建.gitignore文件
        文件中写入 /.idea/*  即忽略.idea文件 (!/.idea/echo.php 即不忽略.idea文件夹下的echo.php文件)
        *.txt 忽略txt类型的文件
        最后别忘了提交.gitignore文件
            
五、分支操作
    1、查看本地仓库分支及当前所在分支
        git branch 
    2、新建分支
        2.1 正常创建
            git branch dev
            git checkout dev
        2.2 命令合并
            git checkout -b dev
        2.3 从远程仓库创建分支
            a. 确保远程仓库有这个分支
            b. 更新本地仓库,确保本地知道远程仓库的分支 
                git remote update
                git fetch
                git remote show origin :查看远程分支
            c. 从远程分支中checkout一个分支到本地
                git checkout -b yl origin/yl
                ps:本地分支名可以起的跟远程分支名不一样,但不建议这么做,除非本地多个分支对应远程同一个分支
            d. 分支改动push到对应远程分支
                git push origin yl
    3、切换分支
        git checkout master
    4、删除分支
        git branch -d dev
        ps:如果删除未合并的分支,需要用-D参数,慎用,除非是写临时脚本的分支
    5、合并分支
        5.1 git merge dev 
        5.2 该命令是将dev分支合并到当前所在分支,因此需要先checkout到你所想要合并到的分支
            git checkout master
        5.3 冲突解决
            a. 手动修改冲突文件,然后提交
            b. 借助 mergetool (还没用过)
        5.3 查看分支合并情况
            git log --graph --pretty=oneline --abbrev-commit
    6、给绑定分支绑定远程分支
        git branch --set-upstream demo origin/yl
    7、拉取合并指定指定远程仓库的指定分支
        git pull  
        git pull origin yl

六、GitLab Flow 工作流
    1、参考文档
        http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
    2、我们采用的方式
        Gitlab flow
        版本发布
    
七、远程仓库使用
    1、添加远程仓库
        git remote add  
        shortname默认是origin
    2、查看远程仓库信息
        git remote :查看shortname
        git remote -v :查看完整信息
    3、拉取远程仓库的信息
        git fetch  :也可以使用,默认是origin
        ps:fetch只会拉取数据,但并不合并,pull是fetch之后会自动合并git merge
    4、推送到远程仓库
        4.1 git push origin master
        4.2 将本地master分支推送到origin远程仓库中对应的分支
        4.3 如果origin仓库里对应的分支已经有人改过,必须先pull下来,然后才能推送
        4.4 master分支推送到origin仓库的具体哪个分支,是由之前创建分支的时候决定的,并不是根据分支名来
            如果两个名字不一样,很容易出现一些看起来莫名其妙的问题,所以不要干这个事
    5、查看远程仓库
        git remote show [remote-name] 

八、标签tag (对应之前的release)
    1、本地仓库 查看所有标签
        1.1 git tag
        1.1 git tag -l '匹配规则,可以写正则'
        1.2 以标签名排序,不以创建时间
    2、本地仓库 创建标签
        2.1 git tag -a v1.0.0 -m '标签注释'
        2.2 不输入-m,在运行编辑器里可以更方便的写各种备注
        2.3 不允许打轻量标签,除非是本地测试用
    3、本地仓库 查看标签信息
        git show v.1.0.0
    4、将标签推送到远程仓库
        4.1 git push origin [tagname]
        4.2 多个标签,也单个单个推,不允许一次性把所有标签推上去
    5、从标签中检出新的分支
        5.1 git checkout -b fix-v2.0.0 v2.0.0
        5.2 类似分支的创建,如果本地没有v2.0.0这个标签,需要先从远程仓库pull或者fetch下来
    6、修复线上tag两种办法
        
        
参考文档:
    1、Git教程
        http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
    2、Git 工作流程
        http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
    3、Git分支管理策略
        http://www.ruanyifeng.com/blog/2012/07/git.html
    4、Git 使用规范流程
        http://www.ruanyifeng.com/blog/2015/08/git-use-process.html
    5、git 文档
        https://git-scm.com/book/zh/v2/
    6、Git可视化极简易教程 — Git GUI使用方法 
        http://www.runoob.com/w3cnote/git-gui-window.html
    7、GitLab Flow的使用
        https://www.15yan.com/story/6yueHxcgD9Z/
        https://about.gitlab.com/2014/09/29/gitlab-flow/
    8、如何使用gitlab的flow以及代码review
        http://mojito515.github.io/blog/2016/03/09/ru-he-shi-yong-gitlabde-flowyi-ji-dai-ma-review/
    9、The 11 Rules of GitLab Flow
        https://about.gitlab.com/2016/07/27/the-11-rules-of-gitlab-flow/
    10、使用git、git-flow与gitlab工作
        http://blog.2baxb.me/archives/736
    11、GitLab的Pull Request工作流
        http://www.jianshu.com/p/6bcd082101c1
    12、常用 Git 命令清单
        http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html
        
        

深入理解CI框架:数据库的加载过程


说明:没特别指明的情况下,本文涉及的Codeigniter均为3.1.2版。


要在 CI 框架里使用数据库,你可以显示地加载它:

$this->load->database(db_to_load);

也可以在加载 model 的时候顺带加载数据库:

// 第三个参数
$this->load->model(model_to_load, name, db_to_load);

当我们只有一个数据库的时候,上面两种方法都行得通。默认情况下,CI 把数据库连接赋值给一个叫 db 的属性。但如果有多个数据库(>=2),并且要经常切换,就只能使用 database() 并且第二个参数传 true,例如:

$this->dblink = $this->load->database(db_to_load, true);//return ?

我们看一下 database() 方法的部分实现细节:

// database() 方法开头会判断 db 是否已被赋值
if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
{
    return FALSE;
}
...
// 返回数据库连接
if ($return === TRUE)
{
    return DB($params, $query_builder);
}
...
// 赋值给 db
$CI->db =& DB($params, $query_builder);

看得出来,每次执行 database(db_to_load, true) 的时候都会连接一次数据库,所以在必要时才 load model。

注:1. 上面指的是调用 database(db_to_load, true);不是 database(db_to_load);
2. 读者事先看过 DB() 方法,知道 DB() 会去连数据库 o( ̄▽ ̄)o 。

下面是 DB() 方法的部分实现细节:

...
//此处有很多 include 和 require_once
...
// $params['dbdriver'] 在配置数据库时指定,比如 mysql
$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
$DB = new $driver($params);
...
// $driver 继承的某个抽象类有 initialize() 方法
$DB->initialize();

下面是 initialize() 方法的部分实现细节:

//这是 CI_DB_driver 类
if ($this->conn_id)
{
    return TRUE;
}
// db_connect 由继承该类的派生类实现
$this->conn_id = $this->db_connect($this->pconnect);
...
// 最后设置mysql(客户端)编码
return $this->db_set_charset($this->char_set);

如果你再深入源码了解 CI_DB_driver 类,你会发现下面的关系链:

$driver extends CI_DB { } // drivers 举例: CI_DB_mysql_driver

class CI_DB extends CI_DB_query_builder { } // CI_DB 是个空类

// or: class CI_DB extends CI_DB_driver { }

abstract class CI_DB_query_builder extends CI_DB_driver { }

abstract class CI_DB_driver { }

上面出现了两个抽象类,它们一个偏向于 Query,一个偏向于 DB,下面是两个类的注释:

CI_DB_query_builder : This is the platform-independent base Query Builder implementation class.
CI_DB_driver: This is the platform-independent base DB implementation class.

CI_DB 是个空类,没有任何实现,但是所有数据库驱动类(例如 CI_DB_mysql_driver)都继承 CI_DB
注意到 &DB(params, query_builder_override) 方法的第二个参数,它决定了 CI_DB 继承 CI_DB_query_builder 还是 CI_DB_driver :

if (! isset($query_builder) OR $query_builder === TRUE) 
{
    if ( ! class_exists('CI_DB', FALSE))
    {
        class CI_DB extends CI_DB_query_builder { }
    }
} 
elseif ( ! class_exists('CI_DB', FALSE))
{
    class CI_DB extends CI_DB_driver { }
}

可以看出来,CI_DB 起到了“适配器”的作用。

完。