【kafka】Golang实现分布式Masscan任务调度系统

news2025/6/12 19:09:04

要求:

        输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。

        命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。

服务端程序:

  1. 从kafka消费者接收扫描任务信息
  2. 通过调用masscan启动探测任务,获取进度和结果信息,进度写入Redis,结果信息写入Kafka。
  3. 要求对启动任务、kafka、整理流程进行封装。
  4. 要求启动2个server端,通过命令行程序下发2个不同网段,可以均匀的分配到2个server上面执行完成。

测试要求:

  1. 启动两个server端程序。
  2. 通过命令行程序下发两个任务,IP不一样。
  3. 看server端程序日志,是否均匀的扫描了两个任务。

     

前置准备:

        安装docker

思路:

1. 系统架构设计

采用生产者-消费者模式:

  • 命令行客户端作为生产者,将扫描任务发布到Kafka
  • 两个服务端实例作为消费者,从Kafka获取任务并执行

2. 关键组件设计

  1. 任务表示

    • 使用JSON格式表示扫描任务,包含:
      • IP范围(单个IP或CIDR格式)
      • 端口范围
      • 扫描带宽限制
      • 任务状态
      • 进度信息
  2. Kafka设计

    • 创建一个主题(如scan-tasks
    • 使用单个分区确保任务顺序性(或根据需求设计分区策略)
    • 考虑使用消费者组实现两个服务端的负载均衡
  3. Redis设计

    • 存储任务进度信息
    • 使用Hash结构存储每个任务的进度百分比
    • 设置适当的TTL防止数据无限增长
  4. 服务端负载均衡

    • 两个服务端加入同一个Kafka消费者组
    • Kafka会自动将任务均匀分配给两个消费者

3. 执行流程

  1. 客户端流程

    • 解析命令行参数(IP范围、端口、带宽)
    • 验证输入格式
    • 创建Kafka生产者
    • 将任务发布到Kafka主题
  2. 服务端流程

    • 初始化Kafka消费者(加入消费者组)
    • 初始化Redis连接
    • 循环消费任务:
      a. 从Kafka获取任务
      b. 更新Redis中任务状态为"running"
      c. 调用masscan执行扫描:
      • 构造masscan命令行参数
      • 启动masscan进程
      • 监控进程输出和退出状态
        d. 实时解析masscan输出,更新Redis中的进度
        e. 扫描完成后:
      • 更新Redis中任务状态为"completed"
      • 将完整结果发布到另一个Kafka主题(如scan-result

4. 关键技术点

  1. Masscan集成

    • 使用exec.Command启动masscan进程
    • 实时解析masscan的标准输出和错误输出
    • 根据输出计算扫描进度
  2. 错误处理

    • 处理无效IP格式
    • 处理masscan执行失败
    • 处理Kafka/Redis连接问题
  3. 日志记录

    • 记录服务端操作日志
    • 记录任务执行状态变化
    • 记录错误信息

5. 测试验证思路

  1. 启动两个服务端实例
  2. 使用客户端提交两个不同网段的任务
  3. 观察:
    • 两个服务端的日志输出
    • 任务是否被均匀分配(一个服务端处理一个任务)
    • 扫描进度是否正确更新
    • 最终结果是否正确输出

6. 扩展考虑

  1. 任务优先级

    • 可以在任务中添加优先级字段
    • 服务端根据优先级处理任务
  2. 任务超时

    • 添加任务超时机制
    • 超时后重新分配任务
  3. 结果存储

    • 可以考虑将结果存入数据库而不仅是Kafka
  4. 水平扩展

    • 设计支持更多服务端实例的扩展方案

这个设计实现了基本的分布式扫描任务调度系统,核心是利用Kafka的消息队列特性实现任务分发,通过消费者组机制实现负载均衡,使用Redis作为共享状态存储。

实现:

        项目结构:
        

         kafka:
         consumer   
        
package kafka

import (
	"context"
	"errors"
	"fmt"
	"github.com/IBM/sarama"
	"log"
	"sync"
)

type MessageHandler func([]byte) error

type SaramaConsumer struct {
	client    sarama.ConsumerGroup
	handlers  map[string]MessageHandler
	ready     chan bool
	ctx       context.Context
	cancel    context.CancelFunc
	consuming sync.WaitGroup
	memberId  string
	groupId   string
}

func NewKafkaConsumer(brokers []string, groupId string, topic []string) (*SaramaConsumer, error) {
	config := sarama.NewConfig()
	config.Version = sarama.V2_5_0_0                      // 使用适当的 Kafka 版本
	config.Consumer.Offsets.Initial = sarama.OffsetOldest //
	config.Consumer.Group.Rebalance.GroupStrategies = []sarama.BalanceStrategy{sarama.NewBalanceStrategyRoundRobin()}

	client, err := sarama.NewConsumerGroup(brokers, groupId, config)
	if err != nil {
		return nil, fmt.Errorf("failed to create consumer: %v", err)
	}

	ctx, cancelFunc := context.WithCancel(context.Background())

	return &SaramaConsumer{
		client:   client,
		handlers: make(map[string]MessageHandler),
		ready:    make(chan bool),
		ctx:      ctx,
		cancel:   cancelFunc,
		groupId:  groupId,
	}, nil
}

func (sc *SaramaConsumer) RegisterHandler(topic string, handler MessageHandler) {
	sc.handlers[topic] = handler
}

func (sc *SaramaConsumer) StartConsuming(topics []string) {
	sc.consuming.Add(1)
	go func() {
		defer sc.consuming.Done()
		for {
			if err := sc.client.Consume(sc.ctx, topics, sc); err != nil {
				if errors.Is(err, sarama.ErrClosedConsumerGroup) {
					return // 正常关闭
				}
				log.Printf("Error from consumer: %v", err)
			}

			if sc.ctx.Err() != nil {
				return
			}
			sc.ready = make(chan bool)
		}
	}()
	<-sc.ready
	log.Println("Sarama consumer up and running!...")
}

func (sc *SaramaConsumer) Setup(session sarama.ConsumerGroupSession) error {
	sc.memberId = session.MemberID()
	claims := session.Claims()
	log.Printf("Rebalance: Consumer [%s] SETUP - Assigned partitions: %v",
		sc.memberId, claims)
	close(sc.ready)
	return nil
}
func (sc *SaramaConsumer) Cleanup(session sarama.ConsumerGroupSession) error {
	claims := session.Claims()
	log.Printf("Rebalance: Consumer [%s] CLEANUP - Releasing partitions: %v",
		sc.memberId, claims)
	return nil
}

func (sc *SaramaConsumer) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
	log.Printf("Rebalance: Consumer [%s] STARTED consuming partition %d (offset %d)",
		sc.memberId, claim.Partition(), claim.InitialOffset())

	defer log.Printf("Rebalance: Consumer [%s] STOPPED consuming partition %d",
		sc.memberId, claim.Partition())
	for message := range claim.Messages() {
		if handler, ok := sc.handlers[message.Topic]; ok {
			if err := handler(message.Value); err != nil {
				log.Printf("Error from consumer: %v", err)
			}
			session.MarkMessage(message, "")
		} else {
			log.Printf("Error from consumer: %v", message.Topic)
		}
	}
	return nil
}

func (sc *SaramaConsumer) Close() error {
	sc.cancel()
	sc.consuming.Wait()
	return sc.client.Close()
}
   producer
package kafka

import (
	"encoding/json"
	"fmt"
	"github.com/IBM/sarama"
	"log"
)

type SaramaProducer struct {
	producer sarama.SyncProducer
}

func NewSaramaProducer(brokers []string) (*SaramaProducer, error) {
	config := sarama.NewConfig()
	config.Producer.RequiredAcks = sarama.WaitForAll              // 等待所有副本确认
	config.Producer.Retry.Max = 5                                 // 重试次数
	config.Producer.Return.Successes = true                       // 需要成功交付的返回
	config.Producer.Partitioner = sarama.NewRoundRobinPartitioner //轮询策略

	producer, err := sarama.NewSyncProducer(brokers, config)
	if err != nil {
		return nil, fmt.Errorf("failed to create Sarama producer: %v", err)
	}
	return &SaramaProducer{producer: producer}, nil
}

func (sp *SaramaProducer) SendMessage(topic string, value interface{}) error {
	jsonValue, err := json.Marshal(value)
	if err != nil {
		return fmt.Errorf("fail to marshal value: %v", err)
	}

	msg := sarama.ProducerMessage{
		Topic: topic,
		Value: sarama.ByteEncoder(jsonValue),
	}
	partition, offset, err := sp.producer.SendMessage(&msg)
	if err != nil {
		return fmt.Errorf("fail to send message: %v", err)
	}
	log.Printf("Message sent to partition %d at offset %d\n", partition, offset)
	return nil
}

func (sp *SaramaProducer) Close() error {
	return sp.producer.Close()
}
main
        server
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
	"week/v3/kafka"
	"week/v3/progress"
	"week/v3/scanner"
	"week/v3/task"
)

type Server struct {
	consumer        *kafka.SaramaConsumer
	producer        *kafka.SaramaProducer
	progressTracker *progress.ProgressTracker
	scanner         *scanner.MasscanExecutor
}

func NewServer(brokers []string, groupId string, topic []string, redisAddr string) (*Server, error) {
	consumer, err := kafka.NewKafkaConsumer(brokers, groupId, topic)
	if err != nil {
		return nil, fmt.Errorf("failed to create Kafka consumer: %w", err)
	}
	producer, err := kafka.NewSaramaProducer(brokers)
	if err != nil {
		return nil, fmt.Errorf("failed to create Kafka producer: %w", err)
	}
	tracker := progress.NewProgressTracker(redisAddr)

	return &Server{
		consumer:        consumer,
		producer:        producer,
		progressTracker: tracker,
		scanner:         scanner.NewMasscanExecutor(),
	}, nil
}

func (s *Server) Start(ctx context.Context) {
	s.consumer.RegisterHandler("scan-tasks", func(msg []byte) error {
		var t task.Task
		if err := json.Unmarshal(msg, &t); err != nil {
			return fmt.Errorf("failed to unmarshal task: %v", err)
		}
		log.Printf(" Received task: %+v\n", t)

		// 更新任务开始状态
		if err := s.progressTracker.UpdateProgress(ctx, t.TaskId, 0); err != nil {
			return fmt.Errorf("failed to update progress: %v", err)
		}

		// 执行 masscan 扫描
		resultChan, errChan := s.scanner.Execute(ctx, t.IPRange, t.Ports, t.BandWidth)
		log.Println("Masscan Execute called")

		select {
		case results := <-resultChan:
			log.Printf(" Scan complete. %d results.\n", len(results))

			for _, result := range results {
				scanResult := struct {
					TaskID string `json:"task_id"`
					scanner.ScanResult
					Timestamp int64 `json:"timestamp"`
				}{
					TaskID:     t.TaskId,
					ScanResult: result,
					Timestamp:  time.Now().Unix(),
				}

				// 发到 scan-result topic
				jsonResult, err := json.Marshal(scanResult)
				if err != nil {
					return fmt.Errorf("failed to marshal scan result: %v", err)
				}
				if err := s.producer.SendMessage("scan-result", jsonResult); err != nil {
					log.Printf(" Failed to send scan result to Kafka: %v", err)
				} else {
					log.Printf(" Sent scan result: %+v", scanResult)
				}
			}

			// 更新任务完成状态
			if err := s.progressTracker.UpdateProgress(ctx, t.TaskId, 100); err != nil {
				return fmt.Errorf("failed to update progress: %v", err)
			}

		case err := <-errChan:
			log.Printf(" Scan error: %v\n", err)

			// 更新任务失败状态
			if err := s.progressTracker.UpdateProgress(ctx, t.TaskId, 0); err != nil {
				return fmt.Errorf("failed to update progress: %v", err)
			}
			return fmt.Errorf("processing task failed: %v", err)
		}

		return nil
	})

	// 启动 Kafka 消费
	go s.consumer.StartConsuming([]string{"scan-tasks"})
	log.Println(" Server is running and waiting for tasks...")

	// 等待退出信号
	<-ctx.Done()
	log.Println(" Shutting down...")

	if err := s.consumer.Close(); err != nil {
		log.Printf("Error closing consumer: %v", err)
	}
	if err := s.producer.Close(); err != nil {
		log.Printf("Error closing producer: %v", err)
	}
	if err := s.progressTracker.Close(); err != nil {
		log.Printf("Error closing progress tracker: %v", err)
	}
	log.Println(" Server shutdown complete")
}

func main() {
	if len(os.Args) < 2 {
		log.Fatal("Usage: server <consumer-group-id>")
	}
	groupID := os.Args[1]
	brokers := []string{"localhost:9092"}
	redisAddr := "localhost:6379"

	server, err := NewServer(brokers, groupID, []string{"scan-tasks"}, redisAddr)
	if err != nil {
		log.Fatalf("Failed to create server: %v", err)
	}

	// 监听中断信号
	ctx, cancel := context.WithCancel(context.Background())
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		<-sigChan
		cancel()
	}()

	server.Start(ctx)
}
client
package main

import (
	"flag"
	"fmt"
	"log"
	"strings"
	"week/v3/task"
)

func main() {
	ipRange := flag.String("ip", "", "ip地址")
	ports := flag.String("ports", "", "端口范围,如 80,443")
	bandwidth := flag.String("bandwidth", "1000", "扫描带宽")
	kafkaBroker := flag.String("kafka", "localhost:9092", "kafka broker 地址")
	topic := flag.String("topic", "scan-tasks", "kafka topic")
	flag.Parse()

	if *ipRange == "" || *ports == "" {
		fmt.Println("必须指定 ip 和 ports 参数")
		flag.PrintDefaults()
		return
	}

	brokers := strings.Split(*kafkaBroker, ",")
	taskManager, err := task.NewTaskManager(*topic, brokers)
	if err != nil {
		log.Fatalf("Failed to create task manager: %v", err)
	}
	defer taskManager.Close()

	taskID, err := taskManager.SubmitTask(*ipRange, *ports, *bandwidth)
	if err != nil {
		log.Fatalf("Failed to submit task: %v", err)
	}

	fmt.Printf(" Task submitted successfully. Task ID: %s\n", taskID)
}
progress
package progress

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	"time"
)

