发表于: 2019-12-15 23:19:55

1 1136


今天完成的事:
1、实现spring RMI。
查看了很多实例,参考如下网址,借助第一个网址的项目框架,相关配置是第二个网址的。
spring RMI简化了RMI的实现,服务接口不需要继承Remote(仅仅是普通接口),服务实现类不用继承UnicastRemoteObject,不用自己注册RMI远程服务实现类,
服务端要做的仅仅是写好需要提供远程服务的实现类,然后将其交给RmiServiceExporter类,RmiServiceExporter类会将实现类发布成RMI服务。
客户端也简单,只需要使用RmiProxyFactoryBean从服务端的url中获取服务对象,然后从spring容器中获取RmiProxyFactoryBean封装的id即可。
具体代码:
(1)为方便自测,将服务端和客户端整合到一个webapp项目中,这里不需要将接口、实体类打成jar包。
pom文件中添加spring的常用依赖。
下图左侧为实际代码结构,左侧为参考案例代码结构,标注了哪些是客户端、哪些是服务端。

实体类:
package com.jnshu.rmi.service;
import java.io.Serializable;
import java.util.Date;
//水果,需要序列化
public class Fruit implements Serializable {
    // 暂时没用
//    private static final long serialVersionUID = 1883838732853579826L;
    Integer id;//编号
    String name;//名称
    Double weight;//重量
    String color;//颜色
    Date pickDay;//采摘日期
    public Fruit() {
        // 暂时没用
//        super();
    }
    public Fruit(Integer id, String name, Double weight, String color, Date pickDay) {
        super();
        this.id = id;
        this.name = name;
        this.weight = weight;
        this.color = color;
        this.pickDay = pickDay;
    }
//  get、set省略。。
    @Override
    public String toString() {
        return "Fruit [id=" + id + ", name=" + name + ", weight=" + weight + ", color=" + color + ", pickDay=" + pickDay + "]";
    }
}
接口:
package com.jnshu.rmi.service;
// 人接口
public interface IPerson {
    public String eat(String fruitName);
    public String eat(Fruit fruit);
}
接口实现类:
package com.jnshu.rmi.service;
// 人实现类
public class Person implements IPerson {
    @Override
    public String eat(String fruitName){
        System.out.println("服务开始------");
        System.out.println("我要吃:"+ fruitName );
        System.out.println("服务结束------");
        return "请喂我吃:" + fruitName;
    }
    @Override
    public String eat(Fruit fruit){
        System.out.println("服务2开始------");
        System.out.println("我想想:"+fruit);
        System.out.println("服务2结束------");
        return "请给我点时间:"+ fruit;
    }
}
服务端配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 人接口 -->
    <bean id="person" class="com.jnshu.rmi.service.Person"/>
    <bean id="personService" class="org.springframework.remoting.rmi.RmiServiceExporter">
        <property name="serviceName" value="helloRMI" />
        <property name="service" ref="person" />
        <property name="serviceInterface" value="com.jnshu.rmi.service.IPerson" />
        <property name="registryPort" value="9999"></property>
    </bean>
</beans>
web.xml:
这里是通过web.xml配置文件读取服务端配置了,需要添加tomcat运行,我在这里没有添加,使用的新建一个类,用main方法运行服务端,在下面的建立项目模块里,添加的是tomcat。
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>Archetype Created Web Application</display-name>
  <!-- springmvc核心,前端控制器:分发servlet -->
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 指定spring mvc的配置文件位置 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:remote-service.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--静态资源访问配置-->
  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
新建测试类,运行服务端:
package com.jnshu.rmi.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RMIServiceTest {
    public static void main(String[] args) {
        new ClassPathXmlApplicationContext("remote-service.xml");
    }
}
客户端测试:
读取客户端配置文件,获取其bean,调用服务端方法。
package com.jnshu.rmi.client;
import java.util.Date;
import com.jnshu.rmi.service.Fruit;
import com.jnshu.rmi.service.IPerson;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
// 客户端调用类
public class Client  {
    //读取配置文件
    static ApplicationContext context = new ClassPathXmlApplicationContext("remote-client-local.xml");
    public static void main(String[] args) {
        eatFruit1();
        eatFruit2();
    }
    public static void eatFruit1()  {
        // 获取bean
        IPerson service = (IPerson) context.getBean("person");
        String ii = service.eat("苹果");
        System.out.println(ii);
    }
    public static void eatFruit2()  {
        IPerson service = (IPerson) context.getBean("person");
        Fruit fruit = new Fruit(1,"西瓜",2.2d,"green",new Date());
        String i = service.eat(fruit);
        System.out.println(i);
    }
}
客户端配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 人接口 -->
    <bean id="person"
          class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
        <property name="serviceUrl" value="rmi://127.0.0.1:9999/helloRMI" />
        <property name="serviceInterface" value="com.jnshu.rmi.service.IPerson" />
    </bean>
