重新整理 .net core 实践篇——— 权限源码阅读四十五]
前言
简单介绍一下权限源码阅读一下。
正文
一直有人对授权这个事情上争论不休,有的人认为在输入账户密码给后台这个时候进行了授权,因为认为发送了一个身份令牌,令牌里面可能有些用户角色信息,认为这就是授权,有的人认为这只是获取令牌的过程。
现实生活中有一个是授权证书,那么有人认为token 是授权证书,但这只是颁发证书。账户密码获取获取身份令牌也不是认证,认证是证明你的身份令牌有效的过程。
那么netcore 中是如何解释授权的:

授权是指确定用户可执行的操作的过程。故而实际上,获取身份令牌只是获取令牌,授权是指在访问过程中,确认是否可以访问的过程。身份令牌中有角色,有些是根据角色还确定是否可以访问的,这就是授权了。
不过随着业务的复杂,网关可以根据角色授权接口,也可以根据自己的策略了,授权的过程五花八门的。
在网关中一般有认证和授权两部分,先认证再授权,先确定合法身份在来确定一下授权。
先来看下认证吧,有些人一认证就想到了jwt,或者想到了具体的认证方式,其实认证就是你的系统认为它符合了合法身份,和具体的东西没有关系,是一个抽象的概念。
app.UseAuthentication();
通过上面这个看下认证过程。
然后看下具体的中间件。
public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (schemes == null)
{
throw new ArgumentNullException(nameof(schemes));
}
_next = next;
Schemes = schemes;
}
从这里看呢,IAuthenticationSchemeProvider 提供了认证解决方案,可以看下这个接口。
/// <summary>
/// Responsible for managing what authenticationSchemes are supported.
/// </summary>
public interface IAuthenticationSchemeProvider
{
/// <summary>
/// Returns all currently registered <see cref="AuthenticationScheme"/>s.
/// </summary>
/// <returns>All currently registered <see cref="AuthenticationScheme"/>s.</returns>
Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync();
/// <summary>
/// Returns the <see cref="AuthenticationScheme"/> matching the name, or null.
/// </summary>
/// <param name="name">The name of the authenticationScheme.</param>
/// <returns>The scheme or null if not found.</returns>
Task<AuthenticationScheme?> GetSchemeAsync(string name);
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultAuthenticateScheme"/>.
/// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultAuthenticateSchemeAsync();
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>.
/// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultChallengeSchemeAsync();
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultForbidScheme"/>.
/// Otherwise, this will fallback to <see cref="GetDefaultChallengeSchemeAsync"/> .
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultForbidSchemeAsync();
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultSignInScheme"/>.
/// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultSignInSchemeAsync();
/// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultSignOutScheme"/>.
/// Otherwise, this will fallback to <see cref="GetDefaultSignInSchemeAsync"/> .
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultSignOutSchemeAsync();
/// <summary>
/// Registers a scheme for use by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="scheme">The scheme.</param>
void AddScheme(AuthenticationScheme scheme);
/// <summary>
/// Registers a scheme for use by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="scheme">The scheme.</param>
/// <returns>true if the scheme was added successfully.</returns>
bool TryAddScheme(AuthenticationScheme scheme)
{
try
{
AddScheme(scheme);
return true;
}
catch {
return false;
}
}
/// <summary>
/// Removes a scheme, preventing it from being used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="name">The name of the authenticationScheme being removed.</param>
void RemoveScheme(string name);
/// <summary>
/// Returns the schemes in priority order for request handling.
/// </summary>
/// <returns>The schemes in priority order for request handling</returns>
Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync();
}
虽然没有看到具体的provider,但是呢,可以通过接口注释看个大概哈。
比如说AuthenticationScheme 是认证方案的意思,从英文表面理解哈。然后里面有方法增删改查,意味着我们可以有多种认证方式。
这其实是刚需,因为比如以前颁发的身份令牌和现在接口颁发的身份令牌不一样了,那么为了无缝衔接,可以认可两种认证方式。
那么看下AuthenticationScheme 认证方案里面有些啥吧。
/// <summary>
/// AuthenticationSchemes assign a name to a specific <see cref="IAuthenticationHandler"/>
/// handlerType.
/// </summary>
public class AuthenticationScheme
{
/// <summary>
/// Initializes a new instance of <see cref="AuthenticationScheme"/>.
/// </summary>
/// <param name="name">The name for the authentication scheme.</param>
/// <param name="displayName">The display name for the authentication scheme.</param>
/// <param name="handlerType">The <see cref="IAuthenticationHandler"/> type that handles this scheme.</param>
public AuthenticationScheme(string name, string? displayName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type handlerType)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (handlerType == null)
{
throw new ArgumentNullException(nameof(handlerType));
}
if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType))
{
throw new ArgumentException("handlerType must implement IAuthenticationHandler.");
}
Name = name;
HandlerType = handlerType;
DisplayName = displayName;
}
/// <summary>
/// The name of the authentication scheme.
/// </summary>
public string Name { get; }
/// <summary>
/// The display name for the scheme. Null is valid and used for non user facing schemes.
/// </summary>
public string? DisplayName { get; }
/// <summary>
/// The <see cref="IAuthenticationHandler"/> type that handles this scheme.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type HandlerType { get; }
}
这里面有name 和displayname,一个是名称,一个是显示名称,相信很多人都见到过这样的类,里面有name 还有 displayname。
不要那么计较,显示名称是为了好大家好而已。比如说我们的sex 表示性别,那么displayname 可以写显示名称。
比如说你的一个计划类,里面可以有name 和 displayname。name 是JC159,displayname 是瞎扯计划,JC159 多难理解啊,瞎扯计划多好理解,瞎扯啊。
然后里面有一个是HandlerType,叫做处理类型,处理认证计划的类型。上面有注释,这个类型继承IAuthenticationHandler这个接口,那么也就是这个方案将由实现IAuthenticationHandler的类来处理,具体看实际的处理方案,比如jwt。
然后看下中间件的invoke。
/// <summary>
/// Invokes the middleware performing authentication.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
public async Task Invoke(HttpContext context)
{
context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
await _next(context);
}
一段一段看吧。
context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
Features 是经过一个集合,比如我们经过中间件,我们可以向里面写入一些东西,然后供下一个中间件使用都行,有点像是游戏里面背包的功能。
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
这里面就是获取就是获取相应的认真方案处理器,然后进行执行HandleRequestAsync。
值得主意的是,这里面并不是执行IAuthenticationHandler的方法,而是IAuthenticationRequestHandler的方法。
所以这并不意味这我们的写入的每个方案都必须通过,而是如果我们的写入的每个认证方案继承IAuthenticationRequestHandler,那么必须通过其中的HandleRequestAsync方法。
然后看下IAuthenticationRequestHandler 这个哈。
/// <summary>
/// Used to determine if a handler wants to participate in request processing.
/// </summary>
public interface IAuthenticationRequestHandler : IAuthenticationHandler
{
/// <summary>
/// Gets a value that determines if the request should stop being processed.
/// <para>
/// This feature is supported by the Authentication middleware
/// which does not invoke any subsequent <see cref="IAuthenticationHandler"/> or middleware configured in the request pipeline
/// if the handler returns <see langword="true" />.
/// </para>
/// </summary>
/// <returns><see langword="true" /> if request processing should stop.</returns>
Task<bool> HandleRequestAsync();
}
继续往下看:
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
继续往下看哈,然后里面也有这个哈,如果有默认的认证方案,那么context.User 会通过默认认证方案的处理器进行获取。也就是说如果我们设置了默认方案,那么就会通过默认方案来进行认证。
await _next(context);
这个表示继续往下执行了。
那么来看下具体服务的认证吧,比如说jwt的。
services.AddAuthentication("Bearer")
// 添加JwtBearer服务
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,则把<是否过期>添加到,返回头信息中
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
首先来看一下:
services.AddAuthentication("Bearer")
这里面就是设置默认的认证方案:
public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, string defaultScheme)
=> services.AddAuthentication(o => o.DefaultScheme = defaultScheme);
public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) {
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}
var builder = services.AddAuthentication();
services.Configure(configureOptions);
return builder;
}
看一下:var builder = services.AddAuthentication();
这个哈,这个才是具体增加具体服务的。
public static AuthenticationBuilder AddAuthentication(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddAuthenticationCore();
services.AddDataProtection();
services.AddWebEncoders();
services.TryAddSingleton<ISystemClock, SystemClock>();
return new AuthenticationBuilder(services);
}
然后看下services.AddAuthenticationCore();,为什么看下这个呢?难道我提前看了这个东西吗?
不是,因为我们知道分层的时候有个Core的层,是具体实现的,那么这种带core 一般就是具体实现方式了。
public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.TryAddScoped<IAuthenticationService, AuthenticationService>();
services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContext
services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();
services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();
return services;
}
前面我们看了这个IAuthenticationHandlerProvider 和IAuthenticationSchemeProvider ,那么这里可以看到他们的具体实现是AuthenticationHandlerProvider和AuthenticationSchemeProvider。
前面提及到会通过handletype来获取具体的处理器,那么来看下具体怎么实现的吧。
/// <summary>
/// Returns the handler instance that will be used.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme being handled.</param>
/// <returns>The handler instance.</returns>
public async Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme)
{
if (_handlerMap.ContainsKey(authenticationScheme))
{
return _handlerMap[authenticationScheme];
}
var scheme = await Schemes.GetSchemeAsync(authenticationScheme);
if (scheme == null)
{
return null;
}
var handler = (context.RequestServices.GetService(scheme.HandlerType) ??
ActivatorUtilities.CreateInstance(context.RequestServices, scheme.HandlerType))
as IAuthenticationHandler;
if (handler != null)
{
await handler.InitializeAsync(scheme, context);
_handlerMap[authenticationScheme] = handler;
}
return handler;
}
看这个GetHandlerAsync,是通过依赖注入的方式来获取的,根据方案里面的GetHandlerAsync。
那么从这里就能猜到jwt的具体实现了,那么直接来看吧。
services.AddAuthentication("Bearer")
// 添加JwtBearer服务
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,则把<是否过期>添加到,返回头信息中
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
其实不建议这么写的,应该是:

直接标明这里使用的策略,之所以这个能够生效,是因为默认的是

红框框部分是Bearer,但是不友好,对框架不熟,容易形成误导。
继续往下看:
public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<JwtBearerOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>());
return builder.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, displayName, configureOptions);
}
AddScheme 就是具体的注入了:
/// <summary>
/// Adds a <see cref="AuthenticationScheme"/> which can be used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <typeparam name="TOptions">The <see cref="AuthenticationSchemeOptions"/> type to configure the handler."/>.</typeparam>
/// <typeparam name="THandler">The <see cref="AuthenticationHandler{TOptions}"/> used to handle this scheme.</typeparam>
/// <param name="authenticationScheme">The name of this scheme.</param>
/// <param name="displayName">The display name of this scheme.</param>
/// <param name="configureOptions">Used to configure the scheme options.</param>
/// <returns>The builder.</returns>
public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : AuthenticationSchemeOptions, new()
where THandler : AuthenticationHandler<TOptions>
=> AddSchemeHelper<TOptions, THandler>(authenticationScheme, displayName, configureOptions);
然后加入到认证方案中去,JwtBearerOptions 就是这个方案的配置,JwtBearerHandler就是具体的处理,看下AddSchemeHelper。
private AuthenticationBuilder AddSchemeHelper<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : class, new()
where THandler : class, IAuthenticationHandler
{
Services.Configure<AuthenticationOptions>(o =>
{
o.AddScheme(authenticationScheme, scheme => {
scheme.HandlerType = typeof(THandler);
scheme.DisplayName = displayName;
});
});
if (configureOptions != null)
{
Services.Configure(authenticationScheme, configureOptions);
}
Services.AddTransient<THandler>();
return this;
}
分步骤看下:
Services.Configure<AuthenticationOptions>(o =>
{
o.AddScheme(authenticationScheme, scheme => {
scheme.HandlerType = typeof(THandler);
scheme.DisplayName = displayName;
});
});
这一步就是添加具体的认证方案。
if (configureOptions != null)
{
Services.Configure(authenticationScheme, configureOptions);
}
Services.AddTransient<THandler>();
这一步就是注入配置文件,并且将处理器注入到ioc中,这里就是JwtBearerHandler了。
JwtBearerHandler 就不看了,就是一些具体的实现,根据配置文件,然后处理,就属于jwt的知识了。
补充
这里扩容一下配置的知识,主要解释一下JwtBearerHandler 是如何根据不同的authenticationScheme 获取不同的配置的。
Services.Configure(authenticationScheme, configureOptions);
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)
where TOptions : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}
services.AddOptions();
services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));
return services;
}
看到吧,实际上获IConfigureOptions,会获取一组ConfigureNamedOptions,然后通过name筛选出来。
看下JwtBearerHandler:
public class JwtBearerHandler : AuthenticationHandler<JwtBearerOptions>
public JwtBearerHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock)
: base(options, logger, encoder, clock)
{ }
将options 传给了AuthenticationHandler。
那么看下AuthenticationHandler 中如何处理的吧。
初始化的时候:
/// <summary>
/// Initialize the handler, resolve the options and validate them.
/// </summary>
/// <param name="scheme"></param>
/// <param name="context"></param>
/// <returns></returns>
public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
if (scheme == null)
{
throw new ArgumentNullException(nameof(scheme));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
Scheme = scheme;
Context = context;
Options = OptionsMonitor.Get(Scheme.Name) ?? new TOptions();
Options.Validate(Scheme.Name);
await InitializeEventsAsync();
await InitializeHandlerAsync();
}
进行一波筛选而来的哈。
结
下一节看下授权的源码吧。
重新整理 .net core 实践篇——— 权限源码阅读四十五]的更多相关文章
- 重新整理 .net core 实践篇——— 权限中间件源码阅读[四十六]
前言 前面介绍了认证中间件,下面看一下授权中间件. 正文 app.UseAuthorization(); 授权中间件是这个,前面我们提及到认证中间件并不会让整个中间件停止. 认证中间件就两个作用,我们 ...
- ABP源码分析四十五:ABP ZERO中的EntityFramework模块
AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...
- J.U.C并发框架源码阅读(十五)CopyOnWriteArrayList
基于版本jdk1.7.0_80 java.util.concurrent.CopyOnWriteArrayList 代码如下 /* * Copyright (c) 2003, 2011, Oracle ...
- 重新整理 .net core 实践篇————熔断与限流[三十五]
前言 简单整理一下熔断与限流,跟上一节息息相关. 正文 polly 的策略类型分为两类: 被动策略(异常处理.结果处理) 主动策略(超时处理.断路器.舱壁隔离.缓存) 熔断和限流通过下面主动策略来实现 ...
- 34 网络相关函数(二)——live555源码阅读(四)网络
34 网络相关函数(二)——live555源码阅读(四)网络 34 网络相关函数(二)——live555源码阅读(四)网络 2)socketErr 套接口错误 3)groupsockPriv函数 4) ...
- 33 网络相关函数(一)——live555源码阅读(四)网络
33 网络相关函数(一)——live555源码阅读(四)网络 33 网络相关函数(一)——live555源码阅读(四)网络 简介 1)IsMulticastAddress多播(组播)地址判断函数 多播 ...
- 31 GroupSock(AddressString)——live555源码阅读(四)网络
31 GroupSock(AddressString)——live555源码阅读(四)网络 31 GroupSock(AddressString)——live555源码阅读(四)网络 简介 Addre ...
- 29 GroupSock(NetAddressList)——live555源码阅读(四)网络
29 GroupSock(NetAddressList)——live555源码阅读(四)网络 29 GroupSock(NetAddressList)——live555源码阅读(四)网络 简介 Net ...
- 28 GroupSock(NetAddress)——live555源码阅读(四)网络
28 GroupSock(NetAddress)——live555源码阅读(四)网络 28 GroupSock(NetAddress)——live555源码阅读(四)网络 简介 1) NetAddre ...
随机推荐
- mysql从零开始之MySQL 删除数据库
MySQL 删除数据库 使用普通用户登陆 MySQL 服务器,你可能需要特定的权限来创建或者删除 MySQL 数据库,所以我们这边使用 root 用户登录,root 用户拥有最高权限. 在删除数据库过 ...
- Dapr + .NET Core实战(十一)单机Dapr集群负载均衡
如何单机部署Dapr集群 第十篇讲过了K8S集群下如何使用Dapr运行程序,但是很多人一直在问如何单机下进行Dapr的负载,这节课我们来聊聊如何单机进行Dapr的负载. 首先要说的是单机下,通过 da ...
- Elasticsearch 存储成本省 60%,稿定科技干货分享
背景 稿定科技旗下稿定设计产品是一个聚焦商业设计的多场景在线设计平台,打破了软硬件间的技术限制,汇集创意内容与设计工具于一体,为不同场景下的设计需求提供优质的解决方案,满足图片.视频等全类型媒介的设计 ...
- 每日总结:charcter方法(2021.10.5)
\t 在文中该处插入一个tab键 \b在文中该处插入一个后退键 \n 换行 \r 在文中该处回车 \f 在文中该处插入换页符 方法: isLetter()是否是一个字母 isDigit()是否是一个 ...
- [spring-rabbit]自动配置原理
1 一个简单的示例 在Spring Boot项目中使用spring-rabbit时,需要经过以下几个步骤: 引入依赖. 配置基本连接信息. 创建消息发布者,并发送消息. 创建消息消费者,监听消息并处理 ...
- js--typeof 和 instanceof 判断数据类型的区别及开发中的使用
前言 日常的开发中,我们经常会遇到判断一个变量的数据类型或者该变量是否为空值的情况,你是如何去选择判断类型的操作符的?本文来总结记录一下我们开发人员必须掌握的关于 typeof 和 instanceo ...
- 2021.7.27--Benelux Algorithm Programming Contest 2020 补提
I Jigsaw 题目内容: 链接:https://ac.nowcoder.com/acm/contest/18454/I 来源:牛客网 You have found an old jigsaw pu ...
- 【转】对于编译程序时出现“Deprecated declaration ultrasonic_Init - give arg types”的解决办法
编译程序时出现"Deprecated declaration ultrasonic_Init - give arg types"中文释义:给定函数的参数的类型过时, 解决办法: 在 ...
- Java:并发笔记-05
Java:并发笔记-05 说明:这是看了 bilibili 上 黑马程序员 的课程 java并发编程 后做的笔记 4. 共享模型之内存 本章内容 上一章讲解的 Monitor 主要关注的是访问共享变量 ...
- spring cloud hystrix的隔离策略和dashboard
随着服务的拆分,各个服务有着明确的职责,服务之间通过轻量级的协议进行通讯.但有时候我们完成一个功能需要同时调用多个微服务,比如完成订单的创建,那么获取用户信息需要调用用户微服务,获取商品信息需要调用商 ...