【Flink实时数仓】数据仓库项目实战 《四》日志数据分流 【DWD】

news2024/11/13 12:34:01

文章目录

  • 【Flink实时数仓】数据仓库项目实战 《四》日志数据分流-流量域 【DWD】
    • 1.流量域未经加工的事务事实表
      • 1.1主要任务
        • 1.1.1数据清洗(ETL)
        • 1.1.2新老访客状态标记修复
        • 1.1.3新老访客状态标记修复
      • 1.2图解
      • 1.3代码
      • 1.4数据测试
        • 1.4.1 测试脏数据
        • 1.4.2 测试err 和 start 数据
        • 1.4.3 输入数据Display Action Page 数据

【Flink实时数仓】数据仓库项目实战 《四》日志数据分流-流量域 【DWD】

DWD层设计要点:
(1)DWD层的设计依据是维度建模理论,该层存储维度模型的事实表。
(2)DWD层表名的命名规范为dwd_数据域_表名

1.流量域未经加工的事务事实表

1.1主要任务

1.1.1数据清洗(ETL)

数据传输过程中可能会出现部分数据丢失的情况,导致 JSON 数据结构不再完整,因此需要对脏数据进行过滤。

1.1.2新老访客状态标记修复

日志数据 common 字段下的 is_new 字段是用来标记新老访客状态的,1 表示新访客,0 表示老访客。前端埋点采集到的数据可靠性无法保证,可能会出现老访客被标记为新访客的问题,因此需要对该标记进行修复。

1.1.3新老访客状态标记修复

本节将通过分流对日志数据进行拆分,生成五张事务事实表写入 Kafka.
流量域页面浏览事务事实表
流量域启动事务事实表
流量域动作事务事实表
流量域曝光事务事实表
流量域错误事务事实表

1.2图解

在这里插入图片描述

1.3代码

代码来自尚硅谷,微信关注尚硅谷公众号 回复: 大数据 即可获取源码及资料。

展示主流程代码。具体工具类及实现请下载源码。

package com.atguigu.app.dwd;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.utils.DateFormatUtil;
import com.atguigu.utils.MyKafkaUtil;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.ProcessFunction;
import org.apache.flink.util.Collector;
import org.apache.flink.util.OutputTag;

//数据流:web/app -> Nginx -> 日志服务器(.log) -> Flume -> Kafka(ODS) -> FlinkApp -> Kafka(DWD)
//程  序:     Mock(lg.sh) -> Flume(f1) -> Kafka(ZK) -> BaseLogApp -> Kafka(ZK)
public class BaseLogApp {

    public static void main(String[] args) throws Exception {

        //TODO 1.获取执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1); //生产环境中设置为Kafka主题的分区数

        //1.1 开启CheckPoint
        //env.enableCheckpointing(5 * 60000L, CheckpointingMode.EXACTLY_ONCE);
        //env.getCheckpointConfig().setCheckpointTimeout(10 * 60000L);
        //env.getCheckpointConfig().setMaxConcurrentCheckpoints(2);
        //env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3, 5000L));

        //1.2 设置状态后端
        //env.setStateBackend(new HashMapStateBackend());
        //env.getCheckpointConfig().setCheckpointStorage("hdfs://hadoop102:8020/211126/ck");
        //System.setProperty("HADOOP_USER_NAME", "atguigu");

        //TODO 2.消费Kafka topic_log 主题的数据创建流
        String topic = "topic_log";
        String groupId = "base_log_app_211126";
        DataStreamSource<String> kafkaDS = env.addSource(MyKafkaUtil.getFlinkKafkaConsumer(topic, groupId));

        //TODO 3.过滤掉非JSON格式的数据&将每行数据转换为JSON对象
        OutputTag<String> dirtyTag = new OutputTag<String>("Dirty") {
        };
        SingleOutputStreamOperator<JSONObject> jsonObjDS = kafkaDS.process(new ProcessFunction<String, JSONObject>() {
            @Override
            public void processElement(String value, Context ctx, Collector<JSONObject> out) throws Exception {

                try {
                    JSONObject jsonObject = JSON.parseObject(value);
                    out.collect(jsonObject);
                } catch (Exception e) {
                    ctx.output(dirtyTag, value);
                }
            }
        });
        //获取侧输出流脏数据并打印
        DataStream<String> dirtyDS = jsonObjDS.getSideOutput(dirtyTag);
        dirtyDS.print("Dirty>>>>>>>>>>>>");

        //TODO 4.按照Mid分组
        KeyedStream<JSONObject, String> keyedStream = jsonObjDS.keyBy(json -> json.getJSONObject("common").getString("mid"));

        //TODO 5.使用状态编程做新老访客标记校验
        SingleOutputStreamOperator<JSONObject> jsonObjWithNewFlagDS = keyedStream.map(new RichMapFunction<JSONObject, JSONObject>() {
            private ValueState<String> lastVisitState;

            @Override
            public void open(Configuration parameters) throws Exception {
                lastVisitState = getRuntimeContext().getState(new ValueStateDescriptor<String>("last-visit", String.class));
            }

            @Override
            public JSONObject map(JSONObject value) throws Exception {

                //获取is_new标记 & ts 并将时间戳转换为年月日
                String isNew = value.getJSONObject("common").getString("is_new");
                Long ts = value.getLong("ts");
                String curDate = DateFormatUtil.toDate(ts);

                //获取状态中的日期
                String lastDate = lastVisitState.value();

                //判断is_new标记是否为"1"
                if ("1".equals(isNew)) {
                    if (lastDate == null) {
                        lastVisitState.update(curDate);
                    } else if (!lastDate.equals(curDate)) {
                        value.getJSONObject("common").put("is_new", "0");
                    }
                } else if (lastDate == null) {
                    lastVisitState.update(DateFormatUtil.toDate(ts - 24 * 60 * 60 * 1000L));
                }
                return value;
            }
        });

        //TODO 6.使用侧输出流进行分流处理  页面日志放到主流  启动、曝光、动作、错误放到侧输出流
        OutputTag<String> startTag = new OutputTag<String>("start") {
        };
        OutputTag<String> displayTag = new OutputTag<String>("display") {
        };
        OutputTag<String> actionTag = new OutputTag<String>("action") {
        };
        OutputTag<String> errorTag = new OutputTag<String>("error") {
        };
        SingleOutputStreamOperator<String> pageDS = jsonObjWithNewFlagDS.process(new ProcessFunction<JSONObject, String>() {
            @Override
            public void processElement(JSONObject value, Context ctx, Collector<String> out) throws Exception {

                //尝试获取错误信息
                String err = value.getString("err");
                if (err != null) {
                    //将数据写到error侧输出流
                    ctx.output(errorTag, value.toJSONString());
                }

                //移除错误信息
                value.remove("err");

                //尝试获取启动信息
                String start = value.getString("start");
                if (start != null) {
                    //将数据写到start侧输出流
                    ctx.output(startTag, value.toJSONString());
                } else {

                    //获取公共信息&页面id&时间戳
                    String common = value.getString("common");
                    String pageId = value.getJSONObject("page").getString("page_id");
                    Long ts = value.getLong("ts");

                    //尝试获取曝光数据
                    JSONArray displays = value.getJSONArray("displays");
                    if (displays != null && displays.size() > 0) {
                        //遍历曝光数据&写到display侧输出流
                        for (int i = 0; i < displays.size(); i++) {
                            JSONObject display = displays.getJSONObject(i);
                            display.put("common", common);
                            display.put("page_id", pageId);
                            display.put("ts", ts);
                            ctx.output(displayTag, display.toJSONString());
                        }
                    }

                    //尝试获取动作数据
                    JSONArray actions = value.getJSONArray("actions");
                    if (actions != null && actions.size() > 0) {
                        //遍历曝光数据&写到display侧输出流
                        for (int i = 0; i < actions.size(); i++) {
                            JSONObject action = actions.getJSONObject(i);
                            action.put("common", common);
                            action.put("page_id", pageId);
                            ctx.output(actionTag, action.toJSONString());
                        }
                    }

                    //移除曝光和动作数据&写到页面日志主流
                    value.remove("displays");
                    value.remove("actions");
                    out.collect(value.toJSONString());
                }
            }
        });

        //TODO 7.提取各个侧输出流数据
        DataStream<String> startDS = pageDS.getSideOutput(startTag);
        DataStream<String> displayDS = pageDS.getSideOutput(displayTag);
        DataStream<String> actionDS = pageDS.getSideOutput(actionTag);
        DataStream<String> errorDS = pageDS.getSideOutput(errorTag);

        //TODO 8.将数据打印并写入对应的主题
        pageDS.print("Page>>>>>>>>>>");
        startDS.print("Start>>>>>>>>");
        displayDS.print("Display>>>>");
        actionDS.print("Action>>>>>>");
        errorDS.print("Error>>>>>>>>");

        String page_topic = "dwd_traffic_page_log";
        String start_topic = "dwd_traffic_start_log";
        String display_topic = "dwd_traffic_display_log";
        String action_topic = "dwd_traffic_action_log";
        String error_topic = "dwd_traffic_error_log";

        pageDS.addSink(MyKafkaUtil.getFlinkKafkaProducer(page_topic));
        startDS.addSink(MyKafkaUtil.getFlinkKafkaProducer(start_topic));
        displayDS.addSink(MyKafkaUtil.getFlinkKafkaProducer(display_topic));
        actionDS.addSink(MyKafkaUtil.getFlinkKafkaProducer(action_topic));
        errorDS.addSink(MyKafkaUtil.getFlinkKafkaProducer(error_topic));

        //TODO 9.启动任务
        env.execute("BaseLogApp");

    }

}

1.4数据测试

1.4.1 测试脏数据

[root@hadoop102 kafka]# bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic topic_log
>{"common":{"ar":
>

idea 结果脏数据打印,kafka未输出。
在这里插入图片描述

1.4.2 测试err 和 start 数据

输入数据

[root@hadoop102 kafka]# bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic topic_log
>{"common":{"ar":
>{"common":{"ar":"110000","ba":"Xiaomi","ch":"xiaomi","is_new":"1","md":"Xiaomi Mix2 ","mid":"mid_1818969","os":"Android 11.0","uid":"513","vc":"v2.1.134"},"err":{"error_code":2633,"msg":" Exception in thread \\  java.net.SocketTimeoutException\\n \\tat com.atgugu.gmall2020.mock.bean.log.AppError.main(AppError.java:xxxxxx)"},"start":{"entry":"notice","loading_time":12438,"open_ad_id":7,"open_ad_ms":4407,"open_ad_skip_ms":0},"ts":1651217959000}
>

输出数据

[root@hadoop102 kafka]# bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic dwd_traffic_start_log
{"common":{"ar":"110000","uid":"513","os":"Android 11.0","ch":"xiaomi","is_new":"1","md":"Xiaomi Mix2 ","mid":"mid_1818969","vc":"v2.1.134","ba":"Xiaomi"},"start":{"entry":"notice","open_ad_skip_ms":0,"open_ad_ms":4407,"loading_time":12438,"open_ad_id":7},"ts":1651217959000}
[root@hadoop102 kafka]# bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic dwd_traffic_error_log
{"common":{"ar":"110000","uid":"513","os":"Android 11.0","ch":"xiaomi","is_new":"1","md":"Xiaomi Mix2 ","mid":"mid_1818969","vc":"v2.1.134","ba":"Xiaomi"},"err":{"msg":" Exception in thread \\  java.net.SocketTimeoutException\\n \\tat com.atgugu.gmall2020.mock.bean.log.AppError.main(AppError.java:xxxxxx)","error_code":2633},"start":{"entry":"notice","open_ad_skip_ms":0,"open_ad_ms":4407,"loading_time":12438,"open_ad_id":7},"ts":1651217959000}

idea打印数据
在这里插入图片描述

1.4.3 输入数据Display Action Page 数据

输入数据

{"common":{"ar":"500000","uid":"981","os":"iOS 13.3.1","ch":"Appstore","is_new":"1","md":"iPhone Xs Max","mid":"mid_7030190","vc":"v2.0.1","ba":"iPhone"},"err":{"msg":" Exception in thread \\  java.net.SocketTimeoutException\\n \\tat com.atgugu.gmall2020.mock.bean.log.AppError.main(AppError.java:xxxxxx)","error_code":1559},"page":{"page_id":"good_detail","item":"5","during_time":7045,"item_type":"sku_id","last_page_id":"good_list","source_type":"activity"},"displays":[{"display_type":"query","item":"15","item_type":"sku_id","pos_id":1,"order":1},{"display_type":"query","item":"26","item_type":"sku_id","pos_id":3,"order":2},{"display_type":"query","item":"31","item_type":"sku_id","pos_id":2,"order":3},{"display_type":"promotion","item":"29","item_type":"sku_id","pos_id":5,"order":4},{"display_type":"query","item":"9","item_type":"sku_id","pos_id":2,"order":5},{"display_type":"recommend","item":"1","item_type":"sku_id","pos_id":1,"order":6}],"actions":[{"item":"5","action_id":"favor_add","item_type":"sku_id","ts":1651217964522}],"ts":1651217961000}

输出数据

dwd_traffic_page_log
[root@hadoop102 kafka]# bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic dwd_traffic_page_log
{"common":{"ar":"500000","uid":"981","os":"iOS 13.3.1","ch":"Appstore","is_new":"1","md":"iPhone Xs Max","mid":"mid_7030190","vc":"v2.0.1","ba":"iPhone"},"page":{"page_id":"good_detail","item":"5","during_time":7045,"item_type":"sku_id","last_page_id":"good_list","source_type":"activity"},"ts":1651217961000}
dwd_traffic_page_log
[root@hadoop102 kafka]# bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic dwd_traffic_page_log
{"common":{"ar":"500000","uid":"981","os":"iOS 13.3.1","ch":"Appstore","is_new":"1","md":"iPhone Xs Max","mid":"mid_7030190","vc":"v2.0.1","ba":"iPhone"},"page":{"page_id":"good_detail","item":"5","during_time":7045,"item_type":"sku_id","last_page_id":"good_list","source_type":"activity"},"ts":1651217961000}
dwd_traffic_display_log
[root@hadoop102 kafka]# bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic  dwd_traffic_display_log
{"display_type":"query","page_id":"good_detail","item":"15","common":"{\"ar\":\"500000\",\"uid\":\"981\",\"os\":\"iOS 13.3.1\",\"ch\":\"Appstore\",\"is_new\":\"1\",\"md\":\"iPhone Xs Max\",\"mid\":\"mid_7030190\",\"vc\":\"v2.0.1\",\"ba\":\"iPhone\"}","item_type":"sku_id","pos_id":1,"order":1,"ts":1651217961000}
{"display_type":"query","page_id":"good_detail","item":"26","common":"{\"ar\":\"500000\",\"uid\":\"981\",\"os\":\"iOS 13.3.1\",\"ch\":\"Appstore\",\"is_new\":\"1\",\"md\":\"iPhone Xs Max\",\"mid\":\"mid_7030190\",\"vc\":\"v2.0.1\",\"ba\":\"iPhone\"}","item_type":"sku_id","pos_id":3,"order":2,"ts":1651217961000}
{"display_type":"query","page_id":"good_detail","item":"31","common":"{\"ar\":\"500000\",\"uid\":\"981\",\"os\":\"iOS 13.3.1\",\"ch\":\"Appstore\",\"is_new\":\"1\",\"md\":\"iPhone Xs Max\",\"mid\":\"mid_7030190\",\"vc\":\"v2.0.1\",\"ba\":\"iPhone\"}","item_type":"sku_id","pos_id":2,"order":3,"ts":1651217961000}
{"display_type":"promotion","page_id":"good_detail","item":"29","common":"{\"ar\":\"500000\",\"uid\":\"981\",\"os\":\"iOS 13.3.1\",\"ch\":\"Appstore\",\"is_new\":\"1\",\"md\":\"iPhone Xs Max\",\"mid\":\"mid_7030190\",\"vc\":\"v2.0.1\",\"ba\":\"iPhone\"}","item_type":"sku_id","pos_id":5,"order":4,"ts":1651217961000}
{"display_type":"query","page_id":"good_detail","item":"9","common":"{\"ar\":\"500000\",\"uid\":\"981\",\"os\":\"iOS 13.3.1\",\"ch\":\"Appstore\",\"is_new\":\"1\",\"md\":\"iPhone Xs Max\",\"mid\":\"mid_7030190\",\"vc\":\"v2.0.1\",\"ba\":\"iPhone\"}","item_type":"sku_id","pos_id":2,"order":5,"ts":1651217961000}
{"display_type":"recommend","page_id":"good_detail","item":"1","common":"{\"ar\":\"500000\",\"uid\":\"981\",\"os\":\"iOS 13.3.1\",\"ch\":\"Appstore\",\"is_new\":\"1\",\"md\":\"iPhone Xs Max\",\"mid\":\"mid_7030190\",\"vc\":\"v2.0.1\",\"ba\":\"iPhone\"}","item_type":"sku_id","pos_id":1,"order":6,"ts":1651217961000}
dwd_traffic_action_log
[root@hadoop102 kafka]# bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic dwd_traffic_action_log
{"page_id":"good_detail","item":"5","common":"{\"ar\":\"500000\",\"uid\":\"981\",\"os\":\"iOS 13.3.1\",\"ch\":\"Appstore\",\"is_new\":\"1\",\"md\":\"iPhone Xs Max\",\"mid\":\"mid_7030190\",\"vc\":\"v2.0.1\",\"ba\":\"iPhone\"}","action_id":"favor_add","item_type":"sku_id","ts":1651217964522}
dwd_traffic_error_log
{"common":{"ar":"500000","uid":"981","os":"iOS 13.3.1","ch":"Appstore","is_new":"1","md":"iPhone Xs Max","mid":"mid_7030190","vc":"v2.0.1","ba":"iPhone"},"err":{"msg":" Exception in thread \\  java.net.SocketTimeoutException\\n \\tat com.atgugu.gmall2020.mock.bean.log.AppError.main(AppError.java:xxxxxx)","error_code":1559},"page":{"page_id":"good_detail","item":"5","during_time":7045,"item_type":"sku_id","last_page_id":"good_list","source_type":"activity"},"displays":[{"display_type":"query","item":"15","item_type":"sku_id","pos_id":1,"order":1},{"display_type":"query","item":"26","item_type":"sku_id","pos_id":3,"order":2},{"display_type":"query","item":"31","item_type":"sku_id","pos_id":2,"order":3},{"display_type":"promotion","item":"29","item_type":"sku_id","pos_id":5,"order":4},{"display_type":"query","item":"9","item_type":"sku_id","pos_id":2,"order":5},{"display_type":"recommend","item":"1","item_type":"sku_id","pos_id":1,"order":6}],"actions":[{"item":"5","action_id":"favor_add","item_type":"sku_id","ts":1651217964522}],"ts":1651217961000}

idea打印数据
在这里插入图片描述

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

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

相关文章

论文写作神器,SCI 写作必备

一、论文写作建议与英文句型积累/引用 1. Academic Phrasebank 网址&#xff1a; https://www.phrasebank.manchester.ac.uk/ Academic Phrasebank&#xff1a;从引言工作到结论提出了论文写作的详细建议&#xff0c;并在每个小点给出了海量的英语表达以供参考。是 SCI 新手…

linux red hat 8.0 搭建DNS服务

DNS简介&#xff1a; 简单的来说&#xff0c;DNS就是把域名和IP地址联系在一起的服务&#xff0c;有了DNS服务器&#xff0c;你就不用输入IP地址来访问一个网站&#xff0c;可以通过输入网址访问。 可以把DNS服务理解成网易有道词典&#xff0c;你去搜索一个英语单词&#xff0…

【MindStudio训练营第一期】【昇腾AI训练营新手班学习笔记】大作业

介绍 大作业&#xff1a;使用MindStudio成功复现昇腾社区中的MindX SDK应用案例 选题&#xff1a;黑白图像上色 https://www.hiascend.com/zh/developer/mindx-sdk/case-studies/d0c56d6f-a6f9-4b77-8587-db8272f22f3b 步骤 运行 使用MindStudio打开下载到的工程文件 创建d…

jsp+ssm计算机毕业设计大学新生军训管理系统【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

笔试强训(四十八)

目录一、选择题二、编程题2.1 左右最值最大差2.1.1 题目2.1.2 题解一、选择题 &#xff08;1&#xff09;常见的http错误描述原因错误的是&#xff08;D&#xff09; A.404-Not found B.302-临时重定向 C.500-内部服务错误 D.403-IP address rejected 403 Forbidden&#xff1…

Unity Animancer插件(三)运动

一、根运动 Animancer的根运动系统与原生的工作原理完全相同&#xff0c;但我们可以通过继承Transition类型或实现ITransition接口&#xff0c;来将额外的数据与动画绑定&#xff0c;从而更方便地控制根运动。 在下面这个示例中&#xff0c;我们通过自定义的Transition类实现…

jsp+ssm计算机毕业设计订单管理系统【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

Linux操作系统~信号处理的底层原理

目录 1.信号在内核中的表示 信号处理的方法 2.信号的递达&#xff0c;未决&#xff0c;阻塞 3.信号集操作函数 &#xff08;1&#xff09;.sigset_t&#xff08;信号集类型&#xff09; &#xff08;2&#xff09;.信号集函数 &#xff08;3&#xff09;.sigprocmask函数…

基于asp.net194校园火车票预订系统-计算机毕业设计

项目介绍 asp.net版火车票查询系统主要有用户注册&#xff0c;在线查询&#xff0c;在线订票&#xff0c;后台管理等功能。用户只有先注册才能登录火车票查询系统的首界面&#xff0c;然后可以进行在线查询、车票订购的功能。可对车次信息的查询和客户对已定车票信息的查询&am…

【云原生进阶之容器】第一章Docker核心技术1.6节——UnionFS

1 UnionFS综述 1.1 什么是 UnionFS 联合文件系统(Union File System),2004年由纽约州立大学开发,它可以把多个目录内容联合挂载到同一个目录下,而目录的物理位置是分开的。UnionFS可以把只读和可读写文件系统合并在一起,具有写时复制功能,允许只读文件系统的修改可以保…

二叉排序树详解及实现

二叉排序树详解及实现1.什么是二叉排序树2.二叉排序树的数据结构2.1二叉排序树的节点类型2.2二叉排序树中插入某个元素2.3 二叉排序树中按值查找元素2.4 找排序二叉树中的最小值2.5返回排序二叉树中ptr中序遍历的后续节点2.6 寻找排序二叉树中的最大值2.7 寻找二叉树中中序遍历…

《痞子衡嵌入式半月刊》 第 68 期

痞子衡嵌入式半月刊: 第 68 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。 上期回顾 :…

【分布式事务之spring实践】分布式事务选型实战

Spring中使用事务 Spring是一个伟大的框架&#xff0c;从一开始只是一个容器框架&#xff0c;到现在已经发展成为了一个包含企业开发中的方方面面的很多框架的总称。它不但从复杂度上&#xff0c;发展出了用于各个方面的子框架。它还从易用性出发&#xff0c;推出了像Spring-B…

多旅行商问题:世界杯优化算法(World Cup Optimization,WCO)求解多仓库多旅行商问题(提供Matlab代码)

一、世界杯优化算法 世界杯优化算法&#xff08;World Cup Optimization&#xff0c;WCO)由Navid Razmjooy等人于2016年提出&#xff0c;该算法模拟了国际足联世界杯比赛&#xff0c;思路新颖&#xff0c;收敛速度快&#xff0c;全局寻优能力强。 算法原理参考&#xff1a;智…

[附源码]Node.js计算机毕业设计河南美丽乡村旅游信息网Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

[附源码]Nodejs计算机毕业设计基于远程协作的汽车故障诊断系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

DevOps实战系列【第十四章-完结篇】:Jenkins Pipeline最佳实践案例

个人亲自录制全套DevOps系列实战教程 &#xff1a;手把手教你玩转DevOps全栈技术 我们将之前的案例&#xff0c;使用流水线构建一下 搭建一个Jenkinsfile模型 pipeline {agent anystages {stage(拉取gitlab项目代码) {steps {echo "拉取git代码"}}stage(构建代码) {…

FRP进阶篇之解决方案

目录 一、前言 二、多客户端使用 1、场景 2、解决方案 3、使用 3.1、服务端配置 3.2、客户端配置 3.3、结果验证 三、dashboard 仪表盘使用 1、场景 2、解决方案 3、使用 4、验证 5、使用Java调用API接口获取设备列表 5.1、样例代码 5.2、结果验证 一、前言 通…

27. SAP OData 框架里的缓存(Cache)设计专题讲座

本教程前一篇文章,25. 答疑 - SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器,我们介绍了 SAP OData 框架处理 metadata 请求的流程,如下图所示: 其中左边的分支,当读取本地服务器的 metadata 时,OData 框架会从 Share Memory 即共…

npm-开发自己的包并发布

目录 1.开发自己的包 1.1. 需要实现的功能 1.2. 初始化包的基本结构 1.3. 初始化 package.json 1.4. 在 index.js 中定义格式化时间的方法 1.5. 在 定义转义 和还原HTML 的方法 1.6. 编写包的说明文档 1.7包的入口文件 2.发布自己的包 2.1注册npm账号 2.2登录npm账…