二、Spring Cloud基础入门

二、Spring Cloud基础入门

学习目标

通过本文的学习,您将可以:

  • 了解Spring Boot概念和Spring Cloud概念
  • 掌握Spring Boot和Spring Cloud的区别
  • 了解Spring Cloud常用组件
  • 学会开发一个Spring Cloud程序
  • 了解服务间的通信方式

第一章 Spring Boot的介绍

在明白了微服务是什么的基础上,我们接下来看一下到底什么是Spring Boot? Spring Boot跟微服务又有些什么联系?让我们带着这些问题进入Spring Boot的内容学习。

1.1 什么是Spring Boot
  • 开发团队:是由Pivotal团队提供的全新框架。

  • 设计目标:简化新Spring应用的初始搭建以及开发过程。

    • Spring Boot框架使用了特定的方式来进行配置,使开发人员不再需要定义样板化的配置。

      image-20211108171933988

    • 简单来说:Spring Boot整合了许多优秀的框架,不需要用户手动的去写一堆xml配置 ,再进行配置。

  • 首先我们看一下什么是Spring Boot。

  • Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

  • Spring Boot相当于Java开发中使用的Maven。

  • Spring Boot的四个核心:

    1. 自动配置
    2. 起步依赖
    3. 命令行界面
    4. Actuator监控
  • Spring Boot将很多魔法带入了Spring应用程序的开发之中,其中最重要的是以下四个核心。

    • 自动配置:针对很多Spring应用程序常见的应用功能,Spring Boot能自动提供相关配置,对于开发人员还需要添加相应jar包,不再需要复杂的xml配置就可以实现功能。

    • 起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库。开发人员不再需要把依赖一个个添加进来,只需要添加对应的starter包依赖就可以解决复杂的依赖问题。

    • 命令行界面:这是Spring Boot的可选特性,借此你只需写代码就能完成完整的应用程序,无需传统项目构建,可以在命令行界面快速构建应用。

    • Actuator监控:让你能够深入运行中的Spring Boot应用程序,一探究竟。添加actuator依赖后,通过在actuator包中预定义好的url,可以很快速的获取应用的运行信息,比如通过 “/autoconfig”可以查看自动配置的使用情况,“/health” 查看应用健康指标,“/env” 查看所有环境变量。

1.2 开发第一个Spring Boot应用程序
  • 开发的基本流程和步骤:

    image-20211108172952981

  • 接下来我将带领大家开发第一个Spring Boot的应用程序

1.2.1 环境准备
  • 环境准备:

    • JDK1.8

    • Maven版本:3.3.9

    • 修改setting配置为国内下载镜像

    • Maven编码过程中需要下载依赖,需要网络环境

    • Eclipse或者IDEA

      • 字符集编码:UTF-8
  • 在真正开发Spring Boot项目之前我们要确保我们的环境是一致,以免中途发生一些意想不到的问题;

    • 首先我们是用的是jdk1.8版本,使用Eclipse或者IDEA作为开发工具(我们演示的使用采用 Eclipse Oxygen 作为开发工具),目前新版的开发工具中已经集成了Maven,我们只需要修 改Maven的Setting配置文件(主要是修改本地仓库位置,国内下载镜像)。
    • 默认情况下Maven的下载地址为国外的站点,依赖包的下载非常缓慢。当然由于要下载相关的依赖包,所以需要有网络环境
    • 使用Eclipse或者IDEA时建议大家把编码都改为UTF-8,以避免一些乱码问题。
1.2.2创建Maven工程并添加依赖
  • 步骤一:
    • 使用IDEA创建Maven项目工程
      • file -> new -> project -> Maven

        image-20211109164143370

      • 详细信息image-20211109164356217

  • 项目创建成功后结构如下图,图中的最后一个文件pom.xml就是maven的核心配置文件,后面我们大量的依赖配置都需要在这个配置文件中进行。image-20211109164751372
  • 步骤二:

    • 修改项目根目录下的pom.xml配置文件,添加SpringBoot依赖

    • 在pom.xml配置文件中的dependencies节点下添加依赖,添加内容如下:

      1
      2
      3
      4
      5
      6
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>1.5.11.RELEASE</version>
      </dependency>

  • 在pom.xml配置文件中添加Spring Boot的依赖,这里我们统一Spring Boot的依赖版本为 1.5.11.RLEASE。

