https://mp.weixin.qq.com/s/AIxJsse6wqpps5QEyiPpaw 学习东西要知行合一,如果只是知道理论而没实践过,那么掌握的也不会特别扎实,估计过几天就会忘记,接下来我们一起实践来学习Spring事务的传播属性。 # 传播属性 传播属性定义的是当一个事务方法碰到另一个事务方法时的处理行为,一共有七种行为,定义如下 其实只看概念的话已经很直截了当了说明了每个传播性的作用,此时我们再用具体的例子演示一下每个传播性属性下的行为。 此次演示我们使用的是H2数据库,这个数据库是作用在内存里面的,所以对于我们演示事务效果来说正好,无需我们在进行其他的配置了,我们新建一个表。将下面语句放在schema.sql文件里面即可,SpringBoot程序在启动的时候就会自动为我们在内存里面建立这样的一个表。 演示之前我们会定义两个类FooService和BarService。我们使用BarService 里面的方法进行调用FooService 中的方法。 # 环境准备 在进行事务演示之前,其实可以分为以下几种情况,根据排列组合,我们可以得出以下八种情况 调用者:有无事务 调用者:是否有异常 被调用者:有无事务(这个是通过传播属性进行控制的)所以并不在排列组合中 被调用者:是否有异常 # 异常类 其中的RollbackException是我们自己定义的一个异常类 # 调用者 在BarService中定义两个方法,一个是带着事务的,一个是不带事务的 接下来我们就根据俄上面定义的八种情况进行事务传播属性的学习。 PROPAGATION_REQUIRED 在此传播属性下,被调用方是否新建事务取决去调用者是否带着事务。 想要了解这个传播属性的特性,其实我们演示上面八种情况的两个例子就够了 第一种情况我们在被调用者抛出异常的情况下,如果查询不到插入的数据,那么就说明被调用者在调用者没有事务的情况下自己新建了事务。 第二种情况我们在调用者抛出异常的情况下,如果查询不到插入的数据,那么就说明被调用者在调用者有事务的情况下就加入当前事务了。 我们先来看一下被调用者的类的方法例子。 接下来我们看一下调用者方法的例子 此时我们在程序调用时进行查询 查看打印出来的日志 我们看到我们都没有查到相应的数据,说明数据都回滚了。此时我们应该就理解了那句话支持当前事务,如果没有就新建事务。 PROPAGATION_SUPPORTS 被调用者是否有事务,完全依赖于调用者,调用者有事务则有事务,调用者没事务则没事务。 接下来我们还是用上面的两个例子进行演示 第一种情况:被调用者抛出异常的情况下,如果仍能查询到数据,说明事务没有回滚,说明被调用者没有事务 第二种情况:调用者抛出异常情况下,如果查不到数据,说明两个方法在一个事务中 接下来仍然是例子演示 被调用者,只是将@Transactional 注解中的propagation 属性更换为了Propagation.SUPPORTS 调用者和上面的例子调用一样,我们直接查看执行效果 我们看到了在第一种情况下查到了数据,说明在第一种情况下被调用者是没有事务的。此时我们应该就理解了这句话 支持当前事务,如果没有就不以事务的方式运行。 PROPAGATION_MANDATORY 依然是这两个例子进行演示 第一种情况:因为调用者没有事务,所以此传播属性下应该是抛异常的 第二种情况:被调用者的事务和调用者事务是同样的 接下来是被调用者的代码例子 调用者和上面的例子调用一样,我们直接查看执行效果 我们发现和我们推测一样,说明被调用者是不会自己新建事务的,此时我们应该就理解了这句话支持当前事务,如果当前没事务就抛异常。 PROPAGATION_REQUIRES_NEW 此传播属性下,无论调用者是否有事务,被调用者都会新建一个事务 […]
已知: lua脚本在redis中直接执行正常 redisTemplate执行lua脚本,出现各种错误,但用返回的sha,在redis中直接执行正常 初步判定,是redis的序列化的问题,出问题时,使用的RedisTemplate,且value的序列化使用的FastJsonRedisSerializer。推测在反序列化时,出现了问题 。 一个解决方案是,RedisTemplate用在通常的kv的操作中,与lua脚本有关的,使用StringRedisTemplate
首先:读写分离只支持一主多从的模式,不适合多主/多写的模式。 经验证, 读写分离 (正常 支持) 所有写走主库 所有读走从库 支持load-balance 分库分表 (正常 支持) 分库分表情况下,不要使用数据库自增主键,可使用雪花Id,因为id决定了要操作的库 读写分离 + 分表 (正常 支持) 读写分离 + 分库分表 (正常 支持) 查询情况正常,且还可支持load-balance,操作了主库和从库 添加功能正常,只操作主库 修改功能正常,只操作主库 删除功能主体正常,会删掉目标记录,但关联关系不会清除(待进一步解决) 相关代码
转:https://www.ruanyifeng.com/blog/2016/04/) CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出[XMLHttpRequest]请求,从而克服了AJAX只能[同源]使用的限制。 本文详细介绍CORS的内部机制。 一、简介 CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。 二、两种请求 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。 只要同时满足以下两大条件,就属于简单请求。 (1) 请求方法是以下三种方法之一: HEAD GET POST (2)HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。 凡是不同时满足上面两个条件,就属于非简单请求。 浏览器对这两种请求的处理,是不一样的。 三、简单请求 3.1 基本流程 对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。 下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。 上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。 如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。 如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。 上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。 (1)Access-Control-Allow-Origin 该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。 (2)Access-Control-Allow-Credentials 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。 (3)Access-Control-Expose-Headers 该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader(‘FooBar’)可以返回FooBar字段的值。 3.2 withCredentials 属性 上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。 另一方面,开发者必须在AJAX请求中打开withCredentials属性。 […]
转:http://www.macrozheng.com/#/reference/rabbitmq_start 以前看过的关于RabbitMQ核心消息模式的文章都是基于Java API的,最近看了下官方文档,发现这些核心消息模式都可以通过Spring AMQP来实现。于是总结了下RabbitMQ的核心知识点,包括RabbitMQ在Windows和Linux下的安装、5种核心消息模式的Spring AMQP实现,相信对于想要学习和回顾RabbitMQ的朋友都会有所帮助。 [简介] RabbitMQ是最受欢迎的开源消息中间件之一,在全球范围内被广泛应用。RabbitMQ是轻量级且易于部署的,能支持多种消息协议。RabbitMQ可以部署在分布式系统中,以满足大规模、高可用的要求。 [相关概念] 我们先来了解下RabbitMQ中的相关概念,这里以5种消息模式中的路由模式为例。 标志 中文名 英文名 描述 P 生产者 Producer 消息的发送者,可以将消息发送到交换机 C 消费者 Consumer 消息的接收者,从队列中获取消息并进行消费 X 交换机 Exchange 接收生产者发送的消息,并根据路由键发送给指定队列 Q 队列 Queue 存储从交换机发来的消息 type 交换机类型 type 不同类型的交换机转发消息方式不同 fanout 发布/订阅模式 fanout 广播消息给所有绑定交换机的队列 direct 路由模式 direct 根据路由键发送消息 topic 通配符模式 topic 根据路由键的匹配规则发送消息 [安装及配置] 接下来我们介绍下RabbitMQ的安装和配置,提供Windows和Linux两种安装方式。 [Windows下的安装] 安装Erlang,下载地址:http://erlang.org/download/otp_win64_21.3.exe 安装RabbitMQ,下载地址:https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe 安装完成后,进入RabbitMQ安装目录下的sbin目录; 在地址栏输入cmd并回车启动命令行,然后输入以下命令启动管理功能。 [Linux下的安装] 下载rabbitmq 3.7.15的Docker镜像; […]
转:阿里技术。做了部分补充 一 为什么讲这个? 总结AQS之后,对这方面顺带的复习一下。本文从以下几个高频问题出发: 对象在内存中的内存布局是什么样的? 描述synchronized和ReentrantLock的底层实现和重入的底层原理。 谈谈AQS,为什么AQS底层是CAS+volatile? 描述下锁的四种状态和锁升级过程? Object o = new Object() 在内存中占用多少字节? 自旋锁是不是一定比重量级锁效率高? 打开偏向锁是否效率一定会提升? 重量级锁到底重在哪里? 重量级锁什么时候比轻量级锁效率高,同样反之呢? 二 加锁发生了什么? 无意识中用到锁的情况: 简单加锁发生了什么? 要弄清楚加锁之后到底发生了什么需要看一下对象创建之后再内存中的布局是个什么样的? 一个对象在new出来之后在内存中主要分为4个部分: markword这部分其实就是加锁的核心,同时还包含的对象的一些生命信息,例如是否GC、经过了几次Young GC还存活。 klass pointer记录了指向对象的class文件指针。 instance data记录了对象里面的变量数据。 padding作为对齐使用,对象在64位服务器版本中,规定对象内存必须要能被8字节整除,如果不能整除,那么就靠对齐来补。举个例子:new出了一个对象,内存只占用18字节,但是规定要能被8整除,所以padding=6。 知道了这4个部分之后,我们来验证一下底层。借助于第三方包 JOL = Java Object Layout java内存布局去看看。很简单的几行代码就可以看到内存布局的样式: 将结果打印出来: 从输出结果看: 1)对象头包含了12个字节分为3行,其中前2行其实就是markword,第三行就是klass指针。值得注意的是在加锁前后输出从001变成了000。Markword用处:8字节(64bit)的头记录一些信息,锁就是修改了markword的内容8字节(64bit)的头记录一些信息。从001无锁状态,变成了00轻量级锁状态。 2)New出一个object对象,占用16个字节。对象头占用12字节,由于Object中没有额外的变量,所以instance = 0,考虑要对象内存大小要被8字节整除,那么padding=4,最后new Object() 内存大小为16字节。 拓展:什么样的对象会进入老年代?很多场景例如对象太大了可以直接进入,但是这里想探讨的是为什么从Young GC的对象最多经历15次Young GC还存活就会进入Old区(年龄是可以调的,默认是15)。上图中hotspots的markword的图中,用了4个bit去表示分代年龄,那么能表示的最大范围就是0-15。所以这也就是为什么设置新生代的年龄不能超过15,工作中可以通过-XX:MaxTenuringThreshold去调整,但是一般我们不会动。 三 锁的升级过程 1 锁的升级验证 探讨锁的升级之前,先做个实验。两份代码,不同之处在于一个中途让它睡了5秒,一个没睡。看看是否有区别。 这两份代码会不会有什么区别?运行之后看看结果: […]