范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Go优雅退出实现方法ampampampcontext原理

  1.通过endless包实现package main  import (     "fmt"     "log"     "syscall"      "github.com/fvbock/endless"      "gin-blog/routers"     "gin-blog/pkg/setting" )  func main() {     endless.DefaultReadTimeOut = setting.ReadTimeout     endless.DefaultWriteTimeOut = setting.WriteTimeout     endless.DefaultMaxHeaderBytes = 1 << 20     endPoint := fmt.Sprintf(":%d", setting.HTTPPort)      server := endless.NewServer(endPoint, routers.InitRouter())     server.BeforeBegin = func(add string) {         log.Printf("Actual pid is %d", syscall.Getpid())     }      err := server.ListenAndServe()     if err != nil {         log.Printf("Server err: %v", err)     } }
  2.通过shutdown实现
  在go 1.8.x后,golang在http里加入了shutdown方法,用来控制优雅退出。什么是优雅退出? 简单说就是不处理新请求,但是会处理正在进行的请求,把旧请求都处理完,也就是都response之后,那么就退出。
  shutdown通过context上下文实现。
  社区里不少http graceful动态重启,平滑重启的库,大多是基于http.shutdown做的。平滑启动的原理很简单,fork子进程,继承listen fd, 老进程优雅退出。 package main  import (     "fmt"     "net/http"     "context"     "log"     "os"     "os/signal"     "time"       "gin-blog/routers"     "gin-blog/pkg/setting" )  func main() {     router := routers.InitRouter()      s := &http.Server{         Addr:           fmt.Sprintf(":%d", setting.HTTPPort),         Handler:        router,         ReadTimeout:    setting.ReadTimeout,         WriteTimeout:   setting.WriteTimeout,         MaxHeaderBytes: 1 << 20,     }      go func() {         if err := s.ListenAndServe(); err != nil {             log.Printf("Listen: %s ", err)         }     }()      quit := make(chan os.Signal)     signal.Notify(quit, os.Interrupt)     <- quit      log.Println("Shutdown Server ...")      ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)     defer cancel()     if err := s.Shutdown(ctx); err != nil {         log.Fatal("Server Shutdown:", err)     }      log.Println("Server exiting") }
  3.context原理
  context  是 Go  并发编程中常用到一种编程模式。 为什么需要context
  在并发程序中,由于超时、取消操作或者一些异常情况,往往需要进行抢占操作或者中断后续操作。熟悉 channel  的朋友应该都见过使用 done channel  来处理此类问题。比如以下这个例子: func main() {     messages := make(chan int, 10)     done := make(chan bool)      defer close(messages)     // consumer     go func() {         ticker := time.NewTicker(1 * time.Second)         for _ = range ticker.C {             select {             case <-done:                 fmt.Println("child process interrupt...")                 return             default:                 fmt.Printf("send message: %d ", <-messages)             }         }     }()      // producer     for i := 0; i < 10; i++ {         messages <- i     }     time.Sleep(5 * time.Second)     close(done)//往done通道发送关闭信号     time.Sleep(1 * time.Second)     fmt.Println("main process exit!") }
  上述例子中定义了一个 buffer  为0的 channel done  , 子协程运行着定时任务。如果主协程需要在某个时刻发送消息通知子协程中断任务退出,那么就可以让子协程监听这个 done channel  ,一旦主协程关闭 done channel  ,那么子协程就可以推出了,这样就实现了主协程通知子协程的需求。这很好,但是这也是有限的。
  如果我们可以在简单的通知上附加传递额外的信息来控制取消:为什么取消,或者有一个它必须要完成的最终期限,更或者有多个取消选项,我们需要根据额外的信息来判断选择执行哪个取消选项。
  考虑下面这种情况:假如主协程中有多个任务1, 2, …m,主协程对这些任务有超时控制;而其中任务1又有多个子任务1, 2, …n,任务1对这些子任务也有自己的超时控制,那么这些子任务既要感知主协程的取消信号,也需要感知任务1的取消信号。
  如果还是使用 done channel  的用法,我们需要定义两个 done channel  ,子任务们需要同时监听这两个 done channel  。嗯,这样其实好像也还行哈。但是如果层级更深,如果这些子任务还有子任务,那么使用 done channel  的方式将会变得非常繁琐且混乱。
  我们需要一种优雅的方案来实现这样一种机制: 上层任务取消后,所有的下层任务都会被取消; 中间某一层的任务取消后,只会将当前任务的下层任务取消,而不会影响上层的任务以及同级任务。
  这个时候 context  就派上用场了。
  我们首先看看 context  的结构设计和实现原理。 context接口
  先看 Context  接口结构,看起来非常简单。 type Context interface {      Deadline() (deadline time.Time, ok bool)      Done() <-chan struct{}      Err() error      Value(key interface{}) interface{} }
  Context  接口包含四个方法: Deadline  返回绑定当前 context  的任务被取消的截止时间;如果没有设定期限,将返回 ok == false  。 Done   当绑定当前 context  的任务被取消时,将返回一个关闭的 channel  ;如果当前 context  不会被取消,将返回 nil  。 Err   如果 Done  返回的 channel  没有关闭,将返回 nil  ;如果 Done  返回的 channel  已经关闭,将返回非空的值表示任务结束的原因。如果是 context  被取消, Err  将返回 Canceled  ;如果是 context  超时, Err  将返回 DeadlineExceeded  。 Value   返回 context  存储的键值对中当前 key  对应的值,如果没有对应的 key  ,则返回 nil  。
  可以看到 Done  方法返回的 channel  正是用来传递结束信号以抢占并中断当前任务; Deadline  方法指示一段时间后当前 goroutine  是否会被取消;以及一个 Err  方法,来解释 goroutine  被取消的原因;而 Value  则用于获取特定于当前任务树的额外信息。而 context  所包含的额外信息键值对是如何存储的呢?其实可以想象一颗树,树的每个节点可能携带一组键值对,如果当前节点上无法找到 key  所对应的值,就会向上去父节点里找,直到根节点,具体后面会说到。
  emptyCtx
  emptyCtx  是一个 int  类型的变量,但实现了 context  的接口。 emptyCtx  没有超时时间,不能取消,也不能存储任何额外信息,所以 emptyCtx  用来作为 context  树的根节点。 // An emptyCtx is never canceled, has no values, and has no deadline. It is not // struct{}, since vars of this type must have distinct addresses. type emptyCtx int  func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {     return }  func (*emptyCtx) Done() <-chan struct{} {     return nil }  func (*emptyCtx) Err() error {     return nil }  func (*emptyCtx) Value(key interface{}) interface{} {     return nil }  func (e *emptyCtx) String() string {     switch e {     case background:         return "context.Background"     case todo:         return "context.TODO"     }     return "unknown empty Context" }  var (     background = new(emptyCtx)     todo       = new(emptyCtx) )  func Background() Context {     return background }  func TODO() Context {     return todo }
  但我们一般不会直接使用 emptyCtx  ,而是使用由 emptyCtx  实例化的两个变量,分别可以通过调用 Background  和 TODO  方法得到,但这两个 context  在实现上是一样的。那么 Background  和 TODO  方法得到的 context  有什么区别呢?可以看一下官方的解释: // Background 返回一个非零的空 Context。它永远不会被取消,没有 // 值,并且没有截止日期。它通常由主函数使用。 // 初始化和测试中使用,并作为最高级别的Context用于传入的 // 请求的顶层上下文。  // TODO返回一个非零的空的Context。代码应该在以下情况下使用context.TODO // 不清楚应该使用哪个Context,或者它还不可用(因为 // 因为周围的函数还没有被扩展到可以接受一个Context // 参数)。
  Background  和 TODO  只是用于不同场景下:
  Background  通常被用于主函数、初始化以及测试中,作为一个顶层的 context  ,也就是说一般我们创建的 context  都是基于 Background  ;
  而 TODO  是在不确定使用什么 context  的时候才会使用。
  下面将介绍两种不同功能的基础 context  类型: valueCtx  和 cancelCtx  。 valueCtxvalueCtx结构体type valueCtx struct {     Context     key, val interface{} }  func (c *valueCtx) Value(key interface{}) interface{} {     if c.key == key {         return c.val     }     return c.Context.Value(key) }
  valueCtx  利用一个 Context  类型的变量来表示父节点 context  ,所以当前 context  继承了父 context  的所有信息; valueCtx  类型还携带一组键值对,也就是说这种 context  可以携带额外的信息。 valueCtx  实现了 Value  方法,用以在 context  链路上获取 key  对应的值,如果当前 context  上不存在需要的 key  ,会沿着 context  链向上寻找 key  对应的值,直到根节点。 WithValue
  WithValue  用以向 context  添加键值对: func WithValue(parent Context, key, val interface{}) Context {     if key == nil {         panic("nil key")     }     if !reflect.TypeOf(key).Comparable() {         panic("key is not comparable")     }     return &valueCtx{parent, key, val} }
  这里添加键值对不是在原 context  结构体上直接添加,而是以此 context  作为父节点,重新创建一个新的 valueCtx  子节点,将键值对添加在子节点上,由此形成一条 context  链。获取 value  的过程就是在这条 context  链上由尾部上前搜寻:
  cancelCtxcancelCtx结构体type cancelCtx struct {     Context      mu       sync.Mutex            // protects following fields     done     chan struct{}         // created lazily, closed by first cancel call     children map[canceler]struct{} // set to nil by the first cancel call     err      error                 // set to non-nil by the first cancel call }  type canceler interface {     cancel(removeFromParent bool, err error)     Done() <-chan struct{} }
  跟 valueCtx  类似, cancelCtx  中也有一个 context  变量作为父节点;变量 done  表示一个 channel  ,用来表示传递关闭信号; children  表示一个 map  ,存储了当前 context  节点下的子节点; err  用于存储错误信息表示任务结束的原因。
  再来看一下 cancelCtx  实现的方法: func (c *cancelCtx) Done() <-chan struct{} {     c.mu.Lock()     if c.done == nil {         c.done = make(chan struct{})     }     d := c.done     c.mu.Unlock()     return d }  func (c *cancelCtx) Err() error {     c.mu.Lock()     err := c.err     c.mu.Unlock()     return err }  func (c *cancelCtx) cancel(removeFromParent bool, err error) {     if err == nil {         panic("context: internal error: missing cancel error")     }     c.mu.Lock()     if c.err != nil {         c.mu.Unlock()         return // already canceled     }     // 设置取消原因     c.err = err     设置一个关闭的channel或者将done channel关闭,用以发送关闭信号     if c.done == nil {         c.done = closedchan     } else {         close(c.done)     }     // 将子节点context依次取消     for child := range c.children {         // NOTE: acquiring the child"s lock while holding parent"s lock.         child.cancel(false, err)     }     c.children = nil     c.mu.Unlock()      if removeFromParent {         // 将当前context节点从父节点上移除         removeChild(c.Context, c)     } }
  可以发现 cancelCtx  类型变量其实也是 canceler  类型,因为 cancelCtx  实现了 canceler  接口。  Done  方法和 Err  方法没必要说了, cancelCtx  类型的 context  在调用 cancel  方法时会设置取消原因,将 done channel  设置为一个关闭 channel  或者关闭 channel  ,然后将子节点 context  依次取消,如果有需要还会将当前节点从父节点上移除。 WithCancel
  WithCancel  函数用来创建一个可取消的 context  ,即 cancelCtx  类型的 context  。 WithCancel  返回一个 context  和一个 CancelFunc  ,调用 CancelFunc  即可触发 cancel  操作。直接看源码: type CancelFunc func()  func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {     c := newCancelCtx(parent)     propagateCancel(parent, &c)     return &c, func() { c.cancel(true, Canceled) } }  // newCancelCtx returns an initialized cancelCtx. func newCancelCtx(parent Context) cancelCtx {     // 将parent作为父节点context生成一个新的子节点     return cancelCtx{Context: parent} }  func propagateCancel(parent Context, child canceler) {     if parent.Done() == nil {         // parent.Done()返回nil表明父节点以上的路径上没有可取消的context         return // parent is never canceled     }     // 获取最近的类型为cancelCtx的祖先节点     if p, ok := parentCancelCtx(parent); ok {         p.mu.Lock()         if p.err != nil {             // parent has already been canceled             child.cancel(false, p.err)         } else {             if p.children == nil {                 p.children = make(map[canceler]struct{})             }             // 将当前子节点加入最近cancelCtx祖先节点的children中             p.children[child] = struct{}{}         }         p.mu.Unlock()     } else {         go func() {             select {             case <-parent.Done():                 child.cancel(false, parent.Err())             case <-child.Done():             }         }()     } }  func parentCancelCtx(parent Context) (*cancelCtx, bool) {     for {         switch c := parent.(type) {         case *cancelCtx:             return c, true         case *timerCtx:             return &c.cancelCtx, true         case *valueCtx:             parent = c.Context         default:             return nil, false         }     } }
  之前说到 cancelCtx  取消时,会将后代节点中所有的 cancelCtx  都取消, propagateCancel  即用来建立当前节点与祖先节点这个取消关联逻辑。 如果 parent.Done()  返回 nil  ,表明父节点以上的路径上没有可取消的 context  ,不需要处理; 如果在 context  链上找到到 cancelCtx  类型的祖先节点,则判断这个祖先节点是否已经取消,如果已经取消就取消当前节点;否则将当前节点加入到祖先节点的 children  列表。 否则开启一个协程,监听 parent.Done()  和 child.Done()  ,一旦 parent.Done()  返回的 channel  关闭,即 context  链中某个祖先节点 context  被取消,则将当前 context  也取消。
  这里或许有个疑问,为什么是祖先节点而不是父节点?这是因为当前 context  链可能是这样的:
  当前 cancelCtx  的父节点 context  并不是一个可取消的 context  ,也就没法记录 children  。 timerCtx
  timerCtx  是一种基于 cancelCtx  的 context  类型,从字面上就能看出,这是一种可以定时取消的 context  。 type timerCtx struct {     cancelCtx     timer *time.Timer // Under cancelCtx.mu.      deadline time.Time }  func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {     return c.deadline, true }  func (c *timerCtx) cancel(removeFromParent bool, err error) {     将内部的cancelCtx取消     c.cancelCtx.cancel(false, err)     if removeFromParent {         // Remove this timerCtx from its parent cancelCtx"s children.         removeChild(c.cancelCtx.Context, c)     }     c.mu.Lock()     if c.timer != nil {         取消计时器         c.timer.Stop()         c.timer = nil     }     c.mu.Unlock() }
  timerCtx  内部使用 cancelCtx  实现取消,另外使用定时器 timer  和过期时间 deadline  实现定时取消的功能。 timerCtx  在调用 cancel  方法,会先将内部的 cancelCtx  取消,如果需要则将自己从 cancelCtx  祖先节点上移除,最后取消计时器。 WithDeadline
  WithDeadline  返回一个基于 parent  的可取消的 context  ,并且其过期时间 deadline  不晚于所设置时间 d  。 func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {     if cur, ok := parent.Deadline(); ok && cur.Before(d) {         // The current deadline is already sooner than the new one.         return WithCancel(parent)     }     c := &timerCtx{         cancelCtx: newCancelCtx(parent),         deadline:  d,     }     // 建立新建context与可取消context祖先节点的取消关联关系     propagateCancel(parent, c)     dur := time.Until(d)     if dur <= 0 {         c.cancel(true, DeadlineExceeded) // deadline has already passed         return c, func() { c.cancel(false, Canceled) }     }     c.mu.Lock()     defer c.mu.Unlock()     if c.err == nil {         c.timer = time.AfterFunc(dur, func() {             c.cancel(true, DeadlineExceeded)         })     }     return c, func() { c.cancel(true, Canceled) } } 如果父节点 parent  有过期时间并且过期时间早于给定时间 d  ,那么新建的子节点 context  无需设置过期时间,使用 WithCancel  创建一个可取消的 context  即可; 否则,就要利用 parent  和过期时间 d  创建一个定时取消的 timerCtx  ,并建立新建 context  与可取消 context  祖先节点的取消关联关系,接下来判断当前时间距离过期时间 d  的时长 dur  : 如果 dur  小于0,即当前已经过了过期时间,则直接取消新建的 timerCtx  ,原因为 DeadlineExceeded  ; 否则,为新建的 timerCtx  设置定时器,一旦到达过期时间即取消当前 timerCtx  。 WithTimeout
  与 WithDeadline  类似, WithTimeout  也是创建一个定时取消的 context  ,只不过 WithDeadline  是接收一个过期时间点,而 WithTimeout  接收一个相对当前时间的过期时长 timeout  : func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {     return WithDeadline(parent, time.Now().Add(timeout)) } context的使用
  首先使用 context  实现文章开头 done channel  的例子来示范一下如何更优雅实现协程间取消信号的同步: func main() {     messages := make(chan int, 10)      // producer     for i := 0; i < 10; i++ {         messages <- i     }      ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)      // consumer     go func(ctx context.Context) {         ticker := time.NewTicker(1 * time.Second)         for _ = range ticker.C {             select {             case <-ctx.Done():                 fmt.Println("child process interrupt...")                 return             default:                 fmt.Printf("send message: %d ", <-messages)             }         }     }(ctx)      defer close(messages)     defer cancel()      select {     case <-ctx.Done():         time.Sleep(1 * time.Second)         fmt.Println("main process exit!")     } }
  这个例子中,只要让子线程监听主线程传入的 ctx  ,一旦 ctx.Done()  返回空 channel  ,子线程即可取消执行任务。但这个例子还无法展现 context  的传递取消信息的强大优势。
  阅读过 net/http  包源码的朋友可能注意到在实现 http server  时就用到了 context  , 下面简单分析一下。
  1、首先 Server  在开启服务时会创建一个 valueCtx  ,存储了 server  的相关信息,之后每建立一条连接就会开启一个协程,并携带此 valueCtx  。 func (srv *Server) Serve(l net.Listener) error {      ...      var tempDelay time.Duration     // how long to sleep on accept failure     baseCtx := context.Background() // base is always background, per Issue 16220     ctx := context.WithValue(baseCtx, ServerContextKey, srv)     for {         rw, e := l.Accept()          ...          tempDelay = 0         c := srv.newConn(rw)         c.setState(c.rwc, StateNew) // before Serve can return         go c.serve(ctx)     } }
  2、建立连接之后会基于传入的 context  创建一个 valueCtx  用于存储本地地址信息,之后在此基础上又创建了一个 cancelCtx  ,然后开始从当前连接中读取网络请求,每当读取到一个请求则会将该 cancelCtx  传入,用以传递取消信号。一旦连接断开,即可发送取消信号,取消所有进行中的网络请求。 func (c *conn) serve(ctx context.Context) {     c.remoteAddr = c.rwc.RemoteAddr().String()     ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())     ...      ctx, cancelCtx := context.WithCancel(ctx)     c.cancelCtx = cancelCtx     defer cancelCtx()      ...      for {         w, err := c.readRequest(ctx)          ...          serverHandler{c.server}.ServeHTTP(w, w.req)          ...     } }
  3、读取到请求之后,会再次基于传入的 context  创建新的 cancelCtx  ,并设置到当前请求对象 req  上,同时生成的 response  对象中 cancelCtx  保存了当前 context  取消方法。 func (c *conn) readRequest(ctx context.Context) (w *response, err error) {      ...      req, err := readRequest(c.bufr, keepHostHeader)      ...      ctx, cancelCtx := context.WithCancel(ctx)     req.ctx = ctx      ...      w = &response{         conn:          c,         cancelCtx:     cancelCtx,         req:           req,         reqBody:       req.Body,         handlerHeader: make(Header),         contentLength: -1,         closeNotifyCh: make(chan bool, 1),          // We populate these ahead of time so we"re not         // reading from req.Header after their Handler starts         // and maybe mutates it (Issue 14940)         wants10KeepAlive: req.wantsHttp10KeepAlive(),         wantsClose:       req.wantsClose(),     }      ...     return w, nil }
  这样处理的目的主要有以下几点: 一旦请求超时,即可中断当前请求; 在处理构建 response  过程中如果发生错误,可直接调用 response  对象的 cancelCtx  方法结束当前请求; 在处理构建 response  完成之后,调用 response  对象的 cancelCtx  方法结束当前请求。
  在整个 server  处理流程中,使用了一条 context  链贯穿 Server  、 Connection  、 Request  ,不仅将上游的信息共享给下游任务,同时实现了上游可发送取消信号取消所有下游任务,而下游任务自行取消不会影响上游任务。 总结
  context  主要用于父子任务之间的同步取消信号,本质上是一种协程调度的方式 。另外在使用 context  时有两点值得注意:上游任务仅仅使用 context  通知下游任务不再需要,但不会直接干涉和中断下游任务的执行,由下游任务自行决定后续的处理操作,也就是说 context  的取消操作是无侵入的; context  是线程安全的,因为 context  本身是不可变的( immutable  ),因此可以放心地在多个协程中传递使用。

各位爸爸们了解一下三手烟的危害吧抽抽抽,一天都在抽烟,孩子都要被熏死了。这是很多孩子童年生活中经常听到的抱怨。这个时候爸爸们就会解释到自己又没有在家里抽。或许很多老爸不知道那些年没在屋里抽的烟也会对家人造成影响。放弃2。5L阿特兹,入手威马汽车,提车两个月,惊喜原来这么多追了十年的马六终究放弃了!从小就非常喜欢车,小时候喜欢遥控四驱赛车,长大了有点零花钱就立马去购买汽车杂志。一开始喜欢大排量的美系车,后来更喜欢轻巧强操控的日系车,被马自达毫不妥协的孙莉晒二女儿近照,9岁多妹涂大红唇穿旗袍,可爱漂亮明星范十足7月20日下午,孙莉在个人社交平台分享了一组小女儿多妹的近照,还开心配文妹妹日常。孙莉时不时地就会po出一些孩子们的成长日常,是公认的好妈妈。多妹从小就古灵精怪,长得漂亮,性格也好赌王二房女儿何超仪状态引争议,烟熏妆抢镜,被指像动画反派人物赌王儿女们各奔东西,拥抱各自的精彩生活。前有何超欣毕业于清华,后有何超仪现身纽约参加活动,两人都是何鸿燊最疼爱的女儿。何超仪是兄弟姐妹中少有的选择出道的人,她至今仍以每年约一部作品Meta与NVIDIA联合打造大型AI研究超级计算机Meta的AI超级计算机是迄今为止最大的NVIDIADGXA100客户系统。该系统将为Meta的AI研究人员提供5百亿亿次级AI计算性能,采用了最先进的NVIDIA系统Infini让孩子充满笑可露齿的自信,扉乐VIIV儿童电动牙刷一直被种草儿童电动牙刷,久而久之我就动了心。可在选购时却发现很多厂家并未重视315岁儿童牙齿的具体护理,想要找到一款适合家里9岁女宝使用的电动牙刷真的不容易。适逢扉乐推出了VIIV这么自信?苹果豪掷1353万元鼓励用户破解iOS16漏洞近日,苹果表示拿出200万美元(约合人民币1353。18万元)的悬赏奖金,鼓励全球用户寻找iOS16操作系统的全新锁定模式(LockdownMode)中的漏洞。苹果表示此举是为了进谢倩霓让阅读照亮童年的旅程,陪伴孩子开启充盈且自信的人生亲子阅读究竟能带给我们什么?在培养孩子的阅读习惯中,家长能做哪些事情?给孩子提供哪三样东西,可以提升孩子的阅读兴趣?在选择优秀儿童读物的时候如何避免踩雷?在昨天晚上举行的东方亲子阅勇士消息人性化举动出炉,新援自信发声,卢尼合同引争议北京时间7月14日,远在大洋彼岸的金州勇士传来了3条消息,一起来看看吧。人性化举动出炉今年休赛期,勇士老将安德烈伊戈达拉一年合同到期,成为了完全自由球员,然而直到现在他还没有确定是脸上的肉肉困扰了十来年了,总是有些不自信,要怎么办呢?面部轮廓主要由骨骼肌肉和软组织(脂肪)组成,因而我们常说的脸大的成因,可具体分为骨骼性肌肉性和脂肪性。对于面部整体轮廓美观度,从面部脂肪分布方面来讲,面部脂肪适当,面部看起来饱满,期待!张君龙得到霍利菲尔德梅威瑟福尔曼真传,自信改写拳击历史近日,中国拳击传奇巨星亚洲重量级第一人龙王张君龙向广大拳迷分享了又一则喜讯,即是他已经成为前重量级拳王乔治福尔曼的关门弟子,通过福尔曼的言传身教,张君龙已经更好懂得了如何分配体能,
长沙出生13天的宝宝阳了,省妇幼专家提醒注意防护三湘都市报新湖南客户端12月15日讯(全媒体记者李琪通讯员武周游)刚出生13天的新生宝宝,竟感染了新冠病毒,让人揪心。经过湖南省妇幼保健院专家团队的救治,患儿发热症状及时得到缓解,一加9周年引加油热议,刘作虎决定未来10年发展的重要时刻12月15日,一加手机官微正式宣布,将于12月17日1430举行一加9周年活动,主题为向新而生,将会带来新方向,新动作,新未来。不知不觉中,一加手机已经成立9年之久,每一代的产品显人在低谷时期时刻提醒自己一切都是有原因的,你并不孤单当您经历艰难时期时,可能很难找到安慰,但可以提供一些安慰的一句话是这也会过去。这句话通常出自波斯苏菲派诗人鲁米之手,它提醒人们,困难时期只是暂时的,美好的日子还在前方。这句话背后的关键时刻出一份力,全力以赴向春天出发!阿里发布助农助商政策2022年12月14日上午,阿里巴巴集团发布了一系列助农助商的平台政策。阿里称,在市场主体千方百计拼经济的关键时刻,阿里巴巴希望能出一份力。帮助商家更好把握发展机遇,释放增长潜力,酒桌上,新人必学的3大礼仪,内行看门道,人缘好更不得罪人现在越来越多的年轻人不喜欢参加酒局,因为酒局上总有各种各样的规矩束缚着他们,原本想要好好品尝美食,但是酒局上的种种礼仪使得他们如坐针毡,吃不好喝不好,于是便干脆不社交,酒局是能避开天冷了,就馋这锅炖菜,热乎乎一大桌,端上桌汤汁都不剩,贼过瘾冬日生活打卡季天冷了,就馋这锅炖菜,热乎乎一大桌,上桌汁都不剩,贼过瘾大雪过后,我们也即将进入数九寒天的一九,天气越来越冷了,今天小编为大家推荐几道适合冬天吃的大烩菜,家人吃了从脚30块挖到宝开架一姐卡尼尔,真不考虑重回中国吗?护肤的尽头是欧莱雅!作为全球顶级美妆集团,欧娘娘家的儿女们多到让人认不全。也不知道还有多少人记得卡尼尔?卡尼尔2011官宣刘亦菲为代言人,2013年合作结束。卡尼尔作为欧莱雅大众化洪金宝二婚妻子气质真好,穿香槟色礼裙有贵妇范,不愧是港姐出身在不同的场合,穿不同风格的服饰,这样才能够让你的着装与场合,产生极强的契合感。比如,在日常穿搭当中,如果我们穿得过于隆重,就会显得过分张扬。但是,到了红毯或者是舞台穿搭中,如果我们美人计今年的桃花KPI,我靠这些最后一搏了!距离圣诞节还有10天,距离跨年还有16天,再不练练手,来不及了呀!对于你我素人来说,全年最支棱的时间段,肯定在年末。圣诞跨年公司年会射手座朋友的生日(众所周知他们很爱办大Party你敢信?下面的毛还可以植发秃头,当代年轻人的通病,放眼望去,不是已经秃了,就是正在秃的路上。枕头上有头发地上有头发,卫生间的下水道也有头发!唯独头上的头发,寥寥无几甚至越来越少,让人倍感焦虑秃头青年据统计,始祖鸟秋冬套装快快安排上吧一组实拍细节图鸟家绝绝紫套装独家定织定染的高级灰面料内里复合羊羔绒克重高达420裤子的颜色也是绝了灰紫色也太高级了胸前两种刺绣一看就是超重工!!!上衣S衣长68胸围116肩宽54袖