-
Notifications
You must be signed in to change notification settings - Fork 22
Tatala Tutorial
Tatala is an easy-to-use RPC middleware, cross language and cross platform, that convert method signature (include callee class name, target method name, the number of its arguments and server return) into byte array, communicate with client and server base on socket.
Right now, there are Tatala-Java (client & server) and Tatala-client-csharp available.
- Easy-to-use quickly develop and setup a network component
- Cross language and platform
- High performance and distributed
- Binary communication protocol
- Support long and short socket connection
- Support multi thread on both client and server side
- Support synchronous or asynchronous method call
- Support compression for big content
- Support server push message to client
- Can use for cross-language RPC, high performance cache server, distributed message service, MMO game server……
Download tatala.jar from repository. If you're using ant, change your build.xml to include tatala.jar. If you're using eclipse, add the jar to your project build path.
As you known, easy-to-use is the first consideration among Tatala features. It can make developer create RPC just like local method call. They don’t have to care about socket or thread all kind stuff.
For example, we have server logic TestManager.class and TestManagerImpl.class.
TestManager.java
public interface TestManager {
public String sayHello(int Id, String name);
}TestManagerImpl.java
public class TestManagerImpl implements TestManager{
public String sayHello(int Id, String name) {
return "["+Id+"]"+"Hello "+name+" !";
}
}We need to create a socket server class, in order to deploy our server logic on server side. In this sample, socket server listener port is 10001. TestServer.java
public class TestServer {
public static void main(String args[]) {
int listenPort = 10001;
int poolSize = 10;
AioSocketServer server = new AioSocketServer(listenPort, poolSize);
server.start();
}
}Then client side code is something like: EasyClient.java
public class EasyClient {
private static TransferObjectFactory transferObjectFactory;
private static TestManager manager;
public static void main(String[] args) {
transferObjectFactory = new TransferObjectFactory("test1", true);
transferObjectFactory.setImplClass("TestManagerImpl");
manager = (TestManager)ClientProxyFactory.create(TestManager.class, transferObjectFactory);
int Id = 18;
String name = "JimT";
String result = manager.sayHello(Id, name);
System.out.println("result: "+result);
}
}Of cause, we need have that interface class (TestManager.class) and put into classpath. And need have a controller.xml as well, that indicates what IP, port and name client want to communicate. (Notice the connection name “test1”) controller.xml
<connections>
<connection>
<hostIp>127.0.0.1</hostIp>
<hostPort>10001</hostPort>
<timeout>5000</timeout>
<retryTime>3</retryTime>
<name>test1</name>
</connection>
</connections>That is everything from server to client codes and configuration for a full RPC. It is very simple, right?
There are more examples on tutorial section.
For build a Tatala RPC, need to three steps:
- Real Server Logic and socket server class
Real business logic codes, which run in server side. Socket server deploys server logic, and listens for client.
- Tatala Proxy
It is the agency between client and server Logic. The codes run in both of client and server side.
- Client
Who is service consumer sent request to socket server proxy and receive response from proxy. The codes run in client side.
Let’s go through all steps.
Just build simple server logic, interface and implement class.
public interface TestManager {
public String sayHello(int Id, String name);
}public class TestManagerImpl implements TestManager{
public String sayHello(int Id, String name) {
return "["+Id+"]"+"Hello "+name+" !";
}
}Create a socket server class, in order to deploy our server logic on server side.
public class TestServer {
public static void main(String args[]) {
int listenPort = 10001;
int poolSize = 10;
AioSocketServer server = new AioSocketServer(listenPort, poolSize);
server.start();
}
}Create controller.xml file on client side, which indicates what IP, port and name client want to communicate. controller.xml
<connections>
<connection>
<hostIp>127.0.0.1</hostIp>
<hostPort>10001</hostPort>
<timeout>5000</timeout>
<retryTime>3</retryTime>
<name>test1</name>
</connection>
</connections>Build Tatala client proxy.
public class TestClientProxy {
public String sayHello(int Id, String name) {
TransferObjectFactory transferObjectFactory = new TransferObjectFactory("test1", true);
StandardTransferObject to = transferObjectFactory.createTransferObject();
to.setCalleeClass("com.qileyuan.tatala.example.proxy.TestServerProxy");
to.setCalleeMethod("sayHello");
to.registerReturnType(TransferObject.DATATYPE_STRING);
to.putInt("Id", Id);
to.putString("name", name);
Object resultObj = ServerExecutor.execute(to);
String result = (String) resultObj;
return result;
}
}Create different TransferObjectFactory by passing different parameter. First parameter is socket connection name present different socket server. That connection name must same as the one in controller.xml file. Second parameter is long connection or not, if long connection flag is true, server will keep that connection until client request disconnect or connection broken. Create caller method running in client side. Create transfer object by TransferObjectFactory. Set callee class (it’s server proxy), callee method and return type. Put parameter into transfer object, include name and value. Call ServerExecutor.
Build Tatala server proxy.
public class TestServerProxy {
private TestManager manager = new TestManagerImpl();
public String sayHello(TransferObject baseto) {
StandardTransferObject to = (StandardTransferObject)baseto;
int Id = to.getInt("Id");
String name = to.getString("name");
String result = manager.sayHello(Id, name);
return result;
}
}Create callee class and callee method running in server side. Get parameters from transfer object. Call real business logic object.
Create client, which send request to client proxy and receive response from proxy.
public class TestClient {
public static void main(String[] args) throws Exception {
TestClientProxy manager = new TestClientProxy();
int Id = 18;
String name = "JimT";
String result = manager.sayHello(Id, name);
}
}We can simply use serialization object to transfer java object cross different JVM. In client proxy, if we want to pass a value object as a parameter. Make object implements serializable, and put into transfer object.
to.putSerializable("account", account);For performance concern, we can use wrapper class instead of serializable object. That is user-defined object, convert data into byte array manually.
TestAccountWrapper accountWrapper = new TestAccountWrapper(account);
to.putWrapper("account", accountWrapper);TestAccountWrapper class contain customization object - TestAccount.
public class TestAccountWrapper implements TransferObjectWrapper {
private TestAccount account;
public TestAccountWrapper(TestAccount account) {
this.account = account;
}
public int getLength(){
return TransferUtil.getLengthOfInt() +
TransferUtil.getLengthOfString(account.getName()) +
TransferUtil.getLengthOfString(account.getAddress());
}
public void getByteArray(TransferOutputStream touts) {
touts.writeInt(account.getId());
touts.writeString(account.getName());
touts.writeString(account.getAddress());
}
public TestAccountWrapper getObjectWrapper(TransferInputStream tins){
account = new TestAccount();
account.setId(tins.readInt());
account.setName(tins.readString());
account.setAddress(tins.readString());
return this;
}
}There are three implemented methods: getLength - get customization object byte array length getByteArray - convert customization object into byte array getObjectWrapper - convert byte array back to customization object
Tatala supports asynchronous call in client side proxy. If we need return object, we can get it from future object.
to.setAsynchronous(true);
Future<TestAccountWrapper> future = (Future<TestAccountWrapper>) ServerExecutor.execute(to);
accountWrapper = future.get();Tatala supports transfer data compression. If we want to send a big content object to server or want to retrieve some big content from server, we can set true on compress flag.
to.setCompress(true);In client side, we put callee class name and callee method name into transfer object. The callee is server side prox, it is executed by reflection. Server side proxy calls real service code. If we don’t want to execute server proxy in reflection way, we can create a server proxy extends DefaultProxy and register our proxy into server before start in server main class.
Add some code on TestServer.
DefaultProxy defaultProxy = new TestDefaultProxy();
server.registerProxy(defaultProxy);
server.start();TestDefaultProxy.java
public class TestDefaultProxy extends DefaultProxy{
private TestManager manager = new TestManagerImpl();
public Object execute(TransferObject abstractto){
StandardTransferObject to = (StandardTransferObject)abstractto;
String calleeMethod = to.getCalleeMethod();
if(calleeMethod.equals("sayHello")){
int Id = to.getInt("Id");
String name = to.getString("name");
String result = manager.sayHello(Id, name);
return result;
}
return null;
}
}Create NewTransferObject instead of StandardTransferObject, let you ignore parameter name, just put parameters value without name, get parameters orderly on server side. That can reduce transmission data length. Recommend!
//StandardTransferObject to = transferObjectFactory.createTransferObject();
NewTransferObject newto = transferObjectFactory.createNewTransferObject();
newto.setCalleeMethod("sayHello");
newto.registerReturnType(NewTransferObject.DATATYPE_STRING);
newto.putInt(Id);
newto.putString(name);
Object resultObj = ServerExecutor.execute(newto);
String result = (String) resultObj;If you feel it’s boring about writing proxy class. Tatala support create RPC without any client and server proxy. It base on Java dynamic proxy. So performance is slight bad, and don’t support wrapper class and Tatala-client-csharp.
Just look up the first example EasyClient on Get Started section.
After client setup long connection with server, sometime server needs to push message to client, such as server broadcast a message to all players in chat room. You can execute a server call by Tatala session, as long as you keep the client session and handle it on server logic.
Check Chat Room example for more detail.
Tatala support server intercepts each client request, and injects business logic on it.
Create session filter.
public class MyFilter implements SessionFilter {
public void onClose(ServerSession session) {
//on connection close, do something
}
public boolean onReceive(ServerSession session, byte[] receiveData) {
//after each receive, do something
return true;
}
}Register my filter on socket server class.
AioSocketServer server = new AioSocketServer(listenPort, poolSize);
MyFilter filter = new MyFilter();
server.registerSessionFilter(filter);In client side, put method signature into transfer object, tatala convert transfer object into byte array and send to server. In server side, convert byte array into transfer object that content method signature, like callee class, method name, arguments and return type. Tatala executor loads that information and invokes that target method.
Following is conversion protocol.
Client -> Server
Normal:
TatalaFlag+Flag+BLength+CLASS+METHOD+ReturnType+CompressFlag+([type+name+value]+[type+name+value]+[...] )
Compress:
TatalaFlag+Flag+UnCompressLength+CompressLength+BLength+CLASS+METHOD+ReturnType+CompressFlag+([type+name+value]+[type+name+value]+[...] )
Wrapper: (if value is wrapper class)
BLength+WCLASS+[value]+[value]+[...]
Server -> Client (server return)
Normal:
Flag+BLength+ReturnType+ReturnValue
Compress:
Flag+UnCompressLength+CompressLength+BLength+ReturnType+ReturnValue
NewTransferObject (remove parameter name)
Client -> Server
Normal:
TatalaFlag+Flag+BLength+CLASS+METHOD+ReturnType+CompressFlag+([type+value]+[type+value]+[...] )
Compress:
TatalaFlag+Flag+UnCompressLength+CompressLength+BLength+CLASS+METHOD+ReturnType+CompressFlag+([type+value]+[type+value]+[...] )
Notes:
TatalaFlag: just claim this request is tatala request
Flag: compress = 1; servercall = 1 << 1; longconnection = 1 << 2; NewTOVersion = 1 << 3; (if it is NewTransferObject)
blength: whole byte array length
class: callee class full name (package and classname)
method: callee method name
returntype: return data type
returnvalue: return data
compressflag: compress flag tell server need compress return data
type: this input parameter type
name: this input parameter name
value: this input parameter value
uncompresslength: byte array length before compress
compresslength: byte array length after compress
compressdata: compressed data using zlib
wclass: wrapper class full name
Supported parameter and return type table
| Type | Java | C# |
| bool | Y | Y |
| byte | Y | Y |
| short | Y | Y |
| chat | Y | Y |
| int | Y | Y |
| long | Y | Y |
| float | Y | Y |
| double | Y | Y |
| Date | Y | Y |
| String | Y | Y |
| byte[] | Y | Y |
| int[] | Y | Y |
| long[] | Y | Y |
| float[] | Y | Y |
| double[] | Y | Y |
| String[] | Y | Y |
| Serializable | Y | N |
| WrapperClass | Y | Y |
Require JDK1.7, because using Java AIO.
Third part libs include XSteam and Log4j.
This library is distributed under Apache License Version 2.0