SpringCloud学记笔记(一)

首先说一下springcloud的几个官网吧

  • 中文网 https://www.springcloud.cc/spring-cloud-dalston.html
  • 中国社区:http://springcloud.cn/

接下来便是在学习springcloud的过程中记录的知识点

1:Eureka Server的首页在正常访问后过一点时间在去访问界面 或出现如下红色字体

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

这个出现的原因是SpringCloud Eureka的自我保护机制
简介SpringCloud的自我保护机制

什么是自我保护模式?
  
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,
微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。
Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内(大概十五秒)丢失过多客户端(大概85%)时(可能发生了网络分区故障),
那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,
不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
 
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,
该Eureka Server节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。
一句话讲解:好死不如赖活着
 
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),
也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
 
在Spring Cloud中,可以使用eureka.server.enable-self-preservation = false 禁用自我保护模式。

2:Eureka修改服务名称

instance:
    instance-id: consumer

在这里插入图片描述
3:Eureka比zookeeper强的地方

作为服务注册中心,Eureka比Zookeeper好在哪里

著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性P在是分布式系统中必须要保证的,
因此我们只能在A和C之间进行权衡。
因此
Zookeeper保证的是CP,
Eureka则是AP。

  • Zookeeper保证CP

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,
服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,
剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不
能容忍的。

  • Eureka保证AP

Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,
剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,
只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,
Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,
此时会出现以下几种情况:

    1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
    1. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
    1. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。

4:为Eureka Server添加用户认证
这一块大概是因为版本的问题吧,差一点整自闭。不管是跟着视频做,还是看课本就是不行。后来百度了一下 终于搞了出来。
先说一下此处用的版本是

  • springboot2.1.7
  • springcloud:Greenwich

步骤:

  • 首先在pom文件中添加maven依赖
		
        
            org.springframework.boot
            spring-boot-starter-security
        

  • 在properties配置文件添加配置,设置账户密码
#新版本开启权限
spring.security.user.name=user
spring.security.user.password=123

倘若在此不设置账户密码的话,则账户默认为user,密码为随机产生,在项目启动的时候随即打出。

  • 在yml文件中添加如下配置
eureka:
  client:
    service-url:
      defaultZone: http://username:password@localhost:端口号/eureka  #设置与eureka service的交互地址
  • 新版本的springcloud说是因为 Spring Security 默认开启了所有 CSRF 攻击防御,需要禁用 /eureka 的防御。因此需要重写WebSecurityConfigurerAdapterconfigure方法,
    详细代码:
package eureka.springcloud.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception{
        // 开启认证
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

至此,eureka服务的配置完成,输入eureka地址登陆即可看到监控页面。

接下来若想要将为服务注册进入Eureka Server,

  • yml配置文件作如下配置
eureka:
  client:
    service-url:
      defaultZone: http://username:password@localhost:端口号/eureka
    instance: 
      prefer-ip-address: true
  • pom.xml添加依赖
		
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

启动项目便可将服务注册进eureka中。

需要注意的是:

  • eureka.instance.prefer-ip-address = true表示将自己的ip注册进Eureka Server中。如不配置或将其设置成false,则表示注册微服务所在操作系统的hostname到Eureka Server中。

  • 在springcloud Edgware之前,想要将服务注册进Eureka Server或其他服务发现组件,必须在启动类上添加@EnableEurekaClient或@EnableDiscoveryClient注解

  • 在更高的版本中,只需要添加相应的依赖便可自动注册。

  • 若不想将服务注册进Eureka中,则只需设置spring.cloud.service-registry.auto-registration.enabled=false或@EnableDiscoveryClient(auto-Register = false)即可

5:使用Ribbon实现客户端侧负载均衡
Ribbon能够自动的帮助服务消费者去请求。默认为我们提供了很多负载均衡地算法,例如轮询,随机等,我们也可以为Ribbon自定义负载均衡算法(不会。。。待学习。。。)

为服务消费者整合Ribbon

  • 为项目引入Ribbon的依赖。
		
            org.springframework.cloud
            spring-cloud-stater-netflix-ribbon
        

如果项目中已经引入了如下依赖,则无需再次引入Ribbon的依赖,因为下面的包含Ribbon。

  	   
           org.springframework.cloud
           spring-cloud-starter-netflix-eureka-client
       
  • 为RestTemplate添加@LoadBalanced注解,就可为RestTemplate整合Ribbon,使其具有负载均衡的能力。
   @Bean
   @LoadBalanced
   public RestTemplate restTemplate(){
       return new RestTemplate();
   }

修改Controller代码,具体如下

@RestController
public class UserController {

    /**
     * 提供一个用于访问rest接口的模板对象
     */
    @Autowired
    private RestTemplate restTemplate;
    
    /*首先是将请求的地址改成生产者为服务的虚拟主机名。当Ribbon和Eureka配合使用时,会自动地将虚拟主机名映射成		为服务的网络地址*/
    private static final String url = "http://PROVIDER-USER";

    @GetMapping("/persion")
    public Persion[] getConsumer(){
        /**
         * 通过访问 rest 获取到json对象 然后转换成user对象
         */
        System.out.println("来到了ribbon。。。。。。。。。。");
        //注意: 返回的是List集合对象,用数组接收   
        Persion[] persion =  restTemplate.getForObject(url + "/persion/", Persion[].class);
        System.out.println("====================>:"+persion);
        return persion;
    }
}

负载均衡基本配置完成。在此说一个碰见过的坑,超级坑,搞了半天。
情况是,之前我使用RestTemplate请求生产者,返回值是单个对象,在反序列化的时候报错,后来查明原因是反序列化的时候,对象需要有无参构造。
这次是返回结果是List集合。消费者在接收数据的时候用的也是List集合来接收的。结果在反序列化的时候报错反序列化失败。json依赖引入了,无参构造也有,但就是不成。后来查资料说是在接收的时候要用到数组。便改成了上面的接收方式。成了。。。。。
无语了,改了半天。但是具体的使用原因还不明白。如果有哪位知道的可以留言。

6:上面已经配置了Ribbon实现了负载均衡。下面大概的说一下Riddon的负载均衡算法。
首先时Ribbon默认使用轮询算法来实现负载均衡。即按顺序依次使用服务的提供者。
若想要使用其他的算法,则只需要在配置类中将其注册即可
例如下面使用的随机算法

	@Bean
    public IRule myRule(){
        return new RandomRule();
    }

还有RetryRule算法。这个算法是先按照轮询的算法的策略获取服务,实现负载均衡。如果获取服务失败则会在指定时间内进行重试,获取可用服务。即若服务有挂掉的,则在一定时间内尝试从新连接这个服务。若连接不上则忽略这个服务,剩下的服务按照轮询算法实施。

负载均衡算法
SpringCloud学记笔记(一)_第1张图片
7:之前记录了Ribbon整合Eureka及Ribbon的负载均衡算法,下面就该说一下自定义的负载均衡算法了

  • 首先创建一个配置类,用来编写负载均衡算法
@Configuration
public class MySelfRule {

    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
}
//随机算法

在此需要注意:该类不应该在主应用程序上下文中的@CompontScan所扫描的包中。
因为@SpringBootApplication注解包含@ComponentScan注解

  • 在启动类上添加@RibbonClient注解
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "RIBBON-ORDER",configuration = MySelfRule.class)
public class RibbonApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }


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

@RibbonClient: name属性客户端的服务名称,@configuration则为自定义配置类

接下来说一个自定义配置类的例子
将轮询算法改成 每个生产者访问5次后,在去访问下一个生产者。

  • 这个是随机算法的git地址
    解析源码:https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java

  • 通过解析源码实现自定义需求的轮询算法

public class MySelfRule_LML extends AbstractLoadBalancerRule {


    private int total = 0;
    private int index = 0;

    public Server choose(ILoadBalancer lb, Object key) {

        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List upList = lb.getReachableServers();
            List allList = lb.getAllServers();

            int serverCount = allList.size();
            if (serverCount == 0) {
                return null;
            }
			/********************************************************************/
            if (total < 5) {
                System.out.println("******************");
                server = upList.get(index);
                total++;
            } else {
                total = 0;
                index++;
            }
                if (index >= upList.size()) {
                    index = 0;
                }
                根据需求改造的代码
                /****************************************************************/

                if (server == null) {
                    Thread.yield();
                    continue;
                }

                if (server.isAlive()) {
                    return (server);
                }

                server = null;
                Thread.yield();
            }
            return server;
        }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(),key);
    }
}
  • 定义一个配置类将自定义负载均衡算法添加进IOC
@Configuration
public class MyRule {
    @Bean
    public IRule mySelfRule(){
            return new MySelfRule_LML();
    }
}
  • 最后在启动类上添加@RibbonClient注解

有一点比较迷,就是网上包括书本上说的 自定义配置类不应该在主应用程序上下文中的@CompontScan所扫描的包中。因为@SpringBootApplication注解包含@ComponentScan注解,否则自定义配置类就会被所有的Ribbon客户端共享。这个地方不太明白。
所以上面我写的自定义负载均衡算法都写在了启动类包下。如果有明白这句话的可以给我留言,教教我

8:使用Feign实现负载均衡
  在Ribbon详解中,我们使用Ribbon客户端负载均衡实现微服务之间的调用,Ribbon本质上就是一个RestTemplate对象。使用RestTemplate时,除了要编写所调用微服务的URL,还需要包装所调用微服务所返回的结果。为了克服这些不足,Spring Cloud提供了声明式调用组件—Feign。
  Feign是一个基于接口的编程方式,开发者只需要声明接口和配置注解,在调度接口方法时,Spring Cloud根据配置来调度对应的REST风格的请求,从其他微服务系统中获取数据。

  • 首先第一步肯定是引入坐标
		 
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
  • yml配置文件
server:
  port: 6900

spring:
  application:
    name: ribbon-order

eureka:
  instance:
    prefer-ip-address: true
    instance-id: ribbon  #更改服务在eureka中的名称

-properties配置文件

#设置Eureka Server交互的地址,查询和注册服务都需要依赖这个地址,与注册中心地址一致
eureka.client.service-url.defaultZone=http://user:123@eureka:10000/eureka
#集群使用  暂时注释,http://eureka-A:11000/eureka,http://eureka-B:12000/eureka
  • 创建一个Feign接口,并添加@FeignClient注解
    自我解释此接口就是用来对应Eureka中的生产者中的接口
@FeignClient(name = "PROVIDER-USER")
@Service
public interface Feign {

    @RequestMapping("/persion")
    public List getPersion();


    @RequestMapping("/findById/{id}")
    @ResponseBody
    public Persion findById(@PathVariable("id") int id);
}

@FeignClient注解中的PROVIDER-USER是任意一个客户端的名称,用于创建负载均衡器。在此因为使用了Eureka,所以将其解析为Eureka Server服务注册表中的服务。

  • 修改controller代码,将其之前的RestTemplate换成接口调用
@RestController
public class UserController {


    @Autowired
    private Feign feign;

    @GetMapping("/persion")
    public List getPersion(){
        List persion = this.feign.getPersion();
        System.out.println("feign------------查询所有");
        return persion;
    }

    @RequestMapping("/findById/{id}")
    @ResponseBody
    public Persion findById(@PathVariable("id") int id){
        Persion persion = this.feign.findById(id);
        System.out.println("feign------------根据ID:"+persion);
        return persion;
    }
}
  • 修改启动类,添加@EnableFeignClients注解

这样,消费者微服务就可以用Feign去调用生产者微服务的API了

你可能感兴趣的:(SpringCloud学记笔记(一))