基于JMS消息中间件的分布式系统初探究(二)

在上一篇文章中,我们解决了子系统之间的通讯问题,并跑起来了一个模型项目。这里我们要详细实现服务端程序。

我们在服务端运行Spring,利用Spring的IoC容器来管理所有的Service组件,然后根据接收到的JMS消息通过反射动态调用Service方法。

首先要先设计一下协议:

{/*** 要调用的接口全限定名*/private String interfaceName;/*** 要调用的方法名*/private String methodName;/*** 方法参数列表*/private List<Object> argList;// setter and setters …}

由上协议类可以看出,我们是通过接口名、方法名和参数列表来定位服务端组件的。假如在服务端我们有一个AccountService接口的实现类DefaultAccountService:

public interface AccountService {void registerMember(MemberDto dto);void sayHello(String name);}

我们想要在客户端调用其registerMember()方法,那么就应该这样封装MessageProtocol类:

interfaceName = “com.fh.common.service.AccountService”;methodName = “registerMember”;argList = Arrays.asList(new MemberDto());

然后将这个对象以ObjectMessagepayload的格式通过JMS发送给服务端,服务端要做的工作是:

由于Java的反射机制中调用Class#getDeclaredMethods()方法开销比较大,所以我们应该缓存得到的Method对象。思想是,当客户端第一次请求该方法时,先调用getDeclaredMethods()方法,将返回的结果保存到一个Map<String, List<Method> >数据结构中,即:

/*** K: 接口的全限定名* <p> V: 这个接口实现类的所有{@code Method}对象*/public static Map<String, List<Method>> methodCache = new HashMap<>();

然后,当下一次客户端要请求这个组件的方法时,直接从methodCache中查找而不是调用getDeclaredMethods()方法,这样能极大提高运行效率。 消息处理方法如下:

public Object onMessage(MessageProtocol protocol) {System.out.println(“message received”);// 取出请求信息// 接口名String interfaceName = protocol.getInterfaceName();// 参数列表List<Object> argList = protocol.getArgList();// 方法名String methodName = protocol.getMethodName();Object returnVal = null;try {Class clazz = Class.forName(interfaceName);Object service = BeanUtil.getBeanByInterface(clazz);// 反射调用returnVal = BeanUtil.invokeMethod(clazz, service, methodName, argList.toArray());} catch (// … …) {// … …}return returnVal;}

BeanUtil工具类的完整实现如下:

{private BeanUtil() {}/*** 通过反射调用业务逻辑方法* @param clazz* @param methodName* @param <T>*/public static <T> Object invokeMethod(Class<T> clazz, Object component, String methodName, Object[] args) throws NoSuchMethodException, NoSuchClassException, InvocationFailedException {Method method = getMethodInCache(clazz.getName(), methodName);Object returnValue = null;try {returnValue = method.invoke(component, args);} catch (IllegalAccessException e) {throw new InvocationFailedException(“invoke ” + methodName + ” failed”);} catch (InvocationTargetException e) {throw new InvocationFailedException(“invoke ” + methodName + ” failed”);}return returnValue;}/*** 通过接口Class对象,,从Spring容器中得到对应的实现组件*/public static <T> T getBeanByInterface(Class<T> clazz) {T result = null;Map<String, T> map = ContextHolder.ctx.getBeansOfType(clazz);// 返回第一个beanSet<Map.Entry<String, T>> set = map.entrySet();for (Map.Entry<String, T> entry : set) {result = entry.getValue();break;}cacheMethod(clazz);return result;}/*** 如果是第一次调用,缓存Class的Method对象* @param clazz* @param <T>*/(Class<T> clazz) {if (false == isAlreadyCached(clazz.getName())) {Method[] methods = clazz.getDeclaredMethods();ContextHolder.methodCache.put(clazz.getName(), Arrays.asList(methods));}}(String className) {return ContextHolder.methodCache.containsKey(className);}/*** 从Cache中得到Method对象*/private static Method getMethodInCache(String className, String methodName) throws NoSuchClassException, NoSuchMethodException {List<Method> methodList = ContextHolder.methodCache.get(className);if (null == methodList) {throw new NoSuchClassException(className);}Optional<Method> optMethod = methodList.stream().filter( (meth) -> {return meth.getName().equals(methodName);}).findAny();if (false == optMethod.isPresent()) {throw new NoSuchMethodException(methodName);}return optMethod.get();}}

至此,一个完整可用的分布式网站后端就能够运行起来了。当然这只是一个粗犷的实现,还有很多可以进行性能优化的地方。

不是每一次努力都有收获,但是,每一次收获都必须经过努力。

基于JMS消息中间件的分布式系统初探究(二)

相关文章:

你感兴趣的文章:

标签云: