015-从零搭建微服务-远程调用(一)

写在最前

如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。

源码地址(后端):https://gitee.com/csps/mingyue

源码地址(前端):https://gitee.com/csps/mingyue-ui

文档地址:https://gitee.com/csps/mingyue/wikis

远程调用

远程调用为分布式系统提供了一种方便、高效和可靠的通信方式,使得不同部分的组件能够协同工作,并实现代码的模块化、解耦和重用。它是构建可扩展、灵活和可维护的分布式系统的重要工具和技术。

为什么要使用远程调用?

  1. 分布式系统:在分布式系统中,不同的组件或服务可能分布在不同的机器或进程中。使用远程调用可以使这些组件能够通过网络相互通信,进行数据交换和协作,实现系统的功能和业务逻辑。
  2. 模块化和解耦:远程调用使得系统的各个模块能够独立开发和部署,通过定义清晰的接口和协议,模块之间可以解耦并独立演化。这样,系统的不同部分可以独立进行修改、升级或替换,而不会对其他部分产生影响。
  3. 代码重用性:通过远程调用,可以将某些功能或服务封装为可复用的模块,供多个应用程序或系统共享和调用。这样可以避免重复开发相同的功能,提高代码的重用性和开发效率。
  4. 扩展性和弹性:远程调用可以实现系统的弹性扩展和水平扩展。当系统负载增加时,可以通过部署更多的服务实例,并使用负载均衡器进行流量分发,以应对更高的并发请求。
  5. 服务治理和监控:使用远程调用框架可以集成服务治理和监控功能,例如服务注册与发现、负载均衡、熔断器、日志记录等。这些功能可以提供更好的可用性、性能和可维护性,同时也可以帮助开发人员进行系统的监控和故障排查。

技术选型

OpenFeign 与 Dobbo 3 都是优秀的微服务架构下的远程调用框架,各有千秋。

维度 OpenFeign Dobbo 3
类型 基于 Java 的声明式 Web 服务客户端 RPC(远程过程调用)框架
协议支持 支持 RESTful API,通常基于 HTTP 协议进行通信 支持多种远程通信协议,不仅包括 Dubbo 自有的二进制协议,还支持 HTTP、gRPC等,这使得 Dubbo 3 在不同场景下更加灵活
编程模型 声明式的 API 定义和调用方式,通过 Java 接口和注解来描述服务接口和方法,使用起来更加简洁和直观 声明式的 API,但更多情况下使用 XML 配置或注解配置,同时支持传统的编程式调用方式。
服务治理 集成了 Netflix 的负载均衡器 Ribbon,但相较 Dubbo 3在服务治理方面较为简单。 提供了更多功能,如负载均衡、服务注册与发现、熔断器等,适用于大规模分布式系统的构建。
生态和社区 2019年 Netflix 公司宣布 Feign 组件正式进入停更维护状态,于是 Spring 官方便推出了一个名为 OpenFeign 的组件作为 Feign 的替代方案。 2011年开源,2012年发布2.5.3版本后停止更新,2017年阿里重启 dubbo 项目,现在由 Apache 软件基金会进行维护和发展,具有稳定的社区支持。

OpenFeign + OkHttp3

本架构将采用 OpenFeign + OkHttp3 作为远程调用工具。

  • 声明式 API 定义:OpenFeign 提供了声明式的 API 定义方式,使用接口和注解来描述服务接口和方法。这使得代码更加简洁、可读性更强,并且开发人员可以更专注于业务逻辑而不是底层的 HTTP 请求细节。
  • 强大的 HTTP 功能:OkHttp3 是一个功能强大的 HTTP 客户端库,提供了丰富的功能和特性,如连接池管理、请求和响应拦截器、请求重试、缓存等。通过将 OpenFeign 与 OkHttp3 结合使用,可以利用 OkHttp3 的这些功能来处理HTTP 请求和响应,提高性能和可靠性。
  • 高性能和可扩展性:OkHttp3 被广泛认为是一个高性能的 HTTP 客户端,具有优秀的性能和效率。它支持并发请求、连接复用和异步操作等,能够满足大规模和高并发的需求。同时,OkHttp3 也具备很好的可扩展性,可以通过自定义拦截器和插件来扩展和定制其功能。
  • 网络层配置:OkHttp3 提供了丰富的网络层配置选项,如连接超时、读写超时、代理设置等。通过使用 OkHttp3 作为OpenFeign 的底层 HTTP 客户端,可以灵活地配置和管理网络层的行为,以满足特定的需求。
  • 生态和社区支持:OpenFeign 和 OkHttp3 都是广受欢迎的开源项目,拥有活跃的社区和稳定的维护。这意味着开发人员可以从社区中获得支持、文档和更新的功能。

综上所述,将 OpenFeign 与 OkHttp3 结合使用可以享受到 OpenFeign 声明式 API 定义的便利性,并利用 OkHttp3 提供的强大的 HTTP 功能和性能优势。这样的组合可以简化代码、提高性能,并为开发人员提供更好的可扩展性和配置灵活性。

添加 mingyue-common-feign

引入依赖

<dependencies>
  <dependency>
    <groupId>com.csp.mingyuegroupId>
    <artifactId>mingyue-common-coreartifactId>
  dependency>

  
  <dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
  dependency>
  
  <dependency>
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-okhttpartifactId>
  dependency>
  
  <dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-loadbalancerartifactId>
  dependency>
dependencies>

自定义注解

覆盖 @EnableFeignClients 注解,默认 basePackages,客户端直接使用 @EnableMingYueFeignClients 注解即可,无须再指定 basePackages。

import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 开启 Feign Client
 *
 * @author Strive
 * @date 2023/7/4
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnableMingYueFeignClients {

	/**
	 * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
	 * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
	 * {@code @ComponentScan(basePackages="org.my.pkg")}.
	 * @return the array of 'basePackages'.
	 */
	String[] value() default {};

	/**
	 * Base packages to scan for annotated components.
	 * 

* {@link #value()} is an alias for (and mutually exclusive with) this attribute. *

* Use {@link #basePackageClasses()} for a type-safe alternative to String-based * package names. * @return the array of 'basePackages'. */ String[] basePackages() default { "com.csp.mingyue" }; /** * Type-safe alternative to {@link #basePackages()} for specifying the packages to * scan for annotated components. The package of each class specified will be scanned. *

* Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. * @return the array of 'basePackageClasses'. */ Class<?>[] basePackageClasses() default {}; /** * A custom @Configuration for all feign clients. Can contain override * @Bean definition for the pieces that make up the client, for instance * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}. * * @see FeignClientsConfiguration for the defaults */ Class<?>[] defaultConfiguration() default {}; /** * List of classes annotated with @FeignClient. If not empty, disables classpath * scanning. * @return */ Class<?>[] clients() default {}; }

Nacos 开启配置

application-common.yml

# feign 配置
feign:
  # 启用 okhttp 作为网络请求框架      
  okhttp:
    enabled: true
  # 关闭 httpclient 作为网络请求框架
  httpclient:
    enabled: false
  client:
    config:
      # 将调用的微服务名称改成 default 就配置成全局的了
      default:
        # 相当于 Request.Optionsn 连接超时时间
        connectTimeout: 10000
        # 相当于 Request.Options 读取超时时间
        readTimeout: 10000
  compression:
    request:
      # 配置请求 GZIP 压缩
      enabled: true
    response:
      # 配置响应 GZIP 压缩
      enabled: true

修改 mingyue-system-api

引入依赖

<dependency>
  <groupId>com.csp.mingyuegroupId>
  <artifactId>mingyue-common-feignartifactId>
dependency>

远程调用用户服务

import com.csp.mingyue.common.core.constant.ServiceNameConstants;
import com.csp.mingyue.common.core.vo.R;
import com.csp.mingyue.system.api.entity.SysUser;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 远程调用用户服务
 *
 * @author Strive
 * @date 2023/7/3 09:48
 */
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE)
public interface RemoteUserService {

	/**
	 * 通过用户名查询用户、角色信息
	 * @param username 用户名
	 * @return R
	 */
	@GetMapping(value = "/sysUser/getSysUserInfoByUsername")
	R<SysUser> getSysUserInfoByUsername(@RequestParam(value = "username") String username);

}

mingyue-auth 远程调用 mingyue-system

引入依赖


<dependency>
  <groupId>com.csp.mingyuegroupId>
  <artifactId>mingyue-system-apiartifactId>
dependency>

修改登录

public SaTokenInfo login(PasswordLoginDto dto) {
  R<SysUser> userInfoResp = remoteUserService.getSysUserInfoByUsername(dto.getUsername());

  if (Objects.isNull(userInfoResp) || Objects.isNull(userInfoResp.getData())) {
    return null;
  }

  SysUser userInfo = userInfoResp.getData();

  if (dto.getUsername().equals(userInfo.getUsername()) && dto.getPassword().equals(userInfo.getPassword())) {
    // 第1步,先登录上
    StpUtil.login(10001);
    // 第2步,获取 Token 相关参数
    SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

    return tokenInfo;
  }

  return null;
}

启动测试

curl -X 'POST' \
  'http://mingyue-gateway:9100/auth/login' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '{
  "username": "mingyue",
  "password": "123456"
}'

返回示例

{
  "code": 200,
  "msg": "登录成功",
  "data": "2GcAFW7UZ0XJjDe5H76CBAtj7zc7bm8S"
}

开启或关闭 OkHttp3

默认的HttpURLConnection是 JDK 自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。OkHttp3 自带连接池管理,提升吞吐量。

核心依赖


<dependency>
  <groupId>io.github.openfeigngroupId>
  <artifactId>feign-okhttpartifactId>
dependency>

使用方式

Nacos 配置 application-common.yml

# feign 配置
feign:
  # 启用 okhttp 作为网络请求框架      
  okhttp:
  	# true 开启 / false 关闭
    enabled: true

验证是否开启

查看 Client 实现,实际发送请求是由 Feign 中的Client接口实现类去处理的,默认使用的是 Defalut 类,该类使用的是HttpURLConnection

015-从零搭建微服务-远程调用(一)_第1张图片

默认实现,关闭 okhttp 后,对此方法断点查看是否进入

@Override
public Response execute(Request request, Options options) throws IOException {
  HttpURLConnection connection = convertAndSend(request, options);
  return convertResponse(connection, request);
}

打开 OkHttpClient 类,查看 OkHttp3 源码,找到 execute 方法。开启 okhttp 后,对此方法断点查看是否进入

@Override
public feign.Response execute(feign.Request input, feign.Request.Options options)
  throws IOException {
  okhttp3.OkHttpClient requestScoped = getClient(options);
  Request request = toOkHttpRequest(input);
  Response response = requestScoped.newCall(request).execute();
  return toFeignResponse(response, input).toBuilder().request(input).build();
}

小结

至此,mingyue-auth 已经可以通过远程调用获取 mingyue-system 服务。下一节,网关增加接口拦截,引入白名单列表。

你可能感兴趣的:(微服务,架构,云原生)