SpringRMI

1.背景介绍

分布式系统:RPC与RMI

RPC:一个对远程节点上的过程的调用就像调用本地节点那样完成

RMI:与特定的Java RMI要进行区分,与RPC最大区别在于使用了面向对象的概念,那么在远程调用时可以把对象应用作为参数传递,它的实现最典型的就是Java RMI,但不限于Java RMI

交互协议

请求协议:可以用在不需要从远程操作中返回值或者客户端不需要得到远程操作执行确认的场景中,请求发送后客户端不需要等待应答消息而可以继续执行

请求-应答协议:这种方式使我们最常见的,服务器的应答消息可以看做是客户端请求消息的一个确认

请求-应答-确认应答协议

请求-应答协议有同步和异步之分,HTTP协议即为请求-应答协议

故障处理

将超时信息抛给客户端

超时可以重试、重试多少次后取消

客户端异步处理服务端响应

Java中的RMI

java内部语言进行的远程方法调用

无法像webservice一样跨语言调用

SpringRMI

RmiServiceExporter:服务端暴露其接口

把任何Spring管理的Bean输出成一个RMI服务。通过把Bean包装在一个适配器类中工作。适配器类被绑定到RMI注册表中,并且将请求代理给服务类。

RmiProxyFactoryBean:客户端通过通过JRMP访问服务

JRMP:java remote method protocol,Java特有的,基于流的协议。

2.知识剖析

RMI的实现

Remote Method Invocation

如何通信?

客户端-调用本地对象stub存根-远程stub-远程skeleton-service

Java1.2之后service直接和stub联系,不需要skeleton

客户端和远程服务器应该都有一个stub

数据传输问题

分布式系统对象不在一个内存空间,如何解决?

序列化(远程对象的引用传递)

条件:基本类型、实现Serializable接口(UnicastRemoteObject已经实现了该接口)

对于容器类,如果其中的对象是可以序列化的,那么该容器也是可以序列化的

可序列化的子类也是可以序列化的

远程对象的发现问题

IP地址相当于远程对象的引用、DNS相当于一个注册表

格式类似于rmi://host:port/name,host指明注册表运行的注解,port表明接收调用的端口,name是一个标识该对象的简单名称。

主机和端口都是可选的,如果省略主机,则默认运行在本地;如果端口也省略,则默认端口是1099(默认注册端口号);

springRMI

不用我们去实现remote等接口

配置文件简单明了

版本太老

怎么实现随机service访问

注意服务端口要手动指定不让它随机生成

不停的从初始化的配置文件中获得bean

这方法有点low

3.编码实战

<bean id="rmiClientSocketFactory" class="beanfactory.RMICustomClientSocketFactory">
  <property name="timeout" value="5000"/>
</bean>
<!-- 客户端 -->
<bean id="userRmiServiceA"  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
  <property name="serviceUrl" value="rmi://127.0.0.1:8080/studentService"/>
  <property name="serviceInterface" value="service.UserRmiService"/>
  <property name="refreshStubOnConnectFailure" value="true"/>
  <property name="lookupStubOnStartup" value="false"/>
  <!--<property name="registryClientSocketFactory" ref="rmiClientSocketFactory"/>-->
</bean>

<bean id="userRmiServiceB"  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
  <property name="serviceUrl" value="rmi://120.131.8.132:8082/studentService"/>
  <property name="serviceInterface" value="service.UserRmiService"/>
  <property name="refreshStubOnConnectFailure" value="true"/>
  <property name="lookupStubOnStartup" value="false"/>
  <!--<property name="registryClientSocketFactory" ref="rmiClientSocketFactory"/>-->
</bean>

<bean id="loadBalancedService" class="beanfactory.LoadBalancedFactoryBean">
  <property name="serviceInterface" value="service.UserRmiService"/>
  <property name="stateful" value="false"/>
  <property name="balanceStrategy">
    <bean class="beanfactory.RoundRobinStrategy"/>
  </property>
  <property name="serviceProxyList">
    <list>
      <ref bean="userRmiServiceA"/>
      <ref bean="userRmiServiceB"/>
    </list>
  </property>
</bean>
private static org.slf4j.Logger logger = LoggerFactory.getLogger(JnshuController.class);
    private static UserRmiService userRmiService;
    private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    private static int flagA=0 ;
    private static int flagB=0 ;

    @Scheduled(cron = "0 0/3 * * * ?")
    private static void print(){
        flagA=0;
        flagB=0;
        System.out.print("将A、B清0\n");
    }

//   private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//   private static UserRmiService userRmiService = (UserRmiService) context.getBean("loadBalancedService");


   private static UserRmiService load(){
       if (Math.random()<0.5 && flagA==0) {
           try {
               System.out.print("准备调用:");
               userRmiService = (UserRmiService) context.getBean("userRmiServiceA");
               System.out.print("调用成功8080端口"+userRmiService+"\n");
           }
           catch (Exception e ){
               flagA = 1;
               userRmiService = (UserRmiService) context.getBean("userRmiServiceB");
               System.out.print("调用失败尝试调用:"+userRmiService+"\n");
           }
       }
       else  if (flagB==0){
           try {
               System.out.print("准备调用:");
               userRmiService = (UserRmiService) context.getBean("userRmiServiceB");
               System.out.print("调用成功8082端口"+userRmiService+"\n");
           }
           catch (Exception e){
               flagB = 1;
               userRmiService = (UserRmiService) context.getBean("userRmiServiceA");
               System.out.print("调用失败尝试调用:"+userRmiService+"\n");
           }
       }
       else if(flagA==1 && flagB ==1) {
           System.out.print("两个端口都挂了,你在搞毛线\n");
       }
       return userRmiService;
   }

4.常见问题

如何捕获一个服务端已经挂掉的异常

如果用读取bean的方式很容易就捕获了那个异常

轮询机制暂时找不到方法

服务端挂掉之后如何重连

<property name="refreshStubOnConnectFailure" value="true"/>

<property name="lookupStubOnStartup" value="false"/>

为什么本地调试正常放在远程就不行了

Linux对localhost解析有问题

5.知识拓展

spring中定时器的使用

corn表达式

6.参考文献

https://blog.csdn.net/u014001866/article/details/50936246

https://blog.csdn.net/lmy86263/article/details/72594760

https://blog.csdn.net/lmy86263/article/details/51771845

7.更多讨论

鸣谢

感谢观看,如有出错,恳请指正


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值