一起写RPC框架(十三)RPC服务提供端四--服务的降级

  对于RPC而言,服务的降级也是必不可少的,何为服务的降级,就是在业务洪流来的时候,服务器的压力陡增,数据库的压力也很大的时候,轻量化服务的功效,比如某个非核心服务需要调用数据库的,我们降级的服务不需要调用数据库,就比如我们在某某电商购物的时候,商品详情页的侧边栏一般会有电商推荐的一些比较类似的产品,这个后台的机制可能是某个推荐算法,根据用户浏览商品的记录给出推荐的产品,这是非核心的逻辑,这个功能在服务器的压力比较大的时候,可以进行降级的处理,我们可以给出几个默认的产品返回,因为推荐算法可能会设计大数据的计算和分析,甚至设计几次的数据库查询,在这个时候我们如果让这个后台方法默认返回几个固定的值的时候,可以减轻服务的压力,给其他的核心服务,例如支付,详情页等服务做出服务资源的让步


服务降级对于RPC来说是可以或缺的,因为对于RPC来说,远程调用是它的核心,但是服务降级对于服务治理是不可或缺的,实现服务降级的方式有很多种,本Demo RPC实现的方式比较简单,提供一个Mock方法,这个Mock方法由我们自己实现:


举例来说,对于一个简单的接口而言比如HelloService来说:

package org.laopopo.example.demo.service;

/**
 * 
 * @author BazingaLyn
 * @description 
 * @time
 * @modifytime
 */
public interface HelloSerivce {
	
	String sayHello(String str);

}
我们先给出他正常逻辑的实现:

package org.laopopo.example.demo.service;

import org.laopopo.client.annotation.RPCService;

/**
 * 
 * @author BazingaLyn
 * @description Demo
 * @time 2016年8月19日
 * @modifytime
 */
public class HelloSerivceImpl implements HelloSerivce {

	@Override
	@RPCService(responsibilityName="xiaoy",
				serviceName="LAOPOPO.TEST.SAYHELLO",
				isVIPService = false,
				isSupportDegradeService = true,
				degradeServicePath="org.laopopo.example.demo.service.HelloServiceMock",
				degradeServiceDesc="默认返回hello")
	public String sayHello(String str) {
		
		//真实逻辑可能涉及到查库
		return "hello "+ str;
		
	}

}
HelloServiceImpl.java这个类是我们一般的业务逻辑的实现,也许会设计到N次的库的查询,甚至还需要调用别人的接口,所以在业务洪流来的时候,这个服务占据的资源过多,但这个服务也非核心的,这时最好的方法就是降级,而不是熔断,服务的熔断也是服务治理的一个手段,我们言归正传,降级的方法就是我们写一个mock方法:

package org.laopopo.example.demo.service;

public class HelloServiceMock implements HelloSerivce {

	@Override
	public String sayHello(String str) {
		
		//直接给出默认的返回值
		return "hello";
	}

}

Mock方法就变得简单的多,这样就是一个服务降级的比较简单的实现,接下来就是处理如何实现服务切换的功能了


当服务器压力不大的时候,我们一般调用HelloServiceImpl的方法,当服务器的压力大的时候,或者服务调用的成功率低于某个值的时候,就开始切换服务的提供对象,由HelloServiceImpl切换到HelloServiceMock上来,关于切换,简单的又分两种,手动和自动:

1)手动切换也很好理解,比如明天双11,或者618,818之类的促销节到来的时候,提前人工把某些非核心的功能,切换到Mock方法上来

2)自动切换也很好理解,比如某些服务相对比较重要,如果直接手动降级也是比较可惜,我们可以设定假如某个服务器的服务调用成功率低于某个值的时候,就开始自动降级


我们看具体的实现:

我们在服务编织的时候,假如某个服务的Annotation上注明该服务有提供Mock类的时候,我们也进行简单的编织:

//如果是支持服务降级服务,则需要根据降级方法的路径去创建这个实例,并编制proxy
if(isSupportDegradeService){
	Class<?> degradeClass = null;
	try {
	degradeClass = Class.forName(degradeServicePath);
		Object nativeObj = degradeClass.newInstance();
		if(null  == globalProviderProxyHandler){
		        this.mockDegradeServiceProvider = nativeObj;
		}else{
			Class<?> globalProxyCls = generateProviderProxyClass(globalProviderProxyHandler, nativeObj.getClass());
			this.mockDegradeServiceProvider = copyProviderProperties(nativeObj, newInstance(globalProxyCls));
		}
	} catch (Exception e) {
		logger.error("[{}] class can not create by reflect [{}]",degradeServicePath,e.getMessage());
		throw new RpcWrapperException("degradeService path " + degradeServicePath +"create failed" ); 
        } 							
}
并将反射创建好的类与原对象一起编织成ServiceWrapper

ServiceWrapper serviceWrapper = new ServiceWrapper(serviceProvider,
						   mockDegradeServiceProvider,
						  serviceName,
						  responsiblityName,
						  methodName,
						  paramters,
						  isSupportDegradeService,
						  degradeServicePath,
						  degradeServiceDesc,
						  weight,
						  connCount,
						  isVIPService,
						  maxCallCount);
//放入到一个缓存中,方便以后consumer来调取服务的时候,该来获取对应真正的编织类
providerController.getProviderContainer().registerService(serviceName, serviceWrapper);


我们在服务调用的章节中说过,我们为每一个服务都配置了一个服务的状态:

/**
<span style="white-space:pre">	</span> * 
<span style="white-space:pre">	</span> * @author BazingaLyn
<span style="white-space:pre">	</span> * @description 当前实例的服务状态
<span style="white-space:pre">	</span> * @time 2016年8月29日
<span style="white-space:pre">	</span> * @modifytime
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>public static class CurrentServiceState {
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>private AtomicBoolean hasDegrade = new AtomicBoolean(false);   // 是否已经降级
<span style="white-space:pre">		</span>private AtomicBoolean hasLimitStream = new AtomicBoolean(true); // 是否已经限流
<span style="white-space:pre">		</span>private AtomicBoolean isAutoDegrade = new AtomicBoolean(false); // 是否已经开始自动降级
<span style="white-space:pre">		</span>private Integer minSuccecssRate = 90; <span style="white-space:pre">				</span>// 服务最低的成功率,调用成功率低于多少开始自动降级


<span style="white-space:pre">		</span>public AtomicBoolean getHasDegrade() {
<span style="white-space:pre">			</span>return hasDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setHasDegrade(AtomicBoolean hasDegrade) {
<span style="white-space:pre">			</span>this.hasDegrade = hasDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public AtomicBoolean getHasLimitStream() {
<span style="white-space:pre">			</span>return hasLimitStream;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setHasLimitStream(AtomicBoolean hasLimitStream) {
<span style="white-space:pre">			</span>this.hasLimitStream = hasLimitStream;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public AtomicBoolean getIsAutoDegrade() {
<span style="white-space:pre">			</span>return isAutoDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setIsAutoDegrade(AtomicBoolean isAutoDegrade) {
<span style="white-space:pre">			</span>this.isAutoDegrade = isAutoDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public Integer getMinSuccecssRate() {
<span style="white-space:pre">			</span>return minSuccecssRate;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setMinSuccecssRate(Integer minSuccecssRate) {
<span style="white-space:pre">			</span>this.minSuccecssRate = minSuccecssRate;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>
<span style="white-space:pre">	</span>}
我们也维护了一个这样的全局变量:

private final ConcurrentMap<String, Pair<CurrentServiceState, ServiceWrapper>> serviceProviders = new ConcurrentHashMap<String, Pair<CurrentServiceState, ServiceWrapper>>();
Key是serviceName也就是服务名,Value是一个键值对,每一个服务的编织类,对应一个服务的状态


当调用的时候获取到每个服务的CurrentServiceState:

//判断服务是否已经被设定为自动降级,如果被设置为自动降级且有它自己的mock类的话,则将targetCallObj切换到mock方法上来
if(currentServiceState.getHasDegrade().get() && serviceWrapper.getMockDegradeServiceProvider() != null){
 <span style="white-space:pre">	</span>targetCallObj = serviceWrapper.getMockDegradeServiceProvider();
}

我们读取到CurrentServiceState中的是否已经降级的字段,如果是true,则将目标对象替换成Mock对象就OK了,其他的流程不变,这样就可以实现一个简单的人工降级了,我们只需要手动修改hasDegrade这个字段的值就可以完成服务的降级和恢复了


关于自动降级,其实就是在手动的基础上加一个定时轮询的检查就可以了,比如每个一分钟查询一下或者统计一下此时服务调用的成功率,如果低于某个临界值,例如成功率低于90%,则将hasDegrade字段修改成true

this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

			@Override
			public void run() {
				//检查是否有服务需要自动降级
				DefaultProvider.this.providerController.checkAutoDegrade();
			}
		}, 30, 60, TimeUnit.SECONDS);


public void checkAutoDegrade() {

		//获取到所有需要降级的服务名
		List<Pair<String, CurrentServiceState>> needDegradeServices = providerContainer.getNeedAutoDegradeService();

		//如果当前实例需要降级的服务列表不为空的情况下,循环每个列表	
		if (!needDegradeServices.isEmpty()) {
			
			for (Pair<String, CurrentServiceState> pair : needDegradeServices) {

				//服务名
				String serviceName = pair.getKey();
				//最低成功率
				Integer minSuccessRate = pair.getValue().getMinSuccecssRate();
				//调用的实际成功率
				Integer realSuccessRate = ServiceMeterManager.calcServiceSuccessRate(serviceName);
				
				if (minSuccessRate > realSuccessRate) {
					
					final Pair<CurrentServiceState, ServiceWrapper> _pair = this.defaultProvider.getProviderController().getProviderContainer()
							.lookupService(serviceName);
					CurrentServiceState currentServiceState = _pair.getKey();
					if (!currentServiceState.getHasDegrade().get()) {
						currentServiceState.getHasDegrade().set(true);
					}
				}
			}
		}
	}
这样就可以完成了一个比较简单的服务降级了



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值