注:JDK7和JDK8关于DNS解析的实现有差异,该问题在JDK7下可能不存在;
Java中的DNS解析一般是通过调用下面的方法:

  1. public static InetAddress getByName(String host)
  2. public static InetAddress[] getAllByName(String host)

getByName先调用getAllByName,然后返回地址列表的第一个地址;

下面主要看看getAllByName的实现;

getAllByName

getAllByName会调用getAllByName0方法:

  1. InetAddress[] addresses = getCachedAddresses(host);
  2.  
  3. /* If no entry in cache, then do the host lookup */
  4. if (addresses == null) {
  5. addresses = getAddressesFromNameService(host, reqAddr);
  6. }
  7.  
  8. if (addresses == unknown_array)
  9. throw new UnknownHostException(host);
  10.  
  11. return addresses.clone();

可以看到首先会从缓存中获取,如果缓存找不到则调用getAddressesFromNameService进行解析;

  1. private static InetAddress[] getCachedAddresses(String hostname) {
  2. hostname = hostname.toLowerCase();
  3.  
  4. // search both positive & negative caches
  5.  
  6. synchronized (addressCache) {
  7. cacheInitIfNeeded();//如果是第一次调用,执行初始化
  8.  
  9. CacheEntry entry = addressCache.get(hostname);
  10. if (entry == null) {
  11. entry = negativeCache.get(hostname);
  12. }
  13.  
  14. if (entry != null) {
  15. return entry.addresses;
  16. }
  17. }
  18.  
  19. // not found
  20. return null;
  21. }

既然JDK对IP地址解析有缓存,那么它是如何缓存的呢?缓存策略定义在InetAddressCachePolicy类,摘录其初始化的代码如下:

  1. static {
  2. Integer tmp = java.security.AccessController.doPrivileged(
  3. new PrivilegedAction<Integer>() {
  4. public Integer run() {
  5. try {
  6. //读取JDK目录java.security文件的属性networkaddress.cache.ttl
  7. String tmpString = Security.getProperty(cachePolicyProp);
  8. if (tmpString != null) {
  9. return Integer.valueOf(tmpString);
  10. }
  11. } catch (NumberFormatException ignored) {
  12. // Ignore
  13. }
  14. try {
  15. //读取-D指定的系统属性sun.net.inetaddr.ttl
  16. String tmpString = System.getProperty(cachePolicyPropFallback);
  17. if (tmpString != null) {
  18. return Integer.decode(tmpString);
  19. }
  20. } catch (NumberFormatException ignored) {
  21. // Ignore
  22. }
  23. return null;
  24. }
  25. });
  26.  
  27. if (tmp != null) {
  28. cachePolicy = tmp.intValue();
  29. if (cachePolicy < 0) {
  30. cachePolicy = FOREVER;//如果配置的是负数,表示缓存永不过期
  31. }
  32. propertySet = true;
  33. } else {
  34. //可以通过-Djava.security.manager-Djava.security.policy=security.policy启动安全管理器
  35. if (System.getSecurityManager() == null) {
  36. cachePolicy = DEFAULT_POSITIVE;//默认是不启动SecurityManager的,也就是说默认缓存失效时间为30s
  37. }
  38. }
  39. tmp = java.security.AccessController.doPrivileged (
  40. new PrivilegedAction<Integer>() {
  41. public Integer run() {
  42. try {
  43. //读取networkaddress.cache.negative.ttl属性,默认是10s
  44. String tmpString = Security.getProperty(negativeCachePolicyProp);
  45. if (tmpString != null) {
  46. return Integer.valueOf(tmpString);
  47. }
  48. } catch (NumberFormatException ignored) {
  49. // Ignore
  50. }
  51.  
  52. try {
  53. //读取-D指定的系统属性sun.net.inetaddr.negative.ttl
  54. String tmpString = System.getProperty(negativeCachePolicyPropFallback);
  55. if (tmpString != null) {
  56. return Integer.decode(tmpString);
  57. }
  58. } catch (NumberFormatException ignored) {
  59. // Ignore
  60. }
  61. return null;
  62. }
  63. });
  64.  
  65. if (tmp != null) {
  66. negativeCachePolicy = tmp.intValue();
  67. if (negativeCachePolicy < 0) {
  68. negativeCachePolicy = FOREVER;
  69. }
  70. propertyNegativeSet = true;
  71. }
  72. }

上面介绍了JVM对ip地址解析的缓存策略和相关的配置,接下来看看,如果缓存找不到,JVM该如何解析ip地址;

getAddressesFromNameService

从上面的代码看到,InetAddress会调用getAddressesFromNameService方法,循环调用nameService的lookupAllHostAddr方法,直到找到结果:
NameService的初始化代码如下:

  1. impl = InetAddressImplFactory.create();
  2.  
  3. // get name service if provided and requested
  4. String provider = null;;
  5. String propPrefix = "sun.net.spi.nameservice.provider.";
  6. int n = 1;
  7. nameServices = new ArrayList<NameService>();
  8. //可以通过sun.net.spi.nameservice.provider.n指定自己的DNS
  9. Provider
  10. provider = AccessController.doPrivileged(
  11. new GetPropertyAction(propPrefix + n));
  12. while (provider != null) {
  13. NameService ns = createNSProvider(provider);
  14. if (ns != null)
  15. nameServices.add(ns);
  16.  
  17. n++;
  18. provider = AccessController.doPrivileged(
  19. new GetPropertyAction(propPrefix + n));
  20. }
  21.  
  22. //如果不单独指定,创建默认的NameService
  23. if (nameServices.size() == 0) {//
  24. NameService ns = createNSProvider("default");
  25. nameServices.add(ns);
  26. }

在这里要特别提下Java提供的DNSNameService,该类可以通过下述参数启用:

  1. -Dsun.net.spi.nameservice.provider.1=dns,sun
  2. -Dsun.net.spi.nameservice.nameservers=192.168.1.188

该类会根据sun.net.spi.nameservice.nameservers指定的name server或/etc/resolv.conf文件中配置的name server进行DNS解析;

创建默认的NameService方法代码如下:

  1. if (provider.equals("default")) {
  2. // initialize the default name service
  3. nameService = new NameService() {
  4. public InetAddress[] lookupAllHostAddr(String host)
  5. throws UnknownHostException {
  6. return impl.lookupAllHostAddr(host);
  7. }
  8. public String getHostByAddr(byte[] addr)
  9. throws UnknownHostException {
  10. return impl.getHostByAddr(addr);
  11. }
  12. };
  13. }

根据指定的provider创建NameService的方法如下:

  1. nameService = java.security.AccessController.doPrivileged(
  2. new java.security.PrivilegedExceptionAction<NameService>() {
  3. public NameService run() {
  4. Iterator itr = Service.providers(NameServiceDescriptor.class);
  5. while (itr.hasNext()) {
  6. NameServiceDescriptor nsd
  7. = (NameServiceDescriptor)itr.next();
  8. if (providerName.
  9. equalsIgnoreCase(nsd.getType()+","
  10. +nsd.getProviderName())) {
  11. try {
  12. return nsd.createNameService();
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. System.err.println(
  16. "Cannot create name service:"
  17. +providerName+": " + e);
  18. }
  19. }
  20. }
  21.  
  22. return null;
  23. }
  24. }
  25. );

对于DNSNameServiceDescriptor,其Type和ProviderName分别为dns,sun;

继续看默认Provider的处理逻辑,可以看到其是通过impl.lookupAllHostAddr(host)方法进行解析的,impl的初始化代码为:

  1. impl = InetAddressImplFactory.create();
  2. static InetAddressImpl create() {
  3. return InetAddress.loadImpl(isIPv6Supported() ?
  4. "Inet6AddressImpl" : "Inet4AddressImpl");
  5. }

这里以Inet4AddressImpl为例,说明DNS的解析:

  1. public native InetAddress[]
  2. lookupAllHostAddr(String hostname) throws UnknownHostException;
  3. public native String getHostByAddr(byte[] addr) throws UnknownHostException;

Inet4AddressImp类的方法是native的,是采用本地方法实现的:

  1. JNIEXPORT jobjectArray JNICALL
  2. Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
  3. jstring host) {
  4. const char *hostname;
  5. jobjectArray ret = 0;
  6. int retLen = 0;
  7. int error = 0;
  8. struct addrinfo hints, *res, *resNew = NULL;
  9.  
  10. if (!initializeInetClasses(env))
  11. return NULL;
  12.  
  13. if (IS_NULL(host)) {
  14. JNU_ThrowNullPointerException(env, "host is null");
  15. return 0;
  16. }
  17. hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
  18. CHECK_NULL_RETURN(hostname, NULL);
  19.  
  20. /* Try once, with our static buffer. */
  21. memset(&hints, 0, sizeof(hints));
  22. hints.ai_flags = AI_CANONNAME;
  23. hints.ai_family = AF_INET;
  24.  
  25. error = getaddrinfo(hostname, NULL, &hints, &res);
  26.  
  27. if (error) {
  28. /* report error */
  29. ThrowUnknownHostExceptionWithGaiError(env, hostname, error);
  30. JNU_ReleaseStringPlatformChars(env, host, hostname);
  31. return NULL;
  32. } else {
  33. int i = 0;
  34. struct addrinfo *itr, *last = NULL, *iterator = res;
  35.  
  36. while (iterator != NULL) {
  37. // remove the duplicate one
  38. int skip = 0;
  39. itr = resNew;
  40. while (itr != NULL) {
  41. struct sockaddr_in *addr1, *addr2;
  42. addr1 = (struct sockaddr_in *)iterator->ai_addr;
  43. addr2 = (struct sockaddr_in *)itr->ai_addr;
  44. if (addr1->sin_addr.s_addr ==
  45. addr2->sin_addr.s_addr) {
  46. skip = 1;
  47. break;
  48. }
  49. itr = itr->ai_next;
  50. }
  51.  
  52. if (!skip) {
  53. struct addrinfo *next
  54. = (struct addrinfo*) malloc(sizeof(struct addrinfo));
  55. if (!next) {
  56. JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
  57. ret = NULL;
  58. goto cleanupAndReturn;
  59. }
  60. memcpy(next, iterator, sizeof(struct addrinfo));
  61. next->ai_next = NULL;
  62. if (resNew == NULL) {
  63. resNew = next;
  64. } else {
  65. last->ai_next = next;
  66. }
  67. last = next;
  68. i++;
  69. }
  70. iterator = iterator->ai_next;
  71. }
  72.  
  73. retLen = i;
  74. iterator = resNew;
  75.  
  76. ret = (*env)->NewObjectArray(env, retLen, ni_iacls, NULL);
  77.  
  78. if (IS_NULL(ret)) {
  79. /* we may have memory to free at the end of this */
  80. goto cleanupAndReturn;
  81. }
  82.  
  83. i = 0;
  84. while (iterator != NULL) {
  85. jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
  86. if (IS_NULL(iaObj)) {
  87. ret = NULL;
  88. goto cleanupAndReturn;
  89. }
  90. setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
  91. setInetAddress_hostName(env, iaObj, host);
  92. (*env)->SetObjectArrayElement(env, ret, i++, iaObj);
  93. iterator = iterator->ai_next;
  94. }
  95. }
  96.  
  97. }

上面的代码一大堆,核心是调用getaddrinfo函数,在getaddrinfo的man文档中有这么一句话:

  1. the application should try using the addresses in the order in which they are returned. The sorting function used within getaddrinfo() is defined in RFC 3484; the order can be tweaked for a
  2. particular system by editing /etc/gai.conf (available since glibc 2.5).

getaddrinfo返回的地址列表根据RFC3484规定的排序算法进行了排序,如果这样的话,那么返回的地址列表顺序是规定的,那就达不到负载均衡的目的了;

关于这个排序的话题,网上有很多讨论:

getaddrinfo的部分代码如下:

  1. int getaddrinfo (const char *__restrict name, const char *__restrict service,
  2. const struct addrinfo *__restrict hints,
  3. struct addrinfo **__restrict pai)
  4. {
  5. int i = 0, j = 0, last_i = 0;
  6. int nresults = 0;
  7. struct addrinfo *p = NULL, **end;
  8. struct gaih *g = gaih, *pg = NULL;
  9. struct gaih_service gaih_service, *pservice;
  10. struct addrinfo local_hints;
  11.  
  12. while (g->gaih)
  13. {
  14. if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
  15. {
  16. j++;
  17. if (pg == NULL || pg->gaih != g->gaih)
  18. {
  19. pg = g;
  20. i = g->gaih (name, pservice, hints, end);
  21. if (i != 0)
  22. {
  23. /* EAI_NODATA is a more specific result as it says that
  24. we found a result but it is not usable. */
  25. if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
  26. last_i = i;
  27. if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
  28. {
  29. ++g;
  30. continue;
  31. }
  32. freeaddrinfo (p);
  33. return -(i & GAIH_EAI);
  34. }
  35. if (end)
  36. while (*end)
  37. {
  38. end = &((*end)->ai_next);
  39. ++nresults;
  40. }
  41. }
  42. }
  43. ++g;
  44. }
  45. if (j == 0)
  46. return EAI_FAMILY;
  47. if (nresults > 1)
  48. {
  49. /* Sort results according to RFC 3484. */
  50. struct sort_result results[nresults];
  51. struct addrinfo *q;
  52. struct addrinfo *last = NULL;
  53. char *canonname = NULL;
  54. for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
  55. {
  56. results[i].dest_addr = q;
  57. results[i].got_source_addr = false;
  58. /* If we just looked up the address for a different
  59. protocol, reuse the result. */
  60. if (last != NULL && last->ai_addrlen == q->ai_addrlen
  61. && memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0)
  62. {
  63. memcpy (&results[i].source_addr, &results[i - 1].source_addr,
  64. results[i - 1].source_addr_len);
  65. results[i].source_addr_len = results[i - 1].source_addr_len;
  66. results[i].got_source_addr = results[i - 1].got_source_addr;
  67. }
  68. else
  69. {
  70. /* We overwrite the type with SOCK_DGRAM since we do not
  71. want connect() to connect to the other side. If we
  72. cannot determine the source address remember this
  73. fact. */
  74. int fd = socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
  75. socklen_t sl = sizeof (results[i].source_addr);
  76. if (fd != -1
  77. && connect (fd, q->ai_addr, q->ai_addrlen) == 0
  78. && getsockname (fd,
  79. (struct sockaddr *) &results[i].source_addr,
  80. &sl) == 0)
  81. {
  82. results[i].source_addr_len = sl;
  83. results[i].got_source_addr = true;
  84. }
  85. else
  86. /* Just make sure that if we have to process the same
  87. address again we do not copy any memory. */
  88. results[i].source_addr_len = 0;
  89. if (fd != -1)
  90. close_not_cancel_no_status (fd);
  91. }
  92. /* Remember the canonical name. */
  93. if (q->ai_canonname != NULL)
  94. {
  95. assert (canonname == NULL);
  96. canonname = q->ai_canonname;
  97. q->ai_canonname = NULL;
  98. }
  99. }
  100. /* We got all the source addresses we can get, now sort using
  101. the information. */
  102. qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
  103. /* Queue the results up as they come out of sorting. */
  104. q = p = results[0].dest_addr;
  105. for (i = 1; i < nresults; ++i)
  106. q = q->ai_next = results[i].dest_addr;
  107. q->ai_next = NULL;
  108. /* Fill in the canonical name into the new first entry. */
  109. p->ai_canonname = canonname;
  110. }
  111. if (p)
  112. {
  113. *pai = p;
  114. return 0;
  115. }
  116. if (pai == NULL && last_i == 0)
  117. return 0;
  118. return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
  119. }

排序是通过rfc3484_sort完成的,后面有时间准备仔细看看其排序规则:

  1. static int
  2. rfc3484_sort (const void *p1, const void *p2)
  3. {
  4. const struct sort_result *a1 = (const struct sort_result *) p1;
  5. const struct sort_result *a2 = (const struct sort_result *) p2;
  6. /* Rule 1: Avoid unusable destinations.
  7. We have the got_source_addr flag set if the destination is reachable. */
  8. if (a1->got_source_addr && ! a2->got_source_addr)
  9. return -1;
  10. if (! a1->got_source_addr && a2->got_source_addr)
  11. return 1;
  12. /* Rule 2: Prefer matching scope. Only interesting if both
  13. destination addresses are IPv6. */
  14. int a1_dst_scope
  15. = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
  16. int a2_dst_scope
  17. = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
  18. if (a1->got_source_addr)
  19. {
  20. int a1_src_scope = get_scope (&a1->source_addr);
  21. int a2_src_scope = get_scope (&a2->source_addr);
  22. if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope)
  23. return -1;
  24. if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope)
  25. return 1;
  26. }
  27. /* Rule 3: Avoid deprecated addresses.
  28. That's something only the kernel could decide. */
  29. /* Rule 4: Prefer home addresses.
  30. Another thing only the kernel can decide. */
  31. /* Rule 5: Prefer matching label. */
  32. if (a1->got_source_addr)
  33. {
  34. int a1_dst_label
  35. = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
  36. int a1_src_label = get_label (&a1->source_addr);
  37. int a2_dst_label
  38. = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
  39. int a2_src_label = get_label (&a2->source_addr);
  40. if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label)
  41. return -1;
  42. if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label)
  43. return 1;
  44. }
  45. /* Rule 6: Prefer higher precedence. */
  46. int a1_prec
  47. = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
  48. int a2_prec
  49. = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
  50. if (a1_prec > a2_prec)
  51. return -1;
  52. if (a1_prec < a2_prec)
  53. return 1;
  54. /* Rule 7: Prefer native transport.
  55. XXX How to recognize tunnels? */
  56. /* Rule 8: Prefer smaller scope. */
  57. if (a1_dst_scope < a2_dst_scope)
  58. return -1;
  59. if (a1_dst_scope > a2_dst_scope)
  60. return 1;
  61. /* Rule 9: Use longest matching prefix. */
  62. if (a1->got_source_addr
  63. && a1->dest_addr->ai_family == a2->dest_addr->ai_family)
  64. {
  65. int bit1 = 0;
  66. int bit2 = 0;
  67. if (a1->dest_addr->ai_family == PF_INET)
  68. {
  69. assert (a1->source_addr.ss_family == PF_INET);
  70. assert (a2->source_addr.ss_family == PF_INET);
  71. struct sockaddr_in *in1_dst;
  72. struct sockaddr_in *in1_src;
  73. struct sockaddr_in *in2_dst;
  74. struct sockaddr_in *in2_src;
  75. in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
  76. in1_src = (struct sockaddr_in *) &a1->source_addr;
  77. in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
  78. in2_src = (struct sockaddr_in *) &a2->source_addr;
  79. bit1 = ffs (in1_dst->sin_addr.s_addr ^ in1_src->sin_addr.s_addr);
  80. bit2 = ffs (in2_dst->sin_addr.s_addr ^ in2_src->sin_addr.s_addr);
  81. }
  82. else if (a1->dest_addr->ai_family == PF_INET6)
  83. {
  84. assert (a1->source_addr.ss_family == PF_INET6);
  85. assert (a2->source_addr.ss_family == PF_INET6);
  86. struct sockaddr_in6 *in1_dst;
  87. struct sockaddr_in6 *in1_src;
  88. struct sockaddr_in6 *in2_dst;
  89. struct sockaddr_in6 *in2_src;
  90. in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr;
  91. in1_src = (struct sockaddr_in6 *) &a1->source_addr;
  92. in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr;
  93. in2_src = (struct sockaddr_in6 *) &a2->source_addr;
  94. int i;
  95. for (i = 0; i < 4; ++i)
  96. if (in1_dst->sin6_addr.s6_addr32[i]
  97. != in1_src->sin6_addr.s6_addr32[i]
  98. || (in2_dst->sin6_addr.s6_addr32[i]
  99. != in2_src->sin6_addr.s6_addr32[i]))
  100. break;
  101. if (i < 4)
  102. {
  103. bit1 = ffs (in1_dst->sin6_addr.s6_addr32[i]
  104. ^ in1_src->sin6_addr.s6_addr32[i]);
  105. bit2 = ffs (in2_dst->sin6_addr.s6_addr32[i]
  106. ^ in2_src->sin6_addr.s6_addr32[i]);
  107. }
  108. }
  109. if (bit1 > bit2)
  110. return -1;
  111. if (bit1 < bit2)
  112. return 1;
  113. }
  114. /* Rule 10: Otherwise, leave the order unchanged. */
  115. return 0;
  116. }

可以看到,首先根据RFC3484的Rule1~Rule9排序,如果上述规则都未触发,则返回原列表;简单的说,返回结果的顺序是不固定的,有可能是DNS Server返回的顺序,也有可能不是;因此最好的办法是在Java层自己进行控制;

转自:https://www.jianshu.com/p/f10808ae4b60
作者:allanYan

