Thrift(PHP)入门无错篇章(一)
一、安装篇
博主注:截至2017-10-10,官网上thrift最新版0.10.0一直无法成功编译。所以,请选择0.9.3版本,避免走各种弯路:
wget http://apache.fayea.com/thrift/0.9.3/thrift-0.9.3.tar.gz
1、安装开发平台工具
yum -y groupinstall "Development Tools"
2、安装autoconf
wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
tar xvf autoconf-2.69.tar.gz
cd autoconf-2.69
./configure --prefix=/usr/local
make
make install
3、安装automake
wget http://ftp.gnu.org/gnu/automake/automake-1.14.tar.gz
tar xvf automake-1.14.tar.gz
cd automake-1.14
./configure --prefix=/usr/local
make
make install
4、安装bison
wget http://ftp.gnu.org/gnu/bison/bison-2.5.1.tar.gz
tar xvf bison-2.5.1.tar.gz
cd bison-2.5.1
./configure --prefix=/usr/local
make
make install
5、安装C++库依赖包
yum -y install libevent-devel zlib-devel openssl-devel
6、安装boost
wget http://sourceforge.net/projects/boost/files/boost/1.53.0/boost_1_53_0.tar.gz
tar xvf boost_1_53_0.tar.gz
cd boost_1_53_0
./bootstrap.sh
./b2
7、安装其它依赖包
yum install gcc gcc-c++ bzip2 bzip2-devel bzip2-libs python-devel -y
8、安装thrift(基本的编译套路)
cd thrift
./bootstrap.sh
./configure --with-lua=no --prefix=/alidata/server/thrift
make
make install
二、Hello World篇
使用套路总结:
1、用IDL语法,定义自己的数据结构与服务接口:
https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=tutorial/tutorial.thrift
https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=tutorial/shared.thrift
将这两个文件下载到本地,分别保存为tutorial.thrift和shared.thrift,并将这两个文件传到与thrift同目录下。
附:
IDL详细教程:http://thrift.apache.org/docs/idl
IDL示例(够用了):https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=tutorial/tutorial.thrift
https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=tutorial/shared.thrift
2、用thrift将上面的thrift的IDL文件,转成对应语言的类:
thrift -gen php:server tutorial.thrift
上面这一句是对应服务端的转换,客户端的转换不用:server;但是服务端方式生成的类文件可以供客户端使用。
3、转换完成后,在本目录下会得到一个gen-php目录,里面有三个文件tutorial.php、shared.php和Types.php。
tutorial.php里面有一个CalculatorIf接口,这个类是我们要实现的服务端业务接口。
shared.php被引用在tutorial.php文件中。
Types.php里面是自定义数据类型。
4、实现server端,即需要创建一个Handler类实再业务接口CalculatorIf
注意:这里的lib文件在thrift源码包lib里,我们移到自己的php需要的路径中。代码中require_once部分、$GEN_DIR部分、Thrift命名空间注册部分的路径需要根据实际情况进行更改
<?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(); if (php_sapi_name() == 'cli') {
ini_set("display_errors", "stderr");
} use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TPhpStream;
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); $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();
5、实现client端:
同样需要注意:这里的lib文件在thrift源码包lib里,我们移到自己的php需要的路径中。代码中require_once部分、$GEN_DIR部分、Thrift命名空间注册部分的路径需要根据实际情况进行更改
<?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)) {
$socket = new THttpClient('localhost', 8080, '/php/PhpServer.php');
} else {
$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";
} ?>
6、因为php的server端代码没有进行端口侦听的网络服务功能,所以要依靠python来提供这个功能。我们把下面的脚本命名为s.py,需要与server.php放在同一个目录下(/alidata/www/thrift)
#!/bin/py
#coding=utf-8 import os
import BaseHTTPServer
import CGIHTTPServer
# chdir(2) into the tutorial directory.
os.chdir('/alidata/www/thrift')
# 指定目录 ,如果目录错误 请求会失败
class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
cgi_directories = ['/'] BaseHTTPServer.HTTPServer(('', 8080), Handler).serve_forever()
7、到现在为止,hello world级别的工程已经完成。
运行服务端s.py
运行客户端php client.php --http
便可以看到响应的实际效果
三、生产使用篇
hello world级别的工程,只有一个进程进行侦听和服务,在多核CPU的时代,只有一个进程不但无法充分利用CPU的性能,也无法为用户提供最高效的服务。
方案一:用php自己实现网络服务方案(这是一个牛人写的多进程服务demo,完全可以参照) http://blog.csdn.net/flynetcn/article/details/47837975
<?php
/**
* 多进程形式的server.
* @package thrift.server
* @author flynetcn
*/
namespace Thrift\Server; use Thrift\Server\TServer;
use Thrift\Transport\TTransport;
use Thrift\Exception\TException;
use Thrift\Exception\TTransportException; class TMultiProcessServer extends TServer
{
/**
* 捕获的信号编号
*/
static $catchQuitSignal = 0; /**
* worker进程数量
*/
private $workProcessNum = 4; /**
* 每个worker进程处理的最大请求数
*/
private $maxWorkRequestNum = 2000; /**
* 当前worker进程已处理的请求数
*/
private $currentWorkRequestNum = 0; /**
* 当前连接调用次数
*/
private $currentConnectCallNum = 0; /**
* 发送超时
*/
private $sendTimeoutSec = 1; /**
* 接收超时
*/
private $recvTimeoutSec = 1; /**
* 当前进程pid
*/
private $pid = 0; /**
* Flag for the main serving loop
*/
private $stop_ = false; /**
* List of children.
*/
protected $childrens = array(); /**
* 服务器日志文件
*/
protected static $logFiles;
protected static $pidFile; /**
* run
*/
public function serve($daemon=false, array $config=array())
{
if (isset($config['workProcessNum'])) {
$this->workProcessNum = intval($config['workProcessNum']);
}
if ($this->workProcessNum < 1) {
self::log(1, "child workProcessNum can not be less than 1");
throw new TException('child workProcessNum can not be less than 1');
}
if (isset($config['maxWorkRequestNum'])) {
$this->maxWorkRequestNum = intval($config['maxWorkRequestNum']);
}
if ($this->maxWorkRequestNum < 1) {
self::log(1, "child maxWorkRequestNum can not be less than 1");
throw new TException('child maxWorkRequestNum can not be less than 1');
}
if (isset($config['sendTimeoutSec'])) {
$this->sendTimeoutSec = intval($config['sendTimeoutSec']);
}
if (isset($config['recvTimeoutSec'])) {
$this->recvTimeoutSec = intval($config['recvTimeoutSec']);
}
if ($daemon) {
$this->daemon();
$this->registerSignalHandler();
self::$logFiles = isset($config['logFiles']) && is_array($config['logFiles']) ? $config['logFiles'] : array();
self::$pidFile = isset($config['pidFile']) ? $config['pidFile'] : '';
declare(ticks=3);
}
$this->pid = posix_getpid();
self::createPidFile($this->pid);
self::log(0, "manage process({$this->pid}) has started");
$this->transport_->listen();
while (!$this->stop_) {
while ($this->workProcessNum > 0) {
try {
$pid = pcntl_fork();
if ($pid > 0) {
$this->handleParent($pid, $this->workProcessNum);
} else if ($pid === 0) {
$this->pid = posix_getpid();
$this->handleChild($this->workProcessNum);
} else {
self::log(1, "Failed to fork");
throw new TException('Failed to fork');
}
$this->workProcessNum--;
} catch (Exception $e) {
}
}
$this->collectChildren();
sleep(2);
if (\Thrift\Server\TMultiProcessServer::$catchQuitSignal) {
$this->stop();
}
}
} public function getCurrentWorkRequestNum()
{
return $this->currentWorkRequestNum;
} public function getCurrentConnectCallNum()
{
return $this->currentConnectCallNum;
} /**
* Code run by the parent
*
* @param int $pid
* @param int $num 进程编号
* @return void
*/
private function handleParent($pid, $num)
{
$this->childrens[$pid] = $num;
} /**
* Code run by the child.
*
* @param int $num 进程编号
* @return void
*/
private function handleChild($num)
{
self::log(0, "child process($this->pid) has started");
$this->childrens = array();
while (!$this->stop_) {
try {
$transport = $this->transport_->accept();
if ($transport != null) {
$transport->setSendTimeout($this->sendTimeoutSec * 1000);
$transport->setRecvTimeout($this->recvTimeoutSec * 1000);
$this->currentWorkRequestNum++;
$this->currentConnectCallNum = 0;
$inputTransport = $this->inputTransportFactory_->getTransport($transport);
$outputTransport = $this->outputTransportFactory_->getTransport($transport);
$inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport);
$outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport);
while ($this->processor_->process($inputProtocol, $outputProtocol)) {
$this->currentConnectCallNum++;
}
@$transport->close();
}
} catch (TTransportException $e) {
} catch (Exception $e) {
self::log(1, $e->getMessage().'('.$e->getCode().')');
}
if (\Thrift\Server\TMultiProcessServer::$catchQuitSignal) {
$this->stop();
}
if ($this->currentWorkRequestNum >= $this->maxWorkRequestNum) {
self::log(0, "child process($this->pid) has processe {$this->currentWorkRequestNum} requests will be exit");
$this->stop();
break;
}
}
exit(0);
} /**
* Collects any children we may have
*
* @return void
*/
private function collectChildren()
{
foreach ($this->childrens as $pid => $num) {
if (pcntl_waitpid($pid, $status, WNOHANG) > 0) {
unset($this->childrens[$pid]);
$this->workProcessNum++;
}
}
} /**
* @return void
*/
public function stop()
{
$this->transport_->close();
$this->stop_ = true;
foreach ($this->childrens as $pid => $num) {
if (!posix_kill($pid, SIGTERM)) {
}
}
} /**
* 附加信号处理
*/
public static function sig_handler($signo)
{
switch ($signo) {
case SIGTERM:
case SIGHUP:
case SIGQUIT:
case SIGTSTP:
$pid = posix_getpid();
self::log(0, "process($pid) catch signo: $signo");
\Thrift\Server\TMultiProcessServer::$catchQuitSignal = $signo;
break;
default:
}
} /**
* 附加信号处理
*/
private function registerSignalHandler()
{
pcntl_signal(SIGTERM, '\Thrift\Server\TMultiProcessServer::sig_handler');
pcntl_signal(SIGHUP, '\Thrift\Server\TMultiProcessServer::sig_handler');
pcntl_signal(SIGQUIT, '\Thrift\Server\TMultiProcessServer::sig_handler');
pcntl_signal(SIGTSTP, '\Thrift\Server\TMultiProcessServer::sig_handler');
declare(ticks=3);
} /**
* 附加守护进程方式
*/
private function daemon()
{
if (!function_exists('posix_setsid')) {
return;
}
if (($pid1 = pcntl_fork()) != 0) {
exit;
}
posix_setsid();
if (($pid2 = pcntl_fork()) != 0) {
exit;
}
} public static function log($type, $msg)
{
static $fds;
$msg = date('Y-m-d H:i:s')." $type {$msg}\n";
if (isset(self::$logFiles[$type]) && self::$logFiles[$type]) {
if (file_exists(self::$logFiles[$type])) {
if (empty($fds[$type])) {
$fds[$type] = fopen(self::$logFiles[$type], 'a');
}
if (!$fds[$type]) {
$fds[$type] = fopen('php://stdout', 'w');
fwrite($fds[$type], date('Y-m-d H:i:s')." WARNING fopen(".self::$logFiles[$type].") failed\n");
}
} else {
if (!is_dir(dirname(self::$logFiles[$type])) && !mkdir(dirname(self::$logFiles[$type]), 0755, true)) {
$fds[$type] = fopen('php://stdout', 'w');
fwrite($fds[$type], date('Y-m-d H:i:s')." WARNING mkdir(".self::$logFiles[$type].") failed\n");
} elseif (!($fds[$type] = fopen(self::$logFiles[$type], 'a'))) {
$fds[$type] = fopen('php://stdout', 'w');
fwrite($fds[$type], date('Y-m-d H:i:s')." WARNING fopen(".self::$logFiles[$type].") failed\n");
}
}
} else {
$fds[$type] = fopen('php://stdout', 'w');
}
$ret = fwrite($fds[$type], $msg);
if (!$ret && self::$logFiles[$type]) {
fclose($fds[$type]);
$fds[$type] = fopen(self::$logFiles[$type], 'a');
$ret = fwrite($fds[$type], $msg);
}
return true;
} public static function createPidFile($pid=0)
{
if (!$pid) {
$pid = posix_getpid();
}
if (file_exists(self::$pidFile)) {
$fd = fopen(self::$pidFile, 'w');
if (!$fd) {
self::log(1, "fopen(".self::$pidFile.") failed");
return false;
}
} else {
if (!is_dir(dirname(self::$pidFile)) && !mkdir(dirname(self::$pidFile), 0755, true)) {
self::log(1, "mkdir(".self::$pidFile.") failed");
return false;
} elseif (!($fd = fopen(self::$pidFile, 'w'))) {
self::log(1, "fopen(".self::$pidFile.") failed");
return false;
}
}
if (!fwrite($fd, "$pid")) {
self::log(1, "fwrite(".self::$pidFile.",$pid) failed");
return false;
}
fclose($fd);
return true;
}
}
方案二:方案一固然不错,但是因为没有实践的检验,所以用起来还是有一点担心的。所以,个人建议采用workman的thrift方案,请参考:http://www.workerman.net/workerman-thrift
1、因为要使用多进程,所以需要先安装pcntl扩展。(安装过程很简单,不是重点,不详细描述过程)
2、workman-thrift下载解压后,目录中Applications/ThriftRpc/Services存放的自己的服务端服务,即业务接口If、自定义数据类型Type和业务接口的实现。
3、根目录下的start.php加载并运行了Applications下的各个服务的start.php文件
4、解压后的目录中Applications/ThriftRpc是一个HelloWorld服务的demo。
5、现在,看Applications/ThriftRpc/start.php中的关键代码:
$worker = new ThriftWorker('tcp://0.0.0.0:9090');
$worker->count = 16;
$worker->class = 'HelloWorld';
这里,设置了侦听的IP与端口、设置了服务进程数量与服务的类
6、查看Applications/ThriftPrc/ThriftWorker.php中的关键代码:
// 载入该服务下的所有文件
foreach(glob(THRIFT_ROOT . '/Services/'.$this->class.'/*.php') as $php_file)
{
require_once $php_file;
}
// 检查类是否存在
$processor_class_name = "\\Services\\".$this->class."\\".$this->class.'Processor';
if(!class_exists($processor_class_name))
{
ThriftWorker::log("Class $processor_class_name not found" );
return;
} // 检查类是否存在
$handler_class_name ="\\Services\\".$this->class."\\".$this->class.'Handler';
if(!class_exists($handler_class_name))
{
ThriftWorker::log("Class $handler_class_name not found" );
return;
}
根据第5步指定的class,在ThriftWorker中会载入对应路径下的所有php文件进行检查,并检查对应的Processor和Handler类是否存在。最终创建Handler和Processor
7、运行根目录下的php start.php start,可以看到
恭喜,服务端运行成功。
8、关于客户端
复制 http://www.workerman.net/workerman-thrift 上的客户端代码,即可正常运行。
<?php // 引入客户端文件
require_once __DIR__.'/Applications/ThriftRpc/Clients/ThriftClient.php';
use ThriftClient\ThriftClient; // 传入配置,一般在某统一入口文件中调用一次该配置接口即可
thriftClient::config(
array(
'HelloWorld' => array(
'addresses' => array(
'127.0.0.1:9090',
'127.0.0.2:9191',
),
'thrift_protocol' => 'TBinaryProtocol',//不配置默认是TBinaryProtocol,对应服务端HelloWorld.conf配置中的thrift_protocol
'thrift_transport' => 'TBufferedTransport',//不配置默认是TBufferedTransport,对应服务端HelloWorld.conf配置中的thrift_transport
),
'DInfoCenter' => array(
'addresses' => array(
'127.0.0.1:9090',
),
'thrift_protocol' => 'TBinaryProtocol',//不配置默认是TBinaryProtocol,对应服务端HelloWorld.conf配置中的thrift_protocol
'thrift_transport' => 'TBufferedTransport',//不配置默认是TBufferedTransport,对应服务端HelloWorld.conf配置中的thrift_transport
),
)
);
// ========= 以上在WEB入口文件中调用一次即可 =========== // ========= 以下是开发过程中的调用示例 ========== // 初始化一个HelloWorld的实例
$client = ThriftClient::instance('HelloWorld'); // --------同步调用实例----------
var_export($client->sayHello('JIM');
//var_export($client->recv_request("req",strval(time()))); // --------异步调用示例-----------
/*
// 异步调用 之 发送请求给服务端(注意:异步发送请求格式统一为 asend_XXX($arg),既在原有方法名前面增加'asend_'前缀)
$client->asend_sayHello("JERRY");
$client->asend_sayHello("KID"); // 这里是其它业务逻辑
sleep(1); // 异步调用 之 接收服务端的回应(注意:异步接收请求格式统一为 arecv_XXX($arg),既在原有方法名前面增加'arecv_'前缀)
var_export($client->arecv_sayHello("KID"));
var_export($client->arecv_sayHello("JERRY"));
*/
9、截止这里,已经可以成功运行workman-thrift的hello world工程
10、关于定义自己的服务:
到这里根据了解,我们可以很容易自己的的服务。比如,我们想定义自己的服务,DInfoCenter,这个过程中有三个一定需要注意的地方:
第一点:目录名称需要改成DInfoCenter
第二点:三个文件的namespace声明(第一行),一定要改为:namespace Services\DInfoCenter;
第三点:默认生成的DInfoCenter.php中,下面很多类以及函数的调用,都带有命名空间(\DInfoCenter),需要把这一段删掉
到这里,就大致完成了,最后将start.php中的class赋值成DInfoCenter,就全部完成了。
第一篇无错版的Thrift php教程,到此完成。只要你按上面的步骤,就能实现thrift php的服务端与客户端。
这是第一篇,但不会是最后一篇关于thrift php的文章,关于服务端使用TMultiplexedProcessor与workman-thrift进行整合,请拭目以待:)
Thrift(PHP)入门无错篇章(一)的更多相关文章
- Thrift快速入门
Thrift 简单示例 2017-01-19 16:47:57 首先通过先面两个示例简单感受一下Thrift(RPC)服务端与客户端之间的通信...... RPC学习----Thrift快速入门和Ja ...
- Netty学习——Thrift的入门使用
Netty学习——Thrift的入门使用 希望你能够,了解并使用它.因为它是一个效率很高的框架 官网地址:http://thrift.apache.org/ 1.Thrift数据类型 一门技术如果需要 ...
- RPC学习----Thrift快速入门和Java简单示例
一.什么是RPC? RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. RPC协议 ...
- Thrift - 快速入门
简单实例 有homebrew的话,直接执行以下命令即可,brew会处理相关依赖(https://thrift.apache.org/docs/install/). brew install thrif ...
- Thrift使用入门---RPC服务
https://blog.csdn.net/zkp_java/article/details/81879577 RPC基本原理 大部分的RPC框架都遵循如下三个开发步骤: RPC通信过程如下图所示 通 ...
- RPC学习--C#使用Thrift简介,C#客户端和Java服务端相互交互
本文主要介绍两部分内容: C#中使用Thrift简介 用Java创建一个服务端,用C#创建一个客户端通过thrift与其交互. 用纯C#实现Client和Server C#服务端,Java客户端 其中 ...
- C#使用Thrift简介,C#客户端和Java服务端相互交互
C#使用Thrift简介,C#客户端和Java服务端相互交互 本文主要介绍两部分内容: C#中使用Thrift简介 用Java创建一个服务端,用C#创建一个客户端通过thrift与其交互. 用纯C#实 ...
- erlang+thrift配合开发
I think, thrift is a tcp/ip based Client-Server architecture multi-languages supported RPC framewo ...
- [ToDo]Thrift学习
这里有较详细的Java项目配置过程: http://bglmmz.iteye.com/blog/2058785 下面有Java项目的示例介绍: http://www.tuicool.com/artic ...
随机推荐
- Victor and String HDU - 5421 双向回文树
题意: 有n种操作,开始给你一个空串,给你4中操作 1 c 在字符串的首部添加字符c 2 c 在字符串的尾部添加字符c 3 询问字符中的本质不同的回文串的个数 4 询问字符串中回文串的个数 思路 ...
- 使用SpringBoot1.4.0的一个坑
时隔半年,再次使用Spring Boot快速搭建微服务,半年前使用的版本是1.2.5,如今看官网最新的release版本是1.4.0,那就用最新的来构建,由于部署环境可能有多套所以使用maven-fi ...
- 如果json中的key需要首字母大写怎么解决?
一般我们命名都是驼峰式的,可是有时候和第三方接口打交道,也会遇到一些奇葩,比如首字母大写........额 这是个什么鬼,对方这么要求,那我们也得这么写呀. 于是乎,第一种方式:把类中的字段首字母大写 ...
- linux 系统优化,调优
1.系统安装前的规则 a.分区:不同环境不同分法,按自己的需求来 以硬盘500G为例 /boot 100M-200M(只存放grub和启动相关文件,不存放其他) / 80G-100G (因为很多人默 ...
- 解析Mybatis入门第三天
目的:使用mybatis对数据的一些标签的使用和表与表之间的一对多和多对一的查询方式. 例如:if.where.foreach 前言:同样是使用idea创建一个普通的maven工程(如何创建一个普通的 ...
- div+css对网页进行布局
div+css对网页进行布局 首先在页面整体上进行div标签划分内容区域,然后再用css进行定位,最后再对相应的区域添加内容. 1.用div将页面划分 拿到网站页面图后,首先将分析页面分为哪几块,然后 ...
- Windows下 vundle的安装和使用
准备工作 1. 安装git 去官网下载,安装即可. 2. 添加git的环境变量 并将Git 的安装路径加入环境变量Path,如 D:\Program Files\Git\cmd 然后运行cmd,输入 ...
- java生成验证码并可刷新
手心创建一个简单的页面来显示所创建的验证码 <body> <form action="loginName.mvc" method="post" ...
- java模拟页面表单登录
简单的通过表单的post请求提交到后台 package testpostlogin; import java.io.BufferedReader; import java.io.File; impor ...
- 第九篇:Spring的applicationContext.xml配置总结
在前面的一篇日志中,记录了web.xml配置启动的顺序,web启动到监听器ContextLoaderListener时,开始加载spring的配置文件applicationContext.xml(通常 ...