前面两次写的是关于SpringCloud微服务以及Eureka注册和发现的,中间由于其他原因我没有继续往后更新,现在正值暑假期间,我又可以接着学习并更新博客了。
首先简单介绍一下什么是Ribbon,Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。
我们新建一个项目名为hello-spring-cloud,然后创建如下的目录子工程,截图如下:
其中hello-spring-cloud-dependencies作为项目的父工程,主要含pom.xml文件,用来Spring Cloud环境的基本配置,并且这里把一些资源文件也一起配置了,代码如下:
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<groupId>com.chen</groupId>
<artifactId>hello-spring-cloud-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>hello-spring-cloud-dependencies</name>
<inceptionYear>2019-Now</inceptionYear>
<properties>
<!-- Environment Settings -->
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring Settings -->
<spring-cloud.version>Greenwich.M1</spring-cloud.version>
</properties>
<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>
<!-- Compiler插件,设定JDK版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<!-- 打包jar文件时,配置manifest文件,加入lib包的jar依赖 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<!-- Add directory entries -->
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
<!-- resource -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<!-- install -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
</plugin>
<!-- clean -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
<!-- ant -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
</plugin>
<!-- dependency -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!-- Java Document Generate -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- YUI Compressor (CSS/JS压缩) -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<version>1.5.1</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>compress</goal>
</goals>
</execution>
</executions>
<configuration>
<encoding>UTF-8</encoding>
<jswarn>false</jswarn>
<nosuffix>true</nosuffix>
<linebreakpos>30000</linebreakpos>
<force>true</force>
<includes>
<include>**/*.js</include>
<include>**/*.css</include>
</includes>
<excludes>
<exclude>**/*.min.js</exclude>
<exclude>**/*.min.css</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<!-- 资源文件配置 -->
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>aliyun-repos</id>
<name>Aliyun Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>sonatype-repos</id>
<name>Sonatype Repository</name>
<url>https://oss.sonatype.org/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>sonatype-repos-s</id>
<name>Sonatype Repository</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-repos</id>
<name>Aliyun Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
hello-spring-cloud-eureka子项目作为服务的注册中心,其pom.xml文件主要引入Eureka Server,并且要继承父工程的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.chen</groupId>
<artifactId>hello-spring-cloud-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../hello-spring-cloud-dependencies/pom.xml</relativePath>
</parent>
<artifactId>hello-spring-cloud-eureka</artifactId>
<packaging>jar</packaging>
<name>hello-spring-cloud-eureka</name>
<inceptionYear>2019-Now</inceptionYear>
<dependencies>
<!-- Spring Boot Begin -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot End -->
<!-- Spring Cloud Begin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- Spring Cloud End -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 必须要配置,作为运行入口 -->
<mainClass>hello.chen.spring.cloud.eureka.EurekaApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
在上述代码中,我们配置了其开启的运行入口EurekaApplication类,添加@EnableEurekaServer是为了确保开启Eureka服务。其代码如下:
package hello.chen.spring.cloud.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
在resources目录下,我们新建application.yml文件,该文件用于表明该子项目作为服务端而不是客户端,代码如下:
spring:
application:
name: hello-spring-cloud-eureka
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
#作为服务端
register-with-eureka: false #自己不注册
fetch-registry: false
service-url:
#单机版
default-zone: http://${eureka.instance.hostname}:${server.port}/eureka/
其中register-with-eureka以及fetch-registry属性均为false,表明自身是服务端,自身不注册。如果这两行去掉了,就表明自身是客户端。
接下来是hello-spring-cloud-service-admin子项目,该子项目作为服务的提供者,其pom.xml文件代码如下:
<?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>
<parent>
<groupId>com.chen</groupId>
<artifactId>hello-spring-cloud-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../hello-spring-cloud-dependencies/pom.xml</relativePath>
</parent>
<artifactId>hello-spring-cloud-service-admin</artifactId>
<packaging>jar</packaging>
<name>hello-spring-cloud-service-admin</name>
<inceptionYear>2019-Now</inceptionYear>
<dependencies>
<!-- Spring Boot Begin -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot End -->
<!-- Spring Cloud Begin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- Spring Cloud End -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.chen.hello.spring.cloud.service.admin.ServiceAdminApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
其配置了服务开启的端口ServiceAdminApplication,这里我们作为客户端,所以添加EnableEurekaClient注解,其代码如下:
package com.chen.spring.cloud.service.admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class ServiceAdminApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAdminApplication.class,args);
}
}
application.yml配置文件代码如下:
spring:
application:
name: hello-spring-cloud-service-admin
server:
port: 8762
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka/
我们新建controller包,创建一个控制器类AdminController,主要用于对Eureka服务的客户端进行测试,其代码如下:
package com.chen.spring.cloud.service.admin.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AdminController {
@Value("${server.port}")
private String port;
@GetMapping(value = "/hi")
public String sayHi(String message) {
return String.format("Hi,your message is:%s,port is %s",message,port);
}
}
写完上述代码后,我们先开启EurekaApplication类(服务器端),然后开启ServiceAdminApplication类(客户端),运行截图如下:
接下来就是如何添加Ribbon实现负载均衡了,我们创建hello-spring-cloud-web-admin-ribbon子项目,该项目主要用于实现负载均衡以及作为Eureka服务的消费者。其pom.xml文件如下:
<?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>
<parent>
<groupId>com.chen</groupId>
<artifactId>hello-spring-cloud-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../hello-spring-cloud-dependencies/pom.xml</relativePath>
</parent>
<artifactId>hello-spring-cloud-web-admin-ribbon</artifactId>
<packaging>jar</packaging>
<name>hello-spring-cloud-web-admin-ribbon</name>
<inceptionYear>2019-Now</inceptionYear>
<dependencies>
<!-- Spring Boot Begin -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot End -->
<!-- Spring Cloud Begin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- Spring Cloud End -->
<!-- 解决thymeleaf模板引擎一定要执行严格的html5格式校验问题 -->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.chen.hello.spring.cloud.web.admin.ribbon.WebAdminRibbonApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
然后我们在resources目录下新建application.yml文件,用于配置其默认地址以及端口号,其代码如下:
spring:
application:
name: hello-spring-cloud-web-admin-ribbon
thymeleaf:
cache: false
mode: LEGACYHTML5
encoding: UTF-8
servlet:
content-type: text/html
server:
port: 8764
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka/
从pom.xml文件中我们可以看出,其运行入口是WebAdminRibbonApplication类,我们编写如下代码:
package com.chen.hello.spring.cloud.web.admin.ribbon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class WebAdminRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(WebAdminRibbonApplication.class,args);
}
}
我们在上述代码中添加了@EnableDiscoveryClient注解,表明是Eureka服务的发现,我们知道,负载均衡中Ribbon和RestTemplate需要联合使用,我们创建config包,并新建RestTemplateConfiguration类,其代码如下:
package com.chen.hello.spring.cloud.web.admin.ribbon.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
其中@Configuration注解相当于SSM框架中的配置bean的xml文件,我们添加@LoadBalanced注解表明是负载均衡,接下来我们进行测试,创建service包,并新建AdminService类,其代码如下:
package com.chen.hello.spring.cloud.web.admin.ribbon.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class AdminService {
@Autowired
private RestTemplate restTemplate;
public String sayHi(String message) {
return restTemplate.getForObject("http://hello-spring-cloud-service-admin/hi?message="+message,String.class);
}
}
我们这里是调用了Eureka已注册服务的内容,我们创建controller包,并新建AdminController类,其代码如下:
package com.chen.hello.spring.cloud.web.admin.ribbon.controller;
import com.chen.hello.spring.cloud.web.admin.ribbon.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AdminController {
@Autowired
private AdminService adminService;
@GetMapping(value = "/hi")
public String sayHi(String message) {
return adminService.sayHi(message);
}
}
该控制类主要是调用了AdminService的sayHi方法,接下来我们为实现负载均衡的效果,必须有Eureka注册的集群模式,我们在运行8761和8762端口后,在hello-spring-cloud-service-admin子项目中修改其application.yml文件中端口为8763,我们点击工具栏的Run,然后选择Edit Configurations,左侧选择ServiceAdminApplication,然后右侧把Single Instance Only的√去掉,这样就可以保证同时开启多个Eureka服务注册了,我们再次运行,然后运行WebAdminRibbonApplication类,截图如下:
该项目运行截图如下:
(1)我们首先开启8761端口,可以发现有两台服务的提供者和一台服务的发现者端口,如下图所示。
(2)然后我们运行8763端口,并测试sayHi方法,我们通过刷新按钮,可以发现端口在8763与8762之间不断切换,这就是负载均衡的体现。
整个项目的整体架构如下图所示:
从上图我们可以看出,8761是Eureka的端口,8762和8763是经过注册得到的,它们都是作为服务的提供者,8764也是注册得到的,不过它是作为服务的发现者,通过Ribbon和RestTemplate对Eureka集群的请求作出相应。
好了,这次的讲解就到这里了,我们下期博客再见!