1.2.3 开发主程序(启动类)
  • 步骤三:

    • 开发主程序:
      • 在src/main/java中新建包com.test
      • 在com.test包中新建类FirstDemoApplication

    image-20211109175944160

  • 在src/main/java中新建包,com.test(这里包的名字大家可以随便建,能识别即可);

  • 编写主程序:

    • 编写FirstDemoApplication 类代码:

      1
      2
      3
      4
      5
      6
      7
      8
      @SpringBootApplication
      public class FirstDemoApplication {

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

  • 开发程序的启动类FirstDemoApplication ;此类主要用于Spring Boot应用程序的启动,程序的入口就是这里了;在运行FirstDemoApplication 类时会进入main方法的调用,然后再main方法中启动了SpringApplication;

  • 此处注意一定要添加@SpringBootApplication注解

  • 详细代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.test;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    @SpringBootApplication
    public class FirstDemoApplication {

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

1.2.4 开发Controller
  • 步骤四:

    • 在FirstDemoApplication 类项目包下新建HelloController类:

      1
      2
      3
      4
      5
      6
      7
      8
      @RestController
      public class HelloController {

      @RequestMapping("/hello")
      public String hello(){
      return "hello world";
      }
      }
  • 开发Controller程序,用于实现业务方法。

  • 详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.test;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

@RequestMapping("/hello")
public String hello(){
return "hello world";
}
}
1.2.5 启动主程序
  • 步骤五:

    • 运行FirstDemoApplication 类,控制台打印如下内容表示启动成功

      image-20211109183820550

  • 运行FirstDemoApplication 类;在IDEA中使用鼠标右键-》RUN ‘FirstDemoApplication ’就可以 启动SpringBoot应用程序。这里的基本配置,以及启动方法在后面会大量用到,所以大家一定要掌握。

    image-20211109184142951

1.2.6 浏览器访问测试
1.2.7 项目完整结构
  • 项目完整截图如下:

    image-20211109184510193

  • 项目的完整结构如上图,Spring Boot项目的创建跟普通maven项目创建是一致的,然后添加 Spring Boot依赖,启动类,业务Controller就完成了我们第一个Spring Boot项目的创建。步骤很简单,大家一定要掌握。

1.3 Spring Boot的示例
  • 接收客户端参数

  • Spring Boot配置文件

  • FirstDemo项目如何接收前端传递的参数呢?

  • 我们都知道tomcat默认的端口是8080,如果我们8080端口被占用了,我们如何修改Spring Boot项目的端口呢?

  • 接下来我将以上面2个案例为大家解决参数接收,自动重启,配置文件中修改端口问题。

  • 注意后面的2个案例都是以FirstDemo作为基础

1.3.1 接收客户端参数
  • 接收客户端参数:
    • 在FirstDemo项目中的HelloController类中新增一个处理前端参数的方法
1
2
3
4
@RequestMapping("/hello/{name}")
public String hello(@PathVariable String name){
return "hello " + name;
}
  • 如果需要接受浏览器传递的参数,在使用Spring Boot项目我们很容易就可以解决这个问题;

    • 路径传参,如上面代码我们使用的是路径传参数,其中“{name}”就是参数;在 @RequestMapping中定义访问路径以及参数后,我们需要方法参数中添加 “@PathVariable String name”,这里注意“@PathVariable String name”中的name要 跟“@RequestMapping(“/hello/{name}”)”中的name一致。
  • 上面的代码演示了从浏览器接收参数并回显到浏览器界面。

  • 浏览器访问测试:

image-20211110110025158

1.3.2 Spring Boot配置文件
  • SpringBoot配置文件默认可以放到以下目录中,可以实现自动读取:

    • 项目的resources目录下

      image-20211110111709413

  • 在介绍修改应用端口之前,我们先看一下Spring Boot应用的配置文件应该放在哪里?如上图位置。

  • 如果tomcat默认的8080端口被占用,我们只需要在上面的任意一个配置文件中添加 “server.port: 要修改的端口号” 就可以修改Spring Boot应用的启动端口,当然这里也支持 properties配置文件,不过我们建议统一都使用yaml文件作为配置文件,其中yaml的具体语法可以参考腾讯云的YAML 格式介绍文档: https://cloud.tencent.com/document/product/649/17925;

  • 如果要修改启动端口,我们只需要在上述配置路径下任意一个 application.yml配置文件中添加配置代码:

    1
    2
    server:
    port: 8000
  • 添加配置代码后,重启应用,就可以通过8000端口访问应用了。

    image-20211110112404165

第二章 Spring Cloud的介绍

在掌握了Spring Boot的基本使用后,接下来我们进入Spring Cloud的内容学习;

什么是Spring Cloud?

Spring Boot和Spring Cloud又有什么关系呢?

带着这些问题我们继续Spring Cloud的内容学习。

2.1 什么是Spring Cloud
  • 什么是Spring Cloud?

    • Spring Cloud是基于Spring Boot实现微服务的框架,包含多个子项目的整体方案。
    • 与其他微服务框架的最明显区别是只能使用Java语言。
  • Spring Cloud的核心

    • 服务的注册与发现

      • 服务提供者:是指服务的被调用方

      • 服务消费者:是指服务的调用方

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线)

  • Spring Cloud是基于Spring Boot 提供的一套微服务解决方案

  • Spring Cloud除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。

  • Spring CLoud利用Spring Boot的开发便利性巧妙的简化了分布式系统基础设施的开发,Spring Cloud为开发人员提供了快速构建分布式系统的一些工具。

  • Spring Cloud的核心是服务注册与发现,后面我们在使用整套Spring Cloud的功能时,都需要先把开发好的服务注册到服务注册中心。

    image-20211110113346246

  • 上图中就是Spring Cloud的基本组件架构图,从上图我们也可以看出来,Spring Cloud包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等。它们都可以用SpringBoot的开发风格做到一键启动和部署。

2.2 Spring Boot和Spring Cloud的区别

image-20211110113448465

上图是Spring官网的关于Spring Boot的介绍图,在了解了Spirng Cloud的基本概念后我们再看这幅图,相信通过这幅图大家应该能明白Spirng Boot和Spring Cloud的基本区别了。

image-20211110113702359

  • Spring boot 是 Spring 的一套快速配置脚手架,可以基于spring boot 快速开发单个微服务; Spring Cloud是一个基于Spring Boot实现的云应用开发工具;

  • Spring boot专注于快速、方便集成的单个个体,Spring Cloud是关注全局的服务治理框架;

  • Spring boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置, Spring Cloud很大的一部分是基于Spring boot来实现。

  • Spring boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring boot, 属于依赖的关系。

2.3 Spring Cloud的常用组件

image-20211110114127171

  • 在介绍Spring Cloud 常用组价之前,首先要介绍一下Netflix ,Netflix 是一个很伟大的公司,在 Spring Cloud项目中占着重要的作用,Netflix公司提供了包括Eureka、Hystrix、Zuul、 Archaius等在内的很多组件,在微服务架构中至关重要,Spring在Netflix的基础上,封装了一系列的组件,命名为:Spring Cloud Eureka、Spring Cloud Hystrix、Spring Cloud Zuul等, 下边对各个组件进行分别介绍:

    • Spring Cloud Eureka
      • 服务注册中心,可以把开发的接口注册到Eureka服务注册中心,所有服务间调用都通过服务的形式访问,并不通过http直连。构成Eureka体系的包括:服务注册中心、服务提供者、 服务消费者
    • Spring Cloud Ribbon
      • Spring Cloud Eureka描述了服务如何进行注册,注册到哪里,服务消费者如何获取服务生产者的服务信息,但是Eureka只是维护了服务生产者、注册中心、服务消费者三者之间的关系,真正的服务消费者调用服务生产者提供的数据是通过Spring Cloud Ribbon 来实现的。
    • Spring Cloud Feign
      • 如果使用过Ribbon调用服务的话,就可以感受到使用Ribbon的方式还是有一些复杂,因 此Spring Cloud Feign应运而生。Spring Cloud Feign 是一个声明web服务客户端,这使得编写Web服务客户端更容易,使用Feign只需要创建一个接口并对它进行注解。 Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign。简单的可以理解为: Spring Cloud Feign 的出现使得Eureka和Ribbon的使用更为简单。
    • Spring Cloud Hystrix
    • 断路器,保险丝;例如:当有一个服务出现了故障,而服务的调用方不知道服务出现故障, 若此时调用放的请求不断的增加,最后就会等待出现故障的依赖方相应形成任务的积压, 最终导致自身服务的瘫痪。Spring Cloud Hystrix正是为了解决这种情况的,防止对某一故障服务持续进行访问
    • Spring Cloud Config
      • 对于微服务还不是很多的时候,各种服务的配置管理起来还相对简单,但是当成百上千的微服务节点起来的时候,服务配置的管理变得会复杂起来。分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。
      • 配置服务的内存中(即本地),也支持放在远程Git仓库中。在Cpring Cloud Config 组件中,分两个角色,一是Config Server,二是Config Client。Config Server用于配置属性的存储,存储的位置可以为Git仓库、SVN仓库、本地文件等,Config Client用于服务属性的读取
    • Spring Cloud Zuul
      • zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门
    • Spring Cloud Bus
      • 我们如果要去更新所有微服务的配置,在不重启的情况下去更新配置,只能依靠spring cloud config了,但是,是我们要一个服务一个服务的发送post请求,这其实还是非常麻烦的。但是通过Bus消息总线,只需要在SpringCloud Config Server端发出refresh,就可以触发所有微服务更新了。
