温馨提示:

本文内容基于个人学习Nacos 2.0.1版本代码总结而来,因个人理解差异,不保证完全正确。如有理解错误之处欢迎各位拍砖指正,相互学习;转载请注明出处。

Nacos服务端在处理健康检查和心跳检查任务的时候它是使用拦截器链来执行的。拦截器链内部有多个拦截器,通过获取不同的拦截器链实例,在实例内部指定具体的拦截器类型来组成一组拦截器。这里使用了拦截器模式和模板模式来组织代码。拦截器模式体现在整体拦截机制的实现;模板模式主要体现在对拦截器链的抽象实现上。

拦截器模式有三个要素

  • 拦截器
  • 调度者
  • 业务逻辑

拦截器

定义一个拦截器的基本功能,同时限定了传入的拦截对象类型必须为Interceptable。这里只定义了基本的功能和基本的限定拦截对象。这里将其描述为基本的功能,那就意味着它的实现将会有更高级的功能。

  1. /**
  2. * Nacos naming interceptor.
  3. * 拦截器对象
  4. * @author xiweng.yy
  5. */
  6. public interface NacosNamingInterceptor<T extends Interceptable> {
  7. /**
  8. * Judge whether the input type is intercepted by this Interceptor.
  9. * 此拦截器的实例将会判断传入的对象是否是他需要处理的类型,此方法可以实现不同拦截器处理不同对象的隔离操作
  10. * <p>This method only should judge the object type whether need be do intercept. Not the intercept logic.
  11. * @param type type
  12. * @return true if the input type is intercepted by this Interceptor, otherwise false
  13. */
  14. boolean isInterceptType(Class<?> type);
  15. /**
  16. * Do intercept operation.
  17. * 执行拦截操作
  18. * <p>This method is the actual intercept operation.
  19. * @param object need intercepted object
  20. * @return true if object is intercepted, otherwise false
  21. */
  22. boolean intercept(T object);
  23. /**
  24. * The order of interceptor. The lower the number, the earlier the execution.
  25. * 拦截器排序,数字越低,优先级越高
  26. * @return the order number of interceptor
  27. */
  28. int order();
  29. }

被拦截的对象

Interceptable 定义了对拦截操作相关的执行方法,passIntercept()在未被拦截的时候需要执行,afterIntercept()在被拦截之后需要执行。被拦截对象的业务逻辑需要由拦截器负责调度。

  1. /**
  2. * Interceptable Interface.
  3. *
  4. * @author xiweng.yy
  5. */
  6. public interface Interceptable {
  7. /**
  8. * If no {@link NacosNamingInterceptor} intercept this object, this method will be called to execute.
  9. */
  10. void passIntercept();
  11. /**
  12. * If one {@link NacosNamingInterceptor} intercept this object, this method will be called.
  13. */
  14. void afterIntercept();
  15. }

调度者

调度者主要是用来管理拦截器的组织方式,触发拦截器的拦截操作。下图展示了Naming模块的拦截器链的继承关系。

整体的构成由NacosNamingInterceptorChain定义基本框架,AbstractNamingInterceptorChain实现通用逻辑,HealthCheckInterceptorChainInstanceBeatCheckTaskInterceptorChain则分别服务于健康检查和心跳检查。

NacosNamingInterceptorChain

定义了拦截器链对象应该具有的基本行为:添加拦截器、执行拦截器。

  1. /**
  2. * Nacos naming interceptor chain.
  3. * Nacos Naming模块的拦截器链接口,拦截器链用于存储并管理多个拦截器
  4. * @author xiweng.yy
  5. */
  6. public interface NacosNamingInterceptorChain<T extends Interceptable> {
  7. /**
  8. * Add interceptor.
  9. * 添加指定类型的拦截器对象
  10. * @param interceptor interceptor
  11. */
  12. void addInterceptor(NacosNamingInterceptor<T> interceptor);
  13. /**
  14. * Do intercept by added interceptors.
  15. * 执行拦截的业务操作
  16. * @param object be interceptor object
  17. */
  18. void doInterceptor(T object);
  19. }

AbstractNamingInterceptorChain

AbstractNamingInterceptorChain实现了NacosNamingInterceptorChain所定义的对NacosNamingInterceptor的操作。在构造方法中提供了具体的拦截器实现类的加载,它这里使用了SPI方式加载。默认可以加载的拦截器必须是NacosNamingInterceptor的实例。在拦截器的执行方法doInterceptor()中会按优先级调用每一个拦截器,首先判断被拦截的对象是否是此拦截器处理,接着调用拦截器的intercept()方法,成功后调用被拦截对象的afterIntercept()方法。若未拦截成功则调用被拦截对象的passIntercept()方法。因此在拦截器中的intercept()方法中可以定义拦截器对被拦截对象的处理逻辑,而被拦截对象则可以在afterIntercept()和passIntercept()方法中定义自身的处理逻辑。从而实现在拦截器中被处理和自身处理任务依赖于拦截器来触发。

  1. /**
  2. * Abstract Naming Interceptor Chain.
  3. * 抽象的命名服务拦截器链,用于定义拦截器链的工作流程
  4. * @author xiweng.yy
  5. */
  6. public abstract class AbstractNamingInterceptorChain<T extends Interceptable> implements NacosNamingInterceptorChain<T> {
  7. // 存储多个拦截器
  8. private final List<NacosNamingInterceptor<T>> interceptors;
  9. // 限制使用范围为当前包或者其子类
  10. protected AbstractNamingInterceptorChain(Class<? extends NacosNamingInterceptor<T>> clazz) {
  11. this.interceptors = new LinkedList<>();
  12. // 使用SPI模式加载指定的拦截器类型
  13. // 而且NacosNamingInterceptor内部有判断它需要拦截对象的类型,因此非常灵活
  14. interceptors.addAll(NacosServiceLoader.load(clazz));
  15. // 对拦截器的顺序进行排序
  16. interceptors.sort(Comparator.comparingInt(NacosNamingInterceptor::order));
  17. }
  18. /**
  19. * Get all interceptors.
  20. *
  21. * @return interceptors list
  22. */
  23. protected List<NacosNamingInterceptor<T>> getInterceptors() {
  24. return interceptors;
  25. }
  26. @Override
  27. public void addInterceptor(NacosNamingInterceptor<T> interceptor) {
  28. // 若手动添加,则需要再次进行排序
  29. interceptors.add(interceptor);
  30. interceptors.sort(Comparator.comparingInt(NacosNamingInterceptor::order));
  31. }
  32. @Override
  33. public void doInterceptor(T object) {
  34. // 因为内部的拦截器已经排序过了,所以直接遍历
  35. for (NacosNamingInterceptor<T> each : interceptors) {
  36. // 若当前拦截的对象不是当前拦截器所要处理的类型则调过
  37. if (!each.isInterceptType(object.getClass())) {
  38. continue;
  39. }
  40. // 执行拦截操作成功之后,继续执行拦截后操作
  41. if (each.intercept(object)) {
  42. object.afterIntercept();
  43. return;
  44. }
  45. }
  46. // 未拦截的操作
  47. object.passIntercept();
  48. }
  49. }

doInterceptor() 方法中使用当前拦截器链内部的所有拦截器对被拦截对象进行处理,并且组织了被拦截对象被拦截之后的方法调用流程。即:拦截之后执行被拦截对象的afterIntercept()方法,未拦截时执行passIntercept()方法。

HealthCheckInterceptorChain

健康检查拦截器链负责加载AbstractHealthCheckInterceptor类型的拦截器。

  1. /**
  2. * Health check interceptor chain.
  3. * @author xiweng.yy
  4. */
  5. public class HealthCheckInterceptorChain extends AbstractNamingInterceptorChain<NacosHealthCheckTask> {
  6. private static final HealthCheckInterceptorChain INSTANCE = new HealthCheckInterceptorChain();
  7. private HealthCheckInterceptorChain() {
  8. super(AbstractHealthCheckInterceptor.class);
  9. }
  10. public static HealthCheckInterceptorChain getInstance() {
  11. return INSTANCE;
  12. }
  13. }

InstanceBeatCheckTaskInterceptorChain

实例心跳检查器链负责加载AbstractBeatCheckInterceptor类型的拦截器。

  1. /**
  2. * Instance beat check interceptor chain.
  3. *
  4. * @author xiweng.yy
  5. */
  6. public class InstanceBeatCheckTaskInterceptorChain extends AbstractNamingInterceptorChain<InstanceBeatCheckTask> {
  7. private static final InstanceBeatCheckTaskInterceptorChain INSTANCE = new InstanceBeatCheckTaskInterceptorChain();
  8. private InstanceBeatCheckTaskInterceptorChain() {
  9. super(AbstractBeatCheckInterceptor.class);
  10. }
  11. public static InstanceBeatCheckTaskInterceptorChain getInstance() {
  12. return INSTANCE;
  13. }
  14. }

小结

通过模板模式来实现拦截器机制。

  • AbstractNamingInterceptorChain 抽象出连接器链对拦截器加载的通用方法,定义了拦截器对被拦截对象的通用处理流程。
  • AbstractHealthCheckInterceptor 定义了健康检查拦截器被拦截的对象类型
  • AbstractBeatCheckInterceptor 定义了心跳检查拦截器被拦截的对象类型

通过对拦截器链的组织方式梳理可以看到有明显的两条路线,一个是健康检查,一个是心跳检查。分析后续具体的拦截器,以及他们所要处理的任务就很清晰了。

业务逻辑

业务逻辑是被拦截器拦截之后需要进行的操作。

健康检查类的被拦截对象

健康检查的抽象拦截器AbstractHealthCheckInterceptor定义了它的子类将要处理的任务类型为NacosHealthCheckTask

HealthCheckTaskV2

  1. /**
  2. * Health check task for v2.x.
  3. * v2版本的健康检查
  4. * <p>Current health check logic is same as v1.x. TODO refactor health check for v2.x.
  5. *
  6. * @author nacos
  7. */
  8. public class HealthCheckTaskV2 extends AbstractExecuteTask implements NacosHealthCheckTask {
  9. /**
  10. * 一个客户端对象(此客户端代表提供服务用于被应用访问的客户端)
  11. * 从这里可以看出,启动一个健康检查任务是以客户端为维度的
  12. */
  13. private final IpPortBasedClient client;
  14. private final String taskId;
  15. private final SwitchDomain switchDomain;
  16. private final NamingMetadataManager metadataManager;
  17. private long checkRtNormalized = -1;
  18. /**
  19. * 检查最佳响应时间
  20. */
  21. private long checkRtBest = -1;
  22. /**
  23. * 检查最差响应时间
  24. */
  25. private long checkRtWorst = -1;
  26. /**
  27. * 检查上次响应时间
  28. */
  29. private long checkRtLast = -1;
  30. /**
  31. * 检查上上次响应时间
  32. */
  33. private long checkRtLastLast = -1;
  34. /**
  35. * 开始时间
  36. */
  37. private long startTime;
  38. /**
  39. * 任务是否取消
  40. */
  41. private volatile boolean cancelled = false;
  42. public HealthCheckTaskV2(IpPortBasedClient client) {
  43. this.client = client;
  44. this.taskId = client.getResponsibleId();
  45. this.switchDomain = ApplicationUtils.getBean(SwitchDomain.class);
  46. this.metadataManager = ApplicationUtils.getBean(NamingMetadataManager.class);
  47. // 初始化响应时间检查
  48. initCheckRT();
  49. }
  50. /**
  51. * 初始化响应时间值
  52. */
  53. private void initCheckRT() {
  54. // first check time delay
  55. // 2000 + (在5000以内的随机数)
  56. checkRtNormalized =
  57. 2000 + RandomUtils.nextInt(0, RandomUtils.nextInt(0, switchDomain.getTcpHealthParams().getMax()));
  58. // 最佳响应时间
  59. checkRtBest = Long.MAX_VALUE;
  60. // 最差响应时间为0
  61. checkRtWorst = 0L;
  62. }
  63. public IpPortBasedClient getClient() {
  64. return client;
  65. }
  66. @Override
  67. public String getTaskId() {
  68. return taskId;
  69. }
  70. /**
  71. * 开始执行健康检查任务
  72. */
  73. @Override
  74. public void doHealthCheck() {
  75. try {
  76. // 获取当前传入的Client所发布的所有Service
  77. for (Service each : client.getAllPublishedService()) {
  78. // 只有当Service开启了健康检查才执行
  79. if (switchDomain.isHealthCheckEnabled(each.getGroupedServiceName())) {
  80. // 获取Service对应的InstancePublishInfo
  81. InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(each);
  82. // 获取集群元数据
  83. ClusterMetadata metadata = getClusterMetadata(each, instancePublishInfo);
  84. // 使用Processor代理对象对任务进行处理
  85. ApplicationUtils.getBean(HealthCheckProcessorV2Delegate.class).process(this, each, metadata);
  86. if (Loggers.EVT_LOG.isDebugEnabled()) {
  87. Loggers.EVT_LOG.debug("[HEALTH-CHECK] schedule health check task: {}", client.getClientId());
  88. }
  89. }
  90. }
  91. } catch (Throwable e) {
  92. Loggers.SRV_LOG.error("[HEALTH-CHECK] error while process health check for {}", client.getClientId(), e);
  93. } finally {
  94. // 若任务执行状态为已取消,则再次启动
  95. if (!cancelled) {
  96. HealthCheckReactor.scheduleCheck(this);
  97. // worst == 0 means never checked
  98. if (this.getCheckRtWorst() > 0) {
  99. // TLog doesn't support float so we must convert it into long
  100. long checkRtLastLast = getCheckRtLastLast();
  101. this.setCheckRtLastLast(this.getCheckRtLast());
  102. if (checkRtLastLast > 0) {
  103. long diff = ((this.getCheckRtLast() - this.getCheckRtLastLast()) * 10000) / checkRtLastLast;
  104. if (Loggers.CHECK_RT.isDebugEnabled()) {
  105. Loggers.CHECK_RT.debug("{}->normalized: {}, worst: {}, best: {}, last: {}, diff: {}",
  106. client.getClientId(), this.getCheckRtNormalized(), this.getCheckRtWorst(),
  107. this.getCheckRtBest(), this.getCheckRtLast(), diff);
  108. }
  109. }
  110. }
  111. }
  112. }
  113. }
  114. @Override
  115. public void passIntercept() {
  116. doHealthCheck();
  117. }
  118. @Override
  119. public void afterIntercept() {
  120. // 若任务执行状态为已取消,则再次启动
  121. if (!cancelled) {
  122. HealthCheckReactor.scheduleCheck(this);
  123. }
  124. }
  125. @Override
  126. public void run() {
  127. // 调用健康检查
  128. doHealthCheck();
  129. }
  130. /**
  131. * 获取集群元数据
  132. * @param service 服务信息
  133. * @param instancePublishInfo 服务对应的ip等信息
  134. * @return
  135. */
  136. private ClusterMetadata getClusterMetadata(Service service, InstancePublishInfo instancePublishInfo) {
  137. Optional<ServiceMetadata> serviceMetadata = metadataManager.getServiceMetadata(service);
  138. if (!serviceMetadata.isPresent()) {
  139. return new ClusterMetadata();
  140. }
  141. String cluster = instancePublishInfo.getCluster();
  142. ClusterMetadata result = serviceMetadata.get().getClusters().get(cluster);
  143. return null == result ? new ClusterMetadata() : result;
  144. }
  145. public long getCheckRtNormalized() {
  146. return checkRtNormalized;
  147. }
  148. public long getCheckRtBest() {
  149. return checkRtBest;
  150. }
  151. public long getCheckRtWorst() {
  152. return checkRtWorst;
  153. }
  154. public void setCheckRtWorst(long checkRtWorst) {
  155. this.checkRtWorst = checkRtWorst;
  156. }
  157. public void setCheckRtBest(long checkRtBest) {
  158. this.checkRtBest = checkRtBest;
  159. }
  160. public void setCheckRtNormalized(long checkRtNormalized) {
  161. this.checkRtNormalized = checkRtNormalized;
  162. }
  163. public boolean isCancelled() {
  164. return cancelled;
  165. }
  166. public void setCancelled(boolean cancelled) {
  167. this.cancelled = cancelled;
  168. }
  169. public long getStartTime() {
  170. return startTime;
  171. }
  172. public void setStartTime(long startTime) {
  173. this.startTime = startTime;
  174. }
  175. public long getCheckRtLast() {
  176. return checkRtLast;
  177. }
  178. public void setCheckRtLast(long checkRtLast) {
  179. this.checkRtLast = checkRtLast;
  180. }
  181. public long getCheckRtLastLast() {
  182. return checkRtLastLast;
  183. }
  184. public void setCheckRtLastLast(long checkRtLastLast) {
  185. this.checkRtLastLast = checkRtLastLast;
  186. }
  187. }

心跳检查类的被拦截对象

ClientBeatCheckTaskV2

虽然它继承了NacosHealthCheckTask,但内部只使用了InstanceBeatCheckTaskInterceptorChain,没有使用HealthCheckInterceptorChain, 按理说应该划分到"心跳检查类的被拦截对象" 这个类别的。不知道为何这样设计,已提issues。

  1. /**
  2. * Client beat check task of service for version 2.x.
  3. * @author nkorange
  4. */
  5. public class ClientBeatCheckTaskV2 extends AbstractExecuteTask implements BeatCheckTask, NacosHealthCheckTask {
  6. private final IpPortBasedClient client;
  7. private final String taskId;
  8. /**
  9. * 使用拦截器链
  10. */
  11. private final InstanceBeatCheckTaskInterceptorChain interceptorChain;
  12. public ClientBeatCheckTaskV2(IpPortBasedClient client) {
  13. this.client = client;
  14. this.taskId = client.getResponsibleId();
  15. this.interceptorChain = InstanceBeatCheckTaskInterceptorChain.getInstance();
  16. }
  17. public GlobalConfig getGlobalConfig() {
  18. return ApplicationUtils.getBean(GlobalConfig.class);
  19. }
  20. @Override
  21. public String taskKey() {
  22. return KeyBuilder.buildServiceMetaKey(client.getClientId(), String.valueOf(client.isEphemeral()));
  23. }
  24. @Override
  25. public String getTaskId() {
  26. return taskId;
  27. }
  28. @Override
  29. public void doHealthCheck() {
  30. try {
  31. // 获取所有的Service
  32. Collection<Service> services = client.getAllPublishedService();
  33. for (Service each : services) {
  34. logger.info("开始对Service进行拦截操作,{}", each.getName());
  35. // 获取Service对应的InstancePublishInfo
  36. HealthCheckInstancePublishInfo instance = (HealthCheckInstancePublishInfo) client.getInstancePublishInfo(each);
  37. // 创建一个InstanceBeatCheckTask,并交由拦截器链处理
  38. interceptorChain.doInterceptor(new InstanceBeatCheckTask(client, each, instance));
  39. }
  40. } catch (Exception e) {
  41. Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
  42. }
  43. }
  44. @Override
  45. public void run() {
  46. doHealthCheck();
  47. }
  48. @Override
  49. public void passIntercept() {
  50. doHealthCheck();
  51. }
  52. @Override
  53. public void afterIntercept() {
  54. }
  55. }

InstanceBeatCheckTask

  1. /**
  2. * Instance beat check task.
  3. * Instance心跳检查任务,此处它作为一个可被拦截器拦截的对象使用。
  4. * @author xiweng.yy
  5. */
  6. public class InstanceBeatCheckTask implements Interceptable {
  7. // 心跳检查者列表
  8. private static final List<InstanceBeatChecker> CHECKERS = new LinkedList<>();
  9. // 客户端对象(因为实例就代表的是客户端)
  10. private final IpPortBasedClient client;
  11. // 服务对象
  12. private final Service service;
  13. // 健康检查信息
  14. private final HealthCheckInstancePublishInfo instancePublishInfo;
  15. static {
  16. // 添加不健康实例检查器
  17. CHECKERS.add(new UnhealthyInstanceChecker());
  18. // 添加过期实例检查器
  19. CHECKERS.add(new ExpiredInstanceChecker());
  20. // 添加用户自定义的心跳检查器
  21. CHECKERS.addAll(NacosServiceLoader.load(InstanceBeatChecker.class));
  22. }
  23. public InstanceBeatCheckTask(IpPortBasedClient client, Service service, HealthCheckInstancePublishInfo instancePublishInfo) {
  24. this.client = client;
  25. this.service = service;
  26. this.instancePublishInfo = instancePublishInfo;
  27. }
  28. @Override
  29. public void passIntercept() {
  30. // 未被拦截的时候执行自身逻辑
  31. for (InstanceBeatChecker each : CHECKERS) {
  32. each.doCheck(client, service, instancePublishInfo);
  33. }
  34. }
  35. @Override
  36. public void afterIntercept() {
  37. }
  38. public IpPortBasedClient getClient() {
  39. return client;
  40. }
  41. public Service getService() {
  42. return service;
  43. }
  44. public HealthCheckInstancePublishInfo getInstancePublishInfo() {
  45. return instancePublishInfo;
  46. }
  47. }

总结

  • 拦截器链确定了要加载的拦截器类型
  • 拦截器确定了要拦截的对象类型
  • 被拦截的对象又建立了自己的检查策略

