微服务之SCF
各个微服务框架原理SCF
SCF
简介
SCF是服务通讯框架(Service Communication Framework)的缩写.
它是一种中间层服务框架.
跨平台并且具有高并发,高性能,高可靠性,并提供异步,多协议,事件驱动的特点.
架构

使用ETCD集群作为存储,key-value形式存储,保存着服务的状态信息,过期时间等.
Raft强一致性算法的具体实现, 是Etcd的核心.因此它保证了CP特性.
服务管理平台作为ETCD集群与服务的桥梁,提供者接收外部信息,维护ETCD集群里面服务信息状态的作用.
服务与服务管理平台保持着一个5s一次的心跳,服务管理平台在接收到心跳后,去etcd中更新过期时间.
数据传输

调用方通过代理,将请求信息序列化为二进制进行网络传输.
服务方启动代理接收信息,反序列化成对象信息.
服务方将处理好的数据进行序列化,返还给调用方.
scf服务端
scf-maven-plugin
版本:0.0.15
命令启动
clean install scf:run -escf命令为scf-maven-plugin依赖包提供出来的专门用来启动scf的命令。

该命令会调用SCFMaojo.execute(),在这里面主要做的是:
获取资源配置文件
<configFolder>${path}</configFolder>配置路径里面的文件。如果不配置的话,默认会读取\src\main\resources\config目录下的配置。
确定scfname,即服务名.
获取当前类加载器scfLoad.
组装部署目录
scfhome + MyPathSeparator + "service" + MyPathSeparator + "deploy" + MyPathSeparator + this.scfname;默认是如果目录存在,则清空部署目录.

通过刚才的类加载器scfLoad加载SCFLoad类,通过反射执行mainScf(…)方法.
maven-scf-load
版本:0.0.8
由上一步通过反射执行这个jar里面的内容,这个包主要是做了2件事:
copyProjectConfigAndJars上一步已经把部署目录文件清空,这一步则复制配置文件和jar包到部署的scf目录,在scf目录下生成服务的部署目录.
startService加载com.bj58.spat.scf.server.bootstrap.Main实例.从而触发scf-server的正式启动.

scf-server
版本:4.1.31
bootstrap.Main()是server端整个流程的主干类.
流程是:
loadServiceConfig(scfConfigPath)将/serviceName/目录下的/scf_config.xml文件映射成ServiceConfig实体.
设置类加载器工作空间
即在进行jar包扫描的时候会扫描的路径.
classLoader.addFolder(new String[]{rootPath + "service/deploy/" + sc.getString("scf.service.name") + "/",rootPath + "service/lib/",rootPath + "lib"});加载人为设置的需要初始化的bean
a.
<scf.init>b.扫描项目工作路径,扫描出包含有@ServiceBehavior的实现类和@ServiceContract的接口,使用javassist接口和实现类进行代理.生成代理invoker.
c.
<scf.init.afterproxy>d.
<scf.shutdown>TypeMap.InitTypeMap()找出@SCFSerializable注解标注的实体,进行序列化.
生成
Map<Class<?>, ClassItem> TypeIdMap和Map<Integer, ClassItem> IdTypeMap,可以快速根据id获取type或者根据type获取id.a.对于预定义的数据类型
对于TypeIdMap,是一系列类型和id的映射,
比如:
{boolean,ClassItem["types"=["Boolean","boolean"],"TypeId"="3"]}对boolean类型指定了一个typeid,并同时会对应到它的相关的类,比如包装类.
IdTypeMap是相对应的id和type的映射.
比如:
{3,ClassItem["types"=["Boolean","boolean"],"TypeId"="3"]}.映射表,看最后.
b.对于自定义的数据类型
typeid使用自定义生成一个hashcode,放进IdTypeMap.
设置自定义拦截器
a.<scf.filter.global.request>:请求拦截器.
b.<scf.filter.global.response>:响应拦截器.
c.<scf.filter.connection>:连接拦截器.
loadServers:启动server
主要是生成一个SocketServer的实例,com.bj58.spat.scf.server.core.communication.tcp.SocketServer
IServer serverImpl = (IServer)classLoader.loadClass(sc.getString(server + ".implement")).newInstance(); //启动server serverImpl.start()
注:
SocketServer是以netty的方式启动的:
InetSocketAddress + ServerBootstrap = channel 的方式.

使用com.bj58.spat.scf.server.core.proxy.AsyncInvokerHandle实例,进行异步代理.
在上面第三步的b步骤,已经加载了各个被注解标注的接口和实现类.AsyncInvokerHandle监听请求,然后通过其找到方法的代理对象,从而实现异步返回结果.
至此完成了server端的启动.
scf 客户端
包名:com.bj58.spat.scf.client
版本:5.0.2
注册代理

解析strUrl,得出被请求的服务名和被请求的类.
通过jdk的动态代理接口IStudentService,生成ProxyStandard代理对象.
ProxyStandard
代理了此次方法调用的请求和响应.在ProxyStandard中主要做的是:
根据配置文件,生成ServiceConfig实例.
在这个步骤,,如果添加了scfkey.key,这样会从srvmgr config server 拉取配置生成本地配置文件.如果没有添加,则会直接使用本地的配置文件.
在配置文件中,可找到所调用的服务使用的节点列表,协议等等配置.
SerializeBase
序列化.
ServiceProxy.
根据ServiceConfig的信息,发起最终的代理请求.负载均衡,从ServerPool找出访问的服务节点.
一次获取节点的过程。

每次获取节点

根据服务节点生成Csocket,进行请求.

超时策略
使用TImeWheel算法实现调用任务的过期。

内部使用数组模拟类似时钟的环形链表数据结构,每一个格子代表一个时间间隔,每个格子对应一个任务的链表,在添加过期任务时,通过过期时间和当前时间计算出任务应该在第几个格子里并计算应该是走到第几圈时触发超时。
假设图中每个格子表示 1s,则一圈代表 8s,当前是走到第 1 圈的第 2 个格子。如果任务 5s 后超时,(5+2)% 8=7,因此将任务放到第 7 个格子对应的链表中,并标记第 1 圈超时。如果任务 10s 后超时,(10+2)% 8=4,(10+2)/8=1,因此将任务放入第 4 个格子对应的链表中,并标记第 2 圈超时。
Last updated
Was this helpful?