之前写过一个HTTP Client 请求,刷新主机缓存,之前实现通过为前台获取主机地址, 通过 for循环进行调用,通过测试之后就没在理,现在发现性能不足,
遇到timeout情况会产生严重延迟效果,无法使用,现在将其改造成并发处理。
之前有学习过并发,只是简单的了解线程,线程状态,线程安全等基本知识,联系过抢票等一下简单实例,具体开发没用到过,之前开发业务逻辑,也不需要,
简单的逻辑即可,突然进行并发处理有点懵,话不多说,直接贴代码。下面详细解释。
// HTTPClient public static String doGet(String url){ String result = ""; BufferedReader in = null; try { URL realUrl = new URL(url); // 打开和URL之间的连接 HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection(); // 设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); //设置超时 System.setProperty("sun.net.client.defaultConnectTimeout", "5000"); System.setProperty("sun.net.client.defaultReadTime", "5000"); connection.setConnectTimeout(5000); connection.setReadTimeout(5000); // 建立实际的连接 connection.connect(); // 获取所有响应头字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { log.debug(key + "--->" + map.get(key)); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (MalformedURLException e) { return "FAILD"; } catch (IOException e) { return "FAILD"; }finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; }
HTTP请求是无状态的请求,所以很容易造成超时处理,必须设定合适的相应控制范围,由于公司主机网络延迟比较严重
设置5秒延迟时间
System.setProperty("sun.net.client.defaultConnectTimeout", "5000"); //JDK 1.5之后推荐这么写,JVM层
System.setProperty("sun.net.client.defaultReadTime", "5000");
connection.setConnectTimeout(5000); //JDK 1.5之前可以这么设置 链接层面
connection.setReadTimeout(5000);
关于超时的设置,网上找到两种方法,我都引用了,万无一失,一个是JVM层面,一个是链接层面
//线程并发处理 // 内部类 class HTTPThread implements Callable<IData> { private IData param = null; private StringBuffer url = null; private CountDownLatch countDownLatch; public void setCountDownLatch(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } public IData getParam() { return param; } public void setParam(IData instance) { this.param = instance; url = new StringBuffer("http://"); url.append(instance.getString("HOST")+":"); url.append(instance.getString("PORT")); url.append(instance.getString("CONTEXT","")); url.append(instance.getString("SERVLET")); url.append("?WadeSafeUpdate"); } @Override public IData call() throws Exception { try { String state = HTTPClientAPI.doGet(url.toString()); this.param.put("RESULT", state); } catch (Exception e) { e.printStackTrace(); } finally { if (countDownLatch != null) { //递减锁存器的计数 countDownLatch.countDown(); } } return this.param; } }
// 调用方法 /** * 刷新所有机器 * @param param * @return * @throws Exception */ public static IDataset refreshALLListener(IData param) throws Exception { int success = 0; int faild = 0; IDataset instances = queryListener(param); ArrayList<Future<IData>> results = new ArrayList<Future<IData>>();// ExecutorService executorService =Executors.newFixedThreadPool(10); CountDownLatch countDownLatch = new CountDownLatch(instances.size()); for(int i=0; i<instances.size() ; i++){ IData instance = instances.getData(i); HTTPThread httpThread = new HTTPThread(); httpThread.setParam(instance); httpThread.setCountDownLatch(countDownLatch); results.add(executorService.submit(httpThread)); } countDownLatch.await(); executorService.shutdown(); IDataset result = new DatasetList(); for(int i=0; i<results.size() ; i++){ result.add(results.get(i).get()); IData res = result.getData(i); String state = res.getString("RESULT"); if("SUCCESS".equals(state)){ success++; res.put("FRESHSTATE","刷新成功"); res.put("REFRESHSTATE",state); SQLParser parser=new SQLParser(res); parser.addSQL(" update vest_server_instance set UPTIME = sysdate() where HOST = :HOST "); parser.addSQL(" AND PORT = :PORT "); parser.addSQL(" AND CONTEXT = :CONTEXT "); parser.addSQL(" AND SERVLET = :SERVLET"); BaseDAO dao = new BaseDAO(); dao.initial("base"); dao.executeUpdate(parser); res.put("UPTIME", TIME_FORMAT.format(new Date())); }else{ faild++; res.put("FRESHSTATE","刷新失败"); res.put("REFRESHSTATE",state); } } if(instances.size() > 0){ instances.getData(0).put("SUCCESS",success); instances.getData(0).put("FAILD",faild); } return result; } }
1:线程池
在之前 没接触过线程池,开启线程都是通过Thread.run方法开启。但是随着new 的Thread 越来越多,创建-销毁,创建-销毁,就会造成资源的严重浪费,效率会下降,快速崩溃。线程池就可以帮助我们解决这个问题,他使线程可以重复使用,就是执行完一个任务线程不会被销毁,而是可以继续执行其他任务
2:线程执行先后、如何同步、处理完成
CountDownLatch 线程同步工具类,相当于一个计数器,通过初始化设置一个数量(只能设置一次,不能更改)。当线程完成任务后,会进行减一。
3:线程如何执行完成如何返回数据
之前都是通过Runnable 接口来创建一个线程,但是run方法没有返回值,所以应该使用Callable,Callable的call方法可以根据你传入的泛型参数返回对应类型的数据。callable的call方法返回的数据是通过Future来接受的,有两个方法鼻祖知道,首先,可以用isDone()方法来查询Future是否已经完成,任务完成后,可以调用get()方法来获取结果 , 如果不加判断直接调用get方法,此时如果线程未完成,get将阻塞,直至结果准备就绪
线程池:
JAVA通过Executors创建线程池
- newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
线程池重要方法:
- execute()实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
- submit()是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。
- shutdown() 关闭线程池
- shutdownNow() 关闭线程池