标签:hessian
Hessian客户端向服务端发送数据
场景:项目日志Token处理,即用户发送一个请求时生成一个日志Token,该Token从各个服务之间传递,并使用该Token记录日志,直至请求结束。可以根据该Token定位所有日志。
问题:由于目前项目使用Hessian协议,所有Token必须使用Hessian传递。查阅相关资料,发现可以请求头传递数据。
解决方法:定义与线程相关的请求头上下文,在客户端发送请求之前,增加请求头。服务端获取请求时,从请求中解决请求头,并放入请求头上下文中,供服务端使用。
实现:
1.定义请求头上下文HessianHeaderContext,代码如下:
package org.enyes.hessian
/**
* Hessian协议请求头上下文。
* <pre>
* 1.该类使用ThreadLocal将客户端请求头信息传递给服务端。
* 2.请求头在HessianProxy发送请求之前,将该类中得请求头附加到请求中。
* 3.服务端使用HessianServiceExporter中获取请求头,并放入HessianHeaderContext中,提供服务端使用。
* 4.使用完记得调用#close方法,防止ThreadLocal内存泄露。
* </pre>
* Created by enyes on 15/8/30.
* @see HessianProxy
* @see HessianProxyFactory
* @see HessianServiceExporter
*/
public class HessianHeaderContext {
private static final ThreadLocal<HessianHeaderContext> THREAD_LOCAL = new ThreadLocal<>();
private Map<String, String> headers = new HashMap<>();
private HessianHeaderContext() {
}
public static HessianHeaderContext getContext() {
HessianHeaderContext context = THREAD_LOCAL.get();
if (context == null) {
context = new HessianHeaderContext();
THREAD_LOCAL.set(context);
}
return context;
}
public void addHeader(String name, String value) {
headers.put(name, value);
}
public String getHeader(String name) {
return headers.get(name);
}
public Map<String, String> getHeaders() {
return headers;
}
public static void close() {
HessianHeaderContext context = THREAD_LOCAL.get();
if (context != null) {
context.headers.clear();
THREAD_LOCAL.set(null);
}
}
}
2.拓展客户端HessianProxy,在发送请求时添加上下文请求头,如下:
/**
* 拓展HessianProxy,在客户端发送请求之前,将HessianHeaderContext中的请求头添加到请求中。
* Created by enyes on 15/8/30.
* @see org.enyes.hessian.HessianProxyFactory
*/
public class HessianProxy extends com.caucho.hessian.client.HessianProxy {
protected HessianProxy(URL url, HessianProxyFactory factory) {
super(url, factory);
}
protected HessianProxy(URL url, HessianProxyFactory factory, Class<?> type) {
super(url, factory, type);
}
@Override
protected void addRequestHeaders(HessianConnection conn)
{
super.addRequestHeaders(conn);
// add Hessian Header
Map<String, String> headerMap = HessianHeaderContext.getContext().getHeaders();
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
conn.addHeader(entry.getKey(), entry.getValue());
}
}
}
3.重写HessianProxyFactory,集成新拓展的HessianProxy,如下:
/**
* 拓展HessianProxyFactory,使用新拓展的HessianProxy。
* <pre>
* 注:如果使用Spring集成,HessianProxyFactoryBean需要设置proxyFactory为该类对象。
* </pre>
* Created by enyes on 15/8/30.
* @see HessianProxy
*/
public class HessianProxyFactory extends com.caucho.hessian.client.HessianProxyFactory {
@Override
public Object create(Class<?> api, URL url, ClassLoader loader)
{
if (api == null) {
throw new NullPointerException("api must not be null for HessianProxyFactory.create()");
}
InvocationHandler handler = new HessianProxy(url, this, api);
return Proxy.newProxyInstance(loader,
new Class[]{api, HessianRemoteObject.class},
handler);
}
}
注意:HessianProxy与HessianProxyFactory命名仍与hessian.jar中一致,一时没有想到合适的名称。
4.由于项目与Spring集成,所以拓展Spring HessianServiceExporter,解析请求头。
/**
* 拓展Spring HessianServiceExporter类,在服务端接受请求时,
* 接收客户端请求头,并保存到HessianHeaderContext中,供服务端使用。
* spring 服务端xml文件配置该类。
* Created by enyes on 15/8/30.
*/
public class HessianServiceExporter extends org.springframework.remoting.caucho.HessianServiceExporter {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (!"POST".equals(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
new String[] {"POST"}, "HessianServiceExporter only supports POST requests");
}
handleHessianHeader(request);
response.setContentType(CONTENT_TYPE_HESSIAN);
try {
invoke(request.getInputStream(), response.getOutputStream());
} catch (Throwable ex) {
throw new NestedServletException("Hessian skeleton invocation failed", ex);
} finally {
HessianHeaderContext.close();
}
}
protected void handleHessianHeader(HttpServletRequest request) {
HessianHeaderContext context = HessianHeaderContext.getContext();
Enumeration enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String name = enumeration.nextElement().toString();
String value = request.getHeader(name);
context.addHeader(name, value);
}
}
}
5.客户端Spring配置文件修改:
<bean id="hessianProxyFactory" class="org.enyes.hessian.HessianProxyFactory" />
<bean id="hessianServer" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="proxyFactory" ref="hessianProxyFactory" />
<property name="serviceUrl" value="${hessian.remote.provider_url}/hessianServer"/>
<property name="serviceInterface" value="org.enyes.hessian.service.HessianServer"/>
<property name="overloadEnabled" value="true" />
</bean>
6.服务端Spring配置文件,如下:
<bean name="/hessianServer" class="org.enyes.hessian.HessianServiceExporter">
<property name="service" ref="hessianServerImpl"/>
<property name="serviceInterface" value="org.enyes.hessian.service.HessianServer"/>
</bean>
7.至次,客户端向HessianHeaderContext添加请求头后,服务端的HessianHeaderContext都能获取到。
Controller测试
@Controller()
@RequestMapping("/hessian")
public class HessianController {
@Autowired
private HessianServer hessianServer;
@RequestMapping("passHeader")
@ResponseBody
public String passHeader() {
HessianHeaderContext context = HessianHeaderContext.getContext();
context.addHeader("log.token", "logToken_111111");
hessianServer.passHeader();
HessianHeaderContext.close();
return "success";
}
}
Junit测试
@Test
public void testPassHeader() throws MalformedURLException {
//Spring Hessian代理Servlet
String url = "http://localhost:8081/demoProvider/hessianServer";
HessianProxyFactory factory = new HessianProxyFactory();
HessianHeaderContext context = HessianHeaderContext.getContext();
context.addHeader("log.token", UUID.randomUUID().toString());
HessianServer api = (HessianServer) factory.create(HessianServer.class, url);
api.passHeader();
}
服务端Service:
@Service
public class HessianServerImpl implements HessianServer {
private static final Logger LOG = LoggerFactory.getLogger(HessianServerImpl.class);
@Override
public String domainChange(User user) throws BusinessException {
throw new IllegalArgumentException( "domainChange. user:" + user);
}
@Override
public void passHeader() {
HessianHeaderContext context = HessianHeaderContext.getContext();
String logToken = context.getHeader("log.token");
LOG.warn("passHeader. Header[log.token]={}", logToken);
}
}
结束语:本文提供一种思路,读者可以查看相关源码或者他人博客。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:hessian
原文地址:http://blog.csdn.net/fangchao2061/article/details/48106283