分布式事务-TCC(Hmily)[通俗易懂]

分布式事务-TCC(Hmily)[通俗易懂]TCC是什么:TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的操作即回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若

大家好,又见面了,我是你们的朋友全栈君。

TCC是什么:
TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try、确认 Confirm、撤销Cancel。Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的 操作即回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所 有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel 操作若执行失败,TM会进行重试。
分布式事务执行成功:
在这里插入图片描述
这边try执行成功后,没有异常会直接进入Confirm确认提交阶段。

分布式执行失败:
在这里插入图片描述
这边try执行失败后,执行成功一方的资源会进行事务回滚。

TCC分为三个阶段:
1:Try 阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm 一起才能 真正构成一个完整的业务逻辑。
2. Confirm 阶段是做确认提交,Try阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用TCC则 认为 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引 入重试机制或人工处理。
3. Cancel 阶段是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采 用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。
4. TM事务管理器 TM事务管理器可以实现为独立的服务,也可以让全局事务发起方充当TM的角色,TM独立出来是为了成为公 用组件,是为了考虑系统结构和软件复用。TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用来记录事务上下文, 追踪和记录状态,由于Confirm 和cancel失败需进行重试,因此需要实现为幂等,幂等性是指同一个操作无论请求 多少次,其结果都相同。

Hmily:
Hmily是一个高性能分布式事务TCC开源框架。基于Java语言来开发(JDK1.8),支持Dubbo,Spring Cloud等 RPC框架进行分布式事务。Hmily实现的TCC服务与普通的服务一样,只需要暴露一个接口,也就是它的Try业务。Confirm/Cancel业务 逻辑,只是因为全局事务提交/回滚的需要才提供的,因此Confirm/Cancel业务只需要被Hmily TCC事务框架 发现即可,不需要被调用它的其他业务服务所感知。
官网介绍:https://dromara.org/website/zh-cn/docs/hmily/index.html

Hmily环境构建:
首先导入hmily:

  <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>hmily-springcloud</artifactId>
            <version>2.0.4-RELEASE</version>
        </dependency>

添加hmily数据库日志:

package com.study1.conf;

import com.alibaba.druid.pool.DruidDataSource;
import org.dromara.hmily.common.config.HmilyConfig;
import org.dromara.hmily.common.config.HmilyDbConfig;
import org.dromara.hmily.core.bootstrap.HmilyTransactionBootstrap;
import org.dromara.hmily.core.service.HmilyInitService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.env.Environment;

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class DatabaseHmliyConfiguration { 
   

    private final ApplicationContext applicationContext;

    @Autowired
    private Environment env;

    public DatabaseHmliyConfiguration(ApplicationContext applicationContext) { 
   
        this.applicationContext = applicationContext;
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource ds0() { 
   
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }

    @Bean
    public HmilyTransactionBootstrap hmilyTransactionBootstrap(HmilyInitService hmilyInitService){ 
   
        HmilyTransactionBootstrap hmilyTransactionBootstrap = new HmilyTransactionBootstrap(hmilyInitService);
        hmilyTransactionBootstrap.setSerializer(env.getProperty("org.dromara.hmily.serializer"));
        hmilyTransactionBootstrap.setRecoverDelayTime(Integer.parseInt(env.getProperty("org.dromara.hmily.recoverDelayTime")));
        hmilyTransactionBootstrap.setRetryMax(Integer.parseInt(env.getProperty("org.dromara.hmily.retryMax")));
        hmilyTransactionBootstrap.setScheduledDelay(Integer.parseInt(env.getProperty("org.dromara.hmily.scheduledDelay")));
        hmilyTransactionBootstrap.setScheduledThreadMax(Integer.parseInt(env.getProperty("org.dromara.hmily.scheduledThreadMax")));
        hmilyTransactionBootstrap.setRepositorySupport(env.getProperty("org.dromara.hmily.repositorySupport"));
        hmilyTransactionBootstrap.setStarted(Boolean.parseBoolean(env.getProperty("org.dromara.hmily.started")));
        HmilyDbConfig hmilyDbConfig = new HmilyDbConfig();
        hmilyDbConfig.setDriverClassName(env.getProperty("org.dromara.hmily.hmilyDbConfig.driverClassName"));
        hmilyDbConfig.setUrl(env.getProperty("org.dromara.hmily.hmilyDbConfig.url"));
        hmilyDbConfig.setUsername(env.getProperty("org.dromara.hmily.hmilyDbConfig.username"));
        hmilyDbConfig.setPassword(env.getProperty("org.dromara.hmily.hmilyDbConfig.password"));
        hmilyTransactionBootstrap.setHmilyDbConfig(hmilyDbConfig);
        return hmilyTransactionBootstrap;
    }

}

添加yml配置:

org:
  dromara:
    hmily:
      serializer: kryo
      recoverDelayTime: 30
      retryMax: 30
      scheduledDelay: 30
      scheduledThreadMax:  10
      repositorySupport: db
      started: true
      hmilyDbConfig:
        driverClassName: com.mysql.jdbc.Driver
        url:  jdbc:mysql://localhost:3306/bank1?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
        username: root
        password: 123

添加扫描:

@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients //使用@feign
//@EnableHystrix //使用hystrix
@ComponentScan({ 
   "com.study1","org.dromara.hmily"})
@MapperScan("com.study1.mapper")
public class ServceBank1Application { 
   
    public static void main(String[] args) { 
   
        SpringApplication.run(ServceBank1Application.class, args);
    }
}

第二个服务同上
这边数据库采用bank1,bank2
bank1实现用户扣款功能:

 //--------------Hmily------------------
    //只要标记@Hmily就是try方法,在注解中指定confirm、cancel两个方法的名字
    @Override
    @Transactional
    @Hmily(confirmMethod = "commit", cancelMethod = "rollback")
    public boolean reduceHmilyAmount(int amount) { 
   
        //预处理->扣款 张三扣款成功
        if (bankMapper.reduceAmount(amount) > 0) { 
   
            System.out.println("开始调用远程服务");
            //李四添加金额
            bankFeignService.addAmount(amount);
            return true;
        } else { 
   
            return false;
        }
    }

    @Transactional
    public void commit(int amount) { 
   
        System.out.println("成功处理事务");
    }

    @Transactional
    public void rollback(int amount) { 
   
        System.out.println("失败处理事务");
    }

bank2实现用户添加余额的功能:

//--------------Hmily------------------
    //只要标记@Hmily就是try方法,在注解中指定confirm、cancel两个方法的名字
    @Override
    @Transactional
    @Hmily(confirmMethod = "commit", cancelMethod = "rollback")
    public void addAmount(Integer amount) { 
   
        //预处理->扣款 张三扣款成功
        bankMapper.addAmount(amount);
        System.out.println("调用完成");
        int y = 3 / 0;
        System.out.println("执行其他业务");
    }

    @Transactional
    public void commit(int amount) { 
   
        System.out.println("bank2成功处理事务");
    }

    @Transactional
    public void rollback(int amount) { 
   
        System.out.println("---------------------失败处理事务------------------------");
        System.out.println("---------------------失败处理事务------------------------");
        System.out.println("---------------------失败处理事务------------------------");
        System.out.println("---------------------失败处理事务------------------------");
    }

feign服务调用:

//可以捕获错误日志
@FeignClient(value = "bank2", fallback = BankFeignServiceImpl.class)
public interface BankFeignService { 
   

    @GetMapping("/bank2/addAmount")
    @Hmily
    public boolean addAmount(@RequestParam("amount") Integer amount);
}

采用的是feign进行微服务调用,测试调用bank2报错,bank1执行回调方法:
在这里插入图片描述

开始调用远程服务
feign.FeignException: status 500 reading BankFeignService#addAmount(Integer)
	at feign.FeignException.errorStatus(FeignException.java:78)
	at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:93)
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:149)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78)
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
	at com.sun.proxy.$Proxy133.addAmount(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
	at org.dromara.hmily.core.service.handler.ConsumeHmilyTransactionHandler.handler(ConsumeHmilyTransactionHandler.java:35)
	at org.dromara.hmily.core.service.impl.HmilyTransactionAspectServiceImpl.invoke(HmilyTransactionAspectServiceImpl.java:63)
	at org.dromara.hmily.springcloud.interceptor.SpringCloudHmilyTransactionInterceptor.interceptor(SpringCloudHmilyTransactionInterceptor.java:78)
	at org.dromara.hmily.core.interceptor.AbstractHmilyTransactionAspect.interceptTccMethod(AbstractHmilyTransactionAspect.java:61)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
	at org.dromara.hmily.springcloud.feign.HmilyFeignHandler.invoke(HmilyFeignHandler.java:62)
	at com.sun.proxy.$Proxy134.addAmount(Unknown Source)
	at com.study1.service.impl.BankServiceImpl.reduceHmilyAmount(BankServiceImpl.java:52)
	at com.study1.service.impl.BankServiceImpl$$FastClassBySpringCGLIB$$b1ddc6fd.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
	at org.dromara.hmily.core.service.handler.StarterHmilyTransactionHandler.handler(StarterHmilyTransactionHandler.java:70)
	at org.dromara.hmily.core.service.impl.HmilyTransactionAspectServiceImpl.invoke(HmilyTransactionAspectServiceImpl.java:63)
	at org.dromara.hmily.springcloud.interceptor.SpringCloudHmilyTransactionInterceptor.interceptor(SpringCloudHmilyTransactionInterceptor.java:78)
	at org.dromara.hmily.core.interceptor.AbstractHmilyTransactionAspect.interceptTccMethod(AbstractHmilyTransactionAspect.java:61)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
	at com.study1.service.impl.BankServiceImpl$$EnhancerBySpringCGLIB$$e3476692.reduceHmilyAmount(<generated>)
	at com.study1.controller.BankController.reduceAmount(BankController.java:19)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:117)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:106)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
