服务连接的历史传承
1. 逻辑关联
先
A后B再C,逻辑实现的时候,即使采用异步的实现办法,但逻辑上依旧是瀑布式的串行逻辑,有一定的先后顺序。不论是最细节的功能实现,还是业务流程的跳转,都遵循着一个步进式的逻辑方向,各操作之间都会逻辑的先后顺序。
2. 服务模块
对于细节的
操作流程,我们提取为方法,然后按照流程进行方法的调用。这样,对于小功能的实现,变成了这样的步骤:
- 小模块的实现
- 模块功能的逻辑组合调用
尤其是那些经常用到的方法,这样就提高了代码的复用率。
从服务的角度来说,也是如此。把服务进行拆解之后,最后的形态也是如此。
唯一的不同,那就是
进程化,代码中的变化,重新修改,影响的功能就得全部重启,因为都是一体的。但是,通过
服务模块的解放,接口的解耦,我们修改针对的部分之后,其他部分能衔接上即可。也就是:修改后启动的范围,影响的范围。
如果一个大服务,全部是在一个进程内,那么
服务模块不过是大一点的方法汇总,除此之外,别无区别。解耦之后,部署多个
服务模块,多进程间,共同完成整个服务功能。
3. 功能的拆分
或许仍然没有明晰一个关键点:逻辑顺序的限制。
为了解决这个问题,我们采用
模块化来进行解决。抽取成方法,或者抽取成服务模块。从而形成插件化的调用形式,当修复问题是,就变成了插件的修复。
这个就存在一个问题:我们的
插件是否足够小。也即是我们
插件化之后,是否真的能够做到只修改插件一处,从而做到全局的更新。问题的核心,就在于逻辑的细粒度。
如果我们拆分的不够细,在不同的模块中,仍然有相同的逻辑实现,想要变动的时候,我们就不得不针对诸多的模块进行修改。从代码的角度无可厚非,但是从服务的角度而言,这对于服务的影响未免过大,毕竟服务不只是程序的问题。
4. 扁平化模块管理
恩,操作拆的够细致,那就是外部引用的方法包。
服务拆的够细致,那就是服务的功能模块。
- 模块化:插件化,把问题更局限,更易维护,更易管理,影响也更小。
- 细粒度:完善模块化的一些不足,让模块化理念更彻底。
接下来的问题,就是如何去进行逻辑的组织。
有两个疑问:
- 接口解耦是否够彻底
- 模块管理是否够稳定
接口解耦
接口解耦不够彻底么?
这是一个微妙的问题,不过性质完全不同,需要明确一下接口的位置。
- 程序接口
对程序接口而言,程序解耦了,目的也达到了。
单从服务角度而言,这种解耦仍然具有一定的耦合性。
- 服务接口
类似
百度地图,人脸识别等服务的http接口。说实话,你可以内嵌,但是两者之间没有一个必要的关联性。
存在业务的逻辑关联,却各自为战。
从管理的角度,我们需要它们分离的更彻底,只要能对接上就行,我们需要的是
服务接口。模块管理
最终,我们的服务,都是逻辑组织,基石在于
模块(插件)的稳定保障。对于这些模块,我么需要如下手段
- 监控
- 管理
不过,这两者并未引入更多的影响,全部都是已有的概念和知识。
它最深远的影响,在于
接口的统一。当全部模块都收归于它之下时,我们的模块不外乎两种。
- 供别的模块调用
- 调用别的模块
或者两者兼有,不过也没有别的花样了。
以前多个模块都调用的部件,拆分之后获得了独立。
所处的位置或高或低,后来不可避免的出现重复。
但是在这里,一切都是平级的,只是
调用或者被用的角色差异。弱化了
逻辑顺序和业务逻辑,强调了模块特性。似乎背道而驰,却把焦点聚焦于了
逻辑组织,更便利的完成开发。
5. 结构变化
- 模块化
- 细粒度
- 扁平化
- 服务化
- 为什么模块化?
聚焦逻辑组织,减少重复工作,提高工作效率。
- 为什么细粒度?
完善模块化,更进一步贴近模块化理念要求。
- 为什么扁平化?
模块管理和组织,最终表现成模块统一对注册中心的接口对接。
- 为什么服务化?
模块间彻底解耦,不再作为功能模块,而是作为服务,提供功能调用。
6. 轨迹
- 程序
服务 -> 小服务 -> 功能块 -> 小功能块 -> 单一功能
- 服务
功能 -> 维护 -> 开发 -> 管理
从程序来看,越来越支离破碎;但是从服务上看,却是不断进步。
- 逻辑
逻辑结果 -> 逻辑流程 -> 逻辑维护 -> 逻辑重组 -> 新逻辑实现
从粗浅的结果,然后思考实现,深入到今后的维护,甚至于重构。
最终形成一套只需要需求,便能够适应一切的开发方法。
要的只是葫芦,但今后各种葫芦,甚至于各种植物,也能够百分百的获取。
eureka
1. 结构
- 模块划分
| 模块 | 作用 |
|---|---|
server |
维护服务模块,供客户端调用 |
client |
进行服务注册和发现 |
- 角色扮演
| 角色 | 功能 |
|---|---|
center |
server |
provider |
生产者,注册到center
|
consumer |
消费者,从center发现服务 |
2. 实现
具体的话请参看简单例子,在这里补充一下原来的错漏点。
@Autowired
DiscoveryClient client;
@GetMapping("/consumer")
public void consumerProduct(@RequestParam("name") String name){
List<String> services = client.getServices();
for(String service : services){
List<ServiceInstance> instances = client.getInstances(service);
for(ServiceInstance instance:instances){
instance.getHost();
instance.getPort();
instance.getUri();
instance.getServiceId();
instance.getScheme();
instance.getMetadata();
}
}
}
DiscoveryClient:正确的一体化方式是利用client进行服务发现。
getServices:获取服务名称列表,如果知道服务名称,可以直接获取实例进行调用。
getInstances:主体实例,统一名称可能多个实例,自行选择调用。
3. 配置
spring:
application:
name: appName
server:
port: port
eureka:
client:
fetch-regiestry: false
regiester-with-eureka: false
service-Url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
appname: server
server:
enable-self-preservation: false
port:每个rest服务,必定有自己的占用端口。
regiester-with-eureka:eureka自己都不放过,也经常把作为服务的自己注册进去,可以不注册。
fetch-regiestry:服务发现,server和单纯的consumer都可以不启用,减少开销。
service-Url:服务地址,eureka服务的注册和发现地址,而不是我们的当前服务地址。
接下来先看一张图
application.name:对应的就是1,为了区别这个还是要的。
instance.name:2对应的名称,不定就会自动生成,实在有点丑。
prefer-ip-address:true就容易定位ip和port了,一直是localhost哪台机器出问题都不知道。
至于
不得不说AP,算是一种安全策略。
服务可以注册,发现,还有注销。这种注销通过心跳进行确认,超时无响应的就注销移除。
如果是网络波动,心跳发了没收到这种,就会保留而不会注销。
enable-self-preservation:就是AP的开关了。
学习中觉得碍眼就关掉吧,但是工作中这东西最好还是要保留,不过关掉了还是有红字,哈哈。
4. 集群
defaultZone: http://godme.com:8761/eureka/,http://judas.com:8762/eureka/
service-Url,逗号隔开也可以配置多个,当在server中配置多个时,此时就成了集群。
集群的话,目前自我理解有两种
- 容灾集群
- 功能集群
- 容灾
对于第一种,也是我接触到的,类似于实现
切换管理的容灾。之下有诸多的同实例服务,对外而言,集群中使用的却只是其中之一。
但是当前的服务异常时,又会切换到另一个,保证服务稳定运行。
比如
redis的哨兵,也正如现在的eureka集群。
- 功能
如果说上面是
1+1,第二种就是2了。虽然是几台计算机,但是
集群所产生的威力和效能,远不是之前所能衡量的。这方面的技术偶是没啥了解,但是为了明晰概念,先划分一把。
第一种的话或许叫做
服务集群比较贴切,容灾只是其中的好处之一。正如
负载均衡,它不只是调用单个服务,而是服务轮询。某种程度而言,介于服务容灾和功效提升之间。
不过更常见的
服务集群只是多个实例干同样的事情,以便于容灾。类比于
多个实例共同干同一件事的功能集群,一个追求稳定,一个追求效能。
CAP
| 名词 | 性能 |
|---|---|
Consistency |
一致性 |
Availablity |
可用性 |
Partition tolerance |
分区容错性 |
实际情况,三者不能够同时满足,于是产生三种组合
CAAPCP
考虑到网络通讯中的丢包,P又必须得到满足,因此只有
APCP
两种选择。
Zookeeper和eureka的根源性质,在于设计理念的不同
| 服务 | 理念 |
|---|---|
zookeeper |
CP |
eureka |
AP |
zookeeper的一致性,确保了服务的准确状态传达和调用,却丢失了A(可用性)。
当节点选举时,服务处于不可用状态,影响服务调用,对业务造成巨大影响。
eureka的可用性,会导致局部节点的信息不一致。但是对外层的服务调用而言,没有影响。
而且通过对节点的轮询,也能够保证信息的获取,不过并非每个节点信息一致这个问题实在明显。
同样的,两者的沟通也有一定区别
| 服务 | 沟通 |
|---|---|
zookeeper |
RPC |
eureka |
rest |
沟通方式各有千秋,仁者见仁了。
小结
统一接口层,就是它了。