0%

说明

在 springboot 中一个接口有多个实现,我们希望通过配置来控制运行时实例化哪个对象,springboot 中 @Conditional 注解可以帮助我们细粒度控制 bean 的实例化。

Spring Bootorg.springframework.boot.autoconfigure.condition 包下定义了以下注解:

注解名 作用
@ConditionalOnJava 基于JVM版本作为判断条件.
@ConditionalOnBean 当容器中有指定的Bean的条件下.
@ConditionalOnClass 当类路径下游指定的类的条件下.
@ConditionalOnExpression 基于SpEL表达式作为判断条件.
@ConditionalOnJndi 在JNDI存在的条件下查找指定的位置.
@ConditionalOnMissingBean 当容器中没有指定Bean的情况下.
@ConditionalOnMissingClass 当类路径下没有指定的类的情况下.
@ConditionalOnNotWebApplication 当前项目不是web项目的条件下.
@ConditionalOnProperty 指定的属性是否有指定的值.
@ConditionalOnResource 类路径是否有指定的值.
@ConditionalOnSingleCandidate 当指定Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean.
@ConditionalOnWebApplication 当前项目是web项目的条件下.

@ConditionalOnExpression

@Component
// user.label 属性等于 user2 创建TestBean
// @ConditionalOnExpression("'${user.label}'.equals('user2')")

// 可以使用 && || 等运算符
// @ConditionalOnExpression("'${user.label}'.equals('user3') && '${xxx}'.equals('xx')")
// @ConditionalOnExpression("'${user.label}'.equals('${xxx}')")

// 当环境变量 user.label=user2 创建TestBean, 注意: 这个环境变量可以通过 application.yml 传入
@ConditionalOnExpression("#{'user2'.equals(environment['user.label'])}")
public class TestBean {
    @PostConstruct
    public void init() {
        System.out.println(111);
    }
}

@ConditionalOnProperty

@Component
// 当有 user.label 这个属性时创建 TestBean
// @ConditionalOnProperty(name = "user.label")
// 当有 user.label 和 xxx 两个属性时创建 TestBean
// @ConditionalOnProperty(name = {"user.label", "xxx"})
// 当有 user.label 这个属性且值为 user2 时创建 TestBean
@ConditionalOnProperty(name = "user.label", havingValue = "user2")
public class TestBean {
    @PostConstruct
    public void init(){
        System.out.println(111);
    }
}

自定义 Conditional

Bean对象

  • 两个 User Bean
@Data
@NoArgsConstructor
public class User1 {
    private String name;
    private int age;
}

@Data
@NoArgsConstructor
public class User2 {
    private String name;
    private int age;
}

方法一

  • 为每类对象创建一个 Conditional,如下为 User1和User2分别创建一个
public class MyConditional1 implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String v = context.getEnvironment().getProperty("user.label");
        if (v.equals("user1")) return true;
        return false;
    }
}

public class MyConditional2 implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String v = context.getEnvironment().getProperty("user.label");
        if (v.equals("user2")) return true;
        return false;
    }
}
  • springboot 实例化 bean
@Bean
@Conditional(MyConditional1.class)
public User init(){
    System.out.println(111);
    return new User();
}

@Bean
@Conditional(MyConditional2.class)
public User2 init2(){
    System.out.println(222);
    return new User2();
}
  • application.propeties
# 如果 label 匹配 bean 的label,该 bean 就会被实例化
user.label=user1

方法二

  • 新增注解给要实例化的对象打上标签
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(MyConditional.class)
public @interface MyConditionalAnnotation {
    String label();
}
  • MyConditional.java
public class MyConditional extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(MyConditionalAnnotation.class.getName());
        // 获取注解所传入的 label
        Object value = annotationAttributes.get("label");
        if (value == null) {
//            return new ConditionOutcome(false, "ERROR");
            return ConditionOutcome.noMatch("ERROR");
        }
        // user.label 的值是通过application.propeties传入
        String v = context.getEnvironment().getProperty("user.label");
        if (v.equals(value)){
            // 如果匹配就实例化该 bean
            return ConditionOutcome.match("OK");
        }
        return ConditionOutcome.noMatch("ERROR");
    }
}

注意:代码中通过 application.propeties 传入配置优先级比较高,所以通过context可以获取到,如果通过别的配置文件可能无法获取则需要手动加载。

Properties properties = new Properties();
try {
    properties.load(conditionContext.getResourceLoader().getResource("test.properties").getInputStream());
} catch (IOException ex) {
    ex.printStackTrace();
}
String v = properties.getProperty("user.label");
  • springboot 创建bean
// 通过自定义注解设置 label
@MyConditionalAnnotation(label = "MyUserLabel1")
@Bean
public User1 init(){
    System.out.println(111);
    return new User1();
}

@MyConditionalAnnotation(label = "MyUserLabel2")
@Bean
public User2 init2(){
    System.out.println(222);
    return new User2();
}
  • application.propeties
# 如果 label 匹配 bean 的label,该 bean 就会被实例化
user.label=MyUserLabel2

说明

EphemeralContainers:官方说明

kubectl 版本为 v1.18 或者更高 beta 功能 默认不开启,需要手动开启, v1.22 版本开始成为正式功能无需开启.

如果执行kubectl debug xxxx 报以下错误说明需要手动开启临时容器功能:

error: ephemeral containers are disabled for this cluster (error from server: "the server could not find the requested resource").

手动开启操作

master节点

  • 修改apiserver
    编辑/etc/kubernetes/manifests/kube-apiserver.yaml

--feature-gates=xxxxx后面添加,EphemeralContainers=true,注意逗号.

  • 修改controller-manager
    编辑/etc/kubernetes/manifests/kube-controller-manager.yaml

--feature-gates=xxxxx后面添加,EphemeralContainers=true,注意逗号.

  • 修改kube-scheduler
    编辑/etc/kubernetes/manifests/kube-scheduler.yaml

--feature-gates=xxxxx后面添加,EphemeralContainers=true,注意逗号.

所有节点

  • 修改kubelet
    编辑/var/lib/kubelet/kubeadm-flags.env

添加--feature-gates=EphemeralContainers=true

修改后如下

KUBELET_KUBEADM_ARGS="--cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2 --feature-gates=EphemeralContainers=true"
  • 重启kubelet
systemctl daemon-reload
systemctl restart kubelet 

储能系统

储能系统(Energy Storage System,简称ESS):是一个可完成存储电能和供电的系统,具有平滑过渡、削峰填谷、调频调压等功能。可以使太阳能、风能发电平滑输出,减少其随机性、间歇性、波动性给电网和用户带来的冲击;通过谷价时段充电,峰价时段放电可以减少用户的电费支出;在大电网断电时,能够孤岛运行,确保对用户不间断供电。

名词 说明
BA 储能电池组
BMS 电池管理系统
PCS 储能变流器,功率变换系统
EMS 能量管理系统

变流器控制策略

  • PQ控制:指恒定有功无功控制,控制储能变流器输出的有功功率和无功功率等于其参考功率。PQ控制是新能源并网最常用的控制方式,只有在储能系统并网模式下才可能运用
  • VF控制:指储能变流器维持输出电压和频率不变;而输出的有功功率和无功功率由负荷决定
  • VSG控制:虚拟同步机模式,该模式下储能可以构网,同时可以和并网型或者构网型系统并机运行

名词

  • 使能:所谓使能信号,英文叫Enable,通过这个信号可以控制某颗芯片/某个电路有没有被“启用”。一个大型的数字系统中,通常都需要使用多个芯片/电路单元来配合完成一个功能。而这些芯片并不是每时每刻都在工作的。因此需要用使能信号来控制应该“启动”哪个芯片来工作。

术语解释

术语 英文 说明
AC Alternating Current 交流电压
DC Direct Current 直流电压
整流器 Rectifier 将交流电转变成直流电的电子设备
逆变器 inverter 把直流电能(电池、蓄电瓶)转变成交流电(一般为220V,50Hz正弦波)
继电器 Relay 通常应用于自动控制电路中,用较小的电流去控制较大电流的一种“自动开关”。在电路中起着自动调节、安全保护、转换电路等作用
变压器 transformer 变换(升高或降低)交流电压,但不改变电源频率的电能转换器
台区 - 指(一台)变压器的供电范围或区域

Metrics Queries

Metrics Queries 可以用来计算日志错误率或最近一段时间日志最多的前 N 个日志源.

LogQL 范围聚合查询函数

函数 说明
rate(log-range) 计​​算每秒的条目数
count_over_time(log-range) 计算给定范围内每个日志流的条目
bytes_rate(log-range) 计算每个流每秒的字节数
bytes_over_time(log-range) 计算给定范围内每个日志流使用的字节数
absent_over_time(log-range) 如果传递给它的范围向量有任何元素,则返回一个空向量;如果传递给它的范围向量没有元素,则返回一个值为 1 的 1 元素向量。(absent_over_time对于在一段时间内不存在标签组合的时间序列和日志流时发出警报很有用。)

示例

# 计算 MySQL 作业最后五分钟内的所有日志行。
count_over_time({job="mysql"}[5m])
# MySQL 作业在每台主机的最后几分钟内所有非超时错误的每秒速率,并且仅包括持续时间超过 10 秒的错误
sum by (host) (rate({job="mysql"} |= "error" != "timeout" | json | duration > 10s [1m]))

内置聚合运算符

函数 说明
sum 计​​算标签的总和
avg 计算标签的平均值
min 在标签上选择最小值
max 选择最大标签
stddev 计算标签的总体标准差
stdvar 计算标签的总体标准方差
count 计​​算向量中的元素个数
topk 通过样本值选择最大的 k 个元素
bottomk 通过样本值选择最小的 k 个元素

示例

# 获取日志吞吐量最高的前 10 个应用程序
topk(10,sum(rate({region="us-east1"}[5m])) by (name))

# 获取 k8s ns1 命名空间下日志输出量前 10 的 Pod
topk(10,sum(rate({namespace="ns1"}[5m])) by (pod))

# 获取指定作业的最后五分钟的日志行数,按级别分组
sum(count_over_time({job="mysql"}[5m])) by (level)

# /home按区域获取对 NGINX 日志端点的 HTTP GET 请求速率
avg(rate(({job="nginx"} |= "GET" | json | path="/home")[10s])) by (region)

参考链接

查看crt证书有效期

openssl x509 -in cert.crt -noout -text

重签服务端证书

./easyrsa build-server-full server_name nopass

然后用新签的证书(./pki/issue/./pki/private/ 目录下)替换在用的 server.crtserver.key.

重签客户端证书

./easyrsa build-client-full client_name nopass

修改证书有效期时间

# 修改 easy-rsa 目录下 vars 模板文件参数
# /usr/share/doc/easy-rsa-3.0.8/vars.example 默认所有参数, 注意修改版本号
set_var EASYRSA_CERT_EXPIRE   3650