diff --git a/src/main/java/com/pancm/arithmetic/PatternMatchingTest.java b/src/main/java/com/pancm/arithmetic/PatternMatchingTest.java index 28dffef..49dbe68 100644 --- a/src/main/java/com/pancm/arithmetic/PatternMatchingTest.java +++ b/src/main/java/com/pancm/arithmetic/PatternMatchingTest.java @@ -5,6 +5,7 @@ import java.util.Scanner; /** * @Title: PatternMatchingTest * @Description: 模式匹配算法 + * 来源:http://www.cnblogs.com/jiaohanhan/p/6654874.html * @Version:1.0.0 * @author pancm * @date 2018年9月14日 diff --git a/src/main/java/com/pancm/arithmetic/SortTest.java b/src/main/java/com/pancm/arithmetic/SortTest.java new file mode 100644 index 0000000..4184848 --- /dev/null +++ b/src/main/java/com/pancm/arithmetic/SortTest.java @@ -0,0 +1,213 @@ +package com.pancm.arithmetic; + +import java.util.Arrays; + +/** + * @author ZERO + * @Data 2017-5-31 下午5:30:29 + * @Description 数组排序算法 主要是冒泡、二分和快速排序 + */ +public class SortTest { + + /** + * @param args + */ + public static void main(String[] args) { + + int[] t={1,44,55,22,34,88,3}; + for(int i=0,j=t.length;i执行耗时 : "+(System.currentTimeMillis()-a)+"毫秒 "); +// int[] p=sort(t); +// for(int i=0,j=p.length;i执行耗时 : "+(System.currentTimeMillis()-a)+"毫秒 "); + } + + /** + * 插入排序 升序 + * 插入排序是循环数组,然后将前一位的数字和后一位的进行比较,将数值大的向后移一位 + * @param a + * @return + */ + public static int[] ps(int[] a){ + for(int i=1,j=a.length;i=0;k--){ + if(a[k]>t){ + a[k+1]=a[k]; + }else{ + break; + } + } + a[k+1]=t; + } + return a; + } + + /** + * 插入排序 降序 + * 插入排序是循环数组,然后将前一位的数字和后一位的进行比较,将数值大的向后移一位 + * @param a + * @return + */ + public static int[] crps(int[] a){ + for(int i=1,j=a.length;i=0;k--){ + if(a[k]m[j]){ + int tmp=m[i]; + m[i]=m[j]; + m[j]=tmp; + } + } + } + return m; + } + + /** + * 冒泡排序 倒序 + * 冒泡排序 是双重循环数组,前一位和后一位进行比较,若前一位大于后一位,就交换位置 + */ + public static int[] mpdx(int[] a){ + for(int i=0;i= left; j--) { + a[j+1] = a[j]; + } + if(left != i){ + a[left] = temp; + } + } + return a; + } + + /** + * 快速排序 + * @param a + */ + private static void quick(int[] a) { + if(a.length>0){ + quickSort(a,0,a.length-1); + } + } + + private static void quickSort(int[] a, int low, int high) { + if(low=temp){ + high--; + } + a[low] = a[high]; + while(low + testServlet + com.pancm.test.servletTest.testServlet + + + + testServlet + /test.do + + 然后启动tomcat,在浏览器输入 ip:端口/项目名/设置的地址 + 就可以访问了 +* Version:1.0.0 +* @author pancm +* @date 2018年3月20日 + */ +public class ServletTest extends HttpServlet { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * 返回结果 + */ + private String result = null; + + private long count=1; + + /** + * + */ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + doPost(req, resp); + } + + /** + * + */ + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + System.out.println("============"); + System.out.println(req.getParameter("param")); + PrintWriter printWriter=resp.getWriter(); + result = "这是第"+count+"次响应!"; + try { + resp.setCharacterEncoding("utf-8"); + count++; + printWriter.print(result); + } catch (Exception e) { + result="第"+count+"次请求错误!"; + printWriter.print(result); + }finally{ + req=null; + printWriter=null; + resp=null; + } + } + + @Override + public void destroy() { + super.destroy(); + } +} diff --git a/src/main/java/com/pancm/basics/StringTest.java b/src/main/java/com/pancm/basics/StringTest.java new file mode 100644 index 0000000..b94aef3 --- /dev/null +++ b/src/main/java/com/pancm/basics/StringTest.java @@ -0,0 +1,89 @@ +package com.pancm.basics; + +/** +* Title: test1 +* Description: string相关问题 +* Version:1.0.0 +* @author pancm +* @date 2017-7-21 + */ +public class StringTest { + public static void main(String[] args) { + + String hello="hello"; + //有15种构造方法,有两种是过时的,其中包含char[],byte[],int[],String,StringBuffer,StringBuilder。 + String newHello=new String("hello"); + char []cHello ={'h','e','l','l','o'}; + String str=new String(cHello); + System.out.println(hello+","+newHello+","+str); + test1(); + test3(); + } + + + + + + private static void test1(){ + String str="Hello World"; + String str1=""; + StringBuffer sbr=new StringBuffer(str); + StringBuilder sbd=new StringBuilder(str); + long start=System.currentTimeMillis(); + for(int i=0;i<10000;i++){ + str1+=str; + } + System.out.println("String累加用时:"+(System.currentTimeMillis()-start)+"ms"); + long start2=System.currentTimeMillis(); + for(int i=0;i<10000;i++){ + sbr.append(str); + } + System.out.println("StringBuffer累加用时:"+(System.currentTimeMillis()-start2)+"ms"); + long start3=System.currentTimeMillis(); + for(int i=0;i<10000;i++){ + sbd.append(str); + } + System.out.println("StringBuilder累加用时:"+(System.currentTimeMillis()-start3)+"ms"); + } + + + private static void test3() { + + String s1 = "test"; + String s2 = new String("test"); + String s3 = "te"; + String s4 = "st"; + String s5 = "te" + "st"; + String s6 = s3 + s4; + String s7 = new String(s1); + // 引用地址不同 equals相同 + System.out.println(s1 == s2); //false + // s5 在编译之前就可以确认s5=Programming 因此相等 + System.out.println(s1 == s5); //true + //字符串常量池的原则 这时 s6 的值是在运行时得到的,它会重新构造字符串对象 所以为false + System.out.println(s1 == s6); //false + System.out.println(s7==s1); //false + System.out.println(s7.equals(s1)); //true + + + String ab="ab"; + String c="c"; + String ab_c=ab+c; + String ab_c1="ab"+"c"; + String abc="abc"; + /** + * 优先级问题 + */ + System.out.println(ab_c == abc + " : " + ab_c.equals(abc));//false + /** + * 字符串常量池的原则 这时 ab_c 的值是在运行时得到的,它会重新构造字符串对象 所以为false + */ + System.out.println((ab_c == abc) + " : " + ab_c.equals(abc));//false : true + /** + * 这条语言在编译时,可以确定 ab_c1 = "abc",因此它与 abc = "abc" 指向同一对象 所以为true + */ + System.out.println((ab_c1 == abc) + " : " + ab_c1.equals(abc));//true : true + + } + +} diff --git a/src/main/java/com/pancm/basics/User.java b/src/main/java/com/pancm/basics/User.java new file mode 100644 index 0000000..5d99346 --- /dev/null +++ b/src/main/java/com/pancm/basics/User.java @@ -0,0 +1,62 @@ +package com.pancm.basics; + +/** + * +* @Title: User +* @Description: +* 反射测试实体类 +* Version:1.0.0 +* @author pancm +* @date 2018年2月9日 + */ +public class User { + private String name; + + //构造方法1(默认构造方法)*********************** + public User(){ + + } + //构造方法2 + public User(String name){ + this.name=name; + } + //******自定义方法************* + public void getMessage(){ + System.out.print("反射测试"); + } + + //******自定义方法2************* + public String getMessage2(int num){ + String str=num+"反射测试!"; + System.out.print(str); + return str; + } + + //******自定义方法3************* + private String getMessage3(){ + String str="这是一个私有的方法!"; + System.out.print(str); + return str; + } + + + //******自定义方法4************* + public static String getMessage4(String s){ + String str=s+"这是一个静态的方法!"; + System.out.print(str); + return str; + } + + //******重写toString方法,在测试的时候会用到***** + @Override + public String toString() { + return "name:"+this.name; + } + //************************** + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/com/pancm/bigdata/hbase/HBaseUtil.java b/src/main/java/com/pancm/bigdata/hbase/HBaseUtil.java new file mode 100644 index 0000000..45c646b --- /dev/null +++ b/src/main/java/com/pancm/bigdata/hbase/HBaseUtil.java @@ -0,0 +1,396 @@ +package com.pancm.bigdata.hbase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellUtil; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.ConnectionFactory; +import org.apache.hadoop.hbase.client.Delete; +import org.apache.hadoop.hbase.client.Get; +import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.util.Bytes; + +import com.alibaba.fastjson.JSONObject; + +/** + * + * Title: HBaseUtil + * Description: HBase工具类 + * Version:1.0.0 + * @author pancm + * @date 2017年12月6日 + */ +public class HBaseUtil { + /** hadoop 连接 */ + private static Configuration conf = null; + /** hbase 连接 */ + private static Connection con = null; + /** 会话 */ + private static Admin admin = null; + + private static String ip ="master"; + private static String port ="2181"; + private static String port1 ="9001"; + + // 初始化连接 + static { + // 获得配制文件对象 + conf = HBaseConfiguration.create(); + // 设置配置参数 + conf.set("hbase.zookeeper.quorum", ip); + conf.set("hbase.zookeeper.property.clientPort", port); + //如果hbase是集群,这个必须加上 + //这个ip和端口是在hadoop/mapred-site.xml配置文件配置的 + conf.set("hbase.master", ip+":"+port1); + } + + + /** + * 获取连接 + * + * @return + */ + public synchronized static Connection getConnection() { + try { + if (null == con || con.isClosed()) { + // 获得连接对象 + con = ConnectionFactory.createConnection(conf); + } + } catch (IOException e) { + System.out.println("获取连接失败!"); + e.printStackTrace(); + } + + return con; + } + + /** + * 连接关闭 + */ + public static void close() { + try { + if (admin != null) { + admin.close(); + } + if (con != null) { + con.close(); + } + } catch (IOException e) { + System.out.println("连接关闭失败!"); + e.printStackTrace(); + } + } + + /** + * 创建表 + * + * @param tableName + * 表名 + * @param columnFamily + * 列族 + */ + public static void creatTable(String tableName, String[] columnFamily) { + if(null==tableName||tableName.length()==0){ + return; + } + if(null==columnFamily||columnFamily.length==0){ + return; + } + // 创建表名对象 + TableName tn = TableName.valueOf(tableName); + // a.判断数据库是否存在 + try { + // 获取会话 + admin = getConnection().getAdmin(); + if (admin.tableExists(tn)) { + System.out.println(tableName + " 表存在,删除表...."); + // 先使表设置为不可编辑 + admin.disableTable(tn); + // 删除表 + admin.deleteTable(tn); + System.out.println("表删除成功....."); + } + // 创建表结构对象 + HTableDescriptor htd = new HTableDescriptor(tn); + for (String str : columnFamily) { + // 创建列族结构对象 + HColumnDescriptor hcd = new HColumnDescriptor(str); + htd.addFamily(hcd); + } + // 创建表 + admin.createTable(htd); + System.out.println(tableName + " 表创建成功!"); + } catch (IOException e) { + e.printStackTrace(); + } finally { + close(); + } + } + + /** + * 数据单条插入或更新 + * + * @param tableName + * 表名 + * @param rowKey + * 行健 (主键) + * @param family + * 列族 + * @param qualifier + * 列 + * @param value + * 存入的值 + * @return + */ + public static void insert(String tableName, String rowKey, String family, + String qualifier, String value) { + Table t = null; + try { + t = getConnection().getTable(TableName.valueOf(tableName)); + Put put = new Put(Bytes.toBytes(rowKey)); + put.addColumn(Bytes.toBytes(family), Bytes.toBytes(qualifier), + Bytes.toBytes(value)); + t.put(put); + System.out.println(tableName + " 更新成功!"); + } catch (IOException e) { + System.out.println(tableName + " 更新失败!"); + e.printStackTrace(); + } finally { + close(); + } + } + + /** + * 数据批量插入或更新 + * + * @param tableName + * 表名 + * @param list + * hbase的数据 + * @return + */ + public static void insertBatch(String tableName, List list) { + if (null == tableName ||tableName.length()==0) { + return; + } + if( null == list || list.size() == 0){ + return; + } + Table t = null; + Put put = null; + JSONObject json = null; + List puts = new ArrayList(); + try { + t = getConnection().getTable(TableName.valueOf(tableName)); + for (int i = 0, j = list.size(); i < j; i++) { + json = (JSONObject) list.get(i); + put = new Put(Bytes.toBytes(json.getString("rowKey"))); + put.addColumn(Bytes.toBytes(json.getString("family")), + Bytes.toBytes(json.getString("qualifier")), + Bytes.toBytes(json.getString("value"))); + puts.add(put); + } + t.put(puts); + System.out.println(tableName + " 更新成功!"); + } catch (IOException e) { + System.out.println(tableName + " 更新失败!"); + e.printStackTrace(); + } finally { + close(); + } + } + + /** + * 数据删除 + * @param tableName 表名 + * @param rowKey 行健 + * @return + */ + public static void delete(String tableName, String rowKey) { + delete(tableName,rowKey,"",""); + } + + /** + * 数据删除 + * @param tableName 表名 + * @param rowKey 行健 + * @param family 列族 + * @return + */ + public static void delete(String tableName, String rowKey, String family) { + delete(tableName,rowKey,family,""); + } + + /** + * 数据删除 + * @param tableName 表名 + * @param rowKey 行健 + * @param family 列族 + * @param qualifier 列 + * @return + */ + public static void delete(String tableName, String rowKey, String family, + String qualifier) { + if (null == tableName ||tableName.length()==0) { + return; + } + if( null == rowKey || rowKey.length() == 0){ + return; + } + Table t = null; + try { + t = getConnection().getTable(TableName.valueOf(tableName)); + Delete del = new Delete(Bytes.toBytes(rowKey)); + // 如果列族不为空 + if (null != family && family.length() > 0) { + // 如果列不为空 + if (null != qualifier && qualifier.length() > 0) { + del.addColumn(Bytes.toBytes(family), + Bytes.toBytes(qualifier)); + } else { + del.addFamily(Bytes.toBytes(family)); + } + } + t.delete(del); + } catch (IOException e) { + System.out.println("删除失败!"); + e.printStackTrace(); + } finally { + close(); + } + } + + /** + * 查询该表中的所有数据 + * + * @param tableName + * 表名 + */ + public static void select(String tableName) { + if(null==tableName||tableName.length()==0){ + return; + } + Table t = null; + List> list=new ArrayList>(); + try { + t = getConnection().getTable(TableName.valueOf(tableName)); + // 读取操作 + Scan scan = new Scan(); + // 得到扫描的结果集 + ResultScanner rs = t.getScanner(scan); + if (null == rs ) { + return; + } + for (Result result : rs) { + // 得到单元格集合 + List cs = result.listCells(); + if (null == cs || cs.size() == 0) { + continue; + } + for (Cell cell : cs) { + Map map=new HashMap(); + map.put("rowKey", Bytes.toString(CellUtil.cloneRow(cell)));// 取行健 + map.put("timestamp", cell.getTimestamp());// 取到时间戳 + map.put("family", Bytes.toString(CellUtil.cloneFamily(cell)));// 取到列族 + map.put("qualifier", Bytes.toString(CellUtil.cloneQualifier(cell)));// 取到列 + map.put("value", Bytes.toString(CellUtil.cloneValue(cell)));// 取到值 + list.add(map); + } + } + System.out.println("查询的数据:"+list); + } catch (IOException e) { + System.out.println("查询失败!"); + e.printStackTrace(); + } finally { + close(); + } + } + + /** + * 根据表名和行健查询 + * @param tableName + * @param rowKey + */ + public static void select(String tableName, String rowKey) { + select(tableName,rowKey,"",""); + } + + /** + * 根据表名、行健和列族查询 + * @param tableName + * @param rowKey + * @param family + */ + public static void select(String tableName, String rowKey, String family) { + select(tableName,rowKey,family,""); + } + + /** + * 根据条件明细查询 + * + * @param tableName + * 表名 + * @param rowKey + * 行健 (主键) + * @param family + * 列族 + * @param qualifier + * 列 + */ + public static void select(String tableName, String rowKey, String family, + String qualifier) { + Table t = null; + List> list=new ArrayList>(); + try { + t = getConnection().getTable(TableName.valueOf(tableName)); + // 通过HBase中的 get来进行查询 + Get get = new Get(Bytes.toBytes(rowKey)); + // 如果列族不为空 + if (null != family && family.length() > 0) { + // 如果列不为空 + if (null != qualifier && qualifier.length() > 0) { + get.addColumn(Bytes.toBytes(family), + Bytes.toBytes(qualifier)); + } else { + get.addFamily(Bytes.toBytes(family)); + } + } + Result r = t.get(get); + List cs = r.listCells(); + if (null == cs || cs.size() == 0) { + return; + } + for (Cell cell : cs) { + Map map=new HashMap(); + map.put("rowKey", Bytes.toString(CellUtil.cloneRow(cell)));// 取行健 + map.put("timestamp", cell.getTimestamp());// 取到时间戳 + map.put("family", Bytes.toString(CellUtil.cloneFamily(cell)));// 取到列族 + map.put("qualifier", Bytes.toString(CellUtil.cloneQualifier(cell)));// 取到列 + map.put("value", Bytes.toString(CellUtil.cloneValue(cell)));// 取到值 + list.add(map); + } + System.out.println("查询的数据:"+list); + } catch (IOException e) { + System.out.println("查询失败!"); + e.printStackTrace(); + } finally { + close(); + } + } + +} diff --git a/src/main/java/com/pancm/bigdata/hbase/HbaseTest.java b/src/main/java/com/pancm/bigdata/hbase/HbaseTest.java new file mode 100644 index 0000000..0dbf757 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/hbase/HbaseTest.java @@ -0,0 +1,86 @@ +package com.pancm.bigdata.hbase; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.fastjson.JSONObject; +/** + * +* Title: hbaseTest +* Description: HBase 相关测试 +* Version:1.0.0 +* @author pancm +* @date 2017年11月23日 + */ +public class HbaseTest { + + public static void main(String[] args) { + test(); + } + + /** + * 一些测试 + */ + private static void test() { + String tableName1="t_student",tableName2="t_student_info"; + String []columnFamily1={"st1","st2"}; + String []columnFamily2={"stf1","stf2"}; + HBaseUtil.creatTable(tableName1, columnFamily1); + HBaseUtil.creatTable(tableName2, columnFamily2); + + HBaseUtil.insert(tableName1, "1001", columnFamily1[0], "name", "zhangsan"); + HBaseUtil.insert(tableName1, "1002", columnFamily1[0], "name", "lisi"); + HBaseUtil.insert(tableName1, "1001", columnFamily1[1], "age", "18"); + HBaseUtil.insert(tableName1, "1002", columnFamily1[1], "age", "20"); + + HBaseUtil.insert(tableName2, "1001", columnFamily2[0], "phone", "123456"); + HBaseUtil.insert(tableName2, "1002", columnFamily2[0], "phone", "234567"); + HBaseUtil.insert(tableName2, "1001", columnFamily2[1], "mail", "123@163.com"); + HBaseUtil.insert(tableName2, "1002", columnFamily2[1], "mail", "234@163.com"); + + HBaseUtil.select(tableName1); //查询该表所有数据 + HBaseUtil.select(tableName1, "1001"); //根据表名和行健查询 + HBaseUtil.select(tableName2, "1002",columnFamily2[0]); //根据表名、行健和列族查询 + HBaseUtil.select(tableName2, "1002",columnFamily2[1],"mail"); //根据表名、行健、列族、和列查询 + + HBaseUtil.select(tableName1, "1002"); //根据表名和行健查询 + HBaseUtil.delete(tableName1, "1002", columnFamily1[0]);//删除数据 + HBaseUtil.select(tableName1, "1002"); //根据表名和行健查询 + + } + + + + + + + /** + * 批量测试方法 + * @param tableName 表名 + * @param family 列族 + * @param qualifier 列 + * @param value 值 + * @param k 次数 + */ + public static void insterTest(String tableName,String family,String qualifier,String value, int k){ + List list =new ArrayList<>(); + for(int i=1;i<=k;i++){ + JSONObject json =new JSONObject(); + json.put("rowKey", i); //行健 + json.put("family", family); //列族 + json.put("qualifier", qualifier); //列 + if("t_student".equals(tableName)){ //如果是t_student 姓名则加上编号 + json.put("value", value+i); //值 + }else if("".equals(value)){ //如果为空,则是年龄 + json.put("value", i); //值 + }else{ //否则就是性别 + json.put("value", value); //值 + } + + list.add(json); + } + HBaseUtil.insertBatch(tableName,list); + } + + +} diff --git a/src/main/java/com/pancm/bigdata/hbase/package-info.java b/src/main/java/com/pancm/bigdata/hbase/package-info.java new file mode 100644 index 0000000..f8bc74d --- /dev/null +++ b/src/main/java/com/pancm/bigdata/hbase/package-info.java @@ -0,0 +1,8 @@ +/** +* @Title: package-info +* @Description: hbase相关的代码 +* @Version:1.0.0 +* @author pancm +* @date 2018年9月21日 +*/ +package com.pancm.bigdata.hbase; \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/storm/example/WordCountApp.java b/src/main/java/com/pancm/bigdata/storm/example/WordCountApp.java new file mode 100644 index 0000000..dd34824 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example/WordCountApp.java @@ -0,0 +1,60 @@ +package com.pancm.bigdata.storm.example; + +import org.apache.storm.Config; +import org.apache.storm.LocalCluster; +import org.apache.storm.StormSubmitter; +import org.apache.storm.generated.AlreadyAliveException; +import org.apache.storm.generated.AuthorizationException; +import org.apache.storm.generated.InvalidTopologyException; +import org.apache.storm.generated.StormTopology; +import org.apache.storm.topology.TopologyBuilder; +import org.apache.storm.tuple.Fields; + + + +/** + * +* Title: WordCountApp +* Description: 测试storm本地模式 统计words单次个数 +* 源代码地址:http://www.tianshouzhi.com/api/tutorials/storm/54 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordCountApp { + public static void main(String[] args) throws InterruptedException, AlreadyAliveException, InvalidTopologyException { + //定义拓扑 + TopologyBuilder builder = new TopologyBuilder(); + builder.setSpout("word-reader" , new WordReader()); + builder.setBolt("word-normalizer" , new WordNormalizer()).shuffleGrouping("word-reader" ); + builder.setBolt("word-counter" , new WordCounter()).fieldsGrouping("word-normalizer" , new Fields("word")); + StormTopology topology = builder.createTopology(); + //配置 + + Config conf = new Config(); + String fileName ="words.txt" ; + conf.put("fileName" , fileName ); + conf.setDebug(false); + + //运行拓扑 + System.out.println("开始..."); + if(args !=null&&args.length>0){ //有参数时,表示向集群提交作业,并把第一个参数当做topology名称 + System.out.println("远程模式"); + try { + StormSubmitter.submitTopology(args[0], conf, topology); + } catch (AuthorizationException e) { + e.printStackTrace(); + } + } else{//没有参数时,本地提交 + //启动本地模式 + System.out.println("本地模式"); + LocalCluster cluster = new LocalCluster(); + cluster.submitTopology("Getting-Started-Topologie" , conf , topology ); + Thread.sleep(5000); + //关闭本地集群 + cluster.shutdown(); + } + System.out.println("结束"); + + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/example/WordCounter.java b/src/main/java/com/pancm/bigdata/storm/example/WordCounter.java new file mode 100644 index 0000000..1e7ef0c --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example/WordCounter.java @@ -0,0 +1,84 @@ +package com.pancm.bigdata.storm.example; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.IRichBolt; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.tuple.Tuple; + +/** + * +* Title: WordCounter +* Description: 该类主要用于统计 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordCounter implements IRichBolt { + /** + * + */ + private static final long serialVersionUID = 1L; + Integer id; + String name; + Map counters; + private OutputCollector collector; + + /** + * 当Bolt销毁时,我们会显示单词数量 + */ + @Override + public void cleanup() { + System.out.println("开始显示单词数量..."); + for (Map.Entry entry : counters.entrySet()) { + System.out.println(entry.getKey() + ": " + entry.getValue()); + } + System.out.println("WordCounter.cleanup()"); + } + + /** + * 为每个单词计数 + */ + @Override + public void execute(Tuple input) { + System.out.println("WordCounter.execute()"); + String str = input.getString(0); + /** + * 如果单词尚不存在于map,我们就创建一个,如果已在,我们就为它加1 + */ + if (!counters.containsKey(str)) { + counters.put(str, 1); + } else { + Integer c = counters.get(str) + 1; + counters.put(str, c); + } + // 对元组作为应答 + collector.ack(input); + } + + /** + * 初始化 + */ + @Override + public void prepare(Map stormConf, TopologyContext context, + OutputCollector collector) { + this.counters = new HashMap(); + this.collector = collector; + this.name = context.getThisComponentId(); + this.id = context.getThisTaskId(); + } + + @Override + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out.println("WordCounter.declareOutputFields()"); + } + + @Override + public Map getComponentConfiguration() { + System.out.println("WordCounter.getComponentConfiguration()"); + return null; + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/example/WordNormalizer.java b/src/main/java/com/pancm/bigdata/storm/example/WordNormalizer.java new file mode 100644 index 0000000..3eb21ef --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example/WordNormalizer.java @@ -0,0 +1,73 @@ +package com.pancm.bigdata.storm.example; + +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.IRichBolt; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Tuple; +import org.apache.storm.tuple.Values; + + +/** + * +* Title: WordNormalizer +* Description:该类主要用于格式化数据 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordNormalizer implements IRichBolt { + /** + * + */ + private static final long serialVersionUID = 3644849073824009317L; + private OutputCollector collector; + private static int count=1; + + /** + * *bolt*从单词文件接收到文本行,并标准化它。 文本行会全部转化成小写,并切分它,从中得到所有单词。 + */ + public void execute(Tuple input) { + System.out.println("WordNormalizer.execute()执行次数:"+count); + String sentence = input.getString(0); + String[] words = sentence.split(" "); + for (String word : words) { + word = word.trim(); + if (!word.isEmpty()) { + word = word.toLowerCase(); + /* //发布这个单词 */ + collector.emit(input, new Values(word)); + } + } + // 对元组做出应答 + collector.ack(input); + count++; + } + + public void prepare(Map stormConf, TopologyContext context, + OutputCollector collector) { + System.out.println("WordNormalizer.prepare()"); + this.collector = collector; + } + + /** + * 这个*bolt*只会发布“word”域 + */ + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out.println("WordNormalizer.declareOutputFields()"); + declarer.declare(new Fields("word")); + } + + @Override + public Map getComponentConfiguration() { + System.out.println("WordNormalizer.getComponentConfiguration()"); + return null; + } + + public void cleanup() { + System.out.println("WordNormalizer.cleanup()"); + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/example/WordReader.java b/src/main/java/com/pancm/bigdata/storm/example/WordReader.java new file mode 100644 index 0000000..631fe46 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example/WordReader.java @@ -0,0 +1,129 @@ +package com.pancm.bigdata.storm.example; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Map; + +import org.apache.storm.spout.SpoutOutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.IRichSpout; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Values; + + +/** + * +* Title: WordReader +* Description: 该类主要用于从外部数据源words.txt中获取数据 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordReader implements IRichSpout { + /** + * + */ + private static final long serialVersionUID = 6146631397258548505L; + private SpoutOutputCollector collector; + private FileReader fileReader; + BufferedReader reader; + private boolean completed = false; + + /** + * 这个方法做的惟一一件事情就是分发文件中的文本行 + */ + public void nextTuple() { + /** + * 这个方法会不断的被调用,直到整个文件都读完了,我们将等待并返回。 + */ + if (completed) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // 什么也不做 + } + return; + } + String str; + + try { + int i = 0; + // 读所有文本行 + while ((str = reader.readLine()) != null) { + System.out.println("WordReader.nextTuple(),emits time:" + i++); + /** + * 按行发布一个新值 + */ + this.collector.emit(new Values(str), str); + } + } catch (Exception e) { + throw new RuntimeException("Error reading tuple", e); + } finally { + completed = true; + } + } + + /** + * + * 当Spout被创建之后,这个方法会被掉用 + */ + public void open(Map conf, TopologyContext context, + SpoutOutputCollector collector) { + + System.out.println("WordReader.open(Map conf, TopologyContext context, SpoutOutputCollector collector)"); + String fileName = conf.get("fileName").toString(); + InputStream inputStream = WordReader.class.getClassLoader() + .getResourceAsStream(fileName); + reader = new BufferedReader(new InputStreamReader(inputStream)); + this.collector = collector; + } + + /** + * 声明数据格式,即输出的一个Tuple中,包含几个字段 + */ + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out + .println("WordReader.declareOutputFields(OutputFieldsDeclarer declarer)"); + declarer.declare(new Fields("line")); + } + + @Override + public void activate() { + System.out.println("WordReader.activate()"); + } + + @Override + public void deactivate() { + System.out.println("WordReader.deactivate()"); + } + + @Override + public Map getComponentConfiguration() { + System.out.println("WordReader.getComponentConfiguration()"); + return null; + } + + /** + * 当一个Tuple处理成功时,会调用这个方法 + */ + public void ack(Object msgId) { + System.out.println("WordReader.ack(Object msgId):" + msgId); + } + + /** + * 当Topology停止时,会调用这个方法 + */ + public void close() { + System.out.println("WordReader.close()"); + } + + /** + * 当一个Tuple处理失败时,会调用这个方法 + */ + public void fail(Object msgId) { + System.out.println("WordReader.fail(Object msgId):" + msgId); + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/example/package-info.java b/src/main/java/com/pancm/bigdata/storm/example/package-info.java new file mode 100644 index 0000000..909e534 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: + * Version:1.0.0 + * @author pancm + * @date 2017年12月28日 + */ +package com.pancm.bigdata.storm.example; \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/storm/example1/ReportBolt.java b/src/main/java/com/pancm/bigdata/storm/example1/ReportBolt.java new file mode 100644 index 0000000..04f6f07 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example1/ReportBolt.java @@ -0,0 +1,66 @@ +package com.pancm.bigdata.storm.example1; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichBolt; +import org.apache.storm.tuple.Tuple; + + + +/** + * 生成一份报告 + * @author soul + * + */ +public class ReportBolt extends BaseRichBolt { + + private HashMap counts = null;//保存单词和对应的计数 + + public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { + // TODO Auto-generated method stub + + this.counts = new HashMap(); + } + + public void execute(Tuple input) { + // TODO Auto-generated method stub + + String word = input.getStringByField("word"); + Long count = input.getLongByField("count"); + this.counts.put(word, count); + + //实时输出 + System.out.println("结果:"+this.counts); + } + + public void declareOutputFields(OutputFieldsDeclarer declarer) { + // TODO Auto-generated method stub + //这里是末端bolt,不需要发射数据流,这里无需定义 + } + + /** + * cleanup是IBolt接口中定义 + * Storm在终止一个bolt之前会调用这个方法 + * 本例我们利用cleanup()方法在topology关闭时输出最终的计数结果 + * 通常情况下,cleanup()方法用来释放bolt占用的资源,如打开的文件句柄或数据库连接 + * 但是当Storm拓扑在一个集群上运行,IBolt.cleanup()方法不能保证执行(这里是开发模式,生产环境不要这样做)。 + */ + public void cleanup(){ + System.out.println("---------- FINAL COUNTS -----------"); + + ArrayList keys = new ArrayList(); + keys.addAll(this.counts.keySet()); + Collections.sort(keys); + for(String key : keys){ + System.out.println(key + " : " + this.counts.get(key)); + } + System.out.println("----------------------------"); + } + +} diff --git a/src/main/java/com/pancm/bigdata/storm/example1/SentenceSpout.java b/src/main/java/com/pancm/bigdata/storm/example1/SentenceSpout.java new file mode 100644 index 0000000..f98fed2 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example1/SentenceSpout.java @@ -0,0 +1,77 @@ +package com.pancm.bigdata.storm.example1; + +import java.util.Map; + +import org.apache.storm.spout.SpoutOutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichSpout; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Values; +import org.apache.storm.utils.Utils; + + + +/** + * 向后端发射tuple数据流 + * @author soul + * + */ +public class SentenceSpout extends BaseRichSpout { + + //BaseRichSpout是ISpout接口和IComponent接口的简单实现,接口对用不到的方法提供了默认的实现 + + /** + * + */ + private static final long serialVersionUID = 1L; + private SpoutOutputCollector collector; + private String[] sentences = { + "my name is soul", + "im a boy", + "i have a dog", + "my dog has fleas", + "my girl friend is beautiful" + }; + + private int index=0; + + /** + * open()方法中是ISpout接口中定义,在Spout组件初始化时被调用。 + * open()接受三个参数:一个包含Storm配置的Map,一个TopologyContext对象,提供了topology中组件的信息,SpoutOutputCollector对象提供发射tuple的方法。 + * 在这个例子中,我们不需要执行初始化,只是简单的存储在一个SpoutOutputCollector实例变量。 + */ + public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) { + // TODO Auto-generated method stub + this.collector = collector; + } + + /** + * nextTuple()方法是任何Spout实现的核心。 + * Storm调用这个方法,向输出的collector发出tuple。 + * 在这里,我们只是发出当前索引的句子,并增加该索引准备发射下一个句子。 + */ + public void nextTuple() { + //collector.emit(new Values("hello world this is a test")); + + // TODO Auto-generated method stub + this.collector.emit(new Values(sentences[index])); + index++; + if (index>=sentences.length) { + index=0; + } + Utils.sleep(1); + } + + /** + * declareOutputFields是在IComponent接口中定义的,所有Storm的组件(spout和bolt)都必须实现这个接口 + * 用于告诉Storm流组件将会发出那些数据流,每个流的tuple将包含的字段 + */ + public void declareOutputFields(OutputFieldsDeclarer declarer) { + // TODO Auto-generated method stub + + declarer.declare(new Fields("sentence"));//告诉组件发出数据流包含sentence字段 + + } + +} diff --git a/src/main/java/com/pancm/bigdata/storm/example1/SplitSentenceBolt.java b/src/main/java/com/pancm/bigdata/storm/example1/SplitSentenceBolt.java new file mode 100644 index 0000000..a0f4a7e --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example1/SplitSentenceBolt.java @@ -0,0 +1,65 @@ +package com.pancm.bigdata.storm.example1; + +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichBolt; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Tuple; +import org.apache.storm.tuple.Values; + + + +/** + * 订阅sentence spout发射的tuple流,实现分割单词 + * @author soul + * + */ +public class SplitSentenceBolt extends BaseRichBolt { + //BaseRichBolt是IComponent和IBolt接口的实现 + //继承这个类,就不用去实现本例不关心的方法 + + /** + * + */ + private static final long serialVersionUID = 1L; + private OutputCollector collector; + + /** + * prepare()方法类似于ISpout 的open()方法。 + * 这个方法在blot初始化时调用,可以用来准备bolt用到的资源,比如数据库连接。 + * 本例子和SentenceSpout类一样,SplitSentenceBolt类不需要太多额外的初始化, + * 所以prepare()方法只保存OutputCollector对象的引用。 + */ + public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { + // TODO Auto-generated method stub + this.collector=collector; + + } + + /** + * SplitSentenceBolt核心功能是在类IBolt定义execute()方法,这个方法是IBolt接口中定义。 + * 每次Bolt从流接收一个订阅的tuple,都会调用这个方法。 + * 本例中,收到的元组中查找“sentence”的值, + * 并将该值拆分成单个的词,然后按单词发出新的tuple。 + */ + public void execute(Tuple input) { + // TODO Auto-generated method stub + String sentence = input.getStringByField("sentence"); + String[] words = sentence.split(" "); + for (String word : words) { + this.collector.emit(new Values(word));//向下一个bolt发射数据 + } + } + + /** + * plitSentenceBolt类定义一个元组流,每个包含一个字段(“word”)。 + */ + public void declareOutputFields(OutputFieldsDeclarer declarer) { + // TODO Auto-generated method stub + declarer.declare(new Fields("word")); + } + +} diff --git a/src/main/java/com/pancm/bigdata/storm/example1/WordCountApp.java b/src/main/java/com/pancm/bigdata/storm/example1/WordCountApp.java new file mode 100644 index 0000000..271c0d4 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example1/WordCountApp.java @@ -0,0 +1,83 @@ +package com.pancm.bigdata.storm.example1; + + + +import org.apache.storm.Config; +import org.apache.storm.LocalCluster; +import org.apache.storm.topology.TopologyBuilder; +import org.apache.storm.tuple.Fields; +import org.apache.storm.utils.Utils; + +import com.pancm.bigdata.storm.example1.ReportBolt; +import com.pancm.bigdata.storm.example1.SentenceSpout; +import com.pancm.bigdata.storm.example1.SplitSentenceBolt; +import com.pancm.bigdata.storm.example1.WordCountBolt; + +/** + * 实现单词计数topology + * + */ +public class WordCountApp +{ + private static final String SENTENCE_SPOUT_ID = "sentence-spout"; + private static final String SPLIT_BOLT_ID = "split-bolt"; + private static final String COUNT_BOLT_ID = "count-bolt"; + private static final String REPORT_BOLT_ID = "report-bolt"; + private static final String TOPOLOGY_NAME = "word-count-topology"; + + public static void main( String[] args ) //throws Exception + { + //System.out.println( "Hello World!" ); + //实例化spout和bolt + + SentenceSpout spout = new SentenceSpout(); + SplitSentenceBolt splitBolt = new SplitSentenceBolt(); + WordCountBolt countBolt = new WordCountBolt(); + ReportBolt reportBolt = new ReportBolt(); + + TopologyBuilder builder = new TopologyBuilder();//创建了一个TopologyBuilder实例 + + //TopologyBuilder提供流式风格的API来定义topology组件之间的数据流 + + //builder.setSpout(SENTENCE_SPOUT_ID, spout);//注册一个sentence spout + + //设置两个Executeor(线程),默认一个 + builder.setSpout(SENTENCE_SPOUT_ID, spout,2); + + // SentenceSpout --> SplitSentenceBolt + + //注册一个bolt并订阅sentence发射出的数据流,shuffleGrouping方法告诉Storm要将SentenceSpout发射的tuple随机均匀的分发给SplitSentenceBolt的实例 + //builder.setBolt(SPLIT_BOLT_ID, splitBolt).shuffleGrouping(SENTENCE_SPOUT_ID); + + //SplitSentenceBolt单词分割器设置4个Task,2个Executeor(线程) + builder.setBolt(SPLIT_BOLT_ID, splitBolt,2).setNumTasks(4).shuffleGrouping(SENTENCE_SPOUT_ID); + + // SplitSentenceBolt --> WordCountBolt + + //fieldsGrouping将含有特定数据的tuple路由到特殊的bolt实例中 + //这里fieldsGrouping()方法保证所有“word”字段相同的tuuple会被路由到同一个WordCountBolt实例中 + //builder.setBolt(COUNT_BOLT_ID, countBolt).fieldsGrouping( SPLIT_BOLT_ID, new Fields("word")); + + //WordCountBolt单词计数器设置4个Executeor(线程) + builder.setBolt(COUNT_BOLT_ID, countBolt,4).fieldsGrouping( SPLIT_BOLT_ID, new Fields("word")); + + // WordCountBolt --> ReportBolt + + //globalGrouping是把WordCountBolt发射的所有tuple路由到唯一的ReportBolt + builder.setBolt(REPORT_BOLT_ID, reportBolt).globalGrouping(COUNT_BOLT_ID); + + + Config config = new Config();//Config类是一个HashMap的子类,用来配置topology运行时的行为 + //设置worker数量 + //config.setNumWorkers(2); + //本地提交 + LocalCluster cluster = new LocalCluster(); + + cluster.submitTopology(TOPOLOGY_NAME, config, builder.createTopology()); + + Utils.sleep(10000); + cluster.killTopology(TOPOLOGY_NAME); + cluster.shutdown(); + + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/example1/WordCountBolt.java b/src/main/java/com/pancm/bigdata/storm/example1/WordCountBolt.java new file mode 100644 index 0000000..10546f9 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example1/WordCountBolt.java @@ -0,0 +1,73 @@ +package com.pancm.bigdata.storm.example1; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichBolt; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Tuple; +import org.apache.storm.tuple.Values; + + + +/** + * 订阅 split sentence bolt的输出流,实现单词计数,并发送当前计数给下一个bolt + * @author soul + * + */ +public class WordCountBolt extends BaseRichBolt { + /** + * + */ + private static final long serialVersionUID = 1L; + private OutputCollector collector; + //存储单词和对应的计数 + private HashMap counts = null;//注:不可序列化对象需在prepare中实例化 + + /** + * 大部分实例变量通常是在prepare()中进行实例化,这个设计模式是由topology的部署方式决定的 + * 因为在部署拓扑时,组件spout和bolt是在网络上发送的序列化的实例变量。 + * 如果spout或bolt有任何non-serializable实例变量在序列化之前被实例化(例如,在构造函数中创建) + * 会抛出NotSerializableException并且拓扑将无法发布。 + * 本例中因为HashMap 是可序列化的,所以可以安全地在构造函数中实例化。 + * 但是,通常情况下最好是在构造函数中对基本数据类型和可序列化的对象进行复制和实例化 + * 而在prepare()方法中对不可序列化的对象进行实例化。 + */ + public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { + // TODO Auto-generated method stub + this.collector = collector; + this.counts = new HashMap(); + } + + /** + * 在execute()方法中,我们查找的收到的单词的计数(如果不存在,初始化为0) + * 然后增加计数并存储,发出一个新的词和当前计数组成的二元组。 + * 发射计数作为流允许拓扑的其他bolt订阅和执行额外的处理。 + */ + public void execute(Tuple input) { + // TODO Auto-generated method stub + + String word = input.getStringByField("word"); + Long count = this.counts.get(word); + if (count == null) { + count = 0L;//如果不存在,初始化为0 + } + count++;//增加计数 + this.counts.put(word, count);//存储计数 + this.collector.emit(new Values(word,count)); + } + + /** + * + */ + public void declareOutputFields(OutputFieldsDeclarer declarer) { + // TODO Auto-generated method stub + //声明一个输出流,其中tuple包括了单词和对应的计数,向后发射 + //其他bolt可以订阅这个数据流进一步处理 + declarer.declare(new Fields("word","count")); + } + +} \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/storm/example1/package-info.java b/src/main/java/com/pancm/bigdata/storm/example1/package-info.java new file mode 100644 index 0000000..0b678bb --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/example1/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: + * Version:1.0.0 + * @author pancm + * @date 2017年12月29日 + */ +package com.pancm.bigdata.storm.example1; \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/storm/one/WordCountApp.java b/src/main/java/com/pancm/bigdata/storm/one/WordCountApp.java new file mode 100644 index 0000000..f4a7746 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/one/WordCountApp.java @@ -0,0 +1,60 @@ +package com.pancm.bigdata.storm.one; + +import org.apache.storm.Config; +import org.apache.storm.LocalCluster; +import org.apache.storm.StormSubmitter; +import org.apache.storm.generated.AlreadyAliveException; +import org.apache.storm.generated.AuthorizationException; +import org.apache.storm.generated.InvalidTopologyException; +import org.apache.storm.generated.StormTopology; +import org.apache.storm.topology.TopologyBuilder; +import org.apache.storm.tuple.Fields; + + + +/** + * +* Title: WordCountApp +* Description: 测试storm本地模式 统计words单次个数 +* 源代码地址:http://www.tianshouzhi.com/api/tutorials/storm/54 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordCountApp { + public static void main(String[] args) throws InterruptedException, AlreadyAliveException, InvalidTopologyException { + //定义拓扑 + TopologyBuilder builder = new TopologyBuilder(); + builder.setSpout("word-reader" , new WordReader()); + builder.setBolt("word-normalizer" , new WordNormalizer()).shuffleGrouping("word-reader" ); + builder.setBolt("word-counter" , new WordCounter()).fieldsGrouping("word-normalizer" , new Fields("word")); + StormTopology topology = builder.createTopology(); + //配置 + + Config conf = new Config(); + String fileName ="words.txt" ; + conf.put("fileName" , fileName ); + conf.setDebug(false); + + //运行拓扑 + System.out.println("开始..."); + if(args !=null&&args.length>0){ //有参数时,表示向集群提交作业,并把第一个参数当做topology名称 + System.out.println("远程模式"); + try { + StormSubmitter.submitTopology(args[0], conf, topology); + } catch (AuthorizationException e) { + e.printStackTrace(); + } + } else{//没有参数时,本地提交 + //启动本地模式 + System.out.println("本地模式"); + LocalCluster cluster = new LocalCluster(); + cluster.submitTopology("Getting-Started-Topologie" , conf , topology ); + Thread.sleep(5000); + //关闭本地集群 + cluster.shutdown(); + } + System.out.println("结束"); + + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/one/WordCounter.java b/src/main/java/com/pancm/bigdata/storm/one/WordCounter.java new file mode 100644 index 0000000..2b001c6 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/one/WordCounter.java @@ -0,0 +1,84 @@ +package com.pancm.bigdata.storm.one; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.IRichBolt; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.tuple.Tuple; + +/** + * +* Title: WordCounter +* Description: 该类主要用于统计 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordCounter implements IRichBolt { + /** + * + */ + private static final long serialVersionUID = 1L; + Integer id; + String name; + Map counters; + private OutputCollector collector; + + /** + * 当Bolt销毁时,我们会显示单词数量 + */ + @Override + public void cleanup() { + System.out.println("开始显示单词数量..."); + for (Map.Entry entry : counters.entrySet()) { + System.out.println(entry.getKey() + ": " + entry.getValue()); + } + System.out.println("WordCounter.cleanup()"); + } + + /** + * 为每个单词计数 + */ + @Override + public void execute(Tuple input) { + System.out.println("WordCounter.execute()"); + String str = input.getString(0); + /** + * 如果单词尚不存在于map,我们就创建一个,如果已在,我们就为它加1 + */ + if (!counters.containsKey(str)) { + counters.put(str, 1); + } else { + Integer c = counters.get(str) + 1; + counters.put(str, c); + } + // 对元组作为应答 + collector.ack(input); + } + + /** + * 初始化 + */ + @Override + public void prepare(Map stormConf, TopologyContext context, + OutputCollector collector) { + this.counters = new HashMap(); + this.collector = collector; + this.name = context.getThisComponentId(); + this.id = context.getThisTaskId(); + } + + @Override + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out.println("WordCounter.declareOutputFields()"); + } + + @Override + public Map getComponentConfiguration() { + System.out.println("WordCounter.getComponentConfiguration()"); + return null; + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/one/WordNormalizer.java b/src/main/java/com/pancm/bigdata/storm/one/WordNormalizer.java new file mode 100644 index 0000000..ddfbce2 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/one/WordNormalizer.java @@ -0,0 +1,71 @@ +package com.pancm.bigdata.storm.one; + +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.IRichBolt; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Tuple; +import org.apache.storm.tuple.Values; + + +/** + * +* Title: WordNormalizer +* Description:该类主要用于格式化数据 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordNormalizer implements IRichBolt { + /** + * + */ + private static final long serialVersionUID = 3644849073824009317L; + private OutputCollector collector; + + /** + * *bolt*从单词文件接收到文本行,并标准化它。 文本行会全部转化成小写,并切分它,从中得到所有单词。 + */ + public void execute(Tuple input) { + System.out.println("WordNormalizer.execute()"); + String sentence = input.getString(0); + String[] words = sentence.split(" "); + for (String word : words) { + word = word.trim(); + if (!word.isEmpty()) { + word = word.toLowerCase(); + /* //发布这个单词 */ + collector.emit(input, new Values(word)); + } + } + // 对元组做出应答 + collector.ack(input); + } + + public void prepare(Map stormConf, TopologyContext context, + OutputCollector collector) { + System.out.println("WordNormalizer.prepare()"); + this.collector = collector; + } + + /** + * 这个*bolt*只会发布“word”域 + */ + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out.println("WordNormalizer.declareOutputFields()"); + declarer.declare(new Fields("word")); + } + + @Override + public Map getComponentConfiguration() { + System.out.println("WordNormalizer.getComponentConfiguration()"); + return null; + } + + public void cleanup() { + System.out.println("WordNormalizer.cleanup()"); + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/one/WordReader.java b/src/main/java/com/pancm/bigdata/storm/one/WordReader.java new file mode 100644 index 0000000..146cd09 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/one/WordReader.java @@ -0,0 +1,130 @@ +package com.pancm.bigdata.storm.one; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Map; + +import org.apache.storm.spout.SpoutOutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.IRichSpout; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Values; + + +/** + * +* Title: WordReader +* Description: 该类主要用于从外部数据源words.txt中获取数据 +* Version:1.0.0 +* @author pancm +* @date 2017年12月28日 + */ +public class WordReader implements IRichSpout { + /** + * + */ + private static final long serialVersionUID = 6146631397258548505L; + private SpoutOutputCollector collector; + private FileReader fileReader; + BufferedReader reader; + private boolean completed = false; + + /** + * 这个方法做的惟一一件事情就是分发文件中的文本行 + */ + public void nextTuple() { + /** + * 这个方法会不断的被调用,直到整个文件都读完了,我们将等待并返回。 + */ + if (completed) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // 什么也不做 + } + return; + } + String str; + + try { + int i = 0; + // 读所有文本行 + while ((str = reader.readLine()) != null) { + System.out.println("WordReader.nextTuple(),emits time:" + i++); + /** + * 按行发布一个新值 + */ + this.collector.emit(new Values(str), str); + } + } catch (Exception e) { + throw new RuntimeException("Error reading tuple", e); + } finally { + completed = true; + } + } + + /** + * + * 当Spout被创建之后,这个方法会被掉用 + */ + public void open(Map conf, TopologyContext context, + SpoutOutputCollector collector) { + + System.out + .println("WordReader.open(Map conf, TopologyContext context, SpoutOutputCollector collector)"); + String fileName = conf.get("fileName").toString(); + InputStream inputStream = WordReader.class.getClassLoader() + .getResourceAsStream(fileName); + reader = new BufferedReader(new InputStreamReader(inputStream)); + this.collector = collector; + } + + /** + * 声明数据格式,即输出的一个Tuple中,包含几个字段 + */ + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out + .println("WordReader.declareOutputFields(OutputFieldsDeclarer declarer)"); + declarer.declare(new Fields("line")); + } + + @Override + public void activate() { + System.out.println("WordReader.activate()"); + } + + @Override + public void deactivate() { + System.out.println("WordReader.deactivate()"); + } + + @Override + public Map getComponentConfiguration() { + System.out.println("WordReader.getComponentConfiguration()"); + return null; + } + + /** + * 当一个Tuple处理成功时,会调用这个方法 + */ + public void ack(Object msgId) { + System.out.println("WordReader.ack(Object msgId):" + msgId); + } + + /** + * 当Topology停止时,会调用这个方法 + */ + public void close() { + System.out.println("WordReader.close()"); + } + + /** + * 当一个Tuple处理失败时,会调用这个方法 + */ + public void fail(Object msgId) { + System.out.println("WordReader.fail(Object msgId):" + msgId); + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/one/package-info.java b/src/main/java/com/pancm/bigdata/storm/one/package-info.java new file mode 100644 index 0000000..a254b55 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/one/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: + * Version:1.0.0 + * @author pancm + * @date 2017年12月28日 + */ +package com.pancm.bigdata.storm.one; \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/storm/package-info.java b/src/main/java/com/pancm/bigdata/storm/package-info.java new file mode 100644 index 0000000..cbf0ffa --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: strom相关代码 + * Version:1.0.0 + * @author pancm + * @date 2018年1月11日 + */ +package com.pancm.bigdata.storm; \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/storm/test/App.java b/src/main/java/com/pancm/bigdata/storm/test/App.java new file mode 100644 index 0000000..a3f922a --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test/App.java @@ -0,0 +1,50 @@ +package com.pancm.bigdata.storm.test; + +import org.apache.storm.Config; +import org.apache.storm.LocalCluster; +import org.apache.storm.StormSubmitter; +import org.apache.storm.topology.TopologyBuilder; + +/** + * +* Title: App +* Description: +* storm测试 +* Version:1.0.0 +* @author pancm +* @date 2018年3月6日 + */ +public class App { + + private static final String str1="test1"; + private static final String str2="test2"; + + public static void main(String[] args) { + // TODO Auto-generated method stub + //定义一个拓扑 + TopologyBuilder builder=new TopologyBuilder(); + builder.setSpout(str1, new TestSpout()); + builder.setBolt(str2, new TestBolt()).shuffleGrouping(str1); + Config conf = new Config(); + conf.put("test", "test"); + try{ + //运行拓扑 + if(args !=null&&args.length>0){ //有参数时,表示向集群提交作业,并把第一个参数当做topology名称 + System.out.println("远程模式"); + StormSubmitter.submitTopology(args[0], conf, builder.createTopology()); + } else{//没有参数时,本地提交 + //启动本地模式 + System.out.println("本地模式"); + LocalCluster cluster = new LocalCluster(); + cluster.submitTopology("111" ,conf, builder.createTopology() ); +// Thread.sleep(2000); +// //关闭本地集群 +// cluster.shutdown(); + } + }catch (Exception e){ + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/com/pancm/bigdata/storm/test/TestBolt.java b/src/main/java/com/pancm/bigdata/storm/test/TestBolt.java new file mode 100644 index 0000000..e65f346 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test/TestBolt.java @@ -0,0 +1,80 @@ +package com.pancm.bigdata.storm.test; + +import java.util.Map; +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichBolt; +import org.apache.storm.tuple.Tuple; + +/** + * +* Title: TestBolt +* Description: +* 用于处理消息 +* Version:1.0.0 +* @author pancm +* @date 2018年3月6日 + */ +public class TestBolt extends BaseRichBolt{ + + /** + * + */ + private static final long serialVersionUID = 4743224635827696343L; + + private OutputCollector collector; + private long count=1; + /** + * 在Bolt启动前执行,提供Bolt启动环境配置的入口 + * 一般对于不可序列化的对象进行实例化。 + * 注:如果是可以序列化的对象,那么最好是使用构造函数。 + */ + @Override + public void prepare(Map map, TopologyContext arg1, OutputCollector collector) { + System.out.println("prepare:"+map.get("test")); + this.collector=collector; + } + + /** + * execute()方法是Bolt实现的核心。 + * 也就是执行方法,每次Bolt从流接收一个订阅的tuple,都会调用这个方法。 + */ + @Override + public void execute(Tuple tuple) { + /** + * 接受消息可以使用这两种方式进行接收。 + * 个人推荐第二种。 + */ +// String msg=tuple.getString(0); + String msg=tuple.getStringByField("test"); + //这里我们就不做消息的处理,只打印 + System.out.println("Bolt第"+count+"接受的消息:"+msg); + count++; + /** + * + * 没次调用处理一个输入的tuple,所有的tuple都必须在一定时间内应答。 + * 可以是ack或者fail。否则,spout就会重发tuple。 + * 如果继承的是IRichBolt,则需要手动ack。 + * 这里就不用了,BaseRichBolt会自动帮我们应答。 + */ +// collector.ack(tuple); + } + + /** + * 声明数据格式 + */ + @Override + public void declareOutputFields(OutputFieldsDeclarer arg0) { + + } + + /** + * cleanup是IBolt接口中定义,用于释放bolt占用的资源。 + * Storm在终止一个bolt之前会调用这个方法。 + */ + @Override + public void cleanup() { + System.out.println("资源释放"); + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/test/TestSpout.java b/src/main/java/com/pancm/bigdata/storm/test/TestSpout.java new file mode 100644 index 0000000..b35486b --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test/TestSpout.java @@ -0,0 +1,92 @@ +package com.pancm.bigdata.storm.test; + +import java.util.Map; + +import org.apache.storm.spout.SpoutOutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichSpout; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Values; + +/** + * +* Title: TestSpout +* Description: +* Spout 发射器 +* 用于向Bolt发送消息 +* Version:1.0.0 +* @author pancm +* @date 2018年3月6日 + */ +public class TestSpout extends BaseRichSpout{ + + private static final long serialVersionUID = 225243592780939490L; + + private SpoutOutputCollector collector; + private String message="这是个测试消息!"; + private static final String field="test"; + private long count=1; + + + /** + * open()方法中是在ISpout接口中定义,在Spout组件初始化时被调用。 + * 有三个参数: + * 1.Storm配置的Map; + * 2.topology中组件的信息; + * 3.发射tuple的方法; + */ + @Override + public void open(Map map, TopologyContext arg1, SpoutOutputCollector collector) { + System.out.println("open:"+map.get("test")); + this.collector = collector; + } + + /** + * nextTuple()方法是Spout实现的核心。 + * 也就是主要执行方法,用于输出信息,通过collector.emit方法发射。 + */ + @Override + public void nextTuple() { + if(count<=2){ + System.out.println("第"+count+"次开始发送数据..."); + this.collector.emit(new Values(message)); + } + count++; + } + + + /** + * declareOutputFields是在IComponent接口中定义,用于声明数据格式。 + * 即输出的一个Tuple中,包含几个字段。 + */ + @Override + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out.println("定义格式..."); + declarer.declare(new Fields(field)); + } + + /** + * 当一个Tuple处理成功时,会调用这个方法 + */ + @Override + public void ack(Object obj) { + System.out.println("ack:"+obj); + } + + /** + * 当Topology停止时,会调用这个方法 + */ + @Override + public void close() { + System.out.println("关闭..."); + } + + /** + * 当一个Tuple处理失败时,会调用这个方法 + */ + @Override + public void fail(Object obj) { + System.out.println("失败:"+obj); + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/test/package-info.java b/src/main/java/com/pancm/bigdata/storm/test/package-info.java new file mode 100644 index 0000000..f8ba568 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: + * Version:1.0.0 + * @author pancm + * @date 2018年3月15日 + */ +package com.pancm.bigdata.storm.test; \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/storm/test2/App.java b/src/main/java/com/pancm/bigdata/storm/test2/App.java new file mode 100644 index 0000000..7a45025 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test2/App.java @@ -0,0 +1,55 @@ +package com.pancm.bigdata.storm.test2; + +import org.apache.storm.Config; +import org.apache.storm.LocalCluster; +import org.apache.storm.StormSubmitter; +import org.apache.storm.topology.TopologyBuilder; +import org.apache.storm.tuple.Fields; + +/** + * +* Title: App +* Description: +* storm测试 +* Version:1.0.0 +* @author pancm +* @date 2018年3月6日 + */ +public class App { + + private static final String test_spout="test_spout"; + private static final String test_bolt="test_bolt"; + private static final String test2_bolt="test2_bolt"; + + public static void main(String[] args) { + //定义一个拓扑 + TopologyBuilder builder=new TopologyBuilder(); + //设置两个Executeor(线程),默认一个 + builder.setSpout(test_spout, new TestSpout(),2); + //shuffleGrouping:表示是随机分组 + //设置两个Executeor(线程),和两个task + builder.setBolt(test_bolt, new TestBolt(),2).setNumTasks(2).shuffleGrouping(test_spout); + //fieldsGrouping:表示是按字段分组 + //设置两个Executeor(线程),和两个task + builder.setBolt(test2_bolt, new Test2Bolt(),2).setNumTasks(2).fieldsGrouping(test_bolt, new Fields("count")); + Config conf = new Config(); + conf.put("test", "test"); + try{ + //运行拓扑 + if(args !=null&&args.length>0){ //有参数时,表示向集群提交作业,并把第一个参数当做topology名称 + System.out.println("运行远程模式"); + StormSubmitter.submitTopology(args[0], conf, builder.createTopology()); + } else{//没有参数时,本地提交 + //启动本地模式 + System.out.println("运行本地模式"); + LocalCluster cluster = new LocalCluster(); + cluster.submitTopology("Word-counts" ,conf, builder.createTopology() ); + Thread.sleep(20000); +// //关闭本地集群 + cluster.shutdown(); + } + }catch (Exception e){ + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/test2/Test2Bolt.java b/src/main/java/com/pancm/bigdata/storm/test2/Test2Bolt.java new file mode 100644 index 0000000..83d299c --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test2/Test2Bolt.java @@ -0,0 +1,89 @@ +package com.pancm.bigdata.storm.test2; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichBolt; +import org.apache.storm.tuple.Tuple; + +/** + * +* Title: Test2Bolt +* Description: +* 统计单词出现的次数 +* Version:1.0.0 +* @author pancm +* @date 2018年3月16日 + */ +public class Test2Bolt extends BaseRichBolt{ + + /** + * + */ + private static final long serialVersionUID = 4743224635827696343L; + + + /** + * 保存单词和对应的计数 + */ + private HashMap counts = null; + + private long count=1; + /** + * 在Bolt启动前执行,提供Bolt启动环境配置的入口 + * 一般对于不可序列化的对象进行实例化。 + * 注:如果是可以序列化的对象,那么最好是使用构造函数。 + */ + @Override + public void prepare(Map map, TopologyContext arg1, OutputCollector collector) { + System.out.println("prepare:"+map.get("test")); + this.counts=new HashMap(); + } + + /** + * execute()方法是Bolt实现的核心。 + * 也就是执行方法,每次Bolt从流接收一个订阅的tuple,都会调用这个方法。 + * + */ + @Override + public void execute(Tuple tuple) { + String msg=tuple.getStringByField("count"); + System.out.println("第"+count+"次统计单词出现的次数"); + /** + * 如果不包含该单词,说明在该map是第一次出现 + * 否则进行加1 + */ + if (!counts.containsKey(msg)) { + counts.put(msg, 1); + } else { + counts.put(msg, counts.get(msg)+1); + } + count++; + } + + + /** + * cleanup是IBolt接口中定义,用于释放bolt占用的资源。 + * Storm在终止一个bolt之前会调用这个方法。 + */ + @Override + public void cleanup() { + System.out.println("===========开始显示单词数量============"); + for (Map.Entry entry : counts.entrySet()) { + System.out.println(entry.getKey() + ": " + entry.getValue()); + } + System.out.println("===========结束============"); + System.out.println("Test2Bolt的资源释放"); + } + + /** + * 声明数据格式 + */ + @Override + public void declareOutputFields(OutputFieldsDeclarer arg0) { + + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/test2/TestBolt.java b/src/main/java/com/pancm/bigdata/storm/test2/TestBolt.java new file mode 100644 index 0000000..70d750e --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test2/TestBolt.java @@ -0,0 +1,74 @@ +package com.pancm.bigdata.storm.test2; + +import java.util.Map; + +import org.apache.storm.task.OutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichBolt; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Tuple; +import org.apache.storm.tuple.Values; + + +/** + * +* Title: TestBolt +* Description: +* 对单词进行分割 +* Version:1.0.0 +* @author pancm +* @date 2018年3月16日 + */ +public class TestBolt extends BaseRichBolt{ + + /** + * + */ + private static final long serialVersionUID = 4743224635827696343L; + + private OutputCollector collector; + + /** + * 在Bolt启动前执行,提供Bolt启动环境配置的入口 + * 一般对于不可序列化的对象进行实例化。 + * 注:如果是可以序列化的对象,那么最好是使用构造函数。 + */ + @Override + public void prepare(Map map, TopologyContext arg1, OutputCollector collector) { + System.out.println("prepare:"+map.get("test")); + this.collector=collector; + } + + /** + * execute()方法是Bolt实现的核心。 + * 也就是执行方法,每次Bolt从流接收一个订阅的tuple,都会调用这个方法。 + */ + @Override + public void execute(Tuple tuple) { + String msg=tuple.getStringByField("word"); + System.out.println("开始分割单词:"+msg); + String[] words = msg.toLowerCase().split(" "); + for (String word : words) { + this.collector.emit(new Values(word));//向下一个bolt发射数据 + } + + } + + /** + * 声明数据格式 + */ + @Override + public void declareOutputFields(OutputFieldsDeclarer declarer) { + declarer.declare(new Fields("count")); + } + + /** + * cleanup是IBolt接口中定义,用于释放bolt占用的资源。 + * Storm在终止一个bolt之前会调用这个方法。 + */ + @Override + public void cleanup() { + System.out.println("TestBolt的资源释放"); + } +} diff --git a/src/main/java/com/pancm/bigdata/storm/test2/TestSpout.java b/src/main/java/com/pancm/bigdata/storm/test2/TestSpout.java new file mode 100644 index 0000000..2c4aaab --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test2/TestSpout.java @@ -0,0 +1,96 @@ +package com.pancm.bigdata.storm.test2; + +import java.util.Map; + +import org.apache.storm.spout.SpoutOutputCollector; +import org.apache.storm.task.TopologyContext; +import org.apache.storm.topology.OutputFieldsDeclarer; +import org.apache.storm.topology.base.BaseRichSpout; +import org.apache.storm.tuple.Fields; +import org.apache.storm.tuple.Values; + +/** + * +* Title: TestSpout +* Description: +* 发送信息 +* Version:1.0.0 +* @author pancm +* @date 2018年3月6日 + */ +public class TestSpout extends BaseRichSpout{ + + private static final long serialVersionUID = 225243592780939490L; + + private SpoutOutputCollector collector; + private static final String field="word"; + private int count=1; + private String[] message = { + "My nickname is xuwujing", + "My blog address is http://www.panchengming.com/", + "My interest is playing games" + }; + + /** + * open()方法中是在ISpout接口中定义,在Spout组件初始化时被调用。 + * 有三个参数: + * 1.Storm配置的Map; + * 2.topology中组件的信息; + * 3.发射tuple的方法; + */ + @Override + public void open(Map map, TopologyContext arg1, SpoutOutputCollector collector) { + System.out.println("open:"+map.get("test")); + this.collector = collector; + } + + /** + * nextTuple()方法是Spout实现的核心。 + * 也就是主要执行方法,用于输出信息,通过collector.emit方法发射。 + */ + @Override + public void nextTuple() { + + if(count<=message.length){ + System.out.println("第"+count +"次开始发送数据..."); + this.collector.emit(new Values(message[count-1])); + } + count++; + } + + + /** + * declareOutputFields是在IComponent接口中定义,用于声明数据格式。 + * 即输出的一个Tuple中,包含几个字段。 + */ + @Override + public void declareOutputFields(OutputFieldsDeclarer declarer) { + System.out.println("定义格式..."); + declarer.declare(new Fields(field)); + } + + /** + * 当一个Tuple处理成功时,会调用这个方法 + */ + @Override + public void ack(Object obj) { + System.out.println("ack:"+obj); + } + + /** + * 当Topology停止时,会调用这个方法 + */ + @Override + public void close() { + System.out.println("关闭..."); + } + + /** + * 当一个Tuple处理失败时,会调用这个方法 + */ + @Override + public void fail(Object obj) { + System.out.println("失败:"+obj); + } + +} diff --git a/src/main/java/com/pancm/bigdata/storm/test2/package-info.java b/src/main/java/com/pancm/bigdata/storm/test2/package-info.java new file mode 100644 index 0000000..d0724fd --- /dev/null +++ b/src/main/java/com/pancm/bigdata/storm/test2/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: + * Version:1.0.0 + * @author pancm + * @date 2018年3月15日 + */ +package com.pancm.bigdata.storm.test2; \ No newline at end of file diff --git a/src/main/java/com/pancm/bigdata/zookeeper/ZookeeperTest.java b/src/main/java/com/pancm/bigdata/zookeeper/ZookeeperTest.java new file mode 100644 index 0000000..faec418 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/zookeeper/ZookeeperTest.java @@ -0,0 +1,84 @@ +package com.pancm.bigdata.zookeeper; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; + +/** + * +* @Title: ZookeeperTest +* @Description: +* zookeeper测试 +* @Version:1.0.0 +* @author pancm +* @date 2018年4月28日 + */ +public class ZookeeperTest { + private static String url="master:2181"; + private static ZooKeeper zk; + private static int CONNECTION_TIMEOUT=30000; + + public static void main(String[] args) throws Exception { + // 创建一个与服务器的连接 + zk = new ZooKeeper(url , + CONNECTION_TIMEOUT, new Watcher() { + // 监控所有被触发的事件 + public void process(WatchedEvent event) { + System.out.println(event.getPath()+"已经触发了" + event.getType() + "事件!"); + } + }); + + /* + * 创建一个给定的目录节点 path, 并给它设置数据, + * CreateMode 标识有四种形式的目录节点,分别是 + * PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失; + * PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1, + * 然后返回给客户端已经成功创建的目录节点名; + * EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session 超时,这种节点会被自动删除; + * EPHEMERAL_SEQUENTIAL:临时自动编号节点 + */ + + // 创建一个父级目录节点 + if(zk.exists("/test", true)==null){ + //参数说明:目录,参数,参数权限,节点类型 + zk.create("/test", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + if(zk.exists("/test/test1", true)==null){ + // 创建一个子目录节点 + zk.create("/test/test1", "data2".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); + } + + System.out.println("="+new String(zk.getData("/test",false,null))); + // 取出子目录节点列表 + System.out.println("=="+zk.getChildren("/test",true)); + + if(zk.exists("/test/test1", true)!=null){ + // 修改子目录节点数据 + zk.setData("/test/test1","testOne".getBytes(),-1); + } + System.out.println("目录节点状态:["+zk.exists("/test",true)+"]"); + if(zk.exists("/test/test1", true)!=null){ + // 创建另外一个子目录节点 + zk.create("/test/test2", "test2".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); + } + System.out.println("==="+new String(zk.getData("/test/test2",true,null))); + + /* + * 删除 path 对应的目录节点,version 为 -1 可以匹配任何版本,也就删除了这个目录节点所有数据 + */ + // 删除子目录节点 + zk.delete("/test/test2",-1); + zk.delete("/test/test1",-1); + + // 删除父目录节点 + zk.delete("/test",-1); + // 关闭连接 + zk.close(); + } + + + + +} diff --git a/src/main/java/com/pancm/bigdata/zookeeper/package-info.java b/src/main/java/com/pancm/bigdata/zookeeper/package-info.java new file mode 100644 index 0000000..ef76320 --- /dev/null +++ b/src/main/java/com/pancm/bigdata/zookeeper/package-info.java @@ -0,0 +1,8 @@ +/** +* @Title: package-info +* @Description: zookeeper相关代码 +* @Version:1.0.0 +* @author pancm +* @date 2018年4月28日 +*/ +package com.pancm.bigdata.zookeeper; \ No newline at end of file diff --git a/src/main/java/com/pancm/commons/apache/CommonsTest.java b/src/main/java/com/pancm/commons/apache/CommonsTest.java new file mode 100644 index 0000000..be28faf --- /dev/null +++ b/src/main/java/com/pancm/commons/apache/CommonsTest.java @@ -0,0 +1,132 @@ +package com.pancm.commons.apache; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.collections.Bag; +import org.apache.commons.collections.BidiMap; +import org.apache.commons.collections.Factory; +import org.apache.commons.collections.HashBag; +import org.apache.commons.collections.bidimap.TreeBidiMap; +import org.apache.commons.collections.list.LazyList; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; + +/** + * +* Title: langTest +* Description: Apache commons工具包测试 +* Version:1.0.0 +* @author pancm +* @date 2017年10月30日 + */ +public class CommonsTest { + + public static void main(String[] args) { + stringTest(); + otherTest(); + } + + /** + * StringUtils 相关测试 + */ + private static void stringTest(){ + /* + * 空指针判断 + */ + String str=""; + String str2=null; + String str3=" "; + System.out.println(":"+StringUtils.isEmpty(str)); //:true + System.out.println("null:"+StringUtils.isEmpty(str2)); //null:true + System.out.println(" :"+StringUtils.isEmpty(str3)); // :false + + /* + * 判断是否为数字 + */ + String str4="123"; + String str5="12 3"; + String str6="123QD#"; + System.out.println("str4:"+StringUtils.isNumeric(str4));//str4:true + System.out.println("str5:"+StringUtils.isNumeric(str5));//str5:false + System.out.println("str6:"+StringUtils.isNumeric(str6));//str6:false + + /* + * 统计子字符串出现的次数 + */ + String str7="abcdefgfedccfg"; + String str8="ac"; + String str9="c"; + System.out.println("count:"+StringUtils.countMatches(str7, str8));//count:0 + System.out.println("count:"+StringUtils.countMatches(str7, str9));//count:3 + + } + + /** + * 其他的测试 + */ + private static void otherTest(){ + System.out.println("十位数字随机数:"+RandomStringUtils.randomNumeric(10)); //0534110685 + System.out.println("十位字母随机数:"+RandomStringUtils.randomAlphabetic(10)); //jLWiHdQhHg + System.out.println("十位ASCII随机数:"+RandomStringUtils.randomAscii(10)); //8&[bxy%h_- + String str="hello world,why are you so happy"; + System.out.println("首字符大写:"+WordUtils.capitalize(str)); //:Hello World,why Are You So Happy + } + + /** + * Bag 测试 + * 主要测试重复元素的统计 + */ + @SuppressWarnings("deprecation") + private static void bagTest(){ + //定义4种球 + Bag box=new HashBag(Arrays.asList("red","blue","black","green")); + System.out.println("box.getCount():"+box.getCount("red"));//box.getCount():1 + box.add("red", 5);//红色的球增加五个 + System.out.println("box.size():"+box.size()); //box.size():9 + System.out.println("box.getCount():"+box.getCount("red"));//box.getCount():6 + } + + /** + * Lazy测试 + * 需要该元素的时候,才会生成 + */ + @SuppressWarnings("unchecked") + private static void lazyTest(){ + List lazy=LazyList.decorate(new ArrayList<>(), new Factory() { + @Override + public Object create() { + return "Hello"; + } + }); + //访问了第三个元素,此时0和1位null + //get几就增加了几加一 , 输出依旧是 Hello + String str=lazy.get(2); + System.out.println("str:"+str);//str:Hello + //加入的第四个元素 + lazy.add("world"); + //元素总个为4个 + System.out.println("lazy.size():"+lazy.size());//lazy.size():4 + } + + /** + * 双向Map + * 唯一的key和map,可以通过键或值来操作 + * 比如删除、查询等 + */ + private static void bidimapTest(){ + BidiMap map=new TreeBidiMap(); + map.put(1, "a"); + map.put(2, "b"); + map.put(3, "c"); + System.out.println("map:"+map); //map:{1=a, 2=b, 3=c} + System.out.println("map.get():"+map.get(2)); //map.get():b + System.out.println("map.getKey():"+map.getKey("a")); //map.getKey():1 + System.out.println("map.removeValue():"+map.removeValue("c")); //map.removeValue():3 + System.out.println("map:"+map); //map:{1=a, 2=b} + } + + +} diff --git a/src/main/java/com/pancm/commons/google/GuavaTest.java b/src/main/java/com/pancm/commons/google/GuavaTest.java new file mode 100644 index 0000000..b1c849a --- /dev/null +++ b/src/main/java/com/pancm/commons/google/GuavaTest.java @@ -0,0 +1,154 @@ +package com.pancm.commons.google; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.base.Joiner; +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Table; +import com.google.common.primitives.Ints; + +/** + * +* Title: guavaTest +* Description:谷歌 guava 工具包测试 +* Version:1.0.0 +* @author pancm +* @date 2017年10月30日 + */ +public class GuavaTest { + + public static void main(String[] args) { + noChangeList(); + one2MoreMap(); + more2One(); + filtedMap(); + joiner(); + splitter(); + integer(); + } + + /** + * 不可变集合测试 + */ + private static void noChangeList(){ + ImmutableList list=ImmutableList.of("A","B","C"); + ImmutableMap map=ImmutableMap.of(1,"壹",2,"贰",3,"叁"); + System.out.println("ImmutableList:"+list); //ImmutableList:[A, B, C] + System.out.println("ImmutableMap:"+map); //ImmutableMap:{1=壹, 2=贰, 3=叁} + + //下面运行直接报错,因为这是不可变集合 +// list.add("D"); +// map.put(4, "肆"); + } + + /** + * map中多个键的测试 + * 例如:一个人多个电话 + */ + private static void one2MoreMap(){ + Multimap map= ArrayListMultimap.create(); + map.put("路人甲", "123"); + map.put("路人甲", "234"); + map.put("路人乙", "567"); + map.put("路人乙", "890"); + System.out.println("Multimap:"+map); //Multimap:{路人乙=[567, 890], 路人甲=[123, 234]} + System.out.println("get:"+map.get("路人乙")); //get:[567, 890] + } + + /** + * 多个键值对一个值 + * 例如:坐标 + */ + private static void more2One(){ + Table table=HashBasedTable.create(); + table.put(22.54, 114.01, "深圳"); + table.put(39.96, 116.40, "北京"); + System.out.println("Table:"+table); //Table:{22.54={114.01=深圳}, 39.96={116.4=北京}} + System.out.println("Table.get:"+table.get(22.54, 114.01));//Table.get:深圳 + } + + /** + * Map的过滤 + * 例如:查找该集合中大于20岁的人 + */ + private static void filtedMap(){ + Map map=new HashMap(); + map.put("张三", 19); + map.put("李四", 20); + map.put("王五", 21); + Map filtedmap =Maps.filterValues(map, + new Predicate(){ + @Override + public boolean apply(Integer age) { + return age>20; + } + }); + System.out.println("Map:"+map); //Map:{张三=19, 李四=20, 王五=21} + System.out.println("filtedmap:"+filtedmap);//filtedmap:{王五=21} + } + + /** + * Joiner连接测试 + * 不局限于连接String,如果是null,会直接跳过 + * + */ + private static void joiner(){ + //设置连接符 + //如:设置为 "和",拼接 “你”,“我” 就变成了“你和我” + Joiner joiner=Joiner.on(","); + String str=joiner.skipNulls().join("你好","java"); + Map map=new HashMap(); + map.put("张三", "你好"); + map.put("李四", "嗨"); + //设置键值的连接符以及键与值之间的连接符 + String str1=Joiner.on(",").withKeyValueSeparator(":").join(map); + System.out.println("Joiner: "+str); //Joiner: 你好,java + System.out.println("Joiner: "+str1); //Joiner: 张三:你好,李四:嗨 + } + + /** + * Splitter拆分测试 + */ + private static void splitter(){ + String str="你好,java"; + //按字符分割 + for(String s:Splitter.on(",").split(str)){ + System.out.println("s:"+s); + } + //按固定长度分割 + for(String d:Splitter.fixedLength(2).split(str)){ + System.out.println("d:"+d); + } + } + + /** + * 基本类型测试 + */ + private static void integer(){ + int []ints={1,4,3,2}; + //找到里面的最大值 + System.out.println("max:"+Ints.max(ints)); //max:4 + + List list=new ArrayList(); + list.add(1); + list.add(3); + list.add(6); + //包装类型集合转变为基本类型集合 + int []arr=Ints.toArray(list); + for(int i=0,j=arr.length;i sy=new HashMap(); + sy.put("channel", "android"); + sy.put("deviceId", "12-ab"); + sy.put("device", "1456"); + sy.put("appVersion", "Version 1.5.1"); + sy.put("account", "This is test message"); + + sy.put("ss","client_bind"); + // logger.info("SentBody:"+sy); + // session.setAttribute("account","hello world"); + session.write(sy);// 发送消息 + } + public static void main(String[] args) { + for(int i=0;i<4000;i++){ + ClientTestServer client = new ClientTestServer(); + IoConnector connector = client.creatClient(); + IoSession session = client.getIOSession(connector); + client.sendMsg(session,Arrays.toString(new byte[1000])+":"+System.currentTimeMillis()); + } + + } + + } diff --git a/src/main/java/com/pancm/nio/mina/demo/MinaClient.java b/src/main/java/com/pancm/nio/mina/demo/MinaClient.java new file mode 100644 index 0000000..b8a8364 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo/MinaClient.java @@ -0,0 +1,107 @@ +package com.pancm.nio.mina.demo; + +import java.net.InetSocketAddress; +import java.util.HashMap; + +import org.apache.log4j.Logger; +import org.apache.mina.core.future.ConnectFuture; +import org.apache.mina.core.service.IoConnector; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory; +import org.apache.mina.transport.socket.nio.NioSocketConnector; + + +/** + * @author ZERO + * @version 2017-3-27 下午5:59:54 + * mina 客户端 + */ +public class MinaClient { + private static Logger logger = Logger.getLogger(MinaClient.class); + private static String HOST = "127.0.0.1"; + private static int PORT = 1255; + private static IoConnector connector=new NioSocketConnector(); + private static IoSession session; + public static IoConnector getConnector() { + return connector; + } + + public static void setConnector(IoConnector connector) { + MinaClient.connector = connector; + } + + /* + * 测试服务端与客户端程序! + a. 启动服务端,然后再启动客户端 + b. 服务端接收消息并处理成功; + */ + @SuppressWarnings("deprecation") + public static void main(String[] args) { + // 设置链接超时时间 + connector.setConnectTimeout(30000); + // 添加过滤器 可序列话的对象 + connector.getFilterChain().addLast( + "codec", + new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); + // 添加业务逻辑处理器类 + connector.setHandler(new MinaClientHandler()); + ConnectFuture future = connector.connect(new InetSocketAddress( + HOST, PORT));// 创建连接 + future.awaitUninterruptibly();// 等待连接创建完成 + session = future.getSession();// 获得session + + bindstart(); + pushstart(); + } + + public static void bindstart(){ + logger.info("客户端绑定服务端"); + // 创建一个非阻塞的客户端程序 + + try { + + HashMap sy=new HashMap(); + sy.put("channel", "xiaoai"); + sy.put("deviceId", "12-ab"); + sy.put("device", "1456"); + sy.put("appVersion", "Version 1.5.1"); + sy.put("account", "0030000100009702"); + + sy.put("client_bind","client_bind"); + logger.info("SentBody:"+sy); + // session.setAttribute("account","hello world"); + session.write(sy);// 发送消息 + logger.info("终端与服务端建立连接成功...发送的消息为:"+sy); + } catch (Exception e) { + e.printStackTrace(); + logger.error("客户端链接异常...", e); + } +// session.getCloseFuture().awaitUninterruptibly();// 等待连接断开 +// connector.dispose(); + } + + public static void pushstart(){ + logger.info("客户端请求服务端推送"); + // 创建一个非阻塞的客户端程序 + // IoConnector connector = new NioSocketConnector(); + // 设置链接超时时间 + try { + HashMap sy=new HashMap(); + sy.put("closeTV", "closeTV"); + sy.put("account", "0030000100009702"); //账号 + + sy.put("put","client_online"); + logger.info("SentBody:"+sy.toString()); + // session.setAttribute("account","hello world"); + session.write(sy);// 发送消息 + logger.info("客户端与服务端建立连接成功...发送的消息为:"+sy); + } catch (Exception e) { + e.printStackTrace(); + logger.error("客户端链接异常...", e); + } +// session.getCloseFuture().awaitUninterruptibly();// 等待连接断开 +// connector.dispose(); + } + +} diff --git a/src/main/java/com/pancm/nio/mina/demo/MinaClientHandler.java b/src/main/java/com/pancm/nio/mina/demo/MinaClientHandler.java new file mode 100644 index 0000000..d687bbd --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo/MinaClientHandler.java @@ -0,0 +1,33 @@ +package com.pancm.nio.mina.demo; + +import org.apache.log4j.Logger; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IoSession; + + +/** + * @author ZERO + * @version 2017-3-27 ??6:01:31 + * 客户端handle + */ +public class MinaClientHandler extends IoHandlerAdapter { + private static Logger logger = Logger.getLogger(MinaClientHandler.class); + + @Override + public void messageReceived(IoSession session, Object message) + throws Exception { + String msg = message.toString(); + logger.info("客户端接收的数据:" + msg); + // if(msg.equals(XiaoaiConstant.CMD_HEARTBEAT_REQUEST)){ + logger.warn("成功收到心跳包"); + // session.write(XiaoaiConstant.CMD_HEARTBEAT_RESPONSE); + // } + + } + + @Override + public void exceptionCaught(IoSession session, Throwable cause) + throws Exception { + logger.error("发生错误...", cause); + } +} diff --git a/src/main/java/com/pancm/nio/mina/demo/MinaServer.java b/src/main/java/com/pancm/nio/mina/demo/MinaServer.java new file mode 100644 index 0000000..0261dfa --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo/MinaServer.java @@ -0,0 +1,58 @@ +package com.pancm.nio.mina.demo; + +import java.net.InetSocketAddress; + +import org.apache.log4j.Logger; +import org.apache.mina.core.service.IoAcceptor; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; + + +/** + * @author ZERO: + * @version 2017-3-27 下午3:20:11 + * 创建服务端 + */ +public class MinaServer { + //记录日志 + public static Logger logger=Logger.getLogger(MinaServer.class); + private static int PORT=3333; + + /* 启动此类 提示服务端运行成功后 + * Windows 命令 输入 telnet 127.0.0.1 3305 + * 然后输入消息 message + * 消息为bye的时候关闭连接 + * */ + public static void main(String[] args) { + IoAcceptor ia=null; + try{ + //创建一个非堵塞的server端Socket + ia=new NioSocketAcceptor(); + //创建 协议编码解码过滤器ProtocolCodecFilter +// ProtocolCodecFilter pf=new ProtocolCodecFilter(new TextLineCodecFactory(Charset +// .forName("UTF-8"), +// LineDelimiter.WINDOWS.getValue(), +// LineDelimiter.WINDOWS.getValue())); + //设置端口 + InetSocketAddress pt=new InetSocketAddress(PORT); + // 设置过滤器(使用Mina提供的文本换行符编解码器) + ia.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); + //设置读取数据的缓存区大小 + ia.getSessionConfig().setReadBufferSize(2048); + //读写通道10秒内无操作进入空闲状态 + ia.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); + //绑定逻辑处理器 + ia.setHandler(new MinaServerHandler()); + //绑定端口 + ia.bind(pt); + logger.info("服务端启动成功...端口号为:"+PORT); + + }catch(Exception e){ + logger.error("服务器的异常..."+e); + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/pancm/nio/mina/demo/MinaServerHandler.java b/src/main/java/com/pancm/nio/mina/demo/MinaServerHandler.java new file mode 100644 index 0000000..4f8919e --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo/MinaServerHandler.java @@ -0,0 +1,67 @@ +package com.pancm.nio.mina.demo; + +import java.net.InetSocketAddress; +import java.util.Date; + +import org.apache.log4j.Logger; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; + + +/** + * @author ZERO: + * @version 2017-3-27 下午3:59:33 + * 业务逻辑实现 + */ +public class MinaServerHandler extends IoHandlerAdapter { + public static Logger logger=Logger.getLogger(MinaServerHandler.class); + + @Override + public void sessionCreated(IoSession iosession) throws Exception{ + InetSocketAddress sa=(InetSocketAddress)iosession.getRemoteAddress(); + String address=sa.getAddress().getHostAddress(); //访问的ip + logger.info("服务端与客户端创建连接..."+"访问的IP:"+address); + } + + + @Override + public void sessionOpened(IoSession iosession) throws Exception{ + logger.info("服务端与客户端连接打开..."); + } + + @Override + public void messageReceived(IoSession session,Object message) throws Exception{ + String msg=message.toString(); + logger.info("服务端收到的数据为:"+msg); + if("bye".equals(msg)){ //服务端断开的条件 + session.close(); + } + Date date=new Date(); + session.write(date); //返回给服务端数据 + } + + @Override + public void messageSent(IoSession session, Object message) throws Exception { +// session.close(); //发送成功后主动断开与客户端的连接 + logger.info("服务端发送信息成功..."); + } + + @Override + public void sessionClosed(IoSession session) throws Exception { + } + + @Override + public void sessionIdle(IoSession session, IdleStatus status) + throws Exception { + logger.info("服务端进入空闲状态..."); + } + + @Override + public void exceptionCaught(IoSession session, Throwable cause) + throws Exception { + logger.error("服务端发送异常...", cause); + } + + +} diff --git a/src/main/java/com/pancm/nio/mina/demo1/MinaClient.java b/src/main/java/com/pancm/nio/mina/demo1/MinaClient.java new file mode 100644 index 0000000..10ac551 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo1/MinaClient.java @@ -0,0 +1,54 @@ +package com.pancm.nio.mina.demo1; + +import java.net.InetSocketAddress; +import java.nio.charset.Charset; + +import org.apache.log4j.Logger; +import org.apache.mina.core.future.ConnectFuture; +import org.apache.mina.core.service.IoConnector; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.transport.socket.nio.NioSocketConnector; + +/** + * @author ZERO + * @version 2017-3-27 下午5:59:54 + * mina 客户端 + */ +public class MinaClient { + private static Logger logger = Logger.getLogger(MinaClient.class); + private static String HOST = "127.0.0.1"; + private static int PORT = 3305; + + /* + * 测试服务端与客户端程序! + a. 启动服务端,然后再启动客户端(客户端发送的消息是"why are you so diao ") + b. 服务端接收消息并处理成功; + */ + public static void main(String[] args) { + // 创建一个非阻塞的客户端程序 + IoConnector connector = new NioSocketConnector(); + // 设置链接超时时间 + connector.setConnectTimeout(30000); + ProtocolCodecFilter pf=new ProtocolCodecFilter((new MyTextLineCodecFactory(Charset .forName("utf-8"), "\r\n"))); + // 添加过滤器 + connector.getFilterChain().addLast("codec", pf); + // 添加业务逻辑处理器类 + connector.setHandler(new MinaClientHandler()); + IoSession session = null; + try { + ConnectFuture future = connector.connect(new InetSocketAddress( + HOST, PORT));// 创建连接 + future.awaitUninterruptibly();// 等待连接创建完成 + session = future.getSession();// 获得session + String msg="hello \r\n"; + session.write(msg);// 发送消息 + logger.info("客户端与服务端建立连接成功...发送的消息为:"+msg); + } catch (Exception e) { + e.printStackTrace(); + logger.error("客户端链接异常...", e); + } + session.getCloseFuture().awaitUninterruptibly();// 等待连接断开 + connector.dispose(); + } +} diff --git a/src/main/java/com/pancm/nio/mina/demo1/MinaClientHandler.java b/src/main/java/com/pancm/nio/mina/demo1/MinaClientHandler.java new file mode 100644 index 0000000..cab6cb3 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo1/MinaClientHandler.java @@ -0,0 +1,27 @@ +package com.pancm.nio.mina.demo1; + +import org.apache.log4j.Logger; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IoSession; + +/** + * @author ZERO + * @version 2017-3-27 下午6:01:31 + * 业务逻辑过滤器代码 + */ +public class MinaClientHandler extends IoHandlerAdapter { + private static Logger logger = Logger.getLogger(MinaClientHandler.class); + + @Override + public void messageReceived(IoSession session, Object message) + throws Exception { + String msg = message.toString(); + logger.info("客户端接收到的信息为:" + msg); + } + + @Override + public void exceptionCaught(IoSession session, Throwable cause) + throws Exception { + logger.error("客户端发生异常...", cause); + } +} diff --git a/src/main/java/com/pancm/nio/mina/demo1/MinaServer.java b/src/main/java/com/pancm/nio/mina/demo1/MinaServer.java new file mode 100644 index 0000000..79a6da5 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo1/MinaServer.java @@ -0,0 +1,64 @@ +package com.pancm.nio.mina.demo1; + +import java.net.InetSocketAddress; +import java.nio.charset.Charset; + +import org.apache.log4j.Logger; +import org.apache.mina.core.service.IoAcceptor; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; + + + + + + +/** + * @author ZERO: + * @version 2017-3-28 上午10:20:11 + * 创建服务端 + */ +public class MinaServer { + //记录日志 + public static Logger logger=Logger.getLogger(MinaServer.class); + private static int PORT=3305; + + /* 启动此类 提示服务端运行成功后 + * Windows 命令 输入 telnet 127.0.0.1 3305 + * 然后输入消息 message + * 消息为bye的时候关闭连接 + * */ + public static void main(String[] args) { + new MinaServer().start(); + } + + public void start() { + IoAcceptor ia=null; + try{ + //创建一个非堵塞的server端Socket + ia=new NioSocketAcceptor(); + //创建 自定义协议编码解码过滤器ProtocolCodecFilter + ProtocolCodecFilter pf=new ProtocolCodecFilter((new MyTextLineCodecFactory(Charset .forName("utf-8"), "\r\n"))); + //设置端口 + InetSocketAddress pt=new InetSocketAddress(PORT); + // 设置过滤器(使用Mina提供的文本换行符编解码器) + ia.getFilterChain().addLast("codec", pf); + //设置读取数据的缓存区大小 + ia.getSessionConfig().setReadBufferSize(2048); + //读写通道10秒内无操作进入空闲状态 + ia.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); + //绑定逻辑处理器 + ia.setHandler(new MinaServerHandler()); + //绑定端口 + ia.bind(pt); + logger.info("服务端启动成功...端口号为:"+PORT); + + }catch(Exception e){ + logger.error("服务器的异常..."+e); + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/com/pancm/nio/mina/demo1/MinaServerHandler.java b/src/main/java/com/pancm/nio/mina/demo1/MinaServerHandler.java new file mode 100644 index 0000000..b46cefd --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo1/MinaServerHandler.java @@ -0,0 +1,73 @@ +package com.pancm.nio.mina.demo1; + +import java.util.Date; + +import org.apache.log4j.Logger; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; + + +/** + * @author ZERO: + * @version 2017-3-27 下午3:59:33 + * 业务逻辑实现 + */ +public class MinaServerHandler extends IoHandlerAdapter { + public static Logger logger=Logger.getLogger(MinaServerHandler.class); + + @Override + public void sessionCreated(IoSession session) throws Exception{ + logger.info("服务端与客户端创建连接..."); + super.sessionCreated(session); + } + + + @Override + public void sessionOpened(IoSession session) throws Exception{ + logger.info("服务端与客户端连接打开..."); + super.sessionOpened(session); + } + + @Override + public void messageReceived(IoSession session,Object message) throws Exception{ + String msg=message.toString(); + logger.info("服务端收到的数据为:"+msg); + if("bye".equals(msg)){ //服务端断开的条件 + session.close(); + } + Date date=new Date(); + String mg="Come on:"+date; + logger.info("服务端返回给客户端的数据:"+mg); + session.write(mg); //返回给服务端数据 + } + + @Override + public void messageSent(IoSession session, Object message) throws Exception { + logger.info("服务端发送数据 = "+message); +// session.close(); //发送成功后主动断开与客户端的连接 实现短连接 + logger.info("服务端发送信息成功..."); + } + + @Override + public void sessionClosed(IoSession session) throws Exception { + logger.info("断开连接"); + super.sessionClosed(session); + } + + @Override + public void sessionIdle(IoSession session, IdleStatus status) + throws Exception { + logger.info("服务端进入空闲状态..."); + super.sessionIdle(session, status); + } + + @Override + public void exceptionCaught(IoSession session, Throwable cause) + throws Exception { + logger.error("服务端发送异常...", cause); + super.exceptionCaught(session, cause); + } + + +} diff --git a/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecDecoder.java b/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecDecoder.java new file mode 100644 index 0000000..407eec7 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecDecoder.java @@ -0,0 +1,206 @@ +package com.pancm.nio.mina.demo1; + +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; + +import org.apache.log4j.Logger; +import org.apache.mina.core.buffer.IoBuffer; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; + +/** + * @author ZERO + * @version 2017-3-28 上午10:44:55 + * 设置编码解码器 + */ + +public class MyTextLineCodecDecoder implements ProtocolDecoder { + private static Logger logger = Logger.getLogger(MyTextLineCodecDecoder.class); + + private Charset charset; // 编码格式 + + private String delimiter; // 文本分隔符 + private IoBuffer delimBuf; // 文本分割符匹配的变量 + + // 定义常量值,作为每个IoSession中保存解码任务的key值 + private static String CONTEXT = MyTextLineCodecDecoder.class.getName() + ".context"; + + public MyTextLineCodecDecoder(Charset charset,String delimiter){ + this.charset=charset; + this.delimiter=delimiter; + } + + + @Override + public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) + throws Exception { + Context ctx = getContext(session); + if (delimiter == null || "".equals(delimiter)) { + // 如果文本换行符未指定,使用默认值 + delimiter = "\r\n"; + } + + if (charset == null) { + charset = Charset.forName("utf-8"); + } + decodeNormal(ctx, in, out); + + } + + private void decodeNormal(Context ctx, IoBuffer in, ProtocolDecoderOutput out) throws CharacterCodingException { + + // 取出未完成任务中已经匹配的文本换行符的个数 + int matchCount = ctx.getMatchCount(); + + // 设置匹配文本换行符的IoBuffer变量 + if (delimBuf == null) { + IoBuffer tmp = IoBuffer.allocate(2).setAutoExpand(true); + tmp.putString(delimiter, charset.newEncoder()); + tmp.flip(); + delimBuf = tmp; + } + + //解码的IoBuffer中数据的原始信息 + int oldPos = in.position(); //输出值为0 + int oldLimit = in.limit(); //输出值为1 + + logger.info("******************************************************************************"); + logger.info("开始进入解码方法-----------------------------------------------------------------"); + logger.info(""); + logger.info("init Start--------------------------------------------------------------------"); + logger.info("in.postion() = "+oldPos); + logger.info("in.Limit() = "+oldLimit); + logger.info("in.capacity() = "+in.capacity()); + logger.info("matchCount = "+matchCount); + logger.info("init End---------------------------------------------------------------------"); + logger.info(""); + + //变量解码的IoBuffer + while (in.hasRemaining()) { + + byte b = in.get(); + logger.info(""); + logger.info("输入进来的字符为 = "+(char)b+",对应的ascii值 = "+b); + logger.info("in.position() = "+in.position()+",in.limit() = "+in.limit()); + logger.info(""); + + //当b的ascii值为13,10 即为\r,\n时,会进入下述if语句 + if (delimBuf.get(matchCount) == b) { + + // b='\r'时,matchCount=1, b='\n'时,matchCount=2 + matchCount++; + + logger.info("matchCount = "+matchCount); + //当前匹配到字节个数与文本换行符字节个数相同,即 b='\n'时 + //此时matchCount=2, delimBuf.limit()=2 + + if (matchCount == delimBuf.limit()) { + + // 获得当前匹配到的position(position前所有数据有效) + int pos = in.position(); //值为2 + logger.info("pos = "+pos); + + in.limit(pos); //值为2 + // position回到原始位置 + in.position(oldPos); //值为0 + + // 追加到Context对象未完成数据后面 + ctx.append(in); //将 \r\n这两个字符添加到 ctx.getBuf()中 + + // in中匹配结束后剩余数据 + in.limit(oldLimit); //值为2 + in.position(pos); //值为2 + + IoBuffer buf = ctx.getBuf(); //此时是得到 he\r\n + buf.flip(); //此时 buf.position=0,buf.limit()=4 + + buf.limit(buf.limit() - matchCount); //4-2 = 2 + try{ + // 输出解码内容 ,即 he + out.write(buf.getString(ctx.getDecoder())); + } + finally { + buf.clear(); // 释放缓存空间 + } + + matchCount = 0; + + } + }else { //h字符,e字符时,均会进入 此else逻辑判断中 + + //把in中未解码内容放回buf中 + //下面会在 输入的字符不是 \r\n时会需要保存使用 + in.position(oldPos); + ctx.append(in); + ctx.setMatchCount(matchCount); + } + } + } + + // 从IoSession中获取Context对象 + private Context getContext(IoSession session) { + Context ctx; + ctx = (Context) session.getAttribute(CONTEXT); + + if (ctx == null) { + ctx = new Context(); + session.setAttribute(CONTEXT, ctx); + } + return ctx; + } + + public void dispose(IoSession arg0) throws Exception { + // TODO Auto-generated method stub + + } + + public void finishDecode(IoSession arg0, ProtocolDecoderOutput arg1) + throws Exception { + // TODO Auto-generated method stub + } + + + // 内部类,保存IoSession解码时未完成的任务 + private class Context { + + private CharsetDecoder decoder; + private IoBuffer buf; + // 保存真实解码内容 + private int matchCount = 0; // 匹配到的文本换行符个数 + private Context() { + decoder = charset.newDecoder(); + buf = IoBuffer.allocate(80).setAutoExpand(true); + } + + // 重置 + public void reset() { + matchCount = 0; + decoder.reset(); + } + + // 追加数据 + public void append(IoBuffer in) { + getBuf().put(in); + } + + public CharsetDecoder getDecoder() { + return decoder; + } + + public IoBuffer getBuf() { + return buf; + } + + public int getMatchCount() { + return matchCount; + } + + public void setMatchCount(int matchCount) { + this.matchCount = matchCount; + } + } + + +} diff --git a/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecEncoder.java b/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecEncoder.java new file mode 100644 index 0000000..c4af748 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecEncoder.java @@ -0,0 +1,50 @@ +package com.pancm.nio.mina.demo1; + +import java.nio.charset.Charset; + +import org.apache.log4j.Logger; +import org.apache.mina.core.buffer.IoBuffer; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolEncoder; +import org.apache.mina.filter.codec.ProtocolEncoderOutput; + +/** + * @author ZERO + * @version 2017-3-28 上午11:30:55 + * 设置编码构造器 + */ +public class MyTextLineCodecEncoder implements ProtocolEncoder{ + + private static Logger logger = Logger.getLogger(MyTextLineCodecEncoder.class); + + private Charset charset; // 编码格式 + private String delimiter; // 文本分隔符 + public MyTextLineCodecEncoder(Charset charset, String delimiter) { + this.charset = charset; + this.delimiter = delimiter; + } + + public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception { + logger.info("开始进入编码方法-----------------------------------------------------------------"); + // 如果文本换行符未指定,使用默认值 + if (delimiter == null || "".equals(delimiter)) { + delimiter = "\r\n"; + } + + if (charset == null) { + charset = Charset.forName("utf-8"); + } + + String value = message.toString(); + IoBuffer buf = IoBuffer.allocate(value.length()).setAutoExpand(true); + //真实数据 + buf.putString(value, charset.newEncoder()); + //文本换行符 + buf.putString(delimiter, charset.newEncoder()); + buf.flip(); + out.write(buf); + } + + public void dispose(IoSession session) throws Exception {} + +} diff --git a/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecFactory.java b/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecFactory.java new file mode 100644 index 0000000..8a5b070 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/demo1/MyTextLineCodecFactory.java @@ -0,0 +1,34 @@ +package com.pancm.nio.mina.demo1; + +import java.nio.charset.Charset; + +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFactory; +import org.apache.mina.filter.codec.ProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolEncoder; + +/** + * @author ZERO + * @version 2017-3-28 上午11:39:50 + * 编码格式工厂 + */ +public class MyTextLineCodecFactory implements ProtocolCodecFactory { + + private Charset charset; // 编码格式 + private String delimiter; // 文本分隔符 + public MyTextLineCodecFactory(Charset charset, String delimiter) { + this.charset = charset; + this.delimiter = delimiter; + } + + + public ProtocolDecoder getDecoder(IoSession session) throws Exception { + return new MyTextLineCodecDecoder(charset, delimiter); + } + + + public ProtocolEncoder getEncoder(IoSession session) throws Exception { + return new MyTextLineCodecEncoder(charset, delimiter); + } + +} diff --git a/src/main/java/com/pancm/nio/mina/package-info.java b/src/main/java/com/pancm/nio/mina/package-info.java new file mode 100644 index 0000000..29b2406 --- /dev/null +++ b/src/main/java/com/pancm/nio/mina/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: Mina测试 + * Version:1.0.0 + * @author pancm + * @date 2017-3-27 + */ +package com.pancm.nio.mina; \ No newline at end of file diff --git a/src/main/java/com/pancm/nio/netty/demo/NettyClient.java b/src/main/java/com/pancm/nio/netty/demo/NettyClient.java new file mode 100644 index 0000000..ea4c753 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo/NettyClient.java @@ -0,0 +1,49 @@ +package com.pancm.nio.netty.demo; + + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; + +import java.io.IOException; +/** + * +* Title: NettyClient +* Description: Netty客户端 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ +public class NettyClient { + + public static String host = "127.0.0.1"; //ip地址 + public static int port = 6789; //端口 + /// 通过nio方式来接收连接和处理连接 + private static EventLoopGroup group = new NioEventLoopGroup(); + private static Bootstrap b = new Bootstrap(); + private static Channel ch; + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 + **/ + public static void main(String[] args) throws InterruptedException, IOException { + System.out.println("客户端成功启动..."); + b.group(group); + b.channel(NioSocketChannel.class); + b.handler(new NettyClientFilter()); + // 连接服务端 + ch = b.connect(host, port).sync().channel(); + star(); + } + + public static void star() throws IOException{ + String str="Hello Netty"; + ch.writeAndFlush(str); +// ch.writeAndFlush(str+ "\r\n"); + System.out.println("客户端发送数据:"+str); + } + +} \ No newline at end of file diff --git a/src/main/java/com/pancm/nio/netty/demo/NettyClientFilter.java b/src/main/java/com/pancm/nio/netty/demo/NettyClientFilter.java new file mode 100644 index 0000000..119a820 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo/NettyClientFilter.java @@ -0,0 +1,32 @@ +package com.pancm.nio.netty.demo; + + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.codec.Delimiters; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +/** + * +* Title: NettyClientFilter +* Description: Netty客户端 过滤器 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ +public class NettyClientFilter extends ChannelInitializer { + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline ph = ch.pipeline(); + /* + * 解码和编码,应和服务端一致 + * */ +// ph.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); + ph.addLast("decoder", new StringDecoder()); + ph.addLast("encoder", new StringEncoder()); + ph.addLast("handler", new NettyClientHandler()); //客户端的逻辑 + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo/NettyClientHandler.java b/src/main/java/com/pancm/nio/netty/demo/NettyClientHandler.java new file mode 100644 index 0000000..c45cb15 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo/NettyClientHandler.java @@ -0,0 +1,33 @@ +package com.pancm.nio.netty.demo; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +/** + * +* Title: NettyClientHandler +* Description: 客户端业务逻辑实现 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ +public class NettyClientHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { + System.out.println("客户端接受的消息: " + msg); + } + + // + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + System.out.println("正在连接... "); + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + System.out.println("连接关闭! "); + super.channelInactive(ctx); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo/NettyServer.java b/src/main/java/com/pancm/nio/netty/demo/NettyServer.java new file mode 100644 index 0000000..163b77c --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo/NettyServer.java @@ -0,0 +1,40 @@ +package com.pancm.nio.netty.demo; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +/** + * +* Title: NettyServer +* Description: Netty服务端 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ +public class NettyServer { + private static final int port = 6789; //设置服务端端口 + private static EventLoopGroup group = new NioEventLoopGroup(); // 通过nio方式来接收连接和处理连接 + private static ServerBootstrap b = new ServerBootstrap(); + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 + **/ + public static void main(String[] args) throws InterruptedException { + try { + b.group(group); + b.channel(NioServerSocketChannel.class); + b.childHandler(new NettyServerFilter()); //设置过滤器 + // 异步地绑定服务器;调用 sync()方法阻塞 等待直到绑定完成 + ChannelFuture f = b.bind(port).sync(); + System.out.println("服务端启动成功,端口是:"+port); + // 获取 Channel 的 CloseFuture,并且阻塞当前线程直到它完成 + f.channel().closeFuture().sync(); + } finally { + group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程 + } + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo/NettyServerFilter.java b/src/main/java/com/pancm/nio/netty/demo/NettyServerFilter.java new file mode 100644 index 0000000..456f60b --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo/NettyServerFilter.java @@ -0,0 +1,32 @@ +package com.pancm.nio.netty.demo; + + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.codec.Delimiters; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; + +/** + * + * Title: HelloServerInitializer + * Description: Netty 服务端过滤器 + * Version:1.0.0 + * @author Administrator + * @date 2017-8-31 + */ +public class NettyServerFilter extends ChannelInitializer { + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline ph = ch.pipeline(); + // 以("\n")为结尾分割的 解码器 +// ph.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); + // 解码和编码,应和客户端一致 + ph.addLast("decoder", new StringDecoder()); + ph.addLast("encoder", new StringEncoder()); + ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑 + } + } diff --git a/src/main/java/com/pancm/nio/netty/demo/NettyServerHandler.java b/src/main/java/com/pancm/nio/netty/demo/NettyServerHandler.java new file mode 100644 index 0000000..7f6ff8b --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo/NettyServerHandler.java @@ -0,0 +1,44 @@ +package com.pancm.nio.netty.demo; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import java.net.InetAddress; +import java.util.Date; + + +/** + * +* Title: HelloServerHandler +* Description: 服务端业务逻辑 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ +public class NettyServerHandler extends SimpleChannelInboundHandler { + /* + * 收到消息时,返回信息 + */ + @Override + protected void channelRead0(ChannelHandlerContext ctx, String msg) + throws Exception { + // 收到消息直接打印输出 + System.out.println("服务端接受的消息 : " + msg); + if("quit".equals(msg)){//服务端断开的条件 + ctx.close(); + } + // 返回客户端消息 +// ctx.writeAndFlush("收到消息:"+ msg+",当前的时间是:"+MyTools.getNowTime("")+"\n"); + ctx.writeAndFlush("收到消息:"+ msg+",当前的时间是:"+new Date()); + } + + /* + * 建立连接时,返回消息 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress()); + ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! "); + super.channelActive(ctx); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo1/BaseClient1Handler.java b/src/main/java/com/pancm/nio/netty/demo1/BaseClient1Handler.java new file mode 100644 index 0000000..c0542f5 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo1/BaseClient1Handler.java @@ -0,0 +1,25 @@ +package com.pancm.nio.netty.demo1; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * +* Title: BaseClient1Handler +* Description: 客户端自定义解码器其一 +* Version:1.0.0 +* @author panchengming +* @date 2017年9月17日 + */ +public class BaseClient1Handler extends ChannelInboundHandlerAdapter{ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + System.out.println("BaseClient1Handler channelActive"); +// ctx.fireChannelActive(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + System.out.println("BaseClient1Handler channelInactive"); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo1/BaseClient2Handler.java b/src/main/java/com/pancm/nio/netty/demo1/BaseClient2Handler.java new file mode 100644 index 0000000..af3cc18 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo1/BaseClient2Handler.java @@ -0,0 +1,20 @@ +package com.pancm.nio.netty.demo1; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * +* Title: BaseClient2Handler +* Description: 客户端自定义解码器其二 +* Version:1.0.0 +* @author panchengming +* @date 2017年9月17日 + */ +public class BaseClient2Handler extends ChannelInboundHandlerAdapter{ + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + System.out.println("BaseClient2Handler Active"); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo1/NettyClient.java b/src/main/java/com/pancm/nio/netty/demo1/NettyClient.java new file mode 100644 index 0000000..c38ea50 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo1/NettyClient.java @@ -0,0 +1,75 @@ +package com.pancm.nio.netty.demo1; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; + + +/** + * +* Title: NettyClient +* Description:Netty客户端 测试 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ +public class NettyClient { + private static final int port = 1234; + private static final String host = "127.0.0.1"; + private static EventLoopGroup group = new NioEventLoopGroup(); // 通过nio方式来接收连接和处理连接 + private static Bootstrap b = new Bootstrap(); + private static Channel ch ; + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 + **/ + public static void main(String[] args) throws Exception { + try{ + b.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY, true) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast("decoder", new StringDecoder()); + p.addLast("encoder", new StringEncoder()); + p.addLast(new BaseClient1Handler()); + p.addLast(new BaseClient2Handler()); + } + }); + + ChannelFuture future = b.connect(host, port).sync(); // 连接服务端 + System.out.println("客户端连接成功!"); + future.channel().writeAndFlush("Hello Netty Server ,I am a common client");//发送消息 + future.channel().closeFuture().sync(); //关闭 + } finally { + group.shutdownGracefully(); //释放资源 + } + + // start(); + } + + public static void start() throws Exception { + System.out.println("客户端像服务端发送数据"); + try { + String str="Hello Netty"; + ch.writeAndFlush(str+"\r\n");//发送消息 + System.out.println("客户端发送的消息:"+str); + ch.closeFuture().sync(); // 应用程序会一直等待,直到channel关闭 + } finally { + // group.shutdownGracefully().sync(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程 + } + } + + +} diff --git a/src/main/java/com/pancm/nio/netty/demo1/NettyClientHandler.java b/src/main/java/com/pancm/nio/netty/demo1/NettyClientHandler.java new file mode 100644 index 0000000..71561cd --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo1/NettyClientHandler.java @@ -0,0 +1,36 @@ +package com.pancm.nio.netty.demo1; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +/** + * +* Title: NettyClientHandler +* Description:客户端业务逻辑 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ +public class NettyClientHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext chc, String str) + throws Exception { + // TODO Auto-generated method stub + System.out.println("消息为:"+str); + + } + + @Override + public void channelActive(ChannelHandlerContext chc) throws Exception { + System.out.println("正在连接..."); + super.channelActive(chc); + } + + @Override + public void channelInactive(ChannelHandlerContext chc) throws Exception { + System.out.println("连接关闭"); + super.channelInactive(chc); + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo1/NettyServer.java b/src/main/java/com/pancm/nio/netty/demo1/NettyServer.java new file mode 100644 index 0000000..eef08e0 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo1/NettyServer.java @@ -0,0 +1,72 @@ +package com.pancm.nio.netty.demo1; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; + + + + + +/** + * +* Title: NettyServer +* Description: Netty服务端 测试自定义channelhandler +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ + +public class NettyServer { + private static final int port = 1234; + public static void main(String[] args) { + System.out.println("开始运行..."); + try { + start(); + } catch (InterruptedException e) { + System.out.println("运行异常..."); + e.printStackTrace(); + } + } + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 + **/ + public static void start() throws InterruptedException { + ServerBootstrap sb = new ServerBootstrap();// 引导辅助程序 + EventLoopGroup group = new NioEventLoopGroup();// 通过nio方式来接收连接和处理连接 + try { + sb.group(group); // 通过nio方式来接收连接和处理连接 + sb.channel(NioServerSocketChannel.class);// 设置nio类型的channel + sb.childHandler(new ChannelInitializer() {//有连接到达时会创建一个channel + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + + // 字符串解码 和 编码 + p.addLast("decoder", new StringDecoder()); + p.addLast("encoder", new StringEncoder()); + // 在channel队列中添加一个handler来处理业务 + p.addLast("handler", new NettyServerHandler()); + // 以("\n")为结尾分割的 解码器 + // cp.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); + } + }); + ChannelFuture f = sb.bind(port).sync();// 配置完成,开始绑定server,通过调用sync同步方法阻塞直到绑定成功 + System.out.println("服务端已启动... 端口是:"+port); + f.channel().closeFuture().sync();// 应用程序会一直等待,直到channel关闭 + } catch (Exception e) { + e.printStackTrace(); + } finally { + // group.shutdownGracefully().sync();//关闭EventLoopGroup,释放掉所有资源包括创建的线程 + } + + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo1/NettyServerHandler.java b/src/main/java/com/pancm/nio/netty/demo1/NettyServerHandler.java new file mode 100644 index 0000000..51cae69 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo1/NettyServerHandler.java @@ -0,0 +1,72 @@ +package com.pancm.nio.netty.demo1; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import java.net.InetAddress; + +/** + * +* Title: NettyServerHandler +* Description:Netty服务端业务逻辑实现 +* Version:1.0.0 +* @author Administrator +* @date 2017-8-31 + */ + + +public class NettyServerHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, String msg) + throws Exception { + // 收到消息直接打印输出 + System.out.println(ctx.channel().remoteAddress() + " Say : " + msg); + + // 返回客户端消息 - 我已经接收到了你的消息 + ctx.writeAndFlush("Received your message !\n"); + } + + /* + * + * 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候) + * + * channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + + System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress()); + ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! \n"); + super.channelActive(ctx); + } + +} + + + +/** + * Sharable表示此对象在channel间共享 + * handler类是我们的具体业务类 + * */ +/*@Sharable//注解@Sharable可以让它在channels间共享 +public class NettyServerHandler extends ChannelInboundHandlerAdapter { + public void channelRead(ChannelHandlerContext ctx, Object msg) { + System.out.println("服务端接受的数据为:" + msg); + if("quit".equals(msg)){ //服务端断开的条件 + ctx.close(); + } + Date date=new Date(); + ctx.write(date);//回写数据 + } + public void channelReadComplete(ChannelHandlerContext ctx) { + System.out.println("1111"); + ctx.writeAndFlush(Unpooled.EMPTY_BUFFER) //flush掉所有写回的数据 + .addListener(ChannelFutureListener.CLOSE); //当flush完成后关闭channel + } + public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) { + System.out.println("2222"); + cause.printStackTrace();//捕捉异常信息 + ctx.close();//出现异常时关闭channel + } +}*/ diff --git a/src/main/java/com/pancm/nio/netty/demo2/NettyClientDemo2.java b/src/main/java/com/pancm/nio/netty/demo2/NettyClientDemo2.java new file mode 100644 index 0000000..4053a1a --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo2/NettyClientDemo2.java @@ -0,0 +1,64 @@ +package com.pancm.nio.netty.demo2; + +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.string.StringDecoder; + +import java.io.IOException; + +/** + * +* Title: NettyClientDemo2 +* Description: Netty客户端 用于测试粘包、拆包 +* Version:1.0.0 +* @author pancm +* @date 2017年9月20日 + */ +public class NettyClientDemo2 { + public static String host = "127.0.0.1"; //ip地址 + public static int port = 2345; //端口 + /// 通过nio方式来接收连接和处理连接 + private static EventLoopGroup group = new NioEventLoopGroup(); + private static Bootstrap b = new Bootstrap(); + private static Channel ch; + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是ServerBootstrap。 + **/ + public static void main(String[] args) throws InterruptedException, IOException { + b.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY,true) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new StringDecoder()); //绑定解码器 + p.addLast(new NettyClientHandlerDemo2()); //绑定自定义业务 + } + }); + // 连接服务端 + ch = b.connect(host, port).sync().channel(); + System.out.println("客户端成功启动..."); + // star(); + } + + public static void star() throws IOException{ + String str="你好 Netty!"; + byte[] by=str.getBytes(); + ByteBuf message = Unpooled.buffer(by.length); ; + message.writeBytes(by); + ch.writeAndFlush(message); + System.out.println("客户端发送数据:"+str); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo2/NettyClientHandlerDemo2.java b/src/main/java/com/pancm/nio/netty/demo2/NettyClientHandlerDemo2.java new file mode 100644 index 0000000..c3e46c4 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo2/NettyClientHandlerDemo2.java @@ -0,0 +1,136 @@ +package com.pancm.nio.netty.demo2; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * +* Title: NettyClientHandlerDemo2 +* Description: Netty客户端业务逻辑处理 +* Version:1.0.0 +* @author pancm +* @date 2017年9月20日 + */ +public class NettyClientHandlerDemo2 extends ChannelInboundHandlerAdapter{ + + private byte[] req; + + private int counter; + + public NettyClientHandlerDemo2() { +// req = ("书到用时方恨少,事非经过不知难!").getBytes(); + + //用于测试字节解码器 LineBasedFrameDecoder(2048) + req = ("春江潮水连海平,海上明月共潮生。" + +" 滟滟随波千万里,何处春江无月明! " + +" 江流宛转绕芳甸,月照花林皆似霰;" + +" 空里流霜不觉飞,汀上白沙看不见。" + +" 江天一色无纤尘,皎皎空中孤月轮。" + +" 江畔何人初见月?江月何年初照人?" + +" 人生代代无穷已,江月年年望相似。" + +" 不知江月待何人,但见长江送流水。" + +" 白云一片去悠悠,青枫浦上不胜愁。" + +" 谁家今夜扁舟子?何处相思明月楼?" + +" 可怜楼上月徘徊,应照离人妆镜台。" + +" 玉户帘中卷不去,捣衣砧上拂还来。" + +" 此时相望不相闻,愿逐月华流照君。" + +" 鸿雁长飞光不度,鱼龙潜跃水成文。" + +" 昨夜闲潭梦落花,可怜春半不还家。" + +" 江水流春去欲尽,江潭落月复西斜。" + +" 斜月沉沉藏海雾,碣石潇湘无限路。" + +" 不知乘月几人归,落月摇情满江树。" + +" 噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。" + +" 西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。上有六龙回日之高标,下有冲波逆折之回川。" + +" 黄鹤之飞尚不得过,猿猱欲度愁攀援。青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。" + +" 问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。" + +" 蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒挂倚绝壁。飞湍瀑流争喧豗,砯崖转石万壑雷。" + +" 其险也如此,嗟尔远道之人胡为乎来哉!剑阁峥嵘而崔嵬,一夫当关,万夫莫开。" + +" 所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。" + +" 蜀道之难,难于上青天,侧身西望长咨嗟!"+System.getProperty("line.separator")).getBytes(); + + //用于测试 固定字符切分解码器 DelimiterBasedFrameDecoder(1024,Unpooled.copiedBuffer("~_~".getBytes()) + /* req = ("春江潮水连海平,海上明月共潮生。" + +" 滟滟随波千万里,何处春江无月明! " + +" 江流宛转绕芳甸,月照花林皆似霰;" + +" 空里流霜不觉飞,汀上白沙看不见。" + +" 江天一色无纤尘,皎皎空中孤月轮。" + +" 江畔何人初见月?江月何年初照人?" + +" 人生代代无穷已,江月年年望相似。~_~" + +" 不知江月待何人,但见长江送流水。" + +" 白云一片去悠悠,青枫浦上不胜愁。" + +" 谁家今夜扁舟子?何处相思明月楼?" + +" 可怜楼上月徘徊,应照离人妆镜台。" + +" 玉户帘中卷不去,捣衣砧上拂还来。" + +" 此时相望不相闻,愿逐月华流照君。~_~" + +" 鸿雁长飞光不度,鱼龙潜跃水成文。" + +" 昨夜闲潭梦落花,可怜春半不还家。" + +" 江水流春去欲尽,江潭落月复西斜。" + +" 斜月沉沉藏海雾,碣石潇湘无限路。" + +" 不知乘月几人归,落月摇情满江树。~_~" + +" 噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。" + +" 西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。~_~ 上有六龙回日之高标,下有冲波逆折之回川。" + +" 黄鹤之飞尚不得过,猿猱欲度愁攀援。~_~ 青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。" + +" 问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。" + +" 蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒挂倚绝壁。~_~ 飞湍瀑流争喧豗,砯崖转石万壑雷。" + +" 其险也如此,嗟尔远道之人胡为乎来哉!剑阁峥嵘而崔嵬,一夫当关,万夫莫开。" + +" 所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。" + +" 蜀道之难,难于上青天,侧身西望长咨嗟!"+System.getProperty("line.separator")).getBytes(); */ + + + + + /* req = ("AAAAAAAAAAAAAAAA" + +" BBBBBBBBBBBBBBBB " + +" CCCCCCCCCCCCCCCCC" + +" DDDDDDDDDDDDD" + +" EEEEEEEEEEEEEE" + +" FFFFFFFFFFFFFF" + +" GGGGGGGGGGGG ~_~" + +" HHHHHHHHHHHHHHHH" + +" IIIIIIIIIIIIII" + +" JJJJJJJJJJJJJJJJJJJ" + +" KKKKKKKKKKKKKKKKK" + +" LLLLLLLLLLLLLLLLL" + +" MMMMMMMMMMMMMMMMMM ~_~" + +" NNNNNNNNNNNNNNNN" + +" OOOOOOOOOOOOOOOOOOOO" + +" PPPPPPPPPPPPPP" + +" QQQQQQQQQQQQQQQQQQQQ" + +" RRRRRRRRRRRRRR ~_~" + +" SSSSSSSSSSSSSSSSS" + +" TTTTTT ~_~ TTTTTTT"+System.getProperty("line.separator")).getBytes(); */ + + //System.getProperty("line.separator") 结束标记 + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ByteBuf message = null; + //会发生粘包现象 +// for (int i = 0; i < 100; i++) { +// message = Unpooled.buffer(req.length); +// message.writeBytes(req); +// ctx.writeAndFlush(message); +// } + message = Unpooled.buffer(req.length); + message.writeBytes(req); + ctx.writeAndFlush(message); + System.out.println("一次发送消息过多,发送拆包现象! "); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) + throws Exception { + String buf = (String) msg; + System.out.println("现在 : " + buf + " ; 条数是 : "+ ++counter); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + ctx.close(); + } + + +} diff --git a/src/main/java/com/pancm/nio/netty/demo2/NettyServerDemo2.java b/src/main/java/com/pancm/nio/netty/demo2/NettyServerDemo2.java new file mode 100644 index 0000000..084c512 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo2/NettyServerDemo2.java @@ -0,0 +1,63 @@ +package com.pancm.nio.netty.demo2; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.codec.FixedLengthFrameDecoder; +import io.netty.handler.codec.LineBasedFrameDecoder; +import io.netty.handler.codec.string.StringDecoder; + +import java.net.InetSocketAddress; + +/** + * +* Title: NettyServerDemo2 +* Description: Netty服务端 用于测试粘包、拆包 +* Version:1.0.0 +* @author pancm +* @date 2017年9月20日 + */ +public class NettyServerDemo2 { + private final static int port=2345; + + public void start(){ + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)) + .childHandler(new ChannelInitializer() { + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + + p.addLast(new LineBasedFrameDecoder(2048)); //字节解码器 ,其中2048是规定一行数据最大的字节数。 用于解决拆包问题 +// p.addLast(new FixedLengthFrameDecoder(100)); //定长数据帧的解码器 ,每帧数据100个字节就切分一次。 用于解决粘包问题 +// p.addLast(new DelimiterBasedFrameDecoder(1024,Unpooled.copiedBuffer("~_~".getBytes()))); //固定字符切分解码器 ,会以"~_~"为分隔符。 注意此方法要放到StringDecoder()上面 + p.addLast(new StringDecoder()); //设置解码器 + p.addLast(new NettyServerHandlerDemo2()); //绑定自定义事物 + }; + + }).option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + // 绑定端口,开始接收进来的连接 + ChannelFuture future = sbs.bind(port).sync(); + + System.out.println("服务端启动成功,端口为 :" + port ); + future.channel().closeFuture().sync(); + } catch (Exception e) { + bossGroup.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程 + workerGroup.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程 + } + } + + public static void main(String[] args) throws Exception { + new NettyServerDemo2().start(); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo2/NettyServerHandlerDemo2.java b/src/main/java/com/pancm/nio/netty/demo2/NettyServerHandlerDemo2.java new file mode 100644 index 0000000..f25835d --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo2/NettyServerHandlerDemo2.java @@ -0,0 +1,33 @@ +package com.pancm.nio.netty.demo2; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * +* Title: NettyServerHandlerDemo2 +* Description: Netty服务端业务逻辑处理 用于测试粘包、拆包 +* Version:1.0.0 +* @author pancm +* @date 2017年9月20日 + */ +public class NettyServerHandlerDemo2 extends ChannelInboundHandlerAdapter{ + + + private int counter; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + + String body = (String)msg; + System.out.println("接受的数据是: " + body + ";条数是: " + ++counter); + } + + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyClientDemo3.java b/src/main/java/com/pancm/nio/netty/demo3/NettyClientDemo3.java new file mode 100644 index 0000000..90f9c6f --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyClientDemo3.java @@ -0,0 +1,59 @@ +package com.pancm.nio.netty.demo3; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; + +import java.io.IOException; + +/** + * +* Title: NettyClientDemo3 +* Description: Netty客户端 测试自定义解码器 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyClientDemo3 { + public static String host = "127.0.0.1"; //ip地址 + public static int port = 3456; //端口 + /// 通过nio方式来接收连接和处理连接 + private static EventLoopGroup group = new NioEventLoopGroup(); + private static Bootstrap b = new Bootstrap(); + private static Channel ch=null; + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是ServerBootstrap。 + **/ + public static void main(String[] args) throws InterruptedException, IOException { + b.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY,true) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new NettyEncoder()); //绑定自定义编码器 + p.addLast(new NettyClientHandlerDemo3()); //绑定自定义业务 + } + }); + // 连接服务端 + ch = b.connect(host, port).sync().channel(); + System.out.println("客户端成功启动..."); + star(); + } + + public static void star() throws IOException{ + String str="你好,Netty"; + NettyMsg customMsg = new NettyMsg((byte)0xAB, (byte)0xCD, str.length(), str); + ch.writeAndFlush(customMsg); + System.out.println("客户端发送数据:"+customMsg); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyClientHandlerDemo3.java b/src/main/java/com/pancm/nio/netty/demo3/NettyClientHandlerDemo3.java new file mode 100644 index 0000000..cd78f50 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyClientHandlerDemo3.java @@ -0,0 +1,24 @@ +package com.pancm.nio.netty.demo3; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * +* Description: +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyClientHandlerDemo3 extends ChannelInboundHandlerAdapter { + + /** + * 连接时发送消息 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + NettyMsg customMsg = new NettyMsg((byte)0xAB, (byte)0xCD, "Hello,Netty".length(), "Hello,Netty"); + ctx.writeAndFlush(customMsg); + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyDecoder.java b/src/main/java/com/pancm/nio/netty/demo3/NettyDecoder.java new file mode 100644 index 0000000..24b756a --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyDecoder.java @@ -0,0 +1,70 @@ +package com.pancm.nio.netty.demo3; + +import java.util.List; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageCodec; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; + +/** + * +* Description: Netty自定义解码器 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyDecoder extends ByteToMessageCodec { + + //判断传送客户端传送过来的数据是否按照协议传输,头部信息的大小应该是 byte+byte+int = 1+1+4 = 6 + private static final int HEADER_SIZE = 6; + + /** 类型 系统编号 0xAB 表示A系统,0xBC 表示B系统 */ + private byte type; + + /** 信息标志 0xAB 表示心跳包 0xBC 表示超时包 0xCD 业务信息包 */ + private byte flag; + + /** 主题信息的长度 */ + private int length; + + /** 主题信息 */ + private String body; + + + @Override + protected void encode(ChannelHandlerContext ctx, NettyMsg msg, ByteBuf out) + throws Exception { + System.out.println("msg:"+msg); + if (out == null) { + return ; + } + if (out.readableBytes() < HEADER_SIZE) { + throw new Exception("可读信息段比头部信息都小,你在逗我?"); + } + //注意在读的过程中,readIndex的指针也在移动 + type = out.readByte(); + + flag = out.readByte(); + + length = out.readInt(); + + if (out.readableBytes() < length) { + throw new Exception("body字段你告诉我长度是"+length+",但是真实情况是没有这么多,你又逗我?"); + } + ByteBuf buf = out.readBytes(length); + byte[] req = new byte[buf.readableBytes()]; + buf.readBytes(req); + body = new String(req, "UTF-8"); + NettyMsg customMsg = new NettyMsg(type,flag,length,body); + + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, + List out) throws Exception { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyDecoder2.java b/src/main/java/com/pancm/nio/netty/demo3/NettyDecoder2.java new file mode 100644 index 0000000..1a45801 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyDecoder2.java @@ -0,0 +1,74 @@ +package com.pancm.nio.netty.demo3; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; + +/** + * +* Description: Netty自定义解码器 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyDecoder2 extends LengthFieldBasedFrameDecoder { + + //判断传送客户端传送过来的数据是否按照协议传输,头部信息的大小应该是 byte+byte+int = 1+1+4 = 6 + private static final int HEADER_SIZE = 6; + + /** 类型 系统编号 0xAB 表示A系统,0xBC 表示B系统 */ + private byte type; + + /** 信息标志 0xAB 表示心跳包 0xBC 表示超时包 0xCD 业务信息包 */ + private byte flag; + + /** 主题信息的长度 */ + private int length; + + /** 主题信息 */ + private String body; + + /** + * + * @param maxFrameLength 解码时,处理每个帧数据的最大长度 + * @param lengthFieldOffset 该帧数据中,存放该帧数据的长度的数据的起始位置 + * @param lengthFieldLength 记录该帧数据长度的字段本身的长度 + * @param lengthAdjustment 修改帧数据长度字段中定义的值,可以为负数 + * @param initialBytesToStrip 解析的时候需要跳过的字节数 + * @param failFast 为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常 + */ + public NettyDecoder2(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, + int lengthAdjustment, int initialBytesToStrip, boolean failFast) { + super(maxFrameLength, lengthFieldOffset, lengthFieldLength, + lengthAdjustment, initialBytesToStrip, failFast); + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + if (in == null) { + return null; + } + if (in.readableBytes() < HEADER_SIZE) { + throw new Exception("可读信息段比头部信息都小,你在逗我?"); + } + + //注意在读的过程中,readIndex的指针也在移动 + type = in.readByte(); + + flag = in.readByte(); + + length = in.readInt(); + + if (in.readableBytes() < length) { + throw new Exception("body字段你告诉我长度是"+length+",但是真实情况是没有这么多,你又逗我?"); + } + ByteBuf buf = in.readBytes(length); + byte[] req = new byte[buf.readableBytes()]; + buf.readBytes(req); + body = new String(req, "UTF-8"); + + NettyMsg customMsg = new NettyMsg(type,flag,length,body); + return customMsg; + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyEncoder.java b/src/main/java/com/pancm/nio/netty/demo3/NettyEncoder.java new file mode 100644 index 0000000..921223a --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyEncoder.java @@ -0,0 +1,33 @@ +package com.pancm.nio.netty.demo3; + +import java.nio.charset.Charset; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +/** + * +* Description:Netty自定义编码器 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, NettyMsg msg, ByteBuf out) throws Exception { + if(null == msg){ + throw new Exception("消息不能为空!"); + } + + String body = msg.getBody(); + byte[] bodyBytes = body.getBytes(Charset.forName("utf-8")); + out.writeByte(msg.getType()); + out.writeByte(msg.getFlag()); + out.writeInt(bodyBytes.length); + out.writeBytes(bodyBytes); + + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyMsg.java b/src/main/java/com/pancm/nio/netty/demo3/NettyMsg.java new file mode 100644 index 0000000..616b66f --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyMsg.java @@ -0,0 +1,84 @@ +package com.pancm.nio.netty.demo3; + +/** + * +* Description: Netty 自定义消息 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyMsg { + + + /** 类型 系统编号 0xAB 表示A系统,0xBC 表示B系统 */ + private byte type; + + /** 信息标志 0xAB 表示心跳包 0xBC 表示超时包 0xCD 业务信息包 */ + private byte flag; + + /** 主题信息的长度 */ + private int length; + + /** 主题信息 */ + private String body; + + public NettyMsg() { + + } + + + /** + * + * @param type 类型 系统编号 0xAB 表示A系统,0xBC 表示B系统 + * @param flag 信息标志 0xAB 表示心跳包 0xBC 表示超时包 0xCD 业务信息包 + * @param length 主题信息的长度 + * @param body 主题信息 + */ + public NettyMsg(byte type, byte flag, int length, String body) { + this.type = type; + this.flag = flag; + this.length = length; + this.body = body; + } + + public byte getType() { + return type; + } + + public void setType(byte type) { + this.type = type; + } + + public byte getFlag() { + return flag; + } + + public void setFlag(byte flag) { + this.flag = flag; + } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + /** + * 重写toString方法,方便打印日志 + */ + @Override + public String toString() { + return "NettyMsg [type=" + type + ", flag=" + flag + ", length=" + + length + ", body=" + body + "]"; + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyServerDemo3.java b/src/main/java/com/pancm/nio/netty/demo3/NettyServerDemo3.java new file mode 100644 index 0000000..a4dfadd --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyServerDemo3.java @@ -0,0 +1,61 @@ +package com.pancm.nio.netty.demo3; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +import java.net.InetSocketAddress; + +/** + * +* Description: Netty 服务端 测试自定义解码器 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyServerDemo3 { + + private static final int MAX_FRAME_LENGTH = 1024 * 1024; + private static final int LENGTH_FIELD_LENGTH = 4; + private static final int LENGTH_FIELD_OFFSET = 2; + private static final int LENGTH_ADJUSTMENT = 0; + private static final int INITIAL_BYTES_TO_STRIP = 0; + + private final static int port=3456; + + + public void start(){ + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)) + .childHandler(new ChannelInitializer() { + + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new NettyDecoder2(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP,false)); +// ch.pipeline().addLast(new NettyDecoder(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP,false)); + ch.pipeline().addLast(new NettyServerHandlerDemo3()); + }; + + }).option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + // 绑定端口,开始接收进来的连接 + ChannelFuture future = sbs.bind(port).sync(); + + System.out.println("Netty服务端成功启动!端口为: " + port ); + future.channel().closeFuture().sync(); + } catch (Exception e) { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + new NettyServerDemo3().start(); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo3/NettyServerHandlerDemo3.java b/src/main/java/com/pancm/nio/netty/demo3/NettyServerHandlerDemo3.java new file mode 100644 index 0000000..2f06c1a --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo3/NettyServerHandlerDemo3.java @@ -0,0 +1,24 @@ +package com.pancm.nio.netty.demo3; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +/** + * +* Description:服务端业务逻辑处理类 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyServerHandlerDemo3 extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + if(msg instanceof NettyMsg) { + NettyMsg customMsg = (NettyMsg)msg; + System.out.println("接受的数据:"+ctx.channel().remoteAddress()+" send "+customMsg.getBody()); + } + + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo4/NettyClientDemo4.java b/src/main/java/com/pancm/nio/netty/demo4/NettyClientDemo4.java new file mode 100644 index 0000000..30dcb3d --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo4/NettyClientDemo4.java @@ -0,0 +1,63 @@ +package com.pancm.nio.netty.demo4; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.string.StringDecoder; + +import java.io.IOException; + +/** + * +* Title: NettyClientDemo4 +* Description: Netty客户端 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyClientDemo4 { + public static String host = "127.0.0.1"; //ip地址 + public static int port = 4567; //端口 + /// 通过nio方式来接收连接和处理连接 + private static EventLoopGroup group = new NioEventLoopGroup(); + private static Bootstrap b = new Bootstrap(); + private static Channel ch=null; + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是ServerBootstrap。 + **/ + public static void main(String[] args) throws InterruptedException, IOException { + b.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY,true) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new StringDecoder()); //绑定自定义编码器 + p.addLast(new NettyClientHandlerDemo4()); //绑定自定义业务 + } + }); + // 连接服务端 + ch = b.connect(host, port).sync().channel(); + System.out.println("客户端成功启动..."); + //发送消息 + star(); + } + + public static void star() throws IOException{ + NettySendBody nsb=new NettySendBody(); + nsb.put("Netty","Hello"); + nsb.put("JAVA","从入门到入土"); + nsb.put("SQL","从删库到跑路"); + ch.writeAndFlush(nsb); + System.out.println("客户端发送数据:"+nsb.toString()); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo4/NettyClientHandlerDemo4.java b/src/main/java/com/pancm/nio/netty/demo4/NettyClientHandlerDemo4.java new file mode 100644 index 0000000..db83032 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo4/NettyClientHandlerDemo4.java @@ -0,0 +1,26 @@ +package com.pancm.nio.netty.demo4; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * +* Description: +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyClientHandlerDemo4 extends ChannelInboundHandlerAdapter { + + /** + * 连接时发送消息 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + NettySendBody nst = new NettySendBody(); + nst.put("1111", "2222"); + ctx.writeAndFlush(nst); + System.out.println(nst); + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo4/NettySendBody.java b/src/main/java/com/pancm/nio/netty/demo4/NettySendBody.java new file mode 100644 index 0000000..11bb318 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo4/NettySendBody.java @@ -0,0 +1,74 @@ +package com.pancm.nio.netty.demo4; + +import java.io.Serializable; +import java.util.concurrent.ConcurrentHashMap; + +/** + * +* Description: Netty 传输对象 +* Version:1.0.0 +* @author pancm +* @date 2017年9月24日 + */ +public class NettySendBody implements Serializable{ + + /** + * 序列化 + */ + private static final long serialVersionUID = 1L; + + + /** 创建集合 */ + private ConcurrentHashMap data; + /** 时间戳 */ + private long timestamp; + + public NettySendBody() { + data = new ConcurrentHashMap(); + timestamp = System.currentTimeMillis(); //取当前时间 + } + + + public ConcurrentHashMap getData() { + return data; + } + + public void setData(ConcurrentHashMap data) { + this.data = data; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public String get(String k) { + return data.get(k); + } + + public void put(String k, String v) { + data.put(k, v); + } + + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(""); + buffer.append(""); + buffer.append("").append(timestamp).append(""); + buffer.append(""); + for (String key : data.keySet()) { + buffer.append("<" + key + ">").append(data.get(key)).append( + ""); + } + buffer.append(""); + buffer.append(""); + return buffer.toString(); + } + + +} diff --git a/src/main/java/com/pancm/nio/netty/demo4/NettyServerDemo4.java b/src/main/java/com/pancm/nio/netty/demo4/NettyServerDemo4.java new file mode 100644 index 0000000..4dab43a --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo4/NettyServerDemo4.java @@ -0,0 +1,57 @@ +package com.pancm.nio.netty.demo4; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.string.StringDecoder; + +import java.net.InetSocketAddress; + +/** + * +* Description: Netty 服务端 测试自定义解码器 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyServerDemo4 { + + private final static int port=4567; + + + public void start(){ + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)) + .childHandler(new ChannelInitializer() { + + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new StringDecoder()); + p.addLast(new NettyServerHandlerDemo4()); //绑定自定义业务逻辑 + }; + + }).option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + // 绑定端口,开始接收进来的连接 + ChannelFuture future = sbs.bind(port).sync(); + + System.out.println("Netty服务端启动成功,端口为: " + port ); + future.channel().closeFuture().sync(); //释放监听 + } catch (Exception e) { + bossGroup.shutdownGracefully(); //释放资源 + workerGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + new NettyServerDemo4().start(); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo4/NettyServerHandlerDemo4.java b/src/main/java/com/pancm/nio/netty/demo4/NettyServerHandlerDemo4.java new file mode 100644 index 0000000..0e375e6 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo4/NettyServerHandlerDemo4.java @@ -0,0 +1,46 @@ +package com.pancm.nio.netty.demo4; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.SimpleChannelInboundHandler; + +/** + * +* Description:服务端业务逻辑处理类 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyServerHandlerDemo4 extends ChannelInboundHandlerAdapter { + + /** + * 处理业务逻辑消息 + */ + protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println(" ----- "+msg); + if(msg instanceof NettySendBody) { + NettySendBody nsb = (NettySendBody)msg; + System.out.println("服务端接受的消息为:"+nsb.toString()); + NettySendBody nsb1 = new NettySendBody(); + nsb1.put("3333","44444"); + ctx.writeAndFlush(nsb1); + }else{ + System.out.println("收到非法请求"); + } + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println(" ----- "+msg); + if(msg instanceof NettySendBody) { + NettySendBody nsb = (NettySendBody)msg; + System.out.println("服务端接受的消息为:"+nsb.toString()); + NettySendBody nsb1 = new NettySendBody(); + nsb1.put("3333","44444"); + ctx.writeAndFlush(nsb1); + }else{ + System.out.println("收到非法请求"); + } + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo5/NettyClientDemo5.java b/src/main/java/com/pancm/nio/netty/demo5/NettyClientDemo5.java new file mode 100644 index 0000000..23bdbcd --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo5/NettyClientDemo5.java @@ -0,0 +1,67 @@ +package com.pancm.nio.netty.demo5; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.IdleStateHandler; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * +* Title: NettyClientDemo5 +* Description: Netty客户端 心跳测试 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyClientDemo5 { + public static String host = "127.0.0.1"; //ip地址 + public static int port = 5678; //端口 + /// 通过nio方式来接收连接和处理连接 + private static EventLoopGroup group = new NioEventLoopGroup(); + private static Bootstrap b = new Bootstrap(); + private static Channel ch=null; + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是ServerBootstrap。 + **/ + public static void main(String[] args) throws InterruptedException, IOException { + b.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY,true) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + //入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式 + //因为服务端设置的超时时间是5秒,所以设置4秒 + p.addLast( new IdleStateHandler(0, 4, 0, TimeUnit.SECONDS)); + p.addLast( new StringDecoder()); + p.addLast( new StringEncoder()); + p.addLast(new NettyClientHandlerDemo5()); //绑定自定义业务 + } + }); + // 连接服务端 + ch = b.connect(host, port).sync().channel(); + System.out.println("客户端成功启动..."); + //发送消息 +// star(); + } + + public static void star() throws IOException{ + String str="你好啊,Netty服务端"; + ch.writeAndFlush(str); + System.out.println("客户端发送数据:"+str); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo5/NettyClientHandlerDemo5.java b/src/main/java/com/pancm/nio/netty/demo5/NettyClientHandlerDemo5.java new file mode 100644 index 0000000..8a16963 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo5/NettyClientHandlerDemo5.java @@ -0,0 +1,79 @@ +package com.pancm.nio.netty.demo5; + +import com.pancm.utils.MyTools; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCountUtil; + +/** + * +* Description: Netty业务处理 心跳测试 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyClientHandlerDemo5 extends ChannelInboundHandlerAdapter { + + /**心跳命令 */ + private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Heartbeat", + CharsetUtil.UTF_8)); + + private static final int TRY_TIMES = 3; + + private int currentTime = 0; + + /** + * 建立连接时 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + System.out.println("激活时间是:"+MyTools.getNowTime("")); + ctx.fireChannelActive(); + } + + /** + * 关闭连接时 + */ + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + System.out.println("停止时间是:"+MyTools.getNowTime("")); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + System.out.println("循环触发时间:"+MyTools.getNowTime("")); + if (evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent) evt; + System.out.println("event.state():"+event.state()+",IdleState.READER_IDLE:"+IdleState.READER_IDLE); + if (event.state() == IdleState.WRITER_IDLE) { + System.out.println("TRY_TIMES:"+TRY_TIMES); + if(currentTime <= TRY_TIMES){ + System.out.println("currentTime:"+currentTime); + currentTime++; + ctx.channel().writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()); + } + } + } + } + + /** + * 业务逻辑处理 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println("接受的消息:"+msg); + String message = (String) msg; + if (message.equals("Heartbeat")) { + ctx.write("成功收到心跳信息"); + ctx.flush(); + } + ReferenceCountUtil.release(msg); + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo5/NettyServerDemo5.java b/src/main/java/com/pancm/nio/netty/demo5/NettyServerDemo5.java new file mode 100644 index 0000000..2075bf5 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo5/NettyServerDemo5.java @@ -0,0 +1,63 @@ +package com.pancm.nio.netty.demo5; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.IdleStateHandler; + +import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; + +/** + * +* Description: Netty 服务端 测试心跳 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyServerDemo5 { + /** 设置端口 */ + private final static int port=5678; + + + public void start(){ + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)) + .childHandler(new ChannelInitializer() { + + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + //入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式 + p.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS)); + p.addLast(new StringDecoder()); //String解码器 + p.addLast(new StringEncoder()); //String编码器 + p.addLast(new NettyServerHandlerDemo5()); //绑定自定义业务逻辑 + }; + + }).option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + // 绑定端口,开始接收进来的连接 + ChannelFuture future = sbs.bind(port).sync(); + + System.out.println("Netty服务端启动成功,端口为: " + port ); + future.channel().closeFuture().sync(); //释放监听 + } catch (Exception e) { + bossGroup.shutdownGracefully(); //释放资源 + workerGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + new NettyServerDemo5().start(); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo5/NettyServerHandlerDemo5.java b/src/main/java/com/pancm/nio/netty/demo5/NettyServerHandlerDemo5.java new file mode 100644 index 0000000..36a61ea --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo5/NettyServerHandlerDemo5.java @@ -0,0 +1,60 @@ +package com.pancm.nio.netty.demo5; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; + +/** + * +* Description:服务端业务逻辑处理类 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class NettyServerHandlerDemo5 extends ChannelInboundHandlerAdapter { + /** 时间 */ + private int loss_connect_time = 0; + /** 发送次数 */ + private int count = 1; + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent) evt; + System.out.println("event.state():"+event.state()+",IdleState.READER_IDLE:"+IdleState.READER_IDLE); + if (event.state() == IdleState.READER_IDLE) { + loss_connect_time++; + System.out.println("5 秒没有接收到客户端的信息了"); + if (loss_connect_time > 2) { + System.out.println("关闭这个不活跃的channel"); + ctx.channel().close(); + } + } + } else { + super.userEventTriggered(ctx, evt); + } + } + + + /** + * 处理业务逻辑 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println(" ----- "+msg); + String a="你好啊"+",count:"+count; + ctx.writeAndFlush(a); + count++; + } + + /** + * 异常处理 + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } + +} diff --git a/src/main/java/com/pancm/nio/netty/demo6/NettyServer.java b/src/main/java/com/pancm/nio/netty/demo6/NettyServer.java new file mode 100644 index 0000000..256db41 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo6/NettyServer.java @@ -0,0 +1,41 @@ +package com.pancm.nio.netty.demo6; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; + + +/** + * +* Title: NettyServer +* Description: Netty服务端 Http测试 +* Version:1.0.0 +* @author pancm +* @date 2017年10月26日 + */ +public class NettyServer { + private static final int port = 6789; //设置服务端端口 + private static EventLoopGroup group = new NioEventLoopGroup(); // 通过nio方式来接收连接和处理连接 + private static ServerBootstrap b = new ServerBootstrap(); + + /** + * Netty创建全部都是实现自AbstractBootstrap。 + * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 + **/ + public static void main(String[] args) throws InterruptedException { + try { + b.group(group); + b.channel(NioServerSocketChannel.class); + b.childHandler(new NettyServerFilter()); //设置过滤器 + // 服务器绑定端口监听 + ChannelFuture f = b.bind(port).sync(); + System.out.println("服务端启动成功,端口是:"+port); + // 监听服务器关闭监听 + f.channel().closeFuture().sync(); + } finally { + group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程 + } + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo6/NettyServerFilter.java b/src/main/java/com/pancm/nio/netty/demo6/NettyServerFilter.java new file mode 100644 index 0000000..2635dec --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo6/NettyServerFilter.java @@ -0,0 +1,31 @@ +package com.pancm.nio.netty.demo6; + + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; + + +/** + * +* Title: NettyServerFilter +* Description: Netty 服务端过滤器 +* Version:1.0.0 +* @author pancm +* @date 2017年10月26日 + */ +public class NettyServerFilter extends ChannelInitializer { + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline ph = ch.pipeline(); + //处理http服务的关键handler + ph.addLast("encoder",new HttpResponseEncoder()); + ph.addLast("decoder",new HttpRequestDecoder()); + ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024)); + ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑 + } + } diff --git a/src/main/java/com/pancm/nio/netty/demo6/NettyServerHandler.java b/src/main/java/com/pancm/nio/netty/demo6/NettyServerHandler.java new file mode 100644 index 0000000..95dbc7e --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo6/NettyServerHandler.java @@ -0,0 +1,123 @@ +package com.pancm.nio.netty.demo6; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.CharsetUtil; +import java.net.InetAddress; + +/** + * +* Title: NettyServerHandler +* Description: 服务端业务逻辑 +* Version:1.0.0 +* @author pancm +* @date 2017年10月26日 + */ +public class NettyServerHandler extends ChannelInboundHandlerAdapter { + private String result=""; + /* + * 收到消息时,返回信息 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if(! (msg instanceof FullHttpRequest)){ + result="未知请求!"; + send(ctx,result,HttpResponseStatus.BAD_REQUEST); + return; + } + FullHttpRequest httpRequest = (FullHttpRequest)msg; + try{ + String path=httpRequest.uri(); //获取路径 + String body = getBody(httpRequest); //获取参数 + HttpMethod method=httpRequest.method();//获取请求方法 + //如果不是这个路径,就直接返回错误 + if(!"/test".equalsIgnoreCase(path)){ + result="非法请求!"; + send(ctx,result,HttpResponseStatus.BAD_REQUEST); + return; + } + System.out.println("接收到:"+method+" 请求"); + //如果是GET请求 + if(HttpMethod.GET.equals(method)){ + //接受到的消息,做业务逻辑处理... + System.out.println("body:"+body); + result="GET请求"; + send(ctx,result,HttpResponseStatus.OK); + return; + } + //如果是POST请求 + if(HttpMethod.POST.equals(method)){ + //接受到的消息,做业务逻辑处理... + System.out.println("body:"+body); + result="POST请求"; + send(ctx,result,HttpResponseStatus.OK); + return; + } + + //如果是PUT请求 + if(HttpMethod.PUT.equals(method)){ + //接受到的消息,做业务逻辑处理... + System.out.println("body:"+body); + result="PUT请求"; + send(ctx,result,HttpResponseStatus.OK); + return; + } + //如果是DELETE请求 + if(HttpMethod.DELETE.equals(method)){ + //接受到的消息,做业务逻辑处理... + System.out.println("body:"+body); + result="DELETE请求"; + send(ctx,result,HttpResponseStatus.OK); + return; + } + }catch(Exception e){ + System.out.println("处理请求失败!"); + e.printStackTrace(); + }finally{ + //释放请求 + httpRequest.release(); + } + } + + /** + * 获取body参数 + * @param request + * @return + */ + private String getBody(FullHttpRequest request){ + ByteBuf buf = request.content(); + return buf.toString(CharsetUtil.UTF_8); + } + + /** + * 发送的返回值 + * @param ctx 返回 + * @param context 消息 + * @param status 状态 + */ + private void send(ChannelHandlerContext ctx, String context,HttpResponseStatus status) { + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8)); + response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + /* + * 建立连接时,返回消息 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress()); + ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! "); + super.channelActive(ctx); + } +} diff --git a/src/main/java/com/pancm/nio/netty/demo6/package-info.java b/src/main/java/com/pancm/nio/netty/demo6/package-info.java new file mode 100644 index 0000000..195ec97 --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/demo6/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: Http服务测试 + * Version:1.0.0 + * @author pancm + * @date 2017年10月26日 + */ +package com.pancm.nio.netty.demo6; \ No newline at end of file diff --git a/src/main/java/com/pancm/nio/netty/package-info.java b/src/main/java/com/pancm/nio/netty/package-info.java new file mode 100644 index 0000000..e209e5f --- /dev/null +++ b/src/main/java/com/pancm/nio/netty/package-info.java @@ -0,0 +1,11 @@ +/** + * + */ +/** + * Title: package-info + * Description: Netty 测试 + * Version:1.0.0 + * @author pancm + * @date 2017-8-31 + */ +package com.pancm.nio.netty; \ No newline at end of file diff --git a/src/main/java/com/pancm/nio/package-info.java b/src/main/java/com/pancm/nio/package-info.java new file mode 100644 index 0000000..17076ca --- /dev/null +++ b/src/main/java/com/pancm/nio/package-info.java @@ -0,0 +1,8 @@ +/** +* @Title: package-info +* @Description: nio 相关的代码 +* @Version:1.0.0 +* @author pancm +* @date 2018年9月21日 +*/ +package com.pancm.nio; \ No newline at end of file diff --git a/src/main/java/com/pancm/others/JsoupHtml.java b/src/main/java/com/pancm/others/JsoupHtml.java new file mode 100644 index 0000000..8c73d8f --- /dev/null +++ b/src/main/java/com/pancm/others/JsoupHtml.java @@ -0,0 +1,67 @@ +package com.pancm.others; + +import java.io.IOException; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +/** + * +* @Title: JsoupHtml +* @Description: 爬虫测试 +* 使用 jsoup +* @Version:1.0.0 +* @author pancm +* @date 2018年3月21日 + */ +public class JsoupHtml { + + private String url="http://sou.zhaopin.com/jobs/searchresult.ashx?jl="; //智联招聘网站 + private String city="深圳"; //搜索工作的城市 + private String keywords="java"; //搜索工作的关键字 + public JsoupHtml(String city,String keywords){ + this.city=city; + this.keywords =keywords; + + } + + public void getZhiLianWork(){ + try { + for (int i=0;i<10;i++) { + System.out.println("*********开始遍历第"+(i+1)+"页的求职信息*********"); + Document doc = Jsoup.connect(url+city+"&kw="+keywords+"&p="+(i+1)+"&isadv=0").get(); + Element content = doc.getElementById("newlist_list_content_table"); + if(content == null){ + continue; + } + Elements zwmcEls = content.getElementsByClass("zwmc"); + Elements gsmcEls = content.getElementsByClass("gsmc"); + Elements zwyxEls = content.getElementsByClass("zwyx"); + Elements gzddEls = content.getElementsByClass("gzdd"); + Elements gxsjEls = content.getElementsByClass("gxsj"); + for(int j = 0;j当启动和去取消任务时可以控制 2>第一次执行任务时可以指定你想要的delay时间 + * 在实现时,Timer类可以调度任务,TimerTask则是通过在run()方法里实现具体任务。 Timer实例可以调度多任务,它是线程安全的。 + * 当Timer的构造器被调用时,它创建了一个线程,这个线程可以用来调度任务。 下面是代码: + */ + public static void timer2(){ + TimerTask task = new TimerTask() { + @Override + public void run() { + // task to run goes here + System.out.println("timer2 Hello !!!"); + } + }; + Timer timer = new Timer(); + long delay = 0; + long intevalPeriod = 1 * 1000; + // schedules the task to be run in an interval + timer.scheduleAtFixedRate(task, delay, intevalPeriod); + } + + /** + * ScheduledExecutorService是从Java SE5的java.util.concurrent里,做为并发工具类被引进的,这是最理想的定时任务实现方式。 + * 相比于上两个方法,它有以下好处: + * 1>相比于Timer的单线程,它是通过线程池的方式来执行任务的 + * 2>可以很灵活的去设定第一次执行任务delay时间 + * 3>提供了良好的约定,以便设定执行的时间间隔 + * 下面是实现代码,我们通过ScheduledExecutorService#scheduleAtFixedRate展示这个例子,通过代码里参数的控制,首次执行加了delay时间。 + */ + public static void timer3(){ + Runnable runnable = new Runnable() { + public void run() { + // task to run goes here + System.out.println("timer3 Hello !!"); + } + }; + ScheduledExecutorService service = Executors .newSingleThreadScheduledExecutor(); + // 第二个参数为首次执行的延时时间10s,,第三个参数为定时执行的间隔时间 2s + service.scheduleAtFixedRate(runnable, 10, 2, TimeUnit.SECONDS); + } + + + // 设定指定任务task在指定时间time执行 schedule(TimerTask task, Date time) + public static void timer4() { + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + public void run() { + System.out.println("timer1() -------设定要指定任务--------"); + } + }, 2000);// 设定指定的时间time,此处为2000毫秒 + } + + // 设定指定任务task在指定延迟delay后进行固定延迟peroid的执行 + // schedule(TimerTask task, long delay, long period) + public static void timer5() { + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + public void run() { + System.out.println("timer2() -------设定要指定任务--------"); + } + }, 1000, 5000); + } + + // 设定指定任务task在指定延迟delay后进行固定频率peroid的执行。 + // scheduleAtFixedRate(TimerTask task, long delay, long period) + public static void timer6() { + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + public void run() { + System.out.println("timer3() -------设定要指定任务--------"); + } + }, 1000, 2000); + } + + // 安排指定的任务task在指定的时间firstTime开始进行重复的固定速率period执行. + // Timer.scheduleAtFixedRate(TimerTask task,Date firstTime,long period) + public static void timer7() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 12); // 控制时 + calendar.set(Calendar.MINUTE, 0); // 控制分 + calendar.set(Calendar.SECOND, 0); // 控制秒 + + Date time = calendar.getTime(); // 得出执行任务的时间,此处为今天的12:00:00 + + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + public void run() { + System.out.println("timer4() -------设定要指定任务--------"); + } + }, time, 1000 * 60 * 60 * 24);// 这里设定将延时每天固定执行 + } +} diff --git a/src/main/java/com/pancm/others/package-info.java b/src/main/java/com/pancm/others/package-info.java new file mode 100644 index 0000000..50ea747 --- /dev/null +++ b/src/main/java/com/pancm/others/package-info.java @@ -0,0 +1,8 @@ +/** +* @Title: package-info +* @Description: 一些其他的测试代码 +* @Version:1.0.0 +* @author pancm +* @date 2018年9月21日 +*/ +package com.pancm.others; \ No newline at end of file diff --git a/src/main/java/com/pancm/pojo/User.java b/src/main/java/com/pancm/pojo/User.java index c1e1dbc..e2338b8 100644 --- a/src/main/java/com/pancm/pojo/User.java +++ b/src/main/java/com/pancm/pojo/User.java @@ -6,8 +6,9 @@ import com.pancm.utils.MyTools; /** * - * Title: User Description:用户pojo类 Version:1.0.0 - * + * @Title: User + * @Description:用户pojo类 + * @Version:1.0.0 * @author pancm * @date 2017年9月26日 */ diff --git a/src/main/java/com/pancm/question/QuestionTest1.java b/src/main/java/com/pancm/question/QuestionTest1.java new file mode 100644 index 0000000..f6c5a30 --- /dev/null +++ b/src/main/java/com/pancm/question/QuestionTest1.java @@ -0,0 +1,77 @@ +package com.pancm.question; + +/** + * @Title: QuestionTest1 + * @Description: + * @Version:1.0.0 + * @author pancm + * @date 2017年7月21日 + */ +public class QuestionTest1 { + + /** + * @param args + */ + public static void main(String[] args) { + + /* + * 在一个二维数组中,每一行都按照从左到右递增的顺序排序, 每一列都按照从上到下递增的顺序排序。请完成一个函数, + * 输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 + */ + int target = 4; + int target1 = 5; + int target2 = 6; + int[][] array = { { 1, 2, 3, 4 }, { 1, 2, 3, 4, 5 } }; + System.out.println(Find(target, array)); + System.out.println(Find(target1, array)); + System.out.println(Find(target2, array)); + System.out.println(Find2(target, array)); + System.out.println(Find2(target1, array)); + System.out.println(Find2(target2, array)); + + + + } + + /** + * 方法一:最笨的方法,利用双重循环找到 + * + * @param target + * @param array + * @return + */ + public static boolean Find(int target, int[][] array) { + for (int[] i : array) { + for (int j : i) { + if (j == target) { + return true; + } + } + } + return false; + } + + /** + * 方法二: 最优解法。 思路:首先我们选择从左下角开始搜寻, (为什么不从左上角开始搜寻,左上角向右和向下都是递增,那么对于一个点, + * 对于向右和向下会产生一个岔路;如果我们选择从左下脚开始搜寻的话, 如果大于就向右,如果小于就向下)。 + * + * @param target + * @param array + * @return + */ + public static boolean Find2(int target, int[][] array) { + int len = array.length - 1; // 得出二维数组的列长度 + int i = 0; + while ((len > 0) && (i < array[0].length)) { + if (array[len][i] > target) { // 如果左下角的数值大于要找的数字,则向右,否则向下 + len--; + } else if (array[len][i] < target) { + i++; + } else { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/com/pancm/question/QuestionTest2.java b/src/main/java/com/pancm/question/QuestionTest2.java new file mode 100644 index 0000000..1dd3874 --- /dev/null +++ b/src/main/java/com/pancm/question/QuestionTest2.java @@ -0,0 +1,60 @@ +package com.pancm.question; + +/** + * @Title: QuestionTest1 + * @Description: + * @Version:1.0.0 + * @author pancm + * @date 2017年7月21日 + */ +public class QuestionTest2 { + + /** + * @param args + */ + public static void main(String[] args) { + + /* + * 请实现一个函数,将一个字符串中的空格替换成“%20”。 + * 例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。 + */ + StringBuffer str=new StringBuffer("We Are Happy"); + System.out.println(replaceString(str)); + System.out.println(replaceString2(str)); + + + } + + /** + * 方法一:利用String的replace 直接替换 + * @param str + * @return String + */ + public static String replaceString(StringBuffer str){ + if(str==null){ + return null; + } + return str.toString().replaceAll(" ", "%20"); + } + + /** + * 方法二:利用数组循环取出 + * @param str + * @return + */ + public static String replaceString2(StringBuffer str){ + if(str==null){ + return null; + } + char []c =str.toString().toCharArray(); + StringBuffer sb=new StringBuffer(); + for(int i=0;i list = jedis.lrange("list", 0, 2); + for (int i = 0, j = list.size(); i < j; i++) { + System.out.println("list的输出结果:" + list.get(i)); + } + + // 设置 redis 字符串数据 + jedis.set("rst", "redisStringTest"); + // 获取存储的数据并输出 + System.out.println("redis 存储的字符串为: " + jedis.get("rst")); + + // 存储数据 + jedis.sadd("setTest1", "abc"); + jedis.sadd("setTest1", "abcd"); + jedis.sadd("setTest1", "abcde"); + // 获取数据并输出 + Set keys = jedis.keys("*"); + // Set keys=jedis.smembers("setTest1"); + // 定义迭代器输出 + Iterator it = keys.iterator(); + while (it.hasNext()) { + String key = it.next(); + System.out.println(key); + } + + } + +} diff --git a/src/main/java/com/pancm/sql/redis/package-info.java b/src/main/java/com/pancm/sql/redis/package-info.java new file mode 100644 index 0000000..8289742 --- /dev/null +++ b/src/main/java/com/pancm/sql/redis/package-info.java @@ -0,0 +1,8 @@ +/** +* @Title: package-info +* @Description: redis 相关代码 +* @Version:1.0.0 +* @author pancm +* @date 2018年9月21日 +*/ +package com.pancm.sql.redis; \ No newline at end of file diff --git a/src/main/java/com/pancm/thread/ThreadPoolTest.java b/src/main/java/com/pancm/thread/ThreadPoolTest.java new file mode 100644 index 0000000..0556ea6 --- /dev/null +++ b/src/main/java/com/pancm/thread/ThreadPoolTest.java @@ -0,0 +1,204 @@ +package com.pancm.thread; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * +* @Title: ThreadPoolTest +* @Description: +* 线程池测试 +* @Version:1.0.0 +* @author pancm +* @date 2018年3月1日 + */ +public class ThreadPoolTest { + + public static void main(String[] args) { +// cachedThreadPool(); +// fixedThreadPool(); +// newSingleThreadExecutor(); +// newScheduledThreadPool(); +// newScheduledThreadPool2(); + threadPoolExecutor(); + } + + /** + * 创建一个可缓存的线程池,如果当前线程池的规模超出了处理需求,将回收空的线程;当需求增加时,会增加线程数量;线程池规模无限制。 + */ + private static void cachedThreadPool() { + ExecutorService exec=Executors.newCachedThreadPool(); + for(int i=0;i<10;i++){ + exec.execute(new MyThread(String.valueOf(i))); + } + //执行到此处并不会马上关闭线程池,执行完成之后才会关闭 但之后不能再往线程池中加线程,否则会报错 + exec.shutdown(); + System.out.println("运行结束!"); + /** + * 1、主线程的执行与线程池里的线程分开,有可能主线程结束了,但是线程池还在运行 + * 2、放入线程池的线程并不一定会按其放入的先后而顺序执行 + * + */ + } + + /** + * 创建一个固定长度的线程池,当到达线程最大数量时,线程池的规模将不再变化。 + */ + private static void fixedThreadPool() { + ExecutorService exec = Executors.newFixedThreadPool(5); + for(int i = 0; i < 10; i++) { + exec.execute(new MyThread(String.valueOf(i))); + } + exec.shutdown(); //执行到此处并不会马上关闭线程池 + System.out.println("运行结束!"); + /** + * 1线程开始运行,时间2018-03-01 11:50:33 170 + 4线程开始运行,时间2018-03-01 11:50:33 170 + 3线程开始运行,时间2018-03-01 11:50:33 170 + 2线程开始运行,时间2018-03-01 11:50:33 170 + 0线程开始运行,时间2018-03-01 11:50:33 170 + 4线程运行结束,时间2018-03-01 11:50:34 171 + 3线程运行结束,时间2018-03-01 11:50:34 171 + 2线程运行结束,时间2018-03-01 11:50:34 171 + 5线程开始运行,时间2018-03-01 11:50:34 172 + 6线程开始运行,时间2018-03-01 11:50:34 172 + 7线程开始运行,时间2018-03-01 11:50:34 172 + 1线程运行结束,时间2018-03-01 11:50:34 172 + 0线程运行结束,时间2018-03-01 11:50:34 172 + 8线程开始运行,时间2018-03-01 11:50:34 172 + 9线程开始运行,时间2018-03-01 11:50:34 172 + 8线程运行结束,时间2018-03-01 11:50:35 181 + 6线程运行结束,时间2018-03-01 11:50:35 181 + 9线程运行结束,时间2018-03-01 11:50:35 181 + 7线程运行结束,时间2018-03-01 11:50:35 181 + 5线程运行结束,时间2018-03-01 11:50:35 181 + + 结论:1,FixedThreadPool模式会使用一个优先固定数目的线程来处理若干数目的任务。 + 2,FixedThreadPool模式下最多 的线程数目是一定的。 + + */ + } + + /** + * 创建一个单线程的Executor,确保任务对了,串行执行 + */ + private static void newSingleThreadExecutor() { + ExecutorService exec = Executors.newSingleThreadExecutor(); //创建大小为1的固定线程池 + for(int i = 0; i < 10; i++) { + exec.execute(new MyThread(String.valueOf(i))); + } + exec.shutdown(); //执行到此处并不会马上关闭线程池 + System.out.println("运行结束!"); + } + + + /** + * 创建一个固定长度的线程池,而且以延迟或者定时的方式来执行,类似Timer; + */ + private static void newScheduledThreadPool() { + ScheduledThreadPoolExecutor exec = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10); //创建大小为10的线程池 + for(int i = 0; i < 10; i++) { + exec.schedule(new MyThread(String.valueOf(i)), 2, TimeUnit.SECONDS);//延迟2秒执行 + } + //如果任务都完成了则返回true + while(!exec.isTerminated()){ + //wait for all tasks to finish +// System.out.println("正在运行中..."); + } + System.out.println("运行结束!"); + } + + /** + * 设置固定时间执行 + */ + private static void newScheduledThreadPool2() { + ScheduledThreadPoolExecutor exec = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10); //创建大小为10的线程池 + long oneDay = 24 * 60 * 60 * 1000; + long initDelay = getTimeMillis("14:08:00") - System.currentTimeMillis(); + initDelay = initDelay > 0 ? initDelay : oneDay + initDelay; + exec.scheduleAtFixedRate(new MyThread(String.valueOf(1)), initDelay, oneDay, TimeUnit.MILLISECONDS); + System.out.println("运行结束!"); + } + + /** + * 获取指定时间对应的毫秒数 + * @param time "HH:mm:ss" + * @return + */ + private static long getTimeMillis(String time) { + try { + DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); + DateFormat dayFormat = new SimpleDateFormat("yy-MM-dd"); + Date curDate = dateFormat.parse(dayFormat.format(new Date()) + " " + time); + return curDate.getTime(); + } catch (ParseException e) { + e.printStackTrace(); + } + return 0; + } + + /** + * ThreadPoolExecutor线程池 + */ + private static void threadPoolExecutor() { + int corePoolSize=5; + int maximumPoolSize=10; + long keepAliveTime=2L; + // 线程核心数,最大线程数,线程缓存时间,时间格式,缓存队列 ,线程工厂,拒绝策略 + ThreadPoolExecutor tpx=new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, + TimeUnit.SECONDS, new ArrayBlockingQueue(3), + Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy()); + + for (int i = 1; i <= 10; i++) { + try { + // 产生一个任务,并将其加入到线程池 + String task = "task@ " + i; +// System.out.println("put " + task); + tpx.execute(new MyThread(task)); + // 便于观察,等待一段时间 + Thread.sleep(20); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + +} + +class MyThread implements Runnable{ + private String name; + public MyThread(String name){ + this.name=name; + } + + @Override + public void run() { + System.out.println(name+ "线程开始运行,时间:" +getNowTime()); + pause(1000); + System.out.println(name+ "线程运行结束,时间:" +getNowTime()); + } + + private void pause(long lo) { + try { + Thread.sleep(lo); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + private String getNowTime(){ + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()); + } + +} diff --git a/src/main/java/com/pancm/thread/concurrent/package-info.java b/src/main/java/com/pancm/thread/concurrent/package-info.java index 4c71df1..229666d 100644 --- a/src/main/java/com/pancm/thread/concurrent/package-info.java +++ b/src/main/java/com/pancm/thread/concurrent/package-info.java @@ -1,6 +1,6 @@ /** * @Title: package-info -* @Description: 并发相关的类 +* @Description: 并发相关的测试代码 * @Version:1.0.0 * @author pancm * @date 2018年9月20日 diff --git a/src/main/java/com/pancm/thread/lock/LockSydTest.java b/src/main/java/com/pancm/thread/lock/LockSydTest.java new file mode 100644 index 0000000..8f26aed --- /dev/null +++ b/src/main/java/com/pancm/thread/lock/LockSydTest.java @@ -0,0 +1,124 @@ +package com.pancm.thread.lock; + +import java.util.Calendar; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** +* @Title: LockSydTest +* @Description: Lock(显示锁)和synchronized(内部锁) 测试 +* @Version:1.0.0 +* @author pancm +* @date 2017年10月18日 + */ +public class LockSydTest { + + public static void main(String[] args) { + try { + runTasks(lockTest.class); + runTasks(synTest.class); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 任务执行器 + * @param cl + * @throws Exception + */ + public static void runTasks(Class cl) throws Exception{ + ExecutorService es=Executors.newCachedThreadPool(); //创建一个执行器 + System.out.println("---开始执行---"+cl.getSimpleName()+" 任务"); + //启动三个线程 + for(int i=0;i<3;i++){ + es.submit(cl.newInstance()); + } + //等待足够长的时间,然后关闭执行器 + TimeUnit.SECONDS.sleep(10); + System.out.println("---结束任务---"+cl.getSimpleName()+" 任务执行完毕 \n"); + es.shutdown();//关闭执行器 + + + /* + * 输出结果: + * ---开始执行---lockTest 任务 + 线程名称:pool-1-thread-2,执行时间:35 s + 线程名称:pool-1-thread-3,执行时间:35 s + 线程名称:pool-1-thread-1,执行时间:35 s + ---结束任务---lockTest 任务执行完毕 + + ---开始执行---synTest 任务 + 线程名称:pool-2-thread-1,执行时间:45 s + 线程名称:pool-2-thread-3,执行时间:47 s + 线程名称:pool-2-thread-2,执行时间:49 s + ---结束任务---synTest 任务执行完毕 + * + */ + + /* 总结: + * lock(显示锁) 是对象级别的锁,而synchronized(内部锁)是类级别的锁, + * 也就是说显示锁是跟着对象的,而内部锁是跟随类的。 + * 简单来说的话,把lock定义为多线程类的私有属性是起不到资源互斥作用的, + * 除非把lock定义为所有线程的共享变量。 + * + */ + } +} + +class Task{ + public void doSomething(){ + try{ + Thread.sleep(2000); //等待2秒,此处线程状态转为WAITING + }catch(Exception e){ + + } + StringBuffer sb=new StringBuffer(); + sb.append("线程名称:"+Thread.currentThread().getName()); + sb.append(",执行时间:"+Calendar.getInstance().get(13)+" s"); + System.out.println(sb); + } +} + +/** + * +* Title: lockTest +* Description: Lock(显示锁)测试 +* Version:1.0.0 +* @author pancm +* @date 2017年10月18日 + */ +class lockTest extends Task implements Runnable{ + private final Lock lock=new ReentrantLock(); + @Override + public void run() { + try{ + lock.lock(); + doSomething(); + }catch(Exception e){ + + }finally{ + lock.unlock(); //释放锁 + } + } +} + +/** + * +* Title: lockTest +* Description: synchronized(内部锁)测试 +* Version:1.0.0 +* @author pancm +* @date 2017年10月18日 + */ +class synTest extends Task implements Runnable{ + @Override + public void run() { + synchronized("A"){ + doSomething(); + } + } +} diff --git a/src/main/java/com/pancm/thread/lock/LockTest1.java b/src/main/java/com/pancm/thread/lock/LockTest1.java new file mode 100644 index 0000000..86394ee --- /dev/null +++ b/src/main/java/com/pancm/thread/lock/LockTest1.java @@ -0,0 +1,84 @@ +package com.pancm.thread.lock; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * +* @Title: LockTest1 +* @Description: Lock(显示锁)和synchronized(内部锁) 测试 +* @Version:1.0.0 +* @author pancm +* @date 2017年10月23日 + */ +public class LockTest1 { + + public static void main(String[] args) { + + } + +} + +class Foo{ + /** 可重入的读写锁 */ + private final ReentrantReadWriteLock rwl=new ReentrantReadWriteLock(); + + /** 读锁 */ + private final Lock r=rwl.readLock(); + + /** 写锁 */ + private final Lock w=rwl.writeLock(); + + + //读操作,可并发执行 + public void read(){ + try{ + r.lock(); + Thread.sleep(1000); + System.out.println("read......"); + }catch(InterruptedException e){ + e.printStackTrace(); + }finally{ + r.unlock(); + } + } + + //写操作,同时值允许一个执行 + public void write(){ + try{ + w.lock(); + Thread.sleep(1000); + System.out.println("write......"); + }catch(InterruptedException e){ + e.printStackTrace(); + }finally{ + w.unlock(); + } + } + + /* + * 1.Lock支持更细粒度的锁控制; + * 2.Lock是无阻塞锁,synchronized是阻塞锁; + * 例:当线程A持有锁的时,线程B也期望获得锁,此时,如果线程是使用的显示锁,则B线程为等待状态(即阻塞); + * 如果使用的是内部锁则为阻塞状态。 + * + * 3.Lock可以实现公平锁,而synchronized只能是非公平锁; + * 非公平锁:当一个线程A持有锁,而线程B、C处于阻塞状态时, 若线程A释放锁,JVM将从线程B、C随机选择一个线程持有锁并使其获得执行权, + * 这叫做非公平锁(因为它抛弃了先来后到的顺序); + * 公平锁:若JVM选择了等待时间最长的一个线程持有锁,则为公平锁(保证每个线程的等待时间均衡)。 + * 需要注意的是,即使是公平锁,JVM也无法做到准确的“公平”,在程序中不能以此作为计算。 + * 显示锁默认是非公平锁,可以在构造函数中增加true来声明公平锁,而synchronized只能是实现非公平锁。 + * + * 4.Lock是代码级,synchronized是JVM级的 + * Lock是通过编码实现的,synchronized是在运行期由JVM解释的,相对来说synchronized的优化可能性更高, + * 毕竟是在最核心部分支持的,Lock的优化则需要用户自行考虑。 + * 灵活、强大选择Lock;快捷、安全选择synchronized。 + * + * + * + * + * + */ + + +} \ No newline at end of file diff --git a/src/main/java/com/pancm/thread/lock/LockTest2.java b/src/main/java/com/pancm/thread/lock/LockTest2.java new file mode 100644 index 0000000..53df28c --- /dev/null +++ b/src/main/java/com/pancm/thread/lock/LockTest2.java @@ -0,0 +1,83 @@ +package com.pancm.thread.lock; + +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * + * @Title: LockTest2 + * @Description: 显示锁的唤醒测试 + * @Version:1.0.0 + * @author pancm + * @date 2018年2月28日 + */ +public class LockTest2 { + final Lock lock = new ReentrantLock(); + + final Condition notfull = lock.newCondition(); + + final Condition notempty = lock.newCondition(); + + final Object[] items = new Object[100]; + + int putptr, takeptr, count; + + public static void main(String[] args) throws InterruptedException { + LockTest2 lt = new LockTest2(); + Object obj = 2; + lt.put(obj); + lt.take(); + } + + /** + * 而现在锁是指定对象lock。所以查找等待唤醒机制方式需要通过lock接口来完成。 + * 而lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。 + * 这个对象就是condition,将object中的三个方法进行单独的封装。 并提供了功能一致的方法 + * await()、signal()、signalall()体现新版本对象的好处。 + * + * @param x + * @throws InterruptedException + */ + public void put(Object x) throws InterruptedException { + lock.lock(); + try { + while (count == items.length) { + notfull.await(); + } + items[putptr] = x; + + if (++putptr == items.length) { + putptr = 0; + } + ++count; + //唤醒一个等待的线程 + notempty.signal(); + + } finally { + lock.unlock(); + } + } + + private Object take() throws InterruptedException { + lock.lock(); + try { + while (count == 0) { + notempty.await(); + } + Object x = items[takeptr]; + + if (++takeptr == items.length) { + takeptr = 0; + } + --count; + //唤醒一个等待的线程 + notfull.signal(); + return x; + + } finally { + lock.unlock(); + } + } + +} diff --git a/src/main/java/com/pancm/thread/lock/VolatileTest1.java b/src/main/java/com/pancm/thread/lock/VolatileTest1.java new file mode 100644 index 0000000..a1e05c5 --- /dev/null +++ b/src/main/java/com/pancm/thread/lock/VolatileTest1.java @@ -0,0 +1,111 @@ +package com.pancm.thread.lock; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * +* Title: VolatileTest1 +* Description: volatile关键字的测试 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class VolatileTest1 { + public volatile int inv = 0; //使用volatile 保证了可见性,其他线程也可以查看更新之后的值 + public int ins = 0; + + public int inl = 0; + Lock lock = new ReentrantLock(); + + public void increase() { + inv++; + } + + public synchronized void insrease() { //使用synchronized,可以保证原子性 + ins++; + } + + public void inlrease(){ //使用loca 也可以保证 + lock.lock(); + try { + inl++; + } finally{ + lock.unlock(); + } + } + + + public static void main(String[] args) { + volatileTs(); + synTs(); + lockTs(); + } + + /** + * 使用 volatile 是无法保证 原子性的 + * 因为 自增操作不是原子性操作 + */ + public static void volatileTs(){ + final VolatileTest1 test = new VolatileTest1(); + for(int i=0;i<10;i++){ + new Thread(){ + @Override + public void run() { + for(int j=0;j<1000;j++) { + test.increase(); + } + }; + }.start(); + } + while(Thread.activeCount()>1) { + Thread.yield(); + } + System.out.println(test.inv); //数据小于 10000 例如:9303,9068 + } + + /** + * 使用synchronized,可以保证原子性 + */ + public static void synTs(){ + final VolatileTest1 test = new VolatileTest1(); + for(int i=0;i<10;i++){ + new Thread(){ + @Override + public void run() { + for(int j=0;j<1000;j++) { + test.insrease(); + } + }; + }.start(); + } + while(Thread.activeCount()>1) { + Thread.yield(); + } + System.out.println(test.ins); // 10000 保证了原子性 + } + + /** + * 使用lock,可以保证原子性 + */ + public static void lockTs(){ + final VolatileTest1 test = new VolatileTest1(); + for(int i=0;i<10;i++){ + new Thread(){ + @Override + public void run() { + for(int j=0;j<1000;j++) { + test.inlrease(); + } + }; + }.start(); + } + while(Thread.activeCount()>1) { + Thread.yield(); + } + System.out.println(test.inl); //10000 保证了原子性 + } + + + } + diff --git a/src/main/java/com/pancm/thread/lock/VolatileTest2.java b/src/main/java/com/pancm/thread/lock/VolatileTest2.java new file mode 100644 index 0000000..8d7325e --- /dev/null +++ b/src/main/java/com/pancm/thread/lock/VolatileTest2.java @@ -0,0 +1,112 @@ +package com.pancm.thread.lock; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * +* Title: volatileTest1 +* Description: +* volatile关键字的测试 +* Version:1.0.0 +* @author pancm +* @date 2017年9月21日 + */ +public class VolatileTest2 { + public volatile long inv = 0; //使用volatile 保证了可见性,其他线程也可以查看更新之后的值 + public long ins = 0; + + public long inl = 0; + Lock lock = new ReentrantLock(); + + public void increase() { + inv++; + } + + public synchronized void insrease() { //使用synchronized,可以保证原子性 + ins++; + } + + public void inlrease(){ //使用loca 也可以保证 + lock.lock(); + try { + inl++; + } finally{ + lock.unlock(); + } + } + + + public static void main(String[] args) { + volatileTs(); + synTs(); + lockTs(); + } + + /** + * 使用 volatile 是无法保证 原子性的 + * 因为 自增操作不是原子性操作 + */ + public static void volatileTs(){ + final VolatileTest2 test = new VolatileTest2(); + for(int i=0;i<10;i++){ + new Thread(){ + @Override + public void run() { + for(int j=0;j<1000;j++) { + test.increase(); + } + }; + }.start(); + } + while(Thread.activeCount()>1) { + Thread.yield(); + } + System.out.println(test.inv); //数据小于 10000 例如:9303,9068 + } + + /** + * 使用synchronized,可以保证原子性 + */ + public static void synTs(){ + final VolatileTest2 test = new VolatileTest2(); + for(int i=0;i<10;i++){ + new Thread(){ + @Override + public void run() { + for(int j=0;j<1000;j++) { + test.insrease(); + } + }; + }.start(); + } + while(Thread.activeCount()>1) { + Thread.yield(); + } + System.out.println(test.ins); // 10000 保证了原子性 + } + + /** + * 使用lock,可以保证原子性 + */ + public static void lockTs(){ + final VolatileTest2 test = new VolatileTest2(); + for(int i=0;i<10;i++){ + new Thread(){ + @Override + public void run() { + for(int j=0;j<1000;j++) { + test.inlrease(); + } + }; + }.start(); + } + while(Thread.activeCount()>1) { + Thread.yield(); + } + System.out.println(test.inl); //10000 保证了原子性 + } + + + } + diff --git a/src/main/java/com/pancm/thread/lock/package-info.java b/src/main/java/com/pancm/thread/lock/package-info.java new file mode 100644 index 0000000..e3e4e7b --- /dev/null +++ b/src/main/java/com/pancm/thread/lock/package-info.java @@ -0,0 +1,8 @@ +/** + * @Title: package-info + * @Description: 一些锁的测试代码 + * @Version:1.0.0 + * @author pancm + * @date 2017年11月7日 + */ +package com.pancm.thread.lock; \ No newline at end of file diff --git a/src/main/java/com/pancm/thread/package-info.java b/src/main/java/com/pancm/thread/package-info.java new file mode 100644 index 0000000..1d70094 --- /dev/null +++ b/src/main/java/com/pancm/thread/package-info.java @@ -0,0 +1,9 @@ +/** + * @Title: package-info + * @Description: + * 线程相关的测试类 + * Version:1.0.0 + * @author pancm + * @date 2018年3月1日 + */ +package com.pancm.thread; \ No newline at end of file