首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

学习ASP.NET Core, 怎能不了解请求处理管道[4]: 应用的入口——Startup

一个ASP.NET Core应用被启动之后就具有了针对请求的处理能力,而这个能力是由管道赋予的,所以应用的启动同时意味着管道的成功构建。由于管道是由注册的服务器和若干中间件构成的,所以应用启动过程中一个核心的工作就是完成中间节的注册。由于依赖注入在ASP.NET Core应用这得到非常广泛的应用,框架绝大部分的工作都会分配给我们预先注册的服务,所以服务注册也是启动WebHost过程的另一项核心工作。这两项在启动过程中必须完成的核心工作通过一个名为Startup的对象来承载。 [本文已经同步到《ASP.NET Core框架揭秘》之中] 目录 一、 DelegateStartup 二、ConventionBasedStartup StartupMethods StartupLoader 如何选择启动类型 如何选择服务注册方法和中间件注册方法 StartupMethods对象的创建 UseStartup方法究竟做了些什么? 三、选择哪一个Startup 这里所谓的Startup实际上是对所有实现了IStartup接口的所有类型以及对应对象的统称。如下面的代码片段所示,服务注册由ConfigureServices方法来实现,它返回一个ServiceProvider对象,至于另一个方法Configure则负责完成中间件的注册,方法输入参数是一个ApplicationBuilder对象。 1: public interface IStartup 2: { 3: IServiceProvider ConfigureServices(IServiceCollection services); 4: void Configure(IApplicationBuilder app); 5: } IStartup接口所在的NuGet包中还定义了另一个实现了这个接口的抽象类StartupBase。如下面的代码片段所示,StartupBase实现了抽象方法ConfigureServices,该方法直接利用提供的ServiceCollection对象创建返回的ServiceProvider。换句话说,派生于StartupBase的Startup类型如果没用重写ConfigureServices方法,它们实际上只关心中间件的注册,而不需要注册额外的服务。 1: public abstract class StartupBase : IStartup 2: { 3: public abstract void Configure(IApplicationBuilder app); 4: public virtual IServiceProvider ConfigureServices(IServiceCollection services) 5: { 6: return services.BuildServiceProvider(); 7: } 8: } 一、 DelegateStartup 我们来想想具体的应用中是如何注册中间件和服务的。中间件的注册可以采用两种方式,最简单的方式就是直接调用IWebHostBuilder的Configure方法。如下面的代码片段所示,这个方法直接上是借助于一个类型为Action<IApplicationBuilder>的委托对象将中间件注册到提供的ApplicationBuilder对象上。 1: public static class WebHostBuilderExtensions 2: { 3: public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action<IApplicationBuilder> configureApp); 4: } 5: 6: new WebHostBuilder() 7: .Configure(app => app 8: .UseExceptionHandler("/Home/Error") 9: .UseStaticFiles() 10: .UseIdentity() 11: .UseMvc()) 12: … 如果我们在应用中通过调用上面这个Configure方法来注册所需的中间件,WebHost在启动的时候会创建一个类型为DelegateStartup的Startup对象来完成真正的中间件注册工作。如下面的代码片段所示,DelegateStartup派生于StartupBase这个抽象类,它利用一个在构造时提供的Action<IApplicationBuilder>对象实现了Configure方法。很明显,我们调用IWebHostBuilder的Configure方法指定的Action<IApplicationBuilder>对象将用来创建这个DelegateStartup对象。 1: public class DelegateStartup : StartupBase 2: { 3: private Action<IApplicationBuilder> _configureApp; 4: 5: public DelegateStartup(Action<IApplicationBuilder> configureApp) 6: { 7: _configureApp = configureApp; 8: } 9: 10: public override void Configure(IApplicationBuilder app) 11: { 12: _configureApp(app); 13: } 14: } 如下的代码片段体现了 IWebHostBuilder的扩展方法Configure的实现逻辑。如下面的代码片段所示,这个方法根据提供的Action<IApplicationBuilder>对象创建了一个DelegateStartup对象,并调用ConfigureServices方法以淡例模式注册到WebHostBuilder的服务集合中。这段代码还体现了另一个细节,除了进行Startup的服务注册之外,该方法还对“ApplicationName”选项(对应WebHostOptions的ApplicationName)进行了设置。 1: public static class WebHostBuilderExtensions 2: { 3: public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action<IApplicationBuilder> configureApp) 4: { 5: var startupAssemblyName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name; 6: return hostBuilder 7: .UseSetting("applicationName", startupAssemblyName) 8: .ConfigureServices(svcs => svcs.AddSingleton<IStartup>(new DelegateStartup(configureApp)); 9: } 10: } 二、ConventionBasedStartup 我们知道应用中最常见的服务和中间件注册代码都定义在一个单独的类中,通常直接将其命名为Startup。为了与IStartup接口代表的Startup相区别,我们使用 “启动类(型)” 来称呼这个类。按照约定,启动类中会分别定义一个ConfigureServices和Configure方法来注册服务和中间件。一般情况下,这样的类型一般需要通过调用UseStartup<T>这个扩展方法注册到WebHostBuilder上。 1: public class Startup 2: { 3: public void ConfigureServies(IServiceCollection services); 4: public void Configure(IApplicationBuilder app); 5: } 6: 7: new WebHostBuilder() 8: .UseStartup<Startup>() 9: … 如果我们在应用中将服务和中间件注册的实现定义在启动类型中,当WebHost被启动的时候,ASP.NET Core会创建一个类型为ConventionBasedStartup的Startup对象。这个Startup类型之所以采用这样的命名方式,是因为ASP.NET Core并没有采用接口实现的方式为启动类型做强制性的约束,而仅仅是为作为启动类型的定义提供了一个约定而已,至于具体采用怎样的约定,我们将在后续部分进行详细介绍。 StartupMethods 在了解了启动类型的约定以及常见的定义形式之外,我们现在来讨论这对这个类型创建的ConventionBasedStartup就是怎样的对象。从下面的代码片段可以看出,一个ConventionBasedStartup对象是根据一个类型为StartupMethods对象创建的。顾名思义,StartupMethods只在提供两个用户注册服务和中间件的方法,这两个方法体现在由它的两个属性(ConfigureServicesDelegate和ConfigureDelegate)提供的两个委托对象,这两个委托对象分别实现了定义在ConventionBasedStartup的ConfigureServices和Configure方法。 1: public class ConventionBasedStartup : IStartup 2: { 3: public ConventionBasedStartup(StartupMethods methods); 4: public IServiceProvider ConfigureServices(IServiceCollection services); 5: public void Configure(IApplicationBuilder app); 6: } 7: 8: public class StartupMethods 9: { 10: public Func<IServiceCollection, IServiceProvider> ConfigureServicesDelegate { get; } 11: public Action<IApplicationBuilder> ConfigureDelegate { get; } 12: 13: public StartupMethods(Action<IApplicationBuilder> configure); 14: public StartupMethods(Action<IApplicationBuilder> configure, Func<IServiceCollection, IServiceProvider> configureServices); 15: } StartupLoader 既然ConventionBasedStartup对象是根据提供的一个StartupMethods对象创建的,那么现在的核心问题则变成了这个StartupMethods对象如何根据启动类型创建的。StartupMethods的创建者是一个类型为StartupLoader的对象,如下面的代码片段所示,StartupLoader定了两个名为FindStartupType和LoadMethods静态方法,前者用于启动类型的解析,后者则实现了StartupMethods对象的创建。 1: public class StartupLoader 2: { 3: public static Type FindStartupType(string startupAssemblyName, string environmentName); 4: public static StartupMethods LoadMethods(IServiceProvider services, Type startupType, string environmentName); 5: } 如何选择启动类型 如果启动类型没有通过调用WebHostBuilder的如下两个扩展方法UseStartup/UseStartup<TStartup>的显式注册,那么StartupLoader的FindStartupType方法会被调用来解析出正确的启动类型。这个方法具有两个参数,分别代表启动类型所在的程序集名称和当前环境名称,它们实际上对应着WebHostOptions的两个同名属性。当FindStartupType方法被执行并成功加载了提供的程序集之后,它会按照约定的启动类型全名从该程序集中加载启动类型,候选的启动类型全名按照选择优先级排列如下: Startup{EnvironmentName} (无命名空间) {StartupAssemblyName}.Startup{EnvironmentName} Startup(无命名空间) {StartupAssemblyName}.Startup{EnvironmentName} **. Startup{EnvironmentName}(任意命名空间) **. Startup(任意命名空间) 这个列表体现了启动类型解析过程中选择有效类型名称的一个基本策略,即“环境名称优先”和“无命名空间优先”。我们可以通过一个简单的实例来证明这个策略的存在。我们在一个ASP.NET Core控制台应用中添加一个名为“StartupLib”(程序集也采用这个名称)的类库项目,然后在这个项目中定义如下两组启动类,其中一组具有命名空间,另一组则采用程序集名称作为命名空间。这些启动类都派生于我们自定义的基类StartupBase,后者的Configure方法中注册了一个中间件将自身的类型作为响应内容。对于每组中的三个启动类,一个命名为Startup,另外两个则分别以环境名称( “Development” 和 “Production” )作为后缀。 1: public class StartupBase 2: { 3: public void ConfigureServices(IServiceCollection services){} 4: public void Configure(IApplicationBuilder app) 5: { 6: app.Run(async context => await context.Response.WriteAsync(this.GetType().FullName)); 7: } 8: } 9: 10: public class Startup : StartupBase{} 11: public class StartupDevelopment : StartupBase{} 12: public class StartProduction : StartupBase{} 13: 14: namespace StartupLib 15: { 16: public class Startup : StartupBase{} 17: public class StartupDevelopment : StartupBase{} 18: public class StartProduction : StartupBase{} 19: } 我们采用如下的程序来启动一个ASP.NET Core应用。如下面的代码代码片段所示,我们在利用WebHostBuilder创建并启动WebHost之前,调用UseSettings方法以配置的形式指定了启动程序集(“StartupLib”)和当前运行环境(“Development”)的名称。 1: public class Program 2: { 3: public static void Main() 4: { 5: new WebHostBuilder() 6: .UseKestrel() 7: .UseSetting("startupAssembly", "StartupLib") 8: .UseSetting("environment", "Development") 9: .Build() 10: .Run(); 11: } 12: } 根据上述的启动类型解析规则,对于六个候选的启动类型,最终被选择的是不具有命名空间的StartupDevelopment类型。当应用启动之后,我们利用浏览器请求应用监听地址(“http://localhost:5000”),这个被选择的启动程序的名称将会以如下的形式直接显示出来。 如何选择服务注册方法和中间件注册方法 在了解了ASP.NET Core针对启动类型命名的约定之后,我们来讨论一下定义在启动类中用于注册服务和中间件的两个方法的约定。这两个方法可以是静态方法,也可以是实例方法。从方法命名来看,这两个方法除了命名为“ConfigureServices”和“Configure”之外,方法名还可以携带运行环境名称,具体采用的格式分别为“Configure{EnvironmentName}Services”和“Configure{EnvironmentName}”,后者具有更高的选择优先级。 ConfigureServices/Configure{EnvironmentName}Services方法具有一个类型为IServiceCollection接口的参数,表示存放注册服务的ServiceCollection对象。如过这个方法没有定义任何参数,它依然是合法的。一般来说,这个方法不具有返回值(返回类型为void),但是它也可以定义成一个返回类型为IServiceProvider的方法。如果这个方法返回一个ServiceProvider对象,后续过程中获取的所有服务将从这个ServiceProvider中提取。对于没有返回值的情况,系统会根据当前注册的服务创建一个ServiceProvider。 Configure/Configure{EnvironmentName}方法只要求只要求第一个参数类型采用IApplicationBuilder接口,至于这个方法可以包含多少个参数,各个参数应该具有怎样的类型,并未做任何规定。实际上我们为这个方法定义任意后续参数都是合法的。当ConventionBasedStartup在调用这个方法的时候,同样是采用依赖注入的方式来提供这些参数。如下面的代码片段所示,我们为启动类的Configure方法定义相应的参数来直接使用在ConfigureServices方法上注册的三个服务。 1: new WebHostBuilder() 2: .ConfigureServices(services => services.AddSingleton<IFoobar, Foobar>()) 3: … 4: 5: public class Startup 6: { 7: public Startup(IFoobar foobar) 8: { 9: Debug.Assert(foobar.GetType() == typeof(Foobar)); 10: } 11: public void ConfigureServies(IServiceCollection services) ; 12: public void Configure(IApplicationBuilder app) ; 13: } StartupMethods对象的创建 除此之外,对于定义成实例类型的启动类,我们并不要求它具有一个默认无参的构造函数。如果构造函数具有参数,ConventionBasedStartup在实例化的时候会采用构造函数注入的方式来提供构造函数的参数。至于提供参数所用的ServiceProvider,就是WebHostBuilder在创建WebHost对象时作为构造函数参数提供的那个ServiceProvider。如下面的代码片段所示,我们利用WebHostBuilder创建并启动WebHost之前,调用他的ConfigureServices方法针对接口IFoobar注册了一个服务,那么注册为启动类的Startup类可以在构造函数中以注入的形式使用这个服务对象。 1: public class StartupLoader 2: { 3: public static StartupMethods LoadMethods(IServiceProvider serviceProvider, Type startupType, string environmentName) 4: { 5: return new StartupMethods(BuildConfigureDelegate(serviceProvider, startupType, environmentName), 6: BuildConfigureServicesDelegate(serviceProvider, startupType, environmentName)); 7: } 8: 9: private static Func<IServiceCollection, IServiceProvider> BuildConfigureServicesDelegate(IServiceProvider serviceProvider, Type startupType, string environmentName) 10: { 11: MethodInfo method = FindMethod(startupType, $"Configure{environmentName}Services", "ConfigureServices"); 12: object instance = method.IsStatic ? null : ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, startupType); 13: return services => 14: { 15: object[] arguments = method.GetParameters().Length > 0 ? new object[] { services } : new object[0]; 16: return (method.Invoke(instance, arguments) as IServiceProvider) ?? services.BuildServiceProvider(); 17: }; 18: } 19: 20: private static Action<IApplicationBuilder> BuildConfigureDelegate(IServiceProvider serviceProvider, Type startupType, string environmentName) 21: { 22: MethodInfo method = FindMethod(startupType, $"Configure{environmentName}", "Configure"); 23: object instance = method.IsStatic ? null : ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, startupType); 24: object[] arguments = method.GetParameters().Select(p => serviceProvider.GetService(p.ParameterType)).ToArray(); 25: return app => 26: { 27: arguments[0] = app; 28: method.Invoke(instance, arguments); 29: }; 30: } 31: 32: private static MethodInfo FindMethod(Type startupType, string method1, string method2) 33: { 34: BindingFlags bindAttribute = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; 35: return startupType.GetMethod(method1, bindAttribute)?? startupType.GetMethod(method2, bindAttribute); 36: } 37: } UseStartup方法究竟做了些什么? 当我们调用IWebHostBuilder接口的扩展方法UseStartup/UseStartup<TStartup>注册某个启动类的时候,该方法会按照如下的形式创建一个ConventionBasedStartup对象并注册到WebHostBuilder的服务集合上。和上面介绍的Configure方法一样,UseStartup方法同样会设置 “ApplicationName” 选项。除此之外,这段还体现了另一个细节,那就是如果我们直接定义一个实现了IStartup接口的启动类,UseStartup方法会直接注册这个类型,而不会再多此一举地创建一个ConventionBasedStartup对象。 1: public static class WebHostBuilderExtensions 2: { 3: public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder) where TStartup : class 4: { 5: return UseStartup(hostBuilder, typeof(TStartup)); 6: } 7: 8: public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType) 9: { 10: var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name; 11: return hostBuilder 12: .UseSetting("ApplicationName", startupAssemblyName) 13: .ConfigureServices(svcs => 14: { 15: if (typeof(IStartup).IsAssignableFrom(startupType)) 16: { 17: svcs.AddSingleton(typeof(IStartup), startupType); 18: } 19: else 20: { 21: svcs.AddSingleton<IStartup>(sp => new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, sp.GetService<IHostingEnvironment>().EnvironmentName))); 22: } 23: }); 24: } 25: } 三、选择哪一个Startup 应用启动的时候,Startup帮助我们完成所需服务和中间件的注册,而Startup对象自身也是服务的形式被注册到WebHostBuilder或者WebHost的服务集合中。总的来说,Startup的注册具有如下三种途径: 调用IWebHostBuilder的扩展方法Configure方法创建并注册一个DelegateStartup对象。 调用IWebHostBuilder的扩展方法UseStartup或者UseStartup<TStartup>创建并注册一个ConventionBasedStartup对象。 如果设置了启动程序集名称(对应WebHostOptions的StartupAssembly属性)并且对应的程序集中存在一个满足约定的启动类型,也会创建并注册一个ConventionBasedStartup对象。 那么现在的问题来说,如果我们采用上述这三种途径创建并注册了多个Startup,那么系统是只选择其中一个,还是所有的Startup均有效呢?就如下这段程序来说,如果当前程序集同时定义了三个有效的Startup类型(Startup、Startup1和Startup2),最终将会有五个Startup对象被注册,其中两个是通过Configure方法注册的DelegateStartup对象,对于额外三个ConventionBasedStartup对象来说, 其中两个针对显式指定的启动类型(Startup1和Startup2),另外一个则是针对默认的约定解析出来的启动类型(Startup)。对于这个五个Startup对象,究竟哪些是有效的呢? 1: new WebHostBuilder() 2: .Configure(app => {}) 3: .Configure(app => {}) 4: .UseStartup<Startup1>() 5: .UseStartup<Startup2>() 6: .UseSetting("startupAssembly", Assembly.GetEntryAssembly().FullName) 7: … 不论我们注册多少个Startup,系统最终都只会其中一个来注册服务和中间件。由于WebHost会直接利用ServiceProvider来获取Startup对象,根据 “后来居上” 的原则,最终选择的总是最后注册的那个Startup。由于Configure方法和UseStartup方法最终都是调用WebHostBuilder的ConfigureServices方法进行服务注册,所以最后调用的方法具有最高的优先级。至于根据指定启动程序集名称而创建出来的ConventionBasedStartup,针对它的注册信息会放在最前面,所以具有最低优先级。根据这个策略,上面这段程序最终选择的启动类是Startup2。 作者:蒋金楠 微信公众账号:大内老A 微博: www.weibo.com/artech 如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 原文链接

优秀的个人博客,低调大师

一脸懵逼学习Hdfs---动态增加节点和副本数量管理(Hdfs动态扩容)

1:按照上篇博客写的,将各个进程都启动起来: 集群规划: 主机名 IP 安装的软件 运行的进程 master 192.168.3.129 jdk、hadoop NameNode、DFSZKFailoverController(zkfc) slaver1 192.168.3.130 jdk、hadoop NameNode、DFSZKFailoverController(zkfc) slaver2 192.168.3.131 jdk、hadoop ResourceManager slaver3 192.168.3.132 jdk、hadoop ResourceManager slaver4 192.168.3.133 jdk、hadoop、zookeeper DataNode、NodeManager、JournalNode、QuorumPeerMain slaver5 192.168.3.134 jdk、hadoop、zookeeper DataNode、NodeManager、JournalNode、QuorumPeerMain slaver6 192.168.3.135 jdk、hadoop、zookeeper DataNode、NodeManager、JournalNode、QuorumPeerMain 2:开始测试动态增加节点和副本数量管理: 首先将master节点的datanode挂掉(即少了一个保存文件的副本): 注意:hadoop datanode节点超时时间设置: datanode进程死亡或者网络故障造成datanode无法与namenode通信,namenode不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长。HDFS默认的超时时长为10分钟+30秒。如果定义超时时间为timeout,则超时时长的计算公式为: timeout = 2 * heartbeat.recheck.interval + 10 * dfs.heartbeat.interval。 而默认的heartbeat.recheck.interval 大小为5分钟,dfs.heartbeat.interval默认为3秒。 需要注意的是hdfs-site.xml 配置文件中的: heartbeat.recheck.interval的单位为毫秒, dfs.heartbeat.interval的单位为秒。 所以,举个例子,如果heartbeat.recheck.interval设置为5000(毫秒),dfs.heartbeat.interval设置为3(秒,默认),则总的超时时间为40秒。 hdfs-site.xml中的参数设置格式:<property><name>heartbeat.recheck.interval</name><value>2000</value></property><property><name>dfs.heartbeat.interval</name><value>1</value></property> 注意:HDFS冗余数据块的自动删除: 在日常维护hadoop集群的过程中发现这样一种情况: 某个节点由于网络故障或者DataNode进程死亡,被NameNode判定为死亡, HDFS马上自动开始数据块的容错拷贝; 当该节点重新添加到集群中时,由于该节点上的数据其实并没有损坏, 所以造成了HDFS上某些block的备份数超过了设定的备份数。 通过观察发现,这些多余的数据块经过很长的一段时间才会被完全删除掉, 那么这个时间取决于什么呢? 该时间的长短跟数据块报告的间隔时间有关。 Datanode会定期将当前该结点上所有的BLOCK信息报告给Namenode, 参数dfs.blockreport.intervalMsec就是控制这个报告间隔的参数。 hdfs-site.xml文件中有一个参数:<property><name>dfs.blockreport.intervalMsec</name><value>10000</value><description>Determines block reporting interval in milliseconds.</description></property> 其中3600000为默认设置,3600000毫秒,即1个小时,也就是说,块报告的时间间隔为1个小时,所以经过了很长时间这些多余的块才被删除掉。通过实际测试发现,当把该参数调整的稍小一点的时候(60秒),多余的数据块确实很快就被删除了。 3:停止一下集群,配置一下hadoop datanode节点超时时间设置和HDFS冗余数据块的自动删除,停止集群如下所示: 依次查看一下各个节点的进程启动情况: 然后将slaver5和slaver6的yarn进程停掉: 然后依次关掉zookeeper的进程:如master,slaver1和slaver2都一样,这里不再重复了: [root@master bin]# ./zkServer.sh stop 现在修改配置一下:hadoop datanode节点超时时间设置和HDFS冗余数据块的自动删除的配置文件hdfs-site.xml: hadoop datanode节点超时时间设置 hdfs-site.xml中的参数设置格式: <property> <name>heartbeat.recheck.interval</name> <value>2000</value> </property> <property> <name>dfs.heartbeat.interval</name> <value>1</value> </property> HDFS冗余数据块的自动删除 hdfs-site.xml文件中有一个参数: <property> <name>dfs.blockreport.intervalMsec</name> <value>10000</value> <description>Determines block reporting interval in milliseconds.</description> </property> 操作如下所示: [root@slaver3 hadoop]# vim hdfs-site.xml 将修改的hdfs-site.xml复制到其他6个节点上面,如下所示: 4:将集群启动起来: 4.1:启动zookeeper集群(分别在master、slaver1、slaver2上启动zookeeper): 4.2:启动journalnode(分别在master、slaver1、slaver2上执行) 4.3:千万不要执行格式化HDFS,千万不要执行格式化ZKFC(在slaver3上执行即可),不然还是报很多错误; 4.4:然后在slaver3启动start-dfs.sh 现在可以去其他节点看看,全部进程都可以正常启动,如果你想启动yarn进程,下面启动yarn进程,slaver5节点和slaver6节点操作一样,这里只贴slaver5即可: 如果你耐心十足,搞了一天,想吐,这里还是再贴一下jps查看的进程情况吧: 5:然后将master的namenode干掉,看看Live Nodes是否变化: 经过14s后,活着的node数目就由3变成了2: 如何新加一个datanode,再搞一个虚拟机(我再新建一个虚拟机,不知道我的电脑撑住撑不住,试试吧先),然后将hadoop的安装包复制过去,然后将datanode启动起来:好吧,最后没有弄出来,以后有机会好好补一下这点,动态增加节点和副本数量管理; 补充要点: 注意:hdfs动态扩容要点,将手动启动节点: hadoop-daemon.sh start namenode 启动namenode hadoop-daemon.sh start datanode 启动datanode. 即可以动态加入,只不过没写到slavers里面,下次重启以后,手动加入即可完成动态扩容了。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册