6.RPC框架的简单实现(服务发布-rmi协议)

通过rmi协议发布一个Java服务,使用java.rim包下的Naming.bind()方法即可,但是jdk默认rmi协议的实现需要接口类继承java.rmi.Remote,接口方法抛出RemoteException,实现类需要继承UnicastRemoteObject类比较繁琐。spring框架也提供了一套rmi协议的实现,参考spring帮助文档中Exposing services using RMI部分,spring对rmi协议的支持,不需要我们的业务接口去继承任何父类,业务代码更加简洁,RmiProtocol类的实现使用spring框架的相关工具类。

在第一篇中我们在maven配置文件中只添加了netty,jboss-marshalling,javassist三个依赖,现在需要增加上spring的依赖:

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>4.1.6.RELEASEversion>
dependency>

在第五篇中有Protocol接口的子类关系图,抽象类AbstractProxyProtocol实现了Protocol接口,RmiProtocol类和LdubboProtocol类又分别继承了该抽象类,这个结构也是跟dubbo源码中的Protocol接口类似的。好的,先看一下抽象类AbstractProxyProtocol的实现,代码如下:

package com.lipenglong.ldubbo.rpc;

import com.lipenglong.ldubbo.config.RegistryConfig;

/**
 * AbstractProxyProtocol
 * 

* Created by lipenglong on 2017/7/27. */ public abstract class AbstractProxyProtocol implements Protocol { @Override public void export(Class interfaceClass, Object ref) { doExport(interfaceClass, ref); } @Override public T refer(Class interfaceClass, RegistryConfig registryConfig) { return doRefer(interfaceClass, registryConfig); } protected abstract void doExport(Class interfaceClass, Object ref); protected abstract T doRefer(Class interfaceClass, RegistryConfig registryConfig); }

在ServiceConfig类的export()方法中,调用protocol.export(interfaceClass, ref);方法,这里调用的就是AbstractProxyProtocol抽象类的export()方法,看上面的类代码,它又调用了抽象方法doExport(interfaceClass, ref);这个抽象方法就是具体的协议类需要实现的方法。

接下来看RmiProtocol类的实现代码:

package com.lipenglong.ldubbo.rpc.protocol;

import com.lipenglong.ldubbo.config.RegistryConfig;
import com.lipenglong.ldubbo.rpc.AbstractProxyProtocol;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import org.springframework.remoting.rmi.RmiServiceExporter;

import java.rmi.RemoteException;

/**
 * rmi协议通信类
 * 

* Created by lipenglong on 2017/7/25. */ public class RmiProtocol extends AbstractProxyProtocol { private static final Integer DEFAULT_PORT = 1099; @Override protected void doExport(Class interfaceClass, Object ref) { RmiServiceExporter rmiServiceExporter = new RmiServiceExporter(); rmiServiceExporter.setRegistryPort(DEFAULT_PORT); rmiServiceExporter.setServiceName(interfaceClass.getName()); rmiServiceExporter.setServiceInterface(interfaceClass); rmiServiceExporter.setService(ref); try { rmiServiceExporter.afterPropertiesSet(); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected T doRefer(Class interfaceClass, RegistryConfig registryConfig) { return null; } }

doExport方法通过spring的RmiServiceExporter类实现服务的发布,首先创建一个RmiServiceExporter对象,设置端口、服务名称、服务接口、实现类,然后通过afterPropertiesSet()方法暴露服务,这个方法是不是有点熟悉?spring的RmiServiceExporter对象也是实现了InitializingBean接口的,当bean初始化后,执行该方法。doExport方法我们是用编码的方式自己调用的,spring帮助文档上面的例子是xml配置文件的方式,写在配置文件中时afterPropertiesSet()方法会自动调用的。
所以spring框架的rmi和dubbo框架发布rpc服务方式的入口是一样的,如果大家看一下dubbo框架的RmiProtocol类的源码,和上面我们自己写的代码是一样的。(doRefer引用服务的方法暂时先不实现)


到目前为止,我们先实现了一个rmi协议的服务发布,那么先来测试一下这个rpc框架的server端能否正常运行起来

把ldubbo项目运行maven的install命令打好jar包,看一下ldubbo项目的结构:(common和remote包先忽略)
6.RPC框架的简单实现(服务发布-rmi协议)_第1张图片

新建一个接口ldubbo-api项目,创建一个测试用的接口类UserService:

package com.lipenglong.ldubbo.api.service;

import com.lipenglong.ldubbo.api.domain.User;

import java.util.List;

/**
 * 用户service接口类
 * 

* Created by lipenglong on 2017/7/24. */ public interface UserService { List queryUserList(); User getUserById(Long id); }

定义一个User对象:

package com.lipenglong.ldubbo.api.domain;

import java.io.Serializable;

/**
 * User
 * 

* Created by lipenglong on 2017/7/24. */ public class User implements Serializable { private static final long serialVersionUID = 2775326738215482673L; private Long id; private String name; private String password; private Integer role; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getRole() { return role; } public void setRole(Integer role) { this.role = role; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", password='" + password + '\'' + ", role=" + role + '}'; } }

接口项目定义完了,然后创建一个新的的provider项目,在pom中把ldubbo和ldubbo-api两个项目引用进来,再增加下面两个slf4j的日志工具包

<dependency>
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-apiartifactId>
    <version>1.7.9version>
dependency>
<dependency>
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-log4j12artifactId>
    <version>1.7.9version>
dependency>

provider项目需要写一个UserService接口的实现,代码如下:

package com.lipenglong.ldubbo.provider.service;

import com.lipenglong.ldubbo.api.domain.User;
import com.lipenglong.ldubbo.api.service.UserService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * UserServiceImpl
 * 

* Created by lipenglong on 2017/7/24. */ @Service(value = "userService") public class UserServiceImpl implements UserService { @Override public List queryUserList() { List result = new ArrayList(); User user = new User(); user.setId(1L); user.setName("user1"); user.setPassword("****"); user.setRole(1); result.add(user); return result; } public User getUserById(Long id) { User user = new User(); user.setId(id); user.setName("user" + id); user.setPassword("****"); user.setRole(1); return user; } }

实现类有了,定义spring的配置文件applicationContext.xml:


<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    
    <context:annotation-config/>
    <context:component-scan base-package="com.lipenglong.ldubbo.provider.service"/>

    <import resource="applicationContext-ldubbo.xml"/>
beans>

applicationContext-ldubbo.xml文件:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:ldubbo="http://www.lipenglong.com/schema/ldubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.lipenglong.com/schema/ldubbo
       http://www.lipenglong.com/schema/ldubbo/ldubbo.xsd" default-lazy-init="true">

    <ldubbo:protocol name="rmi"/>
    <ldubbo:service interface="com.lipenglong.ldubbo.api.service.UserService" ref="userService"/>
beans>

实现类,spring配置文件都定义完了,最后需要一个启动类,创建一个Provider类:

package com.lipenglong.ldubbo.provider;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * ldubbo服务提供者启动类
 * 

* Created by lipenglong on 2017/7/25. */ public class Provider { private static Log logger = LogFactory.getLog(Provider.class); public static void main(String[] args) throws Exception { new ClassPathXmlApplicationContext("applicationContext.xml"); logger.info(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " ldubbo service server started!"); } }

在provider项目下还需定义一个log4j.xml的配置文件,如下:




<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d [%p] %c.%M(%L) | %m%n"/>
        layout>
    appender>

    <root>
        <level value="INFO"/>
        <appender-ref ref="CONSOLE"/>
    root>

log4j:configuration>

运行Provider类,输出:
6.RPC框架的简单实现(服务发布-rmi协议)_第2张图片

ldubbo框架可以发布rmi协议的服务了!

下一篇先不写LdubboProtocol如何使用netty做服务发布,先写rmi的服务引用

你可能感兴趣的:(RPC)