当前位置: 首页 > 编程日记 > 正文

[PHPUnit]自动生成PHPUnit测试骨架脚本-提供您的开发效率【2015升级版】

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

场景

在编写PHPUnit单元测试代码时,其实很多都是对各个类的各个外部调用的函数进行测试验证,检测代码覆盖率,验证预期效果。为避免增加开发量,可以使用PHPUnit提供的phpunit-skelgen来生成测试骨架。只是一开始我不知道有这个脚本,就自己写了一个,大大地提高了开发效率,也不用为另外投入时间去编写测试代码而烦心。但是后来我发现自定义的脚本比phpunit-skelgen更具人性化、更有趣。也更为有效。特此在这里分享一下。


一个待测试的示例类

假如我们现在有一个简单的业务类,实现了加运算,为了验证其功能,下面将会就两种生成测试代码的方式进行说明。

<?phpclass Demo
{/*** 求两数和** @testcase 2 1,1* @testcase -5 -10,5** @param int $left 左操作数* @param int $right 右操作数* @return int*/public function inc($left, $right){return $left + $right;}
}

用phpunit-skelgen生成测试骨架

在安装了phpunit-skelgen后,可以使用以下命令来生成测试骨架。

phpunit-skelgen --test -- Demo ./Demo.php

生成后,使用:

vim ./DemoTest.php

可查看到生成的测试代码如下:

<?php
/*** Generated by PHPUnit_SkeletonGenerator 1.2.1 on 2014-06-30 at 15:53:01.*/
class DemoTest extends PHPUnit_Framework_TestCase
{/*** @var Demo*/protected $object;/*** Sets up the fixture, for example, opens a network connection.* This method is called before a test is executed.*/protected function setUp(){$this->object = new Demo;}/*** Tears down the fixture, for example, closes a network connection.* This method is called after a test is executed.*/protected function tearDown(){}/*** @covers Demo::inc* @todo   Implement testInc().*/public function testInc(){// Remove the following lines when you implement this test.$this->markTestIncomplete('This test has not been implemented yet.');}
}

试运行测试一下:

[test ~/tests]$phpunit ./DemoTest.php    
PHPUnit 3.7.29 by Sebastian Bergmann.PHP Fatal error:  Class 'Demo' not found in ~/tests/DemoTest.php on line 18

可以看到没有将需要的测试类包括进来。当然还有其他一些需要手工改动的地方。但是生成的代码立即执行是失败的!


用自定义的测试代码生成脚本

现在改用自定义的脚本 来生成,虽然也有需要手工改动的地方,但已经尽量将需要改动的代码最小化,让测试人员(很可能是开发人员自己)更关注业务的测试。

先看一下Usage.

$ ./build_phpunit_test_tpl.php Usage: php ./build_phpunit_test_tpl.php <file_path> <class_name> [bootstrap] [author = dogstar]Demo:php ./build_phpunit_test_tpl.php ./Demo.php Demo > Demo_Test.php

然后可以使用:

php ./build_phpunit_test_tpl.php ./Demo.php Demo

来预览看一下将要生成的测试代码,如果没有问题可以使用:

php ./build_phpunit_test_tpl.php ./Demo.php Demo > ./Demo_Test.php

将生成的测试代码保存起来。注意:这里使用的是“_Test.php”后缀,以便和官方的区分。看下生成的代码:

<?php
/*** PhpUnderControl_Demo_Test** 针对 ./Demo.php Demo 类的PHPUnit单元测试** @author: dogstar 20150118*///建议采用统一的测试环境,但由于此次示例中没有,故先注释
//require_once dirname(__FILE__) . '/test_env.php';if (!class_exists('Demo')) {require dirname(__FILE__) . '/./Demo.php';
}class PhpUnderControl_Demo_Test extends PHPUnit_Framework_TestCase
{public $demo;protected function setUp(){parent::setUp();$this->demo = new Demo();}protected function tearDown(){}/*** @group testInc*/ public function testInc(){$left = '';$right = '';$rs = $this->demo->inc($left, $right);$this->assertTrue(is_int($rs));}/*** @group testInc*/ public function testIncCase0(){$rs = $this->demo->inc(1,1);$this->assertEquals(2, $rs);}/*** @group testInc*/ public function testIncCase1(){$rs = $this->demo->inc(-10,5);$this->assertEquals(-5, $rs);}}

随后,试运行一下:

$phpunit ./Demo_Test.php    
PHPUnit 4.3.0 by Sebastian Bergmann....Time: 22 ms, Memory: 4.75MbOK (3 tests, 3 assertions)

测试通过了!!!

起码,我觉得生成的代码在大多数默认情况下是正常通过的话,可以给开发人员带上心理上的喜悦,从而很容易接受并乐意去进行下一步的测试用例完善。

现在,开发人员只须稍微改动测试代码就可以实现对业务的验证。如下示例:

    /*** @group testInc*/public function testInc(){$left = '2015';$right = '1';$rs = $this->demo->inc($left, $right);$this->assertTrue(is_int($rs));$this->assertEquals(2016, $rs);}

然后再运行,依然通过。

根据注释生成测试代码

在上面的示例中,脚本会默认生成一个单元测试,并且尝试对已知类型的返回值作验证。除此之外,还为简单的“输入参数 & 期望结果”生成了对应的单元测试,可以有多组。

下面是相关的注释:

     * @testcase 2 1,1* @testcase -5 -10,5

格式也是显然易见的,就是:

@testcase 期望结果 (空格) [参数1,参数2,...]

其中@testcase为关键字,期望结果为函数应该返回的值,后面的参数串将会原样传递给单元测试的代码。

考虑到单元测试的复杂性和一般性,目前只是提供了这一种简单的根据注释生成测试代码。并且,这里更推荐您亲自来编写单元测试,因为通过对单元测试的编写,你将可以发现很多有趣的问题,有趣的实践。一如TDD。

与测试驱动开发TDD的结合

测试驱动开发,是要求在未写产品代码前先写单元测试的代码,并让它预期的失败。但很多情况下我们更多是针对已有的代码(特别是历史遗留或者过去自己编写的代码)由于后期维护而进行单元测试。这两种情况都稍微显得有点“偏激”,因此我们可以稍微变通一下,以平衡这两种情况之间的微妙关系。

根据三层概念视角,我们显然可以进行共性分析,并且约定好规约接口。由此,类的简单声明和函数签名可以确定并可以开发类的定义代码。随后,再补充@testcase注释并通过本脚本自动生成测试代码,进行测试驱动开发。

下面是一个简单的例子:

假设我们有一个游戏用户的辅助类,可以根据用户的经验值算出用户对应的等级。并且规定:

经验值
等级
0
1级
[1, 10)
1级
[10, 20)
2级
[20, 30)
3级
...
...
[990, 1000)
99级
[1000, +无穷大)
100级

在此业务场景下,我们可以定义一个游戏用户类GameUserHelper为:

//$vim ./GameUserHelper.php 
<?phpclass GameUserHelper
{public static function exp2level($exp){}
}

当此实现开发完成后,外部调用则可以通过以下方式来使用:

$level = GameUserHelper::exp2level(100);    //等级为10

为了快速进行单元测试,我们先补充一下@testcase注释:

//$vim ./GameUserHelper.php 
<?phpclass GameUserHelper
{/*** 根据用户的经验值算出对应的等级** @testcase 10 100 * @testcase 100 9999* @testcase 1 -8** @param int $exp 用户的经验值* @return int*/public static function exp2level($exp){}
}

然后,通过脚本自动生成测试骨架和代码:

$./build_phpunit_test_tpl.php ./GameUserHelper.php GameUserHelper > GameUserHelper_Test.php

执行一下:

$phpunit ./GameUserHelper_Test.php 
PHPUnit 3.7.29 by Sebastian Bergmann.FFFFTime: 30 ms, Memory: 3.75MbThere were 4 failures:1) PhpUnderControl_GameUserHelper_Test::testExp2level
Failed asserting that false is true./mnt/hgfs/php/centos/projects/test/GameUserHelper_Test.php:412) PhpUnderControl_GameUserHelper_Test::testExp2levelCase0
Failed asserting that null matches expected 10./mnt/hgfs/php/centos/projects/test/GameUserHelper_Test.php:523) PhpUnderControl_GameUserHelper_Test::testExp2levelCase1
Failed asserting that null matches expected 100./mnt/hgfs/php/centos/projects/test/GameUserHelper_Test.php:624) PhpUnderControl_GameUserHelper_Test::testExp2levelCase2
Failed asserting that null matches expected 1./mnt/hgfs/php/centos/projects/test/GameUserHelper_Test.php:72FAILURES!
Tests: 4, Assertions: 4, Failures: 4.

Well Done!预期地失败了!下面是更多的业务开发,略。。。

脚本源代码

//$ cat ./build_phpunit_test_tpl.php
#!/usr/bin/env php
<?php
/*** 单元测试骨架代码自动生成脚本* 主要是针对当前项目系列生成相应的单元测试代码,提高开发效率** 用法:* Usage: php ./build_phpunit_test_tpl.php <file_path> <class_name> [bootstrap] [author = dogstar]** 1、针对全部public的函数进行单元测试* 2、可根据@testcase注释自动生成测试用例** 备注:另可使用phpunit-skelgen进行骨架代码生成** @author: dogstar 20150108* @version: 4.0.0*/if ($argc < 3) {echo "
Usage: php $argv[0] <file_path> <class_name> [bootstrap] [author = dogstar]Demo:php ./build_phpunit_test_tpl.php ./Demo.php Demo > Demo_Test.php";die();
}$filePath = $argv[1];
$className = $argv[2];
$bootstrap = isset($argv[3]) ? $argv[3] : null;
$author = isset($argv[4]) ? $argv[4] : 'dogstar';if (!empty($bootstrap)) {require $bootstrap;
}require $filePath;if (!class_exists($className)) {die("Error: cannot find class($className). \n");
}$reflector = new ReflectionClass($className);$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);date_default_timezone_set('Asia/Shanghai');
$objName = lcfirst(str_replace('_', '', $className));$code = "<?php
/*** PhpUnderControl_" . str_replace('_', '', $className) . "_Test** 针对 $filePath $className 类的PHPUnit单元测试** @author: $author " . date('Ymd') . "*/";if (file_exists(dirname(__FILE__) . '/test_env.php')) {$code .= "require_once dirname(__FILE__) . '/test_env.php';
";
} else {$code .= "//require_once dirname(__FILE__) . '/test_env.php';
";
}$initWay = "new $className()";
if (method_exists($className, '__construct')) {$constructMethod = new ReflectionMethod($className, '__construct');if (!$constructMethod->isPublic()) {if (is_callable(array($className, 'getInstance'))) {$initWay = "$className::getInstance()";} else if(is_callable(array($className, 'newInstance'))) {$initWay = "$className::newInstance()";} else {$initWay = 'NULL';}}
}$code .= "
if (!class_exists('$className')) {require dirname(__FILE__) . '/$filePath';
}class PhpUnderControl_" . str_replace('_', '', $className) . "_Test extends PHPUnit_Framework_TestCase
{public \$$objName;protected function setUp(){parent::setUp();\$this->$objName = $initWay;}protected function tearDown(){}";foreach ($methods as $method) {if($method->class != $className) continue;$fun = $method->name;$Fun = ucfirst($fun);if (strlen($Fun) > 2 && substr($Fun, 0, 2) == '__') continue;$rMethod = new ReflectionMethod($className, $method->name);$params = $rMethod->getParameters();$isStatic = $rMethod->isStatic();$isConstructor = $rMethod->isConstructor();if($isConstructor) continue;$initParamStr = '';$callParamStr = '';foreach ($params as $param) {$default = '';$rp = new ReflectionParameter(array($className, $fun), $param->name);if ($rp->isOptional()) {$default = $rp->getDefaultValue();}if (is_string($default)) {$default = "'$default'";} else if (is_array($default)) {$default = var_export($default, true);} else if (is_bool($default)) {$default = $default ? 'true' : 'false';} else if ($default === null) {$default = 'null';} else {$default = "''";}$initParamStr .= "\$" . $param->name . " = $default;";$callParamStr .= '$' . $param->name . ', ';}$callParamStr = empty($callParamStr) ? $callParamStr : substr($callParamStr, 0, -2);/** ------------------- 根据@return对结果类型的简单断言 ------------------ **/$returnAssert = '';$docComment = $rMethod->getDocComment();$docCommentArr = explode("\n", $docComment);foreach ($docCommentArr as $comment) {if (strpos($comment, '@return') == false) {continue;}$returnCommentArr = explode(' ', strrchr($comment, '@return'));if (count($returnCommentArr) >= 2) {switch (strtolower($returnCommentArr[1])) {case 'bool':case 'boolean':$returnAssert = '$this->assertTrue(is_bool($rs));';break;case 'int':$returnAssert = '$this->assertTrue(is_int($rs));';break;case 'integer':$returnAssert = '$this->assertTrue(is_integer($rs));';break;case 'string':$returnAssert = '$this->assertTrue(is_string($rs));';break;case 'object':$returnAssert = '$this->assertTrue(is_object($rs));';break;case 'array':$returnAssert = '$this->assertTrue(is_array($rs));';break;case 'float':$returnAssert = '$this->assertTrue(is_float($rs));';break;}break;}}/** ------------------- 基本的单元测试代码生成 ------------------ **/$code .= "/*** @group test$Fun*/ public function test$Fun(){". (empty($initParamStr) ? '' : "$initParamStr\n") . "\n        ". ($isStatic ? "\$rs = $className::$fun($callParamStr);" : "\$rs = \$this->$objName->$fun($callParamStr);") . (empty($returnAssert) ? '' : "\n\n        " . $returnAssert . "\n") . "}
";/** ------------------- 根据@testcase 生成测试代码 ------------------ **/$caseNum = 0;foreach ($docCommentArr as $comment) {if (strpos($comment, '@testcase') == false) {continue;}$returnCommentArr = explode(' ', strrchr($comment, '@testcase'));if (count($returnCommentArr) > 1) {$expRs = $returnCommentArr[1];$callParamStrInCase = isset($returnCommentArr[2]) ? $returnCommentArr[2] : '';$code .= "/*** @group test$Fun*/ public function test{$Fun}Case{$caseNum}(){". "\n        ". ($isStatic ? "\$rs = $className::$fun($callParamStrInCase);" : "\$rs = \$this->$objName->$fun($callParamStrInCase);") . "\n\n        \$this->assertEquals({$expRs}, \$rs);" . "}
";$caseNum ++;}}}$code .= "
}";echo $code;
echo "\n";


转载于:https://my.oschina.net/dogstar/blog/285579

相关文章:

ORL Faces Database介绍

ORL人脸数据集共包含40个不同人的400张图像&#xff0c;是在1992年4月至1994年4月期间由英国剑桥的Olivetti研究实验室创建。此数据集下包含40个目录&#xff0c;每个目录下有10张图像&#xff0c;每个目录表示一个不同的人。所有的图像是以PGM格式存储&#xff0c;灰度图&…

张俊林:BERT和Transformer到底学到了什么 | AI ProCon 2019

演讲嘉宾 | 张俊林&#xff08;新浪微博机器学习团队AI Lab负责人&#xff09;编辑 | Jane出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;【导读】BERT提出的这一年&#xff0c;也是NLP领域迅速发展的一年。学界不断提出新的预训练模型&#xff0c;刷新各…

Eclipse创建web工程时,报错Dynamic Web Module 3.0 requires Java 1.6 or newer.

报错&#xff1a; 解决方案&#xff1a; 1.打开eclipse工具栏window->preferences 2.打开java->compiler 3.选择compiler compliance level在1.6以上版本&#xff08;此处选择1.8&#xff09; 4.点击apply and close保存修改&#xff0c;即可 转载于:https://www.cnblogs…

Maven学习总结(八)——使用Maven构建多模块项目

2019独角兽企业重金招聘Python工程师标准>>> Maven学习总结(八)——使用Maven构建多模块项目 在平时的Javaweb项目开发中为了便于后期的维护&#xff0c;我们一般会进行分层开发&#xff0c;最常见的就是分为domain&#xff08;域模型层&#xff09;、dao&#xff0…

哈工大、清华、CSDN、嵌入式视觉联盟合办的 AIoT 盛会,你怎么舍得错过?!

2019 嵌入式智能国际大会即将来袭&#xff01;随着物联网和人工智能技术的飞速发展与相互渗透&#xff0c;万物智联的新赛道已经开始显现。据中商产业研究院《2016—2021年中国物联网产业市场研究报告》显示&#xff0c;预计到2020年&#xff0c;中国物联网的整体规模将达2.2万…

OpenCV3.3中主成分分析(Principal Components Analysis, PCA)接口简介及使用

OpenCV3.3中给出了主成分分析(Principal Components Analysis, PCA)的实现&#xff0c;即cv::PCA类&#xff0c;类的声明在include/opencv2/core.hpp文件中&#xff0c;实现在modules/core/src/pca.cpp文件中,其中&#xff1a;(1)、cv::PCA::PCA&#xff1a;构造函数&#xff1…

Spring MVC配置

为什么80%的码农都做不了架构师&#xff1f;>>> 一、传统方式配置Spring MVC &#xff08;1&#xff09;导入jar包 需要导入如下的jar包 junit-3.8.1.jar spring-core-3.0.5.RELEASE.jar commons-logging-1.1.1.jar spring-context-3.0.5.REL…

主成分分析(PCA)Python代码实现

主成分分析(Principal Components Analysis, PCA)简介可以参考&#xff1a; http://blog.csdn.net/fengbingchun/article/details/78977202 这里是参照 http://sebastianraschka.com/Articles/2014_pca_step_by_step.html 文章中的code整理的Python代码&#xff0c;实现过程为…

AI发展这一年:不断衍生的技术丑闻与抵制声潮

作者 | AI Now学院译者 | Raku编辑 | Jane出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】10月2日&#xff0c;纽约大学AI Now学院在纽约大学斯克博剧院&#xff08;Skirball Theatre&#xff09;组织召开了第四届年度AI Now研讨会。研讨会邀请了业内组织者…

Distributed Configuration Management Platform(分布式配置管理平台)

2019独角兽企业重金招聘Python工程师标准>>> 专注于各种 分布式系统配置管理 的通用组件/通用平台, 提供统一的配置管理服务。 主要目标&#xff1a; 部署极其简单&#xff1a;同一个上线包&#xff0c;无须改动配置&#xff0c;即可在 多个环境中(RD/QA/PRODUCTION…

如何利用zendstudio新建 或导入php项目

为什么80%的码农都做不了架构师&#xff1f;>>> 一、利用ZendStudio创建 PHP Project 1. 打开ZendStudio, 选择&#xff1a;File &agrave; New &agrave; PHP Project, 如下图所示: 于是弹出如下界面&#xff1a; 在”Project name”后输入工程名(比如我这里…

一文读懂GoogLeNet神经网络 | CSDN博文精选

作者 | .NY&XX来源 | CSDN博客本文介绍的是著名的网络结构GoogLeNet&#xff0c;目的是试图领会其中结构设计思想。GoogLeNet特点优化网络质量的生物学原理GoogLeNet网络结构的动机GoogLeNet架构细节Inception模块和普通卷积结构的差异辅助分类器GoogLeNet网络架构GoogLeNe…

C++中的函数签名

C中的函数签名(function signature)&#xff1a;包含了一个函数的信息&#xff0c;包括函数名、参数类型、参数个数、顺序以及它所在的类和命名空间。普通函数签名并不包含函数返回值部分&#xff0c;如果两个函数仅仅只有函数返回值不同&#xff0c;那么系统是无法区分这两个函…

MyEclipse断点调试

2019独角兽企业重金招聘Python工程师标准>>> 1、在编辑的程序的左边&#xff0c;你会看到一条浅浅的灰色编带&#xff0c;在这里设置断点。 2、设置断点的方法有很多 方法&#xff1a;1&#xff09;、双击 &#xff1b; 2&#xff09;、右键&#xff0c;选择“Toggl…

C primer plus 练习题 第三章

5. 1 #include <stdio.h>2 3 int main()4 {5 float you_sec;6 printf("请输入你的年龄:");7 scanf("%f", &you_sec);8 printf("年龄合计:%e 秒!\n", you_sec * 3.156e7);9 getchar(); 10 return 0; 11 }

Echache整合Spring缓存实例讲解

2019独角兽企业重金招聘Python工程师标准>>> 摘要&#xff1a;本文主要介绍了EhCache&#xff0c;并通过整合Spring给出了一个使用实例。 一、EhCache 介绍 EhCache 是一个纯Java的进程内缓存框架&#xff0c;具有快速、精干等特点&#xff0c;是Hibernate中默认的C…

灰度图像直方图均衡化公式及实现

图像的直方图&#xff1a;直方图是图像中像素强度分布的图形表达方式。它统计了每一个强度值所具有的像素个数。直方图均衡化&#xff1a;是通过拉伸像素强度分布范围来增强图像对比度的一种方法。是图像处理领域中利用图像直方图对对比度进行调整的方法。均衡化指的是把一个分…

超越最新无监督域自适应方法,研究人员提轻量CNN新架构OSNet

作者 | Kaiyang Zhou, Xiatian Zhu, Yongxin Yang, Andrea Cavallaro, and Tao Xiang 译者 | TroyChang 编辑 | Jane 出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09; CNN新架构OSNet 【导读】今天推荐论文《Learning Generalisable Omni-Scale Repre…

在一台机器上搭建多个redis实例

2019独角兽企业重金招聘Python工程师标准>>> 默认Redis程序安装在/usr/local/redis目录下&#xff1b; 配置文件&#xff1a;/usr/local/redis/redis.conf&#xff0c;该配置文件中配置的端口为默认端口&#xff1a;6379&#xff1b; Redis的启动命令路径&#xff1…

使用kaptcha生成验证码

2019独角兽企业重金招聘Python工程师标准>>> kaptcha是一个简单好用的验证码生成工具&#xff0c;通过配置&#xff0c;可以自己定义验证码大小、颜色、显示的字符等等。下面就来讲一下如何使用kaptcha生成验证码以及在服务器端取出验证码进行校验。 一、搭建测试环…

主成分分析(PCA) C++ 实现

主成分分析(Principal Components Analysis, PCA)简介可以参考&#xff1a; http://blog.csdn.net/fengbingchun/article/details/78977202以下是PCA的C实现&#xff0c;参考OpenCV 3.3中的cv::PCA类。使用ORL Faces Database作为测试图像。关于ORL Faces Database的介绍可以参…

为何Google、微软、华为将亿级源代码放一个仓库?从全球最大代码管理库说起...

作者 | 夕颜编辑 | Just出品 | AI 科技大本营&#xff08;ID:rgznai100&#xff09;【导读】2017 年&#xff0c;在当时微软的一篇官方博客中&#xff0c;时任微软云开发服务副总裁的 Brian Harry 表示微软内部代码开始向 Git 迁移&#xff0c;宣布推出针对大规模 repo 的“Git…

jquery mobie导致超链接不可用

在a标签中添加rel"external"即可转载于:https://blog.51cto.com/here2142/1435434

编译器GCC与Clang的异同

GCC&#xff1a;GNU(Gnus Not Unix)编译器套装(GNU Compiler Collection&#xff0c;GCC)&#xff0c;指一套编程语言编译器&#xff0c;以GPL及LGPL许可证所发行的自由软件&#xff0c;也是GNU项目的关键部分&#xff0c;也是GNU工具链的主要组成部分之一。GCC(特别是其中的C语…

如何正确选择聚类算法? | CSDN博文精选

作者 | Josh Thompson翻译 | 张睿毅校对 | 王雨桐来源 | 数据派THU&#xff08;ID:DatapiTHU&#xff09;本文将介绍四种基本的聚类算法—层次聚类、基于质心的聚类、最大期望算法和基于密度的聚类算法&#xff0c;并讨论不同算法的优缺点。聚类算法十分容易上手&#xff0c;但…

Python工具 | 9个用来爬取网络站点的 Python 库

1️⃣Scrapy 一个开源和协作框架&#xff0c;用于从网站中提取所需的数据。 以快速&#xff0c;简单&#xff0c;可扩展的方式。 官网2️⃣cola 一个分布式爬虫框架。 GitHub3️⃣Demiurge 基于 PyQuery 的爬虫微型框架。 官网4️⃣feedparser 通用 feed 解析器。 官网5️⃣Gra…

Python并发编程实例教程

有关Python中的并发编程实例,主要是对Threading模块的应用,文中自定义了一个Threading类库。 一、简介  我们将一个正在运行的程序称为进程。每个进程都有它自己的系统状态&#xff0c;包含内存状态、打开文件列表、追踪指令执行情况的程序指针以及一个保存局部变量的调用栈。…

Nginx负载均衡之TCP端口高可用(二)

在前面我们实现了基本的HTTP反向代理&#xff0c;从互联网过来的请求已经可以分发到后端多台网站服务器上&#xff0c;但不是所有的业务都是网络类型的&#xff0c;此篇文章我们主要讨论的是TCP 端口的负载均衡做法&#xff0c;昨天也有小伙伴提到了&#xff0c;在HTTP反向代理…

语音识别大牛Daniel Povey为何加入小米?“手机+AIoT”强大生态,开源战略是关键...

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;10 月 17 日&#xff0c;语音识别开源工具 Kaldi 创始人&#xff0c;语音和 AI 领域大牛 Daniel Povey 在10 月 19 日&#xff0c;小米集团副总裁、集团技术委员会主席崔宝秋发布微博&#xff0c;欢迎 Dani…

Python运维项目中用到的redis经验及数据类型

先感叹下&#xff0c;学东西一定要活学活用&#xff01; 我用redis也有几年的历史了&#xff0c;今个才想到把集合可以当python list用。 最近做了几个项目都掺杂了redis&#xff0c; 遇到了一些个问题和开发中提高性能的方法&#xff0c;这都分享出来&#xff0c;共同学习。…