发表于: 2017-07-05 23:06:05

1 1042


今日完成:

使用nginx随机访问两台web,两台web随机访问两个service


明天计划:

使用Tuscany完成WEB端和Service端的分离


收获:

重写了好几遍项目,对RMI分布式有了更多认识。

什么是RMI?

RMI(Remote Method Invocation,远程方法调用),java分布式应用程序的API。

在java中只要一个类extends了java.rmi.Remote接口即可成为存在于服务器的远程对象,同时,远程对象必须实现java.rmi.server.UniCastRemoteObject类才能供客户端访问并提供一定的服务。

RMI开发步骤

  1. 首先创建远程接口并声明远程方法,需要继承自java.rmi.Remote(Client和Server需要共享这个接口);
  2. 创建远程接口的实现类,这个类必须继承自java.rmi.server.UnicastRemoteObject(只有Server需要这个实现类,Client可以没有);
  3. 编写Server,绑定端口,注册对象;
  4. 编写Client,监听端口并查找对象。

Spring对RMI的支持很简单  

不用服务接口继承Remote

不用服务实现类继承UnicastRemoteObject

也不用我们自己注册rmi的远程服务实现类


服务器端 你需要做的仅仅是 写好你需要提供远程服务的实现类

然后将其交给RmiServiceExporter类 RmiServiceExporter会将实现类发布为RMI服务

通过 register Port 提供服务(暴露接口)

  

客户端 也很简单

只需要使用RmiProxyFactoryBean从服务器端的URL从获取服务对象  并进行封装给你定义的id

然后从spring容器中获取RmiProxyFactoryBean封装的id即可

通过 servicePort 传输数据。

  1. 什么是分布式?

分布式应用开发简单的说,是指将用户界面、控制台服务、数据库管理三个层次部署在不同的位置上。其中用户界面是客户端实现的功能,控制台服务是一个专门的服务器,数据管理是在一个专门的数据库服务器上实现的。


    以前的项目持久层、业务层、控制层都是写在一起的,使用了分布式以后把系统的各个模块部署到不同服务器上,项目之间的访问通过指定的端口号和访问地址。

为什么要使用分布式呢?

当访问量不大的时候使用单台模式可以满足需求,但是当访问量有几百上千万的时候,单台服务器已经不能满足需求了,这时可以使用分布式,部署多个业务层缓解系统压力,当其中一个宕机了也不会影响到系统用户访问。



创建一个SpringRMI项目步骤:

1.使用Maven创建一个父项目。

2.创建三个子项目Core、Service、Web分别对应持久层、业务层、控制层。

3.在Service层的Spring配置文件中添加如下配置:

<!--服务端-->
<bean id="studentServiceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">
   <!--serviceName-->
   <property name="serviceName" value="SService"/>
   <!--service实现类-->
   <property name="service" ref="studentService"/>
   <!--service接口-->
   <property name="serviceInterface" value="com.ptteng.service.StudentService"/>
   <!--注册端口号-->
   <property name="registryPort" value="8010"/>
   <!--给服务注册端口号-->
   <property name="servicePort" value="8020"/>
</bean>

<bean id="studentService" class="com.ptteng.service.impl.StudentServiceImpl"/>

其余配置和SpringMVC的一样

4.创建服务端启动程序。

public class StartServer {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring-mvc.xml");
       StudentService service = (StudentService) ac.getBean("studentService");
       System.out.println("server发布成功!");

5.在Web层的配置文件中添加如下配置:

<bean id="studentService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean" scope="prototype">
   <property name="serviceUrl" value="rmi://localhost:8010/SService"/>
   <property name="serviceInterface" value="com.ptteng.service.StudentService"/>
</bean>



6.启动顺序是:先启动服务端程序,再启动客户端的程序。


pom文件配置:

在父pom中配置所需要的依赖,在子项目pom.xml文件中,有个parent节点,继承了根节点的pom,这是maven的项目继承依赖,会从父POM中继承一些值,这对构建一个大型的系统来说很有必要,这样的话你就不需要一遍一遍的重复添加同样的依赖元素,如果在子项目中有同样的依赖,则会覆盖父POM中的值。

简单来说就是在父pom文件里写好依赖,子项目里使用parent继承



  • 3.部署两台Service,在WEB中随机访问任意一台Service。
  • 就是写两个Service层,内容可以一样,但是在Service的配置文件中注册端口要不同。
在Web层使用random函数产生随机数0或1.如果是0则访问service1,如果是1则访问service2
package com.ptteng.controller;

import com.ptteng.model.Student;
import com.ptteng.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Controller
public class StudentController {
int i = 0;
   int j = 0;
   int k = 0;
   int l = 0;

   private ApplicationContext ac = null;

   @RequestMapping(value = "/list", method = RequestMethod.GET)
public String getAll(HttpServletRequest request, HttpServletResponse response) {

ac = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
       int flag = Math.random() > 0.5 ? 1 : 0;//使用random产生随机数0或1,如果是0则访问service1,如果是1则访问service2
       System.out.println("产生的随机数是:" + flag);
       List<Student> list = null;
       try {
switch (flag) {
case 1:
StudentService studentService1 = (StudentService) ac.getBean("studentService");
                   i++;
                   System.out.println("第"+i+"次访问第一层service1");
                   list = studentService1.getAll();
                   break;
               default:
j++;
                   StudentService studentService2 = (StudentService) ac.getBean("studentService2");
                   System.out.println("第"+j+"次访问第一层访问service2");
                   list = studentService2.getAll();
                   break;
           }

} catch (Exception e) {
switch (flag) {
case 1:
k++;
                   StudentService studentService2 = (StudentService) ac.getBean("studentService2");
                   System.out.println("第"+k+"次访问第一层访问失败,转为第二层访问service2");
                   list = studentService2.getAll();

                   break;
               default:
l++;
                   StudentService studentService1 = (StudentService) ac.getBean("studentService");
                   System.out.println("第"+l+"次访问第一层失败,转为第二层访问service1");
                   list = studentService1.getAll();
                   break;
           }
}

request.setAttribute("list", list);
       return "list";
   }
}


  • 4.部署两台WEB,通过Nginx配置两台WEB随机访问,两台WEB可以随机访问两台Service。
  • 创建两个Web,端口不同。使用Nginx配置负载均衡,随机访问两个Web
  • 在hosts文件中添加
  • 实现效果:

关闭一个service


spring 默认scope 是单例模式

这样只会创建一个Action对象

每次访问都是同一个Action对象,数据不安全

struts2 是要求 每次次访问 都对应不同的Action

 scope="prototype" 可以保证 当有请求的时候 都创建一个Action对象

使用Spring工具类WebApplicationContextUtils.getWebApplicationContext手动获取Bean

这样在1个Service宕机重启后通过非单例模式自动创建一个新的Action对象,再手动获取bean。就可以访问恢复后的Service

ac = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());


使用问题:

client的启动端口和service注册的端口不一样,导致报错 :

Error creating bean with name 'studentService' defined in class path resource [Spring-mvc-client.xml]: 

Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: port out of range:80100

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)








返回列表 返回列表
评论

    分享到