【微服务系列】eruka结合Ribbon进行负载均衡
一、Eureka
- Eureka 是 Netflix 出品的用于实现服务注册和发现的工具。
- Eureka是一个基于REST(Representational State Transfer)的服务,主要用于AWS cloud, 提供服务定位(locating services)、负载均衡(load balancing)、故障转移(failover of middle-tier servers)。我们把它叫做Eureka Server.
- Eureka也提供了基于Java的客户端组件,Eureka Client,内置的负载均衡器可以实现基本的round-robin负载均衡能力。
二、Ribbon
- Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP的客户端的行为。
- 在给Ribbon配置服务提供地址后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求
三、SpringBoot集成eureka与Ribbon示例代码:
1、创建eureka注册中心:
配置application.yml
server:
port: 3333
eureka:
instance:
hostname: localhost
server:
enable-self-preservation: false #关闭自我保护
#eviction-interval-timer-in-ms: 8000 #清理间隔(单位毫秒,默认是60*1000)
# 此处不开启缓存
use-read-only-response-cache: false
#不要向注册中心注册自己
client:
#设置是否向注册中心注册,默认是true
register-with-eureka: false
#禁止检索服务
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
对启动类添加**@EnableEurekaServer**注解
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
新建一个listener包并在其内创建EurekaStateListener类(监听服务注册与下线)
package com.example.demo.listener;
import com.netflix.appinfo.InstanceInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.eureka.server.event.*;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class EurekaStateListener {
private final static Logger logger = LoggerFactory.getLogger(EurekaStateListener.class);
@EventListener
public void listen(EurekaInstanceCanceledEvent event) {
logger.info("服务{}已下线", event.getAppName());
logger.info("server地址信息{}", event.getServerId());
}
@EventListener
public void listen(EurekaInstanceRegisteredEvent event) {
InstanceInfo instanceInfo = event.getInstanceInfo();
logger.info("服务{}进行注册", instanceInfo.getAppName()+ instanceInfo.getHostName() +" "+ instanceInfo.getIPAddr() +" "+ instanceInfo.getPort());
}
@EventListener
public void listen(EurekaInstanceRenewedEvent event) {
logger.info("服务{}进行续约", event.getServerId() +" "+ event.getAppName());
}
@EventListener
public void listen(EurekaRegistryAvailableEvent event) {
logger.info("注册中心启动,{}", System.currentTimeMillis());
}
@EventListener
public void listen(EurekaServerStartedEvent event) {
logger.info("注册中心服务端启动,{}", System.currentTimeMillis());
}
}
2、创建ribbon
application.yml配置信息
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:3333/eureka
healthcheck:
enabled: true
instance:
lease-expiration-duration-in-seconds: 90
lease-renewal-interval-in-seconds: 30
appname: ribbon-client
server:
port: 8092
spring:
application:
name: ribbon-client
#必须加下面,否则ribbon重试机制开启不了
cloud:
loadbalancer:
retry:
enabled: true
#重试机制
ribbon:
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: true
ConnectTimeout: 250
ReadTimeout: 1000
#熔断
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 4500
pom.xml里面加入重试机制依赖:feign、retry、hystrix并且加入监控依赖:actuator
依赖信息
<!-- 引入Actuator监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 重试机制 ,必须配,否则重试不生效 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
完整的配置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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RC2</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-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 引入Actuator监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 重试机制 ,必须配,否则重试不生效 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
创建config包,并且创建RibbonConfig类
package com.example.demo.config;
import com.netflix.loadbalancer.BestAvailableRule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import feign.Retryer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
@Primary
@Bean(name="lbcRestTemplate")
RestTemplate lbcRestTemplate() {
return new RestTemplate();
}
/**自定义配置ribbon负载均衡算法
* @return
*/
@Bean
public IRule myRule(){
return new RoundRobinRule();//轮询
//return new RetryRule();//重试
//return new RandomRule(); //随机负载
//return new BestAvailableRule();//选择一个最小的并发请求的server
}
}
创建controller包并创建HelloController类
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
@RestController
public class HelloController {
@Autowired
@Qualifier(value = "restTemplate")
private RestTemplate restTemplate;
@Autowired
@Qualifier(value = "lbcRestTemplate")
private RestTemplate lbcRestTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@CrossOrigin
@RequestMapping(value = "/hello" ,method = RequestMethod.GET)
public String hello(){
String url="http://hello/hello";
ServiceInstance instance = this.loadBalancerClient.choose("hello");
URI helloUri = URI.create(String.format("http://%s:%s/hello", instance.getHost(), instance.getPort()));
System.out.println("Target service uri = "+helloUri.toString());
return restTemplate.getForObject(url,String.class);
}
}
3、创建服务提供者Hello
在pom.xml添加actuator依赖
<!-- 引入Actuator监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
即完整的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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RC2</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-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入Actuator监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
创建controller包并创建HelloController类
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@ResponseBody
@RequestMapping(value = "hello")
public Object hello()
{
return "HelloWorld";
}
}
编写application.yml内容
spring:
application:
name: hello
server:
port: 8089
eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://127.0.0.1:3333/eureka
healthcheck:
enabled: true #开启健康检查(需要spring-boot-starter-actuator依赖)
instance:
lease-expiration-duration-in-seconds: 90 #服务过期时间配置,超过这个时间没有接收到心跳EurekaServer就会将这个实例剔除
lease-renewal-interval-in-seconds: 30 #服务刷新时间配置,每隔这个时间会主动心跳一次
appname: hello
management:
endpoints:
web:
exposure:
include: "*"
server:
port: 8089
servlet:
context-path: /
ssl:
enabled: false
endpoint:
health:
show-details: always
shutdown:
enabled: true
#禁用密码验证
sensitive: false
访问http://localhost:3333出现eureka的界面(hello应用中只有一个url有效,另外一个是我一开始没设置应用名后来添加,eureka还没进行刷新的原因)
访问http://localhost:8092/hello
附上三个demo的目录
完整项目下载地址:https://download.youkuaiyun.com/download/qq_36313726/10933212