SpringCloud学习笔记

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 自学笔记

一、SpringCloud简介

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

SpringBoot是一种服务开发技术

服务注册与发现:EUREKA

服务负载均衡与调用:NETFLIX OSS RIBBON

服务负载与调用:NETTFLIX

服务熔断降级:HYSTRIX

服务网关:Zuul

服务分布式配置:SpringCloud Config

服务开发:SpingBoot

SpringBoot 2.0版和SpringCloud H版 强烈建议使用SpringBoot 2.0以上

SpringBoot和SpringCloud之间版本有约束 H版对应2.2 G版对应2.1

以下技术都围绕主流火爆技术去讲解

img

课程版本约束,以下配置是阳哥的配置

cloud:Hoxton.SR1

boot:2.2.2.RELEASE

cloud alibaba:2.1.0.RELEASE

java:java8

Maven 3.5以上

Mysql:5.7以上

Springboot 与SpringClound关系依赖版本对照表——源自官网,以上的课程版本数据是基于2020年的版本

总结结论:不一定要用最新的,但一定要使用最稳定,最合适的版本

官网地址:https://spring.io/projects/spring-cloud#overview

Springboot的版本和SpringCloud的版本一定要跟官网的对应,否则处理环境问题就够头疼了...(无论更新到何时都通用)

img

img

技术选型的方法:参考SpringCloud的官网推荐中红框的位置点进去有详情

本次我选择的版本是 2021.0.6-SNAPSHOT 版本,对应的Springboot版本是2.6.13

img

此官网是cloud alibaba和SpringCloud版本的对应

https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

截止到2022 / 12 /28的版本对照表

img

因此得出版本选择的结论

SpringCloud: 2021.0.4

Springboot : 2.6.11

cloud alibaba: 2021.0.4.0

java:java8

Maven 3.5以上

Mysql:5.7以上

img

Cloud技术升级

image-20221228194753341

1、新建maven项目:cloud2022

首先将cloud2022定义成父级项目,用于统一jar版本

pom.xml如下

根据自己的需要改变

单击跳过maven的单元测试,节约时间

image-20221228213416138

标签用于统一版本jar包版本号,以统一子项目的jar版本,即子项目无需再声明版本

<?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.xujicheng.springclound</groupId>
    <artifactId>springcloud2022</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>cloud-provider-payment8001</module>
        <module>cloud-consumer-order80</module>
        <module>cloud-api-commons</module>
        <module>cloud-eureka-server7001</module>
    </modules>

    <!--统一管理jar包和版本-->
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>4.6</junit.version>
        <log4j.version>2.17.0</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.verison>1.2.15</druid.verison>
        <mybatis.spring.boot.verison>1.2.2</mybatis.spring.boot.verison>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--spring boot 2.6.11-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.6.11</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud 2021.0.4-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.4</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>2021.0.4.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- MySql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!-- Druid -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.verison}</version>
            </dependency>
            <!-- mybatis-springboot整合 -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.verison}</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!-- log4j -->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

快速创建一个微服务模块的步骤:

  • 1、创建model

image-20221228215106097

  • 2、修改pom.xml文件(父类项目中定义了统一的版本号,无需重复写)
 <dependencies>
        <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>
        </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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
  • 3、写YML配置文件

  • 4、主启动类

  • 5、业务类

项目结构目录如下:

image-20221228231902524

2、创建数据库

CREATE DATABASE /*!32312 IF NOT EXISTS*/`cloud` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci */;
​
USE `cloud`;
​
/*Table structure for table `payment` */
​
DROP TABLE IF EXISTS `payment`;
​
CREATE TABLE `payment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `serial` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
​
/*Data for the table `payment` */
​
insert  into `payment`(`id`,`serial`) values (1,'尚硅谷'),(2,'alibaba'),(3,'京东'),(4,'头条');

3、resource目录下新建 application.yml

注意: mysql8的数据库驱动包是 com.mysql.cj.jdbc.Driver

mysql5是 com.mysql.jdbc.Driver

server:
  port: 8001


spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource  # 当前数据源操作类型
    driver-class-name: com.mysql.jdbc.Driver      # mysql驱动包
    url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.xujicheng.springcloud.entities # 所有Entity 别名类所在包

4、新建启动类PaymentMain8001

/**
 * description
 * 支付模块8001的主启动类
 *
 * @author xujicheng
 * @since 2022年12月28日 22:20
 */

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

5、entities层开发

新建Payment的实体类

/**
 * description
 * 支付模块的主实体类
 *
 * @author xujicheng
 * @since 2022年12月28日 22:40
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {

    private Long id;  //id
    private String serial;   //名称

}

新建Json封装体CommonResult

/**
 * description
 * Json封装体CommonResult相当于与前端的约定
 *
 * @author xujicheng
 * @since 2022年12月28日 22:42
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {

    private Integer code;   //数字编码,约定业务码,如404,500...等
    private String message; //给前端显示的提示信息
    private T data;         //通用的泛型对象

    /**
     * 自定义一个语法塘
     *
     * @param code 业务码
     * @param message 业务对应的提示信息
     */
    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}

6、dao层开发

新建PaymentDao接口

/**
 * description
 * 支付模块的持久层
 *
 * @author xujicheng
 * @since 2022年12月28日 22:54
 */

@Mapper
public interface PaymentDao {
   
   
    int create(Payment payment);  //创建操作
    Payment getPaymentById(@Param("id") Long id);  //读取操作
}

mapper.xml

resource下创建mapper文件夹,新建PaymentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xujicheng.springcloud.dao.PaymentDao">
    <resultMap id="BaseResultMap" type="com.xujicheng.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <id column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <insert id="create" parameterType="payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial)
        values (#{serial})
    </insert>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        select *
        from payment
        where id = #{id}
    </select>
</mapper>

7、service层

service接口

/**
 * description
 * 支付模块业务层接口
 *
 * @author xujicheng
 * @since 2022年12月28日 23:06
 */
public interface PaymentService {
   
   

    int create(Payment payment);  //创建操作
    Payment getPaymentById(@Param("id") Long id);  //读取操作
}

service实现类

/**
 * description
 * 支付模块业务层接口实现类
 *
 * @author xujicheng
 * @since 2022年12月28日 23:07
 */

@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、controller层

/**
 * description
 * 支付模块中的控制层
 *
 * @author xujicheng
 * @since 2022年12月28日 23:12
 */
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
   
   

    //注入PaymentService对象
    @Resource
    private PaymentService paymentService;

    /**
     * 不返回具体的业务逻辑,只返回Json封装体
     *
     * @param payment 支付模块实体对象
     * @return Json封装体,返回封装好的业务码
     */
    @PostMapping("/create")
    public CommonResult create(@RequestBody Payment payment) {
   
   
        int result = paymentService.create(payment);
        log.info("插入数据的ID:\t" + payment.getId());
        log.info("插入结果:" + result);
        if (result > 0) {
   
   
            return new CommonResult(200, "插入数据成功", result);
        } else {
   
   
            return new CommonResult(444, "插入数据失败", null);
        }
    }

    /**
     * 根据id查询对应记录
     *
     * @param id 查询的id是哪个
     * @return Json封装体
     */
    @GetMapping("/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id) {
   
   
        Payment payment = paymentService.getPaymentById(id);
        log.info("***查询结果:" + payment);
        if (payment != null) {
   
   
            return new CommonResult(200, "查询数据成功", payment);
        } else {
   
   
            return new CommonResult(444, "没有对应记录", null);
        }
    }
}

9、测试

1、get测试:浏览器输入:http://localhost:8001/payment/get/2

结果:{"code":200,"message":"查询数据成功","data":{"id":2,"serial":"alibaba"}}

2、post测试:

image-20221228234919664

image-20221228234928942

image-20221228235024746

新建客户端模块cloud-consumer-order80

1、pom文件中添加依赖

<dependencies>
    <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.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </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>
    </dependency>
</dependencies>

2、application.yml

server:
  port: 80

3、启动类

/**
 * description
 * 客户端的主程序入口
 *
 * @author xujicheng
 * @since 2022年12月29日 10:16
 */
@SpringBootApplication
public class OrderMain80 {

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

4、实体类

/**
 * description
 * Json封装体CommonResult相当于与前端的约定
 *
 * @author xujicheng
 * @since 2022年12月28日 22:42
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {

    private Integer code;   //数字编码,约定业务码,如404,500...等
    private String message; //给前端显示的提示信息
    private T data;         //通用的泛型对象

    /**
     * 自定义一个语法塘
     *
     * @param code 业务码
     * @param message 业务对应的提示信息
     */
    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}

/**
 * description
 * 支付模块的主实体类
 *
 * @author xujicheng
 * @since 2022年12月28日 22:40
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {

    private Long id;  //id
    private String serial;   //名称

}

5、RestTemplate

RestTemplate提供了多种便捷访问远程Http服务的方法,

是一种简单便捷的访问restful服务的模板类,是spring提供的用于访问Rest服务的客户端模板工具集。

配置类

/**
 * description
 * RestTemplate配置类
 *
 * @author xujicheng
 * @since 2022年12月29日 11:13
 */
@Configuration
public class ApplicationContextConfig {
   
   

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

6、controller层

/**
 * description
 * 客户端的控制层
 *
 * @author xujicheng
 * @since 2022年12月29日 10:33
 */
@RestController
@Slf4j
public class OrderController {
   
   

    //定义支付端口的URL
    public static final String PAYMENT_URL = "http://localhost:8001";

    //通过RestTemplate完成客户端对支付端的调用
    @Resource
    private RestTemplate restTemplate;

    /**
     * 使用postForObject方法调用支付模块的写入操作
     *
     * @param payment 支付模块的实体对象
     * @return 返回一个Json封装体中的状态码
     */
    @GetMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment) {
   
   
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }

    /**
     * 使用getForObject方法调用支付模块的根据id读取操作
     *
     * @param id id
     * @return 返回一个Json封装体中的状态码
     */
    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
   
   
        return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class, id);
    }
}

7、rundashbroad

运用spring cloud框架基于spring boot构建微服务,一般需要启动多个应用程序,在idea开发工具中,多个同时启动的应用

需要在RunDashboard运行仪表盘中可以更好的管理,但有时候idea中的RunDashboard窗口没有显示出来,也找不到直接的开启按钮

idea中打开Run Dashboard的方法如下

    view > Tool Windows > Run Dashboard

如果上述列表找不到Run Dashboard,则可以在工程目录下找到.idea文件夹下的workspace.xml,在其中相应位置加入以下代码(替换)即可:

<component name="RunDashboard">
<option name="configurationTypes">
  <set>
    <option value="SpringBootApplicationConfigurationType"/>
  </set>
</option>
<option name="ruleStates">
  <list>
    <RuleState>
      <option name="name" value="ConfigurationTypeDashboardGroupingRule"/>
    </RuleState>
    <RuleState>
      <option name="name" value="StatusDashboardGroupingRule"/>
    </RuleState>
  </list>
</option>
</component>

关闭重启后出现。

8、工程重构

提出问题:

下图中的客户端模块和支付端模块中含有相同的代码,结构内容都一致,所以可以将它提取出来,增加代码的复用性

image-20221229130817291

1、新建模块 cloud-api-commons

此模块可以用来提取复用部分的代码,也可以存放工具类、常量等,一处写,处处可以直接调用,无需写重复的代码

依赖:

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.11</version>
    </dependency>
</dependencies>

2、将消费者和服者种的entities拷贝至新模块中

3、删除客户端模块和支付模块中原来的entities包,使用Maven构建clean、install cloud-api-commons 模块,在客户端和服务端pom.xml中分别引入依赖,测试运行。

 <dependency>
            <groupId>com.xujicheng.springclound</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

最终的目录结构:

image-20221229143416273

image-20221229143445159

可以看到,客户端和支付端的实体类部分就被cloud-api-commons模块提取出来了

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
11天前
|
消息中间件 NoSQL Java
Spring Cloud项目实战Spring Cloud视频教程 含源码
Spring Cloud项目实战Spring Cloud视频教程 含源码
35 1
|
11天前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
225 0
|
11天前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
133 0
|
11天前
|
负载均衡 Java 开发者
【SpringCloud】什么是Spring Cloud----综述
【SpringCloud】什么是Spring Cloud----综述
20 0
|
11天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
11天前
|
开发框架 负载均衡 Java
Spring boot与Spring cloud之间的关系
总之,Spring Boot和Spring Cloud之间的关系是一种构建和扩展的关系,Spring Boot提供了基础,而Spring Cloud在此基础上提供了分布式系统和微服务架构所需的扩展和工具。
26 4
Spring boot与Spring cloud之间的关系
|
11天前
|
Cloud Native Dubbo Java
如何确定微服务项目中Spring Boot、Spring Cloud、Spring Cloud Alibaba三者之间的版本
如何确定微服务项目中Spring Boot、Spring Cloud、Spring Cloud Alibaba三者之间的版本
45 0
|
11天前
|
SpringCloudAlibaba 负载均衡 Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
77 1
|
11天前
|
Java Nacos Sentinel
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
265 0
|
11天前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
802 0