電動小客機飛行實驗 加拿大圓滿落幕

摘錄自2019年12月12日公視報導

加拿大一家航空業者,在溫哥華完成全球第一次,「全電動」客機的商業飛行試驗。雖然這架小飛機只是六人座,目前飛行距離的上限也只有160公里,但業者表示這項創舉代表電動航空時代的來臨。未來將可以為航空公司節省大量成本,同時達到零碳排的目標。

一架可以搭載六名乘客的水上飛機,10號在加拿大溫哥華完成了首趟全電動飛行試驗,預示電動客機的時代,即將來臨。加拿大港灣航空公司執行長麥克杜格爾表示,「這次飛行試驗意義重大。我的意思是,這是全電動客機邁向實際獲准飛行的第一步。」

第一次飛行試驗圓滿落幕,但是加拿大港灣航空公司,至少還要再等上兩年,進一步確認飛行安全無虞,並且獲得監管單位准許之後,才能展開下一步計畫。將旗下超過40台水上飛機全部電動化,真正載客上路、達成節能減碳的目標。目前,電池續航力仍是電動客機的一大挑戰,像這次飛行試驗我使用的水上飛機,最多只能靠鋰電池飛行160公里。當天整個飛行時間也僅持續約15分鐘。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

挪威大力提倡電動汽車 9月Tesla銷量奪冠

美國電動汽車廠商特斯拉(Tesla),在挪威推出電動轎車Model S才2個月,需求即湧現,甚至9月榮登挪威最熱賣車款,市場認為這跟挪威政府大力補貼電動車有莫大關係。

特斯拉9月在挪威賣出616輛Model S,在挪威9月汽車總銷售裡占5.1%。至於日產電動車Leaf,1 ~9月為挪威包括汽油等各車種裡,暢銷排名第5的車款,在挪威整體車市裡占約3%。外界估計目前在挪威路上行駛的電動車達14,500輛。根據美國電力驅動運輸協會(EDTA)資料,美國去年僅賣出13,427輛電動車。

挪威大力提倡電動車,成為其他推廣電動車國家的一個典範。挪威首都奧斯陸市長史坦(Fabian Stang),最近宣布奧斯陸為「電動車之都」。他說,挪威對電動車立場,是對環保所做更大承諾的一部份。挪威政府估計,該國為全球人均電動車比例最高的國家。挪威全國人口約500萬人。

外界認為挪威有規模7,500億美元的主權財富基金做後盾,才能提供許多補貼來大力提倡電動車,使許多國家望塵莫及。其主權基金是以石油收益來建立,外界估計是全球規模最大的主權基金。

挪威給電動車豁免領牌費和25%增值稅,如此每輛電動車的車主立即省下數萬美元;豁免通行費,並可在奧斯陸的高速公路上使用公車專用道,能在最繁忙時段避開塞車之苦。挪威汽油價格為歐洲最高,每公升汽油15.16挪威克朗(約74.6台幣),讓電動車有很大吸引力。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

基於領域驅動設計(DDD)超輕量級快速開發架構(二)動態linq查詢的實現方式

-之動態查詢,查詢邏輯封裝復用

基於領域驅動設計(DDD)超輕量級快速開發架構詳細介紹請看

https://www.cnblogs.com/neozhu/p/13174234.html

需求

  1. 配合EasyUI datagird filter實現多字段(任意字段)的篩選
  2. 根據業務需求篩選特定的狀態或條件,如:查看結案的訂單,最近30天的訂單,查看屬於我的訂單.等等,這些邏輯是固定也是可以被重用,但又不想每次寫相同的條件,那麼下面我會給我的解決方案.

需求1隻是一個偷懶的實現方式,因為datagrid自帶這個功能,但又不想根據具體的需求來畫查詢條件,如果需求必須要再datagrid上面做一塊查詢條件的輸入那目前只能在前端自己手工添加,在組織後傳入後台,暫時不在這裏討論

需求2可能不太好解釋,看完代碼就自然理解為什麼要這麼做了,這麼做的好處有哪些

具體實現的方式

 

 默認情況下 datagrid 有幾列就可以對這幾列進行篩選,對於日期型的字段會採用between,選擇2個時間之間進行篩選,数字類型會提供大於小於等符號選擇,可以自行嘗試,其原理是datagrid 會根據datagrid 頭部輸入的值生成一個Json字符串發送後台請求數據

JSON:格式

filterRules: [
{field:field,op:op,value:value},
{field:field,op:op,value:value},
] 
  • 通常的做法是一個一個判斷加條件
  1 var filters = JsonConvert.DeserializeObject<IEnumerable<filterRule>>(filterRules); 
  2 foreach (var rule in filters)
  3         {
  4           if (rule.field == "Id" && !string.IsNullOrEmpty(rule.value) && rule.value.IsInt())
  5           {
  6             var val = Convert.ToInt32(rule.value);
  7             switch (rule.op)
  8             {
  9               case "equal":
 10                 this.And(x => x.Id == val);
 11                 break;
 12               case "notequal":
 13                 this.And(x => x.Id != val);
 14                 break;
 15               case "less":
 16                 this.And(x => x.Id < val);
 17                 break;
 18               case "lessorequal":
 19                 this.And(x => x.Id <= val);
 20                 break;
 21               case "greater":
 22                 this.And(x => x.Id > val);
 23                 break;
 24               case "greaterorequal":
 25                 this.And(x => x.Id >= val);
 26                 break;
 27               default:
 28                 this.And(x => x.Id == val);
 29                 break;
 30             }
 31           }
 32           if (rule.field == "Name" && !string.IsNullOrEmpty(rule.value))
 33           {
 34             this.And(x => x.Name.Contains(rule.value));
 35           }
 36           if (rule.field == "Code" && !string.IsNullOrEmpty(rule.value))
 37           {
 38             this.And(x => x.Code.Contains(rule.value));
 39           }
 40 
 41           if (rule.field == "Address" && !string.IsNullOrEmpty(rule.value))
 42           {
 43             this.And(x => x.Address.Contains(rule.value));
 44           }
 45 
 46           if (rule.field == "Contect" && !string.IsNullOrEmpty(rule.value))
 47           {
 48             this.And(x => x.Contect.Contains(rule.value));
 49           }
 50 
 51           if (rule.field == "PhoneNumber" && !string.IsNullOrEmpty(rule.value))
 52           {
 53             this.And(x => x.PhoneNumber.Contains(rule.value));
 54           }
 55 
 56           if (rule.field == "RegisterDate" && !string.IsNullOrEmpty(rule.value))
 57           {
 58             if (rule.op == "between")
 59             {
 60               var datearray = rule.value.Split(new char[] { '-' });
 61               var start = Convert.ToDateTime(datearray[0]);
 62               var end = Convert.ToDateTime(datearray[1]);
 63 
 64               this.And(x => SqlFunctions.DateDiff("d", start, x.RegisterDate) >= 0);
 65               this.And(x => SqlFunctions.DateDiff("d", end, x.RegisterDate) <= 0);
 66             }
 67           }
 68           if (rule.field == "CreatedDate" && !string.IsNullOrEmpty(rule.value))
 69           {
 70             if (rule.op == "between")
 71             {
 72               var datearray = rule.value.Split(new char[] { '-' });
 73               var start = Convert.ToDateTime(datearray[0]);
 74               var end = Convert.ToDateTime(datearray[1]);
 75 
 76               this.And(x => SqlFunctions.DateDiff("d", start, x.CreatedDate) >= 0);
 77               this.And(x => SqlFunctions.DateDiff("d", end, x.CreatedDate) <= 0);
 78             }
 79           }
 80 
 81 
 82           if (rule.field == "CreatedBy" && !string.IsNullOrEmpty(rule.value))
 83           {
 84             this.And(x => x.CreatedBy.Contains(rule.value));
 85           }
 86 
 87          if (rule.field == "LastModifiedDate" && !string.IsNullOrEmpty(rule.value))
 88           {
 89             if (rule.op == "between")
 90             {
 91               var datearray = rule.value.Split(new char[] { '-' });
 92               var start = Convert.ToDateTime(datearray[0]);
 93               var end = Convert.ToDateTime(datearray[1]);
 94 
 95               this.And(x => SqlFunctions.DateDiff("d", start, x.LastModifiedDate) >= 0);
 96               this.And(x => SqlFunctions.DateDiff("d", end, x.LastModifiedDate) <= 0);
 97             }
 98           }
 99 
100           if (rule.field == "LastModifiedBy" && !string.IsNullOrEmpty(rule.value))
101           {
102             this.And(x => x.LastModifiedBy.Contains(rule.value));
103           }
104 
105         }

View Code

  • 新的做法是動態根據field,op,value生成一個linq 表達式,不用再做繁瑣的判斷,這塊代碼也可以被其它項目使用,非常好用
namespace SmartAdmin
{
 
  public static class PredicateBuilder
  {

    public static Expression<Func<T, bool>> FromFilter<T>(string filtergroup) {
      Expression<Func<T, bool>> any = x => true;
      if (!string.IsNullOrEmpty(filtergroup))
      {
        var filters = JsonSerializer.Deserialize<filter[]>(filtergroup);

          foreach (var filter in filters)
          {
            if (Enum.TryParse(filter.op, out OperationExpression op) && !string.IsNullOrEmpty(filter.value))
            {
              var expression = GetCriteriaWhere<T>(filter.field, op, filter.value);
              any = any.And(expression);
            }
          }
      }

      return any;
    }

    #region -- Public methods --
    public static Expression<Func<T, bool>> GetCriteriaWhere<T>(Expression<Func<T, object>> e, OperationExpression selectedOperator, object fieldValue)
    {
      var name = GetOperand<T>(e);
      return GetCriteriaWhere<T>(name, selectedOperator, fieldValue);
    }

    public static Expression<Func<T, bool>> GetCriteriaWhere<T, T2>(Expression<Func<T, object>> e, OperationExpression selectedOperator, object fieldValue)
    {
      var name = GetOperand<T>(e);
      return GetCriteriaWhere<T, T2>(name, selectedOperator, fieldValue);
    }

    public static Expression<Func<T, bool>> GetCriteriaWhere<T>(string fieldName, OperationExpression selectedOperator, object fieldValue)
    {
      var props = TypeDescriptor.GetProperties(typeof(T));
      var prop = GetProperty(props, fieldName, true);
      var parameter = Expression.Parameter(typeof(T));
      var expressionParameter = GetMemberExpression<T>(parameter, fieldName);
      if (prop != null && fieldValue != null)
      {
       
        BinaryExpression body = null;
        switch (selectedOperator)
        {
          case OperationExpression.equal:
            body = Expression.Equal(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType)?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.notequal:
            body = Expression.NotEqual(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.less:
            body = Expression.LessThan(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.lessorequal:
            body = Expression.LessThanOrEqual(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.greater:
            body = Expression.GreaterThan(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.greaterorequal:
            body = Expression.GreaterThanOrEqual(expressionParameter, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(body, parameter);
          case OperationExpression.contains:
            var contains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            var bodyLike = Expression.Call(expressionParameter, contains, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(bodyLike, parameter);
          case OperationExpression.endwith:
            var endswith = typeof(string).GetMethod("EndsWith",new[] { typeof(string) });
            var bodyendwith = Expression.Call(expressionParameter, endswith, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(bodyendwith, parameter);
          case OperationExpression.beginwith:
            var startswith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
            var bodystartswith = Expression.Call(expressionParameter, startswith, Expression.Constant(Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType), prop.PropertyType));
            return Expression.Lambda<Func<T, bool>>(bodystartswith, parameter);
          case OperationExpression.includes:
            return Includes<T>(fieldValue, parameter, expressionParameter, prop.PropertyType);
          case OperationExpression.between:
            return Between<T>(fieldValue, parameter, expressionParameter, prop.PropertyType);
          default:
            throw new Exception("Not implement Operation");
        }
      }
      else
      {
        Expression<Func<T, bool>> filter = x => true;
        return filter;
      }
    }

    public static Expression<Func<T, bool>> GetCriteriaWhere<T, T2>(string fieldName, OperationExpression selectedOperator, object fieldValue)
    {


      var props = TypeDescriptor.GetProperties(typeof(T));
      var prop = GetProperty(props, fieldName, true);

      var parameter = Expression.Parameter(typeof(T));
      var expressionParameter = GetMemberExpression<T>(parameter, fieldName);

      if (prop != null && fieldValue != null)
      {
        switch (selectedOperator)
        {
          case OperationExpression.any:
            return Any<T, T2>(fieldValue, parameter, expressionParameter);

          default:
            throw new Exception("Not implement Operation");
        }
      }
      else
      {
        Expression<Func<T, bool>> filter = x => true;
        return filter;
      }
    }



    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> or)
    {
      if (expr == null)
      {
        return or;
      }

      return Expression.Lambda<Func<T, bool>>(Expression.OrElse(new SwapVisitor(expr.Parameters[0], or.Parameters[0]).Visit(expr.Body), or.Body), or.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> and)
    {
      if (expr == null)
      {
        return and;
      }

      return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(new SwapVisitor(expr.Parameters[0], and.Parameters[0]).Visit(expr.Body), and.Body), and.Parameters);
    }

    #endregion
    #region -- Private methods --

    private static string GetOperand<T>(Expression<Func<T, object>> exp)
    {
      if (!( exp.Body is MemberExpression body ))
      {
        var ubody = (UnaryExpression)exp.Body;
        body = ubody.Operand as MemberExpression;
      }

      var operand = body.ToString();

      return operand.Substring(2);

    }

    private static MemberExpression GetMemberExpression<T>(ParameterExpression parameter, string propName)
    {
      if (string.IsNullOrEmpty(propName))
      {
        return null;
      }

      var propertiesName = propName.Split('.');
      if (propertiesName.Count() == 2)
      {
        return Expression.Property(Expression.Property(parameter, propertiesName[0]), propertiesName[1]);
      }

      return Expression.Property(parameter, propName);
    }

    private static Expression<Func<T, bool>> Includes<T>(object fieldValue, ParameterExpression parameterExpression, MemberExpression memberExpression ,Type type)
    {
      var safetype= Nullable.GetUnderlyingType(type) ?? type;

      switch (safetype.Name.ToLower())
      {
        case  "string":
          var strlist = (IEnumerable<string>)fieldValue;
          if (strlist == null || strlist.Count() == 0)
          {
            return x => true;
          }
          var strmethod = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });
          var strcallexp = Expression.Call(Expression.Constant(strlist.ToList()), strmethod, memberExpression);
          return Expression.Lambda<Func<T, bool>>(strcallexp, parameterExpression);
        case "int32":
          var intlist = (IEnumerable<int>)fieldValue;
          if (intlist == null || intlist.Count() == 0)
          {
            return x => true;
          }
          var intmethod = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
          var intcallexp = Expression.Call(Expression.Constant(intlist.ToList()), intmethod, memberExpression);
          return Expression.Lambda<Func<T, bool>>(intcallexp, parameterExpression);
        case "float":
          var floatlist = (IEnumerable<float>)fieldValue;
          if (floatlist == null || floatlist.Count() == 0)
          {
            return x => true;
          }
          var floatmethod = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
          var floatcallexp = Expression.Call(Expression.Constant(floatlist.ToList()), floatmethod, memberExpression);
          return Expression.Lambda<Func<T, bool>>(floatcallexp, parameterExpression);
        default:
          return x => true;
      }
      
    }
    private static Expression<Func<T, bool>> Between<T>(object fieldValue, ParameterExpression parameterExpression, MemberExpression memberExpression, Type type)
    {
      
      var safetype = Nullable.GetUnderlyingType(type) ?? type;
      switch (safetype.Name.ToLower())
      {
        case "datetime":
          var datearray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var start = Convert.ToDateTime(datearray[0] + " 00:00:00", CultureInfo.CurrentCulture);
          var end = Convert.ToDateTime(datearray[1] + " 23:59:59", CultureInfo.CurrentCulture);
          var greater = Expression.GreaterThan(memberExpression, Expression.Constant(start, type));
          var less = Expression.LessThan(memberExpression, Expression.Constant(end, type));
          return Expression.Lambda<Func<T, bool>>(greater, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(less, parameterExpression));
        case "int":
        case "int32":
          var intarray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var min = Convert.ToInt32(intarray[0] , CultureInfo.CurrentCulture);
          var max = Convert.ToInt32(intarray[1], CultureInfo.CurrentCulture);
          var maxthen = Expression.GreaterThan(memberExpression, Expression.Constant(min, type));
          var minthen = Expression.LessThan(memberExpression, Expression.Constant(max, type));
          return Expression.Lambda<Func<T, bool>>(maxthen, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(minthen, parameterExpression));
        case "decimal":
          var decarray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var dmin = Convert.ToDecimal(decarray[0], CultureInfo.CurrentCulture);
          var dmax = Convert.ToDecimal(decarray[1], CultureInfo.CurrentCulture);
          var dmaxthen = Expression.GreaterThan(memberExpression, Expression.Constant(dmin, type));
          var dminthen = Expression.LessThan(memberExpression, Expression.Constant(dmax, type));
          return Expression.Lambda<Func<T, bool>>(dmaxthen, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(dminthen, parameterExpression));
        case "float":
          var farray = ((string)fieldValue).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var fmin = Convert.ToDecimal(farray[0], CultureInfo.CurrentCulture);
          var fmax = Convert.ToDecimal(farray[1], CultureInfo.CurrentCulture);
          var fmaxthen = Expression.GreaterThan(memberExpression, Expression.Constant(fmin, type));
          var fminthen = Expression.LessThan(memberExpression, Expression.Constant(fmax, type));
          return Expression.Lambda<Func<T, bool>>(fmaxthen, parameterExpression)
            .And(Expression.Lambda<Func<T, bool>>(fminthen, parameterExpression));
        case "string":
          var strarray = ( (string)fieldValue ).Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
          var smin = strarray[0];
          var smax = strarray[1];
        
          var strmethod = typeof(string).GetMethod("Contains");
          var mm = Expression.Call(memberExpression, strmethod, Expression.Constant(smin, type));
          var nn = Expression.Call(memberExpression, strmethod, Expression.Constant(smax, type));


          return Expression.Lambda<Func<T, bool>>(mm, parameterExpression)
            .Or(Expression.Lambda<Func<T, bool>>(nn, parameterExpression));
        default:
          return x => true;
      }

    }



    private static Expression<Func<T, bool>> Any<T, T2>(object fieldValue, ParameterExpression parameterExpression, MemberExpression memberExpression)
    {
      var lambda = (Expression<Func<T2, bool>>)fieldValue;
      var anyMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
      .First(m => m.Name == "Any" && m.GetParameters().Count() == 2).MakeGenericMethod(typeof(T2));

      var body = Expression.Call(anyMethod, memberExpression, lambda);

      return Expression.Lambda<Func<T, bool>>(body, parameterExpression);
    }

    private static PropertyDescriptor GetProperty(PropertyDescriptorCollection props, string fieldName, bool ignoreCase)
    {
      if (!fieldName.Contains('.'))
      {
        return props.Find(fieldName, ignoreCase);
      }

      var fieldNameProperty = fieldName.Split('.');
      return props.Find(fieldNameProperty[0], ignoreCase).GetChildProperties().Find(fieldNameProperty[1], ignoreCase);

    }
    #endregion
  }

  internal class SwapVisitor : ExpressionVisitor
  {
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to)
    {
      this.from = from;
      this.to = to;
    }
    public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
  }
  public enum OperationExpression
  {
    equal,
    notequal,
    less,
    lessorequal,
    greater,
    greaterorequal,
    contains,
    beginwith,
    endwith,
    includes,
    between,
    any
  }
}

View Code

 1 public async Task<JsonResult> GetData(int page = 1, int rows = 10, string sort = "Id", string order = "asc", string filterRules = "")
 2     {
 3       try
 4       {
 5         var filters = PredicateBuilder.FromFilter<Company>(filterRules);
 6         var total = await this.companyService
 7                              .Query(filters)
 8                              .AsNoTracking()
 9                              .CountAsync()
10                               ;
11         var pagerows = (await this.companyService
12                              .Query(filters)
13                               .AsNoTracking()
14                            .OrderBy(n => n.OrderBy(sort, order))
15                            .Skip(page - 1).Take(rows)
16                            .SelectAsync())
17                            .Select(n => new
18                            {
19                              Id = n.Id,
20                              Name = n.Name,
21                              Code = n.Code,
22                              Address = n.Address,
23                              Contect = n.Contect,
24                              PhoneNumber = n.PhoneNumber,
25                              RegisterDate = n.RegisterDate.ToString("yyyy-MM-dd HH:mm:ss")
26                            }).ToList();
27         var pagelist = new { total = total, rows = pagerows };
28         return Json(pagelist);
29       }
30       catch(Exception e) {
31         throw e;
32         }
33 
34     }

配合使用的代碼

  • 對於固定查詢邏輯的封裝和復用,當然除了復用還可以明顯的提高代碼的可讀性.
public class OrderSalesQuery : QueryObject<Order>
{
    public decimal Amount { get; set; }
    public string Country { get; set; }
    public DateTime FromDate { get; set; }
    public DateTime ToDate { get; set; }

    public override Expression<Func<Order, bool>> Query()
    {
        return (x => 
            x.OrderDetails.Sum(y => y.UnitPrice) > Amount &&
            x.OrderDate >= FromDate &&
            x.OrderDate <= ToDate &&
            x.ShipCountry == Country);
    }
}

查看訂單的銷售情況,條件 金額,國家,日期

var orderRepository = new Repository<Order>(this);
    
var orders = orderRepository
    .Query(new OrderSalesQuery(){ 
        Amount = 100, 
        Country = "USA",
        FromDate = DateTime.Parse("01/01/1996"), 
        ToDate = DateTime.Parse("12/31/1996" )
    })
    .Select();

調用查詢方法

public class CustomerLogisticsQuery : QueryObject<Customer>
{
    public CustomerLogisticsQuery FromCountry(string country)
    {
        Add(x => x.Country == country);
        return this;
    }

    public CustomerLogisticsQuery LivesInCity(string city)
    {   
        Add(x => x.City == city);
        return this;
    }
}

客戶查詢 根據國家和城市查詢

public class CustomerSalesQuery : QueryObject<Customer>
{
    public CustomerSalesQuery WithPurchasesMoreThan(decimal amount)
    {
        Add(x => x.Orders
            .SelectMany(y => y.OrderDetails)
            .Sum(z => z.UnitPrice * z.Quantity) > amount);

        return this;
    }

    public CustomerSalesQuery WithQuantitiesMoreThan(decimal quantity)
    {
        Add(x => x.Orders
            .SelectMany(y => y.OrderDetails)
            .Sum(z => z.Quantity) > quantity);

        return this;
    }
}

客戶的銷售情況,金額和數量

var customerRepository = new Repository<Customer>(this);

var query1 = new CustomerLogisticsQuery()
    .LivesInCity("London");

var query2 = new CustomerSalesQuery()
    .WithPurchasesMoreThan(100)
    .WithQuantitiesMoreThan(10);

customerRepository
    .Query(query1.And(query2))
    .Select()
    .Dump();

復用上面的定義的查詢方法

以上這些都是改項目提供的方法,非常的好用

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

JVM 總結

JVM GC 總結。

周志明大大的《深入理解Java虛擬機》出第三版了,早早的買了這本書,卻一直沒有花時間看。近來抽空溫習了一下,感覺又有了新的收穫。這裏簡單總結下。

GC的由來

由於的動態性,操作系統將堆交由給了開發者自己管理,手動申請,手動釋放。對於C++,則是將這個權限繼續交給了開發者,而對於Java,則是將這個過程自動化了。為什麼要釋放內存呢?最簡單的原因就是操作系統一共給你了4G的內存空間,你需要的時候,就去借用。有借有還,再借不難,只借不還,最後4G內存空間被用完了,你就無法再申請新的內存了。內存泄漏,就是只借不還。

JVM在操作系統與開發者之間又封裝了一層,間接的接管了內存的劃分。同時也將堆統一管理起來,使得開發者只管借用內存,由JVM負責回收,了解JVM的回收機制,明白它的原理,能讓開發者在不同的場景下,定製不同的回收規則,提高回收效率。

關於GC的思考

如果讓我設計一個能自動回收垃圾的虛擬機,我會怎麼設計呢?

  • 什麼時候開始回收?
  • 怎麼判斷這部分內存可以回收?
  • 怎麼回收這部分的垃圾?

這3個問題,也是JVM開發者一直在思考的問題。之前簡單了解過JVM,就知道JVM會有Stop The World的問題,這對於用戶體驗來說非常不好,其根本原因便是因為在回收垃圾的時候,用戶線程可能會修改這部分內存,如果不暫停用戶線程,則可能會導致嚴重的問題,而如何減少Stop The World的時候,甚至讓其消失,是各個垃圾回收器一直追求的目標。

哪些內存可以回收?

對於一個對象來說,當不存在任何一個引用能夠訪問到這個對象的時候,則說明這個對象可以進行回收。因為沒有任何引用指向這個對象,那麼這個對象就不能被讀或寫。

  • 引用計數法

    前面說判斷一個對象可以被回收的標準就是是否還有引用指向這個對象,所以最容易想到的便是引用計數法,通過判斷一個對象的引用數量即可,可是這樣無法判斷兩個循環引用的對象。

  • 可達性分析

    可達性分析指的是從目前程序中正在使用的所有引用的對象出發,循環遍歷所有能找到的對象。

    作為出發的點的這些對象,被稱為GC Roots

    GC Roots主要包括以下幾種:

    • 在虛擬機棧(比如棧幀中的本地遍歷表)中引用的對象
    • 靜態屬性引用的對象
    • 常量池引用對象(比如
      String Table
    • 本地方法棧引用的對象
    • Java虛擬機內部的引用對應的對象
    • 所有被同步鎖持有的對象

    總體來說,就是當前程序中正在被使用的引用所指向的對象會被作為GC Roots

GC Roots出發,依次查找,就能標記出當前存活的對象。但是標記這個過程,細節上依然存在問題:

  • STW : 標記是通過引用查找對象的,如果在標記過程中,用戶修改了引用的對象,那麼會導致不可預估的後果,因此一般標記過程中,是會STW

  • 跨代標記 : 現在的垃圾回收器,大多數都是分代,或者分區域回收的,也就是說,可能進行垃圾回收的時候,不是標記所有的垃圾,而是標記一部分,比如老年代或者新生代。此時就存在一個問題,跨代引用。比如一個新生代的對象,僅僅被一個老年代對象引用的話,對於Yong GC來說,是不會掃描老年代對象的,這個時候就會造成誤判。解決這個誤判的方法便是記憶集(Remembered Set),記憶集通過AOP技術生成寫屏障來維護。

    前面說了從GC Roots開始掃面,那分代收集的,怎麼知道哪些對象是新生代的,哪些對象是老年代的呢?因為GC Roots是包含了所有引用的。後面想想,其實對象的分代信息是存放在對象頭裡面的。在掃描GC Roots的時候,只保留新生代的對象即可。這樣基本能保證掃描到的是新生代對象,然後老年代對新生代引用交給記憶集實現就行(自己的猜測,沒有證據)

    JVM書中說道通過AOP生成的寫屏障會使得只要有更新操作,無論更新的是不是老年代對新生代對象的引用,都會使卡表變髒,不過這樣的代價相對來說是能接受的。

  • GC Roots 需要掃描的引用過多 :隨着現在Java應用越做越大,Java堆也越來越大,GC Roots的掃描是需要STW的,如果每次GC都逐個掃描,會非常的浪費時間。解決這個問題的辦法就是OopMap,使用OopMap記錄應用程序所存放的引用,每次需要GC的時候掃描這個OopMap即可生成對應的GC RootsOopMap通過安全點和安全區域來維護,只有在安全點或安全區域的時候,才更新OopMap和進行垃圾回收。

  • 併發標記過程可能丟失存活的對象 :從CMSG1,都將從GC Roots出發標記存活對象的過程修改成併發的,這樣會需要解決的問題就是標記過程中如果用戶修改了對象的引用,可能會導致本應該存活的對象”丟失“(可以通過三色標記分析),相應的解決方案便是破壞存活對象消失的必要條件,分別是增量更新(Incremental Upate)和原始快照(Snapshot At The Begin,SATB),增量更新破壞的是第一個條件,每插入一個引用,就都記錄下來,而原始快照破壞的是第二個條件,每刪除一個,都將其記錄下來。

    增量更新和併發快照也是通過前面所說的AOP技術生成寫屏障來維護

通過以上分析以及解決方案,基本明白了怎麼標記那些內存可以回收,接下來需要分析的就是什麼時候開始回收

什麼時候開始內存回收?

對於內存回收來說,開始也需要有一定的講究,理論上來說,隨時隨地都可以開始內存回收,但是如果回收時使用的內存過多,會導致GC時間過程,進而STW時間也會很長,如果回收過於頻繁,又會導致吞吐量下降,畢竟每次掃描GC Roots都回STW的。

同時,前面還說過,對於用戶線程來說,需要將用戶線程運行到安全點,更新對應的OopMap,才能開始垃圾回收。

因此,對應何時GC,有以下幾點分析:

  • 對於新生代來說,一般新生代滿了(Eden + Survivor1)就會開始進行(Yong/Minor GC

  • 對於老年代來說,一般是老年代滿了了會開始Full/Major GC

    注意:這裏的滿了,需要根據具體的回收器不同,來衡量真正的滿,對於沒有併發過程的GC,老年代滿一般指的是真正到達100%,已經無法分配內存了,對於有併發過程的GC,則需要預留出來空間給用戶線程在併發過程中同時申請內存,如果預留內存過小,則會使用非併發垃圾回收器進行Full GC

    CMS: -XX:CMSInitiatingOccupancyFraction 設置,默認92% (JDK 8),表示當老年代垃圾佔用到92%就開始老年代回收, JDK 9后便無法使用CMS

    G1: -XX:G1ReservePercent設置,默認為10,表示當整個Java堆使用到達90%,就開始回收。同時配合的參數還有-XX:InitiatingHeapOccupancyPercent=n,默認值為45,表示使用率到達45%就啟動標記周期。這裏的GCMixed GC

    一般來說,只有CMS才有Major GC,其他老年代GC都會回收整個Java堆,也稱為Full GC

  • 統計得到的Minor GC晉陞到老年代的平均大小大於老年代剩餘的空間。(JDK 6 之後已經刪除了擔保規則)

  • GC併發失敗(concurrent mode failure): 情況如前面說的,併發標記過程中,又出現了新生代晉陞的情況,但是此時老年代剩下的內存不足夠放下晉陞的對象的時候,會生導致Full GC

    這裏的Full GC和情況1中說到達預留空間的GC不一樣,情況1是正常進行的GC,而這個併發失敗卻是GC過程中出現了異常,一般需要切換到非併發GC,此時性能會大大下降

  • 方法區區域被使用完畢:JDK 8之後將方法區從Perm Gen替換成了元空間,一般來說元空間大小理論上等於本地內存大小,不過元空間有一個默認初始值,到達默認初始值后,會通過Full GC擴大

    注意:G1只有Yong GCMixed GC。沒有Full GC的概念,也就是說如果需要回收方法區的話,只能退化為Serial GC進行Full GC

    CMS可以通過-XX:+CMSClassUnloadingEnabled設置併發回收方法區

  • 最大連續空間裝不下大對象:對於CMS,基於標記-清除算法來說,即使空間足夠,但是由於內存碎片,裝不下分配的大對象時,會進行一次Full GC,對於G1來說,當分配巨型對象的時候,如果在老年代無法找到連續的Humongous的時候,會進行Full GC

  • 用戶執行System.gc(),可以通過-XX:+DisableExplicitGC屏蔽

怎麼回收這些內存

最後一步便是怎麼回收這些內存。怎麼回收,書中介紹不多,總體來說有以下三種:

  • 標記-清除(
    Mark-Sweep):最原始的方法,實現簡單,不用移動對象,很容易做到不用
    Stop The Word,但是缺點也很致命,容易產生內存碎片。標記清除的速度一般,
    Mark階段與活對象的數量成正比,
    Sweep階段與整堆大小成正比。目前只有
    CMS使用這種回收方案
  • 標記-複製(
    Mark-Copying):基於標記-清除修改的垃圾回收算法,需要移動對象。 前期標記,然後複製活下來的對象到另一個區域,再總體回收整塊區域。標記複製算法對於新生代這種專門放朝生夕死的對象效率非常高,因為存活下來的對象少,所以
    Mark階段和
    Copying階段花費的時間都會比較少,幾乎所有的分代
    GC新生代都是使用的這種算法
  • 標記-整理(
    Mark-Compact): 基於標記-清除算法修改的垃圾回收器,需要移動對象。前期標記,然後將所有對象移動到一起,再對剩餘的區域進行回收,速度最慢,但是不會產生內存碎片。

對於新生代使用標記-複製算法,是毋庸置疑的。但是對於老年代,使用標記清除還是標記整理,需要有一定的考量。因為使用標記-清除,不用移動對象,速度會相對來說比較快,但是由於存在內存碎片,無法使用指針碰撞的方式分配內存,而不得不使用“分區空閑分配鏈表”來解決內存分配的問題,這樣會對在內存分配帶來一定的效率影響,而標記-整理算法需要移動對象,特別是對於老年代這種大對象來說,移動這些對象將是一種極為負重的操作,但是標記-整理不會產生內存碎片。

因此,基於以上考慮,對於CMS這種側重響應速度,致力於減少STW時間的回收器來說,選擇了標記-清除算法,但是由於內存分配是一個非常頻繁的操作,使用”分區空閑分配鏈表”會降低整個垃圾回收器的吞吐量,因此,對於Parllel Scavenge這種注重回收吞吐的垃圾回收器來說,選擇了標記-整理算法。當然,對於G1則是吞吐和響應速度都比較注重,權衡之下,選擇了標記-整理(全局)算法。

GC的概念,到這裏基本總結完畢,但是,如果僅僅是理論,只是讓我們記着一些概念性的東西,接下來,我會結合CMSG1GC日誌以及《深入理解JVM》第四章的內容,聊一聊如何分析以及查看GC過程,簡單介紹如果進行GC調優。

個人公眾號:

不定期更新一些經典Java書籍總結。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

保護復活島生態文化 智利限遊客停留最多30日

摘錄自2018年7月30日自由時報報導

智利國會今年3月立法,強化旅客登復活島規範,並限制外來人口數量,限令於8月1日正式生效。

法新社報導,根據新法令,外籍人士或非當地原住民的智利人,在島上停留的期限從原本的90天降至30天,僅原住民拉帕努伊人(Rapa Nui)的直系血親,才可在島上長期居住。此外,公務員、政府相關組織人士,以及在當地從事獨立經濟活動者和家人,也獲准在島上居住。觀光客則須在抵達時,出示飯店預約證明或當地人的邀請函,日後也將訂定島上最高人數限制。

去年的人口普查資料顯示,島上居民已增至7750人,比數十年前多出一倍以上。

此外,人潮除了破壞文化樣貌和生態平衡,也使當地的基礎服務瀕臨極限。當地政府的環境顧問古特雷茲(Ana Maria Gutierrez)舉例說明,復活島居民10年前每年約產生1.5噸廢棄物,但如今一天就高達2.5噸,因為居民的環保意識相當薄弱。

市長艾德蒙茲一方面認同新法是「好的開始」,但另一方面卻直指新法「規模太小」,不足以保護島上文化、遺產和特色,甚至指稱許多拉帕努伊人企盼的是,全面禁止新住民到來。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

豐田CES展將推燃料電池概念車 加氫只需3分鐘

據悉,日本豐田汽車(Toyota)將在2014年的美國消費電子展(CES)上,主打燃料電池概念車(FCV),展示該公司研發汽車清淨能源的成果。豐田表示,這款概念車可跑310英哩(499公里),加氫時間只需3分鐘,新車預定2015年開賣。

豐田的燃料電池車原本預計要價5萬美元,實際定價可能接近10萬美元。新車改良油電混合車,用燃料電池取代汽油引擎,電動馬達和其他零件則和現行的油電混合相去不遠。

本站,福特汽車(Ford Motor)2日宣佈,C-Max Solar Energi太陽能概念車將在CES亮相。

C-Max純靠電力可行駛大約21英里(34公里),最遠行程620英里左右。C-Max的車頂配備SunPower公司300至350瓦的太陽能電池,預告未來可能生產不需插電的量產充電車。福特表示,一整天的日光照射約等於4小時的充電量,大約有8瓩(KW)。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

思考:如何保證服務穩定性?

最近一直在忙618大促的全鏈路壓測&穩定性保障相關工作,結果618還未開始,生產環境就出了幾次生產故障,且大多都是和系統穩定性、性能相關的bad case。

生產全鏈路壓測終於告一段落,抽出時間將個人收集的穩定性相關資料整理review了一遍,順帶從不同的維度,談談穩定性相關的“務虛”認知和思考。。。

 

一、SLA!

在開始談穩定性保障之前,我們先來聊聊業內經常提及的一個Topic:SLA!

業內喜歡用SLA (服務等級協議,全稱:service level agreement)來衡量系統的穩定性,對互聯網公司來說就是網站服務可用性的一個保證。

9越多代表全年服務可用時間越長服務越可靠,停機時間越短。就以一個標準99.99%為例,停機時間52.6分鐘,平均到每周也就是只能有差不多1分鐘的停機時間,

也就是說網絡抖動這個時間可能就沒了。保證一個系統四個9或者更高的五個9,需要一套全體共識嚴格標準的規章制度,沒有規矩不成方圓。創建的規範有如下幾種:

1、研發規範、自身穩定;

2、事務中不能包含遠程調用;

3、超時時間和重試次數要合理;

4、表數據操作必須double check,合理利用索引,避免出現慢查詢、分庫分表不走分表鍵;

5、沒有有效的資源隔離, 避免不同業務共用一個線程池或連接池;

6、合理的系統拓撲,禁止不合理的服務依賴,能去依賴就去依賴,否則同步依賴盡量改成異步弱依賴;

7、精簡的代碼邏輯;

8、核心路徑流程必須進行資源隔離,確保任何突發情況主流程不能受影響。

 

二、單服務穩定性

關鍵字:開關可控、單一職責、服務隔離、異常兜底、監控發現!

對於穩定性來說,拋開整體系統架構設計,單就每個業務域服務的穩定性也是非常的重要。

只有每個業務環節都穩如泰山,才可以保障整個穩定性。單服務的穩定可以從以下幾個方面來進行:

1、禁用設計:應該提供控制具體功能是否開啟可用的配置,在相應的功能服務出現故障時,快速下線局部功能,以保證整體服務的可用性;

2、必要的緩存:緩存是解決併發的利器,可以有效的提高系統的吞吐量。按照業務以及技術的緯度必要時可以增加多級緩存來保證其命中率;

3、接口無狀態性:服務接口應該是無狀態的,當前接口訪問不應該依賴上層接口的狀態邏輯;

4、接口單一職責性:對於核心功能的接口,不應該過多的耦合不屬於它的功能。如果一個接口做的事情太多應做拆分,保證單接口的穩定性和快速響應;

5、第三方服務隔離性:任何依賴於第三方的服務(不論接口還是中間件等),都應該做到熔斷和降級,不能有強耦合的依賴;

6、業務場景兜底方案:核心業務場景需要做到完整的兜底方法,從前端到後端都應該有兜底措施;

7、服務監控與及時響應:每個服務應該做好對應的監控工作,如有異常應及時響應,不應累積。

 

三、集群穩定性

關鍵字:系統架構、部署發布、限流熔斷、監控體系、壓測機制!

對於集群維度的穩定性來說,穩定性保障會更加複雜。單服務是局部,集群是全局。一個見微知著,一個高瞻遠矚。

1、合理的系統架構:合理的系統架構是穩定的基石;

2、小心的代碼邏輯:代碼時刻都要小心,多擔心一點這裡會不會有性能問題,那裡會不會出現併發,代碼就不會有多少問題;

3、優秀的集群部署:一台機器永遠會有性能瓶頸,優秀的集群部署,可以將一台機器的穩定放大無限倍,是高併發與大流量的保障;

4、科學的限流熔斷:高併發來臨時,科學的限流和熔斷是系統穩定的必要條件;

5、精細的監控體系:沒有監控體系,你永遠不會知道你的系統到底有多少隱藏的問題和坑,也很難知道瓶頸在哪裡;

6、強悍的壓測機制:壓測是高併發穩定性的試金石,能提前預知高併發來臨時,系統應該出現的模樣;

7、膽小的開發人員:永遠需要一群膽小的程序員,他們討厭bug,害怕error,不放過每一個波動,不信任所有的依賴。

 

四、穩定性專項

專項指的是針對某些特定場景下的特定問題而梳理出對應的方案。下面是針對一些常見的穩定性專項的概述:

1、預案:分為定時預案和緊急預案,定時預案是大促常規操作對於一系列開關的編排,緊急預案是應對突發情況的特殊處理,都依賴於事前梳理;

2、預熱:分為JIT代碼預熱和數據預熱,阿里內部有專門的一個產品負責這塊,通過存儲線上的常態化流量或者熱點流量進行回放來提前預熱,

  起源於某年雙十一零點的毛刺問題,原因是訪問了數據庫的冷數據rt增高導致的一系列上層限流,現在預熱已經成了大促之前的一個必要流程。

3、強弱依賴:梳理強弱依賴是一個偏人肉的過程,但是非常重要,這是一個系統自查識別潛在風險點併為後續整理開關限流預案和根因分析的一個重要參考,

  阿里內部有一個強弱依賴檢測的平台,通過對測試用例注入RPC調用的延遲或異常來觀察鏈路的依賴變化,自動梳理出強弱依賴關係。

4、限流降級熔斷:應對突發流量防止請求超出自身處理能力系統被擊垮的必要手段;

5、監控告警&鏈路追蹤:監控分為業務監控、系統監控和中間件監控和基礎監控,作為線上問題發現和排查工具,重要性不言而喻。

 

五、穩定性建設

穩定性建設,就和基礎技術建設一樣,是一個長期迭代和不斷調整的過程,業內常見的穩定性建設類型,主要有如下幾種:

1、容量規劃:個人感覺容量規劃在大廠里也並沒有做的很好,更多依賴的是業務方自己拍腦袋,然後全鏈路壓測期間驗證,不夠就再加機器。

2、混沌工程:混沌工程是近幾年比較火的名詞,通過不斷給系統找麻煩來驗證並完善系統能力,阿里在這塊花了很大的精力建設紅藍軍對抗攻防,進行定期和不定期的演練,

  最後以打分的形式來給各個部門系統做排名,除了系統層面的故障演練外還有資金演練,篡改線上sql語句製造資損來測試業務監控糾錯的能力,通過製造小錯來避免大錯。

  跳轉門:混沌工程-初識

3、流量調度:通過metric秒級監控和聚類算法實時找出異常單機來降低RPC流量權重,提升集群整體吞吐能力減少異常請求。

4、容災&異地多活:起源於15年某施工隊將光纖挖斷帶來的支付寶故障,由此出來的三地五中心和單元化架構,異地多活本身的成本比較高,

  然後又存在數據同步的延時問題和切流帶來的臟數據問題,對於業務和技術都有比較高的要求。常見的容災有如下幾種:

  1)緩存掛掉,集群重啟緩存預熱如何處理?本地緩存,多級緩存是否可以替代?

  2)分佈式鎖,是否有開關一鍵切換?比如:ZK/ETCD編寫的分佈式鎖;

  3)大促峰值流量,如何防止外部ddos攻擊?如何識別流量類型?

  4)資源隔離:資源隔離,服務分組,流量隔離;

  5)高可用思想:避免單點設計!

  6)容錯:容錯上游,防禦下游。容錯主要需要注意如下幾點:

     6-1:外部依賴的地方都要做熔斷,避免雪崩;

     6-2:對於依賴我們的上游要限流,防止上游突發流量超過自己系統能夠扛住的最大QPS;

     6-3:對於下游既要評估好接口超時時間,防止下游接口超時異常導致自己系統被拖累;

     6-4:下游的接口要考慮各種異常情況,需要考慮中間狀態,通過引入柔性事務,確保數據最終一致。

5、異地多活

異地多活的本質,是數據中心架構的演進

1)演進:單機房——雙機房——異地災備——異地多活;

2)定義:分多個地域、多個數據中心運行線上的業務,並且每個IDC均提供在線服務;

3)優點:彈性擴展能力、流量就近接入、靈活調度、提升可用性與用戶體驗、容災;

4)步驟

  4-1:基礎設施:機房之間專線互聯,保證網絡質量穩定;

  4-2:持久存儲:一主三從,主IDC同步複製,異地IDC異步複製;

  4-3:中間件:DB、MQ、分佈式存儲;

  4-4:應用部署:根據應用域劃分,不同應用部署在不同地域,保持親緣性;

  4-5:流量接入與調度:網絡協議兼容,DNS,動態調度用戶就近訪問;

  4-6:監控與運維保障:專線實時監控,確保發生故障時可以觸發Failover(失效備援)和流量調度。

 

