JSON输出和MessagePack输出的大小
您可能正在使用JSON传输数据(我们在消息队列中使用它)。虽然这是好的,但它的唯一好处是人类的可读性。如果您不关心可读性,您可能希望使用更高效的序列化机制。存在多个选项:Protobuf、MessagePack、Protody、java序列化。其中最容易使用的是java序列化,但它比其他解决方案效率低(内存和时间都是如此)。有一些基准可以帮助您选择最有效的解决方案,但是如果您想让它变得简单并且几乎可以替代您的JSON解决方案,MessagePack可能是最好的选择。
我做了一个简单的测试,比较JSON输出和MessagePack输出的大小:2300 vs150个字节,用于一个简单的消息。相当不错的减少,如果消息很多,就必须进行优化。
但是,您需要注册消息包中的所有类。有两种选择:
- 使用
@Message
在序列化图中的所有对象上。这有点乏味,特别是如果您已经转移了很多类。你得看完整张图 - 您可以手动向mesagpack注册所有类。同样繁琐,因为您还必须将消息类包含的所有类注册为字段(递归)。
这就是为什么我编写了下面的代码来循环所有消息类,并在启动时将它们注册到消息包中。它在一定程度上依赖于Spring类,但如果不使用Spring,则可以替换它们:
https://a.meipian.cn/3va13af2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
private MessagePack serializer = new MessagePack();
private ClassMapper classMapper = new DefaultClassMapper();
@PostConstruct
public void init() {
// we need to find all messages, and register their classes, and also all their fields' recursively
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider( false );
Set "com.foo.bar.messages" );
// hacking MessagePack to allow Set handling
Field fld = ReflectionUtils.findField(MessagePack. class , "registry" );
ReflectionUtils.makeAccessible(fld);
TemplateRegistry registry = (TemplateRegistry) ReflectionUtils.getField(fld, serializer);
registry.register(Set. class , new SetTemplate( new AnyTemplate(registry)));
registry.registerGeneric(Set. class , new GenericCollectionTemplate(registry, SetTemplate. class ));
try {
for (BeanDefinition def : classes) {
Class<?> clazz = Class.forName(def.getBeanClassName());
registerHierarcy(clazz, serializer, Sets.
}
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
private void registerHierarcy(Class<?> clazz, MessagePack serializer, Set
if (!isEligibleForRegistration(clazz)) {
return ;
}
Class<?> currentClass = clazz;
while (currentClass != null && !currentClass.isEnum() && currentClass != Object. class ) {
for (Field field : currentClass.getDeclaredFields()) {
registerHierarcy(field.getType(), serializer, handledClasses);
// type parameters
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
for (Type typeParam : ((ParameterizedType) type).getActualTypeArguments()) {
// avoid circular generics references, resulting in stackoverflow
Class<?> typeParamClass = (Class<?>) typeParam;
if (!handledClasses.contains(typeParamClass)) {
handledClasses.add(typeParamClass);
registerHierarcy(typeParamClass, serializer, handledClasses);
}
}
}
}
currentClass = currentClass.getSuperclass();
}
try {
serializer.register(clazz);
} catch (Exception ex) {
logger.warn( "Problem registering class " + clazz, ex.getMessage());
}
}
private boolean isEligibleForRegistration(Class<?> clazz) {
return !(clazz.isAnnotationPresent(Entity. class ) || clazz == Class. class || Type. class .isAssignableFrom(clazz) || clazz.isInterface() || clazz.isArray() || ClassUtils.isPrimitiveOrWrapper(clazz) || clazz == String. class || clazz == Date. class || clazz == Object. cla
|
https://m.douban.com/doulist/146409644/
每个项目都有很多工作要做。设置服务器机器及其集群,管理云实例,设置应用服务器、HAProxy、负载平衡器、数据库集群、消息队列、搜索引擎、DNS、警报等等。这就是为什么DevOps运动很受欢迎的原因--在应用程序之外还有更多的事情发生,这对它的成功至关重要。但是unix/linux是乏味的。老实说,我讨厌这样。Shell脚本很难看,我宁愿发明一种新的语言,并为它编写一个编译器,为它编写一个shell脚本。我知道许多“黑客”会对这句话气喘吁吁,但让我们面对它吧--它应该仅仅作为最后的手段来使用,因为它很可能会被排除在应用程序的存储库之外,它对开发人员不友好,而且它很难看(是的,你可以对它进行版本化,你可以用好的评论来写它,而且仍然是…)
但对于我讨厌shell脚本(以及命令行执行Perl脚本)来说,已经够了。这并不是唯一一件应该保持在最低限度的事情。(顺便说一句,这是“发牢骚”一段,你可以跳过它)。所有数据库、消息队列、搜索引擎、服务器等的“入门”指南都说“易于安装”。当然,您只需安装它,然后转到/usr/lib/foo/bar并更改一个配置文件,然后将权限授予一个新创建的运行它的用户,哦,然后您定制shell脚本来做一些事情,您就在那里了。噢,还有/usr/lib/foo/bar--这取决于您如何安装它,以及谁安装了它。我见过Tomcat至少有5种不同的安装方式。有一次,它的所有文件夹(bin、lib、conf、webapp、log、temp)都位于服务器上一个完全不同的位置。当然,有人决定使用内置连接池,因此必须在servlet容器本身中完成配置。使用默认值。把那个应用服务器放在那里,别碰它。但我们需要一个消息队列。除了MySQL之外,还有一个NoSQL数据库。我们的架构师说“不,这不应该在嵌入式模式下运行,它会耦合组件”。因此,可以很容易地在我们的主要应用程序虚拟机/进程中运行的一系列新的配置和安装。当你认为外部变量太多时,URL重写就来了。是的,这很容易,我们只需再加一条重写规则就可以了。6个月后,一些不走运的开发人员会浏览代码,想知道为什么这该死的代码没有打开。然后,他终于意识到它在应用程序之外,打开Apache配置文件,看到到处都是邪恶的标记。
概括上一段--在操作方面有太多的工作要做,而且(显然)不是编程。OPS人员应该对版本控制、配置甚至整个环境设置进行严格控制(Amazon的云提供了一个很好的选项,可以制作快照,然后将它们部署到新的实例上)。但是,当某些事情“不起作用”时,就会回到开发人员身上去发现代码中的问题。只是它不在那里。
https://greasyfork.org/cs/scripts?set=521390
这就是为什么我一直努力在应用程序本身中保留尽可能多的内容。NoSQL商店?请嵌入。消息队列?再来一次。URL重写-您的Web框架这样做。应用服务器配置?没有,如果可能的话,您可以在每个应用程序中执行它们。修改应用服务器启动脚本?不用了,谢谢。缓存?它是内存中的,你为什么需要一个单独的进程。所需的每个外部配置都位于应用程序外部的单个文件中,操作程序(或DEVS或devop)可以更改该配置。在/usr/appconf、Apache或其他地方找不到更多隐藏的石头。尽可能多地合并到您熟悉和经验丰富的代码中。
显然,并不是所有的东西都能在那里。有些数据库你不能运行嵌入式,或者你真的想要有单独的机器。您需要一个负载均衡器,它必须在应用程序前面。您需要在启动脚本中传递虚拟机/进程的初始化参数。但要坚持最低限度。如果您需要使应用程序透明,请使用一层代码,而不是使用脚本和配置。我不知道这是否符合devops的哲学,因为它更多的是“dev”,而不是“op”,但是它实际上允许开发人员执行OPS部分,因为它被保持在最低限度。它不涉及丑陋的脚本语言和两行长shell命令。
我知道我听起来像个大块头。我真的是但是,由于这些黑客中的大多数都可以放在应用程序中,而且更易于预测和易于阅读和维护--我更愿意保持这种状态。如果这是不可能的-让他们在它之外,但真正的版本,即使在同一个存储库与代码,并记录他们。
这一切的主要目的是提高可维护性和可管理性。您的代码周围有很多工具、基础结构和流程,所以尽可能多地使用它们。