認證授權方案之授權初識

1.前言

回顧:認證授權方案之JwtBearer認證

在上一篇中,我們通過JwtBearer的方式認證,了解在認證時,都是基於Claim的,因此我們可以通過用戶令牌獲取到用戶的Claims,在授權過程中對這些Claims進行驗證,從而來判斷是否具有獲取或執行目標資源操作的權限。本章就來介紹一下 ASP.NET Core 的授權系統的簡單使用。

2.說明

授權與身份認證是相互獨立,但是,授權卻需要一種身份驗證機制,因此,身份驗證可以為當前用戶創建一個或多個標識,是確定用戶真實身份的過程。而授權是根據標識確定用戶可執行的操作的過程,其本質就是具有某種特性的用戶會有權限訪問某個資源或者執行某個操作。例如:一個擁有管理員身份的用戶有創建人員、刪除人員、編輯人員和刪除人員的操作權限,而一個非管理身份的用戶僅有讀取自己信息的權限。

這時候,你可能會問,究竟怎樣特性的用戶可以被授權訪問某個資源或執行某個操作。由此我們引出了授權策略的方式,可以根據用戶擁有的角色,也可以根據用戶的職位,部門甚至是性別,年齡等等特性進行授權。

通過建立授權策略方式,檢驗認證的用戶所攜帶的身份聲明(ClaimsPrincipal對象)與授權策略是否一致,從而確定用戶可否執行操作。

3.授權

3.1. 基於角色

3.1.1 添加角色

將角色賦予某個控制器或控制器內的操作,指定當前用戶必須是其角色才能訪問請求資源。

可以使用Authorize屬性的Roles特性指定所請求資源的角色。

例如:

  • 分配了“admin”角色用戶進行訪問操作
[Authorize(Roles ="admin")]
public class WeatherForecastController : ControllerBase
{

}
  • 以逗號分隔角色名來允行多個角色訪問操作
[Authorize(Roles ="admin,user")]
public class WeatherForecastController : ControllerBase
{ 


}

其中只要滿足admmin或者user其一就可以進行訪問。

  • 同時滿足指定的多個角色進行的訪問操作
[Authorize(Roles = "admin")]
[Authorize(Roles = "user")]
public class WeatherForecastController : ControllerBase
{ 
}

3.1.2 添加策略的角色

可以創建策略的方式進行訪問控制,在配置授權服務中添加註冊授權服務策略。

在Startup.cs文件中,通過ConfigureServices()配置服務,創建一個允許具有admin角色的用戶才能進行訪問的策略

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //添加授權角色策略
        services.AddAuthorization(options =>
        {
            options.AddPolicy("BaseRole", options => options.RequireRole("admin"));
        });
        //或者指定多個允許的角色
        //services.AddAuthorization(options =>
        // {
        //    options.AddPolicy("MoreBaseRole", options => options.RequireRole("admin","user"));
        // });
    }

在控制器方法使用特性Policy的屬性進行策略應用

    [Authorize(Policy = "BaseRole")]
    public class WeatherForecastController : ControllerBase
    {
    
    }

3.2. 基於聲明

3.2.1添加聲明

對當前用戶必須擁有的聲明,並將聲明賦予某個控制器或控制器內的操作,因此,指定聲明必須持有對應的值才能訪問請求資源。

聲明要求基於策略,所以必須進行構建一個表示聲明要求的策略,才能進行授權。

最簡單的類型聲明是將判斷聲明是否存在,而不檢查值。

可以創建策略的方式進行訪問控制,在配置授權服務中添加註冊授權服務策略。

在Startup.cs文件中,通過ConfigureServices()配置服務,創建一個允許具有聲明的用戶才能進行訪問的策略

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //添加基於聲明的授權
        services.AddAuthorization(options =>
        {
            options.AddPolicy("BaseClaims", options => options.RequireClaim("name"));
        });
    }

BaseClaims聲明策略會檢查name當前標識是否存在聲明。

在控制器方法使用特性Policy的屬性進行策略應用

    [Authorize(Policy = "BaseClaims")]
    public class WeatherForecastController : ControllerBase
    {
    
    }

但是,大多時候,我們需要聲明包含值,只有指定允許值的列表,才能授權成功。所以,可以添加指定值。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //添加基於聲明的授權,指定允許值列表。
        services.AddAuthorization(options =>
        {
            options.AddPolicy("BaseClaims", options => options.RequireClaim("name","i3yuan"));
        });
    }

3.3 基於策略

上面介紹的基於角色和基於聲明的授權,都使用了要求、要求處理程序和預配置的策略。這些在構建上提供了便捷,但是最終都是生成授權策略。ASP.NET Core,設計了另一種靈活的授權方式,一種更豐富的可重複使用的授權結構,基於策略的授權,同時這也是授權的核心。

這節會先講一下授權策略的應用,在下一節中,會對授權策略的核心進行一步步的詳解。

在上面我們簡單的介紹了基於策略的角色授權,但是這種方式無非基於角色或者聲明多一些。

因此,這裏我們基於自定義策略授權的方式,實現授權。

自定義授權,就要我們自己寫策略提供器,自己根據不同的參數來生成不同的策略,重新實現策略的方式。策略要求由以下兩種元素組成:僅保留數據的要求類,以及對用戶驗證數據的授權處理程序。創建自定義要求,還可以進一步表達特定策略。

3.3.1. 定義權限策略PermissionRequirement

定義一個權限策略,這個策略並包含一些屬性。

public class PermissionRequirement: IAuthorizationRequirement
{
    public string _permissionName { get; }

    public PermissionRequirement(string PermissionName)
    {
        _permissionName = PermissionName;
    }
}

3.3.2. 再定義一個策略處理類

public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
    {
        var role = context.User.FindFirst(c => c.Type == ClaimTypes.Role);
        if (role != null)
        {
            var roleValue = role.Value;
            if (roleValue==requirement._permissionName)
            {
                context.Succeed(requirement);
            }
        }
        return Task.CompletedTask;

授權處理程序讀取與角色用戶關聯的聲明,並檢查自定義的角色,如果角色匹則成功,否則無法返回成功。

這裏的自定義聲明是寫固定了,但是也可以通過數據庫或外部服務的方式進行運行查詢獲取用戶相關角色信息相對應的判斷條件,從而在處理程序中進行判斷處理。

授權處理程序調用方法 Succeed,同時傳遞當前要求,以通知此要求已成功得到驗證。如果沒有傳遞要求,處理程序無需執行任何操作,可以直接返回內容。不過,如果處理程序要確定是否不符合要求(無論其他處理程序是否已成功驗證同一要求),將會對授權上下文對象調用方法 Fail

3.3.3. 下面展示了如何將自定義要求添加到策略

(請注意,由於這是自定義要求,因此沒有擴展方法,而必須繼續處理策略對象的整個 Requirements 集合):

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        //基於自定義策略授權
        services.AddAuthorization(options =>
        {
            options.AddPolicy("customizePermisson",
              policy => policy
                .Requirements
                .Add(new PermissionRequirement("admin")));
        });
        //此外,還需要在 IAuthorizationHandler 類型的範圍內向 DI 系統註冊新的處理程序:
        services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();
        // 如前所述,要求可包含多個處理程序。如果為授權層的同一要求向 DI 系統註冊多個處理程序,有一個成功就足夠了。

    }

