准备工作

第一步,建立一个模板项目

本文从CookieAuthenticationMiddleware入手分析,首先我们来看看哪里用到了这个中间件,打开VisualStudio,创建一个Mvc项目,然后身份验证选择个人身份验证。此时我们获得了一个完整的项目,这个项目中登陆注册都已实现且较为完整,可以直接运行,所以我们从模板代码中来学习CookieAuthenticationMiddleware.

接下来打开项目下的根目录\App_Start\Startup.Auth.cs这个文件,文件中的部分类Startup。上一篇文章中写到Owin会加载Startup类,所以在这个Mvc项目中会有一个位置加载了这个类,在代码中寻找 根目录\Startup.cs,这是Startup类的另一部分,并且在其命名空间上被打上了[assembly: OwinStartupAttribute(typeof(MyIdentity.Startup))]特性。

回到Startup.Auth.cs

public void ConfigureAuth(IAppBuilder app)
{// 配置数据库上下文、用户管理器和登录管理器,以便为每个请求使用单个实例app.CreatePerOwinContext(ApplicationDbContext.Create);app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);// 使应用程序可以使用 Cookie 来存储已登录用户的信息// 并使用 Cookie 来临时存储有关使用第三方登录提供程序登录的用户的信息// 配置登录 Cookieapp.UseCookieAuthentication(new CookieAuthenticationOptions{AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,LoginPath = new PathString("/Account/Login"),Provider = new CookieAuthenticationProvider{// 当用户登录时使应用程序可以验证安全戳。// 这是一项安全功能,当你更改密码或者向帐户添加外部登录名时,将使用此功能。OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(validateInterval: TimeSpan.FromMinutes(30),regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))}});            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);// 使应用程序可以在双重身份验证过程中验证第二因素时暂时存储用户信息。app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));// 使应用程序可以记住第二登录验证因素,例如电话或电子邮件。// 选中此选项后,登录过程中执行的第二个验证步骤将保存到你登录时所在的设备上。// 此选项类似于在登录时提供的“记住我”选项。app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);}

app.UseCookieAuthenticationOwin管道中添加中间件。进入UseCookieAuthentication的定义

using Microsoft.Owin.Security.Cookies;
namespace Owin
{public static class CookieAuthenticationExtensions{public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options);public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options, PipelineStage stage);}
}

在这个方法在Microsoft.Owin.Security.Cookies.dll程序集中,这个程序集属于Katana项目。

第二步,下载Katana源码

到katana Github仓库地址下载Katana的源代码。然后打开Katana.sln打开解决方案,然后在解决方案上点击右键选择还原Nuget包。

准备工作结束

从CookieAuthenticationExtensions入手

找到UseCookieAuthentication方法如下:

public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options, PipelineStage stage)
{if (app == null){throw new ArgumentNullException("app");}app.Use(typeof(CookieAuthenticationMiddleware), app, options);app.UseStageMarker(stage);return app;
}

app.Use(typeof(CookieAuthenticationMiddleware), app, options);是Owin标准中定义的添加中间件的方法

namespace Owin
{public interface IAppBuilder{IDictionary<string, object> Properties { get; }object Build(Type returnType);IAppBuilder New();IAppBuilder Use(object middleware, params object[] args);}
}

接下来进入CookieAuthenticationMiddleware

public class CookieAuthenticationMiddleware : AuthenticationMiddleware<CookieAuthenticationOptions>
{private readonly ILogger _logger;
...

再进入AuthenticationMiddleware<CookieAuthenticationOptions>

public abstract class AuthenticationMiddleware<TOptions> : OwinMiddleware where TOptions : AuthenticationOptions
{protected AuthenticationMiddleware(OwinMiddleware next, TOptions options): base(next){if (options == null){throw new ArgumentNullException("options");}Options = options;}public TOptions Options { get; set; }public override async Task Invoke(IOwinContext context){AuthenticationHandler<TOptions> handler = CreateHandler();await handler.Initialize(Options, context);if (!await handler.InvokeAsync()){await Next.Invoke(context);}await handler.TeardownAsync();}protected abstract AuthenticationHandler<TOptions> CreateHandler();
}

这里可以看到 AuthenticationMiddleware<TOptions>直接继承了OwinMiddleware也就是Katana中定义的,上一篇文章中提到了这个类,Katana中Middlware的抽象基类。
重点看下Invoke方法

public override async Task Invoke(IOwinContext context)
{AuthenticationHandler<TOptions> handler = CreateHandler();await handler.Initialize(Options, context);if (!await handler.InvokeAsync()){await Next.Invoke(context);}await handler.TeardownAsync();
}

首先通过抽象的CreateHandler()方法获取一个认证委托,然后初始化,然后Invoke。进入InvokeAsync的代码。位于AuthenticationHandler

/// <summary>
/// Called once by common code after initialization. If an authentication middleware responds directly to
/// specifically known paths it must override this virtual, compare the request path to it's known paths,
/// provide any response information as appropriate, and true to stop further processing.
/// </summary>
/// <returns>Returning false will cause the common code to call the next middleware in line. Returning true will
/// cause the common code to begin the async completion journey without calling the rest of the middleware
/// pipeline.</returns>
public virtual Task<bool> InvokeAsync()
{return Task.FromResult<bool>(false);
}

很意外,竟然直接返回了false。仔细看下注释:

在初始化之后的代码调用一次。如果一个 身份认证中间件直接响应了特定的 中间件知道的路径,那么它必须重写这个虚方法,将请求路径与已知路径进行比较,提供适当的响应信息,并停止进一步的处理。
返回false会调用后面的中间件,返回true不会调用下面的中间件

if (!await handler.InvokeAsync())
{await Next.Invoke(context);
}

和注释中描述的一致,最后写在调用 await handler.TeardownAsync();卸载handler。
暂时跳过TeardownAsync();方法,回到CookieAuthenticationMiddleware中。

可以看到其直接继承了AuthenticationMiddleware它没有重写InvokeAsync,这代表Authentication之后不会停止,这就让人很迷惑,认证失败了也不停止吗?我们就绪探索。

先看下 重写CreateHandler抽象方法

protected override AuthenticationHandler<CookieAuthenticationOptions> CreateHandler()
{return new CookieAuthenticationHandler(_logger);
}

进入CookieAuthenticationHandler

CookieAuthenticationHandler的父继承关系如下

CookieAuthenticationHandler从名字中可以猜测这是真正处理 身份认证的类

先看下类中的字段

private const string HeaderNameCacheControl = "Cache-Control";
private const string HeaderNamePragma = "Pragma";
private const string HeaderNameExpires = "Expires";
private const string HeaderValueNoCache = "no-cache";
private const string HeaderValueMinusOne = "-1";
private const string SessionIdClaim = "Microsoft.Owin.Security.Cookies-SessionId";private readonly ILogger _logger;private bool _shouldRenew;
private DateTimeOffset _renewIssuedUtc;
private DateTimeOffset _renewExpiresUtc;
private string _sessionKey;

这里的常量多是关于Http头的,还有Session的Claim

private bool _shouldRenew;
private DateTimeOffset _renewIssuedUtc;
private DateTimeOffset _renewExpiresUtc;
private string _sessionKey;

这一组应该是关于cookie过期时间和滑动窗口的

public CookieAuthenticationHandler(ILogger logger)
{if (logger == null){throw new ArgumentNullException("logger");}_logger = logger;
}

构造函数注入了Logger

接下来是AuthenticationCore,应该就是人分认证的核心了

protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{AuthenticationTicket ticket = null;try{string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);if (string.IsNullOrWhiteSpace(cookie)){return null;}ticket = Options.TicketDataFormat.Unprotect(cookie);if (ticket == null){_logger.WriteWarning(@"Unprotect ticket failed");return null;}if (Options.SessionStore != null){Claim claim = ticket.Identity.Claims.FirstOrDefault(c => c.Type.Equals(SessionIdClaim));if (claim == null){_logger.WriteWarning(@"SessionId missing");return null;}_sessionKey = claim.Value;ticket = await Options.SessionStore.RetrieveAsync(_sessionKey);if (ticket == null){_logger.WriteWarning(@"Identity missing in session store");return null;}}DateTimeOffset currentUtc = Options.SystemClock.UtcNow;DateTimeOffset? issuedUtc = ticket.Properties.IssuedUtc;DateTimeOffset? expiresUtc = ticket.Properties.ExpiresUtc;if (expiresUtc != null && expiresUtc.Value < currentUtc){if (Options.SessionStore != null){await Options.SessionStore.RemoveAsync(_sessionKey);}return null;}bool? allowRefresh = ticket.Properties.AllowRefresh;if (issuedUtc != null && expiresUtc != null && Options.SlidingExpiration&& (!allowRefresh.HasValue || allowRefresh.Value)){TimeSpan timeElapsed = currentUtc.Subtract(issuedUtc.Value);TimeSpan timeRemaining = expiresUtc.Value.Subtract(currentUtc);if (timeRemaining < timeElapsed){_shouldRenew = true;_renewIssuedUtc = currentUtc;TimeSpan timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);_renewExpiresUtc = currentUtc.Add(timeSpan);}}var context = new CookieValidateIdentityContext(Context, ticket, Options);await Options.Provider.ValidateIdentity(context);if (context.Identity == null){// Rejected_shouldRenew = false;return null;}return new AuthenticationTicket(context.Identity, context.Properties);}catch (Exception exception){CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,CookieExceptionContext.ExceptionLocation.AuthenticateAsync, exception, ticket);Options.Provider.Exception(exceptionContext);if (exceptionContext.Rethrow){throw;}return exceptionContext.Ticket;}
}

先进入AuthenticationTicket看下

public class AuthenticationTicket
{/// <summary>/// Initializes a new instance of the <see cref="AuthenticationTicket"/> class/// </summary>/// <param name="identity"></param>/// <param name="properties"></param>public AuthenticationTicket(ClaimsIdentity identity, AuthenticationProperties properties){Identity = identity;Properties = properties ?? new AuthenticationProperties();}/// <summary>/// Gets the authenticated user identity.(获取已经过认证的用户Identity)/// </summary>public ClaimsIdentity Identity { get; private set; }/// <summary>/// Additional state values for the authentication session.(认证回话中的额外的值)/// </summary>public AuthenticationProperties Properties { get; private set; }
}

ClaimsIdentity位于System.Security.Claims,属于mscorlib.dll,是.Net freamework的一部分,之前学习Asp.Net Identity框架时经常和这个类打交道。Asp.Net Identity 是基于Owin的实现Katana的,Katana里用了ClaimsIdentity,所以Asp.Net Identity中的IIdentity类型是ClaimsIdentity,如果你创建了一个没有身份验证的的mvc项目,那么在controller中User的实际类型就不是ClaimsIdentity。

进入AuthenticationProperties

internal const string IssuedUtcKey = ".issued";
internal const string ExpiresUtcKey = ".expires";
internal const string IsPersistentKey = ".persistent";
internal const string RedirectUriKey = ".redirect";
internal const string RefreshKey = ".refresh";
internal const string UtcDateTimeFormat = "r";

这些常量可能是和claims有关(博主并不十分确定)。

这个类主要内容是认证回话中的额外数据,没有太多的方法所以我把注释信息翻译下,以便理解,然后继续分析

/// <summary>
/// Gets or sets if refreshing the authentication session should be allowed.
/// 获取或者设置值指示是否允许舒心 认证会话
/// </summary>
public bool? AllowRefresh
{get...set...
}/// <summary>
/// State values about the authentication session.
/// 关于认证会话的状态值集合
/// </summary>
public IDictionary<string, string> Dictionary
{get { return _dictionary; }
}/// <summary>
/// Gets or sets the time at which the authentication ticket expires.
/// 认证票据的过期时间
/// </summary>
public DateTimeOffset? ExpiresUtc
{get...set...
}/// <summary>
/// Gets or sets whether the authentication session is persisted across multiple requests.
/// 指示认证会话是否在在跨越多个请求中持续保持
/// </summary>
public bool IsPersistent
{get...set...
}/// <summary>
/// Gets or sets the time at which the authentication ticket was issued.
/// 指示 认证票据的签发时间
/// </summary>
public DateTimeOffset? IssuedUtc
{get...set...
}/// <summary>
/// Gets or sets the full path or absolute URI to be used as an http redirect response value.
/// 指示重定向响应所用到的完全路径或者绝对uri路径
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "By design")]
public string RedirectUri
{get...set...
}

现在回到CookieAuthenticationHandler.AuthenticateCoreAsync方法中

string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName); 获取Cookie。进入GetRequestCookie

public interface ICookieManager
{/// <summary>/// Read a cookie with the given name from the request./// </summary>/// <param name="context"></param>/// <param name="key"></param>/// <returns></returns>string GetRequestCookie(IOwinContext context, string key);
...

这是个接口,从请求中获取指定的cookie。OptionsCookieAuthenticationHandler的直接父类中定义的

public abstract class AuthenticationHandler<TOptions> : AuthenticationHandler where TOptions : AuthenticationOptions
{protected TOptions Options { get; private set; }
...

internal class CookieAuthenticationHandler : AuthenticationHandler<CookieAuthenticationOptions>的签名中我们可以知道Options的实际类型,在这里找到上文提到的Options.CookieName

/// <summary>
/// Determines the cookie name used to persist the identity. The default value is ".AspNet.Cookies".
/// This value should be changed if you change the name of the AuthenticationType, especially if your
/// system uses the cookie authentication middleware multiple times.
/// </summary>
public string CookieName
{get { return _cookieName; }set{if (value == null){throw new ArgumentNullException("value");}_cookieName = value;}
}

决定存储identity的cookie name,它的默认值是 .AspNet.Cookies,这个值会在你改变AuthenticationType的名称是改变,尤其是你的系统多次使用了身份认证中间件。

这里至少知道了存储的cookie默认值是.AspNet.Cookies

运行之前的项目注册并登陆,检查Cookie

结果似乎和预期的不太一致,我们继续探索
打开 Startup类

public partial class Startup
{// 有关配置身份验证的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkId=301864public void ConfigureAuth(IAppBuilder app){// 配置数据库上下文、用户管理器和登录管理器,以便为每个请求使用单个实例app.CreatePerOwinContext(ApplicationDbContext.Create);app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);// 使应用程序可以使用 Cookie 来存储已登录用户的信息// 并使用 Cookie 来临时存储有关使用第三方登录提供程序登录的用户的信息// 配置登录 Cookieapp.UseCookieAuthentication(new CookieAuthenticationOptions{AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,LoginPath = new PathString("/Account/Login"),Provider = new CookieAuthenticationProvider...

AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie一行中改变了AuthenticationType的值,ApplicationCookie的值是ApplicationCookie

public const string ApplicationCookie = "ApplicationCookie";

接下来在源代码中搜索 .AspNet.,找到了Microsoft.Owin.Security.Cookies.CookieAuthenticationDefaults

public static class CookieAuthenticationDefaults
{.../// <summary>/// The prefix used to provide a default CookieAuthenticationOptions.CookieName/// </summary>public const string CookiePrefix = ".AspNet.";...

这是CookieAuthenticationOptions.CookieName默认的Cookie前缀

查找CookieAuthenticationDefaults类的引用,我们找到了CookieAuthenticationMiddleware(前文正在研究的),其中的代码

Options.CookieName = CookieAuthenticationDefaults.CookiePrefix + Options.AuthenticationType;

结果不言而喻。

回到之前的AuthenticateCoreAsync方法中

string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);

拿到了存储Identity的cookie。

ticket = Options.TicketDataFormat.Unprotect(cookie);

这一步从Cookie中解密数据获得ticket票据,Options.TicketDataFormat是CookieAuthenticationOptions类型中的属性

/// <summary>
/// The TicketDataFormat is used to protect and unprotect the identity and other properties which are stored in the
/// cookie value. If it is not provided a default data handler is created using the data protection service contained
/// in the IAppBuilder.Properties. The default data protection service is based on machine key when running on ASP.NET,
/// and on DPAPI when running in a different process.
/// </summary>
public ISecureDataFormat<AuthenticationTicket> TicketDataFormat { get; set; }

从注释信息中得知,这个属性用来加解密identity,而且在asp.net中基于 machine key。知道这个之后暂时不研究它的具体实现细节,我们回到之前的AuthenticateCoreAsync方法中

if (Options.SessionStore != null)
{Claim claim = ticket.Identity.Claims.FirstOrDefault(c => c.Type.Equals(SessionIdClaim));if (claim == null){_logger.WriteWarning(@"SessionId missing");return null;}_sessionKey = claim.Value;ticket = await Options.SessionStore.RetrieveAsync(_sessionKey);if (ticket == null){_logger.WriteWarning(@"Identity missing in session store");return null;}
}

这些步骤的主要内容是从解析出的ticket中拿到session id 的claim,RetrieveAsync方法没有注释,不知道这一步对ticket做了什么。

DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
DateTimeOffset? issuedUtc = ticket.Properties.IssuedUtc;
DateTimeOffset? expiresUtc = ticket.Properties.ExpiresUtc;if (expiresUtc != null && expiresUtc.Value < currentUtc)
{if (Options.SessionStore != null){await Options.SessionStore.RemoveAsync(_sessionKey);}return null;
}bool? allowRefresh = ticket.Properties.AllowRefresh;
if (issuedUtc != null && expiresUtc != null && Options.SlidingExpiration&& (!allowRefresh.HasValue || allowRefresh.Value))
{TimeSpan timeElapsed = currentUtc.Subtract(issuedUtc.Value);TimeSpan timeRemaining = expiresUtc.Value.Subtract(currentUtc);if (timeRemaining < timeElapsed){_shouldRenew = true;_renewIssuedUtc = currentUtc;TimeSpan timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);_renewExpiresUtc = currentUtc.Add(timeSpan);}
}

这些步骤做了刷新签发时间的一些工作

var context = new CookieValidateIdentityContext(Context, ticket, Options);

进入CookieValidateIdentityContext这个类中的东西不多

