发表于: 2021-10-27 23:07:55

1 904


一,今天完成的事情

任务八

1,除了概率50%,还要做好错误处理。测试是启动service two测试。启动service one,总共两台测试。关闭service two,留下service one测试。全部能让客户不感知到其实有service不正常。不管是否开Redis,都正常。

任务要求不启动其中一台服务的时候,要能正常运行。真正在生产环境的时候,也常常会出现很多服务器,其中一台不行,但是明明其它可以正常处理的情况。所以一定要处理异常,catch到。如果抛出,一般就是连接异常了。网页会甩错误给client,会看到客户页面,但是其实现在明明可以交给别的服务器。


org.springframework.remoting.rmi.RmiProxyFactoryBean就算连接不到,获取的Bean不会为null,所以不能一开始用null判断。

applicationContext.getBean(serviceName); 这条语句就算连接不到,也不会甩错。


获得service后,只要使用远程的service,其实只要连接有问题,包括物理层的问题(网络断开等),都可能抛出错误。所以使用RMI要把controller用远程service的所有场景,写好try catch,再去尝试获取另一个已经在client.xml中注册过的service看看是否能使用。否则客户通过页面访问就会直接看到错误页面,整页都是报错信息,违背了关掉一个服务器,不能正常运行的要求。

我还是每次随机获取service,在RMIServiceUtil中每次给出,都要试验目前service是不是正常。所以要用service做一个操作,这里选的操作是查,如果有错,就转到下一个service。

package com.nicole.util;
import com.nicole.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import java.util.Random;

@Component
public class RMIServiceUtil {
private Logger logger = LoggerFactory.getLogger(RMIServiceUtil.class);
   private UserService userService;

   public UserService getUserServiceRandom(){
Random random = new Random();
       //产生的随机数为0-100的整数,不包括100
       int randNum = random.nextInt(100);
       String serviceName = null;
       //现在是二选一个service,有一个service关闭,马上看看另一个是否能用
       if( randNum % 2 == 0 ) {
try{
//first choice UserServiceOne
               logger.info("getting service one ... ");
               userService = getUserService("UserServiceOne");
               userService.selectUserName("nicole");
           } catch (Exception exception){
try {
logger.info("Service one is off, getting service two ... error : "
                           + exception.getMessage());
                   userService = getUserService("UserServiceTwo");
               }catch ( Exception e) {
logger.info("Service one and two are off. error : " + e.getMessage());
               }
}
} else {
try {
//first choice UserServiceTwo
               logger.info("getting service two ... ");
               userService = getUserService("UserServiceTwo");
               userService.selectUserName("nicole");
           }catch ( Exception exception) {
try{
logger.info("Service two is off . getting service one ... error : "
                           + exception.getMessage());
                   userService = getUserService("UserServiceOne");
               }catch ( Exception e) {
logger.info("Service one and two are off. error : "+ e.getMessage());
               }
}
}
return userService;
   }

private UserService getUserService( String serviceName){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(
"client.xml");
       return (UserService) applicationContext.getBean(serviceName);
   }

}


我的selectUserName方法代码如下:

@Override
public User selectUserName(String name){
String key = name + "userServiceImpl";
   User user = (User) redisUtil.get(key);
   if( user == null ) {
user = userDao.selectUserName( name );
       redisUtil.set( key, user, 3600);
       logger.info("----- add user to redis : " + user);
   }
return user;
}


我为每个被查询的key,设置了1小时的有效期在redis中,我觉得这个时间比较合理。


我先把redis关掉测试。只启动service two。测试了10次,50%的概率一开始随机到没开的service one,一切正常。


查看日志,如果不被catch的错误都记录好在日志了,而且我的service给出的service one不正常的日志,切换到service two都有体现。直接使用service two都像我预料的一样,给出日志。

正常得到service two


前面已经打出service one,打出报错信息。然后是我的log info : service one关闭,用service two。正常使用service two。


打开redis再测试,如果是查询过的用户名在我设置的1小时内,应该是先在redis中得到信息。也都是关闭了一个service,只有一个service测试正常,用网页查询客户能不感知到后台抛出的错误,能正常使用。我的RedisUtil在common中,如果是不同模块,序列化和反序列化和传递,应该在哪里使用,能正常使用Redis挡住关系数据库的查询,都是项目需要考虑的。各个代码,各个util怎么放,是需要考虑的。


两台service都启动正常。只有service one正常。不管是否开Redis,客户端访问都正常。


2,但是其实在service多的时候,可能有上千上万个service,代码不可能是我目前的RMIServiceUtil这样选择。也不会是每次提供service,都直接操作关系型数据库。

1)选择service应该使用负载均衡算法,一致性哈希算法还有各种优化算法选定服务器。

2)如果有服务器不能使用,需要使用备份服务器方案和均摊工作方案,代码能写很多。

3)保证高可用。询问服务器状态往往是一段时间询问一次,客户有可能短暂时间段内发现服务不对。实际工业用的代码就是要保持平衡。heart beat按照约定时间询问,10分钟一次已经可以。一旦发现问题,就转到2)处理。

我现在只有2台service,所以代码写成了1台不行赶紧找另一台。实际工业用代码的复杂很多。


3,intellij idea 将模块分别打jar包。controller以上是war包。


pom.xml中的<build>和打包有直接关系,特别注意,Spring是自己改成main的入口

 <mainClass>com.nicole.domain.StartServer</mainClass>

虽然可以手动修改jar中的 main入口,jar中的META-INF中MANIFEST.MF的内容添加了Main-Class。但是不推荐


我的build写成下面的样子

<build>

 <plugins>

   <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-shade-plugin</artifactId>
     <version>2.4.1</version>
     <configuration>
       <createDependencyReducedPom>false</createDependencyReducedPom>
     </configuration>
     <executions>
       <execution>
         <phase>package</phase>
         <goals>
           <goal>shade</goal>
         </goals>
         <configuration>
           <filters>
             <filter>
               <artifact>*:*</artifact>
               <excludes>
                 <exclude>META-INF/*.SF</exclude>
                 <exclude>META-INF/*.DSA</exclude>
                 <exclude>META-INF/*.RSA</exclude>
               </excludes>
             </filter>
           </filters>
           <transformers>
             <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
               <mainClass>com.nicole.domain.StartServer</mainClass>
             </transformer>
             <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>
           </transformers>
         </configuration>
       </execution>
     </executions>
   </plugin>

   <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-surefire-plugin</artifactId>
     <version>2.22.1</version>
     <configuration>
       <skipTests>true</skipTests>
     </configuration>
   </plugin>

   <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-compiler-plugin</artifactId>
     <version>3.8.1</version>
     <configuration>
       <source>1.8</source>
       <target>1.8</target>
     </configuration>
   </plugin>

 </plugins>

 <resources>
   <resource>
     <directory>src/main/java</directory>
     <includes>
       <include>**/*.xml</include>
     </includes>
   </resource>
   <resource>
     <directory>src/main/resources</directory>
     <includes>
       <include>*.xml</include>
       <include>**/*.xml</include>
       <include>**/*.properties</include>
       <include>**/*.ini</include>
     </includes>
     <filtering>false</filtering>
   </resource>
 </resources>

</build>


下面部分注意是jar

<groupId>org.example</groupId>
<artifactId>servicetwo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>


以service two为例子,我是通过maven 的package

通过信息确认打的包在哪里


Service One 一样加build


我的最上层的pom.xml有说明自己是

<groupId>org.example</groupId>
<artifactId>register_upload_rmi</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

我会直接在Lifecycle里,先clean,再install,得到所有包。它会告知我去哪里找各包。



4.把jar在本地分别测试。能够正常。

service one一样


5,client的war单独在Linux云上可以测试。测试成功,页面正常打开。

用resin测试部署,xftp上传,修改/usr/local/resin/conf/resin.xml来一套。

4个包都放在/usr/local/jarwar


6,在云Linux关闭一个service的时,能正常使用,就是正常用另一个service。

启动service two测试。启动service one,总共两台测试。关闭service two,留下service one测试。全部能让客户不感知到其实有service不正常。不管是否开Redis,都正常。

cd /usr/local/jarwar

后台运行就在命令后加 &

启动 service one

java -jar serviceone-1.0-SNAPSHOT.jar &

ps -aux| grep serviceone-1.0-SNAPSHOT.jar 查看是否成功运行


java -jar servicetwo-1.0-SNAPSHOT.jar &


ps -aux| grep serviceone-1.0-SNAPSHOT.jar 查看是否成功运行


7,已经配置启动过2台不同的web服务器关闭tomcat,能够通过nginx 的80正常访问。

我的nginx的web分配还是:



关闭8070的resin,或者8080的tomcat都是正常访问


二,今天问题

序列化和反序列化。分布式系统如何把原来一个综合系统拆分。

发现需要补充的任务八内容,我希望展示,希望提醒的多,这个日报就不加深度思考了。


三,今天的收获

随机服务器。如果有服务器不能用,赶快看其它服务器是否可用。


四,明天的计划

任务八深度思考,任务小结


返回列表 返回列表
评论

    分享到