关于php使用thrift做服务端开发的那些事

php使用thrift做服务端开发

thrift采用接口描述语言定义和创建服务,用二进制格式传输数据,体积更小、效率更高,对于高并发、数据量大和多语言的环境有更好的支持。

Apache Thrift是啥?

Apache Thrift是FaceBook开发的一套可扩展的、跨语言的服务调用框架。简单的说就是先定义一个配置文件,不同的语言可以利用thrift基于这个配置文件生成各自语言的服务端,不管客户端用什么语言,都可以调用,也就是说基于thrift协议用java可以调用php的服务。目前支持C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi等语言之间相互调用。

相对于传统的xml和json等数据传输方式来说,thrift采用接口描述语言定义和创建服务,用二进制格式传输数据,体积更小、效率更高,对于高并发、数据量大和多语言的环境有更好的支持。

thrift安装环境要求

  • g++ 4.2

  • boost 1.53.0

  • lex and yacc(基于flex和bison)

如果没安装lex和yacc的话要先安装,否则会make失败,提示lex和yacc command not found错误(一般的机器貌似都没安,Ubuntu用apt-get install flex bision即可)。

安装thrift

下载最新版thrift:

wget http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.3/thrift-0.9.3.tar.gz
tar xvf thrift-0.9.3.tar.gz
cd thrift-0.9.3

2.创建configure文件

// 创建./configure文件
./bootstrap.sh
// 配置并安装
./configure
make
// 检测是否有问题,如果机子没有安装python和java等可能会报错,不过本文主要讲php,安了php环境就行
make check
make install

编译选项

  • 使用./configure --help可以查看选项

  • 如果想禁用某个语言,可以用./configure --without-java

thrift for php安装环境要求

  • php版本>5.0,因为TBinaryProtocol协议用到了pack()和unpack()函数来序列化数据

  • 需要安装APC扩展,因为TSocketPool这个类用到了apc_fetch()和apc_store()函数进行apc缓存操作。

php使用thrift的时候,除了要将thrift/lib/php/lib里的基础文件copy到项目目录下,还需要将根据配置文件生成的php文件也copy到packages文件夹下,并引入到项目中,这个后续会详细讲。

类库说明

数据传输格式(protocol)

定义的了传输内容,对Thrift Type的打包解包,包括:

  • TBinaryProtocol,二进制格式,TBinaryProtocolAccelerated则是依赖于thrift_protocol扩展的快速打包解包。

  • TCompactProtocol,压缩格式

  • TJSONProtocol,JSON格式

  • TMultiplexedProtocol,利用前三种数据格式与支持多路复用协议的服务端(同时提供多个服务,TMultiplexedProcessor)交互

数据传输方式(transport)

定义了如何发送(write)和接收(read)数据,包括:

  • TBufferedTransport,缓存传输,写入数据并不立即开始传输,直到刷新缓存。

  • TSocket,使用socket传输

  • TFramedTransport,采用分块方式进行传输,具体传输实现依赖其他传输方式,比如TSocket

  • TCurlClient,使用curl与服务端交互

  • THttpClient,采用stream方式与HTTP服务端交互

  • TMemoryBuffer,使用内存方式交换数据

  • TPhpStream,使用PHP标准输入输出流进行传输

  • TNullTransport,关闭数据传输

  • TSocketPool在TSocket基础支持多个服务端管理(需要APC支持),自动剔除无效的服务器

开发流程

1、定义IDL(Interface description language)接口描述文件,后缀.thrift

IDL规范:http://thrift.apache.org/docs/idl

thrift types:http://thrift.apache.org/docs/types

2、服务端代码开发

3、客户端编写接入代码

IDL:
1.tutorial.thrift

include "shared.thrift"
namespace php tutorial
typedef i32 MyInteger
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
enum Operation {
  ADD = 1,
  SUBTRACT = 2,
  MULTIPLY = 3,
  DIVIDE = 4
}
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}
exception InvalidOperation {
  1: i32 whatOp,
  2: string why
}
service Calculator extends shared.SharedService {
   void ping(),
   i32 add(1:i32 num1, 2:i32 num2),
   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
   oneway void zip()
}

2.shared.thrift

namespace php shared
struct SharedStruct {
  1: i32 key
  2: string value
}
service SharedService {
  SharedStruct getStruct(1: i32 key)
}

php服务端

