Seata客户端启动流程

自动装配

Springboot启动的时候会将下面这几个类进行自动装配
在这里插入图片描述
在这里插入图片描述

SeataRestTemplateAutoConfiguration(装载拦截器)

这里会装配 SeataRestTemplateInterceptor (Seata的拦截器)

@Configuration(proxyBeanMethods = false)
public class SeataRestTemplateAutoConfiguration {

	@Bean
	public SeataRestTemplateInterceptor seataRestTemplateInterceptor() {
		return new SeataRestTemplateInterceptor();
	}

	@Bean
	public SeataRestTemplateInterceptorAfterPropertiesSet seataRestTemplateInterceptorConfiguration() {
		return new SeataRestTemplateInterceptorAfterPropertiesSet();
	}

}

在bean初始化的时候会将拦截器装配到 RestTemplate 中

public class SeataRestTemplateInterceptorAfterPropertiesSet implements InitializingBean {

	@Autowired(required = false)
	private Collection<RestTemplate> restTemplates;

	@Autowired
	private SeataRestTemplateInterceptor seataRestTemplateInterceptor;
     
    
	@Override
	public void afterPropertiesSet() {
		if (this.restTemplates != null) {
			for (RestTemplate restTemplate : restTemplates) {
				List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(
						restTemplate.getInterceptors());
				interceptors.add(this.seataRestTemplateInterceptor);
				restTemplate.setInterceptors(interceptors);
			}
		}
	}

}

这样每次调用rest接口的时候都会走到拦截器中进行逻辑处理

	@Override
	public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
			ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
		// 1、对httpRequest做一个包装
        HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
        //2、从本地事务上下文(ThreadLocal)中获取全局事务xid
		String xid = RootContext.getXID();
         // 如果可以从本地事务上下文中获取到全局事务xid,则将其添加到httpRequest的请求头上;
		if (!StringUtils.isEmpty(xid)) {
			requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
		}
        //执行http请求
		return clientHttpRequestExecution.execute(requestWrapper, bytes);
	}

SeataHandlerInterceptorConfiguration