Jdk8 DNS解析的更多相关文章

  1. DNS解析过程详解

    先说一下DNS的几个基本概念: 一. 根域 就是所谓的“.”,其实我们的网址www.baidu.com在配置当中应该是www.baidu.com.(最后有一点),一般我们在浏览器里输入时会省略后面的点 ...

  2. C#实现DNS解析服务和智能DNS服务

    C#实现DNS解析服务有一个开源项目ARSoft.Tools.Net, ARSoft.Tools.Net是一个非常强大的开源DNS控件库,包含.Net SPF validation, SenderID ...

  3. 解决域名DNS解析的故障

    在实际应用过程中可能会遇到DNS解析错误的问题,就是说当我们访问一个域名时无法完成将其解析到IP地址的工作,而直接输入网站IP却可以正常访问,这就是因为DNS解析出现故障造成的.这个现象发生的机率比较 ...

  4. LINUX DNS解析的3种修改方法~

    1.HOST 本地DNS解析 vi /etc/hosts 添加规则 例如: 223.231.234.33 www.baidu.com 2.网卡配置文件DNS服务地址  vi /etc/sysconfi ...

  5. DNS解析过程和域名收敛、域名发散、SPDY应用

    前段时间项目要做域名收敛,糊里糊涂的完成了,好多原理不清晰,现在整理搜集下知识点. 域名收敛的目的是什么?简单来说就是域名解析慢.那为什么解析慢?且听下文慢慢道来. 什么是DNS? DNS( Doma ...

  6. 通过统计用户DNS解析记录,实现监控用户上网行为

    上次通过扫描抓包分析TTL的方式检测公司网络开放的端口,发现没有开放53端口(DNS),也就是在公司内部的主机只能用服务器自动分配的DNS,并且发现这是台内部服务器.今天发现bing上不去,检测后发现 ...

  7. DNS解析全过程及原理

    DNS解析原理及过程. 当用户访问我们网站一个网页时,他需要经过以下步骤: 1)找到这个网页的存放服务器: 2)服务器将用户的请求信息接入: 3)服务器通过文件路径(URL)查找用户请求网页: 4)用 ...

  8. 使用dig查询dns解析

    原文地址:使用dig查询dns解析 作者:chenwenming 一般来说linux下查询域名解析有两种选择,nslookup或者dig,而在使用上我觉得dig更加方便顺手. 如果是在debian下的 ...

  9. Dnsmasq安装与配置-搭建本地DNS服务器 更干净更快无广告DNS解析

    默认的情况下,我们平时上网用的本地DNS服务器都是使用电信或者联通的,但是这样也导致了不少的问题,首当其冲的就是上网时经常莫名地弹出广告,或者莫名的流量被消耗掉导致网速变慢.其次是部分网站域名不能正常 ...

随机推荐

  1. Matlab解释器模式

    解释器模式(Interperter),给定一个语言,定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,实际开发中EL表达式或者正则表达式的解释器就是采用这种设计模式.其模式结构如下图.本文使 ...

  2. Java 之 Set 接口

    一.Set 概述 java.util.Set 接口继承 collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Co ...

  3. openssl生成证书及签名

    第一步,生成私钥 $ openssl genrsa -out privatekey.pem 2048 查看生成的私钥内容 $ file privatekey.pem privatekey.pem: P ...

  4. mysql修改密码策略

      版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/Hello_World_QWP/arti ...

  5. springboot配置tomcat大全

    server.tomcat.accept-count=100 # Maximum queue length for incoming connection requests when all poss ...

  6. 浅谈Python设计模式 - 抽象工厂模式

    声明:本系列文章主要参考<精通Python设计模式>一书,并且参考一些资料,结合自己的一些看法来总结而来. 在上一篇我们对工厂模式中的普通工厂模式有了一定的了解,其实抽象工作就是 表示针对 ...

  7. crontab 定时任务简单备份数据库

    备份数据库/usr/local/mysql5.5/bin/mysqldump -uroot -p1234abcd wordpress >~/wordpress_20151206.sql 59 2 ...

  8. 多态典型用例之virtual

    多态典型用例之virtual 参考:https://www.cnblogs.com/dormant/p/5223215.html 1.虚函数(virtual) (1)在某基类中声明为 virtual ...

  9. Pat 1003 甲级

    #include <cstdlib> #include <cstring> #include <iostream> #include <cstdio> ...

  10. thrift rpc通信

    thrift rpc通信 框架 别人的简历: 负责抓取程序的开发和维护,对抓取内容进行数据提取.整理.1.定向数据抓取程序的维护和开发,了解了Sqlite数据库.Thrift服务和多线程的开发调试.2 ...