极端情况分析

1)全量拉取注册表的的时候,就有可能拉着拉着因为某些原因只获得了30个实例(网络故障等等问题)就卡住了,同时也没有开始替换这个客户端的注册表数据。
image.png
2)此时,获取增量注册表的线程也在运行着。在这里有个合并校验的过程,校验的时候就发现数据不对啊,就要从服务端去拉取实例,此时拉了完整的40个实例数据过来给客户端的缓存注册表上赋值。
image.png
3)赋完值后,之前那个拉取全量注册表数据的线程又可以继续往下执行了,就开始cas的去给客户端的缓存注册表上赋值。此时就是有问题的,因为之前这个线程只拉去了30个实例是旧数据,而现在注册表中已经是最新的服务数据了。如果继续赋值,旧数据就会覆盖新数据从注册表版本就混乱了。

AtomicLong解决注册表版本混乱

实际上本质就是和AtomicStampReference解决方法一样,通过版本号来解决。每次网络请求后,要比对下这个版本号,版本号和以前一样就可以去继续赋值操作。

  1. /**
  2. * 本地缓存服务注册表的版本号
  3. */
  4. private AtomicLong applicationVersion = new AtomicLong();
  5. /**
  6. * 全量拉取注册表信息
  7. */
  8. private class FetchFullRegistryWorker extends Thread {
  9. @Override
  10. public void run() {
  11. //获取全量注册表
  12. fetchFullRegistry();
  13. }
  14. }
  15. /**
  16. * 拉取全量注册表到客户端
  17. */
  18. private void fetchFullRegistry() {
  19. //一定要在发起网络请求之前,拿到当前版本号
  20. Long expectedVersion = applicationVersion.get();
  21. //这里发起网络请求,在这个期间别的线程会去操作缓存注册表数据
  22. Applications fetchedApplications = httpSender.fetchFullServiceRegistry();
  23. //当这个版本号更新成功的话就代表没有别的线程对这个注册表操作过,那么才能去修改注册表数据
  24. if (applicationVersion.compareAndSet(expectedVersion,expectedVersion + 1)) {
  25. while (true) {
  26. // Applications expectedApplications = applications.get();
  27. Applications expectedApplications = applications.getReference();
  28. int expectedStamp = applications.getStamp();
  29. if (applications.compareAndSet(expectedApplications, fetchedApplications,
  30. expectedStamp, expectedStamp +1)) {
  31. break;
  32. }
  33. }
  34. }
  35. //如果,有别的线程修改了注册表的数据,就不去更新
  36. }