Seata中AT模式的实现原理01-TM开启全局事务

news2024/11/22 10:13:06

什么是AT模式

AT模式是一种无侵入的分布式事务解决方案 保证最终一致性 是Seata默认的方式,在AT模式下,用户只需要关注自己的“业务SQL”,用户的“业务SQL”作为一阶段,Seata框架会自动的生成事务的二阶段提交和回滚

AT模式的机制

AT模式其实就是二阶段提交的演变
什么是二阶段提交可以看https://cloud.tencent.com/developer/article/2363585?areaId=106001
AT模式的主要流程是
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
提交异步化,非常快速地完成。
回滚通过一阶段的回滚日志进行反向补偿。

项目启动创建GlobalTransactional的代理类

SpringBoot项目启动的时候会通过自动装配机制将GlobalTransactionScanner装载到Bean中 然后 wrapIfNecessary方法会去做增强 创建针对 GlobalTransactionScanner的代理类 并构建全局的 globalTransactionalInterceptor拦截器
GlobalTransactionScanner

   // 对于扫描到的@GlobalTransactional注解, bean和beanName
// 判断类 或 类的某一个方法是否被@GlobalTransactional注解标注,进而决定当前Class是否需要创建动态代理;存在则创建。
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // do checkers,做一些检查,不用花过多精力关注
    if (!doCheckers(bean, beanName)) {
        return bean;
    }

    try {
        synchronized (PROXYED_SET) {
            if (PROXYED_SET.contains(beanName)) {
                return bean;
            }
            interceptor = null;
            //check TCC proxy TCC的动态代理
            if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                // init tcc fence clean task if enable useTccFence
                TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
                //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
                interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                        (ConfigurationChangeListener) interceptor);
            } else {
                // 先获取目标Class的接口
                Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);

                // existsAnnotation()表示类或类方法是否有被@GlobalTransactional注解标注,进而决定类是否需要被动态代理
                if (!existsAnnotation(new Class[]{serviceInterface})
                        && !existsAnnotation(interfacesIfJdk)) {
                    return bean;
                }

                if (globalTransactionalInterceptor == null) {
                    // 构建一个全局拦截器
                    globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                    ConfigurationCache.addConfigListener(
                            ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                            (ConfigurationChangeListener) globalTransactionalInterceptor);
                }
                interceptor = globalTransactionalInterceptor;
            }

            LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
            // 如果当前Bean没有被AOP代理
            if (!AopUtils.isAopProxy(bean)) {
                // 基于Spring AOP的AutoProxyCreator对当前Class创建全局事务动态动态代理类
                bean = super.wrapIfNecessary(bean, beanName, cacheKey);
            } else {
                AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                int pos;
                for (Advisor avr : advisor) {
                    // Find the position based on the advisor's order, and add to advisors by pos
                    // 找到seata切面的位置
                    pos = findAddSeataAdvisorPosition(advised, avr);
                    advised.addAdvisor(pos, avr);
                }
            }
            PROXYED_SET.add(beanName);
            return bean;
        }
    } catch (Exception exx) {
        throw new RuntimeException(exx);
    }
}

全局事务处理拦截器执行

对于一个全局事务来说,开启全局事务的角色,即TM
GlobalTransactionalInterceptor实现了MethodInterceptor接口,所以当每次执行添加了 GlobalTransactionalInterceptor拦截器的Bean的方法时,都会进入到GlobalTransactionalInterceptor类覆写MethodInterceptor接口的invoke()方法:

    @Override
    public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
        //获取目标类
        Class<?> targetClass =
            methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
       //获取调用的目标方法
        Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
        //只要不是object 的方法
        if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
            final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
            //拿到注解
            final GlobalTransactional globalTransactionalAnnotation =
                getAnnotation(method, targetClass, GlobalTransactional.class);
           //获取全局锁
            final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
            boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
            if (!localDisable) {
                if (globalTransactionalAnnotation != null) {
                    //处理全局事务
                    return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
                } else if (globalLockAnnotation != null) {
                    return handleGlobalLock(methodInvocation);
                }
            }
        }
        //调用原始方法
        return methodInvocation.proceed();
    }

GlobalTransactionalInterceptor

    private Object handleGlobalTransaction(final MethodInvocation methodInvocation,
        final GlobalTransactional globalTrxAnno) throws Throwable {
        boolean succeed = true;
        try {
            //调用之后调用重写的方法
            return transactionalTemplate.execute(new TransactionalExecutor() {
                @Override
                public Object execute() throws Throwable {
                    return methodInvocation.proceed();
                }
               //获取名字  GlobalTransactional中定义的name
                public String name() {
                    String name = globalTrxAnno.name();
                    if (!StringUtils.isNullOrEmpty(name)) {
                        return name;
                    }
                    return formatMethod(methodInvocation.getMethod());
                }

                //填充属性
                @Override
                public TransactionInfo getTransactionInfo() {
                    TransactionInfo transactionInfo = new TransactionInfo();
                    transactionInfo.setTimeOut(globalTrxAnno.timeoutMills());
                    transactionInfo.setName(name());
                    transactionInfo.setPropagation(globalTrxAnno.propagation());
                    Set<RollbackRule> rollbackRules = new LinkedHashSet<>();
                    for (Class<?> rbRule : globalTrxAnno.rollbackFor()) {
                        rollbackRules.add(new RollbackRule(rbRule));
                    }
                    for (String rbRule : globalTrxAnno.rollbackForClassName()) {
                        rollbackRules.add(new RollbackRule(rbRule));
                    }
                    for (Class<?> rbRule : globalTrxAnno.noRollbackFor()) {
                        rollbackRules.add(new NoRollbackRule(rbRule));
                    }
                    for (String rbRule : globalTrxAnno.noRollbackForClassName()) {
                        rollbackRules.add(new NoRollbackRule(rbRule));
                    }
                    transactionInfo.setRollbackRules(rollbackRules);
                    return transactionInfo;
                }
            });
        } catch (TransactionalExecutor.ExecutionException e) {
            TransactionalExecutor.Code code = e.getCode();
            switch (code) {
                case RollbackDone:
                    throw e.getOriginalException();
                case BeginFailure:
                    succeed = false;
                    failureHandler.onBeginFailure(e.getTransaction(), e.getCause());
                    throw e.getCause();
                case CommitFailure:
                    succeed = false;
                    failureHandler.onCommitFailure(e.getTransaction(), e.getCause());
                    throw e.getCause();
                case RollbackFailure:
                    failureHandler.onRollbackFailure(e.getTransaction(), e.getOriginalException());
                    throw e.getOriginalException();
                case RollbackRetrying:
                    failureHandler.onRollbackRetrying(e.getTransaction(), e.getOriginalException());
                    throw e.getOriginalException();
                default:
                    throw new ShouldNeverHappenException(String.format("Unknown TransactionalExecutor.Code: %s", code));
            }
        } finally {
            if (degradeCheck) {
                EVENT_BUS.post(new DegradeCheckEvent(succeed));
            }
        }
    }

处理事务传播机制

TransactionalTemplate

 public Object execute(TransactionalExecutor business) throws Throwable {
        // 1 get transactionInfo
        //获取事务信息
        TransactionInfo txInfo = business.getTransactionInfo();
        if (txInfo == null) {
            throw new ShouldNeverHappenException("transactionInfo does not exist");
        }
        // 1.1 get or create a transaction
        //获取全局事务
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();

        // 1.2 Handle the Transaction propatation and the branchType
        //事务传播行为 默认REQUIRED
        Propagation propagation = txInfo.getPropagation();
        SuspendedResourcesHolder suspendedResourcesHolder = null;
        try {
            
            switch (propagation) {
                    //非事务方式执行 存在事务挂起
                case NOT_SUPPORTED:
                    suspendedResourcesHolder = tx.suspend(true);
                    return business.execute();
                    // 新建事务  如果当前存在事务把事务挂起
                case REQUIRES_NEW:
                    suspendedResourcesHolder = tx.suspend(true);
                    break;
                    //支持当前事务 如果当前没有事务已非事务的方式运行
                case SUPPORTS:
                    if (!existingTransaction()) {
                        return business.execute();
                    }
                    break;
                    //支持当前事务 如果没有则新建
                case REQUIRED:
                    break;
                    // 已非事务的方式执行 如果当前存在事务抛出异常
                case NEVER:
                    if (existingTransaction()) {
                        throw new TransactionException(
                                String.format("Existing transaction found for transaction marked with propagation 'never',xid = %s"
                                        ,RootContext.getXID()));
                    } else {
                        return business.execute();
                    }
                    // 支持当前事务 如果当前存在事务则抛异常
                case MANDATORY:
                    if (!existingTransaction()) {
                        throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
                    }
                    break;
                default:
                    throw new TransactionException("Not Supported Propagation:" + propagation);
            }


           ......
        } finally {
            tx.resume(suspendedResourcesHolder);
        }

    }

全局事务开启

   ......
   try {

                // 2. begin transaction 
                //开启全局事务
                beginTransaction(txInfo, tx);
 
                Object rs = null;
                try {
                    //执行业务代码
                    // Do Your Business
                    rs = business.execute();

                } catch (Throwable ex) {
                   //出现异常回滚
                    // 3.the needed business exception to rollback.
                    completeTransactionAfterThrowing(txInfo, tx, ex);
                    throw ex;
                }
                //提交
                // 4. everything is fine, commit.
                commitTransaction(tx);

                return rs;
            } finally {
                //5. clear
                triggerAfterCompletion();
                cleanUp();
            }
            ......

获取全局事务

    public static GlobalTransaction getCurrentOrCreate() {
        GlobalTransaction tx = getCurrent();
        if (tx == null) {
            return createNew();
        }
        return tx;
    }

开启全局事务

    private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
        try {
            //前置钩子扩展
            triggerBeforeBegin();
            tx.begin(txInfo.getTimeOut(), txInfo.getName());
            //后置钩子扩展
            triggerAfterBegin();
        } catch (TransactionException txe) {
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                TransactionalExecutor.Code.BeginFailure);

        }
    }

DefaultGlobalTransaction

    @Override
    public void begin(int timeout, String name) throws TransactionException {
          
        if (role != GlobalTransactionRole.Launcher) {
            assertXIDNotNull();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNull();
        if (RootContext.getXID() != null) {
            throw new IllegalStateException();
        }
        //开启事务  xid是server端创建返回的
        xid = transactionManager.begin(null, null, name, timeout);
        //修改状态为Begin
        status = GlobalStatus.Begin;
        //绑定 xid 在被调用方会从Feign 那个拦截器里拿出来用
        RootContext.bind(xid);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Begin new global transaction [{}]", xid);
        }

    }
    @Override
    public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
        throws TransactionException {
        //创建请求
        GlobalBeginRequest request = new GlobalBeginRequest();
        request.setTransactionName(name);
        request.setTimeout(timeout);
        //发起请求 通过netty
        GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request);
        if (response.getResultCode() == ResultCode.Failed) {
            throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg());
        }
        return response.getXid();
    }

TC处理TM的开启全局事务请求

前面那些Netty怎么处理接收请求的就不写了 想看的可以子集dbug看
AbstractTCInboundHandler

    @Override
    public GlobalBeginResponse handle(GlobalBeginRequest request, final RpcContext rpcContext) {
        GlobalBeginResponse response = new GlobalBeginResponse();
        exceptionHandleTemplate(new AbstractCallback<GlobalBeginRequest, GlobalBeginResponse>() {
            @Override
            public void execute(GlobalBeginRequest request, GlobalBeginResponse response) throws TransactionException {
                try {
                    doGlobalBegin(request, response, rpcContext);
                } catch (StoreException e) {
                    throw new TransactionException(TransactionExceptionCode.FailedStore,
                        String.format("begin global request failed. xid=%s, msg=%s", response.getXid(), e.getMessage()),
                        e);
                }
            }
        }, request, response);
        return response;
    }

