原创|对接三方服务商回调鉴权的程序代码设计

news2024/11/15 19:59:31

文章目录

  • 一、背景
  • 二、详细设计
    • 1、UML设计
    • 2、程序设计
      • 2.1、AuthenticateActionEnum
      • 2.2、AuthenticateDispatcher
      • 2.3、BaseAuthenticateContext`<Request>`
        • 2.3.1、ActivityStatusChangeAuthenticateContext
        • 2.3.2、VodEventNotifyAuthenticateContext
      • 2.4、AbstractAuthenticateHandler
        • 2.4.1、ActivityStatusChangeAuthenticateHandler
        • 2.4.2、VodEventNotifyAuthenticateHandler
      • 2.5、AuthenticateConfig
      • 2.6、VolcAuthenticateApolloConfig
      • 2.7、yml配置
      • 2.8、业务接入

通过系统应用服务总会与三方服务商进行对接,既然有对接,就会有回调。但是此应用服务由于部署在公网访问,为了考虑系统安全系以及防止报文被篡改,这就意味着我们需要跟三方服务商进行鉴权技术方案设计。此文章,就是一个具体典型的案例,由于此应用服务有两个不同的场景,但是鉴权设计上又有不同差异之处,所以在总体程序设计上巧妙的满足场景的需求前提下,又能尽可能做到更好的扩展维护。

一、背景

此次涉及到对接三方的两个不同场景,暂且定位场景1和场景2。场景1的鉴权方案就是通过http接口回调,在请求头+请求报文上做鉴权处理,具体鉴权机制:请求头中的签名=md5(base64(报文)+回调url+私钥+时间戳)。
而场景2的鉴权就是在请求报文中增加鉴权字段,该鉴权字段=md5(秘钥+字段1+字段2+字段3+…)。
总而言之,都是通过md5加密,只不过加密的数据步骤有些区别。
为了考虑减少代码的耦合度,同时尽可能提高后续的扩展性,在程序设计上引入了设计模式。

二、详细设计

1、UML设计

在这里插入图片描述
从上图可以看出,依然采用定义一个上下文对象BaseAuthenticateContext<Request>,该类定义一个泛型,意味着需要子类来继承,并指定请求参数类。通过AbstractAuthenticateHandler它来封装鉴权的共性逻辑,比如鉴权流程,以及相关复用的代码。相关子类来继承它,实现相关抽象方法即可。AuthenticateDispatcher这个类来对外暴露,外部调用无需晓得具体使用哪个Handler来处理,还需要委托给它即可。

2、程序设计

2.1、AuthenticateActionEnum

定义一个枚举,来维护所有的鉴权场景类型,这里把场景抽象成Action

/**
 * 鉴权活动枚举类型
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:30 AM
 */
@Getter
@ToString
public enum AuthenticateActionEnum {
    /**
     * 企业直播活动变更
     */
    ACTIVITY_STATUS_CHANGE("企业直播活动变更"),
    /**
     * 视频点播事件通知
     */
    VOD_EVENT_NOTIFY("视频点播事件通知");

    /**
     * 构造函数
     *
     * @param desc 描述
     */
    AuthenticateActionEnum(String desc) {
        this.desc = desc;
    }

    /**
     * 描述
     */
    private final String desc;
}

2.2、AuthenticateDispatcher

通过@Autowired这个注解,把AbstractAuthenticateHandler的子类集合自动装配,作为该类的一个成员。同时,提供一个分发的方法。

/**
 * 鉴权处理分发器
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:28 AM
 */
@Component
public class AuthenticateDispatcher {

    @Autowired
    private List<AbstractAuthenticateHandler> handlerList;

    /**
     * 执行处理
     *
     * @param context 上下文对象
     */
    public void execute(BaseAuthenticateContext context) {
        handlerList.stream()
            .filter(handler -> handler.getAction() == context.getAction())
            .forEach(handler -> handler.execute(context));
    }
}

2.3、BaseAuthenticateContext<Request>

定义一个上下文类。该类,包含一个内部静态类Response,并作为它的成员属性,来封装鉴权执行结果。

/**
 * 回调鉴权上下文对象
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:08 AM
 */
@ToString
@Getter
@Setter
public abstract class BaseAuthenticateContext<Request> {
    /**
     * 活动类型
     */
    private AuthenticateActionEnum action;
    /**
     * 请求参数
     */
    private Request request;
    /**
     * 响应结果
     */
    private Response response;

    @ToString
    @Getter
    @Setter
    public static class Response {
        /**
         * 静态变量
         */
        public static String SUCCESS = "鉴权成功";
        /**
         * 鉴权是否成功
         */
        private boolean success;
        /**
         * 鉴权结果
         */
        private String result;

        /**
         * 静态方法
         *
         * @param result 鉴权结果
         * @return 响应对象
         */
        public static Response buildSuccess(String result) {
            Response response = new Response();
            response.setResult(result);
            response.setSuccess(Boolean.TRUE);
            return response;
        }

        /**
         * 静态方法
         *
         * @param result 鉴权结果
         * @return 响应对象
         */
        public static Response buildFailure(String result) {
            Response response = new Response();
            response.setResult(result);
            response.setSuccess(Boolean.FALSE);
            return response;
        }
    }
}

2.3.1、ActivityStatusChangeAuthenticateContext

具体的一个场景子类

/**
 * [企业直播活动变更]回调鉴权上下文对象
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:08 AM
 */
@ToString(callSuper = true)
@Getter
@Setter
public class ActivityStatusChangeAuthenticateContext extends BaseAuthenticateContext<SubscribeLiveActivityStatusChangeRequest> {
}

2.3.2、VodEventNotifyAuthenticateContext

具体的一个场景子类

/**
 * [视频点播事件通知]回调鉴权上下文对象
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:08 AM
 */
@ToString(callSuper = true)
@Getter
@Setter
public class VodEventNotifyAuthenticateContext extends BaseAuthenticateContext<VolcVodRequestContext> {
}

2.4、AbstractAuthenticateHandler

鉴权处理类的基类,外部暴露的公共方法为public void execute(Context context)。该方法内部封装了具体鉴权的相关步骤,相关子类只需要实现相关抽象方法即可。

三个重要抽象方法:

  • abstract String getTraceId(Context context) :用于获取请求的traceId,便于日志打印,后续方便追踪问题。
  • abstract void doExecute(Context context):用于做具体的鉴权执行逻辑
  • abstract AuthenticateConfig getConfig():获取处理类场景的鉴权配置,该配置可以通过yml配置文件或者apollo实现,管理维护相关鉴权配置参数。
/**
 * 抽象鉴权处理器
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:14 AM
 */
@Slf4j
public abstract class AbstractAuthenticateHandler<Context extends BaseAuthenticateContext> implements LoggerService {

    @Autowired
    protected VolcAuthenticateApolloConfig volcAuthenticateApolloConfig;

    @PostConstruct
    void init() {
        getLog().info("AuthenticateApolloConfig={}", JsonUtils.toJson(volcAuthenticateApolloConfig));
    }

    /**
     * 鉴权活动类型
     */
    protected AuthenticateActionEnum action;
    /**
     * 活动名称
     */
    protected String actionName;

    /**
     * 构造函数
     *
     * @param action 活动类型
     */
    public AbstractAuthenticateHandler(AuthenticateActionEnum action) {
        this.action = action;
        if (Objects.nonNull(action)) {
            this.actionName = action.name();
        }
    }

    /**
     * 对外部方法
     *
     * @param context
     */
    public void execute(Context context) {
        String traceId = getTraceId(context);
        if (logDebug()) {
            getLog().info("[{}|{}],context={}", traceId, actionName, JsonUtils.toJson(context));
        }
        AuthenticateConfig config = getConfig();
        if (null == config) {
            context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));
            return;
        }
        boolean enableSwitch = Optional.ofNullable(config.getEnableSwitch()).orElse(Boolean.FALSE);
        //如果没有开启鉴权,则不执行鉴权
        if (!enableSwitch) {
            context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));
            return;
        }
        doExecute(context);
        BaseAuthenticateContext.Response response = context.getResponse();
        getLog().info("[{}|{}]{}", traceId, actionName, JsonUtils.toJson(response));
        if (!response.isSuccess()) {
            throw new ForbiddenException("鉴权失败[" + response.getResult() + "]", response.getResult());
        }
    }

    @Override
    public boolean logDebug() {
        Boolean enableLogDebug = volcAuthenticateApolloConfig.getEnableLogDebug();
        Boolean enable = Optional.ofNullable(enableLogDebug).orElse(Boolean.TRUE);
        return enable.booleanValue();
    }

    /**
     * 获取一个traceId,用于问题排查使用
     *
     * @param context 上下文对象
     * @return traceId
     */
    protected abstract String getTraceId(Context context);

    /**
     * 执行鉴权
     * 需要子类实现此方法,完成具体的健全处理
     *
     * @param context 上下文对象
     */
    protected abstract void doExecute(Context context);

    /**
     * 获取鉴权配置
     *
     * @return 鉴权配置
     */
    protected abstract AuthenticateConfig getConfig();

    public AuthenticateActionEnum getAction() {
        return action;
    }
}

2.4.1、ActivityStatusChangeAuthenticateHandler

鉴权场景1的具体鉴权逻辑。

/**
 * [企业直播活动变更]回调鉴权处理器
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:20 AM
 */
@Component
@Slf4j
public class ActivityStatusChangeAuthenticateHandler extends AbstractAuthenticateHandler<ActivityStatusChangeAuthenticateContext> {

    /**
     * 构造函数
     */
    public ActivityStatusChangeAuthenticateHandler() {
        super(AuthenticateActionEnum.ACTIVITY_STATUS_CHANGE);
    }


    @Override
    public Logger getLog() {
        return log;
    }

    @Override
    protected String getTraceId(ActivityStatusChangeAuthenticateContext context) {
        return context.getRequest().getActivityID();
    }

    @Override
    protected AuthenticateConfig getConfig() {
        return volcAuthenticateApolloConfig.getActivityStatusChange();
    }

    @Override
    protected void doExecute(ActivityStatusChangeAuthenticateContext context) {
        SubscribeLiveActivityStatusChangeRequest request = context.getRequest();
        String sign = request.getSign();
        String signature = getSignature(context);
        if (Objects.equals(sign, signature)) {
            context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));
        } else {
            String traceId = getTraceId(context);
            if (logDebug()) {
                getLog().info("[{}|{}],ts={},encrypted={}", traceId, actionName, request.getTimestamp(), signature);
            }
            String debug = MessageFormat.format("activityId={0},signature={1},md5={2}", traceId, sign, signature);
            context.setResponse(BaseAuthenticateContext.Response.buildFailure(debug));
        }
    }

    /**
     * 获取报文加密后的密文
     *
     * @param context 上下文对象
     * @return 密文
     */
    private String getSignature(ActivityStatusChangeAuthenticateContext context) {
        SubscribeLiveActivityStatusChangeRequest request = context.getRequest();
        String privateKey = volcAuthenticateApolloConfig.getActivityStatusChange().getPrivateKey();
        StringBuilder content = new StringBuilder(privateKey);
        content.append(request.getActivityID()).append(request.getEventType())
            .append(request.getStatus()).append(request.getTimestamp());
        String original = content.toString();
        String encrypted = Md5Util.encrypt(original);
        return encrypted;
    }
}

2.4.2、VodEventNotifyAuthenticateHandler

鉴权场景2的具体鉴权逻辑。

/**
 * [视频点播事件通知]回调鉴权处理器
 *
 * @author : Sieg Heil
 * @since 2022/11/25 10:20 AM
 */
@Component
@Slf4j
public class VodEventNotifyAuthenticateHandler extends AbstractAuthenticateHandler<VodEventNotifyAuthenticateContext> {

    /**
     * 构造函数
     */
    public VodEventNotifyAuthenticateHandler() {
        super(AuthenticateActionEnum.VOD_EVENT_NOTIFY);
    }

    @Override
    public Logger getLog() {
        return log;
    }

    @Override
    protected String getTraceId(VodEventNotifyAuthenticateContext context) {
        return context.getRequest().getRequest().getRequestId();
    }

    @Override
    protected AuthenticateConfig getConfig() {
        return volcAuthenticateApolloConfig.getVodEventNotify();
    }

    @Override
    protected void doExecute(VodEventNotifyAuthenticateContext context) {
        VolcVodRequestContext requestContext = context.getRequest();
        String sign = requestContext.getSignature();
        String original = getMd5Content(context);
        String signature = Md5Util.encrypt(original);
        if (Objects.equals(sign, signature)) {
            context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));
        } else {
            String traceId = getTraceId(context);
            if (logDebug()) {
                getLog().info("[{}|{}],encrypted={}", traceId, actionName, signature);
            }
            String debug = MessageFormat.format("requestId={0},signature={1},md5={2}", traceId, sign, signature);
            context.setResponse(BaseAuthenticateContext.Response.buildFailure(debug));
        }
    }

    private String getMd5Content(VodEventNotifyAuthenticateContext context){
        VolcVodRequestContext requestContext = context.getRequest();
        String requestBody = requestContext.getRequestBody();
        String privateKey = volcAuthenticateApolloConfig.getVodEventNotify().getPrivateKey();
        String callbackUrl = volcAuthenticateApolloConfig.getVodEventNotify().getCallbackUrl();
        String callbackContent = encode(requestBody);
        StringBuilder original = new StringBuilder(callbackUrl).append("|")
            .append(requestContext.getTimestamp()).append("|")
            .append(privateKey).append("|")
            .append(callbackContent);
        return original.toString();
    }

    private String encode(String value) {
        Base64.Encoder encoder = Base64.getEncoder();
        return encoder.encodeToString(value.getBytes(StandardCharsets.UTF_8));
    }
}

2.5、AuthenticateConfig

鉴权配置类

/**
 * 鉴权配置类
 *
 * @author : Sieg Heil
 * @since 2022/11/25 11:47 AM
 */
@ToString(callSuper = true)
@Getter
@Setter
public class AuthenticateConfig {
    /**
     * 鉴权开关
     */
    private Boolean enableSwitch;
    /**
     * 鉴权私钥
     */
    private String privateKey;
    /**
     * 回调url
     */
    private String callbackUrl;
    /**
     * 鉴权策略
     */
    private StrategyEnum strategy;

    /**
     * 鉴权策略类型
     */
    public enum StrategyEnum {
        /**
         * 对报文进行MD5加密,防止报文被篡改
         */
        MD5
    }
}

2.6、VolcAuthenticateApolloConfig

所有回调场景鉴权配置类

/**
 * 回调鉴权配置
 *
 * @author : Sieg Heil
 * @since 2022/11/25 11:55 AM
 */
@Component
@RefreshScope
@ConfigurationProperties(prefix = "xxx.xxx.authenticate.volc")
@ToString
@Getter
@Setter
public class VolcAuthenticateApolloConfig {

    /**
     * 是否启用日志输出,便于追踪问题
     */
    private Boolean enableLogDebug;

    /**
     * 企业直播活动变更
     */
    private AuthenticateConfig activityStatusChange;

    /**
     * 视频点播事件通知
     */
    private AuthenticateConfig vodEventNotify;
}

2.7、yml配置

yml配置文件,可以通过diamond或者apollo,当前应用服务对接了apollo。

xxx:
  xxx:
    # 回调配置 true|false
    callback:
      # 订阅企业直播活动状态变更
      subscribeVolcActivityStatusChange:
        # 启用日志输出
        logDebug: true
        # 启用日志输出
        enableHandle: false
      # 订阅视频点播事件通知
      subscribeVolcVodEventNotify:
        # 启用日志输出
        logDebug: true
        # 启用日志输出
        enableHandle: false   
    # 鉴权配置
    authenticate:
      # 鉴权配置
      volc:
        # 是否启用日志输出,便于追踪问题 true|false
        enableLogDebug: true
        # 企业直播活动变更
        activityStatusChange:
          # 鉴权开关
          enableSwitch: true
          # 鉴权私钥
          privateKey: xxxxx
          # 回调url
          callbackUrl: xxxx
          # 鉴权策略
          strategy: MD5
        # 视频点播事件通知
        vodEventNotify:
          # 鉴权开关
          enableSwitch: true
          # 鉴权私钥
          privateKey: xxxx
          # 回调url
          callbackUrl: xxxx         
          # 鉴权策略
          strategy: MD5          
        

2.8、业务接入

/**
 * Created at 2022/5/24 11:01 AM
 *
 * @author : Sieg Heil
 */
@ThriftService(service = "volcEngineCallback")
@Validated
@Slf4j
public class VolcEngineCallbackServiceImpl implements VolcEngineCallbackService{

    @Autowired
    private VolcEngineCallbackConverter volcEngineCallbackConverter;

    @Autowired
    private CallbackEnableSwitch callbackEnableSwitch;

    @Autowired
    private SubscribeLiveStatusEventDispatcher subscribeLiveStatusEventDispatcher;

    @Autowired
    private SubscribeVodEventDispatcher subscribeVodEventDispatcher;

    @Autowired
    private AuthenticateDispatcher authenticateDispatcher;

    @Override
    public void subscribeLiveActivityStatusChange(SubscribeLiveActivityStatusChangeRequest request) {
        ActivityStatusChangeAuthenticateContext authenticateContext = volcEngineCallbackConverter.convertToActivityStatusChangeAuthenticateContext(request);
        authenticateDispatcher.execute(authenticateContext);
        if (!callbackEnableSwitch.subscribeVolcActivityStatusChange()) {
            String traceId = request.getActivityID();
            log.info("[SubscribeLiveActivityStatusChange|{}]业务处理开关关闭|{}", traceId, callbackEnableSwitch.subscribeVolcActivityStatusChange());
            return;
        }
        SubscribeLiveStatusEventContext context = volcEngineCallbackConverter.convertToSubscribeLiveStatusEventContext(request);
        subscribeLiveStatusEventDispatcher.execute(context);
    }
}

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

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

相关文章

【Python+Appium】自动化测试(十一)location与size获取元素坐标

目录 前言 一&#xff0c;获取元素坐标的方法 1&#xff0c;size获取元素的宽、高 2&#xff0c;location获取元素左上角坐标 3&#xff0c;由此可以计算出元素其他的坐标 二&#xff0c;使用场景 结语 前言 appium做app自动化测试过程中&#xff0c;有时需要获取控件元…

git clean 命令详解

1. git clean 介绍 2. git clean 使用 3. clean 和 reset 命令 1. git clean 介绍 git clean 命令用于删除工作目录中没有被 tracked 的文件 这个命令很多人都不知道&#xff0c;也不去用它&#xff0c;而是通过手动去删除这些文件 这个命令一定要慎用&#xff0c;当你对这…

智源社区AI周刊No.108:Meta发布玩外交游戏的Cicero,登Science;Neuralink实现猴子意念打字...

汇聚每周AI热点&#xff0c;不错过重要资讯&#xff01;欢迎扫码&#xff0c;关注并订阅智源社区AI周刊。Meta发布外交谈判策略系统Cicero&#xff1a;模拟人类参与战略决策&#xff0c;已登《Science》期刊近日&#xff0c;Meta发布人工智能系统Cicero&#xff0c;该系统结合神…

【云原生】k8s 管理平台 rancher

文章目录一、概述二、Rancher 架构三、安装 Rancher1&#xff09;安装Helm2&#xff09;安装ingress-controller3&#xff09;为 Rancher 创建命名空间4&#xff09;选择 SSL 配置5&#xff09;安装 cert-manager6&#xff09;通过 Helm 安装 Rancher2&#xff09;添加 Helm Ch…

电子签名-为你的数据签字画押

博主&#xff1a;爱码叔 个人博客站点&#xff1a; icodebook 公众号&#xff1a;漫话软件设计 专注于软件设计与架构、技术管理。擅长用通俗易懂的语言讲解技术。对技术管理工作有自己的一定见解。文章会第一时间首发在个站上&#xff0c;欢迎大家关注访问&#xff01; 更多密…

[附源码]Python计算机毕业设计SSM绝味鸭脖连锁店信息系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

2023年天津天狮学院专升本市场营销专业《管理学》考试大纲

2023天津天狮学院高职升本科市场营销专业入学考试《管理学》考试大纲一、考试性质 《管理学》专业课程考试是天津天狮学院市场营销专业高职升本入学考试的必考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的选拔性考试。《管理学》考试大纲的编…

xcode登陆appleid报错:连接appleid服务器时出错

问题现象&#xff1a; xcode登陆appleid时一直报错&#xff1a;连接appleid服务器时出错 更换连接的wifi也一样报错&#xff0c;怀疑是因为Charles代理导致 解决办法&#xff1a; 网络连接 → 高级 → 代理 → 去掉 勾选【网页代理(HTTP)】

自动化之路:telnet的自动登录脚本

前言 为了测试telnet&#xff0c;首先&#xff0c;要保证系统已经安装了telnet&#xff0c;并且还得有一个端口能用,就是1-65536那个PORT。 一 搭建telenet环境并测试 1 首先查看telnet运行状态&#xff1a; lkmaoubuntu:~$ netstat -a | grep telnet lkmaoubuntu:~$ 输出为…

Linux 文本处理命令 - chmod

chmod命令&#xff1a;修改文件或目录的权限 chmod命令使用数字修改文件权限 Linux 系统中&#xff0c;文件的基本权限由 9 个字符组成&#xff0c;以 rwxrw-r-x 为例&#xff0c;我们可以使用数字来代表各个权限&#xff0c;各个权限与数字的对应关系如下&#xff1a; r --…

【c/c++算法】曼哈顿算法简单运用

✨曼哈顿算法&#x1f996;1.曼哈顿距离算法&#xff1a;&#x1f422;2.例题&#xff1a;打印菱形曼哈顿算法讲解&#xff1a;&#x1f995;3.曼哈顿算法例题解释&#xff1a;&#x1f996;1.曼哈顿距离算法&#xff1a; 我们很早以前就学过了两点间距离公式&#xff0c;欧式…

vue纯手写思维导图,拒绝插件(代码cv即用)

vue纯手写思维导图&#xff0c;拒绝插件(代码cv即用) 已完成功能点&#xff1a;折叠、放大、缩小、移动 后续增加功能点&#xff1a;添加、删除 先看结果&#xff1a; 有这么个需求&#xff0c;按照层级关系&#xff0c;把表格放在思维导图上&#xff0c;我第一时间想到用插件…

【Matplotlib绘制图像大全】(十六):Matplotlib绘制虚线折线图

前言 大家好,我是阿光。 本专栏整理了《Matplotlib绘制图像大全》,内包含了各种常见的绘图方法,以及Matplotlib各种内置函数的使用方法,帮助我们快速便捷的绘制出数据图像。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmMatp…

三翼鸟,用两年开启下一个十年

这段时间以来&#xff0c;我和亲朋好友的居家生活时间都增加了&#xff0c;以前一直存在但被忽视的不便体验&#xff0c;也开始凸显出来。比如在家就餐频次高了&#xff0c;厨房小白会为不会做饭而苦恼&#xff1b;智能设备的很多功能&#xff0c;依然需要手动操作……这些琐碎…

【大数据入门核心技术-Zookeeper】(二)ZAB协议介绍

目录 一、什么是Zab协议 二、Zab协议的4个阶段 1、选举阶段(Leader Election) 2、发现阶段(Discovery) 3、同步阶段(Synchronization) 4、广播阶段(Broadcase) 一、什么是Zab协议 ZAB协议&#xff0c;全称 Zookeeper Atomic Broadcast&#xff08;Zookeeper 原子广播协议…

Keras文本和序列(三)

本篇涉及的内容 如何对文本分词 什么是词嵌入&#xff0c;如何使用词嵌入 什么是循环网络&#xff0c;如何使用循环网络 如何堆叠 RNN 层和使用双向 RNN&#xff0c;以构建更加强大的序列处理模型 如何使用一维卷积神经网络来处理序列 如何结合一维卷积神经网络和 RNN 来处…

焱融全闪系列科普| 为什么 SSD 需要 NVMe?

NVMe 的由来 目前机械硬盘大多数使用 SATA (Serial ATA Advanced Host Controller Interface) 接口&#xff0c;接口协议为 AHCI&#xff0c;是 Intel 联合多家公司研发的系统接口标准。AHCI 最大队列深度为 32&#xff0c;即主机最多可以发 32 条命令给 HDD 或 SSD 执行&…

【观察】软通动力:以数智化技术创新,赋能每一个降碳场景

毫无疑问&#xff0c;“碳达峰、碳中和”已成为当今世界最为紧迫的使命&#xff0c;目前全球已有110多个国家相继承诺“碳中和”目标。同样&#xff0c;2020年9月&#xff0c;中国也提出了到2030年实现“碳达峰”&#xff0c;并努力争取2060年前实现“碳中和”的“双碳”目标&a…

六、组件的生命周期与组件间之间的数据共享

一、组件的生命周期 1.1、生命周期 & 生命周期函数 生命周期&#xff08;Life Cycle&#xff09;是指一个组件从创建→远行→销毁的整个阶段&#xff0c;强调的是一个时间段。 生命周期函数&#xff1a;是由vue框架提供的内置函数&#xff0c;会伴随着组件的生命周期&…

阿里云安装mysql、nginx、redis

目录 安装mysql 安装nginx ​编辑安装redis 先看一下系统基本信息 安装mysql rpm -qa | grep mariadb 卸载mariadb rpm -e --nodeps mariadb-libs-5.5.68-1.el7.x86_64 wget -i http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm yum -y install my…