下面是一段簡(jiǎn)單的判斷一個(gè)ip是否在一組ip中的函數(shù),其中ip和ip組的表達(dá)方式可以為這樣(ip="192.3.22.34",iplist="123.21.23.11,121.2.11.234,12.*.23.*"),下面紅色部分是開始一個(gè)同學(xué)寫的,藍(lán)色部分是后來我嘗試修改的。要說明的就以下幾點(diǎn):
1.很多時(shí)候優(yōu)化可以在海量運(yùn)算之前做好準(zhǔn)備,例如分析的數(shù)據(jù)先做預(yù)處理,因?yàn)橥勺x性和計(jì)算效率會(huì)有沖突。
2.如果沒有緩存,那么構(gòu)建利于高效計(jì)算的代價(jià)可能超出簡(jiǎn)單但計(jì)算效率一般的情況。
3.在數(shù)據(jù)量很大的情況下,如何節(jié)省存儲(chǔ)直接決定你是否能夠緩存這些數(shù)據(jù)用于下次計(jì)算。
有興趣的同學(xué)可以執(zhí)行以下下面一些代碼(拷貝到eclipse格式化看和執(zhí)行比較好):
改動(dòng)的主要內(nèi)容在于對(duì)數(shù)據(jù)作預(yù)處理時(shí)增加了一點(diǎn)轉(zhuǎn)換,6位byte作為支持ipv6的存儲(chǔ),第7位作為標(biāo)識(shí)位來便于避免不需要的比對(duì)。
用到了org.apache.commons.lang.StringUtils,它的split方法就是采用簡(jiǎn)單的substring而不是pattern,效率很高,特別對(duì)于海量計(jì)算。
具體的測(cè)試代碼:
public static void main(String[] args)
{
long beg = System.currentTimeMillis();
//下面這些代碼是用于存粹比較計(jì)算時(shí),對(duì)白名單做預(yù)編譯和緩存
byte[][] whiteIps = prepare("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*");
String[] allowIps = StringUtils.split("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*",",");
List<String[]> whiteIps2 = new ArrayList<String[]>();
if (null != allowIps && allowIps.length > 0) {
for (int i = 0; i < allowIps.length; i++) {
//把每個(gè)ip按“.”拆開,得到一個(gè)四位的數(shù)組
String[] ipParse = StringUtils.split(allowIps[i], ".");
whiteIps2.add(ipParse);
}
}
for(int i = 0 ; i < 1000000 ; i++)
{
//第一組對(duì)比,增加了多一點(diǎn)的預(yù)處理,性能下降
//checkAppIpWhite("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.19");
//checkAppIpWhite("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.11");
//checkAppIpWhiteV2("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.19");
//checkAppIpWhiteV2("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.11");
//存粹的比較計(jì)算,性能上升
// checkAppIpWhite(whiteIps2,"12.24.23.19");
// checkAppIpWhite(whiteIps2,"12.24.23.11");
checkAppIpWhiteV2(whiteIps,"12.24.23.19");
checkAppIpWhiteV2(whiteIps,"12.24.23.11");
}
System.out.println("end test : " + (System.currentTimeMillis() - beg));
}
老的比較代碼函數(shù)如下:
public static boolean checkAppIpWhite(String ipWhilte,String remoteIp){
if(StringUtils.isEmpty(ipWhilte) || StringUtils.isEmpty(remoteIp)){
return true;
}
String[] allowIps = StringUtils.split(ipWhilte,",");
List<String[]> whiteIps = new ArrayList<String[]>();
if (null != allowIps && allowIps.length > 0) {
for (int i = 0; i < allowIps.length; i++) {
//把每個(gè)ip按“.”拆開,得到一個(gè)四位的數(shù)組
String[] ipParse = StringUtils.split(allowIps[i], ".");
whiteIps.add(ipParse);
}
}
String[] requestParse = StringUtils.split(remoteIp, ".");
for (String[] whiteIp : whiteIps) {
if (ipsEqual(requestParse, whiteIp)) {
return true;
}
}
return false;
}
public static boolean checkAppIpWhite(List<String[]> whiteIps,String remoteIp){
String[] requestParse = StringUtils.split(remoteIp, ".");
for (String[] whiteIp : whiteIps) {
if (ipsEqual(requestParse, whiteIp)) {
return true;
}
}
return false;
}
//判斷兩個(gè)ip是否相等
public static boolean ipsEqual(String[] requestIp, String[] whiteIp) {
boolean equal = false;
//判斷白名單ip是否在列表中必須要兩個(gè)ip都不為空進(jìn)行比較
if (requestIp != null && whiteIp != null && requestIp.length == whiteIp.length) {
if (requestIp[0].equals(whiteIp[0])
&& requestIp[1].equals(whiteIp[1])
&& ("*".equals(whiteIp[2]) || requestIp[2]
.equals(whiteIp[2]))
&& ("*".equals(whiteIp[3]) || requestIp[3]
.equals(whiteIp[3]))) {
equal = true;
}
}
return equal;
}
新的代碼:
public static boolean checkAppIpWhiteV2(String ipWhite,String remoteIp){
if(StringUtils.isEmpty(ipWhite) || StringUtils.isEmpty(remoteIp)){
return true;
}
byte[][] whiteIps = prepare(ipWhite);
byte[] rIp = convertIp2Bytes(remoteIp);
return isInclude(whiteIps,rIp);
}
public static boolean checkAppIpWhiteV2(byte[][] whiteIps,String remoteIp)
{
byte[] rIp = convertIp2Bytes(remoteIp);
return isInclude(whiteIps,rIp);
}
public static boolean isInclude(byte[][] whiteIps,byte[] rIp)
{
boolean result = false;
for(byte[] whiteIp : whiteIps)
{
for(int i = 1; i < 7; i++)
{
byte n = (byte)(1 << (i-1));
if ((whiteIp[0] & n) != n)
{
if (i == 6)
return true;
else
continue;
}
else
{
if (whiteIp[i] != rIp[i-1])
break;
}
if (i == 6)
{
return true;
}
}
}
return result;
}
public static byte[] convertIp2Bytes(String remoteIp)
{
byte[] result = new byte[6];
String[] points = StringUtils.split(remoteIp,".");
int cursor = 0;
for(String point : points)
{
int i = Integer.parseInt(point);
if (i > Byte.MAX_VALUE)
result[cursor] = (byte)(i - Byte.MAX_VALUE);
else
result[cursor] = (byte)i;
cursor += 1;
}
return result;
}
public static byte[][] prepare(String ipWhite)
{
String[] allowIps = StringUtils.split(ipWhite,",");//性能很好
byte[][] result = new byte[allowIps.length][7];
int cursorX = 0;
for(String allowIp : allowIps)
{
String[] points = StringUtils.split(allowIp,".");
int cursorY = 0;
byte checkbits = 0;
for(String point : points)
{
if (!point.equals("*"))
{
checkbits += 1 << cursorY;
int i = Integer.parseInt(point);
if (i > Byte.MAX_VALUE)
result[cursorX][cursorY + 1] = (byte)(i - Byte.MAX_VALUE);
else
result[cursorX][cursorY + 1] = (byte)i;
}
cursorY += 1;
}
result[cursorX][0] = checkbits;
cursorX += 1;
}
return result;
}
1.很多時(shí)候優(yōu)化可以在海量運(yùn)算之前做好準(zhǔn)備,例如分析的數(shù)據(jù)先做預(yù)處理,因?yàn)橥勺x性和計(jì)算效率會(huì)有沖突。
2.如果沒有緩存,那么構(gòu)建利于高效計(jì)算的代價(jià)可能超出簡(jiǎn)單但計(jì)算效率一般的情況。
3.在數(shù)據(jù)量很大的情況下,如何節(jié)省存儲(chǔ)直接決定你是否能夠緩存這些數(shù)據(jù)用于下次計(jì)算。
有興趣的同學(xué)可以執(zhí)行以下下面一些代碼(拷貝到eclipse格式化看和執(zhí)行比較好):
改動(dòng)的主要內(nèi)容在于對(duì)數(shù)據(jù)作預(yù)處理時(shí)增加了一點(diǎn)轉(zhuǎn)換,6位byte作為支持ipv6的存儲(chǔ),第7位作為標(biāo)識(shí)位來便于避免不需要的比對(duì)。
用到了org.apache.commons.lang.StringUtils,它的split方法就是采用簡(jiǎn)單的substring而不是pattern,效率很高,特別對(duì)于海量計(jì)算。
具體的測(cè)試代碼:
public static void main(String[] args)
{
long beg = System.currentTimeMillis();
//下面這些代碼是用于存粹比較計(jì)算時(shí),對(duì)白名單做預(yù)編譯和緩存
byte[][] whiteIps = prepare("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*");
String[] allowIps = StringUtils.split("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*",",");
List<String[]> whiteIps2 = new ArrayList<String[]>();
if (null != allowIps && allowIps.length > 0) {
for (int i = 0; i < allowIps.length; i++) {
//把每個(gè)ip按“.”拆開,得到一個(gè)四位的數(shù)組
String[] ipParse = StringUtils.split(allowIps[i], ".");
whiteIps2.add(ipParse);
}
}
for(int i = 0 ; i < 1000000 ; i++)
{
//第一組對(duì)比,增加了多一點(diǎn)的預(yù)處理,性能下降
//checkAppIpWhite("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.19");
//checkAppIpWhite("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.11");
//checkAppIpWhiteV2("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.19");
//checkAppIpWhiteV2("12.24.23.123,12.25.23.255,12.24.23.17,*.25.*.*,12.24.*.*","12.24.23.11");
//存粹的比較計(jì)算,性能上升
// checkAppIpWhite(whiteIps2,"12.24.23.19");
// checkAppIpWhite(whiteIps2,"12.24.23.11");
checkAppIpWhiteV2(whiteIps,"12.24.23.19");
checkAppIpWhiteV2(whiteIps,"12.24.23.11");
}
System.out.println("end test : " + (System.currentTimeMillis() - beg));
}
老的比較代碼函數(shù)如下:
public static boolean checkAppIpWhite(String ipWhilte,String remoteIp){
if(StringUtils.isEmpty(ipWhilte) || StringUtils.isEmpty(remoteIp)){
return true;
}
String[] allowIps = StringUtils.split(ipWhilte,",");
List<String[]> whiteIps = new ArrayList<String[]>();
if (null != allowIps && allowIps.length > 0) {
for (int i = 0; i < allowIps.length; i++) {
//把每個(gè)ip按“.”拆開,得到一個(gè)四位的數(shù)組
String[] ipParse = StringUtils.split(allowIps[i], ".");
whiteIps.add(ipParse);
}
}
String[] requestParse = StringUtils.split(remoteIp, ".");
for (String[] whiteIp : whiteIps) {
if (ipsEqual(requestParse, whiteIp)) {
return true;
}
}
return false;
}
public static boolean checkAppIpWhite(List<String[]> whiteIps,String remoteIp){
String[] requestParse = StringUtils.split(remoteIp, ".");
for (String[] whiteIp : whiteIps) {
if (ipsEqual(requestParse, whiteIp)) {
return true;
}
}
return false;
}
//判斷兩個(gè)ip是否相等
public static boolean ipsEqual(String[] requestIp, String[] whiteIp) {
boolean equal = false;
//判斷白名單ip是否在列表中必須要兩個(gè)ip都不為空進(jìn)行比較
if (requestIp != null && whiteIp != null && requestIp.length == whiteIp.length) {
if (requestIp[0].equals(whiteIp[0])
&& requestIp[1].equals(whiteIp[1])
&& ("*".equals(whiteIp[2]) || requestIp[2]
.equals(whiteIp[2]))
&& ("*".equals(whiteIp[3]) || requestIp[3]
.equals(whiteIp[3]))) {
equal = true;
}
}
return equal;
}
新的代碼:
public static boolean checkAppIpWhiteV2(String ipWhite,String remoteIp){
if(StringUtils.isEmpty(ipWhite) || StringUtils.isEmpty(remoteIp)){
return true;
}
byte[][] whiteIps = prepare(ipWhite);
byte[] rIp = convertIp2Bytes(remoteIp);
return isInclude(whiteIps,rIp);
}
public static boolean checkAppIpWhiteV2(byte[][] whiteIps,String remoteIp)
{
byte[] rIp = convertIp2Bytes(remoteIp);
return isInclude(whiteIps,rIp);
}
public static boolean isInclude(byte[][] whiteIps,byte[] rIp)
{
boolean result = false;
for(byte[] whiteIp : whiteIps)
{
for(int i = 1; i < 7; i++)
{
byte n = (byte)(1 << (i-1));
if ((whiteIp[0] & n) != n)
{
if (i == 6)
return true;
else
continue;
}
else
{
if (whiteIp[i] != rIp[i-1])
break;
}
if (i == 6)
{
return true;
}
}
}
return result;
}
public static byte[] convertIp2Bytes(String remoteIp)
{
byte[] result = new byte[6];
String[] points = StringUtils.split(remoteIp,".");
int cursor = 0;
for(String point : points)
{
int i = Integer.parseInt(point);
if (i > Byte.MAX_VALUE)
result[cursor] = (byte)(i - Byte.MAX_VALUE);
else
result[cursor] = (byte)i;
cursor += 1;
}
return result;
}
public static byte[][] prepare(String ipWhite)
{
String[] allowIps = StringUtils.split(ipWhite,",");//性能很好
byte[][] result = new byte[allowIps.length][7];
int cursorX = 0;
for(String allowIp : allowIps)
{
String[] points = StringUtils.split(allowIp,".");
int cursorY = 0;
byte checkbits = 0;
for(String point : points)
{
if (!point.equals("*"))
{
checkbits += 1 << cursorY;
int i = Integer.parseInt(point);
if (i > Byte.MAX_VALUE)
result[cursorX][cursorY + 1] = (byte)(i - Byte.MAX_VALUE);
else
result[cursorX][cursorY + 1] = (byte)i;
}
cursorY += 1;
}
result[cursorX][0] = checkbits;
cursorX += 1;
}
return result;
}