An introduction to the

In Java development, we can use a variety of remote invocation techniques, such as:

  • Remote Method Invocation (RMI)
  • Hessian and Burlap of Caucho
  • Spring’s HTTP-based remote services
  • SOAP and RESTful Web Services

Note: about the Web Service can reference before I wrote articles on its own (PS: www.zifangsky.cn/webservice). In this article I focus on the specific code implementation of the previous methods

(1) Remote call and local call:

A remote call is a session between a client application and a server. On the client side, some of the functionality it needs is not within the scope of the application’s implementation, so the application seeks help from other systems that can provide these capabilities. Similarly, remote applications expose these capabilities to other systems by publishing services

I) Similarities between the two:

On the surface, an RPC call (remote procedure call) is similar to calling a method on a local object. In contrast to local method calls, both are synchronous operations that block the execution of the calling code until the called procedure has finished executing

Ii) Differences between the two:

A local method call is an exchange of execution flows between two blocks of code in the same application. RPC calls execute the flow from one application to another, which could theoretically be deployed on any other remote machine across the network

(2) Spring’s support for multiple remote calls:

Spring supports a number of different RPC models, including RMI, Hessian/Burlap, and Spring’s own Http Invoker. Here I will briefly introduce the similarities and differences between them:

  • RMI: Used when network constraints are not considered (PS: because RMI uses arbitrary ports to interact, it is sometimes impossible to traverse firewalls)
  • Hessian/Burlap: Access/publish Java-based services over HTTP when considering network constraints. Hessian is a binary based remote call technology. Burlap is an XML-based remote call technology
  • Spring's HttpInvokerHessian/Burlap uses a proprietary object serialization mechanism, whereas Spring’s Http Invoker uses Java’s serialization mechanism

However, no matter which remote invocation model we choose, we will find that Spring provides consistent support. This means that once you understand how to configure and use one of these models in Spring. Then when we want to use another model, it becomes very easy

In all models, the service is configured into our application as spring-managed beans. This is done through a proxy factory bean that can assemble remote services into properties of other beans like local objects. The client makes calls to the proxy as if the proxy provided these services. The proxy communicates with the remote service on behalf of the client, handling the details of the connection and making calls to the remote service. Finally, the proxy returns the result of the remote service execution, at which point the entire invocation process is complete

Whether we develop code that uses remote services, implements those services, or both. In Spring, using remote services is purely a configuration issue. We don’t need to write any Java code to support remote calls. Nor do our service beans need to care if they participate in an RPC(PS: any bean passed to or returned from a remote call may need to implement the Java.io.serializable interface)

Two Remote Method Invocation (RMI)

(1) Define a service exposed to other system calls:

I) Its interface:

package cn.zifangsky.rmi.service;

import java.util.Date;

public interface TestRMIService {

    /** * Time format conversion service *@paramThe date time *@returnTime String */ of String type
    public String formatDateService(Date date);
}Copy the code

Ii) Its implementation class:

package cn.zifangsky.rmi.service.impl;

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

import org.springframework.stereotype.Component;

import cn.zifangsky.rmi.service.TestRMIService;

@Component("testRMIServiceImpl")
public class TestRMIServiceImpl implements TestRMIService {

    @Override
    public String formatDateService(Date date) {
        Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        if(date ! =null) {return format.format(date);
        }else{
            return ""; }}}Copy the code

As you can see from the code above, these are simple POJOs and do not rely on other services

(2) Create a new context_rmi. XML file to export the methods defined above as remote services:

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

    <context:component-scan base-package="cn.zifangsky.rmi.service.impl" />

    <bean id="testRMIService" class="org.springframework.remoting.rmi.RmiServiceExporter">
        <! -- RMI service name -->
        <property name="serviceName" value="testRMIService"/>
        <! -- RMI Concrete service Implementation Class -->
        <property name="service" ref="testRMIServiceImpl" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.rmi.service.TestRMIService" />
        <! -- Register port -->
        <property name="registryPort" value="1099" />
    </bean>

</beans>Copy the code

As you can see, by the above XML configuration is deduced a remote service, the address is: rmi: / / 127.0.0.1:1099 / testRMIService

Note: The value of the above service “testRMIServiceImpl” is an instance of the above testRMIServiceImpl class, generated and injected here via the @Component annotation

(3) Export the Testrvice interface and other classes into JAR packages for the client to use:

Once a service is defined above, if we want to use the service on the client side (another Web project), we must know the class of the remote method, the method name, and so on to be able to call it properly. Therefore, the next step is to package these common classes as JARS into the client’s lib directory

This step can be done manually by selecting the exported class, or it can be done automatically using an Ant script. The Ant script I used here is as follows:

Build_service. XML:

<?xml version="1.0" encoding="UTF-8"? >
<project name="common_build" default="build_service" basedir=".">

    <property name="targetdir" value="target"/>
    <property name="classbase" value="WebContent/WEB-INF/classes/"/>
    <property name="xpf.copy.info" value="true"/>
    <property name="model.name" value="test"/>
    <property name="env.name" value="dev"/>

    <target name="build_service">
        <jar destfile="${basedir}/target/zifangsky_${model.name}_RMIService_api.jar">
            <fileset dir="${classbase}">
                <include name="cn/zifangsky/**/model/*.class" />
                <include name="cn/zifangsky/**/model/**/*.class" />
                <include name="cn/zifangsky/**/service/*.class" />
                <include name="cn/zifangsky/**/common/*.class" />
                <include name="cn/zifangsky/**/common/**/*.class" />
            </fileset>
        </jar>
    </target>

</project>Copy the code

Finally, place the generated ZifangSky_test_rmiservice_api.jar in the lib directory of the client

(4) Client XML configuration:

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

    <context:component-scan base-package="cn.zifangsky.rmi.service" />

    <bean id="testRMIServiceClient" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
        <! -- Service address -->
        <property name="serviceUrl" value="The rmi: / / 127.0.0.1:1099 / testRMIService" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.rmi.service.TestRMIService" />
    </bean>

</beans>Copy the code

The code here is very simple, read the comments to understand

(5) Test:

package cn.zifangsky.test.rmi;

import java.util.Date;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.zifangsky.rmi.service.TestRMIService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml"."classpath:/context/context_rmi_client.xml"})
public class TestRMI {

    @Resource(name="testRMIServiceClient")
    private TestRMIService testRMIService;

    @Test
    public void testFormatDate(a){
        Date date = new Date();
        System.out.println("Time after formatting is:"+ testRMIService.formatDateService(date)); }}Copy the code

The final output is as follows:

The time after formatting is:2017- 01- 08 15:55:49Copy the code

From the console output, the client code does call the remote service to convert the time format. This concludes the configuration and use of RMI, and I’ll briefly cover several other remote invocation configurations and uses that are similar to RMI

Use Hessian to publish remote services

Hessian and Burlap are two lightweight HTTP-based remote service solutions from Caucho Technology. They both aim to simplify Web services by making apis and communication protocols as simple as possible.

In terms of how you choose to publish a service using Hessian or Burlap, for the most part, they are the same. The only difference is that Hessian’s message is binary, while Burlap’s message is XML. Because Hessian’s messages are binary, it has a bandwidth advantage. But if we are more interested in readability (for debugging purposes, for example) or if our application needs to interact with a language that does not have Hessian implementations, then Burlap’s XML messages will be a better choice

(1) Download hessian-related JAR packages:

Download it at hessian.caucho.com

(2) Define a test service:

I) its interface testhessianservice.java:

package cn.zifangsky.hessian.service;

public interface TestHessianService {

    /** * test interface *@return* /
    public String sayHello(a);
}Copy the code

Ii) the implementation class TestHessianServiceImpl. Java:

package cn.zifangsky.hessian.service.impl;

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

import org.springframework.stereotype.Component;

import cn.zifangsky.hessian.service.TestHessianService;

@Component("testHessianServiceImpl")
public class TestHessianServiceImpl implements TestHessianService{

    @Override
    public String sayHello(a) {
        Date date = new Date();
        Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        return "Hello,this's TestHessianService ---Time: "+ format.format(date); }}Copy the code

Context_hessian.xml: context_hessian.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:jee="http://www.springframework.org/schema/jee"
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="cn.zifangsky.hessian.service.impl" />

    <bean id="/testHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter">
        <! -- Hessian service implementation class -->
        <property name="service" ref="testHessianServiceImpl" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.hessian.service.TestHessianService" />
    </bean>

</beans>Copy the code

Configure web.xml to publish all Hessian services to a single directory:

    <servlet>
        <servlet-name>hessian</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:context/context_hessian.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>hessian</servlet-name>
        <url-pattern>/hessian/*</url-pattern>
    </servlet-mapping>Copy the code

Therefore, the above test the service address is: http://localhost:9180/RMIServerDemo/hessian/testHessianService

(4) Export the service interface into A JAR package for the client to use:

The procedure for RMI is the same as that for RMI

(5) XML configuration of the client:

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

    <bean id="testHessianServiceClient" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
        <! -- Service address -->
        <property name="serviceUrl" value="http://localhost:9180/RMIServerDemo/hessian/testHessianService" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.hessian.service.TestHessianService" />
    </bean>

</beans>Copy the code

(6) Test:

package cn.zifangsky.test.hessian;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.zifangsky.hessian.service.TestHessianService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml"."classpath:/context/context_hessian_client.xml"})
public class TestHessian {

    @Resource(name="testHessianServiceClient")
    TestHessianService testHessianService;

    @Test
    public void sayHello(a){ System.out.println(testHessianService.sayHello()); }}Copy the code

The final output is as follows:

Hello,this's TestHessianService ---Time: 2017-01-08 16:48:25Copy the code

Publish remote services using Burlap

(1) Download Burlap related JAR package:

Download address: repo1.maven.org/maven2/edu/…

(2) Define a test service:

Interface TestBurlapService. Java:

package cn.zifangsky.burlap.service;

public interface TestBurlapService {
    /** * test interface *@return* /
    public String sayHello(a);
}Copy the code

Its implementation class TestBurlapServiceImpl. Java:

package cn.zifangsky.burlap.service.impl;

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

import org.springframework.stereotype.Component;

import cn.zifangsky.burlap.service.TestBurlapService;

@Component("testBurlapServiceImpl")
public class TestBurlapServiceImpl implements TestBurlapService{

    @Override
    public String sayHello(a) {
        Date date = new Date();
        Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        return "Hello,this's TestBurlapService ---Time: "+ format.format(date); }}Copy the code

(3) Export to service context_burlap.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:jee="http://www.springframework.org/schema/jee"
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="cn.zifangsky.burlap.service.impl" />

    <bean id="/testBurlapService" class="org.springframework.remoting.caucho.BurlapServiceExporter">
        <! -- Burlap Concrete Service Implementation Class -->
        <property name="service" ref="testBurlapServiceImpl" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.burlap.service.TestBurlapService" />
    </bean>

</beans>Copy the code

Corresponding web.xml configuration:

    <servlet>
        <servlet-name>burlap</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:context/context_burlap.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>burlap</servlet-name>
        <url-pattern>/burlap/*</url-pattern>
    </servlet-mapping>Copy the code

Therefore, the above test the service address is: http://localhost:9180/RMIServerDemo/burlap/testBurlapService

(4) Export the service interface into A JAR package for the client to use:

The procedure for RMI is the same as that for RMI

(5) XML configuration of the client:

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

    <bean id="testBurlapServiceClient" class="org.springframework.remoting.caucho.BurlapProxyFactoryBean">
        <! -- Service address -->
        <property name="serviceUrl" value="http://localhost:9180/RMIServerDemo/burlap/testBurlapService" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.burlap.service.TestBurlapService" />
    </bean>

</beans>Copy the code

(6) Test:

package cn.zifangsky.test.burlap;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.zifangsky.burlap.service.TestBurlapService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml"."classpath:/context/context_burlap_client.xml"})
public class TestBurlap {

    @Resource(name="testBurlapServiceClient")
    private TestBurlapService testBurlapService;

    @Test
    public void sayHello(a){ System.out.println(testBurlapService.sayHello()); }}Copy the code

The final output is as follows:

Hello,this's TestBurlapService ---Time: 2017-01-08 17:03:47Copy the code

Publish remote services using Spring’s Http Invoker

(1) Define a test service:

Interface TestHttpInvokerService. Java:

package cn.zifangsky.httpinvoker.service;

public interface TestHttpInvokerService {
    /** * test interface *@return* /
    public String sayHello(a);
}Copy the code

Its implementation class TestHttpInvokerServiceImpl. Java:

package cn.zifangsky.httpinvoker.service.impl;

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

import org.springframework.stereotype.Component;

import cn.zifangsky.httpinvoker.service.TestHttpInvokerService;

@Component("testHttpInvokerServiceImpl")
public class TestHttpInvokerServiceImpl implements TestHttpInvokerService {

    @Override
    public String sayHello(a) {
        Date date = new Date();
        Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        return "Hello,this's TestHttpInvokerService ---Time: "+ format.format(date); }}Copy the code

Context_invoker.xml: context_invoker.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:jee="http://www.springframework.org/schema/jee"
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="cn.zifangsky.httpinvoker.service.impl" />

    <bean id="/testHttpInvokerService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <! Http Invoker service implementation class -->
        <property name="service" ref="testHttpInvokerServiceImpl" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.httpinvoker.service.TestHttpInvokerService" />
    </bean>

</beans>Copy the code

Corresponding web.xml configuration:

    <servlet>
        <servlet-name>invoker</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:context/context_invoker.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>invoker</servlet-name>
        <url-pattern>/invoker/*</url-pattern>
    </servlet-mapping>Copy the code

Therefore, the above test the service address is: http://localhost:9180/RMIServerDemo/invoker/testHttpInvokerService

(3) Export the service interface into A JAR package for the client to use:

The procedure for RMI is the same as that for RMI

(4) Client XML configuration:

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

    <bean id="testHttpInvokerServiceClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
        <! -- Service address -->
        <property name="serviceUrl" value="http://localhost:9180/RMIServerDemo/invoker/testHttpInvokerService" />
        <! -- Service invocation interface -->
        <property name="serviceInterface" value="cn.zifangsky.httpinvoker.service.TestHttpInvokerService" />
    </bean>

</beans>Copy the code

(5) Test:

package cn.zifangsky.test.httpinvoker;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.zifangsky.httpinvoker.service.TestHttpInvokerService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml"."classpath:/context/context_httpinvoker_client.xml"})
public class TestHttpInvoker {

    @Resource(name="testHttpInvokerServiceClient")
    private TestHttpInvokerService testHttpInvokerService;

    @Test
    public void sayHello(a){ System.out.println(testHttpInvokerService.sayHello()); }}Copy the code

The final output is as follows:

Hello,this's TestHttpInvokerService ---Time: 2017-01-08 17:09:51Copy the code

This concludes my introduction to the four implementations of Spring-based remote procedure calls (RPCS). As you can see from the code above, the publishing and implementation steps of these services are very similar due to Spring’s encapsulation, and what we actually need to do is very simple, and we largely just need to focus on our specific business logic