perf[protocol]:增加支持protocul文件支持配置包名和有优化probuf文件读取

This commit is contained in:
meiwei committed 2023-08-24 12:39:32 +08:00
1 parent 7ba4948817
commit adba146780
28 files changed
+273 -219

No files matched your search

+7
View File
@@ -36,6 +36,7 @@
<netty.version>4.1.84.Final</netty.version>
<jprotobuf.version>2.4.17</jprotobuf.version>
<kryo.version>5.3.0</kryo.version>
<reflections.version>0.10.2</reflections.version>
<!-- zookeeper -->
<curator.version>5.3.0</curator.version>
@@ -156,6 +157,12 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${reflections.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -175,8 +175,8 @@ public abstract class GenerateProtocolFile {
// 生成Protobuf协议
if (generateLanguages.contains(CodeLanguage.Protobuf)) {
GenerateProtobufUtils.init(generateOperation);
GenerateProtobufUtils.createProtocolManager();
GenerateProtobufUtils.createProtocols();
GenerateProtobufUtils.createProtocolManager(allSortedGenerateProtocols);
GenerateProtobufUtils.createProtocols(allSortedGenerateProtocols);
}
// 预留参数,以后可能会用,比如给Lua修改一个后缀名称
@@ -23,6 +23,7 @@ import com.zfoo.protocol.generate.GenerateProtocolFile;
import com.zfoo.protocol.generate.GenerateProtocolNote;
import com.zfoo.protocol.generate.GenerateProtocolPath;
import com.zfoo.protocol.registration.anno.Compatible;
import com.zfoo.protocol.registration.anno.NotEnhance;
import com.zfoo.protocol.registration.anno.Protocol;
import com.zfoo.protocol.registration.field.*;
import com.zfoo.protocol.serializer.cpp.GenerateCppUtils;
@@ -41,6 +42,7 @@ import com.zfoo.protocol.util.StringUtils;
import com.zfoo.protocol.xml.XmlProtocols;
import javassist.CannotCompileException;
import javassist.NotFoundException;
import org.reflections.Reflections;
import java.io.IOException;
import java.lang.reflect.*;
@@ -165,6 +167,10 @@ public class ProtocolAnalysis {
}
public static synchronized void analyze(XmlProtocols xmlProtocols, GenerateOperation generateOperation) {
if (xmlProtocols.isPackages()) {
analyzePackage(xmlProtocols, generateOperation);
return;
}
AssertionUtils.notNull(subProtocolIdMap, "[{}]已经初始完成,请不要重复初始化", ProtocolManager.class.getSimpleName());
try {
var enhanceList = new ArrayList<IProtocolRegistration>();
@@ -207,7 +213,7 @@ public class ProtocolAnalysis {
var clazz = Class.forName(location);
var protocolId = ProtocolManager.protocolId(clazz);
var registration = parseProtocolRegistration(clazz, module);
if (protocolDefinition.isEnhance()) {
if (!clazz.isAnnotationPresent(NotEnhance.class)) {
enhanceList.add(registration);
}
// 注册协议
@@ -220,6 +226,60 @@ public class ProtocolAnalysis {
}
}
public static void analyzePackage(XmlProtocols xmlProtocols, GenerateOperation generateOperation) {
AssertionUtils.notNull(subProtocolIdMap, "[{}]已经初始完成,请不要重复初始化", ProtocolManager.class.getSimpleName());
try {
var packageList = new HashSet<String>();
for (var moduleDefinition : xmlProtocols.getModules()) {
var module = new ProtocolModule(moduleDefinition.getId(), moduleDefinition.getName());
AssertionUtils.isTrue(module.getId() > 0, "[module:{}] [id:{}] 模块必须大于等于1", module.getName(), module.getId());
AssertionUtils.isNull(modules[module.getId()], "duplicate [module:{}] [id:{}] Exception!", module.getName(), module.getId());
AssertionUtils.notNull(moduleDefinition.getProtocols(), "[module:{}] does not have any protocols", module.getName());
modules[module.getId()] = module;
for (var protocolDefinition : moduleDefinition.getProtocols()) {
packageList.add(protocolDefinition.getLocation());
}
}
// 获取所有IPack子类
var packetClazzList = scanClassList(packageList);
for (Class<?> clazz : packetClazzList) {
var protocolId = getProtocolIdAndCheckClass(clazz);
initProtocolClass(protocolId, clazz);
}
var enhanceList = new ArrayList<IProtocolRegistration>();
for (var moduleDefinition : xmlProtocols.getModules()) {
var module = modules[moduleDefinition.getId()];
for (Class<?> clazz : packetClazzList) {
var protocolId = ProtocolManager.protocolId(clazz);
if (protocolId < moduleDefinition.getMinId() || protocolId >= moduleDefinition.getMaxId()) {
continue;
}
var registration = parseProtocolRegistration(clazz, module);
if (!clazz.isAnnotationPresent(NotEnhance.class)) {
enhanceList.add(registration);
}
// 注册协议
protocols[protocolId] = registration;
}
}
enhance(generateOperation, enhanceList);
} catch (Exception e) {
throw new UnknownException(e);
}
}
public static Set<Class<? extends IPacket>> scanClassList(Set<String> packageList) {
//获取该路径下所有类
Reflections reflections = new Reflections(packageList);
//获取继承了IPacket的所有类
Set<Class<? extends IPacket>> classSet = reflections.getSubTypesOf(IPacket.class);
return classSet.stream().filter(k -> !k.isInterface()).collect(Collectors.toSet());
}
private static void enhance(GenerateOperation generateOperation, List<IProtocolRegistration> enhanceList) throws IOException, ClassNotFoundException, NotFoundException, CannotCompileException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
enhanceProtocolBefore(generateOperation);
enhanceProtocolRegistration(enhanceList);
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2020 The zfoo Authors
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*
*/
package com.zfoo.protocol.registration.anno;
import java.lang.annotation.*;
/**
* @author meiw
* @version 3.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface NotEnhance {
}
@@ -14,5 +14,5 @@ public @interface Protocol {
short id() default 0;
String note() default "";
}
@@ -21,22 +21,29 @@ public enum CodeLanguage {
/**
* Javassist字节码增强
*/
Enhance,
Enhance(1),
Cpp,
Cpp(2),
Go,
Go(4),
JavaScript,
JavaScript(8),
TypeScript,
TypeScript(16),
Lua,
Lua(32),
CSharp,
CSharp(64),
GdScript,
GdScript(128),
Protobuf
Protobuf(256)
;
public final int id;
private CodeLanguage(int id) {
this.id = id;
}
}
@@ -14,8 +14,10 @@
package com.zfoo.protocol.serializer.protobuf;
import com.baidu.bjf.remoting.protobuf.annotation.Protobuf;
import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass;
import com.zfoo.protocol.ProtocolManager;
import com.zfoo.protocol.collection.ArrayUtils;
import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.exception.RunException;
import com.zfoo.protocol.generate.GenerateOperation;
import com.zfoo.protocol.generate.GenerateProtocolNote;
@@ -32,10 +34,7 @@ import com.zfoo.protocol.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.*;
import java.util.stream.Collectors;
import static com.zfoo.protocol.util.FileUtils.LS;
@@ -49,11 +48,12 @@ public abstract class GenerateProtobufUtils {
private static String protocolOutputRootPath = "protos/";
private static String protocolManagerName = "protocols";
public static final String NET_COMMON_MODULE = "common";
private static XmlProtobuf xmlProtobuf = null;
private static String protocolParam = null;
public static String syntax() {
return StringUtils.format("syntax = {}{}{};", StringUtils.QUOTATION_MARK, xmlProtobuf.getSyntax(), StringUtils.QUOTATION_MARK);
return StringUtils.format("syntax = {}{}{};", StringUtils.QUOTATION_MARK, "proto3", StringUtils.QUOTATION_MARK);
}
public static String option(String optionKey, String optionValue) {
@@ -65,6 +65,9 @@ public abstract class GenerateProtobufUtils {
}
public static Map<String, String> parseParam(String param) {
if (StringUtils.isEmpty(param)) {
return Map.of();
}
var params = param.trim().split(StringUtils.SEMICOLON_REGEX);
if (ArrayUtils.isEmpty(params)) {
@@ -83,63 +86,22 @@ public abstract class GenerateProtobufUtils {
protocolOutputRootPath = FileUtils.joinPath(generateOperation.getProtocolPath(), protocolOutputRootPath);
var protocolParam = generateOperation.getProtocolParam();
if (StringUtils.isEmpty(protocolParam)) {
throw new RunException("生成protobuf协议的protocolParam参数不能为空");
}
var map = parseParam(protocolParam);
var protobufXmlPath = map.get("protobuf");
GenerateProtobufUtils.protocolParam = protocolParam;
FileUtils.deleteFile(new File(protocolOutputRootPath));
FileUtils.createDirectory(protocolOutputRootPath);
var inputStream = ClassUtils.getFileFromClassPath(protobufXmlPath);
var xmlProtobufObj = DomUtils.inputStream2Object(inputStream, XmlProtobuf.class);
if (!xmlProtobufObj.getSyntax().equals("proto3")) {
throw new RunException("生成protobuf协议只支持proto3");
}
var protoSet = new HashSet<String>();
for (var protos : xmlProtobufObj.getProtos()) {
if (protos.getName().equals(protocolManagerName)) {
throw new RunException("protobuf的协议文件名称不能用保留名称[{}]", protocolManagerName);
}
if (protoSet.contains(protos.getName())) {
throw new RunException("protobuf的协议文件名称重复定义[{}]", protos.getName());
}
protoSet.add(protos.getName());
}
xmlProtobuf = xmlProtobufObj;
}
public static void createProtocolManager() throws ClassNotFoundException {
var allGenerateProtocols = new HashSet<IProtocolRegistration>();
for (var protos : xmlProtobuf.getProtos()) {
for (var protocol : protos.getProtocols()) {
var protocolClass = Class.forName(protocol.getLocation());
var protocolId = ProtocolManager.protocolId(protocolClass);
var protocolRegistration = ProtocolManager.getProtocol(protocolId);
if (allGenerateProtocols.contains(protocolRegistration)) {
throw new RunException("protobuf的xml协议文件中重复定义了协议[{}]", protocolClass.getSimpleName());
}
allGenerateProtocols.add(protocolRegistration);
}
}
public static void createProtocolManager(List<IProtocolRegistration> allGenerateProtocols) {
var builder = new StringBuilder();
builder.append(syntax());
builder.append(LS).append(LS);
if (StringUtils.isNotEmpty(xmlProtobuf.getOption())) {
var optionMap = parseParam(xmlProtobuf.getOption());
var protocolParam = GenerateProtobufUtils.protocolParam;
var optionMap = parseParam(protocolParam);
if (CollectionUtils.isNotEmpty(optionMap)) {
for (var option : optionMap.entrySet()) {
builder.append(option(option.getKey(), option.getValue())).append(LS);
}
@@ -162,36 +124,37 @@ public abstract class GenerateProtobufUtils {
FileUtils.writeStringToFile(new File(protocolOutputPath), builder.toString(), true);
}
public static void createProtocols() throws ClassNotFoundException {
for (var protos : xmlProtobuf.getProtos()) {
public static void createProtocols(List<IProtocolRegistration> allGenerateProtocols) {
var packageMap = new HashMap<String, List<IProtocolRegistration>>();
for (var protocolRegistration : allGenerateProtocols) {
var clazz = protocolRegistration.protocolConstructor().getDeclaringClass();
String packageName = clazz.getPackageName();
var packagList = packageMap.computeIfAbsent(packageName, k -> new ArrayList<>());
packagList.add(protocolRegistration);
}
for (var protos : packageMap.entrySet()) {
var builder = new StringBuilder();
builder.append(syntax());
builder.append(LS).append(LS);
if (StringUtils.isNotEmpty(protos.getImportProto())) {
var params = protos.getImportProto().trim().split(StringUtils.SEMICOLON_REGEX);
for (var importProto : params) {
if (StringUtils.isBlank(importProto)) {
continue;
}
builder.append(importProto(importProto.trim())).append(LS);
}
builder.append(LS);
}
if (StringUtils.isNotEmpty(protos.getOption())) {
var optionMap = parseParam(protos.getOption());
var protocolParam = GenerateProtobufUtils.protocolParam;
var optionMap = parseParam(protocolParam);
if (CollectionUtils.isNotEmpty(optionMap)) {
for (var option : optionMap.entrySet()) {
builder.append(option(option.getKey(), option.getValue())).append(LS);
}
builder.append(LS);
}
builder.append(importProto(NET_COMMON_MODULE)).append(LS);
builder.append(LS);
for (var protocol : protos.getProtocols()) {
var protocolClass = Class.forName(protocol.getLocation());
var protocolId = ProtocolManager.protocolId(protocolClass);
var protocolRegistration = ProtocolManager.getProtocol(protocolId);
for (var protocolRegistration : protos.getValue()) {
var protocolClass = protocolRegistration.protocolConstructor().getDeclaringClass();
var protocolId = protocolRegistration.protocolId();
if (!isProtobufProtocol((ProtocolRegistration) protocolRegistration)) {
continue;
}
builder.append("// id = ").append(protocolId).append(LS);
@@ -205,11 +168,31 @@ public abstract class GenerateProtobufUtils {
builder.append("}").append(LS).append(LS);
}
var protocolOutputPath = StringUtils.format("{}/{}.proto", protocolOutputRootPath, protos.getName());
String protoName = StringUtils.substringAfterLast(protos.getKey(), StringUtils.PERIOD);
var protocolOutputPath = StringUtils.format("{}/{}.proto", protocolOutputRootPath, protoName);
FileUtils.writeStringToFile(new File(protocolOutputPath), builder.toString(), true);
}
}
/**
* 判断是否是protobuf协议
* @param registration
* @return
*/
private static boolean isProtobufProtocol(ProtocolRegistration registration) {
var protocolClass = registration.protocolConstructor().getDeclaringClass();
if (protocolClass.isAnnotationPresent(ProtobufClass.class)) {
return true;
}
var fields = registration.getFields();
for (var field : fields) {
if (!field.isAnnotationPresent(Protobuf.class)) {
return false;
}
}
return true;
}
/**
* 优化一下生成协议顺序
*
@@ -317,7 +300,7 @@ public abstract class GenerateProtobufUtils {
public static void clear() {
protocolOutputRootPath = null;
protocolManagerName = null;
xmlProtobuf = null;
protocolParam = null;
}
}
@@ -79,6 +79,8 @@ public abstract class StringUtils {
public static final String EMPTY_JSON = "{}";
public static final String SEMICOLON_COMMA = ";|,"; //分号或者逗号
public static final String MULTIPLE_HYPHENS = "-----------------------------------------------------------------------";
@@ -482,5 +484,22 @@ public abstract class StringUtils {
return Character.isWhitespace(ch) || STOP_WORD.contains(ch);
}
/**
* 判断是否是数字
* @param str
* @return
*/
public static boolean isNumeric(String str) {
if(str == null) {
return false;
}
int sz = str.length();
for(int i = 0; i < sz; i++) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
return true;
}
}
@@ -16,7 +16,7 @@ package com.zfoo.protocol.xml;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
@JsonPropertyOrder({"id", "location", "enhance"})
@JsonPropertyOrder({"id", "location"})
public class XmlProtocolDefinition {
@JacksonXmlProperty(isAttribute = true, localName = "id")
@@ -25,10 +25,6 @@ public class XmlProtocolDefinition {
@JacksonXmlProperty(isAttribute = true, localName = "location")
private String location;
@JacksonXmlProperty(isAttribute = true, localName = "enhance")
private boolean enhance = true;
public short getId() {
return id;
}
@@ -37,8 +33,4 @@ public class XmlProtocolDefinition {
return location;
}
public boolean isEnhance() {
return enhance;
}
}
@@ -20,13 +20,16 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.util.List;
@JsonPropertyOrder({"author", "modules"})
@JsonPropertyOrder({"author", "packages", "modules"})
@JacksonXmlRootElement(localName = "protocols")
public class XmlProtocols {
@JacksonXmlProperty(isAttribute = true, localName = "author")
private String author;
@JacksonXmlProperty(isAttribute = true, localName = "packages")
private boolean packages;
@JacksonXmlProperty(localName = "module")
@JacksonXmlElementWrapper(useWrapping = false)
private List<XmlModuleDefinition> modules;
@@ -35,6 +38,10 @@ public class XmlProtocols {
return author;
}
public boolean isPackages() {
return packages;
}
public List<XmlModuleDefinition> getModules() {
return modules;
}
@@ -76,7 +76,7 @@ public class JProtobufTest {
var op = GenerateOperation.NO_OPERATION;
op.getGenerateLanguages().add(CodeLanguage.Protobuf);
op.setFoldProtocol(true);
op.setProtocolParam("protobuf=protobuf.xml");
// op.setProtocolParam("protobuf=protobuf.xml");
ProtocolManager.initProtocol(Set.of(ObjectA.class, ObjectB.class, ObjectC.class), op);
}