第二张微服务的调用与注册
文章目录
- 工程导入
- 利用RestTemplate调用服务
- 需求
- 创建RestTemplate的实例到Spring容器
- 使用RestTemplate发送请求
- 消费者和提供者
- Eureka注册中心
- 服务远程调用会出现的问题
- Eureka的结构和作用
- Eureka的配置过程
- 搭建注册中心
- 服务注册
- 服务发现
- Ribbon负载均衡
- 负载均衡原理
- 源码跟踪
- 总结
- 负载均衡策略
- 自定义负载均衡策略
- 饥饿加载
- Nacos注册中心
- 认识和安装Nacos
- 下载安装包
- 解压
- 端口配置
- 启动
- 访问
- 服务注册到nacos
- 导入依赖
- 配置nacos地址
- 服务分级存储模型
- 给user-service配置集群
- 同集群优先的负载均衡
- 权重配置
- 环境隔离
- 创建namespace
- 给微服务配置namespace
- Nacos与Eureka的区别
- Feign远程调用
- Feign替代RestTemplate
- 自定义配置
- 配置文件方式
- Java代码方式
- Feign使用优化
- 最佳实践
- 继承方式
- 抽取方式
工程导入
![image-20230817163705325](https://img-blog.csdnimg.cn/img_convert/0f2cede767bb9fc512cd8d1dd506e770.png)
- 改变cloud_demo里数据库的配置,数据库的账户和密码,就能正常的运行项目,项目的JDK版本的1.8
利用RestTemplate调用服务
需求
- 订单的信息在ordr-service服务中,用户的信息在user-service服务中
修改order-service中的根据id查询订单业务,要求在查询订单的同时,根据订单中包含的userId查询出用户信息,一起返回。
- 因此,我们需要在order-service中 向user-service发起一个http的请求,调用http://localhost:8081/user/{userId}这个接口。
大概的步骤是这样的:
- 注册一个RestTemplate的实例到Spring容器
- 修改order-service服务中的OrderService类中的queryOrderById方法,根据Order对象中的userId查询User
- 将查询的User填充到Order对象,一起返回
创建RestTemplate的实例到Spring容器
@Configuration
public class RestTemplateConfig {@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}
使用RestTemplate发送请求
@RestController
@RequestMapping("order")
public class OrderController {@Resourceprivate RestTemplate restTemplate;@GetMapping("{orderId}")public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {// 根据id查询订单并返回Order order = orderService.queryOrderById(orderId);//查询用户的信息String url="http://localhost:8081/user/"+order.getUserId();User user = restTemplate.getForObject(url, User.class);order.setUser(user);return order;}
}
消费者和提供者
在服务调用关系中,会有两个不同的角色:
- 服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
- 服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
但是,服务提供者与服务消费者的角色并不是绝对的,而是相对于业务而言。
如果服务A调用了服务B,而服务B又调用了服务C,服务B的角色是什么?
- 对于A调用B的业务而言:A是服务消费者,B是服务提供者
- 对于B调用C的业务而言:B是服务消费者,C是服务提供者
- 因此,服务B既可以是服务提供者,也可以是服务消费者。
Eureka注册中心
服务远程调用会出现的问题
![image-20230824084302769](https://img-blog.csdnimg.cn/img_convert/2c787686658c48c21ca3414c2f051ec7.png)
-
order-service在发起远程调用的时候,该如何得知user-service实例的ip地址和端口?
-
有多个user-service实例地址,order-service调用时该如何选择?
-
order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
Eureka的结构和作用
![image-20230824084523570](https://img-blog.csdnimg.cn/img_convert/85f9b23498d2f609be92a025e79a0e8b.png)
问题1:order-service如何得知user-service实例地址?
获取地址信息的流程如下:
-
user-service服务实例启动后,将自己的信息注册到eureka-server(Eureka服务端)。这个叫服务注册
-
eureka-server保存服务名称到服务实例地址列表的映射关系
-
order-service根据服务名称,拉取实例地址列表。这个叫服务发现或服务拉取
问题2:order-service如何从多个user-service实例中选择具体的实例?
- order-service从实例列表中利用负载均衡算法选中一个实例地址向该实例地址发起远程调用
问题3:order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
-
user-service会每隔一段时间(默认30秒)向eureka-server发起请求,报告自己状态,称为心跳
-
当超过一定时间没有发送心跳时,eureka-server会认为微服务实例故障,将该实例从服务列表中剔除,order-service拉取服务时,就能将故障实例排除了
注意:一个微服务,既可以是服务提供者,又可以是服务消费者,因此eureka将服务注册、服务发现等功能统一封装到了eureka-client端
Eureka的配置过程
搭建注册中心
- 在cloud-demo父工程下,创建一个子模块
- 创建maven项目,并导入eureka的依赖
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>
</dependencies>
- 编写启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {public static void main(String[] args) {SpringApplication.run(EurekaApplication.class, args);}
}
- 编写配置文件
server:port: 10086
spring:application:name: eureka-server
eureka:client:service-url: defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: trueinstance-id: 127.0.0.1:${server.port}
- 启动服务
- 启动微服务,然后在浏览器访问:http://127.0.0.1:10086
服务注册
1)引入依赖
在user-service的pom文件中,引入下面的eureka-client依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)配置文件
在user-service中,修改application.yml文件,添加服务名称、eureka地址:
spring:application:name: userservice
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true #以IP地址注册到服务中心,相互注册使用IP地址,如果不配置就是机器的主机名instance-id: 127.0.0.1:${server.port} # instanceID默认值为主机名+服务名+端口
3)启动多个user-service实例
为了演示一个服务有多个实例的场景,我们添加一个SpringBoot的启动配置,再启动一个user-service。
首先,复制原来的user-service启动配置:
服务发现
下面,我们将order-service的逻辑修改:向eureka-server拉取user-service的信息,实现服务发现。
1)引入依赖
之前说过,服务发现、服务注册统一都封装在eureka-client依赖,因此这一步与服务注册时一致。
在order-service的pom文件中,引入下面的eureka-client依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)配置文件
服务发现也需要知道eureka地址,因此第二步与服务注册一致,都是配置eureka信息:
在order-service中,修改application.yml文件,添加服务名称、eureka地址:
spring:application:name: orderservice
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: trueinstance-id: 127.0.0.1:${server.port}
3)服务拉取和负载均衡
最后,我们要去eureka-server中拉取user-service服务的实例列表,并且实现负载均衡。
不过这些动作不用我们去做,只需要添加一些注解即可。
@Configuration
public class RestTemplateConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}
}
修改order-service服务中的cn.itcast.order.service包下的OrderService类中的queryOrderById方法。修改访问的url路径,用服务名代替ip、端口:
@RestController
@RequestMapping("order")
public class OrderController {@Autowiredprivate OrderService orderService;@Resourceprivate RestTemplate restTemplate;@GetMapping("{orderId}")public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {// 根据id查询订单并返回Order order = orderService.queryOrderById(orderId);//查询用户的信息// String url="http://localhost:8081/user/"+order.getUserId();//使用服务名代替String url = "http://user-service/user/"+order.getUserId();User user = restTemplate.getForObject(url, User.class);order.setUser(user);return order;}
}
- spring会自动帮助我们从eureka-server端,根据userservice这个服务名称,获取实例列表,而后完成负载均衡。
Ribbon负载均衡
上一节中,我们添加了@LoadBalanced注解,即可实现负载均衡功能,这是什么原理呢?
负载均衡原理
SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。
- 那么我们发出的请求明明是http://userservice/user/1,怎么变成了http://localhost:8081的呢?
源码跟踪
为什么我们只输入了service名称就可以访问了呢?之前还要获取ip和端口。
- 显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。
我们进行源码跟踪:
1)LoadBalancerIntercepor
可以看到这里的intercept方法,拦截了用户的HttpRequest请求,然后做了几件事:
- request.getURI():获取请求uri,本例中就是 http://user-service/user/8
- originalUri.getHost():获取uri路径的主机名,其实就是服务id,user-service
- this.loadBalancer.execute():处理服务id,和用户请求。
2) LoadBalancerClient
代码是这样的:
-
getLoadBalancer(serviceId):根据服务id获取LoadBalancer,而LoadBalancer会拿着服务id去eureka中获取服务列表并保存起来。
-
getServer(loadBalancer,hint):利用内置的负载均衡算法,从服务列表中选择一个。本例中,可以看到获取了8082端口的服务
放行后,再次访问并跟踪,发现获取的是8081:
3)负载均衡策略IRule
总结
![image-20230824160913523](https://img-blog.csdnimg.cn/img_convert/794a5563c220c150385c6ddffa36f0bf.png)
基本流程如下:
-
拦截我们的RestTemplate请求http://user-service/user/1
-
RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
-
DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
-
eureka返回列表,localhost:8081、localhost:8082
-
IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
-
RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求
负载均衡策略
负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类:
不同规则的含义如下:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的<clientName>.<clientConfigNameSpace>. ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
自定义负载均衡策略
通过定义IRule实现可以修改负载均衡规则,有两种方式:
代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
@Bean
public IRule randomRule(){return new RandomRule();
}
配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
注意,一般用默认的负载均衡规则,不做修改。
饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon:eager-load:enabled: true # 开启饥饿加载clients:- userservice # 指定饥饿加载的服务名称- xxxxservice # 如果需要指定多个,需要这么写
Nacos注册中心
国内公司一般都推崇阿里巴巴的技术,比如注册中心,SpringCloudAlibaba也推出了一个名为Nacos的注册中心。
认识和安装Nacos
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。
- 开发阶段采用单机安装即可。
下载安装包
在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码:
GitHub主页:https://github.com/alibaba/nacos
GitHub的Release下载页:https://github.com/alibaba/nacos/releases
如图:
解压
将这个包解压到任意非中文目录下,如图:
目录说明:
- bin:启动脚本
- conf:配置文件
端口配置
Nacos的默认端口是8848,如果你电脑上的其它进程占用了8848端口,请先尝试关闭该进程。
如果无法关闭占用8848端口的进程,也可以进入nacos的conf目录,修改配置文件中的端口:
修改其中的内容:
启动
启动非常简单,进入bin目录,结构如下:
然后执行命令即可:
-
windows命令:
startup.cmd -m standalone
执行后的效果如图:
访问
在浏览器输入地址:http://127.0.0.1:8848/nacos即可:
服务注册到nacos
Nacos是SpringCloudAlibaba的组件,而SpringCloudAlibaba也遵循SpringCloud中定义的服务注册、服务发现规范。因此使用Nacos和使用Eureka对于微服务来说,并没有太大区别。
主要差异在于:
- 依赖不同
- 服务地址不同
导入依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.6.RELEASE</version><type>pom</type><scope>import</scope>
</dependency>
- 在父工程中
<dependencyManagement>
去控制阿里巴巴组件的版本
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 在需要服务组成的模块中引入对应nacos依赖
- 注意:不要忘了注释掉eureka的依赖。
配置nacos地址
在user-service和order-service的application.yml中添加nacos地址:
spring:cloud:nacos:server-addr: localhost:8848
注意:不要忘了注释掉eureka的地址
服务分级存储模型
一个服务可以有多个实例,例如我们的user-service,可以有:
127.0.0.1:8081
127.0.0.1:8082
127.0.0.1:8083
假如这些实例分布于全国各地的不同机房,例如:
127.0.0.1:8081,在上海机房
127.0.0.1:8082,在上海机房
127.0.0.1:8083,在杭州机房
Nacos就将同一机房内的实例 划分为一个集群。
也就是说,user-service是服务,一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成分级模型,如图:
![image-20230825135253869](https://img-blog.csdnimg.cn/img_convert/bd3e31ce097d8a75f650e4b25d26d290.png)
微服务互相访问时,应该尽可能访问同集群实例,因为本地访问速度更快。当本集群内不可用时,才访问其它集群。例如:
![image-20230825135713606](https://img-blog.csdnimg.cn/img_convert/5c95ad0de908376c3fbd0b330f2d8525.png)
给user-service配置集群
修改user-service的application.yml文件,添加集群配置:
spring:cloud:nacos:server-addr: localhost:8848discovery:cluster-name: HZ # 集群名称
重启两个user-service实例后,我们可以在nacos控制台看到下面结果:
我们再次复制一个user-service启动配置,添加属性:
-Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=WH
同集群优先的负载均衡
默认的ZoneAvoidanceRule并不能实现根据同集群优先来实现负载均衡。
因此Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例。
给order-service配置集群信息
1)修改order-service的application.yml文件,添加集群配置:
spring:cloud:nacos:server-addr: localhost:8848discovery:cluster-name: HZ # 集群名称
2)修改负载均衡规则
修改order-service的application.yml文件,修改负载均衡规则:
userservice:ribbon:NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
权重配置
实际部署中会出现这样的场景:
服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。
但默认情况下NacosRule是同集群内随机挑选,不会考虑机器的性能问题。
因此,Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。
在nacos控制台,找到user-service的实例列表,点击编辑,即可修改权重
![](https://img-blog.csdnimg.cn/img_convert/8d69b0eb85557f4c974866a0dca399ec.png)
环境隔离
Nacos提供了namespace来实现环境隔离功能。
- nacos中可以有多个namespace
- namespace下可以有group、service等
- 不同namespace之间相互隔离,例如不同namespace的服务互相不可见
![image-20230825231039311](https://img-blog.csdnimg.cn/img_convert/983c81e238b41275eb92cb8084e7dda6.png)
创建namespace
默认情况下,所有service、data、group都在同一个namespace,名为public:
给微服务配置namespace
给微服务配置namespace只能通过修改配置来实现。
例如,修改order-service的application.yml文件:
spring:cloud:nacos:server-addr: localhost:8848discovery:cluster-name: HZnamespace: 35d92630-d382-4dab-b659-a7a6c019805f # 命名空间,填ID
此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错:
Nacos与Eureka的区别
Nacos的服务实例分为两种l类型:
-
临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。
-
非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。
配置一个服务实例为永久实例:
spring:cloud:nacos:discovery:ephemeral: false # 设置为非临时实例
Nacos和Eureka整体结构类似,服务注册、服务拉取、心跳等待,但是也存在一些差异:
Nacos与eureka的共同点
-
都支持服务注册和服务拉取
-
都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别
- Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
- Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
- Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
Feign远程调用
先来看我们以前利用RestTemplate发起远程调用的代码:
//String url="http://localhost:8081/user/"+order.getUserId();
User user = restTemplate.getForObject(url, User.class);
存在下面的问题:
- 代码可读性差,编程体验不统一
- 参数复杂URL难以维护
Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign,其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
Feign替代RestTemplate
Fegin的使用步骤如下:
引入依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
添加注解
在order-service的启动类添加注解开启Feign的功能:
编写Feign的客户端
在order-service中新建一个接口,内容如下:
@FeignClient("userservice")
public interface UserClient {@GetMapping("/{id}")public User queryById(@PathVariable("id") Long id);
}
这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值类型:User
测试远程调用接口
@RestController
@RequestMapping("order")
public class OrderController {@Autowiredprivate OrderService orderService;@Resourceprivate UserClient userClient;@GetMapping("{orderId}")public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {// 根据id查询订单并返回Order order = orderService.queryOrderById(orderId);User user = userClient.queryById(orderId);order.setUser(user);return order;}
}
总结
使用Feign的步骤:
① 引入依赖
② 添加@EnableFeignClients注解
③ 编写FeignClient接口
④ 使用FeignClient中定义的方法代替RestTemplate
自定义配置
Feign可以支持很多的自定义配置,如下表所示:
![image-20230827233546332](https://img-blog.csdnimg.cn/img_convert/adfddffda1f29d4bd1767f281a747692.png)
一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
配置文件方式
基于配置文件修改feign的日志级别可以针对单个服务:
feign: client:config: userservice: # 针对某个微服务的配置loggerLevel: FULL # 日志级别
也可以针对所有服务:
feign: client:config: default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置loggerLevel: FULL # 日志级别
而日志的级别分为四种:
-
NONE:不记录任何日志信息,这是默认值。
-
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
-
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
-
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
Java代码方式
也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象:
public class DefaultFeignConfiguration {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.BASIC; // 日志级别为BASIC}
}
如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
如果是局部生效,则把它放到接口对应的@FeignClient这个注解中:
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class)
Feign使用优化
Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
-
URLConnection:默认实现,不支持连接池
-
Apache HttpClient :支持连接池
-
OKHttp:支持连接池
因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。
这里我们用Apache的HttpClient来演示。
1)引入依赖
在order-service的pom文件中引入Apache的HttpClient依赖:
<!--httpClient的依赖 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId>
</dependency>
2)配置连接池
在order-service的application.yml中添加配置:
feign:client:config:default: # default全局的配置loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息httpclient:enabled: true # 开启feign对HttpClient的支持max-connections: 200 # 最大的连接数max-connections-per-route: 50 # 每个路径的最大连接数
总结,Feign的优化:
- 1.日志级别尽量用basic
- 2.使用HttpClient或OKHttp代替URLConnection
- ① 引入feign-httpClient依赖
- ② 配置文件开启httpClient功能,设置连接池参数
最佳实践
所谓最近实践,就是使用过程中总结的经验,最好的一种使用方式。
自习观察可以发现,Feign的客户端与服务提供者的controller代码非常相似:
feign客户端:
UserController:
![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/c4e1cc6ba1c30743f95acd3577653f01.png)
有没有一种办法简化这种重复的代码编写呢?
继承方式
一样的代码可以通过继承来共享:
- 1)定义一个API接口,利用定义方法,并基于SpringMVC注解做声明。
- 2)Feign客户端和Controller都集成改接口
优点:
-
简单
-
实现了代码共享
缺点:
-
服务提供方、服务消费方紧耦合
-
参数列表中的注解映射并不会继承,因此Controller中必须再次声明方法、参数列表、注解
抽取方式
将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用。
例如,将UserClient、User、Feign的默认配置都抽取到一个feign-api包中,所有微服务引用该依赖包,即可直接使用。
在feign-api中然后引入feign的starter依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
然后,order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
重启测试
重启后,发现服务报错了:
这是因为UserClient现在在cn.itcast.feign.clients包下,
而order-service的@EnableFeignClients注解是在cn.itcast.order包下,不在同一个包,无法扫描到UserClient。
解决扫描包问题
方式一:
指定Feign应该扫描的包:
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
方式二:
指定需要加载的Client接口:
@EnableFeignClients(clients = {UserClient.class})
相关文章:
![](https://img-blog.csdnimg.cn/img_convert/0a61ceaf8a73e8b227baf511850664d6.png)
第二张微服务的调用与注册
文章目录 工程导入利用RestTemplate调用服务需求创建RestTemplate的实例到Spring容器使用RestTemplate发送请求消费者和提供者 Eureka注册中心服务远程调用会出现的问题Eureka的结构和作用Eureka的配置过程搭建注册中心服务注册服务发现 Ribbon负载均衡负载均衡原理源码跟踪总结…...
![](https://img-blog.csdnimg.cn/d0ede0ca8ea64325a8c3985237dbbdf8.png)
iWatch框架设计
iWatch框架设计 一、项目框架结构设计 1、项目文件介绍 OverSeaProject:是IOS相关文件文件内容iWatchApp和iWatch Extension:是之前使用xcode14之前的xcode创建的360 app的Watch App,产生的文件结构,包含一个app和Extension的ta…...
![](https://img-blog.csdnimg.cn/e30b554621d2456fb1dec881260f3124.png)
【python】读取.dat格式文件
import binascii# 打开二进制文件以只读二进制模式 with open(EXCEL/文件.dat, rb) as file:binary_data file.read()print(binary_data)# 将二进制数据转换为十六进制字符串 hex_data binascii.hexlify(binary_data).decode(utf-8) # binary_data 现在包含了文件的二进制内容…...
![](https://www.ngui.cc/images/no-images.jpg)
机器学习课后习题 --- 朴素贝叶斯
(一)单选题 1.假设会开车的本科生比例是15%,会开车的研究生比例是23%。若在某大学研究生占学生比例是20%,则会开车的学生是研究生的概率是多少? A:80%B:16.6% C:23% D:15% 2.下列关于朴素贝叶斯的特点说法错误的是…...
![](https://img-blog.csdnimg.cn/4dbc0ed65cf540cda86dac1901cd91e4.png)
【设备树笔记整理7】实践操作
1 使用设备树给DM9000网卡_触摸屏指定中断 1.1 修改方法 根据设备节点的compatible属性,在驱动程序中构造/注册 platform_driver,在 platform_driver 的 probe 函数中获得中断资源。 1.2 实验方法 以下是修改好的代码:第6课第1节_网卡_触摸…...
![](https://img-blog.csdnimg.cn/img_convert/42dfddb0e9471e8e94e0469225a3d625.png)
使用VisualStudio制作上位机(六)
文章目录 使用VisualStudio制作上位机(六)第五部分:应用程序打包第一步:勾选为Release模式第二步:生成解决方案第三步:将我们额外添加的文件放入到Release这个文件夹里 使用VisualStudio制作上位机…...
![](https://img-blog.csdnimg.cn/c509ce863fd14afb8e8fd0115abefebc.gif)
包管理工具--》npm的配置及使用(一)
目录 🌟概念 🌟背景 🌟前端包管理器 🌟包的安装 本地安装 全局安装 🌟包配置 配置文件 保存依赖关系 🌟包的使用 🌟写在最后 🌟概念 模块(module)…...
![](https://img-blog.csdnimg.cn/img_convert/167f49b5998efbbb1a118b6048f82a78.png)
期货基础知识
一、期货是什么? 期货是与现货相对应,并由现货衍生而来。期货通常指期货合约,期货与现货完全不同,现货是实实在在可以交易的货(商品),期货主要不是货,而是以某种大众产品如棉花、大…...
![](https://img-blog.csdnimg.cn/e8fd4bc1731843c1ac0977542eefc031.png)
NC后端扩展开发
前言 在日常的工作中,会遇到各种各样的需要进行扩展开发的需求,可以使用系统预留的扩展开发机制来实现,避免修改源码。因NC产品已迭代至BIP版本,所以前端扩展方式就再进行不赘述了,本文主要介绍后端扩展开发方式&…...
![](https://www.ngui.cc/images/no-images.jpg)
nginx vue2+webpack 和 vue3+vite 配置二级目录访问
我们开发中会遇到这样的需求,让我们用服务器nginx部署一个用域名的二级目录来访问项目 https:xxx/二级目录/来放访问项目 目录 思路 1、nginx配置(vue2 和 vue3配置的nginx相同) 2、vue2webpack的配置 (1࿰…...
![](https://img-blog.csdnimg.cn/img_convert/a87255810161428a9b2ee44127bcaee8.png#pic_center)
无需租云服务器,Linux本地搭建web服务,并内网穿透发布公网访问
文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道,指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 前言 在web项目中,部署的web站点需要被外部访问,则…...
![](https://img-blog.csdnimg.cn/img_convert/9fa3e71b3c4871f1652a850f43cc736a.gif#pic_center)
算法leetcode|76. 最小覆盖子串(rust重拳出击)
文章目录 76. 最小覆盖子串:样例 1:样例 2:样例 3:提示:进阶: 分析:在这里插入图片描述 题解:rust:go:c:python:java: 76.…...
![](https://img-blog.csdnimg.cn/5807c0dac8c0400eafd3d854ae790abe.png)
如何让你的jupyter notebook 排版得像Word(Markdown和网页文件写法)
案例背景 很多时候我们在jupyter notebook里面的写代码,画图,但是文字分析什么的写在里面纯文本不好看,需要进行排版,那么就得用markdown的写法,如何还想居中或者更花里胡哨的字体,那就得要网页文件的一些…...
![](https://img-blog.csdnimg.cn/d5212eb5409541b19989134f01327ab4.png)
AndroidTV端:酒店扫码认证投屏DLNA
被老板叼了几次了,最近实在忍不了,准备离职; 但是担心离职后长时间没有办法找到工作 就想贡献一套平时琢磨出来的程序,请各位有能力的话带我熬过这凛冽的寒冬。 目前写出来的,有三个端:安卓TV端…...
![](https://img-blog.csdnimg.cn/f4cc7c5ae4c9436aaf560ad60abf43bd.png)
基于PyTorch的交通标志目标检测系统
一、开发环境 Windows 10PyCharm 2021.3.2Python 3.7PyTorch 1.7.0 二、制作交通标志数据集,如下图 三、配置好数据集的地址,然后开始训练 python train.py --data traffic_data.yaml --cfg traffic_yolov5s.yaml --weights pretrained/yolov5s.pt --e…...
![](https://www.ngui.cc/images/no-images.jpg)
feign调用失败 feign.RetryableException: xxx-service executing GET http://xxx/test
一。 问题引入 升级springcloud的版本后 突然发现 以前正常的feign调用也报错了 升级后的各组件版本如下 spring cloud 2021.0.5 spring cloud alibaba 2021.0.5.0 spring boot 2.6.13 错误日志如下 feign.RetryableException: xxx-service executing GET http://xxx-servic…...
![](https://www.ngui.cc/images/no-images.jpg)
mysql 用户管理
目录 用户 创建用户 删除用户 修改密码 权限管理 赋权 查看权限 插销权限 总结 用户 mysql 的用户都存在于系统数据库 mysql 的user 表中 mysql> show tables; --------------------------- | Tables_in_mysql | --------------------------- | column…...
![](https://img-blog.csdnimg.cn/1dfe90b1a10f452e9421f9e4ad6bf09e.png#pic_center)
pyinstaller打包exe运行闪退
这里写自定义目录标题 前言问题描述解决过程 前言 闪退原因可能有很多,这里记录下我遇到的问题,简单来说是dll调用错误导致的闪退,因为我的python用的是32位的,但是pyinstaller却是64位的,属于用conda的时候没注意。 …...
![](https://img-blog.csdnimg.cn/5b631699322c43eaa4c120774a697693.png)
ARM 汇编基础知识
1.为什么学习汇编? 我们在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编,因为 Cortex-A 芯片一 上电 SP 指针还没初始化, C 环境还没准备好,所以肯定不能运行 C 代码,必须先用汇编语言设置好 C 环境…...
![](https://img-blog.csdnimg.cn/c5afc7dede854bbeb69c062e00467b28.png#pic_center)
CRM 自动化如何改善销售和客户服务?
许多 B2B 和 B2C 公司都使用 CRM 系统来组织业务流程,使复杂的任务更容易完成。企业可以使用 CRM 自动化来自动化工作流程,让团队有更多的时间来执行高价值的任务,而不是陷于一堆琐碎事情中。 什么是CRM自动化? CRM 自动化是指 C…...
![](https://img-blog.csdnimg.cn/c6b619379edd49eb8d80bd38a84c545b.webp)
Bean 的六种作用域
目录 一、作用域是什么? 1、singleton(单例作用域) 2、prototype(原型作用域) 3、request(请求作用域) 4、session(回话作用域) 5、application(全局作用域&a…...
![](https://img-blog.csdnimg.cn/0ff79cb6ef54449ca33b5c7e8f5a36d2.png)
go语言--锁
锁的基础,go的锁是构建在原子操作和信号锁之上的 原子锁 原子包实现协程的对同一个数据的操作,可以实现原子操作,只能用于简单变量的简单操作,可以把多个操作变成一个操作 sema锁 也叫信号量锁/信号锁 核心是一个uint32值&#…...
![](https://www.ngui.cc/images/no-images.jpg)
再见,CSDN
从我2018年1月31日加入CSDN,到现在已经5年多的时间了。在这5年里,陆陆续续在CSDN上发布了很多论文阅读笔记、教程、技术文章等等,记录了我从大四到研究生再到工作这段时间的学习和成长轨迹。 我一直有备份个人资料的习惯,尤其是耗…...
![](https://img-blog.csdnimg.cn/f4d79833404b4897ba9b0d388482fa68.jpeg)
MySQL总复习
目录 登录 显示数据库 创建数据库 删除数据库 使用数据库 创建表 添加数据表数据 查询表 添加数据表多条数据 查询表中某数据 增insert 删delete 改update 查select where like 编辑 范围查找 order by 聚合函数 count max min sum avg g…...
![](https://img-blog.csdnimg.cn/img_convert/1e2cf1870b5af421e1c4771b268b9a87.png)
桌面平台层安全随手记录
声明 本文是学习桌面云安全技术要求. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 桌面平台层安全 桌面接入安全 用户标识 一般要求 本项要求包括: a) 系统应为用户提供唯一的身份标识,同时将用户的身份标识与该用户的所…...
![](https://img-blog.csdnimg.cn/e56a54e0554f4c64a8a05352422ba658.png)
【Docker】 08-Dockerfile
什么是Dockerfile Dockerfile可以认为是Docker镜像的描述文件,是由一系列命令和参数构成的教程,主要作用是用来构建docker镜像的构建文件。 Dockerfile解析过程 Dockerfile的保留命令 保留字作用FROM当前镜像是基于哪个镜像的 第一个指令必须是FROMMA…...
![](https://img-blog.csdnimg.cn/img_convert/f829b8021af6e6a8583c7babbcacbb92.png)
【二等奖方案】大规模金融图数据中异常风险行为模式挖掘赛题「Aries」解题思路
第十届CCF大数据与计算智能大赛(2022 CCF BDCI)已圆满结束,大赛官方竞赛平台DataFountain(简称DF平台)正在陆续释出各赛题获奖队伍的方案思路,欢迎广大数据科学家交流讨论。 本方案为【大规模金融图数据中…...
![](https://img-blog.csdnimg.cn/c257e6b266ef416888b0c8d1dbbb9967.png)
Github 下载指定文件夹(git sparse-checkout)
比如要下载这里的 data_utils 步骤 1、新建空文件夹,并进入新建的空文件夹。 2、git init 初始化 3、git remote add origin 添加远程仓库 4、git config core.sparsecheckout true 允许稀疏检出 5、git sparse-checkout set 设置需要拉取的文件夹(可…...
![](https://img-blog.csdnimg.cn/img_convert/96eb7f8fc3b5fe74c781b2d10bf730a5.png)
蚂蚁集团SQLess 开源,与内部版有何区别?
当我们使用关系型数据库时,SQL 是联系起用户和数据库的一座桥梁。 SQL 是一种高度非过程化的语言,当我们在编写SQL 时,表达的是想要什么数据,而不是怎么获取数据。因此,我们往往更关心SQL 有没有满足业务逻辑ÿ…...
![](https://www.ngui.cc/images/no-images.jpg)
An Efficient Memory-Augmented Transformer for Knowledge-Intensive NLP Tasks
本文是LLM系列文章,针对《An Efficient Memory-Augmented Transformer for Knowledge 一种用于知识密集型NLP任务的高效内存增强转换器 摘要1 引言2 相关工作3 高效内存增强Transformer4 EMAT的训练流程5 实验6 分析7 结论局限性 摘要 获取外部知识对于许多自然语言…...
![](/images/no-images.jpg)
网络营销推广方法哪家正规/武汉seo首页优化技巧
我在28岁生日那天电信一个副总劝我出来开公司算了,然后想了一天第二天就去工商局注册了,从有开公司的想法到工商局注册仅仅一天时间! 然后2003年8 月份拿到营业执照,根本不知道安全是怎么一回事情,找北大方正一个技术副总和我讲了…...
![](/images/no-images.jpg)
有一个做5s壁纸的网站/手机app免费下载
这是一篇很牛逼的技术文章,讲述如何对 NoSQL 的数据进行建模。 英文原文:http://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/ NoSQL databases are often compared by various non-functional criteria, such as scalabil…...
如何防止网站被攻击/电商培训大概多少学费
1:创建表(Create Table) 定义好各个字段之后,只需要点击[创建]按钮,一张表就生成了 2:全文查找 可以对数据库所有对象,甚至是某一个字段进行查找 3:对象管理 **对于文章出现的任何问题请大家批评指出,一定及时修改…...
临湘市建设局网站/考研培训班集训营
微信小程序官网: https://mp.weixin.qq.com/cgi-bin/wx 小程序如何唤起APP?小程序支持跳转APP了,怎样实现双赢? http://www.apppark.cn/t-941.html 有了小程序是不是可以卸载所有的APP了? http://www.sohu.com/a/200685713_…...
![](/images/no-images.jpg)
wordpress 深色主题/如何在百度推广网站
Qt Quick基础用法1. 简介1.1 Qt Widgets 与 QML/Qt Quick2. QML与QtQuick2.1 QtQuick 1.x VS QtQuick 2.x3. 信号(Signal )与槽(Slot)4. QML与C混合编程4.1 原理和方法4.2 QML访问C 类 (QML>C)4.2.1 信号和槽4.2.2 把类注册到Q…...
![](https://img-blog.csdnimg.cn/img_convert/db8246b8f210fc13cc2c09ef7ef96b2b.png)
深圳专业做网站排名公司哪家好/青岛seo网站排名优化
文|智能相对论 作者|沈浪 百年风雨,世事更迭。 当汽车作为全新的交通工具登上历史的舞台,那时候的我们还沉浸在盲目的封建自足之中,因此错失了一个时代的浪潮。自此,在往后的百年时间内,尽管中国奋力直追࿰…...