本文介绍微服务的服务调用和负载均衡,使用spring cloud的loadbalancer及openfeign两种技术来实现。
本文的操作是在微服务的初步使用的基础上进行。
jdk1.8
maven3.6.3
mysql8
spring cloud2021.0.8
spring boot2.7.12
idea2022
为了方便测试,把高可用Eureka还原为单节点Eureka。
修改eureka_server的application.yml
spring:
application:
name: eureka-server
server:
port: 9000
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 4000
修改product-service服务和order-service服务的application.yml的Eureka配置如下
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/
spring-cloud-loadbalancer和ribbon类似,之前的spring cloud版本eureka内部继承了ribbon ,但spring cloud版本升级后,不再内置ribbon(可查看依赖关系),spring cloud提供了自己的负载均衡的实现,可查看 官方文档
Spring Cloud provides its own client-side load-balancer abstraction and implementation. For the load-balancing mechanism,
ReactiveLoadBalancer
interface has been added and a Round-Robin-based and Random implementations have been provided for it. In order to get instances to select from reactiveServiceInstanceListSupplier
is used. Currently we support a service-discovery-based implementation ofServiceInstanceListSupplier
that retrieves available instances from Service Discovery using a Discovery Client available in the classpath.
之前服务调用时,使用host:ip拼接成URL的方式进行调用,较为麻烦,使用@LoadBalanced注解后,可以使用微服务的服务名称来进行调用,更加方便。
在创建RestTemplate的时候,申明@LoadBalanced
使用restTemplate调用远程微服务:不需要拼接微服务的URL,用服务名称替换IP地址
修改OrderController.java
,使用服务名称代替host:port的形式,避免了之前的使用实例元数据进行拼接。
修改前
product = restTemplate.getForObject("http://"+host+":"+port+"/product/1", Product.class);
修改后
product = restTemplate.getForObject("http://service-product/product/1", Product.class);
启动测试
启动eureka、product、order服务
浏览器访问
能访问到数据,说明服务调用成功了。
使用@LoadBalanced注解后,除了方便使用服务名称进行调用之外,更重要的是也实现了服务调用的负载均衡功能。准备两个商品服务,端口号分别为9001、9011,两个商品服务的作用:1.增加系统吞吐率 2.商品服务高可用。
服务架构及流程如下图:
修改product-service的controller
@Value("${spring.cloud.client.ip-address}") //springcloud自动获取应用的ip地址
private String ip;
启动product服务(9001端口)
修改product服务的yml,修改服务端口为9011
复制运行配置得到另一个proudct服务
启动product2服务(9011端口)
查看Eureka Web页面
http://localhost:9000/
看到SERVICE-PRODUCT,有2个在线状态的实例,端口分别是:9001和9011
启动order服务
分别访问9001和9011
http://localhost:9001/product/1
http://localhost:9011/product/1
两个商品服务均正常
测试负载均衡
访问如下链接2次
http://localhost:9002/order/buy/1
发现实现 第一次访问9001商品服务,第二次访问了9011商品服务,说明实现了客户端的负载均衡
多次刷新访问http://localhost:9002/order/buy/1
,发现9001和9011是交替出现,说明默认使用的负载均衡是轮询策略。
此前的服务调用代码如下:
restTemplate.getForObject("http://service-product/product/1", Product.class);
如果请求参数过多是,拼接URL的方式显得麻烦,可以使用OpenFeign来解决。
OpenFeign 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件,它的出现就是为了替代进入停更维护状态的 Feign。
OpenFeign 是 Spring Cloud 对 Feign 的二次封装,它具有 Feign 的所有功能,并在 Feign 的基础上增加了对 Spring MVC 注解的支持。
在order-service
操作
1.导入依赖
org.springframework.cloud
spring-cloud-starter-openfeign
2.配置调用接口
业务需求:order服务调用product服务
@FeignClient(name = "service-product")
public interface ProductFeignClient {
/**
* 配置需要调用的微服务接口
* @return
*/
@RequestMapping(value = "/product/{id}", method = RequestMethod.GET)
Product findById(@PathVariable("id") Long id);
}
3.在启动类上激活feign
//激活feign
@EnableFeignClients
public class OrderApplication {
4.通过自动注入的接口调用远程微服务
修改之前的Controller代码
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping(value = "/buy/{id}", method = RequestMethod.GET)
public Product findById(@PathVariable Long id){
Product product = null;
product = restTemplate.getForObject("http://service-product/product/1", Product.class);
return product;
}
}
修改之后的代码
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private ProductFeignClient productFeignClient;
@RequestMapping(value = "/buy/{id}", method = RequestMethod.GET)
public Product findById(@PathVariable Long id){
Product product = null;
//调用微服务
product = productFeignClient.findById(id);
return product;
}
}
可以看出,OrderController通过注入ProductFeignClient接口实例,并调用接口方法实现了调用。 可以看出不需要自己构建http请求,就像是调用自身工程的方法调用。如果有多个参数可以传递对象参数,避免了拼接url的麻烦。
测试
启动服务
启动eureka、启动product(启动两个实例:9000和9011)、启动order
浏览器访问
分别访问,确保product服务能正常访问
http://localhost:9001/product/1
http://localhost:9011/product/1
访问order两次
http://localhost:9002/order/buy/1
多次访问,发现9001和9011交替出现,说明实现了负载均衡。
如果需要配置OpenFeign,在application.yml添加相关配置
配置案例如下:
feign:
client:
config:
feignName: #FeginClient的名称
connectTimeout: 5000 #建立链接的超时时长
readTimeout: 5000 #读取超时时长
loggerLevel: full #Fegin的日志级别
errorDecoder: com.example.SimpleErrorDecoder #Feign的错误解码器
retryer: com.example.SimpleRetryer #配置重试
requestInterceptors: #添加请求拦截器
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false #配置熔断不处理404异常
#日志配置
#NONE : 不输出日志(高)
#BASIC: 适用于生产环境追踪问题
#HEADERS : 在BASIC的基础上,记录请求和响应头信息
#FULL : 记录所有
service-product:
logger-level: FULL #配置商品服务日志
compression:
request:
enabled: true #开启请求压缩
mime-types: text/html,application/xml,application/json #设置压缩的数据类型
min-request-size: 2048 #设置触发压缩的大小下限
response:
enabled: true #开启相应压缩
logging:
level:
org.example.order.feign.ProductFeignClient: debug #配置具体接口的日志级别
两种服务调用及负载均衡技术:@LoadBalanced方式和OpenFeign的方式。
1.@LoadBalanced方式是借助于RestTemplate方式进行,可以直接使用服务名称来调用,但需要拼接URL。
2.OpenFeign的方式是通过声明接口并注入接口进行调用,避免了拼接URL的麻烦。
完成!enjoy it!