学Dapr Actors 看这篇就够了

介绍

Actor模式将Actor描述为最低级别的“计算单元”。换句话说,您在一个独立的单元(称为actor)中编写代码,该单元接收消息并一次处理一个消息,没有任何并发或线程。

再换句话说,根据ActorId划分独立计算单元后,相同的ActorId重入要排队,可以理解为lock(ActorId)

:这里有个反例,就是重入性的引入,这个概念目前还是Preview,它允许同一个链内可以重复进入,判断的标准不止是ActorId这么简单,即自己调自己是被允许的。这个默认是关闭的,需要手动开启,即默认不允许自己调自己

当您的代码处理一条消息时,它可以向其他参与者发送一条或多条消息,或者创建新的参与者。底层运行时管理每个参与者运行的方式、时间和地点,并在参与者之间路由消息。

大量的Actor可以同时执行,Actor彼此独立执行。

Dapr 包含一个运行时,它专门实现了 Virtual Actor 模式。 通过 Dapr 的实现,您可以根据 Actor 模型编写 Dapr Actor,而 Dapr 利用底层平台提供的可扩展性和可靠性保证。  

什么时候用Actors

Actor 设计模式非常适合许多分布式系统问题和场景,但您首先应该考虑的是该模式的约束。一般来说,如果出现以下情况,请考虑使用Actors模式来为您的问题或场景建模:

  • 您的问题空间涉及大量(数千个或更多)小的、独立且孤立的状态和逻辑单元

  • 您希望使用需要与外部组件进行大量交互的单线程对象,包括跨一组Actors查询状态。

  • 您的 Actor 实例会通过发出 I/O 操作来阻塞具有不可预测延迟的调用者。

Dapr Actor

每个Actor都被定义为Actor类型的实例,就像对象是类的实例一样。  例如,可能有一个执行计算器功能的Actor类型,并且可能有许多该类型的Actor分布在集群的各个节点上。每个这样的Actor都由一个Acotr ID唯一标识。  

7ee750843addfa583ba105b8a19f1ee0.png

生命周期

Dapr Actors是虚拟的,这意味着他们的生命周期与他们的内存表现无关。因此,它们不需要显式创建或销毁。Dapr Actors运行时在第一次收到对该Actor ID 的请求时会自动激活该Actor。如果一个Actor在一段时间内没有被使用,Dapr Actors运行时就会对内存中的对象进行垃圾回收。如果稍后需要重新激活,它还将保持对参与者存在的了解。如果稍后需要重新激活,它还将保持对 Actor 的一切原有数据。

调用 Actor 方法和提醒会重置空闲时间,例如提醒触发将使Actor保持活跃。无论Actor是活跃还是不活跃,Actor提醒都会触发,如果为不活跃的Actor触发,它将首先激活演员。Actor 计时器不会重置空闲时间,因此计时器触发不会使 Actor 保持活动状态。计时器仅在Actor处于活动状态时触发。

Reminders 和 Timers 最大的区别就是Reminders会保持Actor的活动状态,而Timers不好会

Dapr 运行时用来查看Actor是否可以被垃圾回收的空闲超时和扫描间隔是可配置的。当 Dapr 运行时调用 Actor 服务以获取支持的 Actor 类型时,可以传递此信息。

由于Virtual Actor模型的存在,这种Virtual Actor生命周期抽象带来了一些注意事项,事实上,Dapr Actors实现有时会偏离这个模型。  

第一次将消息发送到Actor ID时,Actor被自动激活(导致构建Actor对象)。 经过一段时间后,Actor对象将被垃圾回收。被回收后再次使用Actor ID将导致构造一个新的Actor对象。 Actor 的状态比对象的生命周期长,因为状态存储在 Dapr 运行时配置的状态管理组件中。

:Actor被垃圾回收之前,Actor对象是会复用的。这里会导致一个问题,在.Net Actor类中,构造函数在Actor存活期间只会被调用一次。

分发和故障转移

为了提供可扩展性和可靠性,Actor 实例分布在整个集群中,Dapr 根据需要自动将它们从故障节点迁移到健康节点。

Actors 分布在 Actor 服务的实例中,而这些实例分布在集群中的节点之间。 对于给定的Actor类型,每个服务实例都包含一组Actor。

Dapr安置服务(Placement Service)

