如何自主实现一个简单微服务架构
Posted 深圳技术研究会
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何自主实现一个简单微服务架构相关的知识,希望对你有一定的参考价值。
---项目背景介绍---
有一个X项目,意在将散落在系统各处的某一类服务,整合为一个独立系统,为外部各系统提供统一的服务接口。
对外提供服务的方式有两种,一种是通过消息中间件,第二种是通过服务远程调用。本文将要分析的方案就是为了满足第二种方式:服务远程调用。
所谓的服务远程调用,即为本地调用一个服务,而服务本身的运算执行是在远端服务器,通过网络来传输最初参数以及最终结果。为了方便调用远程服务,通常采用一些组件来完成底层通信的封装,使得底层通信对应用层是透明的,调用远程服务就像是调用本地服务一样方便简洁。
支持服务远程调用的方式有很多,不同方式的区别主要体现在通信协议上,例如RMI、SOAP、Binary-RPC等,对于一些协议而言,是可以跨语言进行远程调用的。
传统的服务远程调用,为直接点对点交互,服务提供方,通过暴露自己的IP以及端口供调用方调用,在服务提供者数量变大以后,对于调用方而言,需要维护所有提供方的信息,服务治理容易出现混乱。同时一旦服务提供方更换了部署位置,调用方需要同步更新代码或者配置,耦合程度仍然略高。
随着微服务架构理念的提出,一个系统中需要被远程调用的服务数量急剧上升,传统的点对点服务远程调用模式,不再满足业务需求,因此就出现了微服务框架,以支持大规模的远程服务调用。
一个合格的微服务框架提供的核心功能可以总结为如下三个方面:
负载监控 + 负载均衡:负载监控可以监控所有服务提供者的被调用时长以及次数,当一个服务有多个提供者时,根据负载监控的信息,为服务调用者分配一个合理的服务提供者。负载监控如果能以UI界面展示更佳。
多通信协议支持、服务熔断、服务调用链trace等功能
简单的示意图如下
目前比较出名微服务框架为dubbo以及Spring cloud,这两个框架都满足了上述微服务架构所需要的核心功能。
X项目最初设想是直接引入dubbo框架,也展开了测试工作,并且对相关的研究成果进行了分享和文档化记录。在后期的讨论中,有一个问题引起了大家的关注:
微服务,是一种架构设计理念和模式,倾向于将应用模块化为子系统,子系统再进行分割,分割后的功能点之间依然是服务化的远程调用,即服务的粒度被切割的非常小。这和X项目当前的需求是一致的么?X项目确实需要服务远程调用,但是主要是对外提供服务,供外部的系统进行服务调用,并非自身需要设计为微服务模式。那么此时这些微服务框架对于X项目而言,是否太过于重型化了?
对上述问题进行深入讨论后,达成如下统一意见:是否考虑自主实现一个简单版本的类似微服务框架的功能,参考dubbo等框架的设计理念,自主开发一个适合我们自己使用的轻量级框架。接下来则是介绍该框架如何实现,仅为测试版本。
---框架代码示例---
采用了以下设计,实现了一个简约的测试版本微服务调用框架:采用已经存在并使用的zookeeper集群作为注册中心,用作服务注册以及服务发现,同时开发一个网页UI连接zookeeper,用以展示服务的注册情况。服务间远程调用的协议为RMI,支持负载均衡。
下文中的示例代码,仅为测试用例,用来验证技术可行性,后续可优化空间还有很大,比如服务远程调用的协议可以新增支持httprestful等跨语言的,服务熔断机制的引入,负载均衡算法的优化,负载监控的新增等。如果这些功能均自主开发,那么技术可控程度会比采用第三方框架要高,为了提高代码健壮性以及代码质量,可以参考第三方开源实现。
下面是测试代码的简单示例
代码分为四部分:
client为服务调用者,与注册中心交互,完成远程调用
frame为微服务框架代码,提供了服务注册以及发现功能、提供了负载均衡
server为服务提供者,在注册中心完成注册,将service中的服务发布,等待被调用
service中实现了服务的方法
测试代码需要引入zookeeper和log4j的jar包。
Service.java代码如下:
ServiceImp.java代码如下:
服务端Server的代码如下:
首先服务提供者Server启动,启动后会在zookeeper上注册自己,成功启动后,如下:
当有多个server启动,提供相同的服务时,则会在同一个服务名称下注册多个实例
启动客户端Client,服务调用者通过需要调用的服务名称,可以直接得到远程服务对象,然后就像调用本地代码一样,完成本次调用。
客户端Client的代码如下:
运行结果如下:
,至此完成了一次服务远程调用。
其中serviceFrame模块,则是按照如下内容实现:
public class ServiceFrame {
/*zooKeeper对象 */
private ZooKeeper zooKeeper;
/* ServiceFrame的单例对象 */
Private static ServiceFrame instance;
/*单例锁*/
Private static byte[] lock = new byte[0];
privateList<ACL>acls;
public static ServiceFramegetInstance(String zooStr,String passwd){
if(instance!=null){
returninstance;
}else{
synchronized(lock){
if(instance!=null){
returninstance;
}else{
instance = new ServiceFrame(zooStr, passwd);
returninstance;
}
}
}
}
privateServiceFrame(String zooStr,String passwd){
try {
this.zooKeeper = newZooKeeper(zooStr,60000,null);
this.zooKeeper.addAuthInfo("digest", passwd.getBytes());
Id adminId = new Id("digest",DigestAuthenticationProvider.generateDigest(passwd));
ACL acl1 = new ACL(ZooDefs.Perms.ALL,adminId);
this.acls = new ArrayList<ACL>();
this.acls.add(acl1);
} catch (Exception e) {
// TODO自动生成 catch 块
e.printStackTrace();
}
}
/*提供了在zookeeper上注册服务的功能*/
Public voidregistry(String serviceName,String url){
try {
String service_root = "/registryService";
Stat stat = zooKeeper.exists(service_root, false);
if(stat == null){
zooKeeper.create(service_root, newbyte[0], acls, CreateMode.PERSISTENT);
}
String servicePath =service_root + "/" + serviceName;
stat = zooKeeper.exists(servicePath, false);
if(stat == null){
zooKeeper.create(servicePath, newbyte[0], acls, CreateMode.PERSISTENT);
}
String serviceInstance =servicePath + "/url";
zooKeeper.create(serviceInstance, url.getBytes(), acls, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (KeeperException e) {
// TODO自动生成 catch 块
e.printStackTrace();
} catch (InterruptedException e) {
// TODO自动生成 catch 块
e.printStackTrace();
}
}
/*通过服务名称在zookeeper上查询注册信息*/
public StringgetServiceUrl(String serviceName){
byte[]result = null;
try {
String servicePath = "/registryService" + "/" + serviceName;
List<String>allService = zooKeeper.getChildren(servicePath, true);
/*最简单的随机分配负载均衡算法*/
Random rand = new Random();
int locate = rand.nextInt(allService.size());
String serviceInstance =servicePath + "/" + allService.get(locate);
result = zooKeeper.getData(serviceInstance, null, null);
} catch (KeeperException e) {
// TODO自动生成 catch 块
e.printStackTrace();
} catch (InterruptedException e) {
// TODO自动生成 catch 块
e.printStackTrace();
}
return result == null ? "" : new String(result);
}
/*通过服务名称获取注册信息后,在远端服务器获取远程对象*/
public RemotegetService(String serviceName) throwsMalformedURLException, RemoteException, NotBoundException{
String url =getServiceUrl(serviceName);
return Naming.lookup(url);
}
}
通过这种方式实现的服务调用,逻辑简单,方式简洁,能充分利用现有的系统模块和架构。并且RMI的远程调用,效率很高。并且可以较为方便的将该功能整合到现在的框架中,后续可以定制化的加入需要的功能,完全自主开发能够做到非常高掌控力度,缺陷就是重复造了轮子,且外面的轮子经历了千万用户的使用和测试,稳定性以及可靠性比自己开发会更胜一筹。
同时还做了一些关于将spring cloud中的组件精准引入,来实现接入其微服务功能的代码示例,篇幅所限,就下次讲述。
以上是关于如何自主实现一个简单微服务架构的主要内容,如果未能解决你的问题,请参考以下文章