JavaWeb_LeadNews_Day11-KafkaStream实现实时计算文章分数

news2024/7/11 17:21:03

JavaWeb_LeadNews_Day11-KafkaStream实现实时计算文章分数

  • KafkaStream
    • 概述
    • 案例-统计单词个数
    • SpringBoot集成
  • 实时计算文章分值
  • 来源
  • Gitee

KafkaStream

概述

  • Kafka Stream: 提供了对存储与Kafka内的数据进行流式处理和分析的功能
  • 特点:
    • Kafka Stream提供了一个非常简单而轻量的Library, 它可以非常方便地嵌入任意Java应用中, 也可以任意方式打包和部署
    • 除了Kafka外, 无任何外部依赖
    • 通过可容错地state, store实现高效地状态操作(如windowed join和aggregation)
    • 支持基于事件时间地窗口操作, 并且可处理晚到的数据(late arrival of records)
  • 关键概念:
    • 源处理器(Source Processor):源处理器是一个没有任何上游处理器的特殊类型的流处理器。它从一个或多个kafka主题生成输入流。通过消费这些主题的消息并将它们转发到下游处理器.
    • Sink处理器: sink处理器是一个没有下游流处理器的特殊类型的流处理器。它接收上游流处理器的消息发送到一个指定的Kafka主题
  • KStream:
    • 数据结构类似于map, key-value键值对.
    • 一段顺序的, 无限长, 不断更新的数据集.

案例-统计单词个数

  • 依赖
    依赖中有排除部分依赖, 还是一整个放上来好了
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- kafkfa -->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.kafka</groupId>
                    <artifactId>kafka-clients</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-streams</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>connect-json</artifactId>
                    <groupId>org.apache.kafka</groupId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.kafka</groupId>
                    <artifactId>kafka-clients</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
  • 流式处理
    public class KafkaStreamQuickStart {
        public static void main(String[] args) {
    
            // Kafka的配置信息
            Properties prop = new Properties();
            prop.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.174.133:9092");
            prop.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
            prop.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
            prop.put(StreamsConfig.APPLICATION_ID_CONFIG, "streams-quickstart");
    
            // Stream 构建器
            StreamsBuilder streamsBuilder = new StreamsBuilder();
    
            // 流式计算
            streamProcessor(streamsBuilder);
    
            // 创建KafkaStream对象
            KafkaStreams kafkaStreams = new KafkaStreams(streamsBuilder.build(), prop);
    
            // 开启流式计算
            kafkaStreams.start();
        }
    
        /**
         * 流式计算
         * 消息的内容: hello kafka
         * @param streamsBuilder
         */
        private static void streamProcessor(StreamsBuilder streamsBuilder) {
            // 创建Kstream对象, 同时指定从哪个topic中接收消息
            KStream<String, String> stream = streamsBuilder.stream("itcast-topic-input");
            /**
             * 处理消息的value
             */
            stream.flatMapValues(new ValueMapper<String, Iterable<String>>() {
                @Override
                public Iterable<String> apply(String value) {
                    String[] valAry = value.split(" ");
                    return Arrays.asList(valAry);
                }
            })
                    // 按照value聚合处理
                    .groupBy((key, value)->value)
                    // 时间窗口
                    .windowedBy(TimeWindows.of(Duration.ofSeconds(10)))
                    // 统计单词的个数
                    .count()
                    // 转换为KStream
                    .toStream()
                    .map((key, value)->{
                        System.out.println("key:"+key+",value:"+value);
                        return new KeyValue<>(key.key().toString(), value.toString());
                    })
                    // 发送消息
                    .to("itcast-topic-out");
        }
    }
    
  • 发送消息
    for (int i = 0; i < 5; i++) {
        ProducerRecord<String, String> kvProducerRecord = new ProducerRecord<>("icast-topic-input", "hello kafka");
        producer.send(kvProducerRecord);
    }
    
  • 接收消息
    // 订阅主题
    consumer.subscribe(Collections.singleton("itcast-topic-out"));
    

SpringBoot集成

  • 配置
    config.java
    /**
     * 通过重新注册KafkaStreamsConfiguration对象,设置自定配置参数
     */
    @Setter
    @Getter
    @Configuration
    @EnableKafkaStreams
    @ConfigurationProperties(prefix="kafka")
    public class KafkaStreamConfig {
        private static final int MAX_MESSAGE_SIZE = 16* 1024 * 1024;
        private String hosts;
        private String group;
        @Bean(name = KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)
        public KafkaStreamsConfiguration defaultKafkaStreamsConfig() {
            Map<String, Object> props = new HashMap<>();
            props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, hosts);
            props.put(StreamsConfig.APPLICATION_ID_CONFIG, this.getGroup()+"_stream_aid");
            props.put(StreamsConfig.CLIENT_ID_CONFIG, this.getGroup()+"_stream_cid");
            props.put(StreamsConfig.RETRIES_CONFIG, 10);
            props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
            props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
            return new KafkaStreamsConfiguration(props);
        }
    }
    
    application.yml
    kafka:
      hosts: 192.168.174.133:9092
      group: ${spring.application.name}
    
    BeanConfig.java
    @Slf4j
    @Configuration
    public class KafkaStreamHelloListener {
    
        @Bean
        public KStream<String, String> KStream(StreamsBuilder streamsBuilder)
        {
            // 创建Kstream对象, 同时指定从哪个topic中接收消息
            KStream<String, String> stream = streamsBuilder.stream("itcast-topic-input");
            /**
             * 处理消息的value
             */
            stream.flatMapValues(new ValueMapper<String, Iterable<String>>() {
                        @Override
                        public Iterable<String> apply(String value) {
                            String[] valAry = value.split(" ");
                            return Arrays.asList(valAry);
                        }
                    })
                    // 按照value聚合处理
                    .groupBy((key, value)->value)
                    // 时间窗口
                    .windowedBy(TimeWindows.of(Duration.ofSeconds(10)))
                    // 统计单词的个数
                    .count()
                    // 转换为KStream
                    .toStream()
                    .map((key, value)->{
                        System.out.println("key:"+key+",value:"+value);
                        return new KeyValue<>(key.key().toString(), value.toString());
                    })
                    // 发送消息
                    .to("itcast-topic-out");
            return stream;
        }
    }
    

实时计算文章分值

  1. nacos: leadnews-behavior配置kafka生产者
    kafka:
      bootstrap-servers: 192.168.174.133:9092
      producer:
        retries: 10
        key-serializer: org.apache.kafka.common.serialization.StringSerializer
        value-serializer: org.apache.kafka.common.serialization.StringSerializer
    
  2. 修改leadnews-behavior
    like
    public ResponseResult likesBehavior(LikesBehaviorDto dto) {
    
        ...
    
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setArticleId(dto.getArticleId());
        mess.setType(UpdateArticleMess.UpdateArticleType.LIKES);
    
        if(dto.getOperation() == 0){
            ...
            mess.setAdd(1);
        }else{
            ...
            mess.setAdd(-1);
        }
    
        // kafka: 发送消息, 数据聚合
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));
    
        ...
    }
    
    view
    public ResponseResult readBehavior(ReadBehaviorDto dto) {
    
        ...
    
        // kafka: 发送消息, 数据聚合
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setArticleId(dto.getArticleId());
        mess.setType(UpdateArticleMess.UpdateArticleType.VIEWS);
        mess.setAdd(1);
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));
    
        ...
    }
    
  3. leadnews-article中添加流式聚合处理
    @Slf4j
    @Configuration
    public class HotArticleStreamHandler {
    
        @Bean
        public KStream<String, String> KStream(StreamsBuilder streamsBuilder)
        {
            // 接收消息
            KStream<String, String> stream = streamsBuilder.stream(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC);
    
            // 聚合流式处理
            stream.map((key, value)->{
                UpdateArticleMess mess = JSON.parseObject(value, UpdateArticleMess.class);
                // 重置消息的key和value
                return new KeyValue<>(mess.getArticleId().toString(), mess.getType().name()+":"+mess.getAdd());
            })
                    // 根据文章id进行聚合
                    .groupBy((key, value)->key)
                    // 时间窗口
                    .windowedBy(TimeWindows.of(Duration.ofSeconds(10)))
                    // 自行实现聚合计算
                    .aggregate(
                            // 初始方法, 返回值是消息的value
                            new Initializer<String>() {
                                @Override
                                public String apply() {
                                    return "COLLECTION:0,COMMENT:0,LIKES:0,VIEWS:0";
                                }
                            },
                            // 真正的聚合操作, 返回值是消息的value
                            new Aggregator<String, String, String>() {
                                @Override
                                public String apply(String key, String value, String aggValue) {
                                    if(StringUtils.isBlank(value)){
                                        return aggValue;
                                    }
                                    String[] aggAry = aggValue.split(",");
                                    int col=0, com=0, lik=0, vie=0;
                                    for (String agg : aggAry) {
                                        String[] split = agg.split(":");
                                        /**
                                        * 获得初始值, 也是时间窗口内计算之后的值
                                        */
                                        switch (UpdateArticleMess.UpdateArticleType.valueOf(split[0]))
                                        {
                                            case COLLECTION:
                                                col = Integer.parseInt(split[1]);
                                                break;
                                            case COMMENT:
                                                com = Integer.parseInt(split[1]);
                                                break;
                                            case LIKES:
                                                lik = Integer.parseInt(split[1]);
                                                break;
                                            case VIEWS:
                                                vie = Integer.parseInt(split[1]);
                                                break;
                                        }
                                    }
                                    // 累加操作
                                    String[] valAry = value.split(":");
                                    switch (UpdateArticleMess.UpdateArticleType.valueOf(valAry[0]))
                                    {
                                        case COLLECTION:
                                            col += Integer.parseInt(valAry[1]);
                                            break;
                                        case COMMENT:
                                            com += Integer.parseInt(valAry[1]);
                                            break;
                                        case LIKES:
                                            lik += Integer.parseInt(valAry[1]);
                                            break;
                                        case VIEWS:
                                            vie += Integer.parseInt(valAry[1]);
                                            break;
                                    }
                                    String formatStr = String.format("COLLECTION:%d,COMMENT:%d,LIKES:%d,VIEWS:%d", col, com, lik, vie);
                                    System.out.println("文章的id "+key);
                                    System.out.println("当前时间窗口内的消息处理结果: "+formatStr);
                                    return formatStr;
                                }
                            }, Materialized.as("hot-article-stream-count-001")
                    )
                    .toStream()
                    .map((key, value)->{
                        return new KeyValue<>(key.key().toString(), formatObj(key.key().toString(), value));
                    })
                    // 发送消息
                    .to(HotArticleConstants.HOT_ARTICLE_INCR_HANDLE_TOPIC);
            return stream;
        }
    
        /**
        * 格式化消息的value数据
        * @param articleId
        * @param value
        * @return
        */
        private String formatObj(String articleId, String value) {
            ArticleVisitStreamMess mess = new ArticleVisitStreamMess();
            mess.setArticleId(Long.valueOf(articleId));
    
            // COLLECTION:0,COMMENT:0,LIKES:0,VIEWS:0
            String[] valAry = value.split(",");
            for (String val : valAry) {
                String[] split = val.split(":");
                switch (UpdateArticleMess.UpdateArticleType.valueOf(split[0]))
                {
                    case COLLECTION:
                        mess.setCollect(Integer.parseInt(split[1]));
                        break;
                    case COMMENT:
                        mess.setComment(Integer.parseInt(split[1]));
                        break;
                    case LIKES:
                        mess.setLike(Integer.parseInt(split[1]));
                        break;
                    case VIEWS:
                        mess.setView(Integer.parseInt(split[1]));
                        break;
                }
            }
            
            log.info("聚合消息处理之后的结果为: {}", JSON.toJSONString(mess));
            return JSON.toJSONString(mess);
        }
    
    }
    
    
  4. leadnews-article添加聚合数据监听器
    @Slf4j
    @Component
    public class ArticleIncrHandlerListener {
    
        @Autowired
        private ApArticleService apArticleService;
        
        @KafkaListener(topics = HotArticleConstants.HOT_ARTICLE_INCR_HANDLE_TOPIC)
        public void onMessage(String mess)
        {
            if(StringUtils.isNotBlank(mess)){
                ArticleVisitStreamMess articleVisitStreamMess = JSON.parseObject(mess, ArticleVisitStreamMess.class);
                apArticleService.updateScore(articleVisitStreamMess);
                System.out.println(mess);
            }
        }
    
    }
    
  5. 替换redis中的热点数据
    /**
     * 更新 文章分值, 缓存中的热点文章数据
     * @param mess
     */
    @Override
    public void updateScore(ArticleVisitStreamMess mess) {
        // 1. 更新文章的阅读, 点赞, 收藏, 评论的数量
        ApArticle apArticle = updateArticleBehavior(mess);
    
        // 2. 计算文章的分值
        Integer score = hotArticleService.computeArticleScore(apArticle);
        score *= 3;
    
        // 3. 替换当前文章对应频道的热点数据
        replaceDataToRedis(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId(), apArticle, score);
    
        // 4. 替换推荐对应的热点数据
        replaceDataToRedis(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG, apArticle, score);
    }
    
    /**
     * 替换数据并且存入到redis中
     * @param HOT_ARTICLE_FIRST_PAGE
     * @param apArticle
     * @param score
     */
    private void replaceDataToRedis(String HOT_ARTICLE_FIRST_PAGE, ApArticle apArticle, Integer score) {
        String articleList = cacheService.get(HOT_ARTICLE_FIRST_PAGE);
        if(StringUtils.isNotBlank(articleList)){
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(articleList, HotArticleVo.class);
            boolean flag = true;
            // 3.1 文章已是热点文章, 则更新文章分数
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                if(hotArticleVo.getId().equals(apArticle.getId())){
                    hotArticleVo.setScore(score);
                    flag = false;
                    break;
                }
            }
            // 3.2 文章还不是热点文章, 则替换分数最小的文章
            if(flag){
                if(hotArticleVoList.size() >= 30){
                    // 热点文章超过30条
                    hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
                    HotArticleVo lastHot = hotArticleVoList.get(hotArticleVoList.size() - 1);
                    if(lastHot.getScore() < score){
                        hotArticleVoList.remove(lastHot);
                        HotArticleVo hotArticleVo = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hotArticleVo);
                        hotArticleVo.setScore(score);
                        hotArticleVoList.add(hotArticleVo);
                    }
                }else{
                    // 热点文章没超过30条
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hotArticleVo);
                    hotArticleVo.setScore(score);
                    hotArticleVoList.add(hotArticleVo);
                }
            }
            // 3.3 缓存到redis
            hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
            cacheService.set(HOT_ARTICLE_FIRST_PAGE, JSON.toJSONString(hotArticleVoList));
        }
    }
    
    /**
     * 更新文章行为数据
     * @param mess
     */
    private ApArticle updateArticleBehavior(ArticleVisitStreamMess mess) {
        ApArticle apArticle = getById(mess.getArticleId());
        apArticle.setComment(apArticle.getComment()==null?0:apArticle.getComment()+mess.getComment());
        apArticle.setCollection(apArticle.getCollection()==null?0:apArticle.getCollection()+mess.getCollect());
        apArticle.setLikes(apArticle.getLikes()==null?0:apArticle.getLikes()+mess.getLike());
        apArticle.setViews(apArticle.getViews()==null?0:apArticle.getViews()+mess.getView());
    
        updateById(apArticle);
    
        return apArticle;
    }
    

来源

黑马程序员. 黑马头条

Gitee

https://gitee.com/yu-ba-ba-ba/leadnews

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

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

相关文章

使用python爬取网站html代码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言一、Requests是什么&#xff1f;二、使用步骤1.引入库2.创建一个Faker对象&#xff1a;3.设置要访问的目标网站的URL&#xff1a;4.定义HTTP请求头部&#xff1a;6.…

【python手写算法】正则化在线性回归和逻辑回归中的应用

多元线性回归&#xff1a; # codingutf-8 import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3Dif __name__ __main__:X1 [12.46, 0.25, 5.22, 11.3, 6.81, 4.59, 0.66, 14.53, 15.49, 14.43,2.19, 1.35, 10.02, 12.93, 5.93, 2.92,…

day 51 |● 503.下一个更大元素II ● 42. 接雨水

503.下一个更大元素II 显示的是循环链表&#xff0c;所以需要遍历两遍。 用i%len(nums)保证循环两遍即可。 func nextGreaterElements(nums []int) []int {res : make([]int, len(nums))for i : 0; i < len(nums); i{res[i] -1}stack : make([]int, 0)stack append(stac…

VoxWeekly|The Sandbox 生态周报|20230904

欢迎来到由 The Sandbox 发布的《VoxWeekly》。我们会在每周发布&#xff0c;对上一周 The Sandbox 生态系统所发生的事情进行总结。 如果你喜欢我们内容&#xff0c;欢迎与朋友和家人分享。请订阅我们的 Medium 、关注我们的 Twitter&#xff0c;并加入 Discord 社区&#xf…

便利连锁店这波操作,赚麻了!

在当今数字化时代&#xff0c;零售业经历了前所未有的革命性变革。消费者期望在购物时获得更多的便捷性、个性化体验和高度智能化的服务。为了满足这些需求&#xff0c;零售商们不得不重新思考他们的经营模式&#xff0c;并将目光投向了新零售模式的未来。 新零售模式不再局限于…

来看看什么是:编译型语言和解释型语言的区别

通过高级语言编写的源码&#xff0c;我们能够轻松理解&#xff0c;但对于计算机来说&#xff0c;它只认识二进制指令&#xff0c;源码就是天书&#xff0c;根本无法识别。源码要想执行&#xff0c;必须先转换成二进制指令。 所谓二进制指令&#xff0c;也就是由 0 和 1 组成的…

非插座式水壶/插座式水壶:SOR/2016-181(水壶法规)和CSA 22.1标准解读!

电水壶作为一种常见的小家电&#xff0c;受到了广大消费者的喜爱。然而&#xff0c;由于安全问题的日益重视&#xff0c;亚马逊加拿大站决定加强对电水壶产品的审核&#xff0c;以确保消费者的安全和权益。 近日&#xff0c;亚马逊平台发布公告&#xff0c;要求在加拿大站销售…

C/C++苹果和虫子 2019年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C苹果和虫子 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C苹果和虫子 2019年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 你买了一箱n个苹果&#xff0c;很不幸…

printf scanf

目录 printf scanf printf 把十的二进制代码放进去了&#xff0c;i对的是二进制代码&#xff0c;指定这一串0101代码以什么样的格式输出。 为什么要输出控制符&#xff0c;因为里面放的是二进制&#xff0c;必须控制输出的格式&#xff0c;指定这一串二进制以什么样的格式输出…

当妈妈们开始精致悦已,母婴品牌该怎么做营销?媒介盒子告诉你

近年来&#xff0c;二孩政策全面放开&#xff0c;母婴行业产品溢价高利润可观&#xff0c;优质品牌层出不穷&#xff0c;那么母婴行业该如何宣传产品呢&#xff1f; 试试软文推广吧&#xff01;软文推广是企业宣传最有效且投入成本最低的宣传方式了。 母婴行业做软文推广可以达…

