乐观锁实现接口幂等性_什么是幂等性,如何实现,以及乐观锁在项目中的实际用法...

3849ea044de551d81b921a68bc79756a.png

什么是幂等性?

对于同一笔业务操作,不管调用多少次,得到的结果都是一样的。

  1. 普通方式 只适合单机
  2. jvm加锁方式
  • Lock只能在一个jvm中起效,如果多个请求都被同一套系统处理,上面这种使用Lock的方式是没有问题的,不过互联网系统中,多数是采用集群方式部署系统,同一套代码后面会部署多套,如果支付宝同时发来多个通知经过负载均衡转发到不同的机器,上面的锁就不起效了。此时对于多个请求相当于无锁处理了

3. 悲观锁方式,使用数据库中悲观锁实现。悲观锁类似于方式二中的Lock, 只不过是依靠数据库来实现的。数据库中悲观锁使用for update来实现的

  • 方式3可以保证接口的幂等性,不过存在缺点,如果业务耗时,并发情况下,后面线程会长期处于等待状态,占用很多线程,让这些线程处于无效等待状态

4. 乐观锁方式,使用数据库中乐观锁实现。主要还是通过版本号,类似于java的cas操作,那么问题来了,在多线程的情况下,一个线程取得数据了,其它线程会怎么样呢?会不断重试,不会阻塞,可以理解为自旋,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。

在许多场景中,同步资源的锁定时间很短,为了这一小段时间去切换线程,线程挂起和恢复现场的花费可能会让系统得不偿失。如果物理机器有多个处理器,能够让两个或以上的线程同时并行执行,我们就可以让后面那个请求锁的线程不放弃CPU的执行时间,看看持有锁的线程是否很快就会释放锁。

而为了让当前线程“稍等一下”,我们需让当前线程进行自旋,如果在自旋完成后前面锁定同步资源的线程已经释放了锁,那么当前线程就可以不必阻塞而是直接获取同步资源,从而避免切换线程的开销。这就是自旋锁。

5. 唯一约束

常见方式:悲观锁,乐观锁,唯一约束

最优排序:乐观锁 > 唯一约束 > 悲观锁

乐观锁CAS的用法,比方说在couchbase中,key是自带版本号的

object OriginCasStreamingTransfer {
  implicit val conf = ConfigFactory.load

  def main(args: Array[String]): Unit = {
    val cbSingleUerThreshold = conf.getInt("cb_single_user_threshold.size")
    val batchSize = conf.getInt("batch.size")  // 消息批次处理
    val zkQuorum = conf.getString("test_kafka.zkQuorum")
    val brokers = conf.getString("test_kafka.brokers")
    val topics = conf.getString("test_kafka.topics")
    val group = conf.getString("test_kafka.group.id")
    val seconds: Long = 120  // spark streaming mini batch time interval 防止pending batch过多造成流任务失败
    val numThreads = 16
    val topicMap = topics.split(",").map((_, numThreads)).toMap
    val kafkaParams = Map(
      ("metadata.broker.list", brokers),
      ("zookeeper.connect", zkQuorum),
      ("auto.offset.reset", "smallest"),
      ("zookeeper.connection.timeout.ms", "30000"),
      ("group.id", group)
    )

    val sparkConf = new SparkConf()
      .setIfMissing("spark.master", "local[*]")
      .setAppName("CasStreamingTransfer")
      .set("spark.default.parallelism", "1000")
      .set("spark.executor.memory", "8g")
    val spark = SparkSession
      .builder()
      .appName("CasStreamingTransfer")
      .config(sparkConf)
      .getOrCreate()
    val ssc = new StreamingContext(spark.sparkContext, Seconds(seconds))

    val stream = KafkaUtils.createStream[String, String, StringDecoder, StringDecoder](
      ssc, kafkaParams, topicMap,
      StorageLevel.MEMORY_AND_DISK_SER)  // key value key解码格式 value解码格式

    stream.foreachRDD{each_rdd =>
      each_rdd.repartition(1000).foreachPartition{ each_partition =>
        while (each_partition.hasNext){
          var batchCount = 0
          val batchRecord = ListBuffer[String]()
          while (batchCount < batchSize && each_partition.hasNext){
            val item = each_partition.next()
            batchRecord += item._1
            batchCount += 1
          }
          val errorReadResult = collection.mutable.Set[String]()
          val readResult = CbManager.getInstance().readCasAsync("test_playback_record_couchbase", batchRecord.asJava, errorReadResult.asJava)
//          println("readResult: " + readResult)
          val writeResult = collection.mutable.Map[CbCasEntity, Array[Byte]]()
          for ((key, value) <- readResult.asScala){
            val uid = key.getId  // 取出的用户uid
//            println("uid: " + uid)
            val cas = key.getCas  // 取出的用户的couchbase当中的cas值
            val parseResult = PlayRecord.UserPlayRecords.parseFrom(value)  // 解析couchbase当中的value字段

            // 解析
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Java 实现接口幂等性可以通过以下几种方式: 1. 请求参数设计:在接口设计,可以通过设计合理的请求参数来确保幂等性。例如,给每个请求添加一个唯一标识符,服务端可以根据该标识符判断请求是否重复。可以使用 UUID、流水号或者时间戳等来生成唯一标识符。 2. 幂等性校验:在服务端处理请求时,可以进行幂等性校验。可以通过在数据库记录请求的唯一标识符,每次请求前先查询数据库判断是否已经处理过该请求,避免重复处理。可以使用数据库的唯一索引或者乐观锁来保证并发情况下的幂等性。 3. 幂等性处理:如果接口需要对数据进行修改操作,可以使用乐观锁或悲观锁来保证数据的一致性。乐观锁通过版本号或时间戳判断数据是否被修改,悲观锁则通过加锁来确保同一时间只有一个线程能够修改数据。使用数据库事务来保证操作的原子性。 4. 返回结果处理:在接口的返回结果,可以包含一个唯一标识符或者状态码,客户端可以根据这个标识符或状态码来判断是否已经调用过该接口。如果接口已经被调用过,则客户端可以直接使用之前的结果,避免重复操作。 综上所述,通过合理的接口设计和实现幂等性校验,可以保证接口在多次调用时的一致性和可靠性。具体实现方式可以根据项目需求和实际情况选择合适的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值