/// <summary> /// 获取请求信息 /// </summary> private async Task <CustomRouteData> GetRequestData(HttpRequest request) { string requestBodyStr = ""; using (var buffer = new MemoryStream()) { await Request.Body.CopyToAsync(buffer); requestBodyStr = Encoding.UTF8.GetString(buffer.ToArray()); } requestBody = requestBodyStr; //Get authorization information in the request headers, it needs send to the micro service. authorizationHeadValue = Request.Headers[Const.HEAD_NAME_AUTHORIZATION]; var requestHeadStr = Request.Headers[Const.HEAD_NAME_ROUTE_INFO]; var requestHeadDic = HttpHelper.GetFormDictionary(requestHeadStr); requestHead = new RequestHead { BusinessCode = requestHeadDic["BusinessCode"], Ttl = Convert.ToInt32(requestHeadDic.GetValueOrDefault("Ttl", "0")), Channel = requestHeadDic.GetValueOrDefault("Channel", ""), Version = requestHeadDic.GetValueOrDefault("Version", "") }; CustomRouteData route = routeHelper.GetOptimalRoute(requestHead.BusinessCode, requestHead.Version, requestHead.Channel); if (route == null) { throw new Exception("请求路由不存在"); } return(route); }
async Task <HttpResult> HandleRequest(CustomRouteData route) { HttpResult result = new HttpResult(); int expire = requestHead.Ttl.GetValueOrDefault(); if (expire > 0 && route.Cache) { string key = GeneralCacheKey(); var cacheValue = cache.Get(key); if (cacheValue != null) { result.Content = cacheValue.ToString(); result.HttpStatus = 200; fromCache = true; } else { result = await HttpHelper.PostAsync(route.Handle, requestBody, authorizationHeadValue); if (result.HttpStatus == 200) { cache.Set(key, result.Content, TimeSpan.FromSeconds(expire)); } fromCache = false; } } else { result = await HttpHelper.PostAsync(route.Handle, requestBody, authorizationHeadValue); fromCache = false; } return(result); }
public async Task <string> Post() { var routeSetting = await GetRequestData(Request); var requestResult = new HttpResult(); if (routeSetting.RetryTimes > 0) { var policyHandle = Policy.HandleResult <HttpResult>(o => o.HttpStatus != 200) .RetryAsync(routeSetting.RetryTimes, (ex, count) => { logger.Error($"执行{routeSetting.BusinessCode}失败! 重试次数 {count}"); logger.Error($"异常来自 {ex.Result.Content},错误码 {ex.Result.HttpStatus}"); }); requestResult = await policyHandle.ExecuteAsync(() => { optimalRoute = GetLoadBalanceRoute(routeSetting); logger.Debug($"begin request [{optimalRoute.Handle}],resquestHead:{ Request.Headers[Const.HEAD_NAME_ROUTE_INFO]} , requestBody:{requestBody} ,AuthorizationHead:{authorizationHeadValue}"); return(HandleRequest(optimalRoute)); }); logger.Debug($"end request [{optimalRoute.Handle}],from cache {fromCache.ToString()}"); } else { optimalRoute = GetLoadBalanceRoute(routeSetting); requestResult = await HandleRequest(optimalRoute); } if (requestResult.HttpStatus != 200) { Response.StatusCode = requestResult.HttpStatus; } return(requestResult.Content); }
/// <summary> /// 校验是否启用缓存 /// </summary> /// <param name="userCache">请求启用缓存字段</param> /// <param name="channel">请求渠道</param> /// <param name="route">最优路由</param> /// <param name="body">请求参数</param> /// <returns></returns> protected bool CheckUseCache(bool?userCache, string channel, CustomRouteData route, Dictionary <string, object> body) { //启用缓存条件 //- 请求Head参数UserCache:true //- 路由缓存时间配置大于0 //- 渠道不为null,且渠道不在忽略的列表(IgnoreCacheChannel)中 //- 满足请求条件,满足其一即可: //-- 请求Body无参数且路由缓存条件不存在 //-- 请求body含参数且路由缓存存在条件且请求body所有非空字段都包含在路由缓存条件中 if (!userCache.HasValue || userCache.Value == false) { return(false); } if (SettingsHelper.IgnoreCacheChannel(channel)) { return(false); } if (route.CacheTime == 0) { return(false); } if (body == null || body.Count == 0) { return(true); } if (route.CacheCondition == null) { return(false); } List <Tuple <string, bool> > parms = new List <Tuple <string, bool> >(); foreach (var p in body) { if (p.Value != null) { string cValue = string.Empty; if (!route.CacheCondition.TryGetValue(p.Key, out cValue)) { cValue = string.Empty; } var parm = Tuple.Create(p.Key, cValue.Split(',').Contains(p.Value.ToString())); parms.Add(parm); } } if (parms.Count(o => o.Item2 == true) == parms.Count) { return(true); } return(false); }
public BaseModule() { DateTime elapsedTime = DateTime.Now; bool ignoreLog = false; Before += ctx => { var route = GetRequestData(ctx.Request); OptimalRoute = route; HeadData.Authorization = RouteHelper.CreateToken(route); ignoreLog = SettingsHelper.IgnoreLogChannel(HeadData.Channel); return(null); }; After += ctx => { if (!ignoreLog) { string response; using (MemoryStream respData = new MemoryStream()) { ctx.Response.Contents(respData); response = Encoding.UTF8.GetString(respData.ToArray()); } LogHelper.Info(HeadData.Command, string.Format( "Route request successfully,Address:{0},Time:{1}(s),Head:{2},Body:{3},RouteData:{4},Response:{5},UseCache:{6}", Request.Url, (DateTime.Now - elapsedTime).TotalSeconds, JsonConvert.SerializeObject(HeadData), JsonConvert.SerializeObject(BodyData), JsonConvert.SerializeObject(OptimalRoute), response, FinalUseCache)); } }; OnError += (ctx, ex) => { if (!ignoreLog) { LogHelper.Error( string.Format("Route request Error,Command{0}", HeadData == null ? "" : HeadData.Command), string.Format( "Route request error,Address:{0},End time:{1},Head:{2},Body:{3},RouteData:{4},Error Message:{5}", Request.Url, DateTime.Now, JsonConvert.SerializeObject(HeadData), JsonConvert.SerializeObject(BodyData), JsonConvert.SerializeObject(OptimalRoute), ex.Message), ex); } dynamic response = new ExpandoObject(); response.Code = "500"; response.ErrorMessage = string.Format("Route请求异常,Message:{0}", ex.Message); return(JsonConvert.SerializeObject(response)); }; }
/// <summary> /// Instance Constructor /// </summary> public RouteViewService() { var componentParameters = new SortedDictionary <string, object>(); componentParameters.Add("ID", 0); var route = new CustomRouteData() { PageType = typeof(Counter), RouteMatch = @"^\/counted\/(\d+)", ComponentParameters = componentParameters }; Routes.Add(route); Routes.Add(new CustomRouteData() { PageType = typeof(Counter), RouteMatch = @"^\/counters" }); Routes.Add(new CustomRouteData() { PageType = typeof(RouteViewer), RouteMatch = @"^\/routeviewer" }); }
/// <summary> /// 路由负载均衡 /// </summary> /// <param name="routeData"></param> /// <returns></returns> public CustomRouteData RoutingLoadBalance( CustomRouteData routeData) { var route = new CustomRouteData { BusinessCode = routeData.BusinessCode, MicroService = routeData.MicroService, Description = routeData.Description, Channel = routeData.Channel, Version = routeData.Version }; var hostData = GetHostDatas().FirstOrDefault(x => routeData.MicroService == x.MicroService); if (hostData != null) { var randomHost = RandomHelper.GetRandomList(hostData.Hosts.ToList(), 1).First(); route.Handle = string.Concat(randomHost.ServiceUrl, routeData.Handle); } return(route); }
/// <summary> /// 获取请求信息 /// </summary> private CustomRouteData GetRequestData(Request request) { //- Head var head = request.Headers["head"].FirstOrDefault(); if (head == null) { throw new Exception("请求报文头数据不存在或格式不正确"); } head = Encoding.UTF8.GetString(EncodingHelper.Base64UrlDecode(head)); HeadData = JsonConvert.DeserializeObject <RequestHead>(head); if (HeadData == null) { throw new Exception("请求报文头数据不存在"); } if (string.IsNullOrEmpty(HeadData.Command)) { throw new Exception("请求报文头指令名称不能为空"); } //- Body var bodyForm = request.Form["body"]; if (!string.IsNullOrWhiteSpace(bodyForm)) { string key = SettingsHelper.GetDesKey(HeadData.Channel); bodyForm = EncryptHelper.DESDecrypt(bodyForm, key); string bodyStr = Encoding.UTF8.GetString(EncodingHelper.Base64UrlDecode(bodyForm)); BodyData = JsonConvert.DeserializeObject <Dictionary <string, object> >(bodyStr); } //- Route CustomRouteData route = RouteHelper.GetOptimalRoute(HeadData.Command, HeadData.Version, HeadData.System); if (route == null) { throw new Exception("请求路由不存在"); } route = RouteHelper.RoutingLoadBalance(route); return(route); }
/// <summary> /// 路由负载均衡 /// </summary> /// <param name="routeData"></param> /// <returns></returns> public static CustomRouteData RoutingLoadBalance( CustomRouteData routeData) { var route = new CustomRouteData { CacheCondition = routeData.CacheCondition, CacheTime = routeData.CacheTime, Command = routeData.Command, MicroService = routeData.MicroService, Name = routeData.Name, System = routeData.System, Version = routeData.Version }; var hostData = GetHostDatas().FirstOrDefault(x => routeData.MicroService == x.Name); if (hostData != null) { var randomHost = RandomHelper.GetRandomList(hostData.Hosts.ToList(), 1).First(); route.Handle = string.Concat(randomHost.ServiceUrl, routeData.Handle); } return(route); }
public static string CreateToken(CustomRouteData route) { var host = GetHostDatas().First(o => o.Name == route.MicroService); IDateTimeProvider provider = new UtcDateTimeProvider(); var now = provider.GetNow(); var unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); // or use JwtValidator.UnixEpoch var secondsSinceEpoch = Math.Round((now - unixEpoch).TotalSeconds) + 60; //60S后过期 var payload = new Dictionary <string, object> { { "app_id", host.ApplicationId }, { "exp", secondsSinceEpoch } }; var secret = host.ApplicationKey; IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); var token = encoder.Encode(payload, secret); return(token); }
/// <summary> /// 请求处理 /// </summary> /// <param name="command">请求指令</param> /// <param name="head">请求报文头</param> /// <param name="route">最优路由</param> /// <param name="body">请求参数</param> /// <returns></returns> protected async Task <string> HandleRequest(string command, RequestHead head, CustomRouteData route, Dictionary <string, object> body) { string response; // 根据请求参数判断是否启用缓存 // 启用-生成缓存KEY,并尝试读取缓存,成功则返回缓存值,失败则转发请求 // 不启用-转发请求 bool isUseCache = CheckUseCache(head.UseCache, head.Channel, route, body); if (isUseCache) { string key = GeneralCacheKey(command, head.Version, head.System, route, body); var cacheValue = CacheHelper.Get(key); if (cacheValue != null) { response = cacheValue; FinalUseCache = true; } else { response = await HttpClient.PostAsync(route.Handle, head, body); CacheHelper.Set(key, response, new TimeSpan(0, 0, route.CacheTime)); FinalUseCache = false; } } else { response = await HttpClient.PostAsync(route.Handle, head, body); FinalUseCache = false; } return(response); }
/// <summary> /// 生成缓存Key /// </summary> /// <param name="command">请求命令</param> /// <param name="version">请求版本</param> /// <param name="system">请求系统</param> /// <param name="route">最优路由</param> /// <param name="body">请求参数</param> /// <returns></returns> protected string GeneralCacheKey(string command, string version, string system, CustomRouteData route, Dictionary <string, object> body) { // 缓存key格式: // home.banner_1.0.0_pc_condition1=value1_condition2=value2 string key = string.Join("_", command, version, system); if (route.CacheCondition != null) { List <string> userCondition = new List <string>(); foreach (var condition in route.CacheCondition) { if (body != null && body.Keys.Contains(condition.Key, StringComparer.OrdinalIgnoreCase)) { var bodyVal = body.First(x => string.Equals(x.Key, condition.Key, StringComparison.OrdinalIgnoreCase)).Value.ToString(); if (condition.Value.Contains(bodyVal)) { string val = string.Format("{0}={1}", condition.Key, bodyVal); userCondition.Add(val); } } } if (userCondition.Count > 0) { key = string.Join("_", key, string.Join("_", userCondition.ToArray())); } } return(key.ToLower()); }
private CustomRouteData GetLoadBalanceRoute(CustomRouteData route) { return(routeHelper.RoutingLoadBalance(route)); }