首页> 标签> Dubbo
"Dubbo"
共 2940 条结果
全部 问答 文章 公开课 课程 电子书 技术圈 体验
巨坑系列:Java Bean 转 Map 的那些坑
一、背景有些业务场景下需要将 Java Bean 转成 Map 再使用。本以为很简单场景,但是坑很多。二、那些坑2.0 测试对象 import lombok.Data; import java.util.Date; @Data public class MockObject extends MockParent{ private Integer aInteger; private Long aLong; private Double aDouble; private Date aDate; } 父类 import lombok.Data; @Data public class MockParent { private Long parent; } 2.1 JSON 反序列化了类型丢失2.1.1 问题复现将 Java Bean 转 Map 最常见的手段就是使用 JSON 框架,如 fastjson 、 gson、jackson 等。但使用 JSON 将 Java Bean 转 Map 会导致部分数据类型丢失。如使用 fastjson ,当属性为 Long 类型但数字小于 Integer 最大值时,反序列成 Map 之后,将变为 Integer 类型。maven 依赖:<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.8</version> </dependency>示例代码:import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import java.util.Date; import java.util.Map; public class JsonDemo { public static void main(String[] args) { MockObject mockObject = new MockObject(); mockObject.setAInteger(1); mockObject.setALong(2L); mockObject.setADate(new Date()); mockObject.setADouble(3.4D); mockObject.setParent(3L); String json = JSON.toJSONString(mockObject); Map<String,Object> map = JSON.parseObject(json, new TypeReference<Map<String,Object>>(){}); System.out.println(map); } } 结果打印:{"parent":3,"ADouble":3.4,"ALong":2,"AInteger":1,"ADate":1657299916477}调试截图:通过 Java Visualizer 插件进行可视化查看:2.2.2 问题描述存在两个问题(1) 通过 fastjson 将 Java Bean 转为 Map ,类型会发生转变。如 Long 变成 Integer ,Date 变成 Long, Double 变成 Decimal 类型等。(2)在某些场景下,Map 的 key 并非和属性名完全对应,像是通过 get set 方法“推断”出来的属性名。2.2 BeanMap 转换属性名错误2.2.1 commons-beanutils 的 BeanMapmaven 版本:<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> 代码示例:import org.apache.commons.beanutils.BeanMap; import third.fastjson.MockObject; import java.util.Date; public class BeanUtilsDemo { public static void main(String[] args) { MockObject mockObject = new MockObject(); mockObject.setAInteger(1); mockObject.setALong(2L); mockObject.setADate(new Date()); mockObject.setADouble(3.4D); mockObject.setParent(3L); BeanMap beanMap = new BeanMap(mockObject); System.out.println(beanMap); } }调试截图:存在和 cglib 一样的问题,虽然类型没问题但是属性名还是不对。原因分析: /** * Constructs a new <code>BeanMap</code> that operates on the * specified bean. If the given bean is <code>null</code>, then * this map will be empty. * * @param bean the bean for this map to operate on */ public BeanMap(final Object bean) { this.bean = bean; initialise(); } 关键代码: private void initialise() { if(getBean() == null) { return; } final Class<? extends Object> beanClass = getBean().getClass(); try { //BeanInfo beanInfo = Introspector.getBeanInfo( bean, null ); final BeanInfo beanInfo = Introspector.getBeanInfo( beanClass ); final PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); if ( propertyDescriptors != null ) { for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) { if ( propertyDescriptor != null ) { final String name = propertyDescriptor.getName(); final Method readMethod = propertyDescriptor.getReadMethod(); final Method writeMethod = propertyDescriptor.getWriteMethod(); final Class<? extends Object> aType = propertyDescriptor.getPropertyType(); if ( readMethod != null ) { readMethods.put( name, readMethod ); } if ( writeMethod != null ) { writeMethods.put( name, writeMethod ); } types.put( name, aType ); } } } } catch ( final IntrospectionException e ) { logWarn( e ); } }调试一下就会发现,问题出在 BeanInfo 里面 PropertyDescriptor 的 name 不正确。经过分析会发现java.beans.Introspector#getTargetPropertyInfo 方法是字段解析的关键对于无参的以 get 开头的方法名从 index =3 处截取,如 getALong 截取后为 ALong, 如 getADouble 截取后为 ADouble。然后去构造 PropertyDescriptor:/** * Creates <code>PropertyDescriptor</code> for the specified bean * with the specified name and methods to read/write the property value. * * @param bean the type of the target bean * @param base the base name of the property (the rest of the method name) * @param read the method used for reading the property value * @param write the method used for writing the property value * @exception IntrospectionException if an exception occurs during introspection * * @since 1.7 */ PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException { if (bean == null) { throw new IntrospectionException("Target Bean class is null"); } setClass0(bean); setName(Introspector.decapitalize(base)); setReadMethod(read); setWriteMethod(write); this.baseName = base; }底层使用 java.beans.Introspector#decapitalize 进行解析: /** * Utility method to take a string and convert it to normal Java variable * name capitalization. This normally means converting the first * character from upper case to lower case, but in the (unusual) special * case when there is more than one character and both the first and * second characters are upper case, we leave it alone. * <p> * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays * as "URL". * * @param name The string to be decapitalized. * @return The decapitalized version of the string. */ public static String decapitalize(String name) { if (name == null || name.length() == 0) { return name; } if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){ return name; } char chars[] = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); }从代码中我们可以看出(1) 当 name 的长度 > 1,且第一个字符和第二个字符都大写时,直接返回参数作为PropertyDescriptor name。(2) 否则将 name 转为首字母小写这种处理本意是为了不让属性为类似 URL 这种缩略词转为 uRL ,结果“误伤”了我们这种场景。2.2.2 使用 cglib 的 BeanMapcglib 依赖<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.12</version> </dependency>代码示例:import net.sf.cglib.beans.BeanMap; import third.fastjson.MockObject; import java.util.Date; public class BeanMapDemo { public static void main(String[] args) { MockObject mockObject = new MockObject(); mockObject.setAInteger(1); mockObject.setALong(2L); mockObject.setADate(new Date()); mockObject.setADouble(3.4D); mockObject.setParent(3L); BeanMap beanMapp = BeanMap.create(mockObject); System.out.println(beanMapp); } } 结果展示:我们发现类型对了,但是属性名依然不对。关键代码:net.sf.cglib.core.ReflectUtils#getBeanGetters底层也会用到 java.beans.Introspector#decapitalize 所以属性名存在一样的问题就不足为奇了。三、解决办法3.1 解决方案解决方案有很多,本文提供一个基于 dubbo的解决方案。maven 依赖:<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>3.0.9</version> </dependency> 示例代码:import org.apache.dubbo.common.utils.PojoUtils; import third.fastjson.MockObject; import java.util.Date; public class DubboPojoDemo { public static void main(String[] args) { MockObject mockObject = new MockObject(); mockObject.setAInteger(1); mockObject.setALong(2L); mockObject.setADate(new Date()); mockObject.setADouble(3.4D); mockObject.setParent(3L); Object generalize = PojoUtils.generalize(mockObject); System.out.println(generalize); } }调试效果:Java Visualizer 效果:3.2 原理解析大家可以下载源码来简单研究下。https://github.com/apache/dubbo核心代码:org.apache.dubbo.common.utils.PojoUtils#generalize(java.lang.Object)public static Object generalize(Object pojo) { eturn generalize(pojo, new IdentityHashMap()); } 关键代码:// pojo 待转换的对象 // history 缓存 Map,提高性能 private static Object generalize(Object pojo, Map<Object, Object> history) { if (pojo == null) { return null; } // 枚举直接返回枚举名 if (pojo instanceof Enum<?>) { return ((Enum<?>) pojo).name(); } // 枚举数组,返回枚举名数组 if (pojo.getClass().isArray() && Enum.class.isAssignableFrom(pojo.getClass().getComponentType())) { int len = Array.getLength(pojo); String[] values = new String[len]; for (int i = 0; i < len; i++) { values[i] = ((Enum<?>) Array.get(pojo, i)).name(); } return values; } // 基本类型返回 pojo 自身 if (ReflectUtils.isPrimitives(pojo.getClass())) { return pojo; } // Class 返回 name if (pojo instanceof Class) { return ((Class) pojo).getName(); } Object o = history.get(pojo); if (o != null) { return o; } history.put(pojo, pojo); // 数组类型,递归 if (pojo.getClass().isArray()) { int len = Array.getLength(pojo); Object[] dest = new Object[len]; history.put(pojo, dest); for (int i = 0; i < len; i++) { Object obj = Array.get(pojo, i); dest[i] = generalize(obj, history); } return dest; } // 集合类型递归 if (pojo instanceof Collection<?>) { Collection<Object> src = (Collection<Object>) pojo; int len = src.size(); Collection<Object> dest = (pojo instanceof List<?>) ? new ArrayList<Object>(len) : new HashSet<Object>(len); history.put(pojo, dest); for (Object obj : src) { dest.add(generalize(obj, history)); } return dest; } // Map 类型,直接 对 key 和 value 处理 if (pojo instanceof Map<?, ?>) { Map<Object, Object> src = (Map<Object, Object>) pojo; Map<Object, Object> dest = createMap(src); history.put(pojo, dest); for (Map.Entry<Object, Object> obj : src.entrySet()) { dest.put(generalize(obj.getKey(), history), generalize(obj.getValue(), history)); } return dest; } Map<String, Object> map = new HashMap<String, Object>(); history.put(pojo, map); // 开启生成 class 则写入 pojo 的class if (GENERIC_WITH_CLZ) { map.put("class", pojo.getClass().getName()); } // 处理 get 方法 for (Method method : pojo.getClass().getMethods()) { if (ReflectUtils.isBeanPropertyReadMethod(method)) { ReflectUtils.makeAccessible(method); try { map.put(ReflectUtils.getPropertyNameFromBeanReadMethod(method), generalize(method.invoke(pojo), history)); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } // 处理公有属性 for (Field field : pojo.getClass().getFields()) { if (ReflectUtils.isPublicInstanceField(field)) { try { Object fieldValue = field.get(pojo); // 对象已经解析过,直接从缓存里读提高性能 if (history.containsKey(pojo)) { Object pojoGeneralizedValue = history.get(pojo); // 已经解析过该属性则跳过(如公有属性,且有 get 方法的情况) if (pojoGeneralizedValue instanceof Map && ((Map) pojoGeneralizedValue).containsKey(field.getName())) { continue; } } if (fieldValue != null) { map.put(field.getName(), generalize(fieldValue, history)); } } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } return map; }关键截图org.apache.dubbo.common.utils.ReflectUtils#getPropertyNameFromBeanReadMethodpublic static String getPropertyNameFromBeanReadMethod(Method method) { if (isBeanPropertyReadMethod(method)) { // get 方法,则从 index =3 的字符小写 + 后面的字符串 if (method.getName().startsWith("get")) { return method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4); } // is 开头方法, index =2 的字符小写 + 后面的字符串 if (method.getName().startsWith("is")) { return method.getName().substring(2, 3).toLowerCase() + method.getName().substring(3); } } return null; }因此, getALong 方法对应的属性名被解析为 aLong。同时,这么处理也会存在问题。如当属性名叫 URL 时,转为 Map 后 key 就会被解析成 uRL。从这里看出,当属性名比较特殊时也很容易出问题,但 dubbo 这个工具类更符合我们的预期。更多细节,大家可以根据 DEMO 自行调试学习。如果想严格和属性保持一致,可以使用反射获取属性名和属性值,加缓存机制提升解析的效率。四、总结Java Bean 转 Map 的坑很多,最常见的就是类型丢失和属性名解析错误的问题。大家在使用 JSON 框架和 Java Bean 转 Map 的框架时要特别小心。平时使用某些框架时,多写一些 DEMO 进行验证,多读源码,多调试,少趟坑。
文章
JSON  ·  缓存  ·  Dubbo  ·  数据可视化  ·  Java  ·  fastjson  ·  应用服务中间件  ·  Apache  ·  Maven  ·  数据格式
2022-07-09
Spring Cloud架构的各个组件的原理分析
Spring Cloud架构的各个组件的原理分析我们先认识一下SpringCloud的各个组件,然后知其所以然。原理讲解前,先看一个最经典的业务场景,如开发一个电商网站,要实现支付订单的功能,流程如下:创建一个订单之后,如果用户立刻支付了这个订单,我们需要将订单状态更新为“已支付”扣减相应的商品库存通知仓储中心,进行发货给用户的这次购物增加相应的积分如上,微服务的应用场景和核心竞争力:降低耦合:每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开发效率。独立部署:由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。选型灵活:微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。由于每个微服务相对简单,故需要对技术栈进行升级时所面临的风险就较低,甚至完全重构一个微服务也是可行的。容错机制:当某一组建发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。灵活扩展:单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。Dubbo对标Spring Cloud微服务:背景分析:Dubbo,是阿里巴巴服务化治理的核心框架,并被广泛应用于中国各互联网公司;Spring Cloud是知名的Spring家族的产品。阿里巴巴是一个商业公司,虽然也开源了很多的顶级的项目,但从整体战略上来讲,仍然是服务于自身的业务为主。Spring专注于企业级开源框架的研发,不论是在中国还是在世界上使用都非常广泛,开发出通用、开源、稳健的开源框架就是他们的主业。活跃度对比:Dubbo是一个非常优秀的服务治理框架,并且在服务治理、灰度发布、流量分发这方面做的比Spring Cloud还好,除过当当网在基础上增加了rest支持外,已有两年多的时间几乎都没有任何更新了。在使用过程中出现问题,提交到GitHub的Issue也少有回复。相反Spring Cloud自从发展到现在,仍然在不断的高速发展,从GitHub上提交代码的频度和发布版本的时间间隔就可以看出,现在Spring Cloud即将发布2.0版本,到了后期会更加完善和稳定。平台架构:Dubbo框架只是专注于服务之间的治理,如果我们需要使用配置中心、分布式跟踪这些内容都需要自己去集成,这样无形中使用Dubbo的难度就会增加。Spring Cloud几乎考虑了服务治理的方方面面,更有Spring Boot这个大将的支持,开发起来非常的便利和简单。技术前景:Dubbo在各中小公司也从中受益不少。经过了这么多年的发展,互联网行业也是涌现了更多先进的技术和理念,Dubbo有点可惜。Spring 推出Spring Boot/Cloud也是因为自身的很多原因。Spring最初推崇的轻量级框架,随着不断的发展也越来越庞大,随着集成项目越来越多,配置文件也越来越混乱,慢慢的背离最初的理念。随着这么多年的发展,微服务、分布式链路跟踪等更多新的技术理念的出现,Spring急需一款框架来改善以前的开发模式,因此才会出现Spring Boot/Cloud项目,我们现在访问Spring官网,会发现Spring Boot和Spring Cloud已经放到首页最重点突出的三个项目中的前两个,可见Spring对这两个框架的重视程度。Dubbo实现如下:Spring Cloud实现思路:Eureka原理:主管服务注册与发现,也就是微服务的名称注册到Eureka,就可以通过Eureka找到微服务,而不需要修改服务调用的配置文件。分析:Spring Cloud封装了Netflix公司开发的Eureka模块来实现服务的注册与发现,采用的c-s的设计架构,Eureka Server作为服务注册功能的服务器,他是服务注册中心。而系统的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳。这样系统的维护人员可以通过Eureka Server来监控系统中的各个微服务是否正常运行。Spring Cloud的一些其他模块(比如Zuul)就可以通过Eureka Server来发现系统其他的微服务,并执行相关逻辑。Eureka ServerEureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册, 这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。Eureka ClientEureka Client是一个Java客户端, 用于简化Eureka Server的交互,客户端同时也具备一个内置的、 使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒),以证明当前服务是可用状态。如果Eureka Server在一定的时间(默认90秒)未收到客户端的心跳,Eureka Server将会从服务注册表中把这个服务节点移除。Eureka Server的自我保护机制如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)当网络稳定时,当前实例新的注册信息会被同步到其它节点中因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper那样使整个注册服务瘫痪。Eureka和ZooKeeper著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。ZooKeeper保证CP当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是ZooKeeper会出现这样一种情况,当Master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s,且选举期间整个ZooKeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得ZooKeeper集群失去Master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。Eureka保证APEurek在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,见上。总结Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper那样使整个注册服务瘫痪。Eureka作为单纯的服务注册中心来说要比ZooKeeper更加“专业”,因为注册服务更重要的是可用性,我们可以接受短期内达不到一致性的状况。Ribbon和Feign在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于HTTP RESTful的。Spring Cloud有两种服务调用方式,一种是Ribbon+RestTemplate,另一种是Feign。概念基于Netflix Ribbon用过轮询策略实现的一套客户端负载均衡的工具。客户端负载均衡:负载均衡Zuul网关将一个请求发送给某一个服务的应用的时候,如果一个服务启动了多个实例,就会通过Ribbon来通过一定的负载均衡策略来发送给某一一个服务实例。Spring Cloud中的Ribbon,客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法(如简单轮询,随机连接等)选择一个服务器,然后进行访问。负载均衡负载均衡:用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。使用负载均衡带来的好处很明显:当集群里的1台或者多台服务器down的时候,剩余的没有down的服务器可以保证服务的继续使用;将访问压力分配到各个服务器,不会由于某一高峰时刻导致系统cpu急剧上升。负载均衡有好几种实现策略,常见的有:随机(Random),轮询(RoundRobin),一致性哈希(ConsistentHash),哈希(Hash),加权(Weighted)Ribbon的默认策略是轮询RestTemplate传统情况下在Java代码里访问RESTful服务,一般使用Apache的HttpClient,不过此种方法使用起来太过繁琐。Spring提供了一种简单便捷的模板类来进行操作,这就是RestTemplate。Feign是一个声明式http客户端。使用Feign能让编写http客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,避免了调用目标微服务时,需要不断的解析/封装json数据的繁琐。Spring Cloud中Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。Ribbon和Feign的区别Feign目标 使 编写Java Http客户端变得更容易在使用Ribbon+ RestTemplate时,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。利用RestTemplate对http请求的封装处理,形成了-套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可), 即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。Feign集成了RibbonRibbon通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,Feign是一个声明式的Web服务客户端, 使得编写Web服务客户端变得非常容易,只需要创建一个接口, 然后在上面添加注解,像调用本地方法一样调用它就可以,而感觉不到是调用远程方法。SpringCloud中Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。Ribbon和Nginx的区别服务器端负载均衡NginxNginx是客户端所有请求统一交给Nginx,由Nginx进行实现负载均衡请求转发,属于服务器端负载均衡。既请求由Nginx服务器端进行转发。客户端负载均衡Ribbon,Ribbon是从Eureka注册中心服务器端上获取服务注册信息列表,缓存到本地,然后在本地实现轮询负载均衡策略。既在客户端实现负载均衡。应用场景的区别Nginx适合于服务器端实现负载均衡,比如:Tomcat,Ribbon适合与在微服务中RPC远程调用实现本地服务负载均衡,比如:Dubbo、Spring Cloud中都是采用本地负载均衡。Zuul应用场景假如当前有十几个微服务服务,订单,商品,用户等等,显然是客户端不需要和每个服务逐一打交道,这就需要有一个统一入口,它就是服务网关。API网关所有的客户端请求通过这个网关访问后台的服务。他可以使用一定的路由配置来判断某一个URL由哪个服务来处理。并从Eureka获取注册的服务来转发请求。核心功能Zuul包含了对请求的路由和过滤两个最主要的功能,是各种服务的统一入口,同时还会用来提供监控、授权、安全、调度等等。路由功能:负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。过滤器功能:则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。Zuul和Eureka进行整合:将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。注意:Zuul服务最终还是会注册进Eureka,提供代理+路由+过滤三大功能。核心原理Zuul的核心是一系列的filters,其作用可以类比Servlet框架的Filter,或者AOP。过滤器之间没有直接进行通信,而是通过Request Context(上下文)进行数据传递。Zuul的过滤器是由Groovy写成,这些过滤器文件被放在Zuul Server上的特定目录下面,Zuul会定期轮询这些目录,修改过的过滤器会动态的加载到Zuul Server中以便过滤请求使用。Zuul负载均衡:Zuul拦截对应的API前缀请求做转发,转发到对应的serverId上,在Eureka服务上同一个serverId可以对应多个服务,也就是说用同一个服务节点不同的端口注册两个实例,但是serverId是一样Zuul做转发的时候会结合eureka-server起到负载均衡的效果。过滤器的种类:PRE(前置):这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现鉴权、限流、参数校验调整等。ROUTING(路由):这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。POST(后置):这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端、日志等。ERROR(错误):在其他阶段发生错误时执行该过滤器。Zuul和NginxZuul虽然在性能上和Nginx没法比,但它也有它的优点。Zuul提供了认证鉴权,动态路由,监控,弹性,安全,负载均衡等边缘服务,在团队规模不大的情况下,没有专门负责路由开发时,使用Zuul当网关是一个快速上手的好方案。Nginx和Zuul是可以配合使用的,发挥各自的优点,使用Nginx作为负载均衡实现高并发的请求转发,Zuul用作网关。Zuul和Ribbon实现负载均衡Zuul支持Ribbon和Hystrix,也能够实现客户端的负载均衡。我们的Feign不也是实现客户端的负载均衡和Hystrix的吗?既然Zuul已经能够实现了,那我们的Feign还有必要吗?可以这样理解:Zuul是对外暴露的唯一接口相当于路由的是controller的请求,而Ribbonhe和Fegin路由了service的请求。Zuul做最外层请求的负载均衡,而Ribbon和Fegin做的是系统内部各个微服务的service的调用的负载均衡。Hystrix介绍Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避兔的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。Hystrix的出现就是为了解决雪崩效应。服务雪崩多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的”雪崩效应”。服务熔断熔断机制是应对雪崩效应的一种微服务链路保护机制。当删除链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。服务降级整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。Hystrix监控和断路器我们只需要在服务接口上添加Hystrix标签,就可以实现对这个接口的监控和断路器功能。Hystrix Dashboard监控面板,提供了一个界面,可以监控各个服务上的服务调用所消耗的时间等。Hystrix Turbine监控聚合使用Hystrix监控,我们需要打开每一个服务实例的监控信息来查看。而Turbine可以帮助我们把所有的服务实例的监控信息聚合到一个地方统查看。这样就不需要挨个打开一个个的页面一个个查看。Zuul的安全机制签名机制,为防止接口数据篡改和重复调用,增加接口参数校验机制,sig签名算法为MD5(appKey+appSecret+timestamp),appKey是分配给客户端的ID,appSecret是分配给客户端的密钥,timestamp为unix时间戳,请求的URL有效时间为15分钟。Token机制,用户在登录之后会返回一个access_ token,客户端在访问需要登录之后才能访问的资源,需要在在Authorization头部使用Bearer模式新增token,如head(“Authorization”,” Bearer token”)。Hystrix的设计原则资源隔离(线程池隔离和信号量隔离)机制:限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其它服务调用。限流机制:限流机制主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。熔断机制:当失败率达到阀值自动触发降级(如因网络故障、超时造成的失败率真高),熔断器触发的快速失败会进行快速恢复。降级机制:超时降级、资源不足时(线程或信号量)降级、运行异常降级等,降级后可以配合降级接口返回托底数据。缓存支持:提供了请求缓存、请求合并实现。通过近实时的统计/监控/报警功能,来提高故障发现的速度。通过近实时的属性和配置热修改功能,来提高故障处理和恢复的速度。Config介绍Spring Cloud Config是一个解决分布式系统的配置管理方案。微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统 中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、 动态的配置管理设施是必不可少的。Spring Cloud提供了ConfigServer来解决这个问题,我们每一个微服务自 己带着一个application.yml 上百个配置文件的管理。应用场景不方便维护,多人同时对配置文件进行修改,冲突不断,很难维护配置内容安全和权限,主要是针对线上的配置来说,一般不对开发公开,只有运维有权限所以需要将配置文件隔离,不放到项目代码里更新配置项目需要重启,每次更新配置文件都需要重启项目,很耗时。使用了配置中心后,即可实现配置实时更新congfig Server和Config Client结合Spring Cloud Bus实现配置自动刷新。原理配置文件存储在远端Git(比如GitHub,Gitee等仓库),config-server从远端Git拉取配置文件,并保存到本地Git。本地Git和config-server的交互是双向的,因为当远端Git无法访问时,会从本地Git获取配置文件。config-client(即各个微服务),从config-server拉取配置文件。角色Config Server:提供配置文件的存储、以接口的形式将配置文件的内容提供出去。Config Client:通过接口获取数据、并依据此数据初始化自己的应用。总结如下:安全和权限,主要是针对线上的配置来说,一般不对开发公开,只有运维有权限所以需要将配置文件隔离,不放到项目代码里更新配置项目需要重启,每次更新配置文件都需要重启项目,很耗时。使用了配置中心后,即可实现配置实时更新congfig Server和Config Client结合Spring Cloud Bus实现配置自动刷新。原理配置文件存储在远端Git(比如GitHub,Gitee等仓库),config-server从远端Git拉取配置文件,并保存到本地Git。本地Git和config-server的交互是双向的,因为当远端Git无法访问时,会从本地Git获取配置文件。config-client(即各个微服务),从config-server拉取配置文件。角色Config Server:提供配置文件的存储、以接口的形式将配置文件的内容提供出去。Config Client:通过接口获取数据、并依据此数据初始化自己的应用。总结如下:总结如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、收藏,您的支持是我坚持写作最大的动力。
文章
负载均衡  ·  监控  ·  Dubbo  ·  Java  ·  应用服务中间件  ·  开发工具  ·  nginx  ·  git  ·  微服务  ·  Spring
2022-07-08
饿了么 Dubbo 实践分享
饿了么 Dubbo 实践分享——刘军阿里云-云原生应用平台,Apache Dubbo PMC 饿了么从 2021年 11 月启动 Dubbo3 升级工作。在此之前,饿了么使用 HSF2 作为服务框架。升级过程历史半年,目前已基本完成,共有将近 2000 个应用和 10 万个节点运行在 Dubbo3 之上。一、Dubbo 3 简介Dubbo3 是下一代云原生微服务框架。设计原则上, Dubbo3 面向云原生、支持百万集群实例的可伸缩,强调柔性和智能化流量的调度;策略上,Dubbo3 完全开源;业务价值上,Dubbo3 更强调降低单机资源消耗,提升全链路资源利用率以及云原生时代的服务治理能力。 Dubbo3 从 Dubbo2.0 架构演进而来,继承了开源 Dubbo2 以及阿里内部演进多年的 HSF2 的完整特性和优点,并且保持对 Dubbo2 和 HSF2 完全兼容。因此,老用户几乎实现 Dubbo3 的 0 成本迁移。Dubbo3 提供的核心特性主要有以下四个:① 全新服务发现模型。② 提供了基于 HTTP /2 的 Triple 协议。③ 统一流量治理模型,支持更丰富的流量治理场景。④ Service Mesh模型:提供了 Sidecar Mesh 和 ProxyLess Mesh 两种部署架构。Dubbo3 基于 Dubbo2 和 HSF2 两款产品诞生,同时以云原生架构作为指导思想,进行了大量重构,并规划了一系列功能特性。在开源 Dubbo3 产品之上,还有基于 Dubbo3 的企业用户实践、生态产品以及公有云厂商云产品,有大家比较关心的 Dubbo3 典型用户阿里巴巴。在此之前,阿里巴巴一直运行在自研 HSF2 框架之上。虽然 HSF2 和 Dubbo2 有很多相似之处,但如今已经演进成为两个不同框架。实现了 Dubbo3 对 HSF2 和 Dubbo2 的融合后,阿里巴巴完全迁移到Dubbo3。开源的 Dubbo3,如何满足阿里特有的诉求?答案是:通过 Dubbo3 开源的标准 SPI 扩展。因此阿里内部现在还有一套 HSF 3,与以往 HSF 2 完全不同,已经是两个不同的框架。HSF3 是基于标准的 Dubbo3 SPI 生成的扩展库,但也仅仅是一个扩展库,比如注册中心的扩展、路由组件的扩展或者监控组件的扩展,而其他配置的组装服务暴露、服务发现、地址解析等核心流程已经完全跑在 Dubbo3 之上。在此模式之下,阿里巴巴内部的实现诉求将完全体现在开源的 Dubbo3 之上。阿里巴巴内部开发人员同时工作在 Dubbo3 以及内部扩展SPI 库 HSF3 之上。通过 SPI 扩展的模式同样适用于公有云产品或其他厂商实践。升级到 Dubbo3 后,能够获得以下三方面的收益:① 性能和资源利用率提升:升级 Dubbo3 后,预期能够实现单机资源接近 50%的资源利用率下降,集群规模越大,效果越明显。同时,Dubbo3 从架构上支持百万实例级别集群的横向扩展。② 助力业务架构升级:借助于 Dubbo3 提供的新协议,能够让业务架构升级得到更多帮助。比如此前,从前端应用接入到后端,需要经过网关代理,而网关需要进行协议转换或类型映射等工作,因此往往会成为架构的瓶颈。有了 Triple 协议后,过程变得更容易实现,同时还提供了流式通信模型等更丰富的通信场景的支持。③ 云原生:屏蔽了基础设施层变革,降低 Mesh 升级成本,提供了更丰富的流量治理模型。 Dubbo 从设计之初就内置了服务发现能力。 Provider 注册地址到注册中心,Consumer 通过订阅,实时获取注册中心地址并进行更新。接收到地址列表后,Consumer 基于特定的负载均衡策略发起对Provider 的 RPC 调用,如上图所示。过程中,每个Provider通过特定的 key 向注册中心注册本机可访问地址,而注册中心通过 key 对 Provider 实例进行地址聚合。 Consumer 通过同样 key 从注册中心订阅,以便实时收到聚合后的地址列表。 Provider部署的应用通常有多个 Dubbo Service ,每个 Service 可能都有其独有的配置。Service 服务发布的过程就是基于服务配置生成地址 URL 的过程,生成的地址数据就如上图橙色部分 URL 所示。同样其他服务也会生成地址,即 Dubbo 实例会生成多个 Service URL。注册中心以 Service 服务名作为数据划分依据,将服务下所有地址的数据都作为子节点进行聚合。子节点内容是实际可访问的 IP 地址,即 Dubbo 中的 URL 。URL 地址数据的详细格式可划分成四个部分:① 实例可访问地址:消费端将基于这条数据生成 TCP 链接,作为后续 RPC 数据的传输载体。② RPC 服务元数据:主要是用于定义和描述一次 RPC请求,表明这条地址数据是与某条具体 RPC 服务有关,比如接口名、版本号分组以及方法定义等。③  RPC 相关配置数据:有些配置用于控制 RPC 调用行为,有些用于同步 Provider 端进程实例的状态到消费端。典型的配置有超时时间、序列化编码的方式。④ 业务自定义元数据:主要用于区分以上框架预定义的各项配置,为用户提供更大灵活性。用户可以进行任意扩展,将状态同步到消费端。结合上面对 Dubbo2 接口及地址模型的分析,可以得出 Dubbo 服务治理易用性的秘密:首先,在接口级服务发现模型,地址发现聚合 Key 等于 RPC 粒度的服务,比如在 Java 中定义 Dubbo 服务接口;其次,注册中心同步的数据不只包含地址,还包含各种元数据以及配置;最后,得益于前面两点,Dubbo 可支持各种不同粒度的服务治理,比如应用粒度、RPC 服务粒度以及方法粒度。 Dubbo 在地址通知过程中同步的数据非常丰富,这也是一直以来 Dubbo2 在易用性、服务治理、功能性、可扩展性方面强于很多其他框架的核心原因。然而,Dubbo2 地址模型虽然带来了易用性和强大的功能,但在实践过程中也逐渐发现架构在水平可扩展性上出现了限制。这个问题在普通微服务集群下通常感知不到,当集群规模逐渐增长,集群内应用和机器达到一定数量时,集群内的组件就会开始出现规模瓶颈。主要存在以下两个突出问题,如上图橙色所示:由于所有 URL 地址数据都被发送到注册中心,注册中心存储容量容易达到上限,推送效率也会下降;消费端侧,Dubbo2 框架常驻内存通常已经占用超过业务进程的 40%,每次地址推送带来动态 CPU 资源消耗或资源波动也非常明显,甚至会影响正常业务调用。通过DubboProviderr(蓝色部分)实例来分析以上问题出现的原因。假设有一个普通的 DubboProvider 应用,应用内部有 10 个 RPC Service ,Dubbo服务应用被部署在 100 个机器实例之上。那么,应用在集群中产生的数据量微10*100,即 1000 条 URL 数据。数据将从两个维度被放大。从地址角度,有 100 个机器实例,100 条唯一地址数据被放大 10 倍;从服务角度,因为服务元数据在应用内都是唯一元数据,被地址数放大 100 倍。面对上述问题,在 Dubbo3 架构下,我们不得不深入思考如何在保留应用性和功能性的同时,重新组织 URL 地址数据,避免冗余数据的出现,使 Dubbo3 能够支撑更大规模的水平扩容;其次,还需解决如何在地址发现层面与其他微服务体系打通。在地址发现链路上,接口级聚合元素Key 是 RPC 服务。在应用级发现中,设计思路是将聚合 Key 由服务调整为应用。另外,通过对注册中心数据的大幅精简,只保留最核心IP 和 port,使应用级发现链路传输地址的数据量得到大幅度精简。上图为升级后应用级服务发现内部的数据结构。与之前接口级地址发现模型的不同主要在于图中橙色部分。Provider 实例侧:相比于此前每个 RPC Service 都注册一条地址数据,升级后的 Provider 部署机器实例只注册一条地址到注册中心。注册中心侧:地址开始以应用名为粒度进行聚合,应用名节点下是精简后的 Provider 及实例,新生成的 URL 已经只包含 IP 和 port。 应用级发现经过上述调整,实现了地址单条数据大小和地址总数量下降,但也带来了新挑战——基本损失了此前 Dubbo2 强调的易用性和功能性,因为元数据传输被精简掉,如何精细地控制单个服务行为变得无法实现。针对上述问题,Dubbo3 的解法是引入内置元数据服务 MetadataService 。在引入元数据服务后,此前由注册中心从 Provider 同步元数据的行为转变为点对点的拉取行为,消费端在收到纯粹的 IP 和 port 地址通知后,会通过 MetadataService 找到对应的 Provider 进行点对点元数据读取。此模式下,元数据传输的数据量不再是问题,甚至于可以在元数据服务或元数据内容之上扩展出更多参数,暴露出更多服务治理。上图为 Dubbo 应用级服务发现的基本工作原理,重点看 Consumer 侧的地址订阅行为。消费端分两步来完成地址读取以及组装工作。首先,从注册中心收到精简后的 IP 和 port 地址后,通过调用 MetadataService元数据读取到对端的元数据信息。收到这两份数据后,再由消费端完成地址数据聚合,最终在运行态还原出类似Dubbo2 的 URL 地址格式。从运行态的最终结果而言,应用级地址模型同时兼顾了传输层面性能与运行层面的功能性。三、饿了么 Dubbo3 升级过程上图为饿了么基本部署架构图。升级之前,饿了么微服务框架采用HSF2,跨单元 RPC 调用通过中间的 Proxy 中心化部署代理完成中转。过程中 Proxy 所承载的机器数和流量会有迅速增长。与应用级服务发现的地址发现模型相关,比较突出的一点是 Proxy 需要订阅两侧所有地址数据。因此在存储和数据聚合方面,资源消耗和稳定性都受到了严重挑战。因此,我们期望通过升级 Dubbo3 结合整个架构升级来解决通过 Proxy 集中式流量调度以及 Proxy 面临的地址存储和推送压力。主要期望实现两个目标:第一,将地址模型切换到应用级服务发现地址模型,从而大幅度减轻中心化节点和消费端节点的资源消耗压力;第二,以应用级服务发现架构下的全局共享注册中心取代 Proxy 需要订阅两侧不同的注册中心模型,实现跨单元节点的通信直连。Dubbo3 对此前的 Dubbo 2 和 HSF2 做了全面 API 兼容,因此,饿了么升级到 Dubbo3 可以实现零改造升级,并且每个应用都可以进行独立升级,不需要关心上下游应用的升级状态。 Dubbo3 升级后,不论从地址发现模型还是协议默认行为,都保持与 2.0 版本兼容,用户可以在任意时间点对任意应用按需切换。上图右侧模拟展示了饿了么集群在 Dubbo3 升级过程的中间状态,其中灰色代表没有升级的 HSF2 老应用,橙色和绿色代表已经升级到 Dubbo3 的应用,其中橙色表示已完成迁移,绿色代表已升级但未迁移。上图升级过程可以说明两点:Dubbo3 框架升级在 API 和默认行为方面是完全兼容的;另外,集群内应用以及升级到Dubbo3 以后的地址行为和切换行为是完全独立的。橙色部分往发现模型迁移时具体操作如何?首先看 Provider 端行为。Provider 在升级 Dubbo3 以后会默认保持双注册行为,即同时注册接口级地址和应用级地址。一方面是为了保持兼容性,另一方面为未来消费端的迁移做好准备。双注册行为对于注册中心带来的额外压力,在应用级服务发现模型下并不是问题。每增加一条应用及服务发现的 URL 地址,只会带来 0.1% - 1% 的额外开销。Consumer 端与 Provider 端类似,要实现平滑迁移,消费端地址模型不可避免地要做双订阅过程。消费端双订阅行为可以通过规则和开关进行动态调整,控制消费端消费的某个服务或某个应用独立迁移到应用地址模型。除此之外,Dubbo3 还内置了自动决策机制并默认开启,在发现应用级地址可用的情况下,即可自动完成从接口级地址到应用级地址的切换。Dubbo 的基本工作原理比较简单,其核心是围绕提供者、消费者和注册中心三个节点的数据同步。饿了么在升级 double3 应用级发现模型后,成功实现了去 Proxy 的总体架构目标。Dubbo3 可以实现透明兼容升级,并且能够按需切换地址模型行为。Dubbo3在饿了么的升级,代表着阿里巴巴内部真正意义上实现了对于 HSF2 的替换,并且迁移到新的应用级地址服务发现模型。 
文章
存储  ·  Dubbo  ·  Cloud Native  ·  网络协议  ·  前端开发  ·  应用服务中间件  ·  API  ·  调度  ·  HSF  ·  微服务
2022-07-08
阿里云中间件发展历程和开源现状
阿里云中间件发展历程和开源现状——胡伟琪阿里云智能资深技术专家、云原生中间件技术负责人中间件已经发展多年,其目的主要为通过标准接口和协议解决异构网络环境下分布式应用软件互联和互操作问题。近几年,随着云原生技术的高速发展,云时代对中间件的定义又进行了扩充。2020 年由信通院牵头组织的云原生中间件白皮书对于云原生中间件又提出了 10 项新要求,主要分为底层资源、设计原则、运行时和呈现状态四个维度。阿里巴巴中间件已经有 15 年的发展历史,它与阿里业务一起成长,也是阿里巴巴云原生实践 15 年全程见证者。阿里巴巴中间件的发展可以分为四个阶段:中间件 1.0 时代:阿里巴巴彻底放弃 IOE 并启动自研中间件。此阶段发布了 HFS 做分布式 RPC 调用、 Notify 做消息推送以及 TTL 做自研数据库分库分表等。这一系列中间件的发布,象征着阿里巴巴的技术已经具备了支持业务高速发展能力,同时期也开始对外孵化阿里云飞天操作系统。中间件 2.0 时代:随着阿里巴巴内部业务规模不断扩展,对于稳定性和高可用的建设也提上日程。此阶段主要以中间件为核心,在阿里巴巴集团内推出了单元化、异地多活等高可用架构的设计和落地,并且实现了阿里整体应用架构 stable by default 模型落地。中间件 3.0 时代:此阶段,阿里巴巴开始意识到技术输出的重要性,因此启动了中间件上云,借助阿里资深的技术优势,在云上推出了 EDAS、MQ、DRDS 等一系列商业化产品,将阿里巴巴内部积累多年的分布式、微服务领域产品对外输出,普惠大量外部企业客户,也为阿里云带来了巨大的差异化竞争力。除了商业化输出,阿里也开始投入资源和精力做开源,比如 Apache、RocketMQ、Apache Dubbo、Nacos 等等都在 3.0 时代对外进行开源。中间件 4.0 时代:推行三位一体技术发展策略,要求集团内使用的技术、阿里云上使用的技术以及开源技术三位一体,让技术和架构形成协同发展,能够更好地赋能给企业客户。在这一阶段,我们借助云帮助企业充分构建弹性、韧性、安全、易用性、高 SLA 的云原生应用,支持企业快速进行数字化转型。经过四个阶段的发展,阿里巴巴形成了有别于其他企业或传统定义的中间件体系。阿里巴巴中间件是根据自身业务发展出来互联网中间件体系,每一款中间件都体现了互联网和云的价值,比如弹性能力、分布式能力、容灾能力等。阿里巴巴中间件将企业云原生化和分布式化落地过程中所需基础设施一一进行了分布,主要包含架构&监控服务领域、研发框架&消息&工具领域、微服务托管领域以及基础产品领域,其中很大一部分都进行了开源,普惠了数万外部企业和百万开发者。过去十年,中间件开源项目主要集中在分布式应用架构领域,解决使用者在企业内推行分布式从 0 到 1 过程中需要依赖的基础中间件。这些中间件的落地可以大幅度加快企业分布式或者微服务化进程,也可以让架构韧性和稳定性得到更大提升。包括 Apache Dubbo、RocketMQ、Java 领域诊断神器 Arthas、微服务注册和配置管理 Nacos 、分布式事务 Seata 以及与 Spring Cloud 合作共建的 Spring Cloud Alibaba。当前,越来越多客户完成了微服务化和分布式应用落地。随着分布式应用和微服务规模越来越大,如何管好和用好微服务应用被提上日程,这也成为越来越多客户发展中的痛点。因此,我们开始定义分布式应用治理领域,后续也会在此领域不断推进开源项目、技术以及技术产品输出。分布式应用治理领域主要解决用户完成 0 到 1 分布式和微服务落地之后,期望进一步扩大规模时,如何保证大量微服务应用和分布式应用能够运行在一个可靠、稳定、高效的环境中,能够在出现问题时快速容错,没有出现问题时快速预警等,这些对于企业未来的高速发展至关重要。今年,分布式应用治理领域推出了两个新的开源项目,其中 OpenSergo主要做微服务治理,AppActive 主要做应用多活架构。此外,还有已经开源很久的做应用流量管控的 Sentinel ,以及前年开源的 ChaosBlade(目前已经捐给 CNCF 成为 CNCF 沙箱项目),主要做混沌工程、故障演练、依赖注入等。以上四个项目共同组成了当前分布式应用治理领域的完整配套解决方案。相信客户和使用者能够基于这四个开源项目构建出强大的分布式应用治理相关能力。很长一段时间内,微服务领域的治理都没有统一标准,使用不同框架的企业、个人和开发者对应用和流量做管理非常困难,再加上没有统一规范,迁移和跨平台使用能力也受到约束。因此,2021年阿里开始构思开源项目,并与业内同行,包括字节跳动、B站、快手等其他伙伴一起制定标准,于今年 4 月正式对外推出了 OpenSergo 项目。OpenSergo 项目是一套开放的、面向应用的、贴近业务语义的服务治理规范和参考实现,能够帮助企业快速构建面向海量微服务场景下服务治理的相关能力,主要包含三个方面:①  规范:定义了流量治理、流量防护、可观测、服务发现、数据库治理等一系列规范,能够让微服务在全生命周期内都具备很好的治理能力。②  SDK:基于上面的规范,提供了多语言参考实现,包括 Java、Go、C++、 Ruby 等。③  接入&集成:SDK 要适配目前业界市面上的常用框架,包含微服框架、网关、注册中心、配置中心、消息队列等一系列微服务所依赖的框架或微服本身实现的框架,能够实现现有存量用户也能快速遵循相关规范,让应用具备无缝迁移或更好的治理能力。在项目推行之初,我们就设计了四条原则:标准化:希望通过一致、标准的微服务治理模型和规范,降低用户微服化成本。企业开源项目侵入到研发架构和用户开发流程中,接入成本过高。因此我们希望通过标准化规范,降低用户接入系统和实现微服务治理能力的成本,提高效率。开放:作为开源项目,始终要保持自己的开放定位。支持多语言框架,提供多种治理模式,包括现在发展较快的 service mesh 以及一些比较新的领域比如 Proxyless mesh 等。另外,我们也会持续与业界伙伴共建开放规范。价值:减少微服务开发者心智负担,屏蔽底层差异,让开发者能够更专注在业务价值开发上。全方位、全生命周期:覆盖微服务应用全生命周期,让整个微服务从部署、交付、运行、监控、故障排查等全生命周期都具备可治理的能力。随着互联网应用分布越来越广泛,出现了很多与国际、民生息息相关的应用,这对应用的稳定性和容灾能力也提出了更高要求。过去十年,阿里巴巴在容灾领域有了非常深厚的积累,作为单元化、同城双活、异地多活等架构的提出者,我们也在自己的业务中做了很好的践行。今年 1 月,我们将中间件在异地多活容灾的技术和经验积累,以开源的形式对外提出,也希望能够借助项目帮企业构建以应用为中心、面向未来容灾的架构。要实现容灾架构,不管从流量层、接入层、数据层、传输层还是应用层,都需要具备良好的容灾能力,才能认为架构上具备了容灾能力。基于此,我们设计了四条原则:分层的设计标准:定义了接入层、服务层、消息层、数据层的应用多活标准规范。基于规范使用开源项目,即可使 IT 架构具备端到端的多活能力。精简的接入规范:提供了丰富的样例,应用只需进行少量改造即具备应用多活的能力。此前,很多客户认为实现多活需要极大的改造成本,但精简的接入规范使用户可以以比较低的成本实现自己的多活能力。广泛的开源支持:想要端到端地实现多活,必须有丰富的生态作支撑,因此,我们在设计之初就将开源支持作为核心能力,包含了策略开放、核心技术无差别开源、生态开放以及兼容主流开源技术和标准。多样的实践案例:提供面向多云、多行业典型场景的最佳实践和方法论。企业构建无论是构建异地多活、多云多活还是云和非云多活,亦或不同场景再结合自身行业属性做多活等,都可以基于项目找到适合的方案。今年年初,开源社发布的 2021 年中国开源年度报告显示,阿里巴巴蝉联中国企业开源活跃度第一,体现了阿里在开源的投入情况。此外,中国开源十年洞察报告显示,过去十年阿里巴巴始终排名第一,这也很好地体现了阿里巴巴持续做开源的决心。在中国活跃开源项目 top 30 榜单中,阿里巴巴共有 10 个项目上榜,其中中间件团队上榜4 个项目,分别为 Apache Dubbo、Apache RocketMQ、Nacos、Seata ,这也意味着中间件团队一直都是阿里巴巴开源力量中的核心部分。除了开源投入,阿里巴巴还积极参与到许多顶级基金会,比如 Linux 基金会、云原生基金会、Apache 基金会等,贡献的诸多项目也已遍布主流开源社区。后续,阿里巴巴会持续参与到基金会建设以及基金会项目建设里,为中国开源技术发展贡献更多力量。
文章
消息中间件  ·  Cloud Native  ·  容灾  ·  Dubbo  ·  中间件  ·  Java  ·  应用服务中间件  ·  Apache  ·  开发者  ·  微服务
2022-07-08
微服务引擎 MSE 6月份产品动态
MSE 注册配置中心专业版首购享 9 折优惠,MSE 云原生网关预付费全规格享 9 折优惠。
文章
Cloud Native  ·  Dubbo  ·  Java  ·  应用服务中间件  ·  Nacos  ·  Sentinel  ·  微服务  ·  Spring
2022-07-07
OpenSergo 即将发布 v1alpha1,丰富全链路异构架构的服务治理能力
作者:宿何&卜比OpenSergo 是什么在传统微服务架构中,我们将服务调用中各角色分为四大块:服务提供者、服务消费者、注册中心、监控。随着分布式服务架构的不断演进带来诸多复杂的稳定性与易用性问题,单一的监控已无法满足架构的演进。在现代微服务架构中,我们需要一些手段来对复杂的微服务架构进行“治理”。微服务治理就是通过全链路灰度、无损上下线、流控降级、异常流量调度、数据库治理等技术手段来减少甚至避免发布和管理大规模应用过程中遇到的稳定性问题,对微服务领域中的各个组件进行治理。服务提供者、消费者、注册中心、服务治理,构成现代微服务架构中重要的几环。在企业内部,往往存在着不同语言、不同通信协议的微服务,这种异构化的架构会导致治理微服务的过程中,业务开发者、架构师无法用统一的方式来对所有服务进行治理管控,并且这类异构会衍生出更多的痛点:业内对服务治理的能力和边界没有明确的认识,每个企业所定义的服务治理概念不一致,造成很高的理解和沟通成本。开源微服务框架众多,对于服务治理缺乏一些标准化的约定。例如,Spring Cloud 中定义的微服务接口和 Dubbo 中定义的接口就没有办法互通,通过 Dubbo 和 Istio 管理的微服务也没有办法进行统一治理。开发者无法通过统一的配置方式来对不同框架、不同语言的服务进行统一治理管控。缺少真正面向业务、能够减轻认知负担的抽象和标准。开发者真正想要的可能是简单的、指定服务间的调用关系和配置规则。但现在对于业务开发者来说,不仅需要了解不同微服务框架的部署架构,也要了解不同服务治理方式的概念和能力区别,认知成本很大。基于上面这些痛点,阿里巴巴在 2022 年 1 月开始和 bilibili、字节等厂商讨论服务治理如何规范化和更加普及,从而共同发起了 OpenSergo 项目。OpenSergo 是一套开放、通用的、面向分布式服务架构、覆盖全链路异构化生态的服务治理标准,基于业界服务治理场景与实践形成服务治理通用标准。OpenSergo 的最大特点就是以统一的一套配置/DSL/协议定义服务治理规则,面向多语言异构化架构,做到全链路生态覆盖。无论微服务的语言是 Java, Go, Node.js 还是其它语言,无论是标准微服务还是 Mesh 接入,从网关到微服务,从数据库到缓存,从服务注册发现到配置,开发者都可以通过同一套 OpenSergo CRD 标准配置针对每一层进行统一的治理管控,而无需关注各框架、语言的差异点,降低异构化、全链路服务治理管控的复杂度。OpenSergo 标准基于微服务治理中相关领域的实践与场景抽象,覆盖了服务元信息、流量治理、服务容错、数据库/缓存治理、服务注册发现、配置治理等十几个关键领域,覆盖了完整的微服务生命周期(从开发态到测试态,到发布态,再到运行态)。无论我们是希望针对 Spring Cloud + Dubbo 服务链路配置流量灰度隔离,还是希望针对一个 Go gRPC 服务进行流量控制,还是希望针对服务访问数据库的慢 SQL 调用进行自动熔断,我们都可以利用 OpenSergo spec 中定义的 CRD 标准来进行统一配置,而无需关注各框架不同的声明式 API 及互不兼容的配置格式。OpenSergo 生态由以下几部分组成:OpenSergo spec:统一的服务协议与 CRD 标准定义OpenSergo 多语言 SDK:提供统一的标准 CRD 对接模块,供各个框架组件对接 OpenSergo specOpenSergo 数据面:即对接 OpenSergo spec 的框架组件,均可通过 OpenSergo 标准方式进行统一治理OpenSergo 控制面:提供统一的控制台来进行服务元信息查询以及流量路由、流量控制等治理规则配置。我们期望与各个社区进行合作共建,将更多的框架与组件对接到 OpenSergo 生态中,每个框架都是 OpenSergo 的数据面,可以通过 OpenSergo CRD 进行统一治理管控。那么 OpenSergo 标准到底是什么样子的呢?我们可以利用 OpenSergo 标准来做哪些事情呢?下面我们来结合几个例子来进行介绍。OpenSergo 标准介绍OpenSergo 项目涵盖服务元信息、服务注册发现、流量治理、服务容错、数据库治理、缓存治理等领域。在我们的首个版本 v1alpha1 版本中,我们提供了 服务契约(元数据)、流量路由、流控降级 这几个领域的 CRD 标准。下面我们来介绍一下流量路由与流控降级这两个领域的示例。流量路由流量路由,顾名思义就是将具有某些属性特征的流量,路由到指定的目标。流量路由是流量治理中重要的一环,我们可以基于流量路由标准来实现各种场景,如全链路灰度、金丝雀发布、容灾路由等。流量路由规则(v1alpha1) 主要分为三部分:Workload 标签规则 (WorkloadLabelRule):将某一组 workload(如 Kubernetes Deployment, Statefulset 或者一组 pod,或某个 JVM 进程,甚至是一组 DB 实例)打上对应的标签流量标签规则 (TrafficLabelRule):将具有某些属性特征的流量,打上对应的标签按照 Workload 标签和流量标签来做匹配路由,将带有指定标签的流量路由到匹配的 workload 中我们以广泛使用的全链路灰度场景为例。全链路灰度通过一系列的流量路由规则,将链路上的多个服务的相同版本划分到同一个泳道中,从而约束流量只在指定泳道中流转,实现全链路的流量隔离的目的。整个流程可以用下图概括,我们通过通用的 Workload 标签规则与流量标签规则,来以统一的标准方式对完整的服务链路实现灰度的能力。给 Workload 打标签:我们对新版本进行灰度时,通常会有单独的环境,单独的部署集。我们将单独的部署集打上 gray 标签(标签值可自定义),标签会参与到具体的流量路由中。我们可以通过直接在 Kubernetes workload 上打 label 的方式进行标签绑定,如在 Deployment 上打上 traffic.opensergo.io/label: gray标签代表灰度。对于一些复杂的 workload 打标场景(如数据库实例、缓存实例标签),我们可以利用 WorkloadLabelRule CRD 进行打标。示例:apiVersion: traffic.opensergo.io/v1alpha1 kind: WorkloadLabelRule metadata: name: gray-sts-label-rule spec: workloadLabels: ['gray'] selector: app: my-app-gray database: 'foo_db'给流量打标:假设现在需要将内部测试用户灰度到新版主页,测试用户 uid=12345,UID 位于 X-User-Id header 中,那么只需要配置如下 CRD 即可:apiVersion: traffic.opensergo.io/v1alpha1 kind: TrafficLabelRule metadata: name: my-traffic-label-rule labels: app: my-app spec: selector: app: my-app trafficLabel: gray match: - condition: "==" # 匹配表达式 type: header # 匹配属性类型 key: 'X-User-Id' # 参数名 value: 12345 # 参数值 - condition: "==" value: "/index" type: path通过上述配置,我们可以将 path 为 /index,且 uid header 为 12345 的 HTTP 流量,打上 gray 标,代表这个流量为灰度流量。按照标签来路由:在具体的路由过程中,接入了 OpenSergo 的微服务框架、Service Mesh 的 proxy 中,只要实现了 OpenSergo 标准并进行上述规则配置,那么就能识别流量的标签和 workload 的标签。带 gray 标签的流量就会流转到 gray 标签的实例分组中;如果集群中没有 gray 实例分组(即没有 workload 带有这个标签),则默认 fallback 到没有标签的实例上。后续版本标准将提供未匹配流量的兜底配置方式。社区还在不断完善流量路由相关的标准,并与各个社区合作共建,让更多的框架组件支持 OpenSergo 标准,从而支持统一的流量路由管控。流控降级与容错流控降级与容错同样是服务流量治理中关键的一环,以流量为切入点,通过流控、熔断降级、流量平滑、自适应过载保护等手段来保障服务的稳定性。在 OpenSergo 中,我们结合 Sentinel 等框架的场景实践对流控降级与容错抽出标准 CRD。一个容错治理规则 (FaultToleranceRule) 由以下三部分组成:Target: 针对什么样的请求Strategy: 容错或控制策略,如流控、熔断、并发控制、自适应过载保护、离群实例摘除等FallbackAction: 触发后的 fallback 行为,如返回某个错误或状态码无论是 Java 还是 Go 还是 Mesh 服务,无论是 HTTP 请求还是 RPC 调用,还是数据库 SQL 访问,我们都可以用这统一的容错治理规则 CRD 来给微服务架构中的每一环配置容错治理,来保障我们服务链路的稳定性。只要微服务框架适配了 OpenSergo,即可通过统一 CRD 的方式来进行流控降级等治理。以下 YAML CR 示例定义的规则针对 path 为 /foo 的 HTTP 请求(用资源名标识)配置了一条流控策略,全局不超过 10 QPS。当策略触发时,被拒绝的请求将根据配置的 fallback 返回 429 状态码,返回信息为 Blocked by Sentinel,同时返回 header 中增加一个 header,key 为 X-Sentinel-Limit, value 为 foo。apiVersion: fault-tolerance.opensergo.io/v1alpha1 kind: RateLimitStrategy metadata: name: rate-limit-foo spec: metricType: RequestAmount limitMode: Global threshold: 10 statDuration: "1s" --- apiVersion: fault-tolerance.opensergo.io/v1alpha1 kind: HttpRequestFallbackAction metadata: name: fallback-foo spec: behavior: ReturnProvidedResponse behaviorDesc: # 触发策略控制后,HTTP 请求返回 429 状态码,同时携带指定的内容和 header. responseStatusCode: 429 responseContentBody: "Blocked by Sentinel" responseAdditionalHeaders: - key: X-Sentinel-Limit value: "foo" --- apiVersion: fault-tolerance.opensergo.io/v1alpha1 kind: FaultToleranceRule metadata: name: my-rule namespace: prod labels: app: my-app # 规则配置生效的应用名 spec: targets: - targetResourceName: '/foo' strategies: - name: rate-limit-foo fallbackAction: fallback-foo在中间件开发者峰会中,我们宣布了 Sentinel 2.0 流量治理的全面升级。Sentinel 2.0 将原生支持流量治理相关 CRD 配置,结合 Sentinel 提供的各框架的适配模块,让 Dubbo, Spring Cloud Alibaba, gRPC 等20+框架能够无缝接入到 OpenSergo 生态中,用统一的 CRD 来配置流量路由、流控降级、服务容错等治理规则。社区规划让异构微服务能够用统一的服务协议与配置方式进行治理、让更多微服务能够互联互通,塑造更加云原生的微服务,是 OpenSergo 建立之初就树立的长期发展目标。在标准化建设上,OpenSergo 社区会联合更多开源社区与企业,在数据库治理、缓存治理、服务注册发现、配置治理等更多领域层面上标准化微服务治理能力,让企业能够用一套通用语言来描述和治理自己的微服务架构,让开发者专注于业务核心价值,让微服务框架也能够被客户轻松采用。在社区生态建设上,OpenSergo 社区将逐渐覆盖从网关、RPC、数据库、缓存到服务发现、服务配置等分布式服务链路中的每一环生态,通过与各社区合作,让各主流框架均可以借助统一的 OpenSergo spec 来定义与实现服务治理的能力,开发者无需关注各框架、协议的概念与实现差异,降低开发者跨语言、跨框架、跨协议层面服务治理的管控成本。OpenSergo 社区将持续与 Kratos、CloudWeGo Kitex、Spring Cloud Alibaba、Dubbo 等社区进行合作,同时也会推进与 Apache APISIX、Envoy/Istio、gRPC、Druid、ShardingSphere 等更多社区的合作,将标准落地到各个框架中。我们也非常欢迎更多开源社区与企业一起加入 OpenSergo 的标准与生态共建。在控制面建设上,OpenSergo 目前正在联合社区打造 OpenSergo Dashboard 作为统一的服务治理控制面,通过中立、通用的 OpenSergo 标准协议,让所有的微服务框架、所有的通信协议都可以被同一套微服务门户来治理。欢迎加入OpenSergo 自创立就是社区项目,通过 Apache License 2.0 协议开源。OpenSergo 正在与 Apache Dubbo, CloudWeGo Kitex (字节), Kratos (bilibili), Spring Cloud Alibaba, Apache APISIX 等社区进行合作,共同完善服务治理标准设计与实现,一起将 OpenSergo spec 推进和落地到更多微服务生态中。后续在 OpenSergo 服务治理标准的制定、发展上,也会通过公开、透明、民主的方式来制定标准、推动实施。社区也通过 GitHub issue、Gitter、邮件列表、社区双周会等机制,确保通过社区协作的方式来共建标准与实现。欢迎大家通过这些形式一起来讨论、共建。也欢迎大家钉钉扫码加入 OpenSergo 社区钉钉群,一起来定义微服务治理的未来:同时我们在阿里云也提供 OpenSergo 微服务标准的企业级产品 MSE,提供服务治理、流控降级、注册配置中心、云原生网关、分布式事务等核心能力,欢迎大家体验。相关链接:OpenSergo 项目官网:https://opensergo.io/OpenSergo spec: https://github.com/opensergo/opensergo-specification阿里云微服务引擎 MSE,OpenSergo 的企业级实现:https://www.aliyun.com/product/aliware/mseMSE 注册配置中心专业版首购享 9 折优惠,MSE 云原生网关预付费全规格享 9 折优惠。点击此处,即享优惠~
文章
缓存  ·  Dubbo  ·  Java  ·  应用服务中间件  ·  Apache  ·  数据库  ·  开发者  ·  Sentinel  ·  微服务  ·  Spring
2022-07-07
阿里云中间件开源往事
分布式架构和云原生重塑了中间件的游戏规则,这给国内开发者提供了重新定义中间件的历史机遇。在分布式架构流行前,国外 IT 厂商引领着中间件市场的发展,且以闭源、重商业的服务形式为主;随着云计算和互联网的普及,阿里将 RPC 框架、消息队列、服务发现、配置中心、分布式事务、限流降级等核心应用中间件技术对外开源,加速了分布式架构在国内的落地,也使得开发者在 Spring 技术栈以外多了一种选择。而云原生则实现了中间件以 BaaS 或 SaaS 的形态出现,解决了分布式应用架构落地后,中间件在容量管理、交付、运维、容灾上的难题,使用者通过标准化的 API 就可以完成对中间件的调用,从而提升企业整体的开发和运维效率。本文讲述了阿里云在应用中间件领域核心开源项目的过去、现在和未来,篇幅较长,故事线罗列如下:Apache Dubbo:同步架构通信,从 RPC 框架到全面拥抱云原生基础设施Apache RocketMQ :异步架构通信,从 Messaging 到 Streaming 和 EventingNacos:从架构下沉到关键组件,持续突破性能瓶颈,市场占有率已经超过50%Sentinel:首次涉及服务治理领域,但不止于限流降级,即将发布里程碑版本2.0Spring Cloud Alibaba:对国内开发者、阿里云、Spring 三方来说,都是一个好消息Arthas:一款工具型开源项目,Stat 即将突破 3wChaosBlade:业务稳定,不仅需要事中限流降级,更需要事前故障演练Seata:让分布式事务的使用像本地事务的使用一样,简单和高效AppActive:Sentinel、ChaosBlade、AppActive,高可用三家马车成功集结OpenSergo:解决日益增长的微服务框架混用企业的服务治理难从 RPC 框架到全面拥抱云原生基础设施Apache Dubbo(以下简称 Dubbo)是阿里巴巴于 2012 年开源的分布式服务治理框架,是国内影响力最大、使用最广泛的开源 RPC 框架之一,2017 年捐献给 Apache 基金会,2019 年正式毕业。Dubbo 和社区开发者们“从孵化器毕业是一种荣誉,但这并不是结束,而是另一种开始。这有点像求学,毕业并不意味着学习上的中断,而是发挥更大社会价值的开始。毕业也更像是一个成人礼,意味着 Dubbo 团队已经符合 Apache 对一个成熟开源项目的要求,并开始具备独立发展的能力。”阿里云高级技术专家北纬当时在接受媒体采访时回答道。从 Apache 孵化器毕业,并不是结束。服务框架就像铁路的铁轨一样,是互通的基础,只有解决了服务框架的互通,才有可能完成更高层的业务互通,所以采用相同的标准是新一代服务框架发展的必然趋势。2021 年,Dubbo 正式发布 3.0 版本,Dubbo3.0 是 Dubbo2.0 与 HSF 融合而来,是阿里巴巴面向内部业务、商业化、开源的唯一标准服务框架。来自 Dubbo 官网首页Dubbo3.0 的发布,也源自全面拥抱云原生基础设施的核心演进方向随着 K8s 成为资源调度的事实标准,Service Mesh 从提出到发展至今已经逐渐被越来越多用户所接受。Dubbo 这类 Java 微服务治理体系面临了许多新的需求,包括期望应用可以更快的启动、应用通信的协议穿透性可以更高、能够对多语言的支持更加友好等。因此,Dubbo3.0 首次提出了全新的服务发现模型、下一代 RPC 协议和适配云原生基础设施等新能力。Dubbo 3.0 支持应用级服务发现:Dubbo 原本采用接口级别的注册方式,存储在注册中心中的数据会在很大程度上存在重复的内容,随着服务规模的增长,注册中心的数据量就会爆发式地增长,支持应用级服务发现后,不仅大大减少注册中心的内存压力,以获得更强的性能,更重要的是,打通了与其他微服务体系之间在地址发现层面的鸿沟,这是在适配 Kubernetes 等基础设施上,走出的重要一步。Dubbo 3.0 提出了下一代 RPC 协议 —— Triple:这是一个基于 HTTP/2 设计的完全兼容 gRPC 协议的开放性新协议,具有极高的网关友好型和穿透性,完全兼容 gRPC 协议是的天然在多语言互通方面上具有优势。这也解决了上一代协议中生态不互通、协议头无法再承载更多元数据信息的问题。从 Messaging 到 Streaming 和 Eventing如果把 RPC 作为同步通信的实现机制,那么消息队列可以看作是异步通信的实现机制。除了用于异步通信外,消息队列还能用于解耦、削峰填谷、分布式事务等场景,这对消息队列在性能、稳定性上提出了更高的要求。2011 年,当时的双 11 经常会出现消息延迟半天甚至一天以上,导致商家看不到买家已经购买了的商品的问题。而解决这个问题的本质是如何实现高速读写,但基于之前的架构,无法彻底地解决问题。那么,就需要设计一个全新的存储架构。负责全新产品设计的任务,刚好落到了 RocketMQ 创始人&作者王小瑞身上。但当时总共就两个人,一个人负责 Notify,王小瑞则负责全新产品的设计。但开源,可以汇聚数百人、数千人、数万人一起来开发,也能吸收所有公司、行业、业务场景的需求,汇聚最大的生产力。因此,从第一天开始的时候,RocketMQ 就是托管在 GitHub 上,也就是说它的第一行代码就是对所有开发者和用户开放的,整个开发过程也是对用户开放的,这也吸引了特别多的开发者,大家帮助 Review 代码、测试 Bug,RocketMQ 在众多开发者的参与下进展迅速。2016 年的那届双 11,RocketMQ 团队首次将低延迟存储解决方案应用于双 11 的支撑,经受住了流量的大考,整个大促期间,99.996% 的延迟落在了 10ms 以内,完成了保障交易稳定的既定目标,对于读写比例几乎均衡的分布式消息引擎来说,这一技术上的突破,即便是放在全球范围内,也绝对是值得称赞的。同时,“双 11”当天达到万亿级消息量,峰值 TPS 达几千万,也创造了当时世界上最大的消息流转记录。RocketMQ 和社区开发者们2016 年,在历时 3 个月的开源重塑后,RocketMQ 团队启动了向 Apache 软件基金会的捐赠之路,经过近一年的努力,在 2017 年 9 月 25 日,Apache 软件基金会官方宣布,阿里巴巴捐赠给 Apache 社区的开源项目 RocketMQ 从 Apache社区正式毕业,成为 Apache 顶级项目(TLP),这是国内首个非 Hadoop 生态体系的 Apache 社区顶级项目。值得一提的是,根据项目毕业前的统计,RocketMQ 有百分八十的新特性与生态集成来自于社区的贡献。2021 年,在经历社区众多开发者的不断努力,RocketMQ 5.0 正式发布,新版本核心包括两大新亮点:首先,消息核心场景全面扩展,RocketMQ 5.0 不再局限于消息解耦场景,将消息的应用场景从 Messaging 拓展到了 Streaming 和 Eventing 领域;其次,技术架构不断演进,逐渐形成一站式融合处理的技术架构和趋势。2022 年,批量消息索引、逻辑队列发布 RocketMQ-MQTT、RocketMQ-Connect、RocketMQ-Streams,完成了从业务消息平台向『消息、事件、流』一体化融合处理平台的升级。开发者可以实现一份消息存储,支持流式计算、异步投递、集成驱动等多个场景。实现技术问题一站式解决,大大降低技术复杂度和运维成本,简化企业应用架构。分布式应用的落地,仅仅是一个 RPC 框架和一套异步消息系统是不够的,还需要一系列围绕分布式应用架构的组件。技术人的仲夏之夜2018 年夏天,国内应用中间件开源领域,迎来了两位新成员。作为微服务生态下的两款重要开源组件,Nacos 主打动态服务发现、配置和服务管理,Sentinel 则是聚焦在限流和降级两个方面。Nacos 和 Sentinel 均是在阿里 10 多年的核心业务场景下沉淀所产生的,他们的开源是对国内应用中间件领域开源技术方案的有效补充,同时也非常强调融入开源生态,除了兼容 Dubbo,也支持 Spring Cloud 和 Kubenetes 等生态,以增强自身的生命力。“Nacos 起源于阿里巴巴内部,历经十多年双 11 洪峰考验的三款产品 - VIPServer/Configserver/Diamond ,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力,并吸引了 200 多位优秀贡献者,共迭代 44 个版本,服务虎牙、好未来、小米等多家企业,在 2.0 的里程碑版本上,全面升级了通信协议、服务一致性模型、支持服务网格生态和多语言生态。”彦林在 Nacos  star 突破 2w 后分享道。Nacos 2.0 的发布,是 Nacos  star 突破 2w 的又一个里程碑事件。随着 Nacos 用户规模的增长,和用户业务日益复杂,Nacos 2.0 的发布是一个必然。Nacos 1.x 时代:服务发现性能不够强:在 10W、5W 级客户端下,服务端完全处于 Full GC 状态,推送完全失败,集群不可用;在 2W 客户端规模下,虽然服务端运行状态正常,但由于心跳处理不及时,大量服务在摘除和注册阶段反复进行,因此达不到稳定状态,CPU 一直很高;1.2W 客户端规模下,可以稳定运行,但稳态时 CPU 消耗是更大规模下 2.0 的 3 倍以上。配置管理性能不够强:连接客户端数量达到 6000 时,稳定状态的 CPU 一直很高,且 GC 频繁;当客户端规模达到 1.2w 时,已经无法达到稳态,所以无法支撑这个量级的客户端数。推送规模达到 3000TPS 时,占了 80%的 CPU 资源;一旦达到 6000TPS 时,CPU 资源上升到了 90%。Nacos 和社区开发者们Nacos2.0 作为一个跨代版本,对架构做了全面升级,彻底解决了 Nacos1.X 的性能问题,针对服务发现的场景,Nacos2.0 能够在 10w 级规模下,稳定运行,相比 Nacos1.X 版本的 1.2w 规模,提升约 10 倍;针对配置管理的场景,Nacos2.0 单机最高能够支撑 4.2W 个客户端连接,相比 Nacos1.X,提升了 7 倍,且推送时的性能明显好于 1.X。一边是 Nacos 宣布开源,逐步迭代到 2.0 版本,提供企业版 MSE,另一边是 Spring Cloud 生态下的服务注册和发现组件宣布停止开源投入,勇敢者的游戏充满了变数,但在 Nacos 团队看来,这场游戏自己可以走到最后:在 2021 年某第三方媒体对注册中心开源方案采用的调研中,Nacos 的市场占有率已经超过 50%。Nacos 只是阿里云众多中间件开源项目中的一员,随后还有更多的开源项目反哺给社区,形成生态,例如轻量级限流降级组件 Sentinel。影响生产环境的稳定性因素,从来源上看,我们通常可以归纳位两类,一类是版本发布过程中,引入的代码变更带来的风险,一类是外部不规则流量带来的风险。而 Sentinel 作为一款高可用范畴的开源项目,他要解决的就是外部流量导致的稳定性风险。中间件开发者 Meetup 深圳站2018 年 7 月,中间件开发者 Meetup 深圳站现场,只能容纳 400 人的场地,来了近 700 位开发者。Sentinel 创始人子矜在现场宣布了轻量级限流降级组件 Sentinel 的开源。“Sentinel 经历了 10 多年双 11 的考验,覆盖了阿里的所有核心场景,也因此积累了大量的流量归整场景以及生产实践。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。”流量控制:某个服务的上限是 1 秒处理 3000 个 QPS,但如果实际情况遇到高于3000的 QPS 该如何解决呢?Sentinel 通过流量统计的方式,当流量超过阈值,会帮助服务通过直接拒绝、冷启动、匀速器三种方式来应对,从而起流量控制的作用。熔断降级:服务之间会有相互依赖关系,例如服务 A 1 秒可以处理上万个 QPS,但服务 B 不具备这么高的处理能力,那么如何保证服务 A 在高频调用服务B时,服务 B 仍能正常工作呢?Sentinel 通过对并发线程数进行限制和响应时间上对资源进行降级,这两种手段来实现熔断或降级,从而保证服务 B 可以正常工作。2019 年,Sentinel 贡献的 spring-cloud-circuitbreaker-sentinel 模块正式被社区合并至 Spring Cloud Circuit Breaker,由此,Sentinel 也加入了 Spring Cloud Circuit Breaker 俱乐部,成为 Spring Cloud 官方的主流推荐选择之一。开源项目需要不断延展他的能力范畴才能保持持续的生命力,Sentinel 不止于限流降级,他是否也可以帮助开发者解决新版本发布过程中的诸多稳定性风险,这是即将要发布的 Sentinel2.0 要回答的问题。Spring Cloud 官方推荐的微服务方案不止 Sentinel 一个,还有 Spring Cloud Alibaba.2018 年,中国的 Java 圈发生了一件大事。Spring Cloud 联合创始人 Spencer Gibb 在 Spring 官网的博客页面宣布:阿里巴巴开源 Spring Cloud Alibaba,并发布了首个预览版本。随后,Spring Cloud 官方 Twitter 也发布了此消息。来自 Spring Cloud 官方 TwitterSpring Cloud Alibaba 项目由两部分组成:阿里巴巴开源组件和阿里云产品组件,旨在为 Java 开发人员在使用阿里云产品的同时,通过利用 Spring 框架的设计模式和抽象能力,注入 Spring Boot 和 Spring Cloud 的优势。Spring Cloud 本身是一套微服务规范,并不是一个拿来即可用的框架,而 Spring Cloud Alibaba 的开源为开发者们提供了这套规范的实现方式,而这种实现方式对调用阿里云的资源和云产品能力十分友好,这对国内开发者、阿里云、Spring 三方来说,都是一个好消息。夏天过后,开源的热度仍在延续效率的好处在于,我们可以把自己的注意力和时间聚焦在更需要创造力的事情上,做更有成就感的事情。对于工作在工程领域的开发者们而言,他们的效率意识更强。2018 年 9 月,阿里将内部广泛使用的 Java 线上诊断工具进行开源,取名 Arthas (阿尔萨斯)。也许是击中了开发者线上排查问题的痛点,Arthas 在距离开源后的第一个 Release 版发布仅 147 天,就获得了超过 1w 的 star 数,并有 40 多位 Contributors 参与开源贡献。Arthas Contributors从中,我们不仅看到 Arthas 这类工具型开源项目在开发者群体中的受欢迎程度,也发现越来越多的国内开发者开始参与开源贡献,并视为一种社区荣誉。技术领域,一切里程碑的达成,都源于一份技术情怀。截止目前,Arthas 已有接近 3w star 和 146 位  Contributors,这对一款线上诊断工具而言,是一份不错的答卷。时间来到 2019 年。如果把生产阶段比作高考,那么 Sentinel 解决的是事中的稳定性问题,一旦出现流量徒增,可以通过限流和降级来应对,而 2019 年开源的 Chaosblade 则是更多从事前的方式来提高架构的高可用性,通过建立故障演练机制,把各类可以预见的故障提前演练出来,例如随机杀节点、延时响应,甚至中断机房。Chaosblade 和 Sentinel 师出同门,源自阿里在全链路压测、线上流量管控、故障演练上沉淀的这一套高可用核心技术。阿里云资深技术专家中亭说道:“阿里因其多元化的业务场景和日益复杂的技术架构,会遇到各式各样的故障,故障治理的难度增量了几个台阶。Chaosblade 从 2011 年开始,经历了强弱依赖的治理和建设、同城容灾演练、在交易和中间件链路尝试演练三个阶段,经过 6 年多的打磨,最终将最佳实践和工具框架形成统一的标准,对外进行开源,帮助 DevOps 人员缩短构建混沌工程的路径。”在微服务架构普遍落地的今天,分布式事务带来的数据一致性问题、性能问题越来越绕不开。分布式事务理解起来有点门槛,但却无处不在,他是相对本地事务而言的,服务和服务之间不需要跨网络就能完成的,称之为本地事务,需要跨网络才能完成的,称之为分布式事务,例如金融行业银行之间的转账业务需要跨网络才能完成,电商行业交易下单调用外部库存系统、物流系统需要跨网络才能完成等。虽然业内有一些分布式事务开源的解决方案,但要么是性能差、要么是数据一致性不够、要么是侵入性高,不容易落地。总之,是有点“不爽”。宣布 Seata 开源的 Meetup 现场而这种“不爽”集中反映在了分布式事务开源中间件 Seata 上,清铭在 2019 年 1 月中间件开发者 Meetup 上宣布分布式事务中间件 Seata 正式开源后的一周内,Seata 便收获了 3000+ star,社区讨论的 issue 达 58 个。阿里巴巴是国内最早一批进行应用分布式(微服务化)改造的企业,所以很早就遇到微服务架构下的分布式事务问题。2014 年发布 TXC,开始为集团内应用提供分布式事务服务。2016 年,TXC 经过产品化改造,以 GTS 的身份上线阿里云,成为当时业界唯一一款云上提供分布式事务的商业化产品。2019 年,基于 TXC 和 GTS 的技术积累,开源了 Seata,和社区一起共建分布式事务解决方案。2022 年,经过多年的打磨,Seata 发布了 1.5.0 里程碑版本,该版本共有 61 名 contributor 贡献了近 7w+代码,同时也推出 Seata 企业版,并在微服务引擎 MSE 上提供商业化服务。企业版相比开源版内核 RT 降低 20% 以上,TPS 性能提升 30%,并且解决了高并发场景下的事务处理“毛刺”问题。TXC/GTS/Seata/MSE 一脉相承,其愿景是让分布式事务的使用像本地事务的使用一样,简单和高效。从分布式应用架构到分布式应用治理治理不仅是架构的延续,更是下一代应用中间件技术的演进方向。分布式应用架构的需求包括 RPC 框架、消息队列、服务发现、配置中心、网关、分布式事务等,解决的是分布式应用落地的问题,但随着服务数量的增加、服务之间的依赖更加复杂,分布式应用治理成为更加迫切的需求,包括流量治理、开发测试治理、数据库治理、混沌工程、多活,解决的是用好、管好分布式应用的问题。但显然,仅仅是 Sentinel、Chaoblade 还无法满足开发者对于用好、管好分布式应用的开源诉求,于是阿里云再一次开源了两款面向分布式应用治理领域的项目,Appactive 和 OpenSergo。在 2022 年 1 月的云原生实战峰会上,云原生应用平台负责人叔同宣布应用多活 Appactive 正式开源。由此,Sentinel、Chaosblade 和 AppActive 形成了高可用的三架马车,帮助企业构建稳定可靠的企业级生产系统,提高企业面对容灾、容错、容量管控等的稳态系统建设能力。2013 年,当时淘宝完成去 O 没多久,双十一的规模较上年进一步飞增。阿里的工程师正面临以下两大难题,一方面是机房资源非常紧张,容量不足,另一方面是杭州出现罕见的高温天气,机房面临断电的风险,异地多活架构就是在这个背景下孵化出来的。后来,随着淘宝的业务规模演进,异地多活也从近距离同城双机房到远距离异地双活,再到三地四单元、多地多活,沉淀了丰富的机房级应用多活经验。AppActive 的开源,一是希望给多活提供一个统一的规范和定义,在这个基础上,大家才能共享成熟的实践经验,以避免因认知偏差带来的企业在基础设施成本、应用改造成本、运维成本等成本面的投入,从而让“多活”成为一项事实意义的普惠技术;二是基于标准,提供各个层次多活能力的实现。在应用多活的规范中,定义了 LRA(同城多活)、UDA(异地多活)、HCA(混合云多活)和 BFA(业务流量多活)4 层能力。在 AppActive 发布的第一个版本里,提供了 BFA 和 UDA 的基础能力,BFA 和 UDA 的加强能力、LRA 和 HCA 的能力将成为后续的演进方向。借助以上能力,企业可以实现:分钟级 RTO:恢复时间快,阿里内部生产级别恢复时间平均在 30s 以内,外部客户生产系统恢复时间平均在 1 分钟。资源充分利用:资源不存在闲置的问题,多机房多资源充分利用,避免资源浪费。切换成功率高:依托于成熟的多活技术架构和可视化运维平台,相较于现有容灾架构,切换成功率高,阿里内部年切流数千次的成功率高达 99.9% 以上。流量精准控制:应用多活支持流量自顶到底封闭,依托精准引流能力将特定业务流量打入对应机房,企业可基于此优势能力孵化全域灰度、重点流量保障等特性。分布式应用治理领域的开源,不仅是能力的开源,更重要的是规范和定义的统一,AppActive 如此,OpenSergo 亦是。在阿里云首届中间件开发者大会的会前问卷中,采用多种微服务框架或 RPC 框架混用的开发者比例达 24%。“语言和服务框架的异构会使得服务治理的成本呈现指数级的增加,一是因为每个开源框架和协议针对微服务治理的定义概念和能力都不一致,二是大家的治理模型和治理规则也是不同的,三是多云趋势下,不同云厂商提供的服务治理相关的 PaaS 服务(例如阿里云的 Serverless 应用引擎 SAE)也不同,这就会使得开发者无论是在认知还是技术迭代上都会存在非常大的限制。”OpenSergo 创始人望陶在接受 InfoQ 的采访时提到。2022 年 4 月,OpenSergo 对外开源,该项目由阿里云、bilibili、字节,以及 Spring Cloud Alibaba、Nacos、Apache Dubbo、Sentinel、Sofa 社区共同维护,旨在构建一个和语言无关、和技术形态无关,但贴近业务的统一服务治理规范和实现。他的定位和能力就像项目的命名一样,Open 是开放的意思,Sergo 则是取了服务治理两个英文单词 Service Governance 的前部分字母 Ser 和 Go,合起来即是一个开放的服务治理项目。OpenSergo 包含了以下三部分内容:控制面:开发者可以通过 CRD 或者 Dashboard 的方式查看、修改服务治理配置,并将这些管控信息下发到数据面,从而 统一了服务治理的规则,开发者不必再绑定到某个开源方案、某个云厂商提供的服务上。数据面:JavaAgent、Servcie Mesh、各个接入 OpenSergo 的微服务框架都能够接收到服务治理配置,并应用到当前的业务流量中。各个数据面都能够认可 OpenSergo 规定的服务治理配置、流量标签等信息,确保降低开发者理解成本。OpenSergo Spec:Spec 规定了控制面和数据面的通信约定,确保用户使用一种 Spec 即可描述不同框架、不同协议、不同语言的微服务架构,让开发者不再需要关注底层差异。在此基础之上,再逐步将全链路灰度、无损上下线、流量打标等能力融合进来,提供一套完整的服务治理规范和实现的方案。至此,10 个开源项目,覆盖架构到治理,将阿里云在应用中间件领域沉淀的技术倾囊而出。始于架构,精于治理。他们既是独立运行的开源项目,开发者可以搭配其他开源组件形成一套自己的开源技术栈,也是一套完整的分布式应用的开源解决方案,同时使用多个开源项目实现应用的快速交付。开源的故事并没有就此结束,云原生对中间件游戏规则的重塑仍在持续。应用中间件的开源范畴已随容器和 Serverless 技术的普及升级到了应用云原生,他们和 Koordinator、KubeVela、OpenYurt、sealer、OpenKurise、Serverless Devs 等共同构成了阿里云在应用云原生领域的开源全景图。阿里云首届中间件开发者大会举办在即,点击此处,报名参会获取国内中间件开源采用现状调研简报。
文章
消息中间件  ·  Dubbo  ·  Java  ·  中间件  ·  应用服务中间件  ·  Nacos  ·  开发者  ·  Sentinel  ·  微服务  ·  Spring
2022-07-06
技术分享 | 常见接口协议解析
服务与服务之间传递数据包,往往会因为不同的应用场景,使用不同的通讯协议进行传递。比如网站的访问,常常会使用 HTTP 协议进行传递,文件传输使用 FTP,邮件传递使用 SMTP。上述的三种类型的协议都处于网络模型中的应用层。除了应用层的常用协议之外,对于传输层的 TCP、UDP 协议,以及 Restful 架构风格、RPC 协议等等基础网络知识要有一定的了解和认知。网络协议介绍在了解具体的协议之前,需要先了解 OSI 七层模型、TCP/IP 四层模型、五层体系结构这三种不同的网络模型。网络协议模型对比图:OSI 参考模型是一个在制定协调进程间通信标准时所使用的概念性框架,它并不是一个标准。TCP/IP 四层模型是网际网络的基础通信架构。常视为是简化的七层 OSI 模型。五层协议是 OSI 和 TCP/IP 的综合,实际应用还是 TCP/IP 的四层结构。TCP/IP 协议栈是对应 TCP/IP 四层模型所使用的具体的网络协议。TCP协议TCP 协议是在传输层中,一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 协议的工作方式为在建立连接的时候需要进行“三次握手”,终止连接时需要进行“四次挥手”。“三次握手”和“四次挥手”是 TCP 协议的重要知识点,在后面的章节会通过实战和理论结合具体介绍。适用场景TCP 协议的面向连接、错误重传、拥塞控制等特性,适用于可靠性高的场景,比如涉及用户信息的数据传输。UDP协议UDP 协议一旦把应用程序发给网络层的数据发送出去,就不保留数据备份。它仅在 IP 数据包的头部加入复用和数据校验字段。所以 UDP 常常被认为是不可靠的数据包协议。适用场景UDP 协议的不需要提前建立连接、实现简单的特性,非常适用于实时性高的场景,比如流媒体、在线游戏等。HTTP协议HTTP 协议是接口测试中最常见的协议,是用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。客户端向服务端发送 HTTP 请求,服务端则会在响应中返回所请求的数据。在测试过程中,常常需要校验请求和响应结果,所以了解 HTTP 协议,对于接口测试来说,是重中之重。在后面章节将会具体介绍 HTTP、HTTPS 协议的区别,以及 HTTP 协议的基础知识信息。RESTful协议起源Roy Thomas Fielding 博士于 2000 年在他的博士论文中提出来的一种万维网软件架构风格。其目的是为了便于不同的软件在网络中传递信息。RESTful 是基于 HTTP 协议之上制定的一种资源请求、操作的风格,用一句话来概括就是使用 URL 去定位资源,使用 HTTP 动词描述操作。HTTP 请求方法在 RESTful api 中的典型应用:方法意义GET获取资源POST新增或者更新PUT更新资源DELETE删除资源注意: 不同公司在使用 RESTful 架构风格的时候存在部分差别。RPC协议RPC 的英文为 Remote Procedure Call ,它很好的诠释了 RPC 协议的概念,即为以本地代码调用的方式实现远程执行,RPC 主要用于公司内部的服务调用。RPC 接口的优点在于传输效率更高、性能损耗更低、自带负载均衡策略、更好的服务治理能力。常见的 RPC 协议目前在行业内常用的 RPC 协议主要如下:Dubbo:Java 基础之上的高性能 RPC 协议。gRPC:高性能通用 RPC 框架,基于 Protocol Buffers。PB 是一个语言中立、平台中立的数据序列化框架。Thrift:与 gRPC 类似的多语言 RPC 框架。
文章
自然语言处理  ·  负载均衡  ·  网络协议  ·  Dubbo  ·  Java  ·  应用服务中间件  ·  网络性能优化  ·  API  ·  网络架构
2022-07-05
dubbo有没有默认的健康检查配置, 和SAE联动的那种, 配置一哈, 就能用的那种
dubbo有没有默认的健康检查配置, 和SAE联动的那种, 配置一哈, 就能用的那种
问答
Dubbo  ·  应用服务中间件
2022-07-05
微服务引擎 MSE 产品能力图
文章
Cloud Native  ·  Dubbo  ·  Java  ·  应用服务中间件  ·  Nacos  ·  Sentinel  ·  微服务  ·  Spring
2022-07-05
1 2 3 4 5 6 7 8 9
...
20
跳转至:
阿里云云原生
7303 人关注 | 3949 讨论 | 1647 内容
+ 订阅
  • RocketMQ 消息集成:多类型业务消息——定时消息
  • MSE 治理中心重磅升级-流量治理、数据库治理、同 AZ 优先
  • 欢迎参加 ApacheCon Asia 2022 在线会议
查看更多 >
阿里中间件
162596 人关注 | 896 讨论 | 946 内容
+ 订阅
  • 新零售标杆 SKG 全面拥抱 Serverless,实现敏捷交付
  • Helm Chart 多环境、多集群交付实践,透视资源拓扑和差异
  • (已结束)杭州站 | 阿里云中间件开发者 Meetup 开启报名
查看更多 >
阿里云微服务引擎 MSE
7 人关注 | 0 讨论 | 49 内容
+ 订阅
  • 微服务治理热门技术揭秘:无损上线
  • 阿里云微服务引擎 MSE 2022年 7月份产品动态
  • 私有化输出的服务网格我们是这样做的
查看更多 >
Java开发者
284552 人关注 | 126 讨论 | 349 内容
+ 订阅
  • 独家下载!《〈Java开发手册(嵩山版)〉灵魂17问》
  • “Java超神季”重磅来袭,集结阿里Java技术大神送你上王者
  • 阿里巴巴Java大神正在直播中
查看更多 >
微服务
22891 人关注 | 10279 讨论 | 22902 内容
+ 订阅
  • 微服务-分布式事务
  • 深入理解 Spring Cloud Gateway 的原理
  • ECS使用体验
查看更多 >