1.初识RPC
关于RPC,它能够解决不同编程语言间的服务调用与通讯问题。
【百度百科:Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。】
【拓展:RMI 是一个良好的、特殊的RPC实现,但它适用在服务端与客户端都为JAVA语言的情况下,不可以跨语言使用。】
使用Thrift时,需要写IDL(Interface Definition Language)文件,再通过Thrift提供的编译器生成市面上主流开发语言的接口代码及实体类代码,这点与googleRPC在写xx.proto文件时相似。
通过Thrift编写的程序,在传输层上提供服务(基于TCP),其数据传输效率远远超过基于应用层HTTP协议通讯的程序。
2.编写Demo
由于日常业务中,服务端只提供单一的ServiceProcessor无法满足业务的需求,好在Thrift支持多路复用器:TMultiplexedProcessor。
2.1两个Processor的IDL:
// Student.thrift
namespace java com.thrift.generatecode.student
namespace * generatecode.student
//thrift --gen java ./Student.thrift
typedef i32 int
typedef string String
//数据结构
struct Student{
1:optional String name,
2:optional int age
}
//异常
exception MyException{
1:optional String data
}
//接口
service StudentService{
list<Student> queryStudents() ,
int addStudent(1:required String name,2:required int age) ,
int delStudent(1:String name) ,
int updStudent(1:required String name,2:required int age) throws(1:MyException e)
}
// School.thrift
namespace java com.thrift.generatecode.school
namespace * generatecode.school
typedef i32 int
typedef string String
//数据结构
struct School{
1:optional String code,
2:optional String school_name
}
//异常
exception MyException{
1:optional String data
}
//接口
service SchoolService{
list<School> querySchools() ,
int addSchool(1:required School school) ,
int delSchool(1:String school_name) ,
int updSchool(1:required School school) throws(1:MyException e)
}
生成接口文件需要提前下载Thrift工具Apache Thrift - Home,如果使用maven工程,可以整合到plugins。进入到含有IDL文件的目录,使用以下命令生成接口文件,并将生成的文件粘贴到工程中。
thrift --gen java ./Student.thrift
thrift --gen java ./School.thrift
2.2接口的实现类
package com.thrift.server.daoimpl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.thrift.TException;
import com.common.util.DBUtilTransactional;
import com.thrift.generatecode.student.MyException;
import com.thrift.generatecode.student.Student;
import com.thrift.generatecode.student.StudentService;
public class StudentServiceImpl implements StudentService.Iface {
@Override
public List<Student> queryStudents() throws TException {
Connection c = DBUtilTransactional.getConnection();
String sql = "SELECT NAME,AGE FROM STUDENT;";
PreparedStatement pstmt = null;
ResultSet rs = null;
Student student = null;
List<Student> stuList = null;
try {
pstmt = c.prepareStatement(sql);
rs = pstmt.executeQuery();
/*
* 判断结果集(元组)数量,移动游标
*/
rs.last();
int totalCount = rs.getRow();
rs.beforeFirst();
// 初始化数组并填充数据
if ( totalCount > 0 ) {
stuList = new ArrayList<>(totalCount);
while ( rs.next() ) {
student = new Student();
String name = rs.getString("name");
int age = rs.getInt("age");
student.setName(name);
student.setAge(age);
stuList.add(student);
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(rs);
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return stuList;
}
@Override
public int addStudent(String name, int age) throws TException {
int affectLine = 0;
Connection c = DBUtilTransactional.getConnection();
String sql = "INSERT INTO STUDENT(NAME,AGE) VALUES(?,?);";
PreparedStatement pstmt = null;
try {
pstmt = c.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setInt(2, age);
affectLine = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return affectLine;
}
@Override
public int delStudent(String name) throws TException {
int affectLine = 0;
Connection c = DBUtilTransactional.getConnection();
String sql = "DELETE FROM STUDENT WHERE NAME = ?;";
PreparedStatement pstmt = null;
try {
pstmt = c.prepareStatement(sql);
pstmt.setString(1, name);
affectLine = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return affectLine;
}
@Override
public int updStudent(String name, int age) throws MyException, TException {
int affectLine = 0;
Connection c = DBUtilTransactional.getConnection();
String sql = "UPDATE STUDENT SET AGE = ? WHERE NAME = ?;";
PreparedStatement pstmt = null;
try {
pstmt = c.prepareStatement(sql);
pstmt.setInt(1, age);
pstmt.setString(2, name);
affectLine = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return affectLine;
}
}
package com.thrift.server.daoimpl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.thrift.TException;
import com.common.util.DBUtilTransactional;
import com.thrift.generatecode.school.MyException;
import com.thrift.generatecode.school.School;
import com.thrift.generatecode.school.SchoolService;
public class SchoolServiceImpl implements SchoolService.Iface {
@Override
public List<School> querySchools() throws TException {
Connection c = DBUtilTransactional.getConnection();
String sql = "SELECT CODE,SCHOOL_NAME FROM SCHOOL;";
PreparedStatement pstmt = null;
ResultSet rs = null;
School school = null;
List<School> schList = null;
try {
pstmt = c.prepareStatement(sql);
rs = pstmt.executeQuery();
/*
* 判断结果集(元组)数量,移动游标
*/
rs.last();
int totalCount = rs.getRow();
rs.beforeFirst();
// 初始化数组并填充数据
if ( totalCount > 0 ) {
schList = new ArrayList<>(totalCount);
while ( rs.next() ) {
school = new School();
String code = rs.getString("code");
String school_name = rs.getString("school_name");
school.setCode(code);
school.setSchool_name(school_name);
schList.add(school);
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(rs);
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return schList;
}
@Override
public int addSchool(School school) throws TException {
int affectLine = 0;
Connection c = DBUtilTransactional.getConnection();
String sql = "INSERT INTO SCHOOL(CODE,SCHOOL_NAME) VALUES(?,?);";
PreparedStatement pstmt = null;
try {
pstmt = c.prepareStatement(sql);
pstmt.setString(1, school.getCode());
pstmt.setString(2, school.getSchool_name());
affectLine = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return affectLine;
}
@Override
public int delSchool(String school_name) throws TException {
int affectLine = 0;
Connection c = DBUtilTransactional.getConnection();
String sql = "DELETE FROM SCHOOL WHERE SCHOOL_NAME = ?;";
PreparedStatement pstmt = null;
try {
pstmt = c.prepareStatement(sql);
pstmt.setString(1, school_name);
affectLine = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return affectLine;
}
@Override
public int updSchool(School school) throws MyException, TException {
int affectLine = 0;
Connection c = DBUtilTransactional.getConnection();
String sql = "UPDATE SCHOOL SET SCHOOL_NAME = ? WHERE CODE = ?;";
PreparedStatement pstmt = null;
try {
pstmt = c.prepareStatement(sql);
pstmt.setString(1, school.getSchool_name());
pstmt.setString(2, school.getCode());
affectLine = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtilTransactional.close(pstmt);
// DBUtilTransactional.closeConnection();
}
return affectLine;
}
}
2.3服务端
package com.thrift.server;
import org.apache.thrift.TMultiplexedProcessor;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.apache.thrift.transport.layered.TFramedTransport;
import com.thrift.generatecode.school.SchoolService;
import com.thrift.generatecode.student.StudentService;
import com.thrift.server.daoimpl.SchoolServiceImpl;
import com.thrift.server.daoimpl.StudentServiceImpl;
public class ServerStarter {
public static void main(String[] args) throws TTransportException{
// 使用多线程,非阻塞的工作模式
TNonblockingServerSocket server = new TNonblockingServerSocket(8888);
THsHaServer.Args serverArgs = new THsHaServer.Args(server)
.minWorkerThreads(3).maxWorkerThreads(5);
TMultiplexedProcessor processor = new TMultiplexedProcessor();
processor.registerProcessor( "studentService", new StudentService.Processor<StudentService.Iface>(new StudentServiceImpl()));
processor.registerProcessor( "schoolService", new SchoolService.Processor<SchoolService.Iface>(new SchoolServiceImpl()));
// 使用二进制格式传输数据
serverArgs.protocolFactory(new TBinaryProtocol.Factory());
// 使用TFramedTransport方式传输数据
serverArgs.transportFactory(new TFramedTransport.Factory());
serverArgs.processorFactory(new TProcessorFactory(processor));
TServer tserver = new THsHaServer(serverArgs);
// 启动服务
tserver.serve();
}
}
/**
如果在Win环境上启动进程监听后,无法找到观察进程的窗口,可以根据端口号使用命令杀进程
cmd> netstat -ano | findstr 8888
cmd> TASKKILL /F /PID 12305
*/
2.4客户端
package com.thrift.client;
import java.util.List;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TMultiplexedProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.layered.TFramedTransport;
import com.thrift.generatecode.school.School;
import com.thrift.generatecode.school.SchoolService;
import com.thrift.generatecode.student.*;
public class ClientStarter {
public static void main(String[] args){
TTransport transport = null;
try{
transport = new TFramedTransport(new TSocket("127.0.0.1",8888),1000) ;
TProtocol protocol = new TBinaryProtocol(transport) ;
//创建用于访问服务端的对象
TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "studentService");
StudentService.Client client = new StudentService.Client(mp) ;
TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "schoolService");
SchoolService.Client client2 = new SchoolService.Client(mp2) ;
//与服务端建立连接
transport.open();
/**
* 学生表相关业务接口
*/
// 查
System.out.println("查询学生表信息:");
List<Student> stuList = client.queryStudents();
if ( stuList != null )
for ( Student stu : stuList )
System.out.println(
new StringBuilder()
.append("NAME=").append(stu.getName()).append(",")
.append("AGE=").append(stu.getAge())
.toString()
);
/**
* 院校表相关业务接口
*/
// 查
System.out.println("查询院校信息表:");
List<School> schList = client2.querySchools();
if ( schList != null )
for ( School school : schList )
System.out.println(
new StringBuilder()
.append("CODE=").append(school.getCode()).append(",")
.append("SCHOOL=").append(school.getSchool_name())
.toString()
);
}catch (TException e){
e.printStackTrace();
}finally {
if ( transport != null )
transport.close();
}
}
}
代码仓库:传送门
如有问题,请多指教