基于上一篇博客,安装thrift complier之后,就需要进行跑跑程序,来看看是否如同预期的那种效果。
前面的thrift compiler的主要作用,其实就是为了IDL的,就是防止客户端和服务端的接口定义不同,基于IDL操作,最大限度的满足高效准确的实现服务的定义和实现。
1. 首先定义.thrift扩展名的文件,有tutorial.thrift和shared.thrift,其内容如下:
shared.thrift
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ /**
* This Thrift file can be included by other Thrift files that want to share
* these definitions.
*/ namespace cpp shared
namespace d share // "shared" would collide with the eponymous D keyword.
namespace dart shared
namespace java shared
namespace perl shared
namespace php shared
namespace haxe shared struct SharedStruct {
: i32 key
: string value
} service SharedService {
SharedStruct getStruct(: i32 key)
}
tutorial.thrift
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ # Thrift Tutorial
# Mark Slee (mcslee@facebook.com)
#
# This file aims to teach you how to use Thrift, in a .thrift file. Neato. The
# first thing to notice is that .thrift files support standard shell comments.
# This lets you make your thrift file executable and include your Thrift build
# step on the top line. And you can place comments like this anywhere you like.
#
# Before running this file, you will need to have installed the thrift compiler
# into /usr/local/bin. /**
* The first thing to know about are types. The available types in Thrift are:
*
* bool Boolean, one byte
* i8 (byte) Signed 8-bit integer
* i16 Signed 16-bit integer
* i32 Signed 32-bit integer
* i64 Signed 64-bit integer
* double 64-bit floating point value
* string String
* binary Blob (byte array)
* map<t1,t2> Map from one type to another
* list<t1> Ordered list of one type
* set<t1> Set of unique elements of one type
*
* Did you also notice that Thrift supports C style comments?
*/ // Just in case you were wondering... yes. We support simple C comments too. /**
* Thrift files can reference other Thrift files to include common struct
* and service definitions. These are found using the current path, or by
* searching relative to any paths specified with the -I compiler flag.
*
* Included objects are accessed using the name of the .thrift file as a
* prefix. i.e. shared.SharedObject
*/
include "shared.thrift" /**
* Thrift files can namespace, package, or prefix their output in various
* target languages.
*/
namespace cpp tutorial
namespace d tutorial
namespace dart tutorial
namespace java tutorial
namespace php tutorial
namespace perl tutorial
namespace haxe tutorial /**
* Thrift lets you do typedefs to get pretty names for your types. Standard
* C style here.
*/
typedef i32 MyInteger /**
* Thrift also lets you define constants for use across languages. Complex
* types and structs are specified using JSON notation.
*/
const i32 INT32CONSTANT =
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} /**
* You can define enums, which are just 32 bit integers. Values are optional
* and start at 1 if not supplied, C style again.
*/
enum Operation {
ADD = ,
SUBTRACT = ,
MULTIPLY = ,
DIVIDE =
} /**
* Structs are the basic complex data structures. They are comprised of fields
* which each have an integer identifier, a type, a symbolic name, and an
* optional default value.
*
* Fields can be declared "optional", which ensures they will not be included
* in the serialized output if they aren't set. Note that this requires some
* manual management in some languages.
*/
struct Work {
: i32 num1 = ,
: i32 num2,
: Operation op,
: optional string comment,
} /**
* Structs can also be exceptions, if they are nasty.
*/
exception InvalidOperation {
: i32 whatOp,
: string why
} /**
* Ahh, now onto the cool part, defining a service. Services just need a name
* and can optionally inherit from another service using the extends keyword.
*/
service Calculator extends shared.SharedService { /**
* A method definition looks like C code. It has a return type, arguments,
* and optionally a list of exceptions that it may throw. Note that argument
* lists and exception lists are specified using the exact same syntax as
* field lists in struct or exception definitions.
*/ void ping(), i32 add(:i32 num1, :i32 num2), i32 calculate(:i32 logid, :Work w) throws (:InvalidOperation ouch), /**
* This method has a oneway modifier. That means the client only makes
* a request and does not listen for any response at all. Oneway methods
* must be void.
*/
oneway void zip() } /**
* That just about covers the basics. Take a look in the test/ folder for more
* detailed examples. After you run this file, your generated code shows up
* in folders with names gen-<language>. The generated code isn't too scary
* to look at. It even has pretty indentation.
*/
2. 在这两个thrift文件所在的目录下执行java接口文件的定义。
[root@CloudGame thrift-demo]# ll
total
-rw-r--r--. root root Oct : shared.thrift
-rw-r--r--. root root Oct : tutorial.thrift
[root@CloudGame thrift-demo]# thrift -r --gen java tutorial.thrift
[WARNING:/home/water/Work/thrift-demo/shared.thrift:] No generator named 'dart' could be found!
[WARNING:/home/water/Work/thrift-demo/tutorial.thrift:] No generator named 'dart' could be found!
[root@CloudGame thrift-demo]#
[root@CloudGame thrift-demo]# thrift -r --gen java shared.thrift
[WARNING:/home/water/Work/thrift-demo/shared.thrift:] No generator named 'dart' could be found!
最后,你会看到一个gen-java的目录出现:
[root@CloudGame thrift-demo]# ll
total
drwxr-xr-x. root root Oct : gen-java
-rw-r--r--. root root Oct : shared.thrift
-rw-r--r--. root root Oct : tutorial.thrift
[root@CloudGame thrift-demo]# tree gen-java
gen-java
├── shared
│ ├── SharedService.java
│ └── SharedStruct.java
└── tutorial
├── Calculator.java
├── InvalidOperation.java
├── Operation.java
├── tutorialConstants.java
└── Work.java directories, files
3. 创建java工程,我这里用的是eclipse下的maven项目,工程创建好后,将gen-java目录下的相关java文件copy到刚才创建出来的maven工程的java目录下。
最后,将thrift的tutorial链接下,将相关的JavaClient.java以及JavaServer.java文件还有CalculatorHandler.java拷贝到maven工程下面。
注意,按照官方的tutorial来操作,是没有CalculatorHandler.java这个文件的,需要从thrift-0.9.3 (我的版本是这个,根据自己的版本不同,适当调整)解压目录中的tutorial下面的java目录下找到这个文件,拷贝到公程中相关目录下 (src/main/java/tutorial/)
4. 在JavaServer.java文件内执行run java application, 结果,出错了!
Starting the simple server...
org.apache.thrift.transport.TTransportException: Error creating the transport
at org.apache.thrift.transport.TSSLTransportFactory.createSSLContext(TSSLTransportFactory.java:)
at org.apache.thrift.transport.TSSLTransportFactory.getServerSocket(TSSLTransportFactory.java:)
at server.JavaServer.secure(JavaServer.java:)
at server.JavaServer$.run(JavaServer.java:)
at java.lang.Thread.run(Thread.java:)
Caused by: java.io.IOException: Could not load file: ../../lib/java/test/.keystore
at org.apache.thrift.transport.TSSLTransportFactory.getStoreAsStream(TSSLTransportFactory.java:)
at org.apache.thrift.transport.TSSLTransportFactory.createSSLContext(TSSLTransportFactory.java:)
... more
注意,代码中涉及到两个方案,server运行有simple和secure两种实现,simple就不多说了,看看基于SSL的安全解决方案,需要公钥.truststore和私钥.keystore文件。这个嘛,在linux环境下,也比较简单解决,看看keytool,可以搞定。 关于keytool就不多说,google之。
[root@CloudGame java]# keytool
Key and Certificate Management Tool Commands: -certreq Generates a certificate request
-changealias Changes an entry's alias
-delete Deletes an entry
-exportcert Exports certificate
-genkeypair Generates a key pair
-genseckey Generates a secret key
-gencert Generates certificate from a certificate request
-importcert Imports a certificate or a certificate chain
-importkeystore Imports one or all entries from another keystore
-keypasswd Changes the key password of an entry
-list Lists entries in a keystore
-printcert Prints the content of a certificate
-printcertreq Prints the content of a certificate request
-printcrl Prints the content of a CRL file
-storepasswd Changes the store password of a keystore Use "keytool -command_name -help" for usage of command_name
[root@CloudGame java]# keytool -genkeypair -alias certificatekey -keyalg RSA -validity 36500 -keystore .keystore
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: shihu cheng
What is the name of your organizational unit?
[Unknown]: tk
What is the name of your organization?
[Unknown]: tk
What is the name of your City or Locality?
[Unknown]: wuhan
What is the name of your State or Province?
[Unknown]: hubei
What is the two-letter country code for this unit?
[Unknown]:
Is CN=shihu cheng, OU=tk, O=tk, L=wuhan, ST=hubei, C= correct?
[no]: y Enter key password for <certificatekey>
(RETURN if same as keystore password):
Re-enter new password:
这里,密码一定要记得哟,是私钥的密码。在server运行的时候,需要这个信息。
上面是私钥的生成,接下来看看证书的生成过程。
[root@CloudGame java]# keytool -export -alias certificatekey -keystore .keystore -rfc -file server.cer
Enter keystore password:
Certificate stored in file <server.cer>
最后,利用证书生成公钥。
[root@CloudGame java]# keytool -import -alias certificatekey -file server.cer -keystore .truststore
Enter keystore password:
Re-enter new password:
Owner: CN=shihu cheng, OU=tk, O=tk, L=wuhan, ST=hubei, C=
Issuer: CN=shihu cheng, OU=tk, O=tk, L=wuhan, ST=hubei, C=
Serial number: 7b031382
Valid from: Sat Oct :: HKT until: Mon Sep :: HKT
Certificate fingerprints:
MD5: ::F8:BF:::8C:3D:B1:::A7::3F:B0:EC
SHA1: D8::EE:::A0::::D8::7D:7E::C5:9B:::B1:
SHA256: F5:9B:DE::2D:3B::7E:B8:0B:3A:1C::A2:::D4::B4:D8:C4::AF::3B:::::::E1
Signature algorithm name: SHA256withRSA
Version: Extensions: #: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
: 8D F8 1F 2D DD 2C E5 7ad..r.-.,.HB.d.
: 4E DC EA N..y
]
] Trust this certificate? [no]: y
Certificate was added to keystore
将上面生成的私钥.keystore拷贝到maven工程目录的server目录下,将公钥.truststore拷贝到maven工程的client目录下,修改JavaClient.java以及JavaServer.java相关的代码如下:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ package client; // Generated code
import tutorial.*;
import shared.*; import org.apache.thrift.TException;
import org.apache.thrift.transport.TSSLTransportFactory;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol; public class JavaClient {
public static void main(String [] args) { if (args.length != ) {
System.out.println("Please enter 'simple' or 'secure'");
System.exit();
} try {
TTransport transport;
if (args[].contains("simple")) {
transport = new TSocket("localhost", );
transport.open();
}
else {
/*
* Similar to the server, you can use the parameters to setup client parameters or
* use the default settings. On the client side, you will need a TrustStore which
* contains the trusted certificate along with the public key.
* For this example it's a self-signed cert.
*/
TSSLTransportParameters params = new TSSLTransportParameters();
params.setTrustStore("./src/main/java/client/.truststore", "shihuc", "SunX509", "JKS");
/*
* Get a client transport instead of a server transport. The connection is opened on
* invocation of the factory method, no need to specifically call open()
*/
transport = TSSLTransportFactory.getClientSocket("localhost", , , params);
} TProtocol protocol = new TBinaryProtocol(transport);
Calculator.Client client = new Calculator.Client(protocol); perform(client); transport.close();
} catch (TException x) {
x.printStackTrace();
}
} private static void perform(Calculator.Client client) throws TException
{
client.ping();
System.out.println("ping()"); int sum = client.add(,);
System.out.println("1+1=" + sum); Work work = new Work(); work.op = Operation.DIVIDE;
work.num1 = ;
work.num2 = ;
try {
int quotient = client.calculate(, work);
System.out.println("Whoa we can divide by 0");
} catch (InvalidOperation io) {
System.out.println("Invalid operation: " + io.why);
} work.op = Operation.SUBTRACT;
work.num1 = ;
work.num2 = ;
try {
int diff = client.calculate(, work);
System.out.println("15-10=" + diff);
} catch (InvalidOperation io) {
System.out.println("Invalid operation: " + io.why);
} SharedStruct log = client.getStruct();
System.out.println("Check log: " + log.value);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ package server;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TServer.Args;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TSSLTransportFactory;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters; // Generated code
import tutorial.*;
import shared.*; import java.util.HashMap; public class JavaServer { public static CalculatorHandler handler; public static Calculator.Processor processor; public static void main(String [] args) {
try {
handler = new CalculatorHandler();
processor = new Calculator.Processor(handler); Runnable simple = new Runnable() {
public void run() {
simple(processor);
}
};
Runnable secure = new Runnable() {
public void run() {
secure(processor);
}
}; new Thread(simple).start();
new Thread(secure).start();
} catch (Exception x) {
x.printStackTrace();
}
} public static void simple(Calculator.Processor processor) {
try {
TServerTransport serverTransport = new TServerSocket();
TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); // Use this for a multithreaded server
// TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); System.out.println("Starting the simple server...");
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
} public static void secure(Calculator.Processor processor) {
try {
/*
* Use TSSLTransportParameters to setup the required SSL parameters. In this example
* we are setting the keystore and the keystore password. Other things like algorithms,
* cipher suites, client auth etc can be set.
*/
TSSLTransportParameters params = new TSSLTransportParameters();
// The Keystore contains the private key
params.setKeyStore("./src/main/java/server/.keystore", "shihuc", null, null);
/*
* Use any of the TSSLTransportFactory to get a server transport with the appropriate
* SSL configuration. You can use the default settings if properties are set in the command line.
* Ex: -Djavax.net.ssl.keyStore=.keystore and -Djavax.net.ssl.keyStorePassword=thrift
*
* Note: You need not explicitly call open(). The underlying server socket is bound on return
* from the factory class.
*/
TServerTransport serverTransport = TSSLTransportFactory.getServerSocket(, , null, params);
TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); // Use this for a multi threaded server
// TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); System.out.println("Starting the secure server...");
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后,再次运行JavaServer.java,得到下面的信息就对了!
Starting the simple server...
Starting the secure server...
然后,配置eclipse的运行参数arguments为secure,运行JavaClient.java,得到下面的信息:
ping()
+=
Invalid operation: Cannot divide by
-=
Check log:
到此,一个简单的thrift java语言的RPC程序就算是跑通了。
这里演示用的maven工程代码,可以在我的github上下载,作为参考,学习交流。