记一次远程调用优化

整个系统分为三个部分:

原始数据

包装成商品后数据

客户持有列表数据

原有设计是每一次客户获取客户持有列表数据都会从包装成商品之后的数据获取数据(包装成商品之后原有的原始数据的属性会有可能被商城变更)

因为Dubbo的方便性让人不自觉的就忽略掉了远程调用的IO是很重的

原有设计时,每个客户第一次访问会从原始数据+包装后商品数据进行遍历,然后存入redis,接着以后调用直接走redis。

当天上线后,发现原始数据的系统直接出现了百万次调用且因为代码笔误全部打到了数据库上,灰度环境紧急回滚

事后分析可能有以下几个原因:

1、不知道因何系统无法从redis上读取数据,都直接开始了调用

2、因为原有的设计可能流量只会放大N倍,但因为配合其他系统上线,增加了两段逻辑,流量放大为3N倍

3、因为dubbo的超时重试机制,流量再次放大三倍,直接导致整个系统的崩溃

优化方案:

将远程调用转为本地调用

1、给dubbo接口增加Proxy,作为本地缓存

2、定期从原始数据平台拉去数据,保证数据的一致性

3、其他逻辑保持不变

这样将网络请求变为本地内存调用,比较好的解决了因系统流量放大导致的系统雪崩情况

并发冲突处理

昨天晚上,遇上了阿里HR打电话来面试就聊了一下高并发情况下的冲突解决,结果我自己被绕晕了

原问题是:用CAS维持原子操作具体是如何操作的->这样操作有什么样的劣势->展示了一个实际的劣势场景:如何针对AtomicLong做优化

事实上并发问题是有一般解的,即:避免锁,减少锁范围,完全异步化(和我之前说高性能服务器的一般解是一样的)

CAS操作就不说了,一个自旋操作,高并发情况下可能会出现ABA问题,这些都是常见的面试题

那CAS实际的劣势呢,是在高并发操作下,写性能会急剧的降低(多个线程疯狂自旋,每个自旋周期都刚好被他人更新了值)

那为啥我会被绕晕了,其实他的真正问题是,在自旋锁的效应下,如何解决竟态写的性能下降问题,因此该问题不涉及读,而我脑子里面一直想的是AtomicLong.increaseAndGet()。。。。

这是有个解的,即是分线程加,类似于ConcurrentHashMap的处理方法,同理也可以预见到会发生和ConcurrentHashMap一样的sum操作会出现问题。当业务代码要马上获取自增的值时候,要么上锁,要么就得从业务上将读和写分开否则会使得业务代码在某次自增操作后获取到同一个值。

另,hashmap的负载因子的概念是:当hash发生时候,发生冲突的概率是多大

技术栈的选型

技术栈的选型:

  1. 人员成本
  2. 使用成本
  3. 改造成本

第一点指的是在市场上找一个熟悉当前技术栈的人是否困难, 一般情况下开源软件的人员比较容易找, 但也仅限于初级人员, 若是需要到较深层次的改写或者应用, 无论任何技术栈都非常困难(多数情况下情景退化成自研)

第二点是用起来这个组件好不好用,简不简单,比如EJB这种在绑定WebSphere战车上,用起来非常舒服.或者你整体都是Spring,沿用了Spring技术套件也会很舒服

第三点指当前选型与原有选型发生冲突要进行改造改动的返回 ,当然如果一个项目真心是完全敏捷开发这个完全不需要考虑.大部分情况下,比如从Dubbo切换至SpringCloud,从SOAP到Probuf(比如当前我司….),冲突厉害了还要做技术演进方案…如果无法演进就更痛苦成本就变高

私货:

个人偏好充血模型,但是以Spring开发的WEB应用都是贫血模型, 所以技术栈还可能影响开发习惯和偏好

ArrayList和Arrays.ArrayList的区别

1.Arrays.asList返回的是一个List的实现,但不是ArrayList,刚好和java.utils.ArrayList重名而已(吐槽1)

2.Arrays.ArrayList是一个不可修改长度(fix-length)的List,所以既不支持add也不支持remove,如果使用会抛出UnsupportedOperationException

 

建议:

除了自己明确的是ArrayList,对于第三方库返回的List接口,一律当作fix-length对待,避免不符合直觉的操作

 

另外:

大部分情况下..List使用上都是按照appendable来使用的…..(大家都这么用,所以认为都是正确的),这么看来Java以前的库对于接口的设想不太对

 

至少我认为,一个类满足一个接口必然每个接口都必须要操作意义,比如List.add就是声明代表实现类一定要实现这个接口,而不是简单的遇到没有实现的操作就抛出一个运行时异常,明显违反接口单一功能原则

(可添加,添加),我宁愿使用时候就能清楚的明白这是个NotAppendable的对象

 

多态的实现优劣/Go/Java

Go:从语法层面不是一个完备的OO语言,其实现多态用了类似this指针的做法,模拟了OO

Java:完备的OO语法,类是一等公民(java8之后default开始略扭转了趋势)

 

差异:因为OO的完备性需要包含对象与对象之间的关系,所以在JAVA编程中经常蛋疼在对象多态的海洋中(C++更惨)

Go把类之间的关系简化了,只有关系,没有所谓的继承.抛弃了需要大量思考压力的继承体系,Go中的面向对象编程实质上得到了简化(代码上没有…其实完备的OOP在设计上就必须关注…只不过写成代码之后只需要关注是否有关系就好了)

修改:

1.知乎上看到一个观点说的挺不错的,多态等是为了解决归一性,通过归一性来应对可能的修改,也就是抽象

用Golang实现OO编程

package simpleBlog

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

type BlogOperations interface {
	Persistence()
	Delete()
}
type Blog struct {
	Author  string
	Header  string
	Content string
}

func (blog *Blog) Persistence() {
	result, err := DB.Exec("INSERT INTO blog.blog (author, header, content)VALUES (?,?,?)", blog.Author, blog.Header, blog.Content)
	if nil != err {
		fmt.Println(result, err)
	}
	fmt.Println("persistence", blog)
}
func (blog *Blog) Delete() {
	DB.Exec("DELETE FROM blog.blog WHERE header=?", blog.Header)
	fmt.Println("delete", blog)
}

OO三要素:类=数据+方法

后来想想C也能实现OO编程

只要相应的函数传入一个struct的指针….

后来又想想…Python不就是这么实现的么…有一个内置的this指针(虽然觉得是奇技淫巧…)

这么看来C也能实现OO编程,技巧在于传入一个隐式指针指向一个数据结构

那么再回想一下OO三要素,类=数据+方法

也就是通过this/struct指针来实现数据和方法的关联