`
hanqunfeng
  • 浏览: 1526343 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Thrift--Spring集成ThriftServlet

 
阅读更多

Thrift除了可以通过TCP协议访问,还可以通过HTTP/HTTPS协议访问,在java中,thrift提供了一个servlet:org.apache.thrift.server.TServlet,我们只需继承这个TServlet就可以很方便的将TCP服务转换成HTTP/HTTPS服务,参考http://hanqunfeng.iteye.com/blog/1936556,为其中的ContractManage服务提供servlet接口,如下:

一。基本实现方法

1)服务端

package servlet;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TServlet;
import thrift.service.ContactManager;
import thrift.service.impl.ContactManagerImpl;

@SuppressWarnings("serial")
public class ContractManageServlet extends TServlet {
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public ContractManageServlet(ContactManagerImpl contractManage) {
		super(new ContactManager.Processor(contractManage),
				new TCompactProtocol.Factory());
	}

	
	
}

 只需要改写构造函数即可,将实现类通过参数的形式传递给父类。

通过spring对servlet的管理(参考:http://hanqunfeng.iteye.com/blog/605174)来注入参数:

 

 <!-- 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"></property>
    </bean>
    
    
    <!-- servlet -->  
    <bean name="/contractManageServlet.do" class="servlet.ContractManageServlet"> 
    	<constructor-arg>
    		<ref bean="contactManagerImpl"/>
    	</constructor-arg>  
    </bean>
    

 

2)客户端

 模拟一个客户端调用:

 

package 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 thrift.service.ContactManager;

public class ContractMain {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String url = "http://localhost:8080/ThriftServer/contractManageServlet.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;
		try {
			thc = new THttpClient(url, new DefaultHttpClient(cm, params));
			TProtocol loPFactory = new TCompactProtocol(thc);
			ContactManager.Client client = new ContactManager.Client(loPFactory);
			System.out.println(client.getAll());
		} catch (TTransportException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

 OK,每增加一个thrift服务只需要写一个servlet即可,不过每个servlet只是存在一个构造函数,每次都要增加一个类略显繁琐,所以可以使用一个代理类来实现。

 

二。代理实现方法

1)服务端

代理类copy了org.apache.thrift.server.TServlet的代码,并且增加了两个构造函数,使其可以按需创建对象。

public class ThriftServletProxy extends HttpServlet {

………………copy………………

        @SuppressWarnings({ "rawtypes", "unchecked" })
	public ThriftServletProxy(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 ThriftServletProxy(String serviceInterface,
			Object serviceImplObject) throws Exception {
		this(serviceInterface,null,serviceImplObject);

	}

………………copy……………………
}

 这样,每增加一个服务只需要在spring配置文件中增加配置即可,不需要再单独创建一个servlet:

 <!-- 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"></property>
    </bean>
    
    <!-- servlet proxy -->  
    <bean name="/contractManageServletProxy.do" class="servlet.ThriftServletProxy"> 
    	<constructor-arg index="0" value="thrift.service.ContactManager"/>
    	<constructor-arg index="1" value="interfaceI.ContractManage.Iface"/>
    	<constructor-arg>
    		<ref bean="contactManagerImpl"/>
    	</constructor-arg>  
    </bean>
    
     <bean name="/userServiceServletProxy.do" class="servlet.ThriftServletProxy"> 
    	<constructor-arg index="0" value="thrift.service.UserService"/>
    	<constructor-arg index="1" value="interfaceI.Userservice.Iface"/>
    	<constructor-arg>
    		<ref bean="userServiceImpl"/>
    	</constructor-arg>  
    </bean>

 

2)客户端

客户端同样采用代理的方式来实现

package thrift.proxy;

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) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			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) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return schemeRegistry;

	}
}

 

每调用一个接口,在spring中增加一个配置即可:

	<bean id="contractManage"
		class="thrift.proxy.ThriftServletClientProxy">
		<property name="servletUrl">
			<value>
				http://localhost:8080/ThriftServer/contractManageServletProxy.do
			</value>
		</property>
		<property name="serviceInterface"
			value="thrift.service.ContactManager">
		</property>
	</bean>
	
	<bean id="userService"
		class="thrift.proxy.ThriftServletClientProxy">
		<property name="servletUrl">
			<value>
				http://localhost:8080/ThriftServer/userServiceServletProxy.do
			</value>
		</property>
		<property name="serviceInterface"
			value="thrift.service.UserService">
		</property>
	</bean>

 调用方式参考代码中的IndexController。

 

分享到:
评论
2 楼 brilliant1984 2014-11-27  
真棒。 可以用,但目前0.9.1一个端口支持多个service类,正在楼主的基础上改进
1 楼 MrwenQ 2014-09-25  
楼主的代码 我部署报错是什么情况啊,求解释

相关推荐

Global site tag (gtag.js) - Google Analytics