LG Chem看中大陸電動車市場 將設第三座海外電池廠

中國大陸對抗空汙,大力推動環保車,LG Chem看好當地前景,15日宣佈將在中國設立合資電池工廠,目前已進入挑選規則合作對象的最後階段。

LG Chem表示,今年下半會公佈合資電池廠的設廠地點和其他細節。發言人Song Choong-sup強調,這將是LG Chem第三座海外電池工廠,該公司在美國密西根州和中國南京設有工廠。IHS預估,2020年中國電動車的電池市場將成長20倍。

中國第一汽車集團和長安汽車之前就已和LG Chem敲定協議,而最近,LG Chem和中國最大車廠上海汽車集團以及觀致汽車簽訂電池供應合約。Song證實,上汽的插電式油電混合車和觀致的次世代電動車將使用LG電池。

據稱,LG高層一致認為全球科技的未來主軸在聯網汽車和無人駕駛車,LG Display計畫明年成為全球車用面板的頂級供應商,內部主管表示,正與Mercedez、Audi等車廠攜手研發高階顯示面板,他們打算用LG Chem電池廠的客戶,將LG打造為汽車解決方案的供應商,並強調若不隨著時勢改變,將無法生存。

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

【Spring】AOP的代理默認是Jdk還是Cglib?,【DP-動態代理】JDK&Cglib

菜瓜:你覺得AOP是啥

水稻:我覺得吧,AOP是對OOP的補充。通常情況下,OOP代碼專註功能的實現,所謂面向切面編程,大多數時候是對某一類對象的方法或者功能進行增強或者抽象

菜瓜:我看你這個理解就挺抽象的

水稻:舉個栗子!我要在滿足開閉原則的基礎下對已有功能進行擴展

  • 我現在想對很多個功能增加日誌功能,但是代碼已經打好包了,不想改。又或者有時候方法調用很慢,想定位問題
  • low一點的方法就是每個方法調用之前記錄調用開始,之後記錄調用結束

菜瓜:你說的這個low一點的方法怎麼好像是在說我???

水稻:建議看一下動態代理設計模式【DP-動態代理】JDK&Cglib,我當然知道你不會看,所以我還準備了自定義註解的栗子

  • package com.hb.merchant.config.aop;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    /**
     * @author QuCheng on 2020/6/23.
     */
    @Configuration
    @EnableAspectJAutoProxy
    public class AopConfig {
    }
    
    package com.hb.merchant.config.aop;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author QuCheng on 2020/6/23.
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface OperatorLog {
    }
    
    
    package com.hb.merchant.config.aop;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    /**
     *
     * @author QuCheng on 2020/6/23.
     */
    @Aspect
    @Component
    @Slf4j
    public class OperatorAspect {
    
        @Around("@annotation(OperatorLog)")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            //獲取要執行的方法
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //記錄方法執行前日誌
            log.info("startLog: {} 開始了。。。" , methodSignature.getName());
            //獲取方法信息
            String[] argNames = methodSignature.getParameterNames();
            // 參數值:
            final Object[] argValues = joinPoint.getArgs();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < argNames.length; i++) {
                String value = argValues[i] == null ? "null" : argValues[i].toString();
                sb.append(argNames[i]).append("=").append(value).append(",");
            }
            String paramStr = sb.length() > 0 ? sb.toString().substring(0, sb.length() - 1) + "]" : "";
            log.info("參數信息為:[{}", paramStr);
    
            //執行方法
            Object result;
            try {
                result = joinPoint.proceed();
            } catch (Exception e) {
                log.error("errorLog", e);
                return null;
            }
    
            //記錄方法執行後日志
            log.info("endLog: {} 結束了。。。" , methodSignature.getName());
            return result;
        }
    
    }
    
    
    
    package com.hb.merchant.controller.icbc.item.oc;
    
    import com.hb.merchant.config.aop.OperatorLog;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.util.Assert;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author QuCheng on 2020-06-23.
     */
    @RestController
    @RequestMapping("/item")
    @Slf4j
    public class ItemOcController {
    
        @OperatorLog
        @GetMapping("/delete")
        public String delete(Long itemId) {
            Assert.notNull(itemId,"itemId不能為空");
            return "delete finished ...";
        }
    }

    // 後台打印
    startLog: delete 開始了。。。
    參數信息為:[itemId=1]
    endLog: delete 結束了。。。

菜瓜:這個自定義註解又是怎麼實現的呢?

水稻:不愧是你,沒有源碼看來是滿足不了你的好奇心了!!不知道你是否還記得我們之前有聊到過bean創建完畢後會調用一些PostProcessor對其進一步操作

菜瓜:有印象,@PostConstruct註解就是InitDestroyAnnotationBeanPostProcessor在這裏調用的,還自定義過BeanPostProcessorT對象打印輸出過bean信息

水稻:你猜Spring是怎麼操作的

菜瓜:let me try try。結合剛剛的栗子和提示,大膽猜測應該是用PostProcessor在bean創建完成之後生成代理對象。實際調用代理的invoke方法實現對被代理bean的增強

水稻:思路正確。看脈絡

  • 入口在AbstractAdvisorAutoProxyCreator#initializeBean
  • protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
            。。。
                // BeanNameAware BeanFactoryAware ...
                invokeAwareMethods(beanName, bean);
        。。。    
                // BeanPostProcessorBefore  @PostConstruct
                wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        。。。
                // initMethod InitializingBean接口
                invokeInitMethods(beanName, wrappedBean, mbd);
                。。。
            if (mbd == null || !mbd.isSynthetic()) {
                // aop
                wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            }
            return wrappedBean;
        }
  • 從aop入口跟下去
  • protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
            。。。
            // 收集切面信息匹配被代理對象
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
          // 如果符合切面 創建代理,被代理對象被代理引用
                Object proxy = createProxy(
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
    
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
  • 跟createProxy方法 -> DefaultAopProxyFactory#createAopProxy
  • @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
       if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
          Class<?> targetClass = config.getTargetClass();
          if (targetClass == null) {
             throw new AopConfigException("TargetSource cannot determine target class: " +
                   "Either an interface or a target is required for proxy creation.");
          }
          if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            // jdk動態代理類
             return new JdkDynamicAopProxy(config);
          }
          // cglib
          return new ObjenesisCglibAopProxy(config);
       }
       else {
          return new JdkDynamicAopProxy(config);
       }
    }
  • 此處省略了切面類搜集和匹配的過程。可以簡單理解成搜集到所有的切面類信息獲取pointcut的目錄或者註解信息,匹配當前bean是否屬於pointcut目標範圍
  • 另外我們可以看到最後返回的bean已經不是原始bean了,而是代理對象。也就是說getBean(“xxx”)返回的對象實際是代理對象,被代理對象被其成員變量直接引用

菜瓜:然後代理類中都有invoke方法,那些advice(@Around,@Before…)在invoke中找到適當時機調用對吧

水稻:是的,這裏我想結合@Transactional註解會更容易理解,你肯定用過這個註解吧,它其實。。。

菜瓜:停。。。今天獲取的知識量已經夠了,我下去自己斷點走一趟再熟悉熟悉。下次請結合Transactional註解再敲打我吧

水稻:也好,我下去再給你準備幾個栗子

 

總結:

  • AOP提供了在不侵入代碼的前提下動態增強目標對象的途徑,讓OOP更加專註於實現自己的邏輯
  • 而Spring的實現還是老套路,利用PostProcessor在類初始化完成之後替需要的bean創建代理對象
  • 這裏還有一些細節沒有照顧到,譬如說AOP解析類是什麼時候註冊到IOC容器的(偷偷告訴你從@EnableAspectJAutoProxy註解下手)

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

【其他文章推薦】

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

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

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

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

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

【原創】Linux中斷子系統(四)-Workqueue

背景

  • Read the fucking source code! –By 魯迅
  • A picture is worth a thousand words. –By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

  • Workqueue工作隊列是利用內核線程來異步執行工作任務的通用機制;
  • Workqueue工作隊列可以用作中斷處理的Bottom-half機制,利用進程上下文來執行中斷處理中耗時的任務,因此它允許睡眠,而Softirq和Tasklet在處理任務時不能睡眠;

來一張概述圖:

  • 在中斷處理過程中,或者其他子系統中,調用workqueue的調度或入隊接口后,通過建立好的鏈接關係圖逐級找到合適的worker,最終完成工作任務的執行;

2. 數據結構

2.1 總覽

此處應有圖:

  • 先看看關鍵的數據結構:
    1. work_struct:工作隊列調度的最小單位,work item;
    2. workqueue_struct:工作隊列,work item都掛入到工作隊列中;
    3. worker:work item的處理者,每個worker對應一個內核線程;
    4. worker_pool:worker池(內核線程池),是一個共享資源池,提供不同的worker來對work item進行處理;
    5. pool_workqueue:充當橋樑紐帶的作用,用於連接workqueue和worker_pool,建立鏈接關係;

下邊看看細節吧:

2.2 work

struct work_struct用來描述work,初始化一個work並添加到工作隊列后,將會將其傳遞到合適的內核線程來進行處理,它是用於調度的最小單位。

關鍵字段描述如下:

struct work_struct {
	atomic_long_t data;     //低比特存放狀態位,高比特存放worker_pool的ID或者pool_workqueue的指針
	struct list_head entry; //用於添加到其他隊列上
	work_func_t func;       //工作任務的處理函數,在內核線程中回調
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

圖片說明下data字段:

2.3 workqueue

  • 內核中工作隊列分為兩種:

    1. bound:綁定處理器的工作隊列,每個worker創建的內核線程綁定到特定的CPU上運行;
    2. unbound:不綁定處理器的工作隊列,創建的時候需要指定WQ_UNBOUND標誌,內核線程可以在處理器間遷移;
  • 內核默認創建了一些工作隊列(用戶也可以創建):

    1. system_mq:如果work item執行時間較短,使用本隊列,調用schedule[_delayed]_work[_on]()接口就是添加到本隊列中;
    2. system_highpri_mq:高優先級工作隊列,以nice值-20來運行;
    3. system_long_wq:如果work item執行時間較長,使用本隊列;
    4. system_unbound_wq:該工作隊列的內核線程不綁定到特定的處理器上;
    5. system_freezable_wq:該工作隊列用於在Suspend時可凍結的work item;
    6. system_power_efficient_wq:該工作隊列用於節能目的而選擇犧牲性能的work item;
    7. system_freezable_power_efficient_wq:該工作隊列用於節能或Suspend時可凍結目的的work item;

struct workqueue_struct關鍵字段介紹如下:

struct workqueue_struct {
	struct list_head	pwqs;		/* WR: all pwqs of this wq */   //所有的pool_workqueue都添加到本鏈表中
	struct list_head	list;		/* PR: list of all workqueues */    //用於將工作隊列添加到全局鏈表workqueues中

	struct list_head	maydays;	/* MD: pwqs requesting rescue */    //rescue狀態下的pool_workqueue添加到本鏈表中
	struct worker		*rescuer;	/* I: rescue worker */  //rescuer內核線程,用於處理內存緊張時創建工作線程失敗的情況

	struct pool_workqueue	*dfl_pwq;	/* PW: only for unbound wqs */

	char			name[WQ_NAME_LEN]; /* I: workqueue name */

	/* hot fields used during command issue, aligned to cacheline */
	unsigned int		flags ____cacheline_aligned; /* WQ: WQ_* flags */
	struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */     //Per-CPU都創建pool_workqueue
	struct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */    //Per-Node創建pool_workqueue
    ...
};

2.4 worker

  • 每個worker對應一個內核線程,用於對work item的處理;
  • worker根據工作狀態,可以添加到worker_pool的空閑鏈表或忙碌列表中;
  • worker處於空閑狀態時並接收到工作處理請求,將喚醒內核線程來處理;
  • 內核線程是在每個worker_pool中由一個初始的空閑工作線程創建的,並根據需要動態創建和銷毀;

關鍵字段描述如下:

struct worker {
	/* on idle list while idle, on busy hash table while busy */
	union {
		struct list_head	entry;	/* L: while idle */     //用於添加到worker_pool的空閑鏈表中
		struct hlist_node	hentry;	/* L: while busy */ //用於添加到worker_pool的忙碌列表中
	};

	struct work_struct	*current_work;	/* L: work being processed */   //當前正在處理的work
	work_func_t		current_func;	/* L: current_work's fn */                  //當前正在執行的work回調函數
	struct pool_workqueue	*current_pwq; /* L: current_work's pwq */   //指向當前work所屬的pool_workqueue

	struct list_head	scheduled;	/* L: scheduled works */    //所有被調度執行的work都將添加到該鏈表中

	/* 64 bytes boundary on 64bit, 32 on 32bit */

	struct task_struct	*task;		/* I: worker task */    //指向內核線程
	struct worker_pool	*pool;		/* I: the associated pool */    //該worker所屬的worker_pool
						/* L: for rescuers */
	struct list_head	node;		/* A: anchored at pool->workers */  //添加到worker_pool->workers鏈表中
						/* A: runs through worker->node */
    ...
};

2.5 worker_pool

  • worker_pool是一個資源池,管理多個worker,也就是管理多個內核線程;
  • 針對綁定類型的工作隊列,worker_pool是Per-CPU創建,每個CPU都有兩個worker_pool,對應不同的優先級,nice值分別為0和-20;
  • 針對非綁定類型的工作隊列,worker_pool創建後會添加到unbound_pool_hash哈希表中;
  • worker_pool管理一個空閑鏈表和一個忙碌列表,其中忙碌列表由哈希管理;

關鍵字段描述如下:

struct worker_pool {
	spinlock_t		lock;		/* the pool lock */
	int			cpu;		/* I: the associated cpu */     //綁定到CPU的workqueue,代表CPU ID
	int			node;		/* I: the associated node ID */ //非綁定類型的workqueue,代表內存Node ID
	int			id;		/* I: pool ID */
	unsigned int		flags;		/* X: flags */

