`
steeven
  • 浏览: 316646 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

RMI/WebService冗余服务器的通用客户端。

阅读更多
需求:
企业应用要有有redundancy, 两台或者多台服务器提供HA(High available)服务,提供热备用。服务一般是EJB/RMI/Web Service等服务, 在一台发生服务故障后,客户端一般自动切换到其它可用服务器, 所有服务器都依次fail后才报错。

EJB也应该适用,但是一般EJB容器都提供了更完善的HA机制和策略。这里不cover.

不足和限制:
1. 暂时没有考虑内网/外网优先顺序。
2. 使用接口+java proxy实现拦截,接口必须。
3. web service使用动态绑定。
4. 一台失败后,遍历所有服务器,故障服务器没有排除
5. java 6以上测试通过
6. 通信失败的检测不见得完善
7. Web Service采用POJO发布,要求Port和Service名称和接口名称相同,便于简化查找。建议定义为常量。

测试服务代码(同时提供rmi/ws服务):
================================================
package org.steeven.remote;

import java.rmi.Remote;
import java.rmi.RemoteException;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

@WebService(targetNamespace = Hello.WS_QNAME)
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface Hello extends Remote {
	public static final String WS_QNAME = "http://steeeven.org/client";

	public String add(String a, String b) throws RemoteException;
}

package org.steeven.remote;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.ws.Endpoint;

@WebService(targetNamespace = Hello.WS_QNAME, portName = "Hello", serviceName = "Hello")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class ServerImpl extends UnicastRemoteObject implements Hello {

	private static final long serialVersionUID = 1L;

	protected ServerImpl() throws RemoteException {
		super();
	}

	public String add(String a, String b) throws RemoteException {
		if ("steeven".equals(b))
			throw new RuntimeException("SH IT");
		return a + b;
	}

	public static void main(String[] args) throws Exception {
		// 发布为RMI服务
		Registry registry = LocateRegistry.createRegistry(18888);
		registry.bind("server", new ServerImpl());
		// 发布为web service
		Endpoint.publish("http://0.0.0.0:18889/server", new ServerImpl());
		System.err.println("Server ready");
	}
}



实现和测试代码:
===========================================
package org.steeven.client;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.rmi.ConnectException;
import java.util.LinkedList;

public class HaClient<T> {
	private LinkedList<String> preferredUrls = new LinkedList<String>();
	private LinkedList<String> otherUrls = new LinkedList<String>();
	private T currentService;
	private Class<T> type;
	private ProtocolHandler protocalHandler;

	InvocationHandler invokeHandler = new InvocationHandler() {
		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			try {
				try {
					System.out.println("invoke: " + method);
					return method.invoke(getServer(false), args);
				} catch (Throwable e) {
					if (protocalHandler
							.isServiceOut(e instanceof InvocationTargetException ? e
									.getCause()
									: e)) {
						System.out.println("Prefered server failed");
						return method.invoke(getServer(true), args);
					} else
						throw e;
				}
			} catch (InvocationTargetException ex) {
				throw ex.getCause(); // InvocationTargetExcetpion.getCause()
			}
		}
	};

	protected HaClient(ProtocolHandler protocalHandler, Class<T> type,
			String service, InetSocketAddress... nameServers) {
		this.protocalHandler = protocalHandler;
		this.type = type;
		for (InetSocketAddress address : nameServers) {
			if (isLocalHost(address.getAddress()))
				preferredUrls.add(protocalHandler.buildServiceUrl(service,
						address));
			else
				otherUrls
						.add(protocalHandler.buildServiceUrl(service, address));
		}
	}

	protected HaClient(ProtocolHandler protocalHandler, Class<T> type,
			LinkedList<String> preferredUrls, LinkedList<String> otherUrls) {
		this.type = type;
		this.protocalHandler = protocalHandler;
		this.preferredUrls = preferredUrls;
		this.otherUrls = otherUrls;
	}

	/**
	 * check preferred server (local host) list first, then other server list.
	 */
	synchronized protected T getServer(boolean retry) throws Exception {
		// try local first, then others
		if (!retry && currentService != null) {
			System.out.println("user last server");
			return currentService;
		}
		currentService = null;
		tryServers(preferredUrls); // try preferred
		if (currentService == null)
			tryServers(otherUrls); // try others
		if (currentService == null)
			throw new ConnectException("No server available now");
		return currentService;
	}

	/**
	 * remove active from list to current, roll failed server to bottom.
	 */
	@SuppressWarnings("unchecked")
	private void tryServers(LinkedList<String> list) throws Exception {
		LinkedList<String> badList = new LinkedList<String>();
		try {
			String url;
			while (list.size() > 0) {
				url = list.peek();
				try {
					System.out.println("trying: " + url);
					// TODO URI InetAddress.isReachable(n) to ping first
					currentService = (T) protocalHandler.lookupService(url,
							type);
					return;
				} catch (Exception e) {
					if (protocalHandler.isServiceOut(e)) {
						System.out.println("failed to try: " + url);
						list.pop();
						badList.add(url);
					} else
						throw e;
				}
			}
		} finally {
			list.addAll(badList);
		}
	}

	@SuppressWarnings("unchecked")
	public T newInstance() {
		return (T) Proxy.newProxyInstance(type.getClassLoader(),
				new Class<?>[] { type }, invokeHandler);
	}

	private boolean isLocalHost(InetAddress address) {
		boolean local = address.isLoopbackAddress()
				|| address.isAnyLocalAddress();
		if (!local)
			try {
				local = (NetworkInterface.getByInetAddress(address) != null);
			} catch (SocketException e) {
			}
		return local;
	}

	public static <T> T create(ProtocolHandler protocol, Class<T> type,
			String service, InetSocketAddress... addresses) {
		return new HaClient<T>(protocol, type, service, addresses)
				.newInstance();
	}

	public static <T> T create(ProtocolHandler protocol, Class<T> type,
			String service, int port, String... hosts) {
		InetSocketAddress[] addresses = new InetSocketAddress[hosts.length];
		for (int i = 0; i < addresses.length; i++) {
			addresses[i] = new InetSocketAddress(hosts[i], port);
			if (addresses[i].getAddress() == null)
				throw new IllegalArgumentException("Invalid host: " + hosts[i]);
		}
		return new HaClient<T>(protocol, type, service, addresses)
				.newInstance();
	}

	public abstract static class ProtocolHandler {

		public String buildServiceUrl(String service, InetSocketAddress address) {
			return getProtocol() + "://"
					+ address.getAddress().getHostAddress() + ":"
					+ address.getPort() + service;
		}

		public abstract String getProtocol();

		public abstract boolean isServiceOut(Throwable e);

		public abstract Object lookupService(String url, Class<?> type)
				throws Exception;

	}
}



package org.steeven.client;

import java.net.ConnectException;
import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;

import org.steeven.remote.Hello;

/**
 * High Available Http client. Provide service proxy that auto detect available
 * RMI service form several servers.
 * 
 * @author xli
 * 
 * @param <T>
 */
public class HttpProtocolHandler extends HaClient.ProtocolHandler {

	private String qname;

	public HttpProtocolHandler(String qname) {
		this.qname = qname;
	}

	@Override
	public String getProtocol() {
		return "http";
	}

	public boolean isServiceOut(Throwable e) {
		return e instanceof WebServiceException
				&& e.getCause() instanceof ConnectException;
	}

	@Override
	public Object lookupService(String url, Class<?> type) throws Exception {
		Service service = Service.create(new URL(url + "?wsdl"), new QName(
				qname, type.getSimpleName()));
		return service.getPort(new QName(Hello.WS_QNAME, type.getSimpleName()),
				type);
	}

	public static void main(String args[]) throws Exception {
		Hello hello = HaClient.create(new HttpProtocolHandler(Hello.WS_QNAME),
				Hello.class, "/server", 18889, "10.80.1.113", "10.80.2.184",
				"10.80.1.117");
		while (true) {
			try {
				System.out.println("------------------------new-----------------");
				System.out.println(hello.add("hello ", "steeven1"));
			} catch (Exception e) {
				e.printStackTrace();
			}
			Thread.sleep(3000);
		}
	}
}



package org.steeven.client;

import java.rmi.ConnectException;
import java.rmi.Naming;

import org.steeven.remote.Hello;

/**
 * High Available RMI client. Provide service proxy that auto detect available
 * RMI service form several servers.
 * 
 * @author xli
 * 
 * @param <T>
 */
public class RmiProtocolHandler extends HaClient.ProtocolHandler {
	
	@Override
	public String getProtocol() {
		return "rmi";
	}

	public boolean isServiceOut(Throwable e) {
		return e instanceof ConnectException;
	}

	@Override
	public Object lookupService(String url, Class<?> type) throws Exception {
		return Naming.lookup(url);
	}

	public static void main(String args[]) throws Exception {
		Hello hello = HaClient.create(new RmiProtocolHandler(), Hello.class,
				"/server", 18888, "10.80.1.113", "10.80.2.184", "10.80.1.117");
		while (true) {
			try {
				System.out.println("------------------------new-----------------");
				System.out.println(hello.add("hello ", "steeven1"));
			} catch (Exception e) {
				e.printStackTrace();
			}
			Thread.sleep(3000);
		}
	}
}
分享到:
评论
3 楼 steeven 2010-01-19  
C_J 写道
好像挺不错,学习了。

想请教几个问题:

1.一个端口会有多个service?

2.ping机制?你是说ICMP协议的ping么?如果机器禁止了怎么办呢?


这里的端口就是JNDI或者HTTP的端口,里面可能注册了多个service.
ping,对,经常有机器关掉。但是自己的项目里面还是可控的
2 楼 C_J 2010-01-17  
好像挺不错,学习了。

想请教几个问题:

1.一个端口会有多个service?

2.ping机制?你是说ICMP协议的ping么?如果机器禁止了怎么办呢?
1 楼 steeven 2010-01-17  
还有几个问题:
1. 没有考虑到一个端口上的多个service, 没有整体检测, 替换.
2. 没有采用ping机制,效率不高. 有的server可能会不支持.
3. 没有包装成URL对象.
4. WebService相关参数应该从接口反射本身取得

相关推荐

    HTTP客户端,HTTP服务器,RMI客户端和服务器

    具体到实验文件"rmi",这可能是RMI相关代码或者教程的集合,可能包含了服务器端和客户端的示例代码,以及如何在Java中设置和运行RMI服务的说明。"cs"可能代表"Client-Server",也就是客户端-服务器相关的文件,可能...

    RMI 服务器与客户端源码,很简单,自己做的,适合初学者

    本示例中,"RMI 服务器与客户端源码"很可能是为了教学目的设计的,适合初学者了解RMI的基本工作原理和实践操作。 创建RMI服务器主要涉及以下步骤: 1. **定义远程接口**:远程接口是一个Java接口,其中声明了所有...

    RMI RMI-IIOP 客户端服务器交互

    **客户端服务器交互流程:** 1. **客户端初始化ORB**:客户端创建ORB实例,设置必要的配置,如服务器地址和端口。 2. **查找IR(接口 Repository)**:客户端通过ORB查找IR,获取远程接口的IRI(接口引用标识符)。 ...

    JMX以RMI方式连接的场景示例

    JMX以RMI方式连接的场景示例 JMX(Java Management Extensions)是一种Java技术,用于管理...JMX框架提供了一种灵活、可扩展和高效的方式来管理和监控应用程序,而RMI连接方式允许远程客户端访问MBean提供的管理功能。

    (转)通用JAVA RMI服务器框架

    【标题】:“(转)通用JAVA RMI服务器框架” 【正文】: 在Java远程方法调用(Remote Method Invocation,RMI)技术中,构建一个通用的服务器框架是提高开发效率和代码可复用性的关键。Java RMI允许分布式计算,使得...

    分布式程序java 实验名称 基于Java RMI的C/S编程实验

    在这个基于Java的C/S(客户端/服务器)编程实验中,我们将探讨两种不同的实现方式:基于Socket的通信和基于Java Remote Method Invocation (RMI) 的通信。 首先,我们来看基于Socket的C/S编程。Socket是网络编程的...

    编写简单的HTTP 1.0客户端/服务器程序:.编写多线程Web服务器:编写简单的RMI程序

    本实验旨在通过编写简单的HTTP 1.0客户端/服务器程序、多线程Web服务器以及RMI程序,帮助你深入理解这些关键技术。 首先,我们来看HTTP 1.0客户端/服务器程序。HTTP是超文本传输协议,它是互联网上应用最为广泛的一...

    java RMI客户端调用远程服务器系统命令

    RMI使得客户端能够调用运行在远程服务器上的对象的方法,就像调用本地对象一样。这个过程涉及到一系列复杂的步骤,包括序列化、远程接口定义、注册表服务和反序列化等。 首先,我们要理解RMI的基本概念。在RMI中,...

    RMI客户端调用远程服务器方法-远程方法调用

    以下是对“RMI客户端调用远程服务器方法”这一主题的详细解释: 一、RMI基本概念 1. 远程接口(Remote Interface):定义了可以被远程调用的方法,这些接口需要继承自`java.rmi.Remote`接口,并且可能抛出`java....

    客户端服务器,多线程服务器,远程RMI会议

    在给定的实验中,"客户端服务器"部分可能包括了如何建立这样的通信链路,以及如何在Java中编写请求和响应的逻辑。 其次,多线程服务器是用于处理并发请求的高效手段。当多个客户端同时连接到服务器时,单线程服务器...

    rmi 客户端 和 服务端 代码

    RMI的核心概念包括客户端、服务端、接口和 stub/skeleton。 **1. RMI 客户端** RMI 客户端是执行远程调用的应用程序。它通过Java的`java.rmi.*`包中的类与服务端建立连接。客户端需要知道服务端的接口(远程接口)...

    C/S模式下的java图书管理系统

    3. 网络通信:通过Java的Socket编程或者更高层次的API如RMI(远程方法调用)或JDBC(Java数据库连接)与服务器进行通信,发送请求并接收响应。 服务器端的主要职责包括: 1. 数据存储:服务器通常与数据库系统相...

    TCP协议服务器/客户端框架

    在实际应用中,开发者可以利用这个TCP协议服务器/客户端框架,快速搭建自己的网络应用,比如游戏服务器、实时数据交换系统或者分布式应用等。通过理解和掌握上述知识点,开发者可以更好地理解和使用这个框架,进行...

    java-rmi-server.rar_Java RMI_rmi _服务器

    RMI服务器允许一个Java对象在远程机器上执行方法,使得客户端可以像调用本地对象一样调用远程对象,极大地简化了分布式应用的开发。下面我们将深入探讨Java RMI服务器的搭建过程以及其具体实现步骤。 一、RMI基础...

    rmi-client-server:RMI 客户端和服务器的简单 Java 实现

    rmi-客户端-服务器 RMI 客户端和服务器的简单 Java 实现 服务器 shell 脚本会将服务器库编译为 jar,然后运行服务器。 像这样运行它: ./server.sh 现在启动客户端: ./client.sh 客户端当前实现为 Groovy 脚本...

    rmi技术客户端调用服务器的函数解决问题

    rmi技术客户端调用服务器的函数解决问题 刚刚开始准备这个rmi作业的时候,我都有点无从下手。于是我先开始一起找资料,看书,经过不断的尝试编码,以及总结错误,为后期的工作奠定了基础。 在基础知识基本搞定的...

    rmi服务端与客户端小程序

    本项目"rmi服务端与客户端小程序"提供了简单易懂的示例,帮助初学者快速理解和上手RMI。 1. **RMI基本概念** - **远程对象**:在远程JVM上运行的对象,可以通过RMI接口被本地对象调用。 - **接口**:定义了远程...

    rmi客户端和服务端例子程序

    在Java的远程方法调用(Remote Method Invocation, RMI)技术中,客户端和服务端的交互是实现分布式系统的关键。RMI允许一个Java对象在一台计算机上执行另一个计算机上的对象的方法,就像是本地调用一样。本示例将...

    rmi 远程调用 实现客户端之间会话

    在Java RMI中,一个JVM作为服务器,暴露远程接口,而其他JVM作为客户端,通过这些接口进行远程调用。 标题中的“rmi 远程调用 实现客户端之间会话”指的是利用RMI技术让多个客户端与同一服务端建立会话,进行交互。...

    Java_RMI_ClientServer:RMI客户端-服务器示例

    - **启动RMIServer和RMIClient**: 通常,服务器会有一个启动RMI服务的main方法,而客户端也有一个启动并连接到服务器的main方法。 4. **Java_RMI_ClientServer示例** - 项目中的`Java_RMI_ClientServer-master`很...

Global site tag (gtag.js) - Google Analytics