1.Feign远程调⽤
先来看我们以前利⽤RestTemplate发起远程调⽤的代码:
//通过restTemplate调用商品微服务
String url = "service-product";
Product product = restTemplate.getForObject( "http://"+url+"/product/" + pid, Product.class);
存在下⾯的问题:
- 代码可读性差,编程体验不统⼀
- 参数复杂URL难以维护
1.1 Feign简介
Feign是Spring Cloud提供的⼀个声明式的伪Http客户端, 它使得调⽤远程服务就像调⽤本 地服务⼀样简单, 只需要创建⼀个接⼝并添加⼀个注解即可。 Nacos很好的兼容了Feign, Feign 默认集了Ribbon, 所以在Nacos下使⽤Fegin默认就实现了负载均衡的效果
1.2 基本使⽤
1.2.1 在订单shop_order模块中加⼊Fegin的依赖
<!--fegin组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.2.2 在主类OrderApplication上添加Fegin的注解@EnableFeignClients//开启Fegin
注意:记得删除之前写的负载均衡的代码
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//开启Fegin
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
// @Bean
// @LoadBalanced//负载均衡
// public RestTemplate restTemplate() {
// return new RestTemplate();
// }
}
1.2.3 创建⼀个service, 并使⽤Fegin实现微服务调⽤
package com.apesource.service;
import com.apesource.domain.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("service-product")//声明调用的提供者的name
public interface IProductService {
//指定调用提供者的哪个方法
// @FeignClient+@GetMapping 就是一个完整的请求路径 http://service-product/product/{pid}
@GetMapping(value = "/product/{pid}")
Product findByPid(@PathVariable("pid") Integer pid);
}
1.2.4 修改controller代码,并启动验证
注入IProductService接口,通过fegin调用商品微服务 Product product = productService.findByPid(pid);
@RestController
public class OrderController {
@Autowired
private IProductService productService;
@Autowired
private IOrderService orderService;
//Ribbon下单
@RequestMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
//通过fegin调用商品微服务
Product product = productService.findByPid(pid);
//下单(创建订单)
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户");
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.createOrder(order);
return order;
}
}
1.2.5 5 重启order微服务,查看效果
Feign已经集成Ribbon
1.3 ⾃定义配置
Feign可以⽀持很多的⾃定义配置,如下表所示:
⼀般情况下,默认值就能满⾜我们使⽤,如果要⾃定义时,只需要创建⾃定义的@Bean覆盖默认Bean 即可。
配置⽂件⽅式
基于配置⽂件修改feign的⽇志级别可以针对单个服务:
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
也可以针对所有服务:
feign:
client:
config:
default: # 这⾥⽤default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL #
注意:需要把⽇志级别设置
logging:
level:
com.apesource: debug
⽽⽇志的级别分为四种:
- NONE:不记录任何⽇志信息,这是默认值。
- BASIC:仅记录请求的⽅法, URL以及响应状态码和执⾏时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
1.4.Feign使⽤优化
Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
URLConnection:默认实现,不⽀持连接池
Apache HttpClient :⽀持连接池
OKHttp:⽀持连接池
因此提⾼Feign的性能主要⼿段就是使⽤连接池代替默认的URLConnection 这⾥我们⽤Apache的HttpClient来演示
1)引⼊依赖
<!--httpClient的依赖-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2)配置连接池
feign:
client:
config:
default: # default全局的配置
loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和相应信息
httpclient:
enabled: true #开启feign对HttpClient的支持
max-connections: 200 # 最大连接数
max-connections-per-route: 50 #每个路径的最大连接数
总结:
1.⽇志级别尽量⽤basic
2.使⽤HttpClient或OKHttp代替URLConnection
-
引⼊feign-httpClient依赖
-
配置⽂件开启httpClient功能,设置连接池参数
2.Dubbo
Dubbo是阿⾥巴巴开源的基于 Java 的⾼性能 RPC分布式服务框架,致⼒于提供⾼性能和透明化 的RPC远程服务调⽤⽅案,以及SOA服务治理⽅案。 Spring Cloud Alibaba微服务开发框架集成 了Dubbo,可实现微服务对外暴露Dubbo协议的接⼝,Dubbo协议相⽐RESTful协议速度更快 RPC:RPC是远程过程(Remote Procedure Call)的缩写形式,调⽤RPC远程⽅法就像调⽤本地 ⽅法⼀样,Dubbo之前阿⾥研发,中间停⽌维护2年,最后开启维护并捐献给Apache.
dubbo官⽹: http://dubbo.io/
以上是 Dubbo 的⼯作原理图,从抽象架构上分为两层:服务治理抽象控制⾯ 和 Dubbo 数据⾯
- 服务治理控制⾯。服务治理控制⾯不是特指如注册中⼼类的单个具体组件,⽽是对 Dubbo 治 理体系的抽象表达。控制⾯包含协调服务发现的注册中⼼、流量管控策略、Dubbo Admin 控 制台等,如果采⽤了 Service Mesh 架构则还包含 Istio 等服务⽹格控制⾯。
- Dubbo 数据⾯数据⾯代表集群部署的所有 Dubbo 进程,进程之间通过 RPC 协议实现数据交 换,Dubbo 定义了微服务应⽤开发与调⽤规范并负责完成数据传输的编解码⼯作。
- 服务消费者 (Dubbo Consumer),发起业务调⽤或 RPC 通信的 Dubbo 进程
- 服务提供者 (Dubbo Provider),接收业务调⽤或 RPC 通信的 Dubbo 进程
2.2 基础实现
A.提供统⼀业务api
/**
实战中,会将所有服务接⼝设计给⼀个叫做api的服务,单独维护,此处就在common服务设置
*/
public interface IProductService {
Product findByPid(Integer pid);
}
B.编辑服务提供者product
添加依赖
<!--dubbo-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
添加dubbo配置(配置在product模块中)
dubbo:
scan:
base-packages: com.apesource.service.impl
protocols:
dubbo:
name: dubbo
port: -1
registry:
address: spring-cloud://localhost # 注册中⼼
编写并暴露服务
package com.apesource.service.impl;
import com.apesource.dao.ProductMapper;
import com.apesource.domain.Product;
import com.apesource.service.IProductService;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
//暴露服务:注意这里使用的是dubbo提供的注解@Service,而不是Spring的
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService,IService<Product> {
@Autowired
ProductMapper productMapper;
@Override
public Product findByPid(Integer pid) {
return productMapper.selectById(pid);
}
}
C.编辑服务消费者
添加依赖
<!--dubbo-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
添加dubbo配置
dubbo:
registry:
address: spring-cloud://localhost # 注册中⼼
cloud:
subscribed-services: service-product # 订阅的提供者名称
引⽤服务
package com.apesource.controller;
import com.apesource.domain.Order;
import com.apesource.domain.Product;
import com.apesource.service.IOrderService;
import com.apesource.service.IProductService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
//引用服务
@Reference
private IProductService productService;
@Autowired
private IOrderService orderService;
//Ribbon下单
@RequestMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
//通过dubbo调用商品微服务
Product product = productService.findByPid(pid);
//下单(创建订单)
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户");
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.createOrder(order);
return order;
}
}