当前位置: 首页 > news >正文

.Net Core对于RabbitMQ封装分布式事件总线

首先我们需要了解到分布式事件总线是什么;

分布式事件总线是一种在分布式系统中提供事件通知、订阅和发布机制的技术。它允许多个组件或微服务之间的协作和通信,而无需直接耦合或了解彼此的实现细节。通过事件总线,组件或微服务可以通过发布或订阅事件来实现异步通信。

例如,当一个组件完成了某项任务并生成了一个事件,它可以通过事件总线发布该事件。其他相关组件可以通过订阅该事件来接收通知,并做出相应的反应。这样,组件之间的耦合就被减轻了,同时也提高了系统的可维护性和可扩展性。

然后了解一下RabbitMQ

RabbitMQ是一种开源的消息代理和队列管理系统,用于在分布式系统中进行异步通信。它的主要功能是接收和分发消息,并且支持多种协议,包括AMQP,STOMP,MQTT等。RabbitMQ通过一个中间层,可以把消息发送者与消息接收者隔离开来,因此消息发送者和消息接收者并不需要在同一时刻在线,并且也不需要互相知道对方的地址。

  1. RabbitMQ的主要功能包括:

    1. 消息存储:RabbitMQ可以将消息存储在内存或硬盘上,以保证消息的完整性。

    2. 消息路由:RabbitMQ支持消息的路由功能,可以将消息从生产者发送到消费者。

    3. 消息投递:RabbitMQ提供了多种消息投递策略,包括简单模式、工作队列、发布/订阅模式等。

    4. 可靠性:RabbitMQ保证消息的可靠性,即消息不会丢失、不重复、按顺序投递。

    5. 可扩展性:RabbitMQ支持水平扩展,可以通过增加节点来扩展系统的处理能力。

本文将讲解使用RabbitMQ实现分布式事件

实现我们创建一个EventsBus.Contract的类库项目,用于提供基本接口,以支持其他实现

在项目中添加以下依赖引用,并且记得添加EventsBus.Contract项目引用

<ItemGroup><PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /><PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" /><PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" /><PackageReference Include="RabbitMQ.Client" Version="6.4.0" />
</ItemGroup>

创建项目完成以后分别创建EventsBusOptions.cs,IEventsBusHandle.cs,RabbitMQEventsManage.cs,ILoadEventBus.cs ,提供我们的分布式事件基本接口定义

EventsBusOptions.cs

namespace EventsBus.Contract;public class EventsBusOptions
{/// <summary>/// 接收时异常事件/// </summary>public static Action<IServiceProvider, Exception,byte[]>? ReceiveExceptionEvent;
}

IEventsBusHandle.cs

namespace EventsBus.Contract;public interface IEventsBusHandle<in TEto> where TEto : class
{Task HandleAsync(TEto eventData);
}

ILoadEventBus.cs

namespace EventsBus.Contract;public interface ILoadEventBus
{/// <summary>/// 发布事件/// </summary>/// <param name="eto"></param>/// <typeparam name="TEto"></typeparam>/// <returns></returns>Task PushAsync<TEto>(TEto eto) where TEto : class;
}

EventsBusAttribute.cs:用于Eto(Eto 是我们按照约定使用的Event Transfer Objects(事件传输对象)的后缀. s虽然这不是必需的,但我们发现识别这样的事件类很有用(就像应用层上的DTO 一样))的名称,对应到RabbitMQ的通道

namespace EventsBus.RabbitMQ;[AttributeUsage(AttributeTargets.Class)]
public class EventsBusAttribute : Attribute
{public readonly string Name;public EventsBusAttribute(string name){Name = name;}
}

然后可以创建我们的RabbitMQ实现了,创建EventsBus.RabbitMQ类库项目,用于编写EventsBus.ContractRabbitMQ实现

创建项目完成以后分别创建Extensions\EventsBusRabbitMQExtensions.cs,Options\RabbitMQOptions.cs,EventsBusAttribute.cs,,RabbitMQFactory.cs,RabbitMQLoadEventBus.cs

Extensions\EventsBusRabbitMQExtensions.cs:提供我们RabbitMQ扩展方法让使用者更轻松的注入,命名空间使用Microsoft.Extensions.DependencyInjection,这样就在注入的时候减少过度使用命名空间了

using EventsBus.Contract;
using EventsBus.RabbitMQ;
using EventsBus.RabbitMQ.Options;
using Microsoft.Extensions.Configuration;namespace Microsoft.Extensions.DependencyInjection;public static class EventsBusRabbitMQExtensions
{public static IServiceCollection AddEventsBusRabbitMQ(this IServiceCollection services,IConfiguration configuration){services.AddSingleton<RabbitMQFactory>();services.AddSingleton(typeof(RabbitMQEventsManage<>));services.Configure<RabbitMQOptions>(configuration.GetSection(nameof(RabbitMQOptions)));services.AddSingleton<ILoadEventBus, RabbitMQLoadEventBus>();return services;}
}

Options\RabbitMQOptions.cs:提供基本的Options 读取配置文件中并且注入,services.Configure<RabbitMQOptions>(configuration.GetSection(nameof(RabbitMQOptions)));的方法是读取IConfiguration的名称为RabbitMQOptions的配置东西,映射到Options中,具体使用往下看。

using RabbitMQ.Client;namespace EventsBus.RabbitMQ.Options;public class RabbitMQOptions
{/// <summary>/// 要连接的端口。 <see cref="AmqpTcpEndpoint.UseDefaultPort"/>/// 指示应使用的协议的缺省值。/// </summary>public int Port { get; set; } = AmqpTcpEndpoint.UseDefaultPort;/// <summary>/// 地址/// </summary>public string HostName { get; set; }/// <summary>/// 账号/// </summary>public string UserName { get; set; }/// <summary>/// 密码/// </summary>public string Password { get; set; }
}

RabbitMQEventsManage.cs:用于管理RabbitMQ的数据接收,并且将数据传输到指定的事件处理程序

