Redkale 2.3.0 结束

This commit is contained in:
Redkale
2021-04-09 00:28:57 +08:00
parent 4f9c14da97
commit 1917ccf35c
167 changed files with 9725 additions and 5413 deletions

View File

@@ -4,5 +4,5 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" 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 export CLASSPATH=$CLASSPATH:$lib
echo "$APP_HOME" 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

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

View File

@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private int size; 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 * writers used for annotation default and annotation arrays use unnamed
* values. * values.
*/ */
@@ -122,13 +122,13 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param cw * @param cw
* the class writer to which this annotation must be added. * the class writer to which this annotation must be added.
* @param named * @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 * @param bv
* where the annotation values must be stored. * where the annotation values must be stored.
* @param parent * @param parent
* where the number of annotation values must be stored. * where the number of annotation values must be stored.
* @param offset * @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. * be stored.
*/ */
AnnotationWriter(final ClassWriter cw, final boolean named, AnnotationWriter(final ClassWriter cw, final boolean named,
@@ -354,7 +354,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * 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 * @param out
* where the type reference and type path must be put. * where the type reference and type path must be put.
*/ */

View File

@@ -58,13 +58,15 @@
*/ */
package org.redkale.asm; package org.redkale.asm;
import java.util.Arrays;
/** /**
* A non standard class, field, method or code attribute. * A non standard class, field, method or code attribute.
* *
* @author Eric Bruneton * @author Eric Bruneton
* @author Eugene Kuleshov * @author Eugene Kuleshov
*/ */
class Attribute { public class Attribute {
/** /**
* The type of this attribute. * The type of this attribute.
@@ -77,7 +79,7 @@ class Attribute {
byte[] value; 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; Attribute next;
@@ -92,19 +94,19 @@ class Attribute {
} }
/** /**
* Returns <code>true</code> if this type of attribute is unknown. The default * Returns &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is unknown. The default
* implementation of this method always returns <code>true</code>. * 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() { public boolean isUnknown() {
return true; 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() { public boolean isCodeAttribute() {
return false; return false;
@@ -113,7 +115,7 @@ class Attribute {
/** /**
* Returns the labels corresponding to this 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. * this attribute is not a code attribute that contains labels.
*/ */
protected Label[] getLabels() { protected Label[] getLabels() {
@@ -123,7 +125,7 @@ class Attribute {
/** /**
* Reads a {@link #type type} attribute. This method must return a * Reads a {@link #type type} attribute. This method must return a
* <i>new</i> {@link Attribute} object, of type {@link #type type}, * <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. * the given class reader.
* *
* @param cr * @param cr
@@ -146,7 +148,7 @@ class Attribute {
* containing the type and the length of the attribute, are not * containing the type and the length of the attribute, are not
* taken into account here. * taken into account here.
* @param labels * @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. * attribute to be read is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the given * @return a <i>new</i> {@link Attribute} object corresponding to the given
* bytes. * bytes.
@@ -169,11 +171,11 @@ class Attribute {
* class the items that corresponds to this attribute. * class the items that corresponds to this attribute.
* @param code * @param code
* the bytecode of the method corresponding to this 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. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to this * 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. * code attribute.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to this * the maximum stack size of the method corresponding to this
@@ -216,11 +218,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method. * byte arrays, with the {@link #write write} method.
* @param code * @param code
* the bytecode of the method corresponding to these 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. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to * 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. * are not code attributes.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to these * the maximum stack size of the method corresponding to these
@@ -254,11 +256,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method. * byte arrays, with the {@link #write write} method.
* @param code * @param code
* the bytecode of the method corresponding to these 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. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to * 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. * are not code attributes.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to these * the maximum stack size of the method corresponding to these
@@ -281,4 +283,72 @@ class Attribute {
attr = attr.next; 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. * automatically enlarged if necessary.
* *
* @param b * @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. * null bytes into this byte vector.
* @param off * @param off
* index of the fist byte of b that must be copied. * 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) { public ClassReader(final byte[] b, final int off, final int len) {
this.b = b; this.b = b;
// checks the class version // checks the class version
if (readShort(off + 6) > Opcodes.V10) { //if (readShort(off + 6) > Opcodes.V11) {
//throw new IllegalArgumentException(); // throw new IllegalArgumentException();
} //}
// parses the constant pool // parses the constant pool
items = new int[readUnsignedShort(off + 8)]; items = new int[readUnsignedShort(off + 8)];
int n = items.length; int n = items.length;
@@ -205,6 +205,10 @@ public class ClassReader {
case ClassWriter.FLOAT: case ClassWriter.FLOAT:
case ClassWriter.NAME_TYPE: case ClassWriter.NAME_TYPE:
case ClassWriter.INDY: case ClassWriter.INDY:
// @@@ ClassWriter.CONDY
// Enables MethodHandles.lookup().defineClass to function correctly
// when it reads the class name
case 17:
size = 5; size = 5;
break; break;
case ClassWriter.LONG: case ClassWriter.LONG:
@@ -267,7 +271,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). For interfaces, the * {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}. * 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. * {@link Object} class.
* *
* @see ClassVisitor#visit(int, int, String, String, String, String[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
@@ -281,7 +285,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* *
* @return the array of internal names for all implemented interfaces or * @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[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
*/ */
@@ -526,7 +530,7 @@ public class ClassReader {
* , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
*/ */
public void accept(final ClassVisitor classVisitor, final int flags) { 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 * @param v
* start offset in {@link #b b} of the annotations to be read. * start offset in {@link #b b} of the annotations to be read.
* @param visible * @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. * runtime.
*/ */
private void readParameterAnnotations(final MethodVisitor mv, private void readParameterAnnotations(final MethodVisitor mv,
@@ -2474,9 +2478,9 @@ public class ClassReader {
* and the length of the attribute, are not taken into account * and the length of the attribute, are not taken into account
* here. * here.
* @param labels * @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. * 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. * attribute.
*/ */
private Attribute readAttribute(final Attribute[] attrs, final String type, 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 * 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> ] [ * the following order: &#60;tt&#62;visit&#60;/tt&#62; [ &#60;tt&#62;visitSource&#60;/tt&#62; ] [
* <code>visitModule</code> ][ <code>visitOuterClass</code> ] ( <code>visitAnnotation</code> | * &#60;tt&#62;visitModule&#60;/tt&#62; ][ &#60;tt&#62;visitOuterClass&#60;/tt&#62; ] ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* ( * &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* (
* <code>visitInnerClass</code> | <code>visitField</code> | <code>visitMethod</code> )* * &#60;tt&#62;visitInnerClass&#60;/tt&#62; | &#60;tt&#62;visitField&#60;/tt&#62; | &#60;tt&#62;visitMethod&#60;/tt&#62; )*
* <code>visitEnd</code>. * &#60;tt&#62;visitEnd&#60;/tt&#62;.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -104,6 +104,9 @@ public abstract class ClassVisitor {
* calls. May be null. * calls. May be null.
*/ */
public ClassVisitor(final int api, final ClassVisitor cv) { public ClassVisitor(final int api, final ClassVisitor cv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api; this.api = api;
this.cv = cv; this.cv = cv;
} }
@@ -120,18 +123,18 @@ public abstract class ClassVisitor {
* the internal name of the class (see * the internal name of the class (see
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* @param signature * @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 * is not a generic one, and does not extend or implement generic
* classes or interfaces. * classes or interfaces.
* @param superName * @param superName
* the internal of name of the super class (see * the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For * {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be * 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 * @param interfaces
* the internal names of the class's interfaces (see * the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be * {@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, public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) { String superName, String[] interfaces) {
@@ -145,11 +148,11 @@ public abstract class ClassVisitor {
* *
* @param source * @param source
* the name of the source file from which the class was compiled. * 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 * @param debug
* additional debug information to compute the correspondance * additional debug information to compute the correspondance
* between source and compiled elements of the class. May be * 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) { public void visitSource(String source, String debug) {
if (cv != null) { if (cv != null) {
@@ -166,7 +169,7 @@ public abstract class ClassVisitor {
* and {@code ACC_MANDATED}. * and {@code ACC_MANDATED}.
* @param version * @param version
* module version or null. * 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. * this visitor is not interested in visiting this module.
*/ */
public ModuleVisitor visitModule(String name, int access, String version) { 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. * internal name of the enclosing class of the class.
* @param name * @param name
* the name of the method that contains the class, or * 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. * enclosing class.
* @param desc * @param desc
* the descriptor of the method that contains the class, or * 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. * enclosing class.
*/ */
public void visitOuterClass(String owner, String name, String desc) { public void visitOuterClass(String owner, String name, String desc) {
@@ -206,8 +209,8 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @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. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -231,16 +234,19 @@ public abstract class ClassVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * 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 * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @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. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) { TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (cv != null) { if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
} }
@@ -269,10 +275,10 @@ public abstract class ClassVisitor {
* @param outerName * @param outerName
* the internal name of the class to which the inner class * the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}). * 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 * @param innerName
* the (simple) name of the inner class inside its enclosing * 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 * @param access
* the access flags of the inner class as originally declared in * the access flags of the inner class as originally declared in
* the enclosing class. * the enclosing class.
@@ -295,20 +301,20 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type Type}). * the field's descriptor (see {@link Type Type}).
* @param signature * @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. * type does not use generic types.
* @param value * @param value
* the field's initial value. This parameter, which may be * 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 * must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <code>int</code>, * {@link Double} or a {@link String} (for &#60;tt&#62;int&#60;/tt&#62;,
* <code>float</code>, <code>long</code> or <code>String</code> fields * &#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 * respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which * fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in * must be initialized through bytecode instructions in
* constructors or methods. * constructors or methods.
* @return a visitor to visit field annotations and attributes, or * @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. * these annotations and attributes.
*/ */
public FieldVisitor visitField(int access, String name, String desc, 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 * 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. * i.e., it should not return a previously returned visitor.
* *
* @param access * @param access
@@ -333,14 +339,14 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type Type}). * the method's descriptor (see {@link Type Type}).
* @param signature * @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 * parameters, return type and exceptions do not use generic
* types. * types.
* @param exceptions * @param exceptions
* the internal names of the method's exception classes (see * the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be * {@link Type#getInternalName() getInternalName}). May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
* @return an object to visit the byte code of the method, or <code>null</code> * @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 * if this class visitor is not interested in visiting the code of
* this method. * 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 * 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 * 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 * 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> * map frames from scratch. This array associates to each index &#60;tt&#62;i&#60;/tt&#62;
* the Item whose index is <code>i</code>. All Item objects stored in this array * 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 * 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 * 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 * Item from its value. Each Item stores an internal name in its
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
private int compute; 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 * instructions, which need to be expanded into sequences of standard
* bytecode instructions. In this case the class is re-read and re-written * bytecode instructions. In this case the class is re-read and re-written
* with a ClassReader -> ClassWriter chain to perform this transformation. * with a ClassReader -> ClassWriter chain to perform this transformation.
@@ -1297,6 +1297,38 @@ public class ClassWriter extends ClassVisitor {
return result; 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 * 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 * if the constant pool already contains a similar item. <i>This method is
@@ -1486,7 +1518,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @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. * @return a new or already existing method reference item.
*/ */
Item newMethodItem(final String owner, final String name, Item newMethodItem(final String owner, final String name,
@@ -1515,7 +1547,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @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. * @return the index of a new or already existing method reference item.
*/ */
public int newMethod(final String owner, final String name, public int newMethod(final String owner, final String name,
@@ -1778,7 +1810,7 @@ public class ClassWriter extends ClassVisitor {
* @param key * @param key
* a constant pool item. * a constant pool item.
* @return the constant pool's hash table item which is equal to the given * @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) { private Item get(final Item key) {
Item i = items[key.hashCode % items.length]; 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 * A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( <code>visitAnnotation</code> | * the following order: ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* <code>visitEnd</code>. * &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -101,6 +101,9 @@ public abstract class FieldVisitor {
* calls. May be null. * calls. May be null.
*/ */
public FieldVisitor(final int api, final FieldVisitor fv) { public FieldVisitor(final int api, final FieldVisitor fv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api; this.api = api;
this.fv = fv; this.fv = fv;
} }
@@ -111,8 +114,8 @@ public abstract class FieldVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @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. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -132,16 +135,19 @@ public abstract class FieldVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * 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 * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <code>true</code> if the annotation is visible at runtime. * &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if * @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. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) { TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (fv != null) { if (fv != null) {
return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
} }

View File

@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value; 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; 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; 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; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this field. May be * The runtime invisible type annotations of this field. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter itanns; 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; private Attribute attrs;
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type}). * the field's descriptor (see {@link Type}).
* @param signature * @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 * @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, FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) { 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 * 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. * the given label has been changed by this operation.
* *
* @param cw * @param cw
@@ -1413,7 +1413,7 @@ class Frame {
* @param edge * @param edge
* the kind of the {@link Edge} between this label and 'label'. * the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}. * 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. * changed by this operation.
*/ */
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) { 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 * 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. * operation.
* *
* @param cw * @param cw
@@ -1522,7 +1522,7 @@ class Frame {
* an array of types. * an array of types.
* @param index * @param index
* the index of the type that must be merged in 'types'. * 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. * operation.
*/ */
private static boolean merge(final ClassWriter cw, int t, private static boolean merge(final ClassWriter cw, int t,

View File

@@ -99,6 +99,34 @@ public final class Handle {
*/ */
final boolean itf; 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. * 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 * 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; String desc;

View File

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

View File

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

View File

@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions; 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; 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; 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; 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; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this method. May be * The runtime invisible type annotations of this method. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter itanns; private AnnotationWriter itanns;
/** /**
* The runtime visible parameter annotations of this method. May be * The runtime visible parameter annotations of this method. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter[] panns; private AnnotationWriter[] panns;
/** /**
* The runtime invisible parameter annotations of this method. May be * The runtime invisible parameter annotations of this method. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
*/ */
private AnnotationWriter[] ipanns; private AnnotationWriter[] ipanns;
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
private int lastCodeOffset; 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; 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; 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 * 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 * stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block * {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <code>stackSize</code>. * plus &#60;tt&#62;stackSize&#60;/tt&#62;.
*/ */
private int stackSize; 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., * 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 * the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic * 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; private int maxStackSize;
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type}). * the method's descriptor (see {@link Type}).
* @param signature * @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 * @param exceptions
* the internal names of the method's exceptions. May be * the internal names of the method's exceptions. May be
* <code>null</code>. * &#60;tt&#62;null&#60;/tt&#62;.
* @param compute * @param compute
* Indicates what must be automatically computed (see #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 * 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> | * the following order: &#60;tt&#62;visitMainClass&#60;/tt&#62; | ( &#60;tt&#62;visitPackage&#60;/tt&#62; |
* <code>visitRequire</code> | <code>visitExport</code> | <code>visitOpen</code> | * &#60;tt&#62;visitRequire&#60;/tt&#62; | &#60;tt&#62;visitExport&#60;/tt&#62; | &#60;tt&#62;visitOpen&#60;/tt&#62; |
* <code>visitUse</code> | <code>visitProvide</code> )* <code>visitEnd</code>. * &#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...)}, * The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)} * {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can access to * @param modules the qualified names of the modules that can access to
* the public classes of the exported package or * 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) { public void visitExport(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can use deep * @param modules the qualified names of the modules that can use deep
* reflection to the classes of the open package or * 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) { public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {

View File

@@ -80,12 +80,17 @@ public interface Opcodes {
// versions // 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_5 = 0 << 16 | 49;
int V1_6 = 0 << 16 | 50; int V1_6 = 0 << 16 | 50;
int V1_7 = 0 << 16 | 51; int V1_7 = 0 << 16 | 51;
int V1_8 = 0 << 16 | 52; int V1_8 = 0 << 16 | 52;
int V9 = 0 << 16 | 53; int V9 = 0 << 16 | 53;
int V10 = 0 << 16 | 54; int V10 = 0 << 16 | 54;
int V11 = 0 << 16 | 55;
// access flags // access flags

View File

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

View File

@@ -89,6 +89,20 @@ public final class Application {
*/ */
public static final String RESNAME_APP_ADDR = "APP_ADDR"; 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> * 当前Service所属的SNCP Server的地址 类型: SocketAddress、InetSocketAddress、String <br>
*/ */
@@ -106,8 +120,11 @@ public final class Application {
/** /**
* 当前Server的线程池 * 当前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 * 当前Server的ResourceFactory
@@ -123,6 +140,10 @@ public final class Application {
//本地IP地址 //本地IP地址
final InetSocketAddress localAddress; final InetSocketAddress localAddress;
//业务逻辑线程池
//@since 2.3.0
final ExecutorService workExecutor;
//CacheSource 资源 //CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>(); final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -135,6 +156,9 @@ public final class Application {
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用 //SNCP传输端的TransportFactory, 注意: 只给SNCP使用
final TransportFactory sncpTransportFactory; final TransportFactory sncpTransportFactory;
//给客户端使用包含SNCP客户端、自定义数据库客户端连接池
final AsyncGroup asyncGroup;
//第三方服务发现管理接口 //第三方服务发现管理接口
//@since 2.1.0 //@since 2.1.0
final ClusterAgent clusterAgent; final ClusterAgent clusterAgent;
@@ -289,9 +313,6 @@ public final class Application {
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader()); this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------"); logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------");
//------------------配置 <transport> 节点 ------------------ //------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null;
AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null; TransportStrategy strategy = null;
String excludelib0 = null; String excludelib0 = null;
@@ -301,9 +322,9 @@ public final class Application {
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8; int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS; int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS; int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
AtomicLong createBufferCounter = new AtomicLong(); AnyValue executorConf = null;
AtomicLong cycleBufferCounter = new AtomicLong();
if (resources != null) { if (resources != null) {
executorConf = resources.getAnyValue("executor");
AnyValue excludelibConf = resources.getAnyValue("excludelibs"); AnyValue excludelibConf = resources.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value"); if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = resources.getAnyValue("transport"); AnyValue transportConf = resources.getAnyValue("transport");
@@ -316,31 +337,6 @@ public final class Application {
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds); writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2); final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4); bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
final int capacity = bufferCapacity;
transportPool = ObjectPool.createSafePool(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"); AnyValue clusterConf = resources.getAnyValue("cluster");
@@ -424,31 +420,41 @@ public final class Application {
} }
} }
} }
if (transportGroup == null) {
final AtomicInteger counter = new AtomicInteger(); ExecutorService workExecutor0 = null;
transportExec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 8, (Runnable r) -> { if (executorConf != null) {
Thread t = new Thread(r); final AtomicReference<ExecutorService> workref = new AtomicReference<>();
t.setDaemon(true); int executorThreads = executorConf.getIntValue("threads", Math.max(2, Runtime.getRuntime().availableProcessors()));
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet()); boolean executorHash = executorConf.getBoolValue("hash");
return t; if (executorThreads > 0) {
}); final AtomicInteger workcounter = new AtomicInteger();
try { if (executorHash) {
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1); workExecutor0 = new ThreadHashExecutor(executorThreads, (Runnable r) -> {
} catch (Exception e) { int c = workcounter.incrementAndGet();
throw new RuntimeException(e); String threadname = "Redkale-HashWorkThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, workref.get(), r);
return t;
});
} 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);
} }
} }
if (transportPool == null) { this.workExecutor = workExecutor0;
final int capacity = bufferCapacity; this.resourceFactory.register(RESNAME_APP_EXECUTOR, Executor.class, this.workExecutor);
transportPool = ObjectPool.createSafePool(createBufferCounter, cycleBufferCounter, bufferPoolSize, this.resourceFactory.register(RESNAME_APP_EXECUTOR, ExecutorService.class, this.workExecutor);
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false; this.asyncGroup = new AsyncIOGroup(this.workExecutor, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize);
e.clear(); this.resourceFactory.register(RESNAME_APP_GROUP, AsyncGroup.class, this.asyncGroup);
return true;
});
}
this.excludelibs = excludelib0; 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")) 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_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30")); .addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30"));
@@ -470,6 +476,14 @@ public final class Application {
return name; return name;
} }
public ExecutorService getWorkExecutor() {
return workExecutor;
}
public AsyncGroup getAsyncGroup() {
return asyncGroup;
}
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
@@ -677,7 +691,23 @@ public final class Application {
} }
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class); }, 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 (this.clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing"); if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing");
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
@@ -759,7 +789,7 @@ public final class Application {
//------------------------------------------------------------------------ //------------------------------------------------------------------------
for (AnyValue conf : resources.getAnyValues("group")) { for (AnyValue conf : resources.getAnyValues("group")) {
final String group = conf.getValue("name", ""); 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)) { if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol")); throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
} }
@@ -1004,8 +1034,7 @@ public final class Application {
for (final AnyValue serconf : serconfs) { for (final AnyValue serconf : serconfs) {
Thread thread = new Thread() { 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().replaceFirst("\\..+", "") + ":" + serconf.getIntValue("port") + "-Thread");
setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
this.setDaemon(true); this.setDaemon(true);
} }

View File

@@ -46,7 +46,7 @@ public class NodeHttpServer extends NodeServer {
} }
private static Server createServer(Application application, AnyValue serconf) { 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() { public HttpServer getHttpServer() {

View File

@@ -19,7 +19,6 @@ import java.util.concurrent.*;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*; import javax.annotation.*;
import javax.persistence.Transient;
import static org.redkale.boot.Application.*; import static org.redkale.boot.Application.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.net.Filter; import org.redkale.net.Filter;
@@ -154,9 +153,10 @@ public abstract class NodeServer {
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService //必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf); server.init(this.serverConf);
//init之后才有Executor //init之后才有Executor
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor()); //废弃 @since 2.3.0
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor()); // resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.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 注册依赖注入时的监听回调事件。 initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", ""); String interceptorClass = this.serverConf.getValue("interceptor", "");
@@ -307,7 +307,6 @@ public abstract class NodeServer {
SimpleEntry<Class, AnyValue> resEntry = dataResources.get(resourceName); SimpleEntry<Class, AnyValue> resEntry = dataResources.get(resourceName);
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue(); AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
DataSource source = null; DataSource source = null;
boolean needinit = true;
if (sourceConf != null) { if (sourceConf != null) {
final Class sourceType = resEntry.getKey(); final Class sourceType = resEntry.getKey();
if (sourceType == DataJdbcSource.class) { if (sourceType == DataJdbcSource.class) {
@@ -331,15 +330,15 @@ public abstract class NodeServer {
} }
if (source == null) { if (source == null) {
source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建 source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建
needinit = false;
} }
application.dataSources.add(source); application.dataSources.add(source);
appResFactory.register(resourceName, DataSource.class, source); appResFactory.register(resourceName, DataSource.class, source);
field.set(src, source); field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值; rf.inject(source, self); // 给AsyncGroup和其他@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src); //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) { } catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e); logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e);
} }
@@ -383,16 +382,11 @@ public abstract class NodeServer {
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource 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)); 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(); 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); application.cacheSources.add((CacheSource) source);
appResFactory.register(resourceName, genericType, source);
appResFactory.register(resourceName, CacheSource.class, source); appResFactory.register(resourceName, CacheSource.class, source);
if (genericType != CacheSource.class) {
appResFactory.register(resourceName, genericType, source);
}
} }
field.set(src, source); field.set(src, source);
rf.inject(source, self); // rf.inject(source, self); //
@@ -778,7 +772,7 @@ public abstract class NodeServer {
public void start() throws IOException { public void start() throws IOException {
if (interceptor != null) interceptor.preStart(this); if (interceptor != null) interceptor.preStart(this);
server.start(); server.start(application);
postStartServer(localServices, remoteServices); postStartServer(localServices, remoteServices);
} }

View File

@@ -46,7 +46,7 @@ public class NodeSncpServer extends NodeServer {
} }
private static Server createServer(Application application, AnyValue serconf) { 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 @Override

View File

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

View File

@@ -95,7 +95,7 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
public void start() { public void start() {
if (this.scheduler == null) { if (this.scheduler == null) {
this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> { this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> {
final Thread t = new Thread(r, CacheClusterAgent.class.getSimpleName() + "-Task-Thread"); final Thread t = new Thread(r, "Redkale-" + CacheClusterAgent.class.getSimpleName() + "-Task-Thread");
t.setDaemon(true); t.setDaemon(true);
return t; return t;
}); });

View File

@@ -140,6 +140,8 @@ public abstract class ClusterAgent {
protected boolean canRegister(String protocol, Service service) { protected boolean canRegister(String protocol, Service service) {
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false; 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 (service instanceof WebSocketNode) {
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false; if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
} }
@@ -329,7 +331,7 @@ public abstract class ClusterAgent {
this.address = addr; this.address = addr;
this.serviceref = new WeakReference(service); this.serviceref = new WeakReference(service);
Server server = ns.getServer(); 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 @Override

View File

@@ -29,6 +29,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
protected final Encodeable<Writer, Object> componentEncoder; protected final Encodeable<Writer, Object> componentEncoder;
protected final boolean subtypefinal;
protected volatile boolean inited = false; protected volatile boolean inited = false;
protected final Object lock = new Object(); protected final Object lock = new Object();
@@ -47,6 +49,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
factory.register(type, this); factory.register(type, this);
this.componentEncoder = factory.loadEncoder(this.componentType); this.componentEncoder = factory.loadEncoder(this.componentType);
this.anyEncoder = factory.getAnyEncoder(); this.anyEncoder = factory.getAnyEncoder();
this.subtypefinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers());
} finally { } finally {
inited = true; inited = true;
synchronized (lock) { synchronized (lock) {
@@ -81,13 +84,25 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
} }
} }
} }
if (out.writeArrayB(value.length, this, componentEncoder, value) < 0) { Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
final Type comp = this.componentType; if (subtypefinal) {
boolean first = true; if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
for (Object v : value) { boolean first = true;
if (!first) out.writeArrayMark(); for (Object v : value) {
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? componentEncoder : anyEncoder), v, first); if (!first) out.writeArrayMark();
if (first) first = false; 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)) ? itemEncoder : anyEncoder), v, first);
if (first) first = false;
}
} }
} }
out.writeArrayE(); 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[] 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.lang.reflect.Type;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.*; import java.util.function.*;
import org.redkale.util.Attribute; import org.redkale.util.*;
/** /**
* 序列化/反序列化操作类 * 序列化/反序列化操作类
@@ -61,12 +61,16 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public abstract byte[] convertToBytes(final Type type, final Object value); 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 Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value); public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);
public abstract byte[] convertMapToBytes(final Object... values);
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 Convert<R, W> convert;
protected boolean tiny; protected boolean tiny; //String类型值为""Boolean类型值为false时是否需要输出 默认为true
private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this); 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(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance); this.register(Pattern.class, PatternSimpledCoder.instance);
this.register(File.class, FileSimpledCoder.instance); this.register(File.class, FileSimpledCoder.instance);
this.register(Throwable.class, ThrowableSimpledCoder.instance);
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance); this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
this.register(URL.class, URLSimpledCoder.instance); this.register(URL.class, URLSimpledCoder.instance);
this.register(URI.class, URISimpledCoder.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); return new EnumSimpledCoder(enumClass);
} }
protected <E> Encodeable<W, E> createDyncEncoder(Type type) {
return null;
}
protected ObjectDecoder createObjectDecoder(Type type) { protected ObjectDecoder createObjectDecoder(Type type) {
return new ObjectDecoder(type); return new ObjectDecoder(type);
} }
protected ObjectEncoder createObjectEncoder(Type type) { protected <E> ObjectEncoder<W, E> createObjectEncoder(Type type) {
return new ObjectEncoder(type); return new ObjectEncoder(type);
} }
@@ -810,8 +815,11 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
} }
} }
if (simpleCoder == null) { if (simpleCoder == null) {
oe = createObjectEncoder(type); encoder = createDyncEncoder(type);
encoder = oe; if (encoder == null) {
oe = createObjectEncoder(type);
encoder = oe;
}
} else { } else {
encoder = simpleCoder; encoder = simpleCoder;
} }

View File

@@ -31,6 +31,10 @@ public final class EnMember<W extends Writer, T, F> {
//final boolean isnumber; //final boolean isnumber;
final boolean bool; final boolean bool;
final char[] jsonFieldNameChars;
final byte[] jsonFieldNameBytes;
protected int index; protected int index;
protected int position; //从1开始 protected int position; //从1开始
@@ -43,6 +47,8 @@ public final class EnMember<W extends Writer, T, F> {
Class t = attribute.type(); Class t = attribute.type();
this.string = CharSequence.class.isAssignableFrom(t); this.string = CharSequence.class.isAssignableFrom(t);
this.bool = t == Boolean.class || t == boolean.class; 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()); //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; return attribute;
} }
public char[] getJsonFieldNameChars() {
return jsonFieldNameChars;
}
public byte[] getJsonFieldNameBytes() {
return jsonFieldNameBytes;
}
public Encodeable<W, F> getEncoder() { public Encodeable<W, F> getEncoder() {
return encoder; return encoder;
} }

View File

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

View File

@@ -103,7 +103,8 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
if (factory.isConvertDisabled(method)) continue; if (factory.isConvertDisabled(method)) continue;
if (method.getParameterTypes().length != 0) continue; if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) 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"); boolean is = method.getName().startsWith("is");
try { try {
clazz.getMethod(method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType()); clazz.getMethod(method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType());
@@ -114,6 +115,13 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
ref = factory.findRef(clazz, method); ref = factory.findRef(clazz, method);
if (ref != null && ref.ignore()) continue; if (ref != null && ref.ignore()) continue;
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); 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; Encodeable<W, ?> fieldCoder;
if (small != null && method.getReturnType() == String.class) { if (small != null && method.getReturnType() == String.class) {
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;

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 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(); 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); member.encoder.convertTo(this, value);
this.comma = true; this.comma = true;
} }
@@ -160,7 +160,7 @@ public abstract class Writer {
if (!((Boolean) value)) return; if (!((Boolean) value)) return;
} }
} }
this.writeFieldName(fieldName, fieldType, fieldPos); this.writeFieldName(null, fieldName, fieldType, fieldPos);
anyEncoder.convertTo(this, value); anyEncoder.convertTo(this, value);
this.comma = true; this.comma = true;
} }
@@ -172,7 +172,7 @@ public abstract class Writer {
*/ */
public final void writeFieldName(final EnMember member) { public final void writeFieldName(final EnMember member) {
Attribute attr = member.getAttribute(); 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 fieldName 字段名称
* @param fieldType 字段类型 * @param fieldType 字段类型
* @param fieldPos 字段顺序 * @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值 * 写入一个boolean值

View File

@@ -6,10 +6,10 @@
package org.redkale.convert.bson; package org.redkale.convert.bson;
import java.nio.*; import java.nio.*;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*; import org.redkale.convert.*;
import static org.redkale.convert.Reader.SIGN_NULL; import static org.redkale.convert.Reader.SIGN_NULL;
import org.redkale.convert.ext.ByteSimpledCoder; import org.redkale.convert.ext.ByteSimpledCoder;
import org.redkale.util.*;
/** /**
* 以ByteBuffer为数据载体的BsonReader * 以ByteBuffer为数据载体的BsonReader
@@ -233,6 +233,6 @@ public class BsonByteBufferReader extends BsonReader {
int len = readInt(); int len = readInt();
if (len == SIGN_NULL) return null; if (len == SIGN_NULL) return null;
if (len == 0) return ""; 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; this.buffers = null;
return false; 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> { 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; private final boolean tiny;
@@ -84,11 +84,11 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
} }
public BsonReader pollBsonReader() { public BsonReader pollBsonReader() {
return readerPool.get(); return new BsonReader();
} }
public void offerBsonReader(final BsonReader in) { public void offerBsonReader(final BsonReader in) {
if (in != null) readerPool.accept(in); //无需回收
} }
//------------------------------ writer ----------------------------------------------------------- //------------------------------ writer -----------------------------------------------------------
@@ -96,16 +96,25 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
return configWrite(new BsonByteBufferWriter(tiny, supplier)); return configWrite(new BsonByteBufferWriter(tiny, supplier));
} }
public BsonWriter pollBsonWriter(final OutputStream out) { protected BsonWriter pollBsonWriter(final OutputStream out) {
return configWrite(new BsonStreamWriter(tiny, out)); return configWrite(new BsonStreamWriter(tiny, out));
} }
public BsonWriter pollBsonWriter() { 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) { public void offerBsonWriter(final BsonWriter out) {
if (out != null) writerPool.accept(out); if (out != null) {
out.recycle();
writerPool.set(out);
}
} }
//------------------------------ convertFrom ----------------------------------------------------------- //------------------------------ convertFrom -----------------------------------------------------------
@@ -119,11 +128,9 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final byte[] bytes, final int offset, final int len) { public <T> T convertFrom(final Type type, final byte[] bytes, final int offset, final int len) {
if (type == null) return null; if (type == null) return null;
final BsonReader in = readerPool.get(); final BsonReader in = new BsonReader(bytes, offset, len);
in.setBytes(bytes, offset, len);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(in); T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.accept(in);
return rs; return rs;
} }
@@ -159,10 +166,10 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override @Override
public byte[] convertTo(final Object value) { public byte[] convertTo(final Object value) {
if (value == null) { if (value == null) {
final BsonWriter out = writerPool.get().tiny(tiny); final BsonWriter out = pollBsonWriter();
out.writeNull(); out.writeNull();
byte[] result = out.toArray(); byte[] result = out.toArray();
writerPool.accept(out); offerBsonWriter(out);
return result; return result;
} }
return convertTo(value.getClass(), value); return convertTo(value.getClass(), value);
@@ -171,10 +178,10 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override @Override
public byte[] convertTo(final Type type, final Object value) { public byte[] convertTo(final Type type, final Object value) {
if (type == null) return null; if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny); final BsonWriter writer = pollBsonWriter();
factory.loadEncoder(type).convertTo(out, value); factory.loadEncoder(type).convertTo(writer, value);
byte[] result = out.toArray(); byte[] result = writer.toArray();
writerPool.accept(out); offerBsonWriter(writer);
return result; return result;
} }
@@ -189,13 +196,35 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
} }
@Override @Override
public byte[] convertMapTo(final Object... values) { public void convertToBytes(final Object value, final ConvertBytesHandler handler) {
if (values == null) return null; convertToBytes(value == null ? null : value.getClass(), value, handler);
final BsonWriter out = writerPool.get().tiny(tiny); }
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
byte[] result = out.toArray(); @Override
writerPool.accept(out); public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) {
return result; 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) { 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 @Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null; if (supplier == null) return null;
@@ -238,36 +259,13 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override @Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null; if (supplier == null || type == null) return null;
BsonByteBufferWriter out = pollBsonWriter(supplier); BsonByteBufferWriter writer = pollBsonWriter(supplier);
if (value == null) { if (value == null) {
out.writeNull(); writer.writeNull();
} else { } else {
factory.loadEncoder(type).convertTo(out, value); factory.loadEncoder(type).convertTo(writer, value);
} }
return out.toBuffers(); return writer.toBuffers();
}
@Override
public byte[] convertMapToBytes(final Object... values) {
BsonWriter out = pollBsonWriter();
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toArray();
}
@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();
} }
public void convertTo(final BsonWriter writer, final Object value) { public void convertTo(final BsonWriter writer, final Object value) {
@@ -283,14 +281,6 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
factory.loadEncoder(type).convertTo(writer, value); 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) { public BsonWriter convertToWriter(final Object value) {
if (value == null) return null; if (value == null) return null;
return convertToWriter(value.getClass(), value); return convertToWriter(value.getClass(), value);
@@ -298,14 +288,8 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
public BsonWriter convertToWriter(final Type type, final Object value) { public BsonWriter convertToWriter(final Type type, final Object value) {
if (type == null) return null; if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny); final BsonWriter writer = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(out, value); factory.loadEncoder(type).convertTo(writer, value);
return out; return writer;
}
public BsonWriter convertMapToWriter(final Object... values) {
final BsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
return out;
} }
} }

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.convert.bson; package org.redkale.convert.bson;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*; import org.redkale.convert.*;
import static org.redkale.convert.Reader.SIGN_NULL; import static org.redkale.convert.Reader.SIGN_NULL;
import org.redkale.convert.ext.*; import org.redkale.convert.ext.*;
@@ -341,7 +342,7 @@ public class BsonReader extends Reader {
int len = readInt(); int len = readInt();
if (len == SIGN_NULL) return null; if (len == SIGN_NULL) return null;
if (len == 0) return ""; 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 this.position += len - 1;//上一行已经++this.position所以此处要-1
return value; return value;
} }

View File

@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.Consumer;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.ext.ByteSimpledCoder; import org.redkale.convert.ext.ByteSimpledCoder;
import org.redkale.util.*; import org.redkale.util.*;
@@ -18,7 +19,7 @@ import org.redkale.util.*;
* *
* @author zhangjx * @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)); private static final int defaultSize = Integer.getInteger("convert.bson.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
@@ -32,6 +33,47 @@ public class BsonWriter extends Writer {
return ObjectPool.createSafePool(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() { public byte[] toArray() {
if (count == content.length) return content; if (count == content.length) return content;
byte[] newdata = new byte[count]; byte[] newdata = new byte[count];
@@ -55,6 +97,11 @@ public class BsonWriter extends Writer {
this.content = new byte[size > 128 ? size : 128]; this.content = new byte[size > 128 ? size : 128];
} }
public BsonWriter(ByteArray array) {
this.content = array.content();
this.count = array.length();
}
@Override @Override
public final boolean tiny() { public final boolean tiny() {
return tiny; return tiny;
@@ -201,7 +248,7 @@ public class BsonWriter extends Writer {
} }
@Override @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); writeByte(BsonReader.SIGN_HASNEXT);
writeSmallString(fieldName); writeSmallString(fieldName);
writeByte(BsonFactory.typeEnum(fieldType)); writeByte(BsonFactory.typeEnum(fieldType));

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 { public class JsonByteBufferWriter extends JsonWriter {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
protected Charset charset; protected Charset charset;
private final Supplier<ByteBuffer> supplier; private final Supplier<ByteBuffer> supplier;
@@ -36,7 +40,7 @@ public class JsonByteBufferWriter extends JsonWriter {
protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) { protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) {
this.tiny = tiny; this.tiny = tiny;
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset; this.charset = charset;
this.supplier = supplier; this.supplier = supplier;
} }
@@ -56,7 +60,6 @@ public class JsonByteBufferWriter extends JsonWriter {
return false; return false;
} }
@Override
public ByteBuffer[] toBuffers() { public ByteBuffer[] toBuffers() {
if (buffers == null) return new ByteBuffer[0]; if (buffers == null) return new ByteBuffer[0];
for (int i = index; i < this.buffers.length; i++) { for (int i = index; i < this.buffers.length; i++) {
@@ -66,7 +69,6 @@ public class JsonByteBufferWriter extends JsonWriter {
return this.buffers; return this.buffers;
} }
@Override
public int count() { public int count() {
if (this.buffers == null) return 0; if (this.buffers == null) return 0;
int len = 0; int len = 0;
@@ -111,6 +113,32 @@ public class JsonByteBufferWriter extends JsonWriter {
writeTo(-1, false, chs, start, len); 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) { private void writeTo(int expandsize, final boolean quote, final char[] chs, final int start, final int len) {
int byteLength = quote ? 2 : 0; int byteLength = quote ? 2 : 0;
ByteBuffer bb = null; 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 @Override
public void writeInt(int value) { public void writeInt(int value) {
writeLatin1To(false, String.valueOf(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,13 +30,18 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public static final Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() { public static final Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
}.getType(); }.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 static final ThreadLocal<JsonWriter> writerPool = ThreadLocal.withInitial(JsonWriter::new);
private final Consumer<JsonBytesWriter> offerBytesConsumer = w -> offerJsonBytesWriter(w);
private final boolean tiny; private final boolean tiny;
private Encodeable lastConvertEncodeable;
private Decodeable lastConvertDecodeable;
protected JsonConvert(JsonFactory factory, boolean tiny) { protected JsonConvert(JsonFactory factory, boolean tiny) {
super(factory); super(factory);
this.tiny = tiny; this.tiny = tiny;
@@ -66,45 +71,42 @@ 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 ----------------------------------------------------------- //------------------------------ writer -----------------------------------------------------------
private JsonByteBufferWriter pollJsonWriter(final Supplier<ByteBuffer> supplier) { private JsonCharsWriter pollJsonCharsWriter() {
return configWrite(new JsonByteBufferWriter(tiny, supplier)); JsonCharsWriter writer = charsWriterPool.get();
if (writer == null) {
writer = new JsonCharsWriter();
} else {
charsWriterPool.set(null);
}
return configWrite((JsonCharsWriter) writer.tiny(tiny));
} }
private JsonWriter pollJsonWriter(final OutputStream out) { private JsonBytesWriter pollJsonBytesWriter() {
return configWrite(new JsonStreamWriter(tiny, out)); 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 JsonWriter pollJsonWriter() { private void offerJsonCharsWriter(final JsonCharsWriter writer) {
return configWrite(writerPool.get().tiny(tiny)); if (writer != null) {
writer.recycle();
charsWriterPool.set(writer);
}
} }
//
// public void offerJsonWriter(final JsonWriter writer) { private void offerJsonBytesWriter(final JsonBytesWriter writer) {
// if (writer != null) writerPool.accept(writer); if (writer != null) {
// } writer.recycle();
bytesWriterPool.set(writer);
}
}
//------------------------------ convertFrom ----------------------------------------------------------- //------------------------------ convertFrom -----------------------------------------------------------
@Override @Override
public <T> T convertFrom(final Type type, final byte[] bytes) { public <T> T convertFrom(final Type type, final byte[] bytes) {
if (bytes == null) return null; if (bytes == null) return null;
@@ -129,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) { public <T> T convertFrom(final Type type, final char[] text, final int offset, final int length) {
if (text == null || type == null) return null; if (text == null || type == null) return null;
//final JsonReader in = readerPool.get(); Decodeable decoder = this.lastConvertDecodeable;
//in.setText(text, offset, length); if (decoder == null || decoder.getType() != type) {
T rs = (T) factory.loadDecoder(type).convertFrom(new JsonReader(text, offset, length)); decoder = factory.loadDecoder(type);
//readerPool.accept(in); this.lastConvertDecodeable = decoder;
}
T rs = (T) decoder.convertFrom(new JsonReader(text, offset, length));
return rs; return rs;
} }
public <T> T convertFrom(final Type type, final InputStream in) { public <T> T convertFrom(final Type type, final InputStream in) {
if (type == null || in == null) return null; 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 @Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) { public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null; 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 @Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) { public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null; 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) { public <T> T convertFrom(final Type type, final JsonReader reader) {
if (type == null) return null; if (type == null) return null;
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(reader); T rs = (T) decoder.convertFrom(reader);
return rs; return rs;
} }
@@ -217,73 +241,91 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public String convertTo(final Type type, final Object value) { public String convertTo(final Type type, final Object value) {
if (type == null) return null; if (type == null) return null;
if (value == null) return "null"; if (value == null) return "null";
JsonWriter writer = pollJsonWriter(); JsonCharsWriter writer = pollJsonCharsWriter();
if (writer == null) { Encodeable encoder = this.lastConvertEncodeable;
writer = new JsonWriter(); if (encoder == null || encoder.getType() != type) {
} else { encoder = factory.loadEncoder(type);
writerPool.set(null); this.lastConvertEncodeable = encoder;
} }
writer.specify(type); if (encoder.specifyable()) writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value); encoder.convertTo(writer, value);
String result = writer.toString(); String result = writer.toString();
//writerPool.accept(writer); offerJsonCharsWriter(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(writer);
}
return result; return result;
} }
@Override @Override
public byte[] convertToBytes(final Object value) { public byte[] convertToBytes(final Object value) {
if (value == null) return null; if (value == null) return null;
String result = convertTo(value.getClass(), value); return convertToBytes(value.getClass(), value);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
} }
@Override @Override
public byte[] convertToBytes(final Type type, final Object value) { public byte[] convertToBytes(final Type type, final Object value) {
if (type == null) return null; if (type == null) return null;
if (value == null) return null; if (value == null) return null;
JsonWriter writer = pollJsonWriter(); JsonBytesWriter writer = pollJsonBytesWriter();
if (writer == null) { Encodeable encoder = this.lastConvertEncodeable;
writer = new JsonWriter(); if (encoder == null || encoder.getType() != type) {
} else { encoder = factory.loadEncoder(type);
writerPool.set(null); this.lastConvertEncodeable = encoder;
} }
writer.specify(type); if (encoder.specifyable()) writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value); encoder.convertTo(writer, value);
String result = writer.toString();
//writerPool.accept(writer); byte[] result = writer.toBytes();
if (writerPool.get() == null) { offerJsonBytesWriter(writer);
writer.recycle(); return result;
writerPool.set(writer);
}
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
} }
@Override @Override
public String convertMapTo(final Object... values) { public void convertToBytes(final Object value, final ConvertBytesHandler handler) {
if (values == null) return "null"; convertToBytes(value == null ? null : value.getClass(), value, handler);
JsonWriter writer = pollJsonWriter(); }
if (writer == null) {
writer = new JsonWriter(); @Override
public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) {
JsonBytesWriter writer = pollJsonBytesWriter();
if (type == null) {
writer.writeNull();
} else { } else {
writerPool.set(null); 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);
} }
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values); writer.completed(handler, offerBytesConsumer);
String result = writer.toString(); }
//writerPool.accept(writer);
if (writerPool.get() == null) { @Override
writer.recycle(); public void convertToBytes(final ByteArray array, final Object value) {
writerPool.set(writer); 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);
} }
return result; writer.directTo(array);
} }
public void convertTo(final OutputStream out, final Object value) { public void convertTo(final OutputStream out, final Object value) {
if (value == null) { if (value == null) {
pollJsonWriter(out).writeNull(); configWrite(new JsonStreamWriter(tiny, out)).writeNull();
} else { } else {
convertTo(out, value.getClass(), value); convertTo(out, value.getClass(), value);
} }
@@ -292,59 +334,23 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public void convertTo(final OutputStream out, final Type type, final Object value) { public void convertTo(final OutputStream out, final Type type, final Object value) {
if (type == null) return; if (type == null) return;
if (value == null) { if (value == null) {
pollJsonWriter(out).writeNull(); configWrite(new JsonStreamWriter(tiny, out)).writeNull();
} else { } else {
JsonWriter writer = pollJsonWriter(); JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, out));
if (writer == null) { Encodeable encoder = this.lastConvertEncodeable;
writer = new JsonWriter(); if (encoder == null || encoder.getType() != type) {
} else { encoder = factory.loadEncoder(type);
writerPool.set(null); this.lastConvertEncodeable = encoder;
}
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
byte[] bs = writer.toBytes();
//writerPool.accept(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(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 {
JsonWriter writer = pollJsonWriter();
if (writer == null) {
writer = new JsonWriter();
} else {
writerPool.set(null);
}
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
byte[] bs = writer.toBytes();
//writerPool.accept(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(writer);
}
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
} }
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
} }
} }
@Override @Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null; if (supplier == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier); JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier));
if (value == null) { if (value == null) {
out.writeNull(); out.writeNull();
} else { } else {
@@ -356,7 +362,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override @Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null; if (supplier == null || type == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier); JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier));
if (value == null) { if (value == null) {
out.writeNull(); out.writeNull();
} else { } else {
@@ -366,29 +372,6 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return out.toBuffers(); return out.toBuffers();
} }
@Override
public byte[] convertMapToBytes(final Object... values) {
JsonWriter out = pollJsonWriter();
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBytes();
}
@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) { public void convertTo(final JsonWriter writer, final Object value) {
if (value == null) { if (value == null) {
writer.writeNull(); writer.writeNull();
@@ -407,40 +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;
// JsonWriter writer = writerPool.get();// pollJsonWriter();
// if (writer == null) {
// writer = new JsonWriter();
// } else {
// writerPool.set(null);
// }
// writer.specify(type);
// factory.loadEncoder(type).convertTo(writer, value);
// return writer;
// }
//
// public JsonWriter convertMapToWriter(final Object... values) {
// JsonWriter writer = writerPool.get();// pollJsonWriter();
// if (writer == null) {
// writer = new JsonWriter();
// } else {
// writerPool.set(null);
// }
// ((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; package org.redkale.convert.json;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.*; import java.net.*;
import org.redkale.convert.*; 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)); private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true));
static { static {
instance.register(Serializable.class, instance.loadEncoder(Object.class)); instance.register(Serializable.class, instance.loadEncoder(Object.class));
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.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)); 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 @Override
public final JsonConvert getConvert() { public final JsonConvert getConvert() {
if (convert == null) convert = new JsonConvert(this, tiny); if (convert == null) convert = new JsonConvert(this, tiny);

View File

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

View File

@@ -64,6 +64,13 @@ class JsonStreamWriter extends JsonByteBufferWriter {
} else if (c < 0x800) { } else if (c < 0x800) {
out.write((byte) (0xc0 | (c >> 6))); out.write((byte) (0xc0 | (c >> 6)));
out.write((byte) (0x80 | (c & 0x3f))); 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 { } else {
out.write((byte) (0xe0 | ((c >> 12)))); out.write((byte) (0xe0 | ((c >> 12))));
out.write((byte) (0x80 | ((c >> 6) & 0x3f))); out.write((byte) (0x80 | ((c >> 6) & 0x3f)));

View File

@@ -6,7 +6,6 @@
package org.redkale.convert.json; package org.redkale.convert.json;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -18,32 +17,12 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public class JsonWriter extends Writer { public abstract class JsonWriter extends Writer {
private static final char[] CHARS_TUREVALUE = "true".toCharArray(); protected static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
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 boolean tiny; protected boolean tiny;
// public static ObjectPool<JsonWriter> createPool(int max) {
// return new ObjectPool<>(max, (Object... params) -> new JsonWriter(), null, JsonWriter::recycle);
// }
public JsonWriter() {
this(defaultSize);
}
public JsonWriter(int size) {
this.content = new char[size > 128 ? size : 128];
}
@Override @Override
public boolean tiny() { public boolean tiny() {
return tiny; return tiny;
@@ -54,34 +33,18 @@ public class JsonWriter extends Writer {
return this; return this;
} }
//----------------------------------------------------------------------- public boolean isExtFuncEmpty() {
//----------------------------------------------------------------------- return this.objExtFunc == null && this.objFieldFunc == null;
/**
* 返回指定至少指定长度的缓冲区
*
* @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 void writeTo(final char ch) { //只能是 0 - 127 的字符 //-----------------------------------------------------------------------
expand(1); public abstract void writeTo(final char ch); //只能是 0 - 127 的字符
content[count++] = ch;
}
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符 public abstract void writeTo(final char[] chs, final int start, final int len); //只能是 0 - 127 的字符
expand(len);
System.arraycopy(chs, start, content, count, len); public abstract void writeTo(final byte ch); //只能是 0 - 127 的字符
count += len;
} public abstract void writeTo(final byte[] chs, final int start, final int len); //只能是 0 - 127 的字符
/** /**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String * <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
@@ -89,81 +52,29 @@ public class JsonWriter extends Writer {
* @param quote 是否加双引号 * @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值 * @param value 非null且不含需要转义的字符的String值
*/ */
public void writeLatin1To(final boolean quote, final String value) { public abstract 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 @Override
protected boolean recycle() { public abstract void writeBoolean(boolean value);
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 @Override
public void writeString(String value) { public abstract void writeInt(int 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 @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 (this.comma) writeTo(',');
writeLatin1To(true, fieldName); if (member != null) {
writeTo(':'); writeTo(member.getJsonFieldNameChars());
} else {
writeLatin1To(true, fieldName);
writeTo(':');
}
} }
@Override @Override
@@ -171,26 +82,20 @@ public class JsonWriter extends Writer {
writeLatin1To(true, value); writeLatin1To(true, value);
} }
@Override
public String toString() {
return new String(content, 0, count);
}
//---------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------
public final void writeTo(final char... chs) { //只能是 0 - 127 的字符 public final void writeTo(final char... chs) { //只能是 0 - 127 的字符
writeTo(chs, 0, chs.length); writeTo(chs, 0, chs.length);
} }
@Override
public final void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override @Override
public final void writeByte(byte value) { public final void writeByte(byte value) {
writeInt(value); writeInt(value);
} }
public final void writeTo(final byte[] chs) { //只能是 0 - 127 的字符
writeTo(chs, 0, chs.length);
}
@Override @Override
public final void writeByteArray(byte[] values) { public final void writeByteArray(byte[] values) {
if (values == null) { if (values == null) {
@@ -217,101 +122,6 @@ public class JsonWriter extends Writer {
writeInt(value); 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 @Override
public final void writeFloat(float value) { public final void writeFloat(float value) {
writeLatin1To(false, String.valueOf(value)); writeLatin1To(false, String.valueOf(value));

View File

@@ -7,13 +7,14 @@ package org.redkale.mq;
import java.net.*; import java.net.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.logging.Level; import java.util.logging.Level;
import javax.annotation.Resource;
import org.redkale.cluster.ClusterAgent; import org.redkale.cluster.ClusterAgent;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.util.Utility;
/** /**
* 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例 * 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例
@@ -28,19 +29,21 @@ import org.redkale.net.http.*;
public class HttpMessageClusterClient extends HttpMessageClient { public class HttpMessageClusterClient extends HttpMessageClient {
//jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET //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", "date", "expect", "from", "host", "origin",
"referer", "upgrade", "via", "warning"); "referer", "upgrade", "via", "warning");
protected ClusterAgent clusterAgent; 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) { public HttpMessageClusterClient(ClusterAgent clusterAgent) {
super(null); super(null);
Objects.requireNonNull(clusterAgent); Objects.requireNonNull(clusterAgent);
this.clusterAgent = clusterAgent; this.clusterAgent = clusterAgent;
this.httpClient = java.net.http.HttpClient.newHttpClient(); //this.httpClient = java.net.http.HttpClient.newHttpClient();
} }
@Override @Override
@@ -67,16 +70,17 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> { return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> {
if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture(); 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)); final Map<String, String> clientHeaders = new LinkedHashMap<>();
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true"); byte[] clientBody = null;
if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); 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 (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) { if (req.getBody() != null && req.getBody().length > 0) {
String paramstr = req.getParametersToString(); String paramstr = req.getParametersToString();
if (paramstr != null) { if (paramstr != null) {
@@ -86,10 +90,10 @@ public class HttpMessageClusterClient extends HttpMessageClient {
req.setRequestURI(req.getRequestURI() + "?" + paramstr); req.setRequestURI(req.getRequestURI() + "?" + paramstr);
} }
} }
builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody())); clientBody = req.getBody();
} else { } else {
String paramstr = req.getParametersToString(); 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<>(); List<CompletableFuture> futures = new ArrayList<>();
for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) { for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) {
@@ -99,7 +103,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String suburi = req.getRequestURI(); String suburi = req.getRequestURI();
suburi = suburi.substring(1); //跳过 / suburi = suburi.substring(1); //跳过 /
suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/')); 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); if (futures.isEmpty()) return CompletableFuture.completedFuture(null);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> 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, ""); String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> { return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> {
if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture(); 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)); final Map<String, String> clientHeaders = new LinkedHashMap<>();
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true"); byte[] clientBody = null;
if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); 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 (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) { if (req.getBody() != null && req.getBody().length > 0) {
String paramstr = req.getParametersToString(); String paramstr = req.getParametersToString();
if (paramstr != null) { if (paramstr != null) {
@@ -134,46 +139,138 @@ public class HttpMessageClusterClient extends HttpMessageClient {
req.setRequestURI(req.getRequestURI() + "?" + paramstr); req.setRequestURI(req.getRequestURI() + "?" + paramstr);
} }
} }
builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody())); clientBody = req.getBody();
} else { } else {
String paramstr = req.getParametersToString(); 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); if (!it.hasNext()) return CompletableFuture.completedFuture(null);
InetSocketAddress addr = it.next(); InetSocketAddress addr = it.next();
String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi; 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 -> { return httpClient.postAsync(url, clientHeaders, clientBody);
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);
});
} }
// 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; package org.redkale.mq;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import org.redkale.boot.NodeHttpServer; import org.redkale.boot.NodeHttpServer;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor; import org.redkale.util.ObjectPool;
/** /**
* 一个Service对应一个MessageProcessor
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -31,14 +33,12 @@ public class HttpMessageProcessor implements MessageProcessor {
protected final Logger logger; protected final Logger logger;
protected MessageClient messageClient; protected HttpMessageClient messageClient;
protected final MessageProducers producer; protected final MessageProducers producers;
protected final NodeHttpServer server; protected final NodeHttpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service; protected final Service service;
protected final HttpServlet servlet; protected final HttpServlet servlet;
@@ -49,6 +49,12 @@ public class HttpMessageProcessor implements MessageProcessor {
protected final String multimodule; // 前后有/, 例如: /userstat/ protected final String multimodule; // 前后有/, 例如: /userstat/
protected ThreadLocal<ObjectPool<HttpMessageResponse>> respPoolThreadLocal;
protected final Supplier<HttpMessageResponse> respSupplier;
protected final Consumer<HttpMessageResponse> respConsumer;
protected CountDownLatch cdl; protected CountDownLatch cdl;
protected long starttime; protected long starttime;
@@ -57,13 +63,13 @@ public class HttpMessageProcessor implements MessageProcessor {
if (cdl != null) cdl.countDown(); if (cdl != null) cdl.countDown();
}; };
public HttpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageClient messageClient, 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.logger = logger;
this.finest = logger.isLoggable(Level.FINEST); this.finest = logger.isLoggable(Level.FINEST);
this.finer = logger.isLoggable(Level.FINER); this.finer = logger.isLoggable(Level.FINER);
this.fine = logger.isLoggable(Level.FINE); this.fine = logger.isLoggable(Level.FINE);
this.messageClient = messageClient; this.messageClient = messageClient;
this.producer = producer; this.producers = producers;
this.server = server; this.server = server;
this.service = service; this.service = service;
this.servlet = servlet; this.servlet = servlet;
@@ -71,22 +77,21 @@ public class HttpMessageProcessor implements MessageProcessor {
this.multiconsumer = mmc != null; this.multiconsumer = mmc != null;
this.restmodule = "/" + Rest.getRestModule(service) + "/"; this.restmodule = "/" + Rest.getRestModule(service) + "/";
this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null; 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 @Override
public void begin(final int size, long starttime) { public void begin(final int size, long starttime) {
this.starttime = starttime; this.starttime = starttime;
if (this.workExecutor != null) this.cdl = new CountDownLatch(size); this.cdl = new CountDownLatch(size);
} }
@Override @Override
public void process(final MessageRecord message, final Runnable callback) { public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) { execute(message, innerCallback);
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
} }
private void execute(final MessageRecord message, final Runnable callback) { private void execute(final MessageRecord message, final Runnable callback) {
@@ -96,13 +101,13 @@ public class HttpMessageProcessor implements MessageProcessor {
long cha = now - message.createtime; long cha = now - message.createtime;
long e = now - starttime; long e = now - starttime;
if (multiconsumer) message.setResptopic(null); //不容许有响应 if (multiconsumer) message.setResptopic(null); //不容许有响应
HttpContext context = server.getHttpServer().getContext();
request = new HttpMessageRequest(context, message); HttpMessageResponse response = respSupplier.get();
if (multiconsumer) { request = response.request();
request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule)); response.prepare(message, callback, producers.getProducer(message));
} if (multiconsumer) request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
HttpMessageResponse response = new HttpMessageResponse(context, request, callback, null, null, messageClient, producer.getProducer(message));
servlet.execute(request, response); server.getHttpServer().getContext().execute(servlet, request, response);
long o = System.currentTimeMillis() - now; long o = System.currentTimeMillis() - now;
if ((cha > 1000 || e > 100 || o > 1000) && fine) { 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); logger.log(Level.FINE, "HttpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message);
@@ -114,7 +119,7 @@ public class HttpMessageProcessor implements MessageProcessor {
} catch (Throwable ex) { } catch (Throwable ex) {
if (message.getResptopic() != null && !message.getResptopic().isEmpty()) { if (message.getResptopic() != null && !message.getResptopic().isEmpty()) {
HttpMessageResponse.finishHttpResult(finest, request == null ? null : request.getRespConvert(), HttpMessageResponse.finishHttpResult(finest, request == null ? null : request.getRespConvert(),
message, callback, messageClient, 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); logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex);
} }
@@ -133,7 +138,7 @@ public class HttpMessageProcessor implements MessageProcessor {
} }
public MessageProducers getProducer() { public MessageProducers getProducer() {
return producer; return producers;
} }
public NodeHttpServer getServer() { public NodeHttpServer getServer() {

View File

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

View File

@@ -7,13 +7,14 @@ package org.redkale.mq;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.function.*;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.net.Response;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.service.RetResult; import org.redkale.service.RetResult;
import org.redkale.util.ObjectPool;
import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT; import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT;
import org.redkale.net.Response;
/** /**
* *
@@ -27,7 +28,7 @@ import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT;
*/ */
public class HttpMessageResponse extends HttpResponse { public class HttpMessageResponse extends HttpResponse {
protected MessageClient messageClient; protected final HttpMessageClient messageClient;
protected MessageRecord message; protected MessageRecord message;
@@ -37,22 +38,33 @@ public class HttpMessageResponse extends HttpResponse {
protected Runnable callback; protected Runnable callback;
public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback, public HttpMessageResponse(HttpContext context, HttpMessageClient messageClient, final Supplier<HttpMessageResponse> respSupplier, final Consumer<HttpMessageResponse> respConsumer) {
ObjectPool<Response> responsePool, HttpResponseConfig config, MessageClient messageClient, MessageProducer producer) { super(context, new HttpMessageRequest(context), null);
super(context, request, responsePool, config); this.responseSupplier = (Supplier) respSupplier;
this.message = request.message; this.responseConsumer = (Consumer) respConsumer;
this.callback = callback;
this.messageClient = messageClient; 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.producer = producer;
this.finest = producer.logger.isLoggable(Level.FINEST); this.finest = producer.logger.isLoggable(Level.FINEST);
} }
public HttpMessageResponse(HttpContext context, MessageRecord message, Runnable callback, HttpResponseConfig config, MessageClient messageClient, MessageProducer producer) { public HttpMessageRequest request() {
super(context, new HttpMessageRequest(context, message), null, config); return (HttpMessageRequest) request;
this.message = message;
this.callback = callback;
this.messageClient = messageClient;
this.producer = producer;
} }
public void finishHttpResult(HttpResult result) { public void finishHttpResult(HttpResult result) {
@@ -77,6 +89,25 @@ public class HttpMessageResponse extends HttpResponse {
producer.apply(messageClient.createMessageRecord(msg.getSeqid(), CTYPE_HTTP_RESULT, 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 @Override
public void finishJson(org.redkale.service.RetResult ret) { public void finishJson(org.redkale.service.RetResult ret) {
if (message.isEmptyResptopic()) { if (message.isEmptyResptopic()) {
@@ -103,7 +134,7 @@ public class HttpMessageResponse extends HttpResponse {
@Override @Override
public void finish(int status, String msg) { public void finish(int status, String msg) {
if (status > 400) { if (status > 400) {
producer.logger.log(Level.INFO, "HttpMessageResponse.finish status: " + status + ", message: " + this.message); producer.logger.log(Level.WARNING, "HttpMessageResponse.finish status: " + status + ", message: " + this.message);
} else if (finest) { } else if (finest) {
producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status); producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
} }
@@ -125,21 +156,26 @@ public class HttpMessageResponse extends HttpResponse {
} }
@Override @Override
public void finish(final byte[] bs) { public void finish(boolean kill, final byte[] bs, int offset, int length) {
if (message.isEmptyResptopic()) { if (message.isEmptyResptopic()) {
if (callback != null) callback.run(); if (callback != null) callback.run();
return; return;
} }
finishHttpResult(new HttpResult(bs)); if (offset == 0 && bs.length == length) {
finishHttpResult(new HttpResult(bs));
} else {
finishHttpResult(new HttpResult(Arrays.copyOfRange(bs, offset, offset + length)));
}
} }
@Override @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 (message.isEmptyResptopic()) {
if (callback != null) callback.run(); if (callback != null) callback.run();
return; 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 @Override

View File

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

View File

@@ -16,7 +16,7 @@ import static org.redkale.boot.Application.RESNAME_APP_NODEID;
import org.redkale.net.Servlet; import org.redkale.net.Servlet;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
import org.redkale.service.Service; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -56,8 +56,6 @@ public abstract class MessageAgent {
protected ScheduledThreadPoolExecutor timeoutExecutor; protected ScheduledThreadPoolExecutor timeoutExecutor;
protected ThreadHashExecutor workExecutor;
protected int producerCount = 1; protected int producerCount = 1;
//本地Service消息接收处理器 key:consumer //本地Service消息接收处理器 key:consumer
@@ -68,23 +66,16 @@ public abstract class MessageAgent {
this.httpMessageClient = new HttpMessageClient(this); this.httpMessageClient = new HttpMessageClient(this);
this.sncpMessageClient = new SncpMessageClient(this); this.sncpMessageClient = new SncpMessageClient(this);
this.producerCount = config.getIntValue("producers", Runtime.getRuntime().availableProcessors()); 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). // application (it doesn't execute completion handlers).
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> { this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r); Thread t = new Thread(r);
t.setName("MessageAgent-Timeout-Thread"); t.setName("Redkale-MessageAgent-Timeout-Thread");
t.setDaemon(true); t.setDaemon(true);
return t; return t;
}); });
this.timeoutExecutor.setRemoveOnCancelPolicy(true); this.timeoutExecutor.setRemoveOnCancelPolicy(true);
} }
public boolean isHashPool() {
return this.workExecutor != null;
}
public CompletableFuture<Map<String, Long>> start() { public CompletableFuture<Map<String, Long>> start() {
final LinkedHashMap<String, Long> map = new LinkedHashMap<>(); final LinkedHashMap<String, Long> map = new LinkedHashMap<>();
final List<CompletableFuture> futures = new ArrayList<>(); final List<CompletableFuture> futures = new ArrayList<>();
@@ -108,7 +99,6 @@ public abstract class MessageAgent {
public void destroy(AnyValue config) { public void destroy(AnyValue config) {
this.httpMessageClient.close().join(); this.httpMessageClient.close().join();
this.sncpMessageClient.close().join(); this.sncpMessageClient.close().join();
this.workExecutor.shutdown();
if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown(); if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown();
if (this.sncpProducer != null) this.sncpProducer.shutdown().join(); if (this.sncpProducer != null) this.sncpProducer.shutdown().join();
if (this.httpProducer != null) this.httpProducer.shutdown().join(); if (this.httpProducer != null) this.httpProducer.shutdown().join();
@@ -126,12 +116,12 @@ public abstract class MessageAgent {
protected List<MessageProducer> getAllMessageProducer() { protected List<MessageProducer> getAllMessageProducer() {
List<MessageProducer> producers = new ArrayList<>(); List<MessageProducer> producers = new ArrayList<>();
if (this.httpProducer != null) producers.addAll(List.of(this.httpProducer.producers)); if (this.httpProducer != null) producers.addAll(Utility.ofList(this.httpProducer.producers));
if (this.sncpProducer != null) producers.addAll(List.of(this.sncpProducer.producers)); if (this.sncpProducer != null) producers.addAll(Utility.ofList(this.sncpProducer.producers));
MessageProducers one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer(); 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(); 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; return producers;
} }
@@ -224,18 +214,22 @@ public abstract class MessageAgent {
public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor); public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor);
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) { 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[] topics = generateHttpReqTopics(service);
String consumerid = generateHttpConsumerid(topics, service); String consumerid = generateHttpConsumerid(topics, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat"); if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, this.workExecutor, httpMessageClient, 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))); 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) { 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 topic = generateSncpReqTopic(service);
String consumerid = generateSncpConsumerid(topic, service); String consumerid = generateSncpConsumerid(topic, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat"); if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, this.workExecutor, sncpMessageClient, 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))); this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(new String[]{topic}, consumerid, processor)));
} }

View File

@@ -79,11 +79,11 @@ public abstract class MessageClient {
node.future.complete(msg); node.future.complete(msg);
long cha = now - msg.createtime; long cha = now - msg.createtime;
if (cha > 1000 && fine) { if (cha > 1000 && fine) {
messageAgent.logger.log(Level.FINE, clazzName + ".MessageRespFutureNode.process (mqs.delays = " + cha + "ms, mqs.counters = " + ncer + ") mqresp.msg: " + msg); messageAgent.logger.log(Level.FINE, clazzName + ".MessageRespFutureNode.process (mqs.delays = " + cha + "ms, mqs.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
} else if (cha > 50 && finer) { } else if (cha > 50 && finer) {
messageAgent.logger.log(Level.FINER, clazzName + ".MessageRespFutureNode.process (mq.delays = " + cha + "ms, mq.counters = " + ncer + ") mqresp.msg: " + msg); messageAgent.logger.log(Level.FINER, clazzName + ".MessageRespFutureNode.process (mq.delays = " + cha + "ms, mq.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
} else if (finest) { } else if (finest) {
messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.process (mq.delay = " + cha + "ms, mq.counter = " + ncer + ") mqresp.msg: " + 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); MessageConsumer one = messageAgent.createConsumer(new String[]{respTopic}, respConsumerid, processor);
@@ -114,6 +114,10 @@ public abstract class MessageClient {
} }
} }
protected MessageRecord formatRespMessage(MessageRecord message) {
return message;
}
protected abstract MessageProducers getProducer(); protected abstract MessageProducers getProducer();
public MessageRecord createMessageRecord(String resptopic, String content) { public MessageRecord createMessageRecord(String resptopic, String content) {

View File

@@ -6,6 +6,7 @@
package org.redkale.mq; package org.redkale.mq;
/** /**
* 一个Service对应一个MessageProcessor
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -8,7 +8,10 @@ package org.redkale.mq;
import java.io.Serializable; import java.io.Serializable;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.bson.BsonConvert;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.HttpSimpleRequest;
import org.redkale.net.sncp.SncpRequest;
import org.redkale.util.Comment; import org.redkale.util.Comment;
/** /**
@@ -33,6 +36,8 @@ public class MessageRecord implements Serializable {
protected static final byte CTYPE_HTTP_RESULT = 3; protected static final byte CTYPE_HTTP_RESULT = 3;
protected static final byte CTYPE_BSON_RESULT = 4;
@ConvertColumn(index = 1) @ConvertColumn(index = 1)
@Comment("消息序列号") @Comment("消息序列号")
protected long seqid; protected long seqid;
@@ -274,8 +279,17 @@ public class MessageRecord implements Serializable {
if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).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.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\"");
if (this.content != null) { if (this.content != null) {
if (this.ctype == CTYPE_HTTP_REQUEST) { if (this.ctype == CTYPE_BSON_RESULT && this.content.length > SncpRequest.HEADER_SIZE) {
sb.append(",\"content\":").append(HttpSimpleRequestCoder.getInstance().decode(this.content)); 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) { } else if (this.ctype == CTYPE_HTTP_RESULT) {
sb.append(",\"content\":").append(HttpResultCoder.getInstance().decode(this.content)); sb.append(",\"content\":").append(HttpResultCoder.getInstance().decode(this.content));
} else if (localattach != null) { } else if (localattach != null) {

View File

@@ -53,4 +53,9 @@ public class SncpMessageClient extends MessageClient {
return sendMessage(message, true, counter); 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.boot.NodeSncpServer;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
/** /**
* 一个Service对应一个MessageProcessor
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -37,8 +37,6 @@ public class SncpMessageProcessor implements MessageProcessor {
protected final NodeSncpServer server; protected final NodeSncpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service; protected final Service service;
protected final SncpServlet servlet; protected final SncpServlet servlet;
@@ -51,7 +49,7 @@ public class SncpMessageProcessor implements MessageProcessor {
if (cdl != null) cdl.countDown(); if (cdl != null) cdl.countDown();
}; };
public SncpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageClient messageClient, 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.logger = logger;
this.finest = logger.isLoggable(Level.FINEST); this.finest = logger.isLoggable(Level.FINEST);
this.finer = logger.isLoggable(Level.FINER); this.finer = logger.isLoggable(Level.FINER);
@@ -61,22 +59,17 @@ public class SncpMessageProcessor implements MessageProcessor {
this.server = server; this.server = server;
this.service = service; this.service = service;
this.servlet = servlet; this.servlet = servlet;
this.workExecutor = workExecutor;
} }
@Override @Override
public void begin(final int size, long starttime) { public void begin(final int size, long starttime) {
this.starttime = starttime; this.starttime = starttime;
if (this.workExecutor != null) this.cdl = new CountDownLatch(size); this.cdl = new CountDownLatch(size);
} }
@Override @Override
public void process(final MessageRecord message, final Runnable callback) { public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) { execute(message, innerCallback);
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
} }
private void execute(final MessageRecord message, final Runnable callback) { private void execute(final MessageRecord message, final Runnable callback) {
@@ -87,15 +80,16 @@ public class SncpMessageProcessor implements MessageProcessor {
long e = now - starttime; long e = now - starttime;
SncpContext context = server.getSncpServer().getContext(); SncpContext context = server.getSncpServer().getContext();
SncpMessageRequest request = new SncpMessageRequest(context, message); SncpMessageRequest request = new SncpMessageRequest(context, message);
response = new SncpMessageResponse(context, request, callback, null, messageClient, producer.getProducer(message)); response = new SncpMessageResponse(context, request, callback, messageClient, producer.getProducer(message));
servlet.execute(request, response);
context.execute(servlet, request, response);
long o = System.currentTimeMillis() - now; long o = System.currentTimeMillis() - now;
if ((cha > 1000 || e > 100 || o > 1000) && fine) { 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, works=" + (workExecutor != null ? workExecutor.size() : 0) + ") message: " + message); 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) { } 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, works=" + (workExecutor != null ? workExecutor.size() : 0) + ") message: " + message); logger.log(Level.FINER, "SncpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message);
} else if (finest) { } else if (finest) {
logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms, works=" + (workExecutor != null ? workExecutor.size() : 0) + ") message: " + message); logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message);
} }
} catch (Throwable ex) { } catch (Throwable ex) {
if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null);

View File

@@ -28,7 +28,9 @@ public class SncpMessageRequest extends SncpRequest {
public SncpMessageRequest(SncpContext context, MessageRecord message) { public SncpMessageRequest(SncpContext context, MessageRecord message) {
super(context); super(context);
this.message = message; 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调用 @Override //被SncpAsyncHandler.sncp_setParams调用

View File

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

View File

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

View File

@@ -10,8 +10,9 @@ import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.*;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.boot.Application;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -22,20 +23,36 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public class AioTcpProtocolServer 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 AsynchronousChannelGroup group;
private AsynchronousServerSocketChannel serverChannel; private AsynchronousServerSocketChannel serverChannel;
public AioTcpProtocolServer(Context context) { public AsyncAioTcpProtocolServer(Context context) {
super(context); super(context);
} }
@Override @Override
public void open(AnyValue config) throws IOException { public void open(AnyValue config) throws IOException {
//group = AsynchronousChannelGroup.withThreadPool(context.executor); //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); this.serverChannel = AsynchronousServerSocketChannel.open(group);
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions(); final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
@@ -72,7 +89,7 @@ public class AioTcpProtocolServer extends ProtocolServer {
} }
@Override @Override
public void accept(Server server) throws IOException { public void accept(Application application, Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong(); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong(); AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize); ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
@@ -101,10 +118,9 @@ public class AioTcpProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
AsyncConnection conn = new AioTcpAsyncConnection(bufferPool, bufferPool, channel, AsyncConnection conn = new AsyncAioTcpConnection(server.bufferCapacity, bufferPool, bufferPool, channel,
context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter); context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
//context.runAsync(new PrepareRunner(context, responsePool, conn, null, null)); new ProtocolCodec(context, responsePool, responsePool, conn).run(null);
new PrepareRunner(context, responsePool, conn, null, null).run();
} catch (Throwable e) { } catch (Throwable e) {
context.logger.log(Level.INFO, channel + " accept error", e); context.logger.log(Level.INFO, channel + " accept error", e);
} }
@@ -118,6 +134,21 @@ public class AioTcpProtocolServer 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 @Override
public void close() throws IOException { public void close() throws IOException {
this.serverChannel.close(); this.serverChannel.close();

View File

@@ -10,7 +10,6 @@ import java.net.*;
import java.nio.*; import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.function.*; import java.util.function.*;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
@@ -35,10 +34,18 @@ public abstract class AsyncConnection implements AutoCloseable {
protected volatile long writetime; protected volatile long writetime;
protected final boolean client;
protected final int bufferCapacity;
private final Supplier<ByteBuffer> bufferSupplier; private final Supplier<ByteBuffer> bufferSupplier;
private final Consumer<ByteBuffer> bufferConsumer; private final Consumer<ByteBuffer> bufferConsumer;
private ByteBufferWriter pipelineWriter;
private PipelineDataNode pipelineDataNode;
private ByteBuffer readBuffer; private ByteBuffer readBuffer;
//在线数 //在线数
@@ -52,15 +59,20 @@ public abstract class AsyncConnection implements AutoCloseable {
//关联的事件数, 小于1表示没有事件 //关联的事件数, 小于1表示没有事件
private final AtomicInteger eventing = new AtomicInteger(); 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) { 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, protected AsyncConnection(boolean client, final int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SSLContext sslContext, final AtomicLong livingCounter, final AtomicLong closedCounter) { SSLContext sslContext, final AtomicLong livingCounter, final AtomicLong closedCounter) {
Objects.requireNonNull(bufferSupplier); Objects.requireNonNull(bufferSupplier);
Objects.requireNonNull(bufferConsumer); Objects.requireNonNull(bufferConsumer);
this.client = client;
this.bufferCapacity = bufferCapacity;
this.bufferSupplier = bufferSupplier; this.bufferSupplier = bufferSupplier;
this.bufferConsumer = bufferConsumer; this.bufferConsumer = bufferConsumer;
this.sslContext = sslContext; this.sslContext = sslContext;
@@ -92,6 +104,8 @@ public abstract class AsyncConnection implements AutoCloseable {
return eventing.decrementAndGet(); return eventing.decrementAndGet();
} }
protected abstract void continueRead();
public abstract boolean isOpen(); public abstract boolean isOpen();
public abstract boolean isTCP(); public abstract boolean isTCP();
@@ -118,16 +132,77 @@ public abstract class AsyncConnection implements AutoCloseable {
public abstract ReadableByteChannel readableByteChannel(); public abstract ReadableByteChannel readableByteChannel();
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
public abstract WritableByteChannel writableByteChannel(); 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); 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) { public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(srcs, 0, srcs.length, attachment, 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 abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler);
public void setReadBuffer(ByteBuffer buffer) { public void setReadBuffer(ByteBuffer buffer) {
@@ -135,11 +210,139 @@ public abstract class AsyncConnection implements AutoCloseable {
this.readBuffer = buffer; this.readBuffer = buffer;
} }
public static class Message { public boolean hasPipelineData() {
ByteBufferWriter writer = this.pipelineWriter;
return writer != null && writer.position() > 0;
}
public String value; public final void flushPipelineData(CompletionHandler<Integer, Void> handler) {
flushPipelineData(null, handler);
}
public Message() { 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 + "}";
} }
} }
@@ -199,11 +402,7 @@ public abstract class AsyncConnection implements AutoCloseable {
} }
if (this.readBuffer != null) { if (this.readBuffer != null) {
Consumer<ByteBuffer> consumer = this.bufferConsumer; Consumer<ByteBuffer> consumer = this.bufferConsumer;
// Thread thread = Thread.currentThread(); if (consumer != null) consumer.accept(this.readBuffer);
// if (thread instanceof IOThread) {
// consumer = ((IOThread) thread).getBufferPool();
// }
consumer.accept(this.readBuffer);
} }
if (attributes == null) return; if (attributes == null) return;
try { try {
@@ -245,155 +444,4 @@ public abstract class AsyncConnection implements AutoCloseable {
if (this.attributes != null) this.attributes.clear(); 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 AioTcpAsyncConnection(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 BioUdpAsyncConnection(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 BioUdpAsyncConnection(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 BioUdpAsyncConnection(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 BioUdpAsyncConnection(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 AioTcpAsyncConnection(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 AioTcpAsyncConnection(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 AioTcpAsyncConnection(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 AioTcpAsyncConnection(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

@@ -5,6 +5,7 @@
*/ */
package org.redkale.net; package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler; import java.nio.channels.CompletionHandler;
import java.util.concurrent.*; import java.util.concurrent.*;
@@ -17,7 +18,7 @@ import java.util.concurrent.*;
* *
* @since 2.1.0 * @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; private final CompletionHandler<Integer, A> handler;
@@ -25,11 +26,20 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
public ScheduledFuture timeoutFuture; 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.handler = handler;
this.attachment = attachment; this.attachment = attachment;
} }
public void setConnBuffers(AsyncNioConnection conn, ByteBuffer... buffs) {
this.conn = conn;
this.buffers = buffs;
}
public void setAttachment(A attachment) { public void setAttachment(A attachment) {
this.attachment = attachment; this.attachment = attachment;
} }
@@ -41,6 +51,9 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
this.timeoutFuture = null; this.timeoutFuture = null;
future.cancel(true); future.cancel(true);
} }
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.completed(result, attachment); handler.completed(result, attachment);
} }
@@ -51,11 +64,17 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
this.timeoutFuture = null; this.timeoutFuture = null;
future.cancel(true); future.cancel(true);
} }
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.failed(exc, attachment); handler.failed(exc, attachment);
} }
@Override @Override
public void run() { public void run() {
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.failed(new TimeoutException(), attachment); 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,6 +11,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import org.redkale.boot.Application;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -22,23 +24,23 @@ import org.redkale.util.*;
* *
* @since 2.1.0 * @since 2.1.0
*/ */
public class NioTcpProtocolServer extends ProtocolServer { class AsyncNioTcpProtocolServer extends ProtocolServer {
private ObjectPool<ByteBuffer> bufferPool;
private ObjectPool<Response> responsePool;
private ServerSocketChannel serverChannel; private ServerSocketChannel serverChannel;
private Selector selector; private Selector selector;
private NioThreadGroup ioGroup; private AsyncIOGroup ioGroup;
private Thread acceptThread; private Thread acceptThread;
private boolean closed; private boolean closed;
public NioTcpProtocolServer(Context context) { private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
public AsyncNioTcpProtocolServer(Context context) {
super(context); super(context);
} }
@@ -81,20 +83,38 @@ public class NioTcpProtocolServer extends ProtocolServer {
} }
@Override @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); this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
AtomicLong createBufferCounter = new AtomicLong(); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = 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 createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong(); AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
this.responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); final int threads = Runtime.getRuntime().availableProcessors();
this.ioGroup = new NioThreadGroup(server.name, null, Runtime.getRuntime().availableProcessors(), bufferPool, responsePool); 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.ioGroup.start();
this.acceptThread = new Thread() { this.acceptThread = new Thread() {
{
setName(threadPrefixName.replace("ServletThread", "AcceptThread"));
}
@Override @Override
public void run() { public void run() {
while (!closed) { while (!closed) {
@@ -125,9 +145,13 @@ public class NioTcpProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
NioThread ioThread = ioGroup.nextThread(); AsyncIOThread readThread = ioGroup.nextIOThread();
AsyncConnection conn = new NioTcpAsyncConnection(ioGroup, ioThread, channel, context.getSSLContext(), null, livingCounter, closedCounter); ioGroup.connCreateCounter.incrementAndGet();
new NioTcpPrepareRunner(context, responsePool, conn, null, null).run(); 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 @Override
@@ -140,4 +164,18 @@ public class NioTcpProtocolServer extends ProtocolServer {
this.selector.close(); 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

@@ -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 BioUdpAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private final DatagramChannel channel;
private final SocketAddress remoteAddress;
private final boolean client;
public BioUdpAsyncConnection(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,131 +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 BioUdpProtocolServer extends ProtocolServer {
private boolean running;
private DatagramChannel serverChannel;
public BioUdpProtocolServer(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);
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 BioUdpAsyncConnection(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;
}
}

View File

@@ -28,7 +28,9 @@ public class Context {
protected final long serverStartTime; protected final long serverStartTime;
//Server的线程池 //Server的线程池
protected final ThreadPoolExecutor executor; protected final ExecutorService workExecutor;
protected final ThreadHashExecutor workHashExecutor;
//SSL //SSL
protected final SSLContext sslContext; protected final SSLContext sslContext;
@@ -73,17 +75,17 @@ public class Context {
protected Charset charset; protected Charset charset;
public Context(ContextConfig config) { 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.bufferCapacity, config.maxconns, config.maxbody, config.charset, config.address, config.resourceFactory,
config.prepare, config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds); 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, int bufferCapacity, final int maxconns, final int maxbody, Charset charset, InetSocketAddress address,
ResourceFactory resourceFactory, PrepareServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) { ResourceFactory resourceFactory, PrepareServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) {
this.serverStartTime = serverStartTime; this.serverStartTime = serverStartTime;
this.logger = logger; this.logger = logger;
this.executor = executor; this.workExecutor = workExecutor;
this.sslContext = sslContext; this.sslContext = sslContext;
this.bufferCapacity = bufferCapacity; this.bufferCapacity = bufferCapacity;
this.maxconns = maxconns; this.maxconns = maxconns;
@@ -97,6 +99,57 @@ public class Context {
this.writeTimeoutSeconds = writeTimeoutSeconds; this.writeTimeoutSeconds = writeTimeoutSeconds;
this.jsonFactory = JsonFactory.root(); this.jsonFactory = JsonFactory.root();
this.bsonFactory = BsonFactory.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() { public ResourceFactory getResourceFactory() {
@@ -127,22 +180,6 @@ public class Context {
return charset; 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() { public int getBufferCapacity() {
return bufferCapacity; return bufferCapacity;
} }
@@ -177,7 +214,7 @@ public class Context {
public long serverStartTime; public long serverStartTime;
//Server的线程池 //Server的线程池
public ThreadPoolExecutor executor; public ExecutorService workExecutor;
//SSL //SSL
public SSLContext sslContext; public SSLContext sslContext;

View File

@@ -1,473 +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;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioTcpAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private SocketAddress remoteAddress;
final SocketChannel channel;
final NioThread ioThread;
final NioThreadGroup ioGroup;
//连
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 NioTcpAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, SocketChannel ch,
SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
public NioTcpAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread,
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.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;
}
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 read(CompletionHandler<Integer, ByteBuffer> handler) {
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
handler.failed(new NotYetConnectedException(), pollReadBuffer());
return;
}
if (this.readPending) {
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()) {
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) {
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()) {
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) {
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) {
handleConnect(null);
} else if (connectKey == null) {
ioThread.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);
}
}
private void handleConnect(Throwable t) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (t == null) {
handler.completed(null, attach);
} else {
handler.failed(t, attach);
}
}
}
private void clearConnect() {
this.connectCompletionHandler = null;
this.connectAttachment = null;
this.connectPending = false;//必须放最后
}
public void doRead() {
try {
final boolean invokeDirect = this.ioThread.inCurrThread();
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 (!readPending && readKey != null) readKey.interestOps(readKey.interestOps() & ~SelectionKey.OP_READ);
handleRead(totalCount, null);
} else if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = channel.register(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
handleRead(0, e);
}
});
} else {
ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
} catch (Exception e) {
handleRead(0, e);
}
}
private void handleRead(final int totalCount, Throwable t) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (t == null) {
handler.completed(totalCount, attach);
} else {
handler.failed(t, attach);
}
}
}
private void clearRead() {
this.readCompletionHandler = null;
this.readByteBuffer = null;
this.readPending = false; //必须放最后
}
public void doWrite() {
try {
final boolean invokeDirect = this.ioThread.inCurrThread();
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) {
handler.completed(totalCount, 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) {
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) {
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

@@ -1,167 +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.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import org.redkale.util.ObjectPool;
/**
*
* @author zhangjx
*/
public class NioTcpPrepareRunner implements Runnable {
private final AsyncConnection channel;
private final Context context;
private final ObjectPool<Response> responsePool;
private ByteBuffer data;
private Response response;
public NioTcpPrepareRunner(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() {
try {
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer count, ByteBuffer buffer) {
if (response == null) response = ((NioThread) Thread.currentThread()).getResponseSupplier().get();
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

@@ -1,123 +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.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioThread extends WorkThread {
final Selector selector;
private final Supplier<ByteBuffer> bufferSupplier;
private final Consumer<ByteBuffer> bufferConsumer;
private final Supplier<Response> responseSupplier;
private final Consumer<Response> responseConsumer;
private final ConcurrentLinkedQueue<Consumer<Selector>> registers = new ConcurrentLinkedQueue<>();
private boolean closed;
public NioThread(String name, ExecutorService workExecutor, Selector selector,
ObjectPool<ByteBuffer> unsafeBufferPool, ObjectPool<ByteBuffer> safeBufferPool,
ObjectPool<Response> unsafeResponsePool, ObjectPool<Response> safeResponsePool) {
super(name, workExecutor, null);
this.selector = selector;
this.setDaemon(true);
this.bufferSupplier = () -> inCurrThread() ? unsafeBufferPool.get() : safeBufferPool.get();
this.bufferConsumer = (v) -> {
if (inCurrThread()) {
unsafeBufferPool.accept(v);
} else {
safeBufferPool.accept(v);
}
};
this.responseSupplier = () -> inCurrThread() ? unsafeResponsePool.get() : safeResponsePool.get();
this.responseConsumer = (v) -> {
if (inCurrThread()) {
unsafeResponsePool.accept(v);
} else {
safeResponsePool.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 Supplier<Response> getResponseSupplier() {
return responseSupplier;
}
public Consumer<Response> getResponseConsumer() {
return responseConsumer;
}
@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();
if (!key.isValid()) continue;
NioTcpAsyncConnection conn = (NioTcpAsyncConnection) key.attachment();
if (key.isWritable()) {
//key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite();
} else if (key.isReadable()) {
conn.doRead();
} else if (key.isConnectable()) {
conn.doConnect();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public void close() {
this.closed = true;
this.interrupt();
}
}

View File

@@ -1,87 +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.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.redkale.net.Response;
import org.redkale.util.ObjectPool;
/**
* 协议处理的IO线程组
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioThreadGroup {
private NioThread[] threads;
private final AtomicInteger index = new AtomicInteger();
private ScheduledThreadPoolExecutor timeoutExecutor;
public NioThreadGroup(final String serverName, ExecutorService workExecutor, int iothreads,
ObjectPool<ByteBuffer> safeBufferPool, ObjectPool<Response> safeResponsePool) throws IOException {
this.threads = new NioThread[Math.max(iothreads, 1)];
for (int i = 0; i < this.threads.length; i++) {
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 8,
safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
ObjectPool<Response> unsafeResponsePool = ObjectPool.createUnsafePool(safeResponsePool.getCreatCounter(),
safeResponsePool.getCycleCounter(), 8,
safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler());
String name = "Redkale-" + serverName + "-ServletThread" + "-" + (i >= 9 ? (i + 1) : ("0" + (i + 1)));
this.threads[i] = new NioThread(name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool, unsafeResponsePool, safeResponsePool);
}
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName("Redkale-" + serverName + "-IOTimeoutThread");
t.setDaemon(true);
return t;
});
}
public void start() {
for (NioThread thread : threads) {
thread.start();
}
}
public void close() {
for (NioThread thread : threads) {
thread.close();
}
this.timeoutExecutor.shutdownNow();
}
public NioThread nextThread() {
return threads[Math.abs(index.getAndIncrement()) % threads.length];
}
public ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit) {
return timeoutExecutor.schedule(callable, delay, unit);
}
public void interestOpsOr(NioThread 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();
}
}

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.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -207,11 +208,16 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings); 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) {
request.prepare(); try {
response.filter = this.headFilter; request.prepare();
response.servlet = this; response.filter = this.headFilter;
response.nextEvent(); 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) { 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.io.IOException;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.util.AnyValue; import org.redkale.util.AnyValue;
/** /**
@@ -21,20 +22,14 @@ import org.redkale.util.AnyValue;
*/ */
public abstract class ProtocolServer { 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; protected final Context context;
//最大连接数小于1表示无限制 //最大连接数小于1表示无限制
protected int maxconns; protected int maxconns;
@Resource
protected Application application;
public abstract void open(AnyValue config) throws IOException; public abstract void open(AnyValue config) throws IOException;
public abstract void bind(SocketAddress local, int backlog) 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 <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; public abstract void close() throws IOException;
@@ -52,45 +47,28 @@ public abstract class ProtocolServer {
this.maxconns = context.getMaxconns(); 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) { public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader, String netimpl) {
if (netimpl != null) netimpl = netimpl.trim(); if (netimpl != null) netimpl = netimpl.trim();
if ("TCP".equalsIgnoreCase(protocol)) { if ("TCP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) { return new AsyncNioTcpProtocolServer(context);
return new AioTcpProtocolServer(context);
} else if ("aio".equalsIgnoreCase(netimpl)) {
return new AioTcpProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) {
return new NioTcpProtocolServer(context);
}
} else if ("UDP".equalsIgnoreCase(protocol)) { } else if ("UDP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) { return new AsyncNioUdpProtocolServer(context);
return null;// return new UdpBioProtocolServer(context);
} else if ("bio".equalsIgnoreCase(netimpl)) {
return null;// return new UdpBioProtocolServer(context);
}
} else if (netimpl == null || netimpl.isEmpty()) { } 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 { try {
if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
Class clazz = classLoader.loadClass(netimpl); Class clazz = classLoader.loadClass(netimpl);
return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context); return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context);
} catch (Exception e) { } 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

@@ -31,9 +31,13 @@ public abstract class Request<C extends Context> {
protected boolean keepAlive; 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; protected AsyncConnection channel;
@@ -51,41 +55,35 @@ public abstract class Request<C extends Context> {
this.jsonConvert = context.getJsonConvert(); this.jsonConvert = context.getJsonConvert();
} }
protected void setMoredata(ByteBuffer buffer) { protected Request copyHeader() {
this.moredata = buffer; return null;
} }
protected ByteBuffer removeMoredata() { protected Request pipeline(int pipelineIndex, int pipelineCount) {
ByteBuffer rs = this.moredata; this.pipelineIndex = pipelineIndex;
this.moredata = null; this.pipelineCount = pipelineCount;
return rs; return this;
} }
/** /**
* 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。 * 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。
* *
* @param buffer ByteBuffer对象 * @param buffer ByteBuffer对象
* @param last 同一Channel的上一个Request
* *
* @return 缺少的字节数 * @return 缺少的字节数
*/ */
protected abstract int readHeader(ByteBuffer buffer); protected abstract int readHeader(ByteBuffer buffer, Request last);
/**
* 读取buffer并返回读取的有效数据长度
*
* @param buffer ByteBuffer对象
*
* @return 有效数据长度
*/
protected abstract int readBody(ByteBuffer buffer);
protected abstract void prepare(); protected abstract void prepare();
protected void recycle() { protected void recycle() {
hashid = 0;
createtime = 0; createtime = 0;
pipelineIndex = 0;
pipelineCount = 0;
pipelineOver = false;
keepAlive = false; keepAlive = false;
more = false;
moredata = null;
attributes.clear(); attributes.clear();
channel = null; // close it by response channel = null; // close it by response
} }
@@ -136,4 +134,13 @@ public abstract class Request<C extends Context> {
return createtime; 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.nio.channels.CompletionHandler;
import java.util.function.*; import java.util.function.*;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.util.ObjectPool; import org.redkale.util.ByteTuple;
/** /**
* 协议响应对象 * 协议响应对象
@@ -25,11 +25,11 @@ import org.redkale.util.ObjectPool;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public abstract class Response<C extends Context, R extends Request<C>> { public abstract class Response<C extends Context, R extends Request<C>> {
protected static final boolean respConvertByBuffer = Boolean.getBoolean("resp.convert.bytebuffer");
protected final C context; 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; protected final R request;
@@ -45,20 +45,26 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected Servlet<C, R, ? extends Response<C, R>> servlet; 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 @Override
public void completed(Integer result, ByteBuffer attachment) { public void completed(Integer result, ByteBuffer attachment) {
if (attachment.hasRemaining()) { channel.offerBuffer(attachment);
channel.write(attachment, attachment, this); finish();
} 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 @Override
@@ -69,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 @Override
public void completed(final Integer result, final ByteBuffer[] attachments) { public void completed(final Integer result, final ByteBuffer[] attachments) {
int index = -1; if (attachments != null) {
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 {
for (ByteBuffer attachment : attachments) { for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment); 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 @Override
public void failed(Throwable exc, final ByteBuffer[] attachments) { public void failed(Throwable exc, final ByteBuffer[] attachments) {
for (ByteBuffer attachment : attachments) { if (attachments != null) {
channel.offerBuffer(attachment); for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
}
} }
finish(true); finish(true);
} }
}; };
protected Response(C context, final R request, ObjectPool<Response> responsePool) { protected Response(C context, final R request) {
this.context = context; this.context = context;
this.request = request; this.request = request;
this.responsePool = responsePool;
}
protected void offerBuffer(ByteBuffer... buffers) {
for (ByteBuffer buffer : buffers) {
channel.offerBuffer(buffer);
}
} }
protected AsyncConnection removeChannel() { protected AsyncConnection removeChannel() {
@@ -125,6 +113,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected void prepare() { protected void prepare() {
inited = true; inited = true;
request.prepare();
} }
protected boolean recycle() { protected boolean recycle() {
@@ -132,11 +121,14 @@ public abstract class Response<C extends Context, R extends Request<C>> {
this.output = null; this.output = null;
this.filter = null; this.filter = null;
this.servlet = null; this.servlet = null;
boolean notpipeline = request.pipelineIndex == 0 || request.pipelineOver;
request.recycle(); request.recycle();
if (channel != null) { if (channel != null) {
channel.dispose(); if (notpipeline) channel.dispose();
channel = null; channel = null;
} }
this.responseSupplier = null;
this.responseConsumer = null;
this.inited = false; this.inited = false;
return true; return true;
} }
@@ -207,73 +199,127 @@ public abstract class Response<C extends Context, R extends Request<C>> {
} }
this.recycleListener = null; this.recycleListener = null;
} }
if (request.more) removeChannel(); if (request.keepAlive && (request.pipelineIndex == 0 || request.pipelineOver)) {
if (request.keepAlive && !request.more && channel != null) { AsyncConnection conn = removeChannel();
if (channel.isOpen()) { if (conn != null && conn.protocolCodec != null) {
AsyncConnection conn = removeChannel(); this.responseConsumer.accept(this);
this.recycle(); conn.read(conn.protocolCodec);
this.prepare();
new PrepareRunner(context, this.responsePool, conn, null, this).run();
} else { } else {
channel.dispose(); Supplier<Response> poolSupplier = this.responseSupplier;
Consumer<Response> poolConsumer = this.responseConsumer;
this.recycle();
new ProtocolCodec(context, poolSupplier, poolConsumer, conn).response(this).run(null);
} }
} else { } else {
this.responsePool.accept(this); this.responseConsumer.accept(this);
} }
} }
public void finish(final byte[] bs) { public final void finish(final byte[] bs) {
finish(false, bs, 0, bs.length);
}
public final void finish(final byte[] bs, int offset, int length) {
finish(false, bs, offset, length);
}
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 (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) { if (kill) refuseAlive();
ByteBuffer buffer = channel.getBufferSupplier().get(); if (this.channel.hasPipelineData()) {
buffer.put(bs); this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
buffer.flip();
this.finish(buffer); @Override
public void completed(Integer result, Void attachment) {
channel.write(bs, offset, length, finishBytesHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBytesHandler.failed(exc, attachment);
}
});
} else { } else {
this.finish(ByteBuffer.wrap(bs)); this.channel.write(bs, offset, length, finishBytesHandler);
} }
} }
public void finish(ByteBuffer buffer) { protected final void finish(ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭 finish(false, buffer);
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 void finish(boolean kill, ByteBuffer buffer) { protected final void finish(ByteBuffer... buffers) {
finish(false, buffers);
}
protected void finish(boolean kill, ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive(); if (kill) refuseAlive();
final AsyncConnection conn = this.channel; if (this.channel.hasPipelineData()) {
// ByteBuffer data = this.request.removeMoredata(); this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
// final boolean more = data != null && this.request.keepAlive;
// this.request.more = more; @Override
conn.write(buffer, buffer, finishHandler); public void completed(Integer result, Void attachment) {
// if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run(); channel.write(buffer, buffer, finishBufferHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBufferHandler.failed(exc, buffer);
}
});
} else {
this.channel.write(buffer, buffer, finishBufferHandler);
}
} }
public void finish(ByteBuffer... buffers) { protected void finish(boolean kill, 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();
}
public void finish(boolean kill, ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive(); if (kill) refuseAlive();
final AsyncConnection conn = this.channel; if (this.channel.hasPipelineData()) {
ByteBuffer data = this.request.removeMoredata(); this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
final boolean more = data != null && this.request.keepAlive;
this.request.more = more; @Override
conn.write(buffers, buffers, finishHandler2); public void completed(Integer result, Void attachment) {
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run(); 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) { protected <A> void send(final ByteBuffer buffer, final A attachment, final CompletionHandler<Integer, A> handler) {
@@ -281,12 +327,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override @Override
public void completed(Integer result, A attachment) { public void completed(Integer result, A attachment) {
if (buffer.hasRemaining()) { channel.offerBuffer(buffer);
channel.write(buffer, attachment, this); if (handler != null) handler.completed(result, attachment);
} else {
channel.offerBuffer(buffer);
if (handler != null) handler.completed(result, attachment);
}
} }
@Override @Override
@@ -303,21 +345,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override @Override
public void completed(Integer result, A attachment) { public void completed(Integer result, A attachment) {
int index = -1; channel.offerBuffer(buffers);
for (int i = 0; i < buffers.length; i++) { if (handler != null) handler.completed(result, attachment);
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);
} }
@Override @Override

View File

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

View File

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

View File

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

View File

@@ -6,6 +6,7 @@
package org.redkale.net; package org.redkale.net;
import java.util.concurrent.*; import java.util.concurrent.*;
import org.redkale.util.ThreadHashExecutor;
/** /**
* 协议处理的自定义线程类 * 协议处理的自定义线程类
@@ -21,15 +22,42 @@ public class WorkThread extends Thread {
protected final ExecutorService workExecutor; protected final ExecutorService workExecutor;
protected final ThreadHashExecutor hashExecutor;
public WorkThread(String name, ExecutorService workExecutor, Runnable target) { public WorkThread(String name, ExecutorService workExecutor, Runnable target) {
super(target); super(target);
if (name != null) setName(name); if (name != null) setName(name);
this.workExecutor = workExecutor; this.workExecutor = workExecutor;
this.hashExecutor = workExecutor instanceof ThreadHashExecutor ? (ThreadHashExecutor) workExecutor : null;
this.setDaemon(true); this.setDaemon(true);
} }
public void runAsync(Runnable runner) { public void runWork(Runnable command) {
workExecutor.execute(runner); if (workExecutor == null) {
command.run();
} else {
workExecutor.execute(command);
}
}
public void runAsync(Runnable command) {
if (workExecutor == null) {
ForkJoinPool.commonPool().execute(command);
} else {
workExecutor.execute(command);
}
}
public void runAsync(int hash, Runnable command) {
if (hashExecutor == null) {
if (workExecutor == null) {
ForkJoinPool.commonPool().execute(command);
} else {
workExecutor.execute(command);
}
} else {
hashExecutor.execute(hash, command);
}
} }
public ExecutorService getWorkExecutor() { public ExecutorService getWorkExecutor() {

View File

@@ -0,0 +1,219 @@
/*
* 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.client;
import java.net.SocketAddress;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import org.redkale.net.*;
import org.redkale.util.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
*
* @param <R> 请求对象
* @param <P> 响应对象
*/
public abstract class Client<R extends ClientRequest, P> {
protected final AsyncGroup group; //连接构造器
protected final boolean tcp; //是否TCP协议
protected final SocketAddress address; //连接的地址
protected final ConcurrentLinkedDeque<CompletableFuture<ClientConnection>> connQueue = new ConcurrentLinkedDeque();
protected final Creator<ClientCodec<R, P>> codecCreator;
protected final ScheduledThreadPoolExecutor timeoutScheduler;
protected final AtomicLong writeReqCounter = new AtomicLong();
protected final AtomicLong pollRespCounter = new AtomicLong();
protected ScheduledFuture timeoutFuture;
protected ClientConnection<R, P>[] connArray; //连接池
protected AtomicInteger[] connResps; //连接当前处理数
protected AtomicBoolean[] connFlags; //conns的标记组当conn不存在或closed状态标记为false
protected int connLimit = Runtime.getRuntime().availableProcessors(); //最大连接数
protected int maxPipelines = 16; //单个连接最大并行处理数
protected int readTimeoutSeconds;
protected int writeTimeoutSeconds;
//------------------ 可选项 ------------------
//PING心跳的请求数据为null且pingInterval<1表示不需要定时ping
protected R pingRequest;
//关闭请求的数据, 为null表示直接关闭
protected R closeRequest;
//创建连接后进行的登录鉴权操作
protected Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate;
protected Client(AsyncGroup group, SocketAddress address, Creator<ClientCodec<R, P>> responseCreator) {
this(group, true, address, Runtime.getRuntime().availableProcessors(), 16, responseCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, Creator<ClientCodec<R, P>> codecCreator) {
this(group, tcp, address, Runtime.getRuntime().availableProcessors(), 16, codecCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator) {
this(group, tcp, address, maxconns, 16, codecCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, int maxPipelines, Creator<ClientCodec<R, P>> codecCreator) {
this(group, tcp, address, maxconns, maxPipelines, codecCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator,
Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
this(group, tcp, address, maxconns, 16, codecCreator, null, null, authenticate);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator,
R closeRequest, Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
this(group, tcp, address, maxconns, 16, codecCreator, null, closeRequest, authenticate);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns,
int maxPipelines, Creator<ClientCodec<R, P>> codecCreator, R pingRequest, R closeRequest,
Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
if (maxPipelines < 1) throw new IllegalArgumentException("maxPipelines must bigger 0");
this.group = group;
this.tcp = tcp;
this.address = address;
this.connLimit = maxconns;
this.maxPipelines = maxPipelines;
this.pingRequest = pingRequest;
this.closeRequest = closeRequest;
this.codecCreator = codecCreator;
this.authenticate = authenticate;
this.connArray = new ClientConnection[connLimit];
this.connFlags = new AtomicBoolean[connLimit];
this.connResps = new AtomicInteger[connLimit];
for (int i = 0; i < connFlags.length; i++) {
this.connFlags[i] = new AtomicBoolean();
this.connResps[i] = new AtomicInteger();
}
//timeoutScheduler 不仅仅给超时用, 还给write用
this.timeoutScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, "Redkale-" + Client.this.getClass().getSimpleName() + "-Interval-Thread");
t.setDaemon(true);
return t;
});
if (pingRequest != null) {
this.timeoutFuture = this.timeoutScheduler.scheduleAtFixedRate(() -> {
try {
ClientRequest req = pingRequest;
if (req == null) { //可能运行中进行重新赋值
timeoutFuture.cancel(true);
timeoutFuture = null;
return;
}
long now = System.currentTimeMillis();
for (ClientConnection conn : this.connArray) {
if (conn == null) continue;
if (now - conn.getLastWriteTime() > 10_000) conn.writeChannel(req);
}
} catch (Throwable t) {
}
}, 10, 10, TimeUnit.SECONDS);
}
}
public void close() {
this.timeoutScheduler.shutdownNow();
final R closereq = closeRequest;
for (ClientConnection conn : this.connArray) {
if (conn == null) continue;
if (closereq == null) {
conn.dispose(null);
} else {
try {
conn.writeChannel(closereq).get(100, TimeUnit.MILLISECONDS);
} catch (Exception e) {
}
conn.dispose(null);
}
}
}
public CompletableFuture<P> sendAsync(R request) {
return connect().thenCompose(conn -> conn.writeChannel(request));
}
protected CompletableFuture<P> writeChannel(ClientConnection conn, R request) {
return conn.writeChannel(request);
}
protected CompletableFuture<ClientConnection> connect() {
ClientConnection minRunningConn = null;
for (int i = 0; i < this.connArray.length; i++) {
final int index = i;
final ClientConnection conn = this.connArray[index];
if (conn == null || !conn.isOpen()) {
if (this.connFlags[index].compareAndSet(false, true)) {
CompletableFuture<ClientConnection> future = group.create(tcp, address, readTimeoutSeconds, writeTimeoutSeconds).thenApply(c -> createClientConnection(index, c));
return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> {
c.authenticated = true;
this.connArray[index] = c;
return c;
}).whenComplete((r, t) -> {
if (t != null) this.connFlags[index].set(false);
});
}
} else if (conn.runningCount() < 1) {
return CompletableFuture.completedFuture(conn);
} else if (minRunningConn == null || minRunningConn.runningCount() > conn.runningCount()) {
minRunningConn = conn;
}
}
if (minRunningConn != null && minRunningConn.runningCount() < maxPipelines) return CompletableFuture.completedFuture(minRunningConn);
return waitClientConnection();
}
protected CompletableFuture<ClientConnection> waitClientConnection() {
CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS);
connQueue.offer(rs);
return rs;
}
protected ClientConnection createClientConnection(final int index, AsyncConnection channel) {
return new ClientConnection(this, index, channel);
}
public int getReadTimeoutSeconds() {
return readTimeoutSeconds;
}
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
public int getWriteTimeoutSeconds() {
return writeTimeoutSeconds;
}
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.client;
import java.nio.ByteBuffer;
import java.util.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.ByteArray;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
* @param <R> ClientRequest
* @param <P> 响应对象
*/
public abstract class ClientCodec<R extends ClientRequest, P> {
private final List<ClientResult<P>> results = new ArrayList<>();
public ClientCodec() {
}
//返回true后array会clear
public abstract boolean codecResult(ClientConnection conn, List<R> requests, ByteBuffer buffer, ByteArray array);
public List<ClientResult<P>> removeResults() {
if (results.isEmpty()) return null;
List<ClientResult<P>> rs = new ArrayList<>(results);
this.results.clear();
return rs;
}
public void addResult(P result) {
this.results.add(new ClientResult<>(result));
}
public void addResult(Throwable exc) {
this.results.add(new ClientResult<>(exc));
}
public void reset() {
this.results.clear();
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -0,0 +1,273 @@
/*
* 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.client;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.stream.*;
import org.redkale.net.AsyncConnection;
import org.redkale.util.ByteArray;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
*
* @param <R> 请求对象
* @param <P> 响应对象
*/
public class ClientConnection<R extends ClientRequest, P> implements Consumer<AsyncConnection> {
protected final int index;
protected final Client<R, P> client;
protected final AtomicInteger respCounter;
protected final AsyncConnection channel;
protected final ByteArray writeArray = new ByteArray();
protected final ByteArray readArray = new ByteArray();
protected final AtomicBoolean readPending = new AtomicBoolean();
protected final AtomicBoolean writePending = new AtomicBoolean();
protected final ConcurrentLinkedDeque<R> requestQueue = new ConcurrentLinkedDeque();
protected final ConcurrentLinkedDeque<ClientFuture> responseQueue = new ConcurrentLinkedDeque();
protected final CompletionHandler<Integer, Void> writeHandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (writeLastRequest != null && writeLastRequest == client.closeRequest) {
if (closeFuture != null) closeFuture.complete(null);
closeFuture = null;
return;
}
if (continueWrite()) return;
if (!writePending.compareAndSet(true, false)) {
if (continueWrite()) return;
}
readChannel();
}
@Override
public void failed(Throwable exc, Void attachment) {
dispose(exc);
}
};
private boolean continueWrite() {
writeArray.clear();
int pipelines = client.maxPipelines > 1 ? (client.maxPipelines - responseQueue.size()) : 1;
currPipelineIndex = 0;
for (int i = 0; i < pipelines; i++) {
R r = requestQueue.poll();
if (r == null) break;
writeLastRequest = r;
r.accept(ClientConnection.this, writeArray);
currPipelineIndex++;
}
if (writeArray.length() > 0) {
channel.write(writeArray, writeHandler);
return true;
}
return false;
}
protected final CompletionHandler<Integer, ByteBuffer> readHandler = new CompletionHandler<Integer, ByteBuffer>() {
ClientCodec<R, P> codec;
@Override
public void completed(Integer count, ByteBuffer attachment) {
if (count < 1) {
channel.setReadBuffer(attachment);
dispose(new NonReadableChannelException());
return;
}
try {
if (codec == null) codec = client.codecCreator.create();
attachment.flip();
codecResponse(attachment);
} catch (Exception e) {
channel.setReadBuffer(attachment);
dispose(e);
}
}
public void codecResponse(ByteBuffer buffer) {
Stream<R> reqstream = responseQueue.stream().map(r -> (R) r.request);
List<R> requests = reqstream.collect(Collectors.toList());
if (codec.codecResult(ClientConnection.this, requests, buffer, readArray)) { //成功了
readArray.clear();
List<ClientResult<P>> results = codec.removeResults();
if (results != null) {
for (ClientResult<P> rs : results) {
ClientFuture respFuture = responseQueue.poll();
if (respFuture != null) {
respCounter.decrementAndGet();
if (isAuthenticated()) client.pollRespCounter.incrementAndGet();
try {
if (respFuture.timeout != null) respFuture.timeout.cancel(true);
if (rs.exc != null) {
respFuture.completeExceptionally(rs.exc);
} else {
respFuture.complete(rs.result);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}
{
CompletableFuture<ClientConnection> connFuture = client.connQueue.poll();
if (connFuture != null) connFuture.complete(ClientConnection.this);
}
if (buffer.hasRemaining()) {
codecResponse(buffer);
} else if (responseQueue.isEmpty()) { //队列都已处理完了
buffer.clear();
channel.setReadBuffer(buffer);
if (readPending.compareAndSet(true, false)) {
CompletableFuture<ClientConnection> connFuture = client.connQueue.poll();
if (connFuture != null) connFuture.complete(ClientConnection.this);
} else {
channel.read(this);
}
} else {
buffer.clear();
channel.setReadBuffer(buffer);
channel.read(this);
}
} else { //数据不全, 继续读
buffer.clear();
channel.setReadBuffer(buffer);
channel.read(this);
}
}
@Override
public void failed(Throwable t, ByteBuffer attachment) {
dispose(t);
}
};
protected boolean authenticated;
protected int currPipelineIndex;
protected ClientFuture closeFuture;
private R writeLastRequest;
public ClientConnection(Client client, int index, AsyncConnection channel) {
this.client = client;
this.index = index;
this.respCounter = client.connResps[index];
this.channel = channel.beforeCloseListener(this);
}
protected CompletableFuture<P> writeChannel(R request) {
ClientFuture respFuture = createClientFuture(request);
if (request == client.closeRequest) {
respFuture.request = null;
closeFuture = respFuture;
} else {
int rts = this.channel.getReadTimeoutSeconds();
if (rts > 0 && respFuture.request != null) {
respFuture.responseQueue = responseQueue;
respFuture.timeout = client.timeoutScheduler.schedule(respFuture, rts, TimeUnit.SECONDS);
}
}
synchronized (requestQueue) { //保证顺序一致
responseQueue.offer(respFuture.request == null ? ClientFuture.EMPTY : respFuture);
requestQueue.offer(request);
respCounter.incrementAndGet();
if (isAuthenticated()) client.writeReqCounter.incrementAndGet();
}
if (writePending.compareAndSet(false, true)) {
writeArray.clear();
int pipelines = client.maxPipelines > 1 ? client.maxPipelines : 1; //pipelines必须大于0
currPipelineIndex = 0;
for (int i = 0; i < pipelines; i++) {
R r = requestQueue.poll();
if (r == null) break;
r.accept(ClientConnection.this, writeArray);
currPipelineIndex++;
}
channel.write(writeArray, writeHandler);
}
return respFuture;
}
protected ClientFuture createClientFuture(R request) {
return new ClientFuture(request);
}
protected void readChannel() {
if (readPending.compareAndSet(false, true)) {
readArray.clear();
channel.read(readHandler);
}
}
public boolean isAuthenticated() {
return authenticated;
}
public AsyncConnection getChannel() {
return channel;
}
public int currPipelineIndex() {
return currPipelineIndex;
}
@Override //AsyncConnection.beforeCloseListener
public void accept(AsyncConnection t) {
respCounter.set(0);
client.connFlags[index].set(false);
client.connArray[index] = null; //必须connflags之后
}
public void dispose(Throwable exc) {
channel.dispose();
Throwable e = exc;
CompletableFuture f;
respCounter.set(0);
while ((f = responseQueue.poll()) != null) {
if (e == null) e = new ClosedChannelException();
f.completeExceptionally(e);
}
}
public int runningCount() {
return respCounter.get();
}
public long getLastWriteTime() {
return channel.getLastWriteTime();
}
public boolean isOpen() {
return channel.isOpen();
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.client;
import java.util.concurrent.*;
/**
*
* @author zhangjx
* @param <T> 泛型
*/
public class ClientFuture<T> extends CompletableFuture<T> implements Runnable {
public static final ClientFuture EMPTY = new ClientFuture() {
@Override
public boolean complete(Object value) {
return true;
}
@Override
public boolean completeExceptionally(Throwable ex) {
return true;
}
};
protected ClientRequest request;
ScheduledFuture timeout;
ConcurrentLinkedDeque<ClientFuture> responseQueue;
public ClientFuture() {
super();
}
public ClientFuture(ClientRequest request) {
super();
this.request = request;
}
//@Override //JDK9+
public <U> ClientFuture<U> newIncompleteFuture() {
return new ClientFuture<>();
}
public <R extends ClientRequest> R getRequest() {
return (R) request;
}
@Override
public void run() {
if (responseQueue != null) responseQueue.remove(this);
this.completeExceptionally(new TimeoutException());
}
}

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