SpringCloud-Ribbon--负载均衡--搭建(5)

x33g5p2x  于2021-12-18 转载在 其他  
字(12.0k)|赞(0)|评价(0)|浏览(271)

负载均衡

spring cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端 负载均衡的工具
简单的说 Ribbon 是Netflix 发布的开源的项目 主要功能就是提供客户端的软件负载均衡算法 将Netflix的中间层服务连接在一起 Ribbon 客户组件提供一系列完善的配置项如 连接超时 重试等 简单的说就是在配置 文件中列出 Load Balancer (简称 LB) 后面所有的机器 Ribbon 会自动帮助你基于某种规则( 如 简单轮询,随机连接)去连接这些机器。我们也很容易使用Ribbon 实现自定义负载均衡算法

作用

LB即负载均衡(Load Balance)在未付或分布式集群中经常用一种应用、负载均衡简单的说就是将 用户的请求平摊的分配到多个服务上 从而达到系统的HA(高可用)、常见的有 Nginx Lvs 硬件F5等 相应的中间件 比如 dubbo和springcloud中均 提供了负载均衡,springcloud 的负载均衡算法可以自定义

集中式 LB
即在服务的消费方和提供方之间 使用 独立的LB设施(可以是硬件 例如F5 也可以是软件 如nginx)由该设施负责访问请求通过某种策略转发至服务的提供方

进程内LB
将LB 逻辑集成到 消费方 消费方从服务注册中心 获知有哪些地址可用,然后自己再从这些地址选出一个合适的服务器 Ribbon就是属于进程内LB 它只是一个类库, 集成与消费方的进程 消费方通过他来获取到服务提供方的地址

搭建(消费者8084)
pom文件
依赖

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-ribbon</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>

添加yml文件

server:
  port: 8084
  
  
eureka: 
  client: #客户端注册进eureka服务列表内
    register-with-eureka: false  # 自己不能注册
    service-url: 
       defaultZone: http://euerka7001.com:7001/eureka/,http://euerka7002.com:7002/eureka/,http://euerka7003.com:7003/eureka/     
      # 现在是集群

现在的消费端
消费端的RestTemplate上添加注解
@LoadBalanced//开启负载均衡 Ribbon实现了一套客户端 负载均衡的工具

ConfigBean

package com.xxx.cfgbeans;

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 //相当与配置 文件application.xml
public class ConfigBean {
	
	
	@Bean
	@LoadBalanced//开启负载均衡 Ribbon实现了一套客户端  负载均衡的工具
	public RestTemplate geRestTemplate() {
		
		return  new RestTemplate();
	}
	
	

}

controller

private static final String REST_URL_PREFIX="http://MICROSERVICECLOUD-DEPT";

MICROSERVICECLOUD-DEPT 是你注册到 Eureka的服务名

package com.xxx.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.xxx.user.User;

@Controller
public class UserControllerConsumer {
    //定义提供者的http://.........
	private static final String REST_URL_PREFIX="http://MICROSERVICECLOUD-DEPT";
	
	
	@Autowired
	private RestTemplate restTemplate; //RestTemplate提供了很多的便捷访问远程Http服务的方法	是一种简洁的访问restfuiA模板类。
	//是Spring提供的用于 访问 REst服务的 客户端模板工具类
	 
	/**
	 * 消费者 查询
	 * @return
	 */
     /*使用 使用restTemplate访问restful接口非常的简单 (url, requestMap,
	 ResponseBean.class)这三个参数分别代表 REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。*/
	@RequestMapping(value="/tt/user/list",  method = RequestMethod.GET)
    @ResponseBody
	public String   list(ModelMap map) {
		                                  //定义的http+"提供者的方法",类型.class 应为我的提供者方法是 String 所以这里也是String         
		  return restTemplate.getForObject(REST_URL_PREFIX+"/list/user",String.class);
	}
	
	
	// 测试@EnableDiscoveryClient,消费端可以调用服务发现
		@RequestMapping(value = "/tt/dept/discovery")
		@ResponseBody
		public Object discovery()
		{
			return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class);
		}

}

在启动类上添加@EnableEurekaClient

package com.xxx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;

@SpringBootApplication
@EnableEurekaClient
public class UserConsumerApp {

  public static void main(String[] args) {

	  SpringApplication.run(UserConsumerApp.class, args);
	  
}
	
}

现在 就时候测试 一下 访问 微服务 的真实名字 是否可以
因为我 是Eureka集群所以 启动3个 集群的项目*(7001,7002,7003) 在启动我的 提供者(8083) 在启动我的消费者(8084) 所以要启动5个 服务

打开 页面 访问
见下图 项目是完全可以访问的

接下了 为体现负载均衡 所以我是用 多提供者和多Eureka集群

首先建立多个提供者

1)8083是我已经创建好的

2)创建啊8082
还是在父级上右击创建maven Module

和8082同样的方法创建8001项目

Pom文件

3个pom 文件都是一样的 就是体现 有多个 提供者 更好的理解负载均衡

<dependencies>
		<!-- 引入自己定义的api通用包,可以使用User用户user -->
		<dependency>
			<groupId>com.xxx</groupId>
			<artifactId>microservicecloud-api</artifactId>
			<version>${project.version}</version>
		</dependency>
		<!-- actuator监控信息完善 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!-- 将微服务provider侧注册进eureka -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>   
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jetty</artifactId>
		</dependency>
		<dependency> 
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-freemarker</artifactId>
		</dependency>
	</dependencies>

