一、前言(环境搭建)

视频地址(bilibili):【尚硅谷SpringCloud框架开发教程(SpringCloudAlibaba微服务分布式架构丨Spring Cloud)】https://www.bilibili.com/video/BV18E411x7eT

GitHub代码地址https://github.com/zhangzhixi0305/SpringCloud-Study

软件资源地址https://pan.baidu.com/s/1jaFL7M4et8cCTjftc2UO5Q?pwd=8023

?SpringCloud学习_spring

版本选型:

SpringBoot2.x:

源码地址:https://github.com/spring-projects/spring-boot/releases/

SpringBoot2新特性:https://github.com/spring-projects/spring-boot/wiki/spring-Boot-2.0-Release-Notes

SpringCloud H版:

SpringCloud官方文档:https://spring.io/projects/spring-cloud#overview

SpringCloud中文文档:https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md

Spring Boot 与 Spring Cloud 兼容性查看:

官方文档查看:https://spring.io/projects/spring-cloud#overview

JSON对照:https://start.spring.io/actuator/info

开发用到的组件版本:

  • Cloud - Hoxton.SR1
  • Boot - 2.2.2.RELEASE
  • Cloud Alibaba - 2.1.0.RELEASE
  • Java - Java 8+
  • Maven - 3.5及以上
  • MySQL - 5.7及以上

二、初识模块搭建(了解微服务调用)

  以订单模块(provider)和支付模块(consumer)为例

2.1、父模块环境搭建

1、创建一个Maven项目

?SpringCloud学习_ci_02

?SpringCloud学习_SpringCloud_03

2、添加依赖 

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!--统一管理jar包版本-->
    <maven.compiler.target>1.8</maven.compiler.target>
    <junit.version>4.12</junit.version>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.16.18</lombok.version>
    <mysql.version>8.0.32</mysql.version>
    <druid.version>1.1.16</druid.version>
    <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    <cloud-api-common.version>1.0-SNAPSHOT</cloud-api-common.version>
</properties>

<!--子模块继承之后,提供作用:
    锁定版本+子modlue不用写groupId和version-->
<dependencyManagement>

    <dependencies>
        <!--公共模块-->
        <dependency>
            <groupId>com.zhixi</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${cloud-api-common.version}</version>
        </dependency>

        <!--spring boot 2.2.2-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.2.2.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--spring cloud Hoxton.SR1-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--spring cloud alibaba 2.1.0.RELEASE-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.1.0.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.spring.boot.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>
    </dependencies>
</dependencyManagement>

3、设置工程类型  

因为是个聚合项目,在父工程的pom文件的<package>标签中要设置成pom

<packaging>pom</packaging>

2.2、数据库搭建

MySQL中创建数据库:spring-cloud,执行以下SQL

?SpringCloud学习_ci_04

?SpringCloud学习_SpringCloud_05

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80015
 Source Host           : localhost:3306
 Source Schema         : db2019

 Target Server Type    : MySQL
 Target Server Version : 80015
 File Encoding         : 65001

 Date: 06/03/2020 10:19:42
*/

SET NAMES utf8mb4;
SET
FOREIGN_KEY_CHECKS = 0;
CREATE
database db2019;
USE
db2019;

-- ----------------------------
-- Table structure for payment
-- ----------------------------
DROP TABLE IF EXISTS `payment`;
CREATE TABLE `payment`
(
    `id`     bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `serial` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '支付流水号',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '支付表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of payment
-- ----------------------------
INSERT INTO `payment`
VALUES (31, '尚硅谷111');
INSERT INTO `payment`
VALUES (32, 'atguigu002');
INSERT INTO `payment`
VALUES (34, 'atguigu002');
INSERT INTO `payment`
VALUES (35, 'atguigu002');

SET
FOREIGN_KEY_CHECKS = 1;

--- seata  分布式事务
-- the table to store GlobalSession data
create
database seata;
USE
seata;
drop table if exists `global_table`;
create table `global_table`
(
    `xid`                       varchar(128) not null,
    `transaction_id`            bigint,
    `status`                    tinyint      not null,
    `application_id`            varchar(32),
    `transaction_service_group` varchar(32),
    `transaction_name`          varchar(128),
    `timeout`                   int,
    `begin_time`                bigint,
    `application_data`          varchar(2000),
    `gmt_create`                datetime,
    `gmt_modified`              datetime,
    primary key (`xid`),
    key                         `idx_gmt_modified_status` (`gmt_modified`, `status`),
    key                         `idx_transaction_id` (`transaction_id`)
);

-- the table to store BranchSession data
drop table if exists `branch_table`;
create table `branch_table`
(
    `branch_id`         bigint       not null,
    `xid`               varchar(128) not null,
    `transaction_id`    bigint,
    `resource_group_id` varchar(32),
    `resource_id`       varchar(256),
    `lock_key`          varchar(128),
    `branch_type`       varchar(8),
    `status`            tinyint,
    `client_id`         varchar(64),
    `application_data`  varchar(2000),
    `gmt_create`        datetime,
    `gmt_modified`      datetime,
    primary key (`branch_id`),
    key                 `idx_xid` (`xid`)
);

-- the table to store lock data
drop table if exists `lock_table`;
create table `lock_table`
(
    `row_key`        varchar(128) not null,
    `xid`            varchar(96),
    `transaction_id` long,
    `branch_id`      long,
    `resource_id`    varchar(256),
    `table_name`     varchar(32),
    `pk`             varchar(36),
    `gmt_create`     datetime,
    `gmt_modified`   datetime,
    primary key (`row_key`)
);

-- - seata_order
create
database IF NOT EXISTS seata_order ;
USE
seata_order;
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order`
(
    `int`        bigint(11) NOT NULL AUTO_INCREMENT,
    `user_id`    bigint(20) DEFAULT NULL COMMENT '用户id',
    `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
    `count`      int(11) DEFAULT NULL COMMENT '数量',
    `money`      decimal(11, 0) DEFAULT NULL COMMENT '金额',
    `status`     int(1) DEFAULT NULL COMMENT '订单状态:  0:创建中 1:已完结',
    PRIMARY KEY (`int`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '订单表' ROW_FORMAT = Dynamic;

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`
(
    `id`            bigint(20) NOT NULL AUTO_INCREMENT,
    `branch_id`     bigint(20) NOT NULL,
    `xid`           varchar(100) NOT NULL,
    `context`       varchar(128) NOT NULL,
    `rollback_info` longblob     NOT NULL,
    `log_status`    int(11) NOT NULL,
    `log_created`   datetime     NOT NULL,
    `log_modified`  datetime     NOT NULL,
    `ext`           varchar(100) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

create
database IF NOT EXISTS seata_storage;
USE
seata_storage;
DROP TABLE IF EXISTS `t_storage`;
CREATE TABLE `t_storage`
(
    `int`        bigint(11) NOT NULL AUTO_INCREMENT,
    `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
    `total`      int(11) DEFAULT NULL COMMENT '总库存',
    `used`       int(11) DEFAULT NULL COMMENT '已用库存',
    `residue`    int(11) DEFAULT NULL COMMENT '剩余库存',
    PRIMARY KEY (`int`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '库存' ROW_FORMAT = Dynamic;
INSERT INTO `t_storage`
VALUES (1, 1, 100, 0, 100);

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`
(
    `id`            bigint(20) NOT NULL AUTO_INCREMENT,
    `branch_id`     bigint(20) NOT NULL,
    `xid`           varchar(100) NOT NULL,
    `context`       varchar(128) NOT NULL,
    `rollback_info` longblob     NOT NULL,
    `log_status`    int(11) NOT NULL,
    `log_created`   datetime     NOT NULL,
    `log_modified`  datetime     NOT NULL,
    `ext`           varchar(100) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE
database IF NOT EXISTS seata_account;
USE
seata_account;
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account`
(
    `id`      bigint(11) NOT NULL COMMENT 'id',
    `user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
    `total`   decimal(10, 0) DEFAULT NULL COMMENT '总额度',
    `used`    decimal(10, 0) DEFAULT NULL COMMENT '已用余额',
    `residue` decimal(10, 0) DEFAULT NULL COMMENT '剩余可用额度',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '账户表' ROW_FORMAT = Dynamic;

INSERT INTO `t_account`
VALUES (1, 1, 1000, 0, 1000);

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`
(
    `id`            bigint(20) NOT NULL AUTO_INCREMENT,
    `branch_id`     bigint(20) NOT NULL,
    `xid`           varchar(100) NOT NULL,
    `context`       varchar(128) NOT NULL,
    `rollback_info` longblob     NOT NULL,
    `log_status`    int(11) NOT NULL,
    `log_created`   datetime     NOT NULL,
    `log_modified`  datetime     NOT NULL,
    `ext`           varchar(100) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

View Code

2.3、公共模块搭建

创建微服务模块套路:

  1. 建Module
  2. 改POM
  3. 写YML
  4. 主启动
  5. 业务类

1、建模块:cloud-api-common

?SpringCloud学习_SpringCloud_06

 2、改pom

  作为一个公共模块,抽取公共的部分,比如工具类、业务的统一返回值、POJO实体类等,其他微服务模块想要使用这些,直接引入公共模块的gav坐标即可

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.15</version>
    </dependency>
</dependencies>

3、写yml

公共模块不需要写yml

4、主启动

公共模块不需要有主启动类

5、POJO:com.zhixi.pojo.Payment

/**
 * @ClassName SpringCloudProviderPayment
 * @Author zhangzhixi
 * @Description 支付模块实体类
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}

6、统一返回数据对象:com/zhixi/result/CommonResult

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

/**
 * @ClassName SpringCloudProviderPayment
 * @Author zhangzhixi
 * @Description 统一返回结果类
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    /**
     * 返回结果码
     */
    private Integer code;
    /**
     * 返回结果信息
     */
    private String message;
    /**
     * 返回结果数据
     */
    private T data;


    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}

7、Maven进行构建即可

?SpringCloud学习_spring_07

2.3、订单支付模块(provider)

├─java
│  └─com
│      └─zhixi
│          │  SpringCloudProviderPayment8001.java
│          │
│          ├─controller
│          │      PaymentController.java
│          │
│          ├─dao
│          │      PaymentDao.java
│          │
│          └─service
│              │  PaymentService.java
│              │
│              └─impl
│                      PaymentServiceImpl.java
│
└─resources
    │  application.yml
    │  rebel.xml
    │
    └─mapper
            PaymentMapper.xml

1、新建名为:cloud-provider-payment-8001 的maven项目

2、改pom

<dependencies>
    <!--公共模块-->
    <dependency>
        <groupId>com.zhixi</groupId>
        <artifactId>cloud-api-common</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3、写yml:application.yml

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring-cloud?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: zhixi158
#mybatis配置
mybatis:
  mapperLocations: classpath:mapper/**/*.xml
  type-aliases-package: com.zhixi.pojo
  configuration:
    map-underscore-to-camel-case: true
#日志
logging:
  level:
    com.zhixi: debug

4、主启动:com.zhixi.SpringCloudProviderPayment8001

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @ClassName SpringCloudProviderPayment
 * @Author zhangzhixi
 * @Description 支付服务提供者
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@SpringBootApplication
@MapperScan("com.zhixi.dao")
public class SpringCloudProviderPayment8001 {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudProviderPayment8001.class, args);
    }
}

5、dao层:com.zhixi.dao.PaymentDao

import com.zhixi.pojo.Payment;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

/**
 * @ClassName SpringCloudProviderPayment
 * @Author zhangzhixi
 * @Description 支付模块dao层
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@Repository
public interface PaymentDao {
    /**
     * 新增
     *
     * @param payment 实体对象
     * @return 影响行数
     */
    public int create(Payment payment);

    /**
     * 根据id查询
     *
     * @param id 主键
     * @return 实体对象
     */
    public Payment getPaymentById(@Param("id") Long id);
}

6、service层:com.zhixi.service.PaymentService

import com.zhixi.pojo.Payment;
import org.apache.ibatis.annotations.Param;

/**
 * @ClassName SpringCloudProviderPayment
 * @Author zhangzhixi
 * @Description 支付模块service层
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
public interface PaymentService {
    /**
     * 新增
     *
     * @param payment 实体对象
     * @return 影响行数
     */
    public int create(Payment payment);

    /**
     * 根据id查询
     *
     * @param id 主键
     * @return 实体对象
     */
    public Payment getPaymentById(@Param("id") Long id);
}