Nacos 2.0源码分析-拦截器机制的更多相关文章

  1. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  2. springMVC源码分析--拦截器HandlerExecutionChain(三)

    上一篇博客springMVC源码分析--HandlerInterceptor拦截器调用过程(二)中我们介绍了HandlerInterceptor的执行调用地方,最终HandlerInterceptor ...

  3. Struts2 源码分析——拦截器的机制

    本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样 ...

  4. Struts2 源码分析-----拦截器源码解析 --- ParametersInterceptor

    ParametersInterceptor拦截器其主要功能是把ActionContext中的请求参数设置到ValueStack中,如果栈顶是当前Action则把请求参数设置到了Action中,如果栈顶 ...

  5. (一) Mybatis源码分析-解析器模块

    Mybatis源码分析-解析器模块 原创-转载请说明出处 1. 解析器模块的作用 对XPath进行封装,为mybatis-config.xml配置文件以及映射文件提供支持 为处理动态 SQL 语句中的 ...

  6. Solr5.0源码分析-SolrDispatchFilter

    年初,公司开发法律行业的搜索引擎.当时,我作为整个系统的核心成员,选择solr,并在solr根据我们的要求做了相应的二次开发.但是,对solr的还没有进行认真仔细的研究.最近,事情比较清闲,翻翻sol ...

  7. 3 cocos2dx 3.0 源码分析-mainLoop详细

    简述:   我靠上面图是不是太大了, 有点看不清了.  总结一下过程: 之前说过的appController 之后经过了若干初始化, 最后调用了displayLinker 的定时调用, 这里调用了函数 ...

  8. AFNetWorking3.0源码分析

    分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end

  9. Solr4.8.0源码分析(25)之SolrCloud的Split流程

    Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...

随机推荐

  1. C++标准模板库(STL)——set常见用法详解

    set的定义 set<typename> name; typename可以是任何基本类型,如int.double.char.结构体等,也可以是STL标准容器,如vector.set.que ...

  2. Java8 中使用Stream 让List 转 Map使用总结

    在使用 Java 的新特性 Collectors.toMap() 将 List 转换为 Map 时存在一些不容易发现的问题,这里总结一下备查. 空指针风险 java.lang.NullPointerE ...

  3. 视频质量评估学习Note

    术语"编解码器 Coder/Decoder"是压缩器/解压缩器或编码器/解码器一词的缩写.顾名思义,编码可使视频文件变小以进行存储,然后在需要再次使用时将压缩后的数据转换成可用的图 ...

  4. DarkGreenTrip博客搭建成功

    本博客(https://www.cnblogs.com/zhangshuhao1116)自2021年6月19日由 Shu-How Z  搭建成功,2018年搭建过hexo+next.Wordpress ...

  5. 多图:一文带你入门掌握JVM所有知识点

    本JVM系列属于本人学习过程当中总结的一些知识点,目的是想让读者更快地掌握JVM相关的知识要点,难免会有所侧重,若想要更加系统更加详细的学习JVM知识,还是需要去阅读专业的书籍和文档. 本文主题内容: ...

  6. excel VBA中Xldown和xlup用法

    1.Worksheets("Sheet1").Range("A1").End(xlDown).Select     '意思为自A1起,返回从上往下的最后一个非空 ...

  7. unity的安装,配置,及问题

    下载unity 在官网下载unity unity有三个版本,个人版免费,pro和专业版收费. 个人版 在导出exe文件时不能去掉水印片头.其他版本可以. 地址[https://store.unity. ...

  8. Redis计数信号量

    计数信号量是一种锁,它可以让用户限制一项资源最多能够同时被多少个进程访问,通常用于限定能够同时使用的资源数量.你可以把Redis分布式锁里面创建的锁看作是只能被一个进程访问的信号量. 计数信号量和其他 ...

  9. C++ 11 关键字

    1.auto 我现在用auto,基本是在变量定义时根据初始化表达式自动推断该变量的类型. 另外与关键字 decltype 连用,在声明或定义函数时作为函数返回值的占位符. auto不能用来声明函数的返 ...

  10. 8.QSharedPointer

    QSharedPointer 是一个共享指针, 同时是引用计数型的智能指针 ,也就是说,QSharedPointer可以被自由地拷贝和赋值,在任意的地方共享它. QSharedPointer内部会对拥 ...