2.3.1 Spring Cloud Eureka
  • 什么是Spring Cloud Eureka?

    • 一个基于rest服务的服务治理组件。
    • 实现了云端负载均衡和中间层服务器的故障转移。
    • 由两个组件组成
      • Eureka服务器
      • Eureka客户端

    image-20211110115103901

  • Spring Cloud Eureka:

    • 一个基于rest服务的服务治理组件。由两个组件组成:Eureka服务器和Eureka客户端。Eureka服 务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮 询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它 提供基于流量、资源利用率以及出错状态的加权负载均衡。
  • 上图中描述了:

    1. 三台Eureka服务注册中心构成的服务注册中心的主从复制集群;

    2. 然后服务提供者向注册中心进行注册、续约、下线服务等;

    3. 服务消费者向Eureka注册中心拉去服务列表并维护在本地(这也是客户端发现模式的机制体现!);

    4. 然后服务消费者根据从Eureka服务注册中心获取的服务列表选取一个服务提供者进行消费服务。

2.3.2 Spring Cloud Ribbon
  • 什么是Spring Cloud Ribbon?

    • Spring Cloud Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于 Netflix Ribbon实现的。
    • 不需要独立部署,几乎存在于每个微服务的基础设施中
    • 主要提供客户端的软件负载均衡算法,因为负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一
  • 在2.3.1中提到了服务消费者是将服务从注册中心获取服务生产者的服务列表并维护在本地的,这种客户端发现模式的方式是服务消费者选择合适的节点进行访问服务生产者提供的数据,这种选择合适节点的过程就是Spring Cloud Ribbon完成的。

  • 完善的配置选项,比如连接超时、重试、重试算法等。Ribbon内置可插拔、可定制的负载均衡组 件。下面是用到的一些负载均衡策略:

    • 简单轮询负载均衡
    • 加权响应时间负载均衡
    • 区域感知轮询负载均衡
    • 随机负载均衡
2.3.3 Spring Cloud Feign
  • 什么是Spring Cloud Feign?
    • Spring Cloud Feign 是一个声明web服务客户端,这使得编写Web服务客户端更容易 。
    • 完全代理HTTP请求,只需要像调用方法一样调用它就可以完成服务请求。
    • Spring Cloud Feign 的出现使得Eureka和Ribbon的使用更为简单
  • Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign 提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。
  • 而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关 处理。Feign整合了Ribbon和Hystrix(关于Hystrix我们后面再讲),可以让我们不再需要显式地使用这两个组件
2.3.4 Spring Cloud Hystrix
  • 什么是Spring Cloud Hystrix?
    • 容错管理工具,旨在通过控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
      • 也被称之为断路器,Hystrix提供了熔断、隔离、Fallback、cache、监控等功能,能够在一 个、或多个依赖同时出现问题时保证系统依然可用。
  • 断路器可以防止一个应用程序多次试图执行一个操作,即很可能失败,允许它继续而不等待故障恢复或者浪费 CPU 周期,而它确定该故障是持久的。断路器模式也使应用程序能够检测故障是否已经解决。如果问题似乎已经得到纠正,应用程序可以尝试调用操作。
  • 断路器增加了稳定性和灵活性,以一个系统,提供稳定性,而系统从故障中恢复,并尽量减少此 故障的对性能的影响。它可以帮助快速地拒绝对一个操作,即很可能失败,而不是等待操作超时 (或者不返回)的请求,以保持系统的响应时间。
2.3.5 Spring Cloud Config
  • 什么是Spring Cloud Config?

    • Spring Cloud Config是分布式配置中心,可以让你把配置放到远程服务器,目前支持本地存储、Git以及Subversion
    • 在Spring Cloud Config 组件中,分两个角色:
      • Config Server:用于配置属性的存储,存储的位置可以为Git仓库、SVN仓库、本地文件等
      • Config Client:用于服务属性的读取。
  • 对于微服务还不是很多的时候,各种服务的配置管理起来还相对简单,但是当成百上千的微服务节点起来的时候,服务配置的管理变得会复杂起来。

  • 在分布式系统中,每一个功能模块都能拆分成一个独立的服务,一次请求的完成,可能会调用很多个服务协调来完成,为了方便服务配置文件统一管理,更易于部署、维护,所以就需要分布式配置中心组件了,在spring cloud中,有分布式配置中心组件spring cloud config,它支持配置文件放在在配置服务的内存中,也支持放在远程Git仓库里。引入spring cloud config后,我们的外部配置文件就可以集中放置在一个git仓库里,再新建一个config server,用来管理所有的配置文件,维护的时候需要更改配置时,只需要在本地更改后,推送到远程仓库,所有的服务实例都可以通过config server来获取配置文件,这时每个服务实例就相当于配置服务的客户端config client,为了保证系统的稳定,配置服务端config server可以进行集群部署,即使某一个实例,因为某种原因不能提供服务,也还有其他的实例保证服务的继续提供服务。

  • Spring Cloud Config 组件的二个角色:

    image-20211110120519832

  • 在Spring Cloud Config 组件中,分两个角色,一是Config Server,二是Config Client。如上图所示。

2.3.6 Spring Cloud Zuul
  • 什么是Spring Cloud Zuul ?
    • Spring Cloud Zuul是服务网关,边缘服务工具
    • 提供服务路由、均衡负载功能之外,它还具备了权限控制等功能;
    • 为微服务架构提供了前门保护的作用;
    • 将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性;
  • Zuul 是 Netflix 开源的微服务网关,Spring Cloud 对 Zuul 进行了整合和增强。在 SpringCloud 体系中,Zuul 担任着网关的角色,对发送到服务端的请求进行一些预处理,比如 安全验证、动态路由、负载分配等
  • Zuul 的核心是 Filters,根据执行时期分为以下几类:
    • PRE:这种过滤器在请求被路由之前调用
    • ROUTING:这种过滤器将请求路由到微服务
    • POST:这种过滤器在路由到微服务以后执行
    • ERROR:在其他阶段发生错误时执行该过滤器

image-20211110120917045

  • 如上图所示,Zuul 相当于是设备和 Web 网站后端所有请求的前门。
2.3.7 Spring Cloud Bus
  • 什么是Spring Cloud Bus ?

    • Spring Cloud Bus 是事件、消息总线用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
  • 我们如果要去更新所有微服务的配置,在不重启的情况下去更新配置,只能依靠spring cloud config了,但是,是我们要一个服务一个服务的发送post请求,这其实还是非常麻烦的。但是通 过Bus消息总线,只需要在SpringCloud Config Server端发出refresh,就可以触发所有微服务更新了。

  • Spring Cloud Bus官方意义:消息总线。

  • 当然动态更新服务配置只是消息总线的一个用处,还有很多其他用处。

    image-20211110121104828

  • 如上图就演示了联合Spring Cloud Config 动态更新服务配置。

  1. 提交代码触发post给客户端C发送bus/refresh
  2. 客户端C接收到请求从Server端更新配置并且发送给Spring Cloud Bus
  3. Spring Cloud bus接到消息并通知给其它客户端
  4. 其它客户端接收到通知,请求Server端获取最新配置
  5. 全部客户端均获取到最新的配置

第三章 开发一个Spring Cloud程序

  • 在掌握了Spring Boot的基本使用,以及Spring Cloud的基本概念、组件后我们进行Spirng Cloud的代码编写,接下来我将一步步带领大家完成第一个Spring Cloud应用程序。

  • 这里可以再回顾一下Spring Boot项目的创建,接下来的项目创建都会基于Spring Boot创建。

3.1 开发第一个Spring Cloud程序
  • 开发第一个Spring Cloud应用程序(涉及服务注册中心)

  • 包含以下步骤:

    image-20211110121432065

  • 前面我们学习了Spring Cloud的常用组件,此处第一个Spirng Cloud应用程序,只涉及服务提供者,服务消费者,服务注册中心;服务消费者中会涉及到Ribbon的使用。

  • 环境准备:

  • JDK1.8

  • Maven版本:3.3.9

    • 修改setting配置为国内下载镜像
    • Maven编码过程中需要下载依赖,需要网络环境
  • Eclipse或者IDEA

    • 字符集编码:UTF-8
  • 环境准备跟Spirng Boot项目一致。

3.1.1 开发eureka-server
  • 创建eureka-server项目:

    • 根据”开发第一个Spring Boot项目”创建eureka-server项目:

      image-20211110172527172

    • eureka-server项目的基本创建方式跟前面讲述的Spring Boot项目创建方式一致,这里就不重复了。

  • 添加dependencyManagement定义:

    • 在项目根目录下的pom.xml配置文件中添加dependencyManagement定义(放在 dependencies同级节点):

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <dependencyManagement>
      <dependencies>
      <!-- springboot 1.5.11对应的springcloud依赖管理 -->
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Dalston.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
      </dependency>
      </dependencies>
      </dependencyManagement>
  • 这里添加dependencyManagement主要是为了统一定义Spring Cloud的版本信息,注意这里并没有实际引入依赖,只是在后面定义依赖的时候不需要申明版本信息了。

  • 添加eureka依赖:

    • 在项目根目录下的pom.xml添加eureka的依赖配置信息,这里注意添加2个依赖:spring-cloud-starter-config和spring cloud-starter-eureka-server,注意位置要放在dependencies节点下。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-config</artifactId>
      </dependency>

      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka-server</artifactId>
      </dependency>

  • 在启动类,添加注解:

    • 在启动类EurekaServerApplication上添加@EnableEurekaServer注解;

      1
      2
      3
      4
      5
      6
      7
      @EnableEurekaServer
      @SpringBootApplication
      public class EurekaServerApplication {
      public static void main(String[] args) {
      SpringApplication.run(EurekaServerApplication.class, args);
      }
      }
  • 启动类的创建方式跟Spring Boot应用程序是一样,实际上这里就是一个Spirng Boot应用程序;

  • 注意一定要添加@EnableEurekaServer注解,表明当前应用是一个eureka服务注册中心,后面 其它应用程序需要注册到此中心。

  • 添加eureka配置文件:

    • 在resources目录下新建配置文件application.yml,并添加如下配置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      server:
      port: 8000

      eureka:
      client:
      register-with-eureka: false #表示是否将自己注册到Eureka Server,默认为true。
      fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true
      service-url:
      defaultZone: http://localhost:${server.port}/eureka/
    • server.port: 8000这里是把应用的启动端口改为了8000

    • eureka.client.register-with-eureka: fase:表示是否将自己注册到Eureka Server,默认为 true。

    • eureka.client.fetch-registry: false:表示是否从Eureka Server获取注册信息,默认为true

    • eureka.client.service-url.defaultZone: http://localhost:${server.port}/eureka/ 这里是定义了 服务注册中心的地址,其中${server.port}就是前面定义的server.port: 8000配置,时间上这段配 置就是eureka.client.service-url.defaultZone: http://localhost:8000/eureka/;这段配置在其 它应用程序的配置中都需要加上,指明了服务注册中心的地址。

  • 浏览器访问测试:

  • eureka-server项目的完整pom文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.keepdive</groupId>
    <artifactId>eureka-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.5.11.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
    </dependencies>

    <dependencyManagement>
    <dependencies>
    <!-- springboot 1.5.11对应的springcloud依赖管理 -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Dalston.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>


    </project>
3.1.2 开发eureka-provider
  • 创建eureka-provider项目

    • 根据Spring Boot项目创建方式创建eureka-provider项目

    image-20211110182142953

    • 项目创建方式如eureka-server,这里是创建服务提供者,需要把当前服务提供者注册到上一小节中我们创建的eureka-server中。
  • 添加dependencyManagement定义和eureka依赖:

    • eureka-provider项目完整依赖;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>

      <groupId>com.keepdive</groupId>
      <artifactId>eureka-provider</artifactId>
      <version>1.0-SNAPSHOT</version>

      <properties>
      <maven.compiler.source>8</maven.compiler.source>
      <maven.compiler.target>8</maven.compiler.target>
      </properties>


      <dependencies>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>1.5.11.RELEASE</version>
      </dependency>

      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-config</artifactId>
      </dependency>

      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
      </dependency>
      </dependencies>

      <dependencyManagement>
      <dependencies>
      <!-- springboot 1.5.11对应的springcloud依赖管理 -->
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Dalston.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
      </dependency>
      </dependencies>
      </dependencyManagement>

      </project>
  • 在启动类,添加注解

    • 在启动类EurekaProviderApplication上添加@EnableEurekaClient, @EnableDiscoveryClient注解。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      @EnableEurekaClient
      @EnableDiscoveryClient
      @SpringBootApplication
      public class EurekaProviderApplication {

      public static void main(String[] args) {
      SpringApplication.run(EurekaProviderApplication.class, args);
      }
      }
  • 启动类的创建跟eureka-server一致。

  • @EnableEurekaClient表明当前应用是一个eureka客户端程序

  • @EnableDiscoveryClient表明当前应用可以被eureka服务注册中心发现

  • 开发eureka-provider

    • 在启动类EurekaProviderApplication相同包目录下新建 HelloController类:

      1
      2
      3
      4
      5
      6
      7
      8
      9

      @RestController
      public class HelloController {

      @RequestMapping("/hello/{name}")
      public String hello(@PathVariable("name") String name) {
      return "provider: hello " + name;
      }
      }
    • @RestController表明当前Controller类的所有接口都是rest方式,直接返回数据,不返回视图(页面),方法上不需要再添加ResponseBody注解。

  • 添加配置文件:

3.1.3 开发eureka-consumer

开发eureka-consumer项目:

  • 根据前面第一个Spring Boot项目案例创建eureka-consumer项目

    image-20211118153215147

  • 跟eureka-server的项目创建方式一致

  • 添加dependencyManagement定义和eureka依赖

  • eureka-consumer项目的完整依赖如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.keepdive</groupId>
    <artifactId>eureka-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.5.11.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    </dependencies>

    <dependencyManagement>
    <dependencies>
    <!-- springboot 1.5.11对应的springcloud依赖管理 -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Dalston.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>

    </project>
  • 创建启动类:

    • 在启动类EurekaConsumerApplication上添加注解,定义RestTemplate
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableEurekaClient
    public class EurekaConsumerApplication {

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

    @Bean
    @LoadBalanced
    RestTemplate getRestTemplate(){
    return new RestTemplate();
    }

    }
    • 这里有些代码、注解和eureka-server不一致
    • @EnableDiscoveryClient @EnableEurekaClient注解一样跟eureka-provider一致,都是为了注册当前服务到eureka服务注册中心
    • getRrestTemplate是为了注入RestTemplate到spirng容器,这样其它类再使用就可以直接用注解的形式使用,在业务controller中会涉及到
    • @Bean 注入bean到spring容器
    • @LoadBalanced实现客户端负载均衡
  • 添加业务Controller:

    • 在启动类EurekaProviderApplication包路径下新建HelloConsumerController类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @RestController
    public class HelloConsumerController {

    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/consumer/helle/{name}")
    public String helloConsumer(@PathVariable("name") String name){
    return restTemplate.getForObject("http://eureka-provider/hello" + name, String.class);
    }
    }
    • 消费者业务Controller,主要是为了消费eureka-provider应用中提供的服务
    • 这里采用的是路径传参数,在前面的Spring Boot传参示例中有说明
    • 这里业务controller的意思是,当客户端访问/consumer/hello/{name}这个连接时,会通过restTemplate访问eureka-provider服务中hello接口,简单来说就是消费eureka-provider服务
    • @Autowired表示RestTemplate 为自动注入,bean的定义就是启动类中注入到了spring容器,所以这里可以通过spring容器自动注入。
    • resttemplate会在后面的服务通信方式中介绍
  • 修改配置文件:

    • 修改resources目录下配置文件application.yml,并添加如下配置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      server:
      port: 8002
      spring:
      application:
      name: eureka-consumer
      eureka:
      client:
      service-url:
      defaultZone: http://localhost:8000/eureka/
    • server.port: 8002 应用启动端口为8002

    • spring.application.name: eureka-consumer应用服务名称为:eureka-consumer

    • eureka.client.service-url.defaultZone: http://localhost:8000/eureka/注册中心地址

第四章 服务间通信方式

前面我们讲解了spirng Boot,Spring Cloud的基本概念以及使用,那在上一章节中我们还留下一个问题,我们eureka-consumer是如何访问到我们的eureka-provider的?在代码中我们其实看到了是采用RestTemplate的方式访问的,那除了RestTemplate的方式外是否还有其他方式呢?

4.1 服务间通信方式
  • Spring Cloud 与Spring Boot 都采用Restful API 形式进行通信。

    • 同步方式: RestTemplate

    • 异步方式: AsyncRestTemplate

    • 声明式服务调用方式: Feign

  • 首先我们看一下同步,异步的思想:

  • 同步:同步的思想是:所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。

  • **异步(Async)**:将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。

  • 声明式服务调用方式:当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻。那么有没有更好的解决方案呢?答案是确定的有,Netflix已经为我们提供了一个框架:Feign。Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。

4.2 同步方式-RestTemplate
  • RestTemplate示例:

    • 在eureka-consumer项目的启动类EurekaConsumerApplication中注入RestTemplate到Spring容器中

      1
      2
      3
      4
      5
      @Bean
      @LoadBalanced
      RestTemplate getRestTemplate(){
      return new RestTemplate();
      }
    • 注意这里的代码还是基于eureka-consumer的案例,基于前面的代码做扩展,由于前面我们服务间通信就是采用的RestTemplate;所以这里不再重复操作截图,只贴代码。

    • 在HelloConsumerController中添加代码:

      1
      2
      3
      4
      5
      6
      7
      @Autowired
      RestTemplate restTemplate;

      @RequestMapping("/consumer/hello/{name}")
      public String helloConsumer(@PathVariable("name") String name){
      return restTemplate.getForObject("http://eureka-provider/hello/" + name, String.class);
      }
    • 如前面介绍eureka-consumer中HelloConsumerController类,这里代码跟前面一致。

    • 浏览器中访问:http://localhost:8002/consumer/hello/test

      image-20211118175601326

    • 访问结果如eureka-consumer案例中一致,修改url中参数”http://localhost:8002/consumer/hello/参数“,结果也不一致.

4.3 异步方式-AsyncRestTemplate
  • AsyncRestTemplate示例:

    • 在eureka-consumer项目的启动类EurekaConsumerApplication中注入AsyncRestTemplate到Spring容器中
    1
    2
    3
    4
    5
    @Bean
    @LoadBalanced
    AsyncRestTemplate asyncRestTemplate(){
    return new AsyncRestTemplate();
    }
    • 在启动类中注入AsyncRestTemplate 到spirng容器,作用跟RestTemplate一致,都是为了在Controller中可以自动注入使用
    • 各个注解内容跟RestTemplate注入一致
    • 在HelloConsumerController类中添加方法

      1
      2
      3
      4
      5
      6
      7
      8
      @Autowired
      AsyncRestTemplate asyncRestTemplate;

      @RequestMapping("consumer/async/hello/{name}")
      public String asyncRest(@PathVariable("name") String name) throws ExecutionException, InterruptedException {
      ListenableFuture<ResponseEntity<String>> future = asyncRestTemplate.getForEntity("http://eureka-provider/hello/" + name, String.class);
      return future.get().getBody();
      }
    • 通过@Autowired注入AsyncRestTemplate

    • 然后再asyncRest接口中直接使用asyncRestTemplate.getForEntity(“http://eureka-provider/hello/" + name, String.class);如此就是通过异步的方式调用服务提供者

    • 这里虽然是异步但是返回的内容还是同步的,其原因在于接口中返回代码“return future.get().getBody()”,其中用的是future.get(),这个方法会等待接口调用结束后再返回。

    • 可以通过在returen中直接返回,然后注册异步执行的回调方法,演示异步效果

    • 浏览器中访问:http://localhost:8002/consumer/async/hello/test

      image-20211118183638698

    • 服务消费者通过异步方式调用服务提供者过程

      image-20211118183542509

    • 图中演示了服务消费者通过异步方式调用服务提供者过程,异步的方式可以直接返回响应,也可以等待异步调用结束后再返回数据,跟ajax的基本思路一致。可以在不阻塞程序的情况下进行调用,也可以使用阻塞模式等待数据返回。

4.4 声明式服务调用方式-Feign
  • 添加Feign依赖:

    • 在eureka-consumer项目根目录下的pom.xml中添加依赖

      1
      2
      3
      4
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-feign</artifactId>
      </dependency>
  • 添加启动注解:

    • 在EurekaConsumerApplication启动类上添加注解@EnableFeignClients

      image-20211118184303699

    • 启动类上需要添加@EnableFeignClients注解,表明当前应用启用feign客户端;前提是要添加依赖,不然启动失败

  • 添加FeignClient:

    • 在EurekaConsumerApplication启动类所在包下新建HelloClient接口:
    1
    2
    3
    4
    5
    6
    @FeignClient(name = "eureka-provider")
    public interface HelloClient {

    @GetMapping("/hello/{name}")
    public String sayHello(@PathVariable("name")String name);
    }
    • @FeignClient(name=”eureka-provider”) 注解声明feign客户端,这里“eureka-provider”和要访问的服务名称一致,实际上就是要调用的服务名称
    • @GetMapping(“/hello/{name}”) 这里的链接地址要和要访问的服务的详细地址一致
    • 注意这里是一个接口,不是实现类,不需要实现,所以这通feign可以简化我们的服务调用
    • 注意:
      • public String sayHello(@PathVariable(“name”) String name);
      • 上面代码里一定要写成@PathVariable(“name”),不能写成@PathVariable
  • 添加访问入口:

    • 在HelloConsumerController类中添加访问接口代码:

      1
      2
      3
      4
      5
      6
      7
      @Autowired
      HelloClient helloClient;

      @RequestMapping("consumer/feign/hello/{name}")
      public String feign(@PathVariable String name){
      return helloClient.sayHello(name);
      }
      • 在HelloConsumerController添加服务访问入口
      • 通过@Autowired自动注入我们上一步骤中定义的feign客户端:HelloClient
      • 然后再在具体的feign方法中调用helloClient.sayHello(name);其效果跟我们采RestTemplate访问一致。
    • 浏览器访问测试:

4.5 Restful API设计规范
  • Restful API设计规范:
    • RESTful 的核心思想就是,客户端发出的数据操作指令都是”动词 + 宾语”的结构。比如, GET /articles这个命令, GET是动词, /articles是宾语。
    • 五种动词:GET(读取)、POST(新建)、PUT(更新)、PATCH(更新)、DELETE(删除)
    • 客户端的每一个请求,服务器都必须进行回应,包含HTTP状态码和数据。
  • 网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备……)。因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现”APIFirst”的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。
  • REST(Representational State Transfer)表述层状态转换,REST指的是一组架构约束条件和原则。如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力,更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深,但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。
  • RFC 3986定义了通用的URI语法:
  • URI = scheme “://” authority “/” path [ “?” query ] [ “#” fragment ]
  • scheme: 指底层用的协议,如http、https、ftp
  • host: 服务器的IP地址或者域名
  • port: 端口,http中默认80
  • path: 访问资源的路径,就是咱们各种web 框架中定义的route路由
  • query: 为发送给服务器的参数
  • fragment: 锚点,定位到页面的资源,锚点为资源id
  • Eg: http(s)://server.com/api-name/{version}/{domain}/{rest-convention}
  • 这里,{version}代表api的版本信息。{domain}是一个你可以用来定义任何技术的区域(例如:安全-允许指定的用户可以访问这个区域。)或者业务上的区域(例如:同样的功能在同一个前缀之下。)。{rest-convention} 代表这个域(domain)下,约定的rest接口集合。

思考题

  • Spring Boot 跟Spring Cloud的区别是什么?
  • Spring Cloud服务组件之间的通信方式有哪些?

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!