65 Commits
2.2.0 ... 2.3.0

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

View File

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

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

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

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.xml.parsers.*; import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.cluster.*;
import org.redkale.convert.Convert; import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory; import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
@@ -88,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>
*/ */
@@ -105,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
@@ -122,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<>();
@@ -134,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;
@@ -288,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;
@@ -300,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");
@@ -315,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 = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
e.clear();
return true;
});
//-----------transportChannelGroup--------------
try {
final String strategyClass = transportConf.getValue("strategy");
if (strategyClass != null && !strategyClass.isEmpty()) {
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).getDeclaredConstructor().newInstance();
}
final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t;
});
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
} }
AnyValue clusterConf = resources.getAnyValue("cluster"); AnyValue clusterConf = resources.getAnyValue("cluster");
@@ -356,6 +353,13 @@ public final class Application {
break; break;
} }
} }
if (cluster == null) {
ClusterAgent cacheClusterAgent = new CacheClusterAgent();
if (cacheClusterAgent.match(clusterConf)) {
cluster = cacheClusterAgent;
cluster.setConfig(clusterConf);
}
}
if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='value' value error: " + clusterConf); if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='value' value error: " + clusterConf);
} else { } else {
Class type = classLoader.loadClass(clusterConf.getValue("value")); Class type = classLoader.loadClass(clusterConf.getValue("value"));
@@ -416,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 = new ObjectPool<>(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"));
@@ -462,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;
} }
@@ -669,10 +691,30 @@ 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();
if (this.clusterAgent instanceof CacheClusterAgent) {
String sourceName = ((CacheClusterAgent) clusterAgent).getSourceName(); //必须在inject前调用需要赋值Resourcable.name
loadCacheSource(sourceName);
}
clusterAgent.setTransportFactory(this.sncpTransportFactory); clusterAgent.setTransportFactory(this.sncpTransportFactory);
this.resourceFactory.inject(clusterAgent); this.resourceFactory.inject(clusterAgent);
clusterAgent.init(clusterAgent.getConfig()); clusterAgent.init(clusterAgent.getConfig());
@@ -692,7 +734,7 @@ public final class Application {
logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms"); logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms");
} }
//------------------------------------- 注册 DataSource -------------------------------------------------------- //------------------------------------- 注册 HttpMessageClient --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try { try {
if (field.getAnnotation(Resource.class) == null) return; if (field.getAnnotation(Resource.class) == null) return;
@@ -702,12 +744,44 @@ public final class Application {
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值; rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient); rf.register(resourceName, HttpMessageClient.class, messageClient);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject error", e); logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpMessageClient inject error", e);
} }
}, HttpMessageClient.class); }, HttpMessageClient.class);
initResources(); initResources();
} }
private void loadCacheSource(final String sourceName) {
final AnyValue resources = config.getAnyValue("resources");
for (AnyValue sourceConf : resources.getAnyValues("source")) {
if (!sourceName.equals(sourceConf.getValue("name"))) continue;
String classval = sourceConf.getValue("value");
try {
Class sourceType = CacheMemorySource.class;
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
sourceType = s.getClass();
break;
}
}
} else {
sourceType = serverClassLoader.loadClass(classval);
}
CacheSource source = Modifier.isFinal(sourceType.getModifiers()) ? (CacheSource) sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, sourceName, sourceType, null, resourceFactory, sncpTransportFactory, null, null, sourceConf);
cacheSources.add((CacheSource) source);
resourceFactory.register(sourceName, CacheSource.class, source);
resourceFactory.inject(source);
if (source instanceof Service) ((Service) source).init(sourceConf);
logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + sourceName + ", source = " + source);
} catch (Exception e) {
logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e);
}
return;
}
}
private void initResources() throws Exception { private void initResources() throws Exception {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
@@ -715,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"));
} }
@@ -741,17 +815,6 @@ public final class Application {
//------------------------------------------------------------------------ //------------------------------------------------------------------------
} }
public void restoreConfig() throws IOException {
if (!"file".equals(this.confPath.getScheme())) return;
synchronized (this) {
File confFile = new File(this.confPath.toString(), "application.xml");
confFile.renameTo(new File(this.confPath.toString(), "application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
final PrintStream ps = new PrintStream(new FileOutputStream(confFile));
ps.append(config.toXML("application"));
ps.close();
}
}
private void startSelfServer() throws Exception { private void startSelfServer() throws Exception {
final Application application = this; final Application application = this;
new Thread() { new Thread() {
@@ -971,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);
} }
@@ -1071,6 +1133,52 @@ public final class Application {
return new Application(singleton, loadAppXml()); return new Application(singleton, loadAppXml());
} }
public void reloadConfig() throws IOException {
AnyValue newconfig = loadAppXml();
final String confpath = this.confPath.toString();
final String homepath = this.home.getCanonicalPath();
final AnyValue resources = newconfig.getAnyValue("resources");
if (resources != null) {
resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources);
final AnyValue properties = resources.getAnyValue("properties");
if (properties != null) {
String dfloads = properties.getValue("load");
if (dfloads != null) {
for (String dfload : dfloads.split(";")) {
if (dfload.trim().isEmpty()) continue;
final URI df = (dfload.indexOf('/') < 0) ? URI.create(confpath + (confpath.endsWith("/") ? "" : "/") + dfload) : new File(dfload).toURI();
if (!"file".equals(df.getScheme()) || new File(df).isFile()) {
Properties ps = new Properties();
try {
InputStream in = df.toURL().openStream();
ps.load(in);
in.close();
ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath)));
} catch (Exception e) {
logger.log(Level.WARNING, "load properties(" + dfload + ") error", e);
}
}
}
}
for (AnyValue prop : properties.getAnyValues("property")) {
String name = prop.getValue("name");
String value = prop.getValue("value");
if (name == null || value == null) continue;
value = value.replace("${APP_HOME}", homepath);
if (name.startsWith("system.property.")) {
System.setProperty(name.substring("system.property.".length()), value);
} else if (name.startsWith("mimetype.property.")) {
MimeType.add(name.substring("mimetype.property.".length()), value);
} else if (name.startsWith("property.")) {
resourceFactory.register(name, value);
} else {
resourceFactory.register("property." + name, value);
}
}
}
}
}
private static AnyValue loadAppXml() throws IOException { private static AnyValue loadAppXml() throws IOException {
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/'); final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home); System.setProperty(RESNAME_APP_HOME, home);

View File

@@ -46,7 +46,7 @@ public class NodeHttpServer extends NodeServer {
} }
private static Server createServer(Application application, AnyValue serconf) { 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", "");
@@ -271,6 +271,34 @@ public abstract class NodeServer {
} }
}, AnyValue.class, AnyValue[].class); }, AnyValue.class, AnyValue[].class);
//------------------------------------- 注册 Local AutoLoad(false) Service --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
Class<Service> resServiceType = Service.class;
try {
if (field.getAnnotation(Resource.class) == null) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 AutoLoad Service
if (!Service.class.isAssignableFrom(field.getType())) return;
resServiceType = (Class) field.getType();
if (resServiceType.getAnnotation(Local.class) == null) return;
AutoLoad al = resServiceType.getAnnotation(AutoLoad.class);
if (al == null || al.value()) return;
//ResourceFactory resfactory = (isSNCP() ? appResFactory : resourceFactory);
SncpClient client = src instanceof Service ? Sncp.getSncpClient((Service) src) : null;
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
Service service = Sncp.createLocalService(serverClassLoader, resourceName, resServiceType, null, appResFactory, appSncpTranFactory, sncpAddr, groups, null);
appResFactory.register(resourceName, resServiceType, service);
field.set(src, service);
rf.inject(service, self); // 给其可能包含@Resource的字段赋值;
service.init(null);
logger.info("[" + Thread.currentThread().getName() + "] Load Service(@Local @AutoLoad) resourceName = " + resourceName + ", service = " + service);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Load @Local @AutoLoad(false) Service inject " + resServiceType + " to " + src + " error", e);
}
}, Service.class);
//------------------------------------- 注册 DataSource -------------------------------------------------------- //------------------------------------- 注册 DataSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try { try {
@@ -279,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) {
@@ -303,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);
} }
@@ -355,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); //
@@ -375,7 +397,7 @@ public abstract class NodeServer {
sncpServer.getSncpServer().addSncpServlet((Service) source); sncpServer.getSncpServer().addSncpServlet((Service) source);
//logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source); //logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source);
} }
logger.info("[" + Thread.currentThread().getName() + "] Load Source " + source); logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + resourceName + ", source = " + source);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e); logger.log(Level.SEVERE, "DataSource inject error", e);
} }
@@ -750,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

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

View File

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

View File

@@ -107,15 +107,14 @@ public abstract class ClusterAgent {
//注册本地模式 //注册本地模式
for (Service service : localServices) { for (Service service : localServices) {
if (!canRegister(protocol, service)) continue; if (!canRegister(protocol, service)) continue;
register(ns, protocol, service); ClusterEntry htentry = register(ns, protocol, service);
ClusterEntry htentry = new ClusterEntry(ns, protocol, service);
localEntrys.put(htentry.serviceid, htentry); localEntrys.put(htentry.serviceid, htentry);
if (protocol.toLowerCase().startsWith("http")) { if (protocol.toLowerCase().startsWith("http")) {
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
if (mmc != null) { if (mmc != null) {
register(ns, "mqtp", service); ClusterEntry mqentry = register(ns, "mqtp", service);
ClusterEntry mqentry = new ClusterEntry(ns, "mqtp", service);
localEntrys.put(mqentry.serviceid, mqentry); localEntrys.put(mqentry.serviceid, mqentry);
htentry.submqtp = true;
} }
} }
} }
@@ -141,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;
} }
@@ -176,7 +177,7 @@ public abstract class ClusterAgent {
protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry); protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry);
//注册服务 //注册服务
protected abstract void register(NodeServer ns, String protocol, Service service); protected abstract ClusterEntry register(NodeServer ns, String protocol, Service service);
//注销服务 //注销服务
protected abstract void deregister(NodeServer ns, String protocol, Service service); protected abstract void deregister(NodeServer ns, String protocol, Service service);
@@ -313,16 +314,24 @@ public abstract class ClusterAgent {
public boolean canceled; public boolean canceled;
public boolean submqtp;
public ClusterEntry(NodeServer ns, String protocol, Service service) { public ClusterEntry(NodeServer ns, String protocol, Service service) {
this.serviceid = generateServiceId(ns, protocol, service); this.serviceid = generateServiceId(ns, protocol, service);
this.servicename = generateServiceName(ns, protocol, service); this.servicename = generateServiceName(ns, protocol, service);
this.checkid = generateCheckId(ns, protocol, service); this.checkid = generateCheckId(ns, protocol, service);
this.checkname = generateCheckName(ns, protocol, service); this.checkname = generateCheckName(ns, protocol, service);
this.protocol = protocol; this.protocol = protocol;
this.address = ns.getSocketAddress(); InetSocketAddress addr = ns.getSocketAddress();
String host = addr.getHostString();
if ("0.0.0.0".equals(host)) {
host = appAddress.getHostString();
addr = new InetSocketAddress(host, addr.getPort());
}
this.address = addr;
this.serviceref = new WeakReference(service); 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,10 +61,16 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public abstract byte[] convertToBytes(final Type type, final Object value); public abstract 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 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

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

View File

@@ -31,6 +31,10 @@ public final class EnMember<W extends Writer, T, F> {
//final boolean isnumber; //final boolean 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

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

View File

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

View File

@@ -32,5 +32,4 @@ public abstract class TextConvert<R extends Reader, W extends Writer> extends Co
public abstract String convertTo(final Type type, final Object value); public abstract String 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,25 +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 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) {
@@ -272,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);
@@ -287,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.*;
@@ -42,7 +43,7 @@ public class BsonReader extends Reader {
} }
public static ObjectPool<BsonReader> createPool(int max) { public static ObjectPool<BsonReader> createPool(int max) {
return new ObjectPool<>(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle()); return ObjectPool.createSafePool(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle());
} }
public BsonReader(byte[] bytes) { public BsonReader(byte[] bytes) {
@@ -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));
@@ -29,7 +30,48 @@ public class BsonWriter extends Writer {
protected boolean tiny; protected boolean tiny;
public static ObjectPool<BsonWriter> createPool(int max) { public static ObjectPool<BsonWriter> createPool(int max) {
return new ObjectPool<>(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle()); return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
}
@Override
public byte[] content() {
return content;
}
@Override
public int offset() {
return 0;
}
@Override
public int length() {
return count;
}
/**
* 直接获取全部数据, 实际数据需要根据count长度来截取
*
* @return byte[]
*/
public byte[] directBytes() {
return content;
}
/**
* 将本对象的内容引用复制给array
*
* @param array ByteArray
*/
public void directTo(ByteArray array) {
array.directFrom(content, count);
}
public void completed(ConvertBytesHandler handler, Consumer<BsonWriter> callback) {
handler.completed(content, 0, count, callback, this);
}
public ByteArray toByteArray() {
return new ByteArray(this);
} }
public byte[] toArray() { public byte[] toArray() {
@@ -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

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

View File

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

View File

@@ -22,6 +22,10 @@ import org.redkale.util.*;
*/ */
public class JsonByteBufferWriter extends JsonWriter { 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,12 +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 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;
@@ -65,42 +71,39 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
}; };
} }
//------------------------------ reader -----------------------------------------------------------
public JsonReader pollJsonReader(final ByteBuffer... buffers) {
return new JsonByteBufferReader((ConvertMask) null, buffers);
}
public JsonReader pollJsonReader(final InputStream in) {
return new JsonStreamReader(in);
}
public JsonReader pollJsonReader() {
return readerPool.get();
}
public void offerJsonReader(final JsonReader in) {
if (in != null) readerPool.accept(in);
}
//------------------------------ writer ----------------------------------------------------------- //------------------------------ writer -----------------------------------------------------------
public 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));
} }
public 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) { private void offerJsonCharsWriter(final JsonCharsWriter writer) {
return configWrite(new JsonStreamWriter(tiny, charset, out)); if (writer != null) {
writer.recycle();
charsWriterPool.set(writer);
}
} }
public JsonWriter pollJsonWriter() { private void offerJsonBytesWriter(final JsonBytesWriter writer) {
return configWrite(writerPool.get().tiny(tiny)); if (writer != null) {
} writer.recycle();
bytesWriterPool.set(writer);
public void offerJsonWriter(final JsonWriter writer) { }
if (writer != null) writerPool.accept(writer);
} }
//------------------------------ convertFrom ----------------------------------------------------------- //------------------------------ convertFrom -----------------------------------------------------------
@@ -128,34 +131,56 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public <T> T convertFrom(final Type type, final char[] text, final int offset, final int length) { 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(in); 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;
} }
@@ -174,10 +199,10 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
//返回非null的值是由String、ArrayList、HashMap任意组合的对象 //返回非null的值是由String、ArrayList、HashMap任意组合的对象
public <V> V convertFrom(final char[] text, final int offset, final int length) { public <V> V convertFrom(final char[] text, final int offset, final int length) {
if (text == null) return null; if (text == null) return null;
final JsonReader in = readerPool.get(); //final JsonReader in = readerPool.get();
in.setText(text, offset, length); //in.setText(text, offset, length);
Object rs = new AnyDecoder(factory).convertFrom(in); Object rs = new AnyDecoder(factory).convertFrom(new JsonReader(text, offset, length));
readerPool.accept(in); //readerPool.accept(in);
return (V) rs; return (V) rs;
} }
@@ -216,46 +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";
final JsonWriter writer = pollJsonWriter(); JsonCharsWriter writer = pollJsonCharsWriter();
writer.specify(type); Encodeable encoder = this.lastConvertEncodeable;
factory.loadEncoder(type).convertTo(writer, value); if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
String result = writer.toString(); String result = writer.toString();
writerPool.accept(writer); offerJsonCharsWriter(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;
final JsonWriter writer = pollJsonWriter(); JsonBytesWriter writer = pollJsonBytesWriter();
writer.specify(type); Encodeable encoder = this.lastConvertEncodeable;
factory.loadEncoder(type).convertTo(writer, value); if (encoder == null || encoder.getType() != type) {
String result = writer.toString(); encoder = factory.loadEncoder(type);
writerPool.accept(writer); this.lastConvertEncodeable = encoder;
return result == null ? null : result.getBytes(StandardCharsets.UTF_8); }
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
byte[] result = writer.toBytes();
offerJsonBytesWriter(writer);
return result;
} }
@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);
final JsonWriter writer = pollJsonWriter(); }
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
String result = writer.toString(); @Override
writerPool.accept(writer); public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) {
return result; JsonBytesWriter writer = pollJsonBytesWriter();
if (type == null) {
writer.writeNull();
} else {
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
writer.completed(handler, offerBytesConsumer);
}
@Override
public void convertToBytes(final ByteArray array, final Object value) {
convertToBytes(array, value == null ? null : value.getClass(), value);
}
@Override
public void convertToBytes(final ByteArray array, final Type type, final Object value) {
JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, array));
if (type == null) {
writer.writeNull();
} else {
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
writer.directTo(array);
} }
public void convertTo(final OutputStream out, final Object value) { 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);
} }
@@ -264,41 +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 {
final JsonWriter writer = pollJsonWriter(); JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, out));
writer.specify(type); Encodeable encoder = this.lastConvertEncodeable;
factory.loadEncoder(type).convertTo(writer, value); if (encoder == null || encoder.getType() != type) {
byte[] bs = writer.toBytes(); encoder = factory.loadEncoder(type);
writerPool.accept(writer); this.lastConvertEncodeable = encoder;
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
pollJsonWriter(out).writeNull();
} else {
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
byte[] bs = writer.toBytes();
writerPool.accept(writer);
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
} }
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 {
@@ -310,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 {
@@ -320,18 +372,6 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return out.toBuffers(); return out.toBuffers();
} }
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier);
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBuffers();
}
public void convertTo(final JsonWriter writer, final Object value) { public void convertTo(final JsonWriter writer, final Object value) {
if (value == null) { if (value == null) {
writer.writeNull(); writer.writeNull();
@@ -350,30 +390,4 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
} }
} }
public void convertMapTo(final JsonWriter writer, final Object... values) {
if (values == null) {
writer.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
}
}
public JsonWriter convertToWriter(final Object value) {
if (value == null) return null;
return convertToWriter(value.getClass(), value);
}
public JsonWriter convertToWriter(final Type type, final Object value) {
if (type == null) return null;
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
return writer;
}
public JsonWriter convertMapToWriter(final Object... values) {
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
return writer;
}
} }

