標籤: 台北網頁設計

  • 三次握手四次揮手

    三次握手四次揮手

    一直都知道 TCP 建立連接時需要三次握手,釋放連接時需要四次揮手,也大概能說出整個過程,但是一直對其中的設計思想理解不深,停留在“只可意會,不可言傳”的階段。這次寫一篇博客嘗試將其中的思想表達出來。

     

     

     

    TCP 建連三次握手

    首先解釋一下每個步驟的作用:
    1、a 時刻,A 準備就緒,發送 SYN 包給 B,嘗試建立連接
    2、b 時刻,B 收到 A 發來的 SYN 包,知道 A 要請求建連,回 SYN ACK 包,告訴 A 自己收到了建連請求,可以建連了
    3、c 時刻,A 收到了 B 的回復,知道 B 準備好了,鏈路通暢,可以發送數據了。回  ACK 告知 B 收到了 B 的回復,下面要開始發送該數據了
    4、d 時刻,B 收到了 A 的回復,知道 A 接下來要發數據了。至此,AB 雙方都確認整個鏈路已經可靠了,接下來可以發送數據了。

     

    為什麼要多次確認呢?為什麼不可以 A 上來就直接發送數據給 B 呢?
    這裏首先要明確一點,TCP 是傳輸層的協議,是建立在物理層、數據鏈路層、網絡層之上的協議,而底層的網絡是不可靠的,可能路由出問題,可能網關出問題,可能網線出問題,A 沒法保證自己發出來的消息 B 一定能收到,所以一定要反饋機制,即 ACK,這樣才能在不可靠的網絡層智商構建可靠的傳輸層。

     

    類比一下生活中的例子,可以幫助我們理解
    示例1,假設我們在火車上打電話,通話質量很差,我們的通話過程可能會是下面這樣:

     

     

    AB 雙方首先需要確認彼此都能挺到對方的聲音,也就是保證電話通道是可靠的,之後才會開始說正事。如果一上來就直接說正事,可能 A 說完之後 B 根本就沒有聽到。
    實際打電話過程中,如果遇到了斷線的情況,雙方可能需要進行多次“握手”確認。

     

    示例2,假設我們給剛認識的人第一次打電話,通話過程可能是下面這樣:

     

     

    AB 雙方都要確認對方的身份,也就是保證通話是在跟自己人進行,確保電話通道是可靠的,不是跟騙子通話,然後才會開始說正事。如果一上來沒有確認身份,不能保證通道是跟自己人進行的,那直接說出重要的事,很可能就泄漏了機密。

     

    總之,握手過程的最終目的就是保證雙方都準備就緒,通路是可靠的,之後就可以放心的發送重要數據了。

     

    那為什麼一定是三次呢,為什麼不是兩次或者四次呢?
    先來說一下為什麼不能少。
    一次可以嗎?不可以。設想一下,A 對 B 說:我要給你發數據。然後不等 B 的回復,接下來就開始發數據了。這時候根本不能保證 B 已經準備好了,那 A 發出來的數據就沒法保證 B 一定能收到。聯想生活中的場景,你隔着很遠的距離向對方喊話:我要把蘋果扔給你。然後不關心對方有沒有聽到,就直接扔了,那最終的結果通常就是對方接不到蘋果,因為對方可能根本沒有收到消息。
    兩次可以嗎?不可以。設想一下,A 對 B 說:我要給你發數據,然後 B 收到消息后給 A 回復:收到,A 在收到 B 的回復后開始發送數據。這時候 A 端是可以準備就緒的,但是 B 端不知道 A 端當前的狀態。因為 B 在收到 A 的消息的時候,可能已經過去了很長時間,B 在回消息的時候,A 可能已經不在線了,此時 B 是不能直接發數據的。如果 A 再給 B 回一個 ACK,B 就可以確認當前鏈路狀態了,這就變成了三次握手。

    接下來說一下為什麼不是四次。既然三次已經可以保證建立可靠通信,就不需要額外的一次交互了。

     

    下面是幾個生活中相關的示例:

     

     

     

     

     

     

     

     

     

     

     TCP 斷鏈四次揮手
    1、a 時刻,A 向 B 發出 FIN 包,表示自己沒有數據要發送了
    2、b 時刻,B 收到 FIN 包,回復 FIN ACK,表示收到了 A 的 FIN 包,不會再接收 A 的數據了
    3、B 在發完 FIN ACK 后,可能還有數據要發給 A,這個數據是不能停止發送的,有數據還是需要繼續發送
    4、d 時刻,B 發完了數據,也發出 FIN 包,告訴 A 自己的數據發完了,不再發送數據了
    5、e 時刻,A 收到了 B 的 FIN 包,知道 B 也沒有數據要發送了,回復 FIN ACK。此時,連接可以斷開了

    建連只需要交互三次,斷連卻需要四次,這是為什麼呢?其實斷開連接和建立連接還是不一樣的。建連的時候,只要雙方都告知對方自己準備好了就可以,但是斷連的時候,一方提出要斷開連接,不再發數據,另一方不能立即斷開,因為這一方可能還有數據要發送,直到數據全部發送完成后才能確認斷開。

     

    下面是幾個生活中相關的示例:

     

     

     

    以上是對於三次握手、四次揮手的簡單介紹,裏面沒有更詳細的狀態介紹,之後的博客會介紹,這裏先放兩張圖。
    TCP 三次握手

     

     

     

    TCP 四次揮手
     

     

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

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

  • 【Spring】內嵌Tomcat&去Xml&調試Mvc

    菜瓜:今天聽到個名詞“父子容器”,百度了一下,感覺概念有點空洞,這是什麼核武器?

    水稻:你說的是SpringMvc和Spring吧,其實只是一個概念而已,用來將兩個容器做隔離,起到解耦的作用,其中子容器可以拿到父容器的bean,父容器拿不到子容器的。但是SpringBoot出來之後這個概念基本就被淡化掉,沒有太大意義,SpringBoot中只有一個容器了。

    菜瓜:能不能給個demo?

    水稻:可以。由於現在SpringBoot已經大行其道,Mvc你可能接觸的少,甚至沒接觸過。

    • 早些年啟動一個Mvc項目費老鼻子勁了,要配置各種Xml文件(Web.xml,spring.xml,spring-dispather.xml),然後開發完的項目要打成War包發到Tomcat容器中
    • 現在可以直接引入Tomcat包,用main方法直接調起。為了調試方便,我就演示一個Pom引入Tomcat的例子
    • ①啟動類
    • package com.vip.qc.mvc;
      
      import org.apache.catalina.Context;
      import org.apache.catalina.LifecycleException;
      import org.apache.catalina.LifecycleListener;
      import org.apache.catalina.startup.Tomcat;
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.FilterType;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
      
      /**
       * 參考: * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
       * <p>
       * 嵌入tomcat,由Tomcat發起對Spring容器的初始化調用過程
       * <p>
       * - 啟動過程
       * * - Servlet規範,Servlet容器在啟動之後會SPI加載META-INF/services目錄下的實現類並調用其onStartup方法
       * * - Spring遵循規範實現了ServletContainerInitializer接口。該接口在執行時會收集WebApplicationInitializer接口實現類並循環調用其onStartup方法
       * * - 其中AbstractDispatcherServletInitializer
       * * * - 將spring上下文放入ContextLoaderListener監聽器,該監聽會發起對refresh方法的調用
       * * * - 註冊dispatcherServlet,後續會由tomcat調用HttpServletBean的init方法,完成子容器的refresh調用
       * *
       *
       * @author QuCheng on 2020/6/28.
       */
      public class SpringWebStart {
      
          public static void main(String[] args) {
              Tomcat tomcat = new Tomcat();
              try {
                  // 此處需要取一個目錄
                  Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
                  context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
                  tomcat.setPort(8081);
                  tomcat.start();
                  tomcat.getServer().await();
              } catch (LifecycleException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                  e.printStackTrace();
              }
          }
      
      
          static class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
      
              private final static String PACKAGE_PATH = "com.vip.qc.mvc";
      
              @Override
              protected String[] getServletMappings() {
                  return new String[]{"/"};
              }
      
              @Override
              protected Class<?>[] getRootConfigClasses() {
                  // spring 父容器
                  return new Class[]{AppConfig.class};
              }
      
              @Override
              protected Class<?>[] getServletConfigClasses() {
                  // servlet 子容器
                  return new Class[]{ServletConfig.class};
              }
      
              @ComponentScan(value = PACKAGE_PATH,
                      excludeFilters = {
                              @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
                              // 避免掃描到加了註解(@Configuration)的子容器
                              @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ServletConfig.class)})
              static class AppConfig {
              }
      
              @ComponentScan(value = PACKAGE_PATH)
              static class ServletConfig {
              }
          }
      }
    • ②Controller&Service
    • package com.vip.qc.mvc.controller;
      
      import com.vip.qc.mvc.service.ServiceChild;
      import org.springframework.beans.BeansException;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.ApplicationContextAware;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.ResponseBody;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/28.
       */
      @Controller
      public class ControllerT implements ApplicationContextAware {
      
          @Resource
          private ServiceChild child;
      
          @RequestMapping("/hello")
          @ResponseBody
          public String containter() {
              child.getParent();
              System.out.println("parentContainer");
              return "containter";
          }
      
          @Override
          public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
              System.out.println("子容器" + applicationContext);
              System.out.println("子容器中獲取父容器bean" + applicationContext.getBean(ServiceChild.class));
          }
      }
      
      
      package com.vip.qc.mvc.service;
      
      import com.vip.qc.mvc.controller.ControllerT;
      import org.springframework.beans.BeansException;
      import org.springframework.beans.factory.NoSuchBeanDefinitionException;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.ApplicationContextAware;
      import org.springframework.stereotype.Service;
      
      /**
       * @author QuCheng on 2020/6/28.
       */
      @Service
      public class ServiceChild implements ApplicationContextAware {
      
          //    @Resource
          private ControllerT controllerT;
      
          public void getParent() {
      
              System.out.println(controllerT);
          }
      
          @Override
          public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
              System.out.println("父容器" + applicationContext);
              try {
                  System.out.println("父容器中獲取子容器bean" + applicationContext.getBean(ControllerT.class));
              } catch (NoSuchBeanDefinitionException e) {
                  System.out.println("找不到子容器的bean");
              }
          }
      }

      // 調用
      SpringWebStart的main方法啟動-會有如下打印
      父容器Root WebApplicationContext, started on Sun Jun 28 22:03:52 CST 2020
      找不到子容器的bean
      子容器WebApplicationContext for namespace 'dispatcher-servlet', started on Sun Jun 28 22:03:58 CST 2020, parent: Root WebApplicationContext
      子容器中獲取父容器beancom.vip.qc.mvc.service.ServiceChild@4acfc43a
      
      
    • Demo比較簡單,不過也能反映父子容器的關係

    菜瓜:嗯,效果看到了,能不能講一下啟動過程

    水稻:稍等,我去下載源碼。上面代碼演示中已經提前說明了,父子容器的加載是Tomcat依據Servlet規範發起調用完成的

    • spring-web源碼包的/META-INF中能找到SPI的實際加載類SpringServletContainerInitializer#onStartup()方法會搜集實現WebApplicationInitializer接口的類,並調用其onStartup方法
    • 上面MyWebApplicationInitializer啟動類是WebApplicationInitializer的子類,未實現onStartup,實際調用的是其抽象父類AbstractDispatcherServletInitializer的方法。跟進去 
    • @Override
      public void onStartup(ServletContext servletContext) throws ServletException {
         //① 創建Spring父容器上下文-對象放入ContextLoadListener,後續調起完成初始化,
         super.onStartup(servletContext);
         //② 創建DispatcherServlet對象,後續會由tomcat調用其init方法,完成子容器的初始化工作
         registerDispatcherServlet(servletContext);
      }
      
      // ①進來
      protected void registerContextLoaderListener(ServletContext servletContext) {
          // 此處會回調我們啟動類的getRootConfigClasses()方法 - 父容器配置
          WebApplicationContext rootAppContext = createRootApplicationContext();
          if (rootAppContext != null) {
              ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
              istener.setContextInitializers(getRootApplicationContextInitializers());
              servletContext.addListener(listener);
          }
          else {
              logger.debug("No ContextLoaderListener registered, as " +
                    "createRootApplicationContext() did not return an application context");
          }
      }
      
      // ②進來
      protected void registerDispatcherServlet(ServletContext servletContext) {
              。。。
          // 此處會回調我們啟動類的getServletConfigClasses()方法 - 子容器配置
          WebApplicationContext servletAppContext = createServletApplicationContext();
              。。。
          // 初始化的dispatcherServlet,會加入Tomcat容器中-後續調用
          // FrameworkServlet#initServletBean()會完成上下文初始化工作
          FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
              。。。
      }

    菜瓜:這樣容器就可以用了嗎?

    水稻:是的,這樣就可以直接在瀏覽器上面訪問http://localhost:8081/hello,不過這是一個最簡陋的web項目

    菜瓜:懂了,最簡陋是什麼意思

    水稻:如果我們想加一些常見的Web功能,譬如說攔截器,過濾器啥的。可以通過@EnableWebMvc註解自定義一些功能

    • package com.vip.qc.mvc;
      
      import com.vip.qc.mvc.interceptor.MyInterceptor1;
      import com.vip.qc.mvc.interceptor.MyInterceptor2;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.EnableWebMvc;
      import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/28.
       */
      @Configuration
      @EnableWebMvc
      public class WebMvcConfig implements WebMvcConfigurer {
      
          @Resource
          private MyInterceptor1 interceptor1;
          @Resource
          private MyInterceptor2 interceptor2;
      
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(interceptor1).addPathPatterns("/interceptor/**");
              registry.addInterceptor(interceptor2).addPathPatterns("/interceptor/**");
          }
      }
      
      
      
      package com.vip.qc.mvc.interceptor;
      
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.HandlerInterceptor;
      import org.springframework.web.servlet.ModelAndView;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      /**
       * @author QuCheng on 2020/6/28.
       */
      @Configuration
      public class MyInterceptor1 implements HandlerInterceptor {
      
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("嘻嘻 我是攔截器1 pre");
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("嘻嘻 我是攔截器1 post");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("嘻嘻 我是攔截器1 after");
          }
      }
      
      
      package com.vip.qc.mvc.interceptor;
      
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.HandlerInterceptor;
      import org.springframework.web.servlet.ModelAndView;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      /**
       * @author QuCheng on 2020/6/28.
       */
      @Configuration
      public class MyInterceptor2 implements HandlerInterceptor {
      
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("嘻嘻 我是攔截器2 pre");
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("嘻嘻 我是攔截器2 post");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("嘻嘻 我是攔截器2 after");
          }
      
      } 

    菜瓜:我知道,這裏還有個Mvc請求調用流程和這個攔截器有關。而且這個攔截器不是MethodInterceptor(切面)

    水稻:沒錯,說到這裏順便複習一下Mvc的請求過程

    • 請求最開始都是通過Tomcat容器轉發過來的,調用鏈:HttpServlet#service() -> FrameworkServlet#processRequest() -> DispatcherServlet#doDispather()
    •  1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       2     。。。
       3   processedRequest = checkMultipart(request);
       4   multipartRequestParsed = (processedRequest != request);
       5   // 1.返回一個持有methodHandler(按照URL匹配得出的被調用bean對象以及目標方法)調用鏈(攔截器鏈)對象
       6   mappedHandler = getHandler(processedRequest);
       7   。。。
       8   // 2.按照我們現在寫代碼的方式,只會用到HandlerMethod,其他三種基本不會用
       9   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
      10   。。。
      11   // 3.前置過濾器 - 順序調用
      12   if (!mappedHandler.applyPreHandle(processedRequest, response)) {
      13       return;
      14   }
      15   // 4.Actually invoke the handler.
      16   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      17   。。。
      18     applyDefaultViewName(processedRequest, mv);
      19   // 5.後置過濾器 - 逆序調用
      20   mappedHandler.applyPostHandle(processedRequest, response, mv);
      21   。。。
      22   // 6.處理試圖 - 內部render
      23   processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
      24   }
      25   catch (Exception ex) {
      26      // 異常處理
      27      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
      28   }
      29       // 異常處理
      30   catch (Throwable err) {
      31      triggerAfterCompletion(processedRequest, response, mappedHandler,
      32                     new NestedServletException("Handler processing failed", err));
      33   }
      34     。。。

       

     菜瓜:這個之前看過不少,百度一大堆,不過還是源碼親切

     

    總結:

    • 目前基本互聯網項目都是SpringBoot起手了,再難遇到SpringMvc的項目,不過熟悉該流程有利於我們更加深刻的理解Ioc容器
    • Mvc攔截器鏈也是日常開發中會用到的功能,順便熟悉一下請求的執行過程

     

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

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

  • 老當益壯顯身手——記安徽省黃山市市場監管局綜合行政執法支隊恭弘=叶 恭弘建德

      “12315嗎?我要投訴,屯溪區益康堂大藥房賣的飄安口罩生產日期有問題。”1月24日大年三十晚上,安徽省黃山市市場監管局接到了一個投訴。

      領到任務的是該局綜合行政執法支隊主要負責人恭弘=叶 恭弘建德。今年58歲的恭弘=叶 恭弘建德是一名經驗豐富的市場監管老兵,深知執法辦案行動迅速的重要性。1月25日8:00,他和辦案人員便出現在藥房門口。

      9:00,藥房一開門,辦案人員便現場檢查,查獲標註生產日期為2020年2月2日、2月6日的“飄安”口罩各5包。

      恭弘=叶 恭弘建德說,稍遲一步,這10包“早產”口罩就可能被買走。“沒有實物,就無法繼續辦案。”他說。

      1月27日,在接到飄安集團出具的“這批口罩是假冒產品”的鑒別意見后,他又果斷決定啟動行刑銜接機制,及時調整辦案思路,移交公安機關先行偵辦。

      在查處另一起假“民樂”口罩案時,恭弘=叶 恭弘建德與執法人員第一時間趕到被舉報的藥房,卻沒查到實物。他們當機立斷,火速趕到批發這批口罩的黃山博宏醫藥銷售公司,查獲一個空外包裝紙箱和某醫院退回的10包口罩。“稍微一猶豫,可能就會空手而回。” 恭弘=叶 恭弘建德說。

      同事們都說,敢於克難是恭弘=叶 恭弘建德辦案的一大特點。查辦假“民樂”口罩案時,消費者質疑口罩質量有問題,但銷售方出具了一家正規醫藥銷售公司的銷售單據、質檢報告。恭弘=叶 恭弘建德和辦案人員並未被表面證據迷惑,立即聯繫生產廠家新鄉華康衛材有限公司。

      “肯定是假冒產品。”廠家負責人通過對比上傳的產品及合格證圖片后,出具了鑒別意見,成為案件定性的重要證據。

      “我們這裏藥房賣的飄安口罩可能也是假的。”益康堂大藥房銷售假“飄安”口罩被查處后,市場監管局也接到了其他消費者舉報。

      群眾的呼聲就是行動的號角。恭弘=叶 恭弘建德與辦案人員齊心協力,有訴必查、有查必果。之後,他們又查處了5起藥房銷售假“飄安”口罩案。

      由於率先查處了假“飄安”口罩案和案值18.9萬元的假“民樂”口罩案,不少媒體紛紛報道,恭弘=叶 恭弘建德成了“名人”。北京密雲、貴州六盤水、山東德州及本省其他地區市場監管部門紛紛向他請教,每次他都毫無保留,及時與同行分享辦案經驗。

      由於假“飄安”、假“民樂”口罩數量達20多萬隻,恭弘=叶 恭弘建德和辦案人員及時約談藥房經營者,要求其無條件全額退款。經過多日努力,購買假“飄安”口罩消費者的全部退款訴求均已解決。(鞠萱

    責任編輯:邊靜

    本站聲明:網站內容來源再生能源資訊網http://www.ccn.com.cn/,如有侵權請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

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

  • 江蘇省消保委多措並舉 助力消費者提升免“疫”力

      中國消費者報報道史曄 記者薛慶元)眾志成城,共克時艱。一場來勢洶洶的新冠肺炎疫情牽動着每個消費者的心。面對嚴峻的疫情,江蘇省消費者權益保護委員會堅守“疫”線,及時開展消費宣傳引導,努力化解消費糾紛,督促經營者履行責任,儘力護衛消費者權益,用心守護消費環境穩定和民生消費安全。

      受理消費投訴5548件

      疫情期間,口罩一夜之間成了“搶手貨”。據了解,1月24日至2月9日,江蘇全省消保委系統共計受理消費維權投訴5548件,投訴類型分析,商品類投訴3468件,服務類2080件,分別佔比62.51%、37.49%。受疫情影響,線上機票、酒店、旅遊行程退訂,線下口罩、消毒防護用品購買,餐飲退訂等成為消費熱點,網購產品發貨、售後也成關注點。其中,口罩、消毒液、手套等衛生防護用品需求和投訴激增,口罩類投訴1965件,佔總投訴量35.42%,主要問題為發貨延遲、價格上漲、假冒偽劣等方面。

      2月5日,泰興市消費者錢先生通過微信購買口罩50隻,后發現所購口罩無廠名、廠址、合格證,遂向當地消費者組織進行投訴維權。泰興市消費者協會接訴后,詳細向售賣人了解口罩進貨渠道及相關程序手續,售賣人均無法回答,為防止春節期間劣質口罩流入市場,工作人員現場調解售賣人為消費者辦理退款並進行賠償,同時將售賣人基本信息和售賣情況向執法部門書面發出“行政處罰建議函”。

      發出倡議共抗疫情

      面對疫情蔓延的嚴峻形勢和投訴激增的趨勢,江蘇省消保委堅決做到“有訴必接、速查速回”,全力維護消費者權益。針對涉嫌違法的藥店、生產銷售各類劣質口罩、哄抬物價等行為,江蘇省各市消保委組織紛紛發出相關倡議,呼籲經營者尊重市場規律前提下,自覺履行法定義務,助力消費者提升“免疫力”,最大限度保障消費者權益。

      非常時期,人民齊心抗疫。南京市消協系統自除夕起安排了專人值班,全市消協系統(含分會)共有276人在崗工作,全市消協系統受理投訴主要集中在防疫用品的質量和價格、物流問題、旅遊產品等方面;連雲港市消保委成立“疫情期間消費投訴工作應急小組”,執行涉疫投訴“日報告”制度,要求各縣區及時上報涉疫消費投訴動態及處理結果,建立旅遊、商超企業等微信工作群,指導企業依法依規积極快速處理好涉疫投訴,與有關部門密切配合,對農貿市場、藥店和商超進行排查,嚴控商品價格,嚴處哄抬物價…….

      阻擊、保障兩手抓

      奮力戰“疫”,江蘇省各級消保委組織保障民生實招頻出。南通市各級消協工作人員積极參与市場監管部門對轄區內農貿市場、活禽銷售點、大小型餐飲、藥店開的防疫檢查,避免群體聚集,堅決防止疫情蔓延;鎮江丹陽市通過發放《價格政策提醒函》、《禁止野生動物交易告知書》、《餐飲行業防控措施告知書》等,加強法規宣傳和政策引導,督促經營者自律; 無錫市消保委全體人員參与農貿市場巡查督導工作……

      今年年初,楊女士從境外旅遊回南京,回程前2天偶然得知其乘坐聯程航班兩段均被取消,其本人在航司官網、預定頁面均未發現訂單更改信息,也未收到任何通知及後續措施,撥打客服電話显示無法接通狀態,聯繫無果下投訴至消保委。當地消保委接訴后與航空公司聯繫,及時安排工作人員與消費者聯繫,為消費者更改航班,保障了消費者順利返程。

      此外,江蘇省消保委牢牢守住百姓“菜籃子”,揭穿各種謠言,真切做到抓好民生保障,又嚴格做好疫情防控,保障市場秩序穩定有序,築起疫情防控銅牆鐵壁。

      生命重於泰山。江蘇省消保委深刻認識做好疫情防控的重要性和緊迫性,圍繞群眾消費熱點,咬定戰“疫”不鬆勁,多措並舉解民憂、排民難,以對人民高度負責的態度,把疫情防控工作作為當前最重要的工作來抓,全力以赴打好打贏疫情防控這場阻擊戰。

    責任編輯:邊靜

    本站聲明:網站內容來源再生能源資訊網http://www.ccn.com.cn/,如有侵權請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

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

  • 智慧充電管理軟體真的有效,加州電動交通車隊省下 40% 電費

    智慧充電管理軟體真的有效,加州電動交通車隊省下 40% 電費

    隨著綠能推展,全球無數新創事業應運而生,宣稱智慧軟體能在能源多元化時代為顧客省錢,也包括電動車隊的智慧充電管理系統,這些系統真能達到宣稱的效益嗎?如今第一波實際使用的成果已逐漸顯現。新創事業 Amply Power 於 2020 年 4 月發表系統應用於北加州康特拉科斯塔郡 Tri Delta 交通車隊電動車的成效,自 2020 年初以來,為車隊節省了 40% 電費。

    隨著都市市區環保意識上升,以及各國推動減碳,加上電池降價使得電動車購置成本降低,越來越多公車與交通車隊改用電動車,目前全球有超過 200 家交通車隊採用電動車,並預計到 2025 年電動巴士將占全球交通車隊 30%。

    Tri Delta 早在 2018 年就已經開始加入潮流,當時只先測試 4 輛電動車,2 輛來自比亞迪、2 輛來自加州電動巴士廠 Proterra,即使只是先測試 4 輛,對車隊的後勤作業就造成相當大的改變。少了 4 輛車需要柴油與一般引擎車的維修,但多出與電力公司交涉、升級變電器、安裝高壓電力裝置等任務,僅 4 輛車就導致最高電力需求負載高達 300 千瓦,這下突然得處理很多過去不曾想過的電力節費問題。

    於是 2019 年底 Tri Delta 決定採用 Amply Power 的智慧充電管理軟體,由軟體管理充電時間,挑選電費較低的離峰時間充電,以節省電費,但又同時確保要用車的時候電力有充飽,車隊後勤人員不再需要思考何時插上充電座比較省錢,讓軟體決定就好了。此外,改採電動車之後,最大的困擾就是一旦充電沒有插好,等到隔天要用車,車子沒電才發現,就會造成調度嚴重問題,使用充電管理軟體後,充電若沒插好,軟體會發出警告,一勞永逸解決了這個防呆問題。

    充電管理結果能否擴大適用還待驗證

    使用充電管理軟體的期間,Tri Delta 的電力公司太平洋瓦電(PG&E)調整過尖峰用電時間表,Tri Delta 本身用電變化也讓所屬費率區間有變動,過去這些都會造成負責充電的員工傷透腦筋,每週的最佳充電時間都不同,一不小心就讓公司承受高額電費,這些變動更顯出充電管理軟體的重要性,充電管理軟體會自動依據電力公司的時間表與公式調整,算出最划算的充電時間。

    經過幾個月使用下來,Tri Delta 節省了高達 40% 電費支出,這還只是第一季,進入夏季後,尖峰用電費率落差更大,會讓充電管理的效益更明顯。

    不過,這僅是 4 輛電動車的充電管理結果,能否擴大適用到上百台規模的大車隊,尚需進一步驗證。不過越大的車隊,原理上來說,充電管理能達到的效益會更顯著,不僅節省電費更多,軟體最佳化分配車輛充電的時間,更可以讓硬體投資也跟著減少,例如設置較少充電座,或讓電力設備不需太大規模升級,節省建設成本與工程時間。

    Amply Power 此實證案例,不僅證明充電管理的重要性,為許多管理軟體新創事業開創市場,另一方面,也同時為整個電動車產業打了一劑定心針:電動巴士車隊雖然成本較高,但可仰賴電費比燃料費用便宜來達成經濟效益,如今這商業模式面臨挑戰,因油價在全球新冠病毒疫情影響下降到低點,電動車的費用優勢可能消失,但是,若充電管理能省下 40% 電費,那麼,電動車隊尚可取回一部分競爭優勢。

    (合作媒體:。首圖來源:)

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

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

  • 急轉彎!特斯拉取消提前復工,股價由漲轉跌

    急轉彎!特斯拉取消提前復工,股價由漲轉跌

    先前消息傳出,電動車大廠特斯拉(Tesla)要求員工於 29 日返回工作崗位,以便讓加州佛利蒙(Fremont)組裝廠恢復生產。但隨著該廠所在的舊金山灣區六郡宣佈延長居家禁令,特斯拉政策急轉彎,已通知員工無需提前復工。

    CNBC 報導,根據特斯拉的內部信件,24 日和週末,特斯拉管理層要求數十名員工於 29 日重返工作崗位,讓佛利蒙廠產線重啟運作。不過,由於舊金山灣區六郡(舊金山、聖克拉拉、聖馬刁、馬林、康特拉哥斯,以及佛利蒙廠所在的阿拉米達郡)27 日宣佈,將「就地避難」(shelter-in-place)令的期限由 5 月 4 日延長至 5 月底,特斯拉隨後也取消本週的復工計畫。

    特斯拉人力資源部週一發送內部訊息向員工表示:「按照執行領導團隊的指示,我們將不會在 4 月 29 日星期三恢復工作。請忽略關於本週復工的所有訊息和指示。」

    報導指出,特斯拉佛利蒙廠生產 Model S 和 Model X 車型,以及較新的 Model 3 和 Model Y 車型,銷往北美和歐洲市場。位於中國上海的組裝廠,先前也因疫情短暫關閉約兩週,但在當地政府的協助下迅速恢復運作,目前每週營運 6 天。

    在佛利蒙廠有望提前復工的利多消息帶動下,27 日特斯拉股價大漲 10.15% 收 798.75 美元,但取消復工的消息公佈後,盤後股價挫跌 2.10% 至 782.00 美元。今年以來,特斯拉股價累計飆漲 90.98%。

    《華爾街日報》先前報導,根據特斯拉 4 月 2 日資料,2020 年第一季交車數達 88,400 輛,較去年同期成長 40%,雖然疫情導致整體車市市況不振,但特斯拉仍維持原先銷售目標。

    今年初,特斯拉執行長馬斯克(Elon Musk)預估,特斯拉今年電動車銷量將維持強勁成長,比去年至少成長 36%,全球總交車數可望「輕鬆突破」(comfortably exceed)50 萬輛。

    特斯拉預計 29 日發表 2020 年第一季財報。

    (本文內文由  授權使用;首圖來源: CC BY 2.0)

    延伸閱讀:

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

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

  • pythonic context manager知多少

    Context Managers 是我最喜歡的 python feature 之一,在恰當的時機使用 context manager 使代碼更加簡潔、清晰,更加安全,復用性更好,更加 pythonic。本文簡單介紹一下其使用方法以及常見使用場景。

    本文地址:https://www.cnblogs.com/xybaby/p/13202496.html

    with statement and context manager

    Python’s with statement supports the concept of a runtime context defined by a context manager

    new statement “with” to the Python language to make it possible to factor out standard uses of try/finally statements.

    在 pep0343 中,通過引入 context manager protocol 來支持 With statement , context manager 是用來管理 context(上下文)的,即保證程序要保持一種特定的狀態 — 無論是否發生異常。可以說,context manager 簡化了對 try-finally 的使用,而且更加安全,更加便於使用。

    Transforming Code into Beautiful, Idiomatic Python 中,指出了 context manager 的最顯著的優點:

    • Helps separate business logic from administrative logic
    • Clean, beautiful tools for factoring code and improving code reuse

    最廣為人知的例子,就是通過 with statement 來讀寫文件,代碼如下:

    with open('test.txt') as f:
        contect = f.read()
        handle_content(content)
    

    上面的代碼幾乎等價於

    f = open('test.txt') 
    try:
        contect = f.read()
        handle_content(content)
    finally:
        f.close()
    

    注意,上面的finally的作用就是保證file.close一定會被調用,也就是資源一定會釋放。不過,很多時候,都會忘了去寫這個finally,而 with statement 就徹底避免了這個問題。

    從上述兩段代碼也可以看出,with statement 更加簡潔,而且將核心的業務邏輯(從文件中讀取、處理數據)與其他邏輯(打開、關係文件)相分離,可讀性更強。

    實現context manager protocol

    一個類只要定義了__enter____exit__方法就實現了context manager 協議

    object.__enter__(self)
    Enter the runtime context related to this object. The with statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.
    
    object.__exit__(self, exc_type, exc_value, traceback)
    Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None.
    
    If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.
    
    Note that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.
    

    __enter__方法在進入這個 context 的時候調用,返回值賦值給 with as X 中的 X

    __exit__方法在退出 context 的時候調用,如果沒有異常,后三個參數為 None。如果返回值為 True,則Suppress Exception,所以除非特殊情況都應返回 False。另外注意, __exit__方法本身不應該拋出異常。

    例子:BlockGuard

    在看c++代碼(如mongodb源碼)的時候,經常看見其用 RAII 實現BlockGuard, 用以保證在離開 Block 的時候執行某些動作,同時,也提供手段來取消執行。

    下面用python實現一下:

    class BlockGuard(object):
    	def __init__(self, fn, *args, **kwargs):
    		self._fn = fn
    		self._args = args
    		self._kwargs = kwargs
    		self._canceled = False
    
    	def __enter__(self):
    		return self
    
    	def __exit__(self, exc_type, exc_value, traceback):
    		if not self._canceled:
    			self._fn(*self._args, **self._kwargs)
    		self._fn = None
    		self._args = None
    		self._kwargs = None
    		return False
    
    	def cancel(self):
    		self._canceled = True
    
    
    def foo():
    	print 'sth should be called'
    
    
    def test_BlockGuard(cancel_guard):
    	print 'test_BlockGuard'
    	with BlockGuard(foo) as guard:
    		if cancel_guard:
    			guard.cancel()
    	print 'test_BlockGuard  finish'
    

    用yield實現context manager

    標準庫 contextlib 中提供了一些方法,能夠簡化我們使用 context manager,如 contextlib.contextmanager(func) 使我們
    無需再去實現一個包含__enter__ __exit__方法的類。

    The function being decorated must return a generator-iterator when called. This iterator must yield exactly one value, which will be bound to the targets in the with statement’s as clause, if any.

    例子如下:

    from contextlib import contextmanager
    
    @contextmanager
    def managed_resource(*args, **kwds):
        # Code to acquire resource, e.g.:
        resource = acquire_resource(*args, **kwds)
        try:
            yield resource
        finally:
            # Code to release resource, e.g.:
            release_resource(resource)
    
    >>> with managed_resource(timeout=3600) as resource:
    ...     # Resource is released at the end of this block,
    ...     # even if code in the block raises an exception
    

    需要注意的是:

    • 一定要寫 try finally,才能保證release_resource邏輯一定被調用
    • 除非特殊情況,不再 catch exception,這就跟 __exit__ 一般不返回True一樣

    例子: no_throw

    這是業務開發中的一個需求, 比如觀察者模式,不希望因為其中一個觀察者出了 trace 就影響後續的觀察者,就可以這樣做:

    from contextlib import contextmanager
    
    @contextmanager
    def no_throw(*exceptions):
    	try:
    		yield
    	except exceptions:
    		pass
    
    def notify_observers(seq):
    	for fn in [sum, len, max, min]:
    		with no_throw(Exception):
    			print "%s result %s" % (fn.__name__, fn(seq))
    
    if __name__ == '__main__':
    	notify_observers([])
    

    在python 3.x 的 contexlib 中,就提供了一個contextlib.suppress(*exceptions), 實現了同樣的效果。

    context manager 應用場景

    context manager 誕生的初衷就在於簡化 try-finally,因此就適合應用於在需要 finally 的地方,也就是需要清理的地方,比如

    • 保證資源的安全釋放,如 file、lock、semaphore、network connection 等
    • 臨時操作的復原,如果一段邏輯有 setup、prepare,那麼就會對應 cleanup、teardown。

    對於第一種情況,網絡連接釋放的例子,後面會結合 pymongo 的代碼展示。

    在這裏先來看看第二種用途:保證代碼在一個臨時的、特殊的上下文(context)中執行,且在執行結束之後恢復到之前的上下文環境。

    改變工作目錄

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        pass
    

    臨時文件、文件夾

    很多時候會產生一堆臨時文件,比如build的中間狀態,這些臨時文件都需要在結束之後清除。

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        pass
    

    重定向標準輸出、標準錯誤

    @contextmanager
    def redirect_stdout(fileobj):
        oldstdout = sys.stdout
        sys.stdout = fileobj
        try:
            yield fieldobj
        finally:
            sys.stdout = oldstdout
    

    在 python3.x 中,已經提供了 contextlib.redirect_stdout contextlib.redirect_stderr 實現上述功能

    調整logging level

    這個在查問題的適合非常有用,一般生產環境不會輸出 debug level 的日誌,但如果出了問題,可以臨時對某些制定的函數調用輸出debug 日誌

    from contextlib import contextmanager
    import logging
    
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    
    ch = logging.StreamHandler()
    ch.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
    logger.addHandler(ch)
    
    
    @contextmanager
    def change_log_level(level):
    	old_level = logger.getEffectiveLevel()
    	try:
    		logger.setLevel(level)
    		yield
    	finally:
    		logger.setLevel(old_level)
    
    
    def test_logging():
    	logger.debug("this is a debug message")
    	logger.info("this is a info message")
    	logger.warn("this is a warning message")
    
    with change_log_level(logging.DEBUG):
    	test_logging()
    

    pymongo中的context manager使用

    在 pymongo 中,封裝了好幾個 context manager,用以

    • 管理 semaphore
    • 管理 connection
    • 資源清理

    而且,在 pymongo 中,給出了嵌套使用 context manager 的好例子,用來保證 socket 在使用完之後一定返回連接池(pool)。

    # server.py
    @contextlib.contextmanager
    def get_socket(self, all_credentials, checkout=False):
        with self.pool.get_socket(all_credentials, checkout) as sock_info:
            yield sock_info
            
    # pool.py
    @contextlib.contextmanager
    def get_socket(self, all_credentials, checkout=False):
        sock_info = self._get_socket_no_auth()
        try:
            sock_info.check_auth(all_credentials)
            yield sock_info
        except:
            # Exception in caller. Decrement semaphore.
            self.return_socket(sock_info)
            raise
        else:
            if not checkout:
                self.return_socket(sock_info)
    

    可以看到,server.get_socket 調用了 pool.get_socket, 使用 server.get_socket 的代碼完全不了解、也完全不用關心 socket 的釋放細節,如果把 try-except-finally-else 的邏輯移到所有使用socket的地方,代碼就會很醜、很臃腫。

    比如,在mongo_client 中需要使用到 socket:

    with server.get_socket(all_credentials) as sock_info:
        sock_info.authenticate(credentials)
    

    references

    With statement

    Context Managers

    contextlib

    what-is-the-python-with-statement-designed-for

    Transforming Code into Beautiful, Idiomatic Python

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

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

  • 上周熱點回顧(6.22-6.28)

    熱點隨筆:

    · 程序員敲代碼時耳機里聽的到底是什麼? (風的姿態)
    · CPU明明8個核,網卡為啥拚命折騰一號核? (軒轅之風)
    · 手把手教你基於SqlSugar4編寫一個可視化代碼生成器(生成實體,以SqlServer為例,文末附源碼) (熊澤-學習中的苦與樂)
    · 在運行時生成C# .NET類 (芝麻麻雀)
    · 因為我的一個低級錯誤,生產數據庫崩潰了將近半個小時 (鄙人薛某)
    · C# 人臉識別庫 (View12138)
    · 基於領域驅動設計(DDD)超輕量級快速開發架構 (阿新)
    · .Net Core 中GC的工作原理 (她微笑的臉)
    · 關於技術文章“標題黨”一事我想說兩句 (精緻碼農)
    · 【故障公告】阿里雲 RDS 實例 CPU 100% 故障引發全站無法正常訪問 (博客園團隊)
    · 思考:如何保證服務穩定性? (老_張)
    · 只看到了別人28歲從字節跳動退休,背後的期權知識你知道嗎? (四猿外)

    熱點新聞:

    · 瘋王,任正非!
    · VSCode彩虹屁插件:釘宮理惠,英雄聯盟版現已生成,你Pick哪一個?
    · 全國首創!廣東人坐火車就像坐地鐵一樣方便了:無需提前買票
    · 95后快遞小哥獲評“高層次人才”:杭州買房享受百萬元補貼
    · 二線手機廠商墜落簡史:鎚子、魅族、金立已成過客
    · 外賣員確診背後:年近50 每天接老婆下班 工作14小時
    · 歷時26年!中國終於有了自己的全球導航系統
    · 看!北斗三號最後一顆組網衛星在太空張開“翅膀” 畫面燃了
    · 攻克地獄級難度!川藏鐵路拉林段120座橋樑主體工程全部完工
    · 作家王小山控訴攜程欠錢不還:願意下跪懇請梁建章退租
    · 知乎熱議:替代Matlab的國產軟件出現 半年內實現Matlab功能的70%
    · 這個比QQ空間還古老的網站 是多少女孩的精神家園?

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

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

  • 重學 Java 設計模式:實戰備忘錄模式「模擬互聯網系統上線過程中,配置文件回滾場景」

    重學 Java 設計模式:實戰備忘錄模式「模擬互聯網系統上線過程中,配置文件回滾場景」

    作者:小傅哥
    博客:https://bugstack.cn – 原創系列專題文章

    沉澱、分享、成長,讓自己和他人都能有所收穫!

    一、前言

    實現不了是研發的借口?

    實現不了,有時候是功能複雜度較高難以實現,有時候是工期較短實現不完。而編碼的行為又是一個不太好量化的過程,同樣一個功能每個人的實現方式不一樣,遇到開發問題解決問題的速度也不一樣。除此之外還很不好給產品解釋具體為什麼要這個工期時間,這就像蓋樓的圖紙最終要多少水泥砂漿一樣。那麼這時研發會盡可能的去通過一些經驗,制定流程規範、設計、開發、評審等,確定一個可以完成的時間範圍,又避免風險的時間點后。再被壓縮,往往會出一些矛盾點,能壓縮要解釋為什麼之前要那麼多時間,不能壓縮又有各方不斷施加的壓力。因此有時候不一定是借口,是要考慮如何讓整個團隊健康的發展。

    鼓勵有時比壓力要重要!

    在學習的過程中,很多時候我們聽到的都是,你要怎樣,怎樣,你瞧瞧誰誰誰,哪怕今天聽不到這樣的聲音了,但因為曾經反覆聽到過而導致內心抗拒。雖然也知道自己要去學,但是很難堅持,學着學着就沒有了方向,看到還有那麼多不會的就更慌了,以至於最後心態崩了,更不願意學。其實程序員的壓力並不小,想成長几乎是需要一直的學習,就像似乎再也不敢說精通java了一樣,知識量實在是隨着學習的深入,越來越深,越來越廣。所以需要,開心學習,快樂成長!

    臨陣的你好像一直很着急!

    經常的聽到;老師明天就要了你幫我弄弄吧你給我寫一下完事我就學這次着急現在這不是沒時間學嗎快給我看看。其實看到的類似的還有很多,很納悶你的着急怎麼來的,不太可能,人在家中坐,禍從天上落。老師怎麼就那個時間找你了,老闆怎麼就今天管你要了,還不是日積月累你沒有學習,臨時抱佛腳亂着急!即使後來真的有人幫你了,但最好不要放鬆,要儘快學會,躲得過初一還有初二呢!

    二、開發環境

    1. JDK 1.8
    2. Idea + Maven
    3. 涉及工程一個,可以通過關注公眾號bugstack蟲洞棧,回復源碼下載獲取(打開獲取的鏈接,找到序號18)
    工程 描述
    itstack-demo-design-17-00 開發配置文件備忘錄

    三、備忘錄模式介紹

    備忘錄模式是以可以恢復或者說回滾,配置、版本、悔棋為核心功能的設計模式,而這種設計模式屬於行為模式。在功能實現上是以不破壞原對象為基礎增加備忘錄操作類,記錄原對象的行為從而實現備忘錄模式。

    這個設計在我們平常的生活或者開發中也是比較常見的,比如:後悔葯、孟婆湯(一下回滾到0),IDEA編輯和撤銷、小霸王遊戲機存檔。當然還有我們非常常見的Photoshop,如下;

    四、案例場景模擬

    在本案例中我們模擬系統在發布上線的過程中記錄線上配置文件用於緊急回滾

    在大型互聯網公司系統的發布上線一定是易用、安全、可處理緊急狀況的,同時為了可以隔離線上和本地環境,一般會把配置文件抽取出來放到線上,避免有人誤操作導致本地的配置內容發布出去。同時線上的配置文件也會在每次變更的時候進行記錄,包括;版本號、時間、MD5、內容信息和操作人。

    在後續上線時如果發現緊急問題,系統就會需要回滾操作,如果執行回滾那麼也可以設置配置文件是否回滾。因為每一個版本的系統可能會隨着帶着一些配置文件的信息,這個時候就可以很方便的讓系統與配置文件一起回滾操作。

    我們接下來就使用備忘錄模式,模擬如何記錄配置文件信息。實際的使用過程中還會將信息存放到庫中進行保存,這裏暫時只是使用內存記錄。

    五、備忘錄模式記錄配置文件版本信息

    備忘錄的設計模式實現方式,重點在於不更改原有類的基礎上,增加備忘錄類存放記錄。可能平時雖然不一定非得按照這個設計模式的代碼結構來實現自己的需求,但是對於功能上可能也完成過類似的功能,記錄系統的信息。

    除了現在的這個案例外,還可以是運營人員在後台erp創建活動對信息的記錄,方便運營人員可以上下修改自己的版本,而不至於因為誤操作而丟失信息。

    1. 工程結構

    itstack-demo-design-17-00
    └── src
        ├── main
        │   └── java
        │       └── org.itstack.demo.design
        │           ├── Admin.java
        │           ├── ConfigFile.java
        │           ├── ConfigMemento.java
        │           └── ConfigOriginator.java
        └── test
            └── java
                └── org.itstack.demo.design.test
                    └── ApiTest.java
    

    備忘錄模式模型結構

    • 以上是工程結構的一個類圖,其實相對來說並不複雜,除了原有的配置類(ConfigFile)以外,只新增加了三個類。
    • ConfigMemento:備忘錄類,相當於是對原有配置類的擴展
    • ConfigOriginator:記錄者類,獲取和返回備忘錄類對象信息
    • Admin:管理員類,用於操作記錄備忘信息,比如你一些列的順序執行了什麼或者某個版本下的內容信息

    2. 代碼實現

    2.1 配置信息類

    public class ConfigFile {
    
        private String versionNo; // 版本號
        private String content;   // 內容
        private Date dateTime;    // 時間
        private String operator;  // 操作人
        
        // ...get/set
    }
    
    • 配置類可以是任何形式的,這裏只是簡單的描述了一個基本的配置內容信息。

    2.2 備忘錄類

    public class ConfigMemento {
    
        private ConfigFile configFile;
    
        public ConfigMemento(ConfigFile configFile) {
            this.configFile = configFile;
        }
    
        public ConfigFile getConfigFile() {
            return configFile;
        }
    
        public void setConfigFile(ConfigFile configFile) {
            this.configFile = configFile;
        }
        
    }
    
    • 備忘錄是對原有配置類的擴展,可以設置和獲取配置信息。

    2.3 記錄者類

    public class ConfigOriginator {
    
        private ConfigFile configFile;
    
        public ConfigFile getConfigFile() {
            return configFile;
        }
    
        public void setConfigFile(ConfigFile configFile) {
            this.configFile = configFile;
        }
    
        public ConfigMemento saveMemento(){
            return new ConfigMemento(configFile);
        }
    
        public void getMemento(ConfigMemento memento){
            this.configFile = memento.getConfigFile();
        }
    
    }
    
    • 記錄者類除了對ConfigFile配置類增加了獲取和設置方法外,還增加了保存saveMemento()、獲取getMemento(ConfigMemento memento)
    • saveMemento:保存備忘錄的時候會創建一個備忘錄信息,並返回回去,交給管理者處理。
    • getMemento:獲取的之後並不是直接返回,而是把備忘錄的信息交給現在的配置文件this.configFile,這部分需要注意。

    2.4 管理員類

    public class Admin {
    
        private int cursorIdx = 0;
        private List<ConfigMemento> mementoList = new ArrayList<ConfigMemento>();
        private Map<String, ConfigMemento> mementoMap = new ConcurrentHashMap<String, ConfigMemento>();
    
        public void append(ConfigMemento memento) {
            mementoList.add(memento);
            mementoMap.put(memento.getConfigFile().getVersionNo(), memento);
            cursorIdx++;
        }
    
        public ConfigMemento undo() {
            if (--cursorIdx <= 0) return mementoList.get(0);
            return mementoList.get(cursorIdx);
        }
    
        public ConfigMemento redo() {
            if (++cursorIdx > mementoList.size()) return mementoList.get(mementoList.size() - 1);
            return mementoList.get(cursorIdx);
        }
    
        public ConfigMemento get(String versionNo){
            return mementoMap.get(versionNo);
        }
    
    }
    
    • 在這個類中主要實現的核心功能就是記錄配置文件信息,也就是備忘錄的效果,之後提供可以回滾和獲取的方法,拿到備忘錄的具體內容。
    • 同時這裏設置了兩個數據結構來存放備忘錄,實際使用中可以按需設置。List<ConfigMemento>Map<String, ConfigMemento>
    • 最後是提供的備忘錄操作方法;存放(append)、回滾(undo)、返回(redo)、定向獲取(get),這樣四個操作方法。

    3. 測試驗證

    3.1 編寫測試類

    @Test
    public void test() {
        Admin admin = new Admin();
        ConfigOriginator configOriginator = new ConfigOriginator();
        configOriginator.setConfigFile(new ConfigFile("1000001", "配置內容A=哈哈", new Date(), "小傅哥"));
        admin.append(configOriginator.saveMemento()); // 保存配置
        configOriginator.setConfigFile(new ConfigFile("1000002", "配置內容A=嘻嘻", new Date(), "小傅哥"));
        admin.append(configOriginator.saveMemento()); // 保存配置
        configOriginator.setConfigFile(new ConfigFile("1000003", "配置內容A=么么", new Date(), "小傅哥"));
        admin.append(configOriginator.saveMemento()); // 保存配置
        configOriginator.setConfigFile(new ConfigFile("1000004", "配置內容A=嘿嘿", new Date(), "小傅哥"));
        admin.append(configOriginator.saveMemento()); // 保存配置  
    
        // 歷史配置(回滾)
        configOriginator.getMemento(admin.undo());
        logger.info("歷史配置(回滾)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));  
    
        // 歷史配置(回滾)
        configOriginator.getMemento(admin.undo());
        logger.info("歷史配置(回滾)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));  
    
        // 歷史配置(前進)
        configOriginator.getMemento(admin.redo());
        logger.info("歷史配置(前進)redo:{}", JSON.toJSONString(configOriginator.getConfigFile()));   
    
        // 歷史配置(獲取)
        configOriginator.getMemento(admin.get("1000002"));
        logger.info("歷史配置(獲取)get:{}", JSON.toJSONString(configOriginator.getConfigFile()));
    }
    
    • 這個設計模式的學習有一部分重點是體現在了單元測試類上,這裏包括了四次的信息存儲和備忘錄歷史配置操作。
    • 通過上面添加了四次配置后,下面分別進行操作是;回滾1次再回滾1次之後向前進1次最後是獲取指定的版本配置。具體的效果可以參考測試結果。

    3.2 測試結果

    23:12:09.512 [main] INFO  org.itstack.demo.design.test.ApiTest - 歷史配置(回滾)undo:{"content":"配置內容A=嘿嘿","dateTime":159209829432,"operator":"小傅哥","versionNo":"1000004"}
    23:12:09.514 [main] INFO  org.itstack.demo.design.test.ApiTest - 歷史配置(回滾)undo:{"content":"配置內容A=么么","dateTime":159209829432,"operator":"小傅哥","versionNo":"1000003"}
    23:12:09.514 [main] INFO  org.itstack.demo.design.test.ApiTest - 歷史配置(前進)redo:{"content":"配置內容A=嘿嘿","dateTime":159209829432,"operator":"小傅哥","versionNo":"1000004"}
    23:12:09.514 [main] INFO  org.itstack.demo.design.test.ApiTest - 歷史配置(獲取)get:{"content":"配置內容A=嘻嘻","dateTime":159320989432,"operator":"小傅哥","versionNo":"1000002"}
    
    Process finished with exit code 0
    
    • 從測試效果上可以看到,歷史配置按照我們的指令進行了回滾和前進,以及最終通過指定的版本進行獲取,符合預期結果。

    六、總結

    • 此種設計模式的方式可以滿足在不破壞原有屬性類的基礎上,擴充了備忘錄的功能。雖然和我們平時使用的思路是一樣的,但在具體實現上還可以細細品味,這樣的方式在一些源碼中也有所體現。
    • 在以上的實現中我們是將配置模擬存放到內存中,如果關機了會導致配置信息丟失,因為在一些真實的場景里還是需要存放到數據庫中。那麼此種存放到內存中進行回復的場景也不是沒有,比如;Photoshop、運營人員操作ERP配置活動,那麼也就是即時性的一般不需要存放到庫中進行恢復。另外如果是使用內存方式存放備忘錄,需要考慮存儲問題,避免造成內存大量消耗。
    • 設計模式的學習都是為了更好的寫出可擴展、可管理、易維護的代碼,而這個學習的過程需要自己不斷的嘗試實際操作,理論的知識與實際結合還有很長一段距離。切記多多上手!

    七、推薦閱讀

    • 1. 重學 Java 設計模式:實戰工廠方法模式「多種類型商品不同接口,統一發獎服務搭建場景」
    • 2. 重學 Java 設計模式:實戰原型模式「上機考試多套試,每人題目和答案亂序排列場景」
    • 3. 重學 Java 設計模式:實戰橋接模式「多支付渠道(微信、支付寶)與多支付模式(刷臉、指紋)場景」
    • 4. 重學 Java 設計模式:實戰組合模式「營銷差異化人群發券,決策樹引擎搭建場景」
    • 5. 重學 Java 設計模式:實戰外觀模式「基於SpringBoot開發門面模式中間件,統一控制接口白名單場景」
    • 6. 重學 Java 設計模式:實戰享元模式「基於Redis秒殺,提供活動與庫存信息查詢場景」
    • 7. 重學 Java 設計模式:實戰備忘錄模式「模擬互聯網系統上線過程中,配置文件回滾場景」

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

    【其他文章推薦】

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

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

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

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

  • 從0到1打造數據可信的數據產品:解析數據治理在過程可信變革中的運作流程

    從0到1打造數據可信的數據產品:解析數據治理在過程可信變革中的運作流程

    摘要:本文針對“數據牽引改進,工具固化規範”這一思路在業務團隊落地過程中的動作流程進行詳細闡述,並明確了支撐整個流程的關鍵角色定義和組織運作形式。

    目的

    為實現雲服務開發的過程可信,需要基於數據對各個服務產品部的可信變革動作進行數據採集、進展可視、目標牽引、能力評估,最終用數據反映目標達成。與傳統的“基於數據晾曬驅動業務團隊改進,6+1指標度量”的運作方式有本質的區別,我們是基於統一的作業工具上產生的客觀數據呈現,識別研發過程中基本的流程斷裂點和質量缺失動作,和業務團隊達成一致的目標后,把大部分改進動作固話到作業工具中自動化承載,我們稱這個思路為“數據牽引改進,工具固化規範”,也就是我們不僅告訴業務團隊哪裡有問題,同時也要基於我們的作業工具,輔助業務團隊一起改進完善。

    本文針對“數據牽引改進,工具固化規範”這一思路在業務團隊落地過程中的動作流程進行詳細闡述,並明確了支撐整個流程的關鍵角色定義和組織運作形式。

    數據牽引改進,是指關注軟件交付過程中各種度量數據的收集、統計、分析和反饋,通過可視化的數據客觀反映整個研發過程的狀態,以全局視角分析系統約束點,並和業務團隊達成共識,提煉出客觀有效的改進目標;工具固化規範,針對識別出來的Gap點和重點問題進行分析,制定出可以在作業工具承載的模板規範,以及需要工程師行為做出改變的能力要求,並在作業工具上對這些規範要求的落地效果進行檢查,用數據度量改進效果。最後,對改進項目進行總結分享,打造學習型組織,不斷驅動持續改進和價值交付,牽引研發團隊模式和文化的轉變。

    2020年的研發過程可信圍繞CleanCode、構建、開源、E2E追溯四個領域開展,這也是公司要求的可信變革中最基本、最重要、投入產出比最大的四個點。

    整體流程說明

    整個運作流程,圍繞數據,按照“定義軟件工程規範->定義數據分析模型->工具實現數據度量和分析->數據運營發現實際軟件工程活動和規範的偏差->工具輔助團隊改進->工具固化軟件工程規範”這個流程進行實施,並對最終效果進行階段性總結。隨着業務團隊能力的提升以及軟件工程規範性、開發模式的改變,對最初定義的軟件工程規範,會階段性的進行完善,循環往複、持續優化,最終讓業務團隊在遵守公司要求的研發過程可信規範的前提下,實現業務成功。

    1) 定義軟件工程規範:圍繞公司可信變革的目標,BU對各個服務產品部的研發模式規範和能力要求,COE制定適合BU現狀的軟件工程規範;

    2) 定義數據模型:COE針對制定的軟件工程規範,提煉出核心的、有針對性、可用工具度量的數據模型,並且和各個服務產品部達成一致;

    3) 工具實現數據度量和分析:根據這幾個數據模型,數據分析工具自動從數據源進行採集、匯總、計算,並把結果呈現在數據看板上;業務團隊可以打開匯總數據,根據明細數據進行動作規範自檢和改進;

    4) 數據運營發現實際軟件工程活動和規範的偏差:數據治理小組在實際運營過程中,分析度量指標的數據,識別業務團隊實際的軟件工程活動和要求規範不一致的Gap點和關鍵問題;

    5) 工具輔助業務團隊改進:COE針對分析出來的Gap點和關鍵問題,制定相應的改進措施,作業工具承載流程規範模板化整改,並針對業務團隊的不規範行為,制定適合各個服務產品部的公約要求,促使業務團隊人員能力提升;

    6) 工具固化軟件工程規範:針對業務團隊的公約要求,在作業工具上進行check,最終作業工具承載了整個軟件工程規範要求,以及融入到作業流程中的規範要求事前檢查。

    三層數據分析模型

    我們採用了三層數據分析模型,由作業工具自動採集用戶研發過程行為明細數據,數據分析工具進行准實時匯總計算呈現總體目標,三層數據系統性的輔助業務團隊系統性的識別研發過程中的不規範點和能力短板,讓業務團隊“知其然,知其所以然”。這三層數據模型是層層深入,迭代完善,下層支撐上層的關係。

    第一層:目標、進展、結果數據分析;和公司可信變革目標牽引對齊,結合BU實際情況,形成BU的整體可信要求,並在數據分析看板上呈現各個服務產品部要達成的過程可信目標、每日的改進進展和最終完成情況;例如,對各個服務產品部要求達成CleanCode的目標。

    第二層:詞法/語法分析數據;COE針對第一層的目標牽引,分解出來的具體實施環節的度量指標,只有這些分解的指標都完成,第一層的目標才達成。這一層數據的目的主要是圍繞幫助業務團隊分析自己的能力短板在哪裡,進行有針對性的改進指;通過打開匯總數據的層層下鑽,用明細數據來分析業務團隊在DevSecOps軟件工程規範流程中關鍵動作執行的缺失點,並針對性的制定改進規範要求,牽引作業工具或者業務團隊補齊該部分缺失動作;例如,CleanCode的過程可信目標達成,可以分解成:靜態檢查配置合規率、Committer合入保障率、代碼倉Clean三個目標,只有這三個目標達成,就可以認為CleanCode總體目標達成。

    第三層:語義分析數據:COE打開第二層數據,不僅要看這些關鍵動過做沒做,還要看做的效果怎麼樣,最終效果體現在業務團隊的DevSecOps軟件工程規範提升;這一層的數據分析聚焦在防止為了指標而做第二層的數據,而是看業務團隊是否在真正參考BU制定的規範牽引的目標,提升業務交付過程中的效能、可信、質量能力,以及最終產生實際的業務效果。通過打開各個團隊的明細數據分析審視業務團隊執行的關鍵動作是否符合規範,是否在合適的階段點執行,執行效果是否有效;並階段性的總結和提煉經驗,形成知識資產固化到作業工具。例如,針對第二層的靜態檢查配置合規率,可以分解為:靜態檢查配置有效性和靜態檢查執行有效性。靜態檢查配置有效性,包括:檢查靜態檢查工具配置的數量、是否符合BU的配置規範,以及是否在代碼合入主幹master時進行了配置;靜態檢查執行有效性,主要看是否每一次MR提交時都執行靜態檢查、是否發現問題在研發活動的最早階段,攔截的問題的效果怎麼樣。只有第三層的動作度量都達成后,才可以說第二層的目標是達成的。

    數據治理過程流程圖

    為了實現“數據牽引改進,工具固化規範”這個目標,準確、一致、准實時的數據是核心關鍵,但因為數據採集不完整、業務團隊不規範、數據呈現維度不一致等原因,數據的準確性有一個不斷提升的過程,因此需要對各個層級展示的數據進行治理。整個數據治理過程中,由“業務團隊/作業工具/治理小組/數據分析工具(阿基米德)/COE”五個角色緊密配合,而且以年/半年為目標,不斷總結經驗,循環往複、持續優化的過程。

    a) COE:和公司可信變革目標牽引對齊,結合BU能力現狀,形成BU的整體可信要求;

    b) COE:針對BU的業務現狀,定義出適合BU現狀的軟件工程規範要求;業務團隊:和BU發布的各個領域的軟件工程規範牽引目標達成一致;

    c) COE:針對規範分解出核心的度量指標,並制定度量數據模型;

    d) 研發用戶:在使用作業工具進行研發活動;作業工具:承載了BU各個服務產品部在使用過程中沉澱的行為數據;

    e) 數據分析(阿基米德):准實時接入作業工具的數據,展示各個服務產品部當前的研發能力現狀;

    f) COE:和各個服務產品部達成一致,制定各個服務產品部的年度牽引目標;

    g) 數據分析(阿基米德):用數據呈現各個服務產品部的牽引目標和能力現狀,統一數據口徑;呈現月/周/天的明細數據,以及支撐Gap分析和重點問題的數據視圖;

    h) COE:根據牽引目標和能力現狀,分析Gap原因和關鍵問題;治理小組:在數據運營過程中,根據數據分析團隊當前的能力現狀是否和數據呈現一致;

    i) 研發用戶:可以實時登錄數據工具(阿基米德)進行查看各個層級的明細數據;

    j) 治理小組:根據准實時進展數據,分析當前團隊研發過程中的實際問題,並匯總給COE;

    k) COE:結合細粒度的分析數據、以及治理小組匯總出來的各個服務產品部的實際問題,制定規範和改進措施,包括作業工具的規範和研發用戶的動作行為公約;

    l) 作業工具:承載作業工具上落地的規範要求;治理小組:作為接口人,承接研發工程師的行為規範公約,結合各個服務產品部實際情況來負責落地;

    m) 研發用戶:按照規範要求和針對數據的自檢進行研發過程行為規範化;

    n) 研發工具:對研發用戶的行為規範是否滿足要求進行自動化檢查;最終目標是讓整個軟件工程規範都固化在工具中進行承載;

    o) 數據分析(阿基米德):呈現按照規範改進后的明細數據和匯總目標;研發用戶:自助查看整改后的明細數據;

    p) COE:根據數據改進的效果,以及過程中暴露的問題進行總結后形成經驗資產,並持續改進;

    數據流圖

    過程可信的數據在各個工具系統中採集、流轉、匯聚、分析、呈現,整個數據流圖如下:

    其中,識別出6個重要的全量數據源:

    a) 代碼庫數據:該數據由伏羲的服務信息樹上配置的代碼庫數據,加上阿基米德上人工配置的代碼庫,構成各個雲服務發布到生產倉的代碼全集;

    b) Workitem信息流數據:當前識別vision上的需求、問題、task,加上Gitlab/Codeclub上的issue,構成可識別的Workitem數據全集;

    c) SRE現網包數據:包括普羅部署、CCE、CPS、CDK各種類型部署的包數據,構成全量現網包數據;

    d) 開源二進制包數據:開源中心倉數據(java、python、go、nodejs四種)語言,加上公司c/c++的數據構成全量開源二進制包數據;

    e) 研發過程配置數據:阿基米德上配置的committer數據是全量的committer數據;阿基米德上識別出來的主分支是全量的主分支(邏輯“master”)數據;

    f) 伏羲研發過程數據:伏羲三個庫,MongoDB的靜態檢查、門禁數據;MySQL中的測試、發布數據;MySQL中包和多個流水線的對應關係數據;一起構成了以“包”為維度的全量伏羲研發過程數據;

    運作組織

    數據治理運營團隊

    按照過程可信在BU的落地策略,在CleanCode、構建、開源、E2E追溯四個領域設置數據治理運營團隊,由 “數據分析工具(阿基米德)—COE—各個服務產品部接口人組成的治理小組”三個角色組成,以“指標度量為牽引,數據的客觀呈現為落地方式,業務的價值反饋為最終目的”的原則來落地數據治理工作。

    COE的職責:

    1) 和公司可信變革目標牽引對齊,結合BU能力現狀,形成BU的整體可信要求;定義出適合BU現狀的軟件工程規範要求;針對規範分解出核心的度量指標,並制定度量數據模型;

    2) 利用作業工具已經產生的數據,和治理小組一起分析識別數據質量的問題,按照三層數據分析模型,層層打開,識別業務團隊能力Gap點。

    3) 分析典型問題,識別作業流的斷裂點進行補齊,和業務團隊的不規範動作,制定規範和公約要求,逐步改善數據質量。

    4) 事後歸納總結,識別出流程缺失,組織缺失,責任缺失等機制行問題,並固化到作業工具中。

    治理小組:

    1) 結合各個服務產品部的實際情況,承接COE的數據治理規範在各個服務產品部的落地;

    2) 識別數據治理動作在各個服務產品部落地過程中的實際問題,和COE一起分析,提出系統性的解決思路,最終固化到作業工具中。

    3) 跟蹤過程可信在業務團隊落地的過程中的進展,為業務團隊最終達成可信變革目標負責,為改進過程產生實際的業務價值負責;

    數據分析工具(阿基米德):

    1) 確保接入的數據準確、實時、一致,用數據實時反映BU各個服務產品部的能力現狀,為COE和治理小組的數據運營提供數據支撐;

    2) 系統性的落地COE的方案設計,實現整個BU統一標準的數據看板,能夠清晰的通過數據識別出來業務團隊的能力Gap,牽引業務團隊達成整體改進目標;

    3) 按照三層數據模型進行數據展示,層層下鑽,讓業務團隊“知其然,知其所以然”,牽引業務團隊中的每一個人都能自己進行改進;

    4) 通過數據分析,識別DevSecOps軟件工程規範在BU的業務團隊落地過程中的重點問題,以及該問題背後的流程、制度缺失,促使最終規範固化在作業工具中。

    例會設置

    “數據驅動DevSecOps能力提升例會”為研發領域數據治理相關問題的求助和裁決例會。

    會議分為三個階段:

    1) 第一階段,例行議題,形式類似於“體檢報告”,用數據反映業務團隊的現狀和問題;

    2) 第二階段,申報議題,形式類似於“專家會診”,討論某一個具體數據治理過程中的問題和Top困難求助;

    3) 第三階段,靈活安排議題,形式類似於“問題總結”,針對某一類的具體問題,進行集中討論和歸納總結定義,形成BU的規範流程和章程總結。

    主數據承載系統

    主數據是指具有高業務價值的、可以在企業內跨越多個業務部門被重複使用的數據,是單一、準確、權威的數據來源。和業務型數據、分析型數據相比,主數據主要有以下幾個特徵:

    1) 特徵一致性:也就是能否保證主數據的關鍵特徵在不同應用、不同系統中的高度一致,直接關係了數據治理成敗;

    2) 識別唯一性:在一個系統、一個平台,甚至一個企業範圍內,同一主數據實體要求具有唯一的數據標識,即數據編碼;

    3) 長期有效性:貫穿該業務對象的整個生命周期甚至更長,當該主數據失去其效果時,系統採取的措施通常為軟刪除,而不是物理刪除;

    4) 業務穩定性:在業務過程中其識別信息和關鍵特徵會被業務過程中產生的數據繼承、引用和複製。除非該主數據本身的特徵發生變化,否則該主數據不會隨着業務的過程中被其他系統修改。

    主數據源識別原則:

    a) 如果有多個數據源構成同類型數據的主數據,兩種處理策略:

    1)選取一個源系統逐步收編其他源系統的數據,變成唯一主數據源

    2)如果1)不能實現,由阿基米德系統進行封裝后屏蔽多個數據源系統,該類型數據的唯一數據源變成阿基米德,待後續1)實現后,阿基米德該類型主數據源失效。

    3)當數據在多個作業系統中進行流轉時,判斷是否作為主數據源的標準是:數據在該系統有實際的業務動作產生,而不是只承載數據的流轉。

    b) 如果確定為唯一數據源,其他消費該類型數據的系統不能和數據源產生衝突。

    所有數據僅能在數據源產生,其它系統只能讀取不能修改。下游發現的數據源質量問題,應當在數據源頭進行修正。

    c) 主數據使用方不得改變原始數據,但可以進行擴展。

    數據消費方不得對獲取的數據進行增、刪、改,但可以在數據的基礎上進行屬性擴展。

    d) 在滿足信息安全的前提下充分共享,不得拒絕合理的數據共享需求。

    數據如果不流轉,不僅不會產生業務價值,還增加存儲成本;只有不斷流轉,對業務團隊產生實際價值時,還能得到使用效果的反饋,促進數據價值的進一步提升。

    原則為:核心資產安全優先,非關鍵資產效率優先。

    一類主數據源

    二類主數據源

     

    點擊關注,第一時間了解華為雲新鮮技術~

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

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

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