<?php
namespace tutorial\\php;
ini_set('display_errors',1);
error_reporting(E_ALL);
// 引入类自动加载文件
require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php';
// 载入自动加载类
use Thrift\\ClassLoader\\ThriftClassLoader;
// 定义根据.thrift文件生成的php文件
$GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php';
// 注册thrift服务
$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib');
$loader->registerDefinition('shared', $GEN_DIR);
$loader->registerDefinition('tutorial', $GEN_DIR);
$loader->register();
if (php_sapi_name() == 'cli') {
  ini_set("display_errors", "stderr");
}
use Thrift\\Protocol\\TBinaryProtocol; // 二进制格式打包解包
use Thrift\\Transport\\TPhpStream; // php流输入输出
use Thrift\\Transport\\TBufferedTransport; // 使用缓存
// 开始服务端逻辑
class CalculatorHandler implements \\tutorial\\CalculatorIf {
  protected $log = array();
  public function ping() {
    error_log("ping()");
  }
  // 相加
  public function add($num1, $num2) {
    error_log("add({$num1}, {$num2})");
    return $num1 + $num2;
  }
  // 枚举计算类型
  public function calculate($logid, \\tutorial\\Work $w) {
    error_log("calculate({$logid}, {{$w->op}, {$w->num1}, {$w->num2}})");
    switch ($w->op) {
      case \\tutorial\\Operation::ADD:
        $val = $w->num1 + $w->num2;
        break;
      case \\tutorial\\Operation::SUBTRACT:
        $val = $w->num1 - $w->num2;
        break;
      case \\tutorial\\Operation::MULTIPLY:
        $val = $w->num1 * $w->num2;
        break;
      case \\tutorial\\Operation::DIVIDE:
        if ($w->num2 == 0) {
          $io = new \\tutorial\\InvalidOperation();
          $io->whatOp = $w->op;
          $io->why = "Cannot divide by 0";
          throw $io;
        }
        $val = $w->num1 / $w->num2;
        break;
      default:
        $io = new \\tutorial\\InvalidOperation();
        $io->whatOp = $w->op;
        $io->why = "Invalid Operation";
        throw $io;
    }
    $log = new \\shared\\SharedStruct();
    $log->key = $logid;
    $log->value = (string)$val;
    $this->log[$logid] = $log;
    return $val;
  }
  public function getStruct($key) {
    error_log("getStruct({$key})");
    // This actually doesn't work because the PHP interpreter is
    // restarted for every request.
    //return $this->log[$key];
    return new \\shared\\SharedStruct(array("key" => $key, "value" => "PHP is stateless!"));
  }
  public function zip() {
    error_log("zip()");
  }
};
header('Content-Type', 'application/x-thrift');
if (php_sapi_name() == 'cli') {
  echo "\\r\\n";
}
$handler = new CalculatorHandler();
$processor = new \\tutorial\\CalculatorProcessor($handler);
// 客户端和服务端在同一个输入输出流上
//1) cli 方式:php Client.php | php Server.php 
//2) cgi 方式:利用Apache或nginx监听http请求,调用php-fpm处理,将请求转换为PHP标准输入输出流
$transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W));
$protocol = new TBinaryProtocol($transport, true, true);
$transport->open();
$processor->process($protocol, $protocol);
$transport->close();
//作为cli方式运行,非阻塞方式监听,基于libevent实现,非官方实现
//$transportFactory = new TBufferedTransportFactory();
//$protocolFactory = new TBinaryProtocolFactory(true, true);
//$transport = new TNonblockingServerSocket('localhost', 9090);
//$server = new TNonblockingServer($processor, $transport, $transportFactory, $transportFactory, $protocolFactory, $protocolFactory);
//$server->serve();
//作为cli方式运行,监听端口,官方实现
//$transportFactory = new TBufferedTransportFactory();
//$protocolFactory = new TBinaryProtocolFactory(true, true);
//$transport = new TServerSocket('localhost', 9090);
//$server = new TSimpleServer($processor, $transport, $transportFactory, $transportFactory, $protocolFactory, $protocolFactory);
//$server->serve();

php客户端

<?php
namespace tutorial\\php;
error_reporting(E_ALL);
require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php';
use Thrift\\ClassLoader\\ThriftClassLoader;
$GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php';
$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib');
$loader->registerDefinition('shared', $GEN_DIR);
$loader->registerDefinition('tutorial', $GEN_DIR);
$loader->register();
use Thrift\\Protocol\\TBinaryProtocol;
use Thrift\\Transport\\TSocket;
use Thrift\\Transport\\THttpClient;
use Thrift\\Transport\\TBufferedTransport;
use Thrift\\Exception\\TException;
// 以上配置跟服务端类似
try {
  if (array_search('--http', $argv)) {
  // 使用http方式连接
    $socket = new THttpClient('localhost', 8080, '/php/PhpServer.php');
  } else {
    // 使用socket连接
    $socket = new TSocket('localhost', 9090);
  }
  $transport = new TBufferedTransport($socket, 1024, 1024);
  $protocol = new TBinaryProtocol($transport);
  $client = new \\tutorial\\CalculatorClient($protocol);
  $transport->open();
  $client->ping();
  print "ping()\\n";
  $sum = $client->add(1,1);
  print "1+1=$sum\\n";
  // 调试异常情况
  $work = new \\tutorial\\Work();
  $work->op = \\tutorial\\Operation::DIVIDE;
  $work->num1 = 1;
  $work->num2 = 0;
  try {
    $client->calculate(1, $work);
    print "Whoa! We can divide by zero?\\n";
  } catch (\\tutorial\\InvalidOperation $io) {
    print "InvalidOperation: $io->why\\n";
  }
  $work->op = \\tutorial\\Operation::SUBTRACT;
  $work->num1 = 15;
  $work->num2 = 10;
  $diff = $client->calculate(1, $work);
  print "15-10=$diff\\n";
  $log = $client->getStruct(1);
  print "Log: $log->value\\n";
  $transport->close();
} catch (TException $tx) {
  print 'TException: '.$tx->getMessage()."\\n";
}

输出:

// php client.php --http
ping()
1+1=2
InvalidOperation: Cannot divide by 0
15-10=5
Log: PHP is stateless!

关于关于php使用thrift做服务端开发的那些事的文章就分享到这,如果对你有帮助欢迎继续关注我们哦

本文来自投稿,不代表重蔚自留地立场,如若转载,请注明出处https://www.cwhello.com/41688.html

如有侵犯您的合法权益请发邮件951076433@qq.com联系删除

(0)
php学习php学习订阅用户
上一篇 2022年6月21日 22:43
下一篇 2022年6月21日 22:43

相关推荐

  • 实例详解PHP中 $_POST的$_GET的用法和区别

    post和get是指页面提交的两种方式。(推荐教程:php实战视频教程)get:参数都体现在url上,可以用于翻页,简单查询,get只能接收2M以下的内容,所以有局限性,另外由于内容是可见的,安全性就下降了。post:用于页…

    2022年6月17日
    0120
  • 利用PHP8中的函数str_contains()实现快速字符串匹配。

    随着互联网的发展,字符串处理在编程中成为了一项常见的任务。针对字符串匹配问题,PHP8引入了新的函数str_contains(),该函数可以快速地返回一个字符串中是否包含指定的字串。对于需要频繁进行字符串匹配的开发人…

    2023年5月21日
    05
  • PHP入门指南:SQL注入。

    PHP入门指南:SQL注入随着互联网的快速发展,Web应用程序越来越普及,其安全性也成为了人们极为关注的问题。SQL注入是 Web应用程序中的一种常见攻击方式,它可以导致严重的安全问题,从而对 Web应用程序的正常运行…

    2023年5月22日
    01
  • PHP数组的相关介绍

    数组的覆盖 说明:如果后面的元素中的下标和前面元素的下标“重复”了,后面元素的值会覆盖前面的元素值。 数组的自增 说明:有一个固定的数组,这个时候需要在网数组中添加一个新的元素。 数组的分类 索引数组:下标…

    2018年4月7日 PHP自学教程
    0259
  • 今日分享php加密函数有哪些。

    PHP 自带的加密函数有:md5()、crypt()。md5() 用来计算 MD5 哈希值,而 crypt() 将字符串用 UNIX 的标准加密 DES 模块加密,这是单向的加密函数,无法解密 。 什么是加密函数? 加密函数是一种将明文转换为密文的…

    2024年7月10日
    00
  • 谈谈PHP中的 ->、=> 和 :: 符号

    本篇文章给大家介绍一下php新手经常碰到的问题,->、=> 和 :: 这三个家伙是什么分别都是做什么的啊!看着就很晕。没关系,下面我们做一下详细的解释,如果你有C++,Perl基础,你会发现这些家伙和他们里面的一…

    2022年6月11日
    0182
  • PHP实现MySQL数据库主从复制的方法。

    随着互联网的飞速发展,Web应用程序越来越多地集成了数据库操作。MySQL作为一款世界知名的关系型数据库系统,使用广泛。在高并发的Web应用中,MySQL主从复制是一种提高数据库性能和可用性的重要方式。本文将介绍如…

    2023年5月21日
    00
  • PHP实现代码复用的traits新特性的方法

    在阅读yii2源码的时候接触到了trait,就学习了一下,写下博客记录一下。自 PHP 5.4.0 起,PHP 实现了代码复用的一个方法,称为 traits。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 为了减少…

    2022年6月12日
    0122

联系我们

QQ:951076433

在线咨询:点击这里给我发消息邮件:951076433@qq.com工作时间:周一至周五,9:30-18:30,节假日休息