Cluster模式潜在问题及解决方案、Web服务综合解决方案

会不断更新!冲冲冲!跳转连接

https://blog.csdn.net/qq_35349982/category_10317485.html

Cluster模式潜在问题及解决方案、Web服务综合解决方案

1.一致性的哈希算法

1.1分布式与集群

分布式和集群是不⼀样的,分布式⼀定是集群,但是集群不⼀定是分布式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NHrvwdvG-1599116613533)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\课堂pic\分布式和集群.png)]

1.2Hash算法

1.1 顺序查找法

list:List[1,5,7,6,3,4,8]
// 通过循环判断来实现
for(int element: list) {
if(element == n) {
如果相等,说明n存在于数据集中
}}

1.2⼆分查找法

1.3 直接寻址法

定义⼀个数组,数组⻓度⼤于等于数据集⻓度,此处⻓度为9,数据1就存储在下标为1的位置3就存储在下标为3的元素位置,,,依次类推。

这个时候,我想看下5存在与否,只需要判断list.get(5) array[5] 是否为空,如果为空,代表5不存在于数据集,如果不为空代表5在数据集当中,通过⼀次查找就达到了⽬的,时间复杂度为O(1)。这种⽅式叫做“直接寻址法”:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7HXb6jdT-1599116613537)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\课堂pic\直接寻址法.png)]

1.4 开放寻址法 | 拉链法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9aTE4hF-1599116613540)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\课堂pic\拉链法.png)]

1.3⼀致性Hash算法

⾸先有⼀条直线,直线开头和结尾分别定为为1和2的32次⽅减1,这相当于⼀个地址,对于这样⼀条线,弯过来构成⼀个圆环形成闭环,这样的⼀个圆环称为hash环。

红节点 —服务器节点

绿人像 —请求访问

每⼀台服务器负责⼀段,⼀致性哈希算法对于节点的增减都只需重定位环空间中的⼀⼩部分数据,具有较好的容错性和可扩展性。

但是,⼀致性哈希算法在服务节点太少时,容易因为节点分部不均匀⽽造成数据倾斜问题。

例如系统中只有两台服务器,其环分布如下,节点2只能负责⾮常⼩的⼀段,⼤量的客户端请求落在了节点1上,这就是数据(请求)倾斜问题

解决方案:⼀致性哈希算法引⼊了虚拟节点机制,即对每⼀个服务节点计算多个哈希,每个计算结果位置都放置⼀个此服务节点,称为虚拟节点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UvckVAw8-1599116613543)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\课堂pic\圈图.png)]

##[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j18iwYVg-1599116613544)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\课堂pic\一致性的哈希算法.png)]

1.4手写Hash算法

1.流程口述

  1. 声明服务器的节点,计算hashCode然后存到SortedMap(上圈)中,(为解决数据倾斜问题,增加虚拟节点)
  2. 声明客户端的节点
  3. 请求到来时:那客户端的ip的hashCode值去服务器节点中访问,有则取,没有则取第一个
 SortedMap<Integer, String> integerStringSortedMap = hashServerMap.tailMap(clientHash);

2.代码

import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

public class ConsistentHashWithVirtual {

    public static void main(String[] args) {
        //step1 初始化:把服务器节点IP的哈希值对应到哈希环上
        // 定义服务器ip
        String[] tomcatServers = new String[]{"123.111.0.0","123.101.3.1","111.20.35.2","123.98.26.3"};

        SortedMap<Integer,String> hashServerMap = new TreeMap<>();


        // 定义针对每个真实服务器虚拟出来几个节点
        int virtaulCount = 3;


        for(String tomcatServer: tomcatServers) {
            // 求出每一个ip的hash值,对应到hash环上,存储hash值与ip的对应关系
            int serverHash = Math.abs(tomcatServer.hashCode());
            // 存储hash值与ip的对应关系
            hashServerMap.put(serverHash,tomcatServer);

            // 处理虚拟节点
            for(int i = 0; i < virtaulCount; i++) {
                int virtualHash = Math.abs((tomcatServer + "#" + i).hashCode());
                hashServerMap.put(virtualHash,"----由虚拟节点"+ i  + "映射过来的请求:"+ tomcatServer);
            }

        }


        //step2 针对客户端IP求出hash值
        // 定义客户端IP
        String[] clients = new String[]{"10.78.12.3","113.25.63.1","126.12.3.8"};
        for(String client : clients) {
            int clientHash = Math.abs(client.hashCode());
            //step3 针对客户端,找到能够处理当前客户端请求的服务器(哈希环上顺时针最近)
            // 根据客户端ip的哈希值去找出哪一个服务器节点能够处理()
            SortedMap<Integer, String> integerStringSortedMap = hashServerMap.tailMap(clientHash);
            if(integerStringSortedMap.isEmpty()) {
                // 取哈希环上的顺时针第一台服务器
                Integer firstKey = hashServerMap.firstKey();
                System.out.println("==========>>>>客户端:" + client + " 被路由到服务器:" + hashServerMap.get(firstKey));
            }else{
                Integer firstKey = integerStringSortedMap.firstKey();
                System.out.println("==========>>>>客户端:" + client + " 被路由到服务器:" + hashServerMap.get(firstKey));
            }
        }
    }


}

1.5nginx的负载均衡

1.ip_hash的问题

Nginx的IP_hash策略可以在客户端ip不变的情况下,将其发出的请求始终路由到同⼀个⽬标服务器上,实现会话粘滞,避免处理session共享问题

如果没有IP_hash策略,那么如何实现会话粘滞?

可以维护⼀张映射表,存储客户端IP或者sessionid与具体⽬标服务器的映射关系

<ip,tomcat1>

缺点

1)那么,在客户端很多的情况下,映射表⾮常⼤,浪费内存空间

2)客户端上下线,⽬标服务器上下线,都会导致重新维护映射表,映射表维护成本很⼤

2.ngx_http_upstream_consistent_hash模块

ngx_http_upstream_consistent_hash 模块是⼀个负载均衡器,使⽤⼀个内部⼀致性hash算法来选择合适的后端节点。

下载地址: https://github.com/replay/ngx_http_consistent_hash

配置策略

consistent_hash $remote_addr:可以根据客户端ip映射
consistent_hash $request_uri:根据客户端请求的uri映射
consistent_hash $args:根据客户端携带的参数进⾏映

安装模块

./configure —add-module=/root/ngx_http_consistent_hash-master
make
make install
#负载均衡策略
upstream myServer{
#每个请求按照ip的hash结果分配,每⼀个客户端的请求会固定分配到同⼀个⽬标服务器处理,可以解决session问题
 consistent_hash $request_uri;
 #weight代表权重,默认每⼀个负载的服务器都为1,权重越⾼那么被分配的请求越多(⽤于服务器性能不均衡的场景)
 server 47.95.1.96:8080 weight=1;
 server 47.95.1.96:8081 weight=2;
}

proxy_pass http://myServer/;

2.时钟同步问题

1.场景

一个电商系统,三个服务器,三个订单,因为时钟不同步,造成三个系统的`订单时间`不一致

2.方案

2.1访问授时中心

#使⽤ ntpdate ⽹络时间同步命令

ntpdate -u ntp.api.bz #从⼀个时间服务器同步时间		

2.2一个节点访问,其他节点同步

⼀个服务器节点可以访问互联⽹或者所有节点都不能够访问互联⽹

一个节点访问,其他节点同步

##修改/etc/ntp.conf⽂件

#1.先让一个节点同步授时中心
ntpdate -u ntp.api.bz
#1、如果有 restrict default ignore,注释掉它
#2、添加如下⼏⾏内容
 restrict 172.17.0.0 mask 255.255.255.0 nomodify notrap # 放开局域⽹同步功能,172.17.0.0是你的局域⽹⽹段
 server 127.127.1.0 # local clock
 fudge 127.127.1.0 stratum 10  #选择一个主节点
#3、重启⽣效并配置ntpd服务开机⾃启动
 service ntpd restart
 chkconfig ntpd on
 
 ##其他节点同步
 ntpdate 172.17.0.17

3.分布式ID解决⽅案

3.1 UUID

UUID 是指Universally Unique Identififier,翻译为中⽂是通⽤唯⼀识别码

public class MyTest {
 public static void main(String[] args) {
 	System.out.println(java.util.UUID.randomUUID().toString());
 }
}

3.2 独⽴数据库的⾃增ID

场景:分表后两张表公用一个独立表的ID

创建一张表

-- ----------------------------
-- Table structure for DISTRIBUTE_ID
-- ----------------------------
DROP TABLE IF EXISTS `DISTRIBUTE_ID`;
CREATE TABLE `DISTRIBUTE_ID` (
 `id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '主键',
 `createtime` datetime DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into DISTRIBUTE_ID(createtime) values(NOW());
select LAST_INSERT_ID()

缺点:

  1. 需要代码连接到数据库才能获取到id,性能⽆法保障,

  2. mysql数据库实例挂掉了,那么就⽆法获取分布式id了。

3.3 SnowFlake 雪花算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ssHfBBXG-1599116613545)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\课堂pic\雪花ID图.png)]

3.4 借助Redis的Incr命令获取全局唯⼀ID

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZwbRljQR-1599116613547)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\课堂pic\redis流程.png)]

安装redis后

  1. 修改配置文件 redis.conf
  2. 启动
#bind 1270.0.1注释掉
protected-mode no #设置为no

<dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
 <version>2.9.0</version>
</dependency>
Jedis jedis = new Jedis("127.0.0.1",6379);
try {
 long id = jedis.incr("id");//定义的KEY 是ID
 System.out.println("从redis中获取的分布式id为:" + id);
} finally {
 if (null != jedis) {
 jedis.close();
 } }

源码:

   public Long incr(String key) {
        this.checkIsInMultiOrPipeline();//获取链接
        this.client.incr(key); //获取key
        return this.client.getIntegerReply();
    }
//=======================================
//读取
public long readLongCrLf() {
        byte[] buf = this.buf;
        this.ensureFill();
        boolean isNeg = buf[this.count] == 45;
        if (isNeg) {
            ++this.count;
        }

        long value = 0L;

        while(true) {
            this.ensureFill();
            int b = buf[this.count++];
            if (b == 13) {
                this.ensureFill();
                if (buf[this.count++] != 10) {
                    throw new JedisConnectionException("Unexpected character!");
                } else {
                    return isNeg ? -value : value;
                }
            }

            value = value * 10L + (long)b - 48L;
        }
    }

4.分布式任务调度

4.1定时任务场景

  • 订单审核,出库
  • 订单支付退款
  • 礼券同步
  • 物流信息推送
  • 短信推送
  • 日志监控

4.2什么是分布式任务调度

  1. 在分布式集群环境下的调度任务(同一个定时任务部署多分,只执行一个定时任务)
  2. 分布式调度->定时任务的分布式->定时任务拆分(把一个大任务拆分成切分,同时执行)

4.3定时任务与消息队列的区别

  • 共同点

    1. 异步处理

      比如 注册,下单时间

    2. 应用解耦

      不管定时任务作业还是MQ 都可以实现应用解耦

    3. 流量削峰

      任何作业和MQ都可以用来抗流量

  • 不同点

    1. 定时任务作业时时间驱动,MQ是时间驱动
    2. 时间驱动是不可替代的,比如说金融系统是每日的利息结算,不是来一条做一条,还是批量时间去处理
    3. 定时任务作业更倾向于批处理,MQ倾向于逐条处理

4.4 Quartz定时

 <!--任务调度框架quartz-->
        <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.2</version>
        </dependency>
public class QuartzMan {

    // 1、创建任务调度器(好比公交调度站)
    public static Scheduler createScheduler() throws SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        return scheduler;
    }


    // 2、创建一个任务(好比某一个公交车的出行)
    public static JobDetail createJob() {
        JobBuilder jobBuilder = JobBuilder.newJob(DemoJob.class); // TODO 自定义任务类
        jobBuilder.withIdentity("jobName","myJob");
        JobDetail jobDetail = jobBuilder.build();
        return jobDetail;
    }


    /**
     * 3、创建作业任务时间触发器(类似于公交车出车时间表)
     * cron表达式由七个位置组成,空格分隔
     * 1、Seconds(秒)  0~59
     * 2、Minutes(分)  0~59
     * 3、Hours(小时)  0~23
     * 4、Day of Month(天)1~31,注意有的月份不足31天
     * 5、Month(月) 0~11,或者 JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC
     * 6、Day of Week(周)  1~7,1=SUN或者  SUN,MON,TUE,WEB,THU,FRI,SAT
     * 7、Year(年)1970~2099  可选项
     *示例:
     * 0 0 11 * * ? 每天的11点触发执行一次
     * 0 30 10 1 * ? 每月1号上午10点半触发执行一次
     */
    public static Trigger createTrigger() {
        // 创建时间触发器
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("triggerName","myTrigger")
                .startNow()
                .withSchedule(CronScheduleBuilder.cronSchedule("*/2 * * * * ?")).build();
        return cronTrigger;
    }



    /**
     * main函数中开启定时任务
     * @param args
     */
    public static void main(String[] args) throws SchedulerException {
        // 1、创建任务调度器(好比公交调度站)
        Scheduler scheduler = QuartzMan.createScheduler();
        // 2、创建一个任务(好比某一个公交车的出行)
        JobDetail job = QuartzMan.createJob();
        // 3、创建任务的时间触发器(好比这个公交车的出行时间表)
        Trigger trigger = QuartzMan.createTrigger();
        // 4、使用任务调度器根据时间触发器执行我们的任务
        scheduler.scheduleJob(job,trigger);
        scheduler.start();
    }
}

4.5 Elastic-job

github地址:https://github.com/elasticjob

1.主要功能

  • 分布式环境下,能够避免同一个任务多实例重复执行
  • 弹性扩容缩容,当集群中增加某一个实例,它能被选举且执行任务,减少一个实例,任务自动分配给其他实例
  • 任务失败时,转移给其他实例
  • 自动记录错过执行的作业
  • 支持并行调度,支持任务分片
  • 作业分片后,能后保证同一分片在分布式环境中仅执行一个

2.代码执行实例

  • 分片 ====>shardingItemParameters(“0=bachelor,1=master,2=doctor”)
    1. 把一个任务分成多个task,每一个task交给一个具体的一个机器实例去处理
    2. Strategy策略定义这些分片项怎么去分配到机器,默认是平均分,分片和作业是通过一个注册中心协调的
  • 扩容
  1. 分⽚项也是⼀个JOB配置,修改配置,重新分⽚,在下⼀次定时运⾏之前会重新调⽤分⽚算法,那么这个分⽚算法的结果就是:哪台机器运⾏哪⼀个⼀⽚,这个结果存储到zk中的,主节点会把分⽚给分好放到注册中⼼去,然后执⾏节点从注册中⼼获取信息(执⾏节点在定时任务开启的时候获取相应的分⽚)
  2. 高可用,其他节点全部挂点了,剩下一个节点,则所有的节点都指向这个节点
   <!-- https://mvnrepository.com/artifact/com.dangdang/elastic-job-lite-core -->
        <!--elastic-job-lite核心包-->
        <dependency>
            <groupId>com.dangdang</groupId>
            <artifactId>elastic-job-lite-core</artifactId>
            <version>2.1.5</version>
        </dependency>
//任务调度类  
public static void main(String[] args) {
        // 配置分布式协调服务(注册中心)Zookeeper
        ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration("localhost:2181","data-archive-job");
        CoordinatorRegistryCenter coordinatorRegistryCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
    	//初始化
        coordinatorRegistryCenter.init();

        // 配置任务(时间事件、定时任务业务逻辑、调度器)
        JobCoreConfiguration jobCoreConfiguration = JobCoreConfiguration
                .newBuilder("archive-job", "*/2 * * * * ?", 3)
                .shardingItemParameters("0=bachelor,1=master,2=doctor").build();
        SimpleJobConfiguration simpleJobConfiguration = new SimpleJobConfiguration(jobCoreConfiguration,ArchivieJob.class.getName());
		//任务初始化
    	//1.配置任分布式协调中心
    	//2.配置任务,执行的任务类,定时时间
        JobScheduler jobScheduler = new JobScheduler(coordinatorRegistryCenter, LiteJobConfiguration.newBuilder(simpleJobConfiguration).overwrite(true).build());
        jobScheduler.init();


    }
/**
 * ElasticJobLite定时任务业务逻辑处理类
 */
public class ArchivieJob implements SimpleJob {

    /**
     * 需求:resume表中未归档的数据归档到resume_bak表中,每次归档1条记录
     * execute方法中写我们的业务逻辑(execute方法每次定时任务执行都会执行一次)
     * @param shardingContext
     */
    @Override
    public void execute(ShardingContext shardingContext) {
        int shardingItem = shardingContext.getShardingItem();
        System.out.println("=====>>>>当前分片:" + shardingItem);

        // 获取分片参数
        String shardingParameter = shardingContext.getShardingParameter(); // 0=bachelor,1=master,2=doctor


        // 1 从resume表中查询出1条记录(未归档)
        String selectSql = "select * from resume where state='未归档' and education='"+ shardingParameter +"' limit 1";
        List<Map<String, Object>> list = JdbcUtil.executeQuery(selectSql);
        if(list == null || list.size() ==0 ) {
            System.out.println("数据已经处理完毕!!!!!!");
            return;
        }
        // 2 "未归档"更改为"已归档"
        Map<String, Object> stringObjectMap = list.get(0);
        long id = (long) stringObjectMap.get("id");
        String name = (String) stringObjectMap.get("name");
        String education = (String) stringObjectMap.get("education");

        System.out.println("=======>>>>id:" + id + "  name:" + name + " education:" + education);

        String updateSql = "update resume set state='已归档' where id=?";
        JdbcUtil.executeUpdate(updateSql,id);

        // 3 归档这条记录,把这条记录插入到resume_bak表
        String insertSql = "insert into resume_bak select * from resume where id=?";
        JdbcUtil.executeUpdate(insertSql,id);
    }
}

分布式的理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sP7NXIHM-1599116613549)(D:\拉钩笔记\lagouNode\2Stage\Cluster模式潜在问题及解决方案、Web服务综合解决方案\pic\分布式的理解.png)]

5.Session的共享

5.1 Session问题原因分析

Http是无状态协议

两种用于保持Http状态的技术,那就是Cookie和Session

JSESSIONID是一个Cookie,Servlet容器(tomcat,jetty)用来记录用户session

5.2 Session一致性的解决方案

  1. nginx的ip_hash策略

同⼀个客户端IP的请求都会被路由到同⼀个⽬标服务器,也叫做会话粘滞

  • 优点:

配置简单,不⼊侵应⽤,不需要额外修改代码

  • 缺点:

服务器重启Session丢失

存在单点负载⾼的⻛险

单点故障问题

  1. Session复制(不推荐)多个tomcat之间通过修改配置⽂件,达到Session之间的复制

  2. Session共享(交代redis)

优点:

  • 能适应各种负载均衡策略
  • 服务器重启或者当即不会造成Session丢失
  • 扩展能力强
  • 适合大集群数量使用

缺点

  • 对应用有侵入性

5.3代码实现(Session共享)


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379

@SpringBootApplication
@EnableCaching
@EnableRedisHttpSession //加这个注解
public class LoginApplication  extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(LoginApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(LoginApplication.class, args);
    }

}

5.4源码刨析

  1. 引入了RedisHttpSessionConfiguration类
  2. 继承了SpringHttpSessionConfiguration类
  3. SessionRepositoryFilter类
//1.引入了RedisHttpSessionConfiguration类
@Import({RedisHttpSessionConfiguration.class})
public @interface EnableRedisHttpSession {
}
//===========================
//2.继承了SpringHttpSessionConfiguration类
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration {
    
}
//===========================
//3.SessionRepositoryFilter类
public class SpringHttpSessionConfiguration{
  @Bean
    public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) {
        SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter(sessionRepository);
        sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
        return sessionRepositoryFilter;
    }
}
//
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
        SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryFilter.SessionRepositoryRequestWrapper(request, response);
        SessionRepositoryFilter.SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryFilter.SessionRepositoryResponseWrapper(wrappedRequest, response);

        try {
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } finally {
            wrappedRequest.commitSession();//redis提交
        }

    }
//redis提交
    private void commitSession() {
            SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper.HttpSessionWrapper wrappedSession = this.getCurrentSession();
            if (wrappedSession == null) {
                if (this.isInvalidateClientSession()) {
                    SessionRepositoryFilter.this.httpSessionIdResolver.expireSession(this, this.response);
                }
            } else {
                S session = wrappedSession.getSession();
                this.clearRequestedSessionCache();
                SessionRepositoryFilter.this.sessionRepository.save(session);
                String sessionId = session.getId();
                if (!this.isRequestedSessionIdValid() || !sessionId.equals(this.getRequestedSessionId())) {
                    SessionRepositoryFilter.this.httpSessionIdResolver.setSessionId(this, this.response, sessionId);
                }
            }

        }

附录

1.wrapper的概念

实现Wrapper的类需要:

  1. 实现某个接口
  2. 依赖该接口的子类, 并通过接口方法访问子类对象的方法
public class MyArrays {
    public List<Point> asList(Point[] points) {
        List<Point> list = new ArrayList<Point>();
        for (int i = 0; i < points.length; i++) {
            list.add(points[i]);
        }
        
        return new ArrayListWrapper(list);
    }
}

class ArrayListWrapper implements List<Point> {
    private List<Point> list;

    ArrayListWrapper(List<Point> list) {
        this.list = list;
    }

    @Override
    public int size() {
        return list.size();
    }

    @Override
    public boolean add(Point e) {
        throw new UnsupportedOperationException("...");
    }

    @Override
    public boolean remove(Object o) {
        throw new UnsupportedOperationException("...");
    }
…………………………

}

单词

Cluster 群,集群

strict 严格

stratum 层

ensure 保证
Coordinator协调,班主任

elastic 弹性的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis Cluster 连接失败可能是由于以下原因导致的: 1. Redis Cluster 的节点配置不正确。请检查节点的 IP 地址和端口号是否正确配置。可以使用 Redis Cluster 命令行工具 redis-cli 查看 Redis Cluster 的节点信息。 2. 网络连接问题。请确保 Redis Cluster 的节点之间可以相互通信,可以使用 ping 命令测试节点之间的连通性。 3. Redis Cluster 的配置问题。请确保 Redis Cluster 的配置文件正确配置,特别是 cluster-enabled 和 cluster-config-file 这两个参数。 4. Redis Cluster 的密码认证问题。如果 Redis Cluster 开启了密码认证机制,请确保在连接时提供了正确的密码。 针对以上问题,可以尝试以下方法进行解决: 1. 检查节点配置。使用 redis-cli 工具连接到 Redis Cluster,使用 cluster nodes 命令查看节点信息是否正确,如果有节点状态为 FAIL,可以使用 cluster meet 命令重新加入节点。 2. 检查网络连接。使用 ping 命令测试节点之间的连通性,如果发现有节点无法 ping 通,可以检查网络配置,或者尝试重启节点。 3. 检查配置文件。使用 redis-cli 工具连接到 Redis Cluster,使用 cluster info 命令查看 Redis Cluster 的配置信息,检查 cluster-enabled 和 cluster-config-file 参数是否正确配置。 4. 检查密码认证。如果 Redis Cluster 开启了密码认证机制,可以在连接时使用 -a 参数提供密码。例如:redis-cli -c -h host -p port -a password。 如果以上方法都无法解决问题,可以尝试重新部署 Redis Cluster

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值