Dapr Actor 运行时为您管理分发方案和密钥范围设置。这是由Actor Placement 服务完成的。创建服务的新实例时,相应的 Dapr 运行时会注册它可以创建的Actor类型,并且安置服务会计算给定Actor类型的所有实例的分区。每个Actor类型的分区信息表被更新并存储在环境中运行的每个Dapr实例中,并且可以随着Actor服务的新实例的创建和销毁而动态变化。这如下图所示:

a51cafff68fbc7e7772950220a48bd41.png

当客户端调用具有特定ID的Actor(例如,Actor ID 123)时,客户端的 Dapr 实例会Hash Actor类型和 ID,并使用该信息调用可以为特定Actor ID的请求提供服务的相应Dapr实例。因此,始终为任何给定的Actor ID 调用相同的分区(或服务实例)。这如下图所示:

2cc22e316bf82575242b5e66f0fa0d2e.png

这简化了一些选择,但也带来了一些考虑:  

  • 默认情况下,Actor 随机放置到 pod 中,从而实现均匀分布。

  • 因为Actor是随机放置的,应该可以预料到Actor操作总是需要网络通信,包括方法调用数据的序列化和反序列化,产生延迟和开销。

:Dapr Actor 放置服务仅用于 Actor 放置,因此如果您的服务不使用 Dapr Actors,则不需要。 放置服务可以在所有托管环境中运行,包括自托管和 Kubernetes。

Actor通讯

您可以通过HTTP/gRPC调用Actor,当然也可以用SDK。

POST/GET/PUT/DELETE http://localhost:3500/v1.0/actors/<actorType>/<actorId>/<method/state/timers/reminders>

并发

Dapr Actor 运行时为访问 Actor 方法提供了一个简单的回合制(turn-basesd)的访问模型。这意味着在任何时候,Actor 对象的代码中都不能有超过一个线程处于活动状态。

单个Actor实例一次不能处理多个请求。如果预期要处理并发请求,Actor 实例可能会导致吞吐量瓶颈。

单个Actor实例指每个Actor ID对应的Actor对象。单个Actor不并发就没有问题

如果两个 Actor 之间存在循环请求,而同时向其中一个 Actor 发出外部请求,则 Actor 之间可能会陷入僵局。Dapr Actor运行时自动超时Actor调用并向调用者抛出异常以中断可能的死锁情况。

2f8bfc84378535c092688c2d851542f3.png

重入性(Preview)

作为对 dapr 中基础 Actor 的增强。现在重入性为预览功能,感兴趣的小伙伴可以到看官方文档。

回合制访问(Turn-based access)

一个回合包括一个Actor方法的完整执行以响应来自其他Actor或客户端的请求,或者一个计时器/提醒回调的完整执行。即使这些方法和回调是异步的,Dapr Actor运行时也不会将它们交叉。一个回合必须完全完成后,才允许进行新的回合。换句话说,当前正在执行的Actor方法或计时器/提醒回调必须完全完成,才能允许对方法或回调的新调用。

Dapr Actor运行时通过在回合开始时获取每个Actor的锁并在回合结束时释放锁来实现基于回合的并发性。 因此,基于回合的并发是在每个Actor的基础上执行的,而不是跨Actor。Actor 方法和计时器/提醒回调可以代表不同的 Actor 同时执行。

以下示例说明了上述概念。考虑实现两个异步方法(例如 Method1 和 Method2)、计时器和提醒的Actor 类型。下图显示了代表属于此Actor类型的两个Actors(ActorId1 和 ActorId2)执行这些方法和回调的时间线示例。

0bfe013c8c4479077124b9d3d577e3b5.png

Actor状态管理

Actor可以使用状态管理功能可靠地保存状态。您可以通过 HTTP/gRPC 端点与 Dapr 交互以进行状态管理。

要使用 actor,您的状态存储必须支持事务。这意味着您的状态存储组件必须实现 TransactionalStore 接口。只有一个状态存储组件可以用作所有参与者的状态存储。

事务支持列表:https://docs.dapr.io/reference/components-reference/supported-state-stores/

:建议学习的时候都用Redis,官方所有的示例也都是基于Redis,比较容易上手,且Dapr init默认集成

Actor计时器和提醒

Actor可以通过注册计时器或提醒来安排自己的定期工作。

计时器和提醒的功能非常相似。主要区别在于,Dapr Actor 运行时在停用后不保留有关计时器的任何信息,而使用 Dapr Actor 状态提供程序保留有关提醒的信息。

定时器和提醒的调度配置是相同的,总结如下:


DueTime 是一个可选参数,用于设置第一次调用回调之前的时间或时间间隔。如果省略 DueTime,则在定时器/提醒注册后立即调用回调。

支持的格式:

  • RFC3339 日期格式,例如2020-10-02T15:00:00Z

  • time.Duration 格式,例如2h30m

  • ISO 8601 持续时间格式,例如PT2H30M


period 是一个可选参数,用于设置两次连续回调调用之间的时间间隔。当以 ISO 8601-1 持续时间格式指定时,您还可以配置重复次数以限制回调调用的总数。如果省略 period,则回调将仅被调用一次。

支持的格式:

  • time.Duration 格式,例如2h30m

  • ISO 8601 持续时间格式,例如PT2H30M, R5/PT1M30S


ttl 是一个可选参数,用于设置计时器/提醒到期和删除的时间或时间间隔。如果省略 ttl,则不应用任何限制。

支持的格式:

  • RFC3339 日期格式,例如2020-10-02T15:00:00Z

  • time.Duration 格式,例如2h30m

  • ISO 8601 持续时间格式,例如PT2H30M


当您同时指定周期内的重复次数和 ttl 时,计时器/提醒将在满足任一条件时停止。

Actor 运行时配置

  • actorIdleTimeout - 停用空闲 actor 之前的超时时间。每个 actorScanInterval 间隔都会检查超时。默认值:60 分钟

  • actorScanInterval - 指定扫描演员以停用空闲Actor的频率的持续时间。闲置时间超过 actor_idle_timeout 的 Actor 将被停用。默认值:30 秒

  • drainOngoingCallTimeout - 在耗尽Rebalanced的Actor的过程中的持续时间。这指定了当前活动 Actor 方法完成的超时时间。如果当前没有 Actor 方法调用,则忽略此项。默认值:60 秒

  • drainRebalancedActors - 如果为 true,Dapr 将等待 drainOngoingCallTimeout 持续时间以允许当前角色调用完成,然后再尝试停用角色。默认值:true

    drainRebalancedActors与上面的drainOngoingCallTimeout需搭配使用

  • reentrancy - (ActorReentrancyConfig) - 配置角色的重入行为。如果未提供,则禁用可重入。默认值:disabled, 0

  • remindersStoragePartitions - 配置Actor提醒的分区数。如果未提供,则所有提醒都将保存为Actor状态存储中的单个记录。默认值:0

// In Startup.cs
public void ConfigureServices(IServiceCollection services)
{// Register actor runtime with DIservices.AddActors(options =>{// Register actor types and configure actor settingsoptions.Actors.RegisterActor<MyActor>();// Configure default settingsoptions.ActorIdleTimeout = TimeSpan.FromMinutes(60);options.ActorScanInterval = TimeSpan.FromSeconds(30);options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(60);options.DrainRebalancedActors = true;options.RemindersStoragePartitions = 7;// reentrancy not implemented in the .NET SDK at this time});// Register additional services for use with actorsservices.AddSingleton<BankService>();
}

分区提醒(Preview)

在 sidecar 重新启动后,Actor 提醒会保留并继续触发。在 Dapr 运行时版本 1.3 之前,提醒被保存在 actor 状态存储中的单个记录上。

此为Preview功能,感兴趣可以看官方文档

.Net调用Dapr的Actor

与以往不同,Actor示例会多创建一个共享类库用于存放Server和Client共用的部分

创建Assignment.Shared

创建类库项目,并添加Dapr.ActorsNuGet包引用,最后添加以下几个类:

AccountBalance.cs

namespace Assignment.Shared;
public class AccountBalance
{public string AccountId { get; set; } = default!;public decimal Balance { get; set; }
}

IBankActor.cs

:这个是Actor接口,IActor是Dapr SDK提供的

using Dapr.Actors;namespace Assignment.Shared;
public interface IBankActor : IActor
{Task<AccountBalance> GetAccountBalance();Task Withdraw(WithdrawRequest withdraw);
}

OverdraftException.cs

namespace Assignment.Shared;
public class OverdraftException : Exception
{public OverdraftException(decimal balance, decimal amount): base($"Your current balance is {balance:c} - that's not enough to withdraw {amount:c}."){}
}

WithdrawRequest.cs

namespace Assignment.Shared;
public class WithdrawRequest
{public decimal Amount { get; set; }
}

创建Assignment.Server

