16.springcloud_openfeign之父子容器

news2025/5/25 12:03:57

文章目录

  • 前言
  • springcloud中的父子容器
    • 基础知识了解
    • springcloud提供父子容器的支持
    • springcloud_openfeign对父子容器的应用
  • 总结

前言

在前面springcloud_openfeign的文章中了解到FeignClientsRegistrar在扫描到满足条件的feign接口后会生成BeanDefinition并注入到spring上下文容器, 并且在FeignClientFactoryBean中构建Feign.Builder时会从父子容器中获取相关的组件设置给它, 那么注入到容器中时怎么选择容器, 以及使用时是怎么从容器中获取的呢??, 本节我们就来了解一下springcloud中的父子容器, 以及springcloud_openfeign是怎么使用父子容器的。

springcloud中的父子容器

基础知识了解

在spring框架中有一个非常重要的类org.springframework.context.ApplicationContext, 它有个方法getParent

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    
    /**
     * 返回父上下文,如果没有父上下文,则返回null,并且这是上下文层次结构的根。
     */
	@Nullable
	ApplicationContext getParent();
}

一个上下文可以有父上下文, 如果没有, 那么它是根上下文。有了这个我们可以定义父子上下文的关系。同时它也继承了ListableBeanFactory, 它自己本身也是一个BeanFactory

其中parent属性定义在AbstractApplicationContext, 它是所有子容器的一个父抽象类

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    private ApplicationContext parent;
}

同时, AbstractApplicationContext有一个一级子类GenericApplicationContext, 其它具体实现都直接继承与它, 它有个beanFactory属性

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    private final DefaultListableBeanFactory beanFactory;
}

到这里就可以看出ApplicationContext继承ListableBeanFactory是用来包装它内部的beanFactory属性用的(包装模式哈);

所以ApplicationContext和beanFactory的关系其实就是ApplicationContextbeanFactory的包装类, beanFactory作为ApplicationContext的属性存在。

ApplicationContent依赖关系如图

在这里插入图片描述

springcloud提供父子容器的支持

在springcloud中有一个比较重要的类org.springframework.cloud.context.named.NamedContextFactory, 它提供一种机制,根据名称(比如服务名称)动态创建 ApplicationContext,每个命名上下文都拥有独立的配置和 Bean 定义

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
		implements DisposableBean, ApplicationContextAware {
    /**
     * 给指定容器做初始化扩展用的接口, 就是容器创建之后可以用它进行扩展; {容器名, 可扩展的对象}
     */
    private final Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers;

    /**
     * 子容器们; {容器名, 子容器对象}
     */
    private final Map<String, GenericApplicationContext> contexts = new ConcurrentHashMap<>();

    /**
     * 需要注册到子容器中的对象, 其中default.开头的会被注册到所有子容器中; 
     * {容器名, NamedContextFactory.Specification}
     */
	private final Map<String, C> configurations = new ConcurrentHashMap<>();

    /**
     * 父容器对象
     */
	private ApplicationContext parent;

    /**
     * 默认需要注入到所有子容器中的对象
     */
	private final Class<?> defaultConfigType;
}

以上就是NamedContextFactory中比较核心的属性们, 包含子容器, 子容器初始化的扩展对象和需要注入到子容器中的对象等, 下面我们分析几个核心的方法

创建并初始化子容器

/**
 * 根据名称获取容器对象
 */
protected GenericApplicationContext getContext(String name) {
    // 不存在指定容器名的容器对象
    if (!this.contexts.containsKey(name)) {
        // 锁一下, double check了解一下
        synchronized (this.contexts) {
            if (!this.contexts.containsKey(name)) {
                // 创建容器并添加到注册表中
                this.contexts.put(name, createContext(name));
            }
        }
    }
    return this.contexts.get(name);
}

/**
 * 根据名称创建容器对象
 */
public GenericApplicationContext createContext(String name) {
    // 创建一个容器
    GenericApplicationContext context = buildContext(name);
    // 存在对应容器的ApplicationContextInitializer对象
    if (applicationContextInitializers.get(name) != null) {
        // 使用ApplicationContextInitializer对容器进行初始化
        applicationContextInitializers.get(name).initialize(context);
        // 刷新容器
        context.refresh();
        return context;
    }
    // 注册一些bean到容器中
    registerBeans(name, context);
    // 刷新容器
    context.refresh();
    return context;
}

/**
 * 世家创建容器的方法
 * @param name 容器名称
 */
public GenericApplicationContext buildContext(String name) {
    // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
    // https://github.com/spring-cloud/spring-cloud-openfeign/issues/475
    ClassLoader classLoader = getClass().getClassLoader();
    GenericApplicationContext context;
    // 存在父容器
    if (this.parent != null) {
        // 创建一个子类的beanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 父容器默认是ConfigurableApplicationContext类型
        if (parent instanceof ConfigurableApplicationContext) {
            // 使用和父容器一样的bean类加载器
            beanFactory.setBeanClassLoader(
                    ((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());
        }
        else {
            beanFactory.setBeanClassLoader(classLoader);
        }
        // 默认AotDetector.useGeneratedArtifacts()为false, 所以这里默认是AnnotationConfigApplicationContext
        context = AotDetector.useGeneratedArtifacts() ? new GenericApplicationContext(beanFactory)
                : new AnnotationConfigApplicationContext(beanFactory);
    }
    else {
        // 父容器不存在的话,当前就是根容器,所以不需要设置beanFactory
        context = AotDetector.useGeneratedArtifacts() ? new GenericApplicationContext()
                : new AnnotationConfigApplicationContext();
    }
    context.setClassLoader(classLoader);
    // 给子容器环境上下文添加对象
    context.getEnvironment().getPropertySources().addFirst(
            new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, name)));
    // 这里就给当前子容器添加父容器对象
    if (this.parent != null) {
        context.setParent(this.parent);
    }
    // 设置子容器的名称
    context.setDisplayName(generateDisplayName(name));
    return context;
}

/**
 * 给创建的子容器注册bean
 */
public void registerBeans(String name, GenericApplicationContext context) {
    Assert.isInstanceOf(AnnotationConfigRegistry.class, context);
    AnnotationConfigRegistry registry = (AnnotationConfigRegistry) context;
    // 给子容器配置了注册的bean对象
    if (this.configurations.containsKey(name)) {
        // 将子容器配置的对象都注入进去
        for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
            registry.register(configuration);
        }
    }
    // 将defalut.开头的配置注册到所有子容器中
    for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
        if (entry.getKey().startsWith("default.")) {
            for (Class<?> configuration : entry.getValue().getConfiguration()) {
                registry.register(configuration);
            }
        }
    }
    // 给所有子容器注册默认配置
    registry.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
}

方法小结

  1. 如果存在父容器对象, 那么先创建一个beanFactory, 然后默认创建AnnotationConfigApplicationContext作为子容器, 将这个生成的beanFactory设置给创建的子容器中
  2. 给子容器上下文的属性对象添加传入的默认属性对象
  3. 给当前子容器添加父容器对象

默认生成的子容器是AnnotationConfigApplicationContext对象, 它与是GenericApplicationContext的子类, 提供了注解扫描和解析的功能, 所以我们可以在子容器的ApplicationContextInitializer中使用它去扫描指定的包啥的。

  1. 如果存在子容器对应的ApplicationContextInitializer, 则对创建出来的子容器进行初始化
  2. 如果不存在子容器对应的ApplicationContextInitializer
  • 则将子容器配置的所有对象注册configurations到子容器中
  • default.开头的配置注册到所有子容器中
  • 将默认配置注入到所有子容器中

获取子容器中的对象

/**
 * 从指定容器中指定类型的bean
 * @param name  容器名称
 * @param type  bean的类型
 */
public <T> T getInstance(String name, Class<T> type) {
    // 获取或创建子容器
    GenericApplicationContext context = getContext(name);
    try {
        // 从子容器中获取指定类型的bean
        return context.getBean(type);
    }
    catch (NoSuchBeanDefinitionException e) {
        // ignore
    }
    return null;
}

/**
 * 从指定容器中指定类型和泛型的bean
 * @param name 容器名称
 * @param clazz bean的类型
 * @param generics bean类型的泛型参数
 */