View File

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

View File

@@ -6,6 +6,7 @@
package org.redkale.convert.json; 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

@@ -25,10 +25,9 @@ public class JsonReader extends Reader {
private int limit; private int limit;
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 t) -> t.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

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

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
@@ -27,14 +29,16 @@ public class HttpMessageProcessor implements MessageProcessor {
protected final boolean finer; protected final boolean finer;
protected final boolean fine;
protected final Logger logger; protected final Logger logger;
protected final MessageProducers producer; protected HttpMessageClient messageClient;
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;
@@ -45,17 +49,27 @@ 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 final Runnable innerCallback = () -> { protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown(); if (cdl != null) cdl.countDown();
}; };
public HttpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeHttpServer server, Service service, HttpServlet servlet) { public HttpMessageProcessor(Logger logger, HttpMessageClient messageClient, MessageProducers producers, NodeHttpServer server, Service service, HttpServlet servlet) {
this.logger = logger; this.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.producer = producer; this.fine = logger.isLoggable(Level.FINE);
this.messageClient = messageClient;
this.producers = producers;
this.server = server; this.server = server;
this.service = service; this.service = service;
this.servlet = servlet; this.servlet = servlet;
@@ -63,44 +77,49 @@ 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) { public void begin(final int size, long starttime) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size); this.starttime = starttime;
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) {
HttpMessageRequest request = null; HttpMessageRequest request = null;
try { try {
long cha = System.currentTimeMillis() - message.createtime; long now = System.currentTimeMillis();
if (cha > 50 || finer) { long cha = now - message.createtime;
logger.log(Level.FINER, "HttpMessageProcessor.process (mq.delay = " + cha + " ms) message: " + message); long e = now - starttime;
} else if (finest) {
logger.log(Level.FINEST, "HttpMessageProcessor.process (mq.delay = " + cha + " ms) message: " + message);
}
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));
server.getHttpServer().getContext().execute(servlet, request, response);
long o = System.currentTimeMillis() - now;
if ((cha > 1000 || e > 100 || o > 1000) && fine) {
logger.log(Level.FINE, "HttpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message);
} else if ((cha > 50 || e > 10 || o > 50) && finer) {
logger.log(Level.FINER, "HttpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message);
} else if (finest) {
logger.log(Level.FINEST, "HttpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message);
} }
HttpMessageResponse response = new HttpMessageResponse(context, request, callback, null, null, producer.getProducer(message));
servlet.execute(request, response);
} catch (Throwable ex) { } 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, 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);
} }
@@ -112,13 +131,14 @@ public class HttpMessageProcessor implements MessageProcessor {
try { try {
this.cdl.await(30, TimeUnit.SECONDS); this.cdl.await(30, TimeUnit.SECONDS);
} catch (Exception ex) { } catch (Exception ex) {
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " commit error, restmodule=" + this.restmodule, ex);
} }
this.cdl = null; this.cdl = null;
} }
} }
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,12 +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 org.redkale.net.Response;
/** /**
* *
@@ -26,6 +28,8 @@ import org.redkale.util.ObjectPool;
*/ */
public class HttpMessageResponse extends HttpResponse { public class HttpMessageResponse extends HttpResponse {
protected final HttpMessageClient messageClient;
protected MessageRecord message; protected MessageRecord message;
protected MessageProducer producer; protected MessageProducer producer;
@@ -34,27 +38,40 @@ 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, 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.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.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, MessageProducer producer) { public HttpMessageRequest request() {
super(context, new HttpMessageRequest(context, message), null, config); return (HttpMessageRequest) request;
this.message = message;
this.callback = callback;
this.producer = producer;
} }
public void finishHttpResult(HttpResult result) { public void finishHttpResult(HttpResult result) {
finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), this.message, this.callback, this.producer, message.getResptopic(), result); finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), this.message, this.callback, this.messageClient, this.producer, message.getResptopic(), result);
} }
public static void finishHttpResult(boolean finest, Convert respConvert, MessageRecord msg, Runnable callback, MessageProducer producer, String resptopic, HttpResult result) { public static void finishHttpResult(boolean finest, Convert respConvert, MessageRecord msg, Runnable callback, MessageClient messageClient, MessageProducer producer, String resptopic, HttpResult result) {
if (callback != null) callback.run(); if (callback != null) callback.run();
if (resptopic == null || resptopic.isEmpty()) return; if (resptopic == null || resptopic.isEmpty()) return;
if (result.getResult() instanceof RetResult) { if (result.getResult() instanceof RetResult) {
@@ -66,10 +83,29 @@ public class HttpMessageResponse extends HttpResponse {
if (finest) { if (finest) {
Object innerrs = result.getResult(); Object innerrs = result.getResult();
if (innerrs instanceof byte[]) innerrs = new String((byte[]) innerrs, StandardCharsets.UTF_8); if (innerrs instanceof byte[]) innerrs = new String((byte[]) innerrs, StandardCharsets.UTF_8);
producer.logger.log(Level.FINEST, "HttpMessageProcessor.process seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders()); producer.logger.log(Level.FINEST, "HttpMessageResponse.finishHttpResult seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders());
} }
byte[] content = HttpResultCoder.getInstance().encode(result); byte[] content = HttpResultCoder.getInstance().encode(result);
producer.apply(new MessageRecord(msg.getSeqid(), resptopic, null, content)); producer.apply(messageClient.createMessageRecord(msg.getSeqid(), CTYPE_HTTP_RESULT, resptopic, null, content));
}
@Override
protected void prepare() {
super.prepare();
}
@Override
protected boolean recycle() {
Supplier<Response> respSupplier = this.responseSupplier;
Consumer<Response> respConsumer = this.responseConsumer;
boolean rs = super.recycle();
this.responseSupplier = respSupplier;
this.responseConsumer = respConsumer;
this.message = null;
this.producer = null;
this.callback = null;
this.finest = false;
return rs;
} }
@Override @Override
@@ -96,13 +132,17 @@ public class HttpMessageResponse extends HttpResponse {
} }
@Override @Override
public void finish(int status, String message) { public void finish(int status, String msg) {
if (finest) producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status); if (status > 400) {
producer.logger.log(Level.WARNING, "HttpMessageResponse.finish status: " + status + ", message: " + this.message);
} else if (finest) {
producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
}
if (this.message.isEmptyResptopic()) { if (this.message.isEmptyResptopic()) {
if (callback != null) callback.run(); if (callback != null) callback.run();
return; return;
} }
finishHttpResult(new HttpResult(message == null ? "" : message).status(status)); finishHttpResult(new HttpResult(msg == null ? "" : msg).status(status));
} }
@Override @Override
@@ -116,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

@@ -31,7 +31,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
@Override @Override
public byte[] encode(HttpSimpleRequest data) { public byte[] encode(HttpSimpleRequest data) {
byte[] requestURI = MessageCoder.getBytes(data.getRequestURI()); //long-string byte[] requestURI = MessageCoder.getBytes(data.getRequestURI()); //long-string
byte[] path = MessageCoder.getBytes(data.getRequestURI()); //short-string byte[] path = MessageCoder.getBytes(data.getPath()); //short-string
byte[] remoteAddr = MessageCoder.getBytes(data.getRemoteAddr());//short-string byte[] remoteAddr = MessageCoder.getBytes(data.getRemoteAddr());//short-string
byte[] sessionid = MessageCoder.getBytes(data.getSessionid());//short-string byte[] sessionid = MessageCoder.getBytes(data.getSessionid());//short-string
byte[] contentType = MessageCoder.getBytes(data.getContentType());//short-string byte[] contentType = MessageCoder.getBytes(data.getContentType());//short-string
@@ -40,6 +40,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
byte[] body = MessageCoder.getBytes(data.getBody()); 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

@@ -7,6 +7,7 @@ package org.redkale.mq;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -15,7 +16,7 @@ import static org.redkale.boot.Application.RESNAME_APP_NODEID;
import org.redkale.net.Servlet; import org.redkale.net.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.*;
/** /**
@@ -47,14 +48,14 @@ public abstract class MessageAgent {
protected final Object sncpProducerLock = new Object(); protected final Object sncpProducerLock = new Object();
protected final AtomicLong msgSeqno = new AtomicLong(System.nanoTime());
protected HttpMessageClient httpMessageClient; protected HttpMessageClient httpMessageClient;
protected SncpMessageClient sncpMessageClient; protected SncpMessageClient sncpMessageClient;
protected ScheduledThreadPoolExecutor timeoutExecutor; protected ScheduledThreadPoolExecutor timeoutExecutor;
protected ThreadHashExecutor workExecutor;
protected int producerCount = 1; protected int producerCount = 1;
//本地Service消息接收处理器 key:consumer //本地Service消息接收处理器 key:consumer
@@ -65,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<>();
@@ -105,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();
@@ -123,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;
} }
@@ -221,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, 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, 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

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

View File

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

View File

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

View File

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

View File

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

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
@@ -23,54 +23,74 @@ import org.redkale.util.ThreadHashExecutor;
*/ */
public class SncpMessageProcessor implements MessageProcessor { public class SncpMessageProcessor implements MessageProcessor {
protected final boolean finest;
protected final boolean finer;
protected final boolean fine;
protected final Logger logger; protected final Logger logger;
protected MessageClient messageClient;
protected final MessageProducers producer; protected final MessageProducers producer;
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;
protected CountDownLatch cdl; protected CountDownLatch cdl;
protected long starttime;
protected final Runnable innerCallback = () -> { protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown(); if (cdl != null) cdl.countDown();
}; };
public SncpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) { public SncpMessageProcessor(Logger logger, SncpMessageClient messageClient, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) {
this.logger = logger; this.logger = logger;
this.finest = logger.isLoggable(Level.FINEST);
this.finer = logger.isLoggable(Level.FINER);
this.fine = logger.isLoggable(Level.FINE);
this.messageClient = messageClient;
this.producer = producer; this.producer = producer;
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) { public void begin(final int size, long starttime) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size); this.starttime = starttime;
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) {
SncpMessageResponse response = null; SncpMessageResponse response = null;
try { try {
long now = System.currentTimeMillis();
long cha = now - message.createtime;
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, 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;
if ((cha > 1000 || e > 100 || o > 1000) && fine) {
logger.log(Level.FINE, "SncpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message);
} else if ((cha > 50 || e > 10 || o > 50) && finer) {
logger.log(Level.FINER, "SncpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message);
} else if (finest) {
logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message);
}
} catch (Throwable ex) { } catch (Throwable ex) {
if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null);
logger.log(Level.SEVERE, SncpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex); logger.log(Level.SEVERE, SncpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex);

View File

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

View File

@@ -5,12 +5,10 @@
*/ */
package org.redkale.mq; 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;
/** /**
* *
@@ -23,23 +21,27 @@ import org.redkale.util.ObjectPool;
*/ */
public class SncpMessageResponse extends SncpResponse { public class SncpMessageResponse extends SncpResponse {
protected MessageClient messageClient;
protected MessageRecord message; protected MessageRecord message;
protected MessageProducer producer; protected MessageProducer producer;
protected Runnable callback; protected Runnable callback;
public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, ObjectPool<Response> responsePool, 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.producer = producer; this.producer = producer;
} }
public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, ObjectPool<Response> responsePool, 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.producer = producer; this.producer = producer;
} }
@@ -47,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(new MessageRecord(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(new MessageRecord(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 TcpAioAsyncConnection 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 TcpAioAsyncConnection extends AsyncConnection {
private BlockingQueue<WriteEntry> writeQueue; private BlockingQueue<WriteEntry> writeQueue;
public TcpAioAsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, public AsyncAioTcpConnection(int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0, final 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 TcpAioAsyncConnection 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 TcpAioAsyncConnection 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 TcpAioAsyncConnection 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 TcpAioProtocolServer extends ProtocolServer { @Deprecated //@since 2.3.0
class AsyncAioTcpProtocolServer extends ProtocolServer {
//创建数
private final AtomicLong createCounter = new AtomicLong();
//关闭数
private final AtomicLong closedCounter = new AtomicLong();
//在线数
private final AtomicLong livingCounter = new AtomicLong();
private AsynchronousChannelGroup group; private AsynchronousChannelGroup group;
private AsynchronousServerSocketChannel serverChannel; private AsynchronousServerSocketChannel serverChannel;
public TcpAioProtocolServer(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,14 +89,13 @@ public class TcpAioProtocolServer 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);
AtomicLong createResponseCounter = new AtomicLong(); AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong(); AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); ObjectPool<Response> responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
final AsynchronousServerSocketChannel serchannel = this.serverChannel; final AsynchronousServerSocketChannel serchannel = this.serverChannel;
serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@@ -102,10 +118,9 @@ public class TcpAioProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
AsyncConnection conn = new TcpAioAsyncConnection(bufferPool, bufferPool, channel, AsyncConnection conn = new AsyncAioTcpConnection(server.bufferCapacity, bufferPool, bufferPool, channel,
context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter); context.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);
} }
@@ -119,6 +134,21 @@ public class TcpAioProtocolServer extends ProtocolServer {
}); });
} }
@Override
public long getCreateConnectionCount() {
return this.createCounter.get();
}
@Override
public long getClosedConnectionCount() {
return this.closedCounter.get();
}
@Override
public long getLivingConnectionCount() {
return this.livingCounter.get();
}
@Override @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, SSLContext sslContext, protected AsyncConnection(boolean client, final int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
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,6 +210,142 @@ public abstract class AsyncConnection implements AutoCloseable {
this.readBuffer = buffer; this.readBuffer = buffer;
} }
public boolean hasPipelineData() {
ByteBufferWriter writer = this.pipelineWriter;
return writer != null && writer.position() > 0;
}
public final void flushPipelineData(CompletionHandler<Integer, Void> handler) {
flushPipelineData(null, handler);
}
public <A> void flushPipelineData(A attachment, CompletionHandler<Integer, ? super A> handler) {
ByteBufferWriter writer = this.pipelineWriter;
this.pipelineWriter = null;
if (writer == null) {
handler.completed(0, attachment);
} else {
ByteBuffer[] srcs = writer.toBuffers();
CompletionHandler<Integer, ? super A> newhandler = new CompletionHandler<Integer, A>() {
@Override
public void completed(Integer result, A attachment) {
offerBuffer(srcs);
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, A attachment) {
offerBuffer(srcs);
handler.failed(exc, attachment);
}
};
if (srcs.length == 1) {
write(srcs[0], attachment, newhandler);
} else {
write(srcs, attachment, newhandler);
}
}
}
//返回 是否over
public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple array) {
return writePipelineData(pipelineIndex, pipelineCount, array.content(), array.offset(), array.length());
}
//返回 是否over
public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] bs, int offset, int length) {
synchronized (this) {
ByteBufferWriter writer = this.pipelineWriter;
if (writer == null) {
writer = ByteBufferWriter.create(getBufferSupplier());
this.pipelineWriter = writer;
}
if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) {
writer.put(bs, offset, length);
return (pipelineIndex == pipelineCount);
} else {
PipelineDataNode dataNode = this.pipelineDataNode;
if (dataNode == null) {
dataNode = new PipelineDataNode();
this.pipelineDataNode = dataNode;
}
if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值
dataNode.pipelineCount = pipelineCount;
}
dataNode.put(pipelineIndex, bs, offset, length);
if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) {
for (PipelineDataItem item : dataNode.arrayItems()) {
writer.put(item.data);
}
this.pipelineDataNode = null;
return true;
}
return false;
}
}
}
private static class PipelineDataNode {
public int pipelineCount;
public int itemsize;
private PipelineDataItem head;
private PipelineDataItem tail;
public PipelineDataItem[] arrayItems() {
PipelineDataItem[] items = new PipelineDataItem[itemsize];
PipelineDataItem item = head;
int i = 0;
while (item != null) {
items[i] = item;
item = item.next;
items[i].next = null;
i++;
}
Arrays.sort(items);
return items;
}
public void put(int pipelineIndex, byte[] bs, int offset, int length) {
if (tail == null) {
head = new PipelineDataItem(pipelineIndex, bs, offset, length);
tail = head;
} else {
PipelineDataItem item = new PipelineDataItem(pipelineIndex, bs, offset, length);
tail.next = item;
tail = item;
}
itemsize++;
}
}
private static class PipelineDataItem implements Comparable<PipelineDataItem> {
final byte[] data;
final int index;
public PipelineDataItem next;
public PipelineDataItem(int index, byte[] bs, int offset, int length) {
this.index = index;
this.data = Arrays.copyOfRange(bs, offset, offset + length);
}
@Override
public int compareTo(PipelineDataItem o) {
return this.index - o.index;
}
@Override
public String toString() {
return "{\"index\":" + index + "}";
}
}
public ByteBuffer pollReadBuffer() { public ByteBuffer pollReadBuffer() {
ByteBuffer rs = this.readBuffer; ByteBuffer rs = this.readBuffer;
if (rs != null) { if (rs != null) {
@@ -191,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 {
@@ -237,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 TcpAioAsyncConnection(bufferSupplier, bufferConsumer, channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds, null, null));
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
} catch (IOException e) {
future.completeExceptionally(e);
}
return future;
}
// public static AsyncConnection create(final Socket socket) {
// return create(socket, null, 0, 0);
// }
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, null, null);
// }
//
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0,
// final int writeTimeoutSecond0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, livingCounter, closedCounter);
// }
//
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
// }
//
// public static AsyncConnection create(final SocketChannel ch, final SocketAddress addr0, final Selector selector, final Context context) {
// return new TcpNioAsyncConnection(ch, addr0, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
// }
//
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
// final AtomicLong livingCounter, final AtomicLong closedCounter) {
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
// }
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch) {
return create(bufferPool, ch, null, 0, 0);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
} }

View File

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

View File

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

View File

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

View File

@@ -3,8 +3,9 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package org.redkale.net.nio; package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler; import java.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,8 +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 org.redkale.net.*; import java.util.function.*;
import org.redkale.net.nio.*; import org.redkale.boot.Application;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -24,23 +24,23 @@ import org.redkale.util.*;
* *
* @since 2.1.0 * @since 2.1.0
*/ */
public class TcpNioProtocolServer 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 TcpNioProtocolServer(Context context) { private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
public AsyncNioTcpProtocolServer(Context context) {
super(context); super(context);
} }
@@ -83,21 +83,38 @@ public class TcpNioProtocolServer 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();
this.responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); ObjectPool<Response> safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
this.responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool)); final int threads = Runtime.getRuntime().availableProcessors();
ThreadLocal<ObjectPool<Response>> localResponsePool = ThreadLocal.withInitial(() -> {
this.ioGroup = new NioThreadGroup(Runtime.getRuntime().availableProcessors(), context.executor, bufferPool); 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) {
@@ -128,9 +145,13 @@ public class TcpNioProtocolServer 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 TcpNioAsyncConnection(ioGroup, ioThread, context.executor, bufferPool, channel, context.getSSLContext(), null, livingCounter, closedCounter); ioGroup.connCreateCounter.incrementAndGet();
new PrepareRunner(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
@@ -143,4 +164,18 @@ public class TcpNioProtocolServer 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

@@ -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,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 TcpAioProtocolServer(context);
} else if ("aio".equalsIgnoreCase(netimpl)) {
return new TcpAioProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) {
return new TcpNioProtocolServer(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

@@ -9,7 +9,6 @@ import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import org.redkale.convert.bson.BsonConvert; import org.redkale.convert.bson.BsonConvert;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.util.ObjectPool;
/** /**
* 协议请求对象 * 协议请求对象
@@ -24,8 +23,6 @@ public abstract class Request<C extends Context> {
protected final C context; protected final C context;
protected final ObjectPool<ByteBuffer> bufferPool;
protected final BsonConvert bsonConvert; protected final BsonConvert bsonConvert;
protected final JsonConvert jsonConvert; protected final JsonConvert jsonConvert;
@@ -34,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;
@@ -48,48 +49,41 @@ public abstract class Request<C extends Context> {
protected final Map<String, Object> attributes = new HashMap<>(); protected final Map<String, Object> attributes = new HashMap<>();
protected Request(C context, ObjectPool<ByteBuffer> bufferPool) { protected Request(C context) {
this.context = context; this.context = context;
this.bufferPool = bufferPool;
this.bsonConvert = context.getBsonConvert(); this.bsonConvert = context.getBsonConvert();
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
} }
@@ -140,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;
/** /**
* 协议响应对象 * 协议响应对象
@@ -27,7 +27,9 @@ public abstract class Response<C extends Context, R extends Request<C>> {
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;
@@ -43,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
@@ -67,51 +75,33 @@ public abstract class Response<C extends Context, R extends Request<C>> {
}; };
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() { private final CompletionHandler finishBuffersHandler = new CompletionHandler<Integer, ByteBuffer[]>() {
@Override @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() {
@@ -123,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() {
@@ -130,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;
} }
@@ -205,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) {
@@ -279,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
@@ -301,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) -> {
Thread t = new WorkThread(workExecutor, r);
t.setName("Redkale-" + n + "-ServletThread-" + f.format(counter.incrementAndGet()));
return t;
});
} }
protected static int parseLenth(String value, int defValue) { 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);
@@ -368,17 +341,14 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//必须在 createContext()之后调用 //必须在 createContext()之后调用
protected abstract ObjectPool<Response> createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize); protected abstract ObjectPool<Response> createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize);
//必须在 createResponsePool()之后调用
protected abstract Creator<Response> createResponseCreator(ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool);
public void shutdown() throws IOException { 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");
@@ -449,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

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

View File

@@ -30,7 +30,7 @@ import org.redkale.util.*;
*/ */
public final class Transport { public 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 = new ObjectPool<>(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

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

View File

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

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