创建类库项目,并添加Dapr.Actors.AspNetCoreNuGet包引用和Assignment.Shared项目引用,最后修改程序端口为5000。

:Server与Shared和Client的NuGet包不一样,Server是集成了服务端的一些功能

修改program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<BankService>();
builder.Services.AddActors(options =>
{options.Actors.RegisterActor<DemoActor>();
});var app = builder.Build();app.UseRouting();app.UseEndpoints(endpoints =>
{endpoints.MapActorsHandlers();
});app.Run();

添加BankService.cs

using Assignment.Shared;namespace Assignment.Server;
public class BankService
{// Allow overdraft of up to 50 (of whatever currency).private readonly decimal OverdraftThreshold = -50m;public decimal Withdraw(decimal balance, decimal amount){// Imagine putting some complex auditing logic here in addition to the basics.var updated = balance - amount;if (updated < OverdraftThreshold){throw new OverdraftException(balance, amount);}return updated;}
}

添加BankActor.cs

using Assignment.Shared;
using Dapr.Actors.Runtime;
using System;namespace Assignment.Server;
public class BankActor : Actor, IBankActor, IRemindable // IRemindable is not required
{private readonly BankService bank;public BankActor(ActorHost host, BankService bank): base(host){// BankService is provided by dependency injection.// See Program.csthis.bank = bank;}public async Task<AccountBalance> GetAccountBalance(){var starting = new AccountBalance(){AccountId = this.Id.GetId(),Balance = 10m, // Start new accounts with 100, we're pretty generous.};var balance = await StateManager.GetOrAddStateAsync("balance", starting);return balance;}public async Task Withdraw(WithdrawRequest withdraw){var starting = new AccountBalance(){AccountId = this.Id.GetId(),Balance = 10m, // Start new accounts with 100, we're pretty generous.};var balance = await StateManager.GetOrAddStateAsync("balance", starting)!;if (balance.Balance <= 0){// Simulated reminder depositif (Random.Shared.Next(100) > 90){await RegisterReminderAsync("Deposit", null, TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(-1));}}// Throws Overdraft exception if the account doesn't have enough money.var updated = this.bank.Withdraw(balance.Balance, withdraw.Amount);balance.Balance = updated;await StateManager.SetStateAsync("balance", balance);}public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period){if (reminderName == "Deposit"){var balance = await StateManager.GetStateAsync<AccountBalance>("balance")!;if (balance.Balance <= 0){balance.Balance += 60; // 50(Overdraft Threshold) + 10 = 60Console.WriteLine("Deposit: 10");}else{Console.WriteLine("Deposit: ignore");}}}
}

运行Assignment.Server

使用Dapr CLI来启动,先使用命令行工具跳转到目录 dapr-study-room\Assignment07\Assignment.Server,然后执行下面命令

dapr run --app-id testactor --app-port 5000 --dapr-http-port 3500 --dapr-grpc-port 50001 dotnet run

创建Assignment.Client

创建控制台项目,并添加Dapr.ActorsNuGet包引用和Assignment.Shared项目引用。

修改Program.cs

using Assignment.Shared;
using Dapr.Actors;
using Dapr.Actors.Client;Console.WriteLine("Creating a Bank Actor");
var bank = ActorProxy.Create<IBankActor>(ActorId.CreateRandom(), "BankActor");
Parallel.ForEach(Enumerable.Range(1, 10), async i =>
{while (true){var balance = await bank.GetAccountBalance();Console.WriteLine($"[Worker-{i}] Balance for account '{balance.AccountId}' is '{balance.Balance:c}'.");Console.WriteLine($"[Worker-{i}] Withdrawing '{1m:c}'...");try{await bank.Withdraw(new WithdrawRequest() { Amount = 1m });}catch (ActorMethodInvocationException ex){Console.WriteLine("[Worker-{i}] Overdraft: " + ex.Message);}Task.Delay(1000).Wait();}
});Console.ReadKey();

运行Assignment.Client

使用Dapr CLI来启动,先使用命令行工具跳转到目录 dapr-study-room\Assignment07\Assignment.Client,然后执行下面命令

dotnet run

本章源码

Assignment07

https://github.com/doddgu/dapr-study-room

我们正在行动,新的框架、新的生态

我们的目标是自由的易用的可塑性强的功能丰富的健壮的

所以我们借鉴Building blocks的设计理念,正在做一个新的框架MASA Framework,它有哪些特点呢?

  • 原生支持Dapr,且允许将Dapr替换成传统通信方式

  • 架构不限,单体应用、SOA、微服务都支持

  • 支持.Net原生框架,降低学习负担,除特定领域必须引入的概念,坚持不造新轮子

  • 丰富的生态支持,除了框架以外还有组件库、权限中心、配置中心、故障排查中心、报警中心等一系列产品

  • 核心代码库的单元测试覆盖率90%+

  • 开源、免费、社区驱动

  • 还有什么?我们在等你,一起来讨论

经过几个月的生产项目实践,已完成POC,目前正在把之前的积累重构到新的开源项目中

目前源码已开始同步到Github(文档站点在规划中,会慢慢完善起来):

MASA.BuildingBlocks

MASA.Contrib

MASA.Utils

MASA.EShop

BlazorComponent

MASA.Blazor

QQ群:7424099

微信群:加技术运营微信(MasaStackTechOps),备注来意,邀请进群

2e36871249e8b7f97c448bdb4b0c5665.png

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

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

相关文章

git之Pushing to the remote branch is not fast-forward错误解决

今天推送代码的时候报错了这个Pushing to the remote branch is not fast-forward,so the push has to be forced.The commits in the remote branch will be lost 错误&#xff0c;然后就出现这个效果&#xff0c;下面是图片。 问题&#xff08;Non-fast-forward&#xff09;的…

CSS Id 和 Class

2019独角兽企业重金招聘Python工程师标准>>> id 和 class 选择器 如果你要在HTML元素中设置CSS样式&#xff0c;你需要在元素中设置"id" 和 "class"选择器。 id 选择器 id 选择器可以为标有特定 id 的 HTML 元素指定特定的样式。 HTML元素以id属…

这4部有生之年必看的“教材级”纪录片,免费领取!

全世界只有3.14 % 的人关注了爆炸吧知识纪录片是以真实生活为创作素材&#xff0c;以真人真事为表现对象&#xff0c;并对其进行艺术的加工与展现的&#xff0c;以展现真实为本质&#xff0c;并用真实引发人们思考的电影或电视艺术形式。好的纪录片就像打开了一扇新世界的大门&…

Dapr 集成 APISIX 做API网关

在这篇文章中&#xff0c;我将展示如何创建一个 APISIX控制器&#xff0c;该控制器在 Kubernetes 集群中公开启用 Dapr 的应用程序。本质上&#xff0c;APISIX控制器将配置相同的标准 Dapr annotations以注入daprd sidecar。通过公开这个 sidecar&#xff0c;它将允许外部应用程…

判断一个字符串是否包含另一个字符串(用java但是不能用index()这个函数)

目录: 一.方法介绍 二.图示意 三.源代码 一.方法介绍 判断一个字符串str1是否包含另一个字符串str2: 1.取str2的第一个字符一次和str1的字符依次比较,知道找到相等的字符为止或者找完整个str1的length. 2.当找到相等的字符后,在str2长度内str2与str1依次进行比较 二…

跳槽9招让你“空降”任何企业都能成功

2019独角兽企业重金招聘Python工程师标准>>> 作为一名职业经理人&#xff0c;没有谁没跳过槽&#xff0c;撇开在跳槽时对所“空降”的企业所需要的行业知识知根知底而“得心应手”&#xff0c;从而“稳定”外&#xff0c;其他人可能或多或少都有过因“不适应”新单位…

这个年纪,喜欢你的肉体还会送你包的,原来是......

1 iPhone 12一出王守义成最大赢家&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 想搭免费车的喵&#xff08;dy&#xff1a;大兔几&#xff09;▼3 拼纹身的有吗&#xff1f;纹在我身&#xff0c;刀在你身▼4 那你泡啥&#xff1f;▼5 快把知识君p上去&#xff…

C# WPF MVVM模式Caliburn.Micro框架下事件发布与订阅

01—前言处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信&#xff0c;Caliburn提供了一种事件机制&#xff0c;可以在应用程序中低耦合的模块之间进行通信&#xff0c;该机制基于事件聚合器服务&#xff0c;允许发布者和订阅者之间通过事件进行通讯&#xff0c;且彼…

常用的加密算法---数字摘要

数字摘要&#xff1a;数字摘要也称为消息摘要&#xff0c;它是一个唯一对应一个消息或文本的固定长度的值&#xff0c;它是一个单向Hash函数对消息进行计算产生的。摘要生成的过程&#xff1a;待摘要串-----> Hash函数-----> 摘要消息摘要的特点&#xff1a;1.无论输入的…

清华最强本科生Top10出炉,从来没有什么天才学霸!

全世界只有3.14 % 的人关注了爆炸吧知识‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍本文转自&#xff1a;量子位作者&#xff1a;木易 鱼羊一年一度&#xff0c;清华园里的“神仙打架”&#xff0c;投票结果已经出炉。根据清华大学官方消息&#xff0c;2020年清华大学特等奖学金&#x…

day19(中)_IO流3(模拟缓冲区,装饰设计模式)

1.MyBufferedReader和MyBufferedInputStream 1.模拟字符读取流的缓冲区: /*根据readLine原理:自定义一个类包含一个功能和readLine一致的方法来模拟以下BufferedReader方法 */ package myreadline; import java.io.FileReader; import java.io.IOException;class MyBufferedRea…

手把手教你学Dapr - 8. 绑定

介绍使用绑定&#xff0c;您可以使用来自外部系统的事件触发您的应用程序&#xff0c;或与外部系统交互。这个构建块为您和您的代码提供了几个好处&#xff1a;消除连接和轮询消息系统&#xff08;如队列和消息总线&#xff09;的复杂性关注业务逻辑&#xff0c;而不是如何与系…

Android之百度地图定位最详细使用总结

Android之百度定位 如果项目里面有定位功能的话,一般还是觉得蛮高大上的,我们项目用的百度定位,到网上找了很多资料,很多都不全面,很多博客都没有小伙伴期望得到当前的省和城市出来,然后自己动手也有很多错误,不知道错在哪里,也在网上找为什么…

CountDownLatch应用实战

2019独角兽企业重金招聘Python工程师标准>>> 1. CountDownLatch简介 一个同步辅助类&#xff0c;在完成一组正在其他线程中执行的操作之前&#xff0c;它允许一个或多个线程一直等待。其本质就是一个共享锁。 他最主要的作用是用来同步java的线程。 主要有以下三个方…

40张令人震惊的对比图,第一张细思恐极

全世界只有3.14 % 的人关注了爆炸吧知识世界之大&#xff0c;无奇不有。来看Reddit网友们分享的一组对比图&#xff0c;涨涨姿势&#xff01;医生对戴口罩和不戴口罩的培养皿分别&#xff1a;打喷嚏&#xff0c;唱歌&#xff0c;说话和咳嗽之后的区别&#xff08;难怪有些国家疫…

Smark.Data 1.5更新详解

由于一直忙于Beetle的开发和优化&#xff0c;Smark.Data组件已经有很长一段时间更新。这段时间发现Smark.Data在某些情况下不足&#xff0c;而这些情况又比较普遍所以进行了1.5版的更新。其更新的主要功能包括:查询可填充到自定义对象中&#xff0c;可直接执行存储过程和执行存…

C# 数据适配器之 DataAdapter 对象

01 DataAdapter 对象概述DataAdapter 对象是一个数据适配器对象&#xff0c;是DataSet 与数据源之间的桥梁。DataAdapter 对像提供了 4 个属性&#xff0c;用于实现与数据源之间的互通。SelectCommand 属性&#xff1a;向数据库发送查询 SQL 语句。 DeleteCommand 属性&#x…

express 4 简单实现自动注册路由功能

为什么80%的码农都做不了架构师&#xff1f;>>> ##实现 在express&#xff0c;模块的lib/router/index.js 的 proto.route 方法定义附件添加代码&#xff0c;实现自动注册路由方法&#xff1a; var fs require("fs"); var Path require("path&quo…

速战速决?你不会是不行吧......

1 真服了&#xff01;&#xff01;▼2 同猫不同命......▼3 无锡&#xff1a;&#xff1f;&#xff1f;▼4 还是黄金好&#xff1f;▼5 你会不会是不行&#xff1f;&#xff1f;▼6 爸爸&#xff1a;那也太惊喜了...▼7 倒是毫无违和感&#xff1f;▼8 皇上您说的对&…

mysql普通标转分区表_MySQL分区表到普通表互转

由于最近总有人抱怨&#xff0c;数据迁移后执行SQL变慢&#xff0c;经过查看原来是分区导致的问题。原分区根据按月设置RANGE分区&#xff0c;看到这图的时候也许有人就会发现问题.......业务查询SQL&#xff1a;从SQL上看 执行计划确实是走了分区&#xff0c;但为什么没有命中…