Zeroc
文章目录
一个很老的微服务框架,声称性能比 Dubbo 还高 10 倍,实际使用就那样,大家就没必要入坑了,就当扩展扩展视野吧!
Dubbo3 他不香么?
基础架构
内部逻辑结构
Zeroc 框架提供的基础框架
入门栗子
Slice 语言
// TODO
配置
// TODO
特性
// TODO
组件概览
Communicator 组件
Ice::Communicator
是 ICE 运行时的入口,看名字就知道是一个通讯 channal 含:
-
Client-side thread pool: 提供双向连接,异步方法调用(AMI)的响应,避免回调中死锁
-
Server-side thread pool: 接受传入的连接并处理来自客户端的请求
-
Configuration properties: Ice 运行时属性配置
-
Object factories:Ice 运行时的分布式对象(服务)实例化工厂,客户端通过 slice 语言映射过来的
-
Logger object:实现
Ice::Logger
-
Default router:实现
Ice::Router
被 Glacier2 组件使用 -
Default locator:对象标识解析为代理的对象,由 location service 实现
-
Plug-in manager:实现
Ice::PluginManager
提供对通信器的插件集的访问,栗如: IceSSL -
Object adapters:分发传入的请求,并将每个请求传递给正确的服务器
不同 Communicator 内的 adapters 和 服务完全独立
单个 Communicator 的线程池满了不影响其他 Communicator
解决不同的 Communicator 之间调用的性能问题(也就是说:有多少个服务就得有 2 的 m* n 个 Communicator,注定干不了大事)
IceBox 为它加载的每个 Ice 服务使用一个单独的通信器,以确保不同的服务不会互相干扰。多个通信器对于避免线程饥饿也很有用: 如果一个服务用完了线程,那么其余的服务就不会受到影响
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o34fUzeo-1648118762954)(file:///D:/daily/202203/ICE学习/assets/Communicator.jpg?msec=1648118757461)]
Communicator 初始化
可以在 communicator 创建时指定,之后不能修改:
-
property
-
logger
-
instrumentation
-
thread notification hook
-
dispatcher
-
ID 解析器
-
class loader
-
request interceptor
Object Identity
Identity | 属性 | 类型 |
---|---|---|
name | string | |
category | string |
Factory/File
表示:{category:Factory,name:File}
Ice.Util.identityToString
Ice.Util.stringToIdentity
Plugin
Plugin | 方法 | 返回 |
---|---|---|
initialize | void | |
destroy | void |
Ice 插件的生命周期
-
Construction 实例化插件
-
Initialization
-
Active
-
Destruction
实现 PluginFactor
来定义插件
package Ice;
public interface PluginFactory {
Plugin create(Communicator communicator, String name, String[] args);
}
通过如下配置来新增插件
Ice.Plugin.Name=entry_point [arg ...]
Ice.Plugin.name.java=... # Java plug-in
Ice.Plugin.MyPlugin=entry_point --load "C:\Data Files\config.dat"
插件启动顺序
Ice.Plugin.IceSSL=IceSSL:createIceSSL
Ice.Plugin.MyPlugin=MyPlugin:createMyPlugin
Ice.PluginLoadOrder=IceSSL, MyPlugin
PluginManager
module Ice {
local interface PluginManager {
void initializePlugins();
Plugin getPlugin(string name);
void addPlugin(string name, Plugin pi);
};
};
Server 组件
对象适配器
communicator 包含一个或多个 object adapters,沟通 Ice 运行时 和 应用程序代码
-
map 映射
Ice objects
和servants
-
dispatches 请求到
servants
中 -
管理
Ice objects
和servants
创建 和 销毁 -
提供一个或多个传输端点,没有端点时用于双向回调(相当于 Netty 的 Acceptor)
-
每个
adapters
有多个servants
和endpoints
-
只属于一个
communicator
-
每个对象适配器都有一个名称
-
每个对象适配器可以有选择地拥有自己的线程池
-
adapter-name.ThreadPool.Size
不使用communicator
的线程池 -
可以向一个或多个对象适配器注册相同的服务
Servant Map
每个 adapters
维护一个称为活动 servants
映射 (简称 ASM)
-
值是对服务器的引用
-
图中显示的是直接绑定、
-
每个
Ice object
有一个不同的servants
-
所有
servants
永久的放在内存中 -
通常服务器为每个 Ice 对象实例化一个单独的
servants
然后调用adapter
的 activate 方法启动请求 -
由于内存和启动速度问题,引入了
default servant
和servant locator
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z8pUI1rX-1648118792314)(file:///D:/daily/202203/ICE学习/assets/3.png?msec=1648117914880)]
ObjectAdapter
ObjectAdapter
通过内置的行为 Communicator
来创建
module Ice {
local interface ObjectAdapter {
string getName();
Communicator getCommunicator();
// ...
};
local interface Communicator {
ObjectAdapter createObjectAdapter(string name);
ObjectAdapter createObjectAdapterWithEndpoints(string name, string endpoints);
ObjectAdapter createObjectAdapterWithRouter(string name, Router* rtr);
// ...
};
};
ObjectAdapter
通常需要endpoints
和router
代理
Servant 激活和取消激活
Servant
指向 Ice 运行时中 Ice Object
,激活 servant
将注册到 (ASM) 中
-
servants 连接 编程语言中处理逻辑 和 Ice Identity
-
用于将请求分派给正确的
servant
-
激活的
servants
被称为Ice objects
-
停用服务将删除 ASM 中特定条目
module Ice { local interface ObjectAdapter { // ... Object* add(Object servant, Identity id); Object* addWithUUID(Object servant); Object remove(Identity id); Object find(Identity id); Object findByProxy(Object* proxy); // ... }; };
Adapter 状态
-
Holding:任何传入请求都会被保存
-
服务器端运行时停止从相应的传输端点读取数据
-
它也不接受来自客户端的传入连接请求
-
客户端 ``TimeoutException`
-
创建之后的默认状态
-
双向适配器无此状态
-
升级而无需关闭服务器
-
-
Active:开始调度请求
-
Inactive:概念上已被销毁
-
进入非活动状态时正在执行的请求允许完成
-
但不接受新的请求
module Ice { local interface ObjectAdapter { // ... void activate(); void hold(); void waitForHold(); void deactivate(); void waitForDeactivate(); void isDeactivated(); void destroy(); // ... }; };
-
Adapter Endpoints
-
物理端点(physical endpoints):新连接
-
发布端点(published endpoints):client communicate
物理端点
-
接收来自客户端的请求
-
createObjectAdapterWithEndpoints() 创建
-
传输协议
-
host
-
port
-
-
-h 0.0.0.0 监听所有接口
PublishedEndpoints??
MyAdapter.PublishedEndpoints=tcp -h Sun1 -p 9999:tcp -h Sun2 -p 9999
refreshPublishedEndpoints
-
如果一台笔记本电脑网络接口可能会随着时间而改变
-
仅对 -h 0.0.0.0 生效
Adapter 超时
MyAdapter.Endpoints=tcp -p 9999 -t 5000
module Ice {
local interface ObjectAdapter {
// ...
// 获得物理端口
EndpointSeq getEndpoints();
// 获得发布端口
EndpointSeq getPublishedEndpoints();
// ...
};
};
Adapter 创建 Proxy
module Ice {
local interface ObjectAdapter {
// ...
Object* createProxy(Identity id);
Object* createDirectProxy(Identity id);
Object* createIndirectProxy(Identity id);
// ...
};
};
多 Adapte 的情况
很少需要使用多个对象适配器
-
需要另一个对象适配器,限制对安全端点的访问
-
多个对象适配器(每个对象都有自己的线程池)可以用于解决死锁
-
希望暂时禁用处理一组对象的新请求
-
需要设置不同的请求路由
Current
用于应对多服务器中独立的 servant 的情况
module Ice {
local dictionary<string, string> Context;
enum OperationMode {
Normal, Idempotent };
local struct Current {
// 对外暴露 adapter,进而暴露 communicator
ObjectAdapter adapter;
Connection con;
// 服务端 Ice object 的 id
Identity id;
string facet;
// 正在调用的操作
string operation;
// 调用模式(Idempotent、 Normal)
OperationMode mode;
// 请求上下文
Context ctx;
// 将应答与其相应的请求关联起来,单向为 0
int requestId;
// 请求参数编码
EncodingVersion encoding;
};
};
Servant locator
-
当 ASM 没有找到 servants 时, Adapter 调用 servant locator 获得 servant
-
服务器不需要为存在的每个 Ice objects 实例化一个单独的 servant
-
通常存储于数据库?
-
允许服务器只实例化那些由客户端实际使用的 servants
-
有潜在的非常大量的设备时,不会直接撑爆内存
module Ice {
local interface ServantLocator {
["UserException"]
Object locate(Current curr, out LocalObject cookie);
["UserException"]
void finished(Current curr, Object servant, LocalObject cookie);
void deactivate(string category);
};
};
-
要创建一个实际的服务定位器实现,您必须定义自己实现
-
该关联仅对单个请求有效
Servant locator 线程模型
-
Ice 运行时保证由同一个线程调用 locate
-
locate
finished
可能被并发调用
Servant locator 的注册
可以在 Adapter 中注册 ServantLocator
module Ice {
local interface ObjectAdapter {
// ...
void addServantLocator(ServantLocator locator, string category);
ServantLocator removeServantLocator(string category);
ServantLocator findServantLocator(string category);
// ...
};
};
示栗
public class MyServantLocator implements Ice.ServantLocator {
public Ice.Object locate(Ice.Current c, Ice.LocalObjectHolder cookie)
{
// Get the object identity. (We use the name member
// as the database key.
String name = c.id.name;
// Use the identity to retrieve the state
// from the database.
//
ServantDetails d;
try {
// 可以使用 IceUtil::Handle<MyCookie> MyCookiePtr;缓存
d = DB.lookup(name);
} catch (DB.error e) {
return null;
}
// We have the state, instantiate a servant and return it.
//
return new PhoneEntryI(d);
}
public void finished(Ice.Current c, Ice.Object servant, java.lang.Object cookie)
{
}
public void deactivate(String category)
{
}
}
Default Servants
ASM 中定位服务。如果没有找到服务,则对象适配器将请求分派给 Default Servants(动态映射)
-
name 通常是一种编码结构,是数据库查询中的键,而不仅仅是一个字符串
-
通常,默认服务器所服务的所有对象都必须具有相同的接口
-
需要覆盖 ice _ ping
-
考虑 interceptors 实现
module Ice {
local interface ObjectAdapter {
void addDefaultServant(Object servant, string category);
Object removeDefaultServant(string category);
Object findDefaultServant(string category);
// ...
};
};
拦截器
就是一个拦截器啊,做 web 的人都知道
Client 组件
Proxies
-
远程函数的本地代理,提供给本地使用。
-
代理还封装了标识和端点寻址细节
-
代理在必要时启动新连接
初始化
Ice::ObjectPrx p = communicator->stringToProxy("ident:tcp -p 5000");
Ice.ObjectPrx p = communicator.propertyToProxy("MyApp.Proxy");
MyApp.Proxy=ident:tcp -p 5000
MyApp.Proxy.PreferSecure=1
MyApp.Proxy.EndpointSelection=Ordered
等价于
Ice.ObjectPrx p = communicator.stringToProxy("ident:tcp -p 5000");
p = p.ice_preferSecure(true);
p = p.ice_endpointSelection(Ice.EndpointSelectionType.Ordered);
通过工厂方法初始化
Ice.ObjectPrx p = communicator.stringToProxy("...");
应用程序也可以通过 Ice 调用获得代理,不需要手动开启。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFBzfVnh-1648118826333)(file:///D:/daily/202203/ICE学习/assets/proxy.jpg?msec=1648118821061)]
Proxy Endpoints
对应服务端的 adapter endpoints
tcp -h frosty.zeroc.com -p 10000
# 使用 locator 定位
MyObject @ MyAdapter
# 没有 adapter
# 默认配置
Ice.Default.PreferSecure=1
负载均衡
支持两种类型的负载均衡,应用程序只能使用其中一种
-
使用 Locator 负责负载均衡
-
使用客户端 Proxy 负责负载均衡
Proxy Connection 缓存
# 关闭默认开启代理连接缓存
ice _ connectioncache=false
假设一个代理包含多个 endpoints。在其默认配置中,代理使用 Random 端点选择类型并缓存其连接
Ice::ObjectPrx proxy = communicator->stringToProxy("hello:tcp -h 10.0.0.1 -p 2000:tcp -h 10.0.0.2 -p 2001");
proxy = proxy->ice_connectionCached(false);
proxy = proxy->ice_endpointSelection(Ice::Random);
// If also using a locator:
proxy = proxy->ice_locatorCacheTimeout(...);
proxy 格式
identity -f facet -e encoding -p protocol -t -o -O -d -D -s @ adapter_id : endpoints
-
-e:封送参数时使用的 Ice 编码版本
-
-p:Ice 协议版本
-
-t:双向调用
-
-o:单向调用
-
O:批量单向调用
-
-d:数据报调用
-
-D:批量数据报
-
-s:安全调用
-
-t、-o、-o、-d、-D 互斥
Endpoints
x.x.x.x
tcp -h host -p port -t timeout -z --sourceAddress addr
udp -h host -p port -z --ttl TTL --interface INTF --sourceAddress addr
ssl -h host -p port -t timeout -z --sourceAddress addr
ws -r resource -h host -p port -t timeout -z --sourceAddress addr
wss -r resource -h host -p port -t timeout -z --sourceAddress addr
Request Contexts
module Ice {
// 上下文是名称-值对的集合
dictionary<string, string> Context;
};
-
服务端通过
Ice::Current
获得客户端的参数 -
以下划线开头的上下文名称保留给 Ice 使用
-
客户端可以任意传递配置
write policy
PersonPrx p = ...; Address a = ...; java.util.Map<String, String> ctx = new java.util.HashMap<String, String>(); ctx.put("write policy", "immediate"); p.setAddress(a, ctx);
-
使用配置文件配置
PersonProxy=person:tcp -p 5000 PersonProxy.Context.write policy=immediate PersonProxy.Context.failure mode=persistent
-
使用 隐式上下文
module Ice { local interface Communicator { ImplicitContext getImplicitContext(); // ... }; };
local interface ImplicitContext { Context getContext(); void setContext(Context newContext); string get(string key); string put(string key, string value); string remove(string key); bool containsKey(string key); };
-
超时配置
Ice.Default.InvocationTimeout=5000
-
自动重试
-
Ice 从不重试引发 编码或解码Exception 的调用
-
Ice 也不会重试从 RequestFailedException 派生的异常
-
Ice 会重试:用户异常,RequestFailedException,UnknownException
-
通过将某些 Slice 操作标记为 idempotent 幂等来去 at-most-once 化
interface Account { long withdraw(long amount); idempotent long getBalance(); }; };
// -1 表示禁用 Ice.RetryIntervals=0 // Ice会在重试前等待100毫秒,然后等待500毫秒,最后在等待一秒钟后再尝试一次 Ice.RetryIntervals=0 100 500 1000
-
若要监视 Ice 的重试活动
# 设置为非零值 Ice.Trace.Retry=1 # 记录更多关于重试的信息,包含连接和端点详细信息 Ice.Trace.Retry=1
-
超时重试会卡死应用 T = t * (N + 1) + D 秒,建议设置
# 最坏情况超时大约为10 * 5 + 60 = 110秒 Ice.RetryIntervals=0 10000 20000 30000
-
-
Client-Server 其他关键特性
Ice 总体线程模型
Ice 是一个多线程的平台,您必须关注并发性问题,在这里,我们讨论 Ice 的线程池并发模型
Thread Pools
介绍
-
每个 communicator 有两个线程池
-
client thread pool:处理对输出请求的响应
-
server thread pool:分发传入的请求,对于双向连接,进程回复传出的请求
-
-
默认情况下,这两个线程池是共享的
-
线程池耗尽,请求会被透明地延迟
配置线程池
每个线程池都有一个唯一的名称,作为其配置属性的前缀:
-
name.Size:初始大小
-
name.SizeMax
-
name.SizeWarn
-
name.StackSize:线程池中线程的堆栈大小的字节数,0使用操作系统默认值
-
name.Serialize:1 表示串行,默认为 0
-
可以同时分发来自不同连接的请求,同时序列化来自同一连接的请求
-
与单线程池相比可以提高响应性和性能,但额外同步会增加大量开销,导致更高的延迟和更低的吞吐量
-
一个更好的解决方案是与 AMD 一起使用序列化,将传入的请求排入队列
-
-
name.ThreadIdleTime:默认 60 秒
Dynamic Thread Pools
动态线程池可以根据应用程序工作负载的变化在必要时进行增长和收缩
-
name.SizeMax = 5
-
或 name.Size=3, name.SizeMax=5
-
或 name.Size = 3, name.ThreadIdleTime = 10
适配器线程模型(Adapter Thread Pool)
对象适配器的默认共享其通信器的线程池
除非:
-
需要一个额外的单线程池做同步
-
需要一个额外线程池,在嵌套调用时消除死锁
当设置了
adapter.ThreadPool.Size
或者adapter.ThreadPool.SizeMax
时,将创建适配器线程池
Proxy 线程模型
代理对象是完全线程安全的,这意味着客户机可以同时从多个线程调用同一个代理对象
-
新的代理最初没有连接
-
代理上建立一个新连接时,该代理上的所有调用会排队
-
代理在默认情况下共享连接,可以强制代理使用单独的连接
Ice (从版本3.6开始),代理缓存其连接时,保证维护对同一代理对象的调用的顺序
- 当存在多个代理时也适用
客户机中的 Ice 运行时通过连接发送调用的顺序并不一定决定它们在服务器中执行的顺序
嵌套调用
略