php,go,go协程实现斐波那契数列用时比较长度50.

php:50长度时请求超时,长度40时用时在一分钟

//实现1000斐波那契数列
$start =  microtime();
for($i=0;$i<50;$i++){
    echo getfei($i)."<br>";
}

function getfei($i){
    if ($i<=1){
        $fei = 1;
    }else{
        $fei = getfei($i-1)+getfei($i-2);
    }
    return $fei;
}
echo microtime()-$start;

go递归用时:2m23.9160774s

package main

import (
   "fmt"
   "time"
)

//递归实现斐波那契数列
func main() {
   t := time.Now()
   var i int = 0
   for i = 0; i < 50; i++ {
      fe := fei(i)
      fmt.Printf(" vale is %v\n", fe)
   }
   defer func() {
      cost := time.Since(t)
      fmt.Println("cost=", cost)
   }()

}
func fei(a int) (f int) {

   if a <= 1 {
      f = 1
   } else {
      f = fei(a-1) + fei(a-2)
   }
   return
}

go协程用时:1.9949ms

package main

import (
   "fmt"
   "time"
)

func feibonacci(ch chan<- int, quit <-chan bool) {
   x, y := 1, 1
   for {
      select {
      case ch <- x:
         x, y = y, x+y
      case flag := <-quit:
         fmt.Println("flag =", flag)
         return
      }

   }
}
func main() {
   t := time.Now()
   ch := make(chan int)
   quit := make(chan bool)
   go func() {
      for i := 0; i < 50; i++ {
         num := <-ch
         fmt.Println(num)
      }
      quit <- true
   }()
   feibonacci(ch, quit)
   defer func() {
      cost := time.Since(t)
      fmt.Println("cost=", cost)
   }()

}

综上,php用时最长,go协程用时最短毫秒级的

rabbitmq生产与消费php代码剖析

RabbitMQ常用的交换器类型有fanout、direct、topic、headers这四种

fanout(广播)

它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中,转发消息是最快的。

direct(定向)

direct类型的交换器路由规则也很简单,它会把消息路由到那些BindingKey和RoutingKey完全匹配的队列中。

topic(通配符)

前面讲到direct类型的交换器路由规则是完全匹配BindingKey和RoutingKey,但是这种严格的匹配方式在很多情况下不能满足实际业务的需求。topic类型的交换器在匹配规则上进行了扩展,它与direct类型的交换器相似,也是将消息路由到BindingKey和RoutingKey相匹配的队列中,但这里的匹配规则有些不同,它约定:

(一)RoutingKey为一个点好“.”分隔的字符串(被点号“.”分隔开的每一段独立的字符串称为一个单词),如“com.rabbitmq.client”、“com.hidden.client”。

(二)BindingKey和RoutingKey一样也是点号“.”分隔的字符串。

(三)BindingKey中可以存在两种特殊字符串“*”和“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配零个或多个单词。

headers

headers类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。在绑定队列和交换器时指定一组键值对,当发送消息到交换器时,RabbitMQ会获取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers类型的交换器性能会很差,而且也不实用,基本上不会看到它的存在

匹配规则x-match有下列两种类型:

x-match = all :表示所有的键值对都匹配才能接受到消息

x-match = any :表示只要有键值对匹配就能接受到消息

代码示例send.php

<?php

require 'vendor/autoload.php';
$conn = [
    // Rabbitmq 服务地址
    'host' => '192.168.222.129',
    // Rabbitmq 服务端口
    'port' => '5672',
    // Rabbitmq 帐号
    'login' => 'guest',
    // Rabbitmq 密码
    'password' => 'guest',
    'vhost'=>'/'
];

//创建连接和channel
$conn = new AMQPConnection($conn);
if(!$conn->connect()) {
    die("Cannot connect to the broker!\n");
}
$channel = new AMQPChannel($conn);

// 用来绑定交换机和队列
$routingKey = 'key_1';

$ex = new AMQPExchange($channel);
//  交换机名称
$exchangeName = 'ex1';
$ex->setName($exchangeName);

// 设置交换机类型
$ex->setType(AMQP_EX_TYPE_DIRECT);
// 设置交换机是否持久化消息
$ex->setFlags(AMQP_DURABLE);
$ex->declare();

for($i=0; $i<5; ++$i){
    echo "Send Message:".$ex->publish(date('H:i:s')."用户".$i."注册" , $routingKey )."\n";
}

consume.php 创建队列,阻塞状态消费信息

<?php
/**
 * Des 描述
 * Date 2020/8/24
 * Time  10:32
 * User ycl
 */
require 'vendor/autoload.php';
$conn = [
    // Rabbitmq 服务地址
    'host' => '192.168.222.129',
    // Rabbitmq 服务端口
    'port' => '5672',
    // Rabbitmq 帐号
    'login' => 'guest',
    // Rabbitmq 密码
    'password' => 'guest',
    'vhost'=>'/'
];
try {

//创建连接和channel
    $conn = new AMQPConnection($conn);
    if (!$conn->connect()) {
        die("Cannot connect to the broker!\n");
    }
    $channel = new AMQPChannel($conn);
    $exchangeName = 'ex1';

//创建交换机
    $ex = new AMQPExchange($channel);
    $ex->setName($exchangeName);

    $ex->setType(AMQP_EX_TYPE_DIRECT); //direct类型
    $ex->setFlags(AMQP_DURABLE); //持久化交换机
    $ex->declare();

//  创建队列
    $queueName = 'queue1';
    $q = new AMQPQueue($channel);
    $q->setName($queueName);
    $q->setFlags(AMQP_DURABLE);//持久化队列
    $q->declareQueue();

// 用于绑定队列和交换机,跟 send.php 中的一致。
    $routingKey = 'key_1';
    $q->bind($exchangeName, $routingKey);

//接收消息
    $q->consume(function ($envelope, $queue) {
        $msg = $envelope->getBody();
        echo $msg . "\n"; //处理消息
    }, AMQP_AUTOACK);//消息自动确认

    $conn->disconnect();
}catch (AMQPException $e){
    echo $e->getMessage();
}

php-fpm和nginx平滑重启

1.php-fpm 平滑重启

保留主进程,杀死子进程。给php-fpm发送重启信号kill -USR2 pid  这个pid既可以是master进程的pid,也可以是worker进程的pid,如果是master进程的pid就会吧所有worker进程重启,如果是worker进程的pid就是只重启当个worker进程,

如果在php-fpm.conf文件配置的process_control_timeout时间内子进程没有退出,那么master进程会升级SIGQUIT为SIGTERM,SIGTERM为SIGKILL,并注册1s的定时事件。SIGKILL就直接终止worker进程了,SIGTERM还能再给worker进程1s的时间。

发送重启信号是会通知master,master获取event然后给worker进程发送退出的信号SIGQUIT,worker进程接收信号后交给对应的信号处理函数处理,信号函数就是将in_shutdown变量置为1,这样worker进程 在调用fcgi_accept_request进程下一个进程处理时就不接收直接退出了,(注意这里也会有处理时间过长问题),如果进程还没有执行结束也会退出,这样就会出现代码逻辑执行一半的情况,所以要特别注意。从这里可以看出来php-fpm的重启逻辑简单粗暴,(在规定时间内处理完请求,完成不了就不管了),由于php-fpm一个worker进程每次只能处理一个请求,所以不需要计数器之类的,这样就更简单了。

命令:#kill -SIGUSR2 31158  或者  #service php-fpm reload
继续阅读“php-fpm和nginx平滑重启”

composer更换源

为什么慢
由于默认情况下执行 composer 各种命令是去国外的 composer 官方镜像源获取需要安装的具体软件信息,所以在不使用代理、不翻墙的情况下,从国内访问国外服务器的速度相对比较慢

如何修改镜像源
可以使用阿里巴巴提供的 Composer 全量镜像 mirrors.aliyun.com/composer/

a). 配置只在当前项目生效
composer config repo.packagist composer https://mirrors.aliyun.com/composer/

# 取消当前项目配置
composer config –unset repos.packagist
b). 配置全局生效
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

# 取消全局配置
composer config -g –unset repos.packagist
c). 使用第三方软件快速修改、切换 composer 镜像源
crm composer registry manager

安装 crm
composer global require slince/composer-registry-manager
列出可用的所有镜像源,前面带 * 代表当前使用的镜像
composer repo:ls

— ————— ————————————————
composer https://packagist.org
phpcomposer https://packagist.phpcomposer.com
aliyun https://mirrors.aliyun.com/composer
tencent https://mirrors.cloud.tencent.com/composer
huawei https://mirrors.huaweicloud.com/repository/php
laravel-china https://packagist.laravel-china.org
cnpkg https://php.cnpkg.org
sjtug https://packagist.mirrors.sjtug.sjtu.edu.cn
— ————— ————————————————
使用 aliyun 镜像源
composer repo:use aliyun

# 执行成功之后会输出类似以下信息
[OK] Use the repository [aliyun] success
再次执行 composer repo:ls 命令,看到前面带 * 的就是当前使用的镜像
composer repo:ls

# 可以看到 aliyun 前面有一个 * 号,代表当前使用的是 aliyun 的源
— ————— ————————————————
composer https://packagist.org
phpcomposer https://packagist.phpcomposer.com
* aliyun https://mirrors.aliyun.com/composer
tencent https://mirrors.cloud.tencent.com/composer
huawei https://mirrors.huaweicloud.com/repository/php
laravel-china https://packagist.laravel-china.org
cnpkg https://php.cnpkg.org
sjtug https://packagist.mirrors.sjtug.sjtu.edu.cn
— ————— ————————————————

————————————————
转自链接:https://learnku.com/articles/15977/composer-accelerate-and-modify-mirror-source-in-china

php错误处理

1.error_reporting()函数

1.语法

error_reporting(report_level) //可选。规定当前脚本的错误报告级别。值数字和常量名都能接受,但是,考虑未来的 PHP 版本的兼容性,推荐使用常量名

2.error_reporting() 函数规定报告哪个错误。

该函数设置当前脚本的错误报告级别。

该函数返回旧的错误报告级别。

3.

常量 描述
1 E_ERROR 运行时致命的错误。不能修复的错误。停止执行脚本。
2 E_WARNING 运行时非致命的错误。没有停止执行脚本。
4 E_PARSE 编译时的解析错误。解析错误应该只由解析器生成。
8 E_NOTICE 运行时的通知。脚本发现可能是一个错误,但也可能在正常运行脚本时发生。
16 E_CORE_ERROR PHP 启动时的致命错误。这就如同 PHP 核心的 E_ERROR。
32 E_CORE_WARNING PHP 启动时的非致命错误。这就如同 PHP 核心的 E_WARNING。
64 E_COMPILE_ERROR 编译时致命的错误。这就如同由 Zend 脚本引擎生成的 E_ERROR。
128 E_COMPILE_WARNING 编译时非致命的错误。这就如同由 Zend 脚本引擎生成的 E_WARNING。
256 E_USER_ERROR 用户生成的致命错误。这就如同由程序员使用 PHP 函数 trigger_error() 生成的 E_ERROR。
512 E_USER_WARNING 用户生成的非致命错误。这就如同由程序员使用 PHP 函数 trigger_error() 生成的 E_WARNING。
1024 E_USER_NOTICE 用户生成的通知。这就如同由程序员使用 PHP 函数 trigger_error() 生成的 E_NOTICE。
2048 E_STRICT 运行时的通知。PHP 建议您改变代码,以提高代码的互用性和兼容性。
4096 E_RECOVERABLE_ERROR 可捕获的致命错误。这就如同一个可以由用户定义的句柄捕获的 E_ERROR(见 set_error_handler())。
8191 E_ALL 所有的错误和警告的级别,除了 E_STRICT(自 PHP 6.0 起,E_STRICT 将作为 E_ALL的一部分)。

 

2.

display_errors = On //开启状态下,若出现错误,则报错,出现错误提示
dispaly_errors = Off //关闭状态下,若出现错误,则提示:服务器错误。但是不会出现错误提示

是开启php错误提示bai,而且是所有错du误提示, 开启错误提示,是为了方便调zhi试修改!

error_reporting可以选择性的关闭或者说忽略某些不想要的错误提示!

3.register_shutdown_function() :注册一个会在PHP中止时执行的函数

终止执行条件:

  • 执行完成
  • exit/die导致的中止
  • 发生致命错误中止

继续阅读“php错误处理”

php-mysql-es数据写入效率

通过分析得出,查询和循环处理占时短,大部分时间花费在了es的bulk插入上,调优方向就是优化es批量

#sleep(1)的情况
100万数据 读5万 写1万 201秒
100万数据 读5万 写5万 207秒
100万数据 读10万 写1万 203秒
100万数据 读10万 写5万 203秒
100万数据 读10万 写10万 219秒
100万数据 读1万 写1万 344秒

#不加sleep的情况
100万数据 读5万 写1万 205秒
100万数据 读5万 写5万 209秒
100万数据 读10万 写1万 208秒
100万数据 读10万 写5万 198秒
100万数据 读10万 写10万 200秒
100万数据 读1万 写1万 227秒
100万数据 读1万 写5000 226秒

mysql查询效率还可以,es批量写入造成缓慢:
100万数据查询插入消耗250秒

调增’refresh_interval’ => ’30s’,100万数据查询插入230

调增’refresh_interval’ => ’60s’,100万数据查询插入221

调增’refresh_interval’ => ’60s’,100万数据查询插入229

每次插入1000查询插入240

100万数据查询12秒

100万数据查询逻辑循环18秒

500万查询73

500万查询逻辑循环92

设置session cookie_lifetime 使浏览器关闭后还能继续保持登录状态

php.ini

session.cookie.lifetime

设置cookie_lifetime为0但一关闭浏览器后session就被删除无法保持登录状态

如果设置cookie_lifetime为7200,则表示存活2个小时,此时就算关闭浏览器也不会删除session,再次打开浏览器依然保持登录状态

session.gc.maxlifetime

session.gc.maxlifetime是指设置session最大的过期时间,指php按照一定的几率 执行它的垃圾回收机制,

这个机制指判断当前时间减去session文件最后修改时间是否大于session.gc.maxlifetime,是则删除session文件;

但session.save_path分级的话就不会执行这个回收机制;

单点登录

技术实现的机制

当用户第一次访问应用系统的时候,因为还没有登录,会被引导到认证系统中进行登录;根据用户提供的登录信息,认证系统进行身份校验,如果通过校验,应该返回给用户一个认证的凭据--ticket;用户再访问别的应用的时候,就会将这个ticket带上,作为自己认证的凭据,应用系统接受到请求之后会把ticket送到认证系统进行校验,检查ticket的合法性。如果通过校验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。

要实现SSO,需要以下主要的功能:

所有应用系统共享一个身份认证系统。

  • 统一的认证系统是SSO的前提之一。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行效验,判断其有效性。所有应用系统能够识别和提取ticket信息
  • 要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。