服务注册中心是服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来存储服务信息,譬如提供者url串、路由信息等,服务注册中心是微服务架构中最基础的设施之一。
在微服务架构流行之前,注册中心就开始出现分布式框架的系统。Dubbo是一个在国内比较流行的分布式框架,被大量的中小型互联网公司所采用,他提供比较完善的服务治理功能,而服务治理的实现主要依靠的就是注册中心。
学习目标:什么是注册中心,常见的注册中心,为什么需要注册中心,注册中心解决什么问题,什么是Eureka注册中心,Eureka注册中心三种角色,Eureka入门案例等
注册中心可以说是微服务架构中的"通讯录",它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其他服务时,就到这里找到服务是的地址,进行调用。
服务注册中心的作用就是服务的注册和服务的发现。
服务发现:在通讯录里找到服务
服务注册:将服务存入通讯录里
在分布式系统中,我们不仅仅是需要注册中心找到服务和服务地址的映射关系那么简单,我们还需要考虑更多复杂的问题:
服务注册后,如何被及时发现
服务宕机后,如何及时下线
服务如何有效的水平扩展
服务发现时,如何进行路由
服务异常时,如何进行降级
注册中心如何实现自身的高可用
这些问题的解决都依赖于注册中心。简单开,注册中心的功能有点类似于DNS服务器或者负载均衡器,而实际上,注册中心作为微服务的基础组件,可能要更加复杂,也需要更多的灵活性和时效性。所以我们还需要学习更多的Spring Cloud 微服务组件协同完成应用开发。
Eureka是Netflix开发的服务发现组件,本身是个基于REST的服务。Spring Cloud 将它集成在其子项目Spring Cloud Netflix 中,实现spring cloud的服务注册与发现,还提供了负载均衡、故障转移等能力。
通过Register、Get、Renew等接口提供服务的注册和发现。
服务提供方,把自身的服务实例注册到Eureka Server中。
服务调用方,通过Eureka Server获取服务列表,消费服务。
采用聚合项目来讲解Eureka,首先创建一个pom父工程。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.xxxxgroupId>
<artifactId>eureka-demoartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<modules>
<module>eureka-servermodule>
modules>
<parent>
<artifactId>spring-boot-starter-parentartifactId>
<groupId>org.springframework.bootgroupId>
<version>2.3.12.RELEASEversion>
parent>
<properties>
<spring-cloud.version>Hoxton.SR1spring-cloud.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
spring-boot-starter-parent 是 Spring Boot 的父级依赖,这样当前的项目就是 Spring Boot 项目了。spring-boot-starter-parent 是一个特殊的 starter,它用来提供相关的 Maven 默认依赖。使用它之后,常用的包依赖可以省去 version 标签
application.yml
server:
port: 8761 #端口
spring:
application:
name: eureka-server #应用名称
# 配置Eureka Server 注册中心
eureka:
instance:
hostname: localhost # 主机名 不配置的时候将根据操作系统的主机名来获取
client:
register-with-eureka: false # 是否将自己注册到注册中心,默认为true 如果是单体应用 则不需要注册
fetch-registry: false # 是否从注册中心获取服务注册信息 默认为true
service-url: # 注册中心对外暴露的注册地址
defaultZone:
http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone:http://${eureka.instance.hostname}:${server.port}/eureka/
eureka默认开启将自己注册到注册中心,如果应用是单节点,要关闭着两个配置register-with-eureka、
fetch-registry。
EurekaServerApplication.java
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class);
}
}
注意:@EnableEurekaServer 使用注解开启eureka注册中心。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>eureka-demoartifactId>
<groupId>com.xxxxgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>eureka-serverartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
访问连接localhost:8761
(可建立两个eureka注册中心)
暂时采用一个eureka注册中心,过程如7所示。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.18version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 7070 #端口
spring:
application:
name: service-provider #应用名称
eureka:
instance:
prefer-ip-address: true #是否使用ip地址注册
instance-id: ${spring.cloud.client.ip-address}:${server.port} # id :port
client:
service-url:
defaultZone: #设置服务注册中心地址
http://localhost:8761/eureka/
1、实体类
com.xxxx.pojo包下 production.java
package com.xxxx.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.yaml.snakeyaml.events.Event;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Production implements Serializable {
private Integer id;
private String productName;
private Integer productNum;
private Double productPrice;
}
Serializable 序列化
Serializable 序列化
序列化:对象的寿命通常随着生成该对象的程序的终止而终止,有时候需要把在内存中的各种对象的状态(也就是实例变量,不是方法)保存下来,并且可以在需要时再将对象恢复。虽然你可以用你自己的各种各样的方法来保存对象的状态,但是Java给你提供一种应该比你自己的好的保存对象状态的机制,那就是序列化。
总结:Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里(系列化),并且可以从其它地方把该Byte 流里的数据读出来(反序列化)。
序列化 的用途
2、service服务类和实现类
package com.xxxx.service;
import com.xxxx.pojo.Production;
import java.util.List;
/**
* @author xuan
*/
public interface ProductService {
/**
* 查询商品列表
* @return
*/
List<Production> selectProductionList();
}
实现类
package com.xxxx.service.impl;
import com.xxxx.pojo.Production;
import com.xxxx.service.ProductService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
* @author xuan
*/
@Service
public class ProductServiceImpl implements ProductService {
@Override
public List<Production> selectProductionList(){
return Arrays.asList(
new Production(1,"华为手机",2,5888D),
new Production(2,"联想笔记本",1,6888D),
new Production(3,"小米平板",5,2666D)
);
}
}
package com.xxxx.controller;
import com.xxxx.pojo.Production;
import com.xxxx.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author xuan
*/
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/list")
public List<Production> selectProductList(){
return productService.selectProductionList();
}
}
@SpringBootApplication
@EnableEurekaClient
public class EurekaProviderApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaProviderApplication.class,args);
}
}
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.18version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 9090 #端口
spring:
application:
name: service-consumer #应用名称
eureka:
client:
service-url:
defaultZone: #设置服务注册中心地址
http://localhost:8761/eureka/
register-with-eureka: false #不将服务注册到注册中心
registry-fetch-interval-seconds: 30 #每30秒拉取一次服务列表
1、实体类
produc.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Serializable {
private Integer id;
private String productName;
private Integer productNum;
private Double productPrice;
}
order.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order implements Serializable {
private Integer id;
private String orderNo;
private String orderAddress;
private Double totalPrice;
private List<Product> productList;
}
2、服务类
服务接口
package com.xxxx.service;
import com.xxxx.pojo.Order;
public interface OrderService {
/**
* 根据主键查询订单
* @param id
* @return
*/
Order selectOrderById(Integer id);
}
服务实现类 OrderServiceImpl.java
package com.xxxx.service.impl;
import com.xxxx.pojo.Order;
import com.xxxx.pojo.Product;
import com.xxxx.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
/**
* 根据主键查询订单
* @param id
* @return
*/
@Override
public Order selectOrderById(Integer id){
return new Order(id,"order-001","中国",31994D,
selectProductListByDiscoveryClient());
}
private List<Product> selectProductListByDiscoveryClient(){
StringBuffer sb = null;
// 获取服务列表
List<String> serviceIds = discoveryClient.getServices();
//collectionutils 集合判断是否为空
if (CollectionUtils.isEmpty(serviceIds)) {
return null;
}
// 根据服务名称获取服务
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("service-provider");
if(CollectionUtils.isEmpty(serviceInstances)) {
return null;
}
//instance 实例
ServiceInstance si = serviceInstances.get(0);
sb = new StringBuffer();
sb.append("http://"+si.getHost()+":"+si.getPort()+"/product/list");
//ResponseEntity 封装了返回数据
ResponseEntity<List<Product>> response = restTemplate.exchange(
sb.toString(),
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Product>>() {}
);
return response.getBody();
}
}
重点代码是:获取服务列表,根据服务名称获取服务(为集合),根据服务的url获取封装的数据。
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/{id}")
public Order selectOrderById(@PathVariable("id") Integer id){
return orderService.selectOrderById(id);
}
}
@EnableEurekaClient
@SpringBootApplication
public class EurekaConsumerApplication {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerApplication.class,args);
}
}
总结:最主要是学会消费者,client如何获取服务列表和内容,拼接数据。
将三个项目都启动,访问消费者的接口
localhost:9090/order/1 可以获取远程接口localhost:7070/product/list信息。
http://localhost:8761
http://localhost:9090/order/1
http://localhost:7070/product/list