目录
5.4.1 编辑consumer的启动类,直接调用provider--非负载均衡
5.4.3 负载均衡客户端对象--方式2(重点)--策略模式
一 创建空工程
在idea中创建一个空工程就相当于创建了一个空的目录,可以将这个目录看成是一个工作区.例如:
把2104改为2105,文件保存地址改为:E:\CGB2105\GitCGB2105IVProjects
二 基础环境配置
结合微服务架构及解决方案进行配置
1 JDK版本--8
2 Maven配置
推荐使用3.6以上版本,例如:
本地仓库要重新创建一个文件地址,不要和四阶段的地址冲突,以后每次创建一个项目都要重新配置maven,尤其本地仓库要在不同文件目录下
配置maven时地址不能包含中文!!!!!
3 File Encoding
一般创建完一个工程都要去设置一下工程编码,一个团队中使用的工程编码应该是一致.
三 创建一个maven工程
1 创建maven工程
name:01-sca Groupid:com.jt
2 编辑pom.xml文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--项目坐标(groupId->01-sca)-->
<groupId>com.jt</groupId>
<artifactId>01-sca</artifactId>
<version>1.0-SNAPSHOT</version>
<!--依赖版本可统一写在properties标签内,
工程中默认定义的或自己定义的属性,例如一些版本信息下面引用即可 -->
<properties>
<spring.boot.version>2.3.2.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR9</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.6.RELEASE</spring.cloud.alibaba.version>
</properties>
<!--dependencyManagement元素用于定义核心依赖的管理,例如依赖的版本-->
<dependencyManagement>
<dependencies>
<!--Spring boot 依赖(微服务基础)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<!--这里的import表示在其它工程中需要时直接可以引用-->
<scope>import</scope>
<!--假如依赖的scope属性值为scope,则type类型必须为pom-->
<type>pom</type>
</dependency>
<!--Spring Cloud 依赖(定义了微服务规范)-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<!--Spring Cloud Alibaba依赖(基于spring微服务规范做了具体落地实现)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
</project>
3 Nacos解压,登录数据库
nacos称为服务注册中心
nacos解压后的目录:
第一步: 找到MariaDB下的bin目录,搜索栏输入cmd进入DOS命令窗口,登录数据库mysql -u root -p 及密码root
注意:若数据库连接失败,按下图指示调试,重新登录数据库
第二步:找到nacos包下有conf文件目录,修改nacos-mysql.sql文件(是个数据库sql语句,执行创建),然后输入source+sql文件路径启动
注意:在执行此文件时,要求mysql的版本大于5.7版本(MariaDB最好10.5.11),否则会出现如下错误:
第三步:查看数据库下的表,正确的话输入exit退出
第四步:打开/nacos/conf/application.properties里打开默认配置,并基于你当前环境配置要连接的数据库,连接数据库时使用的用户名和密码(假如前面有"#"要将其去掉):
四 nacos服务启动与访问
第一步:启动Nacos服务。
Linux/Unix/Mac启动命令(standalone代表着单机模式运行,非集群模式):
./startup.sh -m standalone
Windows启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
执行步骤: nacos的bin目录栏cmd 后输入命令 startup.cmd -m standalone
注意:如果出问题,就找Caused by,没有caused by就是没问题
错误说明:
- 执行执行令时要么配置环境变量,要么直接在nacos/bin目录下去执行.
- nacos启动时需要本地环境变量中配置了JAVA_HOME(对应jdk的安装目录),
- 一定要确保你连接的数据库(nacos_config)是存在的.
- 假如所有的配置都正确,还有检查一下你有几个数据库(mysql,…)
第二步:访问Nacos服务。第一步先执行启动命令才能进入登录网页,dos命令窗口关闭,nacos网页就连接失败了
打开浏览器,输入http://localhost:8848/nacos地址,出现如下登陆页面:
其中,默认账号密码为nacos/nacos.
五 服务注册与调用入门(重点)
5.1 业务描述
创建两个项目Module分别为服务提供者和服务消费者,两者都要注册到NacosServer中(这个server本质上就是一个web服务,端口默认为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如支付服务为服务提供方,订单服务为服务消费方),如图所示:
5.2 服务提供方的创建及注册
第一步:创建服务提供者工程(module名为sca-provider),继承parent工程(01-sca),其pom.xml文件内容如下:在01-sca工程创建子过程maven,名为sca-provider
子工程sca-provider中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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>01--sca</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sca-provider</artifactId>
<dependencies>
<!--spring web 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring cloud alibaba discovery 服务的注册和发现(我们要将服务注册到nacos)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
此时,可以看父工程的pom文件的变化:如果删除子过程,这两个标签的内容也要删除
第二步:创建子工程的启动类:sca-provider
package com.jt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}
第三步:resources创建配置文件application.yml,实现服务注册,关键代码如下:
注意:服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则。
server:
port: 8081
spring:
application:
name: sca-provider #服务名
cloud:
nacos:
discovery:
server-addr: localhost:8848 #服务注册和发现(默认是注册到这个地址)可写可不写
第四步:启动主启动类,如果报错,清空缓存重启IDEA再启动启动类
第五步:前提时nacos的bin目录下的DOS窗口启动指令还执行着,才能刷新网页成功,如果dos命令窗口关闭,网页就连接失败
如果成功启动后,可查看nacos网页会新增sca-provider,检测是否服务注册成功,如图所示:
第六步 :
1) 在子工程启动类进行数据的获取端口号和一串字符串编辑:
package com.jt;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
/*定义Controller对象(这个对象在spring MVC中给它定义是handler)
* 基于此对象处理客户端的请求*/
@RestController
public class ProviderController{
/*获取端口号
* @Value默认读取项目而皮质文件中的配置内容*/
@Value("${server.port:8080}")
private String server;
//http://localhost:8081/privoder/echo/tedu
@GetMapping("/provider/echo/{msg}")
public String doRestEcho1(@PathVariable String msg){
return server+"say hello"+msg;
}
}
}
2) 前提是启动类和nacos都要启动,
启动后,输入:http://localhost:8081/provider/echo/tedu ,即可在网页看到数据的回显
注意:如果启动报错如下,说明nacos没有启动(nacos的bin目录栏cmd,dos窗口启动nacos命令startup.cmd -m standalone)
5.3 服务消费方的发现及调用
第一步:01-sca创建子过程sca-consumer
第二步:继承parent工程(01-sca),编辑sca-consumer的pom.xml文件添加依赖内容如下:
pom.xml文件错误时的修改方法:如下
pom.xml文件编辑
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>01--sca</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sca-consumer</artifactId>
<dependencies>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--微服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
第三步:resouces添加并编辑application.yml
server:
port: 8090
spring:
application:
name: sca-consumer #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #服务地址
第四步:创建com.jt包编辑启动类ConsumerApplication
package com.jt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
第五步:数据的回显,继续在启动类编辑控制层的请求
这里可以不使用:
package com.jt;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
@RestController
public class ConsumerController{
@Value("${server.port:8080}")//8080不会显示在网页,只是空值,可以不写
//显示的还是配置文件设置的端口号8090
private String server;
//http://localhost:8090/consumer/rest/sca
@GetMapping("/consumer/echo/{msg}")
public String doRestEcho1(@PathVariable String msg){
return server + "say hello Consumer" +msg;
}
}
}
第六步:启动nacos,启动consumer的启动类,url查看网页获取的数据
5.4 实现服务消费方对服务提供方的调用
5.4.1 编辑consumer的启动类,直接调用provider--非负载均衡
package com.jt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication//底层是配置类的启动类
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
/*创建RestTemplate对象,然后基于此对象进行远程服务调用*/
@Bean//表示把对象交给spring池管理,该注解需要写在配置类中
public RestTemplate restTemplate(){
return new RestTemplate();
}
@RestController
public class ConsumerController{
@Autowired
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String appName;
//http://localhost:8090/consumer/echo/sca
@GetMapping("/consumer/echo/{msg}")
public String doRestEcho1(@PathVariable String msg){
//访问谁,调用谁?sca-provider中的url,弊端是url地址固定,无法负载均衡
String url = "http://localhost:8081/provider/echo/"+msg;
//如何调用?
String result = restTemplate.getForObject(url,String.class);
return appName + "get result is" + result;
}
}
}
依次启动provider启动类和consumer启动类,网页输入consumer的url,查看调用结果
网页效果:
5.4.2 手动添加负载均衡 ---方式1
第一步:provider修改端口号8081/8082,启动两个服务提供方的服务器
需要一个设置,并发启动:
启动成功以后,访问nacos的服务列表,检测服务是否成功注册,如图所示:
第二步:编辑consumer类
package com.jt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Random;
@SpringBootApplication//底层是配置类的启动类
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
/*创建RestTemplate对象,然后基于此对象进行远程服务调用*/
@Bean//表示把对象交给spring池管理,该注解需要写在配置类中
public RestTemplate restTemplate(){
return new RestTemplate();
}
@RestController
public class ConsumerController{
@Autowired
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String appName;
//http://localhost:8090/consumer/doRestEcho1
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho1(){
//手动自己写负载均衡算法(随机调用服务列表中的服务对象)
//访问谁,调用谁?sca-provider中的url,弊端是url地址固定,无法负载均衡
String url1 = "http://localhost:8081/provider/echo/"+appName;
String url2 = "http://localhost:8082/provider/echo/"+appName;
String urls[] =new String[]{url1,url2};
int n = new Random().nextInt(2);
System.out.println("n="+n);
//如何调用?
return restTemplate.getForObject(urls[n],String.class);
//return appName + ": get result is : " + result;
}
}
}
第三步:并发启动两个provider'服务器和consumer服务器,页面输入consumer服务器的url,刷新页面查看负载均衡的效果:
弊端:需要创建好多服务器提供方的url地址,太固定了
5.4.3 负载均衡客户端对象--方式2(重点)
1) LoadBalancerClient: 负载均衡客户端对象,基于此对象,可以从nacos中获取服务列表,
并且可以基于一定的算法,从列表中获取一个服务实例.
2) 这里多个实例并发提供服务的方式为负载均衡,这里的负载均衡实现默认是因为Nacos集成了Ribbon来实现的,Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息),这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用。
第一步:同样也是并发启动不同端口号的provider服务器,修改一个端口号启动一次.
第二步:编辑consumer启动类.
package com.jt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication//底层是配置类的启动类
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
/*创建RestTemplate对象,然后基于此对象进行远程服务调用*/
@Bean//表示把对象交给spring池管理,该注解需要写在配置类中
public RestTemplate restTemplate(){
return new RestTemplate();
}
@RestController
public class ConsumerController{
@Autowired
private RestTemplate restTemplate;
//负载均衡客户端对象,基于此对象,可以从nacos中获取服务列表,
//并且可以基于一定的算法,从列表中获取一个服务实例
@Autowired
private LoadBalancerClient loadBalancerClient;
@Value("${spring.application.name}")
private String appName;
//http://localhost:8090/consumer/doRestEcho1
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho1(){
//基于LoadBalancerClient方式获取服务实例
String serviceId = "sca-provider";//id要在nacos的服务列表中才可以
ServiceInstance choose = loadBalancerClient.choose(serviceId);
String ip = choose.getHost();//ip地址
int port = choose.getPort();//端口号
//String url = "http://" + ip + ":" + "/provider/echo/" + appName;
//%s 仅代表占位符的意思,后面的ip,port依次赋给占位符
String url =String.format("http://%s:%s/provider/echo/%s",ip,port,appName);
return restTemplate.getForObject(url,String.class);
}
}
}
第三步:最后启动consumer启动类,输入url,查看页面效果:也可以实现负载均衡的效果
5.4.4 负载均衡的拦截器--@LoadBalanced
和负载均衡客户端对象相比:代码量变少了,但是有拦截肯定消耗了时间效率
@LoadBalanced注解是属于Spring,而不是Ribbon的,Spring在初始化容器的时候,如果检测到Bean被@LoadBalanced注解,Spring会为其设置LoadBalancerInterceptor的拦截器。
步骤和前面一样,分别启动不同端口号的provider服务器,编辑consumer启动类:主要多了三步
package com.jt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication//底层是配置类的启动类
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
/*创建RestTemplate对象,然后基于此对象进行远程服务调用*/
@Bean//表示把对象交给spring池管理,该注解需要写在配置类中
public RestTemplate restTemplate(){
return new RestTemplate();
}
//第一步:
@Bean//表示把对象交给spring池管理,该注解需要写在配置类中
@LoadBalanced//描述RestTemplated对象,底层会对RestTemplate对象的请求进行拦截
public RestTemplate loadBalanceRestTemplate(){
return new RestTemplate();
}
@RestController
public class ConsumerController{
@Autowired
private RestTemplate restTemplate;
/*@Autowired注解描述属性时,会告诉spring框架,
* 要优先按照属性类型进行对象的查找和注入,
* 假如此此类型的对象存在多个,此时还会按照属性名进行查找和对比,
* 有相同的则直接注入(DI),没有相同的则报错,当然也可以在属性上添加
* @Qualifier("bean的名字")注解,指定要注入的具体对象*/
///第二步:
@Autowired
private RestTemplate loadBalanceRestTemplate;
//负载均衡客户端对象,基于此对象,可以从nacos中获取服务列表,
//并且可以基于一定的算法,从列表中获取一个服务实例
@Autowired
private LoadBalancerClient loadBalancerClient;
@Value("${spring.application.name}")
private String appName;
//http://localhost:8090/consumer/doRestEcho1
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho1(){
//第三步:
String serviceId = "sca-provider";
String url =String.format("http://%s/provider/echo/%s",serviceId,appName);
return loadBalanceRestTemplate.getForObject(url,String.class);
}
}
}
最后启动consumer启动类,输入url,查看页面效果:也可以实现负载均衡的效果
5.4.4 对@Bean的分析
5.5 Ribbon负载均衡策略(了解)
基于Ribbon方式的负载均衡,Netflix默认提供了七种负载均衡策略,对于SpringCloud Alibaba解决方案中又提供了NacosRule策略,默认的负载均衡策略是轮训策略。如图所示:
当系统提供的负载均衡策略不能满足我们需求时,我们还可以基于IRule接口自己定义策略.
ctrl+n 查找接口IRule,ctrl + h 查找接口的所有类
5.6 服务注册于调用的面试点
- 为什么要将服务注册到nacos?(为了更好的查找这些服务)
- 在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳)
- 对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)
- 服务启动时如何找到服务启动注册配置类?(NacosNamingService)
- 服务消费方是如何调用服务提供方的服务的?(RestTemplate)
- @Bean注解的作用?(一般用于配置类内部,描述相关方法,用于告诉spring此方法的返回值要交给spring管理,bean的名字默认为方法名,假如需要指定名字可以@Bean(“bean的名字”),最多的应用场景是整合第三方的资源-对象)
- @Autowired注解的作用?(此注解用于描述属性,构造方法,set方法等,用于告诉spring框架,按找一定的规则为属性进行DI操作,默认按属性,方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字也不同,就出报错.)
- Nacos中的负责均衡底层是如何实现的?(通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务)
- Ribbon 是什么?(Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法)
- Ribbon 可以解决什么问题? (基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口)
- Ribbon 内置的负载策略都有哪些?(8种,可以通过查看IRule接口的实现类进行分析)
- @LoadBalanced的作用是什么?(描述RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略。)
- 我们可以自己定义负载均衡策略吗?(可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现)