您的位置:首页 > 其它

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

2017-09-12 16:22 549 查看
通过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的服务引用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  rpc dubbo