public <T> T getInstance(String name, Class<?> clazz, Class<?>... generics) {
    // 生成一个包含泛型信息的 ResolvableType 对象
    // 例如,如果 clazz 是 List,generics 是 String.class,则 ResolvableType 描述的是 List<String>。
    ResolvableType type = ResolvableType.forClassWithGenerics(clazz, generics);
    return getInstance(name, type);
}

public <T> T getInstance(String name, ResolvableType type) {
    // 获取或者创建子容器
    GenericApplicationContext context = getContext(name);
    // 获取当前上下文及其祖先上下文中,符合指定类型的所有 Bean 名称
    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type);
    for (String beanName : beanNames) {
        //  检查 Bean 的类型是否与 ResolvableType 匹配
        if (context.isTypeMatch(beanName, type)) {
            // 从当前容器或者父子容器中获取bean实例
            return (T) context.getBean(beanName);
        }
    }
    return null;
}


public <T> Map<String, T> getInstances(String name, Class<T> type) {
    // 获取指定名称的容器
    GenericApplicationContext context = getContext(name);
	// 从父子容器中获取指定类型的所有 Bean 实例,并返回一个 Map,其中包含 Bean 的名称和实例
    return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}

springcloud_openfeign对父子容器的应用

之前聊到@EnableFeignClients注解使用@Import引入了FeignClientsRegistrar对象, 然后在ImportBeanDefinitionRegistrar的回调方法中注入了一些对象, 如下

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 注册FeignClientSpecification的BeanDefinition
    registerDefaultConfiguration(metadata, registry);

    // 扫描FeignClient注解, 将feign的接口的接口注册成一个BeanDefinition到spring中
    registerFeignClients(metadata, registry);
}

registerDefaultConfiguration

/**
 * 将defaultConfiguration对应的类封装成FeignClientSpecification对象, 并且注册成一个BeanDefinition
 */
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 获取EnableFeignClients注解的属性
    Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

    // 存在defaultConfiguration属性值
    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
        String name;
        // 判断当前类是否是内部类
        if (metadata.hasEnclosingClass()) {
            // default. + EnableFeignClients注解标识的类的外部类的名称
            name = "default." + metadata.getEnclosingClassName();
        }
        else {
            // default. + EnableFeignClients注解标识的类的名称
            name = "default." + metadata.getClassName();
        }
        // 注解feign的全局配置类, 注册全局配置FeignClientSpecification类, 构造器的三个参数为{name}, default, defaultAttrs.get("defaultConfiguration")
        registerClientConfiguration(registry, name, "default", defaultAttrs.get("defaultConfiguration"));
    }
}

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object className,
			Object configuration) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
    builder.addConstructorArgValue(name);
    builder.addConstructorArgValue(className);
    builder.addConstructorArgValue(configuration);
    registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
            builder.getBeanDefinition());
}

这里就将@EnableFeignClients注解中defaultConfiguration属性对应的值, 构建成了一个FeignClientSpecification注册到了容器中, 其中指定的name(容器名称)是以default.开头的

FeignClientSpecification

public class FeignClientSpecification implements NamedContextFactory.Specification {
    /**
     * 对应容器的名称
     */
    private String name;
	
    /**
     * 注入到子容器的对象的名称
     */
	private String className;

    /**
     * 注入到子容器中的对象
     */
	private Class<?>[] configuration;
}

这个类中nameconfiguration比较重要,记录了要注入的目标容器名称name以及注入的实际对象configuration

registerFeignClients

// ... 省略部分代码
for (BeanDefinition candidateComponent : candidateComponents) {
    // 注解定义的BeanDefinition
    if (candidateComponent instanceof AnnotatedBeanDefinition beanDefinition) {
        // verify annotated class is an interface
        AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
        // 只要接口
        Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");

        // @FeignClient注解的属性们
        Map<String, Object> attributes = annotationMetadata
                .getAnnotationAttributes(FeignClient.class.getCanonicalName());

        // 子容器名称, 获取当前feign接口对应要生成的FeignClientSpecification的名称的前缀, 它必须唯一
        // 顺序: contextId>value>name>serviceId
        String name = getClientName(attributes);

        // feign接口的名称, 同时也是feign接口注册在spring中的名称
        String className = annotationMetadata.getClassName();

        // 注册当前feign接口的配置类(FeignClientSpecification的BeanDefinition)到spring中
        registerClientConfiguration(registry, name, className, attributes.get("configuration"));

        // 注册feign接口对应的FeignClientFactoryBean的BeanDefinition到spring中
        registerFeignClient(registry, annotationMetadata, attributes);
    }
}

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object className,
			Object configuration) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
    builder.addConstructorArgValue(name);
    builder.addConstructorArgValue(className);
    builder.addConstructorArgValue(configuration);
    registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
            builder.getBeanDefinition());
}

其中registerClientConfiguration(registry, name, className, attributes.get("configuration"));将@FeignClient注解的configuration属性对应的值构建成FeignClientSpecification注册到spring容器中。

其中名称name的取值顺序为contextId>value>name>serviceId, 所以@FeignClient注解中contextId实际就是子容器的名称

到此, 默认的对于所有子容器应该注入的对象(@EnableFeignClients注解中defaultConfiguration属性对应的值), 以及当个子容器应该注入的对象(@FeignClient注解的configuration属性对应的值)已经全部注入到spring上下文了

子容器对象注入到子容器中

@Configuration(proxyBeanMethods = false)
public class FeignAutoConfiguration {
    
    @Autowired(required = false)
	private List<FeignClientSpecification> configurations = new ArrayList<>();
    
    @Bean
	public FeignClientFactory feignContext() {
		FeignClientFactory context = new FeignClientFactory();
		context.setConfigurations(this.configurations);
		return context;
	}
}

FeignAutoConfiguration类中, 定义了一个方法类型的工厂bean(feignContext方法), 创建了一个FeignClientFactory对象, 并将容器中的FeignClientSpecification对象添加到了FeignClientFactory中; 从这里也可以看出, 我们是可以自定义FeignClientSpecification对象的, 它会在这里直接添加到FeignClientFactory中。

其中关于setConfigurations的作用, 回看一下上面介绍的NamedContextFactory

FeignClientFactory

public class FeignClientFactory extends NamedContextFactory<FeignClientSpecification> {
    
	/**
	 * 从子容器中获取指定类型的单个bean, 多 Bean 时返回null
	 */
	@Nullable
	public <T> T getInstanceWithoutAncestors(String name, Class<T> type) {
		try {
			// getContext方法会把EnableFeignClients#defaultConfiguration指定的接口和默认配置添加到feign接口的上下文
			return BeanFactoryUtils.beanOfType(getContext(name), type);
		}
		catch (BeansException ex) {
            // 多 Bean 时抛异常
			return null;
		}
	}

	/**
	 * 从子容器中获取指定类型的多个bean
	 */
	@Nullable
	public <T> Map<String, T> getInstancesWithoutAncestors(String name, Class<T> type) {
		return getContext(name).getBeansOfType(type);
	}

	/**
	 * 从父子容器中获取指定名字和类型的bean
	 */
	public <T> T getInstance(String contextName, String beanName, Class<T> type) {
		return getContext(contextName).getBean(beanName, type);
	}
}

FeignClientFactory继承了NamedContextFactory抽象类

  1. 它有两个构造器, 在super中给NamedContextFactory设置了defaultConfigType字段, 它将会注入到所有子容器中; 容器名称也会注入到子容器中, 其中key为spring.cloud.openfeign.client.name

    关于这个FeignClientsConfiguration类, 我们这里先不做解释, 先有个印象即可

super(FeignClientsConfiguration.class, "spring.cloud.openfeign", "spring.cloud.openfeign.client.name",
       applicationContextInitializers);
  1. 并新增了三个方法
  • getInstanceWithoutAncestors: 从子容器中获取指定类型的单个bean
  • getInstancesWithoutAncestors: 从子容器中获取指定类型的多个bean
  • getInstance: 从父子容器中获取指定名字和类型的bean

现在我们再回过头来看一下FeignClientFactoryBean中对父子容器的使用

FeignClientFactoryBean

public class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
    /**
	 * 使用容器中配置的组件和属性配置填充Feign.Builder
	 */
	<T> T getTarget() {
		// feign上下文的工具类
		FeignClientFactory feignClientFactory = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class)
				: applicationContext.getBean(FeignClientFactory.class);
        // ... 省略代码
    }
    
    protected Feign.Builder feign(FeignClientFactory context) {
		// 从contextId对应的上下文中获取FeignLoggerFactory对象
		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
		// 创建logger
		Logger logger = loggerFactory.create(type);

		// @formatter:off
		Feign.Builder builder = get(context, Feign.Builder.class)
				// required values
				.logger(logger)
				.encoder(get(context, Encoder.class))
				.decoder(get(context, Decoder.class))
				.contract(get(context, Contract.class));
		// @formatter:on
		// 添加配置
		configureFeign(context, builder);

		return builder;
	}
    
    /**
	 * 从容器中获取一个bean, 没有找到就反射创建一个
	 */
	private <T> T getOrInstantiate(Class<T> tClass) {
		try {
			// 容器中获取
			return beanFactory != null ? beanFactory.getBean(tClass) : applicationContext.getBean(tClass);
		}
		catch (NoSuchBeanDefinitionException e) {
			// 反射创建一个
			return BeanUtils.instantiateClass(tClass);
		}
	}

	/**
	 * 从父子容器中获取一个bean, 没有找到会抛异常
	 */
	protected <T> T get(FeignClientFactory context, Class<T> type) {
		// 从contextId对应的上下文中或者spring上下文获取type对象(从父子容器中获取对象)
		T instance = context.getInstance(contextId, type);
		if (instance == null) {
			throw new IllegalStateException("No bean found of type " + type + " for " + contextId);
		}
		return instance;
	}

	/**
	 * 从父子容器中获取一个bean, 没有找到不会抛异常; 1.先当前上下文中获取 2.没有再从父容器中获取
	 */
	protected <T> T getOptional(FeignClientFactory context, Class<T> type) {
		return context.getInstance(contextId, type);
	}

	/**
	 * 从父子容器或者当前容器中获取一个bean, 没有找到不会抛异常; 1.先当前上下文中获取 2.没有再从父容器中获取
	 */
	protected <T> T getInheritedAwareOptional(FeignClientFactory context, Class<T> type) {
		if (inheritParentContext) {
			// 从当前及父容器中获取一个满足条件的bean; 1.先当前上下文中获取 2.没有再从父容器中获取
			return getOptional(context, type);
		}
		else {
			// 从当前容器中获取一个满足条件的bean
			return context.getInstanceWithoutAncestors(contextId, type);
		}
	}

	protected <T> Map<String, T> getInheritedAwareInstances(FeignClientFactory context, Class<T> type) {
		if (inheritParentContext) {
			// 从当前以及父容器中获取所有type类型的bean; 先从当前容器中拿, 再从父容器中拿, 以此类推, 直到拿到所有的bean
			return context.getInstances(contextId, type);
		}
		else {
			// 从当前容器中拿的满足条件的bean们
			return context.getInstancesWithoutAncestors(contextId, type);
		}
	}
}

FeignClientFactoryBean代表了一个feign接口客户端对象, 它在构建Feign.Builder的时候, 调用了get,getOptional,getInheritedAwareOptional,getInheritedAwareInstances等方法。

这些方法都有个共同点, 就是使用FeignClientFactory对象, 调用它的相关api从父子容器中获取指定类型的对象。

