SpringCloud五大组件

SpringCloud五大组件:
1.注册中心:Eureka,Nacos,Consul;
官方推荐使用的是Eureka,但是常用的是阿里提供的Nacos,而Consul基本很少使用
注册中心的作用:
避免服务与服务之间的调用(服务之间的调用是远程的)时把互相的地址/ip写死。一旦写死,如果有一方服务的地址发生改变,那么调用它的那一方就得修改地址,耦合性太高。
所以这时候就需要使用注册中心来作为一个中间件,服务的提供者负责将自己的ip发送给注册中心,而服务的消费者只需要从注册中心拉取对应提供服务的地址/ip,这样服务的消费者就可以动态获取提供者的地址进行调用了。很好的实现了解耦。

这里就讲一下Nacos的配置,因为Nacos是常用的,而且配置方法都相差不多

Nacos的配置:

首先官网下载Nacos:Nacos1.1.3的版本下载地址
1. 下载完成解压后,进入bin目录,在终端中输入:sh startup.sh -m standalone 指令开启
2.创建一个Maven工程,然后创建一个服务的提供者模块,和一个服务的消费者模块

在这里插入图片描述

 

3.在两个模块中的pom文件中导入依赖:
   
       
       
            org.springframework.cloud
            spring-cloud-starter-alibaba-nacos-discovery
            0.2.2.RELEASE
       

       
            com.alibaba.nacos
            nacos-client
            1.1.0
       


       
            org.springframework.boot
            spring-boot-starter-web
       

       
            org.springframework.boot
            spring-boot-starter-test
            test
       

       
            org.springframework.boot
            spring-boot-starter-actuator
       

   

4.在provider(服务的提供者)模块中创建配置文件:application.yml,并配置参数
server:
  port: 8000
spring:
  cloud:
    nacos:
      discovery:
        server-addr:  127.0.0.1:8848 # 配置nacos 服务端地址
  application:
    name: nacos-provider # 服务名称,大小写不敏感,一般与模块名一致(唯一的)
这样就将该服务注册到了Nacos注册中心中
consumer模块也是一样的配置 将server.port端口和application.name属性改一改就可以了

调用方法演示
package com.MrWang.nacos.controller;


import com.MrWang.nacos.domain.Goods;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * 服务的调用方
 */
@RestController
@RequestMapping("/order")
public class OrderController {
    /*
        这个模板类可以自己创建一个包,在包下创建一个spring的配置类,加上@Configuration声明
        在写一个方法,在方法上加上@Bean,返回值就是RestTemplate,方法体直接new RestTemplate()就可以了
    */
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/goods/{id}")
    public Goods findGoodsById(@PathVariable("id") int id){
        //演示discoveryClient 使用
        List instances = discoveryClient.getInstances("nacos-provider");

        //判断集合是否有数据
        if(instances == null || instances.size() == 0){
            //集合没有数据
            return null;
        }

        ServiceInstance instance = instances.get(0);
        String host = instance.getHost();//获取ip
        int port = instance.getPort();//获取端口

        System.out.println(host);
        System.out.println(port);

        String url = "http://"+host+":"+port+"/goods/findOne/"+id;
        // 3. 调用方法
        Goods goods = restTemplate.getForObject(url, Goods.class);


        return goods;
    }
}


6.创建启动类,启动即可
注意事项:Eureka如果想配置注册中心需要单独创建一个模块作为配置中心,不像Nacos和Consul是一个服务,直接下载启动就可以
2.客服端负载均衡:Ribbon
为什么要使用负载均衡策略?
答:服务的提供方可能不仅仅只有一台服务器,而是一个集群,比如上面调用服务时,使用的是instances.get(0);这样就将调用的服务写死了,拿到的永远都是第一个服务,没有实现服务的均衡。
Ribbon可以简化restTemplate的调用
使用:
在创建restTemplate方法时的@Bean上加上@LoadBalanced注解
这样在contorller中代码就可以这样编写了:

package com.MrWang.nacos.controller;