7、Service实现类:com.zhixi.service.impl.PaymentServiceImpl

import com.zhixi.dao.PaymentDao;
import com.zhixi.pojo.Payment;
import com.zhixi.service.PaymentService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @ClassName PaymentServiceImpl
 * @Author zhangzhixi
 * @Description 支付模块service层实现类
 * @Date 2023-04-03 11:20
 * @Version 1.0
 */
@Service
public class PaymentServiceImpl implements PaymentService {

    @Resource
    private PaymentDao paymentDao;

    @Override
    public int create(Payment payment) {
        return paymentDao.create(payment);
    }

    @Override
    public Payment getPaymentById(Long id) {
        return paymentDao.getPaymentById(id);
    }
}

8、mapper:src/main/resources/mapper/PaymentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhixi.dao.PaymentDao">
    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into `spring-cloud`.payment(serial)
        values (#{serial});
    </insert>

    <resultMap id="BaseResultMap" type="payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <id column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        select *
        from `spring-cloud`.payment
        where id = #{id};
    </select>
</mapper>

9、Controller:com.zhixi.controller.PaymentController

import com.zhixi.pojo.Payment;
import com.zhixi.result.CommonResult;
import com.zhixi.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**
 * @ClassName PaymentController
 * @Author zhangzhixi
 * @Description 支付模块controller层
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@RestController
@Slf4j
@RequestMapping("/payment")
@SuppressWarnings("all")
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

    @PostMapping(value = "/create")
    public CommonResult create(@RequestBody Payment payment) {
        int result = paymentService.create(payment);
        log.info("*****插入结果:" + result);
        if (result > 0) {
            return new CommonResult(200, "插入数据库成功,插入的主键ID是: " + payment.getId(), result);
        } else {
            return new CommonResult(444, "插入数据库失败", null);
        }
    }

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

        if (payment != null) {
            return new CommonResult(200, "查询成功,端口是:  " + serverPort, payment);
        } else {
            return new CommonResult(444, "没有对应记录,查询ID: " + id, null);
        }
    }
}

10、测试功能

http://localhost:8001/payment/get/31

?SpringCloud学习_spring_08

 2.4、订单消费模块(consumer)

 1、建模块:cloud-consumer-order-80

2、改pom

<dependencies>
    <!--公共模块-->
    <dependency>
        <groupId>com.zhixi</groupId>
        <artifactId>cloud-api-common</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3、写yml

server:
  port: 80
spring:
  application:
    name: cloud-consumer-order
# 订单服务调用支付服务
order:
  payment:
    service:
      # 服务名
      url: http://localhost:8001

4、主启动:com.zhixi.controller.OrderController  

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

/**
 * @ClassName OrderMain80
 * @Author zhangzhixi
 * @Description 订单服务消费者
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@SpringBootApplication
/**
 * name: 指定要使用的Ribbon负载均衡策略的服务名称 configuration: 指定自定义的Ribbon负载均衡策略类
 */
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

5、业务类(注入RestTemplate):com.zhixi.config.RestTemplateConfig

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

/**
 * @ClassName RestTemplateConfig
 * @Author zhangzhixi
 * @Description RestTemplate配置类
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@Configuration
public class RestTemplateConfig {
    @Bean
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

6、Controller:com.zhixi.controller.OrderController

import com.zhixi.pojo.Payment;
import com.zhixi.result.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
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 javax.annotation.Resource;

/**
 * @ClassName OrderController
 * @Author zhangzhixi
 * @Description 订单控制器
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@Slf4j
@RestController
@RequestMapping("/consumer/payment")
@SuppressWarnings("all")
public class OrderController {

    @Value("${order.payment.service.url}")
    private String paymentUrl;

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/create")
    public CommonResult<Payment> create(Payment payment) {
        return restTemplate.postForObject(paymentUrl + "/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
        return restTemplate.getForObject(paymentUrl + "/payment/get/" + id, CommonResult.class);
    }
}

7、测试  

启动微服务:

  SpringCloudProviderPayment8001

  OrderMain80 

测试:

  localhost/consumer/payment/get/41

  localhost/consumer/payment/create

?SpringCloud学习_SpringCloud_09

?SpringCloud学习_Cloud_10

三、服务注册与发现

常见的注册中心异同点

?SpringCloud学习_ci_11

CAP:

  • C:Consistency (强一致性)
  • A:Availability (可用性)
  • P:Partition tolerance (分区容错性)

AP架构(Eureka)

  当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。

结论:违背了一致性C的要求,只满足可用性和分区容错,即AP

CP架构(ZooKeeper/Consul)

  当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性。

结论:违背了可用性A的要求,只满足一致性和分区容错,即CP。

3.1、Eureka

前面我们没有服务注册中心,也可以服务间调用,为什么还要服务注册?

当服务很多时,单靠代码手动管理是很麻烦的,需要一个公共组件,统一管理多服务,包括服务是否正常运行等……

Eureka用于服务注册,不过目前官网已经停止更新

3.1.1、什么是服务治理

  Spring Cloud封装了Netflix 公司开发的Eureka模块来实现服务治理

在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂。

所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

3.1.2、什么是服务注册与发现

  Eureka采用了CS的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。

  在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。

  在任何RPC远程框架中,都会有一个注册中心存放服务地址相关信息(接口地址)

?SpringCloud学习_spring_12

3.1.3、Eureka的两个组件:Eureka Server和Eureka Client

Eureka Server:提供服务注册服务

  各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

EurekaClient:通过注册中心进行访问

  它是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。

如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

3.1.4、Eureka服务端环境搭建

1、新建模块:cloud-eureka-server-7001

2、改pom

<dependencies>
    <!--eureka-server-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
    <dependency>
        <groupId>com.zhixi</groupId>
        <artifactId>cloud-api-common</artifactId>
    </dependency>
    <!--boot web actuator-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--一般通用配置-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
</dependencies>

3、写yml  

server:
  port: 7001
spring:
  application:
    name: cloud-eureka-server-7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

4、主启动

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

/**
 * @ClassName CloudEurekaServer7001
 * @Author zhangzhixi
 * @Description Eureka服务端启动类
 * @Date 2023-04-04 9:49
 * @Version 1.0
 */
@SpringBootApplication
@EnableEurekaServer
public class CloudEurekaServer7001 {
    public static void main(String[] args) {
        SpringApplication.run(CloudEurekaServer7001.class, args);
    }
}

3.1.5、Eureka服务端环境搭建(集群版本)

  当然如果是测试的话,也不是非必须搭建集群版本,这里只是演示,具体的还是按照自己笔记本实际情况来操作,搭建集群非必须

?SpringCloud学习_ci_13

为什么要搭建集群版本?

  问题:微服务RPC远程服务调用最核心的是什么(高可用,试想你的注册中心只有一个only one,万一它出故障了,会导致整个为服务环境不可用)

解决办法:

  搭建Eureka注册中心集群,实现负载均衡+故障容错。

重复3.1.4Eureka搭建步骤,分别创建Maven项目:

  cloud-eureka-server-7002

  cloud-eureka-server-7003

其他的不用动,只需要修改pom文件的:defaultZone,让Eureka互相守望即可。

1、修改本机hosts文件:为了模拟域名,更真实环境

  • 找到C:\Windows\System32\drivers\etc路径下的hosts文件,修改映射配置添加进hosts文件
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com

2、修改项目的yml

分别修改Eureka集群:cloud-eureka-server-7002、cloud-eureka-server-7003的yml文件:

7002

server:
  port: 7002
spring:
  application:
    name: cloud-eureka-server-7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/

7003  

server:
  port: 7003
spring:
  application:
    name: cloud-eureka-server-7003

eureka:
  instance:
    hostname: eureka7003.com #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

3、启动集群  

分别启动:cloud-eureka-server-7001、cloud-eureka-server-7002、cloud-eureka-server-7003项目

然后访问:

  http://eureka7001.com:7001/

  http://eureka7002.com:7002/

  http://eureka7003.com:7003/

其实有点掩耳盗铃了,用localhost:7001/localhost:7002/localhost:7003也是一样的~

?SpringCloud学习_ci_14

3.1.6、将订单生产者8001和消费者80注册进Eureka

cloud-provider-payment-8001:

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
  # 服务端的服务发现页面和健康检查地址
  instance:
    instance-id: payment8001
    # 显示IP地址
    prefer-ip-address: true
    #心跳检测与续约时间
    #开发时没置小些,保证服务关闭后注册中心能即使剔除服务
    #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
#    lease-renewal-interval-in-seconds: 2
    #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
#    lease-expiration-duration-in-seconds: 10

cloud-consumer-order-80:  

# Eureka客户端配置
eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka

?SpringCloud学习_SpringCloud_15

3.1.7、Eureka停更说明

https://github.com/Netflix/eureka/wiki

Eureka 2.0 (Discontinued)

The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.

Eureka 1.x is a core part of Netflix’s service discovery system and is still an active project.

?SpringCloud学习_Cloud_16

下面我们用ZooKeeper代替Eureka功能。

 3.2、Zookeeper

zookeeper是一个分布式协调工具,可以实现注册中心功能;

zookeeper服务器取代Eureka服务器,zk作为服务注册中心;

