「这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战」
注册中心场景分析
在分布式服务体系结构的场景下,我们可能将一个大系统拆分为多个子系统,然后各个子系统之间再进行互相调用,这样不仅可以保证服务可用性,同时也便于对某一个子系统进行扩容。项目发布上线,并没有多大的访问量,因此一开始我们的服务可能是这样
如上图order-service需要调用user-service,在order-service中我们可以使用user-service的的ip:port的方式进行访问调用。
但是随着业务的发展,发现用户端压力比较大,因此需要给user-service进行扩容,最终关系图如下:
如果系统的调用不是很复杂,可以通过配置管理,然后实现一个简单的客户端负载均衡也是OK的,但是随着业务的发展,服务模块进行更加细粒度的划分,业务也变得更加复杂,再使用简单的配置文件管理,将变得难以维护。当然我们可以再前面加一个服务代理,比如nginx做反向代理, 如下
如果我们是如下场景呢?
服务不再是A-B,B-C 那么简单,而是错综复杂的微小服务的调用
这个时候我们可以借助于Zookeeper的基本特性来实现一个注册中心,什么是注册中心,顾名思义,就是让众多的服务,都在Zookeeper中进行注册,啥是注册,注册就是把自己的一些服务信息,比如IP,端口,还有一些更加具体的服务信息,都写到 Zookeeper节点上, 这样有需要的服务就可以直接从zookeeper上面去拿,怎么拿呢? 这时我们可以定义统一的名称,比如,User-Service, 那所有的用户服务在启动的时候,都在User-Service 这个节点下面创建一个子节点(临时节点),这个子节点保持唯一就好,代表了每个服务实例的唯一标识,有依赖用户服务的比如Order-Service 就可以通过User-Service 这个父节点,就能获取所有的User-Service 子节点,并且获取所有的子节点信息(IP,端口等信息),拿到子节点的数据后Order-Service可以对其进行缓存,然后实现一个客户端的负载均衡,同时还可以对这个User-Service 目录进行监听, 这样有新的节点加入,或者退出,Order-Service都能收到通知,这样Order-Service重新获取所有子节点,且进行数据更新。这个用户服务的子节点的类型为临时节点。 第一节课有讲过,Zookeeper中临时节点生命周期是和SESSION绑定的,如果SESSION超时了,对应的节点会被删除,被删除时,Zookeeper 会通知对该节点父节点进行监听的客户端, 这样对应的客户端又可以刷新本地缓存了。当有新服务加入时,同样也会通知对应的客户端,刷新本地缓存,要达到这个目标需要客户端重复的注册对父节点的监听。这样就实现了服务的自动注册和自动退出。
Spring Cloud 生态也提供了Zookeeper注册中心的实现,这个项目叫 Spring Cloud Zookeeper 下面我们来进行实战。
项目说明:
为了简化需求,我们以两个服务来进行讲解,实际使用时可以举一反三
user-center : 用户服务
product-center: 产品服务
用户调用产品服务,且实现客户端的负载均衡,产品服务自动加入集群,自动退出服务。
项目构建
使用spring网站构建项目
打开spring网站,它给我们提供了便捷的构建流程start.spring.io/
最后点击GENERATE下载项目,通过上面的方式,我们再创建product-center。
解压下载的项目,用IDE打开
1、使用maven导入项目
2、查看导入的项目\
配置zookeeper服务
我们配置的方案为:product-center和user-center可以互相调用的方式。
user-center服务
application.properties
#服务的名称,后面调用就使用这个名称
spring.application.name=user-center
#zookeeper 连接地址 ,
#如果使用了 spring cloud zookeeper config这个配置应该配置在 bootstrap.yml/bootstrap.properties中
spring.cloud.zookeeper.connect-string=192.168.253.131:2181
#将本服务注册到zookeeper,如果不希望自己被发现可以配置为false, 默认为 true
spring.cloud.zookeeper.discovery.register=true
代码编写:
修改启动类,添加负载均衡支持
配置 Resttemplate 支持负载均衡方式,这样就可以多服务管理了
package com.jony.usercenter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class UserCenterApplication {
public static void main(String[] args) {
SpringApplication.run(UserCenterApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@Bean干啥的?
Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
@LoadBalanced又是干啥的?
在使用 RestTemplate 的时候 如果 RestTemplate 上面有 这个注解,那么 这个 RestTemplate 调用的 远程地址,会走负载均衡器
编写测试类
TestController, Spring Cloud 支持 Feign, Spring RestTemplate,WebClient 以 逻辑名称,替代具体url的形式访问,如下调用produce不适用ip+port而是改为服务名
package com.jony.usercenter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
@RestController
public class TestController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
public String test(){
return this.restTemplate.getForObject( "http://product-center/getInfo" ,String.class);
}
@GetMapping("/getInfo")
public String getServerPortAndName(HttpServletRequest request){
return "user-center" +" : "+ request.getServerPort();
}
}
product-center 服务:
application.properties
#服务名称
spring.application.name=product-center
#zookeeper 连接地址
spring.cloud.zookeeper.connect-string=192.168.253.131:2181
#将本服务注册到zookeeper
spring.cloud.zookeeper.discovery.register=true
代码编写
修改启动类
package com.jony.productcenter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
@SpringBootApplication
public class ProductCenterApplication {
public static void main(String[] args) {
SpringApplication.run(ProductCenterApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
编写主类,提供服务
主类,接收一个getInfo 请求
package com.jony.productcenter;
import lombok.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
@RestController
public class ProductController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/getInfo")
public String getServerPortAndName(HttpServletRequest request){
return "product-center" +" : "+ request.getServerPort();
}
@GetMapping("/getUser")
public String getUserCenter(HttpServletRequest request){
return this.restTemplate.getForObject( "http://user-center/getInfo" ,String.class);
}
}
启动两个product-center 实例
通过idea 配置,启动多个实例,用端口区分不同的应用
1、打开配置界面
2、配置端口号服务名称再复制一个
3、修改复制服务的服务名称及端口号
4、启动product的两个服务
user-center服务启动
启动一个user-center 实例,默认8080端口
三个服务全部启动完毕,开始浏览器访问
执行结果
user-center调用product-center
访问user-cent端(一个服务),去调用product-center(两个服务) http://localhost:8080/test
通过刷新浏览器可以看到,我们成功的完成了通过服务名称进行互相调用的方案啦。这个时候,我们关闭其中product-center:1001,再次刷新浏览器查看结果
我们可以发现,因为关闭了一个produc-center服务,在调用的时候还是会指向这个服务,导致页面发生错误,过一段时间之后,再次刷新,浏览器就可以正常访问,并且只有product-center:1002的服务。
product-center调用user-center
可以正常访问,因为user-center只启动了一个服务,因此端口只有8080,有兴趣的同学可以自行再启动一个服务端口。
查看zookeeper客户端信息
我们可以通过连接zookeeper的客户端查看相关信息,如下:
我们的服务注册到了zookeeper 下 service节点下。
进入product或者ucenter下的子节点 都是一个uuid生成的临时节点,然后我们查节点数据可以得到相关信息,如下:
总结
在分布式服务集群的情况下,我们可以使用zookeeper来作为注册中心,让我们服务之间的调用,变得便捷,缺点是某个服务在宕机的情况下,无法很快的对非正常服务进行阻断,这个不用担心,目前有很多框架可以实现阻断功能.后面再进行讲述。
要实现服务可以通过服务名进行注册,主要就是以下几个步骤:
1、在配置文件中,声明服务名称,以及将服务注册到zookeeper中。
2、在springboot启动类中,添加Resttemplate,来支持负载均衡
3、调用服务的时候使用服务名称和暴露的方法服务名称。