精华内容
下载资源
问答
  • 这种方式虽然能解决持久化的操作,但是不能自动同步更新,也就是持久化配置写成什么样就是什么样,在程序启动后,无法动态更新到持久化存储中!当然能做到持久化Nacos或者本地JSON已经很不错了!如果作用做到这一步...

    前言

    在我们使用Sentinel做熔断限流等操作时,一些设置好的配置默认是存放在当前服的内存中的,那么也就意味着每次重启服务,这些配置好的配置就会消失。在我们搭建微服务架构做测试的时候不是很友好。大家都知道Sentinel提供了一些持久化方式,如持久化到Nacos、本地JSON文件。这种方式虽然能解决持久化的操作,但是不能自动同步更新,也就是持久化配置写成什么样就是什么样,在程序启动后,无法动态更新到持久化存储中!当然能做到持久化Nacos或者本地JSON已经很不错了!如果作用做到这一步那么请见Sentinel整合Nacos做持久化这篇文章就够了,如果想要更加灵活,想在运行过程中动态同步Nacos那么就请往下看!

    Sentinel动态推拉数据持久化同步到Nacos

    这个操作字面意思看似只要同步Nacos这一套逻辑,其实并不是,同步Nacos其实分为普通服务同步Nacos和Gateway同步Nacos。因为二者无论是在Sentinel Datasource(Sentinel服务端) 还是我们自己的业务服(包括网关和其他服)通知拉取推送机制都是不一样的,所以针对这种情况需要做两手逻辑操作!

    在写动态拉取数据持久化Nacos前,先保证普通服务整合Sentinel和Gateway整合Sentinel默认流程先能跑通!

    Sentinel动态推拉数据持久化同步到Nacos(通用部分)

    1.下载Sentinel源码
    Sentinel-GitHub
    在这里插入图片描述
    我这里以1.7.1版本为基准,说到版本,这里我要多提一嘴,微服务架构搭建最恶心的我觉得倒不是代码有多难写,往往是代码没什么问题,总是各种版本对不上,这就比较头痛,不过我这里提供一套方案,这套版本适配的方案是比较准的,能帮我们确定一个版本的请款下适配Spring Boot、Spring Cloud、Spring Cloud Alibaba之间的版本版本说明,我整套微服务架构的版本使用的是Spring Boot 2.3.2.RELEASE、Spring Cloud Alibaba 2.2.5.RELEASE、Spring Cloud Hoxton.SR8

    在这里插入图片描述
    不扯远了,收回来接着本文继续搞重点!
    在这里插入图片描述
    这就是我们要二开的源码,idea打开一下
    在这里插入图片描述
    2.修改pom.xml
    修改pom.xml中的sentinel-datasource-nacos的依赖,将test注释掉,这样才能在主程序中使用。

    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
        <!--<scope>test</scope>-->
    </dependency>
    

    在这里插入图片描述
    3.创建nacossync目录用来编写我们二开的代码
    在这里插入图片描述
    图中nacossync中这些类就是我们要编写的代码!

    4.Naocs数据同步Properties映射类
    这个类无论是Gateway二开同步还是普通服务二开都是公用的

    
    /**
     * @author TAO
     * @description: Naocs数据同步Properties映射类
     * @date 2021/5/1 22:36
     */
    @Component
    @ConfigurationProperties(prefix = "nacos.server")
    public class NacosConfigProperties {
    
        private String ip;
    
        private String port;
    
        private String namespace;
    
        private String groupId;
    
        public String getIp() {
            return ip;
        }
    
        public void setIp(String ip) {
            this.ip = ip;
        }
    
        public String getPort() {
            return port;
        }
    
        public void setPort(String port) {
            this.port = port;
        }
    
        public String getNamespace() {
            return namespace;
        }
    
        public void setNamespace(String namespace) {
            this.namespace = namespace;
        }
    
        public String getGroupId() {
            return groupId;
        }
    
        public void setGroupId(String groupId) {
            this.groupId = groupId;
        }
    
        public String getServerAddr() {
            return this.getIp() + ":" + this.getPort();
        }
    
        @Override
        public String toString() {
            return "NacosConfigProperties [ip=" + ip + ", port=" + port + ", namespace="
                    + namespace + ", groupId=" + groupId + "]";
        }
    
    }
    
    

    5.Nacos同步常量类
    这个类可有可无,就看要不要这么规范

    /**
     * @author TAO
     * @description: Nacos同步常量类
     * @date 2021/5/6 23:19
     */
    public final class NacosConfigConstant {
        public static final String FLOW_DATA_ID_POSTFIX = "-sentinel-flow";//统一sentinel持久化Nacos的文件后缀
    }
    
    

    6.Nacos同步配置类
    这个类的话先写一个空类,后面写到具体的实现时我们在加对应代码!

    /**
     * @author TAO
     * @description: Nacos同步配置类
     * @date 2021/5/6 23:19
     */
    @Configuration
    public class NacosConfig {
    
        @Autowired
        private NacosConfigProperties nacosConfigProperties;
    
        /**
         *  TODO 非常关键 这里将FlowRuleEntity转换成FlowRule才会对客户端生效,一下几对是不同场景的版解码器,
         *  TODO 实际上就是将Nacos中的JSON解析为对应的实体类,将对应的实体类解析为JSON存储的
         * @return FlowRule
         */
    
       	//TODO 后续这里会加对应的Bean代码 注意!
    
        @Bean
        public ConfigService nacosConfigService() throws Exception {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, nacosConfigProperties.getServerAddr());
            properties.put(PropertyKeyConst.NAMESPACE, nacosConfigProperties.getNamespace());
            return ConfigFactory.createConfigService(properties);
        }
    }
    
    

    7.application.properties配置文件,添加Nacos配置
    注意namespace这个Nacos命名空间这个如果使用publish可以不写,如果是其他自建的namespace那么除了写namespace之外还要加其他的标识信息,这个我没深入研究

    nacos.server.ip=nacos-ip
    nacos.server.port=8848
    nacos.server.namespace=
    nacos.server.group-id=DEFAULT_GROUP
    

    8.我们自己的业务服添加一下依赖

    		<!--sentinel-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            </dependency>
    
            <!--sentinel-nacos持久化-->
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
            </dependency>
    
    

    9.我们自己的业务服添加一下配置
    这段配置其实在做持久化Nacos时应该都写上了,这里还是提一嘴!

    spring:
      application:
        name: yy-auth
      cloud:
        nacos:
          discovery:
            server-addr: nacos:8848
        sentinel:
          transport:
            dashboard: sentinel:9999
            client-ip: 192.168.1.109 # 防止sentinel抓取虚拟ip
            port: 8719
          datasource: #注意虽然sentinel配置了同步nacos拓展,并且数据能在控制台上同步显示,但是那些数据只是控制台读取的,并不是我们自己真实的服务读取的,所以下面配置还要写
            ds1:
              nacos:
                server-addr: nacos:8848
                #namespace: sentinel
                dataId: yy-auth-sentinel-flow
                groupId: DEFAULT_GROUP
                data-type: json
                rule-type: flow
    

    走到这一步那么普通服务和Gateway的通用部分就编写完成,下面会开始编写二者不同的代码!

    Sentinel动态推拉数据持久化同步到Nacos(普通服务)

    1.编写拉取Nacos配置类

    /**
     * @author TAO
     * @description: 拉取Nacos配置
     * @date 2021/5/6 23:18
     */
    @Component("flowRuleNacosProvider")
    public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
    
        private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosProvider.class);
    
        @Autowired
        private NacosConfigProperties nacosConfigProperties;
    
        @Autowired
        private ConfigService configService;
    
        @Autowired
        private Converter<String, List<FlowRuleEntity>> converter;
    
        @Override
        public List<FlowRuleEntity> getRules(String appName) throws Exception {
            logger.info("当前从Nacos中拉取的限流规则配置文件名:{}", appName + NacosConfigConstant.FLOW_DATA_ID_POSTFIX);
            String rules = configService.getConfig(appName + NacosConfigConstant.FLOW_DATA_ID_POSTFIX, nacosConfigProperties.getGroupId(), 3000);
            logger.info("从Nacos中拉取到限流规则信息:{}", rules);
            if (StringUtil.isEmpty(rules)) {
                return new ArrayList<>();
            }
            return converter.convert(rules);
        }
    }
    
    

    2.编写推送配置到Nacos类

    
    /**
     * @author TAO
     * @description: 推送配置到Nacos
     * @date 2021/5/6 23:19
     */
    @Component("flowRuleNacosPublisher")
    public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
    
        private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosPublisher.class);
    
        @Autowired
        private NacosConfigProperties nacosConfigProperties;
    
        @Autowired
        private ConfigService configService;
        @Autowired
        private Converter<List<FlowRuleEntity>, String> converter;
    
        @Override
        public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
            AssertUtil.notEmpty(app, "app name cannot be empty");
            if (rules == null) {
                return;
            }
            logger.info("推送到Nacos配置:{}", rules);
            configService.publishConfig(app + NacosConfigConstant.FLOW_DATA_ID_POSTFIX, nacosConfigProperties.getGroupId(), converter.convert(rules));
        }
    }
    
    

    3.修改流控规则跳转URL
    找到resources/app/scripts/directives/sidebar/sidebar.html中的这段代码:

    <li ui-sref-active="active">
        <a ui-sref="dashboard.flowV1({app: entry.app})">
            <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则
        </a>
    </li>
    

    修改为:

    <li ui-sref-active="active">
        <a ui-sref="dashboard.flow({app: entry.app})">
            <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则
        </a>
    </li>
    

    这样修改之后就会跳转到FlowControllerV2的接口。
    4.在Nacos同步配置类中添加对应Bean

    //普通服务-Sentinel同步Nacos
        @Bean
        public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
            return rules -> JSON.toJSONString(rules.stream().map(FlowRuleEntity::toRule).collect(Collectors.toList()), true);
        }
    
        @Bean
        public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
            return s -> JSON.parseArray(s, FlowRuleEntity.class);
        }
    

    5.将flowRuleDefaultProvider、flowRuleDefaultPublisher替换为我们自己的flowRuleNacosProvider、flowRuleNacosPublisher
    将FlowControllerV2中的默认DynamicRuleProvider和DynamicRulePublisher修改为:

    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
     
    
    private void publishRules(/*@NonNull*/ String app) throws Exception {
        List<FlowRuleEntity> rules = repository.findAllByApp(app);
        rulePublisher.publish(app, rules);
        logger.info("添加限流规则成功{}", JSON.toJSONString(rules.stream().map(FlowRuleEntity::toRule).collect(Collectors.toList()), true));
    }
    

    在这里插入图片描述
    在这里插入图片描述
    重启服务即可!注意,我这里直接修改的是导航栏的跳转URL,所以只有在对应位置才有效
    在这里插入图片描述
    其他入口需要修改才生效。这里我就不过多演示了!

    Sentinel动态推拉数据持久化同步到Nacos(Gateway)

    Gateway做推拉持久化就更加简单了!废话不都说,干就完事了!
    1.编写拉取Nacos-Gateway配置

    /**
     * @author TAO
     * @description: 拉取Nacos-Gateway配置
     * @date 2021/5/6 23:18
     */
    @Component("flowRuleNacosGatewayProvider")
    public class FlowRuleNacosGatewatProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {
    
        private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosGatewatProvider.class);
    
        @Autowired
        private NacosConfigProperties nacosConfigProperties;
    
        @Autowired
        private ConfigService configService;
    
        @Autowired
        private Converter<String, List<GatewayFlowRuleEntity>> converter;
    
        @Override
        public List<GatewayFlowRuleEntity> getRules(String appName) throws Exception {
            logger.info("当前从Nacos中拉取Gateway的限流规则配置文件名:{}", appName + NacosConfigConstant.FLOW_DATA_ID_POSTFIX);
            String rules = configService.getConfig(appName + NacosConfigConstant.FLOW_DATA_ID_POSTFIX, nacosConfigProperties.getGroupId(), 3000);
            logger.info("从Nacos中拉取到Gateway限流规则信息:{}", rules);
            if (StringUtil.isEmpty(rules)) {
                return new ArrayList<>();
            }
            return converter.convert(rules);
        }
    }
    
    

    2.编写推送配置到Nacos-Gateway

    /**
     * @author TAO
     * @description: 推送配置到Nacos-Gateway
     * @date 2021/5/6 23:19
     */
    @Component("flowRuleNacosGatewayPublisher")
    public class FlowRuleNacosGatewayPublisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {
    
        private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosGatewayPublisher.class);
    
        @Autowired
        private NacosConfigProperties nacosConfigProperties;
    
        @Autowired
        private ConfigService configService;
        @Autowired
        private Converter<List<GatewayFlowRuleEntity>, String> converter;
    
        @Override
        public void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {
            AssertUtil.notEmpty(app, "app name cannot be empty");
            if (rules == null) {
                return;
            }
            logger.info("推送到Gateway-Nacos配置:{}", rules);
            configService.publishConfig(app + NacosConfigConstant.FLOW_DATA_ID_POSTFIX, nacosConfigProperties.getGroupId(), converter.convert(rules));
        }
    }
    
    

    3.Nacos同步配置类添加相应Bean

    //Gateway-Sentinel同步Nacos
        @Bean
        public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
            return rules -> JSON.toJSONString(rules);
        }
    
        @Bean
        public Converter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {
            return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);
        }
    

    完整的代码
    在这里插入图片描述
    4.Gateway服务pom添加相应依赖

    	<!--sentinel-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            </dependency>
            <!--sentinel-gateway-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
            </dependency>
    
            <!--sentinel-nacos持久化-->
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
            </dependency>
    

    5.Gateway服务添加配置

    spring:
      application:
        name: yy-gateway
      main:
        allow-bean-definition-overriding: true
      cloud:
        nacos:
          discovery:
            server-addr: nacos:8848
        sentinel:
          transport:
            dashboard: sentinel:9999
            client-ip: 10.0.0.208 # 防止sentinel抓取虚拟ip
            port: 8719
          datasource: #注意虽然sentinel配置了同步nacos拓展,并且数据能在控制台上同步显示,但是那些数据只是控制台读取的,并不是我们自己真实的服务读取的,所以下面配置还要写
            ds1:
              nacos:
                server-addr: nacos:8848
                #namespace: sentinel
                dataId: yy-gateway-sentinel-flow
                groupId: DEFAULT_GROUP
                data-type: json
                rule-type: gw-flow
    

    这里注意一下rule-type和普通服务不一样, gw-flow,重启服务即可!

    展开全文
  • 最近学视频剪辑视频已经在premiere中能够正常播放了,但一导出视频总是比音频快,h.264输出全部阵亡,只有avi还能用但画质离谱且大的一批。费了点劲解决后,还是决定记录下来。 网上搜了下发现也就以下几种观点: ...

    最近学视频剪辑视频已经在premiere中能够正常播放了,但一导出视频总是比音频快,h.264输出全部阵亡,只有avi还能用但画质离谱且大的一批。费了点劲解决后,还是决定记录下来。



    网上搜了下发现也就以下几种观点:

    1. 自己不小心划开了或者没保存(智商正常就不会犯);
    2. 音频速率出问题(比如不是48000HZ);
    3. 帧速率出问题


    音频速率一般不会出现问题,因为大家音频都是直接导入,而且市面上一般主要就是48000HZ。
    所以问题主要就是帧速率的问题。首先我们来看一下什么是帧速率。

    帧速率是指每秒钟刷新的图片的帧数,也可以理解为图形处理器每秒钟能够刷新几次。

    简单来说就是精确度,帧速率越快刷新速度越快,帧速率越快。所以都知道只要该帧速率就会解决问题,然而怎么改呢?很多人直接在输出的时候改,比如这里:

    解决方案

    如果改了就完了那还好,然而更多的是改了以后还是发现不同步,为何?根本原因在于你导入的素材的帧速率不相同。我们剪视频通常会下载很多素材,然而没人去关注他们的帧速率,所以这时就要求我们去关注素材的帧速率。选中素材箱中导入的素材,在源窗口中点元数据即可查看。发现不一致后就需要改了,右键视频素材——>修改——>解释素材——>修改采用此帧速率即可,通常来讲修改成25HZ或30HZ都行,但必须是统一的。如果视频编辑好了再改也没有关系,pr已经贴心地在你改素材的时候同步到了序列。




    这是肯定就有人要问了,素材帧速率改了那视频不是变快就是变慢,我想调速度怎么办?右键序列上的视频段——>速度/持续时间修改百分比即可。




    估计还有人会遇到一个问题,导出的时候看不到帧速率,比如这样:


    问题


    这就是分辨率的问题了,你要是电脑默认分辨率不是100%,第一次使用这里就出不来,我们可以把窗口拉长或者win+i设置——>显示——>将分辨率改为100% 即可。

    展开全文
  • 《事件送网关:让cmdb告别“花瓶”》一文我们将cmdb事件送的参数进行了解析,并结合redis做了去重,避免事件的重复送。接下来的任务我们计划是cmdb和zabbix进行资产同步,功能如下: cmdb的业务、集群、模块...

    在这里插入图片描述

    前言

    事件推送网关:让cmdb告别“花瓶”》一文我们将cmdb事件推送的参数进行了解析,并结合redis做了去重,避免事件的重复推送。接下来的任务我们计划是cmdb和zabbix进行资产同步,功能如下:

    • cmdb的业务、集群、模块分组信息同步至zabbix主机分组;
    • cmdb资产删除同步至zabbix进行相应的主机删除;
    • cmdb资产新建同步至zabbix,并根据相应的信息绑定不同的模板;

    此时cmdb只是为zabbix提供了资产新建、分组、变更等基础数据,zabbix的告警信息如何与其关联,让运维能够通过告警内容就能直接知道哪块业务出现问题才是我们真正想要的。

    为此,我们做了如下定义:

    • 定义zabbix的分组规则为:业务_集群_模块;
    • zabbix agent的Hostname参数统一使用ip地址,方便事件推送网关通过zabbix api根据进行查询及其他相关操作;
    • 定义zabbix 主机的可见名称规则为:集群_模块_ip;由于我们的集群基本都放在同一个业务下,可见名称规则中不加业务所属;

    通过以上规则,我们可以将告警主机的ip、分组、可见名称自由添加到告警动作中,实现了告警信息的可读性。
    在这里插入图片描述
    其中:

    • 集群:中间件;
    • 模块:nginx-lan;
    • IP:10.164.193.189;

    通过告警内容,就可以快速定位问题,后期可结合故障自愈实现,更好的提高我们处理问题的效率。

    目录结构

    开发过程中随着功能的不断增多,我们需要对不同情况生成相应格式化参数。

    D:\WORK\BLUEKING\GATEWAY\GATEWAY
    │  manage.py
    │
    ├─.vscode
    │      settings.json
    │
    ├─gateway
    │      asgi.py
    │      settings.py
    │      urls.py
    │      wsgi.py
    │      __init__.py
    │
    └─gw_cmdb
        │  admin.py
        │  apps.py
        │  models.py
        │  tests.py
        │  urls.py
        │  views.py
        │  __init__.py
        │
        ├─common
        │      hostidentifier_cmdb.py    
        │      main.py
        │      module_cmdb.py           
        │      select_cmdb.py           
        └─zabbix
                group.py
                host.py
                main.py
                template.py
    

    其中:
    common目录是与cmdb相关的模块:

    • main 接收事件推送网关推送过来的参数;
    • hostidentifier_cmdb 针对主机相关事件推送返回格式化参数;
    • module_cmdb 针对模块相关事件推送返回格式化参数;
    • select_cmdb 查询cmdb内容如集群、业务、操作系统等辅助信息;

    zabbix目录是与zabbix相关的模块:

    • main 解析格式化参数,对zabbix做相应的处理;
    • group 与主机分组操作相关的模块,如查询、创建、删除、更新等;
    • host 与主机操作相关模块,如查询、创建、更新、删除等;
    • template 与模板操作相关的模块,如查询等;

    由于分组同步可以在不影响zabbix使用的情况下操作,因此我们在此着重介绍此功能。

    分组同步

    分组同步主要有以下几个功能需求:

    • 业务、集群、模块分组同步,如:创建、更新、删除,对应分组名为:业务_集群_模块;
    • 主机模块更新同步,主机模块转移需要在zabbix中修改主机相应的分组,保持和cmdb一致;

    注意:分组同步的前提是通过zabbix的IP查询并操作主机,因此要求zabbix agent 的Hostname必须为IP,并且和cmdb中的IP一一对应。

    通过分组同步最终实现的效果如下:
    在这里插入图片描述在这里插入图片描述

    1.接收cmdb推送参数

    cmdb事件推送将参数发送给网关,由views.py接收

    vim gw_cmdb/views.py
    from django.http import HttpResponse
    from .common.main import main
    from .zabbix.main import zabbix_main
    from .jumpserver.main import jumpserver_main
    import json
    import logging
    
    logger = logging.getLogger('log')
    
    # Create your views here
    def cmdb_request(request):
        if request.method == 'POST':
            data = json.loads(request.body)
            logger.info('cmdb发送消息:{}'.format(data))
            ## 整理cmdb数据
            res=main(data)
            ##是否需要联动zabbix及jumpserver
            if res['result'] == 1:
                return HttpResponse("ok")
            else:
                logger.info(res)
                #zabbix同步
                zabbix_request = zabbix_main(res)
                logger.info('zabbix 同步完毕:{}'.format(zabbix_request))
                #jumpserver同步
    
                return HttpResponse("ok")
        else:
            logger.info('本接口只支持POST模式')
            return HttpResponse("本接口只支持POST模式")
    

    由于cmdb推送过来的参数内容过多,因此我们需要将其进行格式化并且将其解析成不同的动作,以便zabbix进行操作。

    2.解析参数

    (1)common将views接收的请求进行。

    vim common/main.py
    import logging
    from .hostidentifier_cmdb import hostidentifier
    from .module_cmdb import module_action
    
    logger = logging.getLogger('log')
    
    def main(data):
        result = {'result': 1,'data': 0}
        ## 模块操作
        if data['obj_type'] == 'module':
            return module_action(data)  
        ## 主机标识操作
        elif data['obj_type'] == 'hostidentifier':
            if data['action'] == 'update' and   data['event_type'] == 'relation' :
                logger.info("主机标识更新: {}".format(data))
                return hostidentifier(data)
            else:
                logger.info("主机标识未知操作: {}".format(data))
        else:
            logger.info("未知操作: {}".format(data))
        return result
    

    注意:cmdb侧事件推送的触发动作比较多,如主机、业务、组织架构等,但其实在zabbix侧我们没有必要一一和以上事件进行对应,而是只需要和最小操作保持一致即可。

    举例:

    • 根据分组规则:业务_集群_模块,最小级的模块存在的前提肯定是有集群和业务,因此只要最小级的模块创建、更新、删除时,触发zabbix动作创建主机群组即可,而空业务、空集群对zabbix操作没有实际意义。

    • 对于录入cmdb的主机,无论是在资源池或业务下的空闲资源池,此时都没有必要触发zabbix操作,而是当主机转移到相应的模块下,标识已经分配使用了,此时我们将触发zabbix操作,如创建主机、绑模板、增加主机分组等操作。

    因此zabbix侧动作触发,只需要在模块和主机标识更新情况下触发即可。

    (2)模块操作解析

    vim common/module_cmdb.py
    import json
    import logging
    from .select_cmdb import select_cmdb_biz,select_cmdb_set
    
    logger = logging.getLogger('log')
    
    def module_action(data):
        logger.info("模块更新: {}".format(data))
        ##定义数据格式
        datajson= {'action': '','obj_type': '','data': {'cur_data': {'group': ''},'pre_data': {'group': ''}},'result': 1}
        if data['action'] == "create":
           pass
        elif data['action'] == "update":
            datajson['obj_type']=data['obj_type']
            datajson['action']= data['action']
            groupname_biz_name = select_cmdb_biz(data['data'][0]['cur_data']['bk_biz_id'])
            groupname_set_name = select_cmdb_set(data['data'][0]['cur_data']['bk_biz_id'], data['data'][0]['cur_data']['bk_set_id'])
            groupname_module_new_name = data['data'][0]['cur_data']['bk_module_name']
            groupname_module_old_name = data['data'][0]['pre_data']['bk_module_name']
            logger.info("业务id:{},集群id:{},模块新名:{},模块旧名:{}".format(groupname_biz_name,groupname_set_name,groupname_module_new_name,groupname_module_old_name))
            if groupname_module_new_name !=  groupname_module_old_name:
                datajson['data']['cur_data']['group']= groupname_biz_name+"_"+groupname_set_name+"_"+groupname_module_new_name
                datajson['data']['pre_data']['group']= groupname_biz_name+"_"+groupname_set_name+"_"+groupname_module_old_name
                result = {
                    'result': 0,
                    'data': datajson
                }
                return result
        elif data['action'] == "delete":
            datajson['obj_type']=data['obj_type']
            datajson['action']= data['action']
            groupname_biz_name = select_cmdb_biz(data['data'][0]['pre_data']['bk_biz_id'])
            groupname_set_name = select_cmdb_set(data['data'][0]['pre_data']['bk_biz_id'], data['data'][0]['pre_data']['bk_set_id'])
            groupname_module_name = data['data'][0]['pre_data']['bk_module_name']
            datajson['data']['pre_data']['group']= groupname_biz_name+"_"+groupname_set_name+"_"+groupname_module_name
            result = {
                    'result': 0,
                    'data': datajson
                }
            return result
        else:
            pass
        return datajson
        
    

    (3)主机标识解析

    cmdb主机模块转移可能会触发多次请求,因此我们需要借助redis将请求进行去重。

    
    vim common/hostidentifier_cmdb.py
    import redis
    import json
    import hashlib
    import logging
    
    logger = logging.getLogger('log')
    
    r = redis.StrictRedis(host='127.0.0.1',port=6379,db=1)
    
    ## 模块变更获取主机所有模板定制分组
    def hostidentifier(data):
        ##定义数据格式
        datajson= {'tex_id': '','action': data['action'],'obj_type': data['obj_type'],'data': {'cur_data': {'ip': '','group': []},'bk_host_id':data['data']['cur_data']['bk_host_id'],'pre_data': 'None'},'result': 1}
        ## 获取主机组信息,并清理记录reids去除重复会话
        for i in data['data']:
            datajson['data']['cur_data']['ip'] = i['cur_data']['bk_host_innerip']
            grouplist = i['cur_data']['associations']
            for j in grouplist:
                groupname = grouplist[j]['bk_biz_name']+"_"+grouplist[j]['bk_set_name']+"_"+grouplist[j]['bk_module_name']
                datajson['data']['cur_data']['group'].append(groupname)
                datajson['tex_id']= hashlib.md5((data['request_id']+ i['cur_data']['bk_host_innerip']).encode('utf-8')).hexdigest()
        rkey = r.hget('cmdb',datajson['tex_id'])
        logger.info(rkey)
        if rkey is None:
            r.hset('cmdb',datajson['tex_id'],json.dumps(datajson['data']))
            datajson['result'] = 0
            logger.info(datajson)
        return datajson
    

    3.zabbix操作

    当common将cmdb事件的参数解析后,就可传给zabbix模块进行相应的分组同步操作了。

    (1)zabbix操作入口

    
    vim zabbix/main.py
    import json
    import logging
    from django.conf import settings
    from urllib import request, parse
    from .group import select_zabbix_group,create_zabbix_group,main_zabbix_group, update_zabbix_group, delete_zabbix_group
    from .host import select_zabbix_host, select_zabbix_host_group,update_zabbix_host_group
    
    logger = logging.getLogger('log')
    
    def zabbix_auth(ZABBIX_USERNAME,ZABBIX_PASSWORD,ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        # auth user and password
        data = {
            "jsonrpc": "2.0",
            "method": "user.login",
            "params": {
                "user": ZABBIX_USERNAME,
                "password": ZABBIX_PASSWORD
            },
            "id": 1,
        }
        # 由于API接收的是json字符串,故需要转化一下
        value = json.dumps(data).encode('utf-8')
        
        # 对请求进行包装
        req = request.Request(url, headers=header, data=value)
     
        # 验证并获取Auth ID
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("Auth Failed, Please Check Your Name And Password:", e)
        else:
            response = result.read()
            # 上面获取的是bytes类型数据,故需要decode转化成字符串
            page = response.decode('utf-8')
            #  将此json字符串转化为python字典
            page = json.loads(page)
            result.close()
            logger.info("Auth Successful. The Auth ID Is: {}".format(page.get('result')))
            return page.get('result')
    
    def zabbix_main(result):
        # 获取zabbix 认证 
        auth_code = zabbix_auth(settings.ZABBIX_USERNAME,settings.ZABBIX_PASSWORD,settings.ZABBIX_URL)
        logger.info("zabbix授权id:{}".format(auth_code))
        # 模块操作
        if result['obj_type'] == 'module':
            #####zabbix旧组是否存在,不存在直接创建新组
            result_data = select_zabbix_group(result['data']['pre_data']['group'],auth_code,settings.ZABBIX_URL)
            ###zabbix创建
            if result['action'] == 'create' :
                logger.info("zabbix组创建: {}".format(result))
            ###zabbix修改
            elif result['action'] == 'update'  and result['data']['pre_data'] != None:
                if len(result_data)  == 0:
                    logger.info("zabbix组旧模块不存在: {}".format(result['data']['pre_data']))  
                    res = create_zabbix_group(result['data']['cur_data']['group'],auth_code,settings.ZABBIX_URL)
                    if "err" in res:
                        logger.error('zabbix新增加组失败:{}'.format(result['data']['cur_data']))
                    else:
                        logger.info('zabbix新增加组成功:{}'.format(result['data']['cur_data']['group']))
                        return  result['data']['cur_data']['group']
                else:
                    res = update_zabbix_group(result_data[0]['groupid'],result['data']['cur_data']['group'],auth_code,settings.ZABBIX_URL)
                    logger.info('zabbix组修改完毕:{}'.format(result['data']['cur_data']['group']))
                    return result['data']['cur_data']['group']
            ###模块删除
            elif result['action'] == 'delete':
                logger.info("zabbix组删除: {}".format(result))
                result_data = select_zabbix_group(result['data']['pre_data']['group'],auth_code,settings.ZABBIX_URL)
                if len(result_data)  == 0:
                    logger.info("zabbix组旧模块不存在: {}".format(result['data']['pre_data'])) 
                else:
                    res = delete_zabbix_group(result_data[0]['groupid'],auth_code,settings.ZABBIX_URL)
                    logger.info("zabbix组删除完毕: {}".format(result))
            ###模块其他操作
            else:
                logger.info("模块未知操作: {}".format(result))
        ## 主机标识操作
        elif result['obj_type'] == 'hostidentifier':
            if result['action'] == 'update':
                logger.info("主机标识更新: {}".format(result))
                result_Groupidlist = main_zabbix_group(result,auth_code)
                if result_Groupidlist['result'] == 0:
                    res = update_zabbix_host_group(result_Groupidlist['hostid'],result_Groupidlist['grouplistid'],auth_code,settings.ZABBIX_URL)
                    logger.info('数据同步成功:{}'.format(res))
            else:
                logger.info("主机标识未知操作: {}".format(result))
        else:
            logger.info("未知操作: {}".format(result))
    
        return result
    

    (2)主机群组操作

    vim zabbix/group.py
    import json
    import logging
    from urllib import request, parse
    from django.conf import settings
    from .template import select_zabbix_template
    from .host import create_zabbix_host, select_zabbix_host
    from common.select_cmdb import select_cmdb_host
    logger = logging.getLogger('log')
    
    def select_zabbix_group(Groupname,auth_code,ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = {
        "jsonrpc": "2.0",
        "method": "hostgroup.get",
        "params": {
            "output": "extend",
            "filter": {
                "name": Groupname
            }
        },
        "auth": auth_code,
        "id": 1
        }
        value = json.dumps(data).encode('utf-8')
        req = request.Request(url, headers=header, data=value)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("result封装:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            result.close()
            logger.info("group_result: {}".format(response['result']))
            return response['result']
      
    def create_zabbix_group(Groupname,auth_code,ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = {
        "jsonrpc": "2.0",
        "method": "hostgroup.create",
        "params": {
            "name": Groupname
        },
        "auth": auth_code,
        "id": 1
        }
        value = json.dumps(data).encode('utf-8')
        logger.info("zabbix组创建信息: {}".format(value))
        req = request.Request(url, headers=header, data=value)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("主机组结构体创建失败:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            result.close()
            logger.info("group_result: {}".format(response))
            return response
    
    def update_zabbix_group(Groupid,Groupname,auth_code,ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = {
            "jsonrpc": "2.0",
            "method": "hostgroup.update",
            "params": {
                "groupid": Groupid,
                "name": Groupname,
            },
            "auth": auth_code,
            "id": 1
        }
        value = json.dumps(data).encode('utf-8')
        logger.info("zabbix组信息修改如下: {}".format(value))
        req = request.Request(url, headers=header, data=value)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("result封装:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            result.close()
            logger.info("group_result: {}".format(response['result']))
    
    def delete_zabbix_group(Groupid,auth_code,ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = {
            "jsonrpc": "2.0",
            "method": "hostgroup.delete",
            "params": [Groupid],
            "auth": auth_code,
            "id": 1
        }
        value = json.dumps(data).encode('utf-8')
        logger.info("zabbix组删除信息如下: {}".format(value))
        req = request.Request(url, headers=header, data=value)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("result封装:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            result.close()
            logger.info("group_result: {}".format(response['result']))
           
    def main_zabbix_group(result,auth_code):
        zabbix_host_group_id = {'hostid':'','grouplistid':[],'zabbix_host_name':'','result':1}
        Groupidlist = []
        ##获取zabbix中的组是否存在
        for i in result['data']['cur_data']['group']:
            zabbix_host_name= "[" +i+ "_" + result['data']['cur_data']['ip'] + "]" + zabbix_host_name 
            Groupname_result = select_zabbix_group(i,auth_code,settings.ZABBIX_URL)
            logger.info(Groupname_result)
            if len(Groupname_result) == 0:
                res = create_zabbix_group(i,auth_code,settings.ZABBIX_URL)
                if "errr" in res:
                    logger.error('zabbix增加组失败:{}'.format(i))
                else:
                    logger.info('zabbix增加组:{}'.format(i))
                    Groupidlist.append(res['result']['groupids'][0])
            else:
                Groupidlist.append(Groupname_result[0]['groupid'])
                logger.info('zabbix分组已存在:{}'.format(i))
        # 查询zabbix 主机信息获取组id,及主机id
        res = select_zabbix_host( result['data']['cur_data']['ip'],auth_code,settings.ZABBIX_URL)
        if len(res) != 0:
            logger.info('zabbix主机{}查询信息如下:{}'.format(result['data']['cur_data']['ip'],res))
            # 增加原始组id
            #for i in res[0]['groups']:
            #    Groupidlist.append(i['groupid'])
            zabbix_host_group_id['hostid'] = res[0]['hostid']
            zabbix_host_group_id['grouplistid'] = Groupidlist
            zabbix_host_group_id['result'] = 0
            return zabbix_host_group_id
        else:
            logger.info('zabbix主机{}查询信息为空:{}'.format(result['data']['cur_data']['ip']))
            cmdb_host_os_type = select_cmdb_host(result['data']['cur_data']['bk_host_id'])
            zabbix_template_name = "Template OS " + cmdb_host_os_type
            zabbix_template_id = select_zabbix_template(zabbix_template_name,auth_code,settings.ZABBIX_URL)
            logger.info('zabbix创建主机:{},主机系统类型:{},主机模板id:{}'.format(result['data']['cur_data']['ip'],cmdb_host_os_type,zabbix_template_id))
            res = create_zabbix_host(result['data']['cur_data']['ip'],zabbix_host_name,Groupidlist,zabbix_template_id,auth_code,settings.ZABBIX_URL)
            return zabbix_host_group_id
    

    (3)主机操作

    
    vim zabbix/host.py
    import json
    import logging
    from urllib import request, parse
    logger = logging.getLogger('log')
    
    def select_zabbix_host(Host,auth_code,ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = json.dumps({
            "jsonrpc": "2.0",
            "method": "host.get",
            "params": {
                "output": ["hostid","name","status","host","groupids"],
                "selectGroups":"extend",
                "selectInterfaces":["ip"],
                "filter":{"host":Host}
            },
            "id": 1,
            "auth": auth_code}).encode('utf-8')
        
        req = request.Request(url, headers=header, data=data)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("result封装:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            result.close()
            logger.info("group_result: {}".format(response['result']))
            return response['result']
    
    def select_zabbix_host_group(Host,auth_code,ZABBIX_URL):
        group_list = []
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = json.dumps({
                "jsonrpc": "2.0",
                "method": "host.get",
                "params": {
                    "output": ["hostid","name","status","host","groupids"],
                    "selectGroups":"extend",
                    "selectInterfaces":["ip"],
                    "filter":{"host":Host}
                },
                "id": 1,
                "auth": auth_code
            }).encode('utf-8')
        
        req = request.Request(url, headers=header, data=data)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("result封装:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            # 上面获取的是bytes类型数据,故需要decode转化成字符串
            result.close()
            logger.info("group_result: {}".format(response['result']))
            if (response != 0) and (len(response) != 0):
                groups = response['result'][0]['groups']
                for group in groups:
                    var = {}
                    var['groupid'] = group['groupid']
                    group_list.append(var)
                # return host['groups'][0]['groupid']
                return group_list
            else:
                return ""
        return group_list
    
    def update_zabbix_host():
        pass
    
    def update_zabbix_host_group(Hostid,Host_Group_list,auth_code, ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = json.dumps({
                "jsonrpc": "2.0",
                "method": "host.update",
                "params": {
                    # "hostid": '10202',
                    "hostid": Hostid,
                    "groups": Host_Group_list,
                    "status": 0,
                },
                "auth": auth_code,
                "id": 1
        }).encode('utf-8')
        logger.info("更新数据json如下:{}".format(data))
        req = request.Request(url, headers=header, data=data)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("result封装:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            logger.info("返回数据如下:{}".format(response))
            result.close()
            if 'error' in  response.keys():
                logger.info("host组信息修改失败")
            else:
                return response['result']
    
    def create_zabbix_host(Hostip,Hostname,Host_Group_list,templateid,auth_code,ZABBIX_URL):
        url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
        header = {"Content-Type": "application/json"}
        data = json.dumps({
            "jsonrpc": "2.0",
            "method": "host.create",
            "params": {
            "host": Hostip,
            "name": Hostname,
            "interfaces": [
                {
                    "type": 1,
                    "main": 1,
                    "useip": 1,
                    "ip": Hostip,
                    "dns": "",
                    "port": "10050"
                 }
            ],
            "groups": Host_Group_list,
            "templates": [
                {
                    "templateid": templateid
                }
                ]
            },
            "id": 1,
            "auth": auth_code}).encode('utf-8')
        logger.info("更新数据json如下:{}".format(data))
        req = request.Request(url, headers=header, data=data)
        try:
            # 打开包装过的url
            result = request.urlopen(req)
        except Exception as e:
            logger.error("result封装:", e)
        else:
            response = json.loads(result.read().decode('utf-8'))
            logger.info("返回数据如下:{}".format(response))
            result.close()
            if 'error' in  response.keys():
                logger.info("zabbix主机创建失败:{}".format(response))
            else:
                return response['result']
    
        return response['result']
    
    

    总结

    通过事件推送网关我们初步实现了cmdb和zabbix的分组同步,我们只需要在cmdb一侧就可以管理主机资源,不仅节省了我们跨平台处理的时间,而且规范了我们基础设施的管理。

    展开全文
  • 修改: 文章修改于2021/3/1日,昨晚半夜写完的第一套方案白天测试的时候不能实现自动Gitee Pages,所以紧急进行了修改,并对整个流程的细节进行了更详细的描述,相信跟着走下来,大家都可以打造出自己超好用的静态...

    修改: 文章修改于2021/3/1日,昨晚半夜写完的第一套方案白天测试的时候不能实现自动Gitee Pages,所以紧急进行了修改,并对整个流程的细节进行了更详细的描述,相信跟着走下来,大家都可以打造出自己超好用的静态博客写作、部署脚本,从此真正将更多的精力投入的写作中!!!

    我的个人博客是通过Github的Pages技术搭建的静态博客,使用 vuepress 驱动。由于Github在国内环境访问速度慢,并且不够稳定,所以通常对外介绍使用Gitee的镜像博客仓库,但是每次都需要在推送Github之后去Gitee上对应的仓库进行手动同步,就无端增加了很多重复的工作量,经过多方搜索和实践,最终选定了Github一个比较新的技术 – Github Actions 实现代码自动Build,同步到Gitee,自动在Gitee实现Pages功能

    下面来看一下如何操作吧:

    1.Github和Gitee添加公钥

    Window下通过git bash生成公钥

    输入 ssh-keygen ,然后连按三下回车

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iANQttqm-1614607310553)(https://oscimg.oschina.net/oscnet/up-64f9ebb1cfff6c20289786297c495de69a6.png “生成公钥”)]

    Gitee 和 Github 添加公钥

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ie15tXRV-1614607310556)(https://oscimg.oschina.net/oscnet/up-1b752f913a8dd26c5465e3c7e849586b383.png "Github 添加SSH ")]

    检测公钥是否成功添加

    ssh -T git@github.com
    ssh -T git@gitee.com
    #返回如下图所示结果,则成功添加公钥!
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EcDZDewe-1614607310558)(https://oscimg.oschina.net/oscnet/up-c47116a70136263815d14cf472487d39657.png “SSH公钥添加成功”)]

    2.Github仓库添加Token

    1. 生成账号Token

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPmIKUsN-1614607310559)(https://oscimg.oschina.net/oscnet/up-07210357b78ab191ad5fae4b64eedb3fc0e.png “Github中Token生成”)]

    1. 在Secret中添加Token

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-htsTMmiv-1614607310561)(https://oscimg.oschina.net/oscnet/up-e87a1e6f21d518350b90bd9a5b142c7090f.png “Secret添加账号Token”)]

    3.Github仓库添加私钥

    1. 进入Github想要同步的仓库-选择Settings进行设置-选择Secret进行设置

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dfBW4zJA-1614607310564)(https://oscimg.oschina.net/oscnet/up-daca3471008d543b708fc528e1ce8b3c9ab.png “添加仓库Secret”)]

    1. 创建新的仓库Secret GITEE_RSA_PRIVATE_KEY

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SUNWIRmA-1614607310565)(https://oscimg.oschina.net/oscnet/up-94cc86728d23cd3a49e1d2276488b211b31.png “添加私钥”)]

    1. 添加 GITEE_PASSWORD

    同之前一样,在仓库里添加名为 GITEE_PASSWORD 的Secret,内容是你的Gitee账号密码。

    最后检查一下是否在 Github 中添加了 ACCESS_TOKENGITEE_PASSWORDGITEE_RSA_PRIVATE_KEY 三个密钥。如下图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bSTEHxsh-1614607310567)(https://oscimg.oschina.net/oscnet/up-0e7232f89a2ee190b962c808f0a2a43090f.png “成功添加三个密钥的截图”)]

    👀 重头戏来了!!!

    在GitHub仓库里添加 Actions 脚本文件!!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ru4p2QhH-1614607310568)(https://oscimg.oschina.net/oscnet/up-3a8e8ba007f8fb448d4cf04f51051b3a735.png “创建自己的工作流”)]

    • 工作流脚本名字可以自定义,随意起!!
    • 复制下面的自动打包及同步脚本
    • 在之后每次有 Push 请求的时候,该脚本就会自动执行,完成云端Build静态文件部署到 gh_pages 分支,然后将代码同步到 Gitee 的镜像仓库,并且自动执行 Pages 的 Update 操作!!!

    自动打包及自动同步脚本

    name: A TO A:GH_PAGES
    on: [push]
    jobs:
      build-and-deploy:
        runs-on: ubuntu-latest
        steps:
        - name: Checkout
          uses: actions/checkout@master
    
        - name: Build and Deploy
          uses: jenkey2011/vuepress-deploy@master
          env:
            ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
            # 你要操作的目标仓库
            TARGET_REPO: china-fanxin/vuepress-blog
            # 构建结果存储的分支
            TARGET_BRANCH: gh_pages
            # 要使用的构建命令
            BUILD_SCRIPT: yarn && yarn build
            # 构建结果存储目录
            BUILD_DIR: docs/.vuepress/dist/
      sync:
        needs: build-and-deploy
        runs-on: ubuntu-latest
        steps:
        - name: Sync to Gitee
          uses: wearerequired/git-mirror-action@master
          env:
            SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }}
          with:
            # 来源仓库
            source-repo: "git@github.com:china-fanxin/vuepress-blog.git"
            # 目标仓库
            destination-repo: "git@gitee.com:china-fanxin/vuepress-blog.git"
      reload-pages:
        needs: sync
        runs-on: ubuntu-latest
        steps:
          - name: Build Gitee Pages
            uses: yanglbme/gitee-pages-action@main
            with:
              # 注意替换为你的 Gitee 用户名
              gitee-username: china-fanxin
              # 注意在 Settings->Secrets 配置 GITEE_PASSWORD
              gitee-password: ${{ secrets.GITEE_PASSWORD }}
              # 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错
              gitee-repo: china-fanxin/vuepress-blog
              # 要部署的分支,默认是 master,若是其他分支,则需要指定(指定的分支必须存在)
              branch: gh_pages
    

    😍 功能实现效果

    完成了上述配置后,就已经可以实现当有代码 Push 到 Github 后,自动将代码 Build 生成静态文件 Pages ,并且将代码同步到Gitee 上,并自动 Pages !!
    如下的 Actions 执行的结果图!!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3yiGNWOY-1614607310570)(https://oscimg.oschina.net/oscnet/up-d04edb445613dd634bace6415a50f5ab9b2.png “工作流执行结果”)]

    文档信息
    版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
    发表日期:2021-03-01 20:42:19 星期一

    更多精彩内容查看我的技术博客

    本篇文章由一文多发平台ArtiPub自动发布

    展开全文
  • 注:使用方法都在对应的网络连接里面 git clone git clone https://mirror.ghproxy.com/ https://github.com/stilleshan/ServerStatus wget & curl wget ...
  • 一、数据库复制涉及1、发布服务器:数据的来源服务器,维护源数据,决定哪些数据...4、订阅类型订阅订阅是指由发布服务器将所有发生修改过的数据复制给订阅者,这种在数据同步性价比较高的场合,推荐使用推订阅...
  • 一般情况下,我们是通过"show slave status \G;"提供的Seconds_Behind_Master值...但是经验告诉我,仅仅依靠Seconds_Behind_Master的值来监测主从同步数据是否延迟是绝对可靠的!!!曾经遇到过的一个坑:Mysql主...
  • sentinel的功能强大,官方也提供了包括数据库、redis、nacos等...好在官方预留了DynamicRulePublisher和DynamicRuleProvider两个接口,提供了自定义实现拉取、送,从而实现通过dashboard修改规则,也能同步到nacos
  • 如何修复Win11上的时钟不同步?在Windows11上,关键是要有一个准确的为设备配置的时间... 要手动同步时钟以修复Windows11上的时间和日期信息,请使用以下步骤:  1、在Windows11上打开设置。  2、点击时间和语言。
  • 我把 scheme 改成了 World 模式,而 World 模式的 id 取值就是固定的 anyone 不能用其他值,而且我还设置了 perms 为 R,所以这个节点只能读数据,但不能做其他操作,如果使用 setData 对其进行数据修改的话也会...
  • 一、Vue 不能检测数组和对象的变化 1.1数组渲染场景: 解决方法: Vue官方文档指定的$set方法: 向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象...
  • ODI数据增量同步

    2021-05-08 20:11:01
    背景最近在项目上开始使用ODI,需求是要将一个库中的表同步到另一个库中这里可以使用ODI Studio的CDC方式进行同步数据,但CDC方式需要在表上建trigger,会对表的性能造成影响,所以该方案被否掉了,基于...
  • 使用Canal同步数据到ES

    2021-09-08 14:05:18
    canal的介绍,在github 上的官方文档介绍的很好,我这边就介绍了。感兴趣的查看git地址:https://github.com/alibaba/canal 2、Canal版本 Canal 1.1.4版本,迎来最重要的WebUI能力,引入canal-admin工程,支持面向...
  • 问题说明以太坊的geth客户端在运行的过程中经常会出现同步失败或假死的状况。那么针对此状况可编写定时任务脚本来监控geth的日志。并通过日志的输出来确定是否重启geth客户端。实例以下根据geth日志中的...
  • //sxx-github【这里是我对送至 GitHub 做的一个标识,因为还要送至码云所以标识不能相同】//git@github.com:suxiaoxin4242/sxx.git【这里是仓库的地址,填写自己的地址】$ git remote add sxx-github git@github...
  • 本文描述了linux下使用rsync单向同步两个机器目录的问题。 使用rsync同步后可以保持目录的一致性(含删除操作)。数据同步方式1、从主机拉数据备机上启动的流程同步命令:rsync -avzP --delete root@{remoteHost}:{...
  • 1、 早期关系型数据库之间的数据同步1)、全量同步比如从oracle数据库中同步一张表的数据到Mysql中,通常的做法就是 分页查询源端的表,然后通过 jdbc的batch 方式插入到目标表,这个地方需要注意的是,分页查询时,...
  • 我期望实现的需求是,依然github写博客,然后push仓库后可以自动同步到gitee,结合devops流水线的工作原理,无需增加我的操作复杂度,这样国内也可以轻松的访问。完美! 说干就干,开始去网上查资料! 迁移github...
  • otter 数据同步

    2021-04-19 05:51:41
    otter 是一款数据同步利器基于数据库增量日志解析,准实时同步到本机房或异地机房的数据库中,支持 ...manager 运行时同步配置到 node 节点;node 节点将同步状态反馈到 manager 上基于 zookeeper,解决分布式...
  • Mysql数据库复制及备份一、mysql数据库复制概述复制解决的基本问题是让一台服务器的数据和另外的服务器保持同步。一台主服务器可以连接多台从服务器,并且从服务器也可以反过来作为主服务器。主服务器和从服务器可以...
  • 修改pid (canal像一个从库去读取mysql数据,所以他们的id是不能冲突的,mysql的刚才改为12345了) vim conf/canal.properties 4. 配置数据库信息和监听规则 ################################################# ## ...
  • 一、go-mysql-transfergo-mysql-transfer是一款MySQL实时、...go-mysql-transfer具有如下特点:1、依赖其它组件,一键部署2、集成多种接收端,如:Redis、MongoDB、Elasticsearch、RabbitMQ、Kafka、RocketMQ,...
  • kafka实现消息

    2021-03-17 19:53:32
    一个人知道的Topic是单点送,大家都知道Topic是广播。kafka消息消费机制:1.广播消费:通过定义topic前缀来标识属于广播的消息(例如:topicname:gonggao1535680698557,gonggao1535680722624,gonggao1535681677381...
  • 微软(Microsoft)似乎计画在行动软体市场,先前宣布将推出行动版本的 Edge 浏览器到 iOS 与...顾名思义,Microsoft Launcher 是一款建立微软产品之间的生活 App(过去称为 Microsoft Arrow),介面与使用方式相当简单,...
  • 摘要 很多 DBA 同学经常会遇到要从一个数据库实时同步到另一个数据库的问题,同构数据还相对容易,遇上异构数据、表多、数据量大等情况就...跟前面分享到的其他数据库同步的操作方式类似、起来感觉还不错、话多..
  • 在线QQ客服:1922638专业的SQL Server、MySQL数据库同步软件Oracle数据库目前依然处于商用数据库的霸主地位。 运行在Oracle数据库上的核心业务及核心数据的安全性尤为重要。目前市场上针对Oracle数据库常见的容灾...
  • 原标题:游戏同步器功能介绍、作用讲解及使用方法键盘鼠标同步器,可以实现将一套键鼠的信息同时发给多台电脑,适用于游戏,教学,测试等领域。由于采用了迈拓维矩绝对同步技术,鼠标在多台电脑画面完全一致,这一...
  • 同步文件 设置同步方向 批处理 其他类似软件 FreeFileSync linux下类似软件:Rsync 同步文件 网络上和本地同步 (异地备份) 本地和本地同步 (本地备份) 设置同步方向 批处理 ...
  • 一、同步分支 #/user/bin git fetch origin -p
  • java 定时同步数据的任务优化

    千次阅读 2021-03-08 02:49:46
    前言定时任务在系统中并少见,主要目的是用于需要定时处理数据或者执行某个操作的情况下,如定时关闭订单,或者定时备份。而常见的定时任务分为2种,第一种:固定时间执行,如:每分钟执行一次,每天执行一次。第...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 145,504
精华内容 58,201
关键字:

同步推不能用了