using System.Reflection;
using System.Text.Json;
using EventsBus.Contract;
using Microsoft.Extensions.DependencyInjection;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;namespace EventsBus.RabbitMQ;public class RabbitMQEventsManage<TEto> where TEto : class
{private readonly IServiceProvider _serviceProvider;private readonly RabbitMQFactory _rabbitMqFactory;public RabbitMQEventsManage(IServiceProvider serviceProvider, RabbitMQFactory rabbitMqFactory){_serviceProvider = serviceProvider;_rabbitMqFactory = rabbitMqFactory;_ = Task.Run(Start);}private void Start(){var channel = _rabbitMqFactory.CreateRabbitMQ();var eventBus = typeof(TEto).GetCustomAttribute<EventsBusAttribute>();var name = eventBus?.Name ?? typeof(TEto).Name;channel.QueueDeclare(name, false, false, false, null);var consumer = new EventingBasicConsumer(channel); //消费者channel.BasicConsume(name, true, consumer); //消费消息consumer.Received += async (model, ea) =>{var bytes = ea.Body.ToArray();try{// 这样就可以实现多个订阅var events = _serviceProvider.GetServices<IEventsBusHandle<TEto>>();foreach (var handle in events){await handle?.HandleAsync(JsonSerializer.Deserialize<TEto>(bytes));}}catch (Exception e){EventsBusOptions.ReceiveExceptionEvent?.Invoke(_serviceProvider, e, bytes);}};}
}

RabbitMQFactory.cs:提供RabbitMQ链接工厂,在这里你可以自己去定义和管理RabbitMQ工厂

using EventsBus.RabbitMQ.Options;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;namespace EventsBus.RabbitMQ;public class RabbitMQFactory : IDisposable
{private readonly RabbitMQOptions _options;private readonly ConnectionFactory _factory;private IConnection? _connection;public RabbitMQFactory(IOptions<RabbitMQOptions> options){_options = options?.Value;// 将Options中的参数添加到ConnectionFactory_factory = new ConnectionFactory{HostName = _options.HostName,UserName = _options.UserName,Password = _options.Password,Port = _options.Port};}public IModel CreateRabbitMQ(){// 当第一次创建RabbitMQ的时候进行链接_connection ??= _factory.CreateConnection();return _connection.CreateModel();}public void Dispose(){_connection?.Dispose();}
}

RabbitMQLoadEventBus.cs:用于实现ILoadEventBus.cs通过ILoadEventBus发布事件RabbitMQLoadEventBus.cs是RabbitMQ的实现

using System.Reflection;
using System.Text.Json;
using EventsBus.Contract;
using Microsoft.Extensions.DependencyInjection;namespace EventsBus.RabbitMQ;public class RabbitMQLoadEventBus : ILoadEventBus
{private readonly IServiceProvider _serviceProvider;private readonly RabbitMQFactory _rabbitMqFactory;public RabbitMQLoadEventBus(IServiceProvider serviceProvider, RabbitMQFactory rabbitMqFactory){_serviceProvider = serviceProvider;_rabbitMqFactory = rabbitMqFactory;}public async Task PushAsync<TEto>(TEto eto) where TEto : class{//创建一个通道//这里Rabbit的玩法就是一个通道channel下包含多个队列Queueusing var channel = _rabbitMqFactory.CreateRabbitMQ();// 获取Eto中的EventsBusAttribute特性,获取名称,如果没有默认使用类名称var eventBus = typeof(TEto).GetCustomAttribute<EventsBusAttribute>();var name = eventBus?.Name ?? typeof(TEto).Name;// 使用获取的名称创建一个通道channel.QueueDeclare(name, false, false, false, null);var properties = channel.CreateBasicProperties();properties.DeliveryMode = 1;// 将数据序列号,然后发布channel.BasicPublish("", name, false, properties, JsonSerializer.SerializeToUtf8Bytes(eto)); //生产消息// 让其注入启动管理服务,RabbitMQEventsManage需要手动激活,由于RabbitMQEventsManage是单例,只有第一次激活才有效,var eventsManage = _serviceProvider.GetService<RabbitMQEventsManage<TEto>>();await Task.CompletedTask;}
}

在这里我们的RabbitMQ分布式事件就设计完成了,注:这只是简单的一个示例,并未经过大量测试,请勿直接在生产使用;

然后我们需要使用RabbitMQ分布式事件总线工具包

使用RabbitMQ分布式事件总线的示例

首先我们需要准备一个RabbitMQ,可以在官网自行下载,我就先使用简单的,通过docker compose启动一个RabbitMQ,下面提供一个compose文件

version: '3.1'
services:rabbitmq:restart: always # 开机自启image: rabbitmq:3.11-management # RabbitMQ使用的镜像container_name: rabbitmq # docker名称hostname: rabbitports:- 5672:5672 # 只是RabbitMQ SDK使用的端口- 15672:15672 # 这是RabbitMQ管理界面使用的端口environment:TZ: Asia/Shanghai # 设置RabbitMQ时区RABBITMQ_DEFAULT_USER: token # rabbitMQ账号RABBITMQ_DEFAULT_PASS: dd666666 # rabbitMQ密码volumes:- ./data:/var/lib/rabbitmq

启动以后我们创建一个WebApi项目,项目名称Demo,创建完成打开项目文件添加引用

<Project Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>net7.0</TargetFramework><Nullable>enable</Nullable><ImplicitUsings>enable</ImplicitUsings></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.0" /><PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /></ItemGroup><ItemGroup><!-- 引用RabbitMQ事件总线项目--><ProjectReference Include="..\EventsBus.RabbitMQ\EventsBus.RabbitMQ.csproj" /></ItemGroup></Project>

修改appsettings.json配置文件:将RabbitMQ的配置写上,RabbitMQOptions名称对应在EventsBus.RabbitMQ中的RabbitMQOptions文件

在这里注入的时候将配置注入好了

{"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHosts": "*","RabbitMQOptions": {"HostName": "127.0.0.1","UserName": "token","Password": "dd666666"}
}

创建DemoEto.cs文件:

using EventsBus.RabbitMQ;namespace Demo;[EventsBus("Demo")]
public class DemoEto
{public int Size { get; set; }public string Value { get; set; }
}

创建DemoEventsBusHandle.cs文件:这里是订阅DemoEto事件,相当于是DemoEto的处理程序

using System.Text.Json;
using EventsBus.Contract;namespace Demo;/// <summary>
/// 事件处理服务,相当于订阅事件
/// </summary>
public class DemoEventsBusHandle : IEventsBusHandle<DemoEto>
{public async Task HandleAsync(DemoEto eventData){Console.WriteLine($"DemoEventsBusHandle: {JsonSerializer.Serialize(eventData)}");await Task.CompletedTask;}
}

打开Program.cs 修改代码: 在这里注入了事件总线服务,和我们的事件处理服务

using Demo;
using EventsBus.Contract;var builder = WebApplication.CreateBuilder(args);builder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();// 注入事件处理服务
builder.Services.AddSingleton(typeof(IEventsBusHandle<DemoEto>),typeof(DemoEventsBusHandle));// 注入RabbitMQ服务
builder.Services.AddEventsBusRabbitMQ(builder.Configuration);var app = builder.Build();// 只有在Development显示Swagger
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}// 强制Https
app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();

创建Controllers\EventBusController.cs控制器:我们在控制器中注入了ILoadEventBus ,通过调用接口实现发布事件;

using EventsBus.Contract;
using Microsoft.AspNetCore.Mvc;namespace Demo.Controllers;[ApiController]
[Route("[controller]")]
public class EventBusController : ControllerBase
{private readonly ILoadEventBus _loadEventBus;public EventBusController(ILoadEventBus loadEventBus){_loadEventBus = loadEventBus;}/// <summary>/// 发送信息/// </summary>/// <param name="eto"></param>[HttpPost]public async Task Send(DemoEto eto){await _loadEventBus.PushAsync(eto);}
}

然后我们启动程序会打开Swagger调试界面:

然后我们发送一下事件:

我们可以看到,在数据发送的时候也同时订阅到了我们的信息,也可以通过分布式事件总线限流等实现, 

相关文章:

.Net Core对于RabbitMQ封装分布式事件总线

首先我们需要了解到分布式事件总线是什么&#xff1b; 分布式事件总线是一种在分布式系统中提供事件通知、订阅和发布机制的技术。它允许多个组件或微服务之间的协作和通信&#xff0c;而无需直接耦合或了解彼此的实现细节。通过事件总线&#xff0c;组件或微服务可以通过发布…...

GPIO功能描述

GPIO 文章目录 GPIO1. 功能描述1.1 OSCI/OSCO 引脚1.3 HSEIN/HSEOUT引脚1.2 Bit-Band1.4 VRTCAFx引脚1.5 EWKUPx引脚1.6 QSPI0 引脚1.7 LVDIN引脚1.8 SARADC引脚1.9 ADCIN引脚2. 测试项描述2.1 PAD Location2.2 LBOR和BOR复位2.3 驱动能力2.4 模拟态\高阻态2.5 SWD\JTAG2.6 输出…...

指派问题与匈牙利法讲解

指派问题概述&#xff1a;实际中&#xff0c;会遇到这样的问题&#xff0c;有n项不同的任务&#xff0c;需要n个人分别完成其中的1项&#xff0c;每个人完成任务的时间不一样。于是就有一个问题&#xff0c;如何分配任务使得花费时间最少。通俗来讲&#xff0c;就是n*n矩阵中&a…...

day5——冒泡排序,选择排序和插入排序的学习

选择排序冒泡排序插入排序 选择排序 选择排序的基本思路就是&#xff1a; 首先假定第一个的下表为所有元素中最小的一个&#xff0c; 然后用后面的每一个元素跟这个元素进行比较&#xff0c; 如果后面的元素比这个元素更小一点&#xff0c; 那么就将找到的最小的元素的下标和…...

Windows 数据类型 (Windows Data Types)

参考&#xff1a;https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types 要求 要求值最低受支持的客户端Windows XP [仅限桌面应用]最低受支持的服务器Windows Server 2003 [仅限桌面应用]HeaderBaseTsd.h;WinDef.h;WinNT.hAPIENTRY 系统函数的调用约…...

九龙证券|本周5只新股申购,特斯拉、蔚来、理想的供应商来A股了!

据现在组织&#xff0c;2月13日到17日共有5只新股申购&#xff0c;其间上证主板2只&#xff0c;深证主板1只&#xff0c;北交所2只。 2月14日发动打新的深证主板新股多利科技成立于2010年&#xff0c;是一家专心于轿车冲压零部件及相关模具的开发、出产与出售的企业。从2020年…...

设计模式(持续更新)

本文主要是记录java的设计模式在实际工作中的应用案例&#xff0c;或者是对设计模式的个人理解及备忘 一、单例模式Singleton 工作场景&#xff08;静态类&#xff09;&#xff1a; 在外部系统对接中&#xff0c;需要调用外部系统A的接口&#xff0c;但是接口是有身份校验的…...

Prometheus 告警规则

Prometheus 告警规则 Prometheus官方内置的第三方报警通知包括&#xff1a;邮件、 即时通讯软件&#xff08;如Slack、Hipchat&#xff09;、移动应用消息推送(如Pushover)和自动化运维工具&#xff08;例如&#xff1a;Pagerduty、Opsgenie、Victorops&#xff09; Promethe…...

mulesoft MCIA 破釜沉舟备考 2023.02.13.02

mulesoft MCIA 破釜沉舟备考 2023.02.13.03 1. According to MuleSoft, which deployment charcateristic applies to a microservices application architecture?2. A mule application designed to fulfil two requirements3. A mule application must periodically process…...

获取DLL运行时路径的方法

之前项目中发现的问题&#xff0c;记录下解决方案1. 问题背景OVVRNTool项目中&#xff0c;底层图像基本操作功能由DLL库函数提供&#xff0c;上层基于DLL封装了两个应用CMD和GUI&#xff0c;然后通过Qt打包分发&#xff1b;发布是直接采用绿色免安装的方式打包&#xff0c;具体…...

“华为杯”研究生数学建模竞赛2006年-【华为杯】D题:学生面试中教师安排的优化与算法(附获奖论文)

赛题描述 高校自主招生是高考改革中的一项新生事物,现在仍处于探索阶段。某高校拟在全面衡量考生的高中学习成绩及综合表现后再采用专家面试的方式决定录取与否。该校在今年自主招生中,经过初选合格进入面试的考生有N人,拟聘请老师M人。每位学生要分别接受4位老师(简称该学…...

【JavaScript】复习 【对象参数】【函数参数】

js不会检查任何参数类型&#xff0c;任何参数都可以作为参数传递 1、对象参数 改变量随便改&#xff0c;改对象要看这个对象是不是有多个变量同时指向这个对象 const 用来定义常量&#xff0c;只能赋值一次。 变量------->对象------->属性 被const修饰的对象 …...

如何批量提取文件名到excel表格?

批量提取文件名到excel表格&#xff1f;关于这个问题相信很多人都遇到过&#xff0c;大多数人在第一次碰到的时候都不知道如何下手&#xff0c;大家都会立即在百度里面搜索相关方法教程&#xff0c;小编也试着搜索了一下&#xff0c;发现找到的很多方法都大同小异&#xff0c;需…...

CUDA线程层次一文搞懂|参加CUDA线上训练营

设备术语 Host&#xff1a;CPU 和 内存 (host memory)Device&#xff1a;GPU 和显存 (device memory) CUDA 线程层次 CUDA 线程层次分为&#xff1a; Thread 所有线程执行相同的核函数并行执行 Thread Block 执行在一个 Streaming Multiprocessor &#xff08;SM&#xff09…...

Linux文件默认权限:umask

umask就是指定目前用户在建立文件或目录时候的权限默认值 查看方式有两种&#xff1a;一种可以直接输入umask&#xff0c;就可以看到数字类型的权限设置值&#xff0c;一种则是加入umask后加入-S&#xff08;Symbolic&#xff09;选项&#xff0c;就会以符号类型的方式来显示出…...

SonicWall:请立即修复SMA 1000 漏洞

近日&#xff0c;网络安全供应商SonicWall发布了关于安全移动访问 (SMA) 1000设备的三个安全漏洞的紧急报告&#xff0c;其中包括一个高威胁性的身份验证绕过漏洞。SonicWall指出&#xff0c;攻击者可以利用这些漏洞绕过授权&#xff0c;并可能破坏易受攻击的设备。 从报告中可…...

基于VS调试分析 + 堆栈观察问题代码段

文章目录问题代码段1 —— 阶乘之和问题代码段2 —— 越界的危害① 发现问题② 分析问题③ 思考问题【⭐堆栈原理⭐】④ 解决问题【DeBug与Release】&#x1f468;程序员与测试人员&#x1f469;✒总结与提炼问题代码段1 —— 阶乘之和 先来看一道C语言中比较基础的题目&#x…...

QFramework框架学习

主要学习内容TypeEventSystemActionKitTimer类1、TypeEventSystem-适用于一个条件触发&#xff0c;多个组件响应的情况例如&#xff1a;动物园系统中&#xff0c;点击肉食动物按钮&#xff0c;动物园中有肉食属性的动物都进行显示。步骤&#xff1a;1、动物自身脚本上进行判断是…...

移动OA系统,联动企业协作让办公高效无间断

移动oa系统&#xff0c;近年来随着企业办公节奏的变化及人们个性化办公需求的增加迎来了快速发展。一方面&#xff0c;它兼具OA系统诸多优势&#xff0c;既凝聚了企业基础管理工作&#xff0c;联动了企业协作、沟通交流&#xff0c;又进一步提高了企业的综合实力与市场竞争力。…...

结构体熟练掌握--实现通讯录

魔王的介绍&#xff1a;&#x1f636;‍&#x1f32b;️一名双非本科大一小白。魔王的目标&#xff1a;&#x1f92f;努力赶上周围卷王的脚步。魔王的主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王.&#x1f525;&#x1f525;&#x1f525; ❤️‍&#x1…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

电脑桌面太单调,用Python写一个桌面小宠物应用。

下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡&#xff0c;可以响应鼠标点击&#xff0c;并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...

大模型真的像人一样“思考”和“理解”吗?​

Yann LeCun 新研究的核心探讨&#xff1a;大语言模型&#xff08;LLM&#xff09;的“理解”和“思考”方式与人类认知的根本差异。 核心问题&#xff1a;大模型真的像人一样“思考”和“理解”吗&#xff1f; 人类的思考方式&#xff1a; 你的大脑是个超级整理师。面对海量信…...

react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)

之前都是使用react-pdf来渲染pdf文件&#xff0c;这次有个需求是要兼容xp环境&#xff0c;xp上chrome最高支持到49&#xff0c;虽然说iframe或者embed都可以实现预览pdf&#xff0c;但为了后续的定制化需求&#xff0c;还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...

循环语句之while

While语句包括一个循环条件和一段代码块&#xff0c;只要条件为真&#xff0c;就不断 循环执行代码块。 1 2 3 while (条件) { 语句 ; } var i 0; while (i < 100) {console.log(i 当前为&#xff1a; i); i i 1; } 下面的例子是一个无限循环&#xff0c;因…...