fork 同时也存在很多问题,首先第一个是成本: Linux 上的 fork 调用看起来很快,但它会将你所有的内存标记为 copy-on-write。每次写入 copy-on-write 页面都会导致一个小的页面错误,这是一个很难测量的小延迟,进程之间的上下文切换也很昂贵。
另一个问题是规模: 很难在大量子进程中协调共享资源(如 CPU、内存、数据库连接等)的使用。如果流量激增,并且创建了太多进程,那么它们将相互争夺 CPU。但是如果限制创建的进程数量,那么在 CPU 空闲时,大量缓慢的客户端可能会阻塞每个人的正常使用,这时使用超时机制会有所帮助(无论服务器架构如何,超时设置都是很必要的)。
通过使用线程而不是进程,上面这些问题在一定程度上能得到缓解。创建线程比创建进程更“便宜”,因为它共享内存和大多数其它资源。在共享地址空间中,线程之间的通信也相对容易,使用信号量和其它结构来管理共享资源,然而,线程仍然有很大的成本,如果你为每个连接创建一个新线程,你会遇到扩展问题。与进程一样,你此时需要限制正在运行的线程的数量,以避免严重的 CPU 争用,并且需要使慢速请求超时。创建一个新线程仍然需要时间,尽管可以通过使用线程池在请求之间回收线程来缓解这一问题。
这是由于 TCP 的时延机制(因为系统内核并不知道应用能不能立即关闭),当被挥手端(这里是 server 的 443 端口)第一次收到挥手端(这里是 client 的 63612 端口)的 FIN 请求时,并不会立即发送 ACK,而是会经过一段延迟时间后再发送,但是此时被挥手端也没有数据发送,就会向挥手端发送 FIN 请求,这里就可能造成被挥手端发送的 FIN 与 ACK 一起被挥手端收到,导致出现第二、三次挥手合并为一次的现象,也就最终呈现出“三次挥手”的情况。
断开连接四次挥手分为如下四步(假设没有出现挥手合并的情况):
第一步,client 端主动发送 FIN 包给 server 端;
第二步,server 端回复 ACK(对应第一步 FIN 包的 ACK)给 client,表示 server 知道 client 端要断开了;
第三步,server 端发送 FIN 包给 client 端,表示 server 端也没有数据要发送了,可以断开了;
第四步,client 端回复 ACK 包给 server 端,表示既然双发都已发送 FIN 包表示可以断开,那么就真的断开了啊。
你再仔细回看上面三次握手的第二步(SYN + ACK),其实是可以拆分为两步的:第一步回复 ACK,第二步再发 SYN 也是完全可以的,只是效率会比较低,这样的话三次握手不也变成四次握手了。
看起来四次挥手主要是收到第一个 FIN 包后单独回复了一个 ACK 包这里多了一次,如果能像握手那样也回复 FIN + ACK 那么四次挥手也就变成三次了。这里再贴一下上面这个挥手的抓包图:
这个图中第二个红框就是 server 端回复的 FIN + ACK 包,这样四次挥手变成三次了(如果一个包算一次的话)。这里使用四次挥手原因主要是:被动关闭端在收到 FIN 后,知道主动关闭端要关闭了,然后系统内核层会通知应用层要关闭,此时应用层可能还需要做些关闭前的准备工作,可能还有数据没发送完,所以系统内核先回复一个 ACK 包,然后等应用层准备好了主动调 close 关闭时再发 FIN 包。
而握手过程中就没有这个准备过程了,所以可以立即发送 SYN + ACK(在这里的两步合成一步了,提高效率)。挥手过程中系统内核在收到对方的 FIN 后,只能 ACK,不能主动替应用来 FIN,因为系统内核并不知道应用能不能立即关闭。
总结
TCP 是一个很复杂的协议,为了实现可靠传输以及处理各种网络传输中的 N 多问题,有一些很经典的解决方案,比如其中的网络拥塞控制算法、滑动窗口、数据重传等。强烈建议你去读一下 rfc793 和 TCP/IP 详解 卷1:协议 这本书。
The Java memory model describes how threads in the Java programming language interact through memory. Together with the description of single-threaded execution of code, the memory model provides the semantics of the Java programming language.
在文章开头,提到了 JMM 主要是为了解决可见性和有序性问题,那么首先就要先搞清楚,导致可见性和有序性问题发生的本质原因是什么?现在的服务绝大部分都是运行在多核 CPU 的服务器上,每颗 CPU 都有自己的缓存,这时 CPU 缓存与内存的数据就会有一致性问题了,当一个线程对共享变量的修改,另外一个线程无法立刻看到。导致可见性问题的本质原因是缓存。
if (problem is small) { directly solve problem. } else { Step 1. split problem into independent parts. Step 2. fork new subtasks to solve each part. Step 3. join all subtasks. Step 4. compose result from subresults. }
/** * @param <T> the target type of the Feign client * @return a {@link Feign} client created with the specified data and the context * information */ <T> T getTarget(){ // 从 Spring 容器中获取 FeignContext Bean FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class) : applicationContext.getBean(FeignContext.class); // 根据获取到的 FeignContext 构建出 Feign.Builder Feign.Builder builder = feign(context);
// 注解 @FeignClient 未指定 url 属性 if (!StringUtils.hasText(url)) { // url 属性是固定访问某一个实例地址,如果未指定协议则拼接 http 请求协议 if (!name.startsWith("http")) { url = "http://" + name; } else { url = name; } // 格式化 url url += cleanPath(); // 生成代理和我们之前的代理一样,注解 @FeignClient 未指定 url 属性则返回一个带有负载均衡功能的客户端对象 return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url)); } // 注解 @FeignClient 已指定 url 属性 if (StringUtils.hasText(url) && !url.startsWith("http")) { url = "http://" + url; } String url = this.url + cleanPath(); // 获取一个 client Client client = getOptional(context, Client.class); if (client != null) { if (client instanceof FeignBlockingLoadBalancerClient) { // not load balancing because we have a url, // but Spring Cloud LoadBalancer is on the classpath, so unwrap // 这里没有负载是因为我们有指定了 url client = ((FeignBlockingLoadBalancerClient) client).getDelegate(); } builder.client(client); } // 生成代理和我们之前的代理一样,最后被注入到 Spring 容器中 Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url)); }
搜索能力是被绝大多数人低估一项基本素质,绝大部分做编程技术相关的朋友应该都知道如何使用 Google,但是并不知道如何利用它的潜力。其实不管是 Google 还是 百度,会搜索的人一样都可以查找到需要的东西,不会搜索的人用什么都不好使。下面介绍一些 Google 常用的搜索技巧以及搜索快捷方式,可以帮助你更快,更准确地找到结果。Google 是世界上功能最强大的搜索引擎,它已经改变了我们查找信息的方式。
0. 使用准确的词组
将您要搜索的关键字用引号引起来,Google 会进行精确的词组搜索。
语法:”[searchkey 1] [searchkey 2]” [searchkey 3]
1. 多个互斥的搜索条件使用 OR
默认情况下,除非指定,否则 Google 会包含你搜索条件中的所有搜索关键字。通过在您的关键词之字输入OR,Google 会知道它可以查找一组或另一组。大写 OR,否则 Google 会认为它只是你的关键字的一部分。
/* * Constants for CGLIB callback array indices */ privatestaticfinalint AOP_PROXY = 0;
protectedfinal Advised advised;
publicCglibProxyFactory(Advised config){ Assert.notNull(config, "AdvisedSupport must not be null"); if (config.getAdvices().size() == 0) { thrownew AopConfigException("No advisors and no TargetSource specified"); }
this.advised = config; }
@Override public Object getProxy(){ return getProxy(null); }
@Override public Object getProxy(ClassLoader classLoader){ if (logger.isDebugEnabled()) { logger.debug("Creating CGLIB proxy: target class is " + this.advised.getTargetClass()); }
Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int i = 0; i < types.length; i++) { types[i] = callbacks[i].getClass(); } enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised)); enhancer.setCallbackTypes(types); enhancer.setCallbacks(callbacks);
// Generate the proxy class and create a proxy instance. return enhancer.create(); } catch (CodeGenerationException | IllegalArgumentException ex) { thrownew AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex); } catch (Exception ex) { // TargetSource.getTarget() failed thrownew AopConfigException("Unexpected AOP exception", ex); } }
@Override public List<Advice> getAdvices(Method method){ List<Advice> result = new ArrayList<>(); for (Advice advice : this.getAdvices()) { Pointcut pc = advice.getPointcut(); if (pc.getMethodMatcher().matches(method)) { result.add(advice); } } return result; }
本文是 如何实现一个简易版的 Spring 系列第四篇,在 上篇 介绍了 @Component 注解的实现,这篇再来看看在使用 Spring 框架开发中常用的 @Autowired 注入要如何实现,大家用过 Spring 都知道,该注解可以用在字段、构造函数以及setter 方法上,限于篇幅原因我们主要讨论用在字段的方式实现,其它的使用方式大体思路是相同的,不同的只是解析和注入方式有所区别,话不多说,下面进入我们今天的正题—如何实现一个简易版的 Spring - 如何实现 @Autowired 注解。
publicDependencyDescriptor(Field field, boolean required){ Assert.notNull(field, "Field must not be null"); this.field = field; this.required = required; }
public Class<?> getDependencyType() { if (this.field != null) { return field.getType(); } thrownew RuntimeException("only support field dependency"); }