type Progress struct {
	TaskID    string  `json:"task_id"`
	Progress  float64 `json:"progress"`
	Status    string  `json:"status"`
	Timestamp int64   `json:"timestamp"`
}

type ProgressTracker struct {
	redisClient *redis.Client
}

func NewProgressTracker(redisAddr string) *ProgressTracker {
	return &ProgressTracker{
		redisClient: redis.NewClient(&redis.Options{
			Addr: redisAddr,
		}),
	}
}

func (pt *ProgressTracker) UpdateProgress(ctx context.Context, taskID string, progress float64) error {
	p := Progress{
		Progress: progress,
	}
	jsonData, err := json.Marshal(p)
	if err != nil {
		return fmt.Errorf("failed to marshal progress: %v", err)
	}

	err = pt.redisClient.Set(ctx, taskID, string(jsonData), time.Hour).Err()
	if err != nil {
		return fmt.Errorf("failed to update progress tracker: %v", err)
	}
	return nil
}

func (pt *ProgressTracker) Close() error {
	return pt.redisClient.Close()
}
scanner
 
package scanner

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"os/exec"
)

type ScanResult struct {
	IP     string `json:"ip"`
	Port   string `json:"port"`
	Status string `json:"status"`
}

type MasscanExecutor struct{}

func NewMasscanExecutor() *MasscanExecutor {
	return &MasscanExecutor{}
}