[uni-app] 海报图片分享方案 -canvas绘制

文章目录 canvas使用记录先看下实际效果图绘制流程及思路1. 绘制头像, 通过drawImage来绘制2.绘制文字部分 具体代码 分享海报图片的方式,以前再RN端采用的是截图方案, 我记得组件好像是 react-native-view-shot 现在要处理uni-app的海报图片分享, 一般也有 html2canvas的相关插…

二维码怎么做调查问卷?二维码统计数据的技巧

小伙伴们一定发现&#xff0c;现在用来收集用户数据时&#xff0c;通过扫码填表的方式在很多场景中都被应用&#xff0c;这种方式有效的提升制作者获取用户数据的速度&#xff0c;而且对于填表者而言也更加的便捷。那么用二维码生成器&#xff08;免费在线二维码生成器-二维码在…

Docker网络功能

基本网络功能 Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。使用docker network子命令来管理Docker网络。 外部访问容器可通过端口映射实现&#xff0c;启动容器时使用-p参数指定映射关系。-p可多次使用来绑定多个端口。使用docker port命令查看当前映射的端…

VR全景拍摄发展如何?在各行业应用中有优势吗?

现如今&#xff0c;虚拟现实技术正在以惊人的速度改变着我们的生活&#xff0c;而VR全景拍摄作为一种创新的拍摄方式&#xff0c;可以为大家带来全新的视觉体验。通过VR全景拍摄&#xff0c;可以将平面画面变得更加逼真、更具沉浸感&#xff0c;让人仿佛置身于真实场景之中。 近…

蚂蚁发布金融大模型:两大应用产品支小宝2.0、支小助将在完成备案后

9月8日&#xff0c;在上海举办的外滩大会上&#xff0c;蚂蚁集团正式发布金融大模型。据了解&#xff0c;蚂蚁金融大 模型基于蚂蚁自研基础大模型&#xff0c;针对金融产业深度定制&#xff0c;底层算力集群达到万卡规模。该大 模型聚焦真实的金融场景需求&#xff0c;在“认知…

恭贺弘博创新2023下半年软考(中/高级)认证课程顺利举行

为迎接2023年下半年软考考试&#xff0c;弘博创新于2023年9月2日举行了精品的软考中/高级认证课程&#xff0c;线下线上学员都积极参与学习。 在课程开始之前&#xff0c;弘博创新的老师为学员们提供了详细的学习资料和准备建议&#xff0c;以确保学员们在课程中能够跟上老师的…

如何排查网站及APP数据泄露的源头

近年来数据泄露安全事件频发&#xff0c;在今年的hw网络安全攻防演练中&#xff0c;获取敏感信息、数据泄露等漏洞的得分也越来越高&#xff0c;我们SINE安全近十年来成功的帮助了许多客户&#xff0c;查找到了数据泄露的原因&#xff0c;在这里向大家分享我们的经验与心得&…

程序员面试逻辑题

红白帽子推理 答案&#xff1a; 这个题有点像数学归纳法&#xff0c;就是假设有 A A A和 B B B两个人是黑色的帽子&#xff0c;这样的话第一次开灯&#xff0c; A A A看到 B B B是黑色的&#xff0c;其他人都是白色的&#xff0c;那么 A A A会觉得 B B B是那个黑色的&#xff0…

【2023高教社杯】B题 多波束测线问题 问题分析、数学模型及参考文献

【2023高教社杯】B题 多波束测线问题 问题分析、数学模型及参考文献 1 题目 1.1 问题背景 多波束测深系统是利用声波在水中的传播特性来测量水体深度的技术&#xff0c;是在单波束测深的基础上发展起来的&#xff0c;该系统在与航迹垂直的平面内一次能发射出数十个乃至上百个…