介绍
RPC其实在开发中十分的常用。RPC现在有很多种,比如dubbo,thrift等。首先来学习一下thrift
开发工具:Idea+macos
环境准备
安装一下thrift,其实这是一个工具,有点类似于编译器。在mac上比较简单
brew install thrift
安装完了之后执行一下命令,看看是不是安装好了。
thrift --version
如果能正确输出版本就是正常,如果不行就需要查看原因。如果是linux系统可以下载源码自己编译一个二进制文件出来(这个方式特别的费劲,我编译了一半放弃了。编译的时候依赖一个组件bison,这个组件在mac上自带,但是版本不符合,所以你要安装一个符合的版本覆盖掉这个东西)
SOA架构的理解
其实thrift实现的rpc是一种soa架构体系,就是面向服务的架构。这个东西我在大二的时候就听说过很多遍,知道今天我突然醒悟,才明白什么叫做SOA。其实现在的rpc框架,基本都是SOA的思想。SOA的核心思想是面向服务,所以就有了服务的治理,比如服务发现,服务注册,负载均衡等等。之所以有这些东西,是因为人们开发的模式改变了,大家把注意力转到了服务上,或者再直白点点就是面向某些函数,某些类做治理。
QuickStart
使用thrift做个简单的rpc
依赖
我看很多人写博客,直接代码就贴上去了。搞得其他人很难复线,依赖也没说。。。这里我说明一下:
开发环境:IDEA+jdk1.8+macOs
构建方式:maven
依赖pom:
<dependencies>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>
接口定义
通信接口是服务端和客户端的核心,这就是个协议,对于客户端来说,他拿到了这个协议就知道了服务端提供的服务是什么,需要传递什么参数,返回什么。至于怎么实现的不用管。对于服务端,有了这些协议他就能限制客户端的参数传递,必须是合法参数才能发起调用。有关thrift协议的写法,可以参考其他资料。
namespace java com.yg.demo
service Hello{
string helloWorld(1:string para)
}
生成视图
thrift -r -gen java Hello.thrift
通过thrift工具生成一个Java类,这个类的名字和接口的名字一样。这个类里面定义了一个接口:
public class Hello {
public interface Iface {
public java.lang.String helloWorld(java.lang.String para) throws org.apache.thrift.TException;
}
}
这个接口是服务端和客户端通信的关键,所以这里就体现了SOA的思想。其实这个接口是我们关心的服务。
然后服务端需要提供这个接口的实现,也就是具体的处理逻辑。
服务端处理逻辑
注意,上面你已经生成了一个名字叫做Hello的java类,这个类里面定义了一个接口。这个接口有两个含义:
(1)对于客户端来说,它通过这个接口知道自己需要传递什么参数,服务端返回什么参数
(2)对于服务端来说,自己需要实现这个接口,来处理客户端传递来的参数。
所以这里就是实现这个接口。
public class HelloImpl implements Hello.Iface {
@Override
public String helloWorld(String para) throws TException {
System.out.println("hello" + para);
return "hello" + para;
}
}
服务端启动
有了处理逻辑还是不够,因为客户端和服务点事需要通过socket连接的。所以服务端需要启动一个Event Loop来接受客户端连接,这里其实可以用Netty实现,这个以后再做,暂时先做个简单的,就使用基本的实现。
public class HelloServiceServer {
public static void main(String[] args) throws TTransportException {
System.out.println("服务端开启......");
TProcessor tProcessor = new Hello.Processor<Hello.Iface>(new HelloImpl());
TServerSocket serverSocket = new TServerSocket(8999);
TServer.Args tArgs = new TServer.Args(serverSocket);
tArgs.processor(tProcessor);
tArgs.protocolFactory(new TBinaryProtocol.Factory());
TServer server = new TSimpleServer(tArgs);
server.serve();
}
}
这里其实有点像Java的RMI,启动类的作用就是把某个方法发布到端口。但是其实这里和RMI还是有区别的,首先最大的区别就是RMI是虚拟机到虚拟机的过程调用,也就是强制了两两端必须都是Java程序,但是thrift不要求这个。这也是thrift最大的特点:语言无关
客户端调用
public class Client {
public static void main(String[] args) throws TException {
System.out.println("客户端启动.....");
TTransport transport =new TSocket("localhost", 8999, 30000);
// 协议要和服务端一致
TProtocol protocol = new TBinaryProtocol(transport);
Hello.Client client = new Hello.Client(protocol);
transport.open();
String result = client.helloWorld("TOM");
System.out.println(result);
}
}
客户端调用就比较简单了,客户端依赖一个Hello的Client,然后通过这个Client发起请求。这里可以发现:client依赖了一个叫做TProtocl的东西,首先这个东西就是网络传输协议,它不仅包含了两端的序列化方式,还包含了transport信息。我们这里的transport信息是直接通过ip找到了,但是真实的业务场景我们是通过服务发现找到的,服务发现的时候又可以负载均更,所以这里就都串起来了。这个以后再做。