    public CookieValidateIdentityContext(IOwinContext context, AuthenticationTicket ticket, CookieAuthenticationOptions options): base(context, options){if (ticket == null){throw new ArgumentNullException("ticket");}Identity = ticket.Identity;Properties = ticket.Properties;}

所以进入其父类看一下

/// <summary>
/// Base class used for certain event contexts
/// </summary>
public abstract class BaseContext<TOptions>
{protected BaseContext(IOwinContext context, TOptions options){OwinContext = context;Options = options;}public IOwinContext OwinContext { get; private set; }public TOptions Options { get; private set; }public IOwinRequest Request{get { return OwinContext.Request; }}public IOwinResponse Response{get { return OwinContext.Response; }}
}

用于某些事件上下文的基类,这里有 OwinRequest,OwinResponse,OwinContext,加上CookieValidateIdentityContext Identity 和Properties,共同构成了 cookie 验证身份上下文。

CookieValidateIdentityContext中还有一个方法

/// <summary>
/// Called to reject the incoming identity. This may be done if the application has determined the
/// account is no longer active, and the request should be treated as if it was anonymous.
/// </summary>
public void RejectIdentity()
{Identity = null;
}

这个方法可以拒绝输入的Identity,可能在 app发现账户不在活动,并且向对待匿名用户那样对待当前的request

await Options.Provider.ValidateIdentity(context);

然后验证了这个上下文,继续寻找Options.Provider.ValidateIdentity直到

public interface ICookieAuthenticationProvider
{/// <summary>/// Called each time a request identity has been validated by the middleware. By implementing this method the/// application may alter or reject the identity which has arrived with the request./// </summary>/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>/// <returns>A <see cref="Task"/> representing the completed operation.</returns>Task ValidateIdentity(CookieValidateIdentityContext context);...

这是个接口方法,作用是 每次通过中间件验证请求身份时调用, 通过实现此方法,应用程序可以编辑或拒绝与请求一起到达的Identity。
我们知道它是做什么的了,接下来找下它的实现类 CookieAuthenticationProvider

/// <summary>
/// Implements the interface method by invoking the related delegate method
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task ValidateIdentity(CookieValidateIdentityContext context)
{return OnValidateIdentity.Invoke(context);
}

最终调用了OnValidateIdentity委托。
该委托在构造函数中赋予了默认值

public CookieAuthenticationProvider()
{OnValidateIdentity = context => Task.FromResult<object>(null);...

接下来

if (context.Identity == null)
{// Rejected_shouldRenew = false;return null;
}

判断Identity是否是null,上文中提到RejectIdentity方法可能为其赋值为null

return new AuthenticationTicket(context.Identity, context.Properties);

最后 返回一个新的票据。

捕获异常的部分如下,就不看了

catch (Exception exception)
{CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,CookieExceptionContext.ExceptionLocation.AuthenticateAsync, exception, ticket);Options.Provider.Exception(exceptionContext);if (exceptionContext.Rethrow){throw;}return exceptionContext.Ticket;
}

Katana-CookieAuthenticationMiddleware-源码浅析相关推荐

  1. hashmap允许null键和值吗_hashMap底层源码浅析

    来源:https://blog.csdn.net/qq_35824590/article/details/111769203 hashmap是我们经常使用的一个工具类.那么知道它的一些原理和特性吗? ...

  2. Android Loader机制全面详解及源码浅析

    原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...

  3. 内核启动流程分析(四)源码浅析

    目录 kernel(四)源码浅析 建立工程 启动简析 head.s 入口点 查询处理器 查询机器ID 启动MMU 其他操作 start_kernel 处理命令行 分区 kernel(四)源码浅析 建立 ...

  4. harbor登录验证_Harbor 源码浅析

    Harbor 源码浅析​www.qikqiak.com Harbor 是一个CNCF基金会托管的开源的可信的云原生docker registry项目,可以用于存储.签名.扫描镜像内容,Harbor 通 ...

  5. fetch first mysql_MySQL多版本并发控制机制(MVCC)源码浅析

    MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<>诚然讲的非常透彻,但只能提纲挈领,不能让 ...

  6. 【flink】Flink 1.12.2 源码浅析 : Task数据输入

    1.概述 转载:Flink 1.12.2 源码浅析 : Task数据输入 在 Task 中,InputGate 是对输入的封装,InputGate 是和 JobGraph 中 JobEdge 一一对应 ...

