[转]阿里巴巴开源项目TAC——新的服务端开发模式尝试

尝试

TAC是一个基于java的微服务容器,提供从业务代码编写、编译、发布、jar动态加载、运行等一系列常用开发流程的支持,是天猫App在服务端开发模式下的新尝试。TAC和客户端框架Tangram结合,极大提高开发效率;TAC目前在天猫App、手机淘宝特价版广泛使用;

疼痛之初——产生背景

天猫App(以下简称猫客)首页从2015年的坑位运营走向2016年的全面个性化,当时猫客首页的个性化业务多大50多处。以首页为例,这个过程中除了接入导购链路的二方服务之外,接入了大量的三方服务;同时我们发现:

  • 首页应用越来越复杂、庞大,应用代码修改、编译部署耗费时间太长,大部分时间都是苦力劳动;
  • 线上定位问题周期太长,从问题发现到定位再到处理,需要经过客户端、服务端、下游各种服务提供方,链路太长影响业务快速支撑;
  • 大量服务的接入导致发布线上部署频繁,一个简单服务的接入,几行代码的修改导致首页应用重新开发编译部署,影响整个首页稳定性;

当时微服务的概念已经很火,同时阿里内部也开始大力推广docker(现在大部分应用都跑在docker上)。我们想过将庞大的首页应用拆分成微服务,但是和基础服务不同,前台业务变化快,修改频繁,拆分之后开发同学依然要面临各种服务接入等体力劳动,同时需要维护拆分之后的多个应用,反而增加了劳动成本,因此拆分成微服务的方式治标不治本;


解决之道——TAC

在此背景下TAC孕育而生,它提供低成本开发与发布流程、低成本搭建与维护开发环境、高稳定性保障;TAC通过热部署的方式使得研发同学够从苦力劳动中解放出来,回归到业务开发中去,同时一个基础服务接入之后,能够提供给多个业务使用;在此模式下,业务能够进行更细粒度的拆分,且故障隔离业务A的改动不会影响业务B;

在TAC的帮助下,频繁修改的新业务可快速上线,不会出现因为修改一个字段、几行代码就需要重新发布整个应用的情况;同时与tangram结合,实现页面卡片、坑位的快速调整;


经过近三年的沉淀,我们今日放出了开源版本,将集团版本的TAC剥离与阿里相关的中间件、网络、协议、部署环境等,保留其核心功能;开源版本提供了编译、热加载、运行的基础能力;

TAC开源版本

系统结构


  • 开源版本分两部分,tac容器和tac控制台,存储和通信都依赖redis。
  • 为了简便,容器与外部服务直接的交互只通过http进行;
  • 同时为了提升开发体验,提供了与gitlab集成的能力,用户可直接gitlab提交代码并在控制台操作发布;快速验证;

核心类加载器


上图是tac的类加载器结构,每个线上的微服务实例都通过一个新的classloader加载,同时为了方便用户扩展新的数据源,在AppClassLoader上扩展了一个classloader以加载第三方数据源(当然也可以直接在代码中扩展,见tac-infrastructure);

Quick Start —— 如何使用

  • 安装 redis
  • 运行 container
java -jar tac-container.jar
  • 运行 console 控制台
java -jar tac-console.jar --admin
  • 成功后可打开控制台
http://localhost:7001/#/tacMs/list
  • 代码开发
    • 添加 SDK 依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>tac-sdk</artifactId>
    <version>${project.version}</version>
</dependency>
  • 编写代码
public class HelloWorldTac implements TacHandler<Object> {

    /**
     * 引入日志服务
     */
    private TacLogger tacLogger = TacServiceFactory.getLogger();

    /**
     * 编写一个实现TacHandler接口的类
     *
     * @param context
     * @return
     * @throws Exception
     */

    @Override
    public TacResult<Object> execute(Context context) throws Exception {

        // 执行逻辑
        tacLogger.info("Hello World");

        Map<String, Object> data = new HashMap<>();
        data.put("name", "hellotac");
        data.put("platform", "iPhone");
        data.put("clientVersion", "7.0.2");
        data.put("userName", "tac-userName");
        return TacResult.newResult(data);
    }
}
  • 本地编译、打包
  • 发布及测试

  • 正式发布
  • 线上验证
curl  http://localhost:8001/api/tac/execute/helloworld -s|json
{
  "success": true,
  "msgCode": null,
  "msgInfo": null,
  "data": {
    "helloworld": {
      "data": {
        "name": "hellotac",
        "clientVersion": "7.0.2",
        "userName": "tac-userName",
        "platform": "iPhone"
      },
      "success": true,
      "msCode": "helloworld"
    }
  },
  "hasMore": null,
  "ip": "127.0.0.1"
}

Git 私有服务器搭建

有时候需要自己搭建一台Git服务器作为私有仓库使用。

接下来我们将以 Centos 为例搭建 Git 服务器。

1、安装Git

$ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel
$ yum install git

接下来我们 创建一个git用户组和用户,用来运行git服务:

$ groupadd git
$ useradd git -g git

2、创建证书登录

收集所有需要登录的用户的公钥,公钥位于id_rsa.pub文件中,把我们的公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。

如果没有则需要自己产生一个 ssh-keygen 在.ssh/id_rsa.pub 产生公钥

如果没有该文件创建它:

