/** * Project: dubbo.registry.client-1.1.0-SNAPSHOT * * File Created at 2010-4-22 * $Id: RouteUtils.java 181192 2012-06-21 05:05:47Z tony.chenl $ * * Copyright 2008 Alibaba.com Croporation Limited. * All rights reserved. * * This software is the confidential and proprietary information of * Alibaba Company. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Alibaba.com. */ package com.alibaba.dubbo.registry.common.route; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.dubbo.common.utils.StringUtils; import com.alibaba.dubbo.registry.common.domain.Provider; import com.alibaba.dubbo.registry.common.domain.Route; import com.alibaba.dubbo.registry.common.domain.Override; /** * RouteParser 路由规则解析。 * * @author ding.lid */ public class RouteUtils { public static boolean matchRoute(String consumerAddress, String consumerQueryUrl, Route route, Map<String, List<String>> clusters) { RouteRule rule = RouteRule.parseQuitely(route); Map<String, RouteRule.MatchPair> when = RouteRuleUtils.expandCondition( rule.getWhenCondition(), "consumer.cluster", "consumer.host", clusters); Map<String, String> consumerSample = ParseUtils.parseQuery("consumer.", consumerQueryUrl); final int index = consumerAddress.lastIndexOf(":"); String consumerHost = null; if(index != -1){ consumerHost = consumerAddress.substring(0, index); } else { consumerHost = consumerAddress; } consumerSample.put("consumer.host", consumerHost); return RouteRuleUtils.isMatchCondition(when, consumerSample, consumerSample); } public static Map<String, String> previewRoute(String serviceName, String consumerAddress, String queryUrl, Map<String, String> serviceUrls, Route route, Map<String, List<String>> clusters, List<Route> routed) { if(null == route) { throw new IllegalArgumentException("Route is null."); } List<Route> routes = new ArrayList<Route>(); routes.add(route); return route(serviceName, consumerAddress, queryUrl, serviceUrls, routes, clusters, routed); } /** * @return Map<methodName, Route> */ public static List<Route> findUsedRoute(String serviceName, String consumerAddress, String consumerQueryUrl, List<Route> routes, Map<String, List<String>> clusters) { List<Route> routed = new ArrayList<Route>(); Map<String, String> urls = new HashMap<String, String>(); urls.put("dubbo://" + consumerAddress + "/" + serviceName, consumerQueryUrl); RouteUtils.route(serviceName, consumerAddress, consumerQueryUrl, urls, routes, clusters, routed); return routed; } public static List<Provider> route(String serviceName, String consumerAddress, String consumerQueryUrl, List<Provider> providers, List<Override> overrides, List<Route> routes, Map<String, List<String>> clusters, List<Route> routed) { if (providers == null) { return null; } Map<String, String> urls = new HashMap<String, String>(); urls.put("consumer://" + consumerAddress + "/" + serviceName, consumerQueryUrl); // not empty dummy data for (Provider provider : providers) { if (com.alibaba.dubbo.governance.web.common.pulltool.Tool.isProviderEnabled(provider, overrides)) { urls.put(provider.getUrl(), provider.getParameters()); } } urls = RouteUtils.route(serviceName, consumerAddress, consumerQueryUrl, urls, routes, clusters, routed); List<Provider> result = new ArrayList<Provider>(); for (Provider provider : providers) { if (urls.containsKey(provider.getUrl())) { result.add(provider); } } return result; } /** * @param serviceName 服务名,如{@code com.alibaba.morgan.MemberService} * @param consumerAddress 消费者地址,如{@code 192.168.1.3:54333} * @param consumerQueryUrl 订阅参数,如 <code>aplication=nasdaq&dubbo=2.0.3&methods=updateItems,validateNew&revision=1.7.0</code> * @param serviceUrls 提供者 * @param routes 全部路由 * @param clusters 全部的集群 * @return 返回路由结果, Map<url-body, url-params> */ // FIXME clusters和routes的合并,可以在clusters或routes变化时预先做 // FIXME 从Util方法中分离出Cache的操作 public static Map<String, String> route(String serviceName, String consumerAddress, String consumerQueryUrl, Map<String, String> serviceUrls, List<Route> routes, Map<String, List<String>> clusters, List<Route> routed) { if (serviceUrls == null || serviceUrls.size() == 0) { return serviceUrls; } if (routes == null || routes.isEmpty()) { return serviceUrls; } Map<Long, RouteRule> rules = route2RouteRule(routes, clusters); final Map<String, String> consumerSample = ParseUtils.parseQuery("consumer.", consumerQueryUrl); final int index = consumerAddress.lastIndexOf(":"); final String consumerHost; if(consumerAddress != null && index != -1){ consumerHost = consumerAddress.substring(0, index); } else { consumerHost = consumerAddress; } consumerSample.put("consumer.host", consumerHost); Map<String, Map<String, String>> url2ProviderSample = new HashMap<String, Map<String, String>>(); for (Map.Entry<String, String> entry : serviceUrls.entrySet()) { URI uri; try { uri = new URI(entry.getKey()); } catch (URISyntaxException e) { throw new IllegalStateException("fail to parse url(" + entry.getKey() + "):" + e.getMessage(), e); } Map<String, String> sample = new HashMap<String, String>(); sample.putAll(ParseUtils.parseQuery("provider.", entry.getValue())); sample.put("provider.protocol", uri.getScheme()); sample.put("provider.host", uri.getHost()); sample.put("provider.port", String.valueOf(uri.getPort())); url2ProviderSample.put(entry.getKey(), sample); } Map<String, Set<String>> url2Methods = new HashMap<String, Set<String>>(); // consumer可以通过consumer.methods Key指定需要的方法 String methodsString = consumerSample.get("consumer.methods"); String[] methods = methodsString == null || methodsString.length() == 0 ? new String[]{Route.ALL_METHOD} : methodsString.split(ParseUtils.METHOD_SPLIT); for (String method : methods) { consumerSample.put("method", method); // NOTE: // <*方法>只配置 <no method key> // method1方法匹配 <no method key> 和 <method = method1>, 此时要把<no method key>的Route的优先级降低即可 if (routes != null && routes.size() > 0) { for (Route route : routes) { if (isSerivceNameMatched(route.getService(), serviceName)) { RouteRule rule = rules.get(route.getId()); // 当满足when条件时 if (rule != null && RouteRuleUtils.isMatchCondition( rule.getWhenCondition(), consumerSample, consumerSample)) { if (routed != null && ! routed.contains(route)) { routed.add(route); } Map<String, RouteRule.MatchPair> then = rule.getThenCondition(); if (then != null) { Map<String, Map<String, String>> tmp = getUrlsMatchedCondition(then, consumerSample, url2ProviderSample); // 如果规则的结果是空,则该规则无效,使用所有Provider if (route.isForce() || ! tmp.isEmpty()){ url2ProviderSample = tmp; } } } } } } for (String url : url2ProviderSample.keySet()) { Set<String> mts = url2Methods.get(url); if (mts == null) { mts = new HashSet<String>(); url2Methods.put(url, mts); } mts.add(method); } } // end of for methods return appendMethodsToUrls(serviceUrls, url2Methods); } static Map<Long, RouteRule> route2RouteRule(List<Route> routes, Map<String, List<String>> clusters) { Map<Long, RouteRule> rules = new HashMap<Long, RouteRule>(); // route -> RouteRule if (routes != null && routes.size() > 0) { for(Route route: routes) { rules.put(route.getId(), RouteRule.parseQuitely(route)); } } // expand the cluster parameters into conditions of routerule if (clusters != null && clusters.size() > 0) { Map<Long, RouteRule> rrs = new HashMap<Long, RouteRule>(); for (Map.Entry<Long, RouteRule> entry : rules.entrySet()) { RouteRule rr = entry.getValue(); Map<String, RouteRule.MatchPair> when = RouteRuleUtils.expandCondition( rr.getWhenCondition(), "consumer.cluster", "consumer.host", clusters); Map<String, RouteRule.MatchPair> then = RouteRuleUtils.expandCondition( rr.getThenCondition(), "provider.cluster", "provider.host", clusters); rrs.put(entry.getKey(), RouteRule.createFromCondition(when, then)); } rules = rrs; } return rules; } static Map<String, String> appendMethodsToUrls(Map<String, String> serviceUrls, Map<String, Set<String>> url2Methods) { // 为URL上加上方法参数 Map<String, String> results = new HashMap<String, String>(); for (Map.Entry<String, Set<String>> entry : url2Methods.entrySet()) { String url = entry.getKey(); String query = serviceUrls.get(url); Set<String> methodNames = entry.getValue(); if (methodNames != null && methodNames.size() > 0) { String ms = StringUtils.join(methodNames.toArray(new String[0]), ParseUtils.METHOD_SPLIT); query = ParseUtils.replaceParameter(query, "methods", ms); } results.put(url, query); } return results; } static Route getFirstRouteMatchedWhenConditionOfRule(String serviceName, Map<String, String> consumerSample, List<Route> routes, Map<Long, RouteRule> routeRuleMap) { if (serviceName == null || serviceName.length() == 0) { return null; } if (routes != null && routes.size() > 0) { for (Route route : routes) { if (isSerivceNameMatched(route.getService(), serviceName)) { RouteRule rule = routeRuleMap.get(route.getId()); // 当满足when条件时 if (rule != null && RouteRuleUtils.isMatchCondition( rule.getWhenCondition(), consumerSample, consumerSample)) { return route; // 第一个满足即返回 } } } } return null; } /** * 支持匹配的Service * * @param servicePattern * @param serviceName */ static boolean isSerivceNameMatched(String servicePattern, String serviceName) { final int pip = servicePattern.indexOf('/'); final int pi = serviceName.indexOf('/'); if(pip != -1) { // pattern有group if(pi == -1) return false; // servicename无group String gp = servicePattern.substring(0, pip); servicePattern = servicePattern.substring(pip + 1); String g = serviceName.substring(0, pi); if(!gp.equals(g)) return false; } if(pi != -1) serviceName = serviceName.substring(pi + 1); final int vip = servicePattern.lastIndexOf(':'); final int vi = serviceName.lastIndexOf(':'); if(vip != -1) { // pattern有group if(vi == -1) return false; String vp = servicePattern.substring(vip + 1); servicePattern = servicePattern.substring(0, vip); String v = serviceName.substring(vi + 1); if(!vp.equals(v)) return false; } if(vi != -1) serviceName = serviceName.substring(0, vi); return ParseUtils.isMatchGlobPattern(servicePattern, serviceName); } static Map<String, Map<String, String>> getUrlsMatchedCondition(Map<String, RouteRule.MatchPair> condition, Map<String, String> parameters, Map<String, Map<String, String>> url2Sample) { Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>(); for (Map.Entry<String, Map<String, String>> entry : url2Sample.entrySet()) { Map<String, String> sample = entry.getValue(); Map<String, String> params = new HashMap<String, String>(); params.putAll(sample); params.putAll(parameters); if (RouteRuleUtils.isMatchCondition(condition, params, sample)) { result.put(entry.getKey(), entry.getValue()); } } return result; } }