搭建环境:Jdk1.8 、Mysql 、Windows/Linux CentOs 、Idea
xml version="1.0" encoding="UTF-8"?>
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">
4.0.0
com.github
lamzier
1.0-SNAPSHOT
SpringCloud-Api
SpringCloud-consumer-dept-80
SpringCloud-eureka-7001
SpringCloud-eureka-7002
SpringCloud-eureka-7003
SpringCloud-provider-dept-8001
SpringCloud-provider-dept-8002
SpringCloud-provider-dept-8003
UTF-8
1.8
1.8
4.13.2
1.18.24
1.2.17
pom
org.springframework.cloud
spring-cloud-dependencies
2021.0.3
pom
import
org.springframework.boot
spring-boot-dependencies
2.7.2
pom
import
mysql
mysql-connector-java
8.0.30
com.alibaba
druid
1.2.11
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.2
junit
junit
${junit.version}
org.projectlombok
lombok
${lombok.version}
log4j
log4j
${log4j.version}
ch.qos.logback
logback-core
1.2.11
包结构图
package com.github.lamzier.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) //开启 链式写法
public class Dept implements Serializable { //实体类 实现序列化 , 类表关系映射
private Long deptno;//主键
private String dname;
//这个数据是存在哪个数据库的字段~ 微服务:一个服务对应一个数据库,同一个信息可能存在不同的数据库
private String DbSource;
public Dept(String dname){
this.dname = dname;
}
/*
链式写法:
Db01 db01 = new Db01();
db01.setDeptno(11).setDname('阿西吧').setDb_source('001');
*/
}
Dept.java
依赖:Lombok (省略了)
文件结构图
package com.github.lamzier.controller;
import com.github.lamzier.pojo.Dept;
import com.github.lamzier.service.DeptService;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
//提供Restful 服务
@RestController
public class DdptController {
@Resource
private DeptService deptService;
//获取一些配置信息,得到具体的微服务
@Resource
private DiscoveryClient discoveryClient;
@PostMapping("/dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable long id){
return deptService.queryDeptById(id);
}
@GetMapping("/dept/list")
public List list(){
return deptService.queryAllDept();
}
//注册进来的微服务,获取一些消息
@GetMapping("/dept/discovery")
public Object discovery(){
//获取服务列表的清单
List services = discoveryClient.getServices();
System.err.println("discovery=>services:" + services);
//得到一个具体的微服务信息 , 通过具体的微服务id , applicationName
List instances = discoveryClient.getInstances("SPRINGCLOUD-PROVIDER-DEPT-8001");
for (ServiceInstance instance : instances) {
System.err.println(
instance.getHost() + "\t" +
instance.getPort() + "\t" +
instance.getUri() + "\t" +
instance.getServiceId()
);
}
return this.discoveryClient;
}
}
控制层:DdptController.java
数据层:省略
服务层:省略
package com.github.lamzier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//启动类
@SpringBootApplication
//@MapperScan("com.github.lamzier.dao")
@EnableEurekaClient // 开启EurekaClient 在服务启动后自动注册到Eureka中
@EnableDiscoveryClient //开启服务发现
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
启动类 DeptProvider_8001
Mapper配置:省略
server:
port: 8001
#mybatis 配置
mybatis:
type-aliases-package: com.github.lamzier.pojo
# config-location: classpath:mybatis/mybatis-config.xml #不能喝configuration同时使用
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true #驼峰转换
cache-enabled: true #开启二级缓存
#Spring 的配置
spring:
application:
name: springcloud-provider-dept # 3个服务名称一一致
datasource: #注意!这里每个服务配置一个自己的数据库
type: com.alibaba.druid.pool.DruidDataSource #数据源
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test1?userUnicode=true&characterEncoding=utf-8
username: test1
password: test1
#Eureka 的配置
eureka:
client:
service-url:
#注册中心地址
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springCloud-provider-dept8001 #修改eureka上的默认描述信息
#info 配置
info:
app:
name: lamzy-springcloud
company:
name: gdei.lamzy.top
#管理开启
management:
info:
env:
enabled: true
endpoints:
web:
exposure:
include: "*"
配置:application.yml
xml version="1.0" encoding="UTF-8"?>
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">
lamzier
com.github
1.0-SNAPSHOT
4.0.0
SpringCloud-provider-dept-8001
1.8
1.8
com.github
SpringCloud-Api
1.0-SNAPSHOT
junit
junit
mysql
mysql-connector-java
com.alibaba
druid
ch.qos.logback
logback-core
org.mybatis.spring.boot
mybatis-spring-boot-starter
org.springframework.boot
spring-boot-test
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-jetty
org.springframework.boot
spring-boot-devtools
org.springframework.cloud
spring-cloud-starter-eureka
1.4.7.RELEASE
org.springframework.boot
spring-boot-starter-actuator
pom依赖
文件结构图
package com.github.lamzier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
//启动后,访问http://localhost:7001/
@SpringBootApplication
@EnableEurekaServer // 开启EurekaServer 服务端的启动类,可以接收别人注册进来
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class , args);
}
}
启动类:EurekaServer_7001.java
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: eureka7001.com # Eureka 服务端名字
client:
register-with-eureka: false # 表示是否向Eureka注册中心注册自己
fetch-registry: false # 如果为 false , 则表示自己为注册中心
# 监控页面
service-url:
#单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联):
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
配置:application.yml
xml version="1.0" encoding="UTF-8"?>
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">
lamzier
com.github
1.0-SNAPSHOT
4.0.0
SpringCloud-eirela-7001
1.8
1.8
org.springframework.cloud
spring-cloud-starter-eureka-server
1.4.7.RELEASE
org.springframework.boot
spring-boot-devtools
pom依赖
文件结构图
package com.github.lamzier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//import org.springframework.cloud.netflix.ribbon.RibbonClient;
//Ribbon 和 Eureka 整合以后, 客户端可以直接调用,不用关心Ip地址和端口号
@SpringBootApplication
@EnableEurekaClient //启动Eureka客户端
//@RibbonClient(name = "springcloud-provider-dept")
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class , args);
}
}
启动类:DeptConsumer_80.java
package com.github.lamzier.controller;
import com.github.lamzier.pojo.Dept;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
public class DeptConsumerController {
// 理解:消费者,不应该有service层
// RestTemplate ...... 供我们直接调用就可以了!,注册到Spring中
// (url , 实体:Map , Class responseType)
@Resource
private RestTemplate restTemplate; // 提供多种边界访问远程http服务的方法 , 简单的restful服务模板~
//Ribbon。我们这里的地址,应该是一个变量,通过服务名来访问
// private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") long id){
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}
@RequestMapping("/consumer/dept/add")
public Boolean add(Dept db01){
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add" , db01 , Boolean.class);
}
@RequestMapping("/consumer/dept/list")
public List list(){
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list" , List.class);
}
}
控制层:DeptConsumerController.java
package com.github.lamzier.config;
import com.github.lamzier.myrule.LamzyRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
//加载自动加载的Ribbon类
@LoadBalancerClient(name = "springcloud-provider-dept",configuration = LamzyRule.class)
public class ConfigBean { // @Configuration --- spring applicationContext.xml
//配置负载均衡,实现RestTemplate
// IRULE
// RoundRobinRule : 轮询 默认
// RandomRule : 随机
// AvailabilityFilteringRule : 会先过滤掉跳闸的服务,对剩下的进行轮询
// RetryRule : 会先按照轮询获取服务,如果服务获取失败,则会在指定的时间内进行重试
@Bean
@LoadBalanced //Ribbon
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
Ribbon配置类:ConfigBean.java
注意:在新版本中负载均衡的算法或者使用自定义算法不在这里配置,具体配置方法后面会讲。
package com.github.lamzier.myrule;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
//这里 不需要 @configuration注解 不需要 不需要
public class LamzyRule {
@Bean
ReactorLoadBalancer randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
//随机算法
// return new RandomLoadBalancer(loadBalancerClientFactory
// .getLazyProvider(name, ServiceInstanceListSupplier.class),
// name);
//自定义算法
return new PeachLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
负载均衡算法配置:LamzyRule.java
package com.github.lamzier.myrule;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.*;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
//这个方法基本是copy的RoundRobinLoadBalancer自己改一改出来的
public class PeachLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);
final AtomicInteger position;//请求的次数
final String serviceId; //服务名称 用于提示报错信息的
private int flag = 0; //自己定义的计数器
//两个参数的构造方法 需要服务名称和实例提供者 这个在方法中传递进来
public PeachLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider,
String serviceId) {
//如果不传人请求次数就自己初始化 反正每次都+1
this(new Random().nextInt(1000), serviceId,serviceInstanceListSupplierProvider);
}
public PeachLoadBalancer(int seedPosition, String serviceId, ObjectProvider serviceInstanceListSupplierProvider) {
this.position = new AtomicInteger(seedPosition);
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
ObjectProvider serviceInstanceListSupplierProvider;
@Override
public Mono> choose(Request request) {
//从服务提供者中获取到当前request请求中的serviceInstances并且遍历
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
private Response processInstanceResponse(ServiceInstanceListSupplier supplier,
List serviceInstances) {
Response serviceInstanceResponse = getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
private Response getInstanceResponse(List instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}
//pos是当前请求的次数 这样可以自定义负载均衡的切换 这个每次+1的操作是复制的 最好是不删
int pos = Math.abs(this.position.incrementAndGet());
if (pos%4==0){
//是4的倍数就切换
flag += 1;
}
if (flag >= instances.size()){
flag = 0;
}
//主要的就是这句代码设置负载均衡切换
ServiceInstance instance = instances.get(flag);
return new DefaultResponse(instance);
}
}
负载均衡自定义算法:PeachLoadBalancer.java
server:
port: 80
# Eureka配置
eureka:
client:
register-with-eureka: false #不向Eureka注册自己
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
配置:application.yml
xml version="1.0" encoding="UTF-8"?>
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">
lamzier
com.github
1.0-SNAPSHOT
4.0.0
SpringCloud-consumer-
1.8
1.8
com.github
SpringCloud-Api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
org.springframework.boot
spring-boot-starter-jetty
org.springframework.cloud
spring-cloud-starter-eureka
1.4.7.RELEASE
pom依赖
注意:在新版本中spring-cloud-starter-eureka-client依赖默认包含ribbon,因此只需要导入spring-cloud-starter-eureka或者spring-cloud-starter-eureka-client即可!不需要再导入spring-cloud-starter-netfix-ribbon!否则会报500异常,找不到服务。
在新版本中的负载均衡变化较大,而且导包也不同
最后博客更新时间:2022年8月10日 1:42