实践经验
目录:
四、JMX(jconsole)
六、AtomicInteger 原子性数值
七、DocumentHelper,XML解析
♥Mybatis的forEach使用详细:
在需要传入多个数据到Mybatis中是用到
在Collection层中:
HashMap<String, List<Integer>> map = new HashMap<>();//建立Map ArrayList<Integer> list = new ArrayList<>();//建立List map.put("haha",list);//添加list到map中 命名为haha
在Mapper.xml中
<delete id="deleteByPrimaryKey" parameterType="list"> delete
from gzcrm_cmresults <where> <foreach collection="haha" item="list" open="and (" close=")" separator="or"> cm_key = #{list,jdbcType=INTEGER} </foreach> </where> </delete>
♥正则表达式
HTML:
<form action=""> <input type="text" pattern="\d{2}"> <input type="submit" value="提交"> </form>
Java:
String srt = "142-66";
System.out.println(srt.matches(".{2}-\\d{3}||\\d{3}-\\d{2}"));
System.out.println(Pattern.matches("^123\\w{1}$", "123L"));//^$
String text = "北京市()北京市(海淀区)(朝阳区)(西城区)";
Pattern pattern = Pattern.compile("北.*?市(?=\\()" );//零宽断言:(?=exp)、(?<=exp)、贪婪与懒惰
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
System.out.println(matcher.group(0));
}
♥Ajax
function select() { $.post({ url:"${pageContext.request.contextPath}/zhengjianS",//地址 data:{"id":$("#cate_id").val(),"year":$("#cate_year").val()},
// 在Controller层中直接接受参数public List<GzcrmCertificate> selectAll2(String id, String year){} success:function (data) { var html=""; console.log(data); for(var i=0;i<data.length;i++){
//取值时用返回对象的字段取值 var d2=new Date(data[i].cmModificationDate); html+="<tr>\n" + "<td><input name=\"checkbox2\" id=\"checkbox2\" type=\"checkbox\" value="+data[i].cmKey+" /></td>\n" + "<td>"+data[i].cmId+"</td>\n" + "<td>"+data[i].cmCertificateName+"</td>\n" + "<td>"+data[i].cmCertificateType+"</td>\n" + "<td>"+data[i].cmCertificateNo+"</td>\n" + "<td>"+data[i].cmCertificateUnit+"</td>\n" + "<td>"+data[i].cmPeriod+"</td>\n" + "<td>"+data[i].cmCertificateUrl+"</td>\n" + "<td>"+d2.getFullYear()+"-"+[d2.getMonth()+1]+"-"+d2.getDate()+"</td>\n" +
//日期的展示(注意在这里的月份从0-11月份用[]对其数字加以) "<td>"+data[i].cmModificationPerson+"</td>\n" + "</tr>" } $("#list2").html(html);
//修改html、动态修改页面 } }) }
♥JMX
1、新建接口(JMXServiceMXBean ),放接口,Jconsole调用。(类名随便)
public interface JMXServiceMXBean { public void getLeng(); }
2、实现接口(JMXService ),注册JMX,实现类逻辑。
public class JMXService implements JMXServiceMXBean { @Override public void getLeng(){ System.out.println("进入接口"); } }
3、建立初始化
public class ServiceInit { public static void startService() { // MBeanAgent.registMBeanServiceByJMXMP("127.0.0.1", 3333, "FCU:type=JMXService", new JMXService()); MBeanAgent.registMBeanService("MyJMX:type=JMXService", new JMXService()); } }
封装注册类
public class MBeanAgent { public static void registMBeanServiceByJMXMP(String host, int port, String key, Object service) { try { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); Map<String, Object> env = new HashMap<String, Object>(); env.put("com.sun.management.jmxremote.authenticate", "false"); env.put("jmx.remote.server.address.wildcard", "false"); JMXServiceURL jmxURL = null; jmxURL = new JMXServiceURL("jmxmp", host, port); JMXConnectorServer connectServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxURL, env, mbs); connectServer.start(); ObjectName name = new ObjectName(key); if (!mbs.isRegistered(name)) { mbs.registerMBean(service, name); } } catch (Exception e) { e.printStackTrace(); } } public static void registMBeanService(String key, Object service) { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); try { ObjectName name = new ObjectName(key); if (!mbs.isRegistered(name)) { mbs.registerMBean(service, name); } } catch (Exception e) { e.printStackTrace(); } } }
4、启动服务器端
启动时需要指定参数:
-Dcom.sun.management.jmxremote.port=22001
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.hostname=127.0.0.1
-Dcom.sun.management.jmxremote.authenticate=false
public static void main(String[] args) { ServiceInit.startService(); try { Thread.sleep(9999999999999999L); } catch (InterruptedException e) { } }
5、启动客户端,调用函数
try { String jmxUrl = "service:jmx:rmi:///jndi/rmi://127.0.0.1:22001/jmxrmi"; JMXConnector connector = null; Object result = null; JMXServiceURL address; try { String objectName = "MyJMX:type=JMXService"; String operationName = "getLeng"; Object[] params = new Object[]{}; String[] signature = new String[]{}; address = new JMXServiceURL(jmxUrl); connector = JMXConnectorFactory.connect(address); ObjectName name = new ObjectName(objectName); MBeanServerConnection mbs = connector.getMBeanServerConnection(); result = mbs.invoke(name, operationName, params, signature); System.out.println(result); } catch (Exception e) { throw e; } finally { if (null != connector) { connector.close(); } } } catch (Exception e) { e.printStackTrace(); }
二、HtmlAdaptorServer
依赖:
<dependency> <groupId>com.sun.jdmk</groupId> <artifactId>jmxtools</artifactId> <version>1.2.1</version> </dependency>
应用:
public static void main(String[] args) { //创建服务 System.out.println("CREATE the MBeanServer."); MBeanServer server = MBeanServerFactory.createMBeanServer(); //注册并启动 System.out.println("CREATE, REGISTER and START a new HTML adaptor:"); HtmlAdaptorServer html = new HtmlAdaptorServer(); ObjectName html_name = null; try { html_name = new ObjectName("Adaptor:name=wgyhtml,port=8082"); System.out.println("OBJECT NAME= " + html_name); server.registerMBean(html, html_name); html.start(); } catch (Exception e) { System.out.println("!!! Could not create the HTML adaptor !!!"); e.printStackTrace(); return; } }
登录网页,访问端口,内容如jconsole
♥二维码生成
导入依赖:
<dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.3.0</version> </dependency>
需要建三个类:
1:
package tWeiM; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import com.google.zxing.LuminanceSource; public class T extends LuminanceSource { private final BufferedImage image; private final int left; private final int top; public T(BufferedImage image) { this(image, 0, 0, image.getWidth(), image.getHeight()); } public T(BufferedImage image, int left, int top, int width, int height) { super(width, height); int sourceWidth = image.getWidth(); int sourceHeight = image.getHeight(); if (left + width > sourceWidth || top + height > sourceHeight) { throw new IllegalArgumentException("Crop rectangle does not fit within image data."); } for (int y = top; y < top + height; y++) { for (int x = left; x < left + width; x++) { if ((image.getRGB(x, y) & 0xFF000000) == 0) { image.setRGB(x, y, 0xFFFFFFFF); // = white } } } this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY); this.image.getGraphics().drawImage(image, 0, 0, null); this.left = left; this.top = top; } public byte[] getRow(int y, byte[] row) { if (y < 0 || y >= getHeight()) { throw new IllegalArgumentException("Requested row is outside the image: " + y); } int width = getWidth(); if (row == null || row.length < width) { row = new byte[width]; } image.getRaster().getDataElements(left, top + y, width, 1, row); return row; } public byte[] getMatrix() { int width = getWidth(); int height = getHeight(); int area = width * height; byte[] matrix = new byte[area]; image.getRaster().getDataElements(left, top, width, height, matrix); return matrix; } public boolean isCropSupported() { return true; } public LuminanceSource crop(int left, int top, int width, int height) { return new T(image, this.left + left, this.top + top, width, height); } public boolean isRotateSupported() { return true; } public LuminanceSource rotateCounterClockwise() { int sourceWidth = image.getWidth(); int sourceHeight = image.getHeight(); AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth); BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g = rotatedImage.createGraphics(); g.drawImage(image, transform, null); g.dispose(); int width = getWidth(); return new T(rotatedImage, top, sourceWidth - (left + width), getHeight(), width); } }
2:
package tWeiM; import com.google.zxing.*; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import javax.imageio.ImageIO; import java.awt.*; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.OutputStream; import java.util.Hashtable; public class QRCodeUtil { private static final String CHARSET = "utf-8"; private static final String FORMAT_NAME = "JPG"; // 二维码尺寸 private static final int QRCODE_SIZE = 300; // LOGO宽度 private static final int WIDTH = 60; // LOGO高度 private static final int HEIGHT = 60; private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception { Hashtable hints = new Hashtable(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.CHARACTER_SET, CHARSET); hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints); int width = bitMatrix.getWidth(); int height = bitMatrix.getHeight(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); } } if (imgPath == null || "".equals(imgPath)) { return image; } // 插入图片 QRCodeUtil.insertImage(image, imgPath, needCompress); return image; } private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception { File file = new File(imgPath); if (!file.exists()) { System.err.println("" + imgPath + " 该文件不存在!"); return; } Image src = ImageIO.read(new File(imgPath)); int width = src.getWidth(null); int height = src.getHeight(null); if (needCompress) { // 压缩LOGO if (width > WIDTH) { width = WIDTH; } if (height > HEIGHT) { height = HEIGHT; } Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH); BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); g.drawImage(image, 0, 0, null); // 绘制缩小后的图 g.dispose(); src = image; } // 插入LOGO Graphics2D graph = source.createGraphics(); int x = (QRCODE_SIZE - width) / 2; int y = (QRCODE_SIZE - height) / 2; graph.drawImage(src, x, y, width, height, null); Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6); graph.setStroke(new BasicStroke(3f)); graph.draw(shape); graph.dispose(); } public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception { BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress); mkdirs(destPath); // String file = new Random().nextInt(99999999)+".jpg"; // ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file)); ImageIO.write(image, FORMAT_NAME, new File(destPath)); } public static BufferedImage encode(String content, String imgPath, boolean needCompress) throws Exception { BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress); return image; } public static void mkdirs(String destPath) { File file = new File(destPath); // 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常) if (!file.exists() && !file.isDirectory()) { file.mkdirs(); } } public static void encode(String content, String imgPath, String destPath) throws Exception { QRCodeUtil.encode(content, imgPath, destPath, false); } public static void encode(String content, String destPath) throws Exception { QRCodeUtil.encode(content, null, destPath, false); } public static void encode(String content, String imgPath, OutputStream output, boolean needCompress) throws Exception { BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress); ImageIO.write(image, FORMAT_NAME, output); } public static void encode(String content, OutputStream output) throws Exception { QRCodeUtil.encode(content, null, output, false); } public static String decode(File file) throws Exception { BufferedImage image; image = ImageIO.read(file); if (image == null) { return null; } T source = new T(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Result result; Hashtable hints = new Hashtable(); hints.put(DecodeHintType.CHARACTER_SET, CHARSET); result = new MultiFormatReader().decode(bitmap, hints); String resultStr = result.getText(); return resultStr; } public static String decode(String path) throws Exception { return QRCodeUtil.decode(new File(path)); } }
3:
package tWeiM; public class M { public static void main(String[] args) throws Exception { // 存放在二维码中的内容 String text = "https://www.zhihu.com/people/wang-la-la-44-5"; // 嵌入二维码的图片路径 String imgPath = "E:/Tweima/yu.jpg"; // 生成的二维码的路径及名称 String destPath = "E:/Tweima/jam.png"; //生成二维码 QRCodeUtil.encode(text, imgPath, destPath, true); // 解析二维码 String str = QRCodeUtil.decode(destPath); // 打印出解析出的内容 System.out.println(str); } }
六、AtomicInteger
AtomicInteger atomicInteger = new AtomicInteger(10); //加之前获取类似于i++ System.out.println(atomicInteger.getAndIncrement()); //先加后返回,类似以++i System.out.println(atomicInteger.addAndGet(1)); //自增1 System.out.println(atomicInteger.decrementAndGet()); //返回结果,两者本质为get() int i = atomicInteger.get(); int i1 = atomicInteger.intValue();
七、DocumentHelper
String xmlMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><tasks><pojo id=\"1\"><name> 666</name><pw> 123</pw></pojo></tasks>"; //使用工具解析 Document dom = DocumentHelper.parseText(xmlMsg); //获取根rootElement Element rootElement = dom.getRootElement(); List<Element> tasks = rootElement.elements(); //遍历 Iterator<Element> it = tasks.iterator(); while (it.hasNext()) { Element ele =it.next(); //输出自带的属性 例如其中的id <pojo id="1"> System.out.println(ele.getName()+":"); for (Object value : ele.attributes()) { Attribute attribute=(Attribute)value; System.out.println(attribute.getName()+":"+attribute.getValue()); } //输出子属性 Iterator iterator = ele.elementIterator(); while (iterator.hasNext()) { Element element=(Element)iterator.next(); System.out.println(element.getName()+":"+element.getTextTrim()); } }
八、Jackson,Json解析
maven依赖:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.5</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.5</version> </dependency>
主类:
ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); //生成对象 Pojo pojo2String = new Pojo(); pojo2String.setPojoId(123); pojo2String.setPojoName(456); ArrayList<Phone> phones = new ArrayList<>(); phones.add(new Phone("手机", "phone")); phones.add(new Iphone("苹果", "iphone", "siri", "store", "1")); phones.add(new Xphone("小米", "xphone", "xiaoai", "store", "2")); pojo2String.setPhoneList(phones); //将对象转化为Json System.out.println(mapper.writeValueAsString(pojo2String)); //将Json转化为对象 String message = "{\"pojoId\":123,\"pojoName\":456,\"pojoPw\":0,\"list\":null,\"phoneList\":[{\"name\":\"手机\",\"type\":\"phone\"},{\"name\":\"苹果\",\"type\":\"iphone\",\"siRi\":\"siri\",\"appStore\":\"store\",\"id\":\"1\"},{\"name\":\"小米\",\"type\":\"xphone\",\"id\":\"2\",\"xiaomiStore\":\"store\",\"xiaoai\":\"xiaoai\"}]}"; /*在有继承关系时可以在父类中添加注解,根据字段的内容生成对应的子类对象,详见注解*/ Pojo String2Pojo = mapper.readValue(message, Pojo.class); System.out.println(String2Pojo.getPhoneList());
注解:
@JsonIgnoreProperties
这是一个类注解,可以标记多个属性让Jackson忽略。
@JsonIgnoreProperties({ "id" })
@JsonIgnoreProperties(ignoreUnknown = true)
public class BeanWithIgnore { public int id; public String name; }
•参数ignoreUnknown
为true
时, Json字符串如果有未知的属性名, 则不会抛出异常
@JsonIgnore
该注解用于属性级别, 用于标明一个属性可以被Jackson忽略
public class BeanWithIgnore { @JsonIgnore public int id; public String name; }
@JsonIgnoreType
必须作用于类, 标明以该类为类型的属性都会被Jackson忽略(必须是属性,例如LIst中的泛型为该类则不会被忽略)
public class User { public int id; public Name name; @JsonIgnoreType public static class Name { public String firstName; public String lastName; } }
@JsonInclude(JsonInclude.Include.NON_NULL)
作用于类上,被标注后字段为null时则不会输出该字段
@JsonInclude(Include.NON_NULL) public class MyBean { public int id; public String name; }
@JsonProperty("HQ")
@JsonProperty("HQ") //java属性headquarters序列化到json字段的名称为HQ
private String headquarters;
@JsonTypeInfo和@JsonSubTypes
俩注解搭配使用可以解决反序列化中多态问题,即根据Json字符串中某个属性的值生成对应的子类
比如:{"className":"bean.Phone","name":"手机","type":"phone"},
{"className":"bean.Iphone","name":"苹果","type":"iphone","siRi":"siri","appStore":"store","id":"1"},
{"className":"bean.Xphone","name":"小米","type":"xphone","id":"2","xiaomiStore":"store","xiaoai":"xiaoai"}
解析时会根据id=1生成Iphone类,id=2生成Xphone类,默认Phone。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "id", visible = true, defaultImpl = Phone.class) @JsonSubTypes({@JsonSubTypes.Type(name = "1", value = Iphone.class), @Type(name = "2", value = Xphone.class) }) public class Phone {}
九、Mina框架
角色:服务器端、客户端
主要代码:
服务器端:
public class ClientReceived { public static void main(String[] args) { try { //建立对象 NioSocketAcceptor acceptor = new NioSocketAcceptor(); //设置过滤器,添加解析 acceptor.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new MyCodecFactory(new MyEncode(), new MyDecode()))); // acceptor.getSessionConfig().setReadBufferSize(2048); // acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); //设置Handler acceptor.setHandler(new MyIoHandler()); //绑定端口 acceptor.bind(new InetSocketAddress(7317)); System.out.println("程序启动,等待链接"); } catch (Exception e) { e.printStackTrace(); } } }
客户端:
public class ClientWriter { public static void main(String[] args) throws InterruptedException { // 创建客户端连接 IoConnector connector = new NioSocketConnector(); // 增加编码过滤器,统一编码UTF-8 connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new PrefixedStringCodecFactory(Charset.forName("UTF-8")))); // 设置客户端逻辑处理器 connector.setHandler(new MyIoHandler()); // 连接 ConnectFuture connectFuture = connector.connect(new InetSocketAddress("10.1.112.134", 7317)); // 等待建立连接 connectFuture.awaitUninterruptibly(30000); // 获取连接会话 IoSession session = connectFuture.getSession(); System.out.println("客户端内容输入:"); String[] messages = "12 34 68 96".split(" "); byte[] reply = new byte[messages.length]; for (int i = 0; i < messages.length; i++) { reply[i] = (byte) ((Integer.parseInt(messages[i])) & 0xFF); } AbstractIoBuffer buffer = (AbstractIoBuffer) AbstractIoBuffer.allocate(reply.length, false); buffer.put(reply); buffer.flip(); while (true) { Thread.sleep(5000); session.write(buffer); } } }
Handler:
public class MyIoHandler extends IoHandlerAdapter { @Override public void sessionCreated(IoSession session) throws Exception { super.sessionCreated(session); } @Override public void sessionOpened(IoSession session) throws Exception { System.out.println("成功连接到" + session.getId()); super.sessionOpened(session); } @Override public void sessionClosed(IoSession session) throws Exception { System.out.println("链接关闭" + session.getId()); super.sessionClosed(session); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { super.sessionIdle(session, status); } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { System.out.println("异常捕获"); cause.printStackTrace(); super.exceptionCaught(session, cause); } @Override public void messageReceived(IoSession session, Object message) throws Exception { byte[] buffer = (byte[]) message; System.out.print("接收到message"); for (byte b : buffer) { Integer integer = Integer.valueOf(b); String value = integer.toHexString(integer).replace("ffffff", ""); if (value.length() == 1) { value = "0" + value; } System.out.print(value + " "); } byte[] bytes = {02, 03}; session.write(bytes); System.out.print("\n不解密"); for(byte b : buffer){ System.out.print(b+" "); } System.out.println(); } @Override public void messageSent(IoSession session, Object message) throws Exception { System.out.println("发送消息"); super.messageSent(session, message); } }
加密解密工厂:
public class MyCodecFactory implements ProtocolCodecFactory { private ProtocolEncoder encoder; private ProtocolDecoder decoder; public MyCodecFactory(ProtocolEncoder encoder, ProtocolDecoder decoder) { this.encoder = encoder; this.decoder = decoder; } @Override public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception { return encoder; } @Override public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception { return decoder; } }
加密:
public class MyEncode extends ProtocolEncoderAdapter { @Override public void encode(IoSession ioSession, Object message, ProtocolEncoderOutput out) throws Exception { byte[] reply = (byte[]) message; AbstractIoBuffer buffer = (AbstractIoBuffer) AbstractIoBuffer.allocate( reply.length, false); buffer.put(reply); buffer.flip(); out.write(buffer); } }
解密:
public class MyDecode extends CumulativeProtocolDecoder { @Override protected boolean doDecode(IoSession ioSession, IoBuffer in, ProtocolDecoderOutput out) { int recvLen = in.remaining(); if (recvLen < 1) return false; byte[] buffer = new byte[recvLen]; in.get(buffer); out.write(buffer); return false; } }
A、一致性哈希算法+哈希槽分区算法
一致性哈希算法
优点:增减节点时无需对所有的数据再算一遍哈希散落,只需要对新增节点的下个节点上的数据计算即可
缺点:数据倾斜
该方法适用于多节点映射,A始终映射1,B始终映射2,当A节点离线后1寻找其他节点映射。
public class NodeReal extends Thread { private static int virNodesNum = 150; //key:node value:hash private Map<String, List<Integer>> realNodeToVirNodesHashCode = new ConcurrentHashMap<>(); //key:hash value:node private SortedMap<Integer, String> ring = new TreeMap<>(); public synchronized void addNode(String node) { if (realNodeToVirNodesHashCode.containsKey(node)) { return; } // 创建虚拟节点,并放置到环上去 // 虚拟节点的hashcode 放入到 realNodeToVirNodesHashCode 中 int hash; List<Integer> hashCodes = new ArrayList<>(); for (int i = 0; i < virNodesNum; i++) { hash = rehash(node + "---" + i); ring.putIfAbsent(hash, node); hashCodes.add(hash); } realNodeToVirNodesHashCode.put(node, hashCodes); } public String getNode(String key) { int hash = rehash(key); // 从环上选择一个顺时针方向 // (升序方向--或者降序方向都可,这里升序)的 // 第一个虚拟节点作为目标节点返回 SortedMap<Integer, String> tailMap = ring.tailMap(hash); // tailMap中的所有key值>=hash return tailMap.isEmpty() ? ring.get(ring.firstKey()) : tailMap.get(tailMap.firstKey()); } private static int rehash(Object o) { int hash = o.hashCode(); hash *= 16777619;// 32 bit FNV_prime = 2^24 + 2^8 + 0x93 = 16777619 return Math.abs(hash ^ (hash >>> 16)); } }
使用:
public static void main(String[] args) { NodeReal nodeReal = new NodeReal(); int lang = 25; String[] terminals = new String[lang]; for (int i = 1; i <= lang; i++) { terminals[i - 1] = "1000010" + i; } nodeReal.addNode("fcu_1"); nodeReal.addNode("fcu_2"); nodeReal.addNode("fcu_3"); nodeReal.addNode("fcu_4"); nodeReal.addNode("fcu_5"); LinkedHashMap<String, String> relaMapTmp = new LinkedHashMap<>(); for (int i = 0; i < terminals.length; i++) { String terminalId = terminals[i]; String node = nodeReal.getNode(terminalId); relaMapTmp.put(terminalId, node); } relaMapTmp.forEach((k, v) -> { System.out.println("termianId:" + k + ",fcu:" + v); }); }
哈希槽分区算法
将适用的算法获取hash值,将hash值取余散列到对应的槽位上,而所有的节点会平均分配槽位。
当增加或者删除节点时,会将槽位分配到其他节点 或者 将其他节点槽位分配给新增的节点,保证所有所持槽位被平均分配。
同时槽点上的数据会随之迁移到对应节点上去
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class HashSlotPartitioner { private final int numSlots; private final Map<Integer, String> slotToNodeMap; private final List<String> nodes; public HashSlotPartitioner(int numSlots) { this.numSlots = numSlots; this.slotToNodeMap = new HashMap<>(); this.nodes = new ArrayList<>(); } public void addNode(String nodeName) { nodes.add(nodeName); reassignSlots(); } public void removeNode(String nodeName) { nodes.remove(nodeName); reassignSlots(); } public void reassignSlots() { slotToNodeMap.clear(); int slotsPerNode = numSlots / nodes.size(); for (int i = 0; i < nodes.size(); i++) { String node = nodes.get(i); for (int slotIndex = i * slotsPerNode; slotIndex < (i + 1) * slotsPerNode; slotIndex++) { String slotKey = "slot-" + slotIndex; slotToNodeMap.put(slotKey.hashCode(), node); } } } public String getNodeForKey(String key) { int slotIndex = key.hashCode() % numSlots; String slotKey = "slot-" + slotIndex; return slotToNodeMap.get(slotKey.hashCode()); } }
B、RMI
1、建立接口继承Remote类
public interface UserService extends Remote { User findUser(String userId) throws RemoteException; }
2、实现接口,重写方法
public class UserServiceImpl extends UnicastRemoteObject implements UserService { protected UserServiceImpl() throws RemoteException { } @Override public User findUser(String userId) throws RemoteException { return new User("1","wgy"); } public User findUser2(String userId) throws RemoteException { return new User(userId,"wgy"); } }
3、服务器端
public class RmiServer { public static void main(String[] args) { try { UserService userService = new UserServiceImpl(); LocateRegistry.createRegistry(1900); Naming.rebind("//localhost:1900/user", userService); Naming.rebind("//localhost:1900/user1", userService); Naming.rebind("//localhost:1900/user2", userService); System.out.println("start server,port is 1900"); } catch (Exception e) { e.printStackTrace(); } } }
4、客户端
public class RmiClient { public static void main(String args[]) { User answer; String userId = "00001"; try { UserService access = (UserService) Naming.lookup("rmi://localhost:1900/user2"); answer = access.findUser(userId); System.out.println("query:" + userId); System.out.println("result:" + answer.toString()); } catch (Exception ae) { System.out.println(ae); } } }
C、布隆过滤器
依赖:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency>
代码:
public class BloomTest { /** * 预计插入的数据 */ private static Integer expectedInsertions = 100; /** * 误判率 */ private static Double fpp = 0.9; /** * 布隆过滤器 */ private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), expectedInsertions, fpp); public static void main(String[] args) { // 插入 数据 for (int i = 0; i < expectedInsertions; i++) { bloomFilter.put(i); } //测试误判率 int count = 0; for (int i = expectedInsertions; i < expectedInsertions * 3; i++) { if (bloomFilter.mightContain(i)) { count++; } } System.out.println("count:" + count); } }
注:布隆过滤器判断不存在一定不存在,判断存在可能不存在
D、Nacos
依赖:
<dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>1.1.1</version> </dependency>
代码:
public class MyNacos { public static void main(String[] args) throws Exception { Properties properties = new Properties(); properties.put("serverAddr","127.0.0.1:8848"); ConfigService configService = NacosFactory.createConfigService(properties); String content = configService.getConfig("kafka","myGroup",2000); System.out.println(content); } }