org.yx.rpc.client.Client Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sumk Show documentation
Show all versions of sumk Show documentation
A quick developing framewort for internet company
/**
* Copyright (C) 2016 - 2030 youtongluan.
*
* 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.yx.rpc.client;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.apache.mina.core.future.WriteFuture;
import org.yx.common.Host;
import org.yx.common.context.ActionContext;
import org.yx.common.route.Router;
import org.yx.exception.SoaException;
import org.yx.log.Logs;
import org.yx.rpc.InnerRpcUtil;
import org.yx.rpc.RpcActionNode;
import org.yx.rpc.RpcActions;
import org.yx.rpc.RpcErrorCode;
import org.yx.rpc.RpcJson;
import org.yx.rpc.RpcSettings;
import org.yx.rpc.client.route.HostChecker;
import org.yx.rpc.client.route.RpcRoutes;
import org.yx.rpc.codec.Protocols;
import org.yx.rpc.codec.Request;
import org.yx.rpc.server.LocalRequestHandler;
import org.yx.rpc.server.Response;
import org.yx.util.UUIDSeed;
public final class Client {
private static final Host LOCAL = Host.create("local", 0);
private static final AtomicInteger COUNTER = new AtomicInteger();
private final String api;
private Object params;
private ParamType paramType;
private int totalTimeout;
private Host[] directUrls;
private boolean backup;
private int tryCount;
private Consumer callback;
public Client(String api) {
this.api = Objects.requireNonNull(api).trim();
this.totalTimeout = RpcSettings.clientDefaultTimeout();
this.tryCount = RpcSettings.clientTryCount();
}
public Client directUrls(Host... urls) {
this.directUrls = urls;
return this;
}
/**
* 设置发送的尝试次数。只有发送失败会重试,其它的不会
*
* @param tryCount
* 尝试次数,包含第一次发送
* @return 当前对象
*/
public Client tryCount(int tryCount) {
this.tryCount = tryCount > 0 ? tryCount : 1;
return this;
}
/**
* 设置直连url不能用的时候,是否使用注册中心上的地址
*
* @param backup
* 失败时是否启用注册中心上的地址
* @return 当前对象
*/
public Client backup(boolean backup) {
this.backup = backup;
return this;
}
public Client timeout(int timeout) {
this.totalTimeout = timeout > 0 ? timeout : 1;
return this;
}
public Client callback(Consumer callback) {
this.callback = callback;
return this;
}
public Client paramInArray(Object... args) {
if (args == null) {
args = new String[0];
}
String[] params = new String[args.length];
for (int i = 0; i < args.length; i++) {
params[i] = RpcJson.client().toJson(args[i]);
}
this.params = params;
this.paramType = ParamType.JSONARRAY;
return this;
}
public Client paramInJson(String json) {
this.params = json;
this.paramType = ParamType.JSON;
return this;
}
public Client paramInMap(Map map) {
return paramInJson(RpcJson.client().toJson(map));
}
protected Req createReq() {
Req req = new Req();
ActionContext context = ActionContext.current();
if (context.isTest()) {
req.setTest(true);
}
req.setStart(System.currentTimeMillis());
String sn = UUIDSeed.seq18();
req.setFullSn(sn, context.traceId(), context.nextSpanId());
req.setUserId(context.userId());
req.setApi(this.api);
req.setFrom(Rpc.appId());
req.initAcceptResponseTypes(Protocols.RESPONSE_ACCEPT_TYPES);
req.setAttachments(context.attachmentView());
return req;
}
/**
* 本方法调用之后,不允许再调用本对象的任何方法
*
* @return 用无论是否成功,都会返回future。如果失败的话,异常包含在future中。
* 通信异常是SoaException;如果是业务类异常,则是BizException
*/
public RpcFuture execute() {
Objects.requireNonNull(this.paramType, "param have not been set");
Req req = this.createReq();
long endTime = req.getStart() + this.totalTimeout;
req.setParams(this.paramType.protocol(), this.params);
int count = this.tryCount;
while (true) {
RpcFuture f = sendAsync(req, endTime);
if (f.getClass() == ErrorRpcFuture.class) {
ErrorRpcFuture errorFuture = (ErrorRpcFuture) f;
RpcLocker locker = errorFuture.locker;
LockHolder.remove(locker.req.getSn());
if (--count > 0 && errorFuture.rpcResult().exception().getCode() == RpcErrorCode.SEND_FAILED
&& System.currentTimeMillis() + 5 < endTime) {
locker.discard(errorFuture.rpcResult());
Logs.rpc().warn("无法发送数据到{},重试rpc请求", locker.url());
continue;
}
locker.wakeupAndLog(errorFuture.rpcResult());
}
return f;
}
}
private Host selectDirectUrl() {
int index = COUNTER.incrementAndGet();
if (index < 0) {
COUNTER.set((int) (System.nanoTime() & 0xff));
index = COUNTER.incrementAndGet();
}
for (int i = 0; i < this.directUrls.length; i++) {
index %= directUrls.length;
Host url = this.directUrls[index];
if (!HostChecker.get().isDowned(url)) {
return url;
}
}
return null;
}
private RpcFuture sendAsync(final Req req, final long endTime) {
final RpcLocker locker = new RpcLocker(req, callback);
Host url = null;
if (this.directUrls != null && this.directUrls.length > 0) {
url = selectDirectUrl();
if (url == null && !this.backup) {
SoaException ex = new SoaException(RpcErrorCode.NO_NODE_AVAILABLE,
"all directUrls is disabled:" + Arrays.toString(this.directUrls), null);
return new ErrorRpcFuture(ex, locker);
}
}
if (url == null) {
Router route = RpcRoutes.getRoute(api);
RpcFuture future = this.tryLocalHandler(req, locker, route);
if (future != null) {
return future;
}
if (route == null) {
SoaException ex = new SoaException(RpcErrorCode.NO_ROUTE, "can not find route for " + api, null);
return new ErrorRpcFuture(ex, locker);
}
url = route.select();
}
if (url == null) {
SoaException ex = new SoaException(RpcErrorCode.NO_NODE_AVAILABLE, "route for " + api + " are all disabled",
null);
return new ErrorRpcFuture(ex, locker);
}
locker.url(url);
req.setServerProtocol(RpcRoutes.getServerProtocol(url));
WriteFuture f = null;
try {
ReqSession reqSession = ReqSessionHolder.getSession(url);
LockHolder.register(locker, endTime);
f = reqSession.write(req);
} catch (Exception e) {
Logs.rpc().error(e.getLocalizedMessage(), e);
}
if (f == null) {
SoaException ex = new SoaException(RpcErrorCode.SEND_FAILED, url + " can not connect", null);
return new ErrorRpcFuture(ex, locker);
}
f.addListener(locker);
return new RpcFutureImpl(locker);
}
private RpcFuture tryLocalHandler(Req req, RpcLocker locker, Router route) {
RpcActionNode node = RpcActions.getActionNode(api);
if (node == null) {
return null;
}
if (RpcSettings.disableLocalRoute() && route != null) {
return null;
}
Request request = Request.from(req);
req = null;
ActionContext context = ActionContext.current().clone();
try {
InnerRpcUtil.rpcContext(request, context.isTest());
locker.url(LOCAL);
Response resp = LocalRequestHandler.inst.handler(request, node);
ActionContext.store(context);
locker.wakeupAndLog(new RpcResult(resp.json(), resp.exception()));
} finally {
ActionContext.store(context);
}
return new RpcFutureImpl(locker);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy