java分布式系统开关功能设计(服务升降级)

2024-04-05 04:58

本文主要是介绍java分布式系统开关功能设计(服务升降级),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

   ​    ​首先讲一下开关的由来,例如东京在6月18日做店庆促销活动,在交易下单环节,可能需要调用A、B、C三个接口来完成,但是其实A和B是必须的,C只是附加的功能(例如在下单的时候做一下推荐),可有可无,在平时系统没有压力,容量充足的情况下,调用下没问题,但是在类似店庆之类的大促环节,系统已经满负荷了,这时候其实完全可以不去调用C接口,怎么实现这个呢?改代码?no,no,no,这样太不敏捷,此时开关诞生了,开发人员只要简单执行一下命令或者点一下页面,就可以关掉对于C接口的调用,在大促过去之后,再把开关恢复回去即可。

什么是服务降级
服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
服务降级方式:
服务接口拒绝服务:无用户特定信息,页面能访问,但是添加删除提示服务器繁忙。页面内容也可在Varnish或CDN内获取。
页面拒绝服务:页面提示由于服务繁忙此服务暂停。跳转到varnish或nginx的一个静态页面。
延迟持久化:页面访问照常,但是涉及记录变更,会提示稍晚能看到结果,将数据记录到异步队列或log,服务恢复后执行。
随机拒绝服务:服务接口随机拒绝服务,让用户重试,目前较少有人采用。因为用户体验不佳。

服务降级埋点的地方:

消息中间件:所有API调用可以使用消息中间件进行控制
前端页面:指定网址不可访问(NGINX+LUA)
底层数据驱动:拒绝所有增删改动作,只允许查询



    ​    ​问题一:在单个java系统中如何实现开关功能?

    ​    ​其实对于开关来说,对应Java中的类型,很好映射,就是一个boolean值,在需要做开关操作的地方,调用这个属性,判断状态,然后走相应的逻辑即可。这个类是一个单例,保证全局唯一(代码就不写了,单例模式一般是学习设计模式中最开始接触的呵呵)。

 

    ​    ​问题二:单个java系统中,如何实现开关值变更的操作呢?

    ​    ​在单机系统中,改变开关的状态很简单(留一个口子,外部可以改变属性的值,例如改为true或者false),这时候,可以是页面来维护开关,通过页面的点击类改变这个全局唯一的属性,从而实现开关动作的触发。

 

    ​    ​问题三:多个同构java系统,如何实现开关状态的同步呢?

    ​    ​通过一和二的介绍,在单机情况下,开关的变更可以了,但是在多个同构(这里的同构,值得是部署的同一套代码,逻辑完全相同,类似Master和Slaver的模式)系统中,如何保持一致呢?单例模式,开关属性是被加载到本地缓存,就是说java一直持有的对象,在FullGC的时候回收不走的那种。这个时候,如果要保持各个系统中开关属性状态的一致,就需要从第三方外部系统中加载这个数据。

    ​    ​什么系统能充当第三方外部系统呢?可以是一个数据库访问系统,我们暂且称之为MetaServer,开关的属性防止在DB中,然后MetaServer提供页面来修改数据,同时提供接口读取开关的数据,在应用启动的时候,通过MetaServer来读取数据,加载到本地缓存中。这时候就有个问题,就是我通过MetaServer的页面改变了值,各个应用如何知道我改变了属性呢?这个时候就需要通过一些办法(办法很多,可以是消息系统,可以是zookeeper,可以是页面触发)来清理一下开关属性的缓存,让缓存重新加载一下,从而实现最新的状态获取。

    ​    ​总体思路就是:metaServer维护开关数据--应用读取DB中的数据到本地缓存--DB中数据变更--触发开关属性缓存重新加载。

    ​    ​这个是不是有点复杂,有没有更加简单的办法?当然有了,之前淘宝开源了一个系统diamond(持久化配置管理系统,http://code.taobao.org/p/diamond/wiki/index/),其实可以理解为“配置信息的伪推送服务”,例如我变更了一个开关的属性,不再需要做清理缓存的事情,diamond帮你做掉了(原理很简单,例如系统A订阅了在diamond中的开关信息,这时候A会启动一个线程,每隔一段时间来轮循diamond的服务端,看看开关属性的数据有没有变更,如果有变更,在diamond服务端来加载最新的数据)。

    ​    ​总体思路是:在diamond中维护配置信息--系统订阅开关属性--系统轮循配置是否有变更,有变更直接就变掉了。

 

    ​    ​问题四:开关设计的几个坑

    ​    ​有时候,我们为了方便,没有借助问题三种的MetaServer或者diamond的方式,就是留了一个HTTP的接口来触发修改开关(多台机器的话,可以写批量脚本),这时候其实需要我们在apache或者nginx中,把这个URL的访问禁止掉,防止恶意用户在外部拼凑链接来进行开关的变动,这时候只能在服务器上通过linux的curl来触发操作了。

    ​    ​还有一个,就是如果通过HTTP的形式来修改开关的属性,有个是需要注意的,就是开关的执行要幂等操作,这样方便操作,避免出现集群中数据不一致的状态(就是执行开,开关就是开,不能第一次执行是开,第二次执行是关)。

 

    ​    ​问题五:开关组合情况下怎么搞?

    ​    ​上面的几种情况,仅仅是执行单个开关,应该比较简单。但是我同时又A、B、C三个开关,在不同的业务场景下,可能需要关闭A和B开关,在另外一个场景下,可能需要关闭A和C开关,这时候认为操作有可能会有遗漏或者疏忽,怎么搞呢?在单独属性开关的基础上做封装,例如A和B上面增加一层属性,暂且叫“AB”,修改AB的值,对应的系统修改A和B的值,这样就避免人肉记住一些组合。

 

    ​    ​问题六:如何实现自动升降级?

    ​    ​上面的情况,都是在提起可以预知的情况下,我们做一些人为的操作,这个能不能自动化?当然可以,就是这一小节讨论的自动升降级。

    ​    ​举例子,现在东京和作的外部物流公司有多家,会调用它们的系统或者物流节点的状态,这个时候,物流公司系统是不稳定的,如果挂了或者响应时间慢了,对于自身的系统会影响比较大,比较理想的办法是,在物流公司系统出现问题的时候,这块逻辑自动降级处理,然后等物流公司系统好了之后,再把这部分逻辑自动升级,整个过程没有人为参与,自动保持系统稳定性。这里说一下总体思路:

    ​    ​第一步:搞一个计数器,记录接口,暂定A的调用成功次数、失败次数以及响应时间;

    ​    ​第二步:将这些信息放入队列中,同时设置阀值(例如RT超过5秒就降级,1秒就升级)以及阀值触发改动的开关;

    ​    ​第三部:异步启动一个线程,扫描队列,达到我们的条件,就触发做变更(有个问题,就是加入业务降级了,这时候就没有调用量,也就没有了自动升级的条件了,怎么搞呢?这时候业务降级,并不是完全100%的停掉,可以预留一部分流量继续调用A,把A调用的信息放入队列中,根据这些信息,就能实现升级了);

 

    ​    ​总结:

    ​    ​上面这些是在陆续的系统维护中尝试或者看到的处理办法,通过开关的方式,实现系统的升降级,从而更好的保护系统。这篇文章只是阐述了大体的思路,没有涉及到具体的代码,希望能够达到抛砖引玉的作用。


这篇关于java分布式系统开关功能设计(服务升降级)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/877673

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis