一、什么是负载均衡、为什么需要负载均衡?
- LB,即负载均衡(Load Balance),负载均衡是微服务架构中经常使用的一种技术。
- 负载均衡是我们处理高并发、缓解网络压力和进行服务端扩容的重要手段之一。
a:简单的说就是将用户的请求平摊的分配到多个服务上,从而实现系统的高可用性集群。
b:负载均衡可以为微服务集群分担请求,降低系统的压力。
- 负载均衡可通过 硬件设备 及 软件 进行实现,软件比如:Nginx等,硬件比如:F5等
- 负载均衡相应的在中间件,例如:Dubbo 和 SpringCloud 中均给我们提供了负载均衡组件。
- 用户请求先到达负载均衡器(也相当于一个服务),负载均衡器根据负载均衡算法将请求转发到微服务。负载均衡器维护一份服务端列表,根据负载均衡算法 将请求转发到相应的微服务上。
- 负载均衡 算法有:轮训、随机、加权轮训、加权随机、地址哈希等方法。
request
agent
agent
agent
agent
agent
agent
Client
Nginx
Eureka
Eureka
Ribbin
Server 1
Server 2
Server 3
二、客户端负载均衡
1、客户端负载均衡 与 服务端负载均衡的区别在于:客户端负载均衡要维护一份服务列表。
2、客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置。在客户端负载均衡中,每个客户端服务都有一份自己要访问的服务端清单,这些清单统统都是从Eureka服务注册中心获取的。
3、在服务端负载均衡中,只要负载均衡器维护一份服务端列表 。
4、Ribbon 从 Eureka Server获取服务列表,Ribbon根据负载均衡算法直接请求到具体的微服务,中间省去了负载均衡服务。
a:在消费者微服务中使用 Ribbon 实现负载均衡,Ribbon 先从EurekaServer中获取服务列表。
b:Ribbon 根据负载均衡的算法(默认轮训算法)去调用微服务。
三、 主要配置
1、java配置类
作用是:@LoadBalanced表示这个RestTemplate开启负载均衡,在调用服务提供者的接口时,可使用 服务名称
替代真实IP地址。服务名称 就是服务提供者在application.yml中配置的spring.application.name属性的值 。
package com.haoxiansheng.springcloud.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 ConfigBean {
/**
*@LoadBalanced表示这个RestTemplate开启负载均衡,在调用服务提供者的接口时,
*可使用 服务名称 替代真实IP地址。服务名称 就是服务提供者在application.yml中
* 配置的spring.application.name属性的值。
* @return
*/
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
2、依赖
<!-- Ribbon 相关依赖,eureka会自动引入Ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3、具体实现参照 服务调用者
四、工作步骤
1、选择 Eureka Server ,它优先选择在同一个区域内负载较少的server。
2、根据用户指定的策略,在从 Eureka Server 获取的服务注册列表中选择一个地址。
Ribbon提供多种策略:比如轮询、随机和根据响应时间加权等。
五、再增加一个和前面一样的服务提供者
1、配置文件application.yml
server:
port: 8002
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.haoxiansheng.springcloud.entities # 所有Entity别名类所在包
mapper-locations: classpath:mybatis/mapper/*.xml #mybatis/mapper/**/*.xml
spring:
application:
name: microservice-product #这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个 name
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/bookshop?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: root
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 150 # 等待连接获取的最大超时时间
eureka:
client:
registerWithEureka: true #服务组册开关
fetchRegistry: true # 服务发现开关
serviceUrl:
defaultZone: http://127.0.0.1:6002/eureka/, http://127.0.0.1:6001/eureka/
# 客户端(服务提供者)注册到哪一个Eureka Server服务注册中心,多个用逗号分隔
instance:
instanceId: ${spring.application.name}:${server.port} # 指定实例ID,就不会显示主机名了
preferIpAddress: true #访问路径可以显示IP地址
2、 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">
<parent>
<artifactId>microservice-cloud-01</artifactId>
<groupId>com.haoxiansheng.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>microservice-cloud-06-provider-product-8002</artifactId>
<dependencies>
<!-- 导入Eureka客户端的依赖,将 微服务提供者 注册进 Eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.haoxiansheng.springcloud</groupId>
<artifactId>microservice-cloud-02-api</artifactId>
<version>${project.version}</version>
</dependency>
<!--springboot web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis 启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
</dependencies>
</project>
3、Controller
package com.haoxiansheng.springcloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient //本服务启动后会自动注册进Eureka服务器中
@MapperScan("com.zenlayer.springcloud.mapper") //扫描包下面所有Mapper接口
@SpringBootApplication
public class ProductProvider_8002 {
public static void main(String[] args) {
SpringApplication.run(ProductProvider_8002.class, args);
}
}
package com.haoxiansheng.springcloud.controller;
import com.haoxiansheng.springcloud.service.ProductService;
import com.zenlayer.springcloud.entities.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@PostMapping(value = "/add", produces = "application/json;charset=UTF-8")
public boolean add(@RequestBody Product product) {
return productService.add(product);
}
@GetMapping(value = "/get/{id}", produces = "application/json;charset=UTF-8")
public Product get(@PathVariable("id") Integer id) {
return productService.get(id);
}
@GetMapping(value = "/list", produces = "application/json;charset=UTF-8")
public List<Product> list() {
return productService.list();
}
}
4、Service
package com.haoxiansheng.springcloud.service;
import com.zenlayer.springcloud.entities.Product;
import java.util.List;
public interface ProductService {
boolean add(Product product);
Product get(Integer id);
List<Product> list();
}
package com.haoxiansheng.springcloud.service.impl;
import com.haoxiansheng.springcloud.mapper.ProductMapper;
import com.haoxiansheng.springcloud.service.ProductService;
import com.zenlayer.springcloud.entities.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper ;
@Override
public boolean add(Product product) {
return productMapper.addProduct(product);
}
@Override
public Product get(Integer id) {
return productMapper.findById(id);
}
@Override
public List<Product> list() {
return productMapper.findAll();
}
}
5、DAO
package com.haoxiansheng.springcloud.mapper;
import com.zenlayer.springcloud.entities.Product;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ProductMapper {
Product findById(Integer id);
List<Product> findAll();
boolean addProduct(Product product);
}
6、ProductMapper.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="com.haoxiansheng.springcloud.mapper.ProductMapper">
<select id="findById" resultType="Product" parameterType="Integer">
select id, product_name, db_source from product where id=#{id};
</select>
<select id="findAll" resultType="Product">
select id, product_name, db_source from product;
</select>
<insert id="addProduct" parameterType="Product">
INSERT INTO product(product_name, db_source) VALUES(#{productName}, DATABASE());
</insert>
</mapper>
7、mybatis.cfg.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--开启驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
六、详细步骤
启动Eureka
启动调用服务者
请求