发表于: 2021-10-27 23:07:55
1 905
一,今天完成的事情
任务八
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都是正常访问
二,今天问题
序列化和反序列化。分布式系统如何把原来一个综合系统拆分。
发现需要补充的任务八内容,我希望展示,希望提醒的多,这个日报就不加深度思考了。
三,今天的收获
随机服务器。如果有服务器不能用,赶快看其它服务器是否可用。
四,明天的计划
任务八深度思考,任务小结
评论