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

来源:互联网 发布:java多线程高并发购票 编辑:程序博客网 时间:2024/06/11 01:47

通过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.springframework</groupId>    <artifactId>spring-context</artifactId>    <version>4.1.6.RELEASE</version></dependency>

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

package com.lipenglong.ldubbo.rpc;import com.lipenglong.ldubbo.config.RegistryConfig;/** * AbstractProxyProtocol * <p/> * 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> T refer(Class<?> interfaceClass, RegistryConfig registryConfig) {        return doRefer(interfaceClass, registryConfig);    }    protected abstract void doExport(Class interfaceClass, Object ref);    protected abstract <T> 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协议通信类 * <p/> * 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> 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包先忽略)
这里写图片描述

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

package com.lipenglong.ldubbo.api.service;import com.lipenglong.ldubbo.api.domain.User;import java.util.List;/** * 用户service接口类 * <p/> * Created by lipenglong on 2017/7/24. */public interface UserService {    List<User> queryUserList();    User getUserById(Long id);}

定义一个User对象:

package com.lipenglong.ldubbo.api.domain;import java.io.Serializable;/** * User * <p/> * 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.slf4j</groupId>    <artifactId>slf4j-api</artifactId>    <version>1.7.9</version></dependency><dependency>    <groupId>org.slf4j</groupId>    <artifactId>slf4j-log4j12</artifactId>    <version>1.7.9</version></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 * <p/> * Created by lipenglong on 2017/7/24. */@Service(value = "userService")public class UserServiceImpl implements UserService {    @Override    public List<User> queryUserList() {        List<User> result = new ArrayList<User>();        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:

<?xml version="1.0" encoding="UTF-8"?><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文件:

<?xml version="1.0" encoding="UTF-8"?><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服务提供者启动类 * <p/> * 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的配置文件,如下:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"><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类,输出:
这里写图片描述

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

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

原创粉丝点击