$ cd /home/git/
$ mkdir .ssh
$ chmod 755 .ssh
$ touch .ssh/authorized_keys
$ chmod 644 .ssh/authorized_keys

3、初始化Git仓库

首先我们选定一个目录作为Git仓库,假定是/home/gitrepo/runoob.git,在/home/gitrepo目录下输入命令:

$ cd /home
$ mkdir gitrepo
$ chown git:git gitrepo/
$ cd gitrepo

$ git init --bare runoob.git
Initialized empty Git repository in /home/gitrepo/runoob.git/

以上命令Git创建一个空仓库,服务器上的Git仓库通常都以.git结尾。然后,把仓库所属用户改为git:

$ chown -R git:git runoob.git

4、克隆仓库

$ git clone git@192.168.45.4:/home/gitrepo/runoob.git
Cloning into 'runoob'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.

192.168.45.4 为 Git 所在服务器 ip ,你需要将其修改为你自己的 Git 服务 ip。

这样我们的 Git 服务器安装就完成。

本地操作:

 

1,git clone git@192.168.45.4:/home/gitrepo/runoob.git 
2,git init
3, git remote add origin git@192.68.45.3:/home/gitrepo/runoob.git
4, 创建文件touch README.MD
5,git add README.MD
6,git commit -m "add first file"
5,git push -u origin master
(仅第一次推分支需要-u)

Advanced Usage: Admin Server

link:https://docs.hhvm.com/hhvm/advanced-usage/admin-server

The admin server allows the administrator of the HHVM server to query and control the HHVM server process. It is different and separate than the primary HHVM server that you specified with -m server or -m daemon.

To turn on the admin server, you specify the following options at the command line via -d or within your server.ini (or equivalent).

hhvm.admin_server.port=9001
hhvm.admin_server.password=SomePassword

The port can be any open port. And you should always specify a password to secure the admin port since you don’t want just anybody being able to control your server. In fact, you will probably want to put the admin server behind a firewall. You will specify the password with every request to the admin port.

The admin server uses the same protocol as the main server – so, if you’re using FastCGI mode, the admin server will also be FastCGI, and you will need to configure a front-end webserver (like nginx). If you are using Proxygen mode, the admin server will be an HTTP server.

Querying the Admin Server

Once you have set up your admin server, you can query it via curl.

curl http://localhost:9001/

will bring up a list of commands you can use to control and query your admin server.

The port associated with the curl command is the hhvm.admin_server port set above if you are using ProxygenIf you are using FastCGI, then the port will be the webserver port that is the front end to FastCGI.

Sending a Command

Use one of the commands listed with the curl sequence above, along with your password, to send a command to the admin server.

curl http://localhost:9001/compiler-id?auth=SomePassword

Further Reference

There is a good blog post discussing the admin server even further.

[转]Linux系统安装w3af

  w3af这个工具是扫描网站漏洞,比如 SQL注入、盲注、本地\远程文件包含、跨站脚本攻击、跨站伪造请求等。

找了一些正规的工具定义介绍:w3af是一个Web应用安全的攻击、审计(分析)平台,通过增加插件来对功能进行扩展,这是一款用python写的工具,支持GUI,也支持命令行模式。

w3af目前已经集成了非常多的安全审计及攻击插件,并进行了分类,用户在使用的时候,可

以直接选择已经分类好的插件,只需要填写上URL地址即可对目标站点进行安全审计,并且集成了一些好用的小工具,如自定义request功能、Fuzzy

request功能、代理功能、加解密功能,支持非常多的加解密算法,用户完全可以使用w3af完成对一个网址的安全审计(分析)工作。

《w3af用户手册》英文版本于2012年8月8日发布,由Andres Riancho编写,Javier Andalia、Mike Harbison、Andy Bach、Chris Teodorski审阅。中文版本于2013年4月3日发布,由IDF实验室研究员lenchio翻译,研究员做个好人校对、修改、制作,实习生Leo亦参与了文档校对工作。

可以通过连接下载.有windows、Linux版本.

https://sourceforge.net/projects/w3af/files/w3af/

还有另外一种方法……

sudo apt-get install w3af         即可

本次教程讲的是Ubuntu下git从GitHub安装….

https://github.com/andresriancho/w3af

命令如下:还是觉得图形界面直观….

git clone –depth 1 https://github.com/andresriancho/w3af.git

cd w3af

./w3af_gui

到这一步后,会提示你安装某些工具,安装好后,就可以启动了。祝你好运!!!

作者:不着调的小男生
链接:https://www.jianshu.com/p/a4584e7d0d25
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

[Re:]A Stick Figure Guide to the Advanced Encryption Standard (AES)

(A play in 4 acts. Please feel free to exit along with the stage character that best represents you. Take intermissions as you see fit. Click on the stage if you have a hard time seeing it. If you get bored, you can jump to the code. Most importantly, enjoy the show!)

Act 1: Once Upon a Time…

intro
sad
aes act 1 scene 03 cinderella
aes act 1 scene 04 started
aes act 1 scene 05 judge
aes act 1 scene 06 nbs decree
aes act 1 scene 07 lucifer
aes act 1 scene 08 anoint des
aes act 1 scene 09 des ruled
aes act 1 scene 10 des defeated
aes act 1 scene 11 triple des
aes act 1 scene 12 nist decree
aes act 1 scene 13 rallied
aes act 1 scene 14 rijndael
aes act 1 scene 15 vote
aes act 1 scene 16 won
aes act 1 scene 17 intel
aes act 1 scene 18 crypto question

