65 Commits
2.2.0 ... 2.3.0

Author SHA1 Message Date
Redkale
1917ccf35c Redkale 2.3.0 结束 2021-04-09 00:28:57 +08:00
Redkale
4f9c14da97 2021-01-20 19:25:29 +08:00
Redkale
58e23c1b8c 2021-01-20 19:13:38 +08:00
Redkale
34156fc092 2021-01-20 18:52:11 +08:00
Redkale
86fde56129 Convert增加convertMapToBytes方法 2021-01-20 18:15:25 +08:00
Redkale
d997d3a7bb 增加ConvertSmallString功能 2021-01-20 17:37:29 +08:00
Redkale
4e689855f4 2021-01-20 13:20:03 +08:00
Redkale
eace16d7fd 2021-01-20 13:11:20 +08:00
Redkale
53b7f9b26f 2021-01-20 11:45:56 +08:00
Redkale
983c667725 2021-01-20 11:35:51 +08:00
Redkale
96fd660d46 2021-01-20 10:14:52 +08:00
Redkale
7d1770da8a 2021-01-19 17:40:08 +08:00
Redkale
b83f6867f3 2021-01-19 17:00:28 +08:00
Redkale
fa7db42f50 ObjectPool关闭public构造函数 2021-01-19 16:45:00 +08:00
Redkale
80c07a5c5b 2021-01-19 15:44:31 +08:00
Redkale
59a4c85aeb 2021-01-19 15:39:53 +08:00
Redkale
7fc01d38e8 2021-01-18 17:29:26 +08:00
Redkale
3010422f4e 2021-01-16 19:27:52 +08:00
Redkale
1f78bb1a98 2021-01-16 19:25:18 +08:00
Redkale
1d7f32efe4 2021-01-16 18:26:08 +08:00
Redkale
1e3d980437 2021-01-15 15:07:30 +08:00
Redkale
8654b5eef8 2021-01-14 13:37:45 +08:00
Redkale
5781ad1de0 2021-01-14 12:25:23 +08:00
Redkale
e2a1fa15d3 增加CacheClusterAgent功能 2021-01-14 12:01:25 +08:00
Redkale
50da7413d8 2021-01-13 09:24:35 +08:00
Redkale
e45b945024 2021-01-13 09:22:55 +08:00
Redkale
f3668a77ef 2021-01-12 21:39:56 +08:00
Redkale
2aee6cab20 2021-01-12 20:54:23 +08:00
Redkale
6233a0b960 2021-01-12 20:34:39 +08:00
Redkale
0ae469d8e2 2021-01-12 18:38:10 +08:00
Redkale
bcad47ebd5 修复HttpSimpleRequest的path编码bug 2021-01-12 17:59:33 +08:00
Redkale
71d179b8c3 2021-01-12 17:53:13 +08:00
Redkale
af35e5100e 2021-01-12 10:23:28 +08:00
Redkale
96c97c7dd2 2021-01-12 09:53:33 +08:00
Redkale
d5a41fc8b6 2021-01-10 14:59:00 +08:00
Redkale
c3c3afb3c3 2021-01-10 13:10:09 +08:00
Redkale
4f32dcda16 2021-01-10 10:33:48 +08:00
Redkale
67717e73ad 2021-01-10 10:19:05 +08:00
Redkale
1753734581 2021-01-10 09:05:47 +08:00
Redkale
3e408e535c 2021-01-09 15:41:35 +08:00
Redkale
f154292cb5 2021-01-09 15:32:59 +08:00
Redkale
ac261533c3 2021-01-09 14:53:12 +08:00
Redkale
1982c984ee 2021-01-09 14:42:45 +08:00
Redkale
fb5f82c44f 2021-01-09 14:01:41 +08:00
Redkale
840cea06db 2021-01-06 15:07:31 +08:00
Redkale
3237e2488e 2021-01-03 14:02:26 +08:00
Redkale
98e1e40e18 2021-01-03 14:00:54 +08:00
Redkale
def3fc7b2e 2021-01-03 12:04:51 +08:00
Redkale
6057b8f90a 2020-12-26 00:48:05 +08:00
Redkale
2847f761bb 修改MessageRecord的seqid生成规则 2020-12-26 00:07:49 +08:00
Redkale
b0a2d2ad80 2020-12-25 22:37:48 +08:00
Redkale
564b814f77 2020-12-25 20:51:18 +08:00
Redkale
433585a231 2020-12-25 20:33:50 +08:00
Redkale
6a4750e302 2020-12-25 20:30:47 +08:00
Redkale
748a6f11c1 2020-12-25 20:05:10 +08:00
Redkale
bdd9a82682 mq加入一些debug日志 2020-12-25 18:03:19 +08:00
Redkale
c551c157d1 @Local @AutoLoad(false) Service 能自动加载 2020-12-19 19:39:51 +08:00
Redkale
7e777229ba 2020-12-15 21:55:32 +08:00
Redkale
612bfcba37 WebSocket兼容connection:upgrade 小写的upgrade 2020-12-15 17:04:33 +08:00
Redkale
c44e00a100 2020-12-14 13:57:57 +08:00
Redkale
7f46b3194a Application 删除 restoreConfig 方法 2020-12-14 13:56:20 +08:00
Redkale
50b7999dee Application 增加 reloadConfig 方法 2020-12-14 13:55:19 +08:00
Redkale
871f84b1ff Redkale 2.3.0 开始 2020-12-14 13:49:47 +08:00
Redkale
4018a95af0 2020-12-13 12:17:00 +08:00
Redkale
ffe3ff5830 2020-12-13 01:08:09 +08:00
179 changed files with 10742 additions and 5365 deletions

View File

@@ -4,5 +4,5 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
java -server -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application

View File

@@ -24,5 +24,5 @@ done
export CLASSPATH=$CLASSPATH:$lib
echo "$APP_HOME"
nohup java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/logs.out &
nohup java -server -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/logs.out &

View File

@@ -71,6 +71,7 @@
<!--
MQ管理接口配置
不同MQ节点所配置的MQ集群不能重复。
MQ跟着协议走所以mq的属性值需要被赋值在rest节点上, 由于SncpServlet是自动生成的故SNCP协议下mq属性值被赋值在service/services节点上
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
value 实现类名必须是org.redkale.mq.MessageAgent的子类
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
@@ -199,6 +200,7 @@
<service value="com.xxx.XXX1Service"/>
<!--
name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。
mq: 所属的MQ管理器当 protocol == SNCP 时该值才有效, 存在该属性表示Service的SNCP协议采用消息总线代理模式
groups: 显式指定groups覆盖<services>节点的groups默认值。
ignore: 是否禁用, 默认为false。
-->

View File

@@ -51,4 +51,12 @@ public @interface Cacheable {
* @return int
*/
int interval() default 0;
/**
* DataSource是否直接返回对象的真实引用 而不是copy一份
*
* @return boolean
*/
boolean direct() default false;
}

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/**
* A visitor to visit a Java annotation. The methods of this class must be
* called in the following order: ( <code>visit</code> | <code>visitEnum</code> |
* <code>visitAnnotation</code> | <code>visitArray</code> )* <code>visitEnd</code>.
* called in the following order: ( &#60;tt&#62;visit&#60;/tt&#62; | &#60;tt&#62;visitEnum&#60;/tt&#62; |
* &#60;tt&#62;visitAnnotation&#60;/tt&#62; | &#60;tt&#62;visitArray&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
@@ -102,6 +102,9 @@ public abstract class AnnotationVisitor {
* method calls. May be null.
*/
public AnnotationVisitor(final int api, final AnnotationVisitor av) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.av = av;
}
@@ -151,7 +154,7 @@ public abstract class AnnotationVisitor {
* @param desc
* the class descriptor of the nested annotation class.
* @return a visitor to visit the actual nested annotation value, or
* <code>null</code> if this visitor is not interested in visiting this
* &#60;tt&#62;null&#60;/tt&#62; if this visitor is not interested in visiting this
* nested annotation. <i>The nested annotation value must be fully
* visited before calling other methods on this annotation
* visitor</i>.
@@ -172,7 +175,7 @@ public abstract class AnnotationVisitor {
* @param name
* the value name.
* @return a visitor to visit the actual array value elements, or
* <code>null</code> if this visitor is not interested in visiting these
* &#60;tt&#62;null&#60;/tt&#62; if this visitor is not interested in visiting these
* values. The 'name' parameters passed to the methods of this
* visitor are ignored. <i>All the array values must be visited
* before calling other methods on this annotation visitor</i>.

View File

@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private int size;
/**
* <code>true<code> if values are named, <code>false</code> otherwise. Annotation
* &#60;tt&#62;true&#60;tt&#62; if values are named, &#60;tt&#62;false&#60;/tt&#62; otherwise. Annotation
* writers used for annotation default and annotation arrays use unnamed
* values.
*/
@@ -122,13 +122,13 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param cw
* the class writer to which this annotation must be added.
* @param named
* <code>true<code> if values are named, <code>false</code> otherwise.
* &#60;tt&#62;true&#60;tt&#62; if values are named, &#60;tt&#62;false&#60;/tt&#62; otherwise.
* @param bv
* where the annotation values must be stored.
* @param parent
* where the number of annotation values must be stored.
* @param offset
* where in <code>parent</code> the number of annotation values must
* where in &#60;tt&#62;parent&#60;/tt&#62; the number of annotation values must
* be stored.
*/
AnnotationWriter(final ClassWriter cw, final boolean named,
@@ -354,7 +354,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param out
* where the type reference and type path must be put.
*/

View File

@@ -58,13 +58,15 @@
*/
package org.redkale.asm;
import java.util.Arrays;
/**
* A non standard class, field, method or code attribute.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
class Attribute {
public class Attribute {
/**
* The type of this attribute.
@@ -77,7 +79,7 @@ class Attribute {
byte[] value;
/**
* The next attribute in this attribute list. May be <code>null</code>.
* The next attribute in this attribute list. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
Attribute next;
@@ -92,19 +94,19 @@ class Attribute {
}
/**
* Returns <code>true</code> if this type of attribute is unknown. The default
* implementation of this method always returns <code>true</code>.
* Returns &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is unknown. The default
* implementation of this method always returns &#60;tt&#62;true&#60;/tt&#62;.
*
* @return <code>true</code> if this type of attribute is unknown.
* @return &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is unknown.
*/
public boolean isUnknown() {
return true;
}
/**
* Returns <code>true</code> if this type of attribute is a code attribute.
* Returns &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is a code attribute.
*
* @return <code>true</code> if this type of attribute is a code attribute.
* @return &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is a code attribute.
*/
public boolean isCodeAttribute() {
return false;
@@ -113,7 +115,7 @@ class Attribute {
/**
* Returns the labels corresponding to this attribute.
*
* @return the labels corresponding to this attribute, or <code>null</code> if
* @return the labels corresponding to this attribute, or &#60;tt&#62;null&#60;/tt&#62; if
* this attribute is not a code attribute that contains labels.
*/
protected Label[] getLabels() {
@@ -123,7 +125,7 @@ class Attribute {
/**
* Reads a {@link #type type} attribute. This method must return a
* <i>new</i> {@link Attribute} object, of type {@link #type type},
* corresponding to the <code>len</code> bytes starting at the given offset, in
* corresponding to the &#60;tt&#62;len&#60;/tt&#62; bytes starting at the given offset, in
* the given class reader.
*
* @param cr
@@ -146,7 +148,7 @@ class Attribute {
* containing the type and the length of the attribute, are not
* taken into account here.
* @param labels
* the labels of the method's code, or <code>null</code> if the
* the labels of the method's code, or &#60;tt&#62;null&#60;/tt&#62; if the
* attribute to be read is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the given
* bytes.
@@ -169,11 +171,11 @@ class Attribute {
* class the items that corresponds to this attribute.
* @param code
* the bytecode of the method corresponding to this code
* attribute, or <code>null</code> if this attribute is not a code
* attribute, or &#60;tt&#62;null&#60;/tt&#62; if this attribute is not a code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to this
* code attribute, or <code>null</code> if this attribute is not a
* code attribute, or &#60;tt&#62;null&#60;/tt&#62; if this attribute is not a
* code attribute.
* @param maxStack
* the maximum stack size of the method corresponding to this
@@ -216,11 +218,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <code>null</code> if these attributes are not code
* attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <code>null</code> if these attributes
* these code attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these
@@ -254,11 +256,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <code>null</code> if these attributes are not code
* attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <code>null</code> if these attributes
* these code attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these
@@ -281,4 +283,72 @@ class Attribute {
attr = attr.next;
}
}
//The stuff below is temporary - once proper support for nestmate attribute has been added, it can be safely removed.
//see also changes in ClassReader.accept.
public static class NestMembers extends Attribute {
public NestMembers() {
super("NestMembers");
}
byte[] bytes;
String[] classes;
@Override
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
int offset = off;
NestMembers a = new NestMembers();
int size = cr.readShort(off);
a.classes = new String[size];
off += 2;
for (int i = 0; i < size ; i++) {
a.classes[i] = cr.readClass(off, buf);
off += 2;
}
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
return a;
}
@Override
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
ByteVector v = new ByteVector(bytes.length);
v.putShort(classes.length);
for (String s : classes) {
v.putShort(cw.newClass(s));
}
return v;
}
}
public static class NestHost extends Attribute {
byte[] bytes;
String clazz;
public NestHost() {
super("NestHost");
}
@Override
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
int offset = off;
NestHost a = new NestHost();
a.clazz = cr.readClass(off, buf);
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
return a;
}
@Override
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
ByteVector v = new ByteVector(bytes.length);
v.putShort(cw.newClass(clazz));
return v;
}
}
static final Attribute[] DEFAULT_ATTRIBUTE_PROTOS = new Attribute[] {
new NestMembers(),
new NestHost()
};
}

View File

@@ -332,7 +332,7 @@ public class ByteVector {
* automatically enlarged if necessary.
*
* @param b
* an array of bytes. May be <code>null</code> to put <code>len</code>
* an array of bytes. May be &#60;tt&#62;null&#60;/tt&#62; to put &#60;tt&#62;len&#60;/tt&#62;
* null bytes into this byte vector.
* @param off
* index of the fist byte of b that must be copied.

View File

@@ -185,9 +185,9 @@ public class ClassReader {
public ClassReader(final byte[] b, final int off, final int len) {
this.b = b;
// checks the class version
if (readShort(off + 6) > Opcodes.V10) {
//if (readShort(off + 6) > Opcodes.V11) {
// throw new IllegalArgumentException();
}
//}
// parses the constant pool
items = new int[readUnsignedShort(off + 8)];
int n = items.length;
@@ -205,6 +205,10 @@ public class ClassReader {
case ClassWriter.FLOAT:
case ClassWriter.NAME_TYPE:
case ClassWriter.INDY:
// @@@ ClassWriter.CONDY
// Enables MethodHandles.lookup().defineClass to function correctly
// when it reads the class name
case 17:
size = 5;
break;
case ClassWriter.LONG:
@@ -267,7 +271,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}.
*
* @return the internal name of super class, or <code>null</code> for
* @return the internal name of super class, or &#60;tt&#62;null&#60;/tt&#62; for
* {@link Object} class.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
@@ -281,7 +285,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}).
*
* @return the array of internal names for all implemented interfaces or
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
*/
@@ -526,7 +530,7 @@ public class ClassReader {
* , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
*/
public void accept(final ClassVisitor classVisitor, final int flags) {
accept(classVisitor, new Attribute[0], flags);
accept(classVisitor, Attribute.DEFAULT_ATTRIBUTE_PROTOS, flags);
}
/**
@@ -1932,7 +1936,7 @@ public class ClassReader {
* @param v
* start offset in {@link #b b} of the annotations to be read.
* @param visible
* <code>true</code> if the annotations to be read are visible at
* &#60;tt&#62;true&#60;/tt&#62; if the annotations to be read are visible at
* runtime.
*/
private void readParameterAnnotations(final MethodVisitor mv,
@@ -2474,9 +2478,9 @@ public class ClassReader {
* and the length of the attribute, are not taken into account
* here.
* @param labels
* the labels of the method's code, or <code>null</code> if the
* the labels of the method's code, or &#60;tt&#62;null&#60;/tt&#62; if the
* attribute to be read is not a code attribute.
* @return the attribute that has been read, or <code>null</code> to skip this
* @return the attribute that has been read, or &#60;tt&#62;null&#60;/tt&#62; to skip this
* attribute.
*/
private Attribute readAttribute(final Attribute[] attrs, final String type,

View File

@@ -60,11 +60,11 @@ package org.redkale.asm;
/**
* A visitor to visit a Java class. The methods of this class must be called in
* the following order: <code>visit</code> [ <code>visitSource</code> ] [
* <code>visitModule</code> ][ <code>visitOuterClass</code> ] ( <code>visitAnnotation</code> |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* (
* <code>visitInnerClass</code> | <code>visitField</code> | <code>visitMethod</code> )*
* <code>visitEnd</code>.
* the following order: &#60;tt&#62;visit&#60;/tt&#62; [ &#60;tt&#62;visitSource&#60;/tt&#62; ] [
* &#60;tt&#62;visitModule&#60;/tt&#62; ][ &#60;tt&#62;visitOuterClass&#60;/tt&#62; ] ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* (
* &#60;tt&#62;visitInnerClass&#60;/tt&#62; | &#60;tt&#62;visitField&#60;/tt&#62; | &#60;tt&#62;visitMethod&#60;/tt&#62; )*
* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* @author Eric Bruneton
*/
@@ -104,6 +104,9 @@ public abstract class ClassVisitor {
* calls. May be null.
*/
public ClassVisitor(final int api, final ClassVisitor cv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.cv = cv;
}
@@ -120,18 +123,18 @@ public abstract class ClassVisitor {
* the internal name of the class (see
* {@link Type#getInternalName() getInternalName}).
* @param signature
* the signature of this class. May be <code>null</code> if the class
* the signature of this class. May be &#60;tt&#62;null&#60;/tt&#62; if the class
* is not a generic one, and does not extend or implement generic
* classes or interfaces.
* @param superName
* the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be
* <code>null</code>, but only for the {@link Object} class.
* &#60;tt&#62;null&#60;/tt&#62;, but only for the {@link Object} class.
* @param interfaces
* the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
@@ -145,11 +148,11 @@ public abstract class ClassVisitor {
*
* @param source
* the name of the source file from which the class was compiled.
* May be <code>null</code>.
* May be &#60;tt&#62;null&#60;/tt&#62;.
* @param debug
* additional debug information to compute the correspondance
* between source and compiled elements of the class. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visitSource(String source, String debug) {
if (cv != null) {
@@ -166,7 +169,7 @@ public abstract class ClassVisitor {
* and {@code ACC_MANDATED}.
* @param version
* module version or null.
* @return a visitor to visit the module values, or <code>null</code> if
* @return a visitor to visit the module values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this module.
*/
public ModuleVisitor visitModule(String name, int access, String version) {
@@ -187,11 +190,11 @@ public abstract class ClassVisitor {
* internal name of the enclosing class of the class.
* @param name
* the name of the method that contains the class, or
* <code>null</code> if the class is not enclosed in a method of its
* &#60;tt&#62;null&#60;/tt&#62; if the class is not enclosed in a method of its
* enclosing class.
* @param desc
* the descriptor of the method that contains the class, or
* <code>null</code> if the class is not enclosed in a method of its
* &#60;tt&#62;null&#60;/tt&#62; if the class is not enclosed in a method of its
* enclosing class.
*/
public void visitOuterClass(String owner, String name, String desc) {
@@ -206,8 +209,8 @@ public abstract class ClassVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -231,16 +234,19 @@ public abstract class ClassVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
@@ -269,10 +275,10 @@ public abstract class ClassVisitor {
* @param outerName
* the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}).
* May be <code>null</code> for not member classes.
* May be &#60;tt&#62;null&#60;/tt&#62; for not member classes.
* @param innerName
* the (simple) name of the inner class inside its enclosing
* class. May be <code>null</code> for anonymous inner classes.
* class. May be &#60;tt&#62;null&#60;/tt&#62; for anonymous inner classes.
* @param access
* the access flags of the inner class as originally declared in
* the enclosing class.
@@ -295,20 +301,20 @@ public abstract class ClassVisitor {
* @param desc
* the field's descriptor (see {@link Type Type}).
* @param signature
* the field's signature. May be <code>null</code> if the field's
* the field's signature. May be &#60;tt&#62;null&#60;/tt&#62; if the field's
* type does not use generic types.
* @param value
* the field's initial value. This parameter, which may be
* <code>null</code> if the field does not have an initial value,
* &#60;tt&#62;null&#60;/tt&#62; if the field does not have an initial value,
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <code>int</code>,
* <code>float</code>, <code>long</code> or <code>String</code> fields
* {@link Double} or a {@link String} (for &#60;tt&#62;int&#60;/tt&#62;,
* &#60;tt&#62;float&#60;/tt&#62;, &#60;tt&#62;long&#60;/tt&#62; or &#60;tt&#62;String&#60;/tt&#62; fields
* respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in
* constructors or methods.
* @return a visitor to visit field annotations and attributes, or
* <code>null</code> if this class visitor is not interested in visiting
* &#60;tt&#62;null&#60;/tt&#62; if this class visitor is not interested in visiting
* these annotations and attributes.
*/
public FieldVisitor visitField(int access, String name, String desc,
@@ -321,7 +327,7 @@ public abstract class ClassVisitor {
/**
* Visits a method of the class. This method <i>must</i> return a new
* {@link MethodVisitor} instance (or <code>null</code>) each time it is called,
* {@link MethodVisitor} instance (or &#60;tt&#62;null&#60;/tt&#62;) each time it is called,
* i.e., it should not return a previously returned visitor.
*
* @param access
@@ -333,14 +339,14 @@ public abstract class ClassVisitor {
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param signature
* the method's signature. May be <code>null</code> if the method
* the method's signature. May be &#60;tt&#62;null&#60;/tt&#62; if the method
* parameters, return type and exceptions do not use generic
* types.
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* <code>null</code>.
* @return an object to visit the byte code of the method, or <code>null</code>
* &#60;tt&#62;null&#60;/tt&#62;.
* @return an object to visit the byte code of the method, or &#60;tt&#62;null&#60;/tt&#62;
* if this class visitor is not interested in visiting the code of
* this method.
*/

View File

@@ -391,8 +391,8 @@ public class ClassWriter extends ClassVisitor {
* A type table used to temporarily store internal names that will not
* necessarily be stored in the constant pool. This type table is used by
* the control flow and data flow analysis algorithm used to compute stack
* map frames from scratch. This array associates to each index <code>i</code>
* the Item whose index is <code>i</code>. All Item objects stored in this array
* map frames from scratch. This array associates to each index &#60;tt&#62;i&#60;/tt&#62;
* the Item whose index is &#60;tt&#62;i&#60;/tt&#62;. All Item objects stored in this array
* are also stored in the {@link #items} hash table. These two arrays allow
* to retrieve an Item from its index or, conversely, to get the index of an
* Item from its value. Each Item stores an internal name in its
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
private int compute;
/**
* <code>true</code> if some methods have wide forward jumps using ASM pseudo
* &#60;tt&#62;true&#60;/tt&#62; if some methods have wide forward jumps using ASM pseudo
* instructions, which need to be expanded into sequences of standard
* bytecode instructions. In this case the class is re-read and re-written
* with a ClassReader -> ClassWriter chain to perform this transformation.
@@ -1297,6 +1297,38 @@ public class ClassWriter extends ClassVisitor {
return result;
}
/**
* Adds a handle to the constant pool of the class being build. Does nothing
* if the constant pool already contains a similar item. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by
* class generators or adapters.</i>
*
* @param tag
* the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
* {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
* {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the field or method owner class.
* @param name
* the name of the field or method.
* @param desc
* the descriptor of the field or method.
* @return the index of a new or already existing method type reference
* item.
*
* @deprecated this method is superseded by
* {@link #newHandle(int, String, String, String, boolean)}.
*/
@Deprecated
public int newHandle(final int tag, final String owner, final String name,
final String desc) {
return newHandle(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
}
/**
* Adds a handle to the constant pool of the class being build. Does nothing
* if the constant pool already contains a similar item. <i>This method is
@@ -1486,7 +1518,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc
* the method's descriptor.
* @param itf
* <code>true</code> if <code>owner</code> is an interface.
* &#60;tt&#62;true&#60;/tt&#62; if &#60;tt&#62;owner&#60;/tt&#62; is an interface.
* @return a new or already existing method reference item.
*/
Item newMethodItem(final String owner, final String name,
@@ -1515,7 +1547,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc
* the method's descriptor.
* @param itf
* <code>true</code> if <code>owner</code> is an interface.
* &#60;tt&#62;true&#60;/tt&#62; if &#60;tt&#62;owner&#60;/tt&#62; is an interface.
* @return the index of a new or already existing method reference item.
*/
public int newMethod(final String owner, final String name,
@@ -1778,7 +1810,7 @@ public class ClassWriter extends ClassVisitor {
* @param key
* a constant pool item.
* @return the constant pool's hash table item which is equal to the given
* item, or <code>null</code> if there is no such item.
* item, or &#60;tt&#62;null&#60;/tt&#62; if there is no such item.
*/
private Item get(final Item key) {
Item i = items[key.hashCode % items.length];

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/**
* A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( <code>visitAnnotation</code> |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* <code>visitEnd</code>.
* the following order: ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* @author Eric Bruneton
*/
@@ -101,6 +101,9 @@ public abstract class FieldVisitor {
* calls. May be null.
*/
public FieldVisitor(final int api, final FieldVisitor fv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.fv = fv;
}
@@ -111,8 +114,8 @@ public abstract class FieldVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -132,16 +135,19 @@ public abstract class FieldVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (fv != null) {
return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}

View File

@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value;
/**
* The runtime visible annotations of this field. May be <code>null</code>.
* The runtime visible annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this field. May be <code>null</code>.
* The runtime invisible annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ianns;
/**
* The runtime visible type annotations of this field. May be <code>null</code>.
* The runtime visible type annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter tanns;
/**
* The runtime invisible type annotations of this field. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter itanns;
/**
* The non standard attributes of this field. May be <code>null</code>.
* The non standard attributes of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private Attribute attrs;
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
* @param desc
* the field's descriptor (see {@link Type}).
* @param signature
* the field's signature. May be <code>null</code>.
* the field's signature. May be &#60;tt&#62;null&#60;/tt&#62;.
* @param value
* the field's constant value. May be <code>null</code>.
* the field's constant value. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) {

View File

@@ -1403,7 +1403,7 @@ class Frame {
/**
* Merges the input frame of the given basic block with the input and output
* frames of this basic block. Returns <code>true</code> if the input frame of
* frames of this basic block. Returns &#60;tt&#62;true&#60;/tt&#62; if the input frame of
* the given label has been changed by this operation.
*
* @param cw
@@ -1413,7 +1413,7 @@ class Frame {
* @param edge
* the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}.
* @return <code>true</code> if the input frame of the given label has been
* @return &#60;tt&#62;true&#60;/tt&#62; if the input frame of the given label has been
* changed by this operation.
*/
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
@@ -1511,7 +1511,7 @@ class Frame {
/**
* Merges the type at the given index in the given type array with the given
* type. Returns <code>true</code> if the type array has been modified by this
* type. Returns &#60;tt&#62;true&#60;/tt&#62; if the type array has been modified by this
* operation.
*
* @param cw
@@ -1522,7 +1522,7 @@ class Frame {
* an array of types.
* @param index
* the index of the type that must be merged in 'types'.
* @return <code>true</code> if the type array has been modified by this
* @return &#60;tt&#62;true&#60;/tt&#62; if the type array has been modified by this
* operation.
*/
private static boolean merge(final ClassWriter cw, int t,

View File

@@ -99,6 +99,34 @@ public final class Handle {
*/
final boolean itf;
/**
* Constructs a new field or method handle.
*
* @param tag
* the kind of field or method designated by this Handle. Must be
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
* {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the class that owns the field or method
* designated by this handle.
* @param name
* the name of the field or method designated by this handle.
* @param desc
* the descriptor of the field or method designated by this
* handle.
*
* @deprecated this constructor has been superseded
* by {@link #Handle(int, String, String, String, boolean)}.
*/
@Deprecated
public Handle(int tag, String owner, String name, String desc) {
this(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
}
/**
* Constructs a new field or method handle.

View File

@@ -82,7 +82,7 @@ class Handler {
/**
* Internal name of the type of exceptions handled by this handler, or
* <code>null</code> to catch any exceptions.
* &#60;tt&#62;null&#60;/tt&#62; to catch any exceptions.
*/
String desc;

View File

@@ -306,8 +306,8 @@ final class Item {
* @param i
* the item to be compared to this one. Both items must have the
* same {@link #type}.
* @return <code>true</code> if the given item if equal to this one,
* <code>false</code> otherwise.
* @return &#60;tt&#62;true&#60;/tt&#62; if the given item if equal to this one,
* &#60;tt&#62;false&#60;/tt&#62; otherwise.
*/
boolean isEqualTo(final Item i) {
switch (type) {

View File

@@ -325,8 +325,8 @@ public class Label {
* the position of first byte of the bytecode instruction that
* contains this label.
* @param wideOffset
* <code>true</code> if the reference must be stored in 4 bytes, or
* <code>false</code> if it must be stored with 2 bytes.
* &#60;tt&#62;true&#60;/tt&#62; if the reference must be stored in 4 bytes, or
* &#60;tt&#62;false&#60;/tt&#62; if it must be stored with 2 bytes.
* @throws IllegalArgumentException
* if this label has not been created by the given code writer.
*/
@@ -389,7 +389,7 @@ public class Label {
* the position of this label in the bytecode.
* @param data
* the bytecode of the method.
* @return <code>true</code> if a blank that was left for this label was too
* @return &#60;tt&#62;true&#60;/tt&#62; if a blank that was left for this label was too
* small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo

View File

@@ -60,24 +60,24 @@ package org.redkale.asm;
/**
* A visitor to visit a Java method. The methods of this class must be called in
* the following order: ( <code>visitParameter</code> )* [
* <code>visitAnnotationDefault</code> ] ( <code>visitAnnotation</code> |
* <code>visitParameterAnnotation</code> <code>visitTypeAnnotation</code> |
* <code>visitAttribute</code> )* [ <code>visitCode</code> ( <code>visitFrame</code> |
* <code>visit<i>X</i>Insn</code> | <code>visitLabel</code> |
* <code>visitInsnAnnotation</code> | <code>visitTryCatchBlock</code> |
* <code>visitTryCatchAnnotation</code> | <code>visitLocalVariable</code> |
* <code>visitLocalVariableAnnotation</code> | <code>visitLineNumber</code> )*
* <code>visitMaxs</code> ] <code>visitEnd</code>. In addition, the
* <code>visit<i>X</i>Insn</code> and <code>visitLabel</code> methods must be called in
* the following order: ( &#60;tt&#62;visitParameter&#60;/tt&#62; )* [
* &#60;tt&#62;visitAnnotationDefault&#60;/tt&#62; ] ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitParameterAnnotation&#60;/tt&#62; &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitAttribute&#60;/tt&#62; )* [ &#60;tt&#62;visitCode&#60;/tt&#62; ( &#60;tt&#62;visitFrame&#60;/tt&#62; |
* &#60;tt&#62;visit<i>X</i>Insn&#60;/tt&#62; | &#60;tt&#62;visitLabel&#60;/tt&#62; |
* &#60;tt&#62;visitInsnAnnotation&#60;/tt&#62; | &#60;tt&#62;visitTryCatchBlock&#60;/tt&#62; |
* &#60;tt&#62;visitTryCatchAnnotation&#60;/tt&#62; | &#60;tt&#62;visitLocalVariable&#60;/tt&#62; |
* &#60;tt&#62;visitLocalVariableAnnotation&#60;/tt&#62; | &#60;tt&#62;visitLineNumber&#60;/tt&#62; )*
* &#60;tt&#62;visitMaxs&#60;/tt&#62; ] &#60;tt&#62;visitEnd&#60;/tt&#62;. In addition, the
* &#60;tt&#62;visit<i>X</i>Insn&#60;/tt&#62; and &#60;tt&#62;visitLabel&#60;/tt&#62; methods must be called in
* the sequential order of the bytecode instructions of the visited code,
* <code>visitInsnAnnotation</code> must be called <i>after</i> the annotated
* instruction, <code>visitTryCatchBlock</code> must be called <i>before</i> the
* &#60;tt&#62;visitInsnAnnotation&#60;/tt&#62; must be called <i>after</i> the annotated
* instruction, &#60;tt&#62;visitTryCatchBlock&#60;/tt&#62; must be called <i>before</i> the
* labels passed as arguments have been visited,
* <code>visitTryCatchBlockAnnotation</code> must be called <i>after</i> the
* &#60;tt&#62;visitTryCatchBlockAnnotation&#60;/tt&#62; must be called <i>after</i> the
* corresponding try catch block has been visited, and the
* <code>visitLocalVariable</code>, <code>visitLocalVariableAnnotation</code> and
* <code>visitLineNumber</code> methods must be called <i>after</i> the labels
* &#60;tt&#62;visitLocalVariable&#60;/tt&#62;, &#60;tt&#62;visitLocalVariableAnnotation&#60;/tt&#62; and
* &#60;tt&#62;visitLineNumber&#60;/tt&#62; methods must be called <i>after</i> the labels
* passed as arguments have been visited.
*
* @author Eric Bruneton
@@ -118,6 +118,9 @@ public abstract class MethodVisitor {
* calls. May be null.
*/
public MethodVisitor(final int api, final MethodVisitor mv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.mv = mv;
}
@@ -132,11 +135,14 @@ public abstract class MethodVisitor {
* @param name
* parameter name or null if none is provided.
* @param access
* the parameter's access flags, only <code>ACC_FINAL</code>,
* <code>ACC_SYNTHETIC</code> or/and <code>ACC_MANDATED</code> are
* the parameter's access flags, only &#60;tt&#62;ACC_FINAL&#60;/tt&#62;,
* &#60;tt&#62;ACC_SYNTHETIC&#60;/tt&#62; or/and &#60;tt&#62;ACC_MANDATED&#60;/tt&#62; are
* allowed (see {@link Opcodes}).
*/
public void visitParameter(String name, int access) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
mv.visitParameter(name, access);
}
@@ -146,7 +152,7 @@ public abstract class MethodVisitor {
* Visits the default value of this annotation interface method.
*
* @return a visitor to the visit the actual default value of this
* annotation interface method, or <code>null</code> if this visitor is
* annotation interface method, or &#60;tt&#62;null&#60;/tt&#62; if this visitor is
* not interested in visiting this default value. The 'name'
* parameters passed to the methods of this annotation visitor are
* ignored. Moreover, exacly one visit method must be called on this
@@ -165,8 +171,8 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -193,16 +199,19 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
@@ -217,8 +226,8 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitParameterAnnotation(int parameter,
@@ -444,6 +453,34 @@ public abstract class MethodVisitor {
}
}
/**
* Visits a method instruction. A method instruction is an instruction that
* invokes a method.
*
* @param opcode
* the opcode of the type instruction to be visited. This opcode
* is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
* INVOKEINTERFACE.
* @param owner
* the internal name of the method's owner class (see
* {@link Type#getInternalName() getInternalName}).
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type Type}).
*/
@Deprecated
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
if (api >= Opcodes.ASM5) {
boolean itf = opcode == Opcodes.INVOKEINTERFACE;
visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc);
}
}
/**
* Visits a method instruction. A method instruction is an instruction that
@@ -465,6 +502,14 @@ public abstract class MethodVisitor {
*/
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
if (api < Opcodes.ASM5) {
if (itf != (opcode == Opcodes.INVOKEINTERFACE)) {
throw new IllegalArgumentException(
"INVOKESPECIAL/STATIC on interfaces require ASM 5");
}
visitMethodInsn(opcode, owner, name, desc);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
@@ -569,7 +614,7 @@ public abstract class MethodVisitor {
* the constant to be loaded on the stack. This parameter must be
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or
* ARRAY sort for <code>.class</code> constants, for classes whose
* ARRAY sort for &#60;tt&#62;.class&#60;/tt&#62; constants, for classes whose
* version is 49.0, a {@link Type} of METHOD sort or a
* {@link Handle} for MethodType and MethodHandle constants, for
* classes whose version is 51.0.
@@ -604,8 +649,8 @@ public abstract class MethodVisitor {
* @param dflt
* beginning of the default handler block.
* @param labels
* beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <code>min + i</code> key.
* beginnings of the handler blocks. &#60;tt&#62;labels[i]&#60;/tt&#62; is the
* beginning of the handler block for the &#60;tt&#62;min + i&#60;/tt&#62; key.
*/
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label... labels) {
@@ -622,8 +667,8 @@ public abstract class MethodVisitor {
* @param keys
* the values of the keys.
* @param labels
* beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <code>keys[i]</code> key.
* beginnings of the handler blocks. &#60;tt&#62;labels[i]&#60;/tt&#62; is the
* beginning of the handler block for the &#60;tt&#62;keys[i]&#60;/tt&#62; key.
*/
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (mv != null) {
@@ -668,16 +713,19 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitInsnAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
}
@@ -699,7 +747,7 @@ public abstract class MethodVisitor {
* beginning of the exception handler's code.
* @param type
* internal name of the type of exceptions handled by the
* handler, or <code>null</code> to catch any exceptions (for
* handler, or &#60;tt&#62;null&#60;/tt&#62; to catch any exceptions (for
* "finally" blocks).
* @throws IllegalArgumentException
* if one of the labels has already been visited by this visitor
@@ -725,16 +773,19 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
}
@@ -750,7 +801,7 @@ public abstract class MethodVisitor {
* the type descriptor of this local variable.
* @param signature
* the type signature of this local variable. May be
* <code>null</code> if the local variable type does not use generic
* &#60;tt&#62;null&#60;/tt&#62; if the local variable type does not use generic
* types.
* @param start
* the first instruction corresponding to the scope of this local
@@ -782,7 +833,7 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param start
* the fist instructions corresponding to the continuous ranges
* that make the scope of this local variable (inclusive).
@@ -796,13 +847,16 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
TypePath typePath, Label[] start, Label[] end, int[] index,
String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitLocalVariableAnnotation(typeRef, typePath, start,
end, index, desc, visible);
@@ -819,7 +873,7 @@ public abstract class MethodVisitor {
* @param start
* the first instruction corresponding to this line number.
* @throws IllegalArgumentException
* if <code>start</code> has not already been visited by this
* if &#60;tt&#62;start&#60;/tt&#62; has not already been visited by this
* visitor (by the {@link #visitLabel visitLabel} method).
*/
public void visitLineNumber(int line, Label start) {

View File

@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions;
/**
* The annotation default attribute of this method. May be <code>null</code>.
* The annotation default attribute of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private ByteVector annd;
/**
* The runtime visible annotations of this method. May be <code>null</code>.
* The runtime visible annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this method. May be <code>null</code>.
* The runtime invisible annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ianns;
/**
* The runtime visible type annotations of this method. May be <code>null</code>
* The runtime visible type annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;
* .
*/
private AnnotationWriter tanns;
/**
* The runtime invisible type annotations of this method. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter itanns;
/**
* The runtime visible parameter annotations of this method. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter[] panns;
/**
* The runtime invisible parameter annotations of this method. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter[] ipanns;
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
private int lastCodeOffset;
/**
* The runtime visible type annotations of the code. May be <code>null</code>.
* The runtime visible type annotations of the code. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ctanns;
/**
* The runtime invisible type annotations of the code. May be <code>null</code>.
* The runtime invisible type annotations of the code. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ictanns;
@@ -446,7 +446,7 @@ class MethodWriter extends MethodVisitor {
* is relative to the beginning of the current basic block, i.e., the true
* stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <code>stackSize</code>.
* plus &#60;tt&#62;stackSize&#60;/tt&#62;.
*/
private int stackSize;
@@ -455,7 +455,7 @@ class MethodWriter extends MethodVisitor {
* This size is relative to the beginning of the current basic block, i.e.,
* the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic
* block plus <code>stackSize</code>.
* block plus &#60;tt&#62;stackSize&#60;/tt&#62;.
*/
private int maxStackSize;
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
* @param desc
* the method's descriptor (see {@link Type}).
* @param signature
* the method's signature. May be <code>null</code>.
* the method's signature. May be &#60;tt&#62;null&#60;/tt&#62;.
* @param exceptions
* the internal names of the method's exceptions. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
* @param compute
* Indicates what must be automatically computed (see #compute).
*/

View File

@@ -60,9 +60,9 @@ package org.redkale.asm;
/**
* A visitor to visit a Java module. The methods of this class must be called in
* the following order: <code>visitMainClass</code> | ( <code>visitPackage</code> |
* <code>visitRequire</code> | <code>visitExport</code> | <code>visitOpen</code> |
* <code>visitUse</code> | <code>visitProvide</code> )* <code>visitEnd</code>.
* the following order: &#60;tt&#62;visitMainClass&#60;/tt&#62; | ( &#60;tt&#62;visitPackage&#60;/tt&#62; |
* &#60;tt&#62;visitRequire&#60;/tt&#62; | &#60;tt&#62;visitExport&#60;/tt&#62; | &#60;tt&#62;visitOpen&#60;/tt&#62; |
* &#60;tt&#62;visitUse&#60;/tt&#62; | &#60;tt&#62;visitProvide&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can access to
* the public classes of the exported package or
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visitExport(String packaze, int access, String... modules) {
if (mv != null) {
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can use deep
* reflection to the classes of the open package or
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) {

View File

@@ -80,12 +80,17 @@ public interface Opcodes {
// versions
int V1_1 = 3 << 16 | 45;
int V1_2 = 0 << 16 | 46;
int V1_3 = 0 << 16 | 47;
int V1_4 = 0 << 16 | 48;
int V1_5 = 0 << 16 | 49;
int V1_6 = 0 << 16 | 50;
int V1_7 = 0 << 16 | 51;
int V1_8 = 0 << 16 | 52;
int V9 = 0 << 16 | 53;
int V10 = 0 << 16 | 54;
int V11 = 0 << 16 | 55;
// access flags

View File

@@ -71,47 +71,47 @@ import java.lang.reflect.Method;
public class Type {
/**
* The sort of the <code>void</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;void&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int VOID = 0;
/**
* The sort of the <code>boolean</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;boolean&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int BOOLEAN = 1;
/**
* The sort of the <code>char</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;char&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int CHAR = 2;
/**
* The sort of the <code>byte</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;byte&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int BYTE = 3;
/**
* The sort of the <code>short</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;short&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int SHORT = 4;
/**
* The sort of the <code>int</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;int&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int INT = 5;
/**
* The sort of the <code>float</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;float&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int FLOAT = 6;
/**
* The sort of the <code>long</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;long&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int LONG = 7;
/**
* The sort of the <code>double</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;double&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int DOUBLE = 8;
@@ -131,55 +131,55 @@ public class Type {
public static final int METHOD = 11;
/**
* The <code>void</code> type.
* The &#60;tt&#62;void&#60;/tt&#62; type.
*/
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
| (5 << 16) | (0 << 8) | 0, 1);
/**
* The <code>boolean</code> type.
* The &#60;tt&#62;boolean&#60;/tt&#62; type.
*/
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <code>char</code> type.
* The &#60;tt&#62;char&#60;/tt&#62; type.
*/
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
| (0 << 16) | (6 << 8) | 1, 1);
/**
* The <code>byte</code> type.
* The &#60;tt&#62;byte&#60;/tt&#62; type.
*/
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <code>short</code> type.
* The &#60;tt&#62;short&#60;/tt&#62; type.
*/
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
| (0 << 16) | (7 << 8) | 1, 1);
/**
* The <code>int</code> type.
* The &#60;tt&#62;int&#60;/tt&#62; type.
*/
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
| (0 << 16) | (0 << 8) | 1, 1);
/**
* The <code>float</code> type.
* The &#60;tt&#62;float&#60;/tt&#62; type.
*/
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
| (2 << 16) | (2 << 8) | 1, 1);
/**
* The <code>long</code> type.
* The &#60;tt&#62;long&#60;/tt&#62; type.
*/
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
| (1 << 16) | (1 << 8) | 2, 1);
/**
* The <code>double</code> type.
* The &#60;tt&#62;double&#60;/tt&#62; type.
*/
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
| (3 << 16) | (3 << 8) | 2, 1);
@@ -439,8 +439,8 @@ public class Type {
* @return the size of the arguments of the method (plus one for the
* implicit this argument), argSize, and the size of its return
* value, retSize, packed into a single int i =
* <code>(argSize &lt;&lt; 2) | retSize</code> (argSize is therefore equal to
* <code>i &gt;&gt; 2</code>, and retSize to <code>i &amp; 0x03</code>).
* &#60;tt&#62;(argSize &lt;&lt; 2) | retSize&#60;/tt&#62; (argSize is therefore equal to
* &#60;tt&#62;i &gt;&gt; 2&#60;/tt&#62;, and retSize to &#60;tt&#62;i &amp; 0x03&#60;/tt&#62;).
*/
public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1;
@@ -645,9 +645,9 @@ public class Type {
* @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize,
* packed into a single
* int i = <code>(argSize &lt;&lt; 2) | retSize</code>
* (argSize is therefore equal to <code>i &gt;&gt; 2</code>,
* and retSize to <code>i &amp; 0x03</code>).
* int i = &#60;tt&#62;(argSize &lt;&lt; 2) | retSize&#60;/tt&#62;
* (argSize is therefore equal to &#60;tt&#62;i &gt;&gt; 2&#60;/tt&#62;,
* and retSize to &#60;tt&#62;i &amp; 0x03&#60;/tt&#62;).
*/
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
@@ -838,8 +838,8 @@ public class Type {
* Returns the size of values of this type. This method must not be used for
* method types.
*
* @return the size of values of this type, i.e., 2 for <code>long</code> and
* <code>double</code>, 0 for <code>void</code> and 1 otherwise.
* @return the size of values of this type, i.e., 2 for &#60;tt&#62;long&#60;/tt&#62; and
* &#60;tt&#62;double&#60;/tt&#62;, 0 for &#60;tt&#62;void&#60;/tt&#62; and 1 otherwise.
*/
public int getSize() {
// the size is in byte 0 of 'off' for primitive types (buf == null)
@@ -855,8 +855,8 @@ public class Type {
* ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
* ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
* @return an opcode that is similar to the given opcode, but adapted to
* this Java type. For example, if this type is <code>float</code> and
* <code>opcode</code> is IRETURN, this method returns FRETURN.
* this Java type. For example, if this type is &#60;tt&#62;float&#60;/tt&#62; and
* &#60;tt&#62;opcode&#60;/tt&#62; is IRETURN, this method returns FRETURN.
*/
public int getOpcode(final int opcode) {
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
@@ -879,7 +879,7 @@ public class Type {
*
* @param o
* the object to be compared to this type.
* @return <code>true</code> if the given object is equal to this type.
* @return &#60;tt&#62;true&#60;/tt&#62; if the given object is equal to this type.
*/
@Override
public boolean equals(final Object o) {

View File

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.cluster.*;
import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.*;
@@ -88,6 +89,20 @@ public final class Application {
*/
public static final String RESNAME_APP_ADDR = "APP_ADDR";
/**
* 当前进程的work线程池 类型Executor、ExecutorService
*
* @since 2.3.0
*/
public static final String RESNAME_APP_EXECUTOR = "APP_EXECUTOR";
/**
* 当前进程的客户端组, 类型AsyncGroup
*
* @since 2.3.0
*/
public static final String RESNAME_APP_GROUP = "APP_GROUP";
/**
* 当前Service所属的SNCP Server的地址 类型: SocketAddress、InetSocketAddress、String <br>
*/
@@ -105,8 +120,11 @@ public final class Application {
/**
* 当前Server的线程池
*
* @deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
*/
public static final String RESNAME_SERVER_EXECUTOR = Server.RESNAME_SERVER_EXECUTOR;
@Deprecated
public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2;
/**
* 当前Server的ResourceFactory
@@ -122,6 +140,10 @@ public final class Application {
//本地IP地址
final InetSocketAddress localAddress;
//业务逻辑线程池
//@since 2.3.0
final ExecutorService workExecutor;
//CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -134,6 +156,9 @@ public final class Application {
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用
final TransportFactory sncpTransportFactory;
//给客户端使用包含SNCP客户端、自定义数据库客户端连接池
final AsyncGroup asyncGroup;
//第三方服务发现管理接口
//@since 2.1.0
final ClusterAgent clusterAgent;
@@ -288,9 +313,6 @@ public final class Application {
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------");
//------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null;
AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
String excludelib0 = null;
@@ -300,9 +322,9 @@ public final class Application {
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
AnyValue executorConf = null;
if (resources != null) {
executorConf = resources.getAnyValue("executor");
AnyValue excludelibConf = resources.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = resources.getAnyValue("transport");
@@ -315,31 +337,6 @@ public final class Application {
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
final int capacity = bufferCapacity;
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
e.clear();
return true;
});
//-----------transportChannelGroup--------------
try {
final String strategyClass = transportConf.getValue("strategy");
if (strategyClass != null && !strategyClass.isEmpty()) {
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).getDeclaredConstructor().newInstance();
}
final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t;
});
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
}
AnyValue clusterConf = resources.getAnyValue("cluster");
@@ -356,6 +353,13 @@ public final class Application {
break;
}
}
if (cluster == null) {
ClusterAgent cacheClusterAgent = new CacheClusterAgent();
if (cacheClusterAgent.match(clusterConf)) {
cluster = cacheClusterAgent;
cluster.setConfig(clusterConf);
}
}
if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='value' value error: " + clusterConf);
} else {
Class type = classLoader.loadClass(clusterConf.getValue("value"));
@@ -416,31 +420,41 @@ public final class Application {
}
}
}
if (transportGroup == null) {
final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 8, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
ExecutorService workExecutor0 = null;
if (executorConf != null) {
final AtomicReference<ExecutorService> workref = new AtomicReference<>();
int executorThreads = executorConf.getIntValue("threads", Math.max(2, Runtime.getRuntime().availableProcessors()));
boolean executorHash = executorConf.getBoolValue("hash");
if (executorThreads > 0) {
final AtomicInteger workcounter = new AtomicInteger();
if (executorHash) {
workExecutor0 = new ThreadHashExecutor(executorThreads, (Runnable r) -> {
int c = workcounter.incrementAndGet();
String threadname = "Redkale-HashWorkThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, workref.get(), r);
return t;
});
try {
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (transportPool == null) {
final int capacity = bufferCapacity;
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
e.clear();
return true;
} else {
workExecutor0 = Executors.newFixedThreadPool(executorThreads, (Runnable r) -> {
int c = workcounter.incrementAndGet();
String threadname = "Redkale-WorkThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, workref.get(), r);
return t;
});
}
workref.set(workExecutor0);
}
}
this.workExecutor = workExecutor0;
this.resourceFactory.register(RESNAME_APP_EXECUTOR, Executor.class, this.workExecutor);
this.resourceFactory.register(RESNAME_APP_EXECUTOR, ExecutorService.class, this.workExecutor);
this.asyncGroup = new AsyncIOGroup(this.workExecutor, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize);
this.resourceFactory.register(RESNAME_APP_GROUP, AsyncGroup.class, this.asyncGroup);
this.excludelibs = excludelib0;
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
this.sncpTransportFactory = TransportFactory.create(this.asyncGroup, (SSLContext) null, Transport.DEFAULT_NETPROTOCOL, readTimeoutSeconds, writeTimeoutSeconds, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.pool.maxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30"));
@@ -462,6 +476,14 @@ public final class Application {
return name;
}
public ExecutorService getWorkExecutor() {
return workExecutor;
}
public AsyncGroup getAsyncGroup() {
return asyncGroup;
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
@@ -669,10 +691,30 @@ public final class Application {
}
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//------------------------------------- 注册 HttpClient --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
HttpClient httpClient = HttpClient.create(asyncGroup);
field.set(src, httpClient);
rf.inject(httpClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpClient.class, httpClient);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpClient inject error", e);
}
}, HttpClient.class);
//--------------------------------------------------------------------------
if (this.asyncGroup != null) {
((AsyncIOGroup) this.asyncGroup).start();
}
if (this.clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing");
long s = System.currentTimeMillis();
if (this.clusterAgent instanceof CacheClusterAgent) {
String sourceName = ((CacheClusterAgent) clusterAgent).getSourceName(); //必须在inject前调用需要赋值Resourcable.name
loadCacheSource(sourceName);
}
clusterAgent.setTransportFactory(this.sncpTransportFactory);
this.resourceFactory.inject(clusterAgent);
clusterAgent.init(clusterAgent.getConfig());
@@ -692,7 +734,7 @@ public final class Application {
logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms");
}
//------------------------------------- 注册 DataSource --------------------------------------------------------
//------------------------------------- 注册 HttpMessageClient --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
@@ -702,12 +744,44 @@ public final class Application {
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject error", e);
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpMessageClient inject error", e);
}
}, HttpMessageClient.class);
initResources();
}
private void loadCacheSource(final String sourceName) {
final AnyValue resources = config.getAnyValue("resources");
for (AnyValue sourceConf : resources.getAnyValues("source")) {
if (!sourceName.equals(sourceConf.getValue("name"))) continue;
String classval = sourceConf.getValue("value");
try {
Class sourceType = CacheMemorySource.class;
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
sourceType = s.getClass();
break;
}
}
} else {
sourceType = serverClassLoader.loadClass(classval);
}
CacheSource source = Modifier.isFinal(sourceType.getModifiers()) ? (CacheSource) sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, sourceName, sourceType, null, resourceFactory, sncpTransportFactory, null, null, sourceConf);
cacheSources.add((CacheSource) source);
resourceFactory.register(sourceName, CacheSource.class, source);
resourceFactory.inject(source);
if (source instanceof Service) ((Service) source).init(sourceConf);
logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + sourceName + ", source = " + source);
} catch (Exception e) {
logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e);
}
return;
}
}
private void initResources() throws Exception {
//-------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources");
@@ -715,7 +789,7 @@ public final class Application {
//------------------------------------------------------------------------
for (AnyValue conf : resources.getAnyValues("group")) {
final String group = conf.getValue("name", "");
final String protocol = conf.getValue("protocol", Transport.DEFAULT_PROTOCOL).toUpperCase();
final String protocol = conf.getValue("protocol", Transport.DEFAULT_NETPROTOCOL).toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
@@ -741,17 +815,6 @@ public final class Application {
//------------------------------------------------------------------------
}
public void restoreConfig() throws IOException {
if (!"file".equals(this.confPath.getScheme())) return;
synchronized (this) {
File confFile = new File(this.confPath.toString(), "application.xml");
confFile.renameTo(new File(this.confPath.toString(), "application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
final PrintStream ps = new PrintStream(new FileOutputStream(confFile));
ps.append(config.toXML("application"));
ps.close();
}
}
private void startSelfServer() throws Exception {
final Application application = this;
new Thread() {
@@ -971,8 +1034,7 @@ public final class Application {
for (final AnyValue serconf : serconfs) {
Thread thread = new Thread() {
{
String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "*");
setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase().replaceFirst("\\..+", "") + ":" + serconf.getIntValue("port") + "-Thread");
this.setDaemon(true);
}
@@ -1071,6 +1133,52 @@ public final class Application {
return new Application(singleton, loadAppXml());
}
public void reloadConfig() throws IOException {
AnyValue newconfig = loadAppXml();
final String confpath = this.confPath.toString();
final String homepath = this.home.getCanonicalPath();
final AnyValue resources = newconfig.getAnyValue("resources");
if (resources != null) {
resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources);
final AnyValue properties = resources.getAnyValue("properties");
if (properties != null) {
String dfloads = properties.getValue("load");
if (dfloads != null) {
for (String dfload : dfloads.split(";")) {
if (dfload.trim().isEmpty()) continue;
final URI df = (dfload.indexOf('/') < 0) ? URI.create(confpath + (confpath.endsWith("/") ? "" : "/") + dfload) : new File(dfload).toURI();
if (!"file".equals(df.getScheme()) || new File(df).isFile()) {
Properties ps = new Properties();
try {
InputStream in = df.toURL().openStream();
ps.load(in);
in.close();
ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath)));
} catch (Exception e) {
logger.log(Level.WARNING, "load properties(" + dfload + ") error", e);
}
}
}
}
for (AnyValue prop : properties.getAnyValues("property")) {
String name = prop.getValue("name");
String value = prop.getValue("value");
if (name == null || value == null) continue;
value = value.replace("${APP_HOME}", homepath);
if (name.startsWith("system.property.")) {
System.setProperty(name.substring("system.property.".length()), value);
} else if (name.startsWith("mimetype.property.")) {
MimeType.add(name.substring("mimetype.property.".length()), value);
} else if (name.startsWith("property.")) {
resourceFactory.register(name, value);
} else {
resourceFactory.register("property." + name, value);
}
}
}
}
}
private static AnyValue loadAppXml() throws IOException {
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home);

View File

@@ -46,7 +46,7 @@ public class NodeHttpServer extends NodeServer {
}
private static Server createServer(Application application, AnyValue serconf) {
return new HttpServer(application.getStartTime(), application.getResourceFactory().createChild());
return new HttpServer(application, application.getStartTime(), application.getResourceFactory().createChild());
}
public HttpServer getHttpServer() {

View File

@@ -19,7 +19,6 @@ import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import javax.annotation.*;
import javax.persistence.Transient;
import static org.redkale.boot.Application.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.net.Filter;
@@ -154,9 +153,10 @@ public abstract class NodeServer {
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf);
//init之后才有Executor
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor());
//废弃 @since 2.3.0
// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor());
// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor());
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", "");
@@ -271,6 +271,34 @@ public abstract class NodeServer {
}
}, AnyValue.class, AnyValue[].class);
//------------------------------------- 注册 Local AutoLoad(false) Service --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
Class<Service> resServiceType = Service.class;
try {
if (field.getAnnotation(Resource.class) == null) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 AutoLoad Service
if (!Service.class.isAssignableFrom(field.getType())) return;
resServiceType = (Class) field.getType();
if (resServiceType.getAnnotation(Local.class) == null) return;
AutoLoad al = resServiceType.getAnnotation(AutoLoad.class);
if (al == null || al.value()) return;
//ResourceFactory resfactory = (isSNCP() ? appResFactory : resourceFactory);
SncpClient client = src instanceof Service ? Sncp.getSncpClient((Service) src) : null;
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
Service service = Sncp.createLocalService(serverClassLoader, resourceName, resServiceType, null, appResFactory, appSncpTranFactory, sncpAddr, groups, null);
appResFactory.register(resourceName, resServiceType, service);
field.set(src, service);
rf.inject(service, self); // 给其可能包含@Resource的字段赋值;
service.init(null);
logger.info("[" + Thread.currentThread().getName() + "] Load Service(@Local @AutoLoad) resourceName = " + resourceName + ", service = " + service);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Load @Local @AutoLoad(false) Service inject " + resServiceType + " to " + src + " error", e);
}
}, Service.class);
//------------------------------------- 注册 DataSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
@@ -279,7 +307,6 @@ public abstract class NodeServer {
SimpleEntry<Class, AnyValue> resEntry = dataResources.get(resourceName);
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
DataSource source = null;
boolean needinit = true;
if (sourceConf != null) {
final Class sourceType = resEntry.getKey();
if (sourceType == DataJdbcSource.class) {
@@ -303,15 +330,15 @@ public abstract class NodeServer {
}
if (source == null) {
source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建
needinit = false;
}
application.dataSources.add(source);
appResFactory.register(resourceName, DataSource.class, source);
field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
rf.inject(source, self); // 给AsyncGroup和其他@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src);
if (source instanceof Service && needinit) ((Service) source).init(sourceConf);
if (source instanceof Service) ((Service) source).init(sourceConf);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e);
}
@@ -355,16 +382,11 @@ public abstract class NodeServer {
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
source = Modifier.isFinal(sourceType.getModifiers()) ? sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService));
Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[0];
if (CacheSource.class.isAssignableFrom(sourceType)) {
CacheSource cacheSource = (CacheSource) source;
cacheSource.initValueType(valType instanceof Class ? (Class) valType : Object.class);
cacheSource.initTransient(field.getAnnotation(Transient.class) != null); //必须在initValueType之后
}
application.cacheSources.add((CacheSource) source);
appResFactory.register(resourceName, genericType, source);
appResFactory.register(resourceName, CacheSource.class, source);
if (genericType != CacheSource.class) {
appResFactory.register(resourceName, genericType, source);
}
}
field.set(src, source);
rf.inject(source, self); //
@@ -375,7 +397,7 @@ public abstract class NodeServer {
sncpServer.getSncpServer().addSncpServlet((Service) source);
//logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source);
}
logger.info("[" + Thread.currentThread().getName() + "] Load Source " + source);
logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + resourceName + ", source = " + source);
} catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e);
}
@@ -750,7 +772,7 @@ public abstract class NodeServer {
public void start() throws IOException {
if (interceptor != null) interceptor.preStart(this);
server.start();
server.start(application);
postStartServer(localServices, remoteServices);
}

View File

@@ -46,7 +46,7 @@ public class NodeSncpServer extends NodeServer {
}
private static Server createServer(Application application, AnyValue serconf) {
return new SncpServer(application.getStartTime(), application.getResourceFactory().createChild());
return new SncpServer(application, application.getStartTime(), serconf, application.getResourceFactory().createChild());
}
@Override

View File

@@ -61,7 +61,7 @@ public class ServerWatchService extends AbstractWatchService {
final Server server = node.getServer();
InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport);
try {
server.changeAddress(newAddr);
server.changeAddress(application, newAddr);
} catch (IOException e) {
e.printStackTrace();
return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error");
@@ -72,7 +72,7 @@ public class ServerWatchService extends AbstractWatchService {
private Map<String, Object> formatToMap(NodeServer node) {
Server server = node.getServer();
Map<String, Object> rs = new LinkedHashMap<>();
String protocol = server.getProtocol();
String protocol = server.getNetprotocol();
if (node instanceof NodeSncpServer) {
protocol += "/SNCP";
} else if (node instanceof NodeWatchServer) {
@@ -86,7 +86,6 @@ public class ServerWatchService extends AbstractWatchService {
rs.put("name", server.getName());
rs.put("protocol", protocol);
rs.put("address", server.getSocketAddress());
rs.put("threads", server.getThreads());
rs.put("backlog", server.getBacklog());
rs.put("bufferCapacity", server.getBufferCapacity());
rs.put("bufferPoolSize", server.getBufferPoolSize());

View File

@@ -81,7 +81,7 @@ public class TransportWatchService extends AbstractWatchService {
break;
}
}
application.restoreConfig();
//application.restoreConfig();
}
return RetResult.success();
}
@@ -113,7 +113,7 @@ public class TransportWatchService extends AbstractWatchService {
break;
}
}
application.restoreConfig();
//application.restoreConfig();
}
return RetResult.success();
}

View File

@@ -0,0 +1,327 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.cluster;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import javax.annotation.Resource;
import org.redkale.boot.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.service.Service;
import org.redkale.source.CacheSource;
import org.redkale.util.*;
/**
* 使用CacheSource实现的第三方服务发现管理接口cluster
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public class CacheClusterAgent extends ClusterAgent implements Resourcable {
@Resource(name = "$")
private CacheSource source;
private String sourceName;
protected int ttls = 10; //定时检查的秒数
protected ScheduledThreadPoolExecutor scheduler;
//可能被HttpMessageClient用到的服务 key: servicename
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> httpAddressMap = new ConcurrentHashMap<>();
//可能被mqtp用到的服务 key: servicename
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> mqtpAddressMap = new ConcurrentHashMap<>();
@Override
public void init(AnyValue config) {
super.init(config);
this.sourceName = getSourceName();
AnyValue[] properties = config.getAnyValues("property");
for (AnyValue property : properties) {
if ("ttls".equalsIgnoreCase(property.getValue("name"))) {
this.ttls = Integer.parseInt(property.getValue("value", "").trim());
if (this.ttls < 5) this.ttls = 10;
}
}
}
@Override
public void destroy(AnyValue config) {
if (scheduler != null) scheduler.shutdownNow();
}
public String getSourceName() {
AnyValue[] properties = config.getAnyValues("property");
for (AnyValue property : properties) {
if ("source".equalsIgnoreCase(property.getValue("name"))
&& property.getValue("value") != null) {
this.sourceName = property.getValue("value");
return this.sourceName;
}
}
return null;
}
@Override
public String resourceName() {
return sourceName;
}
@Override //ServiceLoader时判断配置是否符合当前实现类
public boolean match(AnyValue config) {
if (config == null) return false;
AnyValue[] properties = config.getAnyValues("property");
if (properties == null || properties.length == 0) return false;
for (AnyValue property : properties) {
if ("source".equalsIgnoreCase(property.getValue("name"))
&& property.getValue("value") != null) return true;
}
return false;
}
@Override
public void start() {
if (this.scheduler == null) {
this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> {
final Thread t = new Thread(r, "Redkale-" + CacheClusterAgent.class.getSimpleName() + "-Task-Thread");
t.setDaemon(true);
return t;
});
this.scheduler.scheduleAtFixedRate(() -> {
try {
checkApplicationHealth();
checkHttpAddressHealth();
loadMqtpAddressHealth();
localEntrys.values().stream().filter(e -> !e.canceled).forEach(entry -> {
checkLocalHealth(entry);
});
remoteEntrys.values().stream().filter(entry -> "SNCP".equalsIgnoreCase(entry.protocol)).forEach(entry -> {
updateSncpTransport(entry);
});
} catch (Exception e) {
logger.log(Level.SEVERE, "scheduleAtFixedRate check error", e);
}
}, Math.max(2000, ttls * 1000), Math.max(2000, ttls * 1000), TimeUnit.MILLISECONDS);
}
}
protected void loadMqtpAddressHealth() {
List<String> keys = source.queryKeysStartsWith("cluster.mqtp:");
keys.forEach(servicename -> {
try {
this.mqtpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS));
} catch (Exception e) {
logger.log(Level.SEVERE, "loadMqtpAddressHealth check " + servicename + " error", e);
}
});
}
protected void checkHttpAddressHealth() {
try {
this.httpAddressMap.keySet().stream().forEach(servicename -> {
try {
this.httpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS));
} catch (Exception e) {
logger.log(Level.SEVERE, "checkHttpAddressHealth check " + servicename + " error", e);
}
});
} catch (Exception ex) {
logger.log(Level.SEVERE, "checkHttpAddressHealth check error", ex);
}
}
protected void checkLocalHealth(final ClusterEntry entry) {
AddressEntry newaddr = new AddressEntry();
newaddr.addr = entry.address;
newaddr.nodeid = this.nodeid;
newaddr.time = System.currentTimeMillis();
source.hset(entry.checkname, entry.checkid, AddressEntry.class, newaddr);
}
@Override //获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段
public CompletableFuture<Map<String, Collection<InetSocketAddress>>> queryMqtpAddress(String protocol, String module, String resname) {
final Map<String, Collection<InetSocketAddress>> rsmap = new ConcurrentHashMap<>();
final String servicenamprefix = generateHttpServiceName(protocol, module, null) + ":";
mqtpAddressMap.keySet().stream().filter(k -> k.startsWith(servicenamprefix))
.forEach(sn -> rsmap.put(sn.substring(servicenamprefix.length()), mqtpAddressMap.get(sn)));
return CompletableFuture.completedFuture(rsmap);
}
@Override //获取HTTP远程服务的可用ip列表
public CompletableFuture<Collection<InetSocketAddress>> queryHttpAddress(String protocol, String module, String resname) {
final String servicename = generateHttpServiceName(protocol, module, resname);
Collection<InetSocketAddress> rs = httpAddressMap.get(servicename);
if (rs != null) return CompletableFuture.completedFuture(rs);
return queryAddress(servicename).thenApply(t -> {
httpAddressMap.put(servicename, t);
return t;
});
}
@Override
protected CompletableFuture<Collection<InetSocketAddress>> queryAddress(final ClusterEntry entry) {
return queryAddress(entry.servicename);
}
private CompletableFuture<Collection<InetSocketAddress>> queryAddress(final String servicename) {
final CompletableFuture<Map<String, AddressEntry>> future = source.hmapAsync(servicename, AddressEntry.class, 0, 10000);
return future.thenApply(map -> {
final Set<InetSocketAddress> set = new HashSet<>();
map.forEach((n, v) -> {
if (v != null && (System.currentTimeMillis() - v.time) / 1000 < ttls) set.add(v.addr);
});
return set;
});
}
protected boolean isApplicationHealth() {
String servicename = generateApplicationServiceName();
String serviceid = generateApplicationServiceId();
AddressEntry entry = (AddressEntry) source.hget(servicename, serviceid, AddressEntry.class);
return entry != null && (System.currentTimeMillis() - entry.time) / 1000 < ttls;
}
protected void checkApplicationHealth() {
String checkname = generateApplicationServiceName();
String checkid = generateApplicationCheckId();
AddressEntry entry = new AddressEntry();
entry.addr = this.appAddress;
entry.nodeid = this.nodeid;
entry.time = System.currentTimeMillis();
source.hset(checkname, checkid, AddressEntry.class, entry);
}
@Override
public void register(Application application) {
if (isApplicationHealth()) throw new RuntimeException("application.nodeid=" + nodeid + " exists in cluster");
deregister(application);
String serviceid = generateApplicationServiceId();
String servicename = generateApplicationServiceName();
AddressEntry entry = new AddressEntry();
entry.addr = this.appAddress;
entry.nodeid = this.nodeid;
entry.time = System.currentTimeMillis();
source.hset(servicename, serviceid, AddressEntry.class, entry);
}
@Override
public void deregister(Application application) {
String servicename = generateApplicationServiceName();
source.remove(servicename);
}
@Override
protected ClusterEntry register(NodeServer ns, String protocol, Service service) {
deregister(ns, protocol, service, false);
//
ClusterEntry clusterEntry = new ClusterEntry(ns, protocol, service);
AddressEntry entry = new AddressEntry();
entry.addr = clusterEntry.address;
entry.nodeid = this.nodeid;
entry.time = System.currentTimeMillis();
source.hset(clusterEntry.servicename, clusterEntry.serviceid, AddressEntry.class, entry);
return clusterEntry;
}
@Override
protected void deregister(NodeServer ns, String protocol, Service service) {
deregister(ns, protocol, service, true);
}
protected void deregister(NodeServer ns, String protocol, Service service, boolean realcanceled) {
String servicename = generateServiceName(ns, protocol, service);
String serviceid = generateServiceId(ns, protocol, service);
ClusterEntry currEntry = null;
for (final ClusterEntry entry : localEntrys.values()) {
if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) {
currEntry = entry;
break;
}
}
if (currEntry == null) {
for (final ClusterEntry entry : remoteEntrys.values()) {
if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) {
currEntry = entry;
break;
}
}
}
source.hremove(servicename, serviceid);
if (realcanceled && currEntry != null) currEntry.canceled = true;
if (!"mqtp".equals(protocol) && currEntry != null && currEntry.submqtp) {
deregister(ns, "mqtp", service, realcanceled);
}
}
@Override
protected String generateApplicationServiceName() {
return "cluster." + super.generateApplicationServiceName();
}
@Override
protected String generateServiceName(NodeServer ns, String protocol, Service service) {
return "cluster." + super.generateServiceName(ns, protocol, service);
}
@Override
public String generateHttpServiceName(String protocol, String module, String resname) {
return "cluster." + super.generateHttpServiceName(protocol, module, resname);
}
@Override
protected String generateApplicationCheckName() {
return generateApplicationServiceName();
}
@Override
protected String generateApplicationCheckId() {
return generateApplicationServiceId();
}
@Override
protected String generateCheckName(NodeServer ns, String protocol, Service service) {
return generateServiceName(ns, protocol, service);
}
@Override
protected String generateCheckId(NodeServer ns, String protocol, Service service) {
return generateServiceId(ns, protocol, service);
}
public static class AddressEntry {
public InetSocketAddress addr;
public int nodeid;
public long time;
public AddressEntry() {
}
public AddressEntry refresh() {
this.time = System.currentTimeMillis();
return this;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
}

View File

@@ -107,15 +107,14 @@ public abstract class ClusterAgent {
//注册本地模式
for (Service service : localServices) {
if (!canRegister(protocol, service)) continue;
register(ns, protocol, service);
ClusterEntry htentry = new ClusterEntry(ns, protocol, service);
ClusterEntry htentry = register(ns, protocol, service);
localEntrys.put(htentry.serviceid, htentry);
if (protocol.toLowerCase().startsWith("http")) {
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
if (mmc != null) {
register(ns, "mqtp", service);
ClusterEntry mqentry = new ClusterEntry(ns, "mqtp", service);
ClusterEntry mqentry = register(ns, "mqtp", service);
localEntrys.put(mqentry.serviceid, mqentry);
htentry.submqtp = true;
}
}
}
@@ -141,6 +140,8 @@ public abstract class ClusterAgent {
protected boolean canRegister(String protocol, Service service) {
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false;
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return false;
if (service instanceof WebSocketNode) {
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
}
@@ -176,7 +177,7 @@ public abstract class ClusterAgent {
protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry);
//注册服务
protected abstract void register(NodeServer ns, String protocol, Service service);
protected abstract ClusterEntry register(NodeServer ns, String protocol, Service service);
//注销服务
protected abstract void deregister(NodeServer ns, String protocol, Service service);
@@ -313,16 +314,24 @@ public abstract class ClusterAgent {
public boolean canceled;
public boolean submqtp;
public ClusterEntry(NodeServer ns, String protocol, Service service) {
this.serviceid = generateServiceId(ns, protocol, service);
this.servicename = generateServiceName(ns, protocol, service);
this.checkid = generateCheckId(ns, protocol, service);
this.checkname = generateCheckName(ns, protocol, service);
this.protocol = protocol;
this.address = ns.getSocketAddress();
InetSocketAddress addr = ns.getSocketAddress();
String host = addr.getHostString();
if ("0.0.0.0".equals(host)) {
host = appAddress.getHostString();
addr = new InetSocketAddress(host, addr.getPort());
}
this.address = addr;
this.serviceref = new WeakReference(service);
Server server = ns.getServer();
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_PROTOCOL;
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL;
}
@Override

View File

@@ -29,6 +29,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
protected final Encodeable<Writer, Object> componentEncoder;
protected final boolean subtypefinal;
protected volatile boolean inited = false;
protected final Object lock = new Object();
@@ -47,6 +49,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
factory.register(type, this);
this.componentEncoder = factory.loadEncoder(this.componentType);
this.anyEncoder = factory.getAnyEncoder();
this.subtypefinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers());
} finally {
inited = true;
synchronized (lock) {
@@ -81,15 +84,27 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
}
}
}
if (out.writeArrayB(value.length, this, componentEncoder, value) < 0) {
Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
if (subtypefinal) {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
writeMemberValue(out, member, itemEncoder, v, first);
if (first) first = false;
}
}
} else {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
final Type comp = this.componentType;
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? componentEncoder : anyEncoder), v, first);
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, first);
if (first) first = false;
}
}
}
out.writeArrayE();
}

View File

@@ -32,5 +32,4 @@ public abstract class BinaryConvert<R extends Reader, W extends Writer> extends
public abstract byte[] convertTo(final Type type, final Object value);
public abstract byte[] convertMapTo(final Object... values);
}

View File

@@ -8,7 +8,7 @@ package org.redkale.convert;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.function.*;
import org.redkale.util.Attribute;
import org.redkale.util.*;
/**
* 序列化/反序列化操作类
@@ -61,10 +61,16 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public abstract byte[] convertToBytes(final Type type, final Object value);
public abstract void convertToBytes(final Object value, final ConvertBytesHandler handler);
public abstract void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler);
public abstract void convertToBytes(final ByteArray array, final Object value);
public abstract void convertToBytes(final ByteArray array, final Type type, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);
public abstract ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values);
}

View File

@@ -0,0 +1,23 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.util.function.Consumer;
/**
*
* convertToBytes系列的方法的回调
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public interface ConvertBytesHandler {
<A> void completed(byte[] bs, int offset, int length, Consumer<A> callback, A attachment);
}

View File

@@ -42,7 +42,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
protected Convert<R, W> convert;
protected boolean tiny;
protected boolean tiny; //String类型值为""Boolean类型值为false时是否需要输出 默认为true
private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this);
@@ -112,6 +112,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance);
this.register(File.class, FileSimpledCoder.instance);
this.register(Throwable.class, ThrowableSimpledCoder.instance);
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
this.register(URL.class, URLSimpledCoder.instance);
this.register(URI.class, URISimpledCoder.instance);
@@ -232,11 +233,15 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
return new EnumSimpledCoder(enumClass);
}
protected <E> Encodeable<W, E> createDyncEncoder(Type type) {
return null;
}
protected ObjectDecoder createObjectDecoder(Type type) {
return new ObjectDecoder(type);
}
protected ObjectEncoder createObjectEncoder(Type type) {
protected <E> ObjectEncoder<W, E> createObjectEncoder(Type type) {
return new ObjectEncoder(type);
}
@@ -810,8 +815,11 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
}
}
if (simpleCoder == null) {
encoder = createDyncEncoder(type);
if (encoder == null) {
oe = createObjectEncoder(type);
encoder = oe;
}
} else {
encoder = simpleCoder;
}

View File

@@ -0,0 +1,27 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 序列化时标记String字段的值是否为无转义字符且长度不超过255的字符串
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*
*/
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ConvertSmallString {
}

View File

@@ -31,6 +31,10 @@ public final class EnMember<W extends Writer, T, F> {
//final boolean isnumber;
final boolean bool;
final char[] jsonFieldNameChars;
final byte[] jsonFieldNameBytes;
protected int index;
protected int position; //从1开始
@@ -43,6 +47,8 @@ public final class EnMember<W extends Writer, T, F> {
Class t = attribute.type();
this.string = CharSequence.class.isAssignableFrom(t);
this.bool = t == Boolean.class || t == boolean.class;
this.jsonFieldNameChars = ('"' + attribute.field() + "\":").toCharArray();
this.jsonFieldNameBytes = ('"' + attribute.field() + "\":").getBytes();
//this.isnumber = Number.class.isAssignableFrom(t) || (!this.isbool && t.isPrimitive());
}
@@ -71,6 +77,14 @@ public final class EnMember<W extends Writer, T, F> {
return attribute;
}
public char[] getJsonFieldNameChars() {
return jsonFieldNameChars;
}
public byte[] getJsonFieldNameBytes() {
return jsonFieldNameBytes;
}
public Encodeable<W, F> getEncoder() {
return encoder;
}

View File

@@ -28,4 +28,9 @@ public interface Encodeable<W extends Writer, T> {
*/
public Type getType();
//是否需要检查Writer.specify
default boolean specifyable() {
return true;
}
}

View File

@@ -8,6 +8,7 @@ package org.redkale.convert;
import org.redkale.util.Creator;
import java.lang.reflect.*;
import java.util.*;
import org.redkale.convert.ext.StringSimpledCoder;
import org.redkale.util.*;
/**
@@ -96,7 +97,13 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(clazz, field);
if (ref != null && ref.ignore()) continue;
Decodeable<R, ?> fieldCoder = factory.findFieldCoder(clazz, field.getName());
ConvertSmallString small = field.getAnnotation(ConvertSmallString.class);
Decodeable<R, ?> fieldCoder;
if (small != null && field.getType() == String.class) {
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
} else {
fieldCoder = factory.findFieldCoder(clazz, field.getName());
}
if (fieldCoder == null) {
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadDecoder(t);
@@ -126,7 +133,13 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
ref = factory.findRef(clazz, method);
if (ref != null && ref.ignore()) continue;
Decodeable<R, ?> fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class);
Decodeable<R, ?> fieldCoder;
if (small != null && method.getReturnType() == String.class) {
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
} else {
fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
}
if (fieldCoder == null) {
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
fieldCoder = factory.loadDecoder(t);

View File

@@ -7,6 +7,7 @@ package org.redkale.convert;
import java.lang.reflect.*;
import java.util.*;
import org.redkale.convert.ext.StringSimpledCoder;
import org.redkale.util.*;
/**
@@ -77,7 +78,13 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(clazz, field);
if (ref != null && ref.ignore()) continue;
Encodeable<W, ?> fieldCoder = factory.findFieldCoder(clazz, field.getName());
ConvertSmallString small = field.getAnnotation(ConvertSmallString.class);
Encodeable<W, ?> fieldCoder;
if (small != null && field.getType() == String.class) {
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
} else {
fieldCoder = factory.findFieldCoder(clazz, field.getName());
}
if (fieldCoder == null) {
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t);
@@ -96,7 +103,8 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
if (factory.isConvertDisabled(method)) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) continue;
if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
String convertname = ConvertFactory.readGetSetFieldName(method);
if (reversible && (cps == null || !contains(cps, convertname))) {
boolean is = method.getName().startsWith("is");
try {
clazz.getMethod(method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType());
@@ -106,7 +114,20 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
}
ref = factory.findRef(clazz, method);
if (ref != null && ref.ignore()) continue;
Encodeable<W, ?> fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class);
if (small == null) {
try {
Field f = clazz.getDeclaredField(convertname);
if (f != null) small = f.getAnnotation(ConvertSmallString.class);
} catch (Exception e) {
}
}
Encodeable<W, ?> fieldCoder;
if (small != null && method.getReturnType() == String.class) {
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
} else {
fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
}
if (fieldCoder == null) {
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t);

View File

@@ -32,5 +32,4 @@ public abstract class TextConvert<R extends Reader, W extends Writer> extends Co
public abstract String convertTo(final Type type, final Object value);
public abstract String convertMapTo(final Object... values);
}

View File

@@ -133,7 +133,7 @@ public abstract class Writer {
}
}
Attribute attr = member.getAttribute();
this.writeFieldName(attr.field(), attr.genericType(), member.getPosition());
this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition());
member.encoder.convertTo(this, value);
this.comma = true;
}
@@ -160,7 +160,7 @@ public abstract class Writer {
if (!((Boolean) value)) return;
}
}
this.writeFieldName(fieldName, fieldType, fieldPos);
this.writeFieldName(null, fieldName, fieldType, fieldPos);
anyEncoder.convertTo(this, value);
this.comma = true;
}
@@ -172,7 +172,7 @@ public abstract class Writer {
*/
public final void writeFieldName(final EnMember member) {
Attribute attr = member.getAttribute();
this.writeFieldName(attr.field(), attr.genericType(), member.getPosition());
this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition());
}
/**
@@ -233,11 +233,12 @@ public abstract class Writer {
/**
* 输出一个字段名
*
* @param member EnMember
* @param fieldName 字段名称
* @param fieldType 字段类型
* @param fieldPos 字段顺序
*/
public abstract void writeFieldName(String fieldName, Type fieldType, int fieldPos);
public abstract void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos);
/**
* 写入一个boolean值

View File

@@ -6,10 +6,10 @@
package org.redkale.convert.bson;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import static org.redkale.convert.Reader.SIGN_NULL;
import org.redkale.convert.ext.ByteSimpledCoder;
import org.redkale.util.*;
/**
* 以ByteBuffer为数据载体的BsonReader
@@ -233,6 +233,6 @@ public class BsonByteBufferReader extends BsonReader {
int len = readInt();
if (len == SIGN_NULL) return null;
if (len == 0) return "";
return new String(Utility.decodeUTF8(read(len)));
return new String(read(len), StandardCharsets.UTF_8);
}
}

View File

@@ -137,4 +137,19 @@ public class BsonByteBufferWriter extends BsonWriter {
this.buffers = null;
return false;
}
@Override
public byte[] content() {
throw new UnsupportedOperationException("Not supported yet."); //无需实现
}
@Override
public int offset() {
throw new UnsupportedOperationException("Not supported yet.");//无需实现
}
@Override
public int length() {
throw new UnsupportedOperationException("Not supported yet."); //无需实现
}
}

View File

@@ -39,9 +39,9 @@ import org.redkale.util.*;
*/
public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
private static final ObjectPool<BsonReader> readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", Integer.getInteger("convert.pool.size", 16)));
private final ThreadLocal<BsonWriter> writerPool = ThreadLocal.withInitial(BsonWriter::new);
private static final ObjectPool<BsonWriter> writerPool = BsonWriter.createPool(Integer.getInteger("convert.bson.pool.size", Integer.getInteger("convert.pool.size", 16)));
private final Consumer<BsonWriter> offerConsumer = w -> offerBsonWriter(w);
private final boolean tiny;
@@ -84,11 +84,11 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
}
public BsonReader pollBsonReader() {
return readerPool.get();
return new BsonReader();
}
public void offerBsonReader(final BsonReader in) {
if (in != null) readerPool.accept(in);
//无需回收
}
//------------------------------ writer -----------------------------------------------------------
@@ -96,16 +96,25 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
return configWrite(new BsonByteBufferWriter(tiny, supplier));
}
public BsonWriter pollBsonWriter(final OutputStream out) {
protected BsonWriter pollBsonWriter(final OutputStream out) {
return configWrite(new BsonStreamWriter(tiny, out));
}
public BsonWriter pollBsonWriter() {
return writerPool.get().tiny(tiny);
BsonWriter writer = writerPool.get();
if (writer == null) {
writer = new BsonWriter();
} else {
writerPool.set(null);
}
return configWrite(writer.tiny(tiny));
}
public void offerBsonWriter(final BsonWriter out) {
if (out != null) writerPool.accept(out);
if (out != null) {
out.recycle();
writerPool.set(out);
}
}
//------------------------------ convertFrom -----------------------------------------------------------
@@ -119,11 +128,9 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final byte[] bytes, final int offset, final int len) {
if (type == null) return null;
final BsonReader in = readerPool.get();
in.setBytes(bytes, offset, len);
final BsonReader in = new BsonReader(bytes, offset, len);
@SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.accept(in);
return rs;
}
@@ -159,10 +166,10 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public byte[] convertTo(final Object value) {
if (value == null) {
final BsonWriter out = writerPool.get().tiny(tiny);
final BsonWriter out = pollBsonWriter();
out.writeNull();
byte[] result = out.toArray();
writerPool.accept(out);
offerBsonWriter(out);
return result;
}
return convertTo(value.getClass(), value);
@@ -171,10 +178,10 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public byte[] convertTo(final Type type, final Object value) {
if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(out, value);
byte[] result = out.toArray();
writerPool.accept(out);
final BsonWriter writer = pollBsonWriter();
factory.loadEncoder(type).convertTo(writer, value);
byte[] result = writer.toArray();
offerBsonWriter(writer);
return result;
}
@@ -189,13 +196,35 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
}
@Override
public byte[] convertMapTo(final Object... values) {
if (values == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
byte[] result = out.toArray();
writerPool.accept(out);
return result;
public void convertToBytes(final Object value, final ConvertBytesHandler handler) {
convertToBytes(value == null ? null : value.getClass(), value, handler);
}
@Override
public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) {
final BsonWriter writer = pollBsonWriter();
if (type == null) {
writer.writeNull();
} else {
factory.loadEncoder(type).convertTo(writer, value);
}
writer.completed(handler, offerConsumer);
}
@Override
public void convertToBytes(final ByteArray array, final Object value) {
convertToBytes(array, value == null ? null : value.getClass(), value);
}
@Override
public void convertToBytes(final ByteArray array, final Type type, final Object value) {
final BsonWriter writer = configWrite(new BsonWriter(array).tiny(tiny));
if (type == null) {
writer.writeNull();
} else {
factory.loadEncoder(type).convertTo(writer, value);
}
writer.directTo(array);
}
public void convertTo(final OutputStream out, final Object value) {
@@ -215,14 +244,6 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
}
}
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
pollBsonWriter(out).writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(pollBsonWriter(out), values);
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
@@ -238,25 +259,13 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
BsonByteBufferWriter out = pollBsonWriter(supplier);
BsonByteBufferWriter writer = pollBsonWriter(supplier);
if (value == null) {
out.writeNull();
writer.writeNull();
} else {
factory.loadEncoder(type).convertTo(out, value);
factory.loadEncoder(type).convertTo(writer, value);
}
return out.toBuffers();
}
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
BsonByteBufferWriter out = pollBsonWriter(supplier);
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBuffers();
return writer.toBuffers();
}
public void convertTo(final BsonWriter writer, final Object value) {
@@ -272,14 +281,6 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
factory.loadEncoder(type).convertTo(writer, value);
}
public void convertMapTo(final BsonWriter writer, final Object... values) {
if (values == null) {
writer.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
}
}
public BsonWriter convertToWriter(final Object value) {
if (value == null) return null;
return convertToWriter(value.getClass(), value);
@@ -287,14 +288,8 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
public BsonWriter convertToWriter(final Type type, final Object value) {
if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(out, value);
return out;
}
public BsonWriter convertMapToWriter(final Object... values) {
final BsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
return out;
final BsonWriter writer = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(writer, value);
return writer;
}
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.convert.bson;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import static org.redkale.convert.Reader.SIGN_NULL;
import org.redkale.convert.ext.*;
@@ -42,7 +43,7 @@ public class BsonReader extends Reader {
}
public static ObjectPool<BsonReader> createPool(int max) {
return new ObjectPool<>(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle());
return ObjectPool.createSafePool(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle());
}
public BsonReader(byte[] bytes) {
@@ -341,7 +342,7 @@ public class BsonReader extends Reader {
int len = readInt();
if (len == SIGN_NULL) return null;
if (len == 0) return "";
String value = new String(Utility.decodeUTF8(content, ++this.position, len));
String value = new String(content, ++this.position, len, StandardCharsets.UTF_8);
this.position += len - 1;//上一行已经++this.position所以此处要-1
return value;
}

View File

@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
import org.redkale.convert.*;
import org.redkale.convert.ext.ByteSimpledCoder;
import org.redkale.util.*;
@@ -18,7 +19,7 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public class BsonWriter extends Writer {
public class BsonWriter extends Writer implements ByteTuple {
private static final int defaultSize = Integer.getInteger("convert.bson.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
@@ -29,7 +30,48 @@ public class BsonWriter extends Writer {
protected boolean tiny;
public static ObjectPool<BsonWriter> createPool(int max) {
return new ObjectPool<>(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
}
@Override
public byte[] content() {
return content;
}
@Override
public int offset() {
return 0;
}
@Override
public int length() {
return count;
}
/**
* 直接获取全部数据, 实际数据需要根据count长度来截取
*
* @return byte[]
*/
public byte[] directBytes() {
return content;
}
/**
* 将本对象的内容引用复制给array
*
* @param array ByteArray
*/
public void directTo(ByteArray array) {
array.directFrom(content, count);
}
public void completed(ConvertBytesHandler handler, Consumer<BsonWriter> callback) {
handler.completed(content, 0, count, callback, this);
}
public ByteArray toByteArray() {
return new ByteArray(this);
}
public byte[] toArray() {
@@ -55,6 +97,11 @@ public class BsonWriter extends Writer {
this.content = new byte[size > 128 ? size : 128];
}
public BsonWriter(ByteArray array) {
this.content = array.content();
this.count = array.length();
}
@Override
public final boolean tiny() {
return tiny;
@@ -201,7 +248,7 @@ public class BsonWriter extends Writer {
}
@Override
public final void writeFieldName(String fieldName, Type fieldType, int fieldPos) {
public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) {
writeByte(BsonReader.SIGN_HASNEXT);
writeSmallString(fieldName);
writeByte(BsonFactory.typeEnum(fieldType));

View File

@@ -12,7 +12,9 @@ import org.redkale.convert.Writer;
/**
* String 的SimpledCoder实现
*
* <p> 详情见: https://redkale.org
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
@@ -31,4 +33,19 @@ public final class StringSimpledCoder<R extends Reader, W extends Writer> extend
return in.readString();
}
public final static class SmallStringSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, String> {
public static final SmallStringSimpledCoder instance = new SmallStringSimpledCoder();
@Override
public void convertTo(W out, String value) {
out.writeSmallString(value);
}
@Override
public String convertFrom(R in) {
return in.readSmallString();
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.ext;
import org.redkale.convert.*;
/**
* 文件 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public class ThrowableSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Throwable> {
public static final ThrowableSimpledCoder instance = new ThrowableSimpledCoder();
@Override
public void convertTo(W out, Throwable value) {
if (value == null) {
out.writeNull();
} else {
out.writeString(value.toString());
}
}
@Override
public Throwable convertFrom(R in) {
String value = in.readString();
if (value == null) return null;
return new Exception(value);
}
}

View File

@@ -22,6 +22,10 @@ import org.redkale.util.*;
*/
public class JsonByteBufferWriter extends JsonWriter {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
protected Charset charset;
private final Supplier<ByteBuffer> supplier;
@@ -36,7 +40,7 @@ public class JsonByteBufferWriter extends JsonWriter {
protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) {
this.tiny = tiny;
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
this.charset = charset;
this.supplier = supplier;
}
@@ -56,7 +60,6 @@ public class JsonByteBufferWriter extends JsonWriter {
return false;
}
@Override
public ByteBuffer[] toBuffers() {
if (buffers == null) return new ByteBuffer[0];
for (int i = index; i < this.buffers.length; i++) {
@@ -66,7 +69,6 @@ public class JsonByteBufferWriter extends JsonWriter {
return this.buffers;
}
@Override
public int count() {
if (this.buffers == null) return 0;
int len = 0;
@@ -111,6 +113,32 @@ public class JsonByteBufferWriter extends JsonWriter {
writeTo(-1, false, chs, start, len);
}
@Override
public void writeTo(final byte ch) { //只能是 0 - 127 的字符
expand(1);
this.buffers[index].put(ch);
}
@Override
public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符
int expandsize = expand(len);
if (expandsize == 0) { // 只需要一个buffer
this.buffers[index].put(chs, start, len);
} else {
ByteBuffer buffer = this.buffers[index];
int remain = len;
int offset = start;
while (remain > 0) {
int bsize = Math.min(buffer.remaining(), remain);
buffer.put(chs, offset, bsize);
offset += bsize;
remain -= bsize;
if (remain < 1) break;
buffer = nextByteBuffer();
}
}
}
private void writeTo(int expandsize, final boolean quote, final char[] chs, final int start, final int len) {
int byteLength = quote ? 2 : 0;
ByteBuffer bb = null;
@@ -266,6 +294,11 @@ public class JsonByteBufferWriter extends JsonWriter {
}
}
@Override
public void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override
public void writeInt(int value) {
writeLatin1To(false, String.valueOf(value));

View File

@@ -0,0 +1,389 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.json;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
import org.redkale.convert.*;
import static org.redkale.convert.json.JsonWriter.*;
import org.redkale.util.*;
/**
*
* writeTo系列的方法输出的字符不能含特殊字符
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public class JsonBytesWriter extends JsonWriter implements ByteTuple {
private static final boolean greatejdk8 = Utility.greaterJDK8();
private static final byte[] BYTES_TUREVALUE = "true".getBytes();
private static final byte[] BYTES_FALSEVALUE = "false".getBytes();
private int count;
private byte[] content;
public JsonBytesWriter() {
this(defaultSize);
}
public JsonBytesWriter(int size) {
this.content = new byte[size > 1024 ? size : 1024];
}
public JsonBytesWriter(ByteArray array) {
this.content = array.content();
this.count = array.length();
}
public JsonBytesWriter(boolean tiny, ByteArray array) {
this.tiny = tiny;
this.content = array.content();
this.count = array.length();
}
protected byte[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
}
@Override
public byte[] content() {
return content;
}
@Override
public int offset() {
return 0;
}
@Override
public int length() {
return count;
}
/**
* 将本对象的内容引用复制给array
*
* @param array ByteArray
*/
public void directTo(ByteArray array) {
array.directFrom(content, count);
}
@Override
public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) {
if (this.comma) writeTo(',');
if (member != null) {
byte[] bs = member.getJsonFieldNameBytes();
expand(bs.length);
System.arraycopy(bs, 0, content, count, bs.length);
count += bs.length;
} else {
writeLatin1To(true, fieldName);
writeTo(':');
}
}
@Override
public void writeTo(final char ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = (byte) ch;
}
@Override
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
for (int i = 0; i < len; i++) {
content[count + i] = (byte) chs[start + i];
}
count += len;
}
@Override
public void writeTo(final byte ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = ch;
}
@Override
public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
*
* @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值
*/
@Override
public void writeLatin1To(final boolean quote, final String value) {
byte[] bs = Utility.byteArray(value);
int len = bs.length;
expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"';
System.arraycopy(bs, 0, content, count, bs.length);
count += len;
if (quote) content[count++] = '"';
}
public JsonBytesWriter clear() {
this.count = 0;
return this;
}
@Override
public boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content != null && this.content.length > defaultSize * 100) {
this.content = new byte[defaultSize];
}
return true;
}
/**
* 直接获取全部数据, 实际数据需要根据count长度来截取
*
* @return byte[]
*/
public byte[] directBytes() {
return content;
}
public void completed(ConvertBytesHandler handler, Consumer<JsonBytesWriter> callback) {
handler.completed(content, 0, count, callback, this);
}
public byte[] toBytes() {
byte[] copy = new byte[count];
System.arraycopy(content, 0, copy, 0, count);
return copy;
}
public int count() {
return this.count;
}
private void writeEscapeLatinString(byte[] value) {
byte[] bytes = expand(value.length * 2 + 2);
int curr = count;
bytes[curr++] = '"';
for (byte b : value) {
if (b == '"') {
bytes[curr++] = '\\';
bytes[curr++] = '"';
} else if (b == '\\') {
bytes[curr++] = '\\';
bytes[curr++] = '\\';
} else if (b < 32) {
if (b == '\n') {
bytes[curr++] = '\\';
bytes[curr++] = 'n';
} else if (b == '\r') {
bytes[curr++] = '\\';
bytes[curr++] = 'r';
} else if (b == '\t') {
bytes[curr++] = '\\';
bytes[curr++] = 't';
} else {
bytes[curr++] = b;
}
} else {
bytes[curr++] = b;
}
}
bytes[curr++] = '"';
count = curr;
}
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
if (greatejdk8 && Utility.isLatin1(value)) {
writeEscapeLatinString(Utility.byteArray(value));
return;
}
byte[] bytes = expand(value.length() * 4 + 2);
int curr = count;
bytes[curr++] = '"';
int len = value.length();
for (int i = 0; i < len; i++) {
char ch = value.charAt(i);
switch (ch) {
case '\n':
bytes[curr++] = '\\';
bytes[curr++] = 'n';
break;
case '\r':
bytes[curr++] = '\\';
bytes[curr++] = 'r';
break;
case '\t':
bytes[curr++] = '\\';
bytes[curr++] = 't';
break;
case '\\':
bytes[curr++] = '\\';
bytes[curr++] = '\\';
break;
case '"':
bytes[curr++] = '\\';
bytes[curr++] = '"';
break;
default:
if (ch < 0x80) {
bytes[curr++] = (byte) ch;
} else if (ch < 0x800) {
bytes[curr++] = (byte) (0xc0 | (ch >> 6));
bytes[curr++] = (byte) (0x80 | (ch & 0x3f));
} else if (Character.isSurrogate(ch)) { //连取两个
int uc = Character.toCodePoint(ch, value.charAt(++i));
bytes[curr++] = (byte) (0xf0 | ((uc >> 18)));
bytes[curr++] = (byte) (0x80 | ((uc >> 12) & 0x3f));
bytes[curr++] = (byte) (0x80 | ((uc >> 6) & 0x3f));
bytes[curr++] = (byte) (0x80 | (uc & 0x3f));
} else {
bytes[curr++] = (byte) (0xe0 | ((ch >> 12)));
bytes[curr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
bytes[curr++] = (byte) (0x80 | (ch & 0x3f));
}
break;
}
}
bytes[curr++] = '"';
count = curr;
}
@Override
public String toString() {
return new String(content, 0, count, StandardCharsets.UTF_8);
}
//----------------------------------------------------------------------------------------------
@Override
public void writeBoolean(boolean value) {
byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE;
expand(bs.length);
System.arraycopy(bs, 0, content, count, bs.length);
count += bs.length;
}
@Override
public void writeInt(int value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size;
for (int i = 0;; i++) {
if (value <= sizeTable[i]) {
size = i + 1;
break;
}
}
if (sign != 0) size++; //负数
byte[] bytes = expand(size);
int q, r;
int charPos = count + size;
// Generate two digits per iteration
while (value >= 65536) {
q = value / 100;
// really: r = i - (q * 100);
r = value - ((q << 6) + (q << 5) + (q << 2));
value = q;
bytes[--charPos] = (byte) DigitOnes[r];
bytes[--charPos] = (byte) DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (value * 52429) >>> (16 + 3);
r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ...
bytes[--charPos] = (byte) digits[r];
value = q;
if (value == 0) break;
}
if (sign != 0) bytes[--charPos] = (byte) sign;
count += size;
}
@Override
public void writeLong(long value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size = 19;
long p = 10;
for (int i = 1; i < 19; i++) {
if (value < p) {
size = i;
break;
}
p = 10 * p;
}
if (sign != 0) size++; //负数
byte[] bytes = expand(size);
long q;
int r;
int charPos = count + size;
// Get 2 digits/iteration using longs until quotient fits into an int
while (value > Integer.MAX_VALUE) {
q = value / 100;
// really: r = i - (q * 100);
r = (int) (value - ((q << 6) + (q << 5) + (q << 2)));
value = q;
content[--charPos] = (byte) DigitOnes[r];
content[--charPos] = (byte) DigitTens[r];
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int) value;
while (i2 >= 65536) {
q2 = i2 / 100;
// really: r = i2 - (q * 100);
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
i2 = q2;
bytes[--charPos] = (byte) DigitOnes[r];
bytes[--charPos] = (byte) DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i2 <= 65536, i2);
for (;;) {
q2 = (i2 * 52429) >>> (16 + 3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
bytes[--charPos] = (byte) digits[r];
i2 = q2;
if (i2 == 0) break;
}
if (sign != 0) bytes[--charPos] = (byte) sign;
count += size;
}
}

View File

@@ -0,0 +1,267 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.json;
import java.nio.ByteBuffer;
import org.redkale.util.*;
/**
*
* writeTo系列的方法输出的字符不能含特殊字符
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public class JsonCharsWriter extends JsonWriter {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
private int count;
private char[] content;
public JsonCharsWriter() {
this(defaultSize);
}
public JsonCharsWriter(int size) {
this.content = new char[size > 1024 ? size : 1024];
}
//-----------------------------------------------------------------------
/**
* 返回指定至少指定长度的缓冲区
*
* @param len
*
* @return
*/
private char[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
}
@Override
public void writeTo(final char ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = ch;
}
@Override
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
@Override
public void writeTo(final byte ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = (char) ch;
}
@Override
public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
for (int i = 0; i < len; i++) {
content[count + i] = (char) chs[start + i];
}
count += len;
}
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
*
* @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值
*/
@Override
public void writeLatin1To(final boolean quote, final String value) {
int len = value.length();
expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"';
value.getChars(0, len, content, count);
count += len;
if (quote) content[count++] = '"';
}
@Override
protected boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content != null && this.content.length > defaultSize) {
this.content = new char[defaultSize];
}
return true;
}
public ByteBuffer[] toBuffers() {
return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))};
}
public byte[] toBytes() {
return Utility.encodeUTF8(content, 0, count);
}
public int count() {
return this.count;
}
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
expand(value.length() * 2 + 2);
content[count++] = '"';
for (char ch : Utility.charArray(value)) {
switch (ch) {
case '\n':
content[count++] = '\\';
content[count++] = 'n';
break;
case '\r':
content[count++] = '\\';
content[count++] = 'r';
break;
case '\t':
content[count++] = '\\';
content[count++] = 't';
break;
case '\\':
content[count++] = '\\';
content[count++] = ch;
break;
case '"':
content[count++] = '\\';
content[count++] = ch;
break;
default:
content[count++] = ch;
break;
}
}
content[count++] = '"';
}
@Override
public String toString() {
return new String(content, 0, count);
}
//----------------------------------------------------------------------------------------------
@Override
public void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override
public void writeInt(int value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size;
for (int i = 0;; i++) {
if (value <= sizeTable[i]) {
size = i + 1;
break;
}
}
if (sign != 0) size++; //负数
expand(size);
int q, r;
int charPos = count + size;
// Generate two digits per iteration
while (value >= 65536) {
q = value / 100;
// really: r = i - (q * 100);
r = value - ((q << 6) + (q << 5) + (q << 2));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (value * 52429) >>> (16 + 3);
r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ...
content[--charPos] = digits[r];
value = q;
if (value == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
@Override
public void writeLong(long value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size = 19;
long p = 10;
for (int i = 1; i < 19; i++) {
if (value < p) {
size = i;
break;
}
p = 10 * p;
}
if (sign != 0) size++; //负数
expand(size);
long q;
int r;
int charPos = count + size;
// Get 2 digits/iteration using longs until quotient fits into an int
while (value > Integer.MAX_VALUE) {
q = value / 100;
// really: r = i - (q * 100);
r = (int) (value - ((q << 6) + (q << 5) + (q << 2)));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int) value;
while (i2 >= 65536) {
q2 = i2 / 100;
// really: r = i2 - (q * 100);
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
i2 = q2;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i2 <= 65536, i2);
for (;;) {
q2 = (i2 * 52429) >>> (16 + 3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
content[--charPos] = digits[r];
i2 = q2;
if (i2 == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
}

View File

@@ -30,12 +30,18 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public static final Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
}.getType();
private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", Integer.getInteger("convert.pool.size", 16)));
private final ThreadLocal<JsonCharsWriter> charsWriterPool = ThreadLocal.withInitial(JsonCharsWriter::new);
private static final ObjectPool<JsonWriter> writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", Integer.getInteger("convert.pool.size", 16)));
private final ThreadLocal<JsonBytesWriter> bytesWriterPool = ThreadLocal.withInitial(JsonBytesWriter::new);
private final Consumer<JsonBytesWriter> offerBytesConsumer = w -> offerJsonBytesWriter(w);
private final boolean tiny;
private Encodeable lastConvertEncodeable;
private Decodeable lastConvertDecodeable;
protected JsonConvert(JsonFactory factory, boolean tiny) {
super(factory);
this.tiny = tiny;
@@ -65,42 +71,39 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
};
}
//------------------------------ reader -----------------------------------------------------------
public JsonReader pollJsonReader(final ByteBuffer... buffers) {
return new JsonByteBufferReader((ConvertMask) null, buffers);
}
public JsonReader pollJsonReader(final InputStream in) {
return new JsonStreamReader(in);
}
public JsonReader pollJsonReader() {
return readerPool.get();
}
public void offerJsonReader(final JsonReader in) {
if (in != null) readerPool.accept(in);
}
//------------------------------ writer -----------------------------------------------------------
public JsonByteBufferWriter pollJsonWriter(final Supplier<ByteBuffer> supplier) {
return configWrite(new JsonByteBufferWriter(tiny, supplier));
private JsonCharsWriter pollJsonCharsWriter() {
JsonCharsWriter writer = charsWriterPool.get();
if (writer == null) {
writer = new JsonCharsWriter();
} else {
charsWriterPool.set(null);
}
return configWrite((JsonCharsWriter) writer.tiny(tiny));
}
public JsonWriter pollJsonWriter(final OutputStream out) {
return configWrite(new JsonStreamWriter(tiny, out));
private JsonBytesWriter pollJsonBytesWriter() {
JsonBytesWriter writer = bytesWriterPool.get();
if (writer == null) {
writer = new JsonBytesWriter();
} else {
bytesWriterPool.set(null);
}
return configWrite((JsonBytesWriter) writer.tiny(tiny));
}
public JsonWriter pollJsonWriter(final Charset charset, final OutputStream out) {
return configWrite(new JsonStreamWriter(tiny, charset, out));
private void offerJsonCharsWriter(final JsonCharsWriter writer) {
if (writer != null) {
writer.recycle();
charsWriterPool.set(writer);
}
}
public JsonWriter pollJsonWriter() {
return configWrite(writerPool.get().tiny(tiny));
private void offerJsonBytesWriter(final JsonBytesWriter writer) {
if (writer != null) {
writer.recycle();
bytesWriterPool.set(writer);
}
public void offerJsonWriter(final JsonWriter writer) {
if (writer != null) writerPool.accept(writer);
}
//------------------------------ convertFrom -----------------------------------------------------------
@@ -128,34 +131,56 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public <T> T convertFrom(final Type type, final char[] text, final int offset, final int length) {
if (text == null || type == null) return null;
final JsonReader in = readerPool.get();
in.setText(text, offset, length);
T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.accept(in);
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
T rs = (T) decoder.convertFrom(new JsonReader(text, offset, length));
return rs;
}
public <T> T convertFrom(final Type type, final InputStream in) {
if (type == null || in == null) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonStreamReader(in));
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
return (T) decoder.convertFrom(new JsonStreamReader(in));
}
@Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
return (T) decoder.convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
}
@Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader(mask, buffers));
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
return (T) decoder.convertFrom(new JsonByteBufferReader(mask, buffers));
}
public <T> T convertFrom(final Type type, final JsonReader reader) {
if (type == null) return null;
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
@SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(reader);
T rs = (T) decoder.convertFrom(reader);
return rs;
}
@@ -174,10 +199,10 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
public <V> V convertFrom(final char[] text, final int offset, final int length) {
if (text == null) return null;
final JsonReader in = readerPool.get();
in.setText(text, offset, length);
Object rs = new AnyDecoder(factory).convertFrom(in);
readerPool.accept(in);
//final JsonReader in = readerPool.get();
//in.setText(text, offset, length);
Object rs = new AnyDecoder(factory).convertFrom(new JsonReader(text, offset, length));
//readerPool.accept(in);
return (V) rs;
}
@@ -216,46 +241,91 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public String convertTo(final Type type, final Object value) {
if (type == null) return null;
if (value == null) return "null";
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
JsonCharsWriter writer = pollJsonCharsWriter();
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
String result = writer.toString();
writerPool.accept(writer);
offerJsonCharsWriter(writer);
return result;
}
@Override
public byte[] convertToBytes(final Object value) {
if (value == null) return null;
String result = convertTo(value.getClass(), value);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
return convertToBytes(value.getClass(), value);
}
@Override
public byte[] convertToBytes(final Type type, final Object value) {
if (type == null) return null;
if (value == null) return null;
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
String result = writer.toString();
writerPool.accept(writer);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
JsonBytesWriter writer = pollJsonBytesWriter();
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
byte[] result = writer.toBytes();
offerJsonBytesWriter(writer);
return result;
}
@Override
public String convertMapTo(final Object... values) {
if (values == null) return "null";
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
String result = writer.toString();
writerPool.accept(writer);
return result;
public void convertToBytes(final Object value, final ConvertBytesHandler handler) {
convertToBytes(value == null ? null : value.getClass(), value, handler);
}
@Override
public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) {
JsonBytesWriter writer = pollJsonBytesWriter();
if (type == null) {
writer.writeNull();
} else {
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
writer.completed(handler, offerBytesConsumer);
}
@Override
public void convertToBytes(final ByteArray array, final Object value) {
convertToBytes(array, value == null ? null : value.getClass(), value);
}
@Override
public void convertToBytes(final ByteArray array, final Type type, final Object value) {
JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, array));
if (type == null) {
writer.writeNull();
} else {
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
writer.directTo(array);
}
public void convertTo(final OutputStream out, final Object value) {
if (value == null) {
pollJsonWriter(out).writeNull();
configWrite(new JsonStreamWriter(tiny, out)).writeNull();
} else {
convertTo(out, value.getClass(), value);
}
@@ -264,41 +334,23 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public void convertTo(final OutputStream out, final Type type, final Object value) {
if (type == null) return;
if (value == null) {
pollJsonWriter(out).writeNull();
configWrite(new JsonStreamWriter(tiny, out)).writeNull();
} else {
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
byte[] bs = writer.toBytes();
writerPool.accept(writer);
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
pollJsonWriter(out).writeNull();
} else {
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
byte[] bs = writer.toBytes();
writerPool.accept(writer);
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, out));
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier);
JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier));
if (value == null) {
out.writeNull();
} else {
@@ -310,7 +362,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier);
JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier));
if (value == null) {
out.writeNull();
} else {
@@ -320,18 +372,6 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return out.toBuffers();
}
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier);
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBuffers();
}
public void convertTo(final JsonWriter writer, final Object value) {
if (value == null) {
writer.writeNull();
@@ -350,30 +390,4 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
}
}
public void convertMapTo(final JsonWriter writer, final Object... values) {
if (values == null) {
writer.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
}
}
public JsonWriter convertToWriter(final Object value) {
if (value == null) return null;
return convertToWriter(value.getClass(), value);
}
public JsonWriter convertToWriter(final Type type, final Object value) {
if (type == null) return null;
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
return writer;
}
public JsonWriter convertMapToWriter(final Object... values) {
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
return writer;
}
}

View File

@@ -0,0 +1,559 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.json;
import java.lang.reflect.*;
import java.lang.reflect.Type;
import java.util.*;
import org.redkale.asm.*;
import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES;
import static org.redkale.asm.Opcodes.*;
import org.redkale.convert.*;
import org.redkale.convert.ext.*;
/**
* 简单对象的JSON序列化操作类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
*
* @param <T> 序列化的数据类型
*/
@SuppressWarnings("unchecked")
public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
protected final Class typeClass;
protected final ObjectEncoder<JsonWriter, T> objectEncoder;
protected JsonDynEncoder(final JsonFactory factory, Type type) {
this.typeClass = (Class) type;
factory.register(type, this);
this.objectEncoder = factory.createObjectEncoder(type);
}
@Override
public boolean specifyable() {
return false;
}
private static boolean checkMemberType(final JsonFactory factory, Type type, Class clazz) {
if (type == String.class) return true;
if (clazz.isPrimitive()) return true;
if (clazz.isEnum()) return true;
if (type == boolean[].class) return true;
if (type == byte[].class) return true;
if (type == short[].class) return true;
if (type == char[].class) return true;
if (type == int[].class) return true;
if (type == float[].class) return true;
if (type == long[].class) return true;
if (type == double[].class) return true;
if (type == Boolean[].class) return true;
if (type == Byte[].class) return true;
if (type == Short[].class) return true;
if (type == Character[].class) return true;
if (type == Integer[].class) return true;
if (type == Float[].class) return true;
if (type == Long[].class) return true;
if (type == Double[].class) return true;
if (type == String[].class) return true;
if (Collection.class.isAssignableFrom(clazz) && type instanceof ParameterizedType) {
Type[] ts = ((ParameterizedType) type).getActualTypeArguments();
if (ts.length == 1) {
Type t = ts[0];
if (t == Boolean.class || t == Byte.class || t == Short.class || t == Character.class
|| t == Integer.class || t == Float.class || t == Long.class || t == Double.class
|| t == String.class || ((t instanceof Class) && ((Class) t).isEnum())) return true;
if (factory.loadEncoder(t) instanceof JsonDynEncoder) return true;
}
}
if (factory.loadEncoder(type) instanceof JsonDynEncoder) return true;
return false;
}
//字段全部是primitive或String类型且没有泛型的类才能动态生成JsonDynEncoder 不支持的返回null
public static JsonDynEncoder createDyncEncoder(final JsonFactory factory, final Type type) {
if (!(type instanceof Class)) return null;
//发现有自定义的基础数据类型Encoder就不动态生成JsonDynEncoder了
if (factory.loadEncoder(boolean.class) != BoolSimpledCoder.instance) return null;
if (factory.loadEncoder(byte.class) != ByteSimpledCoder.instance) return null;
if (factory.loadEncoder(short.class) != ShortSimpledCoder.instance) return null;
if (factory.loadEncoder(char.class) != CharSimpledCoder.instance) return null;
if (factory.loadEncoder(int.class) != IntSimpledCoder.instance) return null;
if (factory.loadEncoder(float.class) != FloatSimpledCoder.instance) return null;
if (factory.loadEncoder(long.class) != LongSimpledCoder.instance) return null;
if (factory.loadEncoder(double.class) != DoubleSimpledCoder.instance) return null;
if (factory.loadEncoder(String.class) != StringSimpledCoder.instance) return null;
//array
if (factory.loadEncoder(boolean[].class) != BoolArraySimpledCoder.instance) return null;
if (factory.loadEncoder(byte[].class) != ByteArraySimpledCoder.instance) return null;
if (factory.loadEncoder(short[].class) != ShortArraySimpledCoder.instance) return null;
if (factory.loadEncoder(char[].class) != CharArraySimpledCoder.instance) return null;
if (factory.loadEncoder(int[].class) != IntArraySimpledCoder.instance) return null;
if (factory.loadEncoder(float[].class) != FloatArraySimpledCoder.instance) return null;
if (factory.loadEncoder(long[].class) != LongArraySimpledCoder.instance) return null;
if (factory.loadEncoder(double[].class) != DoubleArraySimpledCoder.instance) return null;
if (factory.loadEncoder(String[].class) != StringArraySimpledCoder.instance) return null;
final Class clazz = (Class) type;
List<AccessibleObject> members = null;
Set<String> names = new HashSet<>();
try {
ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(clazz, field);
if (ref != null && ref.ignore()) continue;
if (!(checkMemberType(factory, field.getGenericType(), field.getType()))) return null;
String name = convertFieldName(factory, clazz, field);
if (names.contains(name)) continue;
names.add(name);
if (members == null) members = new ArrayList<>();
members.add(field);
}
for (final Method method : clazz.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
if (Modifier.isAbstract(method.getModifiers())) continue;
if (method.isSynthetic()) continue;
if (method.getName().length() < 3) continue;
if (method.getName().equals("getClass")) continue;
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
if (factory.isConvertDisabled(method)) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) continue;
ref = factory.findRef(clazz, method);
if (ref != null && ref.ignore()) continue;
if (!(checkMemberType(factory, method.getGenericReturnType(), method.getReturnType()))) return null;
String name = convertFieldName(factory, clazz, method);
if (names.contains(name)) continue;
names.add(name);
if (members == null) members = new ArrayList<>();
members.add(method);
}
if (members == null) return null;
Collections.sort(members, (o1, o2) -> {
ConvertColumnEntry ref1 = factory.findRef(clazz, o1);
ConvertColumnEntry ref2 = factory.findRef(clazz, o2);
if ((ref1 != null && ref1.getIndex() > 0) || (ref2 != null && ref2.getIndex() > 0)) {
int idx1 = ref1 == null ? Integer.MAX_VALUE / 2 : ref1.getIndex();
int idx2 = ref2 == null ? Integer.MAX_VALUE / 2 : ref2.getIndex();
if (idx1 != idx2) return idx1 - idx2;
}
String n1 = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(o1) : ref1.name();
String n2 = ref2 == null || ref2.name().isEmpty() ? readGetSetFieldName(o2) : ref2.name();
return n1.compareTo(n2);
});
return generateDyncEncoder(factory, clazz, members);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
protected static String convertFieldName(final JsonFactory factory, Class clazz, AccessibleObject element) {
ConvertColumnEntry ref = factory.findRef(clazz, element);
String name = ref == null || ref.name().isEmpty() ? readGetSetFieldName(element) : ref.name();
return name;
}
protected static ConvertSmallString readConvertSmallString(AccessibleObject element) {
if (element instanceof Field) return ((Field) element).getAnnotation(ConvertSmallString.class);
Method method = (Method) element;
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class);
if (small == null) {
try {
Field f = method.getDeclaringClass().getDeclaredField(readGetSetFieldName(method));
if (f != null) small = f.getAnnotation(ConvertSmallString.class);
} catch (Exception e) {
}
}
return small;
}
protected static Class readGetSetFieldType(AccessibleObject element) {
if (element instanceof Field) return ((Field) element).getType();
return element == null ? null : ((Method) element).getReturnType();
}
protected static String readGetSetFieldName(AccessibleObject element) {
if (element instanceof Field) return ((Field) element).getName();
Method method = (Method) element;
if (method == null) return null;
String fname = method.getName();
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname;
fname = fname.substring(fname.startsWith("is") ? 2 : 3);
if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
} else if (fname.length() == 1) {
fname = "" + Character.toLowerCase(fname.charAt(0));
}
return fname;
}
protected static JsonDynEncoder generateDyncEncoder(final JsonFactory factory, final Class clazz, final List<AccessibleObject> members) {
final String supDynName = JsonDynEncoder.class.getName().replace('.', '/');
final String valtypeName = clazz.getName().replace('.', '/');
final String writerName = JsonWriter.class.getName().replace('.', '/');
final String encodeableName = Encodeable.class.getName().replace('.', '/');
final String objEncoderName = ObjectEncoder.class.getName().replace('.', '/');
final String typeDesc = org.redkale.asm.Type.getDescriptor(Type.class);
final String jsonfactoryDesc = org.redkale.asm.Type.getDescriptor(JsonFactory.class);
final String jsonwriterDesc = org.redkale.asm.Type.getDescriptor(JsonWriter.class);
final String writerDesc = org.redkale.asm.Type.getDescriptor(Writer.class);
final String encodeableDesc = org.redkale.asm.Type.getDescriptor(Encodeable.class);
final String objEncoderDesc = org.redkale.asm.Type.getDescriptor(ObjectEncoder.class);
final String valtypeDesc = org.redkale.asm.Type.getDescriptor(clazz);
String newDynName = supDynName + "_Dyn" + clazz.getSimpleName();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (String.class.getClassLoader() != clazz.getClassLoader()) {
loader = clazz.getClassLoader();
newDynName = valtypeName + "_" + JsonDynEncoder.class.getSimpleName();
}
try {
return (JsonDynEncoder) loader.loadClass(newDynName.replace('/', '.')).getDeclaredConstructor().newInstance();
} catch (Throwable ex) {
}
// ------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "L" + supDynName + "<" + valtypeDesc + ">;", supDynName, null);
Map<String, AccessibleObject> mixedNames = null;
for (AccessibleObject element : members) {
ConvertColumnEntry ref1 = factory.findRef(clazz, element);
final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name();
fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FieldBytes", "[B", null, null);
fv.visitEnd();
fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "CommaFieldBytes", "[B", null, null);
fv.visitEnd();
final Class fieldtype = readGetSetFieldType(element);
if (fieldtype != String.class && !fieldtype.isPrimitive()) {
if (mixedNames == null) mixedNames = new HashMap<>();
mixedNames.put(fieldname, element);
fv = cw.visitField(ACC_PROTECTED, fieldname + "Encoder", encodeableDesc, null, null);
fv.visitEnd();
}
}
{ // 构造函数
mv = (cw.visitMethod(ACC_PUBLIC, "<init>", "(" + jsonfactoryDesc + typeDesc + ")V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "(" + jsonfactoryDesc + typeDesc + ")V", false);
for (AccessibleObject element : members) {
ConvertColumnEntry ref1 = factory.findRef(clazz, element);
final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name();
//xxxFieldBytes
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn("\"" + fieldname + "\":");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false);
mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FieldBytes", "[B");
//xxxCommaFieldBytes
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(",\"" + fieldname + "\":");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false);
mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "CommaFieldBytes", "[B");
}
mv.visitInsn(RETURN);
mv.visitMaxs(1 + members.size(), 1 + members.size());
mv.visitEnd();
}
{
mv = (cw.visitMethod(ACC_PUBLIC, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", null, null));
//mv.setDebug(true);
{ //if (value == null) { out.writeObjectNull(null); return; }
mv.visitVarInsn(ALOAD, 2);
Label valif = new Label();
mv.visitJumpInsn(IFNONNULL, valif);
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeObjectNull", "(Ljava/lang/Class;)V", false);
mv.visitInsn(RETURN);
mv.visitLabel(valif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
{ //if (!out.isExtFuncEmpty()) { objectEncoder.convertTo(out, value); return; }
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "isExtFuncEmpty", "()Z", false);
Label extif = new Label();
mv.visitJumpInsn(IFNE, extif);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "objectEncoder", objEncoderDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, objEncoderName, "convertTo", "(" + org.redkale.asm.Type.getDescriptor(Writer.class) + "Ljava/lang/Object;)V", false);
mv.visitInsn(RETURN);
mv.visitLabel(extif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
{ //out.writeTo('{');
mv.visitVarInsn(ALOAD, 1);
mv.visitIntInsn(BIPUSH, '{');
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false);
}
int maxLocals = 4;
int elementIndex = -1;
final Class firstType = readGetSetFieldType(members.get(0));
final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !factory.tiny()); //byte/short/char/int/float/long/double
if (!mustHadComma) { //boolean comma = false;
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, 3);
}
for (AccessibleObject element : members) {
elementIndex++;
ConvertColumnEntry ref1 = factory.findRef(clazz, element);
final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name();
final Class fieldtype = readGetSetFieldType(element);
int storeid = ASTORE;
int loadid = ALOAD;
{ //String message = value.getMessage();
mv.visitVarInsn(ALOAD, 2); //加载 value
if (element instanceof Field) {
mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element).getName(), org.redkale.asm.Type.getDescriptor(fieldtype));
} else {
mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype), false);
}
if (fieldtype == boolean.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == byte.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == short.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == char.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == int.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == float.class) {
storeid = FSTORE;
loadid = FLOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == long.class) {
storeid = LSTORE;
loadid = LLOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == double.class) {
storeid = DSTORE;
loadid = DLOAD;
mv.visitVarInsn(storeid, maxLocals);
} else {
//storeid = ASTORE;
//loadid = ALOAD;
mv.visitVarInsn(storeid, maxLocals);
}
}
Label msgnotemptyif = null;
if (!fieldtype.isPrimitive()) { //if (message != null) { start
mv.visitVarInsn(loadid, maxLocals);
msgnotemptyif = new Label();
mv.visitJumpInsn(IFNULL, msgnotemptyif);
if (factory.tiny() && fieldtype == String.class) {
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z", false);
mv.visitJumpInsn(IFNE, msgnotemptyif);
}
} else if (fieldtype == boolean.class && factory.tiny()) {
mv.visitVarInsn(loadid, maxLocals);
msgnotemptyif = new Label();
mv.visitJumpInsn(IFEQ, msgnotemptyif);
}
if (mustHadComma) { //第一个字段必然会写入
if (elementIndex == 0) { //第一个
//out.writeTo(messageFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
} else {
//out.writeTo(messageCommaFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
}
} else { //if(comma) {} else {} 代码块
//if (comma) { start
mv.visitVarInsn(ILOAD, 3);
Label commaif = new Label();
mv.visitJumpInsn(IFEQ, commaif);
//out.writeTo(messageCommaFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
Label commaelse = new Label();
mv.visitJumpInsn(GOTO, commaelse);
mv.visitLabel(commaif);
if (fieldtype == boolean.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == byte.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == short.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == char.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == int.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == float.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.FLOAT}, 0, null);
} else if (fieldtype == long.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.LONG}, 0, null);
} else if (fieldtype == double.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.DOUBLE}, 0, null);
} else {
mv.visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.INTEGER, "java/lang/String"}, 0, null); // } else { comma
}
//out.writeTo(messageFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
//comma = true;
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ISTORE, 3);
mv.visitLabel(commaelse);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); //if (comma) } end
}
//out.writeString(message);
if (fieldtype == boolean.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeBoolean", "(Z)V", false);
} else if (fieldtype == byte.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeByte", "(B)V", false);
} else if (fieldtype == short.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeShort", "(S)V", false);
} else if (fieldtype == char.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeChar", "(C)V", false);
} else if (fieldtype == int.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeInt", "(I)V", false);
} else if (fieldtype == float.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeFloat", "(F)V", false);
} else if (fieldtype == long.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLong", "(J)V", false);
} else if (fieldtype == double.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeDouble", "(D)V", false);
} else if (fieldtype == String.class) {
if (readConvertSmallString(element) == null) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeString", "(Ljava/lang/String;)V", false);
} else {
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLatin1To", "(ZLjava/lang/String;)V", false);
}
} else { //int[],Boolean[],String[]
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "Encoder", encodeableDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEINTERFACE, encodeableName, "convertTo", "(" + writerDesc + "Ljava/lang/Object;)V", true);
}
if (!fieldtype.isPrimitive()) { //if (message != null) } end
mv.visitLabel(msgnotemptyif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
} else if (fieldtype == boolean.class && factory.tiny()) {
mv.visitLabel(msgnotemptyif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
if (fieldtype == long.class || fieldtype == double.class) {
maxLocals += 2;
} else {
maxLocals++;
}
}
{ //out.writeTo('}');
mv.visitVarInsn(ALOAD, 1);
mv.visitIntInsn(BIPUSH, '}');
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false);
}
mv.visitInsn(RETURN);
mv.visitMaxs(maxLocals, maxLocals);
mv.visitEnd();
}
{
mv = (cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "convertTo", "(" + jsonwriterDesc + "Ljava/lang/Object;)V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, valtypeName);
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
cw.visitEnd();
// ------------------------------------------------------------------------------
byte[] bytes = cw.toByteArray();
Class<?> creatorClazz = new ClassLoader(loader) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
JsonDynEncoder resultEncoder = (JsonDynEncoder) creatorClazz.getDeclaredConstructor(JsonFactory.class, Type.class).newInstance(factory, clazz);
if (mixedNames != null) {
for (Map.Entry<String, AccessibleObject> en : mixedNames.entrySet()) {
Field f = creatorClazz.getDeclaredField(en.getKey() + "Encoder");
f.setAccessible(true);
f.set(resultEncoder, factory.loadEncoder(en.getValue() instanceof Field ? ((Field) en.getValue()).getGenericType() : ((Method) en.getValue()).getGenericReturnType()));
}
}
return resultEncoder;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@Override
public abstract void convertTo(JsonWriter out, T value);
@Override
public Type getType() {
return typeClass;
}
}

View File

@@ -6,6 +6,7 @@
package org.redkale.convert.json;
import java.io.Serializable;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.net.*;
import org.redkale.convert.*;
@@ -26,7 +27,6 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true));
static {
instance.register(Serializable.class, instance.loadEncoder(Object.class));
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class));
@@ -63,6 +63,20 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
return new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true));
}
@Override
protected <E> Encodeable<JsonWriter, E> createDyncEncoder(Type type) {
return JsonDynEncoder.createDyncEncoder(this, type);
}
@Override
protected <E> ObjectEncoder<JsonWriter, E> createObjectEncoder(Type type) {
return super.createObjectEncoder(type);
}
protected boolean tiny() {
return this.tiny;
}
@Override
public final JsonConvert getConvert() {
if (convert == null) convert = new JsonConvert(this, tiny);

View File

@@ -25,10 +25,9 @@ public class JsonReader extends Reader {
private int limit;
public static ObjectPool<JsonReader> createPool(int max) {
return new ObjectPool<>(max, (Object... params) -> new JsonReader(), null, JsonReader::recycle);
}
// public static ObjectPool<JsonReader> createPool(int max) {
// return new ObjectPool<>(max, (Object... params) -> new JsonReader(), null, JsonReader::recycle);
// }
public JsonReader() {
}
@@ -395,13 +394,21 @@ public class JsonReader extends Reader {
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")");
value = firstchar - '0';
}
boolean dot = false;
for (;;) {
if (currpos == eof) break;
char ch = text0[++currpos];
int val = digits[ch];
if (quote && val == -3) continue;
if (val <= -3) break;
if (val == -1) throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
if (dot) continue;
if (val == -1) {
if (ch == '.') {
dot = true;
continue;
}
throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
}
if (val != -2) value = value * 10 + val;
}
this.position = currpos - 1;
@@ -446,13 +453,21 @@ public class JsonReader extends Reader {
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")");
value = firstchar - '0';
}
boolean dot = false;
for (;;) {
if (currpos == eof) break;
char ch = text0[++currpos];
int val = digits[ch];
if (quote && val == -3) continue;
if (val <= -3) break;
if (val == -1) throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
if (dot) continue;
if (val == -1) {
if (ch == '.') {
dot = true;
continue;
}
throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
}
if (val != -2) value = value * 10 + val;
}
this.position = currpos - 1;

View File

@@ -64,6 +64,13 @@ class JsonStreamWriter extends JsonByteBufferWriter {
} else if (c < 0x800) {
out.write((byte) (0xc0 | (c >> 6)));
out.write((byte) (0x80 | (c & 0x3f)));
} else if (Character.isSurrogate(c)) { //连取两个
int uc = Character.toCodePoint(c, chs[i + 1]);
out.write((byte) (0xf0 | ((uc >> 18))));
out.write((byte) (0x80 | ((uc >> 12) & 0x3f)));
out.write((byte) (0x80 | ((uc >> 6) & 0x3f)));
out.write((byte) (0x80 | (uc & 0x3f)));
i++;
} else {
out.write((byte) (0xe0 | ((c >> 12))));
out.write((byte) (0x80 | ((c >> 6) & 0x3f)));

View File

@@ -6,7 +6,6 @@
package org.redkale.convert.json;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import org.redkale.convert.*;
import org.redkale.util.*;
@@ -18,32 +17,12 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public class JsonWriter extends Writer {
public abstract class JsonWriter extends Writer {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
private static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
private int count;
private char[] content;
protected static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
protected boolean tiny;
public static ObjectPool<JsonWriter> createPool(int max) {
return new ObjectPool<>(max, (Object... params) -> new JsonWriter(), null, (JsonWriter t) -> t.recycle());
}
public JsonWriter() {
this(defaultSize);
}
public JsonWriter(int size) {
this.content = new char[size > 128 ? size : 128];
}
@Override
public boolean tiny() {
return tiny;
@@ -54,34 +33,18 @@ public class JsonWriter extends Writer {
return this;
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
/**
* 返回指定至少指定长度的缓冲区
*
* @param len
*
* @return
*/
private char[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
public boolean isExtFuncEmpty() {
return this.objExtFunc == null && this.objFieldFunc == null;
}
public void writeTo(final char ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = ch;
}
//-----------------------------------------------------------------------
public abstract void writeTo(final char ch); //只能是 0 - 127 的字符
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
public abstract void writeTo(final char[] chs, final int start, final int len); //只能是 0 - 127 的字符
public abstract void writeTo(final byte ch); //只能是 0 - 127 的字符
public abstract void writeTo(final byte[] chs, final int start, final int len); //只能是 0 - 127 的字符
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
@@ -89,108 +52,50 @@ public class JsonWriter extends Writer {
* @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值
*/
public void writeLatin1To(final boolean quote, final String value) {
int len = value.length();
expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"';
value.getChars(0, len, content, count);
count += len;
if (quote) content[count++] = '"';
}
public abstract void writeLatin1To(final boolean quote, final String value);
@Override
protected boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content != null && this.content.length > defaultSize) {
this.content = new char[defaultSize];
}
return true;
}
public ByteBuffer[] toBuffers() {
return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))};
}
public byte[] toBytes() {
return Utility.encodeUTF8(content, 0, count);
}
public int count() {
return this.count;
}
public abstract void writeBoolean(boolean value);
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
expand(value.length() * 2 + 2);
content[count++] = '"';
for (char ch : Utility.charArray(value)) {
switch (ch) {
case '\n':
content[count++] = '\\';
content[count++] = 'n';
break;
case '\r':
content[count++] = '\\';
content[count++] = 'r';
break;
case '\t':
content[count++] = '\\';
content[count++] = 't';
break;
case '\\':
content[count++] = '\\';
content[count++] = ch;
break;
case '"':
content[count++] = '\\';
content[count++] = ch;
break;
default:
content[count++] = ch;
break;
}
}
content[count++] = '"';
}
public abstract void writeInt(int value);
@Override
public final void writeFieldName(String fieldName, Type fieldType, int fieldPos) {
public abstract void writeLong(long value);
@Override
public abstract void writeString(String value);
@Override //只容许JsonBytesWriter重写此方法
public void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) {
if (this.comma) writeTo(',');
if (member != null) {
writeTo(member.getJsonFieldNameChars());
} else {
writeLatin1To(true, fieldName);
writeTo(':');
}
}
@Override
public final void writeSmallString(String value) {
writeLatin1To(true, value);
}
@Override
public String toString() {
return new String(content, 0, count);
}
//----------------------------------------------------------------------------------------------
public final void writeTo(final char... chs) { //只能是 0 - 127 的字符
writeTo(chs, 0, chs.length);
}
@Override
public final void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override
public final void writeByte(byte value) {
writeInt(value);
}
public final void writeTo(final byte[] chs) { //只能是 0 - 127 的字符
writeTo(chs, 0, chs.length);
}
@Override
public final void writeByteArray(byte[] values) {
if (values == null) {
@@ -217,101 +122,6 @@ public class JsonWriter extends Writer {
writeInt(value);
}
@Override
public void writeInt(int value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size;
for (int i = 0;; i++) {
if (value <= sizeTable[i]) {
size = i + 1;
break;
}
}
if (sign != 0) size++; //负数
expand(size);
int q, r;
int charPos = count + size;
// Generate two digits per iteration
while (value >= 65536) {
q = value / 100;
// really: r = i - (q * 100);
r = value - ((q << 6) + (q << 5) + (q << 2));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (value * 52429) >>> (16 + 3);
r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ...
content[--charPos] = digits[r];
value = q;
if (value == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
@Override
public void writeLong(long value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size = 19;
long p = 10;
for (int i = 1; i < 19; i++) {
if (value < p) {
size = i;
break;
}
p = 10 * p;
}
if (sign != 0) size++; //负数
expand(size);
long q;
int r;
int charPos = count + size;
// Get 2 digits/iteration using longs until quotient fits into an int
while (value > Integer.MAX_VALUE) {
q = value / 100;
// really: r = i - (q * 100);
r = (int) (value - ((q << 6) + (q << 5) + (q << 2)));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int) value;
while (i2 >= 65536) {
q2 = i2 / 100;
// really: r = i2 - (q * 100);
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
i2 = q2;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i2 <= 65536, i2);
for (;;) {
q2 = (i2 * 52429) >>> (16 + 3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
content[--charPos] = digits[r];
i2 = q2;
if (i2 == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
@Override
public final void writeFloat(float value) {
writeLatin1To(false, String.valueOf(value));

View File

@@ -9,9 +9,10 @@ import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import java.util.logging.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.*;
import static org.redkale.mq.MessageRecord.CTYPE_HTTP_REQUEST;
/**
* 不依赖MessageRecord则可兼容RPC方式
@@ -132,6 +133,13 @@ public class HttpMessageClient extends MessageClient {
});
}
public final <T> CompletableFuture<T> sendMessage(int userid, String groupid, HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult());
});
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(HttpSimpleRequest request) {
return sendMessage(generateHttpReqTopic(request, null), 0, null, request, null);
}
@@ -165,19 +173,20 @@ public class HttpMessageClient extends MessageClient {
}
public CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
//if (finest) logger.log(Level.FINEST, "HttpMessageClient.sendMessage: " + message);
return sendMessage(message, true, counter).thenApply(r -> r.decodeContent(HttpResultCoder.getInstance()));
}
public void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
sendMessage(message, false, counter);
}
public void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
sendMessage(message, false, counter);
}

View File

@@ -7,13 +7,14 @@ package org.redkale.mq;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.*;
import java.util.logging.Level;
import javax.annotation.Resource;
import org.redkale.cluster.ClusterAgent;
import org.redkale.net.http.*;
import org.redkale.util.Utility;
/**
* 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例
@@ -28,19 +29,21 @@ import org.redkale.net.http.*;
public class HttpMessageClusterClient extends HttpMessageClient {
//jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET
private static final Set<String> DISALLOWED_HEADERS_SET = Set.of("connection", "content-length",
private static final Set<String> DISALLOWED_HEADERS_SET = Utility.ofSet("connection", "content-length",
"date", "expect", "from", "host", "origin",
"referer", "upgrade", "via", "warning");
protected ClusterAgent clusterAgent;
protected java.net.http.HttpClient httpClient;
@Resource(name = "cluster.httpClient")
protected HttpClient httpClient;
//protected java.net.http.HttpClient httpClient;
public HttpMessageClusterClient(ClusterAgent clusterAgent) {
super(null);
Objects.requireNonNull(clusterAgent);
this.clusterAgent = clusterAgent;
this.httpClient = java.net.http.HttpClient.newHttpClient();
//this.httpClient = java.net.http.HttpClient.newHttpClient();
}
@Override
@@ -67,16 +70,17 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> {
if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
final Map<String, String> clientHeaders = new LinkedHashMap<>();
byte[] clientBody = null;
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
clientHeaders.put("Content-Type", "x-www-form-urlencoded");
if (req.getBody() != null && req.getBody().length > 0) {
String paramstr = req.getParametersToString();
if (paramstr != null) {
@@ -86,10 +90,10 @@ public class HttpMessageClusterClient extends HttpMessageClient {
req.setRequestURI(req.getRequestURI() + "?" + paramstr);
}
}
builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
clientBody = req.getBody();
} else {
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8);
}
List<CompletableFuture> futures = new ArrayList<>();
for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) {
@@ -99,7 +103,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String suburi = req.getRequestURI();
suburi = suburi.substring(1); //跳过 /
suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/'));
futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator()));
futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, clientHeaders, clientBody, addrs.iterator()));
}
if (futures.isEmpty()) return CompletableFuture.completedFuture(null);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null);
@@ -115,16 +119,17 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> {
if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
final Map<String, String> clientHeaders = new LinkedHashMap<>();
byte[] clientBody = null;
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
clientHeaders.put("Content-Type", "x-www-form-urlencoded");
if (req.getBody() != null && req.getBody().length > 0) {
String paramstr = req.getParametersToString();
if (paramstr != null) {
@@ -134,46 +139,138 @@ public class HttpMessageClusterClient extends HttpMessageClient {
req.setRequestURI(req.getRequestURI() + "?" + paramstr);
}
}
builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
clientBody = req.getBody();
} else {
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8);
}
return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator());
return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), clientHeaders, clientBody, addrs.iterator());
});
}
private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator<InetSocketAddress> it) {
private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, final Map<String, String> clientHeaders, byte[] clientBody, Iterator<InetSocketAddress> it) {
if (!it.hasNext()) return CompletableFuture.completedFuture(null);
InetSocketAddress addr = it.next();
String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi;
return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> {
if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it);
HttpResult rs = new HttpResult();
java.net.http.HttpHeaders hs = resp.headers();
if (hs != null) {
Map<String, List<String>> hm = hs.map();
if (hm != null) {
for (Map.Entry<String, List<String>> en : hm.entrySet()) {
if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
|| "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
List<String> val = en.getValue();
if (val != null && val.size() == 1) {
rs.header(en.getKey(), val.get(0));
}
}
}
}
rs.setResult(resp.body());
if (finest) {
StringBuilder sb = new StringBuilder();
Map<String, String> params = req.getParams();
if (params != null && !params.isEmpty()) {
params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v));
}
logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8));
}
return CompletableFuture.completedFuture(rs);
});
return httpClient.postAsync(url, clientHeaders, clientBody);
}
// private CompletableFuture<HttpResult<byte[]>> mqtpAsync(int userid, HttpSimpleRequest req) {
// final boolean finest = logger.isLoggable(Level.FINEST);
// String module = req.getRequestURI();
// module = module.substring(1); //去掉/
// module = module.substring(0, module.indexOf('/'));
// Map<String, String> headers = req.getHeaders();
// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
// return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> {
// if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture();
// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
// if (headers != null) headers.forEach((n, v) -> {
// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
// });
// builder.header("Content-Type", "x-www-form-urlencoded");
// if (req.getBody() != null && req.getBody().length > 0) {
// String paramstr = req.getParametersToString();
// if (paramstr != null) {
// if (req.getRequestURI().indexOf('?') > 0) {
// req.setRequestURI(req.getRequestURI() + "&" + paramstr);
// } else {
// req.setRequestURI(req.getRequestURI() + "?" + paramstr);
// }
// }
// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
// } else {
// String paramstr = req.getParametersToString();
// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
// }
// List<CompletableFuture> futures = new ArrayList<>();
// for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) {
// String realmodule = en.getKey();
// Collection<InetSocketAddress> addrs = en.getValue();
// if (addrs == null || addrs.isEmpty()) continue;
// String suburi = req.getRequestURI();
// suburi = suburi.substring(1); //跳过 /
// suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/'));
// futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator()));
// }
// if (futures.isEmpty()) return CompletableFuture.completedFuture(null);
// return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null);
// });
// }
//
// private CompletableFuture<HttpResult<byte[]>> httpAsync(int userid, HttpSimpleRequest req) {
// final boolean finest = logger.isLoggable(Level.FINEST);
// String module = req.getRequestURI();
// module = module.substring(1); //去掉/
// module = module.substring(0, module.indexOf('/'));
// Map<String, String> headers = req.getHeaders();
// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
// return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> {
// if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture();
// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
// if (headers != null) headers.forEach((n, v) -> {
// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
// });
// builder.header("Content-Type", "x-www-form-urlencoded");
// if (req.getBody() != null && req.getBody().length > 0) {
// String paramstr = req.getParametersToString();
// if (paramstr != null) {
// if (req.getRequestURI().indexOf('?') > 0) {
// req.setRequestURI(req.getRequestURI() + "&" + paramstr);
// } else {
// req.setRequestURI(req.getRequestURI() + "?" + paramstr);
// }
// }
// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
// } else {
// String paramstr = req.getParametersToString();
// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
// }
// return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator());
// });
// }
//
// private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator<InetSocketAddress> it) {
// if (!it.hasNext()) return CompletableFuture.completedFuture(null);
// InetSocketAddress addr = it.next();
// String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi;
// return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> {
// if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it);
// HttpResult rs = new HttpResult();
// java.net.http.HttpHeaders hs = resp.headers();
// if (hs != null) {
// Map<String, List<String>> hm = hs.map();
// if (hm != null) {
// for (Map.Entry<String, List<String>> en : hm.entrySet()) {
// if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
// || "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
// List<String> val = en.getValue();
// if (val != null && val.size() == 1) {
// rs.header(en.getKey(), val.get(0));
// }
// }
// }
// }
// rs.setResult(resp.body());
// if (finest) {
// StringBuilder sb = new StringBuilder();
// Map<String, String> params = req.getParams();
// if (params != null && !params.isEmpty()) {
// params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v));
// }
// logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8));
// }
// return CompletableFuture.completedFuture(rs);
// });
// }
}

View File

@@ -6,13 +6,15 @@
package org.redkale.mq;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import org.redkale.boot.NodeHttpServer;
import org.redkale.net.http.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
import org.redkale.util.ObjectPool;
/**
* 一个Service对应一个MessageProcessor
*
* <p>
* 详情见: https://redkale.org
@@ -27,14 +29,16 @@ public class HttpMessageProcessor implements MessageProcessor {
protected final boolean finer;
protected final boolean fine;
protected final Logger logger;
protected final MessageProducers producer;
protected HttpMessageClient messageClient;
protected final MessageProducers producers;
protected final NodeHttpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final HttpServlet servlet;
@@ -45,17 +49,27 @@ public class HttpMessageProcessor implements MessageProcessor {
protected final String multimodule; // 前后有/, 例如: /userstat/
protected ThreadLocal<ObjectPool<HttpMessageResponse>> respPoolThreadLocal;
protected final Supplier<HttpMessageResponse> respSupplier;
protected final Consumer<HttpMessageResponse> respConsumer;
protected CountDownLatch cdl;
protected long starttime;
protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown();
};
public HttpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeHttpServer server, Service service, HttpServlet servlet) {
public HttpMessageProcessor(Logger logger, HttpMessageClient messageClient, MessageProducers producers, NodeHttpServer server, Service service, HttpServlet servlet) {
this.logger = logger;
this.finest = logger.isLoggable(Level.FINEST);
this.finer = logger.isLoggable(Level.FINER);
this.producer = producer;
this.fine = logger.isLoggable(Level.FINE);
this.messageClient = messageClient;
this.producers = producers;
this.server = server;
this.service = service;
this.servlet = servlet;
@@ -63,44 +77,49 @@ public class HttpMessageProcessor implements MessageProcessor {
this.multiconsumer = mmc != null;
this.restmodule = "/" + Rest.getRestModule(service) + "/";
this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null;
this.workExecutor = workExecutor;
this.respSupplier = () -> respPoolThreadLocal.get().get();
this.respConsumer = resp -> respPoolThreadLocal.get().accept(resp);
this.respPoolThreadLocal = ThreadLocal.withInitial(() -> ObjectPool.createUnsafePool(Runtime.getRuntime().availableProcessors(),
ps -> new HttpMessageResponse(server.getHttpServer().getContext(), messageClient, respSupplier, respConsumer), HttpMessageResponse::prepare, HttpMessageResponse::recycle));
}
@Override
public void begin(final int size) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
public void begin(final int size, long starttime) {
this.starttime = starttime;
this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
}
private void execute(final MessageRecord message, final Runnable callback) {
HttpMessageRequest request = null;
try {
long cha = System.currentTimeMillis() - message.createtime;
if (cha > 50 || finer) {
logger.log(Level.FINER, "HttpMessageProcessor.process (mq.delay = " + cha + " ms) message: " + message);
} else if (finest) {
logger.log(Level.FINEST, "HttpMessageProcessor.process (mq.delay = " + cha + " ms) message: " + message);
}
long now = System.currentTimeMillis();
long cha = now - message.createtime;
long e = now - starttime;
if (multiconsumer) message.setResptopic(null); //不容许有响应
HttpContext context = server.getHttpServer().getContext();
request = new HttpMessageRequest(context, message);
if (multiconsumer) {
request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
HttpMessageResponse response = respSupplier.get();
request = response.request();
response.prepare(message, callback, producers.getProducer(message));
if (multiconsumer) request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
server.getHttpServer().getContext().execute(servlet, request, response);
long o = System.currentTimeMillis() - now;
if ((cha > 1000 || e > 100 || o > 1000) && fine) {
logger.log(Level.FINE, "HttpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message);
} else if ((cha > 50 || e > 10 || o > 50) && finer) {
logger.log(Level.FINER, "HttpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message);
} else if (finest) {
logger.log(Level.FINEST, "HttpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message);
}
HttpMessageResponse response = new HttpMessageResponse(context, request, callback, null, null, producer.getProducer(message));
servlet.execute(request, response);
} catch (Throwable ex) {
if (message.getResptopic() != null && !message.getResptopic().isEmpty()) {
HttpMessageResponse.finishHttpResult(finest, request == null ? null : request.getRespConvert(),
message, callback, producer.getProducer(message), message.getResptopic(), new HttpResult().status(500));
message, callback, messageClient, producers.getProducer(message), message.getResptopic(), new HttpResult().status(500));
}
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex);
}
@@ -112,13 +131,14 @@ public class HttpMessageProcessor implements MessageProcessor {
try {
this.cdl.await(30, TimeUnit.SECONDS);
} catch (Exception ex) {
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " commit error, restmodule=" + this.restmodule, ex);
}
this.cdl = null;
}
}
public MessageProducers getProducer() {
return producer;
return producers;
}
public NodeHttpServer getServer() {

View File

@@ -21,10 +21,17 @@ public class HttpMessageRequest extends HttpRequest {
protected MessageRecord message;
public HttpMessageRequest(HttpContext context, MessageRecord message) {
super(context, message.decodeContent(HttpSimpleRequestCoder.getInstance()));
public HttpMessageRequest(HttpContext context) {
super(context, (HttpSimpleRequest) null);
}
protected HttpMessageRequest prepare(MessageRecord message) {
super.initSimpleRequest(message.decodeContent(HttpSimpleRequestCoder.getInstance()));
this.message = message;
this.hashid = this.message.hash();
this.currentUserid = message.getUserid();
this.createtime = System.currentTimeMillis();
return this;
}
public void setRequestURI(String uri) {
@@ -35,4 +42,16 @@ public class HttpMessageRequest extends HttpRequest {
public Convert getRespConvert() {
return this.respConvert;
}
@Override
protected void prepare() {
super.prepare();
this.keepAlive = false;
}
@Override
protected void recycle() {
super.recycle();
this.message = null;
}
}

View File

@@ -7,12 +7,14 @@ package org.redkale.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.function.*;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.net.Response;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.ObjectPool;
import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT;
import org.redkale.net.Response;
/**
*
@@ -26,6 +28,8 @@ import org.redkale.util.ObjectPool;
*/
public class HttpMessageResponse extends HttpResponse {
protected final HttpMessageClient messageClient;
protected MessageRecord message;
protected MessageProducer producer;
@@ -34,27 +38,40 @@ public class HttpMessageResponse extends HttpResponse {
protected Runnable callback;
public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback,
ObjectPool<Response> responsePool, HttpResponseConfig config, MessageProducer producer) {
super(context, request, responsePool, config);
this.message = request.message;
public HttpMessageResponse(HttpContext context, HttpMessageClient messageClient, final Supplier<HttpMessageResponse> respSupplier, final Consumer<HttpMessageResponse> respConsumer) {
super(context, new HttpMessageRequest(context), null);
this.responseSupplier = (Supplier) respSupplier;
this.responseConsumer = (Consumer) respConsumer;
this.messageClient = messageClient;
}
// public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback,
// HttpResponseConfig config, HttpMessageClient messageClient, MessageProducer producer) {
// super(context, request, config);
// this.message = request.message;
// this.callback = callback;
// this.messageClient = messageClient;
// this.producer = producer;
// this.finest = producer.logger.isLoggable(Level.FINEST);
// }
public void prepare(MessageRecord message, Runnable callback, MessageProducer producer) {
((HttpMessageRequest)request).prepare(message);
this.message = message;
this.callback = callback;
this.producer = producer;
this.finest = producer.logger.isLoggable(Level.FINEST);
}
public HttpMessageResponse(HttpContext context, MessageRecord message, Runnable callback, HttpResponseConfig config, MessageProducer producer) {
super(context, new HttpMessageRequest(context, message), null, config);
this.message = message;
this.callback = callback;
this.producer = producer;
public HttpMessageRequest request() {
return (HttpMessageRequest) request;
}
public void finishHttpResult(HttpResult result) {
finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), this.message, this.callback, this.producer, message.getResptopic(), result);
finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), this.message, this.callback, this.messageClient, this.producer, message.getResptopic(), result);
}
public static void finishHttpResult(boolean finest, Convert respConvert, MessageRecord msg, Runnable callback, MessageProducer producer, String resptopic, HttpResult result) {
public static void finishHttpResult(boolean finest, Convert respConvert, MessageRecord msg, Runnable callback, MessageClient messageClient, MessageProducer producer, String resptopic, HttpResult result) {
if (callback != null) callback.run();
if (resptopic == null || resptopic.isEmpty()) return;
if (result.getResult() instanceof RetResult) {
@@ -66,10 +83,29 @@ public class HttpMessageResponse extends HttpResponse {
if (finest) {
Object innerrs = result.getResult();
if (innerrs instanceof byte[]) innerrs = new String((byte[]) innerrs, StandardCharsets.UTF_8);
producer.logger.log(Level.FINEST, "HttpMessageProcessor.process seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders());
producer.logger.log(Level.FINEST, "HttpMessageResponse.finishHttpResult seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders());
}
byte[] content = HttpResultCoder.getInstance().encode(result);
producer.apply(new MessageRecord(msg.getSeqid(), resptopic, null, content));
producer.apply(messageClient.createMessageRecord(msg.getSeqid(), CTYPE_HTTP_RESULT, resptopic, null, content));
}
@Override
protected void prepare() {
super.prepare();
}
@Override
protected boolean recycle() {
Supplier<Response> respSupplier = this.responseSupplier;
Consumer<Response> respConsumer = this.responseConsumer;
boolean rs = super.recycle();
this.responseSupplier = respSupplier;
this.responseConsumer = respConsumer;
this.message = null;
this.producer = null;
this.callback = null;
this.finest = false;
return rs;
}
@Override
@@ -96,13 +132,17 @@ public class HttpMessageResponse extends HttpResponse {
}
@Override
public void finish(int status, String message) {
if (finest) producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
public void finish(int status, String msg) {
if (status > 400) {
producer.logger.log(Level.WARNING, "HttpMessageResponse.finish status: " + status + ", message: " + this.message);
} else if (finest) {
producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
}
if (this.message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(message == null ? "" : message).status(status));
finishHttpResult(new HttpResult(msg == null ? "" : msg).status(status));
}
@Override
@@ -116,21 +156,26 @@ public class HttpMessageResponse extends HttpResponse {
}
@Override
public void finish(final byte[] bs) {
public void finish(boolean kill, final byte[] bs, int offset, int length) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
if (offset == 0 && bs.length == length) {
finishHttpResult(new HttpResult(bs));
} else {
finishHttpResult(new HttpResult(Arrays.copyOfRange(bs, offset, offset + length)));
}
}
@Override
public void finish(final String contentType, final byte[] bs) {
public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(bs).contentType(contentType));
byte[] rs = (offset == 0 && bs.length == length) ? bs : Arrays.copyOfRange(bs, offset, offset + length);
finishHttpResult(new HttpResult(rs).contentType(contentType));
}
@Override

View File

@@ -31,7 +31,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
@Override
public byte[] encode(HttpSimpleRequest data) {
byte[] requestURI = MessageCoder.getBytes(data.getRequestURI()); //long-string
byte[] path = MessageCoder.getBytes(data.getRequestURI()); //short-string
byte[] path = MessageCoder.getBytes(data.getPath()); //short-string
byte[] remoteAddr = MessageCoder.getBytes(data.getRemoteAddr());//short-string
byte[] sessionid = MessageCoder.getBytes(data.getSessionid());//short-string
byte[] contentType = MessageCoder.getBytes(data.getContentType());//short-string
@@ -40,6 +40,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
byte[] body = MessageCoder.getBytes(data.getBody());
int count = 1 //rpc
+ 1 //frombody
+ 4 //hashid
+ 4 //reqConvertType
+ 4 //respConvertType
+ 4 + requestURI.length + 2 + path.length + 2 + remoteAddr.length + 2 + sessionid.length
@@ -48,6 +49,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.put((byte) (data.isRpc() ? 'T' : 'F'));
buffer.put((byte) (data.isFrombody() ? 'T' : 'F'));
buffer.putInt(data.getHashid());
buffer.putInt(data.getReqConvertType() == null ? 0 : data.getReqConvertType().getValue());
buffer.putInt(data.getRespConvertType() == null ? 0 : data.getRespConvertType().getValue());
buffer.putInt(requestURI.length);
@@ -75,6 +77,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
HttpSimpleRequest req = new HttpSimpleRequest();
req.setRpc(buffer.get() == 'T');
req.setFrombody(buffer.get() == 'T');
req.setHashid(buffer.getInt());
int reqformat = buffer.getInt();
int respformat = buffer.getInt();
if (reqformat != 0) req.setReqConvertType(ConvertType.find(reqformat));

View File

@@ -7,6 +7,7 @@ package org.redkale.mq;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Resource;
@@ -15,7 +16,7 @@ import static org.redkale.boot.Application.RESNAME_APP_NODEID;
import org.redkale.net.Servlet;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.service.*;
import org.redkale.util.*;
/**
@@ -47,14 +48,14 @@ public abstract class MessageAgent {
protected final Object sncpProducerLock = new Object();
protected final AtomicLong msgSeqno = new AtomicLong(System.nanoTime());
protected HttpMessageClient httpMessageClient;
protected SncpMessageClient sncpMessageClient;
protected ScheduledThreadPoolExecutor timeoutExecutor;
protected ThreadHashExecutor workExecutor;
protected int producerCount = 1;
//本地Service消息接收处理器 key:consumer
@@ -65,23 +66,16 @@ public abstract class MessageAgent {
this.httpMessageClient = new HttpMessageClient(this);
this.sncpMessageClient = new SncpMessageClient(this);
this.producerCount = config.getIntValue("producers", Runtime.getRuntime().availableProcessors());
if ("hash".equalsIgnoreCase(config.getValue("pool", "hash"))) {
this.workExecutor = new ThreadHashExecutor(Math.max(4, config.getIntValue("threads", Runtime.getRuntime().availableProcessors())));
}
// application (it doesn't execute completion handlers).
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName("MessageAgent-Timeout-Thread");
t.setName("Redkale-MessageAgent-Timeout-Thread");
t.setDaemon(true);
return t;
});
this.timeoutExecutor.setRemoveOnCancelPolicy(true);
}
public boolean isHashPool() {
return this.workExecutor != null;
}
public CompletableFuture<Map<String, Long>> start() {
final LinkedHashMap<String, Long> map = new LinkedHashMap<>();
final List<CompletableFuture> futures = new ArrayList<>();
@@ -105,7 +99,6 @@ public abstract class MessageAgent {
public void destroy(AnyValue config) {
this.httpMessageClient.close().join();
this.sncpMessageClient.close().join();
this.workExecutor.shutdown();
if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown();
if (this.sncpProducer != null) this.sncpProducer.shutdown().join();
if (this.httpProducer != null) this.httpProducer.shutdown().join();
@@ -123,12 +116,12 @@ public abstract class MessageAgent {
protected List<MessageProducer> getAllMessageProducer() {
List<MessageProducer> producers = new ArrayList<>();
if (this.httpProducer != null) producers.addAll(List.of(this.httpProducer.producers));
if (this.sncpProducer != null) producers.addAll(List.of(this.sncpProducer.producers));
if (this.httpProducer != null) producers.addAll(Utility.ofList(this.httpProducer.producers));
if (this.sncpProducer != null) producers.addAll(Utility.ofList(this.sncpProducer.producers));
MessageProducers one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
if (one != null) producers.addAll(Utility.ofList(one.producers));
one = this.sncpMessageClient == null ? null : this.sncpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
if (one != null) producers.addAll(Utility.ofList(one.producers));
return producers;
}
@@ -221,18 +214,22 @@ public abstract class MessageAgent {
public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor);
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return;
String[] topics = generateHttpReqTopics(service);
String consumerid = generateHttpConsumerid(topics, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, this.workExecutor, getHttpProducer(), ns, service, servlet);
HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, httpMessageClient, getHttpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(topics, consumerid, processor)));
}
public final synchronized void putService(NodeSncpServer ns, Service service, SncpServlet servlet) {
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return;
String topic = generateSncpReqTopic(service);
String consumerid = generateSncpConsumerid(topic, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, this.workExecutor, getSncpProducer(), ns, service, servlet);
SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, sncpMessageClient, getSncpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(new String[]{topic}, consumerid, processor)));
}

View File

@@ -5,9 +5,14 @@
*/
package org.redkale.mq;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.redkale.convert.Convert;
import org.redkale.convert.json.JsonConvert;
import static org.redkale.mq.MessageRecord.*;
import org.redkale.net.http.*;
/**
*
@@ -24,6 +29,8 @@ public abstract class MessageClient {
protected final MessageAgent messageAgent;
protected final AtomicLong msgSeqno;
protected MessageConsumer respConsumer;
protected String respTopic;
@@ -34,10 +41,17 @@ public abstract class MessageClient {
protected boolean finer;
protected boolean fine;
private final String clazzName;
protected MessageClient(MessageAgent messageAgent) {
this.messageAgent = messageAgent;
this.msgSeqno = messageAgent == null ? new AtomicLong() : messageAgent.msgSeqno;
this.finest = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINEST);
this.finer = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINER);
this.fine = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINE);
this.clazzName = getClass().getSimpleName();
}
protected CompletableFuture<Void> close() {
@@ -45,7 +59,7 @@ public abstract class MessageClient {
return this.respConsumer.shutdown();
}
protected CompletableFuture<MessageRecord> sendMessage(MessageRecord message, boolean needresp, AtomicLong counter) {
protected CompletableFuture<MessageRecord> sendMessage(final MessageRecord message, boolean needresp, AtomicLong counter) {
CompletableFuture<MessageRecord> future = new CompletableFuture<>();
try {
if (this.respConsumer == null) {
@@ -56,19 +70,21 @@ public abstract class MessageClient {
long now = System.currentTimeMillis();
MessageRespFutureNode node = respNodes.remove(msg.getSeqid());
if (node == null) {
messageAgent.logger.log(Level.WARNING, MessageClient.this.getClass().getSimpleName() + " process " + msg + " error not found msgnode");
messageAgent.logger.log(Level.WARNING, MessageClient.this.getClass().getSimpleName() + " process " + msg + " error not found mqresp.futurenode");
return;
}
if (node.scheduledFuture != null) node.scheduledFuture.cancel(true);
AtomicLong ncer = node.getCounter();
if (ncer != null) ncer.decrementAndGet();
node.future.complete(msg);
long cha = now - msg.createtime;
if (cha > 50 || finer) {
messageAgent.logger.log(Level.FINER, "MessageRespFutureNode.process (mq.delay = " + cha + "ms) message: " + msg);
if (cha > 1000 && fine) {
messageAgent.logger.log(Level.FINE, clazzName + ".MessageRespFutureNode.process (mqs.delays = " + cha + "ms, mqs.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
} else if (cha > 50 && finer) {
messageAgent.logger.log(Level.FINER, clazzName + ".MessageRespFutureNode.process (mq.delays = " + cha + "ms, mq.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
} else if (finest) {
messageAgent.logger.log(Level.FINEST, "MessageRespFutureNode.process (mq.delay = " + cha + "ms) message: " + msg);
messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.process (mq.delay = " + cha + "ms, mq.counter = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
}
};
MessageConsumer one = messageAgent.createConsumer(new String[]{respTopic}, respConsumerid, processor);
one.startup().join();
@@ -82,10 +98,12 @@ public abstract class MessageClient {
if (counter != null) counter.incrementAndGet();
getProducer().apply(message);
if (needresp) {
MessageRespFutureNode node = new MessageRespFutureNode(message, respNodes, counter, future);
MessageRespFutureNode node = new MessageRespFutureNode(messageAgent.logger, message, respNodes, counter, future);
respNodes.put(message.getSeqid(), node);
ScheduledThreadPoolExecutor executor = messageAgent.timeoutExecutor;
if (executor != null) executor.schedule(node, 30, TimeUnit.SECONDS);
if (executor != null) {
node.scheduledFuture = executor.schedule(node, 30, TimeUnit.SECONDS);
}
} else {
future.complete(null);
}
@@ -96,5 +114,65 @@ public abstract class MessageClient {
}
}
protected abstract MessageProducers getProducer();
protected MessageRecord formatRespMessage(MessageRecord message) {
return message;
}
protected abstract MessageProducers getProducer();
public MessageRecord createMessageRecord(String resptopic, String content) {
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, null, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord createMessageRecord(String topic, String resptopic, String content) {
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord createMessageRecord(int userid, String topic, String resptopic, String content) {
return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord createMessageRecord(String topic, String resptopic, Convert convert, Object bean) {
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord createMessageRecord(int userid, String topic, String resptopic, Convert convert, Object bean) {
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord createMessageRecord(int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord createMessageRecord(int flag, int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord createMessageRecord(String topic, String resptopic, byte[] content) {
return new MessageRecord(msgSeqno.incrementAndGet(), (byte) 0, topic, resptopic, content);
}
public MessageRecord createMessageRecord(long seqid, String topic, String resptopic, byte[] content) {
return new MessageRecord(seqid, (byte) 0, topic, resptopic, content);
}
protected MessageRecord createMessageRecord(byte ctype, String topic, String resptopic, byte[] content) {
return new MessageRecord(msgSeqno.incrementAndGet(), ctype, topic, resptopic, content);
}
protected MessageRecord createMessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) {
return new MessageRecord(seqid, ctype, topic, resptopic, content);
}
private byte ctype(Convert convert, Object bean) {
byte ctype = 0;
if (convert instanceof JsonConvert) {
if (bean instanceof HttpSimpleRequest) {
ctype = CTYPE_HTTP_REQUEST;
} else if (bean instanceof HttpResult) {
ctype = CTYPE_HTTP_RESULT;
}
}
return ctype;
}
}

View File

@@ -6,6 +6,7 @@
package org.redkale.mq;
/**
* 一个Service对应一个MessageProcessor
*
* <p>
* 详情见: https://redkale.org
@@ -16,7 +17,7 @@ package org.redkale.mq;
*/
public interface MessageProcessor {
default void begin(int size) {
default void begin(int size, long starttime) {
}
public void process(MessageRecord message, Runnable callback);

View File

@@ -8,6 +8,10 @@ package org.redkale.mq;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import org.redkale.convert.bson.BsonConvert;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.HttpSimpleRequest;
import org.redkale.net.sncp.SncpRequest;
import org.redkale.util.Comment;
/**
@@ -26,6 +30,14 @@ public class MessageRecord implements Serializable {
static final byte[] EMPTY_BYTES = new byte[0];
protected static final byte CTYPE_STRING = 1;
protected static final byte CTYPE_HTTP_REQUEST = 2;
protected static final byte CTYPE_HTTP_RESULT = 3;
protected static final byte CTYPE_BSON_RESULT = 4;
@ConvertColumn(index = 1)
@Comment("消息序列号")
protected long seqid;
@@ -62,51 +74,27 @@ public class MessageRecord implements Serializable {
@Comment("消息内容")
protected byte[] content;
@ConvertColumn(index = 10)
@Comment("消息内容的类型")
protected byte ctype;
@Comment("本地附加对象,不会被序列化")
protected Object localattach;
public MessageRecord() {
}
public MessageRecord(String resptopic, String content) {
this(System.nanoTime(), 1, 0, System.currentTimeMillis(), 0, null, null, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
protected MessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) {
this(seqid, ctype, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content);
}
public MessageRecord(String topic, String resptopic, String content) {
this(System.nanoTime(), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
protected MessageRecord(long seqid, byte ctype, int flag, int userid, String groupid, String topic, String resptopic, byte[] content) {
this(seqid, ctype, 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, content);
}
public MessageRecord(int userid, String topic, String resptopic, String content) {
this(System.nanoTime(), 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord(String topic, String resptopic, Convert convert, Object bean) {
this(System.nanoTime(), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord(int userid, String topic, String resptopic, Convert convert, Object bean) {
this(System.nanoTime(), 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord(int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
this(System.nanoTime(), 1, 0, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord(int flag, int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
this(System.nanoTime(), 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord(String topic, String resptopic, byte[] content) {
this(System.nanoTime(), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content);
}
public MessageRecord(long seqid, String topic, String resptopic, byte[] content) {
this(seqid, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content);
}
public MessageRecord(long seqid, int flag, int userid, String groupid, String topic, String resptopic, byte[] content) {
this(seqid, 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, content);
}
public MessageRecord(long seqid, int version, int flag, long createtime, int userid, String groupid, String topic, String resptopic, byte[] content) {
protected MessageRecord(long seqid, byte ctype, int version, int flag, long createtime, int userid, String groupid, String topic, String resptopic, byte[] content) {
this.seqid = seqid;
this.ctype = ctype;
this.version = version;
this.flag = flag;
this.createtime = createtime;
@@ -121,6 +109,11 @@ public class MessageRecord implements Serializable {
return content == null ? null : new String(content, StandardCharsets.UTF_8);
}
public MessageRecord attach(Object attach) {
this.localattach = attach;
return this;
}
@ConvertDisabled
public boolean isEmptyTopic() {
return this.topic == null || this.topic.isEmpty();
@@ -285,7 +278,26 @@ public class MessageRecord implements Serializable {
if (this.groupid != null) sb.append(",\"groupid\":\"").append(this.groupid).append("\"");
if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).append("\"");
if (this.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\"");
if (this.content != null) sb.append(",\"content\":").append(new String(this.content, StandardCharsets.UTF_8)).append("\"");
if (this.content != null) {
if (this.ctype == CTYPE_BSON_RESULT && this.content.length > SncpRequest.HEADER_SIZE) {
int offset = SncpRequest.HEADER_SIZE + 1; //循环占位符
Object rs = BsonConvert.root().convertFrom(Object.class, this.content, offset, this.content.length - offset);
sb.append(",\"content\":").append(rs);
} else if (this.ctype == CTYPE_HTTP_REQUEST) {
HttpSimpleRequest req = HttpSimpleRequestCoder.getInstance().decode(this.content);
if (req != null) {
if (req.getCurrentUserid() == 0) req.setCurrentUserid(this.userid);
if (req.getHashid() == 0) req.setHashid(this.hash());
}
sb.append(",\"content\":").append(req);
} else if (this.ctype == CTYPE_HTTP_RESULT) {
sb.append(",\"content\":").append(HttpResultCoder.getInstance().decode(this.content));
} else if (localattach != null) {
sb.append(",\"attach\":").append(JsonConvert.root().convertTo(localattach));
} else {
sb.append(",\"content\":\"").append(new String(this.content, StandardCharsets.UTF_8)).append("\"");
}
}
sb.append("}");
return sb.toString();
}

View File

@@ -31,10 +31,11 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
byte[] stopics = MessageCoder.getBytes(data.getTopic());
byte[] dtopics = MessageCoder.getBytes(data.getResptopic());
byte[] groupid = MessageCoder.getBytes(data.getGroupid());
int count = 8 + 4 + 4 + 8 + 4 + 2 + stopics.length + 2 + dtopics.length + 2 + groupid.length + 4 + (data.getContent() == null ? 0 : data.getContent().length);
int count = 8 + 1 + 4 + 4 + 8 + 4 + 2 + stopics.length + 2 + dtopics.length + 2 + groupid.length + 4 + (data.getContent() == null ? 0 : data.getContent().length);
final byte[] bs = new byte[count];
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putLong(data.getSeqid());
buffer.put(data.ctype);
buffer.putInt(data.getVersion());
buffer.putInt(data.getFlag());
buffer.putLong(data.getCreatetime());
@@ -59,6 +60,7 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
if (data == null) return null;
ByteBuffer buffer = ByteBuffer.wrap(data);
long seqid = buffer.getLong();
byte ctype = buffer.get();
int version = buffer.getInt();
int flag = buffer.getInt();
long createtime = buffer.getLong();
@@ -74,7 +76,7 @@ public class MessageRecordCoder implements MessageCoder<MessageRecord> {
content = new byte[contentlen];
buffer.get(content);
}
return new MessageRecord(seqid, version, flag, createtime, userid, groupid, topic, resptopic, content);
return new MessageRecord(seqid, ctype, version, flag, createtime, userid, groupid, topic, resptopic, content);
}
}

View File

@@ -7,6 +7,7 @@ package org.redkale.mq;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.*;
/**
* MQ管理器
@@ -28,9 +29,17 @@ public class MessageRespFutureNode implements Runnable {
protected final CompletableFuture<MessageRecord> future;
protected final Logger logger;
protected final MessageRecord message;
protected final ConcurrentHashMap<Long, MessageRespFutureNode> respNodes;
public MessageRespFutureNode(MessageRecord message, ConcurrentHashMap<Long, MessageRespFutureNode> respNodes, AtomicLong counter, CompletableFuture<MessageRecord> future) {
protected ScheduledFuture<?> scheduledFuture;
public MessageRespFutureNode(Logger logger, MessageRecord message, ConcurrentHashMap<Long, MessageRespFutureNode> respNodes, AtomicLong counter, CompletableFuture<MessageRecord> future) {
this.logger = logger;
this.message = message;
this.seqid = message.getSeqid();
this.respNodes = respNodes;
this.counter = counter;
@@ -42,6 +51,8 @@ public class MessageRespFutureNode implements Runnable {
public void run() { //timeout
respNodes.remove(this.seqid);
future.completeExceptionally(new TimeoutException());
logger.log(Level.WARNING, getClass().getSimpleName() + " wait msg: " + message + " timeout " + (System.currentTimeMillis() - createtime) + "ms"
+ (message.userid > 0 || (message.groupid != null && !message.groupid.isEmpty()) ? (message.userid > 0 ? (", userid:" + message.userid) : (", groupid:" + message.groupid)) : ""));
}
public long getSeqid() {

View File

@@ -53,4 +53,9 @@ public class SncpMessageClient extends MessageClient {
return sendMessage(message, true, counter);
}
@Override
protected MessageRecord formatRespMessage(MessageRecord message) {
if (message != null) message.ctype = MessageRecord.CTYPE_BSON_RESULT;
return message;
}
}

View File

@@ -10,9 +10,9 @@ import java.util.logging.*;
import org.redkale.boot.NodeSncpServer;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
/**
* 一个Service对应一个MessageProcessor
*
* <p>
* 详情见: https://redkale.org
@@ -23,54 +23,74 @@ import org.redkale.util.ThreadHashExecutor;
*/
public class SncpMessageProcessor implements MessageProcessor {
protected final boolean finest;
protected final boolean finer;
protected final boolean fine;
protected final Logger logger;
protected MessageClient messageClient;
protected final MessageProducers producer;
protected final NodeSncpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final SncpServlet servlet;
protected CountDownLatch cdl;
protected long starttime;
protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown();
};
public SncpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) {
public SncpMessageProcessor(Logger logger, SncpMessageClient messageClient, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) {
this.logger = logger;
this.finest = logger.isLoggable(Level.FINEST);
this.finer = logger.isLoggable(Level.FINER);
this.fine = logger.isLoggable(Level.FINE);
this.messageClient = messageClient;
this.producer = producer;
this.server = server;
this.service = service;
this.servlet = servlet;
this.workExecutor = workExecutor;
}
@Override
public void begin(final int size) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
public void begin(final int size, long starttime) {
this.starttime = starttime;
this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
}
private void execute(final MessageRecord message, final Runnable callback) {
SncpMessageResponse response = null;
try {
long now = System.currentTimeMillis();
long cha = now - message.createtime;
long e = now - starttime;
SncpContext context = server.getSncpServer().getContext();
SncpMessageRequest request = new SncpMessageRequest(context, message);
response = new SncpMessageResponse(context, request, callback, null, producer.getProducer(message));
servlet.execute(request, response);
response = new SncpMessageResponse(context, request, callback, messageClient, producer.getProducer(message));
context.execute(servlet, request, response);
long o = System.currentTimeMillis() - now;
if ((cha > 1000 || e > 100 || o > 1000) && fine) {
logger.log(Level.FINE, "SncpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message);
} else if ((cha > 50 || e > 10 || o > 50) && finer) {
logger.log(Level.FINER, "SncpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message);
} else if (finest) {
logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message);
}
} catch (Throwable ex) {
if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null);
logger.log(Level.SEVERE, SncpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex);

View File

@@ -6,7 +6,10 @@
package org.redkale.mq;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.redkale.net.sncp.*;
import org.redkale.util.Utility;
/**
*
@@ -23,9 +26,20 @@ public class SncpMessageRequest extends SncpRequest {
@SuppressWarnings("OverridableMethodCallInConstructor")
public SncpMessageRequest(SncpContext context, MessageRecord message) {
super(context, null);
super(context);
this.message = message;
readHeader(ByteBuffer.wrap(message.getContent()));
this.hashid = this.message.hash();
this.createtime = System.currentTimeMillis();
readHeader(ByteBuffer.wrap(message.getContent()), null);
}
@Override //被SncpAsyncHandler.sncp_setParams调用
protected void sncp_setParams(SncpDynServlet.SncpServletAction action, Logger logger, Object... params) {
if (message.localattach != null) return;
if (logger.isLoggable(Level.FINER)) {
message.attach(Utility.append(new Object[]{action.actionName()}, params));
} else {
message.attach(params);
}
}
}

View File

@@ -5,12 +5,10 @@
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import org.redkale.convert.bson.BsonWriter;
import org.redkale.net.Response;
import org.redkale.net.sncp.*;
import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE;
import org.redkale.util.ObjectPool;
import org.redkale.util.ByteArray;
/**
*
@@ -23,23 +21,27 @@ import org.redkale.util.ObjectPool;
*/
public class SncpMessageResponse extends SncpResponse {
protected MessageClient messageClient;
protected MessageRecord message;
protected MessageProducer producer;
protected Runnable callback;
public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, ObjectPool<Response> responsePool, MessageProducer producer) {
super(context, request, responsePool);
public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, MessageClient messageClient, MessageProducer producer) {
super(context, request);
this.message = request.message;
this.callback = callback;
this.messageClient = messageClient;
this.producer = producer;
}
public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, ObjectPool<Response> responsePool, MessageProducer producer) {
super(context, new SncpMessageRequest(context, message), responsePool);
public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, MessageClient messageClient, MessageProducer producer) {
super(context, new SncpMessageRequest(context, message));
this.message = message;
this.callback = callback;
this.messageClient = messageClient;
this.producer = producer;
}
@@ -47,14 +49,14 @@ public class SncpMessageResponse extends SncpResponse {
public void finish(final int retcode, final BsonWriter out) {
if (callback != null) callback.run();
if (out == null) {
final byte[] result = new byte[SncpRequest.HEADER_SIZE];
fillHeader(ByteBuffer.wrap(result), 0, retcode);
producer.apply(new MessageRecord(message.getSeqid(), message.getResptopic(), null, (byte[]) null));
final ByteArray result = new ByteArray(SncpRequest.HEADER_SIZE);
fillHeader(result, 0, retcode);
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, (byte[]) null));
return;
}
final int respBodyLength = out.count(); //body总长度
final byte[] result = out.toArray();
fillHeader(ByteBuffer.wrap(result), respBodyLength - HEADER_SIZE, retcode);
producer.apply(new MessageRecord(message.getSeqid(), message.getResptopic(), null, result));
final ByteArray result = out.toByteArray();
fillHeader(result, respBodyLength - HEADER_SIZE, retcode);
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, result.getBytes()));
}
}

View File

@@ -22,7 +22,8 @@ import javax.net.ssl.SSLContext;
*
* @author zhangjx
*/
class TcpAioAsyncConnection extends AsyncConnection {
@Deprecated //@since 2.3.0
class AsyncAioTcpConnection extends AsyncConnection {
//private final Semaphore semaphore = new Semaphore(1);
private int readTimeoutSeconds;
@@ -35,11 +36,11 @@ class TcpAioAsyncConnection extends AsyncConnection {
private BlockingQueue<WriteEntry> writeQueue;
public TcpAioAsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
public AsyncAioTcpConnection(int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0,
final int readTimeoutSeconds, final int writeTimeoutSeconds,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
super(false, bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
@@ -135,17 +136,6 @@ class TcpAioAsyncConnection extends AsyncConnection {
}
private <A> void write(boolean acquire, ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
// if (acquire && !semaphore.tryAcquire()) {
// if (this.writeQueue == null) {
// synchronized (semaphore) {
// if (this.writeQueue == null) {
// this.writeQueue = new LinkedBlockingDeque<>();
// }
// }
// }
// this.writeQueue.add(new WriteEntry(src, attachment, handler));
// return;
// }
WriteOneCompletionHandler newHandler = new WriteOneCompletionHandler(src, handler);
if (!channel.isOpen()) {
newHandler.failed(new ClosedChannelException(), attachment);
@@ -282,6 +272,8 @@ class TcpAioAsyncConnection extends AsyncConnection {
@Override
public final void close() throws IOException {
super.close();
channel.shutdownInput();
channel.shutdownOutput();
channel.close();
BlockingQueue<WriteEntry> queue = this.writeQueue;
if (queue == null) return;
@@ -306,6 +298,11 @@ class TcpAioAsyncConnection extends AsyncConnection {
return true;
}
@Override
protected void continueRead() {
throw new UnsupportedOperationException("Not supported yet.");
}
private class WriteMoreCompletionHandler<A> implements CompletionHandler<Long, A> {
private final CompletionHandler<Integer, A> writeHandler;

View File

@@ -10,8 +10,9 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.*;
import java.util.logging.Level;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
@@ -22,20 +23,36 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public class TcpAioProtocolServer extends ProtocolServer {
@Deprecated //@since 2.3.0
class AsyncAioTcpProtocolServer extends ProtocolServer {
//创建数
private final AtomicLong createCounter = new AtomicLong();
//关闭数
private final AtomicLong closedCounter = new AtomicLong();
//在线数
private final AtomicLong livingCounter = new AtomicLong();
private AsynchronousChannelGroup group;
private AsynchronousServerSocketChannel serverChannel;
public TcpAioProtocolServer(Context context) {
public AsyncAioTcpProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
//group = AsynchronousChannelGroup.withThreadPool(context.executor);
group = AsynchronousChannelGroup.withFixedThreadPool(context.executor.getCorePoolSize(), context.executor.getThreadFactory());
final AtomicInteger workcounter = new AtomicInteger();
group = AsynchronousChannelGroup.withFixedThreadPool(Runtime.getRuntime().availableProcessors(), (Runnable r) -> {
int c = workcounter.incrementAndGet();
String threadname = "Redkale-AioThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, application.getWorkExecutor(), r);
return t;
});
this.serverChannel = AsynchronousServerSocketChannel.open(group);
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
@@ -72,14 +89,13 @@ public class TcpAioProtocolServer extends ProtocolServer {
}
@Override
public void accept(Server server) throws IOException {
public void accept(Application application, Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
final AsynchronousServerSocketChannel serchannel = this.serverChannel;
serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@@ -102,10 +118,9 @@ public class TcpAioProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
AsyncConnection conn = new TcpAioAsyncConnection(bufferPool, bufferPool, channel,
AsyncConnection conn = new AsyncAioTcpConnection(server.bufferCapacity, bufferPool, bufferPool, channel,
context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
//context.runAsync(new PrepareRunner(context, responsePool, conn, null, null));
new PrepareRunner(context, responsePool, conn, null, null).run();
new ProtocolCodec(context, responsePool, responsePool, conn).run(null);
} catch (Throwable e) {
context.logger.log(Level.INFO, channel + " accept error", e);
}
@@ -119,6 +134,21 @@ public class TcpAioProtocolServer extends ProtocolServer {
});
}
@Override
public long getCreateConnectionCount() {
return this.createCounter.get();
}
@Override
public long getClosedConnectionCount() {
return this.closedCounter.get();
}
@Override
public long getLivingConnectionCount() {
return this.livingCounter.get();
}
@Override
public void close() throws IOException {
this.serverChannel.close();

View File

@@ -10,7 +10,6 @@ import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import javax.net.ssl.SSLContext;
@@ -35,10 +34,18 @@ public abstract class AsyncConnection implements AutoCloseable {
protected volatile long writetime;
protected final boolean client;
protected final int bufferCapacity;
private final Supplier<ByteBuffer> bufferSupplier;
private final Consumer<ByteBuffer> bufferConsumer;
private ByteBufferWriter pipelineWriter;
private PipelineDataNode pipelineDataNode;
private ByteBuffer readBuffer;
//在线数
@@ -52,15 +59,20 @@ public abstract class AsyncConnection implements AutoCloseable {
//关联的事件数, 小于1表示没有事件
private final AtomicInteger eventing = new AtomicInteger();
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext,
//用于服务端的Socket, 等同于一直存在的readCompletionHandler
ProtocolCodec protocolCodec;
protected AsyncConnection(boolean client, final int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
this(bufferPool, bufferPool, sslContext, livingCounter, closedCounter);
this(client, bufferCapacity, bufferPool, bufferPool, sslContext, livingCounter, closedCounter);
}
protected AsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
protected AsyncConnection(boolean client, final int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SSLContext sslContext, final AtomicLong livingCounter, final AtomicLong closedCounter) {
Objects.requireNonNull(bufferSupplier);
Objects.requireNonNull(bufferConsumer);
this.client = client;
this.bufferCapacity = bufferCapacity;
this.bufferSupplier = bufferSupplier;
this.bufferConsumer = bufferConsumer;
this.sslContext = sslContext;
@@ -92,6 +104,8 @@ public abstract class AsyncConnection implements AutoCloseable {
return eventing.decrementAndGet();
}
protected abstract void continueRead();
public abstract boolean isOpen();
public abstract boolean isTCP();
@@ -118,16 +132,77 @@ public abstract class AsyncConnection implements AutoCloseable {
public abstract ReadableByteChannel readableByteChannel();
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
public abstract WritableByteChannel writableByteChannel();
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
//src会写完才会回调
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
//srcs会写完才会回调
public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(srcs, 0, srcs.length, attachment, handler);
}
public final void write(byte[] bytes, CompletionHandler<Integer, Void> handler) {
write(bytes, 0, bytes.length, null, 0, 0, handler);
}
public final void write(ByteTuple array, CompletionHandler<Integer, Void> handler) {
write(array.content(), array.offset(), array.length(), null, 0, 0, handler);
}
public final void write(byte[] bytes, int offset, int length, CompletionHandler<Integer, Void> handler) {
write(bytes, offset, length, null, 0, 0, handler);
}
public final void write(ByteTuple header, ByteTuple body, CompletionHandler<Integer, Void> handler) {
write(header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length(), handler);
}
public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, CompletionHandler<Integer, Void> handler) {
final ByteBuffer buffer = pollWriteBuffer();
if (buffer.remaining() >= headerLength + bodyLength) {
buffer.put(headerContent, headerOffset, headerLength);
if (bodyLength > 0) buffer.put(bodyContent, bodyOffset, bodyLength);
buffer.flip();
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
offerBuffer(buffer);
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, Void attachment) {
offerBuffer(buffer);
handler.failed(exc, attachment);
}
};
write(buffer, null, newhandler);
} else {
ByteBufferWriter writer = ByteBufferWriter.create(bufferSupplier, buffer);
writer.put(headerContent, headerOffset, headerLength);
if (bodyLength > 0) writer.put(bodyContent, bodyOffset, bodyLength);
final ByteBuffer[] buffers = writer.toBuffers();
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
offerBuffer(buffers);
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, Void attachment) {
offerBuffer(buffers);
handler.failed(exc, attachment);
}
};
write(buffers, null, newhandler);
}
}
//srcs会写完才会回调
public abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler);
public void setReadBuffer(ByteBuffer buffer) {
@@ -135,6 +210,142 @@ public abstract class AsyncConnection implements AutoCloseable {
this.readBuffer = buffer;
}
public boolean hasPipelineData() {
ByteBufferWriter writer = this.pipelineWriter;
return writer != null && writer.position() > 0;
}
public final void flushPipelineData(CompletionHandler<Integer, Void> handler) {
flushPipelineData(null, handler);
}
public <A> void flushPipelineData(A attachment, CompletionHandler<Integer, ? super A> handler) {
ByteBufferWriter writer = this.pipelineWriter;
this.pipelineWriter = null;
if (writer == null) {
handler.completed(0, attachment);
} else {
ByteBuffer[] srcs = writer.toBuffers();
CompletionHandler<Integer, ? super A> newhandler = new CompletionHandler<Integer, A>() {
@Override
public void completed(Integer result, A attachment) {
offerBuffer(srcs);
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, A attachment) {
offerBuffer(srcs);
handler.failed(exc, attachment);
}
};
if (srcs.length == 1) {
write(srcs[0], attachment, newhandler);
} else {
write(srcs, attachment, newhandler);
}
}
}
//返回 是否over
public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple array) {
return writePipelineData(pipelineIndex, pipelineCount, array.content(), array.offset(), array.length());
}
//返回 是否over
public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] bs, int offset, int length) {
synchronized (this) {
ByteBufferWriter writer = this.pipelineWriter;
if (writer == null) {
writer = ByteBufferWriter.create(getBufferSupplier());
this.pipelineWriter = writer;
}
if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) {
writer.put(bs, offset, length);
return (pipelineIndex == pipelineCount);
} else {
PipelineDataNode dataNode = this.pipelineDataNode;
if (dataNode == null) {
dataNode = new PipelineDataNode();
this.pipelineDataNode = dataNode;
}
if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值
dataNode.pipelineCount = pipelineCount;
}
dataNode.put(pipelineIndex, bs, offset, length);
if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) {
for (PipelineDataItem item : dataNode.arrayItems()) {
writer.put(item.data);
}
this.pipelineDataNode = null;
return true;
}
return false;
}
}
}
private static class PipelineDataNode {
public int pipelineCount;
public int itemsize;
private PipelineDataItem head;
private PipelineDataItem tail;
public PipelineDataItem[] arrayItems() {
PipelineDataItem[] items = new PipelineDataItem[itemsize];
PipelineDataItem item = head;
int i = 0;
while (item != null) {
items[i] = item;
item = item.next;
items[i].next = null;
i++;
}
Arrays.sort(items);
return items;
}
public void put(int pipelineIndex, byte[] bs, int offset, int length) {
if (tail == null) {
head = new PipelineDataItem(pipelineIndex, bs, offset, length);
tail = head;
} else {
PipelineDataItem item = new PipelineDataItem(pipelineIndex, bs, offset, length);
tail.next = item;
tail = item;
}
itemsize++;
}
}
private static class PipelineDataItem implements Comparable<PipelineDataItem> {
final byte[] data;
final int index;
public PipelineDataItem next;
public PipelineDataItem(int index, byte[] bs, int offset, int length) {
this.index = index;
this.data = Arrays.copyOfRange(bs, offset, offset + length);
}
@Override
public int compareTo(PipelineDataItem o) {
return this.index - o.index;
}
@Override
public String toString() {
return "{\"index\":" + index + "}";
}
}
public ByteBuffer pollReadBuffer() {
ByteBuffer rs = this.readBuffer;
if (rs != null) {
@@ -191,11 +402,7 @@ public abstract class AsyncConnection implements AutoCloseable {
}
if (this.readBuffer != null) {
Consumer<ByteBuffer> consumer = this.bufferConsumer;
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// consumer = ((IOThread) thread).getBufferPool();
// }
consumer.accept(this.readBuffer);
if (consumer != null) consumer.accept(this.readBuffer);
}
if (attributes == null) return;
try {
@@ -237,155 +444,4 @@ public abstract class AsyncConnection implements AutoCloseable {
if (this.attributes != null) this.attributes.clear();
}
//------------------------------------------------------------------------------------------------------------------------------
/**
* 创建TCP协议客户端连接
*
* @param bufferPool ByteBuffer对象池
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousChannelGroup group,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return createTCP(bufferPool, group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
}
/**
* 创建TCP协议客户端连接
*
* @param bufferPool ByteBuffer对象池
* @param address 连接点子
* @param sslContext SSLContext
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousChannelGroup group, final SSLContext sslContext,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return createTCP(bufferPool, bufferPool, group, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds);
}
/**
* 创建TCP协议客户端连接
*
* @param bufferSupplier ByteBuffer生产器
* @param bufferConsumer ByteBuffer回收器
* @param address 连接点子
* @param sslContext SSLContext
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, final AsynchronousChannelGroup group, final SSLContext sslContext,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
try {
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (IOException e) {
}
channel.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
future.complete(new TcpAioAsyncConnection(bufferSupplier, bufferConsumer, channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds, null, null));
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
} catch (IOException e) {
future.completeExceptionally(e);
}
return future;
}
// public static AsyncConnection create(final Socket socket) {
// return create(socket, null, 0, 0);
// }
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, null, null);
// }
//
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0,
// final int writeTimeoutSecond0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, livingCounter, closedCounter);
// }
//
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
// }
//
// public static AsyncConnection create(final SocketChannel ch, final SocketAddress addr0, final Selector selector, final Context context) {
// return new TcpNioAsyncConnection(ch, addr0, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
// }
//
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
// final AtomicLong livingCounter, final AtomicLong closedCounter) {
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
// }
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch) {
return create(bufferPool, ch, null, 0, 0);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
}

View File

@@ -0,0 +1,44 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.net.SocketAddress;
import java.util.concurrent.*;
/**
* Client模式的AsyncConnection连接构造器
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public abstract class AsyncGroup {
public CompletableFuture<AsyncConnection> createTCP(final SocketAddress address) {
return createTCP(address, 0, 0);
}
public abstract CompletableFuture<AsyncConnection> createTCP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds);
public CompletableFuture<AsyncConnection> createUDP(final SocketAddress address) {
return createUDP(address, 0, 0);
}
public abstract CompletableFuture<AsyncConnection> createUDP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds);
public CompletableFuture<AsyncConnection> create(final boolean tcp, final SocketAddress address) {
return tcp ? createTCP(address) : createUDP(address);
}
public CompletableFuture<AsyncConnection> create(final boolean tcp, final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return tcp ? createTCP(address, readTimeoutSeconds, writeTimeoutSeconds) : createUDP(address, readTimeoutSeconds, writeTimeoutSeconds);
}
public abstract ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit);
}

View File

@@ -0,0 +1,217 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import org.redkale.util.*;
/**
* 协议处理的IO线程组
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
@ResourceType(AsyncGroup.class)
public class AsyncIOGroup extends AsyncGroup {
private boolean started;
private boolean closed;
AsyncIOThread[] ioThreads;
private AsyncIOThread connectThread;
final int bufferCapacity;
private final AtomicInteger readIndex = new AtomicInteger();
//创建数
final AtomicLong connCreateCounter = new AtomicLong();
//关闭数
final AtomicLong connLivingCounter = new AtomicLong();
//在线数
final AtomicLong connClosedCounter = new AtomicLong();
private ScheduledThreadPoolExecutor timeoutExecutor;
public AsyncIOGroup(final int bufferCapacity, final int bufferPoolSize) {
this(null, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize);
}
public AsyncIOGroup(final ExecutorService workExecutor,
final int iothreads, final int bufferCapacity, final int bufferPoolSize) {
this(null, workExecutor, iothreads, bufferCapacity, ObjectPool.createSafePool(null, null, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
e.clear();
return true;
}));
}
public AsyncIOGroup(String threadPrefixName0, ExecutorService workExecutor,
int iothreads, final int bufferCapacity, ObjectPool<ByteBuffer> safeBufferPool) {
this.bufferCapacity = bufferCapacity;
final String threadPrefixName = threadPrefixName0 == null ? "Redkale-Client-IOThread" : threadPrefixName0;
this.ioThreads = new AsyncIOThread[Math.max(iothreads, 1)];
try {
for (int i = 0; i < this.ioThreads.length; i++) {
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
String name = threadPrefixName + "-" + (i >= 9 ? (i + 1) : ("0" + (i + 1)));
this.ioThreads[i] = new AsyncIOThread(true, name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool);
}
{
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
String name = threadPrefixName.replace("ServletThread", "ConnectThread").replace("Redkale-Client-IOThread", "Redkale-Client-ConnectThread");
this.connectThread = new AsyncIOThread(false, name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName(threadPrefixName + "-Timeout");
t.setDaemon(true);
return t;
});
}
public int size() {
return this.ioThreads.length;
}
public void start() {
if (started) return;
if (closed) throw new RuntimeException("group is closed");
for (AsyncIOThread thread : ioThreads) {
thread.start();
}
connectThread.start();
started = true;
}
public void close() {
if (closed) return;
for (AsyncIOThread thread : ioThreads) {
thread.close();
}
connectThread.close();
this.timeoutExecutor.shutdownNow();
closed = true;
}
public AtomicLong getCreateConnectionCount() {
return connCreateCounter;
}
public AtomicLong getClosedConnectionCount() {
return connLivingCounter;
}
public AtomicLong getLivingConnectionCount() {
return connClosedCounter;
}
public AsyncIOThread nextIOThread() {
return ioThreads[Math.abs(readIndex.getAndIncrement()) % ioThreads.length];
}
public AsyncIOThread connectThread() {
return connectThread;
}
@Override
public ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit) {
return timeoutExecutor.schedule(callable, delay, unit);
}
public void interestOpsOr(AsyncIOThread thread, SelectionKey key, int opt) {
if (key == null) return;
if (key.selector() != thread.selector) throw new RuntimeException("NioThread.selector not the same to SelectionKey.selector");
if ((key.interestOps() & opt) != 0) return;
key.interestOps(key.interestOps() | opt);
if (thread.inCurrThread()) return;
//非IO线程中
key.selector().wakeup();
}
@Override
public CompletableFuture<AsyncConnection> createTCP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
SocketChannel channel;
try {
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (IOException e) {
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(e);
return future;
}
AsyncIOThread readThread = nextIOThread();
final AsyncNioTcpConnection conn = new AsyncNioTcpConnection(true, this, readThread, connectThread, channel, null, address, connLivingCounter, connClosedCounter);
final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
conn.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
conn.setReadTimeoutSeconds(readTimeoutSeconds);
conn.setWriteTimeoutSeconds(writeTimeoutSeconds);
connCreateCounter.incrementAndGet();
connLivingCounter.incrementAndGet();
future.complete(conn);
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
return Utility.orTimeout(future, 30, TimeUnit.SECONDS);
}
@Override
public CompletableFuture<AsyncConnection> createUDP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
DatagramChannel channel;
try {
channel = DatagramChannel.open();
} catch (IOException e) {
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(e);
return future;
}
AsyncIOThread readThread = nextIOThread();
AsyncNioUdpConnection conn = new AsyncNioUdpConnection(true, this, readThread, connectThread, channel, null, address, connLivingCounter, connClosedCounter);
CompletableFuture future = new CompletableFuture();
conn.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
future.complete(conn);
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
return future;
}
}

View File

@@ -0,0 +1,133 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;
import java.util.logging.*;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class AsyncIOThread extends WorkThread {
protected static final Logger logger = Logger.getLogger(AsyncIOThread.class.getSimpleName());
final Selector selector;
final AtomicInteger connCounter = new AtomicInteger();
private final Supplier<ByteBuffer> bufferSupplier;
private final Consumer<ByteBuffer> bufferConsumer;
private final ConcurrentLinkedQueue<Consumer<Selector>> registers = new ConcurrentLinkedQueue<>();
private boolean closed;
int invoker = 0;
public AsyncIOThread(final boolean readable, String name, ExecutorService workExecutor, Selector selector,
ObjectPool<ByteBuffer> unsafeBufferPool, ObjectPool<ByteBuffer> safeBufferPool) {
super(name, workExecutor, null);
this.selector = selector;
this.setDaemon(true);
this.bufferSupplier = () -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).get();
this.bufferConsumer = (v) -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).accept(v);
}
public void register(Consumer<Selector> consumer) {
registers.offer(consumer);
selector.wakeup();
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferSupplier;
}
public Consumer<ByteBuffer> getBufferConsumer() {
return bufferConsumer;
}
public int currConnections() {
return connCounter.get();
}
@Override
public void run() {
this.localThread = Thread.currentThread();
while (!this.closed) {
try {
Consumer<Selector> register;
while ((register = registers.poll()) != null) {
register.accept(selector);
}
int count = selector.select();
if (count == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
invoker = 0;
if (!key.isValid()) continue;
AsyncNioConnection conn = (AsyncNioConnection) key.attachment();
if (conn.client) {
if (key.isConnectable()) {
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
conn.doConnect();
} else if (key.isReadable()) {
conn.currReadInvoker = 0;
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
conn.doRead(true);
} else if (key.isWritable()) {
conn.currWriteInvoker = 0;
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite(true);
}
} else {
if (key.isReadable()) {
conn.currReadInvoker = 0;
//key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
conn.doRead(true);
} else if (conn.writeCompletionHandler != null && key.isWritable()) {
conn.currWriteInvoker = 0;
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite(true);
} else if (key.isConnectable()) {
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
conn.doConnect();
}
}
}
} catch (Exception ex) {
if (!this.closed) logger.log(Level.FINE, getName() + " selector run failed", ex);
}
}
}
public void close() {
this.closed = true;
this.interrupt();
try {
this.selector.close();
} catch (Exception e) {
logger.log(Level.FINE, getName() + " selector close failed", e);
}
}
}

View File

@@ -3,8 +3,9 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.nio;
package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.*;
@@ -17,7 +18,7 @@ import java.util.concurrent.*;
*
* @since 2.1.0
*/
public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable {
class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable {
private final CompletionHandler<Integer, A> handler;
@@ -25,11 +26,20 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
public ScheduledFuture timeoutFuture;
public NioCompletionHandler(CompletionHandler<Integer, A> handler, A attachment) {
private ByteBuffer[] buffers;
private AsyncNioConnection conn;
public AsyncNioCompletionHandler(CompletionHandler<Integer, A> handler, A attachment) {
this.handler = handler;
this.attachment = attachment;
}
public void setConnBuffers(AsyncNioConnection conn, ByteBuffer... buffs) {
this.conn = conn;
this.buffers = buffs;
}
public void setAttachment(A attachment) {
this.attachment = attachment;
}
@@ -41,6 +51,9 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
this.timeoutFuture = null;
future.cancel(true);
}
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.completed(result, attachment);
}
@@ -51,11 +64,17 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
this.timeoutFuture = null;
future.cancel(true);
}
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.failed(exc, attachment);
}
@Override
public void run() {
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.failed(new TimeoutException(), attachment);
}

View File

@@ -0,0 +1,450 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
import org.redkale.util.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
abstract class AsyncNioConnection extends AsyncConnection {
protected static final int MAX_INVOKER_ONSTACK = Integer.getInteger("net.invoker.max.onstack", 16);
final AsyncIOThread ioThread;
final AsyncIOThread connectThread;
final AsyncIOGroup ioGroup;
protected SocketAddress remoteAddress;
//-------------------------------- 连操作 --------------------------------------
protected Object connectAttachment;
protected CompletionHandler<Void, Object> connectCompletionHandler;
protected boolean connectPending;
protected SelectionKey connectKey;
//-------------------------------- 读操作 --------------------------------------
protected int readTimeoutSeconds;
int currReadInvoker;
protected ByteBuffer readByteBuffer;
protected CompletionHandler<Integer, ByteBuffer> readCompletionHandler;
protected boolean readPending;
protected SelectionKey readKey;
//-------------------------------- 写操作 --------------------------------------
protected int writeTimeoutSeconds;
int currWriteInvoker;
protected byte[] writeByteTuple1Array;
protected int writeByteTuple1Offset;
protected int writeByteTuple1Length;
protected byte[] writeByteTuple2Array;
protected int writeByteTuple2Offset;
protected int writeByteTuple2Length;
//写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值
protected ByteBuffer writeByteBuffer;
protected ByteBuffer[] writeByteBuffers;
protected int writeOffset;
protected int writeLength;
protected Object writeAttachment;
protected CompletionHandler<Integer, Object> writeCompletionHandler;
protected boolean writePending;
protected SelectionKey writeKey;
public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
final int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, bufferCapacity, bufferPool, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.connectThread = connectThread;
}
public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
final int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.connectThread = connectThread;
}
@Override
public SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
protected void continueRead() {
if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = implRegister(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
handleRead(0, e);
}
});
} else {
ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), null);
return;
}
if (this.readPending) {
handler.failed(new ReadPendingException(), null);
return;
}
this.readPending = true;
if (this.readTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, this.readByteBuffer);
this.readCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.readCompletionHandler = handler;
}
doRead(currReadInvoker < MAX_INVOKER_ONSTACK && this.ioThread.inCurrThread());
}
@Override
public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, CompletionHandler<Integer, Void> handler) {
Objects.requireNonNull(headerContent);
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), null);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), null);
return;
}
this.writePending = true;
this.writeByteTuple1Array = headerContent;
this.writeByteTuple1Offset = headerOffset;
this.writeByteTuple1Length = headerLength;
this.writeByteTuple2Array = bodyContent;
this.writeByteTuple2Offset = bodyOffset;
this.writeByteTuple2Length = bodyLength;
this.writeAttachment = null;
if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, null);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = new AsyncNioCompletionHandler(handler, null);
}
doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(src);
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), attachment);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), attachment);
return;
}
this.writePending = true;
this.writeByteBuffer = src;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(srcs);
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), attachment);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), attachment);
return;
}
this.writePending = true;
this.writeByteBuffers = srcs;
this.writeOffset = offset;
this.writeLength = length;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null
}
public void doRead(boolean direct) {
try {
this.readtime = System.currentTimeMillis();
int readCount = 0;
if (direct) {
currReadInvoker++;
if (this.readByteBuffer == null) {
this.readByteBuffer = pollReadBuffer();
if (this.readTimeoutSeconds > 0 && this.readCompletionHandler != null) {
((AsyncNioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer);
}
}
readCount = implRead(readByteBuffer);
}
if (readCount != 0) {
handleRead(readCount, null);
} else if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = implRegister(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
handleRead(0, e);
}
});
} else {
if (client || !direct) ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
} catch (Exception e) {
handleRead(0, e);
}
}
public void doWrite(boolean direct) {
try {
this.writetime = System.currentTimeMillis();
final boolean invokeDirect = direct;
int totalCount = 0;
boolean hasRemain = true;
if (invokeDirect) currWriteInvoker++;
while (invokeDirect && hasRemain) {
if (writeByteTuple1Array != null) {
final ByteBuffer buffer = pollWriteBuffer();
if (buffer.remaining() >= writeByteTuple1Length + writeByteTuple2Length) {
buffer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length);
if (writeByteTuple2Length > 0) buffer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length);
buffer.flip();
writeByteBuffer = buffer;
writeByteTuple1Array = null;
writeByteTuple1Offset = 0;
writeByteTuple1Length = 0;
writeByteTuple2Array = null;
writeByteTuple2Offset = 0;
writeByteTuple2Length = 0;
} else {
ByteBufferWriter writer = ByteBufferWriter.create(getBufferSupplier(), buffer);
writer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length);
if (writeByteTuple2Length > 0) writer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length);
final ByteBuffer[] buffers = writer.toBuffers();
writeByteBuffers = buffers;
writeOffset = 0;
writeLength = buffers.length;
writeByteTuple1Array = null;
writeByteTuple1Offset = 0;
writeByteTuple1Length = 0;
writeByteTuple2Array = null;
writeByteTuple2Offset = 0;
writeByteTuple2Length = 0;
}
if (writeByteBuffer == null) {
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffers);
} else {
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffer);
}
}
int writeCount;
if (writeByteBuffer != null) {
writeCount = implWrite(writeByteBuffer);
hasRemain = writeByteBuffer.hasRemaining();
} else {
writeCount = implWrite(writeByteBuffers, writeOffset, writeLength);
boolean remain = false;
for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) {
if (writeByteBuffers[i].hasRemaining()) {
remain = true;
break;
}
}
hasRemain = remain;
}
if (writeCount <= 0) {
if (totalCount == 0) totalCount = writeCount;
break;
} else {
totalCount += writeCount;
}
if (!hasRemain) break;
}
if (totalCount != 0 || !hasRemain) {
handleWrite(totalCount, null);
} else if (writeKey == null) {
ioThread.register(selector -> {
try {
writeKey = implRegister(selector, SelectionKey.OP_WRITE);
writeKey.attach(this);
} catch (ClosedChannelException e) {
handleWrite(0, e);
}
});
} else {
ioGroup.interestOpsOr(ioThread, writeKey, SelectionKey.OP_WRITE);
}
} catch (IOException e) {
handleWrite(0, e);
}
}
protected void handleConnect(Throwable t) {
if (connectKey != null) {
connectKey.cancel();
connectKey = null;
}
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
this.connectCompletionHandler = null;
this.connectAttachment = null;
this.connectPending = false;//必须放最后
if (handler != null) {
if (t == null) {
handler.completed(null, attach);
} else {
handler.failed(t, attach);
}
}
}
protected void handleRead(final int totalCount, Throwable t) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
//清空读参数
this.readCompletionHandler = null;
this.readByteBuffer = null;
this.readPending = false; //必须放最后
if (handler == null) {
if (t == null) {
protocolCodec.completed(totalCount, attach);
} else {
protocolCodec.failed(t, attach);
}
} else {
if (t == null) {
handler.completed(totalCount, attach);
} else {
handler.failed(t, attach);
}
}
}
protected void handleWrite(final int totalCount, Throwable t) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
//清空写参数
this.writeCompletionHandler = null;
this.writeAttachment = null;
this.writeByteBuffer = null;
this.writeByteBuffers = null;
this.writeOffset = 0;
this.writeLength = 0;
this.writePending = false; //必须放最后
if (t == null) {
handler.completed(totalCount, attach);
} else {
handler.failed(t, attach);
}
}
protected abstract SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException;
protected abstract int implRead(ByteBuffer dst) throws IOException;
protected abstract int implWrite(ByteBuffer src) throws IOException;
protected abstract int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException;
public abstract boolean isConnected();
public abstract void doConnect();
}

View File

@@ -0,0 +1,201 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
class AsyncNioTcpConnection extends AsyncNioConnection {
private final SocketChannel channel;
public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
ioThread.connCounter.incrementAndGet();
}
public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return true;
}
@Override
public boolean shutdownInput() {
try {
this.channel.shutdownInput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public boolean shutdownOutput() {
try {
this.channel.shutdownOutput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public boolean isConnected() {
return this.channel.isConnected();
}
@Override
protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException {
return this.channel.register(sel, ops);
}
@Override
protected int implRead(ByteBuffer dst) throws IOException {
return this.channel.read(dst);
}
@Override
protected int implWrite(ByteBuffer src) throws IOException {
return this.channel.write(src);
}
@Override
protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException {
return (int) this.channel.write(srcs, offset, length);
}
public <A> void connect(SocketAddress remote, A attachment, CompletionHandler<Void, ? super A> handler) {
if (channel.isConnected()) {
throw new AlreadyConnectedException();
}
if (connectPending) {
throw new ConnectionPendingException();
}
connectPending = true;
this.connectAttachment = attachment;
this.connectCompletionHandler = (CompletionHandler<Void, Object>) handler;
this.remoteAddress = remote;
doConnect();
}
@Override
public void doConnect() {
try {
boolean connected = channel.isConnectionPending();
if (connected || channel.connect(remoteAddress)) {
connected = channel.finishConnect();
}
if (connected) {
handleConnect(null);
} else if (connectKey == null) {
connectThread.register(selector -> {
try {
connectKey = channel.register(selector, SelectionKey.OP_CONNECT);
connectKey.attach(this);
} catch (ClosedChannelException e) {
handleConnect(e);
}
});
} else {
handleConnect(new IOException());
}
} catch (IOException e) {
handleConnect(e);
}
}
@Override
public final void close() throws IOException {
super.close();
ioThread.connCounter.decrementAndGet();
channel.shutdownInput();
channel.shutdownOutput();
channel.close();
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
}
}

View File

@@ -11,8 +11,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.net.*;
import org.redkale.net.nio.*;
import java.util.function.*;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
@@ -24,23 +24,23 @@ import org.redkale.util.*;
*
* @since 2.1.0
*/
public class TcpNioProtocolServer extends ProtocolServer {
private ObjectPool<ByteBuffer> bufferPool;
private ObjectPool<Response> responsePool;
class AsyncNioTcpProtocolServer extends ProtocolServer {
private ServerSocketChannel serverChannel;
private Selector selector;
private NioThreadGroup ioGroup;
private AsyncIOGroup ioGroup;
private Thread acceptThread;
private boolean closed;
public TcpNioProtocolServer(Context context) {
private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
public AsyncNioTcpProtocolServer(Context context) {
super(context);
}
@@ -83,21 +83,38 @@ public class TcpNioProtocolServer extends ProtocolServer {
}
@Override
public void accept(Server server) throws IOException {
public void accept(Application application, Server server) throws IOException {
this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
this.bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
this.responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
this.responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
this.ioGroup = new NioThreadGroup(Runtime.getRuntime().availableProcessors(), context.executor, bufferPool);
ObjectPool<Response> safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
final int threads = Runtime.getRuntime().availableProcessors();
ThreadLocal<ObjectPool<Response>> localResponsePool = ThreadLocal.withInitial(() -> {
if (!(Thread.currentThread() instanceof WorkThread)) return null;
return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(),
safeResponsePool.getCycleCounter(), 16, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler());
});
this.responseSupplier = () -> {
ObjectPool<Response> pool = localResponsePool.get();
return pool == null ? safeResponsePool.get() : pool.get();
};
this.responseConsumer = (v) -> {
ObjectPool<Response> pool = localResponsePool.get();
(pool == null ? safeResponsePool : pool).accept(v);
};
final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread");
this.ioGroup = new AsyncIOGroup(threadPrefixName, null, threads, server.bufferCapacity, bufferPool);
this.ioGroup.start();
this.acceptThread = new Thread() {
{
setName(threadPrefixName.replace("ServletThread", "AcceptThread"));
}
@Override
public void run() {
while (!closed) {
@@ -128,9 +145,13 @@ public class TcpNioProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
NioThread ioThread = ioGroup.nextThread();
AsyncConnection conn = new TcpNioAsyncConnection(ioGroup, ioThread, context.executor, bufferPool, channel, context.getSSLContext(), null, livingCounter, closedCounter);
new PrepareRunner(context, responsePool, conn, null, null).run();
AsyncIOThread readThread = ioGroup.nextIOThread();
ioGroup.connCreateCounter.incrementAndGet();
ioGroup.connLivingCounter.incrementAndGet();
AsyncNioTcpConnection conn = new AsyncNioTcpConnection(false, ioGroup, readThread, ioGroup.connectThread(), channel, context.getSSLContext(), null, ioGroup.connLivingCounter, ioGroup.connClosedCounter);
ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn);
conn.protocolCodec = codec;
codec.run(null);
}
@Override
@@ -143,4 +164,18 @@ public class TcpNioProtocolServer extends ProtocolServer {
this.selector.close();
}
@Override
public long getCreateConnectionCount() {
return ioGroup.connCreateCounter.get();
}
@Override
public long getClosedConnectionCount() {
return ioGroup.connClosedCounter.get();
}
@Override
public long getLivingConnectionCount() {
return ioGroup.connLivingCounter.get();
}
}

View File

@@ -0,0 +1,171 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
class AsyncNioUdpConnection extends AsyncNioConnection {
private final DatagramChannel channel;
public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, DatagramChannel ch,
SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
DatagramChannel ch, SSLContext sslContext, final SocketAddress addr0,
AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return false;
}
@Override
public boolean shutdownInput() {
return true;
}
@Override
public boolean shutdownOutput() {
return true;
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public boolean isConnected() {
if (!client) return true;
return this.channel.isConnected();
}
@Override
protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException {
return this.channel.register(sel, ops);
}
@Override
protected int implRead(ByteBuffer dst) throws IOException {
return this.channel.read(dst);
}
@Override
protected int implWrite(ByteBuffer src) throws IOException {
return this.channel.send(src, remoteAddress);
}
@Override
protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException {
int end = offset + length;
for (int i = offset; i < end; i++) {
ByteBuffer buf = srcs[i];
if (buf.hasRemaining()) return this.channel.send(buf, remoteAddress);
}
return 0;
}
public <A> void connect(SocketAddress remote, A attachment, CompletionHandler<Void, ? super A> handler) {
this.connectAttachment = attachment;
this.connectCompletionHandler = (CompletionHandler<Void, Object>) handler;
this.remoteAddress = remote;
doConnect();
}
@Override
public void doConnect() {
try {
channel.connect(remoteAddress);
handleConnect(null);
} catch (IOException e) {
handleConnect(e);
}
}
@Override
public final void close() throws IOException {
super.close();
if (client) channel.close(); //不能关闭channel
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
}
}

View File

@@ -0,0 +1,169 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
* 协议底层Server
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
class AsyncNioUdpProtocolServer extends ProtocolServer {
private DatagramChannel serverChannel;
private Selector selector;
private AsyncIOGroup ioGroup;
private Thread acceptThread;
private boolean closed;
private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
public AsyncNioUdpProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
this.serverChannel = DatagramChannel.open();
this.serverChannel.configureBlocking(false);
this.selector = Selector.open();
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
}
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
}
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
}
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
}
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public void accept(Application application, Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> safeBufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
final int threads = Runtime.getRuntime().availableProcessors();
ThreadLocal<ObjectPool<Response>> localResponsePool = ThreadLocal.withInitial(() -> {
if (!(Thread.currentThread() instanceof WorkThread)) return null;
return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(),
safeResponsePool.getCycleCounter(), 16, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler());
});
this.responseSupplier = () -> {
ObjectPool<Response> pool = localResponsePool.get();
return pool == null ? safeResponsePool.get() : pool.get();
};
this.responseConsumer = (v) -> {
ObjectPool<Response> pool = localResponsePool.get();
(pool == null ? safeResponsePool : pool).accept(v);
};
final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread");
this.ioGroup = new AsyncIOGroup(threadPrefixName, null, threads, server.bufferCapacity, safeBufferPool);
this.ioGroup.start();
this.serverChannel.register(this.selector, SelectionKey.OP_READ);
this.acceptThread = new Thread() {
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
{
setName(threadPrefixName.replace("ServletThread", "AcceptThread"));
}
@Override
public void run() {
while (!closed) {
final ByteBuffer buffer = unsafeBufferPool.get();
try {
SocketAddress address = serverChannel.receive(buffer);
buffer.flip();
accept(address, buffer);
} catch (Throwable t) {
unsafeBufferPool.accept(buffer);
}
}
}
};
this.acceptThread.start();
}
private void accept(SocketAddress address, ByteBuffer buffer) throws IOException {
AsyncIOThread readThread = ioGroup.nextIOThread();
ioGroup.connCreateCounter.incrementAndGet();
ioGroup.connLivingCounter.incrementAndGet();
AsyncNioUdpConnection conn = new AsyncNioUdpConnection(false, ioGroup, readThread, ioGroup.connectThread(), this.serverChannel, context.getSSLContext(), address, ioGroup.connLivingCounter, ioGroup.connClosedCounter);
ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn);
conn.protocolCodec = codec;
codec.run(buffer);
}
@Override
public void close() throws IOException {
if (this.closed) return;
this.closed = true;
this.ioGroup.close();
this.serverChannel.close();
}
@Override
public long getCreateConnectionCount() {
return -1;
}
@Override
public long getClosedConnectionCount() {
return -1;
}
@Override
public long getLivingConnectionCount() {
return -1;
}
}

View File

@@ -28,7 +28,9 @@ public class Context {
protected final long serverStartTime;
//Server的线程池
protected final ThreadPoolExecutor executor;
protected final ExecutorService workExecutor;
protected final ThreadHashExecutor workHashExecutor;
//SSL
protected final SSLContext sslContext;
@@ -73,17 +75,17 @@ public class Context {
protected Charset charset;
public Context(ContextConfig config) {
this(config.serverStartTime, config.logger, config.executor, config.sslContext,
this(config.serverStartTime, config.logger, config.workExecutor, config.sslContext,
config.bufferCapacity, config.maxconns, config.maxbody, config.charset, config.address, config.resourceFactory,
config.prepare, config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds);
}
public Context(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext,
public Context(long serverStartTime, Logger logger, ExecutorService workExecutor, SSLContext sslContext,
int bufferCapacity, final int maxconns, final int maxbody, Charset charset, InetSocketAddress address,
ResourceFactory resourceFactory, PrepareServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) {
this.serverStartTime = serverStartTime;
this.logger = logger;
this.executor = executor;
this.workExecutor = workExecutor;
this.sslContext = sslContext;
this.bufferCapacity = bufferCapacity;
this.maxconns = maxconns;
@@ -97,6 +99,57 @@ public class Context {
this.writeTimeoutSeconds = writeTimeoutSeconds;
this.jsonFactory = JsonFactory.root();
this.bsonFactory = BsonFactory.root();
if (workExecutor instanceof ThreadHashExecutor) {
this.workHashExecutor = (ThreadHashExecutor) workExecutor;
} else {
this.workHashExecutor = null;
}
}
protected void executePrepareServlet(Request request, Response response) {
if (workHashExecutor != null) {
workHashExecutor.execute(request.getHashid(), () -> prepare.prepare(request, response));
} else if (workExecutor != null) {
workExecutor.execute(() -> prepare.prepare(request, response));
} else {
prepare.prepare(request, response);
}
}
public void execute(Servlet servlet, Request request, Response response) {
if (workHashExecutor != null) {
workHashExecutor.execute(request.getHashid(), () -> {
try {
long cha = System.currentTimeMillis() - request.getCreatetime();
servlet.execute(request, response);
if (cha > 1000 && response.context.logger.isLoggable(Level.WARNING)) {
response.context.logger.log(Level.WARNING, "hash execute servlet delays=" + cha + "ms, request=" + request);
} else if (cha > 100 && response.context.logger.isLoggable(Level.FINE)) {
response.context.logger.log(Level.FINE, "hash execute servlet delay=" + cha + "ms, request=" + request);
}
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t);
response.finish(true);
}
});
} else if (workExecutor != null) {
workExecutor.execute(() -> {
try {
servlet.execute(request, response);
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t);
response.finish(true);
}
});
} else {
try {
servlet.execute(request, response);
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t);
response.finish(true);
}
}
}
public ResourceFactory getResourceFactory() {
@@ -127,22 +180,6 @@ public class Context {
return charset;
}
public Future<?> submitAsync(Runnable r) {
return executor.submit(r);
}
public void runAsync(Runnable r) {
executor.execute(r);
}
public int getCorePoolSize() {
return executor.getCorePoolSize();
}
public ThreadFactory getThreadFactory() {
return executor.getThreadFactory();
}
public int getBufferCapacity() {
return bufferCapacity;
}
@@ -177,7 +214,7 @@ public class Context {
public long serverStartTime;
//Server的线程池
public ThreadPoolExecutor executor;
public ExecutorService workExecutor;
//SSL
public SSLContext sslContext;

View File

@@ -1,183 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.nio.*;
import java.nio.channels.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.*;
import org.redkale.util.*;
/**
* 根Servlet的处理逻辑类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@SuppressWarnings("unchecked")
public class PrepareRunner implements Runnable {
private final AsyncConnection channel;
private final Context context;
private final ObjectPool<Response> responsePool;
private ByteBuffer data;
private Response response;
public PrepareRunner(Context context, ObjectPool<Response> responsePool, AsyncConnection channel, ByteBuffer data, Response response) {
this.context = context;
this.responsePool = responsePool;
this.channel = channel;
this.data = data;
this.response = response;
}
@Override
public void run() {
if (data != null) { //BIO模式的UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
if (response == null) response = responsePool.get();
try {
response.init(channel);
codec(data, response);
} catch (Throwable t) {
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
return;
}
if (response == null) response = responsePool.get();
try {
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer count, ByteBuffer buffer) {
if (count < 1) {
buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
return;
}
// { //测试
// buffer.flip();
// byte[] bs = new byte[buffer.remaining()];
// buffer.get(bs);
// System.println(new String(bs));
// }
buffer.flip();
try {
response.init(channel);
codec(buffer, response);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
if (exc != null && context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc);
}
}
});
} catch (Exception te) {
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te);
}
}
}
protected void codec(final ByteBuffer buffer, final Response response) throws IOException {
final Request request = response.request;
final PrepareServlet preparer = context.prepare;
preparer.executeCounter.incrementAndGet();
final int rs = request.readHeader(buffer);
if (rs < 0) { //表示数据格式不正确
channel.offerBuffer(buffer);
if (rs != Integer.MIN_VALUE) preparer.illRequestCounter.incrementAndGet();
response.finish(true);
} else if (rs == 0) {
if (buffer.hasRemaining()) {
request.setMoredata(buffer);
} else {
buffer.clear();
channel.setReadBuffer(buffer);
}
preparer.prepare(request, response);
} else {
buffer.clear();
channel.setReadBuffer(buffer);
final AtomicInteger ai = new AtomicInteger(rs);
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
ai.addAndGet(-request.readBody(attachment));
if (ai.get() > 0) {
attachment.clear();
channel.setReadBuffer(attachment);
channel.read(this);
} else {
if (attachment.hasRemaining()) {
request.setMoredata(attachment);
} else {
attachment.clear();
channel.setReadBuffer(attachment);
}
try {
preparer.prepare(request, response);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免preparer.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
preparer.illRequestCounter.incrementAndGet();
attachment.clear();
channel.setReadBuffer(attachment);
response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc);
}
});
}
}
protected void initResponse(Response response, AsyncConnection channel) {
response.init(channel);
}
protected Response pollResponse() {
return responsePool.get();
}
protected Request pollRequest(Response response) {
return response.request;
}
protected AsyncConnection removeChannel(Response response) {
return response.removeChannel();
}
}

View File

@@ -9,6 +9,7 @@ import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.Predicate;
import java.util.logging.Level;
import org.redkale.util.*;
/**
@@ -207,11 +208,16 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
@SuppressWarnings("unchecked")
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
public final void prepare(final R request, final P response) throws IOException {
public final void prepare(final R request, final P response) {
try {
request.prepare();
response.filter = this.headFilter;
response.servlet = this;
response.nextEvent();
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
protected AnyValue getServletConf(Servlet servlet) {

View File

@@ -0,0 +1,171 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.function.*;
import java.util.logging.Level;
/**
*
* @author zhangjx
*/
class ProtocolCodec implements CompletionHandler<Integer, ByteBuffer> {
private final AsyncConnection channel;
private final Context context;
private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
private Response resp;
public ProtocolCodec(Context context, Supplier<Response> responseSupplier,
Consumer<Response> responseConsumer, AsyncConnection channel) {
this.context = context;
this.channel = channel;
this.responseSupplier = responseSupplier;
this.responseConsumer = responseConsumer;
}
public ProtocolCodec response(Response resp) {
this.resp = resp;
return this;
}
private Response createResponse() {
Response response = resp;
if (response == null) {
response = responseSupplier.get();
} else {
response.prepare();
}
response.responseSupplier = responseSupplier;
response.responseConsumer = responseConsumer;
return response;
}
@Override
public void completed(Integer count, ByteBuffer buffer) {
if (count < 1) {
channel.offerBuffer(buffer);
channel.dispose(); // response.init(channel); 在调用之前异常
return;
}
// { //测试
// buffer.flip();
// byte[] bs = new byte[buffer.remaining()];
// buffer.get(bs);
// System.println(new String(bs));
// }
buffer.flip();
final Response response = createResponse();
try {
decode(buffer, response, 0, null);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
channel.offerBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
if (exc != null && context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc);
}
}
public void run(final ByteBuffer data) {
if (data != null) { //pipeline模式或UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
final Response response = createResponse();
try {
decode(data, response, 0, null);
} catch (Throwable t) {
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
return;
}
try {
channel.read(this);
} catch (Exception te) {
channel.dispose();// response.init(channel); 在调用之前异常
if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te);
}
}
}
protected void decode(final ByteBuffer buffer, final Response response, final int pipelineIndex, final Request lastreq) {
response.init(channel);
final Request request = response.request;
final int rs = request.readHeader(buffer, lastreq);
if (rs < 0) { //表示数据格式不正确
final PrepareServlet preparer = context.prepare;
preparer.executeCounter.incrementAndGet();
channel.offerBuffer(buffer);
if (rs != Integer.MIN_VALUE) preparer.illRequestCounter.incrementAndGet();
response.finish(true);
if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "request.readHeader erroneous (" + rs + "), force to close channel ");
}
} else if (rs == 0) {
final PrepareServlet preparer = context.prepare;
preparer.executeCounter.incrementAndGet();
int pindex = pipelineIndex;
boolean pipeline = false;
Request hreq = lastreq;
if (buffer.hasRemaining()) {
pipeline = true;
if (pindex == 0) pindex++;
request.pipeline(pindex, pindex + 1);
if (hreq == null) hreq = request.copyHeader();
} else {
request.pipeline(pindex, pindex);
channel.setReadBuffer((ByteBuffer) buffer.clear());
}
context.executePrepareServlet(request, response);
if (pipeline) {
final Response pipelineResponse = createResponse();
try {
decode(buffer, pipelineResponse, pindex + 1, hreq);
} catch (Throwable t) { //此处不可 offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare pipeline servlet abort, force to close channel ", t);
pipelineResponse.finish(true);
}
}
} else {
channel.setReadBuffer(buffer);
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer count, ByteBuffer attachment) {
if (count < 1) {
channel.offerBuffer(attachment);
channel.dispose();
return;
}
attachment.flip();
decode(attachment, response, pipelineIndex, lastreq);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
context.prepare.illRequestCounter.incrementAndGet();
channel.offerBuffer(attachment);
response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc);
}
});
}
}
}

View File

@@ -8,7 +8,8 @@ package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.util.AnyValue;
/**
@@ -21,20 +22,14 @@ import org.redkale.util.AnyValue;
*/
public abstract class ProtocolServer {
//创建数
protected final AtomicLong createCounter = new AtomicLong();
//关闭数
protected final AtomicLong closedCounter = new AtomicLong();
//在线数
protected final AtomicLong livingCounter = new AtomicLong();
protected final Context context;
//最大连接数小于1表示无限制
protected int maxconns;
@Resource
protected Application application;
public abstract void open(AnyValue config) throws IOException;
public abstract void bind(SocketAddress local, int backlog) throws IOException;
@@ -43,7 +38,7 @@ public abstract class ProtocolServer {
public abstract <T> void setOption(SocketOption<T> name, T value) throws IOException;
public abstract void accept(Server server) throws IOException;
public abstract void accept(Application application, Server server) throws IOException;
public abstract void close() throws IOException;
@@ -52,45 +47,28 @@ public abstract class ProtocolServer {
this.maxconns = context.getMaxconns();
}
public long getCreateCount() {
return createCounter.longValue();
}
public long getClosedCount() {
return closedCounter.longValue();
}
public long getLivingCount() {
return livingCounter.longValue();
}
//---------------------------------------------------------------------
public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader, String netimpl) {
if (netimpl != null) netimpl = netimpl.trim();
if ("TCP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) {
return new TcpAioProtocolServer(context);
} else if ("aio".equalsIgnoreCase(netimpl)) {
return new TcpAioProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) {
return new TcpNioProtocolServer(context);
}
return new AsyncNioTcpProtocolServer(context);
} else if ("UDP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) {
return null;// return new UdpBioProtocolServer(context);
} else if ("bio".equalsIgnoreCase(netimpl)) {
return null;// return new UdpBioProtocolServer(context);
}
return new AsyncNioUdpProtocolServer(context);
} else if (netimpl == null || netimpl.isEmpty()) {
throw new RuntimeException("ProtocolServer not support protocol " + protocol);
throw new RuntimeException(ProtocolServer.class.getSimpleName() + " not support protocol " + protocol);
}
try {
if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
Class clazz = classLoader.loadClass(netimpl);
return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context);
} catch (Exception e) {
throw new RuntimeException("ProtocolServer(netimple=" + netimpl + ") newinstance error", e);
throw new RuntimeException(ProtocolServer.class.getSimpleName() + "(netimple=" + netimpl + ") newinstance error", e);
}
}
public abstract long getCreateConnectionCount();
public abstract long getClosedConnectionCount();
public abstract long getLivingConnectionCount();
}

View File

@@ -9,7 +9,6 @@ import java.nio.ByteBuffer;
import java.util.*;
import org.redkale.convert.bson.BsonConvert;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.ObjectPool;
/**
* 协议请求对象
@@ -24,8 +23,6 @@ public abstract class Request<C extends Context> {
protected final C context;
protected final ObjectPool<ByteBuffer> bufferPool;
protected final BsonConvert bsonConvert;
protected final JsonConvert jsonConvert;
@@ -34,9 +31,13 @@ public abstract class Request<C extends Context> {
protected boolean keepAlive;
protected boolean more; //pipeline模式
protected int pipelineIndex;
protected ByteBuffer moredata; //pipeline模式
protected int pipelineCount;
protected boolean pipelineOver;
protected int hashid;
protected AsyncConnection channel;
@@ -48,48 +49,41 @@ public abstract class Request<C extends Context> {
protected final Map<String, Object> attributes = new HashMap<>();
protected Request(C context, ObjectPool<ByteBuffer> bufferPool) {
protected Request(C context) {
this.context = context;
this.bufferPool = bufferPool;
this.bsonConvert = context.getBsonConvert();
this.jsonConvert = context.getJsonConvert();
}
protected void setMoredata(ByteBuffer buffer) {
this.moredata = buffer;
protected Request copyHeader() {
return null;
}
protected ByteBuffer removeMoredata() {
ByteBuffer rs = this.moredata;
this.moredata = null;
return rs;
protected Request pipeline(int pipelineIndex, int pipelineCount) {
this.pipelineIndex = pipelineIndex;
this.pipelineCount = pipelineCount;
return this;
}
/**
* 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。
*
* @param buffer ByteBuffer对象
* @param last 同一Channel的上一个Request
*
* @return 缺少的字节数
*/
protected abstract int readHeader(ByteBuffer buffer);
/**
* 读取buffer并返回读取的有效数据长度
*
* @param buffer ByteBuffer对象
*
* @return 有效数据长度
*/
protected abstract int readBody(ByteBuffer buffer);
protected abstract int readHeader(ByteBuffer buffer, Request last);
protected abstract void prepare();
protected void recycle() {
hashid = 0;
createtime = 0;
pipelineIndex = 0;
pipelineCount = 0;
pipelineOver = false;
keepAlive = false;
more = false;
moredata = null;
attributes.clear();
channel = null; // close it by response
}
@@ -140,4 +134,13 @@ public abstract class Request<C extends Context> {
return createtime;
}
public int getHashid() {
return hashid;
}
public Request<C> hashid(int hashid) {
this.hashid = hashid;
return this;
}
}

View File

@@ -10,7 +10,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.function.*;
import java.util.logging.Level;
import org.redkale.util.ObjectPool;
import org.redkale.util.ByteTuple;
/**
* 协议响应对象
@@ -27,7 +27,9 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected final C context;
protected final ObjectPool<Response> responsePool; //虚拟构建的Response可能不存在responsePool
protected Supplier<Response> responseSupplier; //虚拟构建的Response可能不存在responseSupplier
protected Consumer<Response> responseConsumer; //虚拟构建的Response可能不存在responseConsumer
protected final R request;
@@ -43,20 +45,26 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected Servlet<C, R, ? extends Response<C, R>> servlet;
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
private final CompletionHandler finishBytesHandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
finish();
}
@Override
public void failed(Throwable exc, Void attachment) {
finish(true);
}
};
private final CompletionHandler finishBufferHandler = new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
channel.offerBuffer(attachment);
ByteBuffer data = request.removeMoredata();
final boolean more = data != null && request.keepAlive;
request.more = more;
finish();
if (more) new PrepareRunner(context, responsePool, request.channel, null, Response.this).run();
}
}
@Override
@@ -67,51 +75,33 @@ public abstract class Response<C extends Context, R extends Request<C>> {
};
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
private final CompletionHandler finishBuffersHandler = new CompletionHandler<Integer, ByteBuffer[]>() {
@Override
public void completed(final Integer result, final ByteBuffer[] attachments) {
int index = -1;
for (int i = 0; i < attachments.length; i++) {
if (attachments[i].hasRemaining()) {
index = i;
break;
}
}
if (index >= 0) {
channel.write(attachments, index, attachments.length - index, attachments, this);
} else {
if (attachments != null) {
for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
}
ByteBuffer data = request.removeMoredata();
final boolean more = data != null && request.keepAlive;
request.more = more;
finish();
if (more) new PrepareRunner(context, responsePool, request.channel, null, Response.this).run();
}
finish();
}
@Override
public void failed(Throwable exc, final ByteBuffer[] attachments) {
if (attachments != null) {
for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
}
}
finish(true);
}
};
protected Response(C context, final R request, ObjectPool<Response> responsePool) {
protected Response(C context, final R request) {
this.context = context;
this.request = request;
this.responsePool = responsePool;
}
protected void offerBuffer(ByteBuffer... buffers) {
for (ByteBuffer buffer : buffers) {
channel.offerBuffer(buffer);
}
}
protected AsyncConnection removeChannel() {
@@ -123,6 +113,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected void prepare() {
inited = true;
request.prepare();
}
protected boolean recycle() {
@@ -130,11 +121,14 @@ public abstract class Response<C extends Context, R extends Request<C>> {
this.output = null;
this.filter = null;
this.servlet = null;
boolean notpipeline = request.pipelineIndex == 0 || request.pipelineOver;
request.recycle();
if (channel != null) {
channel.dispose();
if (notpipeline) channel.dispose();
channel = null;
}
this.responseSupplier = null;
this.responseConsumer = null;
this.inited = false;
return true;
}
@@ -205,73 +199,127 @@ public abstract class Response<C extends Context, R extends Request<C>> {
}
this.recycleListener = null;
}
if (request.more) removeChannel();
if (request.keepAlive && !request.more && channel != null) {
if (channel.isOpen()) {
if (request.keepAlive && (request.pipelineIndex == 0 || request.pipelineOver)) {
AsyncConnection conn = removeChannel();
if (conn != null && conn.protocolCodec != null) {
this.responseConsumer.accept(this);
conn.read(conn.protocolCodec);
} else {
Supplier<Response> poolSupplier = this.responseSupplier;
Consumer<Response> poolConsumer = this.responseConsumer;
this.recycle();
this.prepare();
new PrepareRunner(context, this.responsePool, conn, null, this).run();
} else {
channel.dispose();
new ProtocolCodec(context, poolSupplier, poolConsumer, conn).response(this).run(null);
}
} else {
this.responsePool.accept(this);
this.responseConsumer.accept(this);
}
}
public void finish(final byte[] bs) {
if (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) {
ByteBuffer buffer = channel.getBufferSupplier().get();
buffer.put(bs);
buffer.flip();
this.finish(buffer);
} else {
this.finish(ByteBuffer.wrap(bs));
}
public final void finish(final byte[] bs) {
finish(false, bs, 0, bs.length);
}
public void finish(ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭
final AsyncConnection conn = this.channel;
// ByteBuffer data = this.request.removeMoredata();
// final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffer, buffer, finishHandler);
// if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
public final void finish(final byte[] bs, int offset, int length) {
finish(false, bs, offset, length);
}
public void finish(boolean kill, ByteBuffer buffer) {
public final void finish(final ByteTuple array) {
finish(false, array.content(), array.offset(), array.length());
}
public final void finish(boolean kill, final byte[] bs) {
finish(kill, bs, 0, bs.length);
}
public final void finish(boolean kill, final ByteTuple array) {
finish(kill, array.content(), array.offset(), array.length());
}
public void finish(boolean kill, final byte[] bs, int offset, int length) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive();
final AsyncConnection conn = this.channel;
// ByteBuffer data = this.request.removeMoredata();
// final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffer, buffer, finishHandler);
// if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
if (this.channel.hasPipelineData()) {
this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
channel.write(bs, offset, length, finishBytesHandler);
}
public void finish(ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭
final AsyncConnection conn = this.channel;
// ByteBuffer data = this.request.removeMoredata();
// final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffers, buffers, finishHandler2);
// if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
@Override
public void failed(Throwable exc, Void attachment) {
finishBytesHandler.failed(exc, attachment);
}
});
} else {
this.channel.write(bs, offset, length, finishBytesHandler);
}
}
public void finish(boolean kill, ByteBuffer... buffers) {
protected final void finish(ByteBuffer buffer) {
finish(false, buffer);
}
protected final void finish(ByteBuffer... buffers) {
finish(false, buffers);
}
protected void finish(boolean kill, ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive();
final AsyncConnection conn = this.channel;
ByteBuffer data = this.request.removeMoredata();
final boolean more = data != null && this.request.keepAlive;
this.request.more = more;
conn.write(buffers, buffers, finishHandler2);
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
if (this.channel.hasPipelineData()) {
this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
channel.write(buffer, buffer, finishBufferHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBufferHandler.failed(exc, buffer);
}
});
} else {
this.channel.write(buffer, buffer, finishBufferHandler);
}
}
protected void finish(boolean kill, ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive();
if (this.channel.hasPipelineData()) {
this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
channel.write(buffers, buffers, finishBuffersHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBuffersHandler.failed(exc, buffers);
}
});
} else {
this.channel.write(buffers, buffers, finishBuffersHandler);
}
}
protected <A> void send(final ByteTuple array, final CompletionHandler<Integer, Void> handler) {
this.channel.write(array, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (handler != null) handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, Void attachment) {
if (handler != null) handler.failed(exc, attachment);
}
});
}
protected <A> void send(final ByteBuffer buffer, final A attachment, final CompletionHandler<Integer, A> handler) {
@@ -279,13 +327,9 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override
public void completed(Integer result, A attachment) {
if (buffer.hasRemaining()) {
channel.write(buffer, attachment, this);
} else {
channel.offerBuffer(buffer);
if (handler != null) handler.completed(result, attachment);
}
}
@Override
public void failed(Throwable exc, A attachment) {
@@ -301,21 +345,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override
public void completed(Integer result, A attachment) {
int index = -1;
for (int i = 0; i < buffers.length; i++) {
if (buffers[i].hasRemaining()) {
index = i;
break;
}
channel.offerBuffer(buffers[i]);
}
if (index == 0) {
channel.write(buffers, attachment, this);
} else if (index > 0) {
ByteBuffer[] newattachs = new ByteBuffer[buffers.length - index];
System.arraycopy(buffers, index, newattachs, 0, newattachs.length);
channel.write(newattachs, attachment, this);
} else if (handler != null) handler.completed(result, attachment);
channel.offerBuffer(buffers);
if (handler != null) handler.completed(result, attachment);
}
@Override

View File

@@ -9,12 +9,11 @@ import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import javax.net.ssl.SSLContext;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
@@ -33,7 +32,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT";
public static final String RESNAME_SERVER_EXECUTOR = "SERVER_EXECUTOR";
@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR";
public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY";
@@ -47,7 +47,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
protected String name;
//应用层协议名
protected final String protocol;
protected final String netprotocol;
//依赖注入工厂类
protected final ResourceFactory resourceFactory;
@@ -82,12 +82,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//ByteBuffer的容量大小
protected int bufferCapacity;
//线程数
protected int threads;
//线程池
protected ThreadPoolExecutor workExecutor;
//ByteBuffer池大小
protected int bufferPoolSize;
@@ -109,9 +103,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//IO写入 的超时秒数小于1视为不设置
protected int writeTimeoutSeconds;
protected Server(long serverStartTime, String protocol, ResourceFactory resourceFactory, PrepareServlet<K, C, R, P, S> servlet) {
protected Server(Application application, long serverStartTime, String netprotocol, ResourceFactory resourceFactory, PrepareServlet<K, C, R, P, S> servlet) {
this.serverStartTime = serverStartTime;
this.protocol = protocol;
this.netprotocol = netprotocol;
this.resourceFactory = resourceFactory;
this.prepare = servlet;
}
@@ -127,12 +121,11 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0);
this.backlog = parseLenth(config.getValue("backlog"), 1024);
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(protocol) ? 1350 : 32 * 1024);
this.bufferCapacity = "UDP".equalsIgnoreCase(protocol) ? bufCapacity : (bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity);
this.threads = config.getIntValue("threads", Math.max(8, Runtime.getRuntime().availableProcessors() * 2));
this.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2);
this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort());
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(netprotocol) ? 1350 : 32 * 1024);
this.bufferCapacity = "UDP".equalsIgnoreCase(netprotocol) ? bufCapacity : (bufCapacity < 1024 ? 1024 : bufCapacity);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", Runtime.getRuntime().availableProcessors() * 2);
this.name = config.getValue("name", "Server-" + (config == null ? netprotocol : config.getValue("protocol", netprotocol).replaceFirst("\\..+", "").toUpperCase()) + "-" + this.address.getPort());
if (!this.name.matches("^[a-zA-Z][\\w_-]{1,64}$")) throw new RuntimeException("server.name (" + this.name + ") is illegal");
AnyValue sslConf = config.getAnyValue("ssl");
if (sslConf != null) {
@@ -148,14 +141,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.resourceFactory.inject(creator);
this.sslContext = creator.create(this, sslConf);
}
final AtomicInteger counter = new AtomicInteger();
final Format f = createFormat();
final String n = name;
this.workExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new WorkThread(workExecutor, r);
t.setName("Redkale-" + n + "-ServletThread-" + f.format(counter.incrementAndGet()));
return t;
});
}
protected static int parseLenth(String value, int defValue) {
@@ -187,10 +172,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return resourceFactory;
}
public ThreadPoolExecutor getWorkExecutor() {
return workExecutor;
}
public InetSocketAddress getSocketAddress() {
return address;
}
@@ -199,8 +180,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return name;
}
public String getProtocol() {
return protocol;
public String getNetprotocol() {
return netprotocol;
}
public Logger getLogger() {
@@ -231,10 +212,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return bufferCapacity;
}
public int getThreads() {
return threads;
}
public int getBufferPoolSize() {
return bufferPoolSize;
}
@@ -263,36 +240,32 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return maxconns;
}
public void setThreads(int threads) {
int oldthreads = this.threads;
this.context.executor.setCorePoolSize(threads);
this.threads = threads;
logger.info("[" + Thread.currentThread().getName() + "] " + this.getClass().getSimpleName() + " change threads from " + oldthreads + " to " + threads);
}
@SuppressWarnings("unchecked")
public void addServlet(S servlet, final Object attachment, AnyValue conf, K... mappings) {
this.prepare.addServlet(servlet, attachment, conf, mappings);
}
public void start() throws IOException {
this.context = this.createContext();
public void start(Application application) throws IOException {
this.context = this.createContext(application);
this.prepare.init(this.context, config);
this.serverChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
this.serverChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
if (application != null) { //main函数调试时可能为null
application.getResourceFactory().inject(this.serverChannel);
}
this.serverChannel.open(config);
serverChannel.bind(address, backlog);
serverChannel.accept(this);
serverChannel.accept(application, this);
final String threadName = "[" + Thread.currentThread().getName() + "] ";
postStart();
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
+ ", cpu: " + Runtime.getRuntime().availableProcessors() + ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol)) + " listen: " + (address.getHostString() + ":" + address.getPort())
+ ", cpu: " + Runtime.getRuntime().availableProcessors() + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
}
protected void postStart() {
}
public void changeAddress(final InetSocketAddress addr) throws IOException {
public void changeAddress(Application application, final InetSocketAddress addr) throws IOException {
long s = System.currentTimeMillis();
Objects.requireNonNull(addr);
final InetSocketAddress oldAddress = context.address;
@@ -300,10 +273,10 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
context.address = addr;
ProtocolServer newServerChannel = null;
try {
newServerChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
newServerChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
newServerChannel.open(config);
newServerChannel.bind(addr, backlog);
newServerChannel.accept(this);
newServerChannel.accept(application, this);
} catch (IOException e) {
context.address = oldAddress;
throw e;
@@ -311,7 +284,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.address = context.address;
this.serverChannel = newServerChannel;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol))
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol))
+ " change address listen: " + address + ", started in " + (System.currentTimeMillis() - s) + " ms");
if (oldServerChannel != null) {
new Thread() {
@@ -360,7 +333,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
if (this.context != null) this.context.aliveTimeoutSeconds = newAliveTimeoutSeconds;
}
protected abstract C createContext();
protected abstract C createContext(Application application);
//必须在 createContext()之后调用
protected abstract ObjectPool<ByteBuffer> createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize);
@@ -368,17 +341,14 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//必须在 createContext()之后调用
protected abstract ObjectPool<Response> createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize);
//必须在 createResponsePool()之后调用
protected abstract Creator<Response> createResponseCreator(ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool);
public void shutdown() throws IOException {
long s = System.currentTimeMillis();
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdowning");
logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdowning");
try {
this.serverChannel.close();
} catch (Exception e) {
}
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdow prepare servlet");
logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdow prepare servlet");
this.prepare.destroy(this.context, config);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
@@ -449,25 +419,17 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//创建数
public long getCreateConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getCreateCount();
return serverChannel == null ? -1 : serverChannel.getCreateConnectionCount();
}
//关闭数
public long getClosedConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getClosedCount();
return serverChannel == null ? -1 : serverChannel.getClosedConnectionCount();
}
//在线数
public long getLivingConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getLivingCount();
}
protected Format createFormat() {
String sf = "0";
if (this.threads > 10) sf = "00";
if (this.threads > 100) sf = "000";
if (this.threads > 1000) sf = "0000";
return new DecimalFormat(sf);
return serverChannel == null ? -1 : serverChannel.getLivingConnectionCount();
}
public static URL[] loadLib(final RedkaleClassLoader classLoader, final Logger logger, final String lib) throws Exception {

View File

@@ -1,545 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
import org.redkale.net.AsyncConnection;
import org.redkale.net.nio.NioCompletionHandler;
import org.redkale.net.nio.NioThread;
import org.redkale.net.nio.NioThreadGroup;
import org.redkale.util.ObjectPool;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class TcpNioAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private final SocketAddress remoteAddress;
final SocketChannel channel;
final NioThread ioThread;
final NioThreadGroup ioGroup;
final ExecutorService workExecutor;
//连
private Object connectAttachment;
private CompletionHandler<Void, Object> connectCompletionHandler;
private boolean connectPending;
private SelectionKey connectKey;
//读操作
private ByteBuffer readByteBuffer;
private CompletionHandler<Integer, ByteBuffer> readCompletionHandler;
private boolean readPending;
private SelectionKey readKey;
//写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值
private ByteBuffer writeByteBuffer;
private ByteBuffer[] writeByteBuffers;
private int writeOffset;
private int writeLength;
private Object writeAttachment;
private CompletionHandler<Integer, Object> writeCompletionHandler;
private boolean writePending;
private SelectionKey writeKey;
public TcpNioAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, ExecutorService workExecutor,
ObjectPool<ByteBuffer> bufferPool, SocketChannel ch,
SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(bufferPool, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.workExecutor = workExecutor;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
public TcpNioAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, ExecutorService workExecutor,
Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0,
AtomicLong livingCounter, AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.workExecutor = workExecutor;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return true;
}
@Override
public boolean shutdownInput() {
try {
this.channel.shutdownInput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public boolean shutdownOutput() {
try {
this.channel.shutdownOutput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), pollReadBuffer());
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), pollReadBuffer()));
}
return;
}
if (this.readPending) {
if (this.workExecutor == null) {
handler.failed(new ReadPendingException(), pollReadBuffer());
} else {
this.workExecutor.execute(() -> handler.failed(new ReadPendingException(), pollReadBuffer()));
}
return;
}
this.readPending = true;
if (this.readTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, this.readByteBuffer);
this.readCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.readCompletionHandler = handler;
}
doRead();
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(src);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), attachment));
}
return;
}
if (this.writePending) {
if (this.workExecutor == null) {
handler.failed(new WritePendingException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new WritePendingException(), attachment));
}
return;
}
this.writePending = true;
this.writeByteBuffer = src;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(srcs);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), attachment));
}
return;
}
if (this.writePending) {
if (this.workExecutor == null) {
handler.failed(new WritePendingException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new WritePendingException(), attachment));
}
return;
}
this.writePending = true;
this.writeByteBuffers = srcs;
this.writeOffset = offset;
this.writeLength = length;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
public void doConnect() {
try {
boolean connected = channel.isConnectionPending();
if (connected || channel.connect(remoteAddress)) {
connected = channel.finishConnect();
}
if (connected) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(null, attach);
} else {
this.workExecutor.execute(() -> handler.completed(null, attach));
}
}
} else if (connectKey == null) {
ioThread.register(selector -> {
try {
connectKey = channel.register(selector, SelectionKey.OP_CONNECT);
connectKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
IOException e = new IOException();
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
} catch (IOException e) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearConnect() {
this.connectCompletionHandler = null;
this.connectAttachment = null;
this.connectPending = false;//必须放最后
}
public void doRead() {
try {
final boolean invokeDirect = this.ioThread.inSameThread();
int totalCount = 0;
boolean hasRemain = true;
if (invokeDirect && this.readByteBuffer == null) {
this.readByteBuffer = pollReadBuffer();
if (this.readTimeoutSeconds > 0) {
((NioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer);
}
}
while (invokeDirect && hasRemain) {
int readCount = this.channel.read(readByteBuffer);
hasRemain = readByteBuffer.hasRemaining();
if (readCount <= 0) {
if (totalCount == 0) totalCount = readCount;
break;
}
totalCount += readCount;
}
if (totalCount != 0 || !hasRemain) {
if (readKey != null) readKey.interestOps(readKey.interestOps() & ~SelectionKey.OP_READ);
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(totalCount, attach);
} else {
final int totalCount0 = totalCount;
this.workExecutor.execute(() -> handler.completed(totalCount0, attach));
}
}
} else if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = channel.register(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
} catch (Exception e) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearRead() {
this.readCompletionHandler = null;
this.readByteBuffer = null;
this.readPending = false; //必须放最后
}
public void doWrite() {
try {
final boolean invokeDirect = this.ioThread.inSameThread();
int totalCount = 0;
boolean hasRemain = true;
while (invokeDirect && hasRemain) {
int writeCount;
if (writeByteBuffer != null) {
writeCount = channel.write(writeByteBuffer);
hasRemain = writeByteBuffer.hasRemaining();
} else {
writeCount = (int) channel.write(writeByteBuffers, writeOffset, writeLength);
boolean remain = false;
for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) {
if (writeByteBuffers[i].hasRemaining()) {
remain = true;
break;
}
}
hasRemain = remain;
}
if (writeCount <= 0) {
if (totalCount == 0) totalCount = writeCount;
break;
}
totalCount += writeCount;
}
if (totalCount > 0 || !hasRemain) {
if (writeKey != null) writeKey.interestOps(writeKey.interestOps() & ~SelectionKey.OP_WRITE);
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(totalCount, attach);
} else {
final int totalCount0 = totalCount;
this.workExecutor.execute(() -> handler.completed(totalCount0, attach));
}
}
} else if (writeKey == null) {
ioThread.register(selector -> {
try {
writeKey = channel.register(selector, SelectionKey.OP_WRITE);
writeKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
ioGroup.interestOpsOr(ioThread, writeKey, SelectionKey.OP_WRITE);
}
} catch (IOException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearWrite() {
this.writeCompletionHandler = null;
this.writeAttachment = null;
this.writeByteBuffer = null;
this.writeByteBuffers = null;
this.writeOffset = 0;
this.writeLength = 0;
this.writePending = false; //必须放最后
}
@Override
public final void close() throws IOException {
super.close();
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
channel.close();
}
}

View File

@@ -30,7 +30,7 @@ import org.redkale.util.*;
*/
public final class Transport {
public static final String DEFAULT_PROTOCOL = "TCP";
public static final String DEFAULT_NETPROTOCOL = "TCP";
protected final AtomicInteger seq = new AtomicInteger(-1);
@@ -40,17 +40,16 @@ public final class Transport {
protected final boolean tcp;
protected final String protocol;
protected final String netprotocol;
protected final AsynchronousChannelGroup group;
//传输端的AsyncGroup
protected final AsyncGroup asyncGroup;
protected final InetSocketAddress clientAddress;
//不可能为null
protected TransportNode[] transportNodes = new TransportNode[0];
protected final ObjectPool<ByteBuffer> bufferPool;
protected final SSLContext sslContext;
//负载均衡策略
@@ -59,23 +58,20 @@ public final class Transport {
//连接上限, 为null表示无限制
protected Semaphore semaphore;
protected Transport(String name, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
protected Transport(String name, TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this(name, DEFAULT_PROTOCOL, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy);
this(name, DEFAULT_NETPROTOCOL, factory, asyncGroup, sslContext, clientAddress, addresses, strategy);
}
protected Transport(String name, String protocol, final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
protected Transport(String name, String netprotocol, final TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this.name = name;
this.protocol = protocol;
this.netprotocol = netprotocol;
this.factory = factory;
factory.transportReferences.add(new WeakReference<>(this));
this.tcp = "TCP".equalsIgnoreCase(protocol);
this.group = transportChannelGroup;
this.tcp = "TCP".equalsIgnoreCase(netprotocol);
this.asyncGroup = asyncGroup;
this.sslContext = sslContext;
this.bufferPool = transportBufferPool;
this.clientAddress = clientAddress;
this.strategy = strategy;
updateRemoteAddresses(addresses);
@@ -190,44 +186,19 @@ public final class Transport {
@Override
public String toString() {
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}";
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + netprotocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}";
}
public ByteBuffer pollBuffer() {
return bufferPool.get();
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferPool;
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
}
public AsynchronousChannelGroup getTransportChannelGroup() {
return group;
}
public String getProtocol() {
return protocol;
public String getNetprotocol() {
return netprotocol;
}
public boolean isTCP() {
return tcp;
}
protected CompletableFuture<AsyncConnection> pollAsync(TransportNode node, SocketAddress addr, Supplier<CompletableFuture<AsyncConnection>> func, final int count) {
if (count >= 5) {
CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("create AsyncConnection error"));
return future;
}
final BlockingQueue<AsyncConnection> queue = node.conns;
protected CompletableFuture<AsyncConnection> pollAsync(TransportNode node, SocketAddress addr, Supplier<CompletableFuture<AsyncConnection>> func) {
final BlockingQueue<AsyncConnection> queue = node.connQueue;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
@@ -239,24 +210,16 @@ public final class Transport {
}
}
if (semaphore != null && !semaphore.tryAcquire()) {
return CompletableFuture.supplyAsync(() -> {
try {
return queue.poll(1, TimeUnit.SECONDS);
} catch (Exception t) {
return null;
}
}, factory.executor).thenCompose((conn2) -> {
if (conn2 != null && conn2.isOpen()) {
return CompletableFuture.completedFuture(conn2);
}
return pollAsync(node, addr, func, count + 1);
});
final CompletableFuture<AsyncConnection> future = Utility.orTimeout(new CompletableFuture<>(), 10, TimeUnit.SECONDS);
future.whenComplete((r, t) -> node.pollQueue.remove(future));
if (node.pollQueue.offer(future)) return future;
future.completeExceptionally(new IOException("create transport connection error"));
return future;
}
return func.get().thenApply(conn -> {
if (conn != null && semaphore != null) conn.beforeCloseListener((c) -> semaphore.release());
return conn;
});
}
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) {
@@ -269,17 +232,12 @@ public final class Transport {
try {
if (!tcp) { // UDP
SocketAddress udpaddr = rand ? nodes[0].address : addr;
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(udpaddr);
return CompletableFuture.completedFuture(AsyncConnection.create(bufferPool, channel, sslContext, udpaddr, true, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
return asyncGroup.createUDP(udpaddr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
}
if (!rand) { //指定地址
TransportNode node = findTransportNode(addr);
if (node == null) {
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
}
return pollAsync(node, addr, () -> AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds), 1);
if (node == null) return asyncGroup.createTCP(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
return pollAsync(node, addr, () -> asyncGroup.createTCP(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
}
//---------------------随机取地址------------------------
@@ -292,7 +250,7 @@ public final class Transport {
final long now = System.currentTimeMillis();
if (enablecount > 0) { //存在可用的地址
final TransportNode one = newnodes[Math.abs(seq.incrementAndGet()) % enablecount];
final BlockingQueue<AsyncConnection> queue = one.conns;
final BlockingQueue<AsyncConnection> queue = one.connQueue;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
@@ -304,52 +262,11 @@ public final class Transport {
}
}
return pollAsync(one, one.getAddress(), () -> {
CompletableFuture future = new CompletableFuture();
AsynchronousSocketChannel channel0 = null;
try {
channel0 = AsynchronousSocketChannel.open(group);
channel0.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel0.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel0.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (Exception ex) {
ex.printStackTrace();
}
final AsynchronousSocketChannel channel = channel0;
channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
}
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
try {
channel.close();
} catch (Exception e) {
}
try {
pollConnection0(nodes, one, now).whenComplete((r, t) -> {
if (t != null) {
future.completeExceptionally(t);
} else {
future.complete(r);
}
return asyncGroup.createTCP(one.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds)
.whenComplete((c, t) -> {
one.disabletime = t == null ? 0 : System.currentTimeMillis();
});
} catch (Exception e) {
future.completeExceptionally(e);
}
}
});
return future;
}, 1);
}
return pollConnection0(nodes, null, now);
} catch (Exception ex) {
@@ -359,42 +276,14 @@ public final class Transport {
private CompletableFuture<AsyncConnection> pollConnection0(TransportNode[] nodes, TransportNode exclude, long now) throws IOException {
//从可用/不可用的地址列表中创建连接
AtomicInteger count = new AtomicInteger(nodes.length);
CompletableFuture future = new CompletableFuture();
for (final TransportNode node : nodes) {
if (node == exclude) continue;
if (future.isDone()) return future;
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.connect(node.address, node, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
try {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
} catch (Exception e) {
failed(e, attachment);
}
}
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
if (count.decrementAndGet() < 1) {
future.completeExceptionally(exc);
}
try {
channel.close();
} catch (Exception e) {
}
}
asyncGroup.createTCP(node.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds)
.whenComplete((c, t) -> {
if (c != null && !future.complete(c)) node.connQueue.offer(c);
node.disabletime = t == null ? 0 : System.currentTimeMillis();
});
}
return future;
@@ -405,7 +294,7 @@ public final class Transport {
if (!forceClose && conn.isTCP()) {
if (conn.isOpen()) {
TransportNode node = findTransportNode(conn.getRemoteAddress());
if (node == null || !node.conns.offer(conn)) conn.dispose();
if (node == null || !node.connQueue.offer(conn)) conn.dispose();
} else {
conn.dispose();
}
@@ -459,25 +348,29 @@ public final class Transport {
protected volatile long disabletime; //不可用时的时间, 为0表示可用
protected final BlockingQueue<AsyncConnection> conns;
protected final BlockingQueue<AsyncConnection> connQueue;
protected final ArrayBlockingQueue<CompletableFuture<AsyncConnection>> pollQueue;
protected final ConcurrentHashMap<String, Object> attributes = new ConcurrentHashMap<>();
public TransportNode(int poolmaxconns, InetSocketAddress address) {
this.address = address;
this.disabletime = 0;
this.conns = new ArrayBlockingQueue<>(poolmaxconns);
this.connQueue = new ArrayBlockingQueue<>(poolmaxconns);
this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100);
}
@ConstructorParameters({"poolmaxconns", "address", "disabletime"})
public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) {
this.address = address;
this.disabletime = disabletime;
this.conns = new LinkedBlockingQueue<>(poolmaxconns);
this.connQueue = new LinkedBlockingQueue<>(poolmaxconns);
this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100);
}
public int getPoolmaxconns() {
return this.conns.remainingCapacity() + this.conns.size();
return this.connQueue.remainingCapacity() + this.connQueue.size();
}
public <T> T setAttribute(String name, T value) {
@@ -518,13 +411,13 @@ public final class Transport {
}
@ConvertDisabled
public BlockingQueue<AsyncConnection> getConns() {
return conns;
public BlockingQueue<AsyncConnection> getConnQueue() {
return connQueue;
}
public void dispose() {
AsyncConnection conn;
while ((conn = conns.poll()) != null) {
while ((conn = connQueue.poll()) != null) {
conn.dispose();
}
}

View File

@@ -5,15 +5,12 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.Supplier;
import java.util.logging.*;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
@@ -45,14 +42,8 @@ public class TransportFactory {
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
//传输端的线程池
protected final ExecutorService executor;
//传输端的ByteBuffer对象池
protected final ObjectPool<ByteBuffer> bufferPool;
//传输端的ChannelGroup
protected final AsynchronousChannelGroup channelGroup;
//传输端的AsyncGroup
protected final AsyncGroup asyncGroup;
//每个地址对应的Group名
protected final Map<InetSocketAddress, String> groupAddrs = new HashMap<>();
@@ -90,23 +81,25 @@ public class TransportFactory {
//pong的数据长度, 小于0表示不进行判断
protected int pongLength;
//是否TCP
protected String netprotocol = "TCP";
//负载均衡策略
protected final TransportStrategy strategy;
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
this.executor = executor;
this.bufferPool = bufferPool;
this.channelGroup = channelGroup;
protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol,
int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
this.asyncGroup = asyncGroup;
this.sslContext = sslContext;
this.netprotocol = netprotocol;
this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
this.strategy = strategy;
}
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
this(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol,
int readTimeoutSeconds, int writeTimeoutSeconds) {
this(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
@@ -143,71 +136,28 @@ public class TransportFactory {
}
}
public static TransportFactory create(int threads) {
return create(threads, threads * 2, 8 * 1024, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
public static TransportFactory create(AsyncGroup asyncGroup, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(asyncGroup, null, "TCP", readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity) {
return create(threads, bufferPoolSize, bufferCapacity, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(asyncGroup, sslContext, "TCP", readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity, int readTimeoutSeconds, int writeTimeoutSeconds) {
final ObjectPool<ByteBuffer> transportPool = new ObjectPool<>(new AtomicLong(), new AtomicLong(), bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
e.clear();
return true;
});
final AtomicInteger counter = new AtomicInteger();
ExecutorService transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t;
});
AsynchronousChannelGroup transportGroup = null;
try {
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (IOException e) {
throw new RuntimeException(e);
}
return create(transportExec, transportPool, transportGroup, readTimeoutSeconds, writeTimeoutSeconds);
public static TransportFactory create(AsyncGroup asyncGroup, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(asyncGroup, null, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
return new TransportFactory(executor, bufferPool, channelGroup, null, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, SSLContext sslContext) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, strategy);
public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, "TCP", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
return new Transport(name, "TCP", this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
public Transport createTransport(String name, String netprotocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, netprotocol, this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy);
}
public String findGroupName(InetSocketAddress addr) {
@@ -269,17 +219,13 @@ public class TransportFactory {
if (info == null) continue;
addresses.addAll(info.addresses);
}
if (info == null) info = new TransportGroupInfo("TCP");
if (info == null) {
info = new TransportGroupInfo(netprotocol);
} else {
info.protocol = netprotocol;
}
if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
public ExecutorService getExecutor() {
return executor;
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferPool;
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.asyncGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
public List<TransportGroupInfo> getGroupInfos() {
@@ -306,11 +252,6 @@ public class TransportFactory {
public void shutdownNow() {
if (this.scheduler != null) this.scheduler.shutdownNow();
try {
this.channelGroup.shutdownNow();
} catch (Exception e) {
logger.log(Level.FINER, "close transportChannelGroup erroneous", e);
}
}
private void checks() {
@@ -324,21 +265,11 @@ public class TransportFactory {
Transport.TransportNode[] nodes = transport.getTransportNodes();
for (final Transport.TransportNode node : nodes) {
if (node.disabletime < 1) continue; //可用
try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(transport.group);
channel.connect(node.address, node, new CompletionHandler<Void, Transport.TransportNode>() {
@Override
public void completed(Void result, Transport.TransportNode attachment) {
attachment.disabletime = 0;
}
@Override
public void failed(Throwable exc, Transport.TransportNode attachment) {
attachment.disabletime = System.currentTimeMillis();
}
CompletableFuture<AsyncConnection> future = Utility.orTimeout(asyncGroup.createTCP(node.address), 2, TimeUnit.SECONDS);
future.whenComplete((r, t) -> {
node.disabletime = t == null ? 0 : System.currentTimeMillis();
if (r != null) r.dispose();
});
} catch (Exception e) {
}
}
}
for (WeakReference ref : nulllist) {
@@ -353,7 +284,7 @@ public class TransportFactory {
if (transport == null) continue;
Transport.TransportNode[] nodes = transport.getTransportNodes();
for (final Transport.TransportNode node : nodes) {
final BlockingQueue<AsyncConnection> queue = node.conns;
final BlockingQueue<AsyncConnection> queue = node.connQueue;
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作
@@ -365,10 +296,6 @@ public class TransportFactory {
localconn.write(sendBuffer, sendBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer wbuffer) {
if (wbuffer.hasRemaining()) {
localconn.write(wbuffer, wbuffer, this);
return;
}
localconn.read(new CompletionHandler<Integer, ByteBuffer>() {
int counter = 0;

View File

@@ -1,178 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
class UdpBioAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private final DatagramChannel channel;
private final SocketAddress remoteAddress;
private final boolean client;
public UdpBioAsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
final DatagramChannel ch, final SSLContext sslContext, SocketAddress addr0, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
this.client = client0;
this.readTimeoutSeconds = readTimeoutSeconds0;
this.writeTimeoutSeconds = writeTimeoutSeconds0;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
public final SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public boolean shutdownInput() {
return false;
}
@Override
public boolean shutdownOutput() {
return false;
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = 0;
for (int i = offset; i < offset + length; i++) {
rs += channel.send(srcs[i], remoteAddress);
if (i != offset) Thread.sleep(10);
}
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (Exception e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
ByteBuffer dst = pollReadBuffer();
try {
int rs = channel.read(dst);
this.readtime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, dst);
} catch (IOException e) {
if (handler != null) handler.failed(e, dst);
}
}
@Override
public final ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public final WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public final void close() throws IOException {
super.close();
if (client) channel.close();
}
@Override
public final boolean isOpen() {
return channel.isOpen();
}
@Override
public final boolean isTCP() {
return false;
}
}

View File

@@ -1,132 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.util.*;
/**
* 协议底层Server
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class UdpBioProtocolServer extends ProtocolServer {
private boolean running;
private DatagramChannel serverChannel;
public UdpBioProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
DatagramChannel ch = DatagramChannel.open();
ch.configureBlocking(true);
this.serverChannel = ch;
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
}
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
}
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
}
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
}
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public void accept(Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
final DatagramChannel serchannel = this.serverChannel;
final int readTimeoutSeconds = this.context.readTimeoutSeconds;
final int writeTimeoutSeconds = this.context.writeTimeoutSeconds;
final CountDownLatch cdl = new CountDownLatch(1);
this.running = true;
new Thread() {
@Override
public void run() {
cdl.countDown();
while (running) {
final ByteBuffer buffer = bufferPool.get();
try {
SocketAddress address = serchannel.receive(buffer);
buffer.flip();
AsyncConnection conn = new UdpBioAsyncConnection(bufferPool, bufferPool, serchannel,
context.getSSLContext(), address, false, readTimeoutSeconds, writeTimeoutSeconds, null, null);
context.runAsync(new PrepareRunner(context, responsePool, conn, buffer, null));
} catch (Exception e) {
bufferPool.accept(buffer);
}
}
}
}.start();
try {
cdl.await();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void close() throws IOException {
this.running = false;
this.serverChannel.close();
}
@Override
public long getCreateCount() {
return -1;
}
@Override
public long getClosedCount() {
return -1;
}
@Override
public long getLivingCount() {
return -1;
}
}

Some files were not shown because too many files have changed in this diff Show More