3.3.4. 應用自定義的策略的特性

指定當前用戶必須是應用對控制器或控制器內的操作,如

   [Authorize(Policy = "customizePermisson")]
    public class WeatherForecastController : ControllerBase
    { 
    }

4.場景

在上一篇認證授權方案之JwtBearer認證中,我們已經實現了獲取token的方式,這一次,我們實現一個以基於角色場景為例的認證授權。

在原來生成token的方式中,添加多一個聲明角色的Claim,如下:

new Claim(JwtClaimTypes.Role,”admin”)

    [HttpGet]
    public IActionResult GetToken()
    {
        try
        {
            //定義發行人issuer
            string iss = "JWTBearer.Auth";
            //定義受眾人audience
            string aud = "api.auth";
            //定義許多種的聲明Claim,信息存儲部分,Claims的實體一般包含用戶和一些元數據
            IEnumerable<Claim> claims = new Claim[]
            {
                new Claim(JwtClaimTypes.Id,"1"),
                new Claim(JwtClaimTypes.Name,"i3yuan"),
                new Claim(JwtClaimTypes.Role,"admin"),
            };
            //notBefore  生效時間
            // long nbf =new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
            var nbf = DateTime.UtcNow;
            //expires   //過期時間
            // long Exp = new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds();
            var Exp = DateTime.UtcNow.AddSeconds(1000);
            //signingCredentials  簽名憑證
            string sign = "q2xiARx$4x3TKqBJ"; //SecurityKey 的長度必須 大於等於 16個字符
            var secret = Encoding.UTF8.GetBytes(sign);
            var key = new SymmetricSecurityKey(secret);
            var signcreds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            //String issuer = default(String), String audience = default(String), IEnumerable<Claim> claims = null, Nullable<DateTime> notBefore = default(Nullable<DateTime>), Nullable<DateTime> expires = default(Nullable<DateTime>), SigningCredentials signingCredentials = null
            var jwt = new JwtSecurityToken(issuer: iss, audience: aud, claims:claims,notBefore:nbf,expires:Exp, signingCredentials: signcreds);
            var JwtHander = new JwtSecurityTokenHandler();
            var token = JwtHander.WriteToken(jwt);
            return Ok(new
            {
                access_token = token,
                token_type = "Bearer",
            });
        }
        catch (Exception ex)
        {
            throw;
        }
    }

對控制器或控制器內的操作,指定當前用戶必須是其角色才能訪問請求資源,如WeatherForecastController.cs

[ApiController]
[Route("[controller]")]
[Authorize(Roles ="admin")]
public class WeatherForecastController : ControllerBase
{ 
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

5.運行

5.1. 獲取token

分別獲取role為admin和role為user的情況下頒發的token,只有在角色為admin的情況下才能授權通過。

5.2. 授權資源接口訪問

在role為admin的情況下

在role為user的情況下

由上可知,只有在角色為admin的情況下,才能訪問目標資源進行操作。

6.總結

  1. 從上一篇的認證到這一篇的授權階段,簡單的介紹了Asp.net Core的認證授權系統,對授權有了初步的認識以及使用,對授權進行劃分為兩種,一種是基於角色的授權,但隨着角色的增加會對處理授權產生限制,不適合表達複雜的授權邏輯。另一種是基於策略的身份驗證,策略包含一系列基於聲明的要求,以及基於可從 HTTP 上下文或外部源注入的其他任何信息的自定義邏輯。這些要求各自與一個或多個處理程序相關聯,這些處理程序負責要求的實際計算。
  2. 可以發現,asp.net core提供的授權策略是一個非常強大豐富且靈活的認證授權方案,能夠滿足大部分的授權場景。
  3. 如果有不對的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學習,共同進步。
  4. 因此,在後續的篇章中,會繼續探索授權系統,對授權策略的核心進行一步步的詳解。
  5. 本示例源碼地址

參考文獻文檔

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

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

阿拉斯加「平紀錄高溫+破紀錄大雪」出現同一天!

摘錄自2019年11月18日ETtoday綜合報導

今年入秋以來,美國阿拉斯加州氣候異常溫暖,當地最大城市安克拉治居民,近日竟在同一天內,先後經歷了創紀錄高溫與破紀錄降雪兩種截然不同的極端天氣型態!

位於安克拉治國際機場南側的美國國家氣象局(NWS)安克拉治辦公室,當天觀測到超過8.3英吋積雪,打破當地1958年以來的降雪新紀錄。奇怪的是,安克拉治市當天不止出現破紀錄降雪,同一天凌晨3時,當地還出現華氏45度(攝氏7.2度)氣溫,與1967創下的的同日最高溫紀錄持平。

NWS預報員德魯茲(Eric Drewitz)解釋,16日凌晨,一股東南風從海上吹來,為城市帶來溫暖空氣,雖著風勢減弱,氣溫也隨之下滑,並且降下硬幣大小的雪花。報導提到,阿拉斯加州今年秋天以來異常溫暖,10月下旬也曾出現破紀錄高溫,溫暖天氣一直延續到11月。在16日降下大雪以前,當地今年入冬第一場,也是唯一一場降雪出現在10月16日,但降雪量僅5分之1英吋(約0.5公分)。

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

【其他文章推薦】

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

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

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

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

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

減塑全球風潮 2020年起泰國商場禁用塑膠袋

摘錄自2019年11月18日NOWnews報導

為了達到環保的目的,2020年起泰國許多連鎖超商或大賣場將不再供應塑膠袋。根據路透社的報導,泰國國家資源和環境部部長瓦拉伍特(Varawut Silpa-archa)在臉書宣佈,許多商店與政府合作,暫停提供一次用塑膠袋,措施將於2020年1月起生效。

「方便性會下降,不過這是為了延續環境的生態循環。」瓦拉伍特受訪時說,他建議民眾可以使用可重複利用的大布袋取代塑膠袋。

不再提供塑膠袋的商店包括在泰國管理上千家7-11的CP All Plc公司和泰國購物商場集團。其實,這並不是泰國第一次嘗試縮減塑膠袋使用的政策。早在2018年,CAN便報導過泰國旅遊局曾發佈「國家淨化聲明」,希望在2021年時,能減少一半以上現行使用的塑膠袋總用量。

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

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

因為我的一個低級錯誤,生產數據庫崩潰了將近半個小時

前言

halo,相信大家一定過了一個很開心的端午節吧,我看朋友圈裡各種曬旅遊,曬美食的,真是羡慕啊,不像我,感冒了只能在家擼文章。
當然,玩的多開心,節後上班就有多鬱悶,假日綜合征可不是說說而已。對此我想表達的是,沒事,不用鬱悶,來看我如何自爆家醜來讓你們開心下。

反常的sql語句

上周四午休時分,我正在工位上小憩,睡夢中彷彿看到了自己拿着李白在榮耀峽谷里大殺四方的情景,就在我剛拿完五殺準備帶領隊友推對面水晶的時候,一句慌亂急促的“糟了”把我從睡夢中驚醒。我眯開朦朧的雙眼,才發現剛才的發聲來源於我的組長庄哥,看到他在緊張的點開日誌系統查看日誌,我預感到有什麼不妙的事情發生,仔細一問才知道,原來就在我眯眼的期間,線上數據庫服務器的CPU被打滿,同時觸發了生產數據庫只讀延遲的限定時間並且發出告警,而且告警的過程持續了半個小時。

這讓我倒吸了一口涼氣,因為我們組做的系統很多都用的是同一個數據庫服務器,日用戶活躍量有好幾十萬,如果服務器崩潰了將會使所有的系統服務都不可用,於是我們趕緊通過sql日誌進行問題查找,最後排查出來是因為一張sql的高量查詢沒有走索引導致,日誌列表显示,這條sql語句的掃描行數達到了上百萬,基本就是全表掃描的情況,而且半個小時的時間查詢了達上萬次,每條sql查詢的耗時都在3000ms以上。我的天啊,難怪服務器會CPU打滿,這麼一條耗時的sql語句查詢量這麼大,數據庫的資源當然是直接就崩潰了,這是當時那條sql的查詢情況:

臨時處理

看了這條語句,我又倒吸一口涼氣,這不就是我寫的系統調用的sql語句嗎?完了,這回逃不掉了,真是人在睡夢裡,鍋從天上來。

當然,因為是我自己寫的sql,所以我一看就知道這條語句是有問題的。

根據我的代碼處理,這條sql的調用還少了個重要的參數user_fruit_id,這個參數沒有傳的話是不應該走這條sql查詢的,在我的設計里,該參數是數據表裡一個聯合索引的最左側字段,如果該字段沒有傳值的話,那麼索引就不會生效了。

KEY `idx_userfruitid_type` (`user_fruit_id`,`task_type`,`receive_start_time`,`receive_end_time`) USING BTREE

雖然定位到了sql語句,但是線上的問題刻不容緩,總不可能找出bug改完再上線吧,所以,我們只能做了一個臨時處理,就是在原來的表上多加了一個聯合索引,其實就是去掉了user_fruit_id 字段,讓這些高量的查詢都能走新的索引,就像下面這樣

KEY `idx_task_type_receive_start_time` (`task_type`,`receive_start_time`,`receive_end_time`,`created_time`) USING BTREE

加上索引后,sql的掃描行數就大幅度的降低了,重啟實例后就又能正常運行了。

最左匹配原則

那麼為什麼最左側的字段沒傳索引就不生效了,這是因為MySQL的聯合索引是基於“最左匹配原則”匹配的。

我們都知道,索引的底層是B+樹結構,聯合索引的結構也是B+樹,只不過鍵值數量不是一個,而是多個,構建一顆B+樹只能根據一個值來構建,因此數據庫依據聯合索引最左的字段來構建B+樹。

例如我們用兩個字段(name,age)這個聯合索引來分析,

圖片來源於林曉斌老師的《MySQL實戰45講》課程,

當我們在where條件中查找name為“張三”的所有記錄的時候,可以快速定位到ID4,並且查出所有包含“張三”的記錄,而如果要查找“張三,10”這一條特定的數據,就可以用 name = “張三” and age = 10 獲取,因為聯合索引的鍵值對是兩個,所以只要前面的name確定的情況下就可以進一步定位到具體的age記錄,但是如果你的查詢條件只有age的話,那麼索引就不會生效,因為沒有匹配最左邊的字段,後面所有的索引字段都不會生效,所以我之前寫的sql語句才會因為少了最左邊的user_fruit_id字段而走了全表掃描的查詢方式。

正常來說,假設一個聯合索引設計成(a,b)這樣的結構的話,那麼用a and b作為條件,或者a單獨作為查詢條件都會走索引,這種情況下我們就不要再為a字段單獨設計索引了。

但如果查詢條件裏面只有b的語句,是無法使用(a,b)這個聯合索引的,這時候你不得不維護另外一個索引,也就是說你需要同時維護(a,b)、(b) 這兩個索引。

找出Bug

雖然臨時做了處理,但問題並不算解決,很明顯是系統出現了bug才會有走這樣的查詢條件。因為是我自己寫的代碼,所以知道是哪條sql后我就馬上定位到了代碼里的具體方法,後來才發現是因為我對user_fruit_id字段的判空處理不生效所致。

因為該字段是從調用方傳過來的,所以我在方法參數里對該字段做了非空限制的註解,也就是javax包下的@NotNull,

public class GardenUserTaskListReq implements Serializable {

    private static final long serialVersionUID = -9161295541482297498L;

    @ApiModelProperty(notes = "水果id")
    @NotNull(message = "水果id不能為空")
    private Long userFruitId;
    /**以下省略*/
    .....................
}

雖然加上該註解來做非空校驗,但我卻沒有在參數加上另一個註解@Validated,該註解如果沒加上的話,那麼調用javax包下的校驗規則就都不生效,正確的寫法是在controller層方法的參數前面加上註解,

除此之外,因為user_fruit_id這個字段是另一張表的主鍵,我在代碼里也沒有對這張表是否存在這個id做查詢判斷,這樣一來,無論調用方傳什麼值過來都會直接觸發sql查詢,並且在不跑索引的情況下直接走全表掃描。

不得不說,這真是個低級錯誤,說真的,我對這個原因真是感到嘀笑皆非,再怎麼說也工作幾年了,怎麼還犯一些新手級別的錯誤呢,這臉打得真是讓我相當慚愧。

總結

雖然是低級錯誤,但造成的後果也算挺嚴重了,這次事件也讓我更加的警醒,在以後的開發工作中必須要遵守該有的原則,大概有這麼幾點:

1、不能相信調用端。重要的參數都要先做驗證,即使是非空值也需要做驗證,不符合條件的就要直接返回或拋異常,不能參与業務sql的查詢,否則頻繁的訪問也會對服務造成負擔。

2、sql語句要先做性能查詢。對於數據量大的表,建好索引后,所有的sql查詢語句要用explain檢測性能,並且根據結果來進一步優化索引。

3、代碼必須要review。之前我沒有放太大的精力在代碼的review上,雖說跟迭代排期的緊湊也有關係,但不管怎麼說,bug確實是我的疏忽造成的,尤其是像空值這種細小的錯誤在Java里可以說家常便飯。千里之堤毀於蟻穴,有時一個小bug很容易就引發整個系統的崩盤,這一次的問題也讓我更加深刻的認識到了review代碼的重要性,不管業務開發的工作量有多麻煩,這一步操作絕對不能忽視。

後續

知道了bug的原因,改完代碼當天就重新發布了,後來,庄哥告訴我說,為了以後讓組裡的其他人對此次問題有所警戒,讓我寫一篇問題記錄總結一下,我想了一下,這不是我的強項啊,但怎麼說也確實是自己的問題,還是老老實實的寫一下記錄好了。我本以為這樣就可以松一口氣了,可平哥 (組裡的一位大佬) 卻突然用詭異的眼神看着我,語重心長的說,上次xxx也因為線上出現問題寫了報告,你這一次估計也不能例外了,可能要一萬字以上。我瞬間就感覺一個雷劈到了我頭上,蒼天啊。。。。。。

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

【其他文章推薦】

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

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

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

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

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

Java 從入門到進階之路(二十五)

在之前的文章我們介紹了一下 Java 中的  集合框架中的Collection 的子接口 List的 增刪改查和與數組間相互轉換的方法,本章我們來看一下 Java 集合框架中的Collection 的子接口 List 的另外一些方法。

我們在使用集合的時候難免會對其中的元素進行排序,因為 Set 集合本身是無序的,所以本章將着重講解 List 集合,如下:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.List;
 4 import java.util.Random;
 5 
 6 /**
 7  * 排序集合元素
 8  * 排序集合使用的是集合的工具類 Collections 的靜態方法 sort
 9  * 排序僅能對 List 集合進行,因為 Set 部分實現類是無序的
10  * */
11 public class Main {
12     public static void main(String[] args) {
13         List<Integer> list = new ArrayList<Integer>();
14         Random random = new Random();
15         for(int i=0;i<10;i++){
16             list.add(random.nextInt(100));
17         }
18         System.out.println(list); // [49, 24, 29, 59, 56, 1, 1, 5, 49, 60]
19 
20         Collections.sort(list);
21         System.out.println(list); // [1, 1, 5, 24, 29, 49, 49, 56, 59, 60]
22     }
23 }

在上面的代碼中,我們隨機生成了一些正數並添加到 List 集合中,我們通過 sort 方法,系統變自動按照自然數的排列方法為我們做好了排序,很是方便,那如果 List 中使我們自己定義的內容,比如說一個類,那該如何排序呢,如下:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 排序集合元素
 * 排序集合使用的是集合的工具類 Collections 的靜態方法 sort
 * 排序僅能對 List 集合進行,因為 Set 部分實現類是無序的
 */
public class Main {
    public static void main(String[] args) {
        List<Point> list = new ArrayList<Point>();
        list.add(new Point(1, 2));
        list.add(new Point(2, 3));
        list.add(new Point(4, 2));
        list.add(new Point(2, 5));
        list.add(new Point(9, 3));
        list.add(new Point(7, 1));
        System.out.println(list); // [(1, 2), (2, 3), (4, 2), (2, 5), (9, 3), (7, 1)]
        // Collections.sort(list); // 編譯錯誤,不知道 Point 排序規則
        /**
         * sort 方法要求集合必須實現 Comparable 接口
         * 該接口用於規定實現類事可以比較的
         * 其中一個 compareTo 方法時用來定義比較大小的規則
         * */
        Collections.sort(list);
        System.out.println(list); // [(1, 2), (2, 3), (4, 2), (2, 5), (7, 1), (9, 3)]

    }
}

class Point implements Comparable<Point> { // 定義為泛型 T 類型
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    /**
     * 當實現了 Comparable 接口后,需要重寫下面的方法
     * 該方法的作用是定義當前對象與給定參數對象比較大小的規則
     * 返回值為一個 int 值,該值表示大小關係
     * 它不關注具體的取值是多少,而關注的是取值範圍
     * 當返回值 >0 時:當前對象不參數對象大
     * 當返回值 <0 時:當前對象比參數對象小
     * 當返回值 =0 時:兩個對象相等
     */
    @Override
    public int compareTo(Point o) {
        /**
         * 比較規則,點到原點的距離長的打
         * */
        int len = this.x * this.x + this.y * this.y;
        int olen = o.x * o.x + o.y * o.y;
        return len - olen;
    }
}

在上面的代碼中,我們跟之前一樣定義了一個 Point 類,然後實例化后存入 list 集合中,如果我們相對 Point 進行排序,如果直接調用 sort 方法會報錯,這是由於編譯器不知道我們定義的 Point 類的排序規則,所以我們需要通過接口 Comparable 接口來自定義排序規則,如下圖:

 

我們可以通過坐標系中點到原點的距離來判斷 Point 的大小,即 x*x+y*y 的大小,然後通過重寫 compareTo 方法來實現 sort 。

實際上雖然能實現,但是並不理想,為了實現一個 sort 方法,要求我們的集合元素必須實現 Comparable 接口並且定義比較規則,這種我們想使用某個功能,而它要求我們修改程序的現象稱為“侵入性”。修改的代碼越多,侵入性越強,越不利於程序的擴展。

我們再來看一下下面的排序:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.List;
 4 
 5 public class Main {
 6     public static void main(String[] args) {
 7         List<String> list1 = new ArrayList<String>();
 8         list1.add("Java");
 9         list1.add("c++");
10         list1.add("Python");
11         list1.add("PHP");
12         Collections.sort(list1);
13         System.out.println(list1); // [Java, PHP, Python, c++]
14 
15         List<String> list2 = new ArrayList<String>();
16         list2.add("孫悟空");
17         list2.add("豬八戒");
18         list2.add("唐僧");
19         list2.add("六小齡童");
20         Collections.sort(list2);
21         System.out.println(list2); // [六小齡童, 唐僧, 孫悟空, 豬八戒]
22 
23     }
24 }

在上面的排序中,當我們對字母或漢字進行排序時,其實是比的第一個字的 Unicode 碼,那如果我們不使用 Comparable 的接口該如何自定義我們想要的實現規則呢?比如根據字符串的長度來進行排序,其實 sort 有一個額外的重載方法來實現我們想要的結果,如下:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.Comparator;
 4 import java.util.List;
 5 
 6 public class Main {
 7     public static void main(String[] args) {
 8         /**
 9          * 重載的 sort 方法要求傳入一個額外的比較器
10          * 該方法不再要求集合元素必須實現 Comparable 接口
11          * 並且不再使用集合元素資深的比較規則排序了
12          * 而是根據給定的這個額外的比較器的比較規則對集合元素進行排序
13          * 實際開發中也推薦使用這種方式進行集合元素排序
14          * 若集合元素是自定義的
15          * 創建比較器時推薦使用匿名內部類的形式
16          */
17         List<String> list1 = new ArrayList<String>();
18         list1.add("Java");
19         list1.add("c++");
20         list1.add("Python");
21         list1.add("PHP");
22         MyComparator myComparator1 = new MyComparator();
23         Collections.sort(list1, myComparator1); // 重載 sort
24         System.out.println(list1); // [c++, PHP, Java, Python]
25 
26         List<String> list2 = new ArrayList<String>();
27         list2.add("孫悟空");
28         list2.add("豬八戒");
29         list2.add("唐僧");
30         list2.add("六小齡童");
31         // 匿名內部類形式創建
32         Comparator<String> myComparator2 = new Comparator<String>() {
33             @Override
34             public int compare(String o1, String o2) {
35                 return o1.length() - o2.length();
36             }
37         };
38         Collections.sort(list2, myComparator2); // 重載 sort
39         System.out.println(list2); // [唐僧, 孫悟空, 豬八戒, 六小齡童]
40 
41     }
42 }
43 
44 /**
45  * 定義一個額外的比較器
46  * 該方法用來定義 o1 和 o2 的比較
47  * 若返回值 >0:o1>o2
48  * 若返回值 <0:o1<o2
49  * 若返回值 =0:兩個對象相等
50  */
51 class MyComparator implements Comparator<String> {
52     @Override
53     public int compare(String o1, String o2) {
54         /**
55          * 字符串中字符多的大
56          * */
57         return o1.length() - o2.length();
58     }
59 }

    

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

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

鋰電池再生利用,新創事業 FreeWire 推行動充電車

