乐优商城第二天(Springcloud上)

Today is 乐优商城的第二天,学习了系统架构的发展,微服务和springcloud的Eureka,每天学新东西都是多的,但是一想到我们的项目不是淘淘商城了,是一个全新的项目,就充满希望,想把这个全新的项目学好。

一.系统架构的演进

1.集中式架构,所有的模块都耦合在一起

2.垂直拆分,分家,各模块相对独立,但是每一个模块都直接和数据库打交道,产生了很多重复的工作

3.分布式服务,将系统的基础服务进行抽取,各个业务都可以调用这些服务,提高了代码服用和开发效率。但是缺乏统一管理,相当于每一个模块都是直接和那些基础服务打交道的。导致关系错综复杂。

4.SOA面向服务,有一个统一的注册中心来管理服务。

5.微服务,服务拆分的更小。

二.服务调用的方式

既然发展到微服务,那么服务之间是怎么通信的?

两种方式:1.RPC和Http

RPC远程服务调用,将请求序列化,通过网络传输,在接收端进行反序列化解析,拿到参数,执行方法,再将结果序列化返回

Http则通过http协议进行通信,信息封装在请求行,请求头,请求体中。

从三个方向比较

1.速度 RPC速度更快

2.难度 RPC更难实现,http简单

3.灵活度 http通过rest风格的链接相互调用,RPC需要更服务之间用相同的语言,因为传入的参数,方法可以接手。并且约定好序列化和反序列化的方式

我们追求自由,不喜欢收到多的限制,所以我们选择基于Http的Rest风格的服务。


三.JAVA代码如何处理HTTP请求?(Http客户端工具)

主流有三种:

  • HttpClient

  • OKHttp

  • URLConnection

客服端发送请求http请求,并接受响应。这里以HttpClient为例,看java代码中如何使用HttpClient发送http请求的

在使用HttpClient之前,需要先创建对象

CloseableHttpClient httpClient;

@Before
public void init() {
    httpClient = HttpClients.createDefault();
}

如果是get请求,发送请求request得到响应response

HttpGet request = new HttpGet("http://localhost:8081/list");
String response = this.httpClient.execute(request, new BasicResponseHandler());

就相当于用一个类进行发送请求和得到响应

这样会相当麻烦,因为不同的客户端需要编写不同的代码,而spring给我们提供了一个通用的模版,restTemplate,她是对常用客户端的一个简单封装,我们只要写通用的代码就可以实现发送请求和得到响应,用法如下:

首先将RestTemplate交给IOC管理,我们使用的是okHttp3ClientHttpRequestFactory

@Bean
public RestTemplate restTemplate() {
   return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}

创建完对象后,restTemplate可以直接发送请求,并将响应解析成对象

得到普通对象:

User user = this.restTemplate.getForObject("http://localhost:8081/hello", User.class);

得到集合:

List<User> list = restTemplate.getForObject("http://localhost:8081/list", List.class);


四.模拟服务提供者和消费者

  当我们可以在java中处理http请求后,我们就可以模拟一个服务提供者和消费者的案例。

消费者通过restTemplate发送http请求,得到响应。但是,这样会产生问题,

1.http请求都是被硬编码到消费者里面的,如果我们的请求地址发生变化,我们还得去java代码里面去更改。

2.不利于扩展,这样始终只有一个服务提供者为我服务,我想要一个集群为我服务,这个坏了,我还可以用里一个备用的,还可以达到负载均衡的目的。

因为有这样的需求,我们需要一个管理者。而这个管理者是springcloud给我们提供的eureka

五.注册中心Eureka

我们对消费者和服务进行改造,服务提供者去注册中心去注册服务,消费者去注册中心去拉取服务。这样,一切都清晰起来,消费者不需要知道服务的提供者是谁,只需要知道他需要什么服务。服务提供者也不需要知道他为谁服务,只需要把服务交个注册中心就行了。


那么,我们想实现这个逻辑,肯定先得有个注册中心,所以注册中心必须先准备好。

  • 搭建服务中心

注册中心是springcloud的一个组件,我们得引入springcloud,我们可以用idea的springInitializr快速构建一个springboot项目,并引入依赖,而如果只有一个注册中心,不安全,我们为了高可用性,一般会有多个注册中新,他们互相注册,让彼此知道对方的存在。这需要在配置文件中配置,具体配置如下:

server:
  port: 10086 # 端口
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中显示
eureka:
  client:
    service-url: # 配置其他Eureka服务的地址,而不是自己,比如10087
      defaultZone: http://127.0.0.1:10087/eureka

配置文件配置好之后,我们需要启动eureka服务,而spring并不知道我们这是一个eureka服务,于是我们需要告诉程序,我们是一个eureka服务,方式就是引入注解。

@SpringBootApplication
@EnableEurekaServer
在启动类上面再加一个@EnableEurekaServer注解

这样,注册中心就搭建完成了。

  • 搭建服务提供者

注册中心搭建完成后,显然下一个就是搭建服务,让服务去注册中心去注册,最后才是消费者去注册中心拉取服务,那么我们开始服务的搭建。步骤跟搭建注册中心差不多,1.写配置文件,2.启动的时候,告诉启动类,去注册中心注册自己

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/day0308
    username: root
    password: 123
    hikari:
      maximum-pool-size: 20
      minimum-idle: 10
  application:
    name: user-service # 应用名称

eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
  instance:
    lease-expiration-duration-in-seconds: 10 # 10秒即过期
    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳
    prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
    ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
mybatis:
  type-aliases-package: com.cloud.pojo

主要配置了服务的id,注册中心的地址,还有心跳。

@SpringBootApplication
@MapperScan("com.cloud.mapper")
@EnableDiscoveryClient

启动类添加的注解

  • 搭建消费者

消费者其实也相当于一个服务,也需要去注册中心注册,所以他的搭建方法跟服务提供者差不多

server:
  port: 8082
spring:
  application:
    name: consumer 
eureka:
  client:
    service-url: 
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true 
    ip-address: 127.0.0.1 
@SpringBootApplication
@EnableDiscoveryClient

消费者去注册中心获取服务的方法

@Autowired
private DiscoveryClient discoveryClient;

public User queryUserById(Long id) {
    List<ServiceInstance> instances = discoveryClient.getInstances("USER-SERVICE");
    ServiceInstance serviceInstance = instances.get(0);
通过instances可以获得instances,通过instances可以获得端口号和ip,这样就能拼接得到url,这就是eurake的作用。当然后面的负载均衡我们连instance都不需要了,直接写服务地址就好了。


其他:


1.在不用restTemlate的情况下,怎么将json转化为对象?

首先注入mapper对象

ObjectMapper mapper;
@Before
public void init() {
    mapper = new ObjectMapper();
}

对于普通java类

 User users = mapper.readValue(response,User.class);

对集合

List<User> users = mapper.readValue(response, mapper.getTypeFactory().constructCollectionType(List.class, User.class));
另外,对mapper类我们采取了封装工具类,在我的工具类中可以找到

2.ymal语法

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb01
    username: root
    password: 123
    hikari:
      maximum-pool-size: 20
      minimum-idle: 10
mybatis:

  type-aliases-package: com.leyou.userservice.pojo

代替properties文件

3.JDK8新特性lambda表达式  

ids.forEach(id -> {
    // 我们测试多次查询,
    users.add(this.restTemplate.getForObject(url + id, User.class));
    // 每次间隔500毫秒
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

对ids这个集合进行forech遍历,这样就代替了foreach

下面连接是大佬对lambda的理解:

https://blog.csdn.net/ioriogami/article/details/12782141/


猜你喜欢

转载自blog.csdn.net/qpc672456416/article/details/80392311