前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >为什么在Go语言中要慎用interface{}

为什么在Go语言中要慎用interface{}

作者头像
我的小碗汤
发布于 2018-08-22 03:02:23
发布于 2018-08-22 03:02:23
1.4K00
代码可运行
举报
文章被收录于专栏:我的小碗汤我的小碗汤
运行总次数:0
代码可运行

在掘金上看到一篇从java转Go思想上的变化以及对go语言思考的文章,写的很透彻,我也推敲了一遍。这里也分享给大家,或许对将要或者已经学习golang的同学有所帮助。提示:代码块可以左右拖动哦~~

记得刚从Java转Go的时候,一个用Go语言的前辈告诉我:“要少用interface{},这玩意儿很好用,但是最好不要用。”那时候我的组长打趣接话:“不会,他是从Java转过来的,碰到个问题就想定义个类。”当时我对interface{}的第一印象也是类比Java中的Object类,我们使用Java肯定不会到处去传Object啊。后来的事实证明,年轻人毕竟是年轻人,看着目前项目里漫天飞的interface{},它们时而变成函数形参让人摸不着头脑;时而隐藏在结构体字段中变化无穷。不禁想起以前看到的一句话:“动态语言一时爽,重构代码火葬场。”故而写下此篇关于interface{}的经验总结,供以后的自己和读者参考。

1、interface{}之对象转型坑

一个语言用的久了,难免使用者的思维会受到这个语言的影响,interface{}作为Go的重要特性之一,它代表的是一个类似*void的指针,可以指向不同类型的数据。所以我们可以使用它来指向任何数据,这会带来类似与动态语言的便利性,如以下的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

type BaseQuestion struct{
  QuestionId int
  QuestionContent string
}

type ChoiceQuestion struct{
  BaseQuestion
  Options []string
}

type BlankQuestion struct{
  BaseQuestion
  Blank string
}

func fetchQuestion(id int) (interface{} , bool) {
  data1 ,ok1 := fetchFromChoiceTable(id) // 根据ID到选择题表中找题目,返回(ChoiceQuestion)
  data2 ,ok2 := fetchFromBlankTable(id)  // 根据ID到填空题表中找题目,返回(BlankQuestion)

  if ok1 {
    return data1,ok1
  }

  if ok2 {
    return data2,ok2
  }

  return nil ,false
}

func fetchFromBlankTable(id int) (BlankQuestion, bool) {

  //模拟查数据库操作,不等于1说明没有数据
  if id != 1 {
    return BlankQuestion{}, false
  }
  blankQuestion := BlankQuestion{
    BaseQuestion : BaseQuestion{
      1,
      "golang是哪个组织发布的?",
    },
    Blank:  "Google",
  }

  return blankQuestion, true
}

func fetchFromChoiceTable(id int) (ChoiceQuestion, bool) {
  //模拟查数据库操作,不等于2说明没有数据
  if id != 2 {
    return ChoiceQuestion{}, false
  }
  choiceQuestion := ChoiceQuestion{
    BaseQuestion : BaseQuestion{
      2,
      "golang有哪些优秀项目?",
    },
    Options : []string{"docker", "Kubernetes", "lantern"},
  }


  return choiceQuestion, true
}

func printQuestion(id int){
  if data, ok := fetchQuestion(id); ok {
    switch v := data.(type) {
    case ChoiceQuestion:
      fmt.Println("ChoiceQuestion--------", v)
    case BlankQuestion:
      fmt.Println("BlankQuestion---------", v)
    case nil:
      fmt.Println("nil-----------", v)
    }
    fmt.Println("data is -----", data)
  }
}

func main() {
  printQuestion(1)
  printQuestion(2)
}

在上面的代码中,data1是ChoiceQuestion类型,data2是BlankQuestion类型。因此,我们的interface{}指代了三种类型,分别是ChoiceQuestion、BlankQuestion和nil,这里就体现了Go和面向对象语言的不同点了,在面向对象语言中,我们本可以这么写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func fetchQuestion(id int) (BaseQuestion , bool) {
    ...
}

只需要返回基类BaseQuestion即可,需要使用子类的方法或者字段只需要向下转型。然而在Go中,并没有这种is-a(父子继承关系,D is a B,即D是B的子类)的概念,代码会无情的提示你,返回值类型不匹配。那么,我们该如何使用这个interface{}返回值呢,我们也不知道它是什么类型啊。所以,你得不厌其烦的一个一个判断:

那么执行上述程序结果就是:

EN,好像通过Go的switch-type语法糖,判断起来也不是很复杂嘛。如果你也这样以为,并且跟我一样用了这个方法,恭喜你已经入坑了。

因为需求永远是多变的,假如现在有个需求,需要在ChoiceQuesiton打印时,给它的QuestionContent字段添加前缀选择题,于是printQuestion函数就变成以下这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func printQuestion(id int) {
  if data, ok := fetchQuestion(id); ok {
    switch v := data.(type) {
    case ChoiceQuestion:
      v.QuestionContent = "选择题" + v.QuestionContent
      fmt.Println("ChoiceQuestion--------", v)
    case BlankQuestion:
      v.QuestionContent = "填空题" + v.QuestionContent
      fmt.Println("BlankQuestion---------", v)
    case nil:
      fmt.Println("nil-----------", v)
    }
    fmt.Println("data is -----", data)
  }
}

看输出结果:

我们看到了不一样的输出结果,data输出没有变化。可能有的读者已经猜到了,v和data根本不是指向同一份数据,换句话说,v := data.(type)这条语句,会新建一个data在对应type下的副本,我们对v操作影响不到data。当然,我们可以要求fetchFrom***Table()返回*ChoiceQuestion类型,这样我们可以通过判断*ChoiceQuestion来处理数据副本问题,那么代码将变成这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

type BaseQuestion struct{
  QuestionId int
  QuestionContent string
}

type ChoiceQuestion struct{
  BaseQuestion
  Options []string
}

type BlankQuestion struct{
  BaseQuestion
  Blank string
}

func fetchQuestion(id int) (interface{} , bool) {
  data1 ,ok1 := fetchFromChoiceTable(id) // 根据ID到选择题表中找题目,返回(ChoiceQuestion)
  data2 ,ok2 := fetchFromBlankTable(id)  // 根据ID到填空题表中找题目,返回(BlankQuestion)

  if ok1 {
    return data1,ok1
  }

  if ok2 {
    return data2,ok2
  }

  return nil ,false
}

func fetchFromBlankTable(id int) (*BlankQuestion, bool) {

  //模拟查数据库操作,不等于1说明没有数据
  if id != 1 {
    return nil, false
  }
  blankQuestion := &BlankQuestion{
    BaseQuestion : BaseQuestion{
      1,
      "golang是哪个组织发布的?",
    },
    Blank:  "Google",
  }

  return blankQuestion, true
}

func fetchFromChoiceTable(id int) (*ChoiceQuestion, bool) {
  //模拟查数据库操作,不等于2说明没有数据
  if id != 2 {
    return nil, false
  }
  choiceQuestion := &ChoiceQuestion{
    BaseQuestion : BaseQuestion{
      2,
      "golang有哪些优秀项目?",
    },
    Options : []string{"docker", "Kubernetes", "lantern"},
  }


  return choiceQuestion, true
}

func printQuestion(id int) {
  if data, ok := fetchQuestion(id); ok {
    switch v := data.(type) {
    case *ChoiceQuestion:
      v.QuestionContent = "选择题" + v.QuestionContent
      fmt.Println("ChoiceQuestion--------", v)
    case *BlankQuestion:
      v.QuestionContent = "填空题" + v.QuestionContent
      fmt.Println("BlankQuestion---------", v)
    case nil:
      fmt.Println("nil-----------", v)
    }
    fmt.Println("data is -----", data)
  }
}

func main() {
  printQuestion(1)
  printQuestion(2)
}

再看看输出结果,发现data也发生了变化:

我们这里修改了fetchFrom***Table函数,不过在实际项目中,你可能有很多理由不能去动fetchFrom***Table(),也许是涉及数据库的操作函数你没有权限改动;也许是项目中很多地方使用了这个方法,你也不能随便改动。这也是我没有写出fetchFrom***Table()的实现的原因,很多时候,这些方法对你只能是黑盒的。退一步讲,即使方法签名可以改动,我们这里也只是列举出了两种题型,可能还有材料题、阅读题、写作题等等,如果需求要对每个题型的QuestonContent添加对应的题型前缀,我们岂不是要写出下面这种代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func printQuestion(id int) {
  if data, ok := fetchQuestion(id); ok {
    switch v := data.(type) {
    case *ChoiceQuestion:
      v.QuestionContent = "选择题" + v.QuestionContent
      fmt.Println(v)
    case *BlankQuestion:
      v.QuestionContent = "填空题" + v.QuestionContent
      fmt.Println(v)
    case *MaterialQuestion:
      v.QuestionContent = "材料题" + v.QuestionContent
      fmt.Println(v)
    case *WritingQuestion:
      v.QuestionContent = "写作题" + v.QuestionContent
      fmt.Println(v)
      ...
    case nil:
      fmt.Println(v)
      fmt.Println(data)
    }
  }
}

这种代码带来了大量的重复结构,由此可见,interface{}的动态特性很不能适应复杂的数据结构,难道我们就不能有更方便的操作了么?山穷水尽之际,或许可以回头看看面向对象思想,也许继承和多态能很好的解决我们遇到的问题。

我们可以把这些题型抽成一个接口,并且让BaseQuestion实现这个接口。那么代码可以写成这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

type IQuestion interface{
  GetQuestionType() int
  GetQuestionContent()string
  AddQuestionContentPrefix(prefix string)
}

type BaseQuestion struct {
  QuestionId      int
  QuestionContent string
  QuestionType    int
}

const (
  ChoiceQuestionType = 1
  BlankQuestionType = 2
)

func (self *BaseQuestion) GetQuestionType() int {
  return self.QuestionType
}

func (self *BaseQuestion) GetQuestionContent() string {
  return self.QuestionContent
}

func (self *BaseQuestion) AddQuestionContentPrefix(prefix string) {
  self.QuestionContent = prefix + self.QuestionContent
}

type ChoiceQuestion struct{
  BaseQuestion
  Options []string
}

type BlankQuestion struct{
  BaseQuestion
  Blank string
}

//修改返回值为IQuestion
func fetchQuestion(id int) (IQuestion, bool) {
  data1, ok1 := fetchFromChoiceTable(id) // 根据ID到选择题表中找题目
  data2, ok2 := fetchFromBlankTable(id)  // 根据ID到选择题表中找题目

  if ok1 {
    return &data1, ok1
  }

  if ok2 {
    return &data2, ok2
  }

  return nil, false
}

func fetchFromBlankTable(id int) (BlankQuestion, bool) {

  //模拟查数据库操作,不等于1说明没有数据
  if id != 1 {
    return BlankQuestion{}, false
  }
  blankQuestion := BlankQuestion{
    BaseQuestion : BaseQuestion{
      1,
      "golang是哪个组织发布的?",
      BlankQuestionType,
    },
    Blank:  "Google",
  }

  return blankQuestion, true
}

func fetchFromChoiceTable(id int) (ChoiceQuestion, bool) {
  //模拟查数据库操作,不等于2说明没有数据
  if id != 2 {
    return ChoiceQuestion{}, false
  }
  choiceQuestion := ChoiceQuestion{
    BaseQuestion : BaseQuestion{
      2,
      "golang有哪些优秀项目?",
      ChoiceQuestionType,
    },
    Options : []string{"docker", "Kubernetes", "lantern"},
  }


  return choiceQuestion, true
}

func printQuestion(id int) {
  if data, ok := fetchQuestion(id); ok {
    var questionPrefix string

    //需要增加题目类型,只需要添加一段case
    switch  data.GetQuestionType() {
    case ChoiceQuestionType:
      questionPrefix = "选择题"
    case BlankQuestionType:
      questionPrefix = "填空题"
    }

    data.AddQuestionContentPrefix(questionPrefix)
    fmt.Println("data - ", data)
  }
}

func main() {
  printQuestion(1)
  printQuestion(2)
}

这里ChoiceQuestion和BlankQuestion类型里包含了BaseQuestion,所以ChoiceQuestion和BlankQuestion也实现了IQuestion接口。不管有多少题型,只要它们包含BaseQuestion,就能自动实现IQuestion接口,从而,我们可以通过定义接口方法来控制数据。

上述代码输出结果为:

这种方法无疑大大减少了副本的创建数量,而且易于扩展。通过这个例子,我们也了解到了Go接口的强大之处,虽然Go并不是面向对象的语言,但是通过良好的接口设计,我们完全可以从中窥探到面向对象思维的影子。也难怪在Go文档的FAQ中,对于Is Go an object-oriented language?这个问题,官方给出的答案是yes and no,官方地址如下:

https://golang.google.cn/doc/faq#Is_Go_an_object-oriented_language

这里还可以多扯一句,前面说了v := data.(type)这条语句是拷贝data的副本,但当data是接口对象时,这条语句就是接口之间的转型而不是数据副本拷贝了。

如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

//IQuestion接口
type IQuestion interface{
  GetQuestionType() int
  GetQuestionContent()string
  AddQuestionContentPrefix(prefix string)
}

//BaseQuestion实现了IQuestion接口
type BaseQuestion struct {
  QuestionId      int
  QuestionContent string
  QuestionType    int
}

//题型常量
const (
  ChoiceQuestionType = 1
  BlankQuestionType = 2
)

func (self *BaseQuestion) GetQuestionType() int {
  return self.QuestionType
}

func (self *BaseQuestion) GetQuestionContent() string {
  return self.QuestionContent
}

func (self *BaseQuestion) AddQuestionContentPrefix(prefix string) {
  self.QuestionContent = prefix + self.QuestionContent
}

//ChoiceQuestion包含了BaseQuestion,故ChoiceQuestion也实现了IQuestion接口
//ChoiceQuestion自己实现了GetOptionsLen() int,故ChoiceQuestion也实现了IChoiceQuestion接口
type ChoiceQuestion struct{
  BaseQuestion
  Options []string
}

//定义新接口IChoiceQuestion,实现了IQuestion接口
type IChoiceQuestion interface {
  IQuestion
  GetOptionsLen() int
}

func (self *ChoiceQuestion) GetOptionsLen() int {
  return len(self.Options)
}

func showOptionsLen(data IQuestion) {
  //choice和data指向同一份数据
  if choice, ok := data.(IChoiceQuestion); ok {
    fmt.Println("Choice has :", choice.GetOptionsLen())
  }
}

func main() {
  choiceQuestion := &ChoiceQuestion{
    BaseQuestion: BaseQuestion{
      QuestionId: 1,
      QuestionContent: "golang有哪些优秀项目?",
      QuestionType: ChoiceQuestionType,
    },
    Options : []string{"docker", "Kubernetes", "lantern"},
  }

  //choiceQuestion实现了IQuestion接口,故可以作为参数传递
  showOptionsLen(choiceQuestion)
}

这里定义了IQuestion和IChoiceQuestion两个接口,IChoiceQuestion里有IQuestion,故IChoiceQuestion实现了IQuestion;

定义了BaseQuestion和ChoiceQuestion两个struct,BaseQuestion实现了IQuestion接口;ChoiceQuestion中包含BaseQuestion,

且实现了IChoiceQuestion中的GetOptionsLen() int方法,故ChoiceQuestion同时实现了IQuestion和IChoiceQuestion两个接口。

程序运行结果如下:

2、interface{}之nil坑

看以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

type BaseQuestion struct {
  QuestionId      int
  QuestionContent string
}

type ChoiceQuestion struct{
  BaseQuestion
  Options []string
}

func fetchFromChoiceTable(id int) (data *ChoiceQuestion) {
  if id == 1 {
    return &ChoiceQuestion{
      BaseQuestion: BaseQuestion{
        QuestionId:      1,
        QuestionContent: "golang有哪些优秀项目?",
      },
      Options: []string{"docker", "Kubernetes", "lantern"},
    }
  }
  return nil
}

func fetchQuestion(id int) (interface{}) {
  data1 := fetchFromChoiceTable(id) // 根据ID到选择题表中找题目
  return data1
}

func sendData(data interface{}) {
  fmt.Println("发送数据 ... " , data)
}

func main(){
  data := fetchQuestion(1)

  if data != nil {
    sendData(data)
  }

  data1 := fetchQuestion(2)

  if data != nil {
    sendData(data1)
  }
}

一串很常见的业务代码,我们根据id查询Question,为了以后能方便的扩展,我们使用interface{}作为返回值,然后根据data是否为nil来判断是不是要发送这个Question。不幸的是,不管fetchQuestion()方法有没有查到数据,sendData()都会被执行。运行程序,输出结果如下:

要明白内中玄机,我们需要回忆下interface{}究竟是个什么东西,文档上说,它是一个空接口,也就是说,一个没有声明任何方法的接口,那么,接口在Go的内部又究竟是怎么表示的?我在官方文档上找到一下几句话:

Under the covers, interfaces are implemented as two elements, a type and a value.

The value, called the interface's dynamic value, is an arbitrary concrete value and the type is that of the value. For the int value 3, an interface value contains, schematically, (int, 3).

An interface value is nil only if the inner value and type are both unset, (nil, nil). In particular, a nil interface will always hold a nil type.

If we store a pointer of type *int inside an interface value, the inner type will be *intregardless of the value of the pointer: (*int, nil).

Such an interface value will therefore be non-nil even when the pointer inside is nil.

看不懂的来看看中文:

在底层,接口作为两个元素实现:一个类型和一个值。

该值被称为接口的动态值, 它是一个任意的具体值,而该接口的类型则为该值的类型。对于 int 值3, 一个接口值示意性地包含(int, 3)。

只有在内部值和类型都未设置时(nil, nil),一个接口的值才为 nil。特别是,一个 nil 接口将总是拥有一个 nil 类型。

若我们在一个接口值中存储一个 *int 类型的指针,则内部类型将为 *int,无论该指针的值是什么:(*int, nil)。

因此,这样的接口值会是非 nil 的,即使在该指针的内部为 nil。

具体到我们的示例代码,fetchQuestion()的返回值interface{},其实是指(*ChoiceQuestion, data1)的集合体,如果没查到数据,则我们的data1为nil,上述集合体变成(*ChoiceQuestion, nil)。而Go规定中,这样的结构的集合体本身是非nil的,进一步的,只有(nil,nil)这样的集合体才能被判断为nil。

这严格来说,不是interface{}的问题,而是Go接口设计的规定,你把以上代码中的interface{}换成其它任意你定义的接口,都会产生此问题。所以我们对接口的判nil,一定要慎重,以上代码如果改成多返回值形式,就能完全避免这个问题。如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

type BaseQuestion struct {
  QuestionId      int
  QuestionContent string
}

type ChoiceQuestion struct{
  BaseQuestion
  Options []string
}

func fetchFromChoiceTable(id int) (data *ChoiceQuestion) {
  if id == 1 {
    return &ChoiceQuestion{
      BaseQuestion: BaseQuestion{
        QuestionId:      1,
        QuestionContent: "golang有哪些优秀项目?",
      },
      Options: []string{"docker", "Kubernetes", "lantern"},
    }
  }
  return nil
}

func fetchQuestion(id int) (interface{},bool) {
  data1 := fetchFromChoiceTable(id) // 根据ID到选择题表中找题目
  if data1 != nil {
    return data1,true
  }
  return nil,false
}

func sendData(data interface{}) {
  fmt.Println("发送数据 ..." , data)
}

func main(){
  if data, ok := fetchQuestion(1); ok {
    sendData(data)
  }

  if data, ok := fetchQuestion(2); ok {
    sendData(data)
  }
}

当然,也有很多其它的办法可以解决,大家可以自行探索。

3、总结和引用

零零散散写了这么多,有点前言不搭后语,语言不通之处还望见谅。Go作为一个设计精巧的语言,它的成功不是没有道理的,通过对目前遇到的几个大问题和总结,慢慢对Go有了一点点浅薄的认识,以后碰到了类似的问题,还可以继续添加在文章里。

interface{}作为Go中最基本的一个接口类型,可以在代码灵活性方面给我们提供很大的便利,但是我们也要认识到,接口就是对一类具体事物的抽象,而interface{}作为每个结构体都实现的接口,提供了一个非常高层次的抽象,以至于我们会丢失事物的大部分信息,所以我们在使用interface{}前,一定要谨慎思考,这就像相亲之前提要求,你要是说只要是个女的我都可以接受,那可就别怪来的人可能是高的矮的胖的瘦的美的丑的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 进击云原生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
SpringCloud技术指南系列(四)服务注册发现之Consul服务注册
目前服务发现的解决方案有Eureka,Consul,Zookeeper等,这三个是SpringCloud官方支持的。
品茗IT
2019/09/12
1K0
SpringCloud微服务实战系列(十七)Ouath2在真实场景中的应用之资源服务器
在《SpringBoot入门建站全系列(三十五)整合Oauth2做单机版认证授权》和《Spring整合Oauth2单机版认证授权详情》中
品茗IT
2020/05/28
8170
SpringCloud微服务实战系列(十八)Ouath2在真实场景中的应用之授权服务器
在《SpringCloud微服务实战系列(十七)Ouath2在真实场景中的应用之资源服务器》]中
品茗IT
2020/05/28
1.5K0
SpringCloud微服务实战系列(二十)Ouath2在真实场景中的应用之客户端接入(第二种写法)
在《SpringCloud微服务实战系列(十七)Ouath2在真实场景中的应用之资源服务器》]中
品茗IT
2020/05/28
9740
SpringCloud微服务实战系列(十四)分布式锁之Zookeeper实现
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
品茗IT
2020/05/28
8260
SpringCloud技术指南系列(六)服务注册发现之Zookeeper服务注册
目前服务发现的解决方案有Eureka,Consul,Zookeeper等,这三个是SpringCloud官方支持的。
品茗IT
2019/09/12
1.6K0
SpringCloud微服务实战系列(十三)分布式锁之Redis实现(redisson)
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
品茗IT
2020/05/28
2.4K0
SpringCloud技术指南系列(十)配置管理之自建配置中心
详细可以查看《SpringBoot入门建站全系列(二十三)配置文件优先级及常用配置方式》.
品茗IT
2019/09/12
9680
SpringCloud技术指南系列(七)服务注册发现之Zookeeper服务调用
目前服务发现的解决方案有Eureka,Consul,Zookeeper等,这三个是SpringCloud官方支持的。
品茗IT
2019/09/12
8820
SpringCloud技术指南系列(三)服务注册发现之Eureka服务调用
目前服务发现的解决方案有Eureka,Consul,Zookeeper等,这三个是SpringCloud官方支持的。
品茗IT
2019/09/12
3710
SpringCloud微服务实战系列(十五)分布式链路跟踪Sleuth与Zipkin实现
分布式链路追踪,是一种用于分析和监控应用程序的方法,尤其是那些使用微服务架构的那些应用。分布式链路跟踪有助于查找故障发生位置和导致性能低下的原因。
品茗IT
2020/05/28
4640
SpringCloud技术指南系列(十一)API网关之Zuul使用
API网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的Facade模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤。它除了要实现请求路由、 负载均衡、 校验过滤等功能之外,还需要更多能力,比如与服务治理框架的结合、请求转发时的熔断机制、服务的聚合等一系列高级功能。
品茗IT
2019/09/12
5170
SpringBoot入门建站全系列(三十六)AspectJ做AOP日志管理
Spring的两大特性,AOP和IOC,AOP面向切面编程,可以对当前代码无侵入的情况下,使用AspectJ对切点数据进行分析存储。常常被用来做日志/流水的存储记录。
品茗IT
2020/09/03
1.7K0
SpringCloud技术指南系列(十二)API网关之Gateway使用
API网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的Facade模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤。它除了要实现请求路由、 负载均衡、 校验过滤等功能之外,还需要更多能力,比如与服务治理框架的结合、请求转发时的熔断机制、服务的聚合等一系列高级功能。
品茗IT
2019/09/12
1K0
SpringCloud技术指南系列(五)服务注册发现之Consul服务调用
目前服务发现的解决方案有Eureka,Consul,Zookeeper等,这三个是SpringCloud官方支持的。
品茗IT
2019/09/12
7660
SpringCloud技术指南系列(五)服务注册发现之Consul服务调用
基于Spring Cloud Oauth2 JWT搭建微服务的安全认证中心
Oauth协议为用户资源的授权提供了一个安全的、开放而又建议的标准。oauth的授权不会是第三方初级到用户的账号信息(如用户名与密码),及第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此oauth是安全的。oauth是Open Authorization的简写
小东啊
2019/06/26
15.7K1
基于Spring Cloud Oauth2 JWT搭建微服务的安全认证中心
SpringBoot入门建站全系列(三十五)整合Oauth2做单机版认证授权
OAuth 2.0 规范定义了一个授权(delegation)协议,对于使用Web的应用程序和API在网络上传递授权决策非常有用。OAuth被用在各钟各样的应用程序中,包括提供用户认证的机制。
品茗IT
2020/05/28
1.5K0
SpringCloud技术指南系列(九)配置管理之Zookeeper配置中心
详细可以查看《SpringBoot入门建站全系列(二十三)配置文件优先级及常用配置方式》.
品茗IT
2019/09/12
1.6K0
spring Cloud微服务 security+oauth2认证授权中心自定义令牌增强,并实现登录和退出
在之前的博客我写了 SpringCloud整合spring security+ oauth2+Redis实现认证授权,本文对返回的token实现自定义增强令牌返回结果,以及对于oauth2存在Redis的数据进行解释。
共饮一杯无
2022/11/28
1.2K0
spring Cloud微服务 security+oauth2认证授权中心自定义令牌增强,并实现登录和退出
Spring整合Sharding-JDBC分库分表详情
最初线上系统的业务量不是很大,业务数据量并不大,比如说单库的数据量在百万级别以下(事实上千万级别以下都还能支撑),那么MySQL的单库即可完成任何增/删/改/查的业务操作。随着业务的发展,单个DB中保存的数据量(用户、订单、计费明细和权限规则等数据)呈现指数级增长,那么各种业务处理操作都会面临单DB的IO读写瓶颈带来的性能问题。
品茗IT
2019/09/12
2.4K0
推荐阅读
SpringCloud技术指南系列(四)服务注册发现之Consul服务注册
1K0
SpringCloud微服务实战系列(十七)Ouath2在真实场景中的应用之资源服务器
8170
SpringCloud微服务实战系列(十八)Ouath2在真实场景中的应用之授权服务器
1.5K0
SpringCloud微服务实战系列(二十)Ouath2在真实场景中的应用之客户端接入(第二种写法)
9740
SpringCloud微服务实战系列(十四)分布式锁之Zookeeper实现
8260
SpringCloud技术指南系列(六)服务注册发现之Zookeeper服务注册
1.6K0
SpringCloud微服务实战系列(十三)分布式锁之Redis实现(redisson)
2.4K0
SpringCloud技术指南系列(十)配置管理之自建配置中心
9680
SpringCloud技术指南系列(七)服务注册发现之Zookeeper服务调用
8820
SpringCloud技术指南系列(三)服务注册发现之Eureka服务调用
3710
SpringCloud微服务实战系列(十五)分布式链路跟踪Sleuth与Zipkin实现
4640
SpringCloud技术指南系列(十一)API网关之Zuul使用
5170
SpringBoot入门建站全系列(三十六)AspectJ做AOP日志管理
1.7K0
SpringCloud技术指南系列(十二)API网关之Gateway使用
1K0
SpringCloud技术指南系列(五)服务注册发现之Consul服务调用
7660
基于Spring Cloud Oauth2 JWT搭建微服务的安全认证中心
15.7K1
SpringBoot入门建站全系列(三十五)整合Oauth2做单机版认证授权
1.5K0
SpringCloud技术指南系列(九)配置管理之Zookeeper配置中心
1.6K0
spring Cloud微服务 security+oauth2认证授权中心自定义令牌增强,并实现登录和退出
1.2K0
Spring整合Sharding-JDBC分库分表详情
2.4K0
相关推荐
SpringCloud技术指南系列(四)服务注册发现之Consul服务注册
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档