Fedora上完美的使用QQ

目前看下来,Linux使用QQ还是Deepin比较好。

CrossOver上的QQ目前不能记住密码,其他还好,比如聊天啥的也是可以的。

Deepin的QQ是目前最好的解决方案。

安装如下:

  • 首先,从百度网盘下载  。解压其中的  到
  • 其次,从镜像站点下载Deepin的QQ,
  • 解压  中的  到
  • 然后执行
  • 在CrossOver点击QQ启动即可。

Continue reading Fedora上完美的使用QQ

Fedora KDE 25下安装fcitx输入法

首先,安装fcitx之后,只要是接受了  、  、  环境变量的程序,都可以使用fcitx输入法了。

但是,即使我在  里面写了这三个export语句,程序也没法读到这三个环境变量。

后来发现,这里面有两个坑:

  1. Fedora KDE的display manager换成了SDDM,而在SDDM的配置文件中,默认不会执行  里面的语句。
  2. SDDM问题解决后,发现fcitx依赖的imsettings,会设置这三个变量,所以在  里面export的变量,其实会被imsettings覆盖。

Continue reading Fedora KDE 25下安装fcitx输入法

composer的自动加载机制解读

按照composer文档的说法,如果是composer项目,只需要在开始的时候require 'vendor/autoload.php'即可享受类的自动加载特性。可是这是如何实现的呢?

vendor/autoload.php

以Laravel 5.1项目为例,vendor/autoload.php文件只做了两件事情:

  1. include vendor/composer/autoload_real.php
  2. 调用ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b::getLoader()

vendor/composer/autoload_real.php仅仅定义了ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b类和composerRequireb6d254015e39cf5090fb84fdb1ed664b函数。(b6d254015e39cf5090fb84fdb1ed664b应该是类似id一样的东西,确保每次不同)

接下来我们关注下ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b::getLoader()做了哪些事情。

ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b::getLoader()

首先,这个类的loader只会初始化一次,第二次是直接返回已经存在的loader了:

如果是第一次调用,先注册['ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b', 'loadClassLoader'],然后new一个  作为loader,然后立马取消注册loadClassLoader

接下来就一步一步处理各种autoload了。

autoload_namespaces.php

直接从vendor/composer/autoload_namespaces.php中返回的就是$namespace$path的一个数组。通过\Composer\Autoload\ClassLoader::set方法来记录这些信息:

autoload_psr4.php

直接从该文件的返回值是$namespace$path的数组。通过\Composer\Autoload\ClassLoader::setPsr4来记录此信息:

autoload_classmap.php

直接从该文件的返回值是$classname$path的数组。通过\Composer\Autoload\ClassLoader::addClassMap来记录此信息:

这些都处理完之后,\Composer\Autoload\ClassLoader::register[$this, 'loadClass']注册为autoload函数(通过spl_autoload_register)。

autoload_files.php

直接从该文件的返回值是$fileIdentifier到文件路径的映射,通过之前定义的composerRequireb6d254015e39cf5090fb84fdb1ed664b函数来require每个文件。

这个函数在require文件的时候同时也设置了$GLOBALS['__composer_autoload_files'][$fileIdentifier]的值为true。

\Composer\Autoload\ClassLoader::loadClass

这个函数的作用是装载一个类。

\Composer\Autoload\ClassLoader::findFile

这个函数是composer装载类的重点。

  1. 首先是从classMap里面找$class对应的文件,如果有,直接返回。
  2. 然后从prefixDirsPsr4找到前缀符合的文件,如果找到,直接返回。(那个prefixLengthsPsr4就是用来判断需要从$class去掉的前缀长度)
  3. 接下来,直接从fallbackDirsPsr4对应的目录中查找文件。
  4. PSR-0加载
    • namespaced class name变换规则:  
    • PEAR-like class name变换规则:  

    prefixesPsr0中查找(和prefixDirsPsr4类似),直接找到对应的文件,返回。

  5. 如果没有,直接从fallbackDirsPsr0中尝试加载。

通过DNS认证来部署Let’s Encrypt

