文章目录

概念测试Ribbon负载均衡搭建Eureka Server搭建Eureka Client

Ribbon负载均衡算法及配置服务之间的远程调用RestTemplateRestTemplate和Ribbon实现Application Client调用Application Service集群

OpenFeignhello world

参数传递

概念

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。 是进程内负载均衡,即使服务内部的负载均衡

Eureka Server允许不同项目的应用名称,即spring.application.name相同。当应用名称相同时会认定这些项目一个集群。所以部署集群时都是设置相应的应用程序相同的应用名称.

注意: ​ Application Client会从Eureka Server中根据spring.application.name加载Application Service的列表。根据设定的负载均衡算法,从列表中取出一个合适的URL,到此Ribbon的事情结束了。(Ribbon的功能只是负责挑选众多Service中的一个url,挑完就没它事了)

主流的负载均衡解决方案有:集中式负载均衡和进程内负载均衡。 1.集中式负载均衡: 在客户端和服务端之间使用独立的负载均衡设施,也叫服务器端负载均衡. 可以是硬件,如F5, 也可以是软件,如nginx(注意这里的客户端指的是用户端,服务端是服务器) 2.进程内负载均衡: 将负载均衡逻辑集成到客户端组件中(注意不是用户端,是相对于Eureka Server中调用其他服务的服务型客户端) 客户端组件从服务注册中心获知有哪些地址可用,再从这些地址中选择出一个合适的服务端发起请求。 Ribbon就是一个进程内的负载均衡实现。也叫做:客户端负载均衡。 (注意: 这里的客户端指的是一个服务调用另一个服务(不涉及用户端,是内部服务间的调用),调用方是客户端,被调用方是服务端)

测试Ribbon负载均衡

搭建Eureka Server

pom.xml

spring-boot-starter-parent

org.springframework.boot

2.3.12.RELEASE

org.springframework.boot

spring-boot-starter-web

org.springframework.cloud

spring-cloud-starter-netflix-eureka-server

org.springframework.cloud

spring-cloud-dependencies

${spring-cloud.version}

pom

import

yml

server:

port: 8888

eureka:

instance:

prefer-ip-address: true

client:

# 因为当前项目为服务,不需要向服务注册自己,默认为true

register-with-eureka: false

# 因为当前为非集群版eureka,所以不需要同步其他节点数据

fetch-registry: false

# 当server.port配置不是8761时需要配置内容

service-url:

defaultZone: http://127.0.0.1:${server.port}/eureka/

搭建Eureka Client

1.搭建Application Server集群 pom.xml

spring-boot-starter-parent

org.springframework.boot

2.3.12.RELEASE

org.springframework.boot

spring-boot-starter-web

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

org.springframework.cloud

spring-cloud-dependencies

Hoxton.SR12

pom

import

集群配置: 创建两个application Server 实例,及两个yml文件: 注意: application Server集群的spring.application.name要一致!!! 1.yml

# 此处应该定义名称,否则注册到Server后的名字为UNKNOWN

server:

port: 8080

spring:

application:

name: application-server

# 注册中心地址,默认值是http://localhost:8761/eureka/,端口号不是8761,则要和Eureka Server一致

eureka:

client:

service-url:

defaultZone: http://127.0.0.1:8888/eureka/

application-server2.yml

server:

port: 8081

spring:

application:

name: application-server

eureka:

client:

service-url:

defaultZone: http://127.0.0.1:8888/eureka/

通过两个配置文件创建application-server实例,及搭建的集群.

2.创建application Client pom.xml 注意spring-cloud-starter-netflix-eureka-client里面就包含Ribbon,所以不需要额外导jar包

org.springframework.boot

spring-boot-starter-parent

2.3.12.RELEASE

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

org.springframework.boot

spring-boot-starter-web

org.springframework.cloud

spring-cloud-dependencies

Hoxton.SR12

pom

import

yml配置

server:

port: 9090

spring:

application:

name: application-client

# 和Eureka Server一致

eureka:

client:

service-url:

defaultZone: http://127.0.0.1:8888/eureka/

client创建controller

import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;

@RestController

public class ApplicationClient{

/**

* Ribbon提供的负载均衡器

*/

@Autowired

private LoadBalancerClient loadBalancerClient;

@RequestMapping("/fs")

public String client() {

ServiceInstance si = loadBalancerClient.choose("application-server");//这里是Application Server集群的统一名称

// 获取Application Service IP。

String port = String.valueOf(si.getPort());

return port;

}

}

可以看到浏览器轮询打印8080,8081

Ribbon负载均衡算法及配置

常用: 轮询策略(默认) 权重轮询策略(常用,中小型项目使用) :根据每个application service的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。

将轮询改为权重轮询策略

@Configuration

public class RibbonConfig {

@Bean

public WeightedResponseTimeRule getWeightedResponseTimeRule(){

return new WeightedResponseTimeRule();

}

}

服务之间的远程调用

两种方式: RestTemplate: spring web 提供: 和eureka无关,就是服务之间发送到对应控制单元类似ajax的请求. OpenFeign: spring cloud 提供 (常用) 发送的请求相当于ajax

RestTemplate

不需要额外导包,包含在spring-boot-starter-web中 和eureka无关,就是服务之间发送到对应控制单元类似ajax的请求.

org.springframework.boot

spring-boot-starter-web

1.一个服务向另一个服务发送get请求

不传参数

@Test

void contextLoads() {

RestTemplate restTemplate=new RestTemplate();

//发送请求 get post put delete ..请求方式都可发送

// xxForObject() 和xxxForEntity() 区别

//xxForObject() 返回的结果只有相应值

//xxxForEntity() 返回结果 中不仅包含了相应的值 还有响应头 和响应体等信息

String s = restTemplate.getForObject("http://192.168.163.1:8081/edit", String.class);

System.out.println(s);

ResponseEntity entity = restTemplate.getForEntity("http://192.168.163.1:8081/edit", String.class);

System.out.println(entity.getStatusCode());

System.out.println(entity.getBody());

System.out.println(entity.getHeaders());

}

传参数

@Test

void contextLoads2() {

RestTemplate restTemplate=new RestTemplate();

//====>请求方式のGET GET不可以发送请求体数据!!!!

//restTemplate.getForObject("http://192.168.163.1:8081/edit2?name=zs&age=19",String.class);

// 在后面书写参数 在 前面进行参数的赋值 {1}...

//restTemplate.getForObject("http://192.168.163.1:8081/edit2?name={1}&age={2}",String.class,"zs",19);

//发送REST风格数据

//restTemplate.getForObject("http://192.168.163.1:8081/edit3/{1}/{2}",String.class,"zs",19);

//restTemplate.getForObject("http://192.168.163.1:8081/edit4?name={1}&age={2}",String.class,"zs",19);

//发送数据 封装到map集合中 在取值的时候需要使用map的key 进行接收

Map map =new HashMap<>();

map.put("a","zs");

map.put("b",80);

restTemplate.getForObject("http://192.168.163.1:8081/edit4?name={a}&age={b}",String.class,map);

}

2.一个服务向另一个服务发送post请求

@Test

void contextLoads3() {

RestTemplate restTemplate=new RestTemplate();

//====>请求方式のPOST

//restTemplate.postForEntity("http://192.168.163.1:8081/edit2?name=zs&age=123",null,String.class);

//restTemplate.postForEntity("http://192.168.163.1:8081/edit2?name={1}&age={2}",null,String.class,"zs",123);

//restTemplate.postForEntity("http://192.168.163.1:8081/edit3/{1}/{2}",null,String.class,"zs",123);

//restTemplate.postForEntity("http://192.168.163.1:8081/edit4?name={1}&age={2}",null,String.class,"zs",123);

//POST请求可以传递请求体数据 例如一个对象 可以直接传递 一次请求中最多有一个请求体

//Student stu=new Student(111,"sxt");

//restTemplate.postForEntity("http://192.168.163.1:8081/edit5",stu,String.class);

//可以传递map集合

Map map =new HashMap<>();

map.put("a","zs");

map.put("b",80);

restTemplate.postForEntity("http://192.168.163.1:8081/edit3/{a}/{b}",null,String.class,map);

}

3.响应结果中有集合或泛型解决方法exchange

@Test

void contextLoads4() {

RestTemplate restTemplate=new RestTemplate();

//exchange 方法可以发送任何请求 并且如何响应结果中含有泛型 使用ParameterizedTypeReference 直接指定即可

ParameterizedTypeReference> pt=new ParameterizedTypeReference>() {};

ResponseEntity> responseEntity = restTemplate.exchange("http://192.168.163.1:8081/list", HttpMethod.GET, null, pt);

List list = responseEntity.getBody();

System.out.println(list);

System.out.println(list.get(0).getName());

}

发送数据设置请求头和响应体HttpEntity

//HttpEntity: 进行请求体数据封装 里面不仅包含了请求体数据 还包含了 请求头 都可以进行设置

//传递的请求体数据

Student stu=new Student(19,"zs");

//请求头

MultiValueMap map =new LinkedMultiValueMap<>();

map.add("abc","xxx");

HttpEntity httpEntity=new HttpEntity(stu,map);

restTemplate.exchange("http://192.168.163.1:8081/edit5",HttpMethod.POST,httpEntity,String.class);

RestTemplate和Ribbon实现Application Client调用Application Service集群

方法1:实际上就是利用Ribbon的负载均衡器,获取不同的 uri 进行拼接成url,然后用RestTemplate动态的发送请求

@Autowired

private LoadBalancerClient loadBalancerClient;

@RequestMapping("/fs")

public String show(){

ServiceInstance choose = loadBalancerClient.choose("e-server");

URI uri = choose.getUri();//http://localhost:8081 || http://localhost:8080

RestTemplate restTemplate = new RestTemplate();

String result = restTemplate.getForObject(uri + "/save", String.class);

return result;

}

方法2.利用Eureka 创建RestTemplate对象的方法,增加注解 LoadBalanced - 把Spring Cloud封装的LoadBalancerClient于RestTemplate整合。 让RestTemplate自带负载均衡能力。仅在当前的Ribbon环境中生效。

@Configuration

public class AppClientConfiguration {

@Bean

@LoadBalanced

public RestTemplate restTemplate(){

return new RestTemplate();

}

}

@RestController

public class DemoController {

@Autowired

private RestTemplate restTemplate;

@RequestMapping("/fs")

public String show(){

String result = restTemplate.getForObject("http://e-server(application server集群名称)/save", String.class(返回值类型));

return result;

}

}

OpenFeign

声明式调用是指,就像调用本地方法一样调用远程方法. 1.Application Service向Eureka Server 注册服务, Application Client从Eureka Server中发现服务。 2.在Application Client中调用OpenFeign接口中方法,Application Client中OpenFeign通过应用程序名访问Application Service。 3.OpenFeign访问远程服务时,基于Ribbon实现负载均衡。

hello world

一.创建Eureka Server,详细参考Eureka 二.创建Application Server,详细参考Eureka 三.创建Application Client 1…导入依赖

org.springframework.boot

spring-boot-starter-parent

2.3.12.RELEASE

org.springframework.cloud

spring-cloud-dependencies

Hoxton.SR12

pom

import

org.springframework.boot

spring-boot-starter-web

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

org.springframework.cloud

spring-cloud-starter-openfeign

2.yml配置文件

server:

port: 9999

spring:

application:

name: eureka-client-open-feign

假设远程调用的Application Server的spring.profiles.name为e-server 远程调用其的控制单元为:

@RequestMapping("/save")

public String show(){

return "hello world";

}

3.接下来在Application Client中的service包下创建AppClientOpenfeignClient接口

@FeignClient("e-server")//这里是Eureka Server中的application Server的spring.profiles.name名称

public interface AppClientOpenfeignClient{

@RequestMapping("/save")//方法名可以不一致外,其余要一样(直接把除方法体外的复制过来就行)

public String show();

}

4.直接注入后调用

@RestController

public class DemoController {

@Autowired

private AppClientOpenfeignClient appClientOpenfeignClient;

@RequestMapping("/fs")

public String show(){

String show = appClientOpenfeignClient.show();

System.out.println(show);

return show;

}

}

5.启动类上加@EnableFeignClients(basePackages = {“feign所在包”})

/**

* EnableFeignClients - 开启Openfeign技术。让spring cloud扫描Openfeign相关注解,

* 生成动态代理实现对象。

* 可选属性 basePackages = {"feign接口所在包1", "feign接口所在包2"}

* 默认扫描当前类型所在包,及所有子孙包。

*/

@SpringBootApplication

@EnableFeignClients(basePackages = {"com.feign"})

public class OpenFeignAppClientApp {

public static void main(String[] args) {

SpringApplication.run(OpenFeignAppClientApp.class, args);

}

}

5.直接访问http://localhost:9999/fs 浏览器输出hello world.

参数传递

调用方法类型,返回值,参数顺序及个数要一致

//定义注解 书写应用名称 底层采用就是动态代理

@FeignClient("APPSERVICE")

public interface StudentFeign {

//4、建议:本地feign中方法名称建议和远程中一致

// 建议:本地feign中方法形参名称建议和远程中方法形参保持一致

@RequestMapping("/edit")

public String edit();

//参数列表默认传递参数都是请求体数据 对每一个参数前加@RequestBody

//如果我们传递是普通表单数据 必须增加@RequestParam

@RequestMapping("/edit2")

String edit2(@RequestParam("name") String name,

@RequestParam("age") int age);

//REST风格参数 要求 参数列表必须增加@PathVariable 尽管 形参名称和路径中名称一致

//也建议增加name属性

@RequestMapping("/edit3/{name}/{age}")

String edit3(@PathVariable("name") String name,

@PathVariable("age") int age);

@RequestMapping("/edit4")

String edit4(@RequestParam("name") String name,

@RequestParam("age") int age);

//默认就是请求体数据 在远程端必须使用@RequestBoby

@RequestMapping("/edit5")

String edit5(@RequestBody Student student);

@RequestMapping("/one")

Student findOne();

@RequestMapping("/list")

List findAll();

}

gzip…

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: