GVKun编程网logo

站群SEO的方法和应用(站群seo技巧)

9

本篇文章给大家谈谈站群SEO的方法和应用,以及站群seo技巧的知识点,同时本文还将给你拓展Go实现安全双检锁的方法和应用场景、JS中Ajax的方法和应用、seo怎么做站群推广(seo站群优化技术)、s

本篇文章给大家谈谈站群SEO的方法和应用,以及站群seo技巧的知识点,同时本文还将给你拓展Go实现安全双检锁的方法和应用场景、JS中Ajax的方法和应用、seo怎么做站群推广(seo站群优化技术)、seo怎么做站群推广(seo站群如何打造)等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

站群SEO的方法和应用(站群seo技巧)

站群SEO的方法和应用(站群seo技巧)

建设站群一直是一个SEO领域比较带有点危险性、运用得当时又很有效的黑帽SEO方法。

我自己没有做过站群,不过有不少朋友、客户在做站群SEO。过去这些年持续观察几个网站站群的SEO效果,凡是以转载、抄袭或所谓伪原创内容为主体内容的站群,无一例外,刚开始往往垄断关键词前十名中的几位,但几个月或几年后,绝大部分站群网站的排名都会消失或大幅下降,一个站群中只能剩下一个效果还不错的。

但也有少数SEO效果保持不错的。这样的站群往往不是同一个行业的网站,而且内容质量问题解决得比较好。

分析观察的站群,有这样几个站群SEO优化要注意的地方。

尽量隐蔽站群

我发现搜索引擎在大部分情况下,不会把同一个人或同一个公司的多个网站,在同一个关键词下给几个好的排名。

如果搜索引擎发现很多站属于同一个人或公司,就算优化的都不错,也只给一个网站好的排名。如果站群做的太过分,可能这一个都不能幸免。

所以要想办法隐藏站群之间的关系。域名Whois信息使用不同的名字,不同的地址,不同的电子邮件,网站放在不同的服务器上,使用不同的IP地址,尽量不要在网站上放相同的联系地址,电话。尽量不要使用浏览器工具条,也别把这些网站都放在同一个站长平台帐号中,等等。

尽管可以采取隔离措施,但要真正隐藏站群是不容易的,经常会因为一些细节暴露网站之间的关系。有的站群多的公司,管理不同网站的团队也是分开的。

站群之间不要交叉链接

做站群的时候很容易就把这些网站全部链接起来,希望这些站都得到好的排名,PR能在各个站之间传递。

不过这很容易暴露站群的所有人,而且容易被判定为链接工厂。

应该选择一个网站做为主网站,其他网站链接向这个主网站,但其他次要网站之间不要链接,把这些网站的能量都集中在主网站身上。

网站内容要有所不同

我发现很多人喜欢用完全一样的网站内容放在多个域名上,这是很不恰当的做法。

建议选择一个做主网站,其他网站都做301转向到主网站。这种情况不会被当作站群,也没有站群的效果,不过可以起到保护相关域名的作用。

如果要把这些域名做成站群,在内容上要有所区别。比如说主网站是关于汽车的,那么可以做一系列站群,一个写轮胎,一个写发动机,一个写配件。这些具有相关内容的站群,在适当的地方链接向主网站。

因为内容不同,不会被当作复制内容。而且来自同一个网站的多个链接效果没有来自不同域名的链接效果好。这些内容相关但不同域名的网站,对主网站可以起到一个很好的支持作用。

用站群来做什么?

同一个行业,同样关键词,做多个网站,占据多个排名,只是站群SEO的一种方式。像前面说的,这样的站群要尽量隐藏网站之间的关系,一旦被发现是站群,往往被惩罚。

站群也可以是由完全不同行业的网站组成,内容当然也就完全不同。如果内容质量问题解决得好的话,这样的站群就只是同一家公司拥有多个高质量网站而已,不会被判断为作弊,也就没什么危险。很多大品牌旗下几百几千个网站是常事,完全没问题。

这样的站群不仅可以在不同领域获得排名和流量,本身的链接权重控制能力也是很强大的。当有自己的、或客户的目标网站要做SEO时,调动部分网站给予一些外链,效果很明显。就我所知,不少SEO公司及特殊行业公司的站群网站数量是数千级别的。

虽然我个人不倾向于用做站群的方式,不过站群如果做的好的话,现在还可以有不错的效果。

Go实现安全双检锁的方法和应用场景

Go实现安全双检锁的方法和应用场景

不安全的双检锁

从其他语言转入Go语言的同学经常会陷入一个思考:如何创建一个单例?