下面就是吧 以前 创建好的 代码 分别拷贝 到其他两个项目中

com.xxx.controller

package com.xxx.controller;

import java.util.List;

import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner.detDSA;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.xxx.service.UserService;
import com.xxx.user.User;

@Controller
public class UserController {

	@Autowired
	private UserService  userService;
	
	@Autowired
	private DiscoveryClient client;     
	
	@RequestMapping(value = "/list/user", method = RequestMethod.GET)
	public String list(ModelMap map){
		List<User> list=userService.list();
		map.put("list", list);
		return "user";
	}
	
	
	@RequestMapping(value = "/dept/discovery", method = RequestMethod.GET)
	@ResponseBody
	
	public Object discovery()
	{
		List<String> list = client.getServices();
		System.out.println("**********" + list);

		List<ServiceInstance> srvList = client.getInstances("MICROSERVICECLOUD-DEPT");
		for (ServiceInstance element : srvList) {
			System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
					+ element.getUri());
		}
		return this.client;
	}
}

com.xxx.mapper

package com.xxx.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.xxx.user.User;

@Mapper
public interface UserMapper {

	
	

	public List<User> findAll();
}

com.xxx.service

package com.xxx.service;

import java.util.List;

import com.xxx.user.User;

public interface UserService {

	
	

	public List<User> list();
}

com.xxx.service.impl

package com.xxx.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xxx.mapper.UserMapper;
import com.xxx.service.UserService;
import com.xxx.user.User;
@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private UserMapper userMapper;

	@Override
	public List<User> list() {
		// TODO Auto-generated method stub
		return userMapper.findAll();
	}
	
	
}

yml 把3个提供者分别改一下端口就可以和数据库名改一下 别的不用改

同时我还让这3个消费者 连接 不同 的数据库 这三个表分别 对照 8081,8082,8083,

server:
  port: 8083
  
mybatis:
                                          
  type-aliases-package: com.xxx.user                  # 所有user别名类所在包
  mapper-locations:
  - classpath:mybatis/mapper/**/*.xml                       # mapper映射文件
    
spring:
   application:
    name: microservicecloud-dept 
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: com.mysql.jdbc.Driver               # mysql驱动包
    url: jdbc:mysql://localhost:3306/1807              # 数据库名称
    username: root
    password: root
    dbcp2:
      min-idle: 5                                           # 数据库连接池的最小维持连接数
      initial-size: 5                                       # 初始化连接数
      max-total: 5                                          # 最大连接数
      max-wait-millis: 200  
eureka: 
  client: #客户端注册进eureka服务列表内
    service-url: 
      # defaultZone: http://localhost:7001/eureka
    
       defaultZone: http://euerka7001.com:7001/eureka/,http://euerka7002.com:7002/eureka/,http://euerka7003.com:7003/eureka/     
    instance: 
       instance-id: microservicecloud-dept8083
       prefer-ip-address: true  # 显示ip地址      
       
       
info: 
  app.name: atguigu-microservicecloud
  company.name: www.baidu.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$
       
      
freemarker: 
    template-loader-path: classpath:/templates/
    cache: false
    charset: UTF-8
    check-template-location: true
    content-type: text/html; charset=utf-8
    expose-request-attributes: true
    expose-session-attributes: true
    request-context-attribute: request                                                          # 等待连接获取的最大超时时间

       
logging: 
   level:   
     com.xxx.mapper: debug

mapper.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.xxx.mapper.UserMapper">

	
	<select id="findAll" resultType="user">
		select  user_id as userId,user_name as userName,user_sex as userSex,user_age as userAge from  user;
	</select>
	

</mapper>

.ftl

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
  <center>
    <table border="1">
        <tr>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>修改</th>
            <th>删除</th>
        </tr>
    <#list list as user>
        <tr>
            <td>${user.userId}</td>
            <td>${user.userName}</td>
             <td>${user.userSex}</td>
            <td>${user.userAge}</td>
            <td><a >修改</a></td>
            <td><a >删除</a></td>

        </tr>
    </#list>
        <tr>
            <td><a >新增</a></td>
        </tr>
    </table>

</center>
</body>
</html>

现在 整个项目的结构

搭建好 就启动 项目 启动顺序–>Eureka(7001,7002,7003)—> 提供者(8081,8082,8083) 消费者–>(8084)一共7个项目

自己访问自己本身的项目 自测一下
首先8081(提供者) 对应的是1807库

8082 (提供者)对应的是1808库

8083提供者)对应的是1809库

现在打开Eureka 就可以看见这三个提供者的服务了已经注册到Eureka了

现在 在去 用我的 消费者(8084) 去访问

用消费者去访问发现 每次访问的都不是同一个库里的数据 这就说明了 Ribbon 默认的状况是轮询

Ribbion 核心组件

RoundRobinRule轮询
RandomRule随机
AvailabilityFilteringRule会先过滤掉由于多次访问故障而处于熔断器跳闸状态的服务,还有并发的连接数量超过閥值的服务,然后对剩下的服务列表按照轮询的策略访问
WeightedResponseTimeRule根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率高。刚启动时如果统计信息不足,则使用 RoundRobinRule策略,等统计信息足够,会切回到WeightedResponseTimeRule
RetryRule先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
BestAvailableRule会先过滤由于多次访问故障而处于熔断器跳闸状态的服务,然后选择一个并发量最小的服务
ZoneAvoidanceRule默认规则 复合判断server 所在区域性能和 server的可用选择性服务器

|

相关文章