一,服务注册中心概述
一,注册中心简介
注册中心类似于微服务架构中的"通讯录",它记录了服务和服务地址的映射关系,在分布式架构中,服务会注册到这里,当服务需要调用其他服务时,就到注册中心找服务的地址,进行调用
我们在手机的通讯录的两个使用场景:① 服务注册 ② 服务发现
- 当我想给法外狂徒张三打电话时,那我需要在通讯录中按照张三的名字为检索寻找张三,然后就可以找到他的电话号码拨打电话 ----- 服务的发现
- 李四办了个手机号码发微信告诉我,我把李四的电话号码存击通讯录,后续我就可以从通讯录找到他 ----服务的注册
二,常见的注册中心
注册中心:
- NetFlix Eureka
- Alibaba Nacos
- HashiCorp Consul
- Apache Zookeeper
- CoreOS Etcd
- CNCF CoreDNS
三,需要注册中心的原因和其需要解决的问题
在分布式系统中,开发者不仅仅只是需要注册中心找到服务和服务地址的映射关系这么简单,它还需要解决更多复杂的问题:
- 服务注册后,如何被及时发现
- 服务宕机后,如何及时下线
- 服务如何有效的水平拓展
- 服务发现时,如何进行路由
- 服务异常时,如何进行降级
- 注册中心如何实现自身的高可用
这些问题的解决都依赖于服务注册中心,简单的看,服务注册中心的功能类似与DNS服务器,你输入域名它帮你定位到具体的服务器并返回数据,注册中心作为微服务的基础组件,可能要更加复杂,也需要更多的灵活性和时效性
做为注册中心主要解决的问题:
- 服务的管理
- 服务依赖关系的管理
二,Eureka概述
Spring Cloud封装了Netfix公司开发的Eureka模块来实现服务治理
在传统的RPC远程调用框架中,管理每一个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务与服务之间的依赖关系,可以实现服务调用,负载均衡,容错等,实现服务发现与注册
服务治理:就是包含服务发现,服务发现,服务调用等一系列策略的集合
一,Eureke的服务注册和发现
Eureke采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心,而系统中的其他微服务,使用Eureka Client的客户端连接到Eureka Server并维护心跳连接,这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行
在服务注册与发现中,有一个注册中心,当服务启动的时候,会把当前自己的服务器信息 比如:服务地址,通信地址等以别名的方式注册到注册中心上,另一方(消费者|服务提供者),以别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用或RPC远程调用
二,Eureka的两大组件
两大组件:
- Eureka Server:提供服务的注册和发现
- Eureka Client:提供注册中心进行访问(远程调用)
1,Eureka Server
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表将存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到
2,Eureka Client
是一个java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的,使用轮询(round-robin)负载算法的负载均衡器,在应用启动后,将会向Eureka Server发送心跳(默认周期30秒),如果Eureka Server在多个心跳周期内没有接收某个节点的心跳,EurekaServer将会从服务注册表中把这个服
三,Eureka服务端的安装和客户端的使用
一,Eureka服务端的安装
Eureka服务端内嵌于SpringBoot,依赖SpringBoot进行构建,它不同于Zookeeper后者有独立的服务端,需要linux单独安装
安装步骤:
- 新建一个SpringBoot项目(模块)
- 导入相关的Maven
- 配置application.yml文件(服务地址,本身是否注册等)
- 在启动类上标注@EnableEurekaServer表示为服务端
- 通过URL访问Eureka服务端
父工程依赖:
<?xml version="1.0" encoding="UTF-8"?> <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.0</modelVersion> <groupId>org.example</groupId> <artifactId>SpringCloud-Dome</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <lombok.version>1.18.10</lombok.version> <log4j.version>1.2.17</log4j.version> <mysql.version>5.1.31</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>2.1.1</mybatis.spring.boot.version> </properties> <dependencyManagement> <dependencies> <!--dependencies代表父工程--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.2.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
① 新建SpringBoot模块省略
② 导入Maven依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--eureka服务端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies>
③ 配置application.yml文件
server: port: 9090 eureka: instance: hostname: localhost #eureka实例的名称 client: register-with-eureka: false #flase表示不向注册中心注册自己(服务端不用注册) fetch-registry: false #flase表示自己就是注册中心,我的职责是维护实例,并不需要检索实例 service-url: #设置与Eureka Server交互和访问的地址,之后服务注册和调用都需要这个地址 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
④ 在启动类上标注@EnableEurekaServer
@SpringBootApplication @EnableEurekaServer public class EurekaServerMain { public static void main(String[] args) { SpringApplication.run(EurekaServerMain.class,args); } }
⑤ 通过URL访问Eureka服务端
二,client服务驻入Eureka服务端
Eureka Server作为服务器需要单独安装运行,而Client是嵌入到具体的项目模块中的,无论是生产者还是消费者都需要Eureka Client跟Eureka Server通从而实现服务注册,服务发现,调用
步骤:
- 新建一个SpringBoot的业务模块
- 导入相关的Eureka Client依赖
- 配置application.yml文件(Eureka服务端的地址等)
- 在启动类上标注@EnableEurekaClient表示为客户端
- 通过URL访问Eureka服务端看是否有该Client端
父工程和Eureka服务端安装是一样的(省略)
① SpringBoot业务模块省略
② 导入相关的Eureka Client依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${cloud-springbootweb}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> <version>${cloud-springbootactuator}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>${cloud-devotools}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${cloud-lombok}</version> <scope>provided</scope> </dependency> <!--Eureka客户端依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.0</version> </dependency> </dependencies>
③ 配置application.yml文件
server: port: 8080 eureka: client: fetch-registry: true #是否抓取已有的注册信息,默认为true,单节点无所谓,集群时必须设置为true才能配合Ribbon使用负载均衡 register-with-eureka: true #是否将自己注册进Eureka Server service-url: defaultZone: http://localhost:9090/eureka #Eureka的地址
④ 启动类上标注@EnableEurekaClient
@SpringBootApplication @EnableEurekaClient public class PayMentMain { public static void main(String[] args) { SpringApplication.run(PayMentMain.class,args); } }
⑤ 测试是否成功
四,Eureka集群搭建和使用
Eureka集群的作用:实现高可用和避免单点故障
Eureka集群顾名思义就是有多台Eureka Server,它们组成一个Eureka整体对外只暴露一个集群地址
Eureka搭建集群的方法:Server相互注册,通过service-url:defaultZone保存连接
演示:新建两个服务一个cloud-eureka-server1和cloud-eureka-server2
由于是单机演示不能用两个IP,所以修改本地hosts文件进行映射
127.0.0.1 eureka1.com 127.0.0.1 eureka2.com
服务 | cloud-eureka-server1 | cloud-eureka-server2 |
IP | eureka1.com | eureka2.com |
端口 | 9090 | 9091 |
1,cloud-eureka-server1的配置(其他和单机部署一样,省略)
server: port: 9090 eureka: instance: hostname: eureka1.com #eureka实例的名称 client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://qureka.com:9091/eureka/#绑定cloud-eureka-server2
2,cloud-eureka-server2的配置
server: port: 9091 eureka: instance: hostname: eureka2.com client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://eureka1.com:9090/eureka/ #相互绑定
测试:
① 访问cloud-eureka-server1的eureka1.com:9090
② 访问cloud-eureka-server2的eureka2.com:9091
二,多个Eureka Client构成服务集群
之前是Eureka服务端搭建集群,其实消费(客户)端也可以组成一个集群对外提供服务
前期准备:新建三个Eureka Client模块一个订单模块,两个支付模块
模块名称 |
cloud-consumer-order
|
cloud-payment-server1
|
cloud-payment-server2
|
端口
|
8081
|
8080
|
8082
|
client服务名称 |
cloud-consumer-server
|
cloud-payment-server
|
cloud-payment-server
|
一,cloud-consumer-order模块的搭建
① pom依赖
<properties> <cloud-springbootweb>2.5.5</cloud-springbootweb> <cloud-springbootactuator>2.5.5</cloud-springbootactuator> <cloud-devotools>2.5.5</cloud-devotools> <cloud-lombok>1.18.2</cloud-lombok> </properties> <dependencies> <dependency> <groupId>com.cloud.commons</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${cloud-springbootweb}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> <version>${cloud-springbootactuator}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>${cloud-devotools}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${cloud-lombok}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.0</version> </dependency> </dependencies>
② application配置
server: port: 8081 #端口 spring: application: name: cloud-consumer-server #服务名称 eureka: client: fetch-registry: true #是否抓取已有的注册信息,默认为true,单节点无所谓,集群时必须设置为true才能配合Ribbon使用负载均衡 register-with-eureka: true #是否将自己注册进Eureka Server service-url: #eureka server地址,因为是eureka集群所以加入两个server地址 defaultZone: http://eureka1.com:9090/eureka/,http://eureka2.com:9091/eureka/
③ 服务类配置
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced //让RestTemplate类可以进行负载均衡(轮询) public RestTemplate getRestTemplate(){ //RestTemplate底层封装了HttpClient可以实现URL的访问,提供这个对象实现访问的调用,生产者提供访问的URL和端口,消费者就通过http服务进行调用 return new RestTemplate(); } }
@RequestMapping("/consumer") @Slf4j public class ConsumerController { //CLOUD-PAYMENT-SERVER为服务名称,搭建完cloud-payment-server生产者服务后提供的服务地址 public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVER"; @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/payment/create",method = RequestMethod.GET) public CommonResult<payment> create(payment payments){ /* 方法:postForObject发送POST请求 参数一:url地址 参数二:请求参数(可以传输类) 参数三:返回值类class */ return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payments,CommonResult.class); //调用生产者服务 } @RequestMapping("/payment/select/{id}") public CommonResult<payment> select(@PathVariable("id") int id){ return restTemplate.getForObject(PAYMENT_URL+"/payment/select/"+id,CommonResult.class); } }
二,cloud-payment-server模块的搭建
<properties> <cloud-springbootweb>2.5.5</cloud-springbootweb> <cloud-springbootactuator>2.5.5</cloud-springbootactuator> <cloud-springbootmybatis>2.1.0</cloud-springbootmybatis> <cloud-druid>1.2.1</cloud-druid> <cloud-mysql>5.1.47</cloud-mysql> <cloud-jdbc>2.5.5</cloud-jdbc> <cloud-devotools>2.5.5</cloud-devotools> <cloud-lombok>1.18.2</cloud-lombok> </properties> <dependencies> <dependency> <groupId>com.cloud.commons</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${cloud-springbootweb}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> <version>${cloud-springbootactuator}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${cloud-springbootmybatis}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${cloud-druid}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${cloud-mysql}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <version>${cloud-jdbc}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>${cloud-devotools}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${cloud-lombok}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.0</version> </dependency> </dependencies>
② Mybatis的xml映射文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="springcloud.dao.PaymentDao"> <insert id="insert" parameterType="com.cloud.commons.entities.payment" > insert into payment(serial) values (#{serial}) </insert> <resultMap id="mypayment" type="com.cloud.commons.entities.payment"> <id property="id" column="id" javaType="int" jdbcType="INTEGER"/> <id property="serial" column="serial" javaType="string" jdbcType="VARCHAR"/> </resultMap> <select id="select" parameterType="int" resultMap="mypayment"> select * from payment where id=#{id} </select> </mapper>
③ pojo,server,返回值工具类 等省略(太多了)
④ controller
@Controller @RequestMapping("/payment") @Slf4j public class PaymentController { @Resource PaymentServiceImpl paymentService; @Value("${server.port}") String server; @RequestMapping(value = "/create",method = RequestMethod.POST) @ResponseBody public CommonResult create(@RequestBody payment pay){ boolean b = paymentService.create(pay); if(b){ log.info("----插入成功----"); return new CommonResult(200,"插入成功:服务"+server); } return new CommonResult(400,"插入失败"); } @RequestMapping(value = "/select/{id}",method = RequestMethod.GET) @ResponseBody public CommonResult create(@PathVariable("id") int id) { payment paymentByid = paymentService.getPaymentByid(id); if(paymentByid!=null){ log.info("----查询成功-----"); return new CommonResult<payment>(200,"查询成功:服务"+server,paymentByid); } return new CommonResult(400,"查询失败"); } }
⑤ application的配置
1,cloud-payment-server1的配置
server: port: 8080 spring: application: name: cloud-payment-server datasource: type: com.alibaba.druid.pool.DruidDataSource username: root password: 123 url: jdbc:mysql://localhost:3306/springcloud_test?useUnicode=true&characterEncoding=utf8 driver-class-name: org.gjt.mm.mysql.Driver mybatis: mapper-locations: classpath:mapper/*.xml eureka: client: fetch-registry: true #是否抓取已有的注册信息,默认为true,单节点无所谓,集群时必须设置为true才能配合Ribbon使用负载均衡 register-with-eureka: true #是否将自己注册进Eureka Server service-url: defaultZone: http://eureka1.com:9090/eureka/,http://eureka2.com:9091/eureka/
2,cloud-payment-server2的配置(只是修改的部分)
server: port: 8082 #端口 spring: application: name: cloud-payment-server #服务名和上面是一样的,这样两个模块集群就只提供一个服务
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109