对在Spring Cloud Alibaba使用nacos做注册和动态配置中心,Feign远程调用,并实现负载均衡的学习过程做一下总结,希望大家共同进步。
一. 安装nacos服务
-
具体安装步骤可参考这篇博客:
https://blog.csdn.net/qq_32352777/article/details/86560333
写的很不错,感谢作者。 -
安装问题总结
-
测试环境一般建议安装单机版的,简单快速。集群安装的话,为了符合高可用的要求,MySQL也需要安装成集群形式。如Master-Slave模式。
-
如果是在虚拟机上安装,一般的虚拟机我们不会给分配太大内存。所以我们得根据自己的机子内存情况修改一下nacos启动时默认分配的虚拟机内存,否则会启动失败,我们去查看nacos的安装目录下的log目录的nacos.log日志,会发现报内存不足的异常。
(1)在nacos安装目录下,打开nacos启动脚本。
vim bin/startup.sh
(2)vim模式下ctrl+f向后翻页,找到如下部分
在这里插入图片描述
(3)根据实际情况进行对nacos启动jvm虚拟机内存设置进行修改。
-
二. 代码实现
-
创建父项目
父pom,具体说明在注释里面
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>cloud-test</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cloud-test</name> <packaging>pom</packaging> <!--子模块--> <modules> <module>cloud-service1</module> <module>cloud-service2</module> <module>cloud-consumer</module> <module>cloud-common</module> </modules> <!--依赖的组件的大版本号--> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version> <spring-cloud-alibaba.version>0.9.0.RELEASE</spring-cloud-alibaba.version> <spring-boot-starter-parent.version>2.2.2.RELEASE</spring-boot-starter-parent.version> </properties> <!--依赖jar包版本管理--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring-boot-starter-parent.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--服务发现(注册中心)--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--配置中心--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>LATEST</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
项目结构图:
在这里插入图片描述 -
创建公共子模块
公共做模块是放服务调用的接口和一些公共实体类和公共工具的。结构如下:
在这里插入图片描述pom文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cloud-consumer</name> <packaging>jar</packaging> <parent> <groupId>com.test</groupId> <artifactId>cloud-test</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.test</groupId> <artifactId>cloud-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project>
创建一个测试的实体类User
package com.common.model; import lombok.Data; /** * Author: yhl * DateTime: 2019/12/8 23:13 * Description: write some description */ @Data public class User { private Long id; private String name; private String url; }
创建微服务调用的接口IUserService
package com.common.service; import com.common.model.User; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.Map; /** * Author: yhl * DateTime: 2019/12/8 23:13 * Description: write some description */ public interface IUserService { @RequestMapping("/testOut") Map<Object,Object> test(@RequestParam("name") String name); @RequestMapping("/testOutObj") User testObj(@RequestBody User user); @RequestMapping("/testNacosDynamicProp") String testNacosDynamicProp(); }
配置文件application.yml可以放置一些公共的参数配置,比如加解密私钥等等
-
创建服务提供者
此模块为服务提供方,实现真正的业务逻辑。结构如下:
在这里插入图片描述为了测试Feign的负载均衡,创建两个一样结构和内容的服务提供者,分别命名为cloud-service1和cloud-service2,在它们各自的application.yml中,使用不同的端口号来模拟不同地址的相同的服务。
pom:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.test</groupId> <artifactId>cloud-test</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>cloud-service1</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cloud-service1</name> <packaging>jar</packaging> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--引入公共模块--> <dependency> <groupId>com.test</groupId> <artifactId>cloud-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> </project>
创建配置文件bootstrap.yaml和application.yml
bootstrap.yml如下:
spring: application: # 服务提供者名称 name: cloud-service cloud: nacos: discovery: # 服务注册于发现地址 server-addr: 192.168.31.217:8848 config: # 配置中心地址 server-addr: ${spring.cloud.nacos.discovery.server-addr} # 配置中心配置文件的格式,如果不设置此项,默认为.properties格式 file-extension: yaml
application.yml如下:
#服务端口,这里我们设置cloud-service1为8001,cloud-service2为8002 server: port: 8081
有了application.yml,为什么还要创建一个bootstrap.yml呢,它们俩的区别如下:
https://blog.csdn.net/ThinkWon/article/details/100007093
感谢这篇文章的作者,解开了我的迷惑。
创建启动类ServiceApplication1
package com.service; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication public class ServiceApplication1 { public static void main(String[] args) { SpringApplication.run(ServiceApplication1.class, args); } }
创建服务提供类UserServiceProvider
package com.service.controller; import com.common.model.User; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; /** * Author: yhl * DateTime: 2019/12/8 23:17 * Description: write some description */ @RestController public class UserServiceProvider { @RequestMapping("/testOut") public Map test(@RequestParam("name") String name, HttpServletRequest req) { Map<Object, Object> map = new HashMap<>(); map.put("url", req.getRequestURL().toString()); map.put("name", name); return map; } @RequestMapping("/testOutObj") public User testObj(@RequestBody() User user, HttpServletRequest req) { user.setUrl(req.getRequestURL().toString()); return user; } }
服务提供者创建完成。
-
创建服务消费者cloud-consumer
pom文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cloud-consumer</name> <packaging>jar</packaging> <parent> <groupId>com.test</groupId> <artifactId>cloud-test</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.test</groupId> <artifactId>cloud-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-Feign---> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project>
因为远程调用使用的是Feign组件,所以pom中加了Feign的依赖。
创建配置文件bootstrap.yaml和application.yml
bootstrap.yml如下:
spring: application: # 服务提供者名称 name: cloud-consumer cloud: nacos: discovery: # 服务注册于发现地址 server-addr: 192.168.31.217:8848 config: # 配置中心地址 server-addr: ${spring.cloud.nacos.discovery.server-addr} # 配置中心配置文件的格式,如果不设置此项,默认为.properties格式 file-extension: yaml
application.yml如下:
#服务端口,这里我们设置cloud-service1为8001,cloud-service2为8002 server: port: 8080
创建服务消费者启动类ConsumerApplication
package com.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients//开启feign public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
在启动类上加@EnableFeignClients,代表开启了对feign组件的支持。
继承服务提供方接口UserService
@FeignClient(name ="cloud-service") public interface UserService extends IUserService { }
此接口方法继承了我们在common模块定义的微服务接口,加上@FeignClient注解之后,表示可使用Feign来调用此接口中方法中@RequestMapping映射路径的服务提供者的http服务。
此接口是我抽象出来的,目的是不用在common模块中也引入Feign的依赖,当然也可以不用这么写。直接在IUserService接口加上@FeignClient注解。@FeignClient中的name属性配置的为bootstrap.yml中配置的服务提供者名称。
创建消费者接口UserConsumerController
package com.consumer.controller; import com.common.model.User; import com.consumer.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; /** * Author: yhl * DateTime: 2019/12/8 23:12 * Description: write some description */ @RestController public class UserConsumerController { @Autowired private UserService userService; @RequestMapping("/test") public Map<Object,Object> test() { return userService.test("张三"); } @RequestMapping("/testObj") public User testObj() { return userService.testObj(new User()); } @RequestMapping("/testNacosDynamicProp") public String testNacosDynamicProp() { return userService.testNacosDynamicProp(); } }
创建负载均衡配置类ConfigBean
package com.consumer.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * @author: yhl * @DateTime: 2019/12/9 13:37 * @Description: */ @Configuration public class ConfigBean { /** * 默认按顺序轮询 * @return */ @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
至此,我们的模块都创建完毕了,接下来进行测试。
-
服务和负载均衡测试
我们将三个模块的服务都启动,如下图:
在这里插入图片描述利用http接口测试工具来测试我们的接口
选择一个接口进行测试,如 http://localhost:8080/test
在这里插入图片描述
在这里插入图片描述发送两次请求,会发现打印的请求地址的端口号不同,说明实现了负载均衡。
连续发送多次请求,端口号会交替变化,因为默认的负载均衡策略为轮训法。由于本片内容过程长,关于feign的负载均衡,单独再开一篇blog,感兴趣的话可以接着看
有什么问题欢迎大家交流。
网友评论