原来是通过http认证的方式来完成ACME 的 Identifier Validation Challenges,但是内网的机器就无法完成这个认证,今天看了下,LE支持dns认证了,所以实践了一下。

首先安装Certbot

然后执行

对于MacOS用户来说,可以执行  

  • 需要输入邮箱

  • 同意用户协议

  • 同意记录IP

  • 设置域名的TXT记录


    比如图中,设置_acme-challenge.ssl-test.robberphex.com的TXT记录为x-P6A_dQ4_ggZtPvX_bOUeaY7hSM_IS6o-Gzj3h7LBw,然后回车。

  • 提示证书生成成功


我们来一个最简版的配置:

curl测试(不需要设置DNS):

浏览器测试(需要设置DNS或者hosts文件):

证书有效

Continue reading 通过DNS认证来部署Let’s Encrypt

使用repox搭建sbt/maven镜像

最近sbt的速度实在是不能忍受了,所以使用repox搭建了sbt镜像。

搭建过程没有什么好说的,直接sbt assembly,然后

就好了。


但是发现了两个repox的问题:

  1. 下载文件时,服务器全部下载完后,才能传输给sbt(不支持nginx那种“流式代理”),这在下载大文件时尤其明显。
  2. 有的时候,pom文件总是404
    比如curl https://repo1.maven.org/maven2/org/w3c/css/sac/1.3/sac-1.3.pom -I的结果是200,curl http://106.75.27.110:8078/org/w3c/css/sac/1.3/sac-1.3.pom -I的结果就是404,重试好几次都不行;重启repox就好了。

Flume在hdfs产生大量日志文件的问题

线上两台flume向hdfs写日志,但是后来发现每5分钟产生的日志文件数量很多(远远超过两个的数量)。

后来找了一个时间观察下日志,发现如下日志:

16/12/13 11:38:11 INFO hdfs.BucketWriter: Closing idle bucketWriter hdfs://xxx/xx/xxx_log_20161213/.xxx_log.1481600173693.tmp at 1481600291480

根据日志找代码,发现是BucketWriter在配置了hdfs.idleTimeout的情况下,会在超时后关闭不活跃的日志文件

flume官方文档对此参数的描述如下:

Timeout after which inactive files get closed (0 = disable automatic closing of idle files)

找到原因后,当然就很简单了,由于此参数默认是0,直接删除这个参数的相关配置就好了。每5分钟产生的日志数量就是两个了。

为何不可使用Redis的KEYS命令

在Redis KEYS命令的文档中,有如下一句话:

consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases.

简而言之,KEYS命令性能很差,尽量不要在生产环境中使用。

不过官方文档也提到了,可以使用SCAN命令

那么,KEYS命令为什么性能差呢?

server.c中可以看到,KEYS命令被keysCommand函数处理

db本来就是一个dict,所以直接拿到一个dictIterator di,然后依次遍历,整个KEYS操作的复杂度是O(n),n为redis中key的数量。

这个操作,会阻塞到redis进程的。

可以看到,redis对于KEYS *这种情况做了优化。

Laravel Facade实现细节考

前两天有人讲Laravel中的Facade的时候,看到了__callStatic的实现,问为何如此,所以查了一番。

现有实现

switch实现

我们在调用Facede的方法的时候,绝大多数都会被__callStatic来处理,Larvel 5.1的__callStatic实现如下:

这个实现是 Taylor Otwell 最初实现的版本

cufa实现

是switch实现的简化版:

unpack实现

注意,Argument Unpacking 特性在php 5.6才实现。

这个是 Laravel 5.3 的实现。

实现对比

代码如下:

表中为在各个情况下调用 Facede::get 100000000次的秒数

参数数量 php70+switch php70+cufa php70+unpack php56+switch php56+cufa php56+unpack
0 33 39 34 74 107 65
1 38 46 37 84 117 75
2 41 48 38 91 118 80
3 44 52 38 108 125 89
4 50 49 39 118 137 94
5 58 50 41 189 146 99
6 61 53 42 194 155 107

结论