有些同学可能会把其它语言中的双检锁模式移植过来,双检锁模式也称为懒汉模式,首次用到的时候才创建实例。大部分人首次用Golang写出来的实例大概是这样的:

type Conn struct {
	Addr  string
	State int
}

var c *Conn
var mu sync.Mutex

func GetInstance() *Conn {
	if c == nil {
		mu.Lock()
		defer mu.Unlock()
		if c == nil {
			c = &Conn{"127.0.0.1:8080", 1}
		}
	}
	return c
}

这里先解释下这段代码的执行逻辑(已经清楚的同学可以直接跳过):

GetInstance用于获取结构体Conn的一个实例,其中:先判断c是否为空,如果为空则加锁,加锁之后再判断一次c是否为空,如果还为空,则创建Conn的一个实例,并赋值给c。这里有两次判空,所以称为双检,需要第二次判空的原因是:加锁之前可能有多个线程/协程都判断为空,这些线程/协程都会在这里等着加锁,它们最终也都会执行加锁操作,不过加锁之后的代码在多个线程/协程之间是串行执行的,一个线程/协程判空之后创建了实例,其它线程/协程在判断c是否为空时必然得出false的结果,这样就能保证c仅创建一次。而且后续调用GetInstance时都会仅执行第一次判空,得出false的结果,然后直接返回c。这样每个线程/协程最多只执行一次加锁操作,后续都只是简单的判断下就能返回结果,其性能必然不错。

了解Java的同学可能知道Java中的双检锁是非线程安全的,这是因为赋值操作中的两个步骤可能会出现乱序执行问题。这两个步骤是:对象内存空间的初始化和将内存地址设置给变量。因为编译器或者CPU优化,它们的执行顺序可能不确定,先执行第2步的话,锁外边的线程很有可能访问到没有初始化完毕的变量,从而引发某些异常。针对这个问题,Java以及其它一些语言中可以使用volatile来修饰变量,实际执行时会通过插入内存栅栏阻止指令重排,强制按照编码的指令顺序执行。

那么Go语言中的双检锁是安全的吗?

答案是也不安全

先来看看指令重排问题:

在Go语言规范中,赋值操作分为两个阶段:第一阶段对赋值操作左右两侧的表达式进行求值,第二阶段赋值按照从左至右的顺序执行。(参考:https://golang.google.cn/ref/spec#Assignments)

说的有点抽象,但没有提到赋值存在指令重排的问题,隐约感觉不会有这个问题。为了验证,让我们看一下上边那段代码中赋值操作的伪汇编代码:

golang赋值的汇编代码

红框圈出来的部分对应的代码是: c = &Conn{"127.0.0.1:8080", 1}

其中有一行:CMPL $0x0, runtime.writeBarrier(SB) ,这个指令就是插入一个内存栅栏。前边是要赋值数据的初始化,后边是赋值操作。如此看,赋值操作不存在指令重排的问题。

既然赋值操作没有指令重排的问题,那这个双检锁怎么还是不安全的呢?

在Golang中,对于大于单个机器字的值,读写它的时候是以一种不确定的顺序多次执行单机器字的操作来完成的。机器字大小就是我们通常说的32位、64位,即CPU完成一次无定点整数运算可以处理的二进制位数,也可以认为是CPU数据通道的大小。比如在32位的机器上读写一个int64类型的值就需要两次操作。(参考:https://golang.google.cn/ref/mem#tmp_2)

因为Golang中对变量的读和写都没有原子性的保证,所以很可能出现这种情况:锁里边变量赋值只处理了一半,锁外边的另一个goroutine就读到了未完全赋值的变量。所以这个双检锁的实现是不安全的。

Golang中将这种问题称为data race,说的是对某个数据产生了并发读写,读到的数据不可预测,可能产生问题,甚至导致程序崩溃。可以在构建或者运行时检查是否会发生这种情况:

$ go test -race mypkg    // to test the package
$ go run -race mysrc.go  // to run the source file
$ go build -race mycmd   // to build the command
$ go install -race mypkg // to install the package

另外上边说单条赋值操作没有重排序的问题,但是重排序问题在Golang中还是存在的,稍不注意就可能写出BUG来。比如下边这段代码:

a=1
b=1
c=a+b

在执行这段程序的goroutine中并不会出现问题,但是另一个goroutine读取到b==1时并不代表此时a==1,因为a=1和b=1的执行顺序可能会被改变。针对重排序问题,Golang并没有暴露类似volatile的关键字,因为理解和正确使用这类能力进行并发编程的门槛比较高,所以Golang只是在一些自己认为比较适合的地方插入了内存栅栏,尽量保持语言的简单。对于goroutine之间的数据同步,Go提供了更好的方式,那就是Channel,不过这不是本文的重点,这里就不介绍了。

sync.Once的启示

还是回到最开始的问题,如何在Golang中创建一个单例?

很多人应该会被推荐使用 sync.Once ,这里看下如何使用:

type Conn struct {
	Addr  string
	State int
}

var c *Conn
var once sync.Once

func setInstance() {
	fmt.Println("setup")
	c = &Conn{"127.0.0.1:8080", 1}
}

func doPrint() {
	once.Do(setInstance)
	fmt.Println(c)
}

func loopPrint() {
	for i := 0; i < 10; i++ {
		go doprint()
	}
}

这里重用上文的结构体Conn,设置Conn单例的方法是setInstance,这个方法在doPrint中被once.Do调用,这里的once就是sync.Once的一个实例,然后我们在loopPrint方法中创建10个goroutine来调用doPrint方法。

按照sync.Once的语义,setInstance应该近执行一次。可以实际执行下看看,我这里直接贴出结果:

setup
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}
&{127.0.0.1:8080 1}

无论执行多少遍,都是这个结果。那么sync.Once是怎么做到的呢?源码很短很清楚:

type Once struct {
	done uint32
	m    Mutex
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 {
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}

Once是一个结构体,其中第一个字段标识是否执行过,第二个字段是一个互斥量。Once仅公开了一个Do方法,用于执行目标函数f。

这里重点看下目标函数f是怎么被执行的?

  1. Do方法中第一行是判断字段done是否为0,为0则代表没执行过,为1则代表执行过。这里用了原子读,写的时候也要原子写,这样可以保证读写不会同时发生,能够读到当前最新的值。
  2. 如果done为0,则调用doSLow方法,从名字我们就可以体会到这个方法比较慢。
  3. doSlow中首先会加锁,使用的是Once结构体的第二个字段。
  4. 然后再判断done是否为0,注意这里没有使用原子读,为什么呢?因为锁中的方法是串行执行的,不会发生并发读写。
  5. 如果done为0,则调用目标函数f,执行相关的业务逻辑。
  6. 在执行目标函数f前,这里还声明了一个defer:defer atomic.StoreUint32(&o.done, 1) ,使用原子写改变done的值为1,代表目标函数已经执行过。它会在目标函数f执行完毕,doSlow方法返回之前执行。这个设计很精妙,精确控制了改写done值的时机。

可以看出,这里用的也是双检锁的模式,只不过做了两个增强:一是使用原子读写,避免了并发读写的内存数据不一致问题;二是在defer中更改完成标识,保证了代码执行顺序,不会出现完成标识更改逻辑被编译器或者CPU优化提前执行。

需要注意,如果目标函数f中发生了panic,目标函数也仅执行一次,不会执行多次直到成功。

安全的双检锁

有了对sync.Once的理解,我们可以改造之前写的双检锁逻辑,让它也能安全起来。

type Conn struct {
	Addr  string
	State int
}

var c *Conn
var mu sync.Mutex
var done uint32

func getInstance() *Conn {
	if atomic.LoadUint32(&done) == 0 {
		mu.Lock()
		defer mu.Unlock()
		if done == 0 {
			defer atomic.StoreUint32(&done, 1)
			c = &Conn{"127.0.0.1:8080", 1}
		}
	}
	return c
}

改变的地方就是sync.Once做的两个增强;原子读写和defer中更改完成标识。

当然如果要做的工作仅限于此,还不如直接使用sync.Once。

有时候我们需要的单例不是一成不变的,比如在ylog中需要每小时创建一个日志文件的实例,再比如需要为每一个用户创建不同的单例;再比如创建实例的过程中发生了错误,可能我们还会期望再执行实例的创建过程,直到成功。这两个需求是sync.Once无法做到的。

处理panic

这里在创建Conn的时候模拟一个panic。

i:=0
func newConn() *Conn {
	fmt.Println("newConn")
	div := i
	i++
	k := 10 / div
	return &Conn{"127.0.0.1:8080", k}
}

第1次执行newConn时会发生一个除零错误,并引发 panic。再执行时则可以正常创建。

panic可以通过recover进行处理,因此可以在捕捉到panic时不更改完成标识,之前的getInstance方法可以修改为:

func getInstance() *Conn {
	if atomic.LoadUint32(&done) == 0 {
		mu.Lock()
		defer mu.Unlock()

		if done == 0 {
			defer func() {
				if r := recover(); r == nil {
					defer atomic.StoreUint32(&done, 1)
				}
			}()

			c = newConn()
		}
	}
	return c
}

可以看到这里只是改了下defer函数,捕捉不到panic时才去更改完成标识。注意此时c并没有创建成功,会返回零值,或许你还需要增加其它的错误处理。

处理error

如果业务代码不是抛出panic,而是返回error,这时候怎么处理?

可以将error转为panic,比如newConn是这样实现的:

func newConn() (*Conn, error) {
	fmt.Println("newConn")
	div := i
	i++
	if div == 0 {
		return nil, errors.New("the divisor is zero")
	}
	k := 1 / div
	return &Conn{"127.0.0.1:8080", k}, nil
}

我们可以再把它包装一层:

func mustNewConn() *Conn {
	conn, err := newConn()
	if err != nil {
		panic(err)
	}
	return conn
}

如果不使用panic,还可以再引入一个变量,有error时对它赋值,在defer函数中增加对这个变量的判断,如果有错误值,则不更新完成标识位。代码也比较容易实现,不过还要增加变量,感觉复杂了,这里就不测试这种方法了。

有范围的单例

前文提到过有时单例不是一成不变的,我这里将这种单例称为有范围的单例。

这里还是复用前文的Conn结构体,不过需求修改为要为每个用户创建一个Conn实例。

看一下User的定义:

type User struct {
	done uint32
	Id   int64
	mu   sync.Mutex
	c    *Conn
}

其中包括一个用户Id,其它三个字段还是用于获取当前用户的Conn单例的。

再看看getInstance函数怎么改:

func getInstance(user *User) *Conn {
	if atomic.LoadUint32(&user.done) == 0 {
		user.mu.Lock()
		defer user.mu.Unlock()

		if user.done == 0 {
			defer func() {
				if r := recover(); r == nil {
					defer atomic.StoreUint32(&user.done, 1)
				}
			}()

			user.c = newConn()
		}
	}
	return user.c
}

这里增加了一个参数 user,方法内的逻辑基本没变,只不过操作的东西都变成user的字段。这样就可以为每个用户创建一个Conn单例。

这个方法有点泛型的意思了,当然不是泛型。

**有范围单例的另一个示例:**在ylog中需要每小时创建一个日志文件用于记录当前小时的日志,在每个小时只需创建并打开这个文件一次。

先看看Logger的定义(这里省略和创建单例无关的内容。):

type FileLogger struct {
	lastHour int64
	file     *os.File
	mu       sync.Mutex
	...
}

lastHour是记录的小时数,如果当前小时数不等于记录的小时数,则说明应该创建新的文件,这个变量类似于sync.Once中的done字段。

file是打开的文件实例。

mu是创建文件实例时需要加的锁。

下边看一下打开文件的方法:

func (l *FileLogger) ensureFile() (err error) {
	curTime := time.Now()
	curHour := getTimeHour(curTime)
	if atomic.LoadInt64(&l.lastHour) != curHour {
		return l.ensureFileSlow(curTime, curHour)
	}

	return
}

func (l *FileLogger) ensureFileSlow(curTime time.Time, curHour int64) (err error) {
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.lastHour != curHour {
		defer func() {
			if r := recover(); r == nil {
				atomic.StoreInt64(&l.lastHour, curHour)
			}
		}()
		l.createFile(curTime, curHour)
	}
	return
}

这里模仿sync.Once中的处理方法,有两点主要的不同:数值比较不再是0和1,而是每个小时都会变化的数字;增加了对panic的处理。如果打开文件失败,则还会再次尝试打开文件。

要查看完整的代码请访问Github:https://github.com/bosima/ylog/tree/1.0

双检锁的性能

从原理上分析,双检锁的性能要好过互斥锁,因为互斥锁每次都要加锁;不使用原子操作的双检锁要比使用原子操作的双检锁好一些,毕竟原子操作也是有些成本的。那么实际差距是多少呢?

这里做一个Benchmark Test,还是处理上文的Conn结构体,为了方便测试,定义一个上下文:

type Context struct {
	done uint32
	c    *Conn
	mu   sync.Mutex
}

编写三个用于测试的方法:

func ensure_unsafe_dcl(context *Context) {
	if context.done == 0 {
		context.mu.Lock()
		defer context.mu.Unlock()
		if context.done == 0 {
			defer func() { context.done = 1 }()
			context.c = newConn()
		}
	}
}

func ensure_dcl(context *Context) {
	if atomic.LoadUint32(&context.done) == 0 {
		context.mu.Lock()
		defer context.mu.Unlock()
		if context.done == 0 {
			defer atomic.StoreUint32(&context.done, 1)
			context.c = newConn()
		}
	}
}

func ensure_mutex(context *Context) {
	context.mu.Lock()
	defer context.mu.Unlock()
	if context.done == 0 {
    defer func() { context.done = 1 }()
		context.c = newConn()
	}
}

这三个方法分别对应不安全的双检锁、使用原子操作的安全双检锁和每次都加互斥锁。它们的作用都是确保Conn结构体的实例存在,如果不存在则创建。

使用的测试方法都是下面这种写法,按照计算机逻辑处理器的数量并行运行测试方法:

func BenchmarkInfo_DCL(b *testing.B) {
	context := &Context{}
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			ensure_dcl(context)
			processConn(context.c)
		}
	})
}

先看一下Benchmark Test的结果:

benchmark-test-for-double-checked-locking

可以看到使用双检锁相比每次加锁的提升是两个数量级,这是正常的。

而不安全的双检锁和使用原子操作的安全双检锁时间消耗相差无几,为什么呢?

主要原因是这里写只有1次,剩下的全是读。即使使用了原子操作,绝大部分情况下CPU读数据的时候也不用在多个核心之间同步(锁总线、锁缓存等),只需要读缓存就可以了。这也从一个方面证明了双检锁模式的意义。

另外上文提到过Go读写超过一个机器字的变量时是非原子的,那如果读写只有1个机器字呢?在64位机器上读写int64本身就是原子操作,也就是说读写应该都只需1次操作,不管用不用atomic方法。这可以在编译器文档或者CPU手册中验证。(Reference:https://preshing.com/20130618/atomic-vs-non-atomic-operations/)

不过这两个分析不是说我们使用原子操作没有意义,不安全双检锁的执行结果是没有Go语言规范保证的,上边的结果只是在特定编译器、特定平台下的基准测试结果,不同的编译器、CPU,甚至不同版本的Go都不知道会出什么幺蛾子,运行的效果也就无法保证。我们不得不考虑程序的可移植性。


以上就是本文主要内容,如有问题欢迎反馈。完整代码已经上传到Github,欢迎访问:https://github.com/bosima/go-demo/tree/main/double-check-locking

JS中Ajax的方法和应用

JS中Ajax的方法和应用

 XMLHttpRequest对象

Ajax技术的核心是XMLHttpRequest对象(简称XHR),这是有微软率先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。

但因为IE的兼容性问题,这里对低版本的IE兼容性不做过多的描述。

XMLHttpRequest是一个构造函数,现在已经被各大浏览器厂商所支持,IE的话事支持IE7以上,XMLHttpRequest函数提供的方法有如下几个:

open: open有三个参数,第一个参数规定了当前请求的类型(get、post、put、delete、head),请求的统一资源定位符url,以及是同步还是异步的一个布尔值。

send: 在使用open方法创建请求以备发送之后,再调用send方法,就可以发送服务器进行这一次请求了。send方法主要是传递当前的请求是否需要给服务器传递数据。

tips: 如果是get请求,则直接在open的url后面拼接上即可,但是不同浏览器对于url的长度是有限制的比如ie是2KB chrome是8KB大小,并且get请求是明文可见的,所以一般只是用来做查询数据的处理。在数据缓存方面因为get是用户查找数据,可以和不同的数据表进行联系,所以可以用于做服务器端缓存,但是post请求则不一样,post一般是用于传递数据进行数据表的增删改,并不合适于做缓存,前端培训并且post的数据传递在ajax中是不可见的,安全性方面也是优于get。

在send传递参数给服务器端之后,便会从服务器端获取服务器端数据的响应,判断服务器端是否成功接收到数据,并且返回响应的数据。

那么此时,会存在几个属性可以调用:

responseText:作为响应主体被返回的文本。

responseXML:如果响应的文件mime类型是text/xml或application/xml,这个属性包含着返回的xml文件。

status:当前响应的HTTP请求。

statusText:HTTP状态的说明。

那么,如何判断当前的响应状态呢,可以使用onreadystatechange这个事件处理程序来进行监听,同时配合readyState属性来搭配监听。

readyState有5个状态:

0    尚未初始化。尚未调用open()方法。

1    启动。已经调用open方法,但还未使用send方法

2    发送。已经调用send方法,但尚未接收到响应数据

3    接受。已经接收到部分响应的数据

4    完成。已经全部接受到响应回来的数据,而且已经可以在客户端使用了。

在开发中经常会遇到在页面DOM加载完成之后向服务器端发起多个Ajax请求,比如有多个请求,后一个的请求需要上一次请求的返回结果。过去常规做法只能 callback 层层嵌套,这也就是多层ajax嵌套的回调地狱。

为了解决异步多层嵌套的问题,ES6推出了一个新的构造函数Promise。

Jquery $ajax

ajax是一个前后台配合的技术,它可以让javascript发送http请求与后台进行通信,从而获取后台返回的数据。ajax的原理是实例化xmlHttp对象,使用此对象与后台通信。在这里Jquery将他封装成了一个函数$.ajax(),并解决了多种浏览器的兼容性问题,我们可以直接使用这个方法。

它是对原生XHR的封装,还支持JSONP,非常方便;真的是用过的都说好。但是随着react,vue等前端框架的兴起,jquery早已不复当年之勇。很多情况下我们只需要使用ajax,但是却需要引入整个jquery,这非常的不合理,所以开始出现了新的插件比如axios等等。

ES6 Promise

Promise是一个构造函数对象本身有all、reject、resolve这几个方法,有fulfilled(已成功)/pengding(进行中)/rejected(已拒绝)这三种状态,原型上有then、catch等方法。

Promise可以接受一个参数那就是函数,并且这个函数有两个参数:resolve,reject,resolve和reject分别表示异步操作执行成功后的回调函数resolve和执行失败之后的回调函数reject。

在实例化promise对象之后,我们可以使用then和catch方法来进行成功后的回调和失败后的处理。then和catch都接受一个返回值,这个值包含了成功和失败后的状态和返回值,下面是执行then方法成功后获取的返回值,是一个对象包含了经典的xmlHttpRequest对象和status状态码,已经从后台获取的数据data。

 如果想要更语义化一点可以使用另一个方法catch,当ajax出现异常的时候用于捕获promise的错误情况。

 Promise.all

为了解决多层回调地狱的嵌套问题,promise提供了一个新的方法。Promise.all接受一个promise对象的数组作为参数,当这个数组里的所有对象都会resolve或者至少有一个reject状态的时候,它才会去调用then方法。

all方法会把回调函数放在promise对象数组中最慢返回的对象中去执行,也就是不必担心多层依赖的问题。

Promise.race

promise.race的字面量意思是竞赛,其用法和接受的参数和all一样,只不过race会把回调函数放在promise对象数组执行最快的回调中去执行。

扩展:Promise和axios在vue中的应用

在vue-cli脚手架中我们使用了axios进行ajax发送请求,axios是一个底层基于xmlHttpRequest的插件可用于node和浏览器端,因为需要使用promise对象来进行结果处理,所以使用的时候需要浏览器支持Es6。

axios支持get和post请求,对于jsonp请求作者认为是不安全的,所以在axios中并没有jsonp的方法,需要另外使用。

在上图中我们可以看到,在vue-cli脚手架中引用了axios之后创建了一个函数creatPromis用来重复创建promise对象,在对象里我们创建了一个axios函数,并且在then和catch中传入了resolve和reject用来分别执行成功和失败的回调。

我们创建了五个promise对象,通过一个数组把这五个对象传入其中并当参数传递给promise.all方法,当着五个promise都被正确执行的时候,我们会执行then里面的方法,否则会执行catch。这样不仅代码简洁还解决了传统意义上的多层ajax嵌套的回调地狱。

ES7 async

Promise虽然一定程度减少了回调嵌套,但是并不能完全消除嵌套。举个例子,对于多个依赖的promise来说,每产生一次依赖,就会增加一次嵌套。另外,采用promise的代码看起来依然是异步的,并不是同步。

async也是函数,所以具有普通函数该有的性质。不过形式上有几点不同:在定义async异步函数的时候,需要在function关键字钱加上async关键字,二是在函数内部可以使用await关键字,代表等待,表示后面跟随的结果当成异步操作并等待其完成。

async函数是generator函数的语法糖。它的定义方式有以下几种:

 async是异步的意思,他本身也是是基于promise对象实现的,在获取到返回的值的时候我们需要用到原型链上的then方法去接受返回值,可以看出加了async关键字的函数timeout并没有阻碍js后面的执行。

async是基于promise对象实现的,所以会有resove和reject方法,这里我们传入一个flag参数,当为true的时候内部就会执行resove,但为false的时候就会执行reject。

await顾名思义有等待的意思,一旦在函数前加了await就代表,必须等当前的函数执行完毕,浏览器才会继续向下运行解析。在下图中我们使用了voteTest函数,并在内部执行了getVoteData函数,并在前面添加了关键字await,3秒钟过后voteTest下面的console开始了打印数据。

 

 await就是用来等待Promise对象中resolve和reject这两个函数的执行的,并且将这两个函数传递的参数当作返回结果赋给变量,如同下图run函数中的代码示例那样。 

seo怎么做站群推广(seo站群优化技术)

seo怎么做站群推广(seo站群优化技术)

群站seo优化流量来源分析,群站如何优化效果比较好?

群站的内容、长尾关键词的挖掘、布局等决定了群站seo优化的流量,但是,不管流量来源是取决于什么,企业都必须做好定期更新内容以及定期监控流量变化。

seo怎么做站群推广(seo站群优化技术)

在企业之间的竞争中,人流量都是竞争的源头,没有人流量,企业就不可能得到发展,没有发展就无法得到扩大。

特别是是移动互联网平台中,企业更需要做到“独树一帜”,才有可能占领较好的发展趋势。所以,在互联网时代上的发展,群站seo优化捕获流量是必要手段,它由多个网站搭建而成,可以帮助企业捕获精准流量,解决获客难等问题。那么,群站seo优化的流量哪里来呢?一起往下看看吧!

一、群站seo优化流量来源分析群站seo优化的流量来源于多个方面,如内容、长尾关键词的挖掘、布局等。但是,不管流量来源哪,企业都必须做好以下工作:

1、定期更新内容陈旧或者一成不变的内容会让用户及搜索引擎感到视觉上的疲劳。所以,保持一定的新鲜感,这也是吸引它们访问下去的动力。因此,要做到定期更新内容,不要三天打鱼 两天晒网。定期更新内容

2、定期监控流量变化用户的每一个微小细节的变化都会影响到企业的发展。所以,对于群站seo优化的流量必须做到定期监控流量的工作,遇到有任何问题,可以及时调整优化方向,确保流量的不流失。

seo怎么做站群推广(seo站群优化技术)

二、群站如何优化效果比较好?1、收集有转化率的长尾关键词不管是关键词优化还是群站seo优化,利用关键词获取流量和取得转化都是必要条件。所以,想要有群站优化效果更好,需要分析用户的搜索习惯,挖掘出有转化率的关键词。然而,长尾关键词是用户常用的搜索词,因此可以选择一批长尾关键词,给企业网站带来精准的用户流量。

2、做好内容攻略网站搭建得再好看,再大气,如果没有优质的内容做支撑,那么网站只会是一个空盒子。所以,要根据每个网站,每个页面做好内容的规划,千万不要觉得网站之间存在关联性,内容就是可以一站多用,毫无差异性。所以,做群站想更有效果就要保持一定的差异性,以吸引搜索引擎和用户的兴趣爱好为前提。做好内容攻略

3、找专业的优化公司群站优化想要有效果,背后付出的专业技能和方法,并不是企业表面看到的那么简单。所以,找专业人士是解决效果的有效途径,如《蜘蛛商务网》,具有以下优点:

(1)覆盖到精准用户精准的用户意味着能为企业带来可观的利润,甚至可以带动企业的发展。换句话说,人流量对一个企业的发展有着至关重要的作用。所以,我们有专业挖掘出转化率高的长尾关键词,能做到覆盖30000+,最大范围的覆盖到需求人群。

(2)费用确保性价比高在互联网最看不到、摸不着的就是成本报价,特别是对不专业的企业来说,更是以成本报价作为合作的前提条件。其实,费用的报价都是看企业选择的优化公司,但是有些优化公司只在乎价格,至于企业的权益是考虑不周的。然而,《蜘蛛商务网》,把群站优化收费拆分为预收款+尾款,当在约定的时间内达不到日PV1000时,尾款分文不取,保障了企业的合法权益。白帽站群优化与SEO运营策略

白帽站群优化与SEO运营策略,本文把SEO培训课件内容整理分享供SEO行业站长朋友学习交流。

本节SEO知识点

-不同的优化方式与区别

-掌握合法白帽群站的关键词优化

-了解和掌握百度关键词流量的围剿

群站模式玩法

有 n 个网站,相同的业务或不同的业务,相同的模式或不同的管理模式,所有的流量都可以实现相应的价值;

seo怎么做站群推广(seo站群优化技术)

白帽站群优势有哪些

1,正规SEO手段

2,实现批量网站优化

3,受百度搜索保护

黑帽站群存在劣势

1,非法SEO手段

2,实现机器作弊优化

3,属于百度打击

seo怎么做站群推广(seo站群优化技术)

对于群站来说,站长SEO们并不陌生。

简单了解黑帽站群

黑帽站群一般是利用系统软件来批量数据生成的网站,也就是因为我们国家所有的批量建站指采集工作内容的形式,如图所示。

特点:批量数据采集,批量生产管理,量产,无质量意义可言。

seo怎么做站群推广(seo站群优化技术)

这种黑帽站群 seo 玩法基本上是过时的,属于搜索引擎创建一个可识别的类别,seo 网站管理员建议不要采用这种方法,一方面它需要花费大量的资金来投资,另一方面,你需要购买基本的软件工具和域名服务器投资,一旦搜索引擎识别出惩罚 k 站着,不值得损失。

建议白帽SEO

如何搭建白帽SEO网站呢?下面导图给大家可以提供一些研究思路。

seo怎么做站群推广(seo站群优化技术)

白帽站群模式

行业站分割,分割多个子业务站点;

关键词拆分,不同网站优化不同的关键词;

需求细分,需求细分;

维护成本,需要通过建立一个团队;

人员配备,费用开销;

多域名多服务器;

需求挖掘研究

下图是需求挖掘数据分析,可以同时通过这种教学方式搭建属于我们自己的白帽站群策略。

seo怎么做站群推广(seo站群优化技术)

seo怎么做站群推广(seo站群如何打造)

seo怎么做站群推广(seo站群如何打造)

站群从本质来讲就是一种黑帽SEO的做法,只是有些站群推广做得比较巧妙,不容易被搜索引擎发现,所以是很多灰色行业常用的方法。站群推广往往借助大量的二级域名来做推广,而且内容多以自动采集为主,所以能在短时间内聚集权重,有排名不稳定的特点。小编在此整理了SEO站群变现5种路径,其中前3条偏正规,也适用于正规网站,后2条偏灰色。

seo怎么做站群推广(seo站群如何打造)

1、权重站出售

通俗的说法就是网站交易。以A5平台为例,从其网站交易的数据来看,网站的报价主要受行业、权重、流量三方面因素的影响。从行业上看,暴利行业(彩票、股票)> 一般垂直行业(教育、装修、B2B等)> 资源站(小说、电影) > 泛内容站(娱乐、星座、养生)。

同样是百度权重2的网站,暴利行业报价50w以上,而泛内容站只能报价几千块。

可见,精准垂直流量在任何渠道都价值连城,其特点:网站本身拥有大量流量,高利润的行业,内容相对稀缺而不太容易通过简单采集获得。

2、淘宝客

网站淘客,简单说就是,在网站后台生成推广链接帮助商家进行推广。当网站用户点击链接进入淘宝卖家店铺完成购买后,就可获取由卖家支付的佣金,通常一段代码就能搞定。所以只要网站有流量,有订单,就能获得返佣。这种操作也同样适用于很多分销体系的商品,比如知识星球、小鹅通课程等。

3、做广告联盟

广告联盟,即网站通过网盟平台选择合适的广告主,并在网站投放其广告提高收益。对于大联盟,比如百度网盟,广告收益相对固定,一个UV的价值大约在1-3毛钱左右,好处是结算稳定,广告正规,也不太容易被百度算法命中。小联盟的话,广告收益比较高,被搜索引擎惩罚的风险也高。

4、出售外链

现在外链是各种算法打击的对象,但是不可否认,外链对于收录和排名的巨大作用。所以目前各种外链中介,依旧非常活跃,并且差价收益也非常可观。一般来说,权重越高的网站,外链价格越高。比如爱站权重5的网站,不论行业,单条链接价格至少200块/月的,如果算挂30条友情链接,一个月就有6000块的固定收益。

当然,因为外链被搜索引擎打击的力度比较大,对于大部分正规网站,不会采用这种杀鸡取卵的盈利方式,只能作为一种收益补充。

5 卖暴利产品

暴利产品主要集中在女性减肥、灰色行业产品等,而利用站群操作,主要就是占领该产品的核心及相关长尾关键词,结合单页面来销售,或者采用直接跳转到目标站方式来销售产品。网站收益很高,但是很难长期。

关于站群SEO的方法和应用站群seo技巧的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于Go实现安全双检锁的方法和应用场景、JS中Ajax的方法和应用、seo怎么做站群推广(seo站群优化技术)、seo怎么做站群推广(seo站群如何打造)等相关内容,可以在本站寻找。

本文标签: