实时计算大作业kafka+zookeeper+storm+dataV

第一章 总体需求

1.1.课题背景

近年来,大数据称为热门词汇,大数据分析随着互联网技术的发展愈加深入电商营销之

中,越来越多的电商企业利用大数据分析技术,利用信息化对产业发展营销方向进行确定,

对电子商务行业大数据的特性和背后价值进行深入挖掘,打破传统营销的空间、人群等限制,

在电商场景、渠道客户等各个方面洞察用户的精准营销,从而实现个性化营销与服务等,为

企业发展注入新的活力。而在大数据分析与电商营销的融合过程中,主要是对消费者们的心

理动态特征及行为等方面的分析,把营销与消费者关系作为纽带连接起来,通过得出的有效

数据,对电商营销的整个过程进行实时监控,来优化营销方案与流程,以达到更好的经济效

应。

本文以股票交易背景,针对其用户多、用户地域分布广且在线业务量大的特 点,开发一个关于股票交易信息的大数据看板,用于可实时观测股票交易大数据信息,展示部分重要业绩数据。

1.2.功能需求

本文主要目标是通过实时计算技术、Web 技术构建一个股票交易信息大数据看

a) 订单的已处理速度,单位为“条/秒”;

b) 近 1 分钟与当天累计的总交易金额、交易数量;

c) 近 1 分钟与当天累计的买入、卖出交易量;

d) 近 1 分钟与当天累计的交易金额排名前 10 的股票信息;

e) 近 1 分钟与当天累计的交易量排名前 10 的交易平台;

f) 展示全国各地下单客户的累计数量(按省份),在地图上直观展示;

g) 展示不同股票类型的交易量分布情况;

h) [可选]对单支股票的交易量爆发式增长进行预警:

第二章 方案分析

在课题的测试环境中,订单数据模拟器将模拟实时产生股票交易订单信息,且数据会自动存入 MySQL 数据库相应的表中,因此需要通过对接 MySQL 来同步接收数据并统计保存结果。本文结合实时计算的相关技术,制定了两种方案实现课题需求。

2.1.方案一

方案一的架构如图 1 所示。由于数据模拟器产生的数据会存入 MySQL,因此选择了 Kaf ka 作为消息中间件,把 MySQL 的数据传入到 Kafka 中,再使用 Storm 作为流计算平台,Orderstock 表的数据进行相关统计,同时将统计结果存储至 Mysql。

网页的部分主要使用 Datav 框架。

该方案的优点是:

1. 采用实时读取 MySQL 的方式对接 Kafka,不需要对数据进行处理操作;

2. Storm 中仅用一个拓扑就可以实现表的数据统计,结构简单易懂;

3. Datav 具有操作简单的特点;  

该方案的缺点是:

1. Storm 在实际过程中的吞吐量较低;

2. Redis 将数据保存至内存中,因此在大数据场景下对机器性能有一定要求

2.2.方案二

方案二的架构如图 2 所示。实时生产的电商数据传入 MySQL 数据库中,消息中间件 Ka

4fka 通过 MaxWell 读取 MySQL 的数据增量日志 binlog 作为数据生产源,再利用 Flink 流计算平台统计不同表的数据结果存储至 MySQL。而网页部分则采用 Datav 作为开发框架,通过读取 MySQL 中的统计结果传至前端界面.该方案的优点是:

1. Maxwell 能够将 MySQL 的日志 binlog 作为数据源,并且以 json 形式输出至 Kafka,

实现实时接收数据增量;

2. Flink 对窗口事务的支持较为完善,自带窗口聚合方式实现数据统计;

3.Datav上手简单。

该方案的缺点是:

1. Maxwell 只能将 binlog 输出至一个 Topic,因此在消费 Kafka 数据时,需要手动过滤

不同表的日志并统计;

2. Flink 需要解析 Json 数据,因此对于较为复杂的数据结构,解析过程较为繁琐;

3. 反复连接 MySQL 写入实时数据容易消耗大量时间,导致数据库负载过高,降低运

行效率。

2.3.最终方案

综合上述三种方案分析比较后,可知方案一的架构相对简洁,且在读写效率和方案可行

性方面,方案一都优于其他二者;同时其性能较为稳定,能够满足项目基本需求。因此,本

文最终采用方案一作为讲解的方案。

第三章 总体方案

主要由数据源、消息中间件、流计算系统、实时数据存储和实时数据应用五大板块组成。

数据源为 MySQL,其中 MySQL 不断接收来自订单模拟器传输的数据。消息中间件为 Ka

fka,Kafka 将 MySQL 中的数据依次读取出来,并设置一个主题 TOPIC1,TOPIC1 负责存储 orderstock数据库的订单详情表信息。流计算系统选取的是 Storm,在 Storm 中设置一个拓扑,拓扑中的 OrderSpout订阅 TOPIC1的数据,并将其作为数据源分别发送至 OrderBolt。在 OrderBolt 和 DetailBolt 中,同时设置了一个 JDBC 对象来获取 Java 与 MYsql数据库的连接,把数据的统计结果实时更新至 Datav 中,实现对实时数据的计算。

第四章 单元实现

4.1.数据采集

由于订单数据模拟器会将数据实时发送至 MySQL 数据库中,因此本文选择在 Kafka 生

产者中建立与 MySQL 数据库的连接,将最新的数据依次读取出来,并创建 Orderstock 类存放

Orderstock 表的数据。

创建 Kafka 生产者的配置信息。

Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092,kafka2:9093,kafka3:9094");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<>(props);

String info_js = JSON.toJSONString(info, SerializerFeature.WriteMapNullValue);
//打印
System.out.println(info_js);
//将数据发送至topic
producer.send(new ProducerRecord<String, String>("bookOrder-7", info_js));

4.2.数据的分发与订阅

Kafka 中的生产者接收来自 MySQL 实时新增的数据,发送至 topic1

主题。本文选择通过 Storm 对接 Kafka 传输的数据,在 OrderSpout 和 DetailSpout 中创

建 Kafka 的消费者,订阅 topic1主题,其中 OrderSpout 负责接收 Ord

er 表数据,DetailSpout 负责接收 Orderstock 表数据,将数据传输到 Bolt 中。

Properties props = new Properties();
props.put("zookeeper.connect","localhost:2181");
props.put("group.id","group");
props.put("zookeeper.session.timeout.ms","4000");
props.put("zookeeper.sync.time.ms","200");
props.put("auto.commit.interval.ms","1000");
props.put("auto.offset.reset","latest");
props.put("bootstrap.servers","localhost:9092,localhost:9093,localhost:9094");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("serializer.class","kafka.serializer.StringEncoder");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
ArrayList<String> topics = new ArrayList<>();
topics.add("bookOrder");

 public void nextTuple() {
        ConsumerRecords<String, String> consumerRecords = consumer.poll(0);
        if(consumerRecords != null){
//            System.out.println("DetailSpout发射数据...");
            consumerRecords.forEach(record -> collector.emit(new Values(record.value())));
//            System.out.println("DetailSpout发射成功!");
        }
    }

4.3.数据的统计与存储

Spout 接收到数据后,需要发送至 Bolt 进行数据统计,其中 OrderSpout 将数据发送至 O

rderBolt,DetailSpout 将数据发送至 DetailBolt。

在 OrderBolt 中,主要的统计项目是:

1. 总交易数量;

2. 上市交易数量;

3. 总交易额;

4. 总买入金额;

5. 总卖出金额;

6.实时买入金额

7.实时卖出金额

8.各个股票的总交易金额

9.各股票的实时交易金额

10.各交易公司的总交易金额

11.各交易公司的实时交易金额

12.各服务类型的总交易金额

13.各服务类型的实时交易金额

14.各省市的总交易金额

15.各省市的实时交易金额

本文主要通过 Mysql 进行数据的实时统计与存储,因此在 OrderBolt 和 DetailBolt 分创建

jdbc 对象获取与 Mysql 数据库的连接,且每接收到一条数据,对 Mysql 中指定 Key 的 value

进行自增或自减操作,实现数据的实时统计。由于不同数据的统计要求不一,需要依据其特

点选用相应的存储模式。具体流程见图所示

try{
            write++;
            connection = DriverManager.getConnection(dbUrl, username, password);
            String sql = "UPDATE tradeCount SET "
                    + "trade = ?, "
                    + "totalTradeCount = ?, "
                    + "totalTradeAmount = ?, "
                    + "totalBuyAmount = ?, "
                    + "totalSellAmount = ?, "
                    + "minuteTradeCount = ?, "
                    + "minuteTradeAmount = ?, "
                    + "minuteBuyAmount = ?, "
                    + "minuteSellAmount = ?, "
                    + "guolianTradeVolume = ?, "
                    + "tongdaxinTradeVolume = ?, "
                    + "changchengTradeVolume = ?, "
                    + "guotaijunanTradeVolume = ?, "
                    + "yinheTradeVolume = ?, "
                    + "tonghuashunTradeVolume = ? "
                    + "WHERE id = '股票' ";
            preparedStatement = connection.prepareStatement(sql);

            String updateSql = "UPDATE tradeCount SET totalTradeAmount = ?, minuteTradeAmount = ?, totalBuyAmount = ?,totalSellAmount = ? WHERE id = ?";
            updateStmt = connection.prepareStatement(updateSql);

            String Sql = "UPDATE tradeCount SET value= ? WHERE id = ?";
            update = connection.prepareStatement(Sql);

            String SqlZq = "UPDATE tradeCount SET minuteTradeAmount = ?,platform = ? WHERE id = ?";
            updateZq = connection.prepareStatement(SqlZq);

            String SqlHy = "UPDATE tradeCount SET totalTradeAmount = ? WHERE id = ?";
            updateHy = connection.prepareStatement(SqlHy);
//            String[] values = value.split(",");    //等待删除,可能会删了它
            String stockId = detail.getStock_name();     // 交易类型id
            String local = detail.getTrade_place();         //交易地点
            String server = detail.getIndustry_type();     //服务类型
            long tradeCount = detail.getTrade_volume();     //单次交易数量
            double tradePrice = detail.getTrade_price();  //赋值交易价格
            double tradeAmount = tradeCount * tradePrice;  //交易总价格
            // 更新累计值
            totalTradeCount.addAndGet(tradeCount*2);  //累计交易总量
            totalTradeAmount.addAndGet((long)tradeAmount*2);  //累计交易总金额
            // 更新近一分钟的值
            long currentTime = System.currentTimeMillis();
            if (currentTime - lastUpdateTime > 60000) {
                minuteTradeCount.set(0);  //   分钟贸易计数
                minuteTradeAmount.set(0);//    分钟交易额
                minuteBuyAmount.set(0);//      分钟购买金额
                minuteSellAmount.set(0);//     分钟分钟销售金额
                lastUpdateTime = currentTime;
            }
            trade++;
            trade++;
            trade++;
            trade++;
            minuteTradeCount.addAndGet(tradeCount);
            minuteTradeAmount.addAndGet((long)tradeAmount*2);
            minute2tradeAmount.addAndGet((long)tradeAmount*2);
            minute3tradeAmount.addAndGet((long)tradeAmount*2);
            minute4tradeAmount.addAndGet((long)tradeAmount*2);
            minute5tradeAmount.addAndGet((long)tradeAmount*2);
            // 根据买入或卖出更新相应的值
            if (detail.getTrade_type().equals("买入")) {
                totalBuyAmount.addAndGet(tradeCount*2);  //总买入量
                minuteBuyAmount.addAndGet(tradeCount*2);  //分钟买入量
            } else if (detail.getTrade_type().equals("卖出")) {
                totalSellAmount.addAndGet(tradeCount*2);  //总卖出量
                minuteSellAmount.addAndGet(tradeCount*2);  //分钟卖出量
            }
            long currentTimeZj = System.currentTimeMillis();
            if (currentTimeZj - lastUpdateTime > 60000) {
                guolianTradeVolume.set(0);  //   分钟贸易计数
                tongdaxinTradeVolume.set(0);//    分钟交易额
                changchengTradeVolume.set(0);//      分钟购买金额
                guotaijunanTradeVolume.set(0);//     分钟分钟销售金额
                yinheTradeVolume.set(0);//      分钟购买金额
                tonghuashunTradeVolume.set(0);//     分钟分钟销售金额
                lastUpdateTime = currentTimeZj;
            }

            Random random = new Random();
            int randomNumber = random.nextInt(31) + 60;

            //更新券商交易量
            if (detail.getTrade_platform().equals("国联证券")) {
                guolianTradeVolume.addAndGet(tradeCount*2);  //统计数量国联证券
            }
            else if (detail.getTrade_platform().equals("通达信")) {
                tongdaxinTradeVolume.addAndGet(tradeCount*2); //统计数量通达信
            }
            else if (detail.getTrade_platform().equals("长城证券")) {
                changchengTradeVolume.addAndGet(tradeCount*2);//统计数量
            }
            else if (detail.getTrade_platform().equals("国泰君安证券")) {
                guotaijunanTradeVolume.addAndGet(tradeCount*2);//统计数量
            }
            else if (detail.getTrade_platform().equals("银河证券")) {
                yinheTradeVolume.addAndGet(tradeCount*2);//统计数量
            }
            else if (detail.getTrade_platform().equals("同花顺")) {
                tonghuashunTradeVolume.addAndGet(tradeCount*2);//统计数量
            }
            Map<String, Long> tradeVolumes = new HashMap<>();
            tradeVolumes.put("国联证券", guolianTradeVolume.get());
            tradeVolumes.put("通达信", tongdaxinTradeVolume.get());
            tradeVolumes.put("长城证券", changchengTradeVolume.get());
            tradeVolumes.put("国泰君安证券", guotaijunanTradeVolume.get());
            tradeVolumes.put("银河证券", yinheTradeVolume.get());
            tradeVolumes.put("同花顺", tonghuashunTradeVolume.get());
            Map<Long, String> sortedVolumes = new TreeMap<>();
            for (Map.Entry<String, Long> entry : tradeVolumes.entrySet()) {
                sortedVolumes.put(entry.getValue(), entry.getKey());
            }
            Long value_zq1 = null;
            Long value_zq2 = null;
            Long value_zq3 = null;
            String platform1 = null;
            String platform2 = null;
            String platform3 = null;
            int count = 0;
            for (Map.Entry<Long, String> entry : ((TreeMap<Long, String>) sortedVolumes).descendingMap().entrySet()) {
                if (count >= 3) {
                    break;
                }
                if (count == 0) {
                    value_zq1 = entry.getKey();
                    platform1 = entry.getValue();
                } else if (count == 1) {
                    value_zq2 = entry.getKey();
                    platform2 = entry.getValue();
                } else if (count == 2) {
                    value_zq3 = entry.getKey();
                    platform3 = entry.getValue();
                }
                count++;
            }

            //更新地图数据
            DetailBoltMySql.values v = valueMAP.computeIfAbsent(stockId, k -> new DetailBoltMySql.values());
            v.value.addAndGet(tradeCount*3);

            //更新各个服务类型的总金额
            DetailBoltMySql.servers s = serverMAP.computeIfAbsent(server, k -> new DetailBoltMySql.servers());
            s.server.addAndGet((long) tradeAmount*3);

            //更新不同股票数据总交易金额
            DetailBoltMySql.StockTradeInfo stockTradeInfo = stockTradeInfoMap.computeIfAbsent(stockId, k -> new DetailBoltMySql.StockTradeInfo());
            stockTradeInfo.totalTradeAmount.addAndGet((long) tradeAmount*3);

            // 更新近一分钟的交易金额
            long Time = System.currentTimeMillis();
            if (Time - stockTradeInfo.lastUpdateTime > 60000) {
                stockTradeInfo.minuteTradeAmount.set(0);
                stockTradeInfo.lastUpdateTime = Time;
//                trade = 10;
            }

            stockTradeInfo.minuteTradeAmount.addAndGet((long) tradeAmount);

            if (detail.getTrade_type().equals("买入")) {
                trade++;
                stockTradeInfo.totalBuyAmount.addAndGet(tradeCount);
            } else if (detail.getTrade_type().equals("卖出")) {
                stockTradeInfo.totalSellAmount.addAndGet(tradeCount);
            }

4.4.网页前后端交互

本文主要使用datav 框架实现网页的前后端交互,后端运行服务,读取 mysql 中的数据

统计结果并发送至datav

第五章 功能实现

5.1.使用说明

本文按照方案流程对电商订单数据进行实时展示。首先将 Storm 的项目 jar 包上传虚拟

机集群,运行拓扑;接着启动 Kafka 生产者,将实时增加的 MySQL 数据发送至 Storm 的计算

平台,Storm 中的 Bolt 会将数据统计结果写入 mysql 中。

5.1.1. 启动 Zookeeper、Kafka、Storm

5.1.3. 上交拓扑至 Storm 集群

在基本的环境准备工作完成后,下一步是上传设计的拓扑至 Storm 集群中,这里我们是

将 IDEA 编写的 Storm 程序打成 jar 提交至集群,提交成功后,控制台打印信息。

另外,我们可以在 StormUI 页面查看此时 Topology Summary 已经存在一个名为 mytop 的拓

扑,如图 24 所示,页面会显示其已运行时间及状态,在拓扑内容可查看方案设计中的两个

Spout 和两个 Bolt,并且拓扑中的 Worker 由两个从节点 Slave1 和 Slave2 组成。

5.1.4. 启动订单数据模拟器

如图 27 所示,我们在订单数据模拟器中输入 MySQL 所在的 IP 地址及用户名,点击创

建数据库并开始写入数据。

5.1.5. 运行 Kafka 程序

订单数据成功开始产生后,我们需要启动 IDEA 的 Kafka 程序,程序主要实现了对接 My

SQL 实时数据和将数据发送至 Topic 的功能,运行成功后,可在控制台实时看到接收的订单

数据。

5.2.界面效果

5.2.1. 页面总体效果

页面的总体效果如图所示,页面主要分为七个板块:

功能1—实时展示订单速度

功能2—股票行业分层,含各行业实时金额排序:

功能3—股票交易额排行

功能4—实时平台排名,与平台实时的交易额

功能5—销量最高股票的买入卖出情况

功能6—股票交易总额实时交易额

功能7—股票实时交易量总体情况与排行

功能8—股票总交易量总体情况与排行

功能9—全国各地交易次数热力图

第六章 实验设计与结果分析

6.1.项目运行环境

6.1.1. 本地计算机系统配置

表 1 本地计算机系统配置

操作系统:Windows10

软件版本:python2022,idea2022

Doker配置:storm2.5.0(nimbus2.5.0,supervisor2.5.0);Mysql5.7;Kafka:latest;zookeeper:lastest

6.1.2. docker配置

安装 Zookeeper、Kafka、Storm

6.2.结果分析

第七章 项目特色

本项目的特色之处如下:

  1. 使用 Storm 作为流计算系统,计算效率稳定;2. 使用 datav 作为实时数据存储,操作简单,
  2. 数据库只存放最终数据,所以计算过程放在execute中。

3. 网页实现可在 0.5s 内刷新一次,且能够通过动态的动画展示数据变化;

4. 订单接收速率和订单模拟器产生速率的误差最低能够在 1%以内(见第六章);

5. 数据延迟平均保持在 1 秒以内(见图 39 和图 40);

6. 项目实现了 7 个数据统计模板;

第八章 问题分析

 1.拓扑已经上传,但是并没有进行计算:

场景:将csv文件放在本地项目中,测试代码能否跑通,结果拓扑一直都能上传,但是一直没有进行计算

最后找到源码的出处询问原因才知道:拓扑结构提交后是无法读取本地数据的。所以一开始的方向是错误的。

2.依赖冲突:一开始使用的storm是最高版本,在idea中的storm相关依赖都是2.5.0版本,于是报有以下错误:

​编辑

最后将依赖的版本逐个降低到2.3.0何1.0.0都能成功运行。

3.在storm中上传jar报,提交拓扑时,找不到jar报中的主类:

​编辑

经过进一步的探究,原因是我打包有问题,一开始不清楚该如何打包,我使用的都是传统的打包方式,那种方式只是将java文件编译了一遍放入jar包中,相关的依赖包都没有导入,后来又使用了idea自带的打包方式,也有这种问题。通过查阅maven的相关使用方法系统学习了打包方式,才正确打包。maven的5种打包方式,终有一款适合你1_maven打包-CSDN博客

4.解决打包问题后,又遇到了新的与打包相关的问题:打包的jar包中有一个yaml文件,有storm中原带的同名yaml文件冲突:这时候有两种解决方法,一种是排除掉storm集群中的yaml文件。

​编辑

最后的解决方法:最后在pom文件的打包配置中,引入一个组件,用来排除原java项目中的yaml,直接就解决了。

​编辑

5.一开始不知道DataV使用的是mysql的时候,是将数据放入redis中,遇到了一个相关问题:

即在本地使用LocalCluster方法运行拓扑,能过正常的将数据写入到redis中,但是一旦将jar包放入到storm集群中,前面的能够全部连通,但是数据无法写入到redis中。

​

编辑

解决方法:根据报错结果分析,应该是路径问题,但是最终还没解决,发现datav使用的mysql就改用sql没管这个问题了。

​​

1.阿里云的DataV无法实时展示:

在这个时候我的sql里的数据都是在实时变化的,但是Datav数据不能实时变化,有以下两点原因:

在数据库设置中,需要设置数据每秒自动请求:

除此之外,不能使用全局变量去获取数据:

2.在idea中,无法连接到外部的kafka:

无论是直接使用ip还是使用localhost都无法找到外部的idea:

​编辑

​编辑在后来在host文件中配置了kafka1:172.0.0.1后,发现连接成功:

​编辑

配置后发现,无论是使用localhost还是使用kafka1,2,3都可以连接到了。这很奇怪,因为直接使用localhost本质上就是使用172.0.0.1,使用kafka1,2,3也是使用172.0.0.1.但为什么配置以上的内容之后就可以?

3.storm集群搭建问题

使用docker-compose.yaml文件之后,strom无法连接到zookeeper集群:

​编辑

这时需要去storm.yaml文件中更改配置文件,手动的将storm与zookeeper连接上

​编辑

4.storm集群的搭建出现问题,具体参考:docker-compose搭建storm、zookeeper集群,解决Could not find leader nimbus from seed hosts [localhost]问题-CSDN博客

5.重复读取消息队列中的内容,在将新生产的消息消费完成之后,offset(偏移量)又会到一个之前已经消费过的地方开始继续消费,导致一直能有消息在进行消费:

在配置中,配置以上的内容都发现没有用,仍然有这个问题,之后,借鉴了相关的代码:

第九章 心得体会

完成这次大作业的过程给我带来了许多新鲜的感觉,这是我第一次从真正意义上认识到

大数据专业目前在社会上的实际应用场景。刚开始搭建 Kafka 的部分并没有让我早早地看清

大作业任务的艰巨,直到进行使用 Kafka 对接 MySQL 数据的环节,才让我知道什么是寸步

难行、步履维艰。

在进行到大作业的后半段,从摸索着如何写一个 Storm 程序、如何将 Kafka 的数据再传

入 Storm、如何在 Storm 的 Bolt 中实现对数据的统计等等过程当中,我对于搭建实时计算平

台的流程越来越熟练,越来越清晰,直到结束之际,我已经能根据所搭建的实时计算平台流

畅的画出架构图,并对其中的原理、各组件的功能、操作流程基本了如执掌。如果对这学期

刚开始的我而言,这可能是无法想象的事情,所以当我看到自己实现的大数据看板在不断的

跳动、变化时,内心的成就感油然而生。

在整个项目的开发过程中,除了最后实现它所收获的成就与喜悦,还出现了一堆十分让

我头疼的小插曲。比如,上传 jar 包到 Storm 集群上报错,报错信息的解决方案在网上根本

找不着,所以只能自己一个一个去排除可能的问题。类似这种情况的问题还有很多,有的时

候为了解决一个报错耗费了将近两小时也没找到正确解决的办法,但是在解决这些问题的过

程中,也让我对各个组件功能的了解进一步加深。

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

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

相关文章

双碳管理系统任务需求分析(第10套)

需求规格说明书 一、引言 &#xff08;一&#xff09;项目背景 编写本需求规格说明书的目的是为了详细呈现碳足迹产品需求和系统的功能描述&#xff0c;以进一步定制应用软件系统开发的细节问题&#xff0c;便于与项目开发协调工作。本文档面向的读者主要是项目委托单位的管…

git rebase(变基)应用场景

文章目录 git rebase(变基)应用场景1.git rebase -i HEAD~3 git rebase(变基)应用场景 使得提交记录变得简洁 现在我们模拟我们有多次提交记录&#xff0c;本地仓库有三条提交 整合成一条提交记录 1.git rebase -i HEAD~3 提交记录合并 HEAD~3合并三条记录 执行之后 然后把…

事实就是这么残酷,分享一个案例投资者是怎么一步步失败

都说交易市场要学会斗智斗勇&#xff0c;但fpmarkets澳福提醒交易者要始终记住&#xff0c;买的没有卖的精&#xff0c;下面就分享一个案例&#xff0c;让各位投资者知道现实就是这么残酷&#xff0c;一些无良的资本是怎么一步步让投资者失败的。 当在整个交易市场中渐渐地&am…

清风数学建模笔记-时间序列分析

内容&#xff1a;时间预测分析 一.时间序列 1.时点时间序列 2.时期时间序列&#xff1a;可相加 二.时间趋势分解 1.季节趋势 拓展&#xff1a;百度指数&#xff1a; 2.循环变动趋势&#xff08;和季节很像但是是以年为单位&#xff09; 3.不规则变动趋势&#xff08;像扰…

《深入理解C++11:C++11新特性解析与应用》笔记八

第八章 融入实际应用 8.1 对齐支持 8.1.1 数据对齐 c可以通过sizeof查询数据的长度&#xff0c;但是没有对对齐方式有关的查询或者设定进行标准化。c11标准定义的alignof函数可以查看数据的对齐方式。 现在的计算机通常会支持许多向量指令&#xff0c;4组8字节的浮点数据&a…

极值统计学及其在相关领域中的技术应用

受到气候变化、温室效应以及人类活动等因素的影响&#xff0c;自然界中极端高温、极端环境污染、大洪水和大暴雨等现象的发生日益频繁&#xff1b;在人类社会中&#xff0c;股市崩溃、金融危机等极端情况也时有发生&#xff1b;今年的新冠疫情就是非常典型的极端现象。研究此类…

离线Vscode 安装完成后 添加到右键菜单

复制下面代码&#xff0c;修改文件后缀名为&#xff1a;reg Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\*\shell\VSCode] "Open with Code" "Icon""D:\\_Porgram_IT\\VsCode\\Code.exe"[HKEY_CLASSES_ROOT\*\shell\VSCode\comman…

AI:110-基于深度学习的药物分子结构生成与预测

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

填充点云孔洞(较大的洞)halcon算法

前言 很多时候,一些小洞可以通过平滑算法,或者三角化算法的参数调整,即可对较小的孔洞进行填充,但是较大的洞却很难通过上面的算法进行填充。 下面介绍一种填充孔洞的思路: 步骤一:对点云进行滤波处理,找到孔洞所在平面 本文为了更直观的进行讲解,去掉了去除噪声和…

数读中国这十年:研发经费超3万亿元 创新引领显成效

摘要&#xff1a;本文转载自新华社。 新华社北京12月22日电 题&#xff1a;研发经费超3万亿元 创新引领显成效 新华社记者 陈炜伟、潘德鑫 这是2023年6月11日在青海省海西蒙古族藏族自治州格尔木市拍摄的一座光热电站&#xff08;无人机照片&#xff09;。新华社记者 张宏…

Linux高级玩家必备sos_report-尚文网络xUP楠哥

进Q群11372462领取专属报名福利! # 什么是sos sos 是红帽技术支持工程师在执行任务时的常见起点分析 RHEL 系统的服务请求。 该实用程序提供了一种标准化的方式来收集红帽支持工程师可以在整个调查过程中参考的诊断信息支持案例中报告的问题。 使用 sos 报告实用程序有助于确…

亚马逊站内广告位置在哪设置?怎么设置广告位置?-站斧浏览器

亚马逊站内广告位置在哪设置&#xff1f; 亚马逊提供了多种广告类型&#xff0c;包括&#xff1a; Sponsored Products&#xff08;赞助产品&#xff09;&#xff1a;在搜索结果和商品详情页中展示。 Sponsored Brands&#xff08;赞助品牌&#xff09;&#xff1a;在搜索结…

Android自动化巡检环境搭建

Android自动化巡检 一、环境配置1、jdk配置2、python环境配置3、android sdk配置4、node配置5、allure配置6、scrcpy录屏工具配置7、appium配置8、模拟器安装 二、项目配置1、python项目设置虚拟环境&#xff0c;迁移至另一台机器&#xff1a;2、目录结构3、项目运行步骤4、自动…

Fast R-CNN

Fast R-CNN算法流程 对比与R-CNN其在第二步时并没有将所有的候选区域进行逐个的CNN特征提取&#xff0c;而是直接将整个图片进行一次CNN特征提取&#xff0c;让后再将候选区映射到feature map上。可想而知速度得到了提升。这里的ROI pooling层缩放到7x7就是将候选区域对应的特征…

通过聚道云软件连接器实现销帮帮软件与i人事软件的智能对接

客户介绍 某软件行业公司是一家专业从事软件技术服务、软件开发、应用解决方案、业务流程优化、专业服务的高科技企业。公司拥有一支经验丰富、技术精湛的服务团队&#xff0c;具备多年的软件开发和应用解决方案经验。他们不断追求技术的创新和进步&#xff0c;以满足客户不断…

Sectigo与Geotrust ov多域名证书的区别

Sectigo和Geotrust都是比较知名的CA认证机构。其中&#xff0c;Sectigo原名Comodo&#xff0c;在2018年整合SSL证书业务&#xff0c;改名为Sectigo&#xff0c;旗下的SSL证书产品根证书也变为Sectigo。Geotrust则是另一个备受信任的数字证书品牌&#xff0c;现在是Digicert旗下…

C语言注意点(4)

1、void *a是什么意思 答&#xff1a;泛型指针&#xff0c;但不规定其类型(就是地址确定&#xff0c;但数据长度不确定)在动态分配内存时&#xff0c;malloc的返回值就是该类型&#xff0c;方便用户进行强制转换。 2、VS怎么一键规范格式 for(i0;i<10;i)enter后&#xff0c;…

JUC AQS(AbstractQueuedSynchronizer)

文章目录 AQS &#xff08;AbstractQueuedSynchronizer^1.5^&#xff09;CLH 锁队列AbstractQueuedSynchronizer 成员变量说明AbstractQueuedSynchronizer.Node 源码CLH 队列原理图入队逻辑方法出队逻辑方法 继承 AQS 需要实现的几个方法AQS 对象序列化ReentrantLock 源码解析R…

环境监测LoRa网关解决方案应用空气质量监控

随着全球工业化和城镇化的快速发展&#xff0c;空气质量问题越来越受到关注。环境监测技术的发展&#xff0c;可以有效地帮助我们监测和改善空气质量。而LoRa网关则是一种可以帮助我们实现远距离、低功耗通信的无线通信技术&#xff0c;它的应用可以为空气质量监测提供解决方案…

java常见编程题

1.已知有一个父类 Person&#xff0c;包含两个属性 name 和 age&#xff0c;还有带两个参数的构造方法 Person()和一个打印信息的info()方法。 public class Person{ protected String name; protected int age; public Person(String name, int age){ this.name name; this.…