  7. 【flink】Flink 1.12.2 源码浅析 :Task数据输出

    1.概述 转载:Flink 1.12.2 源码浅析 :Task数据输出 Stream的计算模型采用的是PUSH模式, 上游主动向下游推送数据, 上下游之间采用生产者-消费者模式, 下游收到数据触发计算 ...

  8. 【flink】Flink 1.12.2 源码浅析 : StreamTask 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : StreamTask 浅析 在Task类的doRun方法中, 首先会构建一个运行环境变量RuntimeEnvironment . 然后会调用lo ...

  9. 【flink】Flink 1.12.2 源码浅析 : Task 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : Task 浅析 Task 表示TaskManager上并行 subtask 的一次执行. Task封装了一个Flink operator(也可能 ...

  10. 【Flink】Flink 1.12.2 源码浅析 : TaskExecutor

    1.概述 转载:Flink 1.12.2 源码浅析 : TaskExecutor TaskExecutor 是TaskManger的具体实现. 二 .TaskExecutorGateway TaskE ...

最新文章

  1. SAP咨询顾问被客户投诉的几个原因
  2. Vue集成Iframe页面
  3. Mark一下 | 当当优惠码,实付满150减30 | + 荐书
  4. 应用服务器——jetty架构分析
  5. Mobile孵化周即将在加州召开!
  6. Maven父子结构的项目依赖使用以及打包依赖_微服务项目(maven父子级项目)怎么打包
  7. arrays合并两个数组_每天一道力扣题: 88. 合并两个有序数组
  8. yolov5s 目标检测模型实战——火点烟雾检测实战
  9. C++调用C#创建的COM组件
  10. picasa2中文版,picasa2中文,picasa2中文版下载,picasa2中文版本
  11. 软件测试方法之正交试验测试方法
  12. 英国退出欧盟,欧盟还是欧盟吗?
  13. win10下pytorch 安装以及查看版本
  14. 配置销售开票时结转销售成本
  15. 3343:热血格斗场
  16. MySQL常用语句(二)
  17. 特拉华大学计算机专业,特拉华大学电气与计算机工程专业设置及申请条件汇总 顶尖名校专业解析!...
  18. 算法导论15.1动态规划之钢条切割
  19. Mac上使用LaTeX(小白入门配置Latex)
  20. c语言的编写程序--最简单的算术题

热门文章

  1. Java开源搜索引擎
  2. 开源电子工作套件 Arduino Start Kit 登场
  3. openssl 自建CA签发证书 网站https的ssl通信
  4. 【跃迁之路】【605天】程序员高效学习方法论探索系列(实验阶段362-2018.10.09)...
  5. jenkins修改启动用户,解决执行脚本权限问题
  6. django ---- models继承
  7. Python 学习笔记 - 字典
  8. 【0521模拟赛】小Z爱数学
  9. 使用Java辅助类(CountDownLatch、CyclicBarrier、Semaphore)并发编程
  10. linux下 proc 目录