目录
一、目标
学会使用spring cloud ribbon进行服务负载调度,所有微服务由eureka发现注册服务。
二、组成成员
整个模块可以分化为3个部分:server管理中心(eureka-server),微服务群(eureka-provider),调用发起端(consumer)
- eureka-server:提供服务注册和发现
- eureka-provider:服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到(向注册中心服务注册、服务同步、服务续约)
- consumer:服务消费方从Eureka获取注册服务列表,从而能够消费服务(往注册中心获取服务、服务调用、服务下线)
三、示例
- 创建eureka-server
- 创建一个新的spring initializr服务
-
选择集成组件
选择组件既是导入了pom中需要依赖的jar
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
配置配置文件
server.port = 12345 spring.application.name = config-center eureka.instance.hostname:localhost #该应用为注册中心,不向自己注册自己 eureka.client.register-with-eureka=false #是否从euruka服务器获取注册信息 eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ logging.level.com.netflix.eureka:OFF
启动类添加eureka-server
@EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
启动服务,启动成功,那么eureka-server中心就完成了,在浏览器输入地址,访问http://localhost:12345
就会出现管理界面,日中Application为空,说明还没有服务注册进来。
- 创建微服务(eureka-provider,也是服务提供者),并注册到eureka-server中
- 还是创建springboot工程
- 只需要选择eureka客服端即可
- 服务是通过web请求,和server之间的联系也是通过web实现的,所以需要引入web依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.sdgtt.com</groupId> <artifactId>eureka-client1</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-client1</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
- 配置文件,确认端口和服务应用名称,还有注册中心地址
server.port = 10001 spring.application.name= sdgttSpringClient eureka.client.serviceUrl.defaultZone= http://localhost:12345/eureka/
- 以两个provide为例,具体代码入口,
- provide1
-
package com.sdgtt.com.eurekaclient1.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author tao * @version 2020-09-17 14:43 */ @RestController public class TestController { @GetMapping("/hello") public Object hello(){ return "hello,I'am service provider 1"; } }
-
provide2:
package com.sdgtt.com.eurekaclient1.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author tao * @version 2020-09-17 14:43 */ @RestController public class TestController { @GetMapping("/hello") public Object hello(){ return "hello,I'am service provider 2"; } }
两个服务返回内容不同。
-
- provide1
- provider在本地测试过程中可以设置成不同的端口,方便启动,如果是在不同宿主机上启动可以忽略端口问题
- 多个provider的spring.application.name必须一致,因为在向server中心注册时,是通过spring.application.name为名注册的
- 启动之后就会发现已经有服务注册进来,Application的name就是spring.application.name,两个provide,分别部署在局域网中的两台宿主机上。
- 创建消费者(consumer,去请求提供的服务),新建项目。在请求消费端的时候,那么该向哪一个provide发出请求呢,这就需要负载均衡请求各个provide,这里是由ribbon来实现负载均衡的。导入jar包依赖。
- pom
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.sdgtt.com</groupId> <artifactId>eureka-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-consumer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
- 配置文件,服务名这里可以随便指定,eureka服务地址还是必须是eureka-server服务的地址。
server.port = 8080 spring.application.name= ribbon-consume eureka.client.serviceUrl.defaultZone= http://localhost:12345/eureka/
- spring管理注入RestTemplate,RestTemplate 需要加上@LoadBalanced,申明需要负载均衡
package com.sdgtt.com.eurekaconsumer.configuration; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; /** * @author tao * @version 2020-09-17 15:53 */ @Component public class BeanConfig { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
- 访问consumer的controller入口
package com.sdgtt.com.eurekaconsumer.controller; import lombok.extern.slf4j.Slf4j; 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 org.springframework.web.client.RestTemplate; /** * @author tao * @version 2020-09-17 15:04 */ @Slf4j @RestController @RequestMapping("springboot/ribbon") public class ConsumerController { @Autowired private RestTemplate restTemplate; @GetMapping("/hello") public Object hello() { String result = restTemplate.getForObject("http://sdgttSpringClient/hello", String.class); log.info("sdgttSpringClient服务返回结果为:{}", result); return result; } }
- RestTemplate访问的地址不再是具体服务的ip,而是注册在server里的服务名,由eureka-server管理之后,eureka-server去解析ip地址然后请求。
- 这时候,查看eureka管理页面,发现consumer启动成功。
- 请求consumer服务,请求:http://localhost:8080/springboot/ribbon/hello,得到结果:
- 刷新地址请求,发现请求异常
- 异常原因是provide服务,主机( DESKTOP-OO7SCB2:sdgttSpringClient:10001)解析失败。原因是这个provide部署在局域网内的另一宿主机上。注册的时候注册的主机名不是ip导致的。
java.net.UnknownHostException: DESKTOP-OO7SCB2 at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) ~[na:1.8.0_151] at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_151] at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_151]
- 原因:是因为provide在eureka-server注册的时候注册成了宿主机的主机名,改成优先ip就好了,修改方法:修改eureka-server服务配置文件
server.port = 12345 spring.application.name = config-center eureka.instance.hostname:localhost #优先使用ip注册 eureka.instance.prefer-ip-address: true #该应用为注册中心,不向自己注册自己 eureka.client.register-with-eureka=false #是否从euruka服务器获取注册信息 eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ logging.level.com.netflix.eureka:OFF
-
如图,两个provide分别在不同的宿主机上启动成功
-
在继续访问得到结果,此时说明完成负载均衡
-
浏览器结果:
- pom
- code:https://github.com/foxiaotao/spring-cloud-demo