	unsigned long		watchdog_ts;	/* L: watchdog timestamp */

	struct list_head	worklist;	/* L: list of pending works */  //pending狀態的work添加到本鏈表
	int			nr_workers;	/* L: total number of workers */    //worker的數量

	/* nr_idle includes the ones off idle_list for rebinding */
	int			nr_idle;	/* L: currently idle ones */

	struct list_head	idle_list;	/* X: list of idle workers */   //處於IDLE狀態的worker添加到本鏈表
	struct timer_list	idle_timer;	/* L: worker idle timeout */
	struct timer_list	mayday_timer;	/* L: SOS timer for workers */

	/* a workers is either on busy_hash or idle_list, or the manager */
	DECLARE_HASHTABLE(busy_hash, BUSY_WORKER_HASH_ORDER);   //工作狀態的worker添加到本哈希表中
						/* L: hash of busy workers */

	/* see manage_workers() for details on the two manager mutexes */
	struct worker		*manager;	/* L: purely informational */
	struct mutex		attach_mutex;	/* attach/detach exclusion */
	struct list_head	workers;	/* A: attached workers */   //worker_pool管理的worker添加到本鏈表中
	struct completion	*detach_completion; /* all workers detached */

	struct ida		worker_ida;	/* worker IDs for task name */

	struct workqueue_attrs	*attrs;		/* I: worker attributes */
	struct hlist_node	hash_node;	/* PL: unbound_pool_hash node */    //用於添加到unbound_pool_hash中
    ...
} ____cacheline_aligned_in_smp;

2.6 pool_workqueue

  • pool_workqueue充當紐帶的作用,用於將workqueue和worker_pool關聯起來;

關鍵字段描述如下:

struct pool_workqueue {
	struct worker_pool	*pool;		/* I: the associated pool */    //指向worker_pool
	struct workqueue_struct *wq;		/* I: the owning workqueue */   //指向所屬的workqueue

	int			nr_active;	/* L: nr of active works */     //活躍的work數量
	int			max_active;	/* L: max active works */   //活躍的最大work數量
	struct list_head	delayed_works;	/* L: delayed works */      //延遲執行的work掛入本鏈表
	struct list_head	pwqs_node;	/* WR: node on wq->pwqs */      //用於添加到workqueue鏈表中
	struct list_head	mayday_node;	/* MD: node on wq->maydays */   //用於添加到workqueue鏈表中
    ...
} __aligned(1 << WORK_STRUCT_FLAG_BITS);

2.7 小結

再來張圖,首尾呼應一下:

3. 流程分析

3.1 workqueue子系統初始化

  • workqueue子系統的初始化分成兩步來完成的:workqueue_init_early和workqueue_init。

3.1.1 workqueue_init_early

  • workqueue子系統早期初始化函數完成的主要工作包括:
    1. 創建pool_workqueue的SLAB緩存,用於動態分配struct pool_workqueue結構;
    2. 為每個CPU都分配兩個worker_pool,其中的nice值分別為0和HIGHPRI_NICE_LEVEL,並且為每個worker_pool從worker_pool_idr中分配一個ID號;
    3. 為unbound工作隊列創建默認屬性,struct workqueue_attrs屬性,主要描述內核線程的nice值,以及cpumask值,分別針對優先級以及允許在哪些CPU上執行;
    4. 為系統默認創建幾個工作隊列,這幾個工作隊列的描述在上文的數據結構部分提及過,不再贅述;

從圖中可以看出創建工作隊列的接口為:alloc_workqueue,如下圖:

  • alloc_workqueue完成的主要工作包括:
    1. 首先當然是要分配一個struct workqueue_struct的數據結構,並且對該結構中的字段進行初始化操作;
    2. 前文提到過workqueue最終需要和worker_pool關聯起來,而這個紐帶就是pool_workqueue,alloc_and_link_pwqs函數就是完成這個功能:1)如果工作隊列是綁定到CPU上的,則為每個CPU都分配pool_workqueue並且初始化,通過link_pwq將工作隊列與pool_workqueue建立連接;2)如果工作隊列不綁定到CPU上,則按內存節點(NUMA,參考之前內存管理的文章)來分配pool_workqueue,調用get_unbound_pool來實現,它會根據wq屬性先去查找,如果沒有找到相同的就創建一個新的pool_workqueue,並且添加到unbound_pool_hash哈希表中,最後也會調用link_pwq來建立連接;
    3. 創建工作隊列時,如果設置了WQ_MEM_RECLAIM標誌,則會新建rescuer worker,對應rescuer_thread內核線程。當內存緊張時,新創建worker可能會失敗,這時候由rescuer來處理這種情況;
    4. 最終將新建好的工作隊列添加到全局鏈表workqueues中;

3.1.2 workqueue_init

workqueue子系統第二階段的初始化:

  • 主要完成的工作是給之前創建好的worker_pool,添加一個初始的worker;
  • create_worker函數中,創建的內核線程名字為kworker/XX:YY或者kworker/uXX:YY,其中XX表示worker_pool的編號,YY表示worker的編號,u表示unbound;

workqueue子系統初始化完成后,基本就已經將數據結構的關聯建立好了,當有work來進行調度的時候,就可以進行處理了。

3.2 work調度

3.2.1 schedule_work

以schedule_work接口為例進行分析:

  • schedule_work默認是將work添加到系統的system_work工作隊列中;

  • queue_work_on接口中的操作判斷要添加work的標誌位,如果已經置位了WORK_STRUCT_PENDING_BIT,表明已經添加到了隊列中等待執行了,否則,需要調用__queue_work來進行添加。注意了,這個操作是在關中斷的情況下進行的,因為工作隊列使用WORK_STRUCT_PENDING_BIT位來同步work的插入和刪除操作,設置了這個比特后,然後才能執行work,這個過程可能被中斷或搶佔打斷;

  • workqueue的標誌位設置了__WQ_DRAINING,表明工作隊列正在銷毀,所有的work都要處理完,此時不允許再將work添加到隊列中,有一種特殊情況:銷毀過程中,執行work時又觸發了新的work,也就是所謂的chained work;

  • 判斷workqueue的類型,如果是bound類型,根據CPU來獲取pool_workqueue,如果是unbound類型,通過node號來獲取pool_workqueue;

  • get_work_pool獲取上一次執行work的worker_pool,如果本次執行的worker_pool與上次執行的worker_pool不一致,且通過find_worker_executing_work判斷work正在某個worker_pool中的worker中執行,考慮到緩存熱度,放到該worker執行是更合理的選擇,進而根據該worker獲取到pool_workqueue;

  • 判斷pool_workqueue活躍的work數量,少於最大限值則將work加入到pool->worklist中,否則加入到pwq->delayed_works鏈表中,如果__need_more_worker判斷沒有worker在執行,則喚醒worker內核線程執行;

  • 總結:

    1. schedule_work完成的工作是將work添加到對應的鏈表中,而在添加的過程中,首先是需要確定pool_workqueue;
    2. pool_workqueue對應一個worker_pool,因此確定了pool_workqueue也就確定了worker_pool,進而可以將work添加到工作鏈表中;
    3. pool_workqueue的確定分為三種情況:1)bound類型的工作隊列,直接根據CPU號獲取;2)unbound類型的工作隊列,根據node號獲取,針對unbound類型工作隊列,pool_workqueue的釋放是異步執行的,需要判斷refcnt的計數值,因此在獲取pool_workqueue時可能要多次retry;3)根據緩存熱度,優先選擇正在被執行的worker_pool;

3.2.2 worker_thread

work添加到工作隊列后,最終的執行在worker_thread函數中:

  • 在創建worker時,創建內核線程,執行函數為worker_thread;

  • worker_thread在開始執行時,設置標誌位PF_WQ_WORKER,調度器在進行調度處理時會對task進行判斷,針對workerqueue worker有特殊處理;

  • worker對應的內核線程,在沒有處理work的時候是睡眠狀態,當被喚醒的時候,跳轉到woke_up開始執行;

  • woke_up之後,如果此時worker是需要銷毀的,那就進行清理工作並返回。否則,離開IDLE狀態,並進入recheck模塊執行;

  • recheck部分,首先判斷是否需要更多的worker來處理,如果沒有任務處理,跳轉到sleep地方進行睡眠。有任務需要處理時,會判斷是否有空閑內核線程以及是否需要動態創建,再清除掉worker的標誌位,然後遍歷工作鏈表,對鏈表中的每個節點調用process_one_worker來處理;

  • sleep部分比較好理解,沒有任務處理時,worker進入空閑狀態,並將當前的內核線程設置成睡眠狀態,讓出CPU;

  • 總結:

    1. 管理worker_pool的內核線程池時,如果有PENDING狀態的work,並且發現沒有正在運行的工作線程(worker_pool->nr_running == 0),喚醒空閑狀態的內核線程,或者動態創建內核線程;
    2. 如果work已經在同一個worker_pool的其他worker中執行,不再對該work進行處理;

work的執行函數為process_one_worker:

  • work可能在同一個CPU上不同的worker中運行,直接退出;
  • 調用worker->current_func(),完成最終work的回調函數執行;

3.3 worker動態管理

3.3.1 worker狀態機變換

  • worker_pool通過nr_running字段來在不同的狀態機之間進行切換;
  • worker_pool中有work需要處理時,需要至少保證有一個運行狀態的worker,當nr_running大於1時,將多餘的worker進入IDLE狀態,沒有work需要處理時,所有的worker都會進入IDLE狀態;
  • 執行work時,如果回調函數阻塞運行,那麼會讓worker進入睡眠狀態,此時調度器會進行判斷是否需要喚醒另一個worker;
  • IDLE狀態的worker都存放在idle_list鏈表中,如果空閑時間超過了300秒,則會將其進行銷毀;
  1. Running->Suspend
  • 當worker進入睡眠狀態時,如果該worker_pool沒有其他的worker處於運行狀態,那麼是需要喚醒一個空閑的worker來維持併發處理的能力;
  1. Suspend->Running
  • 睡眠狀態可以通過wake_up_worker來進行喚醒處理,最終判斷如果該worker不在運行狀態,則增加worker_pool的nr_running值;

3.3.2 worker的動態添加和刪除

  1. 動態刪除
  • worker_pool初始化時,註冊了timer的回調函數,用於定時對空閑鏈表上的worker進行處理,如果worker太多,且空閑時間太長,超過了5分鐘,那麼就直接進行銷毀處理了;
  1. 動態添加
  • 內核線程執行worker_thread函數時,如果沒有空閑的worker,會調用manage_workers接口來創建更多的worker來處理工作;

參考

Documentation/core-api/workqueue.rst
http://kernel.meizu.com/linux-workqueue.html

洗洗睡了,收工!

歡迎關注公眾號,不定期分享Linux內核機制文章

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

【其他文章推薦】

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

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

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

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

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

【String註解驅動開發】你了解@PostConstruct註解和@PreDestroy註解嗎?

寫在前面

在之前的文章中,我們介紹了如何使用@Bean註解指定初始化和銷毀的方法,小夥伴們可以參見《【Spring註解驅動開發】如何使用@Bean註解指定初始化和銷毀的方法?看這一篇就夠了!!》,也介紹了使用InitializingBean和DisposableBean來處理bean的初始化和銷毀,小夥伴們可以參見《【Spring註解驅動開發】Spring中的InitializingBean和DisposableBean,你真的了解嗎?》。除此之外,在JDK中也提供了兩個註解能夠在bean加載到Spring容器之後執行和在bean銷毀之前執行,今天,我們就一起來看看這兩個註解的用法。

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

@PostConstruct註解

@PostConstruct註解好多人以為是Spring提供的。其實是Java自己的註解。我們來看下@PostConstruct註解的源碼,如下所示。

package javax.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}

從源碼可以看出,@PostConstruct註解是Java中的註解,並不是Spring提供的註解。

@PostConstruct註解被用來修飾一個非靜態的void()方法。被@PostConstruct修飾的方法會在服務器加載Servlet的時候運行,並且只會被服務器執行一次。PostConstruct在構造函數之後執行,init()方法之前執行。

通常我們會是在Spring框架中使用到@PostConstruct註解,該註解的方法在整個Bean初始化中的執行順序:

Constructor(構造方法) -> @Autowired(依賴注入) -> @PostConstruct(註釋的方法)。

@PreDestroy註解

@PreDestroy註解同樣是Java提供的,看下源碼,如下所示。

package javax.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}

被@PreDestroy修飾的方法會在服務器卸載Servlet的時候運行,並且只會被服務器調用一次,類似於Servlet的destroy()方法。被@PreDestroy修飾的方法會在destroy()方法之後運行,在Servlet被徹底卸載之前。執行順序如下所示。

調用destroy()方法->@PreDestroy->destroy()方法->bean銷毀。

總結:@PostConstruct,@PreDestroy是Java規範JSR-250引入的註解,定義了對象的創建和銷毀工作,同一期規範中還有註解@Resource,Spring也支持了這些註解。

案例程序

對@PostConstruct註解和@PreDestroy註解有了簡單的了解之後,接下來,我們就寫一個簡單的程序來加深對這兩個註解的理解。

我們創建一個Cat類,如下所示。

package io.mykit.spring.plugins.register.bean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試@PostConstruct註解和@PreDestroy註解
 */
public class Cat {

    public Cat(){
        System.out.println("Cat類的構造方法...");
    }

    public void init(){
        System.out.println("Cat的init()方法...");
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("Cat的postConstruct()方法...");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("Cat的preDestroy()方法...");
    }

    public void destroy(){
        System.out.println("Cat的destroy()方法...");
    }
}

可以看到,在Cat類中,我們提供了構造方法,init()方法、destroy()方法,使用 @PostConstruct註解標註的postConstruct()方法和只用@PreDestroy註解標註的preDestroy()方法。接下來,我們在AnimalConfig類中使用@Bean註解將Cat類註冊到Spring容器中,如下所示。

@Bean(initMethod = "init", destroyMethod = "destroy")
public Cat cat(){
    return new Cat();
}

接下來,在BeanLifeCircleTest類中新建testBeanLifeCircle04()方法進行測試,如下所示。

@Test
public void testBeanLifeCircle04(){
    //創建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
    //關閉IOC容器
    context.close();
}

運行BeanLifeCircleTest類中的testBeanLifeCircle04()方法,輸出的結果信息如下所示。

Cat類的構造方法...
Cat的postConstruct()方法...
Cat的init()方法...
Cat的preDestroy()方法...
Cat的destroy()方法...

從輸出的結果信息中,可以看出執行的順序是: 構造方法 -> @PostConstruct -> init()方法 -> @PreDestroy -> destroy()方法。

好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

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

【其他文章推薦】

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

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

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

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

※超省錢租車方案

Redis SDS 深入一點,看到更多!

1、什麼是SDS?

Redis 自定的字符串存儲結構,關於redis,你需要了解的幾點!中我們對此有過簡要說明。

Redis 底層是用C語言編寫的,可是在字符存儲上,並未使用C原生的String類型,而是定義了自己的字符串結構 Simple Dynamic Stirng,簡稱SDS。

SDS基本結構如下:

struct sdshdr {
int len; // 記錄buf數組中已使用字節的數量,等於SDS所保存字符串的長度
int free; // 記錄buf數組中未使用字節的數量
char buf[];// 字節數組,用於保存字符串
};

例如,字符串“Redis”存儲示意圖為:

圖1

當前存儲字符串長度為5,未使用長度為0,字節數組存儲的字符為“Redis\0”。

這裏需要注意的是:內部數據數組存儲字符串形式符合C語言要求,以‘\0’結尾。且len字符串長度不包含結尾標識符‘\0’。

buf[]的這種遵循C語言形式的存儲,使得Redis可以直接使用C語言的相關字符串函數進行SDS對象的操作。

二、SDS的優勢

1、O(1)時間複雜度獲取字符串長度

SDS內部維護着一個字符串長度的len變量,可以直接讀取,時間複雜度為O(1)。

對於傳統的C字符串:字符+“\0”,想要獲取字符長度,則需要遍歷整個字符串,直到遇到結束字符,時間複雜度為O(n)。

2、緩衝區溢出規避

所謂緩衝區溢出即所需要的內存超出了實際的內存。因此對於C字符串來說,要特別注意內存分配,回收使用問題。

比如,向一個現有字符串內添加特定字符時,需要保證當前已經分配了這足夠的內存。 

圖2

與C不同的是,SDS的空間預分配策略可以避免緩衝區溢出發生,

當需要對SDS進行操作時,首先會檢查當前空間是否滿足需求,不足則擴展當前分配空間。內存檢查相對於C變成了內部預置操作。

3、減少內存重分配次數

上面我們講到過,C字符操作前都需要進行內存的分配操作,同時,操作完成后,也需要進行相應的內存回收操作。一次操作至少涉及一次內存分配操作。

大家都知道內存的重分配是一個比較複雜且需精細控制的過程,耗時耗資源。針對此弊端,Redis 在SDS內存配置策略上採用了空間預分配+惰性刪除相結合的策略。

a)空間預分配:

空間預分配用於優化SDS字符擴展操作。

所謂預分配,也即是說在一次擴展操作中,擴展的空間大小會大於實際需要的空間大小。
如下,圖1執行圖2操作后SDS變更為:

圖3 

預分配空間的大小基於以下規則計算:

SDS len<1M:分配len長度空間作為預分配空間;

SDS len>=1M:分配1M空間作為預分配空間;

這樣,在下次進行字符操作的時候,如果所需要的空間小於當前SDS free空間,則可以直接行操作,而不需要再執行內存擴展,重分配操作。

SDS的預分配機制使得一次擴展操作所需的內存重分配次數變為<=1。

b)惰性刪除機制

所謂惰性刪除,即調整刪除SDS中部分數據時,不會立刻執行內存重分配,而是會保留空出來內存,並更新內部free屬性。以備將來有字符擴展需求,可以直接使用。

當然,Redis也提供了主動釋放未使用內存的方法。

如下,刪除“ent”之後的SDS結構:

 圖4

SDS的內存分配機制,尤其對於以寫為主的應用場景,能夠提供更加優異的性能表現。

3、二進制安全

C字符串由於特殊的編碼要求只能保存文本數據。

SDS相關的功能方法會以二進制的形式來操作SDS存儲的數據,沒有任何中間操作,存儲最原始的數據,因此不會有字符層面的因素影響。

SDS可以保存任何源的二進制數據,字符、圖片、文件或者序列化的對象等等。

 

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

正則匹配中的非貪婪匹配不是最短匹配

最近在工作中遇到一個需求,就是找出html中所有錨文字包含 聯繫方式 的超鏈接。剛開始我寫了一個很簡單的正則來解決這個問題<a.*?聯繫方式.*?</a。但是在測試的時候卻發現這個正則表達式並不像我想象的那樣工作。

圖中給出了一個正則表達式匹配的例子,可以看出在這段文字中有兩個匹配,但是第一個匹配所包含的結果已經超出了實際需要的範圍,包含了太多的超鏈接標籤,而我需要的是最短的匹配也就是圖中橫線畫出的範圍,這是怎麼回事?

正則匹配的原理

這要從正則匹配的原理說起,簡單的來說正則匹配是一種貪心的算法。它總是先找到第一個匹配的位置,然後向後繼續匹配其他的表達式符號。對於本文給出的正則表達式,會現在html中找到一個<a標籤,然後之後是.*?,直到找到一個聯繫方式,在這個過程中如果找到了另一個<a,會被當作.*?匹配的部分,而忘記了要匹配表達式的開頭就是<a。也就是說,除非發生失配,正則表達式不會主動地回溯。儘管使用了?來表達非貪婪匹配,也只能限制向後匹配時盡可能地短,而不能縮短已匹配部分的長度。也就是說,非貪婪匹配向後是最短匹配,但是向前不是最短匹配。

對於這個任務,我後來使用了其他效率更高的方法實現了,但是有沒有可能使用正則表達式來完成這個任務呢?

零寬斷言

零寬斷言是一種零寬度的匹配,它匹配到的內容不會保存到匹配結果中去,最終匹配結果只是一個位置而已。
作用是給指定位置添加一個限定條件,用來規定此位置之前或者之後的字符必須滿足限定條件才能使正則中的字表達式匹配成功。

零寬斷言總共有四種

對於這個需求,實際上應該找到離聯繫方式最近的一個<a,也就是說,在<a到聯繫方式之前不能再有其他的<a了。而最開始的正則匹配表達式<a.*?聯繫方式.*?</a中的.*?可以通過.來匹配任意一個字符,在這裏可以使用零寬度負先行斷言來限制.匹配的任何一個字符的右側不能夠再有<a。也就是.(?!<a),再將這個整體重複多次(.(?!<a))*?。這裏引入了一個額外的括號,為了不產生多餘的匹配,可以使用非捕獲組來去除不需要的匹配,最終可以將整個表達式寫成<a(:?.(?!<a))*?聯繫方式.*?</a。

可以看到匹配的範圍已經縮小到最後一個出現的超鏈接。

總結

因為正則表達式實現原理的限制,儘管選擇非貪婪匹配,匹配到的結果也不一定是最短的匹配。
通常正則表達式總是表明了“要匹配什麼”,而通過零寬度負斷言,則可以表明“不匹配什麼”,這比字符集中使用^來取反更加強大。

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

用Python進行實時計算——PyFlink快速入門

Flink 1.9.0及更高版本支持Python,也就是PyFlink。

在最新版本的Flink 1.10中,PyFlink支持Python用戶定義的函數,使您能夠在Table API和SQL中註冊和使用這些函數。但是,聽完所有這些后,您可能仍然想知道PyFlink的架構到底是什麼?作為PyFlink的快速指南,本文將回答這些問題。

為什麼需要PyFlink?

Python上的Flink和Flink上的Python

那麼,PyFlink到底是什麼?顧名思義,PyFlink就是Apache Flink與Python的組合,或者說是Python上的Flink。但是Flink on Python是什麼意思?首先,兩者的結合意味着您可以在Python中使用Flink的所有功能。而且,更重要的是,PyFlink還允許您在Flink上使用Python廣泛的生態系統的計算功能,從而可以進一步促進其生態系統的開發。換句話說,這對雙方都是雙贏。如果您更深入地研究這個主題,您會發現Flink框架和Python語言的集成絕不是巧合。

Python和大數據生態系統

python語言與大數據緊密相連。為了理解這一點,我們可以看一下人們正在使用Python解決的一些實際問題。一項用戶調查显示,大多數人都在使用Python進行數據分析和機器學習應用程序。對於此類情況,大數據空間中還解決了一些理想的解決方案。除了擴大大數據產品的受眾範圍之外,Python和大數據的集成還通過將其獨立體繫結構擴展到分佈式體繫結構,極大地增強了Python生態系統的功能。這也解釋了在分析大量數據時對Python的強烈需求。

為什麼選擇Flink和Python?

Python和大數據的集成與其他最近的趨勢一致。但是,再次說明一下,為什麼Flink現在支持Python,而不是Go或R或另一種語言?而且,為什麼大多數用戶選擇PyFlink而不是PySpark和PyHive?

為了理解原因,讓我們首先考慮使用Flink框架的一些優勢:

  • 有利的體繫結構: Flink是具有統一流和批處理功能的純流計算引擎。
  • 新的活力:根據ASF的客觀統計,Flink是2019年最活躍的開源項目。
  • 高可靠性:作為一個開源項目,Flink經過長期測試,並廣泛應用於大數據公司的生產環境中。

接下來,讓我們看看為什麼Flink支持Python而不是其他語言。統計數據显示,Python是繼Java和C之後最受歡迎的語言,並且自2018年以來一直在快速發展。Java和Scala是Flink的默認語言,但是Flink支持Python似乎是合理的。

PyFlink是相關技術發展的必然產物。但是,僅僅了解PyFlink的重要性是不夠的,因為我們的最終目標是使Flink和Python用戶受益並解決實際問題。因此,我們需要進一步探索如何實現PyFlink。

PyFlink架構

要實現PyFlink,我們需要知道要實現的關鍵目標和要解決的核心問題。PyFlink的主要目標是什麼?簡而言之,PyFlink的主要目標如下:

  1. 使所有Flink功能對Python用戶可用。
  2. 在Flink上運行Python的分析和計算功能,以提高Python解決大數據問題的能力。

在此基礎上,讓我們分析實現這些目標需要解決的關鍵問題。

使Flink功能可供Python用戶使用

要實現PyFlink,是否需要像現有Java引擎一樣在Flink上開發Python引擎?答案是NO。嘗試在Flink 1.8版或更早版本中進行,但效果不佳。基本設計原則是以最小的成本實現給定的目標。最簡單但最好的方法是提供一層Python API,並重用現有的計算引擎。

那麼,我們應該為Flink提供哪些Python API?他們對我們很熟悉:高級表API和SQL,以及有狀態的DataStream API。現在,我們越來越接近Flink的內部邏輯,下一步是提供適用於Python的Table API和DataStream API。但是,剩下要解決的關鍵問題到底是什麼呢?

關鍵問題

顯然,關鍵問題在於在Python虛擬機(PyVM)和Java虛擬機(JVM)之間建立握手,這對於Flink支持多種語言至關重要。要解決此問題,我們必須選擇適當的通信技術。

選擇虛擬機通信技術

當前,有兩種解決方案可用於實現PyVM和JVM之間的通信,它們是Beam和Py4J。前者是一個著名的項目,具有多語言和多引擎支持,而後者是用於PyVM和JVM之間通信的專用解決方案。我們可以從幾個不同的角度比較和對比Apache Beam和Py4J,以了解它們之間的區別。首先,考慮一個比喻:要越過一堵牆,Py4J會像痣一樣在其中挖一個洞,而Apache Beam會像大熊一樣把整堵牆推倒。從這個角度來看,使用Apache Beam來實現VM通信有點複雜。簡而言之,這是因為Apache Beam專註於通用性,在極端情況下缺乏靈活性。

除此之外,Flink還需要交互式編程。此外,為了使Flink正常工作,我們還需要確保其API設計中的語義一致性,尤其是在其多語言支持方面。Apache Beam的現有體繫結構無法滿足這些要求,因此答案很明顯,Py4J是支持PyVM和JVM之間通信的最佳選擇。

技術架構

在PyVM和JVM之間建立通信之後,我們已經實現了向Python用戶提供Flink功能的第一個目標。我們已經在Flink 1.9版中實現了這一點。現在,讓我們看一下Flink 1.9版中PyFlink API的體繫結構:

Flink 1.9版使用Py4J來實現虛擬機通信。我們為PyVM啟用了網關,為JVM啟用了網關服務器以接收Python請求。此外,我們還提供了Python API中的TableENV和Table之類的對象,這些對象與Java API中提供的對象相同。因此,編寫Python API的本質是關於如何調用Java API。Flink 1.9版還解決了作業部署問題。它使您可以通過各種方式提交作業,例如運行Python命令以及使用Python Shell和CLI。

但是,此體繫結構提供了哪些優勢?首先,該體繫結構很簡單,並且可以確保Python API和Java API之間的語義一致性。其次,它還提供了與Java作業相當的出色Python作業處理性能。

在Flink上運行Python的分析和計算功能

上一節介紹了如何使Flink功能可供Python用戶使用。本節說明如何在Flink上運行Python函數。通常,我們可以通過以下兩種方式之一在Flink上運行Python函數:

  1. 選擇一個典型的Python類庫,並將其API添加到PyFlink。該方法花費很長時間,因為Python包含太多的類庫。在合併任何API之前,我們需要簡化Python執行。
  2. 基於現有的Flink Table API和Python類庫的特徵,我們可以將所有現有的Python類庫函數視為用戶定義的函數,並將其集成到Flink中。Flink 1.10及更高版本中支持此功能。功能集成的關鍵問題是什麼?同樣,它取決於Python用戶定義函數的執行。

接下來,讓我們為這個關鍵問題選擇一種技術。

選擇執行用戶定義功能的技術

實際上,執行Python用戶定義的函數非常複雜。它不僅涉及虛擬機之間的通信,還涉及以下所有方面:管理Python執行環境,解析Java和Python之間交換的業務數據,將Flink中的狀態後端傳遞給Python以及監視執行狀態。鑒於所有這些複雜性,現在是Apache Beam發揮作用的時候了。作為支持多種引擎和多種語言的大熊,Apache Beam可以在解決這種情況方面做很多工作,所以讓我們看看Apache Beam如何處理執行Python用戶定義的函數。

下面显示了可移植性框架,該框架是Apache Beam的高度抽象的體繫結構,旨在支持多種語言和引擎。當前,Apache Beam支持幾種不同的語言,包括Java,Go和Python。

用戶定義的功能架構

UDF體繫結構不僅需要實現PyVM與JVM之間的通信,還需要在編譯和運行階段滿足不同的要求。在下面的PyLink用戶定義功能架構圖中,JVM中的行為以綠色表示,而PyVM中的行為以藍色表示。讓我們看看編譯期間的局部設計。本地設計依賴於純API映射調用。Py4J用於VM通信。

現在,讓我們看看Python API和Java API在此架構中的工作方式。在Java方面,JobMaster將作業分配給TaskManager,就像處理普通Java作業一樣,並且TaskManager執行任務,這涉及到操作員在JVM和PyVM中的執行。在Python用戶定義的函數運算符中,我們將設計各種gRPC服務,用於JVM和PyVM之間的通信。例如,用於業務數據通信的DataService和用於Python UDF的StateService來調用Java State後端。還將提供許多其他服務,例如日誌記錄和指標。

我們如何使用PyFlink?

了解了PyFlink的體繫結構及其背後的思想之後,我們來看一下PyFlink的特定應用場景,以更好地了解其背後的方式和原因。

PyFlink的應用場景

PyFlink支持哪些業務方案?我們可以從兩個角度分析其應用場景:Python和Java。請記住,PyFlink也適用於Java可以應用的所有情況。

  1. 事件驅動的方案,例如實時數據監控。
  2. 數據分析,例如庫存管理和數據可視化。
  3. 數據管道,也稱為ETL方案,例如日誌解析。
  4. 機器學習,例如有針對性的建議。

您可以在所有這些情況下使用PyFlink。PyFlink也適用於特定於Python的方案,例如科學計算。在如此眾多的應用場景中,您可能想知道現在可以使用哪些特定的PyFlink API。因此,現在我們也來研究這個問題。

PyFlink安裝

在使用任何API之前,您需要安裝PyFlink。當前,要安裝PyFlink,請運行命令:pip install apache-Flink

PyFlink API

PyFlink API與Java Table API完全一致,以支持各種關係和窗口操作。某些易於使用的PyFlink API比SQL API更為強大,例如特定於列操作的API。除了API,PyFlink還提供了多種定義Python UDF的方法。

PyFlink中用戶定義的函數定義

可以擴展ScalarFunction(例如,通過添加指標)以提供更多輔助功能。另外,PyFlink用戶功能函數支持Python支持的所有方法定義,例如lambda,命名函數和可調用函數。

定義完這些方法后,我們可以使用PyFlink Decorators進行標記,並描述輸入和輸出數據類型。我們還可以基於Python的類型提示功能進一步簡化更高版本,以進行類型派生。以下示例將幫助您更好地了解如何定義用戶定義的函數。

定義Python用戶定義函數的一種情況

在本例中,我們將兩個数字相加。首先,為此,導入必要的類,然後定義前面提到的函數。這非常簡單,因此讓我們進行一個實際案例。

PyFlink的未來前景如何?

通常,使用PyFlink進行業務開發很簡單。您可以通過SQL或Table API輕鬆描述業務邏輯,而無需了解基礎實現。讓我們看一下PyFlink的整體前景。

目標驅動路線圖

PyFlink的開發始終受到目標的推動,這些目標是使Flink功能可供Python用戶使用並將Python函數集成到Flink中。根據下面显示的PyFlink路線圖,我們首先在PyVM和JVM之間建立了通信。然後,在Flink 1.9中,我們提供了Python Table API,向Python用戶開放了現有的Flink Table API功能。在Flink 1.10中,我們準備通過以下操作將Python函數集成到Flink:集成Apache Beam,設置Python用戶定義的函數執行環境,管理Python對其他類庫的依賴關係以及為用戶定義用戶定義的函數API,以便支持Python用戶定義函數。

為了擴展分佈式Python的功能,PyFlink提供了對Pandas Series和DataFrame支持,以便用戶可以在PyFlink中直接使用Pandas用戶定義的函數。此外,將來會在SQL客戶端上啟用Python用戶定義函數,以使PyFlink易於使用。PyFlink還將提供Python ML管道API,以使Python用戶能夠在機器學習中使用PyFlink。監視Python用戶定義的函數執行對實際生產和業務至關重要。因此,PyFlink將進一步為Python用戶定義函數提供度量管理。這些功能將包含在Flink 1.11中。

但是,這些只是PyFlink未來發展計劃的一部分。還有更多工作要做,例如優化PyFlink的性能,提供圖形計算API以及為Flink上的Pandas支持Pandas的本機API。我們將繼續向Python用戶提供Flink的現有功能,並將Python的強大功能集成到Flink中,以實現擴展Python生態系統的最初目標。

PyFlink的前景如何?您可能知道,PyFlink是Apache Flink的一部分,它涉及運行時和API層。

PyFlink在這兩層將如何發展?在運行時方面,PyFlink將構建用於JVM和PyVM之間通信的gRPC常規服務(例如控件,數據和狀態)。在此框架中,將抽象化Java Python用戶定義函數運算符,並構建Python執行容器以支持Python的多種執行方式。例如,PyFlink可以在Docker容器中甚至在外部服務集群中作為進程運行。特別是在外部服務群集中運行時,將以套接字的形式啟用無限擴展功能。這一切在後續的Python集成中都起着至關重要的作用。

在API方面,我們將在Flink中啟用基於Python的API,以實現我們的使命。這也依賴於Py4J VM通信框架。PyFlink將逐漸支持更多的API,包括Flink中的Java API(例如Python Table API,UDX,ML Pipeline,DataStream,CEP,Gelly和State API)以及在Python用戶中最受歡迎的Pandas API。基於這些API,PyFlink將繼續與其他生態系統集成以便於開發;例如Notebook,Zeppelin,Jupyter和Alink,這是阿里巴巴的Flink開源版本。到目前為止,PyAlink已完全整合了PyFlink的功能。PyFlink也將與現有的AI系統平台集成,例如著名的TensorFlow。

為此,PyFlink將一直保持活力。同樣,PyFlink的任務是使Flink功能可供Python用戶使用,並在Flink上運行Python分析和計算功能。

更多實時數據分析相關博文與科技資訊,歡迎關注 “實時流式計算”
關注 “實時流式計算” 回復 “电子書” 獲取Flink 300頁實戰电子書

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

比亞迪拉丁美洲第一座電動車工廠誕生 投27.1億定位巴西

比亞迪股份有限公司將投資2億巴西雷亞爾(約合新臺幣27.1億元)在巴西投資建廠。這家工廠將落戶聖保羅州坎皮納市(Campinas),是比亞迪在拉丁美洲的第一座工廠。   比亞迪巴西工廠的產品是電動公車、電池和太陽能板。今年1月,比亞迪曾表示其年電動公車最大產能將達到4000輛。其生產的電動公車可能在巴西和拉美地區銷售。

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

【其他文章推薦】

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

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

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

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

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

2014第三屆混合動力技術國際峰會 會後報告

6月26日,以“技術驅動混合動力市場化”為主題的2014第三屆混合動力技術國際峰會開幕。本屆峰會由Merisis Consulting舉辦,超過150位來自政府部門,科研機構,整車,一級和二級的核心零部件供應商的高層代表們齊聚一堂,共議混合動力汽車的技術路線和發展。

乘用車市場方面,目前的市場熱度雖然中等,可以預見在不久以後將會迎來良好的發展勢頭。四部委聯合發佈的《關於繼續開展新能源汽車推廣應用工作的通知》中明確了PHEV的補貼政策,上海廣州北京各地政府也相繼推出了對於PHEV和燃料電池汽車的大力度補貼條例,這將在未來1~2年內對HEV的銷量和市場接受度產生一定的推動力度。而技術相較成熟的商用車市場,大力度的政策補貼,也驅使整車廠們在不斷追求規模的基礎上追求更低成本,最優化的技術。

2014第三屆混合動力技術國際峰會為汽車業界關注混合動力和新能源汽車發展的同仁們提供了一手資訊,説明他們更好的判斷行業未來走向,學習借鑒前沿的經驗與技術,並更好的推動混合動力汽車的研發和市場化進程。

在兩天全程的演講中,嘉賓們從各自領域為在場觀眾分享了行業內前瞻資訊和專業知識。以上總結部分讓筆者印象深刻並值得行業人士深入思考的觀點。

國家發改委耿志成老師在報告中明確國家主要扶持的發展方向有三方面:電動車(包括PHEV),燃料電池汽車,HEV電池控制系統。那麼汽車工業尤其是新能源汽車的未來是靠市場還是靠政策,這一圍繞人們心中許久的問題也許仍然有待時日來解答。

真鋰研究聶昕老師未對目前中國電池行業發展做過於資料性的分析,而是提出未來電池技術的瓶頸將不再是問題。從今年正極材料已經發展到五氟級,以及液體電解質發展到固態電解質,性能大幅度提升等方面可以預見未來無限可能性。同時,也認為目前電池領域存在的主要將圍繞BMS以及一致性問題展開。

第一天上午的小組討論環節中上汽集團商用車技術中心蘭志波總工分享了他個人的獨到觀點,他認為目前HEV存在的成本問題還是基於用戶需求挖掘不夠,用戶並非一味的追求低價格,真正有特點的產品仍然能夠打動消費者。汽車不論技術如何發展,消費者還是講駕駛體驗。

來自吉孚動力的Sven Steinwascher先生分享了混合動力變速箱方面的最新技術挑戰,從客戶接受度到換擋品質,以及變速箱的整合可能性。在演講中他提出了變速箱混合的概念並對其動力總成的佈局進行了詳盡分析。從而最終達到了良好的駕駛性能,經濟型和低成本。

法國Energies Nouvelles的Sébastien MAGAND先生為我們分享了混合動力傳動技術方面的海外領先經驗。IFPEN的傳動系統有11個模式,低於50kg的輕量化設計,有低於300mm長度的無離合器版本,以及模組化的電動汽車整合及比率。優勢在於大量節油,設計輕巧,卓越駕駛性能以及成本最優化。

第二天下午商用車專場中來自五洲龍的全頌華先生細緻深入的為大家講解了公司技術的發展,關鍵零部件的發展以及車配套設施的發展。公司緊跟國家政策,參與了2009年商業示範運行項目並利用2011年世界大學生運動會實現了真正的市場化和批量化。

一位演講者曾經這麼說到,在混合動力越走越看不到前途的過程中,松正的技術突然讓我感覺又有了希望。天津松正的李喬博士在本次論壇上圍繞第IV代插電式深混公車動力系統關鍵技術及應用展開的詳細的解析。觀點很鮮明:技術應以使用者為導向,使用者關心價格又希望避免插電的繁瑣過程。松正的技術可以同時滿足客戶的這兩點需求。

宇通客車主管新能源產品的胡俊傑經理提出混合動力才是未來公交的發展方向。配置可更改、功能可定制的動力系統技術平臺,大力自主研發電機電控電源技術、整車和發動機節能技術和輕量化技術。作為新能源客車推廣應用的領頭企業,他認為制約推廣的瓶頸在於電源和充電技術兩方面,電池的成本和壽命,充電標準、設施、模式包括技術開發。

在聆聽了插電式、混合動力之後,東風揚子江客車雷洪鈞開始了他純電動客車的精彩報告。他認為電動車,混合動力,插電式三者存在都有它的意義,細分市場和技術方向。混合動力中“混合”二字的定義有待深究,混合的普遍解釋是2種以上能源作為動力並存。混合僅僅是油電混合,氣電混合或者也應將燃料汽車加入其中。如果新能源汽車的最終目標是節能減排,那麼他的觀點是減排肯定排第一位,然後才是節能。而我們所談論的重混,中混&微混根本是同一條技術路線,只是發展程度和時間問題。

衷心感謝中國公交資訊網、電動汽車網、高工鋰電網、新華信汽車網等十餘家媒體合作夥伴對論壇進行的全程的報導。我們將在來年呈現更精彩豐富的會議內容,也期待行業內合作夥伴的參與和支持。

更多混合動力行業最新動態、趨勢、資料、人脈資訊,可關注搜索微信服務號“merisis-asia”,掃描以下二維碼關注我們的微信互動平臺

網站:

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

【其他文章推薦】

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

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

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

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

※超省錢租車方案

砸數十億歐元!BMW加碼購三星SDI電池 衝電動車市場

BMW15日表示,計劃為i3電動都市車、插電式混合動力的i8跑車與未來數年新推出的混合動力車款擴大採購SDI電池。三星SDI發表聲明說,與BMW的供應協議價值「數十億歐元」,但未提確切金額。   BMW正推出i8與i3車款來對抗電動車大廠Tesla搶攻豪華車領域,上述交易讓BMW能確保汽車電池的供應來源。對三星SDI來說,BMW的訂單有助擴大汽車電池業務。

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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