3.2.1、Zookeeper安装(Windows)

参考:javascript:void(0)

然后启动Zookeeper服务,如下:

?SpringCloud学习_spring_17

 

3.2.2、服务提供者注册进Zookeeper(支付服务)

1、建模块:cloud-provider-payment-zookeeper-8004

2、加pom

<dependencies>
    <!-- SpringBoot整合Web组件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--公共组件-->
    <dependency>
        <groupId>com.zhixi</groupId>
        <artifactId>cloud-api-common</artifactId>
    </dependency>
    <!-- SpringBoot整合zookeeper客户端 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        <!--先排除自带的zookeeper3.5.3 防止与3.4.9起冲突-->
        <exclusions>
            <exclusion>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--添加zookeeper3.4.9版本-->
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3、写yml 

#8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
  port: 8004

#服务别名----注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-payment-service
  cloud:
    zookeeper:
      connect-string: 127.0.0.1:2181

4、主启动

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

/**
 * @ClassName SpringCloudProviderPayment
 * @Author zhangzhixi
 * @Description 支付服务提供者
 * @Date 2023-04-03 10:39
 * @Version 1.0
 * @deprecated @EnableDiscoveryClient 该注解用于向使用consul或者zookeeper作为注册中心时注册服务
 */
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudProviderPayment8004 {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudProviderPayment8004.class, args);
    }
}

5、Controller

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/zk")
    public String paymentzk() {
        return "springcloud with zookeeper: " + serverPort + "\t" + UUID.randomUUID().toString();
    }
}

6、启动8004注册进zookeeper

浏览器验证:

  http://localhost:8004/payment/zk

ZK客户端验证:

  启动zkCli.cmd

[zk: localhost:2181(CONNECTED) 0] ls /
[services, zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls /services/cloud-payment-service
[f4712ac6-f48d-471a-a520-93f85911e9bb]
[zk: localhost:2181(CONNECTED) 3] get /services/cloud-payment-service/f4712ac6-f48d-471a-a520-93f85911e9bb
{"name":"cloud-payment-service","id":"f4712ac6-f48d-471a-a520-93f85911e9bb","address":"zhixi","port":8004,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"cloud-payment-service","metadata":{}},"registrationTimeUTC":1681964767710,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}

json格式化后:  

{
    "name": "cloud-payment-service",
    "id": "f4712ac6-f48d-471a-a520-93f85911e9bb",
    "address": "zhixi",
    "port": 8004,
    "sslPort": null,
    "payload": {
        "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
        "id": "application-1",
        "name": "cloud-payment-service",
        "metadata": {}
    },
    "registrationTimeUTC": 1681964767710,
    "serviceType": "DYNAMIC",
    "uriSpec": {
        "parts": [
            {
                "value": "scheme",
                "variable": true
            },
            {
                "value": "://",
                "variable": false
            },
            {
                "value": "address",
                "variable": true
            },
            {
                "value": ":",
                "variable": false
            },
            {
                "value": "port",
                "variable": true
            }
        ]
    }
}

3.2.3、订单服务注册进zookeeper

1、建模块:cloud-consumer-order-zookeeper-80

2、加pom

<dependencies>
    <!-- SpringBoot整合Web组件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--公共组件-->
    <dependency>
        <groupId>com.zhixi</groupId>
        <artifactId>cloud-api-common</artifactId>
    </dependency>
    <!-- SpringBoot整合zookeeper客户端 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        <!--先排除自带的zookeeper3.5.3 防止与3.4.9起冲突-->
        <exclusions>
            <exclusion>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--添加zookeeper3.4.9版本-->
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3、写yml 

server:
  port: 80

#服务别名----注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-consumer-order
  cloud:
    zookeeper:
      connect-string: 127.0.0.1:2181
      payment:
        service:
          #服务提供者,注册zookeeper到注册中心名称
          name: cloud-payment-service

4、主启动  

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

/**
 * @ClassName CloudConsumerOrderZookeeper80
 * @Author zhangzhixi
 * @Description
 * @Date 2023-04-06 17:45
 * @Version 1.0
 */
@SpringBootApplication
@EnableDiscoveryClient
public class CloudConsumerOrderZookeeper80 {
    public static void main(String[] args) {
        SpringApplication.run(CloudConsumerOrderZookeeper80.class, args);
    }
}

5、config:com.zhixi.config.CloudConsumerOrderConfig

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;

