注册中心相当于买票乘车,只看有没有票(有没有服务),有就去买票(获取注册列表),然后乘车(调用)。不必关心有多少火车在运行。
注册中心好处:不用关心有多少提供方。
注册中心有哪些:Eureka,Nacos,Consul,Zookeeper等。
服务注册与发现包括两部分,一个是服务器端,另一个是客户端。
Server是一个公共服务,为Client提供服务注册和发现的功能,维护注册到自身的Client的相关信息,同时提供接口给Client获取注册表中其他服务的信息,使得动态变化的Client能够进行服务间的相互调用。
Client将自己的服务信息通过一定的方式登记到Server上,并在正常范围内维护自己信息一致性,方便其他服务发现自己,同时可以通过Server获取到自己依赖的其他服务信息,完成服务调用,还内置了负载均衡器,用来进行基本的负载均衡。
我们课程的Spring Cloud是用Eureka作为服务注册中心。
Eureka:是一个RESTful风格的服务,是一个用于服务发现和注册的基础组件,是搭建Spring Cloud微服务的前提之一,它屏蔽了Server和client的交互细节,使得开发者将精力放到业务上。
serverA从serverB同步信息,则serverB是serverA的peer。
上面例子中如果service-url为空,且register-with-eureka,fetch-registry为true,则会报错,Cannot execute request on any known server,因为server同时也是一个client,他会尝试注册自己,所以要有一个注册中心url去注册。
Netflix开源的组件。包括server和client两部分。
https://github.com/Netflix/Eureka
服务注册表:记录各个微服务信息,例如服务名称,ip,端口等。
注册表提供 查询API(查询可用的微服务实例)和管理API(用于服务的注册和注销)。
服务注册与发现:注册:将微服务信息注册到注册中心。发现:查询可用微服务列表及其网络地址。
服务检查:定时检测已注册的服务,如发现某实例长时间无法访问,就从注册表中移除。
组件:Eureka , Consul , ZooKeeper,nacos等。
(在一台机器上模拟多服务)
- 首先修改本机的hosts文件(C盘–>windows–>system32–>drivers–>etc–>hosts),模拟出两台机器(域名),分别为euk1.com和euk2.com分别指向本机的localhost,如果有两台服务器或者机器的,可以跳过这个步骤。
- 在创建项目时,勾选Eureka-server或者导入下面的starter。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
- 配置properties配置文件
euk1.com上的配置
#是否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
eureka.client.fetch-registry=true
#设置服务注册中心的URL,把euk1.com 注册到euk2.com节点上
eureka.client.service-url.defaultZone=http://euk2.com:7902/eureka/
#每个服务的主机名
eureka.instance.hostname=euk1.com
#项目的项目名,主要用来区分是否是同一个服务,一个服务可以部署在多台机器上,用此名称进行分组
#如果euk1和euk2的name相同,表示这两个服务为同一组服务,那么不同表示为不同的服务
spring.application.name=eureka-server
##必须本服务注册url的端口保持一致http://euk1.com:7901/eureka/
server.port=7901
euk2.con配置
#是否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
eureka.client.fetch-registry=true
#设置服务注册中心的URL,把euk2注册到euk1上
eureka.client.service-url.defaultZone=http://euk1.com:7901/eureka/
#当前服务的名称
eureka.instance.hostname=euk2.com
#项目名或者服务所属组名,用于进行服务分组
spring.application.name=eureka-server
#必须本服务注册url的端口保持一致http://euk2.com:7902/eureka/
server.port=7902
#是否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
eureka.client.register-with-eureka=false
#是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
eureka.client.fetch-registry=false
#设置服务注册中心的URL,把euk1.com 注册到euk2.com节点上
eureka.client.service-url.defaultZone=http://euk1.com:7901/eureka/
#每个服务的主机名
eureka.instance.hostname=euk1.com
#eureka服务的端口号
server.port=7901
application.propertise
spring.profiles.active=privideXX
spring.application.name=sayHi
application-privide01.propertise
eureka.instance.hostname=privide01.com
eureka.client.service-url.defaultZone=http://euk1.com:7901/eureka/
server.port=8081
application-privide02.propertise
eureka.instance.hostname=privide02.com
eureka.client.service-url.defaultZone=http://euk1.com:7901/eureka/
server.port=8082
application-privide03.propertise
eureka.instance.hostname=privide03.com
eureka.client.service-url.defaultZone=http://euk1.com:7901/eureka/
server.port=8083
controller
@RestController
public class SayHiController {
@RequestMapping("/sayHi")
public String sayHi(){
return "你好,大帅哥!";
}
}
eureka.client.service-url.defaultZone=http://euk1.com:7901/eureka/
spring.application.name=eureka-consumer
server.port=8080
controller层
@RestController
public class ConsController {
//eureka 实现的客户端接口
@Autowired
EurekaClient client;
//实现服务调用的负载均衡接口
@Autowired
LoadBalancerClient loadBalancer;
/**
* 简单的远程服务调用
* @return
*/
@RequestMapping("/consayhi")
public String consSayHi(){
String result = "";
List<InstanceInfo> instanceInfos = client.getInstancesByVipAddress("sayHi", false);
for (InstanceInfo ins:instanceInfos){
System.out.println(ToStringBuilder.reflectionToString(ins));
}
if (instanceInfos.size()>0){
InstanceInfo instanceInfo = instanceInfos.get(0);
if (instanceInfo.getStatus() == InstanceInfo.InstanceStatus.UP){
String url = "http://"+instanceInfo.getHostName()+":"+instanceInfo.getPort()+"/sayHi";
RestTemplate restTemplate = new RestTemplate();
result = restTemplate.getForObject(url, String.class);
}
}
return result;
}
/**
* 带服务调用负载均衡的远程服务调用
* @return
*/
@RequestMapping("/consayhi1")
public String consSayHi1(){
String result = "";
//通过loadBalance的负载均衡算法,返回多个服务中的一个服务
ServiceInstance instance = loadBalancer.choose("sayHi");
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/sayHi";
RestTemplate restTemplate = new RestTemplate();
result = restTemplate.getForObject(url,String.class);
return result;
}
}