部落格

  • 蟬聯兩年!Tesla Models S 再登美國年度最佳汽車

    美國消費者報告(Consumer Reports)的調查顯示,電動車製造商 Tesla 的 Model S 已連續 2 年榮登年度最佳汽車。   消費者報告是根據道路表現、可靠度和防撞測試結果來評比車輛,除了選出整體表現最佳的汽車外,也會按照車種來評比。Model S 今年之所以蟬聯年度最佳汽車,原因除了充滿電後能跑 426 公里外,能夠透過網路升級軟體、及 Tesla 已克服初期面臨的技術問題,也是這輛車獲得青睞的原因。   在消費者報告劃分的十大分類中,只有 6 類是由日本車贏得最佳汽車頭銜,為消費者報告 19 年前開始評比以來最少,贏得最佳汽車頭銜的美國汽車品牌則有 3 個。除了 Tesla,另外 2 個品牌是 Buick 及雪佛蘭(Chevrolet)。

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

    【其他文章推薦】

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

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

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

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

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

  • 日本充電樁達4萬個 超傳統加油站

    日產汽車(Nissan)日前宣佈,目前日本全國有4萬個充電設施,其中包括家用充電站,而加油站只有3.4萬個,充電樁數量已超過傳統加油站。   去年5月,豐田、日產、本田、三菱這四大日本汽車製造商與日本政策投資銀行共同成立了“日本充電服務”公司,承擔起在商業設施等地設置充電樁的成本及8年內充電樁的維護費用。   日本便利店巨頭也表示將加快完善電動車充電設施,期待以此增加客流量。全家便利店在今年3月底之前將可為電動車及插電式混合動力車快速充電的店鋪增加至650家,期待達到同行業數量之最,並成為首個能源補給設施遍佈日本全境的便利店企業。羅森便利店也計畫在今年5月前將電動車充電樁由目前的12個增至200個左右。

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

    【其他文章推薦】

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

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    新北清潔公司,居家、辦公、裝潢細清專業服務

  • 比亞迪電動巴士入日本市場 遭日本人吐槽「跑跑就散架」

    比亞迪電動巴士入日本市場 遭日本人吐槽「跑跑就散架」

    近日,比亞迪電動巴士K9亮相日本京都地區,成為首個進入日本市場的中國汽車品牌。據日本當地媒體報導,本次引入京都的比亞迪電動巴士共計5輛,每輛車核定乘員人數69人,將由京都急行巴士株式會社運營。   環球網掌握的公開資料顯示,該款單層電動巴士完成單次充電後,可行駛約250公里。比亞迪在美國亦有生產電動巴士K9,售價約80萬美元。而從 2011年3月比亞迪與丹麥最大的公交公司Movia達成純電動公交合作開始,K9已經駛入了全球五大洲的多個城市,從德國的法蘭克福、到美國的華盛頓、再到英國的倫敦,多個新能源公交領域的訂單花落比亞迪。   而對於比亞迪電動巴士的亮相,日本各界表現出了截然相反的態度。一方面,日本國土交通省對於中國自主品牌積極「點贊」。在京都國立博物館內舉行的K9運營啟動儀式上,日本國土交通省官員阪部光雄表示:「非常高興能在京都迎來比亞迪的純電動巴士,這是京都公交系統首次採用零排放的純電動巴士,運營純電動巴士對於地區的環境治理意義非常深遠。」

    但也有一些日本線民並不是表現那麼「淡定」,他們反應強烈。有網友在網上留言表示,「還不知道一年後是啥樣兒••••估計跑著跑著就散架了,這都能想像到!反正我堅決不坐。」有的網友則質疑純電動公車的續航里程,「到底一次能跑幾個小時啊?不說滿電能跑幾天,也不說充滿電能運行多久••••總感覺這車的事故處理費用和乘客賠償費用會跟高啊!」

    有的甚至表示,「買這廉價玩意兒,還不如讓有軌電車重新上路呢!」還有人則對日本媒體對此事的報導不滿,「看到這條新聞,總感覺很不爽,報導最後竟然還來了一句「今後訪問京都的中國遊客多數人都會看到BYD在日本街頭的雄姿」。」

    不少人則開始擔憂中國產品大量湧入日本,其驚呼,「(中國製造開始不斷湧入日本市場了!)從電視到電腦,再到智慧手機,現在居然輪到汽車了啊!」

    也有一些上了年紀的日本網友感歎中國製造的迅速崛起,「日本的車企也在做HV巴士,但純電動巴士好像還沒有在做呢。估計(比亞迪K9)比較便宜吧,但還是希望安全第一。1985年的時候,日本贈送給了北京兩輛公車,30年後,中國巴士開始進入日本了!好神奇!不過首先讓我們來一起見證這輛車的性能吧!」   文章來源:環球網

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

    【其他文章推薦】

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

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

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

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

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

  • 台達電電動車驅動馬達 打入福斯供應鏈

    電源供應器廠積極跨入車用市場,台達電耕耘 3 年的電動車驅動馬達打入福斯集團供應鏈,今年開始出貨;新巨微動開關則陸續取得德國雙 B、奧迪與福斯,美系福特和日系車廠訂單;康舒電源轉換器也與多家國際電動車大廠談合作。   台達電董事長海英俊表示,目前各國積極發展電動車,基礎設施也開始動起來,因此台達電最近在電動車零組件有不錯的進展,包括出貨給美國知名電動車廠充電樁、轉換器,台達電在歐、美、日、中國等各地的電動車標準都通過認證。   海英俊透露,台達電研發電動車驅動馬達,經過 3 年努力,已獲歐洲車廠認證。

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

    【其他文章推薦】

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 雷軍:小米3至5年內不做電動車

    今日,雷軍出席亞布力中國企業家論壇第十五屆年會,並發表了演講。對於進入電動車領域的傳聞,雷軍稱小米三到五年內不會涉及,主要目標是把現有產品做好。   他明確表示,小米佈局已經完成,包括三類、五種產品,包括手機、平板、電視、電視盒、路由器等,主要是為了智慧家居。他稱,「(不涉及電動車)跟市場好壞沒關係,我們專注的把現在幾個產品做好,就已經很不容易了。不是不看好,是精力有限。」

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

    【其他文章推薦】

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

  • 使用 nuget server 的 API 來實現搜索安裝 nuget 包

    使用 nuget server 的 API 來實現搜索安裝 nuget 包

    使用 nuget server 的 API 來實現搜索安裝 nuget 包

    Intro

    nuget 現在幾乎是 dotnet 開發不可缺少的一部分了,還沒有用過 nuget 的就有點落後時代了,還不快用起來

    nuget 是 dotnet 里的包管理機制,類似於前端的 npm ,php 的 composer,java 里的 maven …

    nuget 定義了一套關於 nuget server 的規範,使得用戶可以自己實現一個 nuget server

    也正是這些規範,使得我們可以根據這些規範來實現 nuget server 的包管理的功能,今天主要介紹一下,根據 nuget server 的 api 規範使用原始的 HTTP 請求來實現 nuget 包的搜索和使用 nuget 提供的客戶端 SDK 來實現 nuget 包的搜索和下載

    Nuget Server Api

    Nuget 協議介紹

    nuget 的協議有好幾個版本,目前主要用的是 v3,開源的 nuget server Baget 也實現了基於 nuget protocal v3 的規範

    我們添加 nuget 源的時候會指定一個 source url,類似 https://api.nuget.org/v3/index.json 這樣的,着通常被稱為 Service Index,是一個 nuget 源的入口,有點類似於 Identity Server 里的發現文檔,通過這個地址可以獲取到一系列的資源的地址

    有一些資源是協議規範里定義的必須要實現的,有一些是可選的,具體參考官方文檔,以後隨着版本變化,可能會有差異,目前 nuget.org 提供的資源如下:

    Nuget.org 提供了兩種搜索的方式,

    一個是 SearchQuery,會根據包名稱、 tag、description 等信息去匹配關鍵詞,

    一個是 SearchAutocomplete 根據包名稱的前綴去匹配包的名稱

    獲取某個 nuget 包的版本信息,可以使用 PackageBaseAddress 來獲取

    ServiceIndex 返回的信息示例如下:

    返回的信息會有一個 resources 的數組,會包含各種不同類型的資源,對應的 @id 就是調用這種類型的API要用到的地址,下面來看一個搜索的示例

    在每個 API 的文檔頁面可以看到會使用的 @type,調用這個 API 的時候應該使用這些 @type 對應的資源

    這裏的 @id 就是上面的 resource 對應的 @id

    參數說明:

    q 搜索時所用的關鍵詞,

    skip/take 用來分頁显示查詢結果,

    prelease 用來指定是否限制預覽版的 package,true 包含預覽版的 nuget 包,false 只包含已經正式發布的 nuget 包

    semVerLevel 是用來指定包的語義版本

    The semVerLevel query parameter is used to opt-in to SemVer 2.0.0 packages. If this query parameter is excluded, only packages with SemVer 1.0.0 compatible versions will be returned (with the standard NuGet versioning caveats, such as version strings with 4 integer pieces). If semVerLevel=2.0.0 is provided, both SemVer 1.0.0 and SemVer 2.0.0 compatible packages will be returned. See the SemVer 2.0.0 support for nuget.org for more information

    packageType 用來指定 nuget 包的類型,目前支持的類型包括 Dependency(默認)項目依賴項,DotnetTool(dotnetcore 2.1 引入的 dotnet cli tool),Template (dotnet new 用) 自定義的項目模板

    其他的 API 可以自行參考官方文檔:https://docs.microsoft.com/en-us/nuget/api/service-index

    Packages

    SearchQuery 返回的信息比較多而且可能並不準確,適用於不清楚包的名稱的時候使用,如果知道 nuget 包的名稱(PackageId) ,可以使用 SearchAutocomplete 來搜索,這樣更精準,返回的信息也更簡單,只有匹配的 package 名稱

    通過原始 api 調用的方式實現 nuget 包的搜索

    using var httpClient = new HttpClient(new NoProxyHttpClientHandler());
    // loadServiceIndex
    var serviceIndexResponse = await httpClient.GetStringAsync(NugetServiceIndex);
    var serviceIndexObject = JObject.Parse(serviceIndexResponse);
    
    var keyword = "weihanli";
    
    //https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
    var queryEndpoint = serviceIndexObject["resources"]
        .First(x => x["@type"].Value<string>() == "SearchQueryService")["@id"]
        .Value<string>();
    var queryUrl = $"{queryEndpoint}?q={keyword}&skip=0&take=5&prerelease=false&semVerLevel=2.0.0";
    var queryResponse = await httpClient.GetStringAsync(queryUrl);
    Console.WriteLine($"formatted queryResponse:");
    Console.WriteLine($"{JObject.Parse(queryResponse).ToString(Formatting.Indented)}");
    
    // https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource
    var autoCompleteQueryEndpoint = serviceIndexObject["resources"]
        .First(x => x["@type"].Value<string>() == "SearchAutocompleteService")["@id"]
        .Value<string>();
    var autoCompleteQueryUrl = $"{autoCompleteQueryEndpoint}?q={keyword}&skip=0&take=5&prerelease=false&semVerLevel=2.0.0";
    var autoCompleteQueryResponse = await httpClient.GetStringAsync(autoCompleteQueryUrl);
    Console.WriteLine($"formatted autoCompleteQueryResponse:");
    Console.WriteLine($"{JObject.Parse(autoCompleteQueryResponse).ToString(Formatting.Indented)}");
    
    

    output 示例:

    Query 返回示例

    Autocomplete 返回結果

    從上面我們可以看到 Query 接口返回了很多的信息,Autocomplete 接口只返回了 package 的名稱,返回的信息更為簡潔,所以如果可以使用 Autocomplete 的方式就盡可能使用 Autocomplete 的方式

    Package Versions

    前面我們提到了可以使用 PackageBaseAddress 來查詢某個 nuget 包的版本信息,文檔地址:https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource,來看一下示例:

    using (var httpClient = new HttpClient(new NoProxyHttpClientHandler()))
    {
        // loadServiceIndex
        var serviceIndexResponse = await httpClient.GetStringAsync(NugetServiceIndex);
        var serviceIndexObject = JObject.Parse(serviceIndexResponse);
    
        // https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
        var packageVersionsEndpoint = serviceIndexObject["resources"]
            .First(x => x["@type"].Value<string>() == "PackageBaseAddress/3.0.0")["@id"]
            .Value<string>();
    
        var packageVersionsQueryUrl = $"{packageVersionsEndpoint}/dbtool.core/index.json";
        var packageVersionsQueryResponse = await httpClient.GetStringAsync(packageVersionsQueryUrl);
        Console.WriteLine("DbTool.Core versions:");
        Console.WriteLine(JObject.Parse(packageVersionsQueryResponse)
                          .ToString(Formatting.Indented));
    }
    

    output 示例:

    注:api 地址中的 packageId 要轉小寫

    Nuget Client SDK

    除了上面的根據 api 自己調用,我們還可以使用 nuget 提供的客戶端 sdk 實現上述功能,這裏就不詳細介紹了,有需要可能查閱官方文檔:https://docs.microsoft.com/en-us/nuget/reference/nuget-client-sdk

    下面給出一個使用示例:

    var packageId = "WeihanLi.Common";
    var packageVersion = new NuGetVersion("1.0.38");
    
    var logger = NullLogger.Instance;
    var cache = new SourceCacheContext();
    // 在 SDK 的概念里,每一個 nuget 源是一個 repository
    var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
    
    // SearchQuery
    {
        var resource = await repository.GetResourceAsync<PackageSearchResource>();
        var searchFilter = new SearchFilter(includePrerelease: false);
    
        var results = await resource.SearchAsync(
            "weihanli",
            searchFilter,
            skip: 0,
            take: 20,
            logger,
            CancellationToken.None);
        foreach (var result in results)
        {
            Console.WriteLine($"Found package {result.Identity.Id} {result.Identity.Version}");
        }
    }
    // SearchAutoComplete
    {
        var autoCompleteResource = await repository.GetResourceAsync<AutoCompleteResource>();
        var packages =
            await autoCompleteResource.IdStartsWith("WeihanLi", false, logger, CancellationToken.None);
        foreach (var package in packages)
        {
            Console.WriteLine($"Found Package {package}");
        }
    }
    //
    {
        // get package versions
        var findPackageByIdResource = await repository.GetResourceAsync<FindPackageByIdResource>();
        var versions = await findPackageByIdResource.GetAllVersionsAsync(
            packageId,
            cache,
            logger,
            CancellationToken.None);
    
        foreach (var version in versions)
        {
            Console.WriteLine($"Found version {version}");
        }
    }
    

    More

    你可以使用 nuget sdk 方便的實現 nuget 包的下載安裝,內部實現了簽名校驗等,這樣就可以把本地不存在的 nuget 包下載到本地了,

    實現示例:

    {
        var pkgDownloadContext = new PackageDownloadContext(cache);
        var downloadRes = await repository.GetResourceAsync<DownloadResource>();
    
        var downloadResult = await RetryHelper.TryInvokeAsync(async () =>
            await downloadRes.GetDownloadResourceResultAsync(
                new PackageIdentity(packageId, packageVersion),
                pkgDownloadContext,
                @"C:\Users\liweihan\.nuget\packages", // nuget globalPackagesFolder
                logger,
                CancellationToken.None), r => true);
        Console.WriteLine(downloadResult.Status.ToString());
    }
    

    最後提供一個解析 nuget globalPackagesFolder 的兩種思路:

    一個是前面有篇文章介紹的,有個默認的配置文件,然後就是默認的配置,寫了一個解析的方法示例,支持 Windows/Linux/Mac:

    {
        var packagesFolder = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
    
        if (string.IsNullOrEmpty(packagesFolder))
        {
            // Nuget globalPackagesFolder resolve
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                var defaultConfigFilePath =
                    $@"{Environment.GetEnvironmentVariable("APPDATA")}\NuGet\NuGet.Config";
                if (File.Exists(defaultConfigFilePath))
                {
                    var doc = new XmlDocument();
                    doc.Load(defaultConfigFilePath);
                    var node = doc.SelectSingleNode("/configuration/config/add[@key='globalPackagesFolder']");
                    if (node != null)
                    {
                        packagesFolder = node.Attributes["value"]?.Value;
                    }
                }
    
                if (string.IsNullOrEmpty(packagesFolder))
                {
                    packagesFolder = $@"{Environment.GetEnvironmentVariable("USERPROFILE")}\.nuget\packages";
                }
            }
            else
            {
                var defaultConfigFilePath =
                    $@"{Environment.GetEnvironmentVariable("HOME")}/.config/NuGet/NuGet.Config";
                if (File.Exists(defaultConfigFilePath))
                {
                    var doc = new XmlDocument();
                    doc.Load(defaultConfigFilePath);
                    var node = doc.SelectSingleNode("/configuration/config/add[@key='globalPackagesFolder']");
                    if (node != null)
                    {
                        packagesFolder = node.Attributes["value"]?.Value;
                    }
                }
    
                if (string.IsNullOrEmpty(packagesFolder))
                {
                    defaultConfigFilePath = $@"{Environment.GetEnvironmentVariable("HOME")}/.nuget/NuGet/NuGet.Config";
                    if (File.Exists(defaultConfigFilePath))
                    {
                        var doc = new XmlDocument();
                        doc.Load(defaultConfigFilePath);
                        var node = doc.SelectSingleNode("/configuration/config/add[@key='globalPackagesFolder']");
                        if (node != null)
                        {
                            packagesFolder = node.Value;
                        }
                    }
                }
    
                if (string.IsNullOrEmpty(packagesFolder))
                {
                    packagesFolder = $@"{Environment.GetEnvironmentVariable("HOME")}/.nuget/packages";
                }
            }
        }
    
        Console.WriteLine($"globalPackagesFolder: {packagesFolder}");
    }
    

    另一個是可以根據 nuget 提供的一個命令查詢 nuget locals global-packages -l,通過命令輸出獲取

    Reference

    • https://github.com/WeihanLi/SamplesInPractice/blob/master/NugetSample/RawApiSample.cs
    • https://github.com/WeihanLi/SamplesInPractice/blob/master/NugetSample/NugetClientSdkSample.cs
    • https://docs.microsoft.com/en-us/nuget/reference/nuget-client-sdk
    • https://docs.microsoft.com/en-us/nuget/create-packages/set-package-type
    • https://docs.microsoft.com/en-us/nuget/api/overview
    • https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource

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

    【【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

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

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

    ※教你寫出一流的銷售文案?

  • Zookeeper分佈式過程協同技術 – 群首選舉

    Zookeeper分佈式過程協同技術 – 群首選舉

    Zookeeper分佈式過程協同技術 – 群首選舉

    群首概念

    群首為集群中服務器選擇出來的一個服務器,並被集群認可。設置群首目的在與對客戶端所發起的狀態變更請求進行排序,包括:create、setData、delete操作。群首將每一個請求轉換為一個事務並將事務發送給追隨者,確保集群按照群首確定的順序接受並處理這些事務。

    Zookeeper事務

    Zookeeper服務器會在本地處理只讀請求(例如:exists、getData、getChildren)。如果一台服務器接收到客戶端的getData請求,服務器讀取該狀態信息,並將這些信息返回客戶端。由於服務器在本地處理讀請求,所以在處理以只讀請求為主要負載時,性能會比較高。

    那些會改變狀態的客戶端請求(create、delete、setData)將會被轉發給群首,由群首執行相應的請求,完成狀態的更新,這就是Zookeeper的事務。

    選舉過程

    每個服務器啟動後進入LOOKING狀態,服務器之間進行通信來選舉一個群首,通過信息交換對群首選舉達成共識。在本次選舉中勝出的服務器將進入LEADING狀態,而集群中其他服務器將進入FOLLOWING狀態。

    群首選舉消息(leader election notifications)或簡單的稱為通知。當一個服務器進入LOOKING狀態就會向集群中的每一個服務器發送一個通知消息。消息中包含該服務器的投票信息。

    投票信息包含服務器標識符(sid)和最近執行的事務的zxid信息,例如投票信息(1,5)代表機器ID為1,最近執行的事務zxid為5。

    zxid為一個long型(64位)整數,分為2部分:時間戳部分和計數器部分,每個部分為32位。

     當一個服務器收到一個投票信息,該服務器將會根據以下規則修改自己的投票信息:

    1. 將接受的voteId和voteZxid作為一個標識符,並獲取接收方當前的投票中的zxid,用myZxid和mySid表示接收方服務器自己的值。
    2. 如果(voteZxid > myZxid)或者(voteZxid = myZxid 且 voteId > mySid),保留當前的投票信息。
    3. 否則,修改自己的投票信息,將voteZxid賦值給myZxid,將voteId賦值給mySid。

    當一個服務器收到的仲裁數量的服務器發來的投票信息都一樣時,就表示群首選舉成功,如果被選舉的群首為某個服務器自己,該服務器將會開始行使群首角色,否則就會成為一個追隨者並嘗試連接被選舉的群首服務器。一旦連接成功,追隨者和群首之間將會進行狀態同步,在同步完成后,追隨者才可以處理新的請求。 

     

     

    通過例子來演示選舉過程,三台服務器在分別發送出不同的選舉投票信息,其投票值包含服務器的標識符和最新的zxid。每個服務器都會收到另外兩個服務器發送的投票信息,在第一輪之後,服務器S2和S3將會改變其投票信息為(1,6),之後服務器S2和S3在改變投票信息之後會發送新的通知消息,S1服務器在接收到仲裁數量的通知消息擁有一樣的投票信息,最後S1被選舉出為集群的群首。

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • C# 9.0 終於來了, Top-level programs 和 Partial Methods 兩大新特性探究

    C# 9.0 終於來了, Top-level programs 和 Partial Methods 兩大新特性探究

    一:背景

    1. 講故事

    .NET 5 終於在 6月25日 發布了第六個預覽版,隨之而來的是更多的新特性加入到了 C# 9 Preview 中,這個系列也可以繼續往下寫了,廢話不多說,今天來看一下 Top-level programsExtending Partial Methods 兩大新特性。

    2. 安裝必備

    下載最新的 .net 5 preview 6

    下載最新的 Visual Studio 2019 version 16.7 Preview 3.1

    二:新特性研究

    1. Top-level programs

    如果大家玩過 python,應該知道在 xxx.py 中寫一句 print,這程序就能跑起來了,簡單高效又粗暴,很開心的是這特性被帶到了C# 9.0 中。

    • 修改前
    
    using System;
    
    namespace ConsoleApp2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Hello World!");
            }
        }
    
    
    • 修改后
    
    System.Console.WriteLine("Hello World!");
    
    

    這就有意思了,Main入口函數去哪了? 沒它的話,JIT還怎麼編譯代碼呢? 想知道答案的話用 ILSpy 反編譯看一下就好啦!

    
    .class private auto ansi abstract sealed beforefieldinit $Program
    	extends [System.Runtime]System.Object
    {
    	.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
    		01 00 00 00
    	)
    	// Methods
    	.method private hidebysig static 
    		void $Main (
    			string[] args
    		) cil managed 
    	{
    		// Method begins at RVA 0x2050
    		// Code size 18 (0x12)
    		.maxstack 8
    		.entrypoint
    
    		IL_0000: ldstr "Hello World!"
    		IL_0005: call void [System.Console]System.Console::WriteLine(string)
    		IL_000a: nop
    		IL_000b: call string [System.Console]System.Console::ReadLine()
    		IL_0010: pop
    		IL_0011: ret
    	} // end of method $Program::$Main
    
    } // end of class $Program
    
    
    

    從 IL 上看,類變成了 $Program, 入口方法變成了 $Main, 這就好玩了,在我們的印象中入口函數必須是 Main,否則編譯器會給你一個大大的錯誤,你加了一個 $ 符號,那CLR還能認識嗎? 能不能認識我們用 windbg 看一些託管和非託管堆棧,看看有什麼新發現。

    
    0:010> ~0s
    ntdll!NtReadFile+0x14:
    00007ffe`f8f8aa64 c3              ret
    0:000> !dumpstack
    OS Thread Id: 0x7278 (0)
    Current frame: ntdll!NtReadFile + 0x14
    Child-SP         RetAddr          Caller, Callee
    0000008551F7E810 00007ffed1e841dc (MethodDesc 00007ffe4020d500 + 0x1c System.Console.ReadLine()), calling 00007ffe400ab090
    0000008551F7E840 00007ffe4014244a (MethodDesc 00007ffe401e58f0 + 0x3a $Program.$Main(System.String[])), calling 00007ffe40240f58
    0000008551F7E880 00007ffe9fcc8b43 coreclr!CallDescrWorkerInternal + 0x83 [F:\workspace\_work\1\s\src\coreclr\src\vm\amd64\CallDescrWorkerAMD64.asm:101]
    0000008551F7E8C0 00007ffe9fbd1e03 coreclr!MethodDescCallSite::CallTargetWorker + 0x263 [F:\workspace\_work\1\s\src\coreclr\src\vm\callhelpers.cpp:554], calling coreclr!CallDescrWorkerWithHandler [F:\workspace\_work\1\s\src\coreclr\src\vm\callhelpers.cpp:56]
    0000008551F7E950 00007ffe9fb8c4e5 coreclr!MethodDesc::IsVoid + 0x21 [F:\workspace\_work\1\s\src\coreclr\src\vm\method.cpp:1098], calling coreclr!MetaSig::IsReturnTypeVoid [F:\workspace\_work\1\s\src\coreclr\src\vm\siginfo.cpp:5189]
    0000008551F7EA00 00007ffe9fb8c4bf coreclr!RunMainInternal + 0x11f [F:\workspace\_work\1\s\src\coreclr\src\vm\assembly.cpp:1488], calling coreclr!MethodDescCallSite::CallTargetWorker [F:\workspace\_work\1\s\src\coreclr\src\vm\callhelpers.cpp:266]
    0000008551F7EB30 00007ffe9fb8c30a coreclr!RunMain + 0xd2 [F:\workspace\_work\1\s\src\coreclr\src\vm\assembly.cpp:1559], calling coreclr!RunMainInternal [F:\workspace\_work\1\s\src\coreclr\src\vm\assembly.cpp:1459]
    
    

    從上面堆棧的流程圖看: coreclr!RunMain -> coreclr!MethodDesc -> coreclr!CallDescrWorkerInternal -> $Program.$Main, 確實被調用了,不過有一個重大發現,在 $Program.$Main 調用之前底層的 CLR 讀取了 方法描述符,這就是一個重大突破點,方法描述符在哪裡呢? 可以用 ildasm 去看一下元數據列表。

    可以看到,入口函數那裡打上了一個 ENTRYPOINT 標記,這就說明入口函數名其實是可以隨便更改的,只要被 ENTRYPOINT打上標記即可,CoreCLR就能認的出來~~~

    2. Partial Methods

    我們知道 部分方法 是一個很好的樁函數,而且在 C# 3.0 中就已經實現了,那時候給我們增加了很多限制,如下圖:

    翻譯過來就是:

    • 部分方法的簽名必須一致

    • 方法必須返回void

    • 不允許使用訪問修飾符,而且還是隱式私有的。

    在 C# 9.0 中放開了對 方法簽名 的所有限制,正如 issue 總結:

    這是一個非常好的消息,現在你的部分方法上可以加上各種類型的返回值啦,這裏我舉一個例子:

    
        class Program
        {
            static void Main(string[] args)
            {
                var person = new Person();
    
                Console.WriteLine(person.Run("jack"));
            }
        }
    
        public partial class Person
        {
            public partial string Run(string name);
        }
    
        public partial class Person
        {
            public partial string Run(string name) => $"{name}:開溜了~";
        }
    
    

    然後我們用 ILSpy 簡單看看底層怎麼玩的,如下圖可以看到其實就是一個簡單的合成,對吧。

    現在我有想法了,如果我不給 Run 方法實現會怎麼樣? 把下面的 partial 類註釋掉看一下。

    從報錯信息看,可訪問的修飾符必須要有方法實現,還以為直接編譯的時候抹掉呢。 這就起不到樁函數的作用:-D,不過這個特性還是給了我們更多的可能用的到的應用場景吧。

    三:總結

    本篇兩個特性還是非常實用的,Top-level programs 讓我們可以寫更少的代碼,甚至拿起 記事本 都可以快捷的編寫類似一次性使用的測試代碼, Partial Methods 特性留給大家補充吧,我基本上算是沒用過 (┬_┬)。

    如您有更多問題與我互動,掃描下方進來吧~

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

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

    ※回頭車貨運收費標準

  • Python 簡明教程 — 20,Python 類中的屬性與方法

    Python 簡明教程 — 20,Python 類中的屬性與方法

    微信公眾號:碼農充電站pro
    個人主頁:https://codeshellme.github.io

    與客戶保持良好的關係可以使生產率加倍。
    —— Larry Bernstain

    目錄

    類中的變量稱為屬性,類中的函數稱為方法

    類中的屬性分為:

    • 實例屬性:對象所有,互不干擾
    • 類屬性:類所有,所有對象共享

    類中的方法分為:

    • 實例方法:定義中有self 參數
    • 類方法:定義中有cls 參數,使用@classmethod 裝飾器
    • 靜態方法:定義中即沒有self 參數,也沒有cls 參數,使用@staticmethod 裝飾器

    1,實例屬性與類屬性

    類的對象,就是類的一個實例。類的實例屬性被對象所有,包含在每個對象之中,不同的對象之間,互不干擾。類的類屬性被類所有,被包含在類中,是所有的類對象共享。

    一般情況下,實例屬性會在__init__ 方法中聲明並初始化,並且使用self 來綁定。而類屬性是在類作用域中被聲明,並且不使用self 來綁定。

    例如下面代碼中,country 為類屬性,__name 為實例屬性:

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    

    訪問實例屬性時使用對象.屬性名的格式,實例屬性屬於對象各自的,互不影響:

    >>> p1 = People('小明')
    >>> p2 = People('小美')
    >>> 
    >>> p1.get_name()
    '小明'
    >>> p2.get_name()
    '小美'
    

    類屬性被所有對象共有,一旦被改變,所有對象獲取到的值都會被改變。訪問類屬性時使用類名.屬性名的格式,也可以使用對象.屬性名的格式來訪問:

    >>> People.country              # 用`類名.屬性名`的格式訪問
    'CHINA'
    >>> p1.country                  # 用`對象.屬性名`的格式訪問
    'china'
    >>> p2.country
    'china'
    >>> 
    >>> People.country = 'CHINA'    # 類屬性的值被改變
    >>> p1.country                  # 每個對象獲取到的值也會被改變
    'CHINA'
    >>> p2.country
    'CHINA'
    

    注意,在使用對象.屬性名的格式訪問對象時,不要以這種格式對類屬性進行賦值,否則結果可能不會像你想象的一樣:

    >>> p1 = People('小明')
    >>> p2 = People('小美')
    >>> People.country
    'china'
    >>> p1.country
    'china'
    >>> p2.country
    'china'
    >>> p1.country = '中國'   # 使用`對象.屬性名`的格式對類對象進行賦值
    >>> p1.country           # 只有 p1.country 被改變
    '中國'
    >>> p2.country           # p2.country 沒有被改變
    'china'
    >>> People.country       # People.country 也沒有被改變
    'china'
    

    從上面代碼中可以看到,在Python 中以對象.屬性名格式對類屬性進行賦值時,只有p1.country 的值被改變了,p2.countryPeople.country 的值都沒有被改變。

    實際上,這種情況下,類屬性的值並沒有被改變,而是對象p1 中多了一個country 實例屬性,此後,p1.country 訪問的是p1 的實例屬性,p1.country對屬性country的訪問屏蔽了類中的country屬性,而p2.countryPeople.country 訪問的依然是原來的類屬性

    所以,類名.屬性名對象.屬性名的格式都可以訪問類屬性的值,但盡量避免使用對象.屬性名的格式對類屬性的值進行賦值,否則可能會發生混亂。

    建議:

    不管是訪問還是改變類屬性的值,都只用類名.屬性名的格式

    2,實例方法,類方法,靜態方法

    Python 類中有三種方法:

    • 實例方法
    • 類方法
    • 靜態方法

    實例方法屬於對象,方法中都有一個self 參數(代表對象本身)。實例方法只能由對象調用,不能通過類名訪問。實例方法中可以訪問實例屬性和類屬性。

    類方法屬於類,方法中都有一個cls 參數(代表類本身)。類方法即可以通過類名訪問,也可以通過對象訪問,類方法中只能訪問類屬性,不能訪問實例屬性。

    注意:

    Python 解釋器在構造類與對象時,是先於對象產生的。

    因此,類屬性與類方法是先於實例屬性與實例方法 產生的。

    所以當類方法產生時,還沒有實例屬性,因此,類方法中不能訪問實例屬性。

    靜態方法中,沒有self 參數,也沒有cls 參數。因此,在靜態方法中,即不能訪問類屬性,也不能訪問實例屬性。靜態方法可以使用類名訪問,也可以使用對象訪問。

    在Python 中,定義類方法需要用到裝飾器@classmethod,定義靜態方法需要用到裝飾器@staticmethod

    實例方法演示

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    
        # 實例方法中有self 參數
        def instance_method_test(self):
            # 在實例方法中訪問了實例屬性和類屬性
            print('name:%s country:%s' % (self.__name, People.country))
    

    使用:

    >>> p = People('小明')
    >>> p.instance_method_test()
    name:小明 country:china
    

    在實例方法中訪問了實例屬性__name和類屬性country,均可以被訪問。

    類方法演示

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    
        # 類方法中都有cls 參數
        @classmethod
        def class_method_test1(cls):
            print('在類方法中訪問類屬性country:%s' % cls.country)
    
        @classmethod
        def class_method_test2(cls):
            print('在類方法中訪問實例屬性__name:%s' % self.__name)
    

    使用:

    >>> p = People('小明')
    >>> p.class_method_test1()         # 在類方法中訪問類屬性,可以
    在類方法中訪問類屬性country:china
    >>>
    >>> p.class_method_test2()         # 在類方法中訪問實例屬性,出現異常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/wp/to_beijing/People.py", line 18, in class_method_test2
        print('在類方法中訪問實例屬性__name:%s' % self.__name)
    NameError: name 'self' is not defined
    

    從上面代碼中可以看到,在類方法中可以訪問類屬性,但在類方法中訪問實例屬性,會出現異常。

    靜態方法演示

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    
        # 靜態方法中即沒有self 參數也不沒有cls 參數
        @staticmethod
        def static_method_test1():
            print('在靜態方法中訪問類屬性country:%s' % cls.country)
    
        @staticmethod
        def static_method_test2():
            print('在靜態方法中訪問實例屬性__name:%s' % self.__name)
    

    使用:

    >>> p = People('小明')
    >>> p.static_method_test1()      # 在靜態方法中訪問類屬性,出現異常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/wp/to_beijing/People.py", line 14, in static_method_test1
        print('在靜態方法中訪問類屬性country:%s' % cls.country)
    NameError: name 'cls' is not defined
    >>>
    >>> p.static_method_test2()     # 在靜態方法中訪問實例屬性,出現異常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/wp/to_beijing/People.py", line 18, in static_method_test2
        print('在靜態方法中訪問實例屬性__name:%s' % self.__name)
    NameError: name 'self' is not defined
    

    從上面代碼中可以看到,在靜態方法中無論訪問實例方法還是類方法,都會出現異常。

    3,專有方法

    我們之前講到過的魔法方法,即以雙下劃線__開頭且結尾的方法__xxx__,就是專有方法。這些方法都被Python 賦予了特殊的含義,用戶可以根據需要,來實現這些方法。

    下面我們介紹一些 Python 中常見的專有方法。

    __len__ 方法

    實現該方法的類的對象,可以用len() 函數計算其長度。

    __str__ 方法

    實現該方法的類的對象,可以轉化為字符串。

    __call__ 方法

    實現該方法的類的對象,可以像函數一樣調用。

    __iter__ 方法

    實現該方法的類的對象,是可迭代的。

    __setitem__ 方法

    實現該方法的類的對象,可以用索引的方式進行賦值。

    __getitem__ 方法

    實現該方法的類的對象,可以用索引的方式進行訪問。

    __contains__ 方法

    實現該方法的類的對象,可以進行in 運算。

    __add__ 方法

    實現該方法的類的對象,可以進行加+運算。

    __sub__ 方法

    實現該方法的類的對象,可以進行減-運算。

    __mul__ 方法

    實現該方法的類的對象,可以進行乘*運算。

    __div__ 方法

    實現該方法的類的對象,可以進行除/運算。

    __pow__ 方法

    實現該方法的類的對象,可以進行乘方運算。

    __mod__ 方法

    實現該方法的類的對象,可以進行取模運算。

    __eq__ 方法

    實現該方法的類的對象,可以進行相等==比較。

    __ne__ 方法

    實現該方法的類的對象,可以進行不等於!=比較。

    __gt__ 方法

    實現該方法的類的對象,可以進行大於>比較。

    __ge__ 方法

    實現該方法的類的對象,可以進行大於等於>=比較。

    __lt__ 方法

    實現該方法的類的對象,可以進行小於<比較。

    __le__ 方法

    實現該方法的類的對象,可以進行小於等於<=比較。

    (完。)

    推薦閱讀:

    Python 簡明教程 — 15,Python 函數

    Python 簡明教程 — 16,Python 高階函數

    Python 簡明教程 — 17,Python 模塊與包

    Python 簡明教程 — 18,Python 面向對象

    Python 簡明教程 — 19,Python 類與對象

    歡迎關注作者公眾號,獲取更多技術乾貨。

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

    【其他文章推薦】

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

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 基於flink和drools的實時日誌處理

    基於flink和drools的實時日誌處理

    1、背景

    日誌系統接入的日誌種類多、格式複雜多樣,主流的有以下幾種日誌:

    • filebeat採集到的文本日誌,格式多樣
    • winbeat採集到的操作系統日誌
    • 設備上報到logstash的syslog日誌
    • 接入到kafka的業務日誌

    以上通過各種渠道接入的日誌,存在2個主要的問題:

    • 格式不統一、不規範、標準化不夠
    • 如何從各類日誌中提取出用戶關心的指標,挖掘更多的業務價值

    為了解決上面2個問題,我們基於flink和drools規則引擎做了實時的日誌處理服務。

    2、系統架構

    架構比較簡單,架構圖如下:

     

    各類日誌都是通過kafka匯總,做日誌中轉。

    flink消費kafka的數據,同時通過API調用拉取drools規則引擎,對日誌做解析處理后,將解析后的數據存儲到Elasticsearch中,用於日誌的搜索和分析等業務。

    為了監控日誌解析的實時狀態,flink會將日誌處理的統計數據,如每分鐘處理的日誌量,每種日誌從各個機器IP來的日誌量寫到Redis中,用於監控統計。

    3、模塊介紹

    系統項目命名為eagle。

    eagle-api:基於springboot,作為drools規則引擎的寫入和讀取API服務。

    eagle-common:通用類模塊。

    eagle-log:基於flink的日誌處理服務。

    重點講一下eagle-log:

    對接kafka、ES和Redis

    對接kafka和ES都比較簡單,用的官方的connector(flink-connector-kafka-0.10和flink-connector-elasticsearch6),詳見代碼。

    對接Redis,最開始用的是org.apache.bahir提供的redis connector,後來發現靈活度不夠,就使用了Jedis。

    在將統計數據寫入redis的時候,最開始用的keyby分組后緩存了分組數據,在sink中做統計處理后寫入,參考代碼如下:

            String name = "redis-agg-log";
            DataStream<Tuple2<String, List<LogEntry>>> keyedStream = dataSource.keyBy((KeySelector<LogEntry, String>) log -> log.getIndex())
                    .timeWindow(Time.seconds(windowTime)).trigger(new CountTriggerWithTimeout<>(windowCount, TimeCharacteristic.ProcessingTime))
                    .process(new ProcessWindowFunction<LogEntry, Tuple2<String, List<LogEntry>>, String, TimeWindow>() {
                        @Override
                        public void process(String s, Context context, Iterable<LogEntry> iterable, Collector<Tuple2<String, List<LogEntry>>> collector) {
                            ArrayList<LogEntry> logs = Lists.newArrayList(iterable);
                            if (logs.size() > 0) {
                                collector.collect(new Tuple2(s, logs));
                            }
                        }
                    }).setParallelism(redisSinkParallelism).name(name).uid(name);

    後來發現這樣做對內存消耗比較大,其實不需要緩存整個分組的原始數據,只需要一個統計數據就OK了,優化后:

            String name = "redis-agg-log";
            DataStream<LogStatWindowResult> keyedStream = dataSource.keyBy((KeySelector<LogEntry, String>) log -> log.getIndex())
                    .timeWindow(Time.seconds(windowTime))
                    .trigger(new CountTriggerWithTimeout<>(windowCount, TimeCharacteristic.ProcessingTime))
                    .aggregate(new LogStatAggregateFunction(), new LogStatWindowFunction())
                    .setParallelism(redisSinkParallelism).name(name).uid(name);

    這裏使用了flink的聚合函數和Accumulator,通過flink的agg操作做統計,減輕了內存消耗的壓力。

    使用broadcast廣播drools規則引擎

    1、drools規則流通過broadcast map state廣播出去。

    2、kafka的數據流connect規則流處理日誌。

    //廣播規則流
    env.addSource(new RuleSourceFunction(ruleUrl)).name(ruleName).uid(ruleName).setParallelism(1)
                    .broadcast(ruleStateDescriptor);
    
    //kafka數據流
    FlinkKafkaConsumer010<LogEntry> source = new FlinkKafkaConsumer010<>(kafkaTopic, new LogSchema(), properties);
    env.addSource(source).name(kafkaTopic).uid(kafkaTopic).setParallelism(kafkaParallelism);
    //數據流connect規則流處理日誌 BroadcastConnectedStream<LogEntry, RuleBase> connectedStreams = dataSource.connect(ruleSource); connectedStreams.process(new LogProcessFunction(ruleStateDescriptor, ruleBase)).setParallelism(processParallelism).name(name).uid(name);

    具體細節參考開源代碼。

    4、小結

    本系統提供了一個基於flink的實時數據處理參考,對接了kafka、redis和elasticsearch,通過可配置的drools規則引擎,將數據處理邏輯配置化和動態化。

    對於處理后的數據,也可以對接到其他sink,為其他各類業務平台提供數據的解析、清洗和標準化服務。

     

    項目地址:

    https://github.com/luxiaoxun/eagle

     

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

    【其他文章推薦】

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

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

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

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

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