Spring 生态笔记 -- Spring Cloud

文章目录

  • 一、简介
  • 二、项目雏形
    • 2.1 创建父项目
    • 2.2 准备数据源
    • 2.3 支付模块
    • 2.4 订单模块
    • 2.5 公用接口模块
    • 2.6 效果演示
  • 三、Eureka
    • 3.1 服务注册
    • 3.2 Eureka 集群
    • 3.3 服务发现
    • 3.4 服务高可用
    • 3.5 细节调优
  • 四、Zookeeper ***
    • 4.1 集群环境搭建
    • 4.2 服务提供者
    • 4.3 消费者
    • 4.4 相比 Eureka
  • 五、Ribbon ***
  • 六、OpenFeign ***
    • 6.1 简介
    • 6.2 实例演示
  • 七、Hystrix
  • 八、Gateway ***
    • 8.1 简介
    • 8.2 实例演示
  • 八、Config
  • 九、Stream ***
    • 9.1 理论
    • 9.2 实战
  • 十、Sleuth
  • 十一、Spring Cloud Alibaba
  • 十二、Nacos 注册中心
    • 12.1 安装
    • 12.2 案例
      • 12.2.1 服务端
      • 12.2.2 客户端
    • 12.3 注册中心对比
  • 十三、Seata *

一、简介

Spring Cloud 是分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶。

  • 课程覆盖组件
    Spring 生态笔记 -- Spring Cloud_第1张图片

  • 版本选型

参考版本约束关系:https://start.spring.io/actuator/info

{
    "spring-cloud": {
        "Finchley.M2": "Spring Boot >=2.0.0.M3 and <2.0.0.M5",
        "Finchley.M3": "Spring Boot >=2.0.0.M5 and <=2.0.0.M5",
        "Finchley.M4": "Spring Boot >=2.0.0.M6 and <=2.0.0.M6",
        "Finchley.M5": "Spring Boot >=2.0.0.M7 and <=2.0.0.M7",
        "Finchley.M6": "Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1",
        "Finchley.M7": "Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2",
        "Finchley.M9": "Spring Boot >=2.0.0.RELEASE and <=2.0.0.RELEASE",
        "Finchley.RC1": "Spring Boot >=2.0.1.RELEASE and <2.0.2.RELEASE",
        "Finchley.RC2": "Spring Boot >=2.0.2.RELEASE and <2.0.3.RELEASE",
        "Finchley.SR4": "Spring Boot >=2.0.3.RELEASE and <2.0.999.BUILD-SNAPSHOT",
        "Finchley.BUILD-SNAPSHOT": "Spring Boot >=2.0.999.BUILD-SNAPSHOT and <2.1.0.M3",
        "Greenwich.M1": "Spring Boot >=2.1.0.M3 and <2.1.0.RELEASE",
        "Greenwich.SR6": "Spring Boot >=2.1.0.RELEASE and <2.1.999.BUILD-SNAPSHOT",
        "Greenwich.BUILD-SNAPSHOT": "Spring Boot >=2.1.999.BUILD-SNAPSHOT and <2.2.0.M4",
        "Hoxton.SR9": "Spring Boot >=2.2.0.M4 and <2.3.8.BUILD-SNAPSHOT",
        "Hoxton.BUILD-SNAPSHOT": "Spring Boot >=2.3.8.BUILD-SNAPSHOT and <2.4.0.M1",
        "2020.0.0-M3": "Spring Boot >=2.4.0.M1 and <=2.4.0.M1",
        "2020.0.0-M4": "Spring Boot >=2.4.0.M2 and <=2.4.0-M3",
        "2020.0.0": "Spring Boot >=2.4.0.M4 and <2.4.2-SNAPSHOT",
        "2020.0.1-SNAPSHOT": "Spring Boot >=2.4.2-SNAPSHOT"
    }
}

Spring 生态笔记 -- Spring Cloud_第2张图片

  • 组件迭代

Spring 生态笔记 -- Spring Cloud_第3张图片

二、项目雏形

  • 雏形介绍

初始项目仅包含两个微服务且逻辑简单:订单模块(Order)需要调用支付模块(Payment)模块的 restful 服务。

源码参考:spring-cloud-2021-get-start 提取码:rays

Spring 生态笔记 -- Spring Cloud_第4张图片

2.1 创建父项目

创建一个空的 Maven 父项目即可,它仅用作整个项目的版本依赖管理。

父项目本身以 spring-boot-starter-parent 作为自己的父项目。


<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<packaging>pompackaging>
	<groupId>com.simwor.springcloudgroupId>
	<artifactId>spring-cloud-2021artifactId>
	<version>0.0.1-SNAPSHOTversion>
	<name>spring-cloud-2021name>
	<description>Learning project for Spring Clouddescription>

	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.4.1version>
		<relativePath/> 
	parent>

	
	<modules>
		<module>payment-8001module>
        <module>order-80module>
        <module>apimodule>
    modules>

	<properties>
		<java.version>15java.version>
		<spring-cloud.version>2020.0.0spring-cloud.version>
		<mybatis-plus.version>3.4.1mybatis-plus.version>
	properties>

	
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloudgroupId>
				<artifactId>spring-cloud-dependenciesartifactId>
				<version>${spring-cloud.version}version>
				
				
				<type>pomtype>
				<scope>importscope>
			dependency>

			<dependency>
				<groupId>com.baomidougroupId>
				<artifactId>mybatis-plus-boot-starterartifactId>
				<version>${mybatis-plus.version}version>
			dependency>
		dependencies>
	dependencyManagement>

	<repositories>
		<repository>
			<id>spring-milestonesid>
			<name>Spring Milestonesname>
			<url>https://repo.spring.io/milestoneurl>
		repository>
	repositories>

project>

Spring 生态笔记 -- Spring Cloud_第5张图片

  1. 父项目只有一个 POM 文件,用作全局的版本依赖管理;
  2. 项目雏形中包含两个微服务: 订单模块(order-80)支付模块(payment-8001)
  3. 项目雏形中包含一个接口模块 api,用作提取各个微服务公用的类。

2.2 准备数据源

  • 准备容器
[root@simwor ~]# cd /opt/docker/mysql/compose/
[root@simwor compose]# cat docker-compose.yml 
version: "3.8"
services:
  
  spring-test-db:
    image: mysql
    container_name: spring-test-db
    ports:
      - 33060:3306
    environment:
      MYSQL_ROOT_PASSWORD: abcd1234..
      MYSQL_DATABASE: simwor
      MYSQL_USER: simwor
      MYSQL_PASSWORD: abcd1234..
    volumes:
      - /opt/docker/mysql/mysql33060:/var/lib/mysql
[root@simwor compose]# docker-compose up -d
  • 新建数据库表
[root@simwor ~]# mysql -h127.0.0.1 -P33060 -usimwor -p
Enter password: 

mysql> use simwor;
Database changed

mysql> CREATE TABLE payment(
    -> id bigint(20) NOT NULL AUTO_INCREMENT,
    -> serial varchar(200),
    -> PRIMARY KEY(id)
    -> );
Query OK, 0 rows affected, 1 warning (0.09 sec)

mysql> 
  • 准备数据
mysql> INSERT INTO payment(serial) values('wx20200110121543089');
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM payment;
+----+---------------------+
| id | serial              |
+----+---------------------+
|  1 | wx20200110121543089 |
+----+---------------------+
1 row in set (0.00 sec)

mysql> 

2.3 支付模块

支付模块直接与 MySQL 数据库进行交互,向外提供两个 restful 接口。

  • 配置文件
server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    url: jdbc:mysql://simwor.com:33060/simwor
    username: simwor
    password: abcd1234..
    driver-class-name: com.mysql.cj.jdbc.Driver
  • 控制器
package com.simwor.springcloud.payment;

import com.simwor.springcloud.entity.CommonResult;
import com.simwor.springcloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
public class PaymentController {
    @Autowired
    PaymentService paymentService;

    @GetMapping("/payment/get")
    public CommonResult<Payment> getPaymentById(@RequestParam("id") Long id) {
        Payment payment = paymentService.getById(id);

        if (payment == null)
            return new CommonResult<>(400, "fail", null);

        return new CommonResult<>(200, "success", payment);
    }

    @PostMapping("/payment/create")
    public CommonResult<Payment> createPayment(@RequestBody Payment payment) {
        boolean result = paymentService.save(payment);

        if (!result)
            return new CommonResult<>(400, "fail", null);

        return new CommonResult<>(200, "success", payment);
    }
}

2.4 订单模块

订单模块通过远程调用功能,间接通过支付模块的接口实现数据库的读写操作。

  • 配置文件
server:
  port: 80
spring:
  application:
    name: cloud-order-service
  • 远程调用模板
  1. RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,是一种简单便捷的访问 restful 服务模板类,是 Spring 提供的用于访问 Rest 服务的客户端模板工具集
  2. RestTemplate 使用前需要通过配置注册到容器中;
  3. 使用 RestTemplate 访问 restful 接口非常简单,只需准备三个参数 url requestMap ResponseBean.class ,分别代表 REST 请求地址、请求参数、HTTP响应转换被转换成的对象类型。
  • 远程调用模板注册
package com.simwor.springcloud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BeanRegistration {

    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
  • 控制器
package com.simwor.springcloud.order;

import com.simwor.springcloud.entity.CommonResult;
import com.simwor.springcloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@RestController
public class OrderController {
    public static final String PAYMENT_URL = "http://localhost:8001";

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/consumer/payment/get")
    public CommonResult<Payment> getPayment(@RequestParam("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get?id="+id, CommonResult.class);
    }

    @PostMapping("/consumer/payment/create")
    public CommonResult<Payment> createPayment(@RequestBody Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL+"/payment/create", payment, CommonResult.class);
    }
}

2.5 公用接口模块

共用接口模块仅作用为各个微服务共有部分的提取,如订单模块和支付模块都须使用类 PaymentCommonResult,将其抽取出来放在这里维护。

  • Payment.java
package com.simwor.springcloud.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}
  • CommentResult.java
package com.simwor.springcloud.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private int code;
    private String message;
    private T data;

    public CommonResult(int code, String message) {
        this(code, message,null);
    }
}
  • 打包部署

注:公用接口模块POM文件中不可包含 spring-boot-maven-plugin 打包插件。

Spring 生态笔记 -- Spring Cloud_第6张图片

  • 外部引用

订单模块 和 支付模块 必须生命引用 公用接口模块。

<dependency>
    <groupId>com.simwor.springcloudgroupId>
    <artifactId>apiartifactId>
    <version>${project.version}version>
dependency>

2.6 效果演示

  • Create Payment
    Spring 生态笔记 -- Spring Cloud_第7张图片
  • Get Payment
    Spring 生态笔记 -- Spring Cloud_第8张图片

三、Eureka

源码参考:spring-cloud-2021-eureka 提取码:rays

服务治理:在传统的 RPC 远程调用框架中,管理每个服务与服务之间的依赖关系比较复杂,服务治理可以简化依赖关系的管理工作,实现服务发现于注册、服务调用、负载均衡、容错等。

  • CS 设计架构
  1. Eureka Server 作为服务注册功能的服务器,它是服务注册中心;
  2. 微服务使用 Eureka 客户端连接到 Eureka Server 并维持心跳连接;
  3. 系统维护人员可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。
    Spring 生态笔记 -- Spring Cloud_第9张图片
  4. 微服务启动时,会将自己的服务器信息以别名方式注册到注册中心上;
  5. 消费者(通常也是微服务)以别名的方式去注册中心获取到服务提供者的实际服务通信地址,然后再在本地实现RPC调用;
  6. 注册中心管理每个服务与服务之间的依赖关系(服务治理概念)。
  • Eureka 组件
  1. Server 提供服务注册:各个微服务节点通过配置启动后,会在 Eureka Server 中进行注册;服务注册表中存储着所有可用服务节点的信息。
  2. Client 通过注册中心访问服务:是一个Java客户端,用于简化与 Eureka Server 的交互;客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器;在应用启动后,将会向 Eureka Server 发送心跳(默认周期为30秒);如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳信息,将会从服务注册表中将该服务节点移除(默认90秒)。

3.1 服务注册

  • 服务端
  1. 新建项目并额外添加依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
  1. 配置文件
server:
  port: 7001

eureka:
  instance:
    hostname: localhost # 服务端的实例名称
  client:
    # 服务端声明自己不需要注册、不需要检索服务。
    register-with-eureka: false
    fetch-registry: false
    service-url:
      # 客户端依赖的注册地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  1. 启用服务
package com.simwor.springcloud;

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

@SpringBootApplication
@EnableEurekaServer
public class Eureka7001Application {
    public static void main(String[] args) {
        SpringApplication.run(Eureka7001Application.class, args);
    }
}
  1. 前端测试

访问测试:http://localhost:7001/

Spring 生态笔记 -- Spring Cloud_第10张图片

  • 客户端 - 支付模块

以同样的步骤注册 订单模块。

  1. 新增依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
  1. 新增配置
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka
  1. 启用服务
package com.simwor.springcloud;

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

@SpringBootApplication
@EnableEurekaClient
public class Payment8001Application {
    public static void main(String[] args) {
        SpringApplication.run(Payment8001Application.class, args);
    }
}
  1. 前端验证

Spring 生态笔记 -- Spring Cloud_第11张图片

3.2 Eureka 集群

单节点Eureka故障会导致整个项目不可用,Eureka集群的各个节点 互相注册、相互守望 以达到高可用的目的。

  1. 创建多个 Eureka 节点,并修改配置文件

此步前需提前修改 hosts 文件。

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com # 服务端的实例名称
  client:
    # 服务端声明自己不需要注册、不需要检索服务。
    register-with-eureka: false
    fetch-registry: false
    service-url:
      # 客户端依赖的注册地址
      defaultZone: http://eureka7002.com:7002/eureka/
server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com # 服务端的实例名称
  client:
    # 服务端声明自己不需要注册、不需要检索服务。
    register-with-eureka: false
    fetch-registry: false
    service-url:
      # 客户端依赖的注册地址
      defaultZone: http://eureka7001.com:7001/eureka/
  1. 前端验证(DS Replicas)

Spring 生态笔记 -- Spring Cloud_第12张图片

3.3 服务发现

不再通过具体的地址而是通过服务提供者注册的别名来访问接口。

  1. 修改配置文件,指向集群地址
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  1. 修改接口访问地址
package com.simwor.springcloud.order;

import com.simwor.springcloud.entity.CommonResult;
import com.simwor.springcloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@RestController
public class OrderController {
//    public static final String PAYMENT_URL = "http://localhost:8001";
	//不再配置具体的地址,而是服务提供者的注册别名。
    public static final String PAYMENT_SRV = "http://CLOUD-PAYMENT-SERVICE";

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/consumer/payment/get")
    public CommonResult<Payment> getPayment(@RequestParam("id") Long id) {
        return restTemplate.getForObject(PAYMENT_SRV+"/payment/get?id="+id, CommonResult.class);
    }

    @PostMapping("/consumer/payment/create")
    public CommonResult<Payment> createPayment(@RequestBody Payment payment) {
        return restTemplate.postForObject(PAYMENT_SRV+"/payment/create", payment, CommonResult.class);
    }
}
  1. 告知 RestTemplate 接口地址访问方式的变化
package com.simwor.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BeanRegistration {

    @Bean
    @LoadBalanced   //告诉RestTemplate以服务发现的方式调用远程接口
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

3.4 服务高可用

多个微服务以相同的别名注册到 Eureka,Eureka 默认会采用轮询的方式调用该多个服务提供者(比如此例中的支付模块),且只要有一个服务提供者存活,服务就可正常调用。

目前这种结构并不优美(可能是因为部署在同一台主机上的缘故,一直会提示两个克隆模块的类冲突),待寻找其它合适的解决方案。

  1. 克隆支付模块,并修改端口地址

完全克隆 payment-8001payment-8002 ,再作模块适配。

# 修改模块的服务器端口号,避免端口冲突。
# 如果有多台服务器主机,这项操作通常是不必要的。
server:
  port: 8002
  1. 修改应用名
package com.simwor.springcloud;

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

@SpringBootApplication
@EnableEurekaClient
//修改主应用名 `Payment8002Application` 到 `Payment8002Application` 避免类名冲突。
public class Payment8002Application {
    public static void main(String[] args) {
        SpringApplication.run(Payment8002Application.class, args);
    }
}
  1. 前端验证

Spring 生态笔记 -- Spring Cloud_第13张图片

3.5 细节调优

  • 服务地址
  1. 默认显示 主机名 + 服务名 + 端口号

Spring 生态笔记 -- Spring Cloud_第14张图片

  1. 新增配置项
eureka:
  instance:
    instance-id: payment8001
  1. 当前显示:实例ID + 鼠标移入浏览器左下角显示主机IP

Spring 生态笔记 -- Spring Cloud_第15张图片

四、Zookeeper ***

源码参考:spring-cloud-2021-zookeeper 提取码:rays

4.1 集群环境搭建

  1. docker-compose.yml
[root@simwor ~]# cd /opt/docker/zookeeper/compose/
[root@simwor compose]# cat docker-compose.yml 
version: "3.8"
services:
  zoo1:
    image: zookeeper
    container_name: zoo1
    hostname: zoo1
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID: 1
      ZOO_SERVERS: server.1=0.0.0.0:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181

  zoo2:
    image: zookeeper
    container_name: zoo2
    hostname: zoo2
    ports:
      - 2182:2181
    environment:
      ZOO_MY_ID: 2
      ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=0.0.0.0:2888:3888;2181 server.3=zoo3:2888:3888;2181

  zoo3:
    image: zookeeper
    container_name: zoo3
    hostname: zoo3
    ports:
      - 2183:2181
    environment:
      ZOO_MY_ID: 3
      ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=0.0.0.0:2888:3888;2181

[root@simwor compose]# docker-compose up -d
  1. 检查集群启动状态
[root@simwor compose]# docker inspect zoo1 | grep IPA
                    "IPAddress": "192.168.64.4",
[root@simwor compose]# docker inspect zoo2 | grep IPA
                    "IPAddress": "192.168.64.3",
[root@simwor compose]# docker inspect zoo3 | grep IPA
                    "IPAddress": "192.168.64.2",
[root@simwor compose]# docker container exec -it zoo1 /bin/bash
root@zoo1:/apache-zookeeper-3.6.2-bin# bin/zkServer.sh status
Mode: follower
root@zoo2:/apache-zookeeper-3.6.2-bin# bin/zkServer.sh status
Mode: follower
root@zoo3:/apache-zookeeper-3.6.2-bin# bin/zkServer.sh status
Mode: leader
root@zoo3:/apache-zookeeper-3.6.2-bin# 

4.2 服务提供者

新创建模块 payment-8004 模拟服务提供者。

  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
dependency>
  1. 添加配置文件
server:
  port: 8004
spring:
  application:
    name: payment-8004
  cloud:
    zookeeper:
      connect-string: simwor.com:2181,simwor.com:2182,simwor.com:2183
  1. 主启动类
package com.simwor.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
//该注解表明将consul或者zookeeper作为服务注册中心
@EnableDiscoveryClient
public class Payment8004Application {
    public static void main(String[] args) {
        SpringApplication.run(Payment8004Application.class, args);
    }
}
  1. 编写测试服务
package com.simwor.springcloud.controller;

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

@RestController
public class PaymentController {

    @GetMapping
    public String getHelloInfo() {
        return "SpringCloud with Zookeeper!";
    }
}
  1. 启动应用,查看 Zookeeper 注册情况
root@zoo3:/apache-zookeeper-3.6.2-bin# bin/zkCli.sh
[zk: localhost:2181(CONNECTED) 0] ls /
[services, zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls /services 
[payment-8004]
[zk: localhost:2181(CONNECTED) 2] ls /services/payment-8004 
[05b304aa-e5dc-4909-91bd-fe44e4f12f61]
[zk: localhost:2181(CONNECTED) 4] get /services/payment-8004/05b304aa-e5dc-4909-91bd-fe44e4f12f61 
{
  "name": "payment-8004",
  "id": "05b304aa-e5dc-4909-91bd-fe44e4f12f61",
  "address": "localhost",
  "port": 8004,
  "sslPort": null,
  "payload": {
    "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
    "id": "payment-8004",
    "name": "payment-8004",
    "metadata": {
      "instance_status": "UP"
    }
  },
  "registrationTimeUTC": 1610348725315,
  "serviceType": "DYNAMIC",
  "uriSpec": {
    "parts": [
      {
        "value": "scheme",
        "variable": true
      },
      {
        "value": "://",
        "variable": false
      },
      {
        "value": "address",
        "variable": true
      },
      {
        "value": ":",
        "variable": false
      },
      {
        "value": "port",
        "variable": true
      }
    ]
  }
}
[zk: localhost:2181(CONNECTED) 5] 

4.3 消费者

新创建模块 order-81 模拟服务提供者。

  1. 添加依赖
  2. 编写配置文件
server:
  port: 81
spring:
  application:
    name: order-81
  cloud:
    zookeeper:
      connect-string: simwor.com:2181,simwor.com:2182,simwor.com:2183
  1. 注册 RCP 服务到容器
package com.simwor.springapplication.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BeanRegistration81 {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
  1. 主启动类
package com.simwor.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class Order81Application {
    public static void main(String[] args) {
        SpringApplication.run(Order81Application.class, args);
    }
}
  1. 通过 Zookeeper 调用远程服务
package com.simwor.springcloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class OrderZKController {
    public static final String PAYMENT_SRV="http://payment-8004";

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/consumer/payment/zk")
    public String getHelloInfoFromPaymentWithZookeeper() {
        return restTemplate.getForObject(PAYMENT_SRV+"/payment/zk", String.class);
    }
}
  1. 测试

Spring 生态笔记 -- Spring Cloud_第16张图片

4.4 相比 Eureka

CAP理论:一个分布式系统不能同时很好的满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个需求。

Spring 生态笔记 -- Spring Cloud_第17张图片

Eureka == AP,Zookeeper == CP

  1. CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大;
  2. CP - 满足一致性,分区容忍性的系统,通常性能不是特别高;
  3. AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低些。

五、Ribbon ***

Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡工具。

  • 与 Nginx 的区别
  1. Nginx 属于集中式 LB,而 Ribbon 属于进程内 LB;
  2. 集中式 LB:即在服务的消费方和提供方之间使用独立的 LB 设施(可以是硬件,如 F5,也可以是软件,如 nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
  3. 进程内LB:将 LB 逻辑集成到消费方,消费方从服务注册中心获知哪些地址可用,然后自己再从这些地址中选择一个合适的服务器;
  • 架构图

spring-cloud-starter-netflix-eureka-client 中默认包含了 Ribbon,所以当有多个实例同时注册到 Eureka 同个别名时,消费者调用服务时就是 自动负载均衡 的。

Spring 生态笔记 -- Spring Cloud_第18张图片

Ribbon 在工作时分成两步:

  1. 第一步选择 EurekaServer,它优先选择在同一个区域内负载较少的 server;
  2. 第二步再根据用户指定的策略(默认轮询),再从server中取到服务注册列表中的实际地址。

六、OpenFeign ***

Feign 是一个声明式的 WebService 客户端,它通过定义一个服务接口与远程服务接口对应然后在上面添加注解,调用服务时只需要调用该本地接口即可。

6.1 简介

  • Why Feign ?
  1. Feign 旨在使编写 Java Http 客户端变得更容易;
  2. RestTemplate 对 http 请求封装处理,形成一套模板化的调用方法;但在实际开发中,对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会 针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用
  3. Feign 来帮助定义和实现依赖服务接口的定义,只需创建一个接口并使用注解的方式,即可完成对服务提供方的接口绑定,简化了使用 Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量。
  • [Open] Feign ?
Feign OpenFeign
Feign 是 Spring Cloud 组件中的一个轻量级 RESTful 的 HTTP 服务客户端,其内置了 Ribbon 用来做客户端负载均衡;Feign 的使用方式是:使用 Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。 Open Feign 是 Spring Cloud 在 Feign 的基础上支持了 Spring MVC 的注解,如 @RequestMapping 等等;OpenFeign 的 @FeignClient 可以解析 SpringMVC @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其它服务

6.2 实例演示

源码参考:spring-cloud-2021-openfeign 提取码:rays

新创建一个模块 order-feign-82 用来演示采用 接口 + 注解 的方式调用远程服务。

  1. 依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
  1. 配置文件
server:
  port: 82

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  1. 主启动类
package com.simwor.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
public class Order82Application {
    public static void main(String[] args) {
        SpringApplication.run(Order82Application.class, args);
    }
}
  1. 接口本地映射
package com.simwor.springcloud.feign;

import com.simwor.springcloud.entity.CommonResult;
import com.simwor.springcloud.entity.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Component
@FeignClient("CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {

    @GetMapping("/payment/get")
    public CommonResult<Payment> getPaymentById(@RequestParam("id") Long id);
}
  1. 测试 Controller
package com.simwor.springcloud.controller;

import com.simwor.springcloud.entity.CommonResult;
import com.simwor.springcloud.entity.Payment;
import com.simwor.springcloud.feign.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PaymentFeignController {
    @Autowired
    private PaymentFeignService paymentFeignService;

    @GetMapping("/consumer/payment/get")
    public CommonResult<Payment> getPaymentById(@RequestParam("id") Long id) {
        return paymentFeignService.getPaymentById(id);
    }
}
  1. 前端验证

Spring 生态笔记 -- Spring Cloud_第19张图片

七、Hystrix

已停止更新:Hystrix is no longer in active development, and is currently in maintenance mode.

Hystrix 是一个用于处理分布式系统 延迟和容错 的开源库。在分布式系统中,许多依赖不可避免地会调用失败,比如超时、异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

  • 分布式服务存在的问题

服务雪崩:单个服务实例的失效,造成所有调用方的异常,如何解决对故障和延迟的隔离和管理?

Spring 生态笔记 -- Spring Cloud_第20张图片

  • 服务降级 fallback
  1. 服务器忙,不让调用方一直等待而是返回友好提示;
  2. 触发条件:程序运行异常、超时、服务熔断触发服务降级、线程池/信号量已满。
  • 服务熔断 break
  1. 类比保险丝达到最大服务访问后,直接拒绝访问,调用 服务降级 的方法返回友好提示。
  • 服务限流 flow-limit
  1. 禁止高并发等操作,限制同时访问服务的连接数量。

八、Gateway ***

Gateway 旨在为微服务架构提供一种简单、有效和统一的API路由管理方式,以及提供一些强大的过滤器功能,例如:熔断、限流、重试、安全、监控/指标等。

8.1 简介

  • 特性
  1. 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 进行构建;
  2. 动态路由:能够匹配任何请求属性;
  3. 可以对路由指定 Predicate(断言)和 Filter(过滤器);
  4. 集成 Hystrix 的断路器功能;
  5. 集成 Spring Cloud 服务发现功能;
  6. 请求限流功能、支持路径重写。
  • 三大核心概念
  1. Route(路由):是构建网关的基本模块,它由ID、目标URI和一系列的断言和过滤器组成,如果断言为 true 则匹配该路由;
  2. Predicate(断言):参考 java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由;
  3. Filter(过滤):指的是Spring框架中 GatewayFilter 的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
  • 请求流程图
    Spring 生态笔记 -- Spring Cloud_第21张图片
  1. 客户端向 Spring Cloud Gateway 发出请求,然后 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler;
  2. Handler 再通过指定的过滤器链来将请求发送到实际的服务,执行业务逻辑然后返回;
  3. 过滤器有多个,可能会在发送代理请求之前或之后执行业务逻辑;
  4. Pre Filter 可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
  5. Post Filter 可以做相应内容、响应头的修改、日志的输出、流量监控等。

8.2 实例演示

源码参考:spring-cloud-2021-gateway 提取码:rays

  • 添加依赖

需要同时添加 spring-cloud-starter-netflix-eureka-client,不用来注册服务而是用来发现服务。

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
  • 配置文件
  1. gateway.discovery.locator.enable=true:开启从注册中心动态创建路由的功能,利用微服务名进行路由
  2. gateway.routes.uri:哪个微服务下的接口需要开启路由功能
  3. gateway.routes.predicates:用来匹配请求特定的条件(更多官网参考)
server:
  port: 9527
spring:
  application:
    name: gateway-9527
  cloud:
    gateway:
      discovery:
        locator:
          locator: true  # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment   # 路由的ID
          uri: lb://CLOUD-PAYMENT-SERVICE  # 微服务注册名
          predicates:
            - Path=/** # 断言,路径相匹配的进行路由
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  • 主启动类
package com.simwor.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class Gateway9527App {
    public static void main(String[] args) {
        SpringApplication.run(Gateway9527App.class, args);
    }
}
  • 过滤器

更多官网实例参考:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

package com.simwor.springcloud.filter;

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

@Component
public class GatewayFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String idStr = exchange.getRequest().getQueryParams().getFirst("id");
        int id = Integer.parseInt(idStr);

        // 只有当query中包含“id”且值为小于10时才放行
        if(id > 10){
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // 过滤器的优先级,越小优先级越大
        return 0;
    }
}
  • 效果演示

Spring 生态笔记 -- Spring Cloud_第22张图片

八、Config

Spring Cloud Config 为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。

Spring 生态笔记 -- Spring Cloud_第23张图片

  • 架构
  1. Spring Cloud Config 分为服务端和客户端两部分;
  2. 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口;
  3. 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心加载配置信息。
  • bootstrap.yml
  1. Spring Cloud 会创建一个 “Bootstrap Context”,作为 Spring 应用 “Application Context” 的父上下文;
  2. 应用初始化的时候,“Bootstrap Context” 负责从外部源加载配置属性并解析配置,这两个上下文共享一个从外部获取的 “Environment”;
  3. “Bootstrap” 属性有高优先级,默认情况下不会被本地配置覆盖,其配置写在 “bootstrap.yml”。

九、Stream ***

Spring Cloud Stream 用于屏蔽消息中间件的差异,降低切换成本,统一消息的编程模型。

9.1 理论

  • 架构图

Spring 生态笔记 -- Spring Cloud_第24张图片

  1. 应用通过 inputs 管道接收消息中间件中存放的消息、 outputs 管道向消息中间件推送消息;
  2. 管道不直接与具体的消息中间件打交道,而是绑定在 Spring Cloud Stream 抽象出的 Binder 绑定器上;
  3. 绑定器可以屏蔽各个消息中间件的差异,为应用提供统一化的消息收发接口,作为消息收发的代理人;
  4. 最后,Middleware 就是具体的消息中间件实现了,不同的消息中间件需要引入不同的依赖包。
  • binder

应用中可以同时定义多个绑定器,不同的绑定器可以是不同的消息中间件。

spring.cloud.stream.binders:
  myrabbit1: # 自定义绑定器名称,用于与管道绑定
    type: rabbit
    environment:
      spring:
        rabbitmq:
          host: simwor.com
          port: 5672
          username: guest
          password: guest
  myrabbit2:
    ....
  mykafka:
    ....
  • 消息收发逻辑

Spring 生态笔记 -- Spring Cloud_第25张图片

  1. 消息收发逻辑可以写在方法(用 @Bean 标注)中,方法的返回类型标识着其是发送消息的Supplier 或是处理消息的 Function 或是接收消息的 Consumer;
  2. 用于收发消息的方法会生成 binding 绑定,如 send-out-0,其中 send 为方法名,out 标识其消息相对于应用的传输方向,0 指定着管道的序号;
  3. 管道序号一般都是0,复杂的返回类型如 Function, Flux> gather() 则会生成两条绑定 gather-in-0 and gather-in-1
  • binding

一条完整的绑定信息应包含:绑定名、消息存放的Topic名、绑定器。

spring:
  cloud:
    stream:
      function: # 哪些方法(@Bean)里面包含消息收发逻辑,在此列出其方法名。
        definition: sendMethod;processMethod;receiveMethod
      bindings:
        sendMethod-out-0: # 生成的绑定名
          destination: message # 消息中间件中存放消息的Topic名
          binder: myrabbit # 绑定器,对应具体的消息中间件
        processMethod-in-0:
          destination: message
          binder: myrabbit
        processMethod-out-0: # 处理消息包含两条绑定,消息哪里来,处理完哪里去
          destination: processed-message
          binder: myrabbit
        receiveMethod-in-0:
          destination: processed-message
          binder: myrabbit
        normalBinding: # 自定义的绑定名
          destination: some-topic
          binder: mykafka
  • 普通发送消息

如果消息是用户/事件驱动而不是 Stream 流式产生的,可以使用普通的方法发送消息。

@Autowired
private StreamBridge streamBridge;

@GetMapping("/rabbitmq/send")
public String sendMessage(@RequestParam("msg") String msg) {
	//"msg-out-0" : 自定义的绑定名
    streamBridge.send("msg-out-0", "Web Message From 8801 >> " + msg);
    return "result: success, msg: " + msg;
}

9.2 实战

源码参考:spring-cloud-2021-stream 提取码:rays

  • 功能简介
  1. 8801 APP 每秒或有前端事件驱动向消息中间件发送消息;
  2. 8802 APP 简单的将消息打印出来;
  3. 两个应用还会在原始消息上做一些处理。

Spring 生态笔记 -- Spring Cloud_第26张图片

  • 环境准备
[root@simwor compose]# pwd
/opt/docker/rabbitmq/compose
[root@simwor compose]# cat docker-compose.yml 
version: "3.8"
services:
  rabbitmq:
    image: rabbitmq
    container_name: rabbitmq
    ports:
      - 5672:5672
[root@simwor compose]# docker-compose up -d
  • 消息发送端应用
  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-stream-rabbitartifactId>
dependency>
  1. 逻辑类
package com.simwor.springcloud;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.function.Function;
import java.util.function.Supplier;

@SpringBootApplication
@Slf4j
@RestController
public class RabbiMQProducer8801Application {
    public static void main(String[] args) {
        SpringApplication.run(RabbiMQProducer8801Application.class, args);
    }

    // Stream 默认情况下每秒调用一次该方法
    @Bean
    public Supplier<String> sendData() {
        return () -> {
            String now = LocalDateTime.now().toString();
            log.info("Date info sent : " + now);
            return now;
        };
    }

    // 处理原始发送的消息
    @Bean
    public Function<String, String> processBeforeSendData() {
        return s -> "Send From 8801 >> " + s;
    }

    @Autowired
    private StreamBridge streamBridge;
    
    // 处理网页的消息发送请求
    @GetMapping("/rabbitmq/send")
    public String sendMessage(@RequestParam("msg") String msg) {
        streamBridge.send("msg-out-0", "Web Message From 8801 >> " + msg);
        log.info("Web Message From 8801 >> " + msg);
        return "result: success, msg: " + msg;
    }
}
  1. 配置文件
server:
  port: 8801

spring:
  # 该展示中就一个消息中间件,直接配置在 spring 下而不用在 spring.cloud.stream.binders 下
  rabbitmq:
    host: simwor.com
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      function:
        definition: sendData;processBeforeSendData
      bindings:
        sendData-out-0:
          destination: echos
          binder: rabbit  # 指向应用的 rabbitmq 配置
        processBeforeSendData-in-0:
          destination: echos
          binder: rabbit
        processBeforeSendData-out-0:
          destination: src-echos # 处理后将其存在另一个topic中
          binder: rabbit
        msg-out-0:
          destination: web-message
          binder: rabbit
  • 消息接收端应用
  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-stream-rabbitartifactId>
dependency>
  1. 逻辑类
package com.simwor.springcloud;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.function.Consumer;
import java.util.function.Function;

@SpringBootApplication
@Slf4j
public class RabbitMQConsumer8802Application {
    public static void main(String[] args) {
        SpringApplication.run(RabbitMQConsumer8802Application.class, args);
    }

    @Bean
    public Function<String, String> processBeforeSinkData() {
        return  s -> s + " >> Received At 8802";
    }

    @Bean
    public Consumer<String> sinkData() {
        return s -> log.info(s);
    }

    @Bean
    public Consumer<String> sinkWebMsg() {
        return s -> log.info(s + " >> Sink Web Message At 8802");
    }
}
  1. 配置文件
server:
  port: 8802

spring:
  rabbitmq:
    host: simwor.com
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      function:
        definition: processBeforeSinkData;sinkData;sinkWebMsg
      bindings:
        processBeforeSinkData-in-0:
          destination: src-echos
          binder: rabbit
        processBeforeSinkData-out-0:
          destination: dest-echos
          binder: rabbit
        sinkData-in-0:
          destination: dest-echos
          binder: rabbit
        sinkWebMsg-in-0:
          destination: web-message
          binder: rabbit
  • 效果展示
  1. 发送端每秒发送一条消息

Spring 生态笔记 -- Spring Cloud_第27张图片

  1. 接收端每秒接收一条消息

Spring 生态笔记 -- Spring Cloud_第28张图片

  1. 网页事件驱动消息发送

Spring 生态笔记 -- Spring Cloud_第29张图片

  1. 接收网页驱动消息

Spring 生态笔记 -- Spring Cloud_第30张图片

十、Sleuth

图形化展示微服务之间接口的调用链路图,方便追踪和解决问题。

十一、Spring Cloud Alibaba

Github 主页:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

  • 是什么?
  1. Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案;
  2. 此项目包含开发分布式应用微服务的必须组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务;
  3. 依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
  • 能做什么?
功能 说明
服务限流降级 默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
服务注册与发现 适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
分布式配置管理 支持分布式系统中的外部化配置,配置更改时自动刷新。
消息驱动能力 基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
分布式事务 使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
阿里云对象存储 阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
分布式任务调度 提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
阿里云短信服务 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
  • 包含哪些组件?
组件 说明
Sentinel 把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Nacos 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ 一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Dubbo Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata 阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Alibaba Cloud OSS 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
Alibaba Cloud SchedulerX 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
Alibaba Cloud SMS 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

十二、Nacos 注册中心

Naming Configuration Service 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台(注册中心+配置中心 = Nacos = Eureka + Config + Bus)。

12.1 安装

下载地址:https://github.com/alibaba/nacos/releases

第一次启动可能会报错,因为其会默认寻找 MySQL 连接;如果确实想 2021-01-18 22:07:01,261 INFO Nacos started successfully in stand alone mode. use embedded storage 使用Nacos本身存储,关闭窗口再次启动就可以了。

  1. 环境准备

初始化 SQL 目录:nacos-server-1.4.1\nacos\conf\nacos-mysql.sql

[root@simwor compose]# pwd
/opt/docker/mysql/compose
[root@simwor compose]# cat docker-compose.yml 
version: "3.8"
services:
  
  spring-test-db:
    image: mysql
    container_name: spring-test-db
    ports:
      - 33060:3306
    environment:
      MYSQL_ROOT_PASSWORD: abcd1234..
      MYSQL_DATABASE: simwor
      MYSQL_USER: simwor
      MYSQL_PASSWORD: abcd1234..
    volumes:
      - ../sql:/tmp/sql
[root@simwor compose]# ls ../sql
nacos-mysql.sql
[root@simwor compose]# docker exec -it spring-test-db /bin/bash
root@b74dc68a672e:/# mysql -usimwor -pabcd1234..
mysql> use nacos;
Database changed
mysql> source /tmp/sql/nacos-mysql.sql
  1. 修改数据库配置

Nacos 配置文件路径:nacos-server-1.4.1\nacos\conf\application.properties

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://simwor.com:33060/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=simwor
db.password.0=abcd1234..
  1. 启动
C:\Users\m1553\Desktop\nacos-server-1.4.1\nacos\bin>set JAVA_HOME=C:\Program Files\Java\jdk-15.0.1
C:\Users\m1553\Desktop\nacos-server-1.4.1\nacos\bin>startup.cmd -m standalone
  1. 前端验证

默认账号密码:nacos/nacos

Spring 生态笔记 -- Spring Cloud_第31张图片

12.2 案例

源码参考:spring-cloud-2021-nacos-discovery 提取码:rays

12.2.1 服务端

  1. 父项目依赖
<properties>
	<spring-cloud-alibaba.version>2.2.4.RELEASEspring-cloud-alibaba.version>
properties>

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>com.alibaba.cloudgroupId>
			<artifactId>spring-cloud-alibaba-dependenciesartifactId>
			<version>${spring-cloud-alibaba.version}version>
			<type>pomtype>
			<scope>importscope>
		dependency>
dependencyManagement>
  1. 子项目依赖
<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-actuatorartifactId>
dependency>
  1. 配置文件
server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # nacos 服务地址
management:
  endpoints:
    web:
      exposure:
        include: '*' # 以 web 方式暴露所有监控端点
  1. 主启动类
package com.simwor.springcloud.nacos;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class NacosPaymentProvider9001Application {
    public static void main(String[] args) {
        SpringApplication.run(NacosPaymentProvider9001Application.class, args);
    }

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/nacos/payment/get-server-port")
    public String getServerPort() {
        return "This micro-service's server port is " + serverPort;
    }
}
  1. 前端验证

Spring 生态笔记 -- Spring Cloud_第32张图片

12.2.2 客户端

  1. 配置文件

POM依赖与服务端完全相同

server:
  port: 83

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # nacos 服务地址
management:
  endpoints:
    web:
      exposure:
        include: '*' # 以 web 方式暴露所有监控端点
service-url:
  nacos-payment: http://nacos-payment-provider
  1. 主启动类
package com.simwor.springcloud.nacos;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class NacosOrder83Application {
    public static void main(String[] args) {
        SpringApplication.run(NacosOrder83Application.class, args);
    }

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

    @Autowired
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-payment}")
    public String serverUrl;

    @GetMapping("/nacos/order/invoke-payment")
    public String invokePayment() {
        return restTemplate.getForObject(serverUrl + "/nacos/payment/get-server-port", String.class);
    }

}

12.3 注册中心对比

  1. 功能概览
    Spring 生态笔记 -- Spring Cloud_第33张图片
  • 模式切换

C 是所有节点在同一时间看到的数据是一致的,而 A 的定义是所有的请求都会收到响应。

切换命令:curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'

  1. 如果不需要存储服务级别的信息且服务实例是通过 nacos-client 注册,并能够保持心跳上报,那就可以选择 AP 模式;
  2. Spring Cloud 和 Dubbo 都适用于 AP 模式,其为了服务的可用性而减弱了一致性,因此 AP 模式下只支持注册临时实例;
  3. 如果需要在服务级别编辑或者存储信息,那么 CP 是必须的,K8S 服务和 DNS 服务都适用于CP模式;
  4. CP 模式下支持注册持久化实例,集群以 Raft 协议运行,该模式下注册实例之前必须先注册服务,如果服务不存在则会返回错误。

十三、Seata *

Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

你可能感兴趣的:(spring,restful,spring,boot)