启动的时候添加一个拦截器 拦截针对的路径为:/**

@ConditionalOnWebApplication
public class SeataHandlerInterceptorConfiguration implements WebMvcConfigurer {

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new SeataHandlerInterceptor()).addPathPatterns("/**");
	}

}

SeataHandlerInterceptor
XID是全局事务统一的标识

preHandle方法:执行请求之前,从本地事务上下文(线程本地变量ThreadLocal)中获取全局事务xid;从请求头中获取全局事务xid(rpcXid);如果xid不存在,但是rpcXid存在,则将rpcXid作为xid绑定到全局事务上下文(线程本地变量ThreadLocal)上;
afterCompletion方法:请求执行完毕之后;如果全局事务上下文中xid存在,但是请求头中rpcXid不存在,则不对xid进行处理,
但是如果全局事务上下文中xid存在 并且 请求头中包括rpcXid,则将xid从全局事务上下文中移除,如果移除的xid和rpcXid不相等,则将rpcXid作为xid绑定到全局事务上下文中。

    
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
			Object handler) {
         
		String xid = RootContext.getXID();
		String rpcXid = request.getHeader(RootContext.KEY_XID);
		if (log.isDebugEnabled()) {
			log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
		}

		if (xid == null && rpcXid != null) {
			RootContext.bind(rpcXid);
			if (log.isDebugEnabled()) {
				log.debug("bind {} to RootContext", rpcXid);
			}
		}
		return true;
	}


	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception e) {

		String rpcXid = request.getHeader(RootContext.KEY_XID);

		if (StringUtils.isEmpty(rpcXid)) {
			return;
		}

		String unbindXid = RootContext.unbind();
		if (log.isDebugEnabled()) {
			log.debug("unbind {} from RootContext", unbindXid);
		}
		if (!rpcXid.equalsIgnoreCase(unbindXid)) {
			log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
			if (unbindXid != null) {
				RootContext.bind(unbindXid);
				log.warn("bind {} back to RootContext", unbindXid);
			}
		}
	}

SeataFeignClientAutoConfiguration

处理Feign调用
在这里插入图片描述
这三个build最后都是build一个SeataFeignClient用来处理
在通过OpenFeign指定请求之前,也是会从事务上下文中获取xid,然后将其放到请求头中;

	@Override
	public Response execute(Request request, Request.Options options) throws IOException {

		Request modifiedRequest = getModifyRequest(request);
		return this.delegate.execute(modifiedRequest, options);
	}

	private Request getModifyRequest(Request request) {
        //获取全局事务ID
		String xid = RootContext.getXID();
        //如果是空直接返回
		if (StringUtils.isEmpty(xid)) {
			return request;
		}

		Map<String, Collection<String>> headers = new HashMap<>(MAP_SIZE);
		headers.putAll(request.headers());

		List<String> seataXid = new ArrayList<>();
		seataXid.add(xid);
		headers.put(RootContext.KEY_XID, seataXid);
        //设置全局事务id到请求头中组装新的请求
		return Request.create(request.method(), request.url(), headers, request.body(),
				request.charset());
	}

SeataAutoConfiguration

    @Bean(BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER)
    @ConditionalOnMissingBean(name = {BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER})
    public SpringApplicationContextProvider springApplicationContextProvider() {
        return new SpringApplicationContextProvider();
    }
    //异常处理
    @Bean(BEAN_NAME_FAILURE_HANDLER)
    @ConditionalOnMissingBean(FailureHandler.class)
    public FailureHandler failureHandler() {
        return new DefaultFailureHandlerImpl();
    }
    //全局事务管理器
    @Bean
    @DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})
    @ConditionalOnMissingBean(GlobalTransactionScanner.class)
    public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties, FailureHandler failureHandler) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Automatically configure Seata");
        }
        return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup(), failureHandler);
    }
    //Seata数据源代理
    @Bean(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)
    @ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = {"enableAutoDataSourceProxy", "enable-auto-data-source-proxy"}, havingValue = "true", matchIfMissing = true)
    @ConditionalOnMissingBean(SeataAutoDataSourceProxyCreator.class)
    public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) {
        return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(),seataProperties.getExcludesForAutoProxying());
    }

初始化全局事务管理器

GlobalTransactionScanner 实现了InitializingBean接口在bean初始化的时候会调用到afterPropertiesSet方法中进行Seata客户端的建立 用来和Seata服务端建立连接

    @Override
    public void afterPropertiesSet() {
        if (disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
            return;
        }
        //初始化客户端
        initClient();
    }
    private void initClient() {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Initializing Global Transaction Clients ... ");
        }
        if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
            throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
        }
        //初始化TM(事务管理者)
        TMClient.init(applicationId, txServiceGroup);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }
        //初始化RM(资源管理者)
        RMClient.init(applicationId, txServiceGroup);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }

        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Global Transaction Clients are initialized. ");
        }
        //注册关闭钩子
        registerSpringShutdownHook();

    }

初始化TM(事务管理者)

    public static void init(String applicationId, String transactionServiceGroup) {
         //创建TM
        TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
        //初始化并启动TM
        tmNettyRemotingClient.init();
    }

创建TM

    public static TmNettyRemotingClient getInstance(String applicationId, String transactionServiceGroup, String accessKey, String secretKey) {
        TmNettyRemotingClient tmRpcClient = getInstance();
        //应用id
        tmRpcClient.setApplicationId(applicationId);
        //事务分组
        tmRpcClient.setTransactionServiceGroup(transactionServiceGroup);
        tmRpcClient.setAccessKey(accessKey);
        tmRpcClient.setSecretKey(secretKey);
        return tmRpcClient;
    }
    public static TmNettyRemotingClient getInstance() {
        if (instance == null) {
            synchronized (TmNettyRemotingClient.class) {
                if (instance == null) {
                    NettyClientConfig nettyClientConfig = new NettyClientConfig();
                    final ThreadPoolExecutor messageExecutor = new ThreadPoolExecutor(
                            nettyClientConfig.getClientWorkerThreads(), nettyClientConfig.getClientWorkerThreads(),
                            KEEP_ALIVE_TIME, TimeUnit.SECONDS,
                            new LinkedBlockingQueue<>(MAX_QUEUE_SIZE),
                            new NamedThreadFactory(nettyClientConfig.getTmDispatchThreadPrefix(),
                                    nettyClientConfig.getClientWorkerThreads()),
                            RejectedPolicies.runsOldestTaskPolicy());
                    instance = new TmNettyRemotingClient(nettyClientConfig, null, messageExecutor);
                }
            }
        }
        return instance;
    }

初始化TM

    @Override
    public void init() {
        // registry processor
        registerProcessor();
        if (initialized.compareAndSet(false, true)) {
            super.init();
        }
    }
    @Override
    public void init() {
        //启动一个延时60s 每隔10秒对Seata Server 发起一个重新连接请求
        timerExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                //和 Seata Server重新连接
                clientChannelManager.reconnect(getTransactionServiceGroup());
            }
        }, SCHEDULE_DELAY_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.MILLISECONDS);
        //是否启动客户端批量发送请求
        if (NettyClientConfig.isEnableClientBatchSendRequest()) {
            mergeSendExecutorService = new ThreadPoolExecutor(MAX_MERGE_SEND_THREAD,
                MAX_MERGE_SEND_THREAD,
                KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(),
                new NamedThreadFactory(getThreadPrefix(), MAX_MERGE_SEND_THREAD));
            mergeSendExecutorService.submit(new MergedSendRunnable());
        }
        //每隔3秒执行一次定时任务 做超时检查
        super.init();
        //启动Netty客户端组件
        clientBootstrap.start();
    }

初始化RM(资源管理者)

    public static void init(String applicationId, String transactionServiceGroup) {
        //创建RM
        RmNettyRemotingClient rmNettyRemotingClient = RmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
        rmNettyRemotingClient.setResourceManager(DefaultResourceManager.get());
        rmNettyRemotingClient.setTransactionMessageHandler(DefaultRMHandler.get());
        //初始化并启动RM
        rmNettyRemotingClient.init();
    }

初始化数据库连接

    //Seata数据源代理
    @Bean(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)
    @ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = {"enableAutoDataSourceProxy", "enable-auto-data-source-proxy"}, havingValue = "true", matchIfMissing = true)
    @ConditionalOnMissingBean(SeataAutoDataSourceProxyCreator.class)
    public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) {
        return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(),seataProperties.getExcludesForAutoProxying());
    }

SeataAutoDataSourceProxyCreator

 private final Advisor advisor = new DefaultIntroductionAdvisor(new SeataAutoDataSourceProxyAdvice());
     @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Auto proxy of [{}]", beanName);
        }
        //这里会构建一个SeataAutoDataSourceProxyAdvice的代理类 然后当去进行增强的时候会走到SeataAutoDataSourceProxyAdvice的invoke方法
        return new Object[]{advisor};
    }

SeataAutoDataSourceProxyAdvice

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //添加数据源
        DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) invocation.getThis());
        Method method = invocation.getMethod();
        Object[] args = invocation.getArguments();
        Method m = BeanUtils.findDeclaredMethod(DataSourceProxy.class, method.getName(), method.getParameterTypes());
        if (m != null) {
            return m.invoke(dataSourceProxy, args);
        } else {
            return invocation.proceed();
        }
    }

这里会去做添加数据源信息 因为这里new了一个 DataSourceProxy 然后会到 DataSourceProxy的构造方法进行数据源的初始化

    public DataSourceProxy putDataSource(DataSource dataSource) {
        return this.dataSourceProxyMap.computeIfAbsent(dataSource, DataSourceProxy::new);
    }

DataSourceProxy

    public DataSourceProxy(DataSource targetDataSource, String resourceGroupId) {
        super(targetDataSource);
        init(targetDataSource, resourceGroupId);
    }
    private void init(DataSource dataSource, String resourceGroupId) {
        this.resourceGroupId = resourceGroupId;
        //读取jdbc 等信息
        try (Connection connection = dataSource.getConnection()) {
            //jdbcURL信息
            jdbcUrl = connection.getMetaData().getURL();
            //db类型
            dbType = JdbcUtils.getDbType(jdbcUrl);
            if (JdbcConstants.ORACLE.equals(dbType)) {
                userName = connection.getMetaData().getUserName();
            }
        } catch (SQLException e) {
            throw new IllegalStateException("can not init dataSource", e);
        }
        //将资源注册到TC
        DefaultResourceManager.get().registerResource(this);
        if (ENABLE_TABLE_META_CHECKER_ENABLE) {
            tableMetaExcutor.scheduleAtFixedRate(() -> {
                try (Connection connection = dataSource.getConnection()) {
                    TableMetaCacheFactory.getTableMetaCache(DataSourceProxy.this.getDbType())
                        .refresh(connection, DataSourceProxy.this.getResourceId());
                } catch (Exception ignore) {
                }
            }, 0, TABLE_META_CHECKER_INTERVAL, TimeUnit.MILLISECONDS);
        }
    }

RM和TM的注册

在这里插入图片描述
可以看到在DataSource初始化完成之后调用 DefaultResourceManager.get().registerResource(this);会去做一个资源的注册 这里最后会走到
RmNettyRemotingClient和TmNettyRemotingClient的onRegisterMsgSuccess方法中进行RM和TM的注册

RM注册

    @Override
    public void onRegisterMsgSuccess(String serverAddress, Channel channel, Object response,
                                     AbstractMessage requestMessage) {
        RegisterRMRequest registerRMRequest = (RegisterRMRequest)requestMessage;
        RegisterRMResponse registerRMResponse = (RegisterRMResponse)response;
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("register RM success. client version:{}, server version:{},channel:{}", registerRMRequest.getVersion(), registerRMResponse.getVersion(), channel);
        }
        getClientChannelManager().registerChannel(serverAddress, channel);
        String dbKey = getMergedResourceKeys();
        if (registerRMRequest.getResourceIds() != null) {
            if (!registerRMRequest.getResourceIds().equals(dbKey)) {
                sendRegisterMessage(serverAddress, channel, dbKey);
            }
        }

    }

TM注册

    @Override
    public void onRegisterMsgSuccess(String serverAddress, Channel channel, Object response,
                                     AbstractMessage requestMessage) {
        RegisterTMRequest registerTMRequest = (RegisterTMRequest)requestMessage;
        RegisterTMResponse registerTMResponse = (RegisterTMResponse)response;
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("register TM success. client version:{}, server version:{},channel:{}", registerTMRequest.getVersion(), registerTMResponse.getVersion(), channel);
        }
        getClientChannelManager().registerChannel(serverAddress, channel);
    }

总结

1.客户端启动的时候会通过SpringBoot的自动装配机制加载配置类
2.设置RestTemplate的拦截器用来拦截所有mvc请求 和Feign的拦截器拦截Feign请求设置全局事务ID
3.初始化TM(事务管理者) RM(资源管理者) 建立Netty客户端的通道并启动
4.初始化数据源信息
5.注册TM 和RM到SeataServer

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

塑料检查井产品设计合理、座盖联合周密,为安装维护带来方便

塑料检查井作为一种新型的检查井材料&#xff0c;其产品设计合理、座盖联合周密&#xff0c;为安装维护带来了极大的方便。 首先&#xff0c;塑料检查井的设计合理&#xff0c;能够满足各种工程需求。其结构紧凑、尺寸精确&#xff0c;可以方便地与管道和其他设施进行连接和安…

UE5 C++(二)— 游戏架构介绍

架构关系如下&#xff1a; 这里只简单描述下&#xff0c;具体的查看官方文档 AGameMode: AGameMode 是 AGameModeBase 的子类&#xff0c;拥有一些额外的功能支持多人游戏和旧行为。 所有新建项目默认使用 AGameModeBase。 如果需要此额外行为&#xff0c;可切换到从 AGameM…

系统安全-WAF入侵防御系统测评指标

WAF&#xff08;Web Application Firewall&#xff09;是一种部署在web应用程序前面的安全系统&#xff0c;其作用是在用户请求到达web服务器之前对用户请求进行扫描和过滤&#xff0c;分析并校验每个用户请求的网络包&#xff0c;确保每个用户请求有效且安全&#xff0c;对无效…

浅入浅出理解MySQL和InnoDB

目录 数据库的定义 数据库和实例 MySQL 的架构 数据的存储 如何存储表 如何存储记录 数据页结构 索引 索引的数据结构 聚集索引和辅助索引 索引的设计 锁 并发控制机制 锁的种类 锁的粒度 锁的算法 死锁的发生 事务与隔离级别 几种隔离级别 脏读 不可重复读 幻读 总结 Innodb与…

压缩包文件暴力破解 -Server2005(解析)

任务五十一: 压缩包文件暴力破解 任务环境说明:Server2005 1. 通过本地PC中渗透测试平台Kali使用Nmap扫描目标靶机服务版本信息,将 Telnet 版本信息字符串 作为 Flag 提交; flag:Microsoft Windows XP telnetd 2. 通过本地PC中渗透测试平台Kali对服务器场景Windows进行渗透测…

YOLOv5原创改进:一种新颖的跨通道交互的高效率通道注意力EMCA,ECA注意力改进版

💡💡💡本文原创自研创新改进:基于ECA注意力,提出了一种新颖的EMCA注意力(跨通道交互的高效率通道注意力),保持高效轻量级的同时,提升多尺度提取能力 强烈推荐,适合直接使用,paper创新级别 💡💡💡 在多个数据集验证涨点,尤其对存在多个尺度的数据集涨点明…

Axure动态面板的使用以及示例分享

目录 一. 什么是动态面板 二. 动态面板教程——以轮播图为例 2.1 创建动态面板 2.2 动态面板自适应大小 2.3 重复状态&#xff0c;将图片导入 2.4 添加交互事件——图片切换 2.5 效果展示 三. 多方式登录示例展示 四. 后台主界面左侧菜单栏示例展示 一. 什么是动态面板…

用Python快速从深层嵌套 JSON 中找到特定的 Value

有时候&#xff0c;我们拿到一个JSON数据的时候&#xff0c;会难以看出其逻辑层次结构。 这时候就需要我们进行代码解析了。 代码&#xff1a; import jsondef find_json_value(data_json, value, path""):if isinstance(data_json, dict):for k, v in data_json.…

Selenium安装WebDriver:ChromeDriver与谷歌浏览器版本快速匹配_最新版120

最近在使用通过selenium操作Chrome浏览器时&#xff0c;安装中遇到了Chrome版本与浏览器驱动不匹配的的问题&#xff0c;在此记录安装下过程&#xff0c;如何快速找到与谷歌浏览器相匹配的ChromeDriver驱动版本。 1. 确定Chrome版本 我们首先确定自己的Chrome版本 Chrome设置…

数据结构奇妙旅程之栈和队列

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

想做游戏开发,我应该会点啥?

在知乎上&#xff0c;经常能看到类似“如何入门游戏开发”这样的问题&#xff0c;这篇文章&#xff0c;我试着概括性的对游戏开发所需要的技能做一个总结&#xff0c;希望大家对游戏开发能有一个基本的认识~ 游戏开发基础要求高么&#xff1f; 和其他程序猿一样&#xff0c;要…

synchronized关键字的使用和原理

synchronized关键字的使用和原理 synchronized&#xff1a;对象锁&#xff0c;保证了临界区内代码的原子性&#xff0c;采用互斥的方式让同一时刻至多只有一个线程能持有对象锁&#xff0c;其它线程获取这个对象锁时会阻塞&#xff0c;保证拥有锁的线程可以安全的执行临界区内…

基于C/C++的rapidxml加载xml大文件 - 上部分翻译

RAPIDXML手册 版本 1.13 版权所有 &#xff08;C&#xff09; 2006&#xff0c; 2009 Marcin Kalicinski有关许可证信息&#xff0c;请参阅随附的文件许可证 .txt。 目录 1. 什么是 RapidXml&#xff1f; 1.1 依赖性和兼容性1.2 字符类型和编码1.3 错误处理1.4 内存分配1.5 …

纽扣电池是什么

纽扣电池 电工电气百科 文章目录 纽扣电池前言一、纽扣电池是什么二、纽扣电池的类别三、纽扣电池的作用原理总结前言 纽扣电池具有易于更换的特点,这使得它们成为许多便携设备的理想电源选择。但是,由于它们较小且外壳易于打开,所以家中有婴幼儿的家庭应特别注意将其放置在…

云原生之深入解析亿级流量架构之服务限流思路与方法

一、限流思路 ① 熔断 系统在设计之初就把熔断措施考虑进去&#xff0c;当系统出现问题时&#xff0c;如果短时间内无法修复&#xff0c;系统要自动做出判断&#xff0c;开启熔断开关&#xff0c;拒绝流量访问&#xff0c;避免大流量对后端的过载请求。系统也应该能够动态监测…

Pycharm第三方库导入失败避坑!

最近遇到了明明安装了 python 第三方库&#xff0c;但是在 pycharm 当中却导入不成功的问题。 使用Pycharm手动安装三方库和自动安装三方库都失败&#xff0c;以及Pycharm终端使用pip命令安装也未解决。网上找各种方法尝试都没成功&#xff0c;原来是一不小心就跳进了虚拟环境…

代码随想录算法训练营 | day52 动态规划 300.最长递增子序列,674.最长连续递增子序列,718.最长重复子数组

刷题 300.最长递增子序列 题目链接 | 文章讲解 | 视频讲解 题目&#xff1a;给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。…

彻底告别pip安装Python第三方库网速慢的问题

方式一、 如果使只下载没几个库 直接: pip install 库名 -i http://mirrors.aliyun.com/pypi/simple/ 方式二、 如果要一直下载库&#xff0c;就可以通过配置pip.ini文件来解决&#xff0c;具体步骤如下所示&#xff1a; 1、打开文件资源管理器 2、搜索%APPDATE% 3、创建一…

LeetCode 2415. 反转二叉树的奇数层:深度优先搜索(DFS)

【LetMeFly】2415.反转二叉树的奇数层&#xff1a;深度优先搜索(DFS) 力扣题目链接&#xff1a;https://leetcode.cn/problems/reverse-odd-levels-of-binary-tree/ 给你一棵 完美 二叉树的根节点 root &#xff0c;请你反转这棵树中每个 奇数 层的节点值。 例如&#xff0c…

CSS盒子的浮动与网页布局(重点,有电影页面案例)

浮动适用于那种盒子的并列布局 CSS 提供了三种传统布局方式(简单说,就是盒子如何进行排列顺序)&#xff1a;  普通流&#xff08;标准流&#xff09;  浮动  定位 标准流&#xff08;普通流/文档流&#xff09; 所谓的标准流: 就是标签按照规定好默认方式排列. 1. 块级…