升级Spring Cloud Stream动态发送消息问题解析

Java之 Spring Cloud 微服务的 Spring Cloud Stream(第四个阶段)【一】【SpringBoot项目实现商品服务器端调用】 在实际的企业开发中,消息中间件是至关重要的组件之一。消息中间件主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。不同的中间件其实现方式,内部结构是不一样的。 阅读详情

升级Spring Cloud Stream动态发送消息问题解析

Spring Cloud Stream从2.0.1升级到3.1.3后,事件服务动态刷新发送消息、消费消息都出现了异常。由于动态发送、消费消息功能,是在源码的基础上做了一些封装和修改,新版本Spring Cloud Stream源码发送了变更导致之前的方式失效或出现异常

Unable to register MBean [bean ‘halt-target-test-20’]异常

复现步骤

此异常复现步骤如下

  • 找到一个依赖事件服务客户端,并且能正常发送消息的服务,并启动服务,发送一条消息

  • 修改事件参数,触发动态刷新发送通道操作

  • 再次发送消息会报出Unable to register MBean [bean 'halt-target-test-20']异常

问题分析

根据执行栈分析可能出现问题点

将断点打在抛出异常处查看整个执行栈
在这里插入图片描述
我们可以看到红框框着的部分是我们自己的业务代码。点击resolveDestination部分我们可以看到如下内容

public synchronized MessageChannel resolveDestination(String channelName) {
    BindingServiceProperties bindingServiceProperties = this.bindingService.getBindingServiceProperties();
    String[] dynamicDestinations = bindingServiceProperties.getDynamicDestinations();
    boolean dynamicAllowed = ObjectUtils.isEmpty(dynamicDestinations) || ObjectUtils.containsElement(dynamicDestinations, channelName);

    MessageChannel channel;
    try {
        channel = super.resolveDestination(channelName);
    } catch (DestinationResolutionException var9) {
        if (!dynamicAllowed) {
            throw var9;
        }

        channel = (MessageChannel)this.bindingTargetFactory.createOutput(channelName);
        ProducerProperties producerProperties = bindingServiceProperties.getProducerProperties(channelName);
        if (this.newBindingCallback != null) {
            Object extendedProducerProperties = this.bindingService.getExtendedProducerProperties(channel, channelName);
            this.newBindingCallback.configure(channelName, channel, producerProperties, extendedProducerProperties);
        }

        bindingServiceProperties.updateProducerProperties(channelName, producerProperties);
        this.beanFactory.registerSingleton(channelName, channel);
        channel = (MessageChannel)this.beanFactory.initializeBean(channel, channelName);
        Binding<MessageChannel> binding = this.bindingService.bindProducer(channel, channelName);
        this.dynamicDestinationsBindable.addOutputBinding(channelName, binding);
    }

    return channel;
}

此方法是Spring Cloud Steam中动态获取发送通道的方法,这个方法我们在之前有做过分析,此方法会从上下文中获取已经存在的bean并返回。我们分析整个调用栈,后面在获取bean没有成功之后又去调用了创建bean的逻辑,从这处可以看出我们之前修改配置参数去销毁发送通道并没有彻底,因为正确的逻辑是,我们在页面上修改发送参数后,发送通道销毁,下次发送消息时在上述channel = super.resolveDestination(channelName);处应该是找不到bean直接进入到异常处理进行新的发送通道的创建。但我们看到的实际情况是在调用super.resolveDestination(channelName)方法里面,没有找到bean但却继续去创建bean去了,因此第一个需要研究的点便是,为什么会去执行bean的创建操作,而不是直接返回或抛出异常,更近一步我们需要知道我们销毁通道的时候少销毁了什么,导致没有按预期执行。之后我们继续分析后面的执行方法,在创建bean之后执行了IntegrationMBeanExporter的postProcessAfterInitialization方法

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (this.singletonsInstantiated) {
        try {
            if (bean instanceof MessageChannel) {
                MessageChannel monitor = (MessageChannel) extractTarget(bean);
                if (monitor instanceof IntegrationManagement) {
                    this.channels.put(beanName, (IntegrationManagement) monitor);
                    registerChannel((IntegrationManagement) monitor);
                    this.runtimeBeans.add(bean);
                }
            }
            else if (bean instanceof MessageProducer && bean instanceof Lifecycle) {
                registerProducer((MessageProducer) bean);
                this.runtimeBeans.add(bean);
            }
            else if (bean instanceof AbstractEndpoint) {
                postProcessAbstractEndpoint(bean);
            }
        }
        catch (Exception e) {
            logger.error("Could not register an MBean for: " + beanName, e);
        }
    }
    return bean;
}

此方法是bean初始化完成之后执行的方法,结合类名称IntegrationMBeanExporter,可以大概猜出这里是在处理监控Bean相关的操作,根据后面执行的方法名可以大概猜测出是在执行bean的监控注册,根据抛出异常的描述可以看出是注册的时候发生了异常,因此第二个要研究清楚的问题是为什么注册MBean会报错

研究问题点搞清楚问题具体原因

上面是根据执行栈猜测出的两个问题点,第一个点销毁通道的时候少销毁了什么,导致没有按预期执行;第二个点为什么注册MBean会报错。此处代码已经执行到异常处,我们要重新执行registerBeanNameOrInstance方法看具体原因。选择执行栈中的registerBeanNameOrInstance,点击"Drop Frame"重新进入registerBeanNameOrInstance

protected ObjectName registerBeanNameOrInstance(Object mapValue, String beanKey) throws MBeanExportException {
    try {
        if (mapValue instanceof String) {
            // Bean name pointing to a potentially lazy-init bean in the factory.
            if (this.beanFactory == null) {
                throw new MBeanExportException("Cannot resolve bean names if not running in a BeanFactory");
            }
            String beanName = (String) mapValue;
            if (isBeanDefinitionLazyInit(this.beanFactory, beanName)) {
                ObjectName objectName = registerLazyInit(beanName, beanKey);
                replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName);
                return objectName;
            }
            else {
                Object bean = this.beanFactory.getBean(beanName);
                ObjectName objectName = registerBeanInstance(bean, beanKey);
                replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName);
                return objectName;
            }
        }
        else {
            // Plain bean instance -> register it directly.
            if (this.beanFactory != null) {
                Map<String, ?> beansOfSameType =
                        this.beanFactory.getBeansOfType(mapValue.getClass(), false, this.allowEagerInit);
                for (Map.Entry<String, ?> entry : beansOfSameType.entrySet()) {
                    if (entry.getValue() == mapValue) {
                        String beanName = entry.getKey();
                        ObjectName objectName = registerBeanInstance(mapValue, beanKey);
                        replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName);
                        return objectName;
                    }
                }
            }
            return registerBeanInstance(mapValue, beanKey);
        }
    }
    catch (Throwable ex) {
        throw new UnableToRegisterMBeanException(
                "Unable to register MBean [" + mapValue + "] with key '" + beanKey + "'", ex);
    }
}

根据调试可以发现代码具体报错地方是调用registerBeanInstance方法,调用此方法的代码块主要做的事情如下

  • 判断beanFactory属性是否为空

  • 不为空获取需要注入bean类型的bean集合

  • 如果两个bean相同则进入registerBeanInstance方法

由上述执行流程可知我们在修改发送参数后,销毁发送通道bean,但并没有销毁监控对应的MBean。继续跟踪代码我们可以发现报错是在doRegister方法中抛出

protected void doRegister(Object mbean, ObjectName objectName) throws JMException {
    Assert.state(this.server != null, "No MBeanServer set");
    ObjectName actualObjectName;

    synchronized (this.registeredBeans) {
        ObjectInstance registeredBean = null;
        try {
            registeredBean = this.server.registerMBean(mbean, objectName);
        }
        catch (InstanceAlreadyExistsException ex) {
            if (this.registrationPolicy == RegistrationPolicy.IGNORE_EXISTING) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignoring existing MBean at [" + objectName + "]");
                }
            }
            else if (this.registrationPolicy == RegistrationPolicy.REPLACE_EXISTING) {
                try {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Replacing existing MBean at [" + objectName + "]");
                    }
                    this.server.unregisterMBean(objectName);
                    registeredBean = this.server.registerMBean(mbean, objectName);
                }
                catch (InstanceNotFoundException ex2) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Unable to replace existing MBean at [" + objectName + "]", ex2);
                    }
                    throw ex;
                }
            }
            else {
                throw ex;
            }
        }

        // Track registration and notify listeners.
        actualObjectName = (registeredBean != null ? registeredBean.getObjectName() : null);
        if (actualObjectName == null) {
            actualObjectName = objectName;
        }
        this.registeredBeans.add(actualObjectName);
    }

    onRegister(actualObjectName, mbean);
}

此方法完成如下事情

  • 调用sever属性的registerMBean方法完成MBean的注册

  • 异常处理,检查注册策略,如果忽略已经注册则打印日志

  • 异常处理,如果是替换现有的,则先移除注册的bean,再从新注册

  • 异常处理,如果两种策略都不是则抛出异常

由上述方法以及调试结果可知,默认是直接抛出异常。我们可以配置策略使其不抛出异常,但策略改动可能会影响其他客户端,并且也会打印日志。也不是最优解决方案。但我们注意到server属性的unregisterMBean方法完成了MBean的移除注册,所以我们完全可以在我们修该发送参数,销毁发送通道时将对应MBean也删除。我们需要研究server属性的unregisterMBean如何可以在我们移除销毁通道时获取并调用

我们尝试找一下其他调用server属性unregisterMBean方法的地方,我们发现就在这个类中有一个doUnregister方法在调用

protected void doUnregister(ObjectName objectName) {
    Assert.state(this.server != null, "No MBeanServer set");
    boolean actuallyUnregistered = false;

    synchronized (this.registeredBeans) {
        if (this.registeredBeans.remove(objectName)) {
            try {
                // MBean might already have been unregistered by an external process
                if (this.server.isRegistered(objectName)) {
                    this.server.unregisterMBean(objectName);
                    actuallyUnregistered = true;
                }
                else {
                    if (logger.isInfoEnabled()) {
                        logger.info("Could not unregister MBean [" + objectName + "] as said MBean " +
                                "is not registered (perhaps already unregistered by an external process)");
                    }
                }
            }
            catch (JMException ex) {
                if (logger.isInfoEnabled()) {
                    logger.info("Could not unregister MBean [" + objectName + "]", ex);
                }
            }
        }
    }

    if (actuallyUnregistered) {
        onUnregister(objectName);
    }
}

我们发现其实这个方法主要就是在完成移除MBean对象的注册,此方法是protected,继续查看调用doUnregister方法的地方,我们发现postProcessBeforeDestruction方法在调用doUnregister,并且这个方法是公共的,传入参数bean及bean的名称。包含这个方法的bean我们可以直接注入使用,传入我们发送通道的bean及名称此方法将完成bean对应监听资源的释放

解决方案

由上面问题分析可知报错出现的原因是在销毁发送通道是没有销毁监听MBean导致的,我们需要注入IntegrationMBeanExporter类型的bean,并调用postProcessBeforeDestruction完成销毁发送通道的监控资源的释放

/**
 * 添加解绑定接口
 *
 * @param channelName
 */
public void unbindOutput(String channelName) {
    if (this.dynamicDestinationsBindable.getOutputs().contains(channelName)) {
        this.dynamicDestinationsBindable.unbindOutput(channelName);
        try {
            if (Objects.nonNull(integrationMBeanExporter)) {
                integrationMBeanExporter.postProcessBeforeDestruction(super.resolveDestination(channelName), channelName);
            }
        } catch (Exception e) {
            LOGGER.info("JMX Bean destroy fail");
        }
        ((DefaultListableBeanFactory) this.beanFactory).destroySingleton(channelName);
        ((DefaultListableBeanFactory) this.beanFactory).removeBeanDefinition(channelName);
    }
}

修改发送参数之后没有生效

问题分析

根据上面分析我们目前解决了一个问题点,还有另一个问题没有解决。销毁通道的时候少销毁了什么,导致没有按预期执行。根据上面的执行栈我们找到doGetBean方法

protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {

    String beanName = transformedBeanName(name);
    Object beanInstance;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Check if bean definition exists in this factory.
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                        nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                // Delegation to parent with explicit args.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }

        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
                .tag("beanName", name);
        try {
            if (requiredType != null) {
                beanCreation.tag("beanType", requiredType::toString);
            }
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // Guarantee initialization of beans that the current bean depends on.
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    registerDependentBean(dep, beanName);
                    try {
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // Create bean instance.
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        // Explicitly remove instance from singleton cache: It might have been put there
                        // eagerly by the creation process, to allow for circular reference resolution.
                        // Also remove any beans that received a temporary reference to the bean.
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                String scopeName = mbd.getScope();
                if (!StringUtils.hasLength(scopeName)) {
                    throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
                }
                Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new ScopeNotActiveException(beanName, scopeName, ex);
                }
            }
        }
        catch (BeansException ex) {
            beanCreation.tag("exception", ex.getClass().toString());
            beanCreation.tag("message", String.valueOf(ex.getMessage()));
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
        finally {
            beanCreation.end();
        }
    }

    return adaptBeanInstance(name, beanInstance, requiredType);
}

此方法是Spring IOC容器相关源码,如果对IOC容器有过研究应该对这个方法所做的事情会比较了解。这也是因为升级了Spring Cloud的版本导致此方法的实现相较于2.0.1的版本有了变化。我们来分析一下这个方法的代码

  • 首先获取bean的转换后的名称,比如别名处理

  • 调用getSingleton方法获取单例对象

  • 判断获取的单例对象是否为空

  • 不为空,通过getObjectForBeanInstance获取bean实例的对象

  • 为空,判断父bean不为空,并且当前bean的定义中不包含该bean,则尝试从父bean中获取需要的bean

  • 如果当前bean的定义中包含该bean,则获取bean的定义,并且根据bean的定义来创建bean

  • 后续bean的处理

上面简单分析了一下这个方法都在干什么事情,我们根据执行栈,可以看出,我们发送通道的bean已经被销毁,但我们bean的定义并没有被移除,所以通过bean的定义又去创建了上一个发送通道的对象,导致我们修改参数之后的发送通道并没有去创建。因此没有生效。所以我们需要将bean的定义也一起移除才行。通过上面分析我们知道containsBeanDefinition方法就在判断是否包含bean的定义

@Override
public boolean containsBeanDefinition(String beanName) {
    Assert.notNull(beanName, "Bean name must not be null");
    return this.beanDefinitionMap.containsKey(beanName);
}

该方法直接判断beanDefinitionMap属性中是否有名称为给定bean名称的定义对象。所以我们需要在销毁发送通道的时候将beanDefinitionMap中的定义对象也一起移除掉。我们发现该类也就是DefaultListableBeanFactory中有一个removeBeanDefinition的方法

public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
    Assert.hasText(beanName, "'beanName' must not be empty");

    BeanDefinition bd = this.beanDefinitionMap.remove(beanName);
    if (bd == null) {
        if (logger.isTraceEnabled()) {
            logger.trace("No bean named '" + beanName + "' found in " + this);
        }
        throw new NoSuchBeanDefinitionException(beanName);
    }

    if (hasBeanCreationStarted()) {
        // Cannot modify startup-time collection elements anymore (for stable iteration)
        synchronized (this.beanDefinitionMap) {
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames);
            updatedDefinitions.remove(beanName);
            this.beanDefinitionNames = updatedDefinitions;
        }
    }
    else {
        // Still in startup registration phase
        this.beanDefinitionNames.remove(beanName);
    }
    this.frozenBeanDefinitionNames = null;

    resetBeanDefinition(beanName);
}

此方法完成了对bean定义的移除及后续操作。那我们要如何调用此方法呢?根据之前的分析文档我们知道,BinderAwareChannelResolver的beanFactory属性就是DefaultListableBeanFactory的实例,而我们创建的发送通道的bean也是注册在beanFactory属性中的。之前我们调用destroySingleton方法完成了bean的销毁,但没有调用removeBeanDefinition完成bean定义的销毁

解决方案

根据上面分析我们可以知道解决方案就是在销毁发送bean的时候需要同时销毁其定义

/**
 * 添加解绑定接口
 *
 * @param channelName
 */
public void unbindOutput(String channelName) {
    if (this.dynamicDestinationsBindable.getOutputs().contains(channelName)) {
        this.dynamicDestinationsBindable.unbindOutput(channelName);
        try {
            if (Objects.nonNull(integrationMBeanExporter)) {
                integrationMBeanExporter.postProcessBeforeDestruction(super.resolveDestination(channelName), channelName);
            }
        } catch (Exception e) {
            LOGGER.info("JMX Bean destroy fail");
        }
        ((DefaultListableBeanFactory) this.beanFactory).destroySingleton(channelName);
        ((DefaultListableBeanFactory) this.beanFactory).removeBeanDefinition(channelName);
    }
}

结语

一切尽在源码中

**Spring Cloud Stream Horsham.SR2** 是 Spring Cloud Stream 的一个维护版本 **Spring Cloud Stream Horsham.SR2** 是 Spring Cloud Stream 的一个维护版本,属于 **Horsham** 发布系列(即 3.0.x 版本线),发布于 **2020 年初**,对应 **Spring Boot 2.2.x ~ 2.3.x** 环境。它是 Spring Cloud Stream 进入函数式编程模型后的首个稳定主版本系列,标志着从传统的注解驱动模式向现代响应式、函数式风格的全面转型。 阅读详情

相关推荐

MyEclipse 复制项目后运行报错,按以下方式改项目名就可以了

MyEclipse 复制项目后运行报错,按以下方式改项目名就可以了

nacos开启鉴权后,服务启动报错(解决方案)

【代码】nacos开启鉴权后,服务启动报错(解决方案)

3479

深入解析:如何在Visual Studio中高效编译libredwg与libdxfrw

本文详细介绍了在Visual Studio中高效编译libredwg与libdxfrw的完整流程,包括环境配置、源码获取、CMake技巧及常见错误解决方案。针对CAD开发者,提供了从基础编译到项目集成的实用指南,帮助快速处理DWG/DXF文件,提升开发效率。

weixin_29215391的博客 233

springboot启动 fastDFS启动报错 mbeanExporter: defined by method 'mbeanExporter' in class path

解决 c 参考:https://blog.youkuaiyun.com/luckyboy198961/article/details/79618693

C18298182575的博客 852

Ubuntu22.04中通过conda快速部署PyTorch开发环境(CPU/GPU双版本实战)

本文详细介绍了在Ubuntu22.04系统中,如何利用Conda高效部署PyTorch开发环境。文章分别提供了CPU与GPU双版本的实战教程,涵盖环境创建、镜像加速、版本匹配及深度验证等关键步骤,旨在帮助开发者快速搭建稳定、兼容的深度学习环境,从而专注于模型开发。

rose2的博客 559

三菱PLC通信库大比拼:手把手教你用C#实现最高效的McProtocol数据读写

本文深入比较了三菱PLC通信库的性能,重点介绍了如何使用C#高效实现McProtocol数据读写。通过实测数据对比HslCommunication、McProtocol-next和自定义方案的优劣,提供优化技巧和代码示例,帮助开发者在工业自动化场景中提升通信效率。

m0n1o2p的博客 259

MBean 错误

spring.jmx.default-domain==http://115.159.58.87:8033

专注Java(全栈)应用开发,求知若渴,虚心若愚,talk is cheap, show me the code. 306

Jetson nano/nx使用麒麟arm版向日葵实现远程控制

Jetson nano/nx使用麒麟arm版向日葵实现远程控制1、安装向日葵软件2、运行Sunlogin Client3、向日葵闪退问题4、重启系统5、重启后黑屏问题 特别提醒: 本博客的第3步切换桌面管理环境到lightdm,存在重启后开机黑屏的风险,我在自己的nano/nx上都没有问题,但帮一个同学的nx配置时,切换到lightdm,重启开机却出现了黑屏,所以执行第3步的话要有心理准备,黑屏解决方法见第5步,特此提醒。

胡乱学点东西 2万+

同一tomcat部署两个项目报unregister mbean error com.alibaba.druid:type=DruidDataSourceStat的错

一、情况: 同一tomcat下部署多个项目,项目报错 unregister mbean error javax.management.InstanceNotFoundException: com.alibaba.druid:type=DruidDataSourceStat 查了一下应该是数据库连接池的错误,“一个tomcat服务器下不能直接部署多个druid作为数据连接池的项目”。 二、...

kkkk0820的博客 3411

SAP交货单过账避坑指南:序列号处理、Buffer清理与BAPI提交回滚的那些事儿

本文深入解析SAP交货单过账中的序列号处理、Buffer清理与BAPI提交回滚等关键问题,提供WS_DELIVERY_UPDATE和BAPI_OUTB_DELIVERY_CONFIRM_DEC的实战优化方案。通过详细的技术分析和最佳实践,帮助开发者避免常见陷阱,提升交货单处理的稳定性和效率。

weixin_31155097的博客 432

PyTorch深度学习实战(1)——神经网络与模型训练过程详解

在本节中,我们将了解传统机器学习与人工神经网络间的差异,并了解如何在实现前向传播之前连接网络的各个层,以计算与网络当前权重对应的损失值;实现反向传播以优化权重达到最小化损失值的目标。并将实现网络的所有关键组成——前向传播、激活函数、损失函数、链式法则和梯度下降,从零开始构建并训练了一个简单的神经网络。

盼小辉丶的博客 9万+

(十八)Spring Cloud 学习笔记之Spring Cloud Stream

Spring Cloud Stream1 概述2 核心概念3 入门案例3.1 准备工作3.2 消息生产者3.3 消息消费者4 自定义消息通道5 消息分组6 消息分区 1 概述 Spring Cloud Stream由一个中间件中立的核组成。应用通过Spring Cloud Stream插入的input(相当于消费者consumer,它是从队列中接收消息的)和output(相当于生产者produce...

淮右布衣的博客 1105

Pyinstaller打包为可执行文件编码错误的问题

Pyinstaller打包为可执行文件编码错误的问题 写好的python程序在vscode上运行没有任何问题,将程序利用pyinstaller打包后,运行程序发生了错误。 错误代码 UnicodeDecodeError: ‘utf-8' codec can't decode byte 0xce in position 20-21:invalid cotinuation byte 网上找到的解决方案是: 1.在命令行中输入 chcp 65001 2.按正常方式进行封装 pyinstaller

xuefeng_he的博客 1560

使用Spring Cloud Stream对RabbitMq进行消息发送

大家如果觉得我写的好,可以关注这个项目,之后我的学习过程中都会把学习笔记放入这个项目中。 https://github.com/IndustriousSnail/learning-notes 使用Spring Cloud Stream对RabbitMq进行消息发送 目录 一、Spring Cloud Stream简介 二、Stream 的使用 2.1 简单使用案例 2.2 发送对象消息 2.3...

iioSnail的博客 4986

华为OD机试 - 第k个排列 - 全排列递归(Python/JS/C/C++ 2025 C卷 100分)

给定参数n,从1到n会有n个整数:1,2,3,…,n,这n个数字共有n!按大小顺序升序列出所有排列的情况,并一一标记当n=3时,所有排列如下“123”,“132,“213”,“231”,“312”,“321”输入两行,第一行为n,第二行为k,给定n的范围是[1,9],给定k的范围是[1,n!排列为“12”和“21”,第2个是“21”。给定n和k,返回第k个排列。输出排在第k位置的数字。只有一个排列,直接返回。

学Java,找哪吒 989

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

消息驱动——Stream

消息驱动——Stream 解决这种分布式系统中消息传递方案最好的选择就是消息中间件 通过消息中间件所提供的松散耦合的方式——存储和转发微服务之间的异步数据 9.1 什么是消息驱动开发 异步消息中间件的消息传递模式又可以分为两种:点对点模式和“发布-订阅”模式。 ·点对点模式:该模式常用于消息生产者和消息消费者之间点到点的通信;·“发布-订阅”模式:该模式使用主题(Topic)代替点对点中的目的消费者。此时消息生产者只需要将消息发布到主题中即可,而不需要关心是谁消费该消息;而消费者如果需要消费消息,只需要订阅

青葱暖咖啡 1386

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Spring Cloud Stream

在实际的企业开发中,消息中间件是至关重要的组件之一。消息中间件主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。不同的中间件其实现方式,内部结构是不一样的。如常见的RabbitMQ和Kafka,由于这两个消息中间件的架构上的不同,像RabbitMQ有exchange,kafka有Topic,partitions分区,这些中间件的差异性导致我们实际项目...

qq_29860591的博客 824

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

SpringCloud Stream 使用

SpringCloud Stream 介绍及使用

小凯的博客 1227

Spring Cloud 系列之 Stream 消息驱动(一)

在实际开发过程中,服务与服务之间通信经常会使用到消息中间件,消息中间件解决了应用解耦、异步处理、流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。 不同中间件内部实现方式是不一样的,这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,比如项目中间件为 Kafka,如果我们要替换为 RabbitMQ,这无疑就是一个灾难性的工作,一大堆东西都要重做,因为它跟我们系统的耦合性非常高。...

哈喽沃德先生的博客 1192

Spring Cloud Alibaba 最佳实践:基于 Spring Boot 4.0 的完整微服务示例项目

使用 BOM 统一管理版本,避免冲突:</</</</</</</</</</</</</</它不是简单的"Hello World",而是真正考虑了生产环境的复杂性。从链路追踪到限流熔断,从响应式编程到消息驱动,每一个模块都经过精心设计,每一行配置都有实际意义。🎯 学习 Spring Cloud Alibaba🚀 准备升级 Spring Boot 4.0🏗️ 设计微服务架构🔍 寻找生产级参考示例那么这个项目绝对值得你 Star ⭐ 和深入研究!立即体验。

武汉红喜 348

Spring Cloud 2024 正式发布,详细解读本次更新

Spring Cloud 2024 正式发布,详细解读本次更新

bjzhang75的博客 2246

Spring Cloud Stream Horsham SR3 是 Spring Cloud Stream 框架的一个重要版本,基于 Spring Cloud Horsham RELEASE 构建

Spring Cloud Stream Horsham SR3 是 Spring Cloud Stream 框架的一个重要版本,基于 Spring Cloud Horsham RELEASE 构建,主要聚焦于稳定性增强、bug 修复以及对上下游组件的兼容性优化。以下从版本背景、核心特性、改进点、依赖组件及适用场景等方面进行详细分析:

[Blog][Domain] programb.blog.youkuaiyun.com 392

springcloud 子项目怎么导入_Spring Cloud 系列之 Stream 消息驱动(一)

在实际开发过程中,服务与服务之间通信经常会使用到消息中间件,消息中间件解决了应用解耦、异步处理、流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。不同中间件内部实现方式是不一样的,这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,比如项目中间件为 Kafka,如果我们要替换为 RabbitMQ,这无疑就是一个灾难性的工作,一大堆东西都要重做,因为它跟我们系统的耦合性非常高。这...

weixin_29010003的博客 533

【转】SpringCloud实战1-Stream消息驱动

官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架。   应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互,通过我们配置来 binding ,而 Spring Cloud Stream 的 binder 负责与消息中间件交互。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互...

xieqi5200的博客 189

《Spring Cloud免费教程》连载目录

Spring Cloud Alibaba基础教程:Nacos 生产级版本 0.8.0Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系说说我为什么看好Spring Cloud AlibabaSpring Cloud Alibaba到底坑不坑?Spring Cloud Alibaba 新版本发布:众多期待内容整合打包加入!Spring Cloud Alibaba基础教程版本升级:0.2.1->0.2.2。

2401_84048935的博客 875

Java学习笔记 - Spring Cloud Stream

Java学习笔记 - Spring Cloud Stream1、消息中间件迁移的问题2、Spring Cloud Stream概述3、核心概念4、入门案例4.1、消息生产者4.2、消息消费者4.3、启动工程5、自定义消息通道6、消息分组7、消息分区   1、消息中间件迁移的问题 在实际的企业开发中,消息中间件是至关重要的组件之一。消息中间件主要解决应用解耦、异步消息、流量削锋等问题,实现高性能、高可用、可伸缩和最终一致性架构。不同的中间件其实现方式,内部结构是不一样的。如常见的RabbitMQ和Kafka,

qq_42141141的博客 1045
上一篇: 图基本算法
下一篇: 本地连接远程K8s集群
炒栗子不加糖
博客等级 码龄10年 68粉丝 123原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值