/**
 * @ClassName CloudConsumerOrderConfig
 * @Author zhangzhixi
 * @Description 消费者配置类
 * @Date 2023-04-06 17:46
 * @Version 1.0
 */
@Configuration
public class CloudConsumerOrderConfig {
    @Bean
    @LoadBalanced // 负载均衡
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

6、Controller:com.zhixi.controller.ConsumerController  

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @ClassName ConsumerController
 * @Author zhangzhixi
 * @Description
 * @Date 2023-04-06 17:47
 * @Version 1.0
 */
@RestController
@Slf4j
@RequestMapping("/consumer")
public class ConsumerController {

    @Value("${spring.cloud.zookeeper.payment.service.name}")
    private String paymentServiceName;

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/payment/zk")
    public String paymentInfo() {
        return restTemplate.getForObject("http://" + paymentServiceName + "/payment/zk", String.class);
    }
}

7、验证测试  

运行ZooKeeper服务端、cloud-provider-payment-zookeeper-8004、cloud-consumer-order-zookeeper-80

打开ZooKeeper客户端:

[zk: localhost:2181(CONNECTED) 0] ls /
[services, zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls /services
[cloud-consumer-order, cloud-payment-service]

8、访问测试

http://localhost/consumer/payment/zk

四、服务调用

4.1、Ribbon

4.1.1、Ribbon介绍

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

简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。

简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

LB负载均衡(Load Balance)是什么

  简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。

常见的负载均衡有软件Nginx,LVS,硬件F5等。

Ribbon本地负载均衡客户端VS Nginx服务端负载均衡区别

  Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。

集中式LB和进程内LB

集中式:

  即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;

进程内:

  将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。

Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。


 

一句话:负载均衡 + RestTemplate调用

4.1.2、Ribbon的负载均衡和Rest调用

1、结构说明

Ribbon其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和Eureka结合只是其中的一个实例。

?SpringCloud学习_ci_18

Ribbon在工作时分成两步:

  • 第一步先选择EurekaServer ,它优先选择在同一个区域内负载较少的server。
  • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。

其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。


 

在Eureka中集成了Ribbon,可以不引入Ribbon即可使用

?SpringCloud学习_ci_19

2、RestTemplate的使用

  • getForObject() / getForEntity() - GET请求方法
  • getForObject():返回对象为响应体中数据转化成的对象,基本上可以理解为Json。
  • getForEntity():返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等。
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPayment2(@PathVariable("id") Long id)
{
    ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);

    if(entity.getStatusCode().is2xxSuccessful()){
        return entity.getBody();//getForObject()
    }else{
        return new CommonResult<>(444,"操作失败");
    }
}

4.1.3、Ribbon默认自带的负载规则

lRule:根据特定算法中从服务列表中选取一个要访问的服务

?SpringCloud学习_Cloud_20

  • RoundRobinRule 轮询
  • RandomRule 随机
  • RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重
  • WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
  • BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
  • ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器

4.1.4、Ribbon负载规则替换

对消费者:cloud-consumer-order-80,进行修改

1、添加Ribbon配置类:com.zhixi.config.MyRibbonConfig

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName MyRibbonConfig
 * @Author zhangzhixi
 * @Description 自定义Ribbon负载均衡策略
 * @Date 2023-04-06 22:03
 * @Version 1.0
 */
@Configuration
public class MyRibbonConfig {

    /**
     * 自定义负载均衡策略
     *
     * @return IRule
     */
    @Bean
    public IRule myRule() {
        return new RandomRule();
    }
}

2、主启动类添加注解:@RibbonClient

import com.zhixi.config.MyRibbonConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

/**
 * @ClassName OrderMain80
 * @Author zhangzhixi
 * @Description 订单服务消费者
 * @Date 2023-04-03 10:39
 * @Version 1.0
 */
@SpringBootApplication
@EnableEurekaClient
/**
 * name: 指定要使用的Ribbon负载均衡策略的服务名称 configuration: 指定自定义的Ribbon负载均衡策略类
 */
@RibbonClient(name = "cloud-payment-service", configuration = MyRibbonConfig.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

3、测试

开启:cloud-eureka-server7001,cloud-provider-payment8001,cloud-provider-payment8002,cloud-consumer-order80

浏览器-输入:http://localhost/consumer/payment/get/31

返回结果中的serverPort在8001与8002两种间反复横跳(随机)

?SpringCloud学习_spring_21

4.2、OpenFeign