最近接到一个需求,需求中需要通过服务的ip+port在注册中心中指定服务来处理请求。
实现方式为:
1、重新实现ProxyFactory类,增加isSpecified属性、unresolvedSocketAddress属性,
增加初始化这两个属性的方法isSpecified,初始化前面两个属性的值;
dispatcher方法增加对isSpecified属性的判断,如果是指定ip的进入新创建的Dispatcher
实现类来处理;
2、新增一个Dispatcher实现类,继承AbstractDispatcher类,将select类里的软负载均衡改为指定。
具体代码如下:
/*
* Copyright (c) 2015 The Jupiter Project
*
* Licensed under the Apache License, version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jupiter.rpc.consumer;
import java.util.Collections;
import java.util.List;
import org.jupiter.common.util.JConstants;
import org.jupiter.common.util.Lists;
import org.jupiter.common.util.Proxies;
import org.jupiter.common.util.Requires;
import org.jupiter.common.util.Strings;
import org.jupiter.rpc.DispatchType;
import org.jupiter.rpc.InvokeType;
import org.jupiter.rpc.JClient;
import org.jupiter.rpc.ServiceProvider;
import org.jupiter.rpc.consumer.cluster.ClusterInvoker;
import org.jupiter.rpc.consumer.dispatcher.CiccOmsDispatcher;
import org.jupiter.rpc.consumer.dispatcher.DefaultBroadcastDispatcher;
import org.jupiter.rpc.consumer.dispatcher.DefaultRoundDispatcher;
import org.jupiter.rpc.consumer.dispatcher.Dispatcher;
import org.jupiter.rpc.consumer.invoker.AsyncInvoker;
import org.jupiter.rpc.consumer.invoker.AutoInvoker;
import org.jupiter.rpc.load.balance.LoadBalancerFactory;
import org.jupiter.rpc.load.balance.LoadBalancerType;
import org.jupiter.rpc.model.metadata.ClusterStrategyConfig;
import org.jupiter.rpc.model.metadata.MethodSpecialConfig;
import org.jupiter.rpc.model.metadata.ServiceMetadata;
import org.jupiter.serialization.SerializerType;
import org.jupiter.transport.*;
/**
* Proxy factory
*
* Consumer对象代理工厂, [group, providerName, version]
*
* jupiter
* org.jupiter.rpc.consumer
*
* @author wangjp
*/
public class ProxyFactory {
// 接口类型
private final Class interfaceClass;
// 服务组别
private String group;
// 服务名称
private String providerName;
// 服务版本号, 通常在接口不兼容时版本号才需要升级
private String version;
// jupiter client
private JClient client;
// 序列化/反序列化方式
private SerializerType serializerType = SerializerType.getDefault();
// 软负载均衡类型
private LoadBalancerType loadBalancerType = LoadBalancerType.getDefault();
// 基于ExtSpiLoadBalancerFactory扩展的负载均衡可以选择指定名字, 可以利用名字作为唯一标识扩展多种类型的负载均衡
private String extLoadBalancerName;
// provider地址
private List addresses;
// 调用方式 [同步, 异步]
private InvokeType invokeType = InvokeType.getDefault();
// 派发方式 [单播, 广播]
private DispatchType dispatchType = DispatchType.getDefault();
// 是否指定处理节点
private boolean isSpecified = false;
// 指定的处理节点地址
private UnresolvedSocketAddress unresolvedSocketAddress;
// 调用超时时间设置
private long timeoutMillis;
// 指定方法的单独配置, 方法参数类型不做区别对待
private List methodSpecialConfigs;
// 消费者端拦截器
private List interceptors;
// 集群容错策略
private ClusterInvoker.Strategy strategy = ClusterInvoker.Strategy.getDefault();
// failover重试次数
private int retries = 2;
public static ProxyFactory factory(Class interfaceClass) {
ProxyFactory factory = new ProxyFactory<>(interfaceClass);
// 初始化数据
factory.addresses = Lists.newArrayList();
factory.interceptors = Lists.newArrayList();
factory.methodSpecialConfigs = Lists.newArrayList();
return factory;
}
private ProxyFactory(Class interfaceClass) {
this.interfaceClass = interfaceClass;
}
public Class getInterfaceClass() {
return interfaceClass;
}
public ProxyFactory group(String group) {
this.group = group;
return this;
}
public ProxyFactory providerName(String providerName) {
this.providerName = providerName;
return this;
}
public ProxyFactory version(String version) {
this.version = version;
return this;
}
public ProxyFactory directory(Directory directory) {
return group(directory.getGroup())
.providerName(directory.getServiceProviderName())
.version(directory.getVersion());
}
public ProxyFactory client(JClient client) {
this.client = client;
return this;
}
public ProxyFactory serializerType(SerializerType serializerType) {
this.serializerType = serializerType;
return this;
}
public ProxyFactory loadBalancerType(LoadBalancerType loadBalancerType) {
this.loadBalancerType = loadBalancerType;
return this;
}
public ProxyFactory loadBalancerType(LoadBalancerType loadBalancerType, String extLoadBalancerName) {
this.loadBalancerType = loadBalancerType;
this.extLoadBalancerName = extLoadBalancerName;
return this;
}
public ProxyFactory addProviderAddress(UnresolvedAddress... addresses) {
Collections.addAll(this.addresses, addresses);
return this;
}
public ProxyFactory addProviderAddress(List addresses) {
this.addresses.addAll(addresses);
return this;
}
public ProxyFactory invokeType(InvokeType invokeType) {
this.invokeType = Requires.requireNotNull(invokeType);
return this;
}
public ProxyFactory dispatchType(DispatchType dispatchType) {
this.dispatchType = Requires.requireNotNull(dispatchType);
return this;
}
public ProxyFactory timeoutMillis(long timeoutMillis) {
this.timeoutMillis = timeoutMillis;
return this;
}
public ProxyFactory addMethodSpecialConfig(MethodSpecialConfig... methodSpecialConfigs) {
Collections.addAll(this.methodSpecialConfigs, methodSpecialConfigs);
return this;
}
public ProxyFactory addInterceptor(ConsumerInterceptor... interceptors) {
Collections.addAll(this.interceptors, interceptors);
return this;
}
public ProxyFactory clusterStrategy(ClusterInvoker.Strategy strategy) {
this.strategy = strategy;
return this;
}
public ProxyFactory failoverRetries(int retries) {
this.retries = retries;
return this;
}
public ProxyFactory isSpecified(UnresolvedSocketAddress unresolvedSocketAddress){
this.isSpecified = true;
this.unresolvedSocketAddress = unresolvedSocketAddress;
return this;
}
public I newProxyInstance() {
// check arguments
Requires.requireNotNull(interfaceClass, "interfaceClass");
ServiceProvider annotation = interfaceClass.getAnnotation(ServiceProvider.class);
if (annotation != null) {
Requires.requireTrue(
group == null,
interfaceClass.getName() + " has a @ServiceProvider annotation, can't set [group] again"
);
Requires.requireTrue(
providerName == null,
interfaceClass.getName() + " has a @ServiceProvider annotation, can't set [providerName] again"
);
group = annotation.group();
String name = annotation.name();
providerName = Strings.isNotBlank(name) ? name : interfaceClass.getName();
}
Requires.requireTrue(Strings.isNotBlank(group), "group");
Requires.requireTrue(Strings.isNotBlank(providerName), "providerName");
Requires.requireNotNull(client, "client");
Requires.requireNotNull(serializerType, "serializerType");
if (dispatchType == DispatchType.BROADCAST && invokeType == InvokeType.SYNC) {
throw reject("broadcast & sync unsupported");
}
// metadata
ServiceMetadata metadata = new ServiceMetadata(
group,
providerName,
Strings.isNotBlank(version) ? version : JConstants.DEFAULT_VERSION
);
JConnector connector = client.connector();
for (UnresolvedAddress address : addresses) {
connector.addChannelGroup(metadata, connector.group(address));
}
// dispatcher
Dispatcher dispatcher = dispatcher()
.interceptors(interceptors)
.timeoutMillis(timeoutMillis)
.methodSpecialConfigs(methodSpecialConfigs);
ClusterStrategyConfig strategyConfig = ClusterStrategyConfig.of(strategy, retries);
Object handler;
switch (invokeType) {
case SYNC:
case AUTO:
handler = new AutoInvoker(client.appName(), metadata, dispatcher, strategyConfig, methodSpecialConfigs);
break;
case ASYNC:
handler = new AsyncInvoker(client.appName(), metadata, dispatcher, strategyConfig, methodSpecialConfigs);
break;
default:
throw reject("invokeType: " + invokeType);
}
return Proxies.getDefault().newProxy(interfaceClass, handler);
}
protected Dispatcher dispatcher() {
switch (dispatchType) {
case ROUND:
if(isSpecified){
return new CiccOmsDispatcher(client,
LoadBalancerFactory.getInstance(loadBalancerType, extLoadBalancerName), serializerType, unresolvedSocketAddress);
}else{
return new DefaultRoundDispatcher(
client,
LoadBalancerFactory.getInstance(loadBalancerType, extLoadBalancerName), serializerType);
}
case BROADCAST:
return new DefaultBroadcastDispatcher(client, serializerType);
default:
throw reject("dispatchType: " + dispatchType);
}
}
private static UnsupportedOperationException reject(String message) {
return new UnsupportedOperationException(message);
}
}
package org.jupiter.rpc.consumer.dispatcher;
import org.jupiter.common.util.SystemClock;
import org.jupiter.common.util.internal.logging.InternalLogger;
import org.jupiter.common.util.internal.logging.InternalLoggerFactory;
import org.jupiter.rpc.DispatchType;
import org.jupiter.rpc.JClient;
import org.jupiter.rpc.JRequest;
import org.jupiter.rpc.consumer.future.InvokeFuture;
import org.jupiter.rpc.load.balance.LoadBalancer;
import org.jupiter.rpc.model.metadata.MessageWrapper;
import org.jupiter.rpc.model.metadata.ServiceMetadata;
import org.jupiter.serialization.Serializer;
import org.jupiter.serialization.SerializerType;
import org.jupiter.serialization.io.OutputBuf;
import org.jupiter.transport.*;
import org.jupiter.transport.channel.CopyOnWriteGroupList;
import org.jupiter.transport.channel.JChannel;
import org.jupiter.transport.channel.JChannelGroup;
/**
* 单播方式派发消息.
*
* jupiter
* org.jupiter.rpc.consumer.dispatcher
*
* @author wangjp
*/
public class CiccOmsDispatcher extends AbstractDispatcher {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CiccOmsDispatcher.class);
private UnresolvedAddress unresolvedAddress;
private final JClient client;
private final LoadBalancer loadBalancer; // 软负载均衡
public CiccOmsDispatcher(
JClient client, LoadBalancer loadBalancer, SerializerType serializerType, UnresolvedAddress unresolvedAddress) {
super(client, loadBalancer, serializerType);
this.unresolvedAddress = unresolvedAddress;
this.client = client;
this.loadBalancer = loadBalancer;
}
@Override
public InvokeFuture dispatch(JRequest request, Class returnType) {
// stack copy
final Serializer _serializer = serializer();
final MessageWrapper message = request.message();
// 通过指定ip获取一个channel
JChannel channel = select(message.getMetadata());
byte s_code = _serializer.code();
// 在业务线程中序列化, 减轻IO线程负担
if (CodecConfig.isCodecLowCopy()) {
OutputBuf outputBuf =
_serializer.writeObject(channel.allocOutputBuf(), message);
request.outputBuf(s_code, outputBuf);
} else {
byte[] bytes = _serializer.writeObject(message);
request.bytes(s_code, bytes);
}
return write(channel, request, returnType, DispatchType.ROUND);
}
@Override
protected JChannel select(ServiceMetadata metadata) {
JChannelGroup group = null;
CopyOnWriteGroupList groups = client
.connector()
.directory(metadata);
JChannelGroup[] elements = groups.getSnapshot();
for (int i = 0; i < elements.length; i++) {
UnresolvedAddress eleUnresolvedAddress = elements[i].remoteAddress();
if (eleUnresolvedAddress.equals(unresolvedAddress)) {
group = elements[i];
break;
}
}
JChannel channel;
if (group != null) {
if (group.isAvailable()) {
return group.next();
}
// to the deadline (no available channel), the time exceeded the predetermined limit
long deadline = group.deadlineMillis();
if (deadline > 0 && SystemClock.millisClock().now() > deadline) {
boolean removed = groups.remove(group);
if (removed) {
if (logger.isWarnEnabled()) {
logger.warn("Removed channel group: {} in directory: {} on [select].",
group, metadata.directoryString());
}
}
}
} else {
// for 3 seconds, expired not wait
if (!client.awaitConnections(metadata, 3000)) {
throw new IllegalStateException("No connections");
}
}
// //以下代码用于处理当本次请求的服务失效时,获取一个有效的服务进行请求处理。
// //在这个项目中,每个服务具有唯一性,所以一下代码不适用于该项目
// JChannelGroup[] snapshot = groups.getSnapshot();
// for (JChannelGroup g : snapshot) {
// if (g.isAvailable()) {
// return g.next();
// }
// }
throw new IllegalStateException("No channel");
}
}