开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

Go 日常开发常备第三方库和工具 业务开发 基础工具 总结

发表于 2021-11-02

不知不觉写 Go 已经快一年了,上线了大大小小好几个项目;心态也经历了几轮变化。

因为我个人大概前五年时间写的是 Java ,中途写过一年多的 Python,所以刚接触到 Go 时的感觉如下图:

既没有 Java 的生态,也没有 Python 这么多语法糖。

写到现在的感觉就是:

这里就不讨论这几门语言谁强谁弱了;重点和大家分享下我们日常开发中所使用到的一些第三方库与工具。

这里我主要将这些库分为两类:

  • 业务开发
  • 基础工具开发

业务开发

首先是业务开发,主要包含了 web、数据库、Redis 等。

Gin ⭐️⭐️⭐️⭐️⭐️

首先是 Gin,一款 HTTP 框架,使用简单、性能优秀、资料众多;你还在犹豫选择哪款框架时,那就选择它吧,基本没错。

当然和它配套的 github.com/swaggo/gin-… swagger 工具也是刚需;利用它可以生成 swagger 文档。

GORM ⭐️⭐️⭐️⭐️⭐️

GORM 也没啥好说的,如果你喜欢 orm 的方式操作数据库,那就选它吧;同样的也是使用简单、资料较多。

如果有读写分离需求,也可以使用 GORM 官方提供的插件 github.com/go-gorm/dbr… ,配合 GORM 使用也是非常简单。

errors ⭐️⭐️⭐️⭐️⭐️

Go 语言自身提供的错误处理比较简单,github.com/pkg/errors 提供了更强大的功能,比如:

  • 包装异常
  • 包装堆栈等。

常用的有以下 API:

1
2
3
4
5
go复制代码// WithMessagef annotates err with the format specifier.
func WithMessagef(err error, format string, args ...interface{}) error

// WithStack annotates err with a stack trace at the point WithStack was called.
func WithStack(err error) error

zorolog ⭐️⭐️⭐️⭐️⭐️

Go 里的日志打印库非常多,日志在日常开发中最好就是存在感低;也就是说性能强(不能影响到业务代码)、使用 API 简单。

1
2
go复制代码"github.com/rs/zerolog/log"
log.Debug().Msgf("OrderID :%s", "12121")

excelize

github.com/qax-os/exce…是一个读写 Excel 的库,基本上你能遇到的 Excel 操作它都能实现。

now ⭐️⭐️⭐️⭐️

github.com/jinzhu/now 是一个时间工具库:

  • 获取当前的年月日、时分秒。
  • 不同时区支持。
  • 最后一周、最后一个月等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
go复制代码import "github.com/jinzhu/now"

time.Now() // 2013-11-18 17:51:49.123456789 Mon

now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon
now.BeginningOfHour() // 2013-11-18 17:00:00 Mon
now.BeginningOfDay() // 2013-11-18 00:00:00 Mon
now.BeginningOfWeek() // 2013-11-17 00:00:00 Sun
now.BeginningOfMonth() // 2013-11-01 00:00:00 Fri
now.BeginningOfQuarter() // 2013-10-01 00:00:00 Tue
now.BeginningOfYear() // 2013-01-01 00:00:00 Tue

now.EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon
now.EndOfHour() // 2013-11-18 17:59:59.999999999 Mon
now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon
now.EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat
now.EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat
now.EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue
now.EndOfYear() // 2013-12-31 23:59:59.999999999 Tue

now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday
now.EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun

Decimal ⭐️⭐️⭐️⭐️

当业务上需要精度计算时 github.com/shopspring/… 可以帮忙。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
go复制代码import (
"fmt"
"github.com/shopspring/decimal"
)

func main() {
price, err := decimal.NewFromString("136.02")

quantity := decimal.NewFromInt(3)
fee, _ := decimal.NewFromString(".035")
taxRate, _ := decimal.NewFromString(".08875")

subtotal := price.Mul(quantity)

preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))

total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))

fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421
fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375
fmt.Println("Total:", total) // Total: 459.824961375
fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
}

基本上你能想到的精度转换它都能做到;配合上 GORM 也可以将 model 字段声明为 decimal 的类型,数据库对应的也是 decimal ,这样使用起来时会更方便。

1
go复制代码Amount decimal.Decimal `gorm:"column:amout;default:0.0000;NOT NULL" json:"amout"`

configor ⭐️⭐️⭐️⭐️

github.com/jinzhu/conf… 是一个配置文件读取库,支持 YAML/JSON/TOML 等格式。

go-cache ⭐️⭐️⭐️

github.com/patrickmn/g… 是一个类似于 Java 中的 Guava cache,线程安全,使用简单;不需要分布式缓存的简单场景可以考虑。

1
2
3
go复制代码	c := cache.New(5*time.Minute, 10*time.Minute)
// Set the value of the key "foo" to "bar", with the default expiration time
c.Set("foo", "bar", cache.DefaultExpiration)

copier ⭐️⭐️⭐️

github.com/jinzhu/copi… 看名字就知道这是一个数据复制的库,与 Java 中的 BeanUtils.copy() 类似;可以将两个字段相同但对象不同的 struct 进行数据复制,也支持深拷贝。

1
go复制代码func Copy(toValue interface{}, fromValue interface{}) (err error)

在我们需要一个临时 struct 来存放数据时很有用,特别是一个 struct 中字段非常多时,一个个来回赋值确实有点费手指。

但也要注意不要什么情况都使用,会带来一些弊端:

  • 当删除字段时,不能利用编译器提示。
  • 当一些字段需要额外人工处理时,代码不易阅读。
  • 反射赋值,有一定性能损耗。

总之在业务开发时,还是建议人工编写,毕竟代码是给人看的。

env ⭐️⭐️⭐️

github.com/caarlos0/en… 这个库可以将我们的环境变量转换为一个 struct.

1
2
3
4
5
6
7
8
9
10
11
12
go复制代码type config struct {
Home string `env:"HOME"`
}

func main() {
cfg := config{}
if err := env.Parse(&cfg); err != nil {
fmt.Printf("%+v\n", err)
}

fmt.Printf("%+v\n", cfg)
}

这个在我们打包代码到不同的运行环境时非常有用,利用它可以方便的获取不同环境变量。

user_agent ⭐️⭐️⭐️

github.com/mssola/user… 是一个格式化 user-agent 的小工具。

当我们需要在服务端收集 user-agen 时可以更快的读取数据。

1
2
3
4
5
6
7
8
9
10
go复制代码func main() {
ua := user_agent.New("Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1")

fmt.Printf("%v\n", ua.Mobile()) // => true
fmt.Printf("%v\n", ua.Bot()) // => false
fmt.Printf("%v\n", ua.Mozilla()) // => "5.0"
fmt.Printf("%v\n", ua.Model()) // => "Nexus One"
fmt.Printf("%v\n", ua.Platform()) // => "Linux"
fmt.Printf("%v\n", ua.OS())
}

phonenumbers ⭐️⭐️⭐️

github.com/nyaruka/pho… 手机号码验证库,可以不用自己写正则表达式了。

1
2
go复制代码// parse our phone number
num, err := phonenumbers.Parse("6502530000", "US")

基础工具

接下来是一些基础工具库,包含一些主流的存储的客户端、中间件等。

gomonkey ⭐️⭐️⭐️⭐️⭐️

github.com/agiledragon… 是一个 mock 打桩工具,当我们写单元测试时,需要对一些非接口函数进行 mock 会比较困难,这时就需要用到它了。

由于它是修改了调用对应函数时机器跳转指令,而 CPU 架构的不同对应的指令也不同,所以在我们使用时还不兼容苹果的 M1 芯片,不过目前应该已经兼容了,大家可以试试。

goconvey ⭐️⭐️⭐️⭐️⭐️

github.com/smartystree… 也是配合单元测试的库,可以兼容 go test 命令。

  • 提供可视化 web UI。
  • 与 IDE 集成显示单元覆盖率。

dig ⭐️⭐️⭐️⭐️⭐️

github.com/uber-go/dig 这是一个依赖注入库,我们这里暂不讨论是否应该使用依赖注入,至少目前我们使用下来还是有几个好处:

  • 所有的对象都是单例。
  • 有一个统一的地方管理对象。
  • 使用时直接传递对象当做参数进来即可(容器会自动注入)。

当然也有一些不太方便的地方:

  • 不熟悉时,一个对象是如何创建的不清楚。
  • 代码不是很好理解。

我们内部有自己开发一个业务框架,其中所有的对象都交由 dig 进行管理,使用起来倒也是比较方便。

cobra ⭐️⭐️⭐️⭐️

github.com/spf13/cobra是一个功能强大的命令行工具库,我们用它来实现内部的命令行工具,同时也推荐使用 github.com/urfave/cli/ 我个人会更习惯用后者,要简洁一些。

BloomRPC ⭐️⭐️⭐️⭐️

github.com/uw-labs/blo… 一个 gRPC 可视化工具,比起自己写 gRPC 客户端的代码那确实是要简单许多。


但也有些小问题,比如精度。如果是 int64 超过了 2^56 服务端拿到的值会发生错误,这点目前还未解决。

redis ⭐️⭐️⭐️⭐️

github.com/go-redis/re… Redis 客户端,没有太多可说的;发展了许多年,该有的的功能都有了。

elastic ⭐️⭐️⭐️⭐️

github.com/olivere/ela… 这也是一个非常成熟的 elasticsearch 库。

resty ⭐️⭐️⭐️⭐️

github.com/go-resty/re… 一个 http client, 使用起来非常简单:

1
2
3
4
5
go复制代码// Create a Resty Client
client := resty.New()
resp, err := client.R().
EnableTrace().
Get("https://httpbin.org/get")

有点 Python requests 包那味了。

pulsar-client-go ⭐️⭐️⭐️

Pulsar 官方出品的 go 语言客户端,相对于 Java 来说其他语言的客户端几乎都是后娘养的;功能会比较少,同时更新也没那么积极;但却没得选。

go-grpc-middleware ⭐️⭐️⭐️

github.com/grpc-ecosys… 官方提供的 gRPC 中间件,可以自己实现内部的一些鉴权、元数据、日志等功能。

go-pilosa ⭐️⭐️⭐️

github.com/pilosa/go-p… 是一个位图数据库的客户端,位图数据库的场景应用比较有限,通常是有标签需求时才会用到;比如求 N 个标签的交并补集;数据有一定规模后运营一定会提相关需求;可以备着以备不时之需。

pb ⭐️⭐️⭐️

github.com/cheggaaa/pb 一个命令行工具进度条,编写命令行工具时使用它交互会更优雅。

总结

最后我汇总了一个表格,方便查看:

名称 类型 功能 星级
Gin 业务开发 HTTP 框架 ⭐️⭐️⭐️⭐️⭐️
GORM 业务开发 ORM 框架 ⭐️⭐️⭐️⭐️⭐️
errors 业务开发 异常处理库 ⭐️⭐️⭐️⭐️⭐️
zorolog 业务开发 日志库 ⭐️⭐️⭐️⭐️⭐️
excelize 业务开发 Excel相关需求 ⭐️⭐️⭐️⭐️⭐️
now 业务开发 时间处理 ⭐️⭐️⭐️⭐️️
Decimal 业务开发 精度处理 ⭐️⭐️⭐️⭐️️
configor 业务开发 配置文件 ⭐️⭐️⭐️⭐️️
go-cache 业务开发 本地缓存 ⭐️⭐️⭐️
copier 业务开发 数据复制 ⭐️⭐️⭐️️️
env 业务开发 环境变量 ⭐️⭐️⭐️️️
user_agent 业务开发 读取 user-agent ⭐️⭐️⭐️️️
phonenumbers 业务开发 手机号码验证 ⭐️⭐️⭐️️️
gomonkey 基础工具 mock工具 ⭐️⭐️⭐️⭐️⭐
goconvey 基础工具 单测覆盖率 ⭐️⭐️⭐️⭐️⭐
dig 基础工具 依赖注入 ⭐️⭐️⭐️⭐️⭐
cobra 基础工具 命令行工具 ⭐️⭐️⭐️⭐
cli 基础工具 命令行工具 ⭐️⭐️⭐️⭐
BloomRPC 基础工具 gRPC 调试客户端 ⭐️⭐️⭐️⭐
redis 基础工具 Redis 客户端 ⭐️⭐️⭐️⭐
elastic 基础工具 elasticsearch 客户端 ⭐️⭐️⭐️⭐
resty 基础工具 http 客户端 ⭐️⭐️⭐️⭐
pulsar-client-go 基础工具 Pulsar 客户端 ⭐️⭐️⭐️
go-grpc-middleware 基础工具 gRPC 中间件 ⭐️⭐️⭐
go-pilosa 基础工具 pilosa 客户端 ⭐️⭐️⭐️
pb 基础工具 命令行工具进度条 ⭐️⭐️⭐️

星级评分的规则主要是看实际使用的频次。

最后夹带一点私货(其实也谈不上)
文中提到了我们内部有基于以上库整合了一个业务开发框架;也基于该框架上线了大大小小10几个项目,改进空间依然不少,目前还是在快速迭代中。

大概的用法,入口 main.go:


最后截取我在内部的分享就概括了整体的思想--引用自公司一司姓同事。

也许我们内部经过多次迭代,觉得有能力开放出来给社区带来一些帮助时也会尝试开源;现阶段就不嫌丑了。

这些库都是我们日常开发最常用到的,也欢迎大家在评论区留下你们常用的库与工具。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Terraform 基于Azure上使用模块(二)

发表于 2021-11-02

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

四、创建模块

在Terraform中创建模块非常容易;我们所需要的只是输入变量和资源的标准配置。在该示例中,我们将为存储帐户创建我们的第一个模块。

我们将首先创建模块文件夹,然后在另一个Terraform配置中引用该模块。我们将首先使用以下内容的文件夹层次结构:

图片.png

复制main.tf和Variables.tf配置的代码并创建每个文件。我们将根据上面的目录结构放置每个文件。

Variables.tf文件包含我们的输入变量。输入变量是我们的模块接受自定义其部署的参数。对于我们的存储帐户模块,我们可以通过接收存储帐户名称,位置和资源组的输入来保持如此简单:

Variables.tf

1
2
3
4
5
6
7
8
9
10
11
12
13
ini复制代码variable "saname" {
type = string
description = "Name of storage account"
}
variable "rgname" {
type = string
description = "Name of resource group"
}
variable "location" {
type = string
description = "Azure location of storage account environment"
default = "westus2"
}

main.tf文件包含用于创建存储帐户的代码。在该示例中,我们只有一小一组用于我们的存储帐户的参数,以保持简单。

但是,在实际生产环境中,我们可能希望实现网络策略以及日志记录选项。然后,我们可以使用我们的模块来定义要配置所有存储帐户的“标准”,以便配置所有存储帐户:

main.tf

1
2
3
4
5
6
7
8
ini复制代码resource "azurerm_storage_account" "sa" {
name = var.saname
resource_group_name = var.rgname
location = var.location
account_tier = "Standard"
account_replication_type = "GRS"

}

接下来,我们将在TerraformDemo文件夹的根目录中创建另一个Main.TF文件,该文件将引用我们新创建的存储帐户模块目录:

图片.png

在根模块上的main.tf文件,我们去调用子模块。在块内部,我们需要通过声明源参数来引用我们使用的模块。在此示例中,我们仅从模块子文件夹中引用模块,因此路径为./modules/storage-Account。

我们还需要为您的存储帐户模块提供任何所需的可变输入。这些是我们在存储帐户模块目录中的Variables.tf文件中创建的相同变量:

根模块main.tf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ini复制代码provider "azurerm" {
version = "1.38.0"
}

#create resource group
resource "azurerm_resource_group" "rg" {
name = "rg-MyFirstTerraform"
location = "westus"
}

#Create Storage Account
module "storage_account" {
source = "./modules/storage-account"

saname = "statfdemosa234"
rgname = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
}

在我们的main.tf文件中,我们还包括Azurerm提供者块。最好的做法是在根模块文件处指定提供程序;这样,调用的所有模块将继承此提供商。在创建模块时,尽量不尽可能地在模块本身内的提供程序。这可能导致进一步的复杂性并使模块很脆弱。


少年,没看够?点击石头的主页,随便点点看看,说不定有惊喜呢?欢迎支持点赞/关注/评论,有你们的支持是我更文最大的动力,多谢啦!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

操作系统——进程调度算法

发表于 2021-11-01

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」。

进程调度有很多种算法吗,我们评价一个进程调度算法是否优秀有以下几个标准。

进程调度算法的准则

  1. CPU利用率:指CPU “忙碌”的时间占总时间的比例。
  2. 系统吞吐量:单位时间内完成作业的数量(总共完成了多少道作业/总共花了多少时间)。
  3. 周转时间,是指从作业被提交给系统开始,到作业完成为止的这段时间间隔(作业完成时间–作业提交时间)。操作系统更关心平均周转时间 = 各作业周转时间之和/作业数

一个周转时间分为四个阶段:

  1. 作业在外存后备队列上等待作业调度(高级调度)的时间、
  2. 进程在就绪队列上等待进程调度(低级调度)的时间、
  3. 进程在CPU上执行的时间、
  4. 进程等待I/O操作完成的时间。

在周转时间的基础下,我们提出带权周转时间和平均带权周转时间,带权周转时间是 =
作业周转时间 / 作业实际运行时间。 平均周转时间上面已经说了注意与平均带权周转时间区分,平均带权周转时间 = 各作业带权周转时间之和 / 作业数。

看起来很难以理解,不过我们可以通过计算考虑,作业周转时间一定,那么作业本身所需的运行时间越长,带权周转时间越少,带权周转时间和周转时间一样,都是时间越少用户的满意度就会越高。而平均带权周转时间在带权周转时间和一定的时候,是作业数越多周转时间越少,而作业数越多代表单个作业所需时间越少,换句话说短作业用户会更加的满意。

通过以上标准,我们就可以评价一个进程调度算法的好坏

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

捋一捋Python中的List(上) 什么是list? li

发表于 2021-11-01

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」

正式的Python专栏第26篇,同学站住,别错过这个从0开始的文章!

前面学委分享了Tuple,谈到了Tuple跟列表list的关系。

这次我们转移视线到list!

什么是list?

list 是一个序列!一串数据,这个串可以追加数据。

我们可以把它看成大型商场内,儿童游玩串串车,它就像一趟一趟车厢一样,可以挂上去(还能增加车厢)。

这样tuple(元组)就是焊死了的串串车!

在数据结构中,我们也学习过链表,某种程度上list就是python给出的一个实现。

它可以无限的存放数据,并通过下标(从0开始计数)获取特定位置的元素。

说这么多我们看看代码,感受一下:

1
2
ini复制代码list_a = [1, 2, 3]
list_b = ['hello','lei','学委', 666]

上面就是python中的list。

list 可以进行哪些操作呢?

我们前面tuple试过 + 和 *, 这些list能做吗?

答案是肯定的。

这里学委复制了前面tuple的代码进行更改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ini复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/10/31 10:36 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : list_demo.py
# @Project : hello


a = [1, 2, 3]
print(a[0])

t = list(a)
print(t[0:2])
print(type(t[0:2]))
# <class 'list'>
t[0] = 22 # list可以修改对应下标的值!
a[0] = 22
mylist = list([a, [2, 3]])
print(mylist)
([22, 2, 3], [2, 3])
mylist[0][0] = 100 #这个可以!真可以!
print(mylist)
print(type(mylist))

动态长度参数传递

1
2
3
4
5
6
7
8
scss复制代码
def show_info_v2(name, title, *info):
print("姓名为:", name)
print("职称为:", title)
print("其他评价:", info)


show_info_v2('雷学委', '搬砖大师', "热爱技术", "热爱生活")

参数是否会被函数攥改?

我们看看下面的程序即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
python复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/10/24 11:39 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : func_call.py
# @Project : hello


def compute_v1(list):
sum = 0
for x in list:
sum += x
list = list + [sum]
print("新地址:", id(list))
return sum


def compute_v2(list):
sum = 0
for x in list:
sum += x
list[0] = list[0] * 100
return sum


_list = [1, 2, 3, 4, 5]
print("调用计算函数v1之前:", _list)
print("调用计算函数v1之前内存地址:", id(_list))
print(compute_v1(_list))
print("调用计算函数v1之后:", _list)
print("调用计算函数v1之后内存地址:", id(_list))

_list = [1, 2, 3, 4, 5]
print("调用计算函数v2之前:", _list)
print("调用计算函数v2之前内存地址:", id(_list))
print(compute_v2(_list))
print("调用计算函数v2之后:", _list)
print("调用计算函数v2之后内存地址:", id(_list))

这是代码运行效果:

屏幕快照 2021-11-01 下午11.39.26.png

我们是可以修改list的元素的。

list 这种对象能执行啥操作?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ruby复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/10/31 10:36 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : list_demo2.py
# @Project : hello


list = [3, 3, 3]
# new_list = list - list #TypeError: unsupported operand type(s) for -: 'listle' and 'listle'
new_list = list + list
print(new_list)

# 学委还是很喜欢三连的,666
new_list = list * 3
print("三连开光过的list:", new_list)
# new_list = list / 3 # 不支持下次一定啊!

print("'666' in new_listle ? ", '666' in new_list)

下面是运行效果:

屏幕快照 2021-11-01 下午11.44.33.png

可以看到,我们之前在tuple中的操作list也一一支持了。

tuple 转list

直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ruby复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/10/31 10:36 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : list_demo2.py
# @Project : hello


mylist = [3, 3, 3]

new_list = [x * 2 for x in mylist]
print("加倍过的list:", new_list)

# 从这里开始展示tuple 转list

tup = (6, 6, 6)
new_list = list(tup)
print("把list转list: ", new_list)
if 6 in new_list:
print("new_list has 6 !")

说这么多,好像漏了点啥?遍历列表还没有展示呢。

这个非常简单,随手就来

1
2
3
css复制代码list = [ 1, 2, 3]
for x in list:
do_on_value(x)

就这样,其实上面的展示代码涵盖了,但是没有特别说出来。

就是这一句 ‘new_list = [x * 2 for x in mylist]’ , 直接遍历列表并把每个函数的值都x2生成的元素构成新列表。

总结

list还有很多功能,我们下篇再继续看,这是tuple介绍的文章:juejin.cn/post/702518…

我们看到list 和 tuple 设计上有一定的对称性!

前者用’[]’来包围元素,后则用’()’来包围元素, 操作上也很相似,甚至可以说非常一致!

对了,喜欢Python的朋友,请关注学委的 Python基础专栏 or Python入门到精通大专栏

持续学习持续开发,我是雷学委!

编程很有趣,关键是把技术搞透彻讲明白。

欢迎关注微信,点赞支持收藏!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

设计模式-建造者模式及应用

发表于 2021-11-01

在系统开发中,往往会有创建一个复杂对象的需求。这个复杂对象由多个子部件组合而成。例如我们现在需要组装一台计算机,计算机由CPU、主板、硬盘、内存、显示器、鼠标、键盘……等组装而成,而选择需要根据我们真实的使用场景去组装不同配置的电脑。例如同一个公司的同事,不同岗位对计算机配置的需求也不同,研发同学的配置可能会高,而文职类工作的同学配置就无须那么高。

公司IT运维部门的同学就需要按照不同需要组装不同配置的电脑。各个组件灵活替换,按需装配。类似于这种场景产品的创建,下面介绍的建造者模式,就能很好的描述此类场景。

建造者模式

建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。此外建造者模式是一种对象创建型模式。

结构与实现

建造者(Builder)模式由产品、抽象建造者、具体建造者、指挥者等 4 个要素构成,现在我们来分析其基本结构和实现方法。

  • 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
  • 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
  • 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
  • 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

结构图如下:
在这里插入图片描述

实例代码:
使用本文开始时候介绍的组装电脑的案例,来完成建造者模式的案例;

  • 产品角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
java复制代码public class Computer {

private String cpu;

private String disk;

private String ram;

private String screen;

private String mouse;

private String keyboard;

public String getCpu() {
return cpu;
}

public void setCpu(String cpu) {
this.cpu = cpu;
}

public String getDisk() {
return disk;
}

public void setDisk(String disk) {
this.disk = disk;
}

public String getRam() {
return ram;
}

public void setRam(String ram) {
this.ram = ram;
}

public String getScreen() {
return screen;
}

public void setScreen(String screen) {
this.screen = screen;
}

public String getMouse() {
return mouse;
}

public void setMouse(String mouse) {
this.mouse = mouse;
}

public String getKeyboard() {
return keyboard;
}

public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}

@Override
public String toString() {
return "Computer [cpu=" + cpu + ", disk=" + disk + ", ram=" + ram + ", screen=" + screen + ", mouse=" + mouse
+ ", keyboard=" + keyboard + "]";
}
}
  • 抽象建造者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
java复制代码public abstract class AbstractBuilder {

/**
* 具体产品
*/
protected Computer computer = new Computer();

/**
* 配置cpu
*/
protected abstract void setCpu();

/**
* 配置disk
*/
protected abstract void setDisk();

/**
* 配置 ram
*/
protected abstract void setRam();

/**
* 配置screen
*/
protected abstract void setScreen();


/**
* 配置keyboard
*/
protected abstract void setKeyboard();


/**
* 配置mouse
*/
protected abstract void setMouse();


/**
* 返回创建具体对象
* @return
*/
public Computer getResult() {
return computer;
}
}
  • 具体建造者1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
java复制代码public class ConcreteBuilderApple extends AbstractBuilder{

@Override
protected void setCpu() {
computer.setCpu("M1");
}

@Override
protected void setDisk() {
computer.setDisk("Seagate 2TB");
}

@Override
protected void setRam() {
computer.setRam("Kingston 16G");
}

@Override
protected void setScreen() {
computer.setScreen("Sasung");
}

@Override
protected void setKeyboard() {
computer.setKeyboard("apple keyboard");
}

@Override
protected void setMouse() {
computer.setMouse("apple mouse");
}
}
  • 具体建造者2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
java复制代码public class ConcreteBuilderHuawei extends AbstractBuilder{

protected void setCpu() {
computer.setCpu("Intel");
}

protected void setDisk() {
computer.setDisk("Sasung 1TB");
}

protected void setRam() {
computer.setRam("AMD 8G");
}


protected void setScreen() {
computer.setScreen("Sony");
}


protected void setKeyboard() {
computer.setKeyboard("Huawei");
}

protected void setMouse() {
computer.setKeyboard("Logitech");
}
}
  • 指挥者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码public class ComputerDirector {

AbstractBuilder builder = null;

public ComputerDirector(AbstractBuilder builder){
this.builder = builder;
}

// 创建实例
public Computer builderComputer() {
builder.setCpu();
builder.setDisk();
builder.setRam();
builder.setRam();
builder.setMouse();
builder.setKeyboard();
builder.setScreen();
return builder.getResult();
}
}
  • 使用方
1
2
3
4
5
6
7
8
9
10
java复制代码public class Client {

public static void main(String[] args) {
ConcreteBuilderApple concreteBuilderApple = new ConcreteBuilderApple();
// 假设生产苹果电脑
ComputerDirector computerDirector = new ComputerDirector(concreteBuilderApple);
Computer macBookPro =computerDirector.builderComputer();
System.out.println(macBookPro.toString());
}
}

执行结果:

1
java复制代码Computer [cpu=M1, disk=Seagate 2TB, ram=Kingston 16G, screen=Sasung, mouse=apple mouse, keyboard=apple keyboard]

案例结构图:
在这里插入图片描述

使用场景

当需要创建的产品具备复杂创建过程时,可以抽取出共性创建过程,然后交由具体实现类自定义创建流程,使得同样的创建行为可以生产出不同的产品,分离了创建与表示,使创建产品的灵活性大大增加。

建造者模式主要适用于以下应用场景:

  • 相同的方法,不同的执行顺序,产生不同的结果。
  • 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
  • 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
  • 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。

项目中经常使用的,静态类的方式实现建造者

还以我们上面的案例来演示,需要变动产品类;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
java复制代码public class Computer2 {

private String cpu;

private String disk;

private String ram;

private String screen;

private String mouse;

private String keyboard;

public String getCpu() {
return cpu;
}

public void setCpu(String cpu) {
this.cpu = cpu;
}

public String getDisk() {
return disk;
}

public void setDisk(String disk) {
this.disk = disk;
}

public String getRam() {
return ram;
}

public void setRam(String ram) {
this.ram = ram;
}

public String getScreen() {
return screen;
}

public void setScreen(String screen) {
this.screen = screen;
}

public String getMouse() {
return mouse;
}

public void setMouse(String mouse) {
this.mouse = mouse;
}

public String getKeyboard() {
return keyboard;
}

public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}

@Override
public String toString() {
return "Computer [cpu=" + cpu + ", disk=" + disk + ", ram=" + ram + ", screen=" + screen + ", mouse=" + mouse
+ ", keyboard=" + keyboard + "]";
}

public static class Builder{
Computer2 product = new Computer2();

public Builder setCpu(String cpu) {
product.cpu = cpu;
return this;
}

public Builder setRam(String ram) {
product.ram = ram;
return this;
}

public Builder setDisk(String disk) {
product.disk = disk;
return this;
}


public Builder setScreen(String screen) {
product.screen = screen;
return this;
}

public Builder setMouse(String mouse) {
product.mouse = mouse;
return this;
}

public Builder setKeyboard(String keyboard) {
product.keyboard = keyboard;
return this;
}


public Computer2 getResult() {
return product;
}
}
}
  • 使用方:
1
2
3
4
5
6
7
java复制代码public class Client2 {

public static void main(String[] args) {
Computer2 computer2 = new Builder().setCpu("Intel").setDisk("wd").setRam("AMD").setScreen("索尼").getResult();
System.out.println(computer2);
}
}

执行结果

1
java复制代码Computer [cpu=Intel, disk=wd, ram=AMD, screen=索尼, mouse=null, keyboard=null]

通过静态内部类(指挥者、建造者)构造具体产品的方式,简化建造者模式的使用。使得代码更简介,缺点是违反开闭原则,产品属性更改,内部类也要更改。

优缺点

优点:

  • 封装性好,讲复杂对象的构建过程与表示分开
  • 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  • 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

缺点:

  • 产品的组成部分必须相同,这限制了其使用范围。
  • 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

建造者模式在源码中的使用

1.建造者模式在MyBatis源码中的应用
MyBatis 中 SqlSessionFactoryBuiler 类用到了建造者模式,SqlSessionFactory由具体SqlSessionFactoryBuilder产生;下面分析一下源码部分

1
2
3
java复制代码public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

这里DefaultSqlSessionFactory创建需要Configuration对象,这里SqlSessionFactoryBuilder 充当了我们的指挥者;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();

try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}

Configuration对象是一个复杂对象,具体的创建Configuration的建造者为XMLConfigBuilder,抽象建造者为BaseBuilder,具体建造者的构建过程,通过parseConfiguration()方法,构造配置项,parse()方法获取我们构造的 Configuration对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
java复制代码// 构造过程
private void parseConfiguration(XNode root) {
try {
Properties settings = this.settingsAsPropertiess(root.evalNode("settings"));
this.propertiesElement(root.evalNode("properties"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectionFactoryElement(root.evalNode("reflectionFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}

// 获取结果
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

夜读Java虚拟机-1 前言 第一部分:走进Java (1章

发表于 2021-11-01

中国有句古话说到:练拳不练功,到老一场空。
对于一个写代码的人来说,基本功同样也是非常重要。
《深入理解Java虚拟机》作为我们Java程序员的必读书籍,相信大家很多都已经看过了。但是大多数同学都是止步于第一章,今天想跟大家一起找到一个正确的学习方式来学这本书。

如何学习?如果我们能够在学习之前就get到作者通过这本书希望告诉我们的要点有哪些,我们的学习就有了抓手,学习的过程就不会盲从。

作者再开始正文介绍之前也大致对于自己的书中的知识点进行了简单的罗列。我们正好在读书之前把这些要点作为今后学习的重中之重。这可能会是一个比较好的学习方式。

废话少说,开始列举

前言

为什么我们要学习Java虚拟机?

  • 提升物理硬件的性能,并无法等比例的提升程序的运行性能和并发能力。
  • 为了达到“所有硬件提供一致的虚拟平台”的目的,Java虚拟机牺牲了一部分硬件相关的性能特性。(ps:这也是第一点问题的原因所在)
  • 如果开发人员不了解虚拟机诸多技术特性的运行原理,就无法写出最适合虚拟机运行和自优化的代码。
  • 商用的高性能Java虚拟机都提供了相当多的优化参数和手段,帮助我们满足日常开发中对西鞥能和稳定性的要求。

因此学习Java虚拟机,是程序员成长之路上必然需要去越过的一道坎。

今天我们开始学习,让我们跟着作者的思路梳理一下我们的学习要点

第一部分:走进Java (1章)

主要了解Java的语言体系,发展趋势,知识面的扩充。

第二部分:自动内存管理(2-5章)

第2章

1、JVM 的内存是如何划分的。(列出自己学习的成果:脑图或结构图)

2、JVM的内存各个区域出现内存溢出异常的常见原因(整理罗列出来)

第3章

1、垃圾收集算法分类及特性。(弄懂并输出自己的学习成果)

2、Hotspot虚拟机中提供的几款垃圾收集器的特点 及 运作原理

3、ZGC和Shenandoah 两款低延迟全并发收集器的原理解析。(ps:这是未来垃圾收集器的发展方向)

4、Hotspot中垃圾收集器实现的几个关键技术点、解决跨代引用的记忆集与卡表、解决并发标记的增量更新 和 原始快照算法、内存读写屏障技术。

第4章

1、JDK发布的基础命令行工具 与 可视化的故障处理工具使用方法 主要是学会使用并实操.

2、JHSDB的使用讲解、JFR和JMC的工作原理和使用方法 的介绍,以及对部分JDK外部的工具(如JIT Watch)的简要介绍.

第5章

1、通过具体案例亲身实战练习

此处应该给出自己的练习收获,和在练习中用到的前面的知识点有哪些.做一个阶段性复盘

第三部分:虚拟机执行子系统(6-9章)

第6章(作者建议本章适合查阅多于适合阅读)

1、class文件结构中的各个组成部分。每部分的定义、数据结构和使用方法。(输出学习成果)

2、实战演示class的数据是如何存储和访问的。(注重这个过程)

class文件格式是虚拟机的基础知识。枯燥但是无法回避,除了一些常规知识外,我们还应该关注在jdk版本升级过程中,新引入的新概念新属性的实际作用、属性等。

第7章

1、(重要)类加载的过程:“加载”,“验证”,“准备”,“解析”,“初始化”。重点理解这五个过程JVM虚拟机分别进行了哪些动作。输出成果

2、类加载的工作原理 和 对于虚拟机的意义

3、jdk12版本中类加载各个过程的细节变动都有哪些,思考为什么要这么改变。

4、搞清楚Java模块化系统是什么,他是如何影响类加载部分的逻辑的。

第8章

1、虚拟机执行代码的过程。画出流程图或者时序图。

2、执行的细节:如何找到正确的方法,如何执行方法内的字节码,执行方法内的字节码时涉及到的内存结构有哪些

3、概括除Java虚拟机执行子系统的概念模型。整理出结构图

4、Java虚拟机对于动态类型语言的支持如何做到,在jdk 的不同版本中如何增强

第9章

1、通过类加载和执行子系统的实际案例,学习值的借鉴的思路。

2、通过一个实战练习加深对于之前的理论知识的理解。此处应该自己概括出来实战中用到的理论知识都有哪些。第二个复盘

3、弄清楚lambda表达式、模块化、动态语言这些新技术在我们的日常开发中的用法,这样用的好处。

第四部分 程序编译与代码优化(10-11章)

第10章

1、脑子里熟悉 Java程序从源码编译成字节码 从字节码编译成本地机器码 的两个过程。形成整体上的概念。作者认为这个过程实际上就是一个传统编译器所执行的编译前端、后端的过程。看完之后需要自己问自己,这个概念是否可以弄清楚。

2、Java中的泛型、主动拆箱、装箱、条件编译 这些语法糖的前因后果,这里需要分别列清楚他们的前因后果。写出来。

3、实战任务:完成使用插入式注解处理器来完成一个检车程序命名规范的编译器插件。

4、针对泛型:不同语言对于泛型的实现方式,Java泛型出现的历史背景 和 使用类型擦除来实现泛型所带来的一些限制。

5、未来可能在Java中出现的值类型有哪些?为什么未来需要这些类型的出现。

第11章

1、虚拟机的热点探测法

2、Hotspot的即时编译器、编译触发条件

3、如何从虚拟机外部观察和分析即时编译的数据和结果

4、弄清楚书中几种常见的编码期优化技术。

5、弄清楚 提前编译器是什么,Hotspot中新增加的Graal编译器是什么。

6、JVMCI接口是什么。重在通过编译器的实战内容来认识他。

第五部分:高效并发(12-13章)

Java语言 和 虚拟机 提供了原生的、完善的多线程支持,使得他天生就适合开发多线程并发的应用程序。但是了解并发的内幕和实现原理也是必不可少的。

第12章

1、通过Java内存模型的结构和操作,了解原子性、可见性和有序性这些特性在Java中的体现。

2、弄清楚先行发生原则的规则 和 使用。

3、Java语言如何实现了线程。

4、提前预习一下Java协程的新并发模型的内容

第13章

1、线程安全涉及到的概念 和 分类,需要自己画出脑图,整体出来清晰的逻辑脉络

2、Java中 同步的实现方式,以及虚拟机底层的运作原理。

3、虚拟机为了实现高效并发所做的一系列锁优化措施是什么,这些措施是如何实现的,解决的关键问题是什么?

以上就是周志明前辈 希望我们能够看到的关于Java虚拟机的一些要点内容,其中也加入了我自己的一些理解。希望我们可以通过这些问题关键点来学习这些专业底层知识,更好的理解更有效的掌握内容。

另外作者还为我们推荐了一些学好虚拟机需要阅读的书籍。

1、《Java虚拟机规范》

2、《Java语言规范》

3、《垃圾回收算法手册:自动内存管理的艺术》

4、《Java性能优化权威指南》

本文完。晚安朋友!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Zookeeper大白话入门,真的很通俗易懂

发表于 2021-11-01

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

前言

今天本来是打算和大家分享一下,Zookeeper作为分布式锁的使用方式,不过斟酌之后,最后决定先来几篇基础性文章,系统的介绍Zookeeper的基础以及核心知识点,最后再来介绍它的应用场景以及集群部署的知识。

对分布式系统有兴趣的小伙伴,可以和花哥一块学习,我们一起勉励,共同进步,也可以在评论区分享一下你在工作中遇到了哪些问题,贴出来大家一起探讨。

zookeeper是什么

首先,我们打开Zookeeper官网(zookeeper.apache.org/),官方在首页第二段中给出定义,简单概括就是:ZooKeeper 是一个用于分布式应用程序的高性能协调服务,提供集中式信息存储服务。

确实,上面这句不像人话。

只看定义确实很难理解这玩意到底是什么,我们先看一下他的结构。Zookeeper内部维护的是类似于文件系统的树形结构(文件和目录),在这个结构中,每一个子目录(如/app1)被称为znode,我们可以对znode执行增加删除等操作,znode下还可以继续增加、删除子znode。

image.png

分布式协调服务是什么

上面说到ZooKeeper 是一个用于分布式应用程序的高性能协调服务,那小伙伴们知道什么是分布式协调服务吗?

单机服务我们都知道,是将整个服务部署在一台机器上,每一次接口涉及到的多个步骤都在单台机器上处理,这样做便于开发部署,但是单台系统处理能力有限,对于并发度高的系统,如淘宝京东等系统,是完全不适用的,这时候就需要分布式系统。

在分布式系统中,将原来单机服务中一次请求涉及的多个步骤拆分,由多台机器上的服务共同完成,但是我们依旧需要保证步骤之间的顺序性,这个时候我们要处理不同机器上进程间的资源竞争,因此就需要一种机制,在分布式系统之间进行协调。我们通常就会把分布式系统需要协调管理的公共部分抽出来作为基础服务供大家使用,这也就是分布式协调服务。

zookeeper能做什么

我们可以通过Zookeeper实现很多功能,比如分布式统一配置中心,服务注册中心、分布式锁等功能。

Zookeeper的应用案例也比较广泛,如:Hbase中服务协调、dubbo的服务注册、Mycat集群管理、配置管理等。

以配置中心为例,在分布式系统中,部署有三台服务,如果修改配置文件,需要将三台服务中的配置都一一修改,但是引入zookeeper统一配置后,我们只要将配置放到zookeeper的某一个目录节点(/app1)中,然后这三台服务都去监听/app1这个znode,只要配置发生变化,zookeeper就会发出通知,这样每一台服务都会收到通知并应用最新的配置。

image.png

如何学习

无论哪一项技术,官网都是最权威的信息来源,这里简单介绍一下官网的学习方法。

输入地址zookeeper.apache.org/index.html可以登录到zookeeper官网:

image.png

点击 Learn about可以打开具体的文档。

  • Developers:开发者
+ [API Docs](https://zookeeper.apache.org/doc/current/apidocs/zookeeper-server/index.html):API文档
+ [Programmer's Guide](https://zookeeper.apache.org/doc/current/zookeeperProgrammers.html) :开发者指南
+ [ZooKeeper Java Example](https://zookeeper.apache.org/doc/current/javaExample.html):Java案例
+ [ZooKeeper Recipes](https://zookeeper.apache.org/doc/current/recipes.html):常见问题的高级解决方案
  • Administrators & Operators:管理员使用
+ [Administrator's Guide](https://zookeeper.apache.org/doc/current/zookeeperAdmin.html):管理员指南
+ [ZooKeeper CLI](https://zookeeper.apache.org/doc/current/zookeeperCLI.html):命令行
+ [ZooKeeper Tools](https://zookeeper.apache.org/doc/current/zookeeperTools.html):工具
+ [ZooKeeper Monitor](https://zookeeper.apache.org/doc/current/zookeeperMonitor.html):监控
  • ZooKeeper Internals:原理相关

image.png

写在最后

今天主要介绍了Zookeeper是什么、能做什么、如何去学习,明天我们会以动手为主,如何在linux、windows中部署,已经常见的基础命令。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

前端必了解的数据库基础 插入数据 从已有数据表向另一个表追加

发表于 2021-11-01

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」

小小前端有时候也需要写一写API,所以对平时常用的 SQL 语句简单的总结了一下。

插入数据

  1. 向已有表插入数据
1
2
sql复制代码insert into [DB_name].[dbo].[table_name]
values('column1', 'column2')
  1. 插入数据并返回该条数据的自增列ID
1
2
3
4
5
6
7
8
9
sql复制代码-- 返回本次语句块中插入数据的最后一个自增量ID
insert into [DB_name].[dbo].[table_name]
values('column1', 'column2')
select SCOPE_IDENTITY();

-- 返回整个数据库中插入数据的最后一个自增量ID
insert into [DB_name].[dbo].[table_name]
values('column1', 'column2')
select @@IDENTITY

[注意] 插入时,忽略自增列的值

从已有数据表向另一个表追加数据

1
2
sql复制代码insert into dbo.a
select * from dbo.b

将查询数据插入临时表(临时表之前不存在)

1
2
3
sql复制代码select * 
into #tempTable
from dbo.b

删除数据

1
2
sql复制代码delete from [DB_name].[dbo].[table_name]
where colName = 'columnValue' and colName = 'columnValue'

查询数据

1
2
3
sql复制代码select top 1000 *
from [DB_name].[dbo].[table_name]
order by colName

修改数据

1
2
3
sql复制代码update [DB_Name].[dbo].[Table_Name]
set column1 = newValue, column2 = newValue
where id = '1'

批量替换特定列的特定字符串

1
2
3
sql复制代码update [dbo].[Table_Name]
set list_URL = replace(list_URL, 'https://baidu.com', 'https://google.com')
where list_URL LIKE '%baidu.com%'

创建表

1
2
3
4
5
6
sql复制代码CREATE TABLE [DB_Name].[dbo].[Table_Name](
id int identity(1, 1), --自增列,从1开始,递增量为1
name char NOT NULL,
age int NOT NULL,
sex boolean NOT NULL
)

修改表结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sql复制代码-- 新增字段
ALTER TABLE [Table_Name] ADD [Column_Name] NVARCHAR(50) NULL
-- 在特定位置新增字段
ALTER TABLE [Table_Name] ADD [Column_Name] NVARCHAR(50) NULL [FIRST | AFTER] [Column_Name]
-- 新增列的时候添加默认值
ALTER TABLE {TABLENAME}
ADD {COLUMNNAME} {TYPE} {NULL|NOT NULL}
CONSTRAINT {CONSTRAINT_NAME} DEFAULT {DEFAULT_VALUE}
WITH VALUES

-- 删除字段
ALTER TABLE [Table_Name] DROP COLUMN [Column_Name]

-- 修改字段
ALTER TABLE [Table_Name] ALTER COLUMN [Column_Name] NVARCHAR(50) NULL

-- 修改字段长度
ALTER TABLE [Table_Name] ALTER COLUMN [Column_Name] varchar(255)

SQL表连接

  • [INNER] JOIN: 内连接, 如果表中有至少一个匹配,则返回行
  • LEFT JOIN: 左连接, 即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN: 右连接, 即使左表中没有匹配,也从右表返回所有的行
  • FULL JOIN: 全连接, 只要其中一个表中存在匹配,就返回行

Table Person

Id_P Name City
1 Winter London
2 Gross New York
3 Mars Beijing

Table Orders

Id_O OrderNo Id_P
1 11111 3
2 22222 3
3 33333 1
4 44444 1
5 55555 65
  1. 谁订购了产品,分别订购了什么产品?
1
2
3
sql复制代码select Person.Name, Orders.OrderNo
from Person, Orders
where Person.Id_P = Orders.Id_O

the Results:

Name OrderNo
Winter 33333
Winter 44444
Mars 11111
Mars 22222
  1. 列出所有人的订购(inner join)
1
2
3
4
5
sql复制代码select Person.Name, Orders.OrderNo
from Person
inner join Orders
on Person.Id_P = Orders.Id_P
order by Person.Name

the Results:

Name OrderNo
Winter 33333
Winter 44444
Mars 11111
Mars 22222

查看特定表的字段结构信息

使用 INFORMATION_SCHEMA.COLUMNS

1
2
3
4
5
6
7
8
9
sql复制代码-- 实体表
select column_name, data_type, *
from serverName.DBName.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME='Portal_DimList'

-- 临时表
select *
from tempdb.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME like '#tempTableName%'

使用 sp_columns 存储过程

适用于 DBLink 下的其他 Server 下的表

缺点:不能筛选特定需要的字段

1
2
3
4
5
sql复制代码-- 实体表
exec serverName.DBName.DBOwner.sp_columns 'B2B_InvoiceNumber_Flag_New'

-- 临时表
exec [tempdb].[dbo].sp_columns #tempTableName

使用 sys.columns

缺点:获取到的字段类型是数据库里的数字代号,不是可读文字

1
2
3
4
sql复制代码-- 临时表
select *
from tempdb.sys.columns
where object_id=OBJECT_ID('tempdb.dbo.#tempTableName');

查看特定表的信息

1
2
3
sql复制代码select  * 
from INFORMATION_SCHEMA.tables
where TABLE_NAME='Portal_DimList'

插入特殊字符值

1
2
3
4
5
6
7
sql复制代码-- 1. 修改列类型为 'NVARCHAR'
-- 2. 插入数据时,引号前加 "N"

select N"¢"

insert into table_name
values(991, 'm01', 'point', 'Point', 'doubleSum', N'c2¢')

修改 text 类型的列为 ntext 类型

1
2
3
4
5
6
7
sql复制代码-- 先修改列的类型为 varchar(max)
alter table [dbo].[Portal_JSON]
alter column JSON_text [varchar](max) NULL

-- 再将列的类型修改为 ntext
alter table [dbo].[Portal_JSON]
alter column JSON_text ntext NULL

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Git详解系列:Git的前世今生 什么是版本控制 本地版本控

发表于 2021-11-01

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」。

通过此篇文章你将了解到什么是版本控制,以及版本控制系统的演进历程,当然还有最重要的Git是什么,它的三个状态,以及如何安装它。

什么是版本控制

版本控制就是来记录一个或多个文件内容的变化,用来方便查阅某个版本的修改记录。

想象一种场景,你在写一篇文章,写完之后你觉得写的不好,就进行修改,修改完就产生了一个新的文件你叫它2.0版本,过了几天你再看文章发现还要修改,衍生出3.0,4.0等等,突然你又觉得之前那样写很好,你又不记得是在哪改的,这就很头疼。这时候一个可以记录每次改动变化的系统出现了,它可以自动记录每次文章的改动,还能让别人一同操作,版本控制系统就产生了。

本地版本控制系统

本地版本控制大多数都是使用数据库来记录文件每次的变更差异。

image-20211101214408671

这里面最典型的就是RCS,它会在硬盘中保持每次修改的补丁,通过所有的补丁,可以计算出各个版本的异同。

集中化版本控制系统

本地版本控制系统虽然解决了查找变更的麻烦,但是如果想多人操作同一个文件就很难实现,而集中化版本控制系统可以解决这个痛点。这里面典型的例如CVS、Subversion等。它会有一个集中的单一服务器来保存所有文件的修改版本,每当有人想修改就可以从服务器上取出最新文件然后修改后再提交到服务器。

image-20211101215023994

集中式版本控制解决了多人协作的问题,但所带来的隐患就是如果服务器故障,那么所有人都操作不了,并且因为数据存储在服务器,如果磁盘发生损坏,可能就会丢失整个变更历史。那还有更好的版本控制系统吗?接着往下看

分布式版本控制系统

分布式版本控制系统中,每个人的电脑上都是一个完整的版本库,多人协作只需要将双方各自修改的点发送给对方,通过服务器来做中间层,对各自的信息进行交换。任何一处参与协同工作的服务器挂了都不会产生太大的影响,都可以用其中一个人的电脑的本地仓库进行恢复。

image-20211101220310003

Git介绍

Git就属于分布式版本控制系统的一种,它诞生于2005年,发源于Linux开源社区。

Git和其他版本控制系统最主要的区别就在于对数据的处理,其他大部分系统都是以文件变更列表的形式存储数据,例如你更改A文件,它只会记录你这个增量差异的部分,不会再将整个文件再重新完整的记录一遍,每个版本都只记录差异。

image-20211101221057219

Git却是每次都会对当时更改的文件全量保存为一个快照,并保存这个快照的索引,当然如果你这次文件没有任何修改,那么这个新的版本就不会再存储该文件,而是保留一个链接指向之前存储的文件。

image-20211101221319182

Git三种状态

在Git中,文件状态是个非常重要的概念,不同的状态会对应不同的操作。我们就来了解一下这三种状态。

这三种状态分别为已提交,已修改,已暂存。已提交就代表数据已经保存在了本地数据库中,已修改就是文件修改了但是还没来得及保存到本地数据库,已暂存相当于对一个新文件或者修改的文件做了一个标记,将其包含在了下次提交的快照中,等到做提交操作时就会被提交。

这里我们用具体代码演示以下来加深这块的理解:

我新建了一个文件,还没有做任何操作,我们看看它现在是什么状态

image-20211101223015678

这里的untracked就代表还没有被暂存,提示我们进行add操作进行暂存

image-20211101223318774

这里文件状态变成了“Changes to be committed:”,就代表已经已经暂存了,等待被提交。这里我们先不提交,我们对这个文件做个修改,然后看看什么状态

image-20211101223528413

可以看到除了之前的“Changes to be committed:”状态,又多了一个”Changes not staged for commit:”状态,表明文件已经被修改了,但还没有放入暂存区域。现在我们先暂存,然后进行commit操作看看状态会发生什么变化

image-20211101224357119

我们的更改已经提交,提示我们push到远程分支,通过状态的变化相信你对这三种状态有了更清晰的理解,总结一句话就是当你新增或修改一个文件,进行了add操作后就变成了已暂存状态,当你对文件修改但还没add,那就是已修改状态,add后又进行了commit操作那就变成了已提交状态。

安装Git

在Linux安装Git

通过一条sudo apt-get install git就可以直接完成Git的安装,非常简单。

在Mac上安装Git

如果你正在使用Mac做开发,有两种安装Git的方法。

一是安装homebrew,然后通过homebrew安装Git,具体方法请参考homebrew的文档:brew.sh/。

第二种方法更简单,也是推荐的方法,就是直接从AppStore安装Xcode,Xcode集成了Git,不过默认没有安装,你需要运行Xcode,选择菜单“Xcode”->“Preferences”,在弹出窗口中找到“Downloads”,选择“Command Line Tools”,点“Install”就可以完成安装了。

在Windows上安装Git

在Windows上使用Git,可以从Git官网直接下载安装程序,然后按默认选项安装即可。

安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!

安装完成后记得要进行下一步的设置,在命令行输入:

1
2
arduino复制代码$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Kafka系列-Apache Kafka 环境搭建

发表于 2021-11-01

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

Apache Kafka 简介

Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。

Linux 环境安装单机Kafka

2.1 检查Jdk是否安装

java -version 命令, 我的linux环境已经安装了JDK,直接跳到下一步

image.png

2.2 检查zk是否安装

因为Kafka的运行需要依赖zookeeper,所以安装前要检查zookeeper是不已经安装了。
直接去官网下载,然后上传到服务器上.

zk官网: downloads.apache.org/zookeeper/s…

选择Binary downloads,选择版本进行下载

  1. 解压
    [root@VM_0_14_centos local]# tar -xzvf apache-zookeeper-3.6.3-bin.tar.gz
  2. 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
bash复制代码## 1. 复制配置文件
[root@VM_0_14_centos ~]# cd /usr/local/
[root@VM_0_14_centos local]# tar -xzvf apache-zookeeper-3.6.3-bin.tar.gz
[root@VM_0_14_centos local]# cd apache-zookeeper/
[root@VM_0_14_centos apache-zookeeper]# cd conf/
[root@VM_0_14_centos conf]# cp zoo_sample.cfg zoo.cfg
[root@VM_0_14_centos conf]# vi zoo.cfg

## 2. zoo.cfg文件末尾添加
dataDir=/tmp/zookeeper/data
dataLogDir=/tmp/zookeeper/log

## 3. 在tmp目录创建目录
[root@VM_0_14_centos conf]# mkdir /tmp/zookeeper
[root@VM_0_14_centos conf]# mkdir /tmp/zookeeper/data
[root@VM_0_14_centos conf]# mkdir /tmp/zookeeper/log

## 4. 配置环境变量
[root@VM_0_14_centos conf]# export ZK_PATH=/usr/local/apache-zookeeper/
[root@VM_0_14_centos conf]# export PATH=$PATH:$ZK_PATH/bin

## 5. 启动zk
[root@VM_0_14_centos bin]# ./zkServer.sh start
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /usr/local/apache-zookeeper-3.6.3/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

## 6. 验证zk
[root@VM-16-2-centos bin]# ./zkServer.sh status

1635780576(1).png

2.3 安装单节点Kafaka

进入Apache官网 http://kafka.apache.org/downloads.html

选择Binary downloads,选择版本进行下载。一般建议安装1.1 版本之前的,安装的问题比较少。

image.png

2.3.1 上传并解压

  1. tar -xzvf kafka_2.11-1.0.2.tgz
  2. mv kafka_2.11-1.0.2 kafka
  3. mkdir -p /tmp/kafka-logs

2.3.2 修改配置文件

修改kafka的config目录下,server.properties文件,主要修改的地方如下:

broker的全局唯一编号,不能重复

broker.id=1

监听

listeners=PLAINTEXT://VM-16-2-centos:9092
advertised.listeners=PLAINTEXT://VM-16-2-centos:9092
日志目录

log.dirs=/tmp/kafka-logs

配置zookeeper的连接(如果不是本机,需要该为ip或主机名)

zookeeper.connect=localhost:2181

注意:这边hostname我直接取的虚拟机配置的名字, linux 系统可以直接用hostname直接查看。

2.3.3 启动kafka

nohup $KAFKA_HOME/bin/kafka-server-start.sh $KAFKA_HOME/config/server.properties 2>&1 &

好了启动了一下报错,内存不足了, 这台虚拟机安装了太多东西,已经不堪重负了。

image.png

没办法只能重新买一台云服务器了,趁着双11活动搞了一台2C4G的服务器重新安装一下。

  1. 新服务器从JDK开始安装起
  1. 查看系统是否已经安装JDK yum list installed |grep java
  2. 在线安装, yum -y install java-1.8.0-openjdk

image.png

  1. 安装zk

image.png

image.png

  1. 安装kafka
    配置完,这样基本上就是启动OK了

image.png

2.4 测试验证

  1. 创建topic

./kafka-topics.sh –create –zookeeper localhost:2181 –replication-factor 1 –partitions 1 –topic test

image.png

  1. 查看topic

./kafka-topics.sh -list -zookeeper localhost:2181

image.png

  1. 启动生产者

./kafka-console-producer.sh –broker-list localhost:9092 –topic test

image.png

  1. 启动消费者

./kafka-console-consumer.sh –bootstrap-server localhost:9092 –topic test –from-beginning

image.png

参考文档

kafka.apache.org/quickstart

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…439440441…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%