调用链跟踪系统-介绍

背景资料

Dapper,大规模分布式系统的跟踪系统

Zipkin

如果要查看zipkin资料,官网的英文文档是最正确的,讲解也很系统;

在阅读文章前,建议先至少看一下Dapper的翻译;

一、介绍

调用链跟踪系统进行了比较大的升级,主要改变有:

  1. 对接zipkin日志标准(仍保留了些许不同);
  2. 使用zipkin的数据库和界面;
  3. 大幅降低对正常业务流程的性能影响;
  4. 将数据压缩后再传递给收集器;
  5. rabbitmq使用单独的数据通道,不再和业务共用一个;
  6. 采用文件日志和mysql记录结合的方式,分别记录调用链的业务数据和性能数据。 业务数据100%记录,性能数据采样记录(5%);
  7. 支持业务数据的上下文透明传输

下面详细讲解系统的构成、设计考量、使用和特点。

二、结构

分布式跟踪系统整体结构

浅绿色的是数据最终落地的地方。

名词解释:临时存储

临时存储究竟是干嘛的呢?在zipkin里,一个Span被分割成两部分,一个是Client/Producer,另一个是Server/Consumer。Span的含义更接近于一次RPC,它横跨了两个服务节点,所以在两个节点分别上报信息,最后再合并成一个Span。

后端收到两部分Span的时间不一样,临时存储就用来存储先到达的Span,等另一半到达后再取出来做合并。

名词解释:收集器

从MQ接收上报的Span,做Span的合并,并存储到持久存储(mysql)

名词解释:压缩

压缩成二进制,再通过MQ传递。实际应用中,是先将Span转换为数组,将key替换成a-z的单字母,再转成json后使用zlib压缩(需要zlib扩展)。

和压缩相关的还有一个叫 msgpack 的东西,但是它需要单独安装PHP扩展,所以放弃了。

三、设计考量

考量一:降低对业务的影响

只在少数地方嵌入跟踪代码。实际操作中,在项目入口(index.php)、rpc请求、输出这三个地方做了埋点。

耗时操作在请求以外执行。我们知道PHP没有异步,但是PHP有个非常有用的函数register_shutdown_function,我们把耗时操作全放里面,这样可以尽量减少对线上业务的影响。如果是fastcgi模式,我们会优先使用fastcgi_finish_request这个函数。http://php.net/manual/zh/function.register-shutdown-function.php#108212

考量二:开关/采样率/消息生产速率控制

一共设计了两个开关(业务日志开关、Span上报开关),默认情况下这两个开关都是开启的。在此基础上,增加了采样率控制,仅当Span上报开关开启,且当前Span需要采样时,Span才会通过MQ传递给后端。业务日志开关控制biz-ztrace日志的记录。

需要注意的时,即便把开关全部关闭,traceid也会生成、传递,但不会有日志记录,也不会有Span上报。

对于消息速率控制,在实际中采用的是“过量丢弃”,阈值是10000。只要队列里的消息超过这个值,即不再接收消息,直接抛弃。

考量三:动态增加消费能力

随着系统扩大,采集的数据增多,动态增强数据的消费能力非常重要。目前的做法是通过加开消费者来实现。之所以能直接增加消费者,是因为它们使用的同一个临时存储,我们只是把消息的消费瓶颈,转移到了临时存储的IO瓶颈上。

想象一下,我们有两个消费者A、B,分别使用两个不同的临时存储,Client端的Span被A接收到,Server端的Span被B接收到,由于它们的临时存储隔开的,A、B都无法做Span的合并!

几乎所有的调用链跟踪系统实现方案,都绕不开这个问题,可能是一个Span被分发到不同处理实例,也可能是同一Trace的不同Span被分发到不同处理实例——这都会带来很多麻烦。

解决方法也很多,比如根据traceid做hash,再转发给不同的后端实例,这样同一个trace下的Span都会被,且只会被转发给同一处理实例。

但是,仅仅做hash是不够的,还必须借助“一致性Hash”算法。下面的例子解释了原因。

假设已经有一个处理实例CA,spanA、SpanB都属于同一个trace。CA已经接收到spanA了,这时我们新增了一个CB(和CA使用不同临时存储),如果hash结果表明spanB应该转发给CB(这完全可能发生),那又会出现CA、CB都无法合并这个Span的情况。

关于一致性Hash,我们后面再讲。

考量四:业务日志、性能日志分开

之所以要分开,是因为业务日志很多(随着采集端的增加,数据增长更多),我们还没处理过这么多的数据。

目前,业务日志记录了$_GET、$_POST、$_SERVER和apiReturn,收集端有seller和api。每天产生的日志共2.5G左右,每月日志总量大概在25G。日志总量和采集端数量呈正相关。

四、使用

下面的例子都有判断$trace_open;,并且使用了 try {} catch () {},这样做是为了减少可能出现的异常,必须要添加。

记录业务日志

业务日志是记录在文件里的,用的最多。

过期数据处理

我们把太久远的数据称为“过期数据”,因为它们被再次使用的概率很低。我们用的最多的是近期的数据,近期数据才是最重要的,业务日志和性能统计数据都适用这个道理。

我们处理过期数据的方式很简单——打包压缩、删除,这是目前能找到的最划算的处理方法。具体处理逻辑:

文件日志:保留一个月的压缩日志,保留一周内的原始日志。
数据库:保留两个月的数据。

global $trace_open;
if ($trace_open) {
    try {
        $tracer = \Tricolor\ZTracker\Core\GlobalTracer::tracer();
        $tracer->log('get', $_GET);
    } catch (\Exception $e) {}
}

记录跟踪日志

跟踪日志会出现在ui里(如果被采样了的话)

global $trace_open;
if ($trace_open) {
    try {
        $tracer = \Tricolor\ZTracker\Core\GlobalTracer::tracer();
        $tracer->currentSpan()->putTag('store_id', (int)$_POST['store_id']);
    } catch (\Exception $e) {}
}

透明传输业务字段(试验性质)

在调用链上游设置一个值,在调用链下游可以获取到。

之所以说是试验性质,是因为设计里有,也有实现,但实际业务里还没使用过。

global $trace_open;
if ($trace_open) {
    try {
        $tracer = \Tricolor\ZTracker\Core\GlobalTracer::tracer();
        $tracer->currentContext()->set('from', 'mamagou');
        // get
        $tracer->currentContext()->get('from');
    } catch (\Exception $e) {}
}

五、特点

  1. 和zipkin的Span比起来,我们的Span结构略有不同。zipkin里的Sampled字段,在我们这里对应Decision字段,Decision字段除了记录是否采样,还记录了log开关和report开关。

  2. 数据库、UI使用zipkin,减少了一些开发量。zipkin的Span设计能适应较多的采集场景,比如单向数据传递、黑盒模式的远端节点(调用数据库时,数据库就是一个黑盒模型)。

  3. 和淘宝类似,加了业务字段透传功能,业务日志和跟踪日志分开。

Elk 6.1 安装使用说明

ELK 不是一款软件,而是 Elasticsearch、Logstash 和 Kibana 三种软件产品的首字母缩写。这三者都是开源软件,通常配合使用,而且又先后归于 Elastic.co 公司名下,所以被简称为 ELK Stack。根据 Google Trend 的信息显示,ELK Stack 已经成为目前最流行的集中式日志解决方案。

架构

Logstash

介绍

数据收集引擎:数据存储与数据流。它支持动态的从各种数据源搜集数据,并对数据进行过滤、分析、丰富、统一格式等操作,然后存储到用户指定的位置;

安装

1、 官方教程
2、 windows下安装Logstash

效果图

注意事项

1、windows下需要使用NSSM

Elasticsearch

安装

说明:Elastic 需要 Java 8 环境,注意要保证环境变量JAVA_HOME正确设置。
1、下载最新Elasticsearch版本,解压到指定目录。
2、在Unix上运行bin/elasticsearch或者在Windows上运行bin\elasticsearch.bat
3、打开:http://localhost:9200/

效果图

注意事项:

  1. 启动内存不足
# vi ${elasticsearch_HOME}/config/jvm.options
#-Xms2g
#-Xmx2g
-Xms512m
-Xmx512m
  1. 无法以root权限启动
# groupadd es
# useradd es -g es -p es
# chown es:es ${elasticsearch_HOME}/
# sudo su es
# ./bin/elasticsearch

安装elasticsearch-head

git clone git://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head
npm install
npm run start
open http://localhost:9100/

谷歌插件安装链接

说明

Kibana

介绍

Kibana是个数据分析和可视化平台。通常与 Elasticsearch 配合使用,对其中数据进行搜索、分析和以统计图表的方式展示;

安装

kibana官方文档

效果图

注意事项

1、 需要cmd执行,直接运行.bat无效
2、 Logstash 6.1.1 issue with setup.bat file "could not find jruby in"
在7.0版本修复,下载master文件,并替换bin目录文件接口。

参考资料
1. Kibana入门教程
2. Logstash 最佳实践
3. kibana官方文档
4. ELK+Filebeat 集中式日志解决方案详解

mysql 如何恢复数据?

问题:

尝试还原数据库,之后提示 table doesn`t exist。

分析:

类型:MyISAM
数据:Table.frm,Table.MYD,Table.MYI
位置:/data/$databasename/目录中
说明:直接复制到mysql中data目录中,便可以使用

类型:InnoDB
数据文件:存储在/$innodb_data_home_dir/中的ibdata1文件中
结构文件:结构文件存在于/data/table_name.frm中
说明:不可以直接使用,并报错:table doesn`t exist

解决方法:

1、 停止 apache 和 mysql服务
2、 拷贝相应文件到/data/目录,在数据库引擎类型为InnoDB时,拷贝数据文件的同时还需要拷贝ibdata1。
3、 将根目录下的ib_logfile*文件全部删除掉

备注:

1、 正常的数据导出恢复,最好用工具,不要在data文件层面去恢复
2、 测试环境在windows下

参考文档:

1、 mysql 直接从date 文件夹备份表,还原数据库之后提示 table doesn`t exist的原因和解决方法

在api里写可以平滑关闭的进程

背景

对于无关紧要的进程,我们可以随时kill掉再重启,但是有时候不能这样粗暴地操作。

假设我们正在处理一个从消息系统得到的消息,而消息是 非持久化/自动ack 的,如果进程被打断,这条消息就可能丢失。

或者我们在处理一个耗时特别长的任务,这时别人不小心kill掉了我们的进程,那我们前面的成果就付诸东流了。

针对上面这些情况,我们在Api进程里加入了平滑关闭的特性,你可以捕获进程关闭指令,自己决定何时关闭。

下面是一些使用方法:

使用

使用方法一:闭包

class Cront extends MY_Controller
{
    public function run()
    {
        $ready_exit = false;
        $this->regSig(SIGTERM, function() use (&$ready_exit) {
            $ready_exit = true;
            echo "准备退出!";
        });
        while (true) {
            {
                /**
                 * 括号里是业务处理代码
                 */
                echo "正在处理业务...\n";
                sleep(3);
                echo "业务处理完毕!\n";
            }
            $this->catchSig();
            if ($ready_exit) {
                echo "exit now!\n";
                exit;
            }
        }
    }
}

使用方法二:对象方法

class Cront extends MY_Controller
{
    public $ready_exit = false;
    public function handSig($sig, $my_var1, $my_var2)
    {
        switch ($my_var1) {
            case 'run':
                $this->ready_exit = true;
                echo "exit ready\n";
                break;
        }
        var_dump(func_get_args());
    }

    public function run()
    {
        $this->regSig(SIGTERM, array($this, 'handSig'), 'run', 'myvar');
        while (true) {
            {
                /**
                 * 括号里是业务处理代码
                 */
                echo "正在处理业务...\n";
                sleep(3);
                echo "业务处理完毕!\n";
            }
            $this->catchSig();
            if ($this->ready_exit) {
                echo "exit now!\n";
                exit;
            }
        }
    }
}

注意

  1. 进程捕获关闭指令后,用 kill -9 仍然可以杀死进程;
  2. api里的 regSig 不仅可以捕获 SIGTERM 信号,还可以捕获其他信号;
  3. 如果调用了 regSig 而没有调用 catchSig ,程序仍然捕获不到信号;
  4. 不用 declare(ticks = 1); 是考虑到性能;

PHP闭包

闭包的介绍

闭包是词法作用域的体现,一般编程语言有这些特性

  1. 函数是一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
  2. 函数可以嵌套定义,即在一个函数内部可以定义另一个函数。

三个关键点

  1. 函数
  2. 自由变量
  3. 作用域

自由变量

let a = 1
let b = function(){
    console.log(a)
}

在这个例子里函数b因为捕获了外部作用域(环境)中的变量a,因此形成了闭包。 而由于变量a并不属于函数b,所以在概念里被称之为「自由变量」。

注意点:

  1. 在一些语言里,「捕获」这一行为还有一个特点:闭包只会捕获自由变量的引用。
  2. 在PHP中,需要使用use关键词显示调用自由变量

作用域

可以使用外部的「自由变量」,内部变量不能被访问,形成一个相对安全的访问环境

总结

  1. 记住定义当时的外部环境
  2. 封闭外部环境
  3. 延迟执行

基本使用

$func = function () {

};

$func();

$a = '1';
$func = function () use ($a) {

}

$func();

作用域

  1. 闭包拥有独立的作用域,基本等同普通函数
  2. 在方法中使用时,等同于实例的方法,可以直接使用$this(>=5.4)

注意点

$this 的使用

在一次开发中遇到关于闭包的一个问题

class Demo
{
    /**
     *
     */
    public function test()
    {
        $data = $this->isCache('name', function () {
            $this->sayHello();
        });
    }

    /**
     *
     */
    protected function sayHello()
    {
        echo 'Hello, World';
    }

    /**
     * @param $name
     * @param $callback
     * @return array
     */
    public function isCache($name, $callback)
    {
        $callback();
        return array();
    }
}

我希望在一个闭包中调用这个类的一个受保护的方法(这里假设是sayHello),结果在运行的时候,直接出现了语法级别的报错。

查了一些资料发现了问题

  1. 5.3 版本不能直接在闭包中使用$this

PHP的bug,在5.4中得以修复

$that = $this;        
$data = $this->isCache('name', function () use ($that) {
    $that->sayHello(); // 这里只能是public
});

看似这个问题解决了,不过在stackoverflow上一位热心人士的提醒中,这样是会出现一些问题的,因为这样传递$this, 是赋值传递,也就是说,在闭包中调用方法导致实例属性的变化不会反应到闭包外面,这样在一些特殊情况,会导致闭包内外属性不一致,所以这里最好使用引用传递除非你知道你在干什么!

$that = $this;        
$data = $this->isCache('name', function () use (&$that) {
    $that->sayHello(); // 这里只能是public
});

如果在5.4版本下,不但可以直接使用$this,而且作用域等同于实例中的方法

$data = $this->isCache('name', function () {
    $that->sayHello(); // 这里可以是任意限制符
});

Closure类

bind和bindTo的区别

// bind,相当把一个类的加上加上一个方法,并且将该方法转换成闭包
class A {
    private static $sfoo = 1;
    private $ifoo = 2;
}
$cl1 = static function() {
    return A::$sfoo;
};
$cl2 = function() {
    return $this->ifoo;
};

$bcl1 = Closure::bind($cl1, null, 'A');
$bcl2 = Closure::bind($cl2, new A(), 'A');
echo $bcl1(), "\n"; // 1
echo $bcl2(), "\n";// 2

// bindTo,改变指定的$this对象和类作用域

class A {
    function __construct($val) {
        $this->val = $val;
    }
    function getClosure() {
        //returns closure bound to this object and scope
        return function() { return $this->val; };
    }
}

$ob1 = new A(1);
$ob2 = new A(2);

$cl = $ob1->getClosure();
echo $cl(), "\n";
$cl = $cl->bindTo($ob2);
echo $cl(), "\n";


虽然两者看上去像,实际上不是一回事。

Vue开发环境搭建

一、安装虚拟机
1)版本:Vmware 12 Pro版 + Cent-OS 32位
2)密钥:5A02H-AU243-TZJ49-GTC7K-3C61N
3)分区:/、/boot、swap

二、配置网络
1)ifconfig查看windows配置
2)修改文件:vim /etc/sysconfig/network-scripts/ifcfg-eth0
# Xen Virtual Ethernet
DEVICE=eth0
BOOTPROTO=none
ONBOOT=yes
HWADDR=2e:c1:12:61:5f:ea
NETMASK=255.255.255.0
IPADDR=192.168.0.249
GATEWAY=192.168.0.1
TYPE=Ethernet

三、配置远程控制器putty
直接设置ip即可,没有选择生成秘钥

四、安装nodejs
1、安装依赖包:yum install gcc-c++ openssl-devel
2、安装最新版本node.js:
cd /usr/local/src
wget http://nodejs.org/dist/node-latest.tar.gz
tar -zxvf node-latest.tar.gz
cd node-v7.7.0
./configure
make && make install
3、如果需要升级gcc,则升级gcc,不要忘记加软连接
4、解决警告:sudo date -s '01:10:00 2017-03-02'
5、重新执行 ./configure 以及 make && make install

五、vue安装
# 全局安装 vue-cli
$ npm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
$ vue init webpack my-project
# 安装依赖,走你
$ cd my-project
$ npm install
$ npm run dev

六、本地(windows)与虚拟机(Cent-OS)交互
1、vim build/dev-server.js
修改localhost为虚拟机IPADDR
2、关闭防火墙
3、在本地浏览器输入IP:8080展现vue界面

关于用户体验的十大原则

用户体验的核心和本质就是研究人在特定场景下的思维模式和行为模式,然后顺应和利用它。
举例:
为什么要把网页的重要内容放到页面的左上部分?因为人的视觉注意力会首先锁定在左上的位置。
为什么按钮平常状态和按下状态一般是不同的?因为用户需要反馈,没有反馈人会产生疑惑。
为什么超市收银台面前会有货物架?因为顾客在排队的时候处于非常无聊的状态,这时候任何商品或信息都会引起他们的注意,是销售某些易耗便携物品的绝佳时机。

  1. 系统可见原则
    保证界面的内容可见、状态可见、变化可见
    12
    内容你要考虑到用户最想看见关注的是什么,收货人信息,支付方式和派送方式,总价以及扣除其它折扣优惠等最终用户需要花的钱,这些是用户最关注的点。

    12
    不管是有网、没网,还是错误、成功、警告、提示等状态都要给用户一个反馈;
    14
    像左划右划,上拉下拉,抽屉效果等动画都需要展现出来给用户一个暗示:这个是可以手势操作的。

  2. 可控性原则
    让用户能对当前情况有很好的了解与掌握,要给用户足够的自由度。

15

  1. 一致性
    用户在同一款产品里接受同一套规范和逻辑。比如说收藏、关注、点赞等,不管是名称还是图标都要形成一个大的规范,更换名词和图标会导致用户的学习成本提高。还有行为逻辑一定要规范。

  2. 防错,防呆原则
    要尽量用足够的提醒和设计,让用户不要混淆、犯错和发呆。
    1
    2

  3. 协助用户记忆原则
    在需要记忆某些信息时,产品功能上要帮助用户记忆。如账号密码的记录,短信的草稿等。
    3
  4. 简约易读原则
    界面足够简单、内容易换

  5. 容错原则
    向用户提醒可能出现的错误,并提供能挽回用户错误的方法。
    8
    9

  6. 帮助和提示
    在任何时候,考虑到用户需要得到的帮助情况予以提示,比如删除东西,添加修改等。
  7. 灵活高效原则
    用户在使用时,要能够方便,高效的完成任务,如注册流程,填写资料、分享文章,书写动态等。
  8. 恢复现场原则
    适应用户的碎片化使用习惯,在各种切换和退出返回时,要有恢复现场的能力。比如在看视频电影的时候,有人找你聊天,你退出回复完,再点击进入还能继续观看你的电影。

Trait

Trait 是一种类似PHP单继承语言而准备的代码复用机制

目前可在很多框架汇总看到,比如Yii 、Laravel

为什么要用到这个?
php是单继承的语言,而Trait就是为了解决php不能多继承提高代码的复用并且摒弃一下多线程的诟病而产生的。

1.确定版本:

使用的是在线调试工具
限制:php5.4开始可以使用

http://www.shucunwang.com/RunCode/php5.4/
图片1
我们选择5.4的版本

ps:当然选择低版本会报错误
syntax error, unexpected T_STRING

2.先简单实现Trait

a)创建
图片2
ps:Trait不能被实例化
b)调用
图片3打印结果:图片4

当要调用多个的时候
图片5打印结果:图片6

3.优先级(如果类中方法与trait中重名,那么用的是谁的?)

图片7运行结果:图片8

总结:
a)Trait会覆盖当前类继承的方法
b)当前类的方法会覆盖Trait的方法
即:调用类>Trait>父类

4.多个Trait冲突问题(ps:当多个Trait中方法名相同时怎么处理?)

如果直接使用,会产生致命错误
因此需要这样解决
图片9运行结果:图片10

由此看出
a)这里声明Test中的testA方法替换Test1中的TestA方法
b)Test1中的testA方法起别名为bb
总结:
当出现冲突时可以使用insteadof来确定使用哪一个方法
使用as就可以使用另一个方法

相关测试

a)直接其别名是别名不使用insteadof声明是否可以? (否,会报错)
b)别名可以单独使用? (可以)
c)当出现三个或三个以上时怎样处理?
图片11运行结果:图片12
ps:少一个依旧会报错

5.Trait嵌套(Trait中包含Trait)
图片13运行结果:图片14
总结:
依旧使用use可以实现相互嵌套

相关测试
a)如果冲突是否和类中解决一样? 是的,不处理会报错
b)Trait中的方法与嵌套中的方法吗一样,那么是冲突还是按优先级分?
图片15运行结果:图片16
可见是按当前的算。

6.Trait支持抽象方法、支持静态方法,不可以直接定义静态变量,但静态变量可以被Trait方法引用
图片17运行结果:图片18

ps:当定义抽象方法时,必须在使用它的class中实现,否则会产生致命错误.
当然Trait中也可以定义属性。不过如果类中定义了和他不同的属性也会报致命错误。

从上面可以看出,Trait既不是接口也不是类。主要是为了解决单继承语言的限制。是PHP多重继承的一直解决方案。
比如同时要继承两个Abstract Class将会很麻烦,而Trait就是为了解决类似问题。它能被加入到一个或多个已存在的类中。它声明了类能做什么(表面了其接口的特性),同时也包含具体的实现(表面了其类的特性)

总的来说,个人感觉就是Trait = abstract class - interface

分析页面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