乐趣区

SpringCloud微服务部署

微服务的其中一个特点就是有许许多的粒度小(功能单一,比如用户管理,短信发送管理,邮件发送管理,文件管理等)、能独立部署、扩展、运行的小应用,可以称为 api, 也就是服务提供者。api 之间可以相互调用,但更多的是供 app 调用,比如学生管理系统,它是面向用户的,是许许多多功能的集合体,它需要调用许多 api 完成业务功能,所以这学生管理系统可以称为 app。
eureka 的作用
传统的单体应用开发,就是将 api 和 app 的代码全部集成在一起,在同一个进程中运行,对应 java web 通俗的说就是全部打包在一个 war 中部署在一个 tomcat 中运行。而微服务的每个 api,app 都拥有自己的进程,也就是都有自己的 tomcat,某个 api 挂了,不影响其他 api 和 app 运行。

api 是采取 restfull 风格暴漏出去的,所以 app(api)调用 api 时,采取 http://ip: 端口这形式进行通讯。那么问题来了,如果有很多 app 都调用了某个 api,如果 api 的 ip 或端口发生了更改,如果 app 中对 api 的 ip 和端口等信息是写在配置文件的,难道要通知每个 app,说这个 api 的地址和端口变了,app 要进行修改重新编译部署(这是举例子而已,实际情况有些企业对常量的配置可能写配置文件,也可能写数据库)。这太麻烦了,如果 api 的地址和端口有发生变化,app 能及时获知自行变更,那就好了。还有个弊端,就是 api 是否挂了,也没法直观观察到。
所以,如果在 app 和 api 之间增加个服务管理中心,api 像服务管理中心注册信息,app 从服务管理中心获取 api 的信息,api 有个唯一标识,api 有变更的时候通知服务管理中心,服务管理中心通知相关的 app 或者 app 定时从服务管理中心获取最新的 api 信息,同时服务管理中心具有很高的稳定性、可靠性。
eureka 就是为了这样的一个服务管理中心,下面是我根据自己的理解画的一张图。

下面这张是官方的架构图

1.Application Service 相当于服务提供者 /api
2.Application Client 相当于服务消费者 /app
3.Make Remote Call,其实就是实现服务的使用 / 比如 httpClient,restTemplate
4.us-east-1 Eureka 集群服务
5.us-east-1c、us-east-1d、us-east-1e 就是具体的某个 eureka
Eureka:

是纯正的 servlet 应用,需构建成 war 包部署
使用了 Jersey 框架实现自身的 RESTful HTTP 接口
peer 之间的同步与服务的注册全部通过 HTTP 协议实现
定时任务 (发送心跳、定时清理过期服务、节点同步等) 通过 JDK 自带的 Timer 实现
内存缓存使用 Google 的 guava 包实现

eureka 集群搭建
和 eureka 类似功能的有 zookeeper,etcd 等。spring boot 已经集成了 eureka,所以我们可以像 spring boot 那样搭建环境,部署运行。
我是在 window10 下使用 eclipse 学习的。
准备工作。
在修改 hosts 文件,在最后面加上(位置:C:WindowsSystem32driversetc)
127.0.0.1 01.eureka.server
127.0.0.1 02.eureka.server
127.0.0.1 03.eureka.server

eclipse 下创建个普通的 maven 项目 eureka-server。
pom.xml
<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>com.fei.springcloud</groupId>
<artifactId>springcloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>eureka 服务端 </description>
<!– 依赖仓库 设置从 aliyun 仓库下载 –>
<repositories>
<repository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<!– 插件依赖仓库 –>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<properties>
<!– 文件拷贝时的编码 –>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!– 编译时的编码 –>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</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>

启动类 Application.java
package com.fei.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

注意注解:@EnableEurekaServer,表明这是 server 服务
配置文件 application.properties
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka 的默认端口是 8761
server.port=8081
server.session-timeout=60
###########
spring.application.name=eureka-server-01
#### 下面 2 个一定要 false,因为这程序是要作为服务端

但是 jar 中存在 eureka-client.jar,所以要 false,否则启动会报错的
#是否注册到 eureka
eureka.client.register-with-eureka=false
#是否获取注册信息
eureka.client.fetch-registry=false

# 为了便于测试,取消 eureka 的保护模式,如果启动的话,

比如 api 提供者关闭了,但是 eureka 仍然保留信息
eureka.server.enable-self-preservation=false
#服务名称
eureka.instance.hostname=01.server.eureka
#eureka 的服务地址,/eureka 是固定的
eureka.client.serviceUrl.defaultZone=http://02.

server.eureka:8082/eureka/,http://03.server.eureka:8083/eureka/

注意:eureka.client.serviceUrl.defaultZone 的配置,如果是 01,则填写 02、03 的地址和端口;如果是 02,则填写 01、03 的地址和端口,也就是说让 01,02,03 这 3 个 eureka 服务能相互间同步数据,如果是 01->02->03->01, 则 api 提供者注册信息到 01 时,01 会同步数据到 02,但 02 不会同步到 03,01 也不会同步到 03,也就是说 03 缺数据了。看源码, 服务的注册信息不会被二次传播,看 PeerAwareInstanceRegistryImpl.java

启动 01 eureka, 然后修改 application.properties, 变为 02 eureka 的配置
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka 的默认端口是 8761
server.port=8082
server.session-timeout=60
###########
spring.application.name=eureka-server-02
#### 下面 2 个一定要 false,因为这程序是要作为服务端,

但是 jar 中存在 eureka-client.jar,所以要 false,否则启动会报错的
#是否注册到 eureka
eureka.client.register-with-eureka=false
#是否获取注册信息
eureka.client.fetch-registry=false
#为了便于测试,取消 eureka 的保护模式,如果启动的话,

比如 api 提供者关闭了,但是 eureka 仍然保留信息
eureka.server.enable-self-preservation=false
#服务名称
eureka.instance.hostname=02.server.eureka
#eureka 的服务地址,/eureka 是固定的
eureka.client.serviceUrl.defaultZone=http://01.server.

eureka:8081/eureka/,http://03.server.eureka:8083/eureka/

然后执行启动类,03 也是一样的操作。
浏览器访问 http://01.server.eureka:8081/(或者 http://02.server.eureka:8082/,或者 http://03.server.eureka:8083/)看到

api 提供者
创建个普通的 maven 项目 eureka-api, 该 api 是个用户服务提供者。采取 spring boot 开发模式

pom.xml
<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>com.fei.springcloud</groupId>
<artifactId>springcloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>eureka 服务端 </description>
<!– 依赖仓库 设置从 aliyun 仓库下载 –>
<repositories>
<repository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<!– 插件依赖仓库 –>
<pluginRepositories>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<properties>
<!– 文件拷贝时的编码 –>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!– 编译时的编码 –>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</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 服务端,有个依赖是不一样的。
application.properties
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka 的默认端口是 8761
server.port=9081
server.session-timeout=60
###########
spring.application.name=api-user-server
#像 eureka 服务注册信息时,使用 ip 地址,默认使用 hostname
eureka.instance.preferIpAddress=true
#服务的 instance-id 默认默认值是 ${spring.cloud.client.hostname

:${spring.aplication.name}

:${spring.application.instance_id:${server.port}},
#也就是机器主机名: 应用名称: 应用端口
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
#eureka 的服务地址
eureka.client.serviceUrl.defaultZone=http://01.server.eureka:8081/eureka/

Application.java
package com.fei.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

@EnableEurekaClient,不管是消费者还是提供者,对应 eureka server 来说都是客户端 client
写个普通的 controller,UserProvider.java. 提供个根据 id 获取用户信息的接口
package com.fei.springcloud.provider;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/user”)
public class UserProvider {
@GetMapping(value=”/find/{id}”)
public String find(@PathVariable(“id”) String id,HttpServletRequest request){
// 实际项目中,这里可以使用 JSONObject,返回 json 字符串
// 为了便于测试消费者 app 的负载均衡,返回服务端端口
String s = “ 张三 ”+” 服务端端口:”+request.getLocalPort();
return s;
}
}

执行 Application.java, 将 application.properties 的端口修改为 9082,再次执行
浏览器访问 http://01.server.eureka:8081/

Application 就是文件中定义的 spring.application.name=api-user-server,它会自动转为大写
如果想免费学习 Java 工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty 源码分析的朋友可以加我的 Java 进阶群:478030634,群里有阿里大牛直播讲解技术,以及 Java 大型互联网技术的视频免费分享给大家。
app 消费者
在 Spring Cloud Netflix 中,使用 Ribbon 实现客户端负载均衡,使用 Feign 实现声明式 HTTP 客户端调用——即写得像本地函数调用一样.