Act 2: Crypto Basics

aes act 2 scene 01 three big ideas
aes act 2 scene 02 confusion
aes act 2 scene 03 diffusion
aes act 2 scene 04 key secrecy
aes act 2 scene 05 aes details question

Act 3: Details

aes act 3 scene 01 sign this
aes act 3 scene 02 agreement
aes act 3 scene 03 state matrix
aes act 3 scene 04 initial round
aes act 3 scene 05 xor tribute
aes act 3 scene 06 key expansion part 1
aes act 3 scene 07 key expansion part 2a
aes act 3 scene 08 key expansion part 2b
aes act 3 scene 09 key expansion part 3
aes act 3 scene 10 intermediate round start
aes act 3 scene 11 substitute bytes
aes act 3 scene 12 shift rows
aes act 3 scene 13 mix columns
aes act 3 scene 14 add round key
aes act 3 scene 15 final round
aes act 3 scene 16 more rounds the merrier
aes act 3 scene 17 tradeoffs
aes act 3 scene 18 security margin
aes act 3 scene 19 in pictures
aes act 3 scene 20 decrypting
aes act 3 scene 21 modes
aes act 3 scene 22 questions what really happens
aes act 3 scene 23 math

Act 4: Math!

aes act 4 scene 01 algebra class
aes act 4 scene 02 reviewing the basics
aes act 4 scene 03 algebra coefficients
aes act 4 scene 04 remember multiplication growth
aes act 4 scene 05 cant go bigger
aes act 4 scene 06 clock math
aes act 4 scene 07 clock math polynomials
aes act 4 scene 08 divide by mx
aes act 4 scene 09 logarithms
aes act 4 scene 10 using logarithms
aes act 4 scene 11 polynomial as byte
aes act 4 scene 12 byte operations
aes act 4 scene 13 byte inverses
aes act 4 scene 14 sbox math
aes act 4 scene 15 round constants
aes act 4 scene 16 mix columns math
aes act 4 scene 17 crib sheet
aes act 4 scene 18 got it now
aes act 4 scene 19 so much more
aes act 4 scene 20 gotta go
aes act 4 scene 21 the end

Epilogue

I created a heavily-commented AES/Rijndael implementation to go along with this post and put it on GitHub. In keeping with the Foot-Shooting Prevention Agreement, it shouldn’t be used for production code, but it should be helpful in seeing exactly where all the numbers came from in this play. Several resources were useful in creating this:

Please leave a comment if you notice something that can be better explained.

Update #1: Several scenes were updated to fix some errors mentioned in the comments.
Update #2: By request, I’ve created a slide show presentation of this play in both PowerPoint and PDFformats. I’ve licensed them under the Creative Commons Attribution License so that you can use them as you see fit. If you’re teaching a class, consider giving extra credit to any student giving a worthy interpretive dance rendition in accordance with the Foot-Shooting Prevention Agreement.

[转]Lumen和Laravel错误处理机制修改

在使用Laravel或者Lumen时会碰到这种情况,如果php的代码中产生了Notice或者Warning,会导致Lumen跳到错误页,日志中会打印一个很长很长的stack trace,如下图:

$app->get('/test', function () use ($app) {
	$arr = [];
	print($arr['name']);
});
#请求/test 产生的日志
[2017-07-12 11:55:54] lumen.ERROR: ErrorException: Undefined index: name in /home/vagrant/Code/featurestream/routes/web.php:16
Stack trace:
#0 /home/vagrant/Code/featurestream/routes/web.php(16): Laravel\Lumen\Application->Laravel\Lumen\Concerns\{closure}(8, 'Undefined index...', '/home/vagrant/C...', 16, Array)
#1 [internal function]: Closure->{closure}()
#2 /home/vagrant/Code/featurestream/vendor/illuminate/container/BoundMethod.php(29): call_user_func_array(Object(Closure), Array)
....

上面的日志只截取了一小部分,实际运行时最简单的api请求Lumen的stack trace会有30层左右,Laravel的会有60层..
另外一个重要的问题是,这种处理机制会让一些小错误把整个请求搞挂,代码中到处加if(isset($arr['xx'])) 或者设置默认值。
对于这个问题,Laravel和Lumen设计的初衷是好的:所有的PHP错误都应该被处理,包括Notice和Warning。就是太严格了,有时候我们并不需要代码有这么严格的检查,出现Notice或者Warning时,只需要打印个模块日志或者有PHP日志就行。
先看下Lumen中的错误是如何处理的(Laravel中也差不多,不再单独讲)。

错误处理流程

错误Handler

先看Lumen的入口:bootstrap/app.php,有一段错误处理相关的代码:

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

singleton是往app(Lumen的服务容器)里注入实例的方法,这里实例化了一个ExceptionHandler类,实例为Handler。后面的代码如果有从$app里取ExceptionHandler的实例的话,会返回Handler这个类的实例。
Handler类在App\Exceptions目录下,代码如下。比较简单,只包含两个方法,再去看父类(Laravel\Lumen\Exceptions\Handler)的方法逻辑会发现,report方法负责打印日志(也就是上文那个长长的trace),render方法会根据错误类型的不同构建错误页面