可以看到,在PHP 5.6下,switch实现比cufa实现,在参数个数比较少的时候,有很大的优势,估计这个也是作者在第一次实现的时候,选择switch实现,而不是更简单的cufa的原因吧。

另外,unpack在PHP 5.6下更快,但由于当时需要支持PHP 5.3,所以不能使用。在Laravel 5.3中,由于最低PHP版本为 5.6.4 ,所以可以大胆的使用unpack了。

此外,实际上, PHP 7.0下,unpack在没有参数时,比switch要慢,但是个人觉得差别不大。

最后,PHP 7.0 的速度提升还是很明显的。

参考资料

https://github.com/laravel/framework/pull/12120

https://gist.github.com/nikic/6390366

composer中指定依赖分支名的坑

之前只是看着别人写的composer.json,知道如果依赖一个项目的master分之,则在依赖的version中可以写dev-master。我就以为所有对分支的依赖,都是写成dev-<branch_name>。

后来发现,v1.x这种分支名,不能直接使用dev-v1.x 来声明依赖。

查了下,https://getcomposer.org/doc/02-libraries.md#branches

For every branch, a package development version will be created. If the branch name looks like a version, the version will be {branchname}-dev. For example, the branch 2.0 will get the 2.0.x-dev version (the .x is added for technical reasons, to make sure it is recognized as a branch). The 2.0.x branch would also be valid and be turned into 2.0.x-dev as well. If the branch does not look like a version, it will be dev-{branchname}. master results in a dev-master version.

翻译如下:

对于每个分支,对应的包都会创建一个开发版本。如果分支名看起来像一个版本号,则开发版本的名字是{branchname}-dev。

比如,分支2.0则会创建2.0.x-dev版本(后面的 .x 是为了技术原因添加,为了确保这个版本能够被识别为分支)。

2.0.x 分支也会被转换为 2.0.x-dev。如果分支看起来不像一个版本,版本号将会是dev-{branchname}。master分支的版本号是dev-master。


另外,当同时存在1.0和1.0.x分支时,1.0.x-dev指向的是1.0分支。

Laravel Migration 类名重复分析

开发者在开发中一般都会为Migration起类名,最常见的就是AlterUserTable这种名字,但是如果后续的开发者第二次修改表,就有了两个类名相同的Migration了。

这样有什么问题吗?

首先,这个Migration如果和之前类名相同的Migration不在同一批次的话,是可以执行成功的。

接下来看看为什么。

Migration的执行

从Migration的执行来看,\Illuminate\Database\Migrations\Migrator::getMigrationFiles拿到所有的Migration列表,通过和执行过的Migration array_diff之后,获取没有执行的Migration列表,在执行这些Migration的时候会在\Illuminate\Database\Migrations\Migrator::requireFiles方法中require对应的Migration文件,如果恰好两个类名相同的Migration需要执行,那么就会出现错误

Cannot declare class AlterTestTable, because the name is already in use in ./database/migrations/2016_07_20_081952_alter_test_table.php on line 31

当然,如果这些Migration都在不同批次中,那么永远也不会有这个错误发生了嘛。

Migration的回退

\Illuminate\Database\Migrations\MigrationRepositoryInterface::getLast获取最后一次执行的Migration文件名列表,然后在\Illuminate\Database\Migrations\Migrator::runDown方法中将Migration名字resolve到类名(比如 2016_07_20_081952_alter_test_tableAlterTestTable 类,这时候就通过new这个类,运行down方法来回退。

同样的,在这种情况下,如果有两个一样名字的类,则autoload机制只会选择一个。

注意,在这种情况下,即使两个Migration不在同一个批次当中回退,那也会有一个Migration永远不能回退。


  • 这也解释了为什么在本地创建了migration后,运行后,然后rollback的时候提示找不到这个类,而必须要dumpautoload才可以。
  • 由于Migration up的时候是通过Laravel自己解析到代码的,而down的时候是通过composer解析的,这导致了Migration的up和down不对称,确实不怎么好看。
  • 作为一个合格的开发,显然不应该让Migration类名重复!