ribbo 负载均衡的 app 消费者
创建个普通的 maven 项目 eureka-app-ribbo.
pom.xml
<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>com.fei.springcloud</groupId>
<artifactId>springcloud-eureka-app-ribbo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>eureka 消费者 ribbo</description>
<!– 依赖仓库 设置从 aliyun 仓库下载 –>
<repositories>
<repository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<!– 插件依赖仓库 –>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<properties>
<!– 文件拷贝时的编码 –>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!– 编译时的编码 –>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<!– 客户端负载均衡 –>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<!– eureka 客户端 –>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!– spring boot 实现 Java Web 服务 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</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>

application.properties
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka 的默认端口是 8761
server.port=7081
server.session-timeout=60
###########
spring.application.name=app-user
#像 eureka 服务注册信息时,使用 ip 地址,默认使用 hostname
eureka.instance.preferIpAddress=true
#服务的 instance-id 默认默认值是 ${spring.cloud.client.hostname}${spring.application.name}

:${spring.application.instance_id:${server.port}},
#也就是机器主机名: 应用名称: 应用端口
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
#eureka 的服务地址
eureka.client.serviceUrl.defaultZone=http://01.server.eureka:

8081/eureka/,http://02.server.eureka:8082/eureka/,

http://03.server.eureka:8083/eureka/

UserController.java
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka 的默认端口是 8761
server.port=7081
server.session-timeout=60
###########
spring.application.name=app-user
#像 eureka 服务注册信息时,使用 ip 地址,默认使用 hostname
eureka.instance.preferIpAddress=true
#服务的 instance-id 默认默认值是 ${spring.cloud.client.hostname}

:${spring.application.name}:${spring.application.instance_id:${server.port}},
#也就是机器主机名: 应用名称: 应用端口
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
#eureka 的服务地址
eureka.client.serviceUrl.defaultZone=http://01.server.eureka:8081/eureka/

,http://02.server.eureka:8082/eureka/,http://03.server.eureka:8083/eureka/

