zl程序教程

您现在的位置是:首页 >  其他

当前栏目

基于Thrift实现跨语言服务

服务语言 实现 基于 thrift
2023-09-14 08:57:29 时间

假设,现在我们有这样一个需求:
要通过一个代理平台,将查询请求转发到后端服务器进行查询。后端存在多种查询服务器,查询方式也不同,比如,有基于SQL的关系数据库查询,也有基于搜索引擎Solr的查询。通过代理平台,将

服务暴露给具有任何编程语言技能的开发人员进行调用。
我们可以选择Thrift来定义语言中性的服务接口,然后通过Thrift编译器将定义生成多种编程语言的客户端代码框架,服务器端使用指定语言进行开发,如Java,最后通过连接Thrift服务器来进行查

询调用。
根据我们的需求,后端服务使用Java实现,而外部使用C#进行调用返回结果,再执行进一步的处理。

Thrift服务定义

首先,看一下,我们给出的示例服务定义,文件命名为queryproxy.thrift,内容如下所示:

namespace java org.shirdrn.queryproxy.thrift.protocol
QueryType 指定查询类型,包括两种类型:查询Solr服务器,或SQL查询 QueryParams 用来设置请求参数 QueryResult 是返回结果对象,封装了查询结果列表,我们将查询结果以JSON列表形式返回 QueryFailureException 如果查询失败,返回该异常 QueryProxyService 定义了服务调用接口

编译Thrift服务定义

根据上面定义的服务,使用Thrift编译器生成不同编程语言的代码,我们生成用于服务器端的Java代码,和客户端服务调用的C#代码,执行命令如下所示:

thrift --gen java queryproxy.thrift

可以直接基于这些代码进行开发服务器端和客户端代码,详见后面说明。

Thrift服务实现

我们使用Java语言实现服务器端的Thrift服务。首先,需要从Thrift的发行包中给出的jar文件,添加到classpath中,基于该库文件进行服务开发。
通过上一步使用Thrift编译器编译生成的Java代码,可以看到一个服务接口:

org.shirdrn.queryproxy.thrift.protocol.QueryProxyService.Iface

我们要对两种类型的查询服务(Solr和SQL)给出实现,首先基于该服务接口来抽象出一层,在抽象类中定义了服务配置对象(读取Properties文件),其中配置文件内容大概如下所示:

query.proxy.thrift.port=9966
Solr查询服务实现

因为后端已经存在一个Solr查询服务器集群(SolrCloud),我们实际上是通过solrj客户端调用来执行查询,所以Thrift服务端的查询也是基于这个原理。下面,看一下Solr查询服务实现类

SolrQueryService的实现内容,代码如下所示:

package org.shirdrn.queryproxy.thrift.service.solr;
public QueryResult query(QueryParams params) throws QueryFailureException, TException {

为简单起见,上面只是使用了一个CloudSolrServer客户端来连接Solr服务器集群(通过ZooKeeper集群)。在query方法中,首先解析查询参数数据,然后构建成Solr查询支持的参数格式,提交到

Solr查询服务器集群,然后等待返回结果QueryResponse ,接着从返回的QueryResponse对象中提取查询命中的结果文档集合,然后转换成Thrift服务定义中满足的返回结果对象形式,通过下面的累

ResultUtils类来实现转换操作,getJSONResults(response)实现如下所示:

private static final String KEY_VERSION = "_version_";

一条结果,构建一个JSON对象,返回一个JSON对象列表。这样,Solr查询服务的Thrift服务就实现了。

SQL查询服务

基于关系数据库的SQL查询就比较容易了,我们简单地使用JDBC来直接进行。我们基于MysQL数据库,实现SQL查询的JDBC配置文件内容,如下所示:

jdbc.jdbcUrl=jdbc:mysql://localhost:3306/wordpress?useUnicode=true characterEncoding=utf-8 zeroDateTimeBehavior=convertToNull transformedBitIsBoolean=true
LOG.info("JDBC: driver=" + driverClass + ", url=" + jdbcUrl + ", user="+ user + ", password=******");
public QueryResult query(QueryParams params) throws QueryFailureException, TException {
public static List String getJSONResults(ResultSet rs, List String fields) throwsSQLException {

返回一组JSON对象,客户端只需要单个解析每一个对象即可。这样基于SQL的Thrift服务也实现完成了。

上面的两类服务都已经实现了,我们最终还要组合成一个服务,然后通过Thrift协议暴露给外部。组合服务的实现类同样实现了org.shirdrn.queryproxy.thrift.protocol.QueryProxyService.Iface

接口,实现代码如下所示:

package org.shirdrn.queryproxy;
public QueryResult query(QueryParams params) throws QueryFailureException, TException {
Iface service = (Iface) ReflectionUtils.getInstance(serviceClass, newObject[] {context});

上面实现,就是通过一个注册方法,将前面实现的两类服务注册管理起来。
下面,我们看一下,基于Thrift的库将组合后的服务以Thrift协议暴露给外部,实际上就是单独启动了一个Thrift服务(关联一个端口),实现的Thrift服务器代码,如下所示:

package org.shirdrn.queryproxy;
int minWorkerThreads = context.getInt("query.proxy.thrift.worker.thread.minCount", 1);
int maxWorkerThreads = context.getInt("query.proxy.thrift.worker.thread.maxCount", 1);
LOG.info("Thrift thread pool: minWorkerThreads=" + minWorkerThreads + ", maxWorkerThreads=" + maxWorkerThreads);

启动该服务以后,外部客户端连接我们配置的9966端口,就可以进行查询调用。

Thrift服务调用

Thrift服务已经发布,我们可以基于Thrift协议进行调用,这里基于Java和C#语言来实现客户端查询调用。

Java查询客户端实现

我们以测试用例的形式展示客户端如何调用。
查询关系数据的SQL调用,客户端实现代码如下所示:

package org.shirdrn.queryproxy.thrift.service.sql;
result=[{"id":"1","post_author":"2"}, {"id":"4","post_author":"2"}, {"id":"57","post_author":"2"}, {"id":"66","post_author":"2"}, {"id":"72","post_author":"2"},
{"id":"75","post_author":"2"}, {"id":"78","post_author":"2"}, {"id":"94","post_author":"2"}, {"id":"100","post_author":"2"}, {"id":"107","post_author":"2"}]

查询Solr服务器的调用,客户端实现代码如下所示:

package org.shirdrn.queryproxy.thrift.service.solr;
result=[{"area":"上海","building_type":1,"floor":28,"category":"住宅","temperature":18,"code":576546387,"latitude":63.054478,"longitude":77.491035,"when":"2013-10-15
02:21:12","id":"0ca9ff70-6b33-42fd-a4e1-23a534b739f5"}, {"area":"上海","building_type":1,"floor":45,"category":"办公建筑","temperature":-
71,"code":824153427,"latitude":6.464198,"longitude":14.567751,"when":"2013-10-15 02:21:20","id":"25f46be0-f875-48b7-9f33-077602b2e820"}, {"area":"上
海","building_type":1,"floor":21,"category":"工业建筑","temperature":16,"code":215543388,"latitude":66.565796,"longitude":34.4735,"when":"2013-10-15 02:21:36","id":"0b32cd10
-ea40-4cdc-b23b-c6edccd7505d"}, {"area":"上海","building_type":1,"floor":53,"category":"工业建筑","temperature":-
23,"code":435344533,"latitude":24.783417,"longitude":119.01849,"when":"2013-10-15 02:21:37","id":"f324b65d-f3be-485a-9ed1-2f12a70b5d87"}, {"area":"上
海","building_type":1,"floor":78,"category":"教育建筑","temperature":-76,"code":298014795,"latitude":9.153033,"longitude":8.273011,"when":"2013-10-15
02:21:39","id":"7a9ea8d8-fad1-4c0b-bdb4-09851eee1432"}, {"area":"上海","building_type":1,"floor":35,"category":"办公建筑","temperature":-
3,"code":228287827,"latitude":13.038927,"longitude":62.056316,"when":"2013-10-15 02:21:42","id":"d6ed8172-a5e6-4391-b039-3b4465eabe9b"}, {"area":"上
海","building_type":1,"floor":77,"category":"公寓","temperature":28,"code":942108396,"latitude":61.970222,"longitude":4.418831,"when":"2013-10-15 02:21:42","id":"85a76085-
90e9-4f62-b10c-5a4895a4d08b"}, {"area":"上海","building_type":1,"floor":40,"category":"办公建
筑","temperature":50,"code":175416908,"latitude":38.17303,"longitude":148.83298,"when":"2013-10-15 02:21:51","id":"1496f73b-8057-4c7b-8ba9-bff4a4ee5189"}, {"area":"上
海","building_type":1,"floor":32,"category":"办公建筑","temperature":-52,"code":299718959,"latitude":30.68149,"longitude":54.327663,"when":"2013-10-15
02:22:05","id":"216a0b93-4872-45aa-baec-2f11e128f76e"}, {"area":"上海","building_type":1,"floor":78,"category":"住
宅","temperature":5,"code":586785207,"latitude":36.27908,"longitude":143.60094,"when":"2013-10-15 02:22:06","id":"10065088-02bf-422e-8761-54635668e1fe"}] C#查询客户端实现

使用C#实现查询服务的调用,首先从Thrift发行包的thrift-0.9.1\lib\csharp目录下C#相关的Thrift库文件导入到开发环境,然后还要将Thrift编译器编译生成的C#代码也加入到开发环境,然后就

可以实现客户端调用查询的代码,我们实现的如下所示:

using System;