import com.MrWang.nacos.domain.Goods;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * 服务的调用方
 */

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/goods/{id}")
    public Goods findGoodsById(@PathVariable("id") int id){

        String url = "http://nacos-provider/goods/findOne/"+id;
        // 3. 调用方法
        Goods goods = restTemplate.getForObject(url, Goods.class);


        return goods;
    }
}



这样就实现了客户端的一个负载均衡
Ribbon负责均衡策略:
随机: RandomRule
轮询 : RoundRobinRule (默认)
最小并发: BestAvailableRule
过滤: AvailabilityFilteringRule
响应时间: WeightedResponse TimeRule
轮询重试: RetryRule
性能可用性: ZoneAvoidanceRule
Ribbon还不是最终解决方案,因为路径写死了String url = “http://nacos-provider/goods/findOne/”+id;
而且每启动一个服务都需要配置该路径,比较麻烦,所以就需要Feign组件完善

3.声明式服务调用:Feign
Feign概述:是一个声明式的 REST 客户端,它用了基于接口的注解方式,很方便实现客户端配置。之前在netfilx公司时是不支持SpringMvc注解的,后来别SpringCloud整合后支持了SpringMvc的注解
1
使用步凑:

导入依赖
       
            org.springframework.cloud
            spring-cloud-starter-openfeign
       


2.编写Feign调用接口,接口编写规则:消费方调用提供方时,接口的编写尽量与消费方暴露的方法(也就是controller中的方法)一直

package com.MrWang.consumer.feign;


import com.MrWang.consumer.config.FeignLogConfig;
import com.MrWang.consumer.domain.Goods;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 *
 * feign声明式接口。发起远程调用的。
 *
 String url = "http://nacos-provider/goods/findOne/"+id;
 Goods goods = restTemplate.getForObject(url, Goods.class);
 *
 * 1. 定义接口
 * 2. 接口上添加注解 @FeignClient,设置value属性为 服务提供者的 应用名称
 * 3. 编写调用接口,接口的声明规则 和 提供方接口保持一致。
 * 4. 注入该接口对象,调用接口方法完成远程调用
 */
@FeignClient(value = "nacos-provider")
public interface GoodsFeignClient {
    @GetMapping("/goods/findOne/{id}")
    public Goods findGoodsById(@PathVariable("id") int id);
}

3.消费方的controller中直接注入该接口并调用方法即可
注意:接口一般是不能注入使用的,但是spring会自动产生接口的代理对象,所以可以直接使用

Feign的超时:两种
1.连接超时 (服务调用服务的时间,默认时间1s)
2.业务逻辑处理超时 (被调用方处理业务的时间,默认时间1s)
可以自己配置,在yml配置文件中

# 设置Ribbon的超时时间
ribbon:
  ConnectTimeout: 1000 # 连接超时时间 默认1s  默认单位毫秒
  ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s 默认单位毫秒

Feign的日志记录
Feign 只能记录 debug 级别的日志信息。也是在yml配置文件中可以配置

# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
  level:
    com.itheima: debug

定义Feign日志级别Bean
FeignLogConfig

package com.MrWang.consumer.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignLogConfig {
    /*
        NONE,不记录
        BASIC,记录基本的请求行,响应状态码数据
        HEADERS,记录基本的请求行,响应状态码数据,记录响应头信息
        FULL;记录完成的请求 响应数据
     */
    @Bean
    public Logger.Level level(){
        return Logger.Level.FULL;
    }
}


最后在声明的接口中的@FeignClient注解中加上configuration = FeignLogConfig.class属性即可

4.熔断器:Hystrix
Hystrix也是Netflix公司的
用于隔离访问远程服务、第三方服务时出现级联失败(雪崩)
也就是用户访问服务时,而这个服务也需要访问其它的服务,如果最底层的服务挂了,从而导致整条链路的服务都挂了,这就称为雪崩

解决方案:
隔离:
1.线程池隔离(默认)
例如:服务的调用方有一个线程池大小为100,这个服务需要调用其它的四个服务。这个时候,就会对100的线程池进行拆分,给每一个需要调用的服务配置一个单独的线程池,比如都是25的线程容量,如果其中的一个服务挂了,请求一直发一直发,直到25的线程容量用完后就不会在调用了,这样也不会影响其它服务的使用
2.线程量隔离
给服务的提供方设置一个阈值,也就是每次请求该服务的线程最大数是多少,如果超过了这个阈值,请求将不会在发送,也有效的隔离了服务之间的耦合
降级:异常,超时
服务的提供方和消费方都可以配置,也就是说在出现异常或则超时错误时采用自定义解决方案
熔断
用于监控微服务之间的调用情况
该机制默认是关闭状态
当请求的失败率达到一个阈值时(5秒失败20次),就会打开熔断器,打开后的一段时间内(默认5秒)所有的请求都将拒绝,直 到过了这段时间,会呈现一个半开状态,这时会放一些少量请求进来,如果请求失败,回到打开状态;如果成功,就回到关闭状态
限流
具体实现自己另外搜
熔断的监控(可视化工具)
Hystrix 提供了 Hystrix-dashboard 功能,用于实时监控微服务运行状态。
但是Hystrix-dashboard只能监控一个微服务。
Netflix 还提供了 Turbine ,进行聚合监控。
Turbine搭建流程:添加链接描述
5.网关:Gateway(需要单独起一个模块)
概述:
网关旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。
即:用于管理客服端发起请求时的路径,一个请求可能会调用多个微服务,如果每个微服务都需要客户端去调用,那么增加了客服端的压力;所以在这时候就需要一个Gateway组件,用于管理客服端的请求,由Gateway统一接收客户端的请求,在由Gateway去调用微服务。

在这里插入图片描述

 

网关模块yml配置如下:

server:
  port: 80

spring:
  application:
    name: api-gateway-server

  cloud:
    # 网关配置
    gateway:
      # 路由配置:转发规则
      routes: #集合。
      # id: 唯一标识。默认是一个UUID
      # uri: 转发路径
      # predicates: 条件,用于请求网关路径的匹配规则
      # filters:配置局部过滤器的
      - id: gateway-provider
        # 静态路由
        uri: http://localhost:8001/
        predicates:
        - Path=/goods/**

该配置是有问题的,直接写死了微服务的地址,如果微服务的地址发生了改变,或者是个集群,那么这边也需要修改,耦合性太高
解决:网关与微服务之间使用注册中心交互
修改yml文件:

server:
  port: 80

spring:
  application:
    name: api-gateway-server

  cloud:
    # 网关配置
    gateway:
      # 路由配置:转发规则
      routes: #集合。
      # id: 唯一标识。默认是一个UUID
      # uri: 转发路径
      # predicates: 条件,用于请求网关路径的匹配规则
      # filters:配置局部过滤器的

      - id: gateway-provider
          # 静态路由
        # uri: http://localhost:8001/
        # 动态路由
        uri: lb://GATEWAY-PROVIDER
        predicates:
        - Path=/goods/**


5.Gateway过滤器:
局部过滤器:
也就是过滤单独的一个微服务;在yml配置文件中配置:

server:
  port: 80
spring:
  application:
    name: api-gateway-server
  cloud:
    # 网关配置
    gateway:
      # 路由配置:转发规则
      routes: #集合。
      # id: 唯一标识。默认是一个UUID
      # uri: 转发路径
      # predicates: 条件,用于请求网关路径的匹配规则
      # filters:配置局部过滤器的

      - id: gateway-provider
        # 静态路由
        # uri: http://localhost:8001/
        # 动态路由
        uri: lb://GATEWAY-PROVIDER
        predicates:
        - Path=/goods/**
        # 配置局部过滤器
        filters:
        - AddRequestParameter=username,zhangsan


全局过滤器
过滤所有的微服务
1、 在Gateway模块中定义一个类实现GlobalFilter,Ordered接口
2、在重写filter和getOrder方法
代码:

package com.MrWang.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class MyFilter implements GlobalFilter, Ordered {
    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        System.out.println("自定义全局过滤器执行了~~~");

        return chain.filter(exchange);//放行
    }

    /**
     * 过滤器排序
     * @return 数值越小 越先执行
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

你可能感兴趣的:(spring,cloud,eureka,微服务)