public class FlowResponseDataWriter implements EntityWriter {
@Override
public void writeTo(FlowTokenResponseData entity, ByteBuf out) {
//剩下的数据
out.writeInt(entity.getRemainingCount());
//等待时间
out.writeInt(entity.getWaitInMs());
}
}
PingRequestDataWriter 测试数据
2.2 配置config
这个包下主要是server的一些配置,包括常量配置、动态配置等。
2.2.1 ServerTransportConfigObserver接口
源码
public interface ServerTransportConfigObserver {
/**
* Callback on server transport config (e.g. port) change.
*
* @param config new server transport config
*/
//定义了一个回调方法,用于传输配置变更时通知
void onTransportConfigChange(ServerTransportConfig config);
}
2.2.2 ServerTransportConfig
实例变量
port:端口
idleSeconds:活跃时间
2.2.3 ServerFlowConfig
服务流控规则配置及默认值,看源码 源码
public static final double DEFAULT_EXCEED_COUNT = 1.0d;
public static final double DEFAULT_MAX_OCCUPY_RATIO = 1.0d;
public static final int DEFAULT_INTERVAL_MS = 1000;
public static final int DEFAULT_SAMPLE_COUNT= 10;
public static final double DEFAULT_MAX_ALLOWED_QPS= 30000;
private final String namespace;
//超过数
private double exceedCount = DEFAULT_EXCEED_COUNT;
//最大比例
private double maxOccupyRatio = DEFAULT_MAX_OCCUPY_RATIO;
//间隔ms
private int intervalMs = DEFAULT_INTERVAL_MS;
//采样个数
private int sampleCount = DEFAULT_SAMPLE_COUNT;
//最大允许qps
private double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS;
2.2.4 ClusterServerConfigManager
集群流控配置管理器 实例变量并赋值默认值
private static boolean embedded = false;
/**
* Server global transport and scope config.
* 全局的服务端传送配置
*/
private static volatile int port = ClusterConstants.DEFAULT_CLUSTER_SERVER_PORT;
private static volatile int idleSeconds = ServerTransportConfig.DEFAULT_IDLE_SECONDS;
private static volatile Set namespaceSet = Collections.singleton(ServerConstants.DEFAULT_NAMESPACE);
/**
* Server global flow config.
* 服务端流控配置
*/
private static volatile double exceedCount = ServerFlowConfig.DEFAULT_EXCEED_COUNT;
private static volatile double maxOccupyRatio = ServerFlowConfig.DEFAULT_MAX_OCCUPY_RATIO;
private static volatile int intervalMs = ServerFlowConfig.DEFAULT_INTERVAL_MS;
private static volatile int sampleCount = ServerFlowConfig.DEFAULT_SAMPLE_COUNT;
private static volatile double maxAllowedQps = ServerFlowConfig.DEFAULT_MAX_ALLOWED_QPS;
动态配置初始化
/**
* Namespace-specific flow config for token server.
* Format: (namespace, config).
*/
//token server的命名配置
private static final Map NAMESPACE_CONF = new ConcurrentHashMap<>();
//服务端Transport观察者
private static final List TRANSPORT_CONFIG_OBSERVERS = new ArrayList<>();
/**
* Property for cluster server global transport configuration.
*/
//传输动态配置
private static SentinelProperty transportConfigProperty = new DynamicSentinelProperty<>();
/**
* Property for cluster server namespace set.
*/
//namespace动态配置
private static SentinelProperty> namespaceSetProperty = new DynamicSentinelProperty<>();
/**
* Property for cluster server global flow control configuration.
*/
//流控规则动态配置
private static SentinelProperty globalFlowProperty = new DynamicSentinelProperty<>();
//配置监听者
private static final PropertyListener TRANSPORT_PROPERTY_LISTENER
= new ServerGlobalTransportPropertyListener();
private static final PropertyListener GLOBAL_FLOW_PROPERTY_LISTENER
= new ServerGlobalFlowPropertyListener();
private static final PropertyListener> NAMESPACE_SET_PROPERTY_LISTENER
= new ServerNamespaceSetPropertyListener();
//启动时加载,增加监听器
static {
transportConfigProperty.addListener(TRANSPORT_PROPERTY_LISTENER);
globalFlowProperty.addListener(GLOBAL_FLOW_PROPERTY_LISTENER);
namespaceSetProperty.addListener(NAMESPACE_SET_PROPERTY_LISTENER);
}
//省略部分代码
//增加传送变更配置变更者
public static void addTransportConfigChangeObserver(ServerTransportConfigObserver observer) {
AssertUtil.notNull(observer, "observer cannot be null");
TRANSPORT_CONFIG_OBSERVERS.add(observer);
}
监听器内部类
//namespace 监听器
private static class ServerNamespaceSetPropertyListener implements PropertyListener> {
@Override
public synchronized void configLoad(Set set) {
if (set == null || set.isEmpty()) {
RecordLog.warn("[ClusterServerConfigManager] WARN: empty initial server namespace set");
return;
}
//更新
applyNamespaceSetChange(set);
}
@Override
public synchronized void configUpdate(Set set) {
// TODO: should debounce?
applyNamespaceSetChange(set);
}
}
private static void applyNamespaceSetChange(Set newSet) {
if (newSet == null) {
return;
}
RecordLog.info("[ClusterServerConfigManager] Server namespace set will be update to: " + newSet);
if (newSet.isEmpty()) {
ClusterServerConfigManager.namespaceSet = Collections.singleton(ServerConstants.DEFAULT_NAMESPACE);
return;
}
newSet = new HashSet<>(newSet);
// Always add the `default` namespace to the namespace set.
newSet.add(ServerConstants.DEFAULT_NAMESPACE);
if (embedded) {
// In embedded server mode, the server itself is also a part of service,
// so it should be added to namespace set.
// By default, the added namespace is the appName.
//嵌入模式也需要增加
newSet.add(ConfigSupplierRegistry.getNamespaceSupplier().get());
}
//更新
Set oldSet = ClusterServerConfigManager.namespaceSet;
if (oldSet != null && !oldSet.isEmpty()) {
for (String ns : oldSet) {
// Remove the cluster rule property for deprecated namespace set.
if (!newSet.contains(ns)) {
ClusterFlowRuleManager.removeProperty(ns);
ClusterParamFlowRuleManager.removeProperty(ns);
}
}
}
ClusterServerConfigManager.namespaceSet = newSet;
//注册规则属性,在后面会讲解
for (String ns : newSet) {
// Register the rule property if needed.
ClusterFlowRuleManager.registerPropertyIfAbsent(ns);
ClusterParamFlowRuleManager.registerPropertyIfAbsent(ns);
// Initialize the global QPS limiter for the namespace.
GlobalRequestLimiter.initIfAbsent(ns);
}
}
//globaTransport监听器
private static class ServerGlobalTransportPropertyListener implements PropertyListener {
@Override
public void configLoad(ServerTransportConfig config) {
if (config == null) {
RecordLog.warn("[ClusterServerConfigManager] Empty initial server transport config");
return;
}
applyConfig(config);
}
@Override
public void configUpdate(ServerTransportConfig config) {
//应用配置
applyConfig(config);
}
private synchronized void applyConfig(ServerTransportConfig config) {
//校验配置
if (!isValidTransportConfig(config)) {
RecordLog.warn(
"[ClusterServerConfigManager] Invalid cluster server transport config, ignoring: " + config);
return;
}
RecordLog.info("[ClusterServerConfigManager] Updating new server transport config: " + config);
if (config.getIdleSeconds() != idleSeconds) {
idleSeconds = config.getIdleSeconds();
}
//更新server的token
updateTokenServer(config);
}
}
private static void updateTokenServer(ServerTransportConfig config) {
int newPort = config.getPort();
AssertUtil.isTrue(newPort > 0, "token server port should be valid (positive)");
if (newPort == port) {
return;
}
ClusterServerConfigManager.port = newPort;
for (ServerTransportConfigObserver observer : TRANSPORT_CONFIG_OBSERVERS) {
//更新,通过观察者模式更新
observer.onTransportConfigChange(config);
}
}
//globalFlow流控监听器
private static class ServerGlobalFlowPropertyListener implements PropertyListener {
@Override
public void configUpdate(ServerFlowConfig config) {
//更新
applyGlobalFlowConfig(config);
}
@Override
public void configLoad(ServerFlowConfig config) {
applyGlobalFlowConfig(config);
}
private synchronized void applyGlobalFlowConfig(ServerFlowConfig config) {
//校验规则
if (!isValidFlowConfig(config)) {
RecordLog.warn(
"[ClusterServerConfigManager] Invalid cluster server global flow config, ignoring: " + config);
return;
}
RecordLog.info("[ClusterServerConfigManager] Updating new server global flow config: " + config);
//判断有没有更新
if (config.getExceedCount() != exceedCount) {
exceedCount = config.getExceedCount();
}
if (config.getMaxOccupyRatio() != maxOccupyRatio) {
maxOccupyRatio = config.getMaxOccupyRatio();
}
if (config.getMaxAllowedQps() != maxAllowedQps) {
maxAllowedQps = config.getMaxAllowedQps();
//调用GlobalRequestLimiter设置qps变更
GlobalRequestLimiter.applyMaxQpsChange(maxAllowedQps);
}
int newIntervalMs = config.getIntervalMs();
int newSampleCount = config.getSampleCount();
if (newIntervalMs != intervalMs || newSampleCount != sampleCount) {
if (newIntervalMs <= 0 || newSampleCount <= 0 || newIntervalMs % newSampleCount != 0) {
RecordLog.warn("[ClusterServerConfigManager] Ignoring invalid flow interval or sample count");
} else {
intervalMs = newIntervalMs;
sampleCount = newSampleCount;
// Reset all the metrics.
//重置统计指标
ClusterMetricStatistics.resetFlowMetrics();
ClusterParamMetricStatistics.resetFlowMetrics();
}
}
}
}
public static boolean isValidTransportConfig(ServerTransportConfig config) {
return config != null && config.getPort() > 0 && config.getPort() <= 65535;
}
public static boolean isValidFlowConfig(ServerFlowConfig config) {
return config != null && config.getMaxOccupyRatio() >= 0 && config.getExceedCount() >= 0
&& config.getMaxAllowedQps() >= 0
&& FlowRuleUtil.isWindowConfigValid(config.getSampleCount(), config.getIntervalMs());
}
2.3 连接器connection
这个包下主要就是集群链接器、连接器组的管理。
2.3.1 Connection接口
源码
public interface Connection extends AutoCloseable {
//获取SocketAddress
SocketAddress getLocalAddress();
//port
int getRemotePort();
//ip
String getRemoteIP();
//刷新readTime
void refreshLastReadTime(long lastReadTime);
//获取lastReadTime
long getLastReadTime();
//获取链接的key
String getConnectionKey();
}
public class ConnectionGroup {
private final String namespace;
private final Set connectionSet = Collections.synchronizedSet(new HashSet());
private final AtomicInteger connectedCount = new AtomicInteger();
//带namespace构造函数
public ConnectionGroup(String namespace) {
AssertUtil.notEmpty(namespace, "namespace cannot be empty");
this.namespace = namespace;
}
//无参构造函数
public ConnectionGroup() {
this(ServerConstants.DEFAULT_NAMESPACE);
}
//增加连接
public ConnectionGroup addConnection(String address) {
AssertUtil.notEmpty(address, "address cannot be empty");
//获取host,若是ip:port形式,就只需要ip
String[] ip = address.split(":");
String host;
if (ip != null && ip.length >= 1) {
host = ip[0];
} else {
host = address;
}
//已经重写的equals、hashCode方法
boolean newAdded = connectionSet.add(new ConnectionDescriptor().setAddress(address).setHost(host));
//增加成功,连接数加1
if (newAdded) {
connectedCount.incrementAndGet();
}
return this;
}
//移除连接
public ConnectionGroup removeConnection(String address) {
AssertUtil.notEmpty(address, "address cannot be empty");
if (connectionSet.remove(new ConnectionDescriptor().setAddress(address))) {
connectedCount.decrementAndGet();
}
return this;
}
}
2.3.6 连接管理ConnectionManager
顾名思义ConnectionManager是对连接的管 实例变量
CONN_MAP:Connection map (namespace, connection).
NAMESPACE_MAP:namespace map (address, namespace).
源码分析
public final class ConnectionManager {
/**
* Connection map (namespace, connection).
*/
private static final Map CONN_MAP = new ConcurrentHashMap<>();
/**
* namespace map (address, namespace).
*/
private static final Map NAMESPACE_MAP = new ConcurrentHashMap<>();
/**
* Get connected count for specific namespace.
*
* @param namespace namespace to check
* @return connected count for specific namespace
*/
//获取namespace的连接数
public static int getConnectedCount(String namespace) {
AssertUtil.notEmpty(namespace, "namespace should not be empty");
ConnectionGroup group = CONN_MAP.get(namespace);
return group == null ? 0 : group.getConnectedCount();
}
//查询获取创建连接组,注意:连接没有创建
public static ConnectionGroup getOrCreateGroup(String namespace) {
AssertUtil.assertNotBlank(namespace, "namespace should not be empty");
ConnectionGroup group = CONN_MAP.get(namespace);
if (group == null) {
//synchronized锁住,防止并发问题
synchronized (CREATE_LOCK) {
if ((group = CONN_MAP.get(namespace)) == null) {
//创建并保存在CONN_MAP方便获取
group = new ConnectionGroup(namespace);
CONN_MAP.put(namespace, group);
}
}
}
return group;
}
//移除连接
public static void removeConnection(String address) {
AssertUtil.assertNotBlank(address, "address should not be empty");
String namespace = NAMESPACE_MAP.get(address);
if (namespace != null) {
ConnectionGroup group = CONN_MAP.get(namespace);
if (group == null) {
return;
}
//调用ConnectionGroup的方法,上面讲解过,其实就是移除保存在set里面的方法,
group.removeConnection(address);
RecordLog.info("[ConnectionManager] Client <{0}> disconnected and removed from namespace <{1}>", address, namespace);
}
//map中移除
NAMESPACE_MAP.remove(address);
}
//传入两个参数移除,少走一步获取namespace
public static void removeConnection(String namespace, String address) {
AssertUtil.assertNotBlank(namespace, "namespace should not be empty");
AssertUtil.assertNotBlank(address, "address should not be empty");
ConnectionGroup group = CONN_MAP.get(namespace);
if (group == null) {
return;
}
group.removeConnection(address);
NAMESPACE_MAP.remove(address);
RecordLog.info("[ConnectionManager] Client <{0}> disconnected and removed from namespace <{1}>", address, namespace);
}
//增加连接,有address参数,需要把address增加到NAMESPACE_MAP map中,并增加连接次数
public static ConnectionGroup addConnection(String namespace, String address) {
AssertUtil.assertNotBlank(namespace, "namespace should not be empty");
AssertUtil.assertNotBlank(address, "address should not be empty");
ConnectionGroup group = getOrCreateGroup(namespace);
group.addConnection(address);
NAMESPACE_MAP.put(address, namespace);
RecordLog.info("[ConnectionManager] Client <{0}> registered with namespace <{1}>", address, namespace);
return group;
}
//增加连接
public static ConnectionGroup getOrCreateConnectionGroup(String namespace) {
AssertUtil.assertNotBlank(namespace, "namespace should not be empty");
ConnectionGroup group = getOrCreateGroup(namespace);
return group;
}
// 拿到连接组
public static ConnectionGroup getConnectionGroup(String namespace) {
AssertUtil.assertNotBlank(namespace, "namespace should not be empty");
ConnectionGroup group = CONN_MAP.get(namespace);
return group;
}
static void clear() {
CONN_MAP.clear();
NAMESPACE_MAP.clear();
}
private static final Object CREATE_LOCK = new Object();
private ConnectionManager() {}
}
2.4 处理器processor
这个包下主要是流控的处理器,分别有普通限流和热点限流处理器
2.4.1 接口RequestProcessor
public interface RequestProcessor {
/**
* Process the cluster request.
*
* @param request Sentinel cluster request
* @return the response after processed
*/
//处理请求
//有两个实现类,分别是FlowRequestProcessor,ParamFlowRequestProcessor
ClusterResponse processRequest(ClusterRequest request);
}
2.4.2 RequestProcessorProvider
请求流控提供者,类似于工厂类 实例变量
PROCESSOR_MAP:请求类型对应的请求处理器
SERVICE_LOADER:通过ServiceLoader加载流控实现类
源码分析
public final class RequestProcessorProvider {
private static final Map PROCESSOR_MAP = new ConcurrentHashMap<>();
//默认配置的实现类有FlowRequestProcessor,ParamFlowRequestProcessor
private static final ServiceLoader SERVICE_LOADER = ServiceLoaderUtil.getServiceLoader(
RequestProcessor.class);
//静态代码快,类启动是会加载
static {
loadAndInit();
}
private static void loadAndInit() {
for (RequestProcessor processor : SERVICE_LOADER) {
Integer type = parseRequestType(processor);
if (type != null) {
//放入map中
PROCESSOR_MAP.put(type, processor);
}
}
}
//获取RequestType
private static Integer parseRequestType(RequestProcessor processor) {
//配置在注解上
RequestType requestType = processor.getClass().getAnnotation(RequestType.class);
if (requestType != null) {
return requestType.value();
} else {
return null;
}
}
//获取RequestProcessor
public static RequestProcessor getProcessor(int type) {
return PROCESSOR_MAP.get(type);
}
static void addProcessorIfAbsent(int type, RequestProcessor processor) {
// TBD: use putIfAbsent in JDK 1.8.
if (PROCESSOR_MAP.containsKey(type)) {
return;
}
PROCESSOR_MAP.put(type, processor);
}
static void addProcessor(int type, RequestProcessor processor) {
AssertUtil.notNull(processor, "processor cannot be null");
PROCESSOR_MAP.put(type, processor);
}
private RequestProcessorProvider() {}
}
2.4.3 FlowRequestProcessor
源码分析
@RequestType(ClusterConstants.MSG_TYPE_FLOW)
public class FlowRequestProcessor implements RequestProcessor {
@Override
public ClusterResponse processRequest(ClusterRequest request) {
//获取TokenService,配置的是DefaultTokenService
TokenService tokenService = TokenServiceProvider.getService();
long flowId = request.getData().getFlowId();
int count = request.getData().getCount();
boolean prioritized = request.getData().isPriority();
//获取请求token
TokenResult result = tokenService.requestToken(flowId, count, prioritized);
//解析响应结果
return toResponse(result, request);
}
private ClusterResponse toResponse(TokenResult result, ClusterRequest request) {
return new ClusterResponse<>(request.getId(), request.getType(), result.getStatus(),
new FlowTokenResponseData()
.setRemainingCount(result.getRemaining())
.setWaitInMs(result.getWaitInMs())
);
}
}
2.4.4 ParamFlowRequestProcessor
源码分析
@RequestType(ClusterConstants.MSG_TYPE_PARAM_FLOW)
public class ParamFlowRequestProcessor implements RequestProcessor {
@Override
public ClusterResponse processRequest(ClusterRequest request) {
TokenService tokenService = TokenServiceProvider.getService();
long flowId = request.getData().getFlowId();
int count = request.getData().getCount();
Collection args = request.getData().getParams();
//请求热点参数
TokenResult result = tokenService.requestParamToken(flowId, count, args);
//流控接口解析
return toResponse(result, request);
}
private ClusterResponse toResponse(TokenResult result, ClusterRequest request) {
return new ClusterResponse<>(request.getId(), request.getType(), result.getStatus(),
new FlowTokenResponseData()
.setRemainingCount(result.getRemaining())
.setWaitInMs(0)
);
}
}
流控解析,下一面讲解。
2.5 启动加载器DefaultClusterServerInitFunc
源码
//实现了InitFunc,InitFunc会在系统启动时加载
public class DefaultClusterServerInitFunc implements InitFunc {
@Override
public void init() throws Exception {
//初始化Decoders
initDefaultEntityDecoders();
//初始化wriders
initDefaultEntityWriters();
//初始化processors
initDefaultProcessors();
// Eagerly-trigger the SPI pre-load of token service.
// 这个时候就把TokenService加载好了
TokenServiceProvider.getService();
RecordLog.info("[DefaultClusterServerInitFunc] Default entity codec and processors registered");
}
private void initDefaultEntityWriters() {
//ping
ResponseDataWriterRegistry.addWriter(ClusterConstants.MSG_TYPE_PING, new PingResponseDataWriter());
//流控Writer
ResponseDataWriterRegistry.addWriter(ClusterConstants.MSG_TYPE_FLOW, new FlowResponseDataWriter());
//热点参数Writer和Flow一样
ResponseDataWriterRegistry.addWriter(ClusterConstants.MSG_TYPE_PARAM_FLOW, new FlowResponseDataWriter());
}
private void initDefaultEntityDecoders() {
//ping
RequestDataDecodeRegistry.addDecoder(ClusterConstants.MSG_TYPE_PING, new PingRequestDataDecoder());
//普通Flow
RequestDataDecodeRegistry.addDecoder(ClusterConstants.MSG_TYPE_FLOW, new FlowRequestDataDecoder());
//热点参数
RequestDataDecodeRegistry.addDecoder(ClusterConstants.MSG_TYPE_PARAM_FLOW, new ParamFlowRequestDataDecoder());
}
private void initDefaultProcessors() {
// Eagerly-trigger the SPI pre-load.
//获取默认,实际上就是加载了,这个类在上面已经讲过了
RequestProcessorProvider.getProcessor(0);
}
}
http://www.namhuy.net/475/how-to-install-gui-to-centos-minimal.html
I have centos 6.3 minimal running as web server. I’m looking to install gui to my server to vnc to my server. You can insta
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'option,changed_ids ) values('0ac91f167f754c8cbac00e9e3dc372
实例1:
package com.bijian.thread;
public class MyThread extends Thread {
private static ThreadLocal tl = new ThreadLocal() {
protected synchronized Object initialValue() {
return new Inte
var v = 'C9CFBAA3CAD0';
console.log(v);
var arr = v.split('');
for (var i = 0; i < arr.length; i ++) {
if (i % 2 == 0) arr[i] = '%' + arr[i];
}
console.log(arr.join(''));
console.log(v.r