失败处理事务

使用Hmily注意事项:
在使用Hmliy时候注意里面的try,confirm,cancel等方法都是异步线程调用,注意避免他们进行频繁调用可以在数据库中添加方法调用记录,避免重复调用导致数据不一致的情况。同时当事务处理失败未得到处理时,数据库表中有一个hmily的日志记录表,它会对日志中的记录进行定时重复调用。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/143564.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月21日 下午12:20
下一篇 2022年5月21日 下午12:40


相关推荐

  • CQRS模式学习

    CQRS模式学习由于存在增删改与查询逻辑有差异的这个问题,为了更好的针对差异进行抽象,我们可以将它们分开进行设计。也就是我们的CQRS模式,即命令查询的责任分离CommandQueryResponsibilitySegregation模式。 简单的需求当我们系统中的数据模型层级较少时,数据模型足够简单时,模型与数据库可以直接进行映射。这种简单数据模型使我们不需要针对其相互关系进行复杂的建模设计,直接在工程中使用经典的三层模型就足以支撑项目需求。对于这种简单系统,过度设计会增加后续维护、重构的成本(并不能保

    2025年6月23日
    7
  • Windows XP虚拟机到期无法使用的解决方案(救急版)[通俗易懂]

    Windows XP虚拟机到期无法使用的解决方案(救急版)[通俗易懂]在之前的文章中已经教过大家如何在VMware中安装WindowsXP虚拟机,但是使用期限只有30天,近期有很多小伙伴说虚拟机到期了,开机之后显示无法登录,进不去桌面,更换密钥似乎也没成功,情况大概如下图这样:解决办法有两种:1.重新配置安装:VMware安装WindowsXP虚拟机并手动安装外加驱动程序但是这样有些麻烦,并且30天后还是会到期,所以推荐第二种办法。2.使用windowsXP的安全模式。…

    2025年11月14日
    3
  • PHP面试-复习知识点整理「建议收藏」

    PHP面试-复习知识点整理

    2022年2月10日
    51
  • 回溯法解决01背包问题算法_01背包问题伪代码

    回溯法解决01背包问题算法_01背包问题伪代码0-1背包问题,在搜索过程中使用递归来完成。packagecom.test;classPack{intn=8;//物品个数intW=110;//背包总容量int[]Weights={1,11,21,23,33,43,45,55};//重量数组int[]Values={11,21,31,33,43,53,55,65};//价值数组intbestValu…

    2022年10月9日
    4
  • json在线解析与格式化

    json在线解析与格式化这种操作 可以通过 https www json cn 的方式进行格式化 但是目前使用的网站内容是有故障的 如果直接使用其中的下载按钮可以下载出相应的 json 文件 但是该文件是错误的 需要通过按钮复制 再粘贴到相关的文件内 如图 第一个为下载按钮 第 2 个为复制按钮

    2026年3月18日
    1
  • 五大常用算法之二:动态规划算法

    一、基本概念动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。二、基本思想与策略基本

    2021年12月25日
    48

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号