/* * Copyright 2009-2016 Weibo, Inc. * * 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 com.weibo.api.motan.protocol.rpc; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.apache.commons.lang3.StringUtils; import com.weibo.api.motan.codec.AbstractCodec; import com.weibo.api.motan.codec.Serialization; import com.weibo.api.motan.common.MotanConstants; import com.weibo.api.motan.common.URLParamType; import com.weibo.api.motan.core.extension.ExtensionLoader; import com.weibo.api.motan.core.extension.SpiMeta; import com.weibo.api.motan.exception.MotanErrorMsgConstant; import com.weibo.api.motan.exception.MotanFrameworkException; import com.weibo.api.motan.rpc.DefaultRequest; import com.weibo.api.motan.rpc.DefaultResponse; import com.weibo.api.motan.rpc.Provider; import com.weibo.api.motan.rpc.Request; import com.weibo.api.motan.rpc.Response; import com.weibo.api.motan.transport.Channel; import com.weibo.api.motan.transport.support.DefaultRpcHeartbeatFactory; import com.weibo.api.motan.util.ByteUtil; import com.weibo.api.motan.util.ConcurrentHashSet; import com.weibo.api.motan.util.ExceptionUtil; import com.weibo.api.motan.util.LoggerUtil; import com.weibo.api.motan.util.MotanDigestUtil; import com.weibo.api.motan.util.MotanFrameworkUtil; import com.weibo.api.motan.util.MotanSwitcherUtil; import com.weibo.api.motan.util.ReflectUtil; /** * 压缩协议codec,支持开启gzip压缩。 * * @author zhanglei * */ @SpiMeta(name = "compressMotan") public class CompressRpcCodec extends AbstractCodec { private static final short MAGIC = (short) 0xF0F0; private static final byte MASK = 0x07; // 保存方法签名与具体方法信息的对应关系,decode request时server端使用 private static ConcurrentHashMap<String, MethodInfo> SIGN_METHOD_MAP = new ConcurrentHashMap<String, MethodInfo>(); // 保存方法信息串与签名之间的对应关系。 private static ConcurrentHashMap<String, String> METHOD_SIGN_MAP = new ConcurrentHashMap<String, String>(); // 保存方法签名与调用方attachment中application等固定信息的对应关系,decode request时server端使用 private static ConcurrentHashMap<String, AttachmentInfo> SIGN_ATTACHMENT_MAP = new ConcurrentHashMap<String, AttachmentInfo>(); private static ConcurrentHashSet<String> ACCEPT_ATTACHMENT_SIGN = new ConcurrentHashSet<String>();// 保存已被server端缓存的attachment信息签名。 // client使用,如果server端已缓存则不用重复发送 private static final String SIGN_FLAG = "1";// 使用方法签名标识位。用来实现新旧版本兼容 // attachment中使用的简化key,都以_开头 private static final String ATTACHMENT_SIGN = "_A";// 压缩attachment固定参数后签名的key。同时也是server确认已保存的签名key private static final String UN_ATTACHMENT_SIGN = "_UA";// server确认尚未保存的签名key private static final String CLIENT_REQUESTID = "_RID";// client requestid的简化key public static final String CODEC_VERSION_SWITCHER = "feature.motanrpc.codecversion.degrade";// codec降级开关,默认为false,为true时会使用v1非压缩版本。 public static final String GROUP_CODEC_VERSION_SWITCHER = "feature.motanrpc.codecversion.groupdegrade.";// 按group分组降级codec开关前缀,默认为false,为true时会使用v1非压缩版本。 private DefaultRpcCodec v1Codec = new DefaultRpcCodec(); static { LoggerUtil.info("init compress codec"); MotanSwitcherUtil.initSwitcher(CODEC_VERSION_SWITCHER, false); } @Override public byte[] encode(Channel channel, Object message) throws IOException { if (needEncodeV1(message)) { return v1Codec.encode(channel, message); } else { // 使用v2压缩版本 return encodeV2(channel, message); } } // v1降级开关打开、心跳请求、client端使用v1版本时,需要使用v1编码 private boolean needEncodeV1(Object message) { if (MotanSwitcherUtil.isOpen(CODEC_VERSION_SWITCHER)) { return true; } if (message instanceof Request) { //  心跳包不压缩 if (DefaultRpcHeartbeatFactory.isHeartbeatRequest(message)) { return true; } // 检查分组降级开关是否开启 String group = MotanFrameworkUtil.getGroupFromRequest((Request) message); if (MotanSwitcherUtil.switcherIsOpenWithDefault(GROUP_CODEC_VERSION_SWITCHER + group, false)) { return true; } } return message instanceof Response && ((Response) message).getRpcProtocolVersion() == RpcProtocolVersion.VERSION_1.getVersion(); } /** * decode data * * <pre> * 对于client端:主要是来自server端的response or exception * 对于server端: 主要是来自client端的request * </pre> * * @param data * @return * @throws IOException */ @Override public Object decode(Channel channel, String remoteIp, byte[] data) throws IOException { if (MotanSwitcherUtil.isOpen(CODEC_VERSION_SWITCHER)) { // 降级开关打开时,使用v1版本codec return v1Codec.decode(channel, remoteIp, data); } else { if (data.length <= 3) { throw new MotanFrameworkException("decode error: format problem", MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } // 只支持v1和v2版本 if (data[2] == RpcProtocolVersion.VERSION_1.getVersion()) { return v1Codec.decode(channel, remoteIp, data); } else if (data[2] == RpcProtocolVersion.VERSION_2.getVersion()) { // 使用v2压缩版本 return decodeV2(channel, remoteIp, data); } else { throw new MotanFrameworkException("decode error: version error. version=" + data[2], MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } } } public byte[] encodeV2(Channel channel, Object message) throws IOException { try { if (message instanceof Request) { return encodeRequest(channel, (Request) message); } else if (message instanceof Response) { return encodeResponse(channel, (Response) message); } } catch (Exception e) { if (ExceptionUtil.isMotanException(e)) { throw (RuntimeException) e; } else { throw new MotanFrameworkException("encode error: isResponse=" + (message instanceof Response), e, MotanErrorMsgConstant.FRAMEWORK_ENCODE_ERROR); } } throw new MotanFrameworkException("encode error: message type not support, " + message.getClass(), MotanErrorMsgConstant.FRAMEWORK_ENCODE_ERROR); } /** * decode data * * <pre> * 对于client端:主要是来自server端的response or exception * 对于server端: 主要是来自client端的request * </pre> * * @param data * @return * @throws IOException */ public Object decodeV2(Channel channel, String remoteIp, byte[] data) throws IOException { if (data.length <= RpcProtocolVersion.VERSION_2.getHeaderLength()) { throw new MotanFrameworkException("decode error: format problem", MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } short type = ByteUtil.bytes2short(data, 0); if (type != MAGIC) { throw new MotanFrameworkException("decode error: magic error", MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } int bodyLength = ByteUtil.bytes2int(data, 12); if (RpcProtocolVersion.VERSION_2.getHeaderLength() + bodyLength != data.length) { throw new MotanFrameworkException("decode error: content length error", MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } byte flag = data[3]; byte dataType = (byte) (flag & MASK); boolean isResponse = (dataType != MotanConstants.FLAG_REQUEST); byte[] body = new byte[bodyLength]; System.arraycopy(data, RpcProtocolVersion.VERSION_1.getHeaderLength(), body, 0, bodyLength); long requestId = ByteUtil.bytes2long(data, 4); Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension( channel.getUrl().getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue())); try { if (isResponse) { return decodeResponse(body, dataType, requestId, data[2], serialization); } else { return decodeRequest(body, requestId, remoteIp, serialization); } } catch (ClassNotFoundException e) { throw new MotanFrameworkException("decode " + (isResponse ? "response" : "request") + " error: class not found", e, MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } catch (Exception e) { if (ExceptionUtil.isMotanException(e)) { throw (RuntimeException) e; } else { throw new MotanFrameworkException("decode error: isResponse=" + isResponse, e, MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } } } /** * request body 数据: * * <pre> * * body: * * byte[] data : * * serialize(interface_name, method_name, method_param_desc, method_param_value, attachments_size, attachments_value) * * method_param_desc: for_each (string.append(method_param_interface_name)) * * method_param_value: for_each (method_param_name, method_param_value) * * attachments_value: for_each (attachment_name, attachment_value) * * </pre> * * @param request * @return * @throws IOException */ private byte[] encodeRequest(Channel channel, Request request) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutput output = createOutput(outputStream); addMethodInfo(output, request); Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension( channel.getUrl().getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue())); if (request.getArguments() != null && request.getArguments().length > 0) { for (Object obj : request.getArguments()) { serialize(output, obj, serialization); } } if (request.getAttachments() == null || request.getAttachments().isEmpty()) { // empty attachments output.writeShort(0); } else { // 需要copy一份attachment进行签名替换,这样在失败重试时原始的request信息不会变更 Map<String, String> attachments = copyMap(request.getAttachments()); replaceAttachmentParamsBySign(channel, attachments); addAttachment(output, attachments); } output.flush(); byte[] body = outputStream.toByteArray(); byte flag = MotanConstants.FLAG_REQUEST; output.close(); Boolean usegz = channel.getUrl().getBooleanParameter(URLParamType.usegz.getName(), URLParamType.usegz.getBooleanValue()); int minGzSize = channel.getUrl().getIntParameter(URLParamType.mingzSize.getName(), URLParamType.mingzSize.getIntValue()); return encode(compress(body, usegz, minGzSize), flag, request.getRequestId()); } private Map<String, String> copyMap(Map<String, String> attachments) { Map<String, String> resultMap = new HashMap<String, String>(); for (Map.Entry<String, String> entry : attachments.entrySet()) { resultMap.put(entry.getKey(), entry.getValue()); } return resultMap; } /** * 添加方法完整信息或方法签名。 * * @param output * @param request * @throws IOException */ private void addMethodInfo(ObjectOutput output, Request request) throws IOException { String methodInfoStr = MotanFrameworkUtil.getServiceKey(request) + request.getMethodName() + request.getParamtersDesc(); String methodSign = METHOD_SIGN_MAP.get(methodInfoStr); if (methodSign == null) { MethodInfo temp = new MethodInfo(MotanFrameworkUtil.getGroupFromRequest(request), request.getInterfaceName(), request.getMethodName(), request.getParamtersDesc(), MotanFrameworkUtil.getVersionFromRequest(request)); try { methodSign = temp.getSign(); METHOD_SIGN_MAP.putIfAbsent(methodInfoStr, methodSign); LoggerUtil.info("add method sign:" + methodSign + ", methodinfo:" + temp.toString()); } catch (Exception e) { LoggerUtil.warn("gen method sign fail!" + e.getMessage()); } } if (methodSign != null) { output.writeUTF(SIGN_FLAG);// 使用方法签名 output.writeUTF(methodSign); } else {// 如果获取签名失败就使用非压缩方式。 output.writeUTF(request.getInterfaceName()); output.writeUTF(request.getMethodName()); output.writeUTF(request.getParamtersDesc()); } } /** * 用签名替换Attachment中每次必传的固定参数。 * * @param attachments */ private void replaceAttachmentParamsBySign(Channel channel, Map<String, String> attachments) { // attachment中的固定参数使用签名方式传递。首次跟server建立链接时传全部信息,之后只传签名。 AttachmentInfo info = getAttachmentInfoMap(attachments); if (info != null) { String sign = info.getAttachmetnSign(); if (sign != null) { attachments.put(ATTACHMENT_SIGN, sign); if (ACCEPT_ATTACHMENT_SIGN.contains(sign)) {// server端已经缓存签名时,不需要传递application等固定信息。 removeAttachmentInfoMap(attachments); } } } // 如果没有client的requestid,则不传递此参数,否则使用简化key传递 // 为保证不同版本兼容性,只在codec中进行处理。 String clientRequestid = attachments.get(URLParamType.requestIdFromClient.getName()); if (clientRequestid != null && !URLParamType.requestIdFromClient.getValue().equals(clientRequestid)) { attachments.put(CLIENT_REQUESTID, clientRequestid); } attachments.remove(URLParamType.requestIdFromClient.getName()); } private void addAttachment(ObjectOutput output, Map<String, String> attachments) throws IOException { output.writeShort(attachments.size()); for (Map.Entry<String, String> entry : attachments.entrySet()) { output.writeUTF(entry.getKey()); output.writeUTF(entry.getValue()); } } /** * response body 数据: * * <pre> * * body: * * byte[] : serialize (result) or serialize (exception) * * </pre> * * @param channel * @param value * @return * @throws IOException */ private byte[] encodeResponse(Channel channel, Response value) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutput output = createOutput(outputStream); Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension( channel.getUrl().getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue())); byte flag = 0; output.writeLong(value.getProcessTime()); if (value.getException() != null) { output.writeUTF(value.getException().getClass().getName()); serialize(output, value.getException(), serialization); flag = MotanConstants.FLAG_RESPONSE_EXCEPTION; } else if (value.getValue() == null) { flag = MotanConstants.FLAG_RESPONSE_VOID; } else { output.writeUTF(value.getValue().getClass().getName()); serialize(output, value.getValue(), serialization); // v2版本可以在response中添加attachment Map<String, String> attachments = value.getAttachments(); if (attachments != null) { String signed = attachments.get(ATTACHMENT_SIGN); String unSigned = attachments.get(UN_ATTACHMENT_SIGN); attachments.clear(); // 除了attachment签名外不返回其他信息。 if (StringUtils.isNotBlank(signed)) { attachments.put(ATTACHMENT_SIGN, signed); } if (StringUtils.isNotBlank(unSigned)) { attachments.put(UN_ATTACHMENT_SIGN, unSigned); } } if (attachments != null && !attachments.isEmpty()) {// 需要回传附加数据 addAttachment(output, attachments); } else { // empty attachments output.writeShort(0); } flag = MotanConstants.FLAG_RESPONSE_ATTACHMENT; // v2版本flag } output.flush(); byte[] body = outputStream.toByteArray(); output.close(); Boolean usegz = channel.getUrl().getBooleanParameter(URLParamType.usegz.getName(), URLParamType.usegz.getBooleanValue()); int minGzSize = channel.getUrl().getIntParameter(URLParamType.mingzSize.getName(), URLParamType.mingzSize.getIntValue()); return encode(compress(body, usegz, minGzSize), flag, value.getRequestId()); } /** * 数据协议: * * <pre> * * header: 16个字节 * * 0-15 bit : magic * 16-23 bit : version * 24-31 bit : extend flag , 其中: 29-30 bit: event 可支持4种event,比如normal, exception等, 31 bit : 0 is request , 1 is response * 32-95 bit : request id * 96-127 bit : body content length * * </pre> * * @param body * @param flag * @param requestId * @return * @throws IOException */ private byte[] encode(byte[] body, byte flag, long requestId) throws IOException { byte[] header = new byte[RpcProtocolVersion.VERSION_2.getHeaderLength()]; int offset = 0; // 0 - 15 bit : magic ByteUtil.short2bytes(MAGIC, header, offset); offset += 2; // 16 - 23 bit : version header[offset++] = RpcProtocolVersion.VERSION_2.getVersion(); // 24 - 31 bit : extend flag header[offset++] = flag; // 32 - 95 bit : requestId ByteUtil.long2bytes(requestId, header, offset); offset += 8; // 96 - 127 bit : body content length ByteUtil.int2bytes(body.length, header, offset); byte[] data = new byte[header.length + body.length]; System.arraycopy(header, 0, data, 0, header.length); System.arraycopy(body, 0, data, header.length, body.length); return data; } private Object decodeRequest(byte[] body, long requestId, String remoteIp, Serialization serialization) throws IOException, ClassNotFoundException { ObjectInput input = createInput(getInputStream(body)); String interfaceName = null; String methodName = null; String paramtersDesc = null; String group = null; String version = null; String flag = input.readUTF(); if (SIGN_FLAG.equals(flag)) {// 方法签名方式 String sign = input.readUTF(); MethodInfo mInfo = SIGN_METHOD_MAP.get(sign); if (mInfo == null) { throw new MotanFrameworkException("decode error: invalid method sign: " + sign, MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } interfaceName = mInfo.getInterfaceName(); methodName = mInfo.getMethodName(); paramtersDesc = mInfo.getParamtersDesc(); group = mInfo.getGroup(); version = mInfo.getVersion(); } else { interfaceName = flag; methodName = input.readUTF(); paramtersDesc = input.readUTF(); } DefaultRequest rpcRequest = new DefaultRequest(); rpcRequest.setRequestId(requestId); rpcRequest.setInterfaceName(interfaceName); rpcRequest.setMethodName(methodName); rpcRequest.setParamtersDesc(paramtersDesc); rpcRequest.setArguments(decodeRequestParameter(input, paramtersDesc, serialization)); rpcRequest.setAttachments(decodeRequestAttachments(input)); rpcRequest.setRpcProtocolVersion(RpcProtocolVersion.VERSION_2.getVersion()); input.close(); Map<String, String> attachments = rpcRequest.getAttachments(); putSignedAttachment(attachments, remoteIp);// 根据签名添加client固定参数。 if (attachments.get(URLParamType.group.name()) == null) { // 如果attachment sign失效时,需要使用methodsign中的group信息。 attachments.put(URLParamType.group.name(), group); attachments.put(URLParamType.version.name(), version); } return rpcRequest; } private void putSignedAttachment(Map<String, String> attachments, String remoteIp) { if (attachments != null && !attachments.isEmpty()) { AttachmentInfo info = getAttachmentInfoMap(attachments); if (info != null) {// 如果client端传递了application等固定参数,则更新对应缓存。并标记已接收缓存。 String sign = attachments.get(ATTACHMENT_SIGN); if (StringUtils.isNotBlank(sign)) { SIGN_ATTACHMENT_MAP.put(remoteIp + sign, info); LoggerUtil.info("update attachment sign:" + remoteIp + sign + ", info-group:" + info.getGroup()); } } else {// 使用签名 String sign = attachments.get(ATTACHMENT_SIGN); if (StringUtils.isNotBlank(sign)) { info = SIGN_ATTACHMENT_MAP.get(remoteIp + sign); if (info != null) { // 把缓存中的信息添加到request的attachment中。 putAttachmentInfoMap(info, attachments); } else {// server端没有缓存sign对应的info,需要通知client重新发送info attachments.put(UN_ATTACHMENT_SIGN, sign); LoggerUtil.info("miss attachment sign:" + remoteIp + sign); } // 返回repsponse时ATTACHMENT_SIGN表示server端已缓存成功, // 如果client没有传递attachmentinfo,则没有必要回应。 attachments.remove(ATTACHMENT_SIGN); } else { LoggerUtil.warn("attachment sign is blank,application info miss!"); } } // 还原client requestid String clientRequestid = URLParamType.requestIdFromClient.getValue();// 默认值 if (attachments.containsKey(CLIENT_REQUESTID)) { clientRequestid = attachments.get(CLIENT_REQUESTID); } attachments.put(URLParamType.requestIdFromClient.getName(), clientRequestid); } } private Object[] decodeRequestParameter(ObjectInput input, String parameterDesc, Serialization serialization) throws IOException, ClassNotFoundException { if (parameterDesc == null || parameterDesc.equals("")) { return null; } Class<?>[] classTypes = ReflectUtil.forNames(parameterDesc); Object[] paramObjs = new Object[classTypes.length]; for (int i = 0; i < classTypes.length; i++) { paramObjs[i] = deserialize((byte[]) input.readObject(), classTypes[i], serialization); } return paramObjs; } private Map<String, String> decodeRequestAttachments(ObjectInput input) throws IOException, ClassNotFoundException { int size = input.readShort(); if (size <= 0) { return null; } Map<String, String> attachments = new HashMap<String, String>(); for (int i = 0; i < size; i++) { attachments.put(input.readUTF(), input.readUTF()); } return attachments; } /** * * @param body * @param dataType * @param requestId * @param rpcProtocolVersion rpc协议的版本号,不同版本可能有不同的序列化方式 * @param serialization * @return * @throws IOException * @throws ClassNotFoundException */ private Object decodeResponse(byte[] body, byte dataType, long requestId, byte rpcProtocolVersion, Serialization serialization) throws IOException, ClassNotFoundException { ObjectInput input = createInput(getInputStream(body)); long processTime = input.readLong(); DefaultResponse response = new DefaultResponse(); response.setRequestId(requestId); response.setProcessTime(processTime); if (dataType == MotanConstants.FLAG_RESPONSE_VOID) { return response; } String className = input.readUTF(); Class<?> clz = ReflectUtil.forName(className); Object result = deserialize((byte[]) input.readObject(), clz, serialization); if (dataType == MotanConstants.FLAG_RESPONSE) { response.setValue(result); } else if (dataType == MotanConstants.FLAG_RESPONSE_ATTACHMENT) { response.setValue(result); Map<String, String> attachment = decodeRequestAttachments(input); checkAttachment(attachment); } else if (dataType == MotanConstants.FLAG_RESPONSE_EXCEPTION) { response.setException((Exception) result); } else { throw new MotanFrameworkException("decode error: response dataType not support " + dataType, MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } response.setRequestId(requestId); input.close(); return response; } // 检查repsonse中是否有server端已缓存的sign或server尚未缓存的sign。 private void checkAttachment(Map<String, String> attachment) { if (attachment != null && !attachment.isEmpty()) { String acceptSign = attachment.get(ATTACHMENT_SIGN); if (StringUtils.isNotBlank(acceptSign)) {// attachment // sign已被server端缓存,则后续请求不用在传递固定的attachment ACCEPT_ATTACHMENT_SIGN.add(acceptSign); } String notAcceptSign = attachment.get(UN_ATTACHMENT_SIGN); if (StringUtils.isNotBlank(notAcceptSign)) {// 如果server端没有缓存sign对应的attachment信息,则下次请求时需要传递 ACCEPT_ATTACHMENT_SIGN.remove(notAcceptSign); } } } // 从request的attachments中获取AttachmentInfo,没有时返回null private AttachmentInfo getAttachmentInfoMap(Map<String, String> attachments) { AttachmentInfo result = null; if (attachments != null && attachments.containsKey(URLParamType.application.name())) { String group = attachments.get(URLParamType.group.name()); String application = attachments.get(URLParamType.application.name()); String module = attachments.get(URLParamType.module.name()); String version = attachments.get(URLParamType.version.name()); result = new AttachmentInfo(group, application, module, version); } return result; } private void putAttachmentInfoMap(AttachmentInfo attachmentInfo, Map<String, String> attachments) { if (attachments != null) { attachments.put(URLParamType.group.name(), attachmentInfo.getGroup()); attachments.put(URLParamType.application.name(), attachmentInfo.getApplication()); attachments.put(URLParamType.module.name(), attachmentInfo.getModule()); attachments.put(URLParamType.version.name(), attachmentInfo.getVersion()); } } private void removeAttachmentInfoMap(Map<String, String> attachments) { if (attachments != null) { attachments.remove(URLParamType.group.name()); attachments.remove(URLParamType.application.name()); attachments.remove(URLParamType.module.name()); attachments.remove(URLParamType.version.name()); } } /** * 获取输入流。兼容gzip * * @param data * @return */ public static InputStream getInputStream(byte[] data) { InputStream ret = new ByteArrayInputStream(data); try { GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(data)); return gis; } catch (Exception ignore) {} return ret; } // 对rpc body进行压缩。 public byte[] compress(byte[] org, boolean useGzip, int minGzSize) throws IOException { if (useGzip && org.length > minGzSize) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(outputStream); gos.write(org); gos.finish(); gos.flush(); gos.close(); byte[] ret = outputStream.toByteArray(); return ret; } else { return org; } } public static void putMethodSign(Provider<?> provider, List<Method> methods) { String group = provider.getUrl().getGroup(); String interfaceName = provider.getInterface().getName(); String version = provider.getUrl().getVersion(); for (Method method : methods) { MethodInfo temp = new MethodInfo(group, interfaceName, method.getName(), ReflectUtil.getMethodParamDesc(method), version); String sign = temp.getSign(); MethodInfo priInfo = SIGN_METHOD_MAP.putIfAbsent(sign, temp); if (priInfo != null && !temp.equals(priInfo)) {// 方法签名冲突 throw new MotanFrameworkException("add method sign conflict! " + temp.toString() + " with " + priInfo.toString(), MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } else { LoggerUtil.info("add method sign:" + sign + ", methodinfo:" + temp.toString()); } } } public static void putMethodSign(String methodSign, MethodInfo methodInfo) { SIGN_METHOD_MAP.putIfAbsent(methodSign, methodInfo); } static class MethodInfo { String group; String interfaceName; String methodName; String paramtersDesc; String version; public MethodInfo(String group, String interfaceName, String methodName, String paramtersDesc, String version) { super(); this.group = group; this.interfaceName = interfaceName; this.methodName = methodName; this.paramtersDesc = paramtersDesc; this.version = version; } /** * 根据方法信息生成对应的签名。 此方法会抛出异常,调用时根据使用情况决定是否处理异常。 * * @return * @throws Exception */ public String getSign() { try { StringBuilder sb = new StringBuilder(); sb.append(group).append(interfaceName).append(methodName).append(paramtersDesc).append(version); String surfix = MotanDigestUtil.md5LowerCase(sb.toString()).substring(8, 20); // 取32位md5的8-20位。 int endIndex = methodName.length() > 4 ? 4 : methodName.length(); String prefix = methodName.substring(0, endIndex); return prefix + surfix; } catch (Exception e) { throw new MotanFrameworkException("gen method sign error! " + this.toString(), MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR); } } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getInterfaceName() { return interfaceName; } public void setInterfaceName(String interfaceName) { this.interfaceName = interfaceName; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public String getParamtersDesc() { return paramtersDesc; } public void setParamtersDesc(String paramtersDesc) { this.paramtersDesc = paramtersDesc; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((group == null) ? 0 : group.hashCode()); result = prime * result + ((interfaceName == null) ? 0 : interfaceName.hashCode()); result = prime * result + ((methodName == null) ? 0 : methodName.hashCode()); result = prime * result + ((paramtersDesc == null) ? 0 : paramtersDesc.hashCode()); result = prime * result + ((version == null) ? 0 : version.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MethodInfo other = (MethodInfo) obj; if (group == null) { if (other.group != null) return false; } else if (!group.equals(other.group)) return false; if (interfaceName == null) { if (other.interfaceName != null) return false; } else if (!interfaceName.equals(other.interfaceName)) return false; if (methodName == null) { if (other.methodName != null) return false; } else if (!methodName.equals(other.methodName)) return false; if (paramtersDesc == null) { if (other.paramtersDesc != null) return false; } else if (!paramtersDesc.equals(other.paramtersDesc)) return false; if (version == null) { if (other.version != null) return false; } else if (!version.equals(other.version)) return false; return true; } @Override public String toString() { return "MethodInfo [group=" + group + ", interfaceName=" + interfaceName + ", methodName=" + methodName + ", paramtersDesc=" + paramtersDesc + ", version=" + version + "]"; } } static class AttachmentInfo { String group; String application; String module; String version; public AttachmentInfo(String group, String application, String module, String version) { super(); this.group = group; this.application = application; this.module = module; this.version = version; } public String getAttachmetnSign() { String signstr = group + application + module + version; String hashcodeStr = null; try { hashcodeStr = MotanDigestUtil.md5LowerCase(signstr).substring(8, 12); // 取md5中的四个字符。 } catch (Exception e) { LoggerUtil.warn("getAttachmetnSign fail!" + e.getMessage()); } return hashcodeStr; } public String getGroup() { return group; } public String getApplication() { return application; } public String getModule() { return module; } public String getVersion() { return version; } } }