六、穩定性思考

關鍵字:階段工作、角色轉變!

穩定性建設是一個演進的階段性過程,主要分為三個階段:

1、發現問題解決問題:當問題較多時候就很被動,很多時候我們通過不斷完善監控來確保我們來快速定位問題,但仍處於被動的一方;

2、主動尋找問題:混沌工程、破壞性測試、極限壓測、紅藍對抗等手段,一方作為創造問題方不斷挑戰系統極限,另一方見招拆招快速修復。

3、角色轉變:這個過程中會積累很多處理問題的經驗,不斷完善系統健壯性,爭取在用戶發現問題前消滅於萌芽中。角色轉變,變被動為主動。

 

七、推薦閱讀

聊聊服務災備

大促穩定性建設

運維監控體系建設

這樣的高可用,我不要

高併發限流,到底限的什麼鬼

新浪微博平台穩定性體系介紹

StabilityGuide—穩定大於一切

沒有預熱,不叫高併發,叫併發高

信號量限流,高併發限流不得不說的秘密

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

特斯拉Model S在港正式銷售 入門級57.9萬港幣

昨(13)日有消息稱,特斯拉Model S車型已正式在香港銷售,其中入門級配置僅售57.9萬港幣。此外有報導稱,比亞迪將與ABB集團及德國戴姆勒共建全球最大電動汽車快速充電網,ABB將在未來6年內為兩家公司提供直流快速充電樁。

上述消息為A股正紅火的新能源汽車相關概念再添一把柴。截至昨天收盤,充電樁概念逆市上漲0.67%,漲幅居前,奧特迅領漲,漲幅為7.60%。特斯拉概念表現較為抗跌僅下挫0.51%,金瑞科技漲停,比亞迪也大漲8.26%。鋰電池概念跌0.93%,其中南洋科技漲9.48%。

證券方面人士認為,在政府大力建設新型城鎮化、發展城市公共交通的背景下,對新能源汽車的政策扶持將會給電池業、電池電機上游的鋰和稀土資源行業、充電站行業、乘用車企業帶來機遇。

但需要注意的是,未來的上漲空間將取決於政策的落實情況和上市公司的基本面,並且投資者不可對其期望過高,尤其是目前市場處於震盪的情形下,一旦后續資金追高乏力,相關個股很難擺脫大勢的影響。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

Blazor帶我重玩前端(一)

寫在前面

曾經我和前端朋友聊天的時候,我說我希望有一天可以用C#寫前端,不過當時更多的是美好的想象,而現在這一切正變得真實……

 

什麼是Blazor

我們知道瀏覽器可以正確解釋並執行JavaScript代碼,那麼瀏覽器是如何執行C#代碼的呢?答案是通過WebAssembly。通過WebAssembly,我們可以讓瀏覽器運行很多的高級語言,如 C#、C、C++、GO等,並使他們運行在基於內存安全的沙箱環境中。如下圖所示:

作為一個已經五六年沒有寫過前端的.NET程序員,遇到Blazor實在是幸運中的幸運。它又讓我可以很愉快的寫前端了,而且還是用C#去寫,我也就不用再分出精力去學習其他的JS框架了。

我們可以認為Blazor是.NET對WebAssembly的實現。通過使用Blazor,我們可以使用C#語言來取代JS去開發交互式Web UI。

值得一提的是,Blazor是由Browser和Razor這兩個單詞合併而成的,意思就是Blazor可以基於客戶端執行Razor視圖后將HTML呈現給瀏覽器。所以想要更好的理解Blazor,就要首先更好的了解瀏覽器和Razor。

Blazor有以下幾個優點:
  • 使用C#來取代JavaScript創建豐富的交互式UI
  • 基於.NET及其生態編寫服務器端和客戶端應用程序邏輯
  • 糅合現有HTML和CSS技術,提供了廣泛的瀏覽器支持,包括移動瀏覽器(注意:Blazor取代的是基於JavaScript的UI交互,而其他部分如HTML、CSS,這些是我們的技術基礎)
  • 與現代託管平台(例如Docker)集成。
  • Blazor是開源的,其源碼位置在GitHub上

另外需要注意的,Blazor和Silverlight不可混為一談,Blazor是基於開放標準而構建的,本身不需要任何額外插件。而Silverlight帶有太多自有特性,所以不得不在瀏覽器上安裝插件以更好的支持其運行。

什麼是WebAssembly

概覽

WebAssembly是一種二進制格式的指令集,其設計目標是能夠在解釋或者將其編譯為本地機器代碼並執行他們的機器上運行,這類似於我們.NET編譯后的IL。

WebAssembly可以作為編譯高級編程語言的可移植目標,通過節省大小和加載時間,充分利用各種平台(移動平台和IOT平台)上的通用應用功能,使得WebAssembly可以以接近於本機(接近於本機的英語單詞是:near-native,在語言學里意思是精通語言的人,所說的話和說母語的人沒有什麼區別)的運行速度運行。

支持

WebAssembly已經獲得了大部分瀏覽器的支持。詳細內容可以移步至Can I Use

手寫一個例子

接下來我們看一個例子,方便起見,我們直接使用在線的WebAssembly編譯工具,地址是:https://mbebenita.github.io/WasmExplorer/。目前,這個工具只支持C和C++。不過也沒有什麼關係,我們寫一個簡單的方法用於測試即可。

  • 首先我們定義了一個計算兩個數和的方法:
1 int Addition(int a, int b)
2 {
3   return a + b; 
4 }
  • 然後點擊COMPILE

在中間的框里會生成WAT(即WebAssembly文本格式)的代碼,最右邊的是二進制了。中間的代碼部分可以幫助我們查看在編譯的過程中發生了什麼,會看到生成了一個名為_Z8Additionii的function,其中8表示這個方法名的長度,後面的i表示有多個參數,接下來我們會去調用它。

 1 (module
 2  (table 0 anyfunc)
 3  (memory $0 1)
 4  (export "memory" (memory $0))
 5  (export "_Z8Additionii" (func $_Z8Additionii))
 6  (func $_Z8Additionii (; 0 ;) (param $0 i32) (param $1 i32) (result i32)
 7   (i32.add
 8    (get_local $1)
 9    (get_local $0)
10   )
11  )
12 )
  • 點擊Download,下載.WAT文件

  • 接下來我們再寫一個HTML網頁出來,就用那種最簡單的HTML代碼,代碼如下:
 1 <HTML>
 2 <HEAD>
 3     <TITLE>WebAssembly Sample: Call C++ Code</TITLE>
 4     <script type="text/javascript">
 5         let addition = fetch('test.wasm')
 6             .then(response => response.arrayBuffer())
 7             .then(buffer => WebAssembly.compile(buffer))
 8             .then(module => { return new WebAssembly.Instance(module) })
 9             .then(instance => { addition = instance.exports._Z8Additionii }); 
10     </script>
11 </HEAD>
12 <BODY BGCOLOR="FFFFFF">
13     <h1>WebAssembly Sample: Call C++ Code</h1>
14 </BODY>
15 </HTML>
  • 最終的效果圖

通過以上示例,我們基本上對Blazor和WebAssembly的部分運行機制有了一個比較清晰的認識了,接下來,我們繼續討論有關Blazor的內容。

參考鏈接:

https://webassembly.org/

https://webassembly.github.io/spec/js-api/index.html

https://caniuse.com/#search=wasm

https://webassembly.github.io/spec/js-api/index.html


本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

特斯拉向企業客戶推出電動車租賃計劃 以提振需求

特斯拉(Tesla)於8日推出面向企業客戶的S型豪華跑車租賃計劃,以爭取更多的美國國內客戶。消費者可分期支付車款,在租期截止後可選擇支付餘款並擁有汽車,也可選擇將汽車歸還。有意願者在網上僅需5分鐘就可提交一份租賃申請。

此舉正逢S型豪華跑車在美國的銷量開始放慢增長。今年第一季度,S型車在美國市場售出4700輛,比去年同期僅增長1%。特斯拉公司表示,相比去年向個人客戶推出的一種功能相當於租賃計劃的推銷模式,面向中小企業的租賃計劃更為直接透明,租賃費可在企業納稅時扣抵。

S型全電動跑車是特斯拉的主打產品,去年登上美國最暢銷豪華車榜單。但市場分析人士認為,美國消費者對該系列汽車的興趣開始消退,需求進入穩定階段,因此開拓國際市場對特斯拉來說尤為重要。他們預計,中國消費者早期的強勁需求可能是提升S型車銷量的最大因素。

特斯拉電池工廠計畫面臨危機

另外,特斯拉早前宣佈將耗資數十億美元建造全球最大的電池工廠,以求大幅降低電動車成本,幫助特斯拉進入中端市場。不過,特斯拉新聞發言人西恩曾表示,特斯拉電動車電池的兩項重要原料資源,現在均面臨缺乏危機。

石墨是特斯拉電動車電池最重要的原料之一,特斯拉目前使用的石墨原料絕大部分來自日本和歐洲市場,且都是化學合成石墨,而並非天然開採的石墨。美國稀有金屬諮詢分析公司Technology Metal預計,未來需要新增6個石墨礦才能夠滿足不斷增長的電池生產對原材料的需求,石墨的價格則會繼續上漲。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!