Springboot Actuator之八:actuator的执行原理
本文接着《Springboot Actuator之七:actuator 中原生endpoint源码解析1》,前面主要分析了原生endpoint的作用。
现在着重了解actuator的执行原理。
在前面一篇文章中,我们已经了解endpoint的暴露方式有http(spring MVC)协议,jmx协议。
整体实现思路是将端点(Endpoint)适配委托给MVC层策略端点(MvcEndpoint),再通过端点MVC适配器(EndpointMvcAdapter)将端点暴露为HTTP请求方式的MVC端点,最后分别使用端点自动配置(EndpointAutoConfiguration)和MVC方式暴露端点的配置(EndpointWebMvcManagementContextConfiguration)来注入端点组件和端点处理程序映射组件、MVC端点注册表组件、MVC端点组件。
其中,端点处理程序映射(EndpointHandlerMapping)通过Spring MVC方式来暴露MVC端点。最后,本文以“shutdown端点示例”收尾。
现在就按照整体实现思路来剖析HTTP端点的实现原理。
1、端点接口(Endpoint<T>)
其抽象实现基类 AbstractEndpoint<T>
2、MVC层策略端点(MvcEndpoint)
- /**
- * 实现类允许使用@RequestMapping和完整的Spring MVC机制,
- * 但不能在类型级别使用@Controller或@RequestMapping,因为这将导致路径的双重映射,
- * 一次通过常规MVC处理程序映射,一次通过{@link EndpointHandlerMapping}。
- *
- * @author Dave Syer
- * @see NamedMvcEndpoint
- */
- // 核心接口 在端点之上的MVC层策略
- public interface MvcEndpoint {
- /**
- * 禁用端点的响应实体
- */
- ResponseEntity<Map<String, String>> DISABLED_RESPONSE = new ResponseEntity<>(
- Collections.singletonMap("message", "This endpoint is disabled"),
- HttpStatus.NOT_FOUND);
- // 核心方法 返回端点的MVC路径
- String getPath();
- /**
- * 返回端点是否暴露敏感信息。
- */
- boolean isSensitive();
- // 核心方法 返回端点暴露的类型/null
- @SuppressWarnings("rawtypes")
- Class<? extends Endpoint> getEndpointType();
- }
2.1、包括逻辑名称的MVC端点(NamedMvcEndpoint)
- /**
- * 名称提供了引用端点的一致方式。
- *
- * @author Madhura Bhave
- * @since 1.5.0
- */
- // 包括逻辑名称的MVC端点
- public interface NamedMvcEndpoint extends MvcEndpoint {
- /**
- * 返回端点的逻辑名称。
- */
- String getName();
- }
- }
3、端点MVC适配器(EndpointMvcAdapter)
- /**
- * 暴露端点({@link Endpoint})为MVC端点({@link MvcEndpoint})的适配器。
- */
- // 端点MVC适配器
- public class EndpointMvcAdapter extends AbstractEndpointMvcAdapter<Endpoint<?>> {
- /**
- * Create a new {@link EndpointMvcAdapter}.
- * @param delegate the underlying {@link Endpoint} to adapt. (用于适配的底层端点)
- */
- public EndpointMvcAdapter(Endpoint<?> delegate) {
- super(delegate); // 委托代理
- }
- // 核心实现 以HTTP GET方式调用
- @Override
- @ActuatorGetMapping
- @ResponseBody
- public Object invoke() {
- return super.invoke(); // 向上调用,链式模式
- }
- }
@ActuatorGetMapping注解源码:
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @RequestMapping(method = RequestMethod.GET, produces = {
- ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,
- MediaType.APPLICATION_JSON_VALUE })
- @interface ActuatorGetMapping {
- /**
- * Alias for {@link RequestMapping#value}.
- * @return the value
- */
- @AliasFor(annotation = RequestMapping.class)
- String[] value() default {};
- }
其抽象实现基类 AbstractEndpointMvcAdapter<E extends Endpoint<?>>
- /**
- * MVC端点({@link MvcEndpoint})实现的抽象基类。
- *
- * @param <E>
- * The delegate endpoint (代理的端点)
- * @author Dave Syer
- * @since 1.3.0
- */
- public abstract class AbstractEndpointMvcAdapter<E extends Endpoint<?>> implements NamedMvcEndpoint {
- /**
- * 被代理的底层端点(端点子类)
- */
- private final E delegate;
- /**
- * 端点URL路径
- */
- private String path;
- public AbstractEndpointMvcAdapter(E delegate) {
- Assert.notNull(delegate, "Delegate must not be null");
- this.delegate = delegate;
- }
- // 核心实现 调用底层端点,并返回调用结果
- protected Object invoke() {
- if (!this.delegate.isEnabled()) { // 端点被禁用
- // Shouldn't happen - shouldn't be registered when delegate's disabled
- return getDisabledResponse();
- }
- return this.delegate.invoke(); // 调用端点
- }
- public E getDelegate() {
- return this.delegate;
- }
- @Override
- public String getName() {
- return this.delegate.getId(); // name = id
- }
- @Override
- public String getPath() {
- return (this.path != null ? this.path : "/" + this.delegate.getId()); // "/id"
- }
- public void setPath(String path) {
- while (path.endsWith("/")) {
- path = path.substring(0, path.length() - 1);
- }
- if (!path.startsWith("/")) {
- path = "/" + path;
- }
- this.path = path;
- }
- @Override
- @SuppressWarnings("rawtypes")
- public Class<? extends Endpoint> getEndpointType() {
- return this.delegate.getClass();
- }
- }
4、端点组件自动配置
基于Spring Boot的自动配置机制(Auto-configuration),其自动配置文件位于spring-boot-actuator资源目录下的META-INF/spring.factories文件:
- # 启用自动配置
- org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
- ...
- org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
- ...
- # 管理上下文配置
- org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
- org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
- ...
4.1、公共管理的端点自动配置(EndpointAutoConfiguration)
4.2、全局的端点属性(EndpointProperties)
4.3、外部化配置的注解(ConfigurationProperties)
- /**
- * 如果要绑定和验证一些外部属性(例如来自.properties文件),请将其添加到@Configuration类中的类定义或@Bean方法。
- */
- // 外部化配置的注解
- @Target({ ElementType.TYPE, ElementType.METHOD })
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface ConfigurationProperties {
- // 属性的名称前缀
- @AliasFor("value")
- String prefix() default "";
- }
5、MVC方式暴露端点的配置(EndpointWebMvcManagementContextConfiguration)
- // 核心类 通过MVC方式来暴露端点的配置
- @ManagementContextConfiguration
- @EnableConfigurationProperties({ HealthMvcEndpointProperties.class, EndpointCorsProperties.class })
- public class EndpointWebMvcManagementContextConfiguration {
- private final HealthMvcEndpointProperties healthMvcEndpointProperties;
- /**
- * 管理服务器的属性
- */
- private final ManagementServerProperties managementServerProperties;
- private final EndpointCorsProperties corsProperties;
- /**
- * 端点处理程序的映射定制程序
- */
- private final List<EndpointHandlerMappingCustomizer> mappingCustomizers;
- // 核心方法 注入端点处理程序映射组件
- @Bean
- @ConditionalOnMissingBean // 组件未注入
- public EndpointHandlerMapping endpointHandlerMapping() {
- // 注册的MVC端点集合
- Set<MvcEndpoint> endpoints = mvcEndpoints().getEndpoints();
- CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
- // 端点处理程序映射
- EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints, corsConfiguration);
- // 管理端点的上下文路径前缀
- mapping.setPrefix(this.managementServerProperties.getContextPath());
- // MVC端点安全处理程序拦截器
- MvcEndpointSecurityInterceptor securityInterceptor = new MvcEndpointSecurityInterceptor(
- this.managementServerProperties.getSecurity().isEnabled(),
- this.managementServerProperties.getSecurity().getRoles());
- mapping.setSecurityInterceptor(securityInterceptor);
- for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
- customizer.customize(mapping);
- }
- return mapping;
- }
- // 核心方法 注入MVC端点注册表组件
- @Bean
- @ConditionalOnMissingBean // 组件未注入
- public MvcEndpoints mvcEndpoints() {
- return new MvcEndpoints();
- }
- @Bean
- @ConditionalOnBean(EnvironmentEndpoint.class)
- @ConditionalOnEnabledEndpoint("env")
- public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
- return new EnvironmentMvcEndpoint(delegate);
- }
- @Bean
- @ConditionalOnBean(HealthEndpoint.class)
- @ConditionalOnEnabledEndpoint("health")
- public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) {
- HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
- this.managementServerProperties.getSecurity().isEnabled());
- if (this.healthMvcEndpointProperties.getMapping() != null) {
- healthMvcEndpoint.addStatusMapping(this.healthMvcEndpointProperties.getMapping());
- }
- return healthMvcEndpoint;
- }
- // 注入关闭应用程序的MVC端点组件
- @Bean
- @ConditionalOnBean(ShutdownEndpoint.class) // 组件已实例化
- @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false) // 端点已启用
- public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
- return new ShutdownMvcEndpoint(delegate);
- }
- }
5.1、MVC端点注册表(MvcEndpoints)
1、MvcEndpoints实现了ApplicationContextAware,取ApplicationContext;
2、MvcEndpoints实现了InitializingBean ,在容器启动后调用afterPropertiesSet()方法适配那些通用的endpoint;
- * 所有MVC端点组件的注册表,以及一组用于包装尚未公开的MVC端点的现有端点实例的通用工厂。
- */
- // 核心类 MVC端点注册表
- public class MvcEndpoints implements ApplicationContextAware, InitializingBean {
- /**
- * 应用上下文
- */
- private ApplicationContext applicationContext;
- /**
- * MVC端点集合
- */
- private final Set<MvcEndpoint> endpoints = new HashSet<>();
- /**
- * MVC端点类型集合
- */
- private Set<Class<?>> customTypes;
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
- @Override
- public void afterPropertiesSet() throws Exception {
- //1、从applicationContext中检索出来已经实例化的MVC端点列表
- Collection<MvcEndpoint> existing = BeanFactoryUtils
- .beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class) // MVC端点
- .values();
- this.endpoints.addAll(existing);
- this.customTypes = findEndpointClasses(existing);
- //2、从applicationContext中检索出来已经实例化的代理端点列表
- @SuppressWarnings("rawtypes")
- Collection<Endpoint> delegates = BeanFactoryUtils
- .beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class) // 端点
- .values();
- for (Endpoint<?> endpoint : delegates) {
- //3、判断是否是通用的endpoint
- if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
- EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint); // 端点MVC适配器
- // 端点路径
- String path = determinePath(endpoint, this.applicationContext.getEnvironment());
- if (path != null) {
- adapter.setPath(path);
- }
- this.endpoints.add(adapter);
- }
- }
- }
- private Set<Class<?>> findEndpointClasses(Collection<MvcEndpoint> existing) {
- Set<Class<?>> types = new HashSet<>();
- for (MvcEndpoint endpoint : existing) {
- Class<?> type = endpoint.getEndpointType(); // 端点类型
- if (type != null) {
- types.add(type);
- }
- }
- return types;
- }
- // 核心方法 返回注册的MVC端点集合
- public Set<MvcEndpoint> getEndpoints() {
- return this.endpoints;
- }
- /**
- * 返回指定类型的MVC端点集合。
- */
- @SuppressWarnings("unchecked")
- public <E extends MvcEndpoint> Set<E> getEndpoints(Class<E> type) {
- Set<E> result = new HashSet<>(this.endpoints.size());
- for (MvcEndpoint candidate : this.endpoints) {
- if (type.isInstance(candidate)) {
- result.add((E) candidate);
- }
- }
- return Collections.unmodifiableSet(result); // 不可修改的集合
- }
- // 判断是否是通用的端点
- private boolean isGenericEndpoint(Class<?> type) {
- return
- //第一步中扫描的已经实例化的MVC端点中不存在
- !this.customTypes.contains(type)
- //确定此类对象表示的类或接口是否与由指定的类参数表示的类或接口相同,或者是该类或接口的超类或超接口。
- && !MvcEndpoint.class.isAssignableFrom(type);
- }
- private String determinePath(Endpoint<?> endpoint, Environment environment) {
- // 配置属性
- ConfigurationProperties configurationProperties = AnnotationUtils.findAnnotation(endpoint.getClass(),
- ConfigurationProperties.class);
- if (configurationProperties != null) {
- return environment.getProperty(configurationProperties.prefix() + ".path");
- }
- return null;
- }
- }
5.2、端点处理程序映射(EndpointHandlerMapping)
- /**
- * handlerMapping通过endpoint.getid()将端点映射到URL。@RequestMapping的语义应该与普通的@Controller相同,
- * 但是端点不应该被注释为@Controller,否则它们将被正常的MVC机制映射。 <p>
- * <p>
- * 映射的目标之一是支持作为HTTP端点工作的端点, 但是当没有HTTP服务器(类路径上没有Spring MVC)时,仍然可以提供有用的服务接口。
- * 注意:具有方法签名的任何端点将在非Servlet环境下中断。
- */
- // 核心类 通过端点的逻辑标识将端点映射到URL的处理程序映射
- public class EndpointHandlerMapping extends AbstractEndpointHandlerMapping<MvcEndpoint> {
- /**
- * Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s
- * will be detected from the {@link ApplicationContext}. The endpoints will
- * accepts CORS requests based on the given {@code corsConfiguration}.
- *
- * @param endpoints
- * the endpoints (MVC端点列表)
- * @param corsConfiguration
- * the CORS configuration for the endpoints
- * @since 1.3.0
- */
- public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints, CorsConfiguration corsConfiguration) {
- super(endpoints, corsConfiguration);
- }
- }
其抽象实现基类 AbstractEndpointHandlerMapping<E extends MvcEndpoint>
- package com.dxz.inject;
- /**
- * @RequestMapping的语义应该与普通的@Controller相同, 但是端点不应该被注释为@Controller,否则它们将被正常的MVC机制映射。
- * 映射的目标之一是支持作为HTTP端点工作的端点,但是当没有HTTP服务器(类路径上没有Spring MVC)时,仍然可以提供有用的服务接口。
- * 注意:具有方法签名的任何端点将在非Servlet环境下中断。
- *
- * @param <E>
- * The endpoint type (端点类型)
- */
- // 核心类 通过端点的逻辑标识将端点映射到URL的处理程序映射的抽象基类
- public abstract class AbstractEndpointHandlerMapping<E extends MvcEndpoint> extends RequestMappingHandlerMapping {
- /**
- * MVC端点集合
- */
- private final Set<E> endpoints;
- /**
- * 安全处理程序拦截器
- */
- private HandlerInterceptor securityInterceptor;
- /**
- * CORS配置
- */
- private final CorsConfiguration corsConfiguration;
- /**
- * 端点的映射路径前缀
- */
- private String prefix = "";
- private boolean disabled = false;
- /**
- * <p>
- * 将从应用上下文检测到所有端点。
- */
- public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints, CorsConfiguration corsConfiguration) {
- this.endpoints = new HashSet<>(endpoints);
- postProcessEndpoints(this.endpoints);
- this.corsConfiguration = corsConfiguration;
- // By default the static resource handler mapping is LOWEST_PRECEDENCE - 1
- // and the RequestMappingHandlerMapping is 0 (we ideally want to be before both)
- // 默认情况下,静态资源处理程序映射的顺序是 LOWEST_PRECEDENCE - 1
- setOrder(-100);
- setUseSuffixPatternMatch(false);
- }
- /**
- * Post process the endpoint setting before they are used. Subclasses can add or
- * modify the endpoints as necessary.
- * <p>
- * 在使用之前,后处理端点设置。
- *
- * @param endpoints
- * the endpoints to post process
- */
- protected void postProcessEndpoints(Set<E> endpoints) {
- }
- @Override
- public void afterPropertiesSet() {
- super.afterPropertiesSet();
- if (!this.disabled) { // 端点处理程序被禁用
- for (MvcEndpoint endpoint : this.endpoints) {
- detectHandlerMethods(endpoint);
- }
- }
- }
- // 核心实现 注册端点处理程序方法及其唯一映射
- @Override
- @Deprecated
- protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
- if (mapping == null) {
- return;
- }
- String[] patterns = getPatterns(handler, mapping);
- if (!ObjectUtils.isEmpty(patterns)) {
- super.registerHandlerMethod(handler, method, withNewPatterns(mapping, patterns));
- }
- }
- private String[] getPatterns(Object handler, RequestMappingInfo mapping) {
- if (handler instanceof String) { // 组件名称
- handler = getApplicationContext().getBean((String) handler);
- }
- Assert.state(handler instanceof MvcEndpoint, "Only MvcEndpoints are supported");
- String path = getPath((MvcEndpoint) handler); // MVC端点路径
- return (path == null ? null : getEndpointPatterns(path, mapping));
- }
- protected String getPath(MvcEndpoint endpoint) {
- return endpoint.getPath();
- }
- // 核心实现 返回端点的路径列表
- private String[] getEndpointPatterns(String path, RequestMappingInfo mapping) {
- // 路径模式前缀
- String patternPrefix = StringUtils.hasText(this.prefix) ? this.prefix + path : path;
- // 默认的路径集合
- Set<String> defaultPatterns = mapping.getPatternsCondition().getPatterns();
- if (defaultPatterns.isEmpty()) {
- // 端点路径
- return new String[] { patternPrefix, patternPrefix + ".json" };
- }
- List<String> patterns = new ArrayList<>(defaultPatterns);
- for (int i = 0; i < patterns.size(); i++) {
- patterns.set(i, patternPrefix + patterns.get(i)); // 端点请求路径
- }
- return patterns.toArray(new String[patterns.size()]);
- }
- // 新的端点路径
- private RequestMappingInfo withNewPatterns(RequestMappingInfo mapping, String[] patternStrings) {
- // 模式请求条件
- PatternsRequestCondition patterns = new PatternsRequestCondition(patternStrings, null, null,
- useSuffixPatternMatch(), useTrailingSlashMatch(), null);
- return new RequestMappingInfo(patterns, mapping.getMethodsCondition(), mapping.getParamsCondition(),
- mapping.getHeadersCondition(), mapping.getConsumesCondition(), mapping.getProducesCondition(),
- mapping.getCustomCondition());
- }
- // 核心实现 获取处理程序执行链
- @Override
- protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
- HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
- if (this.securityInterceptor == null || CorsUtils.isCorsRequest(request)) {
- return chain;
- }
- return addSecurityInterceptor(chain);
- }
- private HandlerExecutionChain addSecurityInterceptor(HandlerExecutionChain chain) {
- // 处理程序拦截器
- List<HandlerInterceptor> interceptors = new ArrayList<>();
- if (chain.getInterceptors() != null) {
- interceptors.addAll(Arrays.asList(chain.getInterceptors()));
- }
- // 添加安全处理程序拦截器
- interceptors.add(this.securityInterceptor);
- return new HandlerExecutionChain(chain.getHandler(),
- interceptors.toArray(new HandlerInterceptor[interceptors.size()]));
- }
- // 获取端点的路径
- public String getPath(String endpoint) {
- return this.prefix + endpoint;
- }
- // 返回MVC端点集合
- public Set<E> getEndpoints() {
- return Collections.unmodifiableSet(this.endpoints); // 不可修改的集合
- }
- }
5.3、组件存在条件(OnBeanCondition)
5.3.1、未注入组件条件(ConditionalOnMissingBean)
5.3.2、组件条件(ConditionalOnBean)
6、shutdown端点示例
6.1、关闭应用程序的端点(ShutdownEndpoint)
6.2、关闭应用程序的MVC端点(ShutdownMvcEndpoint)
- /**
- * 暴露关闭应用上下文端点({@link ShutdownEndpoint})为MVC端点({@link MvcEndpoint})的适配器。
- */
- // 关闭应用程序的MVC端点
- @ConfigurationProperties(prefix = "endpoints.shutdown") // 属性前缀配置
- public class ShutdownMvcEndpoint extends EndpointMvcAdapter {
- public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {
- super(delegate); // 委托代理
- }
- // 核心实现 以HTTP POST方式调用
- @PostMapping(produces = { ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
- @ResponseBody
- @Override
- public Object invoke() {
- if (!getDelegate().isEnabled()) { // 端点被禁用
- return getDisabledResponse();
- }
- return super.invoke(); // 向上调用,链式模式
- }
- }
原文:https://blog.csdn.net/shupili141005/article/details/61476546
Springboot Actuator之八:actuator的执行原理的更多相关文章
- 【spring cloud】【spring boot】网管服务-->配置文件添加endpoints.enabled = false,SpringBoot应用监控Actuator使用的安全隐患
转载:https://xz.aliyun.com/t/2233 ==================================================================== ...
- SpringBoot执行原理
目录 [Toc] 一.执行原理: 每个Spring Boot项目都有一个主程序启动类,在主程序启动类中有一个启动项目的main()方法, 在该方法中通过执行SpringApplication.run( ...
- springboot项目启动成功后执行一段代码的两种方式
springboot项目启动成功后执行一段代码的两种方式 实现ApplicationRunner接口 package com.lnjecit.lifecycle; import org.springf ...
- Javascript之数据执行原理探究
Javascript在Web服务器端执行原理: 1.客户端请求数据,即我们在上网时在地址栏中输入某个网址,浏览器接收到数据之后,向远程web服务器发送请求报文. 2.web服务器响应请求,web服务器 ...
- Python程序的执行原理(转载)
Python程序的执行原理 2013-09-17 10:35 佚名 tech.uc 1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令 ...
- smarty模板执行原理
为了实现程序的业务逻辑和内容表现页面的分离从而提高开发速度,php 引入了模板引擎的概念,php 模板引擎里面最流行的可以说是smarty了,smarty因其功能强大而且速度快而被广大php web开 ...
- MapReduce调度与执行原理之任务调度
前言 :本文旨在理清在Hadoop中一个MapReduce作业(Job)在提交到框架后的整个生命周期过程,权作总结和日后参考,如有问题,请不吝赐教.本文不涉及Hadoop的架构设计,如有兴趣请参考相关 ...
- MapReduce调度与执行原理之作业提交
前言 :本文旨在理清在Hadoop中一个MapReduce作业(Job)在提交到框架后的整个生命周期过程,权作总结和日后参考,如有问题,请不吝赐教.本文不涉及Hadoop的架构设计,如有兴趣请参考相关 ...
- MapReduce调度与执行原理之作业初始化
前言 :本文旨在理清在Hadoop中一个MapReduce作业(Job)在提交到框架后的整个生命周期过程,权作总结和日后参考,如有问题,请不吝赐教.本文不涉及Hadoop的架构设计,如有兴趣请参考相关 ...
随机推荐
- es6的let与const
es6新增命令let,用于声明变量,他与var的不同主要有三点: let有块级作用域 let没有变量提升 同级作用域内,let不可以重复定义 let有块级作用域: es5 for(var i=0;i& ...
- 使用Fiddler监听java HttpURLConnection请求
使用Fiddler监听java HttpURLConnection请求
- 搜索引擎框架之ElasticSearch基础详解(非原创)
文章大纲 一.搜索引擎框架基础介绍二.ElasticSearch的简介三.ElasticSearch安装(Windows版本)四.ElasticSearch操作客户端工具--Kibana五.ES的常用 ...
- 微信小程序+php 授权登陆,完整代码
先上图 实现流程: 1.授权登陆按钮和正文信息放到了同一个页面,未授权的时候显示登陆按钮,已授权的时候隐藏登陆按钮,显示正文信息,当然也可以授权和正文分开成两个页面,在授权页面的onlo ...
- ThinkPHP3.2.3:使用模块映射隐藏后台真实访问地址(如:替换url里的admin字眼)
例如:项目应用目录/Application下模块如下,默认后台模块为Admin 现在需要修改后台模块的访问地址,以防被别有用心的人很容易就猜到,然后各种乱搞... (在公共配置文件/Applicati ...
- 阿里云查看本服务器 公网ip地址 命令
阿里云的服务器用命令ifconfig查看的是本机内网地址 那如何访问公网地址呢? curl httpbin.org/ip
- Linux shell sed命令使用
Linux处理文本文件的工具: grep 过滤文件内容 sed 编辑文件内容 awk 正则表达式Regex ...
- Kubernetes基础服务架构图
最近看了一些kubernetes的相关资料, 简单的画了一个原理图 欢迎大家批阅
- Linux操作系统的打包/归档工具介绍
Linux操作系统的打包/归档工具介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.
- js冒泡排序,快速排序,插入排序
//冒泡排序 function sortBubble(array){ var len=array.length,i,j,tmp; for(i=len-1;i>=1;i--){ ...