public function report(Exception $e)
{
    parent::report($e);
}

public function render($request, Exception $e)
{
    return parent::render($request, $e);
}

错误的处理逻辑找到了,如何触发进入这个逻辑的呢?

错误触发

正常情况下,PHP产生Notice或者Warning是不会抛出Exception的,会产生Exception肯定是框架内部做了更改。
bootstrap/app.php中没找到设置错误处理的地方,接着往下看Lumen框架的容器类Application(Lumen的核心类和入口),目录:vendor/laravel/lumen-framework/src/Application.php
可以看到在其构造函数中调用了一个registerErrorHandling方法,方法代码:

/**
 * Set the error handling for the application.
 *
 * @return void
 */
protected function registerErrorHandling()
{
    error_reporting(-1);

    set_error_handler(function ($level, $message, $file = '', $line = 0) {
        if (error_reporting() & $level) {
            throw new ErrorException($message, 0, $level, $file, $line);
        }
    });

    set_exception_handler(function ($e) {
        $this->handleUncaughtException($e);
    });

    register_shutdown_function(function () {
        $this->handleShutdown();
    });
}

set_error_handler是PHP设置错误处理的方法。registerErrorHandling下用error_reporting(-1)把PHP的报错开关都打开,这样所有级别的错误都会触发error_handler。在error_handler中抛出了一个ErrorException异常。
至此,我们知道ErrorException这个异常是怎么产生的了,知道异常会交由谁来处理了(上一节中的Handler类)。不过还有个疑问,ErrorException和Handler是怎么关联起来的?

ErrorException和Handler

set_error_handler设置的方法中会抛出异常,那肯定存在针对异常的try catch块。
从请求路口public/index.php往下看,
–>bootstrap/app.php –>Application.php -> run –>Application.php -> dispatch
在dispatch方法中发现了try catch的逻辑,在catch到Exception后调用了Handler的report和render方法。

public function dispatch($request = null)
{
   list($method, $pathInfo) = $this->parseIncomingRequest($request);
   try {
			...
   } catch (Exception $e) {
       return $this->prepareResponse($this->sendExceptionToHandler($e));
   } catch (Throwable $e) {
       return $this->prepareResponse($this->sendExceptionToHandler($e));
   }
}
protected function sendExceptionToHandler($e)
{
   $handler = $this->resolveExceptionHandler();

   if ($e instanceof Error) {
       $e = new FatalThrowableError($e);
   }

   $handler->report($e);

   return $handler->render($this->make('request'), $e);
}

解决

知道了错误从触发到结束的整个流程,再来看怎么解决.

方法1.屏蔽错误

简单粗暴的方法,既然框架用来error_reporting(-1)打开了所有错误开关,那我们再用error_reporting把我们不关心的错误给屏蔽了。把下面代码加在bootstrap/app.php中,注意得加在Applition实例化之后

error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);

这种方法的弊端显而易见,我们只是不想让E_NOTICE搞挂请求,但是这类错误还是得关注和修复的。。

方法2.修改Handler

既然是Handler负责错误处理,那我们修改Handler(App\Exceptions\Handler)的逻辑就行。

public function report(Exception $e)
{
    //parent::report($e);
    Log::warning($e->getMessage(), ['file' => $e->getFile(), 'line' => $e->getLine()]);
}

public function render($request, Exception $e)
{
    //return parent::render($request, $e);
    header('Content-type: application/json');
    echo json_encode(['errmsg' => $e->getMessage()]);
    return ;
}

如上代码,将错误处理逻辑进行更改,只打印简单的日志,并且也不跳到错误页了。
不过,这种方法还是有问题:

  1. 代码产生的NOTICE和WARNING还是会中断执行流程(因为会抛出异常)。比如一个请求中执行A->B->C三个方法,如果B中产生了一个NOTICE,整个请求还是会被中断,C不会被执行。
  2. 如果一个请求原本不是返回json,是返回一个view,则上面的render方法就不适用了。

方法3.在业务逻辑顶层中catch异常

原理类似方法2,只不过把异常的处理从Handler中移到了业务逻辑里(比如Controller中)。
这个方法的问题和方法2一样,NOTICE还是会中断请求…

方法4.修改error_handler,不抛出异常

罪魁祸首就在于那个set_error_handler注册的处理方法遇到PHP错误就会抛出异常,那我们修改他就行。
set_error_handler所在的registerErrorHandling方法在框架的源代码中,最好不要直接修改,我们可以在它注册完之后再重新注册一个覆盖它。可以加在bootstrap/app.php中,Application实例化之后。代码如下:

set_error_handler(function ($level, $message, $file = '', $line = 0) {
    if ($level == E_NOTICE || $level == E_WARNING) {
        Log::warning("PHP NOTICE or WARNING; MSG:[$message]", ['file' => $file, 'line' => $line]);
        return;
    }
    if (error_reporting() & $level) {
        throw new ErrorException($message, 0, $level, $file, $line);
    }
});

注意,代码中因为使用了Log这个Facades,因此必须放在$app->withFacades()之后
至此,NOTICE和WARNING不会产生烦人的日志,也不会搞挂请求,并且也保留了有效的提示信息。:)

苹果手游代充灰色产业深度揭秘

[转]http://www.freebuf.com/articles/paper/157719.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

苹果手游代充最早可以追溯到2012年前后,到现在已经经历了多次发展,从最开始的外币汇率差,退款,36漏洞,再到现在黑卡,盗刷信用卡,甚至出现了专门的库存系统。据圈内人士透露,许多人靠这个赚了成百上千万,然而游戏厂商却损失惨重,苹果一直也没彻底打击掉这些乱象。本文从目前最新的库存系统说起,深度揭秘整个产业链。

一、 一切罪恶的源泉——苹果内购机制

代充行业之所以可以持续这么久,根本原因就是苹果内购机制的问题,要是做到支付宝一样就没问题了。所以内购机制一日不改,代充就不会消失,但是苹果爸爸会改吗?可能性很小,其牵扯的利益太多。这个机制直接导致了外币绕过漏洞,36小额漏洞,凭据替换漏洞。

二、 行业的巅峰——库存系统

某游戏厂商朋友问我,“36技术不是被苹果禁止了吗,游戏也做禁止外币了,为什么还是能收到外币,这个月的坏账又是一千多万,现在要怎么预防”?其实,道高一尺魔高一丈 ,聪明的充值商家早就有对应的办法了。

库存系统,顾名思义,就是存货的仓库。

苹果内购流程如下图所示,我们可以看到,第8步,苹果在用户AppStore帐号里面正常扣款后,会返回一个凭据给游戏,游戏收到凭据后,再到苹果校验,无误就发放道具。然而,库存系统会在苹果返回凭据给游戏的时候,把这个凭据拦截并保存起来,不让游戏现在发放道具。同样的道理,用技术手段先模拟1-7步的流程,到8的时候,把保存的凭据发送给游戏程序,游戏收到凭据后,正常发货,这样就实现了凭据的使用。

苹果手游代充灰色产业深度揭秘

据圈内人士的描述,库存系统是这样的,首先有两个插件(要系统越狱才可以用),一个专门用的存货(凭据拦截),另外一个用来发货(凭据替换),货是存在系统后台的,有玩家要充值的时候再使用,非常方便。现在市面上有4-5家这样的系统,价格每个月几千到上万不等。

下图可以看到,这类系统几乎支持所有苹果的应用程序

苹果手游代充灰色产业深度揭秘

甚至还出现了交易系统,商家可以自由买卖凭证,里面的价格很低

苹果手游代充灰色产业深度揭秘

说道这里,大家肯定会有一个疑问,我直接用苹果帐号里面的钱不就可以了,为什么要把凭据拦截保存起来,再使用,不是多此一举吗?

其实,库存系统最大的作用是绕过苹果帐号的风控系统。代充商家的价格之所以比原价低很多,主要因为用的都是黑卡(非法渠道获得的低价苹果礼品卡)或盗刷信用卡这些非法的方式,但是苹果的风控系统检测到这些非法行为,会在很短的时间内进行封号,这样苹果帐号里面的钱就打水漂了。然而,苹果的封号不是秒封的,这里有个时间差,比如15分钟,但是充值商家要在这么短的时间内把帐号里面的钱用完难度也很大。这时用的库存系统就可以秒把苹果帐号里面的钱用完,以凭据的形式保存在系统里面,随时可以用。即使苹果封号了,也不影响,凭据还是可以用,苹果不会封凭据!这样便能绕过苹果风控系统,实现完美套现,但是这些黑钱会被苹果扣除,无法给游戏厂商结算,最后就成了坏账。

总结下,库存系统保存的就是苹果的消费凭据,充值商家等到有客户时候,可以随时使用,可谓完美绕过苹果风控,使黑卡和盗刷可以大规模实现,让供货和销售分开,降低了行业进入的门槛,更加细分了产业链,放大了黑卡和盗刷的影响。库存系统还能绕过大多数游戏的外币检测,甚至充值游戏里已经下架的面值,比如之前某款游戏已经下架了30的面值,库存系统用技术手段依然可以充,让游戏厂商深受其害。

三、 现阶段游戏厂商如何应对

据圈内人士透露,现在网易游戏是查的最严格的,发现代充就会扣回游戏道具,圈内几乎没人敢做,但是,据了解网易在这方面也是下了血本,一般小团队也无法企及。这里有几个简单建议,可以把损失最小化。

1. 针对库存系统:正常充值的玩家,苹果凭据生成的时间和游戏内下订单的时间不会差很多,但是如果用的库存系统,这个时间差必然会大很多,这样基本就可以确定是代充了。

2. 对于外币的问题:苹果已经提供的相关功能,可以关闭外币充值,库存系统也无法绕过,只要开启即可。或者通过苹果票据里面的信息也可以判断。

3. 游戏自身风控:早期苹果充值非常不方便,代充可能还能帮游戏增加一些用户。但是现在苹果已经支持微信和支付宝,这方面完全不用担心,一定要坚决打击。方法一般通过检测低版本越狱系统充值,ip登录异常等。

4. 发现淘宝有代充商家,可以向淘宝发律师函等手段令其下架,并对玩家到官方渠道充值进行引导,营造良好的游戏环境。

[转]小白科普:Netty有什么用?

随着移动互联网的爆发性增长,小明公司的电子商务系统访问量越来越大,由于现有系统是个单体的巨型应用,已经无法满足海量的并发请求,拆分势在必行。

在微服务的大潮之中, 架构师小明把系统拆分成了多个服务,根据需要部署在多个机器上,这些服务非常灵活,可以随着访问量弹性扩展。

世界上没有免费的午餐, 拆分成多个“微服务”以后虽然增加了弹性,但也带来了一个巨大的挑战:服务之间互相调用的开销

比如说:原来用户下一个订单需要登录,浏览产品详情,加入购物车,支付,扣库存等一系列操作,在单体应用的时候它们都在一台机器的同一个进程中,说白了就是模块之间的函数调用,效率超级高

现在好了,服务被安置到了不同的服务器上,一个订单流程,几乎每个操作都要越网络,都是远程过程调用(RPC), 那执行时间、执行效率可远远比不上以前了。

远程过程调用的第一版实现使用了HTTP协议,也就是说各个服务对外提供HTTP接口。 小明发现,HTTP协议虽然简单明了,但是废话太多,仅仅是给服务器发个简单的消息都会附带一大堆无用信息:

GET /orders/1 HTTP/1.1

Host: order.myshop.com

User-Agent: Mozilla/5.0 (Windows NT 6.1; )

Accept: text/html;

Accept-Language: en-US,en;

Accept-Encoding: gzip

Connection: keep-alive

……

看看那User-Agent,Accept-Language ,这个协议明显是为浏览器而生的!但是我这里是程序之间的调用,用这个HTTP有点亏。

能不能自定义一个精简的协议? 在这个协议中我只需要把要调用方法名和参数发给服务器即可,根本不用这么多乱七八糟的额外信息。

但是自定义协议客户端和服务器端就得直接使用“低级”的Socket了,尤其是服务器端,得能够处理高并发的访问请求才行。

小明复习了一下服务器端的socket编程,最早的Java是所谓的阻塞IO(Blocking IO), 想处理多个socket的连接的话需要创建多个线程, 一个线程对应一个。

这种方式写起来倒是挺简单的,但是连接(socket)多了就受不了了,如果真的有成千上万个线程同时处理成千上万个socket,占用大量的空间不说,光是线程之间的切换就是一个巨大的开销。

更重要的是,虽然有大量的socket,但是真正需要处理的(可以读写数据的socket)却不多,大量的线程处于等待数据状态(这也是为什么叫做阻塞的原因),资源浪费得让人心疼。

后来Java为了解决这个问题,又搞了一个非阻塞IO(NIO:Non-Blocking IO,有人也叫做New IO), 改变了一下思路:通过多路复用的方式让一个线程去处理多个Socket。

这样一来,只需要使用少量的线程就可以搞定多个socket了,线程只需要通过Selector去查一下它所管理的socket集合,哪个Socket的数据准备好了,就去处理哪个Socket,一点儿都不浪费。

好了,就是Java NIO了!

小明先定义了一套精简的RPC的协议,里边规定了如何去调用一个服务,方法名和参数该如何传递,返回值用什么格式……等等。然后雄心勃勃地要把这个协议用Java NIO给实现了。

可是美好的理想很快被无情的现实给击碎, 小明努力了一周就意识到自己陷入了一个大坑之中,Java NIO虽然看起来简单,但是API还是太“低级”了,有太多的复杂性,没有强悍的、一流的编程能力根本无法驾驭,根本做不到高并发情况下的可靠和高效。

小明不死心,继续向领导要人要资源,一定要把这个坑给填上,挣扎了6个月以后,终于实现了一个自己的NIO框架,可以执行高并发的RPC调用了。

然后又是长达6个月的修修补补,小明经常半夜被叫醒:生产环境的RPC调用无法返回了! 这样的Bug不知道改了多少个。

在那些不眠之夜中,小明经常仰天长叹:我用NIO做个高并发的RPC框架怎么这么难呐!

一年之后,自研的框架终于稳定,可是小明也从张大胖那里听到了一个让他崩溃的消息: 小明你知道吗?有个叫Netty的开源框架,可以快速地开发高性能的面向协议的服务器和客户端。 易用、健壮、安全、高效,你可以在Netty上轻松实现各种自定义的协议!咱们也试试?

小明赶紧研究,看完后不由得“泪流满面”:这东西怎么不早点出来啊!

好了,这个故事我快编不下去了,要烂尾了。

说说Netty到底是何方神圣, 要解决什么问题吧。

像上面小明的例子,想使用Java NIO来实现一个高性能的RPC框架,调用协议,数据的格式和次序都是自己定义的,现有的HTTP根本玩不转,那使用Netty就是绝佳的选择。

其实游戏领域是个更好的例子,长连接,自定义协议,高并发,Netty就是绝配。

因为Netty本身就是一个基于NIO的网络框架, 封装了Java NIO那些复杂的底层细节,给你提供简单好用的抽象概念来编程。

注意几个关键词,首先它是个框架,是个“半成品”,不能开箱即用,你必须得拿过来做点定制,利用它开发出自己的应用程序,然后才能运行(就像使用Spring那样)。

一个更加知名的例子就是阿里巴巴的Dubbo了,这个RPC框架的底层用的就是Netty。

另外一个关键词是高性能,如果你的应用根本没有高并发的压力,那就不一定要用Netty了。

[抓]反击爬虫,前端工程师的脑洞可以有多大?

原文链接:https://juejin.im/entry/59deb55951882578c2084a63

1. 前言

对于一张网页,我们往往希望它是结构良好,内容清晰的,这样搜索引擎才能准确地认知它。
而反过来,又有一些情景,我们不希望内容能被轻易获取,比方说电商网站的交易额,教育网站的题目等。因为这些内容,往往是一个产品的生命线,必须做到有效地保护。这就是爬虫与反爬虫这一话题的由来。

2. 常见反爬虫策略

但是世界上没有一个网站,能做到完美地反爬虫。

如果页面希望能在用户面前正常展示,同时又不给爬虫机会,就必须要做到识别真人与机器人。因此工程师们做了各种尝试,这些策略大多采用于后端,也是目前比较常规单有效的手段,比如:

  • User-Agent + Referer检测
  • 账号及Cookie验证
  • 验证码
  • IP限制频次

而爬虫是可以无限逼近于真人的,比如:

  • chrome headless或phantomjs来模拟浏览器环境
  • tesseract识别验证码
  • 代理IP淘宝就能买到

所以我们说,100%的反爬虫策略?不存在的。
更多的是体力活,是个难易程度的问题。

不过作为前端工程师,我们可以增加一下游戏难度,设计出一些很(sang)有(xin)意(bing)思(kuang)的反爬虫策略。

3. 前端与反爬虫

3.1 font-face拼凑式

例子:猫眼电影

猫眼电影里,对于票房数据,展示的并不是纯粹的数字。
页面使用了font-face定义了字符集,并通过unicode去映射展示。也就是说,除去图像识别,必须同时爬取字符集,才能识别出数字。

并且,每次刷新页面,字符集的url都是有变化的,无疑更大难度地增加了爬取成本。

3.2 background拼凑式

例子:美团

与font的策略类似,美团里用到的是background拼凑。数字其实是图片,根据不同的background偏移,显示出不同的字符。

并且不同页面,图片的字符排序也是有区别的。不过理论上只需生成0-9与小数点,为何有重复字符就不是很懂。

页面A:

页面B:

3.3 字符穿插式

例子:微信公众号文章

某些微信公众号的文章里,穿插了各种迷之字符,并且通过样式把这些字符隐藏掉。
这种方式虽然令人震惊…但其实没有太大的识别与过滤难度,甚至可以做得更好,不过也算是一种脑洞吧。

对了,我的手机流量可以找谁报销吗?

3.4 伪元素隐藏式

例子:汽车之家

汽车之家里,把关键的厂商信息,做到了伪元素的content里。
这也是一种思路:爬取网页,必须得解析css,需要拿到伪元素的content,这就提升了爬虫的难度。

3.5 元素定位覆盖式

例子:去哪儿

还有热爱数学的去哪儿,对于一个4位数字的机票价格,先用四个i标签渲染,再用两个b标签去绝对定位偏移量,覆盖故意展示错误的i标签,最后在视觉上形成正确的价格…

这说明爬虫会解析css还不行,还得会做数学题。

3.6 iframe异步加载式

例子:网易云音乐

网易云音乐页面一打开,html源码里几乎只有一个iframe,并且它的src是空白的:about:blank。接着js开始运行,把整个页面的框架异步塞到了iframe里面…

不过这个方式带来的难度并不大,只是在异步与iframe处理上绕了个弯(或者有其他原因,不完全是基于反爬虫考虑),无论你是用selenium还是phantom,都有API可以拿到iframe里面的content信息。

3.7 字符分割式

例子:全网代理IP

在一些展示代理IP信息的页面,对于IP的保护也是大费周折。

他们会先把IP的数字与符号分割成dom节点,再在中间插入迷惑人的数字,如果爬虫不知道这个策略,还会以为自己成功拿到了数值;不过如果爬虫注意到,就很好解决了。

3.8 字符集替换式

例子:去哪儿移动侧

同样会欺骗爬虫的还有去哪儿的移动版。

html里明明写的3211,视觉上展示的却是1233。原来他们重新定义了字符集,3与1的顺序刚好调换得来的结果…

VLD扩展使用指南

原文链接:http://www.phppan.com/2011/05/vld-extension/

VLD(Vulcan Logic Dumper)是一个在Zend引擎中,以挂钩的方式实现的用于输出PHP脚本生成的中间代码(执行单元)的扩展。 它可以在一定程序上查看Zend引擎内部的一些实现原理,是我们学习PHP源码的必备良器。它的作者是Derick Rethans, 除了VLD扩展,我们常用的XDebug扩展的也有该牛人的身影。

VLD扩展是一个开源的项目,在这里可以下载到最新的版本,虽然最新版本的更新也是一年前的事了。 作者没有提供编译好的扩展,Win下使用VC6.0编译生成dll文件,可以看我之前写过的一篇文章(使用VC6.0生成VLD扩展)。 *nix系统下直接configue,make,make install生成。如果遇到问题,请自行Google之。

看一个简单的例子,假如存在t.php文件,其内容如下:

$a = 10;
echo $a;

在命令行下使用VLD扩展显示信息。

php -dvld.active=1 t.php

-dvld.active=1表示激活VLD扩展,使用VLD扩展输出中间代码,此命令在CMD中输出信息为:

Branch analysis from position: 0
Return found
filename:       D:\work\xampp\xampp\php\t.php
function name:  (null)
number of ops:  5
compiled vars:  !0 = $a
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   2     0  >   EXT_STMT
         1      ASSIGN                                                   !0, 10
   3     2      EXT_STMT
         3      ECHO                                                     !0
   4     4    > RETURN                                                   1

branch: #  0; line:     2-    4; sop:     0; eop:     4
path #1: 0,
10

如上为VLD输出的PHP代码生成的中间代码的信息,说明如下:

  • Branch analysis from position 这条信息多在分析数组时使用。
  • Return found 是否返回,这个基本上有都有。
  • filename 分析的文件名
  • function name 函数名,针对每个函数VLD都会生成一段如上的独立的信息,这里显示当前函数的名称
  • number of ops 生成的操作数
  • compiled vars 编译期间的变量,这些变量是在PHP5后添加的,它是一个缓存优化。这样的变量在PHP源码中以IS_CV标记。
  • op list 生成的中间代码的变量列表

使用-dvld.active参数输出的是VLD默认设置,如果想看更加详细的内容。可以使用-dvld.verbosity参数。

php -dvld.active=1 -dvld.verbosity=3 t.php

-dvld.verbosity=3或更大的值的效果都是一样的,它们是VLD在当前版本可以显示的最详细的信息了,包括各个中间代码的操作数等。显示结果如下:

Finding entry points
Branch analysis from position: 0
Add 0
Add 1
Add 2
Add 3
Add 4
Return found
filename:       D:\work\xampp\xampp\php\t.php
function name:  (null)
number of ops:  5
compiled vars:  !0 = $a
line     # *  op                           fetch          ext  return  operands
--------------------------------------------------------------------------------
-
   2     0  >   EXT_STMT                                          RES[  IS_UNUSED  ]         OP1[  IS_UNUSED  ] OP2[  IS_UNUSED  ]
         1      ASSIGN                                                    OP1[IS_CV !0 ] OP2[ ,  IS_CONST (0) 10 ]
   3     2      EXT_STMT                                          RES[  IS_UNUSED  ]         OP1[  IS_UNUSED  ] OP2[  IS_UNUSED  ]
         3      ECHO                                                      OP1[IS_CV !0 ]
         4    > RETURN                                                    OP1[IS_CONST (0) 1 ]

branch: #  0; line:     2-    3; sop:     0; eop:     4
path #1: 0,
10

以上的信息与没有加-dvld.verbosity=3的输出相比,多了Add 字段,还有中间代码的操作数的类型,如IS_CV,IS_CONST等。 PHP代码中的$a = 10; 其中10的类型为IS_CONST, $a作为一个编译期间的一个缓存变量存在,其类型为IS_CV。

如果我们只是想要看输出的中间代码,并不想执行这段PHP代码,可以使用-dvld.execute=0来禁用代码的执行。

php -dvld.active=1 -dvld.execute=0 t.php

运行这个命令,你会发现这与最开始的输出有一点点不同,它没有输出10。 除了直接在屏幕上输出以外,VLD扩展还支持输出.dot文件,如下的命令:

php -dvld.active=1 -dvld.save_dir='D:\tmp' -dvld.save_paths=1 -dvld.dump_paths=1 t.php

以上的命令的意思是将生成的中间代码的一些信息输出在D:/tmp/paths.dot文件中。 -dvld.save_dir指定文件输出的路径,-dvld.save_paths控制是否输出文件,-dvld.dump_paths控制输出的内容,现在只有0和1两种情况。 输出的文件名已经在程序中硬编码为paths.dot。这三个参数是相互依赖的关系,一般都会同时出现。

总结一下,VLD扩展的参数列表:

  • -dvld.active 是否在执行PHP时激活VLD挂钩,默认为0,表示禁用。可以使用-dvld.active=1启用。
  • -dvld.skip_prepend 是否跳过php.ini配置文件中auto_prepend_file指定的文件, 默认为0,即不跳过包含的文件,显示这些包含的文件中的代码所生成的中间代码。此参数生效有一个前提条件:-dvld.execute=0
  • -dvld.skip_append 是否跳过php.ini配置文件中auto_append_file指定的文件, 默认为0,即不跳过包含的文件,显示这些包含的文件中的代码所生成的中间代码。此参数生效有一个前提条件:-dvld.execute=0
  • -dvld.execute 是否执行这段PHP脚本,默认值为1,表示执行。可以使用-dvld.execute=0,表示只显示中间代码,不执行生成的中间代码。
  • -dvld.format 是否以自定义的格式显示,默认为0,表示否。可以使用-dvld.format=1,表示以自己定义的格式显示。这里自定义的格式输出是以-dvld.col_sep指定的参数间隔
  • -dvld.col_sep 在-dvld.format参数启用时此函数才会有效,默认为 “\t”。
  • -dvld.verbosity 是否显示更详细的信息,默认为1,其值可以为0,1,2,3 其实比0小的也可以,只是效果和0一样,比如0.1之类,但是负数除外,负数和效果和3的效果一样 比3大的值也是可以的,只是效果和3一样。
  • -dvld.save_dir 指定文件输出的路径,默认路径为/tmp。
  • -dvld.save_paths 控制是否输出文件,默认为0,表示不输出文件
  • -dvld.dump_paths 控制输出的内容,现在只有0和1两种情况,默认为1,输出内容