总结

  1. spring的容器基对象ApplicationContext默认支持父子容器结构, 通过getParent方法获取父容器对象。
  • 在它的一级抽象实现类AbstractApplicationContext中定义了父容器ApplicationContext parent对象。
  • 在它的一级非抽象子类GenericApplicationContext中, 定义了DefaultListableBeanFactory beanFactory对象, beanFactory是applicationContent的一个属性, 并且它俩是包装模式的关系而存在
  1. springcloud提供的NamedContextFactory类, 定义了对容器的操作方法
  • 创建-createContext), buildContext
  • 获取-getContext)
  • 初始化子容器(registerBeans或applicationContextInitializers.get(name).initialize(context))
  • 获取容器中的对象-getInstance、getInstances等
  1. @EnableFeignClients注解使用@Import引入了FeignClientsRegistrar
  • 根据@EnableFeignClients.defaultConfiguration构建名称为default.开头的FeignClientSpecification注入到spring容器中, 它会注册到所有feign的子容器中
  • 根据@FeignClient.configuration构建名称为contextId(子容器名称)的FeignClientSpecification注入到spring容器中
  1. FeignAutoConfiguration类中会创建FeignClientFactory对象, 它是springcloud_feign对NamedContextFactory的一个实现, 它将2,3两步构建的FeignClientSpecification注入到FeignClientFactory中; 同时我们也可以自定义FeignClientSpecification对象的bean放进去使用
  2. FeignClientFactoryBean中, 通过@FeignClient.contextId字段从FeignClientFactory获取指定容器, 获取配置的目标对象(FeignClientSpecification配置的)构建Feign.Builder
  3. 每个子容器共享一个父容器中的对象, 子容器中各个独有组件互相隔离

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

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

相关文章

写入hive metastore报问题Permission denied: user=hadoop,inode=“/user/hive”

背景 使用Doris创建hive catalog后&#xff0c;想在hive上的库中创建一个表&#xff0c;报如下图片错误 解决办法 hdfs dfs -ls /看到如下图片所示&#xff0c;只有root用户有写的权限 所以通过export HADOOP_USER_NAMEroot将hadoop的用户名改成root&#xff0c;然后再hdfs…

【AI图像生成网站Golang】项目测试与优化

AI图像生成网站 目录 一、项目介绍 二、雪花算法 三、JWT认证与令牌桶算法 四、项目架构 五、图床上传与图像生成API搭建 六、项目测试与优化 六、项目测试与优化 在开发过程中&#xff0c;性能优化是保证项目可扩展性和用户体验的关键步骤。本文将详细介绍我如何使用一…

单节点calico性能优化

在单节点上部署calicov3273后&#xff0c;发现资源占用 修改calico以下配置是资源消耗降低 1、因为是单节点&#xff0c;没有跨节点pod网段组网需要&#xff0c;禁用overlay方式网络(ipip&#xff0c;vxlan),使用route方式网络 配置calico-node的环境变量 CALICO_IPV4POOL_I…

Redis 中 IntSet 底层数据结构

IntSet 底层数据结构 序言: 像字符串 SDS 只是保存了一个变量的值&#xff0c;但是像 Redis 中也是需要保存一些集合元素的&#xff0c;这里就介绍一下其中一种集合 IntSet&#xff0c;由于是 Set 所以也有 Set 的一些特性&#xff0c;不过也多加了一些特性&#xff1a; ● 唯…

Java——网络编程(下)

(UDP通讯的实现) 1 UDP通信介绍 (面向无链接的一个传输协议——>不会创建连接——>效率高) (发送数据要经行封包操作——>使用DatagramPacket类——>底层是UDP) (DatagramPacket——>数据封包——>发送数据和接收数据都要去包装对象&#xff01;&#xf…

《Django 5 By Example》读后感

一、 为什么选择这本书&#xff1f; 本人的工作方向为Python Web方向&#xff0c;想了解下今年该方向有哪些新书出版&#xff0c;遂上packt出版社网站上看了看&#xff0c;发现这本书出版时间比较新(2024年9月)&#xff0c;那就它了。 从2024年11月11日至2024年12月18日期间&…

基于Spring Boot的校园商城系统

一、系统背景与意义 随着互联网技术的快速发展&#xff0c;电子商务已经渗透到生活的方方面面。校园作为一个相对封闭但活跃的社群&#xff0c;同样需要一个专门的线上平台来满足其特殊的需求。基于Spring Boot的校园商城系统正是为此目的而设计&#xff0c;它结合了微服务架构…

感知机收敛性定理证明

1. 问题描述 感知机收敛性定理假设&#xff1a; 存在一个参数向量 θ&#xff08;被归一化为单位向量&#xff0c;&#xff0c;以及一个正数 &#xff0c;使得对所有训练样本 满足&#xff1a; 这是线性可分的假设&#xff0c;意味着每个样本点与正确超平面之间有一个至少为的…

ai绘图丨中国新年春节背景第二弹(附关键词

使用工具&#xff1a;千鹿AI 咒语&#xff1a;圆形平面讲台&#xff0c;5 个礼品盒和台灯交错排列&#xff0c;红色背景上的圆形&#xff0c;中国唐朝风格&#xff0c;红色和金色主题&#xff0c;3D 效果图&#xff0c;摄影棚灯光&#xff0c;简约产品展示模型&#xff0c;逼真…

深度学习每周学习总结J9(Inception V3 算法实战与解析 - 天气识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结Inception V1 简介Inception V3 简介1. 设置GPU2. 导入数据及处理部分3. 划分数据集4. 模型构建部分5. 设置超参数&#xff1…

Restaurants WebAPI(一)—— clean architecture

文章目录 项目地址一、Restaurants.Domain 核心业务层1.1 Entities实体层1.2 Repositories 数据操作EF的接口二、Restaurants.Infrastructure 基础设施层2.1 Persistence 数据EF CORE配置2.2 Repositories 数据查询实现2.3 Extensions 服务注册三、Restaurants.Application用例…

道路运输企业安全生产管理人员安全考核试题

道路运输企业安全生产管理人员安全考核试题 一、单选题 题干&#xff1a;在公交车行驶过程中&#xff0c;乘客王某因与驾驶员发生矛盾&#xff0c;遂殴打驾驶员并抢夺方向盘&#xff0c;造成其他乘客受轻微伤&#xff0c;依照《中华人民共和国刑法》的规定&#xff0c;王某触…

FFmpeg库之ffplay

文章目录 FFmpeg环境搭建ffplay使用通用选项视频选项音频选项快捷键使用滤镜直播拉流 FFmpeg环境搭建 FFmpeg官网 FFmpeg环境搭建 ./configure \--prefix"$HOME/ffmpeg" \--extra-cflags"-I$HOME/ffmpeg/include" \--extra-ldflags"-L$HOME/ffmpeg…

HTTP 协议报文结构 | 返回状态码详解

注&#xff1a;本文为 “HTTP 历史 | 协议报文结构 | 返回状态码” 相关文章合辑。 未整理去重。 HTTP 历史 wangjunliang 最后更新: 2024/3/16 上午10:29 超文本传输协议(英语:HyperTextTransferProtocol,缩写:HTTP)是 万维网(World Wide Web)的基础协议&#xff61;自 蒂姆…

springboot444新冠物资管理系统的设计与实现(论文+源码)_kaic

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装新冠物资管理系统软件来发挥其高效地信息处理的作用&#x…

【数字信号处理】数字信号处理试题及答案,离散序列,Z变换,傅里叶变换

关注作者了解更多 我的其他CSDN专栏 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数字信号处理 光电融合集成电路…

【系统】Mac crontab 无法退出编辑模式问题

【系统】Mac crontab 无法退出编辑模式问题 背景一、问题回答1.定位原因&#xff1a;2.确认编辑器类型3.确保编辑器进入正确3.1 确认是否有crontab调度任务3.2 进入编辑器并确保编辑器正常3.3 保存操作 4.确认crontab任务存在5.确保脚本的可执行性和正确性 二、后续 背景 之前…

6.3.1 MR实战:计算总分与平均分

在本次实战中&#xff0c;我们的目标是利用Apache Hadoop的MapReduce框架来处理和分析学生成绩数据。具体来说&#xff0c;我们将计算一个包含五名学生五门科目成绩的数据集的总分和平均分。这个过程包括在云主机上准备数据&#xff0c;将成绩数据存储为文本文件&#xff0c;并…

开发平台接口规范:北斗终端->客户平台(上行)| 时空信息产品

文章目录 引言I 技术架构和业务流程II 渠道接口验证签名白名单IP渠道配置表设计III 其他辅助功能TCP 发送消息到消息中心nginx转发网关服务异常捕获日志采集IV 知识扩展对请求参数进行校验引言 开发平台的应用场景:平台需要开发能力给下游平台需要接收上游的回调数据,例如接收…

MySQL知识汇总(一)

一些命令行操作注意加 分号 “ ; ” show databases 查看所有数据库 use 数据库名 切换数据库 show tables 查看数据库中所有表 describe 表名 显示表中所有信息 create database [if not exists] 新库名 创…