使用 restTemplate 需要自己拼接 url
启动类 Application.java
package com.fei.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableEurekaClient
@SpringBootApplication
public class Application {
@Bean // 定义 REST 客户端,RestTemplate 实例
@LoadBalanced // 开启负债均衡的能力
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

eureka 服务

浏览器访问 http://127.0.0.1:7081/user/find
看到信息“张三 服务端端口:9081”,刷新浏览器看到“张三 服务端端口:9082”,说明的确是有负载均衡。
但是访问外网的时候,http://127.0.0.1:7081/user/test,也就是域名不在 eureka 注册过的,就不行了。

以后再研究下如何解决。
feign 的 app 消费者
feign 可以写个接口,加上相关的注解,调用的时候,会自动拼接 url,调用者就像调用本地接口一样的操作。
Feign 也用到 ribbon,当你使用 @ FeignClient,ribbon 自动被应用。
像 ribbo 创建个项目,或者直接在 ribbo 项目修改都 OK。
pom.xml 把 ribbo 的依赖修改为 feign
<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>com.fei.springcloud</groupId>
<artifactId>springcloud-eureka-app-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>eureka 消费者 feign</description>
<!– 依赖仓库 设置从 aliyun 仓库下载 –>
<repositories>
<repository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content

/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<!– 插件依赖仓库 –>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<properties>
<!– 文件拷贝时的编码 –>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!– 编译时的编码 –>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<!– Feign 实现声明式 HTTP 客户端 –>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!– eureka 客户端 –>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!– spring boot 实现 Java Web 服务 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</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>

application.properties 和上面一样
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka 的默认端口是 8761
server.port=7081
server.session-timeout=60
###########
spring.application.name=app-user
#像 eureka 服务注册信息时,使用 ip 地址,默认使用 hostname
eureka.instance.preferIpAddress=true
#服务的 instance-id 默认默认值是 ${spring.cloud.client.hostname}
:${spring.application.name}:${spring.application.instance_id:${server.port}},
#也就是机器主机名: 应用名称: 应用端口
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
#eureka 的服务地址
eureka.client.serviceUrl.defaultZone=http://01.server.eureka
8081/eureka/,http://02.server.eureka:8082/eureka/,
http://03.server.eureka:8083/eureka/

增加个 UserService.java 接口类
package com.fei.springcloud.service;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(“API-USER-SERVER”)
public interface UserService {
@GetMapping(value=”/user/find/{id}”)
String find(@PathVariable(“id”) String id);
}

UserController.java

package com.fei.springcloud.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fei.springcloud.service.UserService;
@Controller
@RequestMapping(“/user”)
public class UserController {
@Autowired
private UserService userService;
@GetMapping(value = “/find”)
@ResponseBody
public String find() {
//url 中对应 api 提供者的名称,全大写
String s = userService.find(“123”);
return s;
}
}

浏览器访问 http://127.0.0.1:7081/user/find,得到信息“张三 服务端端口:9081”,刷新,得到“张三 服务端端口:9082”,Feign 也用到 ribbon,当你使用 @ FeignClient,ribbon 自动被应用。所以也会负载均衡
ribbo 负载均衡策略选择

AvailabilityFilteringRule:过滤掉那些因为一直连接失败的被标记为 circuit tripped 的后端 server,并过滤掉那些高并发的的后端 server(active connections 超过配置的阈值)| 使用一个 AvailabilityPredicate 来包含过滤 server 的逻辑,其实就就是检查 status 里记录的各个 server 的运行状态
RandomRule:随机选择一个 server
BestAvailabl:选择一个最小的并发请求的 server,逐个考察 Server,如果 Server 被 tripped 了,则忽略
RoundRobinRule:roundRobin 方式轮询选择,轮询 index,选择 index 对应位置的 server
WeightedResponseTimeRule:根据响应时间分配一个 weight(权重),响应时间越长,weight 越小,被选中的可能性越低
RetryRule:对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择 server 不成功,则一直尝试使用 subRule 的方式选择一个可用的 server
ZoneAvoidanceRule:复合判断 server 所在区域的性能和 server 的可用性选择 server
ResponseTimeWeightedRule:作用同 WeightedResponseTimeRule,二者作用是一样的,ResponseTimeWeightedRule 后来改名为 WeightedResponseTimeRule
在 app 消费者的 application.properties 配置文件中加入
#ribbo 负载均衡策略配置,默认是依次轮询
API-USER-SERVER.ribbon.NFLoadBalancerRuleClassName=com.
netflix.loadbalancer.RandomRule

其中 API-USER-SERVER 是 api 服务提供者的服务名称,也就是说,可以给每个不同的 api 服务提供者配置不同的复制均衡策略,验证就不贴图了
负载均衡策略在消费端配置的缺点
在上面的例子中,ribbon 的负载均衡是在消费端完成的。流程是这样的:提供者服务 A 集群,启动 2 个进程 A1,A2,都注册到 eureka,app 消费端根据 api 服务者名称获取到 A1,A2 的具体连接地址,ribbon 就对 A1,A2 进行负载均衡。
缺点:
1)如果所有的 app 消费端的配置策略不好,导致绝大部分的请求都到 A1,那 A1 的压力就大了。也就是说负载策略不是有 api 提供者所控制的了(这里就不说 A1,A2 所在的服务器哪个性能更好了,因为如果 app/api 都是在 Docker 中运行,k8s 负责资源调配的话,可以认为每个服务的进程所在的 docker 配置是一样的,比如 A 服务对应的 A1,A2 系统环境是一致的,B 服务对应的 B1,B2,B3 系统环境是一致的,只是所对应的宿主机服务器估计不一样而已)。
2)如果 api 提供者需要开放给第三方公司的时候,总不能把 A1,A2 告诉第三方吧,说我们这 A 服务集群了,有 A1,A2, 你随意吧。
我们实际项目中的做法,都是每个提供者服务都有自己的 nginx 管理自己的集群,然后把 nginx 的域名提供给 app 消费者即可。之所以每个服务提供者都有自己的 nginx,是因为 docker 被 k8s 管控的时候,ip 都是变化的,需要更新到 nginx 中。如果 A,B 都共用一个 nginx,那 A 重构建部署的时候,A1,A2 的 ip 变化了,需要更新到 nginx 中去,那如果导致 nginx 出现问题了,岂不是影响到 B 的使用了,所以 A,B 都有自己的 nginx。那 spring cloud 没有解决方案了吗?有。spring cloud 集成了 zuul,zuul 服务网关,不但提供了和 nginx 一样的反向代理功能,还提供了负载均衡、监控、过滤等功能,在后面学习 zuul 的时候,我们再学习。
如果想免费学习 Java 工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty 源码分析的朋友可以加我的 Java 进阶群:478030634,群里有阿里大牛直播讲解技术,以及 Java 大型互联网技术的视频免费分享给大家。

docker+k8s 实现的 devOps 平台(paas 平台),构建好镜像后,部署的时候,k8s 负责调控资源,将 docker 分配到不同的节点服务器,同时将 docker 的 ip 相关信息更新到 nginx。这是自动化的,不需要开发人员手动操作 docker,nginx 配置。

退出移动版