热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

微服务简介_服务简介

微服务简介Whileadmittedlytheconceptcomesfromtheempiricalfieldratherthanacademic,itappliesquitew

微服务简介

While admittedly the concept comes from the empirical field rather than academic, it applies quite well to the pragmatism of the real world – most software applications behave pretty much like whimsical babies with an unavoidably “tendency” to grow in size and complexity over time. There’s nothing inherently wrong with having babies growing up healthy here and there, but the situation can be radically different with applications, especially when they become bloated, twisted monsters having little to do with the sweet, tiny creatures they once were.

尽管该概念来自经验领域而不是学术领域,但它非常适用于现实世界的实用主义–大多数软件应用程序的行为都非常像异想天开的婴儿,随着时间的流逝,其大小和复杂性不可避免地会“趋向于增长”。 婴儿在这里和那里长大,并没有天生的错误,但是情况因应用而异,特别是当它们变得肿,扭曲的怪物与他们曾经的甜美,微小的动物无关时。

The problem isn’t growth per se, as having a future-proof application spreading its wings wide toward further horizons can be a sign of good design. The real issue is when the expansion process is achieved at the expense of redundant boilerplate implementations of things scattered throughout different layers. We’ve all been there (mea culpa) and we all know that logic duplication is a serious software disease.

问题并不是本质上的增长,因为具有面向未来的应用程序将其翅膀扩展到更广阔的视野可能是良好设计的标志。 真正的问题是,在实现扩展过程时会以冗余的样板实现方式为代价,这些实现对象分散在不同的层中。 我们都去过那里( mea culpa ),我们都知道逻辑复制是一种严重的软件疾病。

Even when well-trusted programming techniques help strip out duplicated logic, sometimes they’re just not enough to fix the issue by themselves. A clear example of this is MVC; the model (the Domain Model, not the obtrusive, catch-all database one) does its business in relaxed isolation, then the skinny controllers grab the model’s data through a mapper or repository pass it on to the view or any other output handler for further rendering. The scheme may deliver, but it doesn’t scale well.

即使值得信赖的编程技术可以帮助消除重复的逻辑,但有时仅靠它们本身不足以解决问题。 MVC就是一个明显的例子。 该模型( 域模型 ,而不是笨拙的,通用的数据库)以轻松的隔离方式开展业务,然后瘦控制器通过映射器或存储库获取模型的数据,并将其传递给视图或任何其他输出处理程序以进一步处理渲染。 该方案可以实现,但扩展性不佳。

Say the model data should be massaged in some additional manner and interfaced to an external API such as Facebook, Twitter, a third-party mailer, you name it, at multiple places. In such cases, the whole massaging/interfacing process is application logic from top to bottom, hence the controllers’ responsibility. Before you know it, you’re forced to duplicate the same logic across a bunch of controllers, thus putting your toes on the forbidden terrain of logic duplication. Busted!

说应该以其他方式处理模型数据, 并在多个位置连接到外部API,例如Facebook,Twitter,第三方邮件程序(您命名)。 在这种情况下,整个按摩/接口过程都是自上而下的应用逻辑,因此由控制器负责。 在不知不觉中,您被迫跨一堆控制器复制相同的逻辑,从而将自己的脚趾放在禁止的逻辑复制领域。 ted!

If you’re like me, you’re probably wondering how to tackle the problem without hitting your head against a brick wall. In fact, there’s a few approaches that do the job quite well. There’s one in particular I find appealing because it plays nicely with Domain Models, and therefore with Domain-Driven Design. And what’s more, if you’ve peeked at the article’s title, you’ve probably guessed I’m referring to Services.

如果您像我一样,您可能想知道如何在不撞墙的情况下解决问题。 实际上,有几种方法可以很好地完成这项工作。 我特别喜欢一种方法,因为它与Domain Models(以及Domain-Driven Design)配合得很好。 而且,如果您浏览了这篇文章的标题,您可能已经猜到我指的是服务。

什么是服务? (What is a Service?)

A services is an abstraction layer placed on top of the domain model which encapsulates common application logic behind a single API so that it can be easily consumed by different client layers.

服务是放置在域模型之上的抽象层,它在单个API的后面封装了通用的应用程序逻辑,因此可以被不同的客户端层轻松使用。

Don’t let the definition freak you out, as if you’ve been using MVC for a while the chances are you’ve used a service already. Controllers are often called services, as they carry out application logic and additionally are capable of interfacing to several client layers, namely views. Of course in a more demanding environment, plain controllers fail short in handling several clients without the aforementioned duplicating, so that’s why the construction of a standalone layer is more suitable in such cases.

不要让定义吓到您,就好像您已经使用MVC已有一段时间了一样。 控制器通常称为服务,因为它们执行应用程序逻辑,并且还能够与多个客户端层(即视图)接口。 当然,在要求更高的环境中,普通控制器在没有进行上述重复的情况下无法处理多个客户端,因此这就是为什么在这种情况下构建独立层的原因。

创建一个简单的域模型 (Creating a Simplistic Domain Model )

Services are real killers when working with enterprise-level applications whose backbone rests on the pillars of a rich Domain Model and where the interplay with multiple clients is the rule rather than the exception. This doesn’t mean that you just can’t use a service for your next pet blog project, because in fact you can, and most likely nobody will punish you for such an epic effort.

在使用企业级应用程序时,服务是真正的杀手ers,而企业级应用程序的主干基于丰富的域模型,并且与多个客户端的交互是规则而不是例外。 这并不意味着您不能将服务用于您的下一个宠物博客项目,因为实际上您可以,而且极有可能没人会因为如此史诗般的努力而惩罚您。

I have to admit my own words will come back to haunt me sooner or later, though, as my “naughty” plan here is to create a service from scratch which will interface a domain model’s data, composed of a few user objects, to two independent client layers. Sounds overkill, sure, but hopefully didactic in the end.

我必须承认自己的话迟早会困扰我,因为我这里的“顽皮”计划是从头开始创建服务,该服务将域模型的数据(由几个用户对象组成)连接到两个独立的客户端层。 听起来有些矫kill过正,但最终还是希望能有所作为。

The following diagram shows in a nutshell how this experimental service will function at its most basic level:

下图简要说明了该实验服务将如何在其最基本的级别上运行:

Service Diagram

The service’s behavior is rather trivial. Its responsibility can be boiled down to just pulling in data from the domain model, which will be then JSON-encoded/serialized, and exposed to a couple of clients (Client Layer A and Client Layer B). The beauty of this scheme is that the entire encoding/serialization logic will live and breath behind the API of a service class placed on top of the model, thus shielding the application from redundant implementation issues while still leaving the door open to plugging in additional clients further down the road.

该服务的行为相当琐碎。 它的职责可以归结为仅从域模型中提取数据,然后将其进行JSON编码/序列化,并暴露给几个客户端(客户端层A和客户端层B)。 该方案的优点在于,整个编码/序列化逻辑将在位于模型顶部的服务类的API后面起作用,从而使应用程序免受冗余实现问题的困扰,同时仍然为插入其他客户端提供了方便再往前走。

Assuming that building the service will flow from bottom to top, the first tier to be built is the the Data Access Layer (DAL). And since the tasks bounded to the infrastructure will be limited to storing /fetching model data from the database, this layer will look pretty much the same as the one I wrote in a previous article.

假设构建服务将自下而上,则要构建的第一层是数据访问层(DAL)。 并且由于与基础架构相关的任务将仅限于从数据库存储/获取模型数据,因此这一层看起来与我在上一篇文章中写的几乎相同。

With the DAL doing its thing, let’s go one step up and start distilling the domain model. This one will be rather primitive, tasked with modeling generic users:

随着DAL的完成,让我们迈出第一步,开始提炼领域模型。 这将是相当原始的,其任务是为通用用户建模:

namespace Model;
abstract class AbstractEntity
{
public function __set($field, $value) {
if (!property_exists($this, $field)) {
throw new InvalidArgumentException(
"Setting the field '$field' is not valid for this entity.");
}
$mutator = "set" . ucfirst(strtolower($field));
method_exists($this, $mutator) &&
is_callable(array($this, $mutator))
? $this->$mutator($value) : $this->$field = $value;
return $this;
}
public function __get($field) {
if (!property_exists($this, $field)) {
throw new InvalidArgumentException(
"Getting the field '$field' is not valid for this entity.");
}
$accessor = "get" . ucfirst(strtolower($field));
return method_exists($this, $accessor) &&
is_callable(array($this, $accessor))
? $this->$accessor() : $this->$field;
}
public function toArray() {
return get_object_vars($this);
}
}

namespace Model;
interface UserInterface
{
public function setId($id);
public function getId();
public function setName($name);
public function getName();
public function setEmail($email);
public function getEmail();
public function setRanking($ranking);
public function getRanking();
}

namespace Model;
class User extends AbstractEntity implements UserInterface
{
const LOW_POSTER = "low";
const MEDIUM_POSTER = "medium";
const TOP_POSTER = "high";
protected $id;
protected $name;
protected $email;
protected $ranking;
public function __construct($name, $email, $ranking = self::LOW_POSTER) {
$this->setName($name);
$this->setEmail($email);
$this->setRanking($ranking);
}
public function setId($id) {
if ($this->id !== null) {
throw new BadMethodCallException(
"The ID for this user has been set already.");
}
if (!is_int($id) || $id <1) {
throw new InvalidArgumentException(
"The user ID is invalid.");
}
$this->id &#61; $id;
return $this;
}
public function getId() {
return $this->id;
}
public function setName($name) {
if (strlen($name) <2 || strlen($name) > 30) {
throw new InvalidArgumentException(
"The user name is invalid.");
}
$this->name &#61; htmlspecialchars(trim($name), ENT_QUOTES);
return $this;
}
public function getName() {
return $this->name;
}
public function setEmail($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException(
"The user email is invalid.");
}
$this->email &#61; $email;
return $this;
}
public function getEmail() {
return $this->email;
}
public function setRanking($ranking) {
switch ($ranking) {
case self::LOW_POSTER:
case self::MEDIUM_POSTER:
case self::TOP_POSTER:
$this->ranking &#61; $ranking;
break;
default:
throw new InvalidArgumentException(
"The post ranking &#39;$ranking&#39; is invalid.");
}
return $this;
}
public function getRanking() {
return $this->ranking;
}
}

Asides from doing some typical setter/getter mapping, and performing basic validation on the data, the behavior of the AbstractEntity and User classes don’t go any further than that. Regardless, they come in handy for bringing up to life a small, yet clean, domain model which can be tweaked at will.

除了执行一些典型的setter / getter映射以及对数据执行基本验证之外&#xff0c; AbstractEntityUser类的行为没有比这更进一步的了。 无论如何&#xff0c;它们都有助于将一个小型但干净的域模型变为现实&#xff0c;该模型可以随意调整。

The model should be hooked up to the DAL in some fashion without losing the pristine independence they have from each other. One straight and simple manner to accomplish this is through the facilities of a data mapper.

该模型应该以某种方式连接到DAL&#xff0c;而又不会失去它们彼此之间的原始独立性。 一种简单而直接的方法是通过数据映射器的工具。

将模型与DAL接口 (Interfacing the Model with the DAL)

If you’ve ever tackled the process, you’ll know that building a full-stack data mapper capable of handling multiple domain objects and catching any impedance mismatches on the fly is not a simple task, and in many cases is an effective repellent for even the boldest of coders. But since the domain model here is rather simple, the user mapper I plan to deploy here is pretty straightforward.

如果您已经解决了该过程&#xff0c;就会知道构建一个能够处理多个域对象并即时捕获任何阻抗不匹配的全栈数据映射器并不是一件容易的事&#xff0c;并且在许多情况下是一种有效的驱避剂即使是最勇敢的编码人员 但是由于这里的域模型非常简单&#xff0c;因此我计划在此处部署的用户映射器非常简单。

namespace ModelMapper;
use ModelUserInterface;
interface UserMapperInterface
{
public function fetchById($id);
public function fetchAll(array $conditions &#61; array());
public function insert(UserInterface $user);
public function delete($id);
}

namespace ModelMapper;
use LibraryDatabaseDatabaseAdapterInterface,
ModelUserInterface,
ModelUser;
class UserMapper implements UserMapperInterface
{
protected $entityTable &#61; "users";
public function __construct(DatabaseAdapterInterface $adapter) {
$this->adapter &#61; $adapter;
}
public function fetchById($id) {
$this->adapter->select($this->entityTable,
array("id" &#61;> $id));
if (!$row &#61; $this->adapter->fetch()) {
return null;
}
return $this->createUser($row);
}
public function fetchAll(array $conditions &#61; array()) {
$users &#61; array();
$this->adapter->select($this->entityTable, $conditions);
$rows &#61; $this->adapter->fetchAll();
if ($rows) {
foreach ($rows as $row) {
$users[] &#61; $this->createUser($row);
}
}
return $users;
}
public function insert(UserInterface $user) {
$user->id &#61; $this->adapter->insert($this->entityTable, array(
"name" &#61;> $user->name,
"email" &#61;> $user->email,
"ranking" &#61;> $user->ranking));
return $user->id;
}
public function delete($id) {
if ($id instanceof UserInterface) {
$id &#61; $id->id;
}
return $this->adapter->delete($this->entityTable,
array("id &#61; $id"));
}
protected function createUser(array $row) {
$user &#61; new User($row["name"], $row["email"],
$row["ranking"]);
$user->id &#61; $row["id"];
return $user;
}
}

Sure the UserMapper class is miles away for being a production-ready component, but it performs decently. In short, it runs a few CRUD operations on the domain model and reconstitutes User objects via its createUser() method. (I’ve left user updates as an exercise to the reader, so be ready for a pinch of extra fun).

确保UserMapper类是准备投入生产的组件&#xff0c;但它的表现不错。 简而言之&#xff0c;它在域模型上运行一些CRUD操作&#xff0c;并通过其createUser()方法重构User对象。 (我将用户更新作为练习留给读者&#xff0c;因此请准备一点额外的乐趣)。

Moreover, with the mapper comfortably resting between the model and DAL, the implementation of a service that outputs JSON-encoded data to the outer world should now become a more malleable process. As usual, concrete code samples are hard to beat when it comes to further elaborating this concept. So, let’s now build up the service in a few steps.

而且&#xff0c;由于映射器可以轻松地位于模型和DAL之间&#xff0c;因此将JSON编码的数据输出到外部世界的服务的实现现在应该变得更具延展性。 像往常一样&#xff0c;在进一步阐述此概念时&#xff0c;很难击败具体的代码示例。 因此&#xff0c;现在让我们分几步来构建服务。

建立可插拔服务层 (Building a Pluggable Service Layer)

There’s a general consensus, which goes along the lines of Fowler‘s and Evan‘s thoughts that services should be thin containers wrapping only application logic. Business logic, on the other hand, should be shifted to inside the boundaries of the domain model. And as I like to stick to the clever suggestions that come from the higher-ups, the service here will adhere to these.

有一个普遍的共识&#xff0c;这符合Fowler和Evan的思想&#xff0c;即服务应该是仅包含应用程序逻辑的瘦容器。 另一方面&#xff0c;业务逻辑应转移到域模型的边界内。 当我喜欢坚持上级提出的聪明建议时&#xff0c;这里的服务将坚持这些建议。

Having said that, it’s time to create the service layer’s first element. This one is a basic interface which will enable us to inject different encoder/serializer strategies into the service’s internals at runtime without amending a single line of client code:

话虽如此&#xff0c;是时候创建服务层的第一个元素了。 这是一个基本接口&#xff0c;使我们能够在运行时将不同的编码器/序列化器策略注入服务的内部&#xff0c;而无需修改客户端代码的任何一行&#xff1a;

namespace Service;
interface EncoderInterface
{
public function encode();
}

With the above interface in place, spawning a few implementers is really simple. Moreover, the following JSON wrapper proves why my claim is true:

有了上面的接口&#xff0c;产生一些实现者真的很简单。 此外&#xff0c;以下JSON包装器证明了我的主张正确的原因&#xff1a;

namespace Service;
class JsonEncoder implements EncoderInterface
{
protected $data &#61; array();
public function setData(array $data) {
foreach ($data as $key &#61;> $value) {
if (is_object($value)) {
$array &#61; array();
$reflect &#61; new ReflectionObject($value);
foreach ($reflect->getProperties() as $prop) {
$prop->setAccessible(true);
$array[$prop->getName()] &#61;
$prop->getValue($value);
}
$data[$key] &#61; $array;
}
}
$this->data &#61; $data;
return $this;
}
public function encode() {
return array_map("json_encode", $this->data);
}
}

If you found easy to grasp how the JsonEncoder does its thing, make sure to check the one below which sets a naive PHP serializer:

如果您发现容易掌握JsonEncoder工作方式&#xff0c;请确保检查以下设置了朴素PHP序列化程序的代码&#xff1a;

namespace Service;
class Serializer implements EncoderInterface
{
protected $data &#61; array();
public function setData(array $data) {
$this->data &#61; $data;
return $this;
}
public function encode() {
return array_map("serialize", $this->data);
}
}

Because of the functionality provided by the encoder and the serializer out the box, the implementation of a service that JSON-encodes and serializes model data can be now embraced with confidence. Here’s how this service looks:

由于编码器和序列化器提供了开箱即用的功能&#xff0c;因此现在可以放心地接受对JSON编码和序列化模型数据的服务的实现。 这项服务的外观如下&#xff1a;

namespace Service;
use ModelMapperUserMapperInterface;
class UserService
{
protected $userMapper;
protected $encoder;
public function __construct(UserMapperInterface $userMapper, EncoderInterface $encoder &#61; null) {
$this->userMapper &#61; $userMapper;
$this->encoder &#61; $encoder;
}
public function setEncoder(EncoderInterface $encoder) {
$this->encoder &#61; $encoder;
return $this;
}
public function getEncoder() {
if ($this->encoder &#61;&#61;&#61; null) {
throw new RuntimeException(
"There is not an encoder to use.");
}
return $this->encoder;
}
public function fetchById($id) {
return $this->userMapper->fetchById($id);
}
public function fetchAll(array $conditions &#61; array()) {
return $this->userMapper->fetchAll($conditions);
}
public function fetchByIdEncoded($id) {
$user &#61; $this->fetchById($id);
return $this->getEncoder()->setData(array($user))->encode();
}
public function fetchAllEncoded(array $conditions &#61; array()) {
$users &#61; $this->fetchAll($conditions);
return $this->getEncoder()->setData($users)->encode($users);
}
}

At a glance, it seems the UserService class is irreverently sitting on the user mapper with the sole purpose of eating up its finders or acting like a plain repository. But in fact it does a lot more than that, as its fetchByIdEncoded() and fetchAllEncoded() methods encapsulate in one place all the logic necessary for encoding/serializing model data according to the encoder passed in to the constructor or setter.

乍一看&#xff0c; UserService类似乎毫不客气地坐在用户映射器上&#xff0c;其唯一目的是吃掉它的查找器或充当简单的存储库。 但是实际上&#xff0c;它的作用远不止fetchByIdEncoded() &#xff0c;因为它的fetchByIdEncoded()fetchAllEncoded()方法根据传递给构造函数或setter的编码器将对模型数据进行编码/序列化所需的所有逻辑封装在一个位置。

While the class’ functionality is limited, it shows in a nutshell how to build a service layer that mediates between the model and a couple of clients which expect to get in data in a specific format. Of course I’d be a jerk if I didn’t show you how to finally get things rolling with such a service, So, the example below uses it for fetching users from the database:

尽管该类的功能受到限制&#xff0c;但它简要地显示了如何构建一个服务层&#xff0c;该服务层在模型与希望以特定格式获取数据的几个客户端之间进行中介。 如果我不向您展示如何最终使这种服务运转起来&#xff0c;那我当然是个混蛋。因此&#xff0c;下面的示例将其用于从数据库中获取用户&#xff1a;

use LibraryLoaderAutoloader,
LibraryDatabasePdoAdapter,
ModelMapperUserMapper,
ServiceUserService,
ServiceSerializer,
ServiceJsonEncoder;
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader &#61; new Autoloader;
$autoloader->register();
$adapter &#61; new PdoAdapter("mysql:dbname&#61;mydatabase", "myfancyusername", "mysecretpassword");
$userService &#61; new UserService(new UserMapper($adapter));
$userService->setEncoder(new JsonEncoder);
print_r($userService->fetchAllEncoded());
print_r($userService->fetchByIdEncoded(1));
$userService->setEncoder(new Serializer());
print_r($userService->fetchAllEncoded(array("ranking" &#61;> "high")));
print_r($userService->fetchByIdEncoded(1));

Despite a few obvious limitations, it’s clear to see that the service is a flexible component, which not only encodes user objects according to some predefined format, but also allows adding more encoders along the way thanks to the strategy pattern‘s facilities. Of course, its most engaging virtue is the ability for placing common application logic behind a clean API, a must for applications that need to perform a host of additional centralized tasks such as further processing domain model data, validation, logging, and more.

尽管存在一些明显的局限性&#xff0c;但很明显&#xff0c;该服务是一个灵活的组件&#xff0c;它不仅根据某种预定义的格式对用户对象进行编码&#xff0c;而且由于策略模式的便利性&#xff0c;还允许在此过程中添加更多的编码器。 当然&#xff0c;它最吸引人的优点是能够将通用的应用程序逻辑放在干净的API后面&#xff0c;这对于需要执行许多其他集中式任务(例如&#xff0c;进一步处理域模型数据&#xff0c;验证&#xff0c;日志记录等)的应用程序来说是必须的。

摘要 (Summary)

Even while services are still making their first timid steps in PHP’s mainstream (except for some specific platforms like FLOW3 and a few other frameworks that shyly provide a base blueprint for creating services in a painless manner), they’re solid, well-proven solutions in the enterprise world where systems usually rest on the foundations of a rich domain model and interact with a wide variety of client layers.

即使服务仍在PHP主流中迈出了怯的第一步(除了某些特定的平台(如FLOW3和其他一些框架&#xff0c;它们羞怯地为以轻松的方式创建服务提供了基本蓝图))&#xff0c;它们还是可靠的&#xff0c;久经考验的解决方案在企业世界中&#xff0c;系统通常基于丰富域模型的基础上并与各种客户端层进行交互。

Despite this rather overwhelming scenario, there’s nothing that explicitly prevents you from sinking your teeth into the goodies of services in smaller, more modest environments, especially if you’re playing around with some core concepts of DDD. So, now that you know what’s going on under the hood of services, feel free to give them a shot. You won’t regret it.

尽管存在这种情况&#xff0c;但没有任何东西可以明确阻止您陷入更小&#xff0c;更适度的环境中的服务优势中&#xff0c;尤其是当您在使用DDD的一些核心概念时。 因此&#xff0c;既然您知道服务背后发生了什么&#xff0c;请随时给他们一个机会。 你不会后悔的。

Image via kentoh / Shutterstock

图片来自kentoh / Shutterstock

翻译自: https://www.sitepoint.com/an-introduction-to-services/

微服务简介



推荐阅读
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 本文介绍了Python语言程序设计中文件和数据格式化的操作,包括使用np.savetext保存文本文件,对文本文件和二进制文件进行统一的操作步骤,以及使用Numpy模块进行数据可视化编程的指南。同时还提供了一些关于Python的测试题。 ... [详细]
  • wpf+mvvm代码组织结构及实现方式
    本文介绍了wpf+mvvm代码组织结构的由来和实现方式。作者回顾了自己大学时期接触wpf开发和mvvm模式的经历,认为mvvm模式使得开发更加专注于业务且高效。与此同时,作者指出mvvm模式相较于mvc模式的优势。文章还提到了当没有mvvm时处理数据和UI交互的例子,以及前后端分离和组件化的概念。作者希望能够只关注原始数据结构,将数据交给UI自行改变,从而解放劳动力,避免加班。 ... [详细]
  • 本文介绍了iOS开发中检测和解决内存泄漏的方法,包括静态分析、使用instruments检查内存泄漏以及代码测试等。同时还介绍了最能挣钱的行业,包括互联网行业、娱乐行业、教育行业、智能行业和老年服务行业,并提供了选行业的技巧。 ... [详细]
  • 本文详细介绍了在Linux虚拟化部署中进行VLAN配置的方法。首先要确认Linux系统内核是否已经支持VLAN功能,然后配置物理网卡、子网卡和虚拟VLAN网卡的关系。接着介绍了在Linux配置VLAN Trunk的步骤,包括将物理网卡添加到VLAN、检查添加的VLAN虚拟网卡信息以及重启网络服务等。最后,通过验证连通性来确认配置是否成功。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
author-avatar
文弱书生_李
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有