  特斯拉(Tesla)高調宣布鋰電池儲能系統,其中針對企業用戶的「Powerpack」,100 度電售價 25,000 美元,已將平均儲能成本拉低到相當驚人的每度電 250 美元,一般認為若平均儲能成本降到每度電 100 美元,將引爆能源儲存革命,特斯拉挾 GigaFactory 的威力,正全速逼近這個目標,不過,卻有一個小型新創事業,認為現在就可以做到。   這家新創事業 FreeWire 初期募資金額不過 42.5 萬美元,加上美國海軍資助 50 萬美元,如何能超越家大業大的特斯拉?其實原理也不難,就是採用二手電池。   FreeWire 、日產(Nissan)與 NEC 合資的汽車能源供應公司(Automotive Energy Supply Corporation,AESC)合作,AESC 是僅次於 Panasonic,全球第二大車用鋰電池供應商,市佔率達 21%,2014 年銷售最佳的電動車是 Nissan Leaf,其電池正是由 AESC 供應,Nissan 每個月都會測試電池,每六個月就汰換效能已經衰退的鋰電池,數量以千計,對 Nissan 來說庫存這些二線電池是個麻煩,但這些電池的效能或許已經不不敷電動車所需,拿來做為能源儲存用途卻還很有用。   於是,FreeWire 以新電池六分之一的價格取得這些二手電池,第一步,是取之於電動車用之於電動車,將這些二手電池組裝成總容量 48 度電的行動充電車,專供電動車在本來沒有設置充電座的停車場、機場、企業廠區等處充電,行動充電車可以推到停車場任何一個停車格,幫電動車充電,如此一來,停車場可以省下設置大量充電座的成本,卻能提供身為少數的電動車主充電服務,而這些二手電池在每天充放電循環 2 次的情況下,還可以有 5 年的壽命。  
 

    由於使用二手電池,FreeWire 裝置的每度電成本只有 100 美元,執行長阿卡迪‧索西諾夫(Arcady Sosinov)表示,電池產業界要到 2030 年才能達到的成本目標,他們用二手電池壓低成本,所以現在就已經做到。   阿卡迪‧索西諾夫的野心不只是為電動車行動充電,他認為二手電池的商機相當廣大,同樣的系統也可用來取代柴油發電機,為沒有電力供應之處供電,或是作為備援電力,而鋰電池供電比起柴油發電機有更大的優勢,那就是不會產生噪音與廢氣。如果要在戶外辦一場蚊子電影院,使用鋰電池系統顯然更好,而許多開發中國家的手機基地台因電網不穩定都有備用電源需求,以往這些取代柴油發電機的系統生意由奇異的獨拉松獨佔,如今以二手電池成本之低,也可與之競爭。   更進一步,二手電池系統也可進軍特斯拉正大張旗鼓進入的家庭與企業能源儲存領域,不過,二手電池的發展,也不是只會和特斯拉打對台,因為特斯拉發展電動車也一樣會面臨汰換大量二手電池,這些二手電池若都可再利用製成能源儲存裝置,對特斯拉也是一項利多。   FreeWire 利用二手電池的想法也給能源儲存業界一個啟發,過去鋰電池效能會衰退是產業界的一大問題,但換個方向思考,開啟電池的分級使用,反而可開創許多價格彈性商機。     本文全文授權轉載自《科技新報》─〈〉

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

【其他文章推薦】

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

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

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

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

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

刺激銷量?北京6月起收取電動車充電服務費

北京市發改委於5月7日發佈了《關於本市電動汽車充電服務收費有關問題的通知》,從2015年6月1日起,提供電動汽車充電服務的充電設施經營單位在收取電費的同時,可按充電電量額外收取充電服務費,每千瓦時收費上限標準為當日本市92號汽油每升最高零售價的15%。各經營單位可在不超過上限標準情況下,制定具體收費標準。   其充電服務費上限標準隨油價變動自行動態調整。如2015年4月29日本市92號汽油最高零售價為6.46元人民幣(下同)/升,則充電服務收費上限標準為6.46元的15%,即0.97元/度。油價上升,充電服務費相應上升油價下降,充電服務費相應下降。   北京市發改委副主任高朋指出,根據測算,此標準可以確保電動汽車動力成本低於燃油汽車。以本市銷售相對較好的北汽E150EV電動汽車為例(100公里平均耗電16度),當油價在6-10元/升區間變動時,充電服務費為每度電0.9元-1.5元。按國家發展改革委檔,對向電力公司直接報裝的充電服務設施,按大工業用電價格標準執行,按此測算,加上電價費用,電動汽車動力成本約為同款燃油汽車的50%-60%左右。   對此有觀點認為,發改委此舉會影響電動汽車銷量,降低消費者對電動車的購買慾。汽車產業分析師張志勇指出,目前,電動車推廣最大的障礙是充電問題,《通知》中規定了電動車充電服務費,可以鼓勵一些民營資本來加入電動車充電網路的建設,此舉將加快北京地區的充電網路建設。   張志勇還指出,即使充電設施經營單位額外收取充電服務費,這個成本也是遠遠低於燃油車的用車成本,這對消費者來看並不能產生多大影響。因此《通知》發布後不僅不會影響電動車的銷量,反而會增加消費者購買電動車的信心。

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

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

Salesforce LWC學習(十八) datatable展示 image

本篇參看:

https://developer.salesforce.com/docs/component-library/bundle/lightning-datatable/documentation

https://www.lightningdesignsystem.com/components/data-tables/

我們在salesforce中,經常會使用 listview,標準的 lightning listview會有排序,filter展示chart等等標準功能。當然,某些情況標準搞定不了情況下,我們便需要使用自定義的開發。自定義開發我們就可以使用 lightning-datatable組件。一個 lightning-datatable組件用來展示表格數據,它可以根據數據類型展示每一列。比如一個 email類型的字段會展示一個點開以後是一個 mail:to的超鏈接。默認展示的類型是text類型。那 lightning-datatable有哪些特性呢?

  • 展示和格式化焗油適當類型的列
  • 具有無限滾動行功能
  • 指定的數據類型允許 inline編輯
  • 可以定義header的action
  • 可以定義行級別的action
  • 重置每一列展示的大小
  • 選擇行操作
  • 指定的列通過升序或者降序排列
  • 文字換行和剪切
  • 對行進行編號
  • 設置單元格對齊方式

lightning-datatable有很多屬性,其他的請自行查看文檔,這裏只例舉幾個核心屬性或者方法。比較核心的如下:

  • columns:一組列的object用來定義數據類型,這個object有很多的屬性,其中有幾個必填的配置: label & fieldName & type。分別對應着字段的展示名稱,字段的API name以及字段的類型,不同的字段類型將會自動的轉換成不同的展示形式,默認是text。其他的屬性我們後期會有簡單介紹。
  • data:一組數據用來展示,通常 comulns設置表單的頭以及每個單元列的類型,data設置內容;
  • hide-checkbox-column:標籤用來設定是否展示左側的checkbox,值為true/false,true即隱藏checkbox;
  • selected-rows:用來初始化時展示哪些行是被選中的;
  • key-field:用來指定每行的unique id;
  • getSelectedRows:這個是lightning-datatable提供的方法,用來獲取哪些行用戶選中的。

簡單介紹完畢,下面通過幾點來慢慢帶入datatable的功能。

一. 基礎 Datatable展示數據

demo中展示,我們來看官方提供的一個demo。

dataTableExample1.html:用來展示一個 datatable,我們看到屬性中的data / columns / key-field都是上面描述過的, onrowselection方法為當有行選擇的時候的調用

<template>
    <lightning-datatable
            data={data}
            columns={columns}
            key-field="id"
            onrowselection={getSelectedName}>
    </lightning-datatable>
</template>

 dataTableExample1.js:有兩種方式可以獲取選擇的行信息,demo中有兩個。datatable有一個標準事件rowselection,調用以後可以通過event.detail.

selectedRows獲取選中的行的信息,另外一種就是找到datatable然後調用其getSelectedRows方法。

除此之外,我們看到columns的設置方式,除了 label / fieldName / type這三個基礎信息以外,還可以看到有 typeAttributes 以及 cellAttributes兩種設置。

  • typeAttributes:用於對指定的列的類型格式化dataCell,不同的type可能擁有不同的設置。salesforce針對不同的type可以設置不同的 typeAttributes,這裏不再詳細展開,具體查看文檔;
  • cellAttributes:typeAttributes用於設置針對 type的格式化,cellAttributes通常用於列中附加圖標信息,設置圖標位置等信息。
import { LightningElement } from 'lwc';

const columns = [
     {label: 'Opportunity name', fieldName: 'opportunityName', type: 'text'},
     {label: 'Confidence', fieldName: 'confidence', type: 'percent', cellAttributes:{ iconName: { fieldName: 'trendIcon' }, iconPosition: 'right' }},
     {label: 'Amount', fieldName: 'amount', type: 'currency', typeAttributes: { currencyCode: 'EUR'}},
     {label: 'Contact Email', fieldName: 'contact', type: 'email'},
     {label: 'Contact Phone', fieldName: 'phone', type: 'phone'},
];

const data = [{
                    id: 'a',
                    opportunityName: 'Cloudhub',
                    confidence: 0.2,
                    amount: 25000,
                    contact: 'jrogers@cloudhub.com',
                    phone: '2352235235',
                    trendIcon: 'utility:down'
                },
                {
                    id: 'b',
                    opportunityName: 'Quip',
                    confidence: 0.78,
                    amount: 740000,
                    contact: 'quipy@quip.com',
                    phone: '2352235235',
                    trendIcon: 'utility:up'
                }];

export default class DatatableExample1 extends LightningElement {
    data = data;
    columns = columns;

    getSelectedName(event) {
        //兩種方式獲取選中的行,第一種是使用 event.detail.selectedRows,另外一種是使用querySelector找到 lightning-datatable,然後使用datatable封裝的方法
        //const selectedRows = event.detail.selectedRows;
        const dataTable = this.template.querySelector('lightning-datatable');
        const selectedRows = dataTable.getSelectedRows();
        // Display that fieldName of the selected rows
        for (let i = 0; i < selectedRows.length; i++){
            console.log("You selected: " + selectedRows[i].opportunityName);
        }
    }
}

 簡單展示效果如下:

 二. datatable中展示父表中的字段值

上個demo中簡單介紹了datatable的使用,數據全是寫死的demo,接下來通過一個包含後台的邏輯進行datatable展示數據。

DataTableExampleController:用於獲取 Case表中的相關字段以及其父表的字段

public with sharing class DataTableExampleController {
    @AuraEnabled(cacheable=true)
    public static List<Case> getCaseList(){
        List<Case> caseList = [SELECT Id,CaseNumber,Account.Name,
                                    Priority,Status
                                FROM Case
                                LIMIT 100];
        return caseList;
    }
}

datatableExample2.js:用於展示相關的字段,通過wire adapter獲取後台數據放在data中

import { LightningElement, track, wire } from 'lwc';
import getCaseList from '@salesforce/apex/DataTableExampleController.getCaseList';
const COLUMNS = [
    {label: 'Case Number', fieldName: 'CaseNumber', type: 'text'},
    {label: 'Account Name', fieldName: 'Account.Name', type: 'text'},
    {label: 'Priority', fieldName: 'Priority', type: 'text'},
    {label: 'Status', fieldName: 'Status', type: 'text'}
];
export default class DataTableExample2 extends LightningElement {
    columns = COLUMNS;
    @track datas;

    @wire(getCaseList)
    wiredCaseList({ error, data }) {
        if(data) {
            this.datas = data;
        } else if(error) {
            //TODO
            console.log(JSON.stringify(error));
        }
    }
}

dataTableExample.html:展示datatable

<template>
    <lightning-datatable
            data={datas}
            columns={columns}
            key-field="id"
            >
    </lightning-datatable>
</template>

結果展示:通過下圖我們可以看到,其他的字段可以正常展示,但是父表的數據獲取確實失敗的。

 來剖析一下原因,datatable針對columns的fieldName只支持一級結構的獲取,針對獲取父的方式沒法通過 各種點的方式獲取到,那麼如何獲取呢,查看下面的方式,我們對js的代碼進行一下改動。按照下面的步驟兩步走的修改。

 結果展示:我們可以看到 Account Name列的值也順利的展示出來了,按照這種操作做一個拓展,其實我們也可以去做一些字段間的邏輯操作拼出一個符合我們邏輯的列,不僅僅局限於獲取父的對象值這種簡單操作。

 三. 實現datatable展示 formula類型是圖片的功能

自己看文檔的小夥伴可能發現datatable的columns的type沒有類似 image類型,但是在object field創建時我們很容易創建一些IMAGE的字段,那樣的話使用標準的datatable便無法展示,那要如何操作呢?還好lwc提供了一個自定義類型的操作。首先我們在Case表裡面新增一個formula 字段,展示圖片。

 接下來一步接着一步搞定。

dataTableWithImage.js:這裏需要繼承 LightningDatatable,並且引入 imageRow.html,其用於做template

import LightningDatatable from 'lightning/datatable';
import imageRow from './imageRow.html';
export default class DataTableWithImage extends LightningDatatable {
    static customTypes = {
        image: {
            template: imageRow
        }
    };
}

imageRow.html:默認要求 imageRow需要和 dataTableWithImage在同一個目錄下,如果不在一個目錄,需要改上面的文件目錄信息。

<template>
    <c-data-table-image-template
        url={value}
        >
    </c-data-table-image-template>
</template>

dataTableImageTemplate.html:用來通過 img標籤展示圖片

<template>
    <img src={url} class="image"/>
</template>

dataTableImageTemplate.css:用來設置圖片大小的初始值,這種是一種偷懶的方式,正常應該通過 attribute注入進來指定大小的寬和高。

.image {
  height: 30px;
  width: 30px;
}

dataTableImageTemplate.js

import { LightningElement,api } from 'lwc';

export default class DataTableImageTemplate extends LightningElement {
    @api url;
}

OK,通過上面的這些步驟我們實現和繼承了自定義的datatable組件。接下來是調用操作。我們在class裏面搜索一下Priority_Image__c這個字段,apexClass這裏不再寫。

dataTableExample3.js:這裡有幾個關鍵點。

  • 設置 type為 image類型,image類型是我們上文自定義創建的
  • 對 formula類型進行解析,找到他的src部分。比如針對<img src=”/resource/priority/priority/medium.png” alt=”Priority Flag” border=”0″/>這個字符串,我們需要的是/resource/priority/priority/medium.png這個字符串
import { LightningElement, track, wire } from 'lwc';
import getCaseList from '@salesforce/apex/DataTableExampleController.getCaseList';

const COLUMNS = [
    {label: 'Case Number', fieldName: 'CaseNumber', type: 'text'},
    {label: 'Account Name', fieldName: 'AccountName', type: 'text'},
    {label: 'Priority', fieldName: 'Priority', type: 'text'},
    { label: 'Priority Image',fieldName:'Priority_Image__c',type: 'image' },
    {label: 'Status', fieldName: 'Status', type: 'text'}
];
export default class DataTableExample3 extends LightningElement {
    columns = COLUMNS;
    @track datas;

    @wire(getCaseList)
    wiredCaseList({ error, data }) {
        if(data) {
            //this.datas = data;
            let resultList = [];
            let objectTemp;
            data.forEach(item => {
                objectTemp = Object.assign({},item);
                if(item.Account) {
                    objectTemp.AccountName = item.Account.Name;
                }

                const srcIndex = objectTemp.Priority_Image__c.indexOf('src=');
                if(srcIndex !== -1) {
                    const imgSrcSubstring = objectTemp.Priority_Image__c.substring(srcIndex + 5);
                    objectTemp.Priority_Image__c = imgSrcSubstring.substring(0,imgSrcSubstring.indexOf('"'))
                }
                console.log(JSON.stringify(objectTemp));
                resultList.push(objectTemp);
            });
            this.datas = resultList;
        } else if(error) {
            //TODO
            console.log(JSON.stringify(error));
        }
    }
}

dataTableExample3.html

<template>
    <c-data-table-with-image
                    key-field="id"
                    data={datas}
                    columns={columns}
                    >
                </c-data-table-with-image>
</template>

結果展示:針對不同的Priority展示其對應的formula的圖片。

總結:篇中講了datatable的簡單的應用以及相應的擴展,datatable很強悍,這裏不逐點講述,感興趣的可以自行查看 inline edit,action等操作。篇中有錯誤地方歡迎指出,有不懂的歡迎留言。

後記:工作原因這篇博客準備了好久才成型,不知不覺學了半年的lwc的開發,也整理總結了一些博客供自己和他人學習。前半年的主旋律大部分時間都在學習lwc,後半年的時間大部分會放在考證和學習其他技術,不出意外應該是今年的最後一篇lwc的博文。有其他lwc的問題歡迎留言探討,希望各位salesforce開發者可以養成獨立思考的能力,先思考,在查,最後再問。加油!

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

【其他文章推薦】

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

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

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

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

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

Spring Cloud Alibaba系列(五)sentinel實現服務限流降級

一、sentinel是什麼

sentinel的官方名稱叫分佈式系統的流量防衛兵。Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。在Spring Cloud項目中最開始我們使用的是Hystrix,目前已停止更新了。現在Spring Cloud官方推薦的是rensilience4j。當然還有我們今天學習的sentinel。

Sentinel 具有以下特徵:

  • 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峰填谷、集群流量控制、實時熔斷下游不可用應用等。
  • 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制台中看到接入應用的單台機器秒級數據,甚至 500 台以下規模的集群的匯總運 行情況。
  • 廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
  • 完善的 SPI 擴展點:Sentinel 提供簡單易用、完善的 SPI 擴展接口。您可以通過實現擴展接口來快速地定製邏輯。例如定製規則管理、適配動態數據源等。

二、sentinel實現限流

2.1 安裝sentinel控制台

  • 下載地址:https://github.com/alibaba/Sentinel/releases

這裏我們直接下載jar包即可,下載后通過命令行啟動:

java -jar sentinel-dashboard-1.7.2.jar
  • 默認端口:8080
  • 默認用戶名:sentinel
  • 默認密碼:sentinel

啟動成功后,我們瀏覽器訪問http://localhost:8080,出現如下界面。

2.2 微服務繼承sentinel

  • 引入sentinel依賴
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 添加sentinel的相關配置
server:
  port: 7003
spring:
  application:
    name: sentinel-provider
  cloud:
	nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
  • 提供個接口用來測試限流
@SpringBootApplication
public class SentinelApplication {

    public static void main(String[] args) {
        SpringApplication.run(SentinelApplication.class, args);
    }
}

@RestController
class TestController{
    @GetMapping("/test")
    public String test(){
        return "hello! sentinel!";
    }
}

我們請求幾次這個接口后,打開sentinel控制台,就可以實時監控到這個sentinel-provider服務接口調用情況了。

2.3 配置限流規則

我們這裏做一個簡單的規則配置:

  • 閥值類型:QPS

