Làm thế nào để Java tuân theo thời gian chờ của bộ đệm DNS?


101

Chúng tôi sử dụng GSLB để phân phối theo địa lý và cân bằng tải. Mỗi dịch vụ được gán một tên miền cố định. Thông qua một số phép thuật DNS, tên miền được phân giải thành một IP gần máy chủ nhất với tải ít nhất. Để cân bằng tải hoạt động, máy chủ ứng dụng cần tuân theo TTL từ phản hồi DNS và phân giải lại tên miền khi bộ nhớ cache hết thời gian. Tuy nhiên, tôi không thể tìm ra cách để làm điều này trong Java.

Ứng dụng này là Java 5, chạy trên Linux (Centos 5).

Câu trả lời:


76

Theo câu trả lời của Byron, bạn không thể đặt networkaddress.cache.ttlhoặc networkaddress.cache.negative.ttllàm Thuộc tính Hệ thống bằng cách sử dụng -Dcờ hoặc gọi System.setPropertyvì đây không phải là thuộc tính Hệ thống - chúng là thuộc tính Bảo mật .

Nếu bạn muốn sử dụng thuộc tính Hệ thống để kích hoạt hành vi này (để bạn có thể sử dụng -Dcờ hoặc lệnh gọi System.setProperty), bạn sẽ muốn đặt thuộc tính Hệ thống sau :

-Dsun.net.inetaddr.ttl=0

Thuộc tính hệ thống này sẽ kích hoạt hiệu ứng mong muốn.

Nhưng hãy lưu ý: nếu bạn không sử dụng -Dcờ khi bắt đầu quy trình JVM và chọn gọi điều này từ mã thay thế:

java.security.Security.setProperty("networkaddress.cache.ttl" , "0")

Mã này phải thực thi trước khi bất kỳ mã nào khác trong JVM cố gắng thực hiện các hoạt động mạng.

Điều này quan trọng bởi vì, ví dụ: nếu bạn đã gọi Security.setPropertytrong tệp .war và triển khai .war đó cho Tomcat, điều này sẽ không hoạt động: Tomcat sử dụng ngăn xếp mạng Java để tự khởi tạo sớm hơn nhiều so với mã .war của bạn được thực thi. Do 'điều kiện cuộc đua' này, thường sẽ thuận tiện hơn khi sử dụng -Dcờ khi bắt đầu quá trình JVM.

Nếu bạn không sử dụng -Dsun.net.inetaddr.ttl=0hoặc gọi Security.setProperty, bạn sẽ cần chỉnh sửa $JRE_HOME/lib/security/java.securityvà đặt các thuộc tính bảo mật đó trong tệp đó, ví dụ:

networkaddress.cache.ttl = 0
networkaddress.cache.negative.ttl = 0

Nhưng hãy chú ý đến các cảnh báo bảo mật trong các bình luận xung quanh các thuộc tính đó. Chỉ làm điều này nếu bạn chắc chắn rằng bạn không dễ bị tấn công giả mạo DNS .


2
FQN là java.security.Security(ít nhất là ở jdk7)
Pablo Fernandez.

1
Chỉ là một nhận xét, những cảnh báo bảo mật đó chủ yếu liên quan đến trình quản lý bảo mật và tải từ xa. Đối với bất kỳ ứng dụng máy chủ thông thường nào tin tưởng DNS ở một số phần mở rộng, giảm TTL là tốt. (Tuy nhiên, tôi không nghĩ rằng 0 là mức tối thiểu tốt và mặc định là 30s đối với những người quản lý không bảo mật là tốt trong hầu hết các trường hợp).
eckes

3
Thuộc tính hệ thống có hoạt động với OpenJDK hay là đặc biệt của Oracle không?
mhlz

67

Java có một số hành vi bộ nhớ đệm dns kỳ lạ nghiêm trọng. Đặt cược tốt nhất của bạn là tắt bộ nhớ đệm dns hoặc đặt nó ở một số thấp như 5 giây.

networkaddress.cache.ttl (mặc định: -1)
Chỉ ra chính sách bộ nhớ đệm để tra cứu tên thành công từ dịch vụ tên. Giá trị được chỉ định dưới dạng số nguyên để cho biết số giây để lưu vào bộ nhớ cache việc tra cứu thành công. Giá trị -1 cho biết "bộ nhớ cache mãi mãi".

networkaddress.cache.negative.ttl (mặc định: 10)
Chỉ ra chính sách lưu vào bộ nhớ đệm cho các lần tra cứu tên không thành công từ dịch vụ tên. Giá trị được chỉ định dưới dạng số nguyên để cho biết số giây để lưu lỗi vào bộ nhớ cache đối với các tra cứu không thành công. Giá trị 0 cho biết "không bao giờ lưu vào bộ nhớ cache". Giá trị -1 cho biết "bộ nhớ cache mãi mãi".


7
Lưu ý: điều này không vô hiệu hóa tất cả bộ đệm DNS trong hệ điều hành của bạn. Chỉ cần tắt bộ nhớ đệm trong bộ nhớ bị hỏng của Java trong thư viện. Bạn có thể chỉ cần đặt các thuộc tính này trên dòng lệnh khi bạn gọi JVM.
Nelson

2
Tôi không biết rằng "bị hỏng" là hợp lệ. Java (vì lý do bảo mật) lưu trữ vĩnh viễn các mục nhập DNS hoặc cho đến khi JVM được khởi động lại, tùy điều kiện nào đến trước. Điều này (theo những gì tôi có thể nói) là do thiết kế. Cài đặt có thể được thực hiện trong tệp chính sách java.security hoặc tại dòng lệnh. Các cài đặt khác nhau cho từng loại. Tham khảo: rgagnon.com/javadetails/java-0445.html
Milner

4
Lưu ý rằng bạn không thể đặt chúng làm thuộc tính Hệ thống (tức là sử dụng cờ -D hoặc System.setProperty) vì chúng không phải thuộc tính hệ thống - chúng là thuộc tính bảo mật.
Les Hazlewood

6
Tài liệu này hơi khác trong 1.7. Cụ thể, bộ nhớ cache vĩnh viễn hiện chỉ xảy ra khi có trình quản lý bảo mật: "Hành vi mặc định là lưu vào bộ nhớ cache vĩnh viễn khi trình quản lý bảo mật được cài đặt và lưu vào bộ nhớ cache trong một khoảng thời gian cụ thể khi chưa cài đặt trình quản lý bảo mật." docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken

1
@Michael xem System.getSecurityManager(). Docs cho Java 8: docs.oracle.com/javase/8/docs/api/java/lang/…
gesellix 28/02/17

22

Điều này rõ ràng đã được khắc phục trong các bản phát hành mới hơn (SE 6 và 7). Tôi gặp phải thời gian lưu vào bộ nhớ đệm tối đa là 30 giây khi chạy đoạn mã sau trong khi xem hoạt động của cổng 53 bằng tcpdump.

/**
 * http://stackoverflow.com/questions/1256556/any-way-to-make-java-honor-the-dns-caching-timeout-ttl
 *
 * Result: Java 6 distributed with Ubuntu 12.04 and Java 7 u15 downloaded from Oracle have
 * an expiry time for dns lookups of approx. 30 seconds.
 */

import java.util.*;
import java.text.*;
import java.security.*;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class Test {
    final static String hostname = "www.google.com";
    public static void main(String[] args) {
        // only required for Java SE 5 and lower:
        //Security.setProperty("networkaddress.cache.ttl", "30");

        System.out.println(Security.getProperty("networkaddress.cache.ttl"));
        System.out.println(System.getProperty("networkaddress.cache.ttl"));
        System.out.println(Security.getProperty("networkaddress.cache.negative.ttl"));
        System.out.println(System.getProperty("networkaddress.cache.negative.ttl"));

        while(true) {
            int i = 0;
            try {
                makeRequest();
                InetAddress inetAddress = InetAddress.getLocalHost();
                System.out.println(new Date());
                inetAddress = InetAddress.getByName(hostname);
                displayStuff(hostname, inetAddress);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(5L*1000L);
            } catch(Exception ex) {}
            i++;
        }
    }

    public static void displayStuff(String whichHost, InetAddress inetAddress) {
        System.out.println("Which Host:" + whichHost);
        System.out.println("Canonical Host Name:" + inetAddress.getCanonicalHostName());
        System.out.println("Host Name:" + inetAddress.getHostName());
        System.out.println("Host Address:" + inetAddress.getHostAddress());
    }

    public static void makeRequest() {
        try {
            URL url = new URL("http://"+hostname+"/");
            URLConnection conn = url.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();
            InputStreamReader ird = new InputStreamReader(is);
            BufferedReader rd = new BufferedReader(ird);
            String res;
            while((res = rd.readLine()) != null) {
                System.out.println(res);
                break;
            }
            rd.close();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

16
Có, Java 1.5 có giá trị mặc định là bộ nhớ đệm vô hạn. Java 1.6 và 1.7 có mặc định 30 giây.
Michael

7
Tài liệu cho 1.7 chỉ ra điều này chỉ có thể đúng trong trường hợp không có trình quản lý bảo mật: "Hành vi mặc định là lưu vào bộ nhớ cache vĩnh viễn khi trình quản lý bảo mật được cài đặt và lưu vào bộ nhớ cache trong một khoảng thời gian cụ thể khi triển khai trình quản lý chưa được cài đặt. " docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken

1
@Michael quan tâm chia sẻ nguồn cho thông tin đó?
gỉyx

4
@rustyx JDK 1.6 và 1.7 của Oracle có điều này trong jre / lib / security / java.security cho networkaddress.cache.ttl: "# giá trị mặc định là mãi mãi (FOREVER). Vì lý do bảo mật, # bộ nhớ đệm này được tạo vĩnh viễn khi người quản lý bảo mật được đặt. Khi chưa đặt trình quản lý bảo mật #, hành vi mặc định là lưu vào bộ nhớ cache trong 30 giây. " Vì vậy, các applet và ứng dụng được triển khai qua Java Web Start vẫn lưu vào bộ nhớ cache vĩnh viễn nếu không sẽ là 30 giây.
Michael

1
Đây là một con trỏ mã đến java.security của OpenJDK 8, cho biết rằng không có trình quản lý bảo mật, TTL là 30 giây: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f940e7a48b72/src/share/… . Tôi đã thử nghiệm điều này trên Mac OS X và Ubuntu 14.04.
tro

18

Để mở rộng câu trả lời của Byron, tôi tin rằng bạn cần chỉnh sửa tệp java.securitytrong %JRE_HOME%\lib\securitythư mục để thực hiện thay đổi này.

Đây là phần liên quan:

#
# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#
# default value is forever (FOREVER). For security reasons, this
# caching is made forever when a security manager is set. When a security
# manager is not set, the default behavior is to cache for 30 seconds.
#
# NOTE: setting this to anything other than the default value can have
#       serious security implications. Do not set it unless 
#       you are sure you are not exposed to DNS spoofing attack.
#
#networkaddress.cache.ttl=-1 

Tài liệu về java.securitytệp tại đây .


5
Để thêm vào điều này, khi sử dụng tomcat6, tôi phải sửa đổi tệp lib / security của mình, vì việc đặt networkaddress.cache.ttl hoặc sun.net.inetaddr.ttl theo chương trình hoặc thông qua biến JAVA_OPTS không hoạt động.
bramp,

1
@bramp Cảm ơn người anh em, tôi cũng đang gặp phải vấn đề tương tự và được giải quyết bằng cách sử dụng nhận xét và câu trả lời của bạn +1 để nhận xét và trả lời.
Bhavik Ambani

7

Để tóm tắt các câu trả lời khác, <jre-path>/lib/security/java.securitybạn có thể đặt giá trị của thuộc tính networkaddress.cache.ttlđể điều chỉnh cách lưu trữ các tra cứu DNS. Lưu ý rằng đây không phải là thuộc tính hệ thống mà là thuộc tính bảo mật. Tôi đã có thể thiết lập điều này bằng cách sử dụng:

java.security.Security.setProperty("networkaddress.cache.ttl", "<value>");

Điều này cũng có thể được đặt bởi thuộc tính hệ thống -Dsun.net.inetaddr.ttlmặc dù điều này sẽ không ghi đè một thuộc tính bảo mật nếu nó được đặt ở nơi khác.

Tôi cũng muốn nói thêm rằng nếu bạn gặp sự cố này với các dịch vụ web trong WebSphere, như tôi đã từng làm, thì cài đặt networkaddress.cache.ttlsẽ không đủ. Bạn cần đặt thuộc tính hệ thống disableWSAddressCachingthành true. Không giống như thuộc tính thời gian tồn tại, điều này có thể được đặt làm đối số JVM hoặc qua System.setProperty).

IBM có một bài đăng khá chi tiết về cách WebSphere xử lý bộ nhớ đệm DNS tại đây . Phần liên quan đến phần trên là:

Để vô hiệu hóa bộ đệm địa chỉ cho các dịch vụ Web, bạn cần đặt thêm thuộc tính tùy chỉnh JVM vô hiệu hóa WSAddressCaching thành true. Sử dụng thuộc tính này để tắt bộ nhớ đệm địa chỉ cho các dịch vụ Web. Nếu hệ thống của bạn thường chạy với nhiều luồng máy khách và bạn gặp phải tranh chấp khóa trên bộ đệm wsAddrCache, bạn có thể đặt thuộc tính tùy chỉnh này thành true, để ngăn chặn dữ liệu dịch vụ Web vào bộ nhớ đệm.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.