SpringCloud

单体架构

  • 简单方便,高度耦合,扩展性差,适合小型项目,

分布式架构

  • 分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。

  • 优点:降低耦合,有利于服务提升和扩展

微服务

  • 微服务是一种经过良好架构设计的分布式架构方案,其特征:

    1. 单一职责:微服务拆分粒度更小,每一个微服务都对应唯一的业务能力,做到单一职责,避免重复业务开发

    2. 面向服务:微服务对外暴漏业务接口

    3. 自治:团队独立,技术独立,数据独立,部署独立

    4. 隔离性强:服务调用做好隔离,容错,降级,避免出现级联问题

    总结:高内聚,低耦合,降低服务之间的影响,降低服务所能影响的范围,避免整个集群的故障

微服务技术对比

 DubboSpringCloudSpringCloudAlibaba
注册中心 zookeeper,Redis Eureka,Consul Nacos,Eureka
服务远程调用 Dobbo协议 Feign(Http协议) Dobbo协议,Feign(Http协议)
配置中心 SpringCloudConfig SpringCloudConfig,Nacos
服务网关 SpringCloudGateway,zuul SpringCloudGateway,zuul
服务监控和保护 dubbo-admin,功能弱 Hystrix Sentinel

总结:SpringCloudAlibaba 兼容了前面两种

企业需求

1. SpringCloud + Feign:

  • 使用SpringCould技术栈

  • 服务接口采用Restful风格

  • 服务调用采用Feign方式

2. SpringCloudAlibaba + Feign

  • 使用SpringCloudAlibaba技术栈

  • 服务接口采用Restful风格

  • 服务调用采用Feign方式

3. SpringCloudAlibaba + Dubbo

  • 使用SpringCloudAlibaba技术栈

  • 服务接口采用Dubbo协议标准

  • 服务调用采用Dubbo方式

4. Dubbo 原始模式

  • 基于Dubbo老旧技术体系

  • 服务接口采用Dubbo协议标准

  • 服务调用采用Dubbo方式

SpringCloud

  • SpringCloud是目前国内使用最广泛的微服务架构

  • SpringCloud集成了各种微服务功能组件,并基于SpringClould实现了这些组件的自动装配,相比于原生的,更好用。

  • 服务注册发现

  • 服务远程调用

  • 服务链路监控

    1. Zipkin,Sleuth

  • 统一配置管理

  • 统一网关路由

  • 流控,降级,保护

SpringCloud和SpringBoot的版本兼容:有这个问题,

服务拆分和远程调用

  • 不同的微服务,不要重复开发相同的业务

  • 微服务数据独立,不要访问其它微服务的数据库

  • 微服务可以将自己的业务暴露为接口,供其他微服务调用

微服务的远程调用

  • 基于RestTemplate发起的http请求实现远程调用

  • http请求做远程调用是和语言无关的调用,只要知道对方的ip,端口,接口路径,请求参数即可。

消费者和提供者

  • 消费者:调用服务的服务(调用接口)

  • 提供者:服务被调用的服务(提供接口)

  • 提供者和消费者角色是相对的。一个服务既可以是消费者也可以是提供者

服务调用出现的问题

  • 服务消费者该如何获取服务提供者的地址信息?

    1. 服务提供者启动时向eureka注册自己的信息

    2. eureka保存这些信息

    3. 消费者根据服务名称向eureka拉取提供者信息

  • 如果有多个服务提供者,消费者该如何选择?

    1. 服务消费者会根据负载均衡算法,从服务列表中挑选一个

  • 消费者如何得知服务提供者的健康状态?

    1. 服务提供者会每隔30秒向EurekaServer发送心跳请求,报告健康状态

    2. eureka会更新记录服务列表信息,心跳不正常会被剔除

    3. 消费者就可以拉取到最新的信息。

服务之间的远程调用

  • 1.在配置类添加bean: RestTemplate

package cn.itcast.order;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {

   public static void main(String[] args) {
       SpringApplication.run(OrderApplication.class, args);
  }
   //注册RestTemplate,用于远程调用
   @Bean
   public RestTemplate restTemplate(){
       return new RestTemplate();
  }
}
  • 2.

package cn.itcast.order.service;

import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class OrderService {

  @Autowired
  private OrderMapper orderMapper;

  @Autowired
  private RestTemplate restTemplate;

  public Order queryOrderById(Long orderId) {
      // 1.查询订单
      Order order = orderMapper.findById(orderId);
      //定义请求url
      String url = "http://localhost:8081/user/"+order.getUserId();
      //发送Http请求,实现远程调用
      User user = restTemplate.getForObject(url,User.class);
      //将User封装到Order
      order.setUser(user);
      // 4.返回
      return order;
  }
}

 

Eureka 注册中心

  • 在Eureka架构中,微服务角色有两类:

    1. EurekaServer:服务端,注册中心

      • 记录服务信息

      • 心跳监控

    2. EurekaClient:客户端

      • Provider: 服务提供者,例:user-service

        • 注册自己的信息到EurekaService

        • 每隔30秒向EurekaService发送心跳

      • consumer:服务消费者,例order-server

        • 根据服务名称从EurekaService拉取服务列表

        • 基于服务列表做负载均衡,选中一个微服务后发起远程调用。

EurekaServer搭建

  • pom.xml

    <dependencies>
      <!--eureka服务端-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
      </dependency>
  </dependencies>
  • 2.编写启动类,开启注解@EnableEurekaServer

package cn.itcast.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
   public static void main(String[] args) {
       SpringApplication.run(EurekaApplication.class,args);
  }
}
  • application.yml

server:
port: 10087 # 服务端口
spring:
application:
  name: eurekaserver # eureka的服务名称
eureka:
client:
  service-url:  # eureka的地址信息,自己也是服务
    defaultZone: http://127.0.0.1:10087/eureka

EurekaClient服务搭建

  • 引入eureka-client依赖

       <!--eureka客户端依赖-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>

 

  • 在application.yml中配置eureka的地址

#服务名称
spring:
application:
  name: userservice
eureka:
client:
  service-url:  # eureka的地址信息
    defaultZone: http://127.0.0.1:10087/eureka

总结

  • 1.搭建EurekaServer

    • 引入eureka-server依赖

    • 添加@EnableEurekaServer

    • 在application.yml中配置eureka地址

  • 服务注册

    • 引入eureka-client依赖

    • 在application.yml中配置eureka地址

  • 服务发现

    • 引入eureka-client依赖

    • 在application.yml中配置eureka地址

    • 给RestTemplate添加@LoadBalanced注解

          //注册RestTemplate,用于远程调用
         @Bean
         @LoadBalanced//负载均衡
         public RestTemplate restTemplate(){
             return new RestTemplate();
        }

      }

       

    • 用服务提供者的服务名称远程调用

      package cn.itcast.order.service;

      import cn.itcast.order.mapper.OrderMapper;
      import cn.itcast.order.pojo.Order;
      import cn.itcast.order.pojo.User;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.http.ResponseEntity;
      import org.springframework.stereotype.Service;
      import org.springframework.web.client.RestTemplate;

      @Service
      public class OrderService {

         @Autowired
         private OrderMapper orderMapper;

         @Autowired
         private RestTemplate restTemplate;

         public Order queryOrderById(Long orderId) {
             // 1.查询订单
             Order order = orderMapper.findById(orderId);
             //定义请求url
      //       String url = "http://localhost:8081/user/"+order.getUserId();
             //换成服务地址
             String url = "http://userservice/user/"+order.getUserId();
             //发送Http请求,实现远程调用
             User user = restTemplate.getForObject(url,User.class);
             //将User封装到Order
             order.setUser(user);
             // 4.返回
             return order;
        }
      }

       

Ribbon 负载均衡

 

  • 负载均衡流程

    屏幕截图 2022-07-11 110444

屏幕截图 2022-07-11 111412

  • IRule:有轮询,有随机...

  • 屏幕截图 2022-07-11 115426

  • 配置负载均衡Ribbon策略

    //第一种
    package cn.itcast.order;

    @MapperScan("cn.itcast.order.mapper")
    @SpringBootApplication
    public class OrderApplication {

       public static void main(String[] args) {
           SpringApplication.run(OrderApplication.class, args);
      }
       //注册RestTemplate,用于远程调用
       @Bean
       @LoadBalanced//负载均衡
       public RestTemplate restTemplate(){
           return new RestTemplate();
      }
       //全局配置Ribbon策略
       @Bean
       public IRule randomRule(){
           return new RandomRule();
      }
       
    }
    # 第二种 yml
    # 只针对这一个userservice服务
    userservice:
    ribbon:
      NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则,随机的

饥饿加载

  • Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。

  • 饥饿加载,会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载。

  • # 配置饥饿加载
    ribbon:
    eager-load:
      enabled: true #开启饥饿加载
      clients: #这是个数组
        - userserver  #指定对哪个服务进行饥饿加载

总结

  • Ribbon负载均衡规则

    • 规则接口是IRule

    • 默认实现的是ZoneAvoidanceRule,根据Zone选择服务列表

  • 负载均衡自定义方式

    • 代码方式:配置灵活,修改时需要打包重新发布

    • 配置方式:直观,方便,无需重新打包发布,但是无法做全局配置

  • 饥饿加载

    • 开启饥饿加载

    • 指定饥饿加载的微服务名称

Nacos

  • 这个比Eureka 更好一些:注册和发现

  • 官网:nacos.io

  • 在父工程导入

               //nacos
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                  <version>2.2.5.RELEASE</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
  • 在子工程中加入依赖

         //将Eureka的客户端依赖注释掉
        <!-- nacos客户端依赖包 -->
          <dependency>
              <groupId>com.alibaba.cloud</groupId>
              <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
          </dependency>

     

  • 更改yml

      # 这个是Nacos的配置地址端口号8848 ,替换掉了Eureka
    spring:
    cloud:
      nacos:
        server-addr: localhost:8848  

     

总结

  • Nacos 服务搭建

    1. 下载安装包

    2. 解压

    3. 在bin目录下运行指令:

      startup.cmd -m standalone

       

  • Nacos 服务注册或者发现

    1. 引入nacos.discovery依赖

       <!-- Nacos客户端依赖包 -->
             <dependency>
                 <groupId>com.alibaba.cloud</groupId>
                 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
             </dependency>

       

    2. 配置nacos地址spring.cloud.nacos.server-addr

        #这个是Nacos的配置 ,替换掉了Eureka
      spring:
      cloud:
        nacos:
          server-addr: localhost:8848

       

     

Nacos 服务分级模型

  • Nacos服务分级存储模型

    1. 一级服务 userservice

    2. 二级是集群 ,HZ 或者 SH

    3. 三级是实例,例如HZ机房的某台部署了userservice的服务器

    4. 屏幕截图 2022-07-11 173814

  • 如何设置实例的集群属性

    1. 修改yml文件

      spring:
      cloud:
        nacos:
          server-addr: localhost:8848
           # 就是这个
          discovery:
            cluster-name: SH #HZ代指杭州集群 SH代指上海集群

       

Nacos 负载均衡策略

总结:
  • 优先选择同集群服务实例表

  • 本地集群找不到提供者,才去其它集群寻找,并且会报警告,A cross-cluster call occurs跨集群警告

  • 确定了可用实例列表后,在采用随机负载均衡挑选实例

spring: 
cloud:
  nacos:
    server-addr: localhost:8848  #nacos的服务地址
     #配置优先选择本地集群
    discovery:
      cluster-name: HZ


userservice:
ribbon:
   #NacosRule 优先选择本地集群,在本地集群中是随机选择
  NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule  #Nacos负载均衡规则

 

Nacos 的权重设置

  • Nacos控制台可以设置实例的权重值,0~1之间

  • 同集群内的多个实例,权重越高被访问的频率越高

  • 权重设置为0则完全不会被访问。

Nacos环境隔离

  • namespace用来做环境隔离

  • 每个namespace都有唯一的id

  • 不同namespace下的服务不可见

spring:
cloud:
  nacos:
    server-addr: localhost:8848  #nacos的服务地址
    discovery:
      cluster-name: HZ   #配置优先选择本地集群
      namespace: ae981e13-7da1-4247-96ce-e58b05265bfe  #配置的是命名空间的ID
      ephemeral: false   #配置为非临时实例

 

Nacos & Eureka 区别

  • 共同点:

    1. 都支持服务注册和拉取

    2. 都支持服务提供者心跳方式做健康检测

     

  • 区别:

    1. Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式

    2. 临时实例心跳不正常会被踢除,非临时实例不会被剔除

    3. Nacos支持服务列表变更的消息推送,服务列表更新更及时

    4. Nacos集群默认采用AP方式,data的可靠性,当集群中存在非临时实例时,采用CP模式,更注重data的可靠性和一致性;Eureka采用AP模式,但不支持切换。