2017年3月

php禁用eval

The eval() language construct is very dangerous because it allows execution of arbitrary PHP code.

eval是语言结构,不是函数,所以无法使用disable_functions来禁用

之前写过:从 php 内核挂载钩子解密源码,禁用的原理和这个差不多

static zend_op_array* guard_compile_string(zval *source_string, char *filename)
{
    // php_printf("s2: %s %Z\n", filename, source_string);
    if (strstr(filename, "eval()'d code")) {
        return NULL;
    }
    return old_compile_string(source_string, filename);
}

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(guard)
{
    old_compile_string = zend_compile_string;
    zend_compile_string = guard_compile_string;
    return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(guard)
{
    zend_compile_string = old_compile_string;
    return SUCCESS;
}

为什么要写这篇文章

主要是因为之前太浪了:

  1. MySQL之类的端口都是直接绑定到公网的(并没有进行防火墙限制)
  2. 博客的目录权限为了偷懒直接设置成了 0777

最主要的是产生的严重后果:今天写文章的时候突然发现文章附件多了个为归属的fileadmin.zip;瞬间菊花一紧,上服务器一看,各种web shell。

在线工具的各种配置更新的还是比较及时,端口也收的比较紧,review之后发现应该不会产生类似的问题;这个问题暂时出现在了博客的vps上。

后面怎么解决这样的问题

  1. 不向外暴露内部的端口
  2. php hook eval
  3. 及时同步最新的php配置等

其他问题

laravel 中使用了 jeremeamia/superclosure 包,而这个包中使用了eval,所以不能正常工作;这样就需要在上面的代码中做个白名单。

fastjson对范型的封装

为什么要有范型的封装

比如现在需要直接返回http接口或者缓存中的值反序列化之后的对象;如果只是在具体业务代码中反序列化这个字符串的话,那很简单;但是,如果想把这个反序列化的逻辑封装到common包的一个方法中呢?貌似业务代码copy过来是做不到类型可自定义的;

类型封装是什么意思,看下fastjson的文档中的例子,应该就明白了

public static <K, V> Map<K, V> parseToMap(String json, 
                                            Class<K> keyType, 
                                            Class<V> valueType) {
     return JSON.parseObject(json, 
                            new TypeReference<Map<K, V>>(keyType, valueType) {
                            });
}

// 可以这样使用
String json = "{1:{name:\"ddd\"},2:{name:\"zzz\"}}";
Map<Integer, Model> map = parseToMap(json, Integer.class, Model.class);

怎么实现

首先来张图理解一下,ParameterizedType TypeVariable 分别是什么

type.png

看一下fastjson的实现 (省略不需要的代码)

int actualIndex = 0;
for (int i = 0; i < argTypes.length; ++i) {
    if (argTypes[i] instanceof TypeVariable) {
        argTypes[i] = actualTypeArguments[actualIndex++];
        if (actualIndex >= actualTypeArguments.length) {
            break;
        }
    }
}

使用入参中的实际class TypeT模板进行替换,然后通过ParameterizedType来返回

引用

  1. fastjson/wiki

缓存那些事

浏览器缓存头

If-Modified-Since
If-None-Match
Last-Modified
Cache-Control
ETag
Expires

具体可以查看这篇文章中的附件 媒体中心设计分享

varnish / apache traffic server -> cdn

CSI: 指利用ajax等技术,将动态的数据使用异步的方式加载进页面 (比较适用于PC, H5)
SSI: 通常url后缀为shtml
ESI: 最具代表性的 varnish/ats (比较适用于App的接口)

具体可以查看这篇文章:页面静态化

上面的几种方案都需要走到后端的服务器,在并发和加载速度要求比较高的情况下,可以选择生成静态文件上传到cdn

local cache, redis, tair

多级缓存可以降低中心缓存服务器的压力,但是也会存在数据不一致的问题

当当网交易链路:简单的将local cache的过期时间设置为1分钟,降低缓存不一致的概率 (适用于一致性要求不高的情景)

缓存击穿的几种场景:

  1. 缓存过期失效
  2. 不存在的数据
  3. 缓存宕机

对于场景1,为避免瞬时流量将db和缓存击垮,可以使用一个锁,保证并发环境下,只有1个/少量线程写入同一条数据
对于场景2,可以使用empty object,在存取缓存的时候将其替换为null,如果为了池子中有效数据留存率,可以将empty object和正常数据分开存放
对于场景3,需事先脱离缓存,db裸压,保证在没有缓存的情况之后可以正常支持线上的流量 (可忍受的RT内)

pjax

对页面的局部更新,不过会将当前url塞到浏览器的历史记录中

具体可以查看这篇文章:slim框架中pjax的实现

bigpipe

利用服务器端的输出缓存,输出部分页面

缓存优化

在一台机器存不下1个业务所有缓存的时候,一般都会选择分片的策略(大多采用取模的办法),但有的时候缩减单个缓存对象的大小,也可以节省整个池子的资源

  1. 一般情况之下,key的重复度很高,可以选择缩减key的长度
  2. 在序列化的时候选择创建slim object,然后JSON.toJSONString()
  3. 使用gz/br压缩JSONString (考虑平滑兼容多种压缩方式,使用第一个字节作为标志位)