spring cloud gateway网关和链路监控

news2024/10/5 13:08:52

文章目录

目录

文章目录

前言

一、网关

1.1 gateway介绍

1.2 如何使用gateway 

1.3 网关优化

1.4自定义断言和过滤器

1.4.1 自定义断言

二、Sleuth--链路追踪

2.1 链路追踪介绍

2.2 Sleuth介绍

2.3 使用

2.4 Zipkin的集成

 2.5 使用可视化zipkin来监控微服务

总结



前言


一、网关

所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服 务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控(黑白名单)、路由转发等等。

1.1 gateway介绍

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等术开发的网关,它旨在为微服务架构提供一种简单有效的==统一的 API 路由管理方式==。它的目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且==基于 Filter 链的方式提供了网关基本的功能==,例如:安全,监控和限流。

优点:

l 性能强劲:是第一代网关Zuul的1.6倍

l 功能强大:内置了很多实用的功能,例如转发、监控、限流等

l 设计优雅,容易扩展.

缺点:

l 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高

l 不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行 web.Jar

l 需要Spring Boot 2.0及以上的版本,才支持.

因为gateway它内置的服务器Netty,所以不能在使用Tomcat服务器。

1.2 如何使用gateway 

创建一个网关模块,引入依赖

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

修改application.yml文件

server:
  port: 7777
#配置路由
spring:
  cloud:
    gateway:
      routes:
        - id: my-product   #路由的标识,值唯一,默认随机产生一个唯一值
          uri: http://localhost:8080    #它是网关真实转发到的地址
          order: 0    #路由的优先级,值越小优先级越高
          predicates: #断言: 它返回boolean值,如果返回的为true,则转发到真实的地址 http://localhost:8080/product/getById/1
            - Path=/product/**

        - id: my-order
          uri: http://localhost:8090
          order: 0
          predicates:
            - Path=/order/**

 然后编写一个springboot 启动类就能使用了

1.3 网关优化

把网关也放入nacos注册中心

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

 修改各个微服务之间的负载均衡

server:
  port: 7777
#配置路由
spring:
  cloud:
    gateway:
      routes:
        - id: my-product   #路由的标识,值唯一,默认随机产生一个唯一值
          uri: lb://my-product   #它是网关真实转发到的地址
          order: 0    #路由的优先级,值越小优先级越高
          predicates: #断言: 它返回boolean值,如果返回的为true,则转发到真实的地址 http://localhost:8080/product/getById/1
            - Path=/product/**

        - id: my-order
          uri: lb://my-order  #lb 基于ribbon的负载均衡 默认轮询
          order: 0
          predicates:
            - Path=/order/**

简约模式

 #开启简约模式

spring.cloud.gateway.discovery.locator.enabled=true

然后前端访问的时候路径上必须要加上服务的名称

http://localhost:7777/my-order/order/get/1 

1.4自定义断言和过滤器

1.4.1 自定义断言

@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {


    public static final String MIN = "min";
    public static final String MAX = "max";
    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        //读取配置文件中的内容
        return Arrays.asList("min", "max");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {

        return (t)->{
            ServerHttpRequest request = t.getRequest();
            String age = request.getHeaders().getFirst("age").toString();
            if(StrUtil.isNotEmpty(age)&& NumberUtil.isInteger(age)){
                int i = Integer.parseInt(age);
                if(i>=config.getMin() &&i<=config.getMax()){
                    return true;
                }
            }
            return false;
        };
    }

    @Validated
    public static class Config {
        @NotNull
        private Integer min;
        @NotNull
        private Integer max;

        public Integer getMin() {
            return min;
        }

        public void setMin(Integer min) {
            this.min = min;
        }

        public Integer getMax() {
            return max;
        }

        public void setMax(Integer max) {
            this.max = max;
        }
    }
}

分析:添加年龄的断言 需要在配置文件中使用

     predicates:
            - Path=/product/**
#            - Header=X-Request-Id, \d+ 请求头必须带数字
            - Age=18,28 限制18到28的访问

自定义断言的时候要写成 xxxRoutePredicateFactory 然后使用就 -xxx

 网关过滤器

引入工具依赖

<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.12</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

@Component
public class LoginFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //哪些路径允许放行
        RequestPath path = request.getPath();
        if("/login".equals(path)){
            return chain.filter(exchange);
        }

        //获取请求头
        String token = request.getHeaders().getFirst("token");
        if(StrUtil.isNotEmpty(token)&&"admin".equals(token)){
             return chain.filter(exchange);
        }

        //3.2封装返回数据
        Map<String, Object> map = new HashMap<>();
        map.put("msg", "未登录");
        map.put("code", "NOTLOGING");

        //3.3作JSON转换
        byte[] bytes = JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8);

        //3.4调用bufferFactory方法,生成DataBuffer对象
        DataBuffer buffer = response.bufferFactory().wrap(bytes);

        //4.调用Mono中的just方法,返回要写给前端的JSON数据
        return response.writeWith(Mono.just(buffer));

    }

    @Override
    public int getOrder() {
        return 0;
    }
}

二、Sleuth--链路追踪

2.1 链路追踪介绍

在大型系统的微服务化构建中,一个系统被拆分成了许多微服务。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。在这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心【区域】,也就意味着这种架构形式也会存在一些问题:

l 如何快速发现问题?

l 如何判断故障影响范围?

l 如何梳理服务依赖?

l 如何分析链路性能问题以及实时容量规划?

 

分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上IP、每个服务节点的请求状态200 500等等。

2.2 Sleuth介绍

SpringCloud Sleuth主要功能就是在分布式系统中提供追踪解决方案。它大量借用了Google Dapper的设计, 先来了解一下Sleuth中的术语和相关概念。

Trace(一条完整链路--包含很多span(微服务接口))

由一组Trace Id(贯穿整个链路)相同的Span串联形成一个树状结构。为了实现请求跟踪,当请求到达分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的标识(即TraceId),同时在分布式系统内部流转的时候,框架始终保持传递该唯一值,直到整个请求的返回。那么我们就可以使用该唯一标识将所有的请求串联起来,形成一条完整的请求链路。

*Span*

代表了一组基本的工作单元。为了统计各处理单元的延迟,当请求到达各个服务组件的时候,也通过一个唯一标识(SpanId)来标记它的开始、具体过程和结束。通过SpanId的开始和结束时间戳,就能统计该span的调用时间,除此之外,我们还可以获取如事件的名称。请求信息等元数据。

*Annotation*

用它记录一段时间内的事件,内部使用的重要注释:

l cs(Client Send)客户端发出请求,开始一个请求的命令

l sr(Server Received)服务端接受到请求开始进行处理, sr-cs = 网络延迟(服务调用的时间)

l ss(Server Send)服务端处理完毕准备发送到客户端,ss - sr = 服务器上的请求处理时间

l cr(Client Reveived)客户端接受到服务端的响应,请求结束。 cr - cs = 请求的总时间

2.3 使用

在父工程中引入依赖 所有子模块都加入到链路监控中

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

2.4 Zipkin的集成

Zipkin 是 Twitter 的一个开源项目,它基于Google Dapper实现,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储展现、查找和我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的REST API接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源

除了面向开发的 API 接口之外,它也提供了方便的UI组件来帮助我们直观的搜索跟踪信息和分析请求链路明细,比如:可以查询某段时间内各用户请求的处理时间等。

Zipkin 提供了可插拔数据存储方式:In-Memory、MySql、Cassandra 以及 Elasticsearch。

它主要由 4 个核心组件构成:

l Collector:收集器组件,它主要用于处理从外部系统发送过来的跟踪信息,将这些信息转换为Zipkin 内部处理的 Span 格式,以支持后续的存储、分析、展示等功能。

l Storage:存储组件,它主要对处理收集器接收到的跟踪信息,默认会将这些信息存储在内存中,我们也可以修改此存储策略,通过使用其他存储组件将跟踪信息存储到数据库中。

l RESTful API:API 组件,它主要用来提供外部访问接口。比如给客户端展示跟踪信息,或是外接系统访问以实现监控等。

l Web UI:UI 组件,基于 API 组件实现的上层应用。通过 UI 组件用户可以方便而有直观地查询和分析跟踪信息。

Zipkin 分为两端,一个是 Zipkin 服务端,一个是 Zipkin 客户端,客户端也就是微服务的应用。客户端会配置服务端的 URL 地址,一旦发生服务间的调用的时候,会被配置在微服务里面的 Sleuth 的监听器监听,并生成相应的 Trace 和 Span 信息发送给服务端。

 2.5 使用可视化zipkin来监控微服务

下载zipkin的jar包 在mysql中创建zipkin的数据库来持久化监控数据

CREATE TABLE IF NOT EXISTS zipkin_spans (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  `trace_id` BIGINT NOT NULL,
  `id` BIGINT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `remote_service_name` VARCHAR(255),
  `parent_id` BIGINT,
  `debug` BIT(1),
  `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
  `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query',
  PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';

CREATE TABLE IF NOT EXISTS zipkin_annotations (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
  `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
  `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
  `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
  `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
  `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
  `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
  `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
  `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
  `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';

CREATE TABLE IF NOT EXISTS zipkin_dependencies (
  `day` DATE NOT NULL,
  `parent` VARCHAR(255) NOT NULL,
  `child` VARCHAR(255) NOT NULL,
  `call_count` BIGINT,
  `error_count` BIGINT,
  PRIMARY KEY (`day`, `parent`, `child`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=root

在父项目中引入依赖

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

然后每个微服务模块都配置

# zipkin的配置
spring.zipkin.base-url=http://localhost:9411
#zipkin不注册到nacos上
spring.zipkin.discovery-client-enabled=false
# zipkin要把sleuth产生的日志,抽取的比例。 0.03
spring.sleuth.sampler.probability=1.0

 

 通过浏览器访问 http://localhost:9411访问 完成微服务的链路追踪

 


总结

待补充

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/350966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu wordpress建站

nginx 安装测试 https://blog.csdn.net/leon_zeng0/article/details/113578143 ubuntu 基于apache2安装wordpress https://ubuntu.com/tutorials/install-and-configure-wordpress#7-configure-wordpress 报错403的话&#xff0c;是权限没搞对&#xff0c;解决参考https://ww…

空间误差分析:统一的应用导向处理(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…

深度学习算法面试常问问题(二)

X86和ARM架构在深度学习侧的区别&#xff1f; X86和ARM架构分别应用于PC端和低功耗嵌入式设备&#xff0c;X86指令集很复杂&#xff0c;一条很长的指令就可以完成很多功能&#xff1b;而ARM指令集很精简&#xff0c;需要几条精简的短指令完成很多功能。 影响模型推理速度的因…

mysql分库分表概念及原理、ShardingSphere实现mysql集群分库分表读写分离

一&#xff1a;分库分表概念 1.1 为什么要对数据库进行分表 索引的极限&#xff1a;单表数据量达到几十万或上百万以上&#xff0c;使用索引性能提升也不明显。 分表使用门槛&#xff1a;单表行数超过 500 万行或者单表容量超过 2GB&#xff0c;才推荐进行分库分表。 分表适用…

MIT 6.S965 韩松课程 04

Lecture 04: Pruning and Sparsity (Part II) 文章目录Lecture 04: Pruning and Sparsity (Part II)剪枝率分析每层的敏感度自动剪枝微调和训练稀疏网络彩票假说稀疏度的系统支持不均衡负载M:N 稀疏度本讲座提纲章节 1&#xff1a;剪枝率分析每层的敏感度AMC: AutoML for Model…

C#:Krypton控件使用方法详解(第四讲) ——kryptonLabel

今天介绍的Krypton控件中的kryptonLabel&#xff0c;下面开始介绍这个控件的属性&#xff1a;首先介绍控件中的外观属性&#xff1a;Cursor属性&#xff1a;表示功能为鼠标移动过这个控件的时候显示光标的类型。Text属性&#xff1a;表示显示的文本内容。其他属性不做过多的介绍…

编写 Cypher 代码续

编写 Cypher 代码 过滤查询 查看图中的唯一性约束索引 SHOW CONSTRAINTS查看图中关系的属性类型 CALL db.schema.relTypeProperties()查看图中节点的属性类型 CALL db.schema.nodeTypeProperties()查看数据模型 CALL db.schema.visualization()用 WHERE 子句添加过滤条件 查询执…

28k入职腾讯测试岗那天,我哭了,这5个月付出的一切总算没有白费~

先说一下自己的个人情况&#xff0c;计算机专业&#xff0c;16年普通二本学校毕业&#xff0c;经历过一些失败的工作经历后&#xff0c;经推荐就进入了华为的测试岗&#xff0c;进去才知道是接了个外包项目&#xff0c;不太稳定的样子&#xff0c;可是刚毕业谁知道什么外包不外…

jsp营养配餐管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp营养配餐管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;使…

企业带宽控制管理

在企业中保持稳定的网络性能可能具有挑战性&#xff0c;因为采用数字化的网络可扩展性和敏捷性应该与组织的发展同步。随着基础设施的扩展、新应用和新技术的引入&#xff0c;网络的带宽容量也在增加。 停机和带宽过度使用是任何组织都无法避免的两个问题&#xff0c;为了解决…

最新版海豚调度dolphinscheduler-3.1.3配置windows本地开发环境

0 说明 本文基于最新版海豚调度dolphinscheduler-3.1.3配置windows本地开发环境&#xff0c;并在windows本地进行调试和开发 1 准备 1.1 安装mysql 可以指定为windows本地mysql&#xff0c;也可以指定为其他环境mysql&#xff0c;若指定为其他环境mysql则可跳过此步。 我这…

IB学科学习方法分享,看看不同学科怎么学习?(二)

分享人&#xff1a;李 分享学科&#xff1a;物理HL &#xff08;个人选课&#xff1a;HL&#xff1a;数学 物理 VA。SL&#xff1a;英语 中文 经济。&#xff09; IB物理学习内容&#xff1a;&#xff08;IB物理有HL和SL之分&#xff0c;SL学习的是核心内容&#xff0c;HL则…

通过IP地址如何解决反欺诈?

IP地址在反欺诈方面可以提供有用的信息。以下是一些常见的方法&#xff1a;地理定位&#xff1a;根据IP地址&#xff0c;可以确定访问者的地理位置。这可以帮助您确定是否来自欺诈者通常不在的地理区域的IP地址。可疑行为&#xff1a;通过分析来自某个IP地址的活动&#xff0c;…

802.1x认证方式(EAP中继认证与EAP终结认证)

文章目录1、前言2、协议说明3、报文分析EAP中继模式:MD5-challengeEAP终结模式:MD5-challengeRadius CHAP认证原理参考资料&#xff1a;1、前言 802.1x协议是基于Client/Server的访问控制和认证协议。 它可以限制未经授权的用户/设备通过接入端口(access port)访问LAN/WLAN。在…

element-ui日期控件el-canlender学习笔记

需求&#xff1a;点击日历控件中的某一天&#xff0c;弹出弹出框添加这一天的活动。 任务分析&#xff1a; 点击日历控件的某一天&#xff0c;获取当前日期信息 代码实现&#xff1a; <el-calendar v-model"nowDate" ><templateslot"dateCell"sl…

无文件攻击

无文件攻击是一种高级持续性威胁&#xff08;APT&#xff09;的攻击方式&#xff0c;它不会在目标系统的磁盘上留下可执行文件&#xff0c;而是利用系统内置的工具或脚本执行恶意代码&#xff0c;从而绕过传统的安全防护措施。无文件攻击的最大特点就是恶意代码直接在内存中运行…

ce认证机构如何选择?

CE认证想必大家都已经有所了解&#xff0c;它是产品进入欧盟销售的通行证&#xff0c;那么我们在办理CE认证时该怎么进行选择?带大家了解一下CE认证机构&#xff0c;以及该怎么去进行选择? 以下信息由证果果编辑整理&#xff0c;更多认证机构信息请到证果果网站查看。找机构…

Python版本的常见模板(二) 数论(一)

文章目录前言质数相关质数判断求约数求取区间质数埃氏筛法线性筛法分解质因数欧拉欧拉函数求取单个数线性筛法求取欧拉定理求逆元快速幂/幂取模欧几里得算法求最小公约数拓展欧几里得算法求解同余方程前言 本文主要是提供Python版本的常见的一些与数论相关的模板&#xff0c;例…

SQL中常见的数据类型

SQL中常见的数据类型 目录概述一、整型分类特点二、小数分类特点三、字符型四、日期型分类特点概述 数值型 整数&#xff1a;整型 小数&#xff1a;定点数、浮点数字符型 较短的文本&#xff1a;char、varchar 较长的文本&#xff1a;text、blob(用于保存较长的二进制数据&…

GEE学习笔记 八十二:湖北旱情分析(2019年8月-9月)

2019年8月1日至9月15日&#xff0c;湖北平均雨量为近53年来同期最少&#xff0c;仅69.4毫米。尤其是中东部地区降水异常偏少&#xff0c;一个半月里大多数地方的累计降水量均不足70毫米&#xff0c;其中洪湖、英山降水仅4毫米。鄂西东部一直到鄂东地区的降水量均比常年同期偏少…