</beans>
先运行服务端,成功,再运行客户端,成功输出信息。

(2)创建maven quickstart,纯java项目,然后创建两个子模块webapp项目,其父工程都是纯java项目,如下图,依次创建。

按照例子的颜色标注,将几个文件分开,红色为服务端,绿加紫为客户端。这里需要注意的是如果将服务端打成jar包,那么客户端就不需要添加紫色部分了。

将服务端的pom中,war处修改为jar,点击maven的install插件,即可在本地仓库中查找到对应的jar包,可以直接添加依赖到客户端的pom文件中。
这里添加依赖,导入的就是实体类、服务端接口。
这里也可以不导入jar包,相应的客户端就需要添加服务端的实体类、接口,而且类路径需要保持一致。

client客户端依赖添加。

下图左侧为客户端模块目录,右侧为服务端。
两者相同的依赖可以添加到父工程的pom文件中,这里将spring的一些常用依赖添加到父工程pom文件。客户端添加服务端jar包依赖,服务端不添加依赖。

项目代码与上个例子都是一样的,只是对应的类路径修改了,服务端添加了tomcat运行,这里就不放代码了。
2、实现两个service随机访问,两个web随机访问service。
在客户端的模块中,创建一个类,用于随机选择service。
package client;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import service.IPerson;
import java.util.Random;
/**
* 随机选择service
*/
@Component
public class RandomService {
    private static final Logger logger = Logger.getLogger("RandomService");
    @Qualifier("person1")
    @Autowired
    IPerson service1;
    @Qualifier("person2")
    @Autowired
    IPerson service2;
    public IPerson chooseService(){
        IPerson service = (IPerson)choose(service1,service2);
        return service;
    }
    private Object choose(Object m,Object n){
        int choose = new Random().nextInt(2);
        if(0 == choose){
            try{
                logger.info("服务一。。。");
                return m;
            }catch (Exception e1){
                try{
                    logger.info("服务二。。。");
                    return n;
                }catch (Exception e2){
                    logger.error("服务选择出现异常1。。。");
                    throw new RuntimeException("服务选择异常1");
                }
            }
        }
        if(1 == choose){
            try{
                logger.info("服务二。。。");
                return n;
            }catch (Exception e1){
                try{
                    logger.info("服务一。。。");
                    return m;
                }catch (Exception e2){
                    logger.error("服务选择出现异常2。。。");
                    throw new RuntimeException("服务选择异常2");
                }
            }
        }
        logger.error("未知异常。。。");
        throw new RuntimeException("未知异常");
    }
}
将客户端调用服务端方法改为控制层,用于打war包运行。
package client;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import service.Fruit;
import service.IPerson;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 客户端调用类
*/
@Controller
public class Client {
    private static final Logger logger = Logger.getLogger("Client");
    @Autowired
    RandomService randomService;
    @RequestMapping(value = "/client",method = RequestMethod.GET )
    @ResponseBody
    public Map eatFruit1()  {
        String ii = randomService.chooseService().eat("苹果");
        Map<String,String> map = new HashMap<>();
        map.put("水果",ii);
        logger.info("水果"+ii);
        return map;
    }
    @RequestMapping(value = "/client2",method = RequestMethod.GET )
    @ResponseBody
    public Map eatFruit2()  {
        Fruit fruit = new Fruit(1,"西瓜",2.2d,"green",new Date());
        String i = randomService.chooseService().eat(fruit);
        Map<String,String> map = new HashMap<>();
        map.put("水果品种",i);
        logger.info("水果品种"+i);
        return map;
    }
}
服务端添加main方法读取配置文件,用于打jar包运行,并在客户端添加依赖,导入实体类、服务接口。
package service;
import org.apache.log4j.Logger;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试服务端
*/
public class ServiceTest {
    private static final Logger logger = Logger.getLogger("ServiceTest");
    public static void main(String[] args) {
        new ClassPathXmlApplicationContext("remote-service.xml");
        logger.info("服务连接成功------");
    }
}
配置文件的修改:
客户端:
<!--开启注解的扫描-->
<context:component-scan base-package="client" />
<context:annotation-config />
<mvc:annotation-driven />
<!-- 1-接口 -->
<bean id="person1"
      class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://127.0.0.1:9999/helloRMI1" />
    <property name="serviceInterface" value="service.IPerson" />
</bean>
<!-- 2-接口 -->
<bean id="person2"
      class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://127.0.0.1:8888/helloRMI2" />
    <property name="serviceInterface" value="service.IPerson" />
</bean>
服务端:
<!-- 人接口 -->
<bean id="person" class="service.Person"/>
<!-- 1-接口 -->
<bean id="personService1" class="org.springframework.remoting.rmi.RmiServiceExporter">
    <property name="serviceName" value="helloRMI1" />
    <property name="service" ref="person" />
    <property name="serviceInterface" value="service.IPerson" />
    <property name="registryPort" value="9999"></property>
</bean>
<!-- 2-接口 -->
<bean id="personService2" class="org.springframework.remoting.rmi.RmiServiceExporter">
    <property name="serviceName" value="helloRMI2" />
    <property name="service" ref="person" />
    <property name="serviceInterface" value="service.IPerson" />
    <property name="registryPort" value="8888"></property>
</bean>
将服务端jar包运行,cmd到jar包目录,“java -jar xxx.jar”运行。将客户端war包放入两个tomcat的webapp中,启动tomcat、nginx。运行成功。
我是通过日志查看负载均衡以及service切换的。结果如下。

明天计划的事:
1、学习shiro框架。
2、尝试简单案例。
遇到的问题:
1、将服务端打jar包。
因为有相应的依赖,直接使用idea的打包命令不会添加其依赖,所以查看后,得知可以使用maven的一个插件进行打包,maven-shade-plugin。它能够将项目依赖的所有jar包都打成单一的jar包。
代码如下,在service模块pom中添加,但是插件好是很好,可以没法使用,查看别人日报代码没什么问题,百度搜索也不知道。。
       <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                            <resource>META-INF/spring.handlers</resource>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                            <resource>META-INF/spring.schemas</resource>
                        </transformer>
                            <transformer implementation = "org.apache.maven.plugins.shade.resource.MainifestResourceTransformer">
                                <mainClass>service.ServiceTest</mainClass>
                            </transformer>
                        </transformers>
                        <filters>
                            <filter>
                                <artifact>*:*</artifact>
                                <excludes>
                                    <exclude>META-INF/*.SF</exclude>
                                    <exclude>META-INF/*.DSA</exclude>
                                    <exclude>META-INF/*.RSA</exclude>
                                </excludes>
                            </filter>
                        </filters>
                    </configuration>
                </execution>
            </executions>
        </plugin>
</plugins>
报错信息:
Failed to execute goal org.apache.maven.plugins:maven-shade-plugin:2.4.1:shade (default) on project service: Unable to parse configuration of mojo org.apache.maven.plugins:maven-shade-plugin:2.4.1:shade for parameter transformers: Cannot load implementation hint 'org.apache.maven.plugins.shade.resource.MainifestResourceTransformer' -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
插件问题没有解决,询问师兄后,使用idea的打包方式将服务端以及其依赖打jar包成功,需要将所有依赖都添加到运行目录,才能运行服务端jar包。。

2、客户端打war包。
没使用插件之前是将服务端、客户端都打的war包,使用两个tomcat运行的,因为还要复制tomcat,所以将服务端用jar包运行。
但是服务端jar包用上述方法完成后,使用idea的打包插件无法打包客户端war包。。报错说是没有找到服务端的依赖,删除重新导入也不行,服务端重新打包也不行,但是仓库明明有jar包的。。
后来查看到一个说是多模块的打包问题,如下,说是模块依赖本项目模块,依赖的模块无法打包,被依赖的模块可以正常打包,解决办法是将父工程先install安装下,再打包子模块。
于是将父工程install,而后再打包客户端,结果就成功了。。。maven还是不熟练。。

收获:
1、实现spring RMI,单个项目,以及单个项目两个子模块,都成功了。
参考如下网址,借助第一个网址的项目框架,相关配置是第二个网址的。
2、实现两个service随机访问,两个web随机访问service。

返回列表 返回列表
评论

    分享到