func (me *MasscanExecutor) Execute(ctx context.Context, ipRange, ports, bandWidth string) (<-chan []ScanResult, <-chan error) {
	resultChan := make(chan []ScanResult)
	errorChan := make(chan error)

	go func() {
		defer close(resultChan)
		defer close(errorChan)

		args := []string{
			"-p", ports,
			"--rate", bandWidth,
			"-oJ", "-",
			ipRange,
		}
		cmd := exec.CommandContext(ctx, "masscan", args...)
		output, err := cmd.CombinedOutput()
		if err != nil {
			errorChan <- fmt.Errorf("masscan command error: %v, output: %s", err, string(output))
			return
		}

		// 打印输出用于调试
		fmt.Printf("masscan output:\n%s\n", string(output))

		scanner := bufio.NewScanner(bytes.NewReader(output))
		var results []ScanResult
		for scanner.Scan() {
			line := scanner.Bytes()
			// 过滤非JSON行
			if len(line) == 0 || (line[0] != '{' && line[0] != '[') {
				continue
			}
			var r struct {
				IP    string `json:"ip"`
				Ports []struct {
					Port   int    `json:"port"`
					Status string `json:"status"`
				} `json:"ports"`
			}
			if err := json.Unmarshal(line, &r); err != nil {
				errorChan <- fmt.Errorf("failed to unmarshal line: %v", err)
				return
			}
			for _, p := range r.Ports {
				results = append(results, ScanResult{
					IP:     r.IP,
					Port:   fmt.Sprintf("%d", p.Port),
					Status: p.Status,
				})
			}
		}
		if err := scanner.Err(); err != nil {
			errorChan <- fmt.Errorf("scanner error: %v", err)
			return
		}

		resultChan <- results
	}()

	return resultChan, errorChan
}
task
package task

import (
	"encoding/json"
	"fmt"
	"github.com/IBM/sarama"
	"github.com/google/uuid"
	"strings"
)

type Task struct {
	TaskId    string `json:"task_id"`
	IPRange   string `json:"ip_range"`
	Ports     string `json:"ports"`
	BandWidth string `json:"band_width"`
}

type TaskManager struct {
	producer sarama.SyncProducer
	topic    string
}

func NewTaskManager(topic string, brokers []string) (*TaskManager, error) {
	config := sarama.NewConfig()
	config.Producer.RequiredAcks = sarama.WaitForAll
	config.Producer.Retry.Max = 5
	config.Producer.Return.Successes = true
	config.Producer.Partitioner = sarama.NewRoundRobinPartitioner

	producer, err := sarama.NewSyncProducer(brokers, config)
	if err != nil {
		return nil, err
	}
	return &TaskManager{producer: producer, topic: topic}, nil
}

func (tm *TaskManager) SubmitTask(ipRange, ports, bandWidth string) ([]string, error) {
	ipList := strings.Split(ipRange, ",")
	var taskIDs []string

	for _, ip := range ipList {
		task := &Task{
			TaskId:    uuid.New().String(),
			IPRange:   ip, // 这里只发送一个 IP
			Ports:     ports,
			BandWidth: bandWidth,
		}

		jsonTask, err := json.Marshal(task)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal task: %v", err)
		}

		msg := sarama.ProducerMessage{
			Topic: tm.topic,
			Value: sarama.ByteEncoder(jsonTask),
		}

		_, _, err = tm.producer.SendMessage(&msg)
		if err != nil {
			return nil, fmt.Errorf("failed to send message: %v", err)
		}

		taskIDs = append(taskIDs, task.TaskId)
	}

	return taskIDs, nil
}

func (tm *TaskManager) Close() error {
	return tm.producer.Close()
}
docker-compose
version: '3.8'

services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.5.0
    container_name: zookeeper
    ports:
      - "2181:2181"
    environment:
      TZ: Asia/Shanghai
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000

  kafka:
    image: confluentinc/cp-kafka:7.5.0
    container_name: kafka
    ports:
      - "9092:9092"
    depends_on:
      - zookeeper
    environment:
      TZ: Asia/Shanghai
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  redis:
    image: redis:7.2
    container_name: redis
    ports:
      - "6379:6379"
    environment:
      TZ: Asia/Shanghai
    volumes:
      - redis-data:/data

volumes:
  redis-data:

验证

        起两个server端,一个client。先进入docker中起的kafka服务

docker exec -it kafka bash

查看kafka中的topic,在服务端和客户端未开启时,kafka中无topic,启动后注册三个topic。

scan-tasks存放扫描任务,scan-results存放扫描结果,__consumer_offsets存放偏移量

因为要两个server负载平衡,所以topic中要有两个partition,

kafka-topics --alter --bootstrap-server localhost:9092 --topic scan-tasks --partitions 2
kafka-topics --describe --bootstrap-server localhost:9092 --topic scan-tasks


使用命令行工具起两个server端

启动一个client下发命令

服务端能均匀消费信息。

感心趣可以自行查看扫描结果和写入redis的进度。

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

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

相关文章

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …