分类 技术学习 下的文章

使用 Docker 部署 Upsource

Upsource 是 Jetbrains 推出的一款用于 Code Review 的基于 Web 的系统,功能十分强大,最重要的是和 IDE 全家桶无缝集成,可以直接在 IDE 里进行 Code Review。

在选择 Upsource 之前我也调研了 Gerrit 和 Phabricator,觉得不太适合当前的团队。这两个工具更适用于强制 Code Review 并且把其作为 CI 的一环的团队使用,这自然是有好处,不过稍微有些繁琐。这两者的具体区别可以参考 https://stackoverflow.com/questions/10545480/gerrit-phabricator-review

使用 Docker 部署

使用 Docker 部署 Upsource 可以参考官方的文档 https://www.jetbrains.com/upsource/download/#section=docker

有几个注意点:

  • docker pull 的时候必须指定镜像版本号(也就是 .,见https://hub.docker.com/r/jetbrains/upsource/tags/),不然找不到
  • 在跑容器之前,记得把映射的那几个目录按照教程上 chown 一下
  • 配置 base url 时可以先按照能检测通过的配,后面可以通过修改 conf/internal/bundle.properties 来修改
  • 如果提示某个目录不为空,无法下一步的话,可以进目录看看有没有隐藏文件,如果有就全删了

不出意外的话应该就能正常跑起来了。

使用 nginx 配置反代

由于 Upsource 使用了 websocket,所以常规的反代配置可能有问题,直接参考官方文档就 ok 了,见 https://www.jetbrains.com/help/upsource/proxy-configuration.html#NginxConfiguration

server {
         listen  2222;
         server_name  localhost;
         location  / {
            proxy_set_header X-Forwarded-Host $http_host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_http_version 1.1;

            # to proxy WebSockets in nginx
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            proxy_pass http://upsourcemachine.domain.local:1111;
            proxy_pass_header Sec-Websocket-Extensions;
         }
      }

与 IDE 的整合

安装 Upsource Integration 插件即可,首先 Upsource 上先连接号该项目的 Git,然后本地打开的项目右下角点击 Upsource 图标就可以进行关联。

参考链接

http://blog.csdn.net/nikobelic8/article/details/54897314

使用 Docker 搭建 Gitlab + Jenkins + SonarQube 的 PHP 持续集成环境

对于开源 PHP 项目,现在比较成熟的一套持续集成方案是使用 Github + TravisCI + StyleCI + Scrutinizer + coveralls,不过这套方案如果想要用于私有项目的话就抓狂了,个个要买套餐,其中很多还不便宜。而且对于公司内使用的项目来说,内部搭建的 Gitlab 方案更为常见,对于这种情况,我们可以使用 Gitlab + Jenkins + SonarQube 来进行代替。

安装 SonarQube

$ docker pull postgres

$ docker run --name db -e POSTGRES_USER=sonar -e POSTGRES_PASSWORD=sonar -d postgres

$ docker pull sonarqube

$ docker run --name sq --link db -e SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar -p 9000:9000 -d sonarqube

执行完毕上面的命令后通过浏览器进入 SonarQube,默认用户名和密码都是 admin,进去后会有一段引导,里面会让你生成一个 access token,这个后面的配置 Jenkins 时会用到。

如果没有记下来的话,可以点右上角的用户头像里面的 My Account > Security 标签中可以生成一个新的。

配置 Jenkins

Jenkins 需要在全局的 系统设置 里面添加 SonarQube Server,填下对应的访问地址和上一步获取的 access token 即可。服务器地址填写 localhost 可能会有问题,填 ip 会比较好些。

然后需要在 系统管理 的 Global Tool Configuration 菜单中配置 SonarQube Scanner 安装,这个直接选择自动安装就好了,十分方便。

这两步配好之后就到对应的项目配置中添加构建步骤,下拉选择 Execute SonarQube Scanner,然后对于 2.1 版本以上的 SonarQube Scanner 就只需要配置 Analysis properties 这一项就可以了,比较常用的参数包括 sonar.projectKey (用来确定 该项目在 SonarQube 中叫什么名字) 和 sonar.sources=(用来指定需要扫描的目录)。

配完之后选择构建即可,可以去当前构建的 Console Output 里面查看有没有报错,正常执行完成的话,在 SonarQube 项目面板中就可以看到一个新增的命名为配置的 sonar.projectKey 的 项目了。

注意点:

  • SonarPHP 自定义检查规则需要用 java 来写扩展,比较新的版本内置了 psr2 的规则基本够用,内置的 Quality Profiles 是可以复制一个出来进行自定义的
  • Sonar 嗅探出的一些问题可能实际上并没有什么影响,比如变量名中含有 ‘pwd’ 等,如果原本使用方式确实合理则可适当忽略

参考链接:

常见接口加密方式整理

今天在新公司开了个关于公网接口安全性校验的讨论会,貌似这边做这种对公的接口比较少,所以组长就组织大家都参与了一下。

我之前做这种接口做得比较多,对服务端、客户端的都有,加密方式也比较多样,但是仔细想来自己从来没有进行过比较系统的总结。

首先我们组长总结了接口安全性验证的三个核心点:

1.请求不能被篡改
2.请求不能被重放
3.请求可以被溯源

我就针对这三点来分别说说为了实现这三点一般会采用哪些手段。

不能被篡改

这一点是最基础的,之前的实际工作中其实很大一部分接口都只做到了这一点。这一点也可以看作是鉴权(authentication)。

一般常见的对请求合法性进行校验的方式如下:

  • 将参数按一定规则进行拼接,然后加上加密用的 key (或者叫 secret)进行 md5、sha1 之类的 hash,将 hash 值作为签名放在参数或者 http 请求头中进行传递
  • 将参数进行 aes 加密,这种方式的话可以单独对参数值进行 aes 加密,也可以将所有的参数序列化后整个进行 aes 加密
  • RSA 非对称加密,这种方式一般将所有参数序列化后整段进行 RSA 加密,由于非对称加密的资源消耗较大,所以有些应用会采用通过非对称加密方式获取 secret,然后后续请求转为对称加密的方式
  • 其他自定义对称加密算法,这种一般也是把所有参数序列化后通过自定义对称加密算法将其转化成一个可以被解密的字符串进行传递

不能被重放

这一点是为了防止请求链接被拦截后,恶意攻击者通过重复相同的请求对系统造成危害。从根本上来说,防止重放是需要提供接口的服务端能保证对外接口的幂等性。

防止重放的最简易的一个方式是在请求参数中加入时间戳,在处理接口请求时验证时间戳是否过期,过期则判定请求失效。这种方式存在两个弊端,第一点就是允许的时间范围内重复请求仍然可以生效,另一点是林子大了什么鸟都有,不同公司的时间服务器可能存在不同步,或者说不是所有的公司时间都和北京时间同步。

对于仅进行读操作的接口来说由于本身就是幂等的,只要有时间戳其实就可以了,然而对于包含写操作的接口来说,显然单纯的时间戳并不能满足这个需求。不过加入时间戳还有一个用处是相当于加了一层缓存,重复的失效请求在最外层就被屏蔽了,不会给服务器增加业务处理逻辑的压力。

一般实现保障接口请求幂等的方式是在请求参数中增加一个唯一性字段作为标识符,如支付、加币等类似业务中的订单号(或流水号)的概念。

另外也有一些类型的接口中会用 nonce 之类的一次性令牌来防止请求重放,这种方式就和 oauth 协议中使用的 code 基本差不多。

可以被溯源

接口请求可以被溯源对很多应用场景其实都有用,比如:

  • 方便异常的排查,异常请求可直接找到对应请求人
  • 便于特殊情况,比如部分用户密钥泄漏等的处理
  • 对接口进行分用户授权(authorization)
  • 可以进行详细数据统计

比较常见的一些云服务的服务提供商的接口常用的方式就是提供给接口使用者对应的 app_id 和 app_secret(叫法各有不同),其中 app_id 就是接口使用者的身份标识符,该标识符随其他请求参数一起传递,供接口提供者确定请求来源,并取出该标识符对应的使用者的 app_secret 来进行接口的鉴权和授权判断。

常见的分库分表方式

这篇文章主要记录了我自己使用过的一些分库、分表、分区表的使用场景和方式。

分库

按时间分库

我们有很多时效性的运营活动,每次节日之类的都会有,复杂一些的活动一般都会有多张独立的表,而这些表在活动期结束后做完数据就基本没有用了。

对于这种场景,为了避免数据库中表的数量过多,影响主体业务,我们采用了按年分库的方式,把对应年费的活动表放到对应年费的库里。

按业务场景分库

前端面向用户的业务数据库一般会做读写分离,因为面向用户的数据库往往读写请求都很多,而面向业务人员的数据相关库则基本为读请求,一般我们也会把业务数据库和离线报表数据库分离开。

分表

按字段 hash、取模 等方式进行分表

这种方式一般适用于总量的增长比较可预估的情况,不会随着业务增长而不断爆发增加的情况,比如目前对于大多数公司来说,用户注册基本关联手机号等实名信息,增长不会过大,但放单表又怕存在性能问题的,即可采取这种方式。

这种场景下分表方案最基本的就是按 id 进行 hash、取模 等,也有更复杂的加入其他字段作为 seed 以便于登录等的方案,具体视实际情况进行选择。

按时间进行分表

这种方式适用于数据随时间不断增长的情况,比如一些用户行为日志等等,大多数情况下可以进行按月分表,每月数据从对应月份的表中进行获取,后台报表等用到的字段计算后放入折叠表这样的方式。

分区表

目前我对分区表的使用比较简单,基本是在一些相对来说重要性不是太高的业务场景下使用的,比如接口调用日志表,按月分区,一定时间之前的分区备份后进行删除,当前表中就一直保持近期的数据即可。

在原有的 Yii1.1 项目中使用 Yii2 框架的经验记录

目前公司由于历史原因,之前很多主体项目都是使用 Yii1.1 开发的,Yii1.1 在使用上还是有很多不便,为了使用 Yii2 的一些新特性,我决定在一个规模比较小的项目上尝试 Yii1.1 和 Yii2 共存并逐渐使用 Yii2 替代原有 Yii1.1 代码进行项目升级的方案。

主要的参考文档在 google 上搜了下就只有官方文档的一节 Using Yii 2 with Yii 1

最主要的是创建一个自定义的 Yii 类文件,这个类继承自 Yii2 的 BaseYii 类,而类里面需要手工把 Yii1.1 的 YiiBase 类中的代码复制过来,这样这个类就同时拥有 Yii1.1 和 Yii2 的属性和方法了。
然后最下面三行是为了能在项目中使用 Yii2 的自动加载机制和依赖注入容器,一定不能写错。

$yii2path = '/path/to/yii2';
require($yii2path . '/BaseYii.php'); // Yii 2.x

$yii1path = '/path/to/yii1';
require($yii1path . '/YiiBase.php'); // Yii 1.x

class Yii extends \yii\BaseYii
{
    // 这里要把 Yii1.1 中 YiiBase 类里的代码全部复制过来
}

Yii::$classMap = include($yii2path . '/classes.php');
// 通过 Yii1.1 的方法注册 Yii2 的自动加载器
Yii::registerAutoloader(['yii\BaseYii', 'autoload']);
// 创建依赖注入容器
Yii::$container = new yii\di\Container();

这个文件我们可以放在 components\Yii.php,然后我们就需要修改项目原先的 index.php 入口文件了,修改过后的关键代码如下:

// 引入上一步创建好的自定义 Yii 类文件
require(__DIR__ . '/../components/Yii.php');

// 首先读取 Yii2 的配置,实际配置可能更多,这里只是参考
$yii2Config = require(__DIR__ . '/../config/yii2/web.php');
// 不要调用 run() 方法,Yii2 只作为服务定位器使用
new yii\web\Application($yii2Config);

// 读取 Yii1.1 配置,实例化 Application 并运行
$yii1Config = require(__DIR__ . '/../config/yii1/main.php');
Yii::createWebApplication($yii1Config)->run();

配好这些就可以开始使用了,项目的目录组织的话可以根据实际项目情况而定,在编写 Yii2 代码时,我们可以无视原来的 Yii1.1 的所有代码,将项目作为一个常规的 Yii2 项目来看,这样的话目录结构就很清晰了。

这种方案要求 php 版本至少在 5.4 以上,我建议将 Yii1.1 的框架版本升级到最新的 1.1.19 这个版本可以支持 php7.0 和 php7.1。

总的来说并不困难,不过参考资料较少,实际使用过程中可能会遇到坑点,不过也都比较好解决,老项目这样一配可以说是枯木逢春,写起新功能来要舒服太多了。