DefaultCoordinator

    @Override
    protected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext)
            throws TransactionException {
        //设置全局事务id
        response.setXid(core.begin(rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(),
                request.getTransactionName(), request.getTimeout()));
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Begin new global transaction applicationId: {},transactionServiceGroup: {}, transactionName: {},timeout:{},xid:{}",
                    rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout(), response.getXid());
        }
    }
    @Override
    public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
        throws TransactionException {
        //创建一个全局会话
        GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name,
            timeout);
        //设置xid信息 到map里面 
        MDC.put(RootContext.MDC_KEY_XID, session.getXid());
        session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
        //开启一个会话  
        session.begin();
        //发送事件
        // transaction start event
        MetricsPublisher.postSessionDoingEvent(session, false);

        return session.getXid();
    }

GlobalSession


    @Override
    public void begin() throws TransactionException {
        //更新状态
        this.status = GlobalStatus.Begin;
        //更新变更事件
        this.beginTime = System.currentTimeMillis();
        this.active = true;
        //遍历会话生命周期的监听
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            lifecycleListener.onBegin(this);
        }
    }

AbstractSessionManager

    @Override
    public void onBegin(GlobalSession globalSession) throws TransactionException {
        addGlobalSession(globalSession);
    }

AbstractSessionManager

    @Override
    public void addGlobalSession(GlobalSession session) throws TransactionException {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("MANAGER[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_ADD);
        }
        writeSession(LogOperation.GLOBAL_ADD, session);
    }

将全局事务写到事务表(global_table)中
AbstractSessionManager

    private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException {
        if (!transactionStoreManager.writeSession(logOperation, sessionStorable)) {
            if (LogOperation.GLOBAL_ADD.equals(logOperation)) {
                throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to store global session");
            } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) {
                throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to update global session");
            } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) {
                throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to remove global session");
            } else if (LogOperation.BRANCH_ADD.equals(logOperation)) {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to store branch session");
            } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to update branch session");
            } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to remove branch session");
            } else {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Unknown LogOperation:" + logOperation.name());
            }
        }
    }

在这里插入图片描述
transactionStoreManager 分为三种一种是文件的形式 一种是db的形式一种是redis的形式 他们是项目启动的时候在Server的start方法中调用SessionHolder的init方法进行加载 mode是在 配置文件的 store.mode进行配置 我这里是配置的 db 所以会走下面的 DataBaseTransactionStoreManager 的逻辑中
DataBaseTransactionStoreManager

    @Override
    public boolean writeSession(LogOperation logOperation, SessionStorable session) {
        if (LogOperation.GLOBAL_ADD.equals(logOperation)) {
            return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
        } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) {
            return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
        } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) {
            return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
        } else if (LogOperation.BRANCH_ADD.equals(logOperation)) {
            return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
        } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) {
            return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
        } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) {
            return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
        } else {
            throw new StoreException("Unknown LogOperation:" + logOperation.name());
        }
    

LogStoreDataBaseDAO

    @Override
    public boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
         //新增global_table信息的sql
        String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertGlobalTransactionSQL(globalTable);
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            int index = 1;
            conn = logStoreDataSource.getConnection();
            conn.setAutoCommit(true);
            ps = conn.prepareStatement(sql);
            ps.setString(index++, globalTransactionDO.getXid());
            ps.setLong(index++, globalTransactionDO.getTransactionId());
            ps.setInt(index++, globalTransactionDO.getStatus());
            ps.setString(index++, globalTransactionDO.getApplicationId());
            ps.setString(index++, globalTransactionDO.getTransactionServiceGroup());
            String transactionName = globalTransactionDO.getTransactionName();
            transactionName = transactionName.length() > transactionNameColumnSize ?
                transactionName.substring(0, transactionNameColumnSize) :
                transactionName;
            ps.setString(index++, transactionName);
            ps.setInt(index++, globalTransactionDO.getTimeout());
            ps.setLong(index++, globalTransactionDO.getBeginTime());
            ps.setString(index++, globalTransactionDO.getApplicationData());
            return ps.executeUpdate() > 0;
        } catch (SQLException e) {
            throw new StoreException(e);
        } finally {
            IOUtil.close(ps, conn);
        }
    }

在这里插入图片描述
在这里插入图片描述
自此TM开启全局事务结束

总结

1.项目启动的时候会由SpringBoot自动装配机制把GlobalTransactionScanner装配进spring容器中去做增强并会注入一个全局事务的拦截器
2.当有标准了GlobalTransactional的方法被执行的时候会通过拦截器进行拦截进行全局事务的开启
3.开启全局事务的之前会先根据不同的事务隔离级别去做不同的处理
4.然后TM会通过Netty发送一个开启全局事务的请求到TC
5.TC接收到请求之后会根据不同的数据类型去做不同的处理 我这里用的是db 并且用的是mysql 他会在mysql的global_table中去插入一条全局事务的数据 并更新事务状态为Begin
6.返回XID给TM 作为全局事务的ID
请添加图片描述

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

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

相关文章

生活中的物理2——人类迷惑行为(用笔扎手)

1实验 材料 笔、手 实验 1、先用手轻轻碰一下笔尖&#xff08;未成年人须家长监护&#xff09; 2、再用另一只手碰碰笔尾 你发现了什么&#xff1f;&#xff1f; 2发现 你会发现碰笔尖的手明显比碰笔尾的手更痛 你想想为什么 3原理 压强f/s 笔尖的面积明显比笔尾的小 …

AI技术迅猛发展,视频智能化给人类带来了哪些便利?

随着AI技术的迅猛发展&#xff0c;视频智能化也逐渐普及。在我们常见的生产工作和日常生活中&#xff0c;视频智能化都为人类带来了许多便利。今天小编就和大家探讨一下智能化监控带来了哪些便利。 1、安全监控 视频智能化可以实现智能安防监控&#xff0c;例如智慧安防系统Ea…

FLASH闪存的读取、擦除、编程

一、stm32寄存器地址介绍 二、FLASH简介 &#xff08;1&#xff09;STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&#xff0c;通过闪存存储器接口可以对程序存储器和选项字节进行擦除和编程 &#xff08;2&#xff09; 读写FLASH的用途&#xff1a;利用程…

Android Studio打包有哪些优势

大家好&#xff0c;现在移动应用程序的快速发展&#xff0c;开发者需要一个强大又可靠的开发环境来创建和打包高质量的 Android 应用程序。Android Studio 是一款由 Google 官方开发的 Android 应用程序开发环境&#xff0c;提供了许多的优势和便利&#xff0c;那究竟都有哪些优…

LeetCode刷题--- 括号生成

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 http://t.csdnimg.cn/6AbpV 数据结构与算法 http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述递归递归、搜…

【自用】Ubuntu20.4从Vivado到ddr200t运行HelloWorld

【自用】Ubuntu20.4新系统从输入法到ddr200t运行HelloWorld 一、编辑bashrc二、Vivado2022.2安装三、编译蜂鸟E203自测样例1. 环境准备2. 下载e203_hbirdv2工程文件3. 尝试编译自测案例1. 安装RISC-V GNU工具链2. 编译测试样例 4. 用vivado为FPGA生成mcs文件1.准备RTL2.生成bit…

界面控件DevExpress WPF Dock组件,轻松创建类Visual Studio窗口界面!

本文主要为大家介绍DevExpress WPF控件中的Dock组件&#xff0c;它能帮助用户轻松创还能受Microsoft Visual Studio启发的Dock窗口界面。 P.S&#xff1a;DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress …

社交网络分析(汇总)

这里写自定义目录标题 写在最前面社交网络分析系列文章汇总目录 提纲问题一、社交网络相关定义和概念提纲问题1. 社交网络、社交网络分析&#xff1b;2. 六度分隔理论、贝肯数、顿巴数&#xff1b;3. 网络中的数学方法&#xff1a;马尔科夫过程和马尔科夫链、平均场理论、自组织…

EasyUiAutotest 项目目录设置及说明

一、前置说明 清晰的项目目录结构非常重要的&#xff0c;它能够为项目提供结构化、易维护、易理解的环境。 二、目录设置及说明 项目目录结构如下&#xff1a; EasyUiAutotest ├───atme # me&#xff0c;供个人使用的目录&#xff0c;与整体项目无关&#xff0c;存…

ubuntu 20.04安装一系列软件

1&#xff09;安装下载的包的指令&#xff1a; sudo dpkg -i xxx.deb 2&#xff09;通用指令&#xff1a; sudo apt-get install xxxx 3&#xff09;更新和升级软件包&#xff08;遇到Unable to locate packge等问题先尝试这个指令&#xff09;&#xff1a; sudo apt-get up…

如何取消iCloud订阅,这里有非常明细的步骤

iCloud是存储文件、照片和备份的好方法&#xff0c;但每个用户获得的5GB免费iCloud存储空间往往不够。如果你使用iCloud Drive在设备之间存储和传输大量文件&#xff0c;你可能需要购买更多的iCloud存储。我们将向你展示如何在iPhone设置中更改iCloud存储计划或取消iCloud订阅。…

java8流库之Stream.iterate

简介 java.util.stream.Stream 下共有两个 iterate iterate(T seed, final UnaryOperator<T> f)iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> f) 该方法产生一个无限流&#xff0c;它的元素包含seed&#xff0c;在seed上调用f产生的…

跟着野火学FreeRTOS:第一段(空闲任务与阻塞延时的实现)

在前一小节中&#xff0c;任务操作里面的延时就是直接让 C P U CPU CPU干等着&#xff0c;啥也不干&#xff0c;这样会极大的浪费 C P U CPU CPU的资源。这一小节即将要讲到的阻塞延时就是当任务有延时需要的时候让 C P U CPU CPU不要干等着&#xff0c;而是去执行其它的任务&a…

取消paypal免密支付绑定平台

在设置支付中-》自动支付-》取消特定平台即可。

随机森林 2(决策树)

通过 随机森林 1 的介绍&#xff0c;相信大家对随机森林都有了一个初步的认知&#xff0c;知道了随机和森林分别指的是什么&#xff0c;以及决策树根据什么选择内部节点。本文将会从森林深入到树&#xff0c;去看一下决策树是如何构建的。网上很多文章都讲了决策树如何构建&…

【已解决】解决Springboot项目访问本地图片等静态资源无法访问的问题

今天在开发一个招聘系统的时候&#xff0c;有投递简历功能&#xff0c;有投递就会有随之而来的查看简历对吧&#xff0c;我投递过的简历&#xff0c;另存为一个文件夹&#xff0c;就是说本地磁盘(或者服务器)有一个专门存放投递过的简历的文件夹&#xff0c;用于存放PDF&#x…

用户管理第2节课--idea 2023.2 后端--实现基本数据库操作(操作user表)

一、模型user对象>和数据库的字段关联 & 自动生成 【其中涉及删除表数据&#xff0c;一切又从零开始】 二、模型user对象>和数据库的字段关联 2.1在model文件夹下&#xff0c;新建 user对象 2.1.1 概念 大家可以想象我们现在的数据是存储在数据库里的&…

Java中的时间日期类⭐️通过具体案例分析下开发中常用到的几种时间日期格式类的使用

小伙伴们大家好&#xff0c;系统中不少模块都要用到时间日期&#xff0c;来分析总结下项目中用到的些日期类 目录 一、时间日期类 1.java.util.Calendar&#xff1a; 2.java.util.Date&#xff1a; 3.java.time.LocalDate、java.time.LocalTime、java.time.LocalDateTime&…

Ubuntu 常用命令之 netstat 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 netstat 是一个非常有用的命令行工具&#xff0c;它可以帮助我们监控和诊断网络问题。在 Ubuntu 系统中&#xff0c;我们可以使用 netstat 命令来查看网络连接、路由表、接口统计等信息。 netstat 命令的参数有很多&#xff0c;以…

anaconda 安装 使用 pytorch onnx onnxruntime

一&#xff1a;安装 如果不是 x86_64&#xff0c;需要去镜像看对应的版本 安装 Anaconda 输入命令 bash Anaconda3-2021.11-Linux-x86_64.sh 然后输入 yes 表示同意 确认安装的路径&#xff0c;一般直接回车安装在默认的 /home/你的名字/anaconda3 很快就安装完毕。输入 yes…