Thrift除了可以通过TCP协议访问,还可以通过HTTP/HTTPS协议访问,在java中,thrift提供了一个servlet:org.apache.thrift.server.TServlet,我们只需继承这个TServlet就可以很方便的将TCP服务转换成HTTP/HTTPS服务,下面我们仍然以前面的helloworld接口与实现为例演示。
1.导入所需的jar包
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
2.配置工程的web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>ThriftTest</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext-*.xml
</param-value>
</context-param>
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
3.配置spring mvc的dispatcherServlet-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tool="http://www.springframework.org/schema/tool" xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-4.0.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd"
default-lazy-init="true">
<!-- 定义视图查找规则 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView">
</property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 定义注解驱动Controller方法处理适配器 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
<!-- 定义文件上传处理器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:defaultEncoding="UTF-8" />
<!-- 定义本地化变更拦截器 -->
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<util:list id="interceptors">
<ref bean="localeChangeInterceptor" />
</util:list>
<!-- 定义注解URL映射处理器 -->
<bean id="urlMapping"
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors" ref="interceptors" />
<property name="order" value="1" />
</bean>
</beans>
继承TServlet实现
1、服务端
1)TServlet实现类HelloworldServlet.java
package cn.slimsmart.thrift.demo.spring.servlet;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TServlet;
import cn.slimsmart.thrift.demo.helloworld.HelloWorld;
/**
* 服务端TServlet
*/
public class HelloworldServlet extends TServlet {
private static final long serialVersionUID = 1L;
public HelloworldServlet(HelloWorld.Iface helloWorldImpl) {
super(new HelloWorld.Processor<HelloWorld.Iface>(helloWorldImpl) , new TCompactProtocol.Factory());
}
}
2)配置applicationContext-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"
default-lazy-init="true">
<description>thrift-servlet服务</description>
<!-- servlet适配器,这里必须明确声明,因为spring默认没有初始化该适配器 -->
<bean id="servletHandlerAdapter" class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="order" value="2" />
</bean>
<bean id="helloWorldImpl" class="cn.slimsmart.thrift.demo.helloworld.HelloWorldImpl"/>
<!-- servlet -->
<bean name="/helloworldServlet.do" class="cn.slimsmart.thrift.demo.spring.servlet.HelloworldServlet">
<constructor-arg>
<ref bean="helloWorldImpl"/>
</constructor-arg>
</bean>
</beans>
3)启动tomcat服务
4)客户端调用测试
HelloWorldServletTest.java
HelloWorldServletTest.java
package cn.slimsmart.thrift.demo.spring.servlet;
import org.apache.http.HttpVersion;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.BasicClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransportException;
import cn.slimsmart.thrift.demo.helloworld.HelloWorld;
public class HelloWorldServletTest {
public static void main(String[] args) {
String url = "http://127.0.0.1:8080/thrift-demo/helloworldServlet.do";
BasicHttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
// Disable Expect-Continue
params.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
// Enable staleness check
params.setParameter("http.connection.stalecheck", true);
HttpConnectionParams.setSoTimeout(params, 10000); // 10 secondes
HttpConnectionParams.setConnectionTimeout(params, 10000); // 10 secondes
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", 8080, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
BasicClientConnectionManager cm = new BasicClientConnectionManager(schemeRegistry);
THttpClient thc = null;
try {
thc = new THttpClient(url, new DefaultHttpClient(cm, params));
TProtocol loPFactory = new TCompactProtocol(thc);
HelloWorld.Client client = new HelloWorld.Client(loPFactory);
System.out.println(client.sayHello("Jack"));
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
}
}
}
2、客服端
通过代理实现
1、服务端
服务端代理ThriftServletServerProxy.java
package cn.slimsmart.thrift.demo.spring.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.thrift.TException;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TIOStreamTransport;
import org.apache.thrift.transport.TTransport;
import org.springframework.util.StringUtils;
@SuppressWarnings("serial")
public class ThriftServletServerProxy extends HttpServlet {
private final TProcessor processor;
private final TProtocolFactory inProtocolFactory;
private final TProtocolFactory outProtocolFactory;
private final Collection<Map.Entry<String, String>> customHeaders;
@SuppressWarnings({ "rawtypes", "unchecked" })
public ThriftServletServerProxy(String serviceInterface, String serviceIface,
Object serviceImplObject) throws Exception {
super();
Class Processor = Class.forName(serviceInterface + "$Processor");
Class Iface = Class
.forName(StringUtils.hasText(serviceIface) ? serviceIface
: serviceInterface + "$Iface");
Constructor con = Processor.getConstructor(Iface);
TProcessor processor = (TProcessor) con.newInstance(serviceImplObject);
this.processor = processor;
this.inProtocolFactory = new TCompactProtocol.Factory();
this.outProtocolFactory = new TCompactProtocol.Factory();
this.customHeaders = new ArrayList<Map.Entry<String, String>>();
}
public ThriftServletServerProxy(String serviceInterface,
Object serviceImplObject) throws Exception {
this(serviceInterface,null,serviceImplObject);
}
/**
* @see HttpServlet#HttpServlet()
*/
public ThriftServletServerProxy(TProcessor processor,
TProtocolFactory inProtocolFactory,
TProtocolFactory outProtocolFactory) {
super();
this.processor = processor;
this.inProtocolFactory = inProtocolFactory;
this.outProtocolFactory = outProtocolFactory;
this.customHeaders = new ArrayList<Map.Entry<String, String>>();
}
/**
* @see HttpServlet#HttpServlet()
*/
public ThriftServletServerProxy(TProcessor processor,
TProtocolFactory protocolFactory) {
this(processor, protocolFactory, protocolFactory);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
TTransport inTransport = null;
TTransport outTransport = null;
try {
response.setContentType("application/x-thrift");
if (null != this.customHeaders) {
for (Map.Entry<String, String> header : this.customHeaders) {
response.addHeader(header.getKey(), header.getValue());
}
}
InputStream in = request.getInputStream();
OutputStream out = response.getOutputStream();
TTransport transport = new TIOStreamTransport(in, out);
inTransport = transport;
outTransport = transport;
TProtocol inProtocol = inProtocolFactory.getProtocol(inTransport);
TProtocol outProtocol = outProtocolFactory
.getProtocol(outTransport);
processor.process(inProtocol, outProtocol);
out.flush();
} catch (TException te) {
throw new ServletException(te);
}
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
public void addCustomHeader(final String key, final String value) {
this.customHeaders.add(new Map.Entry<String, String>() {
public String getKey() {
return key;
}
public String getValue() {
return value;
}
public String setValue(String value) {
return null;
}
});
}
public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) {
this.customHeaders.clear();
this.customHeaders.addAll(headers);
}
}
在applicationContext-servlet.xml添加如下配置:
<!-- servlet proxy -->
<bean name="/helloworldServletProxy.do" class="cn.slimsmart.thrift.demo.spring.servlet.ThriftServletServerProxy">
<constructor-arg index="0" value="cn.slimsmart.thrift.demo.helloworld.HelloWorld"/>
<constructor-arg>
<ref bean="helloWorldImpl"/>
</constructor-arg>
</bean>
启动tomcat服务。
2、客户端
1)客户端代理ThriftServletClientProxy.java
package cn.slimsmart.thrift.demo.spring.servlet;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.http.HttpVersion;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.BasicClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransportException;
@SuppressWarnings("deprecation")
public class ThriftServletClientProxy {
/**
* servlet 地址
*/
private String servletUrl;
public String getServletUrl() {
return servletUrl;
}
public void setServletUrl(String servletUrl) {
this.servletUrl = servletUrl;
}
/**
* thrift 接口
*/
private String serviceInterface;
public String getServiceInterface() {
return serviceInterface;
}
public void setServiceInterface(String serviceInterface) {
this.serviceInterface = serviceInterface;
}
private static BasicHttpParams params;
static {
params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
// Disable Expect-Continue
params.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
// Enable staleness check
params.setParameter("http.connection.stalecheck", true);
HttpConnectionParams.setSoTimeout(params, 10000); // 10 secondes
HttpConnectionParams.setConnectionTimeout(params, 10000); // 10 secondes
ConnManagerParams.setMaxTotalConnections(params, 20);
ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20);
ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object getClient() {
Object object = null;
try {
BasicClientConnectionManager cm = new BasicClientConnectionManager(getSchemeRegistry());
THttpClient thc = new THttpClient(getServletUrl(), new DefaultHttpClient(cm, params));
TProtocol loPFactory = new TCompactProtocol(thc);
Class client = Class.forName(getServiceInterface() + "$Client");
Constructor con = client.getConstructor(TProtocol.class);
object = con.newInstance(loPFactory);
} catch (TTransportException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return object;
}
public SchemeRegistry getSchemeRegistry() {
SchemeRegistry schemeRegistry = new SchemeRegistry();
URL url;
try {
// 分析url,取出端口
url = new URL(getServletUrl());
String protocol = url.getProtocol();
int port = url.getPort();
if (-1 == port) {
if ("https".equals(protocol)) {
port = 443;
} else {
port = 80;
}
}
if ("https".equals(protocol)) {
schemeRegistry.register(new Scheme("https", port, SSLSocketFactory.getSocketFactory()));
} else {
schemeRegistry.register(new Scheme("http", port, PlainSocketFactory.getSocketFactory()));
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return schemeRegistry;
}
}
2)spring配置applicationContext-servlet-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"
default-lazy-init="true">
<description>thrift配置文件 </description>
<bean id="thriftServletClientProxy" class="cn.slimsmart.thrift.demo.spring.servlet.ThriftServletClientProxy">
<property name="servletUrl">
<value>
http://127.0.0.1:8080/thrift-demo/helloworldServletProxy.do
</value>
</property>
<property name="serviceInterface" value="cn.slimsmart.thrift.demo.helloworld.HelloWorld">
</property>
</bean>
</beans>
3)测试启动类
HelloWorldServletProxyTest.java
package cn.slimsmart.thrift.demo.spring.servlet;
import org.apache.thrift.TException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.slimsmart.thrift.demo.helloworld.HelloWorld;
public class HelloWorldServletProxyTest {
@SuppressWarnings("resource")
public static void main(String[] args) throws TException {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/servlet/applicationContext-servlet-client.xml");
ThriftServletClientProxy thriftServletClientProxy = (ThriftServletClientProxy)context.getBean(ThriftServletClientProxy.class);
HelloWorld.Iface client = (HelloWorld.Iface)thriftServletClientProxy.getClient();
System.out.println(client.sayHello("han meimei"));
}
}