基于上一篇博客,安装thrift complier之后,就需要进行跑跑程序,来看看是否如同预期的那种效果。
前面的thrift compiler的主要作用,其实就是为了IDL的,就是防止客户端和服务端的接口定义不同,基于IDL操作,最大限度的满足高效准确的实现服务的定义和实现。
1. 首先定义.thrift扩展名的文件,有tutorial.thrift和shared.thrift,其内容如下:
shared.thrift

1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 /** 21 * This Thrift file can be included by other Thrift files that want to share 22 * these definitions. 23 */ 24 25 namespace cpp shared 26 namespace d share // "shared" would collide with the eponymous D keyword. 27 namespace dart shared 28 namespace java shared 29 namespace perl shared 30 namespace php shared 31 namespace haxe shared 32 33 struct SharedStruct { 34 1: i32 key 35 2: string value 36 } 37 38 service SharedService { 39 SharedStruct getStruct(1: i32 key) 40 }
tutorial.thrift

1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 # Thrift Tutorial 21 # Mark Slee (mcslee@facebook.com) 22 # 23 # This file aims to teach you how to use Thrift, in a .thrift file. Neato. The 24 # first thing to notice is that .thrift files support standard shell comments. 25 # This lets you make your thrift file executable and include your Thrift build 26 # step on the top line. And you can place comments like this anywhere you like. 27 # 28 # Before running this file, you will need to have installed the thrift compiler 29 # into /usr/local/bin. 30 31 /** 32 * The first thing to know about are types. The available types in Thrift are: 33 * 34 * bool Boolean, one byte 35 * i8 (byte) Signed 8-bit integer 36 * i16 Signed 16-bit integer 37 * i32 Signed 32-bit integer 38 * i64 Signed 64-bit integer 39 * double 64-bit floating point value 40 * string String 41 * binary Blob (byte array) 42 * map<t1,t2> Map from one type to another 43 * list<t1> Ordered list of one type 44 * set<t1> Set of unique elements of one type 45 * 46 * Did you also notice that Thrift supports C style comments? 47 */ 48 49 // Just in case you were wondering... yes. We support simple C comments too. 50 51 /** 52 * Thrift files can reference other Thrift files to include common struct 53 * and service definitions. These are found using the current path, or by 54 * searching relative to any paths specified with the -I compiler flag. 55 * 56 * Included objects are accessed using the name of the .thrift file as a 57 * prefix. i.e. shared.SharedObject 58 */ 59 include "shared.thrift" 60 61 /** 62 * Thrift files can namespace, package, or prefix their output in various 63 * target languages. 64 */ 65 namespace cpp tutorial 66 namespace d tutorial 67 namespace dart tutorial 68 namespace java tutorial 69 namespace php tutorial 70 namespace perl tutorial 71 namespace haxe tutorial 72 73 /** 74 * Thrift lets you do typedefs to get pretty names for your types. Standard 75 * C style here. 76 */ 77 typedef i32 MyInteger 78 79 /** 80 * Thrift also lets you define constants for use across languages. Complex 81 * types and structs are specified using JSON notation. 82 */ 83 const i32 INT32CONSTANT = 9853 84 const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} 85 86 /** 87 * You can define enums, which are just 32 bit integers. Values are optional 88 * and start at 1 if not supplied, C style again. 89 */ 90 enum Operation { 91 ADD = 1, 92 SUBTRACT = 2, 93 MULTIPLY = 3, 94 DIVIDE = 4 95 } 96 97 /** 98 * Structs are the basic complex data structures. They are comprised of fields 99 * which each have an integer identifier, a type, a symbolic name, and an 100 * optional default value. 101 * 102 * Fields can be declared "optional", which ensures they will not be included 103 * in the serialized output if they aren't set. Note that this requires some 104 * manual management in some languages. 105 */ 106 struct Work { 107 1: i32 num1 = 0, 108 2: i32 num2, 109 3: Operation op, 110 4: optional string comment, 111 } 112 113 /** 114 * Structs can also be exceptions, if they are nasty. 115 */ 116 exception InvalidOperation { 117 1: i32 whatOp, 118 2: string why 119 } 120 121 /** 122 * Ahh, now onto the cool part, defining a service. Services just need a name 123 * and can optionally inherit from another service using the extends keyword. 124 */ 125 service Calculator extends shared.SharedService { 126 127 /** 128 * A method definition looks like C code. It has a return type, arguments, 129 * and optionally a list of exceptions that it may throw. Note that argument 130 * lists and exception lists are specified using the exact same syntax as 131 * field lists in struct or exception definitions. 132 */ 133 134 void ping(), 135 136 i32 add(1:i32 num1, 2:i32 num2), 137 138 i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), 139 140 /** 141 * This method has a oneway modifier. That means the client only makes 142 * a request and does not listen for any response at all. Oneway methods 143 * must be void. 144 */ 145 oneway void zip() 146 147 } 148 149 /** 150 * That just about covers the basics. Take a look in the test/ folder for more 151 * detailed examples. After you run this file, your generated code shows up 152 * in folders with names gen-<language>. The generated code isn't too scary 153 * to look at. It even has pretty indentation. 154 */
2. 在这两个thrift文件所在的目录下执行java接口文件的定义。
1 [root@CloudGame thrift-demo]# ll 2 total 12 3 -rw-r--r--. 1 root root 1238 Oct 8 14:43 shared.thrift 4 -rw-r--r--. 1 root root 4906 Oct 8 14:43 tutorial.thrift 5 [root@CloudGame thrift-demo]# thrift -r --gen java tutorial.thrift 6 [WARNING:/home/water/Work/thrift-demo/shared.thrift:27] No generator named 'dart' could be found! 7 [WARNING:/home/water/Work/thrift-demo/tutorial.thrift:67] No generator named 'dart' could be found! 8 [root@CloudGame thrift-demo]# 9 [root@CloudGame thrift-demo]# thrift -r --gen java shared.thrift 10 [WARNING:/home/water/Work/thrift-demo/shared.thrift:27] No generator named 'dart' could be found!
最后,你会看到一个gen-java的目录出现:
1 [root@CloudGame thrift-demo]# ll 2 total 16 3 drwxr-xr-x. 4 root root 4096 Oct 8 17:52 gen-java 4 -rw-r--r--. 1 root root 1238 Oct 8 14:43 shared.thrift 5 -rw-r--r--. 1 root root 4906 Oct 8 14:43 tutorial.thrift
1 [root@CloudGame thrift-demo]# tree gen-java 2 gen-java 3 ├── shared 4 │ ├── SharedService.java 5 │ └── SharedStruct.java 6 └── tutorial 7 ├── Calculator.java 8 ├── InvalidOperation.java 9 ├── Operation.java 10 ├── tutorialConstants.java 11 └── Work.java 12 13 2 directories, 7 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, 结果,出错了!
1 Starting the simple server... 2 org.apache.thrift.transport.TTransportException: Error creating the transport 3 at org.apache.thrift.transport.TSSLTransportFactory.createSSLContext(TSSLTransportFactory.java:214) 4 at org.apache.thrift.transport.TSSLTransportFactory.getServerSocket(TSSLTransportFactory.java:108) 5 at server.JavaServer.secure(JavaServer.java:99) 6 at server.JavaServer$2.run(JavaServer.java:54) 7 at java.lang.Thread.run(Thread.java:745) 8 Caused by: java.io.IOException: Could not load file: ../../lib/java/test/.keystore 9 at org.apache.thrift.transport.TSSLTransportFactory.getStoreAsStream(TSSLTransportFactory.java:255) 10 at org.apache.thrift.transport.TSSLTransportFactory.createSSLContext(TSSLTransportFactory.java:198) 11 ... 4 more
注意,代码中涉及到两个方案,server运行有simple和secure两种实现,simple就不多说了,看看基于SSL的安全解决方案,需要公钥.truststore和私钥.keystore文件。这个嘛,在linux环境下,也比较简单解决,看看keytool,可以搞定。 关于keytool就不多说,google之。
1 [root@CloudGame java]# keytool 2 Key and Certificate Management Tool 3 4 Commands: 5 6 -certreq Generates a certificate request 7 -changealias Changes an entry's alias 8 -delete Deletes an entry 9 -exportcert Exports certificate 10 -genkeypair Generates a key pair 11 -genseckey Generates a secret key 12 -gencert Generates certificate from a certificate request 13 -importcert Imports a certificate or a certificate chain 14 -importkeystore Imports one or all entries from another keystore 15 -keypasswd Changes the key password of an entry 16 -list Lists entries in a keystore 17 -printcert Prints the content of a certificate 18 -printcertreq Prints the content of a certificate request 19 -printcrl Prints the content of a CRL file 20 -storepasswd Changes the store password of a keystore 21 22 Use "keytool -command_name -help" for usage of command_name 23 [root@CloudGame java]# keytool -genkeypair -alias certificatekey -keyalg RSA -validity 36500 -keystore .keystore 24 Enter keystore password: 25 Re-enter new password: 26 What is your first and last name? 27 [Unknown]: shihu cheng 28 What is the name of your organizational unit? 29 [Unknown]: tk 30 What is the name of your organization? 31 [Unknown]: tk 32 What is the name of your City or Locality? 33 [Unknown]: wuhan 34 What is the name of your State or Province? 35 [Unknown]: hubei 36 What is the two-letter country code for this unit? 37 [Unknown]: 86 38 Is CN=shihu cheng, OU=tk, O=tk, L=wuhan, ST=hubei, C=86 correct? 39 [no]: y 40 41 Enter key password for <certificatekey> 42 (RETURN if same as keystore password): 43 Re-enter new password:
这里,密码一定要记得哟,是私钥的密码。在server运行的时候,需要这个信息。
上面是私钥的生成,接下来看看证书的生成过程。
1 [root@CloudGame java]# keytool -export -alias certificatekey -keystore .keystore -rfc -file server.cer 2 Enter keystore password: 3 Certificate stored in file <server.cer>
最后,利用证书生成公钥。
1 [root@CloudGame java]# keytool -import -alias certificatekey -file server.cer -keystore .truststore 2 Enter keystore password: 3 Re-enter new password: 4 Owner: CN=shihu cheng, OU=tk, O=tk, L=wuhan, ST=hubei, C=86 5 Issuer: CN=shihu cheng, OU=tk, O=tk, L=wuhan, ST=hubei, C=86 6 Serial number: 7b031382 7 Valid from: Sat Oct 08 16:39:51 HKT 2016 until: Mon Sep 14 16:39:51 HKT 2116 8 Certificate fingerprints: 9 MD5: 55:52:F8:BF:47:60:8C:3D:B1:46:10:A7:00:3F:B0:EC 10 SHA1: D8:36:EE:48:07:A0:58:40:60:D8:24:7D:7E:61:C5:9B:95:70:B1:89 11 SHA256: F5:9B:DE:91:2D:3B:64:7E:B8:0B:3A:1C:91:A2:55:96:D4:79:B4:D8:C4:67:AF:84:3B:80:41:49:55:89:26:E1 12 Signature algorithm name: SHA256withRSA 13 Version: 3 14 15 Extensions: 16 17 #1: ObjectId: 2.5.29.14 Criticality=false 18 SubjectKeyIdentifier [ 19 KeyIdentifier [ 20 0000: 37 61 64 8D F8 72 1F 2D DD 2C 04 48 42 03 64 E5 7ad..r.-.,.HB.d. 21 0010: 4E DC EA 79 N..y 22 ] 23 ] 24 25 Trust this certificate? [no]: y 26 Certificate was added to keystore
将上面生成的私钥.keystore拷贝到maven工程目录的server目录下,将公钥.truststore拷贝到maven工程的client目录下,修改JavaClient.java以及JavaServer.java相关的代码如下:
1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 package client; 21 22 // Generated code 23 import tutorial.*; 24 import shared.*; 25 26 import org.apache.thrift.TException; 27 import org.apache.thrift.transport.TSSLTransportFactory; 28 import org.apache.thrift.transport.TTransport; 29 import org.apache.thrift.transport.TSocket; 30 import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters; 31 import org.apache.thrift.protocol.TBinaryProtocol; 32 import org.apache.thrift.protocol.TProtocol; 33 34 public class JavaClient { 35 public static void main(String [] args) { 36 37 if (args.length != 1) { 38 System.out.println("Please enter 'simple' or 'secure'"); 39 System.exit(0); 40 } 41 42 try { 43 TTransport transport; 44 if (args[0].contains("simple")) { 45 transport = new TSocket("localhost", 9090); 46 transport.open(); 47 } 48 else { 49 /* 50 * Similar to the server, you can use the parameters to setup client parameters or 51 * use the default settings. On the client side, you will need a TrustStore which 52 * contains the trusted certificate along with the public key. 53 * For this example it's a self-signed cert. 54 */ 55 TSSLTransportParameters params = new TSSLTransportParameters(); 56 params.setTrustStore("./src/main/java/client/.truststore", "shihuc", "SunX509", "JKS"); 57 /* 58 * Get a client transport instead of a server transport. The connection is opened on 59 * invocation of the factory method, no need to specifically call open() 60 */ 61 transport = TSSLTransportFactory.getClientSocket("localhost", 9091, 0, params); 62 } 63 64 TProtocol protocol = new TBinaryProtocol(transport); 65 Calculator.Client client = new Calculator.Client(protocol); 66 67 perform(client); 68 69 transport.close(); 70 } catch (TException x) { 71 x.printStackTrace(); 72 } 73 } 74 75 private static void perform(Calculator.Client client) throws TException 76 { 77 client.ping(); 78 System.out.println("ping()"); 79 80 int sum = client.add(1,1); 81 System.out.println("1+1=" + sum); 82 83 Work work = new Work(); 84 85 work.op = Operation.DIVIDE; 86 work.num1 = 1; 87 work.num2 = 0; 88 try { 89 int quotient = client.calculate(1, work); 90 System.out.println("Whoa we can divide by 0"); 91 } catch (InvalidOperation io) { 92 System.out.println("Invalid operation: " + io.why); 93 } 94 95 work.op = Operation.SUBTRACT; 96 work.num1 = 15; 97 work.num2 = 10; 98 try { 99 int diff = client.calculate(1, work); 100 System.out.println("15-10=" + diff); 101 } catch (InvalidOperation io) { 102 System.out.println("Invalid operation: " + io.why); 103 } 104 105 SharedStruct log = client.getStruct(1); 106 System.out.println("Check log: " + log.value); 107 } 108 }
1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 package server; 21 import org.apache.thrift.server.TServer; 22 import org.apache.thrift.server.TServer.Args; 23 import org.apache.thrift.server.TSimpleServer; 24 import org.apache.thrift.server.TThreadPoolServer; 25 import org.apache.thrift.transport.TSSLTransportFactory; 26 import org.apache.thrift.transport.TServerSocket; 27 import org.apache.thrift.transport.TServerTransport; 28 import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters; 29 30 // Generated code 31 import tutorial.*; 32 import shared.*; 33 34 import java.util.HashMap; 35 36 public class JavaServer { 37 38 public static CalculatorHandler handler; 39 40 public static Calculator.Processor processor; 41 42 public static void main(String [] args) { 43 try { 44 handler = new CalculatorHandler(); 45 processor = new Calculator.Processor(handler); 46 47 Runnable simple = new Runnable() { 48 public void run() { 49 simple(processor); 50 } 51 }; 52 Runnable secure = new Runnable() { 53 public void run() { 54 secure(processor); 55 } 56 }; 57 58 new Thread(simple).start(); 59 new Thread(secure).start(); 60 } catch (Exception x) { 61 x.printStackTrace(); 62 } 63 } 64 65 public static void simple(Calculator.Processor processor) { 66 try { 67 TServerTransport serverTransport = new TServerSocket(9090); 68 TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); 69 70 // Use this for a multithreaded server 71 // TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); 72 73 System.out.println("Starting the simple server..."); 74 server.serve(); 75 } catch (Exception e) { 76 e.printStackTrace(); 77 } 78 } 79 80 public static void secure(Calculator.Processor processor) { 81 try { 82 /* 83 * Use TSSLTransportParameters to setup the required SSL parameters. In this example 84 * we are setting the keystore and the keystore password. Other things like algorithms, 85 * cipher suites, client auth etc can be set. 86 */ 87 TSSLTransportParameters params = new TSSLTransportParameters(); 88 // The Keystore contains the private key 89 params.setKeyStore("./src/main/java/server/.keystore", "shihuc", null, null); 90 91 /* 92 * Use any of the TSSLTransportFactory to get a server transport with the appropriate 93 * SSL configuration. You can use the default settings if properties are set in the command line. 94 * Ex: -Djavax.net.ssl.keyStore=.keystore and -Djavax.net.ssl.keyStorePassword=thrift 95 * 96 * Note: You need not explicitly call open(). The underlying server socket is bound on return 97 * from the factory class. 98 */ 99 TServerTransport serverTransport = TSSLTransportFactory.getServerSocket(9091, 0, null, params); 100 TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); 101 102 // Use this for a multi threaded server 103 // TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); 104 105 System.out.println("Starting the secure server..."); 106 server.serve(); 107 } catch (Exception e) { 108 e.printStackTrace(); 109 } 110 } 111 }
最后,再次运行JavaServer.java,得到下面的信息就对了!
1 Starting the simple server... 2 Starting the secure server...
然后,配置eclipse的运行参数arguments为secure,运行JavaClient.java,得到下面的信息:
1 ping() 2 1+1=2 3 Invalid operation: Cannot divide by 0 4 15-10=5 5 Check log: 5
到此,一个简单的thrift java语言的RPC程序就算是跑通了。
这里演示用的maven工程代码,可以在我的github上下载,作为参考,学习交流。