diff --git a/ip2regiondemo/.gitignore b/ip2regiondemo/.gitignore new file mode 100644 index 0000000..682d4ed --- /dev/null +++ b/ip2regiondemo/.gitignore @@ -0,0 +1,27 @@ +# Created by .ignore support plugin (hsz.mobi) +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/.idea/ +/target/ diff --git a/ip2regiondemo/fanfu-web.iml b/ip2regiondemo/fanfu-web.iml new file mode 100644 index 0000000..4fb2f38 --- /dev/null +++ b/ip2regiondemo/fanfu-web.iml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ip2regiondemo/pom.xml b/ip2regiondemo/pom.xml new file mode 100644 index 0000000..2276085 --- /dev/null +++ b/ip2regiondemo/pom.xml @@ -0,0 +1,136 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.9.RELEASE + + + + com.ayi + woniu-web + 0.0.1-SNAPSHOT + woniou-web + create by woniu + + jar + + + 1.8 + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.google.guava + guava + 30.0-jre + + + org.projectlombok + lombok + + + org.assertj + assertj-core + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.freemarker + freemarker + 2.3.31 + + + + io.springfox + springfox-swagger2 + 2.7.0 + + + io.springfox + springfox-swagger-ui + 2.7.0 + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + com.alibaba + fastjson + 1.2.79 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.aspectj + aspectjweaver + 1.9.5 + + + + + org.lionsoul + ip2region + 2.6.3 + + + + commons-io + commons-io + 2.6 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/ip2regiondemo/src/main/java/com/woniu/WoNiuApplication.java b/ip2regiondemo/src/main/java/com/woniu/WoNiuApplication.java new file mode 100644 index 0000000..a5ca290 --- /dev/null +++ b/ip2regiondemo/src/main/java/com/woniu/WoNiuApplication.java @@ -0,0 +1,16 @@ +package com.woniu; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +@SpringBootApplication +public class WoNiuApplication { + + public static void main(String[] args) { + SpringApplication.run(WoNiuApplication.class, args); + } + + + +} diff --git a/ip2regiondemo/src/main/java/com/woniu/service/controller/TestController.java b/ip2regiondemo/src/main/java/com/woniu/service/controller/TestController.java new file mode 100644 index 0000000..cf90a69 --- /dev/null +++ b/ip2regiondemo/src/main/java/com/woniu/service/controller/TestController.java @@ -0,0 +1,21 @@ +package com.woniu.service.controller; + +import com.woniu.service.ipregion.Ip; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * SpringBoot整合ip2region实现使用ip监控用户访问城市 + * 公众号:程序员蜗牛g + */ +@RestController +@RequestMapping("/test") +public class TestController { + + @GetMapping("/hello") + @Ip + public String hello() { + return "hello"; + } +} \ No newline at end of file diff --git a/ip2regiondemo/src/main/java/com/woniu/service/ipregion/Ip.java b/ip2regiondemo/src/main/java/com/woniu/service/ipregion/Ip.java new file mode 100644 index 0000000..aa2bca2 --- /dev/null +++ b/ip2regiondemo/src/main/java/com/woniu/service/ipregion/Ip.java @@ -0,0 +1,16 @@ +package com.woniu.service.ipregion; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @className: Ip + * @author: woniu 公众号:程序员蜗牛g + * @date: 2023/4/5 + **/ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Ip { +} diff --git a/ip2regiondemo/src/main/java/com/woniu/service/ipregion/IpAspect.java b/ip2regiondemo/src/main/java/com/woniu/service/ipregion/IpAspect.java new file mode 100644 index 0000000..5dc18a7 --- /dev/null +++ b/ip2regiondemo/src/main/java/com/woniu/service/ipregion/IpAspect.java @@ -0,0 +1,34 @@ +package com.woniu.service.ipregion; + +import com.woniu.service.util.AddressUtil; +import com.woniu.service.util.HttpContextUtil; +import com.woniu.service.util.IPUtil; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.text.MessageFormat; + +@Aspect +@Component +@Slf4j +public class IpAspect { + + + @Pointcut("@annotation(com.woniu.service.ipregion.Ip)") + public void pointcut() { + } + + @Around("pointcut()") + public Object doAround(ProceedingJoinPoint point) throws Throwable { + HttpServletRequest request = HttpContextUtil.getHttpServletRequest(); + String ip = IPUtil.getIpAddr(request); + log.info(MessageFormat.format("当前IP为:[{0}];当前IP地址解析出来的地址为:[{1}]", ip, AddressUtil.getCityInfo(ip))); + return point.proceed(); + } + +} diff --git a/ip2regiondemo/src/main/java/com/woniu/service/util/AddressUtil.java b/ip2regiondemo/src/main/java/com/woniu/service/util/AddressUtil.java new file mode 100644 index 0000000..4af40e6 --- /dev/null +++ b/ip2regiondemo/src/main/java/com/woniu/service/util/AddressUtil.java @@ -0,0 +1,31 @@ +package com.woniu.service.util; + +import org.lionsoul.ip2region.xdb.Searcher; + +public class AddressUtil { + + + /** + * 根据IP地址查询登录来源 + * + * @param ip + * @return + */ + public static String getCityInfo(String ip) { + try { + Searcher searcher = Searcher.newWithFileOnly("ip2region/ip2region.xdb"); + //开始查询 + return searcher.searchByStr(ip); + } catch (Exception e) { + e.printStackTrace(); + } + //默认返回空字符串 + return ""; + } + + public static void main(String[] args) { + //204.16.111.255 + System.out.println(getCityInfo("204.16.111.255")); + } + +} \ No newline at end of file diff --git a/ip2regiondemo/src/main/java/com/woniu/service/util/HttpContextUtil.java b/ip2regiondemo/src/main/java/com/woniu/service/util/HttpContextUtil.java new file mode 100644 index 0000000..501cfc6 --- /dev/null +++ b/ip2regiondemo/src/main/java/com/woniu/service/util/HttpContextUtil.java @@ -0,0 +1,28 @@ +package com.woniu.service.util; + + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Objects; + +/** + * @desc 全局获取HttpServletRequest、HttpServletResponse + * 公众号:程序员蜗牛g + */ +public class HttpContextUtil { + + private HttpContextUtil() { + + } + + public static HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + } + + public static HttpServletResponse getHttpServletResponse() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse(); + } +} diff --git a/ip2regiondemo/src/main/java/com/woniu/service/util/IPUtil.java b/ip2regiondemo/src/main/java/com/woniu/service/util/IPUtil.java new file mode 100644 index 0000000..96f91dc --- /dev/null +++ b/ip2regiondemo/src/main/java/com/woniu/service/util/IPUtil.java @@ -0,0 +1,37 @@ +package com.woniu.service.util; + + +import javax.servlet.http.HttpServletRequest; + +/** + * @desc 查询当前访问的IP地址 + */ +public class IPUtil { + + private static final String UNKNOWN = "unknown"; + + protected IPUtil() { + + } + + /** + * 获取 IP地址 + * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址 + * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址, + * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址 + */ + public static String getIpAddr(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + } + +} diff --git a/ip2regiondemo/src/main/resources/ip2region/ip2region.xdb b/ip2regiondemo/src/main/resources/ip2region/ip2region.xdb new file mode 100644 index 0000000..c78b792 Binary files /dev/null and b/ip2regiondemo/src/main/resources/ip2region/ip2region.xdb differ diff --git a/ip2regiondemo/src/test/java/com/woniu/AddressUtil.java b/ip2regiondemo/src/test/java/com/woniu/AddressUtil.java new file mode 100644 index 0000000..66bdbb2 --- /dev/null +++ b/ip2regiondemo/src/test/java/com/woniu/AddressUtil.java @@ -0,0 +1,51 @@ +package com.woniu; + +import org.apache.commons.io.FileUtils; +import org.lionsoul.ip2region.xdb.Searcher; + +import java.io.File; +import java.text.MessageFormat; +import java.util.Objects; + +public class AddressUtil { + + /** + * 当前记录地址的本地DB + */ + private static final String TEMP_FILE_DIR = "/home/admin/app/"; + + /** + * 根据IP地址查询登录来源 + * + * @param ip + * @return + */ + public static String getCityInfo(String ip) { + try { + // 获取当前记录地址位置的文件 + String dbPath = Objects.requireNonNull(AddressUtil.class.getResource("/ip2region/ip2region.xdb")).getPath(); + File file = new File(dbPath); + //如果当前文件不存在,则从缓存中复制一份 + if (!file.exists()) { + dbPath = TEMP_FILE_DIR + "ip.db"; + System.out.println(MessageFormat.format("当前目录为:[{0}]", dbPath)); + file = new File(dbPath); + FileUtils.copyInputStreamToFile(Objects.requireNonNull(AddressUtil.class.getClassLoader().getResourceAsStream("ip2region/ip2region.xdb")), file); + } + //创建查询对象 + Searcher searcher = Searcher.newWithFileOnly(dbPath); + //开始查询 + return searcher.searchByStr(ip); + } catch (Exception e) { + e.printStackTrace(); + } + //默认返回空字符串 + return ""; + } + + public static void main(String[] args) { + System.out.println(getCityInfo("1.2.3.4")); + } + + +} diff --git a/ip2regiondemo/src/test/java/com/woniu/TestA.java b/ip2regiondemo/src/test/java/com/woniu/TestA.java new file mode 100644 index 0000000..79f7650 --- /dev/null +++ b/ip2regiondemo/src/test/java/com/woniu/TestA.java @@ -0,0 +1,171 @@ +package com.woniu; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; + + +/** + * 性能优化-如何爽玩多线程来开发?场景实战! + */ +@SpringBootTest +@RunWith(SpringRunner.class) +@Slf4j +public class TestA { + + ThreadPoolExecutor executor = new ThreadPoolExecutor( + 20, + 50, + 5, + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(200), + Executors.defaultThreadFactory(), + new ThreadPoolExecutor.CallerRunsPolicy() + ); + + /** + * 并行聚合处理数据! + * 主要运用CompletableFuture.allOf()方法, + * 将原本串行的操作改为并行。本案例相对比较常规 + */ + @Test + public void threadDemo1() throws Exception { + + CompletableFuture future1 = CompletableFuture.supplyAsync(() -> { + //逻辑A + log.info("A"); + return "A"; + }, executor); + + CompletableFuture future2 = CompletableFuture.supplyAsync(() -> { + //逻辑B + log.info("B"); + return "B"; + }, executor); + + CompletableFuture future3 = CompletableFuture.supplyAsync(() -> { + //逻辑C + log.info("C"); + return "C"; + }, executor); + + CompletableFuture.allOf(future1, future2, future3) + .handle((result, exception) -> { + if (exception != null) { + log.error("处理失败:{}", exception); + } else { + //等待三个任务执行完 接着处理这个逻辑 + + } + return null; + }); + } + + + /** + * 修改for循环为并行操作 + *

+ * 这里借鉴了parallelStream流的思路,将串行的for循环分割成多个集合后,对分割后的集合进行循环。这应该是最普遍的多线程应用场景了 + */ + @Test + public void threadDemo2() throws Exception { + + List slAddList = new ArrayList<>(50000); + slAddList.addAll(Arrays.asList("1", "2", "3", "4", "5", "6")); + + List> partition = Lists.partition(slAddList, 2); + + CompletableFuture.allOf(partition.stream().map(partitionList -> CompletableFuture.runAsync(() -> { + log.info("处理逻辑partitionList"); + }, executor) + ).toArray(CompletableFuture[]::new)) + .whenComplete((res, e) -> + { + if (e != null) { + log.error("多线程处理数据失败", e); + } else { + try { + log.info("进一步处理循环后的结果"); + } catch (Exception ex) { + log.error("处理失败", ex); + } + } + }); + + } + + /** + * 修改Map遍历为并行操作 + */ + @Test + public void threadDemo3() throws Exception { + //代码示例 + Map> materialMap = new HashMap>() { + { + put("name", Arrays.asList("a","b","c")); + put("age", Arrays.asList("1","2","3")); + put("city", Arrays.asList("nj","bj","cz")); + put("province", Arrays.asList("js","zj","hn")); + put("love", Arrays.asList("bk","jk","bk")); + } + }; + List>> mapList = mapSplit(materialMap, 2); + CompletableFuture handle = + CompletableFuture.allOf(mapList.stream().map(splitMap -> CompletableFuture.runAsync(() -> { + splitMap.forEach((identity, list) -> { + log.info("identity:{},list:{}",identity,list); + }); + }, executor)).toArray(CompletableFuture[]::new)) + .exceptionally(e -> { + log.error("多线程组装数据失败", e); + return null; + }); + + } + + + + + + /** + * 将map切成段--工具类 + * + * @param splitMap 被切段的map + * @param splitNum 每段的大小 + */ + public static List> mapSplit(Map splitMap, int splitNum) { + if (splitMap == null || splitNum <= 0) { + List> list = new ArrayList<>(); + list.add(splitMap); + return list; + } + Set keySet = splitMap.keySet(); + Iterator iterator = keySet.iterator(); + int i = 1; + List> total = new ArrayList<>(); + Map tem = new HashMap<>(); + while (iterator.hasNext()) { + k next = iterator.next(); + tem.put(next, splitMap.get(next)); + if (i == splitNum) { + total.add(tem); + tem = new HashMap<>(); + i = 0; + } + i++; + } + if (!CollectionUtils.isEmpty(tem)) { + total.add(tem); + } + return total; + } + +} diff --git a/ip2regiondemo/woniu-web.iml b/ip2regiondemo/woniu-web.iml new file mode 100644 index 0000000..6e18898 --- /dev/null +++ b/ip2regiondemo/woniu-web.iml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file