本文将带领大家搭建一个简单的微服务项目
一、微服务架构的目的
当我们设计一个服务系统时,若整个系统较为复杂,可能需要将其设计为若干个子系统,由各子系统分管不同的功能模块。这样就可以避免因为一个子系统的故障,影响到其他子系统的正常工作。此外,对于同一个子系统,我们还需要根据其负载情况,设计分布式架构与负载均衡。
二、微服务架构的搭建方法
早期的微服务架构通常是通过反向代理来实现的,为不同的子系统分配不同的域名,为域名设置反向代理,从而实现负载均衡。
但是这种方法存在明显的弊端,最显而易见的就是过分依赖人工配置,在配置反向代理的时候,我们需要为每一台服务器配置一条记录,当大规模增加/移除设备时,人的工作量是很多,而且一旦出现失误,造成的损失更是十分巨大。
因此,现在的微服务架构都会使用一个注册中心,用于自动集中管理各服务。本文将以Nacos注册中心为例,使用Spring Cloud Alibaba带领大家搭建起一个简单的微服务架构。
三、前期准备
使用Spring Cloud Alibaba微服务架构,首先需要做以下几项准备工作:
- jdk1.8.0或以上版本;
- Maven3.6.0或以上版本;
注:为提高Maven下载速度,可在~/.m2/settings.xml
中添加阿里云中心仓库
<mirror>
<id>aliyun-central</id>
<mirrorOf>central</mirrorOf>
<name>阿里云中心仓库</name>
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
- nacos-server 1.3.2(建议下载最新稳定版,此处以1.3.2为例)
官方文档:https://nacos.io/zh-cn/
下载地址:https://github.com/alibaba/nacos/releases
注:Windows系统下载nacos-server-1.3.2.zip
,其他系统下载nacos-server-1.3.2.tar.gz
。
四、启动Nacos服务端
1、Windows系统
# 首先解压 nacos-server.zip
unzip nacos-server.zip
# 进入目录并启动
cd nacos\bin
cmd startup.cmd -m standalone # standalone代表着单机模式运行,非集群模式
2、其他操作系统(Linux/Unix/Mac)
# 首先解压 nacos-server.tar.gz
tar -xvf nacos-server.tar.gz
# 进入目录并启动
cd nacos/bin
sh startup.sh -m standalone # 或 bash startup.sh -m standalone
待启动完成后,可登录Dashboard进行查看,地址为:http://localhost:8848/nacos/#/
,初始用户名与密码均为nacos
。
五、应用服务项目子系统(Nacos客户端)
1、创建Maven项目,这里以多模块项目为例
这里选择Maven
创建完成后,仅需保留pom.xml
文件即可,其他文件(夹)均可删除。向pom.xml
中添加内容:
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/>
</parent>
<modules></modules>
2、添加模块account
这里同样选择Maven
2.1、向pom中添加Maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>0.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
接着Reload Maven Projects
即可。
2.2、编写启动类
@SpringBootApplication
public class AccountApplication {
public static void main(String[] args) {
SpringApplication.run(AccountApplication.class, args);
}
}
2.3、创建配置文件
#application.yml
server:
port: 9001
#bootstrap.yml
spring:
application:
name: account-service
cloud:
nacos:
server-addr: 192.168.1.100:8848
config:
server-addr: 192.168.1.100:8848
discovery:
server-addr: 192.168.1.100:8848
weight: 1
这里需要特别注意,spring.application.name项,这就是该模块注册服务的名字,在后续操作中都需要用到它。
2.4、添加控制器类
这里直接参照Spring boot的方法写就可以了,例如我这里的IndexController
@RestController
public class IndexController {
@GetMapping
public String hello(@RequestParam String name) {
String time = new Date().toString();
System.out.println(name + ": " + time);
return name + ": " + time;
}
}
这时打开浏览器访问 http://localhost:9001/?name=Sky
就可以看到
Sky: Sun Sep 27 19:00:00 CST 2020
说明这一模块本身已经启动正常了。
2.5、查看Nacos-Dashboard
打开http://localhost:8848/nacos/#/
,登录后选择服务管理
->服务列表
,就可以看到,我们的account-service已经成功注册到Nacos了。
这时我们还可以再修改application.yml
中的server.port
为9002,并再启动一个进程,可以在Dashboard中看到,两个实例都已经注册到Nacos了。
3、添加模块viewer
添加方法与上一步相同,此处略去。注:此处将端口设置为9011。
3.1、在viewer模块中调用account中的服务
刚刚已经通过浏览器成功访问过account的服务了,那么在另一个模块中需要如何访问它呢?
当然,我们同样是通过http协议进行访问的,于是我们就需要一个RestTemplate了。
在ViewerApplication中添加一个
@Bean
RestTemplate restTemplate() { return new RestTemplate(); }
在IndexController中添加
@Autowired
RestTemplate restTemplate;
@GetMapping
public String requestForAccountService() {
String response = restTemplate.getForObject("http://localhost:9001/?name=Sky", String.class);
return "viewer => " + response;
}
浏览器访问 http://localhost:9011/
就可以看到
viewer => Sky : Sun Sep 27 20:00:00 CST 2020
这说明这样的访问是可以通的。但是这可不是我们要的效果呀,我们希望能以服务名来访问并实现负载均衡,而非指定一个服务地址呀。
3.2、使用组件Ribbon实现负载均衡
为了解决上面的这个问题,我们引入了Ribbon组件。
我们对restTemplate进行修改,将ViewerApplication中的restTemplate修改为
@Bean
@LoadBalanced
RestTemplate restTemplate() { return new RestTemplate(); }
这样就可以将getForObject的参数修改为http://account-service/?name=Sky
,再次访问http://localhost:9011/
,得到与3.1得到相同结果。继续进行多次访问,我们查看一下两个AccountService进程的控制台,可以看到均有输出,且数量相当。
3.2、使用组件Feign简化内容
尽管上面的方法已经可以达到我们的要求了,但是用起来却不是很方便。那么有没有什么办法能够像调用函数一样来从其他服务获取数据呢?那就是Feign了。
首先,使用Feign需要在pom.xml
中添加一项依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
并在ViewApplication
上添加一条注解
@EnableFeignClients
接着就可以去写接口了,通常接口中的函数与对应服务中的函数相同即可。
@FeignClient("account-service")
public interface AccountService {
@GetMapping
String hello(@RequestParam String name);
}
这样我们就可以直接改为
@RestController
public class IndexController {
@Autowired
AccountService accountService;
@GetMapping
public String requestForAccountService() {
String response = accountService.hello("Sky");
return "viewer => " + response;
}
}
再次访问http://localhost:9011/
,依然能够获得相同结果。
写在最后
做到这些,一个基于Spring Cloud Alibaba的微服务系统就基本完成了,也就可以正式上线发布了。当然,这只是一个最基础的架构,还有着不少问题需要改进,例如分布式事务等等,以后有空再来更新啦。