  • 單機閥值:2

意思就是:該接口每秒最多允許進入兩個請求。

點擊新增后,在流控規則里發現了一條規則:

現在,我們繼續請求3次這個接口。第三次響應的內容如下:

Blocked by Sentinel (flow limiting)

我們打開控制台發現拒絕了一條請求。

三、Sentinel規則介紹

不管是限流還是降級,它都是按照某種規則進行的,下面具體介紹一下sentinel支持的幾種規則。

3.1 流控規則

流量控制,其原理是監控應用流量的QPS(每秒查詢率) 或併發線程數等指標,當達到指定的閾值時

對流量進行控制,以避免被瞬時的流量高峰衝垮,從而保障應用的高可用性。

資源名:唯一名稱,默認是請求路徑,可自定義

針對來源:指定對哪個微服務進行限流,默認指default,意思是不區分來源,全部限制

閾值類型/單機閾值

  • QPS(每秒請求數量): 當調用該接口的QPS達到閾值的時候,進行限流

  • 線程數:當調用該接口的線程數達到閾值的時候,進行限流

3.2 降級規則

降級規則就是當滿足什麼條件時,對服務降級——即將請求轉發到另外接口上,這個接口與業務無關,只是為了保證系統的完整性。

  • RT(平均響應時間) :當資源的平均響應時間超過閾值(以 ms 為單位)之後,資源進入准降級狀態。如果接下來 1s 內持續進入 5 個請求,它們的 RT都持續超過這個閾值,那麼在接下的時間窗口(以 s 為單位)之內,就會對這個方法進行服務降級。

    注意 Sentinel 默認統計的 RT 上限是 4900 ms,超出此閾值的都會算作 4900 ms,若需要變更此上限可以通過啟動配置項 -Dcsp.sentinel.statistic.max.rt=xxx 來配置。

  • 異常比例:當資源的每秒異常總數占通過量的比值超過閾值之後,資源進入降級狀態,即在接下的時間窗口(以 s 為單位)之內,對這個方法的調用都會自動地返回。異常比率的閾值範圍是 [0.0,1.0]。

  • 異常數 :當資源近 1 分鐘的異常數目超過閾值之後會進行服務降級。注意由於統計時間窗口是分鐘級別的,若時間窗口小於 60s,則結束熔斷狀態后仍可能再進入熔斷狀態。

3.3 熱點規則

熱點規則允許將規則具體到參數上。

我們用個例子來看看效果。

  • 編寫接口
@GetMapping("/myTest")
@SentinelResource("test3")
public String test123(String name,String age){
    return  name + "----"+ age;
}
  • 添加規則
  • 運行效果

結果显示,第一個參數被限流了,而第二個參數正常。

3.4 系統規則

系統保護規則是從應用級別的入口流量進行控制,從單台機器的總體 Load、RT、入口 QPS 、CPU使用率和線程數五個維度監控應用數據,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。

系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量 (進入應用的流量) 生效。

  • Load(僅對 Linux/Unix-like 機器生效):當系統 load1 超過閾值,且系統當前的併發線程數超過系統容量時才會觸發系統保護。系統容量由系統的 maxQps * minRt 計算得出。設定參考值一般是 CPU cores * 2.5。

  • RT:當單台機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。

  • 線程數:當單台機器上所有入口流量的併發線程數達到閾值即觸發系統保護。

  • 入口 QPS:當單台機器上所有入口流量的 QPS 達到閾值即觸發系統保護。

  • CPU使用率:當單台機器上所有入口流量的 CPU使用率達到閾值即觸發系統保護。

3.5 授權規則

很多時候,我們需要根據調用來源來判斷該次請求是否允許放行,這時候可以使用 Sentinel 的來源問控制的功能。來源訪問控制根據資源的請求來源(origin)限制資源是否通過:

  • 若配置白名單,則只有請求來源位於白名單內時才可通過;

  • 若配置黑名單,則請求來源位於黑名單時不通過,其餘的請求通過。

流控應用:sentinel提供了RequestOriginParser來處理接口來源。

我們運行abc來源的請求訪問/test接口。

@Component
class requestOrigin implements RequestOriginParser{

    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String server = httpServletRequest.getParameter("server");
        return server;
    }
}

我們請求http://localhost:7003/test?server=abc 和 http://localhost:7003/test?server=ab來分別看看效果。

@SentinelResource的使用

@SentinelResource 用於定義資源,並提供可選的異常處理和 fallback 配置項。

主要參數有以下幾個

屬性 作用
value 資源名稱
entryType entry類型,標記流量的方向,取值IN/OUT,默認是OUT
blockHandler 處理BlockException的函數名稱,函數要求:1. 必須是 public;2.返回類型 參數與原方法一致;3. 默認需和原方法在同一個類中。若希望使用其他類的函數,可配置blockHandlerClass ,並指定blockHandlerClass裏面的方法。
blockHandlerClass 存放blockHandler的類,對應的處理函數必須static修飾。
fallback 1. 返回類型與原方法一致;2. 參數類型需要和原方法相匹配;3. 默認需和原方法在同一個類中。若希望使用其他類的函數,可配置fallbackClass
fallbackClass 存放fallback的類。對應的處理函數必須static修飾。
defaultFallback 若同時配置了 fallback 和 defaultFallback,以fallback為準。
exceptionsToIgnore 指定排除掉哪些異常。排除的異常不會計入異常統計,也不會進入fallback邏輯,而是原樣拋出。
exceptionsToTrace 需要trace的異常

@sentinelResource可結合blockHandler用於限流處理,結合fallback用於降級處理。具體規則可通過sentinel控制台配置,具體我就不演示了,在下一章內容中,我會分別演示限流和降級的應用。

public class MySentinelResource {

    @SentinelResource(value="message",blockHandler="blockHandler",fallback="fallback")
    public String message(String str){
        if(StringUtils.isBlank(str)){
            throw new RuntimeException();
        }
        return str;
    }
    /**
     * 限流處理
     * @param str
     * @param ex
     * @return
     */
    public String blockHandler(String str, BlockedException ex){
        return str + "--"+ ex;
    }
    /**
     * 降級處理
     * @param str
     * @return
     */
    public String fallback(String str){
        return null;
    }
}

代碼示例

gitee:https://gitee.com/zhixie/spring-cloud-alibaba-learning/tree/master/sentinel-server

github:https://github.com/binzh303/spring-cloud-alibaba-learning/tree/master/sentinel-server

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

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

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

特斯拉:將調整以符合中國充電標準

美國電動車大廠特斯拉(Tesla)表示,在中國發布全國電動車充電標準後,該公司將修改新生產的車輛,以便用戶可在符合新標準的充電設施,享受充電服務。    特斯拉在中文網站發布公告:「特斯拉將全面支持新國標,修改新生產車輛以符合新國標的規定… 隨著未來幾年中國充電基礎設施的飛速發展,特斯拉用戶將徹底消除對於充電的顧慮。」    特斯拉的充電規格和中國不相容,是消費者不敢購買電動車的一大阻力。不過,中國當局對此尚未提出明確的時間表,電動車充電的新標準何時出爐